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
package/src/i18n/es.ts CHANGED
@@ -120,6 +120,7 @@ export const es: Record<string, string> = {
120
120
  "Portfolio": "Cartera",
121
121
  "Primary Listing": "Cotización principal",
122
122
  "Other Listings": "Otras cotizaciones",
123
+ "Related": "Relacionados",
123
124
  "Saved Layouts": "Diseños guardados",
124
125
  "Saved": "Guardados",
125
126
  "Search Results": "Resultados de búsqueda",
@@ -228,6 +229,8 @@ export const es: Record<string, string> = {
228
229
  "Remove Pane": "Quitar panel",
229
230
  "Float Pane": "Flotar panel",
230
231
  "Dock Pane": "Anclar panel",
232
+ "Move to New Layout": "Mover a un diseño nuevo",
233
+ "Move Back to {layout}": "Devolver a {layout}",
231
234
  "Settings": "Ajustes",
232
235
  "Copy Screenshot": "Copiar captura",
233
236
  "Share Pane": "Compartir panel",
@@ -791,6 +794,10 @@ export const es: Record<string, string> = {
791
794
  "Group": "Grupo",
792
795
  "DM": "DM",
793
796
  "Conversations": "Conversaciones",
797
+ "Channel": "Canal",
798
+ "Team channel": "Canal del equipo",
799
+ "{count} unread": "{count} sin leer",
800
+ "Start a direct or group chat": "Inicia un chat directo o grupal",
794
801
  "Start or open direct message": "Iniciar o abrir mensaje directo",
795
802
  "Start group chat": "Iniciar chat grupal",
796
803
  "Use @username, or multiple usernames for a group chat": "Usa @username, o varios usuarios para un chat grupal",
@@ -807,7 +814,7 @@ export const es: Record<string, string> = {
807
814
  "run the Equity Diagnostic": "ejecutar el Equity Diagnostic",
808
815
  "stream the market scanners": "recibir los escáneres de mercado en vivo",
809
816
  "manage your Gloom Cloud account": "gestionar tu cuenta de Gloom Cloud",
810
- "ask questions about any pane in the terminal": "hacer preguntas sobre cualquier panel del terminal",
817
+ "ask questions about anything on screen": "hacer preguntas sobre cualquier cosa en pantalla",
811
818
  "search X": "buscar en X",
812
819
  "{shortcut}, then Resend Verification Email": "{shortcut} y luego Reenviar correo de verificación",
813
820
  "editing": "editando",
@@ -993,6 +1000,7 @@ export const es: Record<string, string> = {
993
1000
  "Ticker add failed": "No se pudo agregar el ticker",
994
1001
  "Ticker symbol is required.": "El símbolo del ticker es obligatorio.",
995
1002
  "No ticker selected.": "Ningún ticker seleccionado.",
1003
+ "Select a ticker.": "Selecciona un ticker.",
996
1004
  "No comparison tickers configured.": "Sin tickers de comparación configurados.",
997
1005
  "Loading chart...": "Cargando gráfico...",
998
1006
  "updating": "actualizando",
@@ -1445,6 +1453,11 @@ export const es: Record<string, string> = {
1445
1453
  "Connected! Positions will sync automatically.": "¡Conectado! Las posiciones se sincronizarán solas.",
1446
1454
  "No connectable brokers are installed.": "No hay brókers conectables instalados.",
1447
1455
  "{broker} was not connected.": "{broker} no se conectó.",
1456
+ "Ends {date}": "Termina el {date}",
1457
+ "Ended {date}": "Terminó el {date}",
1458
+ "Sign-in ends {when}": "El inicio de sesión termina {when}",
1459
+ "Sign-in ended {when}": "El inicio de sesión terminó {when}",
1460
+ "{broker} sign-in was not renewed.": "No se renovó el inicio de sesión de {broker}.",
1448
1461
 
1449
1462
  // ── Company picker ───────────────────────────────────────────
1450
1463
  "Which companies do you follow?": "¿Qué empresas sigues?",
package/src/i18n/ja.ts CHANGED
@@ -121,6 +121,7 @@ export const ja: Record<string, string> = {
121
121
  "Portfolio": "ポートフォリオ",
122
122
  "Primary Listing": "主要上場先",
123
123
  "Other Listings": "その他の上場先",
124
+ "Related": "関連",
124
125
  "Saved Layouts": "保存済みレイアウト",
125
126
  "Saved": "保存済み",
126
127
  "Search Results": "検索結果",
@@ -229,6 +230,8 @@ export const ja: Record<string, string> = {
229
230
  "Remove Pane": "ペインを削除",
230
231
  "Float Pane": "ペインをフロート",
231
232
  "Dock Pane": "ペインをドッキング",
233
+ "Move to New Layout": "新しいレイアウトへ移動",
234
+ "Move Back to {layout}": "{layout} に戻す",
232
235
  "Settings": "設定",
233
236
  "Copy Screenshot": "スクリーンショットをコピー",
234
237
  "Share Pane": "ペインを共有",
@@ -805,6 +808,10 @@ export const ja: Record<string, string> = {
805
808
  "Group": "グループ",
806
809
  "DM": "DM",
807
810
  "Conversations": "会話",
811
+ "Channel": "チャンネル",
812
+ "Team channel": "チームチャンネル",
813
+ "{count} unread": "未読 {count}",
814
+ "Start a direct or group chat": "ダイレクトまたはグループチャットを始める",
808
815
  "Start or open direct message": "ダイレクトメッセージを開始または開く",
809
816
  "Start group chat": "グループチャットを開始する",
810
817
  "Use @username, or multiple usernames for a group chat": "グループ チャットには @username または複数のユーザー名を使用してください",
@@ -821,7 +828,7 @@ export const ja: Record<string, string> = {
821
828
  "run the Equity Diagnostic": "Equity Diagnostic を実行",
822
829
  "stream the market scanners": "マーケットスキャナーをストリーミング",
823
830
  "manage your Gloom Cloud account": "Gloom Cloud アカウントを管理",
824
- "ask questions about any pane in the terminal": "ターミナル内のどのペインについても質問",
831
+ "ask questions about anything on screen": "画面上のあらゆる内容について質問",
825
832
  "search X": "X を検索",
826
833
  "{shortcut}, then Resend Verification Email": "{shortcut} から「確認メールを再送信」",
827
834
  "editing": "編集",
@@ -1007,6 +1014,7 @@ export const ja: Record<string, string> = {
1007
1014
  "Ticker add failed": "銘柄の追加に失敗しました",
1008
1015
  "Ticker symbol is required.": "銘柄コードを入力してください。",
1009
1016
  "No ticker selected.": "銘柄が選択されていません。",
1017
+ "Select a ticker.": "銘柄を選択してください。",
1010
1018
  "No comparison tickers configured.": "比較銘柄が設定されていません。",
1011
1019
  "Loading chart...": "チャートを読み込み中...",
1012
1020
  "updating": "更新中",
@@ -1441,6 +1449,11 @@ export const ja: Record<string, string> = {
1441
1449
  "Connected! Positions will sync automatically.": "接続しました。ポジションは自動で同期されます。",
1442
1450
  "No connectable brokers are installed.": "接続できる証券会社がインストールされていません。",
1443
1451
  "{broker} was not connected.": "{broker} は接続されませんでした。",
1452
+ "Ends {date}": "{date} 終了",
1453
+ "Ended {date}": "{date} に終了",
1454
+ "Sign-in ends {when}": "サインインは {when} に終了します",
1455
+ "Sign-in ended {when}": "サインインは {when} に終了しました",
1456
+ "{broker} sign-in was not renewed.": "{broker} のサインインは更新されませんでした。",
1444
1457
 
1445
1458
  // ── Company picker ───────────────────────────────────────────
1446
1459
  "Which companies do you follow?": "どの企業をフォローしていますか?",
package/src/i18n/ko.ts CHANGED
@@ -119,6 +119,7 @@ export const ko: Record<string, string> = {
119
119
  "Portfolio": "포트폴리오",
120
120
  "Primary Listing": "주 상장",
121
121
  "Other Listings": "기타 상장",
122
+ "Related": "관련",
122
123
  "Saved Layouts": "저장된 레이아웃",
123
124
  "Saved": "저장됨",
124
125
  "Search Results": "검색결과",
@@ -227,6 +228,8 @@ export const ko: Record<string, string> = {
227
228
  "Remove Pane": "패널 제거",
228
229
  "Float Pane": "패널 띄우기",
229
230
  "Dock Pane": "패널 도킹",
231
+ "Move to New Layout": "새 레이아웃으로 이동",
232
+ "Move Back to {layout}": "{layout}(으)로 되돌리기",
230
233
  "Settings": "설정",
231
234
  "Copy Screenshot": "스크린샷 복사",
232
235
  "Share Pane": "창 공유",
@@ -803,6 +806,10 @@ export const ko: Record<string, string> = {
803
806
  "Group": "그룹",
804
807
  "DM": "DM",
805
808
  "Conversations": "대화",
809
+ "Channel": "채널",
810
+ "Team channel": "팀 채널",
811
+ "{count} unread": "읽지 않음 {count}",
812
+ "Start a direct or group chat": "DM 또는 그룹 채팅 시작",
806
813
  "Start or open direct message": "DM 시작 또는 열기",
807
814
  "Start group chat": "그룹 채팅 시작",
808
815
  "Use @username, or multiple usernames for a group chat": "그룹 채팅에는 @username 또는 여러 사용자 이름을 사용하세요",
@@ -819,7 +826,7 @@ export const ko: Record<string, string> = {
819
826
  "run the Equity Diagnostic": "Equity Diagnostic을 실행",
820
827
  "stream the market scanners": "마켓 스캐너를 스트리밍",
821
828
  "manage your Gloom Cloud account": "Gloom Cloud 계정을 관리",
822
- "ask questions about any pane in the terminal": "터미널의 모든 패널에 대해 질문",
829
+ "ask questions about anything on screen": "화면에 보이는 모든 내용에 대해 질문",
823
830
  "search X": "X를 검색",
824
831
  "{shortcut}, then Resend Verification Email": "{shortcut} 후 인증 이메일 재전송",
825
832
  "editing": "편집 중",
@@ -1005,6 +1012,7 @@ export const ko: Record<string, string> = {
1005
1012
  "Ticker add failed": "종목 추가 실패",
1006
1013
  "Ticker symbol is required.": "종목코드는 필수입니다.",
1007
1014
  "No ticker selected.": "선택한 종목이 없습니다.",
1015
+ "Select a ticker.": "종목을 선택하세요.",
1008
1016
  "No comparison tickers configured.": "비교 종목이 설정되지 않았습니다.",
1009
1017
  "Loading chart...": "차트 불러오는 중...",
1010
1018
  "updating": "업데이트 중",
@@ -1439,6 +1447,11 @@ export const ko: Record<string, string> = {
1439
1447
  "Connected! Positions will sync automatically.": "연결되었습니다. 포지션이 자동으로 동기화됩니다.",
1440
1448
  "No connectable brokers are installed.": "연결할 수 있는 브로커가 설치되어 있지 않습니다.",
1441
1449
  "{broker} was not connected.": "{broker}이(가) 연결되지 않았습니다.",
1450
+ "Ends {date}": "{date} 종료",
1451
+ "Ended {date}": "{date} 종료됨",
1452
+ "Sign-in ends {when}": "로그인이 {when}에 종료됩니다",
1453
+ "Sign-in ended {when}": "로그인이 {when}에 종료되었습니다",
1454
+ "{broker} sign-in was not renewed.": "{broker} 로그인이 갱신되지 않았습니다.",
1442
1455
 
1443
1456
  // ── Company picker ───────────────────────────────────────────
1444
1457
  "Which companies do you follow?": "어떤 기업을 팔로우하시나요?",
package/src/i18n/zh-cn.ts CHANGED
@@ -121,6 +121,7 @@ export const zhCN: Record<string, string> = {
121
121
  "Portfolio": "投资组合",
122
122
  "Primary Listing": "主要上市",
123
123
  "Other Listings": "其他上市",
124
+ "Related": "相关",
124
125
  "Saved Layouts": "已存布局",
125
126
  "Saved": "已保存",
126
127
  "Search Results": "搜索结果",
@@ -229,6 +230,8 @@ export const zhCN: Record<string, string> = {
229
230
  "Remove Pane": "移除面板",
230
231
  "Float Pane": "浮动面板",
231
232
  "Dock Pane": "停靠面板",
233
+ "Move to New Layout": "移到新布局",
234
+ "Move Back to {layout}": "移回 {layout}",
232
235
  "Settings": "设置",
233
236
  "Copy Screenshot": "复制截图",
234
237
  "Share Pane": "分享窗格",
@@ -805,6 +808,10 @@ export const zhCN: Record<string, string> = {
805
808
  "Group": "群组",
806
809
  "DM": "私信",
807
810
  "Conversations": "会话",
811
+ "Channel": "频道",
812
+ "Team channel": "团队频道",
813
+ "{count} unread": "{count} 条未读",
814
+ "Start a direct or group chat": "发起私信或群聊",
808
815
  "Start or open direct message": "开始或打开私信",
809
816
  "Start group chat": "开始群聊",
810
817
  "Use @username, or multiple usernames for a group chat": "使用 @用户名,或输入多个用户名开始群聊",
@@ -821,7 +828,7 @@ export const zhCN: Record<string, string> = {
821
828
  "run the Equity Diagnostic": "运行 Equity Diagnostic",
822
829
  "stream the market scanners": "接收实时市场扫描",
823
830
  "manage your Gloom Cloud account": "管理你的 Gloom Cloud 账户",
824
- "ask questions about any pane in the terminal": "就终端中的任意面板提问",
831
+ "ask questions about anything on screen": "就屏幕上的任何内容提问",
825
832
  "search X": "搜索 X",
826
833
  "{shortcut}, then Resend Verification Email": "按 {shortcut},然后选择“重新发送验证邮件”",
827
834
  "editing": "编辑中",
@@ -1007,6 +1014,7 @@ export const zhCN: Record<string, string> = {
1007
1014
  "Ticker add failed": "添加股票代码失败",
1008
1015
  "Ticker symbol is required.": "必须输入股票代码。",
1009
1016
  "No ticker selected.": "未选择股票。",
1017
+ "Select a ticker.": "请选择股票。",
1010
1018
  "No comparison tickers configured.": "尚未配置对比股票。",
1011
1019
  "Loading chart...": "正在加载图表...",
1012
1020
  "updating": "更新中",
@@ -1441,6 +1449,11 @@ export const zhCN: Record<string, string> = {
1441
1449
  "Connected! Positions will sync automatically.": "已连接!持仓将自动同步。",
1442
1450
  "No connectable brokers are installed.": "未安装可连接的券商。",
1443
1451
  "{broker} was not connected.": "{broker} 未连接。",
1452
+ "Ends {date}": "{date} 结束",
1453
+ "Ended {date}": "{date} 已结束",
1454
+ "Sign-in ends {when}": "登录将于 {when} 结束",
1455
+ "Sign-in ended {when}": "登录已于 {when} 结束",
1456
+ "{broker} sign-in was not renewed.": "{broker} 的登录未续期。",
1444
1457
 
1445
1458
  // ── Company picker ───────────────────────────────────────────
1446
1459
  "Which companies do you follow?": "你关注哪些公司?",
package/src/i18n/zh-tw.ts CHANGED
@@ -121,6 +121,7 @@ export const zhTW: Record<string, string> = {
121
121
  "Portfolio": "投資組合",
122
122
  "Primary Listing": "主要掛牌",
123
123
  "Other Listings": "其他掛牌",
124
+ "Related": "相關",
124
125
  "Saved Layouts": "已儲存的版面設定",
125
126
  "Saved": "已儲存",
126
127
  "Search Results": "搜尋結果",
@@ -229,6 +230,8 @@ export const zhTW: Record<string, string> = {
229
230
  "Remove Pane": "移除面板",
230
231
  "Float Pane": "浮動面板",
231
232
  "Dock Pane": "停靠面板",
233
+ "Move to New Layout": "移到新版面設定",
234
+ "Move Back to {layout}": "移回 {layout}",
232
235
  "Settings": "設定",
233
236
  "Copy Screenshot": "複製截圖",
234
237
  "Share Pane": "分享窗格",
@@ -805,6 +808,10 @@ export const zhTW: Record<string, string> = {
805
808
  "Group": "群組",
806
809
  "DM": "私訊",
807
810
  "Conversations": "對話",
811
+ "Channel": "頻道",
812
+ "Team channel": "團隊頻道",
813
+ "{count} unread": "{count} 則未讀",
814
+ "Start a direct or group chat": "開始私訊或群組聊天",
808
815
  "Start or open direct message": "開始或開啟私訊",
809
816
  "Start group chat": "開始群組聊天",
810
817
  "Use @username, or multiple usernames for a group chat": "使用 @username,或輸入多個使用者名稱開始群組聊天",
@@ -821,7 +828,7 @@ export const zhTW: Record<string, string> = {
821
828
  "run the Equity Diagnostic": "執行 Equity Diagnostic",
822
829
  "stream the market scanners": "接收即時市場掃描",
823
830
  "manage your Gloom Cloud account": "管理你的 Gloom Cloud 帳戶",
824
- "ask questions about any pane in the terminal": "針對終端機中的任一面板提問",
831
+ "ask questions about anything on screen": "針對畫面上的任何內容提問",
825
832
  "search X": "搜尋 X",
826
833
  "{shortcut}, then Resend Verification Email": "按 {shortcut},然後選擇「重新發送驗證郵件」",
827
834
  "editing": "編輯中",
@@ -1007,6 +1014,7 @@ export const zhTW: Record<string, string> = {
1007
1014
  "Ticker add failed": "新增股票代號失敗",
1008
1015
  "Ticker symbol is required.": "請輸入股票代號。",
1009
1016
  "No ticker selected.": "未選擇股票。",
1017
+ "Select a ticker.": "請選擇股票。",
1010
1018
  "No comparison tickers configured.": "尚未設定比較股票。",
1011
1019
  "Loading chart...": "正在載入圖表...",
1012
1020
  "updating": "更新中",
@@ -1441,6 +1449,11 @@ export const zhTW: Record<string, string> = {
1441
1449
  "Connected! Positions will sync automatically.": "已連線!持倉將自動同步。",
1442
1450
  "No connectable brokers are installed.": "未安裝可連線的券商。",
1443
1451
  "{broker} was not connected.": "{broker} 未連線。",
1452
+ "Ends {date}": "{date} 結束",
1453
+ "Ended {date}": "{date} 已結束",
1454
+ "Sign-in ends {when}": "登入將於 {when} 結束",
1455
+ "Sign-in ended {when}": "登入已於 {when} 結束",
1456
+ "{broker} sign-in was not renewed.": "{broker} 的登入未續期。",
1444
1457
 
1445
1458
  // ── Company picker ───────────────────────────────────────────
1446
1459
  "Which companies do you follow?": "你關注哪些公司?",
@@ -138,10 +138,9 @@ export const DESKS: readonly Desk[] = [
138
138
  { fn: "GC" },
139
139
  { fn: "BTMM" },
140
140
  { fn: "CDX" },
141
- { fn: "SOVR" },
142
141
  { fn: "CBR" },
143
142
  ],
144
- dock: column(0.5, row(0.4, pane(1), row(0.5, pane(2), pane(3))), row(0.34, pane(4), row(0.5, pane(5), pane(6)))),
143
+ dock: column(0.5, row(0.4, pane(1), row(0.5, pane(2), pane(3))), row(0.5, pane(4), pane(5))),
145
144
  },
146
145
  {
147
146
  key: "fx",
@@ -0,0 +1,264 @@
1
+ import {
2
+ findPaneInstance,
3
+ getPlacedPaneInstanceIds,
4
+ isTickerPaneId,
5
+ TICKER_RESEARCH_PANE_ID,
6
+ type AppConfig,
7
+ type DockedPlacementMemory,
8
+ type FloatingPlacementMemory,
9
+ type LayoutConfig,
10
+ type PaneInstanceConfig,
11
+ type PaneMovedFrom,
12
+ type PaneMovedLink,
13
+ } from "../types/config";
14
+ import { pinFollowingPane } from "./pane-follow";
15
+ import { findDockLeaf, getNodeAtPath, getRepresentativeLeafId } from "./pane-manager/dock-tree";
16
+ import { dockPane, insertAtRootEdge, resizeSplitAtPath } from "./pane-manager/docking";
17
+ import { floatAtRect } from "./pane-manager/floating-actions";
18
+ import { finalizeLayout, removePane } from "./pane-manager/layout-state";
19
+
20
+ let nextSavedLayoutSeq = 0;
21
+
22
+ /** A fresh `SavedLayout.id`, unique across devices that sync the same layouts. */
23
+ export function createSavedLayoutId(): string {
24
+ nextSavedLayoutSeq += 1;
25
+ return `layout:${Date.now().toString(36)}${nextSavedLayoutSeq.toString(36)}${Math.random().toString(36).slice(2, 8)}`;
26
+ }
27
+
28
+ /** The ticker a pane shows right now, through its links; null when it shows none. */
29
+ type ResolvePaneSymbol = (instanceId: string) => string | null;
30
+
31
+ /** Why a pane cannot leave for a layout of its own. */
32
+ export type PaneNewLayoutBlock =
33
+ | "missing"
34
+ /** It sits in its own desktop window already. */
35
+ | "detached"
36
+ /** A layout of one pane already is one. */
37
+ | "only-pane"
38
+ /** It follows a list with nothing selected, and a ticker pane with no ticker closes. */
39
+ | "no-ticker";
40
+
41
+ export function paneNewLayoutBlock(
42
+ layout: LayoutConfig,
43
+ paneId: string,
44
+ resolveSymbol: ResolvePaneSymbol,
45
+ ): PaneNewLayoutBlock | null {
46
+ const instance = findPaneInstance(layout, paneId);
47
+ const placed = getPlacedPaneInstanceIds(layout);
48
+ if (!instance || !placed.includes(paneId)) return "missing";
49
+ if ((layout.detached ?? []).some((entry) => entry.instanceId === paneId)) return "detached";
50
+ if (placed.length <= 1) return "only-pane";
51
+ if (
52
+ instance.binding?.kind === "follow"
53
+ && isTickerPaneId(instance.paneId)
54
+ && instance.paneId !== TICKER_RESEARCH_PANE_ID
55
+ && !resolveSymbol(paneId)?.trim()
56
+ ) return "no-ticker";
57
+ return null;
58
+ }
59
+
60
+ function movedLink(before: PaneInstanceConfig, after: PaneInstanceConfig, symbol: string): PaneMovedLink {
61
+ return {
62
+ instanceId: before.instanceId,
63
+ sourceInstanceId: before.binding?.kind === "follow" ? before.binding.sourceInstanceId : "",
64
+ symbol,
65
+ ...(before.title !== undefined && after.title !== before.title ? { title: before.title } : {}),
66
+ };
67
+ }
68
+
69
+ /**
70
+ * Pins every pane whose link source is gone on the ticker it showed, the way
71
+ * closing that source does, and says which links were cut.
72
+ */
73
+ function pinOrphanedFollowers(
74
+ layout: LayoutConfig,
75
+ resolveSymbol: ResolvePaneSymbol,
76
+ ): { layout: LayoutConfig; links: PaneMovedLink[] } {
77
+ const ids = new Set(layout.instances.map((instance) => instance.instanceId));
78
+ const links: PaneMovedLink[] = [];
79
+ let changed = false;
80
+ const instances = layout.instances.map((instance) => {
81
+ if (instance.binding?.kind !== "follow" || ids.has(instance.binding.sourceInstanceId)) return instance;
82
+ changed = true;
83
+ const symbol = resolveSymbol(instance.instanceId)?.trim() || null;
84
+ const pinned = pinFollowingPane(instance, symbol);
85
+ if (symbol) links.push(movedLink(instance, pinned, symbol));
86
+ return pinned;
87
+ });
88
+ return { layout: changed ? finalizeLayout({ ...layout, instances }) : layout, links };
89
+ }
90
+
91
+ /** Where a docked pane sits: its path, the neighbour it splits with, and its share of the split. */
92
+ function dockedPlacement(layout: LayoutConfig, paneId: string): PaneMovedFrom["docked"] {
93
+ const leaf = findDockLeaf(layout, paneId);
94
+ if (!leaf) return { path: [] };
95
+ if (leaf.path.length === 0) return { path: [] };
96
+ const parent = getNodeAtPath(layout.dockRoot, leaf.path.slice(0, -1));
97
+ if (!parent || parent.kind !== "split") return { path: [...leaf.path] };
98
+ const branch = leaf.path[leaf.path.length - 1]!;
99
+ const sibling = branch === 0 ? parent.second : parent.first;
100
+ return {
101
+ path: [...leaf.path],
102
+ anchorInstanceId: getRepresentativeLeafId(sibling, branch === 0),
103
+ position: parent.axis === "horizontal"
104
+ ? (branch === 0 ? "left" : "right")
105
+ : (branch === 0 ? "above" : "below"),
106
+ ratio: parent.ratio,
107
+ };
108
+ }
109
+
110
+ export interface PaneLayoutMove {
111
+ /** The layout the pane left: the dock tree closed over the gap, its followers pinned. */
112
+ source: LayoutConfig;
113
+ /** The layout the pane landed in. */
114
+ target: LayoutConfig;
115
+ }
116
+
117
+ /**
118
+ * Takes a pane out of `layout` into a layout of its own, docked alone so it
119
+ * fills the window. The instance keeps its id, settings and lock, and records
120
+ * where it came from so Move Back can return it. Null when the pane cannot go.
121
+ */
122
+ export function movePaneToOwnLayout(
123
+ layout: LayoutConfig,
124
+ paneId: string,
125
+ sourceLayoutId: string,
126
+ resolveSymbol: ResolvePaneSymbol,
127
+ ): PaneLayoutMove | null {
128
+ if (paneNewLayoutBlock(layout, paneId, resolveSymbol)) return null;
129
+ const instance = findPaneInstance(layout, paneId)!;
130
+ const floatingEntry = layout.floating.find((entry) => entry.instanceId === paneId);
131
+ const floating: FloatingPlacementMemory | undefined = floatingEntry
132
+ ? { x: floatingEntry.x, y: floatingEntry.y, width: floatingEntry.width, height: floatingEntry.height }
133
+ : undefined;
134
+ const left = pinOrphanedFollowers(removePane(layout, paneId), resolveSymbol);
135
+
136
+ // Alone in its layout the pane has nothing to follow, so it keeps the ticker it shows.
137
+ let moved: PaneInstanceConfig = instance;
138
+ const links: PaneMovedLink[] = [];
139
+ if (instance.binding?.kind === "follow") {
140
+ const symbol = resolveSymbol(paneId)?.trim() || null;
141
+ moved = pinFollowingPane(instance, symbol);
142
+ if (symbol) links.push(movedLink(instance, moved, symbol));
143
+ }
144
+ links.push(...left.links);
145
+ const movedFrom: PaneMovedFrom = {
146
+ layoutId: sourceLayoutId,
147
+ ...(floating ? { floating } : { docked: dockedPlacement(layout, paneId) }),
148
+ ...(links.length > 0 ? { links } : {}),
149
+ };
150
+ return {
151
+ source: left.layout,
152
+ target: finalizeLayout({
153
+ dockRoot: { kind: "pane", instanceId: paneId },
154
+ instances: [{ ...moved, movedFrom }],
155
+ floating: [],
156
+ detached: [],
157
+ }),
158
+ };
159
+ }
160
+
161
+ export interface PaneMoveBackTarget {
162
+ /** Index in `config.layouts`. */
163
+ index: number;
164
+ name: string;
165
+ /** The layout the pane came from; false when that one is gone and this is the first other tab. */
166
+ original: boolean;
167
+ }
168
+
169
+ /** Where Move Back takes a pane of the active layout, or null when it has nowhere to go. */
170
+ export function paneMoveBackTarget(
171
+ config: Pick<AppConfig, "layout" | "layouts" | "activeLayoutIndex">,
172
+ paneId: string,
173
+ ): PaneMoveBackTarget | null {
174
+ const movedFrom = findPaneInstance(config.layout, paneId)?.movedFrom;
175
+ if (!movedFrom) return null;
176
+ if ((config.layout.detached ?? []).some((entry) => entry.instanceId === paneId)) return null;
177
+ const others = config.layouts.flatMap((entry, index) => (index === config.activeLayoutIndex ? [] : [{ entry, index }]));
178
+ const original = others.find(({ entry }) => entry.id === movedFrom.layoutId);
179
+ const target = original ?? others[0];
180
+ return target ? { index: target.index, name: target.entry.name, original: !!original } : null;
181
+ }
182
+
183
+ function withDockedMemory(layout: LayoutConfig, paneId: string, docked: DockedPlacementMemory): LayoutConfig {
184
+ return {
185
+ ...layout,
186
+ instances: layout.instances.map((instance) => (
187
+ instance.instanceId === paneId
188
+ ? { ...instance, placementMemory: { ...instance.placementMemory, docked } }
189
+ : instance
190
+ )),
191
+ };
192
+ }
193
+
194
+ /** The split the pane came back into takes the share it had, when the pane landed where it was. */
195
+ function restoreSplitRatio(layout: LayoutConfig, paneId: string, path: Array<0 | 1> | undefined, ratio: number | undefined): LayoutConfig {
196
+ if (ratio === undefined || !path || path.length === 0) return layout;
197
+ const leaf = findDockLeaf(layout, paneId);
198
+ if (!leaf || leaf.path.join(".") !== path.join(".")) return layout;
199
+ return resizeSplitAtPath(layout, path.slice(0, -1), ratio);
200
+ }
201
+
202
+ function followsInto(layout: LayoutConfig, fromId: string, targetId: string): boolean {
203
+ const seen = new Set<string>();
204
+ let current: string | undefined = fromId;
205
+ while (current && !seen.has(current)) {
206
+ if (current === targetId) return true;
207
+ seen.add(current);
208
+ const binding: PaneInstanceConfig["binding"] = findPaneInstance(layout, current)?.binding;
209
+ current = binding?.kind === "follow" ? binding.sourceInstanceId : undefined;
210
+ }
211
+ return false;
212
+ }
213
+
214
+ /** Links again the panes the move pinned, while each still shows the ticker it was pinned on. */
215
+ function relink(layout: LayoutConfig, links: readonly PaneMovedLink[]): LayoutConfig {
216
+ let next = layout;
217
+ for (const link of links) {
218
+ const instance = findPaneInstance(next, link.instanceId);
219
+ if (!instance || instance.binding?.kind !== "fixed" || instance.binding.symbol !== link.symbol) continue;
220
+ if (!findPaneInstance(next, link.sourceInstanceId) || followsInto(next, link.sourceInstanceId, link.instanceId)) continue;
221
+ next = {
222
+ ...next,
223
+ instances: next.instances.map((entry) => (
224
+ entry.instanceId === link.instanceId
225
+ ? { ...entry, binding: { kind: "follow", sourceInstanceId: link.sourceInstanceId }, ...("title" in link ? { title: link.title } : {}) }
226
+ : entry
227
+ )),
228
+ };
229
+ }
230
+ return next === layout ? layout : finalizeLayout(next);
231
+ }
232
+
233
+ /**
234
+ * Takes a pane Move to New Layout moved out of `layout` and puts it back in
235
+ * `target`: where it was when `original`, otherwise docked on the right. Links
236
+ * the move cut are restored when the panes they joined are both there again.
237
+ */
238
+ export function movePaneBack(
239
+ layout: LayoutConfig,
240
+ target: LayoutConfig,
241
+ paneId: string,
242
+ original: boolean,
243
+ resolveSymbol: ResolvePaneSymbol,
244
+ ): PaneLayoutMove | null {
245
+ const instance = findPaneInstance(layout, paneId);
246
+ const movedFrom = instance?.movedFrom;
247
+ if (!instance || !movedFrom) return null;
248
+ const left = pinOrphanedFollowers(removePane(layout, paneId), resolveSymbol).layout;
249
+ const { movedFrom: _movedFrom, ...returning } = instance;
250
+ // A copy an undo put back in the target gives way to the pane coming home.
251
+ const base = findPaneInstance(target, paneId) ? removePane(target, paneId) : target;
252
+ const withPane: LayoutConfig = { ...base, instances: [...base.instances, returning] };
253
+
254
+ let placed: LayoutConfig;
255
+ if (original && movedFrom.floating) {
256
+ placed = floatAtRect(withPane, paneId, movedFrom.floating);
257
+ } else if (original && movedFrom.docked) {
258
+ const { ratio, ...docked } = movedFrom.docked;
259
+ placed = restoreSplitRatio(dockPane(withDockedMemory(withPane, paneId, docked), paneId), paneId, docked.path, ratio);
260
+ } else {
261
+ placed = insertAtRootEdge(withPane, paneId, "right");
262
+ }
263
+ return { source: left, target: original ? relink(placed, movedFrom.links ?? []) : placed };
264
+ }