gloomberb 0.16.3 → 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 (451) 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/signed-in/adapter.ts +33 -2
  30. package/src/brokers/signed-in/client.ts +10 -0
  31. package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
  32. package/src/brokers/signed-in/sign-in.ts +62 -5
  33. package/src/capabilities/registry.ts +8 -1
  34. package/src/capabilities/types.ts +2 -0
  35. package/src/cli/commands/crypto-hints.ts +12 -4
  36. package/src/cli/commands/market.ts +359 -99
  37. package/src/cli/commands/options-chain.ts +172 -0
  38. package/src/cli/commands/options-leaps.ts +422 -0
  39. package/src/cli/commands/overview.ts +164 -26
  40. package/src/cli/commands/search.ts +29 -8
  41. package/src/cli/commands/system.ts +99 -19
  42. package/src/cli/commands/themes.ts +64 -0
  43. package/src/cli/commands/ticker.ts +62 -30
  44. package/src/cli/desktop-pane-shot.ts +97 -16
  45. package/src/cli/errors.ts +25 -2
  46. package/src/cli/freshness.ts +7 -4
  47. package/src/cli/fx-pair.ts +57 -0
  48. package/src/cli/help.ts +14 -1
  49. package/src/cli/index-roots.ts +51 -0
  50. package/src/cli/index.ts +9 -6
  51. package/src/cli/listing-arg.ts +211 -3
  52. package/src/cli/not-a-ticker.ts +37 -0
  53. package/src/cli/options.ts +50 -6
  54. package/src/cli/pane-functions/capabilities.ts +8 -2
  55. package/src/cli/pane-functions/catalog.ts +13 -2
  56. package/src/cli/pane-functions/data.ts +36 -5
  57. package/src/cli/pane-functions/dom.ts +21 -13
  58. package/src/cli/pane-functions/freshness.ts +205 -26
  59. package/src/cli/pane-functions/function-help.ts +67 -22
  60. package/src/cli/pane-functions/headless.ts +5 -4
  61. package/src/cli/pane-functions/index.ts +3 -0
  62. package/src/cli/pane-functions/market-session.ts +133 -0
  63. package/src/cli/pane-functions/options.ts +15 -4
  64. package/src/cli/pane-functions/report-notices.ts +63 -0
  65. package/src/cli/pane-functions/report.ts +4 -0
  66. package/src/cli/pane-functions/resolver.ts +4 -6
  67. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  68. package/src/cli/pane-functions/screenshot.ts +105 -23
  69. package/src/cli/registry.ts +2 -1
  70. package/src/cli/result.ts +50 -13
  71. package/src/cli/row-window.ts +76 -0
  72. package/src/cli/scroll-truncation.ts +34 -0
  73. package/src/components/chart/composite/composite-chart.tsx +43 -4
  74. package/src/components/chart/composite/legend.tsx +39 -6
  75. package/src/components/chart/composite/rasterizer.ts +64 -4
  76. package/src/components/chart/composite/series-cues.ts +71 -0
  77. package/src/components/chart/composite/text-renderer.ts +16 -8
  78. package/src/components/chart/composite/types.ts +3 -0
  79. package/src/components/command-bar/commands/direct/index.ts +9 -1
  80. package/src/components/command-bar/commands/plugin/items.ts +3 -1
  81. package/src/components/command-bar/commands/registry.ts +7 -0
  82. package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
  83. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  84. package/src/components/command-bar/routes/root/command-items.ts +2 -0
  85. package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
  86. package/src/components/command-bar/routes/root/results.ts +40 -10
  87. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  88. package/src/components/command-bar/routes/root/search-providers.ts +97 -14
  89. package/src/components/command-bar/surface/index.tsx +18 -4
  90. package/src/components/command-bar/theme-picker.tsx +48 -13
  91. package/src/components/command-bar/workflow/ops.ts +5 -3
  92. package/src/components/data-table/view.tsx +28 -9
  93. package/src/components/layout/floating-pane.tsx +6 -2
  94. package/src/components/layout/market-summary.ts +23 -9
  95. package/src/components/layout/pane/content.tsx +46 -29
  96. package/src/components/layout/pane/cycle-order.ts +2 -11
  97. package/src/components/layout/pane/header.tsx +2 -1
  98. package/src/components/layout/pane/title.ts +1 -1
  99. package/src/components/layout/presentation.ts +22 -0
  100. package/src/components/layout/shell/active-drag.ts +1 -1
  101. package/src/components/layout/shell/drag/index.ts +4 -0
  102. package/src/components/layout/shell/drag/live.ts +76 -5
  103. package/src/components/layout/shell/drag/runtime.ts +8 -8
  104. package/src/components/layout/shell/index.tsx +47 -6
  105. package/src/components/layout/shell/layout-state.ts +5 -11
  106. package/src/components/layout/shell/menu.ts +28 -1
  107. package/src/components/layout/shell/pane/actions.ts +2 -1
  108. package/src/components/layout/shell/pane/layers.tsx +51 -10
  109. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  110. package/src/components/layout/shell/pane/new-layout.ts +116 -0
  111. package/src/components/layout/shell/shortcuts.ts +5 -2
  112. package/src/components/layout/shell/visible-layout.ts +44 -1
  113. package/src/components/layout/status-bar.tsx +4 -1
  114. package/src/components/markdown-text.tsx +9 -5
  115. package/src/components/metric-treemap/heat-surface.tsx +27 -8
  116. package/src/components/metric-treemap/heat-tiles.ts +48 -17
  117. package/src/components/onboarding/onboarding-frame.tsx +33 -10
  118. package/src/components/onboarding/onboarding-wizard.tsx +49 -40
  119. package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
  120. package/src/components/onboarding/pro-step-layout.ts +86 -0
  121. package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
  122. package/src/components/ui/button.tsx +10 -0
  123. package/src/components/ui/data-table/index.tsx +38 -0
  124. package/src/components/ui/data-table/types.ts +8 -1
  125. package/src/components/ui/data-table/visible-range.ts +2 -0
  126. package/src/components/ui/icon.tsx +11 -2
  127. package/src/components/ui/index.ts +1 -1
  128. package/src/core/connection-health.ts +1 -1
  129. package/src/core/state/app/layout-reducer.ts +89 -3
  130. package/src/core/state/app/state.ts +8 -5
  131. package/src/core/state/app/types.ts +13 -2
  132. package/src/data/config/layout.ts +55 -21
  133. package/src/data/config/store/fold-panes.ts +149 -0
  134. package/src/data/config/store/migrations.ts +7 -0
  135. package/src/data/config/store/node.ts +27 -6
  136. package/src/data/config/store/normalize.ts +37 -3
  137. package/src/i18n/es.ts +14 -1
  138. package/src/i18n/ja.ts +14 -1
  139. package/src/i18n/ko.ts +14 -1
  140. package/src/i18n/zh-cn.ts +14 -1
  141. package/src/i18n/zh-tw.ts +14 -1
  142. package/src/layout/desks.ts +1 -2
  143. package/src/layout/pane-layout-move.ts +264 -0
  144. package/src/layout/pane-manager/hidden-panes.ts +160 -0
  145. package/src/layout/pane-manager/index.ts +1 -0
  146. package/src/market-data/coordinator/entries.ts +7 -1
  147. package/src/market-data/coordinator/quotes.ts +2 -2
  148. package/src/market-data/market/freshness.ts +118 -18
  149. package/src/market-data/market/status.ts +40 -0
  150. package/src/market-data/options-alternatives.ts +69 -0
  151. package/src/market-data/quotes/contributions.ts +19 -2
  152. package/src/market-data/quotes/metadata.ts +16 -0
  153. package/src/market-data/quotes/screener-live-quotes.ts +56 -5
  154. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  155. package/src/plugins/builtin/account-management/model.ts +1 -0
  156. package/src/plugins/builtin/account-management/navigation.ts +1 -1
  157. package/src/plugins/builtin/account-management/pane.tsx +31 -4
  158. package/src/plugins/builtin/alerts/quotes.ts +3 -0
  159. package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
  160. package/src/plugins/builtin/analytics/risk-model.ts +37 -8
  161. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
  162. package/src/plugins/builtin/backtest/pane.tsx +1 -1
  163. package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
  164. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  165. package/src/plugins/builtin/broker-manager/model.ts +8 -0
  166. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
  167. package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
  168. package/src/plugins/builtin/broker-manager/table.ts +5 -0
  169. package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
  170. package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
  171. package/src/plugins/builtin/chat/channels.ts +34 -36
  172. package/src/plugins/builtin/chat/command-bar.ts +138 -0
  173. package/src/plugins/builtin/chat/composer-commands.ts +21 -1
  174. package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
  175. package/src/plugins/builtin/chat/content/footer.tsx +5 -3
  176. package/src/plugins/builtin/chat/content/index.tsx +21 -1
  177. package/src/plugins/builtin/chat/content/mentions.ts +2 -1
  178. package/src/plugins/builtin/chat/content/user-map.ts +3 -1
  179. package/src/plugins/builtin/chat/controller/index.ts +48 -0
  180. package/src/plugins/builtin/chat/conversations.ts +256 -0
  181. package/src/plugins/builtin/chat/discord-command.ts +91 -0
  182. package/src/plugins/builtin/chat/edit-window.ts +12 -6
  183. package/src/plugins/builtin/chat/ghost-user.ts +14 -0
  184. package/src/plugins/builtin/chat/index.tsx +40 -2
  185. package/src/plugins/builtin/chat/layout.ts +2 -0
  186. package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
  187. package/src/plugins/builtin/chat/message/header.tsx +24 -15
  188. package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
  189. package/src/plugins/builtin/chat/message/render-state.ts +3 -0
  190. package/src/plugins/builtin/chat/pane-requests.ts +60 -0
  191. package/src/plugins/builtin/chat/pane-state.ts +32 -2
  192. package/src/plugins/builtin/chat/pane.tsx +8 -0
  193. package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
  194. package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
  195. package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
  196. package/src/plugins/builtin/cloud/askg/model.ts +129 -70
  197. package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
  198. package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
  199. package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
  200. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
  201. package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
  202. package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
  203. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
  204. package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
  205. package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
  206. package/src/plugins/builtin/cloud/plugin.tsx +36 -1
  207. package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
  208. package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
  209. package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
  210. package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
  211. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
  212. package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
  213. package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
  214. package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
  215. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
  216. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
  217. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
  218. package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
  219. package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
  220. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
  221. package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
  222. package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
  223. package/src/plugins/builtin/comment-letters/client.ts +1 -3
  224. package/src/plugins/builtin/company-kpis/client.ts +23 -8
  225. package/src/plugins/builtin/company-kpis/headless.ts +3 -1
  226. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  227. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
  228. package/src/plugins/builtin/composite-plugins.ts +107 -64
  229. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
  230. package/src/plugins/builtin/credit-boards/index.tsx +21 -21
  231. package/src/plugins/builtin/credit-boards/model.ts +0 -1
  232. package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
  233. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
  234. package/src/plugins/builtin/debt-maturities/client.ts +37 -9
  235. package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
  236. package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
  237. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  238. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  239. package/src/plugins/builtin/earnings-calls/data.ts +28 -7
  240. package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
  241. package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
  242. package/src/plugins/builtin/econ/calendar-model.ts +42 -17
  243. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  244. package/src/plugins/builtin/econ/index.tsx +68 -22
  245. package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
  246. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  247. package/src/plugins/builtin/econ-statistics/view.ts +17 -0
  248. package/src/plugins/builtin/executives/data.ts +17 -6
  249. package/src/plugins/builtin/executives/pane.tsx +16 -7
  250. package/src/plugins/builtin/filing-events/pane.tsx +22 -5
  251. package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
  252. package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
  253. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
  254. package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
  255. package/src/plugins/builtin/gpu/headless.ts +9 -3
  256. package/src/plugins/builtin/gpu/model.ts +14 -5
  257. package/src/plugins/builtin/help/index.tsx +1 -0
  258. package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
  259. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  260. package/src/plugins/builtin/holders/headless.ts +6 -1
  261. package/src/plugins/builtin/holders/pane.tsx +3 -0
  262. package/src/plugins/builtin/insider/index.tsx +4 -4
  263. package/src/plugins/builtin/iv-history/charts.tsx +21 -7
  264. package/src/plugins/builtin/iv-history/client.ts +12 -2
  265. package/src/plugins/builtin/iv-history/format.ts +33 -2
  266. package/src/plugins/builtin/iv-history/headless.ts +22 -10
  267. package/src/plugins/builtin/iv-history/index.tsx +3 -3
  268. package/src/plugins/builtin/iv-history/model.ts +61 -13
  269. package/src/plugins/builtin/iv-history/pane.tsx +11 -3
  270. package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
  271. package/src/plugins/builtin/jobs/pane.tsx +1 -1
  272. package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
  273. package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
  274. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  275. package/src/plugins/builtin/kelly-sizer/pane.tsx +109 -39
  276. package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
  277. package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
  278. package/src/plugins/builtin/layout-manager/index.tsx +9 -1
  279. package/src/plugins/builtin/macro-day/client.ts +24 -0
  280. package/src/plugins/builtin/macro-day/headless.ts +11 -8
  281. package/src/plugins/builtin/macro-day/index.tsx +3 -0
  282. package/src/plugins/builtin/macro-day/model.ts +11 -6
  283. package/src/plugins/builtin/macro-day/pane.tsx +12 -8
  284. package/src/plugins/builtin/macro-day/release-table.ts +40 -0
  285. package/src/plugins/builtin/macro-day/releases.ts +62 -44
  286. package/src/plugins/builtin/market-heatmap/data.ts +25 -5
  287. package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
  288. package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
  289. package/src/plugins/builtin/market-heatmap/model.ts +94 -3
  290. package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
  291. package/src/plugins/builtin/market-movers/headless.ts +31 -4
  292. package/src/plugins/builtin/market-movers/index.tsx +8 -1
  293. package/src/plugins/builtin/market-movers/model.ts +71 -17
  294. package/src/plugins/builtin/market-movers/table.ts +50 -23
  295. package/src/plugins/builtin/news/headless.ts +9 -2
  296. package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
  297. package/src/plugins/builtin/openfda/client.ts +1 -1
  298. package/src/plugins/builtin/options/carry.ts +95 -0
  299. package/src/plugins/builtin/options/index.tsx +58 -5
  300. package/src/plugins/builtin/options/strike-window.ts +183 -0
  301. package/src/plugins/builtin/options/table.ts +55 -3
  302. package/src/plugins/builtin/options/types.ts +8 -1
  303. package/src/plugins/builtin/options/view.tsx +161 -12
  304. package/src/plugins/builtin/options-positioning/headless.ts +4 -2
  305. package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
  306. package/src/plugins/builtin/options-scenario/client.ts +73 -17
  307. package/src/plugins/builtin/options-scenario/headless.ts +71 -24
  308. package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
  309. package/src/plugins/builtin/options-scenario/index.tsx +9 -2
  310. package/src/plugins/builtin/options-scenario/model.ts +33 -0
  311. package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
  312. package/src/plugins/builtin/pe-band/model.ts +2 -2
  313. package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
  314. package/src/plugins/builtin/perps/equity-row.tsx +8 -5
  315. package/src/plugins/builtin/perps/model.ts +16 -5
  316. package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
  317. package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
  318. package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
  319. package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
  320. package/src/plugins/builtin/plugin-module.ts +87 -4
  321. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
  322. package/src/plugins/builtin/portfolio-list/cli/render.ts +87 -18
  323. package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
  324. package/src/plugins/builtin/portfolio-list/headless.ts +38 -7
  325. package/src/plugins/builtin/realized-vol/headless.ts +3 -0
  326. package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
  327. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
  328. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
  329. package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
  330. package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
  331. package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
  332. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
  333. package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
  334. package/src/plugins/builtin/risk-factors/data.ts +23 -9
  335. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  336. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  337. package/src/plugins/builtin/seasonality/pane.tsx +1 -1
  338. package/src/plugins/builtin/sec/index.tsx +5 -5
  339. package/src/plugins/builtin/sectors/client.ts +10 -2
  340. package/src/plugins/builtin/sectors/headless.ts +35 -6
  341. package/src/plugins/builtin/sectors/index.tsx +18 -1
  342. package/src/plugins/builtin/sectors/sector-model.ts +150 -41
  343. package/src/plugins/builtin/shared/command-tab.ts +51 -0
  344. package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
  345. package/src/plugins/builtin/shared/members-menu.tsx +4 -4
  346. package/src/plugins/builtin/shared/report-freshness.ts +50 -2
  347. package/src/plugins/builtin/shared/ticker-request.ts +25 -1
  348. package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
  349. package/src/plugins/builtin/shared/volatility/options.ts +1 -46
  350. package/src/plugins/builtin/short-interest/index.tsx +19 -4
  351. package/src/plugins/builtin/short-interest/surface.tsx +4 -4
  352. package/src/plugins/builtin/short-volume/index.tsx +1 -10
  353. package/src/plugins/builtin/supply-chain/model.ts +1 -1
  354. package/src/plugins/builtin/ticker-detail/headless.ts +43 -21
  355. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
  356. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
  357. package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
  358. package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
  359. package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
  360. package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
  361. package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
  362. package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
  363. package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
  364. package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
  365. package/src/plugins/builtin/vol-surface/client.ts +6 -2
  366. package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
  367. package/src/plugins/builtin/vol-surface/headless.ts +124 -20
  368. package/src/plugins/builtin/vol-surface/model.ts +23 -11
  369. package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
  370. package/src/plugins/builtin/vol-surface/tables.ts +124 -0
  371. package/src/plugins/builtin/world-indices/client.ts +22 -4
  372. package/src/plugins/builtin/world-indices/headless.ts +54 -27
  373. package/src/plugins/builtin/world-indices/model.ts +4 -2
  374. package/src/plugins/builtin/world-indices/table.tsx +13 -2
  375. package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
  376. package/src/plugins/builtin/world-venue-map/index.ts +1 -1
  377. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  378. package/src/plugins/builtin-editorial.ts +126 -0
  379. package/src/plugins/catalog-backend.ts +16 -9
  380. package/src/plugins/catalog-browser.ts +127 -63
  381. package/src/plugins/catalog-manifest.ts +73 -40
  382. package/src/plugins/catalog-ui.ts +26 -6
  383. package/src/plugins/extracted-plugins.ts +7 -1
  384. package/src/plugins/ownership.ts +150 -7
  385. package/src/plugins/plugin-functions.ts +54 -0
  386. package/src/plugins/plugin-off.ts +36 -0
  387. package/src/plugins/registry/host-actions.ts +16 -0
  388. package/src/plugins/registry/index.ts +84 -24
  389. package/src/plugins/registry/plugin-state.ts +25 -1
  390. package/src/plugins/runtime/context.tsx +29 -0
  391. package/src/react/input.ts +48 -1
  392. package/src/remote/app-host.tsx +5 -0
  393. package/src/remote/controller.ts +3 -53
  394. package/src/remote/matching.ts +69 -0
  395. package/src/remote/schema.ts +1 -1
  396. package/src/remote/semantic-tree.tsx +14 -0
  397. package/src/remote/types.ts +2 -0
  398. package/src/renderers/dom/desktop/icons.tsx +14 -0
  399. package/src/renderers/dom/desktop/query-bar.tsx +59 -32
  400. package/src/renderers/dom/host/box.tsx +1 -1
  401. package/src/renderers/dom/host/drag-shield.ts +17 -6
  402. package/src/renderers/dom/host/live-frame.ts +10 -2
  403. package/src/renderers/dom/input-host.tsx +6 -1
  404. package/src/renderers/dom/styles.css +13 -0
  405. package/src/renderers/electrobun/bun/desktop/backend.ts +5 -1
  406. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
  407. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -1
  408. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  409. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  410. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
  411. package/src/renderers/electrobun/view/main.tsx +4 -1
  412. package/src/renderers/opentui/input-host.tsx +6 -1
  413. package/src/renderers/opentui/start.tsx +9 -0
  414. package/src/shares/portable-layout.ts +16 -6
  415. package/src/sources/gloomberb-cloud/index.ts +19 -10
  416. package/src/sources/gloomberb-cloud/normalizers.ts +4 -1
  417. package/src/sources/listing-symbols.ts +11 -5
  418. package/src/sources/provider-errors.ts +88 -3
  419. package/src/sources/provider-router/cached-routes.ts +8 -4
  420. package/src/sources/provider-router/financial-routes.ts +11 -5
  421. package/src/sources/provider-router/financials.ts +42 -15
  422. package/src/sources/provider-router/history.ts +27 -11
  423. package/src/sources/provider-router/index.ts +1 -1
  424. package/src/sources/provider-router/primary.ts +23 -5
  425. package/src/sources/sec-edgar.ts +68 -8
  426. package/src/sync/core-contributors.ts +4 -3
  427. package/src/test-support/cli-context.ts +2 -1
  428. package/src/test-support/data-provider.ts +34 -0
  429. package/src/test-support/shell-pane-keys.tsx +1 -0
  430. package/src/theme/series-colors.ts +46 -0
  431. package/src/theme/themes.ts +149 -0
  432. package/src/tickers/search/index.ts +22 -2
  433. package/src/tickers/search/ranking.ts +45 -1
  434. package/src/tickers/search/types.ts +3 -1
  435. package/src/time-series/resolve.ts +1 -14
  436. package/src/types/config.ts +61 -1
  437. package/src/types/headless.ts +11 -0
  438. package/src/types/instrument.ts +8 -0
  439. package/src/types/plugin.ts +49 -3
  440. package/src/ui/host.tsx +8 -4
  441. package/src/ui/index.tsx +2 -0
  442. package/src/utils/cli-output.ts +14 -1
  443. package/src/utils/exchanges.ts +13 -0
  444. package/src/utils/format.ts +2 -1
  445. package/src/utils/fx-market-hours.ts +34 -0
  446. package/src/utils/option-expiry.ts +126 -0
  447. package/src/utils/price-history.ts +39 -6
  448. package/src/utils/rpc-timeout-error.ts +6 -2
  449. package/src/utils/sec-eps-basis.ts +159 -30
  450. package/src/utils/sec.ts +38 -34
  451. package/src/utils/throttled-fetch.ts +51 -22
