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
|
@@ -61,3 +61,39 @@ export function formatAmount(value: string): string {
|
|
|
61
61
|
if (value.trim() === '' || !Number.isFinite(parsed)) return value;
|
|
62
62
|
return formatNumber(parsed);
|
|
63
63
|
}
|
|
64
|
+
|
|
65
|
+
const usdFormatter = new Intl.NumberFormat('id-ID', {
|
|
66
|
+
minimumFractionDigits: 2,
|
|
67
|
+
maximumFractionDigits: 2,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
/** USD amounts from provider reports and local cost records. */
|
|
71
|
+
export function formatUsd(value: number): string {
|
|
72
|
+
if (!Number.isFinite(value)) return '$0,00';
|
|
73
|
+
return `$${usdFormatter.format(value)}`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Render one quota amount in its own unit. `percent` amounts arrive as 0..100
|
|
78
|
+
* (omp's `used`/`limit` for percent units), everything else as a raw count.
|
|
79
|
+
*/
|
|
80
|
+
export function formatUsageAmount(value: number, unit: string): string {
|
|
81
|
+
if (!Number.isFinite(value)) return '—';
|
|
82
|
+
switch (unit) {
|
|
83
|
+
case 'percent':
|
|
84
|
+
return `${new Intl.NumberFormat('id-ID', { maximumFractionDigits: 1 }).format(value)}%`;
|
|
85
|
+
case 'usd':
|
|
86
|
+
return formatUsd(value);
|
|
87
|
+
case 'tokens':
|
|
88
|
+
case 'bytes':
|
|
89
|
+
return formatCompactTokens(value);
|
|
90
|
+
default:
|
|
91
|
+
return formatNumber(value);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/** Fraction (0..1) rendered as a whole-percent string. */
|
|
96
|
+
export function formatFractionPercent(fraction: number): string {
|
|
97
|
+
if (!Number.isFinite(fraction)) return '0%';
|
|
98
|
+
return `${new Intl.NumberFormat('id-ID', { maximumFractionDigits: 1 }).format(fraction * 100)}%`;
|
|
99
|
+
}
|
|
@@ -1,45 +1,69 @@
|
|
|
1
|
-
import type { UsageReport } from '@/shared/types';
|
|
2
|
-
import { formatAmount } from '@/client/components/settings/categories/usage-settings/format';
|
|
1
|
+
import type { UsageLimitWindow, UsageProviderSummary, UsageReport } from '@/shared/types';
|
|
2
|
+
import { formatAmount, formatUsd } from '@/client/components/settings/categories/usage-settings/format';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
|
|
4
|
+
/**
|
|
5
|
+
* Providers surfaced in the Settings → Usage sidebar and the Usage right panel.
|
|
6
|
+
* The id is the provider slug, so the set is open-ended: any provider omp can
|
|
7
|
+
* authenticate shows up here without a code change.
|
|
8
|
+
*/
|
|
9
|
+
export type UsageProviderId = string;
|
|
6
10
|
|
|
7
11
|
export interface ProviderEntry {
|
|
8
12
|
id: UsageProviderId;
|
|
9
13
|
name: string;
|
|
10
|
-
|
|
14
|
+
/** Provider-side failure, already human-readable. */
|
|
11
15
|
error?: string;
|
|
16
|
+
/** Short status line under the name. */
|
|
12
17
|
hint: string;
|
|
18
|
+
/** Provider-reported quota windows; empty when it exposes no usage endpoint. */
|
|
19
|
+
limits: UsageLimitWindow[];
|
|
20
|
+
/** Whether omp ships a usage adapter for this provider. */
|
|
21
|
+
tracked: boolean;
|
|
22
|
+
/** omp tracks this provider but its endpoint produced nothing. */
|
|
23
|
+
limitsUnavailable?: boolean;
|
|
24
|
+
summary: UsageProviderSummary;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** Compact one-line summary of the most-used quota window. */
|
|
28
|
+
function limitHint(limits: UsageLimitWindow[]): string | undefined {
|
|
29
|
+
const used = limits.filter((limit) => limit.usedFraction !== undefined);
|
|
30
|
+
if (used.length === 0) {
|
|
31
|
+
// A credit balance has no limit to take a fraction of; the remaining
|
|
32
|
+
// amount IS the whole story.
|
|
33
|
+
const credit = limits.find((limit) => limit.unit === 'usd' && limit.remaining !== undefined);
|
|
34
|
+
if (credit) return `${formatUsd(credit.remaining ?? 0)} left`;
|
|
35
|
+
return limits[0]?.label;
|
|
36
|
+
}
|
|
37
|
+
const binding = used.reduce((worst, limit) =>
|
|
38
|
+
(limit.usedFraction ?? 0) > (worst.usedFraction ?? 0) ? limit : worst,
|
|
39
|
+
);
|
|
40
|
+
const percent = Math.round((binding.usedFraction ?? 0) * 100);
|
|
41
|
+
return binding.windowLabel ? `${percent}% · ${binding.windowLabel}` : `${percent}% used`;
|
|
13
42
|
}
|
|
14
43
|
|
|
15
44
|
/** Derives the provider list (status + hint) from a usage report. */
|
|
16
45
|
export function buildProviders(report: UsageReport): ProviderEntry[] {
|
|
17
|
-
|
|
18
|
-
|
|
46
|
+
return report.providers.map((summary) => ({
|
|
47
|
+
id: summary.id,
|
|
48
|
+
name: summary.name,
|
|
49
|
+
error: summary.error,
|
|
50
|
+
hint: providerHint(summary),
|
|
51
|
+
limits: summary.limits,
|
|
52
|
+
tracked: summary.tracked,
|
|
53
|
+
limitsUnavailable: summary.limitsUnavailable,
|
|
54
|
+
summary,
|
|
55
|
+
}));
|
|
56
|
+
}
|
|
19
57
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
hint: kenariBalance ?? 'Wallet & quota',
|
|
27
|
-
},
|
|
28
|
-
{
|
|
29
|
-
id: 'deepseek',
|
|
30
|
-
name: 'DeepSeek',
|
|
31
|
-
configured: report.deepseek.configured,
|
|
32
|
-
error: report.deepseek.error,
|
|
33
|
-
hint: deepseekEntry
|
|
34
|
-
? `${deepseekEntry.currency} ${formatAmount(deepseekEntry.totalBalance)}`
|
|
35
|
-
: 'Prepaid balance',
|
|
36
|
-
},
|
|
37
|
-
];
|
|
58
|
+
function providerHint(summary: UsageProviderSummary): string {
|
|
59
|
+
const plan = summary.kenari?.quota?.planName;
|
|
60
|
+
if (plan) return plan;
|
|
61
|
+
const balance = summary.deepseek?.balance?.entries[0];
|
|
62
|
+
if (balance) return `${balance.currency} ${formatAmount(balance.totalBalance)}`;
|
|
63
|
+
return limitHint(summary.limits) ?? 'No quota reported';
|
|
38
64
|
}
|
|
39
65
|
|
|
40
66
|
/** Human-readable connection state for a provider entry. */
|
|
41
67
|
export function statusLabel(provider: ProviderEntry): string {
|
|
42
|
-
|
|
43
|
-
if (provider.error) return 'Error';
|
|
44
|
-
return 'Connected';
|
|
68
|
+
return provider.error ? 'Error' : 'Connected';
|
|
45
69
|
}
|
|
@@ -1,20 +1,17 @@
|
|
|
1
1
|
import { Globe, RotateCw } from 'lucide-preact';
|
|
2
2
|
import { BrowserToolbar, ICON_BUTTON } from '@/client/components/workspace/browser-toolbar/index';
|
|
3
|
-
import type { BrowserViewStatus
|
|
3
|
+
import type { BrowserViewStatus } from '@/shared/types';
|
|
4
4
|
|
|
5
5
|
interface BrowserAddressBarProps {
|
|
6
6
|
url?: string;
|
|
7
7
|
status: BrowserViewStatus;
|
|
8
|
-
viewportMode: ViewportMode;
|
|
9
8
|
zoomLevel?: number;
|
|
10
9
|
onIncludeInChat: () => void;
|
|
11
10
|
onReconnect: () => void;
|
|
12
11
|
onOpenExternal: () => void;
|
|
13
|
-
onChangeViewport: (mode: ViewportMode) => void;
|
|
14
12
|
onZoomIn: () => void;
|
|
15
13
|
onZoomOut: () => void;
|
|
16
14
|
onResetZoom: () => void;
|
|
17
|
-
onSetZoom: (zoom: number) => void;
|
|
18
15
|
}
|
|
19
16
|
|
|
20
17
|
const STATUS_LABEL: Record<BrowserViewStatus, string> = {
|
|
@@ -32,16 +29,13 @@ const STATUS_LABEL: Record<BrowserViewStatus, string> = {
|
|
|
32
29
|
export function BrowserAddressBar({
|
|
33
30
|
url,
|
|
34
31
|
status,
|
|
35
|
-
viewportMode,
|
|
36
32
|
zoomLevel = 100,
|
|
37
33
|
onIncludeInChat,
|
|
38
34
|
onReconnect,
|
|
39
35
|
onOpenExternal,
|
|
40
|
-
onChangeViewport,
|
|
41
36
|
onZoomIn,
|
|
42
37
|
onZoomOut,
|
|
43
38
|
onResetZoom,
|
|
44
|
-
onSetZoom,
|
|
45
39
|
}: BrowserAddressBarProps) {
|
|
46
40
|
const isLive = status === 'live';
|
|
47
41
|
|
|
@@ -50,13 +44,10 @@ export function BrowserAddressBar({
|
|
|
50
44
|
url={url ?? ''}
|
|
51
45
|
statusDotClass={isLive ? 'bg-success animate-pulse' : 'bg-ink/25'}
|
|
52
46
|
statusLabel={STATUS_LABEL[status]}
|
|
53
|
-
viewportMode={viewportMode}
|
|
54
47
|
zoomLevel={zoomLevel}
|
|
55
|
-
onChangeViewport={onChangeViewport}
|
|
56
48
|
onZoomIn={onZoomIn}
|
|
57
49
|
onZoomOut={onZoomOut}
|
|
58
50
|
onResetZoom={onResetZoom}
|
|
59
|
-
onSetZoom={onSetZoom}
|
|
60
51
|
onIncludeInChat={onIncludeInChat}
|
|
61
52
|
onOpenExternal={onOpenExternal}
|
|
62
53
|
openExternalLabel="Buka di jendela baru"
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Globe, Loader2, Radio } from 'lucide-preact';
|
|
2
2
|
import { ActivityToasts } from '@/client/components/workspace/browser-panel/ActivityToasts';
|
|
3
|
-
import {
|
|
4
|
-
import type { BrowserPanelAction, BrowserTabInfo, BrowserViewStatus, ViewportMode } from '@/shared/types';
|
|
3
|
+
import type { BrowserPanelAction, BrowserTabInfo, BrowserViewStatus } from '@/shared/types';
|
|
5
4
|
|
|
6
5
|
interface BrowserScreencastProps {
|
|
7
6
|
status: BrowserViewStatus;
|
|
@@ -11,7 +10,6 @@ interface BrowserScreencastProps {
|
|
|
11
10
|
frameSrc?: string;
|
|
12
11
|
actions: BrowserPanelAction[];
|
|
13
12
|
onSelectTarget: (targetId: string) => void;
|
|
14
|
-
viewportMode: ViewportMode;
|
|
15
13
|
zoomLevel: number;
|
|
16
14
|
}
|
|
17
15
|
|
|
@@ -38,7 +36,6 @@ export function BrowserScreencast({
|
|
|
38
36
|
frameSrc,
|
|
39
37
|
actions,
|
|
40
38
|
onSelectTarget,
|
|
41
|
-
viewportMode,
|
|
42
39
|
zoomLevel,
|
|
43
40
|
}: BrowserScreencastProps) {
|
|
44
41
|
const isLive = status === 'live';
|
|
@@ -73,8 +70,10 @@ export function BrowserScreencast({
|
|
|
73
70
|
|
|
74
71
|
<div className="flex-1 flex items-center justify-center p-2 min-h-0 overflow-auto">
|
|
75
72
|
{isLive ? (
|
|
73
|
+
// Always fills the panel: the surface is a fixed-resolution JPEG, so
|
|
74
|
+
// there is no device box to frame it in — only the zoom scale.
|
|
76
75
|
<div
|
|
77
|
-
className=
|
|
76
|
+
className="relative flex flex-col bg-paper w-full h-full transition-all duration-200"
|
|
78
77
|
style={{
|
|
79
78
|
zoom: zoomLevel !== 100 ? `${zoomLevel}%` : undefined,
|
|
80
79
|
transformOrigin: 'top center',
|
|
@@ -4,7 +4,6 @@ import { BrowserScreencast } from '@/client/components/workspace/browser-panel/S
|
|
|
4
4
|
import { useScreencast } from '@/client/hooks/browser/use-screencast';
|
|
5
5
|
import { useSessionState } from '@/client/hooks/workspace/session-state';
|
|
6
6
|
import { emitBrowserPageContext } from '@/shared/lib/browser/page-context';
|
|
7
|
-
import type { ViewportMode } from '@/shared/types';
|
|
8
7
|
|
|
9
8
|
interface BrowserPanelProps {
|
|
10
9
|
className?: string;
|
|
@@ -18,9 +17,14 @@ interface BrowserPanelProps {
|
|
|
18
17
|
*
|
|
19
18
|
* This panel only mirrors what the agent does. Interactive browsing belongs to
|
|
20
19
|
* the separate user-browser panel, which runs its own private Chromium.
|
|
20
|
+
*
|
|
21
|
+
* There is deliberately NO device/viewport selector here: the surface is a
|
|
22
|
+
* fixed-resolution screencast JPEG, so a preset could only letterbox the image
|
|
23
|
+
* while claiming to emulate a device. Emulation belongs to the agent itself
|
|
24
|
+
* (`tab.emulate()` / `browser.open({ viewport })`); the user-browser panel,
|
|
25
|
+
* whose iframe's layout viewport *is* its CSS box, keeps the selector.
|
|
21
26
|
*/
|
|
22
27
|
export function BrowserPanel({ className = '', active = true }: BrowserPanelProps) {
|
|
23
|
-
const [viewportMode, setViewportMode] = useSessionState<ViewportMode>('browser.viewportMode', 'responsive');
|
|
24
28
|
const [zoomLevel, setZoomLevel] = useSessionState<number>('browser.zoomLevel', 100);
|
|
25
29
|
const { status, url, title, tabs, targetId, frameSrc, actions, selectTarget, reconnect } = useScreencast(active);
|
|
26
30
|
|
|
@@ -41,16 +45,13 @@ export function BrowserPanel({ className = '', active = true }: BrowserPanelProp
|
|
|
41
45
|
<BrowserAddressBar
|
|
42
46
|
url={url}
|
|
43
47
|
status={status}
|
|
44
|
-
viewportMode={viewportMode}
|
|
45
48
|
zoomLevel={zoomLevel}
|
|
46
49
|
onIncludeInChat={handleIncludeInChat}
|
|
47
50
|
onReconnect={reconnect}
|
|
48
51
|
onOpenExternal={handleOpenExternal}
|
|
49
|
-
onChangeViewport={setViewportMode}
|
|
50
52
|
onZoomIn={() => setZoomLevel((prev) => Math.min(200, prev + 10))}
|
|
51
53
|
onZoomOut={() => setZoomLevel((prev) => Math.max(50, prev - 10))}
|
|
52
54
|
onResetZoom={() => setZoomLevel(100)}
|
|
53
|
-
onSetZoom={setZoomLevel}
|
|
54
55
|
/>
|
|
55
56
|
|
|
56
57
|
<BrowserScreencast
|
|
@@ -61,7 +62,6 @@ export function BrowserPanel({ className = '', active = true }: BrowserPanelProp
|
|
|
61
62
|
frameSrc={frameSrc}
|
|
62
63
|
actions={actions}
|
|
63
64
|
onSelectTarget={selectTarget}
|
|
64
|
-
viewportMode={viewportMode}
|
|
65
65
|
zoomLevel={zoomLevel}
|
|
66
66
|
/>
|
|
67
67
|
</div>
|
|
@@ -13,15 +13,22 @@ export interface BrowserToolbarProps {
|
|
|
13
13
|
/** Status dot class plus its label — the only per-panel difference. */
|
|
14
14
|
statusDotClass: string;
|
|
15
15
|
statusLabel: string;
|
|
16
|
-
viewportMode: ViewportMode;
|
|
17
16
|
zoomLevel: number;
|
|
18
|
-
onChangeViewport: (mode: ViewportMode) => void;
|
|
19
17
|
onZoomIn: () => void;
|
|
20
18
|
onZoomOut: () => void;
|
|
21
19
|
onResetZoom: () => void;
|
|
22
|
-
onSetZoom: (zoom: number) => void;
|
|
23
20
|
onIncludeInChat: () => void;
|
|
24
21
|
onOpenExternal: () => void;
|
|
22
|
+
/**
|
|
23
|
+
* Device-testing cluster, rendered only when both are supplied. The USER
|
|
24
|
+
* panel passes them: its iframe's layout viewport *is* its CSS box, so a
|
|
25
|
+
* preset genuinely re-runs the page's media queries. The AGENT panel omits
|
|
26
|
+
* them — its surface is a fixed-resolution screencast JPEG, so a preset
|
|
27
|
+
* could only letterbox the image while claiming to emulate a device.
|
|
28
|
+
*/
|
|
29
|
+
viewportMode?: ViewportMode;
|
|
30
|
+
onChangeViewport?: (mode: ViewportMode) => void;
|
|
31
|
+
onSetZoom?: (zoom: number) => void;
|
|
25
32
|
/** Shown after the include/copy buttons — the agent panel's reconnect button. */
|
|
26
33
|
trailingControls?: ComponentChildren;
|
|
27
34
|
/** Open-external button label/title, differing between the two panels. */
|
|
@@ -34,9 +41,10 @@ export interface BrowserToolbarProps {
|
|
|
34
41
|
/**
|
|
35
42
|
* Shared address bar for both browser panels. The two panels differ only in
|
|
36
43
|
* their status semantics, the middle slot (read-only URL vs editable form),
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
44
|
+
* the agent panel's reconnect button, and whether the device-testing cluster
|
|
45
|
+
* is offered at all (user panel yes, agent panel no — see `viewportMode`);
|
|
46
|
+
* everything else — the include, copy, open-external buttons and the zoom
|
|
47
|
+
* cluster — lives here so the two toolbars cannot drift.
|
|
40
48
|
*/
|
|
41
49
|
export function BrowserToolbar({
|
|
42
50
|
url,
|
|
@@ -86,12 +94,14 @@ export function BrowserToolbar({
|
|
|
86
94
|
{children}
|
|
87
95
|
|
|
88
96
|
<div className="flex items-center space-x-1.5 flex-shrink-0">
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
97
|
+
{viewportMode && onChangeViewport && (
|
|
98
|
+
<BrowserViewportSelector
|
|
99
|
+
viewportMode={viewportMode}
|
|
100
|
+
onChangeViewport={onChangeViewport}
|
|
101
|
+
zoomLevel={zoomLevel}
|
|
102
|
+
onSetZoom={onSetZoom}
|
|
103
|
+
/>
|
|
104
|
+
)}
|
|
95
105
|
|
|
96
106
|
<ZoomControls
|
|
97
107
|
zoomLevel={zoomLevel}
|
|
@@ -3,7 +3,7 @@ import type { ReactNode } from 'preact/compat';
|
|
|
3
3
|
import { Bot, Brain, Clock3, Coins, Hourglass, MessageSquarePlus, MoreHorizontal, RotateCcw, X } from 'lucide-preact';
|
|
4
4
|
import { CopyButton } from '@/client/components/common/CopyButton';
|
|
5
5
|
import { formatDuration } from '@/shared/lib/chat/duration';
|
|
6
|
-
import { providerLabel } from '@/shared/lib/models/provider
|
|
6
|
+
import { providerLabel } from '@/shared/lib/models/provider/label';
|
|
7
7
|
import { formatCompactTokens } from '@/shared/lib/format/number';
|
|
8
8
|
|
|
9
9
|
interface AiMessageFooterProps {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { useEffect, useState } from 'preact/hooks';
|
|
2
2
|
import { Bot } from 'lucide-preact';
|
|
3
|
-
import { providerLabel } from '@/shared/lib/models/provider
|
|
3
|
+
import { providerLabel } from '@/shared/lib/models/provider/label';
|
|
4
4
|
|
|
5
5
|
interface GeneratingIndicatorProps {
|
|
6
6
|
modelName?: string;
|
|
@@ -12,7 +12,7 @@ import { toTitleCase } from '@/shared/lib/chat/title-case';
|
|
|
12
12
|
import { FallbackOutput } from '@/client/components/workspace/chat-timeline/tool-renderers/shared/FallbackOutput';
|
|
13
13
|
import { tryParseJson } from '@/shared/lib/code/syntax-highlight';
|
|
14
14
|
import { JsonCodeBlock } from '@/client/components/workspace/chat-timeline/tool-renderers/shared/JsonCodeBlock';
|
|
15
|
-
import { getTodoSummary } from '@/shared/lib/chat/todo
|
|
15
|
+
import { getTodoSummary } from '@/shared/lib/chat/todo/parser';
|
|
16
16
|
import { parseAskQuestions } from '@/shared/lib/chat/ask-questions';
|
|
17
17
|
|
|
18
18
|
interface ToolCallCardProps {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useMemo } from 'preact/hooks';
|
|
2
2
|
import { CheckCircle2, ListTodo, Loader2, Square } from 'lucide-preact';
|
|
3
3
|
import type { ToolCallData } from '@/shared/types';
|
|
4
|
-
import { parseTodoData } from '@/shared/lib/chat/todo
|
|
4
|
+
import { parseTodoData } from '@/shared/lib/chat/todo/parser';
|
|
5
5
|
import { FallbackOutput } from '@/client/components/workspace/chat-timeline/tool-renderers/shared/FallbackOutput';
|
|
6
6
|
|
|
7
7
|
/** Panel khusus untuk tool `todo` — task list dengan progress bar, phase groups, dan status yang readable. */
|
|
@@ -2,17 +2,21 @@ import { AlignJustify, Check, Code2, Columns, Copy, Minus, Plus, RefreshCw, Spac
|
|
|
2
2
|
import { FileIcon } from '@/client/components/common/file-icon';
|
|
3
3
|
import { getGitStatusInfo } from '@/shared/lib/fs/git-status';
|
|
4
4
|
|
|
5
|
+
export type DiffViewMode = 'unified' | 'split';
|
|
6
|
+
|
|
5
7
|
interface DiffToolbarProps {
|
|
6
8
|
filePath: string;
|
|
7
9
|
status: string;
|
|
8
10
|
isStaged: boolean;
|
|
9
|
-
viewMode:
|
|
11
|
+
viewMode: DiffViewMode;
|
|
10
12
|
ignoreWhitespace: boolean;
|
|
11
13
|
additions: number;
|
|
12
14
|
deletions: number;
|
|
13
15
|
isLoading: boolean;
|
|
14
16
|
isCopied: boolean;
|
|
15
|
-
|
|
17
|
+
/** A stage/unstage/discard write is in flight; the toolbar locks its actions. */
|
|
18
|
+
isBusy?: boolean;
|
|
19
|
+
onSetViewMode: (mode: DiffViewMode) => void;
|
|
16
20
|
onToggleWhitespace: () => void;
|
|
17
21
|
onStageUnstage: () => void;
|
|
18
22
|
onDiscard: () => void;
|
|
@@ -21,6 +25,9 @@ interface DiffToolbarProps {
|
|
|
21
25
|
onCopyDiff: () => void;
|
|
22
26
|
}
|
|
23
27
|
|
|
28
|
+
const ACTION_CLASS =
|
|
29
|
+
'flex items-center justify-center p-1.5 rounded transition-colors cursor-pointer disabled:cursor-default disabled:opacity-40 disabled:hover:bg-transparent disabled:hover:text-ink/50';
|
|
30
|
+
|
|
24
31
|
export function DiffToolbar({
|
|
25
32
|
filePath,
|
|
26
33
|
status,
|
|
@@ -31,7 +38,8 @@ export function DiffToolbar({
|
|
|
31
38
|
deletions,
|
|
32
39
|
isLoading,
|
|
33
40
|
isCopied,
|
|
34
|
-
|
|
41
|
+
isBusy = false,
|
|
42
|
+
onSetViewMode,
|
|
35
43
|
onToggleWhitespace,
|
|
36
44
|
onStageUnstage,
|
|
37
45
|
onDiscard,
|
|
@@ -41,56 +49,72 @@ export function DiffToolbar({
|
|
|
41
49
|
}: DiffToolbarProps) {
|
|
42
50
|
const fileName = filePath.split('/').pop() || filePath;
|
|
43
51
|
const statusInfo = getGitStatusInfo(status, isStaged);
|
|
52
|
+
// Only the actions that write to git lock while one is in flight. Copying the
|
|
53
|
+
// diff is a local read of what is already on screen, so it stays available.
|
|
54
|
+
const locked = isLoading || isBusy;
|
|
44
55
|
|
|
45
56
|
return (
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
57
|
+
// `@container` so the labels below can hide on a NARROW PANEL rather than a
|
|
58
|
+
// narrow viewport: the editor panel is 320px wide on a 1440px screen, and
|
|
59
|
+
// `sm:` (viewport-keyed) kept every label on, overflowing the toolbar.
|
|
60
|
+
<div className="@container flex items-center justify-between px-3 py-1.5 bg-paper border-b border-ink/10 text-xs font-sans select-none flex-shrink-0 gap-2 min-w-0">
|
|
61
|
+
{/* Left: File name and status badge. Hidden when the panel is narrow —
|
|
62
|
+
the editor tab already carries the name and the status character, and
|
|
63
|
+
the mobile diff header carries both. */}
|
|
64
|
+
<div className="hidden @[560px]:flex items-center space-x-2 min-w-0 pr-2">
|
|
51
65
|
<FileIcon name={fileName} size={14} className="flex-shrink-0" />
|
|
52
66
|
<span className="font-mono text-ink font-medium truncate" title={filePath}>
|
|
53
67
|
{fileName}
|
|
54
68
|
</span>
|
|
55
|
-
|
|
69
|
+
|
|
56
70
|
<span className={`px-1.5 py-0.5 text-[10px] font-mono font-semibold rounded border ${statusInfo.badgeBgClass} flex-shrink-0`}>
|
|
57
71
|
{statusInfo.charStatus} {statusInfo.label}
|
|
58
72
|
</span>
|
|
73
|
+
</div>
|
|
59
74
|
|
|
75
|
+
{/* Right: Controls & actions. `ml-auto` keeps them right-aligned where the
|
|
76
|
+
identity block is hidden. This strip scrolls on its own when the panel
|
|
77
|
+
is too narrow, so no action is ever clipped out of reach. Each segment
|
|
78
|
+
sets its OWN mode — toggling meant clicking the already-active segment
|
|
79
|
+
flipped the view to the other one. */}
|
|
80
|
+
<div className="flex items-center space-x-0.5 @[440px]:space-x-1 text-ink/70 ml-auto min-w-0 overflow-x-auto no-scrollbar">
|
|
81
|
+
{/* Change counts live here, not in the identity block: the editor tab
|
|
82
|
+
carries the file name but never the counts, so this is the one piece
|
|
83
|
+
of the header that must survive a narrow panel. */}
|
|
60
84
|
{(additions > 0 || deletions > 0) && (
|
|
61
|
-
<div className="flex items-center space-x-1.5 text-[11px] font-mono flex-shrink-0
|
|
85
|
+
<div className="flex items-center space-x-1.5 text-[11px] font-mono flex-shrink-0 pr-1.5 mr-0.5 @[440px]:mr-1 border-r border-ink/10">
|
|
62
86
|
{additions > 0 && <span className="text-success font-semibold">+{additions}</span>}
|
|
63
87
|
{deletions > 0 && <span className="text-error font-semibold">-{deletions}</span>}
|
|
64
88
|
</div>
|
|
65
89
|
)}
|
|
66
|
-
</div>
|
|
67
90
|
|
|
68
|
-
{/* Right: Controls & actions. `ml-auto` keeps them right-aligned on phone
|
|
69
|
-
widths, where the identity block above is hidden. */}
|
|
70
|
-
<div className="flex items-center space-x-1 text-ink/70 flex-shrink-0 ml-auto">
|
|
71
91
|
{/* View mode toggle (Unified vs Split) */}
|
|
72
|
-
<div className="flex items-center bg-canvas rounded border border-ink/15 p-0.5 mr-1">
|
|
92
|
+
<div className="flex items-center bg-canvas rounded border border-ink/15 p-0.5 mr-0.5 @[440px]:mr-1 flex-shrink-0">
|
|
73
93
|
<button
|
|
74
94
|
type="button"
|
|
75
|
-
onClick={
|
|
76
|
-
className={`px-2 py-0.5 rounded text-[11px] font-medium flex items-center space-x-1 transition-colors ${
|
|
95
|
+
onClick={() => onSetViewMode('split')}
|
|
96
|
+
className={`px-2 py-0.5 rounded text-[11px] font-medium flex items-center space-x-1 transition-colors cursor-pointer flex-shrink-0 ${
|
|
77
97
|
viewMode === 'split' ? 'bg-paper text-ink shadow-xs' : 'text-ink/60 hover:text-ink'
|
|
78
98
|
}`}
|
|
79
|
-
title="Split (side-by-side)
|
|
99
|
+
title="Split (side-by-side) diff"
|
|
100
|
+
aria-label="Split (side-by-side) diff"
|
|
101
|
+
aria-pressed={viewMode === 'split'}
|
|
80
102
|
>
|
|
81
103
|
<Columns size={12} />
|
|
82
|
-
<span className="hidden
|
|
104
|
+
<span className="hidden @[420px]:inline">Split</span>
|
|
83
105
|
</button>
|
|
84
106
|
<button
|
|
85
107
|
type="button"
|
|
86
|
-
onClick={
|
|
87
|
-
className={`px-2 py-0.5 rounded text-[11px] font-medium flex items-center space-x-1 transition-colors ${
|
|
108
|
+
onClick={() => onSetViewMode('unified')}
|
|
109
|
+
className={`px-2 py-0.5 rounded text-[11px] font-medium flex items-center space-x-1 transition-colors cursor-pointer flex-shrink-0 ${
|
|
88
110
|
viewMode === 'unified' ? 'bg-paper text-ink shadow-xs' : 'text-ink/60 hover:text-ink'
|
|
89
111
|
}`}
|
|
90
|
-
title="Unified
|
|
112
|
+
title="Unified diff"
|
|
113
|
+
aria-label="Unified diff"
|
|
114
|
+
aria-pressed={viewMode === 'unified'}
|
|
91
115
|
>
|
|
92
116
|
<AlignJustify size={12} />
|
|
93
|
-
<span className="hidden
|
|
117
|
+
<span className="hidden @[420px]:inline">Unified</span>
|
|
94
118
|
</button>
|
|
95
119
|
</div>
|
|
96
120
|
|
|
@@ -98,10 +122,12 @@ export function DiffToolbar({
|
|
|
98
122
|
<button
|
|
99
123
|
type="button"
|
|
100
124
|
onClick={onToggleWhitespace}
|
|
101
|
-
className={`p-1.5 rounded transition-colors ${
|
|
125
|
+
className={`p-1.5 rounded transition-colors cursor-pointer flex-shrink-0 ${
|
|
102
126
|
ignoreWhitespace ? 'bg-ink/10 text-ink' : 'text-ink/50 hover:text-ink hover:bg-ink/5'
|
|
103
127
|
}`}
|
|
104
|
-
title={ignoreWhitespace ? 'Whitespace
|
|
128
|
+
title={ignoreWhitespace ? 'Whitespace changes hidden' : 'Hide whitespace changes'}
|
|
129
|
+
aria-label={ignoreWhitespace ? 'Whitespace changes hidden' : 'Hide whitespace changes'}
|
|
130
|
+
aria-pressed={ignoreWhitespace}
|
|
105
131
|
>
|
|
106
132
|
<Space size={13} />
|
|
107
133
|
</button>
|
|
@@ -110,19 +136,21 @@ export function DiffToolbar({
|
|
|
110
136
|
<button
|
|
111
137
|
type="button"
|
|
112
138
|
onClick={onRefresh}
|
|
113
|
-
className=
|
|
114
|
-
title="
|
|
115
|
-
|
|
139
|
+
className={`${ACTION_CLASS} text-ink/50 hover:text-ink hover:bg-ink/5 flex-shrink-0`}
|
|
140
|
+
title="Reload this diff"
|
|
141
|
+
aria-label="Reload this diff"
|
|
142
|
+
disabled={locked}
|
|
116
143
|
>
|
|
117
144
|
<RefreshCw size={13} className={isLoading ? 'animate-spin' : ''} />
|
|
118
145
|
</button>
|
|
119
146
|
|
|
120
|
-
{/* Copy diff */}
|
|
147
|
+
{/* Copy diff — never disabled: it reads the buffer already on screen. */}
|
|
121
148
|
<button
|
|
122
149
|
type="button"
|
|
123
150
|
onClick={onCopyDiff}
|
|
124
|
-
className=
|
|
125
|
-
title=
|
|
151
|
+
className={`${ACTION_CLASS} text-ink/50 hover:text-ink hover:bg-ink/5 flex-shrink-0`}
|
|
152
|
+
title={isCopied ? 'Copied' : 'Copy diff text'}
|
|
153
|
+
aria-label={isCopied ? 'Copied' : 'Copy diff text'}
|
|
126
154
|
>
|
|
127
155
|
{isCopied ? <Check size={13} className="text-success" /> : <Copy size={13} />}
|
|
128
156
|
</button>
|
|
@@ -132,8 +160,10 @@ export function DiffToolbar({
|
|
|
132
160
|
<button
|
|
133
161
|
type="button"
|
|
134
162
|
onClick={onDiscard}
|
|
135
|
-
className=
|
|
163
|
+
className={`${ACTION_CLASS} text-ink/50 hover:text-error hover:bg-error/10 flex-shrink-0`}
|
|
136
164
|
title="Discard changes in this file"
|
|
165
|
+
aria-label="Discard changes in this file"
|
|
166
|
+
disabled={locked}
|
|
137
167
|
>
|
|
138
168
|
<Undo2 size={13} />
|
|
139
169
|
</button>
|
|
@@ -143,18 +173,20 @@ export function DiffToolbar({
|
|
|
143
173
|
<button
|
|
144
174
|
type="button"
|
|
145
175
|
onClick={onStageUnstage}
|
|
146
|
-
className="px-2 py-1 text-[11px] font-medium rounded border border-ink/15 hover:bg-ink/5 flex items-center space-x-1 transition-colors text-ink"
|
|
176
|
+
className="px-2 py-1 text-[11px] font-medium rounded border border-ink/15 hover:bg-ink/5 flex items-center space-x-1 transition-colors text-ink cursor-pointer disabled:cursor-default disabled:opacity-40 disabled:hover:bg-transparent flex-shrink-0"
|
|
147
177
|
title={isStaged ? 'Unstage file' : 'Stage file'}
|
|
178
|
+
aria-label={isStaged ? 'Unstage file' : 'Stage file'}
|
|
179
|
+
disabled={locked}
|
|
148
180
|
>
|
|
149
181
|
{isStaged ? (
|
|
150
182
|
<>
|
|
151
183
|
<Minus size={12} />
|
|
152
|
-
<span>Unstage</span>
|
|
184
|
+
<span className="hidden @[360px]:inline">Unstage</span>
|
|
153
185
|
</>
|
|
154
186
|
) : (
|
|
155
187
|
<>
|
|
156
188
|
<Plus size={12} />
|
|
157
|
-
<span>Stage</span>
|
|
189
|
+
<span className="hidden @[360px]:inline">Stage</span>
|
|
158
190
|
</>
|
|
159
191
|
)}
|
|
160
192
|
</button>
|
|
@@ -164,11 +196,12 @@ export function DiffToolbar({
|
|
|
164
196
|
<button
|
|
165
197
|
type="button"
|
|
166
198
|
onClick={onOpenInEditor}
|
|
167
|
-
className="px-2 py-1 text-[11px] font-medium rounded bg-ink text-canvas hover:bg-ink/90 flex items-center space-x-1 transition-colors shadow-xs ml-1"
|
|
168
|
-
title="Open in
|
|
199
|
+
className="px-2 py-1 text-[11px] font-medium rounded bg-ink text-canvas hover:bg-ink/90 flex items-center space-x-1 transition-colors shadow-xs ml-1 cursor-pointer flex-shrink-0"
|
|
200
|
+
title="Open in code editor"
|
|
201
|
+
aria-label="Open in code editor"
|
|
169
202
|
>
|
|
170
203
|
<Code2 size={12} />
|
|
171
|
-
<span className="hidden
|
|
204
|
+
<span className="hidden @[360px]:inline">Edit File</span>
|
|
172
205
|
</button>
|
|
173
206
|
)}
|
|
174
207
|
</div>
|