@gloom-sh/terminal 0.18.5 → 0.19.1
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 +163 -8
- package/src/api-client/app-attention.ts +3 -0
- package/src/api-client/beneficial-owners.ts +3 -0
- package/src/api-client/company-kpis.ts +3 -0
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +19 -7
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/hiring.ts +3 -0
- package/src/api-client/index.ts +15 -0
- package/src/api-client/ipo.ts +24 -1
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +1 -1
- package/src/api-client/mna.ts +3 -0
- package/src/api-client/paths.ts +9 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/roadmap.ts +85 -0
- package/src/api-client/types.ts +82 -2
- package/src/app/pane-runtime/plugin-bindings.ts +9 -5
- package/src/app/runtime/broker-import.ts +0 -2
- package/src/app/runtime/plugin-bindings.ts +73 -56
- package/src/architecture/core-builtin-allowlist.json +2 -2
- 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/market.ts +107 -38
- package/src/cli/commands/news-search.ts +22 -0
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +147 -32
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +87 -36
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +25 -2
- package/src/cli/glossary.ts +31 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index-roots.ts +0 -7
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +235 -60
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +4 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/discovery.ts +21 -1
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +93 -4
- package/src/cli/pane-functions/function-help.ts +20 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +54 -16
- package/src/cli/pane-functions/market-session.ts +66 -4
- package/src/cli/pane-functions/options.ts +19 -1
- package/src/cli/pane-functions/resolver.ts +19 -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 +22 -11
- package/src/cli/saved-copy-persistence.ts +88 -0
- package/src/cli/ticker-resolution.ts +3 -2
- package/src/components/chart/composite/composite-chart.tsx +4 -0
- package/src/components/chart/composite/interactions.ts +6 -0
- package/src/components/chart/composite/legend.tsx +45 -7
- package/src/components/chart/composite/panel-gestures.ts +33 -0
- package/src/components/chart/composite/panel-surface.tsx +52 -3
- package/src/components/chart/composite/rasterizer.ts +24 -13
- package/src/components/chart/composite/scene.ts +39 -4
- 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/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/list/model.ts +5 -0
- package/src/components/command-bar/pane-templates/items.ts +3 -2
- package/src/components/command-bar/routes/root/runtime.ts +6 -7
- package/src/components/command-bar/selection-runtime.ts +52 -3
- package/src/components/command-bar/surface/index.tsx +2 -0
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- package/src/components/layout/pane/capture-stamp.ts +2 -2
- package/src/components/layout/pane/footer/index.tsx +22 -6
- package/src/components/layout/pane/footer/model.ts +83 -15
- package/src/components/layout/pane/status-footer.ts +35 -2
- package/src/components/layout/status-widget-space.ts +9 -0
- package/src/components/onboarding/onboarding-wizard.tsx +4 -1
- package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
- 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 +8 -21
- 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 +18 -3
- package/src/components/ui/icon.tsx +4 -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/data/config/chart-settings.ts +1 -1
- package/src/data/json-ticker-repository.ts +17 -8
- package/src/data/plugin-cache.ts +5 -3
- package/src/data/saved-copies.ts +8 -0
- 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 +103 -142
- package/src/i18n/es.ts +20 -6
- package/src/i18n/ja.ts +20 -6
- package/src/i18n/ko.ts +20 -6
- package/src/i18n/zh-cn.ts +20 -6
- package/src/i18n/zh-tw.ts +20 -6
- package/src/market-data/depositary-pair.ts +27 -3
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +16 -3
- package/src/market-data/market/format.ts +25 -0
- package/src/market-data/market/freshness.ts +2 -1
- package/src/market-data/options-alternatives.ts +81 -4
- package/src/market-data/quotes/metadata.ts +7 -0
- package/src/news/aggregator.ts +119 -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 +17 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +362 -80
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/event-rows.ts +24 -3
- package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
- package/src/plugins/builtin/alerts/fired-log.ts +73 -9
- package/src/plugins/builtin/alerts/format.ts +75 -5
- package/src/plugins/builtin/alerts/headless.ts +34 -12
- package/src/plugins/builtin/alerts/history-rows.ts +3 -2
- package/src/plugins/builtin/alerts/history.ts +1 -1
- package/src/plugins/builtin/alerts/index.tsx +79 -16
- package/src/plugins/builtin/alerts/metric-command.ts +77 -4
- package/src/plugins/builtin/alerts/metric-data.ts +54 -13
- package/src/plugins/builtin/alerts/metric-model.ts +163 -14
- package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
- package/src/plugins/builtin/alerts/pane.tsx +61 -23
- package/src/plugins/builtin/alerts/quotes.ts +6 -0
- package/src/plugins/builtin/alerts/storage.ts +12 -8
- package/src/plugins/builtin/alerts/types.ts +4 -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/broker-manager/index.tsx +7 -3
- 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 +6 -2
- package/src/plugins/builtin/cds/client.ts +142 -30
- package/src/plugins/builtin/cds/headless.ts +35 -8
- package/src/plugins/builtin/cds/model.ts +145 -18
- package/src/plugins/builtin/cds/pane.tsx +16 -5
- package/src/plugins/builtin/changelog/index.tsx +9 -6
- package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
- package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
- package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
- package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
- package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
- package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +5 -6
- package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
- package/src/plugins/builtin/chart-composer/settings.ts +27 -0
- package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
- package/src/plugins/builtin/chat/controller/index.ts +5 -1
- package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
- package/src/plugins/builtin/chat/controller/state.ts +25 -2
- package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
- package/src/plugins/builtin/chat/controller/utils.ts +7 -2
- package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
- package/src/plugins/builtin/chat/status-preview.ts +79 -0
- package/src/plugins/builtin/chat/status-widget.tsx +147 -10
- package/src/plugins/builtin/chat/unread-badge.ts +51 -10
- package/src/plugins/builtin/clinical-trials/client.ts +49 -4
- package/src/plugins/builtin/clinical-trials/model.ts +86 -31
- package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
- package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
- package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- 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/headless.ts +2 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +12 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/company-kpis/headless.ts +8 -3
- package/src/plugins/builtin/company-kpis/model.ts +73 -5
- package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
- package/src/plugins/builtin/composite-plugins.ts +2 -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 +10 -10
- 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/doe/headless.ts +3 -2
- package/src/plugins/builtin/doe/pane.tsx +13 -3
- 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/data.ts +18 -6
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
- package/src/plugins/builtin/econ/calendar-model.ts +48 -1
- package/src/plugins/builtin/econ/calendar-source.ts +4 -2
- package/src/plugins/builtin/econ/detail-view.tsx +2 -2
- package/src/plugins/builtin/econ/figures.ts +48 -0
- package/src/plugins/builtin/econ/index.tsx +14 -5
- package/src/plugins/builtin/econ/print-watch.ts +3 -1
- package/src/plugins/builtin/econ/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/client.ts +11 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
- package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
- 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 +68 -13
- package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
- 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 +6 -3
- package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
- package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
- package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -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 +62 -59
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
- package/src/plugins/builtin/help/table-cells.tsx +13 -2
- package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
- package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +22 -3
- package/src/plugins/builtin/holders/pane.tsx +3 -0
- package/src/plugins/builtin/holders/report-rows.ts +4 -1
- package/src/plugins/builtin/insider/headless.ts +2 -2
- package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
- package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
- package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
- package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
- 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 +33 -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/headless.ts +12 -5
- package/src/plugins/builtin/macro-day/index.tsx +1 -1
- package/src/plugins/builtin/macro-day/model.ts +39 -5
- package/src/plugins/builtin/macro-day/pane.tsx +27 -19
- 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 +27 -7
- package/src/plugins/builtin/market-movers/headless.ts +34 -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 +22 -4
- 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 +2 -1
- package/src/plugins/builtin/mna/headless.ts +2 -0
- package/src/plugins/builtin/mna/pane.tsx +3 -1
- package/src/plugins/builtin/news/headless.ts +3 -1
- package/src/plugins/builtin/news/index.tsx +4 -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 +29 -5
- package/src/plugins/builtin/options/enrichment.ts +17 -5
- package/src/plugins/builtin/options/table.ts +5 -2
- package/src/plugins/builtin/options/types.ts +2 -0
- package/src/plugins/builtin/options/view.tsx +17 -4
- package/src/plugins/builtin/options-positioning/client.ts +11 -0
- package/src/plugins/builtin/options-positioning/headless.ts +21 -8
- package/src/plugins/builtin/options-positioning/index.tsx +1 -1
- package/src/plugins/builtin/options-positioning/model.ts +78 -9
- package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
- 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/headless.ts +65 -9
- package/src/plugins/builtin/perps/index.tsx +7 -5
- package/src/plugins/builtin/perps/model.ts +18 -4
- package/src/plugins/builtin/perps/pane.tsx +19 -22
- 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 +7 -5
- package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
- package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
- 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/index.tsx +0 -14
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
- package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/realized-vol/client.ts +13 -5
- package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
- package/src/plugins/builtin/realized-vol/headless.ts +8 -4
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/model.ts +37 -6
- package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
- 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 +40 -12
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/index.tsx +7 -3
- package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
- package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
- package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
- 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/risk-factors/data.ts +4 -2
- package/src/plugins/builtin/risk-factors/headless.ts +5 -2
- package/src/plugins/builtin/roadmap/index.tsx +28 -0
- package/src/plugins/builtin/roadmap/model.ts +67 -0
- package/src/plugins/builtin/roadmap/pane.tsx +244 -0
- 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 +16 -5
- package/src/plugins/builtin/scanner/live-headless.ts +48 -5
- package/src/plugins/builtin/scanner/live-model.ts +11 -0
- package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
- 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 +4 -2
- package/src/plugins/builtin/sec/index.tsx +3 -0
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
- 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/headless-market-data.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -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 +11 -2
- package/src/plugins/builtin/shared/research-cells.tsx +1 -1
- package/src/plugins/builtin/shared/series-cache.ts +4 -4
- 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 +2 -1
- package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
- package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
- package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
- package/src/plugins/builtin/themes/model.ts +16 -7
- package/src/plugins/builtin/themes/pane.tsx +5 -5
- package/src/plugins/builtin/thirteenf/api.ts +12 -1
- package/src/plugins/builtin/thirteenf/data.ts +7 -7
- package/src/plugins/builtin/thirteenf/headless.ts +31 -4
- 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 +11 -3
- package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
- 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-auctions/headless.ts +21 -12
- package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
- 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/client.ts +3 -13
- package/src/plugins/builtin/vol-surface/headless.ts +3 -3
- package/src/plugins/builtin/vol-surface/index.tsx +1 -1
- package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
- package/src/plugins/builtin/volatility/index.tsx +2 -2
- package/src/plugins/builtin/volatility/pane.tsx +7 -4
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/builtin/yield-curve/client.ts +61 -1
- package/src/plugins/builtin/yield-curve/headless.ts +9 -6
- package/src/plugins/builtin/yield-curve/index.tsx +34 -10
- package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
- package/src/plugins/catalog-browser.ts +2 -1
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/host-link.ts +1 -1
- package/src/plugins/marketplace/model.ts +18 -5
- package/src/portfolio/column-values.ts +11 -7
- package/src/portfolio/futures-positions.ts +6 -3
- package/src/portfolio/position-quantity.ts +15 -9
- package/src/remote/resources.ts +2 -1
- package/src/renderers/dom/data-table/row.tsx +26 -0
- package/src/renderers/dom/desktop/icons.tsx +7 -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 +3 -3
- package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
- package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
- package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
- package/src/renderers/electrobun/bun/index.ts +26 -2
- package/src/renderers/electrobun/bun/window/create.ts +43 -4
- package/src/renderers/electrobun/bun/window/events.ts +20 -0
- package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
- package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
- package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -0
- package/src/renderers/electrobun/shared/protocol.ts +12 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +5 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/ui-host.tsx +8 -0
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- 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/fred-series.ts +3 -1
- package/src/sources/gloomberb-cloud/index.ts +43 -8
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/provider-router/financials.ts +15 -3
- package/src/sources/provider-router/index.ts +8 -5
- package/src/state/ticker-cleanup.ts +50 -0
- package/src/sync/controller.ts +4 -3
- package/src/sync/core-contributors.ts +90 -31
- 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/test-support/preload.ts +14 -0
- package/src/theme/themes.ts +10 -0
- package/src/tickers/company-name-listing.ts +53 -13
- package/src/tickers/company-name.ts +50 -4
- package/src/tickers/index-roots.ts +7 -0
- package/src/tickers/listing-fate.ts +2 -2
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/search/index.ts +6 -2
- package/src/tickers/search/ranking.ts +5 -1
- package/src/tickers/search/upsert.ts +13 -0
- package/src/tickers/terminal-codes.ts +19 -0
- package/src/tickers/ticker-changes.ts +8 -4
- package/src/time-series/alignment.ts +1 -1
- package/src/time-series/economic.ts +18 -4
- package/src/time-series/field-catalog.ts +1 -1
- package/src/time-series/filed-trailing-eps.ts +197 -0
- package/src/time-series/fundamentals.ts +44 -1
- package/src/time-series/ice-credit.ts +3 -0
- package/src/time-series/price-comparison.ts +30 -2
- package/src/time-series/resolve-context.ts +3 -1
- package/src/time-series/resolve.ts +56 -4
- package/src/time-series/session-bar.ts +11 -0
- package/src/time-series/spec.ts +3 -2
- package/src/time-series/studies.ts +8 -2
- package/src/time-series/total-return.ts +160 -0
- package/src/time-series/transforms.ts +2 -3
- package/src/time-series/types.ts +22 -1
- package/src/types/company-from.ts +40 -0
- package/src/types/data-provider.ts +7 -0
- package/src/types/financials.ts +59 -1
- package/src/types/headless.ts +27 -3
- package/src/types/news-source.ts +1 -0
- package/src/types/plugin.ts +9 -0
- package/src/ui/host.tsx +3 -0
- package/src/updater/failure.ts +4 -1
- package/src/utils/cli-output.ts +42 -1
- package/src/utils/crypto-pair.ts +165 -7
- package/src/utils/declared-dividends.ts +1 -1
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +60 -8
- 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/price-history.ts +7 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/version.ts +2 -2
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { formatPercentRaw } from "../../../utils/format";
|
|
2
2
|
import type { ChartVectorShape } from "../../../ui/host";
|
|
3
3
|
import type { NativeChartBitmap } from "../native/chart-rasterizer";
|
|
4
|
-
import { drawCircle, drawLine, fillRect, parseHex } from "../native/raster/primitives";
|
|
5
|
-
import { formatCompositeAxisValue, formatCompositeCursorDate } from "./format";
|
|
4
|
+
import { drawCircle, drawLine, fillRect, parseHex, type RgbaColor } from "../native/raster/primitives";
|
|
5
|
+
import { formatCompositeAxisValue, formatCompositeCursorDate, formatCompositeCursorValue } from "./format";
|
|
6
6
|
import { projectCompositeValue, unprojectCompositeValue } from "./scene";
|
|
7
7
|
import { projectCompositeTimestamp, unprojectCompositeTimestamp } from "./time-scale";
|
|
8
8
|
import type {
|
|
@@ -13,7 +13,14 @@ import type {
|
|
|
13
13
|
import { clamp } from "../../../utils/math";
|
|
14
14
|
|
|
15
15
|
/** Pointer tools that take the drag away from panning while one is picked. */
|
|
16
|
-
export type ChartToolKind = "measure" | "zoom" | "line" | "pencil" | "level";
|
|
16
|
+
export type ChartToolKind = "measure" | "zoom" | "line" | "pencil" | "level" | "rect" | "fib" | "ray";
|
|
17
|
+
|
|
18
|
+
/** How a saved drawing is drawn from its points. */
|
|
19
|
+
export type ChartDrawingShape = "line" | "rect" | "fib" | "ray";
|
|
20
|
+
const SHAPED_TOOLS = ["rect", "fib", "ray"] as const;
|
|
21
|
+
|
|
22
|
+
/** Retracement ratios, measured back from the drag's end toward its start. */
|
|
23
|
+
const FIB_LEVELS = [0, 0.236, 0.382, 0.5, 0.618, 0.786, 1] as const;
|
|
17
24
|
|
|
18
25
|
export interface ChartDrawingPoint {
|
|
19
26
|
time: number;
|
|
@@ -77,6 +84,18 @@ export function parseChartDrawings(value: unknown): ChartDrawing[] {
|
|
|
77
84
|
return drawings;
|
|
78
85
|
}
|
|
79
86
|
|
|
87
|
+
/**
|
|
88
|
+
* The shape rides in the id (`fib:3`) rather than a field of its own: an older
|
|
89
|
+
* build refuses a saved drawing with a key it does not know and would drop every
|
|
90
|
+
* drawing of the pane, while an id it does not know still draws as a line.
|
|
91
|
+
*/
|
|
92
|
+
export function chartDrawingShape(drawing: Pick<ChartDrawing, "id" | "points">): ChartDrawingShape {
|
|
93
|
+
const prefix = drawing.id.slice(0, Math.max(0, drawing.id.indexOf(":")));
|
|
94
|
+
return drawing.points.length === 2 && (SHAPED_TOOLS as readonly string[]).includes(prefix)
|
|
95
|
+
? prefix as ChartDrawingShape
|
|
96
|
+
: "line";
|
|
97
|
+
}
|
|
98
|
+
|
|
80
99
|
export interface ChartDrawingHit {
|
|
81
100
|
drawing: ChartDrawing;
|
|
82
101
|
/** Grabbed endpoint, or null when the whole shape was grabbed. */
|
|
@@ -119,7 +138,7 @@ const HOUR_MS = 60 * MINUTE_MS;
|
|
|
119
138
|
const DAY_MS = 24 * HOUR_MS;
|
|
120
139
|
|
|
121
140
|
function drawingPointFrom(
|
|
122
|
-
scene: CompositeChartScene,
|
|
141
|
+
scene: Pick<CompositeChartScene, "timeScale">,
|
|
123
142
|
domain: CompositeAxisDomain,
|
|
124
143
|
xRatio: number,
|
|
125
144
|
yRatio: number,
|
|
@@ -132,14 +151,23 @@ function drawingPointFrom(
|
|
|
132
151
|
|
|
133
152
|
/** Data anchors for a finished drag, so a drawing survives pan and zoom. */
|
|
134
153
|
export function resolveDrawingFromDrag(
|
|
135
|
-
scene: CompositeChartScene,
|
|
154
|
+
scene: Pick<CompositeChartScene, "timeScale">,
|
|
136
155
|
panel: CompositePanelScene,
|
|
137
156
|
drag: ChartToolDrag,
|
|
138
157
|
color: string,
|
|
139
158
|
id: string,
|
|
140
159
|
): ChartDrawing | null {
|
|
141
160
|
const domain = resolveMeasureAxisDomain(panel);
|
|
142
|
-
|
|
161
|
+
// A ray needs only its price, so a click places one.
|
|
162
|
+
if (!domain || (drag.kind !== "ray" && !isMeaningfulToolDrag(drag))) return null;
|
|
163
|
+
if (isShapedTool(drag.kind)) {
|
|
164
|
+
// A ray holds its first price, so its stored end is level with it and an
|
|
165
|
+
// older build draws the same horizontal run.
|
|
166
|
+
const endYRatio = drag.kind === "ray" ? drag.startYRatio : drag.endYRatio;
|
|
167
|
+
const start = drawingPointFrom(scene, domain, drag.startXRatio, drag.startYRatio);
|
|
168
|
+
const end = drawingPointFrom(scene, domain, drag.endXRatio, endYRatio);
|
|
169
|
+
return start && end ? { id: shapedDrawingId(drag.kind, id), panelId: panel.id, points: [start, end], color } : null;
|
|
170
|
+
}
|
|
143
171
|
const source = drag.kind === "pencil" && drag.path.length > 1
|
|
144
172
|
? drag.path
|
|
145
173
|
: [
|
|
@@ -152,11 +180,74 @@ export function resolveDrawingFromDrag(
|
|
|
152
180
|
return points.length < 2 ? null : { id, panelId: panel.id, points, color };
|
|
153
181
|
}
|
|
154
182
|
|
|
183
|
+
function isShapedTool(kind: ChartToolKind): kind is typeof SHAPED_TOOLS[number] {
|
|
184
|
+
return (SHAPED_TOOLS as readonly string[]).includes(kind);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/** The id a drawing of this tool is saved under, see `chartDrawingShape`. */
|
|
188
|
+
export function shapedDrawingId(kind: ChartToolKind, id: string): string {
|
|
189
|
+
return isShapedTool(kind) ? `${kind}:${id}` : id;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
type RatioPoint = { xRatio: number; yRatio: number };
|
|
193
|
+
|
|
194
|
+
interface DrawingGeometry {
|
|
195
|
+
segments: Array<[RatioPoint, RatioPoint]>;
|
|
196
|
+
/** Corners of a filled box under the segments. */
|
|
197
|
+
box: [RatioPoint, RatioPoint] | null;
|
|
198
|
+
/** The points that can be grabbed and reshaped. */
|
|
199
|
+
anchors: RatioPoint[];
|
|
200
|
+
labels: Array<{ at: RatioPoint; text: string }>;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/** What a drawing paints, in plot ratios: shared by the bitmap, the vectors and the hit test. */
|
|
204
|
+
function drawingGeometry(
|
|
205
|
+
scene: Pick<CompositeChartScene, "timeScale">,
|
|
206
|
+
panel: CompositePanelScene,
|
|
207
|
+
drawing: ChartDrawing,
|
|
208
|
+
): DrawingGeometry | null {
|
|
209
|
+
const projected = projectDrawing(scene, panel, drawing);
|
|
210
|
+
if (!projected) return null;
|
|
211
|
+
const shape = chartDrawingShape(drawing);
|
|
212
|
+
if (shape === "line") {
|
|
213
|
+
return {
|
|
214
|
+
segments: projected.slice(1).map((point, index) => [projected[index]!, point]),
|
|
215
|
+
box: null,
|
|
216
|
+
anchors: projected.length === 2 ? projected : [],
|
|
217
|
+
labels: [],
|
|
218
|
+
};
|
|
219
|
+
}
|
|
220
|
+
const [start, end] = projected as [RatioPoint, RatioPoint];
|
|
221
|
+
if (shape === "ray") {
|
|
222
|
+
return { segments: [[start, { xRatio: 1, yRatio: start.yRatio }]], box: null, anchors: [start], labels: [] };
|
|
223
|
+
}
|
|
224
|
+
if (shape === "rect") {
|
|
225
|
+
const corner = (xRatio: number, yRatio: number) => ({ xRatio, yRatio });
|
|
226
|
+
const a = corner(start.xRatio, end.yRatio);
|
|
227
|
+
const b = corner(end.xRatio, start.yRatio);
|
|
228
|
+
return { segments: [[start, a], [a, end], [end, b], [b, start]], box: [start, end], anchors: [start, end], labels: [] };
|
|
229
|
+
}
|
|
230
|
+
const domain = resolveMeasureAxisDomain(panel)!;
|
|
231
|
+
const left = Math.min(start.xRatio, end.xRatio);
|
|
232
|
+
const right = Math.max(start.xRatio, end.xRatio);
|
|
233
|
+
const [from, to] = drawing.points as [ChartDrawingPoint, ChartDrawingPoint];
|
|
234
|
+
const segments: DrawingGeometry["segments"] = [];
|
|
235
|
+
const labels: DrawingGeometry["labels"] = [];
|
|
236
|
+
for (const level of FIB_LEVELS) {
|
|
237
|
+
const value = to.value + (from.value - to.value) * level;
|
|
238
|
+
const yRatio = projectCompositeValue(value, domain);
|
|
239
|
+
if (yRatio === null) continue;
|
|
240
|
+
segments.push([{ xRatio: left, yRatio }, { xRatio: right, yRatio }]);
|
|
241
|
+
labels.push({ at: { xRatio: left, yRatio }, text: `${level} ${formatCompositeCursorValue(value, domain)}` });
|
|
242
|
+
}
|
|
243
|
+
return { segments, box: null, anchors: [start, end], labels };
|
|
244
|
+
}
|
|
245
|
+
|
|
155
246
|
function projectDrawing(
|
|
156
247
|
scene: Pick<CompositeChartScene, "timeScale">,
|
|
157
248
|
panel: CompositePanelScene,
|
|
158
249
|
drawing: ChartDrawing,
|
|
159
|
-
):
|
|
250
|
+
): RatioPoint[] | null {
|
|
160
251
|
const domain = resolveMeasureAxisDomain(panel);
|
|
161
252
|
if (!domain) return null;
|
|
162
253
|
const projected: Array<{ xRatio: number; yRatio: number }> = [];
|
|
@@ -198,22 +289,21 @@ export function hitTestDrawings(
|
|
|
198
289
|
let best: { hit: ChartDrawingHit; distance: number; endpoint: boolean } | null = null;
|
|
199
290
|
const scale = Math.max(aspect, Number.EPSILON);
|
|
200
291
|
const target = { x: pointer.xRatio * scale, y: pointer.yRatio };
|
|
292
|
+
const toPlot = (point: RatioPoint) => ({ x: point.xRatio * scale, y: point.yRatio });
|
|
201
293
|
for (const drawing of drawings) {
|
|
202
294
|
if (drawing.panelId !== panel.id) continue;
|
|
203
|
-
const
|
|
204
|
-
if (!
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
best = { hit: { drawing, pointIndex: index }, distance, endpoint: true };
|
|
212
|
-
}
|
|
295
|
+
const geometry = drawingGeometry(scene, panel, drawing);
|
|
296
|
+
if (!geometry) continue;
|
|
297
|
+
for (const [index, anchor] of geometry.anchors.entries()) {
|
|
298
|
+
const point = toPlot(anchor);
|
|
299
|
+
const distance = Math.hypot(target.x - point.x, target.y - point.y);
|
|
300
|
+
if (distance > DRAWING_GRAB_RATIO) continue;
|
|
301
|
+
if (!best?.endpoint || best.distance > distance) {
|
|
302
|
+
best = { hit: { drawing, pointIndex: index }, distance, endpoint: true };
|
|
213
303
|
}
|
|
214
304
|
}
|
|
215
|
-
for (
|
|
216
|
-
const distance = distanceToSegment(target,
|
|
305
|
+
for (const [from, to] of geometry.segments) {
|
|
306
|
+
const distance = distanceToSegment(target, toPlot(from), toPlot(to));
|
|
217
307
|
if (distance > DRAWING_GRAB_RATIO || best?.endpoint) continue;
|
|
218
308
|
if (!best || best.distance > distance) {
|
|
219
309
|
best = { hit: { drawing, pointIndex: null }, distance, endpoint: false };
|
|
@@ -235,19 +325,18 @@ export function shiftDrawing(
|
|
|
235
325
|
const domain = resolveMeasureAxisDomain(panel);
|
|
236
326
|
const projected = projectDrawing(scene, panel, drawing);
|
|
237
327
|
if (!domain || !projected) return drawing;
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
};
|
|
328
|
+
const points = drawing.points.map((point, index) => {
|
|
329
|
+
if (pointIndex !== null && index !== pointIndex) return point;
|
|
330
|
+
const source = projected[index]!;
|
|
331
|
+
return drawingPointFrom(
|
|
332
|
+
scene,
|
|
333
|
+
domain,
|
|
334
|
+
clamp(source.xRatio + deltaXRatio, 0, 1),
|
|
335
|
+
clamp(source.yRatio + deltaYRatio, 0, 1),
|
|
336
|
+
) ?? point;
|
|
337
|
+
});
|
|
338
|
+
if (chartDrawingShape(drawing) === "ray") points[1] = { ...points[1]!, value: points[0]!.value };
|
|
339
|
+
return { ...drawing, points };
|
|
251
340
|
}
|
|
252
341
|
|
|
253
342
|
export function nextDrawingColor(color: string): string {
|
|
@@ -268,7 +357,7 @@ export function resolveChartToolKind(modifiers: {
|
|
|
268
357
|
}
|
|
269
358
|
|
|
270
359
|
export function isDrawingTool(tool: ChartToolKind | null): boolean {
|
|
271
|
-
return tool === "line" || tool === "pencil";
|
|
360
|
+
return tool === "line" || tool === "pencil" || (tool !== null && isShapedTool(tool));
|
|
272
361
|
}
|
|
273
362
|
|
|
274
363
|
function isMeaningfulToolDrag(drag: ChartToolDrag): boolean {
|
|
@@ -458,20 +547,29 @@ export function buildChartToolVectors(input: {
|
|
|
458
547
|
}): ChartVectorShape[] {
|
|
459
548
|
const shapes: ChartVectorShape[] = [];
|
|
460
549
|
for (const drawing of input.drawings) {
|
|
461
|
-
const projected = projectDrawing(input.scene, input.panel, drawing);
|
|
462
|
-
if (!projected) continue;
|
|
463
550
|
const selected = input.selectedId === drawing.id;
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
551
|
+
if (chartDrawingShape(drawing) === "line") {
|
|
552
|
+
const projected = projectDrawing(input.scene, input.panel, drawing);
|
|
553
|
+
if (!projected) continue;
|
|
554
|
+
shapes.push({
|
|
555
|
+
id: drawing.id,
|
|
556
|
+
points: projected.map((point) => ({ x: point.xRatio, y: point.yRatio })),
|
|
557
|
+
color: drawing.color,
|
|
558
|
+
strokeWidth: selected ? 2.4 : 1.6,
|
|
559
|
+
handles: selected && drawing.points.length === 2,
|
|
560
|
+
});
|
|
561
|
+
continue;
|
|
562
|
+
}
|
|
563
|
+
shapes.push(...shapedDrawingVectors(input.scene, input.panel, drawing, selected));
|
|
471
564
|
}
|
|
472
565
|
|
|
473
566
|
const drag = input.drag;
|
|
474
567
|
if (!drag) return shapes;
|
|
568
|
+
if (isShapedTool(drag.kind)) {
|
|
569
|
+
const preview = resolveDrawingFromDrag(input.scene, input.panel, drag, input.colors.draw, "tool:draw");
|
|
570
|
+
if (preview) shapes.push(...shapedDrawingVectors(input.scene, input.panel, preview, false));
|
|
571
|
+
return shapes;
|
|
572
|
+
}
|
|
475
573
|
const start = { x: drag.startXRatio, y: drag.startYRatio };
|
|
476
574
|
const end = { x: drag.endXRatio, y: drag.endYRatio };
|
|
477
575
|
if (drag.kind === "zoom") {
|
|
@@ -505,6 +603,55 @@ export function buildChartToolVectors(input: {
|
|
|
505
603
|
return shapes;
|
|
506
604
|
}
|
|
507
605
|
|
|
606
|
+
function shapedDrawingVectors(
|
|
607
|
+
scene: Pick<CompositeChartScene, "timeScale">,
|
|
608
|
+
panel: CompositePanelScene,
|
|
609
|
+
drawing: ChartDrawing,
|
|
610
|
+
selected: boolean,
|
|
611
|
+
): ChartVectorShape[] {
|
|
612
|
+
const geometry = drawingGeometry(scene, panel, drawing);
|
|
613
|
+
if (!geometry) return [];
|
|
614
|
+
const toVector = (point: RatioPoint) => ({ x: point.xRatio, y: point.yRatio });
|
|
615
|
+
const shapes: ChartVectorShape[] = [];
|
|
616
|
+
if (geometry.box) {
|
|
617
|
+
shapes.push({ id: `${drawing.id}:box`, points: geometry.box.map(toVector), color: drawing.color, box: true, fillOpacity: 0.12, strokeWidth: 0 });
|
|
618
|
+
}
|
|
619
|
+
geometry.segments.forEach((segment, index) => shapes.push({
|
|
620
|
+
id: `${drawing.id}:${index}`,
|
|
621
|
+
points: segment.map(toVector),
|
|
622
|
+
color: drawing.color,
|
|
623
|
+
strokeWidth: selected ? 2 : 1.3,
|
|
624
|
+
...(geometry.labels[index] ? { label: geometry.labels[index]!.text } : {}),
|
|
625
|
+
}));
|
|
626
|
+
if (selected) {
|
|
627
|
+
shapes.push({ id: `${drawing.id}:anchors`, points: geometry.anchors.map(toVector), color: drawing.color, opacity: 0, handles: true });
|
|
628
|
+
}
|
|
629
|
+
return shapes;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
/** Paints a shaped drawing's segments, box and handles onto a raster. */
|
|
633
|
+
function paintShapedDrawing(
|
|
634
|
+
data: Uint8Array,
|
|
635
|
+
width: number,
|
|
636
|
+
height: number,
|
|
637
|
+
geometry: DrawingGeometry,
|
|
638
|
+
color: RgbaColor,
|
|
639
|
+
selected: boolean,
|
|
640
|
+
): void {
|
|
641
|
+
const maxX = Math.max(width - 1, 0);
|
|
642
|
+
const maxY = Math.max(height - 1, 0);
|
|
643
|
+
if (geometry.box) {
|
|
644
|
+
const [a, b] = geometry.box;
|
|
645
|
+
fillRect(data, width, height, Math.min(a.xRatio, b.xRatio) * maxX, Math.min(a.yRatio, b.yRatio) * maxY,
|
|
646
|
+
Math.max(a.xRatio, b.xRatio) * maxX, Math.max(a.yRatio, b.yRatio) * maxY, color, 0.12);
|
|
647
|
+
}
|
|
648
|
+
for (const [from, to] of geometry.segments) {
|
|
649
|
+
drawLine(data, width, height, from.xRatio * maxX, from.yRatio * maxY, to.xRatio * maxX, to.yRatio * maxY, color, selected ? 2.2 : 1.4);
|
|
650
|
+
}
|
|
651
|
+
if (!selected) return;
|
|
652
|
+
for (const anchor of geometry.anchors) drawCircle(data, width, height, anchor.xRatio * maxX, anchor.yRatio * maxY, 3.2, color);
|
|
653
|
+
}
|
|
654
|
+
|
|
508
655
|
/**
|
|
509
656
|
* Paints drawings and the active tool on a copy of the panel raster. Copying
|
|
510
657
|
* keeps the cached plot bitmap intact and keeps a single bitmap layer, which is
|
|
@@ -529,10 +676,15 @@ export function drawChartToolOverlay(
|
|
|
529
676
|
const maxY = Math.max(height - 1, 0);
|
|
530
677
|
|
|
531
678
|
for (const drawing of drawings?.items ?? []) {
|
|
532
|
-
const shape = projectDrawing(drawings!.scene, drawings!.panel, drawing);
|
|
533
|
-
if (!shape) continue;
|
|
534
679
|
const selected = drawings!.selectedId === drawing.id;
|
|
535
680
|
const color = parseHex(drawing.color);
|
|
681
|
+
if (chartDrawingShape(drawing) !== "line") {
|
|
682
|
+
const geometry = drawingGeometry(drawings!.scene, drawings!.panel, drawing);
|
|
683
|
+
if (geometry) paintShapedDrawing(data, width, height, geometry, color, selected);
|
|
684
|
+
continue;
|
|
685
|
+
}
|
|
686
|
+
const shape = projectDrawing(drawings!.scene, drawings!.panel, drawing);
|
|
687
|
+
if (!shape) continue;
|
|
536
688
|
for (let index = 1; index < shape.length; index += 1) {
|
|
537
689
|
drawLine(
|
|
538
690
|
data,
|
|
@@ -560,6 +712,13 @@ export function drawChartToolOverlay(
|
|
|
560
712
|
const left = Math.min(x0, x1);
|
|
561
713
|
const right = Math.max(x0, x1);
|
|
562
714
|
|
|
715
|
+
if (isShapedTool(drag.kind)) {
|
|
716
|
+
const preview = drawings && resolveDrawingFromDrag(drawings.scene, drawings.panel, drag, colors.draw, "tool:draw");
|
|
717
|
+
const geometry = preview && drawingGeometry(drawings.scene, drawings.panel, preview);
|
|
718
|
+
if (geometry) paintShapedDrawing(data, width, height, geometry, parseHex(colors.draw), false);
|
|
719
|
+
return { width, height, pixels: data };
|
|
720
|
+
}
|
|
721
|
+
|
|
563
722
|
if (drag.kind === "line" || drag.kind === "pencil") {
|
|
564
723
|
const color = parseHex(colors.draw);
|
|
565
724
|
const trail = drag.kind === "pencil" && drag.path.length > 1
|
|
@@ -26,6 +26,7 @@ import {
|
|
|
26
26
|
resolveZoomTimeRange,
|
|
27
27
|
isDrawingTool,
|
|
28
28
|
nextDrawingColor,
|
|
29
|
+
shapedDrawingId,
|
|
29
30
|
type ChartDrawing,
|
|
30
31
|
type ChartDrawingPoint,
|
|
31
32
|
type ChartToolDrag,
|
|
@@ -301,7 +302,7 @@ export function useCompositeChartKeyboard({
|
|
|
301
302
|
const placement = keyboardPlacement;
|
|
302
303
|
setKeyboardPlacement(null);
|
|
303
304
|
// A placement that never left its anchor spans nothing, like a click.
|
|
304
|
-
if (!placement || placement.start.time === placement.end.time) return;
|
|
305
|
+
if (!placement || (placement.start.time === placement.end.time && placement.kind !== "ray")) return;
|
|
305
306
|
if (placement.kind === "zoom") {
|
|
306
307
|
if (!navigationFrame) return;
|
|
307
308
|
const range = resolveZoomTimeRange(placement.start.time, placement.end.time, navigationFrame.minimumSpanMs);
|
|
@@ -313,9 +314,9 @@ export function useCompositeChartKeyboard({
|
|
|
313
314
|
? placement.path.filter((point, index, path) => (
|
|
314
315
|
index === 0 || point.time !== path[index - 1]!.time || point.value !== path[index - 1]!.value
|
|
315
316
|
))
|
|
316
|
-
: [placement.start, placement.end];
|
|
317
|
+
: [placement.start, placement.kind === "ray" ? { ...placement.end, value: placement.start.value } : placement.end];
|
|
317
318
|
if (points.length < 2) return;
|
|
318
|
-
addDrawing({ id: nextDrawingId(), panelId: placement.panelId, points, color: drawColor });
|
|
319
|
+
addDrawing({ id: shapedDrawingId(placement.kind, nextDrawingId()), panelId: placement.panelId, points, color: drawColor });
|
|
319
320
|
};
|
|
320
321
|
const stepLegend = (direction: -1 | 1) => {
|
|
321
322
|
onActivate?.();
|
|
@@ -566,7 +567,10 @@ export function useCompositeChartKeyboard({
|
|
|
566
567
|
|| ((interaction === "arm-measure"
|
|
567
568
|
|| interaction === "arm-zoom"
|
|
568
569
|
|| interaction === "arm-line"
|
|
569
|
-
|| interaction === "arm-pencil"
|
|
570
|
+
|| interaction === "arm-pencil"
|
|
571
|
+
|| interaction === "arm-ray"
|
|
572
|
+
|| interaction === "arm-rect"
|
|
573
|
+
|| interaction === "arm-fib") && !scene)
|
|
570
574
|
|| (interaction === "arm-level" && !levelsEditable)
|
|
571
575
|
|| (interaction === "delete-drawing" && !canDeleteDrawing)
|
|
572
576
|
|| (interaction === "cycle-colour" && !isDrawingTool(armedTool) && !selectedDrawingId)
|
|
@@ -586,6 +590,9 @@ export function useCompositeChartKeyboard({
|
|
|
586
590
|
case "arm-zoom":
|
|
587
591
|
case "arm-line":
|
|
588
592
|
case "arm-pencil":
|
|
593
|
+
case "arm-ray":
|
|
594
|
+
case "arm-rect":
|
|
595
|
+
case "arm-fib":
|
|
589
596
|
case "arm-level":
|
|
590
597
|
onActivate?.();
|
|
591
598
|
armTool(ARMED_TOOL_BY_INTERACTION[interaction]);
|
|
@@ -119,7 +119,8 @@ export function buildAssistCommandInventory({
|
|
|
119
119
|
prefixes: normalizePrefixes(command.shortcut ? [command.shortcut] : []),
|
|
120
120
|
})),
|
|
121
121
|
...paneTemplates.map((template) => ({
|
|
122
|
-
|
|
122
|
+
// An unlisted function keeps its codes, so nothing else takes them, but is never suggested.
|
|
123
|
+
descriptor: template.listed === false ? null : describe(
|
|
123
124
|
template.shortcut?.prefix ?? "",
|
|
124
125
|
getPaneTemplateDisplayLabel(template),
|
|
125
126
|
template.shortcut?.aliases?.length ? `${template.description} Also ${template.shortcut.aliases.join(", ")}.` : template.description,
|
|
@@ -245,19 +245,34 @@ export function runDirectCommandAction(options: {
|
|
|
245
245
|
}
|
|
246
246
|
case "import-config": {
|
|
247
247
|
const importPath = getDefaultConfigBackupPath();
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
248
|
+
// An import replaces every layout, portfolio and setting at once, so it
|
|
249
|
+
// is never one keystroke away.
|
|
250
|
+
openInlineConfirm({
|
|
251
|
+
confirmId: "import-config",
|
|
252
|
+
title: "Import Config",
|
|
253
|
+
body: [
|
|
254
|
+
`This replaces your layouts, portfolios, watchlists and settings with the ones saved in ${importPath}.`,
|
|
255
|
+
"What you have now is not kept unless you export it first.",
|
|
256
|
+
],
|
|
257
|
+
confirmLabel: "Import",
|
|
258
|
+
cancelLabel: "Back",
|
|
259
|
+
tone: "danger",
|
|
260
|
+
// A stray Enter keeps what is there.
|
|
261
|
+
defaultAction: "cancel",
|
|
262
|
+
onConfirm: async () => {
|
|
263
|
+
try {
|
|
264
|
+
const imported = await importConfig(getState().config.dataDir, importPath);
|
|
265
|
+
cancelThemePreview();
|
|
266
|
+
clearTransientThemePreview();
|
|
267
|
+
dispatch({ type: "SET_CONFIG", config: imported });
|
|
268
|
+
applyTheme(imported.theme);
|
|
269
|
+
dispatch({ type: "SET_THEME", theme: imported.theme });
|
|
270
|
+
notify(`Imported config from ${importPath}.`, { type: "success" });
|
|
271
|
+
} catch (error) {
|
|
272
|
+
notify(error instanceof Error ? error.message : "Import failed.", { type: "error" });
|
|
273
|
+
}
|
|
274
|
+
},
|
|
275
|
+
});
|
|
261
276
|
return;
|
|
262
277
|
}
|
|
263
278
|
case "cycle-chart-renderer": {
|
|
@@ -73,6 +73,11 @@ export interface ResultItem {
|
|
|
73
73
|
action: () => void | Promise<void>;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/** A row plain Enter can run, so the untouched selection may rest on it. */
|
|
77
|
+
export function isDefaultSelectable(item: ResultItem): boolean {
|
|
78
|
+
return item.disabled !== true && item.defaultSelectable !== false;
|
|
79
|
+
}
|
|
80
|
+
|
|
76
81
|
type ListScreenKind = "root" | "mode" | "picker";
|
|
77
82
|
|
|
78
83
|
export interface ListScreenState {
|
|
@@ -260,8 +260,9 @@ export function buildPaneShortcutItems(options: {
|
|
|
260
260
|
showShortcut?: boolean;
|
|
261
261
|
}) => ResultItem;
|
|
262
262
|
}): ResultItem[] {
|
|
263
|
+
// An unlisted template (`listed: false`) still opens when its code is typed, which is resolved apart from these rows.
|
|
263
264
|
const items = options.templates
|
|
264
|
-
.filter((template) => template.shortcut && template.shortcut.showInBrowse !== false)
|
|
265
|
+
.filter((template) => template.shortcut && template.shortcut.showInBrowse !== false && template.listed !== false)
|
|
265
266
|
.map((template) => options.createItem(template, {
|
|
266
267
|
category: "Panes",
|
|
267
268
|
createOptions: options.createOptions,
|
|
@@ -279,7 +280,7 @@ export function buildNonShortcutPaneTemplateItems(options: {
|
|
|
279
280
|
createItem: (template: PaneTemplateDef, options?: { category?: string }) => ResultItem;
|
|
280
281
|
}): ResultItem[] {
|
|
281
282
|
const items = options.templates
|
|
282
|
-
.filter((template) => !template.shortcut)
|
|
283
|
+
.filter((template) => !template.shortcut && template.listed !== false)
|
|
283
284
|
.map((template) => options.createItem(template, { category: "Panes" }));
|
|
284
285
|
|
|
285
286
|
return options.filterQuery
|
|
@@ -7,7 +7,7 @@ import type { TickerRecord } from "../../../../types/ticker";
|
|
|
7
7
|
import { listingChoiceQuery, symbolSearchQuery, type TickerSearchCandidate } from "../../../../tickers/search";
|
|
8
8
|
import type { AssistRowHandlers } from "../../assist/model";
|
|
9
9
|
import { matchPrefix, type Command } from "../../commands/registry";
|
|
10
|
-
import type
|
|
10
|
+
import { isDefaultSelectable, type ResultItem } from "../../list/model";
|
|
11
11
|
import type { CommandBarCategoryPriorities } from "../../view-model";
|
|
12
12
|
import type { CommandBarRoute } from "../../workflow/types";
|
|
13
13
|
import { normalizeCommandTickerSearchText, readRootTickerQuery } from "../ticker-search/results";
|
|
@@ -20,11 +20,6 @@ import { buildRootShortcutFeedback } from "./shortcut-feedback";
|
|
|
20
20
|
import type { ShortcutIntent } from "./shortcuts";
|
|
21
21
|
import { clampIndex } from "../../../../utils/math";
|
|
22
22
|
|
|
23
|
-
/** A row plain Enter can run, so the untouched selection may rest on it. */
|
|
24
|
-
function isDefaultSelectable(item: ResultItem): boolean {
|
|
25
|
-
return item.disabled !== true && item.defaultSelectable !== false;
|
|
26
|
-
}
|
|
27
|
-
|
|
28
23
|
interface UseCommandBarRootRuntimeOptions {
|
|
29
24
|
activeCollectionId: string | null;
|
|
30
25
|
activePortfolio?: AppState["config"]["portfolios"][number];
|
|
@@ -343,8 +338,12 @@ export function useCommandBarRootRuntime({
|
|
|
343
338
|
if (shiftedIdx >= 0) return shiftedIdx;
|
|
344
339
|
return clampIndex(current, resultIds.length);
|
|
345
340
|
}
|
|
346
|
-
// Untouched, the selection follows the best row on offer.
|
|
341
|
+
// Untouched, the selection follows the best row on offer. When every row
|
|
342
|
+
// is an offer or a placeholder, none of them is the answer and none is
|
|
343
|
+
// selected: Enter must not take one of them for the ticker still being
|
|
344
|
+
// searched.
|
|
347
345
|
const defaultIdx = orderedRootResults.findIndex(isDefaultSelectable);
|
|
346
|
+
if (defaultIdx < 0) return -1;
|
|
348
347
|
return clampIndex(Math.max(rootResultModel.initialIdx, defaultIdx), resultIds.length);
|
|
349
348
|
});
|
|
350
349
|
}, [
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { useCallback, type MutableRefObject } from "react";
|
|
1
|
+
import { useCallback, useEffect, useRef, type MutableRefObject } from "react";
|
|
2
2
|
import type { AppState } from "../../state/app/context";
|
|
3
3
|
import type { TickerRecord } from "../../types/ticker";
|
|
4
4
|
import { t } from "../../i18n";
|
|
@@ -12,7 +12,7 @@ import type {
|
|
|
12
12
|
PaneTemplateDef,
|
|
13
13
|
} from "../../types/plugin";
|
|
14
14
|
import { resolveCommandBarMode, type CommandBarMode } from "./view-model";
|
|
15
|
-
import type
|
|
15
|
+
import { isDefaultSelectable, type ListScreenState, type ResultItem } from "./list/model";
|
|
16
16
|
import type { CommandBarRoute } from "./workflow/types";
|
|
17
17
|
import type { CollectionCommandId } from "./helpers";
|
|
18
18
|
import type { CommandBarCollectionWorkflowActions } from "./workflow/collection-actions";
|
|
@@ -67,6 +67,7 @@ interface UseCommandBarSelectionRuntimeOptions {
|
|
|
67
67
|
rootModeKindRef: MutableRefObject<CommandBarMode>;
|
|
68
68
|
rootQuery: string;
|
|
69
69
|
rootQueryRef: MutableRefObject<string>;
|
|
70
|
+
rootSelectedIdx: number;
|
|
70
71
|
rootSelectionNavigatedRef: MutableRefObject<boolean>;
|
|
71
72
|
rootShortcutIntent: ShortcutIntent;
|
|
72
73
|
rootThemeBaseIdRef: MutableRefObject<string | null>;
|
|
@@ -78,6 +79,11 @@ interface UseCommandBarSelectionRuntimeOptions {
|
|
|
78
79
|
*/
|
|
79
80
|
runRootRow?: RunRootRow;
|
|
80
81
|
runSecurityDescriptionShortcut: (query?: string, options?: { forceNewPane?: boolean }) => void | Promise<void>;
|
|
82
|
+
/**
|
|
83
|
+
* The search that decides the best row is still out, so the list on screen
|
|
84
|
+
* is a stand-in for the answer.
|
|
85
|
+
*/
|
|
86
|
+
searchPending: boolean;
|
|
81
87
|
setRootQuery: (query: string) => void;
|
|
82
88
|
stateConfigLayout: AppState["config"]["layout"];
|
|
83
89
|
stateRef: MutableRefObject<AppState>;
|
|
@@ -108,12 +114,14 @@ export function useCommandBarSelectionRuntime({
|
|
|
108
114
|
rootModeKindRef,
|
|
109
115
|
rootQuery,
|
|
110
116
|
rootQueryRef,
|
|
117
|
+
rootSelectedIdx,
|
|
111
118
|
rootSelectionNavigatedRef,
|
|
112
119
|
rootShortcutIntent,
|
|
113
120
|
rootThemeBaseIdRef,
|
|
114
121
|
runDirectCommand,
|
|
115
122
|
runRootRow,
|
|
116
123
|
runSecurityDescriptionShortcut,
|
|
124
|
+
searchPending,
|
|
117
125
|
setRootQuery,
|
|
118
126
|
stateConfigLayout,
|
|
119
127
|
stateRef,
|
|
@@ -276,6 +284,13 @@ export function useCommandBarSelectionRuntime({
|
|
|
276
284
|
updateTopRoute,
|
|
277
285
|
]);
|
|
278
286
|
|
|
287
|
+
/**
|
|
288
|
+
* An Enter pressed with no row to run while the search is still out. It
|
|
289
|
+
* waits for the answer instead of running whatever else the list shows, and
|
|
290
|
+
* is dropped when the user types, moves the selection or leaves the root.
|
|
291
|
+
*/
|
|
292
|
+
const pendingEnterRef = useRef<{ query: string; secondary: boolean } | null>(null);
|
|
293
|
+
|
|
279
294
|
const activateListSelection = useCallback((options?: { secondary?: boolean; item?: ResultItem }) => {
|
|
280
295
|
const listState = visibleListStateRef.current;
|
|
281
296
|
if (!listState) return;
|
|
@@ -311,7 +326,13 @@ export function useCommandBarSelectionRuntime({
|
|
|
311
326
|
secondaryAction: () => runSecurityDescriptionShortcut(symbol, { forceNewPane: true }) };
|
|
312
327
|
}
|
|
313
328
|
const selected = options?.item ?? typed ?? listState.results[listState.selectedIdx];
|
|
314
|
-
if (!selected
|
|
329
|
+
if (!selected) {
|
|
330
|
+
if (searchPending && !currentRoute && !options?.item && !rootSelectionNavigatedRef.current) {
|
|
331
|
+
pendingEnterRef.current = { query: rootQueryRef.current, secondary: options?.secondary === true };
|
|
332
|
+
}
|
|
333
|
+
return;
|
|
334
|
+
}
|
|
335
|
+
if (selected.disabled) return;
|
|
315
336
|
|
|
316
337
|
// Opened from an add row: a venue picked from the colon list joins that
|
|
317
338
|
// collection. Any other text or screen runs as usual.
|
|
@@ -396,11 +417,39 @@ export function useCommandBarSelectionRuntime({
|
|
|
396
417
|
rootShortcutIntent,
|
|
397
418
|
runRootRow,
|
|
398
419
|
runSecurityDescriptionShortcut,
|
|
420
|
+
searchPending,
|
|
399
421
|
stateConfigLayout,
|
|
400
422
|
stateRef,
|
|
401
423
|
visibleListStateRef,
|
|
402
424
|
]);
|
|
403
425
|
|
|
426
|
+
useEffect(() => {
|
|
427
|
+
const pending = pendingEnterRef.current;
|
|
428
|
+
if (!pending) return;
|
|
429
|
+
if (currentRoute || rootQueryRef.current !== pending.query || rootSelectionNavigatedRef.current) {
|
|
430
|
+
pendingEnterRef.current = null;
|
|
431
|
+
return;
|
|
432
|
+
}
|
|
433
|
+
const listState = visibleListStateRef.current;
|
|
434
|
+
if (!listState?.results.some(isDefaultSelectable)) {
|
|
435
|
+
// Still searching: the answer may bring a row. Answered with none: there is nothing to run.
|
|
436
|
+
if (!searchPending) pendingEnterRef.current = null;
|
|
437
|
+
return;
|
|
438
|
+
}
|
|
439
|
+
// The selection follows the rows in the render after they land.
|
|
440
|
+
if (!listState.results[listState.selectedIdx]) return;
|
|
441
|
+
pendingEnterRef.current = null;
|
|
442
|
+
activateListSelection({ secondary: pending.secondary });
|
|
443
|
+
}, [
|
|
444
|
+
activateListSelection,
|
|
445
|
+
currentRoute,
|
|
446
|
+
rootQueryRef,
|
|
447
|
+
rootSelectedIdx,
|
|
448
|
+
rootSelectionNavigatedRef,
|
|
449
|
+
searchPending,
|
|
450
|
+
visibleListStateRef,
|
|
451
|
+
]);
|
|
452
|
+
|
|
404
453
|
return {
|
|
405
454
|
acceptRootShortcutTab,
|
|
406
455
|
acceptSelectedShortcutTab,
|
|
@@ -532,12 +532,14 @@ export function CommandBar({
|
|
|
532
532
|
rootModeKindRef,
|
|
533
533
|
rootQuery,
|
|
534
534
|
rootQueryRef,
|
|
535
|
+
rootSelectedIdx,
|
|
535
536
|
rootSelectionNavigatedRef,
|
|
536
537
|
rootShortcutIntent,
|
|
537
538
|
rootThemeBaseIdRef,
|
|
538
539
|
runDirectCommand,
|
|
539
540
|
runRootRow,
|
|
540
541
|
runSecurityDescriptionShortcut,
|
|
542
|
+
searchPending: rootSearching || !rootResultsCurrent,
|
|
541
543
|
setRootQuery,
|
|
542
544
|
stateConfigLayout: state.config.layout,
|
|
543
545
|
stateRef,
|