199 lines
8.1 KiB
Plaintext
199 lines
8.1 KiB
Plaintext
@using NexusReader.UI.Shared.Services
|
|
@using NexusReader.UI.Shared.Models
|
|
@using NexusReader.Application.DTOs.AI
|
|
@inject KnowledgeCoordinator Coordinator
|
|
@inject IReaderInteractionService InteractionService
|
|
@inject IQuizStateService QuizService
|
|
@inject IJSRuntime JS
|
|
|
|
@if (IsVisible)
|
|
{
|
|
<div class="selection-ai-panel @(_positionBelow ? "below" : "")" style="@_style">
|
|
<button id="summary-btn" class="toolbar-btn primary @(IsLoadingSummary ? "loading" : "") @(IsAnyLoading ? "disabled cursor-not-allowed opacity-50" : "")"
|
|
disabled="@IsAnyLoading"
|
|
@onclick="RequestSummaryAsync">
|
|
@if (IsLoadingSummary)
|
|
{
|
|
<svg class="animate-spin h-4 w-4 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" style="animation: spin 1s linear infinite; width: 14px; height: 14px; color: currentColor; display: inline-block; margin-right: 4px;">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" style="opacity: 0.25;"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" style="opacity: 0.75;"></path>
|
|
</svg>
|
|
<span class="btn-text">Podsumowywanie...</span>
|
|
}
|
|
else
|
|
{
|
|
<NexusIcon Name="book-open" Size="14" Class="btn-icon" />
|
|
<span class="btn-text">Podsumuj</span>
|
|
}
|
|
</button>
|
|
<div class="toolbar-divider"></div>
|
|
<button id="quiz-btn" class="toolbar-btn secondary @(IsLoadingQuiz ? "loading" : "") @(IsAnyLoading ? "disabled cursor-not-allowed opacity-50" : "")"
|
|
disabled="@IsAnyLoading"
|
|
@onclick="GenerateQuizAsync">
|
|
@if (IsLoadingQuiz)
|
|
{
|
|
<svg class="animate-spin h-4 w-4 mr-2" xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" style="animation: spin 1s linear infinite; width: 14px; height: 14px; color: currentColor; display: inline-block; margin-right: 4px;">
|
|
<circle class="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="4" style="opacity: 0.25;"></circle>
|
|
<path class="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z" style="opacity: 0.75;"></path>
|
|
</svg>
|
|
<span class="btn-text">Generowanie...</span>
|
|
}
|
|
else
|
|
{
|
|
<NexusIcon Name="target" Size="14" Class="btn-icon" />
|
|
<span class="btn-text">Quiz</span>
|
|
}
|
|
</button>
|
|
</div>
|
|
}
|
|
|
|
@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()
|
|
{
|
|
Console.WriteLine($"[SelectionAiPanel] Parameters set. SelectedText: {SelectedText.Length} chars, Coordinates: {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<IJSObjectReference>("import", "./_content/NexusReader.UI.Shared/js/selectionHandler.js");
|
|
var result = await module.InvokeAsync<PositionResult>("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)
|
|
{
|
|
Console.WriteLine($"[SelectionAiPanel] Error positioning toolbar: {ex.Message}");
|
|
}
|
|
}
|
|
}
|
|
|
|
private async Task RequestSummaryAsync()
|
|
{
|
|
if (IsAnyLoading) return;
|
|
IsLoadingSummary = true;
|
|
StateHasChanged();
|
|
|
|
try
|
|
{
|
|
var module = await JS.InvokeAsync<IJSObjectReference>("import", "./_content/NexusReader.UI.Shared/js/selectionHandler.js");
|
|
var selectedText = await module.InvokeAsync<string>("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)
|
|
{
|
|
Console.WriteLine($"[SelectionAiPanel] Error requesting summary: {ex.Message}");
|
|
}
|
|
finally
|
|
{
|
|
IsLoadingSummary = false;
|
|
StateHasChanged();
|
|
}
|
|
}
|
|
|
|
private async Task GenerateQuizAsync()
|
|
{
|
|
if (IsAnyLoading) return;
|
|
IsLoadingQuiz = true;
|
|
StateHasChanged();
|
|
|
|
try
|
|
{
|
|
var module = await JS.InvokeAsync<IJSObjectReference>("import", "./_content/NexusReader.UI.Shared/js/selectionHandler.js");
|
|
var selectedText = await module.InvokeAsync<string>("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)
|
|
{
|
|
Console.WriteLine($"[SelectionAiPanel] Error generating quiz: {ex.Message}");
|
|
}
|
|
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; }
|
|
}
|
|
}
|
|
|
|
|