gloomberb 0.11.3 → 0.12.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 +6 -4
- package/README.zh-CN.md +4 -2
- package/package.json +28 -1
- package/src/api-client/auth.ts +10 -0
- package/src/api-client/data.ts +103 -1
- package/src/api-client/index.test.ts +127 -0
- package/src/api-client/index.ts +129 -1
- package/src/api-client/normalizers.ts +39 -0
- package/src/api-client/paths.ts +85 -1
- package/src/api-client/types.ts +196 -0
- package/src/app/pane-runtime/index.tsx +15 -12
- package/src/app/pane-runtime/layout-placement.test.ts +27 -0
- package/src/app/pane-runtime/layout-placement.ts +13 -0
- package/src/app/pane-runtime/plugin-bindings.ts +62 -0
- package/src/app.tsx +60 -18
- package/src/architecture/browser-entry-graph.test.ts +96 -0
- package/src/brokers/profile-form.test.ts +25 -27
- package/src/brokers/test-broker.ts +68 -0
- package/src/cli/commands/plugins.ts +39 -11
- package/src/cli/desktop-pane-shot.ts +285 -16
- package/src/cli/index.ts +1 -1
- package/src/cli/pane-functions/capabilities.ts +58 -28
- package/src/cli/pane-functions/catalog.ts +16 -4
- package/src/cli/pane-functions/discovery.ts +1 -0
- package/src/cli/pane-functions/headless.test.ts +247 -0
- package/src/cli/pane-functions/headless.ts +423 -0
- package/src/cli/pane-functions/index.test.ts +9 -0
- package/src/cli/pane-functions/index.ts +6 -2
- package/src/cli/pane-functions/options.ts +27 -2
- package/src/cli/pane-functions/report.ts +4 -0
- package/src/cli/pane-functions/resolver.ts +26 -8
- package/src/cli/pane-functions/screenshot.test.ts +76 -0
- package/src/cli/pane-functions/screenshot.ts +236 -20
- package/src/cli/restore-plugins.ts +34 -0
- package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
- package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
- package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
- package/src/components/chart/composite/composite-chart.test.tsx +68 -20
- package/src/components/chart/composite/composite-chart.tsx +301 -204
- package/src/components/chart/composite/format.ts +8 -2
- package/src/components/chart/composite/interactions.test.ts +68 -102
- package/src/components/chart/composite/interactions.ts +214 -264
- package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
- package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
- package/src/components/chart/composite/scene.ts +57 -3
- package/src/components/chart/composite/text-renderer.ts +3 -2
- package/src/components/chart/composite/time-scale.ts +138 -57
- package/src/components/chart/composite/types.ts +25 -1
- package/src/components/chart/core/data.ts +9 -13
- package/src/components/chart/core/date-window.ts +7 -1
- package/src/components/chart/core/pointer.ts +3 -0
- package/src/components/chart/core/types.ts +0 -45
- package/src/components/chart/native/chart-rasterizer.ts +0 -1
- package/src/components/chart/native/kitty/index.test.ts +0 -188
- package/src/components/chart/native/raster/crosshair.ts +6 -5
- package/src/components/chart/native/raster/primitives.ts +0 -23
- package/src/components/chart/native/renderer-selection.ts +81 -31
- package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
- package/src/components/chart/static/chart-surface.tsx +150 -0
- package/src/components/chart/static/index.ts +8 -0
- package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
- package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
- package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
- package/src/components/chart/static/series.ts +49 -0
- package/src/components/command-bar/action-runtime.ts +1 -3
- package/src/components/command-bar/assist/model.test.ts +17 -2
- package/src/components/command-bar/assist/model.ts +40 -9
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/commands/direct/runtime.ts +1 -1
- package/src/components/command-bar/commands/registry.ts +0 -11
- package/src/components/command-bar/helpers.test.ts +2 -2
- package/src/components/command-bar/helpers.ts +2 -6
- package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
- package/src/components/command-bar/list/badge.test.ts +54 -0
- package/src/components/command-bar/list/badge.ts +62 -0
- package/src/components/command-bar/list/model.test.ts +103 -0
- package/src/components/command-bar/list/model.ts +61 -4
- package/src/components/command-bar/list/view.test.tsx +113 -0
- package/src/components/command-bar/list/view.tsx +147 -131
- package/src/components/command-bar/pane-templates/workflow.ts +1 -1
- package/src/components/command-bar/panel/body.tsx +51 -65
- package/src/components/command-bar/panel/index.tsx +48 -13
- package/src/components/command-bar/panel/layout.test.ts +139 -0
- package/src/components/command-bar/panel/layout.ts +65 -28
- package/src/components/command-bar/panel/native-surface.ts +24 -0
- package/src/components/command-bar/panel/prompt-binding.ts +54 -0
- package/src/components/command-bar/panel/runtime.ts +6 -3
- package/src/components/command-bar/panel/state.ts +41 -7
- package/src/components/command-bar/panel/types.ts +7 -1
- package/src/components/command-bar/routes/root/provider-search.ts +34 -15
- package/src/components/command-bar/routes/root/results.test.ts +161 -0
- package/src/components/command-bar/routes/root/results.ts +30 -28
- package/src/components/command-bar/routes/root/runtime.ts +37 -20
- package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
- package/src/components/command-bar/routes/root/search-providers.ts +205 -0
- package/src/components/command-bar/routes/root/selection.ts +1 -12
- package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
- package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
- package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
- package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
- package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
- package/src/components/command-bar/routing/actions.ts +1 -81
- package/src/components/command-bar/routing/launch-request.ts +1 -1
- package/src/components/command-bar/routing/list-state.ts +6 -21
- package/src/components/command-bar/selection-runtime.ts +1 -1
- package/src/components/command-bar/surface/assist.test.tsx +14 -10
- package/src/components/command-bar/surface/environment.ts +8 -1
- package/src/components/command-bar/surface/index.test.tsx +84 -68
- package/src/components/command-bar/surface/index.tsx +33 -36
- package/src/components/command-bar/surface/test-harness.tsx +15 -3
- package/src/components/command-bar/theme-picker.tsx +43 -15
- package/src/components/command-bar/view-model.test.ts +53 -8
- package/src/components/command-bar/view-model.ts +43 -14
- package/src/components/command-bar/workflow/tickers.ts +1 -0
- package/src/components/command-bar/workflow/types.ts +1 -1
- package/src/components/data-table/view.test.tsx +45 -6
- package/src/components/data-table/view.tsx +55 -9
- package/src/components/index.ts +10 -2
- package/src/components/input-search-bar.test.tsx +36 -2
- package/src/components/input-search-bar.tsx +18 -1
- package/src/components/layout/header.test.tsx +57 -10
- package/src/components/layout/header.tsx +292 -149
- package/src/components/layout/market-summary.test.ts +81 -0
- package/src/components/layout/market-summary.ts +109 -0
- package/src/components/layout/shell/chrome.ts +96 -1
- package/src/components/layout/shell/index.tsx +11 -3
- package/src/components/layout/shell/menu.ts +9 -0
- package/src/components/layout/shell/pane/actions.ts +29 -0
- package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
- package/src/components/layout/shell/shortcuts.ts +3 -0
- package/src/components/layout/status-bar.test.tsx +9 -8
- package/src/components/layout/status-bar.tsx +85 -54
- package/src/components/onboarding/onboarding-frame.tsx +2 -26
- package/src/components/price-sparkline/index.ts +61 -34
- package/src/components/price-sparkline/view.tsx +1 -1
- package/src/components/sign-in-gate.test.tsx +128 -0
- package/src/components/sign-in-gate.tsx +149 -0
- package/src/components/speedometer-gauge/desktop.tsx +2 -12
- package/src/components/ticker/badge/list.tsx +17 -3
- package/src/components/ticker/list-table-view.tsx +6 -3
- package/src/components/ui/data-table/index.tsx +3 -2
- package/src/components/ui/data-table/opentui/index.tsx +165 -120
- package/src/components/ui/frame.tsx +28 -0
- package/src/components/ui/index.ts +3 -0
- package/src/components/ui/native-select.tsx +20 -10
- package/src/components/ui/select-button.tsx +138 -0
- package/src/components/ui/status.tsx +2 -2
- package/src/components/ui/table-layout.ts +1 -1
- package/src/components/use-double-click-activation.ts +7 -3
- package/src/core/app-services.ts +3 -3
- package/src/data/config/layout.ts +12 -6
- package/src/data/config/store/index.test.ts +97 -0
- package/src/data/config/store/migrations.ts +41 -0
- package/src/data/config/store/normalize.ts +5 -3
- package/src/i18n/es.ts +6 -2
- package/src/i18n/ja.ts +6 -2
- package/src/i18n/ko.ts +6 -2
- package/src/i18n/zh-cn.ts +6 -2
- package/src/i18n/zh-tw.ts +6 -2
- package/src/layout-marketplace/pane-imagery.ts +1 -0
- package/src/layout-marketplace/payload.test.ts +17 -0
- package/src/layout-marketplace/payload.ts +40 -5
- package/src/market-data/coordinator/index.ts +3 -2
- package/src/market-data/coordinator/quotes.ts +173 -66
- package/src/market-data/coordinator/subscriptions.test.ts +215 -37
- package/src/pane-settings.ts +1 -1
- package/src/plugins/builtin/analytics/pane-model.ts +1 -1
- package/src/plugins/builtin/analytics/view.tsx +1 -1
- package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
- package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
- package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
- package/src/plugins/builtin/chat/content/composer.tsx +8 -4
- package/src/plugins/builtin/cloud/auth-dialog.tsx +14 -228
- package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
- package/src/plugins/builtin/cloud/plugin.tsx +1 -0
- package/src/plugins/builtin/composite-plugins.ts +14 -1
- package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
- package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
- package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
- package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
- package/src/plugins/builtin/credit-conditions/index.tsx +2 -21
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/earnings-calls/data.ts +223 -0
- package/src/plugins/builtin/earnings-calls/format.ts +49 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +756 -0
- package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
- package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +334 -0
- package/src/plugins/builtin/econ/detail-view.tsx +1 -2
- package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
- package/src/plugins/builtin/econ/index.tsx +2 -1
- package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
- package/src/plugins/builtin/econ-statistics/client.ts +92 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
- package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
- package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
- package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
- package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
- package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
- package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
- package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
- package/src/plugins/builtin/econ-statistics/view.ts +90 -0
- package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
- package/src/plugins/builtin/fear-greed/data.ts +6 -1
- package/src/plugins/builtin/help/index.tsx +4 -0
- package/src/plugins/builtin/holders/index.tsx +1 -0
- package/src/plugins/builtin/insider/index.tsx +1 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
- package/src/plugins/builtin/market-valuation/align.ts +122 -0
- package/src/plugins/builtin/market-valuation/cache.ts +32 -0
- package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
- package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
- package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
- package/src/plugins/builtin/market-valuation/client.ts +116 -0
- package/src/plugins/builtin/market-valuation/defs.ts +251 -0
- package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
- package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
- package/src/plugins/builtin/market-valuation/headless.ts +170 -0
- package/src/plugins/builtin/market-valuation/index.tsx +64 -0
- package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
- package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
- package/src/plugins/builtin/market-valuation/model.ts +8 -0
- package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
- package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
- package/src/plugins/builtin/market-valuation/series.ts +12 -0
- package/src/plugins/builtin/market-valuation/settings.ts +38 -0
- package/src/plugins/builtin/market-valuation/sources.ts +130 -0
- package/src/plugins/builtin/market-valuation/trend.ts +89 -0
- package/src/plugins/builtin/market-valuation/view.ts +137 -0
- package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
- package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
- package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
- package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
- package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
- package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
- package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
- package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
- package/src/plugins/builtin/news/wire/index.tsx +31 -44
- package/src/plugins/builtin/options/index.tsx +1 -0
- package/src/plugins/builtin/options/view.test.tsx +29 -1
- package/src/plugins/builtin/options/view.tsx +1 -22
- package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
- package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
- package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
- package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
- package/src/plugins/builtin/plugin-module.ts +9 -1
- package/src/plugins/builtin/research/index.tsx +4 -0
- package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
- package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
- package/src/plugins/builtin/research-search/data.ts +109 -0
- package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
- package/src/plugins/builtin/research-search/document-view.tsx +254 -0
- package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
- package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
- package/src/plugins/builtin/research-search/index.tsx +85 -0
- package/src/plugins/builtin/research-search/model.test.ts +40 -0
- package/src/plugins/builtin/research-search/model.ts +291 -0
- package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
- package/src/plugins/builtin/research-search/pane.tsx +678 -0
- package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
- package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
- package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
- package/src/plugins/builtin/research-search/snippet.ts +134 -0
- package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
- package/src/plugins/builtin/scanner/flow-model.ts +29 -1
- package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
- package/src/plugins/builtin/sec/index.tsx +1 -0
- package/src/plugins/builtin/shared/series-cache.ts +93 -0
- package/src/plugins/builtin/short-interest/index.tsx +1 -0
- package/src/plugins/builtin/short-interest/pane.tsx +1 -1
- package/src/plugins/builtin/thirteenf/index.tsx +1 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +0 -14
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
- package/src/plugins/builtin/volatility/index.tsx +1 -1
- package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +213 -69
- package/src/plugins/builtin/world-venue-map/model.test.ts +32 -0
- package/src/plugins/builtin/world-venue-map/model.ts +134 -13
- package/src/plugins/builtin/yield-curve/index.tsx +1 -1
- package/src/plugins/bundle.test.ts +109 -0
- package/src/plugins/bundle.ts +139 -0
- package/src/plugins/catalog-browser.test.ts +1 -0
- package/src/plugins/catalog-browser.ts +10 -0
- package/src/plugins/catalog-manifest.test.ts +43 -0
- package/src/plugins/catalog-manifest.ts +92 -0
- package/src/plugins/catalog-ui.ts +20 -4
- package/src/plugins/catalog.ts +9 -2
- package/src/plugins/current-target.ts +28 -0
- package/src/plugins/host-contract.ts +37 -0
- package/src/plugins/host-link.ts +158 -0
- package/src/plugins/host-modules.ts +61 -0
- package/src/plugins/loader.ts +41 -22
- package/src/plugins/ownership.ts +1 -0
- package/src/plugins/prediction-markets/index.tsx +1 -0
- package/src/plugins/registry/context.ts +3 -0
- package/src/plugins/registry/contributions.ts +27 -0
- package/src/plugins/registry/index.test.ts +25 -0
- package/src/plugins/registry/index.ts +8 -0
- package/src/plugins/registry/pane-settings.ts +6 -13
- package/src/plugins/seed.test.ts +83 -0
- package/src/plugins/seed.ts +80 -0
- package/src/public/broker.ts +31 -0
- package/src/public/market-data.ts +9 -0
- package/src/public/pane-hooks.ts +57 -0
- package/src/public/public-api.test.ts +285 -0
- package/src/public/react.ts +62 -0
- package/src/public/test-support.ts +23 -0
- package/src/public/time-series.ts +8 -0
- package/src/public/utils.ts +77 -0
- package/src/remote/layout-helpers.ts +6 -8
- package/src/remote/resources.ts +2 -0
- package/src/remote/schema.ts +1 -0
- package/src/remote/semantic-tree.test.tsx +105 -0
- package/src/remote/semantic-tree.tsx +53 -35
- package/src/renderers/browser/main.tsx +6 -0
- package/src/renderers/cloudflare/worker.test.ts +19 -0
- package/src/renderers/cloudflare/worker.ts +1 -1
- package/src/renderers/electrobun/bun/external-plugins.ts +140 -0
- package/src/renderers/electrobun/bun/index.ts +5 -2
- package/src/renderers/electrobun/shared/protocol.ts +21 -0
- package/src/renderers/electrobun/view/build-assets.ts +0 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +51 -1
- package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
- package/src/renderers/electrobun/view/external-plugins.ts +63 -0
- package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
- package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
- package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
- package/src/renderers/electrobun/view/host/mouse.ts +16 -2
- package/src/renderers/electrobun/view/main.tsx +27 -2
- package/src/renderers/electrobun/view/styles.css +5 -0
- package/src/renderers/electrobun/view/toast-host.tsx +18 -0
- package/src/renderers/opentui/chart-surface.tsx +5 -1
- package/src/renderers/opentui/host.tsx +16 -2
- package/src/renderers/opentui/interaction-performance.test.ts +79 -0
- package/src/renderers/opentui/interaction-performance.ts +188 -0
- package/src/renderers/opentui/start.tsx +26 -1
- package/src/renderers/opentui/test-utils.tsx +13 -0
- package/src/renderers/opentui/toast-host.tsx +26 -2
- package/src/renderers/opentui/ui-host.tsx +1 -0
- package/src/sources/provider-router/search.test.ts +191 -0
- package/src/sources/provider-router/search.ts +92 -39
- package/src/state/app/context/index.tsx +2 -0
- package/src/state/app/context/selector.test.tsx +33 -1
- package/src/state/hooks/quote-streaming.test.tsx +93 -0
- package/src/state/hooks/quote-streaming.ts +106 -30
- package/src/state/pane-table-export-registry.ts +19 -0
- package/src/theme/color-utils.ts +1 -1
- package/src/theme/colors.ts +50 -0
- package/src/theme/themes.test.ts +15 -2
- package/src/theme/themes.ts +19 -1
- package/src/tickers/search/index.test.ts +26 -0
- package/src/tickers/search/index.ts +42 -17
- package/src/tickers/search/ranking.ts +14 -0
- package/src/time-series/chart-data.test.ts +216 -0
- package/src/time-series/resolution.test.ts +25 -26
- package/src/time-series/resolution.ts +41 -10
- package/src/time-series/resolve.ts +7 -0
- package/src/time-series/types.ts +3 -1
- package/src/time-series/use-chart-resolution.ts +5 -0
- package/src/types/config.ts +123 -3
- package/src/types/data-provider.ts +11 -0
- package/src/types/headless.ts +161 -0
- package/src/types/plugin.ts +102 -0
- package/src/ui/host.tsx +12 -1
- package/src/ui/index.tsx +1 -0
- package/src/ui/toast.tsx +8 -4
- package/src/utils/format.ts +35 -0
- package/src/utils/fuzzy-search.test.ts +94 -0
- package/src/utils/fuzzy-search.ts +81 -22
- package/src/utils/shortcut-labels.ts +14 -0
- package/src/components/chart/core/axis-format.ts +0 -157
- package/src/components/chart/core/index.test.ts +0 -1075
- package/src/components/chart/core/projection.ts +0 -357
- package/src/components/chart/core/render-utils.ts +0 -4
- package/src/components/chart/core/renderer.ts +0 -244
- package/src/components/chart/core/scene.ts +0 -180
- package/src/components/chart/core/time-axis.ts +0 -330
- package/src/components/chart/cursor-motion.ts +0 -47
- package/src/components/chart/indicators/types.ts +0 -26
- package/src/components/chart/market-session.ts +0 -176
- package/src/components/chart/multi-line/renderer.test.ts +0 -83
- package/src/components/chart/multi-line/renderer.ts +0 -161
- package/src/components/chart/multi-line/scene.ts +0 -188
- package/src/components/chart/native/raster/layout.ts +0 -129
- package/src/components/chart/native/raster/price-chart.ts +0 -244
- package/src/components/chart/rendering/chart-draw.ts +0 -270
- package/src/components/chart/rendering/geometry.ts +0 -95
- package/src/components/chart/rendering/pixel-buffer.ts +0 -267
- package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
- package/src/components/chart/scatter-chart-renderer.ts +0 -223
- package/src/components/chart/static/chart/surface.tsx +0 -358
- package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
- package/src/components/command-bar/routes/root/article-results.ts +0 -55
- package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
- package/src/plugins/builtin/news/wire/article-search.ts +0 -140
- package/src/plugins/builtin/substack/api/auth.ts +0 -185
- package/src/plugins/builtin/substack/api/cache.ts +0 -82
- package/src/plugins/builtin/substack/api/loaders.ts +0 -170
- package/src/plugins/builtin/substack/api/store.ts +0 -186
- package/src/plugins/builtin/substack/api/types.ts +0 -42
- package/src/plugins/builtin/substack/api.test.ts +0 -298
- package/src/plugins/builtin/substack/article-detail.tsx +0 -397
- package/src/plugins/builtin/substack/article-stack.tsx +0 -133
- package/src/plugins/builtin/substack/content.ts +0 -478
- package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
- package/src/plugins/builtin/substack/index.tsx +0 -49
- package/src/plugins/builtin/substack/login-view.tsx +0 -126
- package/src/plugins/builtin/substack/model.test.ts +0 -155
- package/src/plugins/builtin/substack/normalize.ts +0 -343
- package/src/plugins/builtin/substack/pane-footer.ts +0 -52
- package/src/plugins/builtin/substack/pane-state.ts +0 -170
- package/src/plugins/builtin/substack/pane.tsx +0 -547
- package/src/plugins/builtin/substack/read-state.ts +0 -30
- package/src/plugins/builtin/substack/table.ts +0 -111
- package/src/plugins/builtin/substack/types.ts +0 -71
- package/src/plugins/builtin/substack/utils.ts +0 -131
- package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
- package/src/plugins/ibkr/account-cache.test.ts +0 -102
- package/src/plugins/ibkr/account-cache.ts +0 -24
- package/src/plugins/ibkr/broker-adapter.ts +0 -286
- package/src/plugins/ibkr/config.test.ts +0 -79
- package/src/plugins/ibkr/config.ts +0 -250
- package/src/plugins/ibkr/dialogs.tsx +0 -40
- package/src/plugins/ibkr/flex/index.test.ts +0 -264
- package/src/plugins/ibkr/flex/index.ts +0 -367
- package/src/plugins/ibkr/flex/statement-client.ts +0 -235
- package/src/plugins/ibkr/fundamental-parser.ts +0 -149
- package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
- package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
- package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
- package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
- package/src/plugins/ibkr/gateway/connection.ts +0 -152
- package/src/plugins/ibkr/gateway/contracts.ts +0 -132
- package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
- package/src/plugins/ibkr/gateway/helpers.ts +0 -33
- package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
- package/src/plugins/ibkr/gateway/history.ts +0 -120
- package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
- package/src/plugins/ibkr/gateway/market-data.ts +0 -345
- package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
- package/src/plugins/ibkr/gateway/orders.ts +0 -63
- package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
- package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
- package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
- package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
- package/src/plugins/ibkr/gateway/selection.ts +0 -51
- package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
- package/src/plugins/ibkr/gateway/service/index.ts +0 -429
- package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
- package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
- package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
- package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
- package/src/plugins/ibkr/gateway/service/native.ts +0 -543
- package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
- package/src/plugins/ibkr/gateway/types.ts +0 -27
- package/src/plugins/ibkr/index.test.tsx +0 -162
- package/src/plugins/ibkr/index.tsx +0 -159
- package/src/plugins/ibkr/instance-selection.test.ts +0 -120
- package/src/plugins/ibkr/instance-selection.ts +0 -36
- package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
- package/src/plugins/ibkr/portfolio-performance.ts +0 -129
- package/src/plugins/ibkr/trade/badge.tsx +0 -28
- package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
- package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
- package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
- package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
- package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
- package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
- package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
- package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
- package/src/plugins/ibkr/trade/tab/model.ts +0 -159
- package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
- package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
- package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
- package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
- package/src/plugins/ibkr/trade/utils.test.ts +0 -175
- package/src/plugins/ibkr/trade/utils.ts +0 -144
- package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
- package/src/plugins/ibkr/trading/pane.tsx +0 -332
- package/src/plugins/ibkr/trading/state.test.ts +0 -109
- package/src/plugins/ibkr/trading/state.ts +0 -335
package/src/app.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import { DetachedPaneShell } from "./components/layout/detached-pane-shell";
|
|
|
17
17
|
import { TransientLayoutProvider } from "./components/layout/transient-layout";
|
|
18
18
|
import { CommandBar } from "./components/command-bar/surface";
|
|
19
19
|
import { OnboardingWizard } from "./components/onboarding/onboarding-wizard";
|
|
20
|
+
import { SignInGate } from "./components/sign-in-gate";
|
|
20
21
|
import { useDialog } from "./ui/dialog";
|
|
21
22
|
import { PluginRegistry } from "./plugins/registry";
|
|
22
23
|
import type { LoadedExternalPlugin } from "./plugins/loader";
|
|
@@ -55,10 +56,12 @@ import { measurePerf } from "./utils/perf-marks";
|
|
|
55
56
|
import { useAppLanguage } from "./i18n/react";
|
|
56
57
|
import { AppLanguageConfigObserver } from "./app/language-observer";
|
|
57
58
|
import { isPaneShareHandoff } from "./shares/location";
|
|
59
|
+
import { apiClient } from "./api-client";
|
|
58
60
|
|
|
59
61
|
const EMPTY_EXTERNAL_PLUGINS: LoadedExternalPlugin[] = [];
|
|
60
62
|
|
|
61
63
|
interface AppInnerProps {
|
|
64
|
+
externalPlugins: readonly LoadedExternalPlugin[];
|
|
62
65
|
pluginRegistry: PluginRegistry;
|
|
63
66
|
tickerRepository: AppTickerRepositoryPort;
|
|
64
67
|
dataProvider: DataProvider;
|
|
@@ -71,6 +74,8 @@ interface AppInnerProps {
|
|
|
71
74
|
updatesEnabled?: boolean;
|
|
72
75
|
onboardingActive?: boolean;
|
|
73
76
|
onOnboardingComplete?: (config: AppConfig) => void | Promise<void>;
|
|
77
|
+
/** Hosted browser terminal: nothing is reachable until a session exists. */
|
|
78
|
+
signInGateActive?: boolean;
|
|
74
79
|
}
|
|
75
80
|
|
|
76
81
|
function ThemedAppRoot({ children }: { children: ReactNode }) {
|
|
@@ -92,6 +97,7 @@ function ThemedAppRoot({ children }: { children: ReactNode }) {
|
|
|
92
97
|
}
|
|
93
98
|
|
|
94
99
|
function AppInner({
|
|
100
|
+
externalPlugins,
|
|
95
101
|
pluginRegistry,
|
|
96
102
|
tickerRepository,
|
|
97
103
|
dataProvider,
|
|
@@ -104,6 +110,7 @@ function AppInner({
|
|
|
104
110
|
updatesEnabled = true,
|
|
105
111
|
onboardingActive = false,
|
|
106
112
|
onOnboardingComplete,
|
|
113
|
+
signInGateActive = false,
|
|
107
114
|
}: AppInnerProps) {
|
|
108
115
|
const dispatch = useAppDispatch();
|
|
109
116
|
const stateRef = useAppStateRef();
|
|
@@ -158,6 +165,13 @@ function AppInner({
|
|
|
158
165
|
renderToast: (notification) => {
|
|
159
166
|
const type = notification.type ?? "info";
|
|
160
167
|
let toastId: string | number | undefined;
|
|
168
|
+
const dismissAfter = (run: () => void) => () => {
|
|
169
|
+
try {
|
|
170
|
+
run();
|
|
171
|
+
} finally {
|
|
172
|
+
if (toastId != null) toast.dismiss(toastId);
|
|
173
|
+
}
|
|
174
|
+
};
|
|
161
175
|
const options = {
|
|
162
176
|
title: notification.title,
|
|
163
177
|
subtitle: notification.subtitle,
|
|
@@ -165,13 +179,13 @@ function AppInner({
|
|
|
165
179
|
action: notification.action
|
|
166
180
|
? {
|
|
167
181
|
label: notification.action.label,
|
|
168
|
-
onClick: () =>
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
182
|
+
onClick: dismissAfter(() => notification.action?.onClick()),
|
|
183
|
+
}
|
|
184
|
+
: undefined,
|
|
185
|
+
secondaryAction: notification.secondaryAction
|
|
186
|
+
? {
|
|
187
|
+
label: notification.secondaryAction.label,
|
|
188
|
+
onClick: dismissAfter(() => notification.secondaryAction?.onClick()),
|
|
175
189
|
}
|
|
176
190
|
: undefined,
|
|
177
191
|
};
|
|
@@ -249,7 +263,10 @@ function AppInner({
|
|
|
249
263
|
desktopDeepLinkBridge,
|
|
250
264
|
desktopWindowKind: desktopWindowBridge?.kind,
|
|
251
265
|
dispatch,
|
|
252
|
-
|
|
266
|
+
// The browser bridge re-emits from the URL on subscribe and nothing rewrites
|
|
267
|
+
// it, so a `?layout=` / `?share=` intent survives the gate and lands once a
|
|
268
|
+
// session exists. Running it earlier would only 401 behind the scrim.
|
|
269
|
+
initialized: state.initialized && !signInGateActive,
|
|
253
270
|
pluginRegistry,
|
|
254
271
|
stateRef,
|
|
255
272
|
});
|
|
@@ -294,16 +311,23 @@ function AppInner({
|
|
|
294
311
|
// onboarding finishes, the normal pull-before-push sync starts immediately.
|
|
295
312
|
initialized: state.initialized
|
|
296
313
|
&& desktopWindowBridge?.kind !== "detached"
|
|
297
|
-
&& !onboardingActive
|
|
314
|
+
&& !onboardingActive
|
|
315
|
+
&& !signInGateActive,
|
|
298
316
|
});
|
|
299
317
|
|
|
318
|
+
const persistConfig = useCallback((nextConfig: AppState["config"]) => {
|
|
319
|
+
scheduleConfigSave(nextConfig);
|
|
320
|
+
}, []);
|
|
321
|
+
|
|
300
322
|
useAppPaneRuntime({
|
|
301
323
|
dataProvider,
|
|
302
324
|
detachedPaneId,
|
|
303
325
|
dialog,
|
|
304
326
|
dispatch,
|
|
327
|
+
externalPlugins,
|
|
305
328
|
isDetachedWindow,
|
|
306
329
|
notify,
|
|
330
|
+
persistConfig,
|
|
307
331
|
pluginRegistry,
|
|
308
332
|
state,
|
|
309
333
|
stateRef,
|
|
@@ -376,14 +400,7 @@ function AppInner({
|
|
|
376
400
|
desktopWindowBridge={desktopWindowBridge}
|
|
377
401
|
>
|
|
378
402
|
<ThemedAppRoot>
|
|
379
|
-
<Header
|
|
380
|
-
onOpenHelp={() => pluginRegistry.showPane("help")}
|
|
381
|
-
onOpenChangelog={(version) => {
|
|
382
|
-
void pluginRegistry.createPaneFromTemplateAsyncFn("changelog-pane", {
|
|
383
|
-
values: { version },
|
|
384
|
-
}).catch(() => {});
|
|
385
|
-
}}
|
|
386
|
-
/>
|
|
403
|
+
<Header onOpenHelp={() => pluginRegistry.showPane("help")} />
|
|
387
404
|
<TransientLayoutProvider>
|
|
388
405
|
<Shell
|
|
389
406
|
pluginRegistry={pluginRegistry}
|
|
@@ -391,7 +408,13 @@ function AppInner({
|
|
|
391
408
|
desktopDockPreview={desktopDockPreview}
|
|
392
409
|
commandBarNativeOccluder={commandBarNativeOccluder}
|
|
393
410
|
/>
|
|
394
|
-
<StatusBar
|
|
411
|
+
<StatusBar
|
|
412
|
+
onOpenChangelog={(version) => {
|
|
413
|
+
void pluginRegistry.createPaneFromTemplateAsyncFn("changelog-pane", {
|
|
414
|
+
values: { version },
|
|
415
|
+
}).catch(() => {});
|
|
416
|
+
}}
|
|
417
|
+
/>
|
|
395
418
|
</TransientLayoutProvider>
|
|
396
419
|
{onboardingActive && onOnboardingComplete ? (
|
|
397
420
|
<OnboardingWizard
|
|
@@ -400,6 +423,7 @@ function AppInner({
|
|
|
400
423
|
onComplete={onOnboardingComplete}
|
|
401
424
|
/>
|
|
402
425
|
) : null}
|
|
426
|
+
{signInGateActive ? <SignInGate /> : null}
|
|
403
427
|
{state.commandBarOpen && (
|
|
404
428
|
<CommandBar
|
|
405
429
|
dataProvider={dataProvider}
|
|
@@ -430,6 +454,11 @@ interface AppProps {
|
|
|
430
454
|
desktopThemePreview?: DesktopThemePreviewState | null;
|
|
431
455
|
remoteControlAdapter?: RemoteControlAdapter;
|
|
432
456
|
updatesEnabled?: boolean;
|
|
457
|
+
/**
|
|
458
|
+
* Requires a Gloom Cloud session before the app is usable. The hosted browser
|
|
459
|
+
* terminal sets this; desktop and the TUI keep sign-in optional.
|
|
460
|
+
*/
|
|
461
|
+
requireSignIn?: boolean;
|
|
433
462
|
}
|
|
434
463
|
|
|
435
464
|
export function App({
|
|
@@ -445,6 +474,7 @@ export function App({
|
|
|
445
474
|
desktopThemePreview = null,
|
|
446
475
|
remoteControlAdapter,
|
|
447
476
|
updatesEnabled = true,
|
|
477
|
+
requireSignIn = false,
|
|
448
478
|
}: AppProps) {
|
|
449
479
|
useAppLanguage();
|
|
450
480
|
const externalPlugins = providedExternalPlugins ?? EMPTY_EXTERNAL_PLUGINS;
|
|
@@ -469,6 +499,16 @@ export function App({
|
|
|
469
499
|
const [config, setConfig] = useState(() => {
|
|
470
500
|
return initialCliLaunch.config;
|
|
471
501
|
});
|
|
502
|
+
// Only the surfaces that require a session subscribe, so desktop and the
|
|
503
|
+
// terminal keep their current render profile.
|
|
504
|
+
const [signedIn, setSignedIn] = useState(() => !requireSignIn || apiClient.isSignedIn());
|
|
505
|
+
useEffect(() => {
|
|
506
|
+
if (!requireSignIn) return;
|
|
507
|
+
const sync = () => setSignedIn(apiClient.isSignedIn());
|
|
508
|
+
sync();
|
|
509
|
+
return apiClient.subscribeCurrentUser(sync);
|
|
510
|
+
}, [requireSignIn]);
|
|
511
|
+
const signInGateActive = requireSignIn && !signedIn;
|
|
472
512
|
const shareHandoff = isPaneShareHandoff();
|
|
473
513
|
const [showOnboarding, setShowOnboarding] = useState(() => (
|
|
474
514
|
desktopWindowBridge?.kind !== "detached"
|
|
@@ -518,6 +558,7 @@ export function App({
|
|
|
518
558
|
>
|
|
519
559
|
<AppLanguageConfigObserver />
|
|
520
560
|
<AppInner
|
|
561
|
+
externalPlugins={externalPlugins}
|
|
521
562
|
pluginRegistry={services.pluginRegistry}
|
|
522
563
|
tickerRepository={services.tickerRepository}
|
|
523
564
|
dataProvider={services.dataProvider}
|
|
@@ -529,6 +570,7 @@ export function App({
|
|
|
529
570
|
remoteControlAdapter={remoteControlAdapter}
|
|
530
571
|
updatesEnabled={updatesEnabled}
|
|
531
572
|
onboardingActive={showOnboarding}
|
|
573
|
+
signInGateActive={signInGateActive}
|
|
532
574
|
onOnboardingComplete={(updatedConfig) => {
|
|
533
575
|
setConfig(updatedConfig);
|
|
534
576
|
setShowOnboarding(false);
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { existsSync } from "fs";
|
|
3
|
+
import { dirname, join, relative, resolve } from "path";
|
|
4
|
+
|
|
5
|
+
const SOURCE_ROOT = process.cwd();
|
|
6
|
+
const IMPORT_PATTERN = /\b(?:import|export)\s+(?:[^'"]*?\s+from\s+)?["']([^"']+)["']|import\(["']([^"']+)["']\)/g;
|
|
7
|
+
const TYPE_ONLY_PATTERN = /\b(?:import|export)\s+type\s/;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Entry points that run in a browser context: the Electrobun desktop view and
|
|
11
|
+
* the hosted web app. Neither has `process`, a filesystem, or a Bun runtime.
|
|
12
|
+
*/
|
|
13
|
+
const BROWSER_ENTRIES = [
|
|
14
|
+
"src/renderers/electrobun/view/main.tsx",
|
|
15
|
+
"src/renderers/browser/main.tsx",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Modules that only work under Bun. Each reads the filesystem or the process
|
|
20
|
+
* environment at module scope, so merely importing one from a browser bundle
|
|
21
|
+
* throws on load — the bundle still builds, which is exactly what makes this
|
|
22
|
+
* worth a test.
|
|
23
|
+
*
|
|
24
|
+
* `plugins/loader` reaching the desktop view through `plugins/bundle` shipped a
|
|
25
|
+
* broken desktop build: the view crashed with "Can't find variable: process"
|
|
26
|
+
* before rendering anything, while `desktop:view:build` reported success.
|
|
27
|
+
*/
|
|
28
|
+
const BUN_ONLY_MODULES = [
|
|
29
|
+
"src/plugins/loader.ts",
|
|
30
|
+
"src/plugins/bundle.ts",
|
|
31
|
+
"src/plugins/seed.ts",
|
|
32
|
+
"src/plugins/host-link.ts",
|
|
33
|
+
"src/cli/restore-plugins.ts",
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const EXTENSIONS = [".ts", ".tsx", ".js", ".jsx"];
|
|
37
|
+
|
|
38
|
+
function resolveSpecifier(fromFile: string, specifier: string): string | null {
|
|
39
|
+
if (!specifier.startsWith(".")) return null;
|
|
40
|
+
const base = resolve(dirname(fromFile), specifier);
|
|
41
|
+
for (const candidate of [base, ...EXTENSIONS.map((ext) => `${base}${ext}`), ...EXTENSIONS.map((ext) => join(base, `index${ext}`))]) {
|
|
42
|
+
if (existsSync(candidate) && !candidate.endsWith("/")) {
|
|
43
|
+
try {
|
|
44
|
+
if (Bun.file(candidate).size >= 0 && !candidate.match(/\/$/)) return candidate;
|
|
45
|
+
} catch {
|
|
46
|
+
// Directory or unreadable entry: keep trying the other candidates.
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Walks runtime imports only; `import type` is erased and cannot pull code in. */
|
|
54
|
+
async function reachableFrom(entry: string): Promise<Map<string, string[]>> {
|
|
55
|
+
const seen = new Map<string, string[]>();
|
|
56
|
+
const queue: Array<{ file: string; path: string[] }> = [{ file: resolve(SOURCE_ROOT, entry), path: [entry] }];
|
|
57
|
+
|
|
58
|
+
while (queue.length > 0) {
|
|
59
|
+
const current = queue.shift()!;
|
|
60
|
+
const key = relative(SOURCE_ROOT, current.file);
|
|
61
|
+
if (seen.has(key)) continue;
|
|
62
|
+
seen.set(key, current.path);
|
|
63
|
+
|
|
64
|
+
let source: string;
|
|
65
|
+
try {
|
|
66
|
+
source = await Bun.file(current.file).text();
|
|
67
|
+
} catch {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
for (const line of source.split("\n")) {
|
|
72
|
+
if (TYPE_ONLY_PATTERN.test(line)) continue;
|
|
73
|
+
for (const match of line.matchAll(IMPORT_PATTERN)) {
|
|
74
|
+
const specifier = match[1] ?? match[2] ?? "";
|
|
75
|
+
const resolved = resolveSpecifier(current.file, specifier);
|
|
76
|
+
if (resolved) queue.push({ file: resolved, path: [...current.path, relative(SOURCE_ROOT, resolved)] });
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
return seen;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
describe("browser entry import graph", () => {
|
|
85
|
+
for (const entry of BROWSER_ENTRIES) {
|
|
86
|
+
test(`${entry} does not reach a Bun-only module`, async () => {
|
|
87
|
+
const reachable = await reachableFrom(entry);
|
|
88
|
+
|
|
89
|
+
const violations = BUN_ONLY_MODULES
|
|
90
|
+
.filter((module) => reachable.has(module))
|
|
91
|
+
.map((module) => `${module} via ${reachable.get(module)!.slice(-3).join(" -> ")}`);
|
|
92
|
+
|
|
93
|
+
expect(violations).toEqual([]);
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
});
|
|
@@ -1,62 +1,60 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import type { BrokerAdapter } from "../types/broker";
|
|
3
3
|
import type { BrokerInstanceConfig } from "../types/config";
|
|
4
|
-
import {
|
|
4
|
+
import { testBroker } from "./test-broker";
|
|
5
5
|
import {
|
|
6
6
|
buildBrokerProfileConfig,
|
|
7
7
|
createBrokerProfileDraft,
|
|
8
8
|
validateBrokerProfileValues,
|
|
9
9
|
} from "./profile-form";
|
|
10
10
|
|
|
11
|
-
function
|
|
11
|
+
function createSavedInstance(): BrokerInstanceConfig {
|
|
12
12
|
return {
|
|
13
|
-
id: "
|
|
14
|
-
brokerType: "
|
|
15
|
-
label: "
|
|
16
|
-
connectionMode: "
|
|
13
|
+
id: "saved-profile",
|
|
14
|
+
brokerType: "test-broker",
|
|
15
|
+
label: "Saved Profile",
|
|
16
|
+
connectionMode: "token",
|
|
17
17
|
config: {
|
|
18
|
-
connectionMode: "
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
gateway: { host: "127.0.0.1", marketDataType: "auto" },
|
|
18
|
+
connectionMode: "token",
|
|
19
|
+
credentials: { token: "saved-token", accountId: "123" },
|
|
20
|
+
host: "127.0.0.1",
|
|
22
21
|
},
|
|
23
22
|
enabled: true,
|
|
24
23
|
};
|
|
25
24
|
}
|
|
26
25
|
|
|
27
26
|
describe("broker profile form helpers", () => {
|
|
28
|
-
test("builds
|
|
29
|
-
const config = buildBrokerProfileConfig(
|
|
30
|
-
connectionMode: "
|
|
27
|
+
test("builds a nested broker config from flat form values", () => {
|
|
28
|
+
const config = buildBrokerProfileConfig(testBroker, {
|
|
29
|
+
connectionMode: "token",
|
|
31
30
|
token: "token",
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
gatewaySetupMode: "auto",
|
|
31
|
+
accountId: "456",
|
|
32
|
+
host: "",
|
|
35
33
|
});
|
|
36
34
|
|
|
37
35
|
expect(config).toMatchObject({
|
|
38
|
-
connectionMode: "
|
|
39
|
-
|
|
36
|
+
connectionMode: "token",
|
|
37
|
+
credentials: { token: "token", accountId: "456" },
|
|
40
38
|
});
|
|
41
39
|
});
|
|
42
40
|
|
|
43
41
|
test("preserves saved password fields when editing leaves them blank", () => {
|
|
44
|
-
const previous =
|
|
45
|
-
const draft = createBrokerProfileDraft(
|
|
42
|
+
const previous = createSavedInstance();
|
|
43
|
+
const draft = createBrokerProfileDraft(testBroker, previous);
|
|
46
44
|
draft.values.token = "";
|
|
47
45
|
|
|
48
|
-
expect(validateBrokerProfileValues(
|
|
49
|
-
expect(buildBrokerProfileConfig(
|
|
50
|
-
|
|
46
|
+
expect(validateBrokerProfileValues(testBroker, draft.values, previous)).toBeNull();
|
|
47
|
+
expect(buildBrokerProfileConfig(testBroker, draft.values, previous)).toMatchObject({
|
|
48
|
+
credentials: { token: "saved-token" },
|
|
51
49
|
});
|
|
52
50
|
});
|
|
53
51
|
|
|
54
52
|
test("requires password fields for new profiles", () => {
|
|
55
|
-
expect(validateBrokerProfileValues(
|
|
56
|
-
connectionMode: "
|
|
53
|
+
expect(validateBrokerProfileValues(testBroker, {
|
|
54
|
+
connectionMode: "token",
|
|
57
55
|
token: "",
|
|
58
|
-
|
|
59
|
-
})).toBe("
|
|
56
|
+
accountId: "123",
|
|
57
|
+
})).toBe("API Token is required.");
|
|
60
58
|
});
|
|
61
59
|
|
|
62
60
|
test("falls back to raw values for generic brokers", () => {
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { BrokerAdapter } from "../types/broker";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* A stand-in broker for tests of the generic broker machinery.
|
|
5
|
+
*
|
|
6
|
+
* These tests used to reach for the real IBKR adapter, which now lives in its
|
|
7
|
+
* own repository. Depending on a plugin to exercise host code is backwards
|
|
8
|
+
* anyway: it made a core test fail whenever that plugin's schema changed, and
|
|
9
|
+
* it hid which behaviour was actually under test.
|
|
10
|
+
*
|
|
11
|
+
* The shape mirrors what the machinery has to handle — a mode switch, a nested
|
|
12
|
+
* config, and a password field that must survive an edit left blank.
|
|
13
|
+
*/
|
|
14
|
+
export const testBroker: BrokerAdapter = {
|
|
15
|
+
id: "test-broker",
|
|
16
|
+
name: "Test Broker",
|
|
17
|
+
configSchema: [
|
|
18
|
+
{
|
|
19
|
+
key: "connectionMode",
|
|
20
|
+
label: "Connection",
|
|
21
|
+
type: "select",
|
|
22
|
+
options: [
|
|
23
|
+
{ value: "token", label: "Token" },
|
|
24
|
+
{ value: "local", label: "Local" },
|
|
25
|
+
],
|
|
26
|
+
defaultValue: "token",
|
|
27
|
+
},
|
|
28
|
+
{ key: "token", label: "API Token", type: "password", required: true, visibleWhen: { connectionMode: "token" } },
|
|
29
|
+
{ key: "accountId", label: "Account", type: "text", required: true, visibleWhen: { connectionMode: "token" } },
|
|
30
|
+
{ key: "host", label: "Host", type: "text", defaultValue: "127.0.0.1", visibleWhen: { connectionMode: "local" } },
|
|
31
|
+
],
|
|
32
|
+
|
|
33
|
+
async validate() {
|
|
34
|
+
return true;
|
|
35
|
+
},
|
|
36
|
+
|
|
37
|
+
async importPositions() {
|
|
38
|
+
return [];
|
|
39
|
+
},
|
|
40
|
+
|
|
41
|
+
getStatus() {
|
|
42
|
+
return { state: "disconnected", message: "", updatedAt: 0 };
|
|
43
|
+
},
|
|
44
|
+
|
|
45
|
+
getProfileActions() {
|
|
46
|
+
// The pane renders and invokes profile actions; one is enough to cover that.
|
|
47
|
+
return [{ id: "test-console", label: "Console", paneId: "ibkr-trading" }];
|
|
48
|
+
},
|
|
49
|
+
|
|
50
|
+
toConfigValues(instance) {
|
|
51
|
+
const config = instance.config as Record<string, unknown>;
|
|
52
|
+
const credentials = (config.credentials ?? {}) as Record<string, unknown>;
|
|
53
|
+
return {
|
|
54
|
+
connectionMode: String(config.connectionMode ?? "token"),
|
|
55
|
+
token: String(credentials.token ?? ""),
|
|
56
|
+
accountId: String(credentials.accountId ?? ""),
|
|
57
|
+
host: String(config.host ?? "127.0.0.1"),
|
|
58
|
+
};
|
|
59
|
+
},
|
|
60
|
+
|
|
61
|
+
fromConfigValues(values) {
|
|
62
|
+
return {
|
|
63
|
+
connectionMode: values.connectionMode,
|
|
64
|
+
credentials: { token: values.token, accountId: values.accountId },
|
|
65
|
+
host: values.host,
|
|
66
|
+
};
|
|
67
|
+
},
|
|
68
|
+
};
|
|
@@ -2,6 +2,7 @@ import { join } from "path";
|
|
|
2
2
|
import { existsSync, mkdirSync, readFileSync, readdirSync, rmSync } from "fs";
|
|
3
3
|
import { execFileSync } from "child_process";
|
|
4
4
|
import { getPluginsDir } from "../../plugins/loader";
|
|
5
|
+
import { linkHostPackages } from "../../plugins/host-link";
|
|
5
6
|
import {
|
|
6
7
|
cliStyles,
|
|
7
8
|
renderSection,
|
|
@@ -50,7 +51,20 @@ function parseGitHubRef(rawRef: string): { url: string; name: string } {
|
|
|
50
51
|
throw new Error(`Invalid plugin reference: ${ref}. Use user/repo or a GitHub URL.`);
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
export
|
|
54
|
+
export interface InstallPluginOptions {
|
|
55
|
+
/**
|
|
56
|
+
* Suppress child-process output and progress logging. The marketplace pane
|
|
57
|
+
* installs while the terminal UI owns the screen, and git and bun writing to
|
|
58
|
+
* stdout corrupts the rendered frame.
|
|
59
|
+
*/
|
|
60
|
+
quiet?: boolean;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function installPlugin(ref: string, options: InstallPluginOptions = {}) {
|
|
64
|
+
const stdio = options.quiet ? "pipe" : "inherit";
|
|
65
|
+
const say = (message: string) => {
|
|
66
|
+
if (!options.quiet) console.log(message);
|
|
67
|
+
};
|
|
54
68
|
ensurePluginsDir();
|
|
55
69
|
const { url, name } = parseGitHubRef(ref);
|
|
56
70
|
const targetDir = join(PLUGINS_DIR, name);
|
|
@@ -59,11 +73,11 @@ export async function installPlugin(ref: string) {
|
|
|
59
73
|
fail(`Plugin "${name}" already exists.`, `Use "gloomberb update ${name}" to refresh it.`);
|
|
60
74
|
}
|
|
61
75
|
|
|
62
|
-
|
|
63
|
-
|
|
76
|
+
say(cliStyles.accent(`Installing ${name}`));
|
|
77
|
+
say(cliStyles.muted(url));
|
|
64
78
|
|
|
65
79
|
try {
|
|
66
|
-
execFileSync("git", ["clone", "--depth", "1", url, targetDir], { stdio
|
|
80
|
+
execFileSync("git", ["clone", "--depth", "1", url, targetDir], { stdio });
|
|
67
81
|
} catch {
|
|
68
82
|
rmSync(targetDir, { recursive: true, force: true });
|
|
69
83
|
fail(`Failed to clone ${url}.`);
|
|
@@ -71,11 +85,24 @@ export async function installPlugin(ref: string) {
|
|
|
71
85
|
|
|
72
86
|
const pkgPath = join(targetDir, "package.json");
|
|
73
87
|
if (existsSync(pkgPath)) {
|
|
74
|
-
|
|
88
|
+
say(cliStyles.muted("Installing plugin dependencies..."));
|
|
75
89
|
try {
|
|
76
|
-
|
|
90
|
+
// --production: plugin repos depend on `gloomberb` as a devDependency so
|
|
91
|
+
// their own CI can typecheck against the real API. At runtime the host is
|
|
92
|
+
// symlinked in instead, and pulling a second full copy here would both
|
|
93
|
+
// waste a lot of disk and risk a duplicate React.
|
|
94
|
+
execFileSync("bun", ["install", "--production"], { cwd: targetDir, stdio });
|
|
77
95
|
} catch {
|
|
78
|
-
console.error(cliStyles.warning("Warning: failed to install plugin dependencies."));
|
|
96
|
+
if (!options.quiet) console.error(cliStyles.warning("Warning: failed to install plugin dependencies."));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// After `bun install`, which prunes links it does not know about.
|
|
101
|
+
const link = linkHostPackages(targetDir);
|
|
102
|
+
if (link.error) {
|
|
103
|
+
if (!options.quiet) {
|
|
104
|
+
console.error(cliStyles.warning(`Warning: could not link the Gloomberb runtime (${link.error}).`));
|
|
105
|
+
console.error(cliStyles.muted("The plugin's \"gloomberb/*\" imports will not resolve."));
|
|
79
106
|
}
|
|
80
107
|
}
|
|
81
108
|
|
|
@@ -98,13 +125,13 @@ export async function installPlugin(ref: string) {
|
|
|
98
125
|
const mod = await import(entryFile);
|
|
99
126
|
const plugin = mod.default ?? mod.plugin;
|
|
100
127
|
if (plugin?.id && plugin?.name) {
|
|
101
|
-
|
|
128
|
+
say(cliStyles.success(`Installed ${plugin.name} v${plugin.version || "0.0.0"}`));
|
|
102
129
|
return;
|
|
103
130
|
}
|
|
104
131
|
}
|
|
105
|
-
|
|
132
|
+
say(cliStyles.warning("Installed files, but no valid GloomPlugin export was found."));
|
|
106
133
|
} catch (err) {
|
|
107
|
-
|
|
134
|
+
say(cliStyles.warning(`Plugin validation failed: ${err}`));
|
|
108
135
|
}
|
|
109
136
|
}
|
|
110
137
|
|
|
@@ -141,8 +168,9 @@ export async function updatePlugins(name?: string) {
|
|
|
141
168
|
execFileSync("git", ["pull", "--ff-only"], { cwd: targetDir, stdio: "inherit" });
|
|
142
169
|
const pkgPath = join(targetDir, "package.json");
|
|
143
170
|
if (existsSync(pkgPath)) {
|
|
144
|
-
execFileSync("bun", ["install"], { cwd: targetDir, stdio: "inherit" });
|
|
171
|
+
execFileSync("bun", ["install", "--production"], { cwd: targetDir, stdio: "inherit" });
|
|
145
172
|
}
|
|
173
|
+
linkHostPackages(targetDir);
|
|
146
174
|
} catch {
|
|
147
175
|
console.error(cliStyles.danger(`Failed to update ${dir}.`));
|
|
148
176
|
}
|