@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
|
@@ -27,6 +27,7 @@ import { usePaneFocused } from "../../../state/app/activity";
|
|
|
27
27
|
import { useTabCursorLink } from "../../../state/app/tab-cursor";
|
|
28
28
|
import { useThemeColors, useThemeId } from "../../../theme/theme-context";
|
|
29
29
|
import { themedSeriesColor, themeSeriesColors } from "../../../theme/series-colors";
|
|
30
|
+
import { themeKeysDirection } from "../../../theme/themes";
|
|
30
31
|
import { assignSeriesLineCues } from "./series-cues";
|
|
31
32
|
import { chartSurfaceBackground, usePaneSurface } from "../../layout/pane/surface";
|
|
32
33
|
import { CHART_WATERMARK_ROLE } from "../../../utils/screenshot-watermark";
|
|
@@ -210,6 +211,7 @@ function ThemedCompositeChart({
|
|
|
210
211
|
onChartLayoutChange,
|
|
211
212
|
}: CompositeChartProps & { chartLayout: ChartLayoutPrefs; onChartLayoutChange: ChartLayoutUpdate }) {
|
|
212
213
|
const activeThemeColors = useThemeColors();
|
|
214
|
+
const keysDirection = themeKeysDirection(useThemeId());
|
|
213
215
|
const { cellWidthPx = 8, cellHeightPx = 18, pixelRatio = 1, fractionalViewport = false } = useUiCapabilities();
|
|
214
216
|
const isDesktopWeb = useUiCapabilities().nativePaneChrome === true;
|
|
215
217
|
const showTextFallback = useShowChartTextFallback();
|
|
@@ -1034,6 +1036,8 @@ function ThemedCompositeChart({
|
|
|
1034
1036
|
visibleSeriesIds={visibleSeriesIds}
|
|
1035
1037
|
lineCues={lineCues}
|
|
1036
1038
|
cellPlot={showTextFallback && !isDesktopWeb}
|
|
1039
|
+
// A color-blind safe theme's blue and vermillion are no convention: a candle series keys them.
|
|
1040
|
+
directionDownColor={keysDirection ? resolvedColors.negative : null}
|
|
1037
1041
|
width={totalWidth}
|
|
1038
1042
|
accessory={legendAccessory}
|
|
1039
1043
|
accessoryWidth={legendAccessoryWidth}
|
|
@@ -17,6 +17,9 @@ export type CompositeChartInteraction =
|
|
|
17
17
|
| "arm-line"
|
|
18
18
|
| "arm-measure"
|
|
19
19
|
| "arm-pencil"
|
|
20
|
+
| "arm-ray"
|
|
21
|
+
| "arm-rect"
|
|
22
|
+
| "arm-fib"
|
|
20
23
|
| "arm-zoom"
|
|
21
24
|
| "cycle-colour"
|
|
22
25
|
| "clear-cursor"
|
|
@@ -366,6 +369,9 @@ export function resolveCompositeChartInteraction(event: {
|
|
|
366
369
|
if (shifted && key === "z") return "arm-zoom";
|
|
367
370
|
if (shifted && key === "d") return "arm-line";
|
|
368
371
|
if (shifted && key === "p") return "arm-pencil";
|
|
372
|
+
if (shifted && key === "j") return "arm-ray";
|
|
373
|
+
if (shifted && key === "b") return "arm-rect";
|
|
374
|
+
if (shifted && key === "f") return "arm-fib";
|
|
369
375
|
if (shifted && key === "h") return "arm-level";
|
|
370
376
|
// Delete is the same act on a keyboard that has the key, or on a Mac with fn.
|
|
371
377
|
if (key === "backspace" || key === "delete") return "delete-drawing";
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
seriesPriceReference,
|
|
14
14
|
} from "./format";
|
|
15
15
|
import type { CompositeChartProps, CompositeChartScene } from "./types";
|
|
16
|
-
import { seriesCueCssBackground, type SeriesLineCue } from "./series-cues";
|
|
16
|
+
import { DIRECTION_MARKER, seriesColorsDirection, seriesCueCssBackground, type SeriesLineCue } from "./series-cues";
|
|
17
17
|
import { DisclosureMarker } from "../../ui/disclosure-marker";
|
|
18
18
|
|
|
19
19
|
const LEGEND_WHEEL_DELTA_PER_CELL = 8;
|
|
@@ -120,12 +120,35 @@ function packLegendLines(widths: readonly number[], available: number, maxLines
|
|
|
120
120
|
return lines;
|
|
121
121
|
}
|
|
122
122
|
|
|
123
|
-
/**
|
|
124
|
-
|
|
123
|
+
/** A triangle pointing up or down, as the desktop legend keys a bar's direction. */
|
|
124
|
+
const DIRECTION_TRIANGLES = {
|
|
125
|
+
up: "polygon(50% 0, 100% 100%, 0 100%)",
|
|
126
|
+
down: "polygon(0 0, 100% 0, 50% 100%)",
|
|
127
|
+
} as const;
|
|
128
|
+
|
|
129
|
+
/** The legend's marker for a series on the desktop: its line as the plot strokes it, its up and down colors, or a dot. */
|
|
130
|
+
function CompositeSeriesSwatch({ series, cue, downColor = null, filled = true }: {
|
|
125
131
|
series: ResolvedSeries;
|
|
126
132
|
cue: SeriesLineCue | null;
|
|
133
|
+
/** The down color, for a series keyed by direction: an up triangle in its color, then a down one in this. */
|
|
134
|
+
downColor?: string | null;
|
|
127
135
|
filled?: boolean;
|
|
128
136
|
}) {
|
|
137
|
+
if (downColor) {
|
|
138
|
+
return (
|
|
139
|
+
<Box
|
|
140
|
+
flexShrink={0}
|
|
141
|
+
flexDirection="row"
|
|
142
|
+
style={{ gap: 2, marginInlineEnd: 6, opacity: filled ? 1 : 0.35 }}
|
|
143
|
+
data-gloom-role="composite-chart-legend-marker"
|
|
144
|
+
data-gloom-cue="direction"
|
|
145
|
+
>
|
|
146
|
+
{([["up", series.color], ["down", downColor]] as const).map(([direction, color]) => (
|
|
147
|
+
<Box key={direction} flexShrink={0} style={{ width: 7, height: 7, backgroundColor: color, clipPath: DIRECTION_TRIANGLES[direction] }} />
|
|
148
|
+
))}
|
|
149
|
+
</Box>
|
|
150
|
+
);
|
|
151
|
+
}
|
|
129
152
|
return cue ? (
|
|
130
153
|
// The line as the plot strokes it: solid, dashed, dotted or heavy.
|
|
131
154
|
<Box
|
|
@@ -162,6 +185,7 @@ export function CompositeLegend({
|
|
|
162
185
|
visibleSeriesIds,
|
|
163
186
|
lineCues = NO_LINE_CUES,
|
|
164
187
|
cellPlot = false,
|
|
188
|
+
directionDownColor = null,
|
|
165
189
|
width,
|
|
166
190
|
accessory,
|
|
167
191
|
accessoryWidth,
|
|
@@ -183,6 +207,12 @@ export function CompositeLegend({
|
|
|
183
207
|
lineCues?: ReadonlyMap<string, SeriesLineCue>;
|
|
184
208
|
/** The plot is cell text, so a line's cue is its mark rather than its dash. */
|
|
185
209
|
cellPlot?: boolean;
|
|
210
|
+
/**
|
|
211
|
+
* The color bars that closed down are drawn in, where the theme's up and
|
|
212
|
+
* down are not the familiar green and red: a candle series' marker then keys
|
|
213
|
+
* both (`▲▼`). Null keeps its dot.
|
|
214
|
+
*/
|
|
215
|
+
directionDownColor?: string | null;
|
|
186
216
|
width: number;
|
|
187
217
|
accessory: CompositeChartProps["legendAccessory"];
|
|
188
218
|
accessoryWidth: CompositeChartProps["legendAccessoryWidth"];
|
|
@@ -212,8 +242,9 @@ export function CompositeLegend({
|
|
|
212
242
|
const valueDates = legendValueDates(series, scene, cursorValueById);
|
|
213
243
|
const buildEntries = (withFiscalPeriods: boolean) => series.map((entry) => {
|
|
214
244
|
const cue = lineCues.get(entry.id) ?? null;
|
|
215
|
-
const
|
|
216
|
-
const
|
|
245
|
+
const downColor = !cue && directionDownColor && seriesColorsDirection(entry) ? directionDownColor : null;
|
|
246
|
+
const marker = cue ? `${cellPlot ? cue.glyph : cue.sample} ` : downColor ? DIRECTION_MARKER.text : DOT_MARKER;
|
|
247
|
+
const markerWidth = isDesktopWeb ? cue || downColor ? DOM_LINE_CELLS : DOM_DOT_CELLS : displayWidth(marker);
|
|
217
248
|
const toggleable = !!onToggleSeries && (isSeriesToggleable?.(entry) ?? true);
|
|
218
249
|
const cursorValue = cursorValueById.get(entry.id);
|
|
219
250
|
// A stacked layer reads its own amount, not the top of the stack it is drawn to.
|
|
@@ -253,6 +284,7 @@ export function CompositeLegend({
|
|
|
253
284
|
return {
|
|
254
285
|
entry,
|
|
255
286
|
cue,
|
|
287
|
+
downColor,
|
|
256
288
|
marker,
|
|
257
289
|
markerWidth,
|
|
258
290
|
label,
|
|
@@ -414,7 +446,7 @@ export function CompositeLegend({
|
|
|
414
446
|
scrollBox.scrollTo({ x: nextLeft, y: scrollBox.scrollTop });
|
|
415
447
|
};
|
|
416
448
|
const renderEntry = (
|
|
417
|
-
{ entry, cue, marker, text, statedUnit, valueTail, toggleable, tooltip, carried, width: entryWidth }: LegendEntry,
|
|
449
|
+
{ entry, cue, downColor, marker, text, statedUnit, valueTail, toggleable, tooltip, carried, width: entryWidth }: LegendEntry,
|
|
418
450
|
index: number,
|
|
419
451
|
) => {
|
|
420
452
|
const entryVisible = visibleSeriesIds.has(entry.id);
|
|
@@ -446,7 +478,13 @@ export function CompositeLegend({
|
|
|
446
478
|
title={isDesktopWeb ? tooltip : undefined}
|
|
447
479
|
>
|
|
448
480
|
{isDesktopWeb ? (
|
|
449
|
-
<CompositeSeriesSwatch series={entry} cue={cue} filled={entryVisible} />
|
|
481
|
+
<CompositeSeriesSwatch series={entry} cue={cue} downColor={downColor} filled={entryVisible} />
|
|
482
|
+
) : downColor && entryVisible ? (
|
|
483
|
+
<Text>
|
|
484
|
+
<Span fg={entry.color}>{DIRECTION_MARKER.up}</Span>
|
|
485
|
+
<Span fg={downColor}>{DIRECTION_MARKER.down}</Span>
|
|
486
|
+
{" "}
|
|
487
|
+
</Text>
|
|
450
488
|
) : (
|
|
451
489
|
<Text fg={entryVisible ? entry.color : themeColors.textMuted}>{marker}</Text>
|
|
452
490
|
)}
|
|
@@ -64,6 +64,39 @@ export function startPanGesture(
|
|
|
64
64
|
};
|
|
65
65
|
}
|
|
66
66
|
|
|
67
|
+
/** Where the pointer was during a pan, for the speed it lets go at. */
|
|
68
|
+
export interface PanSample {
|
|
69
|
+
at: number;
|
|
70
|
+
x: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** Only the last moments of a drag count: a pointer that stopped before letting go throws nothing. */
|
|
74
|
+
const PAN_RELEASE_WINDOW_MS = 100;
|
|
75
|
+
/** How long a throw takes to lose about two thirds of its speed. */
|
|
76
|
+
export const PAN_COAST_DECAY_MS = 325;
|
|
77
|
+
/**
|
|
78
|
+
* Plot widths per second a release must reach to coast, and below which a
|
|
79
|
+
* coast stops. Only a flick coasts: a drag placing a bar under the pointer
|
|
80
|
+
* moves at about one plot width a second and must leave it there.
|
|
81
|
+
*/
|
|
82
|
+
export const PAN_COAST_START_SPEED = 2.5;
|
|
83
|
+
export const PAN_COAST_STOP_SPEED = 0.02;
|
|
84
|
+
|
|
85
|
+
/** Cells per millisecond the pointer moved over its last samples, 0 when it had come to rest. */
|
|
86
|
+
export function panReleaseVelocity(samples: readonly PanSample[], now: number): number {
|
|
87
|
+
const recent = samples.filter((sample) => now - sample.at <= PAN_RELEASE_WINDOW_MS);
|
|
88
|
+
const first = recent[0];
|
|
89
|
+
const last = recent.at(-1);
|
|
90
|
+
if (!first || !last || last.at <= first.at) return 0;
|
|
91
|
+
return (last.x - first.x) / (last.at - first.at);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** One frame of a coast: the cells it moves (the decaying speed integrated over the frame) and the speed left. */
|
|
95
|
+
export function panCoastStep(velocity: number, elapsedMs: number): { cells: number; velocity: number } {
|
|
96
|
+
const decay = Math.exp(-Math.max(0, elapsedMs) / PAN_COAST_DECAY_MS);
|
|
97
|
+
return { cells: velocity * PAN_COAST_DECAY_MS * (1 - decay), velocity: velocity * decay };
|
|
98
|
+
}
|
|
99
|
+
|
|
67
100
|
export interface PendingWheel {
|
|
68
101
|
/** Fraction of the visible span, positive toward older observations. */
|
|
69
102
|
panRatio: number;
|
|
@@ -69,9 +69,14 @@ import {
|
|
|
69
69
|
COMPOSITE_PANEL_ROLE,
|
|
70
70
|
isVerticalWheelDirection,
|
|
71
71
|
manualValueAxes,
|
|
72
|
+
PAN_COAST_START_SPEED,
|
|
73
|
+
PAN_COAST_STOP_SPEED,
|
|
74
|
+
panCoastStep,
|
|
75
|
+
panReleaseVelocity,
|
|
72
76
|
startPanGesture,
|
|
73
77
|
releaseEditableFocus,
|
|
74
78
|
type PanGesture,
|
|
79
|
+
type PanSample,
|
|
75
80
|
type PendingAxisWheel,
|
|
76
81
|
type PendingWheel,
|
|
77
82
|
type ValueAxisStart,
|
|
@@ -733,6 +738,7 @@ export function CompositePanelSurface({
|
|
|
733
738
|
}
|
|
734
739
|
if (!updateCursor(event)) return;
|
|
735
740
|
const values = manualValueAxes(panel);
|
|
741
|
+
panSamplesRef.current = [{ at: performance.now(), x: getGlobalMouseX(event, renderer) }];
|
|
736
742
|
dragRef.current = {
|
|
737
743
|
...startPanGesture(
|
|
738
744
|
frame,
|
|
@@ -815,6 +821,8 @@ export function CompositePanelSurface({
|
|
|
815
821
|
return;
|
|
816
822
|
}
|
|
817
823
|
const globalX = getGlobalMouseX(event, renderer);
|
|
824
|
+
panSamplesRef.current.push({ at: performance.now(), x: globalX });
|
|
825
|
+
if (panSamplesRef.current.length > 12) panSamplesRef.current.shift();
|
|
818
826
|
if (drag.frame !== frame) {
|
|
819
827
|
// Data refreshed mid-drag. The new frame maps positions differently, so
|
|
820
828
|
// continue from where the plot is now instead of replaying the drag.
|
|
@@ -847,6 +855,43 @@ export function CompositePanelSurface({
|
|
|
847
855
|
updateCursor,
|
|
848
856
|
viewport,
|
|
849
857
|
]);
|
|
858
|
+
// A pan let go while still moving keeps going and slows down, on the
|
|
859
|
+
// desktop and web only: in a terminal every frame would upload the plot again.
|
|
860
|
+
const panSamplesRef = useRef<PanSample[]>([]);
|
|
861
|
+
const coastFrameRef = useRef<number | null>(null);
|
|
862
|
+
const coastPanRef = useRef(onPanViewport);
|
|
863
|
+
coastPanRef.current = onPanViewport;
|
|
864
|
+
const stopCoast = useCallback(() => {
|
|
865
|
+
if (coastFrameRef.current === null) return;
|
|
866
|
+
webFrame.cancelAnimationFrame?.(coastFrameRef.current);
|
|
867
|
+
coastFrameRef.current = null;
|
|
868
|
+
globalThis.removeEventListener?.("pointerdown", stopCoast, true);
|
|
869
|
+
globalThis.removeEventListener?.("keydown", stopCoast, true);
|
|
870
|
+
}, []);
|
|
871
|
+
const startCoast = useCallback((velocity: number, positionsPerCell: number) => {
|
|
872
|
+
stopCoast();
|
|
873
|
+
const requestFrame = webFrame.requestAnimationFrame;
|
|
874
|
+
const widthCells = Math.max(plotSize.width, 1);
|
|
875
|
+
const plotWidthsPerSecond = (speed: number) => Math.abs(speed) * 1000 / widthCells;
|
|
876
|
+
if (!isDesktopWeb || !requestFrame || plotWidthsPerSecond(velocity) < PAN_COAST_START_SPEED) return;
|
|
877
|
+
if (globalThis.matchMedia?.("(prefers-reduced-motion: reduce)").matches) return;
|
|
878
|
+
let speed = velocity;
|
|
879
|
+
let last = performance.now();
|
|
880
|
+
const step = () => {
|
|
881
|
+
const now = performance.now();
|
|
882
|
+
const next = panCoastStep(speed, now - last);
|
|
883
|
+
last = now;
|
|
884
|
+
speed = next.velocity;
|
|
885
|
+
coastPanRef.current(next.cells * positionsPerCell);
|
|
886
|
+
if (plotWidthsPerSecond(speed) < PAN_COAST_STOP_SPEED) stopCoast();
|
|
887
|
+
else coastFrameRef.current = requestFrame(step);
|
|
888
|
+
};
|
|
889
|
+
coastFrameRef.current = requestFrame(step);
|
|
890
|
+
// Any press or key anywhere, a range button or a reset, takes the view back from the coast.
|
|
891
|
+
globalThis.addEventListener?.("pointerdown", stopCoast, true);
|
|
892
|
+
globalThis.addEventListener?.("keydown", stopCoast, true);
|
|
893
|
+
}, [isDesktopWeb, plotSize.width, stopCoast]);
|
|
894
|
+
useEffect(() => stopCoast, [stopCoast]);
|
|
850
895
|
const finishToolDrag = useCallback(() => {
|
|
851
896
|
const drag = dragRef.current;
|
|
852
897
|
if (!drag || drag.kind === "pan") return;
|
|
@@ -869,12 +914,14 @@ export function CompositePanelSurface({
|
|
|
869
914
|
if (range) onSetViewport(range);
|
|
870
915
|
}, [drawColor, frame.minimumSpanMs, levels, onDraw, onSetViewport, panel, scene]);
|
|
871
916
|
const resetDrag = useCallback(() => {
|
|
872
|
-
|
|
917
|
+
const drag = dragRef.current;
|
|
918
|
+
if (drag?.kind === "pan") {
|
|
873
919
|
dragRef.current = null;
|
|
920
|
+
startCoast(panReleaseVelocity(panSamplesRef.current, performance.now()), drag.positionsPerCell);
|
|
874
921
|
return;
|
|
875
922
|
}
|
|
876
923
|
finishToolDrag();
|
|
877
|
-
}, [finishToolDrag]);
|
|
924
|
+
}, [finishToolDrag, startCoast]);
|
|
878
925
|
// Said once per change, so a pointer move costs the chart nothing.
|
|
879
926
|
const reportedDragRef = useRef(false);
|
|
880
927
|
const reportDrag = useCallback((dragging: boolean) => {
|
|
@@ -883,10 +930,11 @@ export function CompositePanelSurface({
|
|
|
883
930
|
onDragActiveChange?.(dragging);
|
|
884
931
|
}, [onDragActiveChange]);
|
|
885
932
|
const pressPlot = useCallback((event: ChartMouseEvent) => {
|
|
933
|
+
stopCoast();
|
|
886
934
|
const before = dragRef.current;
|
|
887
935
|
startDrag(event);
|
|
888
936
|
if (dragRef.current !== null && dragRef.current !== before) reportDrag(true);
|
|
889
|
-
}, [reportDrag, startDrag]);
|
|
937
|
+
}, [reportDrag, startDrag, stopCoast]);
|
|
890
938
|
const releasePlot = useCallback(() => {
|
|
891
939
|
resetDrag();
|
|
892
940
|
reportDrag(false);
|
|
@@ -929,6 +977,7 @@ export function CompositePanelSurface({
|
|
|
929
977
|
const panFromWheel = useCallback((event: ChartMouseEvent) => {
|
|
930
978
|
const scroll = event.scroll;
|
|
931
979
|
if (!scroll) return;
|
|
980
|
+
stopCoast();
|
|
932
981
|
onActivate?.();
|
|
933
982
|
consumeChartMouseEvent(event);
|
|
934
983
|
const pointer = plotPointer(event);
|
|
@@ -436,13 +436,14 @@ function drawColumns(
|
|
|
436
436
|
domain: CompositeAxisDomain,
|
|
437
437
|
color: RgbaColor,
|
|
438
438
|
negativeColor: RgbaColor,
|
|
439
|
+
fallColor: RgbaColor,
|
|
439
440
|
layout: CompositeColumnLayout,
|
|
440
441
|
widthByFamily: ReadonlyMap<string, number>,
|
|
441
442
|
opacity: number,
|
|
442
443
|
): void {
|
|
443
444
|
const baseline = pixelY(0, domain, height) ?? height - 1;
|
|
444
445
|
for (const projected of series.points) {
|
|
445
|
-
const fill = projected.value < 0 ? negativeColor : color;
|
|
446
|
+
const fill = projected.value < 0 ? negativeColor : projected.point.fell ? fallColor : color;
|
|
446
447
|
const point = pixelPoint(projected, width, height, domain.manual);
|
|
447
448
|
const geometry = columnPixelGeometry(projected, width, layout, widthByFamily);
|
|
448
449
|
fillRect(
|
|
@@ -487,20 +488,27 @@ function drawOhlc(
|
|
|
487
488
|
const closeY = pixelY(close, domain, height);
|
|
488
489
|
if (highY === null || lowY === null || closeY === null) continue;
|
|
489
490
|
const candleColor = close >= open ? color : negative;
|
|
490
|
-
|
|
491
|
-
if (
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
x
|
|
499
|
-
|
|
500
|
-
candleColor,
|
|
501
|
-
|
|
491
|
+
const style = series.source.style;
|
|
492
|
+
if (style !== "ohlc" && style !== "hlc" && openY !== null) {
|
|
493
|
+
const left = x - candleWidth / 2;
|
|
494
|
+
const right = x + candleWidth / 2;
|
|
495
|
+
const top = Math.min(openY, closeY);
|
|
496
|
+
const bottom = Math.max(openY, closeY) + 1;
|
|
497
|
+
// A hollow rising candle is an outline the wick stops at, as on other terminals.
|
|
498
|
+
if (style === "hollow" && close >= open) {
|
|
499
|
+
drawLine(data, width, height, x, highY, x, top, candleColor, 1.1);
|
|
500
|
+
drawLine(data, width, height, x, bottom, x, lowY, candleColor, 1.1);
|
|
501
|
+
drawLine(data, width, height, left, top, right, top, candleColor, 1.1);
|
|
502
|
+
drawLine(data, width, height, left, bottom, right, bottom, candleColor, 1.1);
|
|
503
|
+
drawLine(data, width, height, left, top, left, bottom, candleColor, 1.1);
|
|
504
|
+
drawLine(data, width, height, right, top, right, bottom, candleColor, 1.1);
|
|
505
|
+
continue;
|
|
506
|
+
}
|
|
507
|
+
drawLine(data, width, height, x, highY, x, lowY, candleColor, 1.1);
|
|
508
|
+
fillRect(data, width, height, left, top, right, bottom, candleColor);
|
|
502
509
|
continue;
|
|
503
510
|
}
|
|
511
|
+
drawLine(data, width, height, x, highY, x, lowY, candleColor, 1.1);
|
|
504
512
|
if (series.source.style === "ohlc" && openY !== null) {
|
|
505
513
|
drawLine(data, width, height, x - candleWidth / 2, openY, x, openY, candleColor, 1.2);
|
|
506
514
|
}
|
|
@@ -585,6 +593,7 @@ export function renderCompositePanelBitmap(
|
|
|
585
593
|
domain,
|
|
586
594
|
color,
|
|
587
595
|
negativeColor,
|
|
596
|
+
negative,
|
|
588
597
|
columnLayout,
|
|
589
598
|
columnWidthByFamily,
|
|
590
599
|
mixesColumnsWithOtherMarks ? 0.48 : 0.72,
|
|
@@ -615,6 +624,8 @@ export function renderCompositePanelBitmap(
|
|
|
615
624
|
break;
|
|
616
625
|
}
|
|
617
626
|
case "candles":
|
|
627
|
+
case "hollow":
|
|
628
|
+
case "heikin-ashi":
|
|
618
629
|
case "ohlc":
|
|
619
630
|
case "hlc":
|
|
620
631
|
drawOhlc(data, width, height, series, domain, color, negative);
|
|
@@ -4,7 +4,7 @@ import type {
|
|
|
4
4
|
ResolvedSeries,
|
|
5
5
|
TimeSeriesPoint,
|
|
6
6
|
} from "../../../time-series/types";
|
|
7
|
-
import { effectiveTimeSeriesPointTime, timeSeriesObservationKey } from "../../../time-series/alignment";
|
|
7
|
+
import { effectiveTimeSeriesPointTime, sortedUniquePoints, timeSeriesObservationKey } from "../../../time-series/alignment";
|
|
8
8
|
import type {
|
|
9
9
|
BuildCompositeChartSceneOptions,
|
|
10
10
|
CompositeAxisDomain,
|
|
@@ -36,6 +36,37 @@ function resolveTimeSeriesPointValue(point: TimeSeriesPoint): number | null {
|
|
|
36
36
|
return null;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Heikin Ashi bars in place of a series' own: each close averages the bar's
|
|
41
|
+
* four prices and each open sits halfway along the previous Heikin Ashi body,
|
|
42
|
+
* so the chain runs from the first loaded bar, before any viewport cut. The
|
|
43
|
+
* real close stays in `rawValue` for the last price marker.
|
|
44
|
+
*/
|
|
45
|
+
export function heikinAshiPoints(points: readonly TimeSeriesPoint[]): TimeSeriesPoint[] {
|
|
46
|
+
let previous: { open: number; close: number } | null = null;
|
|
47
|
+
// Chain over the bars the chart draws: one per observation, in time order,
|
|
48
|
+
// so a superseded copy of a bar never feeds the next open.
|
|
49
|
+
return sortedUniquePoints(points).map((point) => {
|
|
50
|
+
const close = point.close ?? point.value;
|
|
51
|
+
if (!isFiniteNumber(close)) return point;
|
|
52
|
+
const open = isFiniteNumber(point.open) ? point.open : close;
|
|
53
|
+
const high = isFiniteNumber(point.high) ? point.high : Math.max(open, close);
|
|
54
|
+
const low = isFiniteNumber(point.low) ? point.low : Math.min(open, close);
|
|
55
|
+
const haClose = (open + high + low + close) / 4;
|
|
56
|
+
const haOpen = previous ? (previous.open + previous.close) / 2 : (open + close) / 2;
|
|
57
|
+
previous = { open: haOpen, close: haClose };
|
|
58
|
+
return {
|
|
59
|
+
...point,
|
|
60
|
+
value: haClose,
|
|
61
|
+
rawValue: close,
|
|
62
|
+
open: haOpen,
|
|
63
|
+
high: Math.max(high, haOpen, haClose),
|
|
64
|
+
low: Math.min(low, haOpen, haClose),
|
|
65
|
+
close: haClose,
|
|
66
|
+
};
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
39
70
|
function pointTime(point: TimeSeriesPoint): number | null {
|
|
40
71
|
const time = point.date instanceof Date ? point.date.getTime() : new Date(point.date).getTime();
|
|
41
72
|
return Number.isFinite(time) ? time : null;
|
|
@@ -502,9 +533,11 @@ function primaryPriceSeries(series: readonly ResolvedSeries[]): ResolvedSeries |
|
|
|
502
533
|
)) ?? null;
|
|
503
534
|
}
|
|
504
535
|
|
|
505
|
-
function lastCloseOf(points: readonly CompositeProjectedPoint[]): number | null {
|
|
536
|
+
function lastCloseOf(points: readonly CompositeProjectedPoint[], heikinAshi = false): number | null {
|
|
506
537
|
const last = points.at(-1);
|
|
507
538
|
if (!last) return null;
|
|
539
|
+
// A Heikin Ashi close is an average; the marker names the price that traded.
|
|
540
|
+
if (heikinAshi && isFiniteNumber(last.point.rawValue)) return last.point.rawValue;
|
|
508
541
|
return isFiniteNumber(last.point.close) ? last.point.close : last.value;
|
|
509
542
|
}
|
|
510
543
|
|
|
@@ -522,7 +555,7 @@ function attachLastPriceMarker(
|
|
|
522
555
|
// A rejected or missing latest bar cannot make an older close current.
|
|
523
556
|
const latest = normalizedSourcePoints(primary).at(-1);
|
|
524
557
|
if (latest?.point.provenance?.priceHistoryIntegrity || latest?.value === null) return;
|
|
525
|
-
const value = lastCloseOf(projected.points);
|
|
558
|
+
const value = lastCloseOf(projected.points, primary.style === "heikin-ashi");
|
|
526
559
|
if (value === null) return;
|
|
527
560
|
const yRatio = projectCompositeValue(value, domain);
|
|
528
561
|
// An axis scaled by hand can leave the last close off the panel.
|
|
@@ -764,7 +797,9 @@ export function buildCompositeChartScene(
|
|
|
764
797
|
panels: ChartPanelSpec[],
|
|
765
798
|
options: BuildCompositeChartSceneOptions,
|
|
766
799
|
): CompositeChartScene | null {
|
|
767
|
-
const dataSeries = series
|
|
800
|
+
const dataSeries = series
|
|
801
|
+
.map((entry) => entry.style === "heikin-ashi" ? { ...entry, points: heikinAshiPoints(entry.points) } : entry)
|
|
802
|
+
.filter((entry) => normalizedPoints(entry).length > 0);
|
|
768
803
|
if (dataSeries.length === 0) return null;
|
|
769
804
|
|
|
770
805
|
// A known missing observation owns a date even though it has no drawable
|
|
@@ -69,3 +69,17 @@ export function seriesCueCssBackground(cue: SeriesLineCue, color: string): strin
|
|
|
69
69
|
});
|
|
70
70
|
return `repeating-linear-gradient(to right, ${stops.join(", ")})`;
|
|
71
71
|
}
|
|
72
|
+
|
|
73
|
+
/** The OHLC family: one series drawn in the up color for a bar that closed up and the down color for one that closed down. */
|
|
74
|
+
const DIRECTION_STYLES = new Set<SeriesStyle>(["candles", "ohlc", "hlc"]);
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Whether a series colors each bar by its direction, so its legend entry can
|
|
78
|
+
* key the two colors (`▲▼`) where the theme's are not the familiar green and red.
|
|
79
|
+
*/
|
|
80
|
+
export function seriesColorsDirection(series: Pick<ResolvedSeries, "style">): boolean {
|
|
81
|
+
return DIRECTION_STYLES.has(series.style);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** The legend marker that keys up and down: the up color's triangle, then the down color's. */
|
|
85
|
+
export const DIRECTION_MARKER = { up: "▲", down: "▼", text: "▲▼ " } as const;
|
|
@@ -248,11 +248,16 @@ function renderOhlc(
|
|
|
248
248
|
const lowRow = valueRow(low, domain, height);
|
|
249
249
|
if (closeRow === null || highRow === null || lowRow === null) continue;
|
|
250
250
|
drawRowsLine(rows, x, highRow, x, lowRow, "│");
|
|
251
|
-
|
|
251
|
+
const style = series.source.style;
|
|
252
|
+
if (style !== "ohlc" && style !== "hlc" && openRow !== null) {
|
|
252
253
|
const [bodyTop, bodyBottom] = rowSpan(openRow, closeRow, height);
|
|
254
|
+
// A hollow rising body keeps its edges; one cell wide it is the wick alone.
|
|
255
|
+
const hollow = style === "hollow" && close >= open;
|
|
253
256
|
for (let row = bodyTop; row <= bodyBottom; row += 1) {
|
|
254
257
|
for (let offset = -halfCandleWidth; offset <= halfCandleWidth; offset += 1) {
|
|
255
|
-
|
|
258
|
+
const side = Math.abs(offset) === halfCandleWidth;
|
|
259
|
+
const rim = row === bodyTop || row === bodyBottom;
|
|
260
|
+
setCell(rows, x + offset, row, !hollow ? "█" : side ? "│" : rim ? "─" : " ");
|
|
256
261
|
}
|
|
257
262
|
}
|
|
258
263
|
continue;
|
|
@@ -313,6 +318,8 @@ export function renderCompositePanelText(
|
|
|
313
318
|
renderBand(rows, series, domain, plotWidth, height, cue);
|
|
314
319
|
break;
|
|
315
320
|
case "candles":
|
|
321
|
+
case "hollow":
|
|
322
|
+
case "heikin-ashi":
|
|
316
323
|
case "ohlc":
|
|
317
324
|
case "hlc":
|
|
318
325
|
renderOhlc(rows, series, domain, plotWidth, height);
|
|
@@ -5,6 +5,9 @@ const RULER_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
|
|
5
5
|
const PEN_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M2.4 13.6 4 9.9 10.6 3.3a1.6 1.6 0 0 1 2.3 0l0 0a1.6 1.6 0 0 1 0 2.3L6.2 12 2.4 13.6Z" fill="none" stroke="#000" stroke-width="1.5" stroke-linejoin="round"/></svg>`;
|
|
6
6
|
const LINE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M3.2 12.8 12.8 3.2" stroke="#000" stroke-width="1.6" stroke-linecap="round"/><circle cx="3.2" cy="12.8" r="2" fill="none" stroke="#000" stroke-width="1.4"/><circle cx="12.8" cy="3.2" r="2" fill="none" stroke="#000" stroke-width="1.4"/></svg>`;
|
|
7
7
|
const LEVEL_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M1.6 8h4.2M10.2 8h4.2" stroke="#000" stroke-width="1.6" stroke-linecap="round"/><circle cx="8" cy="8" r="2.2" fill="none" stroke="#000" stroke-width="1.4"/></svg>`;
|
|
8
|
+
const RAY_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><circle cx="3.4" cy="8" r="2" fill="none" stroke="#000" stroke-width="1.4"/><path d="M5.4 8h9" stroke="#000" stroke-width="1.6" stroke-linecap="round"/></svg>`;
|
|
9
|
+
const RECT_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><rect x="2.2" y="3.6" width="11.6" height="8.8" rx="0.8" fill="#000" fill-opacity="0.18" stroke="#000" stroke-width="1.4"/></svg>`;
|
|
10
|
+
const FIB_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M2 3h12M2 6.4h12M2 9h12M2 13h12" stroke="#000" stroke-width="1.3" stroke-linecap="round"/></svg>`;
|
|
8
11
|
const MARQUEE_ICON = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path d="M2.2 6V3.4a1.2 1.2 0 0 1 1.2-1.2H6M10 2.2h2.6a1.2 1.2 0 0 1 1.2 1.2V6M13.8 10v2.6a1.2 1.2 0 0 1-1.2 1.2H10M6 13.8H3.4a1.2 1.2 0 0 1-1.2-1.2V10" fill="none" stroke="#000" stroke-width="1.7" stroke-linecap="round"/></svg>`;
|
|
9
12
|
|
|
10
13
|
export const CHART_TOOLS: ReadonlyArray<{
|
|
@@ -56,6 +59,30 @@ export const CHART_TOOLS: ReadonlyArray<{
|
|
|
56
59
|
glyph: "\u223f",
|
|
57
60
|
icon: PEN_ICON,
|
|
58
61
|
},
|
|
62
|
+
{
|
|
63
|
+
kind: "ray",
|
|
64
|
+
label: "Horizontal ray",
|
|
65
|
+
shortcut: "Shift+J",
|
|
66
|
+
hint: "Click a price to run a line from it to the right edge, Backspace deletes",
|
|
67
|
+
glyph: "\u21a6",
|
|
68
|
+
icon: RAY_ICON,
|
|
69
|
+
},
|
|
70
|
+
{
|
|
71
|
+
kind: "rect",
|
|
72
|
+
label: "Rectangle",
|
|
73
|
+
shortcut: "Shift+B",
|
|
74
|
+
hint: "Drag a box over a range, grab a corner to reshape it, Backspace deletes",
|
|
75
|
+
glyph: "\u25ad",
|
|
76
|
+
icon: RECT_ICON,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
kind: "fib",
|
|
80
|
+
label: "Fib retracement",
|
|
81
|
+
shortcut: "Shift+F",
|
|
82
|
+
hint: "Drag from a swing to its end for the 23.6 to 78.6% levels, Backspace deletes",
|
|
83
|
+
glyph: "\u2261",
|
|
84
|
+
icon: FIB_ICON,
|
|
85
|
+
},
|
|
59
86
|
{
|
|
60
87
|
kind: "level",
|
|
61
88
|
label: "Price level",
|
|
@@ -72,6 +99,9 @@ export const CHART_TOOL_MENU: ReadonlyArray<{ kind: ChartToolKind; label: string
|
|
|
72
99
|
{ kind: "zoom", label: "Zoom to Range", accelerator: "Shift+Z" },
|
|
73
100
|
{ kind: "line", label: "Trend Line", accelerator: "Shift+D" },
|
|
74
101
|
{ kind: "pencil", label: "Freehand", accelerator: "Shift+P" },
|
|
102
|
+
{ kind: "ray", label: "Horizontal Ray", accelerator: "Shift+J" },
|
|
103
|
+
{ kind: "rect", label: "Rectangle", accelerator: "Shift+B" },
|
|
104
|
+
{ kind: "fib", label: "Fib Retracement", accelerator: "Shift+F" },
|
|
75
105
|
{ kind: "level", label: "Price Level", accelerator: "Shift+H" },
|
|
76
106
|
];
|
|
77
107
|
|
|
@@ -86,6 +116,9 @@ export const KEYBOARD_TOOL_HINTS: Record<ChartToolKind, {
|
|
|
86
116
|
zoom: { start: "select", startTitle: "Select Range", finish: "zoom", finishTitle: "Zoom to Range" },
|
|
87
117
|
line: { start: "draw", startTitle: "Draw Line", finish: "place", finishTitle: "Place Line" },
|
|
88
118
|
pencil: { start: "draw", startTitle: "Draw Freehand", finish: "place", finishTitle: "Place Drawing" },
|
|
119
|
+
ray: { start: "draw", startTitle: "Draw Ray", finish: "place", finishTitle: "Place Ray" },
|
|
120
|
+
rect: { start: "draw", startTitle: "Draw Rectangle", finish: "place", finishTitle: "Place Rectangle" },
|
|
121
|
+
fib: { start: "draw", startTitle: "Draw Retracement", finish: "place", finishTitle: "Place Retracement" },
|
|
89
122
|
level: { start: "add level", startTitle: "Add Level", finish: "add level", finishTitle: "Add Level" },
|
|
90
123
|
};
|
|
91
124
|
|
|
@@ -94,5 +127,8 @@ export const ARMED_TOOL_BY_INTERACTION = {
|
|
|
94
127
|
"arm-zoom": "zoom",
|
|
95
128
|
"arm-line": "line",
|
|
96
129
|
"arm-pencil": "pencil",
|
|
130
|
+
"arm-ray": "ray",
|
|
131
|
+
"arm-rect": "rect",
|
|
132
|
+
"arm-fib": "fib",
|
|
97
133
|
"arm-level": "level",
|
|
98
134
|
} as const satisfies Record<string, ChartToolKind>;
|