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
@@ -8,6 +8,7 @@ import {
8
8
  isPaneDocked,
9
9
  } from "../../layout/pane-manager";
10
10
  import type { PluginRegistry } from "../../plugins/registry";
11
+ import { PluginOffError, notifyPluginOff } from "../../plugins/plugin-off";
11
12
  import { findFixedTickerPaneForSymbol } from "../../layout/ticker-navigation";
12
13
  import type { AppAction, AppState } from "../../state/app/context";
13
14
  import { TICKER_RESEARCH_PANE_ID, normalizePaneId } from "../../types/config";
@@ -98,10 +99,23 @@ export function useAppTickerOpenRuntime({
98
99
  }
99
100
  }, [dispatch, pluginRegistry.events, stateRef]);
100
101
 
102
+ /**
103
+ * A ticker opens in a pane of its plugin, which is added hidden while that
104
+ * plugin is off, so the open is refused with an offer to turn it on.
105
+ */
106
+ const refuseWhileOwnerOff = useCallback((symbol: string, options: PinTickerOptions | undefined, reopen: () => void) => {
107
+ const paneType = normalizePaneId(options?.paneType ?? TICKER_RESEARCH_PANE_ID);
108
+ const owner = pluginRegistry.getDisabledPaneOwner(paneType, stateRef.current.config.disabledPlugins);
109
+ if (!owner) return false;
110
+ notifyPluginOff(pluginRegistry, new PluginOffError(owner, symbol), reopen);
111
+ return true;
112
+ }, [pluginRegistry, stateRef]);
113
+
101
114
  const placePinnedTickerTarget = useCallback((target: TickerOpenTarget, options?: PinTickerOptions) => {
102
115
  const paneType = normalizePaneId(options?.paneType ?? TICKER_RESEARCH_PANE_ID);
103
116
  const paneDef = pluginRegistry.panes.get(paneType);
104
117
  if (!paneDef) return;
118
+ if (refuseWhileOwnerOff(target.symbol, options, () => placePinnedTickerTarget(target, options))) return;
105
119
 
106
120
  publishTickerOpenTarget(target);
107
121
  const symbol = target.symbol;
@@ -182,10 +196,13 @@ export function useAppTickerOpenRuntime({
182
196
  persistLayout,
183
197
  pluginRegistry,
184
198
  publishTickerOpenTarget,
199
+ refuseWhileOwnerOff,
185
200
  stateRef,
186
201
  ]);
187
202
 
188
203
  const openPinnedTicker = useCallback(async (rawSymbol: string, options?: PinTickerOptions) => {
204
+ // Before resolving, so a refused open adds no ticker either.
205
+ if (refuseWhileOwnerOff(rawSymbol, options, () => { void openPinnedTicker(rawSymbol, options); })) return;
189
206
  const recordAttention = captureAttentionAction();
190
207
  const selectedTicker = options?.instrument !== undefined && (options.instrument || options.listing) ? await tickerRepository.loadTicker(rawSymbol) : null;
191
208
  const target = selectedTicker
@@ -197,7 +214,7 @@ export function useAppTickerOpenRuntime({
197
214
  if (action && pluginRegistry.panes.has(options?.paneType ?? TICKER_RESEARCH_PANE_ID)) {
198
215
  recordAttention(publicTickerKey(target.symbol, target.listing?.exchange ?? target.ticker.metadata.exchange), action);
199
216
  }
200
- }, [placePinnedTickerTarget, pluginRegistry, resolveOpenTickerTarget, tickerRepository]);
217
+ }, [placePinnedTickerTarget, pluginRegistry, refuseWhileOwnerOff, resolveOpenTickerTarget, tickerRepository]);
201
218
 
202
219
  return {
203
220
  openPinnedTicker,
@@ -15,6 +15,7 @@ import { openTeamPane } from "../../plugins/builtin/cloud/team/pane-request";
15
15
  import { chatController } from "../../plugins/builtin/chat/controller";
16
16
  import { getShare } from "../../shares/api";
17
17
  import { openPaneShare } from "../../shares/pane";
18
+ import { PluginOffError, notifyPluginOff } from "../../plugins/plugin-off";
18
19
  import { materializeMarketplaceLayout } from "../../shares/portable-layout";
19
20
 
20
21
  type CloudDeepLinkRoute = {
@@ -564,6 +565,10 @@ export function handleDesktopDeepLink(rawUrl: string, options: DesktopDeepLinkHa
564
565
  return;
565
566
  case "open-share":
566
567
  void handleOpenShare(action, options.pluginRegistry).catch((error) => {
568
+ if (error instanceof PluginOffError) {
569
+ notifyPluginOff(options.pluginRegistry, error, () => handleDesktopDeepLink(rawUrl, options));
570
+ return;
571
+ }
567
572
  notifyError(options.pluginRegistry, error instanceof Error ? error.message : "Could not open shared pane.");
568
573
  });
569
574
  return;
@@ -0,0 +1,98 @@
1
+ import type { StarPromptConfig } from "../../types/config";
2
+ import { isRecord } from "../../utils/guards";
3
+
4
+ /**
5
+ * The one-time line in the terminal's status bar asking to star the
6
+ * repository. It waits for the third distinct day the terminal app is opened
7
+ * on, then for a minute on screen that day, shows once, and never again:
8
+ * opening the repository, dismissing it and its timeout all end it, and so
9
+ * does quitting while it shows. `starPrompt.enabled: false` turns it off.
10
+ */
11
+
12
+ export const STAR_PROMPT_REPO_URL = "https://github.com/gloom-sh/gloomberb";
13
+ /** The line waits for this many distinct days of use, today included. */
14
+ const STAR_PROMPT_SESSION_DAYS = 3;
15
+ /** On-screen time on the due day before the line shows. */
16
+ export const STAR_PROMPT_DELAY_MS = 60_000;
17
+ /** On-screen time the line stays before it times out. */
18
+ export const STAR_PROMPT_VISIBLE_MS = 5 * 60_000;
19
+
20
+ type StarPromptOutcome = NonNullable<StarPromptConfig["outcome"]>;
21
+
22
+ const DAY_PATTERN = /^\d{4}-\d{2}-\d{2}$/;
23
+ const OUTCOMES = new Set<StarPromptOutcome>(["opened", "dismissed", "expired"]);
24
+
25
+ /** The local calendar day of `date`, as `YYYY-MM-DD`. */
26
+ export function localDayKey(date: Date): string {
27
+ const month = String(date.getMonth() + 1).padStart(2, "0");
28
+ const day = String(date.getDate()).padStart(2, "0");
29
+ return `${date.getFullYear()}-${month}-${day}`;
30
+ }
31
+
32
+ function isOff(state: StarPromptConfig | undefined): boolean {
33
+ return state?.enabled === false || !!state?.shownAt;
34
+ }
35
+
36
+ /**
37
+ * Adds `day` to the days of use. Returns the same object when nothing changes:
38
+ * the line is off or already shown, the day is known, or enough days are.
39
+ */
40
+ export function recordStarPromptDay(state: StarPromptConfig | undefined, day: string): StarPromptConfig | undefined {
41
+ if (isOff(state)) return state;
42
+ const days = state?.days ?? [];
43
+ if (days.includes(day) || days.length >= STAR_PROMPT_SESSION_DAYS) return state;
44
+ return { ...state, days: [...days, day] };
45
+ }
46
+
47
+ /** Whether the line is waiting to show: on, never shown, and used on enough days. */
48
+ export function isStarPromptDue(state: StarPromptConfig | undefined): boolean {
49
+ return !isOff(state) && (state?.days?.length ?? 0) >= STAR_PROMPT_SESSION_DAYS;
50
+ }
51
+
52
+ /** Records that the line showed. The days are no longer needed once it has. */
53
+ export function markStarPromptShown(state: StarPromptConfig | undefined, at: Date): StarPromptConfig {
54
+ const { days: _days, ...rest } = state ?? {};
55
+ return { ...rest, shownAt: at.toISOString() };
56
+ }
57
+
58
+ export function finishStarPrompt(state: StarPromptConfig | undefined, outcome: StarPromptOutcome): StarPromptConfig {
59
+ return { ...state, outcome };
60
+ }
61
+
62
+ /** Only the fields that hold a valid value survive; an empty object is the same as none. */
63
+ export function sanitizeStarPrompt(value: unknown): StarPromptConfig | undefined {
64
+ if (!isRecord(value)) return undefined;
65
+ const days = Array.isArray(value.days)
66
+ ? [...new Set(value.days.filter((day): day is string => typeof day === "string" && DAY_PATTERN.test(day)))]
67
+ .slice(0, STAR_PROMPT_SESSION_DAYS)
68
+ : [];
69
+ const state: StarPromptConfig = {
70
+ ...(typeof value.enabled === "boolean" ? { enabled: value.enabled } : {}),
71
+ ...(days.length > 0 ? { days } : {}),
72
+ ...(typeof value.shownAt === "string" && value.shownAt ? { shownAt: value.shownAt } : {}),
73
+ ...(typeof value.outcome === "string" && OUTCOMES.has(value.outcome as StarPromptOutcome)
74
+ ? { outcome: value.outcome as StarPromptOutcome }
75
+ : {}),
76
+ };
77
+ return Object.keys(state).length > 0 ? state : undefined;
78
+ }
79
+
80
+ function isSet(value: string | undefined): boolean {
81
+ const normalized = value?.trim().toLowerCase();
82
+ return !!normalized && normalized !== "0" && normalized !== "false";
83
+ }
84
+
85
+ /**
86
+ * Whether this process is someone sitting at the terminal: both ends of the
87
+ * terminal are a TTY, and it is neither a CI job nor a test run. Bots, CI,
88
+ * scripts and smoke runs that pipe the app never see the line.
89
+ */
90
+ export function starPromptEnvironmentAllows(input: {
91
+ env: Record<string, string | undefined>;
92
+ stdinIsTTY: boolean | undefined;
93
+ stdoutIsTTY: boolean | undefined;
94
+ }): boolean {
95
+ if (input.stdinIsTTY !== true || input.stdoutIsTTY !== true) return false;
96
+ if (isSet(input.env.CI)) return false;
97
+ return input.env.NODE_ENV !== "test";
98
+ }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Off everywhere until the terminal app's entry point allows it, after
3
+ * `starPromptEnvironmentAllows`: the desktop app, the web terminal, CLI
4
+ * commands, `shot` and tests never record days or show the line.
5
+ */
6
+ let allowed = false;
7
+
8
+ export function allowStarPrompt(value: boolean): void {
9
+ allowed = value;
10
+ }
11
+
12
+ export function isStarPromptAllowed(): boolean {
13
+ return allowed;
14
+ }
15
+
16
+ interface StarPromptKeyHandlers {
17
+ open(): void;
18
+ dismiss(): void;
19
+ }
20
+
21
+ /** What the notification keys do while the line shows; null otherwise. */
22
+ let keyHandlers: StarPromptKeyHandlers | null = null;
23
+
24
+ /** Hands the notification keys to the line while it shows; returns the release. */
25
+ export function bindStarPromptKeys(handlers: StarPromptKeyHandlers): () => void {
26
+ keyHandlers = handlers;
27
+ return () => {
28
+ if (keyHandlers === handlers) keyHandlers = null;
29
+ };
30
+ }
31
+
32
+ /**
33
+ * Runs the line's open or dismiss for the notification keys. False when the
34
+ * line is not showing, so the key goes on to whatever else wants it.
35
+ */
36
+ export function pressStarPromptKey(kind: "open" | "dismiss"): boolean {
37
+ if (!keyHandlers) return false;
38
+ if (kind === "open") keyHandlers.open();
39
+ else keyHandlers.dismiss();
40
+ return true;
41
+ }
@@ -0,0 +1,190 @@
1
+ import { useCallback, useEffect, useRef, useState } from "react";
2
+ import { Box, Span, Text, useActionShortcut, useRendererHost } from "../../ui";
3
+ import { Button } from "../../components/ui/button";
4
+ import {
5
+ STATUS_WIDGET_COLUMNS,
6
+ useStatusWidgetClaim,
7
+ useStatusWidgetRoom,
8
+ } from "../../components/layout/status-widget-space";
9
+ import { t } from "../../i18n";
10
+ import { useAppLanguage } from "../../i18n/react";
11
+ import { useAppActive } from "../../state/app/activity";
12
+ import { useAppDispatch, useAppGetState, useAppSelector } from "../../state/app/context";
13
+ import { scheduleConfigSave } from "../../state/config-save-scheduler";
14
+ import { useThemeColors } from "../../theme/theme-context";
15
+ import type { StarPromptConfig } from "../../types/config";
16
+ import { displayWidth } from "../../utils/format";
17
+ import {
18
+ finishStarPrompt,
19
+ isStarPromptDue,
20
+ localDayKey,
21
+ markStarPromptShown,
22
+ recordStarPromptDay,
23
+ STAR_PROMPT_DELAY_MS,
24
+ STAR_PROMPT_REPO_URL,
25
+ STAR_PROMPT_VISIBLE_MS,
26
+ } from "./model";
27
+ import { bindStarPromptKeys, isStarPromptAllowed } from "./runtime";
28
+
29
+ /** A session left open past midnight counts the new day too. */
30
+ const DAY_CHECK_INTERVAL_MS = 10 * 60_000;
31
+ const STAR = "★ ";
32
+ /** Between the star action and the dismiss one. */
33
+ const GAP = " ";
34
+
35
+ interface StarPromptCopy {
36
+ lead: string;
37
+ columns: number;
38
+ }
39
+
40
+ /**
41
+ * The longest lead the status bar has room for beside its other widgets, or
42
+ * null when not even the short one fits; the line then waits for a wider
43
+ * terminal rather than show cut off.
44
+ */
45
+ function starPromptCopy(widgetRoom: number, openKey: string, dismissKey: string): StarPromptCopy | null {
46
+ const spare = widgetRoom - STATUS_WIDGET_COLUMNS;
47
+ const keys = (openKey ? 1 + displayWidth(openKey) : 0)
48
+ + GAP.length
49
+ + 1 + (dismissKey ? 1 + displayWidth(dismissKey) : 0)
50
+ // The chip padding after the line.
51
+ + 1;
52
+ for (const lead of [t("Enjoying Gloomberb? Star it on GitHub"), t("Star on GitHub")]) {
53
+ const columns = displayWidth(STAR + lead) + keys;
54
+ if (spare >= columns) return { lead, columns };
55
+ }
56
+ return null;
57
+ }
58
+
59
+ type Phase = "waiting" | "showing" | "done";
60
+
61
+ interface StarPromptStatusLineProps {
62
+ delayMs?: number;
63
+ visibleMs?: number;
64
+ }
65
+
66
+ /**
67
+ * One line in the terminal's status bar, once, asking to star the repository:
68
+ * the notification action key opens it in the browser, the dismiss key or the
69
+ * × hides it, and it times out on its own. It never takes focus and sits in
70
+ * the status bar's own row, so it covers nothing. See `./model.ts` for when.
71
+ */
72
+ export function StarPromptStatusLine(props: StarPromptStatusLineProps) {
73
+ if (!isStarPromptAllowed()) return null;
74
+ return <StarPromptLine {...props} />;
75
+ }
76
+
77
+ function StarPromptLine({ delayMs = STAR_PROMPT_DELAY_MS, visibleMs = STAR_PROMPT_VISIBLE_MS }: StarPromptStatusLineProps) {
78
+ useAppLanguage();
79
+ const colors = useThemeColors();
80
+ const dispatch = useAppDispatch();
81
+ const getState = useAppGetState();
82
+ const rendererHost = useRendererHost();
83
+ const onScreen = useAppActive();
84
+ const widgetRoom = useStatusWidgetRoom();
85
+ const openKey = useActionShortcut("notification-action");
86
+ const dismissKey = useActionShortcut("notification-dismiss");
87
+ const starPrompt = useAppSelector((state) => state.config.starPrompt);
88
+ // Days of use count from the first one past setup.
89
+ const onboarded = useAppSelector((state) => state.config.onboardingComplete === true && !state.config.onboardingProgress);
90
+ const [phase, setPhase] = useState<Phase>("waiting");
91
+ const waitedMs = useRef(0);
92
+ const shownMs = useRef(0);
93
+ const copy = starPromptCopy(widgetRoom, openKey, dismissKey);
94
+ const fits = copy !== null;
95
+ const due = isStarPromptDue(starPrompt);
96
+
97
+ const update = useCallback((change: (state: StarPromptConfig | undefined) => StarPromptConfig | undefined) => {
98
+ const current = getState().config.starPrompt;
99
+ const next = change(current);
100
+ if (next === current) return;
101
+ dispatch({ type: "SET_STAR_PROMPT", starPrompt: next });
102
+ // Assembled when the write fires, recent tickers included, as the app's own saves are.
103
+ scheduleConfigSave(() => {
104
+ const state = getState();
105
+ return { ...state.config, recentTickers: state.recentTickers };
106
+ });
107
+ }, [dispatch, getState]);
108
+
109
+ useEffect(() => {
110
+ if (!onboarded) return;
111
+ const record = () => update((state) => recordStarPromptDay(state, localDayKey(new Date())));
112
+ record();
113
+ const timer = setInterval(record, DAY_CHECK_INTERVAL_MS);
114
+ return () => clearInterval(timer);
115
+ }, [onboarded, update]);
116
+
117
+ // Counts only time on screen with room for the line, so it never shows to
118
+ // a terminal in the background or too narrow to read it.
119
+ useEffect(() => {
120
+ if (phase !== "waiting" || !due || !onScreen || !fits) return;
121
+ const startedAt = Date.now();
122
+ const timer = setTimeout(() => {
123
+ if (!isStarPromptDue(getState().config.starPrompt)) {
124
+ setPhase("done");
125
+ return;
126
+ }
127
+ // Recorded as it appears, so quitting while it shows also ends it.
128
+ update((state) => markStarPromptShown(state, new Date()));
129
+ setPhase("showing");
130
+ }, Math.max(0, delayMs - waitedMs.current));
131
+ return () => {
132
+ clearTimeout(timer);
133
+ waitedMs.current += Date.now() - startedAt;
134
+ };
135
+ }, [delayMs, due, fits, getState, onScreen, phase, update]);
136
+
137
+ const finish = useCallback((outcome: NonNullable<StarPromptConfig["outcome"]>) => {
138
+ update((state) => finishStarPrompt(state, outcome));
139
+ setPhase("done");
140
+ }, [update]);
141
+ const open = useCallback(() => {
142
+ void rendererHost.openExternal(STAR_PROMPT_REPO_URL);
143
+ finish("opened");
144
+ }, [finish, rendererHost]);
145
+ const dismiss = useCallback(() => finish("dismissed"), [finish]);
146
+
147
+ useEffect(() => {
148
+ if (phase !== "showing" || !onScreen) return;
149
+ const startedAt = Date.now();
150
+ const timer = setTimeout(() => finish("expired"), Math.max(0, visibleMs - shownMs.current));
151
+ return () => {
152
+ clearTimeout(timer);
153
+ shownMs.current += Date.now() - startedAt;
154
+ };
155
+ }, [finish, onScreen, phase, visibleMs]);
156
+
157
+ const showing = phase === "showing" && fits;
158
+ useEffect(() => (showing ? bindStarPromptKeys({ open, dismiss }) : undefined), [dismiss, open, showing]);
159
+ // The version chip gives way to the line.
160
+ useStatusWidgetClaim("star-prompt", showing && copy ? copy.columns : 0);
161
+
162
+ if (!showing || !copy) return null;
163
+ return (
164
+ <Box flexDirection="row" flexShrink={0} paddingRight={1} data-gloom-role="star-prompt">
165
+ <Button
166
+ label={t("Star Gloomberb on GitHub")}
167
+ variant="plain"
168
+ compact
169
+ shortcut={openKey || undefined}
170
+ stopPropagation
171
+ onPress={open}
172
+ >
173
+ <Text fg={colors.text}>
174
+ <Span fg={colors.warning}>{STAR}</Span>
175
+ {copy.lead}
176
+ </Text>
177
+ </Button>
178
+ <Text>{GAP}</Text>
179
+ <Button
180
+ label={t("Dismiss")}
181
+ displayLabel="×"
182
+ variant="plain"
183
+ compact
184
+ shortcut={dismissKey || undefined}
185
+ stopPropagation
186
+ onPress={dismiss}
187
+ />
188
+ </Box>
189
+ );
190
+ }
package/src/app.tsx CHANGED
@@ -12,6 +12,7 @@ import {
12
12
  import { bindAppActivity, useAppActive, useAppVisible } from "./state/app/activity";
13
13
  import { Header } from "./components/layout/header";
14
14
  import { StatusBar } from "./components/layout/status-bar";
15
+ import { useAppChromeShown } from "./components/layout/presentation";
15
16
  import { useLinkedLayoutSync } from "./layout-marketplace/linked-sync";
16
17
  import { useTeamCollectionsSync } from "./plugins/builtin/cloud/team/collections-sync";
17
18
  import { Shell } from "./components/layout/shell";
@@ -20,6 +21,7 @@ import { TransientLayoutProvider } from "./components/layout/transient-layout";
20
21
  import { CommandBar } from "./components/command-bar/surface";
21
22
  import { OnboardingWizard } from "./components/onboarding/onboarding-wizard";
22
23
  import { CompanyPickerHost } from "./plugins/builtin/cloud/company-picker";
24
+ import { TerminalRelayHost } from "./plugins/builtin/cloud/terminal-relay/host";
23
25
  import { SignInGate } from "./components/sign-in-gate";
24
26
  import { useDialog } from "./ui/dialog";
25
27
  import { PluginRegistry } from "./plugins/registry";
@@ -400,6 +402,7 @@ function AppInner({
400
402
  }
401
403
  }, [focusedTickerSymbol]);
402
404
 
405
+ const chrome = useAppChromeShown();
403
406
  const keybindingHost = useUiHost().kind === "opentui" ? "terminal" : "desktop";
404
407
  const keybindings = useResolvedKeybindings(state.config.keybindings, keybindingHost);
405
408
  useAppGlobalShortcuts({
@@ -451,13 +454,15 @@ function AppInner({
451
454
  desktopWindowBridge={desktopWindowBridge}
452
455
  >
453
456
  <ThemedAppRoot>
454
- <Header
455
- onOpenHelp={() => {
456
- recordFunctionOpen({ shortcut: "HELP", externalPluginId: null });
457
- pluginRegistry.showPane("help");
458
- }}
459
- onRestartForUpdate={updatesEnabled && !isDetachedWindow ? restartToApplyUpdate : undefined}
460
- />
457
+ {chrome.header && (
458
+ <Header
459
+ onOpenHelp={() => {
460
+ recordFunctionOpen({ shortcut: "HELP", externalPluginId: null });
461
+ pluginRegistry.showPane("help");
462
+ }}
463
+ onRestartForUpdate={updatesEnabled && !isDetachedWindow ? restartToApplyUpdate : undefined}
464
+ />
465
+ )}
461
466
  <TransientLayoutProvider>
462
467
  <Shell
463
468
  pluginRegistry={pluginRegistry}
@@ -476,6 +481,7 @@ function AppInner({
476
481
  />
477
482
  </TransientLayoutProvider>
478
483
  {!onboardingActive ? <CompanyPickerHost pluginRegistry={pluginRegistry} /> : null}
484
+ <TerminalRelayHost device={remoteControlAdapter?.terminalDevice} />
479
485
  {onboardingActive && onOnboardingComplete ? (
480
486
  <OnboardingWizard
481
487
  pluginRegistry={pluginRegistry}
@@ -0,0 +1,70 @@
1
+ {
2
+ "src/api-client/askg.ts": ["cloud"],
3
+ "src/app.tsx": ["cloud"],
4
+ "src/app/global-shortcuts.ts": ["help"],
5
+ "src/app/pane-runtime/plugin-bindings.ts": ["broker-manager","layout-manager","plugin-marketplace"],
6
+ "src/app/runtime/broker-import.ts": ["portfolio-list"],
7
+ "src/app/runtime/desktop-deeplink.ts": ["account-management","chat","cloud"],
8
+ "src/app/runtime/startup.ts": ["chat","plugin-marketplace"],
9
+ "src/brokers/signed-in/sign-in-dialog.tsx": ["cloud"],
10
+ "src/cli/commands/market.ts": ["holders","research","sec"],
11
+ "src/cli/commands/options-chain.ts": ["options","options-calculator"],
12
+ "src/cli/commands/options-leaps.ts": ["options","vol-surface"],
13
+ "src/cli/commands/overview.ts": ["correlation","econ","market-movers","sectors","world-indices"],
14
+ "src/cli/commands/ticker.ts": ["notes","portfolio-list"],
15
+ "src/cli/desktop-pane-shot.ts": ["market-valuation","time-sales"],
16
+ "src/cli/pane-functions/data.ts": ["chart-composer","correlation","seasonality","ticker-detail"],
17
+ "src/cli/pane-functions/financial-ratio-shot.ts": ["ticker-detail"],
18
+ "src/cli/pane-functions/headless.ts": ["portfolio-list"],
19
+ "src/cli/pane-functions/options.ts": ["ticker-detail"],
20
+ "src/cli/pane-functions/resolver.ts": ["chart-composer"],
21
+ "src/cli/pane-functions/screenshot-evidence.ts": ["attention","awards","catalysts","company-attention","company-kpis","credit-documents","exposure","market-heatmap","options-calculator","options-scenario","power","realized-vol","supply-chain","vol-surface","volatility","world-venue-map"],
22
+ "src/cli/pane-functions/screenshot.ts": ["chart-composer","econ-statistics","market-valuation","portfolio-list","ticker-detail","time-sales"],
23
+ "src/components/command-bar/commands/collection.ts": ["portfolio-list"],
24
+ "src/components/command-bar/commands/direct/index.ts": ["broker-manager","portfolio-list"],
25
+ "src/components/command-bar/routes/root/command-items.ts": ["portfolio-list"],
26
+ "src/components/command-bar/routes/root/help-items.ts": ["help"],
27
+ "src/components/command-bar/routes/root/plugin-install.ts": ["plugin-marketplace"],
28
+ "src/components/command-bar/routes/root/shortcut-items.ts": ["portfolio-list"],
29
+ "src/components/command-bar/workflow/builtin.ts": ["cloud","portfolio-list"],
30
+ "src/components/command-bar/workflow/collection-actions.ts": ["cloud","portfolio-list"],
31
+ "src/components/command-bar/workflow/ops.ts": ["correlation","portfolio-list","ticker-detail"],
32
+ "src/components/command-bar/workflow/tickers.ts": ["portfolio-list"],
33
+ "src/components/feedback-dialog.tsx": ["cloud"],
34
+ "src/components/form-modal/routes.ts": ["portfolio-list"],
35
+ "src/components/index.ts": ["portfolio-list"],
36
+ "src/components/layout/missing-pane.tsx": ["plugin-marketplace"],
37
+ "src/components/layout/shell/index.tsx": ["cloud"],
38
+ "src/components/layout/status-bar-groups.ts": ["cloud"],
39
+ "src/components/layout/status-bar.tsx": ["cloud"],
40
+ "src/components/onboarding/account-step/form-panel.tsx": ["cloud"],
41
+ "src/components/onboarding/account-step/index.tsx": ["cloud"],
42
+ "src/components/onboarding/account-step/qr-panel.tsx": ["cloud"],
43
+ "src/components/onboarding/account-step/signed-in-panel.tsx": ["cloud"],
44
+ "src/components/onboarding/onboarding-keyboard.ts": ["account-management"],
45
+ "src/components/onboarding/onboarding-wizard.tsx": ["account-management","cloud"],
46
+ "src/components/onboarding/use-onboarding-upgrade.ts": ["account-management","chat","cloud"],
47
+ "src/components/onboarding/wizard-account.ts": ["cloud"],
48
+ "src/components/onboarding/wizard-model.ts": ["portfolio-list"],
49
+ "src/components/onboarding/wizard-positions.ts": ["portfolio-list"],
50
+ "src/components/sign-in-gate.tsx": ["cloud"],
51
+ "src/layout-marketplace/gallery.tsx": ["cloud","plugin-marketplace"],
52
+ "src/layout-marketplace/linked-sync.ts": ["cloud"],
53
+ "src/layout/desks.ts": ["chart-composer"],
54
+ "src/plugins/auto-update-node.ts": ["plugin-marketplace"],
55
+ "src/plugins/auto-update.ts": ["plugin-marketplace"],
56
+ "src/plugins/catalog-browser.ts": ["alerts","analytics","attention","awards","backtest","bond-calculator","catalysts","cds","central-bank-rates","changelog","chart-composer","clinical-trials","cloud","comment-letters","company-attention","company-kpis","connections","correlation","cot","cpi","credit-boards","credit-conditions","credit-documents","crypto-board","debt-maturities","dividend-yield","doe","earnings-calls","earnings-ripple","econ","econ-statistics","equity-screener","estimate-revisions","executives","exposure","fear-greed","filing-events","futures","futures-curve","fx-matrix","gpu","help","insider","ipo-calendar","iv-history","jobs","kelly-sizer","layout-manager","macro-day","market-halts","market-heatmap","market-valuation","members","mna","money-markets","news","openfda","options","options-calculator","options-positioning","options-scenario","pe-band","perps","portfolio-list","power","rate-path","realized-vol","relative-rotation","research","research-search","revenue-breakdown","reverse-dcf","risk-factors","scanner","seasonality","sec","sectors","short-interest","short-volume","social-mentions","supply-chain","ticker-detail","time-sales","treasury-auctions","vol-surface","volatility","world-indices","world-venue-map","yield-curve"],
57
+ "src/plugins/catalog-ui.ts": ["alerts","clinical-trials","cloud","comment-letters","custom-view","fear-greed","ipo-calendar","market-halts","market-heatmap","members","news","notes","openfda","research-search"],
58
+ "src/plugins/catalog.ts": ["debug"],
59
+ "src/plugins/installer.ts": ["plugin-marketplace"],
60
+ "src/plugins/manager-node.ts": ["plugin-marketplace"],
61
+ "src/remote/controller.ts": ["custom-view"],
62
+ "src/renderers/browser/main.tsx": ["plugin-marketplace"],
63
+ "src/renderers/electrobun/bun/core-capabilities.ts": ["notes"],
64
+ "src/renderers/electrobun/shared/protocol.ts": ["plugin-marketplace"],
65
+ "src/renderers/electrobun/view/cli-pane-shot-entry.tsx": ["econ-statistics","market-valuation","time-sales"],
66
+ "src/renderers/electrobun/view/main.tsx": ["notes","plugin-marketplace"],
67
+ "src/renderers/electrobun/view/remote/connection-health.ts": ["connections"],
68
+ "src/renderers/opentui/start.tsx": ["plugin-marketplace"],
69
+ "src/sync/core-contributors.ts": ["analytics","portfolio-list"]
70
+ }
@@ -23,11 +23,38 @@ import { SIGNED_IN_BROKER_TYPE, signedInBrokerId } from "./profile";
23
23
 
24
24
  const METHOD = "Sign in";
25
25
 
26
+ /**
27
+ * The message Gloom wrote for the client. The request error's own `message`
28
+ * has the error code appended to it, so this reads the body's field first.
29
+ */
30
+ function writtenMessage(error: ApiRequestError): string | null {
31
+ const written = error.details?.message;
32
+ if (typeof written === "string" && written.trim()) return written.trim();
33
+ const { message, code } = error;
34
+ const text = code && message.endsWith(` ${code}`) ? message.slice(0, -(code.length + 1)) : message;
35
+ return text.trim() || null;
36
+ }
37
+
38
+ /**
39
+ * Why the broker ended a sign-in, as Gloom stores it, without the OAuth code
40
+ * in front: "invalid_grant: Authorization grant has expired" reads
41
+ * "Authorization grant has expired".
42
+ */
43
+ function brokerReason(lastError: string | null | undefined): string | null {
44
+ const text = lastError?.trim();
45
+ if (!text) return null;
46
+ const described = /^[a-z_]+:\s*(\S[\s\S]*)$/.exec(text)?.[1] ?? text;
47
+ return described.replace(/[\s.]+$/, "").slice(0, 200) || null;
48
+ }
49
+
26
50
  /** What to tell the user when Gloom answers a broker call with an error. */
27
51
  function describeSignedInBrokerError(error: unknown, brokerName: string): string {
28
52
  const status = error instanceof ApiRequestError ? error.status : undefined;
29
53
  if (status === 401) return `Sign in to Gloom first, then connect ${brokerName}.`;
30
- if (status === 404 || status === 409) return `${brokerName} needs you to sign in again. Press Connect in Brokers.`;
54
+ const signInAgain = `${brokerName} needs you to sign in again. Press Connect in Brokers.`;
55
+ // A sign-in the broker ended says why, in Gloom's words.
56
+ if (status === 409) return (error instanceof ApiRequestError && writtenMessage(error)) || signInAgain;
57
+ if (status === 404) return signInAgain;
31
58
  if (status === 502) return `${brokerName} is not responding. Try again shortly.`;
32
59
  return error instanceof Error && error.message ? error.message : String(error);
33
60
  }
@@ -215,7 +242,11 @@ export function createSignedInBrokerAdapter(overrides: Partial<SignedInBrokerAda
215
242
  await track(instance, async () => {
216
243
  const connection = await get<SignedInBrokerConnection>(instance, "");
217
244
  if (connection.status !== "connected") {
218
- throw new Error(`Press Connect in Brokers to sign in to ${brokerName(instance)}.`);
245
+ const name = brokerName(instance);
246
+ const reason = brokerReason(connection.lastError);
247
+ throw new Error(reason
248
+ ? `${name} needs you to sign in again: ${reason}. Press Connect in Brokers.`
249
+ : `Press Connect in Brokers to sign in to ${name}.`);
219
250
  }
220
251
  });
221
252
  },
@@ -31,6 +31,16 @@ export interface SignedInBrokerConnection {
31
31
  connectedAt: string | null;
32
32
  refreshedAt: string | null;
33
33
  syncedAt: string | null;
34
+ /**
35
+ * When the broker ends this sign-in on its own clock, however often it was
36
+ * refreshed. Set only while connected and only for a broker that does so.
37
+ * The three fields below are absent from a Gloom server that predates them.
38
+ */
39
+ expiresAt?: string | null;
40
+ /** Within a day of `expiresAt`, or past it: time to connect again. */
41
+ expiresSoon?: boolean;
42
+ /** Why the broker ended the sign-in, in its own words. Null while connected. */
43
+ lastError?: string | null;
34
44
  }
35
45
 
36
46
  /** Public: the list does not depend on the user, so Add Broker can show it signed out. */