@gloom-sh/terminal 0.18.5 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/patches/electrobun@1.18.1.patch +140 -7
- package/src/api-client/cot.ts +6 -2
- package/src/api-client/credit-documents.ts +6 -0
- package/src/api-client/data.ts +17 -6
- package/src/api-client/equity-screener.ts +24 -0
- package/src/api-client/fund-facts.ts +45 -4
- package/src/api-client/index.ts +11 -0
- package/src/api-client/market-discovery.ts +10 -0
- package/src/api-client/market-movers.ts +7 -0
- package/src/api-client/members.ts +1 -1
- package/src/api-client/perps.ts +43 -0
- package/src/api-client/research-activity.ts +3 -2
- package/src/api-client/types.ts +70 -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/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 +72 -26
- package/src/cli/commands/options-chain.ts +48 -7
- package/src/cli/commands/options-leaps.ts +9 -3
- package/src/cli/commands/overview.ts +61 -12
- package/src/cli/commands/search.ts +21 -1
- package/src/cli/commands/themes.ts +11 -2
- package/src/cli/commands/ticker.ts +55 -34
- package/src/cli/company-name-hint.ts +41 -0
- package/src/cli/earnings-calendar.ts +1 -1
- package/src/cli/errors.ts +8 -0
- package/src/cli/glossary.ts +25 -2
- package/src/cli/history-rows.ts +57 -9
- package/src/cli/index.ts +2 -0
- package/src/cli/listing-arg.ts +180 -52
- package/src/cli/output-file.ts +68 -0
- package/src/cli/pane-functions/capabilities.ts +3 -0
- package/src/cli/pane-functions/catalog.ts +80 -7
- package/src/cli/pane-functions/dom.ts +13 -3
- package/src/cli/pane-functions/freshness.ts +86 -3
- package/src/cli/pane-functions/function-help.ts +13 -10
- package/src/cli/pane-functions/headless.ts +17 -7
- package/src/cli/pane-functions/index.ts +49 -14
- 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/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/pane-templates/items.ts +3 -2
- package/src/components/command-bar/workflow/collection-actions.ts +3 -3
- 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 +4 -1
- package/src/components/ui/choice-dialog.tsx +23 -18
- package/src/components/ui/choice-groups.ts +18 -0
- package/src/components/ui/data-table/opentui/index.tsx +9 -7
- package/src/components/ui/data-table/opentui/model.ts +26 -1
- package/src/components/ui/data-table/types.ts +2 -0
- package/src/components/ui/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/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 +71 -137
- package/src/i18n/es.ts +18 -2
- package/src/i18n/ja.ts +18 -2
- package/src/i18n/ko.ts +18 -2
- package/src/i18n/zh-cn.ts +18 -2
- package/src/i18n/zh-tw.ts +18 -2
- package/src/market-data/depositary-pair.ts +2 -2
- package/src/market-data/fund-facts.ts +50 -0
- package/src/market-data/market/change-basis.ts +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/news/aggregator.ts +113 -9
- package/src/news/cloud-session.ts +21 -0
- package/src/news/hooks.ts +1 -0
- package/src/news/news-model.ts +1 -0
- package/src/news/types.ts +11 -0
- package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
- package/src/plugins/builtin/alerts/alert-log.ts +33 -0
- package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
- package/src/plugins/builtin/alerts/cli.ts +336 -77
- package/src/plugins/builtin/alerts/command.ts +25 -1
- package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
- 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 +24 -6
- 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/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/headless-schema.ts +7 -1
- package/src/plugins/builtin/chart-composer/headless.ts +6 -1
- package/src/plugins/builtin/chart-composer/index.tsx +1 -1
- 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/pane-links/commands.ts +7 -1
- package/src/plugins/builtin/clinical-trials/model.ts +0 -9
- package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
- package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
- package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
- package/src/plugins/builtin/company-attention/building.tsx +5 -1
- package/src/plugins/builtin/company-attention/client.ts +8 -9
- package/src/plugins/builtin/company-attention/evidence.ts +4 -1
- package/src/plugins/builtin/company-attention/moves.tsx +5 -1
- package/src/plugins/builtin/company-attention/pane.tsx +9 -2
- package/src/plugins/builtin/company-attention/signals.tsx +5 -1
- package/src/plugins/builtin/correlation/headless.ts +13 -5
- package/src/plugins/builtin/correlation/history.ts +22 -0
- package/src/plugins/builtin/cot/client.ts +22 -18
- package/src/plugins/builtin/cot/headless.ts +15 -10
- package/src/plugins/builtin/cot/model.ts +14 -1
- package/src/plugins/builtin/cot/pane.tsx +22 -14
- package/src/plugins/builtin/credit-boards/client.ts +6 -10
- package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
- package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
- package/src/plugins/builtin/credit-conditions/model.ts +4 -0
- package/src/plugins/builtin/credit-conditions/series.ts +3 -1
- package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
- package/src/plugins/builtin/credit-documents/export.ts +173 -0
- package/src/plugins/builtin/credit-documents/headless.ts +7 -7
- package/src/plugins/builtin/credit-documents/model.ts +104 -14
- package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
- package/src/plugins/builtin/custom-view/sources.ts +2 -0
- package/src/plugins/builtin/dividend-yield/client.ts +131 -23
- package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
- package/src/plugins/builtin/dividend-yield/model.ts +8 -5
- package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
- package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
- package/src/plugins/builtin/dividend-yield/types.ts +20 -2
- package/src/plugins/builtin/dividend-yield/view.ts +30 -2
- package/src/plugins/builtin/docs/targets.ts +5 -1
- package/src/plugins/builtin/earnings/board-model.ts +16 -15
- package/src/plugins/builtin/earnings/board.tsx +62 -71
- package/src/plugins/builtin/earnings/headless.ts +40 -4
- package/src/plugins/builtin/earnings/scope.ts +31 -43
- package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
- package/src/plugins/builtin/earnings-calls/format.ts +31 -2
- package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
- package/src/plugins/builtin/earnings-calls/model.ts +6 -1
- package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
- package/src/plugins/builtin/econ/types.ts +2 -0
- package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
- package/src/plugins/builtin/econ-statistics/headless.ts +6 -4
- package/src/plugins/builtin/econ-statistics/stats.ts +36 -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 +1 -0
- package/src/plugins/builtin/help/shortcut-model.ts +1 -1
- package/src/plugins/builtin/holders/fund-holders.ts +51 -0
- package/src/plugins/builtin/holders/headless.ts +18 -1
- package/src/plugins/builtin/iv-history/client.ts +52 -11
- package/src/plugins/builtin/iv-history/format.ts +36 -23
- package/src/plugins/builtin/iv-history/headless.ts +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/release-table.ts +2 -2
- package/src/plugins/builtin/market-heatmap/data.ts +8 -33
- package/src/plugins/builtin/market-movers/client.ts +25 -7
- package/src/plugins/builtin/market-movers/headless.ts +32 -12
- package/src/plugins/builtin/market-movers/index.tsx +26 -14
- package/src/plugins/builtin/market-movers/min-float.ts +74 -0
- package/src/plugins/builtin/market-movers/model.ts +4 -1
- package/src/plugins/builtin/market-movers/screener.ts +14 -0
- package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
- package/src/plugins/builtin/market-movers/session-table.ts +4 -3
- package/src/plugins/builtin/market-movers/table.ts +17 -5
- package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
- package/src/plugins/builtin/news/index.tsx +1 -0
- package/src/plugins/builtin/news/wire/categories.ts +19 -3
- package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
- package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
- package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
- package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
- package/src/plugins/builtin/openfda/client.ts +14 -2
- package/src/plugins/builtin/openfda/index.tsx +3 -2
- package/src/plugins/builtin/openfda/model.ts +9 -0
- package/src/plugins/builtin/openfda/pane.tsx +42 -6
- package/src/plugins/builtin/options/analytics.ts +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 +4 -1
- package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
- package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
- package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
- package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
- package/src/plugins/builtin/primary-dealers/client.ts +13 -38
- package/src/plugins/builtin/realized-vol/index.tsx +2 -2
- package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
- package/src/plugins/builtin/relative-rotation/client.ts +18 -4
- package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
- package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
- package/src/plugins/builtin/relative-rotation/model.ts +16 -40
- package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
- package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
- package/src/plugins/builtin/research/event-model.ts +10 -8
- package/src/plugins/builtin/research/headless.ts +1 -1
- package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
- package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
- package/src/plugins/builtin/research/relative-valuation-pane.tsx +7 -5
- package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
- package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
- package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
- package/src/plugins/builtin/scanner/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 +27 -3
- 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 +2 -1
- package/src/plugins/builtin/series-inventory.ts +2 -0
- package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
- package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
- package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
- package/src/plugins/builtin/shared/earnings-report.ts +2 -2
- package/src/plugins/builtin/shared/job-posting.ts +1 -0
- package/src/plugins/builtin/shared/members-menu.tsx +3 -2
- package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
- package/src/plugins/builtin/shared/report-freshness.ts +3 -2
- package/src/plugins/builtin/shared/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/ticker-detail/data-panes/historical-prices.tsx +43 -18
- package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
- package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
- package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
- package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
- package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
- package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
- package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
- package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
- package/src/plugins/builtin/time-sales/pane.tsx +31 -19
- package/src/plugins/builtin/treasury-daily/client.ts +41 -5
- package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
- package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
- package/src/plugins/builtin/treasury-daily/model.ts +50 -3
- package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
- package/src/plugins/builtin/vol-surface/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 +2 -2
- package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
- package/src/plugins/catalog-manifest.ts +2 -1
- package/src/plugins/marketplace/model.ts +18 -5
- 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/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 +12 -0
- package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
- package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
- package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
- package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
- package/src/renderers/electrobun/shared/app-name.ts +13 -0
- package/src/renderers/electrobun/shared/protocol.ts +10 -0
- package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
- package/src/renderers/electrobun/view/main.tsx +24 -1
- package/src/renderers/electrobun/view/news-source.ts +3 -1
- package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
- package/src/renderers/electrobun/view/view-ids.ts +85 -0
- package/src/renderers/opentui/start.tsx +5 -0
- package/src/series/registry.ts +1 -0
- package/src/series/types.ts +6 -0
- package/src/series/validate.ts +1 -0
- package/src/sources/gloomberb-cloud/index.ts +1 -0
- package/src/sources/gloomberb-cloud/news.ts +47 -7
- package/src/sources/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.ts +44 -3
- package/src/tickers/listing-venues.ts +15 -3
- package/src/tickers/terminal-codes.ts +19 -0
- 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/financials.ts +32 -1
- package/src/types/headless.ts +21 -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 +44 -0
- package/src/utils/forward-dividend.ts +2 -0
- package/src/utils/fundamentals.ts +24 -3
- package/src/utils/fx-market-hours.ts +26 -3
- package/src/utils/main-thread-stall.ts +35 -2
- package/src/utils/market-capitalization.ts +24 -1
- package/src/utils/price-history.ts +7 -1
- package/src/utils/shell-word.ts +8 -0
- package/src/utils/trailing-dividend.ts +53 -4
- package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
|
@@ -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": {
|
|
@@ -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
|
|
@@ -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) {
|
|
@@ -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);
|
|
@@ -34,6 +34,8 @@ import { PaneStatusBody } from "../ui/status";
|
|
|
34
34
|
|
|
35
35
|
export interface TickerTableCell {
|
|
36
36
|
text: string;
|
|
37
|
+
/** Muted context after `text`, shown whole only when both fit on one line. */
|
|
38
|
+
note?: string;
|
|
37
39
|
color?: string;
|
|
38
40
|
/** The full-precision number behind a rounded `text`, which exports and rendered-view reports keep. */
|
|
39
41
|
value?: number | null;
|
|
@@ -293,11 +295,12 @@ export function TickerListTableView({
|
|
|
293
295
|
};
|
|
294
296
|
}
|
|
295
297
|
|
|
296
|
-
const { text, color, onPress, value, backgroundColor } = resolveCellRef.current.resolve(column, ticker, financials);
|
|
298
|
+
const { text, note, color, onPress, value, backgroundColor } = resolveCellRef.current.resolve(column, ticker, financials);
|
|
297
299
|
const shouldFlash = flashSymbolsRef.current.has(ticker.metadata.ticker)
|
|
298
300
|
&& flashesOnQuoteTick(column.id, financials?.quote);
|
|
299
301
|
return {
|
|
300
302
|
text,
|
|
303
|
+
...(note ? { note } : {}),
|
|
301
304
|
...(value !== undefined ? { value } : {}),
|
|
302
305
|
color: color || (rowState.selected ? colors.selectedText : undefined),
|
|
303
306
|
attributes: shouldFlash ? TextAttributes.DIM : TextAttributes.NONE,
|