@cueloop/client 0.1.0-alpha.7 → 0.1.0-alpha.71

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. package/native/build-pty.sh +51 -0
  2. package/native/build.sh +82 -0
  3. package/native/darwin-arm64/libcuelooppty.dylib +0 -0
  4. package/native/darwin-arm64/libcueloopvt.dylib +0 -0
  5. package/native/src/pty.zig +193 -0
  6. package/native/src/shim.zig +114 -0
  7. package/package.json +39 -32
  8. package/src/App.appearance.test.tsx +53 -0
  9. package/src/App.autoclose.test.tsx +76 -50
  10. package/src/App.diff.test.tsx +156 -42
  11. package/src/App.export.test.tsx +58 -40
  12. package/src/App.plan-review.test.tsx +363 -0
  13. package/src/App.prototype-markdown.test.tsx +92 -0
  14. package/src/App.prototype.test.tsx +176 -0
  15. package/src/App.readonly.test.tsx +128 -67
  16. package/src/App.rendering.test.tsx +61 -35
  17. package/src/App.submit-confirm.test.tsx +191 -0
  18. package/src/App.test.tsx +310 -114
  19. package/src/App.toast.test.tsx +89 -0
  20. package/src/App.tsx +1043 -798
  21. package/src/App.walk.test.tsx +298 -0
  22. package/src/App.workbench.test.tsx +495 -0
  23. package/src/__snapshots__/view-plan.regression.test.ts.snap +100 -0
  24. package/src/annotation-palette.ts +61 -0
  25. package/src/app-key-state.ts +61 -0
  26. package/src/app-screens.tsx +432 -0
  27. package/src/app-view-model.ts +159 -0
  28. package/src/attribution.test.ts +31 -0
  29. package/src/attribution.ts +42 -0
  30. package/src/clipboard.test.ts +23 -0
  31. package/src/clipboard.ts +33 -0
  32. package/src/components/AnnotatableFileView.stories.tsx +42 -0
  33. package/src/components/AnnotatableFileView.test.tsx +102 -0
  34. package/src/components/AnnotatableFileView.tsx +126 -0
  35. package/src/components/AnnotationCards.stories.tsx +87 -0
  36. package/src/components/AnnotationCards.tsx +303 -0
  37. package/src/components/AppHeader.stories.tsx +50 -0
  38. package/src/components/AppHeader.tsx +83 -0
  39. package/src/components/AppShell.stories.tsx +89 -0
  40. package/src/components/AppShell.tsx +290 -0
  41. package/src/components/AppShell.zoom.test.tsx +135 -0
  42. package/src/components/BareWorkbenchFileView.stories.tsx +27 -0
  43. package/src/components/BareWorkbenchFileView.tsx +64 -0
  44. package/src/components/Breadcrumb.stories.tsx +19 -0
  45. package/src/components/Breadcrumb.tsx +45 -0
  46. package/src/components/ChangesColumn.stories.tsx +39 -0
  47. package/src/components/ChangesColumn.tsx +171 -0
  48. package/src/components/CodeBlock.stories.tsx +42 -0
  49. package/src/components/CodeBlock.tsx +84 -0
  50. package/src/components/CompletionOverlay.stories.tsx +23 -0
  51. package/src/components/CompletionOverlay.tsx +60 -0
  52. package/src/components/ConfirmCard.stories.tsx +35 -0
  53. package/src/components/ConfirmCard.tsx +128 -0
  54. package/src/components/ConfirmDialog.stories.tsx +21 -0
  55. package/src/components/ConfirmDialog.tsx +66 -0
  56. package/src/components/DiffContentView.scroll.test.tsx +121 -0
  57. package/src/components/DiffContentView.split.test.tsx +140 -0
  58. package/src/components/DiffContentView.stories.tsx +80 -0
  59. package/src/components/DiffContentView.syntax.test.tsx +82 -0
  60. package/src/components/DiffContentView.tsx +859 -0
  61. package/src/components/DiscussionMarkerRail.stories.tsx +54 -0
  62. package/src/components/DiscussionMarkerRail.tsx +181 -0
  63. package/src/components/EditorGrid.stories.tsx +59 -0
  64. package/src/components/EditorGrid.tabs.test.tsx +118 -0
  65. package/src/components/EditorGrid.tsx +368 -0
  66. package/src/components/EditorGrid.zoom.test.tsx +68 -0
  67. package/src/components/FileContentsView.stories.tsx +16 -0
  68. package/src/components/FileContentsView.test.tsx +29 -0
  69. package/src/components/FileContentsView.tsx +98 -0
  70. package/src/components/KeybindsDialog.stories.tsx +31 -0
  71. package/src/components/KeybindsDialog.tsx +103 -0
  72. package/src/components/MenuBar.stories.tsx +28 -0
  73. package/src/components/MenuBar.tsx +44 -0
  74. package/src/components/OverlayScrollbar.stories.tsx +33 -0
  75. package/src/components/OverlayScrollbar.test.tsx +139 -0
  76. package/src/components/OverlayScrollbar.tsx +129 -0
  77. package/src/components/PaneGrid.stories.tsx +84 -0
  78. package/src/components/PaneGrid.tsx +128 -0
  79. package/src/components/PanelColumn.stories.tsx +25 -0
  80. package/src/components/PanelColumn.tsx +95 -0
  81. package/src/components/ProjectTreeView.stories.tsx +25 -0
  82. package/src/components/ProjectTreeView.test.tsx +32 -0
  83. package/src/components/ProjectTreeView.tsx +83 -0
  84. package/src/components/PromptDialog.stories.tsx +36 -0
  85. package/src/components/PromptDialog.tsx +87 -0
  86. package/src/components/PrototypeContentView.stories.tsx +19 -0
  87. package/src/components/PrototypeContentView.tsx +446 -0
  88. package/src/components/RootOverlay.stories.tsx +44 -0
  89. package/src/components/RootOverlay.tsx +35 -0
  90. package/src/components/ScrollArea.stories.tsx +20 -0
  91. package/src/components/ScrollArea.tsx +44 -0
  92. package/src/components/SettingsDialog.stories.tsx +117 -0
  93. package/src/components/SettingsDialog.tsx +195 -0
  94. package/src/components/SettingsRows.stories.tsx +18 -0
  95. package/src/components/SettingsRows.tsx +112 -0
  96. package/src/components/ShellHeader.stories.tsx +50 -0
  97. package/src/components/ShellHeader.tsx +137 -0
  98. package/src/components/ThreadFooter.stories.tsx +30 -0
  99. package/src/components/ThreadFooter.test.tsx +31 -0
  100. package/src/components/ThreadFooter.tsx +54 -0
  101. package/src/components/ThreadTree.stories.tsx +46 -0
  102. package/src/components/ThreadTree.tsx +219 -0
  103. package/src/components/ThreadView.selection.test.tsx +812 -0
  104. package/src/components/ThreadView.slash.test.tsx +158 -0
  105. package/src/components/ThreadView.stories.tsx +26 -0
  106. package/src/components/ThreadView.tsx +330 -0
  107. package/src/components/ThreadsSidebar.stories.tsx +36 -0
  108. package/src/components/ThreadsSidebar.tsx +71 -0
  109. package/src/components/Toast.stories.tsx +12 -0
  110. package/src/components/Toast.tsx +45 -0
  111. package/src/components/Tooltip.stories.tsx +26 -0
  112. package/src/components/Tooltip.tsx +72 -0
  113. package/src/components/TreePane.stories.tsx +106 -0
  114. package/src/components/TreePane.tsx +157 -0
  115. package/src/components/WalkWizard.stories.tsx +100 -0
  116. package/src/components/WalkWizard.tsx +152 -0
  117. package/src/components/WelcomePlayground.stories.tsx +12 -0
  118. package/src/components/WelcomePlayground.test.tsx +47 -0
  119. package/src/components/WelcomePlayground.tsx +140 -0
  120. package/src/components/__snapshots__/stories.test.tsx.snap +1845 -0
  121. package/src/components/agent-launcher.stories.tsx +26 -0
  122. package/src/components/agent-launcher.test.tsx +50 -0
  123. package/src/components/agent-launcher.tsx +149 -0
  124. package/src/components/diff-content-view-layout.test.ts +198 -0
  125. package/src/components/diff-content-view-layout.ts +84 -0
  126. package/src/components/editor-grid.test.ts +86 -0
  127. package/src/components/editor-grid.ts +138 -0
  128. package/src/components/fade-title.test.ts +49 -0
  129. package/src/components/fade-title.ts +61 -0
  130. package/src/components/file-tree.test.ts +60 -0
  131. package/src/components/file-tree.ts +112 -0
  132. package/src/components/pages/Workspace.stories.tsx +181 -0
  133. package/src/components/primitives/Button.stories.tsx +32 -0
  134. package/src/components/primitives/Button.tsx +52 -0
  135. package/src/components/primitives/Card.stories.tsx +25 -0
  136. package/src/components/primitives/Card.tsx +60 -0
  137. package/src/components/primitives/Dialog.stories.tsx +24 -0
  138. package/src/components/primitives/Dialog.tsx +66 -0
  139. package/src/components/primitives/IconButton.stories.tsx +36 -0
  140. package/src/components/primitives/IconButton.tsx +62 -0
  141. package/src/components/primitives/StatusBar.stories.tsx +11 -0
  142. package/src/components/primitives/StatusBar.tsx +23 -0
  143. package/src/components/primitives/Tabs.stories.tsx +29 -0
  144. package/src/components/primitives/Tabs.tsx +102 -0
  145. package/src/components/primitives/Toolbar.stories.tsx +17 -0
  146. package/src/components/primitives/Toolbar.tsx +14 -0
  147. package/src/components/primitives/Tree.stories.tsx +124 -0
  148. package/src/components/primitives/Tree.tsx +96 -0
  149. package/src/components/primitives/frame.test.ts +12 -0
  150. package/src/components/primitives/frame.ts +14 -0
  151. package/src/components/primitives/icons.ts +61 -0
  152. package/src/components/primitives/tree-model.test.ts +99 -0
  153. package/src/components/primitives/tree-model.ts +137 -0
  154. package/src/components/quick-actions-editor.stories.tsx +54 -0
  155. package/src/components/quick-actions-editor.test.tsx +49 -0
  156. package/src/components/quick-actions-editor.tsx +150 -0
  157. package/src/components/session-tree.test.ts +134 -0
  158. package/src/components/session-tree.ts +108 -0
  159. package/src/components/stories-app.tsx +184 -0
  160. package/src/components/stories.test.tsx +66 -0
  161. package/src/components/story-fixtures.ts +102 -0
  162. package/src/components/story-tree.test.ts +100 -0
  163. package/src/components/story.ts +142 -0
  164. package/src/components/syntax-highlight.ts +110 -0
  165. package/src/components/terminal-pane.test.tsx +40 -0
  166. package/src/components/terminal-pane.ts +199 -0
  167. package/src/components/theme-context.stories.tsx +46 -0
  168. package/src/components/theme-context.tsx +37 -0
  169. package/src/components/truncate-text.ts +7 -0
  170. package/src/config.test.ts +461 -31
  171. package/src/config.ts +389 -57
  172. package/src/diff-fold.test.ts +78 -0
  173. package/src/diff-fold.ts +126 -0
  174. package/src/diff-hunk-curate.test.ts +315 -0
  175. package/src/diff-hunk-curate.ts +153 -0
  176. package/src/diff-intraline.test.ts +165 -0
  177. package/src/diff-intraline.ts +146 -0
  178. package/src/diff-syntax.test.ts +112 -0
  179. package/src/diff-syntax.ts +179 -0
  180. package/src/discussions.ts +79 -0
  181. package/src/editor.test.ts +149 -0
  182. package/src/editor.ts +139 -9
  183. package/src/ghostty-terminal.test.ts +85 -0
  184. package/src/ghostty-terminal.ts +199 -0
  185. package/src/herdr.test.ts +9 -34
  186. package/src/herdr.ts +25 -21
  187. package/src/index.ts +11 -0
  188. package/src/integrations.test.ts +41 -0
  189. package/src/integrations.ts +32 -0
  190. package/src/intent-dispatch.test.ts +698 -0
  191. package/src/intent-dispatch.ts +700 -0
  192. package/src/key-bindings.test.ts +156 -0
  193. package/src/key-bindings.ts +465 -0
  194. package/src/keymap.test.ts +611 -0
  195. package/src/keymap.ts +410 -0
  196. package/src/kitty-image.test.ts +66 -0
  197. package/src/kitty-image.ts +138 -0
  198. package/src/mark-runs.ts +120 -0
  199. package/src/prototype-browser.test.ts +56 -0
  200. package/src/prototype-browser.ts +307 -0
  201. package/src/prototype-pixels.tsx +35 -0
  202. package/src/pty.test.ts +81 -0
  203. package/src/pty.ts +245 -0
  204. package/src/ready-signal.test.ts +85 -0
  205. package/src/ready-signal.ts +52 -0
  206. package/src/run.ts +30 -9
  207. package/src/serve.test.ts +24 -12
  208. package/src/serve.ts +20 -9
  209. package/src/share.ts +215 -0
  210. package/src/skills.test.ts +74 -0
  211. package/src/skills.ts +68 -0
  212. package/src/slash-palette.test.ts +54 -0
  213. package/src/slash-palette.ts +104 -0
  214. package/src/split-diff.test.ts +110 -0
  215. package/src/split-diff.ts +149 -0
  216. package/src/test-support.test.ts +264 -0
  217. package/src/test-support.ts +355 -0
  218. package/src/theme-presets.test.ts +51 -0
  219. package/src/theme-presets.ts +214 -0
  220. package/src/theme.ts +84 -19
  221. package/src/thread-chords.test.ts +170 -0
  222. package/src/thread-chords.ts +169 -0
  223. package/src/thread-controller.curate.test.ts +438 -0
  224. package/src/thread-controller.live-diff.test.ts +199 -0
  225. package/src/thread-controller.share.test.ts +472 -0
  226. package/src/thread-controller.tree.test.ts +255 -0
  227. package/src/thread-controller.ts +1509 -0
  228. package/src/thread-selection.test.ts +210 -0
  229. package/src/thread-selection.ts +243 -0
  230. package/src/tree-view.test.ts +128 -0
  231. package/src/tree-view.ts +148 -0
  232. package/src/use-annotation-surface.tsx +851 -0
  233. package/src/use-changes-workbench.ts +153 -0
  234. package/src/use-frame-measure.ts +38 -0
  235. package/src/use-settings-dialog.tsx +274 -0
  236. package/src/use-terminal-virtualizer.ts +233 -0
  237. package/src/version.ts +5 -0
  238. package/src/view-diff.test.ts +66 -9
  239. package/src/view-diff.ts +231 -23
  240. package/src/view-plan.regression.test.ts +60 -0
  241. package/src/view-plan.test.ts +406 -0
  242. package/src/view-plan.ts +570 -0
  243. package/src/walk.test.ts +121 -0
  244. package/src/walk.ts +74 -0
  245. package/src/syntax.test.ts +0 -41
  246. package/src/syntax.ts +0 -88
  247. package/src/view.test.ts +0 -157
  248. package/src/view.ts +0 -312
