gloomberb 0.11.0 → 0.11.2
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 +38 -15
- package/README.zh-CN.md +12 -12
- package/package.json +10 -3
- package/src/api-client/auth.ts +14 -10
- package/src/api-client/data.ts +54 -0
- package/src/api-client/index.test.ts +124 -0
- package/src/api-client/index.ts +139 -4
- package/src/api-client/paths.ts +89 -0
- package/src/api-client/request.ts +15 -2
- package/src/api-client/socket-health.test.ts +1 -0
- package/src/api-client/socket.ts +2 -1
- package/src/api-client/types.ts +163 -0
- package/src/app/pane-runtime/index.tsx +4 -1
- package/src/app/pane-runtime/plugin-bindings.test.ts +117 -0
- package/src/app/pane-runtime/plugin-bindings.ts +25 -4
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +25 -14
- package/src/app/runtime/desktop-deeplink.test.ts +16 -0
- package/src/app/runtime/desktop-deeplink.ts +66 -2
- package/src/app/runtime/desktop-menu.ts +3 -0
- package/src/app/runtime/plugin-bindings.ts +30 -28
- package/src/app/runtime/update.ts +8 -6
- package/src/app.tsx +21 -7
- package/src/architecture/import-boundaries.test.ts +5 -1
- package/src/capabilities/factories.ts +4 -0
- package/src/cli/commands/automation.test.ts +9 -0
- package/src/cli/commands/automation.ts +22 -73
- package/src/cli/index.ts +10 -6
- package/src/cli/registry.ts +1 -1
- package/src/components/chart/composite/composite-chart.test.tsx +45 -2
- package/src/components/chart/composite/composite-chart.tsx +16 -12
- package/src/components/chart/composite/format.test.ts +34 -0
- package/src/components/chart/composite/format.ts +33 -10
- package/src/components/chart/composite/scene.ts +0 -21
- package/src/components/chart/composite/tools.ts +49 -0
- package/src/components/chart/core/axis-format.ts +13 -3
- package/src/components/chart/core/index.test.ts +7 -0
- package/src/components/chart/core/renderer.ts +3 -4
- package/src/components/command-bar/commands/direct/index.ts +4 -0
- package/src/components/command-bar/commands/registry.ts +8 -1
- package/src/components/command-bar/layout-items/current-layout.ts +1 -1
- package/src/components/command-bar/pane-templates/workflow.ts +4 -0
- package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -0
- package/src/components/command-bar/routes/root/shortcuts.test.ts +19 -0
- package/src/components/command-bar/routes/root/shortcuts.ts +3 -1
- package/src/components/command-bar/surface/index.test.tsx +6 -6
- package/src/components/command-bar/surface/index.tsx +4 -1
- package/src/components/command-bar/surface/pane.test.tsx +66 -2
- package/src/components/command-bar/view-model.test.ts +2 -1
- package/src/components/command-bar/workflow/ops.test.ts +54 -0
- package/src/components/command-bar/workflow/ops.ts +1 -1
- package/src/components/company-logo.test.ts +19 -0
- package/src/components/company-logo.tsx +60 -0
- package/src/components/feed-data-table/stack-view.tsx +7 -1
- package/src/components/index.ts +1 -0
- package/src/components/layout/detached-pane-shell.tsx +85 -15
- package/src/components/layout/header.tsx +5 -5
- package/src/components/layout/pane/title.ts +31 -3
- package/src/components/layout/shell/active-drag.ts +4 -11
- package/src/components/layout/shell/drag/index.ts +4 -20
- package/src/components/layout/shell/drag/runtime.ts +3 -4
- package/src/components/layout/shell/index.test.tsx +17 -3
- package/src/components/layout/shell/index.tsx +89 -31
- package/src/components/layout/shell/menu.test.ts +37 -1
- package/src/components/layout/shell/menu.ts +14 -0
- package/src/components/layout/shell/native/pointer-runtime.ts +22 -8
- package/src/components/layout/shell/pane/actions.ts +0 -5
- package/src/components/layout/shell/pane/management-shortcuts.ts +9 -4
- package/src/components/layout/shell/shortcuts.ts +6 -3
- package/src/components/layout/shell/ticker-link-menu.test.ts +82 -0
- package/src/components/layout/shell/ticker-link-menu.ts +78 -0
- package/src/components/layout/status-bar.test.tsx +62 -61
- package/src/components/layout/status-bar.tsx +53 -75
- package/src/components/onboarding/account-step/form-panel.tsx +4 -2
- package/src/components/onboarding/account-step/index.tsx +1 -1
- package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
- package/src/components/onboarding/onboarding-wizard.test.tsx +44 -1
- package/src/components/onboarding/onboarding-wizard.tsx +13 -5
- package/src/components/onboarding/wizard-account.ts +4 -23
- package/src/components/table-view-shared.test.ts +11 -0
- package/src/components/table-view-shared.tsx +26 -0
- package/src/components/ui/data-table/index.tsx +19 -1
- package/src/components/ui/fields.tsx +5 -1
- package/src/core/state/app/layout-reducer.ts +36 -0
- package/src/core/state/app/layout.ts +10 -5
- package/src/core/state/app/state.ts +0 -13
- package/src/core/state/app/types.ts +6 -4
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/config/layout.ts +2 -0
- package/src/data/config/store/index.test.ts +27 -6
- package/src/data/config/store/normalize.ts +3 -5
- package/src/data/plugin-state-store.ts +11 -0
- package/src/data/sqlite/cache.test.ts +10 -0
- package/src/i18n/es.ts +13 -5
- package/src/i18n/ja.ts +13 -5
- package/src/i18n/ko.ts +13 -5
- package/src/i18n/zh-cn.ts +13 -5
- package/src/i18n/zh-tw.ts +13 -5
- package/src/layout-marketplace/api.test.ts +54 -0
- package/src/layout-marketplace/api.ts +65 -0
- package/src/layout-marketplace/gallery-desktop.test.tsx +262 -0
- package/src/layout-marketplace/gallery-desktop.tsx +375 -0
- package/src/layout-marketplace/gallery-terminal.tsx +376 -0
- package/src/layout-marketplace/gallery.test.tsx +84 -0
- package/src/layout-marketplace/gallery.tsx +323 -0
- package/src/layout-marketplace/geometry.test.ts +47 -0
- package/src/layout-marketplace/geometry.ts +80 -0
- package/src/layout-marketplace/mini-workspace.test.tsx +122 -0
- package/src/layout-marketplace/mini-workspace.tsx +428 -0
- package/src/layout-marketplace/model.test.ts +118 -0
- package/src/layout-marketplace/model.ts +171 -0
- package/src/layout-marketplace/name-dialog.tsx +60 -0
- package/src/layout-marketplace/pane-imagery.ts +85 -0
- package/src/layout-marketplace/pane.tsx +20 -0
- package/src/layout-marketplace/payload.test.ts +246 -0
- package/src/layout-marketplace/payload.ts +624 -0
- package/src/layout-marketplace/use-marketplace.ts +63 -0
- package/src/market-data/coordinator/index.test.ts +2 -2
- package/src/market-data/coordinator/index.ts +1 -1
- package/src/market-data/hooks.tsx +0 -41
- package/src/market-data/market/format.test.ts +7 -0
- package/src/market-data/market/freshness.ts +5 -2
- package/src/market-data/quotes/freshness.test.ts +16 -0
- package/src/news/aggregator.test.ts +92 -3
- package/src/news/aggregator.ts +98 -18
- package/src/news/hooks.test.tsx +2 -0
- package/src/news/hooks.ts +27 -1
- package/src/news/news-model.ts +5 -2
- package/src/news/types.ts +7 -0
- package/src/plugins/broker-sync/broker-sync.test.ts +58 -0
- package/src/plugins/broker-sync/normalize.ts +42 -5
- package/src/plugins/builtin/account-management/pane.tsx +5 -5
- package/src/plugins/builtin/ai/index.tsx +18 -2
- package/src/plugins/builtin/ai/screener/pane.test.tsx +47 -4
- package/src/plugins/builtin/ai/screener/pane.tsx +35 -12
- package/src/plugins/builtin/alerts/alert-engine.test.ts +53 -1
- package/src/plugins/builtin/alerts/alert-engine.ts +34 -1
- package/src/plugins/builtin/alerts/index.test.tsx +49 -6
- package/src/plugins/builtin/alerts/index.tsx +11 -6
- package/src/plugins/builtin/alerts/pane.tsx +51 -9
- package/src/plugins/builtin/alerts/quotes.ts +11 -3
- package/src/plugins/builtin/alerts/types.ts +1 -0
- package/src/plugins/builtin/analytics/index.tsx +11 -1
- package/src/plugins/builtin/analytics/pane-model.ts +6 -4
- package/src/plugins/builtin/broker-manager/index.tsx +3 -0
- package/src/plugins/builtin/buildout/model/index.ts +1 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +17 -18
- package/src/plugins/builtin/cds/client.test.ts +83 -0
- package/src/plugins/builtin/cds/client.ts +67 -0
- package/src/plugins/builtin/cds/index.tsx +41 -0
- package/src/plugins/builtin/cds/model.test.ts +232 -0
- package/src/plugins/builtin/cds/model.ts +401 -0
- package/src/plugins/builtin/cds/pane.test.tsx +132 -0
- package/src/plugins/builtin/cds/pane.tsx +358 -0
- package/src/plugins/builtin/chart-composer/chart-spec.ts +31 -0
- package/src/plugins/builtin/chart-composer/index.tsx +88 -2
- package/src/plugins/builtin/chart-composer/pane-share.test.ts +71 -0
- package/src/plugins/builtin/chart-composer/pane.tsx +103 -15
- package/src/plugins/builtin/chat/content/index.tsx +4 -1
- package/src/plugins/builtin/chat/controller/channels.ts +7 -1
- package/src/plugins/builtin/chat/controller/index.ts +14 -9
- package/src/plugins/builtin/chat/controller/notifications.ts +13 -3
- package/src/plugins/builtin/chat/controller/persistence.ts +0 -3
- package/src/plugins/builtin/chat/controller/realtime.ts +3 -3
- package/src/plugins/builtin/chat/controller/send.ts +1 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +18 -11
- package/src/plugins/builtin/chat/controller/state.ts +1 -3
- package/src/plugins/builtin/chat/controller/storage.ts +0 -2
- package/src/plugins/builtin/chat/controller/utils.ts +4 -6
- package/src/plugins/builtin/chat/controller/view.ts +2 -2
- package/src/plugins/builtin/chat/controller.test.ts +142 -14
- package/src/plugins/builtin/chat/message/desktop.test.tsx +88 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +3 -1
- package/src/plugins/builtin/chat/message/profile-popover.test.tsx +1 -29
- package/src/plugins/builtin/chat/profile-popover.ts +53 -22
- package/src/plugins/builtin/chat/sidebar.test.tsx +81 -0
- package/src/plugins/builtin/chat/sidebar.tsx +63 -0
- package/src/plugins/builtin/chat/test-harness.tsx +29 -1
- package/src/plugins/builtin/cloud/auth-actions.tsx +14 -7
- package/src/plugins/builtin/cloud/auth-commands.ts +21 -51
- package/src/plugins/builtin/cloud/auth-dialog.tsx +296 -0
- package/src/{components/onboarding/account-step/model.test.ts → plugins/builtin/cloud/auth-model.test.ts} +1 -1
- package/src/{components/onboarding/account-step/model.ts → plugins/builtin/cloud/auth-model.ts} +37 -3
- package/src/plugins/builtin/cloud/browser.tsx +9 -0
- package/src/plugins/builtin/cloud/buildout-module.tsx +23 -0
- package/src/plugins/builtin/cloud/device-signin.ts +2 -8
- package/src/plugins/builtin/cloud/index.tsx +2 -0
- package/src/plugins/builtin/cloud/plugin.tsx +14 -24
- package/src/plugins/builtin/cloud/upgrade-command.ts +13 -6
- package/src/plugins/builtin/cloud-tweets/pane-share.test.ts +56 -0
- package/src/plugins/builtin/cloud-tweets/pane.tsx +20 -5
- package/src/plugins/builtin/cloud-tweets/registration.ts +50 -6
- package/src/plugins/builtin/composite-plugins.ts +3 -1
- package/src/plugins/builtin/congress-trades/detail.tsx +43 -1
- package/src/plugins/builtin/congress-trades/model.test.ts +85 -0
- package/src/plugins/builtin/congress-trades/model.ts +69 -1
- package/src/plugins/builtin/congress-trades/pane.tsx +51 -2
- package/src/plugins/builtin/connections/index.ts +3 -0
- package/src/plugins/builtin/dividend-yield/client.ts +18 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +7 -6
- package/src/plugins/builtin/earnings/index.tsx +5 -3
- package/src/plugins/builtin/earnings/model.test.ts +19 -1
- package/src/plugins/builtin/earnings/model.ts +17 -15
- package/src/plugins/builtin/econ/calendar-model.ts +0 -5
- package/src/plugins/builtin/econ/index.test.tsx +6 -1
- package/src/plugins/builtin/fear-greed/charts.tsx +12 -8
- package/src/plugins/builtin/help/index.tsx +2 -2
- package/src/plugins/builtin/holders/thirteenf-match.ts +1 -2
- package/src/plugins/builtin/insider/index.tsx +27 -11
- package/src/plugins/builtin/kelly-sizer/index.tsx +4 -1
- package/src/plugins/builtin/layout-manager/index.tsx +17 -4
- package/src/plugins/builtin/news/index.tsx +11 -6
- package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +2 -0
- package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +2 -0
- package/src/plugins/builtin/news/wire/breaking/pane.tsx +4 -1
- package/src/plugins/builtin/news/wire/feed-config.test.ts +15 -1
- package/src/plugins/builtin/news/wire/feed-config.ts +25 -5
- package/src/plugins/builtin/news/wire/index.tsx +3 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +7 -2
- package/src/plugins/builtin/news/wire/news/footer.ts +34 -1
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +4 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +8 -2
- package/src/plugins/builtin/news/wire/rss/source.test.ts +12 -0
- package/src/plugins/builtin/news/wire/rss/source.ts +1 -1
- package/src/plugins/builtin/notes/files.ts +80 -0
- package/src/plugins/builtin/notes/index.tsx +3 -0
- package/src/plugins/builtin/notes/sync.test.ts +49 -0
- package/src/plugins/builtin/notes/sync.ts +98 -0
- package/src/plugins/builtin/options/index.tsx +1 -0
- package/src/plugins/builtin/options/view.test.tsx +25 -3
- package/src/plugins/builtin/options/view.tsx +1 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.test.ts +22 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +12 -7
- package/src/plugins/builtin/portfolio-list/cli/render.ts +5 -3
- package/src/plugins/builtin/portfolio-list/column-values.ts +15 -4
- package/src/plugins/builtin/portfolio-list/header.tsx +13 -3
- package/src/plugins/builtin/portfolio-list/index.tsx +8 -0
- package/src/plugins/builtin/portfolio-list/metrics.test.ts +31 -1
- package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +1 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +10 -7
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +6 -1
- package/src/plugins/builtin/portfolio-list/position-metrics.ts +25 -6
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +35 -21
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +20 -6
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -12
- package/src/plugins/builtin/research/equity-diagnostic-pane.test.tsx +329 -0
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +582 -0
- package/src/plugins/builtin/research/index.test.ts +14 -0
- package/src/plugins/builtin/research/index.tsx +29 -0
- package/src/plugins/builtin/sec/index.tsx +24 -9
- package/src/plugins/builtin/sectors/index.tsx +14 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +18 -11
- package/src/plugins/builtin/shared/public-pane.test.ts +12 -0
- package/src/plugins/builtin/shared/public-pane.ts +9 -0
- package/src/plugins/builtin/shared/public-share.ts +23 -0
- package/src/plugins/builtin/shared/ticker-source.ts +32 -0
- package/src/plugins/builtin/shared/ticker-surface.test.ts +27 -0
- package/src/plugins/builtin/shared/ticker-surface.ts +17 -0
- package/src/plugins/builtin/substack/api/cache.ts +7 -3
- package/src/plugins/builtin/substack/api/loaders.ts +36 -12
- package/src/plugins/builtin/substack/api/types.ts +2 -0
- package/src/plugins/builtin/substack/pane-state.ts +1 -1
- package/src/plugins/builtin/substack/pane.tsx +55 -12
- package/src/plugins/builtin/thirteenf/api.test.ts +1 -1
- package/src/plugins/builtin/thirteenf/api.ts +40 -18
- package/src/plugins/builtin/thirteenf/data.ts +15 -7
- package/src/plugins/builtin/thirteenf/pane.tsx +45 -10
- package/src/plugins/builtin/ticker-detail/index.tsx +10 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +12 -4
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +8 -2
- package/src/plugins/builtin/ticker-detail/settings.ts +12 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +2 -0
- package/src/plugins/builtin/world-indices/index.tsx +2 -0
- package/src/plugins/catalog-browser.test.ts +78 -0
- package/src/plugins/catalog-browser.ts +136 -0
- package/src/plugins/gridlock-notification.ts +2 -1
- package/src/plugins/ibkr/flex/index.test.ts +19 -0
- package/src/plugins/ibkr/flex/index.ts +1 -1
- package/src/plugins/pane-manager/gridlock.ts +87 -3
- package/src/plugins/pane-manager.test.ts +30 -0
- package/src/plugins/pane-manager.ts +6 -1
- package/src/plugins/prediction-markets/chart.tsx +2 -2
- package/src/plugins/prediction-markets/controller/catalog.ts +77 -2
- package/src/plugins/prediction-markets/controller/data.ts +7 -1
- package/src/plugins/prediction-markets/controller/index.ts +9 -2
- package/src/plugins/prediction-markets/controller/keyboard.ts +10 -14
- package/src/plugins/prediction-markets/detail/overview.tsx +21 -0
- package/src/plugins/prediction-markets/detail/pane.tsx +43 -34
- package/src/plugins/prediction-markets/detail/trades.tsx +34 -9
- package/src/plugins/prediction-markets/detail.test.tsx +29 -2
- package/src/plugins/prediction-markets/index.tsx +8 -4
- package/src/plugins/prediction-markets/navigation.ts +0 -1
- package/src/plugins/prediction-markets/pane.test.tsx +77 -0
- package/src/plugins/prediction-markets/pane.tsx +12 -0
- package/src/plugins/prediction-markets/plugin.test.tsx +36 -0
- package/src/plugins/prediction-markets/services/kalshi/adapter.ts +49 -9
- package/src/plugins/prediction-markets/services/polymarket/adapter.ts +31 -0
- package/src/plugins/prediction-markets/test-helpers.tsx +9 -0
- package/src/plugins/registry/index.ts +2 -0
- package/src/plugins/ticker-navigation.test.ts +24 -0
- package/src/plugins/ticker-navigation.ts +40 -0
- package/src/remote/schema.ts +1 -1
- package/src/renderers/browser/app-services.ts +61 -0
- package/src/renderers/browser/cloud-transport.test.ts +68 -0
- package/src/renderers/browser/cloud-transport.ts +48 -0
- package/src/renderers/browser/config-host.ts +58 -0
- package/src/renderers/browser/deeplink-bridge.test.ts +41 -0
- package/src/renderers/browser/deeplink-bridge.ts +22 -0
- package/src/renderers/browser/error-boundary.tsx +23 -0
- package/src/renderers/browser/main.tsx +64 -0
- package/src/renderers/browser/persistence.ts +74 -0
- package/src/renderers/browser/storage.test.ts +66 -0
- package/src/renderers/browser/storage.ts +50 -0
- package/src/renderers/browser/ticker-repository.ts +59 -0
- package/src/renderers/browser/ui-host.tsx +40 -0
- package/src/renderers/cloudflare/worker.test.ts +136 -0
- package/src/renderers/cloudflare/worker.ts +116 -0
- package/src/renderers/electrobun/bun/application-menu/index.ts +3 -2
- package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/plugin-state.ts +7 -1
- package/src/renderers/electrobun/view/app-services.ts +14 -3
- package/src/renderers/electrobun/view/build-assets.ts +12 -10
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/data-table/index.tsx +10 -1
- package/src/renderers/electrobun/view/desktop/controls.tsx +2 -0
- package/src/renderers/electrobun/view/dom-ui-host.tsx +143 -0
- package/src/renderers/electrobun/view/host/box.test.tsx +33 -0
- package/src/renderers/electrobun/view/host/box.tsx +11 -6
- package/src/renderers/electrobun/view/host/chart-surface.tsx +7 -1
- package/src/renderers/electrobun/view/host/input.tsx +11 -7
- package/src/renderers/electrobun/view/host/native.ts +0 -5
- package/src/renderers/electrobun/view/main.tsx +2 -0
- package/src/renderers/electrobun/view/remote/persistence.ts +5 -0
- package/src/renderers/electrobun/view/styles.css +107 -7
- package/src/renderers/electrobun/view/toast-host.test.tsx +78 -0
- package/src/renderers/electrobun/view/toast-host.tsx +69 -43
- package/src/renderers/electrobun/view/ui-host.tsx +6 -137
- package/src/renderers/opentui/start.tsx +2 -0
- package/src/renderers/share/layout-view.test.tsx +58 -0
- package/src/renderers/share/layout-view.tsx +133 -0
- package/src/renderers/share/main.tsx +102 -0
- package/src/renderers/share/styles.css +54 -0
- package/src/renderers/share/view.test.tsx +100 -0
- package/src/renderers/share/view.tsx +150 -0
- package/src/shares/api.test.ts +105 -0
- package/src/shares/api.ts +127 -0
- package/src/shares/chart-snapshot.test.ts +34 -0
- package/src/shares/chart-snapshot.ts +28 -0
- package/src/shares/location.test.ts +19 -0
- package/src/shares/location.ts +11 -0
- package/src/shares/pane.test.ts +110 -0
- package/src/shares/pane.ts +54 -0
- package/src/shares/payload.ts +177 -0
- package/src/sources/gloomberb-cloud/index.test.ts +61 -1
- package/src/sources/gloomberb-cloud/index.ts +86 -14
- package/src/sources/gloomberb-cloud/normalizers.test.ts +34 -0
- package/src/sources/gloomberb-cloud/normalizers.ts +5 -5
- package/src/sources/provider-router/history.test.ts +2 -2
- package/src/sources/provider-router/news.test.ts +34 -0
- package/src/sources/provider-router/news.ts +15 -1
- package/src/sources/sec-edgar.test.ts +59 -0
- package/src/sources/sec-edgar.ts +63 -23
- package/src/sources/yahoo-finance/mappers.ts +3 -0
- package/src/sources/yahoo-finance/snapshots.ts +10 -2
- package/src/sources/yahoo-finance/symbols.test.ts +10 -0
- package/src/sources/yahoo-finance/symbols.ts +13 -8
- package/src/sources/yahoo-finance.test.ts +1 -1
- package/src/sources/yahoo-finance.ts +6 -2
- package/src/state/app/bootstrap.ts +6 -7
- package/src/state/app/context/index.test.ts +41 -1
- package/src/state/app/context/index.test.tsx +34 -16
- package/src/state/selectors-ui.ts +0 -8
- package/src/sync/baseline.ts +32 -0
- package/src/sync/controller.test.ts +124 -1
- package/src/sync/controller.ts +54 -12
- package/src/sync/core-contributors.test.ts +44 -0
- package/src/sync/core-contributors.ts +59 -10
- package/src/sync/react.ts +22 -2
- package/src/sync/types.ts +12 -0
- package/src/theme/font-scale.ts +0 -11
- package/src/tickers/search/index.test.ts +27 -0
- package/src/tickers/search/upsert.ts +10 -0
- package/src/types/capability-route-source.ts +2 -1
- package/src/types/config.ts +35 -5
- package/src/types/desktop-menu.ts +1 -0
- package/src/types/plugin.ts +36 -0
- package/src/ui/context-menu.tsx +1 -1
- package/src/ui/host.tsx +4 -0
- package/src/ui/toast.tsx +2 -0
- package/src/utils/back-navigation.ts +0 -4
- package/src/utils/price-history.ts +11 -10
- package/src/utils/sec.test.ts +6 -0
- package/src/utils/sec.ts +1 -1
|
@@ -13,6 +13,7 @@ import { VERSION } from "../../version";
|
|
|
13
13
|
const UPDATE_CHECK_INTERVAL_MS = 60 * 60_000; // hourly
|
|
14
14
|
|
|
15
15
|
export function useAppUpdateRuntime({
|
|
16
|
+
enabled = true,
|
|
16
17
|
dispatch,
|
|
17
18
|
isDetachedWindow,
|
|
18
19
|
pluginRegistry,
|
|
@@ -21,6 +22,7 @@ export function useAppUpdateRuntime({
|
|
|
21
22
|
updateCheckInProgress,
|
|
22
23
|
updateProgress,
|
|
23
24
|
}: {
|
|
25
|
+
enabled?: boolean;
|
|
24
26
|
dispatch: Dispatch<AppAction>;
|
|
25
27
|
isDetachedWindow: boolean;
|
|
26
28
|
pluginRegistry: PluginRegistry;
|
|
@@ -76,15 +78,15 @@ export function useAppUpdateRuntime({
|
|
|
76
78
|
}, [dispatch]);
|
|
77
79
|
|
|
78
80
|
useEffect(() => {
|
|
79
|
-
if (isDetachedWindow) return;
|
|
81
|
+
if (!enabled || isDetachedWindow) return;
|
|
80
82
|
void runUpdateCheck(false);
|
|
81
83
|
const interval = setInterval(() => { void runUpdateCheck(false); }, UPDATE_CHECK_INTERVAL_MS);
|
|
82
84
|
return () => { clearInterval(interval); };
|
|
83
|
-
}, [isDetachedWindow, runUpdateCheck]);
|
|
85
|
+
}, [enabled, isDetachedWindow, runUpdateCheck]);
|
|
84
86
|
|
|
85
87
|
// First launch on a new version: show that release's notes, then remember the version.
|
|
86
88
|
useEffect(() => {
|
|
87
|
-
if (isDetachedWindow) return;
|
|
89
|
+
if (!enabled || isDetachedWindow) return;
|
|
88
90
|
const config = stateRef.current.config;
|
|
89
91
|
if (config.lastLaunchedVersion === VERSION) return;
|
|
90
92
|
const isUpgrade = !!config.lastLaunchedVersion;
|
|
@@ -95,14 +97,14 @@ export function useAppUpdateRuntime({
|
|
|
95
97
|
void pluginRegistry.createPaneFromTemplateAsyncFn("changelog-pane", {
|
|
96
98
|
values: { version: VERSION },
|
|
97
99
|
}).catch(() => {});
|
|
98
|
-
}, [dispatch, isDetachedWindow, pluginRegistry, stateRef]);
|
|
100
|
+
}, [dispatch, enabled, isDetachedWindow, pluginRegistry, stateRef]);
|
|
99
101
|
|
|
100
102
|
useEffect(() => {
|
|
101
|
-
if (isDetachedWindow) return;
|
|
103
|
+
if (!enabled || isDetachedWindow) return;
|
|
102
104
|
if (!updateAvailable || updateProgress || updateCheckInProgress) return;
|
|
103
105
|
if (!canSelfUpdate(updateAvailable)) return;
|
|
104
106
|
startUpdate(updateAvailable);
|
|
105
|
-
}, [isDetachedWindow, startUpdate, updateAvailable, updateCheckInProgress, updateProgress]);
|
|
107
|
+
}, [enabled, isDetachedWindow, startUpdate, updateAvailable, updateCheckInProgress, updateProgress]);
|
|
106
108
|
|
|
107
109
|
return {
|
|
108
110
|
runUpdateCheck,
|
package/src/app.tsx
CHANGED
|
@@ -24,7 +24,7 @@ import type { AppServicesFactory, AppTickerRepositoryPort } from "./core/app-ser
|
|
|
24
24
|
import { useThemeColors } from "./theme/theme-context";
|
|
25
25
|
import type { AppConfig } from "./types/config";
|
|
26
26
|
import type { DesktopDeepLinkBridge } from "./types/desktop-deeplink";
|
|
27
|
-
import type { CliLaunchRequest } from "./types/plugin";
|
|
27
|
+
import type { CliLaunchRequest, GloomPlugin } from "./types/plugin";
|
|
28
28
|
import type { DataProvider } from "./types/data-provider";
|
|
29
29
|
import type { DesktopDockPreviewState, DesktopSharedStateSnapshot, DesktopThemePreviewState, DesktopWindowBridge } from "./types/desktop-window";
|
|
30
30
|
import type { DesktopApplicationMenuBridge } from "./types/desktop-menu";
|
|
@@ -32,7 +32,6 @@ import type { LayoutBounds } from "./plugins/pane-manager";
|
|
|
32
32
|
import type { AppSessionSnapshot } from "./core/state/session-persistence";
|
|
33
33
|
import type { MarketDataCoordinator } from "./market-data/coordinator";
|
|
34
34
|
import { createAppNotifier } from "./notifications/app-notifier";
|
|
35
|
-
import { getLoadablePlugins } from "./plugins/catalog";
|
|
36
35
|
import { useBrokerImportRuntime } from "./app/runtime/broker-import";
|
|
37
36
|
import { useDesktopDeepLinkRuntime } from "./app/runtime/desktop-deeplink";
|
|
38
37
|
import { useDesktopApplicationMenuRuntime } from "./app/runtime/desktop-menu";
|
|
@@ -55,6 +54,7 @@ import { scheduleConfigSave } from "./state/config-save-scheduler";
|
|
|
55
54
|
import { measurePerf } from "./utils/perf-marks";
|
|
56
55
|
import { useAppLanguage } from "./i18n/react";
|
|
57
56
|
import { AppLanguageConfigObserver } from "./app/language-observer";
|
|
57
|
+
import { isPaneShareHandoff } from "./shares/location";
|
|
58
58
|
|
|
59
59
|
const EMPTY_EXTERNAL_PLUGINS: LoadedExternalPlugin[] = [];
|
|
60
60
|
|
|
@@ -68,6 +68,7 @@ interface AppInnerProps {
|
|
|
68
68
|
desktopApplicationMenuBridge?: DesktopApplicationMenuBridge;
|
|
69
69
|
desktopDeepLinkBridge?: DesktopDeepLinkBridge;
|
|
70
70
|
remoteControlAdapter?: RemoteControlAdapter;
|
|
71
|
+
updatesEnabled?: boolean;
|
|
71
72
|
onboardingActive?: boolean;
|
|
72
73
|
onOnboardingComplete?: (config: AppConfig) => void | Promise<void>;
|
|
73
74
|
}
|
|
@@ -100,6 +101,7 @@ function AppInner({
|
|
|
100
101
|
desktopApplicationMenuBridge,
|
|
101
102
|
desktopDeepLinkBridge,
|
|
102
103
|
remoteControlAdapter,
|
|
104
|
+
updatesEnabled = true,
|
|
103
105
|
onboardingActive = false,
|
|
104
106
|
onOnboardingComplete,
|
|
105
107
|
}: AppInnerProps) {
|
|
@@ -157,7 +159,9 @@ function AppInner({
|
|
|
157
159
|
const type = notification.type ?? "info";
|
|
158
160
|
let toastId: string | number | undefined;
|
|
159
161
|
const options = {
|
|
160
|
-
|
|
162
|
+
title: notification.title,
|
|
163
|
+
subtitle: notification.subtitle,
|
|
164
|
+
duration: notification.persistent ? 0 : notification.duration,
|
|
161
165
|
action: notification.action
|
|
162
166
|
? {
|
|
163
167
|
label: notification.action.label,
|
|
@@ -221,6 +225,7 @@ function AppInner({
|
|
|
221
225
|
});
|
|
222
226
|
|
|
223
227
|
const { runUpdateCheck, startUpdate } = useAppUpdateRuntime({
|
|
228
|
+
enabled: updatesEnabled,
|
|
224
229
|
dispatch,
|
|
225
230
|
isDetachedWindow,
|
|
226
231
|
pluginRegistry,
|
|
@@ -244,6 +249,7 @@ function AppInner({
|
|
|
244
249
|
desktopDeepLinkBridge,
|
|
245
250
|
desktopWindowKind: desktopWindowBridge?.kind,
|
|
246
251
|
dispatch,
|
|
252
|
+
initialized: state.initialized,
|
|
247
253
|
pluginRegistry,
|
|
248
254
|
stateRef,
|
|
249
255
|
});
|
|
@@ -273,7 +279,7 @@ function AppInner({
|
|
|
273
279
|
importBrokerPositions,
|
|
274
280
|
marketData,
|
|
275
281
|
pluginRegistry,
|
|
276
|
-
|
|
282
|
+
stateRef,
|
|
277
283
|
tickerRepository,
|
|
278
284
|
});
|
|
279
285
|
|
|
@@ -283,6 +289,7 @@ function AppInner({
|
|
|
283
289
|
dispatch,
|
|
284
290
|
tickerRepository,
|
|
285
291
|
pluginRegistry,
|
|
292
|
+
appActive,
|
|
286
293
|
// Keep a first-run workspace stable while the local guide is active. Once
|
|
287
294
|
// onboarding finishes, the normal pull-before-push sync starts immediately.
|
|
288
295
|
initialized: state.initialized
|
|
@@ -399,7 +406,7 @@ function AppInner({
|
|
|
399
406
|
tickerRepository={tickerRepository}
|
|
400
407
|
pluginRegistry={pluginRegistry}
|
|
401
408
|
quitApp={() => rendererHost.requestExit()}
|
|
402
|
-
onCheckForUpdates={() => runUpdateCheck(true)}
|
|
409
|
+
onCheckForUpdates={updatesEnabled ? () => runUpdateCheck(true) : undefined}
|
|
403
410
|
onNativeOccluderChange={setCommandBarNativeOccluder}
|
|
404
411
|
/>
|
|
405
412
|
)}
|
|
@@ -414,6 +421,7 @@ interface AppProps {
|
|
|
414
421
|
config: AppConfig;
|
|
415
422
|
servicesFactory: AppServicesFactory;
|
|
416
423
|
externalPlugins?: LoadedExternalPlugin[];
|
|
424
|
+
plugins: readonly GloomPlugin[];
|
|
417
425
|
cliLaunchRequest?: CliLaunchRequest | null;
|
|
418
426
|
desktopWindowBridge?: DesktopWindowBridge;
|
|
419
427
|
desktopApplicationMenuBridge?: DesktopApplicationMenuBridge;
|
|
@@ -421,12 +429,14 @@ interface AppProps {
|
|
|
421
429
|
desktopSnapshot?: DesktopSharedStateSnapshot | null;
|
|
422
430
|
desktopThemePreview?: DesktopThemePreviewState | null;
|
|
423
431
|
remoteControlAdapter?: RemoteControlAdapter;
|
|
432
|
+
updatesEnabled?: boolean;
|
|
424
433
|
}
|
|
425
434
|
|
|
426
435
|
export function App({
|
|
427
436
|
config: initialConfig,
|
|
428
437
|
servicesFactory,
|
|
429
438
|
externalPlugins: providedExternalPlugins,
|
|
439
|
+
plugins,
|
|
430
440
|
cliLaunchRequest = null,
|
|
431
441
|
desktopWindowBridge,
|
|
432
442
|
desktopApplicationMenuBridge,
|
|
@@ -434,6 +444,7 @@ export function App({
|
|
|
434
444
|
desktopSnapshot = null,
|
|
435
445
|
desktopThemePreview = null,
|
|
436
446
|
remoteControlAdapter,
|
|
447
|
+
updatesEnabled = true,
|
|
437
448
|
}: AppProps) {
|
|
438
449
|
useAppLanguage();
|
|
439
450
|
const externalPlugins = providedExternalPlugins ?? EMPTY_EXTERNAL_PLUGINS;
|
|
@@ -458,8 +469,10 @@ export function App({
|
|
|
458
469
|
const [config, setConfig] = useState(() => {
|
|
459
470
|
return initialCliLaunch.config;
|
|
460
471
|
});
|
|
472
|
+
const shareHandoff = isPaneShareHandoff();
|
|
461
473
|
const [showOnboarding, setShowOnboarding] = useState(() => (
|
|
462
474
|
desktopWindowBridge?.kind !== "detached"
|
|
475
|
+
&& !shareHandoff
|
|
463
476
|
&& (!effectiveInitialConfig.onboardingComplete || !!effectiveInitialConfig.onboardingProgress)
|
|
464
477
|
));
|
|
465
478
|
|
|
@@ -469,14 +482,14 @@ export function App({
|
|
|
469
482
|
return measurePerf("startup.app.create-services", () => (
|
|
470
483
|
servicesFactory({
|
|
471
484
|
config,
|
|
472
|
-
plugins
|
|
485
|
+
plugins,
|
|
473
486
|
})
|
|
474
487
|
), {
|
|
475
488
|
externalPluginCount: externalPlugins.length,
|
|
476
489
|
disabledPluginCount: config.disabledPlugins.length,
|
|
477
490
|
brokerInstanceCount: config.brokerInstances.length,
|
|
478
491
|
});
|
|
479
|
-
}, [config.dataDir, externalPlugins, servicesFactory]);
|
|
492
|
+
}, [config.dataDir, externalPlugins, plugins, servicesFactory]);
|
|
480
493
|
|
|
481
494
|
useEffect(() => {
|
|
482
495
|
return () => services.destroy();
|
|
@@ -514,6 +527,7 @@ export function App({
|
|
|
514
527
|
desktopApplicationMenuBridge={desktopApplicationMenuBridge}
|
|
515
528
|
desktopDeepLinkBridge={desktopDeepLinkBridge}
|
|
516
529
|
remoteControlAdapter={remoteControlAdapter}
|
|
530
|
+
updatesEnabled={updatesEnabled}
|
|
517
531
|
onboardingActive={showOnboarding}
|
|
518
532
|
onOnboardingComplete={(updatedConfig) => {
|
|
519
533
|
setConfig(updatedConfig);
|
|
@@ -61,7 +61,11 @@ describe("import boundaries", () => {
|
|
|
61
61
|
return !file.startsWith("src/renderers/electrobun/");
|
|
62
62
|
}
|
|
63
63
|
if (specifier === "react-dom" || specifier.startsWith("react-dom/")) {
|
|
64
|
-
return !
|
|
64
|
+
return ![
|
|
65
|
+
"src/renderers/electrobun/",
|
|
66
|
+
"src/renderers/browser/",
|
|
67
|
+
"src/renderers/share/",
|
|
68
|
+
].some((prefix) => file.startsWith(prefix));
|
|
65
69
|
}
|
|
66
70
|
return false;
|
|
67
71
|
});
|
|
@@ -147,6 +147,10 @@ export function newsProvider(options: {
|
|
|
147
147
|
supports: op((input: any) => options.provider.supports?.(input.query) ?? true, "query"),
|
|
148
148
|
getCachedNews: op((input: any) => options.provider.getCachedNews?.(input.query) ?? [], "query"),
|
|
149
149
|
fetchNews: op((input: any) => options.provider.fetchNews(input.query), "query"),
|
|
150
|
+
fetchNewsPage: op((input: any) => (
|
|
151
|
+
options.provider.fetchNewsPage?.(input.query)
|
|
152
|
+
?? options.provider.fetchNews(input.query).then((articles) => ({ articles, nextCursor: null }))
|
|
153
|
+
), "query"),
|
|
150
154
|
fetchNewsStory: op((input: any) => options.provider.fetchNewsStory?.(input.storyId) ?? null, "query"),
|
|
151
155
|
},
|
|
152
156
|
};
|
|
@@ -199,4 +199,13 @@ describe("headless Pi AI command", () => {
|
|
|
199
199
|
expect(disconnected.runs).toEqual([]);
|
|
200
200
|
expect(disconnected.closed()).toBe(1);
|
|
201
201
|
});
|
|
202
|
+
|
|
203
|
+
test("rejects unsupported verbs instead of returning a stub payload", async () => {
|
|
204
|
+
const harness = createHarness({
|
|
205
|
+
catalog: { providers: [], refreshErrors: {} },
|
|
206
|
+
});
|
|
207
|
+
await expect(harness.command.execute(["screen"], harness.context))
|
|
208
|
+
.rejects.toThrow("Usage: gloomberb ai providers|ask");
|
|
209
|
+
expect(harness.output).toEqual([]);
|
|
210
|
+
});
|
|
202
211
|
});
|
|
@@ -13,38 +13,26 @@ import type { CliCommandDef } from "../../types/plugin";
|
|
|
13
13
|
import { withCliServices, withConfigData } from "../context";
|
|
14
14
|
import { requireArg, takeOption } from "./command-utils";
|
|
15
15
|
|
|
16
|
-
function deferredResult(command: string, code: "auth_required" | "not_implemented", message: string) {
|
|
17
|
-
return {
|
|
18
|
-
command,
|
|
19
|
-
status: code,
|
|
20
|
-
code,
|
|
21
|
-
message,
|
|
22
|
-
};
|
|
23
|
-
}
|
|
24
|
-
|
|
25
16
|
export const brokerCliCommand: CliCommandDef = {
|
|
26
17
|
name: "broker",
|
|
27
18
|
aliases: ["brokers"],
|
|
28
|
-
description: "Inspect broker profiles
|
|
29
|
-
help: { usage: ["broker list", "broker status"
|
|
19
|
+
description: "Inspect broker profiles",
|
|
20
|
+
help: { usage: ["broker list", "broker status"] },
|
|
30
21
|
execute: async (args, ctx) => {
|
|
31
22
|
const action = args[0] ?? "list";
|
|
23
|
+
if (action !== "list" && action !== "status") {
|
|
24
|
+
ctx.fail("Usage: gloomberb broker list|status");
|
|
25
|
+
}
|
|
32
26
|
await withCliServices(ctx, async (services) => {
|
|
33
|
-
|
|
34
|
-
|
|
27
|
+
ctx.printResult({
|
|
28
|
+
data: services.config.brokerInstances.map((instance) => ({
|
|
35
29
|
id: instance.id,
|
|
36
30
|
type: instance.brokerType,
|
|
37
31
|
label: instance.label,
|
|
38
32
|
enabled: instance.enabled !== false,
|
|
39
33
|
connectionMode: instance.connectionMode ?? "",
|
|
40
34
|
lastSyncedAt: instance.lastSyncedAt ? new Date(instance.lastSyncedAt).toISOString() : "",
|
|
41
|
-
}))
|
|
42
|
-
ctx.printResult({ data: rows });
|
|
43
|
-
return;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
ctx.printResult({
|
|
47
|
-
data: deferredResult("broker", "not_implemented", "Broker mutation and connection commands need shared non-UI broker service extraction before they can safely run headless."),
|
|
35
|
+
})),
|
|
48
36
|
});
|
|
49
37
|
});
|
|
50
38
|
},
|
|
@@ -52,36 +40,24 @@ export const brokerCliCommand: CliCommandDef = {
|
|
|
52
40
|
|
|
53
41
|
export const ibkrCliCommand: CliCommandDef = {
|
|
54
42
|
name: "ibkr",
|
|
55
|
-
description: "
|
|
56
|
-
help: { usage: ["ibkr accounts
|
|
43
|
+
description: "Inspect IBKR profiles",
|
|
44
|
+
help: { usage: ["ibkr accounts", "ibkr status"] },
|
|
57
45
|
execute: async (args, ctx) => {
|
|
58
46
|
const action = args[0] ?? "status";
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
if ((action === "place" || action === "cancel") && (!profile || !account || !ctx.cliOptions.yes)) {
|
|
62
|
-
ctx.fail(`ibkr ${action} requires --profile, --account, and --yes.`);
|
|
47
|
+
if (action !== "accounts" && action !== "status") {
|
|
48
|
+
ctx.fail("Usage: gloomberb ibkr accounts|status");
|
|
63
49
|
}
|
|
64
50
|
await withCliServices(ctx, async (services) => {
|
|
65
|
-
const ibkrProfiles = services.config.brokerInstances.filter((instance) => instance.brokerType === "ibkr");
|
|
66
|
-
if (action === "accounts" || action === "status") {
|
|
67
|
-
ctx.printResult({ data: ibkrProfiles.map((instance) => ({
|
|
68
|
-
id: instance.id,
|
|
69
|
-
label: instance.label,
|
|
70
|
-
enabled: instance.enabled !== false,
|
|
71
|
-
connectionMode: instance.connectionMode ?? "",
|
|
72
|
-
lastSyncedAt: instance.lastSyncedAt ? new Date(instance.lastSyncedAt).toISOString() : "",
|
|
73
|
-
})) });
|
|
74
|
-
return;
|
|
75
|
-
}
|
|
76
51
|
ctx.printResult({
|
|
77
|
-
data:
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
52
|
+
data: services.config.brokerInstances
|
|
53
|
+
.filter((instance) => instance.brokerType === "ibkr")
|
|
54
|
+
.map((instance) => ({
|
|
55
|
+
id: instance.id,
|
|
56
|
+
label: instance.label,
|
|
57
|
+
enabled: instance.enabled !== false,
|
|
58
|
+
connectionMode: instance.connectionMode ?? "",
|
|
59
|
+
lastSyncedAt: instance.lastSyncedAt ? new Date(instance.lastSyncedAt).toISOString() : "",
|
|
60
|
+
})),
|
|
85
61
|
});
|
|
86
62
|
});
|
|
87
63
|
},
|
|
@@ -133,19 +109,12 @@ export function createAiCliCommand(options: CreateAiCliCommandOptions = {}): Cli
|
|
|
133
109
|
usage: [
|
|
134
110
|
"ai providers",
|
|
135
111
|
"ai ask [--provider id] [--model id] <prompt>",
|
|
136
|
-
"ai screen list|show|delete|refresh|export",
|
|
137
112
|
],
|
|
138
113
|
},
|
|
139
114
|
execute: async (args, ctx) => {
|
|
140
115
|
const action = args[0] ?? "providers";
|
|
141
|
-
if (action === "screen") {
|
|
142
|
-
ctx.printResult({
|
|
143
|
-
data: deferredResult("ai screen", "not_implemented", "AI screener tabs are still pane-state backed; expose them through a shared non-UI screener store before headless mutation."),
|
|
144
|
-
});
|
|
145
|
-
return;
|
|
146
|
-
}
|
|
147
116
|
if (action !== "providers" && action !== "ask") {
|
|
148
|
-
ctx.fail("Usage: gloomberb ai providers|ask
|
|
117
|
+
ctx.fail("Usage: gloomberb ai providers|ask");
|
|
149
118
|
}
|
|
150
119
|
|
|
151
120
|
const rawArgs = args.slice(1);
|
|
@@ -258,23 +227,3 @@ export const rssCliCommand: CliCommandDef = {
|
|
|
258
227
|
})) });
|
|
259
228
|
},
|
|
260
229
|
};
|
|
261
|
-
|
|
262
|
-
function cloudCommand(name: string, description: string): CliCommandDef {
|
|
263
|
-
return {
|
|
264
|
-
name,
|
|
265
|
-
description,
|
|
266
|
-
execute: (_args, ctx) => {
|
|
267
|
-
ctx.printResult({
|
|
268
|
-
data: deferredResult(name, "auth_required", "This command needs an existing cloud/session token. New auth/account/chat CLI workflows are deferred in this pass."),
|
|
269
|
-
});
|
|
270
|
-
},
|
|
271
|
-
};
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
export const cloudCliCommands: CliCommandDef[] = [
|
|
275
|
-
cloudCommand("buildout", "Fetch Buildout company data through an existing cloud session"),
|
|
276
|
-
cloudCommand("congress", "Fetch congressional trade data through an existing cloud session"),
|
|
277
|
-
cloudCommand("substack", "Fetch Substack reader data through an existing session"),
|
|
278
|
-
cloudCommand("x-feed", "Fetch X/Twitter feed data through an existing cloud session"),
|
|
279
|
-
cloudCommand("tweets", "Alias for x-feed"),
|
|
280
|
-
];
|
package/src/cli/index.ts
CHANGED
|
@@ -23,7 +23,6 @@ import { createSystemCliCommands } from "./commands/system";
|
|
|
23
23
|
import {
|
|
24
24
|
aiCliCommand,
|
|
25
25
|
brokerCliCommand,
|
|
26
|
-
cloudCliCommands,
|
|
27
26
|
ibkrCliCommand,
|
|
28
27
|
rssCliCommand,
|
|
29
28
|
} from "./commands/automation";
|
|
@@ -35,7 +34,10 @@ import {
|
|
|
35
34
|
} from "./commands/plugins";
|
|
36
35
|
import { runPaneCatalog, runPaneFunction, runPaneScreenshot } from "./pane-functions";
|
|
37
36
|
|
|
38
|
-
function createCoreCliCommands(
|
|
37
|
+
function createCoreCliCommands(
|
|
38
|
+
renderHelp: () => string,
|
|
39
|
+
allCommands: () => CliCommandDef[],
|
|
40
|
+
): CliCommandDef[] {
|
|
39
41
|
let commands: CliCommandDef[] = [];
|
|
40
42
|
const launchUiRequest: CliLaunchRequest = {
|
|
41
43
|
applyConfig: (config) => ({ config }),
|
|
@@ -53,7 +55,7 @@ function createCoreCliCommands(renderHelp: () => string): CliCommandDef[] {
|
|
|
53
55
|
console.log(renderHelp());
|
|
54
56
|
return;
|
|
55
57
|
}
|
|
56
|
-
ctx.printResult({ data:
|
|
58
|
+
ctx.printResult({ data: allCommands().map((command) => ({
|
|
57
59
|
name: command.name,
|
|
58
60
|
aliases: command.aliases ?? [],
|
|
59
61
|
description: command.description,
|
|
@@ -190,12 +192,11 @@ function createCoreCliCommands(renderHelp: () => string): CliCommandDef[] {
|
|
|
190
192
|
...marketDataCliCommands,
|
|
191
193
|
...overviewCliCommands,
|
|
192
194
|
remoteCliCommand,
|
|
193
|
-
...createSystemCliCommands(
|
|
195
|
+
...createSystemCliCommands(allCommands),
|
|
194
196
|
brokerCliCommand,
|
|
195
197
|
ibkrCliCommand,
|
|
196
198
|
aiCliCommand,
|
|
197
199
|
rssCliCommand,
|
|
198
|
-
...cloudCliCommands,
|
|
199
200
|
];
|
|
200
201
|
return commands;
|
|
201
202
|
}
|
|
@@ -207,7 +208,10 @@ export interface DispatchCliOptions {
|
|
|
207
208
|
async function createRegistry(options: DispatchCliOptions = {}): Promise<CliCommandRegistry> {
|
|
208
209
|
const config = await loadCliConfigIfAvailable();
|
|
209
210
|
let registry: CliCommandRegistry | null = null;
|
|
210
|
-
const coreCommands = createCoreCliCommands(
|
|
211
|
+
const coreCommands = createCoreCliCommands(
|
|
212
|
+
() => renderCliHelp(registry!, VERSION),
|
|
213
|
+
() => registry!.commands.map((entry) => entry.command),
|
|
214
|
+
);
|
|
211
215
|
registry = buildCliCommandRegistry({
|
|
212
216
|
coreCommands,
|
|
213
217
|
externalPlugins: options.externalPlugins ?? [],
|
package/src/cli/registry.ts
CHANGED
|
@@ -31,6 +31,9 @@ import {
|
|
|
31
31
|
resolveCompositeMinimumSpanMs,
|
|
32
32
|
zoomCompositeViewport,
|
|
33
33
|
} from "./interactions";
|
|
34
|
+
import { getThemeColors, syncTheme } from "../../../theme/colors";
|
|
35
|
+
import { ThemeProvider } from "../../../theme/theme-context";
|
|
36
|
+
import { DEFAULT_THEME } from "../../../theme/themes";
|
|
34
37
|
|
|
35
38
|
let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
|
|
36
39
|
let chartShortcut: ((event: KeyEventLike) => void) | null = null;
|
|
@@ -131,12 +134,12 @@ function keyEvent(name: string, shift = false): KeyEventLike {
|
|
|
131
134
|
}
|
|
132
135
|
|
|
133
136
|
afterEach(async () => {
|
|
134
|
-
if (
|
|
135
|
-
await act(async () => testSetup!.renderer.destroy());
|
|
137
|
+
if (testSetup) await act(async () => testSetup!.renderer.destroy());
|
|
136
138
|
testSetup = undefined;
|
|
137
139
|
chartShortcut = null;
|
|
138
140
|
capturedSurfaceProps = null;
|
|
139
141
|
capturedSurfaceNode = null;
|
|
142
|
+
syncTheme(DEFAULT_THEME);
|
|
140
143
|
});
|
|
141
144
|
|
|
142
145
|
function point(date: string, value: number): TimeSeriesPoint {
|
|
@@ -204,6 +207,46 @@ function twoSessionCandles(): ResolvedSeries {
|
|
|
204
207
|
}
|
|
205
208
|
|
|
206
209
|
describe("CompositeChart", () => {
|
|
210
|
+
test("repaints cached chart rasters when the theme changes", async () => {
|
|
211
|
+
let setThemeId: ((themeId: string) => void) | null = null;
|
|
212
|
+
const chartSeries = [series("price", "main", "left", "USD", [100, 101, 102])];
|
|
213
|
+
|
|
214
|
+
function ThemedChart() {
|
|
215
|
+
const [themeId, setTheme] = useState(DEFAULT_THEME);
|
|
216
|
+
setThemeId = setTheme;
|
|
217
|
+
return (
|
|
218
|
+
<ThemeProvider themeId={themeId}>
|
|
219
|
+
<CaptureChartSurfaceProvider>
|
|
220
|
+
<CompositeChart
|
|
221
|
+
width={60}
|
|
222
|
+
height={12}
|
|
223
|
+
series={chartSeries}
|
|
224
|
+
panels={[{ id: "main" }]}
|
|
225
|
+
/>
|
|
226
|
+
</CaptureChartSurfaceProvider>
|
|
227
|
+
</ThemeProvider>
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
testSetup = await testRender(<ThemedChart />, { width: 62, height: 14 });
|
|
232
|
+
await act(async () => {
|
|
233
|
+
await testSetup!.renderOnce();
|
|
234
|
+
await testSetup!.renderOnce();
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
const nextThemeId = "github-light";
|
|
238
|
+
await act(async () => {
|
|
239
|
+
setThemeId?.(nextThemeId);
|
|
240
|
+
await testSetup!.renderOnce();
|
|
241
|
+
await testSetup!.renderOnce();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
const bitmap = capturedSurfaceProps!.bitmaps[0] as { pixels: Uint8Array };
|
|
245
|
+
const expectedBackground = getThemeColors(nextThemeId).bg;
|
|
246
|
+
const expectedRgb = [1, 3, 5].map((offset) => parseInt(expectedBackground.slice(offset, offset + 2), 16));
|
|
247
|
+
expect(Array.from(bitmap.pixels.slice(0, 3))).toEqual(expectedRgb);
|
|
248
|
+
});
|
|
249
|
+
|
|
207
250
|
test("shows the regular-session move beside the latest intraday value", async () => {
|
|
208
251
|
testSetup = await testRender(
|
|
209
252
|
<CompositeChart
|
|
@@ -14,6 +14,7 @@ import {
|
|
|
14
14
|
import { useShortcut } from "../../../react/input";
|
|
15
15
|
import { useOptionalPaneInstanceId, usePaneSettingValue } from "../../../state/app/context";
|
|
16
16
|
import { colors as themeColors, hoverBg } from "../../../theme/colors";
|
|
17
|
+
import { useThemeColors } from "../../../theme/theme-context";
|
|
17
18
|
import { formatPercentRaw } from "../../../utils/format";
|
|
18
19
|
import { isPlainKey } from "../../../utils/keyboard";
|
|
19
20
|
import { truncateWithEllipsis } from "../../../utils/text-wrap";
|
|
@@ -38,6 +39,7 @@ import {
|
|
|
38
39
|
compositeAxisTicks,
|
|
39
40
|
formatCompositeAxisValue,
|
|
40
41
|
formatCompositeCursorDate,
|
|
42
|
+
formatCompositeCursorValue,
|
|
41
43
|
formatCompositePointDetails,
|
|
42
44
|
formatCompositeSeriesValue,
|
|
43
45
|
formatCompositeTimeAxisDate,
|
|
@@ -61,6 +63,7 @@ import { renderCompositePanelBitmap } from "./rasterizer";
|
|
|
61
63
|
import {
|
|
62
64
|
buildChartToolVectors,
|
|
63
65
|
CHART_DRAWING_COLORS,
|
|
66
|
+
CHART_DRAWINGS_SETTING_KEY,
|
|
64
67
|
countMeasureBars,
|
|
65
68
|
drawChartToolOverlay,
|
|
66
69
|
resolveChartToolKind,
|
|
@@ -69,6 +72,7 @@ import {
|
|
|
69
72
|
hitTestDrawings,
|
|
70
73
|
isDrawingTool,
|
|
71
74
|
nextDrawingColor,
|
|
75
|
+
parseChartDrawings,
|
|
72
76
|
resolveDrawingFromDrag,
|
|
73
77
|
resolveZoomBoxRange,
|
|
74
78
|
shiftDrawing,
|
|
@@ -291,7 +295,7 @@ function cursorAxisLabel(
|
|
|
291
295
|
const domain = panel.axes[side];
|
|
292
296
|
if (!domain || cursorYRatio === null) return null;
|
|
293
297
|
const value = unprojectCompositeValue(cursorYRatio, domain);
|
|
294
|
-
return value === null ? null :
|
|
298
|
+
return value === null ? null : formatCompositeCursorValue(value, domain);
|
|
295
299
|
}
|
|
296
300
|
|
|
297
301
|
const MINIMUM_AXIS_LABEL_WIDTH = 3;
|
|
@@ -707,10 +711,10 @@ function CompositePanelSurface({
|
|
|
707
711
|
domain: measureDomain,
|
|
708
712
|
}),
|
|
709
713
|
startValueLabel: measureDomain && startValue !== null
|
|
710
|
-
?
|
|
714
|
+
? formatCompositeCursorValue(startValue, measureDomain)
|
|
711
715
|
: null,
|
|
712
716
|
endValueLabel: measureDomain && endValue !== null
|
|
713
|
-
?
|
|
717
|
+
? formatCompositeCursorValue(endValue, measureDomain)
|
|
714
718
|
: null,
|
|
715
719
|
};
|
|
716
720
|
}, [measureDomain, scene, toolDrag]);
|
|
@@ -1370,7 +1374,6 @@ function CompositeLegend({
|
|
|
1370
1374
|
);
|
|
1371
1375
|
}
|
|
1372
1376
|
|
|
1373
|
-
const CHART_DRAWINGS_SETTING_KEY = "chartDrawings";
|
|
1374
1377
|
const NO_DRAWINGS: readonly ChartDrawing[] = [];
|
|
1375
1378
|
/** Coalesces a drag into one write instead of one per pointer move. */
|
|
1376
1379
|
const DRAWING_PERSIST_DELAY_MS = 400;
|
|
@@ -1396,7 +1399,7 @@ function ChartDrawingStore({
|
|
|
1396
1399
|
);
|
|
1397
1400
|
const restoredRef = useRef<readonly ChartDrawing[] | null>(null);
|
|
1398
1401
|
if (restoredRef.current === null) {
|
|
1399
|
-
restoredRef.current =
|
|
1402
|
+
restoredRef.current = parseChartDrawings(stored);
|
|
1400
1403
|
}
|
|
1401
1404
|
|
|
1402
1405
|
useEffect(() => {
|
|
@@ -1444,6 +1447,7 @@ export function CompositeChart({
|
|
|
1444
1447
|
onToggleSeries,
|
|
1445
1448
|
isSeriesToggleable,
|
|
1446
1449
|
}: CompositeChartProps) {
|
|
1450
|
+
const activeThemeColors = useThemeColors();
|
|
1447
1451
|
const { cellWidthPx = 8, pixelRatio = 1 } = useUiCapabilities();
|
|
1448
1452
|
const isDesktopWeb = useUiHost().kind === "desktop-web";
|
|
1449
1453
|
const showTextFallback = useShowChartTextFallback();
|
|
@@ -1691,13 +1695,13 @@ export function CompositeChart({
|
|
|
1691
1695
|
}).join("|");
|
|
1692
1696
|
const plotHeight = Math.max(panelCount, totalHeight - legendRows - timeAxisRows);
|
|
1693
1697
|
const resolvedColors = useMemo<CompositeChartColors>(() => ({
|
|
1694
|
-
background: colors?.background ??
|
|
1695
|
-
grid: colors?.grid ??
|
|
1696
|
-
crosshair: colors?.crosshair ??
|
|
1697
|
-
text: colors?.text ??
|
|
1698
|
-
textDim: colors?.textDim ??
|
|
1699
|
-
negative: colors?.negative ??
|
|
1700
|
-
}), [colors]);
|
|
1698
|
+
background: colors?.background ?? activeThemeColors.bg,
|
|
1699
|
+
grid: colors?.grid ?? activeThemeColors.border,
|
|
1700
|
+
crosshair: colors?.crosshair ?? activeThemeColors.borderFocused,
|
|
1701
|
+
text: colors?.text ?? activeThemeColors.text,
|
|
1702
|
+
textDim: colors?.textDim ?? activeThemeColors.textDim,
|
|
1703
|
+
negative: colors?.negative ?? activeThemeColors.negative,
|
|
1704
|
+
}), [activeThemeColors, colors]);
|
|
1701
1705
|
const projectedScene = useMemo(() => {
|
|
1702
1706
|
// lastTickKey busts this memo when a live tick mutates series identity in place.
|
|
1703
1707
|
void lastTickKey;
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { describe, expect, test } from "bun:test";
|
|
2
2
|
import type { CompositeChartScene } from "./types";
|
|
3
3
|
import {
|
|
4
|
+
formatChartLegendValue,
|
|
4
5
|
formatCompositeAxisValue,
|
|
5
6
|
formatCompositeCursorDate,
|
|
7
|
+
formatCompositeCursorValue,
|
|
6
8
|
formatCompositePointDetails,
|
|
7
9
|
formatCompositeSeriesValue,
|
|
8
10
|
formatCompositeTimeAxisDate,
|
|
@@ -147,4 +149,36 @@ describe("composite chart unit formatting", () => {
|
|
|
147
149
|
seriesIds: ["ratio"],
|
|
148
150
|
})).toBe("0.500");
|
|
149
151
|
});
|
|
152
|
+
|
|
153
|
+
test("shows full price precision in the legend and cursor, not compact axis ticks", () => {
|
|
154
|
+
const btc: ResolvedSeries = {
|
|
155
|
+
id: "btc",
|
|
156
|
+
label: "BTC-USD Price",
|
|
157
|
+
color: "#ffffff",
|
|
158
|
+
unit: "USD",
|
|
159
|
+
unitGroup: "price:USD",
|
|
160
|
+
nativeFrequency: "daily",
|
|
161
|
+
dataShape: "ohlcv",
|
|
162
|
+
style: "candles",
|
|
163
|
+
transform: "raw",
|
|
164
|
+
axis: "left",
|
|
165
|
+
panelId: "main",
|
|
166
|
+
interpolation: "none",
|
|
167
|
+
points: [],
|
|
168
|
+
};
|
|
169
|
+
const domain = {
|
|
170
|
+
side: "left" as const,
|
|
171
|
+
min: 70_000,
|
|
172
|
+
max: 80_000,
|
|
173
|
+
scale: "linear" as const,
|
|
174
|
+
unit: "USD",
|
|
175
|
+
unitGroup: "price:USD",
|
|
176
|
+
seriesIds: ["btc"],
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
expect(formatCompositeSeriesValue(79_432.18, btc)).toBe("$79,432.18");
|
|
180
|
+
expect(formatChartLegendValue(79_432.18, "USD", "price:USD")).toBe("$79,432.18");
|
|
181
|
+
expect(formatCompositeCursorValue(79_432.18, domain)).toBe("$79,432.18");
|
|
182
|
+
expect(formatCompositeAxisValue(79_432.18, domain)).toBe("$79K");
|
|
183
|
+
});
|
|
150
184
|
});
|