gloomberb 0.11.3 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/README.zh-CN.md +4 -2
- package/package.json +28 -1
- package/src/api-client/auth.ts +10 -0
- package/src/api-client/data.ts +103 -1
- package/src/api-client/index.test.ts +127 -0
- package/src/api-client/index.ts +129 -1
- package/src/api-client/normalizers.ts +39 -0
- package/src/api-client/paths.ts +85 -1
- package/src/api-client/types.ts +196 -0
- package/src/app/pane-runtime/index.tsx +15 -12
- package/src/app/pane-runtime/layout-placement.test.ts +27 -0
- package/src/app/pane-runtime/layout-placement.ts +13 -0
- package/src/app/pane-runtime/plugin-bindings.ts +62 -0
- package/src/app.tsx +60 -18
- package/src/architecture/browser-entry-graph.test.ts +96 -0
- package/src/brokers/profile-form.test.ts +25 -27
- package/src/brokers/test-broker.ts +68 -0
- package/src/cli/commands/plugins.ts +39 -11
- package/src/cli/desktop-pane-shot.ts +285 -16
- package/src/cli/index.ts +1 -1
- package/src/cli/pane-functions/capabilities.ts +58 -28
- package/src/cli/pane-functions/catalog.ts +16 -4
- package/src/cli/pane-functions/discovery.ts +1 -0
- package/src/cli/pane-functions/headless.test.ts +247 -0
- package/src/cli/pane-functions/headless.ts +423 -0
- package/src/cli/pane-functions/index.test.ts +9 -0
- package/src/cli/pane-functions/index.ts +6 -2
- package/src/cli/pane-functions/options.ts +27 -2
- package/src/cli/pane-functions/report.ts +4 -0
- package/src/cli/pane-functions/resolver.ts +26 -8
- package/src/cli/pane-functions/screenshot.test.ts +76 -0
- package/src/cli/pane-functions/screenshot.ts +236 -20
- package/src/cli/restore-plugins.ts +34 -0
- package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
- package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
- package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
- package/src/components/chart/composite/composite-chart.test.tsx +68 -20
- package/src/components/chart/composite/composite-chart.tsx +301 -204
- package/src/components/chart/composite/format.ts +8 -2
- package/src/components/chart/composite/interactions.test.ts +68 -102
- package/src/components/chart/composite/interactions.ts +214 -264
- package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
- package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
- package/src/components/chart/composite/scene.ts +57 -3
- package/src/components/chart/composite/text-renderer.ts +3 -2
- package/src/components/chart/composite/time-scale.ts +138 -57
- package/src/components/chart/composite/types.ts +25 -1
- package/src/components/chart/core/data.ts +9 -13
- package/src/components/chart/core/date-window.ts +7 -1
- package/src/components/chart/core/pointer.ts +3 -0
- package/src/components/chart/core/types.ts +0 -45
- package/src/components/chart/native/chart-rasterizer.ts +0 -1
- package/src/components/chart/native/kitty/index.test.ts +0 -188
- package/src/components/chart/native/raster/crosshair.ts +6 -5
- package/src/components/chart/native/raster/primitives.ts +0 -23
- package/src/components/chart/native/renderer-selection.ts +81 -31
- package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
- package/src/components/chart/static/chart-surface.tsx +150 -0
- package/src/components/chart/static/index.ts +8 -0
- package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
- package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
- package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
- package/src/components/chart/static/series.ts +49 -0
- package/src/components/command-bar/action-runtime.ts +1 -3
- package/src/components/command-bar/assist/model.test.ts +17 -2
- package/src/components/command-bar/assist/model.ts +40 -9
- package/src/components/command-bar/commands/direct/index.ts +1 -1
- package/src/components/command-bar/commands/direct/runtime.ts +1 -1
- package/src/components/command-bar/commands/registry.ts +0 -11
- package/src/components/command-bar/helpers.test.ts +2 -2
- package/src/components/command-bar/helpers.ts +2 -6
- package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
- package/src/components/command-bar/list/badge.test.ts +54 -0
- package/src/components/command-bar/list/badge.ts +62 -0
- package/src/components/command-bar/list/model.test.ts +103 -0
- package/src/components/command-bar/list/model.ts +61 -4
- package/src/components/command-bar/list/view.test.tsx +113 -0
- package/src/components/command-bar/list/view.tsx +147 -131
- package/src/components/command-bar/pane-templates/workflow.ts +1 -1
- package/src/components/command-bar/panel/body.tsx +51 -65
- package/src/components/command-bar/panel/index.tsx +48 -13
- package/src/components/command-bar/panel/layout.test.ts +139 -0
- package/src/components/command-bar/panel/layout.ts +65 -28
- package/src/components/command-bar/panel/native-surface.ts +24 -0
- package/src/components/command-bar/panel/prompt-binding.ts +54 -0
- package/src/components/command-bar/panel/runtime.ts +6 -3
- package/src/components/command-bar/panel/state.ts +41 -7
- package/src/components/command-bar/panel/types.ts +7 -1
- package/src/components/command-bar/routes/root/provider-search.ts +34 -15
- package/src/components/command-bar/routes/root/results.test.ts +161 -0
- package/src/components/command-bar/routes/root/results.ts +30 -28
- package/src/components/command-bar/routes/root/runtime.ts +37 -20
- package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
- package/src/components/command-bar/routes/root/search-providers.ts +205 -0
- package/src/components/command-bar/routes/root/selection.ts +1 -12
- package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
- package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
- package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
- package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
- package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
- package/src/components/command-bar/routing/actions.ts +1 -81
- package/src/components/command-bar/routing/launch-request.ts +1 -1
- package/src/components/command-bar/routing/list-state.ts +6 -21
- package/src/components/command-bar/selection-runtime.ts +1 -1
- package/src/components/command-bar/surface/assist.test.tsx +14 -10
- package/src/components/command-bar/surface/environment.ts +8 -1
- package/src/components/command-bar/surface/index.test.tsx +84 -68
- package/src/components/command-bar/surface/index.tsx +33 -36
- package/src/components/command-bar/surface/test-harness.tsx +15 -3
- package/src/components/command-bar/theme-picker.tsx +43 -15
- package/src/components/command-bar/view-model.test.ts +53 -8
- package/src/components/command-bar/view-model.ts +43 -14
- package/src/components/command-bar/workflow/tickers.ts +1 -0
- package/src/components/command-bar/workflow/types.ts +1 -1
- package/src/components/data-table/view.test.tsx +45 -6
- package/src/components/data-table/view.tsx +55 -9
- package/src/components/index.ts +10 -2
- package/src/components/input-search-bar.test.tsx +36 -2
- package/src/components/input-search-bar.tsx +18 -1
- package/src/components/layout/header.test.tsx +57 -10
- package/src/components/layout/header.tsx +292 -149
- package/src/components/layout/market-summary.test.ts +81 -0
- package/src/components/layout/market-summary.ts +109 -0
- package/src/components/layout/shell/chrome.ts +96 -1
- package/src/components/layout/shell/index.tsx +11 -3
- package/src/components/layout/shell/menu.ts +9 -0
- package/src/components/layout/shell/pane/actions.ts +29 -0
- package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
- package/src/components/layout/shell/shortcuts.ts +3 -0
- package/src/components/layout/status-bar.test.tsx +9 -8
- package/src/components/layout/status-bar.tsx +85 -54
- package/src/components/onboarding/onboarding-frame.tsx +2 -26
- package/src/components/price-sparkline/index.ts +61 -34
- package/src/components/price-sparkline/view.tsx +1 -1
- package/src/components/sign-in-gate.test.tsx +128 -0
- package/src/components/sign-in-gate.tsx +149 -0
- package/src/components/speedometer-gauge/desktop.tsx +2 -12
- package/src/components/ticker/badge/list.tsx +17 -3
- package/src/components/ticker/list-table-view.tsx +6 -3
- package/src/components/ui/data-table/index.tsx +3 -2
- package/src/components/ui/data-table/opentui/index.tsx +165 -120
- package/src/components/ui/frame.tsx +28 -0
- package/src/components/ui/index.ts +3 -0
- package/src/components/ui/native-select.tsx +20 -10
- package/src/components/ui/select-button.tsx +138 -0
- package/src/components/ui/status.tsx +2 -2
- package/src/components/ui/table-layout.ts +1 -1
- package/src/components/use-double-click-activation.ts +7 -3
- package/src/core/app-services.ts +3 -3
- package/src/data/config/layout.ts +12 -6
- package/src/data/config/store/index.test.ts +97 -0
- package/src/data/config/store/migrations.ts +41 -0
- package/src/data/config/store/normalize.ts +5 -3
- package/src/i18n/es.ts +6 -2
- package/src/i18n/ja.ts +6 -2
- package/src/i18n/ko.ts +6 -2
- package/src/i18n/zh-cn.ts +6 -2
- package/src/i18n/zh-tw.ts +6 -2
- package/src/layout-marketplace/pane-imagery.ts +1 -0
- package/src/layout-marketplace/payload.test.ts +17 -0
- package/src/layout-marketplace/payload.ts +40 -5
- package/src/market-data/coordinator/index.ts +3 -2
- package/src/market-data/coordinator/quotes.ts +173 -66
- package/src/market-data/coordinator/subscriptions.test.ts +215 -37
- package/src/pane-settings.ts +1 -1
- package/src/plugins/builtin/analytics/pane-model.ts +1 -1
- package/src/plugins/builtin/analytics/view.tsx +1 -1
- package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
- package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
- package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
- package/src/plugins/builtin/chat/content/composer.tsx +8 -4
- package/src/plugins/builtin/cloud/auth-dialog.tsx +14 -228
- package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
- package/src/plugins/builtin/cloud/plugin.tsx +1 -0
- package/src/plugins/builtin/composite-plugins.ts +14 -1
- package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
- package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
- package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
- package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
- package/src/plugins/builtin/credit-conditions/index.tsx +2 -21
- package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
- package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
- package/src/plugins/builtin/earnings-calls/data.ts +223 -0
- package/src/plugins/builtin/earnings-calls/format.ts +49 -0
- package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
- package/src/plugins/builtin/earnings-calls/pane.tsx +756 -0
- package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
- package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
- package/src/plugins/builtin/earnings-calls/transcript-view.tsx +334 -0
- package/src/plugins/builtin/econ/detail-view.tsx +1 -2
- package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
- package/src/plugins/builtin/econ/index.tsx +2 -1
- package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
- package/src/plugins/builtin/econ-statistics/client.ts +92 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
- package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
- package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
- package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
- package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
- package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
- package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
- package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
- package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
- package/src/plugins/builtin/econ-statistics/view.ts +90 -0
- package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
- package/src/plugins/builtin/fear-greed/data.ts +6 -1
- package/src/plugins/builtin/help/index.tsx +4 -0
- package/src/plugins/builtin/holders/index.tsx +1 -0
- package/src/plugins/builtin/insider/index.tsx +1 -0
- package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
- package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
- package/src/plugins/builtin/market-valuation/align.ts +122 -0
- package/src/plugins/builtin/market-valuation/cache.ts +32 -0
- package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
- package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
- package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
- package/src/plugins/builtin/market-valuation/client.ts +116 -0
- package/src/plugins/builtin/market-valuation/defs.ts +251 -0
- package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
- package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
- package/src/plugins/builtin/market-valuation/headless.ts +170 -0
- package/src/plugins/builtin/market-valuation/index.tsx +64 -0
- package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
- package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
- package/src/plugins/builtin/market-valuation/model.ts +8 -0
- package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
- package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
- package/src/plugins/builtin/market-valuation/series.ts +12 -0
- package/src/plugins/builtin/market-valuation/settings.ts +38 -0
- package/src/plugins/builtin/market-valuation/sources.ts +130 -0
- package/src/plugins/builtin/market-valuation/trend.ts +89 -0
- package/src/plugins/builtin/market-valuation/view.ts +137 -0
- package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
- package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
- package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
- package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
- package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
- package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
- package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
- package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
- package/src/plugins/builtin/news/wire/index.tsx +31 -44
- package/src/plugins/builtin/options/index.tsx +1 -0
- package/src/plugins/builtin/options/view.test.tsx +29 -1
- package/src/plugins/builtin/options/view.tsx +1 -22
- package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
- package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
- package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
- package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
- package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
- package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
- package/src/plugins/builtin/plugin-module.ts +9 -1
- package/src/plugins/builtin/research/index.tsx +4 -0
- package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
- package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
- package/src/plugins/builtin/research-search/data.ts +109 -0
- package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
- package/src/plugins/builtin/research-search/document-view.tsx +254 -0
- package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
- package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
- package/src/plugins/builtin/research-search/index.tsx +85 -0
- package/src/plugins/builtin/research-search/model.test.ts +40 -0
- package/src/plugins/builtin/research-search/model.ts +291 -0
- package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
- package/src/plugins/builtin/research-search/pane.tsx +678 -0
- package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
- package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
- package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
- package/src/plugins/builtin/research-search/snippet.ts +134 -0
- package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
- package/src/plugins/builtin/scanner/flow-model.ts +29 -1
- package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
- package/src/plugins/builtin/sec/index.tsx +1 -0
- package/src/plugins/builtin/shared/series-cache.ts +93 -0
- package/src/plugins/builtin/short-interest/index.tsx +1 -0
- package/src/plugins/builtin/short-interest/pane.tsx +1 -1
- package/src/plugins/builtin/thirteenf/index.tsx +1 -0
- package/src/plugins/builtin/ticker-detail/index.tsx +0 -14
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
- package/src/plugins/builtin/volatility/index.tsx +1 -1
- package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
- package/src/plugins/builtin/world-venue-map/map.tsx +213 -69
- package/src/plugins/builtin/world-venue-map/model.test.ts +32 -0
- package/src/plugins/builtin/world-venue-map/model.ts +134 -13
- package/src/plugins/builtin/yield-curve/index.tsx +1 -1
- package/src/plugins/bundle.test.ts +109 -0
- package/src/plugins/bundle.ts +139 -0
- package/src/plugins/catalog-browser.test.ts +1 -0
- package/src/plugins/catalog-browser.ts +10 -0
- package/src/plugins/catalog-manifest.test.ts +43 -0
- package/src/plugins/catalog-manifest.ts +92 -0
- package/src/plugins/catalog-ui.ts +20 -4
- package/src/plugins/catalog.ts +9 -2
- package/src/plugins/current-target.ts +28 -0
- package/src/plugins/host-contract.ts +37 -0
- package/src/plugins/host-link.ts +158 -0
- package/src/plugins/host-modules.ts +61 -0
- package/src/plugins/loader.ts +41 -22
- package/src/plugins/ownership.ts +1 -0
- package/src/plugins/prediction-markets/index.tsx +1 -0
- package/src/plugins/registry/context.ts +3 -0
- package/src/plugins/registry/contributions.ts +27 -0
- package/src/plugins/registry/index.test.ts +25 -0
- package/src/plugins/registry/index.ts +8 -0
- package/src/plugins/registry/pane-settings.ts +6 -13
- package/src/plugins/seed.test.ts +83 -0
- package/src/plugins/seed.ts +80 -0
- package/src/public/broker.ts +31 -0
- package/src/public/market-data.ts +9 -0
- package/src/public/pane-hooks.ts +57 -0
- package/src/public/public-api.test.ts +285 -0
- package/src/public/react.ts +62 -0
- package/src/public/test-support.ts +23 -0
- package/src/public/time-series.ts +8 -0
- package/src/public/utils.ts +77 -0
- package/src/remote/layout-helpers.ts +6 -8
- package/src/remote/resources.ts +2 -0
- package/src/remote/schema.ts +1 -0
- package/src/remote/semantic-tree.test.tsx +105 -0
- package/src/remote/semantic-tree.tsx +53 -35
- package/src/renderers/browser/main.tsx +6 -0
- package/src/renderers/cloudflare/worker.test.ts +19 -0
- package/src/renderers/cloudflare/worker.ts +1 -1
- package/src/renderers/electrobun/bun/external-plugins.ts +140 -0
- package/src/renderers/electrobun/bun/index.ts +5 -2
- package/src/renderers/electrobun/shared/protocol.ts +21 -0
- package/src/renderers/electrobun/view/build-assets.ts +0 -1
- package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +51 -1
- package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
- package/src/renderers/electrobun/view/external-plugins.ts +63 -0
- package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
- package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
- package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
- package/src/renderers/electrobun/view/host/mouse.ts +16 -2
- package/src/renderers/electrobun/view/main.tsx +27 -2
- package/src/renderers/electrobun/view/styles.css +5 -0
- package/src/renderers/electrobun/view/toast-host.tsx +18 -0
- package/src/renderers/opentui/chart-surface.tsx +5 -1
- package/src/renderers/opentui/host.tsx +16 -2
- package/src/renderers/opentui/interaction-performance.test.ts +79 -0
- package/src/renderers/opentui/interaction-performance.ts +188 -0
- package/src/renderers/opentui/start.tsx +26 -1
- package/src/renderers/opentui/test-utils.tsx +13 -0
- package/src/renderers/opentui/toast-host.tsx +26 -2
- package/src/renderers/opentui/ui-host.tsx +1 -0
- package/src/sources/provider-router/search.test.ts +191 -0
- package/src/sources/provider-router/search.ts +92 -39
- package/src/state/app/context/index.tsx +2 -0
- package/src/state/app/context/selector.test.tsx +33 -1
- package/src/state/hooks/quote-streaming.test.tsx +93 -0
- package/src/state/hooks/quote-streaming.ts +106 -30
- package/src/state/pane-table-export-registry.ts +19 -0
- package/src/theme/color-utils.ts +1 -1
- package/src/theme/colors.ts +50 -0
- package/src/theme/themes.test.ts +15 -2
- package/src/theme/themes.ts +19 -1
- package/src/tickers/search/index.test.ts +26 -0
- package/src/tickers/search/index.ts +42 -17
- package/src/tickers/search/ranking.ts +14 -0
- package/src/time-series/chart-data.test.ts +216 -0
- package/src/time-series/resolution.test.ts +25 -26
- package/src/time-series/resolution.ts +41 -10
- package/src/time-series/resolve.ts +7 -0
- package/src/time-series/types.ts +3 -1
- package/src/time-series/use-chart-resolution.ts +5 -0
- package/src/types/config.ts +123 -3
- package/src/types/data-provider.ts +11 -0
- package/src/types/headless.ts +161 -0
- package/src/types/plugin.ts +102 -0
- package/src/ui/host.tsx +12 -1
- package/src/ui/index.tsx +1 -0
- package/src/ui/toast.tsx +8 -4
- package/src/utils/format.ts +35 -0
- package/src/utils/fuzzy-search.test.ts +94 -0
- package/src/utils/fuzzy-search.ts +81 -22
- package/src/utils/shortcut-labels.ts +14 -0
- package/src/components/chart/core/axis-format.ts +0 -157
- package/src/components/chart/core/index.test.ts +0 -1075
- package/src/components/chart/core/projection.ts +0 -357
- package/src/components/chart/core/render-utils.ts +0 -4
- package/src/components/chart/core/renderer.ts +0 -244
- package/src/components/chart/core/scene.ts +0 -180
- package/src/components/chart/core/time-axis.ts +0 -330
- package/src/components/chart/cursor-motion.ts +0 -47
- package/src/components/chart/indicators/types.ts +0 -26
- package/src/components/chart/market-session.ts +0 -176
- package/src/components/chart/multi-line/renderer.test.ts +0 -83
- package/src/components/chart/multi-line/renderer.ts +0 -161
- package/src/components/chart/multi-line/scene.ts +0 -188
- package/src/components/chart/native/raster/layout.ts +0 -129
- package/src/components/chart/native/raster/price-chart.ts +0 -244
- package/src/components/chart/rendering/chart-draw.ts +0 -270
- package/src/components/chart/rendering/geometry.ts +0 -95
- package/src/components/chart/rendering/pixel-buffer.ts +0 -267
- package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
- package/src/components/chart/scatter-chart-renderer.ts +0 -223
- package/src/components/chart/static/chart/surface.tsx +0 -358
- package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
- package/src/components/command-bar/routes/root/article-results.ts +0 -55
- package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
- package/src/plugins/builtin/news/wire/article-search.ts +0 -140
- package/src/plugins/builtin/substack/api/auth.ts +0 -185
- package/src/plugins/builtin/substack/api/cache.ts +0 -82
- package/src/plugins/builtin/substack/api/loaders.ts +0 -170
- package/src/plugins/builtin/substack/api/store.ts +0 -186
- package/src/plugins/builtin/substack/api/types.ts +0 -42
- package/src/plugins/builtin/substack/api.test.ts +0 -298
- package/src/plugins/builtin/substack/article-detail.tsx +0 -397
- package/src/plugins/builtin/substack/article-stack.tsx +0 -133
- package/src/plugins/builtin/substack/content.ts +0 -478
- package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
- package/src/plugins/builtin/substack/index.tsx +0 -49
- package/src/plugins/builtin/substack/login-view.tsx +0 -126
- package/src/plugins/builtin/substack/model.test.ts +0 -155
- package/src/plugins/builtin/substack/normalize.ts +0 -343
- package/src/plugins/builtin/substack/pane-footer.ts +0 -52
- package/src/plugins/builtin/substack/pane-state.ts +0 -170
- package/src/plugins/builtin/substack/pane.tsx +0 -547
- package/src/plugins/builtin/substack/read-state.ts +0 -30
- package/src/plugins/builtin/substack/table.ts +0 -111
- package/src/plugins/builtin/substack/types.ts +0 -71
- package/src/plugins/builtin/substack/utils.ts +0 -131
- package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
- package/src/plugins/ibkr/account-cache.test.ts +0 -102
- package/src/plugins/ibkr/account-cache.ts +0 -24
- package/src/plugins/ibkr/broker-adapter.ts +0 -286
- package/src/plugins/ibkr/config.test.ts +0 -79
- package/src/plugins/ibkr/config.ts +0 -250
- package/src/plugins/ibkr/dialogs.tsx +0 -40
- package/src/plugins/ibkr/flex/index.test.ts +0 -264
- package/src/plugins/ibkr/flex/index.ts +0 -367
- package/src/plugins/ibkr/flex/statement-client.ts +0 -235
- package/src/plugins/ibkr/fundamental-parser.ts +0 -149
- package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
- package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
- package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
- package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
- package/src/plugins/ibkr/gateway/connection.ts +0 -152
- package/src/plugins/ibkr/gateway/contracts.ts +0 -132
- package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
- package/src/plugins/ibkr/gateway/helpers.ts +0 -33
- package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
- package/src/plugins/ibkr/gateway/history.ts +0 -120
- package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
- package/src/plugins/ibkr/gateway/market-data.ts +0 -345
- package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
- package/src/plugins/ibkr/gateway/orders.ts +0 -63
- package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
- package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
- package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
- package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
- package/src/plugins/ibkr/gateway/selection.ts +0 -51
- package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
- package/src/plugins/ibkr/gateway/service/index.ts +0 -429
- package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
- package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
- package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
- package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
- package/src/plugins/ibkr/gateway/service/native.ts +0 -543
- package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
- package/src/plugins/ibkr/gateway/types.ts +0 -27
- package/src/plugins/ibkr/index.test.tsx +0 -162
- package/src/plugins/ibkr/index.tsx +0 -159
- package/src/plugins/ibkr/instance-selection.test.ts +0 -120
- package/src/plugins/ibkr/instance-selection.ts +0 -36
- package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
- package/src/plugins/ibkr/portfolio-performance.ts +0 -129
- package/src/plugins/ibkr/trade/badge.tsx +0 -28
- package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
- package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
- package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
- package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
- package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
- package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
- package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
- package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
- package/src/plugins/ibkr/trade/tab/model.ts +0 -159
- package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
- package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
- package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
- package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
- package/src/plugins/ibkr/trade/utils.test.ts +0 -175
- package/src/plugins/ibkr/trade/utils.ts +0 -144
- package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
- package/src/plugins/ibkr/trading/pane.tsx +0 -332
- package/src/plugins/ibkr/trading/state.test.ts +0 -109
- package/src/plugins/ibkr/trading/state.ts +0 -335
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { describe, expect, test } from "bun:test";
|
|
2
|
+
import { splitFigures, splitSentences } from "./prose";
|
|
3
|
+
|
|
4
|
+
describe("splitFigures", () => {
|
|
5
|
+
test("picks out money, percentages and quantities, leaves years and labels", () => {
|
|
6
|
+
const runs = splitFigures(
|
|
7
|
+
"Revenue grew 20% to $1.2 billion in 2026, or 150 bps above Q2 guidance of $900 million to $1 billion, with 2,500 hires.",
|
|
8
|
+
);
|
|
9
|
+
expect(runs.filter((run) => run.figure).map((run) => run.text)).toEqual([
|
|
10
|
+
"20%",
|
|
11
|
+
"$1.2 billion",
|
|
12
|
+
"150 bps",
|
|
13
|
+
"$900 million",
|
|
14
|
+
"$1 billion",
|
|
15
|
+
"2,500",
|
|
16
|
+
]);
|
|
17
|
+
// Everything is accounted for, nothing dropped or duplicated.
|
|
18
|
+
expect(runs.map((run) => run.text).join("")).toBe(
|
|
19
|
+
"Revenue grew 20% to $1.2 billion in 2026, or 150 bps above Q2 guidance of $900 million to $1 billion, with 2,500 hires.",
|
|
20
|
+
);
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
describe("splitSentences", () => {
|
|
25
|
+
test("splits on sentence ends but not on abbreviations or decimals", () => {
|
|
26
|
+
expect(
|
|
27
|
+
splitSentences(
|
|
28
|
+
"The U.S. business grew. Margins were 27.8% vs. 25% last year. Acme Inc. expects $5 million and under; management sees this as transferable.",
|
|
29
|
+
),
|
|
30
|
+
).toEqual([
|
|
31
|
+
"The U.S. business grew.",
|
|
32
|
+
"Margins were 27.8% vs. 25% last year.",
|
|
33
|
+
"Acme Inc. expects $5 million and under; management sees this as transferable.",
|
|
34
|
+
]);
|
|
35
|
+
});
|
|
36
|
+
});
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Text shaping for the call reader. Model summaries arrive as dense
|
|
3
|
+
* paragraphs; on a wide pane they are a wall. Splitting them into one point
|
|
4
|
+
* per sentence and setting the figures in bold is what makes them scannable.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface ProseRun {
|
|
8
|
+
text: string;
|
|
9
|
+
/** A number the eye should land on: money, percentages, quantities. */
|
|
10
|
+
figure: boolean;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Money, percentages and quantities with a unit, plus bare numbers except
|
|
15
|
+
* years. Ordinals and fiscal labels (Q2, FY26, 2Q) are left alone.
|
|
16
|
+
*/
|
|
17
|
+
const FIGURE_PATTERN =
|
|
18
|
+
/[$€£¥]\s?\d[\d,]*(?:\.\d+)?(?:\s?(?:trillion|billion|million|thousand|bn|mn|k|m|b)\b)?|\b\d[\d,]*(?:\.\d+)?\s?(?:%|percent(?:age points?)?|bps|basis points|x\b|trillion|billion|million|thousand)|\b\d[\d,]*(?:\.\d+)?\b/gi;
|
|
19
|
+
|
|
20
|
+
const YEAR_PATTERN = /^(?:19|20)\d{2}$/;
|
|
21
|
+
|
|
22
|
+
export function splitFigures(text: string): ProseRun[] {
|
|
23
|
+
const runs: ProseRun[] = [];
|
|
24
|
+
let last = 0;
|
|
25
|
+
for (const match of text.matchAll(FIGURE_PATTERN)) {
|
|
26
|
+
const start = match.index ?? 0;
|
|
27
|
+
const value = match[0];
|
|
28
|
+
if (YEAR_PATTERN.test(value)) continue;
|
|
29
|
+
if (start > last) runs.push({ text: text.slice(last, start), figure: false });
|
|
30
|
+
runs.push({ text: value, figure: true });
|
|
31
|
+
last = start + value.length;
|
|
32
|
+
}
|
|
33
|
+
if (last < text.length) runs.push({ text: text.slice(last), figure: false });
|
|
34
|
+
return runs;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* Sentence boundaries, minus the periods inside abbreviations ("U.S.",
|
|
39
|
+
* "Inc.", "vs.") and figures ("$27.8 million").
|
|
40
|
+
*/
|
|
41
|
+
const SENTENCE_BOUNDARY =
|
|
42
|
+
/(?<!\b(?:[A-Z]|Inc|Ltd|Co|Corp|vs|Mr|Ms|Mrs|Dr|No|St|Jr|Sr|U\.S|e\.g|i\.e|approx))[.!?]["”')]?\s+(?=["“(]?[A-Z0-9$])/;
|
|
43
|
+
|
|
44
|
+
export function splitSentences(text: string): string[] {
|
|
45
|
+
const out: string[] = [];
|
|
46
|
+
let rest = text.trim();
|
|
47
|
+
while (rest.length > 0) {
|
|
48
|
+
const match = SENTENCE_BOUNDARY.exec(rest);
|
|
49
|
+
if (!match) {
|
|
50
|
+
out.push(rest);
|
|
51
|
+
break;
|
|
52
|
+
}
|
|
53
|
+
// Keep the closing punctuation, drop the whitespace that followed it.
|
|
54
|
+
const cut = match.index + match[0].trimEnd().length;
|
|
55
|
+
out.push(rest.slice(0, cut).trim());
|
|
56
|
+
rest = rest.slice(match.index + match[0].length);
|
|
57
|
+
}
|
|
58
|
+
return out.filter(Boolean);
|
|
59
|
+
}
|
|
@@ -0,0 +1,334 @@
|
|
|
1
|
+
import { useMemo, type RefObject } from "react";
|
|
2
|
+
import { Spinner } from "../../../components";
|
|
3
|
+
import { Tabs } from "../../../components/ui/tabs";
|
|
4
|
+
import { colors } from "../../../theme/colors";
|
|
5
|
+
import {
|
|
6
|
+
Box,
|
|
7
|
+
ScrollBox,
|
|
8
|
+
StyledText,
|
|
9
|
+
Text,
|
|
10
|
+
TextAttributes,
|
|
11
|
+
useUiCapabilities,
|
|
12
|
+
type ScrollBoxRenderable,
|
|
13
|
+
} from "../../../ui";
|
|
14
|
+
import { wrapTextLines } from "../../../utils/text-wrap";
|
|
15
|
+
import type {
|
|
16
|
+
CloudEarningsTranscriptPayload,
|
|
17
|
+
CloudTranscriptTurnPayload,
|
|
18
|
+
} from "../../../api-client";
|
|
19
|
+
import { formatCallDate, formatDuration, formatSentiment, formatTimestamp } from "./format";
|
|
20
|
+
import { splitFigures, splitSentences } from "./prose";
|
|
21
|
+
|
|
22
|
+
export type ReaderTab = "summary" | "transcript" | "qa";
|
|
23
|
+
|
|
24
|
+
export const READER_TABS: Array<{ label: string; value: ReaderTab }> = [
|
|
25
|
+
{ label: "Summary", value: "summary" },
|
|
26
|
+
{ label: "Transcript", value: "transcript" },
|
|
27
|
+
{ label: "Q&A", value: "qa" },
|
|
28
|
+
];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Prose is capped at a comfortable measure. On a wide pane a full-width line
|
|
32
|
+
* is over two hundred characters, which the eye loses on the way back.
|
|
33
|
+
*/
|
|
34
|
+
const MAX_PROSE_WIDTH = 100;
|
|
35
|
+
|
|
36
|
+
function speakerColor(turn: CloudTranscriptTurnPayload): string {
|
|
37
|
+
if (turn.speaker === "Operator") return colors.textDim;
|
|
38
|
+
if (turn.role === "Analyst") return colors.warning;
|
|
39
|
+
return colors.textBright;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/** Role and firm after the name; "Operator" is not repeated as its own role. */
|
|
43
|
+
function turnDetail(turn: CloudTranscriptTurnPayload): string {
|
|
44
|
+
const role = turn.role && turn.role !== turn.speaker ? turn.role : null;
|
|
45
|
+
return [role, turn.company].filter(Boolean).join(", ");
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const NATIVE_STRETCH_STYLE = { minWidth: 0 };
|
|
49
|
+
const NATIVE_TEXT_STYLE = { display: "block" };
|
|
50
|
+
|
|
51
|
+
/** Figures set in bold, the rest in the given colour. */
|
|
52
|
+
function styledRuns(text: string, color: string, attributes = 0): StyledText {
|
|
53
|
+
return new StyledText(
|
|
54
|
+
splitFigures(text).map((run) => ({
|
|
55
|
+
text: run.text,
|
|
56
|
+
fg: run.figure ? colors.textBright : color,
|
|
57
|
+
attributes: run.figure ? attributes | TextAttributes.BOLD : attributes,
|
|
58
|
+
})),
|
|
59
|
+
);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* A paragraph with figures in bold. The terminal renderer does not wrap on
|
|
64
|
+
* its own, so lines are pre-wrapped and styled one at a time; desktop
|
|
65
|
+
* chrome wraps the styled paragraph itself. `indent` is applied to every
|
|
66
|
+
* line after the first, for bullets.
|
|
67
|
+
*/
|
|
68
|
+
function Prose({
|
|
69
|
+
text,
|
|
70
|
+
width,
|
|
71
|
+
color,
|
|
72
|
+
nativePaneChrome,
|
|
73
|
+
prefix = "",
|
|
74
|
+
}: {
|
|
75
|
+
text: string;
|
|
76
|
+
width: number;
|
|
77
|
+
color: string;
|
|
78
|
+
nativePaneChrome: boolean;
|
|
79
|
+
/** Put before the first line; later lines are indented by its width. */
|
|
80
|
+
prefix?: string;
|
|
81
|
+
}) {
|
|
82
|
+
if (!text.trim()) return null;
|
|
83
|
+
if (nativePaneChrome) {
|
|
84
|
+
return (
|
|
85
|
+
<Box flexDirection="row" width="100%" style={NATIVE_STRETCH_STYLE}>
|
|
86
|
+
{prefix ? <Text fg={colors.textDim}>{prefix}</Text> : null}
|
|
87
|
+
<Text
|
|
88
|
+
wrapText
|
|
89
|
+
width="100%"
|
|
90
|
+
style={NATIVE_TEXT_STYLE}
|
|
91
|
+
content={styledRuns(text, color)}
|
|
92
|
+
/>
|
|
93
|
+
</Box>
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
const indent = " ".repeat(prefix.length);
|
|
97
|
+
return wrapTextLines(text, Math.max(8, width - prefix.length)).map((line, index) => (
|
|
98
|
+
<Box key={index} height={1} flexDirection="row">
|
|
99
|
+
{prefix ? (
|
|
100
|
+
<Text fg={colors.textDim} flexShrink={0}>
|
|
101
|
+
{index === 0 ? prefix : indent}
|
|
102
|
+
</Text>
|
|
103
|
+
) : null}
|
|
104
|
+
<Text content={styledRuns(line, color)} />
|
|
105
|
+
</Box>
|
|
106
|
+
));
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function SectionHeading({ title }: { title: string }) {
|
|
110
|
+
return (
|
|
111
|
+
<Box height={1} marginTop={1}>
|
|
112
|
+
<Text fg={colors.textDim} attributes={TextAttributes.BOLD}>
|
|
113
|
+
{title}
|
|
114
|
+
</Text>
|
|
115
|
+
</Box>
|
|
116
|
+
);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** A summary section as one point per sentence. */
|
|
120
|
+
function Section({
|
|
121
|
+
title,
|
|
122
|
+
body,
|
|
123
|
+
width,
|
|
124
|
+
nativePaneChrome,
|
|
125
|
+
}: {
|
|
126
|
+
title: string;
|
|
127
|
+
body: string;
|
|
128
|
+
width: number;
|
|
129
|
+
nativePaneChrome: boolean;
|
|
130
|
+
}) {
|
|
131
|
+
if (!body.trim()) return null;
|
|
132
|
+
return (
|
|
133
|
+
<Box flexDirection="column">
|
|
134
|
+
<SectionHeading title={title} />
|
|
135
|
+
{splitSentences(body).map((sentence, index) => (
|
|
136
|
+
<Prose
|
|
137
|
+
key={index}
|
|
138
|
+
text={sentence}
|
|
139
|
+
width={width}
|
|
140
|
+
color={colors.text}
|
|
141
|
+
nativePaneChrome={nativePaneChrome}
|
|
142
|
+
prefix="• "
|
|
143
|
+
/>
|
|
144
|
+
))}
|
|
145
|
+
</Box>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function TurnView({
|
|
150
|
+
turn,
|
|
151
|
+
width,
|
|
152
|
+
nativePaneChrome,
|
|
153
|
+
}: {
|
|
154
|
+
turn: CloudTranscriptTurnPayload;
|
|
155
|
+
width: number;
|
|
156
|
+
nativePaneChrome: boolean;
|
|
157
|
+
}) {
|
|
158
|
+
const detail = turnDetail(turn);
|
|
159
|
+
return (
|
|
160
|
+
<Box flexDirection="column" marginTop={1}>
|
|
161
|
+
<Box height={1} flexDirection="row" gap={1} overflow="hidden">
|
|
162
|
+
<Text fg={colors.textDim}>{formatTimestamp(turn.startSeconds)}</Text>
|
|
163
|
+
<Text fg={speakerColor(turn)} attributes={TextAttributes.BOLD}>
|
|
164
|
+
{turn.speaker}
|
|
165
|
+
</Text>
|
|
166
|
+
{detail ? <Text fg={colors.textDim}>{detail}</Text> : null}
|
|
167
|
+
</Box>
|
|
168
|
+
<Prose text={turn.text} width={width} color={colors.text} nativePaneChrome={nativePaneChrome} />
|
|
169
|
+
</Box>
|
|
170
|
+
);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
export function TranscriptView({
|
|
174
|
+
transcript,
|
|
175
|
+
loading,
|
|
176
|
+
error,
|
|
177
|
+
tab,
|
|
178
|
+
onTabChange,
|
|
179
|
+
tabsFocused,
|
|
180
|
+
query,
|
|
181
|
+
width,
|
|
182
|
+
scrollRef,
|
|
183
|
+
}: {
|
|
184
|
+
transcript: CloudEarningsTranscriptPayload | null;
|
|
185
|
+
loading: boolean;
|
|
186
|
+
error: string | null;
|
|
187
|
+
tab: ReaderTab;
|
|
188
|
+
onTabChange: (tab: ReaderTab) => void;
|
|
189
|
+
/** Whether left/right should move between tabs. */
|
|
190
|
+
tabsFocused: boolean;
|
|
191
|
+
/** Free-text filter applied to the turns, for finding a topic in a long call. */
|
|
192
|
+
query?: string;
|
|
193
|
+
width: number;
|
|
194
|
+
/** Lets the owning pane drive keyboard scrolling. */
|
|
195
|
+
scrollRef?: RefObject<ScrollBoxRenderable | null>;
|
|
196
|
+
}) {
|
|
197
|
+
const { nativePaneChrome } = useUiCapabilities();
|
|
198
|
+
const isNative = nativePaneChrome === true;
|
|
199
|
+
|
|
200
|
+
const turns = useMemo(() => {
|
|
201
|
+
const all = transcript?.turns ?? [];
|
|
202
|
+
const scoped = tab === "qa" ? all.filter((turn) => turn.isQa) : all;
|
|
203
|
+
const needle = (query ?? "").trim().toLowerCase();
|
|
204
|
+
if (!needle) return scoped;
|
|
205
|
+
return scoped.filter((turn) =>
|
|
206
|
+
`${turn.speaker} ${turn.company ?? ""} ${turn.text}`.toLowerCase().includes(needle),
|
|
207
|
+
);
|
|
208
|
+
}, [transcript, tab, query]);
|
|
209
|
+
|
|
210
|
+
if (loading && !transcript) {
|
|
211
|
+
return (
|
|
212
|
+
<Box flexGrow={1} alignItems="center" justifyContent="center">
|
|
213
|
+
<Spinner label="Loading transcript..." />
|
|
214
|
+
</Box>
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (error && !transcript) {
|
|
219
|
+
return (
|
|
220
|
+
<Box flexGrow={1} paddingX={1}>
|
|
221
|
+
<Text fg={colors.negative}>{error}</Text>
|
|
222
|
+
</Box>
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
if (!transcript) return null;
|
|
227
|
+
|
|
228
|
+
// The stack title already names ticker and period, so lead with metadata.
|
|
229
|
+
const meta = [
|
|
230
|
+
formatCallDate(transcript.callAt),
|
|
231
|
+
formatDuration(transcript.durationSeconds),
|
|
232
|
+
transcript.participants.length > 0 ? `${transcript.participants.length} speakers` : null,
|
|
233
|
+
transcript.sentiment !== null ? `sentiment ${formatSentiment(transcript.sentiment)}` : null,
|
|
234
|
+
]
|
|
235
|
+
.filter(Boolean)
|
|
236
|
+
.join(" · ");
|
|
237
|
+
|
|
238
|
+
// One column of padding each side inside the scroll box.
|
|
239
|
+
const bodyWidth = Math.max(12, width - 2);
|
|
240
|
+
const proseWidth = Math.min(bodyWidth, MAX_PROSE_WIDTH);
|
|
241
|
+
const contentWidth = isNative ? "100%" : bodyWidth;
|
|
242
|
+
const contentStyle = isNative ? NATIVE_STRETCH_STYLE : undefined;
|
|
243
|
+
const hasQa = (transcript.turns ?? []).some((turn) => turn.isQa);
|
|
244
|
+
|
|
245
|
+
return (
|
|
246
|
+
<Box
|
|
247
|
+
flexDirection="column"
|
|
248
|
+
flexGrow={1}
|
|
249
|
+
flexShrink={1}
|
|
250
|
+
flexBasis={0}
|
|
251
|
+
minHeight={0}
|
|
252
|
+
overflow="hidden"
|
|
253
|
+
>
|
|
254
|
+
<Box height={1} flexShrink={0} paddingX={1} overflow="hidden">
|
|
255
|
+
<Tabs
|
|
256
|
+
tabs={READER_TABS.map((entry) => ({
|
|
257
|
+
label: entry.label,
|
|
258
|
+
value: entry.value,
|
|
259
|
+
disabled: entry.value === "qa" && !hasQa,
|
|
260
|
+
}))}
|
|
261
|
+
activeValue={tab}
|
|
262
|
+
onSelect={(value) => onTabChange(value as ReaderTab)}
|
|
263
|
+
compact
|
|
264
|
+
variant="bare"
|
|
265
|
+
focused={tabsFocused}
|
|
266
|
+
/>
|
|
267
|
+
</Box>
|
|
268
|
+
<ScrollBox
|
|
269
|
+
ref={scrollRef}
|
|
270
|
+
flexGrow={1}
|
|
271
|
+
flexShrink={1}
|
|
272
|
+
flexBasis={0}
|
|
273
|
+
minHeight={0}
|
|
274
|
+
scrollY
|
|
275
|
+
focusable={false}
|
|
276
|
+
paddingX={1}
|
|
277
|
+
>
|
|
278
|
+
<Box flexDirection="column" width={contentWidth} style={contentStyle}>
|
|
279
|
+
{tab === "summary" ? (
|
|
280
|
+
<>
|
|
281
|
+
<Prose text={meta} width={proseWidth} color={colors.textDim} nativePaneChrome={isNative} />
|
|
282
|
+
<Section title="SUMMARY" body={transcript.summary ?? ""} width={proseWidth} nativePaneChrome={isNative} />
|
|
283
|
+
<Section title="WHAT STOOD OUT" body={transcript.notable ?? ""} width={proseWidth} nativePaneChrome={isNative} />
|
|
284
|
+
<Section title="ANALYSTS PRESSED ON" body={transcript.analystFocus ?? ""} width={proseWidth} nativePaneChrome={isNative} />
|
|
285
|
+
<Section title="GUIDANCE" body={transcript.guidance ?? ""} width={proseWidth} nativePaneChrome={isNative} />
|
|
286
|
+
<Section title="RISKS" body={transcript.riskFactors ?? ""} width={proseWidth} nativePaneChrome={isNative} />
|
|
287
|
+
{transcript.participants.length > 0 && (
|
|
288
|
+
<Box flexDirection="column">
|
|
289
|
+
<SectionHeading title="PARTICIPANTS" />
|
|
290
|
+
{transcript.participants.map((participant) => (
|
|
291
|
+
<Prose
|
|
292
|
+
key={participant.name}
|
|
293
|
+
text={[participant.name, participant.role, participant.company]
|
|
294
|
+
.filter(Boolean)
|
|
295
|
+
.join(" · ")}
|
|
296
|
+
width={proseWidth}
|
|
297
|
+
color={colors.text}
|
|
298
|
+
nativePaneChrome={isNative}
|
|
299
|
+
/>
|
|
300
|
+
))}
|
|
301
|
+
</Box>
|
|
302
|
+
)}
|
|
303
|
+
</>
|
|
304
|
+
) : (
|
|
305
|
+
<>
|
|
306
|
+
{turns.map((turn, index) => (
|
|
307
|
+
<TurnView
|
|
308
|
+
key={`${turn.startSeconds}-${index}`}
|
|
309
|
+
turn={turn}
|
|
310
|
+
width={proseWidth}
|
|
311
|
+
nativePaneChrome={isNative}
|
|
312
|
+
/>
|
|
313
|
+
))}
|
|
314
|
+
{turns.length === 0 && (
|
|
315
|
+
<Box marginTop={1}>
|
|
316
|
+
<Prose
|
|
317
|
+
text={
|
|
318
|
+
query?.trim()
|
|
319
|
+
? `Nothing matching "${query.trim()}" in this call.`
|
|
320
|
+
: "No question and answer section in this call."
|
|
321
|
+
}
|
|
322
|
+
width={proseWidth}
|
|
323
|
+
color={colors.textDim}
|
|
324
|
+
nativePaneChrome={isNative}
|
|
325
|
+
/>
|
|
326
|
+
</Box>
|
|
327
|
+
)}
|
|
328
|
+
</>
|
|
329
|
+
)}
|
|
330
|
+
</Box>
|
|
331
|
+
</ScrollBox>
|
|
332
|
+
</Box>
|
|
333
|
+
);
|
|
334
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
2
|
import { Box, ScrollBox, Text, TextAttributes, type ScrollBoxRenderable } from "../../../ui";
|
|
3
3
|
import { StaticChartSurface } from "../../../components";
|
|
4
|
-
import { resolveChartPalette } from "../../../components/chart/core/
|
|
4
|
+
import { resolveChartPalette } from "../../../components/chart/core/palette";
|
|
5
5
|
import type { ProjectedChartPoint } from "../../../components/chart/core/data";
|
|
6
6
|
import { colors } from "../../../theme/colors";
|
|
7
7
|
import { apiClient, type CloudFredObservationPayload } from "../../../api-client";
|
|
@@ -263,7 +263,6 @@ export function EconDetailView({ event, width, height, focused }: EconDetailView
|
|
|
263
263
|
height={chartHeight}
|
|
264
264
|
mode="area"
|
|
265
265
|
colors={palette}
|
|
266
|
-
timeAxisDates={chartPoints.map((p) => p.date)}
|
|
267
266
|
showTimeAxis
|
|
268
267
|
timeAxisColor={colors.textDim}
|
|
269
268
|
yAxisLabel={units ? `Value (${units})` : "Value"}
|
|
@@ -98,6 +98,21 @@ const FRED_CATALOG_SERIES: ReadonlyArray<{ seriesId: string; label: string }> =
|
|
|
98
98
|
}));
|
|
99
99
|
})();
|
|
100
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Macro series the catalog should offer that are not economic-calendar releases, so
|
|
103
|
+
* they have no natural home in SERIES_MAP above.
|
|
104
|
+
*/
|
|
105
|
+
const EXTRA_CATALOG_SERIES: ReadonlyArray<{ seriesId: string; label: string }> = [
|
|
106
|
+
{ seriesId: "M2SL", label: "M2 Money Stock" },
|
|
107
|
+
{ seriesId: "DFII10", label: "10Y TIPS Real Yield" },
|
|
108
|
+
{ seriesId: "NCBEILQ027S", label: "Corporate Equities, Z.1" },
|
|
109
|
+
{ seriesId: "TNWMVBSNNCB", label: "Corporate Net Worth, Z.1" },
|
|
110
|
+
];
|
|
111
|
+
|
|
101
112
|
export function listFredCatalogSeries(): ReadonlyArray<{ seriesId: string; label: string }> {
|
|
102
|
-
|
|
113
|
+
const known = new Set(FRED_CATALOG_SERIES.map((entry) => entry.seriesId));
|
|
114
|
+
return [
|
|
115
|
+
...FRED_CATALOG_SERIES,
|
|
116
|
+
...EXTRA_CATALOG_SERIES.filter((entry) => !known.has(entry.seriesId)),
|
|
117
|
+
];
|
|
103
118
|
}
|
|
@@ -406,8 +406,9 @@ export const economicCalendarModule: PluginModule = {
|
|
|
406
406
|
paneId: "econ-calendar",
|
|
407
407
|
label: "Economic Calendar",
|
|
408
408
|
description: "Upcoming economic events, releases, and indicators.",
|
|
409
|
+
// "econ" stays a keyword so the old ECON prefix still finds this pane.
|
|
409
410
|
keywords: ["econ", "economic", "calendar", "events", "macro", "releases", "fed", "cpi", "gdp"],
|
|
410
|
-
shortcut: { prefix: "
|
|
411
|
+
shortcut: { prefix: "ECO" },
|
|
411
412
|
}],
|
|
412
413
|
setup(ctx) {
|
|
413
414
|
attachEconCalendarPersistence(ctx.persistence);
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { apiClient } from "../../../api-client";
|
|
2
|
+
import type { DatedObservation } from "../shared/series-cache";
|
|
3
|
+
import { statsCache } from "./cache";
|
|
4
|
+
import type { StatDef } from "./defs";
|
|
5
|
+
import { STATS } from "./stats";
|
|
6
|
+
import { applyTransform, type StatPoint } from "./transform";
|
|
7
|
+
import { fitTrend, type TrendFit } from "./trend";
|
|
8
|
+
|
|
9
|
+
export type StatSeriesLoader = (def: StatDef) => Promise<DatedObservation[]>;
|
|
10
|
+
|
|
11
|
+
export interface StatBuild {
|
|
12
|
+
stat: StatDef;
|
|
13
|
+
points: StatPoint[];
|
|
14
|
+
trend: TrendFit;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface StatsBundle {
|
|
18
|
+
builds: StatBuild[];
|
|
19
|
+
errors: string[];
|
|
20
|
+
fetchedAt: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type StatsCloudClient = Pick<typeof apiClient, "getCloudFredSeries">;
|
|
24
|
+
|
|
25
|
+
export function createStatSeriesLoader(client: StatsCloudClient): StatSeriesLoader {
|
|
26
|
+
const cloudLoader: StatSeriesLoader = async (def) => {
|
|
27
|
+
const data = await client.getCloudFredSeries(def.seriesId, {
|
|
28
|
+
limit: def.limit,
|
|
29
|
+
sortOrder: "desc",
|
|
30
|
+
});
|
|
31
|
+
return data.observations;
|
|
32
|
+
};
|
|
33
|
+
return (def) => statsCache.load(def.seriesId, () => cloudLoader(def));
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export const defaultStatLoader = createStatSeriesLoader(apiClient);
|
|
37
|
+
|
|
38
|
+
function build(def: StatDef, observations: DatedObservation[]): StatBuild | null {
|
|
39
|
+
const scale = def.scale ?? 1;
|
|
40
|
+
const scaled = scale === 1
|
|
41
|
+
? observations
|
|
42
|
+
: observations.map((entry) => ({
|
|
43
|
+
date: entry.date,
|
|
44
|
+
value: entry.value == null ? null : entry.value * scale,
|
|
45
|
+
}));
|
|
46
|
+
const points = applyTransform(scaled, def.transform);
|
|
47
|
+
if (points.length === 0) return null;
|
|
48
|
+
return { stat: def, points, trend: fitTrend(points) };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/** Builds whatever the on-disk cache can already answer, for an instant first paint. */
|
|
52
|
+
export function getCachedStatsBundle(stats: readonly StatDef[] = STATS): StatsBundle | null {
|
|
53
|
+
const builds: StatBuild[] = [];
|
|
54
|
+
for (const def of stats) {
|
|
55
|
+
const cached = statsCache.get(def.seriesId, { allowExpired: true });
|
|
56
|
+
if (!cached) continue;
|
|
57
|
+
const entry = build(def, cached.observations);
|
|
58
|
+
if (entry) builds.push(entry);
|
|
59
|
+
}
|
|
60
|
+
return builds.length > 0 ? { builds, errors: [], fetchedAt: Date.now() } : null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function loadStatsBundle(options?: {
|
|
64
|
+
loader?: StatSeriesLoader;
|
|
65
|
+
stats?: readonly StatDef[];
|
|
66
|
+
}): Promise<StatsBundle> {
|
|
67
|
+
const loader = options?.loader ?? defaultStatLoader;
|
|
68
|
+
const stats = options?.stats ?? STATS;
|
|
69
|
+
const errors: string[] = [];
|
|
70
|
+
|
|
71
|
+
const settled = await Promise.all(stats.map(async (def) => {
|
|
72
|
+
try {
|
|
73
|
+
return { def, observations: await loader(def) };
|
|
74
|
+
} catch (error) {
|
|
75
|
+
errors.push(`${def.seriesId}: ${error instanceof Error ? error.message : String(error)}`);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}));
|
|
79
|
+
|
|
80
|
+
const builds: StatBuild[] = [];
|
|
81
|
+
for (const entry of settled) {
|
|
82
|
+
if (!entry) continue;
|
|
83
|
+
const made = build(entry.def, entry.observations);
|
|
84
|
+
if (made) builds.push(made);
|
|
85
|
+
else errors.push(`${entry.def.seriesId}: no observations after transform`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
if (builds.length === 0) {
|
|
89
|
+
throw new Error(errors[0] ?? "Economic statistics unavailable");
|
|
90
|
+
}
|
|
91
|
+
return { builds, errors, fetchedAt: Date.now() };
|
|
92
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { blendHex, colors } from "../../../theme/colors";
|
|
2
|
+
import type { StatTransform } from "./transform";
|
|
3
|
+
|
|
4
|
+
export type StatCategoryId =
|
|
5
|
+
| "inflation"
|
|
6
|
+
| "labor"
|
|
7
|
+
| "growth"
|
|
8
|
+
| "consumer"
|
|
9
|
+
| "housing"
|
|
10
|
+
| "rates"
|
|
11
|
+
| "trade";
|
|
12
|
+
|
|
13
|
+
export const STAT_CATEGORIES: ReadonlyArray<{ id: StatCategoryId; label: string }> = [
|
|
14
|
+
{ id: "inflation", label: "Inflation" },
|
|
15
|
+
{ id: "labor", label: "Labor" },
|
|
16
|
+
{ id: "growth", label: "Growth" },
|
|
17
|
+
{ id: "consumer", label: "Consumer" },
|
|
18
|
+
{ id: "housing", label: "Housing" },
|
|
19
|
+
{ id: "rates", label: "Rates & Money" },
|
|
20
|
+
{ id: "trade", label: "Trade & Costs" },
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Whether a rising number is welcome. Unemployment climbing is bad, payrolls
|
|
25
|
+
* climbing is good, and an index level like CPI is neither on its own, so the
|
|
26
|
+
* table colours the change accordingly instead of pretending every rise is green.
|
|
27
|
+
*/
|
|
28
|
+
export type StatDirection = "higher-is-good" | "higher-is-bad" | "neutral";
|
|
29
|
+
|
|
30
|
+
export interface StatDef {
|
|
31
|
+
id: string;
|
|
32
|
+
label: string;
|
|
33
|
+
/** Fits the table's INDICATOR column. */
|
|
34
|
+
shortLabel: string;
|
|
35
|
+
category: StatCategoryId;
|
|
36
|
+
seriesId: string;
|
|
37
|
+
transform: StatTransform;
|
|
38
|
+
/**
|
|
39
|
+
* Multiplier applied to raw observations before the transform, to bring a series
|
|
40
|
+
* into the unit it is quoted in. Percent changes are ratios, so scaling cannot
|
|
41
|
+
* affect them; it only matters for levels.
|
|
42
|
+
*/
|
|
43
|
+
scale?: number;
|
|
44
|
+
direction: StatDirection;
|
|
45
|
+
/** Drives the chart axis and the cursor readout. */
|
|
46
|
+
axisUnit: "%" | "";
|
|
47
|
+
formatValue: (value: number) => string;
|
|
48
|
+
/** A level that means something: the Fed's target, zero for a spread. */
|
|
49
|
+
reference: { value: number; label: string } | null;
|
|
50
|
+
/** How old the newest print may get before the pane flags it stale. */
|
|
51
|
+
staleAfterMs: number;
|
|
52
|
+
note: string;
|
|
53
|
+
limit: number;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
export function categoryLabel(id: StatCategoryId): string {
|
|
57
|
+
return STAT_CATEGORIES.find((entry) => entry.id === id)?.label ?? id;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** Green when the move is welcome, red when it is not, plain when it is neither. */
|
|
61
|
+
export function changeColor(direction: StatDirection, change: number | null): string {
|
|
62
|
+
if (change == null || !Number.isFinite(change) || change === 0) return colors.textMuted;
|
|
63
|
+
if (direction === "neutral") return colors.text;
|
|
64
|
+
const good = direction === "higher-is-good" ? change > 0 : change < 0;
|
|
65
|
+
return good ? colors.positive : blendHex(colors.negative, "#000000", 0.1);
|
|
66
|
+
}
|