gloomberb 0.16.5 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +3 -1
- package/src/api-client/central-bank-rates.ts +6 -1
- package/src/api-client/chat-pane-link.ts +47 -0
- package/src/api-client/chat.ts +35 -1
- package/src/api-client/credit-boards.ts +2 -0
- package/src/api-client/data.ts +85 -1
- package/src/api-client/equity-screener.ts +3 -0
- package/src/api-client/europe-energy.ts +197 -0
- package/src/api-client/index.ts +15 -0
- package/src/api-client/letters.ts +41 -0
- package/src/api-client/market-discovery.ts +3 -0
- package/src/api-client/normalizers.ts +68 -6
- package/src/api-client/organization-tickers.ts +44 -0
- package/src/api-client/perps.ts +8 -0
- package/src/api-client/request.ts +11 -2
- package/src/api-client/socket.ts +29 -0
- package/src/api-client/themes.ts +22 -1
- package/src/api-client/types.ts +88 -2
- package/src/app/global-shortcuts.ts +35 -1
- 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 +9 -3
- package/src/app/runtime/desktop-deeplink.ts +58 -20
- package/src/app/runtime/startup.ts +1 -1
- package/src/app.tsx +6 -0
- package/src/architecture/core-builtin-allowlist.json +5 -14
- package/src/architecture/dom-cell-metrics-allowlist.json +28 -0
- package/src/ask-gloom/pane-context.ts +118 -0
- package/src/ask-gloom/pane-request.ts +70 -0
- package/src/ask-gloom/pane-text.ts +201 -0
- package/src/capabilities/chart-series.ts +8 -1
- 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 +28 -2
- package/src/cli/commands/fred-series.ts +155 -4
- package/src/cli/commands/market.ts +175 -35
- package/src/cli/commands/overview.ts +62 -20
- package/src/cli/commands/search.ts +254 -23
- package/src/cli/commands/ticker.ts +137 -24
- package/src/cli/desktop-pane-shot-view.ts +114 -0
- package/src/cli/desktop-pane-shot.ts +53 -37
- package/src/cli/entry.ts +7 -0
- package/src/cli/errors.ts +7 -5
- package/src/cli/exit-codes.ts +8 -0
- package/src/cli/export-source.ts +101 -0
- package/src/cli/freshness.ts +18 -3
- package/src/cli/glossary.ts +79 -8
- package/src/cli/help.ts +10 -0
- package/src/cli/helpers.ts +2 -5
- package/src/cli/history-rows.ts +244 -13
- package/src/cli/index.ts +9 -3
- package/src/cli/known-gaps.ts +76 -8
- package/src/cli/listing-arg.ts +201 -90
- package/src/cli/listing-suggestion.ts +159 -0
- package/src/cli/native-smoke.ts +13 -0
- package/src/cli/not-a-ticker.ts +24 -4
- package/src/cli/pane-functions/access-gate.ts +8 -2
- package/src/cli/pane-functions/capabilities.ts +17 -9
- package/src/cli/pane-functions/catalog.ts +183 -39
- package/src/cli/pane-functions/data.ts +30 -0
- package/src/cli/pane-functions/dom.ts +44 -14
- package/src/cli/pane-functions/freshness.ts +48 -24
- package/src/cli/pane-functions/function-help.ts +66 -12
- package/src/cli/pane-functions/headless.ts +81 -22
- package/src/cli/pane-functions/index.ts +90 -18
- package/src/cli/pane-functions/options.ts +8 -0
- package/src/cli/pane-functions/report-notices.ts +6 -3
- package/src/cli/pane-functions/report.ts +16 -2
- package/src/cli/pane-functions/resolver.ts +32 -11
- package/src/cli/pane-functions/screenshot.ts +145 -9
- package/src/cli/perpetual-contract.ts +60 -0
- package/src/cli/registry.ts +3 -1
- package/src/cli/report-tables.ts +53 -13
- package/src/cli/result.ts +91 -17
- package/src/cli/series-search.ts +111 -0
- package/src/cli/staged-tickers.ts +1 -1
- package/src/cli/symbol-args.ts +18 -0
- package/src/cli/symbol-list.ts +1 -19
- package/src/cli/symbol-rows.ts +171 -0
- package/src/cli/ticker-resolution.ts +99 -8
- package/src/components/chart/composite/axis-overlays.tsx +10 -1
- package/src/components/chart/composite/composite-chart.tsx +24 -15
- 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 +57 -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 +6 -8
- package/src/components/chart/composite/price-axis-labels.tsx +1 -0
- 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/list/model.ts +2 -0
- package/src/components/command-bar/pane-templates/items.ts +1 -1
- package/src/components/command-bar/routes/root/plugin-install.ts +4 -4
- package/src/components/command-bar/routes/ticker-search/actions.ts +1 -0
- package/src/components/command-bar/routes/ticker-search/results.ts +3 -1
- package/src/components/command-bar/routing/actions.ts +1 -1
- package/src/components/command-bar/workflow/ops.ts +8 -97
- package/src/components/command-bar/workflow/pane-reuse.ts +177 -0
- package/src/components/command-bar/workflow/tickers.ts +35 -29
- package/src/components/data-table/view.tsx +1 -0
- package/src/components/feed-data-table/stack-view.tsx +18 -42
- package/src/components/layout/detached-pane-shell.tsx +59 -39
- package/src/components/layout/floating-pane.tsx +7 -1
- package/src/components/layout/header.tsx +5 -0
- package/src/components/layout/missing-pane.tsx +5 -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/footer/keyboard.ts +3 -0
- package/src/components/layout/pane/footer/model.ts +7 -1
- package/src/components/layout/pane/header.tsx +26 -0
- package/src/components/layout/pane/index.tsx +9 -1
- package/src/components/layout/pane/sidebar.tsx +10 -11
- package/src/components/layout/shell/cursor-occlusion.ts +2 -1
- package/src/components/layout/shell/index.tsx +62 -60
- package/src/components/layout/shell/menu.ts +9 -1
- package/src/components/layout/shell/pane/actions.ts +32 -10
- package/src/components/layout/shell/pane/ask.ts +136 -0
- package/src/components/layout/shell/pane/capture.ts +136 -0
- package/src/components/layout/shell/pane/layers.tsx +26 -5
- package/src/components/layout/shell/pane/management-shortcuts.ts +46 -1
- package/src/components/layout/shell/shortcuts.ts +21 -4
- package/src/components/markdown-text.tsx +37 -6
- package/src/components/metric-treemap/heat-surface.tsx +1 -3
- package/src/components/reader/article-reader.tsx +467 -0
- package/src/components/reader/article-text.ts +527 -0
- package/src/components/reader/detail-reader.tsx +210 -0
- package/src/components/reader/reader-text.tsx +239 -0
- package/src/components/table-view-shared.tsx +8 -4
- package/src/components/ticker/badge/list.tsx +15 -4
- package/src/components/ticker/list-table-view.tsx +19 -1
- package/src/components/ui/choice-dialog.tsx +3 -0
- package/src/components/ui/data-table/index.tsx +1 -0
- package/src/components/ui/data-table/types.ts +7 -0
- package/src/components/ui/detail-scroll-body.tsx +31 -4
- package/src/components/ui/display.tsx +46 -1
- package/src/components/ui/icon.tsx +14 -1
- package/src/components/ui/list-view.tsx +3 -1
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +18 -1
- package/src/components/ui/select-button.tsx +3 -0
- package/src/data/config/store/fold-panes.ts +202 -11
- package/src/data/config/store/migrations.ts +20 -1
- package/src/data/config/store/portfolio-as-of-column.ts +57 -0
- package/src/docs/snapshot.json +191 -23
- package/src/i18n/es.ts +18 -1
- package/src/i18n/ja.ts +18 -1
- package/src/i18n/ko.ts +18 -1
- package/src/i18n/zh-cn.ts +18 -1
- package/src/i18n/zh-tw.ts +18 -1
- package/src/layout/desk-keys.ts +7 -0
- package/src/layout/desks.ts +2 -2
- package/src/layout-marketplace/gallery.tsx +1 -1
- package/src/market-data/market/freshness.ts +3 -2
- package/src/market-data/market/session-close-note.ts +3 -2
- package/src/market-data/market/status.ts +67 -6
- package/src/market-data/quotes/contributions.ts +17 -0
- package/src/market-data/quotes/metadata.ts +12 -0
- package/src/market-data/quotes/regular-session-valuation.ts +131 -0
- package/src/market-data/quotes/resolution.ts +7 -0
- package/src/market-data/snapshot-provider.ts +6 -3
- package/src/notifications/app-badge.ts +30 -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/calendar-tab.tsx +1 -1
- package/src/plugins/builtin/account-management/form-components.tsx +53 -1
- package/src/plugins/builtin/account-management/index.tsx +1 -0
- package/src/plugins/builtin/account-management/keyboard.ts +5 -0
- package/src/plugins/builtin/account-management/model.ts +27 -0
- package/src/plugins/builtin/account-management/pane.tsx +62 -2
- package/src/plugins/builtin/alerts/catalysts.ts +1 -1
- package/src/plugins/builtin/alerts/cli.ts +6 -4
- package/src/plugins/builtin/alerts/focus.ts +27 -0
- package/src/plugins/builtin/alerts/index.tsx +6 -2
- package/src/plugins/builtin/alerts/pane.tsx +19 -1
- package/src/plugins/builtin/analytics/index.tsx +5 -2
- package/src/plugins/builtin/analytics/risk-headless.ts +30 -15
- package/src/plugins/builtin/attention/headless.ts +1 -1
- package/src/plugins/builtin/awards/headless.ts +88 -18
- package/src/plugins/builtin/awards/model.ts +41 -1
- package/src/plugins/builtin/awards/pane.tsx +48 -30
- package/src/plugins/builtin/backtest/headless.ts +2 -2
- package/src/plugins/builtin/backtest/pane.tsx +2 -1
- package/src/plugins/builtin/bond-calculator/index.tsx +3 -5
- package/src/plugins/builtin/broker-manager/index.tsx +1 -0
- package/src/plugins/builtin/buildout/detail/company.tsx +7 -4
- package/src/plugins/builtin/builtin-plugin-meta.ts +5 -4
- package/src/plugins/builtin/catalysts/headless.ts +1 -1
- package/src/plugins/builtin/catalysts/model.ts +92 -10
- package/src/plugins/builtin/catalysts/pane.tsx +6 -4
- package/src/plugins/builtin/cds/client.ts +11 -31
- package/src/plugins/builtin/cds/headless.ts +5 -2
- package/src/plugins/builtin/cds/index.tsx +21 -30
- package/src/plugins/builtin/cds/model.ts +21 -1
- package/src/plugins/builtin/cds/pane.tsx +6 -4
- 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 +1 -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 +19 -25
- package/src/plugins/builtin/changelog/index.tsx +11 -12
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
- package/src/plugins/builtin/chart-composer/headless-schema.ts +3 -1
- package/src/plugins/builtin/chart-composer/headless.ts +151 -9
- package/src/plugins/builtin/chart-composer/index.tsx +6 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +84 -40
- package/src/plugins/builtin/chart-composer/session-history.ts +20 -5
- package/src/plugins/builtin/chat/command-bar.ts +6 -8
- package/src/plugins/builtin/chat/content/composer-runtime.ts +6 -1
- package/src/plugins/builtin/chat/content/composer.tsx +15 -2
- package/src/plugins/builtin/chat/content/footer.tsx +27 -5
- package/src/plugins/builtin/chat/content/handle-prompt.tsx +326 -0
- package/src/plugins/builtin/chat/content/index.tsx +201 -13
- package/src/plugins/builtin/chat/content/layout-metrics.ts +4 -0
- package/src/plugins/builtin/chat/content/new-messages.tsx +89 -0
- package/src/plugins/builtin/chat/content/open-targets.ts +20 -4
- package/src/plugins/builtin/chat/content/row-activity.ts +132 -0
- package/src/plugins/builtin/chat/content/scroll.ts +133 -28
- package/src/plugins/builtin/chat/content/shortcuts.ts +2 -2
- package/src/plugins/builtin/chat/content/transcript.tsx +155 -79
- 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 +121 -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/handle-prompt.ts +128 -0
- package/src/plugins/builtin/chat/index.tsx +66 -25
- package/src/plugins/builtin/chat/layout.ts +84 -18
- package/src/plugins/builtin/chat/message/desktop.tsx +111 -14
- package/src/plugins/builtin/chat/message/header.tsx +81 -21
- package/src/plugins/builtin/chat/message/pane-chip.tsx +223 -0
- package/src/plugins/builtin/chat/message/person-icons.tsx +53 -0
- package/src/plugins/builtin/chat/message/profile-popover.tsx +77 -9
- package/src/plugins/builtin/chat/message/reactions.tsx +210 -0
- package/src/plugins/builtin/chat/message/render-state.ts +33 -4
- package/src/plugins/builtin/chat/message/terminal.tsx +72 -8
- package/src/plugins/builtin/chat/message/types.ts +15 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +149 -0
- package/src/plugins/builtin/chat/pane-links/open.ts +29 -0
- package/src/plugins/builtin/chat/pane-links/previews.ts +119 -0
- package/src/plugins/builtin/chat/pane-state.ts +5 -8
- package/src/plugins/builtin/chat/people.ts +202 -0
- package/src/plugins/builtin/chat/profile-popover.ts +18 -2
- 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 +7 -9
- package/src/plugins/builtin/chat/unread-badge.ts +65 -0
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +151 -27
- package/src/plugins/builtin/chat/unread-inbox.ts +22 -0
- package/src/plugins/builtin/clinical-trials/client.ts +26 -1
- package/src/plugins/builtin/clinical-trials/index.tsx +10 -2
- package/src/plugins/builtin/clinical-trials/model.ts +269 -0
- package/src/plugins/builtin/clinical-trials/pane.tsx +247 -62
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +156 -0
- package/src/plugins/builtin/cloud/askg/attached-pane.tsx +69 -0
- 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/mcp-connect/dialog.tsx +1 -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/thesis/module.tsx +1 -0
- package/src/plugins/builtin/comment-letters/pane.tsx +30 -11
- package/src/plugins/builtin/commodities-energy/index.tsx +179 -0
- package/src/plugins/builtin/commodity-balances/client.ts +208 -0
- package/src/plugins/builtin/commodity-balances/headless.ts +66 -0
- package/src/plugins/builtin/commodity-balances/index.tsx +32 -0
- package/src/plugins/builtin/commodity-balances/model.ts +261 -0
- package/src/plugins/builtin/commodity-balances/pane.tsx +227 -0
- package/src/plugins/builtin/company-attention/headless.ts +2 -2
- 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 +13 -14
- package/src/plugins/builtin/composite-plugins.ts +26 -1
- package/src/plugins/builtin/connections/index.ts +1 -0
- package/src/plugins/builtin/cot/headless.ts +1 -1
- 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 +21 -42
- package/src/plugins/builtin/credit-boards/model.ts +11 -3
- 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 +20 -22
- package/src/plugins/builtin/credit-conditions/model.ts +19 -11
- package/src/plugins/builtin/credit-conditions/series.ts +8 -18
- package/src/plugins/builtin/credit-documents/headless.ts +2 -2
- package/src/plugins/builtin/credit-documents/index.tsx +27 -10
- package/src/plugins/builtin/debt-maturities/headless.ts +17 -11
- package/src/plugins/builtin/debt-maturities/index.tsx +3 -17
- package/src/plugins/builtin/debug/index.tsx +2 -2
- package/src/plugins/builtin/dividend-yield/client.ts +6 -1
- package/src/plugins/builtin/dividend-yield/headless.ts +8 -3
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
- package/src/plugins/builtin/dividend-yield/pane.tsx +6 -2
- package/src/plugins/builtin/dividend-yield/provider-client.ts +3 -1
- package/src/plugins/builtin/dividend-yield/types.ts +2 -0
- package/src/plugins/builtin/docs/blocks.tsx +4 -3
- package/src/plugins/builtin/docs/pane.tsx +16 -8
- package/src/plugins/builtin/doe/headless.ts +12 -8
- package/src/plugins/builtin/earnings/index.tsx +11 -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-calls/pane.tsx +2 -12
- package/src/plugins/builtin/earnings-ripple/index.tsx +2 -0
- package/src/plugins/builtin/econ/fred-catalog.ts +9 -0
- package/src/plugins/builtin/equity-screener/client.ts +1 -1
- package/src/plugins/builtin/equity-screener/headless.ts +39 -5
- package/src/plugins/builtin/equity-screener/model.ts +121 -68
- package/src/plugins/builtin/estimate-revisions/headless.ts +3 -3
- package/src/plugins/builtin/executives/pane.tsx +3 -1
- package/src/plugins/builtin/exposure/drivers.ts +13 -0
- package/src/plugins/builtin/exposure/headless.ts +205 -13
- 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 +11 -11
- package/src/plugins/builtin/fear-greed/index.tsx +14 -10
- package/src/plugins/builtin/filing-events/index.tsx +20 -0
- package/src/plugins/builtin/fixed-income/index.tsx +46 -0
- package/src/plugins/builtin/fixed-income/model.ts +27 -0
- package/src/plugins/builtin/fixed-income/pane.tsx +162 -0
- package/src/plugins/builtin/fixed-income/shortcut.ts +52 -0
- package/src/plugins/builtin/futures/index.tsx +24 -7
- package/src/plugins/builtin/futures/series.ts +23 -2
- package/src/plugins/builtin/futures/use-front-returns.ts +3 -2
- package/src/plugins/builtin/futures-curve/headless.ts +1 -1
- package/src/plugins/builtin/futures-curve/index.tsx +1 -1
- package/src/plugins/builtin/futures-curve/pane.tsx +6 -4
- 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 +3 -3
- package/src/plugins/builtin/gpu/headless.ts +4 -4
- package/src/plugins/builtin/help/function-index.ts +2 -0
- package/src/plugins/builtin/help/index.tsx +14 -1
- package/src/plugins/builtin/help/shortcut-table.tsx +4 -2
- package/src/plugins/builtin/insider/detail.tsx +20 -12
- package/src/plugins/builtin/insider/index.tsx +2 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +1 -1
- package/src/plugins/builtin/iv-history/client.ts +6 -2
- package/src/plugins/builtin/iv-history/format.ts +9 -0
- package/src/plugins/builtin/iv-history/headless.ts +50 -18
- package/src/plugins/builtin/iv-history/index.tsx +3 -0
- package/src/plugins/builtin/iv-history/model.ts +35 -4
- package/src/plugins/builtin/iv-history/screen-pane.tsx +13 -5
- package/src/plugins/builtin/jobs/headless.ts +6 -8
- package/src/plugins/builtin/jodi/client.ts +220 -0
- package/src/plugins/builtin/jodi/headless.ts +63 -0
- package/src/plugins/builtin/jodi/index.tsx +32 -0
- package/src/plugins/builtin/jodi/model.ts +75 -0
- package/src/plugins/builtin/jodi/pane.tsx +141 -0
- package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
- package/src/plugins/builtin/market-heatmap/evidence.ts +13 -4
- package/src/plugins/builtin/market-heatmap/headless.ts +22 -7
- package/src/plugins/builtin/market-heatmap/index.tsx +47 -23
- package/src/plugins/builtin/market-heatmap/live.ts +2 -1
- package/src/plugins/builtin/market-heatmap/portfolio.ts +67 -21
- package/src/plugins/builtin/market-movers/table.ts +2 -2
- package/src/plugins/builtin/market-valuation/align.ts +10 -9
- package/src/plugins/builtin/members/headless.ts +1 -1
- package/src/plugins/builtin/members/pane.tsx +3 -2
- package/src/plugins/builtin/mna/distress/headless.ts +4 -4
- package/src/plugins/builtin/mna/distress/model.ts +52 -1
- package/src/plugins/builtin/mna/distress/pane.tsx +17 -12
- package/src/plugins/builtin/mna/headless.ts +3 -3
- package/src/plugins/builtin/mna/index.tsx +2 -0
- package/src/plugins/builtin/money-markets/headless.ts +5 -4
- package/src/plugins/builtin/news/index.tsx +0 -2
- package/src/plugins/builtin/news/wire/index.tsx +2 -2
- package/src/plugins/builtin/news/wire/industry-pane.tsx +28 -14
- package/src/plugins/builtin/news/wire/letters/client.ts +108 -0
- package/src/plugins/builtin/news/wire/letters/model.ts +69 -0
- package/src/plugins/builtin/news/wire/letters/pane.tsx +155 -0
- package/src/plugins/builtin/news/wire/mutes.ts +2 -1
- 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/news/wire/news/query-presets.ts +4 -1
- package/src/plugins/builtin/notes/index.tsx +1 -0
- package/src/plugins/builtin/openfda/client.ts +123 -15
- package/src/plugins/builtin/openfda/model.ts +163 -0
- package/src/plugins/builtin/openfda/pane.tsx +109 -65
- package/src/plugins/builtin/openfda/types.ts +25 -0
- package/src/plugins/builtin/options/index.tsx +3 -0
- package/src/plugins/builtin/options/view.tsx +1 -1
- package/src/plugins/builtin/options-positioning/headless.ts +3 -3
- package/src/plugins/builtin/pe-band/model.ts +3 -2
- package/src/plugins/builtin/perps/client.ts +50 -10
- package/src/plugins/builtin/perps/equity-row.tsx +29 -16
- package/src/plugins/builtin/perps/headless.ts +2 -2
- package/src/plugins/builtin/perps/index.tsx +5 -3
- package/src/plugins/builtin/perps/list-perpetuals.ts +118 -0
- package/src/plugins/builtin/perps/open-market.ts +15 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +1 -1
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +7 -7
- package/src/plugins/builtin/plugin-module.ts +3 -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 +24 -136
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +78 -19
- package/src/plugins/builtin/portfolio-list/cli/render.ts +249 -52
- package/src/plugins/builtin/portfolio-list/cli/shared.ts +44 -12
- package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +21 -7
- package/src/plugins/builtin/portfolio-list/column-values.ts +103 -25
- package/src/plugins/builtin/portfolio-list/headless.ts +51 -15
- package/src/plugins/builtin/portfolio-list/index.tsx +5 -1
- package/src/plugins/builtin/portfolio-list/live-valuation.ts +25 -13
- 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 +46 -8
- package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +16 -10
- 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 +8 -3
- package/src/plugins/builtin/portfolio-list/table.tsx +30 -6
- package/src/plugins/builtin/power/evidence.ts +86 -8
- package/src/plugins/builtin/power/gas-client.ts +110 -0
- package/src/plugins/builtin/power/gas-model.ts +194 -0
- package/src/plugins/builtin/power/gas.tsx +145 -0
- package/src/plugins/builtin/power/headless.ts +9 -2
- package/src/plugins/builtin/power/index.tsx +35 -3
- package/src/plugins/builtin/power/model.ts +2 -2
- package/src/plugins/builtin/power/pane.tsx +7 -3
- package/src/plugins/builtin/power/prices-client.ts +139 -0
- package/src/plugins/builtin/power/prices-headless.ts +205 -0
- package/src/plugins/builtin/power/prices-model.ts +103 -0
- package/src/plugins/builtin/power/prices.tsx +202 -0
- package/src/plugins/builtin/primary-dealers/client.ts +108 -0
- package/src/plugins/builtin/primary-dealers/headless.ts +53 -0
- package/src/plugins/builtin/primary-dealers/index.tsx +17 -0
- package/src/plugins/builtin/primary-dealers/model.ts +206 -0
- package/src/plugins/builtin/primary-dealers/pane.tsx +149 -0
- package/src/plugins/builtin/rate-path/headless.ts +38 -18
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +3 -0
- package/src/plugins/builtin/research/analyst-model.ts +9 -5
- package/src/plugins/builtin/research/headless-schema.ts +2 -1
- package/src/plugins/builtin/research/index.tsx +2 -0
- package/src/plugins/builtin/research/relative-valuation-headless.ts +68 -32
- package/src/plugins/builtin/research/relative-valuation-model.ts +45 -24
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +39 -4
- 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/scanner/flow-pane.tsx +8 -1
- package/src/plugins/builtin/sec/filing-display.tsx +3 -36
- package/src/plugins/builtin/sec/headless.ts +25 -3
- package/src/plugins/builtin/sec/index.tsx +133 -44
- package/src/plugins/builtin/sec/model.ts +148 -2
- package/src/plugins/builtin/sec/row-summaries.ts +105 -0
- package/src/plugins/builtin/sectors/index.tsx +2 -0
- package/src/plugins/builtin/sectors/sector-model.ts +2 -2
- package/src/plugins/builtin/series-inventory.ts +3 -0
- package/src/plugins/builtin/shared/cloud-denied.ts +15 -0
- package/src/plugins/builtin/shared/free-preview.ts +47 -0
- package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
- package/src/plugins/builtin/shared/report-freshness.ts +3 -1
- package/src/plugins/builtin/shared/use-quote-board.ts +179 -43
- package/src/plugins/builtin/shared/volatility/options.ts +4 -1
- package/src/plugins/builtin/shared/wall-teaser.tsx +2 -2
- package/src/plugins/builtin/shared/zip-text.ts +46 -0
- package/src/plugins/builtin/short-interest/watch-headless.ts +2 -2
- package/src/plugins/builtin/short-interest/watch-model.ts +11 -2
- package/src/plugins/builtin/short-volume/headless.ts +1 -1
- package/src/plugins/builtin/social-mentions/headless.ts +1 -1
- 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 +5 -5
- 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/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/financials/aggregation.ts +11 -0
- package/src/plugins/builtin/ticker-detail/financials/ratios.ts +81 -19
- package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
- package/src/plugins/builtin/ticker-detail/headless-schema.ts +30 -0
- package/src/plugins/builtin/ticker-detail/headless.ts +88 -2
- package/src/plugins/builtin/ticker-detail/index.tsx +23 -11
- package/src/plugins/builtin/ticker-detail/overview/model.ts +3 -1
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +8 -6
- package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +8 -6
- package/src/plugins/builtin/time-sales/headless.ts +23 -1
- package/src/plugins/builtin/time-sales/index.tsx +2 -1
- package/src/plugins/builtin/trace-bonds/client.ts +196 -0
- package/src/plugins/builtin/trace-bonds/headless.ts +69 -0
- package/src/plugins/builtin/trace-bonds/index.tsx +33 -0
- package/src/plugins/builtin/trace-bonds/model.ts +115 -0
- package/src/plugins/builtin/trace-bonds/pane.tsx +191 -0
- package/src/plugins/builtin/treasury-auctions/index.tsx +33 -47
- package/src/plugins/builtin/treasury-daily/client.ts +96 -0
- package/src/plugins/builtin/treasury-daily/headless.ts +69 -0
- package/src/plugins/builtin/treasury-daily/index.tsx +17 -0
- package/src/plugins/builtin/treasury-daily/model.ts +69 -0
- package/src/plugins/builtin/treasury-daily/pane.tsx +140 -0
- package/src/plugins/builtin/vol-surface/evidence.ts +7 -1
- package/src/plugins/builtin/vol-surface/headless.ts +50 -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/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/entity-detail.tsx +15 -2
- package/src/plugins/builtin/world-venue-map/geo-draw.tsx +2 -1
- package/src/plugins/builtin/world-venue-map/geo-svg.tsx +6 -6
- package/src/plugins/builtin/world-venue-map/layered-pane.tsx +16 -9
- package/src/plugins/builtin/world-venue-map/map.tsx +145 -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/index.tsx +1 -1
- package/src/plugins/builtin-editorial.ts +1 -1
- package/src/plugins/catalog-browser.ts +25 -2
- 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 +63 -27
- package/src/plugins/{builtin/plugin-marketplace → marketplace}/store.ts +1 -1
- package/src/plugins/registry/host-actions.ts +11 -0
- package/src/portfolio/collection-name.ts +31 -0
- package/src/portfolio/valuation-basis.ts +79 -0
- package/src/public/time-series.ts +1 -1
- package/src/react/input.ts +33 -1
- package/src/react/key-repeat.ts +86 -0
- package/src/remote/controller.ts +21 -1
- package/src/remote/schema.ts +3 -2
- package/src/renderers/browser/main.tsx +1 -1
- package/src/renderers/browser/research-entry.ts +4 -2
- package/src/renderers/dom/build-assets.ts +14 -5
- package/src/renderers/dom/data-table/index.tsx +5 -3
- package/src/renderers/dom/data-table/row.tsx +3 -0
- package/src/renderers/dom/desktop/controls.tsx +2 -1
- package/src/renderers/dom/desktop/icons.tsx +34 -0
- package/src/renderers/dom/desktop/list-view.tsx +1 -1
- package/src/renderers/dom/desktop/menu.tsx +1 -1
- package/src/renderers/dom/desktop/query-bar.tsx +3 -1
- package/src/renderers/dom/host/block-glyph-canvas.tsx +6 -3
- package/src/renderers/dom/host/box.tsx +7 -2
- package/src/renderers/dom/host/chart-surface.tsx +34 -2
- package/src/renderers/dom/host/resize-rows.ts +111 -0
- package/src/renderers/dom/host/scroll-box.tsx +32 -0
- package/src/renderers/dom/host/size-observer.ts +24 -9
- package/src/renderers/dom/host/sticky-scroll.ts +147 -0
- package/src/renderers/dom/host/style.ts +15 -1
- package/src/renderers/dom/host/surface-3d.tsx +15 -7
- package/src/renderers/dom/host/surface-fit.ts +410 -0
- package/src/renderers/dom/host/text.tsx +2 -2
- package/src/renderers/dom/input-host.tsx +1 -0
- package/src/renderers/dom/styles.css +109 -16
- package/src/renderers/electrobun/bun/application-menu/click.ts +18 -0
- package/src/renderers/electrobun/bun/desktop/app-badge.ts +37 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +8 -0
- package/src/renderers/electrobun/bun/desktop/macos-dock-badge.ts +37 -0
- package/src/renderers/electrobun/bun/desktop/macos-dock-icon.ts +2 -40
- package/src/renderers/electrobun/bun/desktop/macos-objc.ts +41 -0
- package/src/renderers/electrobun/bun/index.ts +3 -2
- package/src/renderers/electrobun/bun/tui-entry.ts +18 -1
- package/src/renderers/electrobun/shared/protocol.ts +3 -1
- package/src/renderers/electrobun/view/app-badge-host.ts +17 -0
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +20 -3
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +59 -24
- package/src/renderers/electrobun/view/main.tsx +5 -3
- package/src/renderers/electrobun/view/remote/asset-data-client.ts +7 -1
- 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/resolve.ts +3 -2
- package/src/series/types.ts +8 -0
- package/src/series/validate.ts +2 -2
- package/src/shares/api.ts +5 -1
- 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/location.ts +31 -0
- package/src/shares/open.ts +26 -0
- package/src/shares/pane-image.ts +164 -0
- package/src/shares/personal-pane.ts +52 -0
- package/src/shares/portable-layout.ts +4 -2
- package/src/shares/prepare.ts +20 -0
- package/src/sources/gloomberb-cloud/index.ts +109 -5
- package/src/sources/gloomberb-cloud/normalizers.ts +7 -0
- package/src/sources/gloomberb-cloud/perpetuals.ts +249 -0
- package/src/sources/history-coverage.ts +22 -4
- package/src/sources/history-result.ts +3 -1
- package/src/sources/history-retention.ts +11 -0
- package/src/sources/listing-target.ts +4 -0
- package/src/sources/provider-errors.ts +2 -1
- package/src/sources/provider-router/batches.ts +18 -2
- package/src/sources/provider-router/index.ts +16 -1
- package/src/state/app/context/index.tsx +3 -2
- package/src/state/hooks/inline-tickers.ts +49 -11
- package/src/test-support/builtin-panes.ts +31 -0
- package/src/test-support/shell-pane-keys.tsx +1 -0
- package/src/theme/theme-context.tsx +15 -1
- package/src/tickers/instrument-kind.ts +9 -1
- package/src/tickers/listing-name.ts +34 -0
- package/src/tickers/listing-venues.ts +154 -0
- package/src/tickers/open-target.ts +11 -3
- package/src/tickers/perpetual-listing.ts +151 -0
- package/src/tickers/prediction-listing.ts +26 -0
- package/src/tickers/search/index.ts +34 -6
- package/src/tickers/search/ranking.ts +85 -4
- package/src/tickers/ticker-changes.ts +213 -0
- package/src/time-series/chart-data.ts +2 -0
- package/src/time-series/economic.ts +2 -6
- package/src/time-series/ice-credit.ts +39 -0
- package/src/time-series/parsed-history-cache.ts +41 -11
- package/src/time-series/resolve.ts +163 -28
- package/src/time-series/studies.ts +1 -0
- package/src/time-series/types.ts +8 -0
- package/src/types/config.ts +4 -2
- package/src/types/data-provider.ts +19 -1
- package/src/types/financials.ts +23 -0
- package/src/types/headless.ts +74 -0
- package/src/types/instrument.ts +4 -0
- package/src/types/plugin.ts +60 -2
- package/src/types/price-history.ts +2 -0
- package/src/types/ticker-changes.ts +26 -0
- package/src/ui/context-menu.tsx +13 -1
- package/src/ui/host.tsx +49 -7
- package/src/ui/live-resize.ts +10 -0
- package/src/ui/live-stretch.ts +51 -19
- package/src/ui/sticky-scroll.ts +7 -0
- package/src/utils/central-banks.ts +76 -0
- package/src/utils/cli-output.ts +126 -16
- package/src/utils/crypto-pair.ts +1 -1
- package/src/utils/currency-pair.ts +66 -0
- package/src/utils/currency-units.ts +11 -0
- package/src/utils/datetime-format.ts +27 -6
- package/src/utils/decimal-comma.ts +57 -0
- package/src/utils/dom-screenshot.ts +3 -1
- package/src/utils/double-escape-close.ts +15 -1
- package/src/utils/exchanges.ts +36 -0
- package/src/utils/format.ts +19 -2
- package/src/utils/forward-dividend.ts +14 -0
- package/src/utils/fx-status.ts +6 -1
- package/src/utils/guards.ts +25 -0
- package/src/utils/inline-content-tokenizer.ts +22 -3
- package/src/utils/local-utc-offset.ts +37 -0
- 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/plugin-proxy-hosts.json +8 -1
- package/src/utils/utc-time.ts +12 -0
- package/src/plugins/builtin/credit-boards/pane.tsx +0 -30
- package/src/ui/pane-fill.ts +0 -11
- /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
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import type { NativeFrameCapture } from "../ui/host";
|
|
2
|
+
import { displayWidth } from "../utils/format";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* At most this much of a pane's text goes with a question: a summary of what
|
|
6
|
+
* it shows, not a dump. It leaves room in the terminal context Gloom reads,
|
|
7
|
+
* which keeps 2,000 characters of it.
|
|
8
|
+
*/
|
|
9
|
+
export const PANE_TEXT_MAX_CHARS = 1_500;
|
|
10
|
+
const PANE_TEXT_MAX_LINES = 50;
|
|
11
|
+
|
|
12
|
+
export interface CellRect {
|
|
13
|
+
x: number;
|
|
14
|
+
y: number;
|
|
15
|
+
width: number;
|
|
16
|
+
height: number;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** The frame's sides and rules: lines of them say nothing, and an edge cell is not content. */
|
|
20
|
+
const RULE_LINE = /^[\s│┃║┆┊╎─━═┄┈╌┌┐└┘├┤┬┴┼╭╮╯╰▏▕█▐▌░▒▓]*$/u;
|
|
21
|
+
const LEADING_EDGE = /^[│┃║▏▕]\s?/u;
|
|
22
|
+
const TRAILING_EDGE = /\s?[│┃║▏▕█▐▌░▒▓]+$/u;
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* A pane's text, trimmed to what it says: no frame edges, rules or trailing
|
|
26
|
+
* blanks, runs of padding cut to two spaces so columns stay apart, blank
|
|
27
|
+
* lines folded, and the whole bounded. Null when nothing is left.
|
|
28
|
+
*/
|
|
29
|
+
export function normalizePaneText(raw: string): string | null {
|
|
30
|
+
const lines: string[] = [];
|
|
31
|
+
let blank = false;
|
|
32
|
+
for (const rawLine of raw.split(/\r?\n/)) {
|
|
33
|
+
if (RULE_LINE.test(rawLine)) {
|
|
34
|
+
blank = lines.length > 0;
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
const line = rawLine
|
|
38
|
+
.replace(LEADING_EDGE, "")
|
|
39
|
+
.replace(TRAILING_EDGE, "")
|
|
40
|
+
.replace(/\s+$/u, "")
|
|
41
|
+
.replace(/\s{3,}/gu, " ");
|
|
42
|
+
if (!line.trim()) {
|
|
43
|
+
blank = lines.length > 0;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
if (blank) lines.push("");
|
|
47
|
+
blank = false;
|
|
48
|
+
lines.push(line);
|
|
49
|
+
}
|
|
50
|
+
if (lines.length === 0) return null;
|
|
51
|
+
|
|
52
|
+
const kept: string[] = [];
|
|
53
|
+
let used = 0;
|
|
54
|
+
for (const line of lines.slice(0, PANE_TEXT_MAX_LINES)) {
|
|
55
|
+
if (used + line.length + 1 > PANE_TEXT_MAX_CHARS) break;
|
|
56
|
+
kept.push(line);
|
|
57
|
+
used += line.length + 1;
|
|
58
|
+
}
|
|
59
|
+
if (kept.length === 0) kept.push(lines[0]!.slice(0, PANE_TEXT_MAX_CHARS - 1));
|
|
60
|
+
const cut = kept.length < lines.length || kept[0] !== lines[0];
|
|
61
|
+
return cut ? `${kept.join("\n")}\n…` : kept.join("\n");
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function covers(rect: CellRect, x: number, y: number): boolean {
|
|
65
|
+
return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* A terminal pane's text from the frame on screen: the rows under its header,
|
|
70
|
+
* the columns it spans. Cells another surface covers (a floating pane above
|
|
71
|
+
* it, the menu it was asked from) are left blank, so only this pane's own
|
|
72
|
+
* text goes. `rect` and `covered` are in the pane area's cells, which starts
|
|
73
|
+
* `offsetX` columns and `offsetY` rows into the screen (under the app header).
|
|
74
|
+
*/
|
|
75
|
+
export function terminalPaneText(
|
|
76
|
+
frame: NativeFrameCapture,
|
|
77
|
+
rect: CellRect,
|
|
78
|
+
{ offsetX = 0, offsetY = 0, covered = [] }: { offsetX?: number; offsetY?: number; covered?: readonly CellRect[] } = {},
|
|
79
|
+
): string | null {
|
|
80
|
+
const rows: string[] = [];
|
|
81
|
+
const left = Math.max(0, Math.floor(rect.x + offsetX));
|
|
82
|
+
const right = Math.floor(rect.x + offsetX + rect.width);
|
|
83
|
+
const top = Math.floor(rect.y) + 1;
|
|
84
|
+
const bottom = Math.floor(rect.y + rect.height);
|
|
85
|
+
for (let row = top; row < bottom; row += 1) {
|
|
86
|
+
const spans = frame.lines[row + offsetY];
|
|
87
|
+
if (!spans) continue;
|
|
88
|
+
let column = 0;
|
|
89
|
+
let text = "";
|
|
90
|
+
for (const span of spans) {
|
|
91
|
+
// One code point per cell; a wide one is followed by an empty continuation cell.
|
|
92
|
+
for (const char of span.text) {
|
|
93
|
+
const start = column;
|
|
94
|
+
const width = displayWidth(char) >= 2 ? 2 : 1;
|
|
95
|
+
column += width;
|
|
96
|
+
if (start < left || start >= right) continue;
|
|
97
|
+
text += covered.some((other) => covers(other, start - offsetX, row)) ? " ".repeat(width) : char;
|
|
98
|
+
}
|
|
99
|
+
if (column >= right) break;
|
|
100
|
+
}
|
|
101
|
+
rows.push(text);
|
|
102
|
+
}
|
|
103
|
+
return normalizePaneText(rows.join("\n"));
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function attributeValue(value: string): string {
|
|
107
|
+
return value.replace(/\\/g, "\\\\").replace(/"/g, "\\\"");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
interface PixelRect {
|
|
111
|
+
left: number;
|
|
112
|
+
top: number;
|
|
113
|
+
right: number;
|
|
114
|
+
bottom: number;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function intersect(a: PixelRect, b: PixelRect): PixelRect | null {
|
|
118
|
+
const rect = {
|
|
119
|
+
left: Math.max(a.left, b.left),
|
|
120
|
+
top: Math.max(a.top, b.top),
|
|
121
|
+
right: Math.min(a.right, b.right),
|
|
122
|
+
bottom: Math.min(a.bottom, b.bottom),
|
|
123
|
+
};
|
|
124
|
+
return rect.right > rect.left && rect.bottom > rect.top ? rect : null;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Where an element's content can be seen: the pane body cut down by every box
|
|
129
|
+
* between them that clips (a scrolled list, a table body), or null when one
|
|
130
|
+
* of them hides it.
|
|
131
|
+
*/
|
|
132
|
+
function visibleArea(element: Element, body: HTMLElement, cache: Map<Element, PixelRect | null>): PixelRect | null {
|
|
133
|
+
const cached = cache.get(element);
|
|
134
|
+
if (cached !== undefined) return cached;
|
|
135
|
+
let area: PixelRect | null;
|
|
136
|
+
if (element === body) {
|
|
137
|
+
area = body.getBoundingClientRect();
|
|
138
|
+
} else {
|
|
139
|
+
const parent = element.parentElement;
|
|
140
|
+
area = parent ? visibleArea(parent, body, cache) : null;
|
|
141
|
+
const view = element.ownerDocument.defaultView;
|
|
142
|
+
const style = view?.getComputedStyle(element);
|
|
143
|
+
if (area && style) {
|
|
144
|
+
if (style.visibility === "hidden" || style.display === "none" || style.opacity === "0") area = null;
|
|
145
|
+
else if (style.overflowX !== "visible" || style.overflowY !== "visible") area = intersect(area, element.getBoundingClientRect());
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
cache.set(element, area);
|
|
149
|
+
return area;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* A desktop or web pane's text as it is on screen: its body, without the
|
|
154
|
+
* header or footer, and only what can be seen, so rows scrolled out of a list
|
|
155
|
+
* and anything clipped stay out. Text on one line is joined in reading order.
|
|
156
|
+
*/
|
|
157
|
+
export function domPaneText(paneId: string): string | null {
|
|
158
|
+
const doc = (globalThis as { document?: Document }).document;
|
|
159
|
+
if (!doc) return null;
|
|
160
|
+
const pane = doc.querySelector<HTMLElement>(`[data-gloom-pane-id="${attributeValue(paneId)}"]`);
|
|
161
|
+
const body = pane?.querySelector<HTMLElement>("[data-gloom-role=pane-body]") ?? pane;
|
|
162
|
+
if (!body) return null;
|
|
163
|
+
|
|
164
|
+
const areas = new Map<Element, PixelRect | null>();
|
|
165
|
+
const pieces: Array<{ top: number; bottom: number; left: number; right: number; text: string }> = [];
|
|
166
|
+
const range = doc.createRange();
|
|
167
|
+
const walker = doc.createTreeWalker(body, 4 /* NodeFilter.SHOW_TEXT */);
|
|
168
|
+
for (let node = walker.nextNode(); node; node = walker.nextNode()) {
|
|
169
|
+
const text = node.textContent?.replace(/\s+/g, " ").trim();
|
|
170
|
+
const parent = node.parentElement;
|
|
171
|
+
if (!text || !parent) continue;
|
|
172
|
+
const area = visibleArea(parent, body, areas);
|
|
173
|
+
if (!area) continue;
|
|
174
|
+
range.selectNodeContents(node);
|
|
175
|
+
const rects = Array.from(range.getClientRects()).filter((rect) => rect.width > 0 && intersect(area, rect));
|
|
176
|
+
const first = rects[0];
|
|
177
|
+
if (!first) continue;
|
|
178
|
+
pieces.push({ top: first.top, bottom: first.bottom, left: first.left, right: first.right, text });
|
|
179
|
+
}
|
|
180
|
+
range.detach?.();
|
|
181
|
+
|
|
182
|
+
// Pieces whose middles share a line read as one, left to right, with a
|
|
183
|
+
// gap where the pane leaves one between them.
|
|
184
|
+
pieces.sort((a, b) => (a.top + a.bottom) / 2 - (b.top + b.bottom) / 2 || a.left - b.left);
|
|
185
|
+
const lines: Array<{ middle: number; height: number; parts: typeof pieces }> = [];
|
|
186
|
+
for (const piece of pieces) {
|
|
187
|
+
const middle = (piece.top + piece.bottom) / 2;
|
|
188
|
+
const line = lines[lines.length - 1];
|
|
189
|
+
if (line && Math.abs(line.middle - middle) < Math.max(4, line.height / 2)) line.parts.push(piece);
|
|
190
|
+
else lines.push({ middle, height: piece.bottom - piece.top, parts: [piece] });
|
|
191
|
+
}
|
|
192
|
+
const text = lines.map((line) => {
|
|
193
|
+
const parts = [...line.parts].sort((a, b) => a.left - b.left);
|
|
194
|
+
return parts.reduce((out, part, index) => {
|
|
195
|
+
if (index === 0) return part.text;
|
|
196
|
+
const gap = part.left - parts[index - 1]!.right;
|
|
197
|
+
return `${out}${gap > 6 ? " " : gap > 0.5 ? " " : ""}${part.text}`;
|
|
198
|
+
}, "");
|
|
199
|
+
}).join("\n");
|
|
200
|
+
return normalizePaneText(text);
|
|
201
|
+
}
|
|
@@ -202,7 +202,7 @@ function resolvedOutput(value: unknown): ResolvedSeries {
|
|
|
202
202
|
const input = object(value, "resolve output");
|
|
203
203
|
onlyKeys(input, "resolve output", [
|
|
204
204
|
"id", "label", "color", "unit", "unitGroup", "nativeFrequency", "timestampMode", "dataShape", "style", "transform",
|
|
205
|
-
"axis", "panelId", "interpolation", "timeBasis", "latestChangePercent", "points", "warning", "hidden",
|
|
205
|
+
"axis", "panelId", "interpolation", "timeBasis", "latestChangePercent", "points", "warning", "hidden", "attribution",
|
|
206
206
|
]);
|
|
207
207
|
if (!Array.isArray(input.points)) fail("resolve output points", "expected an array.");
|
|
208
208
|
if (input.points.length > MAX_CHART_SERIES_POINTS) {
|
|
@@ -212,6 +212,9 @@ function resolvedOutput(value: unknown): ResolvedSeries {
|
|
|
212
212
|
if (timeBasis) onlyKeys(timeBasis, "resolve output time basis", ["kind", "timeZone", "cadenceMs"]);
|
|
213
213
|
const kind = timeBasis?.kind === undefined ? undefined : enumValue<"market">(timeBasis.kind, "time basis kind", new Set(["market"]));
|
|
214
214
|
const warning = optionalString(input.warning, "resolve output warning", 500);
|
|
215
|
+
const attribution = input.attribution === undefined ? undefined : object(input.attribution, "resolve output attribution");
|
|
216
|
+
if (attribution) onlyKeys(attribution, "resolve output attribution", ["source", "publisher"]);
|
|
217
|
+
const publisher = attribution ? optionalString(attribution.publisher, "resolve output publisher", 160) : undefined;
|
|
215
218
|
return {
|
|
216
219
|
id: boundedString(input.id, "resolve output ID", 160),
|
|
217
220
|
label: boundedString(input.label, "resolve output label", 160),
|
|
@@ -242,6 +245,10 @@ function resolvedOutput(value: unknown): ResolvedSeries {
|
|
|
242
245
|
: {}),
|
|
243
246
|
points: input.points.map(point),
|
|
244
247
|
...(warning !== undefined ? { warning } : {}),
|
|
248
|
+
...(attribution ? { attribution: {
|
|
249
|
+
source: boundedString(attribution.source, "resolve output source", 160),
|
|
250
|
+
...(publisher !== undefined ? { publisher } : {}),
|
|
251
|
+
} } : {}),
|
|
245
252
|
...(input.hidden !== undefined
|
|
246
253
|
? typeof input.hidden === "boolean" ? { hidden: input.hidden } : fail("resolve output hidden", "expected a boolean.")
|
|
247
254
|
: {}),
|
|
@@ -61,6 +61,7 @@ export function assetDataProvider(provider: AssetDataProvider): AssetDataCapabil
|
|
|
61
61
|
if (!provider.getCorporateActions) throw new Error(`${provider.name} does not provide corporate actions.`);
|
|
62
62
|
return provider.getCorporateActions(input.ticker, input.exchange, input.context);
|
|
63
63
|
}, "query"),
|
|
64
|
+
getTickerChanges: op((input: any) => provider.getTickerChanges?.(input.symbol) ?? Promise.resolve(null), "query"),
|
|
64
65
|
getEarningsCalendar: op((input: any) => {
|
|
65
66
|
if (!provider.getEarningsCalendar) throw new Error(`${provider.name} does not provide earnings calendar.`);
|
|
66
67
|
return provider.getEarningsCalendar(input.symbols ?? [], input.context);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { centralBankNamed } from "../utils/central-banks";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Central bank acronyms that are also the symbol of a listing people ask
|
|
5
|
+
* about (BOE is a NYSE fund, RBI Raiffeisen Bank in Vienna), so `events BOE`
|
|
6
|
+
* stays that listing's. ECB S.A. in Warsaw is reached as ECB:WSE.
|
|
7
|
+
*/
|
|
8
|
+
const LISTED_SYMBOLS = new Set(["BOE", "BOC", "RBA", "PBOC", "RBI", "BOK"]);
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* What `events` says for a central bank named on its own ("ECB", "Norges
|
|
12
|
+
* Bank"): its meetings are no company's corporate events, and CBR has its
|
|
13
|
+
* rate and the next decision from the bank's own calendar (China's next LPR
|
|
14
|
+
* fixing; SAMA publishes none). Null for anything else, a symbol with a venue
|
|
15
|
+
* included.
|
|
16
|
+
*/
|
|
17
|
+
export function centralBankEventsMessage(typed: string): string | null {
|
|
18
|
+
const bank = centralBankNamed(typed);
|
|
19
|
+
if (!bank || LISTED_SYMBOLS.has(typed.trim().replace(/^\$/, "").toUpperCase())) return null;
|
|
20
|
+
const next = bank.id === "US" ? " and the next FOMC date" : bank.id === "CN" ? " and the next LPR fixing"
|
|
21
|
+
: bank.id === "SA" ? "" : " and the next decision";
|
|
22
|
+
return `${bank.id === "US" ? "Fed" : bank.short} meetings are not listed; CBR shows the rate${next} (gloomberb fn CBR).`;
|
|
23
|
+
}
|
|
@@ -4,8 +4,11 @@ import { suggestCliOption } from "./help";
|
|
|
4
4
|
|
|
5
5
|
/** A built-in command, which may say more about an option it does not take than the list of those it does. */
|
|
6
6
|
export interface BuiltinCliCommandDef extends CliCommandDef {
|
|
7
|
-
/**
|
|
8
|
-
|
|
7
|
+
/**
|
|
8
|
+
* A sentence for an unknown option people reach for, such as `--from` on
|
|
9
|
+
* `history`; null for the rest. `args` are the command's, to name what it was given.
|
|
10
|
+
*/
|
|
11
|
+
unknownOptionHint?(flag: string, args: readonly string[]): string | null;
|
|
9
12
|
}
|
|
10
13
|
|
|
11
14
|
interface DeclaredOption {
|
|
@@ -34,12 +37,21 @@ function declaredOptions(command: CliCommandDef): Map<string, DeclaredOption> |
|
|
|
34
37
|
return declared;
|
|
35
38
|
}
|
|
36
39
|
|
|
37
|
-
|
|
40
|
+
/** `--explain` on a command that has no glossary of its own points at the commands that do, and at the glossary. */
|
|
41
|
+
const EXPLAIN_HINT = "--explain follows the reports of fn <function> and portfolio show. "
|
|
42
|
+
+ "gloomberb catalog explain <term> prints what one term means; gloomberb catalog glossary lists every term.";
|
|
43
|
+
|
|
44
|
+
function failUnknownOption(
|
|
45
|
+
command: BuiltinCliCommandDef,
|
|
46
|
+
flag: string,
|
|
47
|
+
declared: Map<string, DeclaredOption>,
|
|
48
|
+
args: readonly string[],
|
|
49
|
+
): never {
|
|
38
50
|
const flags = [...declared.keys()];
|
|
39
51
|
const suggestion = suggestCliOption(flag, flags);
|
|
40
52
|
const help = `See gloomberb help ${command.name}.`;
|
|
41
53
|
fail(`Unknown option ${flag} for ${command.name}.`, [
|
|
42
|
-
command.unknownOptionHint?.(flag) ?? "",
|
|
54
|
+
command.unknownOptionHint?.(flag, args) ?? (flag === "--explain" ? EXPLAIN_HINT : ""),
|
|
43
55
|
suggestion ? `Did you mean ${suggestion}?` : "",
|
|
44
56
|
flags.length > 0 ? `Options: ${flags.join(", ")}. ${help}` : `${command.name} takes no options. ${help}`,
|
|
45
57
|
].filter(Boolean).join("\n"));
|
|
@@ -65,7 +77,7 @@ export function checkCliCommandOptions(
|
|
|
65
77
|
const equals = arg.indexOf("=");
|
|
66
78
|
const flag = equals >= 0 ? arg.slice(0, equals) : arg;
|
|
67
79
|
const option = declared.get(flag);
|
|
68
|
-
if (!option) failUnknownOption(command, flag, declared);
|
|
80
|
+
if (!option) failUnknownOption(command, flag, declared, args);
|
|
69
81
|
if (!option.takesValue && equals >= 0) {
|
|
70
82
|
fail(`${flag} takes no value.`, `Write it alone, as ${flag}. See gloomberb help ${command.name}.`);
|
|
71
83
|
}
|
|
@@ -3,6 +3,8 @@ import { isCryptoVenuePerpetual } from "../../tickers/instrument-kind";
|
|
|
3
3
|
import type { Quote } from "../../types/financials";
|
|
4
4
|
import { isCryptoPairSymbol } from "../../utils/crypto-pair";
|
|
5
5
|
import { isNotATickerMessage } from "../not-a-ticker";
|
|
6
|
+
import { groupByCompany } from "../../tickers/company-name";
|
|
7
|
+
import type { TickerRecord } from "../../types/ticker";
|
|
6
8
|
|
|
7
9
|
/** Where a crypto pair that will not load can still be read. `fn CRYP` takes no argument. */
|
|
8
10
|
export const CRYPTO_BOARD_HINT = "Crypto prices may be briefly unavailable; the CRYP function (gloomberb fn CRYP) shows the crypto board.";
|
|
@@ -27,9 +29,10 @@ const MAJOR_COINS = new Map([
|
|
|
27
29
|
/**
|
|
28
30
|
* A bare coin ticker usually resolves to a listed fund, not the coin. The note applies only when
|
|
29
31
|
* the listing is not crypto and its own name says the coin (a "Bitcoin" trust), so a company that
|
|
30
|
-
* happens to share the letters (Interlink Electronics on LINK) is left alone.
|
|
32
|
+
* happens to share the letters (Interlink Electronics on LINK) is left alone. Every command that
|
|
33
|
+
* reads a bare symbol as a listing says it: quote, ticker, and the portfolio and watchlist adds.
|
|
31
34
|
*/
|
|
32
|
-
function notTheCoinNote(symbol: string, quote: Pick<Quote, "name" | "instrumentType"> | null | undefined): string | null {
|
|
35
|
+
export function notTheCoinNote(symbol: string, quote: Pick<Quote, "name" | "instrumentType"> | null | undefined): string | null {
|
|
33
36
|
const coin = MAJOR_COINS.get(symbol);
|
|
34
37
|
const type = quote?.instrumentType?.trim();
|
|
35
38
|
const name = quote?.name?.trim();
|
|
@@ -38,6 +41,29 @@ function notTheCoinNote(symbol: string, quote: Pick<Quote, "name" | "instrumentT
|
|
|
38
41
|
return `${symbol} is ${name}, not ${coin}. For ${coin} use ${symbol}-USD.`;
|
|
39
42
|
}
|
|
40
43
|
|
|
44
|
+
/** `notTheCoinNote` for a saved listing, by the name and asset category it was saved with. */
|
|
45
|
+
export function savedListingCoinNote(symbol: string, ticker: Pick<TickerRecord, "metadata">): string | null {
|
|
46
|
+
return notTheCoinNote(symbol, { name: ticker.metadata.name, instrumentType: ticker.metadata.assetCategory });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* A bare coin ticker several companies list asks which listing; when none of them is the coin
|
|
51
|
+
* (SOL: Solvay, Washington H. Soul Pattinson), the coin is where the user may have meant:
|
|
52
|
+
* "SOL is Solvay SA or Washington H. Soul Pattinson and Company Limited, not solana. For
|
|
53
|
+
* solana use SOL-USD." Null for any other symbol, or when a listing is a crypto pair.
|
|
54
|
+
*/
|
|
55
|
+
export function coinElsewhereNote(symbol: string, listings: ReadonlyArray<{ key: string; name: string }>): string | null {
|
|
56
|
+
const coin = MAJOR_COINS.get(symbol);
|
|
57
|
+
if (!coin || listings.some((listing) => isCryptoPairSymbol(listing.key))) return null;
|
|
58
|
+
const names = groupByCompany(listings.filter((listing) => listing.name.trim()), (listing) => listing.name.trim())
|
|
59
|
+
.map((group) => group[0]!.name.trim());
|
|
60
|
+
if (names.length === 0) return null;
|
|
61
|
+
const named = names.length === 1 ? names[0]!
|
|
62
|
+
: names.length === 2 ? `${names[0]} or ${names[1]}`
|
|
63
|
+
: `${names[0]}, ${names[1]} or another company`;
|
|
64
|
+
return `${symbol} is ${named}, not ${coin}. For ${coin} use ${symbol}-USD.`;
|
|
65
|
+
}
|
|
66
|
+
|
|
41
67
|
interface QuoteNoteResult {
|
|
42
68
|
target: { symbol: string };
|
|
43
69
|
quote: Pick<Quote, "name" | "instrumentType"> | null;
|
|
@@ -1,4 +1,6 @@
|
|
|
1
|
+
import { ICE_SPREAD_SERIES } from "../../time-series/ice-credit";
|
|
1
2
|
import { toBasisPoints } from "../../utils/basis-points";
|
|
3
|
+
import { parseReportTime } from "../../utils/utc-time";
|
|
2
4
|
|
|
3
5
|
/** Where `fred` starts when `--start` is not given. */
|
|
4
6
|
export const DEFAULT_FRED_START = "2021-01-01";
|
|
@@ -9,16 +11,125 @@ export const DEFAULT_FRED_START = "2021-01-01";
|
|
|
9
11
|
*/
|
|
10
12
|
const SPREAD_SERIES: ReadonlySet<string> = new Set([
|
|
11
13
|
"T10Y2Y", "T10Y3M",
|
|
12
|
-
|
|
14
|
+
...ICE_SPREAD_SERIES.map((series) => series.seriesId),
|
|
13
15
|
]);
|
|
14
16
|
|
|
15
17
|
export function isSpreadSeries(seriesId: string): boolean {
|
|
16
18
|
return SPREAD_SERIES.has(seriesId.toUpperCase());
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
/**
|
|
20
|
-
export
|
|
21
|
-
|
|
21
|
+
/**
|
|
22
|
+
* The unit a series' observations export in, for the `currency_or_unit`
|
|
23
|
+
* column beside its bare `value`: `bp` for a spread (exported in basis
|
|
24
|
+
* points), `pct` for a percent, otherwise FRED's own units, with "annual
|
|
25
|
+
* rate" as the text header says it. Null when FRED's metadata did not come
|
|
26
|
+
* back.
|
|
27
|
+
*/
|
|
28
|
+
export function fredUnit(seriesId: string, info: { units?: string | null; seasonalAdjustment?: string | null } | null | undefined): string | null {
|
|
29
|
+
if (isSpreadSeries(seriesId)) return "bp";
|
|
30
|
+
const units = info?.units?.trim();
|
|
31
|
+
if (!units) return null;
|
|
32
|
+
if (/^percent$/i.test(units)) return "pct";
|
|
33
|
+
const annualRate = /annual rate/i.test(info?.seasonalAdjustment ?? "") && !/annual rate/i.test(units);
|
|
34
|
+
return `${units}${annualRate ? ", annual rate" : ""}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** How often a series observes, in FRED's word lower-cased: `Weekly, Ending Friday` is `weekly`. Null when FRED does not say. */
|
|
38
|
+
export function fredInterval(frequency: string | null | undefined): string | null {
|
|
39
|
+
const word = frequency?.split(",")[0]?.trim().toLowerCase();
|
|
40
|
+
return word || null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* The observations from the series' first one on: Gloom Cloud answers a
|
|
45
|
+
* `--start` before a series begins with a blank row for every day in between
|
|
46
|
+
* (DEXSFUS from 1975 has five years of them before 1980-01-02). FRED's
|
|
47
|
+
* `observationStart` says where it begins; without it the blank rows before
|
|
48
|
+
* the first value go. Blank rows after it (a holiday's ".") stay.
|
|
49
|
+
*/
|
|
50
|
+
export function fredObservationsFromStart<T extends { date: string; value: number | null }>(
|
|
51
|
+
observations: readonly T[],
|
|
52
|
+
seriesStart: string | null | undefined,
|
|
53
|
+
): T[] {
|
|
54
|
+
const first = seriesStart && /^\d{4}-\d{2}-\d{2}$/.test(seriesStart)
|
|
55
|
+
? seriesStart
|
|
56
|
+
: observations.reduce<string | null>((earliest, observation) => (
|
|
57
|
+
observation.value != null && Number.isFinite(observation.value) && (!earliest || observation.date < earliest) ? observation.date : earliest
|
|
58
|
+
), null);
|
|
59
|
+
return first ? observations.filter((observation) => observation.date >= first) : [...observations];
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* The value column's header: FRED's units, and "annual rate" when a monthly or
|
|
64
|
+
* quarterly flow is stated at a seasonally adjusted annual rate (GDP, FDEFX),
|
|
65
|
+
* so a quarter's 1,100 billion does not read as spent in that quarter.
|
|
66
|
+
*/
|
|
67
|
+
export function fredValueHeader(info: { units?: string | null; seasonalAdjustment?: string | null } | null | undefined): string {
|
|
68
|
+
const units = info?.units?.trim();
|
|
69
|
+
if (!units) return "Value";
|
|
70
|
+
const annualRate = /annual rate/i.test(info?.seasonalAdjustment ?? "") && !/annual rate/i.test(units);
|
|
71
|
+
return `Value (${units}${annualRate ? ", annual rate" : ""})`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** An observation in the unit it exports in: a spread's percentage points as basis points (3.15 is 315), anything else as FRED gives it. */
|
|
75
|
+
export function fredExportValue(seriesId: string, value: number | null): number | null {
|
|
76
|
+
if (value == null || !Number.isFinite(value)) return null;
|
|
77
|
+
return isSpreadSeries(seriesId) ? toBasisPoints(value, 4) : value;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/** Basis points as a text table prints them, to a tenth: 44 is `44`, -12.5 is `-12.5`. */
|
|
81
|
+
export function basisPointsCell(value: unknown): string {
|
|
82
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return "";
|
|
83
|
+
const rounded = Math.round(value * 10) / 10;
|
|
84
|
+
return String(rounded === 0 ? 0 : rounded);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* When FRED last updated a series, its `last_updated` (`2026-10-09 08:08:04-05`),
|
|
89
|
+
* as an ISO instant in UTC: the publication time an export cites. Only when
|
|
90
|
+
* that update covers the newest observation shown (`observationEnd` reaches
|
|
91
|
+
* it): metadata from before it says nothing about when it was published.
|
|
92
|
+
* Null when it does not, or when FRED's metadata does not say.
|
|
93
|
+
*/
|
|
94
|
+
export function fredPublicationTime(
|
|
95
|
+
info: { lastUpdated?: string | null; observationEnd?: string | null } | null | undefined,
|
|
96
|
+
newestDate: string | null | undefined,
|
|
97
|
+
): string | null {
|
|
98
|
+
if (newestDate && !(info?.observationEnd && info.observationEnd >= newestDate)) return null;
|
|
99
|
+
const iso = (info?.lastUpdated ?? "").trim().replace(/^(\d{4}-\d{2}-\d{2}) /, "$1T").replace(/([+-]\d{2})$/, "$1:00");
|
|
100
|
+
const time = parseReportTime(iso);
|
|
101
|
+
return time && !time.dateOnly ? new Date(time.time).toISOString() : null;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Where a FRED report's figures come from, as its source line names them. */
|
|
105
|
+
export interface FredProvenance {
|
|
106
|
+
source: string;
|
|
107
|
+
publisher?: string;
|
|
108
|
+
publishedAt?: string;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Where the newest of these observations comes from: FRED, unless Gloom
|
|
113
|
+
* Cloud carried a value FRED does not list yet and named its publisher. Then
|
|
114
|
+
* the source line names that publisher and when it published, so a reader
|
|
115
|
+
* looking for the figure in FRED's own data knows it is not there yet:
|
|
116
|
+
* `U.S. Treasury via FRED, published 2026-10-09`. Null for FRED's own.
|
|
117
|
+
*/
|
|
118
|
+
export function fredProvenance(observations: ReadonlyArray<{ date: string; publisher?: string; publishedAt?: string }>): FredProvenance | null {
|
|
119
|
+
const ahead = observations.filter((observation) => observation.publisher?.trim());
|
|
120
|
+
if (ahead.length === 0) return null;
|
|
121
|
+
const publishers = [...new Set(ahead.map((observation) => observation.publisher!.trim()))];
|
|
122
|
+
const times = ahead.flatMap((observation) => {
|
|
123
|
+
const time = parseReportTime(observation.publishedAt);
|
|
124
|
+
return time ? [new Date(time.time).toISOString()] : [];
|
|
125
|
+
}).sort();
|
|
126
|
+
const publishedAt = times.at(-1);
|
|
127
|
+
const publisher = publishers.join(" and ");
|
|
128
|
+
return {
|
|
129
|
+
source: `${publisher} via FRED${publishedAt ? `, published ${publishedAt.slice(0, 10)}` : ""}`,
|
|
130
|
+
publisher,
|
|
131
|
+
...(publishedAt ? { publishedAt } : {}),
|
|
132
|
+
};
|
|
22
133
|
}
|
|
23
134
|
|
|
24
135
|
/**
|
|
@@ -59,3 +170,43 @@ export function fredEmptyMessage(startDate: string, latest: { date: string; valu
|
|
|
59
170
|
? `Latest observation: ${latest.date}${latest.value == null ? "" : ` (${latest.value})`}. Nothing on or after ${startDate}.`
|
|
60
171
|
: "No results.";
|
|
61
172
|
}
|
|
173
|
+
|
|
174
|
+
/** What an export of nothing on or after `--start` says to try instead: a start that reaches the latest observation. */
|
|
175
|
+
export function fredEmptyAdvice(seriesId: string, latest: { date: string } | null): string {
|
|
176
|
+
return latest
|
|
177
|
+
? `Latest observation: ${latest.date}. Try: gloomberb fred ${seriesId} --start ${latest.date.slice(0, 4)}`
|
|
178
|
+
: `No observations came back for ${seriesId}. Try an earlier --start, or gloomberb fred --list for the series served.`;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/** A series as `fred --list` shows it. */
|
|
182
|
+
export interface FredListRow {
|
|
183
|
+
id: string;
|
|
184
|
+
title: string;
|
|
185
|
+
group: string;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** How well a word of the filter matches a series, best first: its id or group name, a word of its group or title, the start of its id, then any text. */
|
|
189
|
+
function matchTier(term: string, row: FredListRow, words: readonly string[]): number {
|
|
190
|
+
if (row.id.toLowerCase() === term || row.group.toLowerCase() === term) return 0;
|
|
191
|
+
if (words.some((word) => word.startsWith(term))) return 1;
|
|
192
|
+
if (row.id.toLowerCase().startsWith(term)) return 2;
|
|
193
|
+
return 3;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* The series that carry every word of the filter in their id, title or group,
|
|
198
|
+
* the ones it names first: a group or a word of the title ahead of the id that
|
|
199
|
+
* only has it inside (`fx` is the FX group, not FDEFX, whose id spells it).
|
|
200
|
+
* Catalog order breaks ties; an empty filter keeps the catalog as it is.
|
|
201
|
+
*/
|
|
202
|
+
export function filterFredSeries(rows: readonly FredListRow[], filter: string): FredListRow[] {
|
|
203
|
+
const terms = filter.toLowerCase().split(/\s+/).filter(Boolean);
|
|
204
|
+
const ranked = rows.flatMap((row, index) => {
|
|
205
|
+
const text = `${row.id} ${row.title} ${row.group}`.toLowerCase();
|
|
206
|
+
if (!terms.every((term) => text.includes(term))) return [];
|
|
207
|
+
const words = `${row.title} ${row.group}`.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
|
|
208
|
+
const tiers = terms.map((term) => matchTier(term, row, words));
|
|
209
|
+
return [{ row, index, worst: Math.max(0, ...tiers), total: tiers.reduce((sum, tier) => sum + tier, 0) }];
|
|
210
|
+
});
|
|
211
|
+
return ranked.sort((a, b) => a.worst - b.worst || a.total - b.total || a.index - b.index).map((entry) => entry.row);
|
|
212
|
+
}
|