@cueloop/client 0.1.0-alpha.10 → 0.1.0-alpha.100

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 (355) 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 +42 -34
  8. package/src/annotations/annotation-palette.ts +62 -0
  9. package/src/annotations/components/AnnotationCards.stories.tsx +86 -0
  10. package/src/annotations/components/AnnotationCards.test.tsx +42 -0
  11. package/src/annotations/components/AnnotationCards.tsx +364 -0
  12. package/src/annotations/components/DiscussionMarkerRail.stories.tsx +54 -0
  13. package/src/annotations/components/DiscussionMarkerRail.tsx +186 -0
  14. package/src/annotations/discussions.ts +77 -0
  15. package/src/annotations/thread-selection.test.ts +210 -0
  16. package/src/annotations/thread-selection.ts +242 -0
  17. package/src/annotations/use-annotation-surface.tsx +1238 -0
  18. package/src/app/App.appearance.test.tsx +53 -0
  19. package/src/{App.autoclose.test.tsx → app/App.autoclose.test.tsx} +77 -51
  20. package/src/app/App.deleted-scroll.test.tsx +187 -0
  21. package/src/app/App.diff.test.tsx +313 -0
  22. package/src/app/App.export.test.tsx +105 -0
  23. package/src/app/App.focus.test.ts +89 -0
  24. package/src/app/App.identity.test.tsx +84 -0
  25. package/src/app/App.plan-render.test.tsx +209 -0
  26. package/src/app/App.plan-review.test.tsx +428 -0
  27. package/src/app/App.prototype-markdown.test.tsx +128 -0
  28. package/src/app/App.prototype.test.tsx +176 -0
  29. package/src/app/App.readonly.test.tsx +210 -0
  30. package/src/app/App.regression.test.tsx +99 -0
  31. package/src/app/App.rendering.test.tsx +134 -0
  32. package/src/app/App.share-dialog.test.tsx +177 -0
  33. package/src/app/App.submit-confirm.test.tsx +267 -0
  34. package/src/app/App.test.tsx +496 -0
  35. package/src/app/App.thread-agent.test.ts +12 -0
  36. package/src/app/App.toast.test.tsx +124 -0
  37. package/src/app/App.tsx +1650 -0
  38. package/src/app/App.visual.test.tsx +59 -0
  39. package/src/app/App.walk.test.tsx +299 -0
  40. package/src/app/App.workbench.test.tsx +1149 -0
  41. package/src/app/app-key-state.ts +61 -0
  42. package/src/app/app-screens.tsx +606 -0
  43. package/src/app/app-view-model.ts +168 -0
  44. package/src/app/components/AppHeader.stories.tsx +50 -0
  45. package/src/app/components/AppHeader.tsx +81 -0
  46. package/src/app/components/AppShell.brand.test.tsx +58 -0
  47. package/src/app/components/AppShell.extensions.test.tsx +191 -0
  48. package/src/app/components/AppShell.stories.tsx +89 -0
  49. package/src/app/components/AppShell.tsx +358 -0
  50. package/src/app/components/AppShell.zoom.test.tsx +149 -0
  51. package/src/app/components/Breadcrumb.stories.tsx +19 -0
  52. package/src/app/components/Breadcrumb.tsx +45 -0
  53. package/src/app/components/MenuBar.stories.tsx +28 -0
  54. package/src/app/components/MenuBar.tsx +44 -0
  55. package/src/app/components/PaneGrid.stories.tsx +84 -0
  56. package/src/app/components/PaneGrid.tsx +128 -0
  57. package/src/app/components/PanelColumn.stories.tsx +25 -0
  58. package/src/app/components/PanelColumn.tsx +93 -0
  59. package/src/app/components/ShellHeader.stories.tsx +50 -0
  60. package/src/app/components/ShellHeader.tsx +137 -0
  61. package/src/app/components/WorkspacePanels.stories.tsx +36 -0
  62. package/src/app/components/WorkspacePanels.tsx +120 -0
  63. package/src/app/components/pages/Workspace.stories.tsx +181 -0
  64. package/src/app/intent-dispatch.test.ts +728 -0
  65. package/src/app/intent-dispatch.ts +687 -0
  66. package/src/app/launch-layout.test.ts +78 -0
  67. package/src/app/launch-layout.ts +57 -0
  68. package/src/app/ready-signal.test.ts +85 -0
  69. package/src/app/ready-signal.ts +52 -0
  70. package/src/app/run.ts +109 -0
  71. package/src/{serve.test.ts → app/serve.test.ts} +24 -12
  72. package/src/app/serve.ts +111 -0
  73. package/src/app/version.ts +5 -0
  74. package/src/appearance/components/theme-context.stories.tsx +46 -0
  75. package/src/appearance/components/theme-context.tsx +37 -0
  76. package/src/appearance/theme-presets.test.ts +51 -0
  77. package/src/appearance/theme-presets.ts +219 -0
  78. package/src/appearance/theme.ts +112 -0
  79. package/src/appearance/visual/contrast.test.ts +35 -0
  80. package/src/appearance/visual/contrast.ts +33 -0
  81. package/src/diff/components/AnnotatableFileView.stories.tsx +42 -0
  82. package/src/diff/components/AnnotatableFileView.test.tsx +157 -0
  83. package/src/diff/components/AnnotatableFileView.tsx +141 -0
  84. package/src/diff/components/BareWorkbenchFileView.stories.tsx +27 -0
  85. package/src/diff/components/BareWorkbenchFileView.tsx +63 -0
  86. package/src/diff/components/DiffContentView.scroll.test.tsx +434 -0
  87. package/src/diff/components/DiffContentView.split.test.tsx +140 -0
  88. package/src/diff/components/DiffContentView.stories.tsx +80 -0
  89. package/src/diff/components/DiffContentView.syntax.test.tsx +82 -0
  90. package/src/diff/components/DiffContentView.tsx +1055 -0
  91. package/src/diff/components/WalkWizard.stories.tsx +100 -0
  92. package/src/diff/components/WalkWizard.tsx +146 -0
  93. package/src/diff/components/diff-content-view-layout.test.ts +198 -0
  94. package/src/diff/components/diff-content-view-layout.ts +84 -0
  95. package/src/diff/diff-fold.test.ts +78 -0
  96. package/src/diff/diff-fold.ts +126 -0
  97. package/src/diff/diff-hunk-curate.test.ts +315 -0
  98. package/src/diff/diff-hunk-curate.ts +152 -0
  99. package/src/diff/diff-intraline.test.ts +239 -0
  100. package/src/diff/diff-intraline.ts +238 -0
  101. package/src/diff/diff-syntax.test.ts +125 -0
  102. package/src/diff/diff-syntax.ts +186 -0
  103. package/src/diff/split-diff.test.ts +110 -0
  104. package/src/diff/split-diff.ts +144 -0
  105. package/src/diff/view-diff.test.ts +325 -0
  106. package/src/diff/view-diff.ts +522 -0
  107. package/src/diff/walk.test.ts +121 -0
  108. package/src/diff/walk.ts +73 -0
  109. package/src/index.ts +31 -2
  110. package/src/integrations/attribution.test.ts +31 -0
  111. package/src/integrations/attribution.ts +42 -0
  112. package/src/integrations/client-extension-registry.test.tsx +61 -0
  113. package/src/integrations/client-extension-registry.ts +159 -0
  114. package/src/integrations/clipboard.test.ts +23 -0
  115. package/src/integrations/clipboard.ts +33 -0
  116. package/src/integrations/components/ExtensionZone.stories.tsx +28 -0
  117. package/src/integrations/components/ExtensionZone.tsx +145 -0
  118. package/src/integrations/components/ShareDialog.stories.tsx +121 -0
  119. package/src/integrations/components/ShareDialog.tsx +550 -0
  120. package/src/integrations/components/share-dialog-store.ts +55 -0
  121. package/src/integrations/editor.test.ts +149 -0
  122. package/src/integrations/editor.ts +160 -0
  123. package/src/integrations/github-identity.test.ts +58 -0
  124. package/src/integrations/github-identity.ts +41 -0
  125. package/src/integrations/herdr.test.ts +43 -0
  126. package/src/integrations/herdr.ts +38 -0
  127. package/src/integrations/integrations.test.ts +41 -0
  128. package/src/integrations/integrations.ts +32 -0
  129. package/src/integrations/pasted-image.test.ts +67 -0
  130. package/src/integrations/pasted-image.ts +60 -0
  131. package/src/integrations/share.ts +238 -0
  132. package/src/keyboard/components/CompletionOverlay.stories.tsx +37 -0
  133. package/src/keyboard/components/CompletionOverlay.tsx +75 -0
  134. package/src/keyboard/components/HandleChipsInput.stories.tsx +24 -0
  135. package/src/keyboard/components/HandleChipsInput.test.tsx +35 -0
  136. package/src/keyboard/components/HandleChipsInput.tsx +143 -0
  137. package/src/keyboard/components/KeybindsDialog.stories.tsx +31 -0
  138. package/src/keyboard/components/KeybindsDialog.tsx +103 -0
  139. package/src/keyboard/components/NavModeHint.stories.tsx +40 -0
  140. package/src/keyboard/components/NavModeHint.tsx +31 -0
  141. package/src/keyboard/components/SlashComposer.stories.tsx +37 -0
  142. package/src/keyboard/components/SlashComposer.tsx +87 -0
  143. package/src/keyboard/key-bindings.test.ts +156 -0
  144. package/src/keyboard/key-bindings.ts +459 -0
  145. package/src/keyboard/keymap.test.ts +611 -0
  146. package/src/keyboard/keymap.ts +395 -0
  147. package/src/keyboard/skills.test.ts +74 -0
  148. package/src/keyboard/skills.ts +75 -0
  149. package/src/keyboard/slash-palette.test.ts +103 -0
  150. package/src/keyboard/slash-palette.ts +129 -0
  151. package/src/keyboard/thread-chords.test.ts +79 -0
  152. package/src/keyboard/thread-chords.ts +163 -0
  153. package/src/keyboard/use-shared-keyboard.ts +55 -0
  154. package/src/markdown/__snapshots__/view-plan.regression.test.ts.snap +100 -0
  155. package/src/markdown/components/MarkdownGridBlock.stories.tsx +33 -0
  156. package/src/markdown/components/MarkdownGridBlock.tsx +72 -0
  157. package/src/markdown/components/MarkdownThreadEditor.stories.tsx +15 -0
  158. package/src/markdown/components/MarkdownThreadEditor.test.tsx +98 -0
  159. package/src/markdown/components/MarkdownThreadEditor.tsx +160 -0
  160. package/src/markdown/components/ThreadView.selection.test.tsx +951 -0
  161. package/src/markdown/components/ThreadView.slash.test.tsx +146 -0
  162. package/src/markdown/components/ThreadView.stories.tsx +26 -0
  163. package/src/markdown/components/ThreadView.tsx +560 -0
  164. package/src/markdown/mark-runs.ts +121 -0
  165. package/src/markdown/markdown-editor-syntax-style.ts +50 -0
  166. package/src/markdown/markdown-grid.test.ts +101 -0
  167. package/src/markdown/markdown-grid.ts +203 -0
  168. package/src/markdown/markdown-highlight.test.ts +61 -0
  169. package/src/markdown/markdown-highlight.ts +124 -0
  170. package/src/markdown/thread-code-syntax.ts +45 -0
  171. package/src/markdown/use-thread-body-editing.tsx +54 -0
  172. package/src/markdown/view-plan.regression.test.ts +60 -0
  173. package/src/markdown/view-plan.test.ts +545 -0
  174. package/src/markdown/view-plan.ts +1009 -0
  175. package/src/performance/otlp-trace.test.ts +119 -0
  176. package/src/performance/otlp-trace.ts +134 -0
  177. package/src/performance/perf-report.ts +18 -0
  178. package/src/performance/perf-timings.test.ts +18 -0
  179. package/src/performance/perf-timings.ts +39 -0
  180. package/src/prototype/components/PrototypeContentView.stories.tsx +19 -0
  181. package/src/prototype/components/PrototypeContentView.tsx +442 -0
  182. package/src/prototype/prototype-browser.test.ts +81 -0
  183. package/src/prototype/prototype-browser.ts +321 -0
  184. package/src/prototype/prototype-pixels.tsx +35 -0
  185. package/src/settings/components/SettingsDialog.stories.tsx +117 -0
  186. package/src/settings/components/SettingsDialog.tsx +183 -0
  187. package/src/settings/components/SettingsRows.stories.tsx +18 -0
  188. package/src/settings/components/SettingsRows.tsx +112 -0
  189. package/src/settings/components/quick-actions-editor.stories.tsx +58 -0
  190. package/src/settings/components/quick-actions-editor.test.tsx +120 -0
  191. package/src/settings/components/quick-actions-editor.tsx +222 -0
  192. package/src/settings/config-identity.test.ts +78 -0
  193. package/src/settings/config.test.ts +676 -0
  194. package/src/settings/config.ts +628 -0
  195. package/src/settings/use-settings-dialog.tsx +351 -0
  196. package/src/stories/__snapshots__/stories.test.tsx.snap +2358 -0
  197. package/src/stories/stories-app.tsx +196 -0
  198. package/src/stories/stories.test.tsx +74 -0
  199. package/src/stories/story-fixtures.ts +102 -0
  200. package/src/stories/story-tree.test.ts +108 -0
  201. package/src/stories/story.ts +154 -0
  202. package/src/terminal/ansi-screen.test.ts +28 -0
  203. package/src/terminal/ansi-screen.ts +72 -0
  204. package/src/terminal/components/AnsiScreen.stories.tsx +12 -0
  205. package/src/terminal/components/AnsiScreen.tsx +28 -0
  206. package/src/terminal/components/terminal-pane.test.tsx +40 -0
  207. package/src/terminal/components/terminal-pane.ts +196 -0
  208. package/src/terminal/ghostty-terminal.test.ts +85 -0
  209. package/src/terminal/ghostty-terminal.ts +197 -0
  210. package/src/terminal/kitty-image.test.ts +66 -0
  211. package/src/terminal/kitty-image.ts +137 -0
  212. package/src/terminal/pty.test.ts +81 -0
  213. package/src/terminal/pty.ts +246 -0
  214. package/src/testing/test-support.test.ts +264 -0
  215. package/src/testing/test-support.ts +367 -0
  216. package/src/thread/agent-continuation.test.ts +32 -0
  217. package/src/thread/agent-continuation.ts +13 -0
  218. package/src/thread/agent-transcript.test.ts +161 -0
  219. package/src/thread/agent-transcript.ts +336 -0
  220. package/src/thread/components/AgentConfigControls.stories.tsx +39 -0
  221. package/src/thread/components/AgentConfigControls.test.tsx +112 -0
  222. package/src/thread/components/AgentConfigControls.tsx +114 -0
  223. package/src/thread/components/AgentThreadPane.stories.tsx +105 -0
  224. package/src/thread/components/AgentThreadPane.test.tsx +829 -0
  225. package/src/thread/components/AgentThreadPane.tsx +367 -0
  226. package/src/thread/components/ThreadFooter.stories.tsx +30 -0
  227. package/src/thread/components/ThreadFooter.test.tsx +49 -0
  228. package/src/thread/components/ThreadFooter.tsx +77 -0
  229. package/src/thread/components/ThreadTree.stories.tsx +46 -0
  230. package/src/thread/components/ThreadTree.test.tsx +41 -0
  231. package/src/thread/components/ThreadTree.tsx +267 -0
  232. package/src/thread/components/thread-tree.test.ts +137 -0
  233. package/src/thread/components/thread-tree.ts +112 -0
  234. package/src/thread/thread-controller.curate.test.ts +502 -0
  235. package/src/thread/thread-controller.derivation.test.ts +62 -0
  236. package/src/thread/thread-controller.live-diff.test.ts +357 -0
  237. package/src/thread/thread-controller.optimistic.test.ts +69 -0
  238. package/src/thread/thread-controller.share.test.ts +573 -0
  239. package/src/thread/thread-controller.switch-cache.test.ts +174 -0
  240. package/src/thread/thread-controller.tree.test.ts +263 -0
  241. package/src/thread/thread-controller.ts +2018 -0
  242. package/src/thread/tree-view.test.ts +128 -0
  243. package/src/thread/tree-view.ts +146 -0
  244. package/src/thread/use-thread-agent.test.tsx +208 -0
  245. package/src/thread/use-thread-agent.ts +111 -0
  246. package/src/ui/components/CodeBlock.stories.tsx +42 -0
  247. package/src/ui/components/CodeBlock.tsx +80 -0
  248. package/src/ui/components/ConfirmCard.stories.tsx +37 -0
  249. package/src/ui/components/ConfirmCard.tsx +151 -0
  250. package/src/ui/components/ConfirmDialog.stories.tsx +21 -0
  251. package/src/ui/components/ConfirmDialog.tsx +72 -0
  252. package/src/ui/components/OverlayScrollbar.stories.tsx +33 -0
  253. package/src/ui/components/OverlayScrollbar.test.tsx +139 -0
  254. package/src/ui/components/OverlayScrollbar.tsx +129 -0
  255. package/src/ui/components/PromptDialog.stories.tsx +36 -0
  256. package/src/ui/components/PromptDialog.test.tsx +65 -0
  257. package/src/ui/components/PromptDialog.tsx +99 -0
  258. package/src/ui/components/RootOverlay.stories.tsx +45 -0
  259. package/src/ui/components/RootOverlay.tsx +49 -0
  260. package/src/ui/components/ScrollArea.stories.tsx +20 -0
  261. package/src/ui/components/ScrollArea.tsx +61 -0
  262. package/src/ui/components/Toast.stories.tsx +19 -0
  263. package/src/ui/components/Toast.tsx +54 -0
  264. package/src/ui/components/Tooltip.stories.tsx +26 -0
  265. package/src/ui/components/Tooltip.tsx +72 -0
  266. package/src/ui/components/menu-control.stories.tsx +35 -0
  267. package/src/ui/components/menu-control.tsx +43 -0
  268. package/src/ui/components/primitives/Button.stories.tsx +32 -0
  269. package/src/ui/components/primitives/Button.tsx +57 -0
  270. package/src/ui/components/primitives/Card.stories.tsx +25 -0
  271. package/src/ui/components/primitives/Card.tsx +62 -0
  272. package/src/ui/components/primitives/Dialog.stories.tsx +24 -0
  273. package/src/ui/components/primitives/Dialog.test.tsx +38 -0
  274. package/src/ui/components/primitives/Dialog.tsx +69 -0
  275. package/src/ui/components/primitives/DialogActions.stories.tsx +20 -0
  276. package/src/ui/components/primitives/DialogActions.tsx +39 -0
  277. package/src/ui/components/primitives/IconButton.stories.tsx +36 -0
  278. package/src/ui/components/primitives/IconButton.tsx +58 -0
  279. package/src/ui/components/primitives/StatusBar.stories.tsx +11 -0
  280. package/src/ui/components/primitives/StatusBar.tsx +23 -0
  281. package/src/ui/components/primitives/Switch.stories.tsx +17 -0
  282. package/src/ui/components/primitives/Switch.tsx +32 -0
  283. package/src/ui/components/primitives/Tabs.stories.tsx +29 -0
  284. package/src/ui/components/primitives/Tabs.tsx +101 -0
  285. package/src/ui/components/primitives/Toolbar.stories.tsx +17 -0
  286. package/src/ui/components/primitives/Toolbar.tsx +14 -0
  287. package/src/ui/components/primitives/Tree.stories.tsx +124 -0
  288. package/src/ui/components/primitives/Tree.tsx +133 -0
  289. package/src/ui/components/primitives/frame.test.ts +12 -0
  290. package/src/ui/components/primitives/frame.ts +17 -0
  291. package/src/ui/components/primitives/icons.ts +61 -0
  292. package/src/ui/components/primitives/tree-model.test.ts +99 -0
  293. package/src/ui/components/primitives/tree-model.ts +137 -0
  294. package/src/ui/components/syntax-highlight.ts +133 -0
  295. package/src/ui/components/truncate-text.ts +7 -0
  296. package/src/ui/components/truncate-title.test.ts +31 -0
  297. package/src/ui/components/truncate-title.ts +15 -0
  298. package/src/ui/use-frame-measure.ts +74 -0
  299. package/src/ui/use-terminal-virtualizer.ts +259 -0
  300. package/src/workbench/components/ChangesColumn.stories.tsx +44 -0
  301. package/src/workbench/components/ChangesColumn.tsx +203 -0
  302. package/src/workbench/components/ChangesFileTree.test.tsx +181 -0
  303. package/src/workbench/components/EditorGrid.split.test.tsx +118 -0
  304. package/src/workbench/components/EditorGrid.stories.tsx +86 -0
  305. package/src/workbench/components/EditorGrid.tabs.test.tsx +118 -0
  306. package/src/workbench/components/EditorGrid.tsx +544 -0
  307. package/src/workbench/components/EditorGrid.zoom.test.tsx +132 -0
  308. package/src/workbench/components/FileContentsView.stories.tsx +16 -0
  309. package/src/workbench/components/FileContentsView.test.tsx +29 -0
  310. package/src/workbench/components/FileContentsView.tsx +100 -0
  311. package/src/workbench/components/GridTabContent.actions.test.tsx +151 -0
  312. package/src/workbench/components/GridTabContent.stories.tsx +59 -0
  313. package/src/workbench/components/GridTabContent.tsx +193 -0
  314. package/src/workbench/components/ProjectTreeView.stories.tsx +25 -0
  315. package/src/workbench/components/ProjectTreeView.test.tsx +236 -0
  316. package/src/workbench/components/ProjectTreeView.tsx +151 -0
  317. package/src/workbench/components/TreePane.stories.tsx +106 -0
  318. package/src/workbench/components/TreePane.tsx +155 -0
  319. package/src/workbench/components/WelcomePlayground.stories.tsx +12 -0
  320. package/src/workbench/components/WelcomePlayground.test.tsx +83 -0
  321. package/src/workbench/components/WelcomePlayground.tsx +138 -0
  322. package/src/workbench/components/editor-grid.test.ts +184 -0
  323. package/src/workbench/components/editor-grid.ts +281 -0
  324. package/src/workbench/components/file-tree.test.ts +60 -0
  325. package/src/workbench/components/file-tree.ts +112 -0
  326. package/src/workbench/use-changes-workbench.ts +205 -0
  327. package/src/workbench/use-remember-layout.ts +31 -0
  328. package/src/workbench/use-repo-changes.ts +91 -0
  329. package/src/workbench/workbench-snapshot.test.ts +99 -0
  330. package/src/workbench/workbench-snapshot.ts +59 -0
  331. package/src/App.diff.test.tsx +0 -99
  332. package/src/App.export.test.tsx +0 -88
  333. package/src/App.plan-review.test.tsx +0 -259
  334. package/src/App.readonly.test.tsx +0 -148
  335. package/src/App.rendering.test.tsx +0 -108
  336. package/src/App.submit-confirm.test.tsx +0 -183
  337. package/src/App.test.tsx +0 -218
  338. package/src/App.tsx +0 -1249
  339. package/src/config.test.ts +0 -116
  340. package/src/config.ts +0 -150
  341. package/src/editor.ts +0 -34
  342. package/src/herdr.test.ts +0 -68
  343. package/src/herdr.ts +0 -34
  344. package/src/keymap.test.ts +0 -270
  345. package/src/keymap.ts +0 -173
  346. package/src/run.ts +0 -30
  347. package/src/serve.ts +0 -75
  348. package/src/session-controller.ts +0 -398
  349. package/src/syntax.test.ts +0 -41
  350. package/src/syntax.ts +0 -88
  351. package/src/theme.ts +0 -46
  352. package/src/view-diff.test.ts +0 -55
  353. package/src/view-diff.ts +0 -86
  354. package/src/view.test.ts +0 -157
  355. package/src/view.ts +0 -364
