@kabmo-labs/kabmo 0.7.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 (351) hide show
  1. package/README.md +7 -0
  2. package/dist/client/index.html +1 -1
  3. package/dist/client/index.js +403 -384
  4. package/dist/client/static/js/{index-0wvbca0b.js → index-bpptfkz2.js} +905 -875
  5. package/dist/client/static/js/index-k315p1e8.css +1 -0
  6. package/package.json +10 -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/header-controls/index.tsx +24 -1
  24. package/src/client/components/common/header-controls/shell-chrome.test.tsx +272 -0
  25. package/src/client/components/common/shell-update-button/Modal.tsx +163 -0
  26. package/src/client/components/common/shell-update-button/index.test.tsx +251 -0
  27. package/src/client/components/common/shell-update-button/index.tsx +125 -0
  28. package/src/client/components/common/toolbar-row/index.test.tsx +166 -0
  29. package/src/client/components/common/toolbar-row/index.tsx +189 -0
  30. package/src/client/components/common/workspace-options-menu/index.tsx +19 -1
  31. package/src/client/components/layout/desktop-layout/TopNavbar.tsx +5 -6
  32. package/src/client/components/layout/desktop-layout/WorkspacePanels.tsx +41 -78
  33. package/src/client/components/layout/desktop-layout/index.tsx +55 -69
  34. package/src/client/components/layout/desktop-layout/resizer/index.tsx +6 -3
  35. package/src/client/components/layout/session-sidebar/CategoryHeader.tsx +24 -0
  36. package/src/client/components/layout/session-sidebar/CategoryHeader.worktree.test.tsx +126 -0
  37. package/src/client/components/layout/session-sidebar/CategoryItem.tsx +4 -0
  38. package/src/client/components/layout/session-sidebar/Footer.tsx +8 -15
  39. package/src/client/components/layout/session-sidebar/SessionItem.tsx +12 -0
  40. package/src/client/components/layout/session-sidebar/SessionList.tsx +4 -0
  41. package/src/client/components/layout/session-sidebar/SessionsView.tsx +24 -0
  42. package/src/client/components/layout/session-sidebar/WorktreeBadge.test.ts +48 -0
  43. package/src/client/components/layout/session-sidebar/WorktreeBadge.tsx +55 -0
  44. package/src/client/components/mobile/LayoutWrapper.tsx +23 -16
  45. package/src/client/components/mobile/RightSidebar.tsx +21 -10
  46. package/src/client/components/mobile/SessionSidebar.tsx +8 -0
  47. package/src/client/components/mobile/header-titlebar.test.tsx +172 -0
  48. package/src/client/components/mobile/mobile-main-view/Header.tsx +10 -1
  49. package/src/client/components/mobile/mobile-right-sidebar/FullDiff.tsx +3 -0
  50. package/src/client/components/mobile/mobile-right-sidebar/FullEditor.tsx +63 -38
  51. package/src/client/components/mobile/mobile-right-sidebar/OutlineButton.test.tsx +137 -0
  52. package/src/client/components/mobile/mobile-right-sidebar/OutlineButton.tsx +14 -3
  53. package/src/client/components/mobile/mobile-session-sidebar/Footer.tsx +9 -21
  54. package/src/client/components/mobile/mobile-session-sidebar/Header.tsx +6 -2
  55. package/src/client/components/mobile/mobile-session-sidebar/Item.tsx +16 -0
  56. package/src/client/components/mobile/mobile-session-sidebar/List.tsx +4 -0
  57. package/src/client/components/mobile/mobile-session-sidebar/SessionRow.tsx +5 -0
  58. package/src/client/components/mobile/mobile-session-sidebar/WorktreeDialog.tsx +49 -0
  59. package/src/client/components/mobile/worktree-session.ts +51 -0
  60. package/src/client/components/settings/Modal.tsx +8 -0
  61. package/src/client/components/settings/categories/AppearanceSettings.tsx +3 -0
  62. package/src/client/components/settings/categories/ProjectSettings.tsx +22 -8
  63. package/src/client/components/settings/categories/appearance-settings/EditorThemeSection.test.tsx +167 -0
  64. package/src/client/components/settings/categories/appearance-settings/EditorThemeSection.tsx +184 -0
  65. package/src/client/components/settings/categories/project-settings/WorktreeSettings.tsx +146 -0
  66. package/src/client/components/settings/categories/project-settings/worktree-row.test.ts +83 -0
  67. package/src/client/components/settings/categories/project-settings/worktree-row.ts +47 -0
  68. package/src/client/components/workspace/chat-timeline/EmptyWorkspacePrompt.tsx +36 -55
  69. package/src/client/components/workspace/chat-timeline/PendingChatView.tsx +8 -0
  70. package/src/client/components/workspace/chat-timeline/SystemNotice.tsx +9 -4
  71. package/src/client/components/workspace/chat-timeline/TaskResultContent.tsx +9 -7
  72. package/src/client/components/workspace/chat-timeline/index.tsx +5 -0
  73. package/src/client/components/workspace/chat-timeline/target-picker/WorkspacePicker.test.tsx +132 -0
  74. package/src/client/components/workspace/chat-timeline/target-picker/WorkspacePicker.tsx +120 -0
  75. package/src/client/components/workspace/chat-timeline/target-picker/WorktreePicker.test.tsx +319 -0
  76. package/src/client/components/workspace/chat-timeline/target-picker/WorktreePicker.tsx +285 -0
  77. package/src/client/components/workspace/chat-timeline/tool-renderers/ask-panel/index.test.ts +2 -0
  78. package/src/client/components/workspace/chat-timeline/tool-renderers/hashline-patch/index.tsx +34 -29
  79. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/AstEdit.tsx +11 -9
  80. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Bash.tsx +10 -7
  81. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Edit.tsx +25 -8
  82. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Eval.test.tsx +146 -0
  83. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Eval.tsx +32 -17
  84. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/FileMatches.test.ts +114 -0
  85. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/FileMatches.tsx +111 -0
  86. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Read.tsx +7 -7
  87. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/SearchTool.tsx +9 -59
  88. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/ArtifactBody.tsx +2 -7
  89. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/DiffView.test.ts +27 -28
  90. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/DiffView.tsx +39 -26
  91. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/ExcerptCode.tsx +8 -8
  92. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/HighlightedCode.tsx +35 -0
  93. package/src/client/components/workspace/chat-timeline/tool-renderers/shared/JsonCodeBlock.tsx +4 -4
  94. package/src/client/components/workspace/diff-panel/MergeDiffView.tsx +179 -0
  95. package/src/client/components/workspace/diff-panel/ThemedMergeDiffView.tsx +24 -0
  96. package/src/client/components/workspace/diff-panel/Toolbar.tsx +224 -186
  97. package/src/client/components/workspace/diff-panel/ToolbarSegments.tsx +98 -0
  98. package/src/client/components/workspace/diff-panel/index.tsx +76 -145
  99. package/src/client/components/workspace/diff-panel/merge.ts +315 -0
  100. package/src/client/components/workspace/editor/Body.tsx +4 -7
  101. package/src/client/components/workspace/editor/CommandPalette.tsx +2 -2
  102. package/src/client/components/workspace/editor/FileDocument.tsx +58 -63
  103. package/src/client/components/workspace/editor/FilePane.tsx +4 -7
  104. package/src/client/components/workspace/editor/FindWidget.tsx +7 -2
  105. package/src/client/components/workspace/editor/Toolbar.tsx +220 -123
  106. package/src/client/components/workspace/editor/index.tsx +7 -9
  107. package/src/client/components/workspace/file-explorer/ContextMenu.tsx +2 -1
  108. package/src/client/components/workspace/file-explorer/Modals.tsx +40 -12
  109. package/src/client/components/workspace/file-explorer/OutlineSection.test.tsx +18 -0
  110. package/src/client/components/workspace/file-explorer/OutlineSection.tsx +10 -3
  111. package/src/client/components/workspace/file-explorer/RootMenu.tsx +14 -5
  112. package/src/client/components/workspace/file-explorer/Toolbar.tsx +74 -0
  113. package/src/client/components/workspace/file-explorer/TreeItem.tsx +38 -3
  114. package/src/client/components/workspace/file-explorer/filter.ts +47 -0
  115. package/src/client/components/workspace/file-explorer/index.tsx +77 -64
  116. package/src/client/components/workspace/file-explorer/local-edit.test.ts +135 -0
  117. package/src/client/components/workspace/search-panel/Body.tsx +1 -1
  118. package/src/client/components/workspace/worktree-modal/index.test.tsx +118 -0
  119. package/src/client/components/workspace/worktree-modal/index.tsx +261 -0
  120. package/src/client/hooks/chat/omp/reconnect.test.ts +338 -0
  121. package/src/client/hooks/chat/omp/reconnect.ts +187 -0
  122. package/src/client/hooks/chat/omp/run-liveness.test.ts +71 -0
  123. package/src/client/hooks/chat/omp/run-liveness.ts +65 -0
  124. package/src/client/hooks/chat/omp/session-list.activity.test.ts +114 -0
  125. package/src/client/hooks/chat/omp/session-list.test-util.ts +164 -0
  126. package/src/client/hooks/chat/omp/session-list.test.ts +30 -150
  127. package/src/client/hooks/chat/omp/session-list.tsx +69 -9
  128. package/src/client/hooks/chat/timeline/generating.ts +19 -2
  129. package/src/client/hooks/chat/timeline/index.ts +27 -28
  130. package/src/client/hooks/chat/timeline/lost-run.test.ts +329 -0
  131. package/src/client/hooks/chat/timeline/lost-run.ts +177 -0
  132. package/src/client/hooks/chat/timeline/send-execute.test.ts +2 -0
  133. package/src/client/hooks/chat/timeline/send-steer.test.ts +2 -0
  134. package/src/client/hooks/chat/timeline/send.test.ts +2 -0
  135. package/src/client/hooks/chat/timeline/send.ts +11 -1
  136. package/src/client/hooks/chat/timeline/session-load.test-util.ts +205 -0
  137. package/src/client/hooks/chat/timeline/session-load.test.ts +61 -162
  138. package/src/client/hooks/chat/timeline/session-load.ts +69 -37
  139. package/src/client/hooks/chat/timeline/session-switch.test.ts +65 -0
  140. package/src/client/hooks/chat/timeline/session-switch.ts +63 -0
  141. package/src/client/hooks/chat/timeline/spawn-target.ts +52 -0
  142. package/src/client/hooks/chat/timeline/tool-open.test.ts +74 -0
  143. package/src/client/hooks/chat/timeline/tool-open.ts +52 -8
  144. package/src/client/hooks/code/deferred-highlight.test.ts +182 -0
  145. package/src/client/hooks/code/deferred-highlight.ts +129 -0
  146. package/src/client/hooks/editor/find-state.ts +1 -1
  147. package/src/client/hooks/editor/theme.test.ts +206 -0
  148. package/src/client/hooks/editor/theme.ts +82 -0
  149. package/src/client/hooks/editor/use-editor-drop.test.ts +101 -0
  150. package/src/client/hooks/editor/use-editor-drop.ts +67 -14
  151. package/src/client/hooks/editor/use-editor-find.ts +7 -7
  152. package/src/client/hooks/editor/use-outline-view.test.ts +182 -0
  153. package/src/client/hooks/editor/use-outline-view.ts +94 -7
  154. package/src/client/hooks/editor/use-scroll-restore.test.ts +55 -2
  155. package/src/client/hooks/editor/use-scroll-restore.ts +124 -4
  156. package/src/client/hooks/editor/use-search-reveal.ts +1 -1
  157. package/src/client/hooks/ui/long-press.test.ts +174 -0
  158. package/src/client/hooks/ui/long-press.ts +112 -0
  159. package/src/client/hooks/ui/settings-modal.ts +56 -0
  160. package/src/client/hooks/ui/updates/shell-download.test.ts +75 -0
  161. package/src/client/hooks/ui/updates/shell-download.ts +153 -28
  162. package/src/client/hooks/ui/updates/shell-offer.test.ts +213 -0
  163. package/src/client/hooks/ui/updates/shell-update.test.ts +140 -0
  164. package/src/client/hooks/ui/updates/shell-update.ts +119 -0
  165. package/src/client/hooks/workspace/diff-file.ts +29 -10
  166. package/src/client/hooks/workspace/diff-git-actions.ts +147 -0
  167. package/src/client/hooks/workspace/editor-view.ts +61 -0
  168. package/src/client/hooks/workspace/file-create.ts +41 -11
  169. package/src/client/hooks/workspace/file-listing.ts +56 -1
  170. package/src/client/hooks/workspace/file-tabs.test.ts +62 -5
  171. package/src/client/hooks/workspace/file-tabs.ts +24 -11
  172. package/src/client/hooks/workspace/file-tree-actions.ts +37 -5
  173. package/src/client/hooks/workspace/navbar-session-actions.ts +72 -0
  174. package/src/client/hooks/workspace/worktree/index.ts +165 -0
  175. package/src/client/lib/desktop/bridge.ts +45 -0
  176. package/src/client/lib/search-reveal.ts +1 -1
  177. package/src/client/main.tsx +5 -1
  178. package/src/client/tailwind.css +56 -8
  179. package/src/server/index.ts +7 -2
  180. package/src/server/lib/auth/guard.test.ts +28 -0
  181. package/src/server/lib/auth/guard.ts +10 -0
  182. package/src/server/lib/bundler/titlebar-css.test.ts +42 -1
  183. package/src/server/lib/db/schema.ts +4 -0
  184. package/src/server/lib/db/worktree-registry.server.ts +91 -0
  185. package/src/server/lib/fs/canonical.ts +52 -0
  186. package/src/server/lib/fs/relative-path.ts +48 -0
  187. package/src/server/lib/fs/root-worktree.test.ts +116 -0
  188. package/src/server/lib/fs/root.ts +39 -10
  189. package/src/server/lib/fs/worktree/carry.ts +134 -0
  190. package/src/server/lib/fs/worktree/create.test.ts +249 -0
  191. package/src/server/lib/fs/worktree/create.ts +262 -0
  192. package/src/server/lib/fs/worktree/errors.ts +43 -0
  193. package/src/server/lib/fs/worktree/list.test.ts +207 -0
  194. package/src/server/lib/fs/worktree/list.ts +142 -0
  195. package/src/server/lib/fs/worktree/location.test.ts +206 -0
  196. package/src/server/lib/fs/worktree/location.ts +114 -0
  197. package/src/server/lib/fs/worktree/project-id.ts +53 -0
  198. package/src/server/lib/fs/worktree/remove.test.ts +242 -0
  199. package/src/server/lib/fs/worktree/remove.ts +152 -0
  200. package/src/server/lib/fs/worktree/settings.server.ts +56 -0
  201. package/src/server/lib/fs/worktree/topology.ts +142 -0
  202. package/src/server/lib/lifecycle/claim-port.test.ts +156 -0
  203. package/src/server/lib/lifecycle/port-guard.test.ts +20 -164
  204. package/src/server/lib/lifecycle/port-guard.ts +39 -7
  205. package/src/server/lib/lifecycle/probe-health.test.ts +166 -0
  206. package/src/server/lib/lifecycle/probe.test.ts +77 -164
  207. package/src/server/lib/lifecycle/probe.ts +121 -13
  208. package/src/server/lib/omp/rpc/frame-fold-signals.test.ts +112 -0
  209. package/src/server/lib/omp/rpc/frame-fold.ts +10 -5
  210. package/src/server/lib/omp/session/sidebar-activity.server.test.ts +162 -0
  211. package/src/server/lib/omp/session/sidebar-activity.server.ts +70 -0
  212. package/src/server/lib/omp/session/sidebar-data.canonical.test.ts +155 -0
  213. package/src/server/lib/omp/session/sidebar-data.server.ts +28 -1
  214. package/src/server/lib/omp/session/telemetry/compute.ts +13 -1
  215. package/src/server/lib/omp/session/telemetry.test.ts +33 -0
  216. package/src/server/lib/panels/local-edits.server.ts +33 -0
  217. package/src/server/lib/panels/sync.fixture.test-util.ts +92 -0
  218. package/src/server/lib/panels/sync.local-edits.test.ts +150 -0
  219. package/src/server/lib/panels/sync.server.test.ts +125 -147
  220. package/src/server/lib/panels/sync.server.ts +153 -48
  221. package/src/server/lib/realtime/signals.server.ts +10 -0
  222. package/src/server/lib/realtime/topics/catalog.server.ts +4 -1
  223. package/src/server/lib/realtime/topics.server.ts +16 -0
  224. package/src/server/lib/updates/shell-assets.test.ts +114 -0
  225. package/src/server/lib/updates/shell-assets.ts +200 -0
  226. package/src/server/lib/updates/shell-cache.ts +105 -0
  227. package/src/server/lib/updates/shell-pages.test.ts +187 -0
  228. package/src/server/lib/updates/shell-pages.ts +157 -0
  229. package/src/server/lib/updates/shell-release-desktop.test.ts +166 -0
  230. package/src/server/lib/updates/shell-release.test.ts +59 -71
  231. package/src/server/lib/updates/shell-release.ts +178 -198
  232. package/src/server/routes/fs/action-create.test.ts +18 -13
  233. package/src/server/routes/fs/action-nested.test.ts +184 -0
  234. package/src/server/routes/fs/action.ts +47 -16
  235. package/src/server/routes/fs/git-merge.test.ts +11 -1
  236. package/src/server/routes/fs/read.test.ts +24 -7
  237. package/src/server/routes/fs/scope.test.ts +15 -10
  238. package/src/server/routes/fs/scope.ts +3 -14
  239. package/src/server/routes/index.ts +2 -0
  240. package/src/server/routes/updates/android.ts +6 -1
  241. package/src/server/routes/updates/desktop.ts +59 -0
  242. package/src/server/routes/updates/index.ts +2 -0
  243. package/src/server/routes/updates/ios.ts +5 -1
  244. package/src/server/routes/worktrees/create.ts +138 -0
  245. package/src/server/routes/worktrees/index.ts +25 -0
  246. package/src/server/routes/worktrees/list.ts +134 -0
  247. package/src/server/routes/worktrees/remove.ts +102 -0
  248. package/src/server/routes/worktrees/routes.test.ts +326 -0
  249. package/src/server/routes/worktrees/shared.ts +90 -0
  250. package/src/shared/lib/chat/timeline/omp-callbacks.test.ts +36 -0
  251. package/src/shared/lib/chat/timeline/omp-callbacks.ts +18 -0
  252. package/src/shared/lib/chat/timeline/run-signals.test.ts +2 -0
  253. package/src/shared/lib/code/editor/comment-syntax.test.ts +37 -0
  254. package/src/shared/lib/code/editor/comment-syntax.ts +55 -0
  255. package/src/shared/lib/code/editor/find-replace.test.ts +134 -0
  256. package/src/shared/lib/code/editor/{find.ts → find-replace.ts} +23 -107
  257. package/src/shared/lib/code/editor/{find.test.ts → find-scan.test.ts} +19 -73
  258. package/src/shared/lib/code/editor/find-scan.ts +177 -0
  259. package/src/shared/lib/code/editor/keymap.ts +3 -3
  260. package/src/shared/lib/code/editor/lines.test.ts +7 -55
  261. package/src/shared/lib/code/editor/lines.ts +8 -48
  262. package/src/shared/lib/code/editor/marks.test.ts +4 -5
  263. package/src/shared/lib/code/editor/marks.ts +13 -25
  264. package/src/shared/lib/code/editor/theme-loader.ts +97 -0
  265. package/src/shared/lib/code/editor/themes.generated.ts +156 -0
  266. package/src/shared/lib/code/editor/themes.test.ts +165 -0
  267. package/src/shared/lib/code/editor/themes.ts +165 -0
  268. package/src/shared/lib/code/highlight-memo.test.ts +103 -0
  269. package/src/shared/lib/code/highlight-memo.ts +149 -0
  270. package/src/shared/lib/code/highlighter-warmup.ts +112 -0
  271. package/src/shared/lib/code/highlighter.ts +3 -1
  272. package/src/shared/lib/code/idle.ts +37 -0
  273. package/src/shared/lib/code/outline/rows.test.ts +96 -0
  274. package/src/shared/lib/code/outline/rows.ts +94 -0
  275. package/src/shared/lib/code/syntax-highlight.test.ts +1 -79
  276. package/src/shared/lib/code/syntax-highlight.ts +2 -3
  277. package/src/shared/lib/fs/file-kind-classify.test.ts +25 -11
  278. package/src/shared/lib/fs/file-kind.ts +47 -13
  279. package/src/shared/lib/fs/file-tree.test.ts +183 -0
  280. package/src/shared/lib/fs/file-tree.ts +159 -0
  281. package/src/shared/lib/fs/file-url.test.ts +52 -0
  282. package/src/shared/lib/fs/file-url.ts +37 -0
  283. package/src/shared/lib/fs/patch-sides.test.ts +114 -0
  284. package/src/shared/lib/fs/patch-sides.ts +139 -0
  285. package/src/shared/lib/fs/paths.test.ts +24 -1
  286. package/src/shared/lib/fs/paths.ts +17 -0
  287. package/src/shared/lib/fs/worktree/settings-keys.ts +18 -0
  288. package/src/shared/lib/omp/session/prewarm.ts +7 -0
  289. package/src/shared/lib/realtime/protocol.ts +16 -0
  290. package/src/shared/lib/ui/toolbar-overflow.test.ts +83 -0
  291. package/src/shared/lib/ui/toolbar-overflow.ts +86 -0
  292. package/src/shared/lib/updates/shell-arch.ts +54 -0
  293. package/src/shared/lib/updates/shell-ua.ts +24 -4
  294. package/src/shared/lib/workspace/active-project.ts +23 -0
  295. package/src/shared/lib/workspace/editor-view-mode.test.ts +70 -0
  296. package/src/shared/lib/workspace/editor-view-mode.ts +48 -0
  297. package/src/shared/lib/workspace/panel-widths.test.ts +30 -0
  298. package/src/shared/lib/workspace/panel-widths.ts +25 -5
  299. package/src/shared/lib/workspace/session-state/cache.ts +159 -0
  300. package/src/shared/lib/workspace/session-state/harness.test-util.ts +55 -0
  301. package/src/shared/lib/workspace/session-state/merge.ts +100 -0
  302. package/src/shared/lib/workspace/session-state/store-load.test.ts +206 -0
  303. package/src/shared/lib/workspace/session-state/store.test.ts +17 -139
  304. package/src/shared/lib/workspace/session-state/store.ts +92 -130
  305. package/src/shared/lib/workspace/workspace-modules.test.ts +49 -1
  306. package/src/shared/lib/workspace/workspace-panel-widths.ts +124 -0
  307. package/src/shared/lib/workspace/worktree/branch-name.test.ts +95 -0
  308. package/src/shared/lib/workspace/worktree/branch-name.ts +115 -0
  309. package/src/shared/lib/workspace/worktree/types.ts +104 -0
  310. package/src/shared/types/fs.ts +10 -0
  311. package/src/shared/types/realtime.ts +29 -0
  312. package/src/shared/types/settings/state.ts +10 -0
  313. package/src/shared/types/workspace.ts +14 -0
  314. package/src/test-support/file-explorer-disk.test-util.ts +76 -0
  315. package/src/test-support/file-explorer.test-util.ts +71 -40
  316. package/src/test-support/loopback.ts +183 -0
  317. package/dist/client/static/js/index-nzyjf92m.css +0 -1
  318. package/src/client/components/common/code-editor/handler.test.ts +0 -274
  319. package/src/client/components/common/code-editor/handler.ts +0 -175
  320. package/src/client/components/common/code-editor/index.tsx +0 -314
  321. package/src/client/components/common/code-surface/measure.ts +0 -148
  322. package/src/client/components/workspace/diff-panel/SplitView.tsx +0 -209
  323. package/src/client/components/workspace/diff-panel/UnifiedView.tsx +0 -150
  324. package/src/client/hooks/editor/scroll-container.test.ts +0 -102
  325. package/src/client/hooks/editor/scroll-container.ts +0 -26
  326. package/src/client/hooks/editor/use-code-window.ts +0 -100
  327. package/src/client/hooks/editor/use-editor-history.test.ts +0 -216
  328. package/src/client/hooks/editor/use-editor-history.ts +0 -134
  329. package/src/client/hooks/editor/use-occurrences.test.ts +0 -236
  330. package/src/client/hooks/editor/use-occurrences.ts +0 -98
  331. package/src/shared/lib/code/editor/commands.test.ts +0 -155
  332. package/src/shared/lib/code/editor/commands.ts +0 -260
  333. package/src/shared/lib/code/editor/edits.test.ts +0 -100
  334. package/src/shared/lib/code/editor/edits.ts +0 -122
  335. package/src/shared/lib/code/editor/history.test.ts +0 -118
  336. package/src/shared/lib/code/editor/history.ts +0 -101
  337. package/src/shared/lib/code/editor/layers.test.ts +0 -77
  338. package/src/shared/lib/code/editor/layers.ts +0 -67
  339. package/src/shared/lib/code/editor/line-edits.test.ts +0 -131
  340. package/src/shared/lib/code/editor/line-edits.ts +0 -159
  341. package/src/shared/lib/code/editor/occurrences.test.ts +0 -117
  342. package/src/shared/lib/code/editor/occurrences.ts +0 -111
  343. package/src/shared/lib/code/editor/reset-css.test.ts +0 -35
  344. package/src/shared/lib/code/editor/reset-css.ts +0 -32
  345. package/src/shared/lib/code/editor/text-commands.test.ts +0 -178
  346. package/src/shared/lib/code/editor/text-commands.ts +0 -154
  347. package/src/shared/lib/code/lazy-window.test.ts +0 -161
  348. package/src/shared/lib/code/lazy-window.ts +0 -156
  349. package/src/shared/lib/code/windowed-highlight.ts +0 -114
  350. package/src/shared/lib/fs/diff-parser.test.ts +0 -116
  351. package/src/shared/lib/fs/diff-parser.ts +0 -213
