gloomberb 0.11.3 → 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 (489) hide show
  1. package/README.md +6 -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 +103 -1
  6. package/src/api-client/index.test.ts +127 -0
  7. package/src/api-client/index.ts +129 -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 +196 -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/cli/commands/plugins.ts +39 -11
  20. package/src/cli/desktop-pane-shot.ts +285 -16
  21. package/src/cli/index.ts +1 -1
  22. package/src/cli/pane-functions/capabilities.ts +58 -28
  23. package/src/cli/pane-functions/catalog.ts +16 -4
  24. package/src/cli/pane-functions/discovery.ts +1 -0
  25. package/src/cli/pane-functions/headless.test.ts +247 -0
  26. package/src/cli/pane-functions/headless.ts +423 -0
  27. package/src/cli/pane-functions/index.test.ts +9 -0
  28. package/src/cli/pane-functions/index.ts +6 -2
  29. package/src/cli/pane-functions/options.ts +27 -2
  30. package/src/cli/pane-functions/report.ts +4 -0
  31. package/src/cli/pane-functions/resolver.ts +26 -8
  32. package/src/cli/pane-functions/screenshot.test.ts +76 -0
  33. package/src/cli/pane-functions/screenshot.ts +236 -20
  34. package/src/cli/restore-plugins.ts +34 -0
  35. package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
  36. package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
  37. package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
  38. package/src/components/chart/composite/composite-chart.test.tsx +68 -20
  39. package/src/components/chart/composite/composite-chart.tsx +301 -204
  40. package/src/components/chart/composite/format.ts +8 -2
  41. package/src/components/chart/composite/interactions.test.ts +68 -102
  42. package/src/components/chart/composite/interactions.ts +214 -264
  43. package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
  44. package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
  45. package/src/components/chart/composite/scene.ts +57 -3
  46. package/src/components/chart/composite/text-renderer.ts +3 -2
  47. package/src/components/chart/composite/time-scale.ts +138 -57
  48. package/src/components/chart/composite/types.ts +25 -1
  49. package/src/components/chart/core/data.ts +9 -13
  50. package/src/components/chart/core/date-window.ts +7 -1
  51. package/src/components/chart/core/pointer.ts +3 -0
  52. package/src/components/chart/core/types.ts +0 -45
  53. package/src/components/chart/native/chart-rasterizer.ts +0 -1
  54. package/src/components/chart/native/kitty/index.test.ts +0 -188
  55. package/src/components/chart/native/raster/crosshair.ts +6 -5
  56. package/src/components/chart/native/raster/primitives.ts +0 -23
  57. package/src/components/chart/native/renderer-selection.ts +81 -31
  58. package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
  59. package/src/components/chart/static/chart-surface.tsx +150 -0
  60. package/src/components/chart/static/index.ts +8 -0
  61. package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
  62. package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
  63. package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
  64. package/src/components/chart/static/series.ts +49 -0
  65. package/src/components/command-bar/action-runtime.ts +1 -3
  66. package/src/components/command-bar/assist/model.test.ts +17 -2
  67. package/src/components/command-bar/assist/model.ts +40 -9
  68. package/src/components/command-bar/commands/direct/index.ts +1 -1
  69. package/src/components/command-bar/commands/direct/runtime.ts +1 -1
  70. package/src/components/command-bar/commands/registry.ts +0 -11
  71. package/src/components/command-bar/helpers.test.ts +2 -2
  72. package/src/components/command-bar/helpers.ts +2 -6
  73. package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
  74. package/src/components/command-bar/list/badge.test.ts +54 -0
  75. package/src/components/command-bar/list/badge.ts +62 -0
  76. package/src/components/command-bar/list/model.test.ts +103 -0
  77. package/src/components/command-bar/list/model.ts +61 -4
  78. package/src/components/command-bar/list/view.test.tsx +113 -0
  79. package/src/components/command-bar/list/view.tsx +147 -131
  80. package/src/components/command-bar/pane-templates/workflow.ts +1 -1
  81. package/src/components/command-bar/panel/body.tsx +51 -65
  82. package/src/components/command-bar/panel/index.tsx +48 -13
  83. package/src/components/command-bar/panel/layout.test.ts +139 -0
  84. package/src/components/command-bar/panel/layout.ts +65 -28
  85. package/src/components/command-bar/panel/native-surface.ts +24 -0
  86. package/src/components/command-bar/panel/prompt-binding.ts +54 -0
  87. package/src/components/command-bar/panel/runtime.ts +6 -3
  88. package/src/components/command-bar/panel/state.ts +41 -7
  89. package/src/components/command-bar/panel/types.ts +7 -1
  90. package/src/components/command-bar/routes/root/provider-search.ts +34 -15
  91. package/src/components/command-bar/routes/root/results.test.ts +161 -0
  92. package/src/components/command-bar/routes/root/results.ts +30 -28
  93. package/src/components/command-bar/routes/root/runtime.ts +37 -20
  94. package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
  95. package/src/components/command-bar/routes/root/search-providers.ts +205 -0
  96. package/src/components/command-bar/routes/root/selection.ts +1 -12
  97. package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
  98. package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
  99. package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
  100. package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
  101. package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
  102. package/src/components/command-bar/routing/actions.ts +1 -81
  103. package/src/components/command-bar/routing/launch-request.ts +1 -1
  104. package/src/components/command-bar/routing/list-state.ts +6 -21
  105. package/src/components/command-bar/selection-runtime.ts +1 -1
  106. package/src/components/command-bar/surface/assist.test.tsx +14 -10
  107. package/src/components/command-bar/surface/environment.ts +8 -1
  108. package/src/components/command-bar/surface/index.test.tsx +84 -68
  109. package/src/components/command-bar/surface/index.tsx +33 -36
  110. package/src/components/command-bar/surface/test-harness.tsx +15 -3
  111. package/src/components/command-bar/theme-picker.tsx +43 -15
  112. package/src/components/command-bar/view-model.test.ts +53 -8
  113. package/src/components/command-bar/view-model.ts +43 -14
  114. package/src/components/command-bar/workflow/tickers.ts +1 -0
  115. package/src/components/command-bar/workflow/types.ts +1 -1
  116. package/src/components/data-table/view.test.tsx +45 -6
  117. package/src/components/data-table/view.tsx +55 -9
  118. package/src/components/index.ts +10 -2
  119. package/src/components/input-search-bar.test.tsx +36 -2
  120. package/src/components/input-search-bar.tsx +18 -1
  121. package/src/components/layout/header.test.tsx +57 -10
  122. package/src/components/layout/header.tsx +292 -149
  123. package/src/components/layout/market-summary.test.ts +81 -0
  124. package/src/components/layout/market-summary.ts +109 -0
  125. package/src/components/layout/shell/chrome.ts +96 -1
  126. package/src/components/layout/shell/index.tsx +11 -3
  127. package/src/components/layout/shell/menu.ts +9 -0
  128. package/src/components/layout/shell/pane/actions.ts +29 -0
  129. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  130. package/src/components/layout/shell/shortcuts.ts +3 -0
  131. package/src/components/layout/status-bar.test.tsx +9 -8
  132. package/src/components/layout/status-bar.tsx +85 -54
  133. package/src/components/onboarding/onboarding-frame.tsx +2 -26
  134. package/src/components/price-sparkline/index.ts +61 -34
  135. package/src/components/price-sparkline/view.tsx +1 -1
  136. package/src/components/sign-in-gate.test.tsx +128 -0
  137. package/src/components/sign-in-gate.tsx +149 -0
  138. package/src/components/speedometer-gauge/desktop.tsx +2 -12
  139. package/src/components/ticker/badge/list.tsx +17 -3
  140. package/src/components/ticker/list-table-view.tsx +6 -3
  141. package/src/components/ui/data-table/index.tsx +3 -2
  142. package/src/components/ui/data-table/opentui/index.tsx +165 -120
  143. package/src/components/ui/frame.tsx +28 -0
  144. package/src/components/ui/index.ts +3 -0
  145. package/src/components/ui/native-select.tsx +20 -10
  146. package/src/components/ui/select-button.tsx +138 -0
  147. package/src/components/ui/status.tsx +2 -2
  148. package/src/components/ui/table-layout.ts +1 -1
  149. package/src/components/use-double-click-activation.ts +7 -3
  150. package/src/core/app-services.ts +3 -3
  151. package/src/data/config/layout.ts +12 -6
  152. package/src/data/config/store/index.test.ts +97 -0
  153. package/src/data/config/store/migrations.ts +41 -0
  154. package/src/data/config/store/normalize.ts +5 -3
  155. package/src/i18n/es.ts +6 -2
  156. package/src/i18n/ja.ts +6 -2
  157. package/src/i18n/ko.ts +6 -2
  158. package/src/i18n/zh-cn.ts +6 -2
  159. package/src/i18n/zh-tw.ts +6 -2
  160. package/src/layout-marketplace/pane-imagery.ts +1 -0
  161. package/src/layout-marketplace/payload.test.ts +17 -0
  162. package/src/layout-marketplace/payload.ts +40 -5
  163. package/src/market-data/coordinator/index.ts +3 -2
  164. package/src/market-data/coordinator/quotes.ts +173 -66
  165. package/src/market-data/coordinator/subscriptions.test.ts +215 -37
  166. package/src/pane-settings.ts +1 -1
  167. package/src/plugins/builtin/analytics/pane-model.ts +1 -1
  168. package/src/plugins/builtin/analytics/view.tsx +1 -1
  169. package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
  170. package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
  171. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
  172. package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
  173. package/src/plugins/builtin/chat/content/composer.tsx +8 -4
  174. package/src/plugins/builtin/cloud/auth-dialog.tsx +14 -228
  175. package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
  176. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  177. package/src/plugins/builtin/composite-plugins.ts +14 -1
  178. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  179. package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
  180. package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
  181. package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
  182. package/src/plugins/builtin/credit-conditions/index.tsx +2 -21
  183. package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
  184. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  185. package/src/plugins/builtin/earnings-calls/data.ts +223 -0
  186. package/src/plugins/builtin/earnings-calls/format.ts +49 -0
  187. package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
  188. package/src/plugins/builtin/earnings-calls/pane.tsx +756 -0
  189. package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
  190. package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
  191. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +334 -0
  192. package/src/plugins/builtin/econ/detail-view.tsx +1 -2
  193. package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
  194. package/src/plugins/builtin/econ/index.tsx +2 -1
  195. package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
  196. package/src/plugins/builtin/econ-statistics/client.ts +92 -0
  197. package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
  198. package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
  199. package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
  200. package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
  201. package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
  202. package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
  203. package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
  204. package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
  205. package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
  206. package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
  207. package/src/plugins/builtin/econ-statistics/view.ts +90 -0
  208. package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
  209. package/src/plugins/builtin/fear-greed/data.ts +6 -1
  210. package/src/plugins/builtin/help/index.tsx +4 -0
  211. package/src/plugins/builtin/holders/index.tsx +1 -0
  212. package/src/plugins/builtin/insider/index.tsx +1 -0
  213. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  214. package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
  215. package/src/plugins/builtin/market-valuation/align.ts +122 -0
  216. package/src/plugins/builtin/market-valuation/cache.ts +32 -0
  217. package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
  218. package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
  219. package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
  220. package/src/plugins/builtin/market-valuation/client.ts +116 -0
  221. package/src/plugins/builtin/market-valuation/defs.ts +251 -0
  222. package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
  223. package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
  224. package/src/plugins/builtin/market-valuation/headless.ts +170 -0
  225. package/src/plugins/builtin/market-valuation/index.tsx +64 -0
  226. package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
  227. package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
  228. package/src/plugins/builtin/market-valuation/model.ts +8 -0
  229. package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
  230. package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
  231. package/src/plugins/builtin/market-valuation/series.ts +12 -0
  232. package/src/plugins/builtin/market-valuation/settings.ts +38 -0
  233. package/src/plugins/builtin/market-valuation/sources.ts +130 -0
  234. package/src/plugins/builtin/market-valuation/trend.ts +89 -0
  235. package/src/plugins/builtin/market-valuation/view.ts +137 -0
  236. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
  237. package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
  238. package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
  239. package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
  240. package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
  241. package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
  242. package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
  243. package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
  244. package/src/plugins/builtin/news/wire/index.tsx +31 -44
  245. package/src/plugins/builtin/options/index.tsx +1 -0
  246. package/src/plugins/builtin/options/view.test.tsx +29 -1
  247. package/src/plugins/builtin/options/view.tsx +1 -22
  248. package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
  249. package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
  250. package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
  251. package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
  252. package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
  253. package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
  254. package/src/plugins/builtin/plugin-module.ts +9 -1
  255. package/src/plugins/builtin/research/index.tsx +4 -0
  256. package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
  257. package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
  258. package/src/plugins/builtin/research-search/data.ts +109 -0
  259. package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
  260. package/src/plugins/builtin/research-search/document-view.tsx +254 -0
  261. package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
  262. package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
  263. package/src/plugins/builtin/research-search/index.tsx +85 -0
  264. package/src/plugins/builtin/research-search/model.test.ts +40 -0
  265. package/src/plugins/builtin/research-search/model.ts +291 -0
  266. package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
  267. package/src/plugins/builtin/research-search/pane.tsx +678 -0
  268. package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
  269. package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
  270. package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
  271. package/src/plugins/builtin/research-search/snippet.ts +134 -0
  272. package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
  273. package/src/plugins/builtin/scanner/flow-model.ts +29 -1
  274. package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
  275. package/src/plugins/builtin/sec/index.tsx +1 -0
  276. package/src/plugins/builtin/shared/series-cache.ts +93 -0
  277. package/src/plugins/builtin/short-interest/index.tsx +1 -0
  278. package/src/plugins/builtin/short-interest/pane.tsx +1 -1
  279. package/src/plugins/builtin/thirteenf/index.tsx +1 -0
  280. package/src/plugins/builtin/ticker-detail/index.tsx +0 -14
  281. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
  282. package/src/plugins/builtin/volatility/index.tsx +1 -1
  283. package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
  284. package/src/plugins/builtin/world-venue-map/map.tsx +213 -69
  285. package/src/plugins/builtin/world-venue-map/model.test.ts +32 -0
  286. package/src/plugins/builtin/world-venue-map/model.ts +134 -13
  287. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  288. package/src/plugins/bundle.test.ts +109 -0
  289. package/src/plugins/bundle.ts +139 -0
  290. package/src/plugins/catalog-browser.test.ts +1 -0
  291. package/src/plugins/catalog-browser.ts +10 -0
  292. package/src/plugins/catalog-manifest.test.ts +43 -0
  293. package/src/plugins/catalog-manifest.ts +92 -0
  294. package/src/plugins/catalog-ui.ts +20 -4
  295. package/src/plugins/catalog.ts +9 -2
  296. package/src/plugins/current-target.ts +28 -0
  297. package/src/plugins/host-contract.ts +37 -0
  298. package/src/plugins/host-link.ts +158 -0
  299. package/src/plugins/host-modules.ts +61 -0
  300. package/src/plugins/loader.ts +41 -22
  301. package/src/plugins/ownership.ts +1 -0
  302. package/src/plugins/prediction-markets/index.tsx +1 -0
  303. package/src/plugins/registry/context.ts +3 -0
  304. package/src/plugins/registry/contributions.ts +27 -0
  305. package/src/plugins/registry/index.test.ts +25 -0
  306. package/src/plugins/registry/index.ts +8 -0
  307. package/src/plugins/registry/pane-settings.ts +6 -13
  308. package/src/plugins/seed.test.ts +83 -0
  309. package/src/plugins/seed.ts +80 -0
  310. package/src/public/broker.ts +31 -0
  311. package/src/public/market-data.ts +9 -0
  312. package/src/public/pane-hooks.ts +57 -0
  313. package/src/public/public-api.test.ts +285 -0
  314. package/src/public/react.ts +62 -0
  315. package/src/public/test-support.ts +23 -0
  316. package/src/public/time-series.ts +8 -0
  317. package/src/public/utils.ts +77 -0
  318. package/src/remote/layout-helpers.ts +6 -8
  319. package/src/remote/resources.ts +2 -0
  320. package/src/remote/schema.ts +1 -0
  321. package/src/remote/semantic-tree.test.tsx +105 -0
  322. package/src/remote/semantic-tree.tsx +53 -35
  323. package/src/renderers/browser/main.tsx +6 -0
  324. package/src/renderers/cloudflare/worker.test.ts +19 -0
  325. package/src/renderers/cloudflare/worker.ts +1 -1
  326. package/src/renderers/electrobun/bun/external-plugins.ts +140 -0
  327. package/src/renderers/electrobun/bun/index.ts +5 -2
  328. package/src/renderers/electrobun/shared/protocol.ts +21 -0
  329. package/src/renderers/electrobun/view/build-assets.ts +0 -1
  330. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +51 -1
  331. package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
  332. package/src/renderers/electrobun/view/external-plugins.ts +63 -0
  333. package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
  334. package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
  335. package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
  336. package/src/renderers/electrobun/view/host/mouse.ts +16 -2
  337. package/src/renderers/electrobun/view/main.tsx +27 -2
  338. package/src/renderers/electrobun/view/styles.css +5 -0
  339. package/src/renderers/electrobun/view/toast-host.tsx +18 -0
  340. package/src/renderers/opentui/chart-surface.tsx +5 -1
  341. package/src/renderers/opentui/host.tsx +16 -2
  342. package/src/renderers/opentui/interaction-performance.test.ts +79 -0
  343. package/src/renderers/opentui/interaction-performance.ts +188 -0
  344. package/src/renderers/opentui/start.tsx +26 -1
  345. package/src/renderers/opentui/test-utils.tsx +13 -0
  346. package/src/renderers/opentui/toast-host.tsx +26 -2
  347. package/src/renderers/opentui/ui-host.tsx +1 -0
  348. package/src/sources/provider-router/search.test.ts +191 -0
  349. package/src/sources/provider-router/search.ts +92 -39
  350. package/src/state/app/context/index.tsx +2 -0
  351. package/src/state/app/context/selector.test.tsx +33 -1
  352. package/src/state/hooks/quote-streaming.test.tsx +93 -0
  353. package/src/state/hooks/quote-streaming.ts +106 -30
  354. package/src/state/pane-table-export-registry.ts +19 -0
  355. package/src/theme/color-utils.ts +1 -1
  356. package/src/theme/colors.ts +50 -0
  357. package/src/theme/themes.test.ts +15 -2
  358. package/src/theme/themes.ts +19 -1
  359. package/src/tickers/search/index.test.ts +26 -0
  360. package/src/tickers/search/index.ts +42 -17
  361. package/src/tickers/search/ranking.ts +14 -0
  362. package/src/time-series/chart-data.test.ts +216 -0
  363. package/src/time-series/resolution.test.ts +25 -26
  364. package/src/time-series/resolution.ts +41 -10
  365. package/src/time-series/resolve.ts +7 -0
  366. package/src/time-series/types.ts +3 -1
  367. package/src/time-series/use-chart-resolution.ts +5 -0
  368. package/src/types/config.ts +123 -3
  369. package/src/types/data-provider.ts +11 -0
  370. package/src/types/headless.ts +161 -0
  371. package/src/types/plugin.ts +102 -0
  372. package/src/ui/host.tsx +12 -1
  373. package/src/ui/index.tsx +1 -0
  374. package/src/ui/toast.tsx +8 -4
  375. package/src/utils/format.ts +35 -0
  376. package/src/utils/fuzzy-search.test.ts +94 -0
  377. package/src/utils/fuzzy-search.ts +81 -22
  378. package/src/utils/shortcut-labels.ts +14 -0
  379. package/src/components/chart/core/axis-format.ts +0 -157
  380. package/src/components/chart/core/index.test.ts +0 -1075
  381. package/src/components/chart/core/projection.ts +0 -357
  382. package/src/components/chart/core/render-utils.ts +0 -4
  383. package/src/components/chart/core/renderer.ts +0 -244
  384. package/src/components/chart/core/scene.ts +0 -180
  385. package/src/components/chart/core/time-axis.ts +0 -330
  386. package/src/components/chart/cursor-motion.ts +0 -47
  387. package/src/components/chart/indicators/types.ts +0 -26
  388. package/src/components/chart/market-session.ts +0 -176
  389. package/src/components/chart/multi-line/renderer.test.ts +0 -83
  390. package/src/components/chart/multi-line/renderer.ts +0 -161
  391. package/src/components/chart/multi-line/scene.ts +0 -188
  392. package/src/components/chart/native/raster/layout.ts +0 -129
  393. package/src/components/chart/native/raster/price-chart.ts +0 -244
  394. package/src/components/chart/rendering/chart-draw.ts +0 -270
  395. package/src/components/chart/rendering/geometry.ts +0 -95
  396. package/src/components/chart/rendering/pixel-buffer.ts +0 -267
  397. package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
  398. package/src/components/chart/scatter-chart-renderer.ts +0 -223
  399. package/src/components/chart/static/chart/surface.tsx +0 -358
  400. package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
  401. package/src/components/command-bar/routes/root/article-results.ts +0 -55
  402. package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
  403. package/src/plugins/builtin/news/wire/article-search.ts +0 -140
  404. package/src/plugins/builtin/substack/api/auth.ts +0 -185
  405. package/src/plugins/builtin/substack/api/cache.ts +0 -82
  406. package/src/plugins/builtin/substack/api/loaders.ts +0 -170
  407. package/src/plugins/builtin/substack/api/store.ts +0 -186
  408. package/src/plugins/builtin/substack/api/types.ts +0 -42
  409. package/src/plugins/builtin/substack/api.test.ts +0 -298
  410. package/src/plugins/builtin/substack/article-detail.tsx +0 -397
  411. package/src/plugins/builtin/substack/article-stack.tsx +0 -133
  412. package/src/plugins/builtin/substack/content.ts +0 -478
  413. package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
  414. package/src/plugins/builtin/substack/index.tsx +0 -49
  415. package/src/plugins/builtin/substack/login-view.tsx +0 -126
  416. package/src/plugins/builtin/substack/model.test.ts +0 -155
  417. package/src/plugins/builtin/substack/normalize.ts +0 -343
  418. package/src/plugins/builtin/substack/pane-footer.ts +0 -52
  419. package/src/plugins/builtin/substack/pane-state.ts +0 -170
  420. package/src/plugins/builtin/substack/pane.tsx +0 -547
  421. package/src/plugins/builtin/substack/read-state.ts +0 -30
  422. package/src/plugins/builtin/substack/table.ts +0 -111
  423. package/src/plugins/builtin/substack/types.ts +0 -71
  424. package/src/plugins/builtin/substack/utils.ts +0 -131
  425. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
  426. package/src/plugins/ibkr/account-cache.test.ts +0 -102
  427. package/src/plugins/ibkr/account-cache.ts +0 -24
  428. package/src/plugins/ibkr/broker-adapter.ts +0 -286
  429. package/src/plugins/ibkr/config.test.ts +0 -79
  430. package/src/plugins/ibkr/config.ts +0 -250
  431. package/src/plugins/ibkr/dialogs.tsx +0 -40
  432. package/src/plugins/ibkr/flex/index.test.ts +0 -264
  433. package/src/plugins/ibkr/flex/index.ts +0 -367
  434. package/src/plugins/ibkr/flex/statement-client.ts +0 -235
  435. package/src/plugins/ibkr/fundamental-parser.ts +0 -149
  436. package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
  437. package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
  438. package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
  439. package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
  440. package/src/plugins/ibkr/gateway/connection.ts +0 -152
  441. package/src/plugins/ibkr/gateway/contracts.ts +0 -132
  442. package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
  443. package/src/plugins/ibkr/gateway/helpers.ts +0 -33
  444. package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
  445. package/src/plugins/ibkr/gateway/history.ts +0 -120
  446. package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
  447. package/src/plugins/ibkr/gateway/market-data.ts +0 -345
  448. package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
  449. package/src/plugins/ibkr/gateway/orders.ts +0 -63
  450. package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
  451. package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
  452. package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
  453. package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
  454. package/src/plugins/ibkr/gateway/selection.ts +0 -51
  455. package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
  456. package/src/plugins/ibkr/gateway/service/index.ts +0 -429
  457. package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
  458. package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
  459. package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
  460. package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
  461. package/src/plugins/ibkr/gateway/service/native.ts +0 -543
  462. package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
  463. package/src/plugins/ibkr/gateway/types.ts +0 -27
  464. package/src/plugins/ibkr/index.test.tsx +0 -162
  465. package/src/plugins/ibkr/index.tsx +0 -159
  466. package/src/plugins/ibkr/instance-selection.test.ts +0 -120
  467. package/src/plugins/ibkr/instance-selection.ts +0 -36
  468. package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
  469. package/src/plugins/ibkr/portfolio-performance.ts +0 -129
  470. package/src/plugins/ibkr/trade/badge.tsx +0 -28
  471. package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
  472. package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
  473. package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
  474. package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
  475. package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
  476. package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
  477. package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
  478. package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
  479. package/src/plugins/ibkr/trade/tab/model.ts +0 -159
  480. package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
  481. package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
  482. package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
  483. package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
  484. package/src/plugins/ibkr/trade/utils.test.ts +0 -175
  485. package/src/plugins/ibkr/trade/utils.ts +0 -144
  486. package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
  487. package/src/plugins/ibkr/trading/pane.tsx +0 -332
  488. package/src/plugins/ibkr/trading/state.test.ts +0 -109
  489. package/src/plugins/ibkr/trading/state.ts +0 -335
