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
|
@@ -4,7 +4,7 @@ import type { MarketDataRequestContext } from "../../types/data-provider";
|
|
|
4
4
|
import type { Quote, TickerFinancials } from "../../types/financials";
|
|
5
5
|
import { normalizeTickerFinancialsPriceHistory } from "../../utils/price-history";
|
|
6
6
|
import { resolveTickerFinancialsQuoteState } from "../../market-data/quotes/resolution";
|
|
7
|
-
import { shouldLogProviderError } from "../provider-errors";
|
|
7
|
+
import { noteProviderAnswer, noteProviderMiss, shouldLogProviderError, type ProviderMissNote } from "../provider-errors";
|
|
8
8
|
import { quoteMetadataFromQuote } from "../../market-data/quotes/metadata";
|
|
9
9
|
import {
|
|
10
10
|
dropUnusableProviderQuote,
|
|
@@ -14,12 +14,19 @@ import {
|
|
|
14
14
|
needsFinancialProfile,
|
|
15
15
|
profileForSameListing,
|
|
16
16
|
isProviderQuoteUsableForCurrentSession,
|
|
17
|
+
lastKnownProviderQuote,
|
|
17
18
|
providerFinancialsMatchTarget,
|
|
18
19
|
mergeMissingStatementArrays,
|
|
19
20
|
mergeFinancials,
|
|
20
21
|
} from "./financials";
|
|
21
22
|
import type { ProviderRouterCoreDeps, SourceResult } from "./route-types";
|
|
22
23
|
|
|
24
|
+
/** What the providers of one quote request said when none had a current quote. */
|
|
25
|
+
export interface ProviderQuoteMissNote extends ProviderMissNote {
|
|
26
|
+
/** The newest priced answer for the target that was not current for its session, flagged stale. */
|
|
27
|
+
lastKnownQuote?: Quote;
|
|
28
|
+
}
|
|
29
|
+
|
|
23
30
|
export class ProviderRouterPrimaryRoutes {
|
|
24
31
|
constructor(private readonly options: ProviderRouterCoreDeps) {}
|
|
25
32
|
|
|
@@ -61,6 +68,7 @@ export class ProviderRouterPrimaryRoutes {
|
|
|
61
68
|
ticker: string,
|
|
62
69
|
exchange?: string,
|
|
63
70
|
context?: MarketDataRequestContext,
|
|
71
|
+
misses?: ProviderMissNote,
|
|
64
72
|
): Promise<SourceResult<TickerFinancials> | null> {
|
|
65
73
|
const entityKey = this.options.getEntityKey(ticker, context?.instrument);
|
|
66
74
|
const variantKey = financialHistoryVariants(this.options.getTickerVariantCandidates(exchange), context)[0] ?? "";
|
|
@@ -70,11 +78,11 @@ export class ProviderRouterPrimaryRoutes {
|
|
|
70
78
|
for (const provider of this.options.providersInPriorityOrder()) {
|
|
71
79
|
try {
|
|
72
80
|
const rawValue = await provider.getTickerFinancials(ticker, exchange, context);
|
|
73
|
-
if (rawValue && !context?.instrument && !providerFinancialsMatchTarget(rawValue, ticker, exchange)) continue;
|
|
81
|
+
if (rawValue && !context?.instrument && !providerFinancialsMatchTarget(rawValue, ticker, exchange)) { noteProviderAnswer(misses); continue; }
|
|
74
82
|
const resolvedValue = resolveTickerFinancialsQuoteState(normalizeTickerFinancialsPriceHistory(rawValue));
|
|
75
|
-
if (resolvedValue && !context?.instrument && !providerFinancialsMatchTarget(resolvedValue, ticker, exchange)) continue;
|
|
83
|
+
if (resolvedValue && !context?.instrument && !providerFinancialsMatchTarget(resolvedValue, ticker, exchange)) { noteProviderAnswer(misses); continue; }
|
|
76
84
|
let value = resolvedValue ? dropUnusableProviderQuote(resolvedValue, exchange, { recentAnswer: true }) : null;
|
|
77
|
-
if (!value) continue;
|
|
85
|
+
if (!value) { noteProviderAnswer(misses); continue; }
|
|
78
86
|
const sourceKey = this.options.providerSourceKey(provider);
|
|
79
87
|
if (context?.statementHistory === "extended" && !hasReusableExtendedHistory(value)) {
|
|
80
88
|
const previous = selectCachedResource<TickerFinancials>(this.options.resources, "financials", entityKey, [variantKey], [sourceKey], true);
|
|
@@ -131,6 +139,7 @@ export class ProviderRouterPrimaryRoutes {
|
|
|
131
139
|
if (!needsFinancialProfile(primaryResult.value) && (context?.statementHistory === "extended" ? primaryResult.value.statementHistory?.status === "available" : hasDetailedStatementRows(primaryResult.value) && hasDeepStatementHistory(primaryResult.value))) return primaryResult;
|
|
132
140
|
}
|
|
133
141
|
} catch (error) {
|
|
142
|
+
noteProviderMiss(misses, error);
|
|
134
143
|
if (shouldLogProviderError(error)) {
|
|
135
144
|
this.options.logProviderError(`${provider.id} failed: ${error}`);
|
|
136
145
|
}
|
|
@@ -181,13 +190,21 @@ export class ProviderRouterPrimaryRoutes {
|
|
|
181
190
|
ticker: string,
|
|
182
191
|
exchange?: string,
|
|
183
192
|
context?: MarketDataRequestContext,
|
|
193
|
+
misses?: ProviderQuoteMissNote,
|
|
184
194
|
): Promise<SourceResult<Quote> | null> {
|
|
185
195
|
const entityKey = this.options.getEntityKey(ticker, context?.instrument);
|
|
186
196
|
const variantKey = this.options.getTickerVariantCandidates(exchange)[0] ?? "";
|
|
187
197
|
for (const provider of this.options.providersInPriorityOrder()) {
|
|
188
198
|
try {
|
|
189
199
|
const quote = await provider.getQuote(ticker, exchange, context);
|
|
190
|
-
if (!isProviderQuoteUsableForCurrentSession(quote, exchange, ticker, { recentAnswer: true }))
|
|
200
|
+
if (!isProviderQuoteUsableForCurrentSession(quote, exchange, ticker, { recentAnswer: true })) {
|
|
201
|
+
noteProviderAnswer(misses);
|
|
202
|
+
const lastKnown = misses && lastKnownProviderQuote(quote, exchange, ticker);
|
|
203
|
+
if (lastKnown && (misses.lastKnownQuote?.lastUpdated ?? -Infinity) < lastKnown.lastUpdated) {
|
|
204
|
+
misses.lastKnownQuote = lastKnown;
|
|
205
|
+
}
|
|
206
|
+
continue;
|
|
207
|
+
}
|
|
191
208
|
const sourceKey = this.options.providerSourceKey(provider);
|
|
192
209
|
this.options.cacheResource(
|
|
193
210
|
"quote",
|
|
@@ -199,6 +216,7 @@ export class ProviderRouterPrimaryRoutes {
|
|
|
199
216
|
);
|
|
200
217
|
return { sourceKey, value: quote };
|
|
201
218
|
} catch (error) {
|
|
219
|
+
noteProviderMiss(misses, error);
|
|
202
220
|
if (shouldLogProviderError(error)) {
|
|
203
221
|
this.options.logProviderError(`${provider.id} failed: ${error}`);
|
|
204
222
|
}
|
package/src/sources/sec-edgar.ts
CHANGED
|
@@ -39,6 +39,8 @@ type CompanyFactsEntry = {
|
|
|
39
39
|
form?: string;
|
|
40
40
|
filed?: string;
|
|
41
41
|
frame?: string;
|
|
42
|
+
/** A fact reporting the same figure for this span carries SEC's calendar-quarter frame. */
|
|
43
|
+
framedQuarter?: boolean;
|
|
42
44
|
};
|
|
43
45
|
|
|
44
46
|
type CompanyFactsStatementField = {
|
|
@@ -398,6 +400,23 @@ function companyFactsEntries(payload: unknown, tag: string, units: string[]): Co
|
|
|
398
400
|
return [];
|
|
399
401
|
}
|
|
400
402
|
|
|
403
|
+
/**
|
|
404
|
+
* SEC frames one fact per span, the latest filed: a 10-K's own fourth quarter
|
|
405
|
+
* is framed only on the next 10-K that repeats it. An earlier fact reporting
|
|
406
|
+
* the framed figure for that span is that quarter too, so it keeps its date.
|
|
407
|
+
*/
|
|
408
|
+
function withFramedQuarters(entries: CompanyFactsEntry[], sameFigure?: SameFigure): CompanyFactsEntry[] {
|
|
409
|
+
const span = (entry: CompanyFactsEntry) => `${entry.start}:${entry.end}`;
|
|
410
|
+
const framed = new Map<string, CompanyFactsEntry[]>();
|
|
411
|
+
for (const entry of entries) {
|
|
412
|
+
if (!entry.start || !/^CY\d{4}Q[1-4]$/.test(entry.frame ?? "")) continue;
|
|
413
|
+
framed.set(span(entry), [...framed.get(span(entry)) ?? [], entry]);
|
|
414
|
+
}
|
|
415
|
+
if (!framed.size) return entries;
|
|
416
|
+
return entries.map((entry) => entry.start && framed.get(span(entry))?.some((quarter) =>
|
|
417
|
+
Object.is(quarter.val, entry.val) || sameFigure?.(entry, quarter)) ? { ...entry, framedQuarter: true } : entry);
|
|
418
|
+
}
|
|
419
|
+
|
|
401
420
|
function secFormRank(form: string | undefined): number {
|
|
402
421
|
const normalized = normalize(form);
|
|
403
422
|
if (normalized === "10-K/A") return 4;
|
|
@@ -415,6 +434,7 @@ function companyFactsFiledRank(value: string | undefined): number {
|
|
|
415
434
|
function shouldReplaceCompanyFact(
|
|
416
435
|
candidate: CompanyFactsEntry,
|
|
417
436
|
selected: CompanyFactsEntry | undefined,
|
|
437
|
+
repeats?: SameFigure,
|
|
418
438
|
): boolean {
|
|
419
439
|
if (!selected) return true;
|
|
420
440
|
const left = candidate;
|
|
@@ -433,13 +453,45 @@ function shouldReplaceCompanyFact(
|
|
|
433
453
|
if (filedDiff < 0) return false;
|
|
434
454
|
// Later comparative repeats do not move the original disclosure date.
|
|
435
455
|
// A later value change is a restatement and replaces the selected fact.
|
|
436
|
-
return !Object.is(left.val, right.val);
|
|
456
|
+
return !Object.is(left.val, right.val) && !repeats?.(left, right);
|
|
437
457
|
}
|
|
438
458
|
const formDiff = secFormRank(left.form) - secFormRank(right.form);
|
|
439
459
|
if (formDiff !== 0) return formDiff > 0;
|
|
440
460
|
return String(left.frame ?? "").localeCompare(String(right.frame ?? "")) > 0;
|
|
441
461
|
}
|
|
442
462
|
|
|
463
|
+
/** Whether two facts for one span report the same figure under different values. */
|
|
464
|
+
type SameFigure = (left: CompanyFactsEntry, right: CompanyFactsEntry) => boolean;
|
|
465
|
+
|
|
466
|
+
/**
|
|
467
|
+
* A split re-expresses EPS on the new share count: two values are one figure
|
|
468
|
+
* when the share-basis evidence puts them on different bases and they agree
|
|
469
|
+
* on the current one within each filing's cent rounding.
|
|
470
|
+
*/
|
|
471
|
+
function splitEquivalent(resolveEps: ReturnType<typeof createSecEpsBasisResolver>): SameFigure {
|
|
472
|
+
return (left, right) => {
|
|
473
|
+
const [a, b] = [resolveEps(left), resolveEps(right)];
|
|
474
|
+
return a.basis?.status === "split-adjusted" && b.basis?.status === "split-adjusted" && a.basis.factor !== b.basis.factor
|
|
475
|
+
&& a.value !== undefined && b.value !== undefined
|
|
476
|
+
&& Math.abs(a.value - b.value) <= .005 / a.basis.factor! + .005 / b.basis.factor! + 1e-12;
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* A later split re-expression of the selected EPS, or a later repeat of one,
|
|
482
|
+
* is a comparative repeat and not a restatement. The original disclosure
|
|
483
|
+
* stays selected and dated; the resolver carries it to the current basis.
|
|
484
|
+
*/
|
|
485
|
+
function splitRepeats(sameFigure: SameFigure): SameFigure {
|
|
486
|
+
const repeated = new WeakMap<CompanyFactsEntry, Set<number>>();
|
|
487
|
+
return (candidate, selected) => {
|
|
488
|
+
if (repeated.get(selected)?.has(candidate.val!)) return true;
|
|
489
|
+
if (!sameFigure(candidate, selected)) return false;
|
|
490
|
+
repeated.set(selected, (repeated.get(selected) ?? new Set()).add(candidate.val!));
|
|
491
|
+
return true;
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
|
|
443
495
|
function compareCompanyFactsChronologically(
|
|
444
496
|
left: CompanyFactsEntry,
|
|
445
497
|
right: CompanyFactsEntry,
|
|
@@ -484,7 +536,7 @@ function isQuarterlyCompanyFact(entry: CompanyFactsEntry, periodType: "duration"
|
|
|
484
536
|
|| new Date(start).toISOString().slice(0, 10) !== entry.start
|
|
485
537
|
|| new Date(end).toISOString().slice(0, 10) !== entry.end) return false;
|
|
486
538
|
}
|
|
487
|
-
if (/^CY\d{4}Q[1-4]$/.test(entry.frame ?? "")) return true;
|
|
539
|
+
if (/^CY\d{4}Q[1-4]$/.test(entry.frame ?? "") || entry.framedQuarter) return true;
|
|
488
540
|
const form = normalize(entry.form);
|
|
489
541
|
const fiscalPeriod = normalize(entry.fp ?? undefined);
|
|
490
542
|
if ((form !== "10-Q" && form !== "10-Q/A") || !/^Q[1-3]$/.test(fiscalPeriod)) return false;
|
|
@@ -505,9 +557,10 @@ function setCompanyFactValue(
|
|
|
505
557
|
field: keyof FinancialStatement,
|
|
506
558
|
value: number,
|
|
507
559
|
entry: CompanyFactsEntry,
|
|
560
|
+
repeats?: SameFigure,
|
|
508
561
|
): void {
|
|
509
562
|
const selectionKey = `${date}:${String(field)}`;
|
|
510
|
-
if (!shouldReplaceCompanyFact(entry, selectedFacts.get(selectionKey))) return;
|
|
563
|
+
if (!shouldReplaceCompanyFact(entry, selectedFacts.get(selectionKey), repeats)) return;
|
|
511
564
|
const row = rows.get(date) ?? { date, dateSource: "sec", currency: "USD" };
|
|
512
565
|
(row as unknown as Record<string, unknown>)[field] = value;
|
|
513
566
|
if (entry.filed) {
|
|
@@ -532,6 +585,7 @@ function fillCompanyFactsStatementRows(
|
|
|
532
585
|
field: CompanyFactsStatementField,
|
|
533
586
|
period: "annual" | "quarterly",
|
|
534
587
|
annualPeriodEnds: ReadonlySet<string>,
|
|
588
|
+
repeats?: SameFigure,
|
|
535
589
|
): void {
|
|
536
590
|
for (const entry of [...entries].sort(compareCompanyFactsChronologically)) {
|
|
537
591
|
if (!entry.end || typeof entry.val !== "number") continue;
|
|
@@ -548,6 +602,7 @@ function fillCompanyFactsStatementRows(
|
|
|
548
602
|
field.field,
|
|
549
603
|
field.transform ? field.transform(entry.val) : entry.val,
|
|
550
604
|
entry,
|
|
605
|
+
repeats,
|
|
551
606
|
);
|
|
552
607
|
}
|
|
553
608
|
}
|
|
@@ -648,10 +703,14 @@ export function parseCompanyFactsFinancialStatements(payload: unknown): SecCompa
|
|
|
648
703
|
const quarterlyRows = new Map<string, FinancialStatement>();
|
|
649
704
|
const annualSelectedFacts = new Map<string, CompanyFactsEntry>();
|
|
650
705
|
const quarterlySelectedFacts = new Map<string, CompanyFactsEntry>();
|
|
706
|
+
const resolveEps = createSecEpsBasisResolver(payload);
|
|
707
|
+
// Diluted EPS only: share counts are kept only on the current basis.
|
|
708
|
+
const sameEps = splitEquivalent(resolveEps);
|
|
651
709
|
const fieldEntries = COMPANY_FACTS_STATEMENT_FIELDS.map((field) => ({
|
|
652
710
|
field,
|
|
653
|
-
entries: withoutDifferentMeasureFallback(field, field.tags.flatMap((tag, tagPriority) =>
|
|
654
|
-
.
|
|
711
|
+
entries: withoutDifferentMeasureFallback(field, field.tags.flatMap((tag, tagPriority) => withFramedQuarters(
|
|
712
|
+
companyFactsEntries(payload, tag, field.units), field.field === "eps" ? sameEps : undefined,
|
|
713
|
+
).map((entry) => ({ ...entry, tagPriority, concept: tag })))),
|
|
655
714
|
}));
|
|
656
715
|
// Balance-sheet facts have no duration. Anchor their dates to actual annual
|
|
657
716
|
// periods, so quarterly comparative snapshots in a 10-K stay quarterly.
|
|
@@ -659,13 +718,14 @@ export function parseCompanyFactsFinancialStatements(payload: unknown): SecCompa
|
|
|
659
718
|
? entries.filter(isAnnualDurationFact).map((entry) => entry.end!)
|
|
660
719
|
: []));
|
|
661
720
|
|
|
721
|
+
const epsRepeats = splitRepeats(sameEps);
|
|
662
722
|
for (const { field, entries } of fieldEntries) {
|
|
663
723
|
if (entries.length === 0) continue;
|
|
664
|
-
|
|
665
|
-
fillCompanyFactsStatementRows(
|
|
724
|
+
const repeats = field.field === "eps" ? epsRepeats : undefined;
|
|
725
|
+
fillCompanyFactsStatementRows(annualRows, annualSelectedFacts, entries, field, "annual", annualPeriodEnds, repeats);
|
|
726
|
+
fillCompanyFactsStatementRows(quarterlyRows, quarterlySelectedFacts, entries, field, "quarterly", annualPeriodEnds, repeats);
|
|
666
727
|
}
|
|
667
728
|
|
|
668
|
-
const resolveEps = createSecEpsBasisResolver(payload);
|
|
669
729
|
const fourthQuarters = FOURTH_QUARTER_LINES.flatMap(({ fields, source }) => secFourthQuarters(fields, typeof source === "string"
|
|
670
730
|
? fieldEntries.find(({ field }) => field.field === source)?.entries ?? []
|
|
671
731
|
: source.flatMap((tag, tagPriority) => companyFactsEntries(payload, tag, ["USD"]).map((entry) => ({ ...entry, tagPriority, concept: tag })))));
|
|
@@ -24,7 +24,8 @@ import {
|
|
|
24
24
|
import {
|
|
25
25
|
addLegacyBuiltinDisabledPluginAliases,
|
|
26
26
|
addLegacyBuiltinPluginOwnerAliases,
|
|
27
|
-
|
|
27
|
+
decodeBuiltinDisabledPluginIds,
|
|
28
|
+
encodeBuiltinDisabledPluginIds,
|
|
28
29
|
normalizeBuiltinPluginStateMap,
|
|
29
30
|
} from "../plugins/ownership";
|
|
30
31
|
import { isRecord } from "../utils/guards";
|
|
@@ -363,7 +364,7 @@ function collectCoreConfigPayload(config: AppConfig) {
|
|
|
363
364
|
// This build merges profiles one by one before it pushes, so a profile
|
|
364
365
|
// missing from its list was removed, not one it never pulled.
|
|
365
366
|
brokerInstancesMergedById: true,
|
|
366
|
-
disabledPlugins: addLegacyBuiltinDisabledPluginAliases(config.disabledPlugins),
|
|
367
|
+
disabledPlugins: addLegacyBuiltinDisabledPluginAliases(encodeBuiltinDisabledPluginIds(config.disabledPlugins)),
|
|
367
368
|
disabledSources: config.disabledSources,
|
|
368
369
|
pluginConfig: addLegacyBuiltinPluginOwnerAliases(config.pluginConfig),
|
|
369
370
|
theme: config.theme,
|
|
@@ -678,7 +679,7 @@ function mergeConfigPayload(
|
|
|
678
679
|
&& Array.isArray(payload.disabledPlugins)
|
|
679
680
|
&& payload.disabledPlugins.every((pluginId) => typeof pluginId === "string")
|
|
680
681
|
) {
|
|
681
|
-
next.disabledPlugins =
|
|
682
|
+
next.disabledPlugins = decodeBuiltinDisabledPluginIds(payload.disabledPlugins);
|
|
682
683
|
}
|
|
683
684
|
if (canApply("pluginConfig") && isPluginStateMap(payload.pluginConfig)) {
|
|
684
685
|
next.pluginConfig = withLocalPluginSecrets(
|
|
@@ -8,7 +8,7 @@ export interface PrintedCliResult {
|
|
|
8
8
|
}
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* A command context whose `initMarketData` and `
|
|
11
|
+
* A command context whose `initMarketData`, `initConfigData` and `initServices` resolve to `market` (config, store, dataProvider, ...),
|
|
12
12
|
* with an empty ticker store unless `market` brings one.
|
|
13
13
|
* `fail` throws, every printResult call is kept in `printed`, and `closeCount` reports how many
|
|
14
14
|
* times the command closed its persistence.
|
|
@@ -29,6 +29,7 @@ export function createTestCliContext(
|
|
|
29
29
|
cliOptions: { ...DEFAULT_CLI_OPTIONS, ...cliOptions },
|
|
30
30
|
initMarketData: init,
|
|
31
31
|
initConfigData: init,
|
|
32
|
+
initServices: async () => ({ ...await init(), destroy: () => { closed += 1; } }),
|
|
32
33
|
printResult: (result: CliResult<any>, options?: CliResultRenderOptions<any, any>) => { printed.push({ result, options }); },
|
|
33
34
|
fail: (message: string): never => { throw new Error(message); },
|
|
34
35
|
} as unknown as CliCommandContext;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { createProviderMiss } from "../sources/provider-errors";
|
|
1
2
|
import type { DataProvider } from "../types/data-provider";
|
|
2
3
|
import type { InstrumentSearchResult } from "../types/instrument";
|
|
3
4
|
import type { Quote, TickerFinancials } from "../types/financials";
|
|
@@ -20,6 +21,22 @@ export function createTestDataProvider(overrides: Partial<DataProvider> = {}): D
|
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
23
|
|
|
24
|
+
/**
|
|
25
|
+
* A source that answers every request the way Gloom Cloud answers a symbol no listing carries:
|
|
26
|
+
* an empty status with the reason code NOT_FOUND.
|
|
27
|
+
*/
|
|
28
|
+
export function createNotFoundProvider(overrides: Partial<DataProvider> = {}): DataProvider {
|
|
29
|
+
const miss = () => Promise.reject(createProviderMiss("NOT_FOUND", undefined, { notFound: true }));
|
|
30
|
+
return createTestDataProvider({
|
|
31
|
+
id: "not-found",
|
|
32
|
+
priority: 100,
|
|
33
|
+
getQuote: miss,
|
|
34
|
+
getTickerFinancials: miss,
|
|
35
|
+
getPriceHistory: miss,
|
|
36
|
+
...overrides,
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
|
|
23
40
|
export function createTestQuote(overrides: Partial<Quote> = {}): Quote {
|
|
24
41
|
return {
|
|
25
42
|
symbol: "AAPL",
|
|
@@ -49,3 +66,20 @@ export const fallbackProvider: DataProvider = createTestDataProvider({
|
|
|
49
66
|
getTickerFinancials: async () => createTestFinancials(),
|
|
50
67
|
getQuote: async () => createTestQuote(),
|
|
51
68
|
});
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* A source that has nothing for any request, the way Gloom Cloud answers an empty status.
|
|
72
|
+
* `reason` is the sentence the service gave for it; without one the miss is bare.
|
|
73
|
+
*/
|
|
74
|
+
export function createEmptyAnswerProvider(reason?: string, overrides: Partial<DataProvider> = {}): DataProvider {
|
|
75
|
+
const miss = () => Promise.reject(createProviderMiss("NOT_FOUND", reason));
|
|
76
|
+
return createTestDataProvider({
|
|
77
|
+
id: "empty-answer",
|
|
78
|
+
priority: 100,
|
|
79
|
+
getQuote: miss,
|
|
80
|
+
getTickerFinancials: miss,
|
|
81
|
+
getExchangeRate: miss,
|
|
82
|
+
getExchangeRateSnapshot: miss,
|
|
83
|
+
...overrides,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
/** Colours for series told apart by name (compared tickers, markets). */
|
|
2
|
+
export const SERIES_COLORS = [
|
|
3
|
+
"#4dabf7",
|
|
4
|
+
"#63e6be",
|
|
5
|
+
"#f6c85f",
|
|
6
|
+
"#b197fc",
|
|
7
|
+
"#ff8787",
|
|
8
|
+
"#ffa94d",
|
|
9
|
+
"#74c0fc",
|
|
10
|
+
"#e599f7",
|
|
11
|
+
"#8ce99a",
|
|
12
|
+
"#ffd43b",
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Okabe-Ito on a dark page, ordered so the first slots, the ones most charts
|
|
17
|
+
* use, sit furthest apart under deuteranopia, protanopia and tritanopia.
|
|
18
|
+
*/
|
|
19
|
+
const COLORBLIND_DARK_SERIES = ["#56b4e9", "#e69f00", "#f0e442", "#d55e00", "#009e73", "#cc79a7", "#0072b2"] as const;
|
|
20
|
+
|
|
21
|
+
/** The same family on a light page: yellow and sky blue fade into white, so ink and a dark ochre stand in. */
|
|
22
|
+
const COLORBLIND_LIGHT_SERIES = ["#0072b2", "#d55e00", "#009e73", "#1f2328", "#7a5c00", "#cc79a7"] as const;
|
|
23
|
+
|
|
24
|
+
const THEME_SERIES_COLORS: Readonly<Record<string, readonly string[]>> = {
|
|
25
|
+
colorblind: COLORBLIND_DARK_SERIES,
|
|
26
|
+
"colorblind-light": COLORBLIND_LIGHT_SERIES,
|
|
27
|
+
"high-contrast": COLORBLIND_DARK_SERIES,
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** The theme's own palette for named series, or null where it draws them in `SERIES_COLORS`. */
|
|
31
|
+
export function themeSeriesColors(themeId: string): readonly string[] | null {
|
|
32
|
+
return THEME_SERIES_COLORS[themeId] ?? null;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const DEFAULT_SLOT = new Map<string, number>(SERIES_COLORS.map((color, index) => [color, index]));
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* A series colour as the theme draws it. A slot of the default palette moves
|
|
39
|
+
* to the same slot of the theme's palette; any other colour, chosen on purpose
|
|
40
|
+
* by a pane or a person, stays as it is.
|
|
41
|
+
*/
|
|
42
|
+
export function themedSeriesColor(color: string, themeId: string): string {
|
|
43
|
+
const palette = THEME_SERIES_COLORS[themeId];
|
|
44
|
+
const slot = palette ? DEFAULT_SLOT.get(color.toLowerCase()) : undefined;
|
|
45
|
+
return slot === undefined ? color : palette![slot % palette!.length]!;
|
|
46
|
+
}
|
package/src/theme/themes.ts
CHANGED
|
@@ -633,6 +633,81 @@ const rawThemes: Record<string, Theme> = {
|
|
|
633
633
|
commandBg: "#000033",
|
|
634
634
|
commandBorder: "#ff6600",
|
|
635
635
|
},
|
|
636
|
+
|
|
637
|
+
// Gains, losses, warnings and chart series stay apart for deuteranopia,
|
|
638
|
+
// protanopia and tritanopia: up is blue, down is vermillion (the Okabe-Ito
|
|
639
|
+
// set), never red against green. The chrome is grey so hue only ever means
|
|
640
|
+
// data. themes.test.ts simulates the three deficiencies on these palettes.
|
|
641
|
+
colorblind: {
|
|
642
|
+
name: "Colorblind",
|
|
643
|
+
description: "Blue gains and vermillion losses, safe for color-blind eyes",
|
|
644
|
+
bg: "#0e1013",
|
|
645
|
+
panel: "#15181c",
|
|
646
|
+
border: "#3b4149",
|
|
647
|
+
borderFocused: "#d7dade",
|
|
648
|
+
text: "#e4e6e9",
|
|
649
|
+
textDim: "#a3a9b2",
|
|
650
|
+
textBright: "#ffffff",
|
|
651
|
+
textMuted: "#858c96",
|
|
652
|
+
positive: "#56b4e9",
|
|
653
|
+
negative: "#e66a1f",
|
|
654
|
+
neutral: "#9aa0a8",
|
|
655
|
+
warning: "#f0e442",
|
|
656
|
+
header: "#1c2026",
|
|
657
|
+
headerText: "#ffffff",
|
|
658
|
+
selected: "#2b3540",
|
|
659
|
+
selectedText: "#ffffff",
|
|
660
|
+
commandBg: "#15181c",
|
|
661
|
+
commandBorder: "#a3a9b2",
|
|
662
|
+
},
|
|
663
|
+
|
|
664
|
+
"colorblind-light": {
|
|
665
|
+
name: "Colorblind Light",
|
|
666
|
+
description: "Light, blue gains and vermillion losses, safe for color-blind eyes",
|
|
667
|
+
bg: "#ffffff",
|
|
668
|
+
panel: "#f5f6f8",
|
|
669
|
+
border: "#c8cdd4",
|
|
670
|
+
borderFocused: "#1f2328",
|
|
671
|
+
text: "#1f2328",
|
|
672
|
+
textDim: "#4d545e",
|
|
673
|
+
textBright: "#000000",
|
|
674
|
+
textMuted: "#646b75",
|
|
675
|
+
positive: "#0072b2",
|
|
676
|
+
negative: "#a84100",
|
|
677
|
+
neutral: "#5f6670",
|
|
678
|
+
warning: "#a27409",
|
|
679
|
+
header: "#e9ecef",
|
|
680
|
+
headerText: "#1f2328",
|
|
681
|
+
selected: "#d3e3f0",
|
|
682
|
+
selectedText: "#000000",
|
|
683
|
+
commandBg: "#f5f6f8",
|
|
684
|
+
commandBorder: "#1f2328",
|
|
685
|
+
},
|
|
686
|
+
|
|
687
|
+
// Every text role clears 7:1 (WCAG AAA) on the page, and the up, down and
|
|
688
|
+
// warning colours are the colour-blind safe ones.
|
|
689
|
+
"high-contrast": {
|
|
690
|
+
name: "High Contrast",
|
|
691
|
+
description: "White on black at AAA contrast, color-blind safe",
|
|
692
|
+
bg: "#000000",
|
|
693
|
+
panel: "#000000",
|
|
694
|
+
border: "#9a9a9a",
|
|
695
|
+
borderFocused: "#ffffff",
|
|
696
|
+
text: "#ffffff",
|
|
697
|
+
textDim: "#e0e0e0",
|
|
698
|
+
textBright: "#ffffff",
|
|
699
|
+
textMuted: "#c8c8c8",
|
|
700
|
+
positive: "#56b4e9",
|
|
701
|
+
negative: "#ff8c42",
|
|
702
|
+
neutral: "#c8c8c8",
|
|
703
|
+
warning: "#f0e442",
|
|
704
|
+
header: "#1a1a1a",
|
|
705
|
+
headerText: "#ffffff",
|
|
706
|
+
selected: "#3d3d3d",
|
|
707
|
+
selectedText: "#ffffff",
|
|
708
|
+
commandBg: "#000000",
|
|
709
|
+
commandBorder: "#ffffff",
|
|
710
|
+
},
|
|
636
711
|
};
|
|
637
712
|
|
|
638
713
|
export const themes: Record<string, Theme> = Object.fromEntries(
|
|
@@ -661,3 +736,77 @@ export function isDarkTheme(themeId: string): boolean {
|
|
|
661
736
|
export function getTheme(id: string): Theme {
|
|
662
737
|
return themes[id] ?? themes[DEFAULT_THEME]!;
|
|
663
738
|
}
|
|
739
|
+
|
|
740
|
+
type ThemeTrait = "colorblind-safe" | "high-contrast";
|
|
741
|
+
|
|
742
|
+
const THEME_TRAITS: Readonly<Record<string, readonly ThemeTrait[]>> = {
|
|
743
|
+
colorblind: ["colorblind-safe"],
|
|
744
|
+
"colorblind-light": ["colorblind-safe"],
|
|
745
|
+
"high-contrast": ["high-contrast", "colorblind-safe"],
|
|
746
|
+
};
|
|
747
|
+
|
|
748
|
+
export interface ThemeSummary {
|
|
749
|
+
id: string;
|
|
750
|
+
name: string;
|
|
751
|
+
appearance: "dark" | "light";
|
|
752
|
+
traits: readonly ThemeTrait[];
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
/** Every theme in registry order: what `config themes`, help and the docs list. */
|
|
756
|
+
export function listThemes(): ThemeSummary[] {
|
|
757
|
+
return getThemeIds().map((id) => ({
|
|
758
|
+
id,
|
|
759
|
+
name: themes[id]!.name,
|
|
760
|
+
appearance: isDarkTheme(id) ? "dark" : "light",
|
|
761
|
+
traits: THEME_TRAITS[id] ?? [],
|
|
762
|
+
}));
|
|
763
|
+
}
|
|
764
|
+
|
|
765
|
+
export type ThemeLookup =
|
|
766
|
+
| { ok: true; id: string }
|
|
767
|
+
| { ok: false; suggestion: string | null };
|
|
768
|
+
|
|
769
|
+
/** `White Phosphor`, `white_phosphor` and `WHITE` all read as `white-phosphor` / `white`. */
|
|
770
|
+
function themeKey(value: string): string {
|
|
771
|
+
return value.trim().toLowerCase().replace(/[\s_]+/g, "-");
|
|
772
|
+
}
|
|
773
|
+
|
|
774
|
+
function editDistance(left: string, right: string): number {
|
|
775
|
+
let previous = Array.from({ length: right.length + 1 }, (_, index) => index);
|
|
776
|
+
for (let row = 1; row <= left.length; row += 1) {
|
|
777
|
+
const current = [row];
|
|
778
|
+
for (let column = 1; column <= right.length; column += 1) {
|
|
779
|
+
const substitution = previous[column - 1]! + (left[row - 1] === right[column - 1] ? 0 : 1);
|
|
780
|
+
current.push(Math.min(previous[column]! + 1, current[column - 1]! + 1, substitution));
|
|
781
|
+
}
|
|
782
|
+
previous = current;
|
|
783
|
+
}
|
|
784
|
+
return previous[right.length]!;
|
|
785
|
+
}
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* The theme id a person meant. An id or a display name, in any case, is that
|
|
789
|
+
* theme; anything else is unknown, with the closest id when the input is a
|
|
790
|
+
* typo of one (or of a name), or names part of exactly one theme.
|
|
791
|
+
*/
|
|
792
|
+
export function resolveThemeId(input: string): ThemeLookup {
|
|
793
|
+
const key = themeKey(input);
|
|
794
|
+
const ids = getThemeIds();
|
|
795
|
+
const exact = ids.find((id) => id === key) ?? ids.find((id) => themeKey(themes[id]!.name) === key);
|
|
796
|
+
if (exact) return { ok: true, id: exact };
|
|
797
|
+
if (!key) return { ok: false, suggestion: null };
|
|
798
|
+
|
|
799
|
+
let best: { id: string; distance: number } | null = null;
|
|
800
|
+
for (const id of ids) {
|
|
801
|
+
for (const candidate of [id, themeKey(themes[id]!.name)]) {
|
|
802
|
+
const distance = editDistance(key, candidate);
|
|
803
|
+
if (!best || distance < best.distance) best = { id, distance };
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
const tolerance = Math.max(2, Math.floor(key.length / 3));
|
|
807
|
+
if (best && best.distance <= tolerance) return { ok: false, suggestion: best.id };
|
|
808
|
+
const partial = key.length >= 3
|
|
809
|
+
? ids.filter((id) => id.includes(key) || themeKey(themes[id]!.name).includes(key))
|
|
810
|
+
: [];
|
|
811
|
+
return { ok: false, suggestion: partial.length === 1 ? partial[0]! : null };
|
|
812
|
+
}
|
|
@@ -9,6 +9,7 @@ import { assetClassMarketSymbol, parseAssetClassQuery } from "./asset-classes";
|
|
|
9
9
|
import { searchContractKey, searchInstrumentKey } from "./identity";
|
|
10
10
|
import { tickerInstrumentLabel } from "../instrument-label";
|
|
11
11
|
import {
|
|
12
|
+
answersOnlyByKeyword,
|
|
12
13
|
buildSymbolAliases,
|
|
13
14
|
classifyInstrumentKind,
|
|
14
15
|
findExactTickerSearchMatch,
|
|
@@ -178,6 +179,7 @@ function createProviderTickerSearchCandidates(
|
|
|
178
179
|
instrumentClass: classifyInstrumentKind(result.brokerContract?.secType || result.type),
|
|
179
180
|
instrumentType: result.brokerContract?.secType || result.type,
|
|
180
181
|
searchAliases: buildSearchResultAliases(result),
|
|
182
|
+
...(searchResultKeywords(result) ? { searchKeywords: searchResultKeywords(result) } : {}),
|
|
181
183
|
result,
|
|
182
184
|
}];
|
|
183
185
|
});
|
|
@@ -251,7 +253,7 @@ export function buildTickerSearchCandidates({
|
|
|
251
253
|
);
|
|
252
254
|
const providerItems = createProviderTickerSearchCandidates(filteredProviderResults, tickers, candidateOptions);
|
|
253
255
|
const ranked = rankTickerSearchItems([...localItems, ...providerItems], query);
|
|
254
|
-
return limitTickerSearchCandidates(assignTickerSearchCategories(ranked), totalLimit, localLimit);
|
|
256
|
+
return limitTickerSearchCandidates(assignTickerSearchCategories(ranked, query), totalLimit, localLimit);
|
|
255
257
|
}
|
|
256
258
|
|
|
257
259
|
interface ResolveTickerSearchOptions {
|
|
@@ -589,10 +591,22 @@ function getProviderHintScore(result: InstrumentSearchResult, preferredExchange?
|
|
|
589
591
|
return getProviderHintRichness(result) + (matchesPreferredExchange ? 10_000 : 0);
|
|
590
592
|
}
|
|
591
593
|
|
|
592
|
-
|
|
594
|
+
/** Older servers send none; anything malformed is ignored. */
|
|
595
|
+
function searchResultKeywords(result: InstrumentSearchResult): string[] | undefined {
|
|
596
|
+
const keywords = Array.isArray(result.searchKeywords)
|
|
597
|
+
? result.searchKeywords.filter((keyword): keyword is string => typeof keyword === "string" && !!keyword.trim())
|
|
598
|
+
: [];
|
|
599
|
+
return keywords.length > 0 ? keywords : undefined;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
function assignTickerSearchCategories<T extends TickerSearchCandidate>(items: T[], query: string): T[] {
|
|
593
603
|
let assignedPrimaryListing = false;
|
|
594
604
|
|
|
595
605
|
return items.map((item) => {
|
|
606
|
+
// A company the theme's funds hold is not another listing of the one searched for.
|
|
607
|
+
if (!item.saved && item.kind === "search" && item.instrumentClass === "equity" && answersOnlyByKeyword(item, query)) {
|
|
608
|
+
return { ...item, category: "Related" };
|
|
609
|
+
}
|
|
596
610
|
if (item.saved || item.kind === "ticker") {
|
|
597
611
|
if (item.instrumentClass !== "fund" && item.instrumentClass !== "derivative") {
|
|
598
612
|
assignedPrimaryListing = true;
|
|
@@ -669,5 +683,11 @@ function buildSearchResultAliases(result: InstrumentSearchResult): string[] {
|
|
|
669
683
|
if (result.brokerContract?.symbol) {
|
|
670
684
|
for (const alias of buildSymbolAliases(result.brokerContract.symbol)) aliases.add(alias);
|
|
671
685
|
}
|
|
686
|
+
// Older servers send none. Anything else malformed is ignored.
|
|
687
|
+
if (Array.isArray(result.searchAliases)) {
|
|
688
|
+
for (const alias of result.searchAliases) {
|
|
689
|
+
if (typeof alias === "string" && alias.trim()) aliases.add(alias.trim());
|
|
690
|
+
}
|
|
691
|
+
}
|
|
672
692
|
return [...aliases];
|
|
673
693
|
}
|