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
|
@@ -2,6 +2,7 @@ import { useEffect, useMemo, useRef, useState } from 'preact/hooks';
|
|
|
2
2
|
import type { KeyboardEvent as ReactKeyboardEvent } from 'preact/compat';
|
|
3
3
|
import { Check, ChevronDown, Search } from 'lucide-preact';
|
|
4
4
|
import { statusLabel, type ProviderEntry, type UsageProviderId } from '@/client/components/settings/categories/usage-settings/providers';
|
|
5
|
+
import { ProviderIcon } from '@/client/components/common/provider-icon';
|
|
5
6
|
import { useOnClickOutside } from '@/client/hooks/ui/on-click-outside';
|
|
6
7
|
|
|
7
8
|
interface ProviderSelectProps {
|
|
@@ -120,7 +121,8 @@ export function ProviderSelect({ providers, value, onChange }: ProviderSelectPro
|
|
|
120
121
|
title="Select usage provider"
|
|
121
122
|
className="flex w-full min-w-0 items-center justify-between gap-2 bg-paper border border-ink/15 rounded-lg px-3 py-1.5 text-xs text-ink hover:bg-ink/5 transition-colors"
|
|
122
123
|
>
|
|
123
|
-
<span className="flex min-w-0 items-
|
|
124
|
+
<span className="flex min-w-0 items-center gap-2">
|
|
125
|
+
{selected && <ProviderIcon slug={selected.id} name={selected.name} size={13} className="text-ink/60" />}
|
|
124
126
|
<span className="truncate font-medium">{selected?.name ?? 'Select provider'}</span>
|
|
125
127
|
{selected && (
|
|
126
128
|
<span className="truncate text-[10px] text-ink/50">{statusLabel(selected)}</span>
|
|
@@ -190,10 +192,13 @@ export function ProviderSelect({ providers, value, onChange }: ProviderSelectPro
|
|
|
190
192
|
index === activeIndex ? 'bg-ink/5' : 'hover:bg-ink/5'
|
|
191
193
|
}`}
|
|
192
194
|
>
|
|
193
|
-
<span className="min-w-0">
|
|
194
|
-
<
|
|
195
|
-
<span className="
|
|
196
|
-
|
|
195
|
+
<span className="flex min-w-0 items-center gap-2">
|
|
196
|
+
<ProviderIcon slug={provider.id} name={provider.name} size={14} className="text-ink/60" />
|
|
197
|
+
<span className="min-w-0">
|
|
198
|
+
<span className="block truncate font-medium text-ink">{provider.name}</span>
|
|
199
|
+
<span className="block truncate text-[10px] text-ink/50">
|
|
200
|
+
{statusLabel(provider)} · {provider.hint}
|
|
201
|
+
</span>
|
|
197
202
|
</span>
|
|
198
203
|
</span>
|
|
199
204
|
{provider.id === value && <Check size={12} className="flex-shrink-0 text-ink" />}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { useEffect } from 'preact/hooks';
|
|
1
2
|
import { AlertCircle, RefreshCw } from 'lucide-preact';
|
|
2
3
|
import { ProviderDetail } from '@/client/components/settings/categories/usage-settings/ProviderDetail';
|
|
3
4
|
import { buildProviders, type UsageProviderId } from '@/client/components/settings/categories/usage-settings/providers';
|
|
@@ -14,19 +15,30 @@ interface UsagePanelProps {
|
|
|
14
15
|
active?: boolean;
|
|
15
16
|
}
|
|
16
17
|
|
|
17
|
-
/** Right-panel Usage: the selected provider's
|
|
18
|
+
/** Right-panel Usage: the selected provider's quota and balance via a dropdown. */
|
|
18
19
|
export function UsagePanel({ className = '', active = true }: UsagePanelProps) {
|
|
19
20
|
const { report, isLoading, error, reload } = useUsageReport();
|
|
20
21
|
const [selectedProviderId, setSelectedProviderId] = useSessionState<UsageProviderId>(
|
|
21
22
|
'usage.selectedProviderId',
|
|
22
|
-
'
|
|
23
|
+
'',
|
|
23
24
|
);
|
|
24
25
|
|
|
25
|
-
//
|
|
26
|
-
// the
|
|
27
|
-
|
|
26
|
+
// Derived before every early return: a hook must not sit behind a conditional
|
|
27
|
+
// return, or the hook count changes between the loading and loaded renders.
|
|
28
|
+
const providers = report ? buildProviders(report) : [];
|
|
29
|
+
const selected = providers.find((provider) => provider.id === selectedProviderId) ?? providers[0];
|
|
30
|
+
|
|
31
|
+
// Auto refresh: keep quota current without a manual reload. Silent — the
|
|
32
|
+
// spinner stays reserved for the user's own refresh button. Polls only while
|
|
33
|
+
// the panel is actually visible.
|
|
28
34
|
usePanelRefresh(() => reload({ silent: true }), active);
|
|
29
35
|
|
|
36
|
+
// A provider can disappear from the report (its key was removed) or a new one
|
|
37
|
+
// can take the first slot; keep the persisted selection valid.
|
|
38
|
+
useEffect(() => {
|
|
39
|
+
if (selected && selected.id !== selectedProviderId) setSelectedProviderId(selected.id);
|
|
40
|
+
}, [selected, selectedProviderId, setSelectedProviderId]);
|
|
41
|
+
|
|
30
42
|
if (error && !report) {
|
|
31
43
|
return (
|
|
32
44
|
<div className={`flex h-full w-full flex-col items-center justify-center gap-2 p-8 text-center text-xs ${className}`}>
|
|
@@ -51,8 +63,6 @@ export function UsagePanel({ className = '', active = true }: UsagePanelProps) {
|
|
|
51
63
|
);
|
|
52
64
|
}
|
|
53
65
|
|
|
54
|
-
const providers = buildProviders(report);
|
|
55
|
-
|
|
56
66
|
return (
|
|
57
67
|
<div className={`flex h-full w-full min-w-0 min-h-0 flex-col overflow-hidden bg-paper text-xs text-ink ${className}`}>
|
|
58
68
|
<div className="px-3.5 py-2.5 border-b border-ink/10 flex items-center justify-between gap-3 flex-shrink-0 min-w-0">
|
|
@@ -69,7 +79,7 @@ export function UsagePanel({ className = '', active = true }: UsagePanelProps) {
|
|
|
69
79
|
</div>
|
|
70
80
|
<button
|
|
71
81
|
type="button"
|
|
72
|
-
onClick={() => reload()}
|
|
82
|
+
onClick={() => reload({ force: true })}
|
|
73
83
|
disabled={isLoading}
|
|
74
84
|
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"
|
|
75
85
|
title="Refresh usage"
|
|
@@ -78,20 +88,28 @@ export function UsagePanel({ className = '', active = true }: UsagePanelProps) {
|
|
|
78
88
|
</button>
|
|
79
89
|
</div>
|
|
80
90
|
|
|
81
|
-
|
|
82
|
-
<div className="
|
|
83
|
-
<
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
91
|
+
{providers.length > 0 && (
|
|
92
|
+
<div className="px-3.5 py-2.5 border-b border-ink/10 flex-shrink-0 min-w-0">
|
|
93
|
+
<div className="flex items-center gap-2 min-w-0">
|
|
94
|
+
<span className="text-[11px] font-semibold text-ink/60 flex-shrink-0">Provider</span>
|
|
95
|
+
<ProviderSelect
|
|
96
|
+
providers={providers}
|
|
97
|
+
value={selected?.id ?? ''}
|
|
98
|
+
onChange={setSelectedProviderId}
|
|
99
|
+
/>
|
|
100
|
+
</div>
|
|
89
101
|
</div>
|
|
90
|
-
|
|
102
|
+
)}
|
|
91
103
|
|
|
92
104
|
<div className="flex-1 min-w-0 min-h-0 w-full overflow-y-auto scrollbar-overlay-container scrollbar-overlay-static p-3.5 space-y-4">
|
|
93
105
|
{error && <p className="text-error text-xs">{error}</p>}
|
|
94
|
-
|
|
106
|
+
{selected ? (
|
|
107
|
+
<ProviderDetail summary={selected.summary} />
|
|
108
|
+
) : (
|
|
109
|
+
<p className="text-[11px] text-ink/50 leading-relaxed">
|
|
110
|
+
No provider credentials detected. Add an API key in Settings → Providers and it will appear here.
|
|
111
|
+
</p>
|
|
112
|
+
)}
|
|
95
113
|
</div>
|
|
96
114
|
</div>
|
|
97
115
|
);
|
|
@@ -42,7 +42,7 @@ export function getSampleDialogueSession(): {
|
|
|
42
42
|
id: '1a6cb095',
|
|
43
43
|
role: 'ai',
|
|
44
44
|
content:
|
|
45
|
-
'Elysia + Bun app (OMPChamber). Top-level contents:\n\n- **App source** — `src/` (client, server, shared)\n- **Config** — `package.json`, `tsconfig.json`, `
|
|
45
|
+
'Elysia + Bun app (OMPChamber). Top-level contents:\n\n- **App source** — `src/` (client, server, shared)\n- **Config** — `package.json`, `tsconfig.json`, `bunfig.toml`, `bun.lock`, `AGENTS.md`, `DESIGN.md`\n- **Static/PWA** — `public/` (icons, manifest, service worker)\n- **DB** — `workspace.db` (112 KB)\n- **Misc** — `notes/` (2 planning docs)\n\nRoot has no stray source files; project is cleanly under `src/`.',
|
|
46
46
|
date: 'Sep 9, 04:33 AM',
|
|
47
47
|
thinking: {
|
|
48
48
|
thought: 'Brief listing.',
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ProviderItem } from '@/shared/types';
|
|
1
|
+
import type { PresetProviderOption, ProviderItem } from '@/shared/types/settings/provider';
|
|
2
2
|
|
|
3
3
|
export const DEFAULT_PROVIDERS_LIST: ProviderItem[] = [
|
|
4
4
|
{
|
|
@@ -8,6 +8,7 @@ export const DEFAULT_PROVIDERS_LIST: ProviderItem[] = [
|
|
|
8
8
|
icon: 'deepseek',
|
|
9
9
|
status: 'connected',
|
|
10
10
|
configuredIn: 'auth credentials',
|
|
11
|
+
credentialSource: 'chamber',
|
|
11
12
|
apiKey: 'sk-ds-••••••••••••••••••••38f1',
|
|
12
13
|
baseUrl: 'https://api.deepseek.com/v1',
|
|
13
14
|
models: [
|
|
@@ -59,6 +60,7 @@ export const DEFAULT_PROVIDERS_LIST: ProviderItem[] = [
|
|
|
59
60
|
icon: 'agentrouter',
|
|
60
61
|
status: 'connected',
|
|
61
62
|
configuredIn: 'auth credentials',
|
|
63
|
+
credentialSource: 'chamber',
|
|
62
64
|
apiKey: 'ar-live-•••••••••••••••••••89c2',
|
|
63
65
|
baseUrl: 'https://router.agent.ai/v1',
|
|
64
66
|
models: [
|
|
@@ -104,6 +106,7 @@ export const DEFAULT_PROVIDERS_LIST: ProviderItem[] = [
|
|
|
104
106
|
icon: 'commandcode',
|
|
105
107
|
status: 'connected',
|
|
106
108
|
configuredIn: 'auth credentials',
|
|
109
|
+
credentialSource: 'chamber',
|
|
107
110
|
apiKey: 'cc-key-••••••••••••••••••••7a12',
|
|
108
111
|
baseUrl: 'https://api.commandcode.dev/v1',
|
|
109
112
|
models: Array.from({ length: 41 }, (_, i) => {
|
|
@@ -142,6 +145,7 @@ export const DEFAULT_PROVIDERS_LIST: ProviderItem[] = [
|
|
|
142
145
|
icon: 'opencode',
|
|
143
146
|
status: 'connected',
|
|
144
147
|
configuredIn: 'auth credentials',
|
|
148
|
+
credentialSource: 'chamber',
|
|
145
149
|
apiKey: 'zen-live-•••••••••••••••••••44fa',
|
|
146
150
|
baseUrl: 'https://zen.opencode.ai/v1',
|
|
147
151
|
models: [
|
|
@@ -161,6 +165,7 @@ export const DEFAULT_PROVIDERS_LIST: ProviderItem[] = [
|
|
|
161
165
|
icon: 'claude',
|
|
162
166
|
status: 'connected',
|
|
163
167
|
configuredIn: 'auth credentials',
|
|
168
|
+
credentialSource: 'chamber',
|
|
164
169
|
apiKey: 'sk-ant-•••••••••••••••••••••991c',
|
|
165
170
|
baseUrl: 'https://api.anthropic.com/v1',
|
|
166
171
|
models: [
|
|
@@ -174,12 +179,139 @@ export const DEFAULT_PROVIDERS_LIST: ProviderItem[] = [
|
|
|
174
179
|
},
|
|
175
180
|
];
|
|
176
181
|
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
182
|
+
/**
|
|
183
|
+
* Providers offered by the Add Provider picker. `group` is the section the
|
|
184
|
+
* picker renders; `api`/`auth`/`discovery` are the `models.yml` fields the
|
|
185
|
+
* preset seeds, so a choice that omp understands is made once here rather than
|
|
186
|
+
* re-derived from the URL at write time.
|
|
187
|
+
*
|
|
188
|
+
* A preset whose id names a provider omp already bundles (`bundled: true`) is
|
|
189
|
+
* an OVERRIDE entry: omp keeps serving its own model list for that provider and
|
|
190
|
+
* only the endpoint changes. That is the documented way to point a bundled
|
|
191
|
+
* provider at a proxy, and it is deliberately not offered for providers whose
|
|
192
|
+
* models the chamber would then have to duplicate.
|
|
193
|
+
*/
|
|
194
|
+
export const PRESET_NEW_PROVIDERS: PresetProviderOption[] = [
|
|
195
|
+
{
|
|
196
|
+
id: 'openai', name: 'OpenAI', slug: 'openai', icon: 'openai',
|
|
197
|
+
defaultUrl: 'https://api.openai.com/v1', api: 'openai-responses', group: 'Frontier APIs',
|
|
198
|
+
},
|
|
199
|
+
{
|
|
200
|
+
id: 'anthropic', name: 'Anthropic', slug: 'anthropic', icon: 'claude',
|
|
201
|
+
defaultUrl: 'https://api.anthropic.com/v1', api: 'anthropic-messages', group: 'Frontier APIs',
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
id: 'google', name: 'Google Gemini', slug: 'google', icon: 'gemini',
|
|
205
|
+
defaultUrl: 'https://generativelanguage.googleapis.com/v1beta',
|
|
206
|
+
api: 'google-generative-ai', group: 'Frontier APIs',
|
|
207
|
+
},
|
|
208
|
+
{
|
|
209
|
+
id: 'groq', name: 'Groq Cloud', slug: 'groq', icon: 'groq',
|
|
210
|
+
defaultUrl: 'https://api.groq.com/openai/v1', api: 'openai-completions', group: 'Frontier APIs',
|
|
211
|
+
},
|
|
212
|
+
{
|
|
213
|
+
id: 'mistral', name: 'Mistral AI', slug: 'mistral', icon: 'mistral',
|
|
214
|
+
defaultUrl: 'https://api.mistral.ai/v1', api: 'openai-completions', group: 'Frontier APIs',
|
|
215
|
+
},
|
|
216
|
+
{
|
|
217
|
+
id: 'xai', name: 'xAI', slug: 'xai', icon: 'xai',
|
|
218
|
+
defaultUrl: 'https://api.x.ai/v1', api: 'openai-completions', group: 'Frontier APIs',
|
|
219
|
+
},
|
|
220
|
+
{
|
|
221
|
+
id: 'cerebras', name: 'Cerebras', slug: 'cerebras', icon: 'cerebras',
|
|
222
|
+
defaultUrl: 'https://api.cerebras.ai/v1', api: 'openai-completions', group: 'Frontier APIs',
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
id: 'deepseek', name: 'DeepSeek', slug: 'deepseek', icon: 'deepseek',
|
|
226
|
+
defaultUrl: 'https://api.deepseek.com/v1', api: 'openai-completions', group: 'Frontier APIs',
|
|
227
|
+
},
|
|
228
|
+
{
|
|
229
|
+
id: 'openrouter', name: 'OpenRouter', slug: 'openrouter', icon: 'openrouter',
|
|
230
|
+
defaultUrl: 'https://openrouter.ai/api/v1', api: 'openai-completions', group: 'Gateways',
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
id: 'together', name: 'Together AI', slug: 'together', icon: 'together',
|
|
234
|
+
defaultUrl: 'https://api.together.xyz/v1', api: 'openai-completions', group: 'Gateways',
|
|
235
|
+
},
|
|
236
|
+
{
|
|
237
|
+
id: 'fireworks', name: 'Fireworks', slug: 'fireworks', icon: 'fireworks',
|
|
238
|
+
defaultUrl: 'https://api.fireworks.ai/inference/v1', api: 'openai-completions', group: 'Gateways',
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
id: 'deepinfra', name: 'DeepInfra', slug: 'deepinfra', icon: 'deepinfra',
|
|
242
|
+
defaultUrl: 'https://api.deepinfra.com/v1/openai', api: 'openai-completions', group: 'Gateways',
|
|
243
|
+
},
|
|
244
|
+
{
|
|
245
|
+
id: 'siliconflow', name: 'SiliconFlow', slug: 'siliconflow', icon: 'siliconflow',
|
|
246
|
+
defaultUrl: 'https://api.siliconflow.com/v1', api: 'openai-completions', group: 'Gateways',
|
|
247
|
+
},
|
|
248
|
+
{
|
|
249
|
+
id: 'nvidia', name: 'NVIDIA NIM', slug: 'nvidia', icon: 'nvidia',
|
|
250
|
+
defaultUrl: 'https://integrate.api.nvidia.com/v1', api: 'openai-completions', group: 'Gateways',
|
|
251
|
+
},
|
|
252
|
+
{
|
|
253
|
+
id: 'novita', name: 'Novita AI', slug: 'novita', icon: 'novita',
|
|
254
|
+
defaultUrl: 'https://api.novita.ai/v3/openai', api: 'openai-completions', group: 'Gateways',
|
|
255
|
+
},
|
|
256
|
+
{
|
|
257
|
+
id: 'vercel-ai-gateway', name: 'Vercel AI Gateway', slug: 'vercel-ai-gateway', icon: 'vercel',
|
|
258
|
+
defaultUrl: 'https://ai-gateway.vercel.sh/v1', api: 'openai-completions', group: 'Gateways',
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
id: 'ollama', name: 'Ollama (Local)', slug: 'ollama', icon: 'ollama',
|
|
262
|
+
defaultUrl: 'http://127.0.0.1:11434', api: 'openai-responses',
|
|
263
|
+
auth: 'none', discovery: 'ollama', group: 'Local & self-hosted',
|
|
264
|
+
},
|
|
265
|
+
{
|
|
266
|
+
id: 'lm-studio', name: 'LM Studio (Local)', slug: 'lm-studio', icon: 'lmstudio',
|
|
267
|
+
defaultUrl: 'http://127.0.0.1:1234/v1', api: 'openai-completions',
|
|
268
|
+
auth: 'none', discovery: 'lm-studio', group: 'Local & self-hosted',
|
|
269
|
+
},
|
|
270
|
+
{
|
|
271
|
+
id: 'llama-cpp', name: 'llama.cpp (Local)', slug: 'llama.cpp', icon: 'llamacpp',
|
|
272
|
+
defaultUrl: 'http://127.0.0.1:8080', api: 'openai-responses',
|
|
273
|
+
auth: 'none', discovery: 'llama.cpp', group: 'Local & self-hosted',
|
|
274
|
+
},
|
|
275
|
+
{
|
|
276
|
+
id: 'vllm', name: 'vLLM (Local)', slug: 'vllm', icon: 'vllm',
|
|
277
|
+
defaultUrl: 'http://127.0.0.1:8000/v1', api: 'openai-completions',
|
|
278
|
+
auth: 'none', discovery: 'openai-models-list', group: 'Local & self-hosted',
|
|
279
|
+
},
|
|
280
|
+
{
|
|
281
|
+
id: 'litellm', name: 'LiteLLM Proxy', slug: 'litellm', icon: 'litellm',
|
|
282
|
+
defaultUrl: 'http://localhost:4000/v1', api: 'openai-completions',
|
|
283
|
+
discovery: 'litellm', group: 'Local & self-hosted',
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
id: 'azure', name: 'Azure OpenAI', slug: 'azure', icon: 'azure',
|
|
287
|
+
defaultUrl: 'https://YOUR-RESOURCE.openai.azure.com/openai/v1',
|
|
288
|
+
api: 'azure-openai-responses', bundled: true, group: 'Other dialects',
|
|
289
|
+
},
|
|
290
|
+
{
|
|
291
|
+
id: 'amazon-bedrock', name: 'Amazon Bedrock', slug: 'amazon-bedrock', icon: 'amazon',
|
|
292
|
+
defaultUrl: 'https://bedrock-runtime.us-east-1.amazonaws.com',
|
|
293
|
+
api: 'bedrock-converse-stream', bundled: true, group: 'Other dialects',
|
|
294
|
+
},
|
|
295
|
+
{
|
|
296
|
+
id: 'google-vertex', name: 'Google Vertex AI', slug: 'google-vertex', icon: 'vertexai',
|
|
297
|
+
defaultUrl: 'https://us-central1-aiplatform.googleapis.com/v1',
|
|
298
|
+
api: 'google-vertex', bundled: true, group: 'Other dialects',
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
id: 'openai-codex', name: 'OpenAI Codex', slug: 'openai-codex', icon: 'codex',
|
|
302
|
+
defaultUrl: 'https://chatgpt.com/backend-api/codex',
|
|
303
|
+
api: 'openai-codex-responses', bundled: true, group: 'Other dialects',
|
|
304
|
+
},
|
|
305
|
+
{
|
|
306
|
+
id: 'anthropic-custom', name: 'Anthropic-Compatible Proxy', slug: 'anthropic-custom', icon: 'claude',
|
|
307
|
+
defaultUrl: 'https://', api: 'anthropic-messages', group: 'Custom',
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
id: 'openai-custom', name: 'OpenAI-Compatible Gateway', slug: 'openai-custom', icon: 'openai',
|
|
311
|
+
defaultUrl: 'https://', api: 'openai-completions', group: 'Custom',
|
|
312
|
+
},
|
|
313
|
+
{
|
|
314
|
+
id: 'custom', name: 'Custom OpenAI-Compatible', slug: 'custom', icon: 'custom',
|
|
315
|
+
defaultUrl: 'https://', api: 'openai-completions', group: 'Custom',
|
|
316
|
+
},
|
|
185
317
|
];
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The harness `use-editor-find` is exercised through.
|
|
8
|
+
*
|
|
9
|
+
* Split out because the find hook has two test files (opening and stepping /
|
|
10
|
+
* writing and options) and both must render it the same way — the session
|
|
11
|
+
* provider it needs, the surface calls it makes, and a fresh session id per test
|
|
12
|
+
* so the PERSISTED query does not leak between them.
|
|
13
|
+
*
|
|
14
|
+
* The hook's modules are imported dynamically, after the DOM globals exist:
|
|
15
|
+
* Preact binds its environment at evaluation time, and a static import would
|
|
16
|
+
* capture the DOM-less one.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
import { afterAll, afterEach, beforeAll, beforeEach } from 'bun:test';
|
|
20
|
+
import { Window } from 'happy-dom';
|
|
21
|
+
import type { ComponentChildren, h as PreactH, render as PreactRender } from 'preact';
|
|
22
|
+
import type { act as PreactAct } from 'preact/test-utils';
|
|
23
|
+
import type { EditorFindState, useEditorFind as UseEditorFindHook } from '@/client/hooks/editor/use-editor-find';
|
|
24
|
+
|
|
25
|
+
export interface FindHarness {
|
|
26
|
+
/** The latest render's state; re-read after every settle, never held across one. */
|
|
27
|
+
readonly find: EditorFindState | null;
|
|
28
|
+
/** What the hook under test is handed — mutated per test, then re-rendered. */
|
|
29
|
+
readonly input: {
|
|
30
|
+
text: string;
|
|
31
|
+
documentKey: string;
|
|
32
|
+
selection: { start: number; end: number } | null;
|
|
33
|
+
};
|
|
34
|
+
/** Surface calls, so "did it select / reveal / write" is observable. */
|
|
35
|
+
readonly selections: Array<[number, number, boolean]>;
|
|
36
|
+
readonly reveals: number[];
|
|
37
|
+
readonly writes: Array<{ value: string; caretStart: number }>;
|
|
38
|
+
/** Re-render with the current `input` and settle. */
|
|
39
|
+
rerender: () => Promise<void>;
|
|
40
|
+
/** Flush pending effects and renders. */
|
|
41
|
+
flush: () => Promise<void>;
|
|
42
|
+
/**
|
|
43
|
+
* Preact's `act`, exposed because every interaction in these tests is a hook
|
|
44
|
+
* call the DOM never sees: it has to be wrapped to be flushed.
|
|
45
|
+
*/
|
|
46
|
+
act: typeof PreactAct;
|
|
47
|
+
/** Register the before/after hooks this harness needs. */
|
|
48
|
+
install: () => void;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export async function createFindHarness(): Promise<FindHarness> {
|
|
52
|
+
let useEditorFind: typeof UseEditorFindHook;
|
|
53
|
+
let SessionStateContext: { Provider: (props: { value: { sessionId: string; ready: boolean }; children: ComponentChildren }) => ComponentChildren };
|
|
54
|
+
let h: typeof PreactH;
|
|
55
|
+
let render: typeof PreactRender;
|
|
56
|
+
let act: typeof PreactAct;
|
|
57
|
+
|
|
58
|
+
let find: EditorFindState | null = null;
|
|
59
|
+
let container: HTMLElement;
|
|
60
|
+
let sessionId = 'test-session';
|
|
61
|
+
let sessionCounter = 0;
|
|
62
|
+
|
|
63
|
+
const input: FindHarness['input'] = { text: 'alpha beta alpha', documentKey: 'file-1', selection: { start: 0, end: 5 } };
|
|
64
|
+
const selections: FindHarness['selections'] = [];
|
|
65
|
+
const reveals: number[] = [];
|
|
66
|
+
const writes: FindHarness['writes'] = [];
|
|
67
|
+
|
|
68
|
+
function Inner() {
|
|
69
|
+
find = useEditorFind({
|
|
70
|
+
text: input.text,
|
|
71
|
+
documentKey: input.documentKey,
|
|
72
|
+
readSelection: () => input.selection,
|
|
73
|
+
select: (start, end, focus = true) => selections.push([start, end, focus]),
|
|
74
|
+
reveal: (offset) => reveals.push(offset),
|
|
75
|
+
applyDocument: (value, caretStart) => writes.push({ value, caretStart }),
|
|
76
|
+
});
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function Harness() {
|
|
81
|
+
// The hook persists the query and the toggles per session, so it needs the
|
|
82
|
+
// provider the panel supplies. A test session id is all it reads.
|
|
83
|
+
return h(SessionStateContext.Provider, { value: { sessionId, ready: true }, children: h(Inner, {}) });
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async function flush() {
|
|
87
|
+
for (let i = 0; i < 3; i++) {
|
|
88
|
+
await act(async () => {
|
|
89
|
+
for (let turn = 0; turn < 4; turn++) await Promise.resolve();
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function rerender() {
|
|
95
|
+
await act(async () => {
|
|
96
|
+
render(h(Harness, {}), container);
|
|
97
|
+
});
|
|
98
|
+
await flush();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const domGlobals = (win: Window): Record<string, unknown> => ({
|
|
102
|
+
window: win,
|
|
103
|
+
document: win.document,
|
|
104
|
+
navigator: win.navigator,
|
|
105
|
+
Node: win.Node,
|
|
106
|
+
Element: win.Element,
|
|
107
|
+
HTMLElement: win.HTMLElement,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const installed: Record<string, unknown> = {};
|
|
111
|
+
const displaced: Record<string, unknown> = {};
|
|
112
|
+
|
|
113
|
+
return {
|
|
114
|
+
get find() {
|
|
115
|
+
return find;
|
|
116
|
+
},
|
|
117
|
+
input,
|
|
118
|
+
selections,
|
|
119
|
+
reveals,
|
|
120
|
+
writes,
|
|
121
|
+
rerender,
|
|
122
|
+
flush,
|
|
123
|
+
get act() {
|
|
124
|
+
return act;
|
|
125
|
+
},
|
|
126
|
+
install() {
|
|
127
|
+
beforeAll(async () => {
|
|
128
|
+
const win = new Window({ url: 'http://localhost' });
|
|
129
|
+
for (const [key, value] of Object.entries(domGlobals(win))) {
|
|
130
|
+
displaced[key] = (globalThis as Record<string, unknown>)[key];
|
|
131
|
+
installed[key] = value;
|
|
132
|
+
(globalThis as Record<string, unknown>)[key] = value;
|
|
133
|
+
}
|
|
134
|
+
({ useEditorFind } = await import('@/client/hooks/editor/use-editor-find'));
|
|
135
|
+
({ SessionStateContext } = await import('@/client/hooks/workspace/session-state/context'));
|
|
136
|
+
({ h, render } = await import('preact'));
|
|
137
|
+
({ act } = await import('preact/test-utils'));
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
afterAll(() => {
|
|
141
|
+
for (const key of Object.keys(installed)) {
|
|
142
|
+
if (displaced[key] === undefined) delete (globalThis as Record<string, unknown>)[key];
|
|
143
|
+
else (globalThis as Record<string, unknown>)[key] = displaced[key];
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
beforeEach(async () => {
|
|
148
|
+
find = null;
|
|
149
|
+
// The query and the toggles are persisted per session — that is the
|
|
150
|
+
// feature — so each test gets its OWN session id rather than sharing a
|
|
151
|
+
// blob with the test before it.
|
|
152
|
+
sessionId = `test-session-${++sessionCounter}`;
|
|
153
|
+
input.text = 'alpha beta alpha';
|
|
154
|
+
input.documentKey = 'file-1';
|
|
155
|
+
input.selection = { start: 0, end: 5 };
|
|
156
|
+
selections.length = 0;
|
|
157
|
+
reveals.length = 0;
|
|
158
|
+
writes.length = 0;
|
|
159
|
+
container = document.createElement('div');
|
|
160
|
+
document.body.appendChild(container);
|
|
161
|
+
await rerender();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
afterEach(() => {
|
|
165
|
+
render(null, container);
|
|
166
|
+
container.remove();
|
|
167
|
+
});
|
|
168
|
+
},
|
|
169
|
+
};
|
|
170
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The scroll-container walk, shared by the editor's lazy window and the find
|
|
8
|
+
* widget's reveal.
|
|
9
|
+
*
|
|
10
|
+
* The two must agree: one resolves which lines to draw from the visible band,
|
|
11
|
+
* the other moves that band. Two copies of this walk would let the widget scroll
|
|
12
|
+
* a container the window never reads — the match would be marked somewhere
|
|
13
|
+
* nobody can see. Exercised against a real DOM (happy-dom) because the answer is
|
|
14
|
+
* a computed style.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
18
|
+
import { Window } from 'happy-dom';
|
|
19
|
+
|
|
20
|
+
import { findScrollContainer } from '@/client/hooks/editor/scroll-container';
|
|
21
|
+
|
|
22
|
+
const installed: Record<string, unknown> = {};
|
|
23
|
+
const displaced: Record<string, unknown> = {};
|
|
24
|
+
|
|
25
|
+
beforeAll(() => {
|
|
26
|
+
const win = new Window({ url: 'http://localhost' });
|
|
27
|
+
for (const key of ['window', 'document', 'Node', 'Element', 'HTMLElement', 'getComputedStyle']) {
|
|
28
|
+
displaced[key] = (globalThis as Record<string, unknown>)[key];
|
|
29
|
+
installed[key] = (win as unknown as Record<string, unknown>)[key];
|
|
30
|
+
(globalThis as Record<string, unknown>)[key] = installed[key];
|
|
31
|
+
}
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
afterAll(() => {
|
|
35
|
+
for (const key of Object.keys(installed)) {
|
|
36
|
+
if (displaced[key] === undefined) delete (globalThis as Record<string, unknown>)[key];
|
|
37
|
+
else (globalThis as Record<string, unknown>)[key] = displaced[key];
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
/** An element whose computed style reports `overflow-y`, with a scrollable height. */
|
|
42
|
+
function makeElement(overflowY: string, scrollable: boolean): HTMLElement {
|
|
43
|
+
const element = document.createElement('div');
|
|
44
|
+
element.style.overflowY = overflowY;
|
|
45
|
+
if (scrollable) {
|
|
46
|
+
// happy-dom reports clientHeight 0, so the walk's `scrollHeight > clientHeight`
|
|
47
|
+
// test needs a stubbed pair to describe a container that can actually scroll.
|
|
48
|
+
Object.defineProperty(element, 'scrollHeight', { value: 400, configurable: true });
|
|
49
|
+
Object.defineProperty(element, 'clientHeight', { value: 100, configurable: true });
|
|
50
|
+
}
|
|
51
|
+
return element;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
describe('findScrollContainer', () => {
|
|
55
|
+
test('finds the nearest ancestor that scrolls', () => {
|
|
56
|
+
const outer = makeElement('auto', true);
|
|
57
|
+
const inner = makeElement('visible', false);
|
|
58
|
+
const surface = document.createElement('div');
|
|
59
|
+
inner.appendChild(surface);
|
|
60
|
+
outer.appendChild(inner);
|
|
61
|
+
document.body.appendChild(outer);
|
|
62
|
+
|
|
63
|
+
expect(findScrollContainer(surface)).toBe(outer);
|
|
64
|
+
outer.remove();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('skips a container that cannot actually scroll', () => {
|
|
68
|
+
// `overflow: auto` on a box whose content fits is not a scroller: returning
|
|
69
|
+
// it would make `revealOffset` write scrollTop onto a box that ignores it.
|
|
70
|
+
const outer = makeElement('auto', true);
|
|
71
|
+
const fitted = makeElement('auto', false);
|
|
72
|
+
const surface = document.createElement('div');
|
|
73
|
+
fitted.appendChild(surface);
|
|
74
|
+
outer.appendChild(fitted);
|
|
75
|
+
document.body.appendChild(outer);
|
|
76
|
+
|
|
77
|
+
expect(findScrollContainer(surface)).toBe(outer);
|
|
78
|
+
outer.remove();
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('reports nothing when no ancestor scrolls', () => {
|
|
82
|
+
const plain = document.createElement('div');
|
|
83
|
+
const surface = document.createElement('div');
|
|
84
|
+
plain.appendChild(surface);
|
|
85
|
+
document.body.appendChild(plain);
|
|
86
|
+
|
|
87
|
+
expect(findScrollContainer(surface)).toBeNull();
|
|
88
|
+
plain.remove();
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
test('prefers the nearest scroller, so a nested panel scrolls itself', () => {
|
|
92
|
+
const outer = makeElement('auto', true);
|
|
93
|
+
const inner = makeElement('scroll', true);
|
|
94
|
+
const surface = document.createElement('div');
|
|
95
|
+
inner.appendChild(surface);
|
|
96
|
+
outer.appendChild(inner);
|
|
97
|
+
document.body.appendChild(outer);
|
|
98
|
+
|
|
99
|
+
expect(findScrollContainer(surface)).toBe(inner);
|
|
100
|
+
outer.remove();
|
|
101
|
+
});
|
|
102
|
+
});
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The ancestor a code surface actually scrolls inside.
|
|
8
|
+
*
|
|
9
|
+
* The lazy editor window and the find widget's "scroll to this match" both need
|
|
10
|
+
* this answer, and they must agree: one resolves which lines to draw from the
|
|
11
|
+
* visible band, the other moves that band. Two copies of the walk would let the
|
|
12
|
+
* widget scroll a container the window never reads.
|
|
13
|
+
*
|
|
14
|
+
* Walks up rather than taking a ref: the surface is mounted under different
|
|
15
|
+
* scrollers (the desktop editor panel, the phone's full-screen editor) and is
|
|
16
|
+
* also mounted inside a page that scrolls itself, which is the `null` case.
|
|
17
|
+
*/
|
|
18
|
+
export function findScrollContainer(element: HTMLElement): HTMLElement | null {
|
|
19
|
+
for (let node = element.parentElement; node; node = node.parentElement) {
|
|
20
|
+
const overflowY = getComputedStyle(node).overflowY;
|
|
21
|
+
if ((overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') && node.scrollHeight > node.clientHeight) {
|
|
22
|
+
return node;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return null;
|
|
26
|
+
}
|