| author | |
| committer | |
| log | ed26b84177b93790481cf34ffe0edd010760e542 |
| tree | cfc11536b438b09e206746639c37571b9d17000d |
| parent | ad4d12cf94e8b1cbd679372ed2bcf453b531b90f |
- Delete draftThreadTerminalState.ts (was only re-exporting shared module)
- Replace draftThreadTerminalState.test.ts with threadTerminalState.test.ts
using createDefaultThreadTerminalState and reduceThreadTerminalState
- Draft threads are first-class: same helpers and types as persisted threads
Made-with: Cursor3 files changed, 87 insertions(+), 107 deletions(-)
apps/web/src/draftThreadTerminalState.test.ts deleted-87| ... | ... | @@ -1,87 +0,0 @@ |
| 1 | import { describe, expect, it } from "vitest"; | |
| 2 | ||
| 3 | import { | |
| 4 | createDefaultDraftThreadTerminalState, | |
| 5 | reduceDraftThreadTerminalState, | |
| 6 | } from "./draftThreadTerminalState"; | |
| 7 | ||
| 8 | describe("draftThreadTerminalState", () => { | |
| 9 | it("creates a closed default terminal state", () => { | |
| 10 | expect(createDefaultDraftThreadTerminalState()).toEqual({ | |
| 11 | terminalOpen: false, | |
| 12 | terminalHeight: 280, | |
| 13 | terminalIds: ["default"], | |
| 14 | runningTerminalIds: [], | |
| 15 | activeTerminalId: "default", | |
| 16 | terminalGroups: [{ id: "group-default", terminalIds: ["default"] }], | |
| 17 | activeTerminalGroupId: "group-default", | |
| 18 | }); | |
| 19 | }); | |
| 20 | ||
| 21 | it("opens and splits terminal tabs into the active group", () => { | |
| 22 | const opened = reduceDraftThreadTerminalState(createDefaultDraftThreadTerminalState(), { | |
| 23 | type: "set-open", | |
| 24 | open: true, | |
| 25 | }); | |
| 26 | const split = reduceDraftThreadTerminalState(opened, { | |
| 27 | type: "split", | |
| 28 | terminalId: "terminal-2", | |
| 29 | }); | |
| 30 | ||
| 31 | expect(split.terminalOpen).toBe(true); | |
| 32 | expect(split.terminalIds).toEqual(["default", "terminal-2"]); | |
| 33 | expect(split.activeTerminalId).toBe("terminal-2"); | |
| 34 | expect(split.terminalGroups).toEqual([ | |
| 35 | { | |
| 36 | id: "group-default", | |
| 37 | terminalIds: ["default", "terminal-2"], | |
| 38 | }, | |
| 39 | ]); | |
| 40 | }); | |
| 41 | ||
| 42 | it("creates new terminals in a new group", () => { | |
| 43 | const next = reduceDraftThreadTerminalState(createDefaultDraftThreadTerminalState(), { | |
| 44 | type: "new", | |
| 45 | terminalId: "terminal-2", | |
| 46 | }); | |
| 47 | ||
| 48 | expect(next.terminalIds).toEqual(["default", "terminal-2"]); | |
| 49 | expect(next.activeTerminalId).toBe("terminal-2"); | |
| 50 | expect(next.activeTerminalGroupId).toBe("group-terminal-2"); | |
| 51 | expect(next.terminalGroups).toEqual([ | |
| 52 | { id: "group-default", terminalIds: ["default"] }, | |
| 53 | { id: "group-terminal-2", terminalIds: ["terminal-2"] }, | |
| 54 | ]); | |
| 55 | }); | |
| 56 | ||
| 57 | it("resets to default when closing the last terminal", () => { | |
| 58 | const closed = reduceDraftThreadTerminalState(createDefaultDraftThreadTerminalState(), { | |
| 59 | type: "close", | |
| 60 | terminalId: "default", | |
| 61 | }); | |
| 62 | ||
| 63 | expect(closed).toEqual(createDefaultDraftThreadTerminalState()); | |
| 64 | }); | |
| 65 | ||
| 66 | it("keeps a valid active terminal after closing an active split terminal", () => { | |
| 67 | const splitOnce = reduceDraftThreadTerminalState(createDefaultDraftThreadTerminalState(), { | |
| 68 | type: "split", | |
| 69 | terminalId: "terminal-2", | |
| 70 | }); | |
| 71 | const splitTwice = reduceDraftThreadTerminalState(splitOnce, { | |
| 72 | type: "split", | |
| 73 | terminalId: "terminal-3", | |
| 74 | }); | |
| 75 | ||
| 76 | const closed = reduceDraftThreadTerminalState(splitTwice, { | |
| 77 | type: "close", | |
| 78 | terminalId: "terminal-3", | |
| 79 | }); | |
| 80 | ||
| 81 | expect(closed.activeTerminalId).toBe("terminal-2"); | |
| 82 | expect(closed.terminalIds).toEqual(["default", "terminal-2"]); | |
| 83 | expect(closed.terminalGroups).toEqual([ | |
| 84 | { id: "group-default", terminalIds: ["default", "terminal-2"] }, | |
| 85 | ]); | |
| 86 | }); | |
| 87 | }); |
apps/web/src/draftThreadTerminalState.ts deleted-20| ... | ... | @@ -1,20 +0,0 @@ |
| 1 | /** | |
| 2 | * Re-exports shared thread terminal state and reducer so draft and persisted | |
| 3 | * flows use the same shape and logic. See threadTerminalState.ts. | |
| 4 | */ | |
| 5 | ||
| 6 | import { | |
| 7 | createDefaultThreadTerminalState, | |
| 8 | normalizeThreadTerminalState, | |
| 9 | reduceThreadTerminalState, | |
| 10 | type ThreadTerminalAction, | |
| 11 | type ThreadTerminalState, | |
| 12 | } from "./threadTerminalState"; | |
| 13 | ||
| 14 | export type DraftThreadTerminalState = ThreadTerminalState; | |
| 15 | export type DraftThreadTerminalAction = ThreadTerminalAction; | |
| 16 | ||
| 17 | export const createDefaultDraftThreadTerminalState = createDefaultThreadTerminalState; | |
| 18 | export const reduceDraftThreadTerminalState = reduceThreadTerminalState; | |
| 19 | ||
| 20 | export { normalizeThreadTerminalState }; |
apps/web/src/threadTerminalState.test.ts created+87| ... | ... | @@ -0,0 +1,87 @@ |
| 1 | import { describe, expect, it } from "vitest"; | |
| 2 | ||
| 3 | import { | |
| 4 | createDefaultThreadTerminalState, | |
| 5 | reduceThreadTerminalState, | |
| 6 | } from "./threadTerminalState"; | |
| 7 | ||
| 8 | describe("threadTerminalState", () => { | |
| 9 | it("creates a closed default terminal state", () => { | |
| 10 | expect(createDefaultThreadTerminalState()).toEqual({ | |
| 11 | terminalOpen: false, | |
| 12 | terminalHeight: 280, | |
| 13 | terminalIds: ["default"], | |
| 14 | runningTerminalIds: [], | |
| 15 | activeTerminalId: "default", | |
| 16 | terminalGroups: [{ id: "group-default", terminalIds: ["default"] }], | |
| 17 | activeTerminalGroupId: "group-default", | |
| 18 | }); | |
| 19 | }); | |
| 20 | ||
| 21 | it("opens and splits terminal tabs into the active group", () => { | |
| 22 | const opened = reduceThreadTerminalState(createDefaultThreadTerminalState(), { | |
| 23 | type: "set-open", | |
| 24 | open: true, | |
| 25 | }); | |
| 26 | const split = reduceThreadTerminalState(opened, { | |
| 27 | type: "split", | |
| 28 | terminalId: "terminal-2", | |
| 29 | }); | |
| 30 | ||
| 31 | expect(split.terminalOpen).toBe(true); | |
| 32 | expect(split.terminalIds).toEqual(["default", "terminal-2"]); | |
| 33 | expect(split.activeTerminalId).toBe("terminal-2"); | |
| 34 | expect(split.terminalGroups).toEqual([ | |
| 35 | { | |
| 36 | id: "group-default", | |
| 37 | terminalIds: ["default", "terminal-2"], | |
| 38 | }, | |
| 39 | ]); | |
| 40 | }); | |
| 41 | ||
| 42 | it("creates new terminals in a new group", () => { | |
| 43 | const next = reduceThreadTerminalState(createDefaultThreadTerminalState(), { | |
| 44 | type: "new", | |
| 45 | terminalId: "terminal-2", | |
| 46 | }); | |
| 47 | ||
| 48 | expect(next.terminalIds).toEqual(["default", "terminal-2"]); | |
| 49 | expect(next.activeTerminalId).toBe("terminal-2"); | |
| 50 | expect(next.activeTerminalGroupId).toBe("group-terminal-2"); | |
| 51 | expect(next.terminalGroups).toEqual([ | |
| 52 | { id: "group-default", terminalIds: ["default"] }, | |
| 53 | { id: "group-terminal-2", terminalIds: ["terminal-2"] }, | |
| 54 | ]); | |
| 55 | }); | |
| 56 | ||
| 57 | it("resets to default when closing the last terminal", () => { | |
| 58 | const closed = reduceThreadTerminalState(createDefaultThreadTerminalState(), { | |
| 59 | type: "close", | |
| 60 | terminalId: "default", | |
| 61 | }); | |
| 62 | ||
| 63 | expect(closed).toEqual(createDefaultThreadTerminalState()); | |
| 64 | }); | |
| 65 | ||
| 66 | it("keeps a valid active terminal after closing an active split terminal", () => { | |
| 67 | const splitOnce = reduceThreadTerminalState(createDefaultThreadTerminalState(), { | |
| 68 | type: "split", | |
| 69 | terminalId: "terminal-2", | |
| 70 | }); | |
| 71 | const splitTwice = reduceThreadTerminalState(splitOnce, { | |
| 72 | type: "split", | |
| 73 | terminalId: "terminal-3", | |
| 74 | }); | |
| 75 | ||
| 76 | const closed = reduceThreadTerminalState(splitTwice, { | |
| 77 | type: "close", | |
| 78 | terminalId: "terminal-3", | |
| 79 | }); | |
| 80 | ||
| 81 | expect(closed.activeTerminalId).toBe("terminal-2"); | |
| 82 | expect(closed.terminalIds).toEqual(["default", "terminal-2"]); | |
| 83 | expect(closed.terminalGroups).toEqual([ | |
| 84 | { id: "group-default", terminalIds: ["default", "terminal-2"] }, | |
| 85 | ]); | |
| 86 | }); | |
| 87 | }); |