@@ -5,6 +5,11 @@ import { QUOTE_STREAM_UPDATE_THROTTLE_MS } from "../quotes/cadence";
5
5
  import { createTestDataProvider } from "../../test-support/data-provider";
6
6
  import type { Quote } from "../../types/financials";
7
7
  import type { DataProvider, QuoteSubscriptionTarget } from "../../types/data-provider";
8
+ import {
9
+ QUOTE_SUBSCRIPTION_PRIORITY_UPDATE_DELAY_MS,
10
+ QUOTE_SUBSCRIPTION_REMOVE_GRACE_MS,
11
+ type QuoteSubscriptionRequest,
12
+ } from "./quotes";
8
13
 
9
14
  function createProvider(): {
10
15
  provider: DataProvider;
@@ -75,47 +80,220 @@ describe("MarketDataCoordinator key subscriptions", () => {
75
80
  });
76
81
 
77
82
  test("keeps the highest-priority target when duplicate surfaces subscribe", () => {
78
- const subscriptions: QuoteSubscriptionTarget[][] = [];
79
- let disposals = 0;
80
- const provider = createTestDataProvider({
81
- id: "test-provider",
82
- subscribeQuotes: (targets) => {
83
- subscriptions.push(targets);
84
- return () => {
85
- disposals += 1;
86
- };
87
- },
88
- });
89
- const coordinator = new MarketDataCoordinator(provider);
90
- const instrument = { symbol: "AAPL", exchange: "NASDAQ" };
83
+ jest.useFakeTimers();
84
+ try {
85
+ const subscriptions: QuoteSubscriptionTarget[][] = [];
86
+ let disposals = 0;
87
+ const provider = createTestDataProvider({
88
+ id: "test-provider",
89
+ subscribeQuotes: (targets) => {
90
+ subscriptions.push(targets);
91
+ return () => {
92
+ disposals += 1;
93
+ };
94
+ },
95
+ });
96
+ const coordinator = new MarketDataCoordinator(provider);
97
+ const instrument = { symbol: "AAPL", exchange: "NASDAQ" };
98
+
99
+ const unsubscribeDetail = coordinator.subscribeQuotes([{
100
+ instrument,
101
+ priority: { surface: "detail", visible: true, selected: true, weight: 100 },
102
+ }]);
103
+ const unsubscribePortfolio = coordinator.subscribeQuotes([{
104
+ instrument,
105
+ priority: { surface: "portfolio", visible: false, selected: false, weight: 10 },
106
+ }]);
107
+
108
+ expect(subscriptions).toHaveLength(1);
109
+ expect(subscriptions[0]?.[0]).toMatchObject({
110
+ surface: "detail",
111
+ visible: true,
112
+ selected: true,
113
+ weight: 100,
114
+ });
91
115
 
92
- const unsubscribeDetail = coordinator.subscribeQuotes([{
93
- instrument,
94
- priority: { surface: "detail", visible: true, selected: true, weight: 100 },
95
- }]);
96
- coordinator.subscribeQuotes([{
97
- instrument,
98
- priority: { surface: "portfolio", visible: false, selected: false, weight: 10 },
99
- }]);
116
+ unsubscribeDetail();
117
+ expect(subscriptions).toHaveLength(1);
100
118
 
101
- expect(subscriptions).toHaveLength(1);
102
- expect(subscriptions[0]?.[0]).toMatchObject({
103
- surface: "detail",
104
- visible: true,
105
- selected: true,
106
- weight: 100,
107
- });
119
+ jest.advanceTimersByTime(QUOTE_SUBSCRIPTION_PRIORITY_UPDATE_DELAY_MS);
120
+ expect(subscriptions).toHaveLength(2);
121
+ expect(disposals).toBe(1);
122
+ expect(subscriptions[1]?.[0]).toMatchObject({
123
+ surface: "portfolio",
124
+ visible: false,
125
+ selected: false,
126
+ weight: 10,
127
+ });
108
128
 
109
- unsubscribeDetail();
129
+ unsubscribePortfolio();
130
+ jest.runAllTimers();
131
+ } finally {
132
+ jest.useRealTimers();
133
+ }
134
+ });
110
135
 
111
- expect(subscriptions).toHaveLength(2);
112
- expect(disposals).toBe(1);
113
- expect(subscriptions[1]?.[0]).toMatchObject({
114
- surface: "portfolio",
115
- visible: false,
116
- selected: false,
117
- weight: 10,
118
- });
136
+ test("coalesces priority updates and overlaps the upstream replacement", () => {
137
+ jest.useFakeTimers();
138
+ try {
139
+ const events: string[] = [];
140
+ const subscriptions: QuoteSubscriptionTarget[][] = [];
141
+ const provider = createTestDataProvider({
142
+ id: "test-provider",
143
+ subscribeQuotes: (targets) => {
144
+ const subscriptionId = subscriptions.length + 1;
145
+ subscriptions.push(targets);
146
+ events.push(`subscribe:${subscriptionId}`);
147
+ return () => events.push(`dispose:${subscriptionId}`);
148
+ },
149
+ });
150
+ const coordinator = new MarketDataCoordinator(provider);
151
+ const instruments = ["AAPL", "MSFT", "NVDA"].map((symbol) => ({
152
+ symbol,
153
+ exchange: "NASDAQ",
154
+ }));
155
+ const requests = (
156
+ selectedSymbol: string,
157
+ selectedSurface: "portfolio" | "detail" = "portfolio",
158
+ ): QuoteSubscriptionRequest[] => instruments.map((instrument) => ({
159
+ instrument,
160
+ priority: {
161
+ surface: instrument.symbol === selectedSymbol ? selectedSurface : "portfolio",
162
+ visible: instrument.symbol === selectedSymbol,
163
+ selected: instrument.symbol === selectedSymbol,
164
+ weight: instrument.symbol === selectedSymbol ? 100 : 10,
165
+ },
166
+ }));
167
+
168
+ const subscription = coordinator.subscribeQuotes(requests("AAPL"));
169
+ subscription.update(requests("MSFT"));
170
+ subscription.update(requests("NVDA", "detail"));
171
+
172
+ jest.advanceTimersByTime(QUOTE_SUBSCRIPTION_PRIORITY_UPDATE_DELAY_MS - 1);
173
+ expect(subscriptions).toHaveLength(1);
174
+ expect(events).toEqual(["subscribe:1"]);
175
+
176
+ jest.advanceTimersByTime(1);
177
+ expect(subscriptions).toHaveLength(2);
178
+ expect(subscriptions[1]?.find((target) => target.symbol === "NVDA")).toMatchObject({
179
+ surface: "detail",
180
+ visible: true,
181
+ selected: true,
182
+ weight: 100,
183
+ });
184
+ expect(events).toEqual(["subscribe:1", "subscribe:2", "dispose:1"]);
185
+
186
+ subscription();
187
+ jest.runAllTimers();
188
+ } finally {
189
+ jest.useRealTimers();
190
+ }
191
+ });
192
+
193
+ test("applies membership and routing changes immediately", () => {
194
+ jest.useFakeTimers();
195
+ try {
196
+ const subscriptions: QuoteSubscriptionTarget[][] = [];
197
+ let disposals = 0;
198
+ const provider = createTestDataProvider({
199
+ id: "test-provider",
200
+ subscribeQuotes: (targets) => {
201
+ subscriptions.push(targets);
202
+ return () => { disposals += 1; };
203
+ },
204
+ });
205
+ const coordinator = new MarketDataCoordinator(provider);
206
+ const aapl = {
207
+ symbol: "AAPL",
208
+ exchange: "NASDAQ",
209
+ brokerId: "ibkr",
210
+ brokerInstanceId: "ibkr-work",
211
+ instrument: {
212
+ brokerId: "ibkr",
213
+ brokerInstanceId: "ibkr-work",
214
+ conId: 1001,
215
+ symbol: "AAPL",
216
+ },
217
+ };
218
+ const msft = { symbol: "MSFT", exchange: "NASDAQ" };
219
+ const subscription = coordinator.subscribeQuotes([{
220
+ instrument: aapl,
221
+ priority: { route: "provider", surface: "portfolio" },
222
+ }]);
223
+
224
+ subscription.update([
225
+ { instrument: aapl, priority: { route: "provider", surface: "portfolio" } },
226
+ { instrument: msft, priority: { route: "provider", surface: "portfolio" } },
227
+ ]);
228
+ expect(subscriptions).toHaveLength(2);
229
+ expect(disposals).toBe(1);
230
+ expect(subscriptions[1]?.map((target) => target.symbol).sort()).toEqual(["AAPL", "MSFT"]);
231
+
232
+ subscription.update([
233
+ { instrument: aapl, priority: { route: "broker", surface: "detail" } },
234
+ { instrument: msft, priority: { route: "provider", surface: "portfolio" } },
235
+ ]);
236
+ expect(subscriptions).toHaveLength(3);
237
+ expect(disposals).toBe(2);
238
+ expect(subscriptions[2]?.find((target) => target.symbol === "AAPL")).toMatchObject({
239
+ route: "broker",
240
+ surface: "detail",
241
+ context: {
242
+ brokerId: "ibkr",
243
+ brokerInstanceId: "ibkr-work",
244
+ instrument: { brokerId: "ibkr", conId: 1001, symbol: "AAPL" },
245
+ },
246
+ });
247
+
248
+ subscription();
249
+ jest.runAllTimers();
250
+ } finally {
251
+ jest.useRealTimers();
252
+ }
253
+ });
254
+
255
+ test("keeps an updated handle callable and honors removal grace on dispose", () => {
256
+ jest.useFakeTimers();
257
+ try {
258
+ const subscriptions: QuoteSubscriptionTarget[][] = [];
259
+ let disposals = 0;
260
+ const provider = createTestDataProvider({
261
+ id: "test-provider",
262
+ subscribeQuotes: (targets) => {
263
+ subscriptions.push(targets);
264
+ return () => { disposals += 1; };
265
+ },
266
+ });
267
+ const coordinator = new MarketDataCoordinator(provider);
268
+ const instrument = { symbol: "AAPL", exchange: "NASDAQ" };
269
+ const subscription = coordinator.subscribeQuotes([{
270
+ instrument,
271
+ priority: { surface: "portfolio", selected: false, weight: 10 },
272
+ }]);
273
+
274
+ expect(typeof subscription).toBe("function");
275
+ expect(typeof subscription.update).toBe("function");
276
+ subscription.update([{
277
+ instrument,
278
+ priority: { surface: "detail", selected: true, weight: 100 },
279
+ }]);
280
+ jest.advanceTimersByTime(QUOTE_SUBSCRIPTION_PRIORITY_UPDATE_DELAY_MS);
281
+ expect(subscriptions).toHaveLength(2);
282
+ expect(disposals).toBe(1);
283
+
284
+ subscription();
285
+ subscription.update([{ instrument: { symbol: "MSFT", exchange: "NASDAQ" } }]);
286
+ subscription();
287
+ jest.advanceTimersByTime(QUOTE_SUBSCRIPTION_REMOVE_GRACE_MS - 1);
288
+ expect(subscriptions).toHaveLength(2);
289
+ expect(disposals).toBe(1);
290
+
291
+ jest.advanceTimersByTime(1);
292
+ expect(subscriptions).toHaveLength(2);
293
+ expect(disposals).toBe(2);
294
+ } finally {
295
+ jest.useRealTimers();
296
+ }
119
297
  });
120
298
 
121
299
  test("replaces a capped target window without retaining pending removals", () => {
@@ -52,7 +52,7 @@ export function setPaneSetting(
52
52
  value: unknown,
53
53
  ): LayoutConfig {
54
54
  const current = findPaneInstance(layout, paneId);
55
- if (!current) return layout;
55
+ if (!current || Object.is(current.settings?.[key], value)) return layout;
56
56
  return setPaneSettings(layout, paneId, {
57
57
  ...(current.settings ?? {}),
58
58
  [key]: value,
@@ -1,4 +1,4 @@
1
- import { resolveChartPalette } from "../../../components/chart/core/renderer";
1
+ import { resolveChartPalette } from "../../../components/chart/core/palette";
2
2
  import { colors, priceColor } from "../../../theme/colors";
3
3
  import type { TickerFinancials, PricePoint } from "../../../types/financials";
4
4
  import type { BrokerPortfolioPerformance } from "../../../types/trading";
@@ -5,7 +5,7 @@ import {
5
5
  loadingText,
6
6
  unavailableText,
7
7
  } from "../../../components";
8
- import type { StaticChartSurfaceProps } from "../../../components/chart/static/chart/surface";
8
+ import type { StaticChartSurfaceProps } from "../../../components/chart/static";
9
9
  import type { ProjectedChartPoint } from "../../../components/chart/core/data";
10
10
  import { colors, priceColor } from "../../../theme/colors";
11
11
  import { formatCompact, formatPercentRaw } from "../../../utils/format";
@@ -7,7 +7,7 @@ import { AppContext, PaneInstanceProvider, createInitialState } from "../../../s
7
7
  import { createTestPluginRuntime } from "../../../test-support/plugin-runtime";
8
8
  import { createDefaultConfig, type BrokerInstanceConfig } from "../../../types/config";
9
9
  import { PluginRenderProvider } from "../../runtime";
10
- import { ibkrBroker } from "../../ibkr/broker-adapter";
10
+ import { testBroker } from "../../../brokers/test-broker";
11
11
  import { BrokersPane } from "./index";
12
12
 
13
13
  let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
@@ -63,7 +63,7 @@ function Harness({
63
63
  };
64
64
  }
65
65
  const runtime = createTestPluginRuntime({
66
- getBrokerAdapter: (brokerType) => brokerType === "ibkr" ? ibkrBroker : null,
66
+ getBrokerAdapter: (brokerType) => brokerType === "ibkr" ? testBroker : null,
67
67
  openCommandBar: (query) => calls.push(`command:${query ?? ""}`),
68
68
  showPane: (paneId) => calls.push(`pane:${paneId}`),
69
69
  connectBrokerInstance: async (instanceId) => { calls.push(`connect:${instanceId}`); },
@@ -113,6 +113,7 @@ async function pressKey(key: string) {
113
113
 
114
114
  describe("BrokersPane", () => {
115
115
  test("renders IBKR row and invokes broker actions", async () => {
116
+
116
117
  const calls: string[] = [];
117
118
  testSetup = await testRender(<FooterHarness calls={calls} instance={createGatewayInstance()} height={35} />, { width: 92, height: 35 });
118
119
  await act(async () => {
@@ -1,7 +1,7 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import type { BrokerAdapter } from "../../../types/broker";
3
3
  import { createDefaultConfig, type BrokerInstanceConfig } from "../../../types/config";
4
- import { ibkrBroker } from "../../ibkr/broker-adapter";
4
+ import { testBroker } from "../../../brokers/test-broker";
5
5
  import { buildBrokerProfileRows, formatBrokerUpdatedAt } from "./model";
6
6
 
7
7
  function createInstance(patch: Partial<BrokerInstanceConfig> = {}): BrokerInstanceConfig {
@@ -131,7 +131,7 @@ describe("broker manager rows", () => {
131
131
  enabled: true,
132
132
  }],
133
133
  };
134
- const rows = buildBrokerProfileRows(config, new Map([["ibkr", ibkrBroker]]), {});
134
+ const rows = buildBrokerProfileRows(config, new Map([["ibkr", testBroker]]), {});
135
135
 
136
136
  expect(rows[0]?.mode).toBe("Flex");
137
137
  expect(rows[0]?.stateLabel).toBe("Sync only");
@@ -10,6 +10,8 @@ import {
10
10
  FUTURES_CONTRACTS,
11
11
  FUTURES_SECTOR_LABELS,
12
12
  } from "../futures/contracts";
13
+ import { INDICATORS as VALUATION_INDICATORS } from "../market-valuation/indicators";
14
+ import { MARKET_VALUATION_CAPABILITY_ID } from "../market-valuation/chart-series";
13
15
  import { TREASURY_MATURITIES } from "../yield-curve/treasury-data";
14
16
  import { fieldCategory, type SeriesCatalogInstrument } from "./series-catalog";
15
17
 
@@ -23,7 +25,8 @@ export type CatalogSourceId =
23
25
  | "crypto"
24
26
  | "fred"
25
27
  | "futures"
26
- | "treasury";
28
+ | "treasury"
29
+ | "valuation";
27
30
 
28
31
  export type CatalogFilterId =
29
32
  | "all"
@@ -31,7 +34,8 @@ export type CatalogFilterId =
31
34
  | "options"
32
35
  | "crypto"
33
36
  | "fred"
34
- | "futures";
37
+ | "futures"
38
+ | "valuation";
35
39
 
36
40
  export interface CatalogSeriesRow {
37
41
  id: string;
@@ -53,6 +57,7 @@ export const CATALOG_FILTERS: ReadonlyArray<{ id: CatalogFilterId; label: string
53
57
  { id: "crypto", label: "Crypto" },
54
58
  { id: "fred", label: "FRED" },
55
59
  { id: "futures", label: "Futures" },
60
+ { id: "valuation", label: "Valuation" },
56
61
  ];
57
62
 
58
63
  const FILTER_SOURCES: Record<CatalogFilterId, ReadonlySet<CatalogSourceId> | null> = {
@@ -62,6 +67,7 @@ const FILTER_SOURCES: Record<CatalogFilterId, ReadonlySet<CatalogSourceId> | nul
62
67
  crypto: new Set(["crypto"]),
63
68
  fred: new Set(["fred", "treasury"]),
64
69
  futures: new Set(["futures"]),
70
+ valuation: new Set(["valuation"]),
65
71
  };
66
72
 
67
73
  const CRYPTO_CATALOG: ReadonlyArray<{ symbol: string; name: string }> = [
@@ -321,6 +327,14 @@ const STATIC_CATALOG_INVENTORY: readonly CatalogSeriesRow[] = [
321
327
  expression: `UST:${entry.maturity}`,
322
328
  url: `https://fred.stlouisfed.org/series/${entry.seriesId}`,
323
329
  })),
330
+ ...VALUATION_INDICATORS.map((entry) => row({
331
+ id: `valuation:${entry.id}`,
332
+ label: entry.label,
333
+ source: "Gloom Cloud",
334
+ sourceId: "valuation",
335
+ kind: "Valuation",
336
+ expression: `CAP:${MARKET_VALUATION_CAPABILITY_ID}:${entry.id}`,
337
+ })),
324
338
  ...FUTURES_CONTRACTS.map((entry) => row({
325
339
  id: `fut:${entry.code}`,
326
340
  label: `${entry.name} (${entry.code})`,
@@ -14,7 +14,10 @@ import { CompositeChart } from "../../../components/chart/composite";
14
14
  import type { PaneProps, TickerResearchTabProps } from "../../../types/plugin";
15
15
  import type { ChartResolution, TimeRange } from "../../../components/chart/core/types";
16
16
  import type { ChartSpec, ResolvedSeries } from "../../../time-series/types";
17
- import { getSupportedChartResolutionsForViewport } from "../../../time-series/resolution";
17
+ import {
18
+ getSupportedChartResolutionsForViewport,
19
+ type ManualChartResolution,
20
+ } from "../../../time-series/resolution";
18
21
  import { useResolvedChartSpec } from "../../../time-series/hooks";
19
22
  import { chartSeriesSourceKey } from "../../../capabilities";
20
23
  import { useShortcut } from "../../../react/input";
@@ -68,6 +71,9 @@ import { isPlainKey } from "../../../utils/keyboard";
68
71
 
69
72
  const RANGE_TABS = RANGES.map((range, index) => ({ label: `${index + 1}:${range}`, value: range }));
70
73
  const AUTO_VIEWPORT_DEBOUNCE_MS = 350;
74
+ /** Bar pitch AUTO aims for; the coarser neighbour wins ties so bars stay readable. */
75
+ const AUTO_RESOLUTION_BAR_PIXELS = 7;
76
+ const MINIMUM_AUTO_RESOLUTION_POINTS = 60;
71
77
 
72
78
  interface RuntimeChartViewport {
73
79
  start: Date;
@@ -137,7 +143,7 @@ function ChartComposerSurface({
137
143
  const dialog = useDialog();
138
144
  const dispatch = useAppDispatch();
139
145
  const isDesktopWeb = useUiHost().kind === "desktop-web";
140
- const { publicSharing } = useUiCapabilities();
146
+ const { publicSharing, cellWidthPx = 8 } = useUiCapabilities();
141
147
  const paneId = usePaneInstanceId();
142
148
  const liveStreaming = useLiveStreamingSetting();
143
149
  const dialogOpen = useDialogState((state) => state.isOpen);
@@ -179,15 +185,24 @@ function ChartComposerSurface({
179
185
  const activeRuntimeViewport = runtimeViewportState?.key === authoredViewportKey
180
186
  ? runtimeViewportState
181
187
  : null;
182
- const targetPointCount = Math.max(60, Math.floor(width * 1.25));
188
+ const targetPointCount = Math.max(
189
+ MINIMUM_AUTO_RESOLUTION_POINTS,
190
+ Math.round((width * cellWidthPx) / AUTO_RESOLUTION_BAR_PIXELS),
191
+ );
192
+ // The loaded resolution is the tie-breaker for the next AUTO pick, so a
193
+ // small zoom keeps the bars it already has. Read from the previous render:
194
+ // the resolver only consults it when the viewport moves.
195
+ const currentResolutionRef = useRef<ManualChartResolution | null>(null);
183
196
  const resolution = useResolvedChartSpec(spec, {
184
197
  autoViewport: spec.viewport.resolution === "auto"
185
198
  ? activeRuntimeViewport?.adaptiveViewport
186
199
  : null,
187
200
  requestViewport: activeRuntimeViewport?.requestViewport,
188
201
  targetPointCount,
202
+ currentResolution: currentResolutionRef.current,
189
203
  liveStreaming: liveStreaming && (liveWhenUnfocused || focused),
190
204
  });
205
+ currentResolutionRef.current = resolution.resolution ?? currentResolutionRef.current;
191
206
  const availableResolutions = useMemo<ChartResolution[]>(() => {
192
207
  if (!resolution.resolutionSupport) {
193
208
  if (!resolution.loading) return RESOLUTIONS;
@@ -320,9 +335,8 @@ function ChartComposerSurface({
320
335
  }, [authoredViewportKey, persistedInteractionViewport, setStoredInteractionViewport]);
321
336
  const handleChartViewportChange = useCallback((
322
337
  next: { start: Date; end: Date } | null,
323
- interaction: "pan" | "reset" | "sync" | "zoom",
338
+ _interaction: "pan" | "reset" | "zoom",
324
339
  ) => {
325
- if (interaction === "sync") return;
326
340
  if (runtimeViewportTimerRef.current !== null) {
327
341
  clearTimeout(runtimeViewportTimerRef.current);
328
342
  runtimeViewportTimerRef.current = null;
@@ -339,9 +353,9 @@ function ChartComposerSurface({
339
353
  if (!Number.isFinite(start) || !Number.isFinite(end) || start > end) return;
340
354
  const viewport = { start: new Date(start), end: new Date(end) };
341
355
  requestViewportRef.current = viewport;
342
- if (interaction === "zoom" && spec.viewport.resolution === "auto") {
343
- adaptiveViewportRef.current = viewport;
344
- }
356
+ // A pan can leave the window a provider serves at the current resolution
357
+ // just as a zoom can, so both re-pick.
358
+ if (spec.viewport.resolution === "auto") adaptiveViewportRef.current = viewport;
345
359
  runtimeViewportTimerRef.current = globalThis.setTimeout(() => {
346
360
  runtimeViewportTimerRef.current = null;
347
361
  const requestViewport = requestViewportRef.current;
@@ -1,7 +1,7 @@
1
- import { Box, Text, TextAttributes, type TextareaRenderable } from "../../../../ui";
1
+ import { Box, Text, TextAttributes, useCommandBarShortcut, type TextareaRenderable } from "../../../../ui";
2
2
  import { getMessageComposerBlockHeight, MessageComposer } from "../../../../components/ui";
3
3
  import { colors } from "../../../../theme/colors";
4
- import { t } from "../../../../i18n";
4
+ import { t, tf } from "../../../../i18n";
5
5
  import { truncateToDisplayWidth } from "../../../../utils/format";
6
6
  import type { ChatMessage } from "../../../../api-client";
7
7
  import { InlineAuthActions } from "../../cloud/auth-actions";
@@ -130,8 +130,10 @@ export function ChatComposerArea({
130
130
  onMentionSelect,
131
131
  user,
132
132
  }: ChatComposerAreaProps) {
133
+ const commandBarShortcut = useCommandBarShortcut();
133
134
  if (!canSend) {
134
- const fitLine = (value: string) => truncateToDisplayWidth(t(value), contentWidth);
135
+ const fitText = (value: string) => truncateToDisplayWidth(value, contentWidth);
136
+ const fitLine = (value: string) => fitText(t(value));
135
137
  return (
136
138
  <Box width={contentWidth} height={2} flexDirection="column">
137
139
  {!user && !hasSavedSession ? (
@@ -147,7 +149,9 @@ export function ChatComposerArea({
147
149
  ) : (
148
150
  <>
149
151
  <Text fg={colors.positive}>{fitLine("Verify your email to send messages.")}</Text>
150
- <Text fg={colors.textDim}>{fitLine("Ctrl+P, then Resend Verification Email")}</Text>
152
+ <Text fg={colors.textDim}>
153
+ {fitText(tf("{shortcut}, then Resend Verification Email", { shortcut: commandBarShortcut }))}
154
+ </Text>
151
155
  </>
152
156
  )}
153
157
  </Box>