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
|
@@ -1,14 +1,11 @@
|
|
|
1
|
-
import { useEffect,
|
|
2
|
-
import type { FormEvent } from 'preact/compat';
|
|
1
|
+
import { useEffect, useState } from 'preact/hooks';
|
|
3
2
|
import { ChevronDown, ChevronRight } from 'lucide-preact';
|
|
4
|
-
import { useFetcher } from '@/client/lib/router/fetcher';
|
|
5
3
|
import { FileIcon } from '@/client/components/common/file-icon';
|
|
6
|
-
import { FileContextMenu, FileDeleteModal, FileHistoryModal, FileRenameModal } from '@/client/components/workspace/file-explorer/Modals';
|
|
4
|
+
import { FileContextMenu, FileCreateModal, FileDeleteModal, FileHistoryModal, FileRenameModal } from '@/client/components/workspace/file-explorer/Modals';
|
|
7
5
|
import { getGitStatusInfo, type FolderGitStatusInfo } from '@/shared/lib/fs/git-status';
|
|
8
|
-
import {
|
|
9
|
-
import { copyToClipboard } from '@/client/hooks/ui/clipboard';
|
|
10
|
-
import type { GitChange } from '@/shared/types/git';
|
|
6
|
+
import { useFileActions } from '@/client/hooks/workspace/file-tree-actions';
|
|
11
7
|
import { useOnClickOutside } from '@/client/hooks/ui/on-click-outside';
|
|
8
|
+
import type { GitChange } from '@/shared/types/git';
|
|
12
9
|
|
|
13
10
|
interface FileTreeItemProps {
|
|
14
11
|
file: any;
|
|
@@ -41,16 +38,8 @@ export function FileTreeItem({
|
|
|
41
38
|
const isFolder = file.type === 'folder';
|
|
42
39
|
const [isOpen, setIsOpen] = useState(false);
|
|
43
40
|
const [isLoadingChildren, setIsLoadingChildren] = useState(false);
|
|
44
|
-
|
|
41
|
+
/** Portals need a real document; the first client render is the gate. */
|
|
45
42
|
const [mounted, setMounted] = useState(false);
|
|
46
|
-
|
|
47
|
-
const [contextMenu, setContextMenu] = useState<{ x: number, y: number } | null>(null);
|
|
48
|
-
const contextMenuRef = useRef<HTMLDivElement>(null);
|
|
49
|
-
const [showRenameModal, setShowRenameModal] = useState(false);
|
|
50
|
-
const [showDeleteModal, setShowDeleteModal] = useState(false);
|
|
51
|
-
const [showHistoryModal, setShowHistoryModal] = useState(false);
|
|
52
|
-
const [renameValue, setRenameValue] = useState(file.path);
|
|
53
|
-
|
|
54
43
|
const children = Array.isArray(file.children) ? file.children : [];
|
|
55
44
|
|
|
56
45
|
const normalizedPath = (file.path || '').replace(/^\/+/, '');
|
|
@@ -80,19 +69,13 @@ export function FileTreeItem({
|
|
|
80
69
|
}
|
|
81
70
|
}, [isFolder, actualIsOpen, file.children, file.path, onLoadChildren]);
|
|
82
71
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
setShowDeleteModal(false);
|
|
91
|
-
onActionComplete();
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}, [actionFetcher.state, actionFetcher.data]);
|
|
72
|
+
const actions = useFileActions({
|
|
73
|
+
file: { path: file.path, name: file.name, basePath, rootPath, repo },
|
|
74
|
+
diff: { staged: gitChange?.staged || (gitChange?.status ? gitChange.status[0] !== ' ' && gitChange.status[0] !== '?' : false), status: gitStatusInfo?.charStatus },
|
|
75
|
+
onOpenFile,
|
|
76
|
+
onActionComplete,
|
|
77
|
+
});
|
|
78
|
+
useOnClickOutside(actions.contextMenuRef, actions.closeContextMenu);
|
|
96
79
|
|
|
97
80
|
const handleToggle = (e: globalThis.MouseEvent) => {
|
|
98
81
|
e.stopPropagation();
|
|
@@ -121,72 +104,7 @@ export function FileTreeItem({
|
|
|
121
104
|
const handleContextMenu = (e: globalThis.MouseEvent) => {
|
|
122
105
|
e.preventDefault();
|
|
123
106
|
e.stopPropagation();
|
|
124
|
-
|
|
125
|
-
};
|
|
126
|
-
|
|
127
|
-
const submitAction = (formData: FormData) => {
|
|
128
|
-
if (repo && repo !== '.') formData.append('repo', repo);
|
|
129
|
-
if (rootPath) formData.append('root', rootPath);
|
|
130
|
-
actionFetcher.submit(formData, { method: 'post', action: '/api/fs/action' });
|
|
131
|
-
};
|
|
132
|
-
|
|
133
|
-
const handleAction = (actionType: string) => {
|
|
134
|
-
setContextMenu(null);
|
|
135
|
-
if (actionType === 'view') {
|
|
136
|
-
if (onOpenFile) {
|
|
137
|
-
onOpenFile({
|
|
138
|
-
...file,
|
|
139
|
-
root: rootPath,
|
|
140
|
-
repo: repo || '.',
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
} else if (actionType === 'diff') {
|
|
144
|
-
window.dispatchEvent(new CustomEvent('omp:open-diff', {
|
|
145
|
-
detail: {
|
|
146
|
-
file: file.path,
|
|
147
|
-
staged: gitChange?.staged || (gitChange?.status && gitChange.status[0] !== ' ' && gitChange.status[0] !== '?'),
|
|
148
|
-
status: gitStatusInfo?.charStatus || 'M',
|
|
149
|
-
repo: repo || '.',
|
|
150
|
-
root: rootPath,
|
|
151
|
-
}
|
|
152
|
-
}));
|
|
153
|
-
} else if (actionType === 'explorer') {
|
|
154
|
-
const fd = new FormData();
|
|
155
|
-
fd.append('actionType', 'open_explorer');
|
|
156
|
-
fd.append('path', file.path);
|
|
157
|
-
submitAction(fd);
|
|
158
|
-
} else if (actionType === 'copy_path') {
|
|
159
|
-
void copyToClipboard(toAbsolutePath(basePath, file.path));
|
|
160
|
-
} else if (actionType === 'copy_relative') {
|
|
161
|
-
void copyToClipboard(file.path);
|
|
162
|
-
} else if (actionType === 'history') {
|
|
163
|
-
setShowHistoryModal(true);
|
|
164
|
-
const fd = new FormData();
|
|
165
|
-
fd.append('actionType', 'git_history');
|
|
166
|
-
fd.append('path', file.path);
|
|
167
|
-
submitAction(fd);
|
|
168
|
-
} else if (actionType === 'rename') {
|
|
169
|
-
setRenameValue(file.path);
|
|
170
|
-
setShowRenameModal(true);
|
|
171
|
-
} else if (actionType === 'delete') {
|
|
172
|
-
setShowDeleteModal(true);
|
|
173
|
-
}
|
|
174
|
-
};
|
|
175
|
-
|
|
176
|
-
const submitRename = (e: FormEvent) => {
|
|
177
|
-
e.preventDefault();
|
|
178
|
-
const fd = new FormData();
|
|
179
|
-
fd.append('actionType', 'rename');
|
|
180
|
-
fd.append('path', file.path);
|
|
181
|
-
fd.append('newPath', renameValue);
|
|
182
|
-
submitAction(fd);
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
const submitDelete = () => {
|
|
186
|
-
const fd = new FormData();
|
|
187
|
-
fd.append('actionType', 'delete');
|
|
188
|
-
fd.append('path', file.path);
|
|
189
|
-
submitAction(fd);
|
|
107
|
+
actions.openContextMenu(e.clientX, e.clientY);
|
|
190
108
|
};
|
|
191
109
|
|
|
192
110
|
return (
|
|
@@ -255,44 +173,56 @@ export function FileTreeItem({
|
|
|
255
173
|
</div>
|
|
256
174
|
)}
|
|
257
175
|
|
|
258
|
-
{mounted && contextMenu && (
|
|
176
|
+
{mounted && actions.contextMenu && (
|
|
259
177
|
<FileContextMenu
|
|
260
|
-
x={contextMenu.x}
|
|
261
|
-
y={contextMenu.y}
|
|
178
|
+
x={actions.contextMenu.x}
|
|
179
|
+
y={actions.contextMenu.y}
|
|
262
180
|
isFolder={isFolder}
|
|
263
181
|
hasGitStatus={hasGitStatus}
|
|
264
|
-
onAction={handleAction}
|
|
182
|
+
onAction={actions.handleAction}
|
|
183
|
+
/>
|
|
184
|
+
)}
|
|
185
|
+
|
|
186
|
+
{mounted && actions.showCreateModal && (
|
|
187
|
+
<FileCreateModal
|
|
188
|
+
folderPath={file.path}
|
|
189
|
+
value={actions.createName}
|
|
190
|
+
error={actions.createError}
|
|
191
|
+
isLoading={actions.busy}
|
|
192
|
+
onChange={actions.setCreateName}
|
|
193
|
+
onCancel={actions.closeCreate}
|
|
194
|
+
onSubmit={actions.submitCreate}
|
|
265
195
|
/>
|
|
266
196
|
)}
|
|
267
197
|
|
|
268
|
-
{mounted && showDeleteModal && (
|
|
198
|
+
{mounted && actions.showDeleteModal && (
|
|
269
199
|
<FileDeleteModal
|
|
270
200
|
fileName={file.name}
|
|
271
201
|
isFolder={isFolder}
|
|
272
|
-
isLoading={
|
|
273
|
-
onCancel={
|
|
274
|
-
onConfirm={submitDelete}
|
|
202
|
+
isLoading={actions.busy}
|
|
203
|
+
onCancel={actions.closeDelete}
|
|
204
|
+
onConfirm={actions.submitDelete}
|
|
275
205
|
/>
|
|
276
206
|
)}
|
|
277
207
|
|
|
278
|
-
{mounted && showRenameModal && (
|
|
208
|
+
{mounted && actions.showRenameModal && (
|
|
279
209
|
<FileRenameModal
|
|
280
210
|
isFolder={isFolder}
|
|
281
|
-
renameValue={renameValue}
|
|
211
|
+
renameValue={actions.renameValue}
|
|
282
212
|
originalPath={file.path}
|
|
283
|
-
isLoading={
|
|
284
|
-
onChange={setRenameValue}
|
|
285
|
-
onCancel={
|
|
286
|
-
onSubmit={submitRename}
|
|
213
|
+
isLoading={actions.busy}
|
|
214
|
+
onChange={actions.setRenameValue}
|
|
215
|
+
onCancel={actions.closeRename}
|
|
216
|
+
onSubmit={actions.submitRename}
|
|
287
217
|
/>
|
|
288
218
|
)}
|
|
289
219
|
|
|
290
|
-
{mounted && showHistoryModal && (
|
|
220
|
+
{mounted && actions.showHistoryModal && (
|
|
291
221
|
<FileHistoryModal
|
|
292
222
|
filePath={file.path}
|
|
293
|
-
fetcherState={
|
|
294
|
-
fetcherData={
|
|
295
|
-
onClose={
|
|
223
|
+
fetcherState={actions.fetcherState}
|
|
224
|
+
fetcherData={actions.fetcherData}
|
|
225
|
+
onClose={actions.closeHistory}
|
|
296
226
|
/>
|
|
297
227
|
)}
|
|
298
228
|
</div>
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import type { TargetedMouseEvent } from 'preact';
|
|
2
|
-
import {
|
|
2
|
+
import { Brain, Check, Command, GripVertical, Star } from 'lucide-preact';
|
|
3
3
|
import type { AIModelOption } from '@/shared/types';
|
|
4
4
|
import { formatContextWindow } from '@/shared/lib/code/format';
|
|
5
|
+
import { ProviderIcon } from '@/client/components/common/provider-icon';
|
|
5
6
|
|
|
6
7
|
interface ModelDropdownItemProps {
|
|
7
8
|
model: AIModelOption;
|
|
@@ -35,28 +36,17 @@ export function ModelDropdownItem({
|
|
|
35
36
|
);
|
|
36
37
|
}
|
|
37
38
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return <Boxes size={13} className="text-emerald-500 flex-shrink-0" />;
|
|
50
|
-
case 'google':
|
|
51
|
-
case 'gemini':
|
|
52
|
-
return <Sparkles size={13} className="text-blue-400 flex-shrink-0" />;
|
|
53
|
-
case 'opencode':
|
|
54
|
-
return <Zap size={13} className="text-violet-400 flex-shrink-0" />;
|
|
55
|
-
case 'mistral':
|
|
56
|
-
return <Globe size={13} className="text-rose-400 flex-shrink-0" />;
|
|
57
|
-
default:
|
|
58
|
-
return <Sparkles size={13} className="text-ink/60 flex-shrink-0" />;
|
|
59
|
-
}
|
|
39
|
+
// The provider's own mark, keyed on the catalog's `providerIcon` first so a
|
|
40
|
+
// legacy key still resolves; unknown providers fall back to their initials.
|
|
41
|
+
return (
|
|
42
|
+
<ProviderIcon
|
|
43
|
+
icon={model.providerIcon}
|
|
44
|
+
slug={model.provider}
|
|
45
|
+
name={model.provider}
|
|
46
|
+
size={13}
|
|
47
|
+
className="text-ink/70"
|
|
48
|
+
/>
|
|
49
|
+
);
|
|
60
50
|
};
|
|
61
51
|
|
|
62
52
|
return (
|
|
@@ -115,7 +115,7 @@ export function ModelDropdownPanel({
|
|
|
115
115
|
key={provider}
|
|
116
116
|
id={provider}
|
|
117
117
|
title={provider}
|
|
118
|
-
iconType=
|
|
118
|
+
iconType="provider"
|
|
119
119
|
models={pModels}
|
|
120
120
|
isCollapsed={!!collapsedSections[provider]}
|
|
121
121
|
onToggleCollapse={onToggleCollapse}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { TargetedMouseEvent } from 'preact';
|
|
2
|
-
import { ChevronDown, ChevronUp,
|
|
2
|
+
import { ChevronDown, ChevronUp, History, Star } from 'lucide-preact';
|
|
3
3
|
import type { AIModelOption } from '@/shared/types';
|
|
4
4
|
import { ModelDropdownItem } from '@/client/components/workspace/model-dropdown/Item';
|
|
5
|
+
import { ProviderIcon } from '@/client/components/common/provider-icon';
|
|
5
6
|
import { modelKey } from '@/shared/lib/models/identity';
|
|
6
7
|
|
|
7
8
|
interface ModelDropdownSectionProps {
|
|
8
9
|
id: string;
|
|
9
10
|
title: string;
|
|
10
|
-
iconType?: 'star' | 'recent' | 'provider'
|
|
11
|
+
iconType?: 'star' | 'recent' | 'provider';
|
|
11
12
|
models: AIModelOption[];
|
|
12
13
|
isCollapsed: boolean;
|
|
13
14
|
onToggleCollapse: (id: string) => void;
|
|
@@ -49,8 +50,16 @@ export function ModelDropdownSection({
|
|
|
49
50
|
<div className="flex items-center space-x-1.5">
|
|
50
51
|
{iconType === 'star' && <Star size={12} className="fill-sky-400 text-sky-400" />}
|
|
51
52
|
{iconType === 'recent' && <History size={12} className="text-ink/60" />}
|
|
52
|
-
{iconType === '
|
|
53
|
-
|
|
53
|
+
{iconType === 'provider' && (
|
|
54
|
+
// The section's own provider, taken from a row rather than the
|
|
55
|
+
// uppercased group key, so the mark resolves off the real slug.
|
|
56
|
+
<ProviderIcon
|
|
57
|
+
icon={models[0]?.providerIcon}
|
|
58
|
+
slug={models[0]?.provider ?? id}
|
|
59
|
+
name={models[0]?.provider ?? id}
|
|
60
|
+
size={12}
|
|
61
|
+
/>
|
|
62
|
+
)}
|
|
54
63
|
<span>{title}</span>
|
|
55
64
|
</div>
|
|
56
65
|
{isCollapsed ? (
|
|
@@ -5,6 +5,7 @@ import type { AIModelOption, ModelPreferences } from '@/shared/types';
|
|
|
5
5
|
import { useOnClickOutside } from '@/client/hooks/ui/on-click-outside';
|
|
6
6
|
import { invalidateModelsCache } from '@/shared/lib/models/client';
|
|
7
7
|
import { ModelDropdownPanel } from '@/client/components/workspace/model-dropdown/Panel';
|
|
8
|
+
import { ProviderIcon } from '@/client/components/common/provider-icon';
|
|
8
9
|
import { useModelCatalog } from '@/client/components/workspace/model-dropdown/use-catalog';
|
|
9
10
|
import { buildPickerGroups } from '@/client/components/workspace/model-dropdown/groups';
|
|
10
11
|
import { modelKey } from '@/shared/lib/models/identity';
|
|
@@ -229,10 +230,13 @@ export function ModelDropdown({
|
|
|
229
230
|
<span className="text-ink/60 flex-shrink-0">
|
|
230
231
|
{selectedModel.isCmdAgent ? (
|
|
231
232
|
<span className="font-mono text-[10px] font-bold text-ink/70">⌘</span>
|
|
232
|
-
) : selectedModel.provider.toLowerCase() === 'deepseek' ? (
|
|
233
|
-
<span>🐋</span>
|
|
234
233
|
) : (
|
|
235
|
-
<
|
|
234
|
+
<ProviderIcon
|
|
235
|
+
icon={selectedModel.providerIcon}
|
|
236
|
+
slug={selectedModel.provider}
|
|
237
|
+
name={selectedModel.provider}
|
|
238
|
+
size={12}
|
|
239
|
+
/>
|
|
236
240
|
)}
|
|
237
241
|
</span>
|
|
238
242
|
<span className="flex items-center space-x-1 min-w-0 max-w-[280px]">
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* One phase group of the todo panel: a header with the phase's own `closed/total`
|
|
8
|
+
* badge, then its task rows.
|
|
9
|
+
*
|
|
10
|
+
* The collapse state is the panel's (persisted per session), not local: a list
|
|
11
|
+
* the operator collapsed should stay collapsed across a reload, and the panel
|
|
12
|
+
* owns every phase's flag in one map so "collapse all" is one write.
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
import { ChevronDown, ChevronRight } from 'lucide-preact';
|
|
16
|
+
import type { TodoPhase } from '@/shared/types/todo';
|
|
17
|
+
import { closedTaskCount } from '@/shared/lib/chat/todo/progress';
|
|
18
|
+
import { TodoRow } from '@/client/components/workspace/todo-panel/Row';
|
|
19
|
+
|
|
20
|
+
interface TodoPhaseGroupProps {
|
|
21
|
+
phase: TodoPhase;
|
|
22
|
+
index: number;
|
|
23
|
+
collapsed: boolean;
|
|
24
|
+
/** `phase:task` of the row to highlight as current, or null. */
|
|
25
|
+
currentKey: string | null;
|
|
26
|
+
onToggle: () => void;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function TodoPhaseGroup({ phase, index, collapsed, currentKey, onToggle }: TodoPhaseGroupProps) {
|
|
30
|
+
const closed = closedTaskCount(phase);
|
|
31
|
+
const isComplete = phase.tasks.length > 0 && closed === phase.tasks.length;
|
|
32
|
+
|
|
33
|
+
return (
|
|
34
|
+
<div className="overflow-hidden rounded-lg border border-ink/10 bg-paper">
|
|
35
|
+
<button
|
|
36
|
+
type="button"
|
|
37
|
+
onClick={onToggle}
|
|
38
|
+
aria-expanded={!collapsed}
|
|
39
|
+
className="flex w-full items-center gap-1.5 border-b border-ink/8 bg-canvas/40 px-2.5 py-1.5 text-left hover:bg-ink/5"
|
|
40
|
+
>
|
|
41
|
+
{collapsed ? (
|
|
42
|
+
<ChevronRight size={12} className="shrink-0 text-ink/40" />
|
|
43
|
+
) : (
|
|
44
|
+
<ChevronDown size={12} className="shrink-0 text-ink/40" />
|
|
45
|
+
)}
|
|
46
|
+
<span
|
|
47
|
+
className={`min-w-0 flex-1 truncate text-[10px] font-semibold uppercase tracking-wider ${
|
|
48
|
+
isComplete ? 'text-ink/35' : 'text-ink/60'
|
|
49
|
+
}`}
|
|
50
|
+
title={phase.name}
|
|
51
|
+
>
|
|
52
|
+
{index + 1}. {phase.name}
|
|
53
|
+
</span>
|
|
54
|
+
<span className="shrink-0 font-mono text-[9.5px] text-ink/40">
|
|
55
|
+
{closed}/{phase.tasks.length}
|
|
56
|
+
</span>
|
|
57
|
+
</button>
|
|
58
|
+
|
|
59
|
+
{!collapsed && (
|
|
60
|
+
<div className="divide-y divide-ink/[0.05] p-0.5">
|
|
61
|
+
{phase.tasks.length === 0 ? (
|
|
62
|
+
<div className="px-2.5 py-1.5 text-[11px] text-ink/40">No tasks in this phase</div>
|
|
63
|
+
) : (
|
|
64
|
+
phase.tasks.map((task, taskIndex) => (
|
|
65
|
+
<TodoRow
|
|
66
|
+
key={`${taskIndex}-${task.content}`}
|
|
67
|
+
task={task}
|
|
68
|
+
current={currentKey === `${index}:${taskIndex}`}
|
|
69
|
+
/>
|
|
70
|
+
))
|
|
71
|
+
)}
|
|
72
|
+
</div>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
);
|
|
76
|
+
}
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* One task row of the todo panel.
|
|
8
|
+
*
|
|
9
|
+
* The five statuses render through one component because they are one axis of
|
|
10
|
+
* the same row — icon, tone, and whether the text is struck through — and
|
|
11
|
+
* splitting them across branches is how a `blocked` task ends up drawn as an
|
|
12
|
+
* ordinary pending one. A blocked task also carries its own note (`blocker`),
|
|
13
|
+
* which is the only place omp records WHY it is waiting.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { Ban, CheckCircle2, Circle, Loader2, OctagonAlert } from 'lucide-preact';
|
|
17
|
+
import type { TodoItem } from '@/shared/types/todo';
|
|
18
|
+
import { taskNote, todoStatusVisual } from '@/shared/lib/chat/todo/progress';
|
|
19
|
+
|
|
20
|
+
function StatusIcon({ status, className }: { status: TodoItem['status']; className: string }) {
|
|
21
|
+
switch (status) {
|
|
22
|
+
case 'completed':
|
|
23
|
+
return <CheckCircle2 size={13} className={className} />;
|
|
24
|
+
case 'in_progress':
|
|
25
|
+
return <Loader2 size={13} className={`${className} animate-spin`} />;
|
|
26
|
+
case 'abandoned':
|
|
27
|
+
return <Ban size={13} className={className} />;
|
|
28
|
+
case 'blocked':
|
|
29
|
+
return <OctagonAlert size={13} className={className} />;
|
|
30
|
+
default:
|
|
31
|
+
return <Circle size={13} className={className} />;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function TodoRow({ task, current }: { task: TodoItem; current: boolean }) {
|
|
36
|
+
const visual = todoStatusVisual(task.status);
|
|
37
|
+
const note = taskNote(task);
|
|
38
|
+
|
|
39
|
+
return (
|
|
40
|
+
<div
|
|
41
|
+
className={`flex items-start gap-2.5 px-2.5 py-1.5 transition-colors ${
|
|
42
|
+
current ? 'bg-ink/[0.04]' : ''
|
|
43
|
+
}`}
|
|
44
|
+
>
|
|
45
|
+
<StatusIcon status={task.status} className={`mt-[2px] shrink-0 ${visual.tone}`} />
|
|
46
|
+
<div className="min-w-0 flex-1">
|
|
47
|
+
<div
|
|
48
|
+
className={`text-[11.5px] leading-snug ${
|
|
49
|
+
visual.closed
|
|
50
|
+
? 'text-ink/40 line-through'
|
|
51
|
+
: visual.active
|
|
52
|
+
? 'font-medium text-ink'
|
|
53
|
+
: 'text-ink/75'
|
|
54
|
+
}`}
|
|
55
|
+
>
|
|
56
|
+
{task.content}
|
|
57
|
+
</div>
|
|
58
|
+
{note && (
|
|
59
|
+
<div className="mt-0.5 text-[10.5px] leading-snug text-warning/90 break-words">{note}</div>
|
|
60
|
+
)}
|
|
61
|
+
</div>
|
|
62
|
+
{visual.active && (
|
|
63
|
+
<span className="shrink-0 rounded bg-ink/8 px-1.5 py-0.5 font-mono text-[9px] uppercase tracking-wider text-ink/70">
|
|
64
|
+
Now
|
|
65
|
+
</span>
|
|
66
|
+
)}
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @license
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Right-panel Todo view: the session's live todo list, as oh-my-pi itself
|
|
8
|
+
* would resume with it.
|
|
9
|
+
*
|
|
10
|
+
* The list is omp's, read from the session transcript (see
|
|
11
|
+
* `@/server/lib/omp/session/todos`), so this panel adds presentation only —
|
|
12
|
+
* phase groups, a progress bar, and the current task called out. It deliberately
|
|
13
|
+
* does NOT recompute or cache a list of its own: a chamber-side copy would drift
|
|
14
|
+
* from the one the agent rehydrates, and a plan panel that disagrees with the
|
|
15
|
+
* agent is worse than no panel.
|
|
16
|
+
*
|
|
17
|
+
* Empty states are distinguished, because they mean different things: no
|
|
18
|
+
* session selected, a session whose transcript has no todo snapshot at all
|
|
19
|
+
* (the agent never used the tool here), and a snapshot whose every task is
|
|
20
|
+
* closed.
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import { useMemo } from 'preact/hooks';
|
|
24
|
+
import { AlertCircle, ListTodo, RefreshCw } from 'lucide-preact';
|
|
25
|
+
import { useSearchParams } from '@/client/lib/router/search-params';
|
|
26
|
+
import { useSessionState } from '@/client/hooks/workspace/session-state';
|
|
27
|
+
import { useSessionTodos } from '@/client/hooks/workspace/session-todos';
|
|
28
|
+
import { TodoPhaseGroup } from '@/client/components/workspace/todo-panel/PhaseGroup';
|
|
29
|
+
import { currentTaskLocation, todoProgressLabel, todoProgressPercent } from '@/shared/lib/chat/todo/progress';
|
|
30
|
+
|
|
31
|
+
interface TodoPanelProps {
|
|
32
|
+
className?: string;
|
|
33
|
+
/**
|
|
34
|
+
* False while the view is not the one on screen: pauses the poll and the
|
|
35
|
+
* event-driven re-read.
|
|
36
|
+
*
|
|
37
|
+
* Deliberately NOT the `enabled` gate the workspace-scoped panels take. That
|
|
38
|
+
* flag means "the active session's cwd resolves to a registered workspace
|
|
39
|
+
* folder", which files/search/git/terminal genuinely need. A todo list does
|
|
40
|
+
* not: it lives in the session's own transcript, so a session running outside
|
|
41
|
+
* every registered workspace still has one — gating on the workspace showed
|
|
42
|
+
* "No session selected" for a session that was open and had a full list.
|
|
43
|
+
*/
|
|
44
|
+
active?: boolean;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function TodoPanel({ className = '', active = true }: TodoPanelProps) {
|
|
48
|
+
const [searchParams] = useSearchParams();
|
|
49
|
+
const sessionId = searchParams.get('sessionId');
|
|
50
|
+
const [collapsedPhases, setCollapsedPhases] = useSessionState<Record<string, boolean>>(
|
|
51
|
+
'todo.collapsedPhases',
|
|
52
|
+
{},
|
|
53
|
+
);
|
|
54
|
+
const { data, isLoading, error, reload } = useSessionTodos(sessionId, active);
|
|
55
|
+
|
|
56
|
+
const snapshot = data?.snapshot ?? null;
|
|
57
|
+
const progress = data?.progress ?? null;
|
|
58
|
+
|
|
59
|
+
// `phase:task` of the row to highlight. Derived from the snapshot rather than
|
|
60
|
+
// stored, so it follows the list as it moves.
|
|
61
|
+
const currentKey = useMemo(() => {
|
|
62
|
+
const location = currentTaskLocation(snapshot?.phases ?? []);
|
|
63
|
+
return location ? `${location.phase}:${location.task}` : null;
|
|
64
|
+
}, [snapshot]);
|
|
65
|
+
|
|
66
|
+
if (!sessionId) {
|
|
67
|
+
return (
|
|
68
|
+
<div className={`flex h-full flex-col items-center justify-center bg-paper text-ink/40 ${className}`}>
|
|
69
|
+
<span className="text-xs font-mono">No session selected</span>
|
|
70
|
+
</div>
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<div className={`flex h-full w-full min-w-0 flex-col overflow-hidden bg-paper text-ink ${className}`}>
|
|
76
|
+
<div className="flex flex-shrink-0 items-center justify-between gap-3 border-b border-ink/10 px-3.5 py-2.5">
|
|
77
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
78
|
+
<h2 className="text-xs font-semibold uppercase tracking-wider text-ink">Todos</h2>
|
|
79
|
+
{snapshot && (
|
|
80
|
+
<span className="truncate font-mono text-[10.5px] text-ink/50" title={todoProgressLabel(progress!)}>
|
|
81
|
+
{todoProgressLabel(progress!)}
|
|
82
|
+
</span>
|
|
83
|
+
)}
|
|
84
|
+
{data?.isMock && (
|
|
85
|
+
<span className="flex-shrink-0 rounded border border-ink/15 bg-ink/5 px-1.5 py-0.5 font-mono text-[10px] uppercase tracking-wider text-ink/60">
|
|
86
|
+
Mock
|
|
87
|
+
</span>
|
|
88
|
+
)}
|
|
89
|
+
</div>
|
|
90
|
+
<button
|
|
91
|
+
type="button"
|
|
92
|
+
onClick={reload}
|
|
93
|
+
disabled={isLoading}
|
|
94
|
+
title="Refresh todos"
|
|
95
|
+
aria-label="Refresh todos"
|
|
96
|
+
className="flex-shrink-0 rounded-md border border-ink/15 bg-paper p-1.5 text-ink/80 transition-colors hover:bg-ink/5 disabled:opacity-50"
|
|
97
|
+
>
|
|
98
|
+
<RefreshCw size={12} className={isLoading ? 'animate-spin text-ink' : ''} />
|
|
99
|
+
</button>
|
|
100
|
+
</div>
|
|
101
|
+
|
|
102
|
+
{progress && progress.total > 0 && (
|
|
103
|
+
<div className="flex-shrink-0 border-b border-ink/10 px-3.5 py-2.5">
|
|
104
|
+
<div className="h-1.5 overflow-hidden rounded-full bg-ink/8">
|
|
105
|
+
<div
|
|
106
|
+
className="h-full rounded-full bg-success transition-all duration-300"
|
|
107
|
+
style={{ width: `${todoProgressPercent(progress)}%` }}
|
|
108
|
+
/>
|
|
109
|
+
</div>
|
|
110
|
+
</div>
|
|
111
|
+
)}
|
|
112
|
+
|
|
113
|
+
<div className="min-h-0 flex-1 space-y-2 overflow-y-auto scrollbar-overlay-container scrollbar-overlay-static p-3.5">
|
|
114
|
+
{error && (
|
|
115
|
+
<div className="flex items-start gap-2 rounded-lg border border-error/30 bg-error/5 px-3 py-2 text-[11px] text-error">
|
|
116
|
+
<AlertCircle size={13} className="mt-[1px] shrink-0" />
|
|
117
|
+
<span className="min-w-0 break-words">{error}</span>
|
|
118
|
+
</div>
|
|
119
|
+
)}
|
|
120
|
+
|
|
121
|
+
{!snapshot ? (
|
|
122
|
+
<div className="flex flex-col items-center justify-center gap-2 py-12 text-center text-ink/40">
|
|
123
|
+
<ListTodo size={20} />
|
|
124
|
+
<p className="max-w-[240px] text-[11.5px] leading-relaxed">
|
|
125
|
+
No todo list in this session. The agent writes one with the <span className="font-mono">todo</span> tool
|
|
126
|
+
when a task is worth planning.
|
|
127
|
+
</p>
|
|
128
|
+
</div>
|
|
129
|
+
) : (
|
|
130
|
+
<>
|
|
131
|
+
{snapshot.phases.map((phase, index) => (
|
|
132
|
+
<TodoPhaseGroup
|
|
133
|
+
key={`${index}-${phase.name}`}
|
|
134
|
+
phase={phase}
|
|
135
|
+
index={index}
|
|
136
|
+
collapsed={Boolean(collapsedPhases[String(index)])}
|
|
137
|
+
currentKey={currentKey}
|
|
138
|
+
onToggle={() =>
|
|
139
|
+
setCollapsedPhases((prev) => ({ ...prev, [String(index)]: !prev[String(index)] }))
|
|
140
|
+
}
|
|
141
|
+
/>
|
|
142
|
+
))}
|
|
143
|
+
|
|
144
|
+
<p className="pt-1 text-[10.5px] leading-relaxed text-ink/35">
|
|
145
|
+
Read from this session's transcript
|
|
146
|
+
{snapshot.updatedAt ? ` · updated ${formatStamp(snapshot.updatedAt)}` : ''}
|
|
147
|
+
{snapshot.source === 'custom' ? ' · edited by hand' : ''}.
|
|
148
|
+
</p>
|
|
149
|
+
</>
|
|
150
|
+
)}
|
|
151
|
+
</div>
|
|
152
|
+
</div>
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
/** Local time of a snapshot stamp; the raw ISO string when it cannot be read. */
|
|
157
|
+
function formatStamp(value: string): string {
|
|
158
|
+
const date = new Date(value);
|
|
159
|
+
if (Number.isNaN(date.getTime())) return value;
|
|
160
|
+
return date.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' });
|
|
161
|
+
}
|