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
@@ -3,6 +3,7 @@ import type { PaneRuntimeState } from "../../../core/state/app/state";
3
3
  import type { DesktopDockPreviewState, DesktopSharedStateSnapshot, DesktopThemePreviewState } from "../../../types/desktop-window";
4
4
  import type { DesktopApplicationMenuCommand } from "../../../types/desktop-menu";
5
5
  import type { AppConfig } from "../../../types/config";
6
+ import type { CachedResourceRecord, ResourceCacheKey } from "../../../data/resource-store";
6
7
  import type { TickerRecord } from "../../../types/ticker";
7
8
  import type { ReleaseInfo, UpdateCheckResult, UpdateProgress } from "../../../updater";
8
9
  import type { CapabilityManifest } from "../../../capabilities";
@@ -18,8 +19,12 @@ export interface ElectrobunBackendInit {
18
19
  desktopSnapshot: DesktopSharedStateSnapshot | null;
19
20
  desktopThemePreview: DesktopThemePreviewState;
20
21
  pluginState: Record<string, Record<string, unknown>>;
22
+ /** Saved broker account snapshots, which the view's in-memory store starts from. */
23
+ savedResources?: CachedResourceRecord[];
21
24
  capabilityManifests: CapabilityManifest[];
22
25
  desktopPlatform: string;
26
+ /** The machine name, which a remote assistant sees this desktop app under. */
27
+ hostName?: string;
23
28
  windowKind: "main" | "detached";
24
29
  paneId?: string;
25
30
  /** What the view needs to send crash reports and usage counts the same way the Bun process would. */
@@ -226,6 +231,9 @@ interface DesktopBackendRequestMap {
226
231
  response: null;
227
232
  };
228
233
  "session.delete": { request: { sessionId: string }; response: null };
234
+ /** Saves or drops a broker account snapshot in the cache database; nothing else is accepted. */
235
+ "resources.set": { request: { record: CachedResourceRecord }; response: null };
236
+ "resources.delete": { request: { key: ResourceCacheKey }; response: null };
229
237
  }
230
238
 
231
239
  export type DesktopBackendRequestMethod = keyof DesktopBackendRequestMap;
@@ -251,7 +259,10 @@ export type DesktopWorkspaceRequest = DesktopBackendRequestFor<Extract<DesktopBa
251
259
  export type DesktopPluginStateRequest = DesktopBackendRequestFor<Extract<DesktopBackendRequestMethod, `pluginState.${string}`>>;
252
260
  export type DesktopHostRequest = DesktopBackendRequestFor<Extract<DesktopBackendRequestMethod, `host.${string}`>>;
253
261
  export type DesktopCoreRequest = DesktopBackendRequestFor<
254
- Extract<DesktopBackendRequestMethod, `update.${string}` | `ticker.${string}` | `config.${string}` | `session.${string}`>
262
+ Extract<
263
+ DesktopBackendRequestMethod,
264
+ `update.${string}` | `ticker.${string}` | `config.${string}` | `session.${string}` | `resources.${string}`
265
+ >
255
266
  >;
256
267
 
