@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.
Files changed (570) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/package.json +1 -1
  4. package/patches/electrobun@1.18.1.patch +163 -8
  5. package/src/api-client/app-attention.ts +3 -0
  6. package/src/api-client/beneficial-owners.ts +3 -0
  7. package/src/api-client/company-kpis.ts +3 -0
  8. package/src/api-client/cot.ts +6 -2
  9. package/src/api-client/credit-documents.ts +6 -0
  10. package/src/api-client/data.ts +19 -7
  11. package/src/api-client/equity-screener.ts +24 -0
  12. package/src/api-client/fund-facts.ts +45 -4
  13. package/src/api-client/hiring.ts +3 -0
  14. package/src/api-client/index.ts +15 -0
  15. package/src/api-client/ipo.ts +24 -1
  16. package/src/api-client/market-discovery.ts +10 -0
  17. package/src/api-client/market-movers.ts +7 -0
  18. package/src/api-client/members.ts +1 -1
  19. package/src/api-client/mna.ts +3 -0
  20. package/src/api-client/paths.ts +9 -1
  21. package/src/api-client/perps.ts +43 -0
  22. package/src/api-client/research-activity.ts +3 -2
  23. package/src/api-client/roadmap.ts +85 -0
  24. package/src/api-client/types.ts +82 -2
  25. package/src/app/pane-runtime/plugin-bindings.ts +9 -5
  26. package/src/app/runtime/broker-import.ts +0 -2
  27. package/src/app/runtime/plugin-bindings.ts +73 -56
  28. package/src/architecture/core-builtin-allowlist.json +2 -2
  29. package/src/architecture/pane-tabs-allowlist.json +3 -0
  30. package/src/architecture/ttl-caches-allowlist.json +19 -0
  31. package/src/brokers/sync-broker-instance.ts +2 -8
  32. package/src/capabilities/chart-series.ts +1 -1
  33. package/src/cli/commands/crypto-hints.ts +40 -24
  34. package/src/cli/commands/market.ts +107 -38
  35. package/src/cli/commands/news-search.ts +22 -0
  36. package/src/cli/commands/options-chain.ts +48 -7
  37. package/src/cli/commands/options-leaps.ts +9 -3
  38. package/src/cli/commands/overview.ts +147 -32
  39. package/src/cli/commands/search.ts +21 -1
  40. package/src/cli/commands/themes.ts +11 -2
  41. package/src/cli/commands/ticker.ts +87 -36
  42. package/src/cli/company-name-hint.ts +41 -0
  43. package/src/cli/earnings-calendar.ts +1 -1
  44. package/src/cli/errors.ts +25 -2
  45. package/src/cli/glossary.ts +31 -2
  46. package/src/cli/history-rows.ts +57 -9
  47. package/src/cli/index-roots.ts +0 -7
  48. package/src/cli/index.ts +2 -0
  49. package/src/cli/listing-arg.ts +235 -60
  50. package/src/cli/output-file.ts +68 -0
  51. package/src/cli/pane-functions/capabilities.ts +4 -0
  52. package/src/cli/pane-functions/catalog.ts +80 -7
  53. package/src/cli/pane-functions/discovery.ts +21 -1
  54. package/src/cli/pane-functions/dom.ts +13 -3
  55. package/src/cli/pane-functions/freshness.ts +93 -4
  56. package/src/cli/pane-functions/function-help.ts +20 -10
  57. package/src/cli/pane-functions/headless.ts +17 -7
  58. package/src/cli/pane-functions/index.ts +54 -16
  59. package/src/cli/pane-functions/market-session.ts +66 -4
  60. package/src/cli/pane-functions/options.ts +19 -1
  61. package/src/cli/pane-functions/resolver.ts +19 -8
  62. package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
  63. package/src/cli/pane-functions/screenshot-types.ts +5 -0
  64. package/src/cli/quote-answer.ts +22 -11
  65. package/src/cli/saved-copy-persistence.ts +88 -0
  66. package/src/cli/ticker-resolution.ts +3 -2
  67. package/src/components/chart/composite/composite-chart.tsx +4 -0
  68. package/src/components/chart/composite/interactions.ts +6 -0
  69. package/src/components/chart/composite/legend.tsx +45 -7
  70. package/src/components/chart/composite/panel-gestures.ts +33 -0
  71. package/src/components/chart/composite/panel-surface.tsx +52 -3
  72. package/src/components/chart/composite/rasterizer.ts +24 -13
  73. package/src/components/chart/composite/scene.ts +39 -4
  74. package/src/components/chart/composite/series-cues.ts +14 -0
  75. package/src/components/chart/composite/text-renderer.ts +9 -2
  76. package/src/components/chart/composite/tool-catalog.ts +36 -0
  77. package/src/components/chart/composite/tools.ts +203 -44
  78. package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
  79. package/src/components/command-bar/assist/inventory.ts +2 -1
  80. package/src/components/command-bar/commands/direct/index.ts +28 -13
  81. package/src/components/command-bar/list/model.ts +5 -0
  82. package/src/components/command-bar/pane-templates/items.ts +3 -2
  83. package/src/components/command-bar/routes/root/runtime.ts +6 -7
  84. package/src/components/command-bar/selection-runtime.ts +52 -3
  85. package/src/components/command-bar/surface/index.tsx +2 -0
  86. package/src/components/command-bar/workflow/collection-actions.ts +3 -3
  87. package/src/components/layout/pane/capture-stamp.ts +2 -2
  88. package/src/components/layout/pane/footer/index.tsx +22 -6
  89. package/src/components/layout/pane/footer/model.ts +83 -15
  90. package/src/components/layout/pane/status-footer.ts +35 -2
  91. package/src/components/layout/status-widget-space.ts +9 -0
  92. package/src/components/onboarding/onboarding-wizard.tsx +4 -1
  93. package/src/components/onboarding/portfolio-step/positions-panel.tsx +12 -7
  94. package/src/components/onboarding/wizard-broker-sync.ts +0 -1
  95. package/src/components/onboarding/wizard-positions.ts +3 -3
  96. package/src/components/ticker/list-table-view.tsx +8 -21
  97. package/src/components/ui/choice-dialog.tsx +23 -18
  98. package/src/components/ui/choice-groups.ts +18 -0
  99. package/src/components/ui/data-table/opentui/index.tsx +9 -7
  100. package/src/components/ui/data-table/opentui/model.ts +26 -1
  101. package/src/components/ui/data-table/types.ts +2 -0
  102. package/src/components/ui/display.tsx +18 -3
  103. package/src/components/ui/icon.tsx +4 -1
  104. package/src/components/ui/list-view.tsx +8 -4
  105. package/src/components/ui/query-bar-keys.tsx +2 -0
  106. package/src/components/ui/query-bar.tsx +1 -0
  107. package/src/components/ui/select-button.tsx +5 -1
  108. package/src/components/ui/select-field.tsx +1 -0
  109. package/src/components/ui/table-layout.ts +8 -5
  110. package/src/core/app-runtime.ts +2 -0
  111. package/src/core/app-services.ts +2 -2
  112. package/src/data/config/chart-settings.ts +1 -1
  113. package/src/data/json-ticker-repository.ts +17 -8
  114. package/src/data/plugin-cache.ts +5 -3
  115. package/src/data/saved-copies.ts +8 -0
  116. package/src/data/ticker-repository.ts +16 -9
  117. package/src/data/ticker-writes.ts +81 -0
  118. package/src/data/ttl-cache.ts +165 -0
  119. package/src/docs/snapshot.json +103 -142
  120. package/src/i18n/es.ts +20 -6
  121. package/src/i18n/ja.ts +20 -6
  122. package/src/i18n/ko.ts +20 -6
  123. package/src/i18n/zh-cn.ts +20 -6
  124. package/src/i18n/zh-tw.ts +20 -6
  125. package/src/market-data/depositary-pair.ts +27 -3
  126. package/src/market-data/fund-facts.ts +50 -0
  127. package/src/market-data/market/change-basis.ts +16 -3
  128. package/src/market-data/market/format.ts +25 -0
  129. package/src/market-data/market/freshness.ts +2 -1
  130. package/src/market-data/options-alternatives.ts +81 -4
  131. package/src/market-data/quotes/metadata.ts +7 -0
  132. package/src/news/aggregator.ts +119 -9
  133. package/src/news/cloud-session.ts +21 -0
  134. package/src/news/hooks.ts +1 -0
  135. package/src/news/news-model.ts +1 -0
  136. package/src/news/types.ts +17 -0
  137. package/src/plugins/builtin/alerts/alert-engine.ts +20 -1
  138. package/src/plugins/builtin/alerts/alert-log.ts +33 -0
  139. package/src/plugins/builtin/alerts/calendar-events.ts +10 -4
  140. package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
  141. package/src/plugins/builtin/alerts/cli.ts +362 -80
  142. package/src/plugins/builtin/alerts/command.ts +25 -1
  143. package/src/plugins/builtin/alerts/event-rows.ts +24 -3
  144. package/src/plugins/builtin/alerts/events-pane.tsx +34 -10
  145. package/src/plugins/builtin/alerts/fired-log.ts +73 -9
  146. package/src/plugins/builtin/alerts/format.ts +75 -5
  147. package/src/plugins/builtin/alerts/headless.ts +34 -12
  148. package/src/plugins/builtin/alerts/history-rows.ts +3 -2
  149. package/src/plugins/builtin/alerts/history.ts +1 -1
  150. package/src/plugins/builtin/alerts/index.tsx +79 -16
  151. package/src/plugins/builtin/alerts/metric-command.ts +77 -4
  152. package/src/plugins/builtin/alerts/metric-data.ts +54 -13
  153. package/src/plugins/builtin/alerts/metric-model.ts +163 -14
  154. package/src/plugins/builtin/alerts/metric-parity-fixture.json +1 -1
  155. package/src/plugins/builtin/alerts/pane.tsx +61 -23
  156. package/src/plugins/builtin/alerts/quotes.ts +6 -0
  157. package/src/plugins/builtin/alerts/storage.ts +12 -8
  158. package/src/plugins/builtin/alerts/types.ts +4 -0
  159. package/src/plugins/builtin/analytics/index.tsx +2 -1
  160. package/src/plugins/builtin/analytics/risk-client.ts +16 -5
  161. package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
  162. package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
  163. package/src/plugins/builtin/analytics/risk-model.ts +55 -9
  164. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  165. package/src/plugins/builtin/attention/index.tsx +2 -1
  166. package/src/plugins/builtin/attention/listing.ts +14 -0
  167. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  168. package/src/plugins/builtin/buildout/cells.tsx +12 -4
  169. package/src/plugins/builtin/buildout/format.ts +38 -0
  170. package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
  171. package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
  172. package/src/plugins/builtin/buildout/table-model.ts +23 -2
  173. package/src/plugins/builtin/builtin-plugin-meta.ts +6 -2
  174. package/src/plugins/builtin/cds/client.ts +142 -30
  175. package/src/plugins/builtin/cds/headless.ts +35 -8
  176. package/src/plugins/builtin/cds/model.ts +145 -18
  177. package/src/plugins/builtin/cds/pane.tsx +16 -5
  178. package/src/plugins/builtin/changelog/index.tsx +9 -6
  179. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +3 -0
  180. package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
  181. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +7 -0
  182. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -2
  183. package/src/plugins/builtin/chart-composer/follow-binding.ts +14 -2
  184. package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -1
  185. package/src/plugins/builtin/chart-composer/headless.ts +6 -1
  186. package/src/plugins/builtin/chart-composer/index.tsx +5 -6
  187. package/src/plugins/builtin/chart-composer/pane.tsx +37 -8
  188. package/src/plugins/builtin/chart-composer/settings.ts +27 -0
  189. package/src/plugins/builtin/chat/content/command-suggestions.ts +1 -0
  190. package/src/plugins/builtin/chat/controller/index.ts +5 -1
  191. package/src/plugins/builtin/chat/controller/notifications.ts +33 -3
  192. package/src/plugins/builtin/chat/controller/state.ts +25 -2
  193. package/src/plugins/builtin/chat/controller/unread-people.ts +89 -23
  194. package/src/plugins/builtin/chat/controller/utils.ts +7 -2
  195. package/src/plugins/builtin/chat/pane-links/commands.ts +7 -1
  196. package/src/plugins/builtin/chat/status-preview.ts +79 -0
  197. package/src/plugins/builtin/chat/status-widget.tsx +147 -10
  198. package/src/plugins/builtin/chat/unread-badge.ts +51 -10
  199. package/src/plugins/builtin/clinical-trials/client.ts +49 -4
  200. package/src/plugins/builtin/clinical-trials/model.ts +86 -31
  201. package/src/plugins/builtin/clinical-trials/pane.tsx +61 -49
  202. package/src/plugins/builtin/clinical-trials/sponsor-listing.ts +90 -0
  203. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +95 -15
  204. package/src/plugins/builtin/clinical-trials/trial-pages.ts +50 -0
  205. package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
  206. package/src/plugins/builtin/company-attention/building.tsx +5 -1
  207. package/src/plugins/builtin/company-attention/client.ts +8 -9
  208. package/src/plugins/builtin/company-attention/evidence.ts +4 -1
  209. package/src/plugins/builtin/company-attention/headless.ts +2 -1
  210. package/src/plugins/builtin/company-attention/moves.tsx +5 -1
  211. package/src/plugins/builtin/company-attention/pane.tsx +12 -2
  212. package/src/plugins/builtin/company-attention/signals.tsx +5 -1
  213. package/src/plugins/builtin/company-kpis/headless.ts +8 -3
  214. package/src/plugins/builtin/company-kpis/model.ts +73 -5
  215. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  216. package/src/plugins/builtin/composite-plugins.ts +2 -1
  217. package/src/plugins/builtin/correlation/headless.ts +13 -5
  218. package/src/plugins/builtin/correlation/history.ts +22 -0
  219. package/src/plugins/builtin/cot/client.ts +22 -18
  220. package/src/plugins/builtin/cot/headless.ts +15 -10
  221. package/src/plugins/builtin/cot/model.ts +14 -1
  222. package/src/plugins/builtin/cot/pane.tsx +22 -14
  223. package/src/plugins/builtin/credit-boards/client.ts +6 -10
  224. package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
  225. package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
  226. package/src/plugins/builtin/credit-conditions/model.ts +4 -0
  227. package/src/plugins/builtin/credit-conditions/series.ts +3 -1
  228. package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
  229. package/src/plugins/builtin/credit-documents/export.ts +173 -0
  230. package/src/plugins/builtin/credit-documents/headless.ts +10 -10
  231. package/src/plugins/builtin/credit-documents/model.ts +104 -14
  232. package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
  233. package/src/plugins/builtin/custom-view/sources.ts +2 -0
  234. package/src/plugins/builtin/dividend-yield/client.ts +131 -23
  235. package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
  236. package/src/plugins/builtin/dividend-yield/model.ts +8 -5
  237. package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
  238. package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
  239. package/src/plugins/builtin/dividend-yield/types.ts +20 -2
  240. package/src/plugins/builtin/dividend-yield/view.ts +30 -2
  241. package/src/plugins/builtin/docs/targets.ts +5 -1
  242. package/src/plugins/builtin/doe/headless.ts +3 -2
  243. package/src/plugins/builtin/doe/pane.tsx +13 -3
  244. package/src/plugins/builtin/earnings/board-model.ts +16 -15
  245. package/src/plugins/builtin/earnings/board.tsx +62 -71
  246. package/src/plugins/builtin/earnings/headless.ts +40 -4
  247. package/src/plugins/builtin/earnings/scope.ts +31 -43
  248. package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
  249. package/src/plugins/builtin/earnings-calls/data.ts +18 -6
  250. package/src/plugins/builtin/earnings-calls/format.ts +31 -2
  251. package/src/plugins/builtin/earnings-calls/headless.ts +20 -5
  252. package/src/plugins/builtin/earnings-calls/model.ts +6 -1
  253. package/src/plugins/builtin/earnings-calls/pane.tsx +12 -4
  254. package/src/plugins/builtin/econ/calendar-model.ts +48 -1
  255. package/src/plugins/builtin/econ/calendar-source.ts +4 -2
  256. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  257. package/src/plugins/builtin/econ/figures.ts +48 -0
  258. package/src/plugins/builtin/econ/index.tsx +14 -5
  259. package/src/plugins/builtin/econ/print-watch.ts +3 -1
  260. package/src/plugins/builtin/econ/types.ts +2 -0
  261. package/src/plugins/builtin/econ-statistics/client.ts +11 -0
  262. package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
  263. package/src/plugins/builtin/econ-statistics/headless.ts +16 -6
  264. package/src/plugins/builtin/econ-statistics/pane.tsx +8 -4
  265. package/src/plugins/builtin/econ-statistics/stats.ts +39 -9
  266. package/src/plugins/builtin/equity-screener/categories.ts +179 -0
  267. package/src/plugins/builtin/equity-screener/client.ts +4 -2
  268. package/src/plugins/builtin/equity-screener/headless.ts +144 -13
  269. package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
  270. package/src/plugins/builtin/equity-screener/model.ts +99 -10
  271. package/src/plugins/builtin/equity-screener/pane.tsx +68 -13
  272. package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
  273. package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
  274. package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
  275. package/src/plugins/builtin/fx-matrix/client.ts +6 -3
  276. package/src/plugins/builtin/fx-matrix/headless.ts +1 -1
  277. package/src/plugins/builtin/fx-matrix/index.tsx +4 -2
  278. package/src/plugins/builtin/fx-matrix/live-legs.ts +42 -3
  279. package/src/plugins/builtin/gpu/headless.ts +8 -2
  280. package/src/plugins/builtin/help/function-index.ts +4 -1
  281. package/src/plugins/builtin/help/index.tsx +62 -59
  282. package/src/plugins/builtin/help/shortcut-model.ts +1 -1
  283. package/src/plugins/builtin/help/shortcut-table.tsx +5 -6
  284. package/src/plugins/builtin/help/table-cells.tsx +13 -2
  285. package/src/plugins/builtin/holders/beneficial-client.ts +5 -1
  286. package/src/plugins/builtin/holders/beneficial-view.tsx +3 -0
  287. package/src/plugins/builtin/holders/fund-holders.ts +51 -0
  288. package/src/plugins/builtin/holders/headless.ts +22 -3
  289. package/src/plugins/builtin/holders/pane.tsx +3 -0
  290. package/src/plugins/builtin/holders/report-rows.ts +4 -1
  291. package/src/plugins/builtin/insider/headless.ts +2 -2
  292. package/src/plugins/builtin/ipo-calendar/client.ts +18 -2
  293. package/src/plugins/builtin/ipo-calendar/headless.ts +75 -11
  294. package/src/plugins/builtin/ipo-calendar/listing-ipo.ts +160 -0
  295. package/src/plugins/builtin/ipo-calendar/model.ts +98 -2
  296. package/src/plugins/builtin/iv-history/client.ts +52 -11
  297. package/src/plugins/builtin/iv-history/format.ts +36 -23
  298. package/src/plugins/builtin/iv-history/headless.ts +33 -18
  299. package/src/plugins/builtin/iv-history/index.tsx +2 -2
  300. package/src/plugins/builtin/iv-history/model.ts +163 -27
  301. package/src/plugins/builtin/iv-history/pane.tsx +10 -6
  302. package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
  303. package/src/plugins/builtin/iv-history/universe.ts +5 -1
  304. package/src/plugins/builtin/jobs/client.ts +25 -29
  305. package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
  306. package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
  307. package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
  308. package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
  309. package/src/plugins/builtin/macro-day/headless.ts +12 -5
  310. package/src/plugins/builtin/macro-day/index.tsx +1 -1
  311. package/src/plugins/builtin/macro-day/model.ts +39 -5
  312. package/src/plugins/builtin/macro-day/pane.tsx +27 -19
  313. package/src/plugins/builtin/macro-day/release-table.ts +2 -2
  314. package/src/plugins/builtin/market-heatmap/data.ts +8 -33
  315. package/src/plugins/builtin/market-movers/client.ts +27 -7
  316. package/src/plugins/builtin/market-movers/headless.ts +34 -12
  317. package/src/plugins/builtin/market-movers/index.tsx +26 -14
  318. package/src/plugins/builtin/market-movers/min-float.ts +74 -0
  319. package/src/plugins/builtin/market-movers/model.ts +4 -1
  320. package/src/plugins/builtin/market-movers/screener.ts +22 -4
  321. package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
  322. package/src/plugins/builtin/market-movers/session-table.ts +4 -3
  323. package/src/plugins/builtin/market-movers/table.ts +17 -5
  324. package/src/plugins/builtin/members/breadth-pane.tsx +2 -1
  325. package/src/plugins/builtin/mna/headless.ts +2 -0
  326. package/src/plugins/builtin/mna/pane.tsx +3 -1
  327. package/src/plugins/builtin/news/headless.ts +3 -1
  328. package/src/plugins/builtin/news/index.tsx +4 -0
  329. package/src/plugins/builtin/news/wire/categories.ts +19 -3
  330. package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
  331. package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
  332. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
  333. package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
  334. package/src/plugins/builtin/openfda/client.ts +14 -2
  335. package/src/plugins/builtin/openfda/index.tsx +3 -2
  336. package/src/plugins/builtin/openfda/model.ts +9 -0
  337. package/src/plugins/builtin/openfda/pane.tsx +42 -6
  338. package/src/plugins/builtin/options/analytics.ts +29 -5
  339. package/src/plugins/builtin/options/enrichment.ts +17 -5
  340. package/src/plugins/builtin/options/table.ts +5 -2
  341. package/src/plugins/builtin/options/types.ts +2 -0
  342. package/src/plugins/builtin/options/view.tsx +17 -4
  343. package/src/plugins/builtin/options-positioning/client.ts +11 -0
  344. package/src/plugins/builtin/options-positioning/headless.ts +21 -8
  345. package/src/plugins/builtin/options-positioning/index.tsx +1 -1
  346. package/src/plugins/builtin/options-positioning/model.ts +78 -9
  347. package/src/plugins/builtin/options-positioning/pane.tsx +21 -6
  348. package/src/plugins/builtin/options-scenario/client.ts +116 -47
  349. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
  350. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
  351. package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
  352. package/src/plugins/builtin/options-scenario/csp.ts +8 -2
  353. package/src/plugins/builtin/options-scenario/index.tsx +9 -7
  354. package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
  355. package/src/plugins/builtin/pe-band/model.ts +12 -49
  356. package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
  357. package/src/plugins/builtin/perps/cascade.ts +236 -0
  358. package/src/plugins/builtin/perps/client.ts +56 -2
  359. package/src/plugins/builtin/perps/headless.ts +65 -9
  360. package/src/plugins/builtin/perps/index.tsx +7 -5
  361. package/src/plugins/builtin/perps/model.ts +18 -4
  362. package/src/plugins/builtin/perps/pane.tsx +19 -22
  363. package/src/plugins/builtin/perps/status-line.ts +25 -0
  364. package/src/plugins/builtin/plugin-module.ts +5 -1
  365. package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
  366. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
  367. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
  368. package/src/plugins/builtin/portfolio-list/cli/render.ts +7 -5
  369. package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
  370. package/src/plugins/builtin/portfolio-list/pane/index.tsx +6 -7
  371. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
  372. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
  373. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +0 -14
  374. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +5 -2
  375. package/src/plugins/builtin/portfolio-list/table.tsx +15 -9
  376. package/src/plugins/builtin/primary-dealers/client.ts +13 -38
  377. package/src/plugins/builtin/realized-vol/client.ts +13 -5
  378. package/src/plugins/builtin/realized-vol/evidence.ts +29 -4
  379. package/src/plugins/builtin/realized-vol/headless.ts +8 -4
  380. package/src/plugins/builtin/realized-vol/index.tsx +2 -2
  381. package/src/plugins/builtin/realized-vol/model.ts +37 -6
  382. package/src/plugins/builtin/realized-vol/pane.tsx +11 -9
  383. package/src/plugins/builtin/relative-rotation/client.ts +18 -4
  384. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  385. package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
  386. package/src/plugins/builtin/relative-rotation/model.ts +16 -40
  387. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
  388. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
  389. package/src/plugins/builtin/research/event-model.ts +40 -12
  390. package/src/plugins/builtin/research/headless.ts +1 -1
  391. package/src/plugins/builtin/research/index.tsx +7 -3
  392. package/src/plugins/builtin/research/relative-valuation-headless.ts +13 -26
  393. package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
  394. package/src/plugins/builtin/research/relative-valuation-pane.tsx +59 -20
  395. package/src/plugins/builtin/research/relative-valuation-peers.ts +217 -20
  396. package/src/plugins/builtin/research/relative-valuation-settings.ts +44 -0
  397. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
  398. package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
  399. package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
  400. package/src/plugins/builtin/risk-factors/data.ts +4 -2
  401. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  402. package/src/plugins/builtin/roadmap/index.tsx +28 -0
  403. package/src/plugins/builtin/roadmap/model.ts +67 -0
  404. package/src/plugins/builtin/roadmap/pane.tsx +244 -0
  405. package/src/plugins/builtin/scanner/flow-model.ts +50 -4
  406. package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
  407. package/src/plugins/builtin/scanner/flow-session.ts +146 -0
  408. package/src/plugins/builtin/scanner/headless.ts +204 -35
  409. package/src/plugins/builtin/scanner/index.ts +16 -5
  410. package/src/plugins/builtin/scanner/live-headless.ts +48 -5
  411. package/src/plugins/builtin/scanner/live-model.ts +11 -0
  412. package/src/plugins/builtin/scanner/live-pane.tsx +3 -3
  413. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  414. package/src/plugins/builtin/seasonality/headless.ts +10 -1
  415. package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
  416. package/src/plugins/builtin/sec/fund-facts.ts +116 -34
  417. package/src/plugins/builtin/sec/headless.ts +4 -2
  418. package/src/plugins/builtin/sec/index.tsx +3 -0
  419. package/src/plugins/builtin/series-inventory.ts +2 -0
  420. package/src/plugins/builtin/shared/cloud-resource.ts +16 -7
  421. package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
  422. package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
  423. package/src/plugins/builtin/shared/earnings-report.ts +2 -2
  424. package/src/plugins/builtin/shared/headless-market-data.ts +2 -2
  425. package/src/plugins/builtin/shared/job-posting.ts +1 -0
  426. package/src/plugins/builtin/shared/members-menu.tsx +3 -2
  427. package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
  428. package/src/plugins/builtin/shared/report-freshness.ts +11 -2
  429. package/src/plugins/builtin/shared/research-cells.tsx +1 -1
  430. package/src/plugins/builtin/shared/series-cache.ts +4 -4
  431. package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
  432. package/src/plugins/builtin/shared/ticker-company.ts +77 -0
  433. package/src/plugins/builtin/shared/use-quote-board.ts +2 -1
  434. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
  435. package/src/plugins/builtin/shared/volatility/pricing.ts +19 -0
  436. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
  437. package/src/plugins/builtin/themes/model.ts +16 -7
  438. package/src/plugins/builtin/themes/pane.tsx +5 -5
  439. package/src/plugins/builtin/thirteenf/api.ts +12 -1
  440. package/src/plugins/builtin/thirteenf/data.ts +7 -7
  441. package/src/plugins/builtin/thirteenf/headless.ts +31 -4
  442. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
  443. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
  444. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
  445. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
  446. package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
  447. package/src/plugins/builtin/ticker-detail/overview/model.ts +11 -3
  448. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +2 -1
  449. package/src/plugins/builtin/ticker-detail/pane.tsx +14 -8
  450. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
  451. package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
  452. package/src/plugins/builtin/time-sales/pane.tsx +31 -19
  453. package/src/plugins/builtin/treasury-auctions/headless.ts +21 -12
  454. package/src/plugins/builtin/treasury-auctions/pane.tsx +9 -3
  455. package/src/plugins/builtin/treasury-daily/client.ts +41 -5
  456. package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
  457. package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
  458. package/src/plugins/builtin/treasury-daily/model.ts +50 -3
  459. package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
  460. package/src/plugins/builtin/vol-surface/client.ts +3 -13
  461. package/src/plugins/builtin/vol-surface/headless.ts +3 -3
  462. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  463. package/src/plugins/builtin/vol-surface/pane.tsx +7 -5
  464. package/src/plugins/builtin/volatility/index.tsx +2 -2
  465. package/src/plugins/builtin/volatility/pane.tsx +7 -4
  466. package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
  467. package/src/plugins/builtin/yield-curve/client.ts +61 -1
  468. package/src/plugins/builtin/yield-curve/headless.ts +9 -6
  469. package/src/plugins/builtin/yield-curve/index.tsx +34 -10
  470. package/src/plugins/builtin/yield-curve/swaps.tsx +9 -5
  471. package/src/plugins/catalog-browser.ts +2 -1
  472. package/src/plugins/catalog-manifest.ts +2 -1
  473. package/src/plugins/host-link.ts +1 -1
  474. package/src/plugins/marketplace/model.ts +18 -5
  475. package/src/portfolio/column-values.ts +11 -7
  476. package/src/portfolio/futures-positions.ts +6 -3
  477. package/src/portfolio/position-quantity.ts +15 -9
  478. package/src/remote/resources.ts +2 -1
  479. package/src/renderers/dom/data-table/row.tsx +26 -0
  480. package/src/renderers/dom/desktop/icons.tsx +7 -0
  481. package/src/renderers/dom/desktop/list-view.tsx +13 -10
  482. package/src/renderers/dom/desktop/query-bar.tsx +4 -2
  483. package/src/renderers/dom/desktop/select-field.tsx +4 -2
  484. package/src/renderers/dom/host/chart-surface.tsx +21 -0
  485. package/src/renderers/electrobun/bun/desktop/app-name.ts +3 -3
  486. package/src/renderers/electrobun/bun/desktop/backend.ts +21 -10
  487. package/src/renderers/electrobun/bun/desktop/host-requests.ts +15 -0
  488. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
  489. package/src/renderers/electrobun/bun/desktop/installer-folder.ts +731 -0
  490. package/src/renderers/electrobun/bun/desktop/relaunch.ts +102 -10
  491. package/src/renderers/electrobun/bun/desktop/update.ts +115 -14
  492. package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
  493. package/src/renderers/electrobun/bun/desktop/windows-script.ts +92 -0
  494. package/src/renderers/electrobun/bun/index.ts +26 -2
  495. package/src/renderers/electrobun/bun/window/create.ts +43 -4
  496. package/src/renderers/electrobun/bun/window/events.ts +20 -0
  497. package/src/renderers/electrobun/bun/window/windows-custom-chrome.ts +9 -2
  498. package/src/renderers/electrobun/bun/window/windows-native.ts +78 -0
  499. package/src/renderers/electrobun/bun/window/windows-quit.ts +57 -0
  500. package/src/renderers/electrobun/shared/app-name.ts +13 -0
  501. package/src/renderers/electrobun/shared/protocol.ts +12 -0
  502. package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
  503. package/src/renderers/electrobun/view/main.tsx +24 -1
  504. package/src/renderers/electrobun/view/news-source.ts +5 -1
  505. package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
  506. package/src/renderers/electrobun/view/ui-host.tsx +8 -0
  507. package/src/renderers/electrobun/view/view-ids.ts +85 -0
  508. package/src/renderers/opentui/start.tsx +5 -0
  509. package/src/series/registry.ts +1 -0
  510. package/src/series/types.ts +6 -0
  511. package/src/series/validate.ts +1 -0
  512. package/src/sources/gloomberb-cloud/fred-series.ts +3 -1
  513. package/src/sources/gloomberb-cloud/index.ts +43 -8
  514. package/src/sources/gloomberb-cloud/news.ts +47 -7
  515. package/src/sources/provider-router/financials.ts +15 -3
  516. package/src/sources/provider-router/index.ts +8 -5
  517. package/src/state/ticker-cleanup.ts +50 -0
  518. package/src/sync/controller.ts +4 -3
  519. package/src/sync/core-contributors.ts +90 -31
  520. package/src/telemetry/stall-reports-dom.ts +38 -0
  521. package/src/telemetry/stall-reports-node.ts +75 -0
  522. package/src/telemetry/stall-reports.ts +214 -0
  523. package/src/test-support/preload.ts +14 -0
  524. package/src/theme/themes.ts +10 -0
  525. package/src/tickers/company-name-listing.ts +53 -13
  526. package/src/tickers/company-name.ts +50 -4
  527. package/src/tickers/index-roots.ts +7 -0
  528. package/src/tickers/listing-fate.ts +2 -2
  529. package/src/tickers/listing-venues.ts +15 -3
  530. package/src/tickers/search/index.ts +6 -2
  531. package/src/tickers/search/ranking.ts +5 -1
  532. package/src/tickers/search/upsert.ts +13 -0
  533. package/src/tickers/terminal-codes.ts +19 -0
  534. package/src/tickers/ticker-changes.ts +8 -4
  535. package/src/time-series/alignment.ts +1 -1
  536. package/src/time-series/economic.ts +18 -4
  537. package/src/time-series/field-catalog.ts +1 -1
  538. package/src/time-series/filed-trailing-eps.ts +197 -0
  539. package/src/time-series/fundamentals.ts +44 -1
  540. package/src/time-series/ice-credit.ts +3 -0
  541. package/src/time-series/price-comparison.ts +30 -2
  542. package/src/time-series/resolve-context.ts +3 -1
  543. package/src/time-series/resolve.ts +56 -4
  544. package/src/time-series/session-bar.ts +11 -0
  545. package/src/time-series/spec.ts +3 -2
  546. package/src/time-series/studies.ts +8 -2
  547. package/src/time-series/total-return.ts +160 -0
  548. package/src/time-series/transforms.ts +2 -3
  549. package/src/time-series/types.ts +22 -1
  550. package/src/types/company-from.ts +40 -0
  551. package/src/types/data-provider.ts +7 -0
  552. package/src/types/financials.ts +59 -1
  553. package/src/types/headless.ts +27 -3
  554. package/src/types/news-source.ts +1 -0
  555. package/src/types/plugin.ts +9 -0
  556. package/src/ui/host.tsx +3 -0
  557. package/src/updater/failure.ts +4 -1
  558. package/src/utils/cli-output.ts +42 -1
  559. package/src/utils/crypto-pair.ts +165 -7
  560. package/src/utils/declared-dividends.ts +1 -1
  561. package/src/utils/forward-dividend.ts +2 -0
  562. package/src/utils/fundamentals.ts +60 -8
  563. package/src/utils/fx-market-hours.ts +26 -3
  564. package/src/utils/main-thread-stall.ts +35 -2
  565. package/src/utils/market-capitalization.ts +24 -1
  566. package/src/utils/price-history.ts +7 -1
  567. package/src/utils/shell-word.ts +8 -0
  568. package/src/utils/trailing-dividend.ts +53 -4
  569. package/src/version.ts +2 -2
  570. 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
- /** The legend's marker for a series on the desktop: its line as the plot strokes it, or a dot. */
124
- function CompositeSeriesSwatch({ series, cue, filled = true }: {
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 marker = cue ? `${cellPlot ? cue.glyph : cue.sample} ` : DOT_MARKER;
216
- const markerWidth = isDesktopWeb ? cue ? DOM_LINE_CELLS : DOM_DOT_CELLS : displayWidth(marker);
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
- if (dragRef.current?.kind === "pan") {
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
- drawLine(data, width, height, x, highY, x, lowY, candleColor, 1.1);
491
- if (series.source.style === "candles" && openY !== null) {
492
- fillRect(
493
- data,
494
- width,
495
- height,
496
- x - candleWidth / 2,
497
- Math.min(openY, closeY),
498
- x + candleWidth / 2,
499
- Math.max(openY, closeY) + 1,
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.filter((entry) => normalizedPoints(entry).length > 0);
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
- if (series.source.style === "candles" && openRow !== null) {
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
- setCell(rows, x + offset, row, "█");
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>;