@@ -0,0 +1,166 @@
1
+ /**
2
+ * @license
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ */
5
+
6
+ /**
7
+ * The DESKTOP half of the shell-release read: which artifact a per-architecture
8
+ * shell installs, and when the answer is only the release page.
9
+ *
10
+ * Split from `shell-release.test.ts` because that file is at the repo's 350-line
11
+ * ceiling, and the two questions are separable: the mobile pair resolves ONE
12
+ * artifact for every device, while the desktop trio's answer depends on the
13
+ * architecture the shell reported.
14
+ */
15
+
16
+ import { beforeEach, describe, expect, test } from 'bun:test';
17
+
18
+ import { fetchShellRelease, resetShellReleaseCache } from '@/server/lib/updates/shell-release';
19
+
20
+ /** A fetch double that answers one body. */
21
+ function stubFetch(body: unknown) {
22
+ const calls: string[] = [];
23
+ const impl = (async (input: string | URL | Request) => {
24
+ calls.push(String(input));
25
+ return {
26
+ ok: true,
27
+ json: async () => body,
28
+ text: async () => (typeof body === 'string' ? body : JSON.stringify(body)),
29
+ } as Response;
30
+ }) as unknown as typeof fetch;
31
+ return { impl, calls };
32
+ }
33
+
34
+ describe('fetchShellRelease — desktop architectures', () => {
35
+ // The cache is process-wide (see `resetShellReleaseCache`), so every case
36
+ // starts from a cold one or it reads the previous case's answer.
37
+ beforeEach(() => resetShellReleaseCache());
38
+
39
+ /**
40
+ * A desktop probe WITHOUT an architecture answers the RELEASE PAGE.
41
+ *
42
+ * Every desktop artifact is per-architecture, and a console served to another
43
+ * machine cannot know which one the window asking runs on. Naming one arch's
44
+ * file would hand half the readers a build that will not launch, so the link is
45
+ * the page that lists all of them and `direct` says it is not installable.
46
+ */
47
+ test('a desktop probe without an arch answers the release page', async () => {
48
+ const { impl } = stubFetch([
49
+ {
50
+ html_url: 'https://x/app',
51
+ assets: [
52
+ { name: 'Kabmo-0.2.0-mac-arm64.zip', browser_download_url: 'https://x/mac-arm.zip' },
53
+ { name: 'Kabmo-0.2.0-mac-x64.dmg', browser_download_url: 'https://x/mac-x64.dmg' },
54
+ ],
55
+ },
56
+ ]);
57
+
58
+ expect(await fetchShellRelease('macos', impl)).toEqual({
59
+ url: 'https://x/app',
60
+ latest: '0.2.0',
61
+ releaseUrl: 'https://x/app',
62
+ direct: false,
63
+ });
64
+ });
65
+
66
+ /**
67
+ * WITH an architecture it answers that machine's own installer.
68
+ *
69
+ * This is what makes the desktop self-update possible at all: the shell
70
+ * reports its arch over the bridge, and the console names ONE file instead of
71
+ * a page a human has to read.
72
+ */
73
+ test('a desktop probe with an arch answers that arch’s installer', async () => {
74
+ const { impl } = stubFetch([
75
+ {
76
+ html_url: 'https://x/app',
77
+ assets: [
78
+ { name: 'Kabmo-0.2.0-mac-x64.zip', browser_download_url: 'https://x/mac-x64.zip' },
79
+ { name: 'Kabmo-0.2.0-mac-arm64.zip', browser_download_url: 'https://x/mac-arm.zip' },
80
+ ],
81
+ },
82
+ ]);
83
+
84
+ expect(await fetchShellRelease('macos', impl, 'arm64')).toEqual({
85
+ url: 'https://x/mac-arm.zip',
86
+ latest: '0.2.0',
87
+ releaseUrl: 'https://x/app',
88
+ direct: true,
89
+ });
90
+ });
91
+
92
+ test('an arch the release does not publish keeps the page, never the sibling', async () => {
93
+ // A build that only shipped x64 must not be installed on an arm64 machine:
94
+ // the page is a correct answer, the other architecture's file is not.
95
+ const { impl } = stubFetch([
96
+ {
97
+ html_url: 'https://x/app',
98
+ assets: [
99
+ { name: 'Kabmo-0.2.0-mac-x64.zip', browser_download_url: 'https://x/mac-x64.zip' },
100
+ ],
101
+ },
102
+ ]);
103
+
104
+ expect(await fetchShellRelease('macos', impl, 'arm64')).toEqual({
105
+ url: 'https://x/app',
106
+ latest: '0.2.0',
107
+ releaseUrl: 'https://x/app',
108
+ direct: false,
109
+ });
110
+ });
111
+
112
+ test('the self-update file is the ONE file that shell can install', async () => {
113
+ // macOS installs from the `.zip` (ditto extracts it); the `.dmg` beside it is
114
+ // for a human. Windows installs from the Inno `.exe`; Linux from the
115
+ // AppImage, which is the only one of its four formats a shell can replace.
116
+ const mac = stubFetch([
117
+ {
118
+ html_url: 'https://x/app',
119
+ assets: [
120
+ { name: 'Kabmo-0.2.0-mac-arm64.dmg', browser_download_url: 'https://x/mac-arm.dmg' },
121
+ { name: 'Kabmo-0.2.0-mac-arm64.zip', browser_download_url: 'https://x/mac-arm.zip' },
122
+ ],
123
+ },
124
+ ]);
125
+ expect((await fetchShellRelease('macos', mac.impl, 'arm64'))?.url).toBe('https://x/mac-arm.zip');
126
+
127
+ const win = stubFetch([
128
+ {
129
+ html_url: 'https://x/app',
130
+ assets: [
131
+ { name: 'Kabmo-0.2.0-win-x64-setup.exe', browser_download_url: 'https://x/win-x64.exe' },
132
+ ],
133
+ },
134
+ ]);
135
+ expect((await fetchShellRelease('windows', win.impl, 'x64'))?.url).toBe('https://x/win-x64.exe');
136
+
137
+ const linux = stubFetch([
138
+ {
139
+ html_url: 'https://x/app',
140
+ assets: [
141
+ { name: 'Kabmo-0.2.0-linux-x86_64.tar.gz', browser_download_url: 'https://x/linux.tgz' },
142
+ { name: 'Kabmo-0.2.0-linux-x86_64.AppImage', browser_download_url: 'https://x/linux.AppImage' },
143
+ ],
144
+ },
145
+ ]);
146
+ expect((await fetchShellRelease('linux', linux.impl, 'x64'))?.url).toBe('https://x/linux.AppImage');
147
+ });
148
+
149
+ test('the arch is part of the cache key', async () => {
150
+ // One console is routinely shared by a whole team, and two machines on it
151
+ // differ only by architecture. A shared slot would answer the second one with
152
+ // the first one's installer.
153
+ const release = {
154
+ html_url: 'https://x/app',
155
+ assets: [
156
+ { name: 'Kabmo-0.3.0-mac-arm64.zip', browser_download_url: 'https://x/arm.zip' },
157
+ { name: 'Kabmo-0.3.0-mac-x64.zip', browser_download_url: 'https://x/x64.zip' },
158
+ ],
159
+ };
160
+ expect((await fetchShellRelease('macos', stubFetch([release]).impl, 'arm64'))?.url).toBe('https://x/arm.zip');
161
+
162
+ const { impl, calls } = stubFetch([release]);
163
+ expect((await fetchShellRelease('macos', impl, 'x64'))?.url).toBe('https://x/x64.zip');
164
+ expect(calls.length, 'a different arch is a different question').toBeGreaterThan(0);
165
+ });
166
+ });
@@ -5,75 +5,16 @@
5
5
 
