diff --git a/.localpilot/map.txt b/.localpilot/map.txt index f82b0b3..ba25deb 100644 --- a/.localpilot/map.txt +++ b/.localpilot/map.txt @@ -1,13 +1,13 @@ ## WORKSPACE EXECUTIVE SUMMARY (YAML) -# Generated: 2026-05-16T15:21:43.3363122+02:00 +# Generated: 2026-05-20T06:11:22.0417511+02:00 # Root: E:\ollama_net\ollama\AuthorBuddy.Web Architecture: Type: Backend (C#/.NET) - Scale: 25 relevant source files + Scale: 34 relevant source files Components: 0 Controllers: 0 - Models: 6 - Services: 8 + Models: 8 + Services: 14 --- - File: AuthorBuddy.Web.csproj Preview: " net10.0 enable en..." @@ -18,25 +18,31 @@ Architecture: - File: AuthorBuddy.Web.slnx Preview: " " - File: Program.cs - Preview: "using AuthorBuddy.Web.Components; using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using AuthorBuddy.Web.Services; using Microsoft.EntityFram..." -- File: Data\AppDbContext.cs - Symbols: [AppDbContext] - Preview: "using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; namespace AuthorBuddy.Web.Data; public class AppDbContext : DbContext { public DbS..." + Preview: "using AuthorBuddy.Web.Components; using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using AuthorBuddy.Web.Services; using LiteDB; var builder ..." - File: Data\DocumentMetaEntity.cs Symbols: [DocumentMetaEntity] - Preview: "using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace AuthorBuddy.Web.Data; [Table('DocumentMeta'..." + Preview: "namespace AuthorBuddy.Web.Data; public class DocumentMetaEntity { public int Id { get; set; } public int? ProjectId { get; set; } public string ..." +- File: Data\DocumentVector.cs + Symbols: [DocumentVector] + Preview: "namespace AuthorBuddy.Web.Data; public class DocumentVector { public int Id { get; set; } public int DocumentMetaId { get; set; } public int Pro..." - File: Data\ProjectEntity.cs Symbols: [ProjectEntity] - Preview: "using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace AuthorBuddy.Web.Data; [Table('Projects')] p..." + Preview: "namespace AuthorBuddy.Web.Data; public class ProjectEntity { public int Id { get; set; } public string Name { get; set; } = string.Empty; public..." - File: Data\SettingEntity.cs Symbols: [SettingEntity] - Preview: "using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; namespace AuthorBuddy.Web.Data; [Table('Settings')] p..." + Preview: "using LiteDB; namespace AuthorBuddy.Web.Data; public class SettingEntity { public int Id { get; set; } public string Key { get; set; } = string.Em..." +- File: Data\UserEntity.cs + Symbols: [UserEntity] + Preview: "namespace AuthorBuddy.Web.Data; public class UserEntity { public int Id { get; set; } public string Name { get; set; } = string.Empty; public bo..." - File: Models\Chapter.cs Symbols: [Chapter] Preview: "namespace AuthorBuddy.Web.Models; public class Chapter { public string Title { get; set; } = string.Empty; public string FilePath { get; set; } = ..." - File: Models\Constants.cs Symbols: [Constants] Preview: "namespace AuthorBuddy.Web.Models; public static class Constants { public static readonly string[] SubFolders = { '00_Drafts', '01_Fakten/Per..." +- File: Models\FileTreeItem.cs + Symbols: [FileTreeItem] + Preview: "namespace AuthorBuddy.Web.Models; public class FileTreeItem { public string Name { get; set; } = string.Empty; public string FullPath { get; set; ..." - File: Models\LLM_Model.cs Symbols: [LLM_Model, LLM_Model] Preview: "using System.Text.Json; namespace AuthorBuddy.Web.Models { public class LLM_Model { public LLM_Model(JsonElement model) { if (mode..." @@ -45,35 +51,56 @@ Architecture: Preview: " //namespace AuthorBuddy.Web.Models; //public class ModelStatus //{ // public string Status { get; private set; } = 'Unknown'; // public double Prog..." - File: Models\OllamaSettings.cs Symbols: [OllamaSettings] - Preview: "namespace AuthorBuddy.Web.Models; public class OllamaSettings { public string ChatModel { get; set; } = 'llama3.2'; public string EmbedModel { get..." + Preview: "namespace AuthorBuddy.Web.Models; public class OllamaSettings { public string StyleModel { get; set; } = 'gemma4:e4b'; public string EmbedModel { ..." - File: Models\Project.cs Symbols: [Project] Preview: "namespace AuthorBuddy.Web.Models; public class Project { public int Id { get; internal set; } public string Name { get; internal set; } = string.E..." +- File: Models\WikipediaResult.cs + Symbols: [WikipediaResult] + Preview: "namespace AuthorBuddy.Web.Models; public class WikipediaResult { public string Title { get; set; } = string.Empty; public string Snippet { get; se..." - File: Properties\launchSettings.json Preview: "{ '$schema': 'https://json.schemastore.org/launchsettings.json', 'profiles': { 'http': { 'commandName': 'Project', 'dotnetRunMessages': ..." - File: Services\FileScannerService.cs Symbols: [FileScannerService] - Preview: "using AuthorBuddy.Web.Data; using Microsoft.Data.Sqlite; using Microsoft.EntityFrameworkCore; namespace AuthorBuddy.Web.Services; public class FileSca..." + Preview: "using AuthorBuddy.Web.Data; using LiteDB; namespace AuthorBuddy.Web.Services; public class FileScannerService : IFileScannerService { private readon..." - File: Services\IFileScannerService.cs Symbols: [IFileScannerService] Preview: "namespace AuthorBuddy.Web.Services; public interface IFileScannerService { Task ScanProjectFolderAsync(int projectId, string rootPath, IProgress> GetLocalModelsAsyn..." - File: Services\IProjectService.cs Symbols: [IProjectService] Preview: "using AuthorBuddy.Web.Models; namespace AuthorBuddy.Web.Services; public interface IProjectService { Task CreateNewProjectAsync(string projectN..." +- File: Services\IRagService.cs + Symbols: [IRagServi, RagSearchResult] + Preview: "namespace AuthorBuddy.Web.Services; public class RagSearchResult { public string Text { get; set; } = string.Empty; public string FilePath { get; ..." - File: Services\ISettingsService.cs Symbols: [ISettingsService] - Preview: "namespace AuthorBuddy.Web.Services; public interface ISettingsService { Task GetAsync(string key); Task SaveAsync(Dictionary GetOllamaUrlAsync(); Task> SearchAsync(strin..." - File: Services\OllamaService.cs - Symbols: [ChatStream, OllamaService, OllamaService, OllamaService, GetLocalModelsAsync, DeleteModel, ExtractResponseText, PullModelAsync, catch, catch, catch, catch, Chat, Reconfigure] - Preview: "using AuthorBuddy.Web.Models; using System.Net.Http.Json; using System.Text.Json; namespace AuthorBuddy.Web.Services; public class OllamaService : IOl..." + Symbols: [OllamaService] + Preview: "using AuthorBuddy.Web.Models; using System.Text.Json; using static System.Net.Mime.MediaTypeNames; namespace AuthorBuddy.Web.Services; public class Ol..." - File: Services\ProjectService.cs Symbols: [ProjectService] - Preview: "using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using Microsoft.EntityFrameworkCore; namespace AuthorBuddy.Web.Services; public class Projec..." + Preview: "using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using LiteDB; namespace AuthorBuddy.Web.Services; public class ProjectService : IProjectServ..." +- File: Services\RagService.cs + Symbols: [RagService] + Preview: "using AuthorBuddy.Web.Data; using LiteDB; using System.Security.Cryptography; using System.Text; namespace AuthorBuddy.Web.Services; public class RagS..." - File: Services\SettingsService.cs Symbols: [SettingsService] - Preview: "using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using Microsoft.EntityFrameworkCore; using Microsoft.JSInterop; using static System.Runtime...." + Preview: "using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using LiteDB; namespace AuthorBuddy.Web.Services; public class SettingsService : ISettingsSe..." +- File: Services\ThemeService.cs + Symbols: [ThemeService, ThemeService] + Preview: "using Microsoft.JSInterop; namespace AuthorBuddy.Web.Services; public class ThemeService : IThemeService { private readonly IJSRuntime _js; public..." +- File: Services\WikipediaService.cs + Symbols: [WikipediaService] + Preview: "using AuthorBuddy.Web.Models; using System.Text.Json; using System.Web; namespace AuthorBuddy.Web.Services; public class WikipediaService : IWikipedia..." - File: Components\Layout\ReconnectModal.razor.js Preview: "// Set up event handlers const reconnectModal = document.getElementById('components-reconnect-modal'); reconnectModal.addEventListener('components-rec..." diff --git a/.vs/AuthorBuddy.Web.slnx/v18/DocumentLayout.backup.json b/.vs/AuthorBuddy.Web.slnx/v18/DocumentLayout.backup.json index f54bf47..7a1ae68 100644 --- a/.vs/AuthorBuddy.Web.slnx/v18/DocumentLayout.backup.json +++ b/.vs/AuthorBuddy.Web.slnx/v18/DocumentLayout.backup.json @@ -3,48 +3,68 @@ "WorkspaceRootPath": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\", "Documents": [ { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\data\\appdbcontext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:data\\appdbcontext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\ollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\editor.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\editor.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" - }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\settingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\settingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\iollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\iollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\models\\ollamasettings.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:models\\ollamasettings.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\settingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\settingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" - }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\filescannerservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\filescannerservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\models\\ollamasettings.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:models\\ollamasettings.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\ollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\models\\project.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:models\\project.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\ragservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ragservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\iragservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\iragservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\editor.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\editor.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\navmenu.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\navmenu.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Data\\AppDbContext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\iollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\iollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\themeservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", @@ -74,26 +94,14 @@ "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}" }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\navmenu.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\navmenu.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" - }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\mainlayout.razor.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\mainlayout.razor.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}" }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\iprojectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\iprojectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\models\\project.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:models\\project.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\data\\projectentity.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:data\\projectentity.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" @@ -114,7 +122,7 @@ "DocumentGroups": [ { "DockedWidth": 200, - "SelectedChildIndex": 9, + "SelectedChildIndex": 7, "Children": [ { "$type": "Bookmark", @@ -122,15 +130,32 @@ }, { "$type": "Document", - "DocumentIndex": 1, - "Title": "OllamaService.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs", - "RelativeDocumentMoniker": "Services\\OllamaService.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs*", - "RelativeToolTip": "Services\\OllamaService.cs*", - "ViewState": "AgIAADQAAAAAAAAAAAAQwEUAAABDAAAAAAAAAA==", + "DocumentIndex": 2, + "Title": "Program.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs", + "RelativeDocumentMoniker": "Program.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs", + "RelativeToolTip": "Program.cs", + "ViewState": "AgIAAAMAAAAAAAAAAAApwBoAAAANAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T06:43:46.404Z", + "WhenOpened": "2026-05-20T04:07:21.54Z", + "EditorCaption": "" + }, + { + "$type": "Bookmark", + "Name": "RST:0:0:{b3f9f954-0ca5-0091-76b4-34a47f44c954}" + }, + { + "$type": "Document", + "DocumentIndex": 1, + "Title": "ISettingsService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ISettingsService.cs", + "RelativeDocumentMoniker": "Services\\ISettingsService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ISettingsService.cs*", + "RelativeToolTip": "Services\\ISettingsService.cs*", + "ViewState": "AgIAABoAAAAAAAAAAAA8wCYAAAAPAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-16T13:21:26.951Z", "EditorCaption": "" }, { @@ -141,46 +166,44 @@ "RelativeDocumentMoniker": "Services\\SettingsService.cs", "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\SettingsService.cs", "RelativeToolTip": "Services\\SettingsService.cs", - "ViewState": "AgIAAMAAAAAAAAAAAAAQwMgAAAAMAAAAAAAAAA==", + "ViewState": "AgIAAAAAAAAAAAAAAAAqwAYAAAAmAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "WhenOpened": "2026-05-16T06:05:12.928Z", "EditorCaption": "" }, { "$type": "Document", - "DocumentIndex": 11, - "Title": "ThemeService.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs", - "RelativeDocumentMoniker": "Services\\ThemeService.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs", - "RelativeToolTip": "Services\\ThemeService.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "DocumentIndex": 9, + "Title": "RagService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\RagService.cs", + "RelativeDocumentMoniker": "Services\\RagService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\RagService.cs", + "RelativeToolTip": "Services\\RagService.cs", + "ViewState": "AgIAABIAAAAAAAAAAAAxwBoAAAAIAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T13:35:33.991Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 4, - "Title": "ISettingsService.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ISettingsService.cs", - "RelativeDocumentMoniker": "Services\\ISettingsService.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ISettingsService.cs", - "RelativeToolTip": "Services\\ISettingsService.cs", - "ViewState": "AgIAAAsAAAAAAAAAAAA8wCEAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T13:21:26.951Z", - "EditorCaption": "" + "WhenOpened": "2026-05-18T15:40:05.413Z" }, { "$type": "Document", "DocumentIndex": 8, + "Title": "Project.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs", + "RelativeDocumentMoniker": "Models\\Project.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs", + "RelativeToolTip": "Models\\Project.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAYAAAAaAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-15T15:57:10.2Z" + }, + { + "$type": "Document", + "DocumentIndex": 0, "Title": "MainLayout.razor", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor", "RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor", - "RelativeToolTip": "Components\\Layout\\MainLayout.razor", - "ViewState": "AgIAAG8AAAAAAAAAAAAAAH0AAAAtAAAAAAAAAA==", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor*", + "RelativeToolTip": "Components\\Layout\\MainLayout.razor*", + "ViewState": "AgIAALUAAAAAAAAAAAApwM4AAABAAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", "WhenOpened": "2026-05-15T16:04:36.444Z", "EditorCaption": "" @@ -193,160 +216,75 @@ "RelativeDocumentMoniker": "Models\\OllamaSettings.cs", "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs", "RelativeToolTip": "Models\\OllamaSettings.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAqwAYAAAA6AAAAAAAAAA==", + "ViewState": "AgIAAAIAAAAAAAAAAAAxwAUAAAA+AAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "WhenOpened": "2026-05-16T13:39:46.179Z", "EditorCaption": "" }, { "$type": "Document", - "DocumentIndex": 9, + "DocumentIndex": 13, + "Title": "AppDbContext.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs", + "RelativeDocumentMoniker": "Data\\AppDbContext.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs", + "RelativeToolTip": "Data\\AppDbContext.cs", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-14T10:50:13.284Z" + }, + { + "$type": "Document", + "DocumentIndex": 5, "Title": "FileScannerService.cs", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs", "RelativeDocumentMoniker": "Services\\FileScannerService.cs", "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs", "RelativeToolTip": "Services\\FileScannerService.cs", - "ViewState": "AgIAACkAAAAAAAAAAIAwwEsAAAAfAAAAAAAAAA==", + "ViewState": "AgIAABIAAAAAAAAAAAAQwCUAAAAWAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "WhenOpened": "2026-05-16T06:02:53.46Z", "EditorCaption": "" }, { "$type": "Document", - "DocumentIndex": 2, - "Title": "Editor.razor", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor", - "RelativeDocumentMoniker": "Components\\Pages\\Editor.razor", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor", - "RelativeToolTip": "Components\\Pages\\Editor.razor", - "ViewState": "AgIAAFgAAAAAAAAAAAApwGsAAAAwAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", - "WhenOpened": "2026-05-16T13:01:59.743Z", - "EditorCaption": "" + "DocumentIndex": 10, + "Title": "IRagService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IRagService.cs", + "RelativeDocumentMoniker": "Services\\IRagService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IRagService.cs", + "RelativeToolTip": "Services\\IRagService.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAVAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-18T15:37:44.565Z" }, { "$type": "Document", - "DocumentIndex": 0, - "Title": "AppDbContext.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs", - "RelativeDocumentMoniker": "Data\\AppDbContext.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs", - "RelativeToolTip": "Data\\AppDbContext.cs", - "ViewState": "AgIAADgAAAAAAAAAAAAuwEsAAAAAAAAAAAAAAA==", + "DocumentIndex": 4, + "Title": "ProjectService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs", + "RelativeDocumentMoniker": "Services\\ProjectService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs", + "RelativeToolTip": "Services\\ProjectService.cs", + "ViewState": "AgIAADQAAAAAAAAAAAAqwEYAAAAWAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-14T10:50:13.284Z", + "WhenOpened": "2026-05-15T15:56:55.229Z", "EditorCaption": "" }, { "$type": "Document", "DocumentIndex": 7, - "Title": "Settings.razor", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor", - "RelativeDocumentMoniker": "Components\\Pages\\Settings.razor", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor", - "RelativeToolTip": "Components\\Pages\\Settings.razor", - "ViewState": "AgIAAHMAAAAAAAAAAADwv4UAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", - "WhenOpened": "2026-05-15T16:43:26.6Z", - "EditorCaption": "" - }, - { - "$type": "Bookmark", - "Name": "RST:0:0:{b3f9f954-0ca5-0091-76b4-34a47f44c954}" - }, - { - "$type": "Document", - "DocumentIndex": 5, - "Title": "IOllamaService.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs", - "RelativeDocumentMoniker": "Services\\IOllamaService.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs", - "RelativeToolTip": "Services\\IOllamaService.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAqwAkAAAAPAAAAAAAAAA==", + "Title": "OllamaService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs", + "RelativeDocumentMoniker": "Services\\OllamaService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs", + "RelativeToolTip": "Services\\OllamaService.cs", + "ViewState": "AgIAAHoAAAAAAAAAAAAcwCoAAAAJAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T07:20:19.212Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 10, - "Title": "Program.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs", - "RelativeDocumentMoniker": "Program.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs", - "RelativeToolTip": "Program.cs", - "ViewState": "AgIAAAYAAAAAAAAAAAAAABUAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T06:03:12.506Z", - "EditorCaption": "" + "WhenOpened": "2026-05-16T06:43:46.404Z" }, { "$type": "Document", "DocumentIndex": 12, - "Title": "App.razor", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor", - "RelativeDocumentMoniker": "Components\\App.razor", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor", - "RelativeToolTip": "Components\\App.razor", - "ViewState": "AgIAAAYAAAAAAAAAAAAAABgAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", - "WhenOpened": "2026-05-16T05:42:06.383Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 15, - "Title": "Models.razor", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor", - "RelativeDocumentMoniker": "Components\\Pages\\Models.razor", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor", - "RelativeToolTip": "Components\\Pages\\Models.razor", - "ViewState": "AgIAAGgAAAAAAAAAAAApwHQAAAAtAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", - "WhenOpened": "2026-05-16T12:51:13.798Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 13, - "Title": "LLM_Model.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs", - "RelativeDocumentMoniker": "Models\\LLM_Model.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs", - "RelativeToolTip": "Models\\LLM_Model.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAqwBQAAAAzAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T12:53:35.486Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 14, - "Title": "ModelStatus.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs", - "RelativeDocumentMoniker": "Models\\ModelStatus.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs", - "RelativeToolTip": "Models\\ModelStatus.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAAABEAAAADAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T13:01:24.508Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 19, - "Title": "MainLayout.razor.css", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css", - "RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor.css", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css", - "RelativeToolTip": "Components\\Layout\\MainLayout.razor.css", - "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|", - "WhenOpened": "2026-05-15T16:04:50.455Z" - }, - { - "$type": "Document", - "DocumentIndex": 18, "Title": "NavMenu.razor", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\NavMenu.razor", "RelativeDocumentMoniker": "Components\\Layout\\NavMenu.razor", @@ -356,9 +294,118 @@ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", "WhenOpened": "2026-05-15T16:04:37.739Z" }, + { + "$type": "Document", + "DocumentIndex": 11, + "Title": "Editor.razor", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor", + "RelativeDocumentMoniker": "Components\\Pages\\Editor.razor", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor", + "RelativeToolTip": "Components\\Pages\\Editor.razor", + "ViewState": "AgIAAAoAAACA6NuCpTYvwJkAAAAcAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", + "WhenOpened": "2026-05-16T13:01:59.743Z", + "EditorCaption": "" + }, { "$type": "Document", "DocumentIndex": 16, + "Title": "ThemeService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs", + "RelativeDocumentMoniker": "Services\\ThemeService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs", + "RelativeToolTip": "Services\\ThemeService.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-16T13:35:33.991Z" + }, + { + "$type": "Document", + "DocumentIndex": 15, + "Title": "Settings.razor", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor", + "RelativeDocumentMoniker": "Components\\Pages\\Settings.razor", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor", + "RelativeToolTip": "Components\\Pages\\Settings.razor", + "ViewState": "AgIAAHMAAAAAAAAAAADwv4UAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", + "WhenOpened": "2026-05-15T16:43:26.6Z" + }, + { + "$type": "Document", + "DocumentIndex": 14, + "Title": "IOllamaService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs", + "RelativeDocumentMoniker": "Services\\IOllamaService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs", + "RelativeToolTip": "Services\\IOllamaService.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAqwAkAAAAPAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-16T07:20:19.212Z" + }, + { + "$type": "Document", + "DocumentIndex": 17, + "Title": "App.razor", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor", + "RelativeDocumentMoniker": "Components\\App.razor", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor", + "RelativeToolTip": "Components\\App.razor", + "ViewState": "AgIAAAYAAAAAAAAAAAAAABgAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", + "WhenOpened": "2026-05-16T05:42:06.383Z" + }, + { + "$type": "Document", + "DocumentIndex": 20, + "Title": "Models.razor", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor", + "RelativeDocumentMoniker": "Components\\Pages\\Models.razor", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor", + "RelativeToolTip": "Components\\Pages\\Models.razor", + "ViewState": "AgIAAGgAAAAAAAAAAAApwHQAAAAtAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", + "WhenOpened": "2026-05-16T12:51:13.798Z" + }, + { + "$type": "Document", + "DocumentIndex": 18, + "Title": "LLM_Model.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs", + "RelativeDocumentMoniker": "Models\\LLM_Model.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs", + "RelativeToolTip": "Models\\LLM_Model.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAqwBQAAAAzAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-16T12:53:35.486Z" + }, + { + "$type": "Document", + "DocumentIndex": 19, + "Title": "ModelStatus.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs", + "RelativeDocumentMoniker": "Models\\ModelStatus.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs", + "RelativeToolTip": "Models\\ModelStatus.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAAABEAAAADAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-16T13:01:24.508Z" + }, + { + "$type": "Document", + "DocumentIndex": 23, + "Title": "MainLayout.razor.css", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css", + "RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor.css", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css", + "RelativeToolTip": "Components\\Layout\\MainLayout.razor.css", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|", + "WhenOpened": "2026-05-15T16:04:50.455Z" + }, + { + "$type": "Document", + "DocumentIndex": 21, "Title": "IFileScannerService.cs", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IFileScannerService.cs", "RelativeDocumentMoniker": "Services\\IFileScannerService.cs", @@ -371,18 +418,6 @@ { "$type": "Document", "DocumentIndex": 22, - "Title": "Project.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs", - "RelativeDocumentMoniker": "Models\\Project.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs", - "RelativeToolTip": "Models\\Project.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAAAAYAAAAaAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-15T15:57:10.2Z" - }, - { - "$type": "Document", - "DocumentIndex": 17, "Title": "bootstrap-utilities.css", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css", "RelativeDocumentMoniker": "wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css", @@ -394,19 +429,7 @@ }, { "$type": "Document", - "DocumentIndex": 20, - "Title": "ProjectService.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs", - "RelativeDocumentMoniker": "Services\\ProjectService.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs", - "RelativeToolTip": "Services\\ProjectService.cs", - "ViewState": "AgIAAB4AAAAAAAAAAAAowAAAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-15T15:56:55.229Z" - }, - { - "$type": "Document", - "DocumentIndex": 23, + "DocumentIndex": 25, "Title": "ProjectEntity.cs", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\ProjectEntity.cs", "RelativeDocumentMoniker": "Data\\ProjectEntity.cs", @@ -418,7 +441,7 @@ }, { "$type": "Document", - "DocumentIndex": 21, + "DocumentIndex": 24, "Title": "IProjectService.cs", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IProjectService.cs", "RelativeDocumentMoniker": "Services\\IProjectService.cs", @@ -430,7 +453,7 @@ }, { "$type": "Document", - "DocumentIndex": 24, + "DocumentIndex": 26, "Title": "AuthorBuddy.Web.csproj", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj", "RelativeDocumentMoniker": "AuthorBuddy.Web.csproj", @@ -442,7 +465,7 @@ }, { "$type": "Document", - "DocumentIndex": 25, + "DocumentIndex": 27, "Title": "appsettings.json", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\appsettings.json", "RelativeDocumentMoniker": "appsettings.json", diff --git a/.vs/AuthorBuddy.Web.slnx/v18/DocumentLayout.json b/.vs/AuthorBuddy.Web.slnx/v18/DocumentLayout.json index 7199885..4236a65 100644 --- a/.vs/AuthorBuddy.Web.slnx/v18/DocumentLayout.json +++ b/.vs/AuthorBuddy.Web.slnx/v18/DocumentLayout.json @@ -2,50 +2,70 @@ "Version": 1, "WorkspaceRootPath": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\", "Documents": [ - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\data\\appdbcontext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:data\\appdbcontext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\ollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\editor.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\editor.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" - }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\settingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\settingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\iollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\iollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\models\\ollamasettings.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:models\\ollamasettings.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" - }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\mainlayout.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" }, { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\filescannerservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\filescannerservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\isettingsservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:program.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\filescannerservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\filescannerservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\models\\ollamasettings.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:models\\ollamasettings.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\ollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\models\\project.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:models\\project.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\ragservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ragservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\iragservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\iragservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\editor.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\editor.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\navmenu.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\navmenu.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" + }, + { + "AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|solutionrelative:Data\\AppDbContext.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\iollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\iollamaservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" + }, + { + "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", + "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\settings.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" + }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\themeservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\themeservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" @@ -74,26 +94,14 @@ "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}" }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\navmenu.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\navmenu.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}" - }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\layout\\mainlayout.razor.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\layout\\mainlayout.razor.css||{A5401142-F49D-43DB-90B1-F57BA349E55C}" }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\projectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\services\\iprojectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\iprojectservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" }, - { - "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\models\\project.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", - "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:models\\project.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" - }, { "AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\data\\projectentity.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}", "RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:data\\projectentity.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}" @@ -114,7 +122,7 @@ "DocumentGroups": [ { "DockedWidth": 200, - "SelectedChildIndex": 8, + "SelectedChildIndex": 4, "Children": [ { "$type": "Bookmark", @@ -127,51 +135,75 @@ { "$type": "Document", "DocumentIndex": 3, - "Title": "SettingsService.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\SettingsService.cs", - "RelativeDocumentMoniker": "Services\\SettingsService.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\SettingsService.cs", - "RelativeToolTip": "Services\\SettingsService.cs", - "ViewState": "AgIAAMAAAAAAAAAAAAAQwMgAAAAMAAAAAAAAAA==", + "Title": "Program.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs", + "RelativeDocumentMoniker": "Program.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs", + "RelativeToolTip": "Program.cs", + "ViewState": "AgIAAAMAAAAAAAAAAAApwBoAAAANAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T06:05:12.928Z", + "WhenOpened": "2026-05-20T04:07:21.54Z", "EditorCaption": "" }, { "$type": "Document", - "DocumentIndex": 11, - "Title": "ThemeService.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs", - "RelativeDocumentMoniker": "Services\\ThemeService.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs", - "RelativeToolTip": "Services\\ThemeService.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T13:35:33.991Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 4, + "DocumentIndex": 2, "Title": "ISettingsService.cs", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ISettingsService.cs", "RelativeDocumentMoniker": "Services\\ISettingsService.cs", "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ISettingsService.cs", "RelativeToolTip": "Services\\ISettingsService.cs", - "ViewState": "AgIAAAsAAAAAAAAAAAA8wCEAAAAAAAAAAAAAAA==", + "ViewState": "AgIAABoAAAAAAAAAAAA8wCYAAAAPAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "WhenOpened": "2026-05-16T13:21:26.951Z", "EditorCaption": "" }, + { + "$type": "Document", + "DocumentIndex": 0, + "Title": "SettingsService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\SettingsService.cs", + "RelativeDocumentMoniker": "Services\\SettingsService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\SettingsService.cs", + "RelativeToolTip": "Services\\SettingsService.cs", + "ViewState": "AgIAADcBAAAAAAAAAAAQwEoBAAAIAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-16T06:05:12.928Z", + "EditorCaption": "" + }, + { + "$type": "Document", + "DocumentIndex": 9, + "Title": "RagService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\RagService.cs", + "RelativeDocumentMoniker": "Services\\RagService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\RagService.cs", + "RelativeToolTip": "Services\\RagService.cs", + "ViewState": "AgIAABIAAAAAAAAAAAAxwBoAAAAIAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-18T15:40:05.413Z" + }, { "$type": "Document", "DocumentIndex": 8, + "Title": "Project.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs", + "RelativeDocumentMoniker": "Models\\Project.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs", + "RelativeToolTip": "Models\\Project.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAYAAAAaAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-15T15:57:10.2Z" + }, + { + "$type": "Document", + "DocumentIndex": 1, "Title": "MainLayout.razor", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor", "RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor", "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor", "RelativeToolTip": "Components\\Layout\\MainLayout.razor", - "ViewState": "AgIAAG8AAAAAAAAAAAAAAH0AAAAtAAAAAAAAAA==", + "ViewState": "AgIAALUAAAAAAAAAAAApwM4AAABAAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", "WhenOpened": "2026-05-15T16:04:36.444Z", "EditorCaption": "" @@ -184,169 +216,75 @@ "RelativeDocumentMoniker": "Models\\OllamaSettings.cs", "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs", "RelativeToolTip": "Models\\OllamaSettings.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAqwAYAAAA6AAAAAAAAAA==", + "ViewState": "AgIAAAIAAAAAAAAAAAAxwAUAAAA+AAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "WhenOpened": "2026-05-16T13:39:46.179Z", "EditorCaption": "" }, { "$type": "Document", - "DocumentIndex": 9, + "DocumentIndex": 13, + "Title": "AppDbContext.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs", + "RelativeDocumentMoniker": "Data\\AppDbContext.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs", + "RelativeToolTip": "Data\\AppDbContext.cs", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-14T10:50:13.284Z" + }, + { + "$type": "Document", + "DocumentIndex": 5, "Title": "FileScannerService.cs", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs", "RelativeDocumentMoniker": "Services\\FileScannerService.cs", "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\FileScannerService.cs", "RelativeToolTip": "Services\\FileScannerService.cs", - "ViewState": "AgIAACkAAAAAAAAAAIAwwEsAAAAfAAAAAAAAAA==", + "ViewState": "AgIAABIAAAAAAAAAAAAQwCUAAAAWAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", "WhenOpened": "2026-05-16T06:02:53.46Z", "EditorCaption": "" }, { "$type": "Document", - "DocumentIndex": 0, - "Title": "AppDbContext.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs", - "RelativeDocumentMoniker": "Data\\AppDbContext.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\AppDbContext.cs", - "RelativeToolTip": "Data\\AppDbContext.cs", - "ViewState": "AgIAADgAAAAAAAAAAAAuwEsAAAAAAAAAAAAAAA==", + "DocumentIndex": 10, + "Title": "IRagService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IRagService.cs", + "RelativeDocumentMoniker": "Services\\IRagService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IRagService.cs", + "RelativeToolTip": "Services\\IRagService.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAVAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-14T10:50:13.284Z", - "EditorCaption": "" + "WhenOpened": "2026-05-18T15:37:44.565Z" }, { "$type": "Document", - "DocumentIndex": 1, - "Title": "OllamaService.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs", - "RelativeDocumentMoniker": "Services\\OllamaService.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs", - "RelativeToolTip": "Services\\OllamaService.cs", - "ViewState": "AgIAADQAAAAAAAAAAAAQwEUAAABDAAAAAAAAAA==", + "DocumentIndex": 4, + "Title": "ProjectService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs", + "RelativeDocumentMoniker": "Services\\ProjectService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs", + "RelativeToolTip": "Services\\ProjectService.cs", + "ViewState": "AgIAADQAAAAAAAAAAAAqwEYAAAAWAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T06:43:46.404Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 2, - "Title": "Editor.razor", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor", - "RelativeDocumentMoniker": "Components\\Pages\\Editor.razor", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor", - "RelativeToolTip": "Components\\Pages\\Editor.razor", - "ViewState": "AgIAAFgAAAAAAAAAAAApwGsAAAAwAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", - "WhenOpened": "2026-05-16T13:01:59.743Z", + "WhenOpened": "2026-05-15T15:56:55.229Z", "EditorCaption": "" }, { "$type": "Document", "DocumentIndex": 7, - "Title": "Settings.razor", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor", - "RelativeDocumentMoniker": "Components\\Pages\\Settings.razor", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor", - "RelativeToolTip": "Components\\Pages\\Settings.razor", - "ViewState": "AgIAAHMAAAAAAAAAAADwv4UAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", - "WhenOpened": "2026-05-15T16:43:26.6Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 5, - "Title": "IOllamaService.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs", - "RelativeDocumentMoniker": "Services\\IOllamaService.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs", - "RelativeToolTip": "Services\\IOllamaService.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAqwAkAAAAPAAAAAAAAAA==", + "Title": "OllamaService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs", + "RelativeDocumentMoniker": "Services\\OllamaService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaService.cs", + "RelativeToolTip": "Services\\OllamaService.cs", + "ViewState": "AgIAAHoAAAAAAAAAAAAcwCoAAAAJAAAAAAAAAA==", "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T07:20:19.212Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 10, - "Title": "Program.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs", - "RelativeDocumentMoniker": "Program.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Program.cs", - "RelativeToolTip": "Program.cs", - "ViewState": "AgIAAAYAAAAAAAAAAAAAABUAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T06:03:12.506Z", - "EditorCaption": "" + "WhenOpened": "2026-05-16T06:43:46.404Z" }, { "$type": "Document", "DocumentIndex": 12, - "Title": "App.razor", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor", - "RelativeDocumentMoniker": "Components\\App.razor", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor", - "RelativeToolTip": "Components\\App.razor", - "ViewState": "AgIAAAYAAAAAAAAAAAAAABgAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", - "WhenOpened": "2026-05-16T05:42:06.383Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 15, - "Title": "Models.razor", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor", - "RelativeDocumentMoniker": "Components\\Pages\\Models.razor", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor", - "RelativeToolTip": "Components\\Pages\\Models.razor", - "ViewState": "AgIAAGgAAAAAAAAAAAApwHQAAAAtAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", - "WhenOpened": "2026-05-16T12:51:13.798Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 13, - "Title": "LLM_Model.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs", - "RelativeDocumentMoniker": "Models\\LLM_Model.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs", - "RelativeToolTip": "Models\\LLM_Model.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAqwBQAAAAzAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T12:53:35.486Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 14, - "Title": "ModelStatus.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs", - "RelativeDocumentMoniker": "Models\\ModelStatus.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs", - "RelativeToolTip": "Models\\ModelStatus.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAAABEAAAADAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-16T13:01:24.508Z", - "EditorCaption": "" - }, - { - "$type": "Document", - "DocumentIndex": 19, - "Title": "MainLayout.razor.css", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css", - "RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor.css", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css", - "RelativeToolTip": "Components\\Layout\\MainLayout.razor.css", - "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|", - "WhenOpened": "2026-05-15T16:04:50.455Z" - }, - { - "$type": "Document", - "DocumentIndex": 18, "Title": "NavMenu.razor", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\NavMenu.razor", "RelativeDocumentMoniker": "Components\\Layout\\NavMenu.razor", @@ -356,9 +294,118 @@ "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", "WhenOpened": "2026-05-15T16:04:37.739Z" }, + { + "$type": "Document", + "DocumentIndex": 11, + "Title": "Editor.razor", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor", + "RelativeDocumentMoniker": "Components\\Pages\\Editor.razor", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor", + "RelativeToolTip": "Components\\Pages\\Editor.razor", + "ViewState": "AgIAAAoAAACA6NuCpTYvwJkAAAAcAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", + "WhenOpened": "2026-05-16T13:01:59.743Z", + "EditorCaption": "" + }, { "$type": "Document", "DocumentIndex": 16, + "Title": "ThemeService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs", + "RelativeDocumentMoniker": "Services\\ThemeService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ThemeService.cs", + "RelativeToolTip": "Services\\ThemeService.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-16T13:35:33.991Z" + }, + { + "$type": "Document", + "DocumentIndex": 15, + "Title": "Settings.razor", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor", + "RelativeDocumentMoniker": "Components\\Pages\\Settings.razor", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Settings.razor", + "RelativeToolTip": "Components\\Pages\\Settings.razor", + "ViewState": "AgIAAHMAAAAAAAAAAADwv4UAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", + "WhenOpened": "2026-05-15T16:43:26.6Z" + }, + { + "$type": "Document", + "DocumentIndex": 14, + "Title": "IOllamaService.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs", + "RelativeDocumentMoniker": "Services\\IOllamaService.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaService.cs", + "RelativeToolTip": "Services\\IOllamaService.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAqwAkAAAAPAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-16T07:20:19.212Z" + }, + { + "$type": "Document", + "DocumentIndex": 17, + "Title": "App.razor", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor", + "RelativeDocumentMoniker": "Components\\App.razor", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\App.razor", + "RelativeToolTip": "Components\\App.razor", + "ViewState": "AgIAAAYAAAAAAAAAAAAAABgAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", + "WhenOpened": "2026-05-16T05:42:06.383Z" + }, + { + "$type": "Document", + "DocumentIndex": 20, + "Title": "Models.razor", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor", + "RelativeDocumentMoniker": "Components\\Pages\\Models.razor", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Models.razor", + "RelativeToolTip": "Components\\Pages\\Models.razor", + "ViewState": "AgIAAGgAAAAAAAAAAAApwHQAAAAtAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|", + "WhenOpened": "2026-05-16T12:51:13.798Z" + }, + { + "$type": "Document", + "DocumentIndex": 18, + "Title": "LLM_Model.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs", + "RelativeDocumentMoniker": "Models\\LLM_Model.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\LLM_Model.cs", + "RelativeToolTip": "Models\\LLM_Model.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAqwBQAAAAzAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-16T12:53:35.486Z" + }, + { + "$type": "Document", + "DocumentIndex": 19, + "Title": "ModelStatus.cs", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs", + "RelativeDocumentMoniker": "Models\\ModelStatus.cs", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\ModelStatus.cs", + "RelativeToolTip": "Models\\ModelStatus.cs", + "ViewState": "AgIAAAAAAAAAAAAAAAAAABEAAAADAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", + "WhenOpened": "2026-05-16T13:01:24.508Z" + }, + { + "$type": "Document", + "DocumentIndex": 23, + "Title": "MainLayout.razor.css", + "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css", + "RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor.css", + "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css", + "RelativeToolTip": "Components\\Layout\\MainLayout.razor.css", + "ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==", + "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|", + "WhenOpened": "2026-05-15T16:04:50.455Z" + }, + { + "$type": "Document", + "DocumentIndex": 21, "Title": "IFileScannerService.cs", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IFileScannerService.cs", "RelativeDocumentMoniker": "Services\\IFileScannerService.cs", @@ -371,18 +418,6 @@ { "$type": "Document", "DocumentIndex": 22, - "Title": "Project.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs", - "RelativeDocumentMoniker": "Models\\Project.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\Project.cs", - "RelativeToolTip": "Models\\Project.cs", - "ViewState": "AgIAAAAAAAAAAAAAAAAAAAYAAAAaAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-15T15:57:10.2Z" - }, - { - "$type": "Document", - "DocumentIndex": 17, "Title": "bootstrap-utilities.css", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css", "RelativeDocumentMoniker": "wwwroot\\lib\\bootstrap\\dist\\css\\bootstrap-utilities.css", @@ -394,19 +429,7 @@ }, { "$type": "Document", - "DocumentIndex": 20, - "Title": "ProjectService.cs", - "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs", - "RelativeDocumentMoniker": "Services\\ProjectService.cs", - "ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ProjectService.cs", - "RelativeToolTip": "Services\\ProjectService.cs", - "ViewState": "AgIAAB4AAAAAAAAAAAAowAAAAAAAAAAAAAAAAA==", - "Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|", - "WhenOpened": "2026-05-15T15:56:55.229Z" - }, - { - "$type": "Document", - "DocumentIndex": 23, + "DocumentIndex": 25, "Title": "ProjectEntity.cs", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\ProjectEntity.cs", "RelativeDocumentMoniker": "Data\\ProjectEntity.cs", @@ -418,7 +441,7 @@ }, { "$type": "Document", - "DocumentIndex": 21, + "DocumentIndex": 24, "Title": "IProjectService.cs", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IProjectService.cs", "RelativeDocumentMoniker": "Services\\IProjectService.cs", @@ -430,7 +453,7 @@ }, { "$type": "Document", - "DocumentIndex": 24, + "DocumentIndex": 26, "Title": "AuthorBuddy.Web.csproj", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj", "RelativeDocumentMoniker": "AuthorBuddy.Web.csproj", @@ -442,7 +465,7 @@ }, { "$type": "Document", - "DocumentIndex": 25, + "DocumentIndex": 27, "Title": "appsettings.json", "DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\appsettings.json", "RelativeDocumentMoniker": "appsettings.json", diff --git a/AuthorBuddy.Web.csproj b/AuthorBuddy.Web.csproj index 4204023..f812cf0 100644 --- a/AuthorBuddy.Web.csproj +++ b/AuthorBuddy.Web.csproj @@ -9,18 +9,7 @@ - - - runtime; build; native; contentfiles; analyzers; buildtransitive - all - - - - - - - PreserveNewest - + diff --git a/Components/Layout/MainLayout.razor b/Components/Layout/MainLayout.razor index 721251f..238a62d 100644 --- a/Components/Layout/MainLayout.razor +++ b/Components/Layout/MainLayout.razor @@ -1,4 +1,5 @@ @inherits LayoutComponentBase +@implements IDisposable @inject IProjectService ProjectService @inject ISettingsService SettingsService @inject IThemeService ThemeService @@ -16,24 +17,12 @@ { } - } @@ -74,17 +64,92 @@ { @foreach (var p in projects) { - + } } } - } + @if (currentProjectId > 0 && fileTree != null) + { + + } + +@if (showRenameDialog) +{ + +} + +@if (showNewFileDialog) +{ + +}
@@ -106,29 +171,93 @@ private string? activeSection; private bool showProjectList; private List? projects; + private List? fileTree; + private int currentProjectId; + private string? currentFilePath; private string statusText = "Bereit"; private bool isBusy; private double operationProgress; + private bool showRenameDialog; + private string renameNewName = string.Empty; + private FileTreeItem? renameTarget; + private bool showNewFileDialog; + private string newFileName = string.Empty; + private FileTreeItem? newFileParentDir; [Inject] private NavigationManager Navigation { get; set; } = default!; protected override async Task OnInitializedAsync() { + await SettingsService.InitializeAsync(); + Navigation.LocationChanged += OnLocationChanged; await LoadProjects(); + await LoadProjectContext(); + await AutoLoadLastProject(); + } + + private async Task AutoLoadLastProject() + { + if (currentProjectId > 0) return; + var lastId = await SettingsService.GetLastProjectIdAsync(); + if (lastId == null) return; + var project = projects?.FirstOrDefault(p => p.Id == lastId.Value); + if (project != null) + { + await SettingsService.InitializeAsync(lastId.Value); + await SelectProject(project); + } } protected override async Task OnAfterRenderAsync(bool firstRender) { if (firstRender) { - await SettingsService.InitializeAsync(); var theme = OllamaSettings.Theme; if (!string.IsNullOrEmpty(theme)) await ThemeService.ApplyTheme(theme); } } + public void Dispose() + { + Navigation.LocationChanged -= OnLocationChanged; + } + + private async void OnLocationChanged(object? sender, Microsoft.AspNetCore.Components.Routing.LocationChangedEventArgs e) + { + await LoadProjects(); + await LoadProjectContext(); + StateHasChanged(); + } + + private async Task LoadProjectContext() + { + currentProjectId = SettingsService.CurrentProjectId ?? 0; + currentFilePath = SettingsService.CurrentFilePath; + + if (currentProjectId > 0) + { + await LoadFileTree(); + } + else + { + fileTree = null; + } + } + + private async Task LoadFileTree() + { + try + { + fileTree = await ProjectService.GetProjectFileTreeAsync(currentProjectId); + } + catch + { + fileTree = new(); + } + } + private async Task LoadProjects() { try @@ -155,13 +284,7 @@ private void CreateNewProject() { activeSection = null; - Navigation.NavigateTo("/editor?action=newproject"); - } - - private void OpenProject() - { - activeSection = null; - Navigation.NavigateTo("/editor?action=open"); + Navigation.NavigateTo("/editor/newproject"); } private void OpenEditor() @@ -176,10 +299,123 @@ Navigation.NavigateTo(path); } - private void SelectProject(Project project) + private async Task SelectProject(Project project) { + activeSection = "files"; + SettingsService.CurrentProjectId = project.Id; + currentProjectId = project.Id; + SettingsService.CurrentFilePath = null; + await SettingsService.SaveLastProjectIdAsync(project.Id); + await SettingsService.LoadProjectSettingsAsync(); + await LoadFileTree(); + Navigation.NavigateTo($"/editor/index/{project.Id}"); + } + + private async Task RefreshFileTree() + { + await LoadFileTree(); + StateHasChanged(); + } + + private void BeginRename(FileTreeItem item) + { + renameTarget = item; + renameNewName = item.Name; + showRenameDialog = true; + } + + private void CancelRename() + { + showRenameDialog = false; + renameTarget = null; + renameNewName = string.Empty; + } + + private async Task ConfirmRename() + { + if (renameTarget == null || string.IsNullOrWhiteSpace(renameNewName)) return; + + try + { + var dir = Path.GetDirectoryName(renameTarget.FullPath)!; + var newPath = Path.Combine(dir, renameNewName); + if (File.Exists(newPath)) + { + statusText = "Datei existiert bereits."; + return; + } + File.Move(renameTarget.FullPath, newPath); + + if (currentFilePath == renameTarget.FullPath) + SettingsService.CurrentFilePath = newPath; + + statusText = $"Umbenannt zu: {renameNewName}"; + } + catch (Exception ex) + { + statusText = $"Fehler: {ex.Message}"; + } + finally + { + showRenameDialog = false; + renameTarget = null; + renameNewName = string.Empty; + await LoadFileTree(); + StateHasChanged(); + } + } + + private void BeginNewFile(FileTreeItem dir) + { + newFileParentDir = dir; + newFileName = string.Empty; + showNewFileDialog = true; + } + + private void CancelNewFile() + { + showNewFileDialog = false; + newFileParentDir = null; + newFileName = string.Empty; + } + + private async Task ConfirmNewFile() + { + if (newFileParentDir == null || string.IsNullOrWhiteSpace(newFileName)) return; + + try + { + var path = Path.Combine(newFileParentDir.FullPath, newFileName); + if (!path.EndsWith(".md", StringComparison.OrdinalIgnoreCase)) + path += ".md"; + if (File.Exists(path)) + { + statusText = "Datei existiert bereits."; + return; + } + File.WriteAllText(path, $"# {Path.GetFileNameWithoutExtension(path)}\n\n"); + statusText = $"Erstellt: {Path.GetFileName(path)}"; + } + catch (Exception ex) + { + statusText = $"Fehler: {ex.Message}"; + } + finally + { + showNewFileDialog = false; + newFileParentDir = null; + newFileName = string.Empty; + await LoadFileTree(); + StateHasChanged(); + } + } + + private void OpenFile(FileTreeItem item) + { + if (item.IsDirectory) return; activeSection = null; - Navigation.NavigateTo($"/editor?projectId={project.Id}"); + SettingsService.CurrentFilePath = item.FullPath; + Navigation.NavigateTo($"/editor/file/{currentProjectId}?t={DateTime.Now.Ticks}"); } public void SetStatus(string text, bool busy = false, double progress = 0) diff --git a/Components/Pages/Editor.razor b/Components/Pages/Editor.razor index 4997b65..050f597 100644 --- a/Components/Pages/Editor.razor +++ b/Components/Pages/Editor.razor @@ -3,12 +3,19 @@ @page "/editor/{action}/{projectId:int}" @inject IOllamaService OllamaService @inject IProjectService ProjectService +@inject IRagService RagService +@inject ISettingsService SettingsService +@inject IWikipediaService WikipediaService @inject NavigationManager Navigation +@implements IDisposable +@rendermode InteractiveServer + Editor - Author Buddy -
- +
+ +
@statusText @@ -17,30 +24,120 @@ { } - - - + + + + +
+ @if (showVersionDialog) + { + + } + +
+ + + + +
+
@searchQuery
+ + @if (showSpellDiff && !string.IsNullOrEmpty(correctedText)) + { +
+
+

Rechtschreibprüfung - Änderungen:

+ +
+
+ } + else if (ragResults != null && ragResults.Count > 0) + { +
+

Gefundene Fakten (RAG):

+ @foreach (var r in ragResults) + { +
+
Ähnlichkeit: @r.Similarity.ToString("P1")
+
@r.FilePath
+
@r.Text
+
+ } +
+ } + else if (wikipediaResults != null && wikipediaResults.Count > 0) + { +
+

Wikipedia-Recherche:

+ @foreach (var r in wikipediaResults) + { +
+
@(r.Language == "de" ? "🇩🇪" : "🇬🇧") @r.Language.ToUpper()
+
+ @r.Title +
+
@r.Snippet
+ @if (!string.IsNullOrEmpty(r.Extract)) + { +
@r.Extract
+ } +
+ + +
+
+ } +
+ } + else if (!string.IsNullOrEmpty(analysisResult)) + { +
+

Ergebnis:

+
@analysisResult
+
+ }
-@if (showSpellDiff && !string.IsNullOrEmpty(correctedText)) +@if (showCreateDialog) { -
-

Rechtschreibprüfung - Änderungen:

- -
-} -else if (!string.IsNullOrEmpty(analysisResult)) -{ -
-

Ergebnis:

-
@analysisResult
+ } @@ -52,19 +149,81 @@ else if (!string.IsNullOrEmpty(analysisResult)) public int? ProjectId { get; set; } private string editorContent = string.Empty; + private string searchQuery = string.Empty; private string statusText = "Bereit"; private bool isChecking; + private bool buttonDisabled; private double checkProgress; private string analysisResult = string.Empty; private string originalText = string.Empty; private string correctedText = string.Empty; private bool showSpellDiff; + private List? ragResults; + private List? wikipediaResults; + + private int currentProjectId; + private string? currentFile; + private bool showCreateDialog; + private string newProjectName = string.Empty; + private string newProjectPath = string.Empty; + private Dictionary loadedArticles = new(); + private bool showVersionDialog; + private string versionComment = string.Empty; + private VersionEntry? latestVersion; + + private record VersionEntry(string FilePath, DateTime Timestamp, string? Comment); protected override async Task OnInitializedAsync() { - if (Action == "newproject") + Navigation.LocationChanged += OnLocationChanged; + await LoadProjectContext(); + + if (string.Equals(Action, "newproject", StringComparison.OrdinalIgnoreCase)) { - statusText = "Bitte nutze die Einstellungen zum Erstellen eines neuen Projekts."; + showCreateDialog = true; + } + } + + private async void OnLocationChanged(object? sender, LocationChangedEventArgs e) + { + await LoadProjectContext(); + StateHasChanged(); + } + + public void Dispose() + { + Navigation.LocationChanged -= OnLocationChanged; + } + + private void HandleSearchInput(ChangeEventArgs e) + { + searchQuery = e.Value?.ToString() ?? string.Empty; + buttonDisabled = isChecking || string.IsNullOrWhiteSpace(searchQuery); + StateHasChanged(); + } + + private async Task LoadProjectContext() + { + if (ProjectId.HasValue) + { + currentProjectId = ProjectId.Value; + SettingsService.CurrentProjectId = currentProjectId; + } + else if (SettingsService.CurrentProjectId.HasValue) + { + currentProjectId = SettingsService.CurrentProjectId.Value; + } + + if (currentProjectId > 0) + await SettingsService.LoadProjectSettingsAsync(); + + var filePath = SettingsService.CurrentFilePath; + if (!string.IsNullOrEmpty(filePath) && File.Exists(filePath)) + { + currentFile = filePath; + editorContent = await File.ReadAllTextAsync(filePath); + statusText = $"Geladen: {Path.GetFileName(filePath)}"; + RefreshLatestVersion(); } } @@ -76,6 +235,8 @@ else if (!string.IsNullOrEmpty(analysisResult)) statusText = "Ollama analysiert Stil..."; checkProgress = 50; showSpellDiff = false; + ragResults = null; + wikipediaResults = null; try { @@ -101,6 +262,8 @@ else if (!string.IsNullOrEmpty(analysisResult)) statusText = "Ollama prüft Rechtschreibung..."; checkProgress = 50; showSpellDiff = false; + ragResults = null; + wikipediaResults = null; try { @@ -122,15 +285,314 @@ else if (!string.IsNullOrEmpty(analysisResult)) private async Task RagSearch() { - if (string.IsNullOrWhiteSpace(editorContent)) return; + if (string.IsNullOrWhiteSpace(searchQuery)) return; isChecking = true; statusText = "Suche verwandte Fakten..."; showSpellDiff = false; + analysisResult = string.Empty; + ragResults = null; + wikipediaResults = null; - await Task.Delay(500); - analysisResult = "RAG-Suche: Stelle sicher, dass ein Projekt geladen und indexiert wurde."; - statusText = "Bereit"; - isChecking = false; + try + { + if (currentProjectId == 0) + { + analysisResult = "Kein Projekt geladen. Öffne zuerst ein Projekt über die Sidebar."; + return; + } + + ragResults = await RagService.SearchAsync(currentProjectId, searchQuery, 5); + + if (ragResults.Count == 0) + { + analysisResult = "Keine relevanten Fakten gefunden. Indexiere das Projekt ggf. über 'Projekt indexieren'."; + } + } + catch (Exception ex) + { + analysisResult = $"RAG-Fehler: {ex.Message}"; + } + finally + { + statusText = "Bereit"; + isChecking = false; + } + } + + private async Task SearchWikipedia() + { + if (string.IsNullOrWhiteSpace(searchQuery)) return; + + isChecking = true; + statusText = "Suche auf Wikipedia..."; + showSpellDiff = false; + analysisResult = string.Empty; + ragResults = null; + wikipediaResults = null; + loadedArticles.Clear(); + + try + { + var request = new WikipediaSearchRequest + { + Query = searchQuery, + SearchGerman = true, + SearchEnglish = true, + MaxResults = 3 + }; + + wikipediaResults = await WikipediaService.SearchAsync(request); + + foreach (var result in wikipediaResults) + { + var extract = await WikipediaService.GetExtractAsync(result.Title, result.Language); + if (extract != null) + result.Extract = extract.Length > 800 ? extract[..800] + "..." : extract; + } + + if (wikipediaResults.Count == 0) + analysisResult = "Keine Wikipedia-Artikel gefunden."; + } + catch (Exception ex) + { + analysisResult = $"Wikipedia-Fehler: {ex.Message}"; + } + finally + { + statusText = "Bereit"; + isChecking = false; + } + } + + private async Task LoadFullArticle(WikipediaResult result) + { + statusText = $"Lade Artikel: {result.Title}..."; + + try + { + var fullText = await WikipediaService.GetFullArticleAsync(result.Title, result.Language); + if (fullText != null) + { + result.Extract = fullText; + loadedArticles[result.Url] = true; + statusText = "Artikel geladen."; + } + else + { + statusText = "Konnte Artikel nicht laden."; + } + } + catch (Exception ex) + { + statusText = $"Fehler: {ex.Message}"; + } + } + + private async Task SaveAsFact(WikipediaResult result) + { + if (string.IsNullOrWhiteSpace(result.Extract) || currentProjectId == 0) return; + + statusText = "Speichere als Fakt..."; + + try + { + var projects = await ProjectService.GetAllProjectsAsync(); + var project = projects.FirstOrDefault(p => p.Id == currentProjectId); + if (project == null) + { + statusText = "Projekt nicht gefunden."; + return; + } + + var factsDir = Path.Combine(project.RootPath, "01_Fakten"); + Directory.CreateDirectory(factsDir); + + var safeName = SanitizeFilename(searchQuery); + var filePath = Path.Combine(factsDir, $"{safeName}.md"); + + var content = $"# {result.Title}\n" + + $"- Quelle: [{result.Title}]({result.Url})\n" + + $"- Sprache: {result.Language.ToUpper()}\n" + + $"- Abgerufen: {DateTime.Now:dd.MM.yyyy HH:mm}\n\n" + + result.Extract; + + await File.WriteAllTextAsync(filePath, content); + statusText = $"Gespeichert als: {safeName}.md"; + } + catch (Exception ex) + { + statusText = $"Fehler beim Speichern: {ex.Message}"; + } + } + + private static string SanitizeFilename(string name) + { + var invalid = Path.GetInvalidFileNameChars(); + var sanitized = new string(name.Where(c => !invalid.Contains(c)).ToArray()); + return string.IsNullOrWhiteSpace(sanitized) ? "fakt" : sanitized.Trim(); + } + + private void CancelCreateProject() + { + showCreateDialog = false; + Navigation.NavigateTo("/editor"); + } + + private async Task CreateProject() + { + if (string.IsNullOrWhiteSpace(newProjectName) || string.IsNullOrWhiteSpace(newProjectPath)) return; + + isChecking = true; + statusText = "Erstelle Projekt..."; + + try + { + Directory.CreateDirectory(newProjectPath); + var id = await ProjectService.CreateNewProjectAsync(newProjectName, newProjectPath); + SettingsService.CurrentProjectId = id; + await SettingsService.LoadProjectSettingsAsync(); + showCreateDialog = false; + Navigation.NavigateTo($"/editor/index/{id}"); + } + catch (Exception ex) + { + analysisResult = $"Fehler beim Erstellen: {ex.Message}"; + } + finally + { + isChecking = false; + statusText = "Bereit"; + } + } + + private async Task IndexProject() + { + if (currentProjectId == 0) + { + analysisResult = "Kein Projekt geladen. Offne zuerst ein Projekt uber die Sidebar."; + return; + } + + isChecking = true; + statusText = "Indexiere Projekt..."; + analysisResult = string.Empty; + ragResults = null; + + try + { + var projects = await ProjectService.GetAllProjectsAsync(); + var project = projects.FirstOrDefault(p => p.Id == currentProjectId); + if (project == null) + { + analysisResult = "Projekt nicht gefunden."; + return; + } + + var p = new Progress(msg => + { + statusText = msg; + InvokeAsync(StateHasChanged); + }); + + await RagService.IndexProjectAsync(currentProjectId, project.RootPath, p); + analysisResult = "Projekt erfolgreich indexiert."; + } + catch (Exception ex) + { + analysisResult = $"Indexierungsfehler: {ex.Message}"; + } + finally + { + statusText = "Bereit"; + isChecking = false; + } + } + + private async Task SaveCurrentFile() + { + if (string.IsNullOrEmpty(currentFile)) return; + await File.WriteAllTextAsync(currentFile, editorContent); + statusText = $"Gespeichert: {Path.GetFileName(currentFile)}"; + } + + private void ShowVersionDialog() + { + showVersionDialog = true; + versionComment = string.Empty; + } + + private async Task SaveVersioned() + { + if (string.IsNullOrEmpty(currentFile)) return; + + var dir = Path.GetDirectoryName(currentFile)!; + var name = Path.GetFileNameWithoutExtension(currentFile); + var ext = Path.GetExtension(currentFile); + var versionsDir = Path.Combine(dir, "_versions"); + Directory.CreateDirectory(versionsDir); + + var stamp = DateTime.Now.ToString("yyyyMMdd_HHmmss"); + var versionPath = Path.Combine(versionsDir, $"{name}_{stamp}{ext}"); + + await File.WriteAllTextAsync(currentFile, editorContent); + await File.WriteAllTextAsync(versionPath, editorContent); + var meta = $"Kommentar: {versionComment}"; + await File.WriteAllTextAsync(versionPath + ".meta", meta); + + statusText = $"Version gespeichert: {name}_{stamp}{ext}"; + showVersionDialog = false; + versionComment = string.Empty; + RefreshLatestVersion(); + } + + private void RefreshLatestVersion() + { + latestVersion = null; + if (string.IsNullOrEmpty(currentFile)) return; + + var dir = Path.GetDirectoryName(currentFile)!; + var name = Path.GetFileNameWithoutExtension(currentFile); + var ext = Path.GetExtension(currentFile); + var versionsDir = Path.Combine(dir, "_versions"); + if (!Directory.Exists(versionsDir)) return; + + var pattern = $"{name}_*{ext}"; + var files = Directory.GetFiles(versionsDir, pattern).OrderByDescending(f => f).ToList(); + if (files.Count == 0) return; + + var latest = files.First(); + var metaFile = latest + ".meta"; + string? comment = null; + if (File.Exists(metaFile)) + { + var lines = File.ReadAllLines(metaFile); + if (lines.Length > 0 && lines[0].StartsWith("Kommentar: ")) + comment = lines[0]["Kommentar: ".Length..]; + } + + var fileName = Path.GetFileNameWithoutExtension(latest); + var stamp = fileName[(name.Length + 1)..]; + var parsed = DateTime.TryParseExact(stamp, "yyyyMMdd_HHmmss", null, + System.Globalization.DateTimeStyles.None, out var dt); + latestVersion = new VersionEntry( + latest, + parsed ? dt : File.GetLastWriteTime(latest), + comment + ); + } + + private async Task LoadLatestVersion() + { + if (latestVersion == null) return; + try + { + editorContent = await File.ReadAllTextAsync(latestVersion.FilePath); + statusText = $"Version vom {latestVersion.Timestamp:dd.MM.yyyy HH:mm} geladen."; + } + catch (Exception ex) + { + statusText = $"Fehler: {ex.Message}"; + } } } diff --git a/Components/Pages/OllamaAssistant.razor b/Components/Pages/OllamaAssistant.razor new file mode 100644 index 0000000..9b92693 --- /dev/null +++ b/Components/Pages/OllamaAssistant.razor @@ -0,0 +1,105 @@ +@page "/assistant" +@inject IOllamaService OllamaService +@inject ISettingsService SettingsService +@inject NavigationManager Navigation + +@rendermode InteractiveServer + +Assistent - Author Buddy + +
+
+ + + @statusText + @if (isRunning) + { + + } +
+ +
+
+

Eingabetext

+ +
+
+

Ergebnis

+
+ @if (!string.IsNullOrEmpty(outputText)) + { +
@outputText
+ } + else if (!string.IsNullOrEmpty(errorMessage)) + { +
@errorMessage
+ } + else + { + Ergebnis erscheint hier nach der Ausführung. + } +
+
+
+
+ +@code { + private string selectedMode = "advocatus"; + private string inputText = string.Empty; + private string outputText = string.Empty; + private string errorMessage = string.Empty; + private string statusText = "Bereit"; + private bool isRunning; + private double progress; + + protected override async Task OnInitializedAsync() + { + await base.OnInitializedAsync(); + } + + private async Task RunAssistant() + { + if (string.IsNullOrWhiteSpace(inputText)) return; + + isRunning = true; + progress = 50; + outputText = string.Empty; + errorMessage = string.Empty; + statusText = "Verarbeite..."; + + try + { + var systemPrompt = selectedMode switch + { + "advocatus" => await SettingsService.GetAdvocatusDiaboliPromptAsync() ?? string.Empty, + "hookfinder" => await SettingsService.GetHookFinderPromptAsync() ?? string.Empty, + "eli5" => await SettingsService.GetEli5PromptAsync() ?? string.Empty, + _ => string.Empty + }; + + if (string.IsNullOrEmpty(systemPrompt)) + { + errorMessage = "Kein System-Prompt für diesen Modus konfiguriert. Bitte in den Einstellungen hinterlegen."; + return; + } + + var model = await SettingsService.GetStyleModelAsync() ?? "llama3.2"; + outputText = await OllamaService.Chat(model, inputText, systemPrompt); + statusText = "Fertig."; + } + catch (Exception ex) + { + errorMessage = $"Fehler: {ex.Message}"; + statusText = "Fehler"; + } + finally + { + isRunning = false; + progress = 0; + } + } +} diff --git a/Components/Pages/Settings.razor b/Components/Pages/Settings.razor index f289df2..c95a6ac 100644 --- a/Components/Pages/Settings.razor +++ b/Components/Pages/Settings.razor @@ -32,7 +32,7 @@
-
+

Modelle

@@ -81,7 +81,7 @@
-
Adresse des Ollama-Servers (z. B. http://192.168.1.100:11434)
+
Adresse des Ollama-Servers
@@ -98,7 +98,7 @@
-
+

Lektor-System-Prompt

@@ -121,6 +121,29 @@
+
+
+

Advocatus Diaboli

+
+ +
+
+ +
+

Hook-Finder

+
+ +
+
+ +
+

ELI5-Vereinfachung

+
+ +
+
+
+
@@ -142,6 +165,9 @@ private string lektorPrompt = string.Empty; private string factCheckTemplate = string.Empty; private string spellingPrompt = string.Empty; + private string advocatusDiaboliPrompt = string.Empty; + private string hookFinderPrompt = string.Empty; + private string eli5Prompt = string.Empty; private string newProjectName = string.Empty; private string newUserName = string.Empty; private string statusMessage = string.Empty; @@ -155,16 +181,7 @@ var current = await SettingsService.GetCurrentUserAsync(); selectedUserId = current.Id; - var userSettings = await LoadUserSettingsAsync(); - - ragModel = userSettings.ragModel; - styleModel = userSettings.styleModel; - spellingModel = userSettings.spellingModel; - selectedTheme = userSettings.selectedTheme; - ollamaUrl = userSettings.ollamaUrl; - lektorPrompt = userSettings.lektorPrompt; - factCheckTemplate = userSettings.factCheckTemplate; - spellingPrompt = userSettings.spellingPrompt; + await LoadUserSettings(); try { @@ -177,18 +194,19 @@ } } - private async Task<(string ragModel, string styleModel, string spellingModel, string selectedTheme, string ollamaUrl, string lektorPrompt, string factCheckTemplate, string spellingPrompt)> LoadUserSettingsAsync() + private async Task LoadUserSettings() { - return ( - await SettingsService.GetRagModelAsync() ?? "llama3.2", - await SettingsService.GetStyleModelAsync() ?? "llama3.2", - await SettingsService.GetSpellingModelAsync() ?? "phi3:mini", - await SettingsService.GetThemeAsync() ?? "light", - await SettingsService.GetOllamaUrlAsync() ?? "http://localhost:11434", - await SettingsService.GetLektorSystemPromptAsync() ?? string.Empty, - await SettingsService.GetFactCheckTemplateAsync() ?? string.Empty, - await SettingsService.GetSpellingPromptAsync() ?? string.Empty - ); + ragModel = await SettingsService.GetRagModelAsync() ?? "llama3.2"; + styleModel = await SettingsService.GetStyleModelAsync() ?? "llama3.2"; + spellingModel = await SettingsService.GetSpellingModelAsync() ?? "phi3:mini"; + selectedTheme = await SettingsService.GetThemeAsync() ?? "light"; + ollamaUrl = await SettingsService.GetOllamaUrlAsync() ?? "http://localhost:11434"; + lektorPrompt = await SettingsService.GetLektorSystemPromptAsync() ?? string.Empty; + factCheckTemplate = await SettingsService.GetFactCheckTemplateAsync() ?? string.Empty; + spellingPrompt = await SettingsService.GetSpellingPromptAsync() ?? string.Empty; + advocatusDiaboliPrompt = await SettingsService.GetAdvocatusDiaboliPromptAsync() ?? string.Empty; + hookFinderPrompt = await SettingsService.GetHookFinderPromptAsync() ?? string.Empty; + eli5Prompt = await SettingsService.GetEli5PromptAsync() ?? string.Empty; } private async Task SwitchUser(ChangeEventArgs e) @@ -197,15 +215,7 @@ await SettingsService.SetCurrentUserAsync(userId); selectedUserId = userId; - var userSettings = await LoadUserSettingsAsync(); - ragModel = userSettings.ragModel; - styleModel = userSettings.styleModel; - spellingModel = userSettings.spellingModel; - selectedTheme = userSettings.selectedTheme; - ollamaUrl = userSettings.ollamaUrl; - lektorPrompt = userSettings.lektorPrompt; - factCheckTemplate = userSettings.factCheckTemplate; - spellingPrompt = userSettings.spellingPrompt; + await LoadUserSettings(); } private async Task AddUser() @@ -221,6 +231,17 @@ private async Task SaveSettings() { await SettingsService.SaveOllamaUrlAsync(ollamaUrl); + OllamaService.Reconfigure(ollamaUrl); + + if (!string.IsNullOrWhiteSpace(ragModel)) + { + var ok = await OllamaService.TestEmbeddingAsync(ragModel); + if (!ok) + { + statusMessage = $"Modell '{ragModel}' unterstützt keine Embeddings. RAG-Modell nicht gespeichert."; + return; + } + } await SettingsService.SaveRagModelAsync(ragModel); await SettingsService.SaveStyleModelAsync(styleModel); await SettingsService.SaveSpellingModelAsync(spellingModel); @@ -228,7 +249,9 @@ await SettingsService.SaveLektorSystemPromptAsync(lektorPrompt); await SettingsService.SaveFactCheckTemplateAsync(factCheckTemplate); await SettingsService.SaveSpellingPromptAsync(spellingPrompt); - OllamaService.Reconfigure(ollamaUrl); + await SettingsService.SaveAdvocatusDiaboliPromptAsync(advocatusDiaboliPrompt); + await SettingsService.SaveHookFinderPromptAsync(hookFinderPrompt); + await SettingsService.SaveEli5PromptAsync(eli5Prompt); statusMessage = "Einstellungen gespeichert."; await ThemeService.ApplyTheme(selectedTheme); diff --git a/Components/_Imports.razor b/Components/_Imports.razor index 37a6891..16d44bb 100644 --- a/Components/_Imports.razor +++ b/Components/_Imports.razor @@ -12,4 +12,3 @@ @using AuthorBuddy.Web.Models @using AuthorBuddy.Web.Services @using AuthorBuddy.Web.Data -@using Microsoft.EntityFrameworkCore diff --git a/Data/AppDbContext.cs b/Data/AppDbContext.cs deleted file mode 100644 index 1338d93..0000000 --- a/Data/AppDbContext.cs +++ /dev/null @@ -1,81 +0,0 @@ -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; - -namespace AuthorBuddy.Web.Data; - -public class AppDbContext : DbContext -{ - public DbSet Projects => Set(); - public DbSet Settings => Set(); - public DbSet DocumentMeta => Set(); - public DbSet Users => Set(); - - public AppDbContext(DbContextOptions options) : base(options) { } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - modelBuilder.Entity(entity => - { - entity.ToTable("Projects"); - entity.HasKey(e => e.Id); - entity.Property(e => e.Name).IsRequired(); - entity.Property(e => e.RootPath).IsRequired(); - entity.Property(e => e.LastOpened); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("Settings"); - entity.HasKey(e => e.Id); - entity.Property(e => e.Key).HasColumnName("key").IsRequired(); - entity.Property(e => e.Value).HasColumnName("value").IsRequired(); - entity.Property(e => e.UserId).HasColumnName("user_id"); - entity.Property(e => e.ProjectId).HasColumnName("project_id"); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("Users"); - entity.HasKey(e => e.Id); - entity.Property(e => e.Name).HasColumnName("name").IsRequired(); - entity.Property(e => e.IsCurrent).HasColumnName("is_current"); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("DocumentMeta"); - entity.HasKey(e => e.Id); - entity.Property(e => e.FilePath).HasColumnName("file_path").IsRequired(); - entity.Property(e => e.LastModified).HasColumnName("last_modified"); - entity.Property(e => e.FileType).HasColumnName("file_type"); - entity.Property(e => e.ProjectId).HasColumnName("project_id"); - }); - } - - public static void SetupVecExtension(string connectionString) - { - using var connection = new SqliteConnection(connectionString); - connection.Open(); - connection.EnableExtensions(true); - - try - { - connection.LoadExtension("vec0"); - using var testCmd = connection.CreateCommand(); - testCmd.CommandText = "SELECT vec_version();"; - var version = testCmd.ExecuteScalar(); - Console.WriteLine($"sqlite-vec Version: {version}"); - } - catch (SqliteException ex) - { - Console.WriteLine($"Fehler beim Laden von vec0: {ex.Message}"); - } - - using var createVecCmd = connection.CreateCommand(); - createVecCmd.CommandText = @" - CREATE VIRTUAL TABLE IF NOT EXISTS vec_documents USING vec0( - embedding float[1024] - );"; - createVecCmd.ExecuteNonQuery(); - } -} diff --git a/Data/DocumentMetaEntity.cs b/Data/DocumentMetaEntity.cs index 7a86abe..ea60b33 100644 --- a/Data/DocumentMetaEntity.cs +++ b/Data/DocumentMetaEntity.cs @@ -1,23 +1,11 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - namespace AuthorBuddy.Web.Data; -[Table("DocumentMeta")] public class DocumentMetaEntity { - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } - public int? ProjectId { get; set; } - - [Required] public string FilePath { get; set; } = string.Empty; - public DateTime? LastModified { get; set; } - public string? Hash { get; set; } - public string? FileType { get; set; } } diff --git a/Data/DocumentVector.cs b/Data/DocumentVector.cs new file mode 100644 index 0000000..9c87b4b --- /dev/null +++ b/Data/DocumentVector.cs @@ -0,0 +1,11 @@ +namespace AuthorBuddy.Web.Data; + +public class DocumentVector +{ + public int Id { get; set; } + public int DocumentMetaId { get; set; } + public int ProjectId { get; set; } + public string Text { get; set; } = string.Empty; + public string Hash { get; set; } = string.Empty; + public float[] Embedding { get; set; } = Array.Empty(); +} diff --git a/Data/ProjectEntity.cs b/Data/ProjectEntity.cs index 6fe345f..1f7b03c 100644 --- a/Data/ProjectEntity.cs +++ b/Data/ProjectEntity.cs @@ -1,20 +1,9 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - namespace AuthorBuddy.Web.Data; -[Table("Projects")] public class ProjectEntity { - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } - - [Required] public string Name { get; set; } = string.Empty; - - [Required] public string RootPath { get; set; } = string.Empty; - public DateTime? LastOpened { get; set; } } diff --git a/Data/SettingEntity.cs b/Data/SettingEntity.cs index fa31c04..ccaf505 100644 --- a/Data/SettingEntity.cs +++ b/Data/SettingEntity.cs @@ -1,22 +1,12 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; +using LiteDB; namespace AuthorBuddy.Web.Data; -[Table("Settings")] public class SettingEntity { - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } - - [Required] public string Key { get; set; } = string.Empty; - - [Required] public string Value { get; set; } = string.Empty; - public int? UserId { get; set; } - public int? ProjectId { get; set; } } diff --git a/Data/UserEntity.cs b/Data/UserEntity.cs index 5070a76..7ecf395 100644 --- a/Data/UserEntity.cs +++ b/Data/UserEntity.cs @@ -1,17 +1,8 @@ -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - namespace AuthorBuddy.Web.Data; -[Table("Users")] public class UserEntity { - [Key] - [DatabaseGenerated(DatabaseGeneratedOption.Identity)] public int Id { get; set; } - - [Required] public string Name { get; set; } = string.Empty; - public bool IsCurrent { get; set; } } diff --git a/Migrations/20260516151053_InitialCreate.Designer.cs b/Migrations/20260516151053_InitialCreate.Designer.cs deleted file mode 100644 index 8473c70..0000000 --- a/Migrations/20260516151053_InitialCreate.Designer.cs +++ /dev/null @@ -1,127 +0,0 @@ -// -using System; -using AuthorBuddy.Web.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Migrations; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace AuthorBuddy.Web.Migrations -{ - [DbContext(typeof(AppDbContext))] - [Migration("20260516151053_InitialCreate")] - partial class InitialCreate - { - /// - protected override void BuildTargetModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "10.0.8"); - - modelBuilder.Entity("AuthorBuddy.Web.Data.DocumentMetaEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("FilePath") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("file_path"); - - b.Property("FileType") - .HasColumnType("TEXT") - .HasColumnName("file_type"); - - b.Property("Hash") - .HasColumnType("TEXT"); - - b.Property("LastModified") - .HasColumnType("TEXT") - .HasColumnName("last_modified"); - - b.Property("ProjectId") - .HasColumnType("INTEGER") - .HasColumnName("project_id"); - - b.HasKey("Id"); - - b.ToTable("DocumentMeta", (string)null); - }); - - modelBuilder.Entity("AuthorBuddy.Web.Data.ProjectEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("LastOpened") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("RootPath") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Projects", (string)null); - }); - - modelBuilder.Entity("AuthorBuddy.Web.Data.SettingEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Key") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("key"); - - b.Property("ProjectId") - .HasColumnType("INTEGER") - .HasColumnName("project_id"); - - b.Property("UserId") - .HasColumnType("INTEGER") - .HasColumnName("user_id"); - - b.Property("Value") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("value"); - - b.HasKey("Id"); - - b.ToTable("Settings", (string)null); - }); - - modelBuilder.Entity("AuthorBuddy.Web.Data.UserEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("IsCurrent") - .HasColumnType("INTEGER") - .HasColumnName("is_current"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("name"); - - b.HasKey("Id"); - - b.ToTable("Users", (string)null); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Migrations/20260516151053_InitialCreate.cs b/Migrations/20260516151053_InitialCreate.cs deleted file mode 100644 index 2a9f35c..0000000 --- a/Migrations/20260516151053_InitialCreate.cs +++ /dev/null @@ -1,93 +0,0 @@ -using System; -using Microsoft.EntityFrameworkCore.Migrations; - -#nullable disable - -namespace AuthorBuddy.Web.Migrations -{ - /// - public partial class InitialCreate : Migration - { - /// - protected override void Up(MigrationBuilder migrationBuilder) - { - migrationBuilder.CreateTable( - name: "DocumentMeta", - columns: table => new - { - Id = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - project_id = table.Column(type: "INTEGER", nullable: true), - file_path = table.Column(type: "TEXT", nullable: false), - last_modified = table.Column(type: "TEXT", nullable: true), - Hash = table.Column(type: "TEXT", nullable: true), - file_type = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_DocumentMeta", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Projects", - columns: table => new - { - Id = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - Name = table.Column(type: "TEXT", nullable: false), - RootPath = table.Column(type: "TEXT", nullable: false), - LastOpened = table.Column(type: "TEXT", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Projects", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Settings", - columns: table => new - { - Id = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - key = table.Column(type: "TEXT", nullable: false), - value = table.Column(type: "TEXT", nullable: false), - user_id = table.Column(type: "INTEGER", nullable: true), - project_id = table.Column(type: "INTEGER", nullable: true) - }, - constraints: table => - { - table.PrimaryKey("PK_Settings", x => x.Id); - }); - - migrationBuilder.CreateTable( - name: "Users", - columns: table => new - { - Id = table.Column(type: "INTEGER", nullable: false) - .Annotation("Sqlite:Autoincrement", true), - name = table.Column(type: "TEXT", nullable: false), - is_current = table.Column(type: "INTEGER", nullable: false) - }, - constraints: table => - { - table.PrimaryKey("PK_Users", x => x.Id); - }); - } - - /// - protected override void Down(MigrationBuilder migrationBuilder) - { - migrationBuilder.DropTable( - name: "DocumentMeta"); - - migrationBuilder.DropTable( - name: "Projects"); - - migrationBuilder.DropTable( - name: "Settings"); - - migrationBuilder.DropTable( - name: "Users"); - } - } -} diff --git a/Migrations/AppDbContextModelSnapshot.cs b/Migrations/AppDbContextModelSnapshot.cs deleted file mode 100644 index 089a0a3..0000000 --- a/Migrations/AppDbContextModelSnapshot.cs +++ /dev/null @@ -1,124 +0,0 @@ -// -using System; -using AuthorBuddy.Web.Data; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; - -#nullable disable - -namespace AuthorBuddy.Web.Migrations -{ - [DbContext(typeof(AppDbContext))] - partial class AppDbContextModelSnapshot : ModelSnapshot - { - protected override void BuildModel(ModelBuilder modelBuilder) - { -#pragma warning disable 612, 618 - modelBuilder.HasAnnotation("ProductVersion", "10.0.8"); - - modelBuilder.Entity("AuthorBuddy.Web.Data.DocumentMetaEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("FilePath") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("file_path"); - - b.Property("FileType") - .HasColumnType("TEXT") - .HasColumnName("file_type"); - - b.Property("Hash") - .HasColumnType("TEXT"); - - b.Property("LastModified") - .HasColumnType("TEXT") - .HasColumnName("last_modified"); - - b.Property("ProjectId") - .HasColumnType("INTEGER") - .HasColumnName("project_id"); - - b.HasKey("Id"); - - b.ToTable("DocumentMeta", (string)null); - }); - - modelBuilder.Entity("AuthorBuddy.Web.Data.ProjectEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("LastOpened") - .HasColumnType("TEXT"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT"); - - b.Property("RootPath") - .IsRequired() - .HasColumnType("TEXT"); - - b.HasKey("Id"); - - b.ToTable("Projects", (string)null); - }); - - modelBuilder.Entity("AuthorBuddy.Web.Data.SettingEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("Key") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("key"); - - b.Property("ProjectId") - .HasColumnType("INTEGER") - .HasColumnName("project_id"); - - b.Property("UserId") - .HasColumnType("INTEGER") - .HasColumnName("user_id"); - - b.Property("Value") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("value"); - - b.HasKey("Id"); - - b.ToTable("Settings", (string)null); - }); - - modelBuilder.Entity("AuthorBuddy.Web.Data.UserEntity", b => - { - b.Property("Id") - .ValueGeneratedOnAdd() - .HasColumnType("INTEGER"); - - b.Property("IsCurrent") - .HasColumnType("INTEGER") - .HasColumnName("is_current"); - - b.Property("Name") - .IsRequired() - .HasColumnType("TEXT") - .HasColumnName("name"); - - b.HasKey("Id"); - - b.ToTable("Users", (string)null); - }); -#pragma warning restore 612, 618 - } - } -} diff --git a/Models/Constants.cs b/Models/Constants.cs index e48f311..f8a9a23 100644 --- a/Models/Constants.cs +++ b/Models/Constants.cs @@ -10,5 +10,5 @@ public static class Constants "Exports" }; - public const string SQLiteConnection = "Data Source=author_brain.db"; + public const string DbFile = "author_brain.db"; } diff --git a/Models/FileTreeItem.cs b/Models/FileTreeItem.cs new file mode 100644 index 0000000..e81651b --- /dev/null +++ b/Models/FileTreeItem.cs @@ -0,0 +1,9 @@ +namespace AuthorBuddy.Web.Models; + +public class FileTreeItem +{ + public string Name { get; set; } = string.Empty; + public string FullPath { get; set; } = string.Empty; + public bool IsDirectory { get; set; } + public List Children { get; set; } = new(); +} diff --git a/Models/OllamaSettings.cs b/Models/OllamaSettings.cs index c88e17b..8f66ebe 100644 --- a/Models/OllamaSettings.cs +++ b/Models/OllamaSettings.cs @@ -2,8 +2,8 @@ namespace AuthorBuddy.Web.Models; public class OllamaSettings { - public string StyleModel { get; set; } = "llama3.2"; - public string EmbedModel { get; set; } = "mxbai-embed-large"; + public string StyleModel { get; set; } = "gemma4:e4b"; + public string EmbedModel { get; set; } = "nomic-embed-text"; public string SpellingModel { get; set; } = "phi3:mini"; public string OllamaUrl { get; set; } = "http://localhost:11434"; public string Theme { get; set; } = "light"; @@ -20,6 +20,12 @@ public class OllamaSettings public string SpellingPrompt { get; set; } = "Du bist ein Rechtschreibprüfer. Korrigiere NUR Fehler (Rechtschreibung/Grammatik). " + "Ändere den Stil nicht. Gib nur den korrigierten Text zurück."; + public string AdvocatusDiaboliPrompt { get; set; } = "Nimm eine radikale Gegenposition zu meinem Text ein. Welche Schwachstellen in meiner Argumentation fallen dir auf?"; + + public string HookFinderPrompt { get; set; } = "Generiere mir 5 emotionale, packende Einstiegssätze (Hooks) für diesen Text, die den Leser sofort fesseln."; + + public string Eli5Prompt { get; set; } = "Wo ist der Text zu kompliziert geschrieben? Liefere mir konkrete Vorschläge, wie ich komplexe Absätze einfacher formulieren kann, ohne den Kern zu verlieren."; + public string GetFactCheckPrompt(string question, string facts) { return string.Format(FactCheckTemplate, question, facts); diff --git a/Models/WikipediaResult.cs b/Models/WikipediaResult.cs new file mode 100644 index 0000000..6a8a3d9 --- /dev/null +++ b/Models/WikipediaResult.cs @@ -0,0 +1,18 @@ +namespace AuthorBuddy.Web.Models; + +public class WikipediaResult +{ + public string Title { get; set; } = string.Empty; + public string Snippet { get; set; } = string.Empty; + public string Url { get; set; } = string.Empty; + public string Language { get; set; } = "de"; + public string? Extract { get; set; } +} + +public class WikipediaSearchRequest +{ + public string Query { get; set; } = string.Empty; + public bool SearchGerman { get; set; } = true; + public bool SearchEnglish { get; set; } = true; + public int MaxResults { get; set; } = 3; +} diff --git a/Program.cs b/Program.cs index 1e900a9..4291a7a 100644 --- a/Program.cs +++ b/Program.cs @@ -2,32 +2,29 @@ using AuthorBuddy.Web.Components; using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; using AuthorBuddy.Web.Services; -using Microsoft.EntityFrameworkCore; +using LiteDB; var builder = WebApplication.CreateBuilder(args); builder.Services.AddRazorComponents() .AddInteractiveServerComponents(); +var dbPath = Path.Combine(AppContext.BaseDirectory, Constants.DbFile); +builder.Services.AddSingleton(new LiteDatabase($"Filename={dbPath};Connection=direct")); + builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddSingleton(); builder.Services.AddScoped(); builder.Services.AddSingleton(); - -builder.Services.AddDbContextFactory(options => - options.UseSqlite(Constants.SQLiteConnection)); +builder.Services.AddSingleton(); +builder.Services.AddSingleton(); var app = builder.Build(); -AppDbContext.SetupVecExtension(Constants.SQLiteConnection); - -await using var db = await app.Services.GetRequiredService>().CreateDbContextAsync(); -await db.Database.MigrateAsync(); - var settingsInit = app.Services.GetRequiredService(); -await settingsInit.InitializeAsync(); +settingsInit.InitializeAsync().GetAwaiter().GetResult(); if (!app.Environment.IsDevelopment()) { diff --git a/Services/FileScannerService.cs b/Services/FileScannerService.cs index 42d2d5e..2948ba4 100644 --- a/Services/FileScannerService.cs +++ b/Services/FileScannerService.cs @@ -1,20 +1,17 @@ using AuthorBuddy.Web.Data; -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; +using LiteDB; namespace AuthorBuddy.Web.Services; public class FileScannerService : IFileScannerService { private readonly IOllamaService _ollama; - private readonly IDbContextFactory _contextFactory; - private readonly string _connectionString; + private readonly ILiteDatabase _db; - public FileScannerService(IOllamaService ollama, IDbContextFactory contextFactory) + public FileScannerService(IOllamaService ollama, ILiteDatabase db) { _ollama = ollama; - _contextFactory = contextFactory; - _connectionString = Models.Constants.SQLiteConnection; + _db = db; } public async Task ScanProjectFolderAsync(int projectId, string rootPath, IProgress progress) @@ -25,77 +22,55 @@ public class FileScannerService : IFileScannerService foreach (var file in files) { var fileInfo = new FileInfo(file); - bool needsUpdate = await CheckIfUpdateRequired(projectId, file, fileInfo.LastWriteTimeUtc); + bool needsUpdate = CheckIfUpdateRequired(projectId, file, fileInfo.LastWriteTimeUtc); if (needsUpdate) { progress?.Report($"Verarbeite: {fileInfo.Name}..."); - await ProcessAndVectorizeFile(projectId, file, fileInfo.LastWriteTimeUtc); + await ProcessFile(projectId, file, fileInfo.LastWriteTimeUtc); } } progress?.Report("Scan abgeschlossen."); } - private async Task CheckIfUpdateRequired(int projectId, string file, DateTime lastWriteTimeUtc) + private bool CheckIfUpdateRequired(int projectId, string file, DateTime lastWriteTimeUtc) { - await using var context = await _contextFactory.CreateDbContextAsync(); - - var meta = await context.DocumentMeta - .Where(m => m.ProjectId == projectId && m.FilePath == file) - .Select(m => m.LastModified) - .FirstOrDefaultAsync(); - + var col = _db.GetCollection("documentMeta"); + var meta = col.FindOne(m => m.ProjectId == projectId && m.FilePath == file); if (meta == null) return true; - return meta.Value < lastWriteTimeUtc; + return meta.LastModified < lastWriteTimeUtc; } - private async Task ProcessAndVectorizeFile(int projectId, string path, DateTime lastModified) + private async Task ProcessFile(int projectId, string path, DateTime lastModified) { string content = await File.ReadAllTextAsync(path); - //var vector = await _ollama.GetEmbeddingAsync(content); - - using var connection = new SqliteConnection(_connectionString); - connection.Open(); - using var transaction = connection.BeginTransaction(); - try { - var cmdMeta = connection.CreateCommand(); - cmdMeta.CommandText = @" - INSERT INTO DocumentMeta (project_id, file_path, last_modified) - VALUES (@pid, @path, @mod) - ON CONFLICT(file_path) DO UPDATE SET last_modified = @mod - RETURNING id;"; - cmdMeta.Parameters.AddWithValue("@pid", projectId); - cmdMeta.Parameters.AddWithValue("@path", path); - cmdMeta.Parameters.AddWithValue("@mod", lastModified); + var col = _db.GetCollection("documentMeta"); + var existing = col.FindOne(m => m.ProjectId == projectId && m.FilePath == path); - var docId = await cmdMeta.ExecuteScalarAsync(); + if (existing != null) + { + existing.LastModified = lastModified; + col.Update(existing); + } + else + { + col.Insert(new DocumentMetaEntity + { + ProjectId = projectId, + FilePath = path, + LastModified = lastModified, + FileType = ".md" + }); + } - byte[] blob = [];// ConvertVectorToBytes(vector); - var cmdVec = connection.CreateCommand(); - cmdVec.CommandText = "INSERT INTO vec_documents(rowid, embedding) VALUES (@id, @vec) " + - "ON CONFLICT(rowid) DO UPDATE SET embedding = @vec;"; - cmdVec.Parameters.AddWithValue("@id", docId); - cmdVec.Parameters.AddWithValue("@vec", blob); - await cmdVec.ExecuteNonQueryAsync(); - - transaction.Commit(); return true; } catch { - transaction.Rollback(); return false; } } - - private static byte[] ConvertVectorToBytes(IList vector) - { - float[] floatVector = vector.Select(d => (float)d).ToArray(); - byte[] bytes = new byte[floatVector.Length * sizeof(float)]; - Buffer.BlockCopy(floatVector, 0, bytes, 0, bytes.Length); - return bytes; - } } diff --git a/Services/IOllamaService.cs b/Services/IOllamaService.cs index 954cac7..4afa12d 100644 --- a/Services/IOllamaService.cs +++ b/Services/IOllamaService.cs @@ -9,5 +9,8 @@ public interface IOllamaService Task> GetLocalModelsAsync(); Task PullModelAsync(string modelName, IProgress progress); Task QuickSpellCheckAsync(string text); + Task GetEmbeddingAsync(string text); + Task TestEmbeddingAsync(string model); + Task Chat(string model, string userMessage, string? systemPrompt = null); void Reconfigure(string ollamaUrl); } diff --git a/Services/IProjectService.cs b/Services/IProjectService.cs index f7742be..6896e6d 100644 --- a/Services/IProjectService.cs +++ b/Services/IProjectService.cs @@ -6,5 +6,7 @@ public interface IProjectService { Task CreateNewProjectAsync(string projectName, string rootPath); Task> GetAllProjectsAsync(); + Task GetProjectAsync(int projectId); + Task> GetProjectFileTreeAsync(int projectId); Task SyncProjectFilesAsync(int projectId, string directoryPath, IProgress progress); } diff --git a/Services/IRagService.cs b/Services/IRagService.cs new file mode 100644 index 0000000..a5b7ae0 --- /dev/null +++ b/Services/IRagService.cs @@ -0,0 +1,14 @@ +namespace AuthorBuddy.Web.Services; + +public class RagSearchResult +{ + public string Text { get; set; } = string.Empty; + public string FilePath { get; set; } = string.Empty; + public double Similarity { get; set; } +} + +public interface IRagService +{ + Task IndexProjectAsync(int projectId, string rootPath, IProgress progress); + Task> SearchAsync(int projectId, string query, int topN = 5); +} diff --git a/Services/ISettingsService.cs b/Services/ISettingsService.cs index 761bbed..7feea40 100644 --- a/Services/ISettingsService.cs +++ b/Services/ISettingsService.cs @@ -18,10 +18,16 @@ public interface ISettingsService Task GetLektorSystemPromptAsync(); Task GetFactCheckTemplateAsync(); Task GetSpellingPromptAsync(); + Task GetAdvocatusDiaboliPromptAsync(); + Task GetHookFinderPromptAsync(); + Task GetEli5PromptAsync(); Task SaveLektorSystemPromptAsync(string prompt); Task SaveFactCheckTemplateAsync(string prompt); Task SaveSpellingPromptAsync(string prompt); + Task SaveAdvocatusDiaboliPromptAsync(string prompt); + Task SaveHookFinderPromptAsync(string prompt); + Task SaveEli5PromptAsync(string prompt); Task> GetUsersAsync(); Task GetCurrentUserAsync(); @@ -29,6 +35,10 @@ public interface ISettingsService Task CreateUserAsync(string name); int? CurrentProjectId { get; set; } + string? CurrentFilePath { get; set; } + Task GetLastProjectIdAsync(); + Task SaveLastProjectIdAsync(int projectId); + Task LoadProjectSettingsAsync(); - Task InitializeAsync(); + Task InitializeAsync(int projectId = -1); } diff --git a/Services/IWikipediaService.cs b/Services/IWikipediaService.cs new file mode 100644 index 0000000..d1b3134 --- /dev/null +++ b/Services/IWikipediaService.cs @@ -0,0 +1,11 @@ +using AuthorBuddy.Web.Models; + +namespace AuthorBuddy.Web.Services; + +public interface IWikipediaService +{ + Task> SearchAsync(string query, int maxResults = 3); + Task> SearchAsync(WikipediaSearchRequest request); + Task GetExtractAsync(string title, string language = "de"); + Task GetFullArticleAsync(string title, string language = "de"); +} diff --git a/Services/OllamaService.cs b/Services/OllamaService.cs index b083082..470dfff 100644 --- a/Services/OllamaService.cs +++ b/Services/OllamaService.cs @@ -22,6 +22,53 @@ public class OllamaService : IOllamaService _baseUrl = url; } + public async Task TestEmbeddingAsync(string model) + { + try + { + var request = new { model = model, prompt = "test" }; + var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}/api/embeddings", request); + if (!response.IsSuccessStatusCode) return false; + + var content = await response.Content.ReadAsStringAsync(); + using var doc = JsonDocument.Parse(content); + if (doc.RootElement.TryGetProperty("embedding", out var embedding)) + return embedding.EnumerateArray().Any(); + + return false; + } + catch + { + return false; + } + } + + public async Task GetEmbeddingAsync(string text) + { + try + { + var request = new { model = _settings.EmbedModel, prompt = text }; + var response = await _httpClient.PostAsJsonAsync($"{_baseUrl}/api/embeddings", request); + if (response.IsSuccessStatusCode) + { + var content = await response.Content.ReadAsStringAsync(); + using var doc = JsonDocument.Parse(content); + if (doc.RootElement.TryGetProperty("embedding", out var embedding)) + { + var list = new List(); + foreach (var val in embedding.EnumerateArray()) + list.Add(val.GetSingle()); + return list.ToArray(); + } + } + return Array.Empty(); + } + catch + { + return Array.Empty(); + } + } + public async Task> GetLocalModelsAsync() { try diff --git a/Services/ProjectService.cs b/Services/ProjectService.cs index 4aedc48..e4369b0 100644 --- a/Services/ProjectService.cs +++ b/Services/ProjectService.cs @@ -1,21 +1,21 @@ using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; -using Microsoft.EntityFrameworkCore; +using LiteDB; namespace AuthorBuddy.Web.Services; public class ProjectService : IProjectService { - private readonly IDbContextFactory _contextFactory; + private readonly ILiteDatabase _db; private readonly IFileScannerService _fileScannerService; - public ProjectService(IDbContextFactory contextFactory, IFileScannerService fileScannerService) + public ProjectService(ILiteDatabase db, IFileScannerService fileScannerService) { - _contextFactory = contextFactory; + _db = db; _fileScannerService = fileScannerService; } - public async Task CreateNewProjectAsync(string projectName, string rootPath) + public Task CreateNewProjectAsync(string projectName, string rootPath) { string[] subFolders = { "00_Drafts", "01_Fakten", "02_Characters", "03_Archive" }; foreach (var subFolder in subFolders) @@ -24,10 +24,9 @@ public class ProjectService : IProjectService } string readmePath = Path.Combine(rootPath, "00_Projekt_Info.md"); - await File.WriteAllTextAsync(readmePath, $"# Projekt: {projectName}\nErstellt am: {DateTime.Now}"); - - await using var context = await _contextFactory.CreateDbContextAsync(); + File.WriteAllText(readmePath, $"# Projekt: {projectName}\nErstellt am: {DateTime.Now}"); + var col = _db.GetCollection("projects"); var entity = new ProjectEntity { Name = projectName, @@ -35,17 +34,14 @@ public class ProjectService : IProjectService LastOpened = DateTime.Now }; - context.Projects.Add(entity); - await context.SaveChangesAsync(); - - return entity.Id; + col.Insert(entity); + return Task.FromResult(entity.Id); } - public async Task> GetAllProjectsAsync() + public Task> GetAllProjectsAsync() { - await using var context = await _contextFactory.CreateDbContextAsync(); - - return await context.Projects + var col = _db.GetCollection("projects"); + var list = col.FindAll() .OrderByDescending(p => p.LastOpened) .Select(p => new Project { @@ -53,7 +49,64 @@ public class ProjectService : IProjectService Name = p.Name, RootPath = p.RootPath }) - .ToListAsync(); + .ToList(); + return Task.FromResult(list); + } + + public Task GetProjectAsync(int projectId) + { + var col = _db.GetCollection("projects"); + var entity = col.FindById(projectId); + if (entity == null) return Task.FromResult(null); + return Task.FromResult(new Project + { + Id = entity.Id, + Name = entity.Name, + RootPath = entity.RootPath + }); + } + + public Task> GetProjectFileTreeAsync(int projectId) + { + var col = _db.GetCollection("projects"); + var entity = col.FindById(projectId); + if (entity == null || !Directory.Exists(entity.RootPath)) + return Task.FromResult(new List()); + + var result = new List(); + BuildTree(entity.RootPath, result, 0); + return Task.FromResult(result); + } + + private static void BuildTree(string directory, List items, int depth) + { + if (depth > 4) return; + + var dirInfo = new DirectoryInfo(directory); + if (!dirInfo.Exists) return; + + foreach (var dir in dirInfo.GetDirectories().OrderBy(d => d.Name)) + { + var node = new FileTreeItem + { + Name = dir.Name, + FullPath = dir.FullName, + IsDirectory = true + }; + BuildTree(dir.FullName, node.Children, depth + 1); + if (node.Children.Count > 0 || dir.Name.StartsWith("00_") || dir.Name.StartsWith("01_") || dir.Name.StartsWith("02_")) + items.Add(node); + } + + foreach (var file in dirInfo.GetFiles("*.md").OrderBy(f => f.Name)) + { + items.Add(new FileTreeItem + { + Name = file.Name, + FullPath = file.FullName, + IsDirectory = false + }); + } } public async Task SyncProjectFilesAsync(int projectId, string directoryPath, IProgress progress) diff --git a/Services/RagService.cs b/Services/RagService.cs new file mode 100644 index 0000000..e20faa7 --- /dev/null +++ b/Services/RagService.cs @@ -0,0 +1,145 @@ +using AuthorBuddy.Web.Data; +using LiteDB; +using System.Security.Cryptography; +using System.Text; + +namespace AuthorBuddy.Web.Services; + +public class RagService : IRagService +{ + private readonly ILiteDatabase _db; + private readonly IOllamaService _ollama; + + public RagService(ILiteDatabase db, IOllamaService ollama) + { + _db = db; + _ollama = ollama; + } + + public async Task IndexProjectAsync(int projectId, string rootPath, IProgress progress) + { + var files = Directory.GetFiles(rootPath, "*.md", SearchOption.AllDirectories) + .Where(f => f.Contains("00_Drafts") || f.Contains("01_Fakten")); + + var metaCol = _db.GetCollection("documentMeta"); + var vecCol = _db.GetCollection("vectors"); + + foreach (var file in files) + { + progress?.Report($"Indexiere: {Path.GetFileName(file)}..."); + + string content = await File.ReadAllTextAsync(file); + string hash = ComputeHash(content); + + var meta = metaCol.FindOne(m => m.ProjectId == projectId && m.FilePath == file); + + if (meta != null && meta.Hash == hash) + { + var existingVec = vecCol.FindOne(v => v.DocumentMetaId == meta.Id); + if (existingVec != null) + { + progress?.Report($" ↳ unverändert, übersprungen"); + continue; + } + } + + var embedding = await _ollama.GetEmbeddingAsync(content); + if (embedding.Length == 0) + { + progress?.Report($" ↳ Fehler beim Embedding, übersprungen"); + continue; + } + + if (meta == null) + { + meta = new DocumentMetaEntity + { + ProjectId = projectId, + FilePath = file, + LastModified = File.GetLastWriteTimeUtc(file), + Hash = hash, + FileType = ".md" + }; + metaCol.Insert(meta); + } + else + { + meta.LastModified = File.GetLastWriteTimeUtc(file); + meta.Hash = hash; + metaCol.Update(meta); + } + + var vec = vecCol.FindOne(v => v.DocumentMetaId == meta.Id); + if (vec != null) + { + vec.Text = content; + vec.Hash = hash; + vec.Embedding = embedding; + vecCol.Update(vec); + } + else + { + vecCol.Insert(new DocumentVector + { + DocumentMetaId = meta.Id, + ProjectId = projectId, + Text = content, + Hash = hash, + Embedding = embedding + }); + } + + progress?.Report($" ↳ ✓ {embedding.Length} Dimensionen"); + } + + progress?.Report("Indexierung abgeschlossen."); + } + + public async Task> SearchAsync(int projectId, string query, int topN = 5) + { + var queryVec = await _ollama.GetEmbeddingAsync(query); + if (queryVec.Length == 0) + return new List(); + + var vecCol = _db.GetCollection("vectors"); + var metaCol = _db.GetCollection("documentMeta"); + + var vectors = vecCol.Find(v => v.ProjectId == projectId).ToList(); + var results = new List(); + + foreach (var vec in vectors) + { + var sim = CosineSimilarity(queryVec, vec.Embedding); + if (sim > 0) + { + var meta = metaCol.FindById(vec.DocumentMetaId); + results.Add(new RagSearchResult + { + Text = vec.Text.Length > 500 ? vec.Text[..500] + "..." : vec.Text, + FilePath = meta?.FilePath ?? "", + Similarity = sim + }); + } + } + + return results.OrderByDescending(r => r.Similarity).Take(topN).ToList(); + } + + private static string ComputeHash(string content) + { + var bytes = SHA256.HashData(Encoding.UTF8.GetBytes(content)); + return Convert.ToHexStringLower(bytes); + } + + private static double CosineSimilarity(float[] a, float[] b) + { + double dotProduct = 0.0, l2A = 0.0, l2B = 0.0; + for (int i = 0; i < a.Length; i++) + { + dotProduct += a[i] * b[i]; + l2A += Math.Pow(a[i], 2); + l2B += Math.Pow(b[i], 2); + } + return dotProduct / (Math.Sqrt(l2A) * Math.Sqrt(l2B)); + } +} diff --git a/Services/SettingsService.cs b/Services/SettingsService.cs index 8d37810..7df40ec 100644 --- a/Services/SettingsService.cs +++ b/Services/SettingsService.cs @@ -1,6 +1,6 @@ using AuthorBuddy.Web.Data; using AuthorBuddy.Web.Models; -using Microsoft.EntityFrameworkCore; +using LiteDB; namespace AuthorBuddy.Web.Services; @@ -14,15 +14,18 @@ public class SettingsService : ISettingsService private const string KeyLektorPrompt = "ollama_lektor_prompt"; private const string KeyFactCheckTemplate = "ollama_fact_check"; private const string KeySpellingPrompt = "ollama_spelling_prompt"; + private const string KeyAdvocatusDiaboli = "ollama_advocatus_diaboli"; + private const string KeyHookFinder = "ollama_hook_finder"; + private const string KeyEli5 = "ollama_eli5"; - private readonly IDbContextFactory _contextFactory; + private readonly ILiteDatabase _db; private readonly OllamaSettings _ollamaConfig; private int? _currentUserId; private int? _currentProjectId; - public SettingsService(IDbContextFactory contextFactory, OllamaSettings ollamaConfig) + public SettingsService(ILiteDatabase db, OllamaSettings ollamaConfig) { - _contextFactory = contextFactory; + _db = db; _ollamaConfig = ollamaConfig; } @@ -32,156 +35,250 @@ public class SettingsService : ISettingsService set => _currentProjectId = value; } - private async Task GetSettingAsync(string key, int? userId, int? projectId) - { - await using var context = await _contextFactory.CreateDbContextAsync(); + public string? CurrentFilePath { get; set; } - var matches = context.Settings.Where(s => + private const string KeyLastProjectId = "last_project_id"; + + public Task GetLastProjectIdAsync() + { + var col = _db.GetCollection("settings"); + var setting = col.FindOne(s => s.Key == KeyLastProjectId && s.UserId == _currentUserId && s.ProjectId == null); + if (setting != null && int.TryParse(setting.Value, out var id)) + return Task.FromResult(id); + return Task.FromResult(null); + } + + public Task SaveLastProjectIdAsync(int projectId) + { + var col = _db.GetCollection("settings"); + var existing = col.FindOne(s => s.Key == KeyLastProjectId && s.UserId == _currentUserId && s.ProjectId == null); + if (existing != null) + { + existing.Value = projectId.ToString(); + col.Update(existing); + } + else + { + col.Insert(new SettingEntity + { + Key = KeyLastProjectId, + Value = projectId.ToString(), + UserId = _currentUserId, + ProjectId = null + }); + } + return Task.CompletedTask; + } + + private string? GetSetting(string key, int? userId, int? projectId) + { + var col = _db.GetCollection("settings"); + + var matches = col.Find(s => s.Key == key && (s.UserId == userId || s.UserId == null) && (s.ProjectId == projectId || s.ProjectId == null)); - var setting = await matches + return matches .OrderByDescending(s => s.UserId != null) .ThenByDescending(s => s.ProjectId != null) - .FirstOrDefaultAsync(); - - return setting?.Value; + .FirstOrDefault()?.Value; } - private async Task GetAsync(string key) + private string? Get(string key) { - return await GetSettingAsync(key, _currentUserId, _currentProjectId); + return GetSetting(key, _currentUserId, null); } - private async Task SetAsync(string key, string value) + private void Set(string key, string value) { - await using var context = await _contextFactory.CreateDbContextAsync(); + var col = _db.GetCollection("settings"); - var existing = await context.Settings - .Where(s => s.Key == key && s.UserId == _currentUserId && s.ProjectId == _currentProjectId) - .FirstOrDefaultAsync(); + var existing = col.FindOne(s => + s.Key == key && s.UserId == _currentUserId && s.ProjectId == null); if (existing != null) { existing.Value = value; + col.Update(existing); } else { - context.Settings.Add(new SettingEntity + col.Insert(new SettingEntity { Key = key, Value = value, UserId = _currentUserId, - ProjectId = _currentProjectId + ProjectId = null }); } - - await context.SaveChangesAsync(); } - public Task GetOllamaUrlAsync() => GetAsync(KeyOllamaUrl); - public Task GetRagModelAsync() => GetAsync(KeyRagModel); - public Task GetStyleModelAsync() => GetAsync(KeyStyleModel); - public Task GetLektorSystemPromptAsync() => GetAsync(KeyLektorPrompt); - public Task GetFactCheckTemplateAsync() => GetAsync(KeyFactCheckTemplate); - public Task GetSpellingPromptAsync() => GetAsync(KeySpellingPrompt); - public Task GetSpellingModelAsync() => GetAsync(KeySpellingModel); - public Task GetThemeAsync() => GetAsync(KeyTheme); + public Task GetOllamaUrlAsync() => Task.FromResult(Get(KeyOllamaUrl)); + public Task GetRagModelAsync() => Task.FromResult(Get(KeyRagModel)); + public Task GetStyleModelAsync() => Task.FromResult(Get(KeyStyleModel)); + public Task GetLektorSystemPromptAsync() => Task.FromResult(Get(KeyLektorPrompt)); + public Task GetFactCheckTemplateAsync() => Task.FromResult(Get(KeyFactCheckTemplate)); + public Task GetSpellingPromptAsync() => Task.FromResult(Get(KeySpellingPrompt)); + public Task GetSpellingModelAsync() => Task.FromResult(Get(KeySpellingModel)); + public Task GetAdvocatusDiaboliPromptAsync() => Task.FromResult(Get(KeyAdvocatusDiaboli)); + public Task GetHookFinderPromptAsync() => Task.FromResult(Get(KeyHookFinder)); + public Task GetEli5PromptAsync() => Task.FromResult(Get(KeyEli5)); + public Task GetThemeAsync() => Task.FromResult(Get(KeyTheme)); - public async Task SaveOllamaUrlAsync(string url) + public Task SaveOllamaUrlAsync(string url) { - await SetAsync(KeyOllamaUrl, url); + Set(KeyOllamaUrl, url); _ollamaConfig.OllamaUrl = url; + return Task.CompletedTask; } - public async Task SaveRagModelAsync(string model) + public Task SaveRagModelAsync(string model) { - await SetAsync(KeyRagModel, model); + Set(KeyRagModel, model); + _ollamaConfig.EmbedModel = model; + return Task.CompletedTask; + } + + public Task SaveStyleModelAsync(string model) + { + Set(KeyStyleModel, model); _ollamaConfig.StyleModel = model; + return Task.CompletedTask; } - public async Task SaveStyleModelAsync(string model) + public Task SaveLektorSystemPromptAsync(string prompt) { - await SetAsync(KeyStyleModel, model); - _ollamaConfig.StyleModel = model; - } - - public async Task SaveLektorSystemPromptAsync(string prompt) - { - await SetAsync(KeyLektorPrompt, prompt); + Set(KeyLektorPrompt, prompt); _ollamaConfig.LektorSystemPrompt = prompt; + return Task.CompletedTask; } - public async Task SaveFactCheckTemplateAsync(string prompt) + public Task SaveFactCheckTemplateAsync(string prompt) { - await SetAsync(KeyFactCheckTemplate, prompt); + Set(KeyFactCheckTemplate, prompt); _ollamaConfig.FactCheckTemplate = prompt; + return Task.CompletedTask; } - public async Task SaveSpellingPromptAsync(string prompt) + public Task SaveSpellingPromptAsync(string prompt) { - await SetAsync(KeySpellingPrompt, prompt); + Set(KeySpellingPrompt, prompt); _ollamaConfig.SpellingPrompt = prompt; + return Task.CompletedTask; } - public async Task SaveSpellingModelAsync(string model) + public Task SaveSpellingModelAsync(string model) { - await SetAsync(KeySpellingModel, model); + Set(KeySpellingModel, model); _ollamaConfig.SpellingModel = model; + return Task.CompletedTask; + } + + public Task SaveAdvocatusDiaboliPromptAsync(string prompt) + { + Set(KeyAdvocatusDiaboli, prompt); + _ollamaConfig.AdvocatusDiaboliPrompt = prompt; + return Task.CompletedTask; + } + + public Task SaveHookFinderPromptAsync(string prompt) + { + Set(KeyHookFinder, prompt); + _ollamaConfig.HookFinderPrompt = prompt; + return Task.CompletedTask; + } + + public Task SaveEli5PromptAsync(string prompt) + { + Set(KeyEli5, prompt); + _ollamaConfig.Eli5Prompt = prompt; + return Task.CompletedTask; } public Task SaveThemeAsync(string theme) - => SetAsync(KeyTheme, theme); - - public async Task> GetUsersAsync() { - await using var context = await _contextFactory.CreateDbContextAsync(); - return await context.Users.ToListAsync(); + Set(KeyTheme, theme); + return Task.CompletedTask; } - public async Task GetCurrentUserAsync() + public Task> GetUsersAsync() { - await using var context = await _contextFactory.CreateDbContextAsync(); - var user = await context.Users.FirstOrDefaultAsync(u => u.IsCurrent); - return user ?? await context.Users.FirstAsync(); + var col = _db.GetCollection("users"); + return Task.FromResult(col.FindAll().ToList()); } - public async Task SetCurrentUserAsync(int userId) + public Task GetCurrentUserAsync() { - await using var context = await _contextFactory.CreateDbContextAsync(); - var users = await context.Users.ToListAsync(); + var col = _db.GetCollection("users"); + var user = col.FindOne(u => u.IsCurrent); + return Task.FromResult(user ?? col.FindAll().First()); + } + + public Task SetCurrentUserAsync(int userId) + { + var col = _db.GetCollection("users"); + var users = col.FindAll().ToList(); foreach (var u in users) + { u.IsCurrent = u.Id == userId; - await context.SaveChangesAsync(); + col.Update(u); + } _currentUserId = userId; + return Task.CompletedTask; } - public async Task CreateUserAsync(string name) + public Task CreateUserAsync(string name) { - await using var context = await _contextFactory.CreateDbContextAsync(); + var col = _db.GetCollection("users"); var user = new UserEntity { Name = name }; - context.Users.Add(user); - await context.SaveChangesAsync(); - return user; + col.Insert(user); + return Task.FromResult(user); } - public async Task InitializeAsync() + public Task LoadProjectSettingsAsync() { - await using var context = await _contextFactory.CreateDbContextAsync(); + var settings = _db.GetCollection("settings"); + var entries = new (string Key, Action Setter)[] + { + (KeyOllamaUrl, val => _ollamaConfig.OllamaUrl = val), + (KeyRagModel, val => _ollamaConfig.EmbedModel = val), + (KeyStyleModel, val => _ollamaConfig.StyleModel = val), + (KeySpellingModel, val => _ollamaConfig.SpellingModel = val), + (KeyTheme, val => _ollamaConfig.Theme = val), + (KeyLektorPrompt, val => _ollamaConfig.LektorSystemPrompt = val), + (KeyFactCheckTemplate, val => _ollamaConfig.FactCheckTemplate = val), + (KeySpellingPrompt, val => _ollamaConfig.SpellingPrompt = val), + (KeyAdvocatusDiaboli, val => _ollamaConfig.AdvocatusDiaboliPrompt = val), + (KeyHookFinder, val => _ollamaConfig.HookFinderPrompt = val), + (KeyEli5, val => _ollamaConfig.Eli5Prompt = val), + }; - var userExists = await context.Users.AnyAsync(); + foreach (var entry in entries) + { + var val = Get(entry.Key); + if (val != null) entry.Setter(val); + } + + return Task.CompletedTask; + } + + public Task InitializeAsync(int projectId = -1) + { + var users = _db.GetCollection("users"); + var settings = _db.GetCollection("settings"); + + var userExists = users.FindAll().Any(); if (!userExists) { var defaultUser = new UserEntity { Name = "Standardbenutzer", IsCurrent = true }; - context.Users.Add(defaultUser); - await context.SaveChangesAsync(); + users.Insert(defaultUser); _currentUserId = defaultUser.Id; } else { - var current = await context.Users.FirstOrDefaultAsync(u => u.IsCurrent); - _currentUserId = current?.Id ?? (await context.Users.FirstAsync()).Id; + var current = users.FindOne(u => u.IsCurrent); + _currentUserId = current?.Id ?? users.FindAll().First().Id; } var entries = new (string Key, string Default)[] @@ -194,29 +291,34 @@ public class SettingsService : ISettingsService (KeyLektorPrompt, _ollamaConfig.LektorSystemPrompt), (KeyFactCheckTemplate, _ollamaConfig.FactCheckTemplate), (KeySpellingPrompt, _ollamaConfig.SpellingPrompt), + (KeyAdvocatusDiaboli, _ollamaConfig.AdvocatusDiaboliPrompt), + (KeyHookFinder, _ollamaConfig.HookFinderPrompt), + (KeyEli5, _ollamaConfig.Eli5Prompt), }; foreach (var entry in entries) { - var existing = await context.Settings - .Where(s => s.Key == entry.Key && s.UserId == _currentUserId && s.ProjectId == null) - .FirstOrDefaultAsync(); + var existing = settings.FindOne(s => + s.Key == entry.Key && s.UserId == _currentUserId && s.ProjectId == projectId); if (existing != null) { var val = existing.Value; if (entry.Key == KeyOllamaUrl) _ollamaConfig.OllamaUrl = val; - else if (entry.Key == KeyRagModel) _ollamaConfig.StyleModel = val; + else if (entry.Key == KeyRagModel) _ollamaConfig.EmbedModel = val; else if (entry.Key == KeyStyleModel) _ollamaConfig.StyleModel = val; else if (entry.Key == KeySpellingModel) _ollamaConfig.SpellingModel = val; else if (entry.Key == KeyTheme) _ollamaConfig.Theme = val; else if (entry.Key == KeyLektorPrompt) _ollamaConfig.LektorSystemPrompt = val; else if (entry.Key == KeyFactCheckTemplate) _ollamaConfig.FactCheckTemplate = val; else if (entry.Key == KeySpellingPrompt) _ollamaConfig.SpellingPrompt = val; + else if (entry.Key == KeyAdvocatusDiaboli) _ollamaConfig.AdvocatusDiaboliPrompt = val; + else if (entry.Key == KeyHookFinder) _ollamaConfig.HookFinderPrompt = val; + else if (entry.Key == KeyEli5) _ollamaConfig.Eli5Prompt = val; } else { - context.Settings.Add(new SettingEntity + settings.Insert(new SettingEntity { Key = entry.Key, Value = entry.Default, @@ -226,6 +328,6 @@ public class SettingsService : ISettingsService } } - await context.SaveChangesAsync(); + return Task.CompletedTask; } } diff --git a/Services/WikipediaService.cs b/Services/WikipediaService.cs new file mode 100644 index 0000000..8f0d76c --- /dev/null +++ b/Services/WikipediaService.cs @@ -0,0 +1,136 @@ +using AuthorBuddy.Web.Models; +using System.Text.Json; +using System.Web; + +namespace AuthorBuddy.Web.Services; + +public class WikipediaService : IWikipediaService +{ + private readonly HttpClient _http; + private readonly string[] _languages = { "de", "en" }; + + public WikipediaService() + { + _http = new HttpClient { Timeout = TimeSpan.FromSeconds(15) }; + _http.DefaultRequestHeaders.UserAgent.ParseAdd("AuthorBuddy/1.0 (writing-assistant)"); + } + + public async Task> SearchAsync(string query, int maxResults = 3) + { + return await SearchAsync(new WikipediaSearchRequest + { + Query = query, + MaxResults = maxResults + }); + } + + public async Task> SearchAsync(WikipediaSearchRequest request) + { + var results = new List(); + + foreach (var lang in _languages) + { + if (lang == "de" && !request.SearchGerman) continue; + if (lang == "en" && !request.SearchEnglish) continue; + + try + { + var langResults = await SearchLanguageAsync(lang, request.Query, request.MaxResults); + results.AddRange(langResults); + } + catch + { + // Sprache überspringen bei Fehler + } + } + + return results; + } + + public async Task GetExtractAsync(string title, string language = "de") + { + try + { + var url = $"https://{language}.wikipedia.org/w/api.php" + + "?action=query" + + "&prop=extracts" + + "&exintro&explaintext" + + $"&titles={HttpUtility.UrlEncode(title)}" + + "&format=json&utf8=1"; + + var response = await _http.GetStringAsync(url); + using var doc = JsonDocument.Parse(response); + var pages = doc.RootElement.GetProperty("query").GetProperty("pages"); + + foreach (var page in pages.EnumerateObject()) + { + if (page.Value.TryGetProperty("extract", out var extract)) + return extract.GetString(); + } + } + catch { } + + return null; + } + + public async Task GetFullArticleAsync(string title, string language = "de") + { + try + { + var url = $"https://{language}.wikipedia.org/w/api.php" + + "?action=query" + + "&prop=extracts" + + "&explaintext" + + $"&titles={HttpUtility.UrlEncode(title)}" + + "&format=json&utf8=1"; + + var response = await _http.GetStringAsync(url); + using var doc = JsonDocument.Parse(response); + var pages = doc.RootElement.GetProperty("query").GetProperty("pages"); + + foreach (var page in pages.EnumerateObject()) + { + if (page.Value.TryGetProperty("extract", out var extract)) + return extract.GetString(); + } + } + catch { } + + return null; + } + + private async Task> SearchLanguageAsync(string lang, string query, int maxResults) + { + var results = new List(); + + var url = $"https://{lang}.wikipedia.org/w/api.php" + + "?action=query" + + "&list=search" + + $"&srsearch={HttpUtility.UrlEncode(query)}" + + $"&srlimit={maxResults}" + + "&format=json&utf8=1"; + + var response = await _http.GetStringAsync(url); + using var doc = JsonDocument.Parse(response); + var search = doc.RootElement.GetProperty("query").GetProperty("search"); + + foreach (var item in search.EnumerateArray()) + { + var title = item.GetProperty("title").GetString() ?? ""; + results.Add(new WikipediaResult + { + Title = title, + Snippet = StripHtml(item.GetProperty("snippet").GetString() ?? ""), + Url = $"https://{lang}.wikipedia.org/wiki/{HttpUtility.UrlEncode(title)}", + Language = lang + }); + } + + return results; + } + + private static string StripHtml(string html) + { + return System.Text.RegularExpressions.Regex.Replace(html, "<[^>]*>", ""); + } +} diff --git a/author_brain.db b/author_brain.db deleted file mode 100644 index 9345e48..0000000 Binary files a/author_brain.db and /dev/null differ diff --git a/vec0.dll b/vec0.dll deleted file mode 100644 index e897f7f..0000000 Binary files a/vec0.dll and /dev/null differ diff --git a/wwwroot/app.css b/wwwroot/app.css index e2ce31b..036b90d 100644 --- a/wwwroot/app.css +++ b/wwwroot/app.css @@ -193,15 +193,68 @@ body { line-height: 1.5; } -/* Editor */ -.editor-container { +.fact-similarity { + font-size: 11px; + color: var(--accent-color); + font-weight: bold; + margin-bottom: 4px; +} + +.fact-path { + font-size: 11px; + color: var(--border-color); + margin-bottom: 6px; + font-family: monospace; +} + +.fact-text { + font-size: 13px; + white-space: pre-wrap; + word-break: break-word; +} + +.fact-extract { + font-size: 12px; + margin-top: 6px; + padding-top: 6px; + border-top: 1px solid var(--border-color); + white-space: pre-wrap; + word-break: break-word; +} + +.fact-card a { + color: var(--accent-color); + text-decoration: none; +} + +.fact-card a:hover { + text-decoration: underline; +} + +.fact-actions { + display: flex; + gap: 6px; + margin-top: 8px; + padding-top: 8px; + border-top: 1px solid var(--border-color); +} + +.fact-actions button { + font-size: 12px; + padding: 4px 10px; +} + +/* Editor page - no scroll */ +.editor-page { display: flex; flex-direction: column; height: 100%; + overflow: hidden; } -.editor-container textarea { +.editor-area { flex: 1; + min-height: 0; font-family: 'Consolas', 'Courier New', monospace; font-size: 14pt; padding: 20px; @@ -213,13 +266,55 @@ body { line-height: 1.6; } -.editor-container .editor-toolbar { +.editor-toolbar { display: flex; justify-content: flex-end; padding: 8px 16px; gap: 8px; border-top: 1px solid var(--border-color); + border-bottom: 1px solid var(--border-color); background: var(--menu-bg); + flex-shrink: 0; +} + +/* Results area - scrollable box */ +.results-area { + max-height: 140px; + overflow-y: auto; + border-top: 1px solid var(--border-color); + background: var(--window-bg); + flex-shrink: 0; +} + +/* Version list */ +.version-item { + display: flex; + align-items: center; + justify-content: space-between; + padding: 6px 8px; + border-bottom: 1px solid var(--border-color); + font-size: 13px; +} + +.version-item:last-child { + border-bottom: none; +} + +.version-meta { + display: flex; + gap: 8px; + align-items: center; +} + +.version-date { + font-weight: bold; + color: var(--accent-color); + font-family: monospace; +} + +.version-comment { + color: var(--border-color); + font-style: italic; } /* Buttons */ @@ -237,6 +332,18 @@ button:hover { color: white; } +button:disabled { + background: var(--disabled-bg); + color: var(--disabled-text); + cursor: default; + border-color: var(--border-color); +} + +button:disabled:hover { + background: var(--disabled-bg); + color: var(--disabled-text); +} + button.primary { background: var(--accent-color); color: white; @@ -295,6 +402,176 @@ button.primary { background: transparent; } +/* Search section below editor */ +.search-section { + display: flex; + gap: 8px; + align-items: center; + padding: 6px 12px; + background: var(--menu-bg); + flex-shrink: 0; +} + +.search-section input { + flex: 1; + padding: 6px 10px; + border: 1px solid var(--border-color); + background: var(--editor-bg); + color: var(--text-color); + font-size: 13px; +} + +.search-section input::placeholder { + color: var(--border-color); +} + + + +/* Modal overlay */ +.modal-overlay { + position: fixed; + top: 0; left: 0; right: 0; bottom: 0; + background: rgba(0,0,0,0.4); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; +} + +.modal-dialog { + background: var(--window-bg); + border: 2px solid var(--border-color); + min-width: 400px; + box-shadow: 4px 4px 8px rgba(0,0,0,0.3); +} + +.modal-title { + background: var(--accent-color); + color: white; + padding: 6px 12px; + font-weight: bold; + font-size: 14px; +} + +.modal-body { + padding: 16px; +} + +.modal-actions { + display: flex; + gap: 8px; + justify-content: flex-end; + padding: 12px 16px; + border-top: 1px solid var(--border-color); +} + +.modal-dialog .form-group { + margin-bottom: 12px; +} + +.modal-dialog .form-group:last-child { + margin-bottom: 0; +} + +.modal-dialog .form-group label { + display: block; + font-weight: bold; + margin-bottom: 4px; + font-size: 13px; +} + +.modal-dialog .form-group input { + width: 100%; + padding: 6px 8px; + border: 1px solid var(--border-color); + background: var(--editor-bg); + color: var(--text-color); + font-size: 13px; +} + +/* File tree in sidebar */ +.tree-item { + display: flex; + align-items: center; + gap: 4px; + padding: 3px 12px; + cursor: pointer; + font-size: 12px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.tree-item:hover { + background: var(--accent-color); + color: white; +} + +.tree-item.active { + background: var(--accent-color); + color: white; + font-weight: bold; +} + +.tree-item.file { + padding-left: 28px; +} + +.tree-item.tree-child { + padding-left: 44px; +} + +.tree-icon { + flex-shrink: 0; + font-size: 11px; +} + +.tree-name { + overflow: hidden; + text-overflow: ellipsis; + flex: 1; +} + +.tree-rename { + background: none; + border: none; + cursor: pointer; + font-size: 12px; + padding: 0 4px; + opacity: 0.4; + color: inherit; + flex-shrink: 0; +} + +.tree-rename:hover { + opacity: 1; +} + +.nav-header-actions { + margin-left: auto; + display: flex; + gap: 2px; +} + +.nav-item-icon { + background: none; + border: none; + cursor: pointer; + font-size: 14px; + padding: 0 4px; + opacity: 0.5; + color: inherit; +} + +.nav-item-icon:hover { + opacity: 1; +} + +.nav-item-sub.active { + background: var(--accent-color); + color: white; +} + /* Progress */ .progress-bar { width: 100%; @@ -340,21 +617,16 @@ button.primary { } .settings-layout { - display: flex; + display: grid; + grid-template-columns: 1fr 1fr 1fr; gap: 16px; } -.settings-layout-left { - flex: 1; +.settings-column { min-width: 0; } -.settings-layout-right { - flex: 1; - min-width: 0; -} - -.settings-layout-right .form-group textarea { +.settings-column .form-group textarea { min-height: 120px; font-family: monospace; font-size: 12px; @@ -450,7 +722,7 @@ button.primary { .result-panel { border-top: 2px solid var(--border-color); padding: 12px; - max-height: 300px; + max-height: 400px; overflow-y: auto; } @@ -465,6 +737,92 @@ button.primary { width: 8px; } +/* Assistant page */ +.assistant-page { + display: flex; + flex-direction: column; + height: 100%; +} + +.assistant-toolbar { + display: flex; + gap: 8px; + align-items: center; + padding: 8px 0; + border-bottom: 1px solid var(--border-color); + margin-bottom: 8px; +} + +.assistant-toolbar select { + padding: 4px 8px; + border: 1px solid var(--border-color); + background: var(--input-bg); + color: var(--text-color); + font-size: 13px; +} + +.assistant-panels { + display: flex; + gap: 12px; + flex: 1; + min-height: 0; +} + +.assistant-input-panel, +.assistant-output-panel { + flex: 1; + display: flex; + flex-direction: column; +} + +.assistant-input-panel h4, +.assistant-output-panel h4 { + margin: 0 0 6px 0; + font-size: 13px; + font-weight: bold; +} + +.assistant-textarea { + flex: 1; + resize: none; + padding: 8px; + border: 1px solid var(--border-color); + background: var(--input-bg); + color: var(--text-color); + font-family: monospace; + font-size: 13px; + min-height: 200px; +} + +.assistant-output { + flex: 1; + padding: 8px; + border: 1px solid var(--border-color); + background: var(--card-bg); + color: var(--text-color); + font-family: monospace; + font-size: 13px; + overflow-y: auto; + white-space: pre-wrap; + min-height: 200px; +} + +.assistant-output pre { + margin: 0; + white-space: pre-wrap; + font-family: inherit; + font-size: inherit; +} + +.assistant-error { + color: red; +} + +.assistant-hint { + color: var(--border-color); + font-style: italic; +} + ::-webkit-scrollbar-track { background: var(--window-bg); } diff --git a/wwwroot/css/themes/dark.css b/wwwroot/css/themes/dark.css index 32c5ab6..5e075e0 100644 --- a/wwwroot/css/themes/dark.css +++ b/wwwroot/css/themes/dark.css @@ -7,4 +7,6 @@ --menu-bg: #252526; --card-bg: #2d2d2d; --status-bar-bg: #007acc; + --disabled-bg: #2a2a2a; + --disabled-text: #606060; } diff --git a/wwwroot/css/themes/light.css b/wwwroot/css/themes/light.css index 6433b50..56eca8a 100644 --- a/wwwroot/css/themes/light.css +++ b/wwwroot/css/themes/light.css @@ -7,4 +7,6 @@ --menu-bg: #e0e0e0; --card-bg: #ffffff; --status-bar-bg: #dcdcdc; + --disabled-bg: #e8e8e8; + --disabled-text: #a0a0a0; } diff --git a/wwwroot/css/themes/w95.css b/wwwroot/css/themes/w95.css index 00cfb62..3ade6a9 100644 --- a/wwwroot/css/themes/w95.css +++ b/wwwroot/css/themes/w95.css @@ -7,4 +7,6 @@ --menu-bg: #c0c0c0; --card-bg: #ffffff; --status-bar-bg: #c0c0c0; + --disabled-bg: #c0c0c0; + --disabled-text: #808080; }