@@ -0,0 +1,100 @@
1
+ import React from "react";
2
+ import { DARK } from "../../appearance/theme";
3
+ import type { WalkFile } from "../walk";
4
+ import type { Story, StoryMeta } from "../../stories/story";
5
+ import { WalkWizard } from "./WalkWizard";
6
+
7
+ export const meta: StoryMeta = { title: "Overlays/WalkWizard" };
8
+
9
+ const FILES: WalkFile[] = [
10
+ {
11
+ path: "packages/daemon/src/store.ts",
12
+ added: 12,
13
+ removed: 3,
14
+ preview: [
15
+ { sign: "+", text: "const viewed = new Set<string>();" },
16
+ { sign: "+", text: "record.viewedPaths = [...viewed];" },
17
+ { sign: "-", text: "// viewed tracking lives in the client only" },
18
+ ],
19
+ },
20
+ {
21
+ path: "packages/client/src/walk.ts",
22
+ added: 48,
23
+ removed: 0,
24
+ preview: [{ sign: "+", text: "export function firstUnviewedIndex(files, viewed) {" }],
25
+ },
26
+ {
27
+ path: "packages/schema/src/types.ts",
28
+ added: 3,
29
+ removed: 0,
30
+ preview: [{ sign: "+", text: "viewedPaths?: string[];" }],
31
+ },
32
+ ];
33
+
34
+ const noop = (): void => {};
35
+
36
+ export const FirstFile: Story = {
37
+ render: () => (
38
+ <WalkWizard
39
+ files={FILES}
40
+ index={0}
41
+ viewedPaths={new Set()}
42
+ terminalWidth={80}
43
+ onSubmitRequest={noop}
44
+ onBack={noop}
45
+ />
46
+ ),
47
+ // the main card wears the accent border; stats carry the +/- colors
48
+ expectedColors: [
49
+ DARK.accent,
50
+ DARK.green,
51
+ DARK.red,
52
+ DARK.insertedForeground,
53
+ DARK.deletedForeground,
54
+ ],
55
+ };
56
+
57
+ export const WithAgentNote: Story = {
58
+ render: () => (
59
+ <WalkWizard
60
+ files={FILES}
61
+ index={1}
62
+ viewedPaths={new Set(["packages/daemon/src/store.ts"])}
63
+ note="New module: pure helpers that pick the next unviewed file. No IO, unit tested."
64
+ terminalWidth={80}
65
+ onSubmitRequest={noop}
66
+ onBack={noop}
67
+ />
68
+ ),
69
+ // the agent-note block wears the plain gray border token, not the accent
70
+ expectedColors: [DARK.accent, DARK.border, DARK.textMuted],
71
+ };
72
+
73
+ export const EndCard: Story = {
74
+ render: () => (
75
+ <WalkWizard
76
+ files={FILES}
77
+ index={3}
78
+ viewedPaths={new Set(FILES.map((file) => file.path))}
79
+ terminalWidth={80}
80
+ onSubmitRequest={noop}
81
+ onBack={noop}
82
+ />
83
+ ),
84
+ // accent border and solid submit; the all-viewed line turns green
85
+ expectedColors: [DARK.green, DARK.accent, DARK.accentInk],
86
+ };
87
+
88
+ export const EndCardPartialPass: Story = {
89
+ render: () => (
90
+ <WalkWizard
91
+ files={FILES}
92
+ index={3}
93
+ viewedPaths={new Set(["packages/daemon/src/store.ts"])}
94
+ terminalWidth={80}
95
+ onSubmitRequest={noop}
96
+ onBack={noop}
97
+ />
98
+ ),
99
+ expectedColors: [DARK.accent, DARK.accentInk],
100
+ };
@@ -0,0 +1,146 @@
1
+ /**
2
+ * The guided walk's focused card wizard: one card per changed file
3
+ * floating over the dimmed diff, with a plain step count in the title - no
4
+ * progress bar. Below the main card a separate gray-bordered block carries
5
+ * the submitting agent's note for the file, when one exists. Past the last
6
+ * file the end card offers the submit action directly with honest counts.
7
+ * The keys live in the keymap's walk overlay; this component only renders.
8
+ */
9
+
10
+ import React from "react";
11
+ import type { Theme } from "../../appearance/theme";
12
+ import { useComponentTheme } from "../../appearance/components/theme-context";
13
+ import type { WalkFile } from "../walk";
14
+ import { viewedCount } from "../walk";
15
+ import { Card } from "../../ui/components/primitives/Card";
16
+ import { DialogActions } from "../../ui/components/primitives/DialogActions";
17
+
18
+ export interface WalkWizardProps {
19
+ files: WalkFile[];
20
+ index: number;
21
+ viewedPaths: ReadonlySet<string>;
22
+ note?: string;
23
+ terminalWidth: number;
24
+ onSubmitRequest: () => void;
25
+ onBack: () => void;
26
+ theme?: Theme;
27
+ }
28
+
29
+ /** The card never crowds the terminal edge nor stretches past reading width. */
30
+ function wizardWidth(terminalWidth: number): number {
31
+ return Math.min(72, Math.max(24, terminalWidth - 8));
32
+ }
33
+
34
+ /** Note rows from a crude wrap estimate, capped so the block stays a block. */
35
+ function noteRows(note: string, innerWidth: number): number {
36
+ return Math.min(3, Math.max(1, Math.ceil(note.length / Math.max(1, innerWidth))));
37
+ }
38
+
39
+ export function WalkWizard({
40
+ files,
41
+ index,
42
+ viewedPaths,
43
+ note,
44
+ terminalWidth,
45
+ onSubmitRequest,
46
+ onBack,
47
+ theme,
48
+ }: WalkWizardProps): React.ReactNode {
49
+ const tokens = useComponentTheme(theme);
50
+ const width = wizardWidth(terminalWidth);
51
+ const viewed = viewedCount(files, viewedPaths);
52
+ const atEnd = index >= files.length;
53
+ const currentFile = files[index];
54
+
55
+ return (
56
+ <box
57
+ style={{
58
+ position: "absolute",
59
+ left: 0,
60
+ top: 0,
61
+ width: "100%",
62
+ height: "100%",
63
+ alignItems: "center",
64
+ justifyContent: "center",
65
+ flexDirection: "column",
66
+ }}
67
+ >
68
+ <box style={{ width, flexDirection: "column" }}>
69
+ {atEnd || !currentFile ? (
70
+ <Card
71
+ title=" walk complete "
72
+ contentRows={3}
73
+ borderColor={tokens.accent}
74
+ backgroundColor={tokens.elevated}
75
+ theme={theme}
76
+ >
77
+ <text fg={viewed === files.length ? tokens.green : tokens.text}>
78
+ {viewed === files.length
79
+ ? `every file viewed (${viewed}/${files.length})`
80
+ : `${viewed} of ${files.length} files viewed`}
81
+ </text>
82
+ <box style={{ height: 1 }} />
83
+ <DialogActions
84
+ confirmLabel="submit"
85
+ cancelLabel="back"
86
+ onConfirm={onSubmitRequest}
87
+ onCancel={onBack}
88
+ theme={theme}
89
+ />
90
+ </Card>
91
+ ) : (
92
+ <>
93
+ <Card
94
+ title={` file ${index + 1} of ${files.length} · ${viewed} viewed `}
95
+ contentRows={3 + Math.max(1, currentFile.preview.length)}
96
+ borderColor={tokens.accent}
97
+ backgroundColor={tokens.elevated}
98
+ theme={theme}
99
+ >
100
+ <text fg={tokens.text}>{currentFile.path}</text>
101
+ <text>
102
+ <span fg={tokens.green}>{`+${currentFile.added}`}</span>
103
+ <span fg={tokens.textDim}> </span>
104
+ <span fg={tokens.red}>{`-${currentFile.removed}`}</span>
105
+ <span fg={tokens.textDim}>
106
+ {viewedPaths.has(currentFile.path) ? " · viewed" : ""}
107
+ </span>
108
+ </text>
109
+ <box style={{ height: 1 }} />
110
+ {currentFile.preview.length === 0 ? (
111
+ <text fg={tokens.textDim}>(no changed lines)</text>
112
+ ) : (
113
+ currentFile.preview.map((previewLine, previewIndex) => (
114
+ <text
115
+ key={previewIndex}
116
+ style={{ wrapMode: "none" }}
117
+ fg={
118
+ previewLine.sign === "+"
119
+ ? tokens.insertedForeground
120
+ : tokens.deletedForeground
121
+ }
122
+ >
123
+ {previewLine.sign + previewLine.text}
124
+ </text>
125
+ ))
126
+ )}
127
+ </Card>
128
+ {note !== undefined ? (
129
+ <box style={{ marginTop: 1, flexDirection: "column" }}>
130
+ <Card
131
+ title=" agent note "
132
+ contentRows={noteRows(note, width - 4)}
133
+ borderColor={tokens.border}
134
+ backgroundColor={tokens.elevated}
135
+ theme={theme}
136
+ >
137
+ <text fg={tokens.textMuted}>{note}</text>
138
+ </Card>
139
+ </box>
140
+ ) : null}
141
+ </>
142
+ )}
143
+ </box>
144
+ </box>
145
+ );
146
+ }
@@ -0,0 +1,198 @@
1
+ import { describe, expect, test } from "bun:test";
2
+ import type { Annotation } from "@cueloop/schema";
3
+ import {
4
+ commentCountsByFile,
5
+ diffRowText,
6
+ fileChangeCounts,
7
+ marksByRows,
8
+ type DiffRow,
9
+ } from "../view-diff";
10
+ import { fixtureDiffSession } from "../../stories/story-fixtures";
11
+ import type { IntralineRun } from "../diff-intraline";
12
+ import type { SyntaxSpan } from "../diff-syntax";
13
+ import { DARK } from "../../appearance/theme";
14
+ import { coloredRowSpans } from "./diff-content-view-layout";
15
+
16
+ function row(kind: DiffRow["kind"], text: string, extra: Partial<DiffRow> = {}): DiffRow {
17
+ return { kind, text, file: "a.ts", ...extra };
18
+ }
19
+
20
+ function annotation(id: string, anchor: Annotation["anchor"]): Annotation {
21
+ return { id, kind: "comment", body: "note", anchor, createdAt: "2026-01-01T00:00:00Z" };
22
+ }
23
+
24
+ describe("diffRowText", () => {
25
+ test("strips the trailing newline the patch carries", () => {
26
+ // Arrange / Act / Assert
27
+ expect(diffRowText(row("add", "const x = 1;\n"))).toBe("const x = 1;");
28
+ });
29
+ });
30
+
31
+ describe("fileChangeCounts", () => {
32
+ test("tallies added and removed lines per file, ignoring context and headers", () => {
33
+ // Arrange
34
+ const rows = [
35
+ row("file", "a.ts"),
36
+ row("hunk", "@@"),
37
+ row("ctx", "unchanged"),
38
+ row("del", "gone"),
39
+ row("add", "one"),
40
+ row("add", "two"),
41
+ row("file", "b.ts", { file: "b.ts" }),
42
+ row("add", "only add", { file: "b.ts" }),
43
+ ];
44
+
45
+ // Act
46
+ const counts = fileChangeCounts(rows);
47
+
48
+ // Assert
49
+ expect(counts.get("a.ts")).toEqual({ additions: 2, deletions: 1 });
50
+ expect(counts.get("b.ts")).toEqual({ additions: 1, deletions: 0 });
51
+ });
52
+ });
53
+
54
+ describe("marksByRows", () => {
55
+ const rows = [
56
+ row("file", "a.ts"),
57
+ row("hunk", "@@"),
58
+ row("ctx", "keep\n"),
59
+ row("add", "const items = new Map();\n"),
60
+ row("add", "return items;\n"),
61
+ ];
62
+
63
+ test("a whole-row quote (the legacy row anchor) marks that row end to end", () => {
64
+ // Act
65
+ const marks = marksByRows(
66
+ [annotation("a1", { quote: "return items;", prefix: "", suffix: "" })],
67
+ rows,
68
+ );
69
+
70
+ // Assert
71
+ expect([...marks.keys()]).toEqual([4]);
72
+ expect(marks.get(4)![0]).toMatchObject({ start: 0, end: 13, annotationId: "a1" });
73
+ });
74
+
75
+ test("a word quote marks only its characters and carries the span", () => {
76
+ // Act
77
+ const marks = marksByRows(
78
+ [annotation("a1", { quote: "new Map()", prefix: "const items = ", suffix: ";" })],
79
+ rows,
80
+ );
81
+
82
+ // Assert
83
+ const mark = marks.get(3)![0]!;
84
+
85
+ expect(mark.start).toBe(14);
86
+ expect(mark.end).toBe(23);
87
+ expect(mark.span).toEqual({
88
+ start: { blockIndex: 3, char: 14 },
89
+ end: { blockIndex: 3, char: 23 },
90
+ });
91
+ });
92
+
93
+ test("a quote across two rows paints a mark on each row with one shared span", () => {
94
+ // Arrange - the anchor a two-row drag produces: rows joined by the block separator
95
+ const marks = marksByRows(
96
+ [
97
+ annotation("a1", {
98
+ quote: "Map();\n\nreturn",
99
+ prefix: "const items = new ",
100
+ suffix: " items;",
101
+ blockIndex: 3,
102
+ start: 18,
103
+ end: 6,
104
+ endBlockIndex: 4,
105
+ }),
106
+ ],
107
+ rows,
108
+ );
109
+
110
+ // Assert
111
+ expect([...marks.keys()].toSorted()).toEqual([3, 4]);
112
+ expect(marks.get(3)![0]!.span).toEqual(marks.get(4)![0]!.span);
113
+ expect(marks.get(3)![0]).toMatchObject({ start: 18, end: 24 });
114
+ expect(marks.get(4)![0]).toMatchObject({ start: 0, end: 6 });
115
+ });
116
+
117
+ test("the focused annotation wears the focus role", () => {
118
+ // Act
119
+ const marks = marksByRows(
120
+ [annotation("a1", { quote: "keep", prefix: "", suffix: "" })],
121
+ rows,
122
+ "a1",
123
+ );
124
+
125
+ // Assert
126
+ expect(marks.get(2)![0]!.role).toBe("mark-focus");
127
+ });
128
+ });
129
+
130
+ describe("commentCountsByFile", () => {
131
+ const rows = [
132
+ row("file", "a.ts"),
133
+ row("add", "const items = new Map();\n"),
134
+ row("file", "b.ts", { file: "b.ts" }),
135
+ row("add", "return other;\n", { file: "b.ts" }),
136
+ ];
137
+ const annotation = (id: string, quote: string, replyTo?: string): Annotation => {
138
+ const comment: Annotation = {
139
+ id,
140
+ kind: "comment",
141
+ body: id,
142
+ anchor: { quote, prefix: "", suffix: "" },
143
+ createdAt: "2026-01-01T00:00:00Z",
144
+ };
145
+
146
+ if (replyTo !== undefined) comment.replyTo = replyTo;
147
+
148
+ return comment;
149
+ };
150
+
151
+ test("counts a discussion's comments (root plus replies) toward its file", () => {
152
+ // Arrange - one thread on a.ts with a reply, one on b.ts
153
+ const session = fixtureDiffSession({
154
+ annotations: [
155
+ annotation("a1", "const items = new Map();"),
156
+ annotation("a1r", "const items = new Map();", "a1"),
157
+ annotation("b1", "return other;"),
158
+ ],
159
+ });
160
+
161
+ // Act
162
+ const counts = commentCountsByFile(session, rows);
163
+
164
+ // Assert
165
+ expect(counts.get("a.ts")).toBe(2);
166
+ expect(counts.get("b.ts")).toBe(1);
167
+ });
168
+ });
169
+
170
+ describe("coloredRowSpans", () => {
171
+ test("a changed word keeps the diff color while unchanged text dims", () => {
172
+ // Arrange - "old" is the change, " tail" unchanged, on a modified line
173
+ const runs: IntralineRun[] = [
174
+ { text: "old", changed: true },
175
+ { text: " tail", changed: false },
176
+ ];
177
+
178
+ // Act
179
+ const spans = coloredRowSpans("old tail", runs, undefined, DARK.deletedForeground, DARK);
180
+
181
+ // Assert
182
+ expect(spans).toEqual([
183
+ { text: "old", foreground: DARK.deletedForeground },
184
+ { text: " tail", foreground: DARK.textDim },
185
+ ]);
186
+ });
187
+
188
+ test("syntax color applies to unchanged text when there is no intra-line run", () => {
189
+ // Arrange - a keyword span over the whole word, no intra-line runs
190
+ const syntax: SyntaxSpan[] = [{ start: 0, end: 6, group: "keyword" }];
191
+
192
+ // Act
193
+ const spans = coloredRowSpans("return", undefined, syntax, DARK.textMuted, DARK);
194
+
195
+ // Assert
196
+ expect(spans).toEqual([{ text: "return", foreground: DARK.accent }]);
197
+ });
198
+ });
@@ -0,0 +1,84 @@
1
+ /**
2
+ * Pure color logic for the diff review sheet, split out so it can be tested
3
+ * without rendering: a row's colored spans (syntax under the intra-line change
4
+ * color). DiffContentView.tsx paints marks and the caret over these per character.
5
+ */
6
+
7
+ import type { Theme } from "../../appearance/theme";
8
+ import type { IntralineRun } from "../diff-intraline";
9
+ import type { SyntaxSpan } from "../diff-syntax";
10
+ import { colorForSyntaxGroup } from "../../ui/components/syntax-highlight";
11
+
12
+ /** One rendered span of a row: its text and its resolved foreground color. */
13
+ export interface ColoredSpan {
14
+ text: string;
15
+ foreground: string;
16
+ }
17
+
18
+ /**
19
+ * Colored spans for one row, resolved per character: an intra-line changed word
20
+ * keeps the diff color, otherwise the syntax color applies, falling back to the
21
+ * dim token on the unchanged part of a modified line or the row's base color.
22
+ * Adjacent same-color characters coalesce so a row stays a few spans.
23
+ */
24
+ export function coloredRowSpans(
25
+ text: string,
26
+ intralineRuns: IntralineRun[] | undefined,
27
+ syntaxSpans: SyntaxSpan[] | undefined,
28
+ baseColor: string,
29
+ tokens: Theme,
30
+ ): ColoredSpan[] {
31
+ const changed = changedColumns(text.length, intralineRuns);
32
+ const syntaxColorByColumn = syntaxColorColumns(text.length, syntaxSpans, tokens);
33
+ const isModifiedLine = intralineRuns !== undefined;
34
+
35
+ const spans: ColoredSpan[] = [];
36
+
37
+ for (let column = 0; column < text.length; column++) {
38
+ const color = changed[column]
39
+ ? baseColor
40
+ : (syntaxColorByColumn[column] ?? (isModifiedLine ? tokens.textDim : baseColor));
41
+ const previous = spans[spans.length - 1];
42
+
43
+ if (previous && previous.foreground === color) previous.text += text[column];
44
+ else spans.push({ text: text[column]!, foreground: color });
45
+ }
46
+
47
+ return spans;
48
+ }
49
+
50
+ /** Which columns are the intra-line change on a modified line. */
51
+ function changedColumns(length: number, intralineRuns: IntralineRun[] | undefined): boolean[] {
52
+ const changed = Array.from({ length }, () => false);
53
+
54
+ if (!intralineRuns) return changed;
55
+ let offset = 0;
56
+
57
+ for (const run of intralineRuns) {
58
+ if (run.changed)
59
+ for (let index = 0; index < run.text.length; index++) changed[offset + index] = true;
60
+ offset += run.text.length;
61
+ }
62
+
63
+ return changed;
64
+ }
65
+
66
+ /** The syntax color per column, or undefined where a column has no token. */
67
+ function syntaxColorColumns(
68
+ length: number,
69
+ syntaxSpans: SyntaxSpan[] | undefined,
70
+ tokens: Theme,
71
+ ): Array<string | undefined> {
72
+ const colors: Array<string | undefined> = Array.from({ length }, () => undefined);
73
+
74
+ for (const span of syntaxSpans ?? []) {
75
+ const color = colorForSyntaxGroup(span.group, tokens);
76
+
77
+ if (!color) continue;
78
+ for (let column = span.start; column < span.end && column < length; column++) {
79
+ colors[column] = color;
80
+ }
81
+ }
82
+
83
+ return colors;
84
+ }
@@ -0,0 +1,78 @@
1
+ /** Per-file collapse and full-file weave over the flat diff rows. */
2
+
3
+ import { describe, expect, test } from "bun:test";
4
+ import type { DiffRow } from "./view-diff";
5
+ import { applyFold, weaveFullFileRows } from "./diff-fold";
6
+
7
+ function row(kind: DiffRow["kind"], text: string, extra: Partial<DiffRow> = {}): DiffRow {
8
+ return { kind, text, file: "a.ts", ...extra };
9
+ }
10
+
11
+ // a two-line change in a four-line file: line 2 swapped, lines 1/3/4 unchanged
12
+ const base: DiffRow[] = [
13
+ row("file", "a.ts"),
14
+ row("hunk", "@@ -1,3 +1,3 @@"),
15
+ row("ctx", "line one", { oldLine: 1, newLine: 1 }),
16
+ row("del", "old two", { oldLine: 2 }),
17
+ row("add", "new two", { newLine: 2 }),
18
+ row("ctx", "line three", { oldLine: 3, newLine: 3 }),
19
+ ];
20
+
21
+ const contents = {
22
+ path: "a.ts",
23
+ oldContents: "line one\nold two\nline three\nline four\n",
24
+ newContents: "line one\nnew two\nline three\nline four\n",
25
+ };
26
+
27
+ describe("applyFold", () => {
28
+ test("with no fold state it returns the base rows unchanged", () => {
29
+ expect(applyFold(base, new Set(), new Set(), [contents])).toBe(base);
30
+ });
31
+
32
+ test("a collapsed file keeps only its file band", () => {
33
+ // Act
34
+ const folded = applyFold(base, new Set(["a.ts"]), new Set(), [contents]);
35
+
36
+ // Assert
37
+ expect(folded).toHaveLength(1);
38
+ expect(folded[0]).toEqual(row("file", "a.ts"));
39
+ });
40
+
41
+ test("an expanded file weaves the unchanged tail line the hunk never showed", () => {
42
+ // Act
43
+ const folded = applyFold(base, new Set(), new Set(["a.ts"]), [contents]);
44
+
45
+ // Assert - the hunk header is gone and "line four" (outside the hunk) now shows
46
+ expect(folded.some((r) => r.kind === "hunk")).toBe(false);
47
+ const four = folded.find((r) => r.text === "line four");
48
+
49
+ expect(four).toEqual(row("ctx", "line four", { oldLine: 4, newLine: 4 }));
50
+ });
51
+
52
+ test("expand is a no-op without the file's full contents", () => {
53
+ // Act - no contents entry, so the file keeps its hunks
54
+ const folded = applyFold(base, new Set(), new Set(["a.ts"]), undefined);
55
+
56
+ // Assert
57
+ expect(folded).toEqual(base);
58
+ });
59
+ });
60
+
61
+ describe("weaveFullFileRows", () => {
62
+ test("keeps the change rows and fills every unchanged line as context in order", () => {
63
+ // Arrange - the file's code rows (no headers)
64
+ const codeRows = base.slice(2);
65
+
66
+ // Act
67
+ const woven = weaveFullFileRows("a.ts", codeRows, contents.newContents);
68
+
69
+ // Assert - full file: ctx1, del, add, ctx3, ctx4
70
+ expect(woven.map((r) => `${r.kind}:${r.text}`)).toEqual([
71
+ "ctx:line one",
72
+ "del:old two",
73
+ "add:new two",
74
+ "ctx:line three",
75
+ "ctx:line four",
76
+ ]);
77
+ });
78
+ });