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.
- package/README.md +31 -11
- package/bunfig.toml +11 -0
- package/dist/client/index.html +16 -5
- package/dist/client/index.js +1393 -0
- package/dist/client/static/js/index-8t70jhyj.js +5149 -0
- package/dist/client/static/js/index-n4wkqj7t.css +1 -0
- package/index.html +54 -0
- package/package.json +9 -10
- package/public/apple-touch-icon.png +0 -0
- package/public/icon-192x192.png +0 -0
- package/public/icon.svg +60 -0
- package/public/manifest.webmanifest +38 -0
- package/src/cli/lib/commands/serve.js +9 -5
- package/src/cli/lib/runtime.js +18 -7
- package/src/client/components/common/code-editor/handler.test.ts +274 -0
- package/src/client/components/common/code-editor/handler.ts +175 -0
- package/src/client/components/common/code-editor/index.tsx +185 -220
- package/src/client/components/common/code-surface/index.tsx +109 -12
- package/src/client/components/common/lazy-panels.tsx +5 -1
- package/src/client/components/common/provider-icon/glyphs.ts +118 -0
- package/src/client/components/common/provider-icon/index.tsx +85 -0
- package/src/client/components/common/viewport.ts +5 -3
- package/src/client/components/layout/RightActivityBar.tsx +2 -1
- package/src/client/components/layout/desktop-layout/TopNavbar.tsx +21 -4
- package/src/client/components/layout/desktop-layout/WorkspacePanels.tsx +4 -0
- package/src/client/components/layout/desktop-layout/index.tsx +26 -4
- package/src/client/components/mobile/LayoutWrapper.tsx +16 -0
- package/src/client/components/mobile/RightSidebar.tsx +11 -4
- package/src/client/components/mobile/SessionSidebar.tsx +10 -0
- package/src/client/components/mobile/mobile-right-sidebar/FullEditor.tsx +0 -1
- package/src/client/components/mobile/mobile-session-sidebar/Item.tsx +16 -1
- package/src/client/components/mobile/mobile-session-sidebar/List.tsx +3 -0
- package/src/client/components/settings/categories/ProviderSettings.tsx +45 -4
- package/src/client/components/settings/categories/UsageSettings.tsx +22 -7
- package/src/client/components/settings/categories/provider-settings/AddModelModal.tsx +322 -0
- package/src/client/components/settings/categories/provider-settings/AddProviderModal.tsx +152 -56
- package/src/client/components/settings/categories/provider-settings/AuthSection.tsx +52 -1
- package/src/client/components/settings/categories/provider-settings/DeleteProviderModal.tsx +133 -0
- package/src/client/components/settings/categories/provider-settings/DialectFields.tsx +136 -0
- package/src/client/components/settings/categories/provider-settings/EffortLadder.tsx +89 -0
- package/src/client/components/settings/categories/provider-settings/Header.tsx +2 -2
- package/src/client/components/settings/categories/provider-settings/LoginModal.tsx +2 -2
- package/src/client/components/settings/categories/provider-settings/ModelsList.tsx +22 -2
- package/src/client/components/settings/categories/provider-settings/PresetPicker.tsx +82 -0
- package/src/client/components/settings/categories/provider-settings/PriceFields.tsx +107 -0
- package/src/client/components/settings/categories/provider-settings/ReconnectModal.tsx +43 -22
- package/src/client/components/settings/categories/provider-settings/SidebarList.tsx +2 -2
- package/src/client/components/settings/categories/usage-settings/LimitsSection.tsx +115 -0
- package/src/client/components/settings/categories/usage-settings/ProviderCard.tsx +19 -26
- package/src/client/components/settings/categories/usage-settings/ProviderDetail.tsx +29 -22
- package/src/client/components/settings/categories/usage-settings/SidebarList.tsx +60 -48
- package/src/client/components/settings/categories/usage-settings/format.ts +36 -0
- package/src/client/components/settings/categories/usage-settings/providers.ts +52 -28
- package/src/client/components/workspace/browser-panel/AddressBar.tsx +1 -10
- package/src/client/components/workspace/browser-panel/Screencast.tsx +4 -5
- package/src/client/components/workspace/browser-panel/index.tsx +6 -6
- package/src/client/components/workspace/browser-toolbar/index.tsx +22 -12
- package/src/client/components/workspace/chat-timeline/AiMessageFooter.tsx +1 -1
- package/src/client/components/workspace/chat-timeline/GeneratingIndicator.tsx +1 -1
- package/src/client/components/workspace/chat-timeline/ToolCallCard.tsx +1 -1
- package/src/client/components/workspace/chat-timeline/tool-renderers/panels/Todo.tsx +1 -1
- package/src/client/components/workspace/diff-panel/Toolbar.tsx +71 -38
- package/src/client/components/workspace/diff-panel/index.tsx +139 -53
- package/src/client/components/workspace/editor/CommandPalette.test.ts +242 -0
- package/src/client/components/workspace/editor/CommandPalette.tsx +138 -0
- package/src/client/components/workspace/editor/FindWidget.test.ts +261 -0
- package/src/client/components/workspace/editor/FindWidget.tsx +219 -0
- package/src/client/components/workspace/editor/Tabs.tsx +2 -2
- package/src/client/components/workspace/editor/Toolbar.tsx +129 -26
- package/src/client/components/workspace/editor/index.tsx +180 -62
- package/src/client/components/workspace/file-explorer/Modals.tsx +79 -2
- package/src/client/components/workspace/file-explorer/TreeItem.tsx +43 -113
- package/src/client/components/workspace/model-dropdown/Item.tsx +13 -23
- package/src/client/components/workspace/model-dropdown/Panel.tsx +1 -1
- package/src/client/components/workspace/model-dropdown/Section.tsx +13 -4
- package/src/client/components/workspace/model-dropdown/index.tsx +7 -3
- package/src/client/components/workspace/todo-panel/PhaseGroup.tsx +76 -0
- package/src/client/components/workspace/todo-panel/Row.tsx +69 -0
- package/src/client/components/workspace/todo-panel/index.tsx +161 -0
- package/src/client/components/workspace/usage-panel/ProviderSelect.tsx +10 -5
- package/src/client/components/workspace/usage-panel/index.tsx +36 -18
- package/src/client/data/samples/dialogue-session.ts +1 -1
- package/src/client/data/samples/sample-tools/other.ts +1 -1
- package/src/client/data/settings/provider.ts +141 -9
- package/src/client/hooks/editor/find-harness.test-util.ts +170 -0
- package/src/client/hooks/editor/scroll-container.test.ts +102 -0
- package/src/client/hooks/editor/scroll-container.ts +26 -0
- package/src/client/hooks/editor/use-code-window.ts +2 -20
- package/src/client/hooks/editor/use-editor-find-write.test.ts +170 -0
- package/src/client/hooks/editor/use-editor-find.test.ts +118 -0
- package/src/client/hooks/editor/use-editor-find.ts +321 -0
- package/src/client/hooks/editor/use-editor-history.test.ts +216 -0
- package/src/client/hooks/editor/use-editor-history.ts +134 -0
- package/src/client/hooks/editor/use-file-editor.ts +24 -27
- package/src/client/hooks/editor/use-occurrences.test.ts +236 -0
- package/src/client/hooks/editor/use-occurrences.ts +98 -0
- package/src/client/hooks/models/use-provider-names.ts +1 -1
- package/src/client/hooks/settings/providers/actions.ts +337 -0
- package/src/client/hooks/settings/providers/api.ts +69 -0
- package/src/client/hooks/settings/providers/index.ts +138 -0
- package/src/client/hooks/settings/providers/messages.ts +77 -0
- package/src/client/hooks/settings/useUsageReport.ts +8 -3
- package/src/client/hooks/ui/theme.ts +3 -3
- package/src/client/hooks/workspace/file-tabs.ts +62 -11
- package/src/client/hooks/workspace/file-tree-actions.test.ts +348 -0
- package/src/client/hooks/workspace/file-tree-actions.ts +250 -0
- package/src/client/hooks/workspace/session-todos.ts +102 -0
- package/src/client/stubs/elkjs.ts +6 -1
- package/src/client/tailwind.css +30 -0
- package/src/server/index.ts +20 -2
- package/src/server/lib/assets/font-css.server.ts +86 -0
- package/src/server/lib/assets/fonts.server.ts +117 -0
- package/src/server/lib/bundler/css.ts +116 -0
- package/src/server/lib/lifecycle/shell-route.ts +17 -0
- package/src/server/lib/models/provider-listing.server.ts +257 -0
- package/src/server/lib/models/provider-registry.server.test.ts +122 -0
- package/src/server/lib/models/provider-registry.server.ts +76 -25
- package/src/server/lib/omp/config/models-config.ts +11 -0
- package/src/server/lib/omp/config/models-validation.test.ts +137 -0
- package/src/server/lib/omp/config/models-validation.ts +111 -2
- package/src/server/lib/omp/config/provider-seeds.test.ts +167 -0
- package/src/server/lib/omp/config/provider-seeds.ts +176 -0
- package/src/server/lib/omp/config/providers.ts +94 -100
- package/src/server/lib/omp/core/auth-credentials.ts +45 -0
- package/src/server/lib/omp/session/todos.ts +100 -0
- package/src/server/lib/omp/session/{usage.ts → usage/aggregate.ts} +1 -118
- package/src/server/lib/omp/session/usage/shaping.ts +130 -0
- package/src/server/lib/usage/deepseek.ts +4 -5
- package/src/server/lib/usage/kenari.ts +7 -6
- package/src/server/lib/usage/omp-usage.server.test.ts +126 -0
- package/src/server/lib/usage/omp-usage.server.ts +194 -0
- package/src/server/lib/usage/provider-key.ts +81 -3
- package/src/server/lib/usage/providers.server.test.ts +47 -0
- package/src/server/lib/usage/providers.server.ts +208 -0
- package/src/server/lib/usage/vendor-balance.server.test.ts +104 -0
- package/src/server/lib/usage/vendor-balance.server.ts +284 -0
- package/src/server/plugins/shell.server.ts +56 -0
- package/src/server/plugins/ssr.ts +24 -36
- package/src/server/plugins/static.test.ts +26 -29
- package/src/server/plugins/static.ts +40 -19
- package/src/server/routes/fs/action-create.test.ts +90 -0
- package/src/server/routes/fs/action.ts +23 -0
- package/src/server/routes/omp/index.ts +2 -0
- package/src/server/routes/omp/session-todos.ts +80 -0
- package/src/server/routes/settings/index.ts +2 -0
- package/src/server/routes/settings/provider-model.ts +107 -0
- package/src/server/routes/settings/provider-models.ts +65 -202
- package/src/server/routes/settings/providers.ts +22 -7
- package/src/server/routes/settings/usage.ts +125 -45
- package/src/server/routes/telemetry/tokens.ts +2 -1
- package/src/shared/lib/chat/{todo-parser.ts → todo/parser.ts} +17 -0
- package/src/shared/lib/chat/todo/progress.ts +97 -0
- package/src/shared/lib/chat/todo/snapshot.test.ts +156 -0
- package/src/shared/lib/chat/todo/snapshot.ts +215 -0
- package/src/shared/lib/code/editor/commands.test.ts +155 -0
- package/src/shared/lib/code/editor/commands.ts +260 -0
- package/src/shared/lib/code/editor/edits.test.ts +100 -0
- package/src/shared/lib/code/editor/edits.ts +122 -0
- package/src/shared/lib/code/editor/find-status.test.ts +50 -0
- package/src/shared/lib/code/editor/find-status.ts +36 -0
- package/src/shared/lib/code/editor/find.test.ts +203 -0
- package/src/shared/lib/code/editor/find.ts +224 -0
- package/src/{client/components/common/code-editor → shared/lib/code/editor}/history.test.ts +1 -1
- package/src/shared/lib/code/editor/keymap.test.ts +109 -0
- package/src/shared/lib/code/editor/keymap.ts +151 -0
- package/src/shared/lib/code/editor/layers.test.ts +77 -0
- package/src/shared/lib/code/editor/layers.ts +67 -0
- package/src/shared/lib/code/editor/line-edits.test.ts +131 -0
- package/src/shared/lib/code/editor/line-edits.ts +159 -0
- package/src/shared/lib/code/editor/lines.test.ts +81 -0
- package/src/shared/lib/code/editor/lines.ts +83 -0
- package/src/shared/lib/code/editor/marks.test.ts +86 -0
- package/src/shared/lib/code/editor/marks.ts +106 -0
- package/src/shared/lib/code/editor/occurrences.test.ts +117 -0
- package/src/shared/lib/code/editor/occurrences.ts +111 -0
- package/src/shared/lib/code/editor/palette.test.ts +91 -0
- package/src/shared/lib/code/editor/palette.ts +92 -0
- package/src/shared/lib/code/editor/reset-css.test.ts +35 -0
- package/src/shared/lib/code/editor/reset-css.ts +32 -0
- package/src/shared/lib/code/editor/text-commands.test.ts +178 -0
- package/src/shared/lib/code/editor/text-commands.ts +154 -0
- package/src/shared/lib/code/syntax-highlight.test.ts +75 -0
- package/src/shared/lib/code/syntax-highlight.ts +29 -17
- package/src/shared/lib/code/windowed-highlight.ts +52 -1
- package/src/shared/lib/fs/diff-parser.test.ts +116 -0
- package/src/shared/lib/fs/diff-parser.ts +86 -33
- package/src/shared/lib/models/{provider-connection.ts → provider/connection.ts} +37 -0
- package/src/shared/lib/models/provider/dialect.ts +179 -0
- package/src/shared/lib/models/provider/glyph.test.ts +116 -0
- package/src/shared/lib/models/provider/glyph.ts +200 -0
- package/src/shared/lib/models/{provider-models.ts → provider/models.ts} +91 -6
- package/src/shared/lib/models/provider/presets.test.ts +172 -0
- package/src/shared/lib/models/provider/presets.ts +83 -0
- package/src/shared/lib/ui/key-binding.test.ts +115 -0
- package/src/shared/lib/ui/key-binding.ts +143 -0
- package/src/shared/lib/util/platform.test.ts +87 -0
- package/src/shared/lib/util/platform.ts +30 -0
- package/src/shared/lib/workspace/file-tab-id.ts +29 -0
- package/src/shared/lib/workspace/keymap.test.ts +82 -0
- package/src/shared/lib/workspace/keymap.ts +30 -0
- package/src/shared/lib/workspace/refresh-cadence.ts +9 -0
- package/src/shared/lib/workspace/right-panels.ts +8 -0
- package/src/shared/lib/workspace/session-state/store.ts +0 -1
- package/src/shared/types/index.ts +1 -0
- package/src/shared/types/settings/provider.ts +81 -0
- package/src/shared/types/settings/usage.ts +88 -14
- package/src/shared/types/todo.ts +108 -0
- package/tsconfig.json +3 -1
- package/dist/client/static/css/index.5f630421b7.css +0 -2
- package/dist/client/static/font/KaTeX_AMS-Regular.a79f1c3119.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Caligraphic-Bold.ec17d13264.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Caligraphic-Regular.55fac25845.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Fraktur-Bold.d42a5579b0.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Fraktur-Regular.d3c882a649.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-Bold.c3fb5ac22f.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-BoldItalic.6f2bb1dff2.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-Italic.8916142bec.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Main-Regular.0462f03bdf.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Math-BoldItalic.572d331f69.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Math-Italic.f28c23acad.woff2 +0 -0
- package/dist/client/static/font/KaTeX_SansSerif-Bold.8c5b5494b6.woff2 +0 -0
- package/dist/client/static/font/KaTeX_SansSerif-Italic.3b1e59b3ba.woff2 +0 -0
- package/dist/client/static/font/KaTeX_SansSerif-Regular.ba21ed5f84.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Script-Regular.03e9641d6f.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Size1-Regular.eae34984b3.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Size2-Regular.5916a24fa3.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Size4-Regular.10d95fd3a2.woff2 +0 -0
- package/dist/client/static/font/KaTeX_Typewriter-Regular.a8709e3622.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-400-normal.2655b10c55.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-500-normal.92a991f204.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-600-normal.43788eb7f1.woff2 +0 -0
- package/dist/client/static/font/fira-code-latin-700-normal.2667215d33.woff2 +0 -0
- package/dist/client/static/js/77.0d81c74cf0.js +0 -139
- package/dist/client/static/js/77.0d81c74cf0.js.LICENSE.txt +0 -8
- package/dist/client/static/js/async/0.8e59f2eb5b.js +0 -1
- package/dist/client/static/js/async/0k.5852e6b4fa.js +0 -1
- package/dist/client/static/js/async/0w.9ed36d5860.js +0 -1
- package/dist/client/static/js/async/1.f387c12b63.js +0 -1
- package/dist/client/static/js/async/12.d091f91c99.js +0 -1
- package/dist/client/static/js/async/13.8f810a8a6c.js +0 -1
- package/dist/client/static/js/async/19.6fe909d55a.js +0 -160
- package/dist/client/static/js/async/1b.23f1141a38.js +0 -1
- package/dist/client/static/js/async/1k.973e8e21fc.js +0 -2
- package/dist/client/static/js/async/1k.973e8e21fc.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/1p.0d5cddda96.js +0 -1
- package/dist/client/static/js/async/2.b589f1733b.js +0 -1
- package/dist/client/static/js/async/2v.96165101de.js +0 -1
- package/dist/client/static/js/async/3.0cacc08423.js +0 -1
- package/dist/client/static/js/async/37.b28b2f61cd.js +0 -1
- package/dist/client/static/js/async/3d.5085fb913f.js +0 -1
- package/dist/client/static/js/async/3j.357c80ff38.js +0 -2
- package/dist/client/static/js/async/3j.357c80ff38.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/3p.bcf32a0983.js +0 -1
- package/dist/client/static/js/async/3r.3a69e98ee0.js +0 -64
- package/dist/client/static/js/async/3u.1228460e2a.js +0 -1
- package/dist/client/static/js/async/3y.3530b824ad.js +0 -198
- package/dist/client/static/js/async/3y.3530b824ad.js.LICENSE.txt +0 -5
- package/dist/client/static/js/async/4.9442eeb472.js +0 -1
- package/dist/client/static/js/async/4e.678285ca55.js +0 -1
- package/dist/client/static/js/async/4k.33a0c7eeb4.js +0 -1
- package/dist/client/static/js/async/4m.928a1dde85.js +0 -31
- package/dist/client/static/js/async/4m.928a1dde85.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/4q.aad20dab25.js +0 -1
- package/dist/client/static/js/async/5.ebaf8b89cc.js +0 -36
- package/dist/client/static/js/async/51.95bcae98af.js +0 -2
- package/dist/client/static/js/async/51.95bcae98af.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/5b.d88304bc13.js +0 -1
- package/dist/client/static/js/async/5j.d6407c4248.js +0 -287
- package/dist/client/static/js/async/5o.88316d4460.js +0 -62
- package/dist/client/static/js/async/6.f034c9b528.js +0 -1
- package/dist/client/static/js/async/66.5dfa29f957.js +0 -93
- package/dist/client/static/js/async/6o.5b1a06a382.js +0 -1
- package/dist/client/static/js/async/7.e4a9288dd0.js +0 -1
- package/dist/client/static/js/async/73.308dff4da8.js +0 -1
- package/dist/client/static/js/async/7e.a1cc9bfd88.js +0 -1
- package/dist/client/static/js/async/7g.a7814350c3.js +0 -1
- package/dist/client/static/js/async/7l.fadbbc0583.js +0 -1
- package/dist/client/static/js/async/8.5067439d8c.js +0 -1
- package/dist/client/static/js/async/8j.ea110663fb.js +0 -1
- package/dist/client/static/js/async/a.bce4542392.js +0 -1
- package/dist/client/static/js/async/aa.65d3852576.js +0 -1
- package/dist/client/static/js/async/ar.9303e5f2e8.js +0 -2
- package/dist/client/static/js/async/ar.9303e5f2e8.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/az.d656062b0f.js +0 -1
- package/dist/client/static/js/async/b.b4f6f06515.js +0 -1
- package/dist/client/static/js/async/bi.09688ba75c.js +0 -1
- package/dist/client/static/js/async/bq.586bc738d1.js +0 -1
- package/dist/client/static/js/async/c.797d933882.js +0 -1
- package/dist/client/static/js/async/c0.0af1e640d3.js +0 -24
- package/dist/client/static/js/async/ca.8a52cfd447.js +0 -1
- package/dist/client/static/js/async/cf.d34fff5d67.js +0 -1
- package/dist/client/static/js/async/ct.b74928f7a3.js +0 -1
- package/dist/client/static/js/async/cx.3cb0242a3a.js +0 -226
- package/dist/client/static/js/async/d.a5fc8fe1b3.js +0 -1
- package/dist/client/static/js/async/da.f62c70f6c8.js +0 -1
- package/dist/client/static/js/async/dp.1725d6432c.js +0 -1
- package/dist/client/static/js/async/e.93214d622b.js +0 -79
- package/dist/client/static/js/async/f.ab50467719.js +0 -19
- package/dist/client/static/js/async/f.ab50467719.js.LICENSE.txt +0 -12
- package/dist/client/static/js/async/f7.46971e9d6d.js +0 -2
- package/dist/client/static/js/async/f7.46971e9d6d.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/f8.bbf0dbae70.js +0 -1
- package/dist/client/static/js/async/g.cb81980498.js +0 -2
- package/dist/client/static/js/async/g.cb81980498.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/h.4e790471e4.js +0 -27
- package/dist/client/static/js/async/h1.393f066d09.js +0 -1
- package/dist/client/static/js/async/h3.216e9798ab.js +0 -148
- package/dist/client/static/js/async/hd.0da931b622.js +0 -1
- package/dist/client/static/js/async/hk.ba7fb04d41.js +0 -1
- package/dist/client/static/js/async/hn.b645041e00.js +0 -1
- package/dist/client/static/js/async/ho.475a5e1ae4.js +0 -1
- package/dist/client/static/js/async/hp.e9fe2e395a.js +0 -2
- package/dist/client/static/js/async/hp.e9fe2e395a.js.LICENSE.txt +0 -17
- package/dist/client/static/js/async/hs.e7c59d8090.js +0 -41
- package/dist/client/static/js/async/i.7c148e8440.js +0 -1
- package/dist/client/static/js/async/id.573df20f29.js +0 -1
- package/dist/client/static/js/async/j.ab925882d8.js +0 -1
- package/dist/client/static/js/async/jd.63886a1f09.js +0 -1
- package/dist/client/static/js/async/jg.d86ebd4013.js +0 -86
- package/dist/client/static/js/async/jl.874d352278.js +0 -1
- package/dist/client/static/js/async/jll.11313b455a.js +0 -1
- package/dist/client/static/js/async/js.67182759ef.js +0 -1
- package/dist/client/static/js/async/jsc.23b70d6f40.js +0 -1
- package/dist/client/static/js/async/jse.334588ff5d.js +0 -1
- package/dist/client/static/js/async/jz.72eb929106.js +0 -77
- package/dist/client/static/js/async/k.3210286401.js +0 -217
- package/dist/client/static/js/async/ka.c1990ecaa3.js +0 -115
- package/dist/client/static/js/async/katex-vendor.de6a87c308.js +0 -1
- package/dist/client/static/js/async/ku.29c8a59d0c.js +0 -1
- package/dist/client/static/js/async/ky.8dab1dade0.js +0 -1
- package/dist/client/static/js/async/l.12dd34e1be.js +0 -1
- package/dist/client/static/js/async/l9.949710adaf.js +0 -83
- package/dist/client/static/js/async/l9.949710adaf.js.LICENSE.txt +0 -13
- package/dist/client/static/js/async/ls.9d9a01026c.js +0 -104
- package/dist/client/static/js/async/m.eb2e6752e2.js +0 -28
- package/dist/client/static/js/async/m.eb2e6752e2.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/m3.d2b05f9604.js +0 -2
- package/dist/client/static/js/async/m3.d2b05f9604.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/ma.2c261b04dd.js +0 -2
- package/dist/client/static/js/async/ma.2c261b04dd.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/me.8ace414f80.js +0 -23
- package/dist/client/static/js/async/ms.98960af4eb.js +0 -67
- package/dist/client/static/js/async/ms.98960af4eb.js.LICENSE.txt +0 -12
- package/dist/client/static/js/async/mt.0352e976d8.js +0 -1
- package/dist/client/static/js/async/n.a9f1c51701.js +0 -1
- package/dist/client/static/js/async/na.a36cd532d6.js +0 -1
- package/dist/client/static/js/async/nk.fc7e58325e.js +0 -1
- package/dist/client/static/js/async/o.36353dfb88.js +0 -1
- package/dist/client/static/js/async/oc.b73042e8bd.js +0 -8
- package/dist/client/static/js/async/og.8eb577893a.js +0 -193
- package/dist/client/static/js/async/p.48ecfcd14e.js +0 -1
- package/dist/client/static/js/async/px.fc6877924b.js +0 -1
- package/dist/client/static/js/async/q.22588845a9.js +0 -21
- package/dist/client/static/js/async/q.22588845a9.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/q5.97dc1481b6.js +0 -286
- package/dist/client/static/js/async/qs.2a9ddb3c72.js +0 -1
- package/dist/client/static/js/async/r.51010ad87c.js +0 -1
- package/dist/client/static/js/async/rd.cb6bf495bb.js +0 -2
- package/dist/client/static/js/async/rd.cb6bf495bb.js.LICENSE.txt +0 -4
- package/dist/client/static/js/async/s.0f395cdda5.js +0 -94
- package/dist/client/static/js/async/t.4dca0e2ba8.js +0 -1
- package/dist/client/static/js/async/t9.d44f57d518.js +0 -1
- package/dist/client/static/js/async/ta.973534bbec.js +0 -1
- package/dist/client/static/js/async/tr.e878658140.js +0 -309
- package/dist/client/static/js/async/u.0871ac5b22.js +0 -1
- package/dist/client/static/js/async/ud.3065bfee31.js +0 -2
- package/dist/client/static/js/async/ud.3065bfee31.js.LICENSE.txt +0 -4
- package/dist/client/static/js/async/up.ea990da0ff.js +0 -32
- package/dist/client/static/js/async/uw.8890abbabc.js +0 -1
- package/dist/client/static/js/async/v.7d28d3c241.js +0 -34
- package/dist/client/static/js/async/vp.9b13bf8610.js +0 -1
- package/dist/client/static/js/async/vt.42416b707d.js +0 -1
- package/dist/client/static/js/async/w.08ac210f60.js +0 -1
- package/dist/client/static/js/async/wt.ad92393a90.js +0 -1
- package/dist/client/static/js/async/x.563f72ca6b.js +0 -1
- package/dist/client/static/js/async/x0.475516418f.js +0 -1
- package/dist/client/static/js/async/x1.da8687159b.js +0 -90
- package/dist/client/static/js/async/xe.da8dcc2015.js +0 -2
- package/dist/client/static/js/async/xe.da8dcc2015.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/xl.a3c562a58c.js +0 -1
- package/dist/client/static/js/async/xn.6510c0abe2.js +0 -2
- package/dist/client/static/js/async/xn.6510c0abe2.js.LICENSE.txt +0 -6
- package/dist/client/static/js/async/xn1.83a7784fe1.js +0 -10
- package/dist/client/static/js/async/xn1.83a7784fe1.js.LICENSE.txt +0 -11
- package/dist/client/static/js/async/xnl.feb0021e50.js +0 -2
- package/dist/client/static/js/async/xnl.feb0021e50.js.LICENSE.txt +0 -12
- package/dist/client/static/js/async/y.1a9ed7a5d8.js +0 -1
- package/dist/client/static/js/async/y6.43c57defe4.js +0 -1
- package/dist/client/static/js/async/yb.8c1342401d.js +0 -1
- package/dist/client/static/js/async/ybq.4491f2f2dc.js +0 -1
- package/dist/client/static/js/async/yg.35bebd6d0c.js +0 -149
- package/dist/client/static/js/async/yq.917ddd7ba2.js +0 -1
- package/dist/client/static/js/async/ys.00687cf82d.js +0 -38
- package/dist/client/static/js/async/yv.d99d829136.js +0 -1
- package/dist/client/static/js/async/z.56f61a4374.js +0 -1
- package/dist/client/static/js/async/zi.5fdb07b86a.js +0 -24
- package/dist/client/static/js/async/zj.654f37e8ff.js +0 -1
- package/dist/client/static/js/async/zn.966d6f1cc6.js +0 -1
- package/dist/client/static/js/async/zo.26213b40c0.js +0 -1
- package/dist/client/static/js/async/zx.e9f4105e00.js +0 -1
- package/dist/client/static/js/index.e6a257402a.js +0 -38
- package/dist/client/static/js/index.e6a257402a.js.LICENSE.txt +0 -4
- package/dist/client/static/js/markdown-vendor.71e43278b3.js +0 -144
- package/dist/client/static/js/shiki-vendor.cb8452ddcd.js +0 -1
- package/src/client/components/settings/categories/provider-settings/Icons.tsx +0 -35
- package/src/client/hooks/settings/providers.ts +0 -326
- package/src/server/plugins/dev-assets.ts +0 -49
- package/src/shared/lib/code/editor-utils.ts +0 -16
- package/src/shared/lib/models/provider-presets.ts +0 -29
- /package/dist/client/{apple-touch-icon.png → static/apple-touch-icon-dskx5sr3.png} +0 -0
- /package/dist/client/{icon-192x192.png → static/icon-192x192-6gn5cr2f.png} +0 -0
- /package/dist/client/{icon.svg → static/icon-d07z9dtz.svg} +0 -0
- /package/dist/client/{manifest.webmanifest → static/manifest-bnyy75kt.webmanifest} +0 -0
- /package/{dist/client → public}/icon-512x512.png +0 -0
- /package/{dist/client → public}/icon-maskable-512x512.png +0 -0
- /package/{dist/client → public}/sw.js +0 -0
- /package/src/{client/components/common/code-editor → shared/lib/code/editor}/history.ts +0 -0
- /package/src/shared/lib/models/{provider-cleanup.ts → provider/cleanup.ts} +0 -0
- /package/src/shared/lib/models/{provider-label.ts → provider/label.ts} +0 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Everything a file-tree row does that is not rendering: the context menu's
|
|
8
|
+
* action dispatch, the create/rename/delete dialogs and their server calls.
|
|
9
|
+
*
|
|
10
|
+
* Extracted from the row component because it is a stateful cluster with one
|
|
11
|
+
* shared dependency (`/api/fs/action` and the fetcher that submits to it), and
|
|
12
|
+
* because the row is at the repository's line ceiling. The component keeps the
|
|
13
|
+
* markup and the handlers that need its own DOM node; this keeps the behavior.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { useEffect, useRef, useState } from 'preact/hooks';
|
|
17
|
+
import type { FormEvent, RefObject } from 'preact/compat';
|
|
18
|
+
import { useFetcher } from '@/client/lib/router/fetcher';
|
|
19
|
+
import { toAbsolutePath } from '@/shared/lib/fs/paths';
|
|
20
|
+
import { copyToClipboard } from '@/client/hooks/ui/clipboard';
|
|
21
|
+
|
|
22
|
+
/** The shape `/api/fs/action` answers with, as far as a row reads it. */
|
|
23
|
+
interface FsActionResponse {
|
|
24
|
+
success?: boolean;
|
|
25
|
+
/** Present on the git-history payload, which the history modal reads. */
|
|
26
|
+
type?: string;
|
|
27
|
+
/** Relative path of a freshly created file. */
|
|
28
|
+
path?: string;
|
|
29
|
+
error?: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface FileActionTarget {
|
|
33
|
+
path: string;
|
|
34
|
+
name: string;
|
|
35
|
+
/** Absolute base dir of the listing, anchoring Copy Path. */
|
|
36
|
+
basePath?: string;
|
|
37
|
+
rootPath?: string;
|
|
38
|
+
repo?: string;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface FileActionDeps {
|
|
42
|
+
file: FileActionTarget;
|
|
43
|
+
/** Which diff to open, when the row is a changed file. */
|
|
44
|
+
diff?: { staged?: boolean; status?: string };
|
|
45
|
+
onOpenFile?: (file: { path: string; name: string; root?: string; repo?: string }) => void;
|
|
46
|
+
onActionComplete: () => void;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export interface FileActions {
|
|
50
|
+
contextMenu: { x: number; y: number } | null;
|
|
51
|
+
contextMenuRef: RefObject<HTMLDivElement>;
|
|
52
|
+
openContextMenu: (x: number, y: number) => void;
|
|
53
|
+
closeContextMenu: () => void;
|
|
54
|
+
showRenameModal: boolean;
|
|
55
|
+
showDeleteModal: boolean;
|
|
56
|
+
showHistoryModal: boolean;
|
|
57
|
+
showCreateModal: boolean;
|
|
58
|
+
/** The server's refusal for the create request, rendered inside its dialog. */
|
|
59
|
+
createError: string | null;
|
|
60
|
+
createName: string;
|
|
61
|
+
setCreateName: (value: string) => void;
|
|
62
|
+
renameValue: string;
|
|
63
|
+
setRenameValue: (value: string) => void;
|
|
64
|
+
/** True while a request is in flight; every dialog's buttons read it. */
|
|
65
|
+
busy: boolean;
|
|
66
|
+
/** The fetcher's own state/data, for the history modal. */
|
|
67
|
+
fetcherState: string;
|
|
68
|
+
fetcherData: FsActionResponse | undefined;
|
|
69
|
+
handleAction: (actionType: string) => void;
|
|
70
|
+
submitCreate: (e: FormEvent) => void;
|
|
71
|
+
submitRename: (e: FormEvent) => void;
|
|
72
|
+
submitDelete: () => void;
|
|
73
|
+
closeCreate: () => void;
|
|
74
|
+
closeDelete: () => void;
|
|
75
|
+
closeRename: () => void;
|
|
76
|
+
closeHistory: () => void;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function useFileActions({ file, diff, onOpenFile, onActionComplete }: FileActionDeps): FileActions {
|
|
80
|
+
const actionFetcher = useFetcher<FsActionResponse>();
|
|
81
|
+
const [contextMenu, setContextMenu] = useState<{ x: number; y: number } | null>(null);
|
|
82
|
+
const contextMenuRef = useRef<HTMLDivElement>(null);
|
|
83
|
+
const [showRenameModal, setShowRenameModal] = useState(false);
|
|
84
|
+
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
|
85
|
+
const [showHistoryModal, setShowHistoryModal] = useState(false);
|
|
86
|
+
const [showCreateModal, setShowCreateModal] = useState(false);
|
|
87
|
+
const [createName, setCreateName] = useState('');
|
|
88
|
+
const [createError, setCreateError] = useState<string | null>(null);
|
|
89
|
+
const [renameValue, setRenameValue] = useState(file.path);
|
|
90
|
+
/** Which dialog the in-flight request belongs to, so its result is routed. */
|
|
91
|
+
const pendingActionRef = useRef<'create' | null>(null);
|
|
92
|
+
|
|
93
|
+
const repo = file.repo;
|
|
94
|
+
const rootPath = file.rootPath;
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* The payload already acted on.
|
|
98
|
+
*
|
|
99
|
+
* The fetcher keeps `data` until the next request, and the callbacks below
|
|
100
|
+
* (`onActionComplete` → the panel's refresh → a re-render with a fresh
|
|
101
|
+
* callback identity) would otherwise re-run this effect for the SAME
|
|
102
|
+
* response forever: an unguarded effect here is an infinite refresh loop that
|
|
103
|
+
* pins the tab the moment a file is created.
|
|
104
|
+
*/
|
|
105
|
+
const handledRef = useRef<FsActionResponse | null>(null);
|
|
106
|
+
|
|
107
|
+
useEffect(() => {
|
|
108
|
+
if (actionFetcher.state !== 'idle' || !actionFetcher.data) return;
|
|
109
|
+
const data = actionFetcher.data;
|
|
110
|
+
if (handledRef.current === data) return;
|
|
111
|
+
handledRef.current = data;
|
|
112
|
+
// The fetcher parses the body whatever the status, so a refusal arrives as
|
|
113
|
+
// `data.error` rather than as a throw — which is what lets the create
|
|
114
|
+
// dialog stay open and put the reason beside the field.
|
|
115
|
+
if (pendingActionRef.current === 'create') {
|
|
116
|
+
pendingActionRef.current = null;
|
|
117
|
+
if (data.success && data.path) {
|
|
118
|
+
// The user asked for a file to work on, so it opens: closing the dialog
|
|
119
|
+
// and refreshing the tree alone would leave them hunting for the empty
|
|
120
|
+
// row they just created.
|
|
121
|
+
setShowCreateModal(false);
|
|
122
|
+
setCreateName('');
|
|
123
|
+
setCreateError(null);
|
|
124
|
+
onActionComplete();
|
|
125
|
+
onOpenFile?.({
|
|
126
|
+
path: data.path,
|
|
127
|
+
name: data.path.split('/').pop() || data.path,
|
|
128
|
+
root: rootPath,
|
|
129
|
+
repo: repo || '.',
|
|
130
|
+
});
|
|
131
|
+
} else {
|
|
132
|
+
setCreateError(data.error || 'Could not create the file');
|
|
133
|
+
}
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
if (data.success && !data.type) {
|
|
137
|
+
setShowRenameModal(false);
|
|
138
|
+
setShowDeleteModal(false);
|
|
139
|
+
onActionComplete();
|
|
140
|
+
}
|
|
141
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
142
|
+
}, [actionFetcher.state, actionFetcher.data]);
|
|
143
|
+
|
|
144
|
+
const submitAction = (formData: FormData) => {
|
|
145
|
+
if (repo && repo !== '.') formData.append('repo', repo);
|
|
146
|
+
if (rootPath) formData.append('root', rootPath);
|
|
147
|
+
void actionFetcher.submit(formData, { method: 'post', action: '/api/fs/action' });
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const openFile = () => {
|
|
151
|
+
onOpenFile?.({ path: file.path, name: file.name, root: rootPath, repo: repo || '.' });
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const handleAction = (actionType: string) => {
|
|
155
|
+
setContextMenu(null);
|
|
156
|
+
if (actionType === 'view') {
|
|
157
|
+
openFile();
|
|
158
|
+
} else if (actionType === 'diff') {
|
|
159
|
+
window.dispatchEvent(new CustomEvent('omp:open-diff', {
|
|
160
|
+
detail: {
|
|
161
|
+
file: file.path,
|
|
162
|
+
staged: diff?.staged,
|
|
163
|
+
status: diff?.status || 'M',
|
|
164
|
+
repo: repo || '.',
|
|
165
|
+
root: rootPath,
|
|
166
|
+
},
|
|
167
|
+
}));
|
|
168
|
+
} else if (actionType === 'explorer') {
|
|
169
|
+
const fd = new FormData();
|
|
170
|
+
fd.append('actionType', 'open_explorer');
|
|
171
|
+
fd.append('path', file.path);
|
|
172
|
+
submitAction(fd);
|
|
173
|
+
} else if (actionType === 'copy_path') {
|
|
174
|
+
void copyToClipboard(toAbsolutePath(file.basePath, file.path));
|
|
175
|
+
} else if (actionType === 'copy_relative') {
|
|
176
|
+
void copyToClipboard(file.path);
|
|
177
|
+
} else if (actionType === 'history') {
|
|
178
|
+
setShowHistoryModal(true);
|
|
179
|
+
const fd = new FormData();
|
|
180
|
+
fd.append('actionType', 'git_history');
|
|
181
|
+
fd.append('path', file.path);
|
|
182
|
+
submitAction(fd);
|
|
183
|
+
} else if (actionType === 'rename') {
|
|
184
|
+
setRenameValue(file.path);
|
|
185
|
+
setShowRenameModal(true);
|
|
186
|
+
} else if (actionType === 'new_file') {
|
|
187
|
+
setCreateName('');
|
|
188
|
+
setCreateError(null);
|
|
189
|
+
setShowCreateModal(true);
|
|
190
|
+
} else if (actionType === 'delete') {
|
|
191
|
+
setShowDeleteModal(true);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
|
|
195
|
+
const submitCreate = (e: FormEvent) => {
|
|
196
|
+
e.preventDefault();
|
|
197
|
+
const name = createName.trim();
|
|
198
|
+
if (!name) return;
|
|
199
|
+
setCreateError(null);
|
|
200
|
+
pendingActionRef.current = 'create';
|
|
201
|
+
const fd = new FormData();
|
|
202
|
+
fd.append('actionType', 'create');
|
|
203
|
+
fd.append('path', file.path);
|
|
204
|
+
fd.append('name', name);
|
|
205
|
+
submitAction(fd);
|
|
206
|
+
};
|
|
207
|
+
|
|
208
|
+
const submitRename = (e: FormEvent) => {
|
|
209
|
+
e.preventDefault();
|
|
210
|
+
const fd = new FormData();
|
|
211
|
+
fd.append('actionType', 'rename');
|
|
212
|
+
fd.append('path', file.path);
|
|
213
|
+
fd.append('newPath', renameValue);
|
|
214
|
+
submitAction(fd);
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
const submitDelete = () => {
|
|
218
|
+
const fd = new FormData();
|
|
219
|
+
fd.append('actionType', 'delete');
|
|
220
|
+
fd.append('path', file.path);
|
|
221
|
+
submitAction(fd);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
return {
|
|
225
|
+
contextMenu,
|
|
226
|
+
contextMenuRef,
|
|
227
|
+
openContextMenu: (x, y) => setContextMenu({ x, y }),
|
|
228
|
+
closeContextMenu: () => setContextMenu(null),
|
|
229
|
+
showRenameModal,
|
|
230
|
+
showDeleteModal,
|
|
231
|
+
showHistoryModal,
|
|
232
|
+
showCreateModal,
|
|
233
|
+
createError,
|
|
234
|
+
createName,
|
|
235
|
+
setCreateName,
|
|
236
|
+
renameValue,
|
|
237
|
+
setRenameValue,
|
|
238
|
+
busy: actionFetcher.state !== 'idle',
|
|
239
|
+
fetcherState: actionFetcher.state,
|
|
240
|
+
fetcherData: actionFetcher.data,
|
|
241
|
+
handleAction,
|
|
242
|
+
submitCreate,
|
|
243
|
+
submitRename,
|
|
244
|
+
submitDelete,
|
|
245
|
+
closeCreate: () => setShowCreateModal(false),
|
|
246
|
+
closeDelete: () => setShowDeleteModal(false),
|
|
247
|
+
closeRename: () => setShowRenameModal(false),
|
|
248
|
+
closeHistory: () => setShowHistoryModal(false),
|
|
249
|
+
};
|
|
250
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Reads a session's live todo list for the right-panel view.
|
|
8
|
+
*
|
|
9
|
+
* The list belongs to omp and its only durable home is the session transcript,
|
|
10
|
+
* so this hook is a reader: it fetches `GET /api/omp/session-todos` and re-reads
|
|
11
|
+
* on two triggers —
|
|
12
|
+
*
|
|
13
|
+
* - a poll (`usePanelRefresh`, paused while hidden), which covers a list the
|
|
14
|
+
* agent changed while this client was not the one driving it;
|
|
15
|
+
* - `omp:session-updated`, which the chat dispatches at every turn boundary
|
|
16
|
+
* and therefore right after each `todo` tool call, so the panel follows a
|
|
17
|
+
* running agent without waiting out the poll.
|
|
18
|
+
*
|
|
19
|
+
* The event path is throttled: one run emits the signal several times around a
|
|
20
|
+
* single tool call, and the read is a disk parse. A read already in flight is
|
|
21
|
+
* not cancelled by a newer trigger — the server answers an unchanged session
|
|
22
|
+
* from a stat, so the extra request is cheap, and dropping the newer one would
|
|
23
|
+
* leave the panel showing the list from before the call that just finished.
|
|
24
|
+
*/
|
|
25
|
+
|
|
26
|
+
import { useCallback, useEffect, useRef, useState } from 'preact/hooks';
|
|
27
|
+
import { useChamberEvent } from '@/client/hooks/ui/window-event';
|
|
28
|
+
import { usePanelRefresh } from '@/client/hooks/workspace/panel-refresh';
|
|
29
|
+
import { TODO_REFRESH_EVENT_THROTTLE_MS } from '@/shared/lib/workspace/refresh-cadence';
|
|
30
|
+
import type { SessionTodosPayload } from '@/shared/types/todo';
|
|
31
|
+
|
|
32
|
+
export interface SessionTodosState {
|
|
33
|
+
data: SessionTodosPayload | null;
|
|
34
|
+
isLoading: boolean;
|
|
35
|
+
/** Set when the last read failed; the previous payload is kept meanwhile. */
|
|
36
|
+
error: string | null;
|
|
37
|
+
reload: () => void;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function useSessionTodos(sessionId: string | null, enabled: boolean): SessionTodosState {
|
|
41
|
+
const [data, setData] = useState<SessionTodosPayload | null>(null);
|
|
42
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
43
|
+
const [error, setError] = useState<string | null>(null);
|
|
44
|
+
|
|
45
|
+
// Latest session id for the fetcher, so switching sessions does not have to
|
|
46
|
+
// rebuild the callback (and with it the poll interval) on every render.
|
|
47
|
+
const sessionIdRef = useRef(sessionId);
|
|
48
|
+
sessionIdRef.current = sessionId;
|
|
49
|
+
// Guards against a response for a session the user has already left being
|
|
50
|
+
// written into the panel that now shows a different one.
|
|
51
|
+
const requestRef = useRef(0);
|
|
52
|
+
|
|
53
|
+
const load = useCallback(() => {
|
|
54
|
+
const id = sessionIdRef.current;
|
|
55
|
+
if (!id) {
|
|
56
|
+
setData(null);
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const request = ++requestRef.current;
|
|
60
|
+
setIsLoading(true);
|
|
61
|
+
fetch(`/api/omp/session-todos?sessionId=${encodeURIComponent(id)}`)
|
|
62
|
+
.then((response) => {
|
|
63
|
+
if (!response.ok) throw new Error(`Todo request failed (${response.status})`);
|
|
64
|
+
return response.json() as Promise<SessionTodosPayload>;
|
|
65
|
+
})
|
|
66
|
+
.then((payload) => {
|
|
67
|
+
if (requestRef.current !== request) return;
|
|
68
|
+
setData(payload);
|
|
69
|
+
setError(null);
|
|
70
|
+
})
|
|
71
|
+
.catch((err: unknown) => {
|
|
72
|
+
if (requestRef.current !== request) return;
|
|
73
|
+
setError(err instanceof Error ? err.message : 'Failed to load todos');
|
|
74
|
+
})
|
|
75
|
+
.finally(() => {
|
|
76
|
+
if (requestRef.current === request) setIsLoading(false);
|
|
77
|
+
});
|
|
78
|
+
}, []);
|
|
79
|
+
|
|
80
|
+
// A session switch must not render the previous session's list under the new
|
|
81
|
+
// title: drop the payload and re-read immediately.
|
|
82
|
+
useEffect(() => {
|
|
83
|
+
setData(null);
|
|
84
|
+
setError(null);
|
|
85
|
+
if (sessionId) load();
|
|
86
|
+
}, [sessionId, load]);
|
|
87
|
+
|
|
88
|
+
usePanelRefresh(load, enabled);
|
|
89
|
+
|
|
90
|
+
const throttleRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
|
|
91
|
+
useChamberEvent('omp:session-updated', () => {
|
|
92
|
+
if (!enabled || throttleRef.current !== undefined) return;
|
|
93
|
+
throttleRef.current = setTimeout(() => {
|
|
94
|
+
throttleRef.current = undefined;
|
|
95
|
+
load();
|
|
96
|
+
}, TODO_REFRESH_EVENT_THROTTLE_MS);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
useEffect(() => () => clearTimeout(throttleRef.current), []);
|
|
100
|
+
|
|
101
|
+
return { data, isLoading, error, reload: load };
|
|
102
|
+
}
|
|
@@ -4,7 +4,12 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
/**
|
|
7
|
-
* Browser replacement for `elkjs`, aliased
|
|
7
|
+
* Browser replacement for `elkjs`, aliased through `tsconfig.json` `paths`.
|
|
8
|
+
*
|
|
9
|
+
* The alias moved there when the client build moved to Bun: Bun's bundler does
|
|
10
|
+
* not read an `alias` option (it is absent from `Bun.build`'s config and from
|
|
11
|
+
* `bun-types`; passing one is silently ignored — verified), while `paths` it
|
|
12
|
+
* honours for both internal aliases and package specifiers.
|
|
8
13
|
*
|
|
9
14
|
* Mermaid 12 changed the global `layout` default from `dagre` to `elk`, so every
|
|
10
15
|
* flowchart pulled in `elkjs/lib/elk.bundled.js` whether the diagram asked for
|
package/src/client/tailwind.css
CHANGED
|
@@ -246,6 +246,36 @@
|
|
|
246
246
|
* The selector keys off the variant attribute, not a list of theme ids. */
|
|
247
247
|
.shiki span { color: var(--shiki-light); }
|
|
248
248
|
html[data-theme-variant="dark"] .shiki span { color: var(--shiki-dark); }
|
|
249
|
+
|
|
250
|
+
/* Find matches, painted inside the token spans the highlighter emits. An
|
|
251
|
+
* inline <mark> colours a background without moving a glyph, which is what
|
|
252
|
+
* keeps the transparent <textarea> above aligned with this layer — a block or
|
|
253
|
+
* a padding would shift the text under the caret. `color: inherit` is required:
|
|
254
|
+
* a <mark> would otherwise take the UA's own black-on-yellow, ignoring the
|
|
255
|
+
* dual-theme token colour it sits inside.
|
|
256
|
+
*
|
|
257
|
+
* No `border-radius`, and the current match is marked with TOP/BOTTOM borders
|
|
258
|
+
* rather than an outline: a match spanning several tokens (`const alpha` is
|
|
259
|
+
* three) is emitted as one <mark> per token, so any per-fragment decoration
|
|
260
|
+
* that draws vertical edges — radius, outline, left/right border — paints a
|
|
261
|
+
* seam in the middle of the match. Horizontal edges join seamlessly. */
|
|
262
|
+
.shiki .find-match {
|
|
263
|
+
color: inherit;
|
|
264
|
+
background: color-mix(in srgb, var(--theme-warning) 32%, transparent);
|
|
265
|
+
}
|
|
266
|
+
/* An extra ⌘D selection: same geometry as a find match, different ink. The
|
|
267
|
+
* caret can only live in the primary range, so the two must not look alike —
|
|
268
|
+
* the user has to see where their keystrokes will land. */
|
|
269
|
+
.shiki .find-match[data-occurrence] {
|
|
270
|
+
background: color-mix(in srgb, var(--theme-info) 30%, transparent);
|
|
271
|
+
border-top: 1px solid color-mix(in srgb, var(--theme-info) 55%, transparent);
|
|
272
|
+
border-bottom: 1px solid color-mix(in srgb, var(--theme-info) 55%, transparent);
|
|
273
|
+
}
|
|
274
|
+
.shiki .find-match[data-find-current] {
|
|
275
|
+
background: color-mix(in srgb, var(--theme-warning) 55%, transparent);
|
|
276
|
+
border-top: 1px solid color-mix(in srgb, var(--theme-warning) 75%, transparent);
|
|
277
|
+
border-bottom: 1px solid color-mix(in srgb, var(--theme-warning) 75%, transparent);
|
|
278
|
+
}
|
|
249
279
|
.prose-content .katex-display { overflow-x: auto; overflow-y: hidden; }
|
|
250
280
|
|
|
251
281
|
/* Math placeholder: `marked.ts` emits a `.math-pending` span and KaTeX is
|
package/src/server/index.ts
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
import { Elysia } from 'elysia';
|
|
2
2
|
import pkg from '@/../package.json';
|
|
3
|
+
import shell from '@/../index.html';
|
|
3
4
|
import { apiRoutes } from '@/server/routes';
|
|
4
5
|
import { ssrRoutes } from '@/server/plugins/ssr';
|
|
6
|
+
import { setShellSource } from '@/server/plugins/shell.server';
|
|
5
7
|
import { getDatabasePath } from '@/server/db.server';
|
|
6
8
|
import { fetchOmpRegistrySnapshot } from '@/server/lib/models/provider-registry.server';
|
|
7
9
|
import { ompStartupError, ompStartupLogLines } from '@/server/lib/omp/core/startup';
|
|
8
10
|
import { readInstanceRecord, removeInstanceRecord, writeInstanceRecord } from '@/server/lib/lifecycle/instance';
|
|
9
11
|
import { resolveLaunchMode } from '@/server/lib/lifecycle/launch-mode';
|
|
10
12
|
import { acquirePortLock, claimPort, PortInUseError } from '@/server/lib/lifecycle/port-guard';
|
|
13
|
+
import { SHELL_ROUTE } from '@/server/lib/lifecycle/shell-route';
|
|
11
14
|
import { isMockMode } from '@/server/mock.server';
|
|
12
15
|
|
|
13
16
|
// Refuse to run without a resolvable omp binary — before the listener opens and
|
|
@@ -39,7 +42,15 @@ if (!lock) {
|
|
|
39
42
|
process.exit(1);
|
|
40
43
|
}
|
|
41
44
|
|
|
42
|
-
|
|
45
|
+
// `serve.routes` is where the HTML shell lives. Elysia merges its own static
|
|
46
|
+
// routes into that same table (its Bun adapter passes `serve` straight to
|
|
47
|
+
// `Bun.serve`), and Bun's table wins for the paths it declares — so `/_shell`
|
|
48
|
+
// renders the bundle while every other path falls through to `fetch`. The
|
|
49
|
+
// WebSocket routes keep working: Elysia composes its `websocket` handler
|
|
50
|
+
// alongside this object.
|
|
51
|
+
const app = new Elysia({ serve: { routes: { [SHELL_ROUTE]: shell } } })
|
|
52
|
+
.use(apiRoutes)
|
|
53
|
+
.use(ssrRoutes);
|
|
43
54
|
|
|
44
55
|
try {
|
|
45
56
|
await claimPort({
|
|
@@ -50,7 +61,14 @@ try {
|
|
|
50
61
|
// `reusePort: true`, which lets a second server bind a port already in
|
|
51
62
|
// use and then sit invisible while the first-bound socket takes every
|
|
52
63
|
// connection.
|
|
53
|
-
|
|
64
|
+
app.listen({ port, hostname: host, reusePort: false });
|
|
65
|
+
if (!app.server) throw new Error('Bun.serve returned no listener');
|
|
66
|
+
// The shell is rendered by asking this same listener for it: Bun renders
|
|
67
|
+
// an HTML route only while serving, and there is no in-process API for an
|
|
68
|
+
// `HTMLBundle` (`new Response(bundle)` is "[object HTMLBundle]",
|
|
69
|
+
// `app.handle` answers 404). Handing the listener over is what makes
|
|
70
|
+
// `renderShell()` possible.
|
|
71
|
+
setShellSource(app.server);
|
|
54
72
|
},
|
|
55
73
|
});
|
|
56
74
|
} catch (error) {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The font stylesheet, assembled at request time.
|
|
8
|
+
*
|
|
9
|
+
* `@font-face` blocks cannot live in the bundle: Bun's CSS loader resolves every
|
|
10
|
+
* local `url()`, and a font is always a local file — so a bundled face is either
|
|
11
|
+
* a base64 blob or an absolute filesystem path. `lib/bundler/css.ts` lifts each
|
|
12
|
+
* block out of its stylesheet and rewrites the urls to `<FONT_ROUTE_PREFIX><basename>`;
|
|
13
|
+
* this module serves the result.
|
|
14
|
+
*
|
|
15
|
+
* Two jobs, one route each:
|
|
16
|
+
*
|
|
17
|
+
* - `FONT_STYLESHEET_ROUTE` (`/fonts.css`) concatenates the extracted faces.
|
|
18
|
+
* Read per request rather than cached: it is a handful of kilobytes, and a
|
|
19
|
+
* cache is what would keep a stale face alive across an edit to a stylesheet.
|
|
20
|
+
* A missing directory is an empty stylesheet, not a 404 — the shell links this
|
|
21
|
+
* unconditionally.
|
|
22
|
+
*
|
|
23
|
+
* - `FONT_ROUTE_PREFIX` (`/fonts/<basename>`) serves the file itself from the
|
|
24
|
+
* installed packages. The basename is the whole key, so the lookup is an index
|
|
25
|
+
* rather than a path built from the request; see `fonts.server.ts`.
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
import { join } from 'path';
|
|
29
|
+
import { Glob } from 'bun';
|
|
30
|
+
|
|
31
|
+
import { packageRoot, resolveFontFile } from '@/server/lib/assets/fonts.server';
|
|
32
|
+
|
|
33
|
+
/** Directory holding one extracted face file per source stylesheet. */
|
|
34
|
+
export const FONT_FACE_DIR = join('.ompchamber-build', 'font-faces');
|
|
35
|
+
|
|
36
|
+
/** The route the assembled stylesheet is served on. */
|
|
37
|
+
export const FONT_STYLESHEET_ROUTE = '/fonts.css';
|
|
38
|
+
|
|
39
|
+
const FONT_MIME: Record<string, string> = {
|
|
40
|
+
'.woff2': 'font/woff2',
|
|
41
|
+
'.woff': 'font/woff',
|
|
42
|
+
'.ttf': 'font/ttf',
|
|
43
|
+
'.otf': 'font/otf',
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
const STYLESHEET_HEADERS = {
|
|
47
|
+
'content-type': 'text/css; charset=utf-8',
|
|
48
|
+
'cache-control': 'no-store',
|
|
49
|
+
} as const;
|
|
50
|
+
|
|
51
|
+
/** Every extracted face file, concatenated in a stable order. */
|
|
52
|
+
export async function fontStylesheet(): Promise<string> {
|
|
53
|
+
const dir = join(packageRoot(), FONT_FACE_DIR);
|
|
54
|
+
const glob = new Glob('*.css');
|
|
55
|
+
const files: string[] = [];
|
|
56
|
+
try {
|
|
57
|
+
for await (const file of glob.scan({ cwd: dir, onlyFiles: true })) files.push(file);
|
|
58
|
+
} catch {
|
|
59
|
+
return '';
|
|
60
|
+
}
|
|
61
|
+
// Sorted so the stylesheet is byte-stable between builds: the browser caches
|
|
62
|
+
// on the ETag, and an unstable order would invalidate it for no change.
|
|
63
|
+
files.sort();
|
|
64
|
+
const parts = await Promise.all(files.map((file) => Bun.file(join(dir, file)).text()));
|
|
65
|
+
return parts.join('\n');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The `/fonts.css` response. */
|
|
69
|
+
export async function serveFontStylesheet(): Promise<Response> {
|
|
70
|
+
return new Response(await fontStylesheet(), { headers: STYLESHEET_HEADERS });
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** The `/fonts/<basename>` response, or null when no package ships that file. */
|
|
74
|
+
export async function serveFontFile(name: string): Promise<Response | null> {
|
|
75
|
+
const file = await resolveFontFile(name);
|
|
76
|
+
if (!file) return null;
|
|
77
|
+
const ext = name.slice(name.lastIndexOf('.')).toLowerCase();
|
|
78
|
+
return new Response(Bun.file(file), {
|
|
79
|
+
headers: {
|
|
80
|
+
'content-type': FONT_MIME[ext] ?? 'application/octet-stream',
|
|
81
|
+
// A font's bytes never change under a fixed basename inside a pinned
|
|
82
|
+
// dependency, so this is the one asset that can be cached hard.
|
|
83
|
+
'cache-control': 'public, max-age=31536000, immutable',
|
|
84
|
+
},
|
|
85
|
+
});
|
|
86
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Font files served straight from the installed packages.
|
|
8
|
+
*
|
|
9
|
+
* The client stylesheet reaches its `@font-face` sources through the bundler's
|
|
10
|
+
* CSS loader (`lib/bundler/css.ts`), which rewrites every one of them to
|
|
11
|
+
* `<FONT_ROUTE_PREFIX><basename>`. Nothing copies them into the repository: a
|
|
12
|
+
* font is addressed by its own basename, and this module resolves that basename
|
|
13
|
+
* back to the package that ships it.
|
|
14
|
+
*
|
|
15
|
+
* A directory index rather than a path built from the request, because the
|
|
16
|
+
* request must never choose a directory. The index is built once, lazily, by
|
|
17
|
+
* walking the vendor font directories, and a basename that is not in it is a
|
|
18
|
+
* 404 rather than a lookup somewhere else.
|
|
19
|
+
*
|
|
20
|
+
* The search roots are not just `process.cwd()`. An installed package runs the
|
|
21
|
+
* AOT bundle from `dist/client`, and a `serve --prod` started from another
|
|
22
|
+
* directory has neither `node_modules` nor the source tree under it — so the
|
|
23
|
+
* roots are every directory from the running module up to the filesystem root,
|
|
24
|
+
* plus the cwd. Whichever one holds `node_modules` is the package root.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import { existsSync } from 'fs';
|
|
28
|
+
import { basename, dirname, join, parse, resolve } from 'path';
|
|
29
|
+
import { Glob } from 'bun';
|
|
30
|
+
|
|
31
|
+
/** Path prefix every rewritten font url carries. */
|
|
32
|
+
export const FONT_ROUTE_PREFIX = '/fonts/';
|
|
33
|
+
|
|
34
|
+
/** Where the fonts live, relative to a package root. */
|
|
35
|
+
const FONT_SOURCES = [
|
|
36
|
+
'node_modules/katex/dist/fonts',
|
|
37
|
+
'node_modules/@fontsource/fira-code/files',
|
|
38
|
+
] as const;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Directories a package root may be found under, nearest first.
|
|
42
|
+
*
|
|
43
|
+
* `import.meta.dir` is the running file's directory — the repo root for the
|
|
44
|
+
* TypeScript entry, `dist/client` for the AOT bundle — and walking up from it
|
|
45
|
+
* finds `node_modules` in both. The cwd is included because a source checkout
|
|
46
|
+
* run from elsewhere still resolves its dependencies through it.
|
|
47
|
+
*/
|
|
48
|
+
function searchRoots(): string[] {
|
|
49
|
+
const roots: string[] = [];
|
|
50
|
+
const seen = new Set<string>();
|
|
51
|
+
const add = (dir: string) => {
|
|
52
|
+
if (dir && !seen.has(dir)) {
|
|
53
|
+
seen.add(dir);
|
|
54
|
+
roots.push(dir);
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
let dir = import.meta.dir;
|
|
59
|
+
const { root } = parse(dir);
|
|
60
|
+
for (;;) {
|
|
61
|
+
add(dir);
|
|
62
|
+
if (dir === root) break;
|
|
63
|
+
dir = dirname(dir);
|
|
64
|
+
}
|
|
65
|
+
add(resolve(process.cwd()));
|
|
66
|
+
return roots;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/** Basename -> absolute path. Built once; the packages do not change at runtime. */
|
|
70
|
+
let index: Record<string, string> | null = null;
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* The package root — the first search root that holds `node_modules`.
|
|
74
|
+
*
|
|
75
|
+
* Both ends of the build need the same answer: the plugin writes the extracted
|
|
76
|
+
* face files here and the server reads them back, and an AOT bundle runs from
|
|
77
|
+
* `dist/client` while a source checkout runs from the repo. Falling back to the
|
|
78
|
+
* cwd keeps a checkout with no `node_modules` (a publish dry-run) working.
|
|
79
|
+
*/
|
|
80
|
+
export function packageRoot(): string {
|
|
81
|
+
for (const root of searchRoots()) {
|
|
82
|
+
// `existsSync` rather than `Bun.file().exists()`: the latter is async and
|
|
83
|
+
// file-only, and `node_modules` is a directory.
|
|
84
|
+
if (existsSync(join(root, 'node_modules'))) return root;
|
|
85
|
+
}
|
|
86
|
+
return process.cwd();
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
async function buildIndex(): Promise<Record<string, string>> {
|
|
90
|
+
const found: Record<string, string> = {};
|
|
91
|
+
const glob = new Glob('*.{woff2,woff,ttf,otf}');
|
|
92
|
+
for (const root of searchRoots()) {
|
|
93
|
+
for (const dir of FONT_SOURCES) {
|
|
94
|
+
const from = join(root, dir);
|
|
95
|
+
try {
|
|
96
|
+
for await (const file of glob.scan({ cwd: from, onlyFiles: true })) {
|
|
97
|
+
const name = basename(file);
|
|
98
|
+
// Nearest root wins: a checkout's own node_modules is what its build
|
|
99
|
+
// was made against, so it must not lose to a parent's copy.
|
|
100
|
+
found[name] ??= join(from, file);
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
// A root without these packages contributes nothing; the CSS that
|
|
104
|
+
// referenced a missing font will 404, which is the honest outcome.
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
return found;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/** The font file for a basename, or null when nothing ships it. */
|
|
112
|
+
export async function resolveFontFile(name: string): Promise<string | null> {
|
|
113
|
+
// A basename only: a request for `../` or a nested path is not a font.
|
|
114
|
+
if (!name || name !== basename(name)) return null;
|
|
115
|
+
index ??= await buildIndex();
|
|
116
|
+
return index[name] ?? null;
|
|
117
|
+
}
|