From faf6ec826e8ad31a803c9fa43d5673240d0f4cc9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Jasi=C5=84ski?= Date: Sat, 6 Jun 2026 10:41:48 +0200 Subject: [PATCH 01/10] feat(intelligence): implement Global AI Q&A screen and paywall blocker - Implemented standard empty and active chat conversation states for the `/intelligence` page - Created interactive `AiResponseRenderer` with AOT-compliant sentence splitting and payment gateway simulation - Added scoped `LibraryStateService` to synchronize book ownership and updates across the application - Obfuscated paywalled content in DOM to prevent inspection bypass - Fixed local port connection mismatch by updating API configurations to use port 5104 --- src/NexusReader.Maui/MauiProgram.cs | 3 +- src/NexusReader.Maui/appsettings.json | 2 +- .../Molecules/AiResponseRenderer.razor | 244 ++++++++++++ .../Molecules/AiResponseRenderer.razor.css | 269 +++++++++++++ .../Models/ReaderModels.cs | 6 + src/NexusReader.UI.Shared/Pages/Catalog.razor | 17 + .../Pages/Intelligence.razor | 240 ++++++++---- .../Pages/Intelligence.razor.css | 364 +++++++----------- src/NexusReader.UI.Shared/Pages/MyBooks.razor | 17 + .../Services/ILibraryStateService.cs | 12 + .../Services/LibraryStateService.cs | 27 ++ src/NexusReader.Web.Client/Program.cs | 1 + src/NexusReader.Web/Program.cs | 44 +++ src/NexusReader.Web/appsettings.Test.json | 2 +- src/NexusReader.Web/appsettings.json | 2 +- 15 files changed, 932 insertions(+), 318 deletions(-) create mode 100644 src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor create mode 100644 src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css create mode 100644 src/NexusReader.UI.Shared/Services/ILibraryStateService.cs create mode 100644 src/NexusReader.UI.Shared/Services/LibraryStateService.cs diff --git a/src/NexusReader.Maui/MauiProgram.cs b/src/NexusReader.Maui/MauiProgram.cs index a4b2c6c..4a5fca4 100644 --- a/src/NexusReader.Maui/MauiProgram.cs +++ b/src/NexusReader.Maui/MauiProgram.cs @@ -56,7 +56,7 @@ public static class MauiProgram builder.Services.AddTransient(); builder.Services.AddHttpClient("NexusAPI", client => { - var apiBaseUrl = builder.Configuration["ApiSettings:BaseUrl"] ?? "http://localhost:5000"; + var apiBaseUrl = builder.Configuration["ApiSettings:BaseUrl"] ?? "http://localhost:5104"; client.BaseAddress = new Uri(apiBaseUrl); }).AddHttpMessageHandler(); @@ -74,6 +74,7 @@ public static class MauiProgram builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); + builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/src/NexusReader.Maui/appsettings.json b/src/NexusReader.Maui/appsettings.json index 4d3ef31..20260df 100644 --- a/src/NexusReader.Maui/appsettings.json +++ b/src/NexusReader.Maui/appsettings.json @@ -1,6 +1,6 @@ { "ApiSettings": { - "BaseUrl": "https://localhost:5000" + "BaseUrl": "http://localhost:5104" }, "Serilog": { "Using": [ diff --git a/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor b/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor new file mode 100644 index 0000000..557d936 --- /dev/null +++ b/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor @@ -0,0 +1,244 @@ +@using NexusReader.UI.Shared.Models +@using NexusReader.UI.Shared.Services +@using NexusReader.Application.DTOs.AI +@using NexusReader.Application.DTOs.User +@using System.Net.Http.Json +@inject HttpClient Http +@inject ILibraryStateService LibraryStateService +@inject NavigationManager NavigationManager + +
+
+ @if (Message.Sender == "User") + { + + } + else + { + + } +
+ +
+
+ @Message.Sender + @Message.Timestamp.ToString("HH:mm") +
+ +
+ @if (Message.Sender == "User") + { +

@Message.Text

+ } + else + { + @if (Message.IsPaywalled && !_isUnlocked) + { +
+ @foreach (var segment in ParseSegments(Message.ClearText)) + { + @if (segment.IsCitation) + { + + } + else + { + @RenderMarkdown(segment.Text) + } + } +
+ +
+ @foreach (var segment in ParseSegments(Message.BlurredTeaserText)) + { + @if (segment.IsCitation) + { + + } + else + { + @RenderMarkdown(segment.Text) + } + } +
+ +
+
+ 🔒 +

Zablokowano pełną odpowiedź (Paywall)

+
+ +

+ Powyższy fragment wiedzy pochodzi z materiału: + '@(string.IsNullOrEmpty(Message.SourceBookTitle) ? "Architektura .NET 10 i Ekosystem Blazor" : Message.SourceBookTitle)'. + Ta pozycja nie znajduje się w Twojej bibliotece (/my-books). Aby uzyskać dostęp do pełnych instrukcji technicznych oraz gotowych kodów C#, odblokuj ten materiał w katalogu. +

+ +
+ @if (_isSimulatingPayment) + { + + } + else + { + + } + + Zobacz szczegóły w Katalogu + +
+
+ } + else + { +
+ @foreach (var segment in Message.Segments) + { + @if (segment.IsCitation) + { + + } + else + { + @RenderMarkdown(segment.Text) + } + } +
+ + @if (_showSuccessBanner) + { +
+ + Odblokowano pełną odpowiedź! Książka została dodana do Twojej biblioteki. +
+ } + } + } +
+
+
+ +@code { + [Parameter] public ChatMessage Message { get; set; } = default!; + [Parameter] public List? OwnedBooks { get; set; } + + private string GetBubbleClass() + { + if (Message.Sender == "User") return "user-bubble"; + return Message.IsPaywalled && !_isUnlocked ? "ai-bubble paywalled-bubble" : "ai-bubble"; + } + + private bool _isUnlocked = false; + private bool _isSimulatingPayment = false; + private bool _showSuccessBanner = false; + + private async Task HandlePurchase() + { + if (_isSimulatingPayment) return; + + _isSimulatingPayment = true; + StateHasChanged(); + + // Simulate payment gateway delay (1.5 seconds) + await Task.Delay(1500); + + try + { + var bookTitle = string.IsNullOrEmpty(Message.SourceBookTitle) + ? "Architektura .NET 10 i Ekosystem Blazor" + : Message.SourceBookTitle; + + // Call POST endpoint to persist the purchase + var response = await Http.PostAsJsonAsync("api/library/purchase", new { Title = bookTitle }); + if (response.IsSuccessStatusCode) + { + _isUnlocked = true; + Message.IsPaywalled = false; + _showSuccessBanner = true; + + // Fetch updated library list and update state manager + var updatedBooks = await Http.GetFromJsonAsync>("api/library/books"); + LibraryStateService.OwnedBooks = updatedBooks; + } + else + { + Console.WriteLine("[AiResponseRenderer] Purchase failed on server."); + } + } + catch (Exception ex) + { + Console.WriteLine($"[AiResponseRenderer] Error processing purchase: {ex.Message}"); + } + finally + { + _isSimulatingPayment = false; + StateHasChanged(); + } + } + + private List ParseSegments(string text) + { + var segments = new List(); + if (string.IsNullOrEmpty(text)) return segments; + + var regex = new System.Text.RegularExpressions.Regex( + @"\[Source ID:\s*([^\]]+)\]|\[([a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12})\]", + System.Text.RegularExpressions.RegexOptions.IgnoreCase); + var matches = regex.Matches(text); + + int lastIndex = 0; + foreach (System.Text.RegularExpressions.Match match in matches) + { + if (match.Index > lastIndex) + { + segments.Add(new ResponseSegment + { + Text = text.Substring(lastIndex, match.Index - lastIndex), + IsCitation = false + }); + } + + var citationId = match.Groups[1].Success + ? match.Groups[1].Value.Trim() + : match.Groups[2].Value.Trim(); + + segments.Add(new ResponseSegment + { + IsCitation = true, + CitationId = citationId + }); + + lastIndex = match.Index + match.Length; + } + + if (lastIndex < text.Length) + { + segments.Add(new ResponseSegment + { + Text = text.Substring(lastIndex), + IsCitation = false + }); + } + + return segments; + } + + private MarkupString RenderMarkdown(string text) + { + if (string.IsNullOrEmpty(text)) return new MarkupString(string.Empty); + + var html = System.Net.WebUtility.HtmlEncode(text); + html = System.Text.RegularExpressions.Regex.Replace(html, @"\*\*(.*?)\*\*", "$1"); + html = System.Text.RegularExpressions.Regex.Replace(html, @"\*(.*?)\*", "$1"); + html = System.Text.RegularExpressions.Regex.Replace(html, @"```(?:[a-zA-Z0-9+#]+)?\s*([\s\S]*?)\s*```", "
$1
"); + html = System.Text.RegularExpressions.Regex.Replace(html, @"`(.*?)`", "$1"); + html = html.Replace("\n", "
"); + + return new MarkupString(html); + } +} diff --git a/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css b/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css new file mode 100644 index 0000000..cc479c0 --- /dev/null +++ b/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css @@ -0,0 +1,269 @@ +.message-row { + display: flex; + gap: 1rem; + width: 100%; + max-width: 90%; + margin-bottom: 1.5rem; + animation: bubble-fade-in 0.35s cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +.user-row { + align-self: flex-end; + margin-left: auto; + flex-direction: row-reverse; +} + +.ai-row { + align-self: flex-start; + margin-right: auto; +} + +.message-avatar { + width: 38px; + height: 38px; + border-radius: 50%; + display: flex; + align-items: center; + justify-content: center; + font-size: 1.1rem; + flex-shrink: 0; +} + +.user-row .message-avatar { + background: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.05) 100%); + color: #ffffff; + border: 1px solid rgba(255, 255, 255, 0.2); + box-shadow: 0 0 10px rgba(255, 255, 255, 0.1); +} + +.ai-row .message-avatar { + background: linear-gradient(135deg, #005f38 0%, #004024 100%); + color: #e6fffa; + border: 1px solid rgba(0, 255, 153, 0.4); + box-shadow: 0 0 10px rgba(0, 255, 153, 0.25); +} + +.message-bubble { + padding: 1.25rem 1.5rem; + border-radius: 16px; + position: relative; + line-height: 1.6; + font-size: 0.975rem; + display: flex; + flex-direction: column; + width: 100%; +} + +.user-bubble { + background: #1a1a1e; + border: 1px solid rgba(255, 255, 255, 0.05); + color: #e4e4e7; + border-top-right-radius: 4px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); +} + +.ai-bubble { + background: rgba(26, 26, 30, 0.6); + border: 1px solid rgba(255, 255, 255, 0.05); + color: #e2e8f0; + border-top-left-radius: 4px; + box-shadow: 0 4px 25px rgba(0, 0, 0, 0.2); +} + +.paywalled-bubble { + border-color: rgba(16, 185, 129, 0.15); +} + +.message-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 0.75rem; + font-size: 0.75rem; + opacity: 0.6; +} + +.sender-name { + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.message-time { + font-family: monospace; +} + +.message-content { + word-break: break-word; +} + +/* Paragraph spacing */ +.message-content p { + margin: 0 0 1rem 0; +} +.message-content p:last-child { + margin-bottom: 0; +} + +/* Paywall Blur Styles */ +.teaser-blur { + position: relative; + filter: blur(5px); + pointer-events: none; + -webkit-user-select: none; + user-select: none; + opacity: 0.35; + margin-top: 1rem; + margin-bottom: 1.5rem; + -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 40%, rgba(0,0,0,0) 100%); + mask-image: linear-gradient(to bottom, rgba(0,0,0,1) 40%, rgba(0,0,0,0) 100%); +} + +/* Upsell Card */ +.upsell-card { + background: #1a1a1e; + border-radius: 12px; + border: 1px solid rgba(16, 185, 129, 0.2); + padding: 1.5rem; + margin-top: 1rem; + box-shadow: 0 8px 30px rgba(0, 0, 0, 0.3); + animation: card-slide-in 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards; +} + +.upsell-header { + display: flex; + align-items: center; + gap: 0.75rem; + margin-bottom: 0.75rem; +} + +.upsell-icon { + font-size: 1.25rem; +} + +.upsell-header h4 { + margin: 0; + color: #10b981; + font-size: 1.1rem; + font-weight: 700; + letter-spacing: 0.5px; +} + +.upsell-text { + color: rgba(255, 255, 255, 0.75); + font-size: 0.9rem; + line-height: 1.55; + margin: 0 0 1.25rem 0; +} + +.upsell-actions { + display: flex; + flex-wrap: wrap; + gap: 1rem; +} + +.btn-upsell { + display: inline-flex; + align-items: center; + justify-content: center; + padding: 0.75rem 1.5rem; + font-size: 0.85rem; + font-weight: 700; + text-transform: uppercase; + border-radius: 8px; + cursor: pointer; + transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); + text-decoration: none; + letter-spacing: 0.5px; + min-height: 44px; +} + +.btn-primary { + background: #10b981; + border: none; + color: #121214; +} + +.btn-primary:hover:not(:disabled) { + background: #0d9668; + transform: translateY(-2px); + box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3); +} + +.btn-primary:active:not(:disabled) { + transform: translateY(0); +} + +.btn-primary:disabled { + background: rgba(16, 185, 129, 0.5); + color: rgba(18, 18, 20, 0.6); + cursor: not-allowed; +} + +.btn-secondary { + background: transparent; + border: 1px solid #10b981; + color: #10b981; +} + +.btn-secondary:hover { + background: rgba(16, 185, 129, 0.05); + transform: translateY(-2px); +} + +.btn-secondary:active { + transform: translateY(0); +} + +/* Success Banner */ +.success-unlock-banner { + display: flex; + align-items: center; + gap: 0.75rem; + background: rgba(16, 185, 129, 0.1); + border: 1px solid rgba(16, 185, 129, 0.3); + color: #10b981; + padding: 1rem; + border-radius: 8px; + margin-top: 1.25rem; + font-size: 0.9rem; + font-weight: 600; + animation: fade-in 0.5s ease-out; +} + +.success-icon { + font-weight: bold; + font-size: 1.1rem; +} + +/* Payment Spinner */ +.payment-spinner { + width: 16px; + height: 16px; + border: 2px solid rgba(18, 18, 20, 0.2); + border-top-color: #121214; + border-radius: 50%; + margin-right: 0.75rem; + animation: spin 0.8s linear infinite; +} + +/* Keyframes */ +@keyframes bubble-fade-in { + 0% { opacity: 0; transform: translateY(12px) scale(0.98); } + 100% { opacity: 1; transform: translateY(0) scale(1); } +} + +@keyframes card-slide-in { + 0% { opacity: 0; transform: translateY(10px); } + 100% { opacity: 1; transform: translateY(0); } +} + +@keyframes fade-in { + from { opacity: 0; } + to { opacity: 1; } +} + +@keyframes spin { + 0% { transform: rotate(0deg); } + 100% { transform: rotate(360deg); } +} diff --git a/src/NexusReader.UI.Shared/Models/ReaderModels.cs b/src/NexusReader.UI.Shared/Models/ReaderModels.cs index 9340821..bcebc18 100644 --- a/src/NexusReader.UI.Shared/Models/ReaderModels.cs +++ b/src/NexusReader.UI.Shared/Models/ReaderModels.cs @@ -28,6 +28,12 @@ public class ChatMessage public DateTime Timestamp { get; set; } = DateTime.UtcNow; public List Segments { get; set; } = new(); public List Citations { get; set; } = new(); + + public string ClearText { get; set; } = string.Empty; + public string BlurredTeaserText { get; set; } = string.Empty; + public bool IsPaywalled { get; set; } + public string SourceBookTitle { get; set; } = string.Empty; + public string DocumentId { get; set; } = string.Empty; } /// diff --git a/src/NexusReader.UI.Shared/Pages/Catalog.razor b/src/NexusReader.UI.Shared/Pages/Catalog.razor index 09eca8b..e4255bd 100644 --- a/src/NexusReader.UI.Shared/Pages/Catalog.razor +++ b/src/NexusReader.UI.Shared/Pages/Catalog.razor @@ -1,5 +1,6 @@ @page "/catalog" @attribute [Authorize] +@implements IDisposable @using NexusReader.UI.Shared.Components.Organisms @using NexusReader.Application.DTOs.User @using NexusReader.UI.Shared.Services @@ -7,6 +8,7 @@ @inject HttpClient Http @inject IReaderNavigationService ReaderNavigation @inject NavigationManager NavigationManager +@inject ILibraryStateService LibraryStateService
@@ -189,6 +191,11 @@ private bool _isLoading = true; private List? _books; + protected override void OnInitialized() + { + LibraryStateService.OnBooksChanged += HandleBooksChanged; + } + protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) @@ -197,6 +204,11 @@ } } + private void HandleBooksChanged() + { + _ = InvokeAsync(LoadBooksAsync); + } + private async Task LoadBooksAsync() { _isLoading = true; @@ -231,4 +243,9 @@ // Showcase callback NavigationManager.NavigateTo("/profile"); } + + public void Dispose() + { + LibraryStateService.OnBooksChanged -= HandleBooksChanged; + } } diff --git a/src/NexusReader.UI.Shared/Pages/Intelligence.razor b/src/NexusReader.UI.Shared/Pages/Intelligence.razor index 8e7e432..fca1618 100644 --- a/src/NexusReader.UI.Shared/Pages/Intelligence.razor +++ b/src/NexusReader.UI.Shared/Pages/Intelligence.razor @@ -1,35 +1,34 @@ @page "/intelligence" @attribute [Authorize] +@implements IDisposable @using NexusReader.Application.DTOs.AI @using NexusReader.Application.Abstractions.Services @using NexusReader.Application.DTOs.User +@using NexusReader.UI.Shared.Components.Molecules @using NexusReader.UI.Shared.Components.Atoms @using NexusReader.UI.Shared.Models @using System.Net.Http.Json @inject HttpClient Http @inject IKnowledgeService KnowledgeService @inject AuthenticationStateProvider AuthStateProvider +@inject ILibraryStateService LibraryStateService
-
-
-

Global Intelligence

-

Interrogate, explore, and synthesize grounded knowledge from your library using Polyglot KM-RAG

-
-
- -
+
@if (_chatMessages.Count == 0) {
- - + + + + + +
-

Start Interrogating Your Library

-

Ask complex questions across your entire ebook collection. The KM-RAG engine dynamically builds semantic maps, resolves dependencies, and formulates high-fidelity, grounded answers with interactive popover citations.

+
Zadaj pytanie globalne do całej biblioteki...
} else @@ -37,37 +36,7 @@
@foreach (var message in _chatMessages) { -
-
- @if (message.Sender == "User") - { - - } - else - { - - } -
-
-
- @message.Sender - @message.Timestamp.ToString("HH:mm") -
-
- @foreach (var segment in message.Segments) - { - @if (segment.IsCitation) - { - - } - else - { - @RenderMarkdown(segment.Text) - } - } -
-
-
+ } @if (_isLoading) @@ -100,9 +69,9 @@
- + @@ -117,7 +117,10 @@ [SupplyParameterFromQuery(Name = "returnUrl")] public string? ReturnUrl { get; set; } - private LoginModel _loginModel = new(); +#pragma warning disable BL0008 + [SupplyParameterFromForm(FormName = "login-form")] + private LoginModel _loginModel { get; set; } = new(); +#pragma warning restore BL0008 private string? _errorMessage; private bool _isSubmitting; private bool _showPassword; diff --git a/src/NexusReader.UI.Shared/Pages/Account/Register.razor b/src/NexusReader.UI.Shared/Pages/Account/Register.razor index b4b18bb..f66ff95 100644 --- a/src/NexusReader.UI.Shared/Pages/Account/Register.razor +++ b/src/NexusReader.UI.Shared/Pages/Account/Register.razor @@ -21,7 +21,7 @@

Utwórz nowe konto

- +
@@ -71,14 +71,17 @@
- @code { - private RegisterModel _registerModel = new(); +#pragma warning disable BL0008 + [SupplyParameterFromForm(FormName = "register-form")] + private RegisterModel _registerModel { get; set; } = new(); +#pragma warning restore BL0008 private string? _errorMessage; private bool _isSubmitting; diff --git a/src/NexusReader.UI.Shared/Pages/Dashboard.razor b/src/NexusReader.UI.Shared/Pages/Dashboard.razor index 4808e74..76be0eb 100644 --- a/src/NexusReader.UI.Shared/Pages/Dashboard.razor +++ b/src/NexusReader.UI.Shared/Pages/Dashboard.razor @@ -61,24 +61,28 @@ @if (_profile?.MappedConcepts != null && _profile.MappedConcepts.Any()) { - @for (int i = 0; i < _profile.MappedConcepts.Count; i++) - { - var concept = _profile.MappedConcepts[i]; - var angle = i * (360.0 / _profile.MappedConcepts.Count); - var dist = 65; -
-
- } +
+ @for (int i = 0; i < _profile.MappedConcepts.Count; i++) + { + var concept = _profile.MappedConcepts[i]; + var angle = i * (360.0 / _profile.MappedConcepts.Count); + var dist = 65; +
+
+ } +
} else { -
-
-
+
+
+
+
+
}
@@ -111,10 +115,10 @@
@if (_profile?.RecentQuizzes != null && _profile.RecentQuizzes.Any()) { -
+
@foreach (var quiz in _profile.RecentQuizzes) { -
+
@quiz.Topic = 50 ? "badge-warning" : "badge-danger")"> @@ -130,7 +134,7 @@ } else { -
+

Brak rozwiązanych quizów

Rozwiązuj quizy w trakcie czytania książek, aby śledzić swoje postępy.

diff --git a/src/NexusReader.UI.Shared/wwwroot/app.css b/src/NexusReader.UI.Shared/wwwroot/app.css index dee9bcc..81690bc 100644 --- a/src/NexusReader.UI.Shared/wwwroot/app.css +++ b/src/NexusReader.UI.Shared/wwwroot/app.css @@ -66,24 +66,25 @@ /* Global Semantic Theme Mapping */ ---nexus-primary: var(--nexus-neon); ---nexus-primary-glow: var(--nexus-neon-glow); ---nexus-primary-hover: #00e688; +:root { + --nexus-primary: var(--nexus-neon); + --nexus-primary-glow: var(--nexus-neon-glow); + --nexus-primary-hover: #00e688; -/* Standard Layout Tokens */ ---radius-sm: 8px; ---radius-md: 12px; ---radius-lg: 16px; ---radius-xl: 20px; + /* Standard Layout Tokens */ + --radius-sm: 8px; + --radius-md: 12px; + --radius-lg: 16px; + --radius-xl: 20px; -/* Safe Area Insets with fallbacks */ ---safe-area-inset-top: env(safe-area-inset-top, 0px); ---safe-area-inset-bottom: env(safe-area-inset-bottom, 0px); ---safe-area-inset-left: env(safe-area-inset-left, 0px); ---safe-area-inset-right: env(safe-area-inset-right, 0px); + /* Safe Area Insets with fallbacks */ + --safe-area-inset-top: env(safe-area-inset-top, 0px); + --safe-area-inset-bottom: env(safe-area-inset-bottom, 0px); + --safe-area-inset-left: env(safe-area-inset-left, 0px); + --safe-area-inset-right: env(safe-area-inset-right, 0px); -/* Transitions */ ---nexus-transition: 0.4s cubic-bezier(0.4, 0, 0.2, 1); + /* Transitions */ + --nexus-transition: 0.4s cubic-bezier(0.4, 0, 0.2, 1); } /* Global Glassmorphism with Fallback */ diff --git a/src/NexusReader.Web.Client/Program.cs b/src/NexusReader.Web.Client/Program.cs index ac06c29..13666ce 100644 --- a/src/NexusReader.Web.Client/Program.cs +++ b/src/NexusReader.Web.Client/Program.cs @@ -62,6 +62,10 @@ builder.Services.AddSingleton(new ThrowingQuizResultRepos builder.Services.AddSingleton(new ThrowingConceptsMapReadRepository()); builder.Services.AddSingleton(new ThrowingSyncBroadcaster()); builder.Services.AddSingleton(new ThrowingEpubExtractor()); +builder.Services.AddSingleton(new ThrowingUserLibraryStore()); +builder.Services.AddSingleton(new ThrowingVectorSearchStore()); +builder.Services.Configure(builder.Configuration.GetSection(NexusReader.Application.Common.RagMonetizationOptions.SectionName)); +builder.Services.AddSingleton(new ThrowingChatClient()); builder.Services.AddApplication(); builder.Services.AddScoped(); @@ -135,3 +139,37 @@ public class ThrowingEpubExtractor : IEpubExtractor => throw new NotSupportedException("EPUB text extraction is not supported in the WASM client."); } +public class ThrowingUserLibraryStore : IUserLibraryStore +{ + public Task> GetOwnedBookIdsAsync(string userId, CancellationToken cancellationToken = default) + => throw new NotSupportedException("UserLibrary operations are not supported in the WASM client."); + + public Task> GetBookTitlesAsync(List bookIds, CancellationToken cancellationToken = default) + => throw new NotSupportedException("UserLibrary operations are not supported in the WASM client."); +} + +public class ThrowingVectorSearchStore : IVectorSearchStore +{ + public Task> SearchGlobalAsync(string queryText, string tenantId, int limit, CancellationToken cancellationToken = default) + => throw new NotSupportedException("VectorSearch operations are not supported in the WASM client."); + + public Task> SearchLocalAsync(string queryText, string tenantId, List whitelistedBookIds, int limit, CancellationToken cancellationToken = default) + => throw new NotSupportedException("VectorSearch operations are not supported in the WASM client."); + + public Task> SearchGlobalExcludeAsync(string queryText, string tenantId, Guid excludeBookId, int limit, CancellationToken cancellationToken = default) + => throw new NotSupportedException("VectorSearch operations are not supported in the WASM client."); +} + +public class ThrowingChatClient : IChatClient +{ + public void Dispose() { } + + public Task GetResponseAsync(IEnumerable chatMessages, ChatOptions? options = null, CancellationToken cancellationToken = default) + => throw new NotSupportedException("Chat operations are not supported in the WASM client."); + + public IAsyncEnumerable GetStreamingResponseAsync(IEnumerable chatMessages, ChatOptions? options = null, CancellationToken cancellationToken = default) + => throw new NotSupportedException("Chat operations are not supported in the WASM client."); + + public object? GetService(Type serviceType, object? serviceKey = null) => null; +} + diff --git a/src/NexusReader.Web/Program.cs b/src/NexusReader.Web/Program.cs index f684de9..dfe8d42 100644 --- a/src/NexusReader.Web/Program.cs +++ b/src/NexusReader.Web/Program.cs @@ -53,6 +53,8 @@ builder.Services.AddHttpContextAccessor(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); +builder.Services.AddScoped(); // Feature settings (avoiding direct raw IConfiguration injection in client pages) var featureSettings = builder.Configuration.GetSection("Features").Get() ?? new FeatureSettings(); builder.Services.AddSingleton(featureSettings); diff --git a/src/NexusReader.Web/Services/ServerRecommendationService.cs b/src/NexusReader.Web/Services/ServerRecommendationService.cs new file mode 100644 index 0000000..0724ba1 --- /dev/null +++ b/src/NexusReader.Web/Services/ServerRecommendationService.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using System.Security.Claims; +using System.Threading; +using System.Threading.Tasks; +using FluentResults; +using MediatR; +using Microsoft.AspNetCore.Http; +using NexusReader.Application.Queries.Recommendations; +using NexusReader.UI.Shared.Services; + +namespace NexusReader.Web.Services; + +/// +/// Server-side implementation of that executes +/// the MediatR query directly inside the Web Server's request context. +/// +public sealed class ServerRecommendationService : IRecommendationService +{ + private readonly IMediator _mediator; + private readonly IHttpContextAccessor _httpContextAccessor; + + public ServerRecommendationService(IMediator mediator, IHttpContextAccessor httpContextAccessor) + { + _mediator = mediator; + _httpContextAccessor = httpContextAccessor; + } + + public async Task?> GetRecommendationsAsync(CancellationToken cancellationToken = default) + { + var httpContext = _httpContextAccessor.HttpContext; + if (httpContext?.User == null) + { + return new List(); + } + + var userId = httpContext.User.FindFirstValue(ClaimTypes.NameIdentifier); + if (string.IsNullOrEmpty(userId)) + { + return new List(); + } + + var result = await _mediator.Send(new GetContextualRecommendationsQuery(userId), cancellationToken); + if (result.IsSuccess && result.Value != null) + { + return result.Value.Recommendations; + } + + return new List(); + } +} diff --git a/src/NexusReader.Web/Services/ServerThemeService.cs b/src/NexusReader.Web/Services/ServerThemeService.cs new file mode 100644 index 0000000..11df3ef --- /dev/null +++ b/src/NexusReader.Web/Services/ServerThemeService.cs @@ -0,0 +1,21 @@ +using NexusReader.Domain.Enums; +using NexusReader.UI.Shared.Services; + +namespace NexusReader.Web.Services; + +public sealed class ServerThemeService : IThemeService +{ + public ThemeMode Mode => ThemeMode.System; + public bool IsLightMode => false; + + // Explicit event implementation to avoid CS0067 warning about unused events on the server + public event Action? OnThemeChanged + { + add { } + remove { } + } + + public Task InitializeAsync() => Task.CompletedTask; + public Task SetThemeAsync(ThemeMode mode) => Task.CompletedTask; + public Task ToggleTheme() => Task.CompletedTask; +} -- 2.52.0 From f6277bacfebb66b42415d59ead71ec1f196c955b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Jasi=C5=84ski?= Date: Sun, 7 Jun 2026 14:01:52 +0200 Subject: [PATCH 09/10] style(theme): complete warm sepia dashboard styles --- .../Molecules/AiResponseRenderer.razor.css | 73 +++++------ .../Layout/MainHubLayout.razor.css | 32 ++--- .../Pages/Account/Profile.razor.css | 117 +++++------------- .../Pages/Catalog.razor.css | 45 +++---- .../Pages/ConceptsDashboard.razor.css | 117 +++++++++--------- .../Pages/Dashboard.razor.css | 93 +++++++------- .../Pages/Intelligence.razor.css | 64 +++++----- .../Pages/MyBooks.razor.css | 48 +++---- 8 files changed, 267 insertions(+), 322 deletions(-) diff --git a/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css b/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css index b8d96f1..7fcb266 100644 --- a/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css +++ b/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css @@ -30,17 +30,17 @@ } .user-row .message-avatar { - background: linear-gradient(135deg, rgba(255, 255, 255, 0.15) 0%, rgba(255, 255, 255, 0.05) 100%); - color: #ffffff; - border: 1px solid rgba(255, 255, 255, 0.2); - box-shadow: 0 0 10px rgba(255, 255, 255, 0.1); + background: var(--bg-surface); + color: var(--text-main); + border: 1px solid var(--border); + box-shadow: 0 0 10px var(--border); } .ai-row .message-avatar { background: linear-gradient(135deg, #005f38 0%, #004024 100%); color: #e6fffa; - border: 1px solid rgba(0, 255, 153, 0.4); - box-shadow: 0 0 10px rgba(0, 255, 153, 0.25); + border: 1px solid var(--accent); + box-shadow: 0 0 10px var(--accent-glow); } .message-bubble { @@ -55,23 +55,23 @@ } .user-bubble { - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.05); - color: #e4e4e7; + background: var(--bg-surface); + border: 1px solid var(--border); + color: var(--text-main); border-top-right-radius: 4px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2); + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.02); } .ai-bubble { - background: rgba(26, 26, 30, 0.6); - border: 1px solid rgba(255, 255, 255, 0.05); - color: #e2e8f0; + background: var(--bg-surface); + border: 1px solid var(--border); + color: var(--text-main); border-top-left-radius: 4px; - box-shadow: 0 4px 25px rgba(0, 0, 0, 0.2); + box-shadow: 0 4px 25px rgba(0, 0, 0, 0.03); } .paywalled-bubble { - border-color: rgba(16, 185, 129, 0.15); + border-color: var(--accent-glow); } .message-header { @@ -109,8 +109,8 @@ .paywall-teaser { position: relative; margin-bottom: 1.5rem; - -webkit-mask-image: linear-gradient(to bottom, black 30%, transparent 100%); - mask-image: linear-gradient(to bottom, black 30%, transparent 100%); + -webkit-mask-image: linear-gradient(to bottom, #000 30%, transparent 100%); + mask-image: linear-gradient(to bottom, #000 30%, transparent 100%); filter: blur(2px); pointer-events: none; -webkit-user-select: none; @@ -119,12 +119,12 @@ /* Upsell Card */ .upsell-card { - background: #1a1a1e; + background: var(--bg-base); border-radius: 12px; - border: 1px solid rgba(16, 185, 129, 0.25); + border: 1px solid var(--accent-glow); padding: 1.5rem; margin-top: 1rem; - box-shadow: 0 8px 32px rgba(16, 185, 129, 0.08), 0 4px 12px rgba(0, 0, 0, 0.4); + box-shadow: 0 8px 32px var(--accent-glow), 0 4px 12px var(--border); animation: card-slide-in 0.4s cubic-bezier(0.16, 1, 0.3, 1) forwards; } @@ -141,14 +141,14 @@ .upsell-header h4 { margin: 0; - color: #10b981; + color: var(--accent); font-size: 1.1rem; font-weight: 700; letter-spacing: 0.5px; } .upsell-text { - color: rgba(255, 255, 255, 0.75); + color: var(--text-main); font-size: 0.9rem; line-height: 1.55; margin: 0 0 1.25rem 0; @@ -177,15 +177,16 @@ } .btn-primary { - background: #10b981; + background: var(--accent); border: none; - color: #121214; + color: var(--bg-surface); } .btn-primary:hover:not(:disabled) { - background: #0d9668; + background: var(--accent); + opacity: 0.9; transform: translateY(-2px); - box-shadow: 0 4px 15px rgba(16, 185, 129, 0.3); + box-shadow: 0 4px 15px var(--accent-glow); } .btn-primary:active:not(:disabled) { @@ -193,19 +194,19 @@ } .btn-primary:disabled { - background: rgba(16, 185, 129, 0.5); - color: rgba(18, 18, 20, 0.6); + background: var(--accent-glow); + color: var(--text-muted); cursor: not-allowed; } .btn-secondary { background: transparent; - border: 1px solid #10b981; - color: #10b981; + border: 1px solid var(--accent); + color: var(--accent); } .btn-secondary:hover { - background: rgba(16, 185, 129, 0.05); + background: var(--accent-glow); transform: translateY(-2px); } @@ -218,9 +219,9 @@ display: flex; align-items: center; gap: 0.75rem; - background: rgba(16, 185, 129, 0.1); - border: 1px solid rgba(16, 185, 129, 0.3); - color: #10b981; + background: var(--accent-glow); + border: 1px solid var(--accent); + color: var(--accent); padding: 1rem; border-radius: 8px; margin-top: 1.25rem; @@ -238,8 +239,8 @@ .payment-spinner { width: 16px; height: 16px; - border: 2px solid rgba(18, 18, 20, 0.2); - border-top-color: #121214; + border: 2px solid var(--border); + border-top-color: var(--accent); border-radius: 50%; margin-right: 0.75rem; animation: spin 0.8s linear infinite; diff --git a/src/NexusReader.UI.Shared/Layout/MainHubLayout.razor.css b/src/NexusReader.UI.Shared/Layout/MainHubLayout.razor.css index 3fcda6e..ddb2cf6 100644 --- a/src/NexusReader.UI.Shared/Layout/MainHubLayout.razor.css +++ b/src/NexusReader.UI.Shared/Layout/MainHubLayout.razor.css @@ -2,16 +2,16 @@ display: flex; width: 100vw; height: 100vh; - background: #121214; - color: #e4e4e7; + background: var(--bg-base); + color: var(--text-main); overflow: hidden; } ::deep .hub-sidebar { width: 80px; height: 100%; - background: #0d0d0d; - border-right: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border-right: 1px solid var(--border); display: flex; flex-direction: column; z-index: 100; @@ -55,7 +55,7 @@ justify-content: center; width: 100%; height: 54px; - color: #8b8273; + color: var(--text-muted); text-decoration: none; transition: color 0.2s ease, background-color 0.2s ease; position: relative; @@ -63,7 +63,7 @@ ::deep .nav-item:hover { color: #10b981; - background: rgba(255, 255, 255, 0.01); + background: rgba(0, 0, 0, 0.04); } ::deep .nav-item:focus-visible { @@ -103,7 +103,7 @@ ::deep .sidebar-footer { padding: 1.5rem 0; - border-top: 1px solid rgba(255, 255, 255, 0.05); + border-top: 1px solid var(--border); display: flex; flex-direction: column; align-items: center; @@ -119,15 +119,15 @@ ::deep .user-avatar { width: 36px; height: 36px; - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.08); + background: var(--bg-base); + border: 1px solid var(--border); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 0.9rem; font-weight: 600; - color: #e4e4e7; + color: var(--text-main); flex-shrink: 0; } @@ -138,7 +138,7 @@ ::deep .logout-btn { background: transparent; border: none; - color: #8b8273; + color: var(--text-muted); cursor: pointer; padding: 0.5rem; border-radius: 8px; @@ -157,7 +157,7 @@ flex: 1; height: 100%; overflow-y: auto; - background: #121214; + background: var(--bg-base); } .hub-content { @@ -204,10 +204,10 @@ left: 0; right: 0; height: 60px; - background: rgba(18, 18, 18, 0.85); + background: var(--bg-surface); backdrop-filter: blur(20px); -webkit-backdrop-filter: blur(20px); - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + border-bottom: 1px solid var(--border); padding: 0 1.25rem; z-index: 150; } @@ -295,7 +295,7 @@ bottom: 0; width: 280px; height: 100%; - background: #141414; + background: var(--bg-surface); z-index: 200; transform: translateX(-100%); will-change: transform; @@ -324,7 +324,7 @@ ::deep .sidebar-header { padding: 1.5rem 1.25rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + border-bottom: 1px solid var(--border); } ::deep .sidebar-nav { diff --git a/src/NexusReader.UI.Shared/Pages/Account/Profile.razor.css b/src/NexusReader.UI.Shared/Pages/Account/Profile.razor.css index f0b98c0..e90bc4d 100644 --- a/src/NexusReader.UI.Shared/Pages/Account/Profile.razor.css +++ b/src/NexusReader.UI.Shared/Pages/Account/Profile.razor.css @@ -2,8 +2,8 @@ position: relative; width: 100%; min-height: 100vh; - background-color: #121214; - color: #e4e4e7; + background-color: var(--bg-base); + color: var(--text-main); overflow-x: hidden; display: flex; justify-content: center; @@ -26,7 +26,7 @@ .mesh-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; - background-image: radial-gradient(circle at 1px 1px, rgba(255, 255, 255, 0.02) 1px, transparent 0); + background-image: radial-gradient(circle at 1px 1px, var(--border) 1px, transparent 0); background-size: 32px 32px; z-index: 1; } @@ -63,7 +63,7 @@ .avatar-inner { width: 120px; height: 120px; - background: #1a1a1e; + background: var(--bg-surface); border: 2px solid #10b981; border-radius: 50%; display: flex; @@ -98,7 +98,7 @@ font-weight: 700; margin: 0; letter-spacing: -0.01em; - color: #ffffff; + color: var(--text-main); } .system-rank { @@ -120,17 +120,17 @@ .glass-panel { padding: 32px; - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border: 1px solid var(--border); border-radius: 12px; transition: all 0.3s ease; } .glass-panel:hover { - border-color: rgba(16, 185, 129, 0.2); + border-color: var(--accent); transform: translateY(-4px); - background: #1e1e24; - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); + background: var(--bg-surface); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); } .metric-card { @@ -154,7 +154,7 @@ font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em; - color: #a0aec0; + color: var(--text-muted); margin: 0; } @@ -177,14 +177,14 @@ gap: 8px; } -.usage-values .current { font-size: 2.5rem; font-weight: 800; color: #fff; line-height: 1; } -.usage-values .separator { font-size: 1.2rem; color: #4a5568; } -.usage-values .total { font-size: 1.2rem; color: #718096; font-weight: 600; } +.usage-values .current { font-size: 2.5rem; font-weight: 800; color: var(--text-main); line-height: 1; } +.usage-values .separator { font-size: 1.2rem; color: var(--border); } +.usage-values .total { font-size: 1.2rem; color: var(--text-muted); font-weight: 600; } .usage-progress { width: 100%; height: 6px; - background: rgba(255, 255, 255, 0.05); + background: var(--border); border-radius: 10px; overflow: hidden; } @@ -198,7 +198,7 @@ .metric-label { font-size: 0.75rem; - color: #718096; + color: var(--text-muted); text-transform: uppercase; letter-spacing: 0.05em; } @@ -218,7 +218,7 @@ .score-label { font-size: 0.75rem; - color: #718096; + color: var(--text-muted); text-transform: uppercase; margin-top: 4px; } @@ -229,11 +229,11 @@ align-items: center; gap: 8px; padding: 10px 16px; - background: rgba(16, 185, 129, 0.05); - border: 1px solid rgba(16, 185, 129, 0.1); + background: var(--bg-base); + border: 1px solid var(--border); border-radius: 12px; font-size: 0.85rem; - color: #cbd5e0; + color: var(--text-main); } .truncate { @@ -273,9 +273,9 @@ } .plan-badge.free { - background: rgba(255, 255, 255, 0.05); - color: #a1a1aa; - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--bg-base); + color: var(--text-muted); + border: 1px solid var(--border); } .tenant-tag { @@ -356,7 +356,7 @@ .theme-description { font-size: 0.9rem; - color: #a0aec0; + color: var(--text-muted); margin: 0 0 16px 0; } @@ -373,10 +373,10 @@ justify-content: center; gap: 8px; padding: 14px 20px; - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.08); + background: var(--bg-base); + border: 1px solid var(--border); border-radius: 8px; - color: #a0aec0; + color: var(--text-muted); font-size: 0.9rem; font-weight: 600; cursor: pointer; @@ -384,9 +384,8 @@ } .theme-option-btn:hover { - background: rgba(255, 255, 255, 0.07); - color: #ffffff; - border-color: rgba(255, 255, 255, 0.15); + background: rgba(0, 0, 0, 0.04); + color: var(--text-main); } .theme-option-btn.active { @@ -396,64 +395,6 @@ box-shadow: 0 0 15px rgba(16, 185, 129, 0.15); } -/* Light Theme overrides for Profile settings page */ -.theme-light .profile-page-container { - background-color: var(--bg-base); - color: var(--text-main); -} - -.theme-light .username { - color: var(--text-main); -} - -.theme-light .glass-panel { - background: var(--bg-surface); - border-color: rgba(0, 0, 0, 0.06); -} - -.theme-light .glass-panel:hover { - border-color: rgba(16, 185, 129, 0.3); - background: var(--bg-surface); - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.04); -} - -.theme-light .card-header h3 { - color: #718096; -} - -.theme-light .usage-values .current { - color: var(--text-main); -} - -.theme-light .last-book { - background: rgba(16, 185, 129, 0.05); - border-color: rgba(16, 185, 129, 0.15); - color: var(--text-main); -} - -.theme-light .theme-description { - color: #718096; -} - -.theme-light .theme-option-btn { - background: rgba(0, 0, 0, 0.03); - border-color: rgba(0, 0, 0, 0.08); - color: #718096; -} - -.theme-light .theme-option-btn:hover { - background: rgba(0, 0, 0, 0.06); - color: var(--text-main); - border-color: rgba(0, 0, 0, 0.15); -} - -.theme-light .theme-option-btn.active { - background: rgba(16, 185, 129, 0.08); - color: #10b981; - border-color: #10b981; - box-shadow: 0 0 15px rgba(16, 185, 129, 0.1); -} - @media (max-width: 768px) { .theme-options { flex-direction: column; diff --git a/src/NexusReader.UI.Shared/Pages/Catalog.razor.css b/src/NexusReader.UI.Shared/Pages/Catalog.razor.css index 976681c..1d1ed3d 100644 --- a/src/NexusReader.UI.Shared/Pages/Catalog.razor.css +++ b/src/NexusReader.UI.Shared/Pages/Catalog.razor.css @@ -14,13 +14,13 @@ font-size: 2.5rem; font-weight: 700; margin: 0 0 0.5rem 0; - color: #ffffff; + color: var(--text-main); letter-spacing: -0.5px; } .catalog-header .subtitle { font-size: 1rem; - color: #a1a1aa; + color: var(--text-muted); margin: 0; } @@ -38,27 +38,27 @@ height: 100%; overflow: hidden; border-radius: 12px; - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border: 1px solid var(--border); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); position: relative; } .course-card:hover { transform: translateY(-4px); - box-shadow: 0 12px 30px rgba(0, 0, 0, 0.3); - border-color: rgba(16, 185, 129, 0.2); + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1); + border-color: var(--accent); } .card-cover-container { position: relative; height: 200px; - background: rgba(0, 0, 0, 0.2); + background: rgba(0, 0, 0, 0.05); overflow: hidden; display: flex; align-items: center; justify-content: center; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + border-bottom: 1px solid var(--border); } .card-cover { @@ -147,8 +147,9 @@ align-self: flex-start; font-size: 0.7rem; font-weight: 700; - color: #a1a1aa; - background: rgba(255, 255, 255, 0.05); + color: var(--text-muted); + background: var(--bg-base); + border: 1px solid var(--border); padding: 0.2rem 0.5rem; border-radius: 4px; text-transform: uppercase; @@ -175,21 +176,21 @@ font-size: 1.25rem; font-weight: 600; margin: 0 0 0.4rem 0; - color: #ffffff; + color: var(--text-main); line-height: 1.3; font-family: var(--nexus-font-sans, "Outfit", sans-serif); } .course-author { font-size: 0.85rem; - color: #a1a1aa; + color: var(--text-muted); margin: 0 0 1rem 0; } .course-desc { font-size: 0.88rem; line-height: 1.5; - color: #a1a1aa; + color: var(--text-muted); margin: 0 0 1.5rem 0; display: -webkit-box; -webkit-line-clamp: 3; @@ -204,8 +205,8 @@ justify-content: space-between; align-items: center; font-size: 0.8rem; - color: #a1a1aa; - border-top: 1px solid rgba(255, 255, 255, 0.05); + color: var(--text-muted); + border-top: 1px solid var(--border); padding-top: 0.75rem; } @@ -256,14 +257,14 @@ border-radius: 12px; overflow: hidden; height: 440px; - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border: 1px solid var(--border); opacity: 0.6; } .skeleton-cover { height: 200px; - background: linear-gradient(90deg, rgba(255,255,255,0.02) 25%, rgba(255,255,255,0.06) 50%, rgba(255,255,255,0.02) 75%); + background: linear-gradient(90deg, var(--bg-base) 25%, var(--border) 50%, var(--bg-base) 75%); background-size: 200% 100%; animation: loading 1.5s infinite; } @@ -276,7 +277,7 @@ } .skeleton-line { - background: linear-gradient(90deg, rgba(255,255,255,0.02) 25%, rgba(255,255,255,0.06) 50%, rgba(255,255,255,0.02) 75%); + background: linear-gradient(90deg, var(--bg-base) 25%, var(--border) 50%, var(--bg-base) 75%); background-size: 200% 100%; animation: loading 1.5s infinite; border-radius: 4px; @@ -314,9 +315,9 @@ gap: 1.25rem; padding: 1.25rem 2.25rem; border-radius: 40px; - background: rgba(13, 13, 15, 0.85); + background: var(--bg-surface); backdrop-filter: blur(16px); - border: 1px solid rgba(255, 255, 255, 0.08); + border: 1px solid var(--border); animation: scaleIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); } @@ -332,7 +333,7 @@ .loader-text { font-weight: 500; - color: #ffffff; + color: var(--text-main); font-size: 0.95rem; } diff --git a/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor.css b/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor.css index 53edb26..e5131aa 100644 --- a/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor.css +++ b/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor.css @@ -17,11 +17,11 @@ align-items: center; gap: 2rem; padding: 1.25rem 2rem; - background: rgba(20, 20, 20, 0.35); - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border: 1px solid var(--border); border-radius: 16px; backdrop-filter: blur(12px); - box-shadow: 0 4px 30px rgba(0, 0, 0, 0.2); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.05); } .header-back .btn-back { @@ -30,22 +30,22 @@ } .header-back .btn-back:hover { - border-color: var(--nexus-neon); - color: var(--nexus-neon); - background: var(--nexus-primary-glow); - box-shadow: 0 0 10px var(--nexus-primary-glow); + border-color: var(--accent); + color: var(--accent); + background: var(--accent-glow); + box-shadow: 0 0 10px var(--accent-glow); } .header-title h1 { margin: 0; font-size: 1.5rem; font-weight: 700; - color: #fff; + color: var(--text-main); } .header-title .subtitle { font-size: 0.85rem; - color: rgba(255, 255, 255, 0.4); + color: var(--text-muted); } .header-actions .btn-action { @@ -56,7 +56,7 @@ } .header-actions .btn-action:hover { - box-shadow: 0 0 20px var(--nexus-primary-glow); + box-shadow: 0 0 20px var(--accent-glow); } /* Grid Layout */ @@ -73,28 +73,26 @@ flex-direction: column; overflow: hidden; padding: 0; + background: var(--bg-surface); + border: 1px solid var(--border); + border-radius: var(--radius-xl, 16px); } .pane-header { padding: 1.25rem 1.5rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + border-bottom: 1px solid var(--border); } .pane-header h3 { margin: 0; font-size: 1rem; font-weight: 600; - color: #fff; + color: var(--text-main); display: flex; align-items: center; gap: 0.5rem; } -.pane-content { - flex-grow: 1; - overflow: hidden; -} - /* Loading, Error and Empty States */ .loading-state, .error-state, .empty-dashboard-state { display: flex; @@ -118,15 +116,15 @@ } .neon-pulse { - color: var(--nexus-neon); - filter: drop-shadow(0 0 10px var(--nexus-neon)); + color: var(--accent); + filter: drop-shadow(0 0 10px var(--accent-glow)); animation: robot-pulse 2s infinite ease-in-out; } @keyframes robot-pulse { - 0% { transform: scale(1); filter: drop-shadow(0 0 10px var(--nexus-neon)); } - 50% { transform: scale(1.08); filter: drop-shadow(0 0 25px var(--nexus-neon)); } - 100% { transform: scale(1); filter: drop-shadow(0 0 10px var(--nexus-neon)); } + 0% { transform: scale(1); filter: drop-shadow(0 0 10px var(--accent-glow)); } + 50% { transform: scale(1.08); filter: drop-shadow(0 0 25px var(--accent-glow)); } + 100% { transform: scale(1); filter: drop-shadow(0 0 10px var(--accent-glow)); } } .scan-line { @@ -135,8 +133,8 @@ left: 0; width: 100%; height: 2px; - background: var(--nexus-neon); - box-shadow: 0 0 15px var(--nexus-neon); + background: var(--accent); + box-shadow: 0 0 15px var(--accent); animation: scan 2s infinite linear; opacity: 0.8; } @@ -149,7 +147,7 @@ .loading-text { font-size: 0.95rem; - color: rgba(255, 255, 255, 0.7); + color: var(--text-muted); margin-top: 1rem; letter-spacing: 0.05em; } @@ -164,17 +162,18 @@ } .dim-icon { - color: rgba(255, 255, 255, 0.15); + color: var(--text-muted); + opacity: 0.4; } .empty-dashboard-state h2, .error-state h3 { - color: #fff; + color: var(--text-main); margin: 0 0 0.75rem 0; font-weight: 600; } .empty-dashboard-state p, .error-state p { - color: rgba(255, 255, 255, 0.45); + color: var(--text-muted); font-size: 0.88rem; line-height: 1.5; margin: 0 0 2rem 0; @@ -189,25 +188,25 @@ flex-grow: 1; padding: 3rem; text-align: center; - color: rgba(255, 255, 255, 0.4); + color: var(--text-muted); } .empty-glowing-brain { width: 80px; height: 80px; border-radius: 50%; - background: rgba(0, 255, 153, 0.04); - border: 1px solid rgba(0, 255, 153, 0.15); + background: var(--accent-glow); + border: 1px solid var(--accent); display: flex; align-items: center; justify-content: center; margin-bottom: 1.5rem; - box-shadow: 0 0 20px var(--nexus-primary-glow); + box-shadow: 0 0 20px var(--accent-glow); } .workspace-empty h4 { margin: 0 0 0.75rem 0; - color: #fff; + color: var(--text-main); font-size: 1.1rem; font-weight: 600; } @@ -227,7 +226,7 @@ .workspace-header { padding: 1.5rem; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + border-bottom: 1px solid var(--border); } .node-meta { @@ -242,7 +241,7 @@ font-size: 0.75rem; font-weight: 700; letter-spacing: 0.08em; - color: var(--nexus-neon); + color: var(--accent); } .badge { @@ -256,22 +255,22 @@ } .badge-unlocked { - background: rgba(0, 255, 153, 0.08); - color: var(--nexus-neon); - border: 1px solid rgba(0, 255, 153, 0.2); + background: var(--accent-glow); + color: var(--accent); + border: 1px solid var(--accent); } .badge-locked { - background: rgba(255, 255, 255, 0.05); - color: rgba(255, 255, 255, 0.4); - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-base); + color: var(--text-muted); + border: 1px solid var(--border); } .workspace-title { margin: 0; font-size: 1.4rem; font-weight: 700; - color: #fff; + color: var(--text-main); } .workspace-body { @@ -291,22 +290,22 @@ background: transparent; } .workspace-body::-webkit-scrollbar-thumb { - background: rgba(255, 255, 255, 0.08); + background: var(--border); border-radius: 3px; } .workspace-body::-webkit-scrollbar-thumb:hover { - background: var(--nexus-neon); + background: var(--accent); } .locked-warning { display: flex; flex-direction: row; gap: 1rem; - background: rgba(255, 171, 0, 0.04); - border: 1px solid rgba(255, 171, 0, 0.15); + background: rgba(217, 119, 6, 0.05); + border: 1px solid rgba(217, 119, 6, 0.15); border-radius: 8px; padding: 1rem; - color: rgba(255, 255, 255, 0.85); + color: var(--text-main); } .lock-warning-icon { @@ -326,14 +325,14 @@ margin: 0; font-size: 0.8rem; line-height: 1.4; - color: rgba(255, 255, 255, 0.55); + color: var(--text-muted); } .metadata-section h4 { margin: 0 0 0.5rem 0; font-size: 0.85rem; font-weight: 600; - color: #aaa; + color: var(--text-muted); display: flex; align-items: center; gap: 0.35rem; @@ -345,12 +344,12 @@ margin: 0; font-size: 0.88rem; line-height: 1.6; - color: rgba(255, 255, 255, 0.7); + color: var(--text-main); } .summary-box { - background: rgba(255, 255, 255, 0.02); - border-left: 3px solid var(--nexus-neon); + background: var(--bg-base); + border-left: 3px solid var(--accent); border-radius: 0 8px 8px 0; padding: 1rem; margin-top: 0.25rem; @@ -365,9 +364,9 @@ .term-pill { font-size: 0.75rem; - background: rgba(255, 255, 255, 0.03); - border: 1px solid rgba(255, 255, 255, 0.05); - color: rgba(255, 255, 255, 0.6); + background: var(--bg-base); + border: 1px solid var(--border); + color: var(--text-muted); padding: 0.3rem 0.75rem; border-radius: 20px; font-weight: 500; @@ -375,14 +374,14 @@ } .term-pill:hover { - border-color: rgba(0, 255, 153, 0.2); - color: var(--nexus-neon); - background: rgba(0, 255, 153, 0.03); + border-color: var(--accent); + color: var(--accent); + background: var(--accent-glow); } .workspace-footer { padding: 1.25rem 1.5rem; - border-top: 1px solid rgba(255, 255, 255, 0.05); + border-top: 1px solid var(--border); } @media (max-width: 1024px) { diff --git a/src/NexusReader.UI.Shared/Pages/Dashboard.razor.css b/src/NexusReader.UI.Shared/Pages/Dashboard.razor.css index 7794270..ddf1ec9 100644 --- a/src/NexusReader.UI.Shared/Pages/Dashboard.razor.css +++ b/src/NexusReader.UI.Shared/Pages/Dashboard.razor.css @@ -17,16 +17,16 @@ display: flex; justify-content: center; overflow: hidden; - background: #0d0d0d; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border-bottom: 1px solid var(--border); } .header-grid-bg { position: absolute; inset: 0; background-image: - linear-gradient(rgba(255,255,255,0.03) 1px, transparent 1px), - linear-gradient(90deg, rgba(255,255,255,0.03) 1px, transparent 1px); + linear-gradient(var(--border) 1px, transparent 1px), + linear-gradient(90deg, var(--border) 1px, transparent 1px); background-size: 60px 60px; background-position: center; mask-image: radial-gradient(circle at center, black, transparent 80%); @@ -52,10 +52,10 @@ height: 100%; border-radius: 50%; object-fit: cover; - border: 3px solid #1a1a1a; + border: 3px solid var(--border); position: relative; z-index: 2; - background: #222; + background: var(--bg-surface); } .avatar-glow { @@ -78,7 +78,7 @@ font-family: var(--nexus-font-sans); font-size: 1.25rem; font-weight: 500; - color: #ffffff; + color: var(--text-main); letter-spacing: 1px; text-transform: lowercase; } @@ -103,17 +103,17 @@ .status-pill { padding: 0.6rem 1.25rem; - background: rgba(16, 185, 129, 0.05); - border: 1px solid rgba(16, 185, 129, 0.3); + background: var(--bg-base); + border: 1px solid var(--border); border-radius: 100px; display: flex; gap: 0.5rem; font-size: 0.9rem; - box-shadow: 0 0 15px rgba(16, 185, 129, 0.1); + box-shadow: 0 0 15px rgba(16, 185, 129, 0.05); } -.pill-label { color: #A0A0A0; } -.pill-value { color: #ffffff; font-weight: 600; } +.pill-label { color: var(--text-muted); } +.pill-value { color: var(--text-main); font-weight: 600; } /* --- Dashboard Content --- */ .dashboard-content { @@ -127,7 +127,7 @@ font-family: var(--nexus-font-serif); font-size: 2rem; margin-bottom: 2rem; - color: #ffffff; + color: var(--text-main); } .main-grid { @@ -137,18 +137,18 @@ } .glass-panel { - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border: 1px solid var(--border); border-radius: 12px; padding: 1.5rem; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); } .glass-panel:hover { - background: #1e1e24; - border-color: rgba(16, 185, 129, 0.2); + background: var(--bg-surface); + border-color: var(--accent); transform: translateY(-4px); - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.3); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1); } /* Reading Card */ @@ -161,7 +161,7 @@ .reading-card h3 { font-size: 1.1rem; font-weight: 600; - color: #E0E0E0; + color: var(--text-main); margin: 0; } @@ -178,7 +178,7 @@ .reading-thumb img { width: 100%; border-radius: 12px; - box-shadow: 0 10px 30px rgba(0,0,0,0.5); + box-shadow: 0 10px 30px rgba(0,0,0,0.15); } .reading-info { @@ -196,12 +196,12 @@ .chapter-label { font-size: 0.85rem; - color: #A0A0A0; + color: var(--text-muted); } .progress-container { height: 8px; - background: rgba(255, 255, 255, 0.05); + background: var(--border); border-radius: 4px; position: relative; } @@ -228,13 +228,13 @@ .progress-detail { font-size: 0.8rem; - color: #666; + color: var(--text-muted); } .reading-desc { font-size: 0.85rem; line-height: 1.6; - color: #888; + color: var(--text-muted); margin: 0; } @@ -261,7 +261,7 @@ margin: 0; font-size: 1rem; font-weight: 600; - color: #E0E0E0; + color: var(--text-main); } /* Graph Placeholder */ @@ -325,7 +325,7 @@ .question { font-size: 0.95rem; - color: #E0E0E0; + color: var(--text-main); } .quiz-options { @@ -336,13 +336,14 @@ .quiz-option { padding: 0.75rem 1rem; - background: rgba(255, 255, 255, 0.02); - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-base); + border: 1px solid var(--border); border-radius: 10px; font-size: 0.9rem; display: flex; gap: 0.75rem; cursor: pointer; + color: var(--text-main); } .quiz-option.active { @@ -372,9 +373,9 @@ } .btn-nexus.secondary { - background: rgba(255, 255, 255, 0.05); - color: #fff; - border: 1px solid rgba(255, 255, 255, 0.1); + background: var(--bg-base); + color: var(--text-main); + border: 1px solid var(--border); } .btn-nexus:hover { @@ -417,16 +418,16 @@ } .quiz-history-item { - background: rgba(255, 255, 255, 0.02); - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border: 1px solid var(--border); border-radius: 12px; padding: 1rem; transition: all 0.2s ease; } .quiz-history-item:hover { - background: rgba(255, 255, 255, 0.04); - border-color: rgba(255, 255, 255, 0.1); + background: var(--bg-base); + border-color: var(--border); } .quiz-item-header { @@ -440,13 +441,13 @@ .quiz-topic { font-size: 0.95rem; font-weight: 500; - color: #ffffff; + color: var(--text-main); } .quiz-item-meta { display: flex; font-size: 0.75rem; - color: #666666; + color: var(--text-muted); } .badge { @@ -481,7 +482,7 @@ .empty-quiz-state .sub-text { font-size: 0.8rem; - color: #666666; + color: var(--text-muted); margin-top: 0.5rem; } @@ -489,8 +490,8 @@ .concept-detail-toast { margin-top: 1rem; padding: 0.75rem 1rem; - background: rgba(255, 255, 255, 0.02); - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-base); + border: 1px solid var(--border); border-radius: 12px; min-height: 80px; display: flex; @@ -514,7 +515,7 @@ .concept-content { font-size: 0.85rem; line-height: 1.4; - color: #E0E0E0; + color: var(--text-main); margin: 0; display: -webkit-box; -webkit-line-clamp: 2; @@ -603,8 +604,8 @@ /* --- Architecture Guide Block --- */ .architecture-guide-panel { margin-top: 2.5rem; - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border: 1px solid var(--border); border-radius: 12px; padding: 2rem; } @@ -618,7 +619,7 @@ .architecture-content h3 { font-size: 1.5rem; font-weight: 700; - color: #ffffff; + color: var(--text-main); margin-bottom: 1.25rem; letter-spacing: -0.01em; } @@ -626,12 +627,12 @@ .architecture-content p { font-size: 0.95rem; line-height: 1.6; - color: #e4e4e7; + color: var(--text-main); margin-bottom: 1.25rem; } .architecture-content code { - background: rgba(255, 255, 255, 0.05); + background: var(--bg-base); color: #10b981; padding: 0.2rem 0.4rem; border-radius: 4px; diff --git a/src/NexusReader.UI.Shared/Pages/Intelligence.razor.css b/src/NexusReader.UI.Shared/Pages/Intelligence.razor.css index faf7c44..1594c97 100644 --- a/src/NexusReader.UI.Shared/Pages/Intelligence.razor.css +++ b/src/NexusReader.UI.Shared/Pages/Intelligence.razor.css @@ -1,7 +1,7 @@ .intelligence-page { margin: -2.5rem; height: 100vh; - background: #121214; + background: var(--bg-base); display: flex; flex-direction: column; overflow: hidden; @@ -45,11 +45,11 @@ background: transparent; } .chat-thread-container::-webkit-scrollbar-thumb { - background: rgba(16, 185, 129, 0.2); + background: var(--accent-glow); border-radius: 4px; } .chat-thread-container::-webkit-scrollbar-thumb:hover { - background: rgba(16, 185, 129, 0.4); + background: var(--accent); } .chat-bubbles-scroll { @@ -78,7 +78,7 @@ .welcome-prompt { font-family: var(--nexus-font-sans, inherit); - color: #e4e4e7; + color: var(--text-main); font-size: 1.35rem; font-weight: 500; letter-spacing: -0.2px; @@ -87,7 +87,7 @@ /* Input Controls */ .chat-input-controls { padding: 1.5rem 4rem 3rem 4rem; - background: linear-gradient(to top, #121214 70%, rgba(18, 18, 20, 0)); + background: linear-gradient(to top, var(--bg-base) 70%, transparent); flex-shrink: 0; } @@ -117,13 +117,13 @@ gap: 0.6rem; font-size: 0.85rem; font-weight: 500; - color: #8b8273; + color: var(--text-muted); } .nexus-select { - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.06); - color: #e4e4e7; + background: var(--bg-surface); + border: 1px solid var(--border); + color: var(--text-main); padding: 0.4rem 2rem 0.4rem 0.75rem; border-radius: 8px; outline: none; @@ -138,38 +138,38 @@ } .nexus-select:focus { - border-color: #10b981; - box-shadow: 0 0 8px rgba(16, 185, 129, 0.15); + border-color: var(--accent); + box-shadow: 0 0 8px var(--accent-glow); } .input-field-group { display: flex; - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.06); + background: var(--bg-surface); + border: 1px solid var(--border); border-radius: 12px; padding: 0.4rem; transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); - box-shadow: 0 10px 30px rgba(0, 0, 0, 0.2); + box-shadow: 0 10px 30px rgba(0, 0, 0, 0.05); } .input-field-group:focus-within { - border-color: rgba(16, 185, 129, 0.5); - background: #1a1a1e; - box-shadow: 0 10px 35px rgba(16, 185, 129, 0.1); + border-color: var(--accent); + background: var(--bg-surface); + box-shadow: 0 10px 35px var(--accent-glow); } .nexus-input { flex-grow: 1; background: transparent; border: none; - color: #ffffff; + color: var(--text-main); font-size: 0.975rem; outline: none; padding: 0.5rem 1rem; } .nexus-input::placeholder { - color: #8b8273; + color: var(--text-muted); } .search-btn { @@ -180,29 +180,31 @@ align-items: center; justify-content: center; border-radius: 8px; - background: #10b981; + background: var(--accent); border: none; - color: #121214; + color: var(--bg-surface); cursor: pointer; transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1); } .search-btn:hover:not(:disabled) { - background: #0d9668; + background: var(--accent); + opacity: 0.9; transform: scale(1.02); } .search-btn:disabled { - background: rgba(26, 26, 30, 0.8); - color: rgba(255, 255, 255, 0.2); - border: 1px solid rgba(255, 255, 255, 0.02); + background: var(--bg-base); + color: var(--text-muted); + opacity: 0.4; + border: 1px solid var(--border); cursor: not-allowed; } /* Typing / Loading Indicators */ .message-bubble.pending-bubble { - border-color: rgba(16, 185, 129, 0.25); - background: rgba(16, 185, 129, 0.03); + border-color: var(--accent-glow); + background: var(--accent-glow); max-width: 450px; } @@ -216,7 +218,7 @@ .typing-indicator span { width: 7px; height: 7px; - background: #10b981; + background: var(--accent); border-radius: 50%; display: inline-block; animation: typing-bounce 1.4s infinite ease-in-out both; @@ -227,16 +229,16 @@ .loading-label { font-size: 0.825rem; - color: rgba(255, 255, 255, 0.45); + color: var(--text-muted); font-style: italic; } .btn-spinner { width: 18px; height: 18px; - border: 2px solid rgba(18, 18, 20, 0.1); + border: 2px solid var(--border); border-radius: 50%; - border-top-color: #121214; + border-top-color: var(--accent); animation: spin 0.8s linear infinite; } diff --git a/src/NexusReader.UI.Shared/Pages/MyBooks.razor.css b/src/NexusReader.UI.Shared/Pages/MyBooks.razor.css index 3f3b0d6..5ddab46 100644 --- a/src/NexusReader.UI.Shared/Pages/MyBooks.razor.css +++ b/src/NexusReader.UI.Shared/Pages/MyBooks.razor.css @@ -19,13 +19,13 @@ font-size: 2.5rem; font-weight: 700; margin: 0 0 0.5rem 0; - color: #ffffff; + color: var(--text-main); letter-spacing: -0.5px; } .header-title-section .subtitle { font-size: 1rem; - color: #a1a1aa; + color: var(--text-muted); margin: 0; } @@ -67,27 +67,27 @@ height: 100%; overflow: hidden; border-radius: 12px; - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border: 1px solid var(--border); transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); position: relative; } .book-card:hover { transform: translateY(-4px); - box-shadow: 0 12px 30px rgba(0, 0, 0, 0.3); - border-color: rgba(16, 185, 129, 0.2); + box-shadow: 0 12px 30px rgba(0, 0, 0, 0.1); + border-color: var(--accent); } .book-cover-container { position: relative; height: 360px; - background: rgba(0, 0, 0, 0.2); + background: rgba(0, 0, 0, 0.05); overflow: hidden; display: flex; align-items: center; justify-content: center; - border-bottom: 1px solid rgba(255, 255, 255, 0.05); + border-bottom: 1px solid var(--border); } .book-cover { @@ -145,7 +145,7 @@ font-size: 1.2rem; font-weight: 600; margin: 0 0 0.4rem 0; - color: #ffffff; + color: var(--text-main); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; @@ -154,7 +154,7 @@ .book-author { font-size: 0.9rem; - color: #a1a1aa; + color: var(--text-muted); margin: 0 0 1.25rem 0; } @@ -179,7 +179,7 @@ .progress-bar { height: 6px; - background: rgba(255, 255, 255, 0.05); + background: var(--border); border-radius: 3px; overflow: hidden; } @@ -192,7 +192,7 @@ .progress-text { font-size: 0.8rem; - color: #a1a1aa; + color: var(--text-muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; @@ -232,14 +232,14 @@ justify-content: center; padding: 5rem 2rem; text-align: center; - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border: 1px solid var(--border); border-radius: 12px; } .empty-icon-pulse { margin-bottom: 2rem; - color: #a1a1aa; + color: var(--text-muted); animation: pulse 3s infinite alternate; } @@ -247,11 +247,11 @@ font-family: var(--nexus-font-serif); font-size: 1.8rem; margin: 0 0 0.5rem 0; - color: #ffffff; + color: var(--text-main); } .empty-state-container p { - color: #a1a1aa; + color: var(--text-muted); max-width: 400px; margin: 0 0 2rem 0; } @@ -278,14 +278,14 @@ border-radius: 12px; overflow: hidden; height: 480px; - background: #1a1a1e; - border: 1px solid rgba(255, 255, 255, 0.05); + background: var(--bg-surface); + border: 1px solid var(--border); opacity: 0.6; } .skeleton-cover { height: 360px; - background: linear-gradient(90deg, rgba(255,255,255,0.02) 25%, rgba(255,255,255,0.06) 50%, rgba(255,255,255,0.02) 75%); + background: linear-gradient(90deg, var(--bg-base) 25%, var(--border) 50%, var(--bg-base) 75%); background-size: 200% 100%; animation: loading 1.5s infinite; } @@ -298,7 +298,7 @@ } .skeleton-line { - background: linear-gradient(90deg, rgba(255,255,255,0.02) 25%, rgba(255,255,255,0.06) 50%, rgba(255,255,255,0.02) 75%); + background: linear-gradient(90deg, var(--bg-base) 25%, var(--border) 50%, var(--bg-base) 75%); background-size: 200% 100%; animation: loading 1.5s infinite; border-radius: 4px; @@ -336,9 +336,9 @@ gap: 1.25rem; padding: 1.25rem 2.25rem; border-radius: 40px; - background: rgba(13, 13, 15, 0.85); + background: var(--bg-surface); backdrop-filter: blur(16px); - border: 1px solid rgba(255, 255, 255, 0.08); + border: 1px solid var(--border); animation: scaleIn 0.3s cubic-bezier(0.34, 1.56, 0.64, 1); } @@ -354,7 +354,7 @@ .loader-text { font-weight: 500; - color: #ffffff; + color: var(--text-main); font-size: 0.95rem; } -- 2.52.0 From ce923ab72a3d931a258eed99cd214288416311a7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marek=20Jasi=C5=84ski?= Date: Sun, 7 Jun 2026 18:29:56 +0200 Subject: [PATCH 10/10] style: refactor light theme CSS overrides to use Blazor isolated scoping --- .../Molecules/AiResponseRenderer.razor.css | 46 +++++++ .../Organisms/ConceptsMap.razor.css | 123 ++++++++++++++++++ .../ContextualRecommendationsWidget.razor.css | 77 +++++++++++ .../Organisms/CurrentReadingWidget.razor.css | 57 ++++++++ .../Layout/MainHubLayout.razor.css | 76 +++++++++++ .../Pages/Account/Profile.razor.css | 56 ++++++++ .../Pages/Catalog.razor.css | 31 +++++ .../Pages/ConceptsDashboard.razor.css | 51 ++++++++ .../Pages/Dashboard.razor.css | 87 +++++++++++++ .../Pages/Intelligence.razor.css | 63 +++++++++ .../Pages/MyBooks.razor.css | 24 ++++ .../Pages/Settings.razor.css | 37 ++++++ 12 files changed, 728 insertions(+) diff --git a/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css b/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css index 7fcb266..09c66a8 100644 --- a/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css +++ b/src/NexusReader.UI.Shared/Components/Molecules/AiResponseRenderer.razor.css @@ -266,3 +266,49 @@ 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } + +/* ============================================================ + LIGHT THEME OVERRIDES — "Warm Paper / Soft Sepia" + ============================================================ */ + +.theme-light .ai-row .message-avatar { + background: linear-gradient(135deg, #10b981 0%, #059669 100%); + color: #ffffff; + border: 1px solid rgba(16, 185, 129, 0.2); + box-shadow: 0 2px 8px rgba(16, 185, 129, 0.15); +} + +.theme-light .user-row .message-avatar { + box-shadow: 0 2px 8px rgba(139, 130, 115, 0.1); +} + +.theme-light .upsell-card { + box-shadow: 0 8px 32px rgba(16, 185, 129, 0.08), 0 4px 12px rgba(0, 0, 0, 0.04); +} + +.theme-light .btn-primary { + background: var(--accent); + color: #ffffff; +} + +.theme-light .btn-primary:hover:not(:disabled) { + background: #059669; + color: #ffffff; + opacity: 1; + box-shadow: 0 4px 12px rgba(16, 185, 129, 0.15); +} + +.theme-light .btn-secondary { + border-color: var(--accent); + color: var(--accent); +} + +.theme-light .btn-secondary:hover { + background: rgba(16, 185, 129, 0.05); +} + +.theme-light .paywall-teaser { + -webkit-mask-image: linear-gradient(to bottom, #000 30%, transparent 100%); + mask-image: linear-gradient(to bottom, #000 30%, transparent 100%); +} + diff --git a/src/NexusReader.UI.Shared/Components/Organisms/ConceptsMap.razor.css b/src/NexusReader.UI.Shared/Components/Organisms/ConceptsMap.razor.css index 47220b5..9b69e69 100644 --- a/src/NexusReader.UI.Shared/Components/Organisms/ConceptsMap.razor.css +++ b/src/NexusReader.UI.Shared/Components/Organisms/ConceptsMap.razor.css @@ -233,3 +233,126 @@ .lock-icon { color: rgba(255, 255, 255, 0.2); } + +/* ============================================================ + LIGHT THEME OVERRIDES — "Warm Paper / Soft Sepia" + ============================================================ */ + +.theme-light .concepts-map::-webkit-scrollbar-thumb:hover { + background: var(--accent); +} + +.theme-light .empty-map-state { + background: rgba(0, 0, 0, 0.01); + border-color: var(--border); + color: var(--text-muted); +} + +.theme-light .empty-map-state .dim-icon { + color: var(--text-muted); + opacity: 0.4; +} + +.theme-light .timeline-step:hover { + background: rgba(0, 0, 0, 0.02); +} + +.theme-light .timeline-step.unlocked:hover { + border-color: rgba(16, 185, 129, 0.15); + box-shadow: 0 4px 20px rgba(16, 185, 129, 0.05); +} + +.theme-light .timeline-step.selected { + background: rgba(16, 185, 129, 0.04); + border-color: var(--accent); + box-shadow: 0 0 12px rgba(16, 185, 129, 0.15); +} + +.theme-light .node-circle { + background: var(--bg-surface); +} + +.theme-light .unlocked .node-circle { + background: var(--bg-surface); + border-color: var(--accent); + color: var(--accent); + box-shadow: none; +} + +.theme-light .locked .node-circle { + background: var(--bg-base); + border-color: var(--border); + color: var(--text-muted); +} + +.theme-light .node-glow { + display: none; +} + +.theme-light .track-active { + background: var(--accent); + box-shadow: none; +} + +.theme-light .track-inactive { + background: var(--border); +} + +.theme-light .node-content { + background: var(--bg-surface); + border: 1px solid var(--border); +} + +.theme-light .timeline-step.selected .node-content { + background: var(--bg-surface); + border-color: rgba(16, 185, 129, 0.2); +} + +.theme-light .segment-tag { + color: var(--text-muted); +} + +.theme-light .unlocked .segment-tag { + color: var(--accent); +} + +.theme-light .badge-unlocked { + background: rgba(16, 185, 129, 0.08); + color: var(--accent); + border-color: rgba(16, 185, 129, 0.2); +} + +.theme-light .badge-locked { + background: var(--bg-base); + color: var(--text-muted); + border-color: var(--border); +} + +.theme-light .node-title { + color: var(--text-main); +} + +.theme-light .timeline-step.unlocked:hover .node-title { + color: var(--accent); +} + +.theme-light .locked .node-title { + color: var(--text-muted); +} + +.theme-light .node-desc { + color: var(--text-muted); +} + +.theme-light .locked .node-desc { + color: var(--text-muted); +} + +.theme-light .check-icon { + color: var(--accent); +} + +.theme-light .lock-icon { + color: var(--text-muted); +} + diff --git a/src/NexusReader.UI.Shared/Components/Organisms/ContextualRecommendationsWidget.razor.css b/src/NexusReader.UI.Shared/Components/Organisms/ContextualRecommendationsWidget.razor.css index 4718152..a014fe9 100644 --- a/src/NexusReader.UI.Shared/Components/Organisms/ContextualRecommendationsWidget.razor.css +++ b/src/NexusReader.UI.Shared/Components/Organisms/ContextualRecommendationsWidget.razor.css @@ -251,3 +251,80 @@ padding: 1.25rem; } } + +/* ============================================================ + LIGHT THEME OVERRIDES — "Warm Paper / Soft Sepia" + ============================================================ */ + +.theme-light .recommendations-panel { + background: var(--bg-surface); + border: 1px solid var(--border); +} + +.theme-light .recommendations-panel:hover { + box-shadow: 0 8px 24px rgba(139, 130, 115, 0.12); +} + +.theme-light .header-left h4 { + color: var(--text-main); +} + +.theme-light .spinner-track { + border: 3px solid rgba(0, 0, 0, 0.05); +} + +.theme-light .loading-label, +.theme-light .empty-state { + color: var(--text-muted); +} + +.theme-light .recommendation-item { + background: rgba(0, 0, 0, 0.02); + border: 1px solid rgba(0, 0, 0, 0.06); +} + +.theme-light .recommendation-item:hover { + background: rgba(0, 0, 0, 0.04); +} + +.theme-light .recommendation-item.premium { + border-color: rgba(245, 158, 11, 0.2); +} + +.theme-light .recommendation-item.premium:hover { + border-color: rgba(245, 158, 11, 0.4); + background: rgba(245, 158, 11, 0.04); +} + +.theme-light .recommendation-item.owned { + border-color: rgba(16, 185, 129, 0.1); +} + +.theme-light .recommendation-item.owned:hover { + border-color: rgba(16, 185, 129, 0.25); +} + +.theme-light .rec-book-title { + color: var(--text-main); +} + +.theme-light .rec-chapter-title { + color: var(--text-muted); +} + +.theme-light .rec-action-btn { + border: 1px solid rgba(0, 0, 0, 0.08); + color: var(--text-muted); +} + +.theme-light .rec-action-btn:hover { + background: rgba(16, 185, 129, 0.1); + border-color: rgba(16, 185, 129, 0.3); + color: var(--accent); +} + +.theme-light .premium .rec-action-btn:hover { + background: rgba(245, 158, 11, 0.1); + border-color: rgba(245, 158, 11, 0.3); + color: #f59e0b; +} diff --git a/src/NexusReader.UI.Shared/Components/Organisms/CurrentReadingWidget.razor.css b/src/NexusReader.UI.Shared/Components/Organisms/CurrentReadingWidget.razor.css index 429b93c..bb2c941 100644 --- a/src/NexusReader.UI.Shared/Components/Organisms/CurrentReadingWidget.razor.css +++ b/src/NexusReader.UI.Shared/Components/Organisms/CurrentReadingWidget.razor.css @@ -210,3 +210,60 @@ align-items: center; } } + +/* ============================================================ + LIGHT THEME OVERRIDES — "Warm Paper / Soft Sepia" + ============================================================ */ + +.theme-light .current-reading-card { + background: var(--bg-surface); + border: 1px solid var(--border); +} + +.theme-light .current-reading-card:hover { + background: var(--bg-surface); + border-color: var(--accent); + box-shadow: 0 10px 30px rgba(139, 130, 115, 0.12); +} + +.theme-light .book-cover img { + box-shadow: 0 15px 35px rgba(139, 130, 115, 0.18); + border: 1px solid rgba(0, 0, 0, 0.06); +} + +.theme-light .book-title { + color: var(--text-main); +} + +.theme-light .author-name { + color: var(--text-muted); +} + +.theme-light .chapter-name { + color: var(--text-main); +} + +.theme-light .progress-bar-container { + background: #e4e1d9; +} + +.theme-light .progress-bar-fill { + box-shadow: 0 0 6px rgba(16, 185, 129, 0.2); +} + +.theme-light .book-excerpt { + color: var(--text-muted); +} + +.theme-light .empty-text h3 { + color: var(--text-main); +} + +.theme-light .empty-text p { + color: var(--text-muted); +} + +.theme-light .empty-icon { + color: var(--accent); + filter: none; +} diff --git a/src/NexusReader.UI.Shared/Layout/MainHubLayout.razor.css b/src/NexusReader.UI.Shared/Layout/MainHubLayout.razor.css index ddb2cf6..fb859da 100644 --- a/src/NexusReader.UI.Shared/Layout/MainHubLayout.razor.css +++ b/src/NexusReader.UI.Shared/Layout/MainHubLayout.razor.css @@ -342,4 +342,80 @@ } } +/* ============================================================ + LIGHT THEME OVERRIDES — "Warm Paper / Soft Sepia" + Scoped via .theme-light on an ancestor element. + ============================================================ */ +/* --- Desktop Sidebar: warm paper shadow --- */ +.theme-light ::deep .hub-sidebar { + box-shadow: 4px 0 20px rgba(139, 130, 115, 0.08); +} + +/* --- Logo icon: remove neon glow --- */ +.theme-light ::deep .logo-icon { + filter: none; +} + +/* --- Nav item hover: ensure green text, warm hover bg --- */ +.theme-light ::deep .nav-item:hover { + color: #10b981; + background: rgba(0, 0, 0, 0.02); +} + +/* --- Nav active indicator: reduced glow --- */ +.theme-light ::deep .nav-item.active::before { + box-shadow: 0 0 8px rgba(16, 185, 129, 0.3); +} + +/* --- Nexus loader: remove neon drop-shadow --- */ +.theme-light ::deep .nexus-loader { + filter: none; +} + +/* --- Mobile Styles --- */ +@media (max-width: 768px) { + + /* Hamburger button: dark text on warm paper */ + .theme-light .hamburger-btn { + color: #292524; + } + + .theme-light .hamburger-btn:hover { + background: rgba(0, 0, 0, 0.04); + } + + /* User avatar mini: solid accent, white text, no neon glow */ + .theme-light .user-avatar-mini { + background: #10b981; + border: 1px solid rgba(0, 0, 0, 0.08); + color: #ffffff; + box-shadow: 0 2px 8px rgba(16, 185, 129, 0.15); + } + + /* Pulsing logo: subtle accent pulse, no neon glow */ + .theme-light .pulsing-logo { + animation: pulse-glow-light 2s infinite ease-in-out; + } + + @keyframes pulse-glow-light { + 0%, 100% { + filter: none; + opacity: 0.85; + } + 50% { + filter: drop-shadow(0 0 4px rgba(16, 185, 129, 0.2)); + opacity: 1; + } + } + + /* Mobile sidebar open state: warm shadow instead of dark */ + .theme-light .mobile-menu-open ::deep .hub-sidebar { + box-shadow: 10px 0 30px rgba(139, 130, 115, 0.2); + } + + /* Mobile topbar: warm paper border */ + .theme-light .nexus-mobile-topbar { + border-bottom-color: rgba(0, 0, 0, 0.08); + } +} diff --git a/src/NexusReader.UI.Shared/Pages/Account/Profile.razor.css b/src/NexusReader.UI.Shared/Pages/Account/Profile.razor.css index e90bc4d..d629224 100644 --- a/src/NexusReader.UI.Shared/Pages/Account/Profile.razor.css +++ b/src/NexusReader.UI.Shared/Pages/Account/Profile.razor.css @@ -400,3 +400,59 @@ flex-direction: column; } } + +/* ============================================ + Light Theme Overrides — Warm Paper / Soft Sepia + ============================================ */ + +/* Background radial — warmer, slightly stronger glow */ +.theme-light .background-radial { + background: radial-gradient(circle, rgba(16, 185, 129, 0.04) 0%, transparent 70%); +} + +/* Avatar — keep green accent, reduce glow intensity */ +.theme-light .avatar-inner { + box-shadow: 0 0 20px rgba(16, 185, 129, 0.12), inset 0 0 15px rgba(16, 185, 129, 0.05); +} + +/* Avatar glow ring — softer border */ +.theme-light .avatar-glow { + border-color: rgba(16, 185, 129, 0.2); +} + +/* Glass panel hover — warm sepia shadow instead of pure black */ +.theme-light .glass-panel:hover { + box-shadow: 0 10px 30px rgba(139, 130, 115, 0.12); +} + +/* Progress bar — reduce neon glow */ +.theme-light .progress-bar { + box-shadow: 0 0 10px rgba(16, 185, 129, 0.2); +} + +/* Decorative text — dark ink on light bg instead of light on dark */ +.theme-light .decoration { + color: rgba(0, 0, 0, 0.04); +} + +/* Tenant tag — warm stone gray */ +.theme-light .tenant-tag { + color: #78716c; +} + +/* Loader — disable neon drop-shadow, softer border */ +.theme-light .nexus-loader { + border-color: rgba(16, 185, 129, 0.15); + border-top-color: #10b981; + filter: none; +} + +/* Theme option active — reduce glow in light mode */ +.theme-light .theme-option-btn.active { + box-shadow: 0 0 10px rgba(16, 185, 129, 0.1); +} + +/* Progress bar track — light stone gray */ +.theme-light .usage-progress { + background: #e4e1d9; +} diff --git a/src/NexusReader.UI.Shared/Pages/Catalog.razor.css b/src/NexusReader.UI.Shared/Pages/Catalog.razor.css index 1d1ed3d..1ae0296 100644 --- a/src/NexusReader.UI.Shared/Pages/Catalog.razor.css +++ b/src/NexusReader.UI.Shared/Pages/Catalog.razor.css @@ -357,3 +357,34 @@ from { transform: translate(-50%, -50%) scale(0.9); opacity: 0; } to { transform: translate(-50%, -50%) scale(1); opacity: 1; } } + +/* ============================================ + LIGHT THEME OVERRIDES — Warm Paper / Soft Sepia + ============================================ */ + +.theme-light .course-card:hover { + box-shadow: 0 12px 30px rgba(139, 130, 115, 0.15); +} + +.theme-light .card-cover-container { + background: rgba(0, 0, 0, 0.03); +} + +.theme-light .cover-overlay { + background: rgba(0, 0, 0, 0.5); +} + +.theme-light .course-card:hover .start-action { + color: #292524; +} + +.theme-light .dotnet-gradient, +.theme-light .blazor-gradient, +.theme-light .graph-gradient { + background: #e4e1d9; +} + +.theme-light .cover-code-text { + color: var(--text-main); + text-shadow: none; +} diff --git a/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor.css b/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor.css index e5131aa..0dbd5ed 100644 --- a/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor.css +++ b/src/NexusReader.UI.Shared/Pages/ConceptsDashboard.razor.css @@ -402,3 +402,54 @@ justify-content: center; } } + +/* ============================================ + Light Theme Overrides — Warm Paper / Soft Sepia + ============================================ */ + +/* Dashboard header — warm sepia shadow instead of pure black */ +.theme-light .dashboard-header { + box-shadow: 0 4px 30px rgba(139, 130, 115, 0.05); +} + +/* Neon pulse icon — disable glow filter entirely */ +.theme-light .neon-pulse { + filter: none; +} + +/* Override the neon pulse keyframe states in light mode */ +.theme-light .neon-pulse { + animation-name: robot-pulse-light; +} + +@keyframes robot-pulse-light { + 0% { transform: scale(1); filter: none; } + 50% { transform: scale(1.08); filter: none; } + 100% { transform: scale(1); filter: none; } +} + +/* Scan line — reduce glow intensity */ +.theme-light .scan-line { + box-shadow: 0 0 8px rgba(16, 185, 129, 0.3); + opacity: 0.5; +} + +/* Glowing brain empty state — subtle warm glow */ +.theme-light .empty-glowing-brain { + box-shadow: 0 0 12px rgba(16, 185, 129, 0.1); +} + +/* Error icon — reduce drop-shadow intensity */ +.theme-light .error-icon { + filter: drop-shadow(0 0 4px rgba(255, 74, 74, 0.2)); +} + +/* Back button hover — warm glow instead of neon */ +.theme-light .header-back .btn-back:hover { + box-shadow: 0 0 8px rgba(16, 185, 129, 0.1); +} + +/* Action button hover — warm glow */ +.theme-light .header-actions .btn-action:hover { + box-shadow: 0 0 12px rgba(16, 185, 129, 0.1); +} diff --git a/src/NexusReader.UI.Shared/Pages/Dashboard.razor.css b/src/NexusReader.UI.Shared/Pages/Dashboard.razor.css index ddf1ec9..29e079a 100644 --- a/src/NexusReader.UI.Shared/Pages/Dashboard.razor.css +++ b/src/NexusReader.UI.Shared/Pages/Dashboard.razor.css @@ -639,4 +639,91 @@ font-size: 0.85rem; } +/* ============================================================ + LIGHT THEME OVERRIDES — "Warm Paper / Soft Sepia" + ============================================================ */ + +.theme-light .username::before, +.theme-light .username::after { + color: var(--accent); +} + +.theme-light .avatar-glow { + background: var(--accent); + filter: blur(15px); + opacity: 0.2; +} + +.theme-light .progress-container { + background: #e4e1d9; +} + +.theme-light .progress-bar { + background: var(--accent); + box-shadow: 0 0 8px rgba(16, 185, 129, 0.2); +} + +.theme-light .progress-bubble { + background: var(--accent); + color: #ffffff; +} + +.theme-light .graph-node { + background: rgba(0, 0, 0, 0.06); + border: 1px solid rgba(0, 0, 0, 0.08); +} + +.theme-light .graph-node.central { + background: var(--accent); + box-shadow: 0 0 12px rgba(16, 185, 129, 0.2); +} + +.theme-light .graph-node.satellite { + background: rgba(16, 185, 129, 0.15); + border: 1px solid var(--accent); +} + +.theme-light .graph-node.satellite:hover { + background: var(--accent); + box-shadow: 0 0 10px var(--accent); +} + +.theme-light .active-node-label { + background: rgba(16, 185, 129, 0.06); + border: 1px solid var(--accent); + color: var(--accent); +} + +.theme-light .quiz-option.active { + background: rgba(16, 185, 129, 0.06); + border-color: var(--accent); + color: var(--accent); +} + +.theme-light .btn-nexus.primary { + background: var(--accent); + color: #0d0d0d; +} + +.theme-light .btn-nexus.primary:hover { + background: #059669; + color: #ffffff; +} + +.theme-light .empty-icon { + color: var(--accent); + filter: none; +} + +.theme-light .badge-success { + background: rgba(16, 185, 129, 0.1); + color: var(--accent); + border: 1px solid rgba(16, 185, 129, 0.2); +} + +.theme-light .concept-type { + color: var(--accent); +} + + diff --git a/src/NexusReader.UI.Shared/Pages/Intelligence.razor.css b/src/NexusReader.UI.Shared/Pages/Intelligence.razor.css index 1594c97..4d78412 100644 --- a/src/NexusReader.UI.Shared/Pages/Intelligence.razor.css +++ b/src/NexusReader.UI.Shared/Pages/Intelligence.razor.css @@ -262,3 +262,66 @@ 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } + +/* ============================================================ + LIGHT THEME OVERRIDES — "Warm Paper / Soft Sepia" + ============================================================ */ + +.theme-light .welcome-prompt { + color: var(--text-main); +} + +.theme-light .welcome-icon svg { + stroke: var(--text-muted); +} + +.theme-light .welcome-icon svg circle { + fill: var(--text-muted); +} + +.theme-light .welcome-icon svg path[stroke^="rgba(139"] { + stroke: rgba(120, 113, 108, 0.4); +} + +.theme-light .welcome-icon svg path[stroke^="rgba(139"][stroke-dasharray] { + stroke: rgba(120, 113, 108, 0.3); +} + +.theme-light .input-field-group { + background: var(--bg-surface); + border: 1px solid var(--border); + box-shadow: 0 10px 30px rgba(139, 130, 115, 0.08); +} + +.theme-light .input-field-group:focus-within { + border-color: var(--accent); + box-shadow: 0 10px 35px rgba(16, 185, 129, 0.15); +} + +.theme-light .nexus-input { + color: var(--text-main); +} + +.theme-light .nexus-input::placeholder { + color: var(--text-muted); +} + +.theme-light .nexus-select { + background-color: var(--bg-surface); + border-color: var(--border); + color: var(--text-main); +} + +.theme-light .nexus-select:focus { + border-color: var(--accent); + box-shadow: 0 0 8px var(--accent-glow); +} + +.theme-light .chat-thread-container::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.1); +} + +.theme-light .chat-thread-container::-webkit-scrollbar-thumb:hover { + background: rgba(0, 0, 0, 0.2); +} + diff --git a/src/NexusReader.UI.Shared/Pages/MyBooks.razor.css b/src/NexusReader.UI.Shared/Pages/MyBooks.razor.css index 5ddab46..4b089b4 100644 --- a/src/NexusReader.UI.Shared/Pages/MyBooks.razor.css +++ b/src/NexusReader.UI.Shared/Pages/MyBooks.razor.css @@ -383,3 +383,27 @@ from { transform: translate(-50%, -50%) scale(0.9); opacity: 0; } to { transform: translate(-50%, -50%) scale(1); opacity: 1; } } + +/* ============================================ + LIGHT THEME OVERRIDES — Warm Paper / Soft Sepia + ============================================ */ + +.theme-light .book-card:hover { + box-shadow: 0 12px 30px rgba(139, 130, 115, 0.15); +} + +.theme-light .book-cover-container { + background: rgba(0, 0, 0, 0.03); +} + +.theme-light .cover-overlay { + background: rgba(0, 0, 0, 0.5); +} + +.theme-light .book-card:hover .read-action { + color: #292524; +} + +.theme-light .progress-bar { + background: #e4e1d9; +} diff --git a/src/NexusReader.UI.Shared/Pages/Settings.razor.css b/src/NexusReader.UI.Shared/Pages/Settings.razor.css index 7df77b0..08911a0 100644 --- a/src/NexusReader.UI.Shared/Pages/Settings.razor.css +++ b/src/NexusReader.UI.Shared/Pages/Settings.razor.css @@ -72,3 +72,40 @@ from { opacity: 0; transform: translateY(15px); } to { opacity: 1; transform: translateY(0); } } + +/* ============================================================ + LIGHT THEME OVERRIDES — "Warm Paper / Soft Sepia" + ============================================================ */ + +.theme-light .settings-page > h1 { + background: none; + -webkit-text-fill-color: initial; + color: var(--text-main); +} + +.theme-light .settings-page > p { + color: var(--text-muted); +} + +.theme-light .settings-section h2 { + color: var(--text-main); +} + +.theme-light .settings-section p { + color: var(--text-muted); +} + +.theme-light .diag-btn { + background: rgba(16, 185, 129, 0.05); + color: var(--accent); + border: 1px solid rgba(16, 185, 129, 0.2); +} + +.theme-light .diag-btn:hover { + background: var(--accent); + color: #ffffff; + border-color: var(--accent); + box-shadow: 0 0 10px rgba(16, 185, 129, 0.2); + transform: translateY(-2px); +} + -- 2.52.0