This commit is contained in:
Roland 2026-06-21 07:52:03 +02:00
parent 0735f81235
commit a5d2c80c70
32 changed files with 2865 additions and 982 deletions

View file

@ -3,13 +3,77 @@
"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\\components\\pages\\login.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\login.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\\resources\\en.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:resources\\en.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
},
{
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\resources\\de.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:resources\\de.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
},
{
"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\\chat.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\chat.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\\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\\services\\ollamabackend.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ollamabackend.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\\illmbackend.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\illmbackend.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\\chatservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\chatservice.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\\data\\userentity.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:data\\userentity.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\\login.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\login.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\\pages\\firstrunsetup.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\firstrunsetup.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\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\ollamaassistant.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\\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\\pages\\dbadmin.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\dbadmin.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\\dbadminservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\dbadminservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\HP\\AppData\\Local\\SourceServer\\dddeb33c46e3c746b90c5f64463103c08ec03b9a85e92eab3b21c11218135d28\\src\\runtime\\src\\libraries\\System.Private.CoreLib\\src\\Microsoft\\Win32\\SafeHandles\\SafeFileHandle.Windows.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
@ -20,22 +84,10 @@
"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\\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\\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\\data\\userentity.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:data\\userentity.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}"
@ -52,14 +104,6 @@
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\install.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:install.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\ollamaassistant.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\\ithemeservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ithemeservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
@ -104,10 +148,6 @@
"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\\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\\app.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\app.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
@ -124,10 +164,6 @@
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\models\\modelstatus.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:models\\modelstatus.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.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\\ifilescannerservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ifilescannerservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
@ -156,7 +192,7 @@
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 8,
"SelectedChildIndex": 6,
"Children": [
{
"$type": "Bookmark",
@ -164,44 +200,140 @@
},
{
"$type": "Document",
"DocumentIndex": 5,
"DocumentIndex": 1,
"Title": "en.json",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Resources\\en.json",
"RelativeDocumentMoniker": "Resources\\en.json",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Resources\\en.json",
"RelativeToolTip": "Resources\\en.json",
"ViewState": "AgIAABQBAAAAAAAAAAAAACQBAAAfAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
"WhenOpened": "2026-06-14T06:03:36.064Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "de.json",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Resources\\de.json",
"RelativeDocumentMoniker": "Resources\\de.json",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Resources\\de.json",
"RelativeToolTip": "Resources\\de.json",
"ViewState": "AgIAABoBAAAAAAAAAAAAACQBAAACAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
"WhenOpened": "2026-06-14T06:02:40.854Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "Chat.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Chat.razor",
"RelativeDocumentMoniker": "Components\\Pages\\Chat.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Chat.razor",
"RelativeToolTip": "Components\\Pages\\Chat.razor",
"ViewState": "AgIAAFgAAAAAAAAAAAApwG0AAAANAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-06-14T05:59:50.644Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "OllamaBackend.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaBackend.cs",
"RelativeDocumentMoniker": "Services\\OllamaBackend.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaBackend.cs",
"RelativeToolTip": "Services\\OllamaBackend.cs",
"ViewState": "AgIAACUAAAAAAAAAAAASwDUAAAAeAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-14T05:03:24.305Z"
},
{
"$type": "Document",
"DocumentIndex": 17,
"Title": "DbAdminService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\DbAdminService.cs",
"RelativeDocumentMoniker": "Services\\DbAdminService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\DbAdminService.cs",
"RelativeToolTip": "Services\\DbAdminService.cs",
"ViewState": "AgIAABUAAAAAAAAAAAAowAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-10T17:34:10.361Z"
},
{
"$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": "AgIAAFAAAAAAAAAAAAAQwGEAAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-15T16:04:36.444Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 12,
"Title": "Login.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Login.razor",
"RelativeDocumentMoniker": "Components\\Pages\\Login.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Login.razor",
"RelativeToolTip": "Components\\Pages\\Login.razor",
"ViewState": "AgIAADAAAAAAAAAAAADwv0QAAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-21T08:47:41.996Z"
},
{
"$type": "Document",
"DocumentIndex": 13,
"Title": "FirstRunSetup.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\FirstRunSetup.razor",
"RelativeDocumentMoniker": "Components\\Pages\\FirstRunSetup.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\FirstRunSetup.razor",
"RelativeToolTip": "Components\\Pages\\FirstRunSetup.razor",
"ViewState": "AgIAACEAAAAAAAAAAAAAACsAAABsAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-06-09T17:25:10.421Z"
},
{
"$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": "AgIAAE0BAAAAAAAAAAAAAC0BAAAMAAAAAAAAAA==",
"ViewState": "AgIAANwBAAAAAAAAAAAAAO4BAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T17:37:24.777Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"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": "AgIAAFAAAAAAAAAAAAAQwGMAAAAhAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-20T04:07:21.54Z"
},
{
"$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": "AgIAAJsAAAAAAAAAAAAAAKwAAAAWAAAAAAAAAA==",
"ViewState": "AgIAAJsAAAAAAAAAAAAAAKwAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-15T16:43:26.6Z"
},
{
"$type": "Document",
"DocumentIndex": 1,
"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": "AgIAADMAAAAAAAAAAAAAAFUAAAADAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-20T04:07:21.54Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 7,
"DocumentIndex": 21,
"Title": "Editor.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor",
"RelativeDocumentMoniker": "Components\\Pages\\Editor.razor",
@ -214,266 +346,7 @@
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "UserEntity.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\UserEntity.cs",
"RelativeDocumentMoniker": "Data\\UserEntity.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\UserEntity.cs",
"RelativeToolTip": "Data\\UserEntity.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAVAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-24T04:52:42.227Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"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": "AgIAAN0AAAAAAAAAAAApwO0AAAAMAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-15T15:56:55.229Z"
},
{
"$type": "Document",
"DocumentIndex": 10,
"Title": "IOllamaSetupService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaSetupService.cs",
"RelativeDocumentMoniker": "Services\\IOllamaSetupService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaSetupService.cs",
"RelativeToolTip": "Services\\IOllamaSetupService.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-30T08:35:51.087Z"
},
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "Login.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Login.razor",
"RelativeDocumentMoniker": "Components\\Pages\\Login.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Login.razor",
"RelativeToolTip": "Components\\Pages\\Login.razor",
"ViewState": "AgIAADYAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-21T08:47:41.996Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "SafeFileHandle.Windows.cs",
"DocumentMoniker": "C:\\Users\\HP\\AppData\\Local\\SourceServer\\dddeb33c46e3c746b90c5f64463103c08ec03b9a85e92eab3b21c11218135d28\\src\\runtime\\src\\libraries\\System.Private.CoreLib\\src\\Microsoft\\Win32\\SafeHandles\\SafeFileHandle.Windows.cs",
"ToolTip": "C:\\Users\\HP\\AppData\\Local\\SourceServer\\dddeb33c46e3c746b90c5f64463103c08ec03b9a85e92eab3b21c11218135d28\\src\\runtime\\src\\libraries\\System.Private.CoreLib\\src\\Microsoft\\Win32\\SafeHandles\\SafeFileHandle.Windows.cs [Schreibgesch\u00FCtzt]",
"ViewState": "AgIAAHwAAAAAAAAAAAAQwHEAAAAQAAAAAQAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-09T14:36:02.721Z",
"EditorCaption": " [Schreibgesch\u00FCtzt]"
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "A7405018.jpg",
"DocumentMoniker": "C:\\Users\\HP\\Desktop\\Website\\A7405018.jpg",
"ToolTip": "C:\\Users\\HP\\Desktop\\Website\\A7405018.jpg",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2026-05-23T05:52:56.265Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "AuthorBuddy.Web.csproj",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
"RelativeDocumentMoniker": "AuthorBuddy.Web.csproj",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
"RelativeToolTip": "AuthorBuddy.Web.csproj",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000758|",
"WhenOpened": "2026-05-14T10:14:30.128Z"
},
{
"$type": "Document",
"DocumentIndex": 12,
"Title": "INSTALL.md",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\INSTALL.md",
"RelativeDocumentMoniker": "INSTALL.md",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\INSTALL.md",
"RelativeToolTip": "INSTALL.md",
"ViewState": "AgIAAEUAAAAAAAAAAAAAAFIAAAAQAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2026-05-31T17:07:33.423Z"
},
{
"$type": "Document",
"DocumentIndex": 13,
"Title": "OllamaAssistant.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
"RelativeDocumentMoniker": "Components\\Pages\\OllamaAssistant.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
"RelativeToolTip": "Components\\Pages\\OllamaAssistant.razor",
"ViewState": "AgIAANoAAAAAAAAAAAAiwPIAAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-20T05:14:39.037Z"
},
{
"$type": "Document",
"DocumentIndex": 14,
"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": "AgIAACABAAAAAAAAAAApwDQBAAARAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-15T16:04:36.444Z"
},
{
"$type": "Document",
"DocumentIndex": 15,
"Title": "IThemeService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IThemeService.cs",
"RelativeDocumentMoniker": "Services\\IThemeService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IThemeService.cs",
"RelativeToolTip": "Services\\IThemeService.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAqwAgAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T17:32:01.784Z"
},
{
"$type": "Document",
"DocumentIndex": 16,
"Title": "RedirectToLogin.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\RedirectToLogin.razor",
"RelativeDocumentMoniker": "Components\\RedirectToLogin.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\RedirectToLogin.razor",
"RelativeToolTip": "Components\\RedirectToLogin.razor",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-21T16:31:16.612Z"
},
{
"$type": "Document",
"DocumentIndex": 17,
"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": "AgIAABwAAAAAAAAAAAA9wC4AAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T16:35:36.655Z"
},
{
"$type": "Document",
"DocumentIndex": 18,
"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": 19,
"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": "AgIAAAAAAAAAAAAAAAAAABEAAAAdAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T13:35:33.991Z"
},
{
"$type": "Document",
"DocumentIndex": 20,
"Title": "logo.jpg",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Assets\\logo.jpg",
"RelativeDocumentMoniker": "Assets\\logo.jpg",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Assets\\logo.jpg",
"RelativeToolTip": "Assets\\logo.jpg",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2026-05-21T17:15:10.852Z"
},
{
"$type": "Document",
"DocumentIndex": 21,
"Title": "OllamaSettings.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
"RelativeDocumentMoniker": "Models\\OllamaSettings.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
"RelativeToolTip": "Models\\OllamaSettings.cs",
"ViewState": "AgIAAAYAAAAAAAAAAAAkwBQAAAASAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T13:39:46.179Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"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": 22,
"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": "AgIAABIAAAAAAAAAAAAQwCUAAAAWAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T06:02:53.46Z"
},
{
"$type": "Document",
"DocumentIndex": 23,
"Title": "TotpHelper.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
"RelativeDocumentMoniker": "Services\\TotpHelper.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
"RelativeToolTip": "Services\\TotpHelper.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T08:20:49.461Z"
},
{
"$type": "Document",
"DocumentIndex": 24,
"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": 25,
"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": 26,
"DocumentIndex": 6,
"Title": "NavMenu.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\NavMenu.razor",
"RelativeDocumentMoniker": "Components\\Layout\\NavMenu.razor",
@ -485,55 +358,7 @@
},
{
"$type": "Document",
"DocumentIndex": 27,
"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": 28,
"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": 29,
"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": 30,
"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": 31,
"DocumentIndex": 5,
"Title": "MainLayout.razor.css",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css",
"RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor.css",
@ -543,9 +368,327 @@
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|",
"WhenOpened": "2026-05-15T16:04:50.455Z"
},
{
"$type": "Document",
"DocumentIndex": 16,
"Title": "DbAdmin.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\DbAdmin.razor",
"RelativeDocumentMoniker": "Components\\Pages\\DbAdmin.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\DbAdmin.razor",
"RelativeToolTip": "Components\\Pages\\DbAdmin.razor",
"ViewState": "AgIAAFwAAAAAAAAAAAApwKMAAAAcAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-06-10T17:34:42.97Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "ChatService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ChatService.cs",
"RelativeDocumentMoniker": "Services\\ChatService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ChatService.cs",
"RelativeToolTip": "Services\\ChatService.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-14T05:02:11.491Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "ILLMBackend.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ILLMBackend.cs",
"RelativeDocumentMoniker": "Services\\ILLMBackend.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ILLMBackend.cs",
"RelativeToolTip": "Services\\ILLMBackend.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAqwAsAAAAVAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-14T05:02:24.894Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "UserEntity.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\UserEntity.cs",
"RelativeDocumentMoniker": "Data\\UserEntity.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\UserEntity.cs",
"RelativeToolTip": "Data\\UserEntity.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAQAAAAUAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-24T04:52:42.227Z"
},
{
"$type": "Document",
"DocumentIndex": 14,
"Title": "OllamaAssistant.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
"RelativeDocumentMoniker": "Components\\Pages\\OllamaAssistant.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
"RelativeToolTip": "Components\\Pages\\OllamaAssistant.razor",
"ViewState": "AgIAAA0AAAAAAAAAAAAhwCEAAAAoAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-20T05:14:39.037Z"
},
{
"$type": "Document",
"DocumentIndex": 22,
"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": "AgIAAN0AAAAAAAAAAAApwO0AAAAMAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-15T15:56:55.229Z"
},
{
"$type": "Document",
"DocumentIndex": 23,
"Title": "IOllamaSetupService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaSetupService.cs",
"RelativeDocumentMoniker": "Services\\IOllamaSetupService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaSetupService.cs",
"RelativeToolTip": "Services\\IOllamaSetupService.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-30T08:35:51.087Z"
},
{
"$type": "Document",
"DocumentIndex": 18,
"Title": "SafeFileHandle.Windows.cs",
"DocumentMoniker": "C:\\Users\\HP\\AppData\\Local\\SourceServer\\dddeb33c46e3c746b90c5f64463103c08ec03b9a85e92eab3b21c11218135d28\\src\\runtime\\src\\libraries\\System.Private.CoreLib\\src\\Microsoft\\Win32\\SafeHandles\\SafeFileHandle.Windows.cs",
"ToolTip": "C:\\Users\\HP\\AppData\\Local\\SourceServer\\dddeb33c46e3c746b90c5f64463103c08ec03b9a85e92eab3b21c11218135d28\\src\\runtime\\src\\libraries\\System.Private.CoreLib\\src\\Microsoft\\Win32\\SafeHandles\\SafeFileHandle.Windows.cs [Schreibgesch\u00FCtzt]",
"ViewState": "AgIAAHwAAAAAAAAAAAAQwHEAAAAQAAAAAQAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-09T14:36:02.721Z",
"EditorCaption": " [Schreibgesch\u00FCtzt]"
},
{
"$type": "Document",
"DocumentIndex": 19,
"Title": "A7405018.jpg",
"DocumentMoniker": "C:\\Users\\HP\\Desktop\\Website\\A7405018.jpg",
"ToolTip": "C:\\Users\\HP\\Desktop\\Website\\A7405018.jpg",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2026-05-23T05:52:56.265Z"
},
{
"$type": "Document",
"DocumentIndex": 24,
"Title": "AuthorBuddy.Web.csproj",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
"RelativeDocumentMoniker": "AuthorBuddy.Web.csproj",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
"RelativeToolTip": "AuthorBuddy.Web.csproj",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000758|",
"WhenOpened": "2026-05-14T10:14:30.128Z"
},
{
"$type": "Document",
"DocumentIndex": 25,
"Title": "INSTALL.md",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\INSTALL.md",
"RelativeDocumentMoniker": "INSTALL.md",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\INSTALL.md",
"RelativeToolTip": "INSTALL.md",
"ViewState": "AgIAAEUAAAAAAAAAAAAAAFIAAAAQAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2026-05-31T17:07:33.423Z"
},
{
"$type": "Document",
"DocumentIndex": 26,
"Title": "IThemeService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IThemeService.cs",
"RelativeDocumentMoniker": "Services\\IThemeService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IThemeService.cs",
"RelativeToolTip": "Services\\IThemeService.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAqwAgAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T17:32:01.784Z"
},
{
"$type": "Document",
"DocumentIndex": 27,
"Title": "RedirectToLogin.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\RedirectToLogin.razor",
"RelativeDocumentMoniker": "Components\\RedirectToLogin.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\RedirectToLogin.razor",
"RelativeToolTip": "Components\\RedirectToLogin.razor",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-21T16:31:16.612Z"
},
{
"$type": "Document",
"DocumentIndex": 28,
"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": "AgIAABwAAAAAAAAAAAA9wC4AAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T16:35:36.655Z"
},
{
"$type": "Document",
"DocumentIndex": 29,
"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": 30,
"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": "AgIAAAAAAAAAAAAAAAAAABEAAAAdAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T13:35:33.991Z"
},
{
"$type": "Document",
"DocumentIndex": 31,
"Title": "logo.jpg",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Assets\\logo.jpg",
"RelativeDocumentMoniker": "Assets\\logo.jpg",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Assets\\logo.jpg",
"RelativeToolTip": "Assets\\logo.jpg",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2026-05-21T17:15:10.852Z"
},
{
"$type": "Document",
"DocumentIndex": 32,
"Title": "OllamaSettings.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
"RelativeDocumentMoniker": "Models\\OllamaSettings.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
"RelativeToolTip": "Models\\OllamaSettings.cs",
"ViewState": "AgIAAAYAAAAAAAAAAAAkwBQAAAASAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T13:39:46.179Z"
},
{
"$type": "Document",
"DocumentIndex": 20,
"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": 33,
"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": "AgIAABIAAAAAAAAAAAAQwCUAAAAWAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T06:02:53.46Z"
},
{
"$type": "Document",
"DocumentIndex": 34,
"Title": "TotpHelper.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
"RelativeDocumentMoniker": "Services\\TotpHelper.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
"RelativeToolTip": "Services\\TotpHelper.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T08:20:49.461Z"
},
{
"$type": "Document",
"DocumentIndex": 35,
"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": 36,
"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": 37,
"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": 38,
"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": 39,
"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": 40,
"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": 41,
"Title": "IFileScannerService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IFileScannerService.cs",
"RelativeDocumentMoniker": "Services\\IFileScannerService.cs",
@ -557,7 +700,7 @@
},
{
"$type": "Document",
"DocumentIndex": 33,
"DocumentIndex": 42,
"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",
@ -569,7 +712,7 @@
},
{
"$type": "Document",
"DocumentIndex": 34,
"DocumentIndex": 43,
"Title": "ProjectEntity.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\ProjectEntity.cs",
"RelativeDocumentMoniker": "Data\\ProjectEntity.cs",
@ -581,7 +724,7 @@
},
{
"$type": "Document",
"DocumentIndex": 35,
"DocumentIndex": 44,
"Title": "IProjectService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IProjectService.cs",
"RelativeDocumentMoniker": "Services\\IProjectService.cs",
@ -593,7 +736,7 @@
},
{
"$type": "Document",
"DocumentIndex": 36,
"DocumentIndex": 45,
"Title": "appsettings.json",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\appsettings.json",
"RelativeDocumentMoniker": "appsettings.json",

View file

@ -7,13 +7,73 @@
"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\\components\\pages\\login.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\login.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\resources\\en.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:resources\\en.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
},
{
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\resources\\de.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:resources\\de.json||{90A6B3A7-C1A3-4009-A288-E2FF89E96FA0}"
},
{
"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\\chat.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\chat.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\\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\\services\\ollamabackend.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ollamabackend.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\\illmbackend.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\illmbackend.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\\chatservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\chatservice.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\\data\\userentity.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:data\\userentity.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\\login.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\login.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\\pages\\firstrunsetup.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\firstrunsetup.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\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\ollamaassistant.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\\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\\pages\\dbadmin.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\dbadmin.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\\dbadminservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\dbadminservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\HP\\AppData\\Local\\SourceServer\\dddeb33c46e3c746b90c5f64463103c08ec03b9a85e92eab3b21c11218135d28\\src\\runtime\\src\\libraries\\System.Private.CoreLib\\src\\Microsoft\\Win32\\SafeHandles\\SafeFileHandle.Windows.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
},
@ -24,22 +84,10 @@
"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\\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\\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\\data\\userentity.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:data\\userentity.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}"
@ -56,10 +104,6 @@
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\install.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:install.md||{EFC0BB08-EA7D-40C6-A696-C870411A895B}"
},
{
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\components\\pages\\ollamaassistant.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\pages\\ollamaassistant.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\\ithemeservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ithemeservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
@ -104,10 +148,6 @@
"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\\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\\app.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:components\\app.razor||{40D31677-CBC0-4297-A9EF-89D907823A98}"
@ -124,10 +164,6 @@
"AbsoluteMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|e:\\ollama_net\\ollama\\authorbuddy.web\\models\\modelstatus.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:models\\modelstatus.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.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\\ifilescannerservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}",
"RelativeMoniker": "D:0:0:{4F4697EF-EFFA-26AC-C8A1-C9859805D247}|AuthorBuddy.Web.csproj|solutionrelative:services\\ifilescannerservice.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
@ -156,7 +192,7 @@
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 9,
"SelectedChildIndex": 6,
"Children": [
{
"$type": "Bookmark",
@ -164,44 +200,140 @@
},
{
"$type": "Document",
"DocumentIndex": 6,
"DocumentIndex": 1,
"Title": "en.json",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Resources\\en.json",
"RelativeDocumentMoniker": "Resources\\en.json",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Resources\\en.json",
"RelativeToolTip": "Resources\\en.json",
"ViewState": "AgIAABQBAAAAAAAAAAAAACQBAAAfAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
"WhenOpened": "2026-06-14T06:03:36.064Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "de.json",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Resources\\de.json",
"RelativeDocumentMoniker": "Resources\\de.json",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Resources\\de.json",
"RelativeToolTip": "Resources\\de.json",
"ViewState": "AgIAABoBAAAAAAAAAAAAACQBAAACAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001642|",
"WhenOpened": "2026-06-14T06:02:40.854Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "Chat.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Chat.razor",
"RelativeDocumentMoniker": "Components\\Pages\\Chat.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Chat.razor",
"RelativeToolTip": "Components\\Pages\\Chat.razor",
"ViewState": "AgIAAFgAAAAAAAAAAAApwG0AAAANAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-06-14T05:59:50.644Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "OllamaBackend.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaBackend.cs",
"RelativeDocumentMoniker": "Services\\OllamaBackend.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\OllamaBackend.cs",
"RelativeToolTip": "Services\\OllamaBackend.cs",
"ViewState": "AgIAACUAAAAAAAAAAAASwDUAAAAeAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-14T05:03:24.305Z"
},
{
"$type": "Document",
"DocumentIndex": 17,
"Title": "DbAdminService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\DbAdminService.cs",
"RelativeDocumentMoniker": "Services\\DbAdminService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\DbAdminService.cs",
"RelativeToolTip": "Services\\DbAdminService.cs",
"ViewState": "AgIAABUAAAAAAAAAAAAowAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-10T17:34:10.361Z"
},
{
"$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": "AgIAAFAAAAAAAAAAAAAQwFUAAAApAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-15T16:04:36.444Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 12,
"Title": "Login.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Login.razor",
"RelativeDocumentMoniker": "Components\\Pages\\Login.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Login.razor",
"RelativeToolTip": "Components\\Pages\\Login.razor",
"ViewState": "AgIAADAAAAAAAAAAAADwv0QAAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-21T08:47:41.996Z"
},
{
"$type": "Document",
"DocumentIndex": 13,
"Title": "FirstRunSetup.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\FirstRunSetup.razor",
"RelativeDocumentMoniker": "Components\\Pages\\FirstRunSetup.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\FirstRunSetup.razor",
"RelativeToolTip": "Components\\Pages\\FirstRunSetup.razor",
"ViewState": "AgIAACEAAAAAAAAAAAAAACsAAABsAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-06-09T17:25:10.421Z"
},
{
"$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": "AgIAAE0BAAAAAAAAAAAAAC0BAAAMAAAAAAAAAA==",
"ViewState": "AgIAANwBAAAAAAAAAAAAAO4BAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T17:37:24.777Z"
},
{
"$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": "AgIAAJsAAAAAAAAAAAAAAKwAAAAWAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-15T16:43:26.6Z"
},
{
"$type": "Document",
"DocumentIndex": 2,
"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": "AgIAADMAAAAAAAAAAAAAAFUAAAADAAAAAAAAAA==",
"ViewState": "AgIAAFAAAAAAAAAAAAAQwGMAAAAhAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-20T04:07:21.54Z",
"EditorCaption": ""
"WhenOpened": "2026-05-20T04:07:21.54Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"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": "AgIAAJsAAAAAAAAAAAAAAKwAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-15T16:43:26.6Z"
},
{
"$type": "Document",
"DocumentIndex": 21,
"Title": "Editor.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Editor.razor",
"RelativeDocumentMoniker": "Components\\Pages\\Editor.razor",
@ -214,267 +346,7 @@
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "UserEntity.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\UserEntity.cs",
"RelativeDocumentMoniker": "Data\\UserEntity.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\UserEntity.cs",
"RelativeToolTip": "Data\\UserEntity.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAVAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-24T04:52:42.227Z"
},
{
"$type": "Document",
"DocumentIndex": 10,
"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": "AgIAAN0AAAAAAAAAAAApwO0AAAAMAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-15T15:56:55.229Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "IOllamaSetupService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaSetupService.cs",
"RelativeDocumentMoniker": "Services\\IOllamaSetupService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaSetupService.cs",
"RelativeToolTip": "Services\\IOllamaSetupService.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-30T08:35:51.087Z"
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "Login.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Login.razor",
"RelativeDocumentMoniker": "Components\\Pages\\Login.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\Login.razor",
"RelativeToolTip": "Components\\Pages\\Login.razor",
"ViewState": "AgIAADYAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-21T08:47:41.996Z",
"EditorCaption": ""
},
{
"$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": "AgIAANQBAAAAAAAAAAAhwPMBAAAJAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-15T16:04:36.444Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "SafeFileHandle.Windows.cs",
"DocumentMoniker": "C:\\Users\\HP\\AppData\\Local\\SourceServer\\dddeb33c46e3c746b90c5f64463103c08ec03b9a85e92eab3b21c11218135d28\\src\\runtime\\src\\libraries\\System.Private.CoreLib\\src\\Microsoft\\Win32\\SafeHandles\\SafeFileHandle.Windows.cs",
"ToolTip": "C:\\Users\\HP\\AppData\\Local\\SourceServer\\dddeb33c46e3c746b90c5f64463103c08ec03b9a85e92eab3b21c11218135d28\\src\\runtime\\src\\libraries\\System.Private.CoreLib\\src\\Microsoft\\Win32\\SafeHandles\\SafeFileHandle.Windows.cs [Schreibgesch\u00FCtzt]",
"ViewState": "AgIAAHwAAAAAAAAAAAAQwHEAAAAQAAAAAQAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-09T14:36:02.721Z",
"EditorCaption": " [Schreibgesch\u00FCtzt]"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "A7405018.jpg",
"DocumentMoniker": "C:\\Users\\HP\\Desktop\\Website\\A7405018.jpg",
"ToolTip": "C:\\Users\\HP\\Desktop\\Website\\A7405018.jpg",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2026-05-23T05:52:56.265Z"
},
{
"$type": "Document",
"DocumentIndex": 12,
"Title": "AuthorBuddy.Web.csproj",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
"RelativeDocumentMoniker": "AuthorBuddy.Web.csproj",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
"RelativeToolTip": "AuthorBuddy.Web.csproj",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000758|",
"WhenOpened": "2026-05-14T10:14:30.128Z"
},
{
"$type": "Document",
"DocumentIndex": 13,
"Title": "INSTALL.md",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\INSTALL.md",
"RelativeDocumentMoniker": "INSTALL.md",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\INSTALL.md",
"RelativeToolTip": "INSTALL.md",
"ViewState": "AgIAAEUAAAAAAAAAAAAAAFIAAAAQAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2026-05-31T17:07:33.423Z"
},
{
"$type": "Document",
"DocumentIndex": 14,
"Title": "OllamaAssistant.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
"RelativeDocumentMoniker": "Components\\Pages\\OllamaAssistant.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
"RelativeToolTip": "Components\\Pages\\OllamaAssistant.razor",
"ViewState": "AgIAANoAAAAAAAAAAAAiwPIAAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-20T05:14:39.037Z"
},
{
"$type": "Document",
"DocumentIndex": 15,
"Title": "IThemeService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IThemeService.cs",
"RelativeDocumentMoniker": "Services\\IThemeService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IThemeService.cs",
"RelativeToolTip": "Services\\IThemeService.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAqwAgAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T17:32:01.784Z"
},
{
"$type": "Document",
"DocumentIndex": 16,
"Title": "RedirectToLogin.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\RedirectToLogin.razor",
"RelativeDocumentMoniker": "Components\\RedirectToLogin.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\RedirectToLogin.razor",
"RelativeToolTip": "Components\\RedirectToLogin.razor",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-21T16:31:16.612Z"
},
{
"$type": "Document",
"DocumentIndex": 17,
"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": "AgIAABwAAAAAAAAAAAA9wC4AAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T16:35:36.655Z"
},
{
"$type": "Document",
"DocumentIndex": 18,
"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": 19,
"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": "AgIAAAAAAAAAAAAAAAAAABEAAAAdAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T13:35:33.991Z"
},
{
"$type": "Document",
"DocumentIndex": 20,
"Title": "logo.jpg",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Assets\\logo.jpg",
"RelativeDocumentMoniker": "Assets\\logo.jpg",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Assets\\logo.jpg",
"RelativeToolTip": "Assets\\logo.jpg",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2026-05-21T17:15:10.852Z"
},
{
"$type": "Document",
"DocumentIndex": 21,
"Title": "OllamaSettings.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
"RelativeDocumentMoniker": "Models\\OllamaSettings.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
"RelativeToolTip": "Models\\OllamaSettings.cs",
"ViewState": "AgIAAAYAAAAAAAAAAAAkwBQAAAASAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T13:39:46.179Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"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": 22,
"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": "AgIAABIAAAAAAAAAAAAQwCUAAAAWAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T06:02:53.46Z"
},
{
"$type": "Document",
"DocumentIndex": 23,
"Title": "TotpHelper.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
"RelativeDocumentMoniker": "Services\\TotpHelper.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
"RelativeToolTip": "Services\\TotpHelper.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T08:20:49.461Z"
},
{
"$type": "Document",
"DocumentIndex": 24,
"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": 25,
"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": 26,
"DocumentIndex": 6,
"Title": "NavMenu.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\NavMenu.razor",
"RelativeDocumentMoniker": "Components\\Layout\\NavMenu.razor",
@ -486,55 +358,7 @@
},
{
"$type": "Document",
"DocumentIndex": 27,
"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": 28,
"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": 29,
"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": 30,
"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": 31,
"DocumentIndex": 5,
"Title": "MainLayout.razor.css",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Layout\\MainLayout.razor.css",
"RelativeDocumentMoniker": "Components\\Layout\\MainLayout.razor.css",
@ -544,9 +368,327 @@
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.003000|",
"WhenOpened": "2026-05-15T16:04:50.455Z"
},
{
"$type": "Document",
"DocumentIndex": 16,
"Title": "DbAdmin.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\DbAdmin.razor",
"RelativeDocumentMoniker": "Components\\Pages\\DbAdmin.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\DbAdmin.razor",
"RelativeToolTip": "Components\\Pages\\DbAdmin.razor",
"ViewState": "AgIAAFwAAAAAAAAAAAApwKMAAAAcAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-06-10T17:34:42.97Z"
},
{
"$type": "Document",
"DocumentIndex": 9,
"Title": "ChatService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ChatService.cs",
"RelativeDocumentMoniker": "Services\\ChatService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ChatService.cs",
"RelativeToolTip": "Services\\ChatService.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-14T05:02:11.491Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "ILLMBackend.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ILLMBackend.cs",
"RelativeDocumentMoniker": "Services\\ILLMBackend.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\ILLMBackend.cs",
"RelativeToolTip": "Services\\ILLMBackend.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAqwAsAAAAVAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-14T05:02:24.894Z"
},
{
"$type": "Document",
"DocumentIndex": 11,
"Title": "UserEntity.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\UserEntity.cs",
"RelativeDocumentMoniker": "Data\\UserEntity.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\UserEntity.cs",
"RelativeToolTip": "Data\\UserEntity.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAQAAAAUAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-24T04:52:42.227Z"
},
{
"$type": "Document",
"DocumentIndex": 14,
"Title": "OllamaAssistant.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
"RelativeDocumentMoniker": "Components\\Pages\\OllamaAssistant.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\Pages\\OllamaAssistant.razor",
"RelativeToolTip": "Components\\Pages\\OllamaAssistant.razor",
"ViewState": "AgIAAA0AAAAAAAAAAAAhwCEAAAAoAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-20T05:14:39.037Z"
},
{
"$type": "Document",
"DocumentIndex": 22,
"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": "AgIAAN0AAAAAAAAAAAApwO0AAAAMAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-15T15:56:55.229Z"
},
{
"$type": "Document",
"DocumentIndex": 23,
"Title": "IOllamaSetupService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaSetupService.cs",
"RelativeDocumentMoniker": "Services\\IOllamaSetupService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IOllamaSetupService.cs",
"RelativeToolTip": "Services\\IOllamaSetupService.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-30T08:35:51.087Z"
},
{
"$type": "Document",
"DocumentIndex": 18,
"Title": "SafeFileHandle.Windows.cs",
"DocumentMoniker": "C:\\Users\\HP\\AppData\\Local\\SourceServer\\dddeb33c46e3c746b90c5f64463103c08ec03b9a85e92eab3b21c11218135d28\\src\\runtime\\src\\libraries\\System.Private.CoreLib\\src\\Microsoft\\Win32\\SafeHandles\\SafeFileHandle.Windows.cs",
"ToolTip": "C:\\Users\\HP\\AppData\\Local\\SourceServer\\dddeb33c46e3c746b90c5f64463103c08ec03b9a85e92eab3b21c11218135d28\\src\\runtime\\src\\libraries\\System.Private.CoreLib\\src\\Microsoft\\Win32\\SafeHandles\\SafeFileHandle.Windows.cs [Schreibgesch\u00FCtzt]",
"ViewState": "AgIAAHwAAAAAAAAAAAAQwHEAAAAQAAAAAQAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-06-09T14:36:02.721Z",
"EditorCaption": " [Schreibgesch\u00FCtzt]"
},
{
"$type": "Document",
"DocumentIndex": 19,
"Title": "A7405018.jpg",
"DocumentMoniker": "C:\\Users\\HP\\Desktop\\Website\\A7405018.jpg",
"ToolTip": "C:\\Users\\HP\\Desktop\\Website\\A7405018.jpg",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2026-05-23T05:52:56.265Z"
},
{
"$type": "Document",
"DocumentIndex": 24,
"Title": "AuthorBuddy.Web.csproj",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
"RelativeDocumentMoniker": "AuthorBuddy.Web.csproj",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\AuthorBuddy.Web.csproj",
"RelativeToolTip": "AuthorBuddy.Web.csproj",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000758|",
"WhenOpened": "2026-05-14T10:14:30.128Z"
},
{
"$type": "Document",
"DocumentIndex": 25,
"Title": "INSTALL.md",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\INSTALL.md",
"RelativeDocumentMoniker": "INSTALL.md",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\INSTALL.md",
"RelativeToolTip": "INSTALL.md",
"ViewState": "AgIAAEUAAAAAAAAAAAAAAFIAAAAQAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001818|",
"WhenOpened": "2026-05-31T17:07:33.423Z"
},
{
"$type": "Document",
"DocumentIndex": 26,
"Title": "IThemeService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IThemeService.cs",
"RelativeDocumentMoniker": "Services\\IThemeService.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IThemeService.cs",
"RelativeToolTip": "Services\\IThemeService.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAqwAgAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T17:32:01.784Z"
},
{
"$type": "Document",
"DocumentIndex": 27,
"Title": "RedirectToLogin.razor",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\RedirectToLogin.razor",
"RelativeDocumentMoniker": "Components\\RedirectToLogin.razor",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Components\\RedirectToLogin.razor",
"RelativeToolTip": "Components\\RedirectToLogin.razor",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAsAAAAIAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000759|",
"WhenOpened": "2026-05-21T16:31:16.612Z"
},
{
"$type": "Document",
"DocumentIndex": 28,
"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": "AgIAABwAAAAAAAAAAAA9wC4AAAAZAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T16:35:36.655Z"
},
{
"$type": "Document",
"DocumentIndex": 29,
"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": 30,
"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": "AgIAAAAAAAAAAAAAAAAAABEAAAAdAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T13:35:33.991Z"
},
{
"$type": "Document",
"DocumentIndex": 31,
"Title": "logo.jpg",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Assets\\logo.jpg",
"RelativeDocumentMoniker": "Assets\\logo.jpg",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Assets\\logo.jpg",
"RelativeToolTip": "Assets\\logo.jpg",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.001533|",
"WhenOpened": "2026-05-21T17:15:10.852Z"
},
{
"$type": "Document",
"DocumentIndex": 32,
"Title": "OllamaSettings.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
"RelativeDocumentMoniker": "Models\\OllamaSettings.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Models\\OllamaSettings.cs",
"RelativeToolTip": "Models\\OllamaSettings.cs",
"ViewState": "AgIAAAYAAAAAAAAAAAAkwBQAAAASAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T13:39:46.179Z"
},
{
"$type": "Document",
"DocumentIndex": 20,
"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": 33,
"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": "AgIAABIAAAAAAAAAAAAQwCUAAAAWAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-16T06:02:53.46Z"
},
{
"$type": "Document",
"DocumentIndex": 34,
"Title": "TotpHelper.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
"RelativeDocumentMoniker": "Services\\TotpHelper.cs",
"ToolTip": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\TotpHelper.cs",
"RelativeToolTip": "Services\\TotpHelper.cs",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-05-21T08:20:49.461Z"
},
{
"$type": "Document",
"DocumentIndex": 35,
"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": 36,
"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": 37,
"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": 38,
"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": 39,
"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": 40,
"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": 41,
"Title": "IFileScannerService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IFileScannerService.cs",
"RelativeDocumentMoniker": "Services\\IFileScannerService.cs",
@ -558,7 +700,7 @@
},
{
"$type": "Document",
"DocumentIndex": 33,
"DocumentIndex": 42,
"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",
@ -570,7 +712,7 @@
},
{
"$type": "Document",
"DocumentIndex": 34,
"DocumentIndex": 43,
"Title": "ProjectEntity.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Data\\ProjectEntity.cs",
"RelativeDocumentMoniker": "Data\\ProjectEntity.cs",
@ -582,7 +724,7 @@
},
{
"$type": "Document",
"DocumentIndex": 35,
"DocumentIndex": 44,
"Title": "IProjectService.cs",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\Services\\IProjectService.cs",
"RelativeDocumentMoniker": "Services\\IProjectService.cs",
@ -594,7 +736,7 @@
},
{
"$type": "Document",
"DocumentIndex": 36,
"DocumentIndex": 45,
"Title": "appsettings.json",
"DocumentMoniker": "E:\\ollama_net\\ollama\\AuthorBuddy.Web\\appsettings.json",
"RelativeDocumentMoniker": "appsettings.json",

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<NameOfLastUsedPublishProfile>E:\ollama_net\ollama\AuthorBuddy.Web\Properties\PublishProfiles\FolderProfile.pubxml</NameOfLastUsedPublishProfile>
</PropertyGroup>
</Project>

View file

@ -41,7 +41,10 @@ else
@if (activeSection == "file")
{
<div class="nav-section-body">
<button class="nav-item" @onclick="CreateNewProject">@Loc["nav.menu.file.new_project"]</button>
@if (!_currentUserRoles.Contains(UserRole.Blogger))
{
<button class="nav-item" @onclick="CreateNewProject">@Loc["nav.menu.file.new_project"]</button>
}
<button class="nav-item" @onclick="OpenEditor">@Loc["nav.menu.file.open_editor"]</button>
<div class="nav-separator"></div>
<button class="nav-item" @onclick="ExitApp">@Loc["nav.menu.file.exit"]</button>
@ -57,7 +60,7 @@ else
{
<div class="nav-section-body">
<button class="nav-item" @onclick='() => NavigateTo("/settings")'>@Loc["nav.menu.extras.settings"]</button>
@if (_currentUserRole == UserRole.Administrator)
@if (_currentUserRoles.Contains(UserRole.Administrator))
{
<button class="nav-item" @onclick='() => NavigateTo("/models")'>@Loc["nav.menu.extras.manage_models"]</button>
<button class="nav-item" @onclick='() => NavigateTo("/assistant")'>@Loc["nav.menu.extras.assistant"]</button>
@ -65,6 +68,7 @@ else
<div class="nav-separator"></div>
<button class="nav-item" @onclick='() => NavigateTo("/user-management")'>@Loc["nav.menu.extras.user_management"]</button>
<button class="nav-item" @onclick='() => NavigateTo("/db-admin")'>📊 DB-Admin</button>
<button class="nav-item" @onclick='() => NavigateTo("/chat")'>💬 Chat</button>
}
else
{
@ -73,6 +77,37 @@ else
</div>
}
</div>
@{
var canMarkdown = _currentUserRoles.Contains(UserRole.Administrator) || _currentUserRoles.Contains(UserRole.Blogger);
var canChat = _currentUserRoles.Contains(UserRole.Administrator) || _currentUserRoles.Contains(UserRole.Chat);
}
@if (canChat || canMarkdown)
{
<div class="nav-section">
<button class="nav-section-header" @onclick='() => ToggleSection("tools")'>
<span class="nav-icon">@(activeSection == "tools" ? "▼" : "▶")</span>
@Loc["nav.menu.chat"]
</button>
@if (activeSection == "tools")
{
@if (canMarkdown)
{
<div class="nav-section-body">
<button class="nav-item" @onclick='() => NavigateTo("/quick-markdown")'>⚡ Quick Markdown</button>
</div>
}
@if (canChat)
{
<div class="nav-section-body">
<button class="nav-item" @onclick='() => NavigateTo("/chat")'>💬 Chat öffnen</button>
</div>
}
}
</div>
}
@if (_currentUserRoles.Contains(UserRole.Administrator) || _currentUserRoles.Contains(UserRole.Author))
{
<div class="nav-section">
<button class="nav-section-header" @onclick='() => ToggleSection("projects")'>
<span class="nav-icon">@(activeSection == "projects" ? "▼" : "▶")</span>
@ -113,6 +148,7 @@ else
</div>
}
</div>
}
@if (currentProjectId > 0 && fileTree != null)
{
<div class="nav-section nav-section-open">
@ -396,7 +432,7 @@ else
@code {
private string _username = string.Empty;
private bool _isAuthenticated;
private UserRole _currentUserRole;
private List<UserRole> _currentUserRoles = new();
private bool _isLoading = true;
private bool _showInfoOverlay;
private string? activeSection;
@ -457,7 +493,7 @@ else
var user = await AuthService.GetCurrentUserAsync();
_username = user?.Name ?? string.Empty;
_isAuthenticated = user != null;
_currentUserRole = user?.Role ?? UserRole.Author;
_currentUserRoles = user?.Roles ?? new List<UserRole> { UserRole.Author };
}
private async Task AutoLoadLastProject()
@ -577,7 +613,7 @@ else
try
{
var currentUser = AuthService.CurrentUser;
int? filterUserId = currentUser?.Role == UserRole.Administrator ? null : currentUser?.Id;
int? filterUserId = currentUser?.IsAdministrator == true ? null : currentUser?.Id;
projects = await ProjectService.GetAllProjectsAsync(filterUserId);
}
catch

188
Components/Pages/Chat.razor Normal file
View file

@ -0,0 +1,188 @@
@page "/chat"
@attribute [Authorize(Roles = "Administrator,Chat")]
@inject IChatService ChatService
@inject IOllamaService OllamaService
@inject OllamaSettings Settings
@inject ISettingsService SettingsService
@inject IAuthService AuthService
@inject NavigationManager Navigation
@rendermode InteractiveServer
<PageTitle>Chat</PageTitle>
<div class="chat-page">
<div class="chat-header">
<h3>Chat</h3>
<div class="chat-header-actions">
<button class="btn btn-sm chat-settings-toggle" @onclick="ToggleSettings">
@(_showSettings ? "Einstellungen ausblenden" : "Einstellungen")
</button>
<button class="btn btn-sm" @onclick="ClearHistory" disabled="@(_messages.Count == 0)">Verlauf löschen</button>
</div>
</div>
@if (_showSettings)
{
<div class="chat-settings">
<div class="form-group">
<label>System-Prompt</label>
<textarea class="chat-settings-textarea" @bind="_systemPrompt" rows="3"></textarea>
</div>
<div class="form-group">
<label for="chatTemp">Temperatur: @_temperature.ToString("F2")</label>
<input id="chatTemp" type="range" min="0" max="2" step="0.05" @bind="_temperature" />
<div class="qm-range-labels">
<span>präzise (0)</span>
<span>kreativ (2)</span>
</div>
</div>
<div class="form-group">
<button class="btn btn-sm btn-primary" @onclick="SaveSettings">System-Prompt & Temperatur speichern</button>
<button class="btn btn-sm" @onclick="ResetToDefault">Auf Standard zurücksetzen</button>
</div>
</div>
}
<div class="chat-messages" id="chat-messages">
@if (_messages.Count == 0)
{
<div class="chat-empty">Noch keine Nachrichten. Stelle eine Frage.</div>
}
@foreach (var msg in _messages)
{
<div class="chat-message chat-user">
<div class="chat-avatar">U</div>
<div class="chat-bubble">@msg.UserMessage</div>
</div>
<div class="chat-message chat-assistant">
<div class="chat-avatar">A</div>
<div class="chat-bubble">@MarkdownToHtml(msg.AssistantResponse)</div>
</div>
}
@if (_isBusy)
{
<div class="chat-message chat-assistant">
<div class="chat-avatar">A</div>
<div class="chat-bubble chat-thinking">…</div>
</div>
}
</div>
<div class="chat-input">
<textarea class="chat-textarea" @bind="_input" rows="3"
placeholder="Nachricht eingeben…"
@onkeydown="HandleKeyDown"
disabled="@_isBusy"></textarea>
<button class="btn btn-primary" @onclick="Send" disabled="@(_isBusy || string.IsNullOrWhiteSpace(_input))">
Senden
</button>
</div>
@if (!string.IsNullOrEmpty(_error))
{
<div class="auth-error">@_error</div>
}
</div>
@code {
private List<ChatEntity> _messages = new();
private string _input = string.Empty;
private string _error = string.Empty;
private bool _isBusy;
private int _userId;
private bool _showSettings;
private string _systemPrompt = string.Empty;
private double _temperature = 0.7;
protected override async Task OnInitializedAsync()
{
var user = await AuthService.GetCurrentUserAsync();
if (user == null || (!user.HasRole(UserRole.Administrator) && !user.HasRole(UserRole.Chat)))
{
Navigation.NavigateTo("/");
return;
}
_userId = user.Id;
_messages = await ChatService.GetHistoryAsync(_userId);
_systemPrompt = await SettingsService.GetChatPromptAsync()
?? Settings.GetChatDefaultPrompt(Settings.Language);
_temperature = await SettingsService.GetChatTempAsync();
}
private void ToggleSettings()
{
_showSettings = !_showSettings;
}
private async Task SaveSettings()
{
await SettingsService.SaveChatPromptAsync(_systemPrompt);
await SettingsService.SaveChatTempAsync(_temperature);
}
private async Task ResetToDefault()
{
_systemPrompt = Settings.GetChatDefaultPrompt(Settings.Language);
_temperature = Settings.ChatTemp;
await SaveSettings();
}
private async Task HandleKeyDown(KeyboardEventArgs e)
{
if (e.Key == "Enter" && !e.ShiftKey)
{
await Send();
}
}
private async Task Send()
{
if (string.IsNullOrWhiteSpace(_input) || _isBusy) return;
var text = _input.Trim();
_input = string.Empty;
_error = string.Empty;
_isBusy = true;
try
{
var response = await OllamaService.Chat(Settings.StyleModel, text, _systemPrompt, _temperature);
var msg = new ChatEntity
{
UserId = _userId,
UserMessage = text,
AssistantResponse = response,
Model = Settings.StyleModel,
Timestamp = DateTime.UtcNow
};
await ChatService.AddMessageAsync(msg);
_messages.Add(msg);
}
catch (Exception ex)
{
_error = $"Fehler: {ex.Message}";
}
finally
{
_isBusy = false;
}
}
private async Task ClearHistory()
{
await ChatService.ClearHistoryAsync(_userId);
_messages.Clear();
}
private MarkupString MarkdownToHtml(string text)
{
if (string.IsNullOrEmpty(text)) return new MarkupString(string.Empty);
var html = System.Web.HttpUtility.HtmlEncode(text);
html = System.Text.RegularExpressions.Regex.Replace(html, @"\*\*(.+?)\*\*", "<strong>$1</strong>");
html = System.Text.RegularExpressions.Regex.Replace(html, @"\*(.+?)\*", "<em>$1</em>");
html = html.Replace("\n", "<br />");
return new MarkupString(html);
}
}

View file

@ -125,7 +125,7 @@
protected override void OnInitialized()
{
var user = AuthService.CurrentUser;
if (user?.Role != UserRole.Administrator)
if (user == null || !user.IsAdministrator)
{
Navigation.NavigateTo("/");
return;

View file

@ -0,0 +1,126 @@
@page "/first-run-setup"
@inject IAuthService AuthService
@inject ISettingsService SettingsService
@inject NavigationManager Navigation
@inject ILocalizationService Loc
@rendermode InteractiveServer
<PageTitle>Ersteinrichtung</PageTitle>
<div class="auth-page">
<div class="auth-card">
<div class="auth-logo">
<img src="/Assets/logo.jpg" alt="Author Buddy" />
</div>
<h2>Ersteinrichtung</h2>
<p>Willkommen bei Author Buddy! Richten Sie Ihren Administrator-Account ein.</p>
@if (!_setupComplete)
{
<div class="form-group">
<label for="adminUsername">Admin-Benutzername</label>
<input id="adminUsername" @bind="_username" disabled="@_isBusy" />
</div>
<button @onclick="GeneratePassword" disabled="@(() => _isBusy || string.IsNullOrWhiteSpace(_username))">Passwort generieren</button>
@if (!string.IsNullOrEmpty(_generatedPassword))
{
<div class="auth-error" style="margin-top: 1rem; padding: 1rem; border: 2px solid red; background: #fff0f0; color: #c00; border-radius: 8px;">
<strong style="font-size: 1.2rem;">Sicherheitswarnung!</strong>
<p>Dieses Passwort wird nur <strong>einmalig</strong> angezeigt. Speichern Sie es sofort sicher ab.</p>
<div style="font-family: monospace; font-size: 1.4rem; text-align: center; padding: 0.5rem; background: white; border: 1px solid #c00; margin: 0.5rem 0; letter-spacing: 2px;">
@_generatedPassword
</div>
<p style="font-size: 0.85rem; color: #888;">Nach dem Klick auf "Admin erstellen" wird das Passwort nicht mehr angezeigt.</p>
</div>
<div class="form-group">
<label for="adminPassword">Passwort eingeben (zur Bestätigung)</label>
<input id="adminPassword" type="password" @bind="_password" />
</div>
<button @onclick="CreateAdmin" disabled="@(() => _isBusy || _password != _generatedPassword)">Admin erstellen</button>
}
}
else
{
<div class="auth-success" style="margin-top: 1rem; padding: 1rem; border: 2px solid green; background: #f0fff0; color: #080; border-radius: 8px;">
<strong>Admin-Account erfolgreich erstellt!</strong>
<p>Sie werden zum Login weitergeleitet...</p>
</div>
}
@if (!string.IsNullOrEmpty(_errorMessage))
{
<div class="auth-error">@_errorMessage</div>
}
</div>
</div>
@code {
private string _username = "admin";
private string? _generatedPassword;
private string _password = string.Empty;
private string _errorMessage = string.Empty;
private bool _isBusy;
private bool _setupComplete;
protected override async Task OnInitializedAsync()
{
var users = await SettingsService.GetUsersAsync();
if (users.Count > 0)
{
Navigation.NavigateTo("/login");
}
}
private void GeneratePassword()
{
using var rng = System.Security.Cryptography.RandomNumberGenerator.Create();
var bytes = new byte[12];
rng.GetBytes(bytes);
_generatedPassword = Convert.ToBase64String(bytes).TrimEnd('=').Replace('+', 'X').Replace('/', 'Y');
_password = string.Empty;
_errorMessage = string.Empty;
}
private async Task CreateAdmin()
{
if (string.IsNullOrWhiteSpace(_username) || string.IsNullOrWhiteSpace(_password))
{
_errorMessage = "Benutzername und Passwort dürfen nicht leer sein.";
return;
}
_isBusy = true;
_errorMessage = string.Empty;
try
{
var result = await AuthService.RegisterAsync(_username, _password);
if (result)
{
_generatedPassword = null;
_password = string.Empty;
_setupComplete = true;
StateHasChanged();
await Task.Delay(2000);
Navigation.NavigateTo("/login");
}
else
{
_errorMessage = "Benutzername bereits vergeben.";
}
}
catch (Exception ex)
{
_errorMessage = $"Fehler: {ex.Message}";
}
finally
{
_isBusy = false;
}
}
}

View file

@ -69,7 +69,7 @@
var users = await SettingsService.GetUsersAsync();
if (users.Count == 0)
{
Navigation.NavigateTo("/register");
Navigation.NavigateTo("/first-run-setup");
}
}

View file

@ -0,0 +1,153 @@
@page "/quick-markdown"
@attribute [Authorize(Roles = "Administrator,Blogger")]
@inject IOllamaService OllamaService
@inject OllamaSettings Settings
@inject IJSRuntime JS
@inject IAuthService AuthService
@inject ISettingsService SettingsService
@inject NavigationManager Navigation
@rendermode InteractiveServer
<PageTitle>Quick Markdown</PageTitle>
<div class="qm-container">
<h2>Quick Markdown</h2>
<p>Erzeugt aus einem Text und einem System-Prompt direkt formatiertes Markdown ohne Projekt oder Speicherung.</p>
<div class="qm-layout">
<div class="qm-input-panel">
<div class="form-group">
<label>System-Prompt</label>
<textarea class="qm-textarea qm-prompt" @bind="_systemPrompt" rows="4"></textarea>
</div>
<div class="form-group">
<label for="tempRange">Temperatur: @_temperature.ToString("F2")</label>
<input id="tempRange" type="range" min="0" max="2" step="0.05" @bind="_temperature" />
<div class="qm-range-labels">
<span>präzise (0)</span>
<span>kreativ (2)</span>
</div>
</div>
<div class="form-group">
<div class="qm-prompt-actions">
<button class="btn btn-secondary" @onclick="SaveSettings">System-Prompt & Temperatur speichern</button>
<button class="btn btn-secondary" @onclick="ResetToDefault">Auf Standard zurücksetzen</button>
</div>
</div>
<div class="form-group">
<label>Text</label>
<textarea class="qm-textarea" @bind="_inputText" rows="12" placeholder="Text hier eingeben…"></textarea>
</div>
<button class="btn btn-primary" @onclick="Generate" disabled="@_isBusy || string.IsNullOrWhiteSpace(_inputText)">
@if (_isBusy)
{
<span>Generiere…</span>
}
else
{
<span>Markdown erzeugen</span>
}
</button>
</div>
<div class="qm-output-panel">
<div class="form-group">
<label>Ausgabe</label>
<div class="qm-output-toolbar">
<button class="btn btn-small" @onclick="CopyOutput" disabled="@string.IsNullOrEmpty(_output)">📋 Kopieren</button>
<button class="btn btn-small" @onclick="ClearOutput">🗑 Leeren</button>
</div>
</div>
<textarea class="qm-textarea qm-output" @bind="_output" rows="20" readonly></textarea>
</div>
</div>
@if (!string.IsNullOrEmpty(_error))
{
<div class="auth-error">@_error</div>
}
@if (!string.IsNullOrEmpty(_modelNotice))
{
<div class="qm-notice">@_modelNotice</div>
}
</div>
@code {
private string _systemPrompt = string.Empty;
private string _inputText = string.Empty;
private string _output = string.Empty;
private string _error = string.Empty;
private string _modelNotice = string.Empty;
private bool _isBusy;
private double _temperature = 0.3;
protected override async Task OnInitializedAsync()
{
var user = await AuthService.GetCurrentUserAsync();
if (user == null || !user.HasAnyRole(UserRole.Administrator, UserRole.Blogger))
{
Navigation.NavigateTo("/");
return;
}
_systemPrompt = await SettingsService.GetQuickMarkdownPromptAsync()
?? Settings.GetQuickMarkdownDefaultPrompt(Settings.Language);
_temperature = await SettingsService.GetQuickMarkdownTempAsync();
_modelNotice = $"Verwendetes Modell: {Settings.StyleModel} (via {Settings.Backend})";
}
private async Task SaveSettings()
{
await SettingsService.SaveQuickMarkdownPromptAsync(_systemPrompt);
await SettingsService.SaveQuickMarkdownTempAsync(_temperature);
}
private async Task ResetToDefault()
{
_systemPrompt = Settings.GetQuickMarkdownDefaultPrompt(Settings.Language);
_temperature = Settings.QuickMarkdownTemp;
await SaveSettings();
}
private async Task Generate()
{
if (string.IsNullOrWhiteSpace(_inputText)) return;
_isBusy = true;
_error = string.Empty;
_output = string.Empty;
try
{
var result = await OllamaService.Chat(Settings.StyleModel, _inputText, _systemPrompt, _temperature);
_output = result;
}
catch (Exception ex)
{
_error = $"Fehler bei der Generierung: {ex.Message}";
}
finally
{
_isBusy = false;
}
}
private async Task CopyOutput()
{
if (string.IsNullOrEmpty(_output)) return;
await JS.InvokeVoidAsync("navigator.clipboard.writeText", _output);
}
private void ClearOutput()
{
_output = string.Empty;
_error = string.Empty;
}
}

View file

@ -14,33 +14,6 @@
<div class="settings-page">
@if (_isAdmin)
{
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.user"]</h3>
<div class="form-group">
<label for="userSelect">@Loc["settings.user.label.active"]</label>
<select id="userSelect" value="@selectedUserId" @onchange="SwitchUser">
@foreach (var u in users)
{
<option value="@u.Id">@u.Name (@(u.Role == AuthorBuddy.Web.Data.UserRole.Administrator ? Loc["settings.user.role.admin"] : Loc["settings.user.role.author"]))</option>
}
</select>
</div>
<div class="form-group">
<label for="newUserName">@Loc["settings.user.label.new"]</label>
<div style="display: flex; gap: 8px;">
<input id="newUserName" @bind="newUserName" placeholder="@Loc["settings.user.placeholder.name"]" style="flex: 1;" />
<button class="btn btn-secondary" @onclick="AddUser">@Loc["settings.user.button.add"]</button>
</div>
</div>
</div>
}
<div class="settings-section" style="margin-bottom:16px;">
<button class="btn btn-secondary" @onclick='() => Navigation.NavigateTo("/setup-2fa")'>@Loc["settings.user.link.2fa"]</button>
</div>
<div class="settings-context">
@if (currentProjectName != null)
{
@ -52,11 +25,44 @@
}
</div>
<div class="settings-tabs">
<button class="settings-tab @(_activePromptTab == "novel" ? "active" : "")" @onclick="SwitchToNovelTab">
Roman
</button>
<button class="settings-tab @(_activePromptTab == "technical" ? "active" : "")" @onclick="SwitchToTechnicalTab">
Technische Vorlage (Requirements)
</button>
</div>
<div class="settings-layout">
<div class="settings-column" >
<div class="settings-column">
@if (_isAdmin)
{
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.user"]</h3>
<div class="form-group">
<label for="userSelect">@Loc["settings.user.label.active"]</label>
<select id="userSelect" value="@selectedUserId" @onchange="SwitchUser">
@foreach (var u in users)
{
<option value="@u.Id">@u.Name (@(u.IsAdministrator ? Loc["settings.user.role.admin"] : Loc["settings.user.role.author"]))</option>
}
</select>
</div>
<div class="form-group">
<label for="newUserName">@Loc["settings.user.label.new"]</label>
<div style="display: flex; gap: 8px;">
<input id="newUserName" @bind="newUserName" placeholder="@Loc["settings.user.placeholder.name"]" style="flex: 1;" />
<button class="btn btn-secondary" @onclick="AddUser">@Loc["settings.user.button.add"]</button>
</div>
</div>
</div>
<div class="settings-section" style="margin-bottom:16px;">
<button class="btn btn-secondary" @onclick='() => Navigation.NavigateTo("/setup-2fa")'>@Loc["settings.user.link.2fa"]</button>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.models"]</h3>
<div class="form-group">
@ -171,201 +177,196 @@
</div>
</div>
@if (currentProjectTemplate == null || currentProjectTemplate == ProjectTemplate.Novel)
@if (_activePromptTab == "novel")
{
<div class="settings-column">
<div class="settings-column settings-column-wide">
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.editor_prompt"]</h3>
<div class="form-group">
<textarea id="lektorPrompt" @bind="lektorPrompt" rows="6"></textarea>
<div class="form-group">
<textarea id="lektorPrompt" @bind="lektorPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@lektorTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(lektorTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => lektorTemp = v))" />
<div class="temp-category">@GetTempCategory(lektorTemp)</div>
<div class="temp-desc">@GetTempDescription(lektorTemp)</div>
</div>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@lektorTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(lektorTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => lektorTemp = v))" />
<div class="temp-category">@GetTempCategory(lektorTemp)</div>
<div class="temp-desc">@GetTempDescription(lektorTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.factcheck_template"]</h3>
<div class="form-group">
<textarea id="factCheckTemplate" @bind="factCheckTemplate" rows="6"></textarea>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.factcheck_template"]</h3>
<div class="form-group">
<textarea id="factCheckTemplate" @bind="factCheckTemplate" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@factCheckTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(factCheckTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => factCheckTemp = v))" />
<div class="temp-category">@GetTempCategory(factCheckTemp)</div>
<div class="temp-desc">@GetTempDescription(factCheckTemp)</div>
</div>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@factCheckTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(factCheckTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => factCheckTemp = v))" />
<div class="temp-category">@GetTempCategory(factCheckTemp)</div>
<div class="temp-desc">@GetTempDescription(factCheckTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.spellcheck_prompt"]</h3>
<div class="form-group">
<textarea id="spellingPrompt" @bind="spellingPrompt" rows="6"></textarea>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.spellcheck_prompt"]</h3>
<div class="form-group">
<textarea id="spellingPrompt" @bind="spellingPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@spellingTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(spellingTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => spellingTemp = v))" />
<div class="temp-category">@GetTempCategory(spellingTemp)</div>
<div class="temp-desc">@GetTempDescription(spellingTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.advocatus_diaboli"]</h3>
<div class="form-group">
<textarea id="advocatusDiaboliPrompt" @bind="advocatusDiaboliPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@advocatusDiaboliTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(advocatusDiaboliTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => advocatusDiaboliTemp = v))" />
<div class="temp-category">@GetTempCategory(advocatusDiaboliTemp)</div>
<div class="temp-desc">@GetTempDescription(advocatusDiaboliTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.hook_finder"]</h3>
<div class="form-group">
<textarea id="hookFinderPrompt" @bind="hookFinderPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@hookFinderTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(hookFinderTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => hookFinderTemp = v))" />
<div class="temp-category">@GetTempCategory(hookFinderTemp)</div>
<div class="temp-desc">@GetTempDescription(hookFinderTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.eli5"]</h3>
<div class="form-group">
<textarea id="eli5Prompt" @bind="eli5Prompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@eli5Temp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(eli5Temp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => eli5Temp = v))" />
<div class="temp-category">@GetTempCategory(eli5Temp)</div>
<div class="temp-desc">@GetTempDescription(eli5Temp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.coauthor_brainstorming"]</h3>
<div class="form-group">
<textarea id="coAuthorPrompt" @bind="coAuthorPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@coAuthorTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(coAuthorTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => coAuthorTemp = v))" />
<div class="temp-category">@GetTempCategory(coAuthorTemp)</div>
<div class="temp-desc">@GetTempDescription(coAuthorTemp)</div>
</div>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@spellingTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(spellingTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => spellingTemp = v))" />
<div class="temp-category">@GetTempCategory(spellingTemp)</div>
<div class="temp-desc">@GetTempDescription(spellingTemp)</div>
</div>
</div>
</div>
}
@if (currentProjectTemplate == null || currentProjectTemplate == ProjectTemplate.Novel)
@if (_activePromptTab == "technical")
{
<div class="settings-column">
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.advocatus_diaboli"]</h3>
<div class="form-group">
<textarea id="advocatusDiaboliPrompt" @bind="advocatusDiaboliPrompt" rows="6"></textarea>
<div class="settings-column settings-column-wide">
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_lastenheft"]</h3>
<div class="form-group">
<textarea id="techLastenheftPrompt" @bind="techLastenheftPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techLastenheftTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techLastenheftTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techLastenheftTemp = v))" />
<div class="temp-category">@GetTempCategory(techLastenheftTemp)</div>
<div class="temp-desc">@GetTempDescription(techLastenheftTemp)</div>
</div>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@advocatusDiaboliTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(advocatusDiaboliTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => advocatusDiaboliTemp = v))" />
<div class="temp-category">@GetTempCategory(advocatusDiaboliTemp)</div>
<div class="temp-desc">@GetTempDescription(advocatusDiaboliTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.hook_finder"]</h3>
<div class="form-group">
<textarea id="hookFinderPrompt" @bind="hookFinderPrompt" rows="6"></textarea>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_pflichtenheft"]</h3>
<div class="form-group">
<textarea id="techPflichtenheftPrompt" @bind="techPflichtenheftPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techPflichtenheftTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techPflichtenheftTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techPflichtenheftTemp = v))" />
<div class="temp-category">@GetTempCategory(techPflichtenheftTemp)</div>
<div class="temp-desc">@GetTempDescription(techPflichtenheftTemp)</div>
</div>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@hookFinderTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(hookFinderTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => hookFinderTemp = v))" />
<div class="temp-category">@GetTempCategory(hookFinderTemp)</div>
<div class="temp-desc">@GetTempDescription(hookFinderTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.eli5"]</h3>
<div class="form-group">
<textarea id="eli5Prompt" @bind="eli5Prompt" rows="6"></textarea>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_usecase"]</h3>
<div class="form-group">
<textarea id="techUseCasePrompt" @bind="techUseCasePrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techUseCaseTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techUseCaseTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techUseCaseTemp = v))" />
<div class="temp-category">@GetTempCategory(techUseCaseTemp)</div>
<div class="temp-desc">@GetTempDescription(techUseCaseTemp)</div>
</div>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@eli5Temp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(eli5Temp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => eli5Temp = v))" />
<div class="temp-category">@GetTempCategory(eli5Temp)</div>
<div class="temp-desc">@GetTempDescription(eli5Temp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.coauthor_brainstorming"]</h3>
<div class="form-group">
<textarea id="coAuthorPrompt" @bind="coAuthorPrompt" rows="6"></textarea>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_stakeholder"]</h3>
<div class="form-group">
<textarea id="techStakeholderPrompt" @bind="techStakeholderPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techStakeholderTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techStakeholderTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techStakeholderTemp = v))" />
<div class="temp-category">@GetTempCategory(techStakeholderTemp)</div>
<div class="temp-desc">@GetTempDescription(techStakeholderTemp)</div>
</div>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@coAuthorTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(coAuthorTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => coAuthorTemp = v))" />
<div class="temp-category">@GetTempCategory(coAuthorTemp)</div>
<div class="temp-desc">@GetTempDescription(coAuthorTemp)</div>
</div>
</div>
</div>
}
@if (currentProjectTemplate == null || currentProjectTemplate == ProjectTemplate.Technical)
{
<div class="settings-column">
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_lastenheft"]</h3>
<div class="form-group">
<textarea id="techLastenheftPrompt" @bind="techLastenheftPrompt" rows="6"></textarea>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_glossar"]</h3>
<div class="form-group">
<textarea id="techGlossarPrompt" @bind="techGlossarPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techGlossarTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techGlossarTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techGlossarTemp = v))" />
<div class="temp-category">@GetTempCategory(techGlossarTemp)</div>
<div class="temp-desc">@GetTempDescription(techGlossarTemp)</div>
</div>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techLastenheftTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techLastenheftTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techLastenheftTemp = v))" />
<div class="temp-category">@GetTempCategory(techLastenheftTemp)</div>
<div class="temp-desc">@GetTempDescription(techLastenheftTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_pflichtenheft"]</h3>
<div class="form-group">
<textarea id="techPflichtenheftPrompt" @bind="techPflichtenheftPrompt" rows="6"></textarea>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_qualitaet"]</h3>
<div class="form-group">
<textarea id="techQualitaetPrompt" @bind="techQualitaetPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techQualitaetTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techQualitaetTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techQualitaetTemp = v))" />
<div class="temp-category">@GetTempCategory(techQualitaetTemp)</div>
<div class="temp-desc">@GetTempDescription(techQualitaetTemp)</div>
</div>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techPflichtenheftTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techPflichtenheftTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techPflichtenheftTemp = v))" />
<div class="temp-category">@GetTempCategory(techPflichtenheftTemp)</div>
<div class="temp-desc">@GetTempDescription(techPflichtenheftTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_usecase"]</h3>
<div class="form-group">
<textarea id="techUseCasePrompt" @bind="techUseCasePrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techUseCaseTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techUseCaseTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techUseCaseTemp = v))" />
<div class="temp-category">@GetTempCategory(techUseCaseTemp)</div>
<div class="temp-desc">@GetTempDescription(techUseCaseTemp)</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_risiko"]</h3>
<div class="form-group">
<textarea id="techRisikoPrompt" @bind="techRisikoPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techRisikoTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techRisikoTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techRisikoTemp = v))" />
<div class="temp-category">@GetTempCategory(techRisikoTemp)</div>
<div class="temp-desc">@GetTempDescription(techRisikoTemp)</div>
</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_stakeholder"]</h3>
<div class="form-group">
<textarea id="techStakeholderPrompt" @bind="techStakeholderPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techStakeholderTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techStakeholderTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techStakeholderTemp = v))" />
<div class="temp-category">@GetTempCategory(techStakeholderTemp)</div>
<div class="temp-desc">@GetTempDescription(techStakeholderTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_glossar"]</h3>
<div class="form-group">
<textarea id="techGlossarPrompt" @bind="techGlossarPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techGlossarTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techGlossarTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techGlossarTemp = v))" />
<div class="temp-category">@GetTempCategory(techGlossarTemp)</div>
<div class="temp-desc">@GetTempDescription(techGlossarTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_qualitaet"]</h3>
<div class="form-group">
<textarea id="techQualitaetPrompt" @bind="techQualitaetPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techQualitaetTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techQualitaetTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techQualitaetTemp = v))" />
<div class="temp-category">@GetTempCategory(techQualitaetTemp)</div>
<div class="temp-desc">@GetTempDescription(techQualitaetTemp)</div>
</div>
</div>
<div class="settings-section">
<h3 class="settings-section-title">@Loc["settings.section.tech_risiko"]</h3>
<div class="form-group">
<textarea id="techRisikoPrompt" @bind="techRisikoPrompt" rows="6"></textarea>
</div>
<div class="temp-slider">
<label class="temp-label">@Loc["settings.temperature.label"] <span class="temp-value">@techRisikoTemp.ToString("F1")</span></label>
<input type="range" min="0" max="2" step="0.1" value="@Fmt(techRisikoTemp)" @oninput="@((ChangeEventArgs e) => SetTemp(e, v => techRisikoTemp = v))" />
<div class="temp-category">@GetTempCategory(techRisikoTemp)</div>
<div class="temp-desc">@GetTempDescription(techRisikoTemp)</div>
</div>
</div>
</div>
}
</div>
@ -381,6 +382,10 @@
</div>
@code {
private string _activePromptTab = "novel";
private void SwitchToNovelTab() => _activePromptTab = "novel";
private void SwitchToTechnicalTab() => _activePromptTab = "technical";
private string ragModel = "llama3.2";
private string styleModel = "llama3.2";
private string spellingModel = "phi3:mini";
@ -436,7 +441,7 @@
protected override async Task OnInitializedAsync()
{
var currentUser = AuthService.CurrentUser;
_isAdmin = currentUser?.Role == UserRole.Administrator;
_isAdmin = currentUser?.IsAdministrator == true;
users = await SettingsService.GetUsersAsync();
var current = await SettingsService.GetCurrentUserAsync();
@ -482,6 +487,12 @@
hookFinderPrompt = await SettingsService.GetHookFinderPromptAsync() ?? string.Empty;
eli5Prompt = await SettingsService.GetEli5PromptAsync() ?? string.Empty;
coAuthorPrompt = await SettingsService.GetCoAuthorBrainstormingPromptAsync() ?? string.Empty;
lektorTemp = await SettingsService.GetLektorTempAsync();
factCheckTemp = await SettingsService.GetFactCheckTempAsync();
spellingTemp = await SettingsService.GetSpellingTempAsync();
advocatusDiaboliTemp = await SettingsService.GetAdvocatusDiaboliTempAsync();
hookFinderTemp = await SettingsService.GetHookFinderTempAsync();
eli5Temp = await SettingsService.GetEli5TempAsync();
coAuthorTemp = await SettingsService.GetCoAuthorTempAsync();
techLastenheftPrompt = await SettingsService.GetTechLastenheftPromptAsync() ?? string.Empty;
techPflichtenheftPrompt = await SettingsService.GetTechPflichtenheftPromptAsync() ?? string.Empty;
@ -674,4 +685,4 @@
_diagnosticsResults = results;
_isDiagnosing = false;
}
}
}

View file

@ -1,21 +1,28 @@
@page "/user-management"
@attribute [Authorize]
@attribute [Authorize(Roles = "Administrator")]
@inject IAuthService AuthService
@inject IEmailService EmailService
@inject ILocalizationService Loc
@inject NavigationManager Navigation
@rendermode InteractiveServer
<PageTitle>@Loc["usermanagement.title"] - Author Buddy</PageTitle>
<div class="usermanagement-page">
<h3>@Loc["usermanagement.title"]</h3>
<div class="usermanagement-header">
<h3>@Loc["usermanagement.title"]</h3>
<button class="btn btn-primary" @onclick="ShowCreateDialog">+ Neuen Benutzer</button>
</div>
<table class="usermanagement-table">
<thead>
<tr>
<th>@Loc["usermanagement.col.name"]</th>
<th>@Loc["usermanagement.col.role"]</th>
<th>@Loc["usermanagement.col.status"]</th>
<th>@Loc["usermanagement.col.actions"]</th>
<th>Name</th>
<th>E-Mail</th>
<th>Rolle</th>
<th>Status</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
@ -23,33 +30,35 @@
{
<tr class="@(u.Id == currentUserId ? "usermanagement-current" : "") @(!u.IsActive ? "usermanagement-inactive" : "")">
<td>@u.Name @(u.Id == currentUserId ? "(Du)" : "")</td>
<td>@(u.Role == UserRole.Administrator ? Loc["settings.user.role.admin"] : Loc["settings.user.role.author"])</td>
<td>@(!string.IsNullOrEmpty(u.Email) ? u.Email : "—")</td>
<td>@string.Join(", ", u.Roles.Select(RoleName))</td>
<td>
@if (u.IsActive)
{
<span class="usermanagement-status-active">@Loc["usermanagement.status.active"]</span>
<span class="usermanagement-status-active">Aktiv</span>
}
else
{
<span class="usermanagement-status-inactive">@Loc["usermanagement.status.inactive"]</span>
<span class="usermanagement-status-inactive">Inaktiv</span>
}
</td>
<td class="usermanagement-actions">
<button class="btn btn-sm" @onclick="() => BeginEdit(u)" title="Bearbeiten">✎</button>
@if (!string.IsNullOrEmpty(u.Email) && EmailService.IsConfigured)
{
<button class="btn btn-sm" @onclick="() => SendRecovery(u)" title="Passwort zurücksetzen per E-Mail">📧</button>
}
@if (u.Id != currentUserId)
{
@if (u.IsActive)
{
<button class="btn btn-sm" @onclick="() => DeactivateUser(u.Id)" title="@Loc["usermanagement.action.deactivate"]">@Loc["usermanagement.action.deactivate"]</button>
<button class="btn btn-sm" @onclick="() => DeactivateUser(u.Id)" title="Deaktivieren">🔴</button>
}
else
{
<button class="btn btn-sm btn-primary" @onclick="() => ActivateUser(u.Id)" title="@Loc["usermanagement.action.activate"]">@Loc["usermanagement.action.activate"]</button>
<button class="btn btn-sm btn-primary" @onclick="() => ActivateUser(u.Id)" title="Aktivieren">🟢</button>
}
<button class="btn btn-sm btn-danger" @onclick="() => DeleteUser(u.Id)" title="@Loc["usermanagement.action.delete"]">@Loc["usermanagement.action.delete"]</button>
}
else
{
<span style="font-size:11px;color:var(--border-color);">@Loc["usermanagement.cannot_self"]</span>
<button class="btn btn-sm btn-danger" @onclick="() => BeginDelete(u)" title="Löschen">🗑</button>
}
</td>
</tr>
@ -61,15 +70,134 @@
{
<div class="usermanagement-status">@statusMessage</div>
}
<button class="btn btn-secondary" @onclick='() => Navigation.NavigateTo("/settings")' style="margin-top:12px;">@Loc["common.back"]</button>
</div>
@* Create User Dialog *@
@if (showCreateDialog)
{
<div class="modal-overlay">
<div class="modal-dialog">
<div class="modal-title">Neuen Benutzer anlegen</div>
<div class="modal-body">
<div class="form-group">
<label>Benutzername</label>
<input @bind="_newUsername" />
</div>
<div class="form-group">
<label>E-Mail</label>
<input @bind="_newEmail" type="email" />
</div>
<div class="form-group">
<label>Rollen</label>
<div class="um-role-checkboxes">
@foreach (var (role, label) in UmRoleOptions)
{
<label><input type="checkbox" checked="@_newRoles.Contains(role)" @onchange="() => ToggleNewRole(role)" /> @label</label>
}
</div>
</div>
<div class="form-group">
<label>Passwort</label>
<div style="display:flex;gap:8px;">
<input @bind="_newPassword" type="password" style="flex:1;" />
<button class="btn btn-sm" @onclick="GeneratePassword" type="button">Generieren</button>
</div>
@if (!string.IsNullOrEmpty(_generatedPassword))
{
<small style="color:#c00;font-family:monospace;">@_generatedPassword</small>
}
</div>
</div>
<div class="modal-actions">
<button @onclick="CancelCreate">Abbrechen</button>
<button @onclick="CreateUser" class="primary" disabled="@(string.IsNullOrWhiteSpace(_newUsername) || string.IsNullOrWhiteSpace(_newPassword))">Anlegen</button>
</div>
</div>
</div>
}
@* Edit User Dialog *@
@if (showEditDialog && _editUser != null)
{
<div class="modal-overlay">
<div class="modal-dialog">
<div class="modal-title">Benutzer bearbeiten @_editUser.Name</div>
<div class="modal-body">
<div class="form-group">
<label>E-Mail</label>
<input @bind="_editEmail" type="email" />
</div>
<div class="form-group">
<label>Rollen</label>
<div class="um-role-checkboxes">
@foreach (var (role, label) in UmRoleOptions)
{
<label><input type="checkbox" checked="@_editRoles.Contains(role)" @onchange="() => ToggleEditRole(role)" /> @label</label>
}
</div>
</div>
</div>
<div class="modal-actions">
<button @onclick="CancelEdit">Abbrechen</button>
<button @onclick="SaveEdit" class="primary">Speichern</button>
</div>
</div>
</div>
}
@* Delete Confirmation Dialog *@
@if (showDeleteDialog && _deleteUser != null)
{
<div class="modal-overlay">
<div class="modal-dialog">
<div class="modal-title">Benutzer löschen</div>
<div class="modal-body">
<p>Soll der Benutzer <strong>@_deleteUser.Name</strong> wirklich gelöscht werden?</p>
</div>
<div class="modal-actions">
<button @onclick="CancelDelete">Abbrechen</button>
<button @onclick="ConfirmDelete" class="btn-danger">Löschen</button>
</div>
</div>
</div>
}
@code {
private List<UserEntity> users = new();
private int currentUserId;
private string statusMessage = string.Empty;
private bool showCreateDialog;
private string _newUsername = string.Empty;
private string _newEmail = string.Empty;
private List<string> _newRoles = new() { "Author" };
private string _newPassword = string.Empty;
private string? _generatedPassword;
private bool showEditDialog;
private UserEntity? _editUser;
private string _editEmail = string.Empty;
private List<string> _editRoles = new();
private bool showDeleteDialog;
private UserEntity? _deleteUser;
private static (string role, string label)[] UmRoleOptions => new[]
{
("Author", "Autor"),
("Blogger", "Blogger"),
("Chat", "Chat"),
("Administrator", "Administrator")
};
private string RoleName(UserRole role) => role switch
{
UserRole.Administrator => "Administrator",
UserRole.Blogger => "Blogger",
UserRole.Chat => "Chat",
_ => "Autor"
};
protected override async Task OnInitializedAsync()
{
await LoadUsers();
@ -84,21 +212,162 @@
private async Task ActivateUser(int userId)
{
var ok = await AuthService.SetUserActiveAsync(userId, true);
statusMessage = ok ? Loc["usermanagement.status.activated"] : Loc["common.error"];
statusMessage = ok ? "Benutzer aktiviert." : "Fehler";
await LoadUsers();
}
private async Task DeactivateUser(int userId)
{
var ok = await AuthService.SetUserActiveAsync(userId, false);
statusMessage = ok ? Loc["usermanagement.status.deactivated"] : Loc["common.error"];
statusMessage = ok ? "Benutzer deaktiviert." : "Fehler";
await LoadUsers();
}
private async Task DeleteUser(int userId)
private void ShowCreateDialog()
{
var ok = await AuthService.DeleteUserAsync(userId);
statusMessage = ok ? Loc["usermanagement.status.deleted"] : Loc["common.error"];
_newUsername = string.Empty;
_newEmail = string.Empty;
_newRoles = new List<string> { "Author" };
_newPassword = string.Empty;
_generatedPassword = null;
showCreateDialog = true;
}
private void CancelCreate()
{
showCreateDialog = false;
}
private void ToggleNewRole(string role)
{
if (_newRoles.Contains(role))
_newRoles.Remove(role);
else
_newRoles.Add(role);
}
private void ToggleEditRole(string role)
{
if (_editRoles.Contains(role))
_editRoles.Remove(role);
else
_editRoles.Add(role);
}
private void GeneratePassword()
{
using var rng = System.Security.Cryptography.RandomNumberGenerator.Create();
var bytes = new byte[10];
rng.GetBytes(bytes);
_generatedPassword = Convert.ToBase64String(bytes).TrimEnd('=').Replace('+', 'X').Replace('/', 'Y');
_newPassword = _generatedPassword;
}
private async Task CreateUser()
{
if (string.IsNullOrWhiteSpace(_newUsername) || string.IsNullOrWhiteSpace(_newPassword))
return;
if (_newRoles.Count == 0)
{
statusMessage = "Mindestens eine Rolle erforderlich.";
return;
}
var roles = _newRoles.Select(r => r switch
{
"Administrator" => UserRole.Administrator,
"Blogger" => UserRole.Blogger,
"Chat" => UserRole.Chat,
_ => UserRole.Author
}).Distinct().ToList();
var ok = await AuthService.CreateUserAsync(_newUsername.Trim(), _newPassword, _newEmail.Trim(), roles);
statusMessage = ok
? $"Benutzer '{_newUsername}' angelegt."
: "Benutzername bereits vergeben oder maximal ein Admin erlaubt.";
showCreateDialog = false;
await LoadUsers();
}
private void BeginEdit(UserEntity user)
{
_editUser = user;
_editEmail = user.Email;
_editRoles = user.Roles.Select(r => r.ToString()).ToList();
showEditDialog = true;
}
private void CancelEdit()
{
showEditDialog = false;
_editUser = null;
}
private async Task SaveEdit()
{
if (_editUser == null) return;
if (_editRoles.Count == 0)
{
statusMessage = "Mindestens eine Rolle erforderlich.";
return;
}
var roles = _editRoles.Select(r => r switch
{
"Administrator" => UserRole.Administrator,
"Blogger" => UserRole.Blogger,
"Chat" => UserRole.Chat,
_ => UserRole.Author
}).Distinct().ToList();
string? email = _editEmail?.Trim();
if (string.IsNullOrEmpty(email)) email = null;
var ok = await AuthService.UpdateUserAsync(_editUser.Id, email, roles);
statusMessage = ok ? "Benutzer aktualisiert." : "Fehler (max. ein Admin erlaubt).";
showEditDialog = false;
_editUser = null;
await LoadUsers();
}
private void BeginDelete(UserEntity user)
{
_deleteUser = user;
showDeleteDialog = true;
}
private void CancelDelete()
{
showDeleteDialog = false;
_deleteUser = null;
}
private async Task ConfirmDelete()
{
if (_deleteUser == null) return;
var ok = await AuthService.DeleteUserAsync(_deleteUser.Id);
statusMessage = ok ? "Benutzer gelöscht." : "Fehler";
showDeleteDialog = false;
_deleteUser = null;
await LoadUsers();
}
private async Task SendRecovery(UserEntity user)
{
if (string.IsNullOrEmpty(user.Email)) return;
var newPassword = await AuthService.ResetPasswordAsync(user.Id);
if (string.IsNullOrEmpty(newPassword))
{
statusMessage = "Fehler beim Zurücksetzen des Passworts.";
return;
}
var sent = await EmailService.SendPasswordAsync(user.Email, user.Name, newPassword);
statusMessage = sent
? $"Neues Passwort an {user.Email} gesendet."
: "E-Mail konnte nicht gesendet werden (SMTP-Konfiguration prüfen).";
}
}

11
Data/ChatEntity.cs Normal file
View file

@ -0,0 +1,11 @@
namespace AuthorBuddy.Web.Data;
public class ChatEntity
{
public int Id { get; set; }
public int UserId { get; set; }
public string UserMessage { get; set; } = string.Empty;
public string AssistantResponse { get; set; } = string.Empty;
public string Model { get; set; } = string.Empty;
public DateTime Timestamp { get; set; } = DateTime.UtcNow;
}

View file

@ -1,18 +1,30 @@
using LiteDB;
namespace AuthorBuddy.Web.Data;
public enum UserRole
{
Author,
Administrator
Administrator,
Blogger,
Chat
}
public class UserEntity
{
public int Id { get; set; }
public string Name { get; set; } = string.Empty;
public string Email { get; set; } = string.Empty;
public string PasswordHash { get; set; } = string.Empty;
public string TwoFactorSecret { get; set; } = string.Empty;
public bool TwoFactorEnabled { get; set; }
public UserRole Role { get; set; } = UserRole.Author;
public List<UserRole> Roles { get; set; } = new() { UserRole.Author };
public bool IsActive { get; set; } = true;
[BsonIgnore]
public bool IsAdministrator => Roles.Contains(UserRole.Administrator);
public bool HasRole(UserRole role) => Roles.Contains(role);
public bool HasAnyRole(params UserRole[] roles) => roles.Any(HasRole);
}

View file

@ -99,6 +99,20 @@ public class OllamaSettings
public double TechQualitaetTemp { get; set; } = 0.3;
public double TechRisikoTemp { get; set; } = 0.5;
public string QuickMarkdownPrompt { get; set; } = "Du bist ein Markdown-Profi. Formatiere den folgenden Text als sauberes, gut strukturiertes Markdown. Verwende Überschriften (##, ###), Listen, Tabellen, Code-Blöcke, fette/kursive Hervorhebungen und Zitate wo sinnvoll. Achte auf Lesbarkeit und eine logische Gliederung.";
public string QuickMarkdownPromptEn { get; } = "You are a Markdown professional. Format the following text as clean, well-structured Markdown. Use headings (##, ###), lists, tables, code blocks, bold/italic emphasis and quotes where appropriate. Ensure readability and logical structure.";
public double QuickMarkdownTemp { get; set; } = 0.3;
public string ChatPrompt { get; set; } = "Du bist ein hilfreicher Assistent. Antworte freundlich und präzise auf Deutsch.";
public string ChatPromptEn { get; } = "You are a helpful assistant. Answer friendly and precisely in English.";
public double ChatTemp { get; set; } = 0.7;
public string GetChatDefaultPrompt(string currentLanguage) => GetDefaultPrompt(ChatPrompt, ChatPromptEn, currentLanguage);
public string GetQuickMarkdownDefaultPrompt(string currentLanguage) => GetDefaultPrompt(QuickMarkdownPrompt, QuickMarkdownPromptEn, currentLanguage);
public string GetDefaultPrompt(string dePrompt, string enPrompt, string currentLanguage)
{
return currentLanguage == "en" ? enPrompt : dePrompt;

13
Models/SmtpSettings.cs Normal file
View file

@ -0,0 +1,13 @@
namespace AuthorBuddy.Web.Models;
public class SmtpSettings
{
public string Host { get; set; } = string.Empty;
public int Port { get; set; } = 587;
public string Username { get; set; } = string.Empty;
public string Password { get; set; } = string.Empty;
public string FromAddress { get; set; } = string.Empty;
public string FromName { get; set; } = "Author Buddy";
public bool UseSsl { get; set; } = true;
public bool Enabled { get; set; }
}

View file

@ -1,4 +1,5 @@
using AuthorBuddy.Web.Components;
using AuthorBuddy.Web.Data;
using AuthorBuddy.Web.Models;
using AuthorBuddy.Web.Services;
using LiteDB;
@ -20,7 +21,9 @@ var dbDir = OperatingSystem.IsLinux()
: AppContext.BaseDirectory;
Directory.CreateDirectory(dbDir);
var dbPath = Path.Combine(dbDir, Constants.DbFile);
builder.Services.AddSingleton<ILiteDatabase>(new LiteDatabase($"Filename={dbPath};Connection=direct"));
var db = new LiteDatabase($"Filename={dbPath};Connection=direct");
MigrateUserRoles(db);
builder.Services.AddSingleton<ILiteDatabase>(db);
builder.Services.AddSingleton<OllamaSettings>();
builder.Services.AddSingleton<IOllamaService, OllamaService>();
@ -37,6 +40,12 @@ builder.Services.AddSingleton<IWikipediaService, WikipediaService>();
builder.Services.AddSingleton<OperationState>();
builder.Services.AddSingleton<IDbAdminService, DbAdminService>();
builder.Services.AddScoped<IAuthService, AuthService>();
builder.Services.AddSingleton<IEmailService, EmailService>();
builder.Services.AddSingleton<IChatService, ChatService>();
var smtpSettings = new SmtpSettings();
builder.Configuration.GetSection("Smtp").Bind(smtpSettings);
builder.Services.AddSingleton(smtpSettings);
builder.Services.AddScoped<ILocalizationService, LocalizationService>();
builder.Services.AddScoped<CustomAuthStateProvider>();
builder.Services.AddScoped<AuthenticationStateProvider>(sp =>
@ -80,7 +89,23 @@ app.MapRazorComponents<App>()
app.Run();
//public partial class Program
//{
// public const bool DebugAutoLogin = true;
//}
static void MigrateUserRoles(ILiteDatabase db)
{
var col = db.GetCollection("users");
var docs = col.FindAll().ToList();
foreach (var doc in docs)
{
if (doc.ContainsKey("Role") && !doc.ContainsKey("Roles"))
{
var oldRole = (Int32)UserRole.Administrator;
doc.Remove("Role");
doc["Roles"] = new BsonArray { new BsonValue(oldRole) };
col.Update(doc);
}
else if (!doc.ContainsKey("Roles") && !doc.ContainsKey("Role"))
{
doc["Roles"] = new BsonArray { new BsonValue(0) };
col.Update(doc);
}
}
}

View file

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<DeleteExistingFiles>false</DeleteExistingFiles>
<ExcludeApp_Data>false</ExcludeApp_Data>
<LaunchSiteAfterPublish>true</LaunchSiteAfterPublish>
<LastUsedBuildConfiguration>Release</LastUsedBuildConfiguration>
<LastUsedPlatform>Any CPU</LastUsedPlatform>
<PublishProvider>FileSystem</PublishProvider>
<PublishUrl>bin\Release\net10.0\publish\</PublishUrl>
<WebPublishMethod>FileSystem</WebPublishMethod>
<_TargetId>Folder</_TargetId>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- https://go.microsoft.com/fwlink/?LinkID=208121. -->
<Project>
<PropertyGroup>
<_PublishTargetUrl>E:\ollama_net\ollama\AuthorBuddy.Web\bin\Release\net10.0\publish\</_PublishTargetUrl>
<History>True|2026-06-14T06:09:44.9432726Z||;</History>
<LastFailureDetails />
</PropertyGroup>
</Project>

View file

@ -289,5 +289,6 @@
"export.label.format": "Format",
"export.format.markdown": "Markdown (Einzeldatei)",
"export.format.zip": "Markdown (ZIP-Archiv)",
"export.button.export": "Exportieren"
"export.button.export": "Exportieren",
"nav.menu.chat": "Chat Tools"
}

View file

@ -289,5 +289,6 @@
"export.label.format": "Format",
"export.format.markdown": "Markdown (single file)",
"export.format.zip": "Markdown (ZIP archive)",
"export.button.export": "Export"
"export.button.export": "Export",
"nav.menu.chat": "Chat Tools"
}

View file

@ -77,7 +77,7 @@ public class AuthService : IAuthService
PasswordHash = TotpHelper.HashPassword(password),
TwoFactorEnabled = false,
TwoFactorSecret = string.Empty,
Role = hasUsers ? UserRole.Author : UserRole.Administrator
Roles = hasUsers ? new List<UserRole> { UserRole.Author } : new List<UserRole> { UserRole.Administrator }
};
col.Insert(user);
await SetAuthenticatedUser(user);
@ -207,4 +207,80 @@ public class AuthService : IAuthService
col.Delete(userId);
return true;
}
public async Task<bool> CreateUserAsync(string username, string password, string email, List<UserRole> roles)
{
var col = _db.GetCollection<UserEntity>("users");
var existing = col.FindOne(u => u.Name == username);
if (existing != null) return false;
if (roles.Contains(UserRole.Administrator) && AdminCount() >= 1)
return false;
var user = new UserEntity
{
Name = username,
Email = email,
PasswordHash = TotpHelper.HashPassword(password),
Roles = roles,
TwoFactorEnabled = false,
TwoFactorSecret = string.Empty,
IsActive = true
};
col.Insert(user);
await Task.CompletedTask;
return true;
}
public async Task<bool> UpdateUserAsync(int userId, string? email, List<UserRole>? roles)
{
var col = _db.GetCollection<UserEntity>("users");
var user = col.FindById(userId);
if (user == null) return false;
if (email != null) user.Email = email;
if (roles != null)
{
var currentlyAdmin = user.Roles.Contains(UserRole.Administrator);
var willBeAdmin = roles.Contains(UserRole.Administrator);
if (willBeAdmin && !currentlyAdmin && AdminCount() >= 1)
return false;
if (!willBeAdmin && currentlyAdmin && AdminCount(userId) == 0)
return false;
user.Roles = roles;
}
col.Update(user);
await Task.CompletedTask;
return true;
}
public async Task<string> ResetPasswordAsync(int userId)
{
var col = _db.GetCollection<UserEntity>("users");
var user = col.FindById(userId);
if (user == null) return string.Empty;
using var rng = System.Security.Cryptography.RandomNumberGenerator.Create();
var bytes = new byte[10];
rng.GetBytes(bytes);
var newPassword = Convert.ToBase64String(bytes).TrimEnd('=').Replace('+', 'X').Replace('/', 'Y');
user.PasswordHash = TotpHelper.HashPassword(newPassword);
col.Update(user);
await Task.CompletedTask;
return newPassword;
}
private int AdminCount(int? excludeUserId = null)
{
var col = _db.GetCollection<UserEntity>("users");
return col.FindAll().Count(u =>
u.Roles.Contains(UserRole.Administrator) &&
(!excludeUserId.HasValue || u.Id != excludeUserId.Value));
}
}

38
Services/ChatService.cs Normal file
View file

@ -0,0 +1,38 @@
using AuthorBuddy.Web.Data;
using LiteDB;
namespace AuthorBuddy.Web.Services;
public class ChatService : IChatService
{
private readonly ILiteDatabase _db;
public ChatService(ILiteDatabase db)
{
_db = db;
}
public Task<List<ChatEntity>> GetHistoryAsync(int userId)
{
var col = _db.GetCollection<ChatEntity>("chats");
return Task.FromResult(col.Find(c => c.UserId == userId)
.OrderByDescending(c => c.Timestamp)
.Take(100)
.Reverse()
.ToList());
}
public Task AddMessageAsync(ChatEntity message)
{
var col = _db.GetCollection<ChatEntity>("chats");
col.Insert(message);
return Task.CompletedTask;
}
public Task ClearHistoryAsync(int userId)
{
var col = _db.GetCollection<ChatEntity>("chats");
col.DeleteMany(c => c.UserId == userId);
return Task.CompletedTask;
}
}

View file

@ -19,11 +19,15 @@ public class CustomAuthStateProvider : AuthenticationStateProvider
var user = await _authService.GetCurrentUserAsync();
if (user != null)
{
var identity = new ClaimsIdentity(new[]
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, user.Name),
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
}, "custom");
};
foreach (var role in user.Roles)
claims.Add(new Claim(ClaimTypes.Role, role.ToString()));
var identity = new ClaimsIdentity(claims, "custom");
return new AuthenticationState(new ClaimsPrincipal(identity));
}
return _anonymous;

55
Services/EmailService.cs Normal file
View file

@ -0,0 +1,55 @@
using System.Net;
using System.Net.Mail;
using AuthorBuddy.Web.Models;
namespace AuthorBuddy.Web.Services;
public class EmailService : IEmailService
{
private readonly SmtpSettings _settings;
public EmailService(SmtpSettings settings)
{
_settings = settings;
}
public bool IsConfigured => _settings.Enabled
&& !string.IsNullOrEmpty(_settings.Host)
&& !string.IsNullOrEmpty(_settings.FromAddress);
public async Task<bool> SendPasswordAsync(string recipientEmail, string recipientName, string newPassword)
{
if (!IsConfigured) return false;
try
{
using var client = new SmtpClient(_settings.Host, _settings.Port)
{
EnableSsl = _settings.UseSsl,
Credentials = string.IsNullOrEmpty(_settings.Username)
? null
: new NetworkCredential(_settings.Username, _settings.Password)
};
using var msg = new MailMessage
{
From = new MailAddress(_settings.FromAddress, _settings.FromName),
Subject = "Ihr Author-Buddy-Passwort",
Body = $"Hallo {recipientName},\n\n"
+ $"Ihr neues Passwort für Author Buddy lautet:\n\n"
+ $" {newPassword}\n\n"
+ $"Bitte ändern Sie es nach dem Login.\n\n"
+ $"Mit freundlichen Grüßen\nIhr Author-Buddy-Team",
IsBodyHtml = false
};
msg.To.Add(recipientEmail);
await client.SendMailAsync(msg);
return true;
}
catch
{
return false;
}
}
}

View file

@ -21,5 +21,8 @@ public interface IAuthService
Task<List<UserEntity>> GetAllUsersAsync();
Task<bool> SetUserActiveAsync(int userId, bool active);
Task<bool> DeleteUserAsync(int userId);
Task<bool> CreateUserAsync(string username, string password, string email, List<UserRole> roles);
Task<bool> UpdateUserAsync(int userId, string? email, List<UserRole>? roles);
Task<string> ResetPasswordAsync(int userId);
UserEntity? CurrentUser { get; }
}

10
Services/IChatService.cs Normal file
View file

@ -0,0 +1,10 @@
using AuthorBuddy.Web.Data;
namespace AuthorBuddy.Web.Services;
public interface IChatService
{
Task<List<ChatEntity>> GetHistoryAsync(int userId);
Task AddMessageAsync(ChatEntity message);
Task ClearHistoryAsync(int userId);
}

View file

@ -0,0 +1,7 @@
namespace AuthorBuddy.Web.Services;
public interface IEmailService
{
Task<bool> SendPasswordAsync(string recipientEmail, string recipientName, string newPassword);
bool IsConfigured { get; }
}

View file

@ -77,6 +77,16 @@ public interface ISettingsService
Task SaveTechGlossarTempAsync(double temp);
Task SaveTechQualitaetTempAsync(double temp);
Task SaveTechRisikoTempAsync(double temp);
Task<string?> GetQuickMarkdownPromptAsync();
Task SaveQuickMarkdownPromptAsync(string prompt);
Task<double> GetQuickMarkdownTempAsync();
Task SaveQuickMarkdownTempAsync(double temp);
Task<string?> GetChatPromptAsync();
Task SaveChatPromptAsync(string prompt);
Task<double> GetChatTempAsync();
Task SaveChatTempAsync(double temp);
Task<string?> GetLanguageAsync();
Task SaveLanguageAsync(string language);

View file

@ -1,3 +1,4 @@
using System.Diagnostics;
using AuthorBuddy.Web.Models;
namespace AuthorBuddy.Web.Services;
@ -7,6 +8,8 @@ public class OllamaService : IOllamaService
private readonly IEnumerable<ILLMBackend> _backends;
private readonly OllamaSettings _settings;
private readonly OperationState _opState;
private readonly object _startLock = new();
private bool _startAttempted;
public OllamaService(IEnumerable<ILLMBackend> backends, OllamaSettings settings, OperationState opState)
{
@ -18,6 +21,53 @@ public class OllamaService : IOllamaService
private ILLMBackend ActiveBackend =>
_backends.FirstOrDefault(b => b.Name == _settings.Backend) ?? _backends.First();
private async Task EnsureRunningAsync()
{
if (ActiveBackend.Name != "Ollama")
return;
var reachable = await ActiveBackend.TestConnectionAsync();
if (reachable)
return;
lock (_startLock)
{
if (_startAttempted)
return;
_startAttempted = true;
}
_opState.SetThought("Ollama läuft nicht starte ollama serve...");
try
{
var psi = new ProcessStartInfo("ollama", "serve")
{
CreateNoWindow = true,
UseShellExecute = false
};
Process.Start(psi);
}
catch (Exception ex)
{
_opState.AppendThought($" Fehler: {ex.Message}");
return;
}
for (int i = 0; i < 30; i++)
{
await Task.Delay(1000);
reachable = await ActiveBackend.TestConnectionAsync();
if (reachable)
{
_opState.AppendThought(" Ollama ist bereit");
return;
}
_opState.AppendThought(".");
}
_opState.AppendThought(" Zeitüberschreitung");
}
public void Reconfigure(string url)
{
_settings.OllamaUrl = url;
@ -25,8 +75,9 @@ public class OllamaService : IOllamaService
public async Task<(bool Success, long LatencyMs)> TestConnectionAsync()
{
await EnsureRunningAsync();
_opState.SetThought($"Teste Verbindung zu {ActiveBackend.Name}...");
var sw = System.Diagnostics.Stopwatch.StartNew();
var sw = Stopwatch.StartNew();
var result = await ActiveBackend.TestConnectionAsync();
sw.Stop();
_opState.AppendThought(result ? " OK" : " Fehlgeschlagen");
@ -35,6 +86,7 @@ public class OllamaService : IOllamaService
public async Task<IEnumerable<LLM_Model>> GetLocalModelsAsync()
{
await EnsureRunningAsync();
_opState.SetThought($"Lade Modelle von {ActiveBackend.Name}...");
var models = await ActiveBackend.GetModelsAsync();
_opState.AppendThought($" {models.Count()} gefunden");
@ -43,47 +95,56 @@ public class OllamaService : IOllamaService
public async Task<bool> TestEmbeddingAsync(string model)
{
await EnsureRunningAsync();
return ActiveBackend.TestEmbedding(model);
}
public async Task<float[]> GetEmbeddingAsync(string text)
{
await EnsureRunningAsync();
return await ActiveBackend.GetEmbeddingAsync(text, _settings.EmbedModel);
}
public async Task<string> QuickSpellCheckAsync(string text)
{
await EnsureRunningAsync();
return await Chat(_settings.SpellingModel, text, _settings.SpellingPrompt, _settings.SpellingTemp);
}
public async Task<string> StreamStyleCheckAsync(string text)
{
await EnsureRunningAsync();
return await Chat(_settings.StyleModel, text, _settings.LektorSystemPrompt, _settings.LektorTemp);
}
public async Task<string> AnswerBasedOnFactsAsync(string question, string facts)
{
await EnsureRunningAsync();
var prompt = _settings.GetFactCheckPrompt(question, facts);
return await Chat(_settings.EmbedModel, prompt, temperature: _settings.FactCheckTemp);
}
public async Task<bool> ModelExistsAsync(string model)
{
await EnsureRunningAsync();
return await ActiveBackend.ModelExistsAsync(model);
}
public async Task<(bool Success, int ContextWindow)> GetModelContextWindowAsync(string model)
{
await EnsureRunningAsync();
return await ActiveBackend.GetModelContextWindowAsync(model);
}
public async Task<long> MeasureLatencyAsync(string model)
{
await EnsureRunningAsync();
return await ActiveBackend.MeasureLatencyAsync(model);
}
public async Task<string> Chat(string model, string userMessage, string? systemPrompt = null, double? temperature = null)
{
await EnsureRunningAsync();
_opState.SetThought($"{ActiveBackend.Name}: Sende Anfrage an {model}...");
var result = await ActiveBackend.ChatAsync(model, userMessage, systemPrompt, temperature);
_opState.AppendThought(" Fertig");
@ -92,6 +153,7 @@ public class OllamaService : IOllamaService
public async Task ChatStream(string model, string userMessage, Func<string, Task> onToken, string? systemPrompt = null, double? temperature = null)
{
await EnsureRunningAsync();
_opState.SetThought($"{ActiveBackend.Name}: Verbinde mit Modell {model}...");
int tokenCount = 0;
@ -107,6 +169,7 @@ public class OllamaService : IOllamaService
public async Task<bool> PullModelAsync(string modelName, IProgress<string> progress)
{
await EnsureRunningAsync();
_opState.SetThought($"Lade Modell {modelName}...");
var result = await ActiveBackend.PullModelAsync(modelName, progress);
_opState.AppendThought(result ? " Erfolgreich" : " Fehlgeschlagen");

View file

@ -41,6 +41,10 @@ public class SettingsService : ISettingsService
private const string KeyTechGlossarTemp = "tech_glossar_temp";
private const string KeyTechQualitaetTemp = "tech_qualitaet_temp";
private const string KeyTechRisikoTemp = "tech_risiko_temp";
private const string KeyQuickMarkdownPrompt = "quick_md_prompt";
private const string KeyQuickMarkdownTemp = "quick_md_temp";
private const string KeyChatPrompt = "chat_prompt";
private const string KeyChatTemp = "chat_temp";
private const string KeyLanguage = "language";
private const string KeyLastProjectId = "last_project_id";
@ -346,6 +350,42 @@ public class SettingsService : ISettingsService
return Task.CompletedTask;
}
public Task<string?> GetQuickMarkdownPromptAsync() => Task.FromResult(Get(KeyQuickMarkdownPrompt));
public Task SaveQuickMarkdownPromptAsync(string prompt)
{
Set(KeyQuickMarkdownPrompt, prompt);
_ollamaConfig.QuickMarkdownPrompt = prompt;
return Task.CompletedTask;
}
public Task<double> GetQuickMarkdownTempAsync() => Task.FromResult(double.TryParse(Get(KeyQuickMarkdownTemp), out var v) ? v : _ollamaConfig.QuickMarkdownTemp);
public Task SaveQuickMarkdownTempAsync(double temp)
{
Set(KeyQuickMarkdownTemp, temp.ToString("F1"));
_ollamaConfig.QuickMarkdownTemp = temp;
return Task.CompletedTask;
}
public Task<string?> GetChatPromptAsync() => Task.FromResult(Get(KeyChatPrompt));
public Task SaveChatPromptAsync(string prompt)
{
Set(KeyChatPrompt, prompt);
_ollamaConfig.ChatPrompt = prompt;
return Task.CompletedTask;
}
public Task<double> GetChatTempAsync() => Task.FromResult(double.TryParse(Get(KeyChatTemp), out var v) ? v : _ollamaConfig.ChatTemp);
public Task SaveChatTempAsync(double temp)
{
Set(KeyChatTemp, temp.ToString("F1"));
_ollamaConfig.ChatTemp = temp;
return Task.CompletedTask;
}
public Task SaveTechLastenheftPromptAsync(string prompt)
{
Set(KeyTechLastenheft, prompt);
@ -479,7 +519,7 @@ public class SettingsService : ISettingsService
var user = new UserEntity
{
Name = name,
Role = hasUsers ? UserRole.Author : UserRole.Administrator
Roles = new List<UserRole> { hasUsers ? UserRole.Author : UserRole.Administrator }
};
col.Insert(user);
return Task.FromResult(user);
@ -498,7 +538,7 @@ public class SettingsService : ISettingsService
var all = users.FindAll().OrderBy(u => u.Id).ToList();
if (all.Count > 0)
{
all[0].Role = UserRole.Administrator;
all[0].Roles = new List<UserRole> { UserRole.Administrator };
foreach (var u in all)
{
u.IsActive = true;
@ -506,20 +546,20 @@ public class SettingsService : ISettingsService
}
}
var authorExists = all.Any(u => u.Name == "Author");
if (!authorExists)
{
var authorUser = new UserEntity
{
Name = "Author",
PasswordHash = TotpHelper.HashPassword("author123"),
TwoFactorEnabled = false,
TwoFactorSecret = string.Empty,
Role = UserRole.Author,
IsActive = true
};
users.Insert(authorUser);
}
//var authorExists = all.Any(u => u.Name == "Author");
//if (!authorExists)
//{
// var authorUser = new UserEntity
// {
// Name = "Author",
// PasswordHash = TotpHelper.HashPassword("author123"),
// TwoFactorEnabled = false,
// TwoFactorSecret = string.Empty,
// Role = UserRole.Author,
// IsActive = true
// };
// users.Insert(authorUser);
//}
var projects = _db.GetCollection<ProjectEntity>("projects");
var orphaned = projects.Find(p => p.UserId == 0).ToList();

View file

@ -5,5 +5,15 @@
"Microsoft.AspNetCore": "Warning"
}
},
"AllowedHosts": "*"
"AllowedHosts": "*",
"Smtp": {
"Host": "",
"Port": 587,
"Username": "",
"Password": "",
"FromAddress": "",
"FromName": "Author Buddy",
"UseSsl": true,
"Enabled": false
}
}

View file

@ -743,9 +743,40 @@ button.primary {
width: 100%;
}
.settings-tabs {
display: flex;
gap: 0;
border-bottom: 2px solid var(--accent-color);
margin-bottom: 16px;
}
.settings-tab {
padding: 10px 24px;
border: 1px solid var(--border-color);
border-bottom: none;
background: var(--menu-bg);
color: var(--text-color);
cursor: pointer;
font-size: 14px;
font-weight: bold;
margin-right: 4px;
border-radius: 4px 4px 0 0;
}
.settings-tab:hover {
background: var(--accent-color);
color: white;
}
.settings-tab.active {
background: var(--accent-color);
color: white;
border-color: var(--accent-color);
}
.settings-layout {
display: grid;
grid-template-columns: 1fr 2fr 2fr;
grid-template-columns: 1fr 2fr;
gap: 16px;
}
@ -753,6 +784,11 @@ button.primary {
min-width: 0;
}
.settings-column-wide {
min-width: 0;
grid-column: 2;
}
.settings-column .form-group textarea {
min-height: 120px;
font-family: monospace;
@ -1401,3 +1437,359 @@ button.primary {
.modal-dialog.modal-sm {
max-width: 400px;
}
/* QuickMarkdown page */
.qm-container {
padding: 20px;
}
.qm-layout {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 16px;
}
.qm-input-panel,
.qm-output-panel {
min-width: 0;
}
.qm-textarea {
width: 100%;
resize: vertical;
padding: 8px;
border: 1px solid var(--border-color);
background: var(--input-bg, var(--editor-bg));
color: var(--text-color);
font-size: 13px;
font-family: inherit;
box-sizing: border-box;
}
.qm-prompt {
font-family: monospace;
font-size: 12px;
}
.qm-output {
font-family: monospace;
font-size: 13px;
}
.qm-notice {
margin-top: 12px;
padding: 8px;
background: var(--card-bg);
border: 1px solid var(--border-color);
font-size: 13px;
}
.qm-range-labels {
display: flex;
justify-content: space-between;
font-size: 11px;
color: var(--border-color);
margin-top: 2px;
}
.qm-prompt-actions {
display: flex;
gap: 8px;
}
/* Chat page */
.chat-page {
display: flex;
flex-direction: column;
height: 100%;
overflow: hidden;
}
.chat-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 12px 16px;
border-bottom: 1px solid var(--border-color);
flex-shrink: 0;
}
.chat-header h3 {
margin: 0;
font-size: 16px;
}
.chat-header-actions {
display: flex;
gap: 8px;
}
.chat-settings {
padding: 12px 16px;
border-bottom: 1px solid var(--border-color);
background: var(--card-bg);
flex-shrink: 0;
}
.chat-settings .form-group {
margin-bottom: 8px;
}
.chat-settings .form-group:last-child {
margin-bottom: 0;
}
.chat-settings-textarea {
width: 100%;
resize: vertical;
padding: 6px 8px;
border: 1px solid var(--border-color);
background: var(--input-bg, var(--editor-bg));
color: var(--text-color);
font-size: 12px;
font-family: monospace;
box-sizing: border-box;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 16px;
}
.chat-empty {
text-align: center;
color: var(--border-color);
font-style: italic;
padding: 40px;
}
.chat-message {
display: flex;
gap: 10px;
margin-bottom: 16px;
}
.chat-avatar {
width: 32px;
height: 32px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-weight: bold;
font-size: 14px;
flex-shrink: 0;
}
.chat-user .chat-avatar {
background: var(--accent-color);
color: white;
}
.chat-assistant .chat-avatar {
background: var(--border-color);
color: var(--text-color);
}
.chat-bubble {
padding: 10px 14px;
border-radius: 4px;
font-size: 14px;
line-height: 1.5;
max-width: 75%;
}
.chat-user .chat-bubble {
background: var(--accent-color);
color: white;
}
.chat-assistant .chat-bubble {
background: var(--card-bg);
border: 1px solid var(--border-color);
}
.chat-assistant .chat-bubble strong {
font-weight: bold;
}
.chat-assistant .chat-bubble em {
font-style: italic;
}
.chat-thinking {
min-width: 40px;
}
.chat-input {
display: flex;
gap: 8px;
padding: 12px 16px;
border-top: 1px solid var(--border-color);
flex-shrink: 0;
}
.chat-textarea {
flex: 1;
resize: none;
padding: 8px;
border: 1px solid var(--border-color);
background: var(--input-bg, var(--editor-bg));
color: var(--text-color);
font-size: 14px;
font-family: inherit;
}
.chat-input button {
align-self: flex-end;
}
/* Responsive: Tablets */
@media (max-width: 1024px) {
.sidebar {
width: 200px;
min-width: 120px;
}
.settings-layout {
grid-template-columns: 1fr;
}
.qm-layout {
grid-template-columns: 1fr;
}
.chat-bubble {
max-width: 85%;
}
}
/* Responsive: Phones */
@media (max-width: 768px) {
body {
overflow: auto;
}
.sidebar {
display: none;
}
.main-content {
flex-direction: column;
}
.detail-panel {
padding: 8px;
}
.qm-layout {
grid-template-columns: 1fr;
}
.qm-container {
padding: 12px;
}
.qm-textarea {
font-size: 16px;
}
.chat-settings,
.chat-settings-toggle {
display: none;
}
.chat-header {
flex-direction: column;
align-items: flex-start;
gap: 8px;
padding: 8px 12px;
}
.chat-header-actions {
width: 100%;
justify-content: flex-end;
}
.chat-messages {
padding: 12px;
}
.chat-bubble {
max-width: 90%;
font-size: 15px;
}
.chat-input {
padding: 8px 12px;
}
.chat-textarea {
font-size: 16px;
}
.settings-layout {
grid-template-columns: 1fr;
}
.settings-page {
padding: 8px;
}
input, select, textarea, button {
font-size: 16px;
}
button {
padding: 10px 16px;
}
.btn-small, .btn-sm {
padding: 8px 12px;
font-size: 14px;
}
.modal-dialog {
min-width: unset;
width: 95%;
margin: 0 auto;
}
}
/* Responsive: Small phones */
@media (max-width: 480px) {
.chat-header h3 {
font-size: 14px;
}
.chat-avatar {
width: 28px;
height: 28px;
font-size: 12px;
}
.chat-bubble {
padding: 8px 10px;
max-width: 95%;
font-size: 14px;
}
.chat-message {
gap: 6px;
}
.chat-empty {
padding: 20px;
}
.usermanagement-table th,
.usermanagement-table td {
padding: 6px 8px;
font-size: 12px;
}
.usermanagement-actions {
flex-wrap: wrap;
}
}