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
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { formatMarketPriceWithCurrency } from "../../../market-data/market/format";
|
|
1
2
|
import type { ResolvedSeries, TimeSeriesPoint } from "../../../time-series/types";
|
|
2
3
|
import type { CompositeAxisDomain } from "./types";
|
|
3
4
|
|
|
@@ -25,23 +26,37 @@ function compactNumber(value: number): string {
|
|
|
25
26
|
return value.toPrecision(3);
|
|
26
27
|
}
|
|
27
28
|
|
|
28
|
-
function
|
|
29
|
+
function unitCurrencyCode(unit: string): string | null {
|
|
29
30
|
const currency = unit.trim().toUpperCase().split(/[\s/]/)[0] ?? "";
|
|
30
|
-
return CURRENCY_SYMBOLS[currency]
|
|
31
|
+
return CURRENCY_SYMBOLS[currency] ? currency : null;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function currencyPrefix(unit: string): string {
|
|
35
|
+
const currency = unitCurrencyCode(unit);
|
|
36
|
+
return currency ? CURRENCY_SYMBOLS[currency] ?? "" : "";
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function formatFullCurrencyValue(value: number, unit: string): string | null {
|
|
40
|
+
const currency = unitCurrencyCode(unit);
|
|
41
|
+
return currency ? formatMarketPriceWithCurrency(value, currency) : null;
|
|
31
42
|
}
|
|
32
43
|
|
|
33
44
|
export function formatCompositeSeriesValue(value: number, series: ResolvedSeries): string {
|
|
34
|
-
|
|
35
|
-
|
|
45
|
+
return formatChartLegendValue(value, series.unit, series.unitGroup);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function formatChartLegendValue(value: number, unit: string, unitGroup = ""): string {
|
|
49
|
+
const trimmed = unit.trim();
|
|
50
|
+
const group = unitGroup.toLowerCase();
|
|
36
51
|
const compact = compactNumber(value);
|
|
37
|
-
if (group.includes("percent") ||
|
|
52
|
+
if (group.includes("percent") || trimmed === "%" || trimmed.toLowerCase().includes("percent")) {
|
|
38
53
|
return `${compact}%`;
|
|
39
54
|
}
|
|
40
|
-
if (group.includes("ratio") ||
|
|
41
|
-
if (group.startsWith("derived-unit:")) return `${compact} ${
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
44
|
-
return
|
|
55
|
+
if (group.includes("ratio") || trimmed.toLowerCase() === "x") return `${compact}x`;
|
|
56
|
+
if (group.startsWith("derived-unit:")) return `${compact} ${trimmed}`;
|
|
57
|
+
const fullPrice = formatFullCurrencyValue(value, trimmed);
|
|
58
|
+
if (fullPrice) return fullPrice;
|
|
59
|
+
return trimmed && trimmed.length <= 6 ? `${compact}${trimmed.startsWith("/") ? "" : " "}${trimmed}` : compact;
|
|
45
60
|
}
|
|
46
61
|
|
|
47
62
|
export function formatCompositeAxisValue(value: number, domain: CompositeAxisDomain): string {
|
|
@@ -55,6 +70,14 @@ export function formatCompositeAxisValue(value: number, domain: CompositeAxisDom
|
|
|
55
70
|
return `${currencyPrefix(domain.unit)}${compact}`;
|
|
56
71
|
}
|
|
57
72
|
|
|
73
|
+
export function formatCompositeCursorValue(value: number, domain: CompositeAxisDomain): string {
|
|
74
|
+
const group = domain.unitGroup.toLowerCase();
|
|
75
|
+
if (group.startsWith("derived-unit:")) return compactNumber(value);
|
|
76
|
+
const fullPrice = formatFullCurrencyValue(value, domain.unit);
|
|
77
|
+
if (fullPrice) return fullPrice;
|
|
78
|
+
return formatCompositeAxisValue(value, domain);
|
|
79
|
+
}
|
|
80
|
+
|
|
58
81
|
export function compositeAxisTicks(domain: CompositeAxisDomain, count = 3): Array<{ ratio: number; value: number; label: string }> {
|
|
59
82
|
const tickCount = Math.max(2, Math.floor(count));
|
|
60
83
|
return Array.from({ length: tickCount }, (_, index) => {
|
|
@@ -17,7 +17,6 @@ import type {
|
|
|
17
17
|
import {
|
|
18
18
|
buildCompositeTimeScale,
|
|
19
19
|
projectCompositeTimestamp,
|
|
20
|
-
unprojectCompositeTimestamp,
|
|
21
20
|
} from "./time-scale";
|
|
22
21
|
import type { CompositeTimeScale } from "./types";
|
|
23
22
|
|
|
@@ -523,26 +522,6 @@ export function resolveCompositeCursorDate(scene: CompositeChartScene, localX: n
|
|
|
523
522
|
return scene.dates[nearestIndex] ?? null;
|
|
524
523
|
}
|
|
525
524
|
|
|
526
|
-
export function resolveCompositeTimeAxisDate(
|
|
527
|
-
scene: CompositeChartScene,
|
|
528
|
-
ratio: number,
|
|
529
|
-
): Date {
|
|
530
|
-
const safeRatio = Math.max(0, Math.min(1, ratio));
|
|
531
|
-
if (scene.timeScale.kind === "market" && scene.dates.length > 0) {
|
|
532
|
-
let nearestIndex = 0;
|
|
533
|
-
let nearestDistance = Number.POSITIVE_INFINITY;
|
|
534
|
-
scene.dateRatios.forEach((candidate, index) => {
|
|
535
|
-
const distance = Math.abs(candidate - safeRatio);
|
|
536
|
-
if (distance < nearestDistance) {
|
|
537
|
-
nearestIndex = index;
|
|
538
|
-
nearestDistance = distance;
|
|
539
|
-
}
|
|
540
|
-
});
|
|
541
|
-
return scene.dates[nearestIndex] ?? new Date(scene.startTime);
|
|
542
|
-
}
|
|
543
|
-
return new Date(unprojectCompositeTimestamp(scene.timeScale, safeRatio));
|
|
544
|
-
}
|
|
545
|
-
|
|
546
525
|
export function resolveAdjacentCompositeCursorDate(
|
|
547
526
|
scene: CompositeChartScene,
|
|
548
527
|
current: Date | null,
|
|
@@ -27,6 +27,55 @@ export interface ChartDrawing {
|
|
|
27
27
|
color: string;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
|
+
export const CHART_DRAWINGS_SETTING_KEY = "chartDrawings";
|
|
31
|
+
const MAX_SHARED_CHART_DRAWINGS = 100;
|
|
32
|
+
const MAX_SHARED_DRAWING_POINTS = 5_000;
|
|
33
|
+
|
|
34
|
+
export function parseChartDrawings(value: unknown): ChartDrawing[] {
|
|
35
|
+
if (!Array.isArray(value) || value.length > MAX_SHARED_CHART_DRAWINGS) return [];
|
|
36
|
+
let pointCount = 0;
|
|
37
|
+
const drawings: ChartDrawing[] = [];
|
|
38
|
+
for (const entry of value) {
|
|
39
|
+
if (!entry || typeof entry !== "object" || Array.isArray(entry)) return [];
|
|
40
|
+
const drawing = entry as Record<string, unknown>;
|
|
41
|
+
if (!Object.keys(drawing).every((key) => ["id", "panelId", "points", "color"].includes(key))) return [];
|
|
42
|
+
if (
|
|
43
|
+
typeof drawing.id !== "string"
|
|
44
|
+
|| drawing.id.length === 0
|
|
45
|
+
|| drawing.id.length > 160
|
|
46
|
+
|| typeof drawing.panelId !== "string"
|
|
47
|
+
|| drawing.panelId.length === 0
|
|
48
|
+
|| drawing.panelId.length > 120
|
|
49
|
+
|| typeof drawing.color !== "string"
|
|
50
|
+
|| !/^#[0-9a-f]{6}$/i.test(drawing.color)
|
|
51
|
+
|| !Array.isArray(drawing.points)
|
|
52
|
+
|| drawing.points.length < 2
|
|
53
|
+
) return [];
|
|
54
|
+
pointCount += drawing.points.length;
|
|
55
|
+
if (pointCount > MAX_SHARED_DRAWING_POINTS) return [];
|
|
56
|
+
const points: ChartDrawingPoint[] = [];
|
|
57
|
+
for (const point of drawing.points) {
|
|
58
|
+
if (!point || typeof point !== "object" || Array.isArray(point)) return [];
|
|
59
|
+
const raw = point as Record<string, unknown>;
|
|
60
|
+
if (
|
|
61
|
+
!Object.keys(raw).every((key) => key === "time" || key === "value")
|
|
62
|
+
|| typeof raw.time !== "number"
|
|
63
|
+
|| !Number.isFinite(raw.time)
|
|
64
|
+
|| typeof raw.value !== "number"
|
|
65
|
+
|| !Number.isFinite(raw.value)
|
|
66
|
+
) return [];
|
|
67
|
+
points.push({ time: raw.time, value: raw.value });
|
|
68
|
+
}
|
|
69
|
+
drawings.push({
|
|
70
|
+
id: drawing.id,
|
|
71
|
+
panelId: drawing.panelId,
|
|
72
|
+
points,
|
|
73
|
+
color: drawing.color,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
return drawings;
|
|
77
|
+
}
|
|
78
|
+
|
|
30
79
|
export interface ChartDrawingHit {
|
|
31
80
|
drawing: ChartDrawing;
|
|
32
81
|
/** Grabbed endpoint, or null when the whole shape was grabbed. */
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
formatCompactMarketPriceWithCurrency,
|
|
3
|
+
formatMarketPrice,
|
|
4
|
+
formatMarketPriceWithCurrency,
|
|
5
|
+
resolveAssetDisplayKind,
|
|
6
|
+
} from "../../../market-data/market/format";
|
|
2
7
|
import type { ChartAxisMode } from "./types";
|
|
3
8
|
|
|
4
9
|
export function formatPrice(
|
|
@@ -27,7 +32,7 @@ export function formatPriceWithCurrency(
|
|
|
27
32
|
minimumFractionDigits = 0,
|
|
28
33
|
fixedFractionDigits?: number,
|
|
29
34
|
): string {
|
|
30
|
-
return
|
|
35
|
+
return formatMarketPriceWithCurrency(value, currency, {
|
|
31
36
|
assetCategory,
|
|
32
37
|
fixedFractionDigits,
|
|
33
38
|
minimumFractionDigits,
|
|
@@ -93,7 +98,11 @@ export function formatAxisValue(
|
|
|
93
98
|
if (axisMode === "percent" && basePrice !== 0) {
|
|
94
99
|
return formatPercentAxisValue(((value - basePrice) / basePrice) * 100);
|
|
95
100
|
}
|
|
96
|
-
return
|
|
101
|
+
return formatCompactMarketPriceWithCurrency(value, currency, {
|
|
102
|
+
assetCategory,
|
|
103
|
+
fixedFractionDigits,
|
|
104
|
+
priceRange,
|
|
105
|
+
});
|
|
97
106
|
}
|
|
98
107
|
|
|
99
108
|
export function formatCursorAxisValue(
|
|
@@ -136,6 +145,7 @@ function getCursorAxisMinimumFractionDigits(value: number, assetCategory?: strin
|
|
|
136
145
|
case "cash":
|
|
137
146
|
return 4;
|
|
138
147
|
case "crypto":
|
|
148
|
+
if (Math.abs(value) >= 100) return 2;
|
|
139
149
|
return Math.abs(value) >= 1 ? 4 : 6;
|
|
140
150
|
case "equity":
|
|
141
151
|
case "contract":
|
|
@@ -448,6 +448,13 @@ describe("renderChart", () => {
|
|
|
448
448
|
expect(formatAxisValue(1.17364, "price", 0, "USD", "CURRENCY")).toBe("$1.17364");
|
|
449
449
|
});
|
|
450
450
|
|
|
451
|
+
test("keeps legend and cursor prices precise while axis ticks stay compact", () => {
|
|
452
|
+
expect(formatAxisValue(79_432.18, "price", 0, "USD", "CRYPTO")).toBe("$79.4K");
|
|
453
|
+
expect(formatCursorAxisValue(79_432.18, "price", 0, "USD", "CRYPTO")).toBe("$79,432.18");
|
|
454
|
+
expect(formatAxisValue(79_432.18, "price", 0, "USD", "STK")).toBe("$79.4K");
|
|
455
|
+
expect(formatCursorAxisValue(79_432.18, "price", 0, "USD", "STK")).toBe("$79,432.18");
|
|
456
|
+
});
|
|
457
|
+
|
|
451
458
|
test("adapts FX axis precision to the visible price range", () => {
|
|
452
459
|
expect(formatAxisValue(1.167815, "price", 0, "USD", "CURRENCY", 0.12)).toBe("$1.17");
|
|
453
460
|
expect(formatAxisValue(1.167815, "price", 0, "USD", "CURRENCY", 0.0024)).toBe("$1.1678");
|
|
@@ -3,7 +3,6 @@ import type { ProjectedChartPoint } from "./data";
|
|
|
3
3
|
import { buildTimeAxis } from "./time-axis";
|
|
4
4
|
import {
|
|
5
5
|
formatAxisValue,
|
|
6
|
-
formatPriceWithCurrency,
|
|
7
6
|
getAxisFractionDigitFloor,
|
|
8
7
|
resolveAxisFractionDigits,
|
|
9
8
|
} from "./axis-format";
|
|
@@ -152,13 +151,13 @@ export function renderChart(
|
|
|
152
151
|
const axisFractionDigits = axisMode === "price"
|
|
153
152
|
? resolveAxisFractionDigits(
|
|
154
153
|
gridLines.map((line) => line.price),
|
|
155
|
-
(price, fixedFractionDigits) =>
|
|
154
|
+
(price, fixedFractionDigits) => formatAxisValue(
|
|
156
155
|
price,
|
|
156
|
+
axisMode,
|
|
157
|
+
points[0]!.close,
|
|
157
158
|
currency,
|
|
158
159
|
assetCategory,
|
|
159
160
|
priceRange,
|
|
160
|
-
0,
|
|
161
|
-
0,
|
|
162
161
|
fixedFractionDigits,
|
|
163
162
|
),
|
|
164
163
|
getAxisFractionDigitFloor(assetCategory, priceRange),
|
|
@@ -87,6 +87,10 @@ export function runDirectCommandAction(options: {
|
|
|
87
87
|
closeAll({ revertThemePreview: false });
|
|
88
88
|
pluginRegistry.showPane("help");
|
|
89
89
|
return;
|
|
90
|
+
case "layout-marketplace":
|
|
91
|
+
closeAll({ revertThemePreview: false });
|
|
92
|
+
pluginRegistry.showPane("layout-marketplace");
|
|
93
|
+
return;
|
|
90
94
|
case "pane-settings":
|
|
91
95
|
if (state.focusedPaneId) openPaneSettingsRoute(state.focusedPaneId);
|
|
92
96
|
return;
|
|
@@ -138,8 +138,15 @@ export const commands: Command[] = [
|
|
|
138
138
|
category: "Config",
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
|
-
id: "layout",
|
|
141
|
+
id: "layout-marketplace",
|
|
142
142
|
prefix: "LAY",
|
|
143
|
+
label: "Layouts",
|
|
144
|
+
description: "Open the layout browser to switch, publish, or add layouts",
|
|
145
|
+
category: "Config",
|
|
146
|
+
},
|
|
147
|
+
{
|
|
148
|
+
id: "layout",
|
|
149
|
+
prefix: "LMA",
|
|
143
150
|
label: "Layout Actions",
|
|
144
151
|
description: "Organize panes and saved layouts",
|
|
145
152
|
hasArg: true,
|
|
@@ -79,7 +79,7 @@ export function buildCurrentLayoutItems({
|
|
|
79
79
|
},
|
|
80
80
|
{
|
|
81
81
|
id: "layout-gridlock",
|
|
82
|
-
label: "
|
|
82
|
+
label: "Tidy Windows",
|
|
83
83
|
detail: currentLayout.floating.length > 0
|
|
84
84
|
? "Infer a tiled layout from the current window positions"
|
|
85
85
|
: "Retile all panes from their current arrangement",
|
|
@@ -112,6 +112,10 @@ export function useCommandBarPaneTemplateActions({
|
|
|
112
112
|
const trimmedArg = rawArg?.trim() || "";
|
|
113
113
|
const argKind = template.shortcut?.argKind ?? template.shortcut?.argPlaceholder;
|
|
114
114
|
if (argKind === "ticker") {
|
|
115
|
+
if (!trimmedArg && template.shortcut?.argOptional) {
|
|
116
|
+
await openPaneTemplateDirect(template);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
115
119
|
const resolvedTicker = await resolveTickerInput(
|
|
116
120
|
trimmedArg || undefined,
|
|
117
121
|
activeTickerSymbol,
|
|
@@ -34,6 +34,9 @@ export function buildRootShortcutFeedback({
|
|
|
34
34
|
if (rootShortcutIntent.source === "pane-template") {
|
|
35
35
|
const argKind = getPaneTemplateArgKind(rootShortcutIntent.template);
|
|
36
36
|
if (argKind === "ticker") {
|
|
37
|
+
if (rootShortcutIntent.template.shortcut?.argOptional && !rootShortcutIntent.argText) {
|
|
38
|
+
return `Shortcut: ${rootShortcutIntent.label}`;
|
|
39
|
+
}
|
|
37
40
|
const symbol = normalizeTickerInput(activeTickerSymbol, rootShortcutIntent.argText);
|
|
38
41
|
if (symbol) {
|
|
39
42
|
return rootShortcutIntent.kind === "inferred-complete"
|
|
@@ -46,6 +46,13 @@ const paneTemplates: PaneTemplateDef[] = [
|
|
|
46
46
|
description: "Search provider instruments",
|
|
47
47
|
shortcut: { prefix: "SRCH", argPlaceholder: "query", argKind: "text" },
|
|
48
48
|
},
|
|
49
|
+
{
|
|
50
|
+
id: "cds-pane",
|
|
51
|
+
paneId: "cds",
|
|
52
|
+
label: "Single-Name CDS",
|
|
53
|
+
description: "CDS shortcut",
|
|
54
|
+
shortcut: { prefix: "CDS", argPlaceholder: "ticker", argKind: "ticker", argOptional: true },
|
|
55
|
+
},
|
|
49
56
|
];
|
|
50
57
|
|
|
51
58
|
const pluginCommands: CommandDef[] = [{
|
|
@@ -135,6 +142,18 @@ describe("ticker data root shortcuts", () => {
|
|
|
135
142
|
}
|
|
136
143
|
});
|
|
137
144
|
|
|
145
|
+
test("optional ticker shortcuts do not infer the active ticker", () => {
|
|
146
|
+
const bare = parse("CDS", "MSFT");
|
|
147
|
+
expect(bare.kind).toBe("partial");
|
|
148
|
+
if (bare.kind === "none") throw new Error("Expected shortcut intent");
|
|
149
|
+
expect(bare.completionQuery).toBeNull();
|
|
150
|
+
|
|
151
|
+
const scoped = parse("CDS ORCL", "MSFT");
|
|
152
|
+
expect(scoped.kind).toBe("complete");
|
|
153
|
+
if (scoped.kind === "none") throw new Error("Expected shortcut intent");
|
|
154
|
+
expect(scoped.argText).toBe("ORCL");
|
|
155
|
+
});
|
|
156
|
+
|
|
138
157
|
test("EM accepts optional text tickers without active ticker inference", () => {
|
|
139
158
|
const bare = parse("EM", "AAPL");
|
|
140
159
|
expect(bare.kind).toBe("partial");
|
|
@@ -145,7 +145,9 @@ export function parseRootShortcutIntent({
|
|
|
145
145
|
if (!match) return { kind: "none" };
|
|
146
146
|
|
|
147
147
|
const argText = trimmed.slice(match.prefix.length).trim();
|
|
148
|
-
const inferredArg =
|
|
148
|
+
const inferredArg = match.source === "pane-template" && match.template?.shortcut?.argOptional
|
|
149
|
+
? null
|
|
150
|
+
: inferShortcutArg(match.argKind, activeTicker);
|
|
149
151
|
const completionQuery = inferredArg ? `${match.prefix} ${inferredArg}` : null;
|
|
150
152
|
|
|
151
153
|
let kind: Exclude<ShortcutIntentKind, "none">;
|
|
@@ -179,17 +179,17 @@ describe("CommandBar", () => {
|
|
|
179
179
|
expect(testSetup.captureCharFrame()).not.toContain("GitHub Light");
|
|
180
180
|
});
|
|
181
181
|
|
|
182
|
-
test("
|
|
182
|
+
test("finds the tidy windows command by its snap alias", async () => {
|
|
183
183
|
const calls: string[] = [];
|
|
184
184
|
|
|
185
185
|
testSetup = await testRender(<CommandBarHarness
|
|
186
|
-
query="
|
|
186
|
+
query="snap"
|
|
187
187
|
configurePluginRegistry={(pluginRegistry) => {
|
|
188
188
|
(pluginRegistry.commands as Map<string, any>).set("gridlock-all", {
|
|
189
189
|
id: "gridlock-all",
|
|
190
|
-
label: "
|
|
191
|
-
description: "Arrange
|
|
192
|
-
keywords: ["grid", "gridlock", "tile", "arrange", "windows", "layout"],
|
|
190
|
+
label: "Tidy Windows",
|
|
191
|
+
description: "Arrange every window into one tiled layout",
|
|
192
|
+
keywords: ["tidy", "snap", "grid", "gridlock", "tile", "arrange", "organize", "organise", "cleanup", "dock", "floating", "windows", "layout"],
|
|
193
193
|
shortcut: "GL",
|
|
194
194
|
category: "config",
|
|
195
195
|
execute: async () => {
|
|
@@ -215,7 +215,7 @@ describe("CommandBar", () => {
|
|
|
215
215
|
await testSetup.renderOnce();
|
|
216
216
|
|
|
217
217
|
const frame = testSetup.captureCharFrame();
|
|
218
|
-
expect(frame).toContain("
|
|
218
|
+
expect(frame).toContain("Tidy Windows");
|
|
219
219
|
expect(frame).toContain("GL");
|
|
220
220
|
|
|
221
221
|
await act(async () => {
|
|
@@ -50,7 +50,7 @@ export function CommandBar({
|
|
|
50
50
|
activePortfolio,
|
|
51
51
|
activeTickerData,
|
|
52
52
|
activeTickerSymbol,
|
|
53
|
-
availableCommands,
|
|
53
|
+
availableCommands: allAvailableCommands,
|
|
54
54
|
cellHeightPx,
|
|
55
55
|
cellWidthPx,
|
|
56
56
|
dispatch,
|
|
@@ -67,6 +67,9 @@ export function CommandBar({
|
|
|
67
67
|
titleBarOverlay,
|
|
68
68
|
visibleListStateRef,
|
|
69
69
|
} = useCommandBarEnvironment();
|
|
70
|
+
const availableCommands = useMemo(() => onCheckForUpdates
|
|
71
|
+
? allAvailableCommands
|
|
72
|
+
: allAvailableCommands.filter((command) => command.id !== "check-for-updates"), [allAvailableCommands, onCheckForUpdates]);
|
|
70
73
|
const {
|
|
71
74
|
applyThemePreview,
|
|
72
75
|
clearThemePreview,
|
|
@@ -142,6 +142,23 @@ function registerOptionalTextPane(pluginRegistry: MutablePaneRegistry): void {
|
|
|
142
142
|
});
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
function registerOptionalTickerPane(pluginRegistry: MutablePaneRegistry): void {
|
|
146
|
+
mutableRegistryMap(pluginRegistry.panes).set("optional-ticker", {
|
|
147
|
+
id: "optional-ticker",
|
|
148
|
+
name: "Optional Ticker",
|
|
149
|
+
component: () => null,
|
|
150
|
+
defaultPosition: "right",
|
|
151
|
+
defaultMode: "floating",
|
|
152
|
+
});
|
|
153
|
+
mutableRegistryMap(pluginRegistry.paneTemplates).set("optional-ticker-pane", {
|
|
154
|
+
id: "optional-ticker-pane",
|
|
155
|
+
paneId: "optional-ticker",
|
|
156
|
+
label: "Optional Ticker",
|
|
157
|
+
description: "Open market-wide or for one ticker.",
|
|
158
|
+
shortcut: { prefix: "OT", argPlaceholder: "ticker", argKind: "ticker", argOptional: true },
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
145
162
|
function registerQueryOnlyPane(pluginRegistry: MutablePaneRegistry): void {
|
|
146
163
|
mutableRegistryMap(pluginRegistry.panes).set("prediction-markets", {
|
|
147
164
|
id: "prediction-markets",
|
|
@@ -204,7 +221,7 @@ describe("CommandBar pane and layout routes", () => {
|
|
|
204
221
|
|
|
205
222
|
test("renders layout mode with focused pane actions", async () => {
|
|
206
223
|
testSetup = await testRender(<CommandBarHarness
|
|
207
|
-
query="
|
|
224
|
+
query="LMA "
|
|
208
225
|
configureConfig={layoutModeConfig}
|
|
209
226
|
configureState={layoutModeState}
|
|
210
227
|
/>, {
|
|
@@ -248,9 +265,29 @@ describe("CommandBar pane and layout routes", () => {
|
|
|
248
265
|
expect(actions.some((action) => action.type === "UNDO_LAYOUT")).toBe(true);
|
|
249
266
|
});
|
|
250
267
|
|
|
268
|
+
test("opens Layouts as a normal pane from LAY", async () => {
|
|
269
|
+
const opened: string[] = [];
|
|
270
|
+
testSetup = await testRender(
|
|
271
|
+
<CommandBarHarness
|
|
272
|
+
query="LAY"
|
|
273
|
+
live
|
|
274
|
+
configurePluginRegistry={(pluginRegistry) => {
|
|
275
|
+
pluginRegistry.showPane = (paneId) => opened.push(paneId);
|
|
276
|
+
}}
|
|
277
|
+
/>,
|
|
278
|
+
{ width: 90, height: 18 },
|
|
279
|
+
);
|
|
280
|
+
await testSetup.renderOnce();
|
|
281
|
+
expect(testSetup.captureCharFrame()).toContain("Layouts");
|
|
282
|
+
|
|
283
|
+
await clickFrameText("Layouts");
|
|
284
|
+
|
|
285
|
+
expect(opened).toEqual(["layout-marketplace"]);
|
|
286
|
+
});
|
|
287
|
+
|
|
251
288
|
test("renders filtered saved layouts with textual previews", async () => {
|
|
252
289
|
testSetup = await testRender(<CommandBarHarness
|
|
253
|
-
query="
|
|
290
|
+
query="LMA Research"
|
|
254
291
|
configureConfig={layoutModeConfig}
|
|
255
292
|
configureState={layoutModeState}
|
|
256
293
|
/>, {
|
|
@@ -353,6 +390,33 @@ describe("CommandBar pane and layout routes", () => {
|
|
|
353
390
|
expect(testSetup.captureCharFrame()).not.toContain("Create Pane");
|
|
354
391
|
});
|
|
355
392
|
|
|
393
|
+
test("keeps an optional ticker shortcut market-wide when a ticker is active", async () => {
|
|
394
|
+
const created: CreatedPaneCall[] = [];
|
|
395
|
+
|
|
396
|
+
testSetup = await testRender(<CommandBarHarness
|
|
397
|
+
query="OT"
|
|
398
|
+
selectedTicker="AAPL"
|
|
399
|
+
live
|
|
400
|
+
configurePluginRegistry={(pluginRegistry) => {
|
|
401
|
+
registerOptionalTickerPane(pluginRegistry);
|
|
402
|
+
recordPaneCreations(pluginRegistry, created);
|
|
403
|
+
}}
|
|
404
|
+
/>, {
|
|
405
|
+
width: 100,
|
|
406
|
+
height: 18,
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
await testSetup.renderOnce();
|
|
410
|
+
|
|
411
|
+
await act(async () => {
|
|
412
|
+
testSetup!.mockInput.pressEnter();
|
|
413
|
+
await Bun.sleep(0);
|
|
414
|
+
await testSetup!.renderOnce();
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
expect(created).toEqual([{ templateId: "optional-ticker-pane", options: undefined }]);
|
|
418
|
+
});
|
|
419
|
+
|
|
356
420
|
for (const scenario of [
|
|
357
421
|
{
|
|
358
422
|
query: "prediction markets",
|
|
@@ -14,7 +14,8 @@ describe("command bar view model helpers", () => {
|
|
|
14
14
|
expect(resolveCommandBarMode("T NVDA")).toMatchObject({ kind: "search", badge: "T" });
|
|
15
15
|
expect(resolveCommandBarMode("TH ")).toMatchObject({ kind: "themes", badge: "THEMES" });
|
|
16
16
|
expect(resolveCommandBarMode("PL notes")).toMatchObject({ kind: "plugins", badge: "PLUGINS" });
|
|
17
|
-
expect(resolveCommandBarMode("LAY
|
|
17
|
+
expect(resolveCommandBarMode("LAY")).toMatchObject({ kind: "direct-command", badge: "COMMAND" });
|
|
18
|
+
expect(resolveCommandBarMode("LMA ")).toMatchObject({ kind: "layout", badge: "LAYOUT" });
|
|
18
19
|
expect(resolveCommandBarMode("NP ")).toMatchObject({ kind: "default", badge: "FILTER" });
|
|
19
20
|
expect(resolveCommandBarMode("PS")).toMatchObject({ kind: "direct-command", badge: "COMMAND" });
|
|
20
21
|
expect(resolveCommandBarMode("AW")).toMatchObject({ kind: "direct-command", badge: "COMMAND" });
|
|
@@ -3,6 +3,7 @@ import { cloneLayout, createDefaultConfig, findPaneInstance, type LayoutConfig }
|
|
|
3
3
|
import { createInitialState } from "../../../state/app/context";
|
|
4
4
|
import { createTestDataProvider } from "../../../test-support/data-provider";
|
|
5
5
|
import { applyPaneSettingFieldValue, createPaneTemplateOrThrow } from "./ops";
|
|
6
|
+
import type { TickerRecord } from "../../../types/ticker";
|
|
6
7
|
|
|
7
8
|
function makeDataProvider() {
|
|
8
9
|
return createTestDataProvider({ id: "test" });
|
|
@@ -68,6 +69,59 @@ describe("createPaneTemplateOrThrow", () => {
|
|
|
68
69
|
expect(placeCalls).toHaveLength(0);
|
|
69
70
|
});
|
|
70
71
|
|
|
72
|
+
test("uses an explicit shared symbol instead of the recipient's active ticker", async () => {
|
|
73
|
+
const config = createDefaultConfig("/tmp/gloomberb-workflow-ops-test");
|
|
74
|
+
const state = createInitialState(config);
|
|
75
|
+
const msft: TickerRecord = {
|
|
76
|
+
metadata: {
|
|
77
|
+
ticker: "MSFT",
|
|
78
|
+
exchange: "NASDAQ",
|
|
79
|
+
currency: "USD",
|
|
80
|
+
name: "Microsoft",
|
|
81
|
+
portfolios: [],
|
|
82
|
+
watchlists: [],
|
|
83
|
+
positions: [],
|
|
84
|
+
broker_contracts: [],
|
|
85
|
+
custom: {},
|
|
86
|
+
tags: [],
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
state.tickers.set("MSFT", msft);
|
|
90
|
+
let createdSymbol: string | null | undefined;
|
|
91
|
+
|
|
92
|
+
await createPaneTemplateOrThrow("ticker-pane", { symbol: "MSFT" }, {
|
|
93
|
+
dataProvider: makeDataProvider() as any,
|
|
94
|
+
tickerRepository: makeTickerRepository() as any,
|
|
95
|
+
dispatch: () => {},
|
|
96
|
+
getState: () => state,
|
|
97
|
+
pluginRegistry: {
|
|
98
|
+
paneTemplates: new Map([["ticker-pane", {
|
|
99
|
+
id: "ticker-pane",
|
|
100
|
+
paneId: "ticker-view",
|
|
101
|
+
label: "Ticker View",
|
|
102
|
+
description: "Ticker View",
|
|
103
|
+
shortcut: { prefix: "TV", argPlaceholder: "ticker", argKind: "ticker" },
|
|
104
|
+
createInstance: (_context: unknown, options: { symbol?: string } | undefined) => {
|
|
105
|
+
createdSymbol = options?.symbol;
|
|
106
|
+
return { binding: { kind: "fixed", symbol: options?.symbol ?? "" } };
|
|
107
|
+
},
|
|
108
|
+
}]]),
|
|
109
|
+
panes: new Map([["ticker-view", {
|
|
110
|
+
id: "ticker-view",
|
|
111
|
+
name: "Ticker View",
|
|
112
|
+
component: () => null,
|
|
113
|
+
defaultPosition: "right",
|
|
114
|
+
}]]),
|
|
115
|
+
getPaneTemplatePluginId: () => undefined,
|
|
116
|
+
events: { emit: () => {} },
|
|
117
|
+
} as any,
|
|
118
|
+
buildPaneInstance: () => ({ instanceId: "ticker-view:1", paneId: "ticker-view" }) as any,
|
|
119
|
+
placePaneInstance: () => {},
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
expect(createdSymbol).toBe("MSFT");
|
|
123
|
+
});
|
|
124
|
+
|
|
71
125
|
test("passes pane template instance ids through to pane creation", async () => {
|
|
72
126
|
const config = createDefaultConfig("/tmp/gloomberb-workflow-ops-test");
|
|
73
127
|
const state = createInitialState(config);
|
|
@@ -152,7 +152,7 @@ async function resolvePaneTemplateOptions(
|
|
|
152
152
|
let resolvedOptions = options;
|
|
153
153
|
if (template.shortcut?.argPlaceholder === "ticker") {
|
|
154
154
|
const resolvedTicker = await resolveTickerInputOrThrow(
|
|
155
|
-
resolvedOptions?.arg,
|
|
155
|
+
resolvedOptions?.symbol ?? resolvedOptions?.arg,
|
|
156
156
|
baseContext.activeTicker,
|
|
157
157
|
baseContext.activeCollectionId,
|
|
158
158
|
deps,
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { resolveCompanyLogoSrc } from "./company-logo";
|
|
3
|
+
|
|
4
|
+
describe("resolveCompanyLogoSrc", () => {
|
|
5
|
+
test("builds a ticker logo URL", () => {
|
|
6
|
+
expect(resolveCompanyLogoSrc({ symbol: "aapl", assetCategory: "STK" }))
|
|
7
|
+
.toMatch(/\/cloud\/logos\/ticker\/AAPL$/);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
test("maps crypto quote pairs onto the crypto path", () => {
|
|
11
|
+
expect(resolveCompanyLogoSrc({ symbol: "btc-usd", assetCategory: "CRYPTO" }))
|
|
12
|
+
.toMatch(/\/cloud\/logos\/crypto\/BTC$/);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
test("skips cash and options", () => {
|
|
16
|
+
expect(resolveCompanyLogoSrc({ symbol: "USD", assetCategory: "CASH" })).toBeNull();
|
|
17
|
+
expect(resolveCompanyLogoSrc({ symbol: "AAPL 240119C00190000", assetCategory: "OPT" })).toBeNull();
|
|
18
|
+
});
|
|
19
|
+
});
|