gloomberb 0.18.4 → 0.19.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 +1 -1
- package/README.zh-CN.md +1 -1
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +169 -5
- package/src/account/auth-model.ts +7 -3
- package/src/api-client/auth.ts +9 -4
- package/src/api-client/catalysts.ts +2 -1
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +31 -6
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/index.ts +171 -11
- package/src/api-client/live-scan.ts +91 -0
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +13 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/request.ts +222 -41
- package/src/api-client/research-activity.ts +6 -12
- package/src/api-client/types.ts +74 -0
- package/src/app/keybindings/index.ts +3 -0
- package/src/app/keybindings/labels.ts +18 -1
- package/src/app/keybindings/resolve.ts +14 -0
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +42 -27
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +1 -1
- package/src/architecture/pane-tabs-allowlist.json +3 -0
- package/src/architecture/ttl-caches-allowlist.json +19 -0
- package/src/brokers/sync-broker-instance.ts +2 -8
- package/src/capabilities/chart-series.ts +1 -1
- package/src/cli/commands/crypto-hints.ts +40 -24
- package/src/cli/commands/fred-series.ts +5 -0
- package/src/cli/commands/keybindings.ts +23 -0
- package/src/cli/commands/market.ts +138 -46
- package/src/cli/commands/options-chain.ts +107 -10
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +176 -39
- package/src/cli/commands/search.ts +39 -3
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +57 -35
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +10 -2
- package/src/cli/export-source.ts +29 -1
- package/src/cli/glossary.ts +73 -1
- package/src/cli/history-rows.ts +87 -16
- package/src/cli/index.ts +2 -0
- package/src/cli/known-gaps.ts +5 -2
- package/src/cli/listing-arg.ts +195 -56
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +3 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +111 -16
- package/src/cli/pane-functions/function-help.ts +23 -13
- package/src/cli/pane-functions/headless.ts +56 -12
- package/src/cli/pane-functions/index.ts +58 -16
- package/src/cli/pane-functions/market-session.ts +81 -9
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/report.ts +5 -1
- package/src/cli/pane-functions/resolver.ts +35 -8
- package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
- package/src/cli/pane-functions/screenshot-types.ts +5 -0
- package/src/cli/quote-answer.ts +30 -13
- package/src/cli/report-tables.ts +18 -7
- package/src/cli/restore-plugins.ts +13 -1
- package/src/cli/result.ts +57 -5
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +26 -4
- package/src/components/chart/composite/format.ts +27 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/levels.ts +2 -0
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-series.ts +1 -0
- package/src/components/chart/composite/panel-surface.tsx +61 -5
- package/src/components/chart/composite/range-statistics.ts +62 -0
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +46 -7
- package/src/components/chart/composite/series-axis-tags.tsx +16 -9
- package/src/components/chart/composite/series-cues.ts +14 -0
- package/src/components/chart/composite/text-renderer.ts +9 -2
- package/src/components/chart/composite/tool-catalog.ts +36 -0
- package/src/components/chart/composite/tools.ts +203 -44
- package/src/components/chart/composite/types.ts +4 -0
- package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
- package/src/components/command-bar/assist/inventory.ts +2 -1
- package/src/components/command-bar/commands/direct/index.ts +28 -13
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/panel/prompt-binding.ts +0 -8
- package/src/components/command-bar/workflow/alert.ts +1 -1
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/header.tsx +26 -40
- package/src/components/layout/pane/footer/index.tsx +1 -0
- package/src/components/layout/pane/footer/model.ts +27 -0
- package/src/components/onboarding/onboarding-frame.tsx +1 -1
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -9
- package/src/components/onboarding/wizard-broker-sync.ts +0 -1
- package/src/components/onboarding/wizard-positions.ts +3 -3
- package/src/components/ticker/list-table-view.tsx +4 -1
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/display.tsx +5 -1
- package/src/components/ui/list-view.tsx +8 -4
- package/src/components/ui/query-bar-keys.tsx +2 -0
- package/src/components/ui/query-bar.tsx +1 -0
- package/src/components/ui/select-button.tsx +5 -1
- package/src/components/ui/select-field.tsx +1 -0
- package/src/components/ui/table-layout.ts +8 -5
- package/src/core/app-runtime.ts +2 -0
- package/src/core/app-services.ts +2 -2
- package/src/core/state/app/layout.ts +2 -0
- package/src/core/state/app/state.ts +9 -0
- package/src/core/state/app/types.ts +3 -0
- package/src/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/saved-copies.ts +55 -16
- package/src/data/ticker-repository.ts +16 -9
- package/src/data/ticker-writes.ts +81 -0
- package/src/data/ttl-cache.ts +165 -0
- package/src/docs/snapshot.json +427 -216
- package/src/i18n/es.ts +19 -3
- package/src/i18n/ja.ts +19 -3
- package/src/i18n/ko.ts +19 -3
- package/src/i18n/zh-cn.ts +19 -3
- package/src/i18n/zh-tw.ts +19 -3
- package/src/market-data/coordinator/financials.ts +10 -2
- package/src/market-data/coordinator/index.ts +3 -1
- package/src/market-data/coordinator/quotes.ts +4 -2
- package/src/market-data/depositary-pair.ts +2 -2
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +48 -7
- package/src/market-data/market/dated-previous-close.ts +203 -0
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/market/futures-week.ts +68 -0
- package/src/market-data/market/status.ts +16 -4
- package/src/market-data/options-alternatives.ts +141 -4
- package/src/market-data/quotes/contributions.ts +20 -2
- package/src/market-data/quotes/resolution.ts +3 -0
- package/src/market-data/selectors.ts +3 -0
- package/src/news/aggregator.ts +113 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +11 -0
- package/src/notifications/notification-log.ts +6 -4
- package/src/plugins/builtin/alerts/alert-engine.ts +25 -2
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +57 -6
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +380 -82
- package/src/plugins/builtin/alerts/command.ts +31 -0
- package/src/plugins/builtin/alerts/constants.ts +1 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
- package/src/plugins/builtin/alerts/fired-log.ts +113 -8
- package/src/plugins/builtin/alerts/format.ts +80 -4
- package/src/plugins/builtin/alerts/headless.ts +30 -7
- package/src/plugins/builtin/alerts/history-rows.ts +7 -3
- package/src/plugins/builtin/alerts/history.ts +2 -0
- package/src/plugins/builtin/alerts/index.tsx +135 -22
- package/src/plugins/builtin/alerts/live.ts +3 -3
- package/src/plugins/builtin/alerts/metric-command.ts +134 -0
- package/src/plugins/builtin/alerts/metric-data.ts +111 -0
- package/src/plugins/builtin/alerts/metric-model.ts +324 -0
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +725 -0
- package/src/plugins/builtin/alerts/pane.tsx +90 -39
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +37 -10
- package/src/plugins/builtin/alerts/types.ts +7 -0
- package/src/plugins/builtin/analytics/index.tsx +2 -1
- package/src/plugins/builtin/analytics/risk-client.ts +16 -5
- package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
- package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
- package/src/plugins/builtin/analytics/risk-model.ts +55 -9
- package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
- package/src/plugins/builtin/attention/index.tsx +2 -1
- package/src/plugins/builtin/attention/listing.ts +14 -0
- package/src/plugins/builtin/backtest/headless.ts +10 -5
- package/src/plugins/builtin/backtest/pane.tsx +9 -4
- package/src/plugins/builtin/backtest/presets.ts +29 -3
- package/src/plugins/builtin/broker-manager/index.tsx +10 -5
- package/src/plugins/builtin/broker-manager/model.ts +6 -1
- package/src/plugins/builtin/broker-trading/controller.ts +22 -12
- package/src/plugins/builtin/broker-trading/index.tsx +13 -1
- package/src/plugins/builtin/broker-trading/order-feed.ts +9 -4
- package/src/plugins/builtin/broker-trading/pane.tsx +1 -1
- package/src/plugins/builtin/buildout/cells.tsx +12 -4
- package/src/plugins/builtin/buildout/format.ts +38 -0
- package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
- package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
- package/src/plugins/builtin/buildout/table-model.ts +23 -2
- package/src/plugins/builtin/builtin-plugin-meta.ts +7 -3
- package/src/plugins/builtin/catalysts/model.ts +2 -1
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +6 -1
- package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +1 -1
- package/src/plugins/builtin/chart-composer/cli-options.ts +9 -5
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +12 -2
- package/src/plugins/builtin/chart-composer/editor/controller.ts +4 -2
- package/src/plugins/builtin/chart-composer/editor/model.ts +2 -0
- package/src/plugins/builtin/chart-composer/editor.tsx +7 -5
- package/src/plugins/builtin/chart-composer/formula.ts +71 -18
- package/src/plugins/builtin/chart-composer/headless-schema.ts +21 -4
- package/src/plugins/builtin/chart-composer/headless.ts +19 -3
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- package/src/plugins/builtin/chart-composer/pane.tsx +53 -9
- package/src/plugins/builtin/chart-composer/presets.ts +50 -15
- package/src/plugins/builtin/chart-composer/quick-add.tsx +40 -25
- package/src/plugins/builtin/chart-composer/session-history.ts +18 -9
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chart-composer/use-series-catalog.ts +40 -26
- package/src/plugins/builtin/chat/content/command-suggestions.ts +93 -0
- package/src/plugins/builtin/chat/content/composer.tsx +13 -6
- package/src/plugins/builtin/chat/content/footer.tsx +6 -3
- package/src/plugins/builtin/chat/content/index.tsx +50 -21
- package/src/plugins/builtin/chat/content/open-targets.ts +14 -3
- package/src/plugins/builtin/chat/controller/session-runtime.ts +4 -2
- package/src/plugins/builtin/chat/index.tsx +23 -0
- package/src/plugins/builtin/chat/message/desktop.tsx +1 -0
- package/src/plugins/builtin/chat/message/inline-tokens.tsx +23 -0
- package/src/plugins/builtin/chat/message/pane-chip.tsx +50 -20
- package/src/plugins/builtin/chat/pane-links/commands.ts +271 -0
- package/src/plugins/builtin/chat/pane-links/model.ts +12 -3
- package/src/plugins/builtin/chat/pane-links/open.ts +8 -1
- package/src/plugins/builtin/chat/unread-inbox-pane.tsx +10 -1
- package/src/plugins/builtin/clinical-trials/model.ts +0 -9
- package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/cloud/plugin.tsx +1 -1
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +9 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +7 -7
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
- package/src/plugins/builtin/econ/calendar-model.ts +14 -9
- package/src/plugins/builtin/econ/figures.ts +72 -0
- package/src/plugins/builtin/econ/index.tsx +33 -3
- package/src/plugins/builtin/econ/types.ts +4 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +85 -35
- package/src/plugins/builtin/econ-statistics/stats.ts +37 -9
- package/src/plugins/builtin/econ-statistics/view.ts +3 -0
- package/src/plugins/builtin/equity-screener/categories.ts +179 -0
- package/src/plugins/builtin/equity-screener/client.ts +4 -2
- package/src/plugins/builtin/equity-screener/headless.ts +144 -13
- package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
- package/src/plugins/builtin/equity-screener/model.ts +99 -10
- package/src/plugins/builtin/equity-screener/pane.tsx +70 -15
- package/src/plugins/builtin/exposure/charts.tsx +2 -2
- package/src/plugins/builtin/exposure/model.ts +13 -1
- package/src/plugins/builtin/exposure/pane.tsx +4 -4
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- package/src/plugins/builtin/futures/index.tsx +1 -0
- package/src/plugins/builtin/futures/model.ts +2 -1
- package/src/plugins/builtin/futures/table.tsx +2 -2
- package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
- package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
- package/src/plugins/builtin/fx-matrix/client.ts +11 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +6 -3
- package/src/plugins/builtin/fx-matrix/index.tsx +16 -4
- package/src/plugins/builtin/fx-matrix/live-legs.ts +54 -3
- package/src/plugins/builtin/gpu/headless.ts +8 -2
- package/src/plugins/builtin/help/function-index.ts +4 -1
- package/src/plugins/builtin/help/index.tsx +1 -0
- package/src/plugins/builtin/help/keybindings-editor.tsx +18 -14
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +18 -1
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +36 -18
- package/src/plugins/builtin/iv-history/index.tsx +2 -2
- package/src/plugins/builtin/iv-history/model.ts +163 -27
- package/src/plugins/builtin/iv-history/pane.tsx +10 -6
- package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
- package/src/plugins/builtin/iv-history/universe.ts +5 -1
- package/src/plugins/builtin/jobs/client.ts +25 -29
- package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
- package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
- package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
- package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
- package/src/plugins/builtin/macro-day/pane.tsx +7 -5
- package/src/plugins/builtin/macro-day/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +25 -7
- package/src/plugins/builtin/market-movers/footer.ts +2 -2
- package/src/plugins/builtin/market-movers/headless.ts +32 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +14 -0
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +82 -0
- package/src/plugins/builtin/members/client.ts +19 -4
- package/src/plugins/builtin/members/headless.ts +20 -7
- package/src/plugins/builtin/members/index.tsx +9 -9
- package/src/plugins/builtin/members/model.ts +23 -4
- package/src/plugins/builtin/members/pane.tsx +25 -15
- package/src/plugins/builtin/news/index.tsx +1 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +54 -6
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/model-spot.ts +5 -2
- package/src/plugins/builtin/options/strike-window.ts +2 -1
- package/src/plugins/builtin/options/table.ts +6 -9
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +42 -12
- package/src/plugins/builtin/options-positioning/client.ts +30 -0
- package/src/plugins/builtin/options-positioning/headless.ts +74 -21
- package/src/plugins/builtin/options-positioning/index.tsx +4 -4
- package/src/plugins/builtin/options-positioning/model.ts +122 -14
- package/src/plugins/builtin/options-positioning/pane.tsx +87 -42
- package/src/plugins/builtin/options-scenario/client.ts +116 -47
- package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
- package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
- package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
- package/src/plugins/builtin/options-scenario/csp.ts +8 -2
- package/src/plugins/builtin/options-scenario/index.tsx +9 -7
- package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
- package/src/plugins/builtin/pe-band/model.ts +12 -49
- package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
- package/src/plugins/builtin/perps/cascade.ts +236 -0
- package/src/plugins/builtin/perps/client.ts +56 -2
- package/src/plugins/builtin/perps/freshness.ts +12 -10
- package/src/plugins/builtin/perps/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/live-rows.ts +10 -3
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +32 -31
- package/src/plugins/builtin/perps/status-line.ts +25 -0
- package/src/plugins/builtin/plugin-module.ts +5 -1
- package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
- package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
- package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
- package/src/plugins/builtin/portfolio-list/cli/render.ts +12 -7
- package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
- package/src/plugins/builtin/portfolio-list/summary/index.tsx +11 -8
- package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/rate-path/pane.tsx +2 -2
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +10 -8
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +11 -9
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/scanner/feed.ts +2 -2
- package/src/plugins/builtin/scanner/flow-model.ts +50 -4
- package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
- package/src/plugins/builtin/scanner/flow-session.ts +146 -0
- package/src/plugins/builtin/scanner/headless.ts +204 -35
- package/src/plugins/builtin/scanner/index.ts +40 -6
- package/src/plugins/builtin/scanner/live-condition-editor.tsx +222 -0
- package/src/plugins/builtin/scanner/live-feed.ts +168 -0
- package/src/plugins/builtin/scanner/live-headless.ts +138 -0
- package/src/plugins/builtin/scanner/live-model.ts +362 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +677 -0
- package/src/plugins/builtin/scanner/live-track.ts +186 -0
- package/src/plugins/builtin/scanner/lock-note.ts +3 -2
- package/src/plugins/builtin/seasonality/headless.ts +10 -1
- package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
- package/src/plugins/builtin/sec/fund-facts.ts +116 -34
- package/src/plugins/builtin/sec/headless.ts +2 -1
- package/src/plugins/builtin/series-inventory.ts +3 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/macro-releases.ts +65 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +3 -2
- package/src/plugins/builtin/shared/scanner-capture.ts +3 -0
- package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
- package/src/plugins/builtin/shared/ticker-company.ts +77 -0
- package/src/plugins/builtin/shared/use-quote-board.ts +10 -3
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +26 -1
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/headless.ts +6 -4
- package/src/plugins/builtin/themes/model.ts +17 -8
- package/src/plugins/builtin/themes/pane.tsx +7 -7
- package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/quote-footer.ts +6 -1
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/charts.tsx +9 -4
- package/src/plugins/builtin/vol-surface/client.ts +9 -17
- package/src/plugins/builtin/vol-surface/headless.ts +63 -14
- package/src/plugins/builtin/vol-surface/index.tsx +2 -2
- package/src/plugins/builtin/vol-surface/model.ts +22 -7
- package/src/plugins/builtin/vol-surface/pane.tsx +48 -9
- package/src/plugins/builtin/vol-surface/tables.ts +31 -3
- package/src/plugins/builtin/volatility/dvol.ts +46 -0
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +4 -4
- package/src/plugins/builtin/volatility/series.ts +26 -0
- package/src/plugins/builtin/world-indices/footer.ts +1 -1
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/catalog-browser.ts +13 -8
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +5 -5
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
- package/src/remote/resources.ts +2 -1
- package/src/renderers/browser/app-services.ts +1 -7
- package/src/renderers/browser/config-host.ts +2 -55
- package/src/renderers/browser/main.tsx +7 -57
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/list-view.tsx +13 -10
- package/src/renderers/dom/desktop/query-bar.tsx +4 -2
- package/src/renderers/dom/desktop/select-field.tsx +4 -2
- package/src/renderers/dom/host/chart-surface.tsx +21 -0
- package/src/renderers/electrobun/bun/desktop/app-name.ts +222 -0
- package/src/renderers/electrobun/bun/desktop/backend.ts +29 -7
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +74 -25
- package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +1 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +173 -5
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/workspace/index.ts +2 -0
- package/src/renderers/electrobun/bun/index.ts +25 -1
- package/src/renderers/electrobun/shared/app-name.ts +30 -0
- package/src/renderers/electrobun/shared/protocol.ts +10 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
- package/src/renderers/electrobun/view/desktop/window/snapshot.ts +1 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +3 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/renderers/opentui/toast-host.tsx +71 -43
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/index.ts +1 -0
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
- package/src/sources/provider-router/batches.ts +10 -2
- package/src/sources/provider-router/financial-routes.ts +3 -3
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/history.ts +28 -7
- package/src/sources/provider-router/index.ts +149 -10
- package/src/sources/provider-router/search.ts +13 -2
- package/src/state/app/context/index.tsx +3 -0
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/auth-transition.ts +19 -9
- package/src/sync/baseline.ts +10 -4
- package/src/sync/controller.ts +70 -19
- package/src/sync/core-contributors.ts +90 -31
- package/src/sync/react.ts +4 -1
- package/src/sync/types.ts +3 -0
- package/src/telemetry/stall-reports-dom.ts +38 -0
- package/src/telemetry/stall-reports-node.ts +75 -0
- package/src/telemetry/stall-reports.ts +214 -0
- package/src/telemetry/usage-counts.ts +0 -18
- package/src/test-support/preload.ts +14 -0
- package/src/theme/colors.ts +8 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name.ts +103 -3
- package/src/tickers/listing-venues.ts +38 -3
- package/src/tickers/search/bare-symbol.ts +36 -0
- package/src/tickers/search/index.ts +36 -7
- package/src/tickers/search/ranking.ts +10 -1
- package/src/tickers/search/types.ts +5 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/base-series.ts +13 -5
- package/src/time-series/daily-valuation.ts +62 -0
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +2 -4
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/forward-valuation.ts +32 -0
- package/src/time-series/fundamentals.ts +198 -7
- package/src/time-series/hooks.ts +1 -0
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/price-units.ts +72 -2
- package/src/time-series/resolve-context.ts +7 -1
- package/src/time-series/resolve.ts +116 -15
- package/src/time-series/series-presentation.ts +40 -6
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +5 -3
- package/src/time-series/studies.ts +278 -74
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +3 -3
- package/src/time-series/types.ts +27 -1
- package/src/time-series/valuation-releases.ts +39 -0
- package/src/types/data-provider.ts +9 -1
- package/src/types/desktop-window.ts +2 -0
- package/src/types/financials.ts +73 -2
- package/src/types/headless.ts +61 -5
- package/src/types/instrument.ts +3 -0
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +10 -1
- package/src/ui/host.tsx +3 -0
- package/src/updater/command-names.ts +12 -7
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +44 -0
- package/src/utils/financial-statements.ts +6 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +24 -3
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/network-failure.ts +9 -6
- package/src/utils/option-expiry.ts +48 -1
- package/src/utils/options.ts +10 -0
- package/src/utils/plugin-proxy-hosts.json +1 -0
- package/src/utils/price-history.ts +12 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
- package/src/renderers/browser/first-screen.ts +0 -193
|
@@ -23,6 +23,7 @@ import type { SeriesCatalogInstrument, SeriesCatalogSuggestion } from "./series-
|
|
|
23
23
|
import { useSeriesCatalogSuggestions } from "./use-series-catalog";
|
|
24
24
|
import { clampIndex } from "../../../utils/math";
|
|
25
25
|
|
|
26
|
+
const RETRY_SEARCH_ID = "\u0000retry-search";
|
|
26
27
|
const MAX_VISIBLE_SUGGESTIONS = 4;
|
|
27
28
|
const CHART_TOOLBAR_HEIGHT = 1;
|
|
28
29
|
const MINIMUM_CHART_HEIGHT = 4;
|
|
@@ -116,7 +117,7 @@ export function ChartSeriesQuickAdd({
|
|
|
116
117
|
const [selectedIndex, setSelectedIndex] = useState(0);
|
|
117
118
|
const [error, setError] = useState<string | null>(null);
|
|
118
119
|
const defaultInstrument = useMemo(() => defaultCatalogInstrument(spec), [spec]);
|
|
119
|
-
const { suggestions, loading, error: searchError } = useSeriesCatalogSuggestions({
|
|
120
|
+
const { suggestions, loading, error: searchError, retry } = useSeriesCatalogSuggestions({
|
|
120
121
|
query,
|
|
121
122
|
defaultInstrument,
|
|
122
123
|
enabled: active,
|
|
@@ -127,10 +128,7 @@ export function ChartSeriesQuickAdd({
|
|
|
127
128
|
const drawerStatus = error
|
|
128
129
|
?? (loading && suggestions.length === 0
|
|
129
130
|
? "Searching instruments..."
|
|
130
|
-
|
|
131
|
-
: searchError && suggestions.length === 0
|
|
132
|
-
? searchError
|
|
133
|
-
: active && query.trim().length > 0 && suggestions.length === 0
|
|
131
|
+
: active && !searchError && query.trim().length > 0 && suggestions.length === 0
|
|
134
132
|
? "No matching security or metric."
|
|
135
133
|
: null);
|
|
136
134
|
const maximumDrawerHeight = Math.max(
|
|
@@ -141,7 +139,7 @@ export function ChartSeriesQuickAdd({
|
|
|
141
139
|
),
|
|
142
140
|
);
|
|
143
141
|
const drawerHeight = active
|
|
144
|
-
? Math.min(maximumDrawerHeight, drawerStatus ? 1 : suggestions.length)
|
|
142
|
+
? Math.min(maximumDrawerHeight, drawerStatus ? 1 : suggestions.length + (searchError ? 1 : 0))
|
|
145
143
|
: 0;
|
|
146
144
|
const nativeSurfaceManager = nativeRenderer.nativeSurfaceManager;
|
|
147
145
|
useAppInputCapture(inputFocused && focused);
|
|
@@ -203,9 +201,14 @@ export function ChartSeriesQuickAdd({
|
|
|
203
201
|
|
|
204
202
|
// Plugin catalogs land after the securities: only pull a stranded cursor
|
|
205
203
|
// back into range, so a row the user already moved to stays selected.
|
|
204
|
+
// -1 is the retry row when a lookup failed. Suggestions retain their indices
|
|
205
|
+
// when that row arrives, so an in-flight failure cannot change what Enter adds.
|
|
206
|
+
const clampSelection = useCallback((index: number) => suggestions.length
|
|
207
|
+
? Math.min(suggestions.length - 1, Math.max(searchError ? -1 : 0, index))
|
|
208
|
+
: -1, [searchError, suggestions.length]);
|
|
206
209
|
useEffect(() => {
|
|
207
|
-
setSelectedIndex(
|
|
208
|
-
}, [
|
|
210
|
+
setSelectedIndex(clampSelection);
|
|
211
|
+
}, [clampSelection]);
|
|
209
212
|
|
|
210
213
|
useEffect(() => {
|
|
211
214
|
if (focused || !active) return;
|
|
@@ -287,9 +290,16 @@ export function ChartSeriesQuickAdd({
|
|
|
287
290
|
queueMicrotask(() => inputRef.current?.blur?.());
|
|
288
291
|
}, [cancelPendingBlur, clearInput, onActiveChange, setSpec, spec]);
|
|
289
292
|
|
|
293
|
+
const retrySearch = useCallback(() => {
|
|
294
|
+
cancelPendingBlur();
|
|
295
|
+
setInputFocused(true);
|
|
296
|
+
queueMicrotask(() => inputRef.current?.focus?.());
|
|
297
|
+
void retry();
|
|
298
|
+
}, [cancelPendingBlur, retry]);
|
|
290
299
|
const submit = useCallback(() => {
|
|
291
|
-
|
|
292
|
-
|
|
300
|
+
if (selectedIndex < 0 && searchError) retrySearch();
|
|
301
|
+
else commitSuggestion(suggestions[clampIndex(selectedIndex, suggestions.length, -1)]);
|
|
302
|
+
}, [commitSuggestion, retrySearch, searchError, selectedIndex, suggestions]);
|
|
293
303
|
const cancel = useCallback(() => {
|
|
294
304
|
cancelPendingBlur();
|
|
295
305
|
inputRef.current?.blur?.();
|
|
@@ -317,11 +327,11 @@ export function ChartSeriesQuickAdd({
|
|
|
317
327
|
} else if (event.name === "up") {
|
|
318
328
|
event.preventDefault?.();
|
|
319
329
|
event.stopPropagation?.();
|
|
320
|
-
setSelectedIndex((current) =>
|
|
330
|
+
setSelectedIndex((current) => clampSelection(current - 1));
|
|
321
331
|
} else if (event.name === "down") {
|
|
322
332
|
event.preventDefault?.();
|
|
323
333
|
event.stopPropagation?.();
|
|
324
|
-
setSelectedIndex((current) =>
|
|
334
|
+
setSelectedIndex((current) => clampSelection(current + 1));
|
|
325
335
|
}
|
|
326
336
|
}, {
|
|
327
337
|
phase: "before",
|
|
@@ -377,12 +387,17 @@ export function ChartSeriesQuickAdd({
|
|
|
377
387
|
/>
|
|
378
388
|
);
|
|
379
389
|
|
|
380
|
-
const items = useMemo<ListViewItem[]>(() =>
|
|
381
|
-
id:
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
390
|
+
const items = useMemo<ListViewItem[]>(() => [
|
|
391
|
+
...(searchError ? [{ id: RETRY_SEARCH_ID, label: searchError, detail: "Retry", color: colors.negative }] : []),
|
|
392
|
+
...suggestions.map((suggestion) => ({
|
|
393
|
+
id: suggestion.id, label: suggestion.label, description: suggestion.description, detail: suggestion.detail,
|
|
394
|
+
})),
|
|
395
|
+
], [searchError, suggestions]);
|
|
396
|
+
const selectedItemIndex = selectedIndex + (searchError ? 1 : 0);
|
|
397
|
+
const activateItem = (id: string) => {
|
|
398
|
+
if (id === RETRY_SEARCH_ID) retrySearch();
|
|
399
|
+
else commitSuggestion(suggestions.find((suggestion) => suggestion.id === id));
|
|
400
|
+
};
|
|
386
401
|
|
|
387
402
|
return (
|
|
388
403
|
<Box
|
|
@@ -408,17 +423,17 @@ export function ChartSeriesQuickAdd({
|
|
|
408
423
|
label="Chart series suggestions"
|
|
409
424
|
minWidth={controlWidth * 8}
|
|
410
425
|
closeOnSelect={false}
|
|
411
|
-
highlightedId={
|
|
412
|
-
onHighlight={(id) => setSelectedIndex(Math.max(0, suggestions.findIndex((suggestion) => suggestion.id === id)))}
|
|
426
|
+
highlightedId={items[selectedItemIndex]?.id ?? null}
|
|
427
|
+
onHighlight={(id) => setSelectedIndex(id === RETRY_SEARCH_ID ? -1 : Math.max(0, suggestions.findIndex((suggestion) => suggestion.id === id)))}
|
|
413
428
|
items={drawerStatus
|
|
414
429
|
? [{ id: "\u0000status", kind: "heading", label: drawerStatus }]
|
|
415
|
-
:
|
|
430
|
+
: items.map((suggestion) => ({
|
|
416
431
|
id: suggestion.id,
|
|
417
432
|
label: suggestion.label,
|
|
418
433
|
description: suggestion.description,
|
|
419
434
|
hint: suggestion.detail,
|
|
420
435
|
}))}
|
|
421
|
-
onSelect={
|
|
436
|
+
onSelect={activateItem}
|
|
422
437
|
/>
|
|
423
438
|
) : quickAddRow}
|
|
424
439
|
{!desktop && drawerHeight > 0 ? (
|
|
@@ -454,15 +469,15 @@ export function ChartSeriesQuickAdd({
|
|
|
454
469
|
) : (
|
|
455
470
|
<ListView
|
|
456
471
|
items={items}
|
|
457
|
-
selectedIndex={clampIndex(
|
|
472
|
+
selectedIndex={clampIndex(selectedItemIndex, items.length, -1)}
|
|
458
473
|
height={drawerHeight}
|
|
459
474
|
surface="plain"
|
|
460
475
|
bgColor={colors.panel}
|
|
461
476
|
scrollable={items.length > drawerHeight}
|
|
462
477
|
rowGap={0}
|
|
463
478
|
selectOnHover
|
|
464
|
-
onSelect={setSelectedIndex}
|
|
465
|
-
onActivate={(
|
|
479
|
+
onSelect={(index) => setSelectedIndex(index - (searchError ? 1 : 0))}
|
|
480
|
+
onActivate={(item) => activateItem(item.id)}
|
|
466
481
|
remoteLabel="Chart series suggestions"
|
|
467
482
|
/>
|
|
468
483
|
)}
|
|
@@ -207,22 +207,27 @@ export function resolveIntradayRequest(options: {
|
|
|
207
207
|
return { rangePreset, resolution, session };
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
function unavailableReason(symbol: string, request: IntradayRequest, kind: "empty" | "not-intraday"): string {
|
|
210
|
+
function unavailableReason(symbol: string, request: IntradayRequest, kind: "empty" | "not-intraday" | "failed"): string {
|
|
211
211
|
const window = request.session
|
|
212
212
|
? `session ${request.session}`
|
|
213
213
|
: request.rangePreset === "1W" ? "the latest five sessions" : "the latest session";
|
|
214
|
+
if (kind === "failed") return `Intraday history request failed for ${symbol}. Try again.`;
|
|
214
215
|
return kind === "not-intraday"
|
|
215
216
|
? `The market-data provider did not return intraday bars for ${symbol} for ${window}.`
|
|
216
217
|
: `No intraday price history is available for ${symbol} for ${window}.`;
|
|
217
218
|
}
|
|
218
219
|
|
|
220
|
+
interface IntradayHistoryAttempt extends PriceHistoryResult {
|
|
221
|
+
requestFailed?: true;
|
|
222
|
+
}
|
|
223
|
+
|
|
219
224
|
async function loadTrailingHistory(
|
|
220
225
|
provider: DataProvider,
|
|
221
226
|
symbol: string,
|
|
222
227
|
exchange: string,
|
|
223
228
|
request: IntradayRequest,
|
|
224
229
|
context?: MarketDataRequestContext,
|
|
225
|
-
): Promise<
|
|
230
|
+
): Promise<IntradayHistoryAttempt> {
|
|
226
231
|
const fetchRange: TimeRange = request.rangePreset === "1W" && request.resolution !== "1m"
|
|
227
232
|
? "1M"
|
|
228
233
|
: "1W";
|
|
@@ -230,8 +235,9 @@ async function loadTrailingHistory(
|
|
|
230
235
|
return await fetchHistoryResult(provider, symbol, exchange,
|
|
231
236
|
{ kind: "resolution", range: fetchRange, resolution: request.resolution }, context)
|
|
232
237
|
?? { points: [], resolution: request.resolution };
|
|
233
|
-
} catch {
|
|
234
|
-
|
|
238
|
+
} catch (error) {
|
|
239
|
+
logFailure("chart-composer.session-history", "Load trailing intraday history failed")(error);
|
|
240
|
+
return { points: [], resolution: request.resolution, requestFailed: true };
|
|
235
241
|
}
|
|
236
242
|
}
|
|
237
243
|
|
|
@@ -243,18 +249,19 @@ async function loadHistoricalFallback(
|
|
|
243
249
|
now: Date,
|
|
244
250
|
timeZone: string,
|
|
245
251
|
context?: MarketDataRequestContext,
|
|
246
|
-
): Promise<
|
|
252
|
+
): Promise<IntradayHistoryAttempt> {
|
|
247
253
|
const empty = { points: [], resolution: request.resolution };
|
|
254
|
+
const failed: IntradayHistoryAttempt = { ...empty, requestFailed: true };
|
|
248
255
|
if (request.session) {
|
|
249
256
|
const bounds = sessionUtcBounds(request.session, timeZone);
|
|
250
257
|
return await fetchHistoryResult(provider, symbol, exchange,
|
|
251
|
-
{ kind: "detail", start: bounds.start, end: bounds.end, interval: request.resolution }, context).catch(logFailure("chart-composer.session-history", "Load requested intraday session failed",
|
|
258
|
+
{ kind: "detail", start: bounds.start, end: bounds.end, interval: request.resolution }, context).catch(logFailure("chart-composer.session-history", "Load requested intraday session failed", failed)) ?? empty;
|
|
252
259
|
}
|
|
253
260
|
const end = new Date(now.getTime() - HISTORICAL_RETRY_DELAY_MS);
|
|
254
261
|
const lookbackDays = request.rangePreset === "1W" ? 35 : 8;
|
|
255
262
|
const start = new Date(end.getTime() - lookbackDays * DAY_MS);
|
|
256
263
|
return await fetchHistoryResult(provider, symbol, exchange,
|
|
257
|
-
{ kind: "detail", start, end, interval: request.resolution }, context).catch(logFailure("chart-composer.session-history", "Load historical intraday fallback failed",
|
|
264
|
+
{ kind: "detail", start, end, interval: request.resolution }, context).catch(logFailure("chart-composer.session-history", "Load historical intraday fallback failed", failed)) ?? empty;
|
|
258
265
|
}
|
|
259
266
|
|
|
260
267
|
export async function loadIntradayWindow(options: {
|
|
@@ -280,6 +287,7 @@ export async function loadIntradayWindow(options: {
|
|
|
280
287
|
options.exchange,
|
|
281
288
|
options.request, options.context,
|
|
282
289
|
);
|
|
290
|
+
let requestFailed = acquisition.requestFailed === true;
|
|
283
291
|
let raw = acquisition.points;
|
|
284
292
|
let window = resolveIntradaySessionWindow(raw, {
|
|
285
293
|
rangePreset: options.request.rangePreset,
|
|
@@ -303,6 +311,7 @@ export async function loadIntradayWindow(options: {
|
|
|
303
311
|
options.request,
|
|
304
312
|
options.now ?? new Date(), timeZone, options.context,
|
|
305
313
|
);
|
|
314
|
+
requestFailed ||= acquisition.requestFailed === true;
|
|
306
315
|
raw = acquisition.points;
|
|
307
316
|
window = resolveIntradaySessionWindow(raw, {
|
|
308
317
|
rangePreset: options.request.rangePreset,
|
|
@@ -324,7 +333,7 @@ export async function loadIntradayWindow(options: {
|
|
|
324
333
|
unavailableReason: unavailableReason(
|
|
325
334
|
options.symbol,
|
|
326
335
|
options.request,
|
|
327
|
-
sawNonIntradayData ? "not-intraday" : "empty",
|
|
336
|
+
requestFailed ? "failed" : sawNonIntradayData ? "not-intraday" : "empty",
|
|
328
337
|
),
|
|
329
338
|
};
|
|
330
339
|
}
|
|
@@ -338,7 +347,7 @@ export async function loadIntradayWindow(options: {
|
|
|
338
347
|
sessionDates: window.sessionDates,
|
|
339
348
|
start: requestedWindow?.start ?? null,
|
|
340
349
|
end: requestedWindow?.end ?? null,
|
|
341
|
-
unavailableReason: unavailableReason(options.symbol, options.request, "not-intraday"),
|
|
350
|
+
unavailableReason: unavailableReason(options.symbol, options.request, requestFailed ? "failed" : "not-intraday"),
|
|
342
351
|
};
|
|
343
352
|
}
|
|
344
353
|
const bufferedPoints = normalizedPoints(raw);
|
|
@@ -129,6 +129,20 @@ export const CHART_SETTING_KEYS = {
|
|
|
129
129
|
realizedVolEstimator: "chartRealizedVolEstimator",
|
|
130
130
|
} as const;
|
|
131
131
|
|
|
132
|
+
/**
|
|
133
|
+
* Whether rising candles take their series' colour or the theme's gain colour.
|
|
134
|
+
* Kept beside the spec in the pane's settings, so a build without it opens the
|
|
135
|
+
* chart unchanged; it is not one of CHART_SETTING_KEYS, which a spec edit
|
|
136
|
+
* clears as derived from the spec.
|
|
137
|
+
*/
|
|
138
|
+
export const CHART_CANDLE_COLOURS_SETTING_KEY = "chartCandleColours";
|
|
139
|
+
|
|
140
|
+
export type ChartCandleColours = "series" | "change";
|
|
141
|
+
|
|
142
|
+
export function chartCandleColours(value: unknown): ChartCandleColours {
|
|
143
|
+
return value === "change" ? "change" : "series";
|
|
144
|
+
}
|
|
145
|
+
|
|
132
146
|
function fallbackSpec(symbol: string | null | undefined): ChartSpec {
|
|
133
147
|
return symbol ? buildPriceChartPreset(symbol) : buildEmptyChartPreset();
|
|
134
148
|
}
|
|
@@ -417,6 +431,7 @@ export function buildChartComposerPaneSettingsDef(
|
|
|
417
431
|
[CHART_SETTING_KEYS.range]: spec.viewport.range,
|
|
418
432
|
[CHART_SETTING_KEYS.resolution]: spec.viewport.resolution,
|
|
419
433
|
[CHART_SETTING_KEYS.mode]: inlineStyleTarget?.style ?? "",
|
|
434
|
+
[CHART_CANDLE_COLOURS_SETTING_KEY]: chartCandleColours(settings[CHART_CANDLE_COLOURS_SETTING_KEY]),
|
|
420
435
|
...(realizedVol ? {
|
|
421
436
|
[CHART_SETTING_KEYS.realizedVolWindow]: String(realizedVol.parameters.window ?? 30),
|
|
422
437
|
[CHART_SETTING_KEYS.realizedVolEstimator]: realizedVol.parameters.estimator ?? "close-to-close",
|
|
@@ -494,6 +509,18 @@ export function buildChartComposerPaneSettingsDef(
|
|
|
494
509
|
options: modes.map((mode) => ({ value: mode, label: mode.toUpperCase() })),
|
|
495
510
|
}]
|
|
496
511
|
: []),
|
|
512
|
+
...(spec.series.some((series) => isOhlcSeriesStyle(series.style))
|
|
513
|
+
? [{
|
|
514
|
+
key: CHART_CANDLE_COLOURS_SETTING_KEY,
|
|
515
|
+
label: "Candle Colours",
|
|
516
|
+
description: "Draw rising candles in their series' colour, or in the theme's gain colour against its loss colour.",
|
|
517
|
+
type: "select" as const,
|
|
518
|
+
options: [
|
|
519
|
+
{ value: "series", label: "Series colour" },
|
|
520
|
+
{ value: "change", label: "Gains & losses" },
|
|
521
|
+
],
|
|
522
|
+
}]
|
|
523
|
+
: []),
|
|
497
524
|
],
|
|
498
525
|
applyValue: applyChartComposerPaneSetting,
|
|
499
526
|
};
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
import { useAsyncResource } from "../../../react/async-resource";
|
|
1
2
|
import { logFailure } from "../../../utils/log-failure";
|
|
2
|
-
import { useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
3
|
+
import { useCallback, useEffect, useMemo, useState, useSyncExternalStore } from "react";
|
|
3
4
|
import {
|
|
4
5
|
buildTickerSearchCandidates,
|
|
5
6
|
searchTickerCandidates,
|
|
@@ -84,7 +85,7 @@ function candidateToInstrument(candidate: {
|
|
|
84
85
|
* folder) are asked for once and listed after the rest; elsewhere, sources
|
|
85
86
|
* that change while the app runs catch up when the catalog opens.
|
|
86
87
|
*/
|
|
87
|
-
export function useRegisteredSeries()
|
|
88
|
+
export function useRegisteredSeries() {
|
|
88
89
|
const registry = getSharedRegistry();
|
|
89
90
|
const series = registry?.series;
|
|
90
91
|
const version = useSyncExternalStore(
|
|
@@ -93,29 +94,32 @@ export function useRegisteredSeries(): readonly SeriesCatalogEntry[] {
|
|
|
93
94
|
() => 0,
|
|
94
95
|
);
|
|
95
96
|
const disabledPlugins = useOptionalAppSelector((state) => state.config.disabledPlugins, EMPTY_PLUGINS);
|
|
96
|
-
const
|
|
97
|
+
const remoteLoader = useMemo(() => registry?.usesRemoteCapabilities && series ? async () => {
|
|
98
|
+
try {
|
|
99
|
+
const result = await registry.invokeCapability<{ entries: SeriesCatalogEntry[] }>(SERIES_CATALOG_CAPABILITY_ID, "list", {});
|
|
100
|
+
if (!Array.isArray(result?.entries)) throw new Error("Invalid series catalog response");
|
|
101
|
+
return result.entries;
|
|
102
|
+
} catch (error) {
|
|
103
|
+
logFailure("chart-composer.use-series-catalog", "Load remote chart series catalog failed")(error);
|
|
104
|
+
throw new Error("Series catalog unavailable.");
|
|
105
|
+
}
|
|
106
|
+
} : null, [registry, series]);
|
|
107
|
+
const remote = useAsyncResource(remoteLoader);
|
|
97
108
|
|
|
98
109
|
useEffect(() => {
|
|
99
|
-
if (!registry
|
|
100
|
-
if (!registry.usesRemoteCapabilities) {
|
|
101
|
-
void series.refresh();
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
let cancelled = false;
|
|
105
|
-
void registry.invokeCapability<{ entries: SeriesCatalogEntry[] }>(SERIES_CATALOG_CAPABILITY_ID, "list", {})
|
|
106
|
-
.then((result) => {
|
|
107
|
-
if (!cancelled && Array.isArray(result?.entries)) setRemote(result.entries);
|
|
108
|
-
})
|
|
109
|
-
.catch(logFailure("chart-composer.use-series-catalog", "Load remote chart series catalog failed"));
|
|
110
|
-
return () => { cancelled = true; };
|
|
110
|
+
if (registry && series && !registry.usesRemoteCapabilities) void series.refresh();
|
|
111
111
|
}, [registry, series]);
|
|
112
|
+
const retry = useCallback(async () => {
|
|
113
|
+
if (remoteLoader) await remote.reload();
|
|
114
|
+
else await series?.refresh();
|
|
115
|
+
}, [remoteLoader, remote.reload, series]);
|
|
112
116
|
|
|
113
|
-
|
|
117
|
+
const entries = useMemo(() => {
|
|
114
118
|
const local = series?.catalog() ?? EMPTY_ENTRIES;
|
|
115
119
|
let entries = local;
|
|
116
|
-
if (remote?.length) {
|
|
120
|
+
if (remote.data?.length) {
|
|
117
121
|
const known = new Set(local.map((entry) => entry.id));
|
|
118
|
-
const extra = remote.filter((entry) => !known.has(entry.id));
|
|
122
|
+
const extra = remote.data.filter((entry) => !known.has(entry.id));
|
|
119
123
|
if (extra.length) entries = [...local, ...extra];
|
|
120
124
|
}
|
|
121
125
|
if (disabledPlugins.length === 0) return entries;
|
|
@@ -123,7 +127,8 @@ export function useRegisteredSeries(): readonly SeriesCatalogEntry[] {
|
|
|
123
127
|
return entries.some((entry) => disabled.has(entry.pluginId))
|
|
124
128
|
? entries.filter((entry) => !disabled.has(entry.pluginId))
|
|
125
129
|
: entries;
|
|
126
|
-
}, [disabledPlugins, remote, series, version]);
|
|
130
|
+
}, [disabledPlugins, remote.data, series, version]);
|
|
131
|
+
return { entries, loading: remote.loading, error: remote.error, retry };
|
|
127
132
|
}
|
|
128
133
|
|
|
129
134
|
export function useCatalogUniverse(query: string): {
|
|
@@ -230,6 +235,7 @@ export interface SeriesCatalogSearchResult {
|
|
|
230
235
|
loading: boolean;
|
|
231
236
|
/** Set when a lookup failed, so an outage is never reported as zero matches. */
|
|
232
237
|
error: string | null;
|
|
238
|
+
retry: () => Promise<void>;
|
|
233
239
|
}
|
|
234
240
|
|
|
235
241
|
function searchFailureMessage(error: unknown): string {
|
|
@@ -248,6 +254,11 @@ export function useSeriesCatalogSuggestions({
|
|
|
248
254
|
}): SeriesCatalogSearchResult {
|
|
249
255
|
const tickers = useOptionalAppSelector((state) => state.tickers, EMPTY_TICKERS);
|
|
250
256
|
const registered = useRegisteredSeries();
|
|
257
|
+
const [attempt, setAttempt] = useState(0);
|
|
258
|
+
const retry = useCallback(async () => {
|
|
259
|
+
setAttempt((value) => value + 1);
|
|
260
|
+
await registered.retry();
|
|
261
|
+
}, [registered.retry]);
|
|
251
262
|
const analysis = useMemo(() => analyzeSeriesSearchQuery(query), [query]);
|
|
252
263
|
const [providerSearch, setProviderSearch] = useState<{
|
|
253
264
|
query: string;
|
|
@@ -295,7 +306,7 @@ export function useSeriesCatalogSuggestions({
|
|
|
295
306
|
controller.abort();
|
|
296
307
|
clearTimeout(timer);
|
|
297
308
|
};
|
|
298
|
-
}, [enabled, query]);
|
|
309
|
+
}, [attempt, enabled, query]);
|
|
299
310
|
|
|
300
311
|
useEffect(() => {
|
|
301
312
|
const instrumentQuery = analysis.instrumentQuery.trim();
|
|
@@ -344,7 +355,7 @@ export function useSeriesCatalogSuggestions({
|
|
|
344
355
|
cancelled = true;
|
|
345
356
|
clearTimeout(timer);
|
|
346
357
|
};
|
|
347
|
-
}, [analysis.directInstrument, analysis.instrumentQuery, enabled, tickers]);
|
|
358
|
+
}, [analysis.directInstrument, analysis.instrumentQuery, attempt, enabled, tickers]);
|
|
348
359
|
|
|
349
360
|
const instruments = search.query === analysis.instrumentQuery
|
|
350
361
|
? search.instruments
|
|
@@ -353,18 +364,21 @@ export function useSeriesCatalogSuggestions({
|
|
|
353
364
|
// and they arrive well before plugin catalogs (which can take seconds), so
|
|
354
365
|
// late plugin results append below instead of pushing the list around.
|
|
355
366
|
const suggestions = useMemo(() => {
|
|
356
|
-
const builtIn = buildSeriesCatalogSuggestions(query, defaultInstrument, instruments, MAX_SERIES_SUGGESTIONS, registered);
|
|
367
|
+
const builtIn = buildSeriesCatalogSuggestions(query, defaultInstrument, instruments, MAX_SERIES_SUGGESTIONS, registered.entries);
|
|
357
368
|
const provider = providerSearch.query === query.trim() ? providerSearch.suggestions : [];
|
|
358
369
|
return [...builtIn, ...provider.filter((entry) => !builtIn.some((candidate) => candidate.id === entry.id))]
|
|
359
370
|
.slice(0, MAX_SERIES_SUGGESTIONS);
|
|
360
|
-
}, [defaultInstrument, instruments, providerSearch, query, registered]);
|
|
371
|
+
}, [defaultInstrument, instruments, providerSearch, query, registered.entries]);
|
|
361
372
|
|
|
362
373
|
return {
|
|
363
374
|
suggestions,
|
|
364
375
|
instruments,
|
|
365
|
-
|
|
376
|
+
retry,
|
|
377
|
+
loading: registered.loading || (search.loading && search.query === analysis.instrumentQuery)
|
|
366
378
|
|| (providerSearch.loading && providerSearch.query === query.trim()),
|
|
367
|
-
error:
|
|
368
|
-
|
|
379
|
+
error: [registered.error,
|
|
380
|
+
search.query === analysis.instrumentQuery ? search.error : null,
|
|
381
|
+
providerSearch.query === query.trim() ? providerSearch.error : null,
|
|
382
|
+
].filter(Boolean).join(" ") || null,
|
|
369
383
|
};
|
|
370
384
|
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import type { ChatFunction } from "../pane-links/commands";
|
|
2
|
+
|
|
3
|
+
/** A function offered after a backtick: its code and what it is called. */
|
|
4
|
+
export interface ChatCommandSuggestion {
|
|
5
|
+
code: string;
|
|
6
|
+
name: string;
|
|
7
|
+
takesArg: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
interface ChatCommandTrigger {
|
|
11
|
+
/** Where the opening backtick is. */
|
|
12
|
+
start: number;
|
|
13
|
+
end: number;
|
|
14
|
+
query: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// An opening backtick after a space or punctuation (not inside a word, not a
|
|
18
|
+
// second tick of a fence), then the start of a code.
|
|
19
|
+
const COMMAND_TRIGGER_RE = /(^|[\s([{<,.;:!?"'])`([A-Za-z][A-Za-z0-9+-]{0,11})$/;
|
|
20
|
+
const FENCE_RE = /^\s{0,3}```/;
|
|
21
|
+
const MAX_COMMAND_SUGGESTIONS = 5;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The code being typed after an opening backtick, up to the cursor. A
|
|
25
|
+
* backtick that closes a span already open on the line, or one inside a
|
|
26
|
+
* fenced code block, starts no code.
|
|
27
|
+
*/
|
|
28
|
+
export function detectChatCommandTrigger(draft: string, cursorOffset: number): ChatCommandTrigger | null {
|
|
29
|
+
const cursor = Math.max(0, Math.min(cursorOffset, draft.length));
|
|
30
|
+
const beforeCursor = draft.slice(0, cursor);
|
|
31
|
+
const match = beforeCursor.match(COMMAND_TRIGGER_RE);
|
|
32
|
+
if (!match) return null;
|
|
33
|
+
const query = match[2]!;
|
|
34
|
+
const start = cursor - query.length - 1;
|
|
35
|
+
const lines = draft.slice(0, start).split("\n");
|
|
36
|
+
const line = lines[lines.length - 1]!;
|
|
37
|
+
if ((line.match(/`/g)?.length ?? 0) % 2 === 1) return null;
|
|
38
|
+
if (lines.slice(0, -1).filter((previous) => FENCE_RE.test(previous)).length % 2 === 1) return null;
|
|
39
|
+
return { start, end: cursor, query };
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The functions whose code starts with the query, the exact one and the
|
|
44
|
+
* shortest first, then those with a word of their name that does, as people
|
|
45
|
+
* are found by their handle or their name.
|
|
46
|
+
*/
|
|
47
|
+
export function filterCommandSuggestions(
|
|
48
|
+
functions: ReadonlyMap<string, ChatFunction>,
|
|
49
|
+
query: string,
|
|
50
|
+
limit = MAX_COMMAND_SUGGESTIONS,
|
|
51
|
+
): ChatCommandSuggestion[] {
|
|
52
|
+
const upper = query.trim().toUpperCase();
|
|
53
|
+
if (!upper) return [];
|
|
54
|
+
const lower = upper.toLowerCase();
|
|
55
|
+
const byCode: ChatFunction[] = [];
|
|
56
|
+
const byName: ChatFunction[] = [];
|
|
57
|
+
for (const fn of new Set(functions.values())) {
|
|
58
|
+
if (fn.unlisted) continue;
|
|
59
|
+
if (fn.code.startsWith(upper) || fn.aliases.includes(upper)) byCode.push(fn);
|
|
60
|
+
else if (fn.name.toLowerCase().split(/[\s/&()-]+/).some((word) => word.startsWith(lower))) byName.push(fn);
|
|
61
|
+
}
|
|
62
|
+
const rank = (fn: ChatFunction) => (fn.code === upper || fn.aliases.includes(upper) ? 0 : 1);
|
|
63
|
+
byCode.sort((a, b) => rank(a) - rank(b) || a.code.length - b.code.length || a.code.localeCompare(b.code));
|
|
64
|
+
byName.sort((a, b) => a.code.localeCompare(b.code));
|
|
65
|
+
return [...byCode, ...byName].slice(0, Math.min(limit, MAX_COMMAND_SUGGESTIONS))
|
|
66
|
+
.map(({ code, name, arg }) => ({ code, name, takesArg: arg !== "none" }));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The code in place of what was typed, with the closing backtick. A function
|
|
71
|
+
* that takes an argument leaves the cursor before it, after a space, for the
|
|
72
|
+
* ticker or topic; any other leaves it after the span and a space.
|
|
73
|
+
*/
|
|
74
|
+
export function applyCommandSuggestion(
|
|
75
|
+
draft: string,
|
|
76
|
+
trigger: ChatCommandTrigger,
|
|
77
|
+
suggestion: ChatCommandSuggestion,
|
|
78
|
+
): { draft: string; cursorOffset: number } {
|
|
79
|
+
const before = draft.slice(0, trigger.start);
|
|
80
|
+
let after = draft.slice(trigger.end);
|
|
81
|
+
// A closing backtick already there is the one this span ends with.
|
|
82
|
+
if (after.startsWith("`")) after = after.slice(1);
|
|
83
|
+
const code = `\`${suggestion.code}`;
|
|
84
|
+
if (suggestion.takesArg) {
|
|
85
|
+
return { draft: `${before}${code} \`${after}`, cursorOffset: before.length + code.length + 1 };
|
|
86
|
+
}
|
|
87
|
+
// A space to go on writing after, unless the text there already goes on.
|
|
88
|
+
const suffix = /^[\s.,;:!?)\]]/.test(after) ? "" : " ";
|
|
89
|
+
return {
|
|
90
|
+
draft: `${before}${code}\`${suffix}${after}`,
|
|
91
|
+
cursorOffset: before.length + code.length + 1 + suffix.length,
|
|
92
|
+
};
|
|
93
|
+
}
|
|
@@ -10,7 +10,6 @@ import { Button, ListView } from "../../../../components/ui";
|
|
|
10
10
|
import { COMPOSER_ACTION_WIDTH } from "../layout";
|
|
11
11
|
import { isManagedBot } from "../ghost-user";
|
|
12
12
|
import { ChatBotIcon } from "../message/person-icons";
|
|
13
|
-
import type { ChatMentionSuggestion } from "./mentions";
|
|
14
13
|
import type { ChatDraftAttachment } from "../controller/attachments";
|
|
15
14
|
import { DesktopChatAttachButton, DesktopChatAttachmentStrip } from "../attachments/desktop";
|
|
16
15
|
import { TerminalChatAttachmentRows } from "../attachments/terminal";
|
|
@@ -48,7 +47,8 @@ interface ChatComposerAreaProps {
|
|
|
48
47
|
replyPreview: string;
|
|
49
48
|
replyTo: ChatMessage | null;
|
|
50
49
|
sendMessage: () => void;
|
|
51
|
-
|
|
50
|
+
/** People after @, or functions after a backtick: one list at a time. */
|
|
51
|
+
suggestions: ChatComposerSuggestion[];
|
|
52
52
|
mentionSelectedIndex: number;
|
|
53
53
|
onMentionCursorChange: () => void;
|
|
54
54
|
onMentionSelect: (index?: number) => boolean;
|
|
@@ -69,6 +69,13 @@ interface ChatComposerAreaProps {
|
|
|
69
69
|
attachPickerRef: { current: (() => void) | null };
|
|
70
70
|
}
|
|
71
71
|
|
|
72
|
+
/** A row of the list over the composer: a handle, or a function's code with its name at the end. */
|
|
73
|
+
interface ChatComposerSuggestion {
|
|
74
|
+
id: string;
|
|
75
|
+
label: string;
|
|
76
|
+
detail?: string;
|
|
77
|
+
}
|
|
78
|
+
|
|
72
79
|
function ChatMentionSuggestions({
|
|
73
80
|
contentWidth,
|
|
74
81
|
onSelect,
|
|
@@ -78,14 +85,14 @@ function ChatMentionSuggestions({
|
|
|
78
85
|
contentWidth: number;
|
|
79
86
|
onSelect: (index?: number) => boolean;
|
|
80
87
|
selectedIndex: number;
|
|
81
|
-
suggestions:
|
|
88
|
+
suggestions: ChatComposerSuggestion[];
|
|
82
89
|
}) {
|
|
83
90
|
if (suggestions.length === 0) return null;
|
|
84
91
|
const width = Math.max(18, Math.min(contentWidth, 44));
|
|
85
92
|
return (
|
|
86
93
|
<Box width={width}>
|
|
87
94
|
<ListView
|
|
88
|
-
items={suggestions
|
|
95
|
+
items={suggestions}
|
|
89
96
|
selectedIndex={selectedIndex}
|
|
90
97
|
height={suggestions.length}
|
|
91
98
|
rowGap={0}
|
|
@@ -119,7 +126,7 @@ export function ChatComposerArea({
|
|
|
119
126
|
replyPreview,
|
|
120
127
|
replyTo,
|
|
121
128
|
sendMessage,
|
|
122
|
-
|
|
129
|
+
suggestions,
|
|
123
130
|
mentionSelectedIndex,
|
|
124
131
|
onMentionCursorChange,
|
|
125
132
|
onMentionSelect,
|
|
@@ -211,7 +218,7 @@ export function ChatComposerArea({
|
|
|
211
218
|
contentWidth={contentWidth}
|
|
212
219
|
onSelect={onMentionSelect}
|
|
213
220
|
selectedIndex={mentionSelectedIndex}
|
|
214
|
-
suggestions={
|
|
221
|
+
suggestions={suggestions}
|
|
215
222
|
/>
|
|
216
223
|
|
|
217
224
|
{!editingMessage && draftAttachments.length > 0 && (nativePaneChrome ? (
|
|
@@ -9,6 +9,7 @@ import { useOptionalDialog, type PromptContext } from "../../../../ui/dialog";
|
|
|
9
9
|
import { chatAuthorName, isDiscordGhost } from "../ghost-user";
|
|
10
10
|
import { chatMessageOpenTargets, type ChatOpenTarget } from "./open-targets";
|
|
11
11
|
import { chatMessagePaneLinks, type ChatMessagePaneLink } from "../pane-links/model";
|
|
12
|
+
import { useChatFunctions } from "../pane-links/commands";
|
|
12
13
|
import { openChatImageViewer } from "../attachments/desktop";
|
|
13
14
|
import { canReactToMessage } from "../reactions";
|
|
14
15
|
|
|
@@ -102,11 +103,13 @@ export function useChatFooter({
|
|
|
102
103
|
const rendererHost = useRendererHost();
|
|
103
104
|
const { links: paneLinks, content } = chatMessagePaneLinks(selectedMessage);
|
|
104
105
|
const attachments = selectedMessage?.attachments;
|
|
106
|
+
const { nativePaneChrome } = useUiCapabilities();
|
|
107
|
+
const functions = useChatFunctions();
|
|
108
|
+
// The desktop and the web draw functions written in a message as chips, so Open offers them too.
|
|
105
109
|
const openTargets = useMemo(
|
|
106
|
-
() => chatMessageOpenTargets(content, catalog, attachments, paneLinks),
|
|
107
|
-
[attachments, catalog, content, paneLinks],
|
|
110
|
+
() => chatMessageOpenTargets(content, catalog, attachments, paneLinks, nativePaneChrome ? functions : undefined),
|
|
111
|
+
[attachments, catalog, content, functions, nativePaneChrome, paneLinks],
|
|
108
112
|
);
|
|
109
|
-
const { nativePaneChrome } = useUiCapabilities();
|
|
110
113
|
|
|
111
114
|
const openTarget = useCallback((target: ChatOpenTarget) => {
|
|
112
115
|
if (target.kind === "pane") {
|