ompchamber 2.0.2 → 3.1.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 (419) hide show
  1. package/README.md +31 -11
  2. package/bunfig.toml +11 -0
  3. package/dist/client/index.html +16 -5
  4. package/dist/client/index.js +1393 -0
  5. package/dist/client/static/js/index-8t70jhyj.js +5149 -0
  6. package/dist/client/static/js/index-n4wkqj7t.css +1 -0
  7. package/index.html +54 -0
  8. package/package.json +9 -10
  9. package/public/apple-touch-icon.png +0 -0
  10. package/public/icon-192x192.png +0 -0
  11. package/public/icon.svg +60 -0
  12. package/public/manifest.webmanifest +38 -0
  13. package/src/cli/lib/commands/serve.js +9 -5
  14. package/src/cli/lib/runtime.js +18 -7
  15. package/src/client/components/common/code-editor/handler.test.ts +274 -0
  16. package/src/client/components/common/code-editor/handler.ts +175 -0
  17. package/src/client/components/common/code-editor/index.tsx +185 -220
  18. package/src/client/components/common/code-surface/index.tsx +109 -12
  19. package/src/client/components/common/lazy-panels.tsx +5 -1
  20. package/src/client/components/common/provider-icon/glyphs.ts +118 -0
  21. package/src/client/components/common/provider-icon/index.tsx +85 -0
  22. package/src/client/components/common/viewport.ts +5 -3
  23. package/src/client/components/layout/RightActivityBar.tsx +2 -1
  24. package/src/client/components/layout/desktop-layout/TopNavbar.tsx +21 -4
  25. package/src/client/components/layout/desktop-layout/WorkspacePanels.tsx +4 -0
  26. package/src/client/components/layout/desktop-layout/index.tsx +26 -4
  27. package/src/client/components/mobile/LayoutWrapper.tsx +16 -0
  28. package/src/client/components/mobile/RightSidebar.tsx +11 -4
  29. package/src/client/components/mobile/SessionSidebar.tsx +10 -0
  30. package/src/client/components/mobile/mobile-right-sidebar/FullEditor.tsx +0 -1
  31. package/src/client/components/mobile/mobile-session-sidebar/Item.tsx +16 -1
  32. package/src/client/components/mobile/mobile-session-sidebar/List.tsx +3 -0
  33. package/src/client/components/settings/categories/ProviderSettings.tsx +45 -4
  34. package/src/client/components/settings/categories/UsageSettings.tsx +22 -7
  35. package/src/client/components/settings/categories/provider-settings/AddModelModal.tsx +322 -0
  36. package/src/client/components/settings/categories/provider-settings/AddProviderModal.tsx +152 -56
  37. package/src/client/components/settings/categories/provider-settings/AuthSection.tsx +52 -1
  38. package/src/client/components/settings/categories/provider-settings/DeleteProviderModal.tsx +133 -0
  39. package/src/client/components/settings/categories/provider-settings/DialectFields.tsx +136 -0
  40. package/src/client/components/settings/categories/provider-settings/EffortLadder.tsx +89 -0
  41. package/src/client/components/settings/categories/provider-settings/Header.tsx +2 -2
  42. package/src/client/components/settings/categories/provider-settings/LoginModal.tsx +2 -2
  43. package/src/client/components/settings/categories/provider-settings/ModelsList.tsx +22 -2
  44. package/src/client/components/settings/categories/provider-settings/PresetPicker.tsx +82 -0
  45. package/src/client/components/settings/categories/provider-settings/PriceFields.tsx +107 -0
  46. package/src/client/components/settings/categories/provider-settings/ReconnectModal.tsx +43 -22
  47. package/src/client/components/settings/categories/provider-settings/SidebarList.tsx +2 -2
  48. package/src/client/components/settings/categories/usage-settings/LimitsSection.tsx +115 -0
  49. package/src/client/components/settings/categories/usage-settings/ProviderCard.tsx +19 -26
  50. package/src/client/components/settings/categories/usage-settings/ProviderDetail.tsx +29 -22
  51. package/src/client/components/settings/categories/usage-settings/SidebarList.tsx +60 -48
  52. package/src/client/components/settings/categories/usage-settings/format.ts +36 -0
  53. package/src/client/components/settings/categories/usage-settings/providers.ts +52 -28
  54. package/src/client/components/workspace/browser-panel/AddressBar.tsx +1 -10
  55. package/src/client/components/workspace/browser-panel/Screencast.tsx +4 -5
  56. package/src/client/components/workspace/browser-panel/index.tsx +6 -6
  57. package/src/client/components/workspace/browser-toolbar/index.tsx +22 -12
  58. package/src/client/components/workspace/chat-timeline/AiMessageFooter.tsx +1 -1
  59. package/src/client/components/workspace/chat-timeline/GeneratingIndicator.tsx +1 -1
  60. package/src/client/components/workspace/chat-timeline/ToolCallCard.tsx +1 -1
  61. package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Todo.tsx +1 -1
  62. package/src/client/components/workspace/diff-panel/Toolbar.tsx +71 -38
  63. package/src/client/components/workspace/diff-panel/index.tsx +139 -53
  64. package/src/client/components/workspace/editor/CommandPalette.test.ts +242 -0
  65. package/src/client/components/workspace/editor/CommandPalette.tsx +138 -0
  66. package/src/client/components/workspace/editor/FindWidget.test.ts +261 -0
  67. package/src/client/components/workspace/editor/FindWidget.tsx +219 -0
  68. package/src/client/components/workspace/editor/Tabs.tsx +2 -2
  69. package/src/client/components/workspace/editor/Toolbar.tsx +129 -26
  70. package/src/client/components/workspace/editor/index.tsx +180 -62
  71. package/src/client/components/workspace/file-explorer/Modals.tsx +79 -2
  72. package/src/client/components/workspace/file-explorer/TreeItem.tsx +43 -113
  73. package/src/client/components/workspace/model-dropdown/Item.tsx +13 -23
  74. package/src/client/components/workspace/model-dropdown/Panel.tsx +1 -1
  75. package/src/client/components/workspace/model-dropdown/Section.tsx +13 -4
  76. package/src/client/components/workspace/model-dropdown/index.tsx +7 -3
  77. package/src/client/components/workspace/todo-panel/PhaseGroup.tsx +76 -0
  78. package/src/client/components/workspace/todo-panel/Row.tsx +69 -0
  79. package/src/client/components/workspace/todo-panel/index.tsx +161 -0
  80. package/src/client/components/workspace/usage-panel/ProviderSelect.tsx +10 -5
  81. package/src/client/components/workspace/usage-panel/index.tsx +36 -18
  82. package/src/client/data/samples/dialogue-session.ts +1 -1
  83. package/src/client/data/samples/sample-tools/other.ts +1 -1
  84. package/src/client/data/settings/provider.ts +141 -9
  85. package/src/client/hooks/editor/find-harness.test-util.ts +170 -0
  86. package/src/client/hooks/editor/scroll-container.test.ts +102 -0
  87. package/src/client/hooks/editor/scroll-container.ts +26 -0
  88. package/src/client/hooks/editor/use-code-window.ts +2 -20
  89. package/src/client/hooks/editor/use-editor-find-write.test.ts +170 -0
  90. package/src/client/hooks/editor/use-editor-find.test.ts +118 -0
  91. package/src/client/hooks/editor/use-editor-find.ts +321 -0
  92. package/src/client/hooks/editor/use-editor-history.test.ts +216 -0
  93. package/src/client/hooks/editor/use-editor-history.ts +134 -0
  94. package/src/client/hooks/editor/use-file-editor.ts +24 -27
  95. package/src/client/hooks/editor/use-occurrences.test.ts +236 -0
  96. package/src/client/hooks/editor/use-occurrences.ts +98 -0
  97. package/src/client/hooks/models/use-provider-names.ts +1 -1
  98. package/src/client/hooks/settings/providers/actions.ts +337 -0
  99. package/src/client/hooks/settings/providers/api.ts +69 -0
  100. package/src/client/hooks/settings/providers/index.ts +138 -0
  101. package/src/client/hooks/settings/providers/messages.ts +77 -0
  102. package/src/client/hooks/settings/useUsageReport.ts +8 -3
  103. package/src/client/hooks/ui/theme.ts +3 -3
  104. package/src/client/hooks/workspace/file-tabs.ts +62 -11
  105. package/src/client/hooks/workspace/file-tree-actions.test.ts +348 -0
  106. package/src/client/hooks/workspace/file-tree-actions.ts +250 -0
  107. package/src/client/hooks/workspace/session-todos.ts +102 -0
  108. package/src/client/stubs/elkjs.ts +6 -1
  109. package/src/client/tailwind.css +30 -0
  110. package/src/server/index.ts +20 -2
  111. package/src/server/lib/assets/font-css.server.ts +86 -0
  112. package/src/server/lib/assets/fonts.server.ts +117 -0
  113. package/src/server/lib/bundler/css.ts +116 -0
  114. package/src/server/lib/lifecycle/shell-route.ts +17 -0
  115. package/src/server/lib/models/provider-listing.server.ts +257 -0
  116. package/src/server/lib/models/provider-registry.server.test.ts +122 -0
  117. package/src/server/lib/models/provider-registry.server.ts +76 -25
  118. package/src/server/lib/omp/config/models-config.ts +11 -0
  119. package/src/server/lib/omp/config/models-validation.test.ts +137 -0
  120. package/src/server/lib/omp/config/models-validation.ts +111 -2
  121. package/src/server/lib/omp/config/provider-seeds.test.ts +167 -0
  122. package/src/server/lib/omp/config/provider-seeds.ts +176 -0
  123. package/src/server/lib/omp/config/providers.ts +94 -100
  124. package/src/server/lib/omp/core/auth-credentials.ts +45 -0
  125. package/src/server/lib/omp/session/todos.ts +100 -0
  126. package/src/server/lib/omp/session/{usage.ts → usage/aggregate.ts} +1 -118
  127. package/src/server/lib/omp/session/usage/shaping.ts +130 -0
  128. package/src/server/lib/usage/deepseek.ts +4 -5
  129. package/src/server/lib/usage/kenari.ts +7 -6
  130. package/src/server/lib/usage/omp-usage.server.test.ts +126 -0
  131. package/src/server/lib/usage/omp-usage.server.ts +194 -0
  132. package/src/server/lib/usage/provider-key.ts +81 -3
  133. package/src/server/lib/usage/providers.server.test.ts +47 -0
  134. package/src/server/lib/usage/providers.server.ts +208 -0
  135. package/src/server/lib/usage/vendor-balance.server.test.ts +104 -0
  136. package/src/server/lib/usage/vendor-balance.server.ts +284 -0
  137. package/src/server/plugins/shell.server.ts +56 -0
  138. package/src/server/plugins/ssr.ts +24 -36
  139. package/src/server/plugins/static.test.ts +26 -29
  140. package/src/server/plugins/static.ts +40 -19
  141. package/src/server/routes/fs/action-create.test.ts +90 -0
  142. package/src/server/routes/fs/action.ts +23 -0
  143. package/src/server/routes/omp/index.ts +2 -0
  144. package/src/server/routes/omp/session-todos.ts +80 -0
  145. package/src/server/routes/settings/index.ts +2 -0
  146. package/src/server/routes/settings/provider-model.ts +107 -0
  147. package/src/server/routes/settings/provider-models.ts +65 -202
  148. package/src/server/routes/settings/providers.ts +22 -7
  149. package/src/server/routes/settings/usage.ts +125 -45
  150. package/src/server/routes/telemetry/tokens.ts +2 -1
  151. package/src/shared/lib/chat/{todo-parser.ts → todo/parser.ts} +17 -0
  152. package/src/shared/lib/chat/todo/progress.ts +97 -0
  153. package/src/shared/lib/chat/todo/snapshot.test.ts +156 -0
  154. package/src/shared/lib/chat/todo/snapshot.ts +215 -0
  155. package/src/shared/lib/code/editor/commands.test.ts +155 -0
  156. package/src/shared/lib/code/editor/commands.ts +260 -0
  157. package/src/shared/lib/code/editor/edits.test.ts +100 -0
  158. package/src/shared/lib/code/editor/edits.ts +122 -0
  159. package/src/shared/lib/code/editor/find-status.test.ts +50 -0
  160. package/src/shared/lib/code/editor/find-status.ts +36 -0
  161. package/src/shared/lib/code/editor/find.test.ts +203 -0
  162. package/src/shared/lib/code/editor/find.ts +224 -0
  163. package/src/{client/components/common/code-editor → shared/lib/code/editor}/history.test.ts +1 -1
  164. package/src/shared/lib/code/editor/keymap.test.ts +109 -0
  165. package/src/shared/lib/code/editor/keymap.ts +151 -0
  166. package/src/shared/lib/code/editor/layers.test.ts +77 -0
  167. package/src/shared/lib/code/editor/layers.ts +67 -0
  168. package/src/shared/lib/code/editor/line-edits.test.ts +131 -0
  169. package/src/shared/lib/code/editor/line-edits.ts +159 -0
  170. package/src/shared/lib/code/editor/lines.test.ts +81 -0
  171. package/src/shared/lib/code/editor/lines.ts +83 -0
  172. package/src/shared/lib/code/editor/marks.test.ts +86 -0
  173. package/src/shared/lib/code/editor/marks.ts +106 -0
  174. package/src/shared/lib/code/editor/occurrences.test.ts +117 -0
  175. package/src/shared/lib/code/editor/occurrences.ts +111 -0
  176. package/src/shared/lib/code/editor/palette.test.ts +91 -0
  177. package/src/shared/lib/code/editor/palette.ts +92 -0
  178. package/src/shared/lib/code/editor/reset-css.test.ts +35 -0
  179. package/src/shared/lib/code/editor/reset-css.ts +32 -0
  180. package/src/shared/lib/code/editor/text-commands.test.ts +178 -0
  181. package/src/shared/lib/code/editor/text-commands.ts +154 -0
  182. package/src/shared/lib/code/syntax-highlight.test.ts +75 -0
  183. package/src/shared/lib/code/syntax-highlight.ts +29 -17
  184. package/src/shared/lib/code/windowed-highlight.ts +52 -1
  185. package/src/shared/lib/fs/diff-parser.test.ts +116 -0
  186. package/src/shared/lib/fs/diff-parser.ts +86 -33
  187. package/src/shared/lib/models/{provider-connection.ts → provider/connection.ts} +37 -0
  188. package/src/shared/lib/models/provider/dialect.ts +179 -0
  189. package/src/shared/lib/models/provider/glyph.test.ts +116 -0
  190. package/src/shared/lib/models/provider/glyph.ts +200 -0
  191. package/src/shared/lib/models/{provider-models.ts → provider/models.ts} +91 -6
  192. package/src/shared/lib/models/provider/presets.test.ts +172 -0
  193. package/src/shared/lib/models/provider/presets.ts +83 -0
  194. package/src/shared/lib/ui/key-binding.test.ts +115 -0
  195. package/src/shared/lib/ui/key-binding.ts +143 -0
  196. package/src/shared/lib/util/platform.test.ts +87 -0
  197. package/src/shared/lib/util/platform.ts +30 -0
  198. package/src/shared/lib/workspace/file-tab-id.ts +29 -0
  199. package/src/shared/lib/workspace/keymap.test.ts +82 -0
  200. package/src/shared/lib/workspace/keymap.ts +30 -0
  201. package/src/shared/lib/workspace/refresh-cadence.ts +9 -0
  202. package/src/shared/lib/workspace/right-panels.ts +8 -0
  203. package/src/shared/lib/workspace/session-state/store.ts +0 -1
  204. package/src/shared/types/index.ts +1 -0
  205. package/src/shared/types/settings/provider.ts +81 -0
  206. package/src/shared/types/settings/usage.ts +88 -14
  207. package/src/shared/types/todo.ts +108 -0
  208. package/tsconfig.json +3 -1
  209. package/dist/client/static/css/index.5f630421b7.css +0 -2
  210. package/dist/client/static/font/KaTeX_AMS-Regular.a79f1c3119.woff2 +0 -0
  211. package/dist/client/static/font/KaTeX_Caligraphic-Bold.ec17d13264.woff2 +0 -0
  212. package/dist/client/static/font/KaTeX_Caligraphic-Regular.55fac25845.woff2 +0 -0
  213. package/dist/client/static/font/KaTeX_Fraktur-Bold.d42a5579b0.woff2 +0 -0
  214. package/dist/client/static/font/KaTeX_Fraktur-Regular.d3c882a649.woff2 +0 -0
  215. package/dist/client/static/font/KaTeX_Main-Bold.c3fb5ac22f.woff2 +0 -0
  216. package/dist/client/static/font/KaTeX_Main-BoldItalic.6f2bb1dff2.woff2 +0 -0
  217. package/dist/client/static/font/KaTeX_Main-Italic.8916142bec.woff2 +0 -0
  218. package/dist/client/static/font/KaTeX_Main-Regular.0462f03bdf.woff2 +0 -0
  219. package/dist/client/static/font/KaTeX_Math-BoldItalic.572d331f69.woff2 +0 -0
  220. package/dist/client/static/font/KaTeX_Math-Italic.f28c23acad.woff2 +0 -0
  221. package/dist/client/static/font/KaTeX_SansSerif-Bold.8c5b5494b6.woff2 +0 -0
  222. package/dist/client/static/font/KaTeX_SansSerif-Italic.3b1e59b3ba.woff2 +0 -0
  223. package/dist/client/static/font/KaTeX_SansSerif-Regular.ba21ed5f84.woff2 +0 -0
  224. package/dist/client/static/font/KaTeX_Script-Regular.03e9641d6f.woff2 +0 -0
  225. package/dist/client/static/font/KaTeX_Size1-Regular.eae34984b3.woff2 +0 -0
  226. package/dist/client/static/font/KaTeX_Size2-Regular.5916a24fa3.woff2 +0 -0
  227. package/dist/client/static/font/KaTeX_Size4-Regular.10d95fd3a2.woff2 +0 -0
  228. package/dist/client/static/font/KaTeX_Typewriter-Regular.a8709e3622.woff2 +0 -0
  229. package/dist/client/static/font/fira-code-latin-400-normal.2655b10c55.woff2 +0 -0
  230. package/dist/client/static/font/fira-code-latin-500-normal.92a991f204.woff2 +0 -0
  231. package/dist/client/static/font/fira-code-latin-600-normal.43788eb7f1.woff2 +0 -0
  232. package/dist/client/static/font/fira-code-latin-700-normal.2667215d33.woff2 +0 -0
  233. package/dist/client/static/js/77.0d81c74cf0.js +0 -139
  234. package/dist/client/static/js/77.0d81c74cf0.js.LICENSE.txt +0 -8
  235. package/dist/client/static/js/async/0.8e59f2eb5b.js +0 -1
  236. package/dist/client/static/js/async/0k.5852e6b4fa.js +0 -1
  237. package/dist/client/static/js/async/0w.9ed36d5860.js +0 -1
  238. package/dist/client/static/js/async/1.f387c12b63.js +0 -1
  239. package/dist/client/static/js/async/12.d091f91c99.js +0 -1
  240. package/dist/client/static/js/async/13.8f810a8a6c.js +0 -1
  241. package/dist/client/static/js/async/19.6fe909d55a.js +0 -160
  242. package/dist/client/static/js/async/1b.23f1141a38.js +0 -1
  243. package/dist/client/static/js/async/1k.973e8e21fc.js +0 -2
  244. package/dist/client/static/js/async/1k.973e8e21fc.js.LICENSE.txt +0 -11
  245. package/dist/client/static/js/async/1p.0d5cddda96.js +0 -1
  246. package/dist/client/static/js/async/2.b589f1733b.js +0 -1
  247. package/dist/client/static/js/async/2v.96165101de.js +0 -1
  248. package/dist/client/static/js/async/3.0cacc08423.js +0 -1
  249. package/dist/client/static/js/async/37.b28b2f61cd.js +0 -1
  250. package/dist/client/static/js/async/3d.5085fb913f.js +0 -1
  251. package/dist/client/static/js/async/3j.357c80ff38.js +0 -2
  252. package/dist/client/static/js/async/3j.357c80ff38.js.LICENSE.txt +0 -11
  253. package/dist/client/static/js/async/3p.bcf32a0983.js +0 -1
  254. package/dist/client/static/js/async/3r.3a69e98ee0.js +0 -64
  255. package/dist/client/static/js/async/3u.1228460e2a.js +0 -1
  256. package/dist/client/static/js/async/3y.3530b824ad.js +0 -198
  257. package/dist/client/static/js/async/3y.3530b824ad.js.LICENSE.txt +0 -5
  258. package/dist/client/static/js/async/4.9442eeb472.js +0 -1
  259. package/dist/client/static/js/async/4e.678285ca55.js +0 -1
  260. package/dist/client/static/js/async/4k.33a0c7eeb4.js +0 -1
  261. package/dist/client/static/js/async/4m.928a1dde85.js +0 -31
  262. package/dist/client/static/js/async/4m.928a1dde85.js.LICENSE.txt +0 -11
  263. package/dist/client/static/js/async/4q.aad20dab25.js +0 -1
  264. package/dist/client/static/js/async/5.ebaf8b89cc.js +0 -36
  265. package/dist/client/static/js/async/51.95bcae98af.js +0 -2
  266. package/dist/client/static/js/async/51.95bcae98af.js.LICENSE.txt +0 -6
  267. package/dist/client/static/js/async/5b.d88304bc13.js +0 -1
  268. package/dist/client/static/js/async/5j.d6407c4248.js +0 -287
  269. package/dist/client/static/js/async/5o.88316d4460.js +0 -62
  270. package/dist/client/static/js/async/6.f034c9b528.js +0 -1
  271. package/dist/client/static/js/async/66.5dfa29f957.js +0 -93
  272. package/dist/client/static/js/async/6o.5b1a06a382.js +0 -1
  273. package/dist/client/static/js/async/7.e4a9288dd0.js +0 -1
  274. package/dist/client/static/js/async/73.308dff4da8.js +0 -1
  275. package/dist/client/static/js/async/7e.a1cc9bfd88.js +0 -1
  276. package/dist/client/static/js/async/7g.a7814350c3.js +0 -1
  277. package/dist/client/static/js/async/7l.fadbbc0583.js +0 -1
  278. package/dist/client/static/js/async/8.5067439d8c.js +0 -1
  279. package/dist/client/static/js/async/8j.ea110663fb.js +0 -1
  280. package/dist/client/static/js/async/a.bce4542392.js +0 -1
  281. package/dist/client/static/js/async/aa.65d3852576.js +0 -1
  282. package/dist/client/static/js/async/ar.9303e5f2e8.js +0 -2
  283. package/dist/client/static/js/async/ar.9303e5f2e8.js.LICENSE.txt +0 -11
  284. package/dist/client/static/js/async/az.d656062b0f.js +0 -1
  285. package/dist/client/static/js/async/b.b4f6f06515.js +0 -1
  286. package/dist/client/static/js/async/bi.09688ba75c.js +0 -1
  287. package/dist/client/static/js/async/bq.586bc738d1.js +0 -1
  288. package/dist/client/static/js/async/c.797d933882.js +0 -1
  289. package/dist/client/static/js/async/c0.0af1e640d3.js +0 -24
  290. package/dist/client/static/js/async/ca.8a52cfd447.js +0 -1
  291. package/dist/client/static/js/async/cf.d34fff5d67.js +0 -1
  292. package/dist/client/static/js/async/ct.b74928f7a3.js +0 -1
  293. package/dist/client/static/js/async/cx.3cb0242a3a.js +0 -226
  294. package/dist/client/static/js/async/d.a5fc8fe1b3.js +0 -1
  295. package/dist/client/static/js/async/da.f62c70f6c8.js +0 -1
  296. package/dist/client/static/js/async/dp.1725d6432c.js +0 -1
  297. package/dist/client/static/js/async/e.93214d622b.js +0 -79
  298. package/dist/client/static/js/async/f.ab50467719.js +0 -19
  299. package/dist/client/static/js/async/f.ab50467719.js.LICENSE.txt +0 -12
  300. package/dist/client/static/js/async/f7.46971e9d6d.js +0 -2
  301. package/dist/client/static/js/async/f7.46971e9d6d.js.LICENSE.txt +0 -6
  302. package/dist/client/static/js/async/f8.bbf0dbae70.js +0 -1
  303. package/dist/client/static/js/async/g.cb81980498.js +0 -2
  304. package/dist/client/static/js/async/g.cb81980498.js.LICENSE.txt +0 -11
  305. package/dist/client/static/js/async/h.4e790471e4.js +0 -27
  306. package/dist/client/static/js/async/h1.393f066d09.js +0 -1
  307. package/dist/client/static/js/async/h3.216e9798ab.js +0 -148
  308. package/dist/client/static/js/async/hd.0da931b622.js +0 -1
  309. package/dist/client/static/js/async/hk.ba7fb04d41.js +0 -1
  310. package/dist/client/static/js/async/hn.b645041e00.js +0 -1
  311. package/dist/client/static/js/async/ho.475a5e1ae4.js +0 -1
  312. package/dist/client/static/js/async/hp.e9fe2e395a.js +0 -2
  313. package/dist/client/static/js/async/hp.e9fe2e395a.js.LICENSE.txt +0 -17
  314. package/dist/client/static/js/async/hs.e7c59d8090.js +0 -41
  315. package/dist/client/static/js/async/i.7c148e8440.js +0 -1
  316. package/dist/client/static/js/async/id.573df20f29.js +0 -1
  317. package/dist/client/static/js/async/j.ab925882d8.js +0 -1
  318. package/dist/client/static/js/async/jd.63886a1f09.js +0 -1
  319. package/dist/client/static/js/async/jg.d86ebd4013.js +0 -86
  320. package/dist/client/static/js/async/jl.874d352278.js +0 -1
  321. package/dist/client/static/js/async/jll.11313b455a.js +0 -1
  322. package/dist/client/static/js/async/js.67182759ef.js +0 -1
  323. package/dist/client/static/js/async/jsc.23b70d6f40.js +0 -1
  324. package/dist/client/static/js/async/jse.334588ff5d.js +0 -1
  325. package/dist/client/static/js/async/jz.72eb929106.js +0 -77
  326. package/dist/client/static/js/async/k.3210286401.js +0 -217
  327. package/dist/client/static/js/async/ka.c1990ecaa3.js +0 -115
  328. package/dist/client/static/js/async/katex-vendor.de6a87c308.js +0 -1
  329. package/dist/client/static/js/async/ku.29c8a59d0c.js +0 -1
  330. package/dist/client/static/js/async/ky.8dab1dade0.js +0 -1
  331. package/dist/client/static/js/async/l.12dd34e1be.js +0 -1
  332. package/dist/client/static/js/async/l9.949710adaf.js +0 -83
  333. package/dist/client/static/js/async/l9.949710adaf.js.LICENSE.txt +0 -13
  334. package/dist/client/static/js/async/ls.9d9a01026c.js +0 -104
  335. package/dist/client/static/js/async/m.eb2e6752e2.js +0 -28
  336. package/dist/client/static/js/async/m.eb2e6752e2.js.LICENSE.txt +0 -11
  337. package/dist/client/static/js/async/m3.d2b05f9604.js +0 -2
  338. package/dist/client/static/js/async/m3.d2b05f9604.js.LICENSE.txt +0 -6
  339. package/dist/client/static/js/async/ma.2c261b04dd.js +0 -2
  340. package/dist/client/static/js/async/ma.2c261b04dd.js.LICENSE.txt +0 -6
  341. package/dist/client/static/js/async/me.8ace414f80.js +0 -23
  342. package/dist/client/static/js/async/ms.98960af4eb.js +0 -67
  343. package/dist/client/static/js/async/ms.98960af4eb.js.LICENSE.txt +0 -12
  344. package/dist/client/static/js/async/mt.0352e976d8.js +0 -1
  345. package/dist/client/static/js/async/n.a9f1c51701.js +0 -1
  346. package/dist/client/static/js/async/na.a36cd532d6.js +0 -1
  347. package/dist/client/static/js/async/nk.fc7e58325e.js +0 -1
  348. package/dist/client/static/js/async/o.36353dfb88.js +0 -1
  349. package/dist/client/static/js/async/oc.b73042e8bd.js +0 -8
  350. package/dist/client/static/js/async/og.8eb577893a.js +0 -193
  351. package/dist/client/static/js/async/p.48ecfcd14e.js +0 -1
  352. package/dist/client/static/js/async/px.fc6877924b.js +0 -1
  353. package/dist/client/static/js/async/q.22588845a9.js +0 -21
  354. package/dist/client/static/js/async/q.22588845a9.js.LICENSE.txt +0 -6
  355. package/dist/client/static/js/async/q5.97dc1481b6.js +0 -286
  356. package/dist/client/static/js/async/qs.2a9ddb3c72.js +0 -1
  357. package/dist/client/static/js/async/r.51010ad87c.js +0 -1
  358. package/dist/client/static/js/async/rd.cb6bf495bb.js +0 -2
  359. package/dist/client/static/js/async/rd.cb6bf495bb.js.LICENSE.txt +0 -4
  360. package/dist/client/static/js/async/s.0f395cdda5.js +0 -94
  361. package/dist/client/static/js/async/t.4dca0e2ba8.js +0 -1
  362. package/dist/client/static/js/async/t9.d44f57d518.js +0 -1
  363. package/dist/client/static/js/async/ta.973534bbec.js +0 -1
  364. package/dist/client/static/js/async/tr.e878658140.js +0 -309
  365. package/dist/client/static/js/async/u.0871ac5b22.js +0 -1
  366. package/dist/client/static/js/async/ud.3065bfee31.js +0 -2
  367. package/dist/client/static/js/async/ud.3065bfee31.js.LICENSE.txt +0 -4
  368. package/dist/client/static/js/async/up.ea990da0ff.js +0 -32
  369. package/dist/client/static/js/async/uw.8890abbabc.js +0 -1
  370. package/dist/client/static/js/async/v.7d28d3c241.js +0 -34
  371. package/dist/client/static/js/async/vp.9b13bf8610.js +0 -1
  372. package/dist/client/static/js/async/vt.42416b707d.js +0 -1
  373. package/dist/client/static/js/async/w.08ac210f60.js +0 -1
  374. package/dist/client/static/js/async/wt.ad92393a90.js +0 -1
  375. package/dist/client/static/js/async/x.563f72ca6b.js +0 -1
  376. package/dist/client/static/js/async/x0.475516418f.js +0 -1
  377. package/dist/client/static/js/async/x1.da8687159b.js +0 -90
  378. package/dist/client/static/js/async/xe.da8dcc2015.js +0 -2
  379. package/dist/client/static/js/async/xe.da8dcc2015.js.LICENSE.txt +0 -11
  380. package/dist/client/static/js/async/xl.a3c562a58c.js +0 -1
  381. package/dist/client/static/js/async/xn.6510c0abe2.js +0 -2
  382. package/dist/client/static/js/async/xn.6510c0abe2.js.LICENSE.txt +0 -6
  383. package/dist/client/static/js/async/xn1.83a7784fe1.js +0 -10
  384. package/dist/client/static/js/async/xn1.83a7784fe1.js.LICENSE.txt +0 -11
  385. package/dist/client/static/js/async/xnl.feb0021e50.js +0 -2
  386. package/dist/client/static/js/async/xnl.feb0021e50.js.LICENSE.txt +0 -12
  387. package/dist/client/static/js/async/y.1a9ed7a5d8.js +0 -1
  388. package/dist/client/static/js/async/y6.43c57defe4.js +0 -1
  389. package/dist/client/static/js/async/yb.8c1342401d.js +0 -1
  390. package/dist/client/static/js/async/ybq.4491f2f2dc.js +0 -1
  391. package/dist/client/static/js/async/yg.35bebd6d0c.js +0 -149
  392. package/dist/client/static/js/async/yq.917ddd7ba2.js +0 -1
  393. package/dist/client/static/js/async/ys.00687cf82d.js +0 -38
  394. package/dist/client/static/js/async/yv.d99d829136.js +0 -1
  395. package/dist/client/static/js/async/z.56f61a4374.js +0 -1
  396. package/dist/client/static/js/async/zi.5fdb07b86a.js +0 -24
  397. package/dist/client/static/js/async/zj.654f37e8ff.js +0 -1
  398. package/dist/client/static/js/async/zn.966d6f1cc6.js +0 -1
  399. package/dist/client/static/js/async/zo.26213b40c0.js +0 -1
  400. package/dist/client/static/js/async/zx.e9f4105e00.js +0 -1
  401. package/dist/client/static/js/index.e6a257402a.js +0 -38
  402. package/dist/client/static/js/index.e6a257402a.js.LICENSE.txt +0 -4
  403. package/dist/client/static/js/markdown-vendor.71e43278b3.js +0 -144
  404. package/dist/client/static/js/shiki-vendor.cb8452ddcd.js +0 -1
  405. package/src/client/components/settings/categories/provider-settings/Icons.tsx +0 -35
  406. package/src/client/hooks/settings/providers.ts +0 -326
  407. package/src/server/plugins/dev-assets.ts +0 -49
  408. package/src/shared/lib/code/editor-utils.ts +0 -16
  409. package/src/shared/lib/models/provider-presets.ts +0 -29
  410. /package/dist/client/{apple-touch-icon.png → static/apple-touch-icon-dskx5sr3.png} +0 -0
  411. /package/dist/client/{icon-192x192.png → static/icon-192x192-6gn5cr2f.png} +0 -0
  412. /package/dist/client/{icon.svg → static/icon-d07z9dtz.svg} +0 -0
  413. /package/dist/client/{manifest.webmanifest → static/manifest-bnyy75kt.webmanifest} +0 -0
  414. /package/{dist/client → public}/icon-512x512.png +0 -0
  415. /package/{dist/client → public}/icon-maskable-512x512.png +0 -0
  416. /package/{dist/client → public}/sw.js +0 -0
  417. /package/src/{client/components/common/code-editor → shared/lib/code/editor}/history.ts +0 -0
  418. /package/src/shared/lib/models/{provider-cleanup.ts → provider/cleanup.ts} +0 -0
  419. /package/src/shared/lib/models/{provider-label.ts → provider/label.ts} +0 -0
