summaryrefslogtreecommitdiff
path: root/llama.cpp/tools/server/webui/docs
diff options
context:
space:
mode:
authorMitja Felicijan <mitja.felicijan@gmail.com>2026-02-12 20:57:17 +0100
committerMitja Felicijan <mitja.felicijan@gmail.com>2026-02-12 20:57:17 +0100
commitb333b06772c89d96aacb5490d6a219fba7c09cc6 (patch)
tree211df60083a5946baa2ed61d33d8121b7e251b06 /llama.cpp/tools/server/webui/docs
downloadllmnpc-b333b06772c89d96aacb5490d6a219fba7c09cc6.tar.gz
Engage!
Diffstat (limited to 'llama.cpp/tools/server/webui/docs')
-rw-r--r--llama.cpp/tools/server/webui/docs/architecture/high-level-architecture-simplified.md106
-rw-r--r--llama.cpp/tools/server/webui/docs/architecture/high-level-architecture.md279
-rw-r--r--llama.cpp/tools/server/webui/docs/flows/chat-flow.md174
-rw-r--r--llama.cpp/tools/server/webui/docs/flows/conversations-flow.md155
-rw-r--r--llama.cpp/tools/server/webui/docs/flows/data-flow-simplified-model-mode.md45
-rw-r--r--llama.cpp/tools/server/webui/docs/flows/data-flow-simplified-router-mode.md77
-rw-r--r--llama.cpp/tools/server/webui/docs/flows/database-flow.md155
-rw-r--r--llama.cpp/tools/server/webui/docs/flows/models-flow.md181
-rw-r--r--llama.cpp/tools/server/webui/docs/flows/server-flow.md76
-rw-r--r--llama.cpp/tools/server/webui/docs/flows/settings-flow.md144
10 files changed, 1392 insertions, 0 deletions
diff --git a/llama.cpp/tools/server/webui/docs/architecture/high-level-architecture-simplified.md b/llama.cpp/tools/server/webui/docs/architecture/high-level-architecture-simplified.md
new file mode 100644
index 0000000..a6cb1e9
--- /dev/null
+++ b/llama.cpp/tools/server/webui/docs/architecture/high-level-architecture-simplified.md
@@ -0,0 +1,106 @@
+```mermaid
+flowchart TB
+ subgraph Routes["📍 Routes"]
+ R1["/ (Welcome)"]
+ R2["/chat/[id]"]
+ RL["+layout.svelte"]
+ end
+
+ subgraph Components["🧩 Components"]
+ C_Sidebar["ChatSidebar"]
+ C_Screen["ChatScreen"]
+ C_Form["ChatForm"]
+ C_Messages["ChatMessages"]
+ C_Message["ChatMessage"]
+ C_MessageEditForm["ChatMessageEditForm"]
+ C_ModelsSelector["ModelsSelector"]
+ C_Settings["ChatSettings"]
+ end
+
+ subgraph Hooks["đŸĒ Hooks"]
+ H1["useModelChangeValidation"]
+ H2["useProcessingState"]
+ end
+
+ subgraph Stores["đŸ—„ī¸ Stores"]
+ S1["chatStore<br/><i>Chat interactions & streaming</i>"]
+ S2["conversationsStore<br/><i>Conversation data & messages</i>"]
+ S3["modelsStore<br/><i>Model selection & loading</i>"]
+ S4["serverStore<br/><i>Server props & role detection</i>"]
+ S5["settingsStore<br/><i>User configuration</i>"]
+ end
+
+ subgraph Services["âš™ī¸ Services"]
+ SV1["ChatService"]
+ SV2["ModelsService"]
+ SV3["PropsService"]
+ SV4["DatabaseService"]
+ SV5["ParameterSyncService"]
+ end
+
+ subgraph Storage["💾 Storage"]
+ ST1["IndexedDB<br/><i>conversations, messages</i>"]
+ ST2["LocalStorage<br/><i>config, userOverrides</i>"]
+ end
+
+ subgraph APIs["🌐 llama-server API"]
+ API1["/v1/chat/completions"]
+ API2["/props"]
+ API3["/models/*"]
+ API4["/v1/models"]
+ end
+
+ %% Routes → Components
+ R1 & R2 --> C_Screen
+ RL --> C_Sidebar
+
+ %% Component hierarchy
+ C_Screen --> C_Form & C_Messages & C_Settings
+ C_Messages --> C_Message
+ C_Message --> C_MessageEditForm
+ C_Form & C_MessageEditForm --> C_ModelsSelector
+
+ %% Components → Hooks → Stores
+ C_Form & C_Messages --> H1 & H2
+ H1 --> S3 & S4
+ H2 --> S1 & S5
+
+ %% Components → Stores
+ C_Screen --> S1 & S2
+ C_Sidebar --> S2
+ C_ModelsSelector --> S3 & S4
+ C_Settings --> S5
+
+ %% Stores → Services
+ S1 --> SV1 & SV4
+ S2 --> SV4
+ S3 --> SV2 & SV3
+ S4 --> SV3
+ S5 --> SV5
+
+ %% Services → Storage
+ SV4 --> ST1
+ SV5 --> ST2
+
+ %% Services → APIs
+ SV1 --> API1
+ SV2 --> API3 & API4
+ SV3 --> API2
+
+ %% Styling
+ classDef routeStyle fill:#e1f5fe,stroke:#01579b,stroke-width:2px
+ classDef componentStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
+ classDef hookStyle fill:#fff8e1,stroke:#ff8f00,stroke-width:2px
+ classDef storeStyle fill:#fff3e0,stroke:#e65100,stroke-width:2px
+ classDef serviceStyle fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
+ classDef storageStyle fill:#fce4ec,stroke:#c2185b,stroke-width:2px
+ classDef apiStyle fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
+
+ class R1,R2,RL routeStyle
+ class C_Sidebar,C_Screen,C_Form,C_Messages,C_Message,C_MessageEditForm,C_ModelsSelector,C_Settings componentStyle
+ class H1,H2 hookStyle
+ class S1,S2,S3,S4,S5 storeStyle
+ class SV1,SV2,SV3,SV4,SV5 serviceStyle
+ class ST1,ST2 storageStyle
+ class API1,API2,API3,API4 apiStyle
+```
diff --git a/llama.cpp/tools/server/webui/docs/architecture/high-level-architecture.md b/llama.cpp/tools/server/webui/docs/architecture/high-level-architecture.md
new file mode 100644
index 0000000..c5ec4d6
--- /dev/null
+++ b/llama.cpp/tools/server/webui/docs/architecture/high-level-architecture.md
@@ -0,0 +1,279 @@
+```mermaid
+flowchart TB
+subgraph Routes["📍 Routes"]
+R1["/ (+page.svelte)"]
+R2["/chat/[id]"]
+RL["+layout.svelte"]
+end
+
+ subgraph Components["🧩 Components"]
+ direction TB
+ subgraph LayoutComponents["Layout"]
+ C_Sidebar["ChatSidebar"]
+ C_Screen["ChatScreen"]
+ end
+ subgraph ChatUIComponents["Chat UI"]
+ C_Form["ChatForm"]
+ C_Messages["ChatMessages"]
+ C_Message["ChatMessage"]
+ C_MessageUser["ChatMessageUser"]
+ C_MessageEditForm["ChatMessageEditForm"]
+ C_Attach["ChatAttachments"]
+ C_ModelsSelector["ModelsSelector"]
+ C_Settings["ChatSettings"]
+ end
+ end
+
+ subgraph Hooks["đŸĒ Hooks"]
+ H1["useModelChangeValidation"]
+ H2["useProcessingState"]
+ H3["isMobile"]
+ end
+
+ subgraph Stores["đŸ—„ī¸ Stores"]
+ direction TB
+ subgraph S1["chatStore"]
+ S1State["<b>State:</b><br/>isLoading, currentResponse<br/>errorDialogState<br/>activeProcessingState<br/>chatLoadingStates<br/>chatStreamingStates<br/>abortControllers<br/>processingStates<br/>activeConversationId<br/>isStreamingActive"]
+ S1LoadState["<b>Loading State:</b><br/>setChatLoading()<br/>isChatLoading()<br/>syncLoadingStateForChat()<br/>clearUIState()<br/>isChatLoadingPublic()<br/>getAllLoadingChats()<br/>getAllStreamingChats()"]
+ S1ProcState["<b>Processing State:</b><br/>setActiveProcessingConversation()<br/>getProcessingState()<br/>clearProcessingState()<br/>getActiveProcessingState()<br/>updateProcessingStateFromTimings()<br/>getCurrentProcessingStateSync()<br/>restoreProcessingStateFromMessages()"]
+ S1Stream["<b>Streaming:</b><br/>streamChatCompletion()<br/>startStreaming()<br/>stopStreaming()<br/>stopGeneration()<br/>isStreaming()"]
+ S1Error["<b>Error Handling:</b><br/>showErrorDialog()<br/>dismissErrorDialog()<br/>isAbortError()"]
+ S1Msg["<b>Message Operations:</b><br/>addMessage()<br/>sendMessage()<br/>updateMessage()<br/>deleteMessage()<br/>getDeletionInfo()"]
+ S1Regen["<b>Regeneration:</b><br/>regenerateMessage()<br/>regenerateMessageWithBranching()<br/>continueAssistantMessage()"]
+ S1Edit["<b>Editing:</b><br/>editAssistantMessage()<br/>editUserMessagePreserveResponses()<br/>editMessageWithBranching()<br/>clearEditMode()<br/>isEditModeActive()<br/>getAddFilesHandler()<br/>setEditModeActive()"]
+ S1Utils["<b>Utilities:</b><br/>getApiOptions()<br/>parseTimingData()<br/>getOrCreateAbortController()<br/>getConversationModel()"]
+ end
+ subgraph S2["conversationsStore"]
+ S2State["<b>State:</b><br/>conversations<br/>activeConversation<br/>activeMessages<br/>usedModalities<br/>isInitialized<br/>titleUpdateConfirmationCallback"]
+ S2Modal["<b>Modalities:</b><br/>getModalitiesUpToMessage()<br/>calculateModalitiesFromMessages()"]
+ S2Lifecycle["<b>Lifecycle:</b><br/>initialize()<br/>loadConversations()<br/>clearActiveConversation()"]
+ S2ConvCRUD["<b>Conversation CRUD:</b><br/>createConversation()<br/>loadConversation()<br/>deleteConversation()<br/>updateConversationName()<br/>updateConversationTitleWithConfirmation()"]
+ S2MsgMgmt["<b>Message Management:</b><br/>refreshActiveMessages()<br/>addMessageToActive()<br/>updateMessageAtIndex()<br/>findMessageIndex()<br/>sliceActiveMessages()<br/>removeMessageAtIndex()<br/>getConversationMessages()"]
+ S2Nav["<b>Navigation:</b><br/>navigateToSibling()<br/>updateCurrentNode()<br/>updateConversationTimestamp()"]
+ S2Export["<b>Import/Export:</b><br/>downloadConversation()<br/>exportAllConversations()<br/>importConversations()<br/>triggerDownload()"]
+ S2Utils["<b>Utilities:</b><br/>setTitleUpdateConfirmationCallback()"]
+ end
+ subgraph S3["modelsStore"]
+ S3State["<b>State:</b><br/>models, routerModels<br/>selectedModelId<br/>selectedModelName<br/>loading, updating, error<br/>modelLoadingStates<br/>modelPropsCache<br/>modelPropsFetching<br/>propsCacheVersion"]
+ S3Getters["<b>Computed Getters:</b><br/>selectedModel<br/>loadedModelIds<br/>loadingModelIds<br/>singleModelName"]
+ S3Modal["<b>Modalities:</b><br/>getModelModalities()<br/>modelSupportsVision()<br/>modelSupportsAudio()<br/>getModelModalitiesArray()<br/>getModelProps()<br/>updateModelModalities()"]
+ S3Status["<b>Status Queries:</b><br/>isModelLoaded()<br/>isModelOperationInProgress()<br/>getModelStatus()<br/>isModelPropsFetching()"]
+ S3Fetch["<b>Data Fetching:</b><br/>fetch()<br/>fetchRouterModels()<br/>fetchModelProps()<br/>fetchModalitiesForLoadedModels()"]
+ S3Select["<b>Model Selection:</b><br/>selectModelById()<br/>selectModelByName()<br/>clearSelection()<br/>findModelByName()<br/>findModelById()<br/>hasModel()"]
+ S3LoadUnload["<b>Loading/Unloading Models:</b><br/>loadModel()<br/>unloadModel()<br/>ensureModelLoaded()<br/>waitForModelStatus()<br/>pollForModelStatus()"]
+ S3Utils["<b>Utilities:</b><br/>toDisplayName()<br/>clear()"]
+ end
+ subgraph S4["serverStore"]
+ S4State["<b>State:</b><br/>props<br/>loading, error<br/>role<br/>fetchPromise"]
+ S4Getters["<b>Getters:</b><br/>defaultParams<br/>contextSize<br/>isRouterMode<br/>isModelMode"]
+ S4Data["<b>Data Handling:</b><br/>fetch()<br/>getErrorMessage()<br/>clear()"]
+ S4Utils["<b>Utilities:</b><br/>detectRole()"]
+ end
+ subgraph S5["settingsStore"]
+ S5State["<b>State:</b><br/>config<br/>theme<br/>isInitialized<br/>userOverrides"]
+ S5Lifecycle["<b>Lifecycle:</b><br/>initialize()<br/>loadConfig()<br/>saveConfig()<br/>loadTheme()<br/>saveTheme()"]
+ S5Update["<b>Config Updates:</b><br/>updateConfig()<br/>updateMultipleConfig()<br/>updateTheme()"]
+ S5Reset["<b>Reset:</b><br/>resetConfig()<br/>resetTheme()<br/>resetAll()<br/>resetParameterToServerDefault()"]
+ S5Sync["<b>Server Sync:</b><br/>syncWithServerDefaults()<br/>forceSyncWithServerDefaults()"]
+ S5Utils["<b>Utilities:</b><br/>getConfig()<br/>getAllConfig()<br/>getParameterInfo()<br/>getParameterDiff()<br/>getServerDefaults()<br/>clearAllUserOverrides()"]
+ end
+
+ subgraph ReactiveExports["⚡ Reactive Exports"]
+ direction LR
+ subgraph ChatExports["chatStore"]
+ RE1["isLoading()"]
+ RE2["currentResponse()"]
+ RE3["errorDialog()"]
+ RE4["activeProcessingState()"]
+ RE5["isChatStreaming()"]
+ RE6["isChatLoading()"]
+ RE7["getChatStreaming()"]
+ RE8["getAllLoadingChats()"]
+ RE9["getAllStreamingChats()"]
+ RE9a["isEditModeActive()"]
+ RE9b["getAddFilesHandler()"]
+ RE9c["setEditModeActive()"]
+ RE9d["clearEditMode()"]
+ end
+ subgraph ConvExports["conversationsStore"]
+ RE10["conversations()"]
+ RE11["activeConversation()"]
+ RE12["activeMessages()"]
+ RE13["isConversationsInitialized()"]
+ RE14["usedModalities()"]
+ end
+ subgraph ModelsExports["modelsStore"]
+ RE15["modelOptions()"]
+ RE16["routerModels()"]
+ RE17["modelsLoading()"]
+ RE18["modelsUpdating()"]
+ RE19["modelsError()"]
+ RE20["selectedModelId()"]
+ RE21["selectedModelName()"]
+ RE22["selectedModelOption()"]
+ RE23["loadedModelIds()"]
+ RE24["loadingModelIds()"]
+ RE25["propsCacheVersion()"]
+ RE26["singleModelName()"]
+ end
+ subgraph ServerExports["serverStore"]
+ RE27["serverProps()"]
+ RE28["serverLoading()"]
+ RE29["serverError()"]
+ RE30["serverRole()"]
+ RE31["defaultParams()"]
+ RE32["contextSize()"]
+ RE33["isRouterMode()"]
+ RE34["isModelMode()"]
+ end
+ subgraph SettingsExports["settingsStore"]
+ RE35["config()"]
+ RE36["theme()"]
+ RE37["isInitialized()"]
+ end
+ end
+ end
+
+ subgraph Services["âš™ī¸ Services"]
+ direction TB
+ subgraph SV1["ChatService"]
+ SV1Msg["<b>Messaging:</b><br/>sendMessage()"]
+ SV1Stream["<b>Streaming:</b><br/>handleStreamResponse()<br/>parseSSEChunk()"]
+ SV1Convert["<b>Conversion:</b><br/>convertMessageToChatData()<br/>convertExtraToApiFormat()"]
+ SV1Utils["<b>Utilities:</b><br/>extractReasoningContent()<br/>getServerProps()<br/>getModels()"]
+ end
+ subgraph SV2["ModelsService"]
+ SV2List["<b>Listing:</b><br/>list()<br/>listRouter()"]
+ SV2LoadUnload["<b>Load/Unload:</b><br/>load()<br/>unload()"]
+ SV2Status["<b>Status:</b><br/>isModelLoaded()<br/>isModelLoading()"]
+ end
+ subgraph SV3["PropsService"]
+ SV3Fetch["<b>Fetching:</b><br/>fetch()<br/>fetchForModel()"]
+ end
+ subgraph SV4["DatabaseService"]
+ SV4Conv["<b>Conversations:</b><br/>createConversation()<br/>getConversation()<br/>getAllConversations()<br/>updateConversation()<br/>deleteConversation()"]
+ SV4Msg["<b>Messages:</b><br/>createMessageBranch()<br/>createRootMessage()<br/>getConversationMessages()<br/>updateMessage()<br/>deleteMessage()<br/>deleteMessageCascading()"]
+ SV4Node["<b>Navigation:</b><br/>updateCurrentNode()"]
+ SV4Import["<b>Import:</b><br/>importConversations()"]
+ end
+ subgraph SV5["ParameterSyncService"]
+ SV5Extract["<b>Extraction:</b><br/>extractServerDefaults()"]
+ SV5Merge["<b>Merging:</b><br/>mergeWithServerDefaults()"]
+ SV5Info["<b>Info:</b><br/>getParameterInfo()<br/>canSyncParameter()<br/>getSyncableParameterKeys()<br/>validateServerParameter()"]
+ SV5Diff["<b>Diff:</b><br/>createParameterDiff()"]
+ end
+ end
+
+ subgraph Storage["💾 Storage"]
+ ST1["IndexedDB"]
+ ST2["conversations"]
+ ST3["messages"]
+ ST5["LocalStorage"]
+ ST6["config"]
+ ST7["userOverrides"]
+ end
+
+ subgraph APIs["🌐 llama-server API"]
+ API1["/v1/chat/completions"]
+ API2["/props<br/>/props?model="]
+ API3["/models<br/>/models/load<br/>/models/unload"]
+ API4["/v1/models"]
+ end
+
+ %% Routes render Components
+ R1 --> C_Screen
+ R2 --> C_Screen
+ RL --> C_Sidebar
+
+ %% Component hierarchy
+ C_Screen --> C_Form & C_Messages & C_Settings
+ C_Messages --> C_Message
+ C_Message --> C_MessageUser
+ C_MessageUser --> C_MessageEditForm
+ C_MessageEditForm --> C_ModelsSelector
+ C_MessageEditForm --> C_Attach
+ C_Form --> C_ModelsSelector
+ C_Form --> C_Attach
+ C_Message --> C_Attach
+
+ %% Components use Hooks
+ C_Form --> H1
+ C_Message --> H1 & H2
+ C_MessageEditForm --> H1
+ C_Screen --> H2
+
+ %% Hooks use Stores
+ H1 --> S3 & S4
+ H2 --> S1 & S5
+
+ %% Components use Stores
+ C_Screen --> S1 & S2
+ C_Messages --> S2
+ C_Message --> S1 & S2 & S3
+ C_Form --> S1 & S3
+ C_Sidebar --> S2
+ C_ModelsSelector --> S3 & S4
+ C_Settings --> S5
+
+ %% Stores export Reactive State
+ S1 -. exports .-> ChatExports
+ S2 -. exports .-> ConvExports
+ S3 -. exports .-> ModelsExports
+ S4 -. exports .-> ServerExports
+ S5 -. exports .-> SettingsExports
+
+ %% Stores use Services
+ S1 --> SV1 & SV4
+ S2 --> SV4
+ S3 --> SV2 & SV3
+ S4 --> SV3
+ S5 --> SV5
+
+ %% Services to Storage
+ SV4 --> ST1
+ ST1 --> ST2 & ST3
+ SV5 --> ST5
+ ST5 --> ST6 & ST7
+
+ %% Services to APIs
+ SV1 --> API1
+ SV2 --> API3 & API4
+ SV3 --> API2
+
+ %% Styling
+ classDef routeStyle fill:#e1f5fe,stroke:#01579b,stroke-width:2px
+ classDef componentStyle fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
+ classDef componentGroupStyle fill:#e1bee7,stroke:#7b1fa2,stroke-width:1px
+ classDef storeStyle fill:#fff3e0,stroke:#e65100,stroke-width:2px
+ classDef stateStyle fill:#ffe0b2,stroke:#e65100,stroke-width:1px
+ classDef methodStyle fill:#ffecb3,stroke:#e65100,stroke-width:1px
+ classDef reactiveStyle fill:#fffde7,stroke:#f9a825,stroke-width:1px
+ classDef serviceStyle fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
+ classDef serviceMStyle fill:#c8e6c9,stroke:#2e7d32,stroke-width:1px
+ classDef storageStyle fill:#fce4ec,stroke:#c2185b,stroke-width:2px
+ classDef apiStyle fill:#e3f2fd,stroke:#1565c0,stroke-width:2px
+
+ class R1,R2,RL routeStyle
+ class C_Sidebar,C_Screen,C_Form,C_Messages,C_Message,C_MessageUser,C_MessageEditForm componentStyle
+ class C_ModelsSelector,C_Settings componentStyle
+ class C_Attach componentStyle
+ class H1,H2,H3 methodStyle
+ class LayoutComponents,ChatUIComponents componentGroupStyle
+ class Hooks storeStyle
+ class S1,S2,S3,S4,S5 storeStyle
+ class S1State,S2State,S3State,S4State,S5State stateStyle
+ class S1Msg,S1Regen,S1Edit,S1Stream,S1LoadState,S1ProcState,S1Error,S1Utils methodStyle
+ class S2Lifecycle,S2ConvCRUD,S2MsgMgmt,S2Nav,S2Modal,S2Export,S2Utils methodStyle
+ class S3Getters,S3Modal,S3Status,S3Fetch,S3Select,S3LoadUnload,S3Utils methodStyle
+ class S4Getters,S4Data,S4Utils methodStyle
+ class S5Lifecycle,S5Update,S5Reset,S5Sync,S5Utils methodStyle
+ class ChatExports,ConvExports,ModelsExports,ServerExports,SettingsExports reactiveStyle
+ class SV1,SV2,SV3,SV4,SV5 serviceStyle
+ class SV1Msg,SV1Stream,SV1Convert,SV1Utils serviceMStyle
+ class SV2List,SV2LoadUnload,SV2Status serviceMStyle
+ class SV3Fetch serviceMStyle
+ class SV4Conv,SV4Msg,SV4Node,SV4Import serviceMStyle
+ class SV5Extract,SV5Merge,SV5Info,SV5Diff serviceMStyle
+ class ST1,ST2,ST3,ST5,ST6,ST7 storageStyle
+ class API1,API2,API3,API4 apiStyle
+```
diff --git a/llama.cpp/tools/server/webui/docs/flows/chat-flow.md b/llama.cpp/tools/server/webui/docs/flows/chat-flow.md
new file mode 100644
index 0000000..05e1df3
--- /dev/null
+++ b/llama.cpp/tools/server/webui/docs/flows/chat-flow.md
@@ -0,0 +1,174 @@
+```mermaid
+sequenceDiagram
+ participant UI as 🧩 ChatForm / ChatMessage
+ participant chatStore as đŸ—„ī¸ chatStore
+ participant convStore as đŸ—„ī¸ conversationsStore
+ participant settingsStore as đŸ—„ī¸ settingsStore
+ participant ChatSvc as âš™ī¸ ChatService
+ participant DbSvc as âš™ī¸ DatabaseService
+ participant API as 🌐 /v1/chat/completions
+
+ Note over chatStore: State:<br/>isLoading, currentResponse<br/>errorDialogState, activeProcessingState<br/>chatLoadingStates (Map)<br/>chatStreamingStates (Map)<br/>abortControllers (Map)<br/>processingStates (Map)
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: đŸ’Ŧ SEND MESSAGE
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>chatStore: sendMessage(content, extras)
+ activate chatStore
+
+ chatStore->>chatStore: setChatLoading(convId, true)
+ chatStore->>chatStore: clearChatStreaming(convId)
+
+ alt no active conversation
+ chatStore->>convStore: createConversation()
+ Note over convStore: → see conversations-flow.mmd
+ end
+
+ chatStore->>chatStore: addMessage("user", content, extras)
+ chatStore->>DbSvc: createMessageBranch(userMsg, parentId)
+ chatStore->>convStore: addMessageToActive(userMsg)
+ chatStore->>convStore: updateCurrentNode(userMsg.id)
+
+ chatStore->>chatStore: createAssistantMessage(userMsg.id)
+ chatStore->>DbSvc: createMessageBranch(assistantMsg, userMsg.id)
+ chatStore->>convStore: addMessageToActive(assistantMsg)
+
+ chatStore->>chatStore: streamChatCompletion(messages, assistantMsg)
+ deactivate chatStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: 🌊 STREAMING
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ activate chatStore
+ chatStore->>chatStore: startStreaming()
+ Note right of chatStore: isStreamingActive = true
+
+ chatStore->>chatStore: setActiveProcessingConversation(convId)
+ chatStore->>chatStore: getOrCreateAbortController(convId)
+ Note right of chatStore: abortControllers.set(convId, new AbortController())
+
+ chatStore->>chatStore: getApiOptions()
+ Note right of chatStore: Merge from settingsStore.config:<br/>temperature, max_tokens, top_p, etc.
+
+ chatStore->>ChatSvc: sendMessage(messages, options, signal)
+ activate ChatSvc
+
+ ChatSvc->>ChatSvc: convertMessageToChatData(messages)
+ Note right of ChatSvc: DatabaseMessage[] → ApiChatMessageData[]<br/>Process attachments (images, PDFs, audio)
+
+ ChatSvc->>API: POST /v1/chat/completions
+ Note right of API: {messages, model?, stream: true, ...params}
+
+ loop SSE chunks
+ API-->>ChatSvc: data: {"choices":[{"delta":{...}}]}
+ ChatSvc->>ChatSvc: parseSSEChunk(line)
+
+ alt content chunk
+ ChatSvc-->>chatStore: onChunk(content)
+ chatStore->>chatStore: setChatStreaming(convId, response, msgId)
+ Note right of chatStore: currentResponse = $state(accumulated)
+ chatStore->>convStore: updateMessageAtIndex(idx, {content})
+ end
+
+ alt reasoning chunk
+ ChatSvc-->>chatStore: onReasoningChunk(reasoning)
+ chatStore->>convStore: updateMessageAtIndex(idx, {thinking})
+ end
+
+ alt tool_calls chunk
+ ChatSvc-->>chatStore: onToolCallChunk(toolCalls)
+ chatStore->>convStore: updateMessageAtIndex(idx, {toolCalls})
+ end
+
+ alt model info
+ ChatSvc-->>chatStore: onModel(modelName)
+ chatStore->>chatStore: recordModel(modelName)
+ chatStore->>DbSvc: updateMessage(msgId, {model})
+ end
+
+ alt timings (during stream)
+ ChatSvc-->>chatStore: onTimings(timings, promptProgress)
+ chatStore->>chatStore: updateProcessingStateFromTimings()
+ end
+
+ chatStore-->>UI: reactive $state update
+ end
+
+ API-->>ChatSvc: data: [DONE]
+ ChatSvc-->>chatStore: onComplete(content, reasoning, timings, toolCalls)
+ deactivate ChatSvc
+
+ chatStore->>chatStore: stopStreaming()
+ chatStore->>DbSvc: updateMessage(msgId, {content, timings, model})
+ chatStore->>convStore: updateCurrentNode(msgId)
+ chatStore->>chatStore: setChatLoading(convId, false)
+ chatStore->>chatStore: clearChatStreaming(convId)
+ chatStore->>chatStore: clearProcessingState(convId)
+ deactivate chatStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: âšī¸ STOP GENERATION
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>chatStore: stopGeneration()
+ activate chatStore
+ chatStore->>chatStore: savePartialResponseIfNeeded(convId)
+ Note right of chatStore: Save currentResponse to DB if non-empty
+ chatStore->>chatStore: abortControllers.get(convId).abort()
+ Note right of chatStore: fetch throws AbortError → caught by isAbortError()
+ chatStore->>chatStore: stopStreaming()
+ chatStore->>chatStore: setChatLoading(convId, false)
+ chatStore->>chatStore: clearChatStreaming(convId)
+ chatStore->>chatStore: clearProcessingState(convId)
+ deactivate chatStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: 🔁 REGENERATE
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>chatStore: regenerateMessageWithBranching(msgId, model?)
+ activate chatStore
+ chatStore->>convStore: findMessageIndex(msgId)
+ chatStore->>chatStore: Get parent of target message
+ chatStore->>chatStore: createAssistantMessage(parentId)
+ chatStore->>DbSvc: createMessageBranch(newAssistantMsg, parentId)
+ chatStore->>convStore: refreshActiveMessages()
+ Note right of chatStore: Same streaming flow
+ chatStore->>chatStore: streamChatCompletion(...)
+ deactivate chatStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: âžĄī¸ CONTINUE
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>chatStore: continueAssistantMessage(msgId)
+ activate chatStore
+ chatStore->>chatStore: Get existing content from message
+ chatStore->>chatStore: streamChatCompletion(..., existingContent)
+ Note right of chatStore: Appends to existing message content
+ deactivate chatStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: âœī¸ EDIT USER MESSAGE
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>chatStore: editUserMessagePreserveResponses(msgId, newContent)
+ activate chatStore
+ chatStore->>chatStore: Get parent of target message
+ chatStore->>DbSvc: createMessageBranch(editedMsg, parentId)
+ chatStore->>convStore: refreshActiveMessages()
+ Note right of chatStore: Creates new branch, original preserved
+ deactivate chatStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: ❌ ERROR HANDLING
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over chatStore: On stream error (non-abort):
+ chatStore->>chatStore: showErrorDialog(type, message)
+ Note right of chatStore: errorDialogState = {type: 'timeout'|'server', message}
+ chatStore->>convStore: removeMessageAtIndex(failedMsgIdx)
+ chatStore->>DbSvc: deleteMessage(failedMsgId)
+```
diff --git a/llama.cpp/tools/server/webui/docs/flows/conversations-flow.md b/llama.cpp/tools/server/webui/docs/flows/conversations-flow.md
new file mode 100644
index 0000000..185ed16
--- /dev/null
+++ b/llama.cpp/tools/server/webui/docs/flows/conversations-flow.md
@@ -0,0 +1,155 @@
+```mermaid
+sequenceDiagram
+ participant UI as 🧩 ChatSidebar / ChatScreen
+ participant convStore as đŸ—„ī¸ conversationsStore
+ participant chatStore as đŸ—„ī¸ chatStore
+ participant DbSvc as âš™ī¸ DatabaseService
+ participant IDB as 💾 IndexedDB
+
+ Note over convStore: State:<br/>conversations: DatabaseConversation[]<br/>activeConversation: DatabaseConversation | null<br/>activeMessages: DatabaseMessage[]<br/>isInitialized: boolean<br/>usedModalities: $derived({vision, audio})
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,IDB: 🚀 INITIALIZATION
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over convStore: Auto-initialized in constructor (browser only)
+ convStore->>convStore: initialize()
+ activate convStore
+ convStore->>convStore: loadConversations()
+ convStore->>DbSvc: getAllConversations()
+ DbSvc->>IDB: SELECT * FROM conversations ORDER BY lastModified DESC
+ IDB-->>DbSvc: Conversation[]
+ DbSvc-->>convStore: conversations
+ convStore->>convStore: conversations = $state(data)
+ convStore->>convStore: isInitialized = true
+ deactivate convStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,IDB: ➕ CREATE CONVERSATION
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>convStore: createConversation(name?)
+ activate convStore
+ convStore->>DbSvc: createConversation(name || "New Chat")
+ DbSvc->>IDB: INSERT INTO conversations
+ IDB-->>DbSvc: conversation {id, name, lastModified, currNode: ""}
+ DbSvc-->>convStore: conversation
+ convStore->>convStore: conversations.unshift(conversation)
+ convStore->>convStore: activeConversation = $state(conversation)
+ convStore->>convStore: activeMessages = $state([])
+ deactivate convStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,IDB: 📂 LOAD CONVERSATION
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>convStore: loadConversation(convId)
+ activate convStore
+ convStore->>DbSvc: getConversation(convId)
+ DbSvc->>IDB: SELECT * FROM conversations WHERE id = ?
+ IDB-->>DbSvc: conversation
+ convStore->>convStore: activeConversation = $state(conversation)
+
+ convStore->>convStore: refreshActiveMessages()
+ convStore->>DbSvc: getConversationMessages(convId)
+ DbSvc->>IDB: SELECT * FROM messages WHERE convId = ?
+ IDB-->>DbSvc: allMessages[]
+ convStore->>convStore: filterByLeafNodeId(allMessages, currNode)
+ Note right of convStore: Filter to show only current branch path
+ convStore->>convStore: activeMessages = $state(filtered)
+
+ convStore->>chatStore: syncLoadingStateForChat(convId)
+ Note right of chatStore: Sync isLoading/currentResponse if streaming
+ deactivate convStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,IDB: đŸŒŗ MESSAGE BRANCHING MODEL
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over IDB: Message Tree Structure:<br/>- Each message has parent (null for root)<br/>- Each message has children[] array<br/>- Conversation.currNode points to active leaf<br/>- filterByLeafNodeId() traverses from root to currNode
+
+ rect rgb(240, 240, 255)
+ Note over convStore: Example Branch Structure:
+ Note over convStore: root → user1 → assistant1 → user2 → assistant2a (currNode)<br/> ↘ assistant2b (alt branch)
+ end
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,IDB: â†”ī¸ BRANCH NAVIGATION
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>convStore: navigateToSibling(msgId, direction)
+ activate convStore
+ convStore->>convStore: Find message in activeMessages
+ convStore->>convStore: Get parent message
+ convStore->>convStore: Find sibling in parent.children[]
+ convStore->>convStore: findLeafNode(siblingId, allMessages)
+ Note right of convStore: Navigate to leaf of sibling branch
+ convStore->>convStore: updateCurrentNode(leafId)
+ convStore->>DbSvc: updateCurrentNode(convId, leafId)
+ DbSvc->>IDB: UPDATE conversations SET currNode = ?
+ convStore->>convStore: refreshActiveMessages()
+ deactivate convStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,IDB: 📝 UPDATE CONVERSATION
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>convStore: updateConversationName(convId, newName)
+ activate convStore
+ convStore->>DbSvc: updateConversation(convId, {name: newName})
+ DbSvc->>IDB: UPDATE conversations SET name = ?
+ convStore->>convStore: Update in conversations array
+ deactivate convStore
+
+ Note over convStore: Auto-title update (after first response):
+ convStore->>convStore: updateConversationTitleWithConfirmation()
+ convStore->>convStore: titleUpdateConfirmationCallback?()
+ Note right of convStore: Shows dialog if title would change
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,IDB: đŸ—‘ī¸ DELETE CONVERSATION
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>convStore: deleteConversation(convId)
+ activate convStore
+ convStore->>DbSvc: deleteConversation(convId)
+ DbSvc->>IDB: DELETE FROM conversations WHERE id = ?
+ DbSvc->>IDB: DELETE FROM messages WHERE convId = ?
+ convStore->>convStore: conversations.filter(c => c.id !== convId)
+ alt deleted active conversation
+ convStore->>convStore: clearActiveConversation()
+ end
+ deactivate convStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,IDB: 📊 MODALITY TRACKING
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over convStore: usedModalities = $derived.by(() => {<br/> calculateModalitiesFromMessages(activeMessages)<br/>})
+
+ Note over convStore: Scans activeMessages for attachments:<br/>- IMAGE → vision: true<br/>- PDF (processedAsImages) → vision: true<br/>- AUDIO → audio: true
+
+ UI->>convStore: getModalitiesUpToMessage(msgId)
+ Note right of convStore: Used for regeneration validation<br/>Only checks messages BEFORE target
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,IDB: 📤 EXPORT / đŸ“Ĩ IMPORT
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>convStore: exportAllConversations()
+ activate convStore
+ convStore->>DbSvc: getAllConversations()
+ loop each conversation
+ convStore->>DbSvc: getConversationMessages(convId)
+ end
+ convStore->>convStore: triggerDownload(JSON blob)
+ deactivate convStore
+
+ UI->>convStore: importConversations(file)
+ activate convStore
+ convStore->>convStore: Parse JSON file
+ convStore->>DbSvc: importConversations(parsed)
+ DbSvc->>IDB: Bulk INSERT conversations + messages
+ convStore->>convStore: loadConversations()
+ deactivate convStore
+```
diff --git a/llama.cpp/tools/server/webui/docs/flows/data-flow-simplified-model-mode.md b/llama.cpp/tools/server/webui/docs/flows/data-flow-simplified-model-mode.md
new file mode 100644
index 0000000..07b3621
--- /dev/null
+++ b/llama.cpp/tools/server/webui/docs/flows/data-flow-simplified-model-mode.md
@@ -0,0 +1,45 @@
+```mermaid
+%% MODEL Mode Data Flow (single model)
+%% Detailed flows: ./flows/server-flow.mmd, ./flows/models-flow.mmd, ./flows/chat-flow.mmd
+
+sequenceDiagram
+ participant User as 👤 User
+ participant UI as 🧩 UI
+ participant Stores as đŸ—„ī¸ Stores
+ participant DB as 💾 IndexedDB
+ participant API as 🌐 llama-server
+
+ Note over User,API: 🚀 Initialization (see: server-flow.mmd, models-flow.mmd)
+
+ UI->>Stores: initialize()
+ Stores->>DB: load conversations
+ Stores->>API: GET /props
+ API-->>Stores: server config + modalities
+ Stores->>API: GET /v1/models
+ API-->>Stores: single model (auto-selected)
+
+ Note over User,API: đŸ’Ŧ Chat Flow (see: chat-flow.mmd)
+
+ User->>UI: send message
+ UI->>Stores: sendMessage()
+ Stores->>DB: save user message
+ Stores->>API: POST /v1/chat/completions (stream)
+ loop streaming
+ API-->>Stores: SSE chunks
+ Stores-->>UI: reactive update
+ end
+ API-->>Stores: done + timings
+ Stores->>DB: save assistant message
+
+ Note over User,API: 🔁 Regenerate
+
+ User->>UI: regenerate
+ Stores->>DB: create message branch
+ Note right of Stores: same streaming flow
+
+ Note over User,API: âšī¸ Stop
+
+ User->>UI: stop
+ Stores->>Stores: abort stream
+ Stores->>DB: save partial response
+```
diff --git a/llama.cpp/tools/server/webui/docs/flows/data-flow-simplified-router-mode.md b/llama.cpp/tools/server/webui/docs/flows/data-flow-simplified-router-mode.md
new file mode 100644
index 0000000..bccacf5
--- /dev/null
+++ b/llama.cpp/tools/server/webui/docs/flows/data-flow-simplified-router-mode.md
@@ -0,0 +1,77 @@
+```mermaid
+%% ROUTER Mode Data Flow (multi-model)
+%% Detailed flows: ./flows/server-flow.mmd, ./flows/models-flow.mmd, ./flows/chat-flow.mmd
+
+sequenceDiagram
+ participant User as 👤 User
+ participant UI as 🧩 UI
+ participant Stores as đŸ—„ī¸ Stores
+ participant DB as 💾 IndexedDB
+ participant API as 🌐 llama-server
+
+ Note over User,API: 🚀 Initialization (see: server-flow.mmd, models-flow.mmd)
+
+ UI->>Stores: initialize()
+ Stores->>DB: load conversations
+ Stores->>API: GET /props
+ API-->>Stores: {role: "router"}
+ Stores->>API: GET /v1/models
+ API-->>Stores: models[] with status (loaded/available)
+ loop each loaded model
+ Stores->>API: GET /props?model=X
+ API-->>Stores: modalities (vision/audio)
+ end
+
+ Note over User,API: 🔄 Model Selection (see: models-flow.mmd)
+
+ User->>UI: select model
+ alt model not loaded
+ Stores->>API: POST /models/load
+ loop poll status
+ Stores->>API: GET /v1/models
+ API-->>Stores: check if loaded
+ end
+ Stores->>API: GET /props?model=X
+ API-->>Stores: cache modalities
+ end
+ Stores->>Stores: validate modalities vs conversation
+ alt valid
+ Stores->>Stores: select model
+ else invalid
+ Stores->>API: POST /models/unload
+ UI->>User: show error toast
+ end
+
+ Note over User,API: đŸ’Ŧ Chat Flow (see: chat-flow.mmd)
+
+ User->>UI: send message
+ UI->>Stores: sendMessage()
+ Stores->>DB: save user message
+ Stores->>API: POST /v1/chat/completions {model: X}
+ Note right of API: router forwards to model
+ loop streaming
+ API-->>Stores: SSE chunks + model info
+ Stores-->>UI: reactive update
+ end
+ API-->>Stores: done + timings
+ Stores->>DB: save assistant message + model used
+
+ Note over User,API: 🔁 Regenerate (optional: different model)
+
+ User->>UI: regenerate
+ Stores->>Stores: validate modalities up to this message
+ Stores->>DB: create message branch
+ Note right of Stores: same streaming flow
+
+ Note over User,API: âšī¸ Stop
+
+ User->>UI: stop
+ Stores->>Stores: abort stream
+ Stores->>DB: save partial response
+
+ Note over User,API: đŸ—‘ī¸ LRU Unloading
+
+ Note right of API: Server auto-unloads LRU models<br/>when cache full
+ User->>UI: select unloaded model
+ Note right of Stores: triggers load flow again
+```
diff --git a/llama.cpp/tools/server/webui/docs/flows/database-flow.md b/llama.cpp/tools/server/webui/docs/flows/database-flow.md
new file mode 100644
index 0000000..50f8284
--- /dev/null
+++ b/llama.cpp/tools/server/webui/docs/flows/database-flow.md
@@ -0,0 +1,155 @@
+```mermaid
+sequenceDiagram
+ participant Store as đŸ—„ī¸ Stores
+ participant DbSvc as âš™ī¸ DatabaseService
+ participant Dexie as đŸ“Ļ Dexie ORM
+ participant IDB as 💾 IndexedDB
+
+ Note over DbSvc: Stateless service - all methods static<br/>Database: "LlamacppWebui"
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over Store,IDB: 📊 SCHEMA
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ rect rgb(240, 248, 255)
+ Note over IDB: conversations table:<br/>id (PK), lastModified, currNode, name
+ end
+
+ rect rgb(255, 248, 240)
+ Note over IDB: messages table:<br/>id (PK), convId (FK), type, role, timestamp,<br/>parent, children[], content, thinking,<br/>toolCalls, extra[], model, timings
+ end
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over Store,IDB: đŸ’Ŧ CONVERSATIONS CRUD
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Store->>DbSvc: createConversation(name)
+ activate DbSvc
+ DbSvc->>DbSvc: Generate UUID
+ DbSvc->>Dexie: db.conversations.add({id, name, lastModified, currNode: ""})
+ Dexie->>IDB: INSERT
+ IDB-->>Dexie: success
+ DbSvc-->>Store: DatabaseConversation
+ deactivate DbSvc
+
+ Store->>DbSvc: getConversation(convId)
+ DbSvc->>Dexie: db.conversations.get(convId)
+ Dexie->>IDB: SELECT WHERE id = ?
+ IDB-->>DbSvc: DatabaseConversation
+
+ Store->>DbSvc: getAllConversations()
+ DbSvc->>Dexie: db.conversations.orderBy('lastModified').reverse().toArray()
+ Dexie->>IDB: SELECT ORDER BY lastModified DESC
+ IDB-->>DbSvc: DatabaseConversation[]
+
+ Store->>DbSvc: updateConversation(convId, updates)
+ DbSvc->>Dexie: db.conversations.update(convId, {...updates, lastModified})
+ Dexie->>IDB: UPDATE
+
+ Store->>DbSvc: deleteConversation(convId)
+ activate DbSvc
+ DbSvc->>Dexie: db.conversations.delete(convId)
+ Dexie->>IDB: DELETE FROM conversations
+ DbSvc->>Dexie: db.messages.where('convId').equals(convId).delete()
+ Dexie->>IDB: DELETE FROM messages WHERE convId = ?
+ deactivate DbSvc
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over Store,IDB: 📝 MESSAGES CRUD
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Store->>DbSvc: createRootMessage(convId)
+ activate DbSvc
+ DbSvc->>DbSvc: Create root message {type: "root", parent: null}
+ DbSvc->>Dexie: db.messages.add(rootMsg)
+ Dexie->>IDB: INSERT
+ DbSvc-->>Store: rootMessageId
+ deactivate DbSvc
+
+ Store->>DbSvc: createMessageBranch(message, parentId)
+ activate DbSvc
+ DbSvc->>DbSvc: Generate UUID for new message
+ DbSvc->>Dexie: db.messages.add({...message, id, parent: parentId})
+ Dexie->>IDB: INSERT message
+
+ alt parentId exists
+ DbSvc->>Dexie: db.messages.get(parentId)
+ Dexie->>IDB: SELECT parent
+ DbSvc->>DbSvc: parent.children.push(newId)
+ DbSvc->>Dexie: db.messages.update(parentId, {children})
+ Dexie->>IDB: UPDATE parent.children
+ end
+
+ DbSvc->>Dexie: db.conversations.update(convId, {currNode: newId})
+ Dexie->>IDB: UPDATE conversation.currNode
+ DbSvc-->>Store: DatabaseMessage
+ deactivate DbSvc
+
+ Store->>DbSvc: getConversationMessages(convId)
+ DbSvc->>Dexie: db.messages.where('convId').equals(convId).toArray()
+ Dexie->>IDB: SELECT WHERE convId = ?
+ IDB-->>DbSvc: DatabaseMessage[]
+
+ Store->>DbSvc: updateMessage(msgId, updates)
+ DbSvc->>Dexie: db.messages.update(msgId, updates)
+ Dexie->>IDB: UPDATE
+
+ Store->>DbSvc: deleteMessage(msgId)
+ DbSvc->>Dexie: db.messages.delete(msgId)
+ Dexie->>IDB: DELETE
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over Store,IDB: đŸŒŗ BRANCHING OPERATIONS
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Store->>DbSvc: updateCurrentNode(convId, nodeId)
+ DbSvc->>Dexie: db.conversations.update(convId, {currNode: nodeId, lastModified})
+ Dexie->>IDB: UPDATE
+
+ Store->>DbSvc: deleteMessageCascading(msgId)
+ activate DbSvc
+ DbSvc->>DbSvc: findDescendantMessages(msgId, allMessages)
+ Note right of DbSvc: Recursively find all children
+ loop each descendant
+ DbSvc->>Dexie: db.messages.delete(descendantId)
+ Dexie->>IDB: DELETE
+ end
+ DbSvc->>Dexie: db.messages.delete(msgId)
+ Dexie->>IDB: DELETE target message
+ deactivate DbSvc
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over Store,IDB: đŸ“Ĩ IMPORT
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Store->>DbSvc: importConversations(data)
+ activate DbSvc
+ loop each conversation in data
+ DbSvc->>DbSvc: Generate new UUIDs (avoid conflicts)
+ DbSvc->>Dexie: db.conversations.add(conversation)
+ Dexie->>IDB: INSERT conversation
+ loop each message
+ DbSvc->>Dexie: db.messages.add(message)
+ Dexie->>IDB: INSERT message
+ end
+ end
+ deactivate DbSvc
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over Store,IDB: 🔗 MESSAGE TREE UTILITIES
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over DbSvc: Used by stores (imported from utils):
+
+ rect rgb(240, 255, 240)
+ Note over DbSvc: filterByLeafNodeId(messages, leafId)<br/>→ Returns path from root to leaf<br/>→ Used to display current branch
+ end
+
+ rect rgb(240, 255, 240)
+ Note over DbSvc: findLeafNode(startId, messages)<br/>→ Traverse to deepest child<br/>→ Used for branch navigation
+ end
+
+ rect rgb(240, 255, 240)
+ Note over DbSvc: findDescendantMessages(msgId, messages)<br/>→ Find all children recursively<br/>→ Used for cascading deletes
+ end
+```
diff --git a/llama.cpp/tools/server/webui/docs/flows/models-flow.md b/llama.cpp/tools/server/webui/docs/flows/models-flow.md
new file mode 100644
index 0000000..c3031b7
--- /dev/null
+++ b/llama.cpp/tools/server/webui/docs/flows/models-flow.md
@@ -0,0 +1,181 @@
+```mermaid
+sequenceDiagram
+ participant UI as 🧩 ModelsSelector
+ participant Hooks as đŸĒ useModelChangeValidation
+ participant modelsStore as đŸ—„ī¸ modelsStore
+ participant serverStore as đŸ—„ī¸ serverStore
+ participant convStore as đŸ—„ī¸ conversationsStore
+ participant ModelsSvc as âš™ī¸ ModelsService
+ participant PropsSvc as âš™ī¸ PropsService
+ participant API as 🌐 llama-server
+
+ Note over modelsStore: State:<br/>models: ModelOption[]<br/>routerModels: ApiModelDataEntry[]<br/>selectedModelId, selectedModelName<br/>loading, updating, error<br/>modelLoadingStates (Map)<br/>modelPropsCache (Map)<br/>propsCacheVersion
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: 🚀 INITIALIZATION (MODEL mode)
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>modelsStore: fetch()
+ activate modelsStore
+ modelsStore->>modelsStore: loading = true
+
+ alt serverStore.props not loaded
+ modelsStore->>serverStore: fetch()
+ Note over serverStore: → see server-flow.mmd
+ end
+
+ modelsStore->>ModelsSvc: list()
+ ModelsSvc->>API: GET /v1/models
+ API-->>ModelsSvc: ApiModelListResponse {data: [model]}
+
+ modelsStore->>modelsStore: models = $state(mapped)
+ Note right of modelsStore: Map to ModelOption[]:<br/>{id, name, model, description, capabilities}
+
+ Note over modelsStore: MODEL mode: Get modalities from serverStore.props
+ modelsStore->>modelsStore: modelPropsCache.set(model.id, serverStore.props)
+ modelsStore->>modelsStore: models[0].modalities = props.modalities
+
+ modelsStore->>modelsStore: Auto-select single model
+ Note right of modelsStore: selectedModelId = models[0].id
+ modelsStore->>modelsStore: loading = false
+ deactivate modelsStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: 🚀 INITIALIZATION (ROUTER mode)
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>modelsStore: fetch()
+ activate modelsStore
+ modelsStore->>ModelsSvc: list()
+ ModelsSvc->>API: GET /v1/models
+ API-->>ModelsSvc: ApiModelListResponse
+ modelsStore->>modelsStore: models = $state(mapped)
+ deactivate modelsStore
+
+ Note over UI: After models loaded, layout triggers:
+ UI->>modelsStore: fetchRouterModels()
+ activate modelsStore
+ modelsStore->>ModelsSvc: listRouter()
+ ModelsSvc->>API: GET /v1/models
+ API-->>ModelsSvc: ApiRouterModelsListResponse
+ Note right of API: {data: [{id, status, path, in_cache}]}
+ modelsStore->>modelsStore: routerModels = $state(data)
+
+ modelsStore->>modelsStore: fetchModalitiesForLoadedModels()
+ loop each model where status === "loaded"
+ modelsStore->>PropsSvc: fetchForModel(modelId)
+ PropsSvc->>API: GET /props?model={modelId}
+ API-->>PropsSvc: ApiLlamaCppServerProps
+ modelsStore->>modelsStore: modelPropsCache.set(modelId, props)
+ end
+ modelsStore->>modelsStore: propsCacheVersion++
+ deactivate modelsStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: 🔄 MODEL SELECTION (ROUTER mode)
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>Hooks: useModelChangeValidation({getRequiredModalities, onSuccess?, onValidationFailure?})
+ Note over Hooks: Hook configured per-component:<br/>ChatForm: getRequiredModalities = usedModalities<br/>ChatMessage: getRequiredModalities = getModalitiesUpToMessage(msgId)
+
+ UI->>Hooks: handleModelChange(modelId, modelName)
+ activate Hooks
+ Hooks->>Hooks: previousSelectedModelId = modelsStore.selectedModelId
+ Hooks->>modelsStore: isModelLoaded(modelName)?
+
+ alt model NOT loaded
+ Hooks->>modelsStore: loadModel(modelName)
+ Note over modelsStore: → see LOAD MODEL section below
+ end
+
+ Note over Hooks: Always fetch props (from cache or API)
+ Hooks->>modelsStore: fetchModelProps(modelName)
+ modelsStore-->>Hooks: props
+
+ Hooks->>convStore: getRequiredModalities()
+ convStore-->>Hooks: {vision, audio}
+
+ Hooks->>Hooks: Validate: model.modalities ⊇ required?
+
+ alt validation PASSED
+ Hooks->>modelsStore: selectModelById(modelId)
+ Hooks-->>UI: return true
+ else validation FAILED
+ Hooks->>UI: toast.error("Model doesn't support required modalities")
+ alt model was just loaded
+ Hooks->>modelsStore: unloadModel(modelName)
+ end
+ alt onValidationFailure provided
+ Hooks->>modelsStore: selectModelById(previousSelectedModelId)
+ end
+ Hooks-->>UI: return false
+ end
+ deactivate Hooks
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: âŦ†ī¸ LOAD MODEL (ROUTER mode)
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ modelsStore->>modelsStore: loadModel(modelId)
+ activate modelsStore
+
+ alt already loaded
+ modelsStore-->>modelsStore: return (no-op)
+ end
+
+ modelsStore->>modelsStore: modelLoadingStates.set(modelId, true)
+ modelsStore->>ModelsSvc: load(modelId)
+ ModelsSvc->>API: POST /models/load {model: modelId}
+ API-->>ModelsSvc: {status: "loading"}
+
+ modelsStore->>modelsStore: pollForModelStatus(modelId, LOADED)
+ loop poll every 500ms (max 60 attempts)
+ modelsStore->>modelsStore: fetchRouterModels()
+ modelsStore->>ModelsSvc: listRouter()
+ ModelsSvc->>API: GET /v1/models
+ API-->>ModelsSvc: models[]
+ modelsStore->>modelsStore: getModelStatus(modelId)
+ alt status === LOADED
+ Note right of modelsStore: break loop
+ else status === LOADING
+ Note right of modelsStore: wait 500ms, continue
+ end
+ end
+
+ modelsStore->>modelsStore: updateModelModalities(modelId)
+ modelsStore->>PropsSvc: fetchForModel(modelId)
+ PropsSvc->>API: GET /props?model={modelId}
+ API-->>PropsSvc: props with modalities
+ modelsStore->>modelsStore: modelPropsCache.set(modelId, props)
+ modelsStore->>modelsStore: propsCacheVersion++
+
+ modelsStore->>modelsStore: modelLoadingStates.set(modelId, false)
+ deactivate modelsStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: âŦ‡ī¸ UNLOAD MODEL (ROUTER mode)
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ modelsStore->>modelsStore: unloadModel(modelId)
+ activate modelsStore
+ modelsStore->>modelsStore: modelLoadingStates.set(modelId, true)
+ modelsStore->>ModelsSvc: unload(modelId)
+ ModelsSvc->>API: POST /models/unload {model: modelId}
+
+ modelsStore->>modelsStore: pollForModelStatus(modelId, UNLOADED)
+ loop poll until unloaded
+ modelsStore->>ModelsSvc: listRouter()
+ ModelsSvc->>API: GET /v1/models
+ end
+
+ modelsStore->>modelsStore: modelLoadingStates.set(modelId, false)
+ deactivate modelsStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: 📊 COMPUTED GETTERS
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over modelsStore: Getters:<br/>- selectedModel: ModelOption | null<br/>- loadedModelIds: string[] (from routerModels)<br/>- loadingModelIds: string[] (from modelLoadingStates)<br/>- singleModelName: string | null (MODEL mode only)
+
+ Note over modelsStore: Modality helpers:<br/>- getModelModalities(modelId): {vision, audio}<br/>- modelSupportsVision(modelId): boolean<br/>- modelSupportsAudio(modelId): boolean
+```
diff --git a/llama.cpp/tools/server/webui/docs/flows/server-flow.md b/llama.cpp/tools/server/webui/docs/flows/server-flow.md
new file mode 100644
index 0000000..d6a1611
--- /dev/null
+++ b/llama.cpp/tools/server/webui/docs/flows/server-flow.md
@@ -0,0 +1,76 @@
+```mermaid
+sequenceDiagram
+ participant UI as 🧩 +layout.svelte
+ participant serverStore as đŸ—„ī¸ serverStore
+ participant PropsSvc as âš™ī¸ PropsService
+ participant API as 🌐 llama-server
+
+ Note over serverStore: State:<br/>props: ApiLlamaCppServerProps | null<br/>loading, error<br/>role: ServerRole | null (MODEL | ROUTER)<br/>fetchPromise (deduplication)
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: 🚀 INITIALIZATION
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>serverStore: fetch()
+ activate serverStore
+
+ alt fetchPromise exists (already fetching)
+ serverStore-->>UI: return fetchPromise
+ Note right of serverStore: Deduplicate concurrent calls
+ end
+
+ serverStore->>serverStore: loading = true
+ serverStore->>serverStore: fetchPromise = new Promise()
+
+ serverStore->>PropsSvc: fetch()
+ PropsSvc->>API: GET /props
+ API-->>PropsSvc: ApiLlamaCppServerProps
+ Note right of API: {role, model_path, model_alias,<br/>modalities, default_generation_settings, ...}
+
+ PropsSvc-->>serverStore: props
+ serverStore->>serverStore: props = $state(data)
+
+ serverStore->>serverStore: detectRole(props)
+ Note right of serverStore: role = props.role === "router"<br/> ? ServerRole.ROUTER<br/> : ServerRole.MODEL
+
+ serverStore->>serverStore: loading = false
+ serverStore->>serverStore: fetchPromise = null
+ deactivate serverStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: 📊 COMPUTED GETTERS
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over serverStore: Getters from props:
+
+ rect rgb(240, 255, 240)
+ Note over serverStore: defaultParams<br/>→ props.default_generation_settings.params<br/>(temperature, top_p, top_k, etc.)
+ end
+
+ rect rgb(240, 255, 240)
+ Note over serverStore: contextSize<br/>→ props.default_generation_settings.n_ctx
+ end
+
+ rect rgb(255, 240, 240)
+ Note over serverStore: isRouterMode<br/>→ role === ServerRole.ROUTER
+ end
+
+ rect rgb(255, 240, 240)
+ Note over serverStore: isModelMode<br/>→ role === ServerRole.MODEL
+ end
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: 🔗 RELATIONSHIPS
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over serverStore: Used by:
+ Note right of serverStore: - modelsStore: role detection, MODEL mode modalities<br/>- settingsStore: syncWithServerDefaults (defaultParams)<br/>- chatStore: contextSize for processing state<br/>- UI components: isRouterMode for conditional rendering
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,API: ❌ ERROR HANDLING
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over serverStore: getErrorMessage(): string | null<br/>Returns formatted error for UI display
+
+ Note over serverStore: clear(): void<br/>Resets all state (props, error, loading, role)
+```
diff --git a/llama.cpp/tools/server/webui/docs/flows/settings-flow.md b/llama.cpp/tools/server/webui/docs/flows/settings-flow.md
new file mode 100644
index 0000000..578e01e
--- /dev/null
+++ b/llama.cpp/tools/server/webui/docs/flows/settings-flow.md
@@ -0,0 +1,144 @@
+```mermaid
+sequenceDiagram
+ participant UI as 🧩 ChatSettings
+ participant settingsStore as đŸ—„ī¸ settingsStore
+ participant serverStore as đŸ—„ī¸ serverStore
+ participant ParamSvc as âš™ī¸ ParameterSyncService
+ participant LS as 💾 LocalStorage
+
+ Note over settingsStore: State:<br/>config: SettingsConfigType<br/>theme: string ("auto" | "light" | "dark")<br/>isInitialized: boolean<br/>userOverrides: Set&lt;string&gt;
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,LS: 🚀 INITIALIZATION
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over settingsStore: Auto-initialized in constructor (browser only)
+ settingsStore->>settingsStore: initialize()
+ activate settingsStore
+
+ settingsStore->>settingsStore: loadConfig()
+ settingsStore->>LS: get("llama-config")
+ LS-->>settingsStore: StoredConfig | null
+
+ alt config exists
+ settingsStore->>settingsStore: Merge with SETTING_CONFIG_DEFAULT
+ Note right of settingsStore: Fill missing keys with defaults
+ else no config
+ settingsStore->>settingsStore: config = SETTING_CONFIG_DEFAULT
+ end
+
+ settingsStore->>LS: get("llama-userOverrides")
+ LS-->>settingsStore: string[] | null
+ settingsStore->>settingsStore: userOverrides = new Set(data)
+
+ settingsStore->>settingsStore: loadTheme()
+ settingsStore->>LS: get("llama-theme")
+ LS-->>settingsStore: theme | "auto"
+
+ settingsStore->>settingsStore: isInitialized = true
+ deactivate settingsStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,LS: 🔄 SYNC WITH SERVER DEFAULTS
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over UI: Triggered from +layout.svelte when serverStore.props loaded
+ UI->>settingsStore: syncWithServerDefaults()
+ activate settingsStore
+
+ settingsStore->>serverStore: defaultParams
+ serverStore-->>settingsStore: {temperature, top_p, top_k, ...}
+
+ settingsStore->>ParamSvc: extractServerDefaults(defaultParams)
+ ParamSvc-->>settingsStore: Record<string, value>
+
+ settingsStore->>ParamSvc: mergeWithServerDefaults(config, serverDefaults)
+ Note right of ParamSvc: For each syncable parameter:<br/>- If NOT in userOverrides → use server default<br/>- If in userOverrides → keep user value
+ ParamSvc-->>settingsStore: mergedConfig
+
+ settingsStore->>settingsStore: config = mergedConfig
+ settingsStore->>settingsStore: saveConfig()
+ deactivate settingsStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,LS: âš™ī¸ UPDATE CONFIG
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>settingsStore: updateConfig(key, value)
+ activate settingsStore
+ settingsStore->>settingsStore: config[key] = value
+ settingsStore->>settingsStore: userOverrides.add(key)
+ Note right of settingsStore: Mark as user-modified (won't be overwritten by server)
+ settingsStore->>settingsStore: saveConfig()
+ settingsStore->>LS: set("llama-config", config)
+ settingsStore->>LS: set("llama-userOverrides", [...userOverrides])
+ deactivate settingsStore
+
+ UI->>settingsStore: updateMultipleConfig({key1: val1, key2: val2})
+ activate settingsStore
+ Note right of settingsStore: Batch update, single save
+ settingsStore->>settingsStore: For each key: config[key] = value
+ settingsStore->>settingsStore: For each key: userOverrides.add(key)
+ settingsStore->>settingsStore: saveConfig()
+ deactivate settingsStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,LS: 🔄 RESET
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>settingsStore: resetConfig()
+ activate settingsStore
+ settingsStore->>settingsStore: config = SETTING_CONFIG_DEFAULT
+ settingsStore->>settingsStore: userOverrides.clear()
+ settingsStore->>settingsStore: syncWithServerDefaults()
+ Note right of settingsStore: Apply server defaults for syncable params
+ settingsStore->>settingsStore: saveConfig()
+ deactivate settingsStore
+
+ UI->>settingsStore: resetParameterToServerDefault(key)
+ activate settingsStore
+ settingsStore->>settingsStore: userOverrides.delete(key)
+ settingsStore->>serverStore: defaultParams[key]
+ settingsStore->>settingsStore: config[key] = serverDefault
+ settingsStore->>settingsStore: saveConfig()
+ deactivate settingsStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,LS: 🎨 THEME
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>settingsStore: updateTheme(newTheme)
+ activate settingsStore
+ settingsStore->>settingsStore: theme = newTheme
+ settingsStore->>settingsStore: saveTheme()
+ settingsStore->>LS: set("llama-theme", theme)
+ deactivate settingsStore
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,LS: 📊 PARAMETER INFO
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ UI->>settingsStore: getParameterInfo(key)
+ settingsStore->>ParamSvc: getParameterInfo(key, config, serverDefaults, userOverrides)
+ ParamSvc-->>settingsStore: ParameterInfo
+ Note right of ParamSvc: {<br/> currentValue,<br/> serverDefault,<br/> isUserOverride: boolean,<br/> canSync: boolean,<br/> isDifferentFromServer: boolean<br/>}
+
+ UI->>settingsStore: getParameterDiff()
+ settingsStore->>ParamSvc: createParameterDiff(config, serverDefaults, userOverrides)
+ ParamSvc-->>settingsStore: ParameterDiff[]
+ Note right of ParamSvc: Array of parameters where user != server
+
+ %% ═══════════════════════════════════════════════════════════════════════════
+ Note over UI,LS: 📋 CONFIG CATEGORIES
+ %% ═══════════════════════════════════════════════════════════════════════════
+
+ Note over settingsStore: Syncable with server (from /props):
+ rect rgb(240, 255, 240)
+ Note over settingsStore: temperature, top_p, top_k, min_p<br/>repeat_penalty, presence_penalty, frequency_penalty<br/>dynatemp_range, dynatemp_exponent<br/>typ_p, xtc_probability, xtc_threshold<br/>dry_multiplier, dry_base, dry_allowed_length, dry_penalty_last_n
+ end
+
+ Note over settingsStore: UI-only (not synced):
+ rect rgb(255, 240, 240)
+ Note over settingsStore: systemMessage, custom (JSON)<br/>showStatistics, enableContinueGeneration<br/>autoMicOnEmpty, disableAutoScroll<br/>apiKey, pdfAsImage, disableReasoningFormat
+ end
+```