@@ -0,0 +1,298 @@
1
+ /** The guided walk in the virtual terminal: the wizard steps every changed file with a plain progress title, ] marks viewed and persists with the session, esc keeps progress, w resumes at the first unviewed file (across App instances via the daemon round-trip), the agent-note block renders only for files carrying a note, and the end card hands over to the submit confirm with the honest viewed count. */
2
+
3
+ import { afterEach, beforeEach, describe, expect, test } from "bun:test";
4
+ import { mkdtempSync, rmSync } from "node:fs";
5
+ import { tmpdir } from "node:os";
6
+ import { join } from "node:path";
7
+ import React from "react";
8
+ import { DaemonServer } from "@cueloop/daemon";
9
+ import type { Thread } from "@cueloop/schema";
10
+ import { App } from "./App";
11
+ import { DARK } from "./theme";
12
+ import {
13
+ isolateUserConfig,
14
+ press,
15
+ pressKey,
16
+ renderReadyApp,
17
+ waitForState,
18
+ waitForText,
19
+ waitForTextGone,
20
+ } from "./test-support";
21
+
22
+ const PATCH = `diff --git a/src/a.ts b/src/a.ts
23
+ index 111..222 100644
24
+ --- a/src/a.ts
25
+ +++ b/src/a.ts
26
+ @@ -1,2 +1,2 @@
27
+ export const a = 1;
28
+ -export const b = 2;
29
+ +export const b = 3;
30
+ diff --git a/src/b.ts b/src/b.ts
31
+ index 333..444 100644
32
+ --- a/src/b.ts
33
+ +++ b/src/b.ts
34
+ @@ -1,2 +1,2 @@
35
+ // header
36
+ -old line
37
+ +new line
38
+ diff --git a/src/c.ts b/src/c.ts
39
+ index 555..666 100644
40
+ --- a/src/c.ts
41
+ +++ b/src/c.ts
42
+ @@ -1,1 +1,2 @@
43
+ keep
44
+ +added tail
45
+ `;
46
+
47
+ let home: string;
48
+ let restoreUserConfig: () => void;
49
+ let server: DaemonServer;
50
+ let session: Thread;
51
+
52
+ beforeEach(() => {
53
+ home = mkdtempSync(join(tmpdir(), "cueloop-walk-"));
54
+ restoreUserConfig = isolateUserConfig(home);
55
+ server = new DaemonServer({ home, idleExitMs: 0 });
56
+ server.start();
57
+ session = server.core.sessionCreate({
58
+ workspace: { repoRoot: "/repo", branch: "main" },
59
+ artifact: { type: "diff", content: PATCH, meta: { title: "working tree" } },
60
+ });
61
+ });
62
+ afterEach(() => {
63
+ restoreUserConfig();
64
+ server.stop();
65
+ rmSync(home, { recursive: true, force: true });
66
+ });
67
+
68
+ async function renderApp() {
69
+ const setup = await renderReadyApp(<App home={home} sessionId={session.id} />, {
70
+ width: 120,
71
+ height: 34,
72
+ });
73
+
74
+ // Wait for session content, not just the chrome: pressing w before the diff
75
+ // loads drops the walk intent (no session, no diff view) and hangs the test.
76
+ await waitForText(setup, "src/a.ts");
77
+
78
+ return setup;
79
+ }
80
+
81
+ type Setup = Awaited<ReturnType<typeof renderApp>>;
82
+
83
+ /** Foreground colors (hex) of every styled span containing the needle. */
84
+ function foregroundsOf(setup: Setup, needle: string): string[] {
85
+ const foregrounds: string[] = [];
86
+
87
+ for (const line of setup.captureSpans().lines) {
88
+ for (const span of line.spans) {
89
+ if (!span.text.includes(needle)) continue;
90
+ const [red, green, blue] = span.fg.toInts();
91
+
92
+ foregrounds.push(
93
+ "#" + [red, green, blue].map((part) => part.toString(16).padStart(2, "0")).join(""),
94
+ );
95
+ }
96
+ }
97
+
98
+ return foregrounds;
99
+ }
100
+
101
+ describe("the guided walk", () => {
102
+ test("w walks all three files: progress title, persisted marks, end card, submit hand-over", async () => {
103
+ // Arrange
104
+ const setup = await renderApp();
105
+
106
+ // Act
107
+ await pressKey(setup, "k", { meta: true });
108
+
109
+ // Assert
110
+ await waitForText(setup, "file 1 of 3 · 0 viewed");
111
+ expect(setup.captureCharFrame()).toContain("src/a.ts");
112
+ expect(setup.captureCharFrame()).toContain("+1 -1");
113
+
114
+ // Act
115
+ await press(setup, "]");
116
+
117
+ // Assert
118
+ await waitForText(setup, "file 2 of 3 · 1 viewed");
119
+ // the viewed mark rides the session record, not the client
120
+ await waitForState(setup, () =>
121
+ (server.core.sessionGet(session.id).viewedPaths ?? []).includes("src/a.ts"),
122
+ );
123
+
124
+ // Act
125
+ // [ steps back; the revisited card shows its viewed marker
126
+ await press(setup, "[");
127
+
128
+ // Assert
129
+ await waitForText(setup, "file 1 of 3 · 1 viewed");
130
+ expect(setup.captureCharFrame()).toContain("· viewed");
131
+
132
+ // Act
133
+ await press(setup, "]");
134
+
135
+ // Assert
136
+ await waitForText(setup, "file 2 of 3 · 1 viewed");
137
+
138
+ // Act
139
+ await press(setup, "]");
140
+
141
+ // Assert
142
+ await waitForText(setup, "file 3 of 3 · 2 viewed");
143
+
144
+ // Act
145
+ await press(setup, "]");
146
+
147
+ // Assert
148
+ await waitForText(setup, "walk complete");
149
+ expect(setup.captureCharFrame()).toContain("every file viewed (3/3)");
150
+ expect(server.core.sessionGet(session.id).viewedPaths).toEqual([
151
+ "src/a.ts",
152
+ "src/b.ts",
153
+ "src/c.ts",
154
+ ]);
155
+
156
+ // Act
157
+ // the end card's return leaves the walk and opens the rail confirm
158
+ await press(setup, "enter");
159
+
160
+ // Assert
161
+ await waitForText(setup, "send message");
162
+ expect(setup.captureCharFrame()).toContain("3/3 files viewed");
163
+
164
+ // Act
165
+ await press(setup, "enter");
166
+
167
+ // Assert
168
+ await waitForState(setup, () => server.core.sessionGet(session.id).status === "resolved");
169
+ });
170
+
171
+ test("esc keeps progress and w resumes at the first unviewed file", async () => {
172
+ // Arrange
173
+ const setup = await renderApp();
174
+
175
+ await pressKey(setup, "k", { meta: true });
176
+ await waitForText(setup, "file 1 of 3 · 0 viewed");
177
+ await press(setup, "]");
178
+ await waitForText(setup, "file 2 of 3 · 1 viewed");
179
+
180
+ // Act
181
+ await press(setup, "escape");
182
+
183
+ // Assert
184
+ await waitForTextGone(setup, "file 2 of 3");
185
+ // the sheet is back at full strength: no wizard card on screen
186
+ expect(setup.captureCharFrame()).not.toContain("· 1 viewed");
187
+
188
+ // Act
189
+ await pressKey(setup, "k", { meta: true });
190
+
191
+ // Assert
192
+ await waitForText(setup, "file 2 of 3 · 1 viewed");
193
+ });
194
+
195
+ test("a half-walked review resumes across App instances via the daemon", async () => {
196
+ // Arrange
197
+ const first = await renderApp();
198
+
199
+ await pressKey(first, "k", { meta: true });
200
+ await waitForText(first, "file 1 of 3 · 0 viewed");
201
+ await press(first, "]");
202
+ await press(first, "]");
203
+ await waitForText(first, "file 3 of 3 · 2 viewed");
204
+ await waitForState(
205
+ first,
206
+ () => (server.core.sessionGet(session.id).viewedPaths ?? []).length === 2,
207
+ );
208
+ first.renderer.destroy();
209
+
210
+ // Act
211
+ // a fresh client reads the viewed set back from the session record
212
+ const second = await renderApp();
213
+
214
+ await pressKey(second, "k", { meta: true });
215
+
216
+ // Assert
217
+ await waitForText(second, "file 3 of 3 · 2 viewed");
218
+ expect(second.captureCharFrame()).toContain("src/c.ts");
219
+ // two full App boots with daemon round-trips need more than the default
220
+ // budget, and the whole-suite publish lane has timed even 15s out; give the
221
+ // double-boot chain generous headroom so runner load cannot flake it.
222
+ }, 60_000);
223
+
224
+ test("the agent-note block renders only for files carrying a note", async () => {
225
+ // Arrange
226
+ server.core.sessionAnnotate(session.id, {
227
+ id: "note-b",
228
+ kind: "note",
229
+ anchor: { quote: "src/b.ts", prefix: "", suffix: "" },
230
+ body: "Swaps the stale line for the new constant.",
231
+ });
232
+ const setup = await renderApp();
233
+
234
+ // Act
235
+ await pressKey(setup, "k", { meta: true });
236
+
237
+ // Assert
238
+ await waitForText(setup, "file 1 of 3");
239
+ expect(setup.captureCharFrame()).not.toContain("agent note");
240
+
241
+ // Act
242
+ await press(setup, "]");
243
+
244
+ // Assert
245
+ await waitForText(setup, "agent note");
246
+ expect(setup.captureCharFrame()).toContain("Swaps the stale line for the new constant.");
247
+
248
+ // Act
249
+ // a note is agent context, not reviewer feedback: it neither counts nor
250
+ // flips the default verdict, and never comes back in the feedback doc
251
+ await press(setup, "escape");
252
+ await waitForTextGone(setup, "agent note");
253
+ await pressKey(setup, "RETURN", { meta: true });
254
+ // nothing pending despite the note, so the confirm card defaults to approve
255
+ await waitForText(setup, "[Approve]");
256
+ await press(setup, "enter");
257
+
258
+ // Assert
259
+ await waitForState(setup, () => server.core.sessionGet(session.id).status === "resolved");
260
+ const resolved = server.core.sessionGet(session.id);
261
+
262
+ expect(resolved.verdict!.kind).toBe("approve");
263
+ expect(resolved.verdict!.feedback).not.toContain("Swaps the stale line");
264
+ });
265
+
266
+ test("the sheet dims behind the wizard; the preview keeps the diff colors", async () => {
267
+ // Arrange
268
+ const setup = await renderApp();
269
+
270
+ // Assert
271
+ // before the walk the changed word on the added line wears the insertion color
272
+ await waitForText(setup, "+ new line");
273
+ expect(foregroundsOf(setup, "new")).toContain(DARK.insertedForeground);
274
+
275
+ // Act
276
+ await pressKey(setup, "k", { meta: true });
277
+
278
+ // Assert
279
+ await waitForText(setup, "file 1 of 3 · 0 viewed");
280
+ // dimmed: an added line the wizard preview does not show drops to the dim token...
281
+ expect(foregroundsOf(setup, "added tail")).toEqual([DARK.textDim]);
282
+ // ...while the wizard preview keeps the insertion color
283
+ expect(foregroundsOf(setup, "+export const b = 3;")).toContain(DARK.insertedForeground);
284
+ });
285
+
286
+ test("walking a resolved review answers read-only", async () => {
287
+ // Arrange
288
+ server.core.sessionResolve(session.id, "approve", "");
289
+ const setup = await renderApp();
290
+
291
+ // Act - renderApp already waited for the diff to load; the session is resolved
292
+ await pressKey(setup, "k", { meta: true });
293
+
294
+ // Assert
295
+ await waitForText(setup, "review submitted - read-only");
296
+ expect(setup.captureCharFrame()).not.toContain("file 1 of 3");
297
+ });
298
+ });