gloomberb 0.15.8 → 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 +10 -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 +187 -3
- package/src/api-client/distress.ts +164 -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 +77 -3
- package/src/api-client/normalizers.ts +31 -1
- 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 +31 -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 +205 -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-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/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/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/price-performance.tsx +31 -34
- package/src/components/ui/data-table/index.tsx +11 -8
- package/src/components/ui/display.tsx +9 -3
- package/src/components/ui/figure.tsx +94 -0
- package/src/components/ui/icon.tsx +3 -1
- package/src/components/ui/message-composer.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 +1 -0
- package/src/i18n/ja.ts +1 -0
- package/src/i18n/ko.ts +1 -0
- package/src/i18n/zh-cn.ts +1 -0
- package/src/i18n/zh-tw.ts +1 -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/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/index.tsx +8 -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 +32 -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/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-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 +3 -1
- 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/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 +4 -8
- 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/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-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/headless.ts +237 -0
- package/src/plugins/builtin/portfolio-list/index.tsx +2 -0
- package/src/plugins/builtin/portfolio-list/mutations.ts +17 -0
- 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 +5 -3
- 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 +2 -1
- 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/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 +18 -2
- 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 +111 -46
- package/src/plugins/builtin/ticker-detail/pane.tsx +50 -6
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +20 -41
- 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 +38 -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/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 +2 -0
- 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/search/asset-classes.ts +91 -0
- package/src/tickers/search/index.ts +18 -4
- package/src/tickers/search/ranking.ts +22 -3
- package/src/types/config.ts +23 -1
- package/src/types/headless.ts +17 -0
- package/src/ui/host.tsx +6 -0
- package/src/utils/double-escape-close.ts +20 -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/sec.ts +19 -1
- package/src/utils/text-wrap.ts +13 -8
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { Box, ScrollBox, Text, type InputRenderable, type ScrollBoxRenderable } from "../../../ui";
|
|
2
|
-
import { useCallback, useMemo, useRef, useState } from "react";
|
|
1
|
+
import { Box, ScrollBox, Text, TextAttributes, type InputRenderable, type ScrollBoxRenderable } from "../../../ui";
|
|
2
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
3
3
|
import { PaneStatusBody, QueryBar, usePaneFooter, usePaneNoticeFooter } from "../../../components";
|
|
4
4
|
import { handleRefreshKey } from "../../../components/data-table/table-pane";
|
|
5
|
+
import { afterLayout } from "../../../components/ui/reveal-in-scroll-box";
|
|
5
6
|
import type { PaneProps } from "../../../types/plugin";
|
|
6
7
|
import type { PluginModule } from "../plugin-module";
|
|
7
8
|
import { TICKER_RESEARCH_PANE_ID } from "../../../types/config";
|
|
8
9
|
import { colors } from "../../../theme/colors";
|
|
9
|
-
import { usePluginTickerActions } from "../../runtime";
|
|
10
|
+
import { usePluginAppActions, usePluginTickerActions } from "../../runtime";
|
|
10
11
|
import { useAppSelector, usePaneInstance, usePaneSettingValue } from "../../../state/app/context";
|
|
11
12
|
import { useChartQueries } from "../../../market-data/hooks";
|
|
12
13
|
import { getSharedMarketDataCoordinator } from "../../../market-data/coordinator";
|
|
@@ -45,12 +46,21 @@ import {
|
|
|
45
46
|
} from "./matrix/model";
|
|
46
47
|
import { SymbolLabelCell } from "./matrix/symbol-cell";
|
|
47
48
|
import { correlationHeadless, relationshipHeadless } from "./headless";
|
|
49
|
+
import { CORRELATION_HISTORY_RESOLUTION } from "./history";
|
|
50
|
+
import { buildMatrixPairHistory, clampMatrixCursor, matrixChartRows, matrixSelection, moveMatrixCursor, type MatrixCursor } from "./matrix/selection";
|
|
51
|
+
import { MatrixPairChart } from "./matrix/pair-chart";
|
|
48
52
|
|
|
49
53
|
function CorrelationMatrixPane({ focused, width, height }: PaneProps) {
|
|
50
54
|
const pane = usePaneInstance();
|
|
51
55
|
const { navigateTicker, pinTicker } = usePluginTickerActions();
|
|
56
|
+
const { createPaneFromTemplate } = usePluginAppActions();
|
|
52
57
|
const tickers = useAppSelector((state) => state.tickers);
|
|
53
|
-
const [hoveredSymbol,
|
|
58
|
+
const [{ cursor: cellCursor, hoveredSymbol }, setCursorState] = useState<{ cursor: MatrixCursor; hoveredSymbol: string | null }>({
|
|
59
|
+
cursor: { row: 0, column: 1 }, hoveredSymbol: null,
|
|
60
|
+
});
|
|
61
|
+
const hoverSymbol = useCallback((symbol: string) => {
|
|
62
|
+
setCursorState((current) => ({ ...current, hoveredSymbol: symbol }));
|
|
63
|
+
}, []);
|
|
54
64
|
const settings = useMemo(() => getCorrelationPaneSettings(pane?.settings), [pane?.settings]);
|
|
55
65
|
const [rangePreset, setRangePreset] = usePaneSettingValue<CorrelationRangePreset>("rangePreset", settings.rangePreset);
|
|
56
66
|
const [symbolsText, setSymbolsText] = usePaneSettingValue<string>("symbolsText", settings.symbolsText);
|
|
@@ -63,6 +73,7 @@ function CorrelationMatrixPane({ focused, width, height }: PaneProps) {
|
|
|
63
73
|
// Remounts the query bar so the field drops the draft Esc threw away.
|
|
64
74
|
const [queryBarRevision, setQueryBarRevision] = useState(0);
|
|
65
75
|
const matrixScrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
76
|
+
const horizontalScrollRef = useRef<ScrollBoxRenderable | null>(null);
|
|
66
77
|
|
|
67
78
|
const instruments = useMemo(() => {
|
|
68
79
|
if (settings.symbolsError) return [];
|
|
@@ -85,7 +96,7 @@ function CorrelationMatrixPane({ focused, width, height }: PaneProps) {
|
|
|
85
96
|
},
|
|
86
97
|
bufferRange: settings.rangePreset,
|
|
87
98
|
granularity: "resolution" as const,
|
|
88
|
-
resolution:
|
|
99
|
+
resolution: CORRELATION_HISTORY_RESOLUTION,
|
|
89
100
|
})),
|
|
90
101
|
[instrumentKey, settings.rangePreset],
|
|
91
102
|
);
|
|
@@ -106,6 +117,15 @@ function CorrelationMatrixPane({ focused, width, height }: PaneProps) {
|
|
|
106
117
|
|
|
107
118
|
const symbols = instruments.map((instrument) => instrument.symbol);
|
|
108
119
|
const symbolsKey = symbols.join(",");
|
|
120
|
+
const cursor = clampMatrixCursor(cellCursor, symbols.length);
|
|
121
|
+
const selection = matrixSelection(symbols, cursor);
|
|
122
|
+
const pair = selection && selection[0] !== selection[1] ? selection : null;
|
|
123
|
+
useEffect(() => {
|
|
124
|
+
setCursorState((current) => ({ ...current, cursor: clampMatrixCursor(current.cursor, symbols.length) }));
|
|
125
|
+
}, [symbolsKey]);
|
|
126
|
+
const pairHistory = useMemo(() => pair ? buildMatrixPairHistory(
|
|
127
|
+
seriesBySymbol.get(pair[0]), seriesBySymbol.get(pair[1]), settings.rangePreset,
|
|
128
|
+
) : null, [pair?.[0], pair?.[1], seriesBySymbol, settings.rangePreset]);
|
|
109
129
|
|
|
110
130
|
const matrix = useMemo(() => {
|
|
111
131
|
return buildCorrelationMatrix(symbols, seriesBySymbol);
|
|
@@ -129,34 +149,31 @@ function CorrelationMatrixPane({ focused, width, height }: PaneProps) {
|
|
|
129
149
|
navigateTicker(symbol);
|
|
130
150
|
}, [navigateTicker, pinTicker, tickers]);
|
|
131
151
|
|
|
132
|
-
/**
|
|
133
|
-
const
|
|
152
|
+
/** Hovered symbols remain a keyboard starting point; Left reaches their row cursor. */
|
|
153
|
+
const moveCursor = (key: string) => {
|
|
134
154
|
if (symbols.length === 0) return;
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
:
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
if (!scroll) return;
|
|
142
|
-
const viewportHeight = Math.max(1, scroll.viewport?.height ?? 1);
|
|
143
|
-
if (next < scroll.scrollTop) scroll.scrollTo(next);
|
|
144
|
-
else if (next + 1 > scroll.scrollTop + viewportHeight) scroll.scrollTo(next + 1 - viewportHeight);
|
|
145
|
-
}, [hoveredSymbol, symbolsKey]);
|
|
155
|
+
setCursorState((current) => {
|
|
156
|
+
const hoveredRow = current.hoveredSymbol ? symbols.indexOf(current.hoveredSymbol) : -1;
|
|
157
|
+
const start = hoveredRow >= 0 ? { row: hoveredRow, column: -1 } : current.cursor;
|
|
158
|
+
return { cursor: moveMatrixCursor(start, key, symbols.length), hoveredSymbol: null };
|
|
159
|
+
});
|
|
160
|
+
};
|
|
146
161
|
|
|
147
162
|
useShortcut((event) => {
|
|
148
163
|
if (!focused || symbolsEditing || event.defaultPrevented) return;
|
|
149
164
|
if (handleRefreshKey(event, refresh, { stopPropagation: true })) return;
|
|
150
|
-
if (isPlainKey(event, "j", "down", "k", "up")) {
|
|
165
|
+
if (isPlainKey(event, "j", "down", "k", "up", "h", "left", "l", "right")) {
|
|
151
166
|
event.preventDefault();
|
|
152
167
|
event.stopPropagation();
|
|
153
|
-
|
|
154
|
-
} else if (isPlainKey(event, "return", "enter") &&
|
|
168
|
+
moveCursor(event.name ?? event.key ?? "");
|
|
169
|
+
} else if (isPlainKey(event, "return", "enter") && selection) {
|
|
155
170
|
event.preventDefault();
|
|
156
171
|
event.stopPropagation();
|
|
157
|
-
openSymbol(hoveredSymbol);
|
|
172
|
+
if (hoveredSymbol && symbols.includes(hoveredSymbol)) openSymbol(hoveredSymbol);
|
|
173
|
+
else if (pair) createPaneFromTemplate("relationship-graph-pane", { symbols: pair, arg: formatTickerListInput(pair) });
|
|
174
|
+
else openSymbol(selection[0]);
|
|
158
175
|
}
|
|
159
|
-
});
|
|
176
|
+
}, { phase: "before", scope: "correlation:matrix" });
|
|
160
177
|
|
|
161
178
|
// Esc cancels an edit of the list instead of clearing it: the field's own
|
|
162
179
|
// Esc empties the draft, which would fall back to the default tickers.
|
|
@@ -177,7 +194,7 @@ function CorrelationMatrixPane({ focused, width, height }: PaneProps) {
|
|
|
177
194
|
registrationId: "correlation-warnings", focused,
|
|
178
195
|
notices: [...seriesBySymbol.values()].flatMap((series) => series.refreshError
|
|
179
196
|
? [`${series.symbol}: ${series.refreshError}${series.fetchedAt ? ` Retained history retrieved ${new Date(series.fetchedAt).toISOString()}.` : ""}`]
|
|
180
|
-
: []),
|
|
197
|
+
: []).concat(pair && pairHistory?.unavailable ? [`${pair.join("/")}: ${pairHistory.unavailable}`] : []),
|
|
181
198
|
});
|
|
182
199
|
|
|
183
200
|
// The ticker set and range are visible in-pane and an invalid list shows in
|
|
@@ -189,7 +206,7 @@ function CorrelationMatrixPane({ focused, width, height }: PaneProps) {
|
|
|
189
206
|
}), [settings.symbolsError, statusSummary, symbols.length]);
|
|
190
207
|
|
|
191
208
|
const clearHoveredSymbol = useCallback((symbol: string) => {
|
|
192
|
-
|
|
209
|
+
setCursorState((current) => current.hoveredSymbol === symbol ? { ...current, hoveredSymbol: null } : current);
|
|
193
210
|
}, []);
|
|
194
211
|
|
|
195
212
|
const headerBg = colors.panel;
|
|
@@ -203,6 +220,27 @@ function CorrelationMatrixPane({ focused, width, height }: PaneProps) {
|
|
|
203
220
|
// Rows are exactly as wide as the matrix (after the one-cell inset), so the
|
|
204
221
|
// zebra and hover bands stop at the last column instead of running on.
|
|
205
222
|
const matrixRowWidth = 1 + rowHeaderWidth + symbols.length * cellWidth;
|
|
223
|
+
const chartRows = pair ? matrixChartRows(width, height, symbols.length, matrixRowWidth) : 0;
|
|
224
|
+
const matrixHeight = Math.max(1, height - 1 - (chartRows > 0 ? chartRows + 1 : 0));
|
|
225
|
+
const horizontalBarRows = matrixRowWidth > width - 1 ? 1 : 0;
|
|
226
|
+
|
|
227
|
+
// Scroll after React commits a movement, including several keys in one input batch.
|
|
228
|
+
useEffect(() => afterLayout(() => {
|
|
229
|
+
const scroll = matrixScrollRef.current;
|
|
230
|
+
if (scroll) {
|
|
231
|
+
const viewportHeight = Math.max(1, scroll.viewport?.height ?? 1);
|
|
232
|
+
if (cursor.row < scroll.scrollTop) scroll.scrollTo(cursor.row);
|
|
233
|
+
else if (cursor.row + 1 > scroll.scrollTop + viewportHeight) scroll.scrollTo(cursor.row + 1 - viewportHeight);
|
|
234
|
+
}
|
|
235
|
+
const horizontal = horizontalScrollRef.current;
|
|
236
|
+
if (!horizontal) return;
|
|
237
|
+
const left = cursor.column < 0 ? 0 : 1 + rowHeaderWidth + cursor.column * cellWidth;
|
|
238
|
+
const right = left + (cursor.column < 0 ? rowHeaderWidth : cellWidth);
|
|
239
|
+
const scrollLeft = horizontal.scrollLeft ?? 0;
|
|
240
|
+
const viewportWidth = horizontal.viewport?.width || width;
|
|
241
|
+
if (left < scrollLeft) horizontal.scrollTo({ x: left, y: 0 });
|
|
242
|
+
else if (right > scrollLeft + viewportWidth) horizontal.scrollTo({ x: right - viewportWidth, y: 0 });
|
|
243
|
+
}), [cursor.row, cursor.column, matrixHeight, rowHeaderWidth, cellWidth, width]);
|
|
206
244
|
const bodyStatus = settings.symbolsError
|
|
207
245
|
? <PaneStatusBody error={settings.symbolsError} />
|
|
208
246
|
: symbols.length < 2
|
|
@@ -210,7 +248,7 @@ function CorrelationMatrixPane({ focused, width, height }: PaneProps) {
|
|
|
210
248
|
: null;
|
|
211
249
|
|
|
212
250
|
return (
|
|
213
|
-
<Box flexDirection="column" width={width} height={height}>
|
|
251
|
+
<Box flexDirection="column" width={width} height={height} overflow="hidden">
|
|
214
252
|
<QueryBar
|
|
215
253
|
key={queryBarRevision}
|
|
216
254
|
width={width}
|
|
@@ -243,67 +281,88 @@ function CorrelationMatrixPane({ focused, width, height }: PaneProps) {
|
|
|
243
281
|
/>
|
|
244
282
|
{bodyStatus ?? (
|
|
245
283
|
<>
|
|
246
|
-
{
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
284
|
+
<ScrollBox ref={horizontalScrollRef} height={matrixHeight} flexShrink={1} minHeight={Math.min(matrixHeight, symbols.length + 1, 5)} scrollX scrollY={false} focusable={false}>
|
|
285
|
+
<Box flexDirection="column" width={Math.max(width - 1, matrixRowWidth)} height={matrixHeight - horizontalBarRows}>
|
|
286
|
+
{/* Column header row */}
|
|
287
|
+
<Box flexDirection="row" paddingLeft={1} height={1} flexShrink={0} width={matrixRowWidth} backgroundColor={headerBg}>
|
|
288
|
+
<Box width={rowHeaderWidth} flexShrink={0} />
|
|
289
|
+
{symbols.map((sym) => (
|
|
290
|
+
<SymbolLabelCell
|
|
291
|
+
key={sym}
|
|
292
|
+
symbol={sym}
|
|
293
|
+
width={cellWidth}
|
|
294
|
+
align="flex-end"
|
|
295
|
+
color={colors.textDim}
|
|
296
|
+
hovered={hoveredSymbol === sym || selection?.[1] === sym}
|
|
297
|
+
onHover={hoverSymbol}
|
|
298
|
+
onLeave={clearHoveredSymbol}
|
|
299
|
+
onOpen={openSymbol}
|
|
300
|
+
/>
|
|
301
|
+
))}
|
|
302
|
+
</Box>
|
|
263
303
|
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
<Box
|
|
271
|
-
width={rowHeaderWidth}
|
|
272
|
-
flexShrink={0}
|
|
273
|
-
overflow="hidden"
|
|
274
|
-
>
|
|
275
|
-
<SymbolLabelCell
|
|
276
|
-
symbol={rowSym}
|
|
277
|
-
width={rowHeaderWidth}
|
|
278
|
-
color={rowHeaderColor(seriesBySymbol.get(rowSym)?.status ?? "loading")}
|
|
279
|
-
hovered={hoveredSymbol === rowSym}
|
|
280
|
-
onHover={setHoveredSymbol}
|
|
281
|
-
onLeave={clearHoveredSymbol}
|
|
282
|
-
onOpen={openSymbol}
|
|
283
|
-
/>
|
|
284
|
-
</Box>
|
|
285
|
-
{/* Cells */}
|
|
286
|
-
{symbols.map((colSym) => {
|
|
287
|
-
const r = matrix.results.get(pairKey(rowSym, colSym))?.correlation ?? null;
|
|
288
|
-
const cellColors = resolveHeatCellColors(r, { quiet: rowSym === colSym });
|
|
289
|
-
const text = formatCorrelation(r);
|
|
290
|
-
return (
|
|
304
|
+
{/* Matrix rows */}
|
|
305
|
+
<ScrollBox ref={matrixScrollRef} flexGrow={1} flexBasis={0} minHeight={0} scrollY scrollX={false} focusable={false}>
|
|
306
|
+
<Box flexDirection="column">
|
|
307
|
+
{symbols.map((rowSym, rowIndex) => (
|
|
308
|
+
<Box key={rowSym} flexDirection="row" paddingLeft={1} width={matrixRowWidth} backgroundColor={rowIndex % 2 === 0 ? colors.bg : undefined}>
|
|
309
|
+
{/* Row header */}
|
|
291
310
|
<Box
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
justifyContent="flex-end"
|
|
296
|
-
paddingRight={1}
|
|
297
|
-
backgroundColor={cellColors.background}
|
|
311
|
+
width={rowHeaderWidth}
|
|
312
|
+
flexShrink={0}
|
|
313
|
+
overflow="hidden"
|
|
298
314
|
>
|
|
299
|
-
<
|
|
315
|
+
<SymbolLabelCell
|
|
316
|
+
symbol={rowSym}
|
|
317
|
+
width={rowHeaderWidth}
|
|
318
|
+
color={rowHeaderColor(seriesBySymbol.get(rowSym)?.status ?? "loading")}
|
|
319
|
+
hovered={hoveredSymbol === rowSym || selection?.[0] === rowSym}
|
|
320
|
+
onHover={hoverSymbol}
|
|
321
|
+
onLeave={clearHoveredSymbol}
|
|
322
|
+
onOpen={openSymbol}
|
|
323
|
+
/>
|
|
300
324
|
</Box>
|
|
301
|
-
|
|
302
|
-
|
|
325
|
+
{/* Cells */}
|
|
326
|
+
{symbols.map((colSym, colIndex) => {
|
|
327
|
+
const r = matrix.results.get(pairKey(rowSym, colSym))?.correlation ?? null;
|
|
328
|
+
const cellColors = resolveHeatCellColors(r, { quiet: rowSym === colSym });
|
|
329
|
+
const text = formatCorrelation(r);
|
|
330
|
+
const selected = cursor.row === rowIndex && cursor.column === colIndex;
|
|
331
|
+
return (
|
|
332
|
+
<Box
|
|
333
|
+
key={colSym}
|
|
334
|
+
width={cellWidth}
|
|
335
|
+
flexShrink={0}
|
|
336
|
+
flexDirection="row"
|
|
337
|
+
justifyContent="flex-end"
|
|
338
|
+
paddingRight={1}
|
|
339
|
+
backgroundColor={cellColors.background}
|
|
340
|
+
style={{ cursor: "pointer" }}
|
|
341
|
+
onMouseDown={(event: any) => {
|
|
342
|
+
event.preventDefault?.();
|
|
343
|
+
event.stopPropagation?.();
|
|
344
|
+
setCursorState({ cursor: { row: rowIndex, column: colIndex }, hoveredSymbol: null });
|
|
345
|
+
}}
|
|
346
|
+
>
|
|
347
|
+
<Text fg={cellColors.foreground} attributes={selected ? TextAttributes.BOLD | TextAttributes.UNDERLINE : TextAttributes.NONE}>{text}</Text>
|
|
348
|
+
</Box>
|
|
349
|
+
);
|
|
350
|
+
})}
|
|
351
|
+
</Box>
|
|
352
|
+
))}
|
|
303
353
|
</Box>
|
|
304
|
-
|
|
354
|
+
</ScrollBox>
|
|
305
355
|
</Box>
|
|
306
356
|
</ScrollBox>
|
|
357
|
+
{chartRows > 0 && pair && pairHistory ? (
|
|
358
|
+
<MatrixPairChart
|
|
359
|
+
pair={pair}
|
|
360
|
+
history={pairHistory}
|
|
361
|
+
fullPeriod={matrix.results.get(pairKey(...pair))?.correlation ?? null}
|
|
362
|
+
width={width}
|
|
363
|
+
height={chartRows}
|
|
364
|
+
/>
|
|
365
|
+
) : null}
|
|
307
366
|
</>
|
|
308
367
|
)}
|
|
309
368
|
</Box>
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { useMemo } from "react";
|
|
2
|
+
import { CompositeChart } from "../../../../components";
|
|
3
|
+
import { scalarPoint, staticSeries } from "../../../../components/chart/static/series";
|
|
4
|
+
import { colors } from "../../../../theme/colors";
|
|
5
|
+
import { Box, Text } from "../../../../ui";
|
|
6
|
+
import type { buildMatrixPairHistory } from "./selection";
|
|
7
|
+
import { matrixPairRead } from "./selection";
|
|
8
|
+
|
|
9
|
+
const PANELS = [{ id: "main" }];
|
|
10
|
+
const formatValue = (value: number) => value.toFixed(2);
|
|
11
|
+
|
|
12
|
+
export function MatrixPairChart({ pair, history, fullPeriod, width, height }: {
|
|
13
|
+
pair: [string, string];
|
|
14
|
+
history: ReturnType<typeof buildMatrixPairHistory>;
|
|
15
|
+
fullPeriod: number | null;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
}) {
|
|
19
|
+
const series = useMemo(() => [
|
|
20
|
+
staticSeries(history.points.map(({ date, value }) => scalarPoint(date, value)), {
|
|
21
|
+
id: "rolling", label: `${pair[0]}/${pair[1]} (${history.window} day)`, color: "#f6c85f",
|
|
22
|
+
}),
|
|
23
|
+
staticSeries(history.points.map(({ date }) => scalarPoint(date, fullPeriod)), {
|
|
24
|
+
id: "full-period", label: "Full period", color: colors.textDim,
|
|
25
|
+
}),
|
|
26
|
+
], [history, pair[0], pair[1], fullPeriod]);
|
|
27
|
+
return (
|
|
28
|
+
<Box flexDirection="column" flexGrow={1} flexBasis={0} minHeight={0} overflow="hidden">
|
|
29
|
+
<Box flexGrow={1} flexBasis={0} minHeight={0} overflow="hidden">
|
|
30
|
+
<CompositeChart
|
|
31
|
+
series={series}
|
|
32
|
+
panels={PANELS}
|
|
33
|
+
width={width}
|
|
34
|
+
height={height}
|
|
35
|
+
focused={false}
|
|
36
|
+
navigable={false}
|
|
37
|
+
showTimeAxis
|
|
38
|
+
formatAxisValue={formatValue}
|
|
39
|
+
formatValue={formatValue}
|
|
40
|
+
emptyMessage={history.unavailable ?? "No paired history"}
|
|
41
|
+
/>
|
|
42
|
+
</Box>
|
|
43
|
+
<Box paddingX={1} height={1} flexShrink={0} overflow="hidden">
|
|
44
|
+
<Text fg={colors.textMuted} wrapMode="none">{matrixPairRead(pair, fullPeriod, history)}</Text>
|
|
45
|
+
</Box>
|
|
46
|
+
</Box>
|
|
47
|
+
);
|
|
48
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { chartTableLayout } from "../../../../components/chart-table/layout";
|
|
2
|
+
import { alignDailyCloses } from "../compute";
|
|
3
|
+
import { buildRelationshipReturns, buildRollingCorrelationPoints } from "../relationship/model";
|
|
4
|
+
import type { CorrelationRangePreset } from "../settings";
|
|
5
|
+
import type { CorrelationSeries } from "./model";
|
|
6
|
+
|
|
7
|
+
/** Column -1 is the existing symbol-row cursor, reachable from the first cell. */
|
|
8
|
+
export interface MatrixCursor { row: number; column: number }
|
|
9
|
+
|
|
10
|
+
export function clampMatrixCursor(cursor: MatrixCursor, count: number): MatrixCursor {
|
|
11
|
+
const last = Math.max(0, count - 1);
|
|
12
|
+
return {
|
|
13
|
+
row: Math.max(0, Math.min(last, cursor.row)),
|
|
14
|
+
column: Math.max(-1, Math.min(last, cursor.column)),
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function moveMatrixCursor(cursor: MatrixCursor, key: string, count: number): MatrixCursor {
|
|
19
|
+
const next = clampMatrixCursor(cursor, count);
|
|
20
|
+
if (key === "j" || key === "down") next.row++;
|
|
21
|
+
if (key === "k" || key === "up") next.row--;
|
|
22
|
+
if (key === "h" || key === "left") next.column--;
|
|
23
|
+
if (key === "l" || key === "right") next.column++;
|
|
24
|
+
return clampMatrixCursor(next, count);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function matrixSelection(symbols: readonly string[], cursor: MatrixCursor): [string, string] | null {
|
|
28
|
+
if (!symbols.length) return null;
|
|
29
|
+
const { row, column } = clampMatrixCursor(cursor, symbols.length);
|
|
30
|
+
return [symbols[row]!, symbols[column < 0 ? row : column]!];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/** The kit budget includes the read line. No compact strip: the matrix wins. */
|
|
34
|
+
export function matrixChartRows(width: number, height: number, count: number, matrixWidth: number): number {
|
|
35
|
+
const layout = chartTableLayout({
|
|
36
|
+
width, height, queryRows: 1, tableRows: count,
|
|
37
|
+
tableChromeRows: matrixWidth > width - 1 ? 2 : 1,
|
|
38
|
+
chart: { minRows: 7, strip: false },
|
|
39
|
+
});
|
|
40
|
+
return layout.mode === "full" ? layout.chartRows - 1 : 0;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function buildMatrixPairHistory(
|
|
44
|
+
left: CorrelationSeries | undefined,
|
|
45
|
+
right: CorrelationSeries | undefined,
|
|
46
|
+
range: CorrelationRangePreset,
|
|
47
|
+
) {
|
|
48
|
+
const window = range === "1M" ? 20 : 60;
|
|
49
|
+
const aligned = alignDailyCloses(left?.prices ?? [], right?.prices ?? []).map((point) => ({
|
|
50
|
+
...point, date: new Date(`${point.dateKey}T00:00:00Z`),
|
|
51
|
+
}));
|
|
52
|
+
const returns = buildRelationshipReturns(aligned);
|
|
53
|
+
const points = buildRollingCorrelationPoints(returns, window);
|
|
54
|
+
const byTime = new Map(points.map((point) => [point.date.getTime(), point.close]));
|
|
55
|
+
const values = points.map((point) => point.close);
|
|
56
|
+
const latest = byTime.get(returns.at(-1)?.date.getTime() ?? NaN) ?? null;
|
|
57
|
+
return {
|
|
58
|
+
window,
|
|
59
|
+
// Explicit gaps retain the warm-up period and zero-variance windows.
|
|
60
|
+
points: aligned.map(({ date }) => ({ date, value: byTime.get(date.getTime()) ?? null })),
|
|
61
|
+
latest,
|
|
62
|
+
min: values.length ? Math.min(...values) : null,
|
|
63
|
+
max: values.length ? Math.max(...values) : null,
|
|
64
|
+
unavailable: returns.length < window
|
|
65
|
+
? `Rolling correlation needs ${window} shared returns; ${returns.length} available.`
|
|
66
|
+
: latest === null ? `Rolling correlation unavailable: zero return variance in the latest ${window} shared returns.` : null,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function matrixPairRead(
|
|
71
|
+
pair: readonly [string, string],
|
|
72
|
+
fullPeriod: number | null,
|
|
73
|
+
history: Pick<ReturnType<typeof buildMatrixPairHistory>, "window" | "latest" | "min" | "max">,
|
|
74
|
+
): string {
|
|
75
|
+
const value = (number: number | null) => number !== null && Number.isFinite(number) ? number.toFixed(2) : "—";
|
|
76
|
+
return `${pair[0]} and ${pair[1]}: full period ${value(fullPeriod)}, now ${value(history.latest)} (${history.window} day), range ${value(history.min)} to ${value(history.max)}`;
|
|
77
|
+
}
|
|
@@ -72,7 +72,7 @@ function alignRelationshipPrices(leftPoints: PricePoint[], rightPoints: PricePoi
|
|
|
72
72
|
}));
|
|
73
73
|
}
|
|
74
74
|
|
|
75
|
-
function buildRelationshipReturns(aligned: RelationshipAlignedPoint[]): RelationshipReturnPoint[] {
|
|
75
|
+
export function buildRelationshipReturns(aligned: readonly Omit<RelationshipAlignedPoint, "ratio">[]): RelationshipReturnPoint[] {
|
|
76
76
|
const returns: RelationshipReturnPoint[] = [];
|
|
77
77
|
for (let index = 1; index < aligned.length; index++) {
|
|
78
78
|
const previous = aligned[index - 1]!;
|
|
@@ -91,7 +91,7 @@ function buildRelationshipReturns(aligned: RelationshipAlignedPoint[]): Relation
|
|
|
91
91
|
return returns;
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
function buildRollingCorrelationPoints(
|
|
94
|
+
export function buildRollingCorrelationPoints(
|
|
95
95
|
returns: RelationshipReturnPoint[],
|
|
96
96
|
windowSize: number,
|
|
97
97
|
): ProjectedChartPoint[] {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { apiClient } from "../../../api-client";
|
|
2
|
+
import type { CpiBoardPayload, CpiRow } from "../../../api-client/cpi";
|
|
3
|
+
import { ApiRequestError } from "../../../api-client/errors";
|
|
4
|
+
import { createPluginCache } from "../../../data/plugin-cache";
|
|
5
|
+
import { cachedCloudResource, loadCloudResource } from "../shared/cloud-resource";
|
|
6
|
+
|
|
7
|
+
/** What the pane says while the server has no months to serve: before the backend ships, or before its first read. */
|
|
8
|
+
export const CPI_NOT_AVAILABLE = "US consumer prices are not available yet.";
|
|
9
|
+
|
|
10
|
+
export const cpiBoardCache = createPluginCache<CpiBoardPayload>({
|
|
11
|
+
kind: "cpi-board", source: "gloom-cloud", schemaVersion: 1,
|
|
12
|
+
// Monthly data: a quarter hour is soon enough to catch a release.
|
|
13
|
+
policy: { staleMs: 15 * 60_000, expireMs: 45 * 24 * 60 * 60_000 },
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const month = (value: unknown) => typeof value === "string" && /^\d{4}-(0[1-9]|1[0-2])$/.test(value);
|
|
17
|
+
const monthOrNull = (value: unknown) => value === null || month(value);
|
|
18
|
+
const instantOrNull = (value: unknown) => value === null || typeof value === "string" && Number.isFinite(Date.parse(value));
|
|
19
|
+
const finite = (value: unknown): value is number => typeof value === "number" && Number.isFinite(value);
|
|
20
|
+
const finiteOrNull = (value: unknown) => value === null || finite(value);
|
|
21
|
+
|
|
22
|
+
function validRow(row: CpiRow): boolean {
|
|
23
|
+
return typeof row.id === "string" && typeof row.label === "string" && Number.isInteger(row.depth) && row.depth >= 0
|
|
24
|
+
&& (row.code === null || typeof row.code === "string") && (row.parent === null || typeof row.parent === "string")
|
|
25
|
+
&& typeof row.pinned === "boolean" && typeof row.derived === "boolean"
|
|
26
|
+
&& [row.weight, row.change, row.annualized3m, row.annualized6m, row.yoy, row.contribution, row.contributionYoy].every(finiteOrNull)
|
|
27
|
+
&& (row.read === null || typeof row.read === "string")
|
|
28
|
+
&& Array.isArray(row.history) && row.history.every((point) => Array.isArray(point) && month(point[0])
|
|
29
|
+
&& finiteOrNull(point[1]) && finiteOrNull(point[2]));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** A board that breaks the contract is refused whole rather than drawn with holes in it. */
|
|
33
|
+
function validateCpiBoard(payload: CpiBoardPayload): CpiBoardPayload {
|
|
34
|
+
const release = payload?.release;
|
|
35
|
+
if (!payload || payload.source !== "BLS" || !Number.isFinite(Date.parse(payload.generatedAt))
|
|
36
|
+
|| !instantOrNull(payload.retrievedAt ?? null)
|
|
37
|
+
|| !["available", "partial", "unavailable"].includes(payload.status)
|
|
38
|
+
|| !Array.isArray(payload.gaps) || !payload.gaps.every((gap) => typeof gap === "string")
|
|
39
|
+
|| !release || !monthOrNull(release.period) || !monthOrNull(release.weightsPeriod) || !monthOrNull(release.nextPeriod)
|
|
40
|
+
|| !instantOrNull(release.releasedAt) || !instantOrNull(release.nextReleaseAt)
|
|
41
|
+
|| !Array.isArray(payload.rows) || new Set(payload.rows.map((row) => row.id)).size !== payload.rows.length
|
|
42
|
+
|| !payload.rows.every(validRow)) {
|
|
43
|
+
throw new Error("The server returned an invalid consumer price board");
|
|
44
|
+
}
|
|
45
|
+
return payload;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The board, or the not-available message when the server does not serve the
|
|
50
|
+
* route yet (404), cannot read its tables yet (503) or has no months stored.
|
|
51
|
+
*/
|
|
52
|
+
export async function fetchCpiBoard(client: Pick<typeof apiClient, "getCloudCpiBoard"> = apiClient): Promise<CpiBoardPayload> {
|
|
53
|
+
let payload: CpiBoardPayload;
|
|
54
|
+
try {
|
|
55
|
+
payload = await client.getCloudCpiBoard();
|
|
56
|
+
} catch (error) {
|
|
57
|
+
if (error instanceof ApiRequestError && (error.status === 404 || error.status === 503)) throw new Error(CPI_NOT_AVAILABLE);
|
|
58
|
+
throw error;
|
|
59
|
+
}
|
|
60
|
+
const board = validateCpiBoard(payload);
|
|
61
|
+
if (board.status === "unavailable" || !board.release.period || !board.rows.length) throw new Error(CPI_NOT_AVAILABLE);
|
|
62
|
+
return board;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export const getCachedCpiBoard = () => cachedCloudResource(cpiBoardCache, "board", validateCpiBoard);
|
|
66
|
+
export const loadCpiBoard = (force = false) => loadCloudResource(cpiBoardCache, "board", () => fetchCpiBoard(), { force, validate: validateCpiBoard });
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import type { CpiRow } from "../../../api-client/cpi";
|
|
2
|
+
import type { HeadlessPaneDefinition } from "../../../types/plugin";
|
|
3
|
+
import { fetchCpiBoard } from "./client";
|
|
4
|
+
import { cpiHeaderLine, cpiMonth, cpiRowOption, cpiRows } from "./model";
|
|
5
|
+
|
|
6
|
+
/** Two decimals; a value that rounds to zero reads 0.00, never -0.00. */
|
|
7
|
+
const percent = (value: unknown) => typeof value === "number" ? (Number(value.toFixed(2)) === 0 ? "0.00" : value.toFixed(2)) : "--";
|
|
8
|
+
const weight = (value: unknown) => typeof value === "number" ? value.toFixed(3) : "--";
|
|
9
|
+
const text = (value: unknown) => typeof value === "string" ? value : "--";
|
|
10
|
+
|
|
11
|
+
/** One row per component in the release's order; numbers stay raw for JSON, rounded for text. */
|
|
12
|
+
function reportRow(row: CpiRow, indent = true) {
|
|
13
|
+
return {
|
|
14
|
+
id: row.id, component: `${indent ? " ".repeat(row.depth) : ""}${row.label}`, depth: row.depth, weight: row.weight, monthChange: row.change,
|
|
15
|
+
annualized3m: row.annualized3m, annualized6m: row.annualized6m, yearChange: row.yoy,
|
|
16
|
+
contributionMonth: row.contribution, contributionYear: row.contributionYoy, read: row.read,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const SUMMARY_COLUMNS = [
|
|
21
|
+
{ key: "component", header: "Component" },
|
|
22
|
+
{ key: "weight", header: "Weight %", align: "right" as const, format: weight },
|
|
23
|
+
{ key: "monthChange", header: "M/M % SA", align: "right" as const, format: percent },
|
|
24
|
+
{ key: "annualized3m", header: "3M ann %", align: "right" as const, format: percent },
|
|
25
|
+
{ key: "annualized6m", header: "6M ann %", align: "right" as const, format: percent },
|
|
26
|
+
{ key: "yearChange", header: "Y/Y % NSA", align: "right" as const, format: percent },
|
|
27
|
+
{ key: "contributionMonth", header: "M/M pts", align: "right" as const, format: percent },
|
|
28
|
+
{ key: "contributionYear", header: "Y/Y pts", align: "right" as const, format: percent },
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
/** The selected row's months against the headline's year over year: the chart as a table. */
|
|
32
|
+
function historyRows(row: CpiRow, headline: CpiRow | null) {
|
|
33
|
+
const headlineYear = new Map((headline?.history ?? []).map(([month, , yoy]) => [month, yoy]));
|
|
34
|
+
return [...row.history].reverse().map(([month, change, yoy]) => ({
|
|
35
|
+
month, monthChange: change, yearChange: yoy, headlineYearChange: headlineYear.get(month) ?? null,
|
|
36
|
+
}));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** `CPI` reads every component; a component argument adds its monthly history. */
|
|
40
|
+
export const cpiHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
41
|
+
shape: "bundle",
|
|
42
|
+
argument: { kind: "free-text", optional: true, placeholder: "component", description: "A component such as shelter, core, gasoline or supercore; omit for the table." },
|
|
43
|
+
options: [],
|
|
44
|
+
discovery: { aliases: ["CPI", "ECAN"], dataRequirements: ["Gloom Cloud US consumer price history"],
|
|
45
|
+
limitations: ["Monthly data, published by BLS at 08:30 ET on its release schedule",
|
|
46
|
+
"Weights and contributions are BLS's for the month; services ex shelter is computed from two BLS series",
|
|
47
|
+
"No October 2025 index was published, so changes that need it are empty"] },
|
|
48
|
+
describe: "US consumer prices by component",
|
|
49
|
+
async load(args) {
|
|
50
|
+
const data = await fetchCpiBoard();
|
|
51
|
+
const header = cpiHeaderLine(data.release) ?? "US consumer prices";
|
|
52
|
+
const headline = data.rows.find((row) => row.code === "SA0") ?? null;
|
|
53
|
+
const argument = Array.isArray(args.argument) ? args.argument.join(" ") : args.argument;
|
|
54
|
+
const metadata = { ...data, complete: data.status === "available" };
|
|
55
|
+
if (argument) {
|
|
56
|
+
const option = cpiRowOption(argument);
|
|
57
|
+
const row = option ? data.rows.find((entry) => entry.id === option.value) : null;
|
|
58
|
+
if (!row) throw new Error("Use a component such as shelter, core, gasoline or supercore");
|
|
59
|
+
return {
|
|
60
|
+
sections: [
|
|
61
|
+
{ title: header, columns: [...SUMMARY_COLUMNS, { key: "read", header: "Read", format: text }], rows: [reportRow(row, false)] },
|
|
62
|
+
{ title: `${row.label} by month to ${data.release.period ? cpiMonth(data.release.period) : "date"}`,
|
|
63
|
+
columns: [{ key: "month", header: "Month" }, { key: "monthChange", header: "M/M % SA", align: "right" as const, format: percent },
|
|
64
|
+
{ key: "yearChange", header: "Y/Y % NSA", align: "right" as const, format: percent },
|
|
65
|
+
...(headline && headline.id !== row.id ? [{ key: "headlineYearChange", header: "All items Y/Y %", align: "right" as const, format: percent }] : [])],
|
|
66
|
+
rows: historyRows(row, headline) },
|
|
67
|
+
],
|
|
68
|
+
errors: data.gaps,
|
|
69
|
+
metadata: { ...metadata, rows: [row] },
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
return {
|
|
73
|
+
sections: [{ title: header, columns: SUMMARY_COLUMNS, rows: cpiRows(data).map((row) => reportRow(row)) }],
|
|
74
|
+
errors: data.gaps,
|
|
75
|
+
metadata,
|
|
76
|
+
};
|
|
77
|
+
},
|
|
78
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { PaneTemplateCreateOptions, PaneTemplateInstanceConfig } from "../../../types/plugin";
|
|
2
|
+
import type { PluginModule } from "../plugin-module";
|
|
3
|
+
import { cpiBoardCache } from "./client";
|
|
4
|
+
import { cpiHeadless } from "./headless";
|
|
5
|
+
import { CPI_ROW_OPTIONS, cpiRowOption } from "./model";
|
|
6
|
+
import { CpiPane } from "./pane";
|
|
7
|
+
|
|
8
|
+
/** Opens on the table, or on the component typed after the mnemonic. */
|
|
9
|
+
function cpiInstance(options?: PaneTemplateCreateOptions): PaneTemplateInstanceConfig {
|
|
10
|
+
const input = options?.arg?.trim();
|
|
11
|
+
const row = cpiRowOption(input);
|
|
12
|
+
if (input && !row) throw new Error("Use a component such as shelter, core, gasoline or supercore");
|
|
13
|
+
return { placement: "floating", ...(row ? { params: { row: row.value } } : {}) };
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export const cpiModule: PluginModule = {
|
|
17
|
+
panes: [{ id: "cpi", name: "US Consumer Prices", icon: "C", component: CpiPane,
|
|
18
|
+
defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 100, height: 36 },
|
|
19
|
+
tableExport: true, headless: cpiHeadless }],
|
|
20
|
+
paneTemplates: [{
|
|
21
|
+
id: "cpi-pane", paneId: "cpi", label: "US Consumer Prices",
|
|
22
|
+
description: "The US consumer price index by component: weights, monthly, annualised and yearly changes, and contributions to the headline.",
|
|
23
|
+
keywords: ["cpi", "ecan", "inflation", "consumer prices", "bls", "core", "shelter", "supercore", "rent", "gasoline"],
|
|
24
|
+
shortcut: { prefix: "CPI", aliases: ["ECAN"], argKind: "text", argPlaceholder: "component", argOptional: true,
|
|
25
|
+
argOptions: () => CPI_ROW_OPTIONS.map(({ value, label }) => ({ value, label })) },
|
|
26
|
+
headless: cpiHeadless,
|
|
27
|
+
createInstance: (_context, options) => cpiInstance(options),
|
|
28
|
+
}],
|
|
29
|
+
setup(ctx) { cpiBoardCache.attach(ctx.persistence); },
|
|
30
|
+
dispose() { cpiBoardCache.reset(); },
|
|
31
|
+
};
|