@@ -15,7 +15,7 @@ import {
15
15
  type ShortcutDisplayMode,
16
16
  } from "../../../utils/shortcut-labels";
17
17
  import { PANE_MANAGEMENT_ACCELERATORS, type PaneManagementAccelerators } from "./shortcuts";
18
- import { t } from "../../../i18n";
18
+ import { t, tf } from "../../../i18n";
19
19
  import { displayWidth } from "../../../utils/format";
20
20
  import { fireAndForget } from "../../../utils/fire-and-forget";
21
21
 
@@ -42,6 +42,14 @@ export interface PaneMenuQuickSetting {
42
42
  toggle: () => void;
43
43
  }
44
44
 
45
+ /** Move to New Layout and Move Back as the pane menu offers them; either may be absent. */
46
+ export interface PaneMenuLayoutMove {
47
+ /** Absent when the pane is alone in its layout. */
48
+ moveToNewLayout?: () => void;
49
+ /** Present when the pane was moved here from another layout. */
50
+ moveBack?: { layoutName: string; onSelect: () => void };
51
+ }
52
+
45
53
  /**
46
54
  * The pane's own actions at the top of its menu: every footer hint with its
47
55
  * key, a footer shortcut such as `!`, then what the pane's kit controls add
@@ -93,6 +101,7 @@ export function menuForPane(
93
101
  quickSettings: PaneMenuQuickSetting[] = [],
94
102
  fullscreen?: { active: boolean; toggle: () => void },
95
103
  paneItems: ContextMenuItem[] = [],
104
+ layoutMove: PaneMenuLayoutMove = {},
96
105
  ): ContextMenuItem[] {
97
106
  const baseActions: ContextMenuItem[] = [...paneItems];
98
107
  if (baseActions.length > 0) baseActions.push(contextMenuDivider("pane:own-actions-divider"));
@@ -147,6 +156,24 @@ export function menuForPane(
147
156
  });
148
157
  }
149
158
 
159
+ if (layoutMove.moveToNewLayout) {
160
+ baseActions.push({
161
+ id: "move-to-new-layout",
162
+ label: "Move to New Layout",
163
+ accelerator: accelerators.newLayout,
164
+ onSelect: layoutMove.moveToNewLayout,
165
+ });
166
+ }
167
+ if (layoutMove.moveBack) {
168
+ baseActions.push({
169
+ id: "move-back",
170
+ label: tf("Move Back to {layout}", { layout: layoutMove.moveBack.layoutName }),
171
+ // The shortcut moves a pane back only when it is alone, where Move to New Layout is not offered.
172
+ accelerator: layoutMove.moveToNewLayout ? undefined : accelerators.newLayout,
173
+ onSelect: layoutMove.moveBack.onSelect,
174
+ });
175
+ }
176
+
150
177
  if (pane.floating) {
151
178
  baseActions.push({
152
179
  id: "dock",
@@ -172,7 +172,8 @@ export function useShellPaneActions({
172
172
  paneTypes: pluginRegistry.panes,
173
173
  apply: persistLayout,
174
174
  notify: pluginRegistry.notify,
175
- onRevert: () => pluginRegistry.updateLayout(visibleLayout),
175
+ // Back through the shell's save, which puts hidden panes back too.
176
+ onRevert: () => persistLayout(visibleLayout),
176
177
  });
177
178
  return true;
178
179
  }, [contentHeight, persistLayout, pluginRegistry, visibleLayout, width]);
@@ -1,4 +1,4 @@
1
- import { memo, useCallback, useMemo, useRef } from "react";
1
+ import { memo, useCallback, useMemo, useRef, type RefObject } from "react";
2
2
  import { Box, type BoxRenderable, type LiveBoxFrame } from "../../../../ui";
3
3
  import { resolveOccludedPaneIds } from "../pane-occlusion";
4
4
  import type {
@@ -9,8 +9,8 @@ import type {
9
9
  ResolvedPane,
10
10
  } from "../../../../layout/pane-manager";
11
11
  import { colors } from "../../../../theme/colors";
12
- import { constrainFloatingRectToBounds } from "../drag";
13
- import { useLiveBoxFrame, useLiveDrag, type LiveDragGeometry, type LiveDragStore } from "../drag/live";
12
+ import { constrainFloatingRectToBounds, sameRect } from "../drag";
13
+ import { useLiveBoxFrame, useLiveDrag, useLiveResizeRect, type LiveDragGeometry, type LiveDragStore } from "../drag/live";
14
14
  import { pathKey } from "../../window-edit/mode";
15
15
  import { FloatingPaneWrapper } from "../../floating-pane";
16
16
  import { PaneContent } from "../../pane/content";
@@ -207,7 +207,16 @@ const FloatingPaneFrame = memo(function FloatingPaneFrame({
207
207
  getPaneQuickSettings,
208
208
  actions,
209
209
  zIndex,
210
- }: PaneLayerProps & { rect: FloatingRect; zIndex: number }) {
210
+ frameRef,
211
+ contentPinned = false,
212
+ }: PaneLayerProps & {
213
+ rect: FloatingRect;
214
+ zIndex: number;
215
+ /** Desktop: the frame a resize sizes outside React. */
216
+ frameRef?: RefObject<BoxRenderable | null>;
217
+ /** Desktop: a resize is under way, so the content keeps the size it last rendered at. */
218
+ contentPinned?: boolean;
219
+ }) {
211
220
  const paneId = pane.instance.instanceId;
212
221
  const quickSettings = usePaneQuickSettings(getPaneQuickSettings, paneId);
213
222
  const nativeMove = nativePaneChrome && movable;
@@ -252,6 +261,7 @@ const FloatingPaneFrame = memo(function FloatingPaneFrame({
252
261
  onResizeMouseDown={nativeMove ? (event) => actions.floatResizeStart(paneId, rect, event) : undefined}
253
262
  onResizeMouseDrag={nativeMove ? actions.drag : undefined}
254
263
  onResizeMouseDragEnd={nativeMove ? actions.drag : undefined}
264
+ frameRef={frameRef}
255
265
  >
256
266
  <PaneFooterKeys paneId={paneId} footer={footer} focused={focused} />
257
267
  <PaneContent
@@ -265,6 +275,7 @@ const FloatingPaneFrame = memo(function FloatingPaneFrame({
265
275
  inView={inView}
266
276
  onClose={actions.close}
267
277
  closePane={actions.close}
278
+ pinned={contentPinned}
268
279
  />
269
280
  </FloatingPaneWrapper>
270
281
  );
@@ -278,8 +289,11 @@ const FloatingPaneFrame = memo(function FloatingPaneFrame({
278
289
  * On the desktop a move never renders: the pane's layer is promoted at the
279
290
  * press and each frame of the move only writes its transform (see
280
291
  * `useLiveBoxFrame`), so nothing inside it re-renders, lays out or repaints
281
- * until the release commits the new position. A resize, and every terminal
282
- * frame, redraws it at the drag's rect.
292
+ * until the release commits the new position. A resize writes the frame's
293
+ * size the same way, on the same layer, and the header, body and footer
294
+ * stretch with it; the content keeps its size and renders at the new one a
295
+ * few times a second (`useLiveResizeRect`). Every terminal frame redraws it
296
+ * at the drag's rect.
283
297
  */
284
298
  const FloatingPaneLayer = memo(function FloatingPaneLayer({
285
299
  live,
@@ -293,13 +307,20 @@ const FloatingPaneLayer = memo(function FloatingPaneLayer({
293
307
  const slide = frame.nativePaneChrome;
294
308
  const selectRedraw = useCallback((geometry: LiveDragGeometry) => {
295
309
  if (geometry.floating?.paneId !== paneId) return null;
296
- // A desktop move follows on the pane's own layer instead.
297
- if (slide && geometry.paneDrag?.paneId === paneId) return null;
310
+ // A desktop move or resize follows by restyling the pane instead.
311
+ if (slide && (geometry.paneDrag?.paneId === paneId || geometry.paneResize?.paneId === paneId)) return null;
298
312
  return geometry.floating.rect;
299
313
  }, [paneId, slide]);
300
314
  const dragRect = useLiveDrag(live, selectRedraw);
315
+ const selectResizing = useCallback((geometry: LiveDragGeometry) => (
316
+ slide && geometry.paneResize?.paneId === paneId
317
+ ), [paneId, slide]);
318
+ const resizing = useLiveDrag(live, selectResizing);
301
319
  const layerRef = useRef<BoxRenderable | null>(null);
302
320
  const frameOf = useMemo(() => (slide ? (geometry: LiveDragGeometry): LiveBoxFrame | null => {
321
+ // A resize keeps the pane on its own layer too: the panes it uncovers or
322
+ // covers never repaint, only the pane does.
323
+ if (geometry.paneResize?.paneId === paneId) return { dx: 0, dy: 0 };
303
324
  if (geometry.paneDrag?.paneId !== paneId) return null;
304
325
  const moving = geometry.floating?.paneId === paneId ? geometry.floating.rect : null;
305
326
  // Promoted from the press, so the first move already only slides it.
@@ -308,8 +329,28 @@ const FloatingPaneLayer = memo(function FloatingPaneLayer({
308
329
  return { dx: at.x - rect.x, dy: at.y - rect.y };
309
330
  } : null), [contentHeight, paneId, rect.x, rect.y, slide, width]);
310
331
  useLiveBoxFrame(live, layerRef, frameOf);
311
- const preview = dragRect ? constrainFloatingRectToBounds(dragRect, width, contentHeight) : rect;
312
- const content = <FloatingPaneFrame {...frame} rect={preview} zIndex={zIndex} />;
332
+ const frameRef = useRef<BoxRenderable | null>(null);
333
+ const sizeOf = useMemo(() => (slide ? (geometry: LiveDragGeometry): LiveBoxFrame | null => {
334
+ if (geometry.paneResize?.paneId !== paneId || geometry.floating?.paneId !== paneId) return null;
335
+ const at = constrainFloatingRectToBounds(geometry.floating.rect, width, contentHeight);
336
+ return { width: at.width, height: at.height };
337
+ } : null), [contentHeight, paneId, slide, width]);
338
+ useLiveBoxFrame(live, frameRef, sizeOf);
339
+ const resizeRect = useLiveResizeRect(live, paneId, slide);
340
+ const preview = dragRect
341
+ ? constrainFloatingRectToBounds(dragRect, width, contentHeight)
342
+ : resizeRect && !sameRect(resizeRect, rect)
343
+ ? constrainFloatingRectToBounds(resizeRect, width, contentHeight)
344
+ : rect;
345
+ const content = (
346
+ <FloatingPaneFrame
347
+ {...frame}
348
+ rect={preview}
349
+ zIndex={zIndex}
350
+ frameRef={slide ? frameRef : undefined}
351
+ contentPinned={resizing}
352
+ />
353
+ );
313
354
  if (!slide) return content;
314
355
  return (
315
356
  <Box
@@ -33,6 +33,7 @@ interface ShellPaneManagementShortcutOptions {
33
33
  startWindowMode(paneId?: string, mode?: WindowEditMode): void;
34
34
  toggleFocusedPaneFullscreen(): boolean;
35
35
  toggleFocusedPaneFloating(): boolean;
36
+ toggleFocusedPaneNewLayout(): boolean;
36
37
  }
37
38
 
38
39
  export function useShellPaneManagementShortcuts({
@@ -54,6 +55,7 @@ export function useShellPaneManagementShortcuts({
54
55
  startWindowMode,
55
56
  toggleFocusedPaneFullscreen,
56
57
  toggleFocusedPaneFloating,
58
+ toggleFocusedPaneNewLayout,
57
59
  }: ShellPaneManagementShortcutOptions): void {
58
60
  const doubleEscapeCloseRef = useRef(createDoubleEscapeCloseState());
59
61
  const keybindings = useKeybindings();
@@ -153,6 +155,9 @@ export function useShellPaneManagementShortcuts({
153
155
  case "toggle-floating":
154
156
  handled = toggleFocusedPaneFloating();
155
157
  break;
158
+ case "new-layout":
159
+ handled = toggleFocusedPaneNewLayout();
160
+ break;
156
161
  case "pop-out":
157
162
  handled = popOutFocusedPane();
158
163
  break;
@@ -0,0 +1,116 @@
1
+ import { useCallback, type Dispatch } from "react";
2
+ import { formatAdvertisedChord, type ResolvedKeybindings } from "../../../../app/keybindings";
3
+ import { tf } from "../../../../i18n";
4
+ import {
5
+ createSavedLayoutId,
6
+ paneMoveBackTarget,
7
+ paneNewLayoutBlock,
8
+ type PaneMoveBackTarget,
9
+ type PaneNewLayoutBlock,
10
+ } from "../../../../layout/pane-layout-move";
11
+ import type { PluginRegistry } from "../../../../plugins/registry";
12
+ import { resolveTickerForPane, type AppAction, type AppState } from "../../../../state/app/context";
13
+ import { findPaneInstance } from "../../../../types/config";
14
+ import type { ShortcutDisplayMode } from "../../../../utils/shortcut-labels";
15
+ import { getBasePaneDisplayTitle } from "../../pane/title";
16
+ import type { PaneMenuLayoutMove } from "../menu";
17
+
18
+ const BLOCK_MESSAGES: Partial<Record<PaneNewLayoutBlock, string>> = {
19
+ "only-pane": "Already the only pane in this layout",
20
+ "no-ticker": "Select a ticker in the list this pane follows first",
21
+ };
22
+
23
+ // The first move of a session says how to undo it; after that the layout tab is enough.
24
+ let moveBackHintShown = false;
25
+
26
+ interface UseShellPaneLayoutMovesOptions {
27
+ closePaneMenu: () => void;
28
+ dispatch: Dispatch<AppAction>;
29
+ keybindings: ResolvedKeybindings;
30
+ /** Leaves anything that shows this pane over the layout, such as fullscreen, before it moves. */
31
+ leavePane: (paneId: string) => void;
32
+ pluginRegistry: PluginRegistry;
33
+ shortcutDisplayMode: ShortcutDisplayMode;
34
+ stateRef: { current: AppState };
35
+ }
36
+
37
+ /**
38
+ * Move to New Layout and Move Back for the shell's pane menu, its shortcut
39
+ * and the command bar. The layout work is in `layout/pane-layout-move`.
40
+ */
41
+ export function useShellPaneLayoutMoves({
42
+ closePaneMenu,
43
+ dispatch,
44
+ keybindings,
45
+ leavePane,
46
+ pluginRegistry,
47
+ shortcutDisplayMode,
48
+ stateRef,
49
+ }: UseShellPaneLayoutMovesOptions) {
50
+ const blockFor = useCallback((paneId: string): PaneNewLayoutBlock | null => {
51
+ const state = stateRef.current;
52
+ return paneNewLayoutBlock(state.config.layout, paneId, (instanceId) => resolveTickerForPane(state, instanceId));
53
+ }, [stateRef]);
54
+
55
+ const moveBackTargetFor = useCallback(
56
+ (paneId: string): PaneMoveBackTarget | null => paneMoveBackTarget(stateRef.current.config, paneId),
57
+ [stateRef],
58
+ );
59
+
60
+ const movePaneToNewLayout = useCallback((paneId: string): boolean => {
61
+ const block = blockFor(paneId);
62
+ if (block === "missing" || block === "detached") return false;
63
+ const blockMessage = block ? BLOCK_MESSAGES[block] : undefined;
64
+ if (blockMessage) {
65
+ pluginRegistry.notify({ body: blockMessage, type: "info" });
66
+ return true;
67
+ }
68
+ const state = stateRef.current;
69
+ const instance = findPaneInstance(state.config.layout, paneId);
70
+ if (!instance) return false;
71
+ const def = pluginRegistry.panes.get(instance.paneId);
72
+ const name = (def ? getBasePaneDisplayTitle(state, instance, def, pluginRegistry.panes) : instance.title)?.trim()
73
+ || instance.paneId;
74
+ closePaneMenu();
75
+ leavePane(paneId);
76
+ dispatch({ type: "MOVE_PANE_TO_NEW_LAYOUT", paneId, name, sourceLayoutId: createSavedLayoutId() });
77
+ if (!moveBackHintShown) {
78
+ moveBackHintShown = true;
79
+ const shortcut = formatAdvertisedChord(keybindings, "pane-new-layout", shortcutDisplayMode);
80
+ pluginRegistry.notify({
81
+ body: shortcut
82
+ ? tf("Moved to a layout of its own. {shortcut} moves it back.", { shortcut })
83
+ : "Moved to a layout of its own. Move Back in its menu returns it.",
84
+ type: "info",
85
+ });
86
+ }
87
+ return true;
88
+ }, [blockFor, closePaneMenu, dispatch, keybindings, leavePane, pluginRegistry, shortcutDisplayMode, stateRef]);
89
+
90
+ const movePaneBack = useCallback((paneId: string): boolean => {
91
+ if (!moveBackTargetFor(paneId)) return false;
92
+ closePaneMenu();
93
+ leavePane(paneId);
94
+ dispatch({ type: "MOVE_PANE_BACK", paneId });
95
+ return true;
96
+ }, [closePaneMenu, dispatch, leavePane, moveBackTargetFor]);
97
+
98
+ /** The shortcut: a pane alone in the layout it was moved to goes back, any other gets a layout of its own. */
99
+ const togglePaneNewLayout = useCallback((paneId: string | null): boolean => {
100
+ if (!paneId) return false;
101
+ if (blockFor(paneId) === "only-pane" && movePaneBack(paneId)) return true;
102
+ return movePaneToNewLayout(paneId);
103
+ }, [blockFor, movePaneBack, movePaneToNewLayout]);
104
+
105
+ /** What the pane menu offers: Move to New Layout unless the pane is alone, Move Back when it came from elsewhere. */
106
+ const paneLayoutMoveMenu = useCallback((paneId: string): PaneMenuLayoutMove => {
107
+ const block = blockFor(paneId);
108
+ const back = moveBackTargetFor(paneId);
109
+ return {
110
+ ...(block === null || block === "no-ticker" ? { moveToNewLayout: () => { movePaneToNewLayout(paneId); } } : {}),
111
+ ...(back ? { moveBack: { layoutName: back.name, onSelect: () => { movePaneBack(paneId); } } } : {}),
112
+ };
113
+ }, [blockFor, moveBackTargetFor, movePaneBack, movePaneToNewLayout]);
114
+
115
+ return { movePaneBack, movePaneToNewLayout, paneLayoutMoveMenu, togglePaneNewLayout };
116
+ }
@@ -12,6 +12,7 @@ export type PaneManagementShortcut =
12
12
  | "menu"
13
13
  | "settings"
14
14
  | "toggle-fullscreen"
15
+ | "new-layout"
15
16
  | "toggle-floating"
16
17
  | "pop-out"
17
18
  | "copy-screenshot"
@@ -28,6 +29,7 @@ const PANE_ACTION_TO_SHORTCUT: Partial<Record<CoreKeybindingActionId, PaneManage
28
29
  "pane-menu": "menu",
29
30
  "pane-settings": "settings",
30
31
  "pane-fullscreen": "toggle-fullscreen",
32
+ "pane-new-layout": "new-layout",
31
33
  "pane-float": "toggle-floating",
32
34
  "pane-pop-out": "pop-out",
33
35
  "pane-screenshot": "copy-screenshot",
@@ -42,7 +44,7 @@ const PANE_ACTION_TO_SHORTCUT: Partial<Record<CoreKeybindingActionId, PaneManage
42
44
  };
43
45
 
44
46
  export type PaneManagementAccelerators = Record<
45
- "menu" | "settings" | "fullscreen" | "toggleFloating" | "popOut" | "copyScreenshot" | "exportCsv" | "share" | "close"
47
+ "menu" | "settings" | "fullscreen" | "newLayout" | "toggleFloating" | "popOut" | "copyScreenshot" | "exportCsv" | "share" | "close"
46
48
  | "closeAllFloating" | "layoutGallery" | "gridlockAll" | "windowMode" | "windowResizeMode",
47
49
  string | undefined
48
50
  >;
@@ -53,6 +55,7 @@ export function paneManagementAccelerators(keybindings: ResolvedKeybindings): Pa
53
55
  menu: menuAcceleratorFor(keybindings, "pane-menu"),
54
56
  settings: menuAcceleratorFor(keybindings, "pane-settings"),
55
57
  fullscreen: menuAcceleratorFor(keybindings, "pane-fullscreen"),
58
+ newLayout: menuAcceleratorFor(keybindings, "pane-new-layout"),
56
59
  toggleFloating: menuAcceleratorFor(keybindings, "pane-float"),
57
60
  popOut: menuAcceleratorFor(keybindings, "pane-pop-out"),
58
61
  copyScreenshot: menuAcceleratorFor(keybindings, "pane-screenshot"),
@@ -98,7 +101,7 @@ export function inputCaptureAllowsPaneManagementShortcut(
98
101
  // A screenshot does not edit the field that captured input. Keep it available
99
102
  // from composers (ASKG, chat, etc.) on both desktop modifier layouts.
100
103
  if (shortcut === "copy-screenshot") return event.ctrl || event.meta || event.super === true;
101
- if (shortcut === "toggle-fullscreen") return event.meta || event.super === true;
104
+ if (shortcut === "toggle-fullscreen" || shortcut === "new-layout") return event.meta || event.super === true;
102
105
  if (shortcut !== "close" && shortcut !== "close-all-floating") return false;
103
106
  return event.meta || event.super || event.targetEditable !== true;
104
107
  }
@@ -1,8 +1,22 @@
1
1
  import {
2
2
  removeUnavailablePaneTypes,
3
+ restoreHiddenPanes,
3
4
  type PaneTypeAvailability,
4
5
  } from "../../../layout/pane-manager";
5
- import type { LayoutConfig } from "../../../types/config";
6
+ import type { PluginRegistry } from "../../../plugins/registry";
7
+ import type { LayoutConfig, PaneInstanceConfig } from "../../../types/config";
8
+
9
+ /** The pane types of every plugin that is switched off. */
10
+ export function collectDisabledPaneIds(
11
+ pluginRegistry: Pick<PluginRegistry, "getPluginPaneIds">,
12
+ disabledPlugins: readonly string[],
13
+ ): Set<string> {
14
+ const paneIds = new Set<string>();
15
+ for (const pluginId of new Set(disabledPlugins)) {
16
+ for (const paneId of pluginRegistry.getPluginPaneIds(pluginId)) paneIds.add(paneId);
17
+ }
18
+ return paneIds;
19
+ }
6
20
 
7
21
  export function resolveShellVisibleLayout(
8
22
  layout: LayoutConfig,
@@ -15,3 +29,32 @@ export function resolveShellVisibleLayout(
15
29
  { disabledPaneIds },
16
30
  );
17
31
  }
32
+
33
+ /** A pane the shell leaves off screen: its plugin is off, or nothing registered its type. */
34
+ function isShellHiddenPane(
35
+ instance: PaneInstanceConfig,
36
+ disabledPaneIds: ReadonlySet<string>,
37
+ registeredPaneIds: PaneTypeAvailability,
38
+ ): boolean {
39
+ const registered = typeof registeredPaneIds === "function"
40
+ ? !!registeredPaneIds(instance.paneId)
41
+ : registeredPaneIds.has(instance.paneId);
42
+ return !registered || disabledPaneIds.has(instance.paneId);
43
+ }
44
+
45
+ /**
46
+ * What to save after an edit made on the visible layout: the edit, with every
47
+ * pane the shell hid put back from the saved layout.
48
+ */
49
+ export function restoreShellHiddenPanes(
50
+ saved: LayoutConfig,
51
+ edited: LayoutConfig,
52
+ disabledPaneIds: ReadonlySet<string>,
53
+ registeredPaneIds: PaneTypeAvailability,
54
+ ): LayoutConfig {
55
+ return restoreHiddenPanes(
56
+ saved,
57
+ edited,
58
+ (instance) => isShellHiddenPane(instance, disabledPaneIds, registeredPaneIds),
59
+ );
60
+ }
@@ -13,6 +13,7 @@ import { blendHex, hoverBg } from "../../theme/colors";
13
13
  import { t, tf } from "../../i18n";
14
14
  import { useThemeColors } from "../../theme/theme-context";
15
15
  import { useAppDispatch, useAppSelector } from "../../state/app/context";
16
+ import { useAppChromeShown } from "./presentation";
16
17
  import { useViewport } from "../../react/input";
17
18
  import {
18
19
  advertisedChord,
@@ -53,6 +54,7 @@ import {
53
54
  useClaimedStatusWidgetColumns,
54
55
  usePublishStatusWidgetRoom,
55
56
  } from "./status-widget-space";
57
+ import { StarPromptStatusLine } from "../../app/star-prompt/status-line";
56
58
 
57
59
  type StatusBarEvent = { stopPropagation?: () => void; preventDefault?: () => void };
58
60
  type HoveredControl = string | null;
@@ -145,7 +147,7 @@ export function StatusBar({ onOpenChangelog }: { onOpenChangelog?: (version: str
145
147
  const { width: termWidth } = useViewport();
146
148
  const layouts = useAppSelector((state) => state.config.layouts);
147
149
  const activeLayoutIdx = useAppSelector((state) => state.config.activeLayoutIndex);
148
- const statusBarVisible = useAppSelector((state) => state.statusBarVisible);
150
+ const statusBarVisible = useAppChromeShown().statusBar;
149
151
  const layout = useAppSelector((state) => state.config.layout);
150
152
  const { transientLayout } = useTransientLayout();
151
153
  const [hoveredControl, setHoveredControl] = useState<string | null>(null);
@@ -498,6 +500,7 @@ function TerminalStatusBar({
498
500
  {showTidyWindows && <TerminalTidyWindows {...props} />}
499
501
  <Box flexGrow={1} minWidth={0} />
500
502
  <StatusBarSummary nativePaneChrome={false} {...props} />
503
+ <StarPromptStatusLine />
501
504
  <PluginSlot name="status:widget" />
502
505
  <StatusBarFeedback nativePaneChrome={false} {...props} />
503
506
  </Box>
@@ -30,23 +30,27 @@ interface ParsedLine {
30
30
  indent?: number;
31
31
  }
32
32
 
33
+ // A backslash escapes the punctuation markdown would read ("\\*" is a star), and
34
+ // an italic span may hold a bold one ("*covers **96%** of value*").
33
35
  const INLINE_RE =
34
- /(\*\*(?<bold>[^*]+)\*\*|(?<!\*)\*(?!\*)(?<italic>[^*]+)\*(?!\*)|`(?<code>[^`]+)`|~~(?<strike>[^~]+)~~)/g;
36
+ /(\\(?<escaped>[\\`*_~#>[\]()-])|\*\*(?<bold>[^*]+)\*\*|(?<!\*)\*(?!\*)(?<italic>(?:[^*]|\*\*[^*]+\*\*)+?)\*(?!\*)|`(?<code>[^`]+)`|~~(?<strike>[^~]+)~~)/g;
35
37
 
36
38
  function parseInlineMarkdown(text: string): StyledSegment[] {
37
39
  const segments: StyledSegment[] = [];
38
40
  let cursor = 0;
39
41
 
40
- INLINE_RE.lastIndex = 0;
42
+ const pattern = new RegExp(INLINE_RE.source, "g");
41
43
  let match: RegExpExecArray | null;
42
- while ((match = INLINE_RE.exec(text)) !== null) {
44
+ while ((match = pattern.exec(text)) !== null) {
43
45
  if (match.index > cursor) {
44
46
  segments.push({ text: text.slice(cursor, match.index) });
45
47
  }
46
- if (match.groups?.bold != null) {
48
+ if (match.groups?.escaped != null) {
49
+ segments.push({ text: match.groups.escaped });
50
+ } else if (match.groups?.bold != null) {
47
51
  segments.push({ text: match.groups.bold, bold: true });
48
52
  } else if (match.groups?.italic != null) {
49
- segments.push({ text: match.groups.italic, italic: true });
53
+ for (const inner of parseInlineMarkdown(match.groups.italic)) segments.push({ ...inner, italic: true });
50
54
  } else if (match.groups?.code != null) {
51
55
  segments.push({ text: match.groups.code, code: true });
52
56
  } else if (match.groups?.strike != null) {
@@ -209,6 +209,17 @@ function useItemsById<T>(scene: MetricTreemapScene<T>, items: readonly MetricTre
209
209
 
210
210
  const VISIBLE_FADE_DISTANCE = 0.03;
211
211
 
212
+ /** Text up a tall tile reads bottom to top, as a chart's vertical axis title does; the move sits right of the ticker. */
213
+ const VERTICAL_LABEL_STYLE: CSSProperties = {
214
+ position: "relative",
215
+ display: "block",
216
+ flexShrink: 0,
217
+ maxHeight: "100%",
218
+ overflow: "hidden",
219
+ writingMode: "vertical-rl",
220
+ transform: "rotate(180deg)",
221
+ };
222
+
212
223
  const DomHeatTile = memo(function DomHeatTile({
213
224
  id, rect, canvasWidth, canvasHeight, gap, label, value, valueSuffix, background, foreground,
214
225
  selected, reducedMotion, glide, pulseCount, events,
@@ -230,7 +241,10 @@ const DomHeatTile = memo(function DomHeatTile({
230
241
  events: SurfaceEvents;
231
242
  }) {
232
243
  const text = heatTileLabelPx(rect.width - gap * 2, rect.height - gap * 2, label, value);
233
- const shownValue = heatTileValueWithSuffix(value, valueSuffix, { width: rect.width - gap * 2, valuePx: text.valuePx });
244
+ const shownValue = heatTileValueWithSuffix(value, valueSuffix, {
245
+ width: (text.vertical ? rect.height : rect.width) - gap * 2,
246
+ valuePx: text.valuePx,
247
+ });
234
248
  // Where this tile sat before the last relayout, kept to slide it from there.
235
249
  const placedRef = useRef(rect);
236
250
  const glideRef = useRef<{ dx: number; dy: number; count: number }>({ dx: 0, dy: 0, count: 0 });
@@ -305,13 +319,23 @@ const DomHeatTile = memo(function DomHeatTile({
305
319
  const lineStyle: CSSProperties = {
306
320
  position: "relative",
307
321
  display: "block",
308
- maxWidth: "100%",
322
+ maxWidth: text.vertical ? undefined : "100%",
309
323
  overflow: "hidden",
310
324
  whiteSpace: "nowrap",
311
325
  textAlign: "center",
312
326
  letterSpacing: 0,
313
327
  color: foreground,
314
328
  };
329
+ const lines = (
330
+ <>
331
+ {text.tier !== "none" && (
332
+ <Text style={{ ...lineStyle, fontSize: text.tickerPx, fontWeight: 700, lineHeight: 1.12 }}>{label}</Text>
333
+ )}
334
+ {text.tier === "full" && value && (
335
+ <Text style={{ ...lineStyle, fontSize: text.valuePx, fontWeight: 500, lineHeight: 1.12 }}>{shownValue}</Text>
336
+ )}
337
+ </>
338
+ );
315
339
  return (
316
340
  <Box
317
341
  data-gloom-role="heat-tile"
@@ -347,12 +371,7 @@ const DomHeatTile = memo(function DomHeatTile({
347
371
  }}
348
372
  />
349
373
  )}
350
- {text.tier !== "none" && (
351
- <Text style={{ ...lineStyle, fontSize: text.tickerPx, fontWeight: 700, lineHeight: 1.12 }}>{label}</Text>
352
- )}
353
- {text.tier === "full" && value && (
354
- <Text style={{ ...lineStyle, fontSize: text.valuePx, fontWeight: 500, lineHeight: 1.12 }}>{shownValue}</Text>
355
- )}
374
+ {text.vertical ? <Box style={VERTICAL_LABEL_STYLE}>{lines}</Box> : lines}
356
375
  </Box>
357
376
  );
358
377
  });
@@ -11,11 +11,16 @@ export interface HeatTileLabel {
11
11
  /** Font sizes in px, for the desktop and the web. */
12
12
  tickerPx: number;
13
13
  valuePx: number;
14
+ /** The label runs up the tile: it fits only along a tall tile's length. */
15
+ vertical: boolean;
14
16
  }
15
17
 
16
18
  /** Advance of one monospace glyph, in em. */
17
19
  const MONO_CHAR_EM = 0.62;
20
+ /** The margin either side of the text while it has room to grow. */
18
21
  const TILE_PAD_X_PX = 3;
22
+ /** The margin it gives way to rather than drop below its smallest size. */
23
+ const MIN_TILE_PAD_X_PX = 1;
19
24
  const TILE_PAD_Y_PX = 2;
20
25
  const MAX_TICKER_PX = 26;
21
26
  const MIN_TICKER_PX = 9;
@@ -27,36 +32,62 @@ const LINE_HEIGHT = 1.12;
27
32
  /** Text grows with the tile's area, not just its width, so a long thin tile stays calm. */
28
33
  const AREA_FONT_DIVISOR = 4.4;
29
34
 
30
- const NO_LABEL: HeatTileLabel = { tier: "none", tickerPx: 0, valuePx: 0 };
35
+ const NO_LABEL: HeatTileLabel = { tier: "none", tickerPx: 0, valuePx: 0, vertical: false };
31
36
 
32
- export function heatTileLabelPx(width: number, height: number, ticker: string, value: string | null): HeatTileLabel {
33
- const innerWidth = width - TILE_PAD_X_PX * 2;
37
+ /**
38
+ * The largest font size at which text `ems` glyph advances long crosses
39
+ * `width` with the full margin. Rather than drop below `floor`, the text takes
40
+ * the margin, down to the narrowest one.
41
+ */
42
+ function sizeAcross(width: number, ems: number, floor: number): number {
43
+ const size = (width - TILE_PAD_X_PX * 2) / ems;
44
+ if (size >= floor) return size;
45
+ return (width - MIN_TILE_PAD_X_PX * 2) / ems >= floor ? floor : size;
46
+ }
47
+
48
+ /** The label laid across a `width` by `height` box. */
49
+ function fitLabel(width: number, height: number, ticker: string, value: string | null): HeatTileLabel {
50
+ const roomWidth = width - MIN_TILE_PAD_X_PX * 2;
34
51
  const innerHeight = height - TILE_PAD_Y_PX * 2;
35
- if (innerWidth <= 0 || innerHeight <= 0 || ticker.length === 0) return NO_LABEL;
36
- const byWidth = innerWidth / (ticker.length * MONO_CHAR_EM);
52
+ if (roomWidth <= 0 || innerHeight <= 0 || ticker.length === 0) return NO_LABEL;
37
53
  // Area caps big tiles only; a small one may still use the smallest readable size.
38
54
  const byArea = Math.max(MIN_FULL_TICKER_PX, Math.sqrt(width * height) / AREA_FONT_DIVISOR);
39
- const cap = Math.min(MAX_TICKER_PX, byWidth, byArea);
55
+ const lines = innerHeight / LINE_HEIGHT;
40
56
 
41
57
  if (value) {
42
- const valueByWidth = innerWidth / (value.length * MONO_CHAR_EM * VALUE_SCALE);
43
- const byHeight = innerHeight / (LINE_HEIGHT * (1 + VALUE_SCALE));
44
- const tickerPx = Math.min(cap, valueByWidth, byHeight);
58
+ const tickerPx = Math.min(
59
+ MAX_TICKER_PX,
60
+ byArea,
61
+ sizeAcross(width, ticker.length * MONO_CHAR_EM, MIN_FULL_TICKER_PX),
62
+ sizeAcross(width, value.length * MONO_CHAR_EM * VALUE_SCALE, MIN_FULL_TICKER_PX),
63
+ // Both lines stacked, with the move at its own floor when the ticker is small.
64
+ lines / (1 + VALUE_SCALE),
65
+ lines - MIN_VALUE_PX,
66
+ );
45
67
  const valuePx = Math.max(MIN_VALUE_PX, tickerPx * VALUE_SCALE);
46
- if (
47
- tickerPx >= MIN_FULL_TICKER_PX
48
- && value.length * MONO_CHAR_EM * valuePx <= innerWidth
49
- && (tickerPx + valuePx) * LINE_HEIGHT <= innerHeight
50
- ) {
51
- return { tier: "full", tickerPx, valuePx };
68
+ if (tickerPx >= MIN_FULL_TICKER_PX && value.length * MONO_CHAR_EM * valuePx <= roomWidth) {
69
+ return { tier: "full", tickerPx, valuePx, vertical: false };
52
70
  }
53
71
  }
54
72
 
55
- const tickerPx = Math.min(cap, innerHeight / LINE_HEIGHT);
56
- if (tickerPx >= MIN_TICKER_PX) return { tier: "ticker", tickerPx, valuePx: 0 };
73
+ const tickerPx = Math.min(MAX_TICKER_PX, byArea, sizeAcross(width, ticker.length * MONO_CHAR_EM, MIN_TICKER_PX), lines);
74
+ if (tickerPx >= MIN_TICKER_PX) return { tier: "ticker", tickerPx, valuePx: 0, vertical: false };
57
75
  return NO_LABEL;
58
76
  }
59
77
 
78
+ /**
79
+ * The label is laid across the tile where it fits that way, and otherwise up
80
+ * a tall tile, so a tile the layout stood on end is labelled as it would be
81
+ * lying down: a tall tile narrower than its ticker still carries it. A flat
82
+ * strip too short for a line stays blank rather than show a letter on its side.
83
+ */
84
+ export function heatTileLabelPx(width: number, height: number, ticker: string, value: string | null): HeatTileLabel {
85
+ const across = fitLabel(width, height, ticker, value);
86
+ if (across.tier !== "none" || height <= width) return across;
87
+ const along = fitLabel(height, width, ticker, value);
88
+ return along.tier === "none" ? NO_LABEL : { ...along, vertical: true };
89
+ }
90
+
60
91
  /**
61
92
  * The move with its qualifier (an after-hours move's AH) where that still fits
62
93
  * at the size the move alone was given, else the move alone. The qualifier