gloomberb 0.16.2 → 0.16.4

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 (527) hide show
  1. package/package.json +4 -1
  2. package/src/api-client/askg.ts +43 -0
  3. package/src/api-client/auth.ts +15 -0
  4. package/src/api-client/beneficial-owners.ts +4 -0
  5. package/src/api-client/chat.ts +19 -0
  6. package/src/api-client/company-kpis.ts +14 -2
  7. package/src/api-client/data.ts +34 -16
  8. package/src/api-client/index.ts +12 -0
  9. package/src/api-client/normalizers.ts +27 -0
  10. package/src/api-client/paths.ts +88 -23
  11. package/src/api-client/request.ts +2 -1
  12. package/src/api-client/research-activity.ts +31 -0
  13. package/src/api-client/socket.ts +39 -8
  14. package/src/api-client/types.ts +57 -1
  15. package/src/api-client/upgrade-experiment.ts +90 -0
  16. package/src/app/global-shortcuts.ts +11 -1
  17. package/src/app/keybindings/actions.ts +10 -0
  18. package/src/app/pane-runtime/index.tsx +8 -0
  19. package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
  20. package/src/app/pane-runtime/plugin-bindings.ts +65 -25
  21. package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
  22. package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
  23. package/src/app/runtime/desktop-deeplink.ts +5 -0
  24. package/src/app/star-prompt/model.ts +98 -0
  25. package/src/app/star-prompt/runtime.ts +41 -0
  26. package/src/app/star-prompt/status-line.tsx +190 -0
  27. package/src/app.tsx +13 -7
  28. package/src/architecture/core-builtin-allowlist.json +70 -0
  29. package/src/brokers/account-cache.ts +45 -0
  30. package/src/brokers/signed-in/adapter.ts +33 -2
  31. package/src/brokers/signed-in/client.ts +10 -0
  32. package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
  33. package/src/brokers/signed-in/sign-in.ts +62 -5
  34. package/src/capabilities/registry.ts +8 -1
  35. package/src/capabilities/types.ts +2 -0
  36. package/src/cli/command-options.ts +74 -0
  37. package/src/cli/commands/command-utils.ts +29 -0
  38. package/src/cli/commands/crypto-hints.ts +12 -4
  39. package/src/cli/commands/market.ts +524 -148
  40. package/src/cli/commands/options-chain.ts +172 -0
  41. package/src/cli/commands/options-leaps.ts +422 -0
  42. package/src/cli/commands/overview.ts +223 -42
  43. package/src/cli/commands/search.ts +29 -8
  44. package/src/cli/commands/system.ts +99 -19
  45. package/src/cli/commands/themes.ts +64 -0
  46. package/src/cli/commands/ticker.ts +200 -73
  47. package/src/cli/desktop-pane-shot.ts +100 -16
  48. package/src/cli/errors.ts +25 -2
  49. package/src/cli/freshness.ts +7 -4
  50. package/src/cli/fx-pair.ts +57 -0
  51. package/src/cli/help.ts +40 -7
  52. package/src/cli/history-rows.ts +187 -15
  53. package/src/cli/index-roots.ts +51 -0
  54. package/src/cli/index.ts +21 -13
  55. package/src/cli/listing-arg.ts +211 -3
  56. package/src/cli/not-a-ticker.ts +37 -0
  57. package/src/cli/options.ts +55 -7
  58. package/src/cli/pane-functions/capabilities.ts +13 -2
  59. package/src/cli/pane-functions/catalog.ts +13 -2
  60. package/src/cli/pane-functions/data.ts +36 -5
  61. package/src/cli/pane-functions/dom.ts +89 -28
  62. package/src/cli/pane-functions/freshness.ts +205 -26
  63. package/src/cli/pane-functions/function-help.ts +71 -24
  64. package/src/cli/pane-functions/headless.ts +168 -35
  65. package/src/cli/pane-functions/index.ts +12 -2
  66. package/src/cli/pane-functions/market-session.ts +133 -0
  67. package/src/cli/pane-functions/options.ts +15 -4
  68. package/src/cli/pane-functions/report-notices.ts +63 -0
  69. package/src/cli/pane-functions/report.ts +12 -1
  70. package/src/cli/pane-functions/resolver.ts +32 -8
  71. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  72. package/src/cli/pane-functions/screenshot.ts +109 -23
  73. package/src/cli/registry.ts +8 -4
  74. package/src/cli/report-tables.ts +460 -0
  75. package/src/cli/result.ts +63 -16
  76. package/src/cli/row-window.ts +76 -0
  77. package/src/cli/scroll-truncation.ts +34 -0
  78. package/src/components/chart/composite/composite-chart.tsx +43 -4
  79. package/src/components/chart/composite/legend.tsx +39 -6
  80. package/src/components/chart/composite/rasterizer.ts +64 -4
  81. package/src/components/chart/composite/series-cues.ts +71 -0
  82. package/src/components/chart/composite/text-renderer.ts +16 -8
  83. package/src/components/chart/composite/types.ts +3 -0
  84. package/src/components/command-bar/commands/direct/index.ts +9 -1
  85. package/src/components/command-bar/commands/plugin/items.ts +3 -1
  86. package/src/components/command-bar/commands/registry.ts +7 -0
  87. package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
  88. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  89. package/src/components/command-bar/routes/root/command-items.ts +2 -0
  90. package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
  91. package/src/components/command-bar/routes/root/results.ts +40 -10
  92. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  93. package/src/components/command-bar/routes/root/search-providers.ts +97 -14
  94. package/src/components/command-bar/surface/index.tsx +18 -4
  95. package/src/components/command-bar/theme-picker.tsx +48 -13
  96. package/src/components/command-bar/workflow/ops.ts +5 -3
  97. package/src/components/data-table/export.ts +6 -51
  98. package/src/components/data-table/view.tsx +31 -10
  99. package/src/components/layout/floating-pane.tsx +6 -2
  100. package/src/components/layout/market-summary.ts +23 -9
  101. package/src/components/layout/pane/content.tsx +46 -29
  102. package/src/components/layout/pane/cycle-order.ts +2 -11
  103. package/src/components/layout/pane/header.tsx +2 -1
  104. package/src/components/layout/pane/title.ts +1 -1
  105. package/src/components/layout/presentation.ts +22 -0
  106. package/src/components/layout/shell/active-drag.ts +67 -35
  107. package/src/components/layout/shell/cursor-occlusion.ts +6 -5
  108. package/src/components/layout/shell/drag/index.ts +16 -7
  109. package/src/components/layout/shell/drag/live.ts +193 -0
  110. package/src/components/layout/shell/drag/overlays.tsx +179 -35
  111. package/src/components/layout/shell/drag/runtime.ts +62 -50
  112. package/src/components/layout/shell/drag/slide.ts +10 -0
  113. package/src/components/layout/shell/index.tsx +84 -57
  114. package/src/components/layout/shell/layout-state.ts +5 -11
  115. package/src/components/layout/shell/menu.ts +28 -1
  116. package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
  117. package/src/components/layout/shell/native/surfaces.ts +30 -14
  118. package/src/components/layout/shell/pane/actions.ts +2 -1
  119. package/src/components/layout/shell/pane/layers.tsx +461 -180
  120. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  121. package/src/components/layout/shell/pane/new-layout.ts +116 -0
  122. package/src/components/layout/shell/shortcuts.ts +5 -2
  123. package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
  124. package/src/components/layout/shell/visible-layout.ts +44 -1
  125. package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
  126. package/src/components/layout/status-bar.tsx +4 -1
  127. package/src/components/markdown-text.tsx +9 -5
  128. package/src/components/metric-treemap/heat-surface.tsx +27 -8
  129. package/src/components/metric-treemap/heat-tiles.ts +48 -17
  130. package/src/components/onboarding/onboarding-frame.tsx +33 -10
  131. package/src/components/onboarding/onboarding-wizard.tsx +49 -40
  132. package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
  133. package/src/components/onboarding/pro-step-layout.ts +86 -0
  134. package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
  135. package/src/components/ui/button.tsx +10 -0
  136. package/src/components/ui/data-table/index.tsx +38 -0
  137. package/src/components/ui/data-table/types.ts +8 -1
  138. package/src/components/ui/data-table/visible-range.ts +2 -0
  139. package/src/components/ui/icon.tsx +11 -2
  140. package/src/components/ui/index.ts +1 -1
  141. package/src/core/connection-health.ts +1 -1
  142. package/src/core/state/app/layout-reducer.ts +89 -3
  143. package/src/core/state/app/state.ts +8 -5
  144. package/src/core/state/app/types.ts +13 -2
  145. package/src/data/config/layout.ts +55 -21
  146. package/src/data/config/store/fold-panes.ts +149 -0
  147. package/src/data/config/store/migrations.ts +7 -0
  148. package/src/data/config/store/node.ts +27 -6
  149. package/src/data/config/store/normalize.ts +37 -3
  150. package/src/data/json-persistence.ts +17 -3
  151. package/src/data/json-storage.ts +2 -0
  152. package/src/data/memory-resource-store.ts +46 -0
  153. package/src/i18n/es.ts +14 -1
  154. package/src/i18n/ja.ts +14 -1
  155. package/src/i18n/ko.ts +14 -1
  156. package/src/i18n/zh-cn.ts +14 -1
  157. package/src/i18n/zh-tw.ts +14 -1
  158. package/src/layout/desks.ts +1 -2
  159. package/src/layout/pane-layout-move.ts +264 -0
  160. package/src/layout/pane-manager/hidden-panes.ts +160 -0
  161. package/src/layout/pane-manager/index.ts +1 -0
  162. package/src/market-data/coordinator/entries.ts +7 -1
  163. package/src/market-data/coordinator/quotes.ts +2 -2
  164. package/src/market-data/market/freshness.ts +156 -11
  165. package/src/market-data/market/status.ts +40 -0
  166. package/src/market-data/options-alternatives.ts +69 -0
  167. package/src/market-data/quotes/contributions.ts +19 -2
  168. package/src/market-data/quotes/freshness.ts +5 -0
  169. package/src/market-data/quotes/metadata.ts +16 -0
  170. package/src/market-data/quotes/screener-live-quotes.ts +56 -5
  171. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  172. package/src/plugins/builtin/account-management/model.ts +1 -0
  173. package/src/plugins/builtin/account-management/navigation.ts +1 -1
  174. package/src/plugins/builtin/account-management/pane.tsx +31 -4
  175. package/src/plugins/builtin/alerts/quotes.ts +3 -0
  176. package/src/plugins/builtin/analytics/pane-model.ts +10 -6
  177. package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
  178. package/src/plugins/builtin/analytics/risk-model.ts +37 -8
  179. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
  180. package/src/plugins/builtin/attention/pane.tsx +1 -1
  181. package/src/plugins/builtin/backtest/pane.tsx +1 -1
  182. package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
  183. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  184. package/src/plugins/builtin/broker-manager/model.ts +8 -0
  185. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
  186. package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
  187. package/src/plugins/builtin/broker-manager/table.ts +5 -0
  188. package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
  189. package/src/plugins/builtin/catalysts/detail.tsx +1 -1
  190. package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
  191. package/src/plugins/builtin/chat/channels.ts +34 -36
  192. package/src/plugins/builtin/chat/command-bar.ts +138 -0
  193. package/src/plugins/builtin/chat/composer-commands.ts +21 -1
  194. package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
  195. package/src/plugins/builtin/chat/content/footer.tsx +5 -3
  196. package/src/plugins/builtin/chat/content/index.tsx +21 -1
  197. package/src/plugins/builtin/chat/content/mentions.ts +2 -1
  198. package/src/plugins/builtin/chat/content/user-map.ts +3 -1
  199. package/src/plugins/builtin/chat/controller/index.ts +48 -0
  200. package/src/plugins/builtin/chat/conversations.ts +256 -0
  201. package/src/plugins/builtin/chat/discord-command.ts +91 -0
  202. package/src/plugins/builtin/chat/edit-window.ts +12 -6
  203. package/src/plugins/builtin/chat/ghost-user.ts +14 -0
  204. package/src/plugins/builtin/chat/index.tsx +40 -2
  205. package/src/plugins/builtin/chat/layout.ts +2 -0
  206. package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
  207. package/src/plugins/builtin/chat/message/header.tsx +24 -15
  208. package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
  209. package/src/plugins/builtin/chat/message/render-state.ts +3 -0
  210. package/src/plugins/builtin/chat/pane-requests.ts +60 -0
  211. package/src/plugins/builtin/chat/pane-state.ts +32 -2
  212. package/src/plugins/builtin/chat/pane.tsx +8 -0
  213. package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
  214. package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
  215. package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
  216. package/src/plugins/builtin/cloud/askg/model.ts +129 -70
  217. package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
  218. package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
  219. package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
  220. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
  221. package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
  222. package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
  223. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
  224. package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
  225. package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
  226. package/src/plugins/builtin/cloud/plugin.tsx +36 -1
  227. package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
  228. package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
  229. package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
  230. package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
  231. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
  232. package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
  233. package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
  234. package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
  235. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
  236. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
  237. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
  238. package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
  239. package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
  240. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
  241. package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
  242. package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
  243. package/src/plugins/builtin/comment-letters/client.ts +1 -3
  244. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  245. package/src/plugins/builtin/company-kpis/client.ts +23 -8
  246. package/src/plugins/builtin/company-kpis/headless.ts +3 -1
  247. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  248. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
  249. package/src/plugins/builtin/composite-plugins.ts +107 -64
  250. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
  251. package/src/plugins/builtin/credit-boards/index.tsx +21 -21
  252. package/src/plugins/builtin/credit-boards/model.ts +0 -1
  253. package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
  254. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
  255. package/src/plugins/builtin/debt-maturities/client.ts +37 -9
  256. package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
  257. package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
  258. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  259. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  260. package/src/plugins/builtin/earnings-calls/data.ts +28 -7
  261. package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
  262. package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
  263. package/src/plugins/builtin/econ/calendar-model.ts +42 -17
  264. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  265. package/src/plugins/builtin/econ/index.tsx +68 -22
  266. package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
  267. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  268. package/src/plugins/builtin/econ-statistics/view.ts +17 -0
  269. package/src/plugins/builtin/executives/data.ts +17 -6
  270. package/src/plugins/builtin/executives/pane.tsx +16 -7
  271. package/src/plugins/builtin/filing-events/pane.tsx +22 -5
  272. package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
  273. package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
  274. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
  275. package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
  276. package/src/plugins/builtin/gpu/headless.ts +9 -3
  277. package/src/plugins/builtin/gpu/model.ts +14 -5
  278. package/src/plugins/builtin/help/index.tsx +1 -0
  279. package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
  280. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  281. package/src/plugins/builtin/holders/client.ts +3 -6
  282. package/src/plugins/builtin/holders/format.ts +31 -1
  283. package/src/plugins/builtin/holders/headless.ts +32 -6
  284. package/src/plugins/builtin/holders/pane.tsx +25 -13
  285. package/src/plugins/builtin/holders/report-header.ts +46 -5
  286. package/src/plugins/builtin/insider/index.tsx +4 -4
  287. package/src/plugins/builtin/iv-history/charts.tsx +21 -7
  288. package/src/plugins/builtin/iv-history/client.ts +12 -2
  289. package/src/plugins/builtin/iv-history/format.ts +33 -2
  290. package/src/plugins/builtin/iv-history/headless.ts +22 -10
  291. package/src/plugins/builtin/iv-history/index.tsx +3 -3
  292. package/src/plugins/builtin/iv-history/model.ts +61 -13
  293. package/src/plugins/builtin/iv-history/pane.tsx +11 -3
  294. package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
  295. package/src/plugins/builtin/jobs/pane.tsx +1 -1
  296. package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
  297. package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
  298. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  299. package/src/plugins/builtin/kelly-sizer/pane.tsx +121 -45
  300. package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
  301. package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
  302. package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
  303. package/src/plugins/builtin/layout-manager/index.tsx +9 -1
  304. package/src/plugins/builtin/macro-day/client.ts +24 -0
  305. package/src/plugins/builtin/macro-day/headless.ts +11 -8
  306. package/src/plugins/builtin/macro-day/index.tsx +3 -0
  307. package/src/plugins/builtin/macro-day/model.ts +11 -6
  308. package/src/plugins/builtin/macro-day/pane.tsx +12 -8
  309. package/src/plugins/builtin/macro-day/release-table.ts +40 -0
  310. package/src/plugins/builtin/macro-day/releases.ts +62 -44
  311. package/src/plugins/builtin/market-heatmap/data.ts +25 -5
  312. package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
  313. package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
  314. package/src/plugins/builtin/market-heatmap/model.ts +94 -3
  315. package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
  316. package/src/plugins/builtin/market-movers/headless.ts +31 -4
  317. package/src/plugins/builtin/market-movers/index.tsx +8 -1
  318. package/src/plugins/builtin/market-movers/model.ts +71 -17
  319. package/src/plugins/builtin/market-movers/table.ts +50 -23
  320. package/src/plugins/builtin/members/pane.tsx +10 -0
  321. package/src/plugins/builtin/news/headless.ts +9 -2
  322. package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
  323. package/src/plugins/builtin/openfda/client.ts +1 -1
  324. package/src/plugins/builtin/options/carry.ts +95 -0
  325. package/src/plugins/builtin/options/index.tsx +58 -5
  326. package/src/plugins/builtin/options/strike-window.ts +183 -0
  327. package/src/plugins/builtin/options/table.ts +55 -3
  328. package/src/plugins/builtin/options/types.ts +8 -1
  329. package/src/plugins/builtin/options/view.tsx +161 -12
  330. package/src/plugins/builtin/options-positioning/headless.ts +4 -2
  331. package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
  332. package/src/plugins/builtin/options-scenario/client.ts +73 -17
  333. package/src/plugins/builtin/options-scenario/headless.ts +71 -24
  334. package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
  335. package/src/plugins/builtin/options-scenario/index.tsx +9 -2
  336. package/src/plugins/builtin/options-scenario/model.ts +33 -0
  337. package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
  338. package/src/plugins/builtin/pe-band/model.ts +2 -2
  339. package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
  340. package/src/plugins/builtin/perps/equity-row.tsx +8 -5
  341. package/src/plugins/builtin/perps/model.ts +16 -5
  342. package/src/plugins/builtin/perps/pane.tsx +1 -1
  343. package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
  344. package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
  345. package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
  346. package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
  347. package/src/plugins/builtin/plugin-module.ts +87 -4
  348. package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
  349. package/src/plugins/builtin/portfolio-list/allocation.ts +3 -0
  350. package/src/plugins/builtin/portfolio-list/cached-account.ts +18 -9
  351. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
  352. package/src/plugins/builtin/portfolio-list/cli/render.ts +89 -18
  353. package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
  354. package/src/plugins/builtin/portfolio-list/headless.ts +40 -7
  355. package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -4
  356. package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -4
  357. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
  358. package/src/plugins/builtin/realized-vol/headless.ts +3 -0
  359. package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
  360. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
  361. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
  362. package/src/plugins/builtin/research/relative-valuation-model.ts +7 -3
  363. package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
  364. package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
  365. package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
  366. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
  367. package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
  368. package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
  369. package/src/plugins/builtin/risk-factors/data.ts +23 -9
  370. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  371. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  372. package/src/plugins/builtin/seasonality/pane.tsx +1 -1
  373. package/src/plugins/builtin/sec/index.tsx +5 -5
  374. package/src/plugins/builtin/sectors/client.ts +10 -2
  375. package/src/plugins/builtin/sectors/headless.ts +35 -6
  376. package/src/plugins/builtin/sectors/index.tsx +18 -1
  377. package/src/plugins/builtin/sectors/sector-model.ts +150 -41
  378. package/src/plugins/builtin/shared/command-tab.ts +51 -0
  379. package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
  380. package/src/plugins/builtin/shared/members-menu.tsx +260 -0
  381. package/src/plugins/builtin/shared/report-freshness.ts +50 -2
  382. package/src/plugins/builtin/shared/ticker-request.ts +25 -1
  383. package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
  384. package/src/plugins/builtin/shared/volatility/options.ts +1 -46
  385. package/src/plugins/builtin/short-interest/index.tsx +19 -4
  386. package/src/plugins/builtin/short-interest/surface.tsx +4 -4
  387. package/src/plugins/builtin/short-volume/index.tsx +1 -10
  388. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  389. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  390. package/src/plugins/builtin/supply-chain/model.ts +1 -1
  391. package/src/plugins/builtin/themes/pane.tsx +3 -0
  392. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
  393. package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
  394. package/src/plugins/builtin/ticker-detail/headless.ts +63 -25
  395. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -4
  396. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +3 -2
  397. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
  398. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
  399. package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
  400. package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
  401. package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
  402. package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
  403. package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
  404. package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
  405. package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
  406. package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
  407. package/src/plugins/builtin/vol-surface/client.ts +6 -2
  408. package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
  409. package/src/plugins/builtin/vol-surface/headless.ts +124 -20
  410. package/src/plugins/builtin/vol-surface/model.ts +23 -11
  411. package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
  412. package/src/plugins/builtin/vol-surface/tables.ts +124 -0
  413. package/src/plugins/builtin/world-indices/client.ts +22 -4
  414. package/src/plugins/builtin/world-indices/headless.ts +54 -27
  415. package/src/plugins/builtin/world-indices/model.ts +4 -2
  416. package/src/plugins/builtin/world-indices/table.tsx +13 -2
  417. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +2 -0
  418. package/src/plugins/builtin/world-venue-map/basemap.ts +18 -0
  419. package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
  420. package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
  421. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +2 -1
  422. package/src/plugins/builtin/world-venue-map/index.ts +6 -6
  423. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +27 -14
  424. package/src/plugins/builtin/world-venue-map/layers.ts +68 -10
  425. package/src/plugins/builtin/world-venue-map/map.tsx +198 -62
  426. package/src/plugins/builtin/world-venue-map/model.ts +16 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +3 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
  429. package/src/plugins/builtin/world-venue-map/pane.tsx +16 -10
  430. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  431. package/src/plugins/builtin-editorial.ts +126 -0
  432. package/src/plugins/catalog-backend.ts +16 -9
  433. package/src/plugins/catalog-browser.ts +127 -63
  434. package/src/plugins/catalog-manifest.ts +73 -40
  435. package/src/plugins/catalog-ui.ts +26 -6
  436. package/src/plugins/extracted-plugins.ts +7 -1
  437. package/src/plugins/ownership.ts +150 -7
  438. package/src/plugins/plugin-functions.ts +54 -0
  439. package/src/plugins/plugin-off.ts +36 -0
  440. package/src/plugins/registry/host-actions.ts +16 -0
  441. package/src/plugins/registry/index.ts +84 -24
  442. package/src/plugins/registry/plugin-state.ts +25 -1
  443. package/src/plugins/runtime/context.tsx +29 -0
  444. package/src/react/input.ts +48 -1
  445. package/src/remote/app-host.tsx +5 -0
  446. package/src/remote/controller.ts +3 -53
  447. package/src/remote/matching.ts +69 -0
  448. package/src/remote/schema.ts +1 -1
  449. package/src/remote/semantic-tree.tsx +14 -0
  450. package/src/remote/types.ts +2 -0
  451. package/src/renderers/browser/app-services.ts +3 -1
  452. package/src/renderers/dom/data-table/dom.ts +31 -0
  453. package/src/renderers/dom/data-table/index.tsx +5 -0
  454. package/src/renderers/dom/desktop/icons.tsx +14 -0
  455. package/src/renderers/dom/desktop/query-bar.tsx +59 -32
  456. package/src/renderers/dom/host/box.tsx +113 -17
  457. package/src/renderers/dom/host/drag-shield.ts +35 -0
  458. package/src/renderers/dom/host/live-frame.ts +62 -0
  459. package/src/renderers/dom/host/mouse.ts +8 -1
  460. package/src/renderers/dom/host/scroll-box.tsx +6 -5
  461. package/src/renderers/dom/host/tabs.tsx +3 -2
  462. package/src/renderers/dom/input-host.tsx +6 -1
  463. package/src/renderers/dom/styles.css +26 -5
  464. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
  465. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  466. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
  467. package/src/renderers/electrobun/bun/desktop/initialization.ts +15 -1
  468. package/src/renderers/electrobun/shared/protocol.ts +12 -1
  469. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -1
  470. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
  471. package/src/renderers/electrobun/view/host/native.ts +0 -10
  472. package/src/renderers/electrobun/view/host/window-move.ts +28 -0
  473. package/src/renderers/electrobun/view/main.tsx +4 -1
  474. package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
  475. package/src/renderers/electrobun/view/ui-host.tsx +2 -5
  476. package/src/renderers/opentui/input-host.tsx +6 -1
  477. package/src/renderers/opentui/start.tsx +9 -0
  478. package/src/renderers/opentui/ui-host.tsx +7 -1
  479. package/src/shares/portable-layout.ts +16 -6
  480. package/src/sources/gloomberb-cloud/index.ts +19 -10
  481. package/src/sources/gloomberb-cloud/normalizers.ts +20 -21
  482. package/src/sources/listing-symbols.ts +11 -5
  483. package/src/sources/provider-errors.ts +88 -3
  484. package/src/sources/provider-router/batches.ts +3 -3
  485. package/src/sources/provider-router/cached-routes.ts +8 -4
  486. package/src/sources/provider-router/financial-routes.ts +13 -7
  487. package/src/sources/provider-router/financials.ts +106 -16
  488. package/src/sources/provider-router/history.ts +27 -11
  489. package/src/sources/provider-router/index.ts +1 -1
  490. package/src/sources/provider-router/primary.ts +24 -6
  491. package/src/sources/sec-edgar.ts +68 -8
  492. package/src/state/pane-scroll-registry.tsx +67 -35
  493. package/src/sync/core-contributors.ts +4 -3
  494. package/src/test-support/cli-context.ts +2 -1
  495. package/src/test-support/data-provider.ts +34 -0
  496. package/src/test-support/margin-account.ts +108 -0
  497. package/src/test-support/shell-pane-keys.tsx +1 -0
  498. package/src/theme/series-colors.ts +46 -0
  499. package/src/theme/themes.ts +149 -0
  500. package/src/tickers/search/index.ts +22 -2
  501. package/src/tickers/search/ranking.ts +45 -1
  502. package/src/tickers/search/types.ts +3 -1
  503. package/src/time-series/resolution.ts +15 -0
  504. package/src/time-series/resolve.ts +1 -14
  505. package/src/types/config.ts +61 -1
  506. package/src/types/financials.ts +32 -3
  507. package/src/types/headless.ts +11 -0
  508. package/src/types/instrument.ts +8 -0
  509. package/src/types/plugin.ts +49 -3
  510. package/src/ui/host.tsx +16 -0
  511. package/src/ui/index.tsx +10 -3
  512. package/src/ui/scroll-box-axis.ts +26 -0
  513. package/src/utils/cli-output.ts +14 -1
  514. package/src/utils/currency-units.ts +8 -3
  515. package/src/utils/depositary-receipt.ts +54 -10
  516. package/src/utils/display-number.ts +63 -0
  517. package/src/utils/exchanges.ts +13 -0
  518. package/src/utils/format.ts +2 -1
  519. package/src/utils/fundamentals.ts +17 -1
  520. package/src/utils/fx-market-hours.ts +34 -0
  521. package/src/utils/option-expiry.ts +126 -0
  522. package/src/utils/price-history-integrity.ts +19 -6
  523. package/src/utils/price-history.ts +39 -6
  524. package/src/utils/rpc-timeout-error.ts +6 -2
  525. package/src/utils/sec-eps-basis.ts +159 -30
  526. package/src/utils/sec.ts +38 -34
  527. package/src/utils/throttled-fetch.ts +51 -22