@@ -1,4 +1,4 @@
1
- import { BarChart3, Bot, Files, GitBranch, Globe, Layers, Search, Terminal } from 'lucide-preact';
1
+ import { BarChart3, Bot, Files, GitBranch, Globe, Layers, ListTodo, Search, Terminal } from 'lucide-preact';
2
2
  import type { ReactNode } from 'preact/compat';
3
3
  import { useGitStatus } from '@/client/hooks/workspace/git-status';
4
4
  import { GIT_STATUS_POLL_MS } from '@/shared/lib/workspace/refresh-cadence';
@@ -27,6 +27,7 @@ const PANEL_META: Record<RightPanelType, { title: string; icon: ReactNode }> = {
27
27
  'user-browser': { title: 'Browser (Anda)', icon: <Globe size={16} /> },
28
28
  browser: { title: 'Browser Agent', icon: <Bot size={16} /> },
29
29
  usage: { title: 'Usage', icon: <BarChart3 size={16} /> },
30
+ todo: { title: 'Todos', icon: <ListTodo size={16} /> },
30
31
  };
31
32
 
32
33
  export function RightActivityBar({ activePanel, onChangePanel, isPanelOpen, hasActiveContext, activeProjectPath, refreshKey }: RightActivityBarProps) {
@@ -2,6 +2,19 @@ import { LayoutTemplate, MoreHorizontal, PanelLeft, PanelRight, PanelRightClose,
2
2
  import { PWAInstallButton } from '@/client/components/common/PWAInstallButton';
3
3
  import { StreamStatusDot } from '@/client/components/common/StreamStatusDot';
4
4
  import type { AgentStreamStatus } from '@/shared/lib/chat/omp/status';
5
+ import { WORKSPACE_KEY_BINDINGS, type WorkspaceCommand } from '@/shared/lib/workspace/keymap';
6
+ import { describeBinding } from '@/shared/lib/ui/key-binding';
7
+
8
+ /**
9
+ * A tooltip's text with the chord that actually invokes it. Derived from the
10
+ * workspace keymap rather than typed out, because a hand-written "⌘B" beside a
11
+ * binding that listens for something else is exactly the drift the keymap
12
+ * table exists to prevent.
13
+ */
14
+ function withChord(command: WorkspaceCommand, label: string): string {
15
+ const binding = WORKSPACE_KEY_BINDINGS.find((entry) => entry.command === command);
16
+ return binding ? `${label} (${describeBinding(binding)})` : label;
17
+ }
5
18
 
6
19
  interface TopNavbarProps {
7
20
  sessionTitle: string | null;
@@ -42,8 +55,8 @@ export function TopNavbar({
42
55
  type="button"
43
56
  onClick={onToggleLeftPanel}
44
57
  className="p-1.5 rounded hover:bg-ink/10 transition-colors text-ink/60 hover:text-ink cursor-pointer flex-shrink-0 titlebar-no-drag"
45
- title="Expand Sidebar"
46
- aria-label="Expand Sidebar"
58
+ title={withChord('toggleSidebar', 'Expand Sidebar')}
59
+ aria-label={withChord('toggleSidebar', 'Expand Sidebar')}
47
60
  >
48
61
  <PanelLeft size={16} />
49
62
  </button>
@@ -86,7 +99,9 @@ export function TopNavbar({
86
99
  type="button"
87
100
  onClick={onToggleEditor}
88
101
  className={`p-1.5 rounded hover:bg-ink/10 transition-colors cursor-pointer ${showEditor ? 'text-ink' : 'text-ink/40'}`}
89
- title="Toggle Editor Layout"
102
+ title={withChord('toggleEditorPanel', 'Toggle Editor Layout')}
103
+ aria-label={withChord('toggleEditorPanel', 'Toggle Editor Layout')}
104
+ aria-pressed={showEditor}
90
105
  >
91
106
  <LayoutTemplate size={16} />
92
107
  </button>
@@ -94,7 +109,9 @@ export function TopNavbar({
94
109
  type="button"
95
110
  onClick={onToggleRightPanel}
96
111
  className={`p-1.5 rounded hover:bg-ink/10 transition-colors cursor-pointer ${showRightPanel ? 'text-ink' : 'text-ink/40'}`}
97
- title="Toggle Right Panel"
112
+ title={withChord('toggleRightPanel', 'Toggle Right Panel')}
113
+ aria-label={withChord('toggleRightPanel', 'Toggle Right Panel')}
114
+ aria-pressed={showRightPanel}
98
115
  >
99
116
  {showRightPanel ? <PanelRightClose size={16} /> : <PanelRight size={16} />}
100
117
  </button>
@@ -56,6 +56,8 @@ interface WorkspacePanelsProps {
56
56
  refreshKey: number;
57
57
  onSetActiveFileId: (id: number | string | null) => void;
58
58
  onCloseFile: (id: number | string) => void;
59
+ /** Replace a diff tab with the plain editor tab for the same file. */
60
+ onConvertDiffToEditor: (id: number | string) => void;
59
61
  onOpenFile: (file: any) => void;
60
62
  onRefreshWorkspace: () => void;
61
63
  onChangeRightPanel: (panel: RightPanelType) => void;
@@ -81,6 +83,7 @@ export function WorkspacePanels(props: WorkspacePanelsProps) {
81
83
  refreshKey,
82
84
  onSetActiveFileId,
83
85
  onCloseFile,
86
+ onConvertDiffToEditor,
84
87
  onOpenFile,
85
88
  onRefreshWorkspace,
86
89
  onChangeRightPanel,
@@ -185,6 +188,7 @@ export function WorkspacePanels(props: WorkspacePanelsProps) {
185
188
  activeFileId={activeFileId}
186
189
  onSelectFile={onSetActiveFileId}
187
190
  onCloseFile={onCloseFile}
191
+ onConvertDiffToEditor={onConvertDiffToEditor}
188
192
  refreshKey={refreshKey}
189
193
  onFileSaved={onRefreshWorkspace}
190
194
  />
@@ -16,6 +16,8 @@ import { useSidebarData } from '@/client/hooks/chat/omp/session-list';
16
16
  import { DEFAULT_LEFT_PANEL_WIDTH, MAX_LEFT_PANEL_WIDTH, MIN_LEFT_PANEL_WIDTH, type EditorWidthMode } from '@/shared/lib/workspace/panel-widths';
17
17
  import { ResizeHandle } from '@/client/components/layout/desktop-layout/ResizeHandle';
18
18
  import { useChamberEvent, useWindowEvent } from '@/client/hooks/ui/window-event';
19
+ import { WORKSPACE_KEY_BINDINGS } from '@/shared/lib/workspace/keymap';
20
+ import { resolveBinding } from '@/shared/lib/ui/key-binding';
19
21
 
20
22
  interface DesktopLayoutProps {
21
23
  sessionId: string | null;
@@ -56,6 +58,7 @@ export function DesktopLayout({ sessionId, onSwitchToMobile, appSettings = {} }:
56
58
  setActiveFileId,
57
59
  handleOpenFile,
58
60
  handleCloseFile,
61
+ convertDiffToEditor,
59
62
  } = useFileTabs(activeProjectPath, sessionId || '', handleOpenTab);
60
63
 
61
64
  const showEditor = openedFiles.length > 0 && (userToggledEditor ?? true);
@@ -83,13 +86,31 @@ export function DesktopLayout({ sessionId, onSwitchToMobile, appSettings = {} }:
83
86
  const [sessionTitle, setSessionTitle] = useState<string | null>(null);
84
87
  const streamStatus = useAgentStreamStatus();
85
88
 
86
- // Global keyboard shortcut for settings (Cmd/Ctrl + ,)
89
+ /**
90
+ * The workspace's own shortcuts, resolved from the shared keymap so the
91
+ * tooltips on the panel toggles name the chord that is actually bound.
92
+ *
93
+ * `⌘S` is deliberately absent here: the editor panel already owns it (it is
94
+ * the only surface with a buffer to save), and binding it twice would save
95
+ * the file twice.
96
+ */
87
97
  useWindowEvent('keydown', (e) => {
88
- if ((e.metaKey || e.ctrlKey) && e.key === ',') {
89
- e.preventDefault();
98
+ const command = resolveBinding(WORKSPACE_KEY_BINDINGS, e);
99
+ if (!command) return;
100
+ if (command === 'saveFile') return;
101
+ if (command === 'toggleEditorPanel' && !showEditor) {
102
+ // The editor panel is only meaningful with a file open, and `showEditor`
103
+ // already folds in the user's own toggle.
104
+ if (openedFiles.length === 0) return;
105
+ }
106
+ e.preventDefault();
107
+ if (command === 'toggleSidebar') setShowLeftPanel((previous) => !previous);
108
+ else if (command === 'toggleEditorPanel') setUserToggledEditor((previous) => !(previous ?? showEditor));
109
+ else if (command === 'toggleRightPanel') setShowRightPanel((previous) => !previous);
110
+ else if (command === 'openSettings') {
90
111
  setSettingsCategory('appearance');
91
112
  setAutoOpenAddProvider(false);
92
- setSettingsOpen(prev => !prev);
113
+ setSettingsOpen((previous) => !previous);
93
114
  }
94
115
  });
95
116
 
@@ -183,6 +204,7 @@ export function DesktopLayout({ sessionId, onSwitchToMobile, appSettings = {} }:
183
204
  refreshKey={refreshKey}
184
205
  onSetActiveFileId={setActiveFileId}
185
206
  onCloseFile={handleCloseFile}
207
+ onConvertDiffToEditor={convertDiffToEditor}
186
208
  onOpenFile={handleOpenFile}
187
209
  onRefreshWorkspace={handleRefreshWorkspace}
188
210
  onChangeRightPanel={handleChangeRightPanel}
@@ -118,6 +118,21 @@ export function MobileLayoutWrapper({ onDesktopToggle, appSettings = {} }: Mobil
118
118
  setCurrentScreen('main');
119
119
  };
120
120
 
121
+ // Desktop parity: the `+` on a workspace header creates the pending session
122
+ // INSIDE that folder, so the prewarmed cwd and the eventual spawn cwd are the
123
+ // folder's — not whatever the active session resolved to.
124
+ const handleNewSessionForFolder = (targetFolderId: number) => {
125
+ const cwd = spawnCwdForNewSession(folders, sessionParam, targetFolderId);
126
+ if (cwd) triggerSessionPrewarm(cwd);
127
+ setSearchParams(prev => {
128
+ const next = new URLSearchParams(prev);
129
+ next.set('sessionId', `new-${Date.now()}`);
130
+ next.set('folderId', targetFolderId.toString());
131
+ return next;
132
+ }, { replace: true });
133
+ setCurrentScreen('main');
134
+ };
135
+
121
136
  const handleCreateFolder = async (input: { name: string; path?: string }) => {
122
137
  const res = await fetch('/api/folders', {
123
138
  method: 'POST',
@@ -162,6 +177,7 @@ export function MobileLayoutWrapper({ onDesktopToggle, appSettings = {} }: Mobil
162
177
  activeSessionId={sessionId}
163
178
  onSelectSession={handleSelectSession}
164
179
  onNewSession={handleNewSession}
180
+ onNewSessionForFolder={handleNewSessionForFolder}
165
181
  onCreateFolder={handleCreateFolder}
166
182
  onClose={() => setCurrentScreen('main')}
167
183
  onDesktopToggle={onDesktopToggle}
@@ -1,8 +1,8 @@
1
1
  import { useState } from 'preact/hooks';
2
2
  import { Suspense } from 'preact/compat';
3
3
  import type { ReactNode } from 'preact/compat';
4
- import { BarChart3, Bot, Files, GitBranch, Globe, Layers, Search, Terminal, X } from 'lucide-preact';
5
- import { LazyBrowserPanel, LazyContextPanel, LazyFileExplorer, LazyGitPanel, LazySearchPanel, LazyTerminalPanel, LazyUsagePanel, LazyUserBrowserPanel } from '@/client/components/common/lazy-panels';
4
+ import { BarChart3, Bot, Files, GitBranch, Globe, Layers, ListTodo, Search, Terminal, X } from 'lucide-preact';
5
+ import { LazyBrowserPanel, LazyContextPanel, LazyFileExplorer, LazyGitPanel, LazySearchPanel, LazyTerminalPanel, LazyTodoPanel, LazyUsagePanel, LazyUserBrowserPanel } from '@/client/components/common/lazy-panels';
6
6
  import { useGitStatus } from '@/client/hooks/workspace/git-status';
7
7
  import { GIT_STATUS_POLL_MS } from '@/shared/lib/workspace/refresh-cadence';
8
8
  import { RIGHT_PANEL_TYPES, type RightPanelType } from '@/shared/lib/workspace/right-panels';
@@ -31,6 +31,7 @@ const PANEL_META: Record<RightPanelType, { title: string; label: string; icon: R
31
31
  'user-browser': { title: 'Browser (Anda)', label: 'Browser', icon: <Globe size={14} className="flex-shrink-0" /> },
32
32
  browser: { title: 'Browser Agent', label: 'Agent', icon: <Bot size={14} className="flex-shrink-0" /> },
33
33
  usage: { title: 'Usage', label: 'Usage', icon: <BarChart3 size={14} className="flex-shrink-0" /> },
34
+ todo: { title: 'Todos', label: 'Todos', icon: <ListTodo size={14} className="flex-shrink-0" /> },
34
35
  };
35
36
 
36
37
  export function MobileRightSidebar({
@@ -100,12 +101,17 @@ export function MobileRightSidebar({
100
101
  </button>
101
102
  </div>
102
103
 
103
- {/* Main Tab Content — shared components so mobile == desktop features */}
104
+ {/* Main Tab Content — shared components so mobile == desktop features.
105
+ The workspace gate below applies per TAB, not to the whole drawer: a
106
+ todo list belongs to the session, not to the folder its cwd resolves
107
+ to, so the Todos tab stays reachable for a session running outside
108
+ every registered workspace. Every other view here reads the working
109
+ tree and genuinely needs one. */}
104
110
  <div
105
111
  className="flex-1 min-h-0 overflow-hidden relative"
106
112
  style={{ paddingBottom: 'env(safe-area-inset-bottom, 0px)' }}
107
113
  >
108
- {!enabled ? (
114
+ {!enabled && activeTab !== 'todo' ? (
109
115
  <div className="h-full flex items-center justify-center text-ink/40">
110
116
  <span className="text-xs font-mono">No session selected</span>
111
117
  </div>
@@ -130,6 +136,7 @@ export function MobileRightSidebar({
130
136
  {activeTab === 'user-browser' && <LazyUserBrowserPanel className="h-full w-full" />}
131
137
  {activeTab === 'browser' && <LazyBrowserPanel className="h-full w-full" active />}
132
138
  {activeTab === 'usage' && <LazyUsagePanel className="h-full w-full" />}
139
+ {activeTab === 'todo' && <LazyTodoPanel className="h-full w-full" />}
133
140
  </Suspense>
134
141
  </>
135
142
  )}
@@ -16,6 +16,7 @@ interface MobileSessionSidebarProps {
16
16
  activeSessionId: number | string | null;
17
17
  onSelectSession: (id: number | string) => void;
18
18
  onNewSession: () => void;
19
+ onNewSessionForFolder: (folderId: number) => void;
19
20
  onCreateFolder: (input: { name: string; path?: string }) => Promise<void> | void;
20
21
  onClose: () => void;
21
22
  onDesktopToggle?: () => void;
@@ -26,6 +27,7 @@ export function MobileSessionSidebar({
26
27
  activeSessionId,
27
28
  onSelectSession,
28
29
  onNewSession,
30
+ onNewSessionForFolder,
29
31
  onCreateFolder,
30
32
  onClose,
31
33
  onDesktopToggle,
@@ -96,6 +98,13 @@ export function MobileSessionSidebar({
96
98
  onClose();
97
99
  };
98
100
 
101
+ // Same dismissal as the toolbar's New Session: the pending session is opened
102
+ // behind the drawer, so the user must land on the chat, not on the list.
103
+ const handleNewSessionForFolderAndClose = (folderId: number) => {
104
+ onNewSessionForFolder(folderId);
105
+ onClose();
106
+ };
107
+
99
108
  const handleToggleArchived = () => {
100
109
  setShowArchived(!showArchived);
101
110
  setOptionsOpen(false);
@@ -136,6 +145,7 @@ export function MobileSessionSidebar({
136
145
  isScrolling={isScrolling}
137
146
  onScroll={handleScroll}
138
147
  onSelectSession={handleSelectSession}
148
+ onNewSessionForFolder={handleNewSessionForFolderAndClose}
139
149
  onToggleFolder={toggleFolder}
140
150
  />
141
151
  )}
@@ -29,7 +29,6 @@ export function MobileFullEditor({ file, onClose, onFileSaved }: MobileFullEdito
29
29
  const isMd = file.name.endsWith('.md');
30
30
 
31
31
  const editor = useFileEditor(file, {
32
- reportLoadError: true,
33
32
  flushOnUnmount: true,
34
33
  reportSaveError: true,
35
34
  downloadMimeType: 'text/plain;charset=utf-8',
@@ -1,5 +1,5 @@
1
1
  import { useRef, useState } from 'preact/hooks';
2
- import { ChevronDown, ChevronRight, GitBranch, MessageSquare, MoreHorizontal } from 'lucide-preact';
2
+ import { ChevronDown, ChevronRight, GitBranch, MessageSquare, MoreHorizontal, Plus } from 'lucide-preact';
3
3
  import { MobileSessionRow } from '@/client/components/mobile/mobile-session-sidebar/SessionRow';
4
4
  import { WorkspaceOptionsMenu } from '@/client/components/common/workspace-options-menu';
5
5
  import { useShowMore } from '@/client/hooks/ui/show-more';
@@ -14,6 +14,7 @@ interface MobileSessionCategoryProps {
14
14
  folder: WorkspaceFolderData;
15
15
  activeSessionId: number | string | null;
16
16
  onSelectSession: (id: number | string) => void;
17
+ onNewSessionForFolder: (folderId: number) => void;
17
18
  isExpanded: boolean;
18
19
  onToggleExpand: () => void;
19
20
  showArchived?: boolean;
@@ -24,6 +25,7 @@ export function MobileSessionCategory({
24
25
  folder,
25
26
  activeSessionId,
26
27
  onSelectSession,
28
+ onNewSessionForFolder,
27
29
  isExpanded,
28
30
  onToggleExpand,
29
31
  showArchived = false,
@@ -104,6 +106,19 @@ export function MobileSessionCategory({
104
106
  <GitBranch size={13} className="text-ink/50 ml-1" />
105
107
  )}
106
108
 
109
+ <button
110
+ type="button"
111
+ onClick={(event) => {
112
+ event.stopPropagation();
113
+ onNewSessionForFolder(folder.id);
114
+ }}
115
+ className="p-1.5 rounded-lg hover:bg-ink/10 active:bg-ink/15 text-ink/60 hover:text-ink"
116
+ title="New Session"
117
+ aria-label={`New session in ${folder.name}`}
118
+ >
119
+ <Plus size={15} />
120
+ </button>
121
+
107
122
  <div className="relative flex items-center" ref={menuRef} onClick={(event) => event.stopPropagation()}>
108
123
  <button
109
124
  type="button"
@@ -11,6 +11,7 @@ interface MobileSessionListProps {
11
11
  isScrolling: boolean;
12
12
  onScroll: () => void;
13
13
  onSelectSession: (id: number | string) => void;
14
+ onNewSessionForFolder: (folderId: number) => void;
14
15
  onToggleFolder: (folderId: number) => void;
15
16
  }
16
17
 
@@ -23,6 +24,7 @@ export function MobileSessionList({
23
24
  isScrolling,
24
25
  onScroll,
25
26
  onSelectSession,
27
+ onNewSessionForFolder,
26
28
  onToggleFolder,
27
29
  }: MobileSessionListProps) {
28
30
  return (
@@ -41,6 +43,7 @@ export function MobileSessionList({
41
43
  folder={folder}
42
44
  activeSessionId={activeSessionId}
43
45
  onSelectSession={onSelectSession}
46
+ onNewSessionForFolder={onNewSessionForFolder}
44
47
  isExpanded={expandedFolders[folder.id] ?? true}
45
48
  onToggleExpand={() => onToggleFolder(folder.id)}
46
49
  showArchived={showArchived}
@@ -9,6 +9,10 @@ import { ReconnectModal } from '@/client/components/settings/categories/provider
9
9
  import { LoginModal } from '@/client/components/settings/categories/provider-settings/LoginModal';
10
10
  import { ModelConfigModal } from '@/client/components/settings/categories/provider-settings/ModelConfigModal';
11
11
  import { ModelCapabilitiesModal } from '@/client/components/settings/categories/provider-settings/ModelCapabilitiesModal';
12
+ import { AddModelModal } from '@/client/components/settings/categories/provider-settings/AddModelModal';
13
+ import { DeleteProviderModal } from '@/client/components/settings/categories/provider-settings/DeleteProviderModal';
14
+ import { providerEmptyReason } from '@/shared/lib/models/provider/dialect';
15
+ import { configuredProviderSlugs } from '@/shared/lib/models/provider/presets';
12
16
 
13
17
  interface ProviderSettingsProps {
14
18
  autoOpenAdd?: boolean;
@@ -28,7 +32,15 @@ export function ProviderSettings({
28
32
  setCurrentProject,
29
33
  isFetchingModels,
30
34
  toasts,
35
+ pushToast,
31
36
  dismissToast,
37
+ isAddModelModalOpen,
38
+ setIsAddModelModalOpen,
39
+ handleModelAdded,
40
+ isDeleteModalOpen,
41
+ setIsDeleteModalOpen,
42
+ isDeletingProvider,
43
+ handleDeleteProvider,
32
44
  isAddModalOpen,
33
45
  setIsAddModalOpen,
34
46
  isReconnectModalOpen,
@@ -72,19 +84,31 @@ export function ProviderSettings({
72
84
  <ProviderAuthSection
73
85
  provider={selectedProvider}
74
86
  onOpenReconnectModal={() => {
75
- if (selectedProvider.id.startsWith('omp-auth-')) setIsLoginModalOpen(true);
76
- else setIsReconnectModalOpen(true);
87
+ // A provider omp signs in through `/login` needs the OAuth
88
+ // dialog; one whose endpoint and key live in models.yml (or the
89
+ // chamber overlay) is edited in place. Testing the provider ID
90
+ // for `omp-auth-` was wrong: a native models.yml entry shares
91
+ // that ID prefix once merged, and a keyless local server has no
92
+ // login flow to complete at all.
93
+ if (selectedProvider.credentialSource === 'omp-auth' && selectedProvider.auth !== 'none') {
94
+ setIsLoginModalOpen(true);
95
+ } else {
96
+ setIsReconnectModalOpen(true);
97
+ }
77
98
  }}
78
99
  onToggleDisconnect={handleToggleDisconnect}
79
100
  onToggleDisabled={handleToggleProviderDisabled}
101
+ onDelete={() => setIsDeleteModalOpen(true)}
80
102
  />
81
103
 
82
104
  <ProviderModelsList
83
105
  models={selectedProvider.models}
106
+ emptyReason={providerEmptyReason(selectedProvider)}
84
107
  onToggleModelVisibility={handleToggleModelVisibility}
85
108
  onHideAll={handleHideAll}
86
109
  onShowAll={handleShowAll}
87
110
  onFetchModels={handleFetchModelsFromList}
111
+ onAddModel={() => setIsAddModelModalOpen(true)}
88
112
  canFetchModels={Boolean(selectedProvider.baseUrl)}
89
113
  isFetchingModels={isFetchingModels}
90
114
  onOpenModelConfig={(model) => setConfigModel(model)}
@@ -108,7 +132,7 @@ export function ProviderSettings({
108
132
  }}
109
133
  onAddProvider={handleAddProvider}
110
134
  presets={availablePresetProviders}
111
- existingSlugs={providers.map((provider) => provider.slug)}
135
+ existingSlugs={configuredProviderSlugs(providers)}
112
136
  />
113
137
 
114
138
  {selectedProvider && (
@@ -131,13 +155,30 @@ export function ProviderSettings({
131
155
  />
132
156
  )}
133
157
 
158
+ {selectedProvider && (
159
+ <AddModelModal
160
+ isOpen={isAddModelModalOpen}
161
+ provider={selectedProvider}
162
+ onClose={() => setIsAddModelModalOpen(false)}
163
+ onAdded={(modelId) => void handleModelAdded(modelId)}
164
+ onError={(message) => pushToast(message, 'error')}
165
+ />
166
+ )}
167
+
168
+ <DeleteProviderModal
169
+ isOpen={isDeleteModalOpen}
170
+ provider={selectedProvider ?? null}
171
+ isDeleting={isDeletingProvider}
172
+ onClose={() => setIsDeleteModalOpen(false)}
173
+ onConfirm={() => void handleDeleteProvider()}
174
+ />
175
+
134
176
  <ModelConfigModal
135
177
  isOpen={!!configModel}
136
178
  model={configModel}
137
179
  onClose={() => setConfigModel(null)}
138
180
  onSaveModelConfig={handleSaveModelConfig}
139
181
  />
140
-
141
182
  <ModelCapabilitiesModal
142
183
  isOpen={!!capabilitiesModel}
143
184
  model={capabilitiesModel}
@@ -1,15 +1,24 @@
1
- import { useState } from 'preact/hooks';
1
+ import { useEffect, useState } from 'preact/hooks';
2
2
  import { AlertCircle, RefreshCw } from 'lucide-preact';
3
3
  import { UsageSidebarList } from '@/client/components/settings/categories/usage-settings/SidebarList';
4
- import type { UsageProviderId } from '@/client/components/settings/categories/usage-settings/providers';
4
+ import { buildProviders, type UsageProviderId } from '@/client/components/settings/categories/usage-settings/providers';
5
5
  import { ProviderDetail } from '@/client/components/settings/categories/usage-settings/ProviderDetail';
6
6
  import { formatDateTime } from '@/client/components/settings/categories/usage-settings/format';
7
7
  import { useUsageReport } from '@/client/hooks/settings/useUsageReport';
8
8
 
9
- /** Settings → Usage: provider balances, quota, and 30-day spend. */
9
+ /** Settings → Usage: provider quota, balances, and locally recorded burn. */
10
10
  export function UsageSettings() {
11
11
  const { report, isLoading, error, reload } = useUsageReport();
12
- const [selectedProviderId, setSelectedProviderId] = useState<UsageProviderId>('kenari');
12
+ const [selectedProviderId, setSelectedProviderId] = useState<UsageProviderId>('');
13
+
14
+ const providers = report ? buildProviders(report) : [];
15
+ const selected = providers.find((provider) => provider.id === selectedProviderId) ?? providers[0];
16
+
17
+ // The report can arrive with a different provider set (a key was added or
18
+ // removed); keep the selection pointing at something that exists.
19
+ useEffect(() => {
20
+ if (selected && selected.id !== selectedProviderId) setSelectedProviderId(selected.id);
21
+ }, [selected, selectedProviderId]);
13
22
 
14
23
  if (error && !report) {
15
24
  return (
@@ -39,7 +48,7 @@ export function UsageSettings() {
39
48
  <div className="flex h-full w-full overflow-hidden bg-paper text-xs text-ink">
40
49
  <UsageSidebarList
41
50
  report={report}
42
- selectedProviderId={selectedProviderId}
51
+ selectedProviderId={selected?.id ?? ''}
43
52
  onSelectProvider={setSelectedProviderId}
44
53
  />
45
54
 
@@ -57,7 +66,7 @@ export function UsageSettings() {
57
66
  </div>
58
67
  <button
59
68
  type="button"
60
- onClick={() => reload()}
69
+ onClick={() => reload({ force: true })}
61
70
  disabled={isLoading}
62
71
  className="p-1.5 bg-paper border border-ink/15 rounded-md hover:bg-ink/5 text-ink/80 transition-colors disabled:opacity-50 flex-shrink-0"
63
72
  title="Refresh usage"
@@ -69,7 +78,13 @@ export function UsageSettings() {
69
78
  <div className="flex-1 overflow-y-auto scrollbar-overlay-container scrollbar-overlay-static p-3.5 space-y-4">
70
79
  {error && <p className="text-error text-xs">{error}</p>}
71
80
 
72
- <ProviderDetail providerId={selectedProviderId} report={report} />
81
+ {selected ? (
82
+ <ProviderDetail summary={selected.summary} />
83
+ ) : (
84
+ <p className="text-[11px] text-ink/50 leading-relaxed">
85
+ No provider credentials detected. Add an API key in Settings → Providers and it will appear here.
86
+ </p>
87
+ )}
73
88
  </div>
74
89
  </div>
75
90
  </div>