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 @@
{
-
+
}
-
-
- @if (activeSection == "view")
- {
-
-
-
- }
-
@@ -74,17 +64,92 @@
{
@foreach (var p in projects)
{
-
+
}
}
}
-
}
+ @if (currentProjectId > 0 && fileTree != null)
+ {
+
+
+
+ @foreach (var item in fileTree)
+ {
+
OpenFile(item)">
+ @(item.IsDirectory ? "📁" : "📄")
+ @item.Name
+ @if (item.IsDirectory)
+ {
+
+ }
+ else
+ {
+
+ }
+
+ @foreach (var child in item.Children)
+ {
+
OpenFile(child)">
+ 📄
+ @child.Name
+
+
+ }
+ }
+
+
+ }
+
+@if (showRenameDialog)
+{
+
+
+
Datei umbenennen
+
+
+
+
+
+
+
+}
+
+@if (showNewFileDialog)
+{
+
+
+
Neue Datei
+
+
+
+
+
+
+
+}
@@ -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
-
-
+
+
+
+ @if (showVersionDialog)
+ {
+
+
+
Version speichern
+
+
+
+
+
+
+
+
+
+
+
+
+ }
+
+
+
+
+
+
+
+
@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.Snippet
+ @if (!string.IsNullOrEmpty(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
+
+
+
Neues Projekt erstellen
+
+
+
+
+
+
}
@@ -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
+
+
+
+
+
+
+
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
@@ -98,7 +98,7 @@
-
+
Lektor-System-Prompt
@@ -121,6 +121,29 @@
+
+
+
Advocatus Diaboli
+
+
+
+
+
+
+
+
+
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;
}