@kabmo-labs/kabmo 0.8.0 → 0.9.0

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 (254) hide show
  1. package/README.md +7 -0
  2. package/dist/client/index.html +1 -1
  3. package/dist/client/index.js +314 -314
  4. package/dist/client/static/js/{index-qwpvd9fv.js → index-bpptfkz2.js} +922 -892
  5. package/dist/client/static/js/index-k315p1e8.css +1 -0
  6. package/package.json +9 -1
  7. package/src/client/components/common/code-surface/documents.test.ts +120 -0
  8. package/src/client/components/common/code-surface/documents.ts +201 -0
  9. package/src/client/components/common/code-surface/extensions.test.ts +110 -0
  10. package/src/client/components/common/code-surface/extensions.ts +162 -0
  11. package/src/client/components/common/code-surface/handle.ts +102 -0
  12. package/src/client/components/common/code-surface/index.tsx +253 -314
  13. package/src/client/components/common/code-surface/keymap.test.ts +133 -0
  14. package/src/client/components/common/code-surface/keymap.ts +267 -0
  15. package/src/client/components/common/code-surface/marks.ts +74 -0
  16. package/src/client/components/common/code-surface/shiki.ts +234 -0
  17. package/src/client/components/common/code-surface/sync.test.ts +185 -0
  18. package/src/client/components/common/code-surface/sync.ts +58 -0
  19. package/src/client/components/common/code-surface/theme.test.ts +202 -0
  20. package/src/client/components/common/code-surface/theme.ts +111 -0
  21. package/src/client/components/common/editor-view-buttons/index.test.tsx +148 -0
  22. package/src/client/components/common/editor-view-buttons/index.tsx +109 -0
  23. package/src/client/components/common/shell-update-button/Modal.tsx +163 -0
  24. package/src/client/components/common/shell-update-button/index.test.tsx +251 -0
  25. package/src/client/components/common/shell-update-button/index.tsx +125 -0
  26. package/src/client/components/common/toolbar-row/index.test.tsx +166 -0
  27. package/src/client/components/common/toolbar-row/index.tsx +189 -0
  28. package/src/client/components/layout/desktop-layout/WorkspacePanels.tsx +41 -78
  29. package/src/client/components/layout/desktop-layout/index.tsx +47 -67
  30. package/src/client/components/layout/desktop-layout/resizer/index.tsx +6 -3
  31. package/src/client/components/layout/session-sidebar/Footer.tsx +8 -15
  32. package/src/client/components/mobile/LayoutWrapper.tsx +1 -0
  33. package/src/client/components/mobile/mobile-right-sidebar/FullDiff.tsx +3 -0
  34. package/src/client/components/mobile/mobile-right-sidebar/FullEditor.tsx +63 -38
  35. package/src/client/components/mobile/mobile-right-sidebar/OutlineButton.test.tsx +137 -0
  36. package/src/client/components/mobile/mobile-right-sidebar/OutlineButton.tsx +14 -3
  37. package/src/client/components/mobile/mobile-session-sidebar/Footer.tsx +9 -21
  38. package/src/client/components/settings/Modal.tsx +8 -0
  39. package/src/client/components/settings/categories/AppearanceSettings.tsx +3 -0
  40. package/src/client/components/settings/categories/appearance-settings/EditorThemeSection.test.tsx +167 -0
  41. package/src/client/components/settings/categories/appearance-settings/EditorThemeSection.tsx +184 -0
  42. package/src/client/components/settings/categories/project-settings/WorktreeSettings.tsx +8 -2
  43. package/src/client/components/workspace/chat-timeline/EmptyWorkspacePrompt.tsx +16 -8
  44. package/src/client/components/workspace/chat-timeline/SystemNotice.tsx +9 -4
  45. package/src/client/components/workspace/chat-timeline/TaskResultContent.tsx +9 -7
  46. package/src/client/components/workspace/chat-timeline/target-picker/WorkspacePicker.test.tsx +132 -0
  47. package/src/client/components/workspace/chat-timeline/target-picker/WorkspacePicker.tsx +120 -0
  48. package/src/client/components/workspace/chat-timeline/target-picker/WorktreePicker.test.tsx +319 -0
  49. package/src/client/components/workspace/chat-timeline/target-picker/WorktreePicker.tsx +285 -0
  50. package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/index.test.ts +2 -0
  51. package/src/client/components/workspace/chat-timeline/tool-renderers/hashline-patch/index.tsx +34 -29
  52. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/AstEdit.tsx +11 -9
  53. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Bash.tsx +10 -7
  54. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Edit.tsx +25 -8
  55. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Eval.tsx +8 -11
  56. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/FileMatches.test.ts +114 -0
  57. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/FileMatches.tsx +111 -0
  58. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Read.tsx +7 -7
  59. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/SearchTool.tsx +9 -59
  60. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/ArtifactBody.tsx +2 -7
  61. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/DiffView.test.ts +27 -28
  62. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/DiffView.tsx +39 -26
  63. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/ExcerptCode.tsx +8 -8
  64. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/HighlightedCode.tsx +35 -0
  65. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/JsonCodeBlock.tsx +4 -4
  66. package/src/client/components/workspace/diff-panel/MergeDiffView.tsx +179 -0
  67. package/src/client/components/workspace/diff-panel/ThemedMergeDiffView.tsx +24 -0
  68. package/src/client/components/workspace/diff-panel/Toolbar.tsx +224 -186
  69. package/src/client/components/workspace/diff-panel/ToolbarSegments.tsx +98 -0
  70. package/src/client/components/workspace/diff-panel/index.tsx +76 -145
  71. package/src/client/components/workspace/diff-panel/merge.ts +315 -0
  72. package/src/client/components/workspace/editor/Body.tsx +4 -7
  73. package/src/client/components/workspace/editor/CommandPalette.tsx +2 -2
  74. package/src/client/components/workspace/editor/FileDocument.tsx +58 -63
  75. package/src/client/components/workspace/editor/FilePane.tsx +4 -7
  76. package/src/client/components/workspace/editor/FindWidget.tsx +7 -2
  77. package/src/client/components/workspace/editor/Toolbar.tsx +220 -123
  78. package/src/client/components/workspace/editor/index.tsx +7 -9
  79. package/src/client/components/workspace/file-explorer/OutlineSection.test.tsx +18 -0
  80. package/src/client/components/workspace/file-explorer/OutlineSection.tsx +10 -3
  81. package/src/client/components/workspace/search-panel/Body.tsx +1 -1
  82. package/src/client/components/workspace/worktree-modal/index.test.tsx +118 -0
  83. package/src/client/hooks/chat/omp/reconnect.test.ts +338 -0
  84. package/src/client/hooks/chat/omp/reconnect.ts +187 -0
  85. package/src/client/hooks/chat/omp/run-liveness.test.ts +71 -0
  86. package/src/client/hooks/chat/omp/run-liveness.ts +65 -0
  87. package/src/client/hooks/chat/omp/session-list.activity.test.ts +114 -0
  88. package/src/client/hooks/chat/omp/session-list.test-util.ts +164 -0
  89. package/src/client/hooks/chat/omp/session-list.test.ts +30 -150
  90. package/src/client/hooks/chat/omp/session-list.tsx +69 -9
  91. package/src/client/hooks/chat/timeline/generating.ts +19 -2
  92. package/src/client/hooks/chat/timeline/index.ts +19 -24
  93. package/src/client/hooks/chat/timeline/lost-run.test.ts +329 -0
  94. package/src/client/hooks/chat/timeline/lost-run.ts +177 -0
  95. package/src/client/hooks/chat/timeline/session-load.test-util.ts +205 -0
  96. package/src/client/hooks/chat/timeline/session-load.test.ts +61 -162
  97. package/src/client/hooks/chat/timeline/session-load.ts +69 -37
  98. package/src/client/hooks/chat/timeline/session-switch.test.ts +65 -0
  99. package/src/client/hooks/chat/timeline/session-switch.ts +63 -0
  100. package/src/client/hooks/code/deferred-highlight.test.ts +182 -0
  101. package/src/client/hooks/code/deferred-highlight.ts +129 -0
  102. package/src/client/hooks/editor/find-state.ts +1 -1
  103. package/src/client/hooks/editor/theme.test.ts +206 -0
  104. package/src/client/hooks/editor/theme.ts +82 -0
  105. package/src/client/hooks/editor/use-editor-drop.test.ts +101 -0
  106. package/src/client/hooks/editor/use-editor-drop.ts +67 -14
  107. package/src/client/hooks/editor/use-editor-find.ts +7 -7
  108. package/src/client/hooks/editor/use-outline-view.test.ts +182 -0
  109. package/src/client/hooks/editor/use-outline-view.ts +94 -7
  110. package/src/client/hooks/editor/use-scroll-restore.test.ts +55 -2
  111. package/src/client/hooks/editor/use-scroll-restore.ts +124 -4
  112. package/src/client/hooks/editor/use-search-reveal.ts +1 -1
  113. package/src/client/hooks/ui/settings-modal.ts +56 -0
  114. package/src/client/hooks/ui/updates/shell-download.test.ts +2 -0
  115. package/src/client/hooks/ui/updates/shell-download.ts +55 -14
  116. package/src/client/hooks/ui/updates/shell-offer.test.ts +26 -0
  117. package/src/client/hooks/ui/updates/shell-update.test.ts +140 -0
  118. package/src/client/hooks/ui/updates/shell-update.ts +119 -0
  119. package/src/client/hooks/workspace/diff-file.ts +29 -10
  120. package/src/client/hooks/workspace/diff-git-actions.ts +147 -0
  121. package/src/client/hooks/workspace/editor-view.ts +61 -0
  122. package/src/client/hooks/workspace/file-tabs.test.ts +62 -5
  123. package/src/client/hooks/workspace/file-tabs.ts +24 -11
  124. package/src/client/hooks/workspace/navbar-session-actions.ts +72 -0
  125. package/src/client/hooks/workspace/worktree/index.ts +20 -5
  126. package/src/client/lib/desktop/bridge.ts +45 -0
  127. package/src/client/lib/search-reveal.ts +1 -1
  128. package/src/client/main.tsx +5 -1
  129. package/src/client/tailwind.css +37 -8
  130. package/src/server/lib/auth/guard.test.ts +28 -0
  131. package/src/server/lib/auth/guard.ts +10 -0
  132. package/src/server/lib/fs/worktree/create.ts +2 -3
  133. package/src/server/lib/fs/worktree/list.ts +13 -0
  134. package/src/server/lib/fs/worktree/remove.ts +3 -3
  135. package/src/server/lib/lifecycle/claim-port.test.ts +156 -0
  136. package/src/server/lib/lifecycle/port-guard.test.ts +20 -164
  137. package/src/server/lib/lifecycle/port-guard.ts +39 -7
  138. package/src/server/lib/lifecycle/probe-health.test.ts +166 -0
  139. package/src/server/lib/lifecycle/probe.test.ts +77 -164
  140. package/src/server/lib/lifecycle/probe.ts +121 -13
  141. package/src/server/lib/omp/rpc/frame-fold-signals.test.ts +112 -0
  142. package/src/server/lib/omp/rpc/frame-fold.ts +10 -5
  143. package/src/server/lib/omp/session/sidebar-activity.server.test.ts +162 -0
  144. package/src/server/lib/omp/session/sidebar-activity.server.ts +70 -0
  145. package/src/server/lib/omp/session/telemetry/compute.ts +13 -1
  146. package/src/server/lib/omp/session/telemetry.test.ts +33 -0
  147. package/src/server/lib/realtime/signals.server.ts +10 -0
  148. package/src/server/lib/realtime/topics/catalog.server.ts +4 -1
  149. package/src/server/lib/realtime/topics.server.ts +16 -0
  150. package/src/server/lib/updates/shell-assets.ts +94 -3
  151. package/src/server/lib/updates/shell-cache.ts +105 -0
  152. package/src/server/lib/updates/shell-pages.test.ts +33 -2
  153. package/src/server/lib/updates/shell-pages.ts +17 -5
  154. package/src/server/lib/updates/shell-release-desktop.test.ts +166 -0
  155. package/src/server/lib/updates/shell-release.test.ts +10 -27
  156. package/src/server/lib/updates/shell-release.ts +107 -111
  157. package/src/server/routes/fs/git-merge.test.ts +11 -1
  158. package/src/server/routes/fs/read.test.ts +24 -7
  159. package/src/server/routes/fs/scope.test.ts +15 -10
  160. package/src/server/routes/fs/scope.ts +3 -14
  161. package/src/server/routes/updates/android.ts +6 -1
  162. package/src/server/routes/updates/desktop.ts +29 -7
  163. package/src/server/routes/updates/ios.ts +5 -1
  164. package/src/server/routes/worktrees/list.ts +11 -1
  165. package/src/server/routes/worktrees/routes.test.ts +17 -0
  166. package/src/shared/lib/chat/timeline/omp-callbacks.test.ts +36 -0
  167. package/src/shared/lib/chat/timeline/omp-callbacks.ts +18 -0
  168. package/src/shared/lib/chat/timeline/run-signals.test.ts +2 -0
  169. package/src/shared/lib/code/editor/comment-syntax.test.ts +37 -0
  170. package/src/shared/lib/code/editor/comment-syntax.ts +55 -0
  171. package/src/shared/lib/code/editor/find-replace.test.ts +134 -0
  172. package/src/shared/lib/code/editor/{find.ts → find-replace.ts} +23 -107
  173. package/src/shared/lib/code/editor/{find.test.ts → find-scan.test.ts} +19 -73
  174. package/src/shared/lib/code/editor/find-scan.ts +177 -0
  175. package/src/shared/lib/code/editor/keymap.ts +3 -3
  176. package/src/shared/lib/code/editor/lines.test.ts +7 -55
  177. package/src/shared/lib/code/editor/lines.ts +8 -48
  178. package/src/shared/lib/code/editor/marks.test.ts +4 -5
  179. package/src/shared/lib/code/editor/marks.ts +13 -25
  180. package/src/shared/lib/code/editor/theme-loader.ts +97 -0
  181. package/src/shared/lib/code/editor/themes.generated.ts +156 -0
  182. package/src/shared/lib/code/editor/themes.test.ts +165 -0
  183. package/src/shared/lib/code/editor/themes.ts +165 -0
  184. package/src/shared/lib/code/highlight-memo.test.ts +103 -0
  185. package/src/shared/lib/code/highlight-memo.ts +149 -0
  186. package/src/shared/lib/code/highlighter-warmup.ts +112 -0
  187. package/src/shared/lib/code/highlighter.ts +3 -1
  188. package/src/shared/lib/code/idle.ts +37 -0
  189. package/src/shared/lib/code/outline/rows.test.ts +96 -0
  190. package/src/shared/lib/code/outline/rows.ts +94 -0
  191. package/src/shared/lib/code/syntax-highlight.test.ts +1 -79
  192. package/src/shared/lib/code/syntax-highlight.ts +2 -3
  193. package/src/shared/lib/fs/file-kind-classify.test.ts +25 -11
  194. package/src/shared/lib/fs/file-kind.ts +47 -13
  195. package/src/shared/lib/fs/file-url.test.ts +52 -0
  196. package/src/shared/lib/fs/file-url.ts +37 -0
  197. package/src/shared/lib/fs/patch-sides.test.ts +114 -0
  198. package/src/shared/lib/fs/patch-sides.ts +139 -0
  199. package/src/shared/lib/realtime/protocol.ts +16 -0
  200. package/src/shared/lib/ui/toolbar-overflow.test.ts +83 -0
  201. package/src/shared/lib/ui/toolbar-overflow.ts +86 -0
  202. package/src/shared/lib/updates/shell-arch.ts +54 -0
  203. package/src/shared/lib/updates/shell-ua.ts +4 -4
  204. package/src/shared/lib/workspace/editor-view-mode.test.ts +70 -0
  205. package/src/shared/lib/workspace/editor-view-mode.ts +48 -0
  206. package/src/shared/lib/workspace/panel-widths.test.ts +30 -0
  207. package/src/shared/lib/workspace/panel-widths.ts +25 -5
  208. package/src/shared/lib/workspace/session-state/cache.ts +159 -0
  209. package/src/shared/lib/workspace/session-state/harness.test-util.ts +55 -0
  210. package/src/shared/lib/workspace/session-state/merge.ts +100 -0
  211. package/src/shared/lib/workspace/session-state/store-load.test.ts +206 -0
  212. package/src/shared/lib/workspace/session-state/store.test.ts +17 -139
  213. package/src/shared/lib/workspace/session-state/store.ts +92 -130
  214. package/src/shared/lib/workspace/workspace-panel-widths.ts +124 -0
  215. package/src/shared/types/realtime.ts +29 -0
  216. package/src/shared/types/settings/state.ts +10 -0
  217. package/src/test-support/loopback.ts +183 -0
  218. package/dist/client/static/js/index-gtbpewr7.css +0 -1
  219. package/src/client/components/common/code-editor/handler.test.ts +0 -274
  220. package/src/client/components/common/code-editor/handler.ts +0 -175
  221. package/src/client/components/common/code-editor/index.tsx +0 -314
  222. package/src/client/components/common/code-surface/measure.ts +0 -148
  223. package/src/client/components/workspace/chat-timeline/target-picker/index.test.tsx +0 -227
  224. package/src/client/components/workspace/chat-timeline/target-picker/index.tsx +0 -260
  225. package/src/client/components/workspace/diff-panel/SplitView.tsx +0 -209
  226. package/src/client/components/workspace/diff-panel/UnifiedView.tsx +0 -150
  227. package/src/client/hooks/editor/scroll-container.test.ts +0 -102
  228. package/src/client/hooks/editor/scroll-container.ts +0 -26
  229. package/src/client/hooks/editor/use-code-window.ts +0 -100
  230. package/src/client/hooks/editor/use-editor-history.test.ts +0 -216
  231. package/src/client/hooks/editor/use-editor-history.ts +0 -134
  232. package/src/client/hooks/editor/use-occurrences.test.ts +0 -236
  233. package/src/client/hooks/editor/use-occurrences.ts +0 -98
  234. package/src/shared/lib/code/editor/commands.test.ts +0 -155
  235. package/src/shared/lib/code/editor/commands.ts +0 -260
  236. package/src/shared/lib/code/editor/edits.test.ts +0 -100
  237. package/src/shared/lib/code/editor/edits.ts +0 -122
  238. package/src/shared/lib/code/editor/history.test.ts +0 -118
  239. package/src/shared/lib/code/editor/history.ts +0 -101
  240. package/src/shared/lib/code/editor/layers.test.ts +0 -77
  241. package/src/shared/lib/code/editor/layers.ts +0 -67
  242. package/src/shared/lib/code/editor/line-edits.test.ts +0 -131
  243. package/src/shared/lib/code/editor/line-edits.ts +0 -159
  244. package/src/shared/lib/code/editor/occurrences.test.ts +0 -117
  245. package/src/shared/lib/code/editor/occurrences.ts +0 -111
  246. package/src/shared/lib/code/editor/reset-css.test.ts +0 -35
  247. package/src/shared/lib/code/editor/reset-css.ts +0 -32
  248. package/src/shared/lib/code/editor/text-commands.test.ts +0 -178
  249. package/src/shared/lib/code/editor/text-commands.ts +0 -154
  250. package/src/shared/lib/code/lazy-window.test.ts +0 -161
  251. package/src/shared/lib/code/lazy-window.ts +0 -156
  252. package/src/shared/lib/code/windowed-highlight.ts +0 -114
  253. package/src/shared/lib/fs/diff-parser.test.ts +0 -116
  254. package/src/shared/lib/fs/diff-parser.ts +0 -213
