import { ref, shallowRef } from 'vue'
/**
* State of the chat store: the refs, maps and sets the area modules share.
* Created once per store instance.
*
* `toolIndex` and the two underscore sets are deliberately non-reactive. The
* index must hold the array's reactive proxy (a write to a raw copy never
* wakes a computed that read the card through the proxy), and a late event
* arriving after the turn closed must still find its card and its task.
*/
export function createChatState() {
return {
currentId: ref(null),
currentProfileId: ref(null),
messages: ref([]),
streaming: ref(false),
pendingImages: ref([]), // base64 strings
pendingFiles: ref([]), // { name, path, size, content_type }
artifacts: ref([]), // published session content
files: ref([]), // session directory tree
contextTokens: ref(0),
maxContextTokens: ref(0),
// The model (and llm.json target) that answered the current turn, from the
// model_info event. `servedFallback` is true when it is NOT the route's
// primary — the one case the UI has to say out loud: a backup host is
// carrying the conversation, so answers differ in style and window.
servedModel: ref(null),
servedTarget: ref(null),
servedFallback: ref(false),
// WebSocket liveness, mirrored from useWebSocket so the session-info dialog
// can show it without reaching into ChatArea's composable instance.
wsConnected: ref(false),
wsReconnecting: ref(false),
loading: ref(false),
// Why the last session load failed (translated, ready to show), and the
// session it was for — the retry button reopens exactly that one. A failed
// load keeps the previous transcript in place instead of blanking the page.
loadError: ref(null),
loadErrorSessionId: ref(null),
// Images currently being read/resized (FileReader + canvas) — no preview yet
imagesProcessing: ref(0),
// Message sent over WS but the run hasn't started (stream_start not yet seen)
awaitingStream: ref(false),
// The in-progress streaming message (direct ref for perf)
streamingMsg: shallowRef(null),
// True while replaying buffered events from a reconnect — suppresses animations
replayMode: ref(false),
// Index of the message to scroll to after loading a session from search results
scrollToMessageIndex: ref(null),
// Current scheduled recall for the active session (null if none)
recall: ref(null),
// Persistent terminal output keyed by terminal name
terminals: ref({}),
// Live background tasks keyed by task_id (from out-of-band task_update events)
backgroundTasks: ref({}),
// tool_call_id → tool card. Non-reactive index for id-first matching —
// works even for events that arrive after stream_end closed the turn.
toolIndex: new Map(),
// Images Navi itself looked at (image_view and friends): captured live from
// tool_call metadata and from history. Rendered as a group in the artifacts
// panel, not as chat bubbles.
viewedImages: ref([]),
_viewedImageKeys: new Set(),
// Task ids whose terminal task_update (completed/failed/cancelled) was seen
// — a late duplicate 'running' must not resurrect the chip (out-of-order
// delivery would otherwise leave a "ghost" task running forever).
_terminalTaskIds: new Set(),
// Archive (older messages) pagination state
archiveLoading: ref(false),
archiveHasMore: ref(false),
archiveNextBefore: ref(null),
// Message count from the last full history load — the cheap fingerprint that
// tells whether another device advanced the session. Compared against
// GET /sessions/{id}/meta when the tab comes back to the foreground.
historyCount: ref(0),
// The user scrolled into older pages: a silent reload would throw them away
// (and jump the scroll position), so automatic catch-up stays off for the
// rest of this session view.
archiveExtended: ref(false),
}
}