import { atom, getDefaultStore } from "jotai" import { atomWithStorage } from "jotai/utils" import { ASSISTANT_DETAIL_VISIBILITY_STORAGE_KEY, type AssistantDetailVisibility, DEFAULT_ASSISTANT_DETAIL_VISIBILITY, assistantDetailVisibilityStorage, shouldShowAssistantMessage, } from "@/features/chat/detail-visibility" import { getInitialActiveSessionId, writeStoredSessionId, } from "@/features/chat/state" export interface ChatAttachment { type: "image" | "audio" | "video" | "file" url: string filename?: string contentType?: string } export interface ChatToolCallFunction { name?: string arguments?: string } export interface ChatToolCallExtraContent { toolFeedbackExplanation?: string } export interface ChatToolCall { id?: string type?: string function?: ChatToolCallFunction extraContent?: ChatToolCallExtraContent } export type AssistantMessageKind = "normal" | "thought" | "tool_calls" export interface ChatMessage { id: string role: "user" | "assistant" content: string timestamp: number | string kind?: AssistantMessageKind modelName?: string attachments?: ChatAttachment[] toolCalls?: ChatToolCall[] } export interface ContextUsage { used_tokens: number total_tokens: number compress_at_tokens: number summarize_at_tokens?: number used_percent: number } export type ConnectionState = | "disconnected" | "connecting" | "connected" | "error" export interface ChatStoreState { messages: ChatMessage[] connectionState: ConnectionState isTyping: boolean activeSessionId: string hasHydratedActiveSession: boolean contextUsage?: ContextUsage } type ChatStorePatch = Partial const DEFAULT_CHAT_STATE: ChatStoreState = { messages: [], connectionState: "disconnected", isTyping: false, activeSessionId: getInitialActiveSessionId(), hasHydratedActiveSession: false, } export const chatAtom = atom(DEFAULT_CHAT_STATE) export const assistantDetailVisibilityAtom = atomWithStorage( ASSISTANT_DETAIL_VISIBILITY_STORAGE_KEY, DEFAULT_ASSISTANT_DETAIL_VISIBILITY, assistantDetailVisibilityStorage, { getOnInit: true }, ) export const showAssistantDetailsAtom = atom( (get) => get(assistantDetailVisibilityAtom) !== "none", ) const store = getDefaultStore() export function getChatState() { return store.get(chatAtom) } export function updateChatStore( patch: | ChatStorePatch | ((prev: ChatStoreState) => ChatStorePatch | ChatStoreState), ) { store.set(chatAtom, (prev) => { const nextPatch = typeof patch === "function" ? patch(prev) : patch const next = { ...prev, ...nextPatch } if (next.activeSessionId !== prev.activeSessionId) { writeStoredSessionId(next.activeSessionId) } return next }) } export { shouldShowAssistantMessage, DEFAULT_ASSISTANT_DETAIL_VISIBILITY } export type { AssistantDetailVisibility }