gloomberb 0.16.3 → 0.16.4
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/package.json +4 -1
- package/src/api-client/askg.ts +43 -0
- package/src/api-client/auth.ts +15 -0
- package/src/api-client/beneficial-owners.ts +4 -0
- package/src/api-client/chat.ts +19 -0
- package/src/api-client/company-kpis.ts +14 -2
- package/src/api-client/data.ts +34 -16
- package/src/api-client/index.ts +12 -0
- package/src/api-client/normalizers.ts +27 -0
- package/src/api-client/paths.ts +88 -23
- package/src/api-client/request.ts +2 -1
- package/src/api-client/research-activity.ts +31 -0
- package/src/api-client/socket.ts +39 -8
- package/src/api-client/types.ts +57 -1
- package/src/api-client/upgrade-experiment.ts +90 -0
- package/src/app/global-shortcuts.ts +11 -1
- package/src/app/keybindings/actions.ts +10 -0
- package/src/app/pane-runtime/index.tsx +8 -0
- package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
- package/src/app/pane-runtime/plugin-bindings.ts +65 -25
- package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
- package/src/app/runtime/desktop-deeplink.ts +5 -0
- package/src/app/star-prompt/model.ts +98 -0
- package/src/app/star-prompt/runtime.ts +41 -0
- package/src/app/star-prompt/status-line.tsx +190 -0
- package/src/app.tsx +13 -7
- package/src/architecture/core-builtin-allowlist.json +70 -0
- package/src/brokers/signed-in/adapter.ts +33 -2
- package/src/brokers/signed-in/client.ts +10 -0
- package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
- package/src/brokers/signed-in/sign-in.ts +62 -5
- package/src/capabilities/registry.ts +8 -1
- package/src/capabilities/types.ts +2 -0
- package/src/cli/commands/crypto-hints.ts +12 -4
- package/src/cli/commands/market.ts +359 -99
- package/src/cli/commands/options-chain.ts +172 -0
- package/src/cli/commands/options-leaps.ts +422 -0
- package/src/cli/commands/overview.ts +164 -26
- package/src/cli/commands/search.ts +29 -8
- package/src/cli/commands/system.ts +99 -19
- package/src/cli/commands/themes.ts +64 -0
- package/src/cli/commands/ticker.ts +62 -30
- package/src/cli/desktop-pane-shot.ts +97 -16
- package/src/cli/errors.ts +25 -2
- package/src/cli/freshness.ts +7 -4
- package/src/cli/fx-pair.ts +57 -0
- package/src/cli/help.ts +14 -1
- package/src/cli/index-roots.ts +51 -0
- package/src/cli/index.ts +9 -6
- package/src/cli/listing-arg.ts +211 -3
- package/src/cli/not-a-ticker.ts +37 -0
- package/src/cli/options.ts +50 -6
- package/src/cli/pane-functions/capabilities.ts +8 -2
- package/src/cli/pane-functions/catalog.ts +13 -2
- package/src/cli/pane-functions/data.ts +36 -5
- package/src/cli/pane-functions/dom.ts +21 -13
- package/src/cli/pane-functions/freshness.ts +205 -26
- package/src/cli/pane-functions/function-help.ts +67 -22
- package/src/cli/pane-functions/headless.ts +5 -4
- package/src/cli/pane-functions/index.ts +3 -0
- package/src/cli/pane-functions/market-session.ts +133 -0
- package/src/cli/pane-functions/options.ts +15 -4
- package/src/cli/pane-functions/report-notices.ts +63 -0
- package/src/cli/pane-functions/report.ts +4 -0
- package/src/cli/pane-functions/resolver.ts +4 -6
- package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
- package/src/cli/pane-functions/screenshot.ts +105 -23
- package/src/cli/registry.ts +2 -1
- package/src/cli/result.ts +50 -13
- package/src/cli/row-window.ts +76 -0
- package/src/cli/scroll-truncation.ts +34 -0
- package/src/components/chart/composite/composite-chart.tsx +43 -4
- package/src/components/chart/composite/legend.tsx +39 -6
- package/src/components/chart/composite/rasterizer.ts +64 -4
- package/src/components/chart/composite/series-cues.ts +71 -0
- package/src/components/chart/composite/text-renderer.ts +16 -8
- package/src/components/chart/composite/types.ts +3 -0
- package/src/components/command-bar/commands/direct/index.ts +9 -1
- package/src/components/command-bar/commands/plugin/items.ts +3 -1
- package/src/components/command-bar/commands/registry.ts +7 -0
- package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
- package/src/components/command-bar/pane-templates/workflow.ts +5 -0
- package/src/components/command-bar/routes/root/command-items.ts +2 -0
- package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
- package/src/components/command-bar/routes/root/results.ts +40 -10
- package/src/components/command-bar/routes/root/runtime.ts +8 -0
- package/src/components/command-bar/routes/root/search-providers.ts +97 -14
- package/src/components/command-bar/surface/index.tsx +18 -4
- package/src/components/command-bar/theme-picker.tsx +48 -13
- package/src/components/command-bar/workflow/ops.ts +5 -3
- package/src/components/data-table/view.tsx +28 -9
- package/src/components/layout/floating-pane.tsx +6 -2
- package/src/components/layout/market-summary.ts +23 -9
- package/src/components/layout/pane/content.tsx +46 -29
- package/src/components/layout/pane/cycle-order.ts +2 -11
- package/src/components/layout/pane/header.tsx +2 -1
- package/src/components/layout/pane/title.ts +1 -1
- package/src/components/layout/presentation.ts +22 -0
- package/src/components/layout/shell/active-drag.ts +1 -1
- package/src/components/layout/shell/drag/index.ts +4 -0
- package/src/components/layout/shell/drag/live.ts +76 -5
- package/src/components/layout/shell/drag/runtime.ts +8 -8
- package/src/components/layout/shell/index.tsx +47 -6
- package/src/components/layout/shell/layout-state.ts +5 -11
- package/src/components/layout/shell/menu.ts +28 -1
- package/src/components/layout/shell/pane/actions.ts +2 -1
- package/src/components/layout/shell/pane/layers.tsx +51 -10
- package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
- package/src/components/layout/shell/pane/new-layout.ts +116 -0
- package/src/components/layout/shell/shortcuts.ts +5 -2
- package/src/components/layout/shell/visible-layout.ts +44 -1
- package/src/components/layout/status-bar.tsx +4 -1
- package/src/components/markdown-text.tsx +9 -5
- package/src/components/metric-treemap/heat-surface.tsx +27 -8
- package/src/components/metric-treemap/heat-tiles.ts +48 -17
- package/src/components/onboarding/onboarding-frame.tsx +33 -10
- package/src/components/onboarding/onboarding-wizard.tsx +49 -40
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
- package/src/components/onboarding/pro-step-layout.ts +86 -0
- package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
- package/src/components/ui/button.tsx +10 -0
- package/src/components/ui/data-table/index.tsx +38 -0
- package/src/components/ui/data-table/types.ts +8 -1
- package/src/components/ui/data-table/visible-range.ts +2 -0
- package/src/components/ui/icon.tsx +11 -2
- package/src/components/ui/index.ts +1 -1
- package/src/core/connection-health.ts +1 -1
- package/src/core/state/app/layout-reducer.ts +89 -3
- package/src/core/state/app/state.ts +8 -5
- package/src/core/state/app/types.ts +13 -2
- package/src/data/config/layout.ts +55 -21
- package/src/data/config/store/fold-panes.ts +149 -0
- package/src/data/config/store/migrations.ts +7 -0
- package/src/data/config/store/node.ts +27 -6
- package/src/data/config/store/normalize.ts +37 -3
- package/src/i18n/es.ts +14 -1
- package/src/i18n/ja.ts +14 -1
- package/src/i18n/ko.ts +14 -1
- package/src/i18n/zh-cn.ts +14 -1
- package/src/i18n/zh-tw.ts +14 -1
- package/src/layout/desks.ts +1 -2
- package/src/layout/pane-layout-move.ts +264 -0
- package/src/layout/pane-manager/hidden-panes.ts +160 -0
- package/src/layout/pane-manager/index.ts +1 -0
- package/src/market-data/coordinator/entries.ts +7 -1
- package/src/market-data/coordinator/quotes.ts +2 -2
- package/src/market-data/market/freshness.ts +118 -18
- package/src/market-data/market/status.ts +40 -0
- package/src/market-data/options-alternatives.ts +69 -0
- package/src/market-data/quotes/contributions.ts +19 -2
- package/src/market-data/quotes/metadata.ts +16 -0
- package/src/market-data/quotes/screener-live-quotes.ts +56 -5
- package/src/plugins/builtin/account-management/keyboard.ts +3 -0
- package/src/plugins/builtin/account-management/model.ts +1 -0
- package/src/plugins/builtin/account-management/navigation.ts +1 -1
- package/src/plugins/builtin/account-management/pane.tsx +31 -4
- package/src/plugins/builtin/alerts/quotes.ts +3 -0
- package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
- package/src/plugins/builtin/analytics/risk-model.ts +37 -8
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
- package/src/plugins/builtin/backtest/pane.tsx +1 -1
- package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
- package/src/plugins/builtin/broker-manager/index.tsx +7 -3
- package/src/plugins/builtin/broker-manager/model.ts +8 -0
- package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
- package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
- package/src/plugins/builtin/broker-manager/table.ts +5 -0
- package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
- package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
- package/src/plugins/builtin/chat/channels.ts +34 -36
- package/src/plugins/builtin/chat/command-bar.ts +138 -0
- package/src/plugins/builtin/chat/composer-commands.ts +21 -1
- package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
- package/src/plugins/builtin/chat/content/footer.tsx +5 -3
- package/src/plugins/builtin/chat/content/index.tsx +21 -1
- package/src/plugins/builtin/chat/content/mentions.ts +2 -1
- package/src/plugins/builtin/chat/content/user-map.ts +3 -1
- package/src/plugins/builtin/chat/controller/index.ts +48 -0
- package/src/plugins/builtin/chat/conversations.ts +256 -0
- package/src/plugins/builtin/chat/discord-command.ts +91 -0
- package/src/plugins/builtin/chat/edit-window.ts +12 -6
- package/src/plugins/builtin/chat/ghost-user.ts +14 -0
- package/src/plugins/builtin/chat/index.tsx +40 -2
- package/src/plugins/builtin/chat/layout.ts +2 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
- package/src/plugins/builtin/chat/message/header.tsx +24 -15
- package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
- package/src/plugins/builtin/chat/message/render-state.ts +3 -0
- package/src/plugins/builtin/chat/pane-requests.ts +60 -0
- package/src/plugins/builtin/chat/pane-state.ts +32 -2
- package/src/plugins/builtin/chat/pane.tsx +8 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
- package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
- package/src/plugins/builtin/cloud/askg/model.ts +129 -70
- package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
- package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
- package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
- package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
- package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
- package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
- package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
- package/src/plugins/builtin/cloud/plugin.tsx +36 -1
- package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
- package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
- package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
- package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
- package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
- package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
- package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
- package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
- package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
- package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
- package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
- package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
- package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
- package/src/plugins/builtin/comment-letters/client.ts +1 -3
- package/src/plugins/builtin/company-kpis/client.ts +23 -8
- package/src/plugins/builtin/company-kpis/headless.ts +3 -1
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
- package/src/plugins/builtin/composite-plugins.ts +107 -64
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
- package/src/plugins/builtin/credit-boards/index.tsx +21 -21
- package/src/plugins/builtin/credit-boards/model.ts +0 -1
- package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
- package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
- package/src/plugins/builtin/debt-maturities/client.ts +37 -9
- package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
- package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/earnings-calls/data.ts +28 -7
- package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
- package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
- package/src/plugins/builtin/econ/calendar-model.ts +42 -17
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/index.tsx +68 -22
- package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
- package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
- package/src/plugins/builtin/econ-statistics/view.ts +17 -0
- package/src/plugins/builtin/executives/data.ts +17 -6
- package/src/plugins/builtin/executives/pane.tsx +16 -7
- package/src/plugins/builtin/filing-events/pane.tsx +22 -5
- package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
- package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
- package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
- package/src/plugins/builtin/gpu/headless.ts +9 -3
- package/src/plugins/builtin/gpu/model.ts +14 -5
- package/src/plugins/builtin/help/index.tsx +1 -0
- package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
- package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
- package/src/plugins/builtin/holders/headless.ts +6 -1
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/insider/index.tsx +4 -4
- package/src/plugins/builtin/iv-history/charts.tsx +21 -7
- package/src/plugins/builtin/iv-history/client.ts +12 -2
- package/src/plugins/builtin/iv-history/format.ts +33 -2
- package/src/plugins/builtin/iv-history/headless.ts +22 -10
- package/src/plugins/builtin/iv-history/index.tsx +3 -3
- package/src/plugins/builtin/iv-history/model.ts +61 -13
- package/src/plugins/builtin/iv-history/pane.tsx +11 -3
- package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
- package/src/plugins/builtin/jobs/pane.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
- package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +109 -39
- package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
- package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
- package/src/plugins/builtin/layout-manager/index.tsx +9 -1
- package/src/plugins/builtin/macro-day/client.ts +24 -0
- package/src/plugins/builtin/macro-day/headless.ts +11 -8
- package/src/plugins/builtin/macro-day/index.tsx +3 -0
- package/src/plugins/builtin/macro-day/model.ts +11 -6
- package/src/plugins/builtin/macro-day/pane.tsx +12 -8
- package/src/plugins/builtin/macro-day/release-table.ts +40 -0
- package/src/plugins/builtin/macro-day/releases.ts +62 -44
- package/src/plugins/builtin/market-heatmap/data.ts +25 -5
- package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
- package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
- package/src/plugins/builtin/market-heatmap/model.ts +94 -3
- package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
- package/src/plugins/builtin/market-movers/headless.ts +31 -4
- package/src/plugins/builtin/market-movers/index.tsx +8 -1
- package/src/plugins/builtin/market-movers/model.ts +71 -17
- package/src/plugins/builtin/market-movers/table.ts +50 -23
- package/src/plugins/builtin/news/headless.ts +9 -2
- package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
- package/src/plugins/builtin/openfda/client.ts +1 -1
- package/src/plugins/builtin/options/carry.ts +95 -0
- package/src/plugins/builtin/options/index.tsx +58 -5
- package/src/plugins/builtin/options/strike-window.ts +183 -0
- package/src/plugins/builtin/options/table.ts +55 -3
- package/src/plugins/builtin/options/types.ts +8 -1
- package/src/plugins/builtin/options/view.tsx +161 -12
- package/src/plugins/builtin/options-positioning/headless.ts +4 -2
- package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
- package/src/plugins/builtin/options-scenario/client.ts +73 -17
- package/src/plugins/builtin/options-scenario/headless.ts +71 -24
- package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
- package/src/plugins/builtin/options-scenario/index.tsx +9 -2
- package/src/plugins/builtin/options-scenario/model.ts +33 -0
- package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
- package/src/plugins/builtin/pe-band/model.ts +2 -2
- package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
- package/src/plugins/builtin/perps/equity-row.tsx +8 -5
- package/src/plugins/builtin/perps/model.ts +16 -5
- package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
- package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
- package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
- package/src/plugins/builtin/plugin-module.ts +87 -4
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
- package/src/plugins/builtin/portfolio-list/cli/render.ts +87 -18
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
- package/src/plugins/builtin/portfolio-list/headless.ts +38 -7
- package/src/plugins/builtin/realized-vol/headless.ts +3 -0
- package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
- package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
- package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
- package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
- package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
- package/src/plugins/builtin/risk-factors/data.ts +23 -9
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
- package/src/plugins/builtin/seasonality/pane.tsx +1 -1
- package/src/plugins/builtin/sec/index.tsx +5 -5
- package/src/plugins/builtin/sectors/client.ts +10 -2
- package/src/plugins/builtin/sectors/headless.ts +35 -6
- package/src/plugins/builtin/sectors/index.tsx +18 -1
- package/src/plugins/builtin/sectors/sector-model.ts +150 -41
- package/src/plugins/builtin/shared/command-tab.ts +51 -0
- package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
- package/src/plugins/builtin/shared/members-menu.tsx +4 -4
- package/src/plugins/builtin/shared/report-freshness.ts +50 -2
- package/src/plugins/builtin/shared/ticker-request.ts +25 -1
- package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
- package/src/plugins/builtin/shared/volatility/options.ts +1 -46
- package/src/plugins/builtin/short-interest/index.tsx +19 -4
- package/src/plugins/builtin/short-interest/surface.tsx +4 -4
- package/src/plugins/builtin/short-volume/index.tsx +1 -10
- package/src/plugins/builtin/supply-chain/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +43 -21
- package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
- package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
- package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
- package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
- package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
- package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
- package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
- package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
- package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
- package/src/plugins/builtin/vol-surface/client.ts +6 -2
- package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
- package/src/plugins/builtin/vol-surface/headless.ts +124 -20
- package/src/plugins/builtin/vol-surface/model.ts +23 -11
- package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
- package/src/plugins/builtin/vol-surface/tables.ts +124 -0
- package/src/plugins/builtin/world-indices/client.ts +22 -4
- package/src/plugins/builtin/world-indices/headless.ts +54 -27
- package/src/plugins/builtin/world-indices/model.ts +4 -2
- package/src/plugins/builtin/world-indices/table.tsx +13 -2
- package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
- package/src/plugins/builtin/world-venue-map/index.ts +1 -1
- package/src/plugins/builtin/yield-curve/index.tsx +1 -1
- package/src/plugins/builtin-editorial.ts +126 -0
- package/src/plugins/catalog-backend.ts +16 -9
- package/src/plugins/catalog-browser.ts +127 -63
- package/src/plugins/catalog-manifest.ts +73 -40
- package/src/plugins/catalog-ui.ts +26 -6
- package/src/plugins/extracted-plugins.ts +7 -1
- package/src/plugins/ownership.ts +150 -7
- package/src/plugins/plugin-functions.ts +54 -0
- package/src/plugins/plugin-off.ts +36 -0
- package/src/plugins/registry/host-actions.ts +16 -0
- package/src/plugins/registry/index.ts +84 -24
- package/src/plugins/registry/plugin-state.ts +25 -1
- package/src/plugins/runtime/context.tsx +29 -0
- package/src/react/input.ts +48 -1
- package/src/remote/app-host.tsx +5 -0
- package/src/remote/controller.ts +3 -53
- package/src/remote/matching.ts +69 -0
- package/src/remote/schema.ts +1 -1
- package/src/remote/semantic-tree.tsx +14 -0
- package/src/remote/types.ts +2 -0
- package/src/renderers/dom/desktop/icons.tsx +14 -0
- package/src/renderers/dom/desktop/query-bar.tsx +59 -32
- package/src/renderers/dom/host/box.tsx +1 -1
- package/src/renderers/dom/host/drag-shield.ts +17 -6
- package/src/renderers/dom/host/live-frame.ts +10 -2
- package/src/renderers/dom/input-host.tsx +6 -1
- package/src/renderers/dom/styles.css +13 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +5 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -1
- package/src/renderers/electrobun/shared/protocol.ts +2 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
- package/src/renderers/electrobun/view/main.tsx +4 -1
- package/src/renderers/opentui/input-host.tsx +6 -1
- package/src/renderers/opentui/start.tsx +9 -0
- package/src/shares/portable-layout.ts +16 -6
- package/src/sources/gloomberb-cloud/index.ts +19 -10
- package/src/sources/gloomberb-cloud/normalizers.ts +4 -1
- package/src/sources/listing-symbols.ts +11 -5
- package/src/sources/provider-errors.ts +88 -3
- package/src/sources/provider-router/cached-routes.ts +8 -4
- package/src/sources/provider-router/financial-routes.ts +11 -5
- package/src/sources/provider-router/financials.ts +42 -15
- package/src/sources/provider-router/history.ts +27 -11
- package/src/sources/provider-router/index.ts +1 -1
- package/src/sources/provider-router/primary.ts +23 -5
- package/src/sources/sec-edgar.ts +68 -8
- package/src/sync/core-contributors.ts +4 -3
- package/src/test-support/cli-context.ts +2 -1
- package/src/test-support/data-provider.ts +34 -0
- package/src/test-support/shell-pane-keys.tsx +1 -0
- package/src/theme/series-colors.ts +46 -0
- package/src/theme/themes.ts +149 -0
- package/src/tickers/search/index.ts +22 -2
- package/src/tickers/search/ranking.ts +45 -1
- package/src/tickers/search/types.ts +3 -1
- package/src/time-series/resolve.ts +1 -14
- package/src/types/config.ts +61 -1
- package/src/types/headless.ts +11 -0
- package/src/types/instrument.ts +8 -0
- package/src/types/plugin.ts +49 -3
- package/src/ui/host.tsx +8 -4
- package/src/ui/index.tsx +2 -0
- package/src/utils/cli-output.ts +14 -1
- package/src/utils/exchanges.ts +13 -0
- package/src/utils/format.ts +2 -1
- package/src/utils/fx-market-hours.ts +34 -0
- package/src/utils/option-expiry.ts +126 -0
- package/src/utils/price-history.ts +39 -6
- package/src/utils/rpc-timeout-error.ts +6 -2
- package/src/utils/sec-eps-basis.ts +159 -30
- package/src/utils/sec.ts +38 -34
- package/src/utils/throttled-fetch.ts +51 -22
|
@@ -10,7 +10,9 @@ import { CompositeLegend } from "./legend";
|
|
|
10
10
|
import { ChartToolbar, CHART_TOOLBAR_WIDTH } from "./toolbar";
|
|
11
11
|
import { AsciiText, Box, ChartSurface, Text, useUiCapabilities } from "../../../ui";
|
|
12
12
|
import { useOptionalPaneInstanceId } from "../../../state/app/context";
|
|
13
|
-
import { useThemeColors } from "../../../theme/theme-context";
|
|
13
|
+
import { useThemeColors, useThemeId } from "../../../theme/theme-context";
|
|
14
|
+
import { themedSeriesColor, themeSeriesColors } from "../../../theme/series-colors";
|
|
15
|
+
import { assignSeriesLineCues } from "./series-cues";
|
|
14
16
|
import { chartSurfaceBackground, usePaneSurface } from "../../layout/pane/surface";
|
|
15
17
|
import { CHART_WATERMARK_ROLE } from "../../../utils/screenshot-watermark";
|
|
16
18
|
import type { ResolvedSeries } from "../../../time-series/types";
|
|
@@ -56,7 +58,28 @@ function chartWatermarkScale(plotWidthPx: number, plotHeightPx: number): number
|
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
const NO_X_MARKERS: readonly CompositeChartXMarker[] = [];
|
|
59
|
-
|
|
61
|
+
|
|
62
|
+
/** Default-palette series in the theme's own palette, where it has one; the same list otherwise. */
|
|
63
|
+
function useThemedSeriesList(list: ResolvedSeries[], themeId: string): ResolvedSeries[];
|
|
64
|
+
function useThemedSeriesList(list: ResolvedSeries[] | undefined, themeId: string): ResolvedSeries[] | undefined;
|
|
65
|
+
function useThemedSeriesList(list: ResolvedSeries[] | undefined, themeId: string): ResolvedSeries[] | undefined {
|
|
66
|
+
return useMemo(() => {
|
|
67
|
+
if (!list || !themeSeriesColors(themeId)) return list;
|
|
68
|
+
return list.map((entry) => {
|
|
69
|
+
const color = themedSeriesColor(entry.color, themeId);
|
|
70
|
+
return color === entry.color ? entry : { ...entry, color };
|
|
71
|
+
});
|
|
72
|
+
}, [list, themeId]);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function CompositeChart(props: CompositeChartProps) {
|
|
76
|
+
const themeId = useThemeId();
|
|
77
|
+
const series = useThemedSeriesList(props.series, themeId);
|
|
78
|
+
const legendSeries = useThemedSeriesList(props.legendSeries, themeId);
|
|
79
|
+
return <ThemedCompositeChart {...props} series={series} legendSeries={legendSeries} />;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function ThemedCompositeChart({
|
|
60
83
|
series,
|
|
61
84
|
legendSeries,
|
|
62
85
|
timelineSeries,
|
|
@@ -155,6 +178,17 @@ export function CompositeChart({
|
|
|
155
178
|
() => new Set(visibleSeries.map((entry) => entry.id)),
|
|
156
179
|
[visibleSeries],
|
|
157
180
|
);
|
|
181
|
+
// Assigned over the legend's list, hidden lines included, so toggling one
|
|
182
|
+
// never restyles the rest.
|
|
183
|
+
const cueSource = legendSeries ?? stableSeries;
|
|
184
|
+
const cueKey = cueSource.map((entry) => `${entry.id}\u0000${entry.style}\u0000${entry.profile ? 1 : 0}`).join("\u0001");
|
|
185
|
+
const cueSourceRef = useRef(cueSource);
|
|
186
|
+
cueSourceRef.current = cueSource;
|
|
187
|
+
// Keyed by ids and styles: a live tick hands over new series objects.
|
|
188
|
+
const lineCues = useMemo(() => {
|
|
189
|
+
void cueKey;
|
|
190
|
+
return assignSeriesLineCues(cueSourceRef.current);
|
|
191
|
+
}, [cueKey]);
|
|
158
192
|
const {
|
|
159
193
|
navigationFrame, activeUserViewport, effectiveViewport,
|
|
160
194
|
panViewport, panViewportByRatio, zoomViewport, setViewportRange, resetViewport,
|
|
@@ -239,8 +273,11 @@ export function CompositeChart({
|
|
|
239
273
|
plotHeight,
|
|
240
274
|
);
|
|
241
275
|
// Terminal labels snap to rows; a fractional viewport places them exactly.
|
|
242
|
-
return projectedScene.panels.map((panel) =>
|
|
243
|
-
|
|
276
|
+
return projectedScene.panels.map((panel) => {
|
|
277
|
+
const resized = resizeCompositePanel(panel, panelHeights.get(panel.id) ?? 1, !fractionalViewport);
|
|
278
|
+
return lineCues.size > 0 ? { ...resized, lineCues } : resized;
|
|
279
|
+
});
|
|
280
|
+
}, [fractionalViewport, lineCues, panels, plotHeight, projectedScene]);
|
|
244
281
|
// Static overview charts also pin the latest price with cursor precision.
|
|
245
282
|
const includeCursorLabels = interactive || cursorDate !== undefined || !!scenePanels?.some((panel) => panel.lastPrice);
|
|
246
283
|
const resolvedAxisWidth = useMemo(() => maximumAxisWidth === 0 ? 0 : Math.min(
|
|
@@ -479,6 +516,8 @@ export function CompositeChart({
|
|
|
479
516
|
scene={scene}
|
|
480
517
|
series={visibleLegendSeries}
|
|
481
518
|
visibleSeriesIds={visibleSeriesIds}
|
|
519
|
+
lineCues={lineCues}
|
|
520
|
+
cellPlot={showTextFallback && !isDesktopWeb}
|
|
482
521
|
width={totalWidth}
|
|
483
522
|
accessory={legendAccessory}
|
|
484
523
|
accessoryWidth={legendAccessoryWidth}
|
|
@@ -7,8 +7,15 @@ import type { ResolvedSeries } from "../../../time-series/types";
|
|
|
7
7
|
import { consumeChartMouseEvent, type ChartMouseEvent } from "../core/pointer";
|
|
8
8
|
import { formatCompositePointDetails, formatCompositeSeriesValue, seriesPriceReference } from "./format";
|
|
9
9
|
import type { CompositeChartProps, CompositeChartScene } from "./types";
|
|
10
|
+
import { seriesCueCssBackground, type SeriesLineCue } from "./series-cues";
|
|
10
11
|
|
|
11
12
|
const LEGEND_WHEEL_DELTA_PER_CELL = 8;
|
|
13
|
+
const NO_LINE_CUES: ReadonlyMap<string, SeriesLineCue> = new Map();
|
|
14
|
+
/** The dot every legend entry had before lines carried cues, and still has where they do not. */
|
|
15
|
+
const DOT_MARKER = "● ";
|
|
16
|
+
/** Cells the desktop and web marker takes: an 8px dot, or a 16px line sample, plus its margin. */
|
|
17
|
+
const DOM_DOT_CELLS = 2;
|
|
18
|
+
const DOM_LINE_CELLS = 3;
|
|
12
19
|
|
|
13
20
|
function legendValue(
|
|
14
21
|
series: ResolvedSeries,
|
|
@@ -50,6 +57,8 @@ export function CompositeLegend({
|
|
|
50
57
|
scene,
|
|
51
58
|
series,
|
|
52
59
|
visibleSeriesIds,
|
|
60
|
+
lineCues = NO_LINE_CUES,
|
|
61
|
+
cellPlot = false,
|
|
53
62
|
width,
|
|
54
63
|
accessory,
|
|
55
64
|
accessoryWidth,
|
|
@@ -63,6 +72,10 @@ export function CompositeLegend({
|
|
|
63
72
|
scene: CompositeChartScene | null;
|
|
64
73
|
series: ResolvedSeries[];
|
|
65
74
|
visibleSeriesIds: ReadonlySet<string>;
|
|
75
|
+
/** Each line's cue on a chart that draws several; a chart with one keeps its dot. */
|
|
76
|
+
lineCues?: ReadonlyMap<string, SeriesLineCue>;
|
|
77
|
+
/** The plot is cell text, so a line's cue is its mark rather than its dash. */
|
|
78
|
+
cellPlot?: boolean;
|
|
66
79
|
width: number;
|
|
67
80
|
accessory: CompositeChartProps["legendAccessory"];
|
|
68
81
|
accessoryWidth: CompositeChartProps["legendAccessoryWidth"];
|
|
@@ -79,6 +92,9 @@ export function CompositeLegend({
|
|
|
79
92
|
scene?.cursorValues.map((entry) => [entry.seriesId, entry] as const) ?? [],
|
|
80
93
|
);
|
|
81
94
|
const entries = series.map((entry) => {
|
|
95
|
+
const cue = lineCues.get(entry.id) ?? null;
|
|
96
|
+
const marker = cue ? `${cellPlot ? cue.glyph : cue.sample} ` : DOT_MARKER;
|
|
97
|
+
const markerWidth = isDesktopWeb ? cue ? DOM_LINE_CELLS : DOM_DOT_CELLS : displayWidth(marker);
|
|
82
98
|
const toggleable = !!onToggleSeries && (isSeriesToggleable?.(entry) ?? true);
|
|
83
99
|
const cursorValue = cursorValueById.get(entry.id);
|
|
84
100
|
const changeText = showLatestChangePercent
|
|
@@ -107,10 +123,13 @@ export function CompositeLegend({
|
|
|
107
123
|
const text = [label, valueText].filter(Boolean).join(" ");
|
|
108
124
|
return {
|
|
109
125
|
entry,
|
|
126
|
+
cue,
|
|
127
|
+
marker,
|
|
128
|
+
markerWidth,
|
|
110
129
|
label,
|
|
111
130
|
compactLabel,
|
|
112
131
|
text,
|
|
113
|
-
width: Math.max(1, displayWidth(text)) +
|
|
132
|
+
width: Math.max(1, displayWidth(text)) + markerWidth,
|
|
114
133
|
labelWidth,
|
|
115
134
|
valueText,
|
|
116
135
|
toggleable,
|
|
@@ -122,7 +141,7 @@ export function CompositeLegend({
|
|
|
122
141
|
target.label = label;
|
|
123
142
|
target.labelWidth = labelWidth;
|
|
124
143
|
target.text = [truncateToDisplayWidth(label, labelWidth), target.valueText].filter(Boolean).join(" ");
|
|
125
|
-
target.width = Math.max(1, displayWidth(target.text)) +
|
|
144
|
+
target.width = Math.max(1, displayWidth(target.text)) + target.markerWidth;
|
|
126
145
|
};
|
|
127
146
|
const measureSeriesWidth = () => entries.reduce(
|
|
128
147
|
(total, entry, index) => total + entry.width + (index > 0 ? 1 : 0),
|
|
@@ -146,7 +165,7 @@ export function CompositeLegend({
|
|
|
146
165
|
const minimumSeriesWidth = entries.reduce((total, entry, index) => {
|
|
147
166
|
const text = [truncateToDisplayWidth(entry.compactLabel, Math.min(8, displayWidth(entry.compactLabel))), entry.valueText]
|
|
148
167
|
.filter(Boolean).join(" ");
|
|
149
|
-
return total + Math.max(1, displayWidth(text)) +
|
|
168
|
+
return total + Math.max(1, displayWidth(text)) + entry.markerWidth + (index > 0 ? 1 : 0);
|
|
150
169
|
}, 0);
|
|
151
170
|
if (minimumSeriesWidth <= widthBeforeAccessory) {
|
|
152
171
|
while (measureSeriesWidth() > widthBeforeAccessory) {
|
|
@@ -247,7 +266,7 @@ export function CompositeLegend({
|
|
|
247
266
|
data-gloom-role="composite-chart-legend-scroll"
|
|
248
267
|
>
|
|
249
268
|
<Box flexDirection="row" width={desiredSeriesWidth} height={1} gap={1}>
|
|
250
|
-
{entries.map(({ entry, text, toggleable, tooltip, width: entryWidth }, index) => {
|
|
269
|
+
{entries.map(({ entry, cue, marker, text, toggleable, tooltip, width: entryWidth }, index) => {
|
|
251
270
|
const entryVisible = visibleSeriesIds.has(entry.id);
|
|
252
271
|
return (
|
|
253
272
|
<Box
|
|
@@ -274,7 +293,21 @@ export function CompositeLegend({
|
|
|
274
293
|
data-visible={entryVisible ? "true" : "false"}
|
|
275
294
|
title={isDesktopWeb ? tooltip : undefined}
|
|
276
295
|
>
|
|
277
|
-
{isDesktopWeb ? (
|
|
296
|
+
{isDesktopWeb && cue ? (
|
|
297
|
+
// The line as the plot strokes it: solid, dashed, dotted or heavy.
|
|
298
|
+
<Box
|
|
299
|
+
flexShrink={0}
|
|
300
|
+
style={{
|
|
301
|
+
width: 16,
|
|
302
|
+
height: Math.max(2, Math.round(cue.thickness)),
|
|
303
|
+
marginInlineEnd: 6,
|
|
304
|
+
background: seriesCueCssBackground(cue, entry.color),
|
|
305
|
+
opacity: entryVisible ? 1 : 0.35,
|
|
306
|
+
}}
|
|
307
|
+
data-gloom-role="composite-chart-legend-marker"
|
|
308
|
+
data-gloom-cue={cue.id}
|
|
309
|
+
/>
|
|
310
|
+
) : isDesktopWeb ? (
|
|
278
311
|
<Box
|
|
279
312
|
flexShrink={0}
|
|
280
313
|
style={{
|
|
@@ -288,7 +321,7 @@ export function CompositeLegend({
|
|
|
288
321
|
data-gloom-role="composite-chart-legend-marker"
|
|
289
322
|
/>
|
|
290
323
|
) : (
|
|
291
|
-
<Text fg={entryVisible ? entry.color : themeColors.textMuted}
|
|
324
|
+
<Text fg={entryVisible ? entry.color : themeColors.textMuted}>{marker}</Text>
|
|
292
325
|
)}
|
|
293
326
|
{/* The filled/hollow marker already says whether a series is
|
|
294
327
|
shown; the word only repeated it in every legend slot. */}
|
|
@@ -17,6 +17,7 @@ import { compositeGridRatios } from "./format";
|
|
|
17
17
|
import { projectCompositeValue } from "./scene";
|
|
18
18
|
import { paintVolumeProfile } from "./volume-profile-paint";
|
|
19
19
|
import { paintExtendedHours } from "./session-shading";
|
|
20
|
+
import type { SeriesLineCue } from "./series-cues";
|
|
20
21
|
import type {
|
|
21
22
|
CompositeAxisDomain,
|
|
22
23
|
CompositeChartColors,
|
|
@@ -168,6 +169,55 @@ function fillBand(
|
|
|
168
169
|
}
|
|
169
170
|
}
|
|
170
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Strokes a line's segments with a dash pattern that runs on along the whole
|
|
174
|
+
* line, so a dense series with segments shorter than one dash still shows the
|
|
175
|
+
* pattern rather than restarting it at every point.
|
|
176
|
+
*/
|
|
177
|
+
class DashedStroke {
|
|
178
|
+
private index = 0;
|
|
179
|
+
private remaining: number;
|
|
180
|
+
|
|
181
|
+
constructor(
|
|
182
|
+
private readonly data: Uint8Array,
|
|
183
|
+
private readonly width: number,
|
|
184
|
+
private readonly height: number,
|
|
185
|
+
private readonly color: RgbaColor,
|
|
186
|
+
private readonly cue: SeriesLineCue,
|
|
187
|
+
) {
|
|
188
|
+
this.remaining = cue.dash[0] ?? 0;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
segment(x0: number, y0: number, x1: number, y1: number): void {
|
|
192
|
+
const { dash, thickness } = this.cue;
|
|
193
|
+
const length = Math.hypot(x1 - x0, y1 - y0);
|
|
194
|
+
if (dash.length === 0 || length === 0) {
|
|
195
|
+
drawLine(this.data, this.width, this.height, x0, y0, x1, y1, this.color, thickness);
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
let travelled = 0;
|
|
199
|
+
while (travelled < length) {
|
|
200
|
+
const run = Math.min(this.remaining, length - travelled);
|
|
201
|
+
if (this.index % 2 === 0) {
|
|
202
|
+
const from = travelled / length;
|
|
203
|
+
const to = (travelled + run) / length;
|
|
204
|
+
drawLine(
|
|
205
|
+
this.data, this.width, this.height,
|
|
206
|
+
x0 + (x1 - x0) * from, y0 + (y1 - y0) * from,
|
|
207
|
+
x0 + (x1 - x0) * to, y0 + (y1 - y0) * to,
|
|
208
|
+
this.color, thickness,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
travelled += run;
|
|
212
|
+
this.remaining -= run;
|
|
213
|
+
if (this.remaining <= 0) {
|
|
214
|
+
this.index = (this.index + 1) % dash.length;
|
|
215
|
+
this.remaining = dash[this.index]!;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
171
221
|
function drawConnectedSeries(
|
|
172
222
|
data: Uint8Array,
|
|
173
223
|
width: number,
|
|
@@ -176,6 +226,7 @@ function drawConnectedSeries(
|
|
|
176
226
|
domain: CompositeAxisDomain,
|
|
177
227
|
color: RgbaColor,
|
|
178
228
|
area: boolean,
|
|
229
|
+
cue?: SeriesLineCue,
|
|
179
230
|
): void {
|
|
180
231
|
const points = series.points.map((point) => pixelPoint(point, width, height));
|
|
181
232
|
if (points.length === 0) return;
|
|
@@ -184,11 +235,19 @@ function drawConnectedSeries(
|
|
|
184
235
|
|
|
185
236
|
if (area) fillAreaGradient(data, width, height, series, points, baseline, step, color);
|
|
186
237
|
|
|
238
|
+
const stroke = cue ? new DashedStroke(data, width, height, color, cue) : null;
|
|
187
239
|
for (let index = 1; index < points.length; index += 1) {
|
|
188
240
|
const previous = points[index - 1]!;
|
|
189
241
|
const current = points[index]!;
|
|
190
242
|
if (series.points[index]?.breakBefore) continue;
|
|
191
|
-
if (
|
|
243
|
+
if (stroke) {
|
|
244
|
+
if (step) {
|
|
245
|
+
stroke.segment(previous.x, previous.y, current.x, previous.y);
|
|
246
|
+
stroke.segment(current.x, previous.y, current.x, current.y);
|
|
247
|
+
} else {
|
|
248
|
+
stroke.segment(previous.x, previous.y, current.x, current.y);
|
|
249
|
+
}
|
|
250
|
+
} else if (step) {
|
|
192
251
|
drawLine(data, width, height, previous.x, previous.y, current.x, previous.y, color, 1.4);
|
|
193
252
|
drawLine(data, width, height, current.x, previous.y, current.x, current.y, color, 1.4);
|
|
194
253
|
} else {
|
|
@@ -459,6 +518,7 @@ export function renderCompositePanelBitmap(
|
|
|
459
518
|
if (!domain) continue;
|
|
460
519
|
const color = parseHex(series.source.color);
|
|
461
520
|
const negativeColor = series.source.negativeColor ? parseHex(series.source.negativeColor) : color;
|
|
521
|
+
const cue = panel.lineCues?.get(series.source.id);
|
|
462
522
|
switch (series.source.style) {
|
|
463
523
|
case "columns":
|
|
464
524
|
drawColumns(
|
|
@@ -475,11 +535,11 @@ export function renderCompositePanelBitmap(
|
|
|
475
535
|
);
|
|
476
536
|
break;
|
|
477
537
|
case "area":
|
|
478
|
-
drawConnectedSeries(data, width, height, series, domain, color, true);
|
|
538
|
+
drawConnectedSeries(data, width, height, series, domain, color, true, cue);
|
|
479
539
|
break;
|
|
480
540
|
case "band":
|
|
481
541
|
fillBand(data, width, height, series, domain, color);
|
|
482
|
-
drawConnectedSeries(data, width, height, series, domain, color, false);
|
|
542
|
+
drawConnectedSeries(data, width, height, series, domain, color, false, cue);
|
|
483
543
|
break;
|
|
484
544
|
case "points":
|
|
485
545
|
for (const point of series.points) {
|
|
@@ -494,7 +554,7 @@ export function renderCompositePanelBitmap(
|
|
|
494
554
|
break;
|
|
495
555
|
case "line":
|
|
496
556
|
case "step":
|
|
497
|
-
drawConnectedSeries(data, width, height, series, domain, color, false);
|
|
557
|
+
drawConnectedSeries(data, width, height, series, domain, color, false, cue);
|
|
498
558
|
break;
|
|
499
559
|
}
|
|
500
560
|
}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ResolvedSeries, SeriesStyle } from "../../../time-series/types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* What tells one line from another besides its colour, so a chart still reads
|
|
5
|
+
* in greyscale and for colour-blind eyes. Each renderer draws its own form of
|
|
6
|
+
* the same cue: the bitmap (kitty, desktop, web, `shot`) a dash pattern or a
|
|
7
|
+
* heavier stroke, the cell fallback a different mark along the line, and the
|
|
8
|
+
* legend a sample of whichever the plot shows next to the series name.
|
|
9
|
+
*/
|
|
10
|
+
export interface SeriesLineCue {
|
|
11
|
+
id: "solid" | "dashed" | "dotted" | "dash-dot" | "heavy";
|
|
12
|
+
/** On and off lengths along the line in bitmap pixels; empty is a solid line. */
|
|
13
|
+
dash: readonly number[];
|
|
14
|
+
/** Stroke width in bitmap pixels. */
|
|
15
|
+
thickness: number;
|
|
16
|
+
/** The mark the cell renderer draws the line with. */
|
|
17
|
+
glyph: string;
|
|
18
|
+
/** Two terminal cells of the line, for the legend beside a bitmap plot. */
|
|
19
|
+
sample: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/** The plain line every single-line chart keeps. */
|
|
23
|
+
const SOLID_LINE_THICKNESS = 1.5;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Dash lengths allow for the stroke's round ends, which eat about its width
|
|
27
|
+
* out of every gap: a 6 on, 5 off pattern reads as even dashes.
|
|
28
|
+
*/
|
|
29
|
+
const LINE_CUES: readonly SeriesLineCue[] = [
|
|
30
|
+
{ id: "solid", dash: [], thickness: SOLID_LINE_THICKNESS, glyph: "•", sample: "──" },
|
|
31
|
+
{ id: "dashed", dash: [7, 6], thickness: SOLID_LINE_THICKNESS, glyph: "+", sample: "╌╌" },
|
|
32
|
+
{ id: "dotted", dash: [1, 4.5], thickness: 2, glyph: "×", sample: "┈┈" },
|
|
33
|
+
{ id: "dash-dot", dash: [9, 4.5, 1, 4.5], thickness: SOLID_LINE_THICKNESS, glyph: "o", sample: "─·" },
|
|
34
|
+
{ id: "heavy", dash: [], thickness: 3, glyph: "*", sample: "━━" },
|
|
35
|
+
];
|
|
36
|
+
|
|
37
|
+
const LINE_STYLES = new Set<SeriesStyle>(["line", "step", "area", "band"]);
|
|
38
|
+
|
|
39
|
+
function isLineCueStyle(style: SeriesStyle): boolean {
|
|
40
|
+
return LINE_STYLES.has(style);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* A cue for each line of a chart that draws two or more. Lines are counted in
|
|
45
|
+
* legend order, hidden ones included, so hiding one does not restyle the
|
|
46
|
+
* others; the first keeps the plain solid line. A chart with one line gets
|
|
47
|
+
* none and looks as it always has.
|
|
48
|
+
*/
|
|
49
|
+
export function assignSeriesLineCues(series: readonly ResolvedSeries[]): ReadonlyMap<string, SeriesLineCue> {
|
|
50
|
+
const lines = series.filter((entry) => isLineCueStyle(entry.style) && !entry.profile);
|
|
51
|
+
const cues = new Map<string, SeriesLineCue>();
|
|
52
|
+
if (lines.length < 2) return cues;
|
|
53
|
+
for (const entry of lines) {
|
|
54
|
+
if (!cues.has(entry.id)) cues.set(entry.id, LINE_CUES[cues.size % LINE_CUES.length]!);
|
|
55
|
+
}
|
|
56
|
+
return cues;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/** A repeating CSS gradient that draws the cue's dashes, for the desktop and web legend. */
|
|
60
|
+
export function seriesCueCssBackground(cue: SeriesLineCue, color: string): string {
|
|
61
|
+
if (cue.dash.length === 0) return color;
|
|
62
|
+
// Half the bitmap lengths: the legend sample is drawn in CSS pixels at its own size.
|
|
63
|
+
const stops: string[] = [];
|
|
64
|
+
let at = 0;
|
|
65
|
+
cue.dash.forEach((length, index) => {
|
|
66
|
+
const end = at + Math.max(1, length / 2 + (index % 2 === 0 ? 0.5 : -0.5));
|
|
67
|
+
stops.push(`${index % 2 === 0 ? color : "transparent"} ${at}px ${end}px`);
|
|
68
|
+
at = end;
|
|
69
|
+
});
|
|
70
|
+
return `repeating-linear-gradient(to right, ${stops.join(", ")})`;
|
|
71
|
+
}
|
|
@@ -5,6 +5,7 @@ import { buildCompositeColumnLayout, type CompositeColumnLayout } from "./column
|
|
|
5
5
|
import { projectCompositeValue } from "./scene";
|
|
6
6
|
import { writeVolumeProfileText } from "./volume-profile-paint";
|
|
7
7
|
import { writeSessionBreaksText } from "./session-shading";
|
|
8
|
+
import type { SeriesLineCue } from "./series-cues";
|
|
8
9
|
import type {
|
|
9
10
|
CompositeAxisDomain,
|
|
10
11
|
CompositePanelScene,
|
|
@@ -59,19 +60,23 @@ function renderLineLike(
|
|
|
59
60
|
series: CompositeProjectedSeries,
|
|
60
61
|
width: number,
|
|
61
62
|
height: number,
|
|
63
|
+
cue?: SeriesLineCue,
|
|
62
64
|
): void {
|
|
63
65
|
const points = series.points.map((point) => ({ ...cellPoint(point, width, height), point }));
|
|
64
66
|
if (points.length === 0) return;
|
|
65
67
|
const step = series.source.style === "step" || series.source.interpolation === "step-after";
|
|
68
|
+
// Beside other lines, each line keeps its own mark the whole way, steps
|
|
69
|
+
// included, since cell text has no colour of its own here.
|
|
70
|
+
const mark = cue && cue.id !== "solid" ? cue.glyph : null;
|
|
66
71
|
for (let index = 1; index < points.length; index += 1) {
|
|
67
72
|
const previous = points[index - 1]!;
|
|
68
73
|
const current = points[index]!;
|
|
69
74
|
if (current.point.breakBefore) continue;
|
|
70
75
|
if (step) {
|
|
71
|
-
drawLine(rows, previous.x, previous.y, current.x, previous.y, "─");
|
|
72
|
-
drawLine(rows, current.x, previous.y, current.x, current.y, "│");
|
|
76
|
+
drawLine(rows, previous.x, previous.y, current.x, previous.y, mark ?? "─");
|
|
77
|
+
drawLine(rows, current.x, previous.y, current.x, current.y, mark ?? "│");
|
|
73
78
|
} else {
|
|
74
|
-
drawLine(rows, previous.x, previous.y, current.x, current.y, "•");
|
|
79
|
+
drawLine(rows, previous.x, previous.y, current.x, current.y, mark ?? "•");
|
|
75
80
|
}
|
|
76
81
|
}
|
|
77
82
|
for (let index = 0; index < points.length; index += 1) {
|
|
@@ -102,6 +107,7 @@ function renderArea(
|
|
|
102
107
|
domain: CompositeAxisDomain,
|
|
103
108
|
width: number,
|
|
104
109
|
height: number,
|
|
110
|
+
cue?: SeriesLineCue,
|
|
105
111
|
): void {
|
|
106
112
|
const baseline = valueRow(0, domain, height) ?? height - 1;
|
|
107
113
|
const points = series.points.map((point) => cellPoint(point, width, height));
|
|
@@ -110,7 +116,7 @@ function renderArea(
|
|
|
110
116
|
const bottom = Math.max(point.y, baseline);
|
|
111
117
|
for (let row = top; row <= bottom; row += 1) setCell(rows, point.x, row, "░");
|
|
112
118
|
}
|
|
113
|
-
renderLineLike(rows, series, width, height);
|
|
119
|
+
renderLineLike(rows, series, width, height, cue);
|
|
114
120
|
}
|
|
115
121
|
|
|
116
122
|
/** A band's range as shade between its points' low and high rows, under its line. */
|
|
@@ -120,6 +126,7 @@ function renderBand(
|
|
|
120
126
|
domain: CompositeAxisDomain,
|
|
121
127
|
width: number,
|
|
122
128
|
height: number,
|
|
129
|
+
cue?: SeriesLineCue,
|
|
123
130
|
): void {
|
|
124
131
|
const edges = series.points.map((projected) => {
|
|
125
132
|
const { high, low } = projected.point;
|
|
@@ -150,7 +157,7 @@ function renderBand(
|
|
|
150
157
|
shade(x, previous.top + (current.top - previous.top) * t, previous.bottom + (current.bottom - previous.bottom) * t);
|
|
151
158
|
}
|
|
152
159
|
}
|
|
153
|
-
renderLineLike(rows, series, width, height);
|
|
160
|
+
renderLineLike(rows, series, width, height, cue);
|
|
154
161
|
}
|
|
155
162
|
|
|
156
163
|
function renderColumns(
|
|
@@ -267,15 +274,16 @@ export function renderCompositePanelText(
|
|
|
267
274
|
for (const series of orderedSeries) {
|
|
268
275
|
const domain = panel.axes[series.source.axis];
|
|
269
276
|
if (!domain) continue;
|
|
277
|
+
const cue = panel.lineCues?.get(series.source.id);
|
|
270
278
|
switch (series.source.style) {
|
|
271
279
|
case "columns":
|
|
272
280
|
renderColumns(rows, series, domain, plotWidth, height, columnLayout);
|
|
273
281
|
break;
|
|
274
282
|
case "area":
|
|
275
|
-
renderArea(rows, series, domain, plotWidth, height);
|
|
283
|
+
renderArea(rows, series, domain, plotWidth, height, cue);
|
|
276
284
|
break;
|
|
277
285
|
case "band":
|
|
278
|
-
renderBand(rows, series, domain, plotWidth, height);
|
|
286
|
+
renderBand(rows, series, domain, plotWidth, height, cue);
|
|
279
287
|
break;
|
|
280
288
|
case "candles":
|
|
281
289
|
case "ohlc":
|
|
@@ -284,7 +292,7 @@ export function renderCompositePanelText(
|
|
|
284
292
|
break;
|
|
285
293
|
case "line":
|
|
286
294
|
case "step":
|
|
287
|
-
renderLineLike(rows, series, plotWidth, height);
|
|
295
|
+
renderLineLike(rows, series, plotWidth, height, cue);
|
|
288
296
|
break;
|
|
289
297
|
case "points":
|
|
290
298
|
renderPoints(rows, series, plotWidth, height);
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ReactNode } from "react";
|
|
2
2
|
import type { CompositeChartLevels } from "./levels";
|
|
3
|
+
import type { SeriesLineCue } from "./series-cues";
|
|
3
4
|
import type {
|
|
4
5
|
ChartPanelSpec,
|
|
5
6
|
PanelScale,
|
|
@@ -134,6 +135,8 @@ export interface CompositePanelScene {
|
|
|
134
135
|
extendedHours?: readonly CompositeExtendedHoursSpan[];
|
|
135
136
|
/** The previous session's close on a one-day chart, drawn as a dotted reference line. */
|
|
136
137
|
priorClose?: { axis: CompositeAxisSide; value: number; yRatio: number };
|
|
138
|
+
/** How each line tells itself apart besides colour, by series id; absent on a chart with one line. */
|
|
139
|
+
lineCues?: ReadonlyMap<string, SeriesLineCue>;
|
|
137
140
|
}
|
|
138
141
|
|
|
139
142
|
export interface CompositeCursorValue {
|
|
@@ -17,7 +17,7 @@ import { openBrokerAddFlow } from "../../../../plugins/builtin/broker-manager/ad
|
|
|
17
17
|
import { isManualPortfolio } from "../../../../plugins/builtin/portfolio-list/mutations";
|
|
18
18
|
import { CHART_RENDERER_PREFERENCES } from "../../../chart/core/types";
|
|
19
19
|
import type { Command } from "../registry";
|
|
20
|
-
import type { BrokerInstanceConfig } from "../../../../types/config";
|
|
20
|
+
import type { AppConfig, BrokerInstanceConfig } from "../../../../types/config";
|
|
21
21
|
import { requestFeedbackDialog } from "../../../feedback-dialog";
|
|
22
22
|
import type { OpenInlineConfirm } from "../../routing/confirm";
|
|
23
23
|
import {
|
|
@@ -288,6 +288,14 @@ export function runDirectCommandAction(options: {
|
|
|
288
288
|
closeAll({ revertThemePreview: false });
|
|
289
289
|
return;
|
|
290
290
|
}
|
|
291
|
+
case "toggle-presentation-mode": {
|
|
292
|
+
const { presentationMode, ...rest } = state.config;
|
|
293
|
+
const nextConfig: AppConfig = presentationMode ? rest : { ...rest, presentationMode: true };
|
|
294
|
+
dispatch({ type: "SET_CONFIG", config: nextConfig });
|
|
295
|
+
persistConfig(nextConfig);
|
|
296
|
+
closeAll({ revertThemePreview: false });
|
|
297
|
+
return;
|
|
298
|
+
}
|
|
291
299
|
case "toggle-value-flashing": {
|
|
292
300
|
const nextConfig = {
|
|
293
301
|
...state.config,
|
|
@@ -156,8 +156,10 @@ export function buildPluginCommandResultItem(options: {
|
|
|
156
156
|
detail: options.result.detail ?? options.command.description ?? "",
|
|
157
157
|
category,
|
|
158
158
|
kind: "command",
|
|
159
|
+
badge: options.result.badge,
|
|
160
|
+
name: options.result.name,
|
|
159
161
|
right: (options.result.right ?? options.command.shortcut?.trim()) || undefined,
|
|
160
|
-
searchText: `${options.result.label} ${options.result.detail || ""} ${(options.result.keywords ?? []).join(" ")} ${options.command.label} ${options.command.description || ""} ${(options.command.keywords ?? []).join(" ")}`,
|
|
162
|
+
searchText: `${options.result.label} ${options.result.detail || ""} ${options.result.name || ""} ${(options.result.keywords ?? []).join(" ")} ${options.command.label} ${options.command.description || ""} ${(options.command.keywords ?? []).join(" ")}`,
|
|
161
163
|
disabled: options.result.disabled,
|
|
162
164
|
current: options.result.current,
|
|
163
165
|
action: async () => {
|
|
@@ -207,6 +207,13 @@ export const commands: Command[] = [
|
|
|
207
207
|
dispatch({ type: "TOGGLE_STATUS_BAR" });
|
|
208
208
|
},
|
|
209
209
|
},
|
|
210
|
+
{
|
|
211
|
+
id: "toggle-presentation-mode",
|
|
212
|
+
prefix: "",
|
|
213
|
+
label: "Toggle Presentation Mode",
|
|
214
|
+
description: "Let the panes fill the window, without the header or the status bar",
|
|
215
|
+
category: "Config",
|
|
216
|
+
},
|
|
210
217
|
{
|
|
211
218
|
id: "toggle-value-flashing",
|
|
212
219
|
prefix: "VF",
|
|
@@ -4,7 +4,10 @@ import {
|
|
|
4
4
|
getDockedPaneIds,
|
|
5
5
|
removePane,
|
|
6
6
|
} from "../../../layout/pane-manager";
|
|
7
|
+
import { paneMoveBackTarget, paneNewLayoutBlock } from "../../../layout/pane-layout-move";
|
|
8
|
+
import { resolveTickerForPane } from "../../../state/app/context";
|
|
7
9
|
import { findPaneInstance } from "../../../types/config";
|
|
10
|
+
import { tf } from "../../../i18n";
|
|
8
11
|
import type { ResultItem } from "../list/model";
|
|
9
12
|
import type { LayoutItemsContext } from "./types";
|
|
10
13
|
import { WINDOW_MODE_COMMAND_OPTIONS } from "./window-mode";
|
|
@@ -20,6 +23,7 @@ export function buildFocusedPaneLayoutItems({
|
|
|
20
23
|
persistLayoutChange,
|
|
21
24
|
pluginRegistry,
|
|
22
25
|
pushRoute,
|
|
26
|
+
state,
|
|
23
27
|
}: LayoutItemsContext): ResultItem[] {
|
|
24
28
|
const focusedPane = focusedPaneId ? findPaneInstance(currentLayout, focusedPaneId) : null;
|
|
25
29
|
const focusedPaneDef = focusedPane ? pluginRegistry.panes.get(focusedPane.paneId) : null;
|
|
@@ -37,6 +41,12 @@ export function buildFocusedPaneLayoutItems({
|
|
|
37
41
|
|
|
38
42
|
const dockedPaneIds = getDockedPaneIds(currentLayout);
|
|
39
43
|
const focusedFloating = currentLayout.floating.find((entry) => entry.instanceId === focusedPane.instanceId);
|
|
44
|
+
const newLayoutBlock = paneNewLayoutBlock(
|
|
45
|
+
currentLayout,
|
|
46
|
+
focusedPane.instanceId,
|
|
47
|
+
(instanceId) => resolveTickerForPane(state, instanceId),
|
|
48
|
+
);
|
|
49
|
+
const moveBack = paneMoveBackTarget(state.config, focusedPane.instanceId);
|
|
40
50
|
|
|
41
51
|
return [
|
|
42
52
|
{
|
|
@@ -65,6 +75,34 @@ export function buildFocusedPaneLayoutItems({
|
|
|
65
75
|
pluginRegistry.togglePaneFullscreen(focusedPane.instanceId);
|
|
66
76
|
},
|
|
67
77
|
},
|
|
78
|
+
{
|
|
79
|
+
id: "layout-move-to-new-layout",
|
|
80
|
+
label: "Move to New Layout",
|
|
81
|
+
detail: newLayoutBlock === "only-pane"
|
|
82
|
+
? "Already the only pane in this layout"
|
|
83
|
+
: "Give the focused pane a layout of its own that it fills",
|
|
84
|
+
searchText: "own layout tab fullscreen",
|
|
85
|
+
category: "Focused Pane",
|
|
86
|
+
kind: "action",
|
|
87
|
+
disabled: newLayoutBlock === "only-pane" || newLayoutBlock === "detached",
|
|
88
|
+
action: () => {
|
|
89
|
+
closeAll({ revertThemePreview: false });
|
|
90
|
+
pluginRegistry.movePaneToNewLayout(focusedPane.instanceId);
|
|
91
|
+
},
|
|
92
|
+
},
|
|
93
|
+
...(moveBack ? [{
|
|
94
|
+
id: "layout-move-back",
|
|
95
|
+
label: tf("Move Back to {layout}", { layout: moveBack.name }),
|
|
96
|
+
detail: moveBack.original
|
|
97
|
+
? "Return the focused pane to the layout it came from"
|
|
98
|
+
: "The layout it came from is gone, so it docks in this one instead",
|
|
99
|
+
category: "Focused Pane",
|
|
100
|
+
kind: "action" as const,
|
|
101
|
+
action: () => {
|
|
102
|
+
closeAll({ revertThemePreview: false });
|
|
103
|
+
pluginRegistry.movePaneBack(focusedPane.instanceId);
|
|
104
|
+
},
|
|
105
|
+
}] : []),
|
|
68
106
|
...WINDOW_MODE_COMMAND_OPTIONS.map((option) => ({
|
|
69
107
|
id: `layout-window-mode:${option.mode}`,
|
|
70
108
|
label: option.label,
|
|
@@ -29,6 +29,7 @@ import {
|
|
|
29
29
|
type SharedWorkflowDeps,
|
|
30
30
|
} from "../workflow/ops";
|
|
31
31
|
import type { PluginRegistry } from "../../../plugins/registry";
|
|
32
|
+
import { PluginOffError, notifyPluginOff } from "../../../plugins/plugin-off";
|
|
32
33
|
import { shouldOpenPaneTemplateConfig } from "./workflow-route";
|
|
33
34
|
|
|
34
35
|
type CloseAllFn = (options?: { revertThemePreview?: boolean }) => void;
|
|
@@ -107,6 +108,10 @@ export function useCommandBarPaneTemplateActions({
|
|
|
107
108
|
await pluginRegistry.createPaneFromTemplateAsync(template.id, createOptions);
|
|
108
109
|
closeAll({ revertThemePreview: false });
|
|
109
110
|
} catch (error) {
|
|
111
|
+
if (error instanceof PluginOffError) {
|
|
112
|
+
notifyPluginOff(pluginRegistry, error, () => { void openPaneTemplateDirect(template, createOptions); });
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
110
115
|
const displayLabel = getPaneTemplateDisplayLabel(template);
|
|
111
116
|
notify(
|
|
112
117
|
error instanceof Error ? error.message : `Could not create ${displayLabel.toLowerCase()}.`,
|
|
@@ -147,6 +147,8 @@ export function createRootCommandItemBuilder({
|
|
|
147
147
|
return command.description;
|
|
148
148
|
case "toggle-value-flashing":
|
|
149
149
|
return state.config.valueFlashingEnabled ? "Currently on" : "Currently off";
|
|
150
|
+
case "toggle-presentation-mode":
|
|
151
|
+
return state.config.presentationMode ? "Currently on" : "Currently off";
|
|
150
152
|
case "toggle-crash-reports":
|
|
151
153
|
return state.config.telemetry?.crashReports === false ? "Currently off" : "Currently on";
|
|
152
154
|
case "toggle-usage-counts":
|