@using NexusReader.UI.Shared.Services @using NexusReader.UI.Shared.Models @using NexusReader.Application.DTOs.AI @using Microsoft.Extensions.Logging @inject KnowledgeCoordinator Coordinator @inject IReaderInteractionService InteractionService @inject IQuizStateService QuizService @inject IJSRuntime JS @inject ILogger Logger @if (IsVisible) {
} @code { [Parameter] public string SelectedText { get; set; } = string.Empty; [Parameter] public string BlockId { get; set; } = string.Empty; [Parameter] public SelectionCoordinates? Coordinates { get; set; } [Parameter] public string FullPageContent { get; set; } = string.Empty; private bool IsVisible => !string.IsNullOrEmpty(SelectedText) && Coordinates != null; private bool IsLoadingSummary = false; private bool IsLoadingQuiz = false; private bool IsAnyLoading => IsLoadingSummary || IsLoadingQuiz; private string _style = "visibility: hidden; opacity: 0; pointer-events: none;"; private bool _positionBelow = false; private SelectionCoordinates? _lastCoordinates; protected override void OnParametersSet() { Logger.LogDebug("[SelectionAiPanel] Parameters set. SelectedText: {Length} chars, Coordinates: {Top}", SelectedText.Length, Coordinates?.Top); if (Coordinates != _lastCoordinates) { _lastCoordinates = Coordinates; _style = "visibility: hidden; opacity: 0; pointer-events: none;"; _positionBelow = false; } // Reset loading states when parameters change IsLoadingSummary = false; IsLoadingQuiz = false; } protected override async Task OnAfterRenderAsync(bool firstRender) { if (IsVisible && _style.Contains("visibility: hidden")) { try { var module = await JS.InvokeAsync("import", "./_content/NexusReader.UI.Shared/js/selectionHandler.js"); var result = await module.InvokeAsync("positionToolbar"); if (result != null) { _style = string.Create(System.Globalization.CultureInfo.InvariantCulture, $"left: {result.Left:F1}px !important; " + $"top: {result.Top:F1}px !important; " + $"visibility: visible !important; " + $"opacity: 1 !important; " + $"pointer-events: auto !important;"); _positionBelow = result.Below; StateHasChanged(); } } catch (Exception ex) { Logger.LogWarning(ex, "[SelectionAiPanel] Error positioning toolbar."); } } } private async Task RequestSummaryAsync() { if (IsAnyLoading) return; IsLoadingSummary = true; StateHasChanged(); try { var module = await JS.InvokeAsync("import", "./_content/NexusReader.UI.Shared/js/selectionHandler.js"); var selectedText = await module.InvokeAsync("getSelectionText"); if (string.IsNullOrWhiteSpace(selectedText)) { selectedText = SelectedText; } if (!string.IsNullOrWhiteSpace(selectedText)) { var contextPrompt = !string.IsNullOrWhiteSpace(FullPageContent) ? $"ANALYSIS CONTEXT (Full Page Content):\n{FullPageContent}\n\nUSER SELECTION TO SUMMARIZE:\n" : ""; _ = Coordinator.StartSelectionSummaryAsync($"{contextPrompt}{selectedText}"); await CloseAsync(); await InteractionService.RequestAssistant(); } } catch (Exception ex) { Logger.LogError(ex, "[SelectionAiPanel] Error requesting summary for block {BlockId}.", BlockId); } finally { IsLoadingSummary = false; StateHasChanged(); } } private async Task GenerateQuizAsync() { if (IsAnyLoading) return; IsLoadingQuiz = true; StateHasChanged(); try { var module = await JS.InvokeAsync("import", "./_content/NexusReader.UI.Shared/js/selectionHandler.js"); var selectedText = await module.InvokeAsync("getSelectionText"); if (string.IsNullOrWhiteSpace(selectedText)) { selectedText = SelectedText; } if (!string.IsNullOrWhiteSpace(selectedText)) { var contextPrompt = !string.IsNullOrWhiteSpace(FullPageContent) ? $"ANALYSIS CONTEXT (Full Page Content):\n{FullPageContent}\n\nUSER SELECTION TO SUMMARIZE:\n" : ""; var result = await Coordinator.RequestSummaryAndQuizAsync($"{contextPrompt}{selectedText}"); if (result.IsSuccess) { await CloseAsync(); await QuizService.RequestQuiz(BlockId); } } } catch (Exception ex) { Logger.LogError(ex, "[SelectionAiPanel] Error generating quiz for block {BlockId}.", BlockId); } finally { IsLoadingQuiz = false; StateHasChanged(); } } private async Task CloseAsync() { await InteractionService.NotifyTextSelected(string.Empty, string.Empty, null!); } private class PositionResult { public double Left { get; set; } public double Top { get; set; } public bool Below { get; set; } } }