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
@@ -8,6 +8,7 @@ import { scopedBrokerContractIdentityKey } from "../../../utils/instrument-ident
8
8
  import { tickerSelectionFromSearchResult } from "../../../tickers/selection";
9
9
  import { tickerInstrumentLabel } from "../../../tickers/instrument-label";
10
10
  import type { PluginRegistry } from "../../../plugins/registry";
11
+ import { PluginOffError } from "../../../plugins/plugin-off";
11
12
  import { formatTickerListInput } from "../../../tickers/list";
12
13
  import { PANE_LOCK_SETTING_KEY, setPaneLocked, updatePaneInstance, setPaneSettings } from "../../../pane-settings";
13
14
  import { TICKER_RESEARCH_PANE_ID } from "../../../types/config";
@@ -291,9 +292,10 @@ export async function createPaneTemplateOrThrow(
291
292
 
292
293
  const state = deps.getState();
293
294
  const pluginId = deps.pluginRegistry.getPaneTemplatePluginId(templateId);
294
- if (pluginId && state.config.disabledPlugins.includes(pluginId)) {
295
- throw new Error("Enable this plugin before creating its pane.");
296
- }
295
+ // The template's plugin, or the plugin of the pane it opens (CHOKE opens a
296
+ // chart): a pane of a switched-off plugin would be added hidden.
297
+ const disabledOwner = deps.pluginRegistry.getDisabledPaneTemplateOwner(templateId, state.config.disabledPlugins);
298
+ if (disabledOwner) throw new PluginOffError(disabledOwner, getPaneTemplateDisplayLabel(template));
297
299
 
298
300
  const { context, resolvedOptions } = await resolvePaneTemplateOptions(template, options, deps);
299
301
 
@@ -1,56 +1,11 @@
1
1
  import type { DataTableCell, DataTableColumn, DataTableProps } from "../ui";
2
2
  import { serializeCsv } from "../../utils/csv";
3
-
4
- /** What a cell draws for a missing value: a dash, a run of dashes, or the loading ellipsis. */
5
- const PLACEHOLDER = /^(?:[-\u2013\u2014]{1,3}|\u2026|\.\.\.)$/;
6
-
7
- /**
8
- * A number as a table draws it: sign, currency symbol, thousands commas,
9
- * decimals, a compact scale (12.3k, 1.20B) and a unit (%, x, bp, USD).
10
- */
11
- const DISPLAY_NUMBER = /^([+-]?)([A-Z]{0,3}[$\u20ac\u00a3\u00a5\u20b9\u20a9\u20bd\u20ba\u20aa\u20ab\u0e3f])?(?=\.?\d)(\d{1,3}(?:,\d{3})+|\d*)(?:\.(\d+))?([kKMBT])?(%|x| ?bps?| [A-Z]{3})?$/;
12
-
13
- const SCALE_DIGITS: Record<string, number> = { k: 3, K: 3, M: 6, B: 9, T: 12 };
14
-
15
- interface DisplayNumber {
16
- /** Plain decimal text: no sign for positives, no commas, no scale letter. */
17
- number: string;
18
- /** What the column header has to name once the cells are bare numbers. */
19
- unit: string;
20
- }
21
-
22
- const BLANK = "blank";
23
-
24
- /** Moves the decimal point right without going through a float, so 1.23B is exactly 1230000000. */
25
- function scaleDecimal(integer: string, fraction: string, digits: number): string {
26
- const whole = `${integer}${fraction.padEnd(digits, "0").slice(0, digits)}`.replace(/^0+(?=\d)/, "") || "0";
27
- const rest = fraction.slice(digits);
28
- return rest ? `${whole}.${rest}` : whole;
29
- }
30
-
31
- function parseDisplayNumber(text: string): DisplayNumber | typeof BLANK | null {
32
- // A direction arrow beside a signed move repeats the sign.
33
- const trimmed = text.trim().replace(/\u2212/g, "-").replace(/\s*[\u2191\u2193]$/, "");
34
- if (!trimmed || PLACEHOLDER.test(trimmed)) return BLANK;
35
- const match = DISPLAY_NUMBER.exec(trimmed);
36
- if (!match) return null;
37
- const [, sign, currency = "", grouped, fraction = "", scale, unitText = ""] = match;
38
- const integer = grouped!.replace(/,/g, "") || "0";
39
- const digits = scale ? SCALE_DIGITS[scale]! : 0;
40
- const magnitude = digits ? scaleDecimal(integer, fraction, digits) : fraction ? `${integer}.${fraction}` : integer;
41
- const unit = unitText.trim().replace(/^bps$/, "bp");
42
- return { number: `${sign === "-" ? "-" : ""}${magnitude}`, unit: [currency, unit].filter(Boolean).join(" ") };
43
- }
44
-
45
- /** Adds the unit the cells dropped, unless the header already names it. */
46
- function labelWithUnit(label: string, unit: string): string {
47
- if (!unit) return label;
48
- // A word unit has to stand alone: "Max" does not name x.
49
- const named = unit === "bp" ? /\bbps?\b/i.test(label)
50
- : /^[A-Za-z]+$/.test(unit) ? new RegExp(`\\b${unit}\\b`, "i").test(label)
51
- : label.includes(unit);
52
- return named ? label : `${label} (${unit})`;
53
- }
3
+ import {
4
+ BLANK_DISPLAY as BLANK,
5
+ DISPLAY_PLACEHOLDER as PLACEHOLDER,
6
+ labelWithUnit,
7
+ parseDisplayNumber,
8
+ } from "../../utils/display-number";
54
9
 
55
10
  function rawExportValue(value: NonNullable<DataTableCell["value"]> | null): unknown {
56
11
  if (value == null) return "";
@@ -9,7 +9,7 @@ import {
9
9
  type RefObject,
10
10
  } from "react";
11
11
  import type { ScrollBoxRenderable } from "../../ui";
12
- import { useShortcut } from "../../react/input";
12
+ import { useBeforeShortcut, useShortcut } from "../../react/input";
13
13
  import { isPlainKey, isPlainKeyboardEvent } from "../../utils/keyboard";
14
14
  import { DataTable, type DataTableColumn, type DataTableProps } from "../ui";
15
15
  import { useDataTableSortMenu } from "./sort-menu";
@@ -42,6 +42,11 @@ function stopTableKey(event: DataTableKeyEvent) {
42
42
 
43
43
  const DATA_TABLE_SELECTION_COMMIT_DELAY_MS = 150;
44
44
 
45
+ /** The keys that step the cursor: a burst of them is what the commit delay collapses. */
46
+ function isCursorStepKey(event: DataTableKeyEvent): boolean {
47
+ return isPlainKey(event, "j", "k", "down", "up", "home", "end", "pageup", "pagedown");
48
+ }
49
+
45
50
  /** Shift+Left/Right or Ctrl+Left/Right, with no other modifier. */
46
51
  function horizontalScrollDirection(event: DataTableKeyEvent): -1 | 0 | 1 {
47
52
  if (event.name !== "left" && event.name !== "right") return 0;
@@ -108,7 +113,8 @@ export interface DataTableViewProps<
108
113
  /**
109
114
  * Fires immediately as the lightweight visual cursor moves. Expensive detail
110
115
  * work and persisted selection belong in selection.onChange, which is
111
- * coalesced across keyboard repeats.
116
+ * coalesced across keyboard repeats and lands before any other key reaches
117
+ * a handler.
112
118
  */
113
119
  onCursorChange?: (
114
120
  item: T,
@@ -445,6 +451,24 @@ export function DataTableView<
445
451
  commitTarget(target, reason);
446
452
  }, [clearPendingCommit, commitTarget, getCommitTarget]);
447
453
 
454
+ const flushPendingCommit = useCallback(() => {
455
+ if (!pendingCommitRef.current) return;
456
+ const target = pendingCommitTargetRef.current;
457
+ clearPendingCommit();
458
+ lastKeyboardCommitAtRef.current = performance.now();
459
+ commitTarget(target, "keyboard");
460
+ }, [clearPendingCommit, commitTarget]);
461
+ const flushPendingCommitRef = useRef(flushPendingCommit);
462
+ flushPendingCommitRef.current = flushPendingCommit;
463
+
464
+ // The delay only coalesces a burst of steps. Any other key acts on the row
465
+ // under the cursor, so a step still waiting commits before the key reaches
466
+ // a handler, which reads the selection the pane holds.
467
+ useBeforeShortcut((event) => {
468
+ if (event.defaultPrevented || event.propagationStopped || isCursorStepKey(event)) return;
469
+ flushPendingCommitRef.current();
470
+ });
471
+
448
472
  const scheduleCommitIndex = useCallback((index: number) => {
449
473
  if (selection.kind === "none") return;
450
474
  const target = getCommitTarget(index);
@@ -464,14 +488,9 @@ export function DataTableView<
464
488
  pendingCommitTargetRef.current = target;
465
489
  pendingCommitTimerRef.current = setTimeout(() => {
466
490
  pendingCommitTimerRef.current = null;
467
- pendingCommitRef.current = false;
468
- const pendingTarget = pendingCommitTargetRef.current;
469
- pendingCommitTargetRef.current = null;
470
- clearSelectionScrollTarget();
471
- lastKeyboardCommitAtRef.current = performance.now();
472
- commitTarget(pendingTarget, "keyboard");
491
+ flushPendingCommitRef.current();
473
492
  }, DATA_TABLE_SELECTION_COMMIT_DELAY_MS);
474
- }, [clearSelectionScrollTarget, commitIndexImmediately, commitTarget, getCommitTarget, selection.kind]);
493
+ }, [commitIndexImmediately, getCommitTarget, selection.kind]);
475
494
 
476
495
  const updateCursorIndex = useCallback((
477
496
  index: number,
@@ -672,7 +691,9 @@ export function DataTableView<
672
691
  event.preventDefault();
673
692
  return;
674
693
  }
675
- if (tableProps.items.length === 0) return;
694
+ // Without a cursor there is nothing for these keys to move, so they stay
695
+ // free for the pane scroll keys to scroll the rows.
696
+ if (tableProps.items.length === 0 || selection.kind === "none") return;
676
697
 
677
698
  if (isPlainKey(event, "j", "down")) {
678
699
  stopTableKey(event);
@@ -1,5 +1,5 @@
1
- import { Box, Text, useUiCapabilities } from "../../ui";
2
- import type { ReactNode } from "react";
1
+ import { Box, Text, useUiCapabilities, type BoxRenderable } from "../../ui";
2
+ import type { ReactNode, RefObject } from "react";
3
3
  import { colors, floatingPaneBg } from "../../theme/colors";
4
4
  import { PaneBodyFrame, getPaneWindowAttributes } from "./pane/frame";
5
5
  import { PaneHeader, type PaneHeaderQuickSetting } from "./pane/header";
@@ -37,6 +37,8 @@ interface FloatingPaneWrapperProps {
37
37
  onResizeMouseDrag?: (event: any) => void;
38
38
  onResizeMouseDragEnd?: (event: any) => void;
39
39
  footer?: CombinedPaneFooter | null;
40
+ /** The pane's frame, which a desktop resize sizes outside React; its header, body and footer stretch with it. */
41
+ frameRef?: RefObject<BoxRenderable | null>;
40
42
  children: ReactNode;
41
43
  }
42
44
 
@@ -88,6 +90,7 @@ export function FloatingPaneWrapper({
88
90
  onResizeMouseDrag,
89
91
  onResizeMouseDragEnd,
90
92
  footer,
93
+ frameRef,
91
94
  children,
92
95
  }: FloatingPaneWrapperProps) {
93
96
  const { nativePaneChrome } = useUiCapabilities();
@@ -100,6 +103,7 @@ export function FloatingPaneWrapper({
100
103
 
101
104
  return (
102
105
  <Box
106
+ ref={frameRef}
103
107
  position="absolute"
104
108
  top={y}
105
109
  left={x}
@@ -1,5 +1,5 @@
1
1
  import { useEffect, useRef, useState } from "react";
2
- import { priceColor } from "../../theme/colors";
2
+ import { priceColor, type ThemeColors } from "../../theme/colors";
3
3
  import { useThemeColors } from "../../theme/theme-context";
4
4
  import { useAppVisible } from "../../state/app/activity";
5
5
  import { useAppSelector } from "../../state/app/context";
@@ -10,7 +10,8 @@ import { useQuoteStreaming } from "../../state/hooks/quote-streaming";
10
10
  import type { QuoteSubscriptionTarget } from "../../types/data-provider";
11
11
  import { formatPercentRaw } from "../../utils/format";
12
12
  import { formatMarketPrice, liveQuoteFormatOptions } from "../../market-data/market/format";
13
- import { getActiveQuoteDisplay, marketStateColor, marketStateCountdown, marketStateLabel } from "../../market-data/market/status";
13
+ import { getSessionMoveDisplay, marketStateColor, marketStateCountdown, marketStateLabel } from "../../market-data/market/status";
14
+ import type { Quote } from "../../types/financials";
14
15
 
15
16
  /**
16
17
  * SPY rides the shared feed. The snapshot poll only runs while the feed has
@@ -76,6 +77,25 @@ export function resolveMarketSummaryFit(options: {
76
77
  return { showBaseCurrency, showCountdown, showState, showSpy };
77
78
  }
78
79
 
80
+ /**
81
+ * SPY in the header's one slot, beside the market state: the live quote while
82
+ * the regular session trades, and once it is over its close and the move to
83
+ * it, as `ticker SPY` headlines them. While a pre-market or after-hours
84
+ * session is open (PRE-MKT, AFTER-HRS) the slot is that session's print and
85
+ * its move from the close, so the figure is the session the label names.
86
+ */
87
+ export function spySummary(quote: Quote | null | undefined, colors: ThemeColors): Pick<MarketSummary, "spyColor" | "spyText"> {
88
+ const display = getSessionMoveDisplay(quote);
89
+ // Fixed decimals and a percent slot as wide as "+0.53%", so a streamed tick
90
+ // never slides the market-state label or re-fits the cluster.
91
+ return {
92
+ spyColor: display?.change != null ? priceColor(display.change, colors) : colors.textDim,
93
+ spyText: display
94
+ ? `SPY ${formatMarketPrice(display.price, liveQuoteFormatOptions(quote, quote?.currency, "ETF"))} ${formatPercentRaw(display.changePercent).padStart(6)}`
95
+ : "SPY —",
96
+ };
97
+ }
98
+
79
99
  export function useMarketSummary(): MarketSummary {
80
100
  const colors = useThemeColors();
81
101
  const appActive = useAppVisible();
@@ -112,13 +132,7 @@ export function useMarketSummary(): MarketSummary {
112
132
  return () => { clearInterval(id); };
113
133
  }, [appActive]);
114
134
 
115
- const activeSpyQuote = getActiveQuoteDisplay(spyQuote);
116
- const spyColor = activeSpyQuote?.change != null ? priceColor(activeSpyQuote.change, colors) : colors.textDim;
117
- // Fixed decimals and a percent slot as wide as "+0.53%", so a streamed tick
118
- // never slides the market-state label or re-fits the cluster.
119
- const spyText = activeSpyQuote
120
- ? `SPY ${formatMarketPrice(activeSpyQuote.price, liveQuoteFormatOptions(spyQuote, spyQuote?.currency, "ETF"))} ${formatPercentRaw(activeSpyQuote.changePercent).padStart(6)}`
121
- : "SPY —";
135
+ const { spyColor, spyText } = spySummary(spyQuote, colors);
122
136
 
123
137
  const mktCountdown = mktState ? marketStateCountdown(mktState, now) : null;
124
138
  const marketLabelShort = mktState ? t(marketStateLabel(mktState)) : "";
@@ -1,8 +1,9 @@
1
- import { memo, Profiler, useCallback, type ReactNode } from "react";
1
+ import { memo, Profiler, useCallback, useMemo, type ReactNode } from "react";
2
2
  import { useAppLanguage } from "../../../i18n/react";
3
3
  import { isPerfTraceEnabled, recordPerfSample } from "../../../utils/perf-marks";
4
4
  import { PaneInViewProvider } from "../../../state/app/activity";
5
5
  import { PaneInstanceProvider } from "../../../state/app/context";
6
+ import { RemoteUiPaneScope } from "../../../remote/semantic-tree";
6
7
  import { useThemeColors } from "../../../theme/theme-context";
7
8
  import { PaneKeyboardScrollController } from "../../../state/pane-scroll-registry";
8
9
  import type { PaneDef } from "../../../types/plugin";
@@ -41,6 +42,13 @@ interface PaneContentProps {
41
42
  onClose?: (paneId: string) => void;
42
43
  /** Takes the pane out of its layout; the failure card offers it. Keep it stable: the content is memoized. */
43
44
  closePane?: (paneId: string) => void;
45
+ /**
46
+ * Desktop: lays the content out at `width` by `height` instead of filling
47
+ * the pane, while a resize sizes the pane's frame ahead of it. The content,
48
+ * and whatever measures it, then lays out again only when it renders at a
49
+ * new size.
50
+ */
51
+ pinned?: boolean;
44
52
  }
45
53
 
46
54
  export const PaneContent = memo(function PaneContent({
@@ -54,44 +62,53 @@ export const PaneContent = memo(function PaneContent({
54
62
  inView = true,
55
63
  onClose,
56
64
  closePane,
65
+ pinned = false,
57
66
  }: PaneContentProps) {
58
- useAppLanguage();
59
- useThemeColors();
67
+ // The pane reads text and colors while it renders, so either redraws it.
68
+ const language = useAppLanguage();
69
+ const theme = useThemeColors();
60
70
  const close = useCallback(() => {
61
71
  onClose?.(paneId);
62
72
  }, [onClose, paneId]);
63
73
  const closeFailedPane = useCallback(() => {
64
74
  closePane?.(paneId);
65
75
  }, [closePane, paneId]);
76
+ // Built again only when what the pane is given changes: pinning or
77
+ // unpinning the content box does not render the pane.
78
+ const body = useMemo(() => (
79
+ <PaneErrorBoundary paneType={paneType} title={title} onClose={closePane ? closeFailedPane : undefined}>
80
+ <PaneRenderTrace paneId={paneId} paneType={paneType}>
81
+ <Component
82
+ paneId={paneId}
83
+ paneType={paneType}
84
+ focused={focused}
85
+ width={width}
86
+ height={height}
87
+ close={onClose ? close : undefined}
88
+ />
89
+ </PaneRenderTrace>
90
+ </PaneErrorBoundary>
91
+ ), [Component, close, closeFailedPane, closePane, focused, height, language, onClose, paneId, paneType, theme, title, width]);
66
92
 
67
93
  return (
68
94
  <PaneInstanceProvider paneId={paneId}>
69
- <PaneInViewProvider value={inView}>
70
- <PaneKeyboardScrollController paneId={paneId} focused={focused} />
71
- <Box
72
- flexDirection="column"
73
- flexGrow={1}
74
- flexShrink={1}
75
- flexBasis={0}
76
- minWidth={0}
77
- minHeight={0}
78
- overflow="hidden"
79
- data-gloom-role="pane-content"
80
- >
81
- <PaneErrorBoundary paneType={paneType} title={title} onClose={closePane ? closeFailedPane : undefined}>
82
- <PaneRenderTrace paneId={paneId} paneType={paneType}>
83
- <Component
84
- paneId={paneId}
85
- paneType={paneType}
86
- focused={focused}
87
- width={width}
88
- height={height}
89
- close={onClose ? close : undefined}
90
- />
91
- </PaneRenderTrace>
92
- </PaneErrorBoundary>
93
- </Box>
94
- </PaneInViewProvider>
95
+ <RemoteUiPaneScope paneId={paneId}>
96
+ <PaneInViewProvider value={inView}>
97
+ <PaneKeyboardScrollController paneId={paneId} focused={focused} />
98
+ <Box
99
+ flexDirection="column"
100
+ {...(pinned
101
+ ? { width, height, flexGrow: 0, flexShrink: 0 }
102
+ : { flexGrow: 1, flexShrink: 1, flexBasis: 0 })}
103
+ minWidth={0}
104
+ minHeight={0}
105
+ overflow="hidden"
106
+ data-gloom-role="pane-content"
107
+ >
108
+ {body}
109
+ </Box>
110
+ </PaneInViewProvider>
111
+ </RemoteUiPaneScope>
95
112
  </PaneInstanceProvider>
96
113
  );
97
114
  });
@@ -1,23 +1,14 @@
1
1
  import { getDockedPaneIds } from "../../../layout/pane-manager";
2
2
  import type { PluginRegistry } from "../../../plugins/registry";
3
3
  import { findPaneInstance, type LayoutConfig } from "../../../types/config";
4
-
5
- function disabledPaneTypes(pluginRegistry: PluginRegistry, disabledPlugins: readonly string[]): Set<string> {
6
- const paneTypes = new Set<string>();
7
- for (const pluginId of disabledPlugins) {
8
- for (const paneId of pluginRegistry.getPluginPaneIds(pluginId)) {
9
- paneTypes.add(paneId);
10
- }
11
- }
12
- return paneTypes;
13
- }
4
+ import { collectDisabledPaneIds } from "../shell/visible-layout";
14
5
 
15
6
  export function getVisiblePaneCycleOrder(
16
7
  layout: LayoutConfig,
17
8
  pluginRegistry: PluginRegistry,
18
9
  disabledPlugins: readonly string[],
19
10
  ): string[] {
20
- const disabledTypes = disabledPaneTypes(pluginRegistry, disabledPlugins);
11
+ const disabledTypes = collectDisabledPaneIds(pluginRegistry, disabledPlugins);
21
12
  const isVisiblePane = (instanceId: string) => {
22
13
  const instance = findPaneInstance(layout, instanceId);
23
14
  return !!instance
@@ -174,10 +174,11 @@ export function PaneHeader({
174
174
 
175
175
  if (nativePaneChrome) {
176
176
  const ruleColor = visuallyFocused ? colors.borderFocused : colors.border;
177
+ // No width of its own: it stretches across its pane, so it follows a
178
+ // frame a resize sizes outside React.
177
179
  return (
178
180
  <Box
179
181
  height={titleBar?.rows ?? nativePaneHeaderRows()}
180
- width={width}
181
182
  backgroundColor={backgroundColor}
182
183
  flexDirection="row"
183
184
  data-gloom-role="pane-header"
@@ -13,7 +13,7 @@ import { canFollowTickerSource, isTickerLinkPeer } from "../../../layout/ticker-
13
13
  const LINK_GLYPH = "\u29c9";
14
14
 
15
15
  /** The title without the link suffix. */
16
- function getBasePaneDisplayTitle(
16
+ export function getBasePaneDisplayTitle(
17
17
  state: Pick<AppState, "config" | "paneState">,
18
18
  instance: PaneInstanceConfig,
19
19
  paneDef: PaneDef,
@@ -0,0 +1,22 @@
1
+ import { useAppSelector } from "../../state/app/context";
2
+ import { useUiCapabilities } from "../../ui";
3
+
4
+ /**
5
+ * Which app chrome is on screen. Presentation mode (`presentationMode` in the
6
+ * config) leaves the window to the panes: no header (the command prompt and
7
+ * the market summary) and no status bar (layout tabs, version, account and
8
+ * feedback). The header comes back while the command bar is open, as its
9
+ * prompt is where the command bar types, and stays in the desktop app, where
10
+ * it is the window's own title bar with its window controls.
11
+ */
12
+ export function useAppChromeShown(): { header: boolean; statusBar: boolean } {
13
+ const { titleBarOverlay = false, nativeWindowChrome = true } = useUiCapabilities();
14
+ const windowTitleBar = titleBarOverlay && nativeWindowChrome;
15
+ const presentation = useAppSelector((state) => state.config.presentationMode === true);
16
+ const commandBarOpen = useAppSelector((state) => state.commandBarOpen);
17
+ const statusBarVisible = useAppSelector((state) => state.statusBarVisible);
18
+ return {
19
+ header: !presentation || commandBarOpen || windowTitleBar,
20
+ statusBar: statusBarVisible && !presentation,
21
+ };
22
+ }
@@ -1,4 +1,4 @@
1
- import { useCallback } from "react";
1
+ import { useCallback, useRef } from "react";
2
2
  import {
3
3
  floatAtRect,
4
4
  getRememberedFloatingRect,
@@ -7,6 +7,7 @@ import {
7
7
  type DockGeometryOptions,
8
8
  type DockLeafLayout,
9
9
  type DropTarget,
10
+ type FloatingRect,
10
11
  type LayoutBounds,
11
12
  type ResolvedPane,
12
13
  } from "../../../layout/pane-manager";
@@ -23,6 +24,7 @@ import {
23
24
  resolveHoverOverlay,
24
25
  resolvePaneDragFloatingRect,
25
26
  resolveSnapGuide,
27
+ type DragPreview,
26
28
  } from "./drag";
27
29
  import type { ShellDragRuntimeState, ShellMouseEvent } from "./drag/runtime";
28
30
  import type { WindowEditState } from "../window-edit/mode";
@@ -65,37 +67,74 @@ export function useShellActiveDrag({
65
67
  windowMode,
66
68
  }: UseShellActiveDragOptions) {
67
69
  const {
68
- dividerPreviewRef,
69
- dockPreviewRef,
70
+ cancelActiveDrag,
70
71
  dragRef,
71
- setDragCursor,
72
+ live,
73
+ setHoverTarget,
72
74
  updateDividerPreview,
73
75
  updateDockPreview,
74
76
  updateDragFloatingRect,
75
77
  } = dragRuntime;
78
+ // What one drag keeps between moves: a docked pane's floating size, and the
79
+ // last drop it simulated, so a move over the same target computes nothing.
80
+ const memoRef = useRef<{
81
+ drag: unknown;
82
+ baseRect: FloatingRect | null;
83
+ dropKey: string | null;
84
+ drop: DragPreview | null;
85
+ }>({ drag: null, baseRect: null, dropKey: null, drop: null });
76
86
 
77
87
  return useCallback((event: ShellMouseEvent) => {
78
88
  const shellY = event.y - appHeaderHeight;
79
89
  const preciseX = event.preciseX ?? event.x;
80
90
  const preciseShellY = (event.preciseY ?? event.y) - appHeaderHeight;
91
+ // Drawn a moment ahead where the host predicts the pointer; hit tests and
92
+ // the release use where it is.
93
+ const drawX = event.predictedX ?? preciseX;
94
+ const drawShellY = event.predictedY != null ? event.predictedY - appHeaderHeight : preciseShellY;
81
95
  const hitX = precisePointer ? preciseX : event.x;
82
96
  const hitShellY = precisePointer ? preciseShellY : shellY;
83
97
  const dragThreshold = precisePointer ? PRECISE_PANE_DRAG_THRESHOLD : PANE_DRAG_THRESHOLD;
84
98
  const drag = dragRef.current;
85
99
  if (!drag) return;
86
100
  const baseLayout = windowMode?.previewLayout ?? visibleLayout;
101
+ const memo = memoRef.current;
102
+ if (memo.drag !== drag) {
103
+ memo.drag = drag;
104
+ memo.baseRect = null;
105
+ memo.dropKey = null;
106
+ memo.drop = null;
107
+ }
108
+ const dividerRatioAt = (x: number, y: number) => {
109
+ if (drag.type !== "divider") return 0;
110
+ const total = drag.axis === "horizontal" ? drag.bounds.width : drag.bounds.height;
111
+ const delta = drag.axis === "horizontal" ? x - drag.startX : y - drag.startY;
112
+ return Math.max(0.1, Math.min(0.9, drag.startRatio + (delta / Math.max(1, total))));
113
+ };
114
+ const dragBaseRect = () => {
115
+ if (drag.type !== "pane-drag") return null;
116
+ if (drag.mode === "floating") return drag.origRect;
117
+ memo.baseRect ??= getRememberedFloatingRect(baseLayout, drag.paneId, width, contentHeight, paneMap.get(drag.paneId)?.def);
118
+ return memo.baseRect;
119
+ };
120
+
121
+ if (event.type === "drag-cancel") {
122
+ // The window lost focus or the system took the pointer: put everything back.
123
+ cancelActiveDrag();
124
+ event.stopPropagation();
125
+ event.preventDefault();
126
+ return;
127
+ }
87
128
 
88
129
  if (event.type === "drag") {
89
130
  if (drag.type === "divider") {
90
- const total = drag.axis === "horizontal" ? drag.bounds.width : drag.bounds.height;
91
- const delta = drag.axis === "horizontal" ? preciseX - drag.startX : preciseShellY - drag.startY;
92
- const nextRatio = Math.max(0.1, Math.min(0.9, drag.startRatio + (delta / Math.max(1, total))));
131
+ const nextRatio = dividerRatioAt(drawX, drawShellY);
93
132
  const nextRect = resolveDividerPreviewRect(drag.axis, drag.bounds, nextRatio, nativePaneChrome === true);
94
133
  updateDividerPreview({ pathKey: drag.path.join("."), rect: nextRect, ratio: nextRatio });
95
134
  } else if (drag.type === "pane-drag") {
96
135
  if (!isMeaningfulPaneDrag(drag.startX, drag.startY, preciseX, preciseShellY, dragThreshold)) {
97
136
  updateDockPreview(null);
98
- setDragCursor(null);
137
+ setHoverTarget(null);
99
138
  if (drag.mode === "floating") {
100
139
  updateDragFloatingRect({ paneId: drag.paneId, rect: drag.origRect });
101
140
  }
@@ -104,25 +143,22 @@ export function useShellActiveDrag({
104
143
  return;
105
144
  }
106
145
 
107
- const pane = paneMap.get(drag.paneId);
108
- const baseRect = drag.mode === "docked"
109
- ? getRememberedFloatingRect(baseLayout, drag.paneId, width, contentHeight, pane?.def)
110
- : drag.origRect;
111
- const nextRect = resolvePaneDragFloatingRect(drag, baseRect, preciseX, preciseShellY, width, contentHeight);
146
+ const nextRect = resolvePaneDragFloatingRect(drag, dragBaseRect()!, drawX, drawShellY, width, contentHeight);
112
147
  updateDragFloatingRect({ paneId: drag.paneId, rect: nextRect });
113
- setDragCursor({ x: hitX, y: hitShellY });
114
148
 
115
149
  const hoveredOverlay = resolveHoverOverlay(hitX, hitShellY, dockLeafLayouts, drag.paneId);
150
+ setHoverTarget(hoveredOverlay?.targetId ?? null);
116
151
  if (hoveredOverlay) {
117
152
  const hoveredCell = hoveredOverlay.cells.find((cell) => pointInRect(cell.rect, hitX, hitShellY));
118
153
  if (hoveredCell) {
119
- const target: DropTarget = { kind: "leaf", targetId: hoveredOverlay.targetId, position: hoveredCell.position };
120
- const simulation = simulateDrop(baseLayout, drag.paneId, target, bounds, dockGeometryOptions);
121
- if (simulation.previewRect) {
122
- updateDockPreview({ kind: "dock", target, rect: simulation.previewRect });
123
- } else {
124
- updateDockPreview(null);
154
+ const dropKey = `${hoveredOverlay.targetId}:${hoveredCell.position}`;
155
+ if (memo.dropKey !== dropKey) {
156
+ const target: DropTarget = { kind: "leaf", targetId: hoveredOverlay.targetId, position: hoveredCell.position };
157
+ const simulation = simulateDrop(baseLayout, drag.paneId, target, bounds, dockGeometryOptions);
158
+ memo.dropKey = dropKey;
159
+ memo.drop = simulation.previewRect ? { kind: "dock", target, rect: simulation.previewRect } : null;
125
160
  }
161
+ updateDockPreview(memo.drop);
126
162
  } else {
127
163
  updateDockPreview(null);
128
164
  }
@@ -143,9 +179,8 @@ export function useShellActiveDrag({
143
179
 
144
180
  if (event.type === "up" || event.type === "drag-end") {
145
181
  if (drag.type === "divider") {
146
- const preview = dividerPreviewRef.current;
147
- if (preview) {
148
- const nextLayout = resizeSplitAtPath(baseLayout, drag.path, preview.ratio);
182
+ if (live.get().divider) {
183
+ const nextLayout = resizeSplitAtPath(baseLayout, drag.path, dividerRatioAt(preciseX, preciseShellY));
149
184
  if (windowMode) {
150
185
  updateWindowModePreviewLayout(nextLayout);
151
186
  } else {
@@ -157,15 +192,11 @@ export function useShellActiveDrag({
157
192
  const movedEnough = isMeaningfulPaneDrag(drag.startX, drag.startY, preciseX, preciseShellY, dragThreshold);
158
193
  if (!movedEnough) {
159
194
  updateDockPreview(null);
160
- setDragCursor(null);
195
+ setHoverTarget(null);
161
196
  updateDragFloatingRect(null);
162
197
  } else {
163
- const pane = paneMap.get(drag.paneId);
164
- const baseRect = drag.mode === "docked"
165
- ? getRememberedFloatingRect(baseLayout, drag.paneId, width, contentHeight, pane?.def)
166
- : drag.origRect;
167
- const releaseRect = resolvePaneDragFloatingRect(drag, baseRect, preciseX, preciseShellY, width, contentHeight);
168
- const nextLayout = finalizePaneDragRelease(baseLayout, drag.paneId, releaseRect, dockPreviewRef.current);
198
+ const releaseRect = resolvePaneDragFloatingRect(drag, dragBaseRect()!, preciseX, preciseShellY, width, contentHeight);
199
+ const nextLayout = finalizePaneDragRelease(baseLayout, drag.paneId, releaseRect, live.get().dockPreview);
169
200
  if (windowMode) {
170
201
  updateWindowModePreviewLayout(nextLayout, drag.paneId);
171
202
  } else {
@@ -173,7 +204,7 @@ export function useShellActiveDrag({
173
204
  }
174
205
  focusPane(drag.paneId);
175
206
  updateDockPreview(null);
176
- setDragCursor(null);
207
+ setHoverTarget(null);
177
208
  updateDragFloatingRect(null);
178
209
  }
179
210
  } else if (drag.type === "float-resize") {
@@ -185,27 +216,28 @@ export function useShellActiveDrag({
185
216
  persistLayout(nextLayout);
186
217
  }
187
218
  updateDragFloatingRect(null);
188
- setDragCursor(null);
219
+ setHoverTarget(null);
189
220
  }
190
221
  dragRef.current = null;
222
+ live.set({ paneDrag: null, paneResize: null });
191
223
  event.stopPropagation();
192
224
  event.preventDefault();
193
225
  }
194
226
  }, [
195
227
  appHeaderHeight,
196
228
  bounds,
229
+ cancelActiveDrag,
197
230
  contentHeight,
198
231
  dockGeometryOptions,
199
- dividerPreviewRef,
200
232
  dockLeafLayouts,
201
- dockPreviewRef,
202
233
  dragRef,
203
234
  focusPane,
235
+ live,
204
236
  nativePaneChrome,
205
237
  paneMap,
206
238
  persistLayout,
207
239
  precisePointer,
208
- setDragCursor,
240
+ setHoverTarget,
209
241
  snapGuides,
210
242
  updateDividerPreview,
211
243
  updateDockPreview,