@cueloop/client 0.1.0-alpha.9 → 0.1.0-alpha.90

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 (316) 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/App.appearance.test.tsx +53 -0
  9. package/src/App.autoclose.test.tsx +77 -51
  10. package/src/App.diff.test.tsx +259 -45
  11. package/src/App.export.test.tsx +58 -41
  12. package/src/App.focus.test.ts +89 -0
  13. package/src/App.identity.test.tsx +78 -0
  14. package/src/App.plan-render.test.tsx +209 -0
  15. package/src/App.plan-review.test.tsx +330 -161
  16. package/src/App.prototype-markdown.test.tsx +128 -0
  17. package/src/App.prototype.test.tsx +176 -0
  18. package/src/App.readonly.test.tsx +129 -67
  19. package/src/App.regression.test.tsx +94 -0
  20. package/src/App.rendering.test.tsx +61 -35
  21. package/src/App.share-dialog.test.tsx +172 -0
  22. package/src/App.submit-confirm.test.tsx +264 -0
  23. package/src/App.test.tsx +402 -126
  24. package/src/App.toast.test.tsx +98 -0
  25. package/src/App.tsx +1402 -1060
  26. package/src/App.visual.test.tsx +57 -0
  27. package/src/App.walk.test.tsx +299 -0
  28. package/src/App.workbench.test.tsx +774 -0
  29. package/src/__snapshots__/view-plan.regression.test.ts.snap +100 -0
  30. package/src/annotation-palette.ts +62 -0
  31. package/src/ansi-screen.test.ts +28 -0
  32. package/src/ansi-screen.ts +72 -0
  33. package/src/app-key-state.ts +61 -0
  34. package/src/app-screens.tsx +577 -0
  35. package/src/app-view-model.ts +168 -0
  36. package/src/attribution.test.ts +31 -0
  37. package/src/attribution.ts +42 -0
  38. package/src/client-extension-registry.test.tsx +61 -0
  39. package/src/client-extension-registry.ts +159 -0
  40. package/src/clipboard.test.ts +23 -0
  41. package/src/clipboard.ts +33 -0
  42. package/src/components/AnnotatableFileView.stories.tsx +42 -0
  43. package/src/components/AnnotatableFileView.test.tsx +102 -0
  44. package/src/components/AnnotatableFileView.tsx +134 -0
  45. package/src/components/AnnotationCards.stories.tsx +86 -0
  46. package/src/components/AnnotationCards.tsx +341 -0
  47. package/src/components/AnsiScreen.stories.tsx +12 -0
  48. package/src/components/AnsiScreen.tsx +28 -0
  49. package/src/components/AppHeader.stories.tsx +50 -0
  50. package/src/components/AppHeader.tsx +83 -0
  51. package/src/components/AppShell.brand.test.tsx +58 -0
  52. package/src/components/AppShell.extensions.test.tsx +191 -0
  53. package/src/components/AppShell.stories.tsx +89 -0
  54. package/src/components/AppShell.tsx +362 -0
  55. package/src/components/AppShell.zoom.test.tsx +135 -0
  56. package/src/components/BareWorkbenchFileView.stories.tsx +27 -0
  57. package/src/components/BareWorkbenchFileView.tsx +64 -0
  58. package/src/components/Breadcrumb.stories.tsx +19 -0
  59. package/src/components/Breadcrumb.tsx +45 -0
  60. package/src/components/ChangesColumn.stories.tsx +44 -0
  61. package/src/components/ChangesColumn.tsx +199 -0
  62. package/src/components/ChangesFileTree.test.tsx +61 -0
  63. package/src/components/CodeBlock.stories.tsx +42 -0
  64. package/src/components/CodeBlock.tsx +84 -0
  65. package/src/components/CompletionOverlay.stories.tsx +37 -0
  66. package/src/components/CompletionOverlay.tsx +77 -0
  67. package/src/components/ConfirmCard.stories.tsx +37 -0
  68. package/src/components/ConfirmCard.tsx +150 -0
  69. package/src/components/ConfirmDialog.stories.tsx +21 -0
  70. package/src/components/ConfirmDialog.tsx +72 -0
  71. package/src/components/DiffContentView.scroll.test.tsx +149 -0
  72. package/src/components/DiffContentView.split.test.tsx +140 -0
  73. package/src/components/DiffContentView.stories.tsx +80 -0
  74. package/src/components/DiffContentView.syntax.test.tsx +82 -0
  75. package/src/components/DiffContentView.tsx +906 -0
  76. package/src/components/DiscussionMarkerRail.stories.tsx +54 -0
  77. package/src/components/DiscussionMarkerRail.tsx +186 -0
  78. package/src/components/EditorGrid.stories.tsx +59 -0
  79. package/src/components/EditorGrid.tabs.test.tsx +118 -0
  80. package/src/components/EditorGrid.tsx +426 -0
  81. package/src/components/EditorGrid.zoom.test.tsx +68 -0
  82. package/src/components/ExtensionZone.stories.tsx +28 -0
  83. package/src/components/ExtensionZone.tsx +145 -0
  84. package/src/components/FileContentsView.stories.tsx +16 -0
  85. package/src/components/FileContentsView.test.tsx +29 -0
  86. package/src/components/FileContentsView.tsx +98 -0
  87. package/src/components/GridTabContent.stories.tsx +59 -0
  88. package/src/components/GridTabContent.tsx +182 -0
  89. package/src/components/HandleChipsInput.stories.tsx +24 -0
  90. package/src/components/HandleChipsInput.test.tsx +35 -0
  91. package/src/components/HandleChipsInput.tsx +145 -0
  92. package/src/components/KeybindsDialog.stories.tsx +31 -0
  93. package/src/components/KeybindsDialog.tsx +103 -0
  94. package/src/components/MarkdownGridBlock.stories.tsx +33 -0
  95. package/src/components/MarkdownGridBlock.tsx +72 -0
  96. package/src/components/MarkdownThreadEditor.stories.tsx +15 -0
  97. package/src/components/MarkdownThreadEditor.test.tsx +98 -0
  98. package/src/components/MarkdownThreadEditor.tsx +162 -0
  99. package/src/components/MenuBar.stories.tsx +28 -0
  100. package/src/components/MenuBar.tsx +44 -0
  101. package/src/components/NavModeHint.stories.tsx +40 -0
  102. package/src/components/NavModeHint.tsx +31 -0
  103. package/src/components/OverlayScrollbar.stories.tsx +33 -0
  104. package/src/components/OverlayScrollbar.test.tsx +139 -0
  105. package/src/components/OverlayScrollbar.tsx +129 -0
  106. package/src/components/PaneGrid.stories.tsx +84 -0
  107. package/src/components/PaneGrid.tsx +128 -0
  108. package/src/components/PanelColumn.stories.tsx +25 -0
  109. package/src/components/PanelColumn.tsx +98 -0
  110. package/src/components/ProjectTreeView.stories.tsx +25 -0
  111. package/src/components/ProjectTreeView.test.tsx +75 -0
  112. package/src/components/ProjectTreeView.tsx +125 -0
  113. package/src/components/PromptDialog.stories.tsx +36 -0
  114. package/src/components/PromptDialog.test.tsx +61 -0
  115. package/src/components/PromptDialog.tsx +99 -0
  116. package/src/components/PrototypeContentView.stories.tsx +19 -0
  117. package/src/components/PrototypeContentView.tsx +446 -0
  118. package/src/components/RootOverlay.stories.tsx +45 -0
  119. package/src/components/RootOverlay.tsx +48 -0
  120. package/src/components/ScrollArea.stories.tsx +20 -0
  121. package/src/components/ScrollArea.tsx +44 -0
  122. package/src/components/SettingsDialog.stories.tsx +117 -0
  123. package/src/components/SettingsDialog.tsx +186 -0
  124. package/src/components/SettingsRows.stories.tsx +18 -0
  125. package/src/components/SettingsRows.tsx +112 -0
  126. package/src/components/ShareDialog.stories.tsx +121 -0
  127. package/src/components/ShareDialog.tsx +552 -0
  128. package/src/components/ShellHeader.stories.tsx +50 -0
  129. package/src/components/ShellHeader.tsx +137 -0
  130. package/src/components/SlashComposer.stories.tsx +37 -0
  131. package/src/components/SlashComposer.tsx +87 -0
  132. package/src/components/ThreadFooter.stories.tsx +30 -0
  133. package/src/components/ThreadFooter.test.tsx +47 -0
  134. package/src/components/ThreadFooter.tsx +76 -0
  135. package/src/components/ThreadTree.stories.tsx +46 -0
  136. package/src/components/ThreadTree.tsx +255 -0
  137. package/src/components/ThreadView.selection.test.tsx +951 -0
  138. package/src/components/ThreadView.slash.test.tsx +146 -0
  139. package/src/components/ThreadView.stories.tsx +26 -0
  140. package/src/components/ThreadView.tsx +525 -0
  141. package/src/components/Toast.stories.tsx +19 -0
  142. package/src/components/Toast.tsx +52 -0
  143. package/src/components/Tooltip.stories.tsx +26 -0
  144. package/src/components/Tooltip.tsx +72 -0
  145. package/src/components/TreePane.stories.tsx +106 -0
  146. package/src/components/TreePane.tsx +157 -0
  147. package/src/components/WalkWizard.stories.tsx +100 -0
  148. package/src/components/WalkWizard.tsx +150 -0
  149. package/src/components/WelcomePlayground.stories.tsx +12 -0
  150. package/src/components/WelcomePlayground.test.tsx +49 -0
  151. package/src/components/WelcomePlayground.tsx +139 -0
  152. package/src/components/WorkspacePanels.stories.tsx +36 -0
  153. package/src/components/WorkspacePanels.tsx +116 -0
  154. package/src/components/__snapshots__/stories.test.tsx.snap +2264 -0
  155. package/src/components/diff-content-view-layout.test.ts +198 -0
  156. package/src/components/diff-content-view-layout.ts +84 -0
  157. package/src/components/editor-grid.test.ts +86 -0
  158. package/src/components/editor-grid.ts +138 -0
  159. package/src/components/file-tree.test.ts +60 -0
  160. package/src/components/file-tree.ts +112 -0
  161. package/src/components/menu-control.stories.tsx +35 -0
  162. package/src/components/menu-control.tsx +43 -0
  163. package/src/components/pages/Workspace.stories.tsx +181 -0
  164. package/src/components/primitives/Button.stories.tsx +32 -0
  165. package/src/components/primitives/Button.tsx +59 -0
  166. package/src/components/primitives/Card.stories.tsx +25 -0
  167. package/src/components/primitives/Card.tsx +63 -0
  168. package/src/components/primitives/Dialog.stories.tsx +24 -0
  169. package/src/components/primitives/Dialog.test.tsx +37 -0
  170. package/src/components/primitives/Dialog.tsx +71 -0
  171. package/src/components/primitives/DialogActions.stories.tsx +20 -0
  172. package/src/components/primitives/DialogActions.tsx +39 -0
  173. package/src/components/primitives/IconButton.stories.tsx +36 -0
  174. package/src/components/primitives/IconButton.tsx +62 -0
  175. package/src/components/primitives/StatusBar.stories.tsx +11 -0
  176. package/src/components/primitives/StatusBar.tsx +23 -0
  177. package/src/components/primitives/Switch.stories.tsx +17 -0
  178. package/src/components/primitives/Switch.tsx +32 -0
  179. package/src/components/primitives/Tabs.stories.tsx +29 -0
  180. package/src/components/primitives/Tabs.tsx +102 -0
  181. package/src/components/primitives/Toolbar.stories.tsx +17 -0
  182. package/src/components/primitives/Toolbar.tsx +14 -0
  183. package/src/components/primitives/Tree.stories.tsx +124 -0
  184. package/src/components/primitives/Tree.tsx +100 -0
  185. package/src/components/primitives/frame.test.ts +12 -0
  186. package/src/components/primitives/frame.ts +17 -0
  187. package/src/components/primitives/icons.ts +61 -0
  188. package/src/components/primitives/tree-model.test.ts +99 -0
  189. package/src/components/primitives/tree-model.ts +137 -0
  190. package/src/components/quick-actions-editor.stories.tsx +58 -0
  191. package/src/components/quick-actions-editor.test.tsx +117 -0
  192. package/src/components/quick-actions-editor.tsx +226 -0
  193. package/src/components/share-dialog-store.ts +58 -0
  194. package/src/components/stories-app.tsx +193 -0
  195. package/src/components/stories.test.tsx +74 -0
  196. package/src/components/story-fixtures.ts +102 -0
  197. package/src/components/story-tree.test.ts +108 -0
  198. package/src/components/story.ts +159 -0
  199. package/src/components/syntax-highlight.ts +133 -0
  200. package/src/components/terminal-pane.test.tsx +40 -0
  201. package/src/components/terminal-pane.ts +199 -0
  202. package/src/components/theme-context.stories.tsx +46 -0
  203. package/src/components/theme-context.tsx +37 -0
  204. package/src/components/thread-tree.test.ts +134 -0
  205. package/src/components/thread-tree.ts +109 -0
  206. package/src/components/truncate-text.ts +7 -0
  207. package/src/components/truncate-title.test.ts +31 -0
  208. package/src/components/truncate-title.ts +15 -0
  209. package/src/config-identity.test.ts +78 -0
  210. package/src/config.test.ts +579 -34
  211. package/src/config.ts +533 -57
  212. package/src/diff-fold.test.ts +78 -0
  213. package/src/diff-fold.ts +126 -0
  214. package/src/diff-hunk-curate.test.ts +315 -0
  215. package/src/diff-hunk-curate.ts +153 -0
  216. package/src/diff-intraline.test.ts +238 -0
  217. package/src/diff-intraline.ts +236 -0
  218. package/src/diff-syntax.test.ts +112 -0
  219. package/src/diff-syntax.ts +179 -0
  220. package/src/discussions.ts +79 -0
  221. package/src/editor.test.ts +149 -0
  222. package/src/editor.ts +139 -9
  223. package/src/ghostty-terminal.test.ts +85 -0
  224. package/src/ghostty-terminal.ts +199 -0
  225. package/src/github-identity.test.ts +58 -0
  226. package/src/github-identity.ts +41 -0
  227. package/src/herdr.test.ts +9 -34
  228. package/src/herdr.ts +25 -21
  229. package/src/index.ts +28 -0
  230. package/src/integrations.test.ts +41 -0
  231. package/src/integrations.ts +32 -0
  232. package/src/intent-dispatch.test.ts +724 -0
  233. package/src/intent-dispatch.ts +690 -0
  234. package/src/key-bindings.test.ts +156 -0
  235. package/src/key-bindings.ts +459 -0
  236. package/src/keymap.test.ts +456 -115
  237. package/src/keymap.ts +315 -81
  238. package/src/kitty-image.test.ts +66 -0
  239. package/src/kitty-image.ts +138 -0
  240. package/src/launch-layout.test.ts +67 -0
  241. package/src/launch-layout.ts +47 -0
  242. package/src/mark-runs.ts +121 -0
  243. package/src/markdown-editor-syntax-style.ts +50 -0
  244. package/src/markdown-grid.test.ts +101 -0
  245. package/src/markdown-grid.ts +203 -0
  246. package/src/markdown-highlight.test.ts +61 -0
  247. package/src/markdown-highlight.ts +122 -0
  248. package/src/pasted-image.test.ts +67 -0
  249. package/src/pasted-image.ts +60 -0
  250. package/src/perf/otlp-trace.test.ts +116 -0
  251. package/src/perf/otlp-trace.ts +130 -0
  252. package/src/perf/perf-report.ts +17 -0
  253. package/src/perf/perf-timings.test.ts +18 -0
  254. package/src/perf/perf-timings.ts +38 -0
  255. package/src/prototype-browser.test.ts +81 -0
  256. package/src/prototype-browser.ts +329 -0
  257. package/src/prototype-pixels.tsx +35 -0
  258. package/src/pty.test.ts +81 -0
  259. package/src/pty.ts +245 -0
  260. package/src/ready-signal.test.ts +85 -0
  261. package/src/ready-signal.ts +52 -0
  262. package/src/run.ts +85 -12
  263. package/src/serve.test.ts +24 -12
  264. package/src/serve.ts +52 -10
  265. package/src/share.ts +237 -0
  266. package/src/skills.test.ts +74 -0
  267. package/src/skills.ts +75 -0
  268. package/src/slash-palette.test.ts +103 -0
  269. package/src/slash-palette.ts +129 -0
  270. package/src/split-diff.test.ts +110 -0
  271. package/src/split-diff.ts +149 -0
  272. package/src/test-support.test.ts +264 -0
  273. package/src/test-support.ts +369 -0
  274. package/src/theme-presets.test.ts +51 -0
  275. package/src/theme-presets.ts +219 -0
  276. package/src/theme.ts +88 -19
  277. package/src/thread-chords.test.ts +79 -0
  278. package/src/thread-chords.ts +163 -0
  279. package/src/thread-code-syntax.ts +45 -0
  280. package/src/thread-controller.curate.test.ts +476 -0
  281. package/src/thread-controller.derivation.test.ts +62 -0
  282. package/src/thread-controller.live-diff.test.ts +322 -0
  283. package/src/thread-controller.optimistic.test.ts +69 -0
  284. package/src/thread-controller.share.test.ts +561 -0
  285. package/src/thread-controller.switch-cache.test.ts +176 -0
  286. package/src/thread-controller.tree.test.ts +261 -0
  287. package/src/thread-controller.ts +2050 -0
  288. package/src/thread-selection.test.ts +210 -0
  289. package/src/thread-selection.ts +243 -0
  290. package/src/tree-view.test.ts +128 -0
  291. package/src/tree-view.ts +153 -0
  292. package/src/use-annotation-surface.tsx +975 -0
  293. package/src/use-changes-workbench.ts +190 -0
  294. package/src/use-frame-measure.ts +41 -0
  295. package/src/use-remember-layout.ts +31 -0
  296. package/src/use-repo-changes.ts +87 -0
  297. package/src/use-settings-dialog.tsx +364 -0
  298. package/src/use-terminal-virtualizer.ts +259 -0
  299. package/src/use-thread-body-editing.tsx +58 -0
  300. package/src/version.ts +5 -0
  301. package/src/view-diff.test.ts +279 -9
  302. package/src/view-diff.ts +460 -24
  303. package/src/view-plan.regression.test.ts +60 -0
  304. package/src/view-plan.test.ts +545 -0
  305. package/src/view-plan.ts +1019 -0
  306. package/src/visual/contrast.test.ts +35 -0
  307. package/src/visual/contrast.ts +33 -0
  308. package/src/walk.test.ts +121 -0
  309. package/src/walk.ts +74 -0
  310. package/src/workbench-snapshot.test.ts +99 -0
  311. package/src/workbench-snapshot.ts +59 -0
  312. package/src/session-controller.ts +0 -398
  313. package/src/syntax.test.ts +0 -41
  314. package/src/syntax.ts +0 -88
  315. package/src/view.test.ts +0 -157
  316. package/src/view.ts +0 -364
@@ -0,0 +1,577 @@
1
+ import { ScrollArea } from "./components/ScrollArea";
2
+ import React, { useCallback, useMemo, type Dispatch, type SetStateAction } from "react";
3
+ import type { Thread, MessageOutcome } from "@cueloop/schema";
4
+ import { returnPaneFor } from "@cueloop/schema";
5
+ import type { Theme } from "./theme";
6
+ import type { QuickAction } from "./config";
7
+ import type { LaunchLayout } from "./launch-layout";
8
+ import { useRememberLayout } from "./use-remember-layout";
9
+ import type { Mode, TreeAsk } from "./intent-dispatch";
10
+ import type { Intent } from "./keymap";
11
+ import type { ReviewController, ToastState } from "./thread-controller";
12
+ import { noteForFile } from "./walk";
13
+ import type { WalkFile } from "./walk";
14
+ import type { CheatsheetSection } from "./key-bindings";
15
+ import type { SettingsCategory, SettingsValues } from "./components/SettingsDialog";
16
+ import type { SettingsNav } from "./use-settings-dialog";
17
+ import { CLIENT_VERSION } from "./version";
18
+ import { ThemeProvider } from "./components/theme-context";
19
+ import type { InboxRow } from "./components/thread-tree";
20
+ import { SettingsDialog } from "./components/SettingsDialog";
21
+ import { CompletionOverlay } from "./components/CompletionOverlay";
22
+ import { ThreadTree } from "./components/ThreadTree";
23
+ import { WelcomePlayground } from "./components/WelcomePlayground";
24
+ import { AppShell, type FocusPane, type ProjectPanelMode } from "./components/AppShell";
25
+ import type { ClientExtensionRegistry } from "./client-extension-registry";
26
+ import type { ExtensionUIContext } from "@cueloop/extension-api/client";
27
+ import { EditorGrid } from "./components/EditorGrid";
28
+ import { MenuControlProvider, type MenuControlApi } from "./components/menu-control";
29
+ import { ProjectTreeView } from "./components/ProjectTreeView";
30
+ import { ChangesFileTree } from "./components/ChangesColumn";
31
+ import { BareWorkbenchFileView, draftThread } from "./components/BareWorkbenchFileView";
32
+ import { GridTabContent, type DiffSurfaceProps } from "./components/GridTabContent";
33
+ import { useChangesWorkbench } from "./use-changes-workbench";
34
+ import { useRepoChanges } from "./use-repo-changes";
35
+ import { ConfirmDialog } from "./components/ConfirmDialog";
36
+ import { PromptDialog } from "./components/PromptDialog";
37
+ import { WalkWizard } from "./components/WalkWizard";
38
+ import { Toast } from "./components/Toast";
39
+
40
+ export function ErrorScreen({ error, theme }: { error: string; theme: Theme }): React.ReactNode {
41
+ return (
42
+ <ThemeProvider theme={theme}>
43
+ <text fg={theme.red}>cueloop: {error}</text>
44
+ </ThemeProvider>
45
+ );
46
+ }
47
+
48
+ export function ConnectingScreen({ theme }: { theme: Theme }): React.ReactNode {
49
+ return (
50
+ <ThemeProvider theme={theme}>
51
+ <text fg={theme.textDim}>connecting to the daemon…</text>
52
+ </ThemeProvider>
53
+ );
54
+ }
55
+
56
+ export function MenuChrome(props: {
57
+ menuDialog: "keybinds" | "settings" | null;
58
+ theme: Theme;
59
+ keybindsSections: CheatsheetSection[];
60
+ settingsCategories: SettingsCategory[];
61
+ settingsValues: SettingsValues;
62
+ settingsNav: SettingsNav;
63
+ onCategorySelect: (categoryId: string) => void;
64
+ cycleSetting: (rowKey: string) => void;
65
+ onClose: () => void;
66
+ }): React.ReactNode {
67
+ const {
68
+ menuDialog,
69
+ theme,
70
+ keybindsSections,
71
+ settingsCategories,
72
+ settingsValues,
73
+ settingsNav,
74
+ onCategorySelect,
75
+ cycleSetting,
76
+ onClose,
77
+ } = props;
78
+
79
+ // the gear opens the settings dialog directly; Keybinds is a leaf in its tree nav
80
+ if (menuDialog !== "settings") return null;
81
+
82
+ return (
83
+ <SettingsDialog
84
+ isOpen
85
+ version={CLIENT_VERSION}
86
+ keybindsSections={keybindsSections}
87
+ categories={settingsCategories}
88
+ values={settingsValues}
89
+ activeCategoryId={settingsNav.categoryId}
90
+ activeRowIndex={settingsNav.rowIndex}
91
+ activeZone={settingsNav.zone}
92
+ onCategorySelect={onCategorySelect}
93
+ onRowActivate={(row) => cycleSetting(row.key)}
94
+ onClose={onClose}
95
+ theme={theme}
96
+ />
97
+ );
98
+ }
99
+
100
+ /** The welcome shell's Project pane: the launch repo's changed files in changes mode, its full tree otherwise. */
101
+ /** A shared empty rejected-rows set: the bare-launch diff rejects nothing, and this keeps a stable ref. */
102
+ const EMPTY_ROWS: Set<number> = new Set();
103
+
104
+ function WelcomeProjectPanel({
105
+ mode,
106
+ controller,
107
+ onOpenChangedFile,
108
+ onOpenProjectFile,
109
+ focused,
110
+ theme,
111
+ }: {
112
+ mode: ProjectPanelMode;
113
+ controller: ReviewController;
114
+ /** Changes tree click -> open the file's working-tree diff. */
115
+ onOpenChangedFile: (path: string) => void;
116
+ /** Project tree click -> open the file's read-only contents. */
117
+ onOpenProjectFile: (path: string) => void;
118
+ focused?: boolean;
119
+ theme: Theme;
120
+ }): React.ReactNode {
121
+ const changes = useRepoChanges({
122
+ loadChanges: () => controller.repoChanges(),
123
+ visible: mode === "changes",
124
+ diffSourceKey: "welcome",
125
+ });
126
+
127
+ if (mode === "changes") {
128
+ return (
129
+ <ChangesFileTree
130
+ files={changes}
131
+ onSelectFile={onOpenChangedFile}
132
+ focused={focused}
133
+ theme={theme}
134
+ />
135
+ );
136
+ }
137
+
138
+ return (
139
+ <ProjectTreeView
140
+ loadFiles={() => controller.repoFiles()}
141
+ onSelectFile={onOpenProjectFile}
142
+ focused={focused}
143
+ theme={theme}
144
+ />
145
+ );
146
+ }
147
+
148
+ /**
149
+ * The shell with no thread open: the same header and Projects/Threads sidebar as
150
+ * the thread view, and a disposable Welcome tab in the center. There is no
151
+ * separate inbox screen - opening the app lands here, and picking a thread swaps
152
+ * the center for it. Closing the Welcome tab leaves a bare "select a thread" hint.
153
+ */
154
+ export function NoThreadShell(props: {
155
+ toast?: ToastState | null;
156
+ extensionRegistry?: ClientExtensionRegistry;
157
+ extensionContext?: ExtensionUIContext;
158
+ rows: InboxRow[];
159
+ inboxCursor: number;
160
+ /** Open a sidebar thread and move the cursor onto it, so the row shows its selected backdrop at once. */
161
+ onOpenThread: (id: string) => void;
162
+ mode: Mode;
163
+ theme: Theme;
164
+ controller: ReviewController;
165
+ setMode: Dispatch<SetStateAction<Mode>>;
166
+ menuChrome: React.ReactNode;
167
+ onOpenMenu: () => void;
168
+ /** Shared with the thread view, so picking a thread preserves the sidebar. */
169
+ sidebarOpen: boolean;
170
+ onToggleSidebar: () => void;
171
+ focusedPane: FocusPane;
172
+ onFocusPane: (pane: FocusPane) => void;
173
+ /** App owns the one open-menu id, so the bare shell's menus share App's modal keyboard handling. */
174
+ menuControl: MenuControlApi;
175
+ pinnedIds: ReadonlySet<string>;
176
+ onPin: (id: string) => void;
177
+ onRename: (id: string, title: string) => void;
178
+ /** Drives the "/" quick actions and skills in the welcome playground's composer. */
179
+ quickActions: QuickAction[];
180
+ /** Reports the welcome composer's open state, so the shell suspends its inbox keys while typing. */
181
+ onWelcomeComposingChange: (composing: boolean) => void;
182
+ /** The pane composition this vanilla launch restores; changes here are remembered for the next one. */
183
+ layout?: LaunchLayout;
184
+ }): React.ReactNode {
185
+ const {
186
+ rows,
187
+ inboxCursor,
188
+ onOpenThread,
189
+ mode,
190
+ theme,
191
+ controller,
192
+ setMode,
193
+ menuChrome,
194
+ onOpenMenu,
195
+ sidebarOpen,
196
+ onToggleSidebar,
197
+ focusedPane,
198
+ onFocusPane,
199
+ menuControl,
200
+ pinnedIds,
201
+ onPin,
202
+ onRename,
203
+ quickActions,
204
+ onWelcomeComposingChange,
205
+ layout,
206
+ } = props;
207
+ const showExtensionError = useCallback(
208
+ (message: string) => controller.showToast(message),
209
+ [controller],
210
+ );
211
+ const confirming = mode.type === "confirmDelete" ? mode : null;
212
+ // The bare-launch shell is the same four panes as a thread: the Thread pane waits in its empty state
213
+ // and a disposable Welcome tab rides in the Changes editor until a thread or diff is opened.
214
+ const workbench = useChangesWorkbench({ seed: "welcome", layout });
215
+ // the vanilla shell remembers the composition the user leaves it in, for the next bare launch
216
+ useRememberLayout(
217
+ layout,
218
+ true,
219
+ sidebarOpen,
220
+ workbench.changesOpen,
221
+ workbench.projectOpen,
222
+ workbench.zoomed,
223
+ );
224
+ // the Changes tree opens a file's working-tree diff; the Project tree opens read-only contents
225
+ const openChangedFile = (path: string): void => {
226
+ // no thread means no live-diff refresh loop, so re-capture on open or a long-lived shell goes
227
+ // stale; the catch keeps a fire-and-forget refresh from throwing when the shell tears down
228
+ void controller.repoChanges().catch(() => undefined);
229
+ workbench.openFile(path, "diff");
230
+ };
231
+ const openProjectFile = (path: string): void => workbench.openFile(path, "contents");
232
+ // a bare launch has no thread yet, so the diff renders against a draft session; the first note
233
+ // promotes it to the per-repo workbench thread (commentOnWorkbenchDiff)
234
+ const draft = useMemo(() => draftThread(), []);
235
+ const bareSurface: DiffSurfaceProps = {
236
+ session: draft,
237
+ quickActions,
238
+ observer: false,
239
+ commentsEnabled: true,
240
+ resolved: false,
241
+ suspended: focusedPane !== "changes" || menuControl.openMenuId !== null,
242
+ onComposingChange: onWelcomeComposingChange,
243
+ onObserverBlocked: () => {},
244
+ onCursorChange: () => {},
245
+ focusedAnnotationId: undefined,
246
+ onFocusAnnotation: () => {},
247
+ onAnnotate: (span, body) =>
248
+ void controller.commentOnWorkbenchDiff(
249
+ span.start.blockIndex,
250
+ span.start.char,
251
+ span.end.char,
252
+ span.end.blockIndex,
253
+ body,
254
+ ),
255
+ onReply: () => undefined,
256
+ onUpdateAnnotation: () => {},
257
+ onExit: () => {},
258
+ };
259
+
260
+ return (
261
+ <ThemeProvider theme={theme}>
262
+ <MenuControlProvider value={menuControl}>
263
+ <AppShell
264
+ extensionRegistry={props.extensionRegistry}
265
+ extensionContext={props.extensionContext}
266
+ onExtensionError={showExtensionError}
267
+ sidebarOpen={sidebarOpen}
268
+ onToggleSidebar={onToggleSidebar}
269
+ onOpenMenu={onOpenMenu}
270
+ onFocusPane={onFocusPane}
271
+ focusedPane={focusedPane}
272
+ threadsPanel={
273
+ <ScrollArea>
274
+ <ThreadTree
275
+ rows={rows}
276
+ cursor={inboxCursor}
277
+ focused={focusedPane === "threads"}
278
+ pinnedIds={pinnedIds}
279
+ width={30}
280
+ onSelect={onOpenThread}
281
+ onRequestDelete={(id, title) =>
282
+ setMode({ type: "confirmDelete", sessionId: id, title })
283
+ }
284
+ onPin={onPin}
285
+ onRename={onRename}
286
+ theme={theme}
287
+ />
288
+ </ScrollArea>
289
+ }
290
+ threadTitle=""
291
+ threadPanel={
292
+ <box
293
+ style={{
294
+ flexGrow: 1,
295
+ flexDirection: "column",
296
+ alignItems: "center",
297
+ justifyContent: "center",
298
+ }}
299
+ >
300
+ <text fg={theme.textDim}>Select a thread</text>
301
+ </box>
302
+ }
303
+ changesOpen={workbench.changesOpen}
304
+ projectOpen={workbench.projectOpen}
305
+ onToggleChanges={workbench.toggleChanges}
306
+ onToggleProject={workbench.toggleProject}
307
+ onToggleRight={workbench.toggleRight}
308
+ projectMode={workbench.projectMode}
309
+ zoomHideThread={workbench.zoomed}
310
+ changesPanel={
311
+ <EditorGrid
312
+ tree={workbench.grid}
313
+ focusedGroupId={workbench.activeGroup}
314
+ onFocusGroup={workbench.focusGroup}
315
+ onActivateTab={workbench.activate}
316
+ onCloseTab={workbench.close}
317
+ onSplit={workbench.split}
318
+ onZoom={workbench.toggleZoom}
319
+ zoomed={workbench.zoomed}
320
+ renderTab={(tab) =>
321
+ tab.kind === "welcome" ? (
322
+ <WelcomePlayground
323
+ quickActions={quickActions}
324
+ onComposingChange={onWelcomeComposingChange}
325
+ suspended={focusedPane !== "changes" || menuControl.openMenuId !== null}
326
+ theme={theme}
327
+ />
328
+ ) : tab.fileView === "contents" ? (
329
+ <BareWorkbenchFileView
330
+ path={tab.path ?? ""}
331
+ controller={controller}
332
+ quickActions={quickActions}
333
+ onComposingChange={onWelcomeComposingChange}
334
+ onExit={() => {}}
335
+ theme={theme}
336
+ />
337
+ ) : (
338
+ <GridTabContent
339
+ tab={tab}
340
+ rows={controller.rows()}
341
+ surface={bareSurface}
342
+ rejectedRows={EMPTY_ROWS}
343
+ dimmed={false}
344
+ readFile={(path) => controller.repoReadFile(path)}
345
+ onAddFileComment={(path, anchor, body) =>
346
+ void controller.commentOnWorkbench(
347
+ anchor,
348
+ { kind: "file", path, rev: "worktree" },
349
+ body,
350
+ )
351
+ }
352
+ theme={theme}
353
+ />
354
+ )
355
+ }
356
+ theme={theme}
357
+ />
358
+ }
359
+ projectPanel={
360
+ <WelcomeProjectPanel
361
+ mode={workbench.projectMode}
362
+ controller={controller}
363
+ onOpenChangedFile={openChangedFile}
364
+ onOpenProjectFile={openProjectFile}
365
+ focused={focusedPane === "project"}
366
+ theme={theme}
367
+ />
368
+ }
369
+ theme={theme}
370
+ >
371
+ {menuChrome}
372
+ <ConfirmDialog
373
+ isOpen={confirming !== null}
374
+ title=" delete thread "
375
+ message={confirming ? `Delete "${confirming.title}"? This removes the thread.` : ""}
376
+ onConfirm={() => {
377
+ if (confirming) controller.deleteSession(confirming.sessionId);
378
+ setMode({ type: "normal" });
379
+ }}
380
+ onCancel={() => setMode({ type: "normal" })}
381
+ theme={theme}
382
+ />
383
+ {mode.type === "renameThread" ? (
384
+ <PromptDialog
385
+ isOpen
386
+ title=" rename thread "
387
+ value={mode.text}
388
+ placeholder="a short title"
389
+ onInput={(text) => setMode({ ...mode, text })}
390
+ onSave={() => {
391
+ controller.renameSession(mode.sessionId, mode.text.trim());
392
+ setMode({ type: "normal" });
393
+ }}
394
+ onCancel={() => setMode({ type: "normal" })}
395
+ theme={theme}
396
+ />
397
+ ) : null}
398
+ {props.toast ? (
399
+ <Toast title={props.toast.title} body={props.toast.body} theme={theme} />
400
+ ) : null}
401
+ </AppShell>
402
+ </MenuControlProvider>
403
+ </ThemeProvider>
404
+ );
405
+ }
406
+
407
+ export function CompletionScreen(props: {
408
+ theme: Theme;
409
+ session: Thread;
410
+ message: MessageOutcome;
411
+ completion: { phase: "prompt" } | { phase: "counting"; remaining: number };
412
+ status: string;
413
+ onClose: () => void;
414
+ onBackToPlan: () => void;
415
+ onAlways: () => void;
416
+ }): React.ReactNode {
417
+ const { theme, session, message, completion, status, onClose, onBackToPlan, onAlways } = props;
418
+
419
+ return (
420
+ <ThemeProvider theme={theme}>
421
+ <CompletionOverlay
422
+ message={message}
423
+ completion={completion}
424
+ status={status}
425
+ onClose={onClose}
426
+ onBackToPlan={onBackToPlan}
427
+ onAlways={onAlways}
428
+ returnsTo={
429
+ returnPaneFor(session.artifact.meta.herdrPane)
430
+ ? (session.artifact.meta.agent ?? "the agent")
431
+ : undefined
432
+ }
433
+ />
434
+ </ThemeProvider>
435
+ );
436
+ }
437
+
438
+ /** The words each tree prompt uses; enter with an empty summary moves back without one. */
439
+ const TREE_PROMPTS: Record<TreeAsk, { title: string; label: string; placeholder: string }> = {
440
+ branch: { title: " Branch ", label: "Name for the new branch:", placeholder: "alt" },
441
+ label: {
442
+ title: " Checkpoint ",
443
+ label: "Name for this checkpoint:",
444
+ placeholder: "before the rewrite",
445
+ },
446
+ navigate: {
447
+ title: " Move back ",
448
+ label: "Summary of what you leave behind (optional):",
449
+ placeholder: "tried a shorter plan",
450
+ },
451
+ };
452
+
453
+ export function TrailingOverlays(props: {
454
+ walking: boolean;
455
+ walk: { index: number } | null;
456
+ walkFileList: WalkFile[];
457
+ viewedPaths: Set<string>;
458
+ session: Thread;
459
+ terminalWidth: number;
460
+ theme: Theme;
461
+ mode: Mode;
462
+ toast: ToastState | null;
463
+ setMode: Dispatch<SetStateAction<Mode>>;
464
+ dispatch: (intent: Intent) => void;
465
+ }): React.ReactNode {
466
+ const {
467
+ walking,
468
+ walk,
469
+ walkFileList,
470
+ viewedPaths,
471
+ session,
472
+ terminalWidth,
473
+ theme,
474
+ mode,
475
+ toast,
476
+ setMode,
477
+ dispatch,
478
+ } = props;
479
+
480
+ return (
481
+ <>
482
+ {walking && walk !== null ? (
483
+ <WalkWizard
484
+ files={walkFileList}
485
+ index={walk.index}
486
+ viewedPaths={viewedPaths}
487
+ note={
488
+ walkFileList[walk.index] !== undefined
489
+ ? noteForFile(session.annotations, walkFileList[walk.index]!.path)
490
+ : undefined
491
+ }
492
+ terminalWidth={terminalWidth}
493
+ onSubmitRequest={() => {
494
+ dispatch({ type: "walkLeave" });
495
+ dispatch({ type: "openSubmit" });
496
+ }}
497
+ onBack={() => dispatch({ type: "walkBack" })}
498
+ />
499
+ ) : null}
500
+ {mode.type === "rename" ? (
501
+ <PromptDialog
502
+ isOpen
503
+ title=" Rename author "
504
+ label="Display name for this collaborator:"
505
+ value={mode.text}
506
+ placeholder="their name"
507
+ onInput={(text) => setMode({ ...mode, text })}
508
+ onSave={() => dispatch({ type: "confirmDialog" })}
509
+ onCancel={() => setMode({ type: "normal" })}
510
+ theme={theme}
511
+ />
512
+ ) : null}
513
+ {mode.type === "renameThread" ? (
514
+ <PromptDialog
515
+ isOpen
516
+ title=" rename thread "
517
+ value={mode.text}
518
+ placeholder="a short title"
519
+ onInput={(text) => setMode({ ...mode, text })}
520
+ onSave={() => dispatch({ type: "confirmDialog" })}
521
+ onCancel={() => setMode({ type: "normal" })}
522
+ theme={theme}
523
+ />
524
+ ) : null}
525
+ {mode.type === "confirmDelete" ? (
526
+ <ConfirmDialog
527
+ isOpen
528
+ title=" delete thread "
529
+ message={`Delete "${mode.title}"? This removes the thread.`}
530
+ onConfirm={() => dispatch({ type: "confirmDialog" })}
531
+ onCancel={() => setMode({ type: "normal" })}
532
+ theme={theme}
533
+ />
534
+ ) : null}
535
+ {mode.type === "nameSelf" ? (
536
+ <PromptDialog
537
+ isOpen
538
+ title=" Welcome "
539
+ label="Your name (optional) - it attributes the notes you leave:"
540
+ value={mode.text}
541
+ placeholder="your name"
542
+ onInput={(text) => setMode({ ...mode, text })}
543
+ onSave={() => dispatch({ type: "confirmDialog" })}
544
+ onCancel={() => setMode({ type: "normal" })}
545
+ theme={theme}
546
+ />
547
+ ) : null}
548
+ {mode.type === "renameSelf" ? (
549
+ <PromptDialog
550
+ isOpen
551
+ title=" Display name "
552
+ label="Your display name - it attributes the notes you leave:"
553
+ value={mode.text}
554
+ placeholder="your name"
555
+ onInput={(text) => setMode({ ...mode, text })}
556
+ onSave={() => dispatch({ type: "confirmDialog" })}
557
+ onCancel={() => setMode({ type: "normal" })}
558
+ theme={theme}
559
+ />
560
+ ) : null}
561
+ {mode.type === "treePrompt" ? (
562
+ <PromptDialog
563
+ isOpen
564
+ title={TREE_PROMPTS[mode.ask].title}
565
+ label={TREE_PROMPTS[mode.ask].label}
566
+ value={mode.text}
567
+ placeholder={TREE_PROMPTS[mode.ask].placeholder}
568
+ onInput={(text) => setMode({ ...mode, text })}
569
+ onSave={() => dispatch({ type: "confirmDialog" })}
570
+ onCancel={() => setMode({ type: "normal" })}
571
+ theme={theme}
572
+ />
573
+ ) : null}
574
+ {toast ? <Toast title={toast.title} body={toast.body} theme={theme} /> : null}
575
+ </>
576
+ );
577
+ }