gloomberb 0.15.7 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -2
- package/package.json +1 -1
- package/src/api-client/app-attention.ts +147 -0
- package/src/api-client/attention.ts +26 -0
- package/src/api-client/awards.ts +215 -0
- package/src/api-client/catalysts.ts +160 -0
- package/src/api-client/chat.ts +137 -14
- package/src/api-client/company-kpis.ts +153 -0
- package/src/api-client/cpi.ts +52 -0
- package/src/api-client/credit-documents.ts +107 -0
- package/src/api-client/data.ts +192 -3
- package/src/api-client/distress.ts +164 -0
- package/src/api-client/doe.ts +75 -0
- package/src/api-client/equity-screener.ts +11 -0
- package/src/api-client/exposure.ts +202 -0
- package/src/api-client/gpu.ts +93 -0
- package/src/api-client/hiring.ts +110 -0
- package/src/api-client/index.ts +78 -3
- package/src/api-client/normalizers.ts +31 -1
- package/src/api-client/paths.ts +3 -0
- package/src/api-client/perps.ts +127 -0
- package/src/api-client/power.ts +148 -0
- package/src/api-client/request.ts +58 -1
- package/src/api-client/research-activity.ts +172 -13
- package/src/api-client/socket.ts +75 -27
- package/src/api-client/supply-chain-graph.ts +40 -0
- package/src/api-client/supply-chain.ts +98 -0
- package/src/api-client/telemetry.ts +29 -0
- package/src/api-client/themes.ts +28 -0
- package/src/api-client/types.ts +33 -1
- package/src/api-client/wall-experiments.ts +100 -0
- package/src/api-client/wall-summary.ts +49 -0
- package/src/api-client/web-experiments.ts +136 -0
- package/src/app/pane-runtime/plugin-bindings.ts +5 -0
- package/src/app/pane-runtime/ticker-open-runtime.ts +8 -2
- package/src/app/runtime/update.ts +13 -1
- package/src/cli/commands/system.ts +2 -0
- package/src/cli/desktop-pane-shot.ts +3 -4
- package/src/cli/pane-functions/data.ts +78 -0
- package/src/cli/pane-functions/financial-ratio-shot.ts +160 -0
- package/src/cli/pane-functions/function-help.ts +220 -8
- package/src/cli/pane-functions/headless.ts +4 -0
- package/src/cli/pane-functions/screenshot-evidence.ts +21 -0
- package/src/cli/pane-functions/screenshot.ts +49 -5
- package/src/components/chart/composite/composite-chart.tsx +15 -2
- package/src/components/chart/composite/format.ts +24 -11
- package/src/components/chart/composite/rasterizer.ts +67 -16
- package/src/components/chart/composite/scene.ts +53 -2
- package/src/components/chart/composite/session-shading.ts +3 -2
- package/src/components/chart/composite/text-renderer.ts +52 -1
- package/src/components/chart/composite/time-axis.ts +100 -30
- package/src/components/chart/composite/tools.ts +2 -1
- package/src/components/chart/composite/types.ts +4 -0
- package/src/components/chart-table/index.ts +1 -0
- package/src/components/chart-table/layout.ts +15 -0
- package/src/components/command-bar/action-runtime.ts +1 -0
- package/src/components/command-bar/commands/direct/index.ts +32 -0
- package/src/components/command-bar/commands/registry.ts +7 -0
- package/src/components/command-bar/pane-templates/items.ts +11 -0
- package/src/components/command-bar/pane-templates/workflow.ts +19 -4
- package/src/components/command-bar/routes/root/command-items.ts +6 -0
- package/src/components/command-bar/routes/root/provider-search.ts +2 -0
- package/src/components/command-bar/routes/root/results.ts +64 -0
- package/src/components/command-bar/routes/root/runtime.ts +8 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +89 -24
- package/src/components/command-bar/routes/ticker-search/route.ts +2 -0
- package/src/components/command-bar/surface/index.tsx +15 -0
- package/src/components/command-bar/view-model.ts +3 -0
- package/src/components/command-bar/workflow/collection-actions.ts +9 -7
- package/src/components/command-bar/workflow/ops.ts +19 -0
- package/src/components/command-bar/workflow/tickers.ts +5 -0
- package/src/components/company-logo.tsx +15 -2
- package/src/components/data-table/table-pane.ts +2 -1
- package/src/components/index.ts +1 -0
- package/src/components/layout/detached-pane-shell.tsx +12 -1
- package/src/components/layout/missing-pane.tsx +87 -23
- package/src/components/layout/pane/footer/index.tsx +64 -18
- package/src/components/layout/pane/footer/model.ts +85 -0
- package/src/components/layout/pane/sidebar.tsx +19 -1
- package/src/components/layout/shell/index.tsx +38 -28
- package/src/components/layout/shell/pane/management-shortcuts.ts +13 -1
- package/src/components/layout/status-bar.tsx +93 -28
- package/src/components/layout/status-widget-space.ts +72 -0
- package/src/components/onboarding/first-run-workspace.ts +8 -2
- package/src/components/onboarding/onboarding-wizard.tsx +35 -16
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -5
- package/src/components/onboarding/wizard-model.ts +13 -5
- package/src/components/onboarding/wizard-positions.ts +56 -7
- package/src/components/price-performance.tsx +31 -34
- package/src/components/ui/data-table/index.tsx +11 -8
- package/src/components/ui/display.tsx +15 -6
- package/src/components/ui/fields.tsx +5 -0
- package/src/components/ui/figure.tsx +94 -0
- package/src/components/ui/header-case.ts +25 -3
- package/src/components/ui/icon.tsx +3 -1
- package/src/components/ui/message-composer.tsx +8 -1
- package/src/components/ui/query-bar.tsx +8 -1
- package/src/components/ui/ratio-bar.tsx +84 -0
- package/src/components/ui/toggle.tsx +3 -1
- package/src/components/use-scrollbox-layout.ts +17 -0
- package/src/core/state/app/layout.ts +35 -1
- package/src/core/state/app/state.ts +15 -0
- package/src/core/state/app/types.ts +1 -0
- package/src/data/config/store/normalize.ts +24 -0
- package/src/i18n/es.ts +2 -0
- package/src/i18n/ja.ts +2 -0
- package/src/i18n/ko.ts +2 -0
- package/src/i18n/zh-cn.ts +2 -0
- package/src/i18n/zh-tw.ts +2 -0
- package/src/layout-marketplace/pane-imagery.ts +1 -0
- package/src/market-data/coordinator/entries.ts +13 -0
- package/src/market-data/coordinator/financials.ts +33 -8
- package/src/market-data/market/freshness.ts +16 -5
- package/src/market-data/market/trading-sessions.ts +65 -1
- package/src/market-data/published-cn-sessions.ts +25 -0
- package/src/market-data/published-nse-sessions.ts +20 -0
- package/src/news/aggregator.ts +4 -0
- package/src/plugins/builtin/account-management/model.ts +6 -1
- package/src/plugins/builtin/account-management/pane.tsx +36 -25
- package/src/plugins/builtin/alerts/catalysts.ts +77 -0
- package/src/plugins/builtin/alerts/events-pane.tsx +3 -0
- package/src/plugins/builtin/alerts/events.ts +10 -4
- package/src/plugins/builtin/alerts/history-pane.tsx +1 -1
- package/src/plugins/builtin/alerts/history.ts +1 -0
- package/src/plugins/builtin/alerts/index.tsx +6 -0
- package/src/plugins/builtin/alerts/research-builder.ts +10 -3
- package/src/plugins/builtin/alerts/research-rules.ts +15 -1
- package/src/plugins/builtin/analytics/risk-client.ts +15 -3
- package/src/plugins/builtin/analytics/risk-headless.ts +123 -18
- package/src/plugins/builtin/attention/client.ts +80 -0
- package/src/plugins/builtin/attention/evidence.ts +30 -0
- package/src/plugins/builtin/attention/headless.ts +33 -0
- package/src/plugins/builtin/attention/index.tsx +18 -0
- package/src/plugins/builtin/attention/model.ts +59 -0
- package/src/plugins/builtin/attention/pane.tsx +211 -0
- package/src/plugins/builtin/attention/trending.tsx +59 -0
- package/src/plugins/builtin/awards/alerts.ts +32 -0
- package/src/plugins/builtin/awards/client.ts +82 -0
- package/src/plugins/builtin/awards/detail.tsx +107 -0
- package/src/plugins/builtin/awards/evidence.ts +36 -0
- package/src/plugins/builtin/awards/headless.ts +55 -0
- package/src/plugins/builtin/awards/index.tsx +20 -0
- package/src/plugins/builtin/awards/model.ts +42 -0
- package/src/plugins/builtin/awards/pane.tsx +263 -0
- package/src/plugins/builtin/catalysts/client.ts +28 -0
- package/src/plugins/builtin/catalysts/detail.tsx +88 -0
- package/src/plugins/builtin/catalysts/evidence.ts +47 -0
- package/src/plugins/builtin/catalysts/headless.ts +42 -0
- package/src/plugins/builtin/catalysts/history.ts +26 -0
- package/src/plugins/builtin/catalysts/index.tsx +29 -0
- package/src/plugins/builtin/catalysts/model.ts +135 -0
- package/src/plugins/builtin/catalysts/pane.tsx +159 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +6 -0
- package/src/plugins/builtin/chart-composer/index.tsx +8 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +28 -1
- package/src/plugins/builtin/chat/attachments/desktop.tsx +610 -0
- package/src/plugins/builtin/chat/attachments/files.ts +45 -0
- package/src/plugins/builtin/chat/attachments/model.ts +164 -0
- package/src/plugins/builtin/chat/attachments/terminal.tsx +72 -0
- package/src/plugins/builtin/chat/attachments/transfer.ts +165 -0
- package/src/plugins/builtin/chat/content/channel-navigation.ts +13 -12
- package/src/plugins/builtin/chat/content/composer-runtime.ts +25 -4
- package/src/plugins/builtin/chat/content/composer.tsx +46 -4
- package/src/plugins/builtin/chat/content/footer.tsx +46 -8
- package/src/plugins/builtin/chat/content/index.tsx +263 -62
- package/src/plugins/builtin/chat/content/layout-metrics.ts +10 -2
- package/src/plugins/builtin/chat/content/open-targets.ts +17 -3
- package/src/plugins/builtin/chat/content/scroll.ts +7 -1
- package/src/plugins/builtin/chat/content/shortcuts.ts +36 -11
- package/src/plugins/builtin/chat/content/snapshot.ts +3 -0
- package/src/plugins/builtin/chat/content/transcript.tsx +17 -1
- package/src/plugins/builtin/chat/content/types.ts +7 -0
- package/src/plugins/builtin/chat/controller/attachments.ts +213 -0
- package/src/plugins/builtin/chat/controller/connections.ts +3 -1
- package/src/plugins/builtin/chat/controller/index.ts +114 -2
- package/src/plugins/builtin/chat/controller/lifecycle.ts +9 -0
- package/src/plugins/builtin/chat/controller/messages.ts +35 -2
- package/src/plugins/builtin/chat/controller/send.ts +128 -25
- package/src/plugins/builtin/chat/controller/state.ts +12 -1
- package/src/plugins/builtin/chat/controller/utils.ts +7 -5
- package/src/plugins/builtin/chat/controller/view.ts +2 -0
- package/src/plugins/builtin/chat/index.tsx +21 -0
- package/src/plugins/builtin/chat/layout.ts +9 -4
- package/src/plugins/builtin/chat/message/desktop.tsx +55 -20
- package/src/plugins/builtin/chat/message/header.tsx +30 -5
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +9 -0
- package/src/plugins/builtin/chat/message/pending-send.ts +35 -0
- package/src/plugins/builtin/chat/message/profile-popover.tsx +5 -1
- package/src/plugins/builtin/chat/message/render-state.ts +12 -2
- package/src/plugins/builtin/chat/message/terminal.tsx +51 -4
- package/src/plugins/builtin/chat/message/types.ts +6 -1
- package/src/plugins/builtin/chat/pane-state.ts +59 -1
- package/src/plugins/builtin/chat/profile-popover.ts +50 -1
- package/src/plugins/builtin/chat/sidebar.tsx +8 -3
- package/src/plugins/builtin/chat/status-widget.tsx +29 -12
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +143 -0
- package/src/plugins/builtin/chat/unread-inbox.ts +91 -0
- package/src/plugins/builtin/clinical-trials/client.ts +237 -0
- package/src/plugins/builtin/clinical-trials/index.tsx +74 -0
- package/src/plugins/builtin/clinical-trials/pane.tsx +225 -0
- package/src/plugins/builtin/clinical-trials/types.ts +2 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +37 -6
- package/src/plugins/builtin/cloud/askg/executor.ts +59 -44
- package/src/plugins/builtin/cloud/askg/host.ts +8 -1
- package/src/plugins/builtin/cloud/askg/manifest.ts +29 -2
- package/src/plugins/builtin/cloud/askg/model.ts +47 -13
- package/src/plugins/builtin/cloud/askg/notes.ts +100 -0
- package/src/plugins/builtin/cloud/askg/pane.tsx +50 -14
- package/src/plugins/builtin/cloud/askg/protocol.ts +39 -1
- package/src/plugins/builtin/cloud/askg/result-budget.ts +156 -0
- package/src/plugins/builtin/cloud/askg/user-data.ts +89 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +55 -31
- package/src/plugins/builtin/cloud/plugin.tsx +4 -1
- package/src/plugins/builtin/cloud/team/pane-sections.tsx +10 -1
- package/src/plugins/builtin/cloud/team/pane.tsx +3 -2
- package/src/plugins/builtin/cloud/thesis/board-pane.tsx +2 -2
- package/src/plugins/builtin/cloud/thesis/detail.tsx +1 -1
- package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -2
- package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +202 -0
- package/src/plugins/builtin/cloud/upgrade-command.ts +1 -1
- package/src/plugins/builtin/cloud/upgrade-dialog.tsx +29 -3
- package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +1 -1
- package/src/plugins/builtin/cloud-tweets/table.tsx +1 -1
- package/src/plugins/builtin/comment-letters/client.ts +342 -0
- package/src/plugins/builtin/comment-letters/index.tsx +74 -0
- package/src/plugins/builtin/comment-letters/pane.tsx +295 -0
- package/src/plugins/builtin/comment-letters/types.ts +43 -0
- package/src/plugins/builtin/company-attention/app-rank.tsx +79 -0
- package/src/plugins/builtin/company-attention/client.ts +69 -0
- package/src/plugins/builtin/company-attention/evidence.ts +45 -0
- package/src/plugins/builtin/company-attention/headless.ts +47 -0
- package/src/plugins/builtin/company-attention/index.tsx +30 -0
- package/src/plugins/builtin/company-attention/model.ts +129 -0
- package/src/plugins/builtin/company-attention/pane.tsx +183 -0
- package/src/plugins/builtin/company-kpis/client.ts +92 -0
- package/src/plugins/builtin/company-kpis/headless.ts +32 -0
- package/src/plugins/builtin/company-kpis/index.tsx +26 -0
- package/src/plugins/builtin/company-kpis/model.ts +124 -0
- package/src/plugins/builtin/company-kpis/pane.tsx +226 -0
- package/src/plugins/builtin/company-kpis/related.ts +11 -0
- package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +44 -0
- package/src/plugins/builtin/composite-plugins.ts +34 -0
- package/src/plugins/builtin/correlation/history.ts +4 -1
- package/src/plugins/builtin/correlation/index.tsx +139 -80
- package/src/plugins/builtin/correlation/matrix/pair-chart.tsx +48 -0
- package/src/plugins/builtin/correlation/matrix/selection.ts +77 -0
- package/src/plugins/builtin/correlation/relationship/model.ts +2 -2
- package/src/plugins/builtin/cpi/client.ts +66 -0
- package/src/plugins/builtin/cpi/headless.ts +78 -0
- package/src/plugins/builtin/cpi/index.tsx +31 -0
- package/src/plugins/builtin/cpi/model.ts +198 -0
- package/src/plugins/builtin/cpi/pane.tsx +157 -0
- package/src/plugins/builtin/credit-documents/client.ts +99 -0
- package/src/plugins/builtin/credit-documents/detail.tsx +109 -0
- package/src/plugins/builtin/credit-documents/evidence.ts +64 -0
- package/src/plugins/builtin/credit-documents/gauge.tsx +55 -0
- package/src/plugins/builtin/credit-documents/headless.ts +40 -0
- package/src/plugins/builtin/credit-documents/index.tsx +19 -0
- package/src/plugins/builtin/credit-documents/model.ts +336 -0
- package/src/plugins/builtin/credit-documents/pane.tsx +185 -0
- package/src/plugins/builtin/custom-view/view-spec.ts +14 -2
- package/src/plugins/builtin/debt-maturities/pane.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/doe/client.ts +73 -0
- package/src/plugins/builtin/doe/headless.ts +93 -0
- package/src/plugins/builtin/doe/index.tsx +41 -0
- package/src/plugins/builtin/doe/model.ts +215 -0
- package/src/plugins/builtin/doe/pane.tsx +148 -0
- package/src/plugins/builtin/doe/range-bar.tsx +45 -0
- package/src/plugins/builtin/earnings/board.tsx +3 -0
- package/src/plugins/builtin/earnings/index.tsx +2 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +7 -1
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +7 -7
- package/src/plugins/builtin/earnings-ripple/client.ts +60 -0
- package/src/plugins/builtin/earnings-ripple/headless.ts +35 -0
- package/src/plugins/builtin/earnings-ripple/index.tsx +35 -0
- package/src/plugins/builtin/earnings-ripple/model.ts +98 -0
- package/src/plugins/builtin/earnings-ripple/pane.tsx +103 -0
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/criterion-editor.tsx +1 -1
- package/src/plugins/builtin/equity-screener/model.ts +74 -9
- package/src/plugins/builtin/equity-screener/pane.tsx +2 -0
- package/src/plugins/builtin/estimate-revisions/pane.tsx +3 -0
- package/src/plugins/builtin/executives/pane.tsx +7 -5
- package/src/plugins/builtin/exposure/charts.tsx +66 -0
- package/src/plugins/builtin/exposure/client.ts +53 -0
- package/src/plugins/builtin/exposure/drivers.ts +29 -0
- package/src/plugins/builtin/exposure/editor.tsx +61 -0
- package/src/plugins/builtin/exposure/evidence.ts +39 -0
- package/src/plugins/builtin/exposure/headless.ts +51 -0
- package/src/plugins/builtin/exposure/holdings.ts +56 -0
- package/src/plugins/builtin/exposure/index.tsx +13 -0
- package/src/plugins/builtin/exposure/model.ts +104 -0
- package/src/plugins/builtin/exposure/pane.tsx +247 -0
- package/src/plugins/builtin/fear-greed/charts.tsx +1 -1
- package/src/plugins/builtin/fear-greed/pane.tsx +2 -9
- package/src/plugins/builtin/filing-events/pane.tsx +2 -1
- package/src/plugins/builtin/gpu/board.tsx +163 -0
- package/src/plugins/builtin/gpu/changes.tsx +94 -0
- package/src/plugins/builtin/gpu/client.ts +130 -0
- package/src/plugins/builtin/gpu/equities.tsx +112 -0
- package/src/plugins/builtin/gpu/headless.ts +67 -0
- package/src/plugins/builtin/gpu/history.tsx +140 -0
- package/src/plugins/builtin/gpu/index.tsx +18 -0
- package/src/plugins/builtin/gpu/ladder.tsx +127 -0
- package/src/plugins/builtin/gpu/model.ts +317 -0
- package/src/plugins/builtin/gpu/pane.tsx +63 -0
- package/src/plugins/builtin/help/index.tsx +19 -2
- package/src/plugins/builtin/holders/pane.tsx +5 -9
- package/src/plugins/builtin/insider/detail.tsx +119 -0
- package/src/plugins/builtin/insider/display.ts +221 -0
- package/src/plugins/builtin/insider/headless.ts +24 -3
- package/src/plugins/builtin/insider/index.tsx +206 -108
- package/src/plugins/builtin/insider/insider-data.ts +31 -3
- package/src/plugins/builtin/insider/model.ts +53 -4
- package/src/plugins/builtin/insider/table-model.ts +226 -0
- package/src/plugins/builtin/insider/table.tsx +79 -0
- package/src/plugins/builtin/iv-history/screen-pane.tsx +1 -1
- package/src/plugins/builtin/jobs/pane.tsx +14 -7
- package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
- package/src/plugins/builtin/macro-day/client.ts +50 -0
- package/src/plugins/builtin/macro-day/headless.ts +51 -0
- package/src/plugins/builtin/macro-day/index.tsx +21 -0
- package/src/plugins/builtin/macro-day/model.ts +137 -0
- package/src/plugins/builtin/macro-day/pane.tsx +118 -0
- package/src/plugins/builtin/macro-day/releases.ts +58 -0
- package/src/plugins/builtin/market-halts/model.ts +30 -17
- package/src/plugins/builtin/market-halts/pane.tsx +15 -21
- package/src/plugins/builtin/market-heatmap/data.ts +1 -1
- package/src/plugins/builtin/market-heatmap/index.tsx +198 -50
- package/src/plugins/builtin/market-heatmap/portfolio.ts +220 -0
- package/src/plugins/builtin/market-movers/index.tsx +1 -0
- package/src/plugins/builtin/market-movers/session-body.tsx +5 -2
- package/src/plugins/builtin/mna/distress/client.ts +326 -0
- package/src/plugins/builtin/mna/distress/detail.tsx +247 -0
- package/src/plugins/builtin/mna/distress/headless.ts +257 -0
- package/src/plugins/builtin/mna/distress/model.ts +427 -0
- package/src/plugins/builtin/mna/distress/pane.tsx +606 -0
- package/src/plugins/builtin/mna/index.tsx +15 -0
- package/src/plugins/builtin/mna/pane.tsx +42 -1
- package/src/plugins/builtin/news/index.tsx +62 -16
- package/src/plugins/builtin/news/wire/filter-articles.ts +54 -0
- package/src/plugins/builtin/news/wire/index.tsx +37 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +29 -13
- package/src/plugins/builtin/news/wire/mutes.ts +253 -0
- package/src/plugins/builtin/news/wire/news/command-bar-search.ts +117 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +4 -0
- package/src/plugins/builtin/news/wire/news/list-search.ts +34 -0
- package/src/plugins/builtin/news/wire/news/pop-out.tsx +158 -0
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +64 -19
- package/src/plugins/builtin/openfda/client.ts +300 -0
- package/src/plugins/builtin/openfda/index.tsx +71 -0
- package/src/plugins/builtin/openfda/pane.tsx +382 -0
- package/src/plugins/builtin/openfda/types.ts +76 -0
- package/src/plugins/builtin/options/footer.ts +1 -0
- package/src/plugins/builtin/options/view.tsx +1 -1
- package/src/plugins/builtin/options-calculator/pane.tsx +0 -1
- package/src/plugins/builtin/options-positioning/index.tsx +4 -2
- package/src/plugins/builtin/options-positioning/pane.tsx +1 -1
- package/src/plugins/builtin/pe-band/client.ts +61 -0
- package/src/plugins/builtin/pe-band/headless.ts +51 -0
- package/src/plugins/builtin/pe-band/index.tsx +20 -0
- package/src/plugins/builtin/pe-band/model.ts +267 -0
- package/src/plugins/builtin/pe-band/pane.tsx +159 -0
- package/src/plugins/builtin/perps/client.ts +66 -0
- package/src/plugins/builtin/perps/equity-row.tsx +28 -0
- package/src/plugins/builtin/perps/headless.ts +34 -0
- package/src/plugins/builtin/perps/index.tsx +14 -0
- package/src/plugins/builtin/perps/model.ts +58 -0
- package/src/plugins/builtin/perps/pane.tsx +185 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +0 -1
- package/src/plugins/builtin/portfolio-list/cli/render.ts +177 -87
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +6 -20
- package/src/plugins/builtin/portfolio-list/command-bar.ts +6 -2
- package/src/plugins/builtin/portfolio-list/headless.ts +237 -0
- package/src/plugins/builtin/portfolio-list/index.tsx +2 -0
- package/src/plugins/builtin/portfolio-list/mutations.ts +70 -1
- package/src/plugins/builtin/portfolio-list/pane/data.ts +17 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +88 -5
- package/src/plugins/builtin/portfolio-list/pane/remove-ticker.ts +85 -0
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +18 -12
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +6 -0
- package/src/plugins/builtin/power/client.ts +100 -0
- package/src/plugins/builtin/power/detail.tsx +82 -0
- package/src/plugins/builtin/power/evidence.ts +56 -0
- package/src/plugins/builtin/power/generation.tsx +37 -0
- package/src/plugins/builtin/power/headless.ts +43 -0
- package/src/plugins/builtin/power/index.tsx +16 -0
- package/src/plugins/builtin/power/model.ts +89 -0
- package/src/plugins/builtin/power/pane.tsx +209 -0
- package/src/plugins/builtin/power/query.ts +25 -0
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -3
- package/src/plugins/builtin/research/analyst-pane.tsx +1 -1
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +13 -4
- package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +11 -9
- package/src/plugins/builtin/research-search/pane.tsx +21 -7
- package/src/plugins/builtin/revenue-breakdown/pane.tsx +1 -1
- package/src/plugins/builtin/reverse-dcf/client.ts +35 -0
- package/src/plugins/builtin/reverse-dcf/headless.ts +44 -0
- package/src/plugins/builtin/reverse-dcf/index.tsx +20 -0
- package/src/plugins/builtin/reverse-dcf/model.ts +87 -0
- package/src/plugins/builtin/reverse-dcf/pane.tsx +107 -0
- package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
- package/src/plugins/builtin/scanner/denied.tsx +4 -2
- package/src/plugins/builtin/scanner/feed.ts +1 -0
- package/src/plugins/builtin/scanner/flow-pane.tsx +3 -2
- package/src/plugins/builtin/scanner/hilo-pane.tsx +1 -1
- package/src/plugins/builtin/seasonality/client.ts +4 -1
- package/src/plugins/builtin/sec/command-bar-search.ts +217 -0
- package/src/plugins/builtin/sec/forms.ts +54 -0
- package/src/plugins/builtin/sec/headless.ts +31 -5
- package/src/plugins/builtin/sec/index.tsx +67 -11
- package/src/plugins/builtin/sec/model.ts +35 -5
- package/src/plugins/builtin/sectors/headless.ts +1 -0
- package/src/plugins/builtin/sectors/index.tsx +72 -26
- package/src/plugins/builtin/shared/cloud-upgrade.ts +143 -39
- package/src/plugins/builtin/shared/pro-feature-wall.tsx +8 -4
- package/src/plugins/builtin/shared/research-cells.tsx +107 -0
- package/src/plugins/builtin/shared/series-list-detail.tsx +5 -1
- package/src/plugins/builtin/shared/ticker-request.ts +1 -1
- package/src/plugins/builtin/shared/wall-teaser-catalog.ts +80 -0
- package/src/plugins/builtin/shared/wall-teaser.tsx +192 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +43 -0
- package/src/plugins/builtin/short-interest/index.tsx +31 -0
- package/src/plugins/builtin/short-interest/watch-client.ts +70 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +49 -0
- package/src/plugins/builtin/short-interest/watch-model.ts +185 -0
- package/src/plugins/builtin/short-interest/watch-pane.tsx +169 -0
- 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/client.ts +104 -0
- package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +17 -0
- package/src/plugins/builtin/supply-chain/evidence.ts +88 -0
- package/src/plugins/builtin/supply-chain/flow-layout.ts +133 -0
- package/src/plugins/builtin/supply-chain/flow.tsx +323 -0
- package/src/plugins/builtin/supply-chain/graph-client.ts +83 -0
- package/src/plugins/builtin/supply-chain/graph-evidence.ts +25 -0
- package/src/plugins/builtin/supply-chain/graph-model.ts +45 -0
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +218 -0
- package/src/plugins/builtin/supply-chain/graph.tsx +163 -0
- package/src/plugins/builtin/supply-chain/headless.ts +105 -0
- package/src/plugins/builtin/supply-chain/index.tsx +17 -0
- package/src/plugins/builtin/supply-chain/model.ts +145 -0
- package/src/plugins/builtin/supply-chain/pane.tsx +276 -0
- package/src/plugins/builtin/supply-chain/row-evidence.tsx +96 -0
- package/src/plugins/builtin/supply-chain/trust.ts +34 -0
- package/src/plugins/builtin/themes/client.ts +39 -0
- package/src/plugins/builtin/themes/headless.ts +38 -0
- package/src/plugins/builtin/themes/model.ts +92 -0
- package/src/plugins/builtin/themes/pane.tsx +125 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +6 -15
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +5 -6
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +201 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +327 -0
- package/src/plugins/builtin/ticker-detail/financials/period-end-history.ts +5 -2
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +60 -12
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/headless.ts +7 -2
- package/src/plugins/builtin/ticker-detail/index.tsx +22 -0
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +53 -30
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -0
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +112 -47
- package/src/plugins/builtin/ticker-detail/pane.tsx +50 -6
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +21 -42
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +3 -0
- package/src/plugins/builtin/time-sales/pane.tsx +1 -1
- package/src/plugins/builtin/world-indices/indices.ts +1 -0
- package/src/plugins/catalog-browser.ts +40 -0
- package/src/plugins/catalog-manifest.ts +3 -0
- package/src/plugins/catalog-ui.ts +6 -0
- package/src/plugins/current-target.ts +0 -9
- package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
- package/src/renderers/browser/cloud-transport.ts +66 -1
- package/src/renderers/browser/main.tsx +2 -0
- package/src/renderers/dom/data-table/row.tsx +2 -0
- package/src/renderers/dom/desktop/controls.tsx +38 -25
- package/src/renderers/dom/desktop/icons.tsx +8 -0
- package/src/renderers/dom/host/chart-surface.tsx +36 -2
- package/src/renderers/dom/host/focus-scope.ts +11 -1
- package/src/renderers/dom/styles.css +28 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +3 -2
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
- package/src/renderers/electrobun/view/http-fetch.ts +2 -0
- package/src/renderers/electrobun/view/main.tsx +3 -1
- package/src/renderers/electrobun/view/rpc-timeout.ts +59 -0
- package/src/renderers/opentui/start.tsx +3 -0
- package/src/shares/api.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +47 -13
- package/src/sources/history-result.ts +1 -0
- package/src/sources/history-retention.ts +16 -0
- package/src/sources/provider-router/history.ts +4 -2
- package/src/sources/sec-edgar/identity.ts +36 -0
- package/src/sources/sec-edgar.ts +1 -34
- package/src/state/app/context/index.tsx +8 -0
- package/src/sync/core-contributors.ts +20 -2
- package/src/telemetry/attention-counts.ts +164 -0
- package/src/telemetry/live-config.ts +7 -0
- package/src/test-support/rpc-loopback.ts +7 -3
- package/src/test-support/shell-pane-keys.tsx +34 -0
- package/src/tickers/instrument-kind.ts +12 -6
- package/src/tickers/open-target.ts +1 -1
- package/src/tickers/search/asset-classes.ts +91 -0
- package/src/tickers/search/index.ts +62 -15
- package/src/tickers/search/ranking.ts +22 -3
- package/src/tickers/search/upsert.ts +4 -1
- package/src/time-series/live-bars.ts +8 -3
- package/src/time-series/resolve.ts +224 -17
- package/src/time-series/spec.ts +1 -0
- package/src/time-series/types.ts +9 -1
- package/src/types/config.ts +23 -1
- package/src/types/data-provider.ts +2 -0
- package/src/types/headless.ts +17 -0
- package/src/types/price-history.ts +2 -0
- package/src/ui/host.tsx +6 -0
- package/src/utils/double-escape-close.ts +20 -0
- package/src/utils/exchanges.ts +1 -0
- package/src/utils/html-entities.ts +15 -0
- package/src/utils/http-proxy-response.ts +44 -7
- package/src/utils/plugin-proxy-hosts.json +3 -0
- package/src/utils/price-history.ts +35 -1
- package/src/utils/sec.ts +21 -2
- package/src/utils/text-wrap.ts +13 -8
- package/src/utils/zoned-date-time.ts +67 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A/B experiments on the hosted web terminal (term.gloom.sh).
|
|
3
|
+
*
|
|
4
|
+
* The registry, the bucketing and the bot rules live with the API
|
|
5
|
+
* (gloomberb-platform `shared/experiments.ts`). The terminal asks for its arm
|
|
6
|
+
* at the moment an experiment's change would show, in every arm, and that
|
|
7
|
+
* request is the exposure: the API answers with the variant and counts the
|
|
8
|
+
* visitor in it. Turning an experiment off there takes effect here without a
|
|
9
|
+
* release.
|
|
10
|
+
*
|
|
11
|
+
* The first variant this browser is given is kept and sent back, so a visitor
|
|
12
|
+
* whose anonymous id changes later stays in the same arm, and it rides on every
|
|
13
|
+
* later milestone as `experiments`, which the API turns into `exp_<key>` and
|
|
14
|
+
* saves to the account once they sign in.
|
|
15
|
+
*/
|
|
16
|
+
|
|
17
|
+
export interface StorageLike {
|
|
18
|
+
getItem(key: string): string | null;
|
|
19
|
+
setItem(key: string, value: string): void;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface ExperimentAnswer {
|
|
23
|
+
accepted: boolean;
|
|
24
|
+
variant?: string;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface ExperimentExposureRequest {
|
|
28
|
+
experiment: string;
|
|
29
|
+
/** The variant this browser was given before, if any. */
|
|
30
|
+
variant?: string;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export interface WebExperimentEnvironment {
|
|
34
|
+
/** Absent with Do Not Track or Global Privacy Control: the terminal never minted one. */
|
|
35
|
+
anonymousId: string | undefined;
|
|
36
|
+
signedIn: boolean;
|
|
37
|
+
/** A browser driven by automation (`navigator.webdriver`). */
|
|
38
|
+
automated: boolean;
|
|
39
|
+
/** Kept across visits: the first variant of each experiment. */
|
|
40
|
+
local: StorageLike;
|
|
41
|
+
/** Kept for this browser session: the experiments already counted in it. */
|
|
42
|
+
session: StorageLike;
|
|
43
|
+
ask(request: ExperimentExposureRequest): Promise<ExperimentAnswer>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export const EXPERIMENTS_STORAGE_KEY = "gloomberb.web.experiments";
|
|
47
|
+
const EXPOSED_SESSION_KEY = "gloomberb.web.experiments-exposed";
|
|
48
|
+
|
|
49
|
+
const ASSIGNMENT = /^([a-z][a-z0-9_]{0,47}):([a-z][a-z0-9_]{0,47})$/;
|
|
50
|
+
const MAX_ASSIGNMENTS_LENGTH = 500;
|
|
51
|
+
|
|
52
|
+
/** "web_terminal_trial_offer:offer,other:control", the API's format, to an ordered map. */
|
|
53
|
+
function parseExperimentAssignments(value: string | null | undefined): Map<string, string> {
|
|
54
|
+
const assignments = new Map<string, string>();
|
|
55
|
+
for (const part of (value ?? "").split(",")) {
|
|
56
|
+
const match = ASSIGNMENT.exec(part.trim());
|
|
57
|
+
if (match && !assignments.has(match[1]!)) assignments.set(match[1]!, match[2]!);
|
|
58
|
+
}
|
|
59
|
+
return assignments;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function serializeExperimentAssignments(assignments: ReadonlyMap<string, string>): string | undefined {
|
|
63
|
+
let serialized = "";
|
|
64
|
+
for (const [key, variant] of assignments) {
|
|
65
|
+
const part = `${key}:${variant}`;
|
|
66
|
+
if (!ASSIGNMENT.test(part)) continue;
|
|
67
|
+
const next = serialized ? `${serialized},${part}` : part;
|
|
68
|
+
if (next.length > MAX_ASSIGNMENTS_LENGTH) break;
|
|
69
|
+
serialized = next;
|
|
70
|
+
}
|
|
71
|
+
return serialized || undefined;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function read(storage: StorageLike, key: string): string | null {
|
|
75
|
+
try {
|
|
76
|
+
return storage.getItem(key);
|
|
77
|
+
} catch {
|
|
78
|
+
return null;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function write(storage: StorageLike, key: string, value: string): void {
|
|
83
|
+
try {
|
|
84
|
+
storage.setItem(key, value);
|
|
85
|
+
} catch {
|
|
86
|
+
/* Private browsing still runs the terminal; the arm is just not remembered. */
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/** The stored assignments as the attribution string, or undefined when there are none. */
|
|
91
|
+
export function storedExperimentAssignments(storage: StorageLike): string | undefined {
|
|
92
|
+
return serializeExperimentAssignments(parseExperimentAssignments(read(storage, EXPERIMENTS_STORAGE_KEY)));
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const pending = new Map<string, Promise<string | null>>();
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* The variant to show, or null to show nothing: the visitor is signed in, has
|
|
99
|
+
* no anonymous id, is an automated browser, was already counted in this
|
|
100
|
+
* browser session, or the API did not put them in the experiment (a bot, the
|
|
101
|
+
* experiment ended, the request failed). Call it when the change would show,
|
|
102
|
+
* in every arm.
|
|
103
|
+
*/
|
|
104
|
+
export function exposeExperiment(experiment: string, env: WebExperimentEnvironment): Promise<string | null> {
|
|
105
|
+
if (!env.anonymousId || env.signedIn || env.automated) return Promise.resolve(null);
|
|
106
|
+
if (parseSessionList(read(env.session, EXPOSED_SESSION_KEY)).has(experiment)) return Promise.resolve(null);
|
|
107
|
+
const inFlight = pending.get(experiment);
|
|
108
|
+
if (inFlight) return inFlight;
|
|
109
|
+
|
|
110
|
+
const request = (async () => {
|
|
111
|
+
const assignments = parseExperimentAssignments(read(env.local, EXPERIMENTS_STORAGE_KEY));
|
|
112
|
+
let answer: ExperimentAnswer;
|
|
113
|
+
try {
|
|
114
|
+
answer = await env.ask({ experiment, variant: assignments.get(experiment) });
|
|
115
|
+
} catch {
|
|
116
|
+
// Not counted, so the next page load in this session may ask again.
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
const exposed = parseSessionList(read(env.session, EXPOSED_SESSION_KEY));
|
|
120
|
+
exposed.add(experiment);
|
|
121
|
+
write(env.session, EXPOSED_SESSION_KEY, [...exposed].join(","));
|
|
122
|
+
if (!answer.accepted || !answer.variant || !ASSIGNMENT.test(`${experiment}:${answer.variant}`)) return null;
|
|
123
|
+
if (!assignments.has(experiment)) {
|
|
124
|
+
assignments.set(experiment, answer.variant);
|
|
125
|
+
const serialized = serializeExperimentAssignments(assignments);
|
|
126
|
+
if (serialized) write(env.local, EXPERIMENTS_STORAGE_KEY, serialized);
|
|
127
|
+
}
|
|
128
|
+
return answer.variant;
|
|
129
|
+
})().finally(() => pending.delete(experiment));
|
|
130
|
+
pending.set(experiment, request);
|
|
131
|
+
return request;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function parseSessionList(value: string | null): Set<string> {
|
|
135
|
+
return new Set((value ?? "").split(",").filter(Boolean));
|
|
136
|
+
}
|
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
import type { PluginRegistry } from "../../plugins/registry";
|
|
28
28
|
import { reportCrash } from "../../telemetry/crash-reports";
|
|
29
29
|
import { recordFunctionOpen, usageFunctionForPane } from "../../telemetry/usage-counts";
|
|
30
|
+
import { captureAttentionAction } from "../../telemetry/attention-counts";
|
|
31
|
+
import { publicTickerKey } from "../../utils/exchanges";
|
|
30
32
|
import {
|
|
31
33
|
resolveTickerNavigationReplacementPane,
|
|
32
34
|
shouldFocusTickerNavigationTarget,
|
|
@@ -252,6 +254,7 @@ export function bindAppPanePluginRegistry({
|
|
|
252
254
|
},
|
|
253
255
|
navigateTicker: (rawSymbol, options) => {
|
|
254
256
|
if (isDetachedWindow) return;
|
|
257
|
+
const recordAttention = captureAttentionAction();
|
|
255
258
|
recordFunctionOpen(usageFunctionForPane(pluginRegistry, TICKER_RESEARCH_PANE_ID));
|
|
256
259
|
const sourcePaneId = options?.sourcePaneId ?? stateRef.current.focusedPaneId;
|
|
257
260
|
const requests = tickerNavigationRequests.get(pluginRegistry) ?? new Map<string | null, symbol>();
|
|
@@ -311,12 +314,14 @@ export function bindAppPanePluginRegistry({
|
|
|
311
314
|
};
|
|
312
315
|
persistLayout(nextLayout);
|
|
313
316
|
focusIfStillOwned(detailPane.instanceId);
|
|
317
|
+
recordAttention(publicTickerKey(symbol, target.listing?.exchange ?? target.ticker.metadata.exchange), "des");
|
|
314
318
|
} else if (shouldFocusTickerNavigationTarget({
|
|
315
319
|
sourcePaneId,
|
|
316
320
|
currentFocusedPaneId: stateRef.current.focusedPaneId,
|
|
317
321
|
targetPaneId: null,
|
|
318
322
|
})) {
|
|
319
323
|
placePinnedTickerTarget(target, { floating: false });
|
|
324
|
+
recordAttention(publicTickerKey(symbol, target.listing?.exchange ?? target.ticker.metadata.exchange), "des");
|
|
320
325
|
}
|
|
321
326
|
} catch (err) {
|
|
322
327
|
if (!canPresentFeedback()) return;
|
|
@@ -24,7 +24,8 @@ import {
|
|
|
24
24
|
} from "../../tickers/open-target";
|
|
25
25
|
import { AmbiguousTickerError, findExactTickerSearchMatch } from "../../tickers/search";
|
|
26
26
|
import { isDialogOpen } from "../../ui/dialog-stack";
|
|
27
|
-
import { parsePublicTickerKey } from "../../utils/exchanges";
|
|
27
|
+
import { parsePublicTickerKey, publicTickerKey } from "../../utils/exchanges";
|
|
28
|
+
import { attentionActionForPane, captureAttentionAction } from "../../telemetry/attention-counts";
|
|
28
29
|
import { tickerHasListingSuffix } from "../../sources/listing-symbols";
|
|
29
30
|
import { instrumentFromTicker } from "../../market-data/request-types";
|
|
30
31
|
import { tickerInstrumentLabel } from "../../tickers/instrument-label";
|
|
@@ -185,13 +186,18 @@ export function useAppTickerOpenRuntime({
|
|
|
185
186
|
]);
|
|
186
187
|
|
|
187
188
|
const openPinnedTicker = useCallback(async (rawSymbol: string, options?: PinTickerOptions) => {
|
|
189
|
+
const recordAttention = captureAttentionAction();
|
|
188
190
|
const selectedTicker = options?.instrument !== undefined && (options.instrument || options.listing) ? await tickerRepository.loadTicker(rawSymbol) : null;
|
|
189
191
|
const target = selectedTicker
|
|
190
192
|
? { symbol: selectedTicker.metadata.ticker, ticker: selectedTicker, created: false, instrument: options?.instrument, listing: options?.listing }
|
|
191
193
|
: await resolveOpenTickerTarget(rawSymbol, options?.instrument === null);
|
|
192
194
|
if (!target) return;
|
|
193
195
|
placePinnedTickerTarget(target, options);
|
|
194
|
-
|
|
196
|
+
const action = attentionActionForPane(options?.paneType ?? TICKER_RESEARCH_PANE_ID);
|
|
197
|
+
if (action && pluginRegistry.panes.has(options?.paneType ?? TICKER_RESEARCH_PANE_ID)) {
|
|
198
|
+
recordAttention(publicTickerKey(target.symbol, target.listing?.exchange ?? target.ticker.metadata.exchange), action);
|
|
199
|
+
}
|
|
200
|
+
}, [placePinnedTickerTarget, pluginRegistry, resolveOpenTickerTarget, tickerRepository]);
|
|
195
201
|
|
|
196
202
|
return {
|
|
197
203
|
openPinnedTicker,
|
|
@@ -7,8 +7,10 @@ import {
|
|
|
7
7
|
checkForUpdateDetailed,
|
|
8
8
|
performUpdate,
|
|
9
9
|
type ReleaseInfo,
|
|
10
|
+
type UpdateCheckResult,
|
|
10
11
|
} from "../../updater";
|
|
11
12
|
import { VERSION } from "../../version";
|
|
13
|
+
import { reportCrash } from "../../telemetry/crash-reports";
|
|
12
14
|
import { runAutomated } from "../../telemetry/usage-counts";
|
|
13
15
|
|
|
14
16
|
const UPDATE_CHECK_INTERVAL_MS = 60 * 60_000; // hourly
|
|
@@ -48,7 +50,17 @@ export function useAppUpdateRuntime({
|
|
|
48
50
|
dispatch({ type: "SET_UPDATE_NOTICE", notice: null });
|
|
49
51
|
}
|
|
50
52
|
|
|
51
|
-
|
|
53
|
+
// The desktop host can reject (its RPC request timed out). That is a
|
|
54
|
+
// failed check like any other: the hourly one must not end as an
|
|
55
|
+
// unhandled rejection, and a manual one must clear its progress state.
|
|
56
|
+
// The timeout is still reported, as it was when it went unhandled.
|
|
57
|
+
const result = await checkForUpdateDetailed(VERSION).catch((error: unknown): UpdateCheckResult => {
|
|
58
|
+
const message = error instanceof Error ? error.message : "";
|
|
59
|
+
if (message.startsWith("RPC request timed out")) {
|
|
60
|
+
reportCrash(error, { kind: "unhandled-rejection" });
|
|
61
|
+
}
|
|
62
|
+
return { kind: "error", error: message || "Update check failed" };
|
|
63
|
+
});
|
|
52
64
|
|
|
53
65
|
if (!manual) {
|
|
54
66
|
if (result.kind === "available") {
|
|
@@ -19,6 +19,7 @@ const DOCTOR_COUNT_UNITS: Record<string, string> = { plugins: "loaded", capabili
|
|
|
19
19
|
const TELEMETRY_CONFIG_KEYS = {
|
|
20
20
|
"telemetry.crashReports": "crashReports",
|
|
21
21
|
"telemetry.usage": "usage",
|
|
22
|
+
"telemetry.attention": "attention",
|
|
22
23
|
} as const satisfies Record<string, keyof TelemetryConfig>;
|
|
23
24
|
type TelemetryConfigKey = keyof typeof TELEMETRY_CONFIG_KEYS;
|
|
24
25
|
const EDITABLE_CONFIG_KEYS = ["baseCurrency", "refreshIntervalMinutes", "theme", "valueFlashingEnabled", ...Object.keys(TELEMETRY_CONFIG_KEYS)];
|
|
@@ -195,6 +196,7 @@ export function createSystemCliCommands(): CliCommandDef[] {
|
|
|
195
196
|
valueFlashingEnabled: context.config.valueFlashingEnabled,
|
|
196
197
|
"telemetry.crashReports": context.config.telemetry?.crashReports !== false,
|
|
197
198
|
"telemetry.usage": context.config.telemetry?.usage !== false,
|
|
199
|
+
"telemetry.attention": context.config.telemetry?.attention === true,
|
|
198
200
|
};
|
|
199
201
|
|
|
200
202
|
if (action === "list") {
|
|
@@ -546,7 +546,6 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
|
|
|
546
546
|
.forEach((table, tableIndex) => {
|
|
547
547
|
const tableMetadata = semanticTables[tableIndex] && semanticTables[tableIndex].metadata || {};
|
|
548
548
|
const semanticColumns = Array.isArray(tableMetadata.columns) ? tableMetadata.columns : [];
|
|
549
|
-
const semanticRows = Array.isArray(tableMetadata.rows) ? tableMetadata.rows : [];
|
|
550
549
|
const headers = [...table.querySelectorAll('[data-gloom-role="data-table-header-cell"]')]
|
|
551
550
|
.map((cell) => normalize(cell.innerText || cell.textContent));
|
|
552
551
|
const rowElements = [...table.querySelectorAll(
|
|
@@ -555,7 +554,7 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
|
|
|
555
554
|
rowElements.forEach((row, rowIndex) => {
|
|
556
555
|
const cellElements = [...row.querySelectorAll('[data-gloom-role="data-table-cell"]')];
|
|
557
556
|
const values = cellElements.length > 0 ? cellElements : [row];
|
|
558
|
-
const
|
|
557
|
+
const rowKey = row.getAttribute("data-gloom-row-key");
|
|
559
558
|
const cells = values.map((cell, cellIndex) => {
|
|
560
559
|
const semanticColumn = semanticColumns[cellIndex] || {};
|
|
561
560
|
const text = normalize(cell.innerText || cell.textContent);
|
|
@@ -582,8 +581,8 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
|
|
|
582
581
|
rows.push({
|
|
583
582
|
tableIndex,
|
|
584
583
|
rowIndex,
|
|
585
|
-
...(
|
|
586
|
-
selected: row.getAttribute("data-selected") === "true"
|
|
584
|
+
...(rowKey ? { key: rowKey } : {}),
|
|
585
|
+
selected: row.getAttribute("data-selected") === "true",
|
|
587
586
|
cells,
|
|
588
587
|
});
|
|
589
588
|
});
|
|
@@ -9,6 +9,14 @@ import { cleanTickerInput } from "./options";
|
|
|
9
9
|
import { parsePublicTickerKey, publicTickerKey } from "../../utils/exchanges";
|
|
10
10
|
import type { ResolvedPaneFunction } from "./resolver";
|
|
11
11
|
import { toMarketDataContext } from "../../market-data/selectors";
|
|
12
|
+
import { loadSeasonalityHistory, SEASONALITY_HISTORY_RESOLUTION } from "../../plugins/builtin/seasonality/client";
|
|
13
|
+
import type { InstrumentRef } from "../../market-data/request-types";
|
|
14
|
+
import { CORRELATION_HISTORY_RESOLUTION, loadCorrelationHistory } from "../../plugins/builtin/correlation/history";
|
|
15
|
+
import type { TimeRange } from "../../time-series/range";
|
|
16
|
+
import { loadPeriodEndHistory, PERIOD_END_HISTORY_RESOLUTION } from "../../plugins/builtin/ticker-detail/financials/period-end-history";
|
|
17
|
+
import { latestFinancialPeriodEnd } from "../../plugins/builtin/ticker-detail/financials/model";
|
|
18
|
+
import { oldestRatioPeriodEnd, periodEndClose, ratioStatements } from "../../plugins/builtin/ticker-detail/financials/ratios";
|
|
19
|
+
import { shotFinancialPeriod } from "./financial-ratio-shot";
|
|
12
20
|
|
|
13
21
|
const SHOT_PRICE_HISTORY_RANGE = "5Y" as const;
|
|
14
22
|
const FINANCIAL_ANALYSIS_PANE_ID = "financial-analysis";
|
|
@@ -57,6 +65,76 @@ export async function withShotPriceHistory(
|
|
|
57
65
|
}
|
|
58
66
|
}
|
|
59
67
|
|
|
68
|
+
/**
|
|
69
|
+
* SEAS reads monthly closes. Captured history answers every request, so the
|
|
70
|
+
* weekly seed would stand in for them and price each month at a week's close.
|
|
71
|
+
* The pane's own load fetches them, tagged so no other cadence is answered.
|
|
72
|
+
*/
|
|
73
|
+
export async function withShotSeasonalityHistory(
|
|
74
|
+
context: MarketContext,
|
|
75
|
+
instrument: InstrumentRef,
|
|
76
|
+
financials: TickerFinancials,
|
|
77
|
+
): Promise<TickerFinancials> {
|
|
78
|
+
const monthly = await loadSeasonalityHistory({ instrument, forceRefresh: context.refresh }, context.dataProvider);
|
|
79
|
+
if (monthly.error || monthly.stale) {
|
|
80
|
+
throw new Error(`${instrument.symbol}: ${monthly.error ?? "monthly price history is stale"}`);
|
|
81
|
+
}
|
|
82
|
+
return { ...financials, priceHistory: monthly.history, priceHistoryResolution: SEASONALITY_HISTORY_RESOLUTION };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** Correlation and relationship panes read daily returns at every range. */
|
|
86
|
+
export function readsDailyReturns(resolved: ResolvedPaneFunction): boolean {
|
|
87
|
+
return resolved.capability.id === "return-correlation" || resolved.capability.id === "security-relationship";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* A range capture is weekly at 5Y and monthly at ALL. Captured history answers
|
|
92
|
+
* the panes' daily request, so it would turn their daily returns into weekly or
|
|
93
|
+
* monthly ones.
|
|
94
|
+
*/
|
|
95
|
+
export async function withShotDailyReturns(
|
|
96
|
+
context: MarketContext,
|
|
97
|
+
instrument: InstrumentRef,
|
|
98
|
+
exchange: string,
|
|
99
|
+
range: TimeRange,
|
|
100
|
+
financials: TickerFinancials,
|
|
101
|
+
): Promise<TickerFinancials> {
|
|
102
|
+
const priceHistory = await loadCorrelationHistory(context.dataProvider, instrument.symbol, exchange, range,
|
|
103
|
+
{ ...toMarketDataContext(instrument), ...(context.refresh ? { cacheMode: "refresh" as const } : {}) });
|
|
104
|
+
return { ...financials, priceHistory, priceHistoryResolution: CORRELATION_HISTORY_RESOLUTION };
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Valuation prices each column at its period-end close and accepts only daily
|
|
109
|
+
* bars. Captured history answers every request, so the weekly seed stood in
|
|
110
|
+
* for them and every cell read "no price". The pane's own load fetches daily
|
|
111
|
+
* closes back to its oldest column, tagged so no other cadence is answered.
|
|
112
|
+
*/
|
|
113
|
+
export async function withShotPeriodEndHistory(
|
|
114
|
+
context: MarketContext,
|
|
115
|
+
instrument: InstrumentRef,
|
|
116
|
+
exchange: string,
|
|
117
|
+
resolved: ResolvedPaneFunction,
|
|
118
|
+
financials: TickerFinancials,
|
|
119
|
+
): Promise<TickerFinancials> {
|
|
120
|
+
const statements = ratioStatements(financials, shotFinancialPeriod(resolved, financials));
|
|
121
|
+
const oldest = oldestRatioPeriodEnd(statements);
|
|
122
|
+
// Without statements the pane shows its empty state, which the shot reports.
|
|
123
|
+
if (!oldest) return financials;
|
|
124
|
+
const fail = (reason: string) => new Error(`${instrument.symbol}: ${reason}`);
|
|
125
|
+
const priceHistory = await loadPeriodEndHistory(context.dataProvider, instrument.symbol, exchange, oldest,
|
|
126
|
+
{ ...toMarketDataContext(instrument), ...(context.refresh ? { cacheMode: "refresh" as const } : {}) })
|
|
127
|
+
.catch((error: unknown) => { throw fail(error instanceof Error ? error.message : String(error)); });
|
|
128
|
+
const last = priceHistory.reduce((latest, point) => Math.max(latest, new Date(point.date).getTime() || -Infinity), -Infinity);
|
|
129
|
+
if (!Number.isFinite(last)) throw fail("daily price history is unavailable");
|
|
130
|
+
const lastClose = new Date(last).toISOString().slice(0, 10);
|
|
131
|
+
const newest = latestFinancialPeriodEnd(statements)!;
|
|
132
|
+
if (lastClose < newest && periodEndClose(priceHistory, newest) === undefined) {
|
|
133
|
+
throw fail(`daily price history is stale: the last close is ${lastClose}, before the ${newest} period end`);
|
|
134
|
+
}
|
|
135
|
+
return { ...financials, priceHistory, priceHistoryResolution: PERIOD_END_HISTORY_RESOLUTION };
|
|
136
|
+
}
|
|
137
|
+
|
|
60
138
|
export function isFinancialAnalysisFunction(resolved: ResolvedPaneFunction): boolean {
|
|
61
139
|
if (resolved.pane.id === FINANCIAL_ANALYSIS_PANE_ID) return true;
|
|
62
140
|
if (resolved.template?.id === FINANCIAL_ANALYSIS_TEMPLATE_ID) return true;
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import type { TickerFinancials } from "../../types/financials";
|
|
2
|
+
import type { DesktopPaneShotPayload, DesktopPaneShotRenderResult } from "../desktop-pane-shot";
|
|
3
|
+
import type { ResolvedPaneFunction } from "./resolver";
|
|
4
|
+
import { createValuationCurrencyContext } from "../../time-series/valuation-currency";
|
|
5
|
+
import {
|
|
6
|
+
resolveFinancialPeriod,
|
|
7
|
+
resolveFinancialPeriodOption,
|
|
8
|
+
type FinancialPeriod,
|
|
9
|
+
} from "../../plugins/builtin/ticker-detail/financials/model";
|
|
10
|
+
import { PERIOD_END_HISTORY_RESOLUTION } from "../../plugins/builtin/ticker-detail/financials/period-end-history";
|
|
11
|
+
import {
|
|
12
|
+
findRatioTab,
|
|
13
|
+
formatRatioInput,
|
|
14
|
+
formatRatioValue,
|
|
15
|
+
ratioPeriodEnd,
|
|
16
|
+
ratioTableForFinancials,
|
|
17
|
+
resolveFinancialSectionKey,
|
|
18
|
+
type RatioTabDef,
|
|
19
|
+
type RatioTableModel,
|
|
20
|
+
} from "../../plugins/builtin/ticker-detail/financials/ratios";
|
|
21
|
+
|
|
22
|
+
export interface PaneScreenshotFinancialRatioEvidence {
|
|
23
|
+
kind: "financial-ratios";
|
|
24
|
+
symbol: string;
|
|
25
|
+
statement: string;
|
|
26
|
+
period: FinancialPeriod;
|
|
27
|
+
/** The newest column with a ratio, and the close it was priced at. */
|
|
28
|
+
latest: {
|
|
29
|
+
date: string;
|
|
30
|
+
periodEnd: string;
|
|
31
|
+
price: number | null;
|
|
32
|
+
ratios: Array<{ id: string; label: string; value: number }>;
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** The ratio tab an FA shot opens, or null for a statement tab. */
|
|
37
|
+
export function shotRatioTab(resolved: ResolvedPaneFunction): RatioTabDef | null {
|
|
38
|
+
if (resolved.capability.id !== "financial-statements") return null;
|
|
39
|
+
return findRatioTab(resolveFinancialSectionKey(String(resolved.options.statement ?? ""))) ?? null;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** The period FA shows: the requested one, or the other when it has no statements. */
|
|
43
|
+
export function shotFinancialPeriod(resolved: ResolvedPaneFunction, financials: TickerFinancials): FinancialPeriod {
|
|
44
|
+
const hasAnnual = financials.annualStatements.length > 0;
|
|
45
|
+
const hasQuarterly = financials.quarterlyStatements.length > 0;
|
|
46
|
+
const requested = typeof resolved.options.period === "string" ? resolveFinancialPeriodOption(resolved.options.period) : undefined;
|
|
47
|
+
return resolveFinancialPeriod(requested ?? (hasAnnual ? "annual" : "quarterly"), hasAnnual, hasQuarterly);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
interface RatioShot {
|
|
51
|
+
symbol: string;
|
|
52
|
+
financials: TickerFinancials;
|
|
53
|
+
tab: RatioTabDef;
|
|
54
|
+
period: FinancialPeriod;
|
|
55
|
+
model: RatioTableModel;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/** The table the page computes from the captured data, with every ratio expanded. */
|
|
59
|
+
function ratioShot(resolved: ResolvedPaneFunction, payload: DesktopPaneShotPayload): RatioShot | null {
|
|
60
|
+
const tab = shotRatioTab(resolved);
|
|
61
|
+
const [symbol, financials] = payload.financials[0] ?? [];
|
|
62
|
+
if (!tab || !symbol || !financials) return null;
|
|
63
|
+
const period = shotFinancialPeriod(resolved, financials);
|
|
64
|
+
// The page answers Valuation's daily request only from a daily capture.
|
|
65
|
+
const history = tab.key === "valuation" && financials.priceHistoryResolution === PERIOD_END_HISTORY_RESOLUTION
|
|
66
|
+
? financials.priceHistory : null;
|
|
67
|
+
return { symbol, financials, tab, period, model: ratioTableForFinancials(financials, tab, period, history) };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const hasRatio = ({ model, tab }: RatioShot, index: number) => tab.ratios.some((def) => (
|
|
71
|
+
typeof model.cells.get(def.id)?.[index]?.value === "number"
|
|
72
|
+
));
|
|
73
|
+
|
|
74
|
+
/** Why the captured data cannot make a usable ratio shot, or null when it can. */
|
|
75
|
+
export function financialRatioShotGap(resolved: ResolvedPaneFunction, payload: DesktopPaneShotPayload): string | null {
|
|
76
|
+
const shot = ratioShot(resolved, payload);
|
|
77
|
+
if (!shot) return null;
|
|
78
|
+
if (shot.tab.key === "valuation") {
|
|
79
|
+
const unpriced = shot.model.periods.flatMap(({ statement, price }) => typeof price === "number" ? [] : [statement]);
|
|
80
|
+
// A close in another currency than the statements prices nothing; the pane's footer says so.
|
|
81
|
+
if (unpriced.length) {
|
|
82
|
+
return createValuationCurrencyContext(shot.financials).warning(unpriced)
|
|
83
|
+
?? `Valuation has no period-end close for ${unpriced.map(ratioPeriodEnd).join(", ")}, so those columns read "no price".`;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if (!shot.model.periods.some((_, index) => hasRatio(shot, index))) {
|
|
87
|
+
return `Every ${shot.tab.name} ratio is unreported or not meaningful.`;
|
|
88
|
+
}
|
|
89
|
+
return null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function financialRatioShotEvidence(
|
|
93
|
+
resolved: ResolvedPaneFunction,
|
|
94
|
+
payload: DesktopPaneShotPayload,
|
|
95
|
+
): PaneScreenshotFinancialRatioEvidence | null {
|
|
96
|
+
const shot = ratioShot(resolved, payload);
|
|
97
|
+
if (!shot || financialRatioShotGap(resolved, payload)) return null;
|
|
98
|
+
const index = shot.model.periods.findIndex((_, column) => hasRatio(shot, column));
|
|
99
|
+
const { statement, price } = shot.model.periods[index]!;
|
|
100
|
+
return {
|
|
101
|
+
kind: "financial-ratios",
|
|
102
|
+
symbol: shot.symbol,
|
|
103
|
+
statement: shot.tab.key,
|
|
104
|
+
period: shot.period,
|
|
105
|
+
latest: {
|
|
106
|
+
date: statement.date,
|
|
107
|
+
periodEnd: ratioPeriodEnd(statement),
|
|
108
|
+
price: typeof price === "number" ? price : null,
|
|
109
|
+
ratios: shot.tab.ratios.flatMap((def) => {
|
|
110
|
+
const value = shot.model.cells.get(def.id)![index]!.value;
|
|
111
|
+
return typeof value === "number" ? [{ id: def.id, label: def.label, value }] : [];
|
|
112
|
+
}),
|
|
113
|
+
},
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const STATEMENT_COLUMN = /^statement:(.+):(\d+)$/;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Rendered ratio cells that differ from the table the captured data computes.
|
|
121
|
+
* A page that drew "no price" or "--" where the capture has a close, or drew
|
|
122
|
+
* other periods, is not a picture of the data.
|
|
123
|
+
*/
|
|
124
|
+
export function financialRatioRenderMismatches(
|
|
125
|
+
resolved: ResolvedPaneFunction,
|
|
126
|
+
payload: DesktopPaneShotPayload,
|
|
127
|
+
render: Pick<DesktopPaneShotRenderResult, "rows">,
|
|
128
|
+
): string[] {
|
|
129
|
+
const shot = ratioShot(resolved, payload);
|
|
130
|
+
if (!shot) return [];
|
|
131
|
+
const { model, tab } = shot;
|
|
132
|
+
const rows = new Map(model.rows.map((row) => [row.id, row]));
|
|
133
|
+
const mismatches: string[] = [];
|
|
134
|
+
let compared = 0;
|
|
135
|
+
for (const rendered of render.rows) {
|
|
136
|
+
const row = rendered.key ? rows.get(rendered.key) : undefined;
|
|
137
|
+
if (!row) continue;
|
|
138
|
+
for (const cell of rendered.cells) {
|
|
139
|
+
const column = STATEMENT_COLUMN.exec(cell.columnId ?? "");
|
|
140
|
+
if (!column) continue;
|
|
141
|
+
const index = Number(column[2]);
|
|
142
|
+
const period = model.periods[index];
|
|
143
|
+
const header = cell.columnLabel.trim();
|
|
144
|
+
if (period?.statement.date !== column[1]) {
|
|
145
|
+
mismatches.push(`${tab.name} column ${header} is not a captured period`);
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
const amounts = model.cells.get(row.def.id)![index]!;
|
|
149
|
+
const expected = row.kind === "ratio"
|
|
150
|
+
? formatRatioValue(row.def.format, amounts.value)
|
|
151
|
+
: formatRatioInput(row.def.inputs[row.index]!.format, amounts.inputs[row.index]!, row.divisor);
|
|
152
|
+
compared += 1;
|
|
153
|
+
if (cell.text.trim() !== expected.trim()) {
|
|
154
|
+
mismatches.push(`${tab.name} ${row.label} ${header} reads "${cell.text.trim()}", the captured data gives "${expected.trim()}"`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
if (compared === 0) mismatches.push(`${tab.name} ratio cells are missing`);
|
|
159
|
+
return mismatches;
|
|
160
|
+
}
|