6
6
  import { beforeEach, describe, expect, test } from 'bun:test';
7
7
 
8
- import { fetchShellRelease, resetShellReleaseCache, versionFromAssetName } from '@/server/lib/updates/shell-release';
8
+ import { fetchShellRelease, resetShellReleaseCache } from '@/server/lib/updates/shell-release';
9
9
 
10
10
  /**
11
- * The asset name is the only place a shell artifact's version exists: the
12
- * release TAG names the SERVER version, and the two move independently. A parser
13
- * that read the tag would compare a server version against a shell version.
11
+ * A fetch double that answers one body, and counts its calls.
14
12
  *
15
- * Both shells are pinned here, because they share the parser and differ only by
16
- * prefix/extension — the shape that silently accepts the other shell's asset.
13
+ * `text()` is provided as well as `json()`: the public-pages fallback reads Atom
14
+ * and HTML, and a stub that only answered `json()` would throw inside it and make
15
+ * a broken parse look like a clean "no release" — the exact confusion these
16
+ * tests exist to prevent.
17
17
  */
18
- describe('versionFromAssetName', () => {
19
- test('reads the version the release workflows write', () => {
20
- expect(versionFromAssetName('android', 'Kabmo-0.5.0-android.apk')).toBe('0.5.0');
21
- expect(versionFromAssetName('ios', 'Kabmo-0.1.0-ios.ipa')).toBe('0.1.0');
22
- });
23
-
24
- /**
25
- * The old spelling is still READ, and that is not sentiment.
26
- *
27
- * These names are the only record of a published shell, and every release
28
- * already on GitHub carries `kabmo-android-v<v>.apk`. A parser that only knew
29
- * the new scheme would answer "no update" for an installed shell the moment
30
- * this shipped — silently, because a name that does not match reads exactly
31
- * like a release with no artifact.
32
- */
33
- test('still reads the legacy spelling every published release carries', () => {
34
- expect(versionFromAssetName('android', 'kabmo-android-v0.5.0.apk')).toBe('0.5.0');
35
- expect(versionFromAssetName('ios', 'kabmo-ios-v0.1.0.ipa')).toBe('0.1.0');
36
- });
37
-
38
- test('ignores anything that is not that shell’s artifact', () => {
39
- expect(versionFromAssetName('android', 'Kabmo-1.2.3-android.apk.zip')).toBeNull();
40
- expect(versionFromAssetName('android', 'source.tar.gz')).toBeNull();
41
- expect(versionFromAssetName('android', 'Kabmo-latest-android.apk')).toBeNull();
42
- expect(versionFromAssetName('android', 'Kabmo--android.apk')).toBeNull();
43
- expect(versionFromAssetName('android', '')).toBeNull();
44
- });
45
-
46
- /**
47
- * The desktop assets share the `Kabmo-` prefix, so the EXTENSION is what keeps
48
- * them out. A probe that matched on the prefix alone would offer a Mac `.dmg`
49
- * to an Android phone.
50
- */
51
- test('does not read a desktop asset as a mobile one', () => {
52
- for (const name of [
53
- 'Kabmo-0.1.2-mac-arm64.zip',
54
- 'Kabmo-0.1.2-mac-arm64.dmg',
55
- 'Kabmo-0.1.0-win-x64-setup.exe',
56
- 'Kabmo-0.1.0-linux-x86_64.tar.gz',
57
- 'Kabmo-0.1.0-linux-amd64.deb',
58
- 'Kabmo-0.1.0-linux-x86_64.AppImage',
59
- ]) {
60
- expect(versionFromAssetName('android', name), name).toBeNull();
61
- expect(versionFromAssetName('ios', name), name).toBeNull();
62
- }
63
- });
64
-
65
- test('does not accept one shell’s asset for the other', () => {
66
- // The two probes read the SAME release list, so a parser that ignored the
67
- // kind would let an iOS probe answer with the APK (and offer an `.apk` to an
68
- // iPhone).
69
- expect(versionFromAssetName('ios', 'Kabmo-0.5.0-android.apk')).toBeNull();
70
- expect(versionFromAssetName('android', 'Kabmo-0.1.0-ios.ipa')).toBeNull();
71
- expect(versionFromAssetName('ios', 'kabmo-android-v1.2.3.apk')).toBeNull();
72
- expect(versionFromAssetName('android', 'kabmo-ios-v1.2.3.ipa')).toBeNull();
73
- });
74
- });
75
-
76
- /** A fetch double that answers one body, and counts its calls. */
77
18
  function stubFetch(body: unknown, ok = true) {
78
19
  const calls: string[] = [];
79
20
  const impl = (async (input: string | URL | Request) => {
@@ -81,11 +22,45 @@ function stubFetch(body: unknown, ok = true) {
81
22
  return {
82
23
  ok,
83
24
  json: async () => body,
25
+ text: async () => (typeof body === 'string' ? body : JSON.stringify(body)),
84
26
  } as Response;
85
27
  }) as unknown as typeof fetch;
86
28
  return { impl, calls };
87
29
  }
88
30
 
31
+ /**
32
+ * A double that routes by URL, for the two-path probe.
33
+ *
34
+ * `api` answers the REST read (or fails, which is what a spent quota does) and
35
+ * `pages` answers the Atom feed and each release's asset fragment. Returning
36
+ * `null` for a route means "this request fails", which is how the fallback is
37
+ * entered at all.
38
+ */
39
+ function routedFetch(routes: {
40
+ api?: unknown;
41
+ atom?: string;
42
+ assets?: Record<string, string>;
43
+ }) {
44
+ const calls: string[] = [];
45
+ const impl = (async (input: string | URL | Request) => {
46
+ const url = String(input);
47
+ calls.push(url);
48
+
49
+ if (url.includes('/repos/')) {
50
+ if (routes.api === undefined) return { ok: false, json: async () => ({}), text: async () => '' } as Response;
51
+ return { ok: true, json: async () => routes.api, text: async () => JSON.stringify(routes.api) } as Response;
52
+ }
53
+ if (url.endsWith('/releases.atom')) {
54
+ const body = routes.atom ?? '';
55
+ return { ok: Boolean(routes.atom), json: async () => ({}), text: async () => body } as Response;
56
+ }
57
+ const tag = url.slice(url.lastIndexOf('/') + 1);
58
+ const body = routes.assets?.[decodeURIComponent(tag)];
59
+ return { ok: body !== undefined, json: async () => ({}), text: async () => body ?? '' } as Response;
60
+ }) as unknown as typeof fetch;
61
+ return { impl, calls };
62
+ }
63
+
89
64
  describe('fetchShellRelease', () => {
90
65
  // The cache is process-wide (see `resetShellReleaseCache`), so every case
91
66
  // starts from a cold one or it reads the previous case's answer.
@@ -109,6 +84,7 @@ describe('fetchShellRelease', () => {
109
84
  url: 'https://x/Kabmo-1.2.3-android.apk',
110
85
  latest: '1.2.3',
111
86
  releaseUrl: 'https://github.com/kabmo-labs/kabmo/releases/tag/v9.9.9',
87
+ direct: true,
112
88
  });
113
89
  });
114
90
 
@@ -127,6 +103,7 @@ describe('fetchShellRelease', () => {
127
103
  url: 'https://x/i.ipa',
128
104
  latest: '0.4.0',
129
105
  releaseUrl: 'https://x/app',
106
+ direct: true,
130
107
  });
131
108
  });