257
268
  interface BackendRequestPayload {
@@ -397,6 +397,9 @@ function HydratePayload({
397
397
  tickers: new Map(payload.tickers.map((ticker) => [ticker.metadata.ticker, ticker])),
398
398
  });
399
399
  dispatch({ type: "HYDRATE_FINANCIALS", financials: new Map(payload.financials) });
400
+ for (const [instanceId, accounts] of Object.entries(payload.brokerAccounts ?? {})) {
401
+ dispatch({ type: "SET_BROKER_ACCOUNTS", instanceId, accounts });
402
+ }
400
403
  dispatch({ type: "SET_INITIALIZED" });
401
404
 
402
405
  return waitForShotReadiness();
@@ -438,7 +441,7 @@ function ShotPane({ payload, registry }: { payload: DesktopPaneShotPayload; regi
438
441
  const height = payload.heightCells;
439
442
  // PORT keeps what its basket covers, so a capture never reads as the whole account.
440
443
  return (
441
- <PaneShotFrame paneId={instance.instanceId} title={title} width={width} height={height} preserveStatus={["time-sales", "gpu", "hiring", "apps", "catalysts", "litigation", "perps", "members", "analytics"].includes(pane.id)}>
444
+ <PaneShotFrame paneId={instance.instanceId} title={title} width={width} height={height} statusLine={payload.statusLine === true} preserveStatus={["time-sales", "gpu", "hiring", "apps", "catalysts", "litigation", "perps", "members", "analytics"].includes(pane.id)}>
442
445
  {(bodyFrame) => <PaneContent
443
446
  component={pane.component}
444
447
  paneId={instance.instanceId}
@@ -1,20 +1,116 @@
1
1
  /** @jsxImportSource react */
2
- import type { ReactNode } from "react";
2
+ import { useEffect, useState, type ReactNode } from "react";
3
3
  import { FloatingPaneWrapper } from "../../../components/layout/floating-pane";
4
- import { PaneFooterProvider, hasPaneFooterContent } from "../../../components/layout/pane/footer";
4
+ import { PaneFooterProvider, hasPaneFooterContent, type PaneFooterSegment } from "../../../components/layout/pane/footer";
5
5
  import { resolvePaneBodyFrame } from "../../../components/layout/pane/sizing";
6
6
 
7
- /** Static captures retain warnings and optional source status without action shortcuts. */
8
- export function PaneShotFrame({ paneId, title, width, height, preserveStatus = false, children }: {
7
+ declare global {
8
+ interface Window {
9
+ /**
10
+ * Set by a capture that carries a status line: shows the first of the
11
+ * variants (longest first) that fits the footer, cut in the middle when
12
+ * none does, and resolves to the text shown.
13
+ */
14
+ __GLOOM_CLI_SHOT_FIT_STATUS__?: (variants: string[]) => Promise<string>;
15
+ }
16
+ }
17
+
18
+ /**
19
+ * Holds the footer row from the first frame, while the status line is still
20
+ * being worked out from what the pane loaded, so the body never resizes.
21
+ */
22
+ const PENDING_STATUS = "\u00a0";
23
+
24
+ function nextFrames(count: number): Promise<void> {
25
+ return new Promise((resolve) => {
26
+ const step = (left: number) => (left <= 0 ? resolve() : requestAnimationFrame(() => step(left - 1)));
27
+ step(count);
28
+ });
29
+ }
30
+
31
+ /** Keeps the start and the end of a line that does not fit, `keep` characters in all. */
32
+ function middleEllipsis(text: string, keep: number): string {
33
+ if (keep >= text.length) return text;
34
+ const head = Math.ceil(keep / 2);
35
+ const tail = Math.max(0, keep - head);
36
+ return `${text.slice(0, head).trimEnd()}…${tail > 0 ? text.slice(text.length - tail).trimStart() : ""}`;
37
+ }
38
+
39
+ /**
40
+ * The first variant that fits, else the last one cut in the middle as little
41
+ * as it must be. `show` puts a text on screen and resolves once it is drawn.
42
+ */
43
+ async function fitStatusLine(
44
+ variants: readonly string[],
45
+ show: (text: string) => Promise<void>,
46
+ overflows: () => boolean,
47
+ ): Promise<string> {
48
+ for (const variant of variants) {
49
+ await show(variant);
50
+ if (!overflows()) return variant;
51
+ }
52
+ const last = variants.at(-1) ?? "";
53
+ let low = 1;
54
+ let high = last.length - 1;
55
+ let best = middleEllipsis(last, 1);
56
+ while (low <= high) {
57
+ const keep = Math.floor((low + high) / 2);
58
+ const text = middleEllipsis(last, keep);
59
+ await show(text);
60
+ if (overflows()) {
61
+ high = keep - 1;
62
+ } else {
63
+ best = text;
64
+ low = keep + 1;
65
+ }
66
+ }
67
+ await show(best);
68
+ return best;
69
+ }
70
+
71
+ function footerOverflows(): boolean {
72
+ const footer = document.querySelector('[data-gloom-role="pane-footer"]');
73
+ if (!footer) return false;
74
+ return [footer, ...footer.querySelectorAll("*")].some((element) => (
75
+ element instanceof HTMLElement && element.scrollWidth > element.clientWidth + 1
76
+ ));
77
+ }
78
+
79
+ function useShotStatusLine(enabled: boolean): string {
80
+ const [text, setText] = useState(PENDING_STATUS);
81
+ useEffect(() => {
82
+ if (!enabled) return;
83
+ window.__GLOOM_CLI_SHOT_FIT_STATUS__ = (variants) => fitStatusLine(variants, async (next) => {
84
+ setText(next);
85
+ await nextFrames(2);
86
+ }, footerOverflows);
87
+ return () => {
88
+ delete window.__GLOOM_CLI_SHOT_FIT_STATUS__;
89
+ };
90
+ }, [enabled]);
91
+ return text;
92
+ }
93
+
94
+ /**
95
+ * Static captures retain warnings and optional source status without action
96
+ * shortcuts. With `statusLine` the footer also carries the capture's dated
97
+ * status line (`Fri 9 Oct close · 15 min delayed · markets closed until Mon`),
98
+ * inside the requested size: the body gives up the footer row for it.
99
+ */
100
+ export function PaneShotFrame({ paneId, title, width, height, preserveStatus = false, statusLine = false, children }: {
9
101
  paneId: string;
10
102
  title: string;
11
103
  width: number;
12
104
  height: number;
13
105
  preserveStatus?: boolean;
106
+ statusLine?: boolean;
14
107
  children: (frame: ReturnType<typeof resolvePaneBodyFrame>) => ReactNode;
15
108
  }) {
109
+ const statusText = useShotStatusLine(statusLine);
16
110
  return <PaneFooterProvider>{(registeredFooter) => {
17
- const footer = { info: registeredFooter.info.filter((segment) => preserveStatus || segment.icon === "warning"), hints: [], menu: [], keys: [] };
111
+ const status: PaneFooterSegment[] = statusLine ? [{ id: "shot-status", parts: [{ text: statusText, tone: "label" }] }] : [];
112
+ const info = registeredFooter.info.filter((segment) => preserveStatus || segment.icon === "warning");
113
+ const footer = { info: [...info, ...status], hints: [], menu: [], keys: [] };
18
114
  const bodyFrame = resolvePaneBodyFrame({
19
115
  width,
20
116
  height,
@@ -14,16 +14,6 @@ const contextMenuActionScope = new DesktopContextMenuActionScope(
14
14
  CONTEXT_MENU_ACTION_TTL_MS,
15
15
  );
16
16
 
17
- export function startElectrobunWindowDrag(): void {
18
- window.__electrobunInternalBridge?.postMessage(JSON.stringify([
19
- JSON.stringify({
20
- type: "message",
21
- id: "startWindowMove",
22
- payload: { id: window.__electrobunWindowId },
23
- }),
24
- ]));
25
- }
26
-
27
17
  export async function showDesktopContextMenu(items: ContextMenuItem[]): Promise<boolean> {
28
18
  if (!NATIVE_CONTEXT_MENU_SUPPORTED) return false;
29
19
  contextMenuActionScope.clear();
@@ -0,0 +1,28 @@
1
+ function postWindowMove(id: "startWindowMove" | "stopWindowMove"): void {
2
+ window.__electrobunInternalBridge?.postMessage(JSON.stringify([
3
+ JSON.stringify({ type: "message", id, payload: { id: window.__electrobunWindowId } }),
4
+ ]));
5
+ }
6
+
7
+ let endWindowMove: (() => void) | null = null;
8
+
9
+ /**
10
+ * Moves the window with the pointer until the button comes up. Electrobun
11
+ * stops the move when its native monitor sees the release, but only if the
12
+ * start reached it first: the start goes through the Bun process, and a quick
13
+ * click releases before it lands, leaving the window following the bare
14
+ * pointer until the next click. The release is sent from here too, as
15
+ * Electrobun's own drag regions do; it always arrives after the start.
16
+ */
17
+ export function startElectrobunWindowDrag(): void {
18
+ // A press whose release never reached the page (it ended outside the window) is over.
19
+ endWindowMove?.();
20
+ postWindowMove("startWindowMove");
21
+ const end = () => {
22
+ window.removeEventListener("mouseup", end, true);
23
+ if (endWindowMove === end) endWindowMove = null;
24
+ postWindowMove("stopWindowMove");
25
+ };
26
+ endWindowMove = end;
27
+ window.addEventListener("mouseup", end, true);
28
+ }
@@ -210,7 +210,10 @@ async function boot() {
210
210
  });
211
211
 
212
212
  const remoteControlAdapter = init.windowKind === "main"
213
- ? { registerHandler: setElectrobunRemoteRequestHandler }
213
+ ? {
214
+ registerHandler: setElectrobunRemoteRequestHandler,
215
+ terminalDevice: { kind: "desktop" as const, name: init.hostName?.trim() || "Gloom desktop" },
216
+ }
214
217
  : undefined;
215
218
  measurePerfAsync("startup.electrobun.root-render", async () => {
216
219
  root.render(
@@ -4,7 +4,8 @@ import {
4
4
  SESSION_SAVE_DEBOUNCE_MS,
5
5
  } from "../../../../state/persist-scheduler";
6
6
  import { backendRequest, getElectrobunBackendInitSnapshot } from "../backend-rpc";
7
- import { MemoryResourceStore } from "../../../../data/memory-resource-store";
7
+ import { WriteThroughResourceStore } from "../../../../data/memory-resource-store";
8
+ import { isBrokerAccountSnapshotKey } from "../../../../brokers/account-cache";
8
9
 
9
10
  class RemoteSessionStore {
10
11
  private snapshot = getElectrobunBackendInitSnapshot()?.sessionSnapshot ?? null;
@@ -123,9 +124,21 @@ class RemotePluginStateStore {
123
124
  }
124
125
  }
125
126
 
127
+ /**
128
+ * Caches stay in the view's memory, except broker account snapshots: the Bun
129
+ * process saves those in the cache database, as the terminal does, so a
130
+ * broker portfolio opens with its last known equity and cash.
131
+ */
132
+ function createViewResourceStore(): WriteThroughResourceStore {
133
+ return new WriteThroughResourceStore(getElectrobunBackendInitSnapshot()?.savedResources ?? [], {
134
+ set: (record) => void backendRequest("resources.set", { record }).catch(() => {}),
135
+ delete: (key) => void backendRequest("resources.delete", { key }).catch(() => {}),
136
+ }, isBrokerAccountSnapshotKey);
137
+ }
138
+
126
139
  export class RemotePersistence {
127
140
  readonly tickers = {};
128
- readonly resources = new MemoryResourceStore();
141
+ readonly resources = createViewResourceStore();
129
142
  readonly pluginState = new RemotePluginStateStore(getElectrobunBackendInitSnapshot()?.pluginState ?? {});
130
143
  readonly sessions = new RemoteSessionStore();
131
144
 
@@ -2,11 +2,8 @@
2
2
  import type { RendererHost, UiHost } from "../../../ui/host";
3
3
  import { flushUsageCounts } from "../../../telemetry/usage-counts";
4
4
  import { backendRequest } from "./backend-rpc";
5
- import {
6
- NATIVE_CONTEXT_MENU_SUPPORTED,
7
- showDesktopContextMenu,
8
- startElectrobunWindowDrag,
9
- } from "./host/native";
5
+ import { NATIVE_CONTEXT_MENU_SUPPORTED, showDesktopContextMenu } from "./host/native";
6
+ import { startElectrobunWindowDrag } from "./host/window-move";
10
7
  import { createDomUiHost } from "../../dom/dom-ui-host";
11
8
 
12
9
  export function createWebUiHost(desktopPlatform?: string): UiHost {
@@ -1,8 +1,10 @@
1
+ import { flushSync } from "@opentui/react";
1
2
  import { useMemo, type ReactNode } from "react";
2
3
  import { useNativeRenderer } from "../../ui";
3
4
  import {
4
5
  createShortcutRegistry,
5
6
  InputHostProvider,
7
+ useRegisteredBeforeShortcut,
6
8
  useRegisteredShortcut,
7
9
  type InputHost,
8
10
  } from "../../react/input";
@@ -10,7 +12,7 @@ import { toKeyEventLike, useKeyboard, useTerminalDimensions } from "./host";
10
12
 
11
13
  export function OpenTuiInputHostProvider({ children }: { children: ReactNode }) {
12
14
  const renderer = useNativeRenderer();
13
- const shortcutRegistry = useMemo(() => createShortcutRegistry(), []);
15
+ const shortcutRegistry = useMemo(() => createShortcutRegistry({ flushSync }), []);
14
16
 
15
17
  useKeyboard((event) => {
16
18
  const shortcutEvent = toKeyEventLike(event);
@@ -22,6 +24,9 @@ export function OpenTuiInputHostProvider({ children }: { children: ReactNode })
22
24
  useShortcut(handler, options) {
23
25
  useRegisteredShortcut(shortcutRegistry, handler, options);
24
26
  },
27
+ useBeforeShortcut(listener) {
28
+ useRegisteredBeforeShortcut(shortcutRegistry, listener);
29
+ },
25
30
  useViewport() {
26
31
  const dimensions = useTerminalDimensions();
27
32
  return { width: dimensions.width, height: dimensions.height };
@@ -1,3 +1,4 @@
1
+ import { hostname } from "os";
1
2
  import { getGloomberbDirs } from "../../data/config/home";
2
3
  import { existsSync, mkdirSync } from "fs";
3
4
  import { App } from "../../app";
@@ -38,6 +39,8 @@ import {
38
39
  import { currentTelemetryConfig } from "../../telemetry/live-config";
39
40
  import { flushUsageCounts, installUsageCounter, usageCountsEnabled } from "../../telemetry/usage-counts";
40
41
  import { attentionCountsEnabled, installAttentionCounter } from "../../telemetry/attention-counts";
42
+ import { starPromptEnvironmentAllows } from "../../app/star-prompt/model";
43
+ import { allowStarPrompt } from "../../app/star-prompt/runtime";
41
44
 
42
45
  // Declared here rather than sniffed: the desktop view and the hosted browser
43
46
  // app are both browser contexts but differ in what plugins may do.
@@ -56,6 +59,7 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
56
59
  const appLog = debugLog.createLogger("app");
57
60
  appLog.info("Gloomberb starting");
58
61
  const remoteControlAdapter: RemoteControlAdapter = {
62
+ terminalDevice: { kind: "tui", name: hostname() },
59
63
  startServer: ({ dataDir, handle }) => {
60
64
  let closed = false;
61
65
  const serverPromise: Promise<RemoteControlServer> = startRemoteControlServer({
@@ -121,6 +125,11 @@ export async function startOpenTuiApp({ externalPlugins, cliLaunchRequest }: Sta
121
125
  officialPluginIds: loadOfficialPluginIds,
122
126
  });
123
127
  const stopAttention = installAttentionCounter(() => attentionCountsEnabled(currentTelemetryConfig(config), process.env));
128
+ allowStarPrompt(starPromptEnvironmentAllows({
129
+ env: process.env,
130
+ stdinIsTTY: process.stdin.isTTY,
131
+ stdoutIsTTY: process.stdout.isTTY,
132
+ }));
124
133
  host = await measurePerfAsync("startup.opentui.create-host", () => createOpenTuiHost());
125
134
  const renderer = host.renderer;
126
135
  renderer.once("destroy", stopAttention);
@@ -2,6 +2,7 @@ import { RGBA, StyledText as OpenTuiStyledText, SyntaxStyle, TextAttributes as O
2
2
  import { createElement, forwardRef, useEffect, useRef, useState, type ReactNode } from "react";
3
3
  import { TextAttributes, type UiHost, type TextProps } from "../../ui/host";
4
4
  import { renderAsciiText } from "../../ui/ascii-font";
5
+ import { scrollBoxScrollsVertically } from "../../ui/scroll-box-axis";
5
6
  import { OpenTuiImageSurface } from "./image/surface";
6
7
  import { OpenTuiChartSurface } from "./chart-surface";
7
8
 
@@ -141,7 +142,12 @@ function mapTextContent(content: unknown): unknown {
141
142
  }
142
143
 
143
144
  const OpenTuiBox = createOpenTuiPrimitive("box");
144
- const OpenTuiScrollBox = createOpenTuiPrimitive("scrollbox");
145
+ const OpenTuiScrollBoxPrimitive = createOpenTuiPrimitive("scrollbox");
146
+ // The native box scrolls vertically unless scrollY is false; the shared rule
147
+ // also keeps the one-row horizontal strips from scrolling on that axis.
148
+ const OpenTuiScrollBox = forwardRef<unknown, OpenTuiPrimitiveProps>(function OpenTuiScrollBox(props, ref) {
149
+ return createElement(OpenTuiScrollBoxPrimitive, { ...props, scrollY: scrollBoxScrollsVertically(props), ref });
150
+ });
145
151
  const OpenTuiInput = createOpenTuiPrimitive("input");
146
152
  const OpenTuiTextarea = createOpenTuiPrimitive("textarea");
147
153
  const OpenTuiMediaSurface = createOpenTuiPrimitive("box");
@@ -1,4 +1,5 @@
1
1
  import type { PaneRuntimeState } from "../core/state/app/types";
2
+ import { foldPaneLayout } from "../data/config/store/fold-panes";
2
3
  import { publicTickerBindingSymbol } from "../tickers/selection";
3
4
  import { pinFollowingPane } from "../layout/pane-follow";
4
5
  import {
@@ -641,28 +642,37 @@ export function materializeMarketplaceLayout(
641
642
  `${paneId.slice(0, 80)}:shared-${crypto.randomUUID().replaceAll("-", "").slice(0, 20)}`
642
643
  ),
643
644
  ): { layout: LayoutConfig; paneState: Record<string, PaneRuntimeState> } {
644
- const ids = new Map(payload.layout.instances.map((instance, index) => [
645
+ // A layout or pane shared before SIV and SOVR folded into SI and CDX still
646
+ // names the old panes. It opens the way a saved layout migrates.
647
+ const folded = foldPaneLayout(payload.layout);
648
+ const source = {
649
+ layout: folded.layout,
650
+ paneState: Object.fromEntries(Object.entries(payload.paneState).flatMap(([id, state]) => (
651
+ folded.removed.has(id) ? [] : [[folded.idMap.get(id) ?? id, state]]
652
+ ))),
653
+ };
654
+ const ids = new Map(source.layout.instances.map((instance, index) => [
645
655
  instance.instanceId,
646
656
  createId(instance.paneId, index),
647
657
  ]));
648
658
  if (new Set(ids.values()).size !== ids.size) throw new Error("Could not create unique pane ids for this layout.");
649
659
  const layout: LayoutConfig = {
650
- dockRoot: mapDockNode(payload.layout.dockRoot, ids),
651
- instances: payload.layout.instances.map((instance) => ({
660
+ dockRoot: mapDockNode(source.layout.dockRoot, ids),
661
+ instances: source.layout.instances.map((instance) => ({
652
662
  ...structuredClone(instance),
653
663
  instanceId: ids.get(instance.instanceId)!,
654
664
  ...(instance.binding ? { binding: mapBinding(instance.binding, ids) } : {}),
655
665
  })),
656
- floating: payload.layout.floating.map((entry) => ({
666
+ floating: source.layout.floating.map((entry) => ({
657
667
  ...entry,
658
668
  instanceId: ids.get(entry.instanceId) ?? entry.instanceId,
659
669
  })),
660
- detached: payload.layout.detached.map((entry) => ({
670
+ detached: source.layout.detached.map((entry) => ({
661
671
  ...entry,
662
672
  instanceId: ids.get(entry.instanceId) ?? entry.instanceId,
663
673
  })),
664
674
  };
665
- const paneState = Object.fromEntries(Object.entries(payload.paneState).flatMap(([id, state]) => {
675
+ const paneState = Object.fromEntries(Object.entries(source.paneState).flatMap(([id, state]) => {
666
676
  const materializedId = ids.get(id);
667
677
  return materializedId ? [[materializedId, structuredClone(state)]] : [];
668
678
  }));
@@ -39,13 +39,13 @@ import { normalizeNewsFeed } from "../../news/news-model";
39
39
  import { resolveCurrencyUnit } from "../../utils/currency-units";
40
40
  import { canonicalExchange, canonicalTickerKey, parsePublicTickerKey } from "../../utils/exchanges";
41
41
  import { normalizePriceHistory, priceHistoryIntervalMs, reachesLatestSettledSession } from "../../utils/price-history";
42
- import { createProviderMiss } from "../provider-errors";
42
+ import { createProviderMiss, providerMissReason } from "../provider-errors";
43
43
  import { assertSecRegistrantMatches } from "../sec-registrant";
44
44
  import { nonUsSecListingVenue } from "../../utils/sec";
45
45
  import { publicListingTarget } from "../listing-target";
46
46
  import { canonicalHistoryInterval, HistoryRetentionError, parseHistoryRecoveryCandidate, parseHistoryRetention, type HistoryRetention } from "../history-retention";
47
47
  import { getRouterEntityKey } from "../provider-router/cache";
48
- import { tickerHasListingSuffix } from "../listing-symbols";
48
+ import { hongKongListingCode, tickerHasListingSuffix } from "../listing-symbols";
49
49
  import { parseSecAcceptanceTime } from "../sec-edgar/acceptance-time";
50
50
  import { hasMalformedIntradayHistory } from "../../time-series/history-quality";
51
51
  import {
@@ -57,6 +57,7 @@ import {
57
57
  formatCloudDateTime,
58
58
  isEmptyCloudStatus,
59
59
  mapBatchError,
60
+ NOT_FOUND_REASON,
60
61
  mapCloudFinancials,
61
62
  mapOptionsChain,
62
63
  mapPricePoint,
@@ -124,7 +125,7 @@ async function withCloudFallback<T>(load: () => Promise<T>, message: string): Pr
124
125
  return await load();
125
126
  } catch (error) {
126
127
  if (isCloudProviderMiss(error)) {
127
- throw createProviderMiss(message);
128
+ throw createProviderMiss(message, providerMissReason(error));
128
129
  }
129
130
  throw error;
130
131
  }
@@ -237,7 +238,7 @@ function cloudHistoryResolution(interval: string): ManualChartResolution | null
237
238
  }
238
239
 
239
240
  function quoteTargetKey(symbol: string, exchange?: string): string {
240
- const target = publicListingTarget(symbol, exchange);
241
+ const target = cloudInstrumentTarget(symbol, exchange);
241
242
  const base = target.exchange && tickerHasListingSuffix(target.symbol) ? target.symbol.slice(0, target.symbol.indexOf(".")) : target.symbol;
242
243
  return canonicalTickerKey(base, target.exchange);
243
244
  }
@@ -251,7 +252,14 @@ function cloudResponseTargetKey(key: string, requested: Set<string>): string | u
251
252
  ? symbol : undefined;
252
253
  }
253
254
 
254
- const cloudInstrumentTarget = publicListingTarget;
255
+ /**
256
+ * The listing as Gloom Cloud knows it. A Hong Kong code goes out with its four
257
+ * digits: Cloud has no 700 or 5 on HKEX, only 0700 and 0005.
258
+ */
259
+ function cloudInstrumentTarget(symbol: string, exchange?: string) {
260
+ const target = publicListingTarget(symbol, exchange);
261
+ return target.exchange ? { ...target, symbol: hongKongListingCode(target.symbol, target.exchange) } : target;
262
+ }
255
263
 
256
264
  function cloudHistoryRecovery(
257
265
  context: MarketDataRequestContext | undefined,
@@ -323,7 +331,7 @@ function unwrapRequiredCloudResponse<T>(response: CloudMarketResponse<T>, messag
323
331
  return response.data;
324
332
  }
325
333
  if (isEmptyCloudStatus(response.status)) {
326
- throw createProviderMiss(response.reasonCode ?? message);
334
+ throw createProviderMiss(response.reasonCode ?? message, response.message, { notFound: response.reasonCode === NOT_FOUND_REASON });
327
335
  }
328
336
  throw new Error(response.reasonCode ?? message);
329
337
  }
@@ -404,13 +412,14 @@ export class GloomberbCloudProvider implements AssetDataProvider {
404
412
  return withCloudFallback(
405
413
  async () => {
406
414
  const response = await apiClient.getCloudQuote(target.symbol, target.exchange);
407
- if (isStaleCloudResponse(response)) {
408
- throw createProviderMiss(`Cloud quotes are stale for ${ticker}`);
409
- }
410
- return retainRequestedQuoteSymbol(mapQuote(
415
+ const quote = retainRequestedQuoteSymbol(mapQuote(
411
416
  unwrapRequiredCloudResponse(response, `Cloud quotes are unavailable for ${ticker}`),
412
417
  response.providerMeta,
413
418
  ), ticker);
419
+ // A stale answer is the last price the service has. It stays flagged, so
420
+ // no freshness check passes it and only a request with no current quote
421
+ // from any source falls back to it.
422
+ return isStaleCloudResponse(response) ? { ...quote, stale: true } : quote;
414
423
  },
415
424
  `Cloud quotes are unavailable for ${ticker}`,
416
425
  );
@@ -1,5 +1,6 @@
1
1
  import { futuresGenericListing, futuresGenericPriceBasis } from "../../utils/futures-generic";
2
2
  import type { TimeRange } from "../../time-series/range";
3
+ import { RANGE_HISTORY_RESOLUTION } from "../../time-series/resolution";
3
4
  import type {
4
5
  FinancialStatement,
5
6
  Fundamentals,
@@ -305,6 +306,9 @@ export function mapOptionsChain(
305
306
  };
306
307
  }
307
308
 
309
+ /** The reason code Gloom Cloud gives an empty answer for a symbol no listing carries. */
310
+ export const NOT_FOUND_REASON = "NOT_FOUND";
311
+
308
312
  export function isEmptyCloudStatus(
309
313
  status: CloudMarketResponse<unknown>["status"],
310
314
  ): boolean {
@@ -316,7 +320,7 @@ export function mapBatchError<T>(
316
320
  fallbackMessage: string,
317
321
  ): Error {
318
322
  if (isEmptyCloudStatus(item.status)) {
319
- return createProviderMiss(item.reasonCode ?? fallbackMessage);
323
+ return createProviderMiss(item.reasonCode ?? fallbackMessage, item.message, { notFound: item.reasonCode === NOT_FOUND_REASON });
320
324
  }
321
325
  return new Error(item.reasonCode ?? fallbackMessage);
322
326
  }
@@ -370,29 +374,24 @@ export function formatCloudDateTime(
370
374
  return `${year}-${month}-${day}`;
371
375
  }
372
376
 
377
+ const HISTORY_OUTPUT_SIZE: Record<TimeRange, number> = {
378
+ "1D": 24 * 12,
379
+ "1W": 7 * 24,
380
+ "1M": 31,
381
+ "3M": 93,
382
+ "6M": 186,
383
+ "1Y": 366,
384
+ "5Y": 261,
385
+ "ALL": 600,
386
+ };
387
+
388
+ /** The bar size comes from the shared range table, which `gloomberb help history` also lists. */
373
389
  export function toHistoryRequest(range: TimeRange): {
374
390
  interval: string;
375
391
  outputsize: number;
376
392
  rangeKey: TimeRange;
377
393
  } {
378
- switch (range) {
379
- case "1D":
380
- return { interval: "5min", outputsize: 24 * 12, rangeKey: range };
381
- case "1W":
382
- return { interval: "1h", outputsize: 7 * 24, rangeKey: range };
383
- case "1M":
384
- return { interval: "1day", outputsize: 31, rangeKey: range };
385
- case "3M":
386
- return { interval: "1day", outputsize: 93, rangeKey: range };
387
- case "6M":
388
- return { interval: "1day", outputsize: 186, rangeKey: range };
389
- case "1Y":
390
- return { interval: "1day", outputsize: 366, rangeKey: range };
391
- case "5Y":
392
- return { interval: "1week", outputsize: 261, rangeKey: range };
393
- case "ALL":
394
- return { interval: "1month", outputsize: 600, rangeKey: range };
395
- default:
396
- return { interval: "1day", outputsize: 366, rangeKey: range };
397
- }
394
+ const resolution = RANGE_HISTORY_RESOLUTION[range];
395
+ if (!resolution) return { interval: "1day", outputsize: 366, rangeKey: range };
396
+ return { interval: toCloudInterval(resolution), outputsize: HISTORY_OUTPUT_SIZE[range], rangeKey: range };
398
397
  }
@@ -23,7 +23,7 @@ const EXCHANGE_SUFFIX_MAP: Record<string, string> = {
23
23
  TASE: ".TA",
24
24
  JSE: ".JO",
25
25
  BVMF: ".SA", MEXI: ".MX", BYMA: ".BA", BCS: ".SN",
26
- TADAWUL: ".SAU", QSE: ".QA", DFM: ".AE",
26
+ TADAWUL: ".SR", QE: ".QA", DFM: ".AE",
27
27
  };
28
28
 
29
29
  const EXCHANGE_FALLBACKS: Record<string, string[]> = {
@@ -143,11 +143,17 @@ export function listingSuffixExchange(ticker: string): string | undefined {
143
143
  return venues.size === 1 ? [...venues][0] : undefined;
144
144
  }
145
145
 
146
+ /**
147
+ * A Hong Kong listing's four-digit code. Brokers such as Interactive Brokers
148
+ * report Tencent as 700 and HSBC as 5; the exchange and market data know
149
+ * them as 0700 and 0005.
150
+ */
151
+ export function hongKongListingCode(ticker: string, exchange: string): string {
152
+ return isHongKongExchange(exchange) && /^\d+$/.test(ticker) ? ticker.padStart(4, "0") : ticker;
153
+ }
154
+
146
155
  function normalizeListingTicker(ticker: string, exchange: string): string {
147
- if (isHongKongExchange(exchange) && /^\d+$/.test(ticker)) {
148
- return ticker.padStart(4, "0");
149
- }
150
- return ticker.replace(/ /g, "-");
156
+ return hongKongListingCode(ticker, exchange).replace(/ /g, "-");
151
157
  }
152
158
 
153
159
  function isHongKongExchange(exchange: string): boolean {