feat: implement dynamic knowledge graph updates and state management services
This commit is contained in:
@@ -25,6 +25,9 @@
|
||||
case "target":
|
||||
<circle cx="12" cy="12" r="10" /><circle cx="12" cy="12" r="6" /><circle cx="12" cy="12" r="2" />
|
||||
break;
|
||||
case "trash":
|
||||
<path d="M3 6h18M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2M10 11v6M14 11v6" />
|
||||
break;
|
||||
default:
|
||||
<!-- Fallback circle -->
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -31,13 +31,10 @@
|
||||
[Parameter] public List<string> Actions { get; set; } = new();
|
||||
[Parameter] public EventCallback<string> OnActionTriggered { get; set; }
|
||||
|
||||
private bool _isQuizMode = false;
|
||||
|
||||
private async Task HandleActionClick(string action)
|
||||
{
|
||||
if (action.Contains("quiz", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
_isQuizMode = true;
|
||||
QuizState.RequestQuiz(ContextBlockId);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
@using NexusReader.UI.Shared.Services
|
||||
@using NexusReader.Application.Abstractions.Services
|
||||
@inject IFocusModeService FocusMode
|
||||
@inject IKnowledgeService KnowledgeService
|
||||
|
||||
<aside class="intelligence-toolbar">
|
||||
<div class="toolbar-top">
|
||||
@@ -21,6 +23,9 @@
|
||||
<button class="toolbar-item" title="Search">
|
||||
<NexusIcon Name="search" Size="20" />
|
||||
</button>
|
||||
<button class="toolbar-item danger" @onclick="HandleClearCache" title="Clear AI Cache">
|
||||
<NexusIcon Name="trash" Size="20" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="toolbar-bottom">
|
||||
@@ -40,6 +45,17 @@
|
||||
FocusMode.OnFocusModeChanged += StateHasChanged;
|
||||
}
|
||||
|
||||
private async Task HandleClearCache()
|
||||
{
|
||||
// For now, a simple console log confirm or just do it
|
||||
Console.WriteLine("[IntelligenceToolbar] Requesting cache clear...");
|
||||
var result = await KnowledgeService.ClearCacheAsync();
|
||||
if (result.IsSuccess)
|
||||
{
|
||||
Console.WriteLine("[IntelligenceToolbar] Cache cleared successfully!");
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
FocusMode.OnFocusModeChanged -= StateHasChanged;
|
||||
|
||||
@@ -65,3 +65,8 @@
|
||||
.rotate-180 {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.toolbar-item.danger:hover {
|
||||
color: #ff4d4d;
|
||||
background: rgba(255, 77, 77, 0.1);
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
@using NexusReader.Application.Abstractions.Services
|
||||
@inject IMediator Mediator
|
||||
@inject IPlatformService PlatformService
|
||||
@inject IQuizStateService QuizService
|
||||
|
||||
<div class="knowledge-check">
|
||||
<div class="quiz-header">
|
||||
@@ -11,14 +12,14 @@
|
||||
<button class="expand-btn">⌵</button>
|
||||
</div>
|
||||
|
||||
@if (_isLoading)
|
||||
@if (QuizService.IsHydrating)
|
||||
{
|
||||
<div class="loading-state">Pobieranie pytań...</div>
|
||||
<div class="loading-state shimmer">Skanowanie wiedzy przez AI...</div>
|
||||
}
|
||||
else if (_quiz != null)
|
||||
else if (QuizService.CurrentQuiz != null)
|
||||
{
|
||||
<div class="quiz-body">
|
||||
@foreach (var question in _quiz.Questions)
|
||||
@foreach (var question in QuizService.CurrentQuiz.Questions)
|
||||
{
|
||||
<div class="question-container">
|
||||
<p class="question-text">@question.Question</p>
|
||||
@@ -50,21 +51,16 @@
|
||||
@code {
|
||||
[Parameter] public string ContextBlockId { get; set; } = string.Empty;
|
||||
|
||||
private bool _isLoading = true;
|
||||
private QuizDto? _quiz;
|
||||
|
||||
private Dictionary<QuizQuestionDto, (int SelectedIndex, bool IsCorrect)> _states = new();
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
_isLoading = true;
|
||||
var query = new GetQuizQuestionsQuery(ContextBlockId);
|
||||
var result = await Mediator.Send(query);
|
||||
|
||||
if (result.IsSuccess)
|
||||
_quiz = result.Value;
|
||||
|
||||
_isLoading = false;
|
||||
QuizService.OnQuizUpdated += () => InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
QuizService.OnQuizUpdated -= StateHasChanged;
|
||||
}
|
||||
|
||||
private async Task SelectOptionAsync(QuizQuestionDto question, int index)
|
||||
@@ -89,7 +85,7 @@
|
||||
|
||||
private bool AllQuestionsAnswered()
|
||||
{
|
||||
return _quiz != null && _states.Count == _quiz.Questions.Count;
|
||||
return QuizService.CurrentQuiz != null && _states.Count == QuizService.CurrentQuiz.Questions.Count;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -106,3 +106,18 @@
|
||||
border-color: #ff4444 !important;
|
||||
background: rgba(255, 68, 68, 0.1) !important;
|
||||
}
|
||||
|
||||
.loading-state.shimmer {
|
||||
background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.05), transparent);
|
||||
background-size: 200% 100%;
|
||||
animation: shimmer 1.5s infinite;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
color: var(--nexus-neon);
|
||||
text-shadow: 0 0 10px var(--nexus-neon);
|
||||
}
|
||||
|
||||
@keyframes shimmer {
|
||||
0% { background-position: -200% 0; }
|
||||
100% { background-position: 200% 0; }
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@
|
||||
@inject IMediator Mediator
|
||||
@inject IJSRuntime JS
|
||||
@inject IFocusModeService FocusMode
|
||||
@inject IKnowledgeGraphService GraphService
|
||||
|
||||
<div class="knowledge-graph-container" id="@ContainerId">
|
||||
@if (GraphData == null)
|
||||
@@ -37,6 +38,21 @@
|
||||
protected override void OnInitialized()
|
||||
{
|
||||
FocusMode.OnFocusModeChanged += HandleFocusSimulation;
|
||||
GraphService.OnGraphUpdated += HandleGraphUpdate;
|
||||
GraphService.OnActiveNodeChanged += HandleActiveNodeChange;
|
||||
}
|
||||
|
||||
private async void HandleGraphUpdate()
|
||||
{
|
||||
if (_module == null) return;
|
||||
await _module.InvokeVoidAsync("updateData", GraphService.CurrentGraphData);
|
||||
await InvokeAsync(StateHasChanged);
|
||||
}
|
||||
|
||||
private async void HandleActiveNodeChange(string nodeId)
|
||||
{
|
||||
if (_module == null) return;
|
||||
await _module.InvokeVoidAsync("setActiveNode", nodeId);
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
@inject IThemeService ThemeService
|
||||
@inject IFocusModeService FocusMode
|
||||
@inject IReaderNavigationService NavigationService
|
||||
@inject KnowledgeCoordinator Coordinator
|
||||
|
||||
<div class="reader-canvas @(ThemeService.IsLightMode ? "theme-light" : "theme-dark")">
|
||||
@if (ViewModel == null)
|
||||
@@ -59,6 +60,31 @@
|
||||
{
|
||||
await LoadChapterAsync(NavigationService.CurrentChapterIndex);
|
||||
StateHasChanged();
|
||||
await InitializeObserverAsync();
|
||||
}
|
||||
|
||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||
{
|
||||
if (firstRender)
|
||||
{
|
||||
await InitializeObserverAsync();
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InitializeObserverAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var module = await JS.InvokeAsync<IJSObjectReference>("import", "./_content/NexusReader.UI.Shared/js/readerObserver.js");
|
||||
await module.InvokeVoidAsync("initObserver", DotNetObjectReference.Create(this), ".reader-flow-container", ".block-wrapper");
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
[JSInvokable]
|
||||
public void HandleBlockReached(string blockId, string content)
|
||||
{
|
||||
Coordinator.OnBlockReached(blockId, content);
|
||||
}
|
||||
|
||||
private async Task LoadChapterAsync(int index)
|
||||
|
||||
Reference in New Issue
Block a user