docs/session-module-split-plan.md
Purpose: Document the dependency analysis and proposed module split for src/renderer/stores/sessionActions.ts (1799 lines) to enable safe refactoring without circular imports.
The sessionActions.ts file has grown to 1799 lines and handles multiple responsibilities:
The file contains two shared state objects that must be moved to a central location:
// Line 1054-1055
const pendingNameGenerations = new Map<string, ReturnType<typeof setTimeout>>()
const activeNameGenerations = new Set<string>()
Purpose: Debounce and deduplicate name generation requests
Used by: scheduleGenerateNameAndThreadName, scheduleGenerateThreadName
Strategy: Move to stores/session/state.ts and import where needed
submitNewUserMessage
└── insertMessage
└── insertMessageAfter
└── modifyMessage
└── generate (internal)
└── genMessageContext
└── streamText (external)
└── generateImage (external)
└── modifyMessage
└── trackGenerateEvent (internal)
generateMore
└── insertMessageAfter
└── generate (internal)
generateMoreInNewFork
└── createNewFork
└── generateMore
regenerateInNewFork
└── findMessageLocation (internal)
└── createNewFork
└── generateMore (or passed in runGenerateMore)
└── generate (internal, fallback)
scheduleGenerateNameAndThreadName
└── generateNameAndThreadName (internal)
└── _generateName (internal)
└── modifyNameAndThreadName
scheduleGenerateThreadName
└── generateThreadName (internal)
└── _generateName (internal)
└── modifyThreadName
createNewFork / switchFork / deleteFork / expandFork
└── buildCreateForkPatch / buildSwitchForkPatch / buildDeleteForkPatch / buildExpandForkPatch (internal)
└── applyForkTransform (internal)
└── switchForkInMessages (internal, for switchFork only)
└── computeNextMessageForksHash (internal)
startNewThread
└── refreshContextAndCreateNewThread
moveThreadToConversations
└── copySession (internal)
└── removeThread
└── switchCurrentSession
moveCurrentThreadToConversations
└── copySession (internal)
└── removeCurrentThread
└── switchCurrentSession
| Import | Used By |
|---|---|
@dnd-kit/sortable (arrayMove) | reorderSessions |
@sentry/react | submitNewUserMessage, generate, _generateName |
@shared/defaults | refreshContextAndCreateNewThread, compressAndCreateThread |
@shared/models (getModel) | submitNewUserMessage, generate, _generateName |
jotai (getDefaultStore) | switchCurrentSession, switchToNext |
lodash (identity, omit, pickBy) | copySession, generate |
uuid (uuidv4) | refreshContextAndCreateNewThread, switchThread, compressAndCreateThread, fork operations |
@/adapters (createModelDependencies) | submitNewUserMessage, generate, _generateName |
@/hooks/dom | startNewThread, compressAndCreateThread |
@/i18n/locales | _generateName |
@/packages/apple_app_store | generate |
@/packages/context-management | submitNewUserMessage, genMessageContext |
@/packages/model-calls | generate, _generateName |
@/packages/model-setting-utils | submitNewUserMessage, generate |
@/packages/token | insertMessage, insertMessageAfter, modifyMessage, genMessageContext |
@/router | switchCurrentSession |
@/storage/StoreStorage | generate |
@/utils/session-utils | reorderSessions |
@/utils/track | trackGenerateEvent |
@shared/models/errors | submitNewUserMessage, generate |
@shared/types | Various (type imports) |
@shared/utils/message | Various message operations |
../packages/prompts | _generateName |
../platform | submitNewUserMessage, generate, _generateName |
../storage | generate, genMessageContext |
./atoms | switchCurrentSession, switchToNext |
./chatStore | Most operations |
./scrollActions | switchCurrentSession, startNewThread, compressAndCreateThread, switchThread |
./sessionHelpers | createEmpty, refreshContextAndCreateNewThread, compressAndCreateThread, exportSessionChat |
./settingActions | submitNewUserMessage |
./settingsStore | generate, _generateName |
./uiStore | getSessionWebBrowsing, generate |
stores/session/state.tsShared module state (no dependencies on other session modules):
export const pendingNameGenerations = new Map<string, ReturnType<typeof setTimeout>>()
export const activeNameGenerations = new Set<string>()
stores/session/types.tsInternal types used across modules:
export type MessageForkEntry = NonNullable<Session['messageForksHash']>[string]
export type MessageLocation = { list: Message[]; index: number }
stores/session/crud.ts (~150 lines)Session lifecycle operations:
createEmpty - creates new chat/picture sessioncopyAndSwitchSession - duplicates sessionswitchCurrentSession - changes active sessionswitchToIndex - switch by indexswitchToNext - switch to next/prevreorderSessions - drag-drop reorderclearConversationList - bulk delete sessionsclear - clear messages in sessionInternal: create, copySession, clearSessionList
Dependencies: chatStore, atoms, scrollActions, router, sessionHelpers
stores/session/messages.ts (~200 lines)Message CRUD operations:
insertMessage - add message to sessioninsertMessageAfter - insert after specific messagemodifyMessage - update messageremoveMessage - delete messagesubmitNewUserMessage - handle user input with AI responseDependencies: chatStore, settingActions, settingsStore, generation.ts (imports generate)
Note: submitNewUserMessage calls generate - will need to import from generation.ts
stores/session/threads.ts (~250 lines)Thread/history management:
editThread - rename threadremoveThread - delete threadswitchThread - change active threadrefreshContextAndCreateNewThread - archive current, start freshstartNewThread - wrapper with scroll/focusremoveCurrentThread - delete current threadcompressAndCreateThread - compress with summarymoveThreadToConversations - promote thread to sessionmoveCurrentThreadToConversations - promote current threadDependencies: chatStore, scrollActions, dom, sessionHelpers, crud.ts (for switchCurrentSession, copySession)
stores/session/forks.ts (~400 lines)Message fork/branch operations:
createNewFork - create branch pointswitchFork - navigate branchesdeleteFork - remove current branchexpandFork - flatten all branchesInternal helpers:
buildCreateForkPatchbuildSwitchForkPatchbuildDeleteForkPatchbuildExpandForkPatchswitchForkInMessagesapplyForkTransformcomputeNextMessageForksHashDependencies: chatStore, types.ts
stores/session/generation.ts (~450 lines)AI generation orchestration:
generate (internal, but used by messages.ts) - core generation logicgenerateMore - continue generationgenerateMoreInNewFork - new branch + generateregenerateInNewFork - regenerate in new branchcreateLoadingPictures - placeholder imagesgenMessageContext - build prompt contextgetMessageThreadContext - get thread messagesInternal helpers:
trackGenerateEventgetSessionWebBrowsingfindMessageLocationDependencies: chatStore, settingsStore, uiStore, platform, storage, model-calls, messages.ts (circular - see below)
Circular Dependency Issue:
generation.ts exports generate which is called by submitNewUserMessage in messages.tsgenerate doesn't call anything from messages.ts directly (it calls modifyMessage but that can be imported directly)messages.ts imports generate from generation.ts. No circular dependency.stores/session/naming.ts (~150 lines)Session/thread naming:
modifyNameAndThreadName - update session + thread namemodifyThreadName - update thread name onlyscheduleGenerateNameAndThreadName - debounced auto-namingscheduleGenerateThreadName - debounced thread namingInternal helpers:
_generateName - core name generationgenerateNameAndThreadName - wrappergenerateThreadName - wrapperDependencies: chatStore, settingsStore, platform, state.ts, model-calls, prompts
stores/session/export.ts (~20 lines)Export functionality:
exportSessionChat - export session to fileDependencies: chatStore, sessionHelpers
stores/session/index.tsRe-exports all public functions (37 total):
// CRUD (7)
export { createEmpty, copyAndSwitchSession, switchCurrentSession } from './crud'
export { switchToIndex, switchToNext, reorderSessions, clearConversationList, clear } from './crud'
// Messages (5)
export { insertMessage, insertMessageAfter, modifyMessage, removeMessage } from './messages'
export { submitNewUserMessage } from './messages'
// Threads (9)
export { editThread, removeThread, switchThread } from './threads'
export { refreshContextAndCreateNewThread, startNewThread, removeCurrentThread } from './threads'
export { compressAndCreateThread, moveThreadToConversations, moveCurrentThreadToConversations } from './threads'
// Forks (4)
export { createNewFork, switchFork, deleteFork, expandFork } from './forks'
// Generation (6)
export { generateMore, generateMoreInNewFork, regenerateInNewFork } from './generation'
export { createLoadingPictures, genMessageContext, getMessageThreadContext } from './generation'
// Naming (4)
export { modifyNameAndThreadName, modifyThreadName } from './naming'
export { scheduleGenerateNameAndThreadName, scheduleGenerateThreadName } from './naming'
// Export (1)
export { exportSessionChat } from './export'
Total exported: 36 functions (Note: clear is included in CRUD = 37)
Decision: Centralized State Module
The pendingNameGenerations and activeNameGenerations Maps will be moved to stores/session/state.ts and imported by naming.ts.
Rationale:
Alternative considered: Zustand store
generate to messages.ts)npx madge --circular src/renderer/stores/)npm run check)_) not exported| Module | Estimated Lines |
|---|---|
| state.ts | ~10 |
| types.ts | ~20 |
| crud.ts | ~150 |
| messages.ts | ~200 |
| threads.ts | ~250 |
| forks.ts | ~400 |
| generation.ts | ~450 |
| naming.ts | ~150 |
| export.ts | ~20 |
| index.ts | ~50 |
| sessionActions.ts (facade) | <100 |
Circular imports: The main risk is between messages.ts and generation.ts. Analysis shows generate is called by submitNewUserMessage, but generate only calls modifyMessage which can be a direct chatStore call. No circular dependency.
Missing exports: Use TypeScript to ensure all 37 exports are available after split.
Broken imports: Update all imports in codebase to use sessionActions.ts facade (re-exports maintain compatibility).
State synchronization: pendingNameGenerations/activeNameGenerations are simple Maps/Sets - no sync issues expected.