gloomberb 0.11.0 → 0.11.2

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 (392) hide show
  1. package/README.md +38 -15
  2. package/README.zh-CN.md +12 -12
  3. package/package.json +10 -3
  4. package/src/api-client/auth.ts +14 -10
  5. package/src/api-client/data.ts +54 -0
  6. package/src/api-client/index.test.ts +124 -0
  7. package/src/api-client/index.ts +139 -4
  8. package/src/api-client/paths.ts +89 -0
  9. package/src/api-client/request.ts +15 -2
  10. package/src/api-client/socket-health.test.ts +1 -0
  11. package/src/api-client/socket.ts +2 -1
  12. package/src/api-client/types.ts +163 -0
  13. package/src/app/pane-runtime/index.tsx +4 -1
  14. package/src/app/pane-runtime/plugin-bindings.test.ts +117 -0
  15. package/src/app/pane-runtime/plugin-bindings.ts +25 -4
  16. package/src/app/pane-runtime/ticker-inspector-runtime.ts +25 -14
  17. package/src/app/runtime/desktop-deeplink.test.ts +16 -0
  18. package/src/app/runtime/desktop-deeplink.ts +66 -2
  19. package/src/app/runtime/desktop-menu.ts +3 -0
  20. package/src/app/runtime/plugin-bindings.ts +30 -28
  21. package/src/app/runtime/update.ts +8 -6
  22. package/src/app.tsx +21 -7
  23. package/src/architecture/import-boundaries.test.ts +5 -1
  24. package/src/capabilities/factories.ts +4 -0
  25. package/src/cli/commands/automation.test.ts +9 -0
  26. package/src/cli/commands/automation.ts +22 -73
  27. package/src/cli/index.ts +10 -6
  28. package/src/cli/registry.ts +1 -1
  29. package/src/components/chart/composite/composite-chart.test.tsx +45 -2
  30. package/src/components/chart/composite/composite-chart.tsx +16 -12
  31. package/src/components/chart/composite/format.test.ts +34 -0
  32. package/src/components/chart/composite/format.ts +33 -10
  33. package/src/components/chart/composite/scene.ts +0 -21
  34. package/src/components/chart/composite/tools.ts +49 -0
  35. package/src/components/chart/core/axis-format.ts +13 -3
  36. package/src/components/chart/core/index.test.ts +7 -0
  37. package/src/components/chart/core/renderer.ts +3 -4
  38. package/src/components/command-bar/commands/direct/index.ts +4 -0
  39. package/src/components/command-bar/commands/registry.ts +8 -1
  40. package/src/components/command-bar/layout-items/current-layout.ts +1 -1
  41. package/src/components/command-bar/pane-templates/workflow.ts +4 -0
  42. package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -0
  43. package/src/components/command-bar/routes/root/shortcuts.test.ts +19 -0
  44. package/src/components/command-bar/routes/root/shortcuts.ts +3 -1
  45. package/src/components/command-bar/surface/index.test.tsx +6 -6
  46. package/src/components/command-bar/surface/index.tsx +4 -1
  47. package/src/components/command-bar/surface/pane.test.tsx +66 -2
  48. package/src/components/command-bar/view-model.test.ts +2 -1
  49. package/src/components/command-bar/workflow/ops.test.ts +54 -0
  50. package/src/components/command-bar/workflow/ops.ts +1 -1
  51. package/src/components/company-logo.test.ts +19 -0
  52. package/src/components/company-logo.tsx +60 -0
  53. package/src/components/feed-data-table/stack-view.tsx +7 -1
  54. package/src/components/index.ts +1 -0
  55. package/src/components/layout/detached-pane-shell.tsx +85 -15
  56. package/src/components/layout/header.tsx +5 -5
  57. package/src/components/layout/pane/title.ts +31 -3
  58. package/src/components/layout/shell/active-drag.ts +4 -11
  59. package/src/components/layout/shell/drag/index.ts +4 -20
  60. package/src/components/layout/shell/drag/runtime.ts +3 -4
  61. package/src/components/layout/shell/index.test.tsx +17 -3
  62. package/src/components/layout/shell/index.tsx +89 -31
  63. package/src/components/layout/shell/menu.test.ts +37 -1
  64. package/src/components/layout/shell/menu.ts +14 -0
  65. package/src/components/layout/shell/native/pointer-runtime.ts +22 -8
  66. package/src/components/layout/shell/pane/actions.ts +0 -5
  67. package/src/components/layout/shell/pane/management-shortcuts.ts +9 -4
  68. package/src/components/layout/shell/shortcuts.ts +6 -3
  69. package/src/components/layout/shell/ticker-link-menu.test.ts +82 -0
  70. package/src/components/layout/shell/ticker-link-menu.ts +78 -0
  71. package/src/components/layout/status-bar.test.tsx +62 -61
  72. package/src/components/layout/status-bar.tsx +53 -75
  73. package/src/components/onboarding/account-step/form-panel.tsx +4 -2
  74. package/src/components/onboarding/account-step/index.tsx +1 -1
  75. package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
  76. package/src/components/onboarding/onboarding-wizard.test.tsx +44 -1
  77. package/src/components/onboarding/onboarding-wizard.tsx +13 -5
  78. package/src/components/onboarding/wizard-account.ts +4 -23
  79. package/src/components/table-view-shared.test.ts +11 -0
  80. package/src/components/table-view-shared.tsx +26 -0
  81. package/src/components/ui/data-table/index.tsx +19 -1
  82. package/src/components/ui/fields.tsx +5 -1
  83. package/src/core/state/app/layout-reducer.ts +36 -0
  84. package/src/core/state/app/layout.ts +10 -5
  85. package/src/core/state/app/state.ts +0 -13
  86. package/src/core/state/app/types.ts +6 -4
  87. package/src/data/config/chart-settings.ts +1 -1
  88. package/src/data/config/layout.ts +2 -0
  89. package/src/data/config/store/index.test.ts +27 -6
  90. package/src/data/config/store/normalize.ts +3 -5
  91. package/src/data/plugin-state-store.ts +11 -0
  92. package/src/data/sqlite/cache.test.ts +10 -0
  93. package/src/i18n/es.ts +13 -5
  94. package/src/i18n/ja.ts +13 -5
  95. package/src/i18n/ko.ts +13 -5
  96. package/src/i18n/zh-cn.ts +13 -5
  97. package/src/i18n/zh-tw.ts +13 -5
  98. package/src/layout-marketplace/api.test.ts +54 -0
  99. package/src/layout-marketplace/api.ts +65 -0
  100. package/src/layout-marketplace/gallery-desktop.test.tsx +262 -0
  101. package/src/layout-marketplace/gallery-desktop.tsx +375 -0
  102. package/src/layout-marketplace/gallery-terminal.tsx +376 -0
  103. package/src/layout-marketplace/gallery.test.tsx +84 -0
  104. package/src/layout-marketplace/gallery.tsx +323 -0
  105. package/src/layout-marketplace/geometry.test.ts +47 -0
  106. package/src/layout-marketplace/geometry.ts +80 -0
  107. package/src/layout-marketplace/mini-workspace.test.tsx +122 -0
  108. package/src/layout-marketplace/mini-workspace.tsx +428 -0
  109. package/src/layout-marketplace/model.test.ts +118 -0
  110. package/src/layout-marketplace/model.ts +171 -0
  111. package/src/layout-marketplace/name-dialog.tsx +60 -0
  112. package/src/layout-marketplace/pane-imagery.ts +85 -0
  113. package/src/layout-marketplace/pane.tsx +20 -0
  114. package/src/layout-marketplace/payload.test.ts +246 -0
  115. package/src/layout-marketplace/payload.ts +624 -0
  116. package/src/layout-marketplace/use-marketplace.ts +63 -0
  117. package/src/market-data/coordinator/index.test.ts +2 -2
  118. package/src/market-data/coordinator/index.ts +1 -1
  119. package/src/market-data/hooks.tsx +0 -41
  120. package/src/market-data/market/format.test.ts +7 -0
  121. package/src/market-data/market/freshness.ts +5 -2
  122. package/src/market-data/quotes/freshness.test.ts +16 -0
  123. package/src/news/aggregator.test.ts +92 -3
  124. package/src/news/aggregator.ts +98 -18
  125. package/src/news/hooks.test.tsx +2 -0
  126. package/src/news/hooks.ts +27 -1
  127. package/src/news/news-model.ts +5 -2
  128. package/src/news/types.ts +7 -0
  129. package/src/plugins/broker-sync/broker-sync.test.ts +58 -0
  130. package/src/plugins/broker-sync/normalize.ts +42 -5
  131. package/src/plugins/builtin/account-management/pane.tsx +5 -5
  132. package/src/plugins/builtin/ai/index.tsx +18 -2
  133. package/src/plugins/builtin/ai/screener/pane.test.tsx +47 -4
  134. package/src/plugins/builtin/ai/screener/pane.tsx +35 -12
  135. package/src/plugins/builtin/alerts/alert-engine.test.ts +53 -1
  136. package/src/plugins/builtin/alerts/alert-engine.ts +34 -1
  137. package/src/plugins/builtin/alerts/index.test.tsx +49 -6
  138. package/src/plugins/builtin/alerts/index.tsx +11 -6
  139. package/src/plugins/builtin/alerts/pane.tsx +51 -9
  140. package/src/plugins/builtin/alerts/quotes.ts +11 -3
  141. package/src/plugins/builtin/alerts/types.ts +1 -0
  142. package/src/plugins/builtin/analytics/index.tsx +11 -1
  143. package/src/plugins/builtin/analytics/pane-model.ts +6 -4
  144. package/src/plugins/builtin/broker-manager/index.tsx +3 -0
  145. package/src/plugins/builtin/buildout/model/index.ts +1 -1
  146. package/src/plugins/builtin/buildout/pane/index.tsx +17 -18
  147. package/src/plugins/builtin/cds/client.test.ts +83 -0
  148. package/src/plugins/builtin/cds/client.ts +67 -0
  149. package/src/plugins/builtin/cds/index.tsx +41 -0
  150. package/src/plugins/builtin/cds/model.test.ts +232 -0
  151. package/src/plugins/builtin/cds/model.ts +401 -0
  152. package/src/plugins/builtin/cds/pane.test.tsx +132 -0
  153. package/src/plugins/builtin/cds/pane.tsx +358 -0
  154. package/src/plugins/builtin/chart-composer/chart-spec.ts +31 -0
  155. package/src/plugins/builtin/chart-composer/index.tsx +88 -2
  156. package/src/plugins/builtin/chart-composer/pane-share.test.ts +71 -0
  157. package/src/plugins/builtin/chart-composer/pane.tsx +103 -15
  158. package/src/plugins/builtin/chat/content/index.tsx +4 -1
  159. package/src/plugins/builtin/chat/controller/channels.ts +7 -1
  160. package/src/plugins/builtin/chat/controller/index.ts +14 -9
  161. package/src/plugins/builtin/chat/controller/notifications.ts +13 -3
  162. package/src/plugins/builtin/chat/controller/persistence.ts +0 -3
  163. package/src/plugins/builtin/chat/controller/realtime.ts +3 -3
  164. package/src/plugins/builtin/chat/controller/send.ts +1 -3
  165. package/src/plugins/builtin/chat/controller/session-runtime.ts +18 -11
  166. package/src/plugins/builtin/chat/controller/state.ts +1 -3
  167. package/src/plugins/builtin/chat/controller/storage.ts +0 -2
  168. package/src/plugins/builtin/chat/controller/utils.ts +4 -6
  169. package/src/plugins/builtin/chat/controller/view.ts +2 -2
  170. package/src/plugins/builtin/chat/controller.test.ts +142 -14
  171. package/src/plugins/builtin/chat/message/desktop.test.tsx +88 -0
  172. package/src/plugins/builtin/chat/message/desktop.tsx +3 -1
  173. package/src/plugins/builtin/chat/message/profile-popover.test.tsx +1 -29
  174. package/src/plugins/builtin/chat/profile-popover.ts +53 -22
  175. package/src/plugins/builtin/chat/sidebar.test.tsx +81 -0
  176. package/src/plugins/builtin/chat/sidebar.tsx +63 -0
  177. package/src/plugins/builtin/chat/test-harness.tsx +29 -1
  178. package/src/plugins/builtin/cloud/auth-actions.tsx +14 -7
  179. package/src/plugins/builtin/cloud/auth-commands.ts +21 -51
  180. package/src/plugins/builtin/cloud/auth-dialog.tsx +296 -0
  181. package/src/{components/onboarding/account-step/model.test.ts → plugins/builtin/cloud/auth-model.test.ts} +1 -1
  182. package/src/{components/onboarding/account-step/model.ts → plugins/builtin/cloud/auth-model.ts} +37 -3
  183. package/src/plugins/builtin/cloud/browser.tsx +9 -0
  184. package/src/plugins/builtin/cloud/buildout-module.tsx +23 -0
  185. package/src/plugins/builtin/cloud/device-signin.ts +2 -8
  186. package/src/plugins/builtin/cloud/index.tsx +2 -0
  187. package/src/plugins/builtin/cloud/plugin.tsx +14 -24
  188. package/src/plugins/builtin/cloud/upgrade-command.ts +13 -6
  189. package/src/plugins/builtin/cloud-tweets/pane-share.test.ts +56 -0
  190. package/src/plugins/builtin/cloud-tweets/pane.tsx +20 -5
  191. package/src/plugins/builtin/cloud-tweets/registration.ts +50 -6
  192. package/src/plugins/builtin/composite-plugins.ts +3 -1
  193. package/src/plugins/builtin/congress-trades/detail.tsx +43 -1
  194. package/src/plugins/builtin/congress-trades/model.test.ts +85 -0
  195. package/src/plugins/builtin/congress-trades/model.ts +69 -1
  196. package/src/plugins/builtin/congress-trades/pane.tsx +51 -2
  197. package/src/plugins/builtin/connections/index.ts +3 -0
  198. package/src/plugins/builtin/dividend-yield/client.ts +18 -0
  199. package/src/plugins/builtin/dividend-yield/pane.tsx +7 -6
  200. package/src/plugins/builtin/earnings/index.tsx +5 -3
  201. package/src/plugins/builtin/earnings/model.test.ts +19 -1
  202. package/src/plugins/builtin/earnings/model.ts +17 -15
  203. package/src/plugins/builtin/econ/calendar-model.ts +0 -5
  204. package/src/plugins/builtin/econ/index.test.tsx +6 -1
  205. package/src/plugins/builtin/fear-greed/charts.tsx +12 -8
  206. package/src/plugins/builtin/help/index.tsx +2 -2
  207. package/src/plugins/builtin/holders/thirteenf-match.ts +1 -2
  208. package/src/plugins/builtin/insider/index.tsx +27 -11
  209. package/src/plugins/builtin/kelly-sizer/index.tsx +4 -1
  210. package/src/plugins/builtin/layout-manager/index.tsx +17 -4
  211. package/src/plugins/builtin/news/index.tsx +11 -6
  212. package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +2 -0
  213. package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +2 -0
  214. package/src/plugins/builtin/news/wire/breaking/pane.tsx +4 -1
  215. package/src/plugins/builtin/news/wire/feed-config.test.ts +15 -1
  216. package/src/plugins/builtin/news/wire/feed-config.ts +25 -5
  217. package/src/plugins/builtin/news/wire/index.tsx +3 -3
  218. package/src/plugins/builtin/news/wire/industry-pane.tsx +7 -2
  219. package/src/plugins/builtin/news/wire/news/footer.ts +34 -1
  220. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +4 -1
  221. package/src/plugins/builtin/news/wire/news/table.tsx +8 -2
  222. package/src/plugins/builtin/news/wire/rss/source.test.ts +12 -0
  223. package/src/plugins/builtin/news/wire/rss/source.ts +1 -1
  224. package/src/plugins/builtin/notes/files.ts +80 -0
  225. package/src/plugins/builtin/notes/index.tsx +3 -0
  226. package/src/plugins/builtin/notes/sync.test.ts +49 -0
  227. package/src/plugins/builtin/notes/sync.ts +98 -0
  228. package/src/plugins/builtin/options/index.tsx +1 -0
  229. package/src/plugins/builtin/options/view.test.tsx +25 -3
  230. package/src/plugins/builtin/options/view.tsx +1 -0
  231. package/src/plugins/builtin/portfolio-list/account-metrics.test.ts +22 -0
  232. package/src/plugins/builtin/portfolio-list/account-metrics.ts +12 -7
  233. package/src/plugins/builtin/portfolio-list/cli/render.ts +5 -3
  234. package/src/plugins/builtin/portfolio-list/column-values.ts +15 -4
  235. package/src/plugins/builtin/portfolio-list/header.tsx +13 -3
  236. package/src/plugins/builtin/portfolio-list/index.tsx +8 -0
  237. package/src/plugins/builtin/portfolio-list/metrics.test.ts +31 -1
  238. package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +1 -0
  239. package/src/plugins/builtin/portfolio-list/pane/index.tsx +10 -7
  240. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +6 -1
  241. package/src/plugins/builtin/portfolio-list/position-metrics.ts +25 -6
  242. package/src/plugins/builtin/portfolio-list/summary/index.tsx +35 -21
  243. package/src/plugins/builtin/portfolio-list/summary/totals.ts +20 -6
  244. package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -12
  245. package/src/plugins/builtin/research/equity-diagnostic-pane.test.tsx +329 -0
  246. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +582 -0
  247. package/src/plugins/builtin/research/index.test.ts +14 -0
  248. package/src/plugins/builtin/research/index.tsx +29 -0
  249. package/src/plugins/builtin/sec/index.tsx +24 -9
  250. package/src/plugins/builtin/sectors/index.tsx +14 -0
  251. package/src/plugins/builtin/shared/cloud-upgrade.ts +18 -11
  252. package/src/plugins/builtin/shared/public-pane.test.ts +12 -0
  253. package/src/plugins/builtin/shared/public-pane.ts +9 -0
  254. package/src/plugins/builtin/shared/public-share.ts +23 -0
  255. package/src/plugins/builtin/shared/ticker-source.ts +32 -0
  256. package/src/plugins/builtin/shared/ticker-surface.test.ts +27 -0
  257. package/src/plugins/builtin/shared/ticker-surface.ts +17 -0
  258. package/src/plugins/builtin/substack/api/cache.ts +7 -3
  259. package/src/plugins/builtin/substack/api/loaders.ts +36 -12
  260. package/src/plugins/builtin/substack/api/types.ts +2 -0
  261. package/src/plugins/builtin/substack/pane-state.ts +1 -1
  262. package/src/plugins/builtin/substack/pane.tsx +55 -12
  263. package/src/plugins/builtin/thirteenf/api.test.ts +1 -1
  264. package/src/plugins/builtin/thirteenf/api.ts +40 -18
  265. package/src/plugins/builtin/thirteenf/data.ts +15 -7
  266. package/src/plugins/builtin/thirteenf/pane.tsx +45 -10
  267. package/src/plugins/builtin/ticker-detail/index.tsx +10 -0
  268. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +12 -4
  269. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +8 -2
  270. package/src/plugins/builtin/ticker-detail/settings.ts +12 -0
  271. package/src/plugins/builtin/treasury-auctions/index.tsx +2 -0
  272. package/src/plugins/builtin/world-indices/index.tsx +2 -0
  273. package/src/plugins/catalog-browser.test.ts +78 -0
  274. package/src/plugins/catalog-browser.ts +136 -0
  275. package/src/plugins/gridlock-notification.ts +2 -1
  276. package/src/plugins/ibkr/flex/index.test.ts +19 -0
  277. package/src/plugins/ibkr/flex/index.ts +1 -1
  278. package/src/plugins/pane-manager/gridlock.ts +87 -3
  279. package/src/plugins/pane-manager.test.ts +30 -0
  280. package/src/plugins/pane-manager.ts +6 -1
  281. package/src/plugins/prediction-markets/chart.tsx +2 -2
  282. package/src/plugins/prediction-markets/controller/catalog.ts +77 -2
  283. package/src/plugins/prediction-markets/controller/data.ts +7 -1
  284. package/src/plugins/prediction-markets/controller/index.ts +9 -2
  285. package/src/plugins/prediction-markets/controller/keyboard.ts +10 -14
  286. package/src/plugins/prediction-markets/detail/overview.tsx +21 -0
  287. package/src/plugins/prediction-markets/detail/pane.tsx +43 -34
  288. package/src/plugins/prediction-markets/detail/trades.tsx +34 -9
  289. package/src/plugins/prediction-markets/detail.test.tsx +29 -2
  290. package/src/plugins/prediction-markets/index.tsx +8 -4
  291. package/src/plugins/prediction-markets/navigation.ts +0 -1
  292. package/src/plugins/prediction-markets/pane.test.tsx +77 -0
  293. package/src/plugins/prediction-markets/pane.tsx +12 -0
  294. package/src/plugins/prediction-markets/plugin.test.tsx +36 -0
  295. package/src/plugins/prediction-markets/services/kalshi/adapter.ts +49 -9
  296. package/src/plugins/prediction-markets/services/polymarket/adapter.ts +31 -0
  297. package/src/plugins/prediction-markets/test-helpers.tsx +9 -0
  298. package/src/plugins/registry/index.ts +2 -0
  299. package/src/plugins/ticker-navigation.test.ts +24 -0
  300. package/src/plugins/ticker-navigation.ts +40 -0
  301. package/src/remote/schema.ts +1 -1
  302. package/src/renderers/browser/app-services.ts +61 -0
  303. package/src/renderers/browser/cloud-transport.test.ts +68 -0
  304. package/src/renderers/browser/cloud-transport.ts +48 -0
  305. package/src/renderers/browser/config-host.ts +58 -0
  306. package/src/renderers/browser/deeplink-bridge.test.ts +41 -0
  307. package/src/renderers/browser/deeplink-bridge.ts +22 -0
  308. package/src/renderers/browser/error-boundary.tsx +23 -0
  309. package/src/renderers/browser/main.tsx +64 -0
  310. package/src/renderers/browser/persistence.ts +74 -0
  311. package/src/renderers/browser/storage.test.ts +66 -0
  312. package/src/renderers/browser/storage.ts +50 -0
  313. package/src/renderers/browser/ticker-repository.ts +59 -0
  314. package/src/renderers/browser/ui-host.tsx +40 -0
  315. package/src/renderers/cloudflare/worker.test.ts +136 -0
  316. package/src/renderers/cloudflare/worker.ts +116 -0
  317. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -2
  318. package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
  319. package/src/renderers/electrobun/bun/desktop/plugin-state.ts +7 -1
  320. package/src/renderers/electrobun/view/app-services.ts +14 -3
  321. package/src/renderers/electrobun/view/build-assets.ts +12 -10
  322. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  323. package/src/renderers/electrobun/view/data-table/index.tsx +10 -1
  324. package/src/renderers/electrobun/view/desktop/controls.tsx +2 -0
  325. package/src/renderers/electrobun/view/dom-ui-host.tsx +143 -0
  326. package/src/renderers/electrobun/view/host/box.test.tsx +33 -0
  327. package/src/renderers/electrobun/view/host/box.tsx +11 -6
  328. package/src/renderers/electrobun/view/host/chart-surface.tsx +7 -1
  329. package/src/renderers/electrobun/view/host/input.tsx +11 -7
  330. package/src/renderers/electrobun/view/host/native.ts +0 -5
  331. package/src/renderers/electrobun/view/main.tsx +2 -0
  332. package/src/renderers/electrobun/view/remote/persistence.ts +5 -0
  333. package/src/renderers/electrobun/view/styles.css +107 -7
  334. package/src/renderers/electrobun/view/toast-host.test.tsx +78 -0
  335. package/src/renderers/electrobun/view/toast-host.tsx +69 -43
  336. package/src/renderers/electrobun/view/ui-host.tsx +6 -137
  337. package/src/renderers/opentui/start.tsx +2 -0
  338. package/src/renderers/share/layout-view.test.tsx +58 -0
  339. package/src/renderers/share/layout-view.tsx +133 -0
  340. package/src/renderers/share/main.tsx +102 -0
  341. package/src/renderers/share/styles.css +54 -0
  342. package/src/renderers/share/view.test.tsx +100 -0
  343. package/src/renderers/share/view.tsx +150 -0
  344. package/src/shares/api.test.ts +105 -0
  345. package/src/shares/api.ts +127 -0
  346. package/src/shares/chart-snapshot.test.ts +34 -0
  347. package/src/shares/chart-snapshot.ts +28 -0
  348. package/src/shares/location.test.ts +19 -0
  349. package/src/shares/location.ts +11 -0
  350. package/src/shares/pane.test.ts +110 -0
  351. package/src/shares/pane.ts +54 -0
  352. package/src/shares/payload.ts +177 -0
  353. package/src/sources/gloomberb-cloud/index.test.ts +61 -1
  354. package/src/sources/gloomberb-cloud/index.ts +86 -14
  355. package/src/sources/gloomberb-cloud/normalizers.test.ts +34 -0
  356. package/src/sources/gloomberb-cloud/normalizers.ts +5 -5
  357. package/src/sources/provider-router/history.test.ts +2 -2
  358. package/src/sources/provider-router/news.test.ts +34 -0
  359. package/src/sources/provider-router/news.ts +15 -1
  360. package/src/sources/sec-edgar.test.ts +59 -0
  361. package/src/sources/sec-edgar.ts +63 -23
  362. package/src/sources/yahoo-finance/mappers.ts +3 -0
  363. package/src/sources/yahoo-finance/snapshots.ts +10 -2
  364. package/src/sources/yahoo-finance/symbols.test.ts +10 -0
  365. package/src/sources/yahoo-finance/symbols.ts +13 -8
  366. package/src/sources/yahoo-finance.test.ts +1 -1
  367. package/src/sources/yahoo-finance.ts +6 -2
  368. package/src/state/app/bootstrap.ts +6 -7
  369. package/src/state/app/context/index.test.ts +41 -1
  370. package/src/state/app/context/index.test.tsx +34 -16
  371. package/src/state/selectors-ui.ts +0 -8
  372. package/src/sync/baseline.ts +32 -0
  373. package/src/sync/controller.test.ts +124 -1
  374. package/src/sync/controller.ts +54 -12
  375. package/src/sync/core-contributors.test.ts +44 -0
  376. package/src/sync/core-contributors.ts +59 -10
  377. package/src/sync/react.ts +22 -2
  378. package/src/sync/types.ts +12 -0
  379. package/src/theme/font-scale.ts +0 -11
  380. package/src/tickers/search/index.test.ts +27 -0
  381. package/src/tickers/search/upsert.ts +10 -0
  382. package/src/types/capability-route-source.ts +2 -1
  383. package/src/types/config.ts +35 -5
  384. package/src/types/desktop-menu.ts +1 -0
  385. package/src/types/plugin.ts +36 -0
  386. package/src/ui/context-menu.tsx +1 -1
  387. package/src/ui/host.tsx +4 -0
  388. package/src/ui/toast.tsx +2 -0
  389. package/src/utils/back-navigation.ts +0 -4
  390. package/src/utils/price-history.ts +11 -10
  391. package/src/utils/sec.test.ts +6 -0
  392. package/src/utils/sec.ts +1 -1
@@ -13,6 +13,7 @@ import { VERSION } from "../../version";
13
13
  const UPDATE_CHECK_INTERVAL_MS = 60 * 60_000; // hourly
14
14
 
15
15
  export function useAppUpdateRuntime({
16
+ enabled = true,
16
17
  dispatch,
17
18
  isDetachedWindow,
18
19
  pluginRegistry,
@@ -21,6 +22,7 @@ export function useAppUpdateRuntime({
21
22
  updateCheckInProgress,
22
23
  updateProgress,
23
24
  }: {
25
+ enabled?: boolean;
24
26
  dispatch: Dispatch<AppAction>;
25
27
  isDetachedWindow: boolean;
26
28
  pluginRegistry: PluginRegistry;
@@ -76,15 +78,15 @@ export function useAppUpdateRuntime({
76
78
  }, [dispatch]);
77
79
 
78
80
  useEffect(() => {
79
- if (isDetachedWindow) return;
81
+ if (!enabled || isDetachedWindow) return;
80
82
  void runUpdateCheck(false);
81
83
  const interval = setInterval(() => { void runUpdateCheck(false); }, UPDATE_CHECK_INTERVAL_MS);
82
84
  return () => { clearInterval(interval); };
83
- }, [isDetachedWindow, runUpdateCheck]);
85
+ }, [enabled, isDetachedWindow, runUpdateCheck]);
84
86
 
85
87
  // First launch on a new version: show that release's notes, then remember the version.
86
88
  useEffect(() => {
87
- if (isDetachedWindow) return;
89
+ if (!enabled || isDetachedWindow) return;
88
90
  const config = stateRef.current.config;
89
91
  if (config.lastLaunchedVersion === VERSION) return;
90
92
  const isUpgrade = !!config.lastLaunchedVersion;
@@ -95,14 +97,14 @@ export function useAppUpdateRuntime({
95
97
  void pluginRegistry.createPaneFromTemplateAsyncFn("changelog-pane", {
96
98
  values: { version: VERSION },
97
99
  }).catch(() => {});
98
- }, [dispatch, isDetachedWindow, pluginRegistry, stateRef]);
100
+ }, [dispatch, enabled, isDetachedWindow, pluginRegistry, stateRef]);
99
101
 
100
102
  useEffect(() => {
101
- if (isDetachedWindow) return;
103
+ if (!enabled || isDetachedWindow) return;
102
104
  if (!updateAvailable || updateProgress || updateCheckInProgress) return;
103
105
  if (!canSelfUpdate(updateAvailable)) return;
104
106
  startUpdate(updateAvailable);
105
- }, [isDetachedWindow, startUpdate, updateAvailable, updateCheckInProgress, updateProgress]);
107
+ }, [enabled, isDetachedWindow, startUpdate, updateAvailable, updateCheckInProgress, updateProgress]);
106
108
 
107
109
  return {
108
110
  runUpdateCheck,
package/src/app.tsx CHANGED
@@ -24,7 +24,7 @@ import type { AppServicesFactory, AppTickerRepositoryPort } from "./core/app-ser
24
24
  import { useThemeColors } from "./theme/theme-context";
25
25
  import type { AppConfig } from "./types/config";
26
26
  import type { DesktopDeepLinkBridge } from "./types/desktop-deeplink";
27
- import type { CliLaunchRequest } from "./types/plugin";
27
+ import type { CliLaunchRequest, GloomPlugin } from "./types/plugin";
28
28
  import type { DataProvider } from "./types/data-provider";
29
29
  import type { DesktopDockPreviewState, DesktopSharedStateSnapshot, DesktopThemePreviewState, DesktopWindowBridge } from "./types/desktop-window";
30
30
  import type { DesktopApplicationMenuBridge } from "./types/desktop-menu";
@@ -32,7 +32,6 @@ import type { LayoutBounds } from "./plugins/pane-manager";
32
32
  import type { AppSessionSnapshot } from "./core/state/session-persistence";
33
33
  import type { MarketDataCoordinator } from "./market-data/coordinator";
34
34
  import { createAppNotifier } from "./notifications/app-notifier";
35
- import { getLoadablePlugins } from "./plugins/catalog";
36
35
  import { useBrokerImportRuntime } from "./app/runtime/broker-import";
37
36
  import { useDesktopDeepLinkRuntime } from "./app/runtime/desktop-deeplink";
38
37
  import { useDesktopApplicationMenuRuntime } from "./app/runtime/desktop-menu";
@@ -55,6 +54,7 @@ import { scheduleConfigSave } from "./state/config-save-scheduler";
55
54
  import { measurePerf } from "./utils/perf-marks";
56
55
  import { useAppLanguage } from "./i18n/react";
57
56
  import { AppLanguageConfigObserver } from "./app/language-observer";
57
+ import { isPaneShareHandoff } from "./shares/location";
58
58
 
59
59
  const EMPTY_EXTERNAL_PLUGINS: LoadedExternalPlugin[] = [];
60
60
 
@@ -68,6 +68,7 @@ interface AppInnerProps {
68
68
  desktopApplicationMenuBridge?: DesktopApplicationMenuBridge;
69
69
  desktopDeepLinkBridge?: DesktopDeepLinkBridge;
70
70
  remoteControlAdapter?: RemoteControlAdapter;
71
+ updatesEnabled?: boolean;
71
72
  onboardingActive?: boolean;
72
73
  onOnboardingComplete?: (config: AppConfig) => void | Promise<void>;
73
74
  }
@@ -100,6 +101,7 @@ function AppInner({
100
101
  desktopApplicationMenuBridge,
101
102
  desktopDeepLinkBridge,
102
103
  remoteControlAdapter,
104
+ updatesEnabled = true,
103
105
  onboardingActive = false,
104
106
  onOnboardingComplete,
105
107
  }: AppInnerProps) {
@@ -157,7 +159,9 @@ function AppInner({
157
159
  const type = notification.type ?? "info";
158
160
  let toastId: string | number | undefined;
159
161
  const options = {
160
- duration: notification.duration,
162
+ title: notification.title,
163
+ subtitle: notification.subtitle,
164
+ duration: notification.persistent ? 0 : notification.duration,
161
165
  action: notification.action
162
166
  ? {
163
167
  label: notification.action.label,
@@ -221,6 +225,7 @@ function AppInner({
221
225
  });
222
226
 
223
227
  const { runUpdateCheck, startUpdate } = useAppUpdateRuntime({
228
+ enabled: updatesEnabled,
224
229
  dispatch,
225
230
  isDetachedWindow,
226
231
  pluginRegistry,
@@ -244,6 +249,7 @@ function AppInner({
244
249
  desktopDeepLinkBridge,
245
250
  desktopWindowKind: desktopWindowBridge?.kind,
246
251
  dispatch,
252
+ initialized: state.initialized,
247
253
  pluginRegistry,
248
254
  stateRef,
249
255
  });
@@ -273,7 +279,7 @@ function AppInner({
273
279
  importBrokerPositions,
274
280
  marketData,
275
281
  pluginRegistry,
276
- state,
282
+ stateRef,
277
283
  tickerRepository,
278
284
  });
279
285
 
@@ -283,6 +289,7 @@ function AppInner({
283
289
  dispatch,
284
290
  tickerRepository,
285
291
  pluginRegistry,
292
+ appActive,
286
293
  // Keep a first-run workspace stable while the local guide is active. Once
287
294
  // onboarding finishes, the normal pull-before-push sync starts immediately.
288
295
  initialized: state.initialized
@@ -399,7 +406,7 @@ function AppInner({
399
406
  tickerRepository={tickerRepository}
400
407
  pluginRegistry={pluginRegistry}
401
408
  quitApp={() => rendererHost.requestExit()}
402
- onCheckForUpdates={() => runUpdateCheck(true)}
409
+ onCheckForUpdates={updatesEnabled ? () => runUpdateCheck(true) : undefined}
403
410
  onNativeOccluderChange={setCommandBarNativeOccluder}
404
411
  />
405
412
  )}
@@ -414,6 +421,7 @@ interface AppProps {
414
421
  config: AppConfig;
415
422
  servicesFactory: AppServicesFactory;
416
423
  externalPlugins?: LoadedExternalPlugin[];
424
+ plugins: readonly GloomPlugin[];
417
425
  cliLaunchRequest?: CliLaunchRequest | null;
418
426
  desktopWindowBridge?: DesktopWindowBridge;
419
427
  desktopApplicationMenuBridge?: DesktopApplicationMenuBridge;
@@ -421,12 +429,14 @@ interface AppProps {
421
429
  desktopSnapshot?: DesktopSharedStateSnapshot | null;
422
430
  desktopThemePreview?: DesktopThemePreviewState | null;
423
431
  remoteControlAdapter?: RemoteControlAdapter;
432
+ updatesEnabled?: boolean;
424
433
  }
425
434
 
426
435
  export function App({
427
436
  config: initialConfig,
428
437
  servicesFactory,
429
438
  externalPlugins: providedExternalPlugins,
439
+ plugins,
430
440
  cliLaunchRequest = null,
431
441
  desktopWindowBridge,
432
442
  desktopApplicationMenuBridge,
@@ -434,6 +444,7 @@ export function App({
434
444
  desktopSnapshot = null,
435
445
  desktopThemePreview = null,
436
446
  remoteControlAdapter,
447
+ updatesEnabled = true,
437
448
  }: AppProps) {
438
449
  useAppLanguage();
439
450
  const externalPlugins = providedExternalPlugins ?? EMPTY_EXTERNAL_PLUGINS;
@@ -458,8 +469,10 @@ export function App({
458
469
  const [config, setConfig] = useState(() => {
459
470
  return initialCliLaunch.config;
460
471
  });
472
+ const shareHandoff = isPaneShareHandoff();
461
473
  const [showOnboarding, setShowOnboarding] = useState(() => (
462
474
  desktopWindowBridge?.kind !== "detached"
475
+ && !shareHandoff
463
476
  && (!effectiveInitialConfig.onboardingComplete || !!effectiveInitialConfig.onboardingProgress)
464
477
  ));
465
478
 
@@ -469,14 +482,14 @@ export function App({
469
482
  return measurePerf("startup.app.create-services", () => (
470
483
  servicesFactory({
471
484
  config,
472
- plugins: getLoadablePlugins(externalPlugins),
485
+ plugins,
473
486
  })
474
487
  ), {
475
488
  externalPluginCount: externalPlugins.length,
476
489
  disabledPluginCount: config.disabledPlugins.length,
477
490
  brokerInstanceCount: config.brokerInstances.length,
478
491
  });
479
- }, [config.dataDir, externalPlugins, servicesFactory]);
492
+ }, [config.dataDir, externalPlugins, plugins, servicesFactory]);
480
493
 
481
494
  useEffect(() => {
482
495
  return () => services.destroy();
@@ -514,6 +527,7 @@ export function App({
514
527
  desktopApplicationMenuBridge={desktopApplicationMenuBridge}
515
528
  desktopDeepLinkBridge={desktopDeepLinkBridge}
516
529
  remoteControlAdapter={remoteControlAdapter}
530
+ updatesEnabled={updatesEnabled}
517
531
  onboardingActive={showOnboarding}
518
532
  onOnboardingComplete={(updatedConfig) => {
519
533
  setConfig(updatedConfig);
@@ -61,7 +61,11 @@ describe("import boundaries", () => {
61
61
  return !file.startsWith("src/renderers/electrobun/");
62
62
  }
63
63
  if (specifier === "react-dom" || specifier.startsWith("react-dom/")) {
64
- return !file.startsWith("src/renderers/electrobun/");
64
+ return ![
65
+ "src/renderers/electrobun/",
66
+ "src/renderers/browser/",
67
+ "src/renderers/share/",
68
+ ].some((prefix) => file.startsWith(prefix));
65
69
  }
66
70
  return false;
67
71
  });
@@ -147,6 +147,10 @@ export function newsProvider(options: {
147
147
  supports: op((input: any) => options.provider.supports?.(input.query) ?? true, "query"),
148
148
  getCachedNews: op((input: any) => options.provider.getCachedNews?.(input.query) ?? [], "query"),
149
149
  fetchNews: op((input: any) => options.provider.fetchNews(input.query), "query"),
150
+ fetchNewsPage: op((input: any) => (
151
+ options.provider.fetchNewsPage?.(input.query)
152
+ ?? options.provider.fetchNews(input.query).then((articles) => ({ articles, nextCursor: null }))
153
+ ), "query"),
150
154
  fetchNewsStory: op((input: any) => options.provider.fetchNewsStory?.(input.storyId) ?? null, "query"),
151
155
  },
152
156
  };
@@ -199,4 +199,13 @@ describe("headless Pi AI command", () => {
199
199
  expect(disconnected.runs).toEqual([]);
200
200
  expect(disconnected.closed()).toBe(1);
201
201
  });
202
+
203
+ test("rejects unsupported verbs instead of returning a stub payload", async () => {
204
+ const harness = createHarness({
205
+ catalog: { providers: [], refreshErrors: {} },
206
+ });
207
+ await expect(harness.command.execute(["screen"], harness.context))
208
+ .rejects.toThrow("Usage: gloomberb ai providers|ask");
209
+ expect(harness.output).toEqual([]);
210
+ });
202
211
  });
@@ -13,38 +13,26 @@ import type { CliCommandDef } from "../../types/plugin";
13
13
  import { withCliServices, withConfigData } from "../context";
14
14
  import { requireArg, takeOption } from "./command-utils";
15
15
 
16
- function deferredResult(command: string, code: "auth_required" | "not_implemented", message: string) {
17
- return {
18
- command,
19
- status: code,
20
- code,
21
- message,
22
- };
23
- }
24
-
25
16
  export const brokerCliCommand: CliCommandDef = {
26
17
  name: "broker",
27
18
  aliases: ["brokers"],
28
- description: "Inspect broker profiles and guarded broker operations",
29
- help: { usage: ["broker list", "broker status", "broker add|edit|remove|connect|sync"] },
19
+ description: "Inspect broker profiles",
20
+ help: { usage: ["broker list", "broker status"] },
30
21
  execute: async (args, ctx) => {
31
22
  const action = args[0] ?? "list";
23
+ if (action !== "list" && action !== "status") {
24
+ ctx.fail("Usage: gloomberb broker list|status");
25
+ }
32
26
  await withCliServices(ctx, async (services) => {
33
- if (action === "list" || action === "status") {
34
- const rows = services.config.brokerInstances.map((instance) => ({
27
+ ctx.printResult({
28
+ data: services.config.brokerInstances.map((instance) => ({
35
29
  id: instance.id,
36
30
  type: instance.brokerType,
37
31
  label: instance.label,
38
32
  enabled: instance.enabled !== false,
39
33
  connectionMode: instance.connectionMode ?? "",
40
34
  lastSyncedAt: instance.lastSyncedAt ? new Date(instance.lastSyncedAt).toISOString() : "",
41
- }));
42
- ctx.printResult({ data: rows });
43
- return;
44
- }
45
-
46
- ctx.printResult({
47
- data: deferredResult("broker", "not_implemented", "Broker mutation and connection commands need shared non-UI broker service extraction before they can safely run headless."),
35
+ })),
48
36
  });
49
37
  });
50
38
  },
@@ -52,36 +40,24 @@ export const brokerCliCommand: CliCommandDef = {
52
40
 
53
41
  export const ibkrCliCommand: CliCommandDef = {
54
42
  name: "ibkr",
55
- description: "Guarded IBKR account, order preview, and order action commands",
56
- help: { usage: ["ibkr accounts|positions|orders|preview|place|cancel"] },
43
+ description: "Inspect IBKR profiles",
44
+ help: { usage: ["ibkr accounts", "ibkr status"] },
57
45
  execute: async (args, ctx) => {
58
46
  const action = args[0] ?? "status";
59
- const profile = takeOption(args, "--profile");
60
- const account = takeOption(args, "--account");
61
- if ((action === "place" || action === "cancel") && (!profile || !account || !ctx.cliOptions.yes)) {
62
- ctx.fail(`ibkr ${action} requires --profile, --account, and --yes.`);
47
+ if (action !== "accounts" && action !== "status") {
48
+ ctx.fail("Usage: gloomberb ibkr accounts|status");
63
49
  }
64
50
  await withCliServices(ctx, async (services) => {
65
- const ibkrProfiles = services.config.brokerInstances.filter((instance) => instance.brokerType === "ibkr");
66
- if (action === "accounts" || action === "status") {
67
- ctx.printResult({ data: ibkrProfiles.map((instance) => ({
68
- id: instance.id,
69
- label: instance.label,
70
- enabled: instance.enabled !== false,
71
- connectionMode: instance.connectionMode ?? "",
72
- lastSyncedAt: instance.lastSyncedAt ? new Date(instance.lastSyncedAt).toISOString() : "",
73
- })) });
74
- return;
75
- }
76
51
  ctx.printResult({
77
- data: {
78
- ...deferredResult("ibkr", "not_implemented", "IBKR trading commands are registered with safety gates, but headless order/account service extraction is still required."),
79
- action,
80
- profile: profile ?? null,
81
- account: account ?? null,
82
- dryRun: ctx.cliOptions.dryRun,
83
- confirmed: ctx.cliOptions.yes,
84
- },
52
+ data: services.config.brokerInstances
53
+ .filter((instance) => instance.brokerType === "ibkr")
54
+ .map((instance) => ({
55
+ id: instance.id,
56
+ label: instance.label,
57
+ enabled: instance.enabled !== false,
58
+ connectionMode: instance.connectionMode ?? "",
59
+ lastSyncedAt: instance.lastSyncedAt ? new Date(instance.lastSyncedAt).toISOString() : "",
60
+ })),
85
61
  });
86
62
  });
87
63
  },
@@ -133,19 +109,12 @@ export function createAiCliCommand(options: CreateAiCliCommandOptions = {}): Cli
133
109
  usage: [
134
110
  "ai providers",
135
111
  "ai ask [--provider id] [--model id] <prompt>",
136
- "ai screen list|show|delete|refresh|export",
137
112
  ],
138
113
  },
139
114
  execute: async (args, ctx) => {
140
115
  const action = args[0] ?? "providers";
141
- if (action === "screen") {
142
- ctx.printResult({
143
- data: deferredResult("ai screen", "not_implemented", "AI screener tabs are still pane-state backed; expose them through a shared non-UI screener store before headless mutation."),
144
- });
145
- return;
146
- }
147
116
  if (action !== "providers" && action !== "ask") {
148
- ctx.fail("Usage: gloomberb ai providers|ask|screen");
117
+ ctx.fail("Usage: gloomberb ai providers|ask");
149
118
  }
150
119
 
151
120
  const rawArgs = args.slice(1);
@@ -258,23 +227,3 @@ export const rssCliCommand: CliCommandDef = {
258
227
  })) });
259
228
  },
260
229
  };
261
-
262
- function cloudCommand(name: string, description: string): CliCommandDef {
263
- return {
264
- name,
265
- description,
266
- execute: (_args, ctx) => {
267
- ctx.printResult({
268
- data: deferredResult(name, "auth_required", "This command needs an existing cloud/session token. New auth/account/chat CLI workflows are deferred in this pass."),
269
- });
270
- },
271
- };
272
- }
273
-
274
- export const cloudCliCommands: CliCommandDef[] = [
275
- cloudCommand("buildout", "Fetch Buildout company data through an existing cloud session"),
276
- cloudCommand("congress", "Fetch congressional trade data through an existing cloud session"),
277
- cloudCommand("substack", "Fetch Substack reader data through an existing session"),
278
- cloudCommand("x-feed", "Fetch X/Twitter feed data through an existing cloud session"),
279
- cloudCommand("tweets", "Alias for x-feed"),
280
- ];
package/src/cli/index.ts CHANGED
@@ -23,7 +23,6 @@ import { createSystemCliCommands } from "./commands/system";
23
23
  import {
24
24
  aiCliCommand,
25
25
  brokerCliCommand,
26
- cloudCliCommands,
27
26
  ibkrCliCommand,
28
27
  rssCliCommand,
29
28
  } from "./commands/automation";
@@ -35,7 +34,10 @@ import {
35
34
  } from "./commands/plugins";
36
35
  import { runPaneCatalog, runPaneFunction, runPaneScreenshot } from "./pane-functions";
37
36
 
38
- function createCoreCliCommands(renderHelp: () => string): CliCommandDef[] {
37
+ function createCoreCliCommands(
38
+ renderHelp: () => string,
39
+ allCommands: () => CliCommandDef[],
40
+ ): CliCommandDef[] {
39
41
  let commands: CliCommandDef[] = [];
40
42
  const launchUiRequest: CliLaunchRequest = {
41
43
  applyConfig: (config) => ({ config }),
@@ -53,7 +55,7 @@ function createCoreCliCommands(renderHelp: () => string): CliCommandDef[] {
53
55
  console.log(renderHelp());
54
56
  return;
55
57
  }
56
- ctx.printResult({ data: commands.map((command) => ({
58
+ ctx.printResult({ data: allCommands().map((command) => ({
57
59
  name: command.name,
58
60
  aliases: command.aliases ?? [],
59
61
  description: command.description,
@@ -190,12 +192,11 @@ function createCoreCliCommands(renderHelp: () => string): CliCommandDef[] {
190
192
  ...marketDataCliCommands,
191
193
  ...overviewCliCommands,
192
194
  remoteCliCommand,
193
- ...createSystemCliCommands(() => commands),
195
+ ...createSystemCliCommands(allCommands),
194
196
  brokerCliCommand,
195
197
  ibkrCliCommand,
196
198
  aiCliCommand,
197
199
  rssCliCommand,
198
- ...cloudCliCommands,
199
200
  ];
200
201
  return commands;
201
202
  }
@@ -207,7 +208,10 @@ export interface DispatchCliOptions {
207
208
  async function createRegistry(options: DispatchCliOptions = {}): Promise<CliCommandRegistry> {
208
209
  const config = await loadCliConfigIfAvailable();
209
210
  let registry: CliCommandRegistry | null = null;
210
- const coreCommands = createCoreCliCommands(() => renderCliHelp(registry!, VERSION));
211
+ const coreCommands = createCoreCliCommands(
212
+ () => renderCliHelp(registry!, VERSION),
213
+ () => registry!.commands.map((entry) => entry.command),
214
+ );
211
215
  registry = buildCliCommandRegistry({
212
216
  coreCommands,
213
217
  externalPlugins: options.externalPlugins ?? [],
@@ -208,7 +208,7 @@ export function buildCliCommandRegistry({
208
208
  commands,
209
209
  lookup,
210
210
  config,
211
- plugins: loadablePlugins,
211
+ plugins: loadablePlugins.filter((plugin) => !disabledPlugins.has(plugin.id)),
212
212
  externalPlugins,
213
213
  descriptors,
214
214
  };
@@ -31,6 +31,9 @@ import {
31
31
  resolveCompositeMinimumSpanMs,
32
32
  zoomCompositeViewport,
33
33
  } from "./interactions";
34
+ import { getThemeColors, syncTheme } from "../../../theme/colors";
35
+ import { ThemeProvider } from "../../../theme/theme-context";
36
+ import { DEFAULT_THEME } from "../../../theme/themes";
34
37
 
35
38
  let testSetup: Awaited<ReturnType<typeof testRender>> | undefined;
36
39
  let chartShortcut: ((event: KeyEventLike) => void) | null = null;
@@ -131,12 +134,12 @@ function keyEvent(name: string, shift = false): KeyEventLike {
131
134
  }
132
135
 
133
136
  afterEach(async () => {
134
- if (!testSetup) return;
135
- await act(async () => testSetup!.renderer.destroy());
137
+ if (testSetup) await act(async () => testSetup!.renderer.destroy());
136
138
  testSetup = undefined;
137
139
  chartShortcut = null;
138
140
  capturedSurfaceProps = null;
139
141
  capturedSurfaceNode = null;
142
+ syncTheme(DEFAULT_THEME);
140
143
  });
141
144
 
142
145
  function point(date: string, value: number): TimeSeriesPoint {
@@ -204,6 +207,46 @@ function twoSessionCandles(): ResolvedSeries {
204
207
  }
205
208
 
206
209
  describe("CompositeChart", () => {
210
+ test("repaints cached chart rasters when the theme changes", async () => {
211
+ let setThemeId: ((themeId: string) => void) | null = null;
212
+ const chartSeries = [series("price", "main", "left", "USD", [100, 101, 102])];
213
+
214
+ function ThemedChart() {
215
+ const [themeId, setTheme] = useState(DEFAULT_THEME);
216
+ setThemeId = setTheme;
217
+ return (
218
+ <ThemeProvider themeId={themeId}>
219
+ <CaptureChartSurfaceProvider>
220
+ <CompositeChart
221
+ width={60}
222
+ height={12}
223
+ series={chartSeries}
224
+ panels={[{ id: "main" }]}
225
+ />
226
+ </CaptureChartSurfaceProvider>
227
+ </ThemeProvider>
228
+ );
229
+ }
230
+
231
+ testSetup = await testRender(<ThemedChart />, { width: 62, height: 14 });
232
+ await act(async () => {
233
+ await testSetup!.renderOnce();
234
+ await testSetup!.renderOnce();
235
+ });
236
+
237
+ const nextThemeId = "github-light";
238
+ await act(async () => {
239
+ setThemeId?.(nextThemeId);
240
+ await testSetup!.renderOnce();
241
+ await testSetup!.renderOnce();
242
+ });
243
+
244
+ const bitmap = capturedSurfaceProps!.bitmaps[0] as { pixels: Uint8Array };
245
+ const expectedBackground = getThemeColors(nextThemeId).bg;
246
+ const expectedRgb = [1, 3, 5].map((offset) => parseInt(expectedBackground.slice(offset, offset + 2), 16));
247
+ expect(Array.from(bitmap.pixels.slice(0, 3))).toEqual(expectedRgb);
248
+ });
249
+
207
250
  test("shows the regular-session move beside the latest intraday value", async () => {
208
251
  testSetup = await testRender(
209
252
  <CompositeChart
@@ -14,6 +14,7 @@ import {
14
14
  import { useShortcut } from "../../../react/input";
15
15
  import { useOptionalPaneInstanceId, usePaneSettingValue } from "../../../state/app/context";
16
16
  import { colors as themeColors, hoverBg } from "../../../theme/colors";
17
+ import { useThemeColors } from "../../../theme/theme-context";
17
18
  import { formatPercentRaw } from "../../../utils/format";
18
19
  import { isPlainKey } from "../../../utils/keyboard";
19
20
  import { truncateWithEllipsis } from "../../../utils/text-wrap";
@@ -38,6 +39,7 @@ import {
38
39
  compositeAxisTicks,
39
40
  formatCompositeAxisValue,
40
41
  formatCompositeCursorDate,
42
+ formatCompositeCursorValue,
41
43
  formatCompositePointDetails,
42
44
  formatCompositeSeriesValue,
43
45
  formatCompositeTimeAxisDate,
@@ -61,6 +63,7 @@ import { renderCompositePanelBitmap } from "./rasterizer";
61
63
  import {
62
64
  buildChartToolVectors,
63
65
  CHART_DRAWING_COLORS,
66
+ CHART_DRAWINGS_SETTING_KEY,
64
67
  countMeasureBars,
65
68
  drawChartToolOverlay,
66
69
  resolveChartToolKind,
@@ -69,6 +72,7 @@ import {
69
72
  hitTestDrawings,
70
73
  isDrawingTool,
71
74
  nextDrawingColor,
75
+ parseChartDrawings,
72
76
  resolveDrawingFromDrag,
73
77
  resolveZoomBoxRange,
74
78
  shiftDrawing,
@@ -291,7 +295,7 @@ function cursorAxisLabel(
291
295
  const domain = panel.axes[side];
292
296
  if (!domain || cursorYRatio === null) return null;
293
297
  const value = unprojectCompositeValue(cursorYRatio, domain);
294
- return value === null ? null : formatCompositeAxisValue(value, domain);
298
+ return value === null ? null : formatCompositeCursorValue(value, domain);
295
299
  }
296
300
 
297
301
  const MINIMUM_AXIS_LABEL_WIDTH = 3;
@@ -707,10 +711,10 @@ function CompositePanelSurface({
707
711
  domain: measureDomain,
708
712
  }),
709
713
  startValueLabel: measureDomain && startValue !== null
710
- ? formatCompositeAxisValue(startValue, measureDomain)
714
+ ? formatCompositeCursorValue(startValue, measureDomain)
711
715
  : null,
712
716
  endValueLabel: measureDomain && endValue !== null
713
- ? formatCompositeAxisValue(endValue, measureDomain)
717
+ ? formatCompositeCursorValue(endValue, measureDomain)
714
718
  : null,
715
719
  };
716
720
  }, [measureDomain, scene, toolDrag]);
@@ -1370,7 +1374,6 @@ function CompositeLegend({
1370
1374
  );
1371
1375
  }
1372
1376
 
1373
- const CHART_DRAWINGS_SETTING_KEY = "chartDrawings";
1374
1377
  const NO_DRAWINGS: readonly ChartDrawing[] = [];
1375
1378
  /** Coalesces a drag into one write instead of one per pointer move. */
1376
1379
  const DRAWING_PERSIST_DELAY_MS = 400;
@@ -1396,7 +1399,7 @@ function ChartDrawingStore({
1396
1399
  );
1397
1400
  const restoredRef = useRef<readonly ChartDrawing[] | null>(null);
1398
1401
  if (restoredRef.current === null) {
1399
- restoredRef.current = Array.isArray(stored) ? stored : NO_DRAWINGS;
1402
+ restoredRef.current = parseChartDrawings(stored);
1400
1403
  }
1401
1404
 
1402
1405
  useEffect(() => {
@@ -1444,6 +1447,7 @@ export function CompositeChart({
1444
1447
  onToggleSeries,
1445
1448
  isSeriesToggleable,
1446
1449
  }: CompositeChartProps) {
1450
+ const activeThemeColors = useThemeColors();
1447
1451
  const { cellWidthPx = 8, pixelRatio = 1 } = useUiCapabilities();
1448
1452
  const isDesktopWeb = useUiHost().kind === "desktop-web";
1449
1453
  const showTextFallback = useShowChartTextFallback();
@@ -1691,13 +1695,13 @@ export function CompositeChart({
1691
1695
  }).join("|");
1692
1696
  const plotHeight = Math.max(panelCount, totalHeight - legendRows - timeAxisRows);
1693
1697
  const resolvedColors = useMemo<CompositeChartColors>(() => ({
1694
- background: colors?.background ?? themeColors.bg,
1695
- grid: colors?.grid ?? themeColors.border,
1696
- crosshair: colors?.crosshair ?? themeColors.borderFocused,
1697
- text: colors?.text ?? themeColors.text,
1698
- textDim: colors?.textDim ?? themeColors.textDim,
1699
- negative: colors?.negative ?? themeColors.negative,
1700
- }), [colors]);
1698
+ background: colors?.background ?? activeThemeColors.bg,
1699
+ grid: colors?.grid ?? activeThemeColors.border,
1700
+ crosshair: colors?.crosshair ?? activeThemeColors.borderFocused,
1701
+ text: colors?.text ?? activeThemeColors.text,
1702
+ textDim: colors?.textDim ?? activeThemeColors.textDim,
1703
+ negative: colors?.negative ?? activeThemeColors.negative,
1704
+ }), [activeThemeColors, colors]);
1701
1705
  const projectedScene = useMemo(() => {
1702
1706
  // lastTickKey busts this memo when a live tick mutates series identity in place.
1703
1707
  void lastTickKey;
@@ -1,8 +1,10 @@
1
1
  import { describe, expect, test } from "bun:test";
2
2
  import type { CompositeChartScene } from "./types";
3
3
  import {
4
+ formatChartLegendValue,
4
5
  formatCompositeAxisValue,
5
6
  formatCompositeCursorDate,
7
+ formatCompositeCursorValue,
6
8
  formatCompositePointDetails,
7
9
  formatCompositeSeriesValue,
8
10
  formatCompositeTimeAxisDate,
@@ -147,4 +149,36 @@ describe("composite chart unit formatting", () => {
147
149
  seriesIds: ["ratio"],
148
150
  })).toBe("0.500");
149
151
  });
152
+
153
+ test("shows full price precision in the legend and cursor, not compact axis ticks", () => {
154
+ const btc: ResolvedSeries = {
155
+ id: "btc",
156
+ label: "BTC-USD Price",
157
+ color: "#ffffff",
158
+ unit: "USD",
159
+ unitGroup: "price:USD",
160
+ nativeFrequency: "daily",
161
+ dataShape: "ohlcv",
162
+ style: "candles",
163
+ transform: "raw",
164
+ axis: "left",
165
+ panelId: "main",
166
+ interpolation: "none",
167
+ points: [],
168
+ };
169
+ const domain = {
170
+ side: "left" as const,
171
+ min: 70_000,
172
+ max: 80_000,
173
+ scale: "linear" as const,
174
+ unit: "USD",
175
+ unitGroup: "price:USD",
176
+ seriesIds: ["btc"],
177
+ };
178
+
179
+ expect(formatCompositeSeriesValue(79_432.18, btc)).toBe("$79,432.18");
180
+ expect(formatChartLegendValue(79_432.18, "USD", "price:USD")).toBe("$79,432.18");
181
+ expect(formatCompositeCursorValue(79_432.18, domain)).toBe("$79,432.18");
182
+ expect(formatCompositeAxisValue(79_432.18, domain)).toBe("$79K");
183
+ });
150
184
  });