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
@@ -160,15 +160,16 @@ function shouldHideCursor({
160
160
  }
161
161
 
162
162
  export function useShellCursorOcclusionGuard({
163
- occlusionRects,
163
+ resolveOcclusionRects,
164
164
  shellRef,
165
165
  }: {
166
- occlusionRects: readonly ShellCursorOcclusionRect[];
166
+ /** Called on every terminal frame. */
167
+ resolveOcclusionRects: () => readonly ShellCursorOcclusionRect[];
167
168
  shellRef: RefObject<unknown>;
168
169
  }) {
169
170
  const nativeRenderer = useNativeRenderer();
170
- const stateRef = useRef({ occlusionRects, shellRef });
171
- stateRef.current = { occlusionRects, shellRef };
171
+ const stateRef = useRef({ resolveOcclusionRects, shellRef });
172
+ stateRef.current = { resolveOcclusionRects, shellRef };
172
173
 
173
174
  useEffect(() => {
174
175
  if (
@@ -186,7 +187,7 @@ export function useShellCursorOcclusionGuard({
186
187
  const shellBounds = readRenderableBounds(stateRef.current.shellRef.current);
187
188
  if (!shellBounds) return;
188
189
 
189
- const terminalRects = stateRef.current.occlusionRects.map((rect) => toTerminalRect(shellBounds, rect));
190
+ const terminalRects = stateRef.current.resolveOcclusionRects().map((rect) => toTerminalRect(shellBounds, rect));
190
191
  if (terminalRects.length === 0) return;
191
192
 
192
193
  const cursorOwnerPaneId = readRenderablePaneId(nativeRenderer.currentFocusedEditor);
@@ -63,6 +63,10 @@ const DOCK_DIVIDER_SIZE = 1;
63
63
  export const PANE_DRAG_THRESHOLD = 2;
64
64
  export const PRECISE_PANE_DRAG_THRESHOLD = 0.15;
65
65
 
66
+ export function sameRect(a: LayoutBounds, b: LayoutBounds): boolean {
67
+ return a.x === b.x && a.y === b.y && a.width === b.width && a.height === b.height;
68
+ }
69
+
66
70
  export function pointInRect(rect: LayoutBounds, x: number, y: number): boolean {
67
71
  return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;
68
72
  }
@@ -212,6 +216,16 @@ function makeOverlayCellRects(rect: LayoutBounds): HoverOverlay["cells"] {
212
216
  return cells;
213
217
  }
214
218
 
219
+ /** The drop grid drawn over a docked pane while another pane is dragged onto it. */
220
+ export function hoverOverlayForLeaf(leaf: DockLeafLayout): HoverOverlay {
221
+ const overlayRect = compactOverlayRect(leaf.rect);
222
+ return {
223
+ targetId: leaf.instanceId,
224
+ rect: overlayRect,
225
+ cells: makeOverlayCellRects(overlayRect),
226
+ };
227
+ }
228
+
215
229
  export function resolveHoverOverlay(
216
230
  x: number,
217
231
  y: number,
@@ -220,13 +234,8 @@ export function resolveHoverOverlay(
220
234
  ): HoverOverlay | null {
221
235
  const targetLeaf = leaves.find((leaf) => leaf.instanceId !== draggedPaneId && pointInRect(leaf.rect, x, y));
222
236
  if (!targetLeaf) return null;
223
- const overlayRect = compactOverlayRect(targetLeaf.rect);
224
- if (!pointInRect(overlayRect, x, y)) return null;
225
- return {
226
- targetId: targetLeaf.instanceId,
227
- rect: overlayRect,
228
- cells: makeOverlayCellRects(overlayRect),
229
- };
237
+ const overlay = hoverOverlayForLeaf(targetLeaf);
238
+ return pointInRect(overlay.rect, x, y) ? overlay : null;
230
239
  }
231
240
 
232
241
  export function resolveDividerPreviewRect(
@@ -0,0 +1,193 @@
1
+ import { useCallback, useLayoutEffect, useMemo, useRef, useState, useSyncExternalStore, type RefObject } from "react";
2
+ import type { DockLeafLayout, FloatingRect } from "../../../../layout/pane-manager";
3
+ import type { BoxRenderable, LiveBoxFrame } from "../../../../ui";
4
+ import type { DividerPreviewState } from "../native/window-state";
5
+ import { hoverOverlayForLeaf, sameRect, type DragPreview, type HoverOverlay } from "./index";
6
+
7
+ /**
8
+ * Where an in-progress drag is right now: the pane on the move, its floating
9
+ * rect, the divider line and the drop target. It lives outside React state,
10
+ * so the shell renders nothing while a drag follows the pointer.
11
+ */
12
+ export interface LiveDragGeometry {
13
+ /** The pane being moved, from mouse down to release. */
14
+ paneDrag: { paneId: string; mode: "docked" | "floating" } | null;
15
+ /** The floating pane being resized, from mouse down to release. */
16
+ paneResize: { paneId: string } | null;
17
+ /** The floating rect being moved or resized, already kept inside the shell. */
18
+ floating: { paneId: string; rect: FloatingRect } | null;
19
+ divider: DividerPreviewState | null;
20
+ /** The tiled pane whose drop grid is under the pointer during a pane move. */
21
+ hoverTargetId: string | null;
22
+ /** Where the release would dock or snap. */
23
+ dockPreview: DragPreview | null;
24
+ }
25
+
26
+ /**
27
+ * Two kinds of change. `set` is something React draws: a drag starting or
28
+ * ending, or a new drop target; it reaches every subscriber. `move` is the
29
+ * desktop following the pointer: it reaches only the elements that restyle
30
+ * themselves to follow it (`subscribeMotion`), so a move renders nothing. The
31
+ * terminal redraws cells, so it sends every change through `set`.
32
+ */
33
+ export interface LiveDragStore {
34
+ get(): LiveDragGeometry;
35
+ set(patch: Partial<LiveDragGeometry>): void;
36
+ move(patch: Partial<LiveDragGeometry>): void;
37
+ subscribe(listener: () => void): () => void;
38
+ subscribeMotion(listener: () => void): () => void;
39
+ }
40
+
41
+ export const IDLE_DRAG: LiveDragGeometry = {
42
+ paneDrag: null,
43
+ paneResize: null,
44
+ floating: null,
45
+ divider: null,
46
+ hoverTargetId: null,
47
+ dockPreview: null,
48
+ };
49
+
50
+ export function createLiveDragStore(): LiveDragStore {
51
+ let geometry = IDLE_DRAG;
52
+ const listeners = new Set<() => void>();
53
+ const motionListeners = new Set<() => void>();
54
+ const apply = (patch: Partial<LiveDragGeometry>): boolean => {
55
+ let changed = false;
56
+ for (const key of Object.keys(patch) as Array<keyof LiveDragGeometry>) {
57
+ if (patch[key] !== geometry[key]) changed = true;
58
+ }
59
+ if (changed) geometry = { ...geometry, ...patch };
60
+ return changed;
61
+ };
62
+ const notify = (targets: Set<() => void>) => {
63
+ for (const listener of targets) listener();
64
+ };
65
+ return {
66
+ get: () => geometry,
67
+ set(patch) {
68
+ if (!apply(patch)) return;
69
+ notify(motionListeners);
70
+ notify(listeners);
71
+ },
72
+ move(patch) {
73
+ if (apply(patch)) notify(motionListeners);
74
+ },
75
+ subscribe(listener) {
76
+ listeners.add(listener);
77
+ return () => { listeners.delete(listener); };
78
+ },
79
+ subscribeMotion(listener) {
80
+ motionListeners.add(listener);
81
+ return () => { motionListeners.delete(listener); };
82
+ },
83
+ };
84
+ }
85
+
86
+ /** Re-renders the caller only when `select` returns something new; keep its result stable. */
87
+ export function useLiveDrag<T>(store: LiveDragStore, select: (geometry: LiveDragGeometry) => T): T {
88
+ const getSnapshot = useCallback(() => select(store.get()), [select, store]);
89
+ return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
90
+ }
91
+
92
+ function sameFrame(a: LiveBoxFrame | null, b: LiveBoxFrame | null): boolean {
93
+ if (a === null || b === null) return a === b;
94
+ return a.dx === b.dx && a.dy === b.dy && a.width === b.width && a.height === b.height;
95
+ }
96
+
97
+ /**
98
+ * Desktop: keeps `box` where the drag has it by restyling its element on every
99
+ * move (`setLiveFrame`), with no render. `frameOf` must be stable while its
100
+ * inputs are; a new one redraws the box once. A render of the caller during
101
+ * the drag may write the box's laid-out size again, so it is drawn back after
102
+ * every one.
103
+ */
104
+ export function useLiveBoxFrame(
105
+ store: LiveDragStore,
106
+ box: RefObject<BoxRenderable | null>,
107
+ frameOf: ((geometry: LiveDragGeometry) => LiveBoxFrame | null) | null,
108
+ ): void {
109
+ // What the element carries now, across re-subscriptions.
110
+ const drawn = useRef<LiveBoxFrame | null>(null);
111
+ useLayoutEffect(() => {
112
+ if (!frameOf) return;
113
+ const follow = () => {
114
+ const next = frameOf(store.get());
115
+ if (sameFrame(drawn.current, next)) return;
116
+ drawn.current = next;
117
+ box.current?.setLiveFrame?.(next);
118
+ };
119
+ follow();
120
+ return store.subscribeMotion(follow);
121
+ }, [box, frameOf, store]);
122
+ useLayoutEffect(() => {
123
+ if (drawn.current) box.current?.setLiveFrame?.(drawn.current);
124
+ });
125
+ }
126
+
127
+ /** How often a pane being resized on the desktop draws its content at the new size. */
128
+ const RESIZE_CONTENT_INTERVAL_MS = 100;
129
+
130
+ /**
131
+ * Desktop: the rect a floating pane being resized draws its content at. Its
132
+ * frame follows every move on its own (`useLiveBoxFrame`); the content, which
133
+ * charts and tables lay out for one size, catches up with it at most every
134
+ * `RESIZE_CONTENT_INTERVAL_MS`, and once more after the last move. Null when
135
+ * the pane is not being resized, or has not changed size yet.
136
+ */
137
+ export function useLiveResizeRect(store: LiveDragStore, paneId: string, enabled: boolean): FloatingRect | null {
138
+ const [rect, setRect] = useState<FloatingRect | null>(null);
139
+ // What `rect` holds, so a move never queues a state update that changes nothing.
140
+ const shown = useRef<FloatingRect | null>(null);
141
+ useLayoutEffect(() => {
142
+ if (!enabled) return;
143
+ let timer: ReturnType<typeof setTimeout> | null = null;
144
+ // The size the content was last drawn at in this resize; the press's own first.
145
+ let drawn: FloatingRect | null = null;
146
+ const show = (next: FloatingRect | null) => {
147
+ if (shown.current === next) return;
148
+ shown.current = next;
149
+ setRect(next);
150
+ };
151
+ const resizing = () => {
152
+ const { paneResize, floating } = store.get();
153
+ return paneResize?.paneId === paneId && floating?.paneId === paneId ? floating.rect : null;
154
+ };
155
+ const catchUp = () => {
156
+ timer = null;
157
+ const next = resizing();
158
+ if (!next || (drawn && sameRect(next, drawn))) return;
159
+ drawn = next;
160
+ show(next);
161
+ };
162
+ const follow = () => {
163
+ const next = resizing();
164
+ if (!next) {
165
+ // Released or put back: the layout's rect draws it, in the same commit.
166
+ if (timer !== null) clearTimeout(timer);
167
+ timer = null;
168
+ drawn = null;
169
+ show(null);
170
+ return;
171
+ }
172
+ if (!drawn) drawn = next;
173
+ else if (timer === null && !sameRect(next, drawn)) timer = setTimeout(catchUp, RESIZE_CONTENT_INTERVAL_MS);
174
+ };
175
+ const unsubscribe = store.subscribeMotion(follow);
176
+ return () => {
177
+ unsubscribe();
178
+ if (timer !== null) clearTimeout(timer);
179
+ };
180
+ }, [enabled, paneId, store]);
181
+ return enabled ? rect : null;
182
+ }
183
+
184
+ /** The drop grid under the pointer during a pane move. Re-renders only when the pointer enters another pane's grid. */
185
+ export function useLiveHoverOverlay(store: LiveDragStore, leaves: DockLeafLayout[]): HoverOverlay | null {
186
+ const targetId = useLiveDrag(store, selectHoverTarget);
187
+ return useMemo(() => {
188
+ const leaf = targetId ? leaves.find((entry) => entry.instanceId === targetId) : undefined;
189
+ return leaf ? hoverOverlayForLeaf(leaf) : null;
190
+ }, [leaves, targetId]);
191
+ }
192
+
193
+ const selectHoverTarget = (geometry: LiveDragGeometry) => (geometry.paneDrag ? geometry.hoverTargetId : null);
@@ -1,28 +1,76 @@
1
- import { Box } from "../../../../ui";
1
+ import { useRef, type CSSProperties, type RefObject } from "react";
2
+ import { Box, useUiCapabilities, type BoxRenderable, type LiveBoxFrame } from "../../../../ui";
2
3
  import { colors } from "../../../../theme/colors";
3
- import type { FloatingRect } from "../../../../layout/pane-manager";
4
+ import type { DockLeafLayout, LayoutBounds } from "../../../../layout/pane-manager";
4
5
  import type { DragPreview, HoverOverlay } from "./index";
6
+ import { useLiveBoxFrame, useLiveDrag, useLiveHoverOverlay, type LiveDragGeometry, type LiveDragStore } from "./live";
7
+ import { placeStyle } from "./slide";
5
8
 
6
- export function ShellDragOverlays({
7
- activeHoverOverlay,
8
- activePaneDrag,
9
- dockPreview,
10
- dragFloatingRect,
11
- effectiveDockPreview,
9
+ const CELL_POSITIONS = ["top", "left", "center", "right", "bottom"] as const;
10
+
11
+ function selectDockedOutline(geometry: LiveDragGeometry) {
12
+ const { paneDrag, floating } = geometry;
13
+ return paneDrag?.mode === "docked" && floating?.paneId === paneDrag.paneId ? floating.rect : null;
14
+ }
15
+
16
+ const selectDockPreview = (geometry: LiveDragGeometry) => geometry.dockPreview;
17
+ const selectPaneDragActive = (geometry: LiveDragGeometry) => geometry.paneDrag !== null;
18
+ const selectOutlineWidth = (geometry: LiveDragGeometry) => selectDockedOutline(geometry)?.width ?? null;
19
+ const selectOutlineHeight = (geometry: LiveDragGeometry) => selectDockedOutline(geometry)?.height ?? null;
20
+
21
+ function isActiveCell(dockPreview: DragPreview | null, overlay: HoverOverlay, position: string): boolean {
22
+ return dockPreview?.kind === "dock"
23
+ && dockPreview.target.kind === "leaf"
24
+ && dockPreview.target.targetId === overlay.targetId
25
+ && dockPreview.target.position === position;
26
+ }
27
+
28
+ export function ShellDragOverlays(props: {
29
+ dockLeafLayouts: DockLeafLayout[];
30
+ /** A pane from another window over this one's edge. */
31
+ externalDockPreview: DragPreview | null;
32
+ live: LiveDragStore;
33
+ }) {
34
+ const { nativePaneChrome } = useUiCapabilities();
35
+ return nativePaneChrome ? <DesktopDragOverlays {...props} /> : <TerminalDragOverlays {...props} />;
36
+ }
37
+
38
+ /** A docked pane on the move is drawn as an outline at its floating size, until it is over a drop target. */
39
+ function TerminalDockedDragOutline({ live }: { live: LiveDragStore }) {
40
+ const rect = useLiveDrag(live, selectDockedOutline);
41
+ if (!rect) return null;
42
+ return (
43
+ <Box
44
+ position="absolute"
45
+ left={rect.x}
46
+ top={rect.y}
47
+ width={rect.width}
48
+ height={rect.height}
49
+ border
50
+ borderStyle="single"
51
+ borderColor={colors.borderFocused}
52
+ backgroundColor={colors.panel}
53
+ zIndex={95}
54
+ />
55
+ );
56
+ }
57
+
58
+ function TerminalDragOverlays({
59
+ dockLeafLayouts,
60
+ externalDockPreview,
61
+ live,
12
62
  }: {
13
- activeHoverOverlay: HoverOverlay | null;
14
- activePaneDrag: { paneId: string; mode: "docked" | "floating" } | null;
15
- dockPreview: DragPreview | null;
16
- dragFloatingRect: { paneId: string; rect: FloatingRect } | null;
17
- effectiveDockPreview: DragPreview | null;
63
+ dockLeafLayouts: DockLeafLayout[];
64
+ externalDockPreview: DragPreview | null;
65
+ live: LiveDragStore;
18
66
  }) {
67
+ const activeHoverOverlay = useLiveHoverOverlay(live, dockLeafLayouts);
68
+ const dockPreview = useLiveDrag(live, selectDockPreview);
69
+ const effectiveDockPreview = dockPreview ?? externalDockPreview;
19
70
  return (
20
71
  <>
21
72
  {activeHoverOverlay && activeHoverOverlay.cells.map((cell) => {
22
- const active = dockPreview?.kind === "dock"
23
- && dockPreview.target.kind === "leaf"
24
- && dockPreview.target.targetId === activeHoverOverlay.targetId
25
- && dockPreview.target.position === cell.position;
73
+ const active = isActiveCell(dockPreview, activeHoverOverlay, cell.position);
26
74
  return (
27
75
  <Box
28
76
  key={`cell:${activeHoverOverlay.targetId}:${cell.position}`}
@@ -40,24 +88,7 @@ export function ShellDragOverlays({
40
88
  );
41
89
  })}
42
90
 
43
- {activePaneDrag
44
- && activePaneDrag.mode === "docked"
45
- && dragFloatingRect?.paneId === activePaneDrag.paneId
46
- && !effectiveDockPreview
47
- && (
48
- <Box
49
- position="absolute"
50
- left={dragFloatingRect.rect.x}
51
- top={dragFloatingRect.rect.y}
52
- width={dragFloatingRect.rect.width}
53
- height={dragFloatingRect.rect.height}
54
- border
55
- borderStyle="single"
56
- borderColor={colors.borderFocused}
57
- backgroundColor={colors.panel}
58
- zIndex={95}
59
- />
60
- )}
91
+ {!effectiveDockPreview && <TerminalDockedDragOutline live={live} />}
61
92
 
62
93
  {effectiveDockPreview && (
63
94
  <Box
@@ -76,3 +107,116 @@ export function ShellDragOverlays({
76
107
  </>
77
108
  );
78
109
  }
110
+
111
+ /**
112
+ * Desktop: an outline laid out at the shell's corner and drawn at `rect` by a
113
+ * transform, hidden by opacity, so a new drop target restyles it in place.
114
+ */
115
+ function OutlineBox({
116
+ rect,
117
+ zIndex,
118
+ borderColor = colors.borderFocused,
119
+ backgroundColor = colors.panel,
120
+ role,
121
+ boxRef,
122
+ follows = false,
123
+ }: {
124
+ rect: LayoutBounds | null;
125
+ zIndex: number;
126
+ borderColor?: string;
127
+ backgroundColor?: string;
128
+ role: string;
129
+ boxRef?: RefObject<BoxRenderable | null>;
130
+ /** Its position follows the pointer outside React (`useLiveBoxFrame`). */
131
+ follows?: boolean;
132
+ }) {
133
+ const style: CSSProperties = {
134
+ ...(rect && !follows ? placeStyle(rect.x, rect.y) : undefined),
135
+ opacity: rect ? 1 : 0,
136
+ pointerEvents: "none",
137
+ };
138
+ return (
139
+ <Box
140
+ ref={boxRef}
141
+ position="absolute"
142
+ left={0}
143
+ top={0}
144
+ width={rect?.width ?? 0}
145
+ height={rect?.height ?? 0}
146
+ border
147
+ borderStyle="single"
148
+ borderColor={borderColor}
149
+ backgroundColor={backgroundColor}
150
+ zIndex={zIndex}
151
+ data-gloom-role={role}
152
+ style={style}
153
+ />
154
+ );
155
+ }
156
+
157
+ /**
158
+ * Desktop overlays of a pane move: the outline a docked pane drags around, the
159
+ * drop grid of the pane under the pointer and the dock or snap preview. They
160
+ * render when the drop target changes, never on a move: the outline follows
161
+ * the pointer by restyling itself.
162
+ */
163
+ function DesktopDragOverlays({
164
+ dockLeafLayouts,
165
+ externalDockPreview,
166
+ live,
167
+ }: {
168
+ dockLeafLayouts: DockLeafLayout[];
169
+ externalDockPreview: DragPreview | null;
170
+ live: LiveDragStore;
171
+ }) {
172
+ const dragging = useLiveDrag(live, selectPaneDragActive);
173
+ if (!dragging && !externalDockPreview) return null;
174
+ return <DesktopDragOverlayLayers dockLeafLayouts={dockLeafLayouts} externalDockPreview={externalDockPreview} live={live} />;
175
+ }
176
+
177
+ function DesktopDragOverlayLayers({
178
+ dockLeafLayouts,
179
+ externalDockPreview,
180
+ live,
181
+ }: {
182
+ dockLeafLayouts: DockLeafLayout[];
183
+ externalDockPreview: DragPreview | null;
184
+ live: LiveDragStore;
185
+ }) {
186
+ const activeHoverOverlay = useLiveHoverOverlay(live, dockLeafLayouts);
187
+ const dockPreview = useLiveDrag(live, selectDockPreview);
188
+ const effectiveDockPreview = dockPreview ?? externalDockPreview;
189
+ const outlineWidth = useLiveDrag(live, selectOutlineWidth);
190
+ const outlineHeight = useLiveDrag(live, selectOutlineHeight);
191
+ const outlineRef = useRef<BoxRenderable | null>(null);
192
+ useLiveBoxFrame(live, outlineRef, followDockedOutline);
193
+ const outline = outlineWidth !== null && outlineHeight !== null && !effectiveDockPreview
194
+ ? { x: 0, y: 0, width: outlineWidth, height: outlineHeight }
195
+ : null;
196
+ return (
197
+ <>
198
+ {CELL_POSITIONS.map((position) => {
199
+ const cell = activeHoverOverlay?.cells.find((entry) => entry.position === position) ?? null;
200
+ const active = !!activeHoverOverlay && isActiveCell(dockPreview, activeHoverOverlay, position);
201
+ return (
202
+ <OutlineBox
203
+ key={position}
204
+ role="drop-cell"
205
+ rect={cell?.rect ?? null}
206
+ borderColor={active ? colors.borderFocused : colors.border}
207
+ backgroundColor={active ? colors.header : colors.panel}
208
+ zIndex={position === "center" ? 98 : 97}
209
+ />
210
+ );
211
+ })}
212
+ <OutlineBox role="drag-outline" rect={outline} zIndex={95} boxRef={outlineRef} follows />
213
+ <OutlineBox role="drop-preview" rect={effectiveDockPreview?.rect ?? null} zIndex={96} />
214
+ </>
215
+ );
216
+ }
217
+
218
+ /** The docked pane's outline sits where the drag has it; laid out at the shell's corner. */
219
+ function followDockedOutline(geometry: LiveDragGeometry): LiveBoxFrame | null {
220
+ const rect = selectDockedOutline(geometry);
221
+ return rect ? { dx: rect.x, dy: rect.y } : null;
222
+ }
@@ -6,7 +6,6 @@ import {
6
6
  type MutableRefObject,
7
7
  type SetStateAction,
8
8
  } from "react";
9
- import { useRafCallback } from "../../../../react/use-raf-callback";
10
9
  import {
11
10
  type DockDividerLayout,
12
11
  type DockGeometryOptions,
@@ -19,11 +18,13 @@ import type { LayoutConfig } from "../../../../types/config";
19
18
  import {
20
19
  constrainFloatingRectToBounds,
21
20
  makeSnapGuides,
21
+ sameRect,
22
22
  type DragPreview,
23
23
  type PaneDragRectState,
24
24
  } from "./index";
25
25
  import type { ActionMenuState } from "../action-menu-overlay";
26
26
  import type { DividerPreviewState } from "../native/window-state";
27
+ import { createLiveDragStore, IDLE_DRAG, type LiveDragStore } from "./live";
27
28
  import type { WindowEditState } from "../../window-edit/mode";
28
29
  import { useShellActiveDrag } from "../active-drag";
29
30
  import { useShellNativePointerRuntime } from "../native/pointer-runtime";
@@ -58,6 +59,9 @@ export interface ShellMouseEvent {
58
59
  button?: number;
59
60
  preciseX?: number;
60
61
  preciseY?: number;
62
+ /** Desktop: where the browser predicts the pointer a moment later, for drawing only. */
63
+ predictedX?: number;
64
+ predictedY?: number;
61
65
  /** Whether a later handler has claimed this pointer interaction. */
62
66
  isDefaultPrevented?: () => boolean;
63
67
  stopPropagation: () => void;
@@ -71,88 +75,96 @@ export interface VisibleFloatingPane {
71
75
 
72
76
  export interface ShellDragRuntimeState {
73
77
  cancelActiveDrag: () => void;
74
- dividerPreview: DividerPreviewState | null;
75
- dividerPreviewRef: MutableRefObject<DividerPreviewState | null>;
76
- dockPreview: DragPreview | null;
77
- dockPreviewRef: MutableRefObject<DragPreview | null>;
78
- dragCursor: { x: number; y: number } | null;
79
- dragFloatingRect: { paneId: string; rect: FloatingRect } | null;
80
78
  dragRef: MutableRefObject<DragMode | null>;
81
79
  hasActiveDrag: () => boolean;
82
- setDragCursor: Dispatch<SetStateAction<{ x: number; y: number } | null>>;
80
+ /** Positions that follow the pointer, outside React state. */
81
+ live: LiveDragStore;
82
+ /** The tiled pane whose drop grid is under the pointer. */
83
+ setHoverTarget: (targetId: string | null) => void;
84
+ startDrag: (drag: DragMode) => void;
83
85
  updateDividerPreview: (next: DividerPreviewState | null) => void;
84
86
  updateDockPreview: (next: DragPreview | null) => void;
85
87
  updateDragFloatingRect: (next: { paneId: string; rect: FloatingRect } | null) => void;
86
88
  }
87
89
 
90
+ function sameDragPreview(a: DragPreview | null, b: DragPreview | null): boolean {
91
+ if (a === b) return true;
92
+ if (!a || !b || a.kind !== b.kind || !sameRect(a.rect, b.rect)) return false;
93
+ if (a.kind === "snap" && b.kind === "snap") return a.position === b.position;
94
+ if (a.kind === "dock" && b.kind === "dock") {
95
+ const [x, y] = [a.target, b.target];
96
+ if (x.kind === "frame" && y.kind === "frame") return x.edge === y.edge;
97
+ if (x.kind === "leaf" && y.kind === "leaf") return x.targetId === y.targetId && x.position === y.position;
98
+ }
99
+ return false;
100
+ }
101
+
88
102
  export function useShellDragRuntimeState({
89
103
  contentHeight,
90
- throttleFloatingPreview = false,
104
+ nativePaneChrome,
91
105
  width,
92
106
  }: {
93
107
  contentHeight: number;
94
- throttleFloatingPreview?: boolean;
108
+ nativePaneChrome: boolean;
95
109
  width: number;
96
110
  }): ShellDragRuntimeState {
97
111
  const dragRef = useRef<DragMode | null>(null);
98
- const [dragFloatingRect, setDragFloatingRect] = useState<{ paneId: string; rect: FloatingRect } | null>(null);
99
- const pendingDragFloatingRectRef = useRef<{ paneId: string; rect: FloatingRect } | null>(null);
100
- const [dragCursor, setDragCursor] = useState<{ x: number; y: number } | null>(null);
101
- const [dividerPreview, setDividerPreview] = useState<DividerPreviewState | null>(null);
102
- const [dockPreview, setDockPreview] = useState<DragPreview | null>(null);
103
- const dividerPreviewRef = useRef<DividerPreviewState | null>(null);
104
- const dockPreviewRef = useRef<DragPreview | null>(null);
105
- const flushDragFloatingRect = useRafCallback(() => {
106
- setDragFloatingRect(pendingDragFloatingRectRef.current);
107
- });
112
+ const [live] = useState(createLiveDragStore);
108
113
 
109
114
  const updateDragFloatingRect = useCallback((next: { paneId: string; rect: FloatingRect } | null) => {
110
- const constrainedNext = next
115
+ const current = live.get();
116
+ const floating = next
111
117
  ? { paneId: next.paneId, rect: constrainFloatingRectToBounds(next.rect, width, contentHeight) }
112
118
  : null;
113
- pendingDragFloatingRectRef.current = constrainedNext;
114
- if (!next) {
115
- setDragFloatingRect(null);
116
- return;
117
- }
118
- if (!throttleFloatingPreview) {
119
- setDragFloatingRect(constrainedNext);
120
- return;
121
- }
122
- flushDragFloatingRect();
123
- }, [contentHeight, flushDragFloatingRect, throttleFloatingPreview, width]);
119
+ // A desktop pane being moved or resized follows by restyling itself
120
+ // (`move`); its start and end, and every terminal frame, redraw through React.
121
+ const follows = nativePaneChrome
122
+ && floating !== null
123
+ && current.floating?.paneId === floating.paneId
124
+ && (current.paneDrag?.paneId === floating.paneId || current.paneResize?.paneId === floating.paneId);
125
+ if (follows) live.move({ floating });
126
+ else live.set({ floating });
127
+ }, [contentHeight, live, nativePaneChrome, width]);
128
+
129
+ const setHoverTarget = useCallback((targetId: string | null) => {
130
+ live.set({ hoverTargetId: targetId });
131
+ }, [live]);
124
132
 
125
133
  const updateDividerPreview = useCallback((next: DividerPreviewState | null) => {
126
- dividerPreviewRef.current = next;
127
- setDividerPreview(next);
128
- }, []);
134
+ const current = live.get().divider;
135
+ // The desktop divider follows by restyling itself; React hears only that it started or ended.
136
+ if (nativePaneChrome && next && current?.pathKey === next.pathKey) live.move({ divider: next });
137
+ else live.set({ divider: next });
138
+ }, [live, nativePaneChrome]);
129
139
 
130
140
  const updateDockPreview = useCallback((next: DragPreview | null) => {
131
- dockPreviewRef.current = next;
132
- setDockPreview(next);
133
- }, []);
141
+ // Recomputed on every move; only a different target is news.
142
+ if (sameDragPreview(live.get().dockPreview, next)) return;
143
+ live.set({ dockPreview: next });
144
+ }, [live]);
145
+
146
+ const startDrag = useCallback((drag: DragMode) => {
147
+ dragRef.current = drag;
148
+ live.set({
149
+ paneDrag: drag.type === "pane-drag" ? { paneId: drag.paneId, mode: drag.mode } : null,
150
+ paneResize: drag.type === "float-resize" ? { paneId: drag.paneId } : null,
151
+ });
152
+ }, [live]);
134
153
 
135
154
  const cancelActiveDrag = useCallback(() => {
136
155
  dragRef.current = null;
137
- updateDragFloatingRect(null);
138
- setDragCursor(null);
139
- updateDividerPreview(null);
140
- updateDockPreview(null);
141
- }, [updateDividerPreview, updateDockPreview, updateDragFloatingRect]);
156
+ live.set(IDLE_DRAG);
157
+ }, [live]);
142
158
 
143
159
  const hasActiveDrag = useCallback(() => dragRef.current != null, []);
144
160
 
145
161
  return {
146
162
  cancelActiveDrag,
147
- dividerPreview,
148
- dividerPreviewRef,
149
- dockPreview,
150
- dockPreviewRef,
151
- dragCursor,
152
- dragFloatingRect,
153
163
  dragRef,
154
164
  hasActiveDrag,
155
- setDragCursor,
165
+ live,
166
+ setHoverTarget,
167
+ startDrag,
156
168
  updateDividerPreview,
157
169
  updateDockPreview,
158
170
  updateDragFloatingRect,