132
109
 
@@ -155,6 +132,7 @@ describe('fetchShellRelease', () => {
155
132
  url: 'https://x/new.apk',
156
133
  latest: '0.6.0',
157
134
  releaseUrl: 'https://x/r',
135
+ direct: true,
158
136
  });
159
137
  });
160
138
 
@@ -208,6 +186,7 @@ describe('fetchShellRelease', () => {
208
186
  url: 'https://x/a.apk',
209
187
  latest: '0.3.0',
210
188
  releaseUrl: 'https://x/app',
189
+ direct: true,
211
190
  });
212
191
  });
213
192
 
@@ -237,19 +216,28 @@ describe('fetchShellRelease', () => {
237
216
  { html_url: 'https://x', assets: [{ name: 'notes.md' }] },
238
217
  { html_url: 'https://y', assets: [] },
239
218
  ]);
240
- expect(await fetchShellRelease('android', impl)).toEqual({ url: null, latest: null, releaseUrl: null });
219
+ expect(await fetchShellRelease('android', impl)).toEqual({
220
+ url: null,
221
+ latest: null,
222
+ releaseUrl: null,
223
+ direct: false,
224
+ });
241
225
  });
242
226
 
243
227
  test('answers null when the probe fails', async () => {
244
- // An offline box or a rate-limited GitHub must not take the sidebar down,
245
- // and a failure must not be read as "no newer version" either — it is
246
- // simply no offer.
247
- const { impl } = stubFetch({}, false);
228
+ // An offline box must not take the sidebar down, and a failure must not be
229
+ // read as "no newer version" either — it is simply no offer. BOTH paths are
230
+ // down here: the API refuses and the feed is unreachable, which is the only
231
+ // shape that means "cannot ask" rather than "nothing published".
232
+ const { impl } = routedFetch({});
248
233
  expect(await fetchShellRelease('android', impl)).toBeNull();
249
234
  });
250
235
 
251
236
  test('answers null when the body is malformed', async () => {
252
- const { impl } = stubFetch({ assets: 'nope' });
237
+ // The API answers 200 with something that is not a release list, and the
238
+ // feed is unreachable — the API's answer is a failed probe, not an empty
239
+ // repository.
240
+ const { impl } = routedFetch({ api: { assets: 'nope' } });
253
241
  expect(await fetchShellRelease('android', impl)).toBeNull();
254
242
  });
255
243
  });