156 lines
9.8 KiB
Markdown
156 lines
9.8 KiB
Markdown
```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
|
||
```
|