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
@@ -6,6 +6,7 @@ import {
6
6
  useRef,
7
7
  type CSSProperties,
8
8
  type MouseEvent,
9
+ type PointerEvent,
9
10
  type ReactNode,
10
11
  type Ref,
11
12
  } from "react";
@@ -19,8 +20,56 @@ import {
19
20
  cellMouseEvent,
20
21
  requestWebFrame,
21
22
  } from "./mouse";
23
+ import { hideDragShield, showDragShield } from "./drag-shield";
24
+ import { applyLiveFrame } from "./live-frame";
22
25
  import { cleanDomProps, commonStyle } from "./style";
23
26
 
27
+ /** Farther than this from the pointer, in px, a predicted point is noise. */
28
+ const MAX_PREDICTION_PX = 48;
29
+
30
+ type PointerMoveEvent = globalThis.MouseEvent & {
31
+ pointerId?: number;
32
+ getPredictedEvents?: () => Array<{ clientX: number; clientY: number }>;
33
+ };
34
+
35
+ function isPointerMove(event: globalThis.MouseEvent): boolean {
36
+ return typeof (event as PointerMoveEvent).pointerId === "number";
37
+ }
38
+
39
+ /**
40
+ * Where the browser predicts the pointer will be a moment after this move, when
41
+ * it predicts at all (Chrome does for the mouse; WebKit returns none). The
42
+ * event itself already sits at the last of its coalesced points.
43
+ */
44
+ function predictedPoint(event: globalThis.MouseEvent): { clientX: number; clientY: number } | null {
45
+ const predicted = (event as PointerMoveEvent).getPredictedEvents?.();
46
+ const next = predicted?.[0];
47
+ if (!next) return null;
48
+ const dx = next.clientX - event.clientX;
49
+ const dy = next.clientY - event.clientY;
50
+ return dx * dx + dy * dy <= MAX_PREDICTION_PX * MAX_PREDICTION_PX ? next : null;
51
+ }
52
+
53
+ function capturePointer(element: HTMLElement | null, pointerId: number | null): { element: HTMLElement; pointerId: number } | null {
54
+ if (!element || pointerId === null || typeof element.setPointerCapture !== "function") return null;
55
+ try {
56
+ element.setPointerCapture(pointerId);
57
+ return { element, pointerId };
58
+ } catch {
59
+ // The pointer is already up or belongs to another frame: drag uncaptured.
60
+ return null;
61
+ }
62
+ }
63
+
64
+ function releasePointer(captured: { element: HTMLElement; pointerId: number } | null): void {
65
+ if (!captured) return;
66
+ try {
67
+ if (captured.element.hasPointerCapture?.(captured.pointerId)) captured.element.releasePointerCapture(captured.pointerId);
68
+ } catch {
69
+ // Released already, by the browser on pointerup.
70
+ }
71
+ }
72
+
24
73
  export const WebBox = forwardRef<HTMLDivElement, Record<string, unknown> & { children?: ReactNode }>(
25
74
  function WebBox({ children, ...props }, ref: Ref<HTMLDivElement>) {
26
75
  const elementRef = useRef<HTMLDivElement | null>(null);
@@ -34,7 +83,13 @@ export const WebBox = forwardRef<HTMLDivElement, Record<string, unknown> & { chi
34
83
  ? props.hoverBackgroundColor
35
84
  : undefined;
36
85
 
37
- useImperativeHandle(ref, () => cellBoundsForElement(() => elementRef.current, () => propsRef.current) as unknown as HTMLDivElement, []);
86
+ useImperativeHandle(ref, () => {
87
+ const handle = cellBoundsForElement(() => elementRef.current, () => propsRef.current);
88
+ handle.setLiveFrame = (frame) => {
89
+ if (elementRef.current) applyLiveFrame(elementRef.current, frame, propsRef.current);
90
+ };
91
+ return handle as unknown as HTMLDivElement;
92
+ }, []);
38
93
 
39
94
  const cancelPendingFrame = () => {
40
95
  if (frameRef.current !== null) {
@@ -63,7 +118,11 @@ export const WebBox = forwardRef<HTMLDivElement, Record<string, unknown> & { chi
63
118
  flushPendingFrameMouseHandlers();
64
119
  };
65
120
 
66
- const scheduleFrameMouseHandler = (event: MouseLikeEvent, type: "move" | "drag") => {
121
+ const scheduleFrameMouseHandler = (
122
+ event: MouseLikeEvent,
123
+ type: "move" | "drag",
124
+ ahead: { clientX: number; clientY: number } | null = null,
125
+ ) => {
67
126
  // The DOM keeps firing mousemove while a button is held. A move means no
68
127
  // button anywhere else in the app, so drags must not surface as moves.
69
128
  if (type === "move" && draggingRef.current) return;
@@ -74,7 +133,7 @@ export const WebBox = forwardRef<HTMLDivElement, Record<string, unknown> & { chi
74
133
  return;
75
134
  }
76
135
 
77
- const nextEvent = cellMouseEvent(event, type);
136
+ const nextEvent = cellMouseEvent(event, type, ahead);
78
137
  if (type === "move") {
79
138
  pendingMoveRef.current = nextEvent;
80
139
  } else {
@@ -89,27 +148,42 @@ export const WebBox = forwardRef<HTMLDivElement, Record<string, unknown> & { chi
89
148
  };
90
149
 
91
150
  const lastPointRef = useRef({ x: 0, y: 0 });
151
+ // The pointer that pressed this box, so a drag can capture it.
152
+ const pressedPointerRef = useRef<number | null>(null);
92
153
  const dragSessionRef = useRef<{
93
154
  move: (event: globalThis.MouseEvent) => void;
94
155
  up: (event: globalThis.MouseEvent) => void;
95
156
  cancel: () => void;
157
+ captured: { element: HTMLElement; pointerId: number } | null;
158
+ /** Pointer events are arriving, so the mousemove after each is a repeat. */
159
+ pointerMoves: boolean;
160
+ /** Moved far enough to be a drag: the drag shield is up. */
161
+ shielded: boolean;
96
162
  } | null>(null);
97
- const endDocumentDragRef = useRef<(event: globalThis.MouseEvent | null, notify: boolean) => void>(() => {});
98
- endDocumentDragRef.current = (event, notify) => {
163
+ /**
164
+ * Ends the press. "end" reports a release at `event` (or the last point);
165
+ * "cancel" reports the drag abandoned (the window lost focus or the system
166
+ * took the pointer), so a move can put things back.
167
+ */
168
+ const endDocumentDragRef = useRef<(event: globalThis.MouseEvent | null, outcome: "end" | "cancel") => void>(() => {});
169
+ endDocumentDragRef.current = (event, outcome) => {
99
170
  const session = dragSessionRef.current;
100
171
  const wasDragging = draggingRef.current;
101
172
  if (!session && !wasDragging) return;
102
173
  draggingRef.current = false;
174
+ const shielded = session?.shielded === true;
103
175
  dragSessionRef.current = null;
104
- document.body.classList.remove("gloom-dragging");
176
+ if (shielded) hideDragShield();
105
177
  if (session) {
178
+ document.removeEventListener("pointermove", session.move);
106
179
  document.removeEventListener("mousemove", session.move);
107
180
  document.removeEventListener("mouseup", session.up);
108
181
  document.removeEventListener("pointerup", session.up);
109
182
  document.removeEventListener("pointercancel", session.cancel);
110
183
  window.removeEventListener("blur", session.cancel);
184
+ releasePointer(session.captured);
111
185
  }
112
- if (!notify || !wasDragging) return;
186
+ if (!wasDragging) return;
113
187
  const point = event ?? new MouseEvent("mouseup", {
114
188
  clientX: lastPointRef.current.x,
115
189
  clientY: lastPointRef.current.y,
@@ -119,14 +193,14 @@ export const WebBox = forwardRef<HTMLDivElement, Record<string, unknown> & { chi
119
193
  flushPendingFrameNow();
120
194
  callMouseHandler(propsRef.current.onMouse, point, "up");
121
195
  callMouseHandler(propsRef.current.onMouseUp, point, "up");
122
- callMouseHandler(propsRef.current.onMouseDragEnd, point, "drag-end");
196
+ callMouseHandler(propsRef.current.onMouseDragEnd, point, outcome === "cancel" ? "drag-cancel" : "drag-end");
123
197
  };
124
198
 
125
199
  useEffect(() => () => {
126
200
  cancelPendingFrame();
127
201
  pendingMoveRef.current = null;
128
202
  pendingDragRef.current = null;
129
- endDocumentDragRef.current(null, true);
203
+ endDocumentDragRef.current(null, "end");
130
204
  }, []);
131
205
 
132
206
  const handlesWheel = typeof props.onMouseScroll === "function";
@@ -146,7 +220,7 @@ export const WebBox = forwardRef<HTMLDivElement, Record<string, unknown> & { chi
146
220
  pendingMoveRef.current = null;
147
221
  // A press that never saw mouseup (the window move took the pointer) is still tracked.
148
222
  // Finish it before the new press, so its drag-end cannot clear the drag this press starts.
149
- endDocumentDragRef.current(null, true);
223
+ endDocumentDragRef.current(null, "end");
150
224
  callMouseHandler(propsRef.current.onMouseDown, event, "down");
151
225
  if (event.button !== 0 || (event.isPropagationStopped() && !hasDirectDrag)) return;
152
226
  if (!hasSyntheticDrag && !hasDirectDrag) return;
@@ -157,27 +231,41 @@ export const WebBox = forwardRef<HTMLDivElement, Record<string, unknown> & { chi
157
231
  const originY = event.clientY;
158
232
  const move = (moveEvent: globalThis.MouseEvent) => {
159
233
  if (!draggingRef.current) return;
234
+ const current = dragSessionRef.current;
235
+ if (isPointerMove(moveEvent)) {
236
+ if (current) current.pointerMoves = true;
237
+ } else if (current?.pointerMoves) {
238
+ return;
239
+ }
160
240
  lastPointRef.current = { x: moveEvent.clientX, y: moveEvent.clientY };
161
241
  // The button is up and mouseup never arrived. End the drag instead of following the cursor.
162
242
  if (moveEvent.buttons === 0) {
163
- endDocumentDragRef.current(moveEvent, true);
243
+ endDocumentDragRef.current(moveEvent, "end");
164
244
  return;
165
245
  }
166
246
  const dx = moveEvent.clientX - originX;
167
247
  const dy = moveEvent.clientY - originY;
168
- if (!document.body.classList.contains("gloom-dragging")) {
248
+ if (current && !current.shielded) {
169
249
  if (dx * dx + dy * dy < 9) return;
170
- document.body.classList.add("gloom-dragging");
250
+ current.shielded = true;
251
+ showDragShield(elementRef.current);
252
+ // Captured once it is a drag (a click keeps its usual target), the
253
+ // moves skip hit testing and leave every other element's hover state
254
+ // alone: nothing under the pointer restyles or repaints while a pane,
255
+ // divider or chart is dragged across it. The document listeners still
256
+ // see every move.
257
+ if (!current.captured) current.captured = capturePointer(elementRef.current, pressedPointerRef.current);
171
258
  }
172
- scheduleFrameMouseHandler(moveEvent, "drag");
259
+ scheduleFrameMouseHandler(moveEvent, "drag", predictedPoint(moveEvent));
173
260
  };
174
261
  const up = (upEvent: globalThis.MouseEvent) => {
175
- endDocumentDragRef.current(upEvent, true);
262
+ endDocumentDragRef.current(upEvent, "end");
176
263
  };
177
264
  const cancel = () => {
178
- endDocumentDragRef.current(null, true);
265
+ endDocumentDragRef.current(null, "cancel");
179
266
  };
180
- dragSessionRef.current = { move, up, cancel };
267
+ dragSessionRef.current = { move, up, cancel, captured: null, pointerMoves: false, shielded: false };
268
+ document.addEventListener("pointermove", move);
181
269
  document.addEventListener("mousemove", move);
182
270
  document.addEventListener("mouseup", up);
183
271
  document.addEventListener("pointerup", up);
@@ -185,6 +273,13 @@ export const WebBox = forwardRef<HTMLDivElement, Record<string, unknown> & { chi
185
273
  window.addEventListener("blur", cancel);
186
274
  };
187
275
 
276
+ const handlesDrag = typeof props.onMouse === "function"
277
+ || typeof props.onMouseDrag === "function"
278
+ || typeof props.onMouseDragEnd === "function";
279
+ const handlePointerDown = (event: PointerEvent) => {
280
+ pressedPointerRef.current = event.pointerId;
281
+ };
282
+
188
283
  const handleMouseDownCapture = (event: MouseEvent) => {
189
284
  callMouseHandler(propsRef.current.onMouseDownCapture, event, "down");
190
285
  };
@@ -204,6 +299,7 @@ export const WebBox = forwardRef<HTMLDivElement, Record<string, unknown> & { chi
204
299
  data-gloom-hover-bg={hoverBackgroundColor ? "true" : undefined}
205
300
  ref={elementRef}
206
301
  onMouseDownCapture={typeof props.onMouseDownCapture === "function" ? handleMouseDownCapture : undefined}
302
+ onPointerDown={handlesDrag ? handlePointerDown : undefined}
207
303
  onMouseDown={handleMouseDown}
208
304
  onMouseOver={handleMouseOver}
209
305
  onMouseMove={(event) => scheduleFrameMouseHandler(event, "move")}
@@ -0,0 +1,35 @@
1
+ let shield: HTMLElement | null = null;
2
+
3
+ /**
4
+ * While a press drags, one transparent sheet covers the window: it keeps the
5
+ * grabbing cursor wherever the pointer goes and takes the hover, so nothing
6
+ * under the pointer restyles while a pane, divider or chart moves across it.
7
+ * It is a single element on purpose: a class on body matched by descendant
8
+ * rules restyled every element with a role (thousands on a busy layout) in
9
+ * the frame the drag began, and again when it ended.
10
+ *
11
+ * A press on a resize edge (a pane's corner, a split) keeps its resize cursor
12
+ * for the drag instead, as a window edge does.
13
+ */
14
+ export function showDragShield(pressed?: Element | null): void {
15
+ document.body.classList.add("gloom-dragging");
16
+ if (!shield?.isConnected) {
17
+ shield = document.createElement("div");
18
+ shield.setAttribute("data-gloom-role", "drag-shield");
19
+ shield.setAttribute("aria-hidden", "true");
20
+ document.body.appendChild(shield);
21
+ }
22
+ shield.style.cursor = resizeCursor(pressed);
23
+ }
24
+
25
+ function resizeCursor(element: Element | null | undefined): string {
26
+ if (!element || typeof getComputedStyle !== "function") return "";
27
+ const cursor = getComputedStyle(element).cursor;
28
+ return cursor.endsWith("-resize") ? cursor : "";
29
+ }
30
+
31
+ export function hideDragShield(): void {
32
+ document.body.classList.remove("gloom-dragging");
33
+ shield?.remove();
34
+ shield = null;
35
+ }
@@ -0,0 +1,62 @@
1
+ import type { CSSProperties } from "react";
2
+ import type { LiveBoxFrame } from "../../../ui/host";
3
+ import { WEB_CELL_HEIGHT, WEB_CELL_WIDTH } from "../../../theme/font-scale";
4
+ import { commonStyle } from "./style";
5
+
6
+ /**
7
+ * Blink lays the whole page out into compositor layers again whenever a
8
+ * style transform changes, even on a `will-change` layer: 4 to 9 ms a frame
9
+ * on a ten-pane desktop. The same transform held by a running animation's
10
+ * keyframes only updates the compositor (under 1 ms), at the same latency.
11
+ * WebKit has no such pass, and there an animation per frame costs more than
12
+ * the plain style, so it keeps the style.
13
+ */
14
+ const ANIMATION_CARRIES_SLIDE = typeof navigator !== "undefined"
15
+ && /Chrome\//.test(navigator.userAgent)
16
+ && typeof Element !== "undefined"
17
+ && typeof Element.prototype.animate === "function";
18
+
19
+ const slides = new WeakMap<HTMLElement, Animation>();
20
+
21
+ /** A style value as React writes it: a bare number is pixels. */
22
+ function renderedValue(value: unknown): string {
23
+ if (typeof value === "number") return `${value}px`;
24
+ return typeof value === "string" ? value : "";
25
+ }
26
+
27
+ function slide(element: HTMLElement, transform: string): void {
28
+ if (!ANIMATION_CARRIES_SLIDE) {
29
+ element.style.transform = transform;
30
+ return;
31
+ }
32
+ const keyframes = [{ transform }, { transform }];
33
+ const running = slides.get(element);
34
+ if (running) (running.effect as KeyframeEffect).setKeyframes(keyframes);
35
+ else slides.set(element, element.animate(keyframes, { duration: 1e9, fill: "both" }));
36
+ }
37
+
38
+ /**
39
+ * Draws a box where a drag has it this frame by restyling its element,
40
+ * outside React: an offset on its own compositor layer, with no layout or
41
+ * repaint, and a size, which lays out only the box's subtree. `null` hands
42
+ * the properties back to what React last rendered.
43
+ */
44
+ export function applyLiveFrame(element: HTMLElement, frame: LiveBoxFrame | null, props: Record<string, unknown>): void {
45
+ const style = element.style;
46
+ if (!frame) {
47
+ const rendered: CSSProperties = { ...commonStyle(props), ...(props.style as CSSProperties | undefined) };
48
+ slides.get(element)?.cancel();
49
+ slides.delete(element);
50
+ style.transform = renderedValue(rendered.transform);
51
+ style.willChange = renderedValue(rendered.willChange);
52
+ style.width = renderedValue(rendered.width);
53
+ style.height = renderedValue(rendered.height);
54
+ return;
55
+ }
56
+ if (frame.width !== undefined) style.width = `${frame.width * WEB_CELL_WIDTH}px`;
57
+ if (frame.height !== undefined) style.height = `${frame.height * WEB_CELL_HEIGHT}px`;
58
+ if (frame.dx === undefined && frame.dy === undefined) return;
59
+ // Promoted once, before the first frame of the drag.
60
+ if (style.willChange !== "transform") style.willChange = "transform";
61
+ slide(element, `translate3d(${(frame.dx ?? 0) * WEB_CELL_WIDTH}px, ${(frame.dy ?? 0) * WEB_CELL_HEIGHT}px, 0)`);
62
+ }
@@ -54,7 +54,12 @@ function resolveScroll(event: MouseLikeEvent) {
54
54
  };
55
55
  }
56
56
 
57
- export function cellMouseEvent(event: MouseLikeEvent, type?: string) {
57
+ /**
58
+ * `ahead` is where the browser predicts the pointer will be a moment later
59
+ * (`getPredictedEvents`), for drawing what follows it; hit tests and releases
60
+ * keep the real point.
61
+ */
62
+ export function cellMouseEvent(event: MouseLikeEvent, type?: string, ahead?: { clientX: number; clientY: number } | null) {
58
63
  const preciseX = event.clientX / WEB_CELL_WIDTH;
59
64
  const preciseY = event.clientY / WEB_CELL_HEIGHT;
60
65
  return {
@@ -63,6 +68,8 @@ export function cellMouseEvent(event: MouseLikeEvent, type?: string) {
63
68
  y: Math.max(0, Math.floor(preciseY)),
64
69
  preciseX: Math.max(0, preciseX),
65
70
  preciseY: Math.max(0, preciseY),
71
+ predictedX: ahead ? Math.max(0, ahead.clientX / WEB_CELL_WIDTH) : undefined,
72
+ predictedY: ahead ? Math.max(0, ahead.clientY / WEB_CELL_HEIGHT) : undefined,
66
73
  pixelX: event.clientX,
67
74
  pixelY: event.clientY,
68
75
  button: event.button,
@@ -13,6 +13,7 @@ import {
13
13
  } from "react";
14
14
  import type { ScrollBoxRenderable } from "../../../ui/host";
15
15
  import { WEB_CELL_HEIGHT, WEB_CELL_WIDTH } from "../../../theme/font-scale";
16
+ import { isHeaderStripScrollBox, scrollBoxScrollsVertically } from "../../../ui/scroll-box-axis";
16
17
  import { callMouseHandler } from "./mouse";
17
18
  import { cleanDomProps, commonStyle } from "./style";
18
19
  import { useScrollbarActivity } from "../scrollbar-activity";
@@ -48,15 +49,15 @@ function contentInsetStyle(props: Record<string, unknown>, contentOptions: unkno
48
49
  export const WebScrollBox = forwardRef<ScrollBoxRenderable, Record<string, unknown> & { children?: ReactNode }>(
49
50
  function WebScrollBox({ children, contentOptions, ...props }, ref) {
50
51
  const elementRef = useRef<HTMLDivElement | null>(null);
51
- const isHeaderLikeScroller = props.scrollX === true && props.scrollY !== true && props.height === 1;
52
+ const isHeaderLikeScroller = isHeaderStripScrollBox(props);
52
53
  /**
53
54
  * OpenTUI's ScrollBox scrolls vertically unless told otherwise, so this one
54
55
  * has to as well: a pane that renders a ScrollBox and nothing more expects
55
- * the wheel to work, and used to get an unscrollable box that only its own
56
- * arrow-key handler could move. The one-row horizontal strips (tab bars,
57
- * table headers, chart legends) are the exception and scroll on their axis.
56
+ * the wheel and the arrow keys to work (the pane keyboard scroll registry
57
+ * reads the same rule). The one-row horizontal strips (tab bars, table
58
+ * headers, chart legends) are the exception and scroll on their axis.
58
59
  */
59
- const scrollYEnabled = props.scrollY !== false && !isHeaderLikeScroller;
60
+ const scrollYEnabled = scrollBoxScrollsVertically(props);
60
61
  const [horizontalScrollBarVisible, setHorizontalScrollBarVisible] = useState(
61
62
  () => props.scrollX === true && !isHeaderLikeScroller,
62
63
  );
@@ -13,6 +13,7 @@ import { WebMenu } from "../desktop/menu";
13
13
  import { WebPopover } from "../desktop/popover";
14
14
  import { useHorizontalOverflow } from "./overflow-fade";
15
15
  import { useTopSurfaceColor } from "./top-surface";
16
+ import { hideDragShield, showDragShield } from "./drag-shield";
16
17
 
17
18
  type CssVars = CSSProperties & Record<`--${string}`, string>;
18
19
 
@@ -106,7 +107,7 @@ export function WebTabs({
106
107
  const cleanup = () => {
107
108
  document.removeEventListener("mousemove", handleMove);
108
109
  document.removeEventListener("mouseup", handleUp);
109
- document.body.classList.remove("gloom-dragging");
110
+ hideDragShield();
110
111
  dragCleanupRef.current = null;
111
112
  dragOffsetXRef.current = 0;
112
113
  dragSourceWidthRef.current = 0;
@@ -120,7 +121,7 @@ export function WebTabs({
120
121
  moveEvent.preventDefault();
121
122
  dragOffsetXRef.current = offsetX;
122
123
  sourceElement.style.transform = `translateX(${offsetX}px) scale(1.03)`;
123
- document.body.classList.add("gloom-dragging");
124
+ showDragShield();
124
125
  setDragTargetValue(resolveTarget(sourceBounds.left + offsetX + sourceBounds.width / 2));
125
126
  };
126
127
  const handleUp = (upEvent: MouseEvent) => {
@@ -1,8 +1,10 @@
1
1
  /** @jsxImportSource react */
2
2
  import { useEffect, useMemo, useSyncExternalStore, type ReactNode } from "react";
3
+ import { flushSync } from "react-dom";
3
4
  import {
4
5
  createShortcutRegistry,
5
6
  InputHostProvider,
7
+ useRegisteredBeforeShortcut,
6
8
  useRegisteredShortcut,
7
9
  type InputHost,
8
10
  type KeyEventLike,
@@ -76,7 +78,7 @@ function getViewport() {
76
78
  }
77
79
 
78
80
  export function WebInputHostProvider({ children }: { children: ReactNode }) {
79
- const shortcutRegistry = useMemo(() => createShortcutRegistry(), []);
81
+ const shortcutRegistry = useMemo(() => createShortcutRegistry({ flushSync }), []);
80
82
 
81
83
  useEffect(() => {
82
84
  // Windows fires the Menu key's contextmenu on keyup. When the app used the
@@ -144,6 +146,9 @@ export function WebInputHostProvider({ children }: { children: ReactNode }) {
144
146
  useShortcut(handler, options) {
145
147
  useRegisteredShortcut(shortcutRegistry, handler, options);
146
148
  },
149
+ useBeforeShortcut(listener) {
150
+ useRegisteredBeforeShortcut(shortcutRegistry, listener);
151
+ },
147
152
  useViewport() {
148
153
  return useSyncExternalStore(subscribeViewport, getViewport, getViewport);
149
154
  },
@@ -159,10 +159,21 @@ button, [data-gloom-interactive="true"] { cursor: pointer; }
159
159
  [data-gloom-role="pane-restore"] {
160
160
  cursor: pointer;
161
161
  }
162
- body.gloom-dragging,
163
- body.gloom-dragging [data-gloom-role="pane-header"] {
162
+ /* Up for the length of a drag (see drag-shield.ts): the grabbing cursor
163
+ everywhere, and no hover under the pointer. */
164
+ [data-gloom-role="drag-shield"] {
165
+ position: fixed;
166
+ inset: 0;
167
+ z-index: 2147483647;
164
168
  cursor: grabbing;
165
169
  }
170
+ /* Surfaces that drag a pane, its size or a split keep the whole pointer
171
+ stream: a touch or pen press on them never starts a pan or zoom. */
172
+ [data-gloom-role="pane-header"][data-drag-surface="pane"],
173
+ [data-gloom-role="resize-handle"],
174
+ [data-gloom-role="dock-divider"] {
175
+ touch-action: none;
176
+ }
166
177
  [data-gloom-role="pane-title"] {
167
178
  overflow: hidden;
168
179
  text-overflow: ellipsis;
@@ -310,9 +321,6 @@ body.gloom-dragging [data-gloom-role="pane-header"] {
310
321
  [data-gloom-role="chart-surface"] {
311
322
  cursor: crosshair;
312
323
  }
313
- body.gloom-dragging [data-gloom-role="chart-surface"] {
314
- cursor: grabbing;
315
- }
316
324
  [data-gloom-role="tab-list"] {
317
325
  scrollbar-width: none;
318
326
  -webkit-overflow-scrolling: touch;
@@ -745,6 +753,19 @@ html[data-gloom-screenshot="true"] [data-gloom-role="chart-watermark"] {
745
753
  box-shadow: inset 1px 0 0 var(--qb-hairline);
746
754
  }
747
755
  .gloom-qb-inline .gloom-qb-view { margin-left: 0; box-shadow: none; align-self: stretch; }
756
+ /* A long inline choice (an expiry strip) scrolls by itself, as the terminal's
757
+ does, so its label and the controls beside it stay in view. */
758
+ .gloom-qb-filters[data-strip="true"],
759
+ .gloom-qb-inline[data-strip="true"] { flex: 0 1 auto; min-width: 0; }
760
+ .gloom-qb-inline[data-strip="true"] .gloom-qb-segments {
761
+ flex: 0 1 auto;
762
+ min-width: 12ch;
763
+ overflow-x: auto;
764
+ scrollbar-width: none;
765
+ }
766
+ .gloom-qb-inline[data-strip="true"] .gloom-qb-segments::-webkit-scrollbar { display: none; }
767
+ .gloom-qb-inline[data-strip="true"] .gloom-qb-segments > button { flex: none; white-space: nowrap; }
768
+ .gloom-qb-filters[data-strip="true"] > :not([data-strip="true"]) { flex: none; }
748
769
  .gloom-qb-view button {
749
770
  height: 100%;
750
771
  padding: 0 8px;
@@ -6,6 +6,7 @@ import {
6
6
  saveConfig,
7
7
  } from "../../../../data/config/store";
8
8
  import type { UpdateProgress } from "../../../../updater";
9
+ import { deleteBrokerAccountRecord, saveBrokerAccountRecord } from "../../../../brokers/account-cache";
9
10
  import type {
10
11
  DesktopBackendRequestResponse,
11
12
  DesktopCoreRequest,
@@ -105,6 +106,12 @@ export async function handleDesktopBackendRequest(
105
106
  case "session.delete":
106
107
  backend.requireServices().persistence.sessions.delete(request.payload.sessionId);
107
108
  return null;
109
+ case "resources.set":
110
+ saveBrokerAccountRecord(backend.requireServices().persistence.resources, request.payload.record);
111
+ return null;
112
+ case "resources.delete":
113
+ deleteBrokerAccountRecord(backend.requireServices().persistence.resources, request.payload.key);
114
+ return null;
108
115
  default: {
109
116
  const exhaustive: never = request;
110
117
  throw new Error(`Unknown core backend method: ${String(exhaustive)}`);
@@ -105,6 +105,8 @@ export class DesktopBackend {
105
105
  getWindowKey: this.rpcs.getRpcWindowKey,
106
106
  });
107
107
  readonly detachedWindows = new DesktopDetachedWindowManager(this);
108
+ /** Each pane type's plugin, for hiding popped-out windows with their plugin. */
109
+ paneOwners: ReadonlyMap<string, string> = new Map();
108
110
 
109
111
  private crashReporterInstalled = false;
110
112
  private restartInProgress = false;
@@ -162,9 +164,11 @@ export class DesktopBackend {
162
164
  async startServices(nextConfig: AppConfig): Promise<{ config: AppConfig; services: AppServices }> {
163
165
  this.setConfig(nextConfig);
164
166
  const config = this.requireConfig();
167
+ const { paneOwners, ...backendPlugins } = await loadDesktopBackendPlugins();
168
+ this.paneOwners = paneOwners;
165
169
  const services = createAppServices({
166
170
  config,
167
- ...await loadDesktopBackendPlugins(),
171
+ ...backendPlugins,
168
172
  });
169
173
  this.services = services;
170
174
  await services.ready;
@@ -511,6 +515,8 @@ export class DesktopBackend {
511
515
  case "config.import":
512
516
  case "session.set":
513
517
  case "session.delete":
518
+ case "resources.set":
519
+ case "resources.delete":
514
520
  return handleDesktopBackendRequest(this, rpc, request);
515
521
  default: {
516
522
  const exhaustive: never = request;
@@ -73,7 +73,18 @@ export class DesktopDetachedWindowManager {
73
73
  const config = this.backend.config;
74
74
  if (!config) return;
75
75
 
76
- const desiredEntries = new Map(config.layout.detached.map((entry) => [entry.instanceId, entry] as const));
76
+ // A pane whose plugin is switched off stays in the layout, out of sight,
77
+ // as it does in the main window: its window closes without undocking or
78
+ // removing it, and opens again where it was when the plugin is back on.
79
+ const disabledPlugins = new Set(config.disabledPlugins);
80
+ const hidden = (instanceId: string) => {
81
+ const paneType = findPaneInstance(config.layout, instanceId)?.paneId;
82
+ const owner = paneType ? this.backend.paneOwners.get(paneType) : undefined;
83
+ return !!owner && disabledPlugins.has(owner);
84
+ };
85
+ const desiredEntries = new Map(config.layout.detached
86
+ .filter((entry) => !hidden(entry.instanceId))
87
+ .map((entry) => [entry.instanceId, entry] as const));
77
88
  for (const [instanceId, entry] of desiredEntries) {
78
89
  const existingWindow = this.windows.get(instanceId);
79
90
  if (!existingWindow) {
@@ -1,5 +1,5 @@
1
1
  import { existsSync, mkdirSync } from "fs";
2
- import { homedir } from "os";
2
+ import { homedir, hostname } from "os";
3
3
  import { getGloomberbDirs } from "../../../../data/config/home";
4
4
  import type { AppServices } from "../../../../core/app-services";
5
5
  import { restoreExtractedPlugins } from "../../../../cli/restore-plugins";
@@ -8,6 +8,8 @@ import {
8
8
  initDataDir,
9
9
  } from "../../../../data/config/store";
10
10
  import type { AppConfig } from "../../../../types/config";
11
+ import { listPersistedBrokerAccountRecords } from "../../../../brokers/account-cache";
12
+ import type { CachedResourceRecord } from "../../../../data/resource-store";
11
13
  import {
12
14
  loadDesktopPluginState,
13
15
  } from "./plugin-state";
@@ -54,6 +56,16 @@ export function desktopRendererCapabilityManifests(registry: CapabilityRegistry)
54
56
  return registry.manifests({ rendererOnly: true, includeDisabled: true });
55
57
  }
56
58
 
59
+ /** The view's resource store lives in memory; it starts from the account snapshots saved here. */
60
+ function loadSavedResources(services: AppServices, config: AppConfig): CachedResourceRecord[] {
61
+ try {
62
+ return listPersistedBrokerAccountRecords(services.persistence.resources, config.brokerInstances);
63
+ } catch (error) {
64
+ console.warn("saved broker accounts could not be read", error);
65
+ return [];
66
+ }
67
+ }
68
+
57
69
  function buildInitializationPayload(
58
70
  backend: DesktopBackend,
59
71
  config: AppConfig,
@@ -66,8 +78,10 @@ function buildInitializationPayload(
66
78
  desktopSnapshot: backend.workspace?.getSnapshot() ?? null,
67
79
  desktopThemePreview: backend.stateBroadcaster.currentThemePreview,
68
80
  pluginState: loadDesktopPluginState(services.pluginRegistry),
81
+ savedResources: loadSavedResources(services, config),
69
82
  capabilityManifests: desktopRendererCapabilityManifests(services.pluginRegistry.capabilities),
70
83
  desktopPlatform: process.platform,
84
+ hostName: hostname(),
71
85
  windowKind: windowTarget.kind,
72
86
  paneId: windowTarget.paneId,
73
87
  telemetry: {