Initial commit: NexusArchitect Professional Workstation Overhaul

This commit is contained in:
Debian
2026-04-24 20:27:22 +02:00
commit f3e94c4f42
193 changed files with 5809 additions and 0 deletions
@@ -0,0 +1,12 @@
using FluentResults;
using MediatR;
namespace NexusReader.Application.Abstractions.Messaging;
public interface ICommand : IRequest<Result>
{
}
public interface ICommand<TResponse> : IRequest<Result<TResponse>>
{
}
@@ -0,0 +1,14 @@
using FluentResults;
using MediatR;
namespace NexusReader.Application.Abstractions.Messaging;
public interface ICommandHandler<TCommand> : IRequestHandler<TCommand, Result>
where TCommand : ICommand
{
}
public interface ICommandHandler<TCommand, TResponse> : IRequestHandler<TCommand, Result<TResponse>>
where TCommand : ICommand<TResponse>
{
}
@@ -0,0 +1,8 @@
using FluentResults;
using MediatR;
namespace NexusReader.Application.Abstractions.Messaging;
public interface IQuery<TResponse> : IRequest<Result<TResponse>>
{
}
@@ -0,0 +1,9 @@
using FluentResults;
using MediatR;
namespace NexusReader.Application.Abstractions.Messaging;
public interface IQueryHandler<TQuery, TResponse> : IRequestHandler<TQuery, Result<TResponse>>
where TQuery : IQuery<TResponse>
{
}
@@ -0,0 +1,9 @@
using FluentResults;
using NexusReader.Application.Queries.Quiz;
namespace NexusReader.Application.Abstractions.Services;
public interface IAiGenerateQuizService
{
Task<Result<QuizDto>> GenerateQuizAsync(string contextBlockId, CancellationToken cancellationToken = default);
}
@@ -0,0 +1,12 @@
using FluentResults;
namespace NexusReader.Application.Abstractions.Services;
public interface INativeStorageService
{
Result SaveString(string key, string value);
Result<string?> GetString(string key);
Result SaveBool(string key, bool value);
Result<bool> GetBool(string key, bool defaultValue = false);
Result Remove(string key);
}
@@ -0,0 +1,33 @@
using FluentResults;
namespace NexusReader.Application.Abstractions.Services;
public interface IPlatformService
{
Task<Result> VibrateSuccessAsync();
Task<Result> VibrateErrorAsync();
Task<Result> VibrateAsync(int milliseconds);
Result<DeviceContext> GetDeviceContext();
}
public record DeviceContext(
string Model,
string Manufacturer,
DeviceType DeviceType,
DisplayOrientation Orientation
);
public enum DeviceType
{
Unknown,
Phone,
Tablet,
Desktop
}
public enum DisplayOrientation
{
Unknown,
Portrait,
Landscape
}
@@ -0,0 +1,5 @@
using NexusReader.Application.Abstractions.Messaging;
namespace NexusReader.Application.Commands.Quiz;
public record SubmitAnswerCommand(int SelectedIndex, int CorrectIndex) : ICommand;
@@ -0,0 +1,26 @@
using FluentResults;
using NexusReader.Application.Abstractions.Messaging;
using NexusReader.Application.Abstractions.Services;
namespace NexusReader.Application.Commands.Quiz;
internal sealed class SubmitAnswerCommandHandler : ICommandHandler<SubmitAnswerCommand>
{
private readonly IPlatformService _platformService;
public SubmitAnswerCommandHandler(IPlatformService platformService)
{
_platformService = platformService;
}
public async Task<Result> Handle(SubmitAnswerCommand request, CancellationToken cancellationToken)
{
if (request.SelectedIndex == request.CorrectIndex)
{
await _platformService.VibrateAsync(50);
return Result.Ok();
}
return Result.Fail("Incorrect answer.");
}
}
@@ -0,0 +1,14 @@
using System.Text.Json.Serialization;
using NexusReader.Application.Queries.Graph;
namespace NexusReader.Application.Common;
[JsonSourceGenerationOptions(WriteIndented = true)]
[JsonSerializable(typeof(GraphNodeDto))]
[JsonSerializable(typeof(GraphLinkDto))]
[JsonSerializable(typeof(GraphDataDto))]
[JsonSerializable(typeof(List<GraphNodeDto>))]
[JsonSerializable(typeof(List<GraphLinkDto>))]
public partial class AppJsonContext : JsonSerializerContext
{
}
@@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
using NexusReader.Application.Mappings;
namespace NexusReader.Application;
public static class DependencyInjection
{
public static IServiceCollection AddApplication(this IServiceCollection services)
{
services.AddMapsterConfiguration();
services.AddMediatR(config =>
{
config.RegisterServicesFromAssembly(typeof(DependencyInjection).Assembly);
});
return services;
}
}
@@ -0,0 +1,22 @@
using Mapster;
using MapsterMapper;
using Microsoft.Extensions.DependencyInjection;
using System.Reflection;
namespace NexusReader.Application.Mappings;
public static class MappingConfig
{
public static IServiceCollection AddMapsterConfiguration(this IServiceCollection services)
{
var config = TypeAdapterConfig.GlobalSettings;
// Manual registration for AOT (or use Source Generator)
// config.NewConfig<Source, Destination>();
services.AddSingleton(config);
services.AddScoped<IMapper, ServiceMapper>();
return services;
}
}
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<ItemGroup>
<ProjectReference Include="..\NexusReader.Domain\NexusReader.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="FluentResults" Version="4.0.0" />
<PackageReference Include="Mapster" Version="10.0.7" />
<PackageReference Include="Mapster.DependencyInjection" Version="10.0.7" />
<PackageReference Include="MediatR" Version="12.1.1" />
</ItemGroup>
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
@@ -0,0 +1,5 @@
using NexusReader.Application.Abstractions.Messaging;
namespace NexusReader.Application.Queries.Graph;
public record GetKnowledgeGraphQuery : IQuery<GraphDataDto>;
@@ -0,0 +1,30 @@
using FluentResults;
using NexusReader.Application.Abstractions.Messaging;
namespace NexusReader.Application.Queries.Graph;
internal sealed class GetKnowledgeGraphQueryHandler : IQueryHandler<GetKnowledgeGraphQuery, GraphDataDto>
{
public Task<Result<GraphDataDto>> Handle(GetKnowledgeGraphQuery request, CancellationToken cancellationToken)
{
var nodes = new List<GraphNodeDto>
{
new("renesans-intro", "Renesans", "Concept"),
new("florencja", "Florencja", "Location"),
new("medyceusze", "Medyceusze", "Entity"),
new("da-vinci-ai", "Leonardo da Vinci", "Person"),
new("humanizm", "Humanizm", "Concept")
};
var links = new List<GraphLinkDto>
{
new("renesans-intro", "florencja", 1),
new("florencja", "medyceusze", 2),
new("medyceusze", "da-vinci-ai", 3),
new("renesans-intro", "humanizm", 1),
new("da-vinci-ai", "humanizm", 2)
};
return Task.FromResult(Result.Ok(new GraphDataDto(nodes, links)));
}
}
@@ -0,0 +1,5 @@
namespace NexusReader.Application.Queries.Graph;
public record GraphNodeDto(string Id, string Label, string Group);
public record GraphLinkDto(string Source, string Target, int Value);
public record GraphDataDto(List<GraphNodeDto> Nodes, List<GraphLinkDto> Links);
@@ -0,0 +1,5 @@
using NexusReader.Application.Abstractions.Messaging;
namespace NexusReader.Application.Queries.Quiz;
public record GetQuizQuestionsQuery(string ContextBlockId) : IQuery<QuizDto>;
@@ -0,0 +1,20 @@
using FluentResults;
using NexusReader.Application.Abstractions.Messaging;
using NexusReader.Application.Abstractions.Services;
namespace NexusReader.Application.Queries.Quiz;
internal sealed class GetQuizQuestionsQueryHandler : IQueryHandler<GetQuizQuestionsQuery, QuizDto>
{
private readonly IAiGenerateQuizService _aiService;
public GetQuizQuestionsQueryHandler(IAiGenerateQuizService aiService)
{
_aiService = aiService;
}
public async Task<Result<QuizDto>> Handle(GetQuizQuestionsQuery request, CancellationToken cancellationToken)
{
return await _aiService.GenerateQuizAsync(request.ContextBlockId, cancellationToken);
}
}
@@ -0,0 +1,4 @@
namespace NexusReader.Application.Queries.Quiz;
public record QuizQuestionDto(string Question, List<string> Options, int CorrectIndex);
public record QuizDto(List<QuizQuestionDto> Questions);
@@ -0,0 +1,5 @@
using NexusReader.Application.Abstractions.Messaging;
namespace NexusReader.Application.Queries.Reader;
public record GetReaderPageQuery : IQuery<ReaderPageViewModel>;
@@ -0,0 +1,20 @@
using FluentResults;
using NexusReader.Application.Abstractions.Messaging;
namespace NexusReader.Application.Queries.Reader;
internal sealed class GetReaderPageQueryHandler : IQueryHandler<GetReaderPageQuery, ReaderPageViewModel>
{
public Task<Result<ReaderPageViewModel>> Handle(GetReaderPageQuery request, CancellationToken cancellationToken)
{
var blocks = new List<ContentBlock>
{
new TextSegmentBlock("renesans-intro", "Renesans, nazywany również odrodzeniem, to epoka w historii kultury europejskiej, która zapoczątkowała odejście od średniowiecznego teocentryzmu na rzecz humanizmu. Narodził się we Włoszech, a dokładnie we Florencji, w XV wieku, skąd promieniował na całą Europę."),
new TextSegmentBlock("medyceusze", "Głównym mecenasem sztuki i nauki we Florencji był potężny ród Medyceuszy. To dzięki ich wsparciu miasto stało się kolebką nowożytnej myśli, gromadząc wokół siebie najwybitniejsze umysły tamtych czasów."),
new AiActionTriggerBlock("da-vinci-ai", "Leonardo da Vinci był jednym z najważniejszych twórców tego okresu. Czy chciałbyś dowiedzieć się więcej o jego najważniejszych wynalazkach, czy wolisz sprawdzić swoją dotychczasową wiedzę?", new List<string> { "Pokaż więcej", "Rozwiąż quiz" }),
new TextSegmentBlock("leonardo-detail", "Człowiek renesansu, uosabiany właśnie przez Leonarda, był wszechstronnie wykształcony. Interesował się sztuką, inżynierią, anatomią i filozofią, stawiając jednostkę w centrum wszechświata.")
};
return Task.FromResult(Result.Ok(new ReaderPageViewModel(blocks)));
}
}
@@ -0,0 +1,7 @@
namespace NexusReader.Application.Queries.Reader;
public abstract record ContentBlock(string Id);
public record TextSegmentBlock(string Id, string Content) : ContentBlock(Id);
public record AiActionTriggerBlock(string Id, string Dialogue, List<string> ActionOptions) : ContentBlock(Id);
public record ReaderPageViewModel(List<ContentBlock> Blocks);
@@ -0,0 +1,5 @@
using NexusReader.Application.Abstractions.Messaging;
namespace NexusReader.Application.Queries.System;
public record GetInitializationStatusQuery : IQuery<string>;
@@ -0,0 +1,12 @@
using FluentResults;
using NexusReader.Application.Abstractions.Messaging;
namespace NexusReader.Application.Queries.System;
internal sealed class GetInitializationStatusQueryHandler : IQueryHandler<GetInitializationStatusQuery, string>
{
public Task<Result<string>> Handle(GetInitializationStatusQuery request, CancellationToken cancellationToken)
{
return Task.FromResult(Result.Ok("Nexus E-Reader Application is fully initialized and operational."));
}
}