gloomberb 0.16.4 → 0.16.6
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 +14 -91
- package/package.json +5 -1
- package/src/{plugins/builtin/cloud → account}/auth-form.tsx +9 -9
- package/src/{plugins/builtin/cloud → account}/auth-model.ts +4 -4
- package/src/account/billing.ts +57 -0
- package/src/{plugins/builtin/cloud → account}/device-signin-dialog.tsx +18 -12
- package/src/{plugins/builtin/cloud → account}/device-signin.ts +2 -2
- package/src/{plugins/builtin/cloud → account}/email-auth-attempt.ts +2 -2
- package/src/account/session.ts +16 -0
- package/src/{plugins/builtin/cloud → account}/upgrade-dialog.tsx +16 -15
- package/src/{plugins/builtin/cloud → account}/upgrade-personalization.ts +7 -7
- package/src/{plugins/builtin/cloud → account}/upgrade-tickers.ts +6 -6
- package/src/{plugins/builtin/cloud → account}/verification-panel.tsx +6 -6
- package/src/api-client/central-bank-rates.ts +6 -1
- package/src/api-client/chat.ts +26 -0
- package/src/api-client/credit-boards.ts +2 -0
- package/src/api-client/data.ts +73 -3
- package/src/api-client/earnings.ts +6 -0
- package/src/api-client/europe-energy.ts +106 -0
- package/src/api-client/futures-curve.ts +56 -0
- package/src/api-client/index.ts +13 -1
- package/src/api-client/market-discovery.ts +13 -1
- package/src/api-client/normalizers.ts +32 -0
- package/src/api-client/paths.ts +7 -0
- package/src/api-client/perps.ts +8 -0
- package/src/api-client/request.ts +9 -2
- package/src/api-client/research-activity.ts +15 -10
- package/src/api-client/socket.ts +29 -0
- package/src/api-client/themes.ts +22 -1
- package/src/api-client/types.ts +78 -2
- package/src/api-client/yield-curves.ts +8 -0
- package/src/app/global-shortcuts.ts +50 -2
- package/src/app/keybindings/actions.ts +12 -1
- package/src/app/pane-runtime/plugin-bindings.ts +2 -2
- package/src/app/pane-runtime/ticker-open-runtime.ts +16 -6
- package/src/app/runtime/desktop-deeplink.ts +10 -5
- package/src/app/runtime/startup.ts +1 -1
- package/src/app.tsx +6 -0
- package/src/architecture/core-builtin-allowlist.json +8 -26
- package/src/ask-gloom/pane-context.ts +114 -0
- package/src/ask-gloom/pane-request.ts +70 -0
- package/src/ask-gloom/pane-text.ts +201 -0
- package/src/brokers/signed-in/sign-in-dialog.tsx +11 -5
- package/src/capabilities/chart-series.ts +19 -2
- package/src/capabilities/factories.ts +1 -0
- package/src/cli/central-bank-events.ts +23 -0
- package/src/cli/command-options.ts +17 -5
- package/src/cli/commands/crypto-hints.ts +35 -2
- package/src/cli/commands/fred-series.ts +146 -0
- package/src/cli/commands/market.ts +501 -115
- package/src/cli/commands/news-hints.ts +62 -0
- package/src/cli/commands/news-search.ts +53 -0
- package/src/cli/commands/options-leaps.ts +5 -2
- package/src/cli/commands/overview.ts +132 -26
- package/src/cli/commands/remote.ts +2 -1
- package/src/cli/commands/search.ts +317 -13
- package/src/cli/commands/system.ts +55 -8
- package/src/cli/commands/ticker.ts +155 -18
- package/src/cli/context.ts +45 -14
- package/src/cli/desktop-pane-shot.ts +76 -7
- package/src/cli/earnings-calendar.ts +71 -0
- package/src/cli/errors.ts +7 -5
- package/src/cli/exit-codes.ts +8 -0
- package/src/cli/export-source.ts +58 -0
- package/src/cli/freshness.ts +18 -7
- package/src/cli/glossary.ts +483 -0
- package/src/cli/help.ts +10 -0
- package/src/cli/helpers.ts +2 -5
- package/src/cli/history-rows.ts +118 -7
- package/src/cli/index-roots.ts +7 -0
- package/src/cli/index.ts +29 -9
- package/src/cli/known-gaps.ts +84 -0
- package/src/cli/listing-arg.ts +551 -97
- package/src/cli/not-a-ticker.ts +24 -9
- package/src/cli/pane-functions/access-gate.ts +13 -2
- package/src/cli/pane-functions/capabilities.ts +4 -3
- package/src/cli/pane-functions/catalog.ts +214 -40
- package/src/cli/pane-functions/data.ts +42 -8
- package/src/cli/pane-functions/discovery.ts +1 -0
- package/src/cli/pane-functions/dom.ts +37 -8
- package/src/cli/pane-functions/freshness.ts +393 -49
- package/src/cli/pane-functions/function-help.ts +31 -15
- package/src/cli/pane-functions/headless.ts +98 -30
- package/src/cli/pane-functions/index.ts +101 -19
- package/src/cli/pane-functions/market-session.ts +22 -8
- package/src/cli/pane-functions/options.ts +71 -22
- package/src/cli/pane-functions/report-notices.ts +25 -7
- package/src/cli/pane-functions/report.ts +15 -8
- package/src/cli/pane-functions/resolver.ts +74 -19
- package/src/cli/pane-functions/screenshot.ts +250 -27
- package/src/cli/perpetual-contract.ts +60 -0
- package/src/cli/registry.ts +3 -1
- package/src/cli/report-tables.ts +26 -7
- package/src/cli/result.ts +45 -11
- package/src/cli/series-search.ts +111 -0
- package/src/cli/staged-tickers.ts +52 -0
- package/src/cli/symbol-args.ts +27 -0
- package/src/cli/symbol-list.ts +154 -0
- package/src/cli/symbol-rows.ts +154 -0
- package/src/cli/ticker-resolution.ts +311 -27
- package/src/cli/types.ts +5 -0
- package/src/components/chart/composite/axis-overlays.tsx +14 -3
- package/src/components/chart/composite/composite-chart.tsx +71 -11
- package/src/components/chart/composite/crosshair-readout.tsx +150 -0
- package/src/components/chart/composite/downsample.ts +1 -1
- package/src/components/chart/composite/format.ts +29 -0
- package/src/components/chart/composite/interactions.ts +9 -2
- package/src/components/chart/composite/legend.tsx +100 -33
- package/src/components/chart/composite/panel-bitmap.ts +70 -35
- package/src/components/chart/composite/panel-labels.ts +18 -17
- package/src/components/chart/composite/panel-series.ts +1 -0
- package/src/components/chart/composite/panel-surface.tsx +46 -21
- package/src/components/chart/composite/price-axis-labels.tsx +3 -1
- package/src/components/chart/composite/rasterizer.ts +50 -8
- package/src/components/chart/composite/scene.ts +2 -1
- package/src/components/chart/composite/time-scale.ts +26 -9
- package/src/components/chart/composite/types.ts +18 -0
- package/src/components/chart/composite/use-composite-viewport.ts +30 -2
- package/src/components/chart/native/raster/crosshair.ts +8 -3
- package/src/components/chart/native/raster/primitives.ts +85 -21
- package/src/components/chart/native/surface/manager.ts +33 -8
- package/src/components/chart/surface3d/chart.tsx +1 -1
- package/src/components/command-bar/action-runtime.ts +1 -1
- package/src/components/command-bar/commands/collection-list.ts +210 -0
- package/src/components/command-bar/commands/collection.ts +40 -1
- package/src/components/command-bar/commands/direct/index.ts +4 -0
- package/src/components/command-bar/commands/registry.ts +8 -0
- package/src/components/command-bar/panel/index.tsx +11 -3
- package/src/components/command-bar/panel/runtime.ts +3 -0
- package/src/components/command-bar/panel/types.ts +2 -0
- package/src/components/command-bar/routes/root/plugin-install.ts +4 -4
- package/src/components/command-bar/routes/root/provider-search.ts +4 -3
- package/src/components/command-bar/routes/ticker-search/results.ts +12 -11
- package/src/components/command-bar/routes/ticker-search/route.ts +4 -3
- package/src/components/command-bar/routing/actions.ts +1 -1
- package/src/components/command-bar/workflow/tickers.ts +42 -17
- package/src/components/layout/detached-pane-shell.tsx +68 -39
- package/src/components/layout/floating-pane.tsx +10 -1
- package/src/components/layout/header.tsx +25 -5
- package/src/components/layout/missing-pane.tsx +3 -3
- package/src/components/layout/pane/capture-stamp.ts +41 -0
- package/src/components/layout/pane/capture-status.ts +12 -0
- package/src/components/layout/pane/content.tsx +7 -13
- package/src/components/layout/pane/footer/index.tsx +6 -2
- package/src/components/layout/pane/footer/keyboard.ts +3 -0
- package/src/components/layout/pane/footer/model.ts +41 -1
- package/src/components/layout/pane/header.tsx +50 -1
- package/src/components/layout/pane/index.tsx +21 -3
- package/src/components/layout/pane/sidebar.tsx +88 -21
- package/src/components/layout/shell/active-drag.ts +21 -5
- package/src/components/layout/shell/cursor-occlusion.ts +2 -1
- package/src/components/layout/shell/drag/index.ts +39 -9
- package/src/components/layout/shell/drag/live.ts +61 -51
- package/src/components/layout/shell/drag/runtime.ts +54 -12
- package/src/components/layout/shell/index.tsx +84 -65
- package/src/components/layout/shell/menu.ts +14 -1
- package/src/components/layout/shell/native/pointer-runtime.ts +3 -1
- package/src/components/layout/shell/pane/actions.ts +32 -10
- package/src/components/layout/shell/pane/ask.ts +131 -0
- package/src/components/layout/shell/pane/capture.ts +162 -0
- package/src/components/layout/shell/pane/layers.tsx +218 -50
- package/src/components/layout/shell/pane/management-shortcuts.ts +46 -1
- package/src/components/layout/shell/shortcuts.ts +21 -4
- package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -0
- package/src/components/markdown-text.tsx +62 -20
- package/src/components/metric-treemap/heat-surface.tsx +8 -1
- package/src/components/onboarding/account-step/form-panel.tsx +1 -1
- package/src/components/onboarding/account-step/index.tsx +1 -1
- package/src/components/onboarding/account-step/qr-panel.tsx +1 -1
- package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
- package/src/components/onboarding/onboarding-keyboard.ts +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +2 -2
- package/src/components/onboarding/use-onboarding-upgrade.ts +3 -3
- package/src/components/onboarding/wizard-account.ts +2 -2
- package/src/components/pane-template-wizard.tsx +9 -1
- package/src/components/price-selector-dialog.tsx +11 -2
- package/src/components/reader/article-reader.tsx +642 -0
- package/src/components/reader/article-text.ts +527 -0
- package/src/components/sign-in-gate.tsx +3 -3
- package/src/components/ticker/list-table-view.tsx +43 -2
- package/src/components/ui/action-row.tsx +2 -1
- package/src/components/ui/button.tsx +7 -1
- package/src/components/ui/choice-dialog.tsx +6 -4
- package/src/components/ui/confirm-dialog.tsx +28 -18
- package/src/components/ui/data-table/index.tsx +18 -6
- package/src/components/ui/data-table/opentui/index.tsx +37 -6
- package/src/components/ui/data-table/types.ts +12 -0
- package/src/components/ui/dialog-actions.tsx +64 -0
- package/src/components/ui/display.tsx +17 -3
- package/src/components/ui/frame.tsx +35 -2
- package/src/components/ui/icon.tsx +6 -1
- package/src/components/ui/query-bar.tsx +10 -1
- package/src/components/ui/text-prompt-dialog.tsx +11 -9
- package/src/data/cached-query.ts +2 -0
- package/src/data/config/store/migrations.ts +7 -0
- package/src/data/config/store/normalize.ts +7 -2
- package/src/data/config/store/portfolio-as-of-column.ts +57 -0
- package/src/data/ticker-repository.ts +5 -0
- package/src/data/ticker-store.ts +15 -0
- package/src/docs/content.ts +215 -0
- package/src/docs/load.ts +139 -0
- package/src/docs/requests.ts +39 -0
- package/src/docs/snapshot.chunk.ts +8 -0
- package/src/docs/snapshot.json +7555 -0
- package/src/docs/topics.ts +216 -0
- package/src/i18n/es.ts +65 -5
- package/src/i18n/ja.ts +65 -5
- package/src/i18n/ko.ts +65 -5
- package/src/i18n/zh-cn.ts +65 -5
- package/src/i18n/zh-tw.ts +65 -5
- package/src/layout/desks.ts +1 -1
- package/src/layout/pane-manager/dock-tree.ts +18 -0
- package/src/layout/pane-manager/index.ts +1 -0
- package/src/layout-marketplace/gallery.tsx +1 -1
- package/src/market-data/coordinator/chart.ts +10 -4
- package/src/market-data/coordinator/index.ts +6 -3
- package/src/{plugins/builtin/futures/contracts.ts → market-data/futures-board.ts} +81 -28
- package/src/market-data/futures-contract.ts +151 -0
- package/src/market-data/hooks.tsx +10 -2
- package/src/market-data/market/format.ts +39 -21
- package/src/market-data/market/freshness.ts +84 -20
- package/src/market-data/market/session-close-note.ts +3 -2
- package/src/market-data/market/status.ts +19 -23
- package/src/market-data/published-apac-sessions.ts +23 -1
- package/src/market-data/quotes/contributions.ts +5 -0
- package/src/market-data/quotes/metadata.ts +19 -24
- package/src/market-data/quotes/resolution.ts +5 -0
- package/src/market-data/quotes/screener-live-quotes.ts +5 -4
- package/src/market-data/result-types.ts +2 -0
- package/src/notifications/app-notifier.ts +17 -1
- package/src/notifications/notification-log.ts +180 -0
- package/src/plugins/auto-update-node.ts +2 -2
- package/src/plugins/auto-update.ts +2 -2
- package/src/plugins/builtin/account-management/index.tsx +1 -0
- package/src/plugins/builtin/account-management/model.ts +1 -56
- package/src/plugins/builtin/account-management/pane.tsx +6 -4
- package/src/plugins/builtin/account-management/password-dialog.tsx +1 -1
- package/src/plugins/builtin/alerts/catalysts.ts +1 -1
- package/src/plugins/builtin/alerts/cli.ts +313 -47
- package/src/plugins/builtin/alerts/events-pane.tsx +45 -7
- package/src/plugins/builtin/alerts/events.ts +23 -2
- package/src/plugins/builtin/alerts/focus.ts +27 -0
- package/src/plugins/builtin/alerts/history-pane.tsx +7 -2
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +47 -11
- package/src/plugins/builtin/alerts/pane.tsx +27 -1
- package/src/plugins/builtin/alerts/research-builder.ts +8 -3
- package/src/plugins/builtin/alerts/research-rules.ts +18 -2
- package/src/plugins/builtin/alerts/scheduled-check.ts +122 -0
- package/src/plugins/builtin/alerts/scheduled-sources.ts +25 -0
- package/src/plugins/builtin/alerts/scheduled.ts +254 -0
- package/src/plugins/builtin/alerts/targets.ts +92 -0
- package/src/plugins/builtin/analytics/index.tsx +8 -3
- package/src/plugins/builtin/analytics/risk-headless.ts +60 -27
- package/src/plugins/builtin/analytics/risk-math.ts +91 -27
- package/src/plugins/builtin/analytics/risk-model.ts +35 -5
- package/src/plugins/builtin/analytics/risk-pane.tsx +15 -8
- package/src/plugins/builtin/attention/headless.ts +1 -1
- package/src/plugins/builtin/attention/index.tsx +2 -0
- package/src/plugins/builtin/attention/pane.tsx +3 -1
- package/src/plugins/builtin/attention/series.ts +26 -0
- package/src/plugins/builtin/awards/headless.ts +88 -18
- package/src/plugins/builtin/awards/index.tsx +2 -0
- package/src/plugins/builtin/awards/model.ts +41 -1
- package/src/plugins/builtin/awards/pane.tsx +22 -13
- package/src/plugins/builtin/awards/series.ts +29 -0
- package/src/plugins/builtin/backtest/headless.ts +2 -2
- package/src/plugins/builtin/bond-calculator/model.ts +2 -1
- package/src/plugins/builtin/broker-manager/index.tsx +1 -0
- package/src/plugins/builtin/catalysts/headless.ts +1 -1
- package/src/plugins/builtin/catalysts/pane.tsx +3 -1
- package/src/plugins/builtin/cds/client.ts +49 -12
- package/src/plugins/builtin/cds/headless.ts +119 -19
- package/src/plugins/builtin/cds/index.tsx +3 -1
- package/src/plugins/builtin/cds/model.ts +214 -32
- package/src/plugins/builtin/cds/pane.tsx +58 -14
- package/src/plugins/builtin/cds/series.ts +19 -0
- package/src/plugins/builtin/central-bank-rates/client.ts +4 -0
- package/src/plugins/builtin/central-bank-rates/headless.ts +46 -16
- package/src/plugins/builtin/central-bank-rates/index.tsx +3 -1
- package/src/plugins/builtin/central-bank-rates/model.ts +99 -0
- package/src/plugins/builtin/central-bank-rates/pane.tsx +1 -1
- package/src/plugins/builtin/central-bank-rates/series.ts +36 -0
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +193 -255
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +84 -46
- package/src/plugins/builtin/chart-composer/editor.tsx +5 -4
- package/src/plugins/builtin/chart-composer/field-series.ts +40 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +1 -1
- package/src/plugins/builtin/chart-composer/headless.ts +70 -5
- package/src/plugins/builtin/chart-composer/index.tsx +15 -3
- package/src/plugins/builtin/chart-composer/pane.tsx +81 -38
- package/src/plugins/builtin/chart-composer/series-catalog.ts +58 -50
- package/src/plugins/builtin/chart-composer/series-expression.ts +18 -10
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +59 -3
- package/src/plugins/builtin/chart-composer/user-series.ts +84 -0
- package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
- package/src/plugins/builtin/chat/content/composer.tsx +5 -1
- package/src/plugins/builtin/chat/content/footer.tsx +12 -0
- package/src/plugins/builtin/chat/content/index.tsx +89 -5
- package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +10 -2
- package/src/plugins/builtin/chat/content/new-messages.tsx +89 -0
- package/src/plugins/builtin/chat/content/row-activity.ts +132 -0
- package/src/plugins/builtin/chat/content/scroll.ts +127 -27
- package/src/plugins/builtin/chat/content/shortcuts.ts +2 -2
- package/src/plugins/builtin/chat/content/transcript.tsx +145 -78
- package/src/plugins/builtin/chat/content/types.ts +1 -0
- package/src/plugins/builtin/chat/controller/fetch.ts +8 -2
- package/src/plugins/builtin/chat/controller/index.ts +123 -1
- package/src/plugins/builtin/chat/controller/message-loading.ts +3 -0
- package/src/plugins/builtin/chat/controller/messages.ts +7 -2
- package/src/plugins/builtin/chat/controller/realtime.ts +10 -1
- package/src/plugins/builtin/chat/controller/send.ts +61 -1
- package/src/plugins/builtin/chat/controller/state.ts +8 -0
- package/src/plugins/builtin/chat/ghost-user.ts +20 -0
- package/src/plugins/builtin/chat/index.tsx +23 -5
- package/src/plugins/builtin/chat/layout.ts +78 -5
- package/src/plugins/builtin/chat/message/ai-badge.tsx +18 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +55 -6
- package/src/plugins/builtin/chat/message/header.tsx +16 -4
- package/src/plugins/builtin/chat/message/profile-popover.tsx +5 -1
- package/src/plugins/builtin/chat/message/reactions.tsx +210 -0
- package/src/plugins/builtin/chat/message/render-state.ts +18 -2
- package/src/plugins/builtin/chat/message/terminal.tsx +43 -4
- package/src/plugins/builtin/chat/message/types.ts +12 -0
- package/src/plugins/builtin/chat/reactions.ts +197 -0
- package/src/plugins/builtin/chat/share/flow.tsx +87 -0
- package/src/plugins/builtin/chat/share/targets.ts +27 -0
- package/src/plugins/builtin/chat/status-widget.tsx +6 -3
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +148 -27
- package/src/plugins/builtin/chat/unread-inbox.ts +22 -0
- package/src/plugins/builtin/cloud/askg/attached-pane.tsx +69 -0
- package/src/plugins/builtin/cloud/askg/controller.ts +111 -19
- package/src/plugins/builtin/cloud/askg/executor.ts +57 -3
- package/src/plugins/builtin/cloud/askg/manifest.ts +32 -7
- package/src/plugins/builtin/cloud/askg/model.ts +27 -8
- package/src/plugins/builtin/cloud/askg/pane-attachment.ts +94 -0
- package/src/plugins/builtin/cloud/askg/pane.tsx +141 -12
- package/src/plugins/builtin/cloud/askg/pending-question.ts +11 -0
- package/src/plugins/builtin/cloud/askg/protocol.ts +10 -1
- package/src/plugins/builtin/cloud/askg/tool-display.ts +3 -0
- package/src/plugins/builtin/cloud/askg/undo.ts +2 -0
- package/src/plugins/builtin/cloud/auth-actions.tsx +1 -1
- package/src/plugins/builtin/cloud/auth-commands.ts +1 -1
- package/src/plugins/builtin/cloud/auth-dialog.tsx +3 -3
- package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
- package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +3 -0
- package/src/plugins/builtin/cloud/plugin.tsx +19 -2
- package/src/plugins/builtin/cloud/team/model.ts +5 -0
- package/src/plugins/builtin/cloud/team/module.tsx +1 -0
- package/src/plugins/builtin/cloud/team/store.ts +8 -3
- package/src/plugins/builtin/cloud/terminal-relay/engine.ts +77 -9
- package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +6 -1
- package/src/plugins/builtin/cloud/terminal-relay/policy.ts +9 -0
- package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +35 -11
- package/src/plugins/builtin/cloud/thesis/exposure.ts +1 -1
- package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
- package/src/plugins/builtin/cloud/thesis/prompts.tsx +2 -1
- package/src/plugins/builtin/company-attention/app-rank.tsx +1 -1
- package/src/plugins/builtin/company-attention/headless.ts +21 -7
- package/src/plugins/builtin/company-attention/index.tsx +2 -0
- package/src/plugins/builtin/company-attention/model.ts +26 -7
- package/src/plugins/builtin/company-attention/pane.tsx +12 -9
- package/src/plugins/builtin/company-attention/series.ts +52 -0
- package/src/plugins/builtin/company-kpis/headless.ts +97 -10
- package/src/plugins/builtin/company-kpis/model.ts +24 -2
- package/src/plugins/builtin/company-kpis/pane.tsx +17 -16
- package/src/plugins/builtin/composite-plugins.ts +19 -13
- package/src/plugins/builtin/connections/index.ts +1 -0
- package/src/plugins/builtin/cot/headless.ts +1 -1
- package/src/plugins/builtin/cot/index.tsx +3 -1
- package/src/plugins/builtin/cot/model.ts +40 -11
- package/src/plugins/builtin/cot/series.ts +27 -0
- package/src/plugins/builtin/cpi/headless.ts +3 -3
- package/src/plugins/builtin/credit-boards/cdx-pane.tsx +10 -1
- package/src/plugins/builtin/credit-boards/client.ts +6 -2
- package/src/plugins/builtin/credit-boards/headless.ts +108 -31
- package/src/plugins/builtin/credit-boards/index.tsx +2 -0
- package/src/plugins/builtin/credit-boards/model.ts +11 -1
- package/src/plugins/builtin/credit-boards/series.ts +66 -0
- package/src/plugins/builtin/credit-conditions/client.ts +12 -9
- package/src/plugins/builtin/credit-conditions/headless.ts +13 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +18 -9
- package/src/plugins/builtin/credit-conditions/model.ts +19 -11
- package/src/plugins/builtin/credit-conditions/series.ts +16 -0
- package/src/plugins/builtin/credit-documents/headless.ts +33 -3
- package/src/plugins/builtin/credit-documents/pane.tsx +3 -1
- package/src/plugins/builtin/crypto-board/headless.ts +2 -3
- package/src/plugins/builtin/crypto-board/index.tsx +2 -0
- package/src/plugins/builtin/crypto-board/model.ts +12 -0
- package/src/plugins/builtin/crypto-board/pane.tsx +19 -12
- package/src/plugins/builtin/crypto-board/series.ts +48 -0
- package/src/plugins/builtin/custom-view/live-quotes.ts +2 -2
- package/src/plugins/builtin/debt-maturities/headless.ts +17 -11
- package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
- package/src/plugins/builtin/debt-maturities/series.ts +45 -0
- package/src/plugins/builtin/dividend-yield/client.ts +3 -0
- package/src/plugins/builtin/dividend-yield/headless.ts +7 -4
- package/src/plugins/builtin/dividend-yield/index.tsx +3 -1
- package/src/plugins/builtin/dividend-yield/pane.tsx +3 -1
- package/src/plugins/builtin/dividend-yield/series.ts +20 -0
- package/src/plugins/builtin/dividend-yield/types.ts +2 -0
- package/src/plugins/builtin/docs/blocks.tsx +181 -0
- package/src/plugins/builtin/docs/index.tsx +68 -0
- package/src/plugins/builtin/docs/pane.tsx +426 -0
- package/src/plugins/builtin/docs/targets.ts +72 -0
- package/src/plugins/builtin/docs/use-docs.ts +60 -0
- package/src/plugins/builtin/doe/headless.ts +12 -8
- package/src/plugins/builtin/doe/index.tsx +2 -0
- package/src/plugins/builtin/doe/series.ts +62 -0
- package/src/plugins/builtin/earnings/client.ts +3 -2
- package/src/plugins/builtin/earnings/format.ts +1 -5
- package/src/plugins/builtin/earnings/headless.ts +58 -6
- package/src/plugins/builtin/earnings/history-model.ts +15 -1
- package/src/plugins/builtin/earnings/history-table.ts +13 -2
- package/src/plugins/builtin/earnings/history.tsx +7 -3
- package/src/plugins/builtin/earnings/index.tsx +9 -0
- package/src/plugins/builtin/earnings-calls/headless.ts +28 -2
- package/src/plugins/builtin/earnings-calls/index.tsx +1 -1
- package/src/plugins/builtin/earnings-ripple/index.tsx +2 -0
- package/src/plugins/builtin/econ/fred-catalog.ts +198 -0
- package/src/plugins/builtin/econ/fred-series-map.ts +0 -46
- package/src/plugins/builtin/econ/index.tsx +2 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +5 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +19 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +1 -0
- package/src/plugins/builtin/equity-screener/headless.ts +3 -3
- package/src/plugins/builtin/equity-screener/pane.tsx +4 -11
- package/src/plugins/builtin/estimate-revisions/headless.ts +3 -3
- package/src/plugins/builtin/exposure/drivers.ts +13 -0
- package/src/plugins/builtin/exposure/headless.ts +205 -13
- package/src/plugins/builtin/exposure/holdings.ts +1 -1
- package/src/plugins/builtin/exposure/index.tsx +1 -0
- package/src/plugins/builtin/exposure/model.ts +23 -3
- package/src/plugins/builtin/exposure/pane.tsx +14 -12
- package/src/plugins/builtin/fear-greed/index.tsx +18 -10
- package/src/plugins/builtin/fear-greed/series.ts +52 -0
- package/src/plugins/builtin/futures/index.tsx +43 -3
- package/src/plugins/builtin/futures/model.ts +1 -1
- package/src/plugins/builtin/futures/returns.ts +1 -1
- package/src/plugins/builtin/futures/series.ts +13 -0
- package/src/plugins/builtin/futures/table.tsx +1 -1
- package/src/plugins/builtin/futures/use-front-returns.ts +1 -1
- package/src/plugins/builtin/futures-curve/client.ts +12 -3
- package/src/plugins/builtin/futures-curve/headless.ts +71 -10
- package/src/plugins/builtin/futures-curve/index.tsx +1 -1
- package/src/plugins/builtin/futures-curve/model.ts +66 -11
- package/src/plugins/builtin/futures-curve/pane.tsx +52 -18
- package/src/plugins/builtin/fx-matrix/client.ts +63 -0
- package/src/plugins/builtin/fx-matrix/export.ts +2 -1
- package/src/plugins/builtin/fx-matrix/headless.ts +154 -0
- package/src/plugins/builtin/fx-matrix/index.tsx +4 -4
- package/src/plugins/builtin/gpu/board.tsx +13 -10
- package/src/plugins/builtin/gpu/changes.tsx +4 -4
- package/src/plugins/builtin/gpu/client.ts +19 -1
- package/src/plugins/builtin/gpu/equities.tsx +14 -10
- package/src/plugins/builtin/gpu/headless.ts +58 -27
- package/src/plugins/builtin/gpu/history.tsx +3 -3
- package/src/plugins/builtin/gpu/index.tsx +2 -0
- package/src/plugins/builtin/gpu/model.ts +101 -16
- package/src/plugins/builtin/gpu/pane.tsx +28 -12
- package/src/plugins/builtin/gpu/series.ts +38 -0
- package/src/plugins/builtin/help/function-card.tsx +25 -6
- package/src/plugins/builtin/help/function-index.ts +2 -0
- package/src/plugins/builtin/help/index.tsx +31 -9
- package/src/plugins/builtin/holders/beneficial-model.ts +90 -3
- package/src/plugins/builtin/holders/beneficial-report.ts +57 -5
- package/src/plugins/builtin/holders/beneficial-view.tsx +55 -11
- package/src/plugins/builtin/holders/client.ts +22 -3
- package/src/plugins/builtin/holders/format.ts +18 -3
- package/src/plugins/builtin/holders/headless.ts +36 -133
- package/src/plugins/builtin/holders/pane.tsx +11 -4
- package/src/plugins/builtin/holders/report-header.ts +40 -5
- package/src/plugins/builtin/holders/report-rows.ts +240 -0
- package/src/plugins/builtin/insider/client.ts +35 -20
- package/src/plugins/builtin/insider/headless.ts +41 -11
- package/src/plugins/builtin/insider/index.tsx +26 -9
- package/src/plugins/builtin/insider/insider-data.ts +10 -0
- package/src/plugins/builtin/insider/issuer.ts +49 -0
- package/src/plugins/builtin/insider/model.ts +3 -0
- package/src/plugins/builtin/ipo-calendar/headless.ts +1 -1
- package/src/plugins/builtin/iv-history/client.ts +20 -6
- package/src/plugins/builtin/iv-history/format.ts +9 -0
- package/src/plugins/builtin/iv-history/headless.ts +56 -21
- package/src/plugins/builtin/iv-history/index.tsx +2 -0
- package/src/plugins/builtin/iv-history/model.ts +143 -8
- package/src/plugins/builtin/iv-history/screen-pane.tsx +5 -9
- package/src/plugins/builtin/iv-history/series.ts +29 -0
- package/src/plugins/builtin/jobs/headless.ts +26 -3
- package/src/plugins/builtin/jobs/index.tsx +3 -1
- package/src/plugins/builtin/jobs/series.ts +19 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +2 -1
- package/src/plugins/builtin/kelly-sizer/portfolio.ts +0 -39
- package/src/plugins/builtin/macro-day/client.ts +44 -3
- package/src/plugins/builtin/macro-day/headless.ts +13 -10
- package/src/plugins/builtin/macro-day/model.ts +56 -10
- package/src/plugins/builtin/macro-day/pane.tsx +30 -20
- package/src/plugins/builtin/market-heatmap/headless.ts +20 -5
- package/src/plugins/builtin/market-heatmap/index.tsx +6 -0
- package/src/plugins/builtin/market-heatmap/model.ts +19 -11
- package/src/plugins/builtin/market-heatmap/portfolio.ts +2 -2
- package/src/plugins/builtin/market-movers/client.ts +7 -5
- package/src/plugins/builtin/market-movers/index.tsx +3 -2
- package/src/plugins/builtin/market-movers/model.ts +30 -0
- package/src/plugins/builtin/market-movers/screener.ts +31 -0
- package/src/plugins/builtin/market-valuation/chart-series.ts +29 -69
- package/src/plugins/builtin/market-valuation/index.tsx +3 -3
- package/src/plugins/builtin/market-valuation/indicators.ts +0 -5
- package/src/plugins/builtin/members/headless.ts +8 -3
- package/src/plugins/builtin/members/model.ts +31 -3
- package/src/plugins/builtin/members/pane.tsx +6 -8
- package/src/plugins/builtin/mna/distress/headless.ts +4 -4
- package/src/plugins/builtin/mna/headless.ts +18 -6
- package/src/plugins/builtin/money-markets/headless.ts +5 -4
- package/src/plugins/builtin/money-markets/index.tsx +2 -0
- package/src/plugins/builtin/money-markets/series.ts +41 -0
- package/src/plugins/builtin/news/index.tsx +0 -2
- package/src/plugins/builtin/news/wire/news/detail-view.tsx +53 -104
- package/src/plugins/builtin/news/wire/news/footer.ts +2 -25
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +0 -2
- package/src/plugins/builtin/notes/cli.ts +12 -7
- package/src/plugins/builtin/notes/index.tsx +1 -0
- package/src/plugins/builtin/options/index.tsx +3 -0
- package/src/plugins/builtin/options/view.tsx +1 -1
- package/src/plugins/builtin/options-calculator/inputs.ts +2 -1
- package/src/plugins/builtin/options-positioning/client.ts +7 -3
- package/src/plugins/builtin/options-positioning/headless.ts +4 -3
- package/src/plugins/builtin/options-positioning/pane.tsx +2 -2
- package/src/plugins/builtin/options-scenario/client.ts +2 -1
- package/src/plugins/builtin/options-scenario/headless.ts +1 -0
- package/src/plugins/builtin/options-scenario/hedge.ts +2 -1
- package/src/plugins/builtin/perps/client.ts +90 -16
- package/src/plugins/builtin/perps/equity-row.tsx +52 -17
- package/src/plugins/builtin/perps/freshness.ts +66 -0
- package/src/plugins/builtin/perps/headless.ts +47 -32
- package/src/plugins/builtin/perps/index.tsx +23 -4
- package/src/plugins/builtin/perps/list-perpetuals.ts +118 -0
- package/src/plugins/builtin/perps/model.ts +20 -5
- package/src/plugins/builtin/perps/open-market.ts +15 -0
- package/src/plugins/builtin/perps/pane.tsx +76 -20
- package/src/plugins/builtin/perps/series.ts +76 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +1 -1
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +73 -67
- package/src/plugins/builtin/plugin-module.ts +3 -0
- package/src/plugins/builtin/portfolio-list/account-metrics.ts +1 -1
- package/src/plugins/builtin/portfolio-list/allocation.ts +5 -4
- package/src/plugins/builtin/portfolio-list/cell-version.ts +8 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +161 -0
- package/src/plugins/builtin/portfolio-list/cli/import-13f.ts +109 -0
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +384 -74
- package/src/plugins/builtin/portfolio-list/cli/render.ts +437 -71
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +21 -4
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +121 -47
- package/src/plugins/builtin/portfolio-list/column-values.ts +104 -26
- package/src/plugins/builtin/portfolio-list/grid.tsx +1 -1
- package/src/plugins/builtin/portfolio-list/headless.ts +84 -18
- package/src/plugins/builtin/portfolio-list/imported-from.ts +8 -0
- package/src/plugins/builtin/portfolio-list/index.tsx +5 -1
- package/src/plugins/builtin/portfolio-list/live-valuation.ts +6 -2
- package/src/plugins/builtin/portfolio-list/mutations.ts +2 -9
- package/src/plugins/builtin/portfolio-list/pane/data.ts +5 -1
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +52 -9
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +33 -11
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +21 -1
- package/src/plugins/builtin/portfolio-list/settings.ts +17 -0
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +12 -0
- package/src/plugins/builtin/portfolio-list/summary/totals.ts +9 -4
- package/src/plugins/builtin/portfolio-list/table.tsx +37 -7
- package/src/plugins/builtin/power/client.ts +16 -0
- package/src/plugins/builtin/power/evidence.ts +63 -10
- package/src/plugins/builtin/power/headless.ts +77 -9
- package/src/plugins/builtin/power/index.tsx +28 -3
- package/src/plugins/builtin/power/model.ts +73 -2
- package/src/plugins/builtin/power/pane.tsx +37 -9
- package/src/plugins/builtin/power/prices-client.ts +139 -0
- package/src/plugins/builtin/power/prices-headless.ts +137 -0
- package/src/plugins/builtin/power/prices-model.ts +103 -0
- package/src/plugins/builtin/power/prices.tsx +179 -0
- package/src/plugins/builtin/rate-path/headless.ts +46 -18
- package/src/plugins/builtin/realized-vol/headless.ts +5 -0
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -2
- package/src/plugins/builtin/research/event-model.ts +68 -24
- package/src/plugins/builtin/research/events-headless.ts +2 -1
- package/src/plugins/builtin/research/headless.ts +2 -1
- package/src/plugins/builtin/research/index.tsx +2 -0
- package/src/plugins/builtin/research-search/article.ts +65 -0
- package/src/plugins/builtin/research-search/document-view.tsx +33 -215
- package/src/plugins/builtin/research-search/model.ts +0 -9
- package/src/plugins/builtin/research-search/pane.tsx +41 -30
- package/src/plugins/builtin/research-search/snippet.ts +0 -29
- package/src/plugins/builtin/revenue-breakdown/headless.ts +18 -6
- package/src/plugins/builtin/seasonality/client.ts +15 -7
- package/src/plugins/builtin/seasonality/headless.ts +36 -12
- package/src/plugins/builtin/seasonality/index.tsx +2 -2
- package/src/plugins/builtin/seasonality/model.ts +119 -0
- package/src/plugins/builtin/seasonality/pane.tsx +108 -37
- package/src/plugins/builtin/sec/headless.ts +25 -3
- package/src/plugins/builtin/sec/index.tsx +2 -1
- package/src/plugins/builtin/series-inventory.ts +93 -0
- package/src/plugins/builtin/shared/cloud-denied.ts +15 -0
- package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -1
- package/src/plugins/builtin/shared/command-tab.ts +30 -12
- package/src/plugins/builtin/shared/earnings-report.ts +55 -0
- package/src/plugins/builtin/shared/fred-points.ts +20 -0
- package/src/plugins/builtin/shared/free-preview.ts +47 -0
- package/src/plugins/builtin/shared/fx-rate-freshness.ts +101 -0
- package/src/plugins/builtin/shared/open-description.ts +29 -0
- package/src/plugins/builtin/shared/report-freshness.ts +12 -1
- package/src/plugins/builtin/shared/series-warning.ts +5 -0
- package/src/plugins/builtin/shared/volatility/options.ts +4 -1
- package/src/plugins/builtin/short-interest/headless.ts +1 -0
- package/src/plugins/builtin/short-interest/index.tsx +2 -0
- package/src/plugins/builtin/short-interest/series.ts +36 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +3 -2
- package/src/plugins/builtin/short-interest/watch-model.ts +9 -5
- package/src/plugins/builtin/short-volume/headless.ts +2 -1
- package/src/plugins/builtin/short-volume/index.tsx +2 -0
- package/src/plugins/builtin/short-volume/series.ts +23 -0
- package/src/plugins/builtin/social-mentions/headless.ts +1 -1
- package/src/plugins/builtin/social-mentions/index.tsx +2 -0
- package/src/plugins/builtin/social-mentions/series.ts +23 -0
- package/src/plugins/builtin/supply-chain/graph-pane.tsx +3 -2
- package/src/plugins/builtin/supply-chain/headless.ts +43 -11
- package/src/plugins/builtin/supply-chain/model.ts +13 -0
- package/src/plugins/builtin/supply-chain/pane.tsx +8 -6
- package/src/plugins/builtin/themes/headless.ts +74 -10
- package/src/plugins/builtin/themes/model.ts +71 -4
- package/src/plugins/builtin/themes/pane.tsx +22 -5
- package/src/plugins/builtin/thirteenf/api.ts +1 -0
- package/src/plugins/builtin/thirteenf/data.ts +6 -4
- package/src/plugins/builtin/thirteenf/headless.ts +217 -36
- package/src/plugins/builtin/thirteenf/model.ts +39 -1
- package/src/plugins/builtin/thirteenf/overlap.ts +75 -0
- package/src/plugins/builtin/thirteenf/pane.tsx +10 -1
- package/src/plugins/builtin/thirteenf/report-notes.ts +103 -1
- package/src/plugins/builtin/thirteenf/types.ts +2 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +114 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +57 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +19 -55
- package/src/plugins/builtin/ticker-detail/headless-schema.ts +30 -0
- package/src/plugins/builtin/ticker-detail/headless.ts +86 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +26 -11
- package/src/plugins/builtin/ticker-detail/overview/components.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/overview/futures-contract.ts +31 -0
- package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -3
- package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +49 -5
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +13 -3
- package/src/plugins/builtin/time-sales/headless.ts +2 -1
- package/src/plugins/builtin/time-sales/index.tsx +2 -1
- package/src/plugins/builtin/treasury-auctions/headless.ts +16 -2
- package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
- package/src/plugins/builtin/treasury-auctions/model.ts +8 -0
- package/src/plugins/builtin/vol-surface/evidence.ts +7 -1
- package/src/plugins/builtin/vol-surface/headless.ts +51 -17
- package/src/plugins/builtin/vol-surface/heat-grid.tsx +113 -0
- package/src/plugins/builtin/vol-surface/model.ts +39 -6
- package/src/plugins/builtin/vol-surface/pane.tsx +14 -13
- package/src/plugins/builtin/vol-surface/stored.ts +63 -3
- package/src/plugins/builtin/vol-surface/surface.tsx +30 -2
- package/src/plugins/builtin/volatility/index.tsx +2 -0
- package/src/plugins/builtin/volatility/series.ts +84 -0
- package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +6 -5
- package/src/plugins/builtin/world-venue-map/basemap.ts +6 -3
- package/src/plugins/builtin/world-venue-map/geo-draw.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/geo-series.ts +26 -1
- package/src/plugins/builtin/world-venue-map/geo-svg.tsx +6 -6
- package/src/plugins/builtin/world-venue-map/headless.ts +4 -1
- package/src/plugins/builtin/world-venue-map/index.ts +2 -1
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +24 -13
- package/src/plugins/builtin/world-venue-map/layers.ts +29 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +144 -13
- package/src/plugins/builtin/world-venue-map/model.ts +56 -19
- package/src/plugins/builtin/world-venue-map/pane.tsx +5 -2
- package/src/plugins/builtin/yield-curve/chart.ts +2 -2
- package/src/plugins/builtin/yield-curve/curves.ts +21 -8
- package/src/plugins/builtin/yield-curve/headless.ts +71 -18
- package/src/plugins/builtin/yield-curve/index.tsx +4 -2
- package/src/plugins/builtin/yield-curve/series.ts +86 -0
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/installer.ts +2 -2
- package/src/plugins/loader.ts +15 -0
- package/src/plugins/manager-node.ts +1 -1
- package/src/plugins/{builtin/plugin-marketplace → marketplace}/feed.ts +2 -2
- package/src/plugins/{builtin/plugin-marketplace → marketplace}/model.ts +6 -6
- package/src/plugins/{builtin/plugin-marketplace → marketplace}/store.ts +1 -1
- package/src/plugins/registry/host-actions.ts +11 -0
- package/src/plugins/registry/index.ts +89 -4
- package/src/portfolio/collection-name.ts +31 -0
- package/src/{plugins/builtin/portfolio-list → portfolio}/position-metrics.ts +6 -6
- package/src/portfolio/position-value.ts +40 -0
- package/src/portfolio/valuation-basis.ts +79 -0
- package/src/public/__snapshots__/public-api.test.ts.snap +7 -0
- package/src/public/time-series.ts +17 -0
- package/src/react/auto-refresh.ts +13 -3
- package/src/react/input.ts +33 -1
- package/src/react/key-repeat.ts +86 -0
- package/src/remote/app-host.tsx +20 -4
- package/src/remote/confirm-change.tsx +85 -0
- package/src/remote/controller-utils.ts +8 -1
- package/src/remote/controller.ts +144 -7
- package/src/remote/pane-screenshot.ts +47 -0
- package/src/remote/schema.ts +65 -2
- package/src/remote/types.ts +30 -0
- package/src/remote/watchlist.ts +306 -0
- package/src/renderers/browser/main.tsx +4 -3
- package/src/renderers/browser/research-entry.ts +4 -2
- package/src/renderers/cloudflare/worker.ts +1 -1
- package/src/renderers/dom/data-table/frozen-column.ts +3 -1
- package/src/renderers/dom/data-table/index.tsx +33 -6
- package/src/renderers/dom/data-table/row.tsx +30 -1
- package/src/renderers/dom/desktop/control-styles.ts +63 -1
- package/src/renderers/dom/desktop/controls.tsx +30 -23
- package/src/renderers/dom/desktop/dialog-actions.tsx +170 -0
- package/src/renderers/dom/desktop/icons.tsx +16 -0
- package/src/renderers/dom/dom-ui-host.tsx +3 -0
- package/src/renderers/dom/host/block-glyph-canvas.tsx +6 -3
- package/src/renderers/dom/host/box.tsx +8 -0
- package/src/renderers/dom/host/chart-surface.tsx +33 -2
- package/src/renderers/dom/host/live-frame.ts +6 -2
- package/src/renderers/dom/host/overflow-fade.ts +14 -1
- package/src/renderers/dom/host/scroll-box.tsx +32 -0
- package/src/renderers/dom/host/size-observer.ts +41 -0
- package/src/renderers/dom/host/sticky-scroll.ts +128 -0
- package/src/renderers/dom/host/style.ts +1 -1
- package/src/renderers/dom/host/surface-3d.tsx +19 -7
- package/src/renderers/dom/host/surface-fit.ts +406 -0
- package/src/renderers/dom/host/text.tsx +1 -1
- package/src/renderers/dom/input-host.tsx +1 -0
- package/src/renderers/dom/styles.css +102 -5
- package/src/renderers/electrobun/bun/application-menu/click.ts +18 -0
- package/src/renderers/electrobun/bun/index.ts +3 -2
- package/src/renderers/electrobun/shared/protocol.ts +1 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +29 -2
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +63 -20
- package/src/renderers/electrobun/view/cli-pane-shot-health.ts +10 -26
- package/src/renderers/electrobun/view/main.tsx +5 -3
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +2 -0
- package/src/renderers/opentui/chart-surface.tsx +1 -0
- package/src/renderers/opentui/frame-capture.ts +33 -0
- package/src/renderers/opentui/host.tsx +10 -13
- package/src/renderers/opentui/pane-image/glyphs-license.txt +107 -0
- package/src/renderers/opentui/pane-image/glyphs.json +1 -0
- package/src/renderers/opentui/pane-image/index.ts +107 -0
- package/src/renderers/opentui/pane-image/raster.ts +428 -0
- package/src/renderers/opentui/start.tsx +3 -3
- package/src/renderers/opentui/test-utils.tsx +3 -0
- package/src/renderers/opentui/ui-host.tsx +29 -1
- package/src/series/capability.ts +75 -0
- package/src/series/csv.ts +160 -0
- package/src/series/files.ts +107 -0
- package/src/series/registry.ts +198 -0
- package/src/series/resolve.ts +108 -0
- package/src/series/standalone.ts +48 -0
- package/src/series/types.ts +153 -0
- package/src/series/validate.ts +171 -0
- package/src/shares/chat.ts +34 -0
- package/src/shares/dialog.tsx +295 -0
- package/src/shares/flow.tsx +56 -0
- package/src/shares/live.ts +14 -22
- package/src/shares/pane-image.ts +152 -0
- package/src/shares/personal-pane.ts +64 -0
- package/src/shares/portable-layout.ts +4 -2
- package/src/shares/prepare.ts +20 -0
- package/src/sources/gloomberb-cloud/index.ts +109 -10
- package/src/sources/gloomberb-cloud/normalizers.ts +3 -0
- package/src/sources/gloomberb-cloud/perpetuals.ts +249 -0
- package/src/sources/history-result.ts +1 -0
- package/src/sources/history-retention.ts +11 -0
- package/src/sources/listing-symbols.ts +4 -1
- package/src/sources/listing-target.ts +4 -0
- package/src/sources/provider-router/batches.ts +4 -1
- package/src/sources/provider-router/cache.ts +4 -2
- package/src/sources/provider-router/cached-routes.ts +1 -1
- package/src/sources/provider-router/financial-routes.ts +1 -0
- package/src/sources/provider-router/financials.ts +13 -0
- package/src/sources/provider-router/history.ts +43 -25
- package/src/sources/provider-router/index.ts +14 -0
- package/src/sources/provider-router/search.ts +1 -0
- package/src/state/hooks/inline-tickers.ts +49 -11
- package/src/sync/core-contributors.ts +1 -0
- package/src/test-support/shell-pane-keys.tsx +1 -0
- package/src/theme/theme-context.tsx +15 -1
- package/src/tickers/company-name.ts +75 -0
- package/src/tickers/instrument-kind.ts +34 -1
- package/src/tickers/listing-name.ts +34 -0
- package/src/tickers/listing-venues.ts +132 -0
- package/src/tickers/open-target.ts +13 -4
- package/src/tickers/perpetual-listing.ts +151 -0
- package/src/tickers/prediction-listing.ts +26 -0
- package/src/tickers/search/index.ts +72 -11
- package/src/tickers/search/ranking.ts +98 -20
- package/src/tickers/search/types.ts +7 -1
- package/src/tickers/ticker-changes.ts +213 -0
- package/src/tickers/us-listing.ts +34 -0
- package/src/time-series/economic.ts +2 -6
- package/src/time-series/hooks.ts +9 -0
- package/src/time-series/ice-credit.ts +39 -0
- package/src/time-series/resolve.ts +211 -29
- package/src/time-series/studies.ts +1 -0
- package/src/time-series/types.ts +12 -2
- package/src/time-series/use-chart-resolution.ts +32 -0
- package/src/types/config.ts +10 -2
- package/src/types/data-provider.ts +11 -0
- package/src/types/financials.ts +62 -1
- package/src/types/headless.ts +113 -0
- package/src/types/instrument.ts +4 -0
- package/src/types/plugin.ts +52 -2
- package/src/types/price-history.ts +7 -0
- package/src/types/ticker-changes.ts +26 -0
- package/src/types/ticker.ts +14 -0
- package/src/ui/context-menu.tsx +13 -1
- package/src/ui/host.tsx +57 -3
- package/src/ui/live-resize.ts +91 -0
- package/src/ui/live-stretch.ts +153 -0
- package/src/ui/measured-cells.ts +58 -0
- package/src/ui/sticky-scroll.ts +7 -0
- package/src/utils/basis-points.ts +17 -0
- package/src/utils/central-banks.ts +76 -0
- package/src/utils/cli-output.ts +131 -15
- package/src/utils/concurrency.ts +25 -0
- package/src/utils/crypto-pair.ts +20 -0
- package/src/utils/currency-pair.ts +19 -0
- package/src/utils/currency-units.ts +11 -0
- package/src/utils/datetime-format.ts +27 -6
- package/src/utils/decimal-comma.ts +19 -0
- package/src/utils/dom-screenshot-css.ts +265 -0
- package/src/utils/dom-screenshot-rasters.ts +276 -0
- package/src/utils/dom-screenshot.ts +439 -52
- package/src/utils/double-escape-close.ts +15 -1
- package/src/utils/exchange-rate-snapshot.ts +6 -2
- package/src/utils/exchanges.ts +143 -1
- package/src/utils/format.ts +35 -5
- package/src/utils/forward-dividend.ts +14 -0
- package/src/utils/fundamentals.ts +66 -0
- package/src/utils/futures-generic.ts +6 -12
- package/src/utils/fx-market-hours.ts +15 -5
- package/src/utils/fx-status.ts +6 -1
- package/src/utils/inline-content-tokenizer.ts +22 -3
- package/src/utils/market-capitalization.ts +8 -0
- package/src/utils/one-shot.ts +73 -0
- package/src/utils/options.ts +37 -8
- package/src/utils/price-history.ts +99 -42
- package/src/utils/sec-eps-basis.ts +24 -11
- package/src/utils/sec.ts +61 -7
- package/src/utils/utc-time.ts +63 -4
- package/src/plugins/builtin/fx-matrix/freshness.ts +0 -65
- /package/src/plugins/{builtin/plugin-marketplace → marketplace}/activation.ts +0 -0
- /package/src/plugins/{builtin/plugin-marketplace → marketplace}/ids.ts +0 -0
- /package/src/plugins/{builtin/plugin-marketplace → marketplace}/packs.ts +0 -0
- /package/src/{plugins/builtin/cloud → tickers}/starter-symbols.ts +0 -0
|
@@ -1,25 +1,68 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import {
|
|
1
|
+
import type { DataTableColumn } from "../../../components";
|
|
2
|
+
import type { HeadlessPaneColumn, HeadlessPaneDefinition, HeadlessPaneRow } from "../../../types/plugin";
|
|
3
|
+
import { withoutQuietColumns } from "../shared/research-cells";
|
|
4
|
+
import { fetchPowerBoard, fetchAllPowerHistory, fetchPowerTickerScopes } from "./client";
|
|
3
5
|
import { powerQuery } from "./query";
|
|
4
|
-
import {
|
|
6
|
+
import { loadPowerPricesReport } from "./prices-headless";
|
|
7
|
+
import { isCarbonQuery, powerPriceMarket } from "./prices-model";
|
|
8
|
+
import {
|
|
9
|
+
COVERAGE_COLUMNS, EXPOSURE_COLUMNS, GENERATION_COLUMNS, HISTORY_COLUMNS, POWER_TABS, PROJECT_COLUMNS, RATE_COLUMNS, UTILITY_COLUMNS,
|
|
10
|
+
historyRows, powerCoverageNote, powerLoadedSources, powerNumber, powerPercent, powerRegion, powerScopeOptions, powerTab, powerTickerEmpty, projectRows, tickerLabel, titleCase, UNMAPPED, type PowerTab,
|
|
11
|
+
} from "./model";
|
|
12
|
+
|
|
13
|
+
/** Bars the pane draws beside the numbers they are made of; the report has the numbers. */
|
|
14
|
+
const GRAPHIC_COLUMNS = new Set(["outcome", "pipeline"]);
|
|
15
|
+
/** Columns that only repeat a default on every row, as the pane leaves them out. */
|
|
16
|
+
const QUIET_COLUMNS: Record<string, readonly string[]> = { scope: ["Project"], unknownCapacity: ["0"], unknown: ["0"], fuel: ["Load"], role: ["Direct"], kind: ["Queue"] };
|
|
17
|
+
const text = (value: unknown) => value == null || value === "" ? "-" : String(value);
|
|
18
|
+
const coded = (value: unknown) => typeof value === "string" && value ? titleCase(value) : "-";
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* The pane's columns for a tab, printed as the pane prints them: MW and MWh
|
|
22
|
+
* to a tenth with thousands separators, rates as percentages, register codes
|
|
23
|
+
* by name, and each record's listed company or "unmapped". Raw metrics and
|
|
24
|
+
* evidence stay in the exported rows, out of the table.
|
|
25
|
+
*/
|
|
26
|
+
function reportColumns(tab: PowerTab, context: unknown): HeadlessPaneColumn[] {
|
|
27
|
+
const columns: DataTableColumn[] = tab === "capacity" && context === "generation" ? GENERATION_COLUMNS : tab === "capacity" && context === "utility" ? UTILITY_COLUMNS
|
|
28
|
+
: ["queue", "loads", "capacity"].includes(tab) ? PROJECT_COLUMNS : tab === "history" ? HISTORY_COLUMNS : tab === "outcomes" ? RATE_COLUMNS : tab === "utilities" ? EXPOSURE_COLUMNS : COVERAGE_COLUMNS;
|
|
29
|
+
// The coverage list names its register by its own id; everything else by the record's source.
|
|
30
|
+
const region = (value: unknown, row: HeadlessPaneRow) => typeof value !== "string" ? "-"
|
|
31
|
+
: tab === "utilities" ? value : powerRegion(value, String((tab === "coverage" ? row.id : row.sourceId) ?? ""));
|
|
32
|
+
const format = (column: DataTableColumn): HeadlessPaneColumn["format"] => {
|
|
33
|
+
if (column.id === "region") return region;
|
|
34
|
+
if (["fuel", "status", "kind", "role"].includes(column.id)) return coded;
|
|
35
|
+
if (column.id === "ticker") return (value) => typeof value === "string" && value ? value : UNMAPPED;
|
|
36
|
+
if (column.id === "developer") return (value, row) => text(value ?? row.utility);
|
|
37
|
+
if (column.id === "observedAt") return (value) => typeof value === "string" ? value.slice(0, 16).replace("T", " ") : "-";
|
|
38
|
+
if (/Rate$/.test(column.id)) return (value) => typeof value === "number" ? powerPercent(value) : "-";
|
|
39
|
+
if (column.align === "right") return (value) => typeof value === "number" ? `${column.id === "changeMw" && value > 0 ? "+" : ""}${powerNumber(value)}` : "-";
|
|
40
|
+
return text;
|
|
41
|
+
};
|
|
42
|
+
// The pane groups outcomes under region headings; the report names the region on each row.
|
|
43
|
+
return [...(tab === "outcomes" ? [{ id: "region", label: "Region", width: 16, align: "left" as const }] : []), ...columns].filter((column) => !GRAPHIC_COLUMNS.has(column.id))
|
|
44
|
+
.map((column) => ({ key: column.id, header: column.label, align: column.align === "right" ? "right" as const : "left" as const, format: format(column) }));
|
|
45
|
+
}
|
|
5
46
|
|
|
6
47
|
export const powerHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
7
48
|
shape: "bundle",
|
|
8
|
-
freshness: { source: "Grid operator queues and utility filings", status: "not-a-feed", basis: "published records" },
|
|
49
|
+
freshness: { source: "Grid operator queues and utility filings", status: "not-a-feed", basis: "published records", dataset: true },
|
|
9
50
|
argument: { kind: "free-text", optional: true, placeholder: "ticker", description: "Optional listed developer or utility ticker, with exchange suffix where needed." },
|
|
10
51
|
options: [
|
|
11
|
-
{ key: "tab", type: "enum", settingKey: "tab", defaultValue: "queue", values: POWER_TABS.map(({ value }) => ({ value })), description: "Queue, history, outcomes, large loads, utility exposure, capacity or coverage." },
|
|
52
|
+
{ key: "tab", type: "enum", settingKey: "tab", defaultValue: "queue", values: POWER_TABS.map(({ value }) => ({ value })), description: "Queue, history, outcomes, large loads, utility exposure, capacity, European day-ahead prices or coverage." },
|
|
12
53
|
...["country", "region", "fuel", "status", "search", "sourceId", "from", "to"].map((key) => ({ key, type: "string" as const, settingKey: key, description: `Filter by ${key}.` })),
|
|
13
54
|
{ key: "context", type: "enum", settingKey: "context", values: [{ value: "capacity" }, { value: "generation" }, { value: "utility" }], defaultValue: "capacity", description: "Generation capacity, monthly generation or utility context on the Capacity tab." },
|
|
14
55
|
{ key: "loadClass", aliases: ["load-class"], type: "enum", settingKey: "loadClass", values: [{ value: "datacenter" }], description: "Only loads explicitly classified as datacenters." },
|
|
15
56
|
{ key: "historical", type: "boolean", settingKey: "historical", defaultValue: false, description: "Use historical benchmark queues separately from direct observations." },
|
|
16
57
|
],
|
|
17
58
|
discovery: { aliases: ["POWER"], dataRequirements: ["Gloom Cloud power queues and large-load disclosures"],
|
|
18
|
-
limitations: ["Pro dataset; free accounts see three rows per section and limited history", "Proposed projects are not committed supply; requests and approvals are distinct", "Coverage varies by jurisdiction; missing MW and unavailable sources remain explicit"] },
|
|
59
|
+
limitations: ["Pro dataset; free accounts see three rows per section and limited history.", "Proposed projects are not committed supply; requests and approvals are distinct.", "Coverage varies by jurisdiction; missing MW and unavailable sources remain explicit."] },
|
|
19
60
|
describe: "Power queues, large loads and utility exposure (Pro)",
|
|
20
61
|
async load(args, ctx) {
|
|
21
62
|
const tab = powerTab(args.options.tab);
|
|
22
63
|
const symbol = (Array.isArray(args.argument) ? args.argument.join(" ") : args.argument)?.trim();
|
|
64
|
+
// Prices are their own board (EPWR); a typed market narrows it, a ticker does not apply.
|
|
65
|
+
if (tab === "prices") return loadPowerPricesReport(powerPriceMarket(symbol) || isCarbonQuery(symbol) ? symbol : undefined, ctx.apiClient);
|
|
23
66
|
const filter = { ...powerQuery(args.options, symbol), limit: 500 };
|
|
24
67
|
const board = await fetchPowerBoard(filter, ctx.apiClient);
|
|
25
68
|
const projects = [...board.projects];
|
|
@@ -36,10 +79,35 @@ export const powerHeadless: HeadlessPaneDefinition<"bundle"> = {
|
|
|
36
79
|
projects.push(...page.projects); offset = page.nextOffset; hasMore = page.hasMore;
|
|
37
80
|
}
|
|
38
81
|
const history = tab === "history" ? await fetchAllPowerHistory(filter, ctx.apiClient) : null;
|
|
39
|
-
|
|
82
|
+
// Exported rows keep every field the server sent; the pane's derived cells (the ticker map, location, scope) ride beside them.
|
|
83
|
+
const cells = projectRows(projects);
|
|
84
|
+
const rows: HeadlessPaneRow[] = tab === "history" ? historyRows(history!.points).map((point) => ({ ...point, date: point.basis === "published" ? point.period : point.observedAt.slice(0, 16).replace("T", " ") }))
|
|
85
|
+
: tab === "outcomes" ? board.rates.map((rate) => ({ ...rate }))
|
|
86
|
+
: tab === "utilities" ? board.exposure.map((exposure) => ({ ...exposure, ticker: tickerLabel(exposure) }))
|
|
87
|
+
: tab === "coverage" ? board.coverage.map((coverage) => ({ ...coverage }))
|
|
88
|
+
: projects.map((project, index) => {
|
|
89
|
+
const { ticker, location, scope, generationMwh, summerPeakDemandMw, winterPeakDemandMw, salesMwh, revenueUsd } = cells[index]!.cells;
|
|
90
|
+
return { ...project, ticker, location, scope, generationMwh, summerPeakDemandMw, winterPeakDemandMw, salesMwh, revenueUsd };
|
|
91
|
+
});
|
|
40
92
|
const locked = history?.locked ?? (tab === "outcomes" ? board.locked.rates : tab === "utilities" ? board.locked.exposure : tab === "coverage" ? 0 : board.locked.projects);
|
|
41
|
-
|
|
93
|
+
const columns = reportColumns(tab, args.options.context);
|
|
94
|
+
const shown = withoutQuietColumns(columns.map((column) => ({ id: column.key, label: column.header, width: 0, align: "left" as const })), rows,
|
|
95
|
+
(row, id) => columns.find((column) => column.key === id)!.format!(row[id], row), QUIET_COLUMNS).map((column) => column.id);
|
|
96
|
+
// A ticker with nothing on this tab says where its records are instead; a narrower filter is the filters' doing.
|
|
97
|
+
const narrowed = ["country", "region", "fuel", "status", "search", "sourceId", "from", "to", "loadClass", "historical"].some((key) => !!args.options[key]);
|
|
98
|
+
const empty = !rows.length && symbol && !narrowed && tab !== "coverage" ? await fetchPowerTickerScopes(symbol, tab, String(args.options.context ?? "capacity"), ctx.apiClient)
|
|
99
|
+
.then((scopes) => {
|
|
100
|
+
const message = powerTickerEmpty(symbol, tab, String(args.options.context ?? "capacity"), scopes);
|
|
101
|
+
const where = message.found.map((scope) => `${powerNumber(scope.total)} ${scope.label} (${powerScopeOptions(scope)})`).join(", ");
|
|
102
|
+
const loaded = powerLoadedSources(tab, board.coverage);
|
|
103
|
+
return [message.title, where ? `${symbol} has ${where}.` : message.hint?.replace(/\.$/, " (--search <name>)."),
|
|
104
|
+
loaded ? `Sources loaded now: ${loaded}.` : null].filter(Boolean).join(" ");
|
|
105
|
+
}) : "No reported records match these filters.";
|
|
106
|
+
// Missing or unsupported sources are coverage, not failures: one plain line, details in --tab coverage.
|
|
107
|
+
const coverageNote = tab === "capacity" ? null : powerCoverageNote(board.coverage);
|
|
108
|
+
return { complete: locked === 0, sections: [{ title: POWER_TABS.find((t) => t.value === tab)!.label, columns: columns.filter((column) => shown.includes(column.key)), rows, empty }],
|
|
42
109
|
metadata: { generatedAt: board.generatedAt, access: board.access, locked, total: board.total, units: "MW unless specified", filters: filter, aggregates: board.aggregates, coverage: board.coverage },
|
|
43
|
-
|
|
110
|
+
...(coverageNote ? { notes: [coverageNote] } : {}),
|
|
111
|
+
...(locked ? { errors: ["Additional records and history need Gloom Pro."] } : {}) };
|
|
44
112
|
},
|
|
45
113
|
};
|
|
@@ -2,15 +2,40 @@ import type { PluginModule } from "../plugin-module";
|
|
|
2
2
|
import { powerBoardCache, powerDetailCache, powerHistoryCache } from "./client";
|
|
3
3
|
import { powerHeadless } from "./headless";
|
|
4
4
|
import { PowerPane } from "./pane";
|
|
5
|
+
import { carbonHistoryCache, carbonPriceCache, powerPriceHistoryCache, powerPricesCache } from "./prices-client";
|
|
6
|
+
import { powerPricesHeadless } from "./prices-headless";
|
|
7
|
+
import { CARBON_SELECTION, CARBON_SERIES, DAY_AHEAD_SERIES, isCarbonQuery, POWER_PRICE_MARKETS, powerPriceMarket } from "./prices-model";
|
|
8
|
+
|
|
9
|
+
const CACHES = [powerBoardCache, powerHistoryCache, powerDetailCache, powerPricesCache, powerPriceHistoryCache, carbonPriceCache, carbonHistoryCache];
|
|
5
10
|
|
|
6
11
|
export const powerModule: PluginModule = {
|
|
12
|
+
series: [...DAY_AHEAD_SERIES, ...CARBON_SERIES],
|
|
7
13
|
panes: [{ id: "power", name: "Power & Grid", icon: "P", component: PowerPane, defaultPosition: "right", defaultMode: "floating",
|
|
8
14
|
defaultFloatingSize: { width: 132, height: 34 }, tableExport: true, headless: powerHeadless }],
|
|
9
15
|
paneTemplates: [{ id: "power-pane", paneId: "power", label: "Power & Grid",
|
|
10
16
|
description: "Pro: interconnection queues, historical MW and outcomes, datacenter loads, utility exposure and generation capacity, with primary evidence.",
|
|
11
17
|
keywords: ["power", "electricity", "interconnection", "grid", "datacenter", "utility", "renewables", "storage", "capacity"],
|
|
12
18
|
shortcut: { prefix: "POWER", argKind: "text", argPlaceholder: "ticker", argOptional: true }, headless: powerHeadless,
|
|
13
|
-
createInstance: (_context, options) =>
|
|
14
|
-
|
|
15
|
-
|
|
19
|
+
createInstance: (_context, options) => {
|
|
20
|
+
const symbol = options?.symbol ?? options?.arg?.trim().toUpperCase() ?? "";
|
|
21
|
+
return { placement: "floating", params: { symbol }, ...(symbol ? { title: `POWER ${symbol}` } : {}) };
|
|
22
|
+
} }, {
|
|
23
|
+
id: "power-prices-pane", paneId: "power", label: "EU Power & Carbon",
|
|
24
|
+
description: "Day-ahead power prices for 32 European markets in EUR/MWh and the EU carbon allowance (EUA) auction price in EUR/tCO2: the latest day, 7- and 30-day averages, a year ago and the 52-week range, with daily history.",
|
|
25
|
+
keywords: ["day-ahead", "day ahead", "power price", "power prices", "electricity price", "electricity prices", "wholesale power",
|
|
26
|
+
"spot power", "baseload", "europe", "european", "eur/mwh", "carbon", "eu carbon", "eua", "eu ets", "carbon price",
|
|
27
|
+
"carbon allowance", "emission allowances", "co2", "eur/tco2"],
|
|
28
|
+
shortcut: { prefix: "EPWR", argKind: "text", argPlaceholder: "market", argOptional: true,
|
|
29
|
+
argOptions: () => [{ value: "EUA", label: "EU carbon allowance" }, ...POWER_PRICE_MARKETS.map(({ code, name }) => ({ value: code, label: name }))] },
|
|
30
|
+
headless: powerPricesHeadless,
|
|
31
|
+
createInstance: (_context, options) => {
|
|
32
|
+
const input = options?.arg?.trim();
|
|
33
|
+
const market = powerPriceMarket(input);
|
|
34
|
+
// EPWR EUA opens the tab with the carbon allowance charted above the board.
|
|
35
|
+
const carbon = isCarbonQuery(input);
|
|
36
|
+
if (input && !market && !carbon) throw new Error("Use a market such as DE, FR, GB or Spain, or EUA");
|
|
37
|
+
return { placement: "floating", settings: { tab: "prices" }, ...(market || carbon ? { params: { market: market?.code ?? CARBON_SELECTION } } : {}) };
|
|
38
|
+
} }],
|
|
39
|
+
setup(ctx) { for (const cache of CACHES) cache.attach(ctx.persistence); },
|
|
40
|
+
dispose() { for (const cache of CACHES) cache.reset(); },
|
|
16
41
|
};
|
|
@@ -2,16 +2,87 @@ import type { PowerAggregate, PowerBoard, PowerCoverage, PowerExposure, PowerHis
|
|
|
2
2
|
import { staticSeries, type DataTableColumn } from "../../../components";
|
|
3
3
|
|
|
4
4
|
export const POWER_TABS = [{ value: "queue", label: "Queue" }, { value: "history", label: "History" }, { value: "outcomes", label: "Outcomes" },
|
|
5
|
-
{ value: "loads", label: "Loads" }, { value: "utilities", label: "Utilities" }, { value: "capacity", label: "Capacity" }, { value: "coverage", label: "Coverage" }] as const;
|
|
5
|
+
{ value: "loads", label: "Loads" }, { value: "utilities", label: "Utilities" }, { value: "capacity", label: "Capacity" }, { value: "prices", label: "Prices" }, { value: "coverage", label: "Coverage" }] as const;
|
|
6
6
|
export type PowerTab = typeof POWER_TABS[number]["value"];
|
|
7
7
|
export const powerTab = (value: unknown): PowerTab => POWER_TABS.find((tab) => tab.value === value)?.value ?? "queue";
|
|
8
8
|
const REGISTERS: Record<string, string> = { "neso-tec": "GB transmission", "neso-embedded": "GB embedded", "neso-interconnector": "GB interconnectors" };
|
|
9
9
|
const REGIONS: Record<string, string> = { LBNL: "US project queues", "LBNL-ANNUAL": "US annual queues", EIA860: "US generation capacity", EIA861: "US utilities", EIA923: "US generation output" };
|
|
10
10
|
export const powerRegion = (value: string, sourceId?: string) => REGISTERS[sourceId ?? ""] ?? REGIONS[value] ?? value;
|
|
11
|
+
/** A source with stored records, even when its latest refresh failed. */
|
|
12
|
+
const loadedSource = (c: PowerCoverage) => (c.status === "current" || c.status === "stale") && c.records > 0;
|
|
13
|
+
const names = (rows: readonly PowerCoverage[]) => [...new Set(rows.map((c) => c.region === "EirGrid" ? "Ireland" : c.region))].join(", ");
|
|
14
|
+
/**
|
|
15
|
+
* Which US grid operators' queues loaded, in plain words. Source errors and
|
|
16
|
+
* unsupported registers stay in the Coverage tab and on the server; a missing
|
|
17
|
+
* queue is a gap in coverage, never zero.
|
|
18
|
+
*/
|
|
19
|
+
export function powerCoverageNote(coverage: readonly PowerCoverage[]): string | null {
|
|
20
|
+
const queues = coverage.filter((c) => c.country === "US" && c.role === "direct" && c.kind === "queue");
|
|
21
|
+
const missing = queues.filter((c) => !loadedSource(c));
|
|
22
|
+
if (!missing.length) return null;
|
|
23
|
+
const loaded = queues.filter(loadedSource);
|
|
24
|
+
return `US queues: ${loaded.length ? `${names(loaded)} loaded; ` : ""}${names(missing)} not available`;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* The registers carrying records right now, named, for a ticker that has none
|
|
28
|
+
* in them: grid operator and TSO registers, or the EIA filings behind the
|
|
29
|
+
* Capacity tab. Null when nothing is loaded.
|
|
30
|
+
*/
|
|
31
|
+
export function powerLoadedSources(tab: PowerTab, coverage: readonly PowerCoverage[]): string | null {
|
|
32
|
+
const loaded = coverage.filter((c) => loadedSource(c) && (tab === "capacity" ? c.role === "context" : c.role === "direct"));
|
|
33
|
+
if (!loaded.length) return null;
|
|
34
|
+
return tab === "capacity" ? "US EIA generation and utility filings" : names([...loaded.filter((c) => c.country === "US"), ...loaded.filter((c) => c.country !== "US")]);
|
|
35
|
+
}
|
|
11
36
|
export const titleCase = (value: string) => value.replace(/(^|[ _-])\w/g, (match) => match.toUpperCase());
|
|
12
37
|
export const powerNumber = (n: number | null | undefined) => n == null ? "--" : n.toLocaleString("en-US", { maximumFractionDigits: 1 });
|
|
13
38
|
export const powerPercent = (n: number | null) => n == null ? "--" : `${(n * 100).toFixed(1)}%`;
|
|
14
|
-
|
|
39
|
+
/** The listed companies a record names, or null when its register names none: the ticker map. */
|
|
40
|
+
export const tickerLabel = (row: { entities: PowerProject["entities"] }) => [...new Set(row.entities.flatMap((e) => e.tickers.map((t) => t.ticker)))].join(", ") || null;
|
|
41
|
+
/** What the ticker column says for a record no listed company is mapped to. */
|
|
42
|
+
export const UNMAPPED = "unmapped";
|
|
43
|
+
|
|
44
|
+
/** Where else a ticker's records can sit: each tab, and the capacity tab's contexts, with what they list. */
|
|
45
|
+
const POWER_TICKER_SCOPES = [
|
|
46
|
+
{ tab: "queue", context: "capacity", label: "interconnection queue records", view: "Queue" },
|
|
47
|
+
{ tab: "loads", context: "capacity", label: "large-load requests", view: "Loads" },
|
|
48
|
+
{ tab: "capacity", context: "capacity", label: "generation capacity records", view: "Capacity" },
|
|
49
|
+
{ tab: "capacity", context: "generation", label: "monthly generation records", view: "Generation" },
|
|
50
|
+
{ tab: "capacity", context: "utility", label: "utility sales records", view: "Utility sales" },
|
|
51
|
+
] as const satisfies readonly { tab: PowerTab; context: string; label: string; view: string }[];
|
|
52
|
+
/** `total` is null for a scope that did not answer, which is no evidence either way. */
|
|
53
|
+
export type PowerTickerScope = (typeof POWER_TICKER_SCOPES)[number] & { total: number | null };
|
|
54
|
+
const sameScope = (scope: { tab: string; context: string }, tab: PowerTab, context: string) =>
|
|
55
|
+
scope.tab === tab && (tab !== "capacity" || scope.context === context)
|
|
56
|
+
// Utilities are the large loads summed by utility: a ticker with no loads has no exposure either.
|
|
57
|
+
|| tab === "utilities" && scope.tab === "loads";
|
|
58
|
+
/** The scopes to count a ticker's records in when the open tab has none of them. */
|
|
59
|
+
export const powerTickerScopes = (tab: PowerTab, context: string) => POWER_TICKER_SCOPES.filter((scope) => !sameScope(scope, tab, context));
|
|
60
|
+
|
|
61
|
+
/** What the open tab lists, for the sentence that says none of it maps to a ticker. */
|
|
62
|
+
function powerTabRecords(tab: PowerTab, context: string): string {
|
|
63
|
+
if (tab === "capacity") return POWER_TICKER_SCOPES.find((scope) => scope.tab === "capacity" && scope.context === context)?.label ?? "capacity records";
|
|
64
|
+
return { queue: "interconnection queue records", history: "queue history records", outcomes: "queue outcomes", loads: "large-load requests",
|
|
65
|
+
utilities: "large-load requests or utility exposure", prices: "records", coverage: "records" }[tab];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Why a ticker's tab is empty, and where its records are instead: the title
|
|
70
|
+
* says what does not map to it, the hint where it has records, or, once every
|
|
71
|
+
* other tab has answered with none, that a name search is what is left.
|
|
72
|
+
*/
|
|
73
|
+
export function powerTickerEmpty(symbol: string, tab: PowerTab, context: string, scopes: readonly PowerTickerScope[]) {
|
|
74
|
+
const found = scopes.filter((scope): scope is PowerTickerScope & { total: number } => (scope.total ?? 0) > 0);
|
|
75
|
+
const answered = scopes.length > 0 && scopes.every((scope) => scope.total != null);
|
|
76
|
+
return {
|
|
77
|
+
title: `No ${powerTabRecords(tab, context)} map to ${symbol}.`,
|
|
78
|
+
found,
|
|
79
|
+
hint: found.length ? `${symbol} has ${found.map((scope) => `${powerNumber(scope.total)} ${scope.label}`).join(", ")}.`
|
|
80
|
+
: answered ? `No power records map to ${symbol}; search by the company's or a subsidiary's name instead.` : null,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/** The options that open a scope from the command line. */
|
|
84
|
+
export const powerScopeOptions = (scope: { tab: string; context: string }) =>
|
|
85
|
+
scope.tab === "capacity" && scope.context !== "capacity" ? `--tab capacity --context ${scope.context}` : `--tab ${scope.tab}`;
|
|
15
86
|
const historyKey = (p: PowerHistoryPoint) => JSON.stringify([p.sourceId, p.country, p.region, p.kind, p.fuel, p.status, p.historical, p.basis]);
|
|
16
87
|
/** Annual benchmarks use their stated period. Current queues use when we observed them, never a proposed completion date. */
|
|
17
88
|
export function historyDate(p: PowerHistoryPoint): Date {
|
|
@@ -2,25 +2,28 @@ import { useCallback, useEffect, useMemo, useRef } from "react";
|
|
|
2
2
|
import type { PowerBoard, PowerFilter, PowerHistory } from "../../../api-client/power";
|
|
3
3
|
import { isAccessDenied } from "../../../api-client/errors";
|
|
4
4
|
import { usePlanAccess } from "../../../api-client/plan-access";
|
|
5
|
-
import { ChartTableHeader, DataTableStackView, PaneStatusBody, QueryBar, useChartTableSelection, usePagedRows, usePaneNoticeFooter, usePaneStatusFooter, usePaneTabs, useQueryBarSearch, useTableLoadMore, type DataTableCell, type PaneHint, type QueryBarFilter } from "../../../components";
|
|
5
|
+
import { Button, ChartTableHeader, DataTableStackView, EmptyState, PaneStatusBody, QueryBar, useChartTableSelection, usePagedRows, usePaneNoticeFooter, usePaneStatusFooter, usePaneTabs, useQueryBarSearch, useTableLoadMore, type DataTableCell, type PaneHint, type QueryBarFilter } from "../../../components";
|
|
6
6
|
import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
|
|
7
|
-
import { useAutoRefresh, usePaneSettingValue, usePluginAppActions, usePluginPaneState } from "../../../public/react";
|
|
7
|
+
import { useAsyncResource, useAutoRefresh, usePaneSettingValue, usePluginAppActions, usePluginPaneState } from "../../../public/react";
|
|
8
8
|
import { useThemeColors } from "../../../theme/theme-context";
|
|
9
9
|
import type { PaneProps } from "../../../types/plugin";
|
|
10
10
|
import { Box, Text, TextAttributes, useRendererHost, useUiCapabilities, type ScrollBoxRenderable } from "../../../ui";
|
|
11
11
|
import { publicTickerKey } from "../../../utils/exchanges";
|
|
12
12
|
import { getSharedRegistry } from "../../registry/shared";
|
|
13
|
+
import { usePaneInstance } from "../../../state/app/context";
|
|
13
14
|
import { SignInWall } from "../cloud/auth-actions";
|
|
14
15
|
import { CLOUD_PLAN_KEY, useCloudUpgradeAction } from "../shared/cloud-upgrade";
|
|
16
|
+
import { useOpenDescription } from "../shared/open-description";
|
|
15
17
|
import { Blurred, LockedOverlay, UpgradeLabel } from "../shared/locked-rows";
|
|
16
18
|
import { isCloudSessionRequired, useResearchCloudSession } from "../shared/research-cloud-session";
|
|
17
|
-
import { loadPowerBoard, loadPowerHistory, validatePowerBoard, validatePowerHistory } from "./client";
|
|
19
|
+
import { fetchPowerTickerScopes, loadPowerBoard, loadPowerHistory, validatePowerBoard, validatePowerHistory } from "./client";
|
|
18
20
|
import { usePowerEvidence } from "./evidence";
|
|
19
21
|
import { POWER_SORT_COLUMNS, powerQuery } from "./query";
|
|
20
22
|
import { PowerDetailView } from "./detail";
|
|
23
|
+
import { PowerPricesView } from "./prices";
|
|
21
24
|
import { blendHex } from "../../../theme/colors";
|
|
22
25
|
import { missingCell, PartsBar, signedColor, toneColor, withoutQuietColumns, type StateTone } from "../shared/research-cells";
|
|
23
|
-
import { COVERAGE_COLUMNS, EXPOSURE_COLUMNS, HISTORY_COLUMNS, GENERATION_COLUMNS, UTILITY_COLUMNS, POWER_TABS, PROJECT_COLUMNS, RATE_COLUMNS, historyDate, historyId, historySeries, otherRows, powerFigures, powerRegion, powerNumber, powerPercent, powerTab, projectRows, titleCase, type PowerRow } from "./model";
|
|
26
|
+
import { COVERAGE_COLUMNS, EXPOSURE_COLUMNS, HISTORY_COLUMNS, GENERATION_COLUMNS, UTILITY_COLUMNS, POWER_TABS, PROJECT_COLUMNS, RATE_COLUMNS, historyDate, historyId, historySeries, otherRows, powerCoverageNote, powerFigures, powerLoadedSources, powerRegion, powerNumber, powerPercent, powerTab, powerTickerEmpty, projectRows, titleCase, UNMAPPED, type PowerRow, type PowerTickerScope } from "./model";
|
|
24
27
|
|
|
25
28
|
const rowId = (r: PowerRow) => r.id;
|
|
26
29
|
const isRecord = (r: PowerRow) => !r.section;
|
|
@@ -36,7 +39,11 @@ const UTILITY_SORT: typeof DEFAULT_SORT = { column: "salesMwh", direction: "desc
|
|
|
36
39
|
export function PowerPane(props: PaneProps) {
|
|
37
40
|
const session = useResearchCloudSession();
|
|
38
41
|
const access = usePlanAccess();
|
|
42
|
+
const [openingTab] = usePaneSettingValue("tab", "queue");
|
|
43
|
+
const [savedTab] = usePluginPaneState("power:tab", openingTab);
|
|
39
44
|
const scope = `${session.requestKey}:${access.hasProAccess ? "full" : "preview"}`;
|
|
45
|
+
// Prices are open data from another source: their own view, free and without the queue board.
|
|
46
|
+
if (powerTab(savedTab) === "prices") return <PowerPricesView {...props} />;
|
|
40
47
|
return <PowerView key={scope} {...props} scope={scope} needsVerification={session.needsVerification} />;
|
|
41
48
|
}
|
|
42
49
|
function PowerView({ width, height, focused, scope, needsVerification }: PaneProps & { scope: string; needsVerification: boolean }) {
|
|
@@ -44,6 +51,7 @@ function PowerView({ width, height, focused, scope, needsVerification }: PanePro
|
|
|
44
51
|
const host = useRendererHost();
|
|
45
52
|
const desktop = !!useUiCapabilities().nativePaneChrome;
|
|
46
53
|
const { createPaneFromTemplate } = usePluginAppActions();
|
|
54
|
+
const openDescription = useOpenDescription();
|
|
47
55
|
const upgrade = useCloudUpgradeAction("power");
|
|
48
56
|
const [openingTab] = usePaneSettingValue("tab", "queue");
|
|
49
57
|
const [savedTab, setTab] = usePluginPaneState("power:tab", openingTab);
|
|
@@ -58,7 +66,10 @@ function PowerView({ width, height, focused, scope, needsVerification }: PanePro
|
|
|
58
66
|
const [fuel, setFuel] = usePluginPaneState("power:fuel", initialFuel);
|
|
59
67
|
const [initialStatus] = usePaneSettingValue("status", "");
|
|
60
68
|
const [status, setStatus] = usePluginPaneState("power:status", initialStatus);
|
|
61
|
-
|
|
69
|
+
// `POWER VST` opens with the ticker in the instance params; a saved setting still wins.
|
|
70
|
+
const params = usePaneInstance()?.params;
|
|
71
|
+
const [settingSymbol] = usePaneSettingValue("symbol", "");
|
|
72
|
+
const symbol = settingSymbol || params?.symbol || "";
|
|
62
73
|
const [initialHistorical] = usePaneSettingValue("historical", false);
|
|
63
74
|
const [historical, setHistorical] = usePluginPaneState("power:historical", initialHistorical);
|
|
64
75
|
const [initialContext] = usePaneSettingValue("context", "capacity");
|
|
@@ -73,8 +84,9 @@ function PowerView({ width, height, focused, scope, needsVerification }: PanePro
|
|
|
73
84
|
const [selectedId, setSelectedId] = usePluginPaneState<string | null>(`power:selected:${tab}`, null);
|
|
74
85
|
const [openId, setOpenId] = usePluginPaneState<string | null>(`power:open:${tab}`, null);
|
|
75
86
|
const searchFocus = useQueryBarSearch();
|
|
76
|
-
const [snapshotSetting] = usePaneSettingValue<{ board: PowerBoard; history?: PowerHistory } | null>("powerSnapshot", null);
|
|
77
|
-
const snapshot = useMemo(() => { try { return snapshotSetting ? { board: validatePowerBoard(snapshotSetting.board), history: snapshotSetting.history ? validatePowerHistory(snapshotSetting.history) : undefined
|
|
87
|
+
const [snapshotSetting] = usePaneSettingValue<{ board: PowerBoard; history?: PowerHistory; tickerScopes?: PowerTickerScope[] } | null>("powerSnapshot", null);
|
|
88
|
+
const snapshot = useMemo(() => { try { return snapshotSetting ? { board: validatePowerBoard(snapshotSetting.board), history: snapshotSetting.history ? validatePowerHistory(snapshotSetting.history) : undefined,
|
|
89
|
+
tickerScopes: Array.isArray(snapshotSetting.tickerScopes) ? snapshotSetting.tickerScopes : null } : null; } catch { return null; } }, [snapshotSetting]);
|
|
78
90
|
const projectTab = ["queue", "loads", "capacity"].includes(tab);
|
|
79
91
|
const filter = useMemo(() => powerQuery({ tab, country, region, fuel, status, search, historical, context, loadClass, sourceId, from, to,
|
|
80
92
|
sort: sort.column, direction: sort.direction }, symbol), [tab, country, region, fuel, status, search, historical, context, loadClass, sourceId, from, to, sort.column, sort.direction, symbol]);
|
|
@@ -106,6 +118,14 @@ function PowerView({ width, height, focused, scope, needsVerification }: PanePro
|
|
|
106
118
|
usePowerEvidence(board, historyData?.payload, tab, rows.map((row) => row.id), pages.loading || historyPages.loading || historyPages.hasMore);
|
|
107
119
|
const locked = board ? tab === "history" ? historyData?.payload.locked ?? 0 : tab === "outcomes" ? board.locked.rates : tab === "utilities" ? board.locked.exposure : projectTab ? board.locked.projects : 0 : 0;
|
|
108
120
|
const items = useMemo(() => [...rows, ...Array.from({ length: Math.min(3, locked) }, (_, i): PowerRow => ({ id: `locked:${i}`, label: "Pro", cells: {}, locked: true }))], [rows, locked]);
|
|
121
|
+
// A ticker with nothing on this tab says where its records are instead, and opens them; a narrower filter is the filters' doing.
|
|
122
|
+
const narrowed = !!(country || region || fuel || status || search || loadClass || historical || sourceId || from || to);
|
|
123
|
+
const tickerEmpty = !!board && !!symbol && !narrowed && tab !== "coverage" && !items.length && !pages.loading && !historyPages.loading;
|
|
124
|
+
const scopesLoader = useCallback(() => fetchPowerTickerScopes(symbol, tab, context), [symbol, tab, context]);
|
|
125
|
+
const scopes = useAsyncResource(tickerEmpty && !snapshot ? scopesLoader : null);
|
|
126
|
+
const tickerScopes = snapshot ? snapshot.tickerScopes : scopes.data;
|
|
127
|
+
const tickerMessage = tickerEmpty ? powerTickerEmpty(symbol, tab, context, tickerScopes ?? []) : null;
|
|
128
|
+
const loadedSources = tickerEmpty && board ? powerLoadedSources(tab, board.coverage) : null;
|
|
109
129
|
const selected = rows.find((r) => r.id === selectedId && isRecord(r)) ?? rows.find(isRecord);
|
|
110
130
|
const openRow = rows.find((r) => r.id === openId);
|
|
111
131
|
const scrollRef = useRef<ScrollBoxRenderable>(null);
|
|
@@ -120,7 +140,7 @@ function PowerView({ width, height, focused, scope, needsVerification }: PanePro
|
|
|
120
140
|
const hints: PaneHint[] = [
|
|
121
141
|
...(sourceUrl ? [{ id: "source", key: "o", label: "pen source", onPress: () => void host.openExternal(sourceUrl) }] : []),
|
|
122
142
|
...(tickerKey ? [
|
|
123
|
-
{ id: "description", key: "d", label: "es", onPress: () =>
|
|
143
|
+
{ id: "description", key: "d", label: "es", onPress: () => openDescription(tickerKey) },
|
|
124
144
|
{ id: "financials", key: "f", label: "a", onPress: () => navigate("financial-analysis-pane") },
|
|
125
145
|
{ id: "chart", key: "g", label: "raph", onPress: () => createPaneFromTemplate("chart-composer-pane", { arg: tickerKey }) },
|
|
126
146
|
{ id: "supply", key: "s", label: "plc", onPress: () => navigate("supply-chain-pane") },
|
|
@@ -134,8 +154,9 @@ function PowerView({ width, height, focused, scope, needsVerification }: PanePro
|
|
|
134
154
|
error: board ? error : null, stale: pages.pages.some((p) => p.stale) || historyData?.stale,
|
|
135
155
|
info: [...(pages.loadingMore || historyPages.loadingMore ? [{ id: "more", parts: [{ text: "loading more", tone: "muted" as const }] }] : []),
|
|
136
156
|
...(active?.project ? [{ id: "as-of", parts: [{ text: active.project.asOf ? `as of ${active.project.asOf}` : `observed ${active.project.observedAt.slice(0, 10)}`, tone: "muted" as const }] }] : [])], hints });
|
|
157
|
+
// Unavailable queues are a coverage gap in plain words; per-source reasons live on the Coverage tab.
|
|
137
158
|
const notices = [pages.moreError?.message, historyPages.moreError?.message, ...pages.pages.map((p) => p.refreshError), historyData?.refreshError,
|
|
138
|
-
|
|
159
|
+
board && tab !== "capacity" ? powerCoverageNote(board.coverage) : null].filter((v): v is string => !!v);
|
|
139
160
|
usePaneNoticeFooter({ registrationId: "power:notices", focused, notices: [...new Set(notices)], enabled: !openRow });
|
|
140
161
|
const { strip, rows: tabRows } = usePaneTabs(board ? { tabs: [...POWER_TABS], activeValue: tab, onSelect: setTab, focused, dense: true } : null);
|
|
141
162
|
const baseColumns = tab === "capacity" && context === "generation" ? GENERATION_COLUMNS : tab === "capacity" && context === "utility" ? UTILITY_COLUMNS : projectTab ? PROJECT_COLUMNS : tab === "history" ? HISTORY_COLUMNS : tab === "outcomes" ? RATE_COLUMNS : tab === "utilities" ? EXPOSURE_COLUMNS : COVERAGE_COLUMNS;
|
|
@@ -174,6 +195,8 @@ function PowerView({ width, height, focused, scope, needsVerification }: PanePro
|
|
|
174
195
|
{ id: "operating", value: e.operatingMw, color: colors.positive }, { id: "approved", value: Math.max(0, e.approvedMw - e.operatingMw), color: blendHex(colors.bg, colors.borderFocused, 0.55) },
|
|
175
196
|
{ id: "requested", value: Math.max(0, e.requestedMw - Math.max(e.approvedMw, e.operatingMw)), color: blendHex(colors.bg, colors.textDim, 0.4) }]} /> }; }
|
|
176
197
|
const value = row.cells[column.id];
|
|
198
|
+
// The ticker map: a record no listed company is mapped to says so.
|
|
199
|
+
if (column.id === "ticker" && value == null && (row.project || row.exposure)) return { text: UNMAPPED, color: colors.textMuted };
|
|
177
200
|
if (value == null || value === "") return missingCell(colors);
|
|
178
201
|
const text = typeof value === "number" ? /Rate$/.test(column.id) ? powerPercent(value) : powerNumber(value) : String(value);
|
|
179
202
|
const numeric = typeof value === "number" && /Rate$/.test(column.id) ? value * 100 : value;
|
|
@@ -202,6 +225,11 @@ function PowerView({ width, height, focused, scope, needsVerification }: PanePro
|
|
|
202
225
|
chart={tab === "history" ? { series, ...chartLink, formatValue: (n) => `${powerNumber(n)} MW`, loading: historyPages.loading || historyPages.hasMore && !historyPages.moreError,
|
|
203
226
|
empty: series.length ? undefined : historyPages.moreError ? "Full history could not load. Refresh to retry." : historyData?.payload.access === "preview" ? "Full history is available with Pro." : "History is accumulating from recorded snapshots.", remoteKind: "power-history" } : null} />}
|
|
204
227
|
emptyStateTitle={tab === "history" ? historyPages.error?.message ?? "No recorded history for these filters." : "No reported records match these filters."}
|
|
228
|
+
emptyContent={tickerMessage ? <Box paddingX={1} paddingY={1}>
|
|
229
|
+
<EmptyState title={tickerMessage.title} hint={[tickerMessage.hint, loadedSources ? `Sources loaded now: ${loadedSources}.` : null].filter(Boolean).join(" ") || undefined}
|
|
230
|
+
actions={tickerMessage.found.length ? tickerMessage.found.map((scope) => <Button key={`${scope.tab}:${scope.context}`} label={`Open ${scope.view}`}
|
|
231
|
+
onPress={() => { if (scope.tab === "capacity") setContext(scope.context); setTab(scope.tab); }} />) : undefined} />
|
|
232
|
+
</Box> : undefined}
|
|
205
233
|
bodyAfter={locked && desktop ? <LockedOverlay rows={Math.min(3, locked)} text="Unlock full power data with Pro" onPress={upgrade} role="power-upgrade" /> : undefined}
|
|
206
234
|
/> : null}
|
|
207
235
|
</PaneStatusBody>
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import { apiClient } from "../../../api-client";
|
|
2
|
+
import type { CarbonBoard, CarbonHistory, EuropePowerBoard, EuropePowerHistory, PowerPriceRow } from "../../../api-client/europe-energy";
|
|
3
|
+
import { createPluginCache } from "../../../data/plugin-cache";
|
|
4
|
+
import { cachedCloudResource, loadCloudResource, unavailableOnServer } from "../shared/cloud-resource";
|
|
5
|
+
|
|
6
|
+
/** What the tab and the series say while the server has no prices to serve: before the backend ships, or before its first read. */
|
|
7
|
+
export const POWER_PRICES_NOT_AVAILABLE = "European power prices are not available yet.";
|
|
8
|
+
const CARBON_PRICE_NOT_AVAILABLE = "The EU carbon auction price is not available yet.";
|
|
9
|
+
|
|
10
|
+
// Daily data, rewritten once a day: a quarter hour is soon enough to catch it.
|
|
11
|
+
const policy = { staleMs: 15 * 60_000, expireMs: 14 * 86_400_000 };
|
|
12
|
+
export const powerPricesCache = createPluginCache<EuropePowerBoard>({ kind: "europe-power-board", source: "gloom-cloud", schemaVersion: 1, policy });
|
|
13
|
+
export const powerPriceHistoryCache = createPluginCache<EuropePowerHistory>({ kind: "europe-power-history", source: "gloom-cloud", schemaVersion: 1, policy });
|
|
14
|
+
export const carbonPriceCache = createPluginCache<CarbonBoard>({ kind: "europe-carbon-board", source: "gloom-cloud", schemaVersion: 1, policy });
|
|
15
|
+
export const carbonHistoryCache = createPluginCache<CarbonHistory>({ kind: "europe-carbon-history", source: "gloom-cloud", schemaVersion: 1, policy });
|
|
16
|
+
|
|
17
|
+
const record = (v: unknown): v is Record<string, any> => !!v && typeof v === "object" && !Array.isArray(v);
|
|
18
|
+
const text = (v: unknown): v is string => typeof v === "string";
|
|
19
|
+
const finite = (v: unknown): v is number => typeof v === "number" && Number.isFinite(v);
|
|
20
|
+
const date = (v: unknown) => text(v) && /^\d{4}-\d{2}-\d{2}$/.test(v) && new Date(`${v}T00:00:00Z`).toISOString().slice(0, 10) === v;
|
|
21
|
+
const dateOrNull = (v: unknown) => v === null || date(v);
|
|
22
|
+
const finiteOrNull = (v: unknown) => v === null || finite(v);
|
|
23
|
+
const instantOrNull = (v: unknown) => v === null || text(v) && Number.isFinite(Date.parse(v));
|
|
24
|
+
const source = (v: unknown) => record(v) && text(v.name) && text(v.licence) && text(v.url);
|
|
25
|
+
const publisher = (v: unknown) => record(v) && text(v.name) && text(v.url);
|
|
26
|
+
const nullableText = (v: unknown) => v === null || text(v);
|
|
27
|
+
const nonnegativeOrNull = (v: unknown) => v === null || finite(v) && v >= 0;
|
|
28
|
+
const points = (v: unknown) => Array.isArray(v) && v.every((p) => record(p) && date(p.date) && finite(p.value));
|
|
29
|
+
|
|
30
|
+
function validRow(row: PowerPriceRow, code = /^[A-Z]{2}$/): boolean {
|
|
31
|
+
return record(row) && code.test(row.code) && text(row.name) && dateOrNull(row.date) && finiteOrNull(row.price)
|
|
32
|
+
&& (row.price == null) === (row.date == null) && dateOrNull(row.previousDate) && dateOrNull(row.yearAgoDate)
|
|
33
|
+
&& [row.change, row.average7, row.average30, row.yearAgo, row.low52w, row.high52w].every(finiteOrNull) && points(row.trail);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/** A board that breaks the contract is refused whole rather than drawn with holes in it. */
|
|
37
|
+
export function validatePowerPrices(data: EuropePowerBoard): EuropePowerBoard {
|
|
38
|
+
if (!record(data) || !text(data.generatedAt) || !instantOrNull(data.generatedAt) || !["available", "unavailable"].includes(data.status)
|
|
39
|
+
|| data.market !== "day-ahead" || data.unit !== "EUR/MWh" || !source(data.source)
|
|
40
|
+
|| !instantOrNull(data.checkedAt) || !instantOrNull(data.publishedAt)
|
|
41
|
+
|| !Array.isArray(data.gaps) || !data.gaps.every(text)
|
|
42
|
+
|| !Array.isArray(data.rows) || !data.rows.every((row) => validRow(row)) || new Set(data.rows.map((row) => row.code)).size !== data.rows.length) {
|
|
43
|
+
throw new Error("The server returned unreadable power prices.");
|
|
44
|
+
}
|
|
45
|
+
return data;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function validateHistory(data: EuropePowerHistory): EuropePowerHistory {
|
|
49
|
+
if (!record(data) || !/^[A-Z]{2}$/.test(data.code) || !text(data.name) || data.unit !== "EUR/MWh" || !source(data.source) || !points(data.points)) {
|
|
50
|
+
throw new Error("The server returned unreadable power price history.");
|
|
51
|
+
}
|
|
52
|
+
return data;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/** An EUA board that breaks the contract is refused whole. */
|
|
56
|
+
export function validateCarbonPrice(data: CarbonBoard): CarbonBoard {
|
|
57
|
+
const row = data?.row;
|
|
58
|
+
if (!record(data) || !text(data.generatedAt) || !instantOrNull(data.generatedAt) || !["available", "unavailable"].includes(data.status)
|
|
59
|
+
|| data.market !== "primary auction" || data.unit !== "EUR/tCO2" || !publisher(data.source)
|
|
60
|
+
|| !instantOrNull(data.checkedAt) || !instantOrNull(data.publishedAt) || !Array.isArray(data.gaps) || !data.gaps.every(text)
|
|
61
|
+
|| !record(row) || !validRow(row, /^EUA$/) || !nonnegativeOrNull(row.volume) || !nonnegativeOrNull(row.auctions)
|
|
62
|
+
|| !Array.isArray(data.auctions) || !data.auctions.every((a) => record(a) && date(a.date) && text(a.auction) && text(a.contract) && text(a.status)
|
|
63
|
+
&& nullableText(a.zone) && [a.price, a.minBid, a.maxBid, a.meanBid, a.medianBid].every(finiteOrNull) && finite(a.volume)
|
|
64
|
+
&& [a.coverRatio, a.bidders, a.successfulBidders].every(nonnegativeOrNull) && typeof a.aviation === "boolean")) {
|
|
65
|
+
throw new Error("The server returned an unreadable EU carbon price.");
|
|
66
|
+
}
|
|
67
|
+
return data;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function validateCarbonHistory(data: CarbonHistory): CarbonHistory {
|
|
71
|
+
if (!record(data) || data.code !== "EUA" || data.unit !== "EUR/tCO2" || !publisher(data.source) || !points(data.points)) {
|
|
72
|
+
throw new Error("The server returned an unreadable EU carbon price history.");
|
|
73
|
+
}
|
|
74
|
+
return data;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
type PricesApi = Pick<typeof apiClient, "getCloudEuropePower" | "getCloudEuropePowerHistory" | "getCloudEuropeCarbon" | "getCloudEuropeCarbonHistory">;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* The board, or the not-available message when the server does not serve the
|
|
81
|
+
* route yet (404), cannot read its tables (503) or has no days stored.
|
|
82
|
+
*/
|
|
83
|
+
export async function fetchPowerPrices(client: Pick<PricesApi, "getCloudEuropePower"> = apiClient): Promise<EuropePowerBoard> {
|
|
84
|
+
let board: EuropePowerBoard;
|
|
85
|
+
try {
|
|
86
|
+
board = validatePowerPrices(await client.getCloudEuropePower());
|
|
87
|
+
} catch (error) {
|
|
88
|
+
throw unavailableOnServer(error, POWER_PRICES_NOT_AVAILABLE, [404, 503]);
|
|
89
|
+
}
|
|
90
|
+
if (board.status === "unavailable") throw new Error(POWER_PRICES_NOT_AVAILABLE);
|
|
91
|
+
return board;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** One market's days from `from`, all of them without one. */
|
|
95
|
+
export async function fetchPowerPriceHistory(
|
|
96
|
+
code: string, from?: string | null, client: Pick<PricesApi, "getCloudEuropePowerHistory"> = apiClient,
|
|
97
|
+
): Promise<EuropePowerHistory> {
|
|
98
|
+
try {
|
|
99
|
+
return validateHistory(await client.getCloudEuropePowerHistory(code, from));
|
|
100
|
+
} catch (error) {
|
|
101
|
+
throw unavailableOnServer(error, POWER_PRICES_NOT_AVAILABLE, [404, 503]);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The EUA board, or the not-available message before the server serves it or has an auction stored. */
|
|
106
|
+
export async function fetchCarbonPrice(client: Pick<PricesApi, "getCloudEuropeCarbon"> = apiClient): Promise<CarbonBoard> {
|
|
107
|
+
let board: CarbonBoard;
|
|
108
|
+
try {
|
|
109
|
+
board = validateCarbonPrice(await client.getCloudEuropeCarbon());
|
|
110
|
+
} catch (error) {
|
|
111
|
+
throw unavailableOnServer(error, CARBON_PRICE_NOT_AVAILABLE, [404, 503]);
|
|
112
|
+
}
|
|
113
|
+
if (board.status === "unavailable") throw new Error(CARBON_PRICE_NOT_AVAILABLE);
|
|
114
|
+
return board;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/** The daily EUA price from `from`, every auction day since 2020 without one. */
|
|
118
|
+
export async function fetchCarbonHistory(
|
|
119
|
+
from?: string | null, client: Pick<PricesApi, "getCloudEuropeCarbonHistory"> = apiClient,
|
|
120
|
+
): Promise<CarbonHistory> {
|
|
121
|
+
try {
|
|
122
|
+
return validateCarbonHistory(await client.getCloudEuropeCarbonHistory(from));
|
|
123
|
+
} catch (error) {
|
|
124
|
+
throw unavailableOnServer(error, CARBON_PRICE_NOT_AVAILABLE, [404, 503]);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export const getCachedPowerPrices = () => cachedCloudResource(powerPricesCache, "board", validatePowerPrices);
|
|
129
|
+
export const loadPowerPrices = (force = false) =>
|
|
130
|
+
loadCloudResource(powerPricesCache, "board", () => fetchPowerPrices(), { force, validate: validatePowerPrices });
|
|
131
|
+
export const getCachedCarbonPrice = () => cachedCloudResource(carbonPriceCache, "board", validateCarbonPrice);
|
|
132
|
+
export const loadCarbonPrice = (force = false) =>
|
|
133
|
+
loadCloudResource(carbonPriceCache, "board", () => fetchCarbonPrice(), { force, validate: validateCarbonPrice });
|
|
134
|
+
/** Every auction day of the EUA since 2020. */
|
|
135
|
+
export const loadCarbonHistory = (force = false) =>
|
|
136
|
+
loadCloudResource(carbonHistoryCache, "EUA", () => fetchCarbonHistory(), { force, validate: validateCarbonHistory });
|
|
137
|
+
/** The whole history of one market, cached per market. */
|
|
138
|
+
export const loadPowerPriceHistory = (code: string, force = false) =>
|
|
139
|
+
loadCloudResource(powerPriceHistoryCache, code, () => fetchPowerPriceHistory(code), { force, validate: validateHistory });
|