ClawDesk uses Zustand for state management with the persist middleware for selective localStorage persistence. The store is defined in src/store/index.ts and accessed via the useStore() hook.
These fields survive page reloads (stored in localStorage key clawcontrol-storage):
Field
Type
Default
Description
theme
'dark' | 'light'
'dark'
UI color theme
serverUrl
string
''
WebSocket server URL
authMode
'token' | 'password'
'token'
Authentication method
insecureAuth
boolean
false
Skip device identity
sidebarCollapsed
boolean
false
Sidebar collapsed state
collapsedSessionGroups
string[]
[]
Collapsed date groups
thinkingEnabled
boolean
false
Extended thinking mode
notificationsEnabled
boolean
false
Desktop notifications
rightPanelWidth
number
320
Right panel width (px)
serverProfiles
ServerProfile[]
[]
Saved server connection profiles
activeProfileId
string | null
null
Currently active server profile
Note:gatewayToken is NOT persisted in Zustand. It’s stored in platform-specific secure storage (Electron safeStorage / Capacitor Preferences). Each server profile stores its own token independently.
Derived selectors for the current session (avoid unnecessary re-renders):
import{
selectIsStreaming,// Is current session streaming?
selectHadStreamChunks,// Did current session receive chunks?
selectActiveToolCalls,// Tool calls for current session
selectStreamingThinking,// Thinking text for current session
selectIsCompacting // Is current session being compacted?}from'../store'// Usage in component:const isStreaming =useStore(selectIsStreaming)
1. User types message in InputArea
2. sendMessage() called:
a. Creates session if currentSessionId is null
b. Sets primary session key on client
c. Resets streaming state for session
d. Adds user message to store immediately
e. Calls client.sendMessage() via WebSocket
3. Server starts streaming:
a. streamStart event -> mark session as streaming
b. streamChunk events -> append to streaming-{timestamp} placeholder
c. toolCall events -> finalize current message, add tool call
d. More streamChunk events -> create new streaming message
e. message event (final) -> replace placeholder with canonical message
f. streamEnd event -> clear streaming state
4. Session switch:
a. Cache current session's messages to _sessionMessagesCache
b. Load cached messages for new session (instant)
c. Fetch fresh messages from server (async)
d. Guard against stale loads via _sessionLoadVersion