@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
|
@@ -36,7 +36,7 @@ import { resolvePortfolioTotalsCurrency } from "../../../portfolio/totals-curren
|
|
|
36
36
|
import { createBaseConverter } from "../../../cli/base-converter";
|
|
37
37
|
import { heldCurrencyUnit } from "../../../utils/currency-units";
|
|
38
38
|
import { positionCostTermsPrefill } from "./portfolio-position";
|
|
39
|
-
import {
|
|
39
|
+
import { ambiguousQuantityProblem } from "../../../portfolio/position-quantity";
|
|
40
40
|
import type { CommandBarFieldValue } from "./types";
|
|
41
41
|
import type { WorkflowStringValues } from "./broker";
|
|
42
42
|
import { coerceFieldString } from "../helpers";
|
|
@@ -259,8 +259,8 @@ export function createCommandBarCollectionWorkflowActions(options: {
|
|
|
259
259
|
// Negative for a short; zero is no position. A future counts contracts, and the messages say so.
|
|
260
260
|
const quantityLabel = positionQuantityLabel(ticker);
|
|
261
261
|
const shares = readTypedNumber(coerceFieldString(values.shares), quantityLabel);
|
|
262
|
-
// 1.250
|
|
263
|
-
const lots =
|
|
262
|
+
// 1.250 could be 1,250 as well, and asks the same way.
|
|
263
|
+
const lots = ambiguousQuantityProblem(coerceFieldString(values.shares).trim(), ticker, { label: quantityLabel });
|
|
264
264
|
if (lots) throw new Error(lots);
|
|
265
265
|
const sharesError = validatePositionShares(shares, quantityLabel);
|
|
266
266
|
if (sharesError) {
|
|
@@ -28,10 +28,10 @@ export function usePaneCaptureStamp(paneId: string): string | null {
|
|
|
28
28
|
|
|
29
29
|
export function captureStampFooter(footer: CombinedPaneFooter, text: string | null): CombinedPaneFooter {
|
|
30
30
|
if (!text) return footer;
|
|
31
|
-
//
|
|
31
|
+
// The footer sets it apart from what the pane says about its data, which may itself be a time.
|
|
32
32
|
const segment: PaneFooterSegment = {
|
|
33
33
|
id: "capture-stamp",
|
|
34
|
-
parts: [
|
|
34
|
+
parts: [{ text, tone: "label" }],
|
|
35
35
|
};
|
|
36
36
|
return {
|
|
37
37
|
...footer,
|
|
@@ -11,7 +11,9 @@ import { useOptionalDialog, type PromptContext } from "../../../../ui/dialog";
|
|
|
11
11
|
import { nativePaneFooterRows } from "../sizing";
|
|
12
12
|
import {
|
|
13
13
|
EMPTY_FOOTER,
|
|
14
|
+
PANE_FOOTER_SEPARATOR,
|
|
14
15
|
fitPaneFooterInfo,
|
|
16
|
+
separatesPaneFooterSegments,
|
|
15
17
|
hasPaneFooterContent,
|
|
16
18
|
layoutPaneFooterHintRow,
|
|
17
19
|
totalHintsWidth,
|
|
@@ -22,6 +24,7 @@ import {
|
|
|
22
24
|
} from "./model";
|
|
23
25
|
|
|
24
26
|
export {
|
|
27
|
+
companyDataFooterSegment,
|
|
25
28
|
datesPaneData,
|
|
26
29
|
DELAYED_SEGMENT,
|
|
27
30
|
delayedFooterSegment,
|
|
@@ -267,6 +270,7 @@ function FooterContent({
|
|
|
267
270
|
// A segment with shorter forms takes the longest that fits beside the hints, gaps and terminal keys included.
|
|
268
271
|
const keysWidth = nativePaneChrome ? 0 : footer.info.reduce((total, segment) => total + terminalSegmentKey(segment, focused).length, 0);
|
|
269
272
|
const info = infoWidth != null ? fitPaneFooterInfo(footer.info, infoWidth - keysWidth, { besideHints: hasHints }) : footer.info;
|
|
273
|
+
const lastTextIndex = info.findLastIndex((segment) => !segment.icon);
|
|
270
274
|
|
|
271
275
|
if (!hasInfo && !hasHints) {
|
|
272
276
|
return <Box flexGrow={1} height={1} />;
|
|
@@ -291,12 +295,24 @@ function FooterContent({
|
|
|
291
295
|
flexShrink={1}
|
|
292
296
|
{...(infoWidth != null ? { width: infoWidth } : {})}
|
|
293
297
|
>
|
|
294
|
-
{info.map((segment, index) =>
|
|
295
|
-
//
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
298
|
+
{info.map((segment, index) => {
|
|
299
|
+
// Every pane's segments are set apart the same way, so two never read as one sentence.
|
|
300
|
+
const separated = index > 0 && separatesPaneFooterSegments(info[index - 1]!, segment);
|
|
301
|
+
return (
|
|
302
|
+
<Box
|
|
303
|
+
key={segment.id}
|
|
304
|
+
flexDirection="row"
|
|
305
|
+
overflow="hidden"
|
|
306
|
+
// A row too narrow cuts from its end: the last text segment gives way, then the row clips the
|
|
307
|
+
// one before it. An icon is a few cells that mean nothing cut, and earlier text says more.
|
|
308
|
+
flexShrink={!segment.icon && index === lastTextIndex ? 1 : 0}
|
|
309
|
+
marginLeft={index > 0 && !separated ? 1 : 0}
|
|
310
|
+
>
|
|
311
|
+
{separated && <Box flexShrink={0}><Text fg={colors.textMuted}>{PANE_FOOTER_SEPARATOR}</Text></Box>}
|
|
312
|
+
<SegmentView segment={segment} focused={focused} />
|
|
313
|
+
</Box>
|
|
314
|
+
);
|
|
315
|
+
})}
|
|
300
316
|
</Box>
|
|
301
317
|
)}
|
|
302
318
|
{hasHints && (
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ContextMenuItem } from "../../../../types/context-menu";
|
|
2
|
-
import { t } from "../../../../i18n";
|
|
2
|
+
import { t, tf } from "../../../../i18n";
|
|
3
|
+
import { type CompanyFrom, companyFromLabel } from "../../../../types/company-from";
|
|
3
4
|
import { clipToDisplayWidth, displayWidth } from "../../../../utils/format";
|
|
4
5
|
import { formatShortDate } from "../../../../utils/datetime-format";
|
|
5
6
|
import { getShortcutHintWidth } from "../../../ui/shortcut-hint";
|
|
@@ -44,8 +45,9 @@ export interface PaneFooterSegment {
|
|
|
44
45
|
*/
|
|
45
46
|
quoteStatus?: boolean;
|
|
46
47
|
/**
|
|
47
|
-
* The segment says what the rows leave out (releases a filter hides),
|
|
48
|
-
*
|
|
48
|
+
* The segment says what the rows leave out (releases a filter hides), or
|
|
49
|
+
* whose they are (a receipt's company data from its home line), which a
|
|
50
|
+
* picture of the rows cannot: a `gloom shot` keeps it, as it keeps a
|
|
49
51
|
* warning.
|
|
50
52
|
*/
|
|
51
53
|
capture?: boolean;
|
|
@@ -117,6 +119,26 @@ export function savedCopyFooterSegment(id: string, savedAt: number, now = Date.n
|
|
|
117
119
|
};
|
|
118
120
|
}
|
|
119
121
|
|
|
122
|
+
/**
|
|
123
|
+
* The footer's one form for company data a pane shows from another listing
|
|
124
|
+
* of the company: a receipt or a foreign line (SKHY, HY9H in Frankfurt) reads
|
|
125
|
+
* its home line's news, filings or holders, `company data from 000660 KRX`.
|
|
126
|
+
* Null when the data is the listing's own.
|
|
127
|
+
*/
|
|
128
|
+
export function companyDataFooterSegment(from: CompanyFrom | null | undefined): PaneFooterSegment | null {
|
|
129
|
+
if (!from) return null;
|
|
130
|
+
const listing = companyFromLabel(from);
|
|
131
|
+
const text = tf("company data from {listing}", { listing });
|
|
132
|
+
return {
|
|
133
|
+
id: "company-data",
|
|
134
|
+
title: from.name ? `${text} (${from.name})` : text,
|
|
135
|
+
// A capture of the rows keeps it: the picture cannot say whose they are.
|
|
136
|
+
capture: true,
|
|
137
|
+
parts: [{ text, tone: "muted" }],
|
|
138
|
+
variants: [[{ text: tf("from {listing}", { listing }), tone: "muted" }]],
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
120
142
|
/** A pane whose market data is all delayed, by no one figure: the clock alone, its tooltip saying so. */
|
|
121
143
|
export const DELAYED_SEGMENT: PaneFooterSegment = delayedFooterSegment({ id: "delayed", detail: "Delayed market data" });
|
|
122
144
|
|
|
@@ -202,6 +224,25 @@ const INFO_FLOOR_CHARS = 10;
|
|
|
202
224
|
const WARNING_FLOOR_CHARS = 24;
|
|
203
225
|
const HINT_GAP = 1;
|
|
204
226
|
|
|
227
|
+
/**
|
|
228
|
+
* What the footer draws between two info segments, muted, as a status draws
|
|
229
|
+
* between its own parts ("Newest story Mon 14:23 · 12h delayed"): two
|
|
230
|
+
* segments side by side never read as one sentence ("company data from PBR ·
|
|
231
|
+
* as of 2026-06-30"). An icon (`⚠[!]`, `◷15m`) is a mark, not a phrase: a
|
|
232
|
+
* column sets it apart.
|
|
233
|
+
*/
|
|
234
|
+
export const PANE_FOOTER_SEPARATOR = " · ";
|
|
235
|
+
const SEPARATOR_WIDTH = displayWidth(PANE_FOOTER_SEPARATOR);
|
|
236
|
+
|
|
237
|
+
/** Whether the footer draws {@link PANE_FOOTER_SEPARATOR} between two adjacent segments, else a one-column gap. */
|
|
238
|
+
export function separatesPaneFooterSegments(before: Pick<PaneFooterSegment, "icon">, after: Pick<PaneFooterSegment, "icon">): boolean {
|
|
239
|
+
return !before.icon && !after.icon;
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
function segmentGapWidth(before: PaneFooterSegment, after: PaneFooterSegment): number {
|
|
243
|
+
return separatesPaneFooterSegments(before, after) ? SEPARATOR_WIDTH : 1;
|
|
244
|
+
}
|
|
245
|
+
|
|
205
246
|
function paneHintWidth(hint: Pick<PaneHint, "key" | "label">, prefix = ""): number {
|
|
206
247
|
return getShortcutHintWidth(hint.key, hint.label, prefix);
|
|
207
248
|
}
|
|
@@ -216,15 +257,12 @@ function infoTextWidth(segments: readonly PaneFooterSegment[]): number {
|
|
|
216
257
|
const text = segment.parts.reduce((sum, part, partIndex) => (
|
|
217
258
|
sum + (partIndex > 0 ? 1 : 0) + displayWidth(part.text)
|
|
218
259
|
), 0);
|
|
219
|
-
return total + (index > 0 ? 1 : 0) + text;
|
|
260
|
+
return total + (index > 0 ? segmentGapWidth(segments[index - 1]!, segment) : 0) + text;
|
|
220
261
|
}, 0);
|
|
221
262
|
}
|
|
222
263
|
|
|
223
|
-
/**
|
|
224
|
-
|
|
225
|
-
* together, one variant at a time, until the row fits or none is left.
|
|
226
|
-
*/
|
|
227
|
-
export function fitPaneFooterInfo(info: PaneFooterSegment[], width: number, { besideHints = true }: { besideHints?: boolean } = {}): PaneFooterSegment[] {
|
|
264
|
+
/** Segments with `variants` step down together, one variant at a time, until they fit or none is left. */
|
|
265
|
+
function stepDownTogether(info: PaneFooterSegment[], width: number): PaneFooterSegment[] {
|
|
228
266
|
const stages = Math.max(0, ...info.map((segment) => segment.variants?.length ?? 0));
|
|
229
267
|
let fitted = info;
|
|
230
268
|
for (let stage = 0; stage < stages && infoTextWidth(fitted) > width; stage += 1) {
|
|
@@ -237,20 +275,50 @@ export function fitPaneFooterInfo(info: PaneFooterSegment[], width: number, { be
|
|
|
237
275
|
} : segment;
|
|
238
276
|
});
|
|
239
277
|
}
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
278
|
+
return fitted;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* A trailing status cut mid-word reads as part of the hints beside it ("quote
|
|
283
|
+
* 12h t[x] put"): one that does not fit ends in an ellipsis a column short of
|
|
284
|
+
* them, or is left out when less than half of it would show, until the row
|
|
285
|
+
* fits or `keep` segments are left. A warning, an icon or a pressable segment
|
|
286
|
+
* stays as it is, clipped by the row if it must be.
|
|
287
|
+
*/
|
|
288
|
+
function trimTrailing(info: PaneFooterSegment[], width: number, keep: number): PaneFooterSegment[] {
|
|
289
|
+
let fitted = info;
|
|
290
|
+
while (fitted.length > keep && infoTextWidth(fitted) > width) {
|
|
245
291
|
const last = fitted.at(-1)!;
|
|
246
292
|
if (last.icon || last.onPress || last.parts.some((part) => part.tone === "warning" || part.tone === "negative")) break;
|
|
247
|
-
|
|
293
|
+
// After the separator, a column short of the hints.
|
|
294
|
+
const room = width - infoTextWidth(fitted.slice(0, -1)) - segmentGapWidth(fitted.at(-2)!, last) - 1;
|
|
248
295
|
if (room * 2 >= infoTextWidth([last])) return [...fitted.slice(0, -1), clipSegment(last, room)];
|
|
249
296
|
fitted = fitted.slice(0, -1);
|
|
250
297
|
}
|
|
251
298
|
return fitted;
|
|
252
299
|
}
|
|
253
300
|
|
|
301
|
+
/**
|
|
302
|
+
* The info segments as they fit `width`: segments with `variants` step down
|
|
303
|
+
* together, one variant at a time, until the row fits or none is left; beside
|
|
304
|
+
* the hints, the trailing status that still does not fit is then cut or left
|
|
305
|
+
* out (trimTrailing). A status after every segment with shorter forms gives
|
|
306
|
+
* way first, before any of them shortens: a pane puts what goes first last
|
|
307
|
+
* (an update time after a note that explains the row in hand).
|
|
308
|
+
*
|
|
309
|
+
* With no hints beside it (a pane picture's footer), the row clips the rest,
|
|
310
|
+
* so a frame that fits its own status line sees it overflow.
|
|
311
|
+
*/
|
|
312
|
+
export function fitPaneFooterInfo(info: PaneFooterSegment[], width: number, { besideHints = true }: { besideHints?: boolean } = {}): PaneFooterSegment[] {
|
|
313
|
+
if (!besideHints) return stepDownTogether(info, width);
|
|
314
|
+
const lastWithVariants = info.findLastIndex((segment) => (segment.variants?.length ?? 0) > 0);
|
|
315
|
+
if (lastWithVariants >= 0 && lastWithVariants < info.length - 1 && infoTextWidth(info) > width) {
|
|
316
|
+
const trimmed = trimTrailing(info, width, lastWithVariants + 1);
|
|
317
|
+
if (infoTextWidth(trimmed) <= width) return trimmed;
|
|
318
|
+
}
|
|
319
|
+
return trimTrailing(stepDownTogether(info, width), width, 1);
|
|
320
|
+
}
|
|
321
|
+
|
|
254
322
|
/** A segment cut to `width` behind an ellipsis, each part keeping its tone; the full text stays as its title. */
|
|
255
323
|
function clipSegment(segment: PaneFooterSegment, width: number): PaneFooterSegment {
|
|
256
324
|
const full = segment.parts.map((part) => part.text).join(" ");
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
2
|
-
import { usePaneFooter, type PaneFooterSegment, type PaneHint } from "./footer";
|
|
1
|
+
import { useId, useMemo } from "react";
|
|
2
|
+
import { companyDataFooterSegment, usePaneFooter, type PaneFooterSegment, type PaneHint } from "./footer";
|
|
3
|
+
import type { CompanyFrom } from "../../../types/company-from";
|
|
4
|
+
import { useAppLanguage } from "../../../i18n/react";
|
|
3
5
|
import { useExternalLinkFooter } from "../../use-external-link-footer";
|
|
4
6
|
import { loadingErrorFooterInfo } from "../../data-table/table-pane";
|
|
5
7
|
|
|
@@ -28,6 +30,37 @@ function buildPaneStatusInfo({
|
|
|
28
30
|
return [...info, ...(stale ? [STALE_SEGMENT] : []), ...loadingErrorFooterInfo(loading, error)];
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
/**
|
|
34
|
+
* Where the company data note sits: after the warning indicator (-10) and
|
|
35
|
+
* before the pane's own status (0 and the ticker quote's -1), so it leads what
|
|
36
|
+
* the pane says about its data and a narrow footer cuts that status first.
|
|
37
|
+
*/
|
|
38
|
+
const COMPANY_DATA_ORDER = -5;
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* A company pane on a receipt or a foreign line that shows its company's data
|
|
42
|
+
* from the home line says which: `company data from 000660 KRX · as of
|
|
43
|
+
* 2026-06-30`, before the pane's own status (after a warning). Nothing when
|
|
44
|
+
* the data is the listing's own. Every company pane registers it here, so it
|
|
45
|
+
* reads, looks and sits the same in each.
|
|
46
|
+
*/
|
|
47
|
+
export function useCompanyDataFooter(companyFrom: CompanyFrom | null | undefined, enabled = true) {
|
|
48
|
+
const language = useAppLanguage();
|
|
49
|
+
const segment = useMemo(() => companyDataFooterSegment(companyFrom), [
|
|
50
|
+
language,
|
|
51
|
+
companyFrom?.symbol,
|
|
52
|
+
companyFrom?.exchange,
|
|
53
|
+
companyFrom?.name,
|
|
54
|
+
]);
|
|
55
|
+
// Its own registration, so a view nested in a pane (13D/G under holders) never clears the pane's.
|
|
56
|
+
const id = useId();
|
|
57
|
+
usePaneFooter(
|
|
58
|
+
`company-data:${id}`,
|
|
59
|
+
() => enabled && segment ? { order: COMPANY_DATA_ORDER, info: [segment] } : null,
|
|
60
|
+
[enabled, segment],
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
31
64
|
export function usePaneStatusFooter({
|
|
32
65
|
registrationId,
|
|
33
66
|
loading = false,
|
|
@@ -53,6 +53,15 @@ export function usePublishStatusWidgetRoom(columns: number): void {
|
|
|
53
53
|
}, []);
|
|
54
54
|
}
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Extra columns the other widgets claim, for a widget that sizes itself to
|
|
58
|
+
* what they leave rather than run into them.
|
|
59
|
+
*/
|
|
60
|
+
export function useOtherStatusWidgetClaims(id: string): number {
|
|
61
|
+
const others = () => claimedColumns() - (claims.get(id) ?? 0);
|
|
62
|
+
return useSyncExternalStore(subscribe, others, others);
|
|
63
|
+
}
|
|
64
|
+
|
|
56
65
|
/** The columns the widgets can use, as the status bar last published them. */
|
|
57
66
|
export function useStatusWidgetRoom(): number {
|
|
58
67
|
return useSyncExternalStore(subscribe, () => room, () => room);
|
|
@@ -421,7 +421,10 @@ export function OnboardingWizard(props: OnboardingWizardProps) {
|
|
|
421
421
|
{persistenceError}
|
|
422
422
|
</Text>
|
|
423
423
|
) : null}
|
|
424
|
-
<OnboardingActions hint={portfolioSub === "positions" && desktop
|
|
424
|
+
<OnboardingActions hint={portfolioSub === "positions" && desktop
|
|
425
|
+
// The step needs one ticker; once it has one, more can be added later.
|
|
426
|
+
? positionCount === 0 ? t("Add one ticker to continue.") : tf("Later: {shortcut}, then AP.", { shortcut: commandBarShortcut })
|
|
427
|
+
: undefined}>
|
|
425
428
|
{portfolioSub === "positions" ? (
|
|
426
429
|
<>
|
|
427
430
|
{brokerOptions.length > 0 && positionCount > 0 ? (
|
|
@@ -279,6 +279,17 @@ function TuiFieldRow({
|
|
|
279
279
|
);
|
|
280
280
|
}
|
|
281
281
|
|
|
282
|
+
/**
|
|
283
|
+
* The line under the form: what Enter does, and how the step goes on. It
|
|
284
|
+
* needs one ticker, and while a field has the keyboard Enter adds, so Esc
|
|
285
|
+
* comes first.
|
|
286
|
+
*/
|
|
287
|
+
function positionsHint(editing: boolean, count: number): string {
|
|
288
|
+
if (!editing && count > 0) return tf("Enter continues · a adds another{broker}", { broker: "" });
|
|
289
|
+
const enter = editing ? t("Enter: next field, then add") : t("Enter to add a ticker");
|
|
290
|
+
return `${enter} · ${count > 0 ? t("Esc, then Enter: continue") : t("Add one ticker to continue")}`;
|
|
291
|
+
}
|
|
292
|
+
|
|
282
293
|
/** The size held: negative for a short. */
|
|
283
294
|
function SignedNumberField(props: ComponentProps<typeof NumberField>) {
|
|
284
295
|
return <NumberField {...props} allowNegative />;
|
|
@@ -302,13 +313,7 @@ function TuiPositionsPanel({ state, inputRef, editing, selectedSymbol, shortcut
|
|
|
302
313
|
) : state.submitting ? (
|
|
303
314
|
<Text fg={colors.textDim}>{t("adding...")}</Text>
|
|
304
315
|
) : (
|
|
305
|
-
<Text fg={colors.textMuted}>
|
|
306
|
-
{editing
|
|
307
|
-
? t("Enter: next field, then add.")
|
|
308
|
-
: state.positions.length > 0
|
|
309
|
-
? tf("Enter continues · a adds another{broker}", { broker: "" })
|
|
310
|
-
: t("Enter to add a ticker.")}
|
|
311
|
-
</Text>
|
|
316
|
+
<Text fg={colors.textMuted}>{positionsHint(editing, state.positions.length)}</Text>
|
|
312
317
|
)}
|
|
313
318
|
</Box>
|
|
314
319
|
<Box height={1} />
|
|
@@ -160,7 +160,6 @@ export function useOnboardingBrokerSync({
|
|
|
160
160
|
draftConfig?: AppConfig,
|
|
161
161
|
) => importBrokerPositions(instanceId, undefined, {
|
|
162
162
|
config: draftConfig,
|
|
163
|
-
persistResolvedBrokerConfig: true,
|
|
164
163
|
signal,
|
|
165
164
|
onCommitStart: () => {
|
|
166
165
|
if (brokerSyncAttemptRef.current !== attemptId) return;
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
} from "../../portfolio/mutations";
|
|
13
13
|
import { isFuturesTicker } from "../../portfolio/futures-positions";
|
|
14
14
|
import { loadFuturesEntrySize } from "../../portfolio/futures-terms";
|
|
15
|
-
import {
|
|
15
|
+
import { ambiguousQuantityProblem } from "../../portfolio/position-quantity";
|
|
16
16
|
import { useAppDispatch, useAppSelector, useAppStateRef } from "../../state/app/context";
|
|
17
17
|
import { saveConfigImmediately } from "../../state/config-save-scheduler";
|
|
18
18
|
import { resolveTickerSearch, upsertTickerFromSearchResult, type ResolvedTickerSearch } from "../../tickers/search";
|
|
@@ -292,8 +292,8 @@ export function useOnboardingPositions({
|
|
|
292
292
|
if (shares === null) {
|
|
293
293
|
nextTicker = addTickerToPortfolio(ticker, portfolioId).ticker;
|
|
294
294
|
} else {
|
|
295
|
-
// 1.250
|
|
296
|
-
const lots =
|
|
295
|
+
// 1.250 could be 1,250 as well, and asks as 1,250 does.
|
|
296
|
+
const lots = ambiguousQuantityProblem(current.shares.trim(), ticker, { label: quantityLabel });
|
|
297
297
|
if (lots) {
|
|
298
298
|
setFieldIdx(1);
|
|
299
299
|
setError(lots);
|
|
@@ -29,11 +29,11 @@ import type { QuoteFlashDirection } from "../quote-flash";
|
|
|
29
29
|
import { objectVersion } from "../../utils/object-version";
|
|
30
30
|
import { getCompletedRegularSessionDisplay } from "../../market-data/market/status";
|
|
31
31
|
import { usePaneFooter } from "../layout/pane/footer";
|
|
32
|
-
import { Button } from "../ui/button";
|
|
33
|
-
import { PaneStatusBody } from "../ui/status";
|
|
34
32
|
|
|
35
33
|
export interface TickerTableCell {
|
|
36
34
|
text: string;
|
|
35
|
+
/** Muted context after `text`, shown whole only when both fit on one line. */
|
|
36
|
+
note?: string;
|
|
37
37
|
color?: string;
|
|
38
38
|
/** The full-precision number behind a rounded `text`, which exports and rendered-view reports keep. */
|
|
39
39
|
value?: number | null;
|
|
@@ -97,11 +97,10 @@ export interface TickerListTableViewProps {
|
|
|
97
97
|
emptyTitle?: string;
|
|
98
98
|
emptyHint?: string;
|
|
99
99
|
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
100
|
+
* The pane's own key for adding a ticker, which the empty table names. The
|
|
101
|
+
* command bar's key when absent. The pane shows its add control once, in its
|
|
102
|
+
* own row, so the empty table points at the key and offers no second one.
|
|
102
103
|
*/
|
|
103
|
-
onAddTicker?: () => void;
|
|
104
|
-
/** The pane's key for the same flow, named after the click. The command bar's key when absent. */
|
|
105
104
|
addTickerShortcut?: string;
|
|
106
105
|
virtualize?: boolean;
|
|
107
106
|
overscan?: number;
|
|
@@ -235,24 +234,12 @@ export function TickerListTableView({
|
|
|
235
234
|
rowMenuAccelerators,
|
|
236
235
|
emptyTitle = t("No tickers."),
|
|
237
236
|
emptyHint,
|
|
238
|
-
onAddTicker,
|
|
239
237
|
addTickerShortcut,
|
|
240
238
|
virtualize = true,
|
|
241
239
|
overscan = 4,
|
|
242
240
|
}: TickerListTableViewProps) {
|
|
243
241
|
const commandBarShortcut = useCommandBarShortcut();
|
|
244
|
-
const
|
|
245
|
-
const resolvedEmptyHint = emptyHint ?? (onAddTicker
|
|
246
|
-
? tf("Click {button} to add one, or press {shortcut}.", { button: addLabel, shortcut: addTickerShortcut ?? commandBarShortcut })
|
|
247
|
-
: tf("Press {shortcut} to add one.", { shortcut: commandBarShortcut }));
|
|
248
|
-
const emptyContent = onAddTicker ? (
|
|
249
|
-
<PaneStatusBody
|
|
250
|
-
empty
|
|
251
|
-
emptyTitle={emptyTitle}
|
|
252
|
-
emptyMessage={resolvedEmptyHint}
|
|
253
|
-
actions={<Button label="Add ticker" displayLabel={addLabel} compact onPress={onAddTicker} />}
|
|
254
|
-
/>
|
|
255
|
-
) : undefined;
|
|
242
|
+
const resolvedEmptyHint = emptyHint ?? tf("Press {shortcut} to add one.", { shortcut: addTickerShortcut ?? commandBarShortcut });
|
|
256
243
|
const renderer = useRendererHost();
|
|
257
244
|
const { showContextMenu } = useContextMenu();
|
|
258
245
|
const { nativeContextMenu } = useUiCapabilities();
|
|
@@ -293,11 +280,12 @@ export function TickerListTableView({
|
|
|
293
280
|
};
|
|
294
281
|
}
|
|
295
282
|
|
|
296
|
-
const { text, color, onPress, value, backgroundColor } = resolveCellRef.current.resolve(column, ticker, financials);
|
|
283
|
+
const { text, note, color, onPress, value, backgroundColor } = resolveCellRef.current.resolve(column, ticker, financials);
|
|
297
284
|
const shouldFlash = flashSymbolsRef.current.has(ticker.metadata.ticker)
|
|
298
285
|
&& flashesOnQuoteTick(column.id, financials?.quote);
|
|
299
286
|
return {
|
|
300
287
|
text,
|
|
288
|
+
...(note ? { note } : {}),
|
|
301
289
|
...(value !== undefined ? { value } : {}),
|
|
302
290
|
color: color || (rowState.selected ? colors.selectedText : undefined),
|
|
303
291
|
attributes: shouldFlash ? TextAttributes.DIM : TextAttributes.NONE,
|
|
@@ -395,7 +383,6 @@ export function TickerListTableView({
|
|
|
395
383
|
getRowVersion={getRowVersion}
|
|
396
384
|
emptyStateTitle={emptyTitle}
|
|
397
385
|
emptyStateHint={resolvedEmptyHint}
|
|
398
|
-
emptyContent={emptyContent}
|
|
399
386
|
virtualize={virtualize}
|
|
400
387
|
overscan={overscan}
|
|
401
388
|
rootBefore={rootBefore}
|
|
@@ -6,10 +6,13 @@ import { t } from "../../i18n";
|
|
|
6
6
|
import { DialogFrame, fitDialogWidth } from "./frame";
|
|
7
7
|
import { ListView, listCursorMove, type ListViewItem } from "./list-view";
|
|
8
8
|
import { clampIndex } from "../../utils/math";
|
|
9
|
+
import { withChoiceGroupHeadings } from "./choice-groups";
|
|
9
10
|
|
|
10
11
|
export interface ChoiceDialogChoice {
|
|
11
12
|
id: string;
|
|
12
13
|
label: string;
|
|
14
|
+
/** Heading for consecutive choices in the same group. */
|
|
15
|
+
group?: string;
|
|
13
16
|
description?: string;
|
|
14
17
|
detail?: string;
|
|
15
18
|
disabled?: boolean;
|
|
@@ -31,10 +34,10 @@ function getChoiceDescription(choice: ChoiceDialogChoice | undefined): string {
|
|
|
31
34
|
return choice?.description ?? "";
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
function getInitialChoiceIndex(choices:
|
|
37
|
+
function getInitialChoiceIndex(choices: ListViewItem[], selectedChoiceId: string | undefined): number {
|
|
35
38
|
if (!selectedChoiceId) return choices.findIndex((choice) => !choice.disabled);
|
|
36
|
-
const selectedIndex = choices.findIndex((choice) => choice.id === selectedChoiceId);
|
|
37
|
-
return selectedIndex >= 0 ? selectedIndex :
|
|
39
|
+
const selectedIndex = choices.findIndex((choice) => choice.id === selectedChoiceId && choice.kind !== "heading");
|
|
40
|
+
return selectedIndex >= 0 ? selectedIndex : choices.findIndex((choice) => choice.kind !== "heading");
|
|
38
41
|
}
|
|
39
42
|
|
|
40
43
|
function choiceDialogWidth(title: string, choices: ChoiceDialogChoice[]): number {
|
|
@@ -42,6 +45,7 @@ function choiceDialogWidth(title: string, choices: ChoiceDialogChoice[]): number
|
|
|
42
45
|
title.length,
|
|
43
46
|
...choices.map((choice) => choice.label.length + (choice.detail ? choice.detail.length + 4 : 0)),
|
|
44
47
|
...choices.map((choice) => getChoiceDescription(choice).length),
|
|
48
|
+
...choices.map((choice) => choice.group?.length ?? 0),
|
|
45
49
|
);
|
|
46
50
|
return Math.max(34, Math.min(76, contentWidth + 4));
|
|
47
51
|
}
|
|
@@ -56,27 +60,28 @@ export function ChoiceDialog({
|
|
|
56
60
|
}: ChoiceDialogProps) {
|
|
57
61
|
const colors = useThemeColors();
|
|
58
62
|
const desktop = useUiHost().kind === "desktop-web";
|
|
59
|
-
const [
|
|
60
|
-
clampIndex(getInitialChoiceIndex(choices, selectedChoiceId), choices.length, -1)
|
|
61
|
-
);
|
|
62
|
-
const selectedIndex = clampIndex(index, choices.length, -1);
|
|
63
|
-
const selectedChoice = selectedIndex >= 0 ? choices[selectedIndex] : undefined;
|
|
64
|
-
// Terminal input can deliver navigation and Enter before React renders.
|
|
65
|
-
const cursor = useRef(selectedIndex);
|
|
66
|
-
const selectIndex = (next: number) => { cursor.current = next; setIndex(next); };
|
|
67
|
-
const items = useMemo<ListViewItem[]>(() => choices.map((choice) => ({
|
|
63
|
+
const items = useMemo<ListViewItem[]>(() => withChoiceGroupHeadings(choices.map((choice) => ({
|
|
68
64
|
id: choice.id,
|
|
69
65
|
label: choice.label,
|
|
66
|
+
group: choice.group,
|
|
70
67
|
description: getChoiceDescription(choice),
|
|
71
68
|
detail: choice.detail,
|
|
72
69
|
disabled: choice.disabled,
|
|
73
70
|
color: choice.color,
|
|
74
|
-
})), [choices]);
|
|
71
|
+
}))), [choices]);
|
|
72
|
+
const [index, setIndex] = useState(() =>
|
|
73
|
+
clampIndex(getInitialChoiceIndex(items, selectedChoiceId), items.length, -1)
|
|
74
|
+
);
|
|
75
|
+
const selectedIndex = clampIndex(index, items.length, -1);
|
|
76
|
+
const selectedChoice = selectedIndex >= 0 ? items[selectedIndex] : undefined;
|
|
77
|
+
// Terminal input can deliver navigation and Enter before React renders.
|
|
78
|
+
const cursor = useRef(selectedIndex);
|
|
79
|
+
const selectIndex = (next: number) => { cursor.current = next; setIndex(next); };
|
|
75
80
|
const width = useMemo(() => choiceDialogWidth(title, choices), [choices, title]);
|
|
76
81
|
|
|
77
82
|
useEffect(() => {
|
|
78
|
-
selectIndex(clampIndex(cursor.current,
|
|
79
|
-
}, [
|
|
83
|
+
selectIndex(clampIndex(cursor.current, items.length, -1));
|
|
84
|
+
}, [items.length]);
|
|
80
85
|
|
|
81
86
|
const activateChoice = (choice: ChoiceDialogChoice | undefined) => {
|
|
82
87
|
if (!choice || choice.disabled) return;
|
|
@@ -87,9 +92,9 @@ export function ChoiceDialog({
|
|
|
87
92
|
event.stopPropagation();
|
|
88
93
|
const move = listCursorMove(event, MAX_VISIBLE_CHOICE_ROWS - 1);
|
|
89
94
|
if (move) {
|
|
90
|
-
selectIndex(move(
|
|
95
|
+
selectIndex(move(items, cursor.current));
|
|
91
96
|
} else if (event.name === "enter" || event.name === "return") {
|
|
92
|
-
activateChoice(
|
|
97
|
+
activateChoice(items[clampIndex(cursor.current, items.length, -1)]);
|
|
93
98
|
} else if (event.name === "escape") {
|
|
94
99
|
resolve("");
|
|
95
100
|
}
|
|
@@ -110,7 +115,7 @@ export function ChoiceDialog({
|
|
|
110
115
|
scrollable={items.length > MAX_VISIBLE_CHOICE_ROWS}
|
|
111
116
|
selectOnHover
|
|
112
117
|
onSelect={selectIndex}
|
|
113
|
-
onActivate={
|
|
118
|
+
onActivate={activateChoice}
|
|
114
119
|
/>
|
|
115
120
|
<Box height={1} />
|
|
116
121
|
<Text fg={colors.textDim} wrapText>{getChoiceDescription(selectedChoice)}</Text>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
interface ChoiceGroupHeading {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
kind: "heading";
|
|
5
|
+
disabled: true;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
/** Label each contiguous group without changing the order or identity of its choices. */
|
|
9
|
+
export function withChoiceGroupHeadings<T extends { group?: string }>(choices: readonly T[]): Array<T | ChoiceGroupHeading> {
|
|
10
|
+
let previousGroup: string | undefined;
|
|
11
|
+
return choices.flatMap((choice, index): Array<T | ChoiceGroupHeading> => {
|
|
12
|
+
const heading = choice.group && choice.group !== previousGroup
|
|
13
|
+
? { id: `\u0000group:${index}`, label: choice.group, kind: "heading" as const, disabled: true as const }
|
|
14
|
+
: null;
|
|
15
|
+
previousGroup = choice.group;
|
|
16
|
+
return heading ? [heading, choice] : [choice];
|
|
17
|
+
});
|
|
18
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
2
|
-
import { Box, ScrollBox, Text, TextAttributes, useNativeRenderer } from "../../../../ui";
|
|
2
|
+
import { Box, ScrollBox, Span, Text, TextAttributes, useNativeRenderer } from "../../../../ui";
|
|
3
3
|
import { hoverBg } from "../../../../theme/colors";
|
|
4
4
|
import { useThemeColors } from "../../../../theme/theme-context";
|
|
5
5
|
import { usePaneInstance } from "../../../../state/app/context";
|
|
@@ -12,7 +12,6 @@ import { EmptyState } from "../../status";
|
|
|
12
12
|
import { observeScrollBoxContentSize, observeScrollBoxViewportSize } from "../../../../renderers/opentui/scrollbox-layout";
|
|
13
13
|
import {
|
|
14
14
|
expandTableColumns,
|
|
15
|
-
fitTableCellText,
|
|
16
15
|
fitTableHeaderText,
|
|
17
16
|
getTableWidth,
|
|
18
17
|
hasMeaningfulTableHorizontalOverflow,
|
|
@@ -30,6 +29,7 @@ import { useFocusOwningPane, useVisibleRangeEmitter } from "../hooks";
|
|
|
30
29
|
import { useStableColumns } from "../stable-columns";
|
|
31
30
|
import {
|
|
32
31
|
cutColumnText,
|
|
32
|
+
fitColumnCellText,
|
|
33
33
|
resolveDataTableColumnClips,
|
|
34
34
|
resolveDataTableVisibleWindow,
|
|
35
35
|
resolveMoreColumnsHint,
|
|
@@ -212,10 +212,8 @@ function OpenTuiDataTableRowInner<
|
|
|
212
212
|
const shownWidth = clip ? clip.width : column.width - inset;
|
|
213
213
|
// Cut text is fitted to what shows; a number the edge cuts stays blank in its own place,
|
|
214
214
|
// and its cell keeps its background and its press.
|
|
215
|
-
const
|
|
216
|
-
|
|
217
|
-
: shownWidth > 0 ? fitTableCellText(cell.text, shownWidth, column.align) : "";
|
|
218
|
-
const contentCut = clip !== null && column.align === "right" && shownText.trim() === "";
|
|
215
|
+
const fitted = fitColumnCellText(cell.text, clip ? column.width : shownWidth, column.align, clip, cell.note);
|
|
216
|
+
const contentCut = clip !== null && column.align === "right" && fitted.text.trim() === "";
|
|
219
217
|
return (
|
|
220
218
|
<Box
|
|
221
219
|
key={column.id}
|
|
@@ -255,7 +253,11 @@ function OpenTuiDataTableRowInner<
|
|
|
255
253
|
? colors.selectedText
|
|
256
254
|
: cell.color ?? colors.text}
|
|
257
255
|
>
|
|
258
|
-
{
|
|
256
|
+
{fitted.note ? <>
|
|
257
|
+
{`${frozen ? " ".repeat(horizontalPadding) : ""}${" ".repeat(inset)}${fitted.text}`}
|
|
258
|
+
<Span fg={colors.textDim} attributes={TextAttributes.NONE}>{fitted.note}</Span>
|
|
259
|
+
{`${fitted.padding ?? ""}${frozen ? " ".repeat(columnGap) : ""}`}
|
|
260
|
+
</> : `${frozen ? " ".repeat(horizontalPadding) : ""}${" ".repeat(inset)}${fitted.text}${frozen ? " ".repeat(columnGap) : ""}`}
|
|
259
261
|
</Text>
|
|
260
262
|
)}
|
|
261
263
|
</Box>
|