| 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 | }); | ||