gloomberb 0.16.2 → 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/account-cache.ts +45 -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/command-options.ts +74 -0
- package/src/cli/commands/command-utils.ts +29 -0
- package/src/cli/commands/crypto-hints.ts +12 -4
- package/src/cli/commands/market.ts +524 -148
- package/src/cli/commands/options-chain.ts +172 -0
- package/src/cli/commands/options-leaps.ts +422 -0
- package/src/cli/commands/overview.ts +223 -42
- 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 +200 -73
- package/src/cli/desktop-pane-shot.ts +100 -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 +40 -7
- package/src/cli/history-rows.ts +187 -15
- package/src/cli/index-roots.ts +51 -0
- package/src/cli/index.ts +21 -13
- package/src/cli/listing-arg.ts +211 -3
- package/src/cli/not-a-ticker.ts +37 -0
- package/src/cli/options.ts +55 -7
- package/src/cli/pane-functions/capabilities.ts +13 -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 +89 -28
- package/src/cli/pane-functions/freshness.ts +205 -26
- package/src/cli/pane-functions/function-help.ts +71 -24
- package/src/cli/pane-functions/headless.ts +168 -35
- package/src/cli/pane-functions/index.ts +12 -2
- 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 +12 -1
- package/src/cli/pane-functions/resolver.ts +32 -8
- package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
- package/src/cli/pane-functions/screenshot.ts +109 -23
- package/src/cli/registry.ts +8 -4
- package/src/cli/report-tables.ts +460 -0
- package/src/cli/result.ts +63 -16
- 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/export.ts +6 -51
- package/src/components/data-table/view.tsx +31 -10
- 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 +67 -35
- package/src/components/layout/shell/cursor-occlusion.ts +6 -5
- package/src/components/layout/shell/drag/index.ts +16 -7
- package/src/components/layout/shell/drag/live.ts +193 -0
- package/src/components/layout/shell/drag/overlays.tsx +179 -35
- package/src/components/layout/shell/drag/runtime.ts +62 -50
- package/src/components/layout/shell/drag/slide.ts +10 -0
- package/src/components/layout/shell/index.tsx +84 -57
- package/src/components/layout/shell/layout-state.ts +5 -11
- package/src/components/layout/shell/menu.ts +28 -1
- package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
- package/src/components/layout/shell/native/surfaces.ts +30 -14
- package/src/components/layout/shell/pane/actions.ts +2 -1
- package/src/components/layout/shell/pane/layers.tsx +461 -180
- 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/terminal-pointer-runtime.ts +18 -18
- package/src/components/layout/shell/visible-layout.ts +44 -1
- package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
- 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/data/json-persistence.ts +17 -3
- package/src/data/json-storage.ts +2 -0
- package/src/data/memory-resource-store.ts +46 -0
- 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 +156 -11
- 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/freshness.ts +5 -0
- 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/pane-model.ts +10 -6
- 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/attention/pane.tsx +1 -1
- 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/catalysts/detail.tsx +1 -1
- 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-attention/pane.tsx +1 -1
- 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/client.ts +3 -6
- package/src/plugins/builtin/holders/format.ts +31 -1
- package/src/plugins/builtin/holders/headless.ts +32 -6
- package/src/plugins/builtin/holders/pane.tsx +25 -13
- package/src/plugins/builtin/holders/report-header.ts +46 -5
- 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 +121 -45
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
- 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/members/pane.tsx +10 -0
- 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/perps/pane.tsx +1 -1
- 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/account-metrics.ts +16 -1
- package/src/plugins/builtin/portfolio-list/allocation.ts +3 -0
- package/src/plugins/builtin/portfolio-list/cached-account.ts +18 -9
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
- package/src/plugins/builtin/portfolio-list/cli/render.ts +89 -18
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
- package/src/plugins/builtin/portfolio-list/headless.ts +40 -7
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -4
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -4
- package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
- 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/research/relative-valuation-model.ts +7 -3
- 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/model.ts +2 -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 +260 -0
- 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/short-volume/pane.tsx +1 -1
- package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
- package/src/plugins/builtin/supply-chain/model.ts +1 -1
- package/src/plugins/builtin/themes/pane.tsx +3 -0
- package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
- package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
- package/src/plugins/builtin/ticker-detail/headless.ts +63 -25
- package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -4
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +3 -2
- 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/basemap-layer.tsx +2 -0
- package/src/plugins/builtin/world-venue-map/basemap.ts +18 -0
- package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
- package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
- package/src/plugins/builtin/world-venue-map/geo-svg.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/index.ts +6 -6
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +27 -14
- package/src/plugins/builtin/world-venue-map/layers.ts +68 -10
- package/src/plugins/builtin/world-venue-map/map.tsx +198 -62
- package/src/plugins/builtin/world-venue-map/model.ts +16 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +3 -0
- package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
- package/src/plugins/builtin/world-venue-map/pane.tsx +16 -10
- 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/browser/app-services.ts +3 -1
- package/src/renderers/dom/data-table/dom.ts +31 -0
- package/src/renderers/dom/data-table/index.tsx +5 -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 +113 -17
- package/src/renderers/dom/host/drag-shield.ts +35 -0
- package/src/renderers/dom/host/live-frame.ts +62 -0
- package/src/renderers/dom/host/mouse.ts +8 -1
- package/src/renderers/dom/host/scroll-box.tsx +6 -5
- package/src/renderers/dom/host/tabs.tsx +3 -2
- package/src/renderers/dom/input-host.tsx +6 -1
- package/src/renderers/dom/styles.css +26 -5
- package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
- package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
- package/src/renderers/electrobun/bun/desktop/initialization.ts +15 -1
- package/src/renderers/electrobun/shared/protocol.ts +12 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -1
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
- package/src/renderers/electrobun/view/host/native.ts +0 -10
- package/src/renderers/electrobun/view/host/window-move.ts +28 -0
- package/src/renderers/electrobun/view/main.tsx +4 -1
- package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
- package/src/renderers/electrobun/view/ui-host.tsx +2 -5
- package/src/renderers/opentui/input-host.tsx +6 -1
- package/src/renderers/opentui/start.tsx +9 -0
- package/src/renderers/opentui/ui-host.tsx +7 -1
- package/src/shares/portable-layout.ts +16 -6
- package/src/sources/gloomberb-cloud/index.ts +19 -10
- package/src/sources/gloomberb-cloud/normalizers.ts +20 -21
- package/src/sources/listing-symbols.ts +11 -5
- package/src/sources/provider-errors.ts +88 -3
- package/src/sources/provider-router/batches.ts +3 -3
- package/src/sources/provider-router/cached-routes.ts +8 -4
- package/src/sources/provider-router/financial-routes.ts +13 -7
- package/src/sources/provider-router/financials.ts +106 -16
- 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 +24 -6
- package/src/sources/sec-edgar.ts +68 -8
- package/src/state/pane-scroll-registry.tsx +67 -35
- 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/margin-account.ts +108 -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/resolution.ts +15 -0
- package/src/time-series/resolve.ts +1 -14
- package/src/types/config.ts +61 -1
- package/src/types/financials.ts +32 -3
- 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 +16 -0
- package/src/ui/index.tsx +10 -3
- package/src/ui/scroll-box-axis.ts +26 -0
- package/src/utils/cli-output.ts +14 -1
- package/src/utils/currency-units.ts +8 -3
- package/src/utils/depositary-receipt.ts +54 -10
- package/src/utils/display-number.ts +63 -0
- package/src/utils/exchanges.ts +13 -0
- package/src/utils/format.ts +2 -1
- package/src/utils/fundamentals.ts +17 -1
- package/src/utils/fx-market-hours.ts +34 -0
- package/src/utils/option-expiry.ts +126 -0
- package/src/utils/price-history-integrity.ts +19 -6
- 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
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 })))));
|
|
@@ -5,9 +5,12 @@ import { useAppSelector, useOptionalPaneInstanceId } from "./app/context";
|
|
|
5
5
|
|
|
6
6
|
type ScrollDirection = "up" | "down";
|
|
7
7
|
|
|
8
|
+
/** A scroll position: rows (or cells), or device pixels where the host measures in them. */
|
|
9
|
+
type ScrollTarget = { cells: number } | { pixels: number };
|
|
10
|
+
|
|
8
11
|
interface PaneScrollAction {
|
|
9
12
|
direction: ScrollDirection;
|
|
10
|
-
resolveTarget(scrollBox: ScrollBoxRenderable):
|
|
13
|
+
resolveTarget(scrollBox: ScrollBoxRenderable): ScrollTarget | null;
|
|
11
14
|
}
|
|
12
15
|
|
|
13
16
|
interface PaneScrollEntry {
|
|
@@ -57,17 +60,58 @@ function isVisibleScrollBox(scrollBox: ScrollBoxRenderable): boolean {
|
|
|
57
60
|
return !hasHiddenAncestor(scrollBox) && !hasCollapsedBounds(scrollBox);
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
function
|
|
63
|
+
function maxScrollTop(scrollBox: ScrollBoxRenderable): number {
|
|
61
64
|
const viewportHeight = Math.max(0, scrollBox.viewport?.height ?? 0);
|
|
62
|
-
if (viewportHeight <= 0) return
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
65
|
+
if (viewportHeight <= 0) return 0;
|
|
66
|
+
return Math.max(0, scrollBox.scrollHeight - viewportHeight);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The desktop host rounds its viewport and content to whole rows, which can
|
|
71
|
+
* leave up to half a row of content past the last scrollable row. Where it
|
|
72
|
+
* also reports pixels, the ends are measured in them.
|
|
73
|
+
*/
|
|
74
|
+
function pixelRange(scrollBox: ScrollBoxRenderable): { top: number; max: number } | null {
|
|
75
|
+
const { scrollTopPx, scrollHeightPx, viewportPx } = scrollBox;
|
|
76
|
+
if (!scrollBox.scrollToPixels || typeof scrollTopPx !== "number" || typeof scrollHeightPx !== "number" || !viewportPx) {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
return { top: scrollTopPx, max: Math.max(0, scrollHeightPx - viewportPx.height) };
|
|
66
80
|
}
|
|
67
81
|
|
|
68
|
-
function
|
|
69
|
-
|
|
70
|
-
|
|
82
|
+
function topTarget(scrollBox: ScrollBoxRenderable): ScrollTarget | null {
|
|
83
|
+
if ((scrollBox.viewport?.height ?? 0) <= 0) return null;
|
|
84
|
+
const pixels = pixelRange(scrollBox);
|
|
85
|
+
if (pixels) return pixels.top >= 1 ? { pixels: 0 } : null;
|
|
86
|
+
return scrollBox.scrollTop > 0 ? { cells: 0 } : null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
function bottomTarget(scrollBox: ScrollBoxRenderable): ScrollTarget | null {
|
|
90
|
+
const max = maxScrollTop(scrollBox);
|
|
91
|
+
// A body that overflows by less than a row is not worth capturing the keys for.
|
|
92
|
+
if (max <= 0) return null;
|
|
93
|
+
const pixels = pixelRange(scrollBox);
|
|
94
|
+
if (pixels) return pixels.max - pixels.top >= 1 ? { pixels: pixels.max } : null;
|
|
95
|
+
return max !== scrollBox.scrollTop ? { cells: max } : null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function scrollTargetByDelta(scrollBox: ScrollBoxRenderable, delta: number): ScrollTarget | null {
|
|
99
|
+
if ((scrollBox.viewport?.height ?? 0) <= 0) return null;
|
|
100
|
+
const wanted = scrollBox.scrollTop + delta;
|
|
101
|
+
if (delta < 0 && wanted <= 0) return topTarget(scrollBox);
|
|
102
|
+
if (delta > 0 && wanted >= maxScrollTop(scrollBox)) return bottomTarget(scrollBox);
|
|
103
|
+
return delta === 0 ? null : { cells: wanted };
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function applyScrollTarget(scrollBox: ScrollBoxRenderable, target: ScrollTarget): void {
|
|
107
|
+
if ("pixels" in target) {
|
|
108
|
+
scrollBox.scrollToPixels?.(target.pixels);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
scrollBox.scrollTo(target.cells);
|
|
112
|
+
if (scrollBox.scrollTop !== target.cells) {
|
|
113
|
+
scrollBox.scrollTop = target.cells;
|
|
114
|
+
}
|
|
71
115
|
}
|
|
72
116
|
|
|
73
117
|
/**
|
|
@@ -76,7 +120,7 @@ function scrollTargetByDelta(scrollBox: ScrollBoxRenderable, delta: number): num
|
|
|
76
120
|
*/
|
|
77
121
|
export function scrollByLines(scrollBox: ScrollBoxRenderable, delta: number): void {
|
|
78
122
|
const target = scrollTargetByDelta(scrollBox, delta);
|
|
79
|
-
if (target
|
|
123
|
+
if (target) applyScrollTarget(scrollBox, target);
|
|
80
124
|
}
|
|
81
125
|
|
|
82
126
|
function arrowScrollLines(scrollBox: ScrollBoxRenderable): number {
|
|
@@ -117,20 +161,9 @@ function resolveKeyScrollAction(event: PaneScrollKeyEvent): PaneScrollAction | n
|
|
|
117
161
|
),
|
|
118
162
|
};
|
|
119
163
|
case "home":
|
|
120
|
-
return {
|
|
121
|
-
direction: "up",
|
|
122
|
-
resolveTarget: (scrollBox) => (
|
|
123
|
-
scrollBox.scrollTop > 0 ? 0 : null
|
|
124
|
-
),
|
|
125
|
-
};
|
|
164
|
+
return { direction: "up", resolveTarget: topTarget };
|
|
126
165
|
case "end":
|
|
127
|
-
return {
|
|
128
|
-
direction: "down",
|
|
129
|
-
resolveTarget: (scrollBox) => {
|
|
130
|
-
const target = clampScrollTop(scrollBox, Number.MAX_SAFE_INTEGER);
|
|
131
|
-
return target == null || target === scrollBox.scrollTop ? null : target;
|
|
132
|
-
},
|
|
133
|
-
};
|
|
166
|
+
return { direction: "down", resolveTarget: bottomTarget };
|
|
134
167
|
default:
|
|
135
168
|
return null;
|
|
136
169
|
}
|
|
@@ -145,12 +178,12 @@ function getEntryScore(entry: PaneScrollEntry, scrollBox: ScrollBoxRenderable):
|
|
|
145
178
|
function findScrollableEntry(paneId: string, action: PaneScrollAction): {
|
|
146
179
|
entry: PaneScrollEntry;
|
|
147
180
|
scrollBox: ScrollBoxRenderable;
|
|
148
|
-
target:
|
|
181
|
+
target: ScrollTarget;
|
|
149
182
|
} | null {
|
|
150
183
|
const entries = paneScrollBoxes.get(paneId);
|
|
151
184
|
if (!entries) return null;
|
|
152
185
|
|
|
153
|
-
let best: { entry: PaneScrollEntry; scrollBox: ScrollBoxRenderable; target:
|
|
186
|
+
let best: { entry: PaneScrollEntry; scrollBox: ScrollBoxRenderable; target: ScrollTarget; score: number } | null = null;
|
|
154
187
|
for (const entry of entries.values()) {
|
|
155
188
|
const scrollBox = entry.ref.current;
|
|
156
189
|
if (!scrollBox) continue;
|
|
@@ -176,17 +209,16 @@ function scrollPaneByKey(paneId: string, event: PaneScrollKeyEvent): boolean {
|
|
|
176
209
|
const match = findScrollableEntry(paneId, action);
|
|
177
210
|
if (!match) return false;
|
|
178
211
|
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
212
|
+
const { scrollBox } = match;
|
|
213
|
+
const position = () => scrollBox.scrollTopPx ?? scrollBox.scrollTop;
|
|
214
|
+
const previousTop = scrollBox.scrollTop;
|
|
215
|
+
const previousPosition = position();
|
|
216
|
+
applyScrollTarget(scrollBox, match.target);
|
|
184
217
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
return delta > 0;
|
|
218
|
+
if (position() === previousPosition) return false;
|
|
219
|
+
const delta = Math.max(1, Math.abs(scrollBox.scrollTop - previousTop));
|
|
220
|
+
match.entry.onScrollActivity?.({ scroll: { direction: action.direction, delta } });
|
|
221
|
+
return true;
|
|
190
222
|
}
|
|
191
223
|
|
|
192
224
|
export function useForwardedScrollBoxRef<T>(
|
|
@@ -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,108 @@
|
|
|
1
|
+
import type { Quote, TickerFinancials } from "../types/financials";
|
|
2
|
+
import type { Portfolio, TickerRecord } from "../types/ticker";
|
|
3
|
+
import type { BrokerAccount } from "../types/trading";
|
|
4
|
+
import { createTestTicker } from "./ticker";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* An invented margin account: 1.0M of equity carrying 1.6M of positions, the
|
|
8
|
+
* difference borrowed in three currencies. It holds a US stock, a listing
|
|
9
|
+
* quoted in euros and an option, which no quote prices, so it stays at its
|
|
10
|
+
* broker mark. Every figure is made up.
|
|
11
|
+
*/
|
|
12
|
+
export const MARGIN_PORTFOLIO: Portfolio = {
|
|
13
|
+
id: "broker:margin-test:M1",
|
|
14
|
+
name: "Margin test",
|
|
15
|
+
currency: "USD",
|
|
16
|
+
brokerId: "test",
|
|
17
|
+
brokerInstanceId: "margin-test",
|
|
18
|
+
brokerAccountId: "M1",
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/** Dollars per unit of each currency. */
|
|
22
|
+
export const MARGIN_EXCHANGE_RATES = new Map([["USD", 1], ["EUR", 1.2], ["JPY", 0.008]]);
|
|
23
|
+
|
|
24
|
+
const OPTION = "CCC 280121C00050000";
|
|
25
|
+
|
|
26
|
+
function holding(symbol: string, details: {
|
|
27
|
+
exchange: string;
|
|
28
|
+
currency: string;
|
|
29
|
+
shares: number;
|
|
30
|
+
mark: number;
|
|
31
|
+
multiplier?: number;
|
|
32
|
+
assetCategory?: string;
|
|
33
|
+
}): TickerRecord {
|
|
34
|
+
return createTestTicker(symbol, symbol, {
|
|
35
|
+
exchange: details.exchange,
|
|
36
|
+
currency: details.currency,
|
|
37
|
+
assetCategory: details.assetCategory,
|
|
38
|
+
portfolios: [MARGIN_PORTFOLIO.id],
|
|
39
|
+
positions: [{
|
|
40
|
+
portfolio: MARGIN_PORTFOLIO.id,
|
|
41
|
+
shares: details.shares,
|
|
42
|
+
avgCost: details.mark,
|
|
43
|
+
markPrice: details.mark,
|
|
44
|
+
marketValue: details.shares * details.mark * (details.multiplier ?? 1),
|
|
45
|
+
unrealizedPnl: 0,
|
|
46
|
+
multiplier: details.multiplier,
|
|
47
|
+
currency: details.currency,
|
|
48
|
+
side: "long",
|
|
49
|
+
broker: "test",
|
|
50
|
+
brokerInstanceId: "margin-test",
|
|
51
|
+
brokerAccountId: "M1",
|
|
52
|
+
}],
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export const MARGIN_TICKERS: TickerRecord[] = [
|
|
57
|
+
holding("AAA", { exchange: "NASDAQ", currency: "USD", shares: 4_000, mark: 200 }),
|
|
58
|
+
holding("BBB", { exchange: "XETRA", currency: "EUR", shares: 5_000, mark: 100 }),
|
|
59
|
+
holding(OPTION, { exchange: "", currency: "USD", shares: 20, mark: 100, multiplier: 100, assetCategory: "OPT" }),
|
|
60
|
+
];
|
|
61
|
+
|
|
62
|
+
export function createMarginAccount(overrides: Partial<BrokerAccount> = {}): BrokerAccount {
|
|
63
|
+
return {
|
|
64
|
+
accountId: "M1",
|
|
65
|
+
name: "Margin test",
|
|
66
|
+
currency: "USD",
|
|
67
|
+
source: "cloud",
|
|
68
|
+
updatedAt: Date.now() - 60_000,
|
|
69
|
+
netLiquidation: 1_000_000,
|
|
70
|
+
grossPositionValue: 1_600_000,
|
|
71
|
+
totalCashValue: -600_000,
|
|
72
|
+
cashBalances: [
|
|
73
|
+
{ currency: "EUR", quantity: -250_000, baseValue: -300_000, baseCurrency: "USD" },
|
|
74
|
+
{ currency: "JPY", quantity: -25_000_000, baseValue: -200_000, baseCurrency: "USD" },
|
|
75
|
+
{ currency: "USD", quantity: -100_000, baseValue: -100_000, baseCurrency: "USD" },
|
|
76
|
+
],
|
|
77
|
+
...overrides,
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function liveQuote(symbol: string, price: number, previousClose: number, currency: string, exchange: string): TickerFinancials {
|
|
82
|
+
const quote: Quote = {
|
|
83
|
+
symbol,
|
|
84
|
+
price,
|
|
85
|
+
change: price - previousClose,
|
|
86
|
+
changePercent: (price / previousClose - 1) * 100,
|
|
87
|
+
previousClose,
|
|
88
|
+
currency,
|
|
89
|
+
lastUpdated: Date.now() - 1_000,
|
|
90
|
+
listingExchangeName: exchange,
|
|
91
|
+
marketState: "REGULAR",
|
|
92
|
+
dataSource: "live",
|
|
93
|
+
};
|
|
94
|
+
return { annualStatements: [], quarterlyStatements: [], priceHistory: [], quote };
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Real-time quotes for the stock and the euro listing, the stock `move`
|
|
99
|
+
* dollars above its mark. With `option`, the option is quoted at its mark too,
|
|
100
|
+
* so every position has a live price.
|
|
101
|
+
*/
|
|
102
|
+
export function marginQuotes({ move = 0, option = false }: { move?: number; option?: boolean } = {}): Map<string, TickerFinancials> {
|
|
103
|
+
return new Map([
|
|
104
|
+
["AAA", liveQuote("AAA", 200 + move, 200, "USD", "NASDAQ")],
|
|
105
|
+
["BBB", liveQuote("BBB", 100, 100, "EUR", "XETRA")],
|
|
106
|
+
...(option ? [[OPTION, liveQuote(OPTION, 100, 100, "USD", "CBOE")] as const] : []),
|
|
107
|
+
]);
|
|
108
|
+
}
|
|
@@ -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
|
+
}
|