gloomberb 0.10.4 → 0.11.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 (555) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +30 -3
  3. package/README.zh-CN.md +1 -1
  4. package/package.json +5 -5
  5. package/src/api-client/auth.ts +34 -0
  6. package/src/api-client/data.ts +25 -0
  7. package/src/api-client/index.test.ts +81 -0
  8. package/src/api-client/index.ts +37 -0
  9. package/src/api-client/request.test.ts +30 -0
  10. package/src/api-client/request.ts +44 -20
  11. package/src/api-client/socket-health.test.ts +79 -0
  12. package/src/api-client/socket.ts +108 -5
  13. package/src/api-client/types.ts +139 -0
  14. package/src/app/global-shortcuts.test.tsx +144 -9
  15. package/src/app/global-shortcuts.ts +33 -11
  16. package/src/app/runtime/broker-import.ts +39 -5
  17. package/src/app/runtime/startup.ts +7 -1
  18. package/src/app/runtime/update.ts +26 -1
  19. package/src/app.tsx +36 -19
  20. package/src/brokers/broker-ticker-sync.ts +3 -8
  21. package/src/brokers/sync-broker-instance.test.ts +59 -0
  22. package/src/brokers/sync-broker-instance.ts +37 -13
  23. package/src/capabilities/chart-series.ts +318 -0
  24. package/src/capabilities/factories.ts +46 -0
  25. package/src/capabilities/index.ts +1 -0
  26. package/src/capabilities/registry.test.ts +135 -0
  27. package/src/capabilities/registry.ts +31 -4
  28. package/src/capabilities/types.ts +52 -0
  29. package/src/cli/commands/overview.ts +2 -2
  30. package/src/cli/desktop-pane-shot.ts +2 -0
  31. package/src/cli/entry.ts +11 -1
  32. package/src/cli/native-smoke.ts +5 -0
  33. package/src/cli/pane-functions/catalog.ts +4 -0
  34. package/src/cli/pane-functions/data.ts +6 -1
  35. package/src/cli/pane-functions/discovery.ts +76 -10
  36. package/src/cli/pane-functions/report.test.ts +76 -0
  37. package/src/cli/pane-functions/report.ts +7 -1
  38. package/src/cli/pane-functions/resolver.ts +4 -1
  39. package/src/cli/pane-functions/screenshot.test.ts +56 -3
  40. package/src/cli/pane-functions/screenshot.ts +68 -5
  41. package/src/components/chart/composite/composite-chart.test.tsx +54 -0
  42. package/src/components/chart/composite/composite-chart.tsx +92 -15
  43. package/src/components/chart/composite/downsample.test.ts +136 -0
  44. package/src/components/chart/composite/downsample.ts +208 -0
  45. package/src/components/chart/composite/panel-series.test.ts +66 -0
  46. package/src/components/chart/composite/panel-series.ts +101 -0
  47. package/src/components/chart/multi-line/renderer.ts +1 -0
  48. package/src/components/chart/native/kitty/index.test.ts +25 -3
  49. package/src/components/chart/native/kitty/support.ts +37 -3
  50. package/src/components/chart/native/use-chart-text-fallback.ts +19 -0
  51. package/src/components/chart/static/chart/surface.tsx +43 -12
  52. package/src/components/chart/static/multi-line-chart-surface.tsx +41 -5
  53. package/src/components/command-bar/commands/direct/index.ts +18 -0
  54. package/src/components/command-bar/commands/registry.ts +14 -0
  55. package/src/components/command-bar/routes/root/article-results.test.ts +83 -0
  56. package/src/components/command-bar/routes/root/article-results.ts +55 -0
  57. package/src/components/command-bar/routes/root/command-items.ts +3 -0
  58. package/src/components/command-bar/routes/root/provider-search.ts +7 -3
  59. package/src/components/command-bar/routes/root/results.ts +38 -11
  60. package/src/components/command-bar/routes/root/runtime.ts +6 -2
  61. package/src/components/command-bar/routes/ticker-search/actions.ts +1 -1
  62. package/src/components/command-bar/routes/ticker-search/results.test.ts +34 -0
  63. package/src/components/command-bar/routes/ticker-search/results.ts +4 -6
  64. package/src/components/command-bar/routes/ticker-search/route.ts +2 -2
  65. package/src/components/command-bar/routing/list-state.ts +2 -1
  66. package/src/components/command-bar/surface/assist.test.tsx +13 -6
  67. package/src/components/command-bar/surface/index.test.tsx +65 -0
  68. package/src/components/command-bar/surface/index.tsx +35 -0
  69. package/src/components/command-bar/view-model.test.ts +43 -0
  70. package/src/components/command-bar/view-model.ts +41 -4
  71. package/src/components/command-bar/workflow/collection-actions.test.ts +94 -0
  72. package/src/components/command-bar/workflow/collection-actions.ts +14 -3
  73. package/src/components/command-bar/workflow/ops.test.ts +136 -0
  74. package/src/components/command-bar/workflow/ops.ts +71 -1
  75. package/src/components/index.ts +1 -1
  76. package/src/components/layout/header.test.tsx +35 -0
  77. package/src/components/layout/header.tsx +101 -9
  78. package/src/components/layout/pane/cycle-order.test.ts +0 -1
  79. package/src/components/layout/pane/footer/index.test.tsx +19 -19
  80. package/src/components/layout/pane/header.tsx +2 -13
  81. package/src/components/layout/shell/index.tsx +3 -0
  82. package/src/components/layout/status-bar.test.tsx +36 -1
  83. package/src/components/layout/status-bar.tsx +48 -2
  84. package/src/components/onboarding/account-step/chooser-panel.tsx +27 -50
  85. package/src/components/onboarding/account-step/form-panel.tsx +85 -29
  86. package/src/components/onboarding/account-step/index.tsx +10 -1
  87. package/src/components/onboarding/account-step/model.ts +3 -3
  88. package/src/components/onboarding/account-step/qr-panel.tsx +68 -0
  89. package/src/components/onboarding/account-step/signed-in-panel.tsx +4 -7
  90. package/src/components/onboarding/onboarding-frame.tsx +517 -0
  91. package/src/components/onboarding/onboarding-steps.tsx +0 -264
  92. package/src/components/onboarding/onboarding-wizard.test.tsx +626 -200
  93. package/src/components/onboarding/onboarding-wizard.tsx +848 -266
  94. package/src/components/onboarding/portfolio-step/broker-fields-panel.tsx +81 -51
  95. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +14 -20
  96. package/src/components/onboarding/portfolio-step/broker-sync-panel.tsx +8 -11
  97. package/src/components/onboarding/portfolio-step/choose-panel.tsx +18 -22
  98. package/src/components/onboarding/portfolio-step/index.tsx +3 -0
  99. package/src/components/onboarding/portfolio-step/types.ts +3 -0
  100. package/src/components/onboarding/wizard-account.ts +38 -3
  101. package/src/components/onboarding/wizard-broker-sync.ts +45 -67
  102. package/src/components/onboarding/wizard-model.ts +18 -2
  103. package/src/components/ui/data-table/opentui/index.tsx +29 -9
  104. package/src/components/ui/data-table/types.ts +2 -0
  105. package/src/components/ui/external-link.tsx +4 -2
  106. package/src/components/ui/loading.tsx +1 -1
  107. package/src/components/ui/status.tsx +76 -4
  108. package/src/components/ui/table-layout.test.ts +24 -0
  109. package/src/components/ui/table-layout.ts +90 -23
  110. package/src/components/ui/tabs.tsx +86 -4
  111. package/src/core/app-services.ts +14 -3
  112. package/src/core/connection-health.test.ts +143 -0
  113. package/src/core/connection-health.ts +295 -0
  114. package/src/core/state/app/layout-reducer.ts +34 -0
  115. package/src/core/state/app/layout.ts +21 -0
  116. package/src/core/state/app/state.ts +10 -0
  117. package/src/core/state/app/types.ts +7 -1
  118. package/src/data/config/chart-settings.ts +11 -2
  119. package/src/data/config/store/index.test.ts +83 -9
  120. package/src/data/config/store/migrations.ts +16 -0
  121. package/src/data/config/store/normalize.ts +59 -1
  122. package/src/data/fred-series.ts +8 -2
  123. package/src/i18n/es.ts +126 -0
  124. package/src/i18n/ja.ts +126 -0
  125. package/src/i18n/ko.ts +126 -0
  126. package/src/i18n/zh-cn.ts +126 -0
  127. package/src/i18n/zh-tw.ts +126 -0
  128. package/src/market-data/coordinator/chart.ts +3 -2
  129. package/src/market-data/hooks.tsx +3 -2
  130. package/src/market-data/market/status.test.ts +12 -0
  131. package/src/market-data/market/status.ts +34 -17
  132. package/src/news/aggregator.test.ts +39 -0
  133. package/src/news/aggregator.ts +78 -22
  134. package/src/news/hooks.ts +4 -0
  135. package/src/notifications/app-notifier.test.ts +28 -6
  136. package/src/notifications/app-notifier.ts +24 -5
  137. package/src/plugins/broker-sync/broker-sync.test.ts +130 -0
  138. package/src/plugins/broker-sync/native-loader.ts +10 -0
  139. package/src/plugins/broker-sync/normalize.ts +271 -0
  140. package/src/plugins/broker-sync/public.ts +122 -0
  141. package/src/plugins/broker-sync/robinhood-native.ts +367 -0
  142. package/src/plugins/broker-sync/robinhood.ts +114 -0
  143. package/src/plugins/broker-sync/simplefin-native.ts +201 -0
  144. package/src/plugins/broker-sync/simplefin.ts +70 -0
  145. package/src/plugins/builtin/account-management/footer.ts +8 -19
  146. package/src/plugins/builtin/account-management/pane.tsx +13 -8
  147. package/src/plugins/builtin/ai/pi/host.ts +6 -5
  148. package/src/plugins/builtin/ai/providers.ts +1 -1
  149. package/src/plugins/builtin/ai/screener/footer.ts +0 -7
  150. package/src/plugins/builtin/ai/screener/pane.test.tsx +2 -1
  151. package/src/plugins/builtin/ai/screener/pane.tsx +3 -1
  152. package/src/plugins/builtin/ai/screener/results-view.tsx +8 -0
  153. package/src/plugins/builtin/ai/screener/runner.ts +7 -3
  154. package/src/plugins/builtin/ai/workspace/pane.tsx +13 -2
  155. package/src/plugins/builtin/alerts/alert-engine.ts +13 -0
  156. package/src/plugins/builtin/alerts/constants.ts +2 -1
  157. package/src/plugins/builtin/alerts/index.tsx +67 -36
  158. package/src/plugins/builtin/alerts/pane.tsx +17 -65
  159. package/src/plugins/builtin/analytics/index.tsx +24 -11
  160. package/src/plugins/builtin/analytics/pane-model.ts +48 -14
  161. package/src/plugins/builtin/analytics/portfolio-selection.ts +19 -0
  162. package/src/plugins/builtin/analytics/view.tsx +5 -3
  163. package/src/plugins/builtin/broker-manager/detail.tsx +7 -2
  164. package/src/plugins/builtin/broker-manager/footer.ts +4 -9
  165. package/src/plugins/builtin/broker-manager/index.tsx +1 -6
  166. package/src/plugins/builtin/buildout/detail/ui.tsx +15 -4
  167. package/src/plugins/builtin/buildout/pane/header.tsx +1 -0
  168. package/src/plugins/builtin/changelog/index.tsx +60 -45
  169. package/src/plugins/builtin/chart-composer/catalog-inventory.test.ts +97 -0
  170. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +366 -0
  171. package/src/plugins/builtin/chart-composer/chart-spec.ts +21 -1
  172. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +307 -0
  173. package/src/plugins/builtin/chart-composer/editor/controller.ts +1 -1
  174. package/src/plugins/builtin/chart-composer/index.tsx +45 -5
  175. package/src/plugins/builtin/chart-composer/pane.tsx +7 -88
  176. package/src/plugins/builtin/chart-composer/presets.test.ts +48 -0
  177. package/src/plugins/builtin/chart-composer/presets.ts +69 -3
  178. package/src/plugins/builtin/chart-composer/quick-add.tsx +8 -5
  179. package/src/plugins/builtin/chart-composer/semantic.ts +18 -14
  180. package/src/plugins/builtin/chart-composer/series-catalog.test.ts +39 -0
  181. package/src/plugins/builtin/chart-composer/series-catalog.ts +104 -3
  182. package/src/plugins/builtin/chart-composer/shortcuts.test.ts +10 -10
  183. package/src/plugins/builtin/chart-composer/shortcuts.ts +4 -12
  184. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +230 -23
  185. package/src/plugins/builtin/chat/chat.test.tsx +22 -0
  186. package/src/plugins/builtin/chat/content/composer.tsx +6 -4
  187. package/src/plugins/builtin/chat/content/index.tsx +7 -0
  188. package/src/plugins/builtin/chat/content/snapshot.ts +9 -2
  189. package/src/plugins/builtin/chat/content/transcript.tsx +10 -0
  190. package/src/plugins/builtin/chat/controller/channel-actions.ts +11 -1
  191. package/src/plugins/builtin/chat/controller/channels.ts +13 -3
  192. package/src/plugins/builtin/chat/controller/index.ts +61 -7
  193. package/src/plugins/builtin/chat/controller/lifecycle.ts +3 -0
  194. package/src/plugins/builtin/chat/controller/message-loading.ts +5 -1
  195. package/src/plugins/builtin/chat/controller/messages.ts +3 -1
  196. package/src/plugins/builtin/chat/controller/notifications.ts +32 -11
  197. package/src/plugins/builtin/chat/controller/state.ts +7 -0
  198. package/src/plugins/builtin/chat/controller/view.ts +1 -0
  199. package/src/plugins/builtin/chat/controller.test.ts +117 -28
  200. package/src/plugins/builtin/cloud/auth-commands.ts +18 -0
  201. package/src/plugins/builtin/cloud/device-signin-dialog.tsx +243 -0
  202. package/src/plugins/builtin/cloud/device-signin.test.ts +217 -0
  203. package/src/plugins/builtin/cloud/device-signin.ts +240 -0
  204. package/src/plugins/builtin/cloud/plugin.tsx +7 -1
  205. package/src/plugins/builtin/cloud-tweets/table.tsx +57 -10
  206. package/src/plugins/builtin/composite-plugins.ts +39 -5
  207. package/src/plugins/builtin/congress-trades/detail.tsx +2 -4
  208. package/src/plugins/builtin/congress-trades/footer.ts +7 -16
  209. package/src/plugins/builtin/congress-trades/pane.tsx +12 -20
  210. package/src/plugins/builtin/connections/index.ts +53 -0
  211. package/src/plugins/builtin/connections/pane.test.tsx +79 -0
  212. package/src/plugins/builtin/connections/pane.tsx +258 -0
  213. package/src/plugins/builtin/correlation/index.test.tsx +2 -1
  214. package/src/plugins/builtin/correlation/index.tsx +58 -19
  215. package/src/plugins/builtin/correlation/matrix/model.ts +18 -7
  216. package/src/plugins/builtin/correlation/relationship/pane.tsx +43 -19
  217. package/src/plugins/builtin/correlation/settings.ts +1 -1
  218. package/src/plugins/builtin/credit-conditions/client.test.ts +80 -0
  219. package/src/plugins/builtin/credit-conditions/client.ts +105 -0
  220. package/src/plugins/builtin/credit-conditions/index.test.tsx +111 -0
  221. package/src/plugins/builtin/credit-conditions/index.tsx +227 -0
  222. package/src/plugins/builtin/credit-conditions/model.test.ts +43 -0
  223. package/src/plugins/builtin/credit-conditions/model.ts +64 -0
  224. package/src/plugins/builtin/debug/index.tsx +11 -6
  225. package/src/plugins/builtin/dividend-yield/client.test.ts +58 -0
  226. package/src/plugins/builtin/dividend-yield/client.ts +252 -0
  227. package/src/plugins/builtin/dividend-yield/index.tsx +61 -0
  228. package/src/plugins/builtin/dividend-yield/model.ts +83 -0
  229. package/src/plugins/builtin/dividend-yield/pane.tsx +305 -0
  230. package/src/plugins/builtin/dividend-yield/types.ts +22 -0
  231. package/src/plugins/builtin/earnings/data/cache.test.ts +10 -7
  232. package/src/plugins/builtin/earnings/data/cache.ts +23 -9
  233. package/src/plugins/builtin/earnings/index.tsx +57 -6
  234. package/src/plugins/builtin/econ/calendar-model.test.ts +37 -3
  235. package/src/plugins/builtin/econ/calendar-model.ts +32 -26
  236. package/src/plugins/builtin/econ/fred-cache.test.ts +8 -1
  237. package/src/plugins/builtin/econ/fred-series-map.ts +18 -0
  238. package/src/plugins/builtin/econ/index.test.tsx +105 -0
  239. package/src/plugins/builtin/econ/index.tsx +95 -52
  240. package/src/plugins/builtin/fear-greed/cache.test.ts +18 -3
  241. package/src/plugins/builtin/fear-greed/cache.ts +18 -6
  242. package/src/plugins/builtin/fear-greed/charts.tsx +9 -2
  243. package/src/plugins/builtin/fear-greed/format.ts +0 -9
  244. package/src/plugins/builtin/fear-greed/pane.tsx +21 -21
  245. package/src/plugins/builtin/futures/contracts.ts +100 -0
  246. package/src/plugins/builtin/futures/index.tsx +298 -0
  247. package/src/plugins/builtin/futures/model.test.ts +129 -0
  248. package/src/plugins/builtin/futures/model.ts +136 -0
  249. package/src/plugins/builtin/futures/pane.test.tsx +239 -0
  250. package/src/plugins/builtin/futures/table.test.ts +155 -0
  251. package/src/plugins/builtin/futures/table.tsx +241 -0
  252. package/src/plugins/builtin/fx-matrix/index.tsx +160 -134
  253. package/src/plugins/builtin/fx-matrix/pairs.ts +12 -1
  254. package/src/plugins/builtin/fx-matrix/pane.test.tsx +109 -0
  255. package/src/plugins/builtin/help/components.tsx +1 -30
  256. package/src/plugins/builtin/help/index.tsx +5 -25
  257. package/src/plugins/builtin/holders/pane.tsx +17 -12
  258. package/src/plugins/builtin/holders/treemap.tsx +4 -2
  259. package/src/plugins/builtin/insider/index.tsx +20 -10
  260. package/src/plugins/builtin/ipo-calendar/cache.ts +101 -0
  261. package/src/plugins/builtin/ipo-calendar/client.test.ts +31 -0
  262. package/src/plugins/builtin/ipo-calendar/client.ts +296 -0
  263. package/src/plugins/builtin/ipo-calendar/index.tsx +65 -0
  264. package/src/plugins/builtin/ipo-calendar/model.ts +145 -0
  265. package/src/plugins/builtin/ipo-calendar/pane.tsx +328 -0
  266. package/src/plugins/builtin/ipo-calendar/types.ts +19 -0
  267. package/src/plugins/builtin/kelly-sizer/index.test.tsx +3 -3
  268. package/src/plugins/builtin/kelly-sizer/model.ts +8 -1
  269. package/src/plugins/builtin/kelly-sizer/pane.tsx +33 -15
  270. package/src/plugins/builtin/kelly-sizer/view.tsx +12 -5
  271. package/src/plugins/builtin/market-halts/client.ts +114 -0
  272. package/src/plugins/builtin/market-halts/index.tsx +29 -0
  273. package/src/plugins/builtin/market-halts/model.test.ts +187 -0
  274. package/src/plugins/builtin/market-halts/model.ts +291 -0
  275. package/src/plugins/builtin/market-halts/pane.tsx +256 -0
  276. package/src/plugins/builtin/market-heatmap/data.test.ts +1 -0
  277. package/src/plugins/builtin/market-heatmap/data.ts +12 -3
  278. package/src/plugins/builtin/market-heatmap/index.tsx +131 -46
  279. package/src/plugins/builtin/market-movers/index.tsx +145 -63
  280. package/src/plugins/builtin/market-movers/model.ts +19 -3
  281. package/src/plugins/builtin/market-movers/screener.test.ts +108 -0
  282. package/src/plugins/builtin/market-movers/screener.ts +168 -10
  283. package/src/plugins/builtin/news/index.tsx +78 -99
  284. package/src/plugins/builtin/news/wire/article-search.test.ts +72 -0
  285. package/src/plugins/builtin/news/wire/article-search.ts +140 -0
  286. package/src/plugins/builtin/news/wire/breaking/pane.tsx +18 -8
  287. package/src/plugins/builtin/news/wire/categories.ts +35 -0
  288. package/src/plugins/builtin/news/wire/default-feeds.ts +47 -1
  289. package/src/plugins/builtin/news/wire/feed-config.test.ts +10 -5
  290. package/src/plugins/builtin/news/wire/index.tsx +41 -4
  291. package/src/plugins/builtin/news/wire/industry-pane.tsx +38 -19
  292. package/src/plugins/builtin/news/wire/news/detail-view.tsx +10 -5
  293. package/src/plugins/builtin/news/wire/news/footer.ts +1 -0
  294. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +15 -6
  295. package/src/plugins/builtin/news/wire/news/query-presets.ts +16 -2
  296. package/src/plugins/builtin/news/wire/news/table.test.tsx +54 -0
  297. package/src/plugins/builtin/news/wire/news/table.tsx +111 -17
  298. package/src/plugins/builtin/notes/files.test.ts +18 -0
  299. package/src/plugins/builtin/notes/files.ts +10 -4
  300. package/src/plugins/builtin/notes/index.tsx +4 -1
  301. package/src/plugins/builtin/notes/quick-notes-pane.test.tsx +182 -0
  302. package/src/plugins/builtin/notes/quick-notes-pane.tsx +74 -31
  303. package/src/plugins/builtin/notes/ticker-notes-tab.test.tsx +380 -0
  304. package/src/plugins/builtin/notes/ticker-notes-tab.tsx +97 -40
  305. package/src/plugins/builtin/options/calc-seed.test.ts +118 -0
  306. package/src/plugins/builtin/options/calc-seed.ts +52 -0
  307. package/src/plugins/builtin/options/footer.ts +4 -1
  308. package/src/plugins/builtin/options/index.tsx +22 -1
  309. package/src/plugins/builtin/options/live-quotes.ts +8 -0
  310. package/src/plugins/builtin/options/table.ts +3 -1
  311. package/src/plugins/builtin/options/view.test.tsx +5 -1
  312. package/src/plugins/builtin/options/view.tsx +112 -17
  313. package/src/plugins/builtin/options-calculator/index.tsx +35 -0
  314. package/src/plugins/builtin/options-calculator/model.test.ts +172 -0
  315. package/src/plugins/builtin/options-calculator/model.ts +273 -0
  316. package/src/plugins/builtin/options-calculator/pane.test.tsx +138 -0
  317. package/src/plugins/builtin/options-calculator/pane.tsx +218 -0
  318. package/src/plugins/builtin/plugin-module.test.ts +16 -2
  319. package/src/plugins/builtin/plugin-module.ts +5 -1
  320. package/src/plugins/builtin/polls/client.ts +57 -0
  321. package/src/plugins/builtin/polls/hooks.ts +20 -0
  322. package/src/plugins/builtin/polls/index.tsx +35 -0
  323. package/src/plugins/builtin/polls/normalize.test.ts +230 -0
  324. package/src/plugins/builtin/polls/normalize.ts +319 -0
  325. package/src/plugins/builtin/polls/pane.tsx +722 -0
  326. package/src/plugins/builtin/polls/types.ts +77 -0
  327. package/src/plugins/builtin/portfolio-list/header.tsx +15 -4
  328. package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -3
  329. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +1 -1
  330. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -0
  331. package/src/plugins/builtin/portfolio-list/summary/index.tsx +2 -1
  332. package/src/plugins/builtin/research/analyst-pane.tsx +40 -67
  333. package/src/plugins/builtin/research/corporate-actions-pane.tsx +25 -19
  334. package/src/plugins/builtin/research/index.test.ts +6 -25
  335. package/src/plugins/builtin/research/index.tsx +11 -5
  336. package/src/plugins/builtin/research/relative-valuation-pane.tsx +86 -26
  337. package/src/plugins/builtin/scanner/denied.tsx +31 -0
  338. package/src/plugins/builtin/scanner/feed.ts +94 -0
  339. package/src/plugins/builtin/scanner/flow-model.test.ts +85 -0
  340. package/src/plugins/builtin/scanner/flow-model.ts +133 -0
  341. package/src/plugins/builtin/scanner/flow-pane.tsx +197 -0
  342. package/src/plugins/builtin/scanner/hilo-bars.tsx +121 -0
  343. package/src/plugins/builtin/scanner/hilo-model.test.ts +44 -0
  344. package/src/plugins/builtin/scanner/hilo-model.ts +75 -0
  345. package/src/plugins/builtin/scanner/hilo-pane.tsx +165 -0
  346. package/src/plugins/builtin/scanner/index.ts +109 -0
  347. package/src/plugins/builtin/scanner/waiting.tsx +11 -0
  348. package/src/plugins/builtin/sec/index.tsx +11 -7
  349. package/src/plugins/builtin/sectors/index.tsx +105 -81
  350. package/src/plugins/builtin/sectors/move-bar.tsx +43 -0
  351. package/src/plugins/builtin/sectors/sector-data.ts +18 -0
  352. package/src/plugins/builtin/sectors/sector-model.ts +13 -15
  353. package/src/plugins/builtin/shared/auto-refresh.ts +51 -0
  354. package/src/plugins/builtin/shared/cloud-upgrade.ts +29 -5
  355. package/src/plugins/builtin/shared/pane-footer.ts +16 -3
  356. package/src/plugins/builtin/shared/screener-live-quotes.test.ts +91 -0
  357. package/src/plugins/builtin/shared/screener-live-quotes.ts +134 -0
  358. package/src/plugins/builtin/shared/table-pane.ts +1 -5
  359. package/src/plugins/builtin/shared/use-quote-board.test.tsx +211 -0
  360. package/src/plugins/builtin/shared/use-quote-board.ts +195 -0
  361. package/src/plugins/builtin/short-interest/client.ts +124 -0
  362. package/src/plugins/builtin/short-interest/index.tsx +62 -0
  363. package/src/plugins/builtin/short-interest/model.ts +113 -0
  364. package/src/plugins/builtin/short-interest/pane.tsx +252 -0
  365. package/src/plugins/builtin/short-interest/types.ts +9 -0
  366. package/src/plugins/builtin/substack/article-detail.tsx +15 -1
  367. package/src/plugins/builtin/substack/pane-footer.ts +3 -22
  368. package/src/plugins/builtin/substack/pane.tsx +1 -2
  369. package/src/plugins/builtin/thirteenf/api.ts +4 -1
  370. package/src/plugins/builtin/thirteenf/model.ts +2 -0
  371. package/src/plugins/builtin/thirteenf/pane.tsx +6 -2
  372. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +10 -4
  373. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +67 -11
  374. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +15 -8
  375. package/src/plugins/builtin/ticker-detail/index.tsx +5 -21
  376. package/src/plugins/builtin/ticker-detail/overview/components.tsx +72 -32
  377. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +0 -10
  378. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -1
  379. package/src/plugins/builtin/ticker-detail/quote-monitor/card.test.tsx +70 -0
  380. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +34 -26
  381. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +96 -49
  382. package/src/plugins/builtin/ticker-detail/research-tabs.ts +32 -0
  383. package/src/plugins/builtin/ticker-research-backend-plugin.ts +2 -9
  384. package/src/plugins/builtin/ticker-research-plugin.ts +6 -0
  385. package/src/plugins/builtin/treasury-auctions/cache.test.ts +173 -0
  386. package/src/plugins/builtin/treasury-auctions/cache.ts +104 -0
  387. package/src/plugins/builtin/treasury-auctions/client.test.ts +181 -0
  388. package/src/plugins/builtin/treasury-auctions/client.ts +151 -0
  389. package/src/plugins/builtin/treasury-auctions/index.tsx +90 -0
  390. package/src/plugins/builtin/treasury-auctions/model.test.ts +147 -0
  391. package/src/plugins/builtin/treasury-auctions/model.ts +188 -0
  392. package/src/plugins/builtin/treasury-auctions/pane.test.tsx +170 -0
  393. package/src/plugins/builtin/treasury-auctions/pane.tsx +417 -0
  394. package/src/plugins/builtin/treasury-auctions/types.ts +59 -0
  395. package/src/plugins/builtin/tv/pane.tsx +67 -7
  396. package/src/plugins/builtin/tv/youtube-embed.test.ts +84 -0
  397. package/src/plugins/builtin/tv/youtube-embed.ts +173 -0
  398. package/src/plugins/builtin/tv/youtube-stream.ts +27 -2
  399. package/src/plugins/builtin/volatility/client.ts +87 -0
  400. package/src/plugins/builtin/volatility/index.test.tsx +107 -0
  401. package/src/plugins/builtin/volatility/index.tsx +193 -0
  402. package/src/plugins/builtin/volatility/model.test.ts +66 -0
  403. package/src/plugins/builtin/volatility/model.ts +113 -0
  404. package/src/plugins/builtin/world-indices/footer.ts +43 -23
  405. package/src/plugins/builtin/world-indices/index.tsx +44 -93
  406. package/src/plugins/builtin/world-indices/indices.ts +11 -2
  407. package/src/plugins/builtin/world-indices/model.ts +17 -24
  408. package/src/plugins/builtin/world-indices/pane.test.tsx +116 -0
  409. package/src/plugins/builtin/world-indices/table.tsx +82 -37
  410. package/src/plugins/builtin/yield-curve/index.tsx +16 -7
  411. package/src/plugins/builtin/yield-curve/treasury-data.test.ts +25 -0
  412. package/src/plugins/builtin/yield-curve/treasury-data.ts +16 -0
  413. package/src/plugins/catalog-backend.ts +8 -0
  414. package/src/plugins/catalog-ui.ts +8 -0
  415. package/src/plugins/event-bus.ts +1 -0
  416. package/src/plugins/ibkr/gateway/account-loaders.ts +1 -1
  417. package/src/plugins/ibkr/trade/tab/footer.ts +9 -23
  418. package/src/plugins/ibkr/trade/tab/index.tsx +1 -5
  419. package/src/plugins/ibkr/trading/pane-view.tsx +149 -87
  420. package/src/plugins/ibkr/trading/pane.tsx +2 -2
  421. package/src/plugins/ownership.ts +3 -0
  422. package/src/plugins/pane-manager/floating.ts +8 -2
  423. package/src/plugins/pane-manager.test.ts +13 -0
  424. package/src/plugins/prediction-markets/backend-plugin.ts +2 -0
  425. package/src/plugins/prediction-markets/capability.test.ts +281 -0
  426. package/src/plugins/prediction-markets/capability.ts +171 -0
  427. package/src/plugins/prediction-markets/chart.tsx +18 -26
  428. package/src/plugins/prediction-markets/columns.ts +3 -2
  429. package/src/plugins/prediction-markets/controller/catalog.ts +15 -12
  430. package/src/plugins/prediction-markets/controller/detail.ts +4 -0
  431. package/src/plugins/prediction-markets/controller/index.ts +0 -15
  432. package/src/plugins/prediction-markets/detail/book.tsx +3 -23
  433. package/src/plugins/prediction-markets/detail/overview.tsx +0 -18
  434. package/src/plugins/prediction-markets/detail/pane.tsx +36 -12
  435. package/src/plugins/prediction-markets/detail.test.tsx +1 -1
  436. package/src/plugins/prediction-markets/index.tsx +2 -0
  437. package/src/plugins/prediction-markets/metrics.ts +28 -18
  438. package/src/plugins/prediction-markets/navigation.ts +1 -0
  439. package/src/plugins/prediction-markets/pane.test.tsx +1 -1
  440. package/src/plugins/prediction-markets/pane.tsx +0 -1
  441. package/src/plugins/prediction-markets/services/fetch.ts +3 -2
  442. package/src/plugins/prediction-markets/services/kalshi/adapter.ts +82 -23
  443. package/src/plugins/prediction-markets/services/kalshi/normalize.ts +3 -1
  444. package/src/plugins/prediction-markets/services/kalshi/types.ts +14 -0
  445. package/src/plugins/prediction-markets/services/polymarket/adapter.ts +26 -14
  446. package/src/plugins/prediction-markets/services/polymarket/detail.ts +67 -18
  447. package/src/plugins/prediction-markets/test-helpers.tsx +0 -1
  448. package/src/plugins/prediction-markets/types.ts +3 -9
  449. package/src/plugins/registry/context.ts +6 -1
  450. package/src/plugins/registry/index.test.ts +2 -0
  451. package/src/plugins/registry/index.ts +53 -3
  452. package/src/plugins/runtime/context.tsx +4 -2
  453. package/src/plugins/runtime/index.tsx +9 -0
  454. package/src/remote/controller.test.ts +17 -4
  455. package/src/renderers/electrobun/bun/application-menu/index.ts +1 -1
  456. package/src/renderers/electrobun/bun/core-capabilities.ts +2 -2
  457. package/src/renderers/electrobun/bun/desktop/capability-bridge.test.ts +64 -0
  458. package/src/renderers/electrobun/bun/desktop/capability-bridge.ts +41 -7
  459. package/src/renderers/electrobun/bun/desktop/host-requests.ts +5 -2
  460. package/src/renderers/electrobun/bun/desktop/http-fetch.test.ts +33 -0
  461. package/src/renderers/electrobun/bun/desktop/http-fetch.ts +53 -6
  462. package/src/renderers/electrobun/bun/desktop/initialization.test.ts +27 -0
  463. package/src/renderers/electrobun/bun/desktop/initialization.ts +6 -1
  464. package/src/renderers/electrobun/bun/index.ts +1 -0
  465. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  466. package/src/renderers/electrobun/view/app-services.ts +25 -2
  467. package/src/renderers/electrobun/view/build-assets.ts +5 -0
  468. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +186 -65
  469. package/src/renderers/electrobun/view/cli-pane-shot-health.test.ts +25 -0
  470. package/src/renderers/electrobun/view/cli-pane-shot-health.ts +31 -0
  471. package/src/renderers/electrobun/view/data-table/index.tsx +10 -11
  472. package/src/renderers/electrobun/view/data-table/row.tsx +2 -0
  473. package/src/renderers/electrobun/view/desktop/controls.test.tsx +68 -0
  474. package/src/renderers/electrobun/view/desktop/controls.tsx +25 -1
  475. package/src/renderers/electrobun/view/host/box.test.tsx +80 -0
  476. package/src/renderers/electrobun/view/host/media-surface.tsx +70 -3
  477. package/src/renderers/electrobun/view/host/tabs.tsx +97 -5
  478. package/src/renderers/electrobun/view/host/text.tsx +5 -2
  479. package/src/renderers/electrobun/view/input-host.test.tsx +140 -0
  480. package/src/renderers/electrobun/view/input-host.tsx +4 -2
  481. package/src/renderers/electrobun/view/native-stubs/backend-rpc.ts +19 -1
  482. package/src/renderers/electrobun/view/native-stubs/broker-sync-native-loader.ts +7 -0
  483. package/src/renderers/electrobun/view/native-stubs/chart/kitty-support.ts +10 -0
  484. package/src/renderers/electrobun/view/remote/capability-invoker.test.ts +46 -0
  485. package/src/renderers/electrobun/view/remote/capability-invoker.ts +34 -13
  486. package/src/renderers/electrobun/view/remote/connection-health-backend.ts +19 -0
  487. package/src/renderers/electrobun/view/remote/connection-health.test.ts +44 -0
  488. package/src/renderers/electrobun/view/remote/connection-health.ts +49 -0
  489. package/src/renderers/electrobun/view/styles.css +5 -0
  490. package/src/renderers/opentui/dialog-host.tsx +279 -80
  491. package/src/renderers/opentui/host.tsx +15 -1
  492. package/src/renderers/opentui/terminal-media.test.ts +102 -0
  493. package/src/renderers/opentui/terminal-media.ts +142 -0
  494. package/src/renderers/opentui/test-utils.tsx +9 -6
  495. package/src/renderers/opentui/toast-host.tsx +136 -7
  496. package/src/renderers/opentui/ui-host.tsx +19 -3
  497. package/src/sources/provider-router/connection-health.test.ts +28 -0
  498. package/src/sources/provider-router/connection-health.ts +47 -0
  499. package/src/sources/provider-router/history.test.ts +53 -4
  500. package/src/sources/provider-router/history.ts +119 -14
  501. package/src/sources/provider-router/index.ts +12 -1
  502. package/src/sources/yahoo-finance/history.ts +2 -9
  503. package/src/sources/yahoo-finance/http.ts +1 -1
  504. package/src/sources/yahoo-finance/symbols.ts +1 -1
  505. package/src/sources/yahoo-finance/types.ts +19 -0
  506. package/src/state/app/context/index.test.tsx +101 -0
  507. package/src/state/app/context/index.tsx +10 -0
  508. package/src/state/config-save-scheduler.ts +1 -6
  509. package/src/state/persist-scheduler.test.ts +31 -0
  510. package/src/state/persist-scheduler.ts +21 -8
  511. package/src/sync/auth-transition.test.ts +36 -0
  512. package/src/sync/auth-transition.ts +22 -0
  513. package/src/sync/core-contributors.test.ts +58 -1
  514. package/src/sync/core-contributors.ts +39 -2
  515. package/src/sync/react.ts +9 -0
  516. package/src/test-support/plugin-runtime.ts +3 -0
  517. package/src/theme/font-scale.test.ts +14 -0
  518. package/src/theme/font-scale.ts +69 -0
  519. package/src/tickers/search/index.test.ts +284 -2
  520. package/src/tickers/search/index.ts +55 -18
  521. package/src/tickers/search/ranking.ts +143 -18
  522. package/src/tickers/search/types.ts +1 -0
  523. package/src/time-series/alignment.ts +15 -11
  524. package/src/time-series/field-catalog.ts +10 -0
  525. package/src/time-series/fundamentals.ts +19 -11
  526. package/src/time-series/hooks.ts +8 -2
  527. package/src/time-series/parsed-history-cache.ts +35 -0
  528. package/src/time-series/resolution.ts +10 -0
  529. package/src/time-series/resolve.test.ts +332 -3
  530. package/src/time-series/resolve.ts +225 -25
  531. package/src/time-series/spec-studies.test.ts +23 -0
  532. package/src/time-series/spec.ts +21 -2
  533. package/src/time-series/transforms.test.ts +76 -1
  534. package/src/time-series/transforms.ts +39 -8
  535. package/src/time-series/types.ts +9 -2
  536. package/src/time-series/use-chart-resolution.test.tsx +105 -1
  537. package/src/time-series/use-chart-resolution.ts +67 -13
  538. package/src/types/config.ts +30 -27
  539. package/src/types/media.ts +2 -0
  540. package/src/types/plugin.ts +8 -1
  541. package/src/ui/host.tsx +4 -0
  542. package/src/ui/pointer-drag.ts +12 -0
  543. package/src/ui/qr.test.ts +79 -0
  544. package/src/ui/qr.ts +82 -0
  545. package/src/updater/github-releases.test.ts +3 -3
  546. package/src/updater/github-releases.ts +1 -1
  547. package/src/updater.test.ts +117 -16
  548. package/src/updater.ts +32 -5
  549. package/src/utils/exchanges.ts +1 -0
  550. package/src/utils/external-url.test.ts +32 -0
  551. package/src/utils/external-url.ts +23 -0
  552. package/src/utils/sort-values.ts +31 -0
  553. package/src/utils/zoned-date-time.ts +67 -0
  554. package/src/components/onboarding/wizard-keyboard.ts +0 -306
  555. package/src/plugins/builtin/chart-composer/date-window-dialog.tsx +0 -120
@@ -95,7 +95,7 @@ async function runFearGreed(_args: string[], ctx: Parameters<CliCommandDef["exec
95
95
  "fear-greed",
96
96
  ));
97
97
  try {
98
- const data = await loadFearGreed(ctx.cliOptions.refresh);
98
+ const { data } = await loadFearGreed(ctx.cliOptions.refresh);
99
99
  ctx.printResult({
100
100
  data: [{
101
101
  score: data.overall.score,
@@ -119,7 +119,7 @@ async function runEcon(args: string[], ctx: Parameters<CliCommandDef["execute"]>
119
119
  const country = (takeOption(rawArgs, "--country") ?? "all") as CountryFilter;
120
120
  const impact = (takeOption(rawArgs, "--impact") ?? "all") as ImpactFilter;
121
121
  await withCliServices(ctx, async (services) => {
122
- const events = await loadCalendar(ctx.cliOptions.refresh);
122
+ const { data: events } = await loadCalendar(ctx.cliOptions.refresh);
123
123
  const rows = events
124
124
  .filter((event) => matchesCountry(event, country) && matchesImpact(event, impact))
125
125
  .sort((left, right) => left.date.getTime() - right.date.getTime())
@@ -3,6 +3,7 @@ import { tmpdir } from "os";
3
3
  import { join } from "path";
4
4
  import { pathToFileURL } from "url";
5
5
  import type { AppConfig } from "../types/config";
6
+ import type { ResolvedSeries } from "../time-series/types";
6
7
  import type { OptionsChain, TickerFinancials } from "../types/financials";
7
8
  import type { TickerRecord } from "../types/ticker";
8
9
  import type { PaneRuntimeState } from "../core/state/app/state";
@@ -24,6 +25,7 @@ export interface DesktopPaneShotPayload {
24
25
  financials: Array<[string, TickerFinancials]>;
25
26
  optionsChains: Array<[string, OptionsChain]>;
26
27
  fredSeries: Array<[string, FredSeriesCacheEntry]>;
28
+ capabilitySeries: Array<[string, ResolvedSeries]>;
27
29
  paneState: Record<string, PaneRuntimeState>;
28
30
  }
29
31
 
package/src/cli/entry.ts CHANGED
@@ -2,7 +2,12 @@ import { dispatchCli } from "./index";
2
2
  import { fail, inferCliErrorOptions, printCliError } from "./errors";
3
3
  import { loadExternalPlugins } from "../plugins/loader";
4
4
  import type { CliLaunchRequest } from "../types/plugin";
5
- import { OPEN_TUI_NATIVE_SMOKE_COMMAND, smokeOpenTuiNative } from "./native-smoke";
5
+ import {
6
+ OPEN_TUI_NATIVE_SMOKE_COMMAND,
7
+ OPEN_TUI_RUNTIME_SMOKE_COMMAND,
8
+ smokeOpenTuiNative,
9
+ smokeOpenTuiRuntime,
10
+ } from "./native-smoke";
6
11
 
7
12
  async function launchOpenTuiApp(options: {
8
13
  externalPlugins: Awaited<ReturnType<typeof loadExternalPlugins>>;
@@ -26,6 +31,11 @@ export async function runCliEntrypoint(rawArgs = process.argv.slice(2)): Promise
26
31
  process.exit(0);
27
32
  }
28
33
 
34
+ if (command === OPEN_TUI_RUNTIME_SMOKE_COMMAND) {
35
+ await smokeOpenTuiRuntime();
36
+ process.exit(0);
37
+ }
38
+
29
39
  const externalPlugins = await loadExternalPlugins();
30
40
 
31
41
  if (!command) {
@@ -1,5 +1,10 @@
1
1
  export const OPEN_TUI_NATIVE_SMOKE_COMMAND = "__gloomberb-smoke-opentui-native";
2
+ export const OPEN_TUI_RUNTIME_SMOKE_COMMAND = "__gloomberb-smoke-opentui-runtime";
2
3
 
3
4
  export async function smokeOpenTuiNative(): Promise<void> {
4
5
  await import("../renderers/opentui/native-smoke");
5
6
  }
7
+
8
+ export async function smokeOpenTuiRuntime(): Promise<void> {
9
+ await import("../renderers/opentui/start");
10
+ }
@@ -55,8 +55,12 @@ function registerResolverToken(
55
55
 
56
56
  export function buildPaneFunctionLookup(registry: PaneFunctionCatalog): Map<string, PaneTemplateDef | PaneDef> {
57
57
  const lookup = new Map<string, PaneTemplateDef | PaneDef>();
58
+ // Shortcut prefixes first: "AI" must open the pane that owns the shortcut, not
59
+ // one that happens to list "ai" as a keyword.
58
60
  for (const template of registry.paneTemplates.values()) {
59
61
  registerResolverToken(lookup, template.shortcut?.prefix, template);
62
+ }
63
+ for (const template of registry.paneTemplates.values()) {
60
64
  registerResolverToken(lookup, template.id, template);
61
65
  registerResolverToken(lookup, template.label, template);
62
66
  for (const keyword of template.keywords ?? []) registerResolverToken(lookup, keyword, template);
@@ -93,9 +93,14 @@ export function collectShotSymbols(resolved: ResolvedPaneFunction, rawArg: strin
93
93
  )).filter(Boolean))];
94
94
  }
95
95
  }
96
+ // A text argument is a phrase, not a symbol, so it must never be looked up as one.
97
+ const argIsTicker = resolved.template?.shortcut?.argKind !== "text";
96
98
  let symbols = resolved.createOptions?.symbols?.length
97
99
  ? resolved.createOptions.symbols
98
- : [resolved.createOptions?.symbol ?? normalizeTickerInput(null, cleanTickerInput(rawArg))].filter((symbol): symbol is string => !!symbol);
100
+ : [
101
+ resolved.createOptions?.symbol
102
+ ?? (argIsTicker ? normalizeTickerInput(null, cleanTickerInput(rawArg)) : null),
103
+ ].filter((symbol): symbol is string => !!symbol);
99
104
  if (resolved.capability.id === "security-relationship" && symbols.length === 1) {
100
105
  symbols = [...symbols, "SPY"];
101
106
  }
@@ -1,3 +1,6 @@
1
+ import { ConnectionHealthRegistry } from "../../core/connection-health";
2
+ import type { AppConfig } from "../../types/config";
3
+ import type { DataProvider } from "../../types/data-provider";
1
4
  import type {
2
5
  GloomPlugin,
3
6
  GloomPluginContext,
@@ -6,17 +9,69 @@ import type {
6
9
  PluginPersistence,
7
10
  } from "../../types/plugin";
8
11
  import type { PersistedResourceValue } from "../../types/persistence";
12
+ import type { TickerRepository } from "../../data/ticker-repository";
9
13
  import type { MarketContext } from "../types";
10
14
  import type { PaneFunctionCatalog } from "./catalog";
11
15
 
12
- export async function createPaneCatalog(context: MarketContext, plugins: GloomPlugin[]): Promise<PaneFunctionCatalog> {
13
- const panes = new Map<string, PaneDef>();
14
- const paneTemplates = new Map<string, PaneTemplateDef>();
15
- const setupPlugins: GloomPlugin[] = [];
16
+ export interface PaneDiscoveryOptions {
17
+ getConfig: () => AppConfig;
18
+ marketData?: DataProvider;
19
+ tickerRepository?: TickerRepository;
20
+ panes?: Map<string, PaneDef>;
21
+ paneTemplates?: Map<string, PaneTemplateDef>;
22
+ }
23
+
24
+ /** Throws only if a plugin actually reaches for a dependency discovery has no answer for. */
25
+ function unavailable<T>(name: string): T {
26
+ return new Proxy({}, {
27
+ get() {
28
+ throw new Error(`${name} is unavailable during pane discovery.`);
29
+ },
30
+ }) as T;
31
+ }
32
+
33
+ /**
34
+ * Context that collects `setup()`-registered panes and templates without
35
+ * booting the real plugin runtime or its polling engines. Keep this object
36
+ * exhaustively typed so additions to GloomPluginContext fail at compile time
37
+ * instead of crashing `gloomberb catalog` at runtime.
38
+ */
39
+ export function createPaneDiscoveryContext(options: PaneDiscoveryOptions): GloomPluginContext & {
40
+ panes: Map<string, PaneDef>;
41
+ paneTemplates: Map<string, PaneTemplateDef>;
42
+ } {
43
+ const panes = options.panes ?? new Map<string, PaneDef>();
44
+ const paneTemplates = options.paneTemplates ?? new Map<string, PaneTemplateDef>();
45
+ return {
46
+ ...buildDiscoveryContext({
47
+ panes,
48
+ paneTemplates,
49
+ getConfig: options.getConfig,
50
+ marketData: options.marketData ?? unavailable<DataProvider>("Market data"),
51
+ tickerRepository: options.tickerRepository ?? unavailable<TickerRepository>("The ticker repository"),
52
+ connectionHealth: new ConnectionHealthRegistry(),
53
+ }),
54
+ panes,
55
+ paneTemplates,
56
+ };
57
+ }
58
+
59
+ function buildDiscoveryContext({
60
+ panes,
61
+ paneTemplates,
62
+ getConfig,
63
+ marketData,
64
+ tickerRepository,
65
+ connectionHealth,
66
+ }: {
67
+ panes: Map<string, PaneDef>;
68
+ paneTemplates: Map<string, PaneTemplateDef>;
69
+ getConfig: () => AppConfig;
70
+ marketData: DataProvider;
71
+ tickerRepository: TickerRepository;
72
+ connectionHealth: ConnectionHealthRegistry;
73
+ }): GloomPluginContext {
16
74
  const fakePersistence = createDiscoveryPluginPersistence();
17
- // Collect setup-registered panes without booting the real plugin runtime or polling engines.
18
- // Keep this object exhaustively typed so additions to GloomPluginContext fail at
19
- // compile time instead of crashing `gloomberb catalog` at runtime.
20
75
  const discoveryContext: GloomPluginContext = {
21
76
  registerPane: (pane: PaneDef) => panes.set(pane.id, pane),
22
77
  registerPaneTemplate: (template: PaneTemplateDef) => paneTemplates.set(template.id, template),
@@ -33,10 +88,11 @@ export async function createPaneCatalog(context: MarketContext, plugins: GloomPl
33
88
  watchNewsQuery: () => () => {},
34
89
  getData: () => null,
35
90
  getTicker: () => null,
36
- getConfig: () => context.config,
91
+ getConfig,
37
92
  getPaneDef: (paneId: string) => panes.get(paneId),
38
- marketData: context.dataProvider,
39
- tickerRepository: context.store,
93
+ marketData,
94
+ connectionHealth,
95
+ tickerRepository,
40
96
  persistence: fakePersistence,
41
97
  log: {
42
98
  debug: () => {},
@@ -84,6 +140,16 @@ export async function createPaneCatalog(context: MarketContext, plugins: GloomPl
84
140
  emit: () => {},
85
141
  notify: () => {},
86
142
  };
143
+ return discoveryContext;
144
+ }
145
+
146
+ export async function createPaneCatalog(context: MarketContext, plugins: GloomPlugin[]): Promise<PaneFunctionCatalog> {
147
+ const setupPlugins: GloomPlugin[] = [];
148
+ const { panes, paneTemplates, ...discoveryContext } = createPaneDiscoveryContext({
149
+ getConfig: () => context.config,
150
+ marketData: context.dataProvider,
151
+ tickerRepository: context.store,
152
+ });
87
153
 
88
154
  for (const plugin of plugins) {
89
155
  for (const pane of plugin.panes ?? []) panes.set(pane.id, pane);
@@ -0,0 +1,76 @@
1
+ import { afterEach, expect, test } from "bun:test";
2
+ import { createDefaultConfig } from "../../types/config";
3
+ import { CHART_SPEC_VERSION, type ChartSpec } from "../../time-series/types";
4
+ import { createTestDataProvider } from "../../test-support/data-provider";
5
+ import { setSharedRegistryForTests } from "../../plugins/registry";
6
+ import { buildFunctionReport } from "./report";
7
+ import type { ResolvedPaneFunction } from "./resolver";
8
+ import type { MarketContext } from "../types";
9
+
10
+ const spec: ChartSpec = {
11
+ version: CHART_SPEC_VERSION,
12
+ viewport: { range: "1M", resolution: "auto" },
13
+ panels: [{ id: "main" }],
14
+ series: [{
15
+ id: "prediction",
16
+ source: { kind: "capability", capabilityId: "prediction.series", seriesId: "market-one" },
17
+ style: "area",
18
+ transform: "raw",
19
+ axis: "auto",
20
+ panelId: "main",
21
+ interpolation: "none",
22
+ }],
23
+ studies: [],
24
+ };
25
+
26
+ afterEach(() => setSharedRegistryForTests(undefined));
27
+
28
+ test("chart composer reports accept capability-backed series", async () => {
29
+ const date = new Date(Date.now() - 24 * 60 * 60 * 1_000);
30
+ setSharedRegistryForTests({
31
+ capabilityManifests: () => [{
32
+ id: "prediction.series",
33
+ kind: "chart-series",
34
+ name: "Prediction",
35
+ operations: [{ id: "resolve", kind: "query", rendererSafe: true }],
36
+ }],
37
+ invokeCapability: async () => ({
38
+ id: "provider-id",
39
+ label: "Prediction",
40
+ color: "#fff",
41
+ unit: "probability",
42
+ unitGroup: "probability",
43
+ nativeFrequency: "daily",
44
+ dataShape: "scalar",
45
+ style: "line",
46
+ transform: "raw",
47
+ axis: "left",
48
+ panelId: "main",
49
+ interpolation: "none",
50
+ points: [{ date, observedAt: date, value: 0.62 }],
51
+ }),
52
+ } as any);
53
+ const resolved = {
54
+ token: "chart-composer",
55
+ label: "Chart Composer",
56
+ description: "",
57
+ pane: { id: "chart-composer", name: "Chart Composer" },
58
+ instance: { settings: { chartSpec: spec } },
59
+ capability: { id: "chart-composer", reportReadiness: "ready" },
60
+ } as unknown as ResolvedPaneFunction;
61
+ const context = {
62
+ config: createDefaultConfig("/tmp/gloomberb-chart-report"),
63
+ dataProvider: createTestDataProvider(),
64
+ store: { loadTicker: async () => null },
65
+ } as unknown as MarketContext;
66
+
67
+ const report = await buildFunctionReport(resolved, context, "");
68
+ expect(report.data).toMatchObject({
69
+ kind: "chart-composer",
70
+ complete: true,
71
+ empty: false,
72
+ unavailableSymbols: [],
73
+ rowCount: 1,
74
+ });
75
+ expect(report.text).toContain("Prediction");
76
+ });
@@ -29,6 +29,8 @@ import { publicTickerKey } from "../../utils/exchanges";
29
29
  import { apiClient } from "../../api-client";
30
30
  import { parseChartSpec } from "../../plugins/builtin/chart-composer/chart-spec";
31
31
  import { resolveChartSpecData } from "../../time-series/resolve";
32
+ import { createChartSeriesResolver } from "../../capabilities";
33
+ import { getSharedRegistry } from "../../plugins/registry";
32
34
  import { formatTimestamp } from "../helpers";
33
35
  import { buildTickerReport } from "../commands/ticker";
34
36
  import { createBaseConverter } from "../base-converter";
@@ -226,8 +228,10 @@ async function buildChartComposerReport(
226
228
  : series;
227
229
  })),
228
230
  };
231
+ const capabilityInvoker = getSharedRegistry();
229
232
  const result = await resolveChartSpecData(spec, {
230
233
  dataProvider: context.dataProvider,
234
+ ...(capabilityInvoker ? { resolveCapabilitySeries: createChartSeriesResolver(capabilityInvoker) } : {}),
231
235
  loadFredSeries: async (request) => ({
232
236
  data: await apiClient.getCloudFredSeries(request.seriesId, {
233
237
  startDate: request.startDate,
@@ -261,7 +265,9 @@ async function buildChartComposerReport(
261
265
  if (output?.points.length) return [];
262
266
  return [entry.source.kind === "security"
263
267
  ? publicTickerKey(entry.source.instrument.symbol, entry.source.instrument.exchange)
264
- : `FRED:${entry.source.seriesId}`];
268
+ : entry.source.kind === "economic"
269
+ ? `FRED:${entry.source.seriesId}`
270
+ : `CAP:${entry.source.capabilityId}:${entry.source.seriesId}`];
265
271
  });
266
272
  const rowCount = series.reduce((count, entry) => count + entry.observations.length, 0);
267
273
  const tableRows = series.map((entry) => {
@@ -45,9 +45,12 @@ async function buildPaneInstance(
45
45
  context: MarketContext,
46
46
  target: string,
47
47
  ): Promise<PaneInstanceConfig> {
48
+ // A text argument is a phrase, not a symbol: uppercasing it and binding it as
49
+ // a ticker makes the pane look for a provider for "HIGH GROWTH SOFTWARE".
50
+ const argKind = resolved.template?.shortcut?.argKind;
48
51
  const primarySymbol = resolved.createOptions?.symbol
49
52
  ?? resolved.createOptions?.symbols?.[0]
50
- ?? normalizeTickerInput(null, cleanTickerInput(target));
53
+ ?? (argKind === "text" ? null : normalizeTickerInput(null, cleanTickerInput(target)));
51
54
  const templateContext = buildTemplateContext(context, primarySymbol ?? null);
52
55
  const spec = await resolved.template?.createInstance?.(templateContext, resolved.createOptions) ?? {};
53
56
  const instanceId = `${resolved.pane.id}:cli-${slugifyName(target || resolved.pane.id, "pane")}`;
@@ -5,6 +5,7 @@ import {
5
5
  chartEvidenceMismatchesFor,
6
6
  missingActiveTabSelections,
7
7
  shotDataEvidenceFor,
8
+ shotExpectedText,
8
9
  shotPriceHistoryRange,
9
10
  shotSemanticRowCount,
10
11
  shotUnavailableSymbols,
@@ -130,6 +131,18 @@ describe("pane screenshot chart-data verification", () => {
130
131
  panelId: "macro",
131
132
  visible: true,
132
133
  },
134
+ {
135
+ id: "prediction",
136
+ sourceKind: "capability",
137
+ capabilityId: "prediction-markets.series",
138
+ providerSeriesId: "polymarket/event-1/market-1",
139
+ first: { date: "2026-01-01T00:00:00.000Z", value: 0.4 },
140
+ last: { date: "2026-01-02T00:00:00.000Z", value: 0.6 },
141
+ style: "area",
142
+ transform: "raw",
143
+ panelId: "prediction",
144
+ visible: true,
145
+ },
133
146
  ],
134
147
  };
135
148
  const metadata = {
@@ -137,12 +150,18 @@ describe("pane screenshot chart-data verification", () => {
137
150
  projectedPointCount: 42,
138
151
  baseSeries: expectedComposer.baseSeries?.map((series) => ({ ...series, pointCount: 12 })),
139
152
  };
140
- expect(chartEvidenceMismatchesFor([{
153
+ const semanticUi = [{
141
154
  id: "chart-composer-data",
142
- role: "chart-data",
155
+ role: "chart-data" as const,
143
156
  actions: [],
144
157
  metadata,
145
- }], expectedComposer)).toEqual([]);
158
+ }];
159
+ expect(chartEvidenceMismatchesFor(semanticUi, expectedComposer)).toEqual([]);
160
+
161
+ const wrong = structuredClone(semanticUi);
162
+ (wrong[0]!.metadata.baseSeries![3]!.last as { value: number }).value = 0.7;
163
+ expect(chartEvidenceMismatchesFor(wrong, expectedComposer))
164
+ .toContain("rendered chart series prediction does not match");
146
165
  });
147
166
  });
148
167
 
@@ -178,6 +197,40 @@ describe("pane screenshot chart-composer inputs", () => {
178
197
  expect(shotUnavailableSymbols(composer, payload([]), semanticUi)).toEqual([]);
179
198
  });
180
199
 
200
+ test("accepts capability-backed composer evidence and reports an empty provider series", () => {
201
+ const composer = resolved("chart-composer", {});
202
+ const populated: RemoteUiNodeSnapshot[] = [{
203
+ id: "chart-composer-data",
204
+ role: "chart-data",
205
+ actions: [],
206
+ metadata: {
207
+ kind: "chart-composer",
208
+ baseSeries: [{
209
+ sourceKind: "capability",
210
+ capabilityId: "prediction-markets.series",
211
+ providerSeriesId: "polymarket:one",
212
+ pointCount: 5,
213
+ }],
214
+ },
215
+ }];
216
+ expect(shotSemanticRowCount(composer, payload([]), populated)).toBe(5);
217
+ expect(shotUnavailableSymbols(composer, payload([]), populated)).toEqual([]);
218
+
219
+ const empty = structuredClone(populated);
220
+ (empty[0]!.metadata as any).baseSeries[0].pointCount = 0;
221
+ expect(shotUnavailableSymbols(composer, payload([]), empty))
222
+ .toEqual(["CAP:prediction-markets.series:polymarket:one"]);
223
+ });
224
+
225
+ test("expects the composer legend short label, not the catalog metric label", () => {
226
+ const valuation = {
227
+ ...resolved("valuation-series", { metric: "priceSales", period: "annual" }),
228
+ pane: { id: CHART_COMPOSER_PANE_ID },
229
+ } as unknown as ResolvedPaneFunction;
230
+
231
+ expect(shotExpectedText(valuation, ["AAPL"], payload([]))).toEqual(["AAPL", "P/S"]);
232
+ });
233
+
181
234
  test("reports an empty FRED composer source as unavailable", () => {
182
235
  const semanticUi: RemoteUiNodeSnapshot[] = [{
183
236
  id: "chart-composer-data",
@@ -20,6 +20,7 @@ import {
20
20
  limitGraphRowsBySymbol,
21
21
  metricDef,
22
22
  } from "../../time-series/reporting";
23
+ import { getTimeSeriesField } from "../../time-series/field-catalog";
23
24
  import {
24
25
  buildFinancialTableModel,
25
26
  formatFinancialHeader,
@@ -31,6 +32,7 @@ import type {
31
32
  } from "../../time-series/reporting";
32
33
  import type { TimeRange } from "../../time-series/range";
33
34
  import { appendLiveQuotePoint } from "../../time-series/chart-data";
35
+ import { applyResolvedSeriesTransform } from "../../time-series/transforms";
34
36
  import { subtractTimeRange } from "../../time-series/date-window";
35
37
  import {
36
38
  buildPresetDateWindow,
@@ -40,6 +42,9 @@ import { parseChartSpec } from "../../plugins/builtin/chart-composer/chart-spec"
40
42
  import { publicTickerKey } from "../../utils/exchanges";
41
43
  import { apiClient } from "../../api-client";
42
44
  import type { FredSeriesCacheEntry } from "../../data/fred-series";
45
+ import type { ResolvedSeries } from "../../time-series/types";
46
+ import { chartSeriesSourceKey, createChartSeriesResolver } from "../../capabilities";
47
+ import { getSharedRegistry } from "../../plugins/registry";
43
48
  import {
44
49
  collectShotSymbols,
45
50
  clipPriceHistoryToRange,
@@ -76,6 +81,23 @@ async function collectShotFredSeries(
76
81
  return loaded.filter((entry): entry is [string, FredSeriesCacheEntry] => !!entry);
77
82
  }
78
83
 
84
+ async function collectShotCapabilitySeries(
85
+ resolved: ResolvedPaneFunction,
86
+ ): Promise<Array<[string, ResolvedSeries]>> {
87
+ if (resolved.pane.id !== CHART_COMPOSER_PANE_ID) return [];
88
+ const spec = parseChartSpec(resolved.instance.settings?.chartSpec);
89
+ const invoker = getSharedRegistry();
90
+ if (!spec || !invoker) return [];
91
+ const resolveSeries = createChartSeriesResolver(invoker);
92
+ return (await Promise.all(spec.series.flatMap((series) => {
93
+ if (series.source.kind !== "capability") return [];
94
+ const source = series.source;
95
+ return [resolveSeries(source, spec.viewport, series)
96
+ .then((value) => [chartSeriesSourceKey(source), value] as [string, ResolvedSeries])
97
+ .catch(() => null)];
98
+ }))).filter((entry): entry is [string, ResolvedSeries] => entry !== null);
99
+ }
100
+
79
101
  export interface PaneScreenshotExpectedSelection {
80
102
  control: "metric" | "statement" | "period";
81
103
  value?: string;
@@ -106,10 +128,14 @@ export interface PaneScreenshotExpectedChartEvidence {
106
128
  sourceSeries?: PaneScreenshotChartSeriesEvidence[];
107
129
  baseSeries?: Array<{
108
130
  id: string;
109
- sourceKind: "security" | "economic";
131
+ sourceKind: "security" | "economic" | "capability";
110
132
  symbol?: string;
111
133
  fieldId?: string;
112
134
  economicSeriesId?: string;
135
+ capabilityId?: string;
136
+ providerSeriesId?: string;
137
+ first?: { date: string; value: number | null } | null;
138
+ last?: { date: string; value: number | null } | null;
113
139
  style: string;
114
140
  transform: string;
115
141
  panelId: string;
@@ -259,7 +285,10 @@ async function buildDesktopShotPayload(
259
285
  const tickers: TickerRecord[] = [];
260
286
  const financials: Array<[string, TickerFinancials]> = [];
261
287
  const optionsChains: Array<[string, OptionsChain]> = [];
262
- const fredSeries = await collectShotFredSeries(resolved);
288
+ const [fredSeries, capabilitySeries] = await Promise.all([
289
+ collectShotFredSeries(resolved),
290
+ collectShotCapabilitySeries(resolved),
291
+ ]);
263
292
  const includeOptionsChains = resolved.pane.id === OPTIONS_PANE_ID || resolved.template?.paneId === OPTIONS_PANE_ID;
264
293
  for (const symbol of collectShotSymbols(resolved, rawArg)) {
265
294
  const entry = await fetchTickerFinancials(context, symbol);
@@ -302,6 +331,7 @@ async function buildDesktopShotPayload(
302
331
  financials,
303
332
  optionsChains,
304
333
  fredSeries,
334
+ capabilitySeries,
305
335
  paneState,
306
336
  };
307
337
  }
@@ -612,6 +642,13 @@ function shotExpectedChart(
612
642
  if (resolved.pane.id === CHART_COMPOSER_PANE_ID) {
613
643
  const spec = parseChartSpec(resolved.instance.settings?.chartSpec);
614
644
  if (!spec) return null;
645
+ const capabilitySeries = new Map(payload.capabilitySeries);
646
+ const capabilityPoint = (point: ResolvedSeries["points"][number] | undefined) => point
647
+ ? {
648
+ date: point.date.toISOString(),
649
+ value: typeof (point.value ?? point.close) === "number" ? point.value ?? point.close ?? null : null,
650
+ }
651
+ : null;
615
652
  return {
616
653
  kind: "chart-composer",
617
654
  symbols: [...new Set(spec.series.flatMap((series) => (
@@ -629,7 +666,18 @@ function shotExpectedChart(
629
666
  symbol: publicTickerKey(series.source.instrument.symbol, series.source.instrument.exchange),
630
667
  fieldId: series.source.fieldId,
631
668
  }
632
- : { economicSeriesId: series.source.seriesId }),
669
+ : series.source.kind === "economic"
670
+ ? { economicSeriesId: series.source.seriesId }
671
+ : (() => {
672
+ const loaded = capabilitySeries.get(chartSeriesSourceKey(series.source));
673
+ const resolvedSeries = loaded ? applyResolvedSeriesTransform(loaded, series.transform) : undefined;
674
+ return {
675
+ capabilityId: series.source.capabilityId,
676
+ providerSeriesId: series.source.seriesId,
677
+ first: capabilityPoint(resolvedSeries?.points[0]),
678
+ last: capabilityPoint(resolvedSeries?.points.at(-1)),
679
+ };
680
+ })()),
633
681
  style: series.style,
634
682
  transform: series.transform,
635
683
  panelId: series.panelId,
@@ -780,6 +828,12 @@ export function chartEvidenceMismatchesFor(
780
828
  || actual.symbol !== series.symbol
781
829
  || actual.fieldId !== series.fieldId
782
830
  || actual.economicSeriesId !== series.economicSeriesId
831
+ || actual.capabilityId !== series.capabilityId
832
+ || actual.providerSeriesId !== series.providerSeriesId
833
+ || (series.sourceKind === "capability" && (
834
+ JSON.stringify(actual.first) !== JSON.stringify(series.first)
835
+ || JSON.stringify(actual.last) !== JSON.stringify(series.last)
836
+ ))
783
837
  || actual.style !== series.style
784
838
  || actual.transform !== series.transform
785
839
  || actual.panelId !== series.panelId
@@ -886,6 +940,9 @@ export function shotUnavailableSymbols(
886
940
  if (entry.sourceKind === "economic" && typeof entry.economicSeriesId === "string") {
887
941
  return [`FRED:${entry.economicSeriesId}`];
888
942
  }
943
+ if (entry.sourceKind === "capability" && typeof entry.capabilityId === "string" && typeof entry.providerSeriesId === "string") {
944
+ return [`CAP:${entry.capabilityId}:${entry.providerSeriesId}`];
945
+ }
889
946
  return [];
890
947
  });
891
948
  }
@@ -966,7 +1023,7 @@ export function shotSemanticRowCount(
966
1023
  return payload.financials.filter(([, financials]) => !!financials.quote).length;
967
1024
  }
968
1025
 
969
- function shotExpectedText(
1026
+ export function shotExpectedText(
970
1027
  resolved: ResolvedPaneFunction,
971
1028
  symbols: string[],
972
1029
  payload: DesktopPaneShotPayload,
@@ -978,8 +1035,14 @@ function shotExpectedText(
978
1035
  const definition = metricDef(graphKind, metric);
979
1036
  const period = resolved.options.period as FundamentalPeriod;
980
1037
  const periodCount = resolved.options.periods == null ? null : Number(resolved.options.periods);
1038
+ if (resolved.pane.id === CHART_COMPOSER_PANE_ID) {
1039
+ // Chart series are labelled with the field short label ("P/S"), not the
1040
+ // catalog label ("Price / Sales").
1041
+ const field = getTimeSeriesField(`${graphKind}.${metric}`);
1042
+ expected.push(field?.shortLabel ?? definition.label);
1043
+ return expected.filter(Boolean);
1044
+ }
981
1045
  expected.push(definition.label);
982
- if (resolved.pane.id === CHART_COMPOSER_PANE_ID) return expected.filter(Boolean);
983
1046
  for (const [symbol, financials] of payload.financials) {
984
1047
  const latestRow = limitGraphRowsBySymbol(
985
1048
  graphRowsForFinancials(financials, graphKind, metric, period, symbol),
@@ -25,6 +25,8 @@ import {
25
25
  type KeyEventLike,
26
26
  } from "../../../react/input";
27
27
  import { CompositeChart } from "./composite-chart";
28
+ import { createDefaultConfig } from "../../../types/config";
29
+ import { AppContext, createInitialState, PaneInstanceProvider } from "../../../state/app/context";
28
30
  import {
29
31
  resolveCompositeMinimumSpanMs,
30
32
  zoomCompositeViewport,
@@ -1517,6 +1519,58 @@ describe("CompositeChart", () => {
1517
1519
  expect(recoveredFrame).toContain("Jan 9");
1518
1520
  });
1519
1521
 
1522
+ test("restores persisted drawings from pane settings on mount", async () => {
1523
+ const renderWithSettings = async (settings: Record<string, unknown>) => {
1524
+ const config = createDefaultConfig("/tmp/gloomberb-composite-drawings");
1525
+ config.layout.instances.push({
1526
+ instanceId: "chart:test",
1527
+ paneId: "chart-composer",
1528
+ title: "Chart",
1529
+ binding: { kind: "fixed", symbol: "ACME" },
1530
+ settings,
1531
+ });
1532
+ testSetup = await testRender(
1533
+ <AppContext value={{ state: createInitialState(config), dispatch: () => {} }}>
1534
+ <PaneInstanceProvider paneId="chart:test">
1535
+ <CaptureChartSurfaceProvider>
1536
+ <CompositeChart
1537
+ width={60}
1538
+ height={12}
1539
+ series={[series("price", "main", "left", "USD", [100, 101, 102, 103, 104, 105, 106, 107, 108])]}
1540
+ panels={[{ id: "main" }]}
1541
+ />
1542
+ </CaptureChartSurfaceProvider>
1543
+ </PaneInstanceProvider>
1544
+ </AppContext>,
1545
+ { width: 62, height: 14 },
1546
+ );
1547
+ await act(async () => {
1548
+ await testSetup!.renderOnce();
1549
+ await testSetup!.renderOnce();
1550
+ });
1551
+ const bitmap = capturedSurfaceProps!.bitmaps?.[0] as { pixels: Uint8Array };
1552
+ const pixels = Buffer.from(bitmap.pixels);
1553
+ await act(async () => testSetup!.renderer.destroy());
1554
+ testSetup = undefined;
1555
+ return pixels;
1556
+ };
1557
+
1558
+ const blank = await renderWithSettings({});
1559
+ const restored = await renderWithSettings({
1560
+ chartDrawings: [{
1561
+ id: "drawing-1",
1562
+ panelId: "main",
1563
+ color: "#f5a524",
1564
+ points: [
1565
+ { time: Date.UTC(2025, 0, 2), value: 101 },
1566
+ { time: Date.UTC(2025, 0, 8), value: 107 },
1567
+ ],
1568
+ }],
1569
+ });
1570
+
1571
+ expect(restored.equals(blank)).toBe(false);
1572
+ });
1573
+
1520
1574
  test("shows useful UTC times for an intraday shared cursor and time axis", async () => {
1521
1575
  const intraday = {
1522
1576
  ...series("price", "main", "left", "USD", []),