| author | |
| committer | |
| log | 6f50867aa1ee94fc7582340ce728655e173db144 |
| tree | 44f58a309ab47f1ec9777fadc55d9b907bc185a8 |
| parent | 4fca60469dfc5dab7d8d742e965f4ab260f20c3c |
- Deep clone DEFAULT_THREAD_TERMINAL_STATE in createDefaultThreadTerminalState
- Remove imperative getTerminalState from terminal store; export
selectThreadTerminalState for reactive selectors instead
- Fix Sidebar terminal activity indicator not updating (was using
non-reactive getter)
- Add no-op bailouts to app store transitions (updateThread,
markThreadVisited, markThreadUnread, setError, setThreadBranch,
setProjectExpanded, setRuntimeMode) to prevent unnecessary re-renders
- Use createDefaultThreadTerminalState in clearTerminalState to avoid
storing frozen reference
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>5 files changed, 79 insertions(+), 68 deletions(-)
apps/web/src/components/ChatView.tsx+4-2| ... | @@ -124,7 +124,7 @@ import { | ... | @@ -124,7 +124,7 @@ import { |
| 124 | useComposerDraftStore, | 124 | useComposerDraftStore, |
| 125 | useComposerThreadDraft, | 125 | useComposerThreadDraft, |
| 126 | } from "../composerDraftStore"; | 126 | } from "../composerDraftStore"; |
| 127 | import { useTerminalStateStore } from "../terminalStateStore"; | 127 | import { selectThreadTerminalState, useTerminalStateStore } from "../terminalStateStore"; |
| 128 | import { clamp } from "effect/Number"; | 128 | import { clamp } from "effect/Number"; |
| 129 | 129 | ||
| 130 | function formatMessageMeta(createdAt: string, duration: string | null): string { | 130 | function formatMessageMeta(createdAt: string, duration: string | null): string { |
| ... | @@ -515,7 +515,9 @@ export default function ChatView({ threadId }: ChatViewProps) { | ... | @@ -515,7 +515,9 @@ export default function ChatView({ threadId }: ChatViewProps) { |
| 515 | setMessagesScrollElement(element); | 515 | setMessagesScrollElement(element); |
| 516 | }, []); | 516 | }, []); |
| 517 | 517 | ||
| 518 | const terminalState = useTerminalStateStore((state) => state.getTerminalState(threadId)); | 518 | const terminalState = useTerminalStateStore((state) => |
| 519 | selectThreadTerminalState(state.terminalStateByThreadId, threadId), | ||
| 520 | ); | ||
| 519 | const storeSetTerminalOpen = useTerminalStateStore((s) => s.setTerminalOpen); | 521 | const storeSetTerminalOpen = useTerminalStateStore((s) => s.setTerminalOpen); |
| 520 | const storeSetTerminalHeight = useTerminalStateStore((s) => s.setTerminalHeight); | 522 | const storeSetTerminalHeight = useTerminalStateStore((s) => s.setTerminalHeight); |
| 521 | const storeSplitTerminal = useTerminalStateStore((s) => s.splitTerminal); | 523 | const storeSplitTerminal = useTerminalStateStore((s) => s.splitTerminal); |
apps/web/src/components/Sidebar.tsx+4-3| ... | @@ -21,7 +21,7 @@ import { gitRemoveWorktreeMutationOptions, gitStatusQueryOptions } from "../lib/ | ... | @@ -21,7 +21,7 @@ import { gitRemoveWorktreeMutationOptions, gitStatusQueryOptions } from "../lib/ |
| 21 | import { serverConfigQueryOptions } from "../lib/serverReactQuery"; | 21 | import { serverConfigQueryOptions } from "../lib/serverReactQuery"; |
| 22 | import { readNativeApi } from "../nativeApi"; | 22 | import { readNativeApi } from "../nativeApi"; |
| 23 | import { type DraftThreadEnvMode, useComposerDraftStore } from "../composerDraftStore"; | 23 | import { type DraftThreadEnvMode, useComposerDraftStore } from "../composerDraftStore"; |
| 24 | import { useTerminalStateStore } from "../terminalStateStore"; | 24 | import { selectThreadTerminalState, useTerminalStateStore } from "../terminalStateStore"; |
| 25 | import { toastManager } from "./ui/toast"; | 25 | import { toastManager } from "./ui/toast"; |
| 26 | import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collapsible"; | 26 | import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "./ui/collapsible"; |
| 27 | import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip"; | 27 | import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip"; |
| ... | @@ -240,7 +240,7 @@ export default function Sidebar() { | ... | @@ -240,7 +240,7 @@ export default function Sidebar() { |
| 240 | (store) => store.getDraftThreadByProjectId, | 240 | (store) => store.getDraftThreadByProjectId, |
| 241 | ); | 241 | ); |
| 242 | const getDraftThread = useComposerDraftStore((store) => store.getDraftThread); | 242 | const getDraftThread = useComposerDraftStore((store) => store.getDraftThread); |
| 243 | const getTerminalState = useTerminalStateStore((state) => state.getTerminalState); | 243 | const terminalStateByThreadId = useTerminalStateStore((state) => state.terminalStateByThreadId); |
| 244 | const setProjectDraftThreadId = useComposerDraftStore((store) => store.setProjectDraftThreadId); | 244 | const setProjectDraftThreadId = useComposerDraftStore((store) => store.setProjectDraftThreadId); |
| 245 | const setDraftThreadContext = useComposerDraftStore((store) => store.setDraftThreadContext); | 245 | const setDraftThreadContext = useComposerDraftStore((store) => store.setDraftThreadContext); |
| 246 | const clearProjectDraftThreadId = useComposerDraftStore( | 246 | const clearProjectDraftThreadId = useComposerDraftStore( |
| ... | @@ -829,7 +829,8 @@ export default function Sidebar() { | ... | @@ -829,7 +829,8 @@ export default function Sidebar() { |
| 829 | ); | 829 | ); |
| 830 | const prStatus = prStatusIndicator(prByThreadId.get(thread.id) ?? null); | 830 | const prStatus = prStatusIndicator(prByThreadId.get(thread.id) ?? null); |
| 831 | const terminalStatus = terminalStatusFromRunningIds( | 831 | const terminalStatus = terminalStatusFromRunningIds( |
| 832 | getTerminalState(thread.id).runningTerminalIds, | 832 | selectThreadTerminalState(terminalStateByThreadId, thread.id) |
| 833 | .runningTerminalIds, | ||
| 833 | ); | 834 | ); |
| 834 | 835 | ||
| 835 | return ( | 836 | return ( |
apps/web/src/store.ts+54-49| ... | @@ -101,7 +101,14 @@ function updateThread( | ... | @@ -101,7 +101,14 @@ function updateThread( |
| 101 | threadId: ThreadId, | 101 | threadId: ThreadId, |
| 102 | updater: (t: Thread) => Thread, | 102 | updater: (t: Thread) => Thread, |
| 103 | ): Thread[] { | 103 | ): Thread[] { |
| 104 | return threads.map((t) => (t.id === threadId ? updater(t) : t)); | 104 | let changed = false; |
| 105 | const next = threads.map((t) => { | ||
| 106 | if (t.id !== threadId) return t; | ||
| 107 | const updated = updater(t); | ||
| 108 | if (updated !== t) changed = true; | ||
| 109 | return updated; | ||
| 110 | }); | ||
| 111 | return changed ? next : threads; | ||
| 105 | } | 112 | } |
| 106 | 113 | ||
| 107 | function mapProjectsFromReadModel( | 114 | function mapProjectsFromReadModel( |
| ... | @@ -277,34 +284,30 @@ export function markThreadVisited( | ... | @@ -277,34 +284,30 @@ export function markThreadVisited( |
| 277 | ): AppState { | 284 | ): AppState { |
| 278 | const at = visitedAt ?? new Date().toISOString(); | 285 | const at = visitedAt ?? new Date().toISOString(); |
| 279 | const visitedAtMs = Date.parse(at); | 286 | const visitedAtMs = Date.parse(at); |
| 280 | return { | 287 | const threads = updateThread(state.threads, threadId, (thread) => { |
| 281 | ...state, | 288 | const previousVisitedAtMs = thread.lastVisitedAt ? Date.parse(thread.lastVisitedAt) : NaN; |
| 282 | threads: updateThread(state.threads, threadId, (thread) => { | 289 | if ( |
| 283 | const previousVisitedAtMs = thread.lastVisitedAt ? Date.parse(thread.lastVisitedAt) : NaN; | 290 | Number.isFinite(previousVisitedAtMs) && |
| 284 | if ( | 291 | Number.isFinite(visitedAtMs) && |
| 285 | Number.isFinite(previousVisitedAtMs) && | 292 | previousVisitedAtMs >= visitedAtMs |
| 286 | Number.isFinite(visitedAtMs) && | 293 | ) { |
| 287 | previousVisitedAtMs >= visitedAtMs | 294 | return thread; |
| 288 | ) { | 295 | } |
| 289 | return thread; | 296 | return { ...thread, lastVisitedAt: at }; |
| 290 | } | 297 | }); |
| 291 | return { ...thread, lastVisitedAt: at }; | 298 | return threads === state.threads ? state : { ...state, threads }; |
| 292 | }), | ||
| 293 | }; | ||
| 294 | } | 299 | } |
| 295 | 300 | ||
| 296 | export function markThreadUnread(state: AppState, threadId: ThreadId): AppState { | 301 | export function markThreadUnread(state: AppState, threadId: ThreadId): AppState { |
| 297 | return { | 302 | const threads = updateThread(state.threads, threadId, (thread) => { |
| 298 | ...state, | 303 | if (!thread.latestTurn?.completedAt) return thread; |
| 299 | threads: updateThread(state.threads, threadId, (thread) => { | 304 | const latestTurnCompletedAtMs = Date.parse(thread.latestTurn.completedAt); |
| 300 | if (!thread.latestTurn?.completedAt) return thread; | 305 | if (Number.isNaN(latestTurnCompletedAtMs)) return thread; |
| 301 | const latestTurnCompletedAtMs = Date.parse(thread.latestTurn.completedAt); | 306 | const unreadVisitedAt = new Date(latestTurnCompletedAtMs - 1).toISOString(); |
| 302 | if (Number.isNaN(latestTurnCompletedAtMs)) return thread; | 307 | if (thread.lastVisitedAt === unreadVisitedAt) return thread; |
| 303 | const unreadVisitedAt = new Date(latestTurnCompletedAtMs - 1).toISOString(); | 308 | return { ...thread, lastVisitedAt: unreadVisitedAt }; |
| 304 | if (thread.lastVisitedAt === unreadVisitedAt) return thread; | 309 | }); |
| 305 | return { ...thread, lastVisitedAt: unreadVisitedAt }; | 310 | return threads === state.threads ? state : { ...state, threads }; |
| 306 | }), | ||
| 307 | }; | ||
| 308 | } | 311 | } |
| 309 | 312 | ||
| 310 | export function toggleProject(state: AppState, projectId: Project["id"]): AppState { | 313 | export function toggleProject(state: AppState, projectId: Project["id"]): AppState { |
| ... | @@ -321,19 +324,21 @@ export function setProjectExpanded( | ... | @@ -321,19 +324,21 @@ export function setProjectExpanded( |
| 321 | projectId: Project["id"], | 324 | projectId: Project["id"], |
| 322 | expanded: boolean, | 325 | expanded: boolean, |
| 323 | ): AppState { | 326 | ): AppState { |
| 324 | return { | 327 | let changed = false; |
| 325 | ...state, | 328 | const projects = state.projects.map((p) => { |
| 326 | projects: state.projects.map((p) => | 329 | if (p.id !== projectId || p.expanded === expanded) return p; |
| 327 | p.id === projectId ? { ...p, expanded } : p, | 330 | changed = true; |
| 328 | ), | 331 | return { ...p, expanded }; |
| 329 | }; | 332 | }); |
| 333 | return changed ? { ...state, projects } : state; | ||
| 330 | } | 334 | } |
| 331 | 335 | ||
| 332 | export function setError(state: AppState, threadId: ThreadId, error: string | null): AppState { | 336 | export function setError(state: AppState, threadId: ThreadId, error: string | null): AppState { |
| 333 | return { | 337 | const threads = updateThread(state.threads, threadId, (t) => { |
| 334 | ...state, | 338 | if (t.error === error) return t; |
| 335 | threads: updateThread(state.threads, threadId, (t) => ({ ...t, error })), | 339 | return { ...t, error }; |
| 336 | }; | 340 | }); |
| 341 | return threads === state.threads ? state : { ...state, threads }; | ||
| 337 | } | 342 | } |
| 338 | 343 | ||
| 339 | export function setThreadBranch( | 344 | export function setThreadBranch( |
| ... | @@ -342,21 +347,21 @@ export function setThreadBranch( | ... | @@ -342,21 +347,21 @@ export function setThreadBranch( |
| 342 | branch: string | null, | 347 | branch: string | null, |
| 343 | worktreePath: string | null, | 348 | worktreePath: string | null, |
| 344 | ): AppState { | 349 | ): AppState { |
| 345 | return { | 350 | const threads = updateThread(state.threads, threadId, (t) => { |
| 346 | ...state, | 351 | if (t.branch === branch && t.worktreePath === worktreePath) return t; |
| 347 | threads: updateThread(state.threads, threadId, (t) => { | 352 | const cwdChanged = t.worktreePath !== worktreePath; |
| 348 | const cwdChanged = t.worktreePath !== worktreePath; | 353 | return { |
| 349 | return { | 354 | ...t, |
| 350 | ...t, | 355 | branch, |
| 351 | branch, | 356 | worktreePath, |
| 352 | worktreePath, | 357 | ...(cwdChanged ? { session: null } : {}), |
| 353 | ...(cwdChanged ? { session: null } : {}), | 358 | }; |
| 354 | }; | 359 | }); |
| 355 | }), | 360 | return threads === state.threads ? state : { ...state, threads }; |
| 356 | }; | ||
| 357 | } | 361 | } |
| 358 | 362 | ||
| 359 | export function setRuntimeMode(state: AppState, mode: RuntimeMode): AppState { | 363 | export function setRuntimeMode(state: AppState, mode: RuntimeMode): AppState { |
| 364 | if (state.runtimeMode === mode) return state; | ||
| 360 | return { ...state, runtimeMode: mode }; | 365 | return { ...state, runtimeMode: mode }; |
| 361 | } | 366 | } |
| 362 | 367 | ||
| ... | @@ -377,7 +382,7 @@ interface AppStore extends AppState { | ... | @@ -377,7 +382,7 @@ interface AppStore extends AppState { |
| 377 | setRuntimeMode: (mode: RuntimeMode) => void; | 382 | setRuntimeMode: (mode: RuntimeMode) => void; |
| 378 | } | 383 | } |
| 379 | 384 | ||
| 380 | export const useStore = create<AppStore>((set, _get) => ({ | 385 | export const useStore = create<AppStore>((set) => ({ |
| 381 | ...readPersistedState(), | 386 | ...readPersistedState(), |
| 382 | syncServerReadModel: (readModel) => | 387 | syncServerReadModel: (readModel) => |
| 383 | set((state) => syncServerReadModel(state, readModel)), | 388 | set((state) => syncServerReadModel(state, readModel)), |
apps/web/src/terminalStateStore.test.ts+8-8| ... | @@ -1,7 +1,7 @@ | ... | @@ -1,7 +1,7 @@ |
| 1 | import { ThreadId } from "@t3tools/contracts"; | 1 | import { ThreadId } from "@t3tools/contracts"; |
| 2 | import { beforeEach, describe, expect, it } from "vitest"; | 2 | import { beforeEach, describe, expect, it } from "vitest"; |
| 3 | 3 | ||
| 4 | import { useTerminalStateStore } from "./terminalStateStore"; | 4 | import { selectThreadTerminalState, useTerminalStateStore } from "./terminalStateStore"; |
| 5 | 5 | ||
| 6 | const THREAD_ID = ThreadId.makeUnsafe("thread-1"); | 6 | const THREAD_ID = ThreadId.makeUnsafe("thread-1"); |
| 7 | 7 | ||
| ... | @@ -14,7 +14,7 @@ describe("terminalStateStore actions", () => { | ... | @@ -14,7 +14,7 @@ describe("terminalStateStore actions", () => { |
| 14 | }); | 14 | }); |
| 15 | 15 | ||
| 16 | it("returns a closed default terminal state for unknown threads", () => { | 16 | it("returns a closed default terminal state for unknown threads", () => { |
| 17 | const terminalState = useTerminalStateStore.getState().getTerminalState(THREAD_ID); | 17 | const terminalState = selectThreadTerminalState(useTerminalStateStore.getState().terminalStateByThreadId, THREAD_ID); |
| 18 | expect(terminalState).toEqual({ | 18 | expect(terminalState).toEqual({ |
| 19 | terminalOpen: false, | 19 | terminalOpen: false, |
| 20 | terminalHeight: 280, | 20 | terminalHeight: 280, |
| ... | @@ -31,7 +31,7 @@ describe("terminalStateStore actions", () => { | ... | @@ -31,7 +31,7 @@ describe("terminalStateStore actions", () => { |
| 31 | store.setTerminalOpen(THREAD_ID, true); | 31 | store.setTerminalOpen(THREAD_ID, true); |
| 32 | store.splitTerminal(THREAD_ID, "terminal-2"); | 32 | store.splitTerminal(THREAD_ID, "terminal-2"); |
| 33 | 33 | ||
| 34 | const terminalState = useTerminalStateStore.getState().getTerminalState(THREAD_ID); | 34 | const terminalState = selectThreadTerminalState(useTerminalStateStore.getState().terminalStateByThreadId, THREAD_ID); |
| 35 | expect(terminalState.terminalOpen).toBe(true); | 35 | expect(terminalState.terminalOpen).toBe(true); |
| 36 | expect(terminalState.terminalIds).toEqual(["default", "terminal-2"]); | 36 | expect(terminalState.terminalIds).toEqual(["default", "terminal-2"]); |
| 37 | expect(terminalState.activeTerminalId).toBe("terminal-2"); | 37 | expect(terminalState.activeTerminalId).toBe("terminal-2"); |
| ... | @@ -43,7 +43,7 @@ describe("terminalStateStore actions", () => { | ... | @@ -43,7 +43,7 @@ describe("terminalStateStore actions", () => { |
| 43 | it("creates new terminals in a separate group", () => { | 43 | it("creates new terminals in a separate group", () => { |
| 44 | useTerminalStateStore.getState().newTerminal(THREAD_ID, "terminal-2"); | 44 | useTerminalStateStore.getState().newTerminal(THREAD_ID, "terminal-2"); |
| 45 | 45 | ||
| 46 | const terminalState = useTerminalStateStore.getState().getTerminalState(THREAD_ID); | 46 | const terminalState = selectThreadTerminalState(useTerminalStateStore.getState().terminalStateByThreadId, THREAD_ID); |
| 47 | expect(terminalState.terminalIds).toEqual(["default", "terminal-2"]); | 47 | expect(terminalState.terminalIds).toEqual(["default", "terminal-2"]); |
| 48 | expect(terminalState.activeTerminalId).toBe("terminal-2"); | 48 | expect(terminalState.activeTerminalId).toBe("terminal-2"); |
| 49 | expect(terminalState.activeTerminalGroupId).toBe("group-terminal-2"); | 49 | expect(terminalState.activeTerminalGroupId).toBe("group-terminal-2"); |
| ... | @@ -57,12 +57,12 @@ describe("terminalStateStore actions", () => { | ... | @@ -57,12 +57,12 @@ describe("terminalStateStore actions", () => { |
| 57 | const store = useTerminalStateStore.getState(); | 57 | const store = useTerminalStateStore.getState(); |
| 58 | store.splitTerminal(THREAD_ID, "terminal-2"); | 58 | store.splitTerminal(THREAD_ID, "terminal-2"); |
| 59 | store.setTerminalActivity(THREAD_ID, "terminal-2", true); | 59 | store.setTerminalActivity(THREAD_ID, "terminal-2", true); |
| 60 | expect(useTerminalStateStore.getState().getTerminalState(THREAD_ID).runningTerminalIds).toEqual([ | 60 | expect(selectThreadTerminalState(useTerminalStateStore.getState().terminalStateByThreadId, THREAD_ID).runningTerminalIds).toEqual([ |
| 61 | "terminal-2", | 61 | "terminal-2", |
| 62 | ]); | 62 | ]); |
| 63 | 63 | ||
| 64 | store.setTerminalActivity(THREAD_ID, "terminal-2", false); | 64 | store.setTerminalActivity(THREAD_ID, "terminal-2", false); |
| 65 | expect(useTerminalStateStore.getState().getTerminalState(THREAD_ID).runningTerminalIds).toEqual([]); | 65 | expect(selectThreadTerminalState(useTerminalStateStore.getState().terminalStateByThreadId, THREAD_ID).runningTerminalIds).toEqual([]); |
| 66 | }); | 66 | }); |
| 67 | 67 | ||
| 68 | it("resets to default and clears persisted entry when closing the last terminal", () => { | 68 | it("resets to default and clears persisted entry when closing the last terminal", () => { |
| ... | @@ -70,7 +70,7 @@ describe("terminalStateStore actions", () => { | ... | @@ -70,7 +70,7 @@ describe("terminalStateStore actions", () => { |
| 70 | store.closeTerminal(THREAD_ID, "default"); | 70 | store.closeTerminal(THREAD_ID, "default"); |
| 71 | 71 | ||
| 72 | expect(useTerminalStateStore.getState().terminalStateByThreadId[THREAD_ID]).toBeUndefined(); | 72 | expect(useTerminalStateStore.getState().terminalStateByThreadId[THREAD_ID]).toBeUndefined(); |
| 73 | expect(useTerminalStateStore.getState().getTerminalState(THREAD_ID).terminalIds).toEqual(["default"]); | 73 | expect(selectThreadTerminalState(useTerminalStateStore.getState().terminalStateByThreadId, THREAD_ID).terminalIds).toEqual(["default"]); |
| 74 | }); | 74 | }); |
| 75 | 75 | ||
| 76 | it("keeps a valid active terminal after closing an active split terminal", () => { | 76 | it("keeps a valid active terminal after closing an active split terminal", () => { |
| ... | @@ -79,7 +79,7 @@ describe("terminalStateStore actions", () => { | ... | @@ -79,7 +79,7 @@ describe("terminalStateStore actions", () => { |
| 79 | store.splitTerminal(THREAD_ID, "terminal-3"); | 79 | store.splitTerminal(THREAD_ID, "terminal-3"); |
| 80 | store.closeTerminal(THREAD_ID, "terminal-3"); | 80 | store.closeTerminal(THREAD_ID, "terminal-3"); |
| 81 | 81 | ||
| 82 | const terminalState = useTerminalStateStore.getState().getTerminalState(THREAD_ID); | 82 | const terminalState = selectThreadTerminalState(useTerminalStateStore.getState().terminalStateByThreadId, THREAD_ID); |
| 83 | expect(terminalState.activeTerminalId).toBe("terminal-2"); | 83 | expect(terminalState.activeTerminalId).toBe("terminal-2"); |
| 84 | expect(terminalState.terminalIds).toEqual(["default", "terminal-2"]); | 84 | expect(terminalState.terminalIds).toEqual(["default", "terminal-2"]); |
| 85 | expect(terminalState.terminalGroups).toEqual([ | 85 | expect(terminalState.terminalGroups).toEqual([ |
apps/web/src/terminalStateStore.ts+9-6| ... | @@ -163,7 +163,12 @@ const DEFAULT_THREAD_TERMINAL_STATE: ThreadTerminalState = Object.freeze({ | ... | @@ -163,7 +163,12 @@ const DEFAULT_THREAD_TERMINAL_STATE: ThreadTerminalState = Object.freeze({ |
| 163 | }); | 163 | }); |
| 164 | 164 | ||
| 165 | function createDefaultThreadTerminalState(): ThreadTerminalState { | 165 | function createDefaultThreadTerminalState(): ThreadTerminalState { |
| 166 | return { ...DEFAULT_THREAD_TERMINAL_STATE }; | 166 | return { |
| 167 | ...DEFAULT_THREAD_TERMINAL_STATE, | ||
| 168 | terminalIds: [...DEFAULT_THREAD_TERMINAL_STATE.terminalIds], | ||
| 169 | runningTerminalIds: [...DEFAULT_THREAD_TERMINAL_STATE.runningTerminalIds], | ||
| 170 | terminalGroups: copyTerminalGroups(DEFAULT_THREAD_TERMINAL_STATE.terminalGroups), | ||
| 171 | }; | ||
| 167 | } | 172 | } |
| 168 | 173 | ||
| 169 | function getDefaultThreadTerminalState(): ThreadTerminalState { | 174 | function getDefaultThreadTerminalState(): ThreadTerminalState { |
| ... | @@ -403,7 +408,7 @@ function setThreadTerminalActivity( | ... | @@ -403,7 +408,7 @@ function setThreadTerminalActivity( |
| 403 | return { ...normalized, runningTerminalIds: [...runningTerminalIds] }; | 408 | return { ...normalized, runningTerminalIds: [...runningTerminalIds] }; |
| 404 | } | 409 | } |
| 405 | 410 | ||
| 406 | function selectThreadTerminalState( | 411 | export function selectThreadTerminalState( |
| 407 | terminalStateByThreadId: Record<ThreadId, ThreadTerminalState>, | 412 | terminalStateByThreadId: Record<ThreadId, ThreadTerminalState>, |
| 408 | threadId: ThreadId, | 413 | threadId: ThreadId, |
| 409 | ): ThreadTerminalState { | 414 | ): ThreadTerminalState { |
| ... | @@ -444,7 +449,6 @@ function updateTerminalStateByThreadId( | ... | @@ -444,7 +449,6 @@ function updateTerminalStateByThreadId( |
| 444 | 449 | ||
| 445 | interface TerminalStateStoreState { | 450 | interface TerminalStateStoreState { |
| 446 | terminalStateByThreadId: Record<ThreadId, ThreadTerminalState>; | 451 | terminalStateByThreadId: Record<ThreadId, ThreadTerminalState>; |
| 447 | getTerminalState: (threadId: ThreadId) => ThreadTerminalState; | ||
| 448 | setTerminalOpen: (threadId: ThreadId, open: boolean) => void; | 452 | setTerminalOpen: (threadId: ThreadId, open: boolean) => void; |
| 449 | setTerminalHeight: (threadId: ThreadId, height: number) => void; | 453 | setTerminalHeight: (threadId: ThreadId, height: number) => void; |
| 450 | splitTerminal: (threadId: ThreadId, terminalId: string) => void; | 454 | splitTerminal: (threadId: ThreadId, terminalId: string) => void; |
| ... | @@ -461,7 +465,7 @@ interface TerminalStateStoreState { | ... | @@ -461,7 +465,7 @@ interface TerminalStateStoreState { |
| 461 | 465 | ||
| 462 | export const useTerminalStateStore = create<TerminalStateStoreState>()( | 466 | export const useTerminalStateStore = create<TerminalStateStoreState>()( |
| 463 | persist( | 467 | persist( |
| 464 | (set, get) => { | 468 | (set) => { |
| 465 | const updateTerminal = ( | 469 | const updateTerminal = ( |
| 466 | threadId: ThreadId, | 470 | threadId: ThreadId, |
| 467 | updater: (state: ThreadTerminalState) => ThreadTerminalState, | 471 | updater: (state: ThreadTerminalState) => ThreadTerminalState, |
| ... | @@ -483,7 +487,6 @@ export const useTerminalStateStore = create<TerminalStateStoreState>()( | ... | @@ -483,7 +487,6 @@ export const useTerminalStateStore = create<TerminalStateStoreState>()( |
| 483 | 487 | ||
| 484 | return { | 488 | return { |
| 485 | terminalStateByThreadId: {}, | 489 | terminalStateByThreadId: {}, |
| 486 | getTerminalState: (threadId) => selectThreadTerminalState(get().terminalStateByThreadId, threadId), | ||
| 487 | setTerminalOpen: (threadId, open) => | 490 | setTerminalOpen: (threadId, open) => |
| 488 | updateTerminal(threadId, (state) => setThreadTerminalOpen(state, open)), | 491 | updateTerminal(threadId, (state) => setThreadTerminalOpen(state, open)), |
| 489 | setTerminalHeight: (threadId, height) => | 492 | setTerminalHeight: (threadId, height) => |
| ... | @@ -501,7 +504,7 @@ export const useTerminalStateStore = create<TerminalStateStoreState>()( | ... | @@ -501,7 +504,7 @@ export const useTerminalStateStore = create<TerminalStateStoreState>()( |
| 501 | setThreadTerminalActivity(state, terminalId, hasRunningSubprocess), | 504 | setThreadTerminalActivity(state, terminalId, hasRunningSubprocess), |
| 502 | ), | 505 | ), |
| 503 | clearTerminalState: (threadId) => | 506 | clearTerminalState: (threadId) => |
| 504 | updateTerminal(threadId, () => getDefaultThreadTerminalState()), | 507 | updateTerminal(threadId, () => createDefaultThreadTerminalState()), |
| 505 | }; | 508 | }; |
| 506 | }, | 509 | }, |
| 507 | { | 510 | { |