authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-03-02 09:59:16-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-03-02 13:13:19-08:00
loged26b84177b93790481cf34ffe0edd010760e542
treecfc11536b438b09e206746639c37571b9d17000d
parentad4d12cf94e8b1cbd679372ed2bcf453b531b90f

Remove draftThreadTerminalState re-exports; use threadTerminalState directly

- 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: Cursor

3 files changed, 87 insertions(+), 107 deletions(-)

apps/web/src/draftThreadTerminalState.test.ts deleted-87
...@@ -1,87 +0,0 @@
1import { describe, expect, it } from "vitest";
2
3import {
4 createDefaultDraftThreadTerminalState,
5 reduceDraftThreadTerminalState,
6} from "./draftThreadTerminalState";
7
8describe("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
6import {
7 createDefaultThreadTerminalState,
8 normalizeThreadTerminalState,
9 reduceThreadTerminalState,
10 type ThreadTerminalAction,
11 type ThreadTerminalState,
12} from "./threadTerminalState";
13
14export type DraftThreadTerminalState = ThreadTerminalState;
15export type DraftThreadTerminalAction = ThreadTerminalAction;
16
17export const createDefaultDraftThreadTerminalState = createDefaultThreadTerminalState;
18export const reduceDraftThreadTerminalState = reduceThreadTerminalState;
19
20export { normalizeThreadTerminalState };
apps/web/src/threadTerminalState.test.ts created+87
...@@ -0,0 +1,87 @@
1import { describe, expect, it } from "vitest";
2
3import {
4 createDefaultThreadTerminalState,
5 reduceThreadTerminalState,
6} from "./threadTerminalState";
7
8describe("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});