@@ -3,12 +3,12 @@ import { FilePane } from '@/client/components/workspace/editor/FilePane';
3
3
  import type { EditorFindState } from '@/client/hooks/editor/use-editor-find';
4
4
  import type { UseFileEditorResult } from '@/client/hooks/editor/use-file-editor';
5
5
  import type { ResolvedPluginFileView } from '@/client/components/workspace/editor/plugin-file-view';
6
- import type { TextRange } from '@/shared/lib/code/editor/commands';
7
6
  import type { EditorCommand } from '@/shared/lib/code/editor/keymap';
8
7
  import type { CodeSurfaceHandle } from '@/client/components/common/code-surface';
9
8
  import type { RefObject } from 'preact';
10
9
  import type { ViewerKind } from '@/shared/lib/fs/document-kind';
11
10
  import type { OpenedFile } from '@/shared/types/fs';
11
+ import type { EditorThemeState } from '@/client/hooks/editor/theme';
12
12
 
13
13
  interface EditorBodyProps {
14
14
  activeFile: OpenedFile | undefined;
@@ -25,12 +25,11 @@ interface EditorBodyProps {
25
25
  setIsMaximized: (next: boolean) => void;
26
26
  zoomLevel: number;
27
27
  setZoomLevel: (update: (current: number) => number) => void;
28
- occurrences: readonly TextRange[];
29
- setOccurrences: (next: readonly TextRange[]) => void;
30
28
  paletteOpen: boolean;
31
29
  setPaletteOpen: (open: boolean) => void;
32
30
  runCommand: (command: EditorCommand) => void;
33
31
  editorFontFamily: string;
32
+ editorTheme: EditorThemeState;
34
33
  togglePreview: () => void;
35
34
  revealActive: boolean;
36
35
  missing: boolean;
@@ -60,12 +59,11 @@ export function EditorBody({
60
59
  setIsMaximized,
61
60
  zoomLevel,
62
61
  setZoomLevel,
63
- occurrences,
64
- setOccurrences,
65
62
  paletteOpen,
66
63
  setPaletteOpen,
67
64
  runCommand,
68
65
  editorFontFamily,
66
+ editorTheme,
69
67
  togglePreview,
70
68
  revealActive,
71
69
  missing,
@@ -113,12 +111,11 @@ export function EditorBody({
113
111
  setIsMaximized={setIsMaximized}
114
112
  zoomLevel={zoomLevel}
115
113
  setZoomLevel={setZoomLevel}
116
- occurrences={occurrences}
117
- setOccurrences={setOccurrences}
118
114
  paletteOpen={paletteOpen}
119
115
  setPaletteOpen={setPaletteOpen}
120
116
  runCommand={runCommand}
121
117
  editorFontFamily={editorFontFamily}
118
+ editorTheme={editorTheme}
122
119
  togglePreview={togglePreview}
123
120
  revealActive={revealActive}
124
121
  missing={missing}
@@ -7,8 +7,8 @@
7
7
  * The editor's command palette (⌘⇧P).
8
8
  *
9
9
  * Every editor command this app implements is listed here with the chord that
10
- * reaches it, both derived from the same keymap table the textarea matches
11
- * against. The palette is therefore a VIEW of the keymap rather than a second
10
+ * reaches it, both derived from the same keymap table the editor binds against.
11
+ * The palette is therefore a VIEW of the keymap rather than a second
12
12
  * list to keep in sync — a command cannot be listed here and unbound there, and
13
13
  * a chord cannot be shown that does not work.
14
14
  *
@@ -1,3 +1,4 @@
1
+ import { useState } from 'preact/hooks';
1
2
  import type { RefObject } from 'preact';
2
3
  import { MarkdownRenderer } from '@/client/components/common/MarkdownRenderer';
3
4
  import { CodeSurface, type CodeSurfaceHandle } from '@/client/components/common/code-surface';
@@ -8,8 +9,8 @@ import type { UseFileEditorResult } from '@/client/hooks/editor/use-file-editor'
8
9
  import type { EditorFindState } from '@/client/hooks/editor/use-editor-find';
9
10
  import { EDITOR_LINE_HEIGHT } from '@/shared/lib/code/editor/typography';
10
11
  import type { ViewerKind } from '@/shared/lib/fs/document-kind';
11
- import type { TextRange } from '@/shared/lib/code/editor/commands';
12
12
  import type { EditorCommand } from '@/shared/lib/code/editor/keymap';
13
+ import type { EditorThemeState } from '@/client/hooks/editor/theme';
13
14
 
14
15
  interface FileDocumentProps {
15
16
  activeFile: { id: number | string; name: string; path: string; root?: string; repo?: string };
@@ -22,8 +23,7 @@ interface FileDocumentProps {
22
23
  wordWrap: boolean;
23
24
  zoomLevel: number;
24
25
  editorFontFamily: string;
25
- occurrences: readonly TextRange[];
26
- onOccurrencesChange: (next: readonly TextRange[]) => void;
26
+ editorTheme: EditorThemeState;
27
27
  /** The surface this file's kind is drawn by (see `document-kind`). */
28
28
  viewerKind: ViewerKind;
29
29
  /** The rendered view is on for the active file — render it, not the source. */
@@ -57,8 +57,7 @@ export function FileDocument({
57
57
  wordWrap,
58
58
  zoomLevel,
59
59
  editorFontFamily,
60
- occurrences,
61
- onOccurrencesChange,
60
+ editorTheme,
62
61
  viewerKind,
63
62
  isPreview,
64
63
  revealActive,
@@ -67,6 +66,12 @@ export function FileDocument({
67
66
  // SVG picture are drawn by the viewer dispatch instead (they replace the code
68
67
  // surface rather than living inside its scroller).
69
68
  const isMd = viewerKind === 'markdown';
69
+ /**
70
+ * The code surface's scroller, which belongs to CodeMirror and appears only
71
+ * once its view mounts. Reported through `onScroller` because a `ref` cannot
72
+ * reach a node the component does not render.
73
+ */
74
+ const [codeScroller, setCodeScroller] = useState<HTMLElement | null>(null);
70
75
  // The document owns its scroller, so it remembers where the reader left it.
71
76
  // One offset per SURFACE: the rendered markdown and its source are different
72
77
  // documents in the same container and share neither a line nor a height.
@@ -74,26 +79,29 @@ export function FileDocument({
74
79
  tabKey: String(activeFile.id),
75
80
  surface: isMd && isPreview ? 'rendered' : 'code',
76
81
  ready: editor.loaded,
82
+ // `undefined` on the markdown branch, which owns its own scroller and uses
83
+ // the binding's `ref` instead.
84
+ element: isMd && isPreview ? undefined : codeScroller,
77
85
  });
78
86
 
79
87
  return (
80
88
  <div className="relative flex-1 min-h-0 flex flex-col">
81
89
  {find.open ? <FindWidget find={find} /> : null}
82
90
  {paletteOpen ? <CommandPalette onRun={onRunCommand} onClose={onClosePalette} /> : null}
83
- <div
84
- ref={scroll.ref}
85
- onScroll={scroll.onScroll}
86
- // No accessible name exists for a scroll container, so the E2E layer
87
- // addresses it by testid (see `tests/e2e/fixtures/selectors.ts`).
88
- data-testid="editor-scroller"
89
- className="flex-1 overflow-auto bg-paper flex"
90
- >
91
- {isMd && isPreview ? (
92
- // No `prose` wrapper: markdown is styled by `.prose-content` alone
93
- // (the same system the chat timeline uses). The typography plugin
94
- // fought it — `.prose img` added 2em vertical margins that ballooned
95
- // a badge row into its own line box, and `--tw-prose-*` colors
96
- // ignored the theme.
91
+ {isMd && isPreview ? (
92
+ <div
93
+ ref={scroll.ref}
94
+ onScroll={scroll.onScroll}
95
+ // No accessible name exists for a scroll container, so the E2E layer
96
+ // addresses it by testid (see `tests/e2e/fixtures/selectors.ts`).
97
+ data-testid="editor-scroller"
98
+ className="flex-1 overflow-auto bg-paper flex"
99
+ >
100
+ {/* No `prose` wrapper: markdown is styled by `.prose-content` alone
101
+ (the same system the chat timeline uses). The typography plugin
102
+ fought it — `.prose img` added 2em vertical margins that ballooned
103
+ a badge row into its own line box, and `--tw-prose-*` colors
104
+ ignored the theme. */}
97
105
  <div className="p-6 max-w-4xl mx-auto font-sans flex-1" style={{ fontSize: `${zoomLevel}px` }}>
98
106
  <MarkdownRenderer
99
107
  content={editor.content}
@@ -101,50 +109,37 @@ export function FileDocument({
101
109
  scope={{ path: activeFile.path, root: activeFile.root, repo: activeFile.repo }}
102
110
  />
103
111
  </div>
104
- ) : (
105
- <CodeSurface
106
- ref={surfaceRef}
107
- value={editor.content}
108
- onValueChange={editor.onChange}
109
- language={editor.language}
110
- wordWrap={wordWrap}
111
- marks={find.open || revealActive ? find.matches : undefined}
112
- currentMark={find.currentIndex}
113
- occurrences={occurrences}
114
- onOccurrencesChange={onOccurrencesChange}
115
- onCommand={onRunCommand}
116
- rootClassName="flex"
117
- gutterClassName="flex flex-col text-right pl-4 pr-3 select-none text-ink/30 font-mono border-r border-ink/10 bg-canvas sticky left-0 z-10"
118
- gutterStyle={{
119
- fontSize: zoomLevel,
120
- paddingTop: 16,
121
- paddingBottom: 16,
122
- lineHeight: EDITOR_LINE_HEIGHT,
123
- fontFamily: editorFontFamily,
124
- }}
125
- gutterLineClassName="min-w-[1.5rem]"
126
- // `min-w-max` keeps a long line intact and lets the panel scroll
127
- // sideways; while wrapping it would instead widen the column past
128
- // the panel, so the toggle had no effect at all.
129
- editorWrapperClassName={`flex-1 code-surface ${wordWrap ? 'min-w-0' : 'min-w-max'}`}
130
- editorPadding={16}
131
- editorClassName="font-mono focus:outline-none"
132
- editorStyle={{
133
- fontFamily: editorFontFamily,
134
- fontSize: zoomLevel,
135
- lineHeight: EDITOR_LINE_HEIGHT,
136
- minHeight: '100%',
137
- // Breathing room under the last line. It has to live on the
138
- // surface, not on the scroll container: a scroll container's own
139
- // bottom padding is not part of its scrollable overflow, so
140
- // `pb-*` on the scroller left the last line flush with the
141
- // panel's edge (measured: padding-bottom 32px did not change
142
- // scrollHeight by a single pixel).
143
- paddingBottom: 16,
144
- }}
145
- />
146
- )}
147
- </div>
112
+ </div>
113
+ ) : (
114
+ // The code surface owns its own scroller (CodeMirror's), so it is NOT
115
+ // wrapped in one: an outer `overflow-auto` would take the document's
116
+ // height and the editor's viewport would be the whole file.
117
+ <CodeSurface
118
+ ref={surfaceRef}
119
+ value={editor.content}
120
+ onValueChange={editor.onChange}
121
+ language={editor.language}
122
+ wordWrap={wordWrap}
123
+ documentKey={String(activeFile.id)}
124
+ marks={find.open || revealActive ? find.matches : undefined}
125
+ currentMark={find.currentIndex}
126
+ onCommand={onRunCommand}
127
+ onScroller={setCodeScroller}
128
+ scrollTestId="editor-scroller"
129
+ // The editor theme paints its own surface, so the app's `bg-paper`
130
+ // must not sit under it: an explicit theme's background is opaque and
131
+ // would be a rectangle inside the panel's own colour.
132
+ className="flex-1 min-h-0 text-ink select-text"
133
+ themeVars={editorTheme.vars}
134
+ themes={editorTheme.themes}
135
+ padding={16}
136
+ style={{
137
+ fontFamily: editorFontFamily,
138
+ fontSize: `${zoomLevel}px`,
139
+ lineHeight: EDITOR_LINE_HEIGHT,
140
+ }}
141
+ />
142
+ )}
148
143
  </div>
149
144
  );
150
145
  }
@@ -8,11 +8,11 @@ import { ViewerSurface, hasViewerSurface } from '@/client/components/workspace/e
8
8
  import type { CodeSurfaceHandle } from '@/client/components/common/code-surface';
9
9
  import type { UseFileEditorResult } from '@/client/hooks/editor/use-file-editor';
10
10
  import type { EditorFindState } from '@/client/hooks/editor/use-editor-find';
11
- import type { TextRange } from '@/shared/lib/code/editor/commands';
12
11
  import type { EditorCommand } from '@/shared/lib/code/editor/keymap';
13
12
  import { EDITOR_MAX_FONT_SIZE, EDITOR_MIN_FONT_SIZE } from '@/shared/lib/code/editor/typography';
14
13
  import { RENDERED_VIEWERS, type ViewerKind } from '@/shared/lib/fs/document-kind';
15
14
  import type { OpenedFile } from '@/shared/types/fs';
15
+ import type { EditorThemeState } from '@/client/hooks/editor/theme';
16
16
 
17
17
  interface FilePaneProps {
18
18
  activeFile: OpenedFile;
@@ -30,12 +30,11 @@ interface FilePaneProps {
30
30
  setIsMaximized: (next: boolean) => void;
31
31
  zoomLevel: number;
32
32
  setZoomLevel: (update: (current: number) => number) => void;
33
- occurrences: readonly TextRange[];
34
- setOccurrences: (next: readonly TextRange[]) => void;
35
33
  paletteOpen: boolean;
36
34
  setPaletteOpen: (open: boolean) => void;
37
35
  runCommand: (command: EditorCommand) => void;
38
36
  editorFontFamily: string;
37
+ editorTheme: EditorThemeState;
39
38
  togglePreview: () => void;
40
39
  /** A search hit was jumped to in this file, so its matches are painted. */
41
40
  revealActive: boolean;
@@ -72,12 +71,11 @@ export function FilePane({
72
71
  setIsMaximized,
73
72
  zoomLevel,
74
73
  setZoomLevel,
75
- occurrences,
76
- setOccurrences,
77
74
  paletteOpen,
78
75
  setPaletteOpen,
79
76
  runCommand,
80
77
  editorFontFamily,
78
+ editorTheme,
81
79
  togglePreview,
82
80
  revealActive,
83
81
  missing,
@@ -216,8 +214,7 @@ export function FilePane({
216
214
  wordWrap={wordWrap}
217
215
  zoomLevel={zoomLevel}
218
216
  editorFontFamily={editorFontFamily}
219
- occurrences={occurrences}
220
- onOccurrencesChange={setOccurrences}
217
+ editorTheme={editorTheme}
221
218
  isPreview={isPreview}
222
219
  viewerKind={viewerKind}
223
220
  revealActive={revealActive}
@@ -22,7 +22,7 @@ import { EDITOR_KEY_BINDINGS, type EditorCommand } from '@/shared/lib/code/edito
22
22
  import { describeBinding } from '@/shared/lib/ui/key-binding';
23
23
  import { isMacPlatform } from '@/shared/lib/util/platform';
24
24
  import type { EditorFindState } from '@/client/hooks/editor/use-editor-find';
25
- import type { FindOptions } from '@/shared/lib/code/editor/find';
25
+ import type { FindOptions } from '@/shared/lib/code/editor/find-scan';
26
26
 
27
27
  const BUTTON_CLASS =
28
28
  'flex items-center justify-center p-1 rounded transition-colors cursor-pointer disabled:cursor-default disabled:opacity-40';
@@ -112,7 +112,12 @@ export function FindWidget({ find }: FindWidgetProps) {
112
112
  );
113
113
 
114
114
  return (
115
- <div className="absolute top-2 right-4 z-30 flex flex-col gap-1 bg-paper border border-ink/15 rounded-md shadow-lg p-1.5 text-xs text-ink font-sans w-[330px]">
115
+ // `w-[330px]` was wider than the panel's own default (320px), so the bar's
116
+ // leftmost control — Show replace — hung past the panel's edge and its click
117
+ // landed on whatever was beside it. A max width keeps the bar inside the
118
+ // panel it belongs to at any width, and `min-w-0` lets the fields shrink
119
+ // with it instead of pushing the buttons out.
120
+ <div className="absolute top-2 right-2 z-30 flex w-[330px] max-w-[calc(100%-1rem)] min-w-0 flex-col gap-1 bg-paper border border-ink/15 rounded-md shadow-lg p-1.5 text-xs text-ink font-sans">
116
121
  <div className="flex items-center gap-1">
117
122
  <button
118
123
  type="button"