@@ -30,6 +30,7 @@ import type {
30
30
  } from "./types";
31
31
  import type { RemoteUiRegistry } from "./semantic-tree";
32
32
  import { commandBarResultsFromNodes, isCommandBarInputNode } from "./command-bar";
33
+ import { findCommandBarResult, findMatchingUiNode } from "./matching";
33
34
  import { REMOTE_AGENT_HELP, remoteControlSchema } from "./schema";
34
35
  import {
35
36
  fail,
@@ -48,7 +49,6 @@ import {
48
49
  } from "./layout-helpers";
49
50
  import { createRemoteResources } from "./resources";
50
51
  import { dismissTopmostDialog, isDialogOpen as isAnyDialogOpen } from "../ui/dialog-stack";
51
- import { findFormNode } from "./form";
52
52
  import { asRecord } from "../utils/guards";
53
53
  import { runAutomated } from "../telemetry/usage-counts";
54
54
 
@@ -247,20 +247,7 @@ export function createAppRemoteController({
247
247
  };
248
248
 
249
249
  const activateCommandBarResult = async (input: Record<string, unknown>): Promise<unknown> => {
250
- const nodeId = optionalString(input, "nodeId");
251
- const index = optionalNumber(input, "index");
252
- const itemId = optionalString(input, "itemId");
253
- const label = optionalString(input, "label");
254
- const results = commandBarResultsFromNodes(uiRegistry?.snapshot() ?? []);
255
- const result = nodeId
256
- ? results.find((entry) => entry.nodeId === nodeId)
257
- : itemId
258
- ? results.find((entry) => entry.itemId === itemId)
259
- : label
260
- ? results.find((entry) => entry.label === label)
261
- : typeof index === "number"
262
- ? results.find((entry) => entry.index === index)
263
- : results.find((entry) => entry.selected) ?? results[0];
250
+ const result = findCommandBarResult(commandBarResultsFromNodes(uiRegistry?.snapshot() ?? []), input);
264
251
  if (!result) throw new Error("No matching command-bar result is visible.");
265
252
  if (!result.actions.includes("activate")) {
266
253
  throw new Error(`Command-bar result "${result.nodeId}" does not expose activate.`);
@@ -270,45 +257,8 @@ export function createAppRemoteController({
270
257
  };
271
258
 
272
259
  const invokeMatchingUiNode = async (input: Record<string, unknown>): Promise<unknown> => {
273
- const role = optionalString(input, "role");
274
- const label = optionalString(input, "label");
275
- const contains = optionalString(input, "contains");
276
- const index = optionalNumber(input, "index");
277
260
  const action = optionalString(input, "action") ?? "press";
278
- const metadataFilter = asRecord(input.metadata);
279
- const nodes = uiRegistry?.snapshot() ?? [];
280
- // A dialog over the form (a listing picker, a sign-in) keeps its controls
281
- // out of reach, as it does from the mouse.
282
- const formCovered = findFormNode(nodes)?.metadata?.covered === true;
283
- const candidates = nodes.filter((node) => {
284
- if (formCovered && node.metadata?.scope === "form") return false;
285
- if (role && node.role !== role) return false;
286
- if (label && node.label !== label && node.metadata?.item && typeof node.metadata.item === "object") {
287
- const item = node.metadata.item as Record<string, unknown>;
288
- if (item.label !== label && item.id !== label) return false;
289
- } else if (label && node.label !== label) {
290
- return false;
291
- }
292
- if (contains) {
293
- const haystack = [
294
- node.label,
295
- node.role,
296
- JSON.stringify(node.metadata ?? {}),
297
- ].filter((entry): entry is string => typeof entry === "string").join(" ").toLowerCase();
298
- if (!haystack.includes(contains.toLowerCase())) return false;
299
- }
300
- for (const [key, value] of Object.entries(metadataFilter)) {
301
- if (node.metadata?.[key] !== value) return false;
302
- }
303
- if (!node.actions.includes(action)) return false;
304
- if (node.disabled) return false;
305
- return true;
306
- });
307
- // An open form or confirm covers every pane, so its own controls come
308
- // first: its Cancel, not a pane's Cancel behind it.
309
- const inForm = candidates.filter((node) => node.metadata?.scope === "form");
310
- const matches = inForm.length > 0 ? inForm : candidates;
311
- const node = typeof index === "number" ? matches[index] : matches[0];
261
+ const node = findMatchingUiNode(uiRegistry?.snapshot() ?? [], input);
312
262
  if (!node) throw new Error("No matching semantic UI node is visible.");
313
263
  const result = await uiRegistry?.invoke(node.id, action, input.input);
314
264
  return getAfterMutationSummary({ invokedNode: node, result });
@@ -0,0 +1,69 @@
1
+ import { optionalNumber, optionalString } from "./controller-utils";
2
+ import { findFormNode } from "./form";
3
+ import type { RemoteUiNodeSnapshot } from "./types";
4
+ import { asRecord } from "../utils/guards";
5
+
6
+ /** The visible command-bar row commandBar.activateResult picks for this input. */
7
+ export function findCommandBarResult<T extends { nodeId: string; itemId?: unknown; label?: unknown; index?: unknown; selected: boolean }>(
8
+ results: readonly T[],
9
+ input: Record<string, unknown>,
10
+ ): T | undefined {
11
+ const nodeId = optionalString(input, "nodeId");
12
+ const index = optionalNumber(input, "index");
13
+ const itemId = optionalString(input, "itemId");
14
+ const label = optionalString(input, "label");
15
+ return nodeId
16
+ ? results.find((entry) => entry.nodeId === nodeId)
17
+ : itemId
18
+ ? results.find((entry) => entry.itemId === itemId)
19
+ : label
20
+ ? results.find((entry) => entry.label === label)
21
+ : typeof index === "number"
22
+ ? results.find((entry) => entry.index === index)
23
+ : results.find((entry) => entry.selected) ?? results[0];
24
+ }
25
+
26
+ /** The semantic UI node ui.invokeMatching acts on for this input, or undefined. */
27
+ export function findMatchingUiNode(
28
+ nodes: RemoteUiNodeSnapshot[],
29
+ input: Record<string, unknown>,
30
+ ): RemoteUiNodeSnapshot | undefined {
31
+ const role = optionalString(input, "role");
32
+ const label = optionalString(input, "label");
33
+ const contains = optionalString(input, "contains");
34
+ const index = optionalNumber(input, "index");
35
+ const action = optionalString(input, "action") ?? "press";
36
+ const metadataFilter = asRecord(input.metadata);
37
+ // A dialog over the form (a listing picker, a sign-in) keeps its controls
38
+ // out of reach, as it does from the mouse.
39
+ const formCovered = findFormNode(nodes)?.metadata?.covered === true;
40
+ const candidates = nodes.filter((node) => {
41
+ if (formCovered && node.metadata?.scope === "form") return false;
42
+ if (role && node.role !== role) return false;
43
+ if (label && node.label !== label && node.metadata?.item && typeof node.metadata.item === "object") {
44
+ const item = node.metadata.item as Record<string, unknown>;
45
+ if (item.label !== label && item.id !== label) return false;
46
+ } else if (label && node.label !== label) {
47
+ return false;
48
+ }
49
+ if (contains) {
50
+ const haystack = [
51
+ node.label,
52
+ node.role,
53
+ JSON.stringify(node.metadata ?? {}),
54
+ ].filter((entry): entry is string => typeof entry === "string").join(" ").toLowerCase();
55
+ if (!haystack.includes(contains.toLowerCase())) return false;
56
+ }
57
+ for (const [key, value] of Object.entries(metadataFilter)) {
58
+ if (node.metadata?.[key] !== value) return false;
59
+ }
60
+ if (!node.actions.includes(action)) return false;
61
+ if (node.disabled) return false;
62
+ return true;
63
+ });
64
+ // An open form or confirm covers every pane, so its own controls come
65
+ // first: its Cancel, not a pane's Cancel behind it.
66
+ const inForm = candidates.filter((node) => node.metadata?.scope === "form");
67
+ const matches = inForm.length > 0 ? inForm : candidates;
68
+ return typeof index === "number" ? matches[index] : matches[0];
69
+ }
@@ -115,7 +115,7 @@ export const REMOTE_OPERATIONS: RemoteOperationSchema[] = [
115
115
  op("pane.close", "Close a pane instance or pane type.", "{ paneId: string }", "local-write", paneIdInput),
116
116
  op(
117
117
  "pane.createFromTemplate",
118
- "Create a pane from a registered template.",
118
+ "Open a pane from a registered template. It only opens the pane: it adds nothing to a portfolio or watchlist.",
119
119
  "{ templateId: string, options?: object }",
120
120
  "local-write",
121
121
  objectSchema({ templateId: requiredStringSchema, options: openObjectSchema }, ["templateId"]),
@@ -11,6 +11,8 @@ type RemoteUiAction = (input?: unknown) => unknown | Promise<unknown>;
11
11
 
12
12
  export interface RemoteUiNodeRegistration {
13
13
  role: string;
14
+ /** The pane instance the node is drawn in, set from RemoteUiPaneScope. */
15
+ paneId?: string;
14
16
  label?: string;
15
17
  disabled?: boolean;
16
18
  actions?: Record<string, RemoteUiAction | undefined>;
@@ -56,6 +58,7 @@ export function createRemoteUiRegistry(): RemoteUiRegistry {
56
58
  .map(([action]) => action)
57
59
  .sort(),
58
60
  metadata: registration.getMetadata?.() ?? registration.metadata,
61
+ ...(registration.paneId ? { paneId: registration.paneId } : {}),
59
62
  };
60
63
  });
61
64
  },
@@ -110,6 +113,13 @@ export function useRemoteUiScope(): string | null {
110
113
  return useContext(RemoteUiScopeContext);
111
114
  }
112
115
 
116
+ const RemoteUiPaneContext = createContext<string | null>(null);
117
+
118
+ /** Tags the nodes drawn below with the pane instance they belong to. */
119
+ export function RemoteUiPaneScope({ paneId, children }: { paneId: string; children: ReactNode }) {
120
+ return <RemoteUiPaneContext value={paneId}>{children}</RemoteUiPaneContext>;
121
+ }
122
+
113
123
  let nextRemoteNodeSequence = 0;
114
124
 
115
125
  /**
@@ -120,9 +130,12 @@ let nextRemoteNodeSequence = 0;
120
130
  */
121
131
  export function useRemoteUiNode(registration: RemoteUiNodeRegistration | null | undefined): string | null {
122
132
  const registry = useRemoteUiRegistry();
133
+ const paneId = useContext(RemoteUiPaneContext);
123
134
  const nodeIdRef = useRef<string | null>(null);
124
135
  const registrationRef = useRef(registration);
125
136
  registrationRef.current = registration;
137
+ const paneIdRef = useRef(paneId);
138
+ paneIdRef.current = paneId;
126
139
  const dynamicRegistrationRef = useRef<RemoteUiNodeRegistration | null>(null);
127
140
  const registered = registration != null;
128
141
  if (registered && !nodeIdRef.current) {
@@ -130,6 +143,7 @@ export function useRemoteUiNode(registration: RemoteUiNodeRegistration | null |
130
143
  nodeIdRef.current = `ui:${nextRemoteNodeSequence.toString(36)}`;
131
144
  dynamicRegistrationRef.current = {
132
145
  get role() { return registrationRef.current?.role ?? "unknown"; },
146
+ get paneId() { return registrationRef.current?.paneId ?? paneIdRef.current ?? undefined; },
133
147
  get label() { return registrationRef.current?.label; },
134
148
  get disabled() { return registrationRef.current?.disabled; },
135
149
  get actions() { return registrationRef.current?.actions; },
@@ -172,6 +172,8 @@ export interface RemoteControlSchema {
172
172
  export interface RemoteUiNodeSnapshot {
173
173
  id: string;
174
174
  role: string;
175
+ /** The pane instance the node is drawn in; absent outside panes. */
176
+ paneId?: string;
175
177
  label?: string;
176
178
  disabled?: boolean;
177
179
  actions: string[];
@@ -5,6 +5,7 @@ import { createAppRuntime } from "../../core/app-runtime";
5
5
  import { createGloomberbCloudCapabilities, createGloomberbCloudProvider } from "../../sources/gloomberb-cloud";
6
6
  import { AssetDataRouter } from "../../sources/provider-router";
7
7
  import { JsonPersistence } from "../../data/json-persistence";
8
+ import { isBrokerAccountSnapshotKey } from "../../brokers/account-cache";
8
9
  import { JsonTickerRepository } from "../../data/json-ticker-repository";
9
10
 
10
11
  export function createBrowserAppServices({ config, plugins, externalPlugins }: AppServicesFactoryOptions) {
@@ -15,7 +16,8 @@ export function createBrowserAppServices({ config, plugins, externalPlugins }: A
15
16
  );
16
17
  return createAppRuntime({
17
18
  config, plugins, externalPlugins, dataProvider,
18
- persistence: new JsonPersistence(localStorage),
19
+ // Broker account snapshots are kept with the positions, so a reload keeps the account's equity and cash.
20
+ persistence: new JsonPersistence(localStorage, { keepResource: isBrokerAccountSnapshotKey }),
19
21
  tickerRepository: new JsonTickerRepository(localStorage),
20
22
  configure({ pluginRegistry, newsService }) {
21
23
  dataProvider.attachRegistry(pluginRegistry);
@@ -143,6 +143,24 @@ export function useScrollBoxHandle(
143
143
  get scrollWidth() {
144
144
  return toCellX(elementRef.current?.scrollWidth ?? 0);
145
145
  },
146
+ // Whole rows are rounded, so the scroll keys read the ends in pixels.
147
+ get scrollTopPx() {
148
+ return options.headerOnly ? 0 : Math.max(0, elementRef.current?.scrollTop ?? 0);
149
+ },
150
+ get scrollHeightPx() {
151
+ const element = elementRef.current;
152
+ if (!element) return 0;
153
+ return options.headerOnly ? tableHeaderPx() : Math.max(0, element.scrollHeight - (options.viewportTopInsetPx ?? 0));
154
+ },
155
+ get viewportPx() {
156
+ const element = elementRef.current;
157
+ return {
158
+ width: Math.max(0, element?.clientWidth ?? 0),
159
+ height: options.headerOnly
160
+ ? tableHeaderPx()
161
+ : Math.max(0, (element?.clientHeight ?? 0) - (options.viewportTopInsetPx ?? 0)),
162
+ };
163
+ },
146
164
  get viewport() {
147
165
  const element = elementRef.current;
148
166
  if (options.headerOnly) {
@@ -161,6 +179,19 @@ export function useScrollBoxHandle(
161
179
  },
162
180
  horizontalScrollBar,
163
181
  verticalScrollBar,
182
+ scrollToPixels(target: number | { x?: number; y?: number }, y?: number) {
183
+ const element = elementRef.current;
184
+ if (!element || options.headerOnly) return;
185
+ if (typeof target === "number") {
186
+ element.scrollTop = Math.max(0, target);
187
+ if (typeof y === "number") element.scrollLeft = Math.max(0, y);
188
+ return;
189
+ }
190
+ element.scrollTo({
191
+ left: Math.max(0, target.x ?? element.scrollLeft),
192
+ top: Math.max(0, target.y ?? element.scrollTop),
193
+ });
194
+ },
164
195
  scrollTo(target: number | { x?: number; y?: number }, y?: number) {
165
196
  const element = elementRef.current;
166
197
  if (!element) return;
@@ -11,6 +11,7 @@ import {
11
11
  } from "react";
12
12
  import { EmptyState } from "../../../components/ui/status";
13
13
  import { usePaneInstance } from "../../../state/app/context";
14
+ import { useRegisterPaneScrollBox } from "../../../state/pane-scroll-registry";
14
15
  import { useRafCallback } from "../../../react/use-raf-callback";
15
16
  import { measurePerf } from "../../../utils/perf-marks";
16
17
  import type {
@@ -284,6 +285,10 @@ export function WebDataTable<T, C extends DataTableColumn = DataTableColumn>({
284
285
  bodyVertical.bar,
285
286
  { viewportTopInsetPx: headerPx },
286
287
  );
288
+ // The terminal table body is a ScrollBox the pane scroll keys already reach;
289
+ // this is the same for the desktop one. A table with a cursor takes the keys
290
+ // first, so only a table without one is scrolled by them.
291
+ useRegisterPaneScrollBox(scrollRef, { enabled: true });
287
292
 
288
293
  useEffect(() => {
289
294
  headerHorizontal.bar.visible = false;
@@ -88,6 +88,20 @@ const ICONS: Record<IconName, ReactNode> = {
88
88
  <path d="M2 9.1 4.7 6.6l1.6 1.5 1.8-2 2.4 2.5" stroke="currentColor" strokeWidth="1.1" strokeLinecap="round" strokeLinejoin="round" />
89
89
  </>
90
90
  ),
91
+ // A hand with the thumb raised, on the 24-unit grid; the thumbs down is the
92
+ // same hand turned over.
93
+ "thumbs-up": (
94
+ <g transform="scale(0.5)">
95
+ <rect x="3" y="10" width="4" height="10" rx="1" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round" />
96
+ <path d="M7 10l3.5-6.5a1.8 1.8 0 0 1 3.3 1.2L13 9.5h5.6a2 2 0 0 1 2 2.4l-1.4 6.5a2 2 0 0 1-2 1.6H7" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
97
+ </g>
98
+ ),
99
+ "thumbs-down": (
100
+ <g transform="scale(0.5) rotate(180 12 12)">
101
+ <rect x="3" y="10" width="4" height="10" rx="1" stroke="currentColor" strokeWidth="1.8" strokeLinejoin="round" />
102
+ <path d="M7 10l3.5-6.5a1.8 1.8 0 0 1 3.3 1.2L13 9.5h5.6a2 2 0 0 1 2 2.4l-1.4 6.5a2 2 0 0 1-2 1.6H7" stroke="currentColor" strokeWidth="1.8" strokeLinecap="round" strokeLinejoin="round" />
103
+ </g>
104
+ ),
91
105
  restore: (
92
106
  <>
93
107
  <rect x="2" y="4" width="6" height="6" rx="0.8" stroke="currentColor" strokeWidth="1.1" />
@@ -10,6 +10,53 @@ import { WebIcon } from "./icons";
10
10
 
11
11
  /** Input width of a text filter that names none, in cells (the terminal's default). */
12
12
  const DEFAULT_TEXT_FIELD_CELLS = 20;
13
+ /** Beyond this many choices an inline filter is a strip that scrolls by itself, as the terminal's does. */
14
+ const STRIP_OPTION_LIMIT = 6;
15
+
16
+ function isStrip(item: HostQueryBarItem): boolean {
17
+ return item.kind === "select" && item.inline === true && item.options.length > STRIP_OPTION_LIMIT;
18
+ }
19
+
20
+ /**
21
+ * The choices of an inline filter. A long one (expiries, chart ranges) is a
22
+ * strip that scrolls on its own, so the label and the controls beside it stay
23
+ * in view while it brings the chosen one into view.
24
+ */
25
+ function InlineChoices({ item }: { item: HostQueryBarItem }) {
26
+ const stripRef = useRef<HTMLDivElement | null>(null);
27
+ const strip = isStrip(item);
28
+ const overflow = useHorizontalOverflow(stripRef, [item.options.length, strip]);
29
+ return (
30
+ <div className="gloom-qb-inline" data-item-id={item.id} data-narrowing={item.narrowing ? "true" : undefined}
31
+ data-strip={strip ? "true" : undefined}>
32
+ <span className="gloom-qb-label">{item.label}</span>
33
+ <div
34
+ ref={stripRef}
35
+ className="gloom-qb-view gloom-qb-segments"
36
+ role="radiogroup"
37
+ aria-label={item.label}
38
+ style={strip ? overflow.maskStyle : undefined}
39
+ onWheel={strip ? overflow.onWheel : undefined}
40
+ >
41
+ {item.options.map((option) => (
42
+ <button
43
+ key={option.value}
44
+ type="button"
45
+ role="radio"
46
+ aria-checked={option.selected}
47
+ disabled={option.disabled}
48
+ title={option.hint ? `${option.label} (${option.hint})` : undefined}
49
+ data-active={option.selected ? "true" : undefined}
50
+ onMouseDown={(event) => event.stopPropagation()}
51
+ onClick={() => item.onSelect(option.value)}
52
+ >
53
+ <span className="gloom-qb-text">{option.label}</span>
54
+ </button>
55
+ ))}
56
+ </div>
57
+ </div>
58
+ );
59
+ }
13
60
 
14
61
  function Chevron() {
15
62
  return <span className="gloom-qb-chevron"><WebIcon name="chevron-down" size={9} /></span>;
@@ -87,30 +134,7 @@ function FilterChip({ item, open, onOpenChange }: { item: HostQueryBarItem; open
87
134
  useEffect(() => {
88
135
  if (open) triggerRef.current?.scrollIntoView({ block: "nearest", inline: "nearest" });
89
136
  }, [open]);
90
- if (item.kind === "select" && item.inline) {
91
- return (
92
- <div className="gloom-qb-inline" data-item-id={item.id} data-narrowing={item.narrowing ? "true" : undefined}>
93
- <span className="gloom-qb-label">{item.label}</span>
94
- <div className="gloom-qb-view gloom-qb-segments" role="radiogroup" aria-label={item.label}>
95
- {item.options.map((option) => (
96
- <button
97
- key={option.value}
98
- type="button"
99
- role="radio"
100
- aria-checked={option.selected}
101
- disabled={option.disabled}
102
- title={option.hint ? `${option.label} (${option.hint})` : undefined}
103
- data-active={option.selected ? "true" : undefined}
104
- onMouseDown={(event) => event.stopPropagation()}
105
- onClick={() => item.onSelect(option.value)}
106
- >
107
- <span className="gloom-qb-text">{option.label}</span>
108
- </button>
109
- ))}
110
- </div>
111
- </div>
112
- );
113
- }
137
+ if (item.kind === "select" && item.inline) return <InlineChoices item={item} />;
114
138
  if (item.kind === "toggle") {
115
139
  return (
116
140
  <button
@@ -190,27 +214,30 @@ export function WebQueryBar({ search, items, view, onClearAll, meta, openRequest
190
214
  const overflow = useHorizontalOverflow(scrollRef, [items.length, !!search, !!view, !!onClearAll, meta]);
191
215
  const searchRef = useRef<HTMLDivElement | null>(null);
192
216
  // When an inline choice or the view changes (a shortcut, the next expiry),
193
- // bring the new selection into a scrolled bar. Not on mount: a view at the
194
- // right edge must not scroll the search away when the pane opens.
217
+ // bring the new selection into a scrolled bar. On mount only a strip that
218
+ // scrolls by itself shows its choice: an expiry chosen far down the strip
219
+ // is what the pane shows, but scrolling the whole bar to a view at the right
220
+ // edge would hide the search when the pane opens.
195
221
  const selectionKey = [
196
222
  ...items.filter((item) => item.inline).map((item) => `${item.id}=${item.options.find((option) => option.selected)?.value ?? ""}`),
197
223
  `view=${view?.value ?? ""}`,
198
224
  ].join("|");
199
- const previousSelectionKey = useRef(selectionKey);
225
+ const previousSelectionKey = useRef<string | null>(null);
200
226
  useEffect(() => {
201
227
  const previous = previousSelectionKey.current;
202
228
  previousSelectionKey.current = selectionKey;
203
229
  if (previous === selectionKey) return;
204
- const before = new Map(previous.split("|").map((entry) => entry.split("=") as [string, string]));
230
+ const before = previous == null ? null : new Map(previous.split("|").map((entry) => entry.split("=") as [string, string]));
205
231
  const bar = scrollRef.current;
206
232
  if (!bar) return;
207
233
  for (const entry of selectionKey.split("|")) {
208
234
  const [id, value] = entry.split("=") as [string, string];
209
- if (before.get(id) === value) continue;
235
+ if (before?.get(id) === value) continue;
210
236
  const group = id === "view"
211
237
  ? bar.querySelector(":scope > .gloom-qb-view")
212
- : bar.querySelector(`[data-item-id="${CSS.escape(id)}"]`);
213
- group?.querySelector<HTMLElement>("button[data-active=true]")?.scrollIntoView({ block: "nearest", inline: "nearest" });
238
+ : [...bar.querySelectorAll("[data-item-id]")].find((item) => item.getAttribute("data-item-id") === id);
239
+ if (!before && group?.getAttribute("data-strip") !== "true") continue;
240
+ group?.querySelector<HTMLElement>("button[data-active=true]")?.scrollIntoView?.({ block: "nearest", inline: "nearest" });
214
241
  }
215
242
  }, [selectionKey]);
216
243
  useEffect(() => {
@@ -280,7 +307,7 @@ export function WebQueryBar({ search, items, view, onClearAll, meta, openRequest
280
307
  )}
281
308
  {searchNode}
282
309
  {items.length > 0 && (
283
- <div className="gloom-qb-filters">
310
+ <div className="gloom-qb-filters" data-strip={items.some(isStrip) ? "true" : undefined}>
284
311
  {items.map((item) => (
285
312
  <FilterChip key={item.id} item={item} open={openId === item.id} onOpenChange={(open) => setOpen(item.id, open)} />
286
313
  ))}