gloomberb 0.11.2 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (531) hide show
  1. package/README.md +7 -4
  2. package/README.zh-CN.md +4 -2
  3. package/package.json +28 -1
  4. package/src/api-client/auth.ts +10 -0
  5. package/src/api-client/data.ts +108 -1
  6. package/src/api-client/index.test.ts +127 -0
  7. package/src/api-client/index.ts +134 -1
  8. package/src/api-client/normalizers.ts +39 -0
  9. package/src/api-client/paths.ts +85 -1
  10. package/src/api-client/types.ts +220 -0
  11. package/src/app/pane-runtime/index.tsx +15 -12
  12. package/src/app/pane-runtime/layout-placement.test.ts +27 -0
  13. package/src/app/pane-runtime/layout-placement.ts +13 -0
  14. package/src/app/pane-runtime/plugin-bindings.ts +62 -0
  15. package/src/app.tsx +60 -18
  16. package/src/architecture/browser-entry-graph.test.ts +96 -0
  17. package/src/brokers/profile-form.test.ts +25 -27
  18. package/src/brokers/test-broker.ts +68 -0
  19. package/src/capabilities/chart-series.ts +3 -2
  20. package/src/cli/commands/market.ts +2 -2
  21. package/src/cli/commands/plugins.ts +39 -11
  22. package/src/cli/desktop-pane-shot.ts +285 -16
  23. package/src/cli/index.ts +1 -1
  24. package/src/cli/pane-functions/capabilities.ts +61 -31
  25. package/src/cli/pane-functions/catalog.ts +16 -4
  26. package/src/cli/pane-functions/discovery.ts +1 -0
  27. package/src/cli/pane-functions/headless.test.ts +247 -0
  28. package/src/cli/pane-functions/headless.ts +423 -0
  29. package/src/cli/pane-functions/index.test.ts +9 -0
  30. package/src/cli/pane-functions/index.ts +6 -2
  31. package/src/cli/pane-functions/options.ts +27 -2
  32. package/src/cli/pane-functions/report.ts +4 -0
  33. package/src/cli/pane-functions/resolver.ts +26 -8
  34. package/src/cli/pane-functions/screenshot.test.ts +76 -0
  35. package/src/cli/pane-functions/screenshot.ts +236 -20
  36. package/src/cli/registry.ts +0 -22
  37. package/src/cli/restore-plugins.ts +34 -0
  38. package/src/cli/result.ts +7 -10
  39. package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
  40. package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
  41. package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
  42. package/src/components/chart/composite/composite-chart.test.tsx +68 -20
  43. package/src/components/chart/composite/composite-chart.tsx +301 -204
  44. package/src/components/chart/composite/format.ts +8 -2
  45. package/src/components/chart/composite/interactions.test.ts +68 -102
  46. package/src/components/chart/composite/interactions.ts +214 -264
  47. package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
  48. package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
  49. package/src/components/chart/composite/scene.ts +57 -3
  50. package/src/components/chart/composite/text-renderer.ts +3 -2
  51. package/src/components/chart/composite/time-scale.ts +138 -57
  52. package/src/components/chart/composite/types.ts +25 -1
  53. package/src/components/chart/core/data.ts +9 -13
  54. package/src/components/chart/core/date-window.ts +7 -1
  55. package/src/components/chart/core/pointer.ts +3 -0
  56. package/src/components/chart/core/types.ts +0 -45
  57. package/src/components/chart/native/chart-rasterizer.ts +0 -1
  58. package/src/components/chart/native/kitty/index.test.ts +0 -188
  59. package/src/components/chart/native/raster/crosshair.ts +6 -5
  60. package/src/components/chart/native/raster/primitives.ts +0 -23
  61. package/src/components/chart/native/renderer-selection.ts +81 -31
  62. package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
  63. package/src/components/chart/static/chart-surface.tsx +150 -0
  64. package/src/components/chart/static/index.ts +8 -0
  65. package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
  66. package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
  67. package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
  68. package/src/components/chart/static/series.ts +49 -0
  69. package/src/components/command-bar/action-runtime.ts +1 -3
  70. package/src/components/command-bar/assist/model.test.ts +17 -2
  71. package/src/components/command-bar/assist/model.ts +40 -9
  72. package/src/components/command-bar/commands/direct/index.ts +1 -1
  73. package/src/components/command-bar/commands/direct/runtime.ts +1 -1
  74. package/src/components/command-bar/commands/registry.ts +0 -11
  75. package/src/components/command-bar/helpers.test.ts +2 -2
  76. package/src/components/command-bar/helpers.ts +2 -6
  77. package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
  78. package/src/components/command-bar/list/badge.test.ts +54 -0
  79. package/src/components/command-bar/list/badge.ts +62 -0
  80. package/src/components/command-bar/list/model.test.ts +103 -0
  81. package/src/components/command-bar/list/model.ts +61 -4
  82. package/src/components/command-bar/list/view.test.tsx +113 -0
  83. package/src/components/command-bar/list/view.tsx +147 -131
  84. package/src/components/command-bar/pane-templates/workflow.ts +1 -1
  85. package/src/components/command-bar/panel/body.tsx +51 -65
  86. package/src/components/command-bar/panel/index.tsx +48 -13
  87. package/src/components/command-bar/panel/layout.test.ts +139 -0
  88. package/src/components/command-bar/panel/layout.ts +65 -28
  89. package/src/components/command-bar/panel/native-surface.ts +24 -0
  90. package/src/components/command-bar/panel/prompt-binding.ts +54 -0
  91. package/src/components/command-bar/panel/runtime.ts +6 -3
  92. package/src/components/command-bar/panel/state.ts +41 -7
  93. package/src/components/command-bar/panel/types.ts +7 -1
  94. package/src/components/command-bar/routes/root/provider-search.ts +34 -15
  95. package/src/components/command-bar/routes/root/results.test.ts +161 -0
  96. package/src/components/command-bar/routes/root/results.ts +30 -28
  97. package/src/components/command-bar/routes/root/runtime.ts +37 -20
  98. package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
  99. package/src/components/command-bar/routes/root/search-providers.ts +205 -0
  100. package/src/components/command-bar/routes/root/selection.ts +1 -12
  101. package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
  102. package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
  103. package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
  104. package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
  105. package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
  106. package/src/components/command-bar/routing/actions.ts +1 -81
  107. package/src/components/command-bar/routing/launch-request.ts +1 -1
  108. package/src/components/command-bar/routing/list-state.ts +6 -21
  109. package/src/components/command-bar/selection-runtime.ts +1 -1
  110. package/src/components/command-bar/surface/assist.test.tsx +14 -10
  111. package/src/components/command-bar/surface/environment.ts +8 -1
  112. package/src/components/command-bar/surface/index.test.tsx +84 -68
  113. package/src/components/command-bar/surface/index.tsx +33 -36
  114. package/src/components/command-bar/surface/test-harness.tsx +15 -3
  115. package/src/components/command-bar/theme-picker.tsx +43 -15
  116. package/src/components/command-bar/view-model.test.ts +53 -8
  117. package/src/components/command-bar/view-model.ts +43 -14
  118. package/src/components/command-bar/workflow/tickers.ts +1 -0
  119. package/src/components/command-bar/workflow/types.ts +1 -1
  120. package/src/components/data-table/export.test.ts +21 -0
  121. package/src/components/data-table/export.ts +21 -0
  122. package/src/components/data-table/view.test.tsx +45 -6
  123. package/src/components/data-table/view.tsx +55 -9
  124. package/src/components/index.ts +10 -2
  125. package/src/components/input-search-bar.test.tsx +36 -2
  126. package/src/components/input-search-bar.tsx +18 -1
  127. package/src/components/layout/header.test.tsx +57 -10
  128. package/src/components/layout/header.tsx +292 -149
  129. package/src/components/layout/market-summary.test.ts +81 -0
  130. package/src/components/layout/market-summary.ts +109 -0
  131. package/src/components/layout/shell/chrome.ts +96 -1
  132. package/src/components/layout/shell/index.tsx +11 -3
  133. package/src/components/layout/shell/menu.ts +9 -0
  134. package/src/components/layout/shell/pane/actions.ts +29 -0
  135. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  136. package/src/components/layout/shell/shortcuts.ts +3 -0
  137. package/src/components/layout/status-bar.test.tsx +9 -8
  138. package/src/components/layout/status-bar.tsx +85 -54
  139. package/src/components/onboarding/onboarding-frame.tsx +2 -26
  140. package/src/components/price-sparkline/index.ts +61 -34
  141. package/src/components/price-sparkline/view.tsx +1 -1
  142. package/src/components/sign-in-gate.test.tsx +128 -0
  143. package/src/components/sign-in-gate.tsx +149 -0
  144. package/src/components/speedometer-gauge/desktop.tsx +2 -12
  145. package/src/components/ticker/badge/list.tsx +17 -3
  146. package/src/components/ticker/list-table-view.tsx +6 -3
  147. package/src/components/ui/data-table/index.tsx +22 -4
  148. package/src/components/ui/data-table/opentui/index.tsx +165 -120
  149. package/src/components/ui/frame.tsx +28 -0
  150. package/src/components/ui/index.ts +3 -0
  151. package/src/components/ui/native-select.tsx +20 -10
  152. package/src/components/ui/select-button.tsx +138 -0
  153. package/src/components/ui/status.tsx +2 -2
  154. package/src/components/ui/table-layout.ts +1 -1
  155. package/src/components/use-double-click-activation.ts +7 -3
  156. package/src/core/app-services.ts +3 -3
  157. package/src/data/config/chart-settings.ts +4 -4
  158. package/src/data/config/layout.ts +12 -6
  159. package/src/data/config/store/index.test.ts +97 -0
  160. package/src/data/config/store/migrations.ts +41 -0
  161. package/src/data/config/store/normalize.ts +5 -3
  162. package/src/i18n/es.ts +6 -2
  163. package/src/i18n/ja.ts +6 -2
  164. package/src/i18n/ko.ts +6 -2
  165. package/src/i18n/zh-cn.ts +6 -2
  166. package/src/i18n/zh-tw.ts +6 -2
  167. package/src/layout-marketplace/gallery-terminal.tsx +126 -96
  168. package/src/layout-marketplace/gallery.test.tsx +65 -1
  169. package/src/layout-marketplace/pane-imagery.ts +1 -0
  170. package/src/layout-marketplace/payload.test.ts +17 -0
  171. package/src/layout-marketplace/payload.ts +40 -5
  172. package/src/market-data/coordinator/index.ts +3 -2
  173. package/src/market-data/coordinator/quotes.ts +173 -66
  174. package/src/market-data/coordinator/subscriptions.test.ts +215 -37
  175. package/src/pane-settings.ts +1 -1
  176. package/src/plugins/builtin/analytics/pane-model.ts +1 -1
  177. package/src/plugins/builtin/analytics/view.tsx +1 -1
  178. package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
  179. package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
  180. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
  181. package/src/plugins/builtin/chart-composer/cli-options.ts +12 -9
  182. package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
  183. package/src/plugins/builtin/chart-composer/settings.ts +2 -13
  184. package/src/plugins/builtin/chat/content/composer.tsx +8 -4
  185. package/src/plugins/builtin/cloud/auth-dialog.tsx +14 -228
  186. package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
  187. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  188. package/src/plugins/builtin/composite-plugins.ts +16 -1
  189. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  190. package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
  191. package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
  192. package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
  193. package/src/plugins/builtin/credit-conditions/index.tsx +2 -21
  194. package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
  195. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  196. package/src/plugins/builtin/earnings/index.tsx +1 -0
  197. package/src/plugins/builtin/earnings-calls/data.ts +223 -0
  198. package/src/plugins/builtin/earnings-calls/format.ts +49 -0
  199. package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
  200. package/src/plugins/builtin/earnings-calls/pane.tsx +756 -0
  201. package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
  202. package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
  203. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +334 -0
  204. package/src/plugins/builtin/econ/detail-view.tsx +1 -2
  205. package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
  206. package/src/plugins/builtin/econ/index.tsx +3 -1
  207. package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
  208. package/src/plugins/builtin/econ-statistics/client.ts +92 -0
  209. package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
  210. package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
  211. package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
  212. package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
  213. package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
  214. package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
  215. package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
  216. package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
  217. package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
  218. package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
  219. package/src/plugins/builtin/econ-statistics/view.ts +90 -0
  220. package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
  221. package/src/plugins/builtin/fear-greed/data.ts +6 -1
  222. package/src/plugins/builtin/futures/index.tsx +1 -0
  223. package/src/plugins/builtin/fx-matrix/index.tsx +1 -0
  224. package/src/plugins/builtin/help/index.tsx +4 -0
  225. package/src/plugins/builtin/holders/index.tsx +1 -0
  226. package/src/plugins/builtin/insider/index.tsx +1 -0
  227. package/src/plugins/builtin/ipo-calendar/index.tsx +1 -0
  228. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  229. package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
  230. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  231. package/src/plugins/builtin/market-movers/index.tsx +1 -0
  232. package/src/plugins/builtin/market-valuation/align.ts +122 -0
  233. package/src/plugins/builtin/market-valuation/cache.ts +32 -0
  234. package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
  235. package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
  236. package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
  237. package/src/plugins/builtin/market-valuation/client.ts +116 -0
  238. package/src/plugins/builtin/market-valuation/defs.ts +251 -0
  239. package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
  240. package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
  241. package/src/plugins/builtin/market-valuation/headless.ts +170 -0
  242. package/src/plugins/builtin/market-valuation/index.tsx +64 -0
  243. package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
  244. package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
  245. package/src/plugins/builtin/market-valuation/model.ts +8 -0
  246. package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
  247. package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
  248. package/src/plugins/builtin/market-valuation/series.ts +12 -0
  249. package/src/plugins/builtin/market-valuation/settings.ts +38 -0
  250. package/src/plugins/builtin/market-valuation/sources.ts +130 -0
  251. package/src/plugins/builtin/market-valuation/trend.ts +89 -0
  252. package/src/plugins/builtin/market-valuation/view.ts +137 -0
  253. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
  254. package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
  255. package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
  256. package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
  257. package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
  258. package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
  259. package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
  260. package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
  261. package/src/plugins/builtin/news/wire/index.tsx +31 -44
  262. package/src/plugins/builtin/options/analytics.test.ts +83 -0
  263. package/src/plugins/builtin/options/analytics.ts +110 -0
  264. package/src/plugins/builtin/options/index.tsx +18 -2
  265. package/src/plugins/builtin/options/table.ts +121 -51
  266. package/src/plugins/builtin/options/types.ts +22 -15
  267. package/src/plugins/builtin/options/view.test.tsx +53 -1
  268. package/src/plugins/builtin/options/view.tsx +92 -41
  269. package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
  270. package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
  271. package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
  272. package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
  273. package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
  274. package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
  275. package/src/plugins/builtin/plugin-module.test.ts +2 -4
  276. package/src/plugins/builtin/plugin-module.ts +9 -8
  277. package/src/plugins/builtin/portfolio-list/index.tsx +1 -0
  278. package/src/plugins/builtin/research/index.tsx +4 -0
  279. package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
  280. package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
  281. package/src/plugins/builtin/research-search/data.ts +109 -0
  282. package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
  283. package/src/plugins/builtin/research-search/document-view.tsx +254 -0
  284. package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
  285. package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
  286. package/src/plugins/builtin/research-search/index.tsx +85 -0
  287. package/src/plugins/builtin/research-search/model.test.ts +40 -0
  288. package/src/plugins/builtin/research-search/model.ts +291 -0
  289. package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
  290. package/src/plugins/builtin/research-search/pane.tsx +678 -0
  291. package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
  292. package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
  293. package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
  294. package/src/plugins/builtin/research-search/snippet.ts +134 -0
  295. package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
  296. package/src/plugins/builtin/scanner/flow-model.ts +29 -1
  297. package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
  298. package/src/plugins/builtin/scanner/index.ts +2 -0
  299. package/src/plugins/builtin/sec/index.tsx +1 -0
  300. package/src/plugins/builtin/sectors/index.tsx +1 -0
  301. package/src/plugins/builtin/shared/series-cache.ts +93 -0
  302. package/src/plugins/builtin/short-interest/index.tsx +1 -0
  303. package/src/plugins/builtin/short-interest/pane.tsx +1 -1
  304. package/src/plugins/builtin/thirteenf/index.tsx +1 -0
  305. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +3 -5
  306. package/src/plugins/builtin/ticker-detail/index.tsx +2 -14
  307. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
  308. package/src/plugins/builtin/treasury-auctions/index.tsx +1 -0
  309. package/src/plugins/builtin/volatility/index.tsx +1 -1
  310. package/src/plugins/builtin/world-indices/index.tsx +1 -0
  311. package/src/plugins/builtin/world-venue-map/index.ts +27 -0
  312. package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
  313. package/src/plugins/builtin/world-venue-map/map.tsx +481 -0
  314. package/src/plugins/builtin/world-venue-map/model.test.ts +85 -0
  315. package/src/plugins/builtin/world-venue-map/model.ts +268 -0
  316. package/src/plugins/builtin/world-venue-map/pane.tsx +300 -0
  317. package/src/plugins/builtin/world-venue-map/world-outlines.ts +2 -0
  318. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  319. package/src/plugins/bundle.test.ts +109 -0
  320. package/src/plugins/bundle.ts +139 -0
  321. package/src/plugins/catalog-backend.ts +6 -44
  322. package/src/plugins/catalog-browser.test.ts +1 -0
  323. package/src/plugins/catalog-browser.ts +12 -0
  324. package/src/plugins/catalog-manifest.test.ts +43 -0
  325. package/src/plugins/catalog-manifest.ts +92 -0
  326. package/src/plugins/catalog-ui.ts +20 -4
  327. package/src/plugins/catalog.ts +9 -2
  328. package/src/plugins/current-target.ts +28 -0
  329. package/src/plugins/host-contract.ts +37 -0
  330. package/src/plugins/host-link.ts +158 -0
  331. package/src/plugins/host-modules.ts +61 -0
  332. package/src/plugins/loader.ts +41 -22
  333. package/src/plugins/ownership.ts +1 -0
  334. package/src/plugins/prediction-markets/index.tsx +1 -0
  335. package/src/plugins/registry/context.ts +3 -0
  336. package/src/plugins/registry/contributions.ts +27 -0
  337. package/src/plugins/registry/index.test.ts +25 -0
  338. package/src/plugins/registry/index.ts +8 -0
  339. package/src/plugins/registry/pane-settings.test.ts +53 -0
  340. package/src/plugins/registry/pane-settings.ts +29 -3
  341. package/src/plugins/seed.test.ts +83 -0
  342. package/src/plugins/seed.ts +80 -0
  343. package/src/public/broker.ts +31 -0
  344. package/src/public/market-data.ts +9 -0
  345. package/src/public/pane-hooks.ts +57 -0
  346. package/src/public/public-api.test.ts +285 -0
  347. package/src/public/react.ts +62 -0
  348. package/src/public/test-support.ts +23 -0
  349. package/src/public/time-series.ts +8 -0
  350. package/src/public/utils.ts +77 -0
  351. package/src/remote/layout-helpers.ts +6 -8
  352. package/src/remote/resources.ts +2 -0
  353. package/src/remote/schema.ts +1 -0
  354. package/src/remote/semantic-tree.test.tsx +105 -0
  355. package/src/remote/semantic-tree.tsx +53 -35
  356. package/src/renderers/browser/main.tsx +6 -0
  357. package/src/renderers/browser/ui-host.tsx +11 -0
  358. package/src/renderers/cloudflare/worker.test.ts +19 -0
  359. package/src/renderers/cloudflare/worker.ts +1 -1
  360. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -2
  361. package/src/renderers/electrobun/bun/external-plugins.ts +140 -0
  362. package/src/renderers/electrobun/bun/index.ts +6 -2
  363. package/src/renderers/electrobun/shared/protocol.ts +25 -0
  364. package/src/renderers/electrobun/view/build-assets.ts +0 -1
  365. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +51 -1
  366. package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
  367. package/src/renderers/electrobun/view/external-plugins.ts +63 -0
  368. package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
  369. package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
  370. package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
  371. package/src/renderers/electrobun/view/host/mouse.ts +16 -2
  372. package/src/renderers/electrobun/view/main.tsx +27 -2
  373. package/src/renderers/electrobun/view/styles.css +5 -0
  374. package/src/renderers/electrobun/view/toast-host.tsx +18 -0
  375. package/src/renderers/electrobun/view/ui-host.tsx +3 -0
  376. package/src/renderers/opentui/chart-surface.tsx +5 -1
  377. package/src/renderers/opentui/host.tsx +20 -2
  378. package/src/renderers/opentui/interaction-performance.test.ts +79 -0
  379. package/src/renderers/opentui/interaction-performance.ts +188 -0
  380. package/src/renderers/opentui/start.tsx +26 -1
  381. package/src/renderers/opentui/test-utils.tsx +13 -0
  382. package/src/renderers/opentui/toast-host.tsx +26 -2
  383. package/src/renderers/opentui/ui-host.tsx +1 -0
  384. package/src/sources/provider-router/search.test.ts +191 -0
  385. package/src/sources/provider-router/search.ts +92 -39
  386. package/src/state/app/context/index.tsx +2 -0
  387. package/src/state/app/context/selector.test.tsx +33 -1
  388. package/src/state/hooks/quote-streaming.test.tsx +93 -0
  389. package/src/state/hooks/quote-streaming.ts +106 -30
  390. package/src/state/pane-table-export-registry.ts +50 -0
  391. package/src/theme/color-utils.ts +1 -1
  392. package/src/theme/colors.ts +50 -0
  393. package/src/theme/themes.test.ts +15 -2
  394. package/src/theme/themes.ts +19 -1
  395. package/src/tickers/search/index.test.ts +26 -0
  396. package/src/tickers/search/index.ts +42 -17
  397. package/src/tickers/search/ranking.ts +14 -0
  398. package/src/time-series/chart-data.test.ts +216 -0
  399. package/src/time-series/range.ts +12 -0
  400. package/src/time-series/resolution.test.ts +25 -26
  401. package/src/time-series/resolution.ts +44 -25
  402. package/src/time-series/resolve.ts +7 -0
  403. package/src/time-series/spec.ts +5 -5
  404. package/src/time-series/types.ts +3 -1
  405. package/src/time-series/use-chart-resolution.ts +5 -0
  406. package/src/types/config.ts +123 -3
  407. package/src/types/data-provider.ts +11 -0
  408. package/src/types/headless.ts +161 -0
  409. package/src/types/plugin.ts +104 -20
  410. package/src/ui/host.tsx +19 -1
  411. package/src/ui/index.tsx +1 -0
  412. package/src/ui/toast.tsx +8 -4
  413. package/src/utils/csv.test.ts +13 -0
  414. package/src/utils/csv.ts +37 -0
  415. package/src/utils/format.ts +35 -0
  416. package/src/utils/fuzzy-search.test.ts +94 -0
  417. package/src/utils/fuzzy-search.ts +81 -22
  418. package/src/utils/save-text-file.test.ts +24 -0
  419. package/src/utils/save-text-file.ts +36 -0
  420. package/src/utils/shortcut-labels.ts +14 -0
  421. package/src/components/chart/core/axis-format.ts +0 -157
  422. package/src/components/chart/core/index.test.ts +0 -1075
  423. package/src/components/chart/core/projection.ts +0 -357
  424. package/src/components/chart/core/render-utils.ts +0 -4
  425. package/src/components/chart/core/renderer.ts +0 -244
  426. package/src/components/chart/core/scene.ts +0 -180
  427. package/src/components/chart/core/time-axis.ts +0 -330
  428. package/src/components/chart/cursor-motion.ts +0 -47
  429. package/src/components/chart/indicators/types.ts +0 -26
  430. package/src/components/chart/market-session.ts +0 -176
  431. package/src/components/chart/multi-line/renderer.test.ts +0 -83
  432. package/src/components/chart/multi-line/renderer.ts +0 -161
  433. package/src/components/chart/multi-line/scene.ts +0 -188
  434. package/src/components/chart/native/raster/layout.ts +0 -129
  435. package/src/components/chart/native/raster/price-chart.ts +0 -244
  436. package/src/components/chart/rendering/chart-draw.ts +0 -270
  437. package/src/components/chart/rendering/geometry.ts +0 -95
  438. package/src/components/chart/rendering/pixel-buffer.ts +0 -267
  439. package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
  440. package/src/components/chart/scatter-chart-renderer.ts +0 -223
  441. package/src/components/chart/static/chart/surface.tsx +0 -358
  442. package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
  443. package/src/components/command-bar/routes/root/article-results.ts +0 -55
  444. package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
  445. package/src/plugins/builtin/news/wire/article-search.ts +0 -140
  446. package/src/plugins/builtin/substack/api/auth.ts +0 -185
  447. package/src/plugins/builtin/substack/api/cache.ts +0 -82
  448. package/src/plugins/builtin/substack/api/loaders.ts +0 -170
  449. package/src/plugins/builtin/substack/api/store.ts +0 -186
  450. package/src/plugins/builtin/substack/api/types.ts +0 -42
  451. package/src/plugins/builtin/substack/api.test.ts +0 -298
  452. package/src/plugins/builtin/substack/article-detail.tsx +0 -397
  453. package/src/plugins/builtin/substack/article-stack.tsx +0 -133
  454. package/src/plugins/builtin/substack/content.ts +0 -478
  455. package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
  456. package/src/plugins/builtin/substack/index.tsx +0 -49
  457. package/src/plugins/builtin/substack/login-view.tsx +0 -126
  458. package/src/plugins/builtin/substack/model.test.ts +0 -155
  459. package/src/plugins/builtin/substack/normalize.ts +0 -343
  460. package/src/plugins/builtin/substack/pane-footer.ts +0 -52
  461. package/src/plugins/builtin/substack/pane-state.ts +0 -170
  462. package/src/plugins/builtin/substack/pane.tsx +0 -547
  463. package/src/plugins/builtin/substack/read-state.ts +0 -30
  464. package/src/plugins/builtin/substack/table.ts +0 -111
  465. package/src/plugins/builtin/substack/types.ts +0 -71
  466. package/src/plugins/builtin/substack/utils.ts +0 -131
  467. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
  468. package/src/plugins/ibkr/account-cache.test.ts +0 -102
  469. package/src/plugins/ibkr/account-cache.ts +0 -24
  470. package/src/plugins/ibkr/broker-adapter.ts +0 -286
  471. package/src/plugins/ibkr/config.test.ts +0 -79
  472. package/src/plugins/ibkr/config.ts +0 -250
  473. package/src/plugins/ibkr/dialogs.tsx +0 -40
  474. package/src/plugins/ibkr/flex/index.test.ts +0 -264
  475. package/src/plugins/ibkr/flex/index.ts +0 -367
  476. package/src/plugins/ibkr/flex/statement-client.ts +0 -235
  477. package/src/plugins/ibkr/fundamental-parser.ts +0 -149
  478. package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
  479. package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
  480. package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
  481. package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
  482. package/src/plugins/ibkr/gateway/connection.ts +0 -152
  483. package/src/plugins/ibkr/gateway/contracts.ts +0 -132
  484. package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
  485. package/src/plugins/ibkr/gateway/helpers.ts +0 -33
  486. package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
  487. package/src/plugins/ibkr/gateway/history.ts +0 -120
  488. package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
  489. package/src/plugins/ibkr/gateway/market-data.ts +0 -345
  490. package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
  491. package/src/plugins/ibkr/gateway/orders.ts +0 -63
  492. package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
  493. package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
  494. package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
  495. package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
  496. package/src/plugins/ibkr/gateway/selection.ts +0 -51
  497. package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
  498. package/src/plugins/ibkr/gateway/service/index.ts +0 -429
  499. package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
  500. package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
  501. package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
  502. package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
  503. package/src/plugins/ibkr/gateway/service/native.ts +0 -543
  504. package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
  505. package/src/plugins/ibkr/gateway/types.ts +0 -27
  506. package/src/plugins/ibkr/index.test.tsx +0 -162
  507. package/src/plugins/ibkr/index.tsx +0 -159
  508. package/src/plugins/ibkr/instance-selection.test.ts +0 -120
  509. package/src/plugins/ibkr/instance-selection.ts +0 -36
  510. package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
  511. package/src/plugins/ibkr/portfolio-performance.ts +0 -129
  512. package/src/plugins/ibkr/trade/badge.tsx +0 -28
  513. package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
  514. package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
  515. package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
  516. package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
  517. package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
  518. package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
  519. package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
  520. package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
  521. package/src/plugins/ibkr/trade/tab/model.ts +0 -159
  522. package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
  523. package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
  524. package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
  525. package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
  526. package/src/plugins/ibkr/trade/utils.test.ts +0 -175
  527. package/src/plugins/ibkr/trade/utils.ts +0 -144
  528. package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
  529. package/src/plugins/ibkr/trading/pane.tsx +0 -332
  530. package/src/plugins/ibkr/trading/state.test.ts +0 -109
  531. package/src/plugins/ibkr/trading/state.ts +0 -335
@@ -49,12 +49,31 @@ function pointTimestampForScale(
49
49
  : timestamp;
50
50
  }
51
51
 
52
- function normalizedSourcePoints(series: ResolvedSeries, timeScale?: CompositeTimeScale): Array<{
52
+ interface NormalizedSourcePoint {
53
53
  point: TimeSeriesPoint;
54
54
  timestamp: number;
55
55
  value: number | null;
56
- }> {
57
- const bySourceTimestamp = new Map<number, { point: TimeSeriesPoint; timestamp: number; value: number | null }>();
56
+ }
57
+
58
+ interface NormalizedPointsCache {
59
+ points: readonly TimeSeriesPoint[];
60
+ pointCount: number;
61
+ last: TimeSeriesPoint | undefined;
62
+ lastTimestamp: number | null;
63
+ lastValue: number | null;
64
+ byPlacement: Map<boolean, NormalizedSourcePoint[]>;
65
+ }
66
+
67
+ // Every scene build normalises each series several times over; a viewport
68
+ // change must not re-sort thousands of unchanged points. Live ticks mutate
69
+ // the last point in place, so the cache also keys on its time and value.
70
+ const normalizedPointsCache = new WeakMap<ResolvedSeries, NormalizedPointsCache>();
71
+
72
+ function computeNormalizedSourcePoints(
73
+ series: ResolvedSeries,
74
+ timeScale?: CompositeTimeScale,
75
+ ): NormalizedSourcePoint[] {
76
+ const bySourceTimestamp = new Map<number, NormalizedSourcePoint>();
58
77
  for (const point of series.points) {
59
78
  const sourceTimestamp = pointTime(point);
60
79
  const timestamp = pointTimestampForScale(series, point, timeScale);
@@ -71,6 +90,41 @@ function normalizedSourcePoints(series: ResolvedSeries, timeScale?: CompositeTim
71
90
  ));
72
91
  }
73
92
 
93
+ function normalizedSourcePoints(
94
+ series: ResolvedSeries,
95
+ timeScale?: CompositeTimeScale,
96
+ ): NormalizedSourcePoint[] {
97
+ const effectiveTimes = timeScale?.kind === "market" && !series.timeBasis;
98
+ const last = series.points[series.points.length - 1];
99
+ const lastTimestamp = last ? pointTime(last) : null;
100
+ const lastValue = last ? resolveTimeSeriesPointValue(last) : null;
101
+ let cached = normalizedPointsCache.get(series);
102
+ if (
103
+ !cached
104
+ || cached.points !== series.points
105
+ || cached.pointCount !== series.points.length
106
+ || cached.last !== last
107
+ || cached.lastTimestamp !== lastTimestamp
108
+ || cached.lastValue !== lastValue
109
+ ) {
110
+ cached = {
111
+ points: series.points,
112
+ pointCount: series.points.length,
113
+ last,
114
+ lastTimestamp,
115
+ lastValue,
116
+ byPlacement: new Map(),
117
+ };
118
+ normalizedPointsCache.set(series, cached);
119
+ }
120
+ let normalized = cached.byPlacement.get(effectiveTimes);
121
+ if (!normalized) {
122
+ normalized = computeNormalizedSourcePoints(series, timeScale);
123
+ cached.byPlacement.set(effectiveTimes, normalized);
124
+ }
125
+ return normalized;
126
+ }
127
+
74
128
  function normalizedPoints(series: ResolvedSeries): Array<{
75
129
  point: TimeSeriesPoint;
76
130
  timestamp: number;
@@ -1,4 +1,4 @@
1
- import { compositeAxisTicks } from "./format";
1
+ import { compositeAxisTicks, type CompositeAxisValueFormatter } from "./format";
2
2
  import type { CompositeViewportRange } from "./interactions";
3
3
  import { resolveCompositeObservationWidth } from "./rasterizer";
4
4
  import { buildCompositeColumnLayout, type CompositeColumnLayout } from "./column-layout";
@@ -274,10 +274,11 @@ export function renderCompositeAxisText(
274
274
  height: number,
275
275
  width: number,
276
276
  side: "left" | "right",
277
+ format?: CompositeAxisValueFormatter,
277
278
  ): string[] {
278
279
  const rows = Array.from({ length: Math.max(1, height) }, () => " ".repeat(Math.max(0, width)));
279
280
  if (!domain || width <= 0) return rows;
280
- for (const tick of compositeAxisTicks(domain)) {
281
+ for (const tick of compositeAxisTicks(domain, 3, format)) {
281
282
  const row = clamp(Math.round(tick.ratio * Math.max(height - 1, 0)), 0, Math.max(height - 1, 0));
282
283
  const label = tick.label.length > width ? tick.label.slice(0, width) : tick.label;
283
284
  rows[row] = side === "left" ? label.padStart(width) : label.padEnd(width);
@@ -2,8 +2,27 @@ import type { ResolvedSeries } from "../../../time-series/types";
2
2
  import type { CompositeTimeScale } from "./types";
3
3
 
4
4
  const DAY_MS = 24 * 60 * 60 * 1_000;
5
+ const QUARTER_HOUR_MS = 15 * 60 * 1_000;
5
6
  const MINIMUM_SLOT_FRACTION = 0.25;
6
7
  const dateFormatters = new Map<string, Intl.DateTimeFormat>();
8
+ // Session dates are looked up once per quarter hour, not once per bar: a day
9
+ // of one-minute bars asks the same question hundreds of times.
10
+ const sessionDateBuckets = new Map<string, Map<number, string>>();
11
+ const SESSION_DATE_BUCKET_LIMIT = 200_000;
12
+
13
+ type MarketAnchors = Extract<CompositeTimeScale, { kind: "market" }>["anchors"];
14
+
15
+ interface CachedMarketAnchors {
16
+ points: readonly ResolvedSeries["points"][number][];
17
+ pointCount: number;
18
+ lastTimestamp: number | null;
19
+ timeZone: string;
20
+ configuredCadence: number | undefined;
21
+ cadenceMs: number;
22
+ anchors: MarketAnchors;
23
+ }
24
+
25
+ const anchorCache = new WeakMap<ResolvedSeries, CachedMarketAnchors>();
7
26
 
8
27
  function finiteTimestamp(value: Date): number | null {
9
28
  const timestamp = value.getTime();
@@ -31,7 +50,7 @@ function formatterFor(timeZone: string): Intl.DateTimeFormat {
31
50
  return formatter;
32
51
  }
33
52
 
34
- function sessionDate(timestamp: number, timeZone: string): string {
53
+ function formatSessionDate(timestamp: number, timeZone: string): string {
35
54
  try {
36
55
  const parts = formatterFor(timeZone).formatToParts(new Date(timestamp));
37
56
  const year = parts.find((part) => part.type === "year")?.value;
@@ -44,6 +63,23 @@ function sessionDate(timestamp: number, timeZone: string): string {
44
63
  return new Date(timestamp).toISOString().slice(0, 10);
45
64
  }
46
65
 
66
+ function sessionDate(timestamp: number, timeZone: string): string {
67
+ // Every IANA offset is a multiple of fifteen minutes, so local midnight
68
+ // always lands on a bucket edge and one bucket never spans two sessions.
69
+ let buckets = sessionDateBuckets.get(timeZone);
70
+ if (!buckets) {
71
+ buckets = new Map();
72
+ sessionDateBuckets.set(timeZone, buckets);
73
+ }
74
+ const bucket = Math.floor(timestamp / QUARTER_HOUR_MS);
75
+ const cached = buckets.get(bucket);
76
+ if (cached !== undefined) return cached;
77
+ const date = formatSessionDate(timestamp, timeZone);
78
+ if (buckets.size >= SESSION_DATE_BUCKET_LIMIT) buckets.clear();
79
+ buckets.set(bucket, date);
80
+ return date;
81
+ }
82
+
47
83
  function median(values: readonly number[]): number | null {
48
84
  const sorted = values.filter((value) => Number.isFinite(value) && value > 0)
49
85
  .sort((left, right) => left - right);
@@ -55,7 +91,7 @@ function median(values: readonly number[]): number | null {
55
91
  }
56
92
 
57
93
  function lowerBoundAnchor(
58
- anchors: Extract<CompositeTimeScale, { kind: "market" }>["anchors"],
94
+ anchors: MarketAnchors,
59
95
  timestamp: number,
60
96
  ): number {
61
97
  let low = 0;
@@ -69,7 +105,7 @@ function lowerBoundAnchor(
69
105
  }
70
106
 
71
107
  function positionForTimestamp(
72
- scale: Extract<CompositeTimeScale, { kind: "market" }>,
108
+ scale: Pick<Extract<CompositeTimeScale, { kind: "market" }>, "anchors" | "cadenceMs">,
73
109
  timestamp: number,
74
110
  ): number {
75
111
  const { anchors, cadenceMs } = scale;
@@ -90,69 +126,134 @@ function positionForTimestamp(
90
126
  + (next.position - previous.position) * ((timestamp - previous.timestamp) / elapsed);
91
127
  }
92
128
 
93
- export function buildCompositeTimeScale(
94
- timelineSeries: readonly ResolvedSeries[],
95
- startTime: number,
96
- endTime: number,
97
- ): CompositeTimeScale {
98
- const anchorSeries = timelineSeries.find((series) => series.timeBasis?.kind === "market");
99
- const anchorTimestamps = anchorSeries ? uniqueSeriesTimestamps(anchorSeries) : [];
100
- if (!anchorSeries?.timeBasis || anchorTimestamps.length === 0) {
101
- return { kind: "calendar", startTime, endTime };
102
- }
103
-
104
- const { timeZone } = anchorSeries.timeBasis;
129
+ function buildMarketAnchors(series: ResolvedSeries, timeZone: string): {
130
+ cadenceMs: number;
131
+ anchors: MarketAnchors;
132
+ } {
133
+ const anchorTimestamps = uniqueSeriesTimestamps(series);
134
+ const sessionDates = anchorTimestamps.map((timestamp) => sessionDate(timestamp, timeZone));
105
135
  const sameSessionGaps: number[] = [];
106
136
  const allGaps: number[] = [];
107
137
  for (let index = 1; index < anchorTimestamps.length; index += 1) {
108
- const previous = anchorTimestamps[index - 1]!;
109
- const next = anchorTimestamps[index]!;
110
- const gap = next - previous;
138
+ const gap = anchorTimestamps[index]! - anchorTimestamps[index - 1]!;
111
139
  if (gap <= 0) continue;
112
140
  allGaps.push(gap);
113
- if (sessionDate(previous, timeZone) === sessionDate(next, timeZone)) {
114
- sameSessionGaps.push(gap);
115
- }
141
+ if (sessionDates[index - 1] === sessionDates[index]) sameSessionGaps.push(gap);
116
142
  }
117
- const configuredCadence = anchorSeries.timeBasis.cadenceMs;
143
+ const configuredCadence = series.timeBasis?.cadenceMs;
118
144
  const cadenceMs = typeof configuredCadence === "number"
119
145
  && Number.isFinite(configuredCadence)
120
146
  && configuredCadence > 0
121
147
  ? configuredCadence
122
148
  : median(sameSessionGaps) ?? median(allGaps) ?? DAY_MS;
123
149
 
124
- const anchors: Extract<CompositeTimeScale, { kind: "market" }>["anchors"] = [];
150
+ const anchors: MarketAnchors = [];
125
151
  let position = 0;
126
152
  anchorTimestamps.forEach((timestamp, index) => {
127
153
  if (index > 0) {
128
154
  const previous = anchorTimestamps[index - 1]!;
129
- const sameSession = sessionDate(previous, timeZone) === sessionDate(timestamp, timeZone);
130
- position += sameSession
155
+ position += sessionDates[index - 1] === sessionDates[index]
131
156
  ? Math.max((timestamp - previous) / cadenceMs, MINIMUM_SLOT_FRACTION)
132
157
  : 1;
133
158
  }
134
159
  anchors.push({ timestamp, position });
135
160
  });
161
+ return { cadenceMs, anchors };
162
+ }
163
+
164
+ /**
165
+ * Anchor slots for a market series, cached on the series object. Live ticks
166
+ * mutate the last point in place, so the cache also keys on the point count
167
+ * and the last timestamp.
168
+ */
169
+ function resolveMarketAnchors(
170
+ series: ResolvedSeries,
171
+ ): { cadenceMs: number; anchors: MarketAnchors; timeZone: string } | null {
172
+ if (!series.timeBasis || series.timeBasis.kind !== "market") return null;
173
+ const { timeZone, cadenceMs: configuredCadence } = series.timeBasis;
174
+ const lastTimestamp = series.points.length > 0
175
+ ? finiteTimestamp(series.points[series.points.length - 1]!.date)
176
+ : null;
177
+ const cached = anchorCache.get(series);
178
+ if (
179
+ cached
180
+ && cached.points === series.points
181
+ && cached.pointCount === series.points.length
182
+ && cached.lastTimestamp === lastTimestamp
183
+ && cached.timeZone === timeZone
184
+ && cached.configuredCadence === configuredCadence
185
+ ) {
186
+ return { cadenceMs: cached.cadenceMs, anchors: cached.anchors, timeZone };
187
+ }
188
+ const built = buildMarketAnchors(series, timeZone);
189
+ anchorCache.set(series, {
190
+ points: series.points,
191
+ pointCount: series.points.length,
192
+ lastTimestamp,
193
+ timeZone,
194
+ configuredCadence,
195
+ cadenceMs: built.cadenceMs,
196
+ anchors: built.anchors,
197
+ });
198
+ return { ...built, timeZone };
199
+ }
136
200
 
137
- const provisional: Extract<CompositeTimeScale, { kind: "market" }> = {
201
+ export function buildCompositeTimeScale(
202
+ timelineSeries: readonly ResolvedSeries[],
203
+ startTime: number,
204
+ endTime: number,
205
+ ): CompositeTimeScale {
206
+ const anchorSeries = timelineSeries.find((series) => series.timeBasis?.kind === "market");
207
+ const resolved = anchorSeries ? resolveMarketAnchors(anchorSeries) : null;
208
+ if (!anchorSeries || !resolved || resolved.anchors.length === 0) {
209
+ return { kind: "calendar", startTime, endTime };
210
+ }
211
+ const { cadenceMs, anchors } = resolved;
212
+ const startPosition = positionForTimestamp({ anchors, cadenceMs }, startTime);
213
+ const endPosition = positionForTimestamp({ anchors, cadenceMs }, endTime);
214
+ return {
138
215
  kind: "market",
139
216
  startTime,
140
217
  endTime,
141
218
  anchorSeriesId: anchorSeries.id,
142
219
  cadenceMs,
143
220
  anchors,
144
- startPosition: 0,
145
- endPosition: 1,
146
- };
147
- const startPosition = positionForTimestamp(provisional, startTime);
148
- const endPosition = positionForTimestamp(provisional, endTime);
149
- return {
150
- ...provisional,
151
221
  startPosition,
152
222
  endPosition: endPosition > startPosition ? endPosition : startPosition + 1,
153
223
  };
154
224
  }
155
225
 
226
+ /** Slot position of a timestamp on a market scale, or wall-clock time on a calendar scale. */
227
+ export function compositeTimePosition(scale: CompositeTimeScale, timestamp: number): number {
228
+ return scale.kind === "calendar" ? timestamp : positionForTimestamp(scale, timestamp);
229
+ }
230
+
231
+ /** Inverse of `compositeTimePosition`. */
232
+ export function compositeTimeAtPosition(scale: CompositeTimeScale, position: number): number {
233
+ if (scale.kind === "calendar") return position;
234
+ const anchors = scale.anchors;
235
+ let low = 0;
236
+ let high = anchors.length;
237
+ while (low < high) {
238
+ const middle = low + Math.floor((high - low) / 2);
239
+ if (anchors[middle]!.position < position) low = middle + 1;
240
+ else high = middle;
241
+ }
242
+ const next = anchors[low];
243
+ const previous = anchors[low - 1];
244
+ if (!previous) {
245
+ return anchors[0]!.timestamp + (position - anchors[0]!.position) * scale.cadenceMs;
246
+ }
247
+ if (!next) {
248
+ const last = anchors.at(-1)!;
249
+ return last.timestamp + (position - last.position) * scale.cadenceMs;
250
+ }
251
+ const positionSpan = next.position - previous.position;
252
+ if (positionSpan <= 0) return previous.timestamp;
253
+ return previous.timestamp
254
+ + (next.timestamp - previous.timestamp) * ((position - previous.position) / positionSpan);
255
+ }
256
+
156
257
  export function projectCompositeTimestamp(
157
258
  scale: CompositeTimeScale,
158
259
  timestamp: number,
@@ -190,28 +291,8 @@ export function unprojectCompositeTimestamp(
190
291
  if (scale.kind === "calendar") {
191
292
  return scale.startTime + (scale.endTime - scale.startTime) * safeRatio;
192
293
  }
193
- const targetPosition = scale.startPosition
194
- + (scale.endPosition - scale.startPosition) * safeRatio;
195
- const anchors = scale.anchors;
196
- let low = 0;
197
- let high = anchors.length;
198
- while (low < high) {
199
- const middle = low + Math.floor((high - low) / 2);
200
- if (anchors[middle]!.position < targetPosition) low = middle + 1;
201
- else high = middle;
202
- }
203
- const next = anchors[low];
204
- const previous = anchors[low - 1];
205
- if (!previous) {
206
- return anchors[0]!.timestamp + (targetPosition - anchors[0]!.position) * scale.cadenceMs;
207
- }
208
- if (!next) {
209
- const last = anchors.at(-1)!;
210
- return last.timestamp + (targetPosition - last.position) * scale.cadenceMs;
211
- }
212
- const positionSpan = next.position - previous.position;
213
- if (positionSpan <= 0) return previous.timestamp;
214
- return previous.timestamp
215
- + (next.timestamp - previous.timestamp)
216
- * ((targetPosition - previous.position) / positionSpan);
294
+ return compositeTimeAtPosition(
295
+ scale,
296
+ scale.startPosition + (scale.endPosition - scale.startPosition) * safeRatio,
297
+ );
217
298
  }
@@ -111,6 +111,23 @@ export interface BuildCompositeChartSceneOptions {
111
111
  timelineSeries?: ResolvedSeries[];
112
112
  }
113
113
 
114
+ export interface CompositeChartXMarker {
115
+ id: string;
116
+ xRatio: number;
117
+ label?: string;
118
+ color?: string;
119
+ lineChar?: string;
120
+ }
121
+
122
+ /** Replaces the date axis for charts whose x is not time: a fraction, a tenor, a return. */
123
+ export interface CompositeChartXAxis {
124
+ /** Evenly spread labels drawn instead of date ticks. */
125
+ labels?: readonly string[];
126
+ /** Vertical guide lines through the plot, captioned below the axis. */
127
+ markers?: readonly CompositeChartXMarker[];
128
+ formatCursor?: (xRatio: number) => string;
129
+ }
130
+
114
131
  export interface CompositeChartProps {
115
132
  series: ResolvedSeries[];
116
133
  /** Optional legend model; can include hidden series that are not plotted. */
@@ -122,6 +139,13 @@ export interface CompositeChartProps {
122
139
  height: number;
123
140
  focused?: boolean;
124
141
  interactive?: boolean;
142
+ /** False keeps the hover cursor but removes pan, zoom, tools, and navigation keys. */
143
+ navigable?: boolean;
144
+ /** Overrides the unit-derived tick and cursor labels on both value axes. */
145
+ formatAxisValue?: (value: number, domain: CompositeAxisDomain) => string;
146
+ xAxis?: CompositeChartXAxis;
147
+ /** Tagged onto the plot's semantic node so remote clients can find this chart. */
148
+ remoteKind?: string;
125
149
  /** Permit panning into an older unloaded window so the owner can backfill it. */
126
150
  allowHistoricalBackfill?: boolean;
127
151
  cursorDate?: Date | null;
@@ -148,7 +172,7 @@ export interface CompositeChartProps {
148
172
  /** Reports a user-created viewport, or null when the user resets to the authored viewport. */
149
173
  onViewportChange?: (
150
174
  viewport: { start: Date; end: Date } | null,
151
- interaction: "pan" | "reset" | "sync" | "zoom",
175
+ interaction: "pan" | "reset" | "zoom",
152
176
  ) => void;
153
177
  onActivate?: () => void;
154
178
  onToggleSeries?: (seriesId: string) => void;
@@ -1,13 +1,9 @@
1
- export { appendLiveQuotePoint } from "../../../time-series/chart-data";
2
- export type { AppendLiveQuotePointOptions } from "../../../time-series/chart-data";
3
- export {
4
- bucketOhlcSeries,
5
- projectChartData,
6
- resolveRenderMode,
7
- resolveStableOhlcProjectionOptions,
8
- } from "./projection";
9
- export type {
10
- ChartProjection,
11
- ProjectChartDataOptions,
12
- ProjectedChartPoint,
13
- } from "./projection";
1
+ /** One observation of a simple, non-navigable chart: a value with optional bar extremes. */
2
+ export interface ProjectedChartPoint {
3
+ date: Date;
4
+ open: number;
5
+ high: number;
6
+ low: number;
7
+ close: number;
8
+ volume: number;
9
+ }
@@ -1,5 +1,11 @@
1
1
  import type { PricePoint } from "../../../types/financials";
2
- import type { TimeRange, VisibleWindow } from "./types";
2
+ import type { TimeRange } from "./types";
3
+
4
+ export interface VisibleWindow {
5
+ points: PricePoint[];
6
+ startIdx: number;
7
+ endIdx: number;
8
+ }
3
9
  import type { DateWindowRange } from "../../../time-series/date-window";
4
10
  import { subtractTimeRange } from "../../../time-series/date-window";
5
11
 
@@ -34,6 +34,9 @@ export interface ChartMouseEvent {
34
34
  scroll?: {
35
35
  direction: "up" | "down" | "left" | "right";
36
36
  delta: number;
37
+ /** Signed pixel travel on hosts that report it; terminals report notches only. */
38
+ deltaX?: number;
39
+ deltaY?: number;
37
40
  };
38
41
  }
39
42
 
@@ -1,26 +1,11 @@
1
- import type { PricePoint } from "../../../types/financials";
2
1
  export type { ChartResolution, TimeRange } from "../../../time-series/range";
3
2
 
4
3
  export type ChartRenderMode = "area" | "line" | "candles" | "ohlc" | "hlc";
5
- export type ChartAxisMode = "price" | "percent";
6
4
  export type ChartRendererPreference = "auto" | "kitty" | "braille";
7
5
  export type ResolvedChartRenderer = "kitty" | "braille";
8
- export type ChartSessionBackgroundKind = "pre" | "post";
9
6
 
10
7
  export const CHART_RENDERER_PREFERENCES: ChartRendererPreference[] = ["auto", "kitty", "braille"];
11
8
 
12
- export interface Pixel {
13
- color: string;
14
- layer: number;
15
- }
16
-
17
- export interface PixelBuffer {
18
- width: number;
19
- height: number; // virtual pixel rows (4x terminal rows for braille rendering)
20
- pixels: (Pixel | null)[][]; // [y][x] = pixel with color and layer, or null
21
- backgrounds: (string | null)[][];
22
- }
23
-
24
9
  export interface ChartColors {
25
10
  lineColor: string;
26
11
  fillColor: string;
@@ -35,33 +20,3 @@ export interface ChartColors {
35
20
  preMarketBgColor: string;
36
21
  postMarketBgColor: string;
37
22
  }
38
-
39
- export interface ChartMarketSession {
40
- timeZone: string;
41
- regularStartMinutes: number;
42
- regularEndMinutes: number;
43
- preMarketStartMinutes: number;
44
- postMarketEndMinutes: number;
45
- }
46
-
47
- export interface ChartSessionBackgroundSpan {
48
- kind: ChartSessionBackgroundKind;
49
- startIndex: number;
50
- endIndex: number;
51
- }
52
-
53
- export interface VisibleWindow {
54
- points: PricePoint[];
55
- startIdx: number;
56
- endIdx: number;
57
- }
58
-
59
- import type { OverlayPoint, OscillatorPoint, MacdResult, BollingerResult } from "../indicators/types";
60
-
61
- export interface ChartIndicatorOverlays {
62
- smaLines: { period: number; points: OverlayPoint[]; color: string }[];
63
- emaLines: { period: number; points: OverlayPoint[]; color: string }[];
64
- bollinger: (BollingerResult & { color: string }) | null;
65
- rsi: OscillatorPoint[] | null;
66
- macd: MacdResult | null;
67
- }
@@ -1,5 +1,4 @@
1
1
  export { renderCrosshairStrips } from "./raster/crosshair";
2
- export { renderNativeChartBase } from "./raster/price-chart";
3
2
 
4
3
  export {
5
4
  computeBitmapSize,