gloomberb 0.16.2 → 0.16.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (527) hide show
  1. package/package.json +4 -1
  2. package/src/api-client/askg.ts +43 -0
  3. package/src/api-client/auth.ts +15 -0
  4. package/src/api-client/beneficial-owners.ts +4 -0
  5. package/src/api-client/chat.ts +19 -0
  6. package/src/api-client/company-kpis.ts +14 -2
  7. package/src/api-client/data.ts +34 -16
  8. package/src/api-client/index.ts +12 -0
  9. package/src/api-client/normalizers.ts +27 -0
  10. package/src/api-client/paths.ts +88 -23
  11. package/src/api-client/request.ts +2 -1
  12. package/src/api-client/research-activity.ts +31 -0
  13. package/src/api-client/socket.ts +39 -8
  14. package/src/api-client/types.ts +57 -1
  15. package/src/api-client/upgrade-experiment.ts +90 -0
  16. package/src/app/global-shortcuts.ts +11 -1
  17. package/src/app/keybindings/actions.ts +10 -0
  18. package/src/app/pane-runtime/index.tsx +8 -0
  19. package/src/app/pane-runtime/pane-template-runtime.ts +5 -0
  20. package/src/app/pane-runtime/plugin-bindings.ts +65 -25
  21. package/src/app/pane-runtime/ticker-inspector-runtime.ts +12 -0
  22. package/src/app/pane-runtime/ticker-open-runtime.ts +18 -1
  23. package/src/app/runtime/desktop-deeplink.ts +5 -0
  24. package/src/app/star-prompt/model.ts +98 -0
  25. package/src/app/star-prompt/runtime.ts +41 -0
  26. package/src/app/star-prompt/status-line.tsx +190 -0
  27. package/src/app.tsx +13 -7
  28. package/src/architecture/core-builtin-allowlist.json +70 -0
  29. package/src/brokers/account-cache.ts +45 -0
  30. package/src/brokers/signed-in/adapter.ts +33 -2
  31. package/src/brokers/signed-in/client.ts +10 -0
  32. package/src/brokers/signed-in/sign-in-dialog.tsx +23 -9
  33. package/src/brokers/signed-in/sign-in.ts +62 -5
  34. package/src/capabilities/registry.ts +8 -1
  35. package/src/capabilities/types.ts +2 -0
  36. package/src/cli/command-options.ts +74 -0
  37. package/src/cli/commands/command-utils.ts +29 -0
  38. package/src/cli/commands/crypto-hints.ts +12 -4
  39. package/src/cli/commands/market.ts +524 -148
  40. package/src/cli/commands/options-chain.ts +172 -0
  41. package/src/cli/commands/options-leaps.ts +422 -0
  42. package/src/cli/commands/overview.ts +223 -42
  43. package/src/cli/commands/search.ts +29 -8
  44. package/src/cli/commands/system.ts +99 -19
  45. package/src/cli/commands/themes.ts +64 -0
  46. package/src/cli/commands/ticker.ts +200 -73
  47. package/src/cli/desktop-pane-shot.ts +100 -16
  48. package/src/cli/errors.ts +25 -2
  49. package/src/cli/freshness.ts +7 -4
  50. package/src/cli/fx-pair.ts +57 -0
  51. package/src/cli/help.ts +40 -7
  52. package/src/cli/history-rows.ts +187 -15
  53. package/src/cli/index-roots.ts +51 -0
  54. package/src/cli/index.ts +21 -13
  55. package/src/cli/listing-arg.ts +211 -3
  56. package/src/cli/not-a-ticker.ts +37 -0
  57. package/src/cli/options.ts +55 -7
  58. package/src/cli/pane-functions/capabilities.ts +13 -2
  59. package/src/cli/pane-functions/catalog.ts +13 -2
  60. package/src/cli/pane-functions/data.ts +36 -5
  61. package/src/cli/pane-functions/dom.ts +89 -28
  62. package/src/cli/pane-functions/freshness.ts +205 -26
  63. package/src/cli/pane-functions/function-help.ts +71 -24
  64. package/src/cli/pane-functions/headless.ts +168 -35
  65. package/src/cli/pane-functions/index.ts +12 -2
  66. package/src/cli/pane-functions/market-session.ts +133 -0
  67. package/src/cli/pane-functions/options.ts +15 -4
  68. package/src/cli/pane-functions/report-notices.ts +63 -0
  69. package/src/cli/pane-functions/report.ts +12 -1
  70. package/src/cli/pane-functions/resolver.ts +32 -8
  71. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  72. package/src/cli/pane-functions/screenshot.ts +109 -23
  73. package/src/cli/registry.ts +8 -4
  74. package/src/cli/report-tables.ts +460 -0
  75. package/src/cli/result.ts +63 -16
  76. package/src/cli/row-window.ts +76 -0
  77. package/src/cli/scroll-truncation.ts +34 -0
  78. package/src/components/chart/composite/composite-chart.tsx +43 -4
  79. package/src/components/chart/composite/legend.tsx +39 -6
  80. package/src/components/chart/composite/rasterizer.ts +64 -4
  81. package/src/components/chart/composite/series-cues.ts +71 -0
  82. package/src/components/chart/composite/text-renderer.ts +16 -8
  83. package/src/components/chart/composite/types.ts +3 -0
  84. package/src/components/command-bar/commands/direct/index.ts +9 -1
  85. package/src/components/command-bar/commands/plugin/items.ts +3 -1
  86. package/src/components/command-bar/commands/registry.ts +7 -0
  87. package/src/components/command-bar/layout-items/focused-pane.ts +38 -0
  88. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  89. package/src/components/command-bar/routes/root/command-items.ts +2 -0
  90. package/src/components/command-bar/routes/root/plugin-turn-on.ts +92 -0
  91. package/src/components/command-bar/routes/root/results.ts +40 -10
  92. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  93. package/src/components/command-bar/routes/root/search-providers.ts +97 -14
  94. package/src/components/command-bar/surface/index.tsx +18 -4
  95. package/src/components/command-bar/theme-picker.tsx +48 -13
  96. package/src/components/command-bar/workflow/ops.ts +5 -3
  97. package/src/components/data-table/export.ts +6 -51
  98. package/src/components/data-table/view.tsx +31 -10
  99. package/src/components/layout/floating-pane.tsx +6 -2
  100. package/src/components/layout/market-summary.ts +23 -9
  101. package/src/components/layout/pane/content.tsx +46 -29
  102. package/src/components/layout/pane/cycle-order.ts +2 -11
  103. package/src/components/layout/pane/header.tsx +2 -1
  104. package/src/components/layout/pane/title.ts +1 -1
  105. package/src/components/layout/presentation.ts +22 -0
  106. package/src/components/layout/shell/active-drag.ts +67 -35
  107. package/src/components/layout/shell/cursor-occlusion.ts +6 -5
  108. package/src/components/layout/shell/drag/index.ts +16 -7
  109. package/src/components/layout/shell/drag/live.ts +193 -0
  110. package/src/components/layout/shell/drag/overlays.tsx +179 -35
  111. package/src/components/layout/shell/drag/runtime.ts +62 -50
  112. package/src/components/layout/shell/drag/slide.ts +10 -0
  113. package/src/components/layout/shell/index.tsx +84 -57
  114. package/src/components/layout/shell/layout-state.ts +5 -11
  115. package/src/components/layout/shell/menu.ts +28 -1
  116. package/src/components/layout/shell/native/pointer-runtime.ts +13 -13
  117. package/src/components/layout/shell/native/surfaces.ts +30 -14
  118. package/src/components/layout/shell/pane/actions.ts +2 -1
  119. package/src/components/layout/shell/pane/layers.tsx +461 -180
  120. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  121. package/src/components/layout/shell/pane/new-layout.ts +116 -0
  122. package/src/components/layout/shell/shortcuts.ts +5 -2
  123. package/src/components/layout/shell/terminal-pointer-runtime.ts +18 -18
  124. package/src/components/layout/shell/visible-layout.ts +44 -1
  125. package/src/components/layout/shell/window-mode/overlays.tsx +12 -4
  126. package/src/components/layout/status-bar.tsx +4 -1
  127. package/src/components/markdown-text.tsx +9 -5
  128. package/src/components/metric-treemap/heat-surface.tsx +27 -8
  129. package/src/components/metric-treemap/heat-tiles.ts +48 -17
  130. package/src/components/onboarding/onboarding-frame.tsx +33 -10
  131. package/src/components/onboarding/onboarding-wizard.tsx +49 -40
  132. package/src/components/onboarding/portfolio-step/positions-panel.tsx +5 -2
  133. package/src/components/onboarding/pro-step-layout.ts +86 -0
  134. package/src/components/onboarding/use-onboarding-upgrade.ts +6 -2
  135. package/src/components/ui/button.tsx +10 -0
  136. package/src/components/ui/data-table/index.tsx +38 -0
  137. package/src/components/ui/data-table/types.ts +8 -1
  138. package/src/components/ui/data-table/visible-range.ts +2 -0
  139. package/src/components/ui/icon.tsx +11 -2
  140. package/src/components/ui/index.ts +1 -1
  141. package/src/core/connection-health.ts +1 -1
  142. package/src/core/state/app/layout-reducer.ts +89 -3
  143. package/src/core/state/app/state.ts +8 -5
  144. package/src/core/state/app/types.ts +13 -2
  145. package/src/data/config/layout.ts +55 -21
  146. package/src/data/config/store/fold-panes.ts +149 -0
  147. package/src/data/config/store/migrations.ts +7 -0
  148. package/src/data/config/store/node.ts +27 -6
  149. package/src/data/config/store/normalize.ts +37 -3
  150. package/src/data/json-persistence.ts +17 -3
  151. package/src/data/json-storage.ts +2 -0
  152. package/src/data/memory-resource-store.ts +46 -0
  153. package/src/i18n/es.ts +14 -1
  154. package/src/i18n/ja.ts +14 -1
  155. package/src/i18n/ko.ts +14 -1
  156. package/src/i18n/zh-cn.ts +14 -1
  157. package/src/i18n/zh-tw.ts +14 -1
  158. package/src/layout/desks.ts +1 -2
  159. package/src/layout/pane-layout-move.ts +264 -0
  160. package/src/layout/pane-manager/hidden-panes.ts +160 -0
  161. package/src/layout/pane-manager/index.ts +1 -0
  162. package/src/market-data/coordinator/entries.ts +7 -1
  163. package/src/market-data/coordinator/quotes.ts +2 -2
  164. package/src/market-data/market/freshness.ts +156 -11
  165. package/src/market-data/market/status.ts +40 -0
  166. package/src/market-data/options-alternatives.ts +69 -0
  167. package/src/market-data/quotes/contributions.ts +19 -2
  168. package/src/market-data/quotes/freshness.ts +5 -0
  169. package/src/market-data/quotes/metadata.ts +16 -0
  170. package/src/market-data/quotes/screener-live-quotes.ts +56 -5
  171. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  172. package/src/plugins/builtin/account-management/model.ts +1 -0
  173. package/src/plugins/builtin/account-management/navigation.ts +1 -1
  174. package/src/plugins/builtin/account-management/pane.tsx +31 -4
  175. package/src/plugins/builtin/alerts/quotes.ts +3 -0
  176. package/src/plugins/builtin/analytics/pane-model.ts +10 -6
  177. package/src/plugins/builtin/analytics/risk-headless.ts +3 -1
  178. package/src/plugins/builtin/analytics/risk-model.ts +37 -8
  179. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -4
  180. package/src/plugins/builtin/attention/pane.tsx +1 -1
  181. package/src/plugins/builtin/backtest/pane.tsx +1 -1
  182. package/src/plugins/builtin/broker-manager/detail.tsx +4 -0
  183. package/src/plugins/builtin/broker-manager/index.tsx +7 -3
  184. package/src/plugins/builtin/broker-manager/model.ts +8 -0
  185. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -2
  186. package/src/plugins/builtin/broker-manager/sign-in-end.ts +82 -0
  187. package/src/plugins/builtin/broker-manager/table.ts +5 -0
  188. package/src/plugins/builtin/builtin-plugin-meta.ts +121 -11
  189. package/src/plugins/builtin/catalysts/detail.tsx +1 -1
  190. package/src/plugins/builtin/chart-composer/pane.tsx +1 -1
  191. package/src/plugins/builtin/chat/channels.ts +34 -36
  192. package/src/plugins/builtin/chat/command-bar.ts +138 -0
  193. package/src/plugins/builtin/chat/composer-commands.ts +21 -1
  194. package/src/plugins/builtin/chat/content/composer-runtime.ts +11 -1
  195. package/src/plugins/builtin/chat/content/footer.tsx +5 -3
  196. package/src/plugins/builtin/chat/content/index.tsx +21 -1
  197. package/src/plugins/builtin/chat/content/mentions.ts +2 -1
  198. package/src/plugins/builtin/chat/content/user-map.ts +3 -1
  199. package/src/plugins/builtin/chat/controller/index.ts +48 -0
  200. package/src/plugins/builtin/chat/conversations.ts +256 -0
  201. package/src/plugins/builtin/chat/discord-command.ts +91 -0
  202. package/src/plugins/builtin/chat/edit-window.ts +12 -6
  203. package/src/plugins/builtin/chat/ghost-user.ts +14 -0
  204. package/src/plugins/builtin/chat/index.tsx +40 -2
  205. package/src/plugins/builtin/chat/layout.ts +2 -0
  206. package/src/plugins/builtin/chat/message/desktop.tsx +2 -1
  207. package/src/plugins/builtin/chat/message/header.tsx +24 -15
  208. package/src/plugins/builtin/chat/message/profile-popover.tsx +2 -1
  209. package/src/plugins/builtin/chat/message/render-state.ts +3 -0
  210. package/src/plugins/builtin/chat/pane-requests.ts +60 -0
  211. package/src/plugins/builtin/chat/pane-state.ts +32 -2
  212. package/src/plugins/builtin/chat/pane.tsx +8 -0
  213. package/src/plugins/builtin/cloud/askg/controller.ts +112 -4
  214. package/src/plugins/builtin/cloud/askg/conversation-store.ts +21 -0
  215. package/src/plugins/builtin/cloud/askg/manifest.ts +1 -0
  216. package/src/plugins/builtin/cloud/askg/model.ts +129 -70
  217. package/src/plugins/builtin/cloud/askg/pane.tsx +772 -377
  218. package/src/plugins/builtin/cloud/askg/protocol.ts +40 -2
  219. package/src/plugins/builtin/cloud/askg/tool-display.ts +437 -0
  220. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +403 -0
  221. package/src/plugins/builtin/cloud/company-picker.tsx +1 -9
  222. package/src/plugins/builtin/cloud/mcp-connect/command.ts +24 -0
  223. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +360 -0
  224. package/src/plugins/builtin/cloud/mcp-connect/model.ts +135 -0
  225. package/src/plugins/builtin/cloud/mcp-connect/sections.ts +62 -0
  226. package/src/plugins/builtin/cloud/plugin.tsx +36 -1
  227. package/src/plugins/builtin/cloud/starter-symbols.ts +8 -0
  228. package/src/plugins/builtin/cloud/terminal-relay/acm-tab.tsx +89 -0
  229. package/src/plugins/builtin/cloud/terminal-relay/client.ts +97 -0
  230. package/src/plugins/builtin/cloud/terminal-relay/connect-section.tsx +57 -0
  231. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +666 -0
  232. package/src/plugins/builtin/cloud/terminal-relay/grants.ts +137 -0
  233. package/src/plugins/builtin/cloud/terminal-relay/host.tsx +81 -0
  234. package/src/plugins/builtin/cloud/terminal-relay/indicator.tsx +82 -0
  235. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +234 -0
  236. package/src/plugins/builtin/cloud/terminal-relay/policy.ts +153 -0
  237. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +168 -0
  238. package/src/plugins/builtin/cloud/terminal-relay/redact.ts +73 -0
  239. package/src/plugins/builtin/cloud/terminal-relay/summary.ts +62 -0
  240. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +26 -2
  241. package/src/plugins/builtin/cloud/upgrade-personalization.ts +57 -0
  242. package/src/plugins/builtin/cloud/upgrade-tickers.ts +124 -0
  243. package/src/plugins/builtin/comment-letters/client.ts +1 -3
  244. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  245. package/src/plugins/builtin/company-kpis/client.ts +23 -8
  246. package/src/plugins/builtin/company-kpis/headless.ts +3 -1
  247. package/src/plugins/builtin/company-kpis/pane.tsx +15 -7
  248. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +10 -3
  249. package/src/plugins/builtin/composite-plugins.ts +107 -64
  250. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +4 -4
  251. package/src/plugins/builtin/credit-boards/index.tsx +21 -21
  252. package/src/plugins/builtin/credit-boards/model.ts +0 -1
  253. package/src/plugins/builtin/credit-boards/pane.tsx +30 -0
  254. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +4 -4
  255. package/src/plugins/builtin/debt-maturities/client.ts +37 -9
  256. package/src/plugins/builtin/debt-maturities/headless.ts +3 -1
  257. package/src/plugins/builtin/debt-maturities/pane.tsx +5 -4
  258. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  259. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  260. package/src/plugins/builtin/earnings-calls/data.ts +28 -7
  261. package/src/plugins/builtin/earnings-calls/headless.ts +3 -2
  262. package/src/plugins/builtin/earnings-calls/pane.tsx +10 -5
  263. package/src/plugins/builtin/econ/calendar-model.ts +42 -17
  264. package/src/plugins/builtin/econ/detail-view.tsx +2 -2
  265. package/src/plugins/builtin/econ/index.tsx +68 -22
  266. package/src/plugins/builtin/econ-statistics/headless.ts +12 -2
  267. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  268. package/src/plugins/builtin/econ-statistics/view.ts +17 -0
  269. package/src/plugins/builtin/executives/data.ts +17 -6
  270. package/src/plugins/builtin/executives/pane.tsx +16 -7
  271. package/src/plugins/builtin/filing-events/pane.tsx +22 -5
  272. package/src/plugins/builtin/fx-matrix/freshness.ts +65 -0
  273. package/src/plugins/builtin/fx-matrix/index.tsx +22 -1
  274. package/src/plugins/builtin/fx-matrix/pairs.ts +1 -1
  275. package/src/plugins/builtin/fx-matrix/settings.ts +42 -2
  276. package/src/plugins/builtin/gpu/headless.ts +9 -3
  277. package/src/plugins/builtin/gpu/model.ts +14 -5
  278. package/src/plugins/builtin/help/index.tsx +1 -0
  279. package/src/plugins/builtin/holders/beneficial-client.ts +29 -7
  280. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  281. package/src/plugins/builtin/holders/client.ts +3 -6
  282. package/src/plugins/builtin/holders/format.ts +31 -1
  283. package/src/plugins/builtin/holders/headless.ts +32 -6
  284. package/src/plugins/builtin/holders/pane.tsx +25 -13
  285. package/src/plugins/builtin/holders/report-header.ts +46 -5
  286. package/src/plugins/builtin/insider/index.tsx +4 -4
  287. package/src/plugins/builtin/iv-history/charts.tsx +21 -7
  288. package/src/plugins/builtin/iv-history/client.ts +12 -2
  289. package/src/plugins/builtin/iv-history/format.ts +33 -2
  290. package/src/plugins/builtin/iv-history/headless.ts +22 -10
  291. package/src/plugins/builtin/iv-history/index.tsx +3 -3
  292. package/src/plugins/builtin/iv-history/model.ts +61 -13
  293. package/src/plugins/builtin/iv-history/pane.tsx +11 -3
  294. package/src/plugins/builtin/iv-history/screen-pane.tsx +42 -18
  295. package/src/plugins/builtin/jobs/pane.tsx +1 -1
  296. package/src/plugins/builtin/kelly-sizer/headless.ts +149 -0
  297. package/src/plugins/builtin/kelly-sizer/history.ts +80 -0
  298. package/src/plugins/builtin/kelly-sizer/index.tsx +2 -0
  299. package/src/plugins/builtin/kelly-sizer/pane.tsx +121 -45
  300. package/src/plugins/builtin/kelly-sizer/portfolio.ts +25 -0
  301. package/src/plugins/builtin/kelly-sizer/sections.tsx +14 -0
  302. package/src/plugins/builtin/kelly-sizer/types.ts +9 -1
  303. package/src/plugins/builtin/layout-manager/index.tsx +9 -1
  304. package/src/plugins/builtin/macro-day/client.ts +24 -0
  305. package/src/plugins/builtin/macro-day/headless.ts +11 -8
  306. package/src/plugins/builtin/macro-day/index.tsx +3 -0
  307. package/src/plugins/builtin/macro-day/model.ts +11 -6
  308. package/src/plugins/builtin/macro-day/pane.tsx +12 -8
  309. package/src/plugins/builtin/macro-day/release-table.ts +40 -0
  310. package/src/plugins/builtin/macro-day/releases.ts +62 -44
  311. package/src/plugins/builtin/market-heatmap/data.ts +25 -5
  312. package/src/plugins/builtin/market-heatmap/evidence.ts +90 -0
  313. package/src/plugins/builtin/market-heatmap/index.tsx +144 -63
  314. package/src/plugins/builtin/market-heatmap/model.ts +94 -3
  315. package/src/plugins/builtin/market-heatmap/portfolio.ts +12 -2
  316. package/src/plugins/builtin/market-movers/headless.ts +31 -4
  317. package/src/plugins/builtin/market-movers/index.tsx +8 -1
  318. package/src/plugins/builtin/market-movers/model.ts +71 -17
  319. package/src/plugins/builtin/market-movers/table.ts +50 -23
  320. package/src/plugins/builtin/members/pane.tsx +10 -0
  321. package/src/plugins/builtin/news/headless.ts +9 -2
  322. package/src/plugins/builtin/news/wire/news/footer.ts +10 -0
  323. package/src/plugins/builtin/openfda/client.ts +1 -1
  324. package/src/plugins/builtin/options/carry.ts +95 -0
  325. package/src/plugins/builtin/options/index.tsx +58 -5
  326. package/src/plugins/builtin/options/strike-window.ts +183 -0
  327. package/src/plugins/builtin/options/table.ts +55 -3
  328. package/src/plugins/builtin/options/types.ts +8 -1
  329. package/src/plugins/builtin/options/view.tsx +161 -12
  330. package/src/plugins/builtin/options-positioning/headless.ts +4 -2
  331. package/src/plugins/builtin/options-scenario/charts.tsx +2 -2
  332. package/src/plugins/builtin/options-scenario/client.ts +73 -17
  333. package/src/plugins/builtin/options-scenario/headless.ts +71 -24
  334. package/src/plugins/builtin/options-scenario/hedge.ts +284 -0
  335. package/src/plugins/builtin/options-scenario/index.tsx +9 -2
  336. package/src/plugins/builtin/options-scenario/model.ts +33 -0
  337. package/src/plugins/builtin/options-scenario/pane.tsx +40 -16
  338. package/src/plugins/builtin/pe-band/model.ts +2 -2
  339. package/src/plugins/builtin/pe-band/report-dates.ts +11 -7
  340. package/src/plugins/builtin/perps/equity-row.tsx +8 -5
  341. package/src/plugins/builtin/perps/model.ts +16 -5
  342. package/src/plugins/builtin/perps/pane.tsx +1 -1
  343. package/src/plugins/builtin/plugin-marketplace/model.ts +64 -21
  344. package/src/plugins/builtin/plugin-marketplace/packs.ts +118 -0
  345. package/src/plugins/builtin/plugin-marketplace/pane.tsx +344 -32
  346. package/src/plugins/builtin/plugin-marketplace/store.ts +7 -1
  347. package/src/plugins/builtin/plugin-module.ts +87 -4
  348. package/src/plugins/builtin/portfolio-list/account-metrics.ts +16 -1
  349. package/src/plugins/builtin/portfolio-list/allocation.ts +3 -0
  350. package/src/plugins/builtin/portfolio-list/cached-account.ts +18 -9
  351. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +71 -36
  352. package/src/plugins/builtin/portfolio-list/cli/render.ts +89 -18
  353. package/src/plugins/builtin/portfolio-list/cli/shared.ts +2 -16
  354. package/src/plugins/builtin/portfolio-list/headless.ts +40 -7
  355. package/src/plugins/builtin/portfolio-list/pane/index.tsx +7 -4
  356. package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -4
  357. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +11 -1
  358. package/src/plugins/builtin/realized-vol/headless.ts +3 -0
  359. package/src/plugins/builtin/realized-vol/pane.tsx +1 -1
  360. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -2
  361. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +1 -1
  362. package/src/plugins/builtin/research/relative-valuation-model.ts +7 -3
  363. package/src/plugins/builtin/revenue-breakdown/client.ts +13 -6
  364. package/src/plugins/builtin/revenue-breakdown/headless.ts +4 -1
  365. package/src/plugins/builtin/revenue-breakdown/pane.tsx +7 -4
  366. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -1
  367. package/src/plugins/builtin/reverse-dcf/model.ts +2 -1
  368. package/src/plugins/builtin/reverse-dcf/pane.tsx +1 -1
  369. package/src/plugins/builtin/risk-factors/data.ts +23 -9
  370. package/src/plugins/builtin/risk-factors/headless.ts +5 -2
  371. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  372. package/src/plugins/builtin/seasonality/pane.tsx +1 -1
  373. package/src/plugins/builtin/sec/index.tsx +5 -5
  374. package/src/plugins/builtin/sectors/client.ts +10 -2
  375. package/src/plugins/builtin/sectors/headless.ts +35 -6
  376. package/src/plugins/builtin/sectors/index.tsx +18 -1
  377. package/src/plugins/builtin/sectors/sector-model.ts +150 -41
  378. package/src/plugins/builtin/shared/command-tab.ts +51 -0
  379. package/src/plugins/builtin/shared/headless-market-data.ts +33 -1
  380. package/src/plugins/builtin/shared/members-menu.tsx +260 -0
  381. package/src/plugins/builtin/shared/report-freshness.ts +50 -2
  382. package/src/plugins/builtin/shared/ticker-request.ts +25 -1
  383. package/src/plugins/builtin/shared/use-quote-board.ts +25 -5
  384. package/src/plugins/builtin/shared/volatility/options.ts +1 -46
  385. package/src/plugins/builtin/short-interest/index.tsx +19 -4
  386. package/src/plugins/builtin/short-interest/surface.tsx +4 -4
  387. package/src/plugins/builtin/short-volume/index.tsx +1 -10
  388. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  389. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  390. package/src/plugins/builtin/supply-chain/model.ts +1 -1
  391. package/src/plugins/builtin/themes/pane.tsx +3 -0
  392. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +4 -0
  393. package/src/plugins/builtin/ticker-detail/financials/model.ts +25 -2
  394. package/src/plugins/builtin/ticker-detail/headless.ts +63 -25
  395. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -4
  396. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +3 -2
  397. package/src/plugins/builtin/ticker-detail/pane.tsx +4 -5
  398. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +94 -6
  399. package/src/plugins/builtin/treasury-auctions/cache.ts +5 -2
  400. package/src/plugins/builtin/treasury-auctions/client.ts +22 -8
  401. package/src/plugins/builtin/treasury-auctions/headless.ts +40 -4
  402. package/src/plugins/builtin/treasury-auctions/index.tsx +11 -9
  403. package/src/plugins/builtin/treasury-auctions/model.ts +197 -30
  404. package/src/plugins/builtin/treasury-auctions/pane.tsx +35 -11
  405. package/src/plugins/builtin/treasury-auctions/types.ts +11 -1
  406. package/src/plugins/builtin/vol-surface/charts.tsx +1 -3
  407. package/src/plugins/builtin/vol-surface/client.ts +6 -2
  408. package/src/plugins/builtin/vol-surface/evidence.ts +11 -6
  409. package/src/plugins/builtin/vol-surface/headless.ts +124 -20
  410. package/src/plugins/builtin/vol-surface/model.ts +23 -11
  411. package/src/plugins/builtin/vol-surface/pane.tsx +38 -27
  412. package/src/plugins/builtin/vol-surface/tables.ts +124 -0
  413. package/src/plugins/builtin/world-indices/client.ts +22 -4
  414. package/src/plugins/builtin/world-indices/headless.ts +54 -27
  415. package/src/plugins/builtin/world-indices/model.ts +4 -2
  416. package/src/plugins/builtin/world-indices/table.tsx +13 -2
  417. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +2 -0
  418. package/src/plugins/builtin/world-venue-map/basemap.ts +18 -0
  419. package/src/plugins/builtin/world-venue-map/country-labels.ts +78 -0
  420. package/src/plugins/builtin/world-venue-map/geo-series.ts +1 -1
  421. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +2 -1
  422. package/src/plugins/builtin/world-venue-map/index.ts +6 -6
  423. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +27 -14
  424. package/src/plugins/builtin/world-venue-map/layers.ts +68 -10
  425. package/src/plugins/builtin/world-venue-map/map.tsx +198 -62
  426. package/src/plugins/builtin/world-venue-map/model.ts +16 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +3 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/labels.chunk.ts +244 -0
  429. package/src/plugins/builtin/world-venue-map/pane.tsx +16 -10
  430. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  431. package/src/plugins/builtin-editorial.ts +126 -0
  432. package/src/plugins/catalog-backend.ts +16 -9
  433. package/src/plugins/catalog-browser.ts +127 -63
  434. package/src/plugins/catalog-manifest.ts +73 -40
  435. package/src/plugins/catalog-ui.ts +26 -6
  436. package/src/plugins/extracted-plugins.ts +7 -1
  437. package/src/plugins/ownership.ts +150 -7
  438. package/src/plugins/plugin-functions.ts +54 -0
  439. package/src/plugins/plugin-off.ts +36 -0
  440. package/src/plugins/registry/host-actions.ts +16 -0
  441. package/src/plugins/registry/index.ts +84 -24
  442. package/src/plugins/registry/plugin-state.ts +25 -1
  443. package/src/plugins/runtime/context.tsx +29 -0
  444. package/src/react/input.ts +48 -1
  445. package/src/remote/app-host.tsx +5 -0
  446. package/src/remote/controller.ts +3 -53
  447. package/src/remote/matching.ts +69 -0
  448. package/src/remote/schema.ts +1 -1
  449. package/src/remote/semantic-tree.tsx +14 -0
  450. package/src/remote/types.ts +2 -0
  451. package/src/renderers/browser/app-services.ts +3 -1
  452. package/src/renderers/dom/data-table/dom.ts +31 -0
  453. package/src/renderers/dom/data-table/index.tsx +5 -0
  454. package/src/renderers/dom/desktop/icons.tsx +14 -0
  455. package/src/renderers/dom/desktop/query-bar.tsx +59 -32
  456. package/src/renderers/dom/host/box.tsx +113 -17
  457. package/src/renderers/dom/host/drag-shield.ts +35 -0
  458. package/src/renderers/dom/host/live-frame.ts +62 -0
  459. package/src/renderers/dom/host/mouse.ts +8 -1
  460. package/src/renderers/dom/host/scroll-box.tsx +6 -5
  461. package/src/renderers/dom/host/tabs.tsx +3 -2
  462. package/src/renderers/dom/input-host.tsx +6 -1
  463. package/src/renderers/dom/styles.css +26 -5
  464. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +7 -0
  465. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  466. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +12 -1
  467. package/src/renderers/electrobun/bun/desktop/initialization.ts +15 -1
  468. package/src/renderers/electrobun/shared/protocol.ts +12 -1
  469. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +4 -1
  470. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +101 -5
  471. package/src/renderers/electrobun/view/host/native.ts +0 -10
  472. package/src/renderers/electrobun/view/host/window-move.ts +28 -0
  473. package/src/renderers/electrobun/view/main.tsx +4 -1
  474. package/src/renderers/electrobun/view/remote/persistence.ts +15 -2
  475. package/src/renderers/electrobun/view/ui-host.tsx +2 -5
  476. package/src/renderers/opentui/input-host.tsx +6 -1
  477. package/src/renderers/opentui/start.tsx +9 -0
  478. package/src/renderers/opentui/ui-host.tsx +7 -1
  479. package/src/shares/portable-layout.ts +16 -6
  480. package/src/sources/gloomberb-cloud/index.ts +19 -10
  481. package/src/sources/gloomberb-cloud/normalizers.ts +20 -21
  482. package/src/sources/listing-symbols.ts +11 -5
  483. package/src/sources/provider-errors.ts +88 -3
  484. package/src/sources/provider-router/batches.ts +3 -3
  485. package/src/sources/provider-router/cached-routes.ts +8 -4
  486. package/src/sources/provider-router/financial-routes.ts +13 -7
  487. package/src/sources/provider-router/financials.ts +106 -16
  488. package/src/sources/provider-router/history.ts +27 -11
  489. package/src/sources/provider-router/index.ts +1 -1
  490. package/src/sources/provider-router/primary.ts +24 -6
  491. package/src/sources/sec-edgar.ts +68 -8
  492. package/src/state/pane-scroll-registry.tsx +67 -35
  493. package/src/sync/core-contributors.ts +4 -3
  494. package/src/test-support/cli-context.ts +2 -1
  495. package/src/test-support/data-provider.ts +34 -0
  496. package/src/test-support/margin-account.ts +108 -0
  497. package/src/test-support/shell-pane-keys.tsx +1 -0
  498. package/src/theme/series-colors.ts +46 -0
  499. package/src/theme/themes.ts +149 -0
  500. package/src/tickers/search/index.ts +22 -2
  501. package/src/tickers/search/ranking.ts +45 -1
  502. package/src/tickers/search/types.ts +3 -1
  503. package/src/time-series/resolution.ts +15 -0
  504. package/src/time-series/resolve.ts +1 -14
  505. package/src/types/config.ts +61 -1
  506. package/src/types/financials.ts +32 -3
  507. package/src/types/headless.ts +11 -0
  508. package/src/types/instrument.ts +8 -0
  509. package/src/types/plugin.ts +49 -3
  510. package/src/ui/host.tsx +16 -0
  511. package/src/ui/index.tsx +10 -3
  512. package/src/ui/scroll-box-axis.ts +26 -0
  513. package/src/utils/cli-output.ts +14 -1
  514. package/src/utils/currency-units.ts +8 -3
  515. package/src/utils/depositary-receipt.ts +54 -10
  516. package/src/utils/display-number.ts +63 -0
  517. package/src/utils/exchanges.ts +13 -0
  518. package/src/utils/format.ts +2 -1
  519. package/src/utils/fundamentals.ts +17 -1
  520. package/src/utils/fx-market-hours.ts +34 -0
  521. package/src/utils/option-expiry.ts +126 -0
  522. package/src/utils/price-history-integrity.ts +19 -6
  523. package/src/utils/price-history.ts +39 -6
  524. package/src/utils/rpc-timeout-error.ts +6 -2
  525. package/src/utils/sec-eps-basis.ts +159 -30
  526. package/src/utils/sec.ts +38 -34
  527. package/src/utils/throttled-fetch.ts +51 -22
@@ -10,6 +10,7 @@ import type { ManualChartResolution } from "../time-series/resolution";
10
10
  import type { SnapshotMarketData } from "../market-data/snapshot-provider";
11
11
  import type { InstrumentRef } from "../market-data/request-types";
12
12
  import type { TickerRecord } from "../types/ticker";
13
+ import type { BrokerAccount } from "../types/trading";
13
14
  import type { PaneRuntimeState } from "../core/state/app/state";
14
15
  import type { RemoteUiNodeSnapshot } from "../remote/types";
15
16
  import type { DatedObservation } from "../plugins/builtin/market-valuation/series";
@@ -17,6 +18,7 @@ import type { DesktopExternalPluginBundle } from "../renderers/electrobun/shared
17
18
  import type { HttpProxyRequestEnvelope, HttpProxyResponseEnvelope } from "../utils/http-proxy-response";
18
19
  import type { PaneAccessGate } from "./pane-functions/access-gate";
19
20
  import { readVisibleKeyValues } from "./visible-key-values";
21
+ import { scrollTruncationReasons } from "./scroll-truncation";
20
22
  import { SHOT_API_PROXY_PREFIX, SHOT_HTTP_BRIDGE_PATH, SHOT_MARKET_BRIDGE_PATH } from "./desktop-pane-shot-routes";
21
23
  import { SESSION_COOKIE_NAMES } from "../api-client/session-cookie";
22
24
  import {
@@ -53,7 +55,14 @@ export interface DesktopPaneShotPayload {
53
55
  deviceScaleFactor?: number;
54
56
  /** Label drawn at the right edge of the pane title bar; null draws nothing. */
55
57
  watermark?: string | null;
58
+ /**
59
+ * The pane footer keeps a row for the capture's dated status line, filled
60
+ * once the pane has loaded. Absent for `--no-status` and for reports.
61
+ */
62
+ statusLine?: boolean;
56
63
  tickers: TickerRecord[];
64
+ /** Broker accounts as last synced, by profile, so a broker portfolio shows its equity and cash. */
65
+ brokerAccounts?: Record<string, BrokerAccount[]>;
57
66
  financials: Array<[string, TickerFinancials]>;
58
67
  instrumentFinancials?: SnapshotMarketData["instrumentFinancials"];
59
68
  historyVariants?: SnapshotMarketData["historyVariants"];
@@ -108,6 +117,14 @@ export interface DesktopPaneShotRenderedRow {
108
117
 
109
118
  export interface DesktopPaneShotRenderResult {
110
119
  visibleText: string;
120
+ /**
121
+ * The rows a report reads, when a table reports every row (an option chain's
122
+ * strike window) rather than those in the viewport; `rows` stays what the
123
+ * capture shows. Absent when every table is read from the viewport.
124
+ */
125
+ reportRows?: DesktopPaneShotRenderedRow[];
126
+ /** `truncationReasons` without the cuts of tables whose every row is in `reportRows`. */
127
+ reportTruncationReasons?: string[];
111
128
  /** Text of the pane footers, where a pane says whether its data is live, delayed or stale. */
112
129
  footerText?: string;
113
130
  visibleKeyValues?: Array<{ label: string; text: string }>;
@@ -122,8 +139,16 @@ export interface DesktopPaneShotRenderResult {
122
139
  /** A wall that needs an account, or a Pro lock on part of the pane. */
123
140
  accessGate?: PaneAccessGate | null;
124
141
  semanticUi: RemoteUiNodeSnapshot[];
142
+ /** The status line drawn in the capture's footer, as it fit the width. */
143
+ statusLine?: string | null;
125
144
  }
126
145
 
146
+ /**
147
+ * The status line variants for a capture, longest first, worked out from what
148
+ * the pane rendered; null leaves the footer row empty.
149
+ */
150
+ type ShotStatusLine = (rendered: DesktopPaneShotRenderResult) => readonly string[] | null | Promise<readonly string[] | null>;
151
+
127
152
  type CdpResponse = {
128
153
  id?: number;
129
154
  result?: unknown;
@@ -160,7 +185,7 @@ export async function renderDesktopPaneScreenshot(
160
185
  payload: DesktopPaneShotPayload,
161
186
  outputPath: string,
162
187
  apiProxy: DesktopPaneShotApiProxy,
163
- options: { captureImage?: boolean; bridge?: DesktopPaneShotBridge } = {},
188
+ options: { captureImage?: boolean; bridge?: DesktopPaneShotBridge; statusLine?: ShotStatusLine } = {},
164
189
  ): Promise<DesktopPaneShotRenderResult> {
165
190
  const tempDir = await mkdtemp(join(tmpdir(), "gloom-pane-shot-"));
166
191
  let server: ReturnType<typeof Bun.serve> | null = null;
@@ -179,6 +204,7 @@ export async function renderDesktopPaneScreenshot(
179
204
  deviceScaleFactor: payload.deviceScaleFactor ?? DEFAULT_DEVICE_SCALE_FACTOR,
180
205
  userDataDir: join(tempDir, "chrome-profile"),
181
206
  captureImage: options.captureImage !== false,
207
+ statusLine: payload.statusLine ? options.statusLine : undefined,
182
208
  });
183
209
  } finally {
184
210
  server?.stop(true);
@@ -399,6 +425,7 @@ async function capturePageScreenshot({
399
425
  deviceScaleFactor,
400
426
  userDataDir,
401
427
  captureImage,
428
+ statusLine,
402
429
  }: {
403
430
  chrome: string;
404
431
  url: string;
@@ -408,6 +435,7 @@ async function capturePageScreenshot({
408
435
  deviceScaleFactor: number;
409
436
  userDataDir: string;
410
437
  captureImage: boolean;
438
+ statusLine?: ShotStatusLine;
411
439
  }): Promise<DesktopPaneShotRenderResult> {
412
440
  await mkdir(userDataDir, { recursive: true });
413
441
  const port = 43000 + Math.floor(Math.random() * 10000);
@@ -454,6 +482,10 @@ async function capturePageScreenshot({
454
482
  });
455
483
  await waitForShotReady(session);
456
484
  const rendered = await readRenderedPaneState(session);
485
+ if (captureImage && statusLine) {
486
+ const variants = await statusLine(rendered);
487
+ rendered.statusLine = variants?.length ? await fitShotStatusLine(session, variants) : null;
488
+ }
457
489
  if (captureImage) {
458
490
  const screenshot = await session.send("Page.captureScreenshot", {
459
491
  format: "png",
@@ -512,6 +544,8 @@ const EMPTY_STATE_PATTERNS = [
512
544
  /\bNo transcribed calls(?: for [^.]+)? yet\b/gi,
513
545
  /\bNo short interest (?:data|found)\b/gi,
514
546
  /\bNo (?:House PTR |insider )?(?:trades|members|transactions|filings)\b/gi,
547
+ // OMON on an expiry the chain does not list, with the dates nearest it.
548
+ /\bNo (?:option contracts returned for )?expiry \d{4}-\d{2}-\d{2}(?:; (?:available|nearest others): \d{4}-\d{2}-\d{2}(?:, \d{4}-\d{2}-\d{2})*(?: \(\+\d+ more\))?|; the chain lists no expiries)?/gi,
515
549
  ];
516
550
 
517
551
  function stateMarkers(text: string, patterns: RegExp[]): string[] {
@@ -538,26 +572,39 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
538
572
  const semanticTables = semanticUi.filter((node) => node && node.role === "table");
539
573
  const rows = [];
540
574
  const truncationReasons = new Set();
575
+ const scrollReasons = (${scrollTruncationReasons.toString()});
576
+ const visibleTables = [...root.querySelectorAll('[data-gloom-role="data-table"]')].filter(isVisible);
577
+ // A table that publishes every row has nothing cut from a report, whatever the viewport shows.
578
+ const reportedRowsOf = (tableIndex) => {
579
+ const metadata = semanticTables[tableIndex] && semanticTables[tableIndex].metadata;
580
+ return metadata && Array.isArray(metadata.rows) ? metadata.rows : null;
581
+ };
582
+ const reportedTables = new Set(visibleTables.filter((_table, tableIndex) => reportedRowsOf(tableIndex)));
583
+ const reportRows = [];
584
+ const reportTruncationReasons = new Set();
585
+ const inReportedTable = (element) => {
586
+ const table = element.closest('[data-gloom-role="data-table"]');
587
+ return !!table && reportedTables.has(table);
588
+ };
541
589
  const markScrollTruncation = (element) => {
542
590
  if (!(element instanceof HTMLElement) || !isVisible(element)) return;
543
- if (
544
- element.getAttribute("data-gloom-scrollbar-y") === "visible"
545
- || element.scrollHeight > element.clientHeight + 1
546
- ) {
547
- truncationReasons.add("rows extend below the rendered viewport");
548
- }
549
- if (
550
- element.getAttribute("data-gloom-scrollbar-x") === "visible"
551
- || element.scrollWidth > element.clientWidth + 1
552
- ) {
553
- truncationReasons.add("columns extend beyond the rendered viewport");
554
- }
591
+ const reasons = scrollReasons({
592
+ scrollTop: element.scrollTop,
593
+ scrollHeight: element.scrollHeight,
594
+ clientHeight: element.clientHeight,
595
+ scrollLeft: element.scrollLeft,
596
+ scrollWidth: element.scrollWidth,
597
+ clientWidth: element.clientWidth,
598
+ scrollbarY: element.getAttribute("data-gloom-scrollbar-y") === "visible",
599
+ scrollbarX: element.getAttribute("data-gloom-scrollbar-x") === "visible",
600
+ });
601
+ reasons.forEach((reason) => truncationReasons.add(reason));
602
+ if (!inReportedTable(element)) reasons.forEach((reason) => reportTruncationReasons.add(reason));
555
603
  };
556
604
  [...root.querySelectorAll(
557
605
  '[data-gloom-scrollbar-x="visible"], [data-gloom-scrollbar-y="visible"], [data-gloom-role="data-table-body-scroll"]',
558
606
  )].forEach(markScrollTruncation);
559
- [...root.querySelectorAll('[data-gloom-role="data-table"]')]
560
- .filter(isVisible)
607
+ visibleTables
561
608
  .forEach((table, tableIndex) => {
562
609
  const tableMetadata = semanticTables[tableIndex] && semanticTables[tableIndex].metadata || {};
563
610
  const semanticColumns = Array.isArray(tableMetadata.columns) ? tableMetadata.columns : [];
@@ -583,6 +630,7 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
583
630
  ))
584
631
  ) {
585
632
  truncationReasons.add("one or more cells are visibly clipped");
633
+ if (!reportedTables.has(table)) reportTruncationReasons.add("one or more cells are visibly clipped");
586
634
  }
587
635
  const instant = cell.getAttribute("data-gloom-cell-instant");
588
636
  return {
@@ -595,12 +643,27 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
595
643
  };
596
644
  }).filter((cell) => cell.text.length > 0);
597
645
  if (cells.length === 0) return;
598
- rows.push({
646
+ const captured = {
599
647
  tableIndex,
600
648
  rowIndex,
601
649
  ...(rowKey ? { key: rowKey } : {}),
602
650
  selected: row.getAttribute("data-selected") === "true",
603
651
  cells,
652
+ };
653
+ rows.push(captured);
654
+ if (!reportedTables.has(table)) reportRows.push(captured);
655
+ });
656
+ const reported = reportedRowsOf(tableIndex);
657
+ if (!reported) return;
658
+ reported.forEach((row, rowIndex) => {
659
+ const cells = Array.isArray(row && row.cells) ? row.cells.filter((cell) => cell && cell.text) : [];
660
+ if (cells.length === 0) return;
661
+ reportRows.push({
662
+ tableIndex,
663
+ rowIndex,
664
+ ...(typeof row.key === "string" ? { key: row.key } : {}),
665
+ selected: row.selected === true,
666
+ cells,
604
667
  });
605
668
  });
606
669
  });
@@ -639,6 +702,7 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
639
702
  rows,
640
703
  truncated: truncationReasons.size > 0,
641
704
  truncationReasons: [...truncationReasons],
705
+ ...(reportedTables.size > 0 ? { reportRows, reportTruncationReasons: [...reportTruncationReasons] } : {}),
642
706
  semanticUi,
643
707
  };
644
708
  })()`,
@@ -657,6 +721,8 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
657
721
  rows?: DesktopPaneShotRenderedRow[];
658
722
  truncated?: boolean;
659
723
  truncationReasons?: string[];
724
+ reportRows?: DesktopPaneShotRenderedRow[];
725
+ reportTruncationReasons?: string[];
660
726
  semanticUi?: RemoteUiNodeSnapshot[];
661
727
  };
662
728
  };
@@ -684,6 +750,10 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
684
750
  rows,
685
751
  truncated: value?.truncated === true || textShowsEllipsis,
686
752
  truncationReasons,
753
+ ...(Array.isArray(value?.reportRows) ? {
754
+ reportRows: value.reportRows,
755
+ reportTruncationReasons: (value.reportTruncationReasons ?? []).filter((reason): reason is string => typeof reason === "string"),
756
+ } : {}),
687
757
  loadingStateDetected: value?.loadingStateDetected === true || loadingStateMarkers.length > 0,
688
758
  errorStateDetected: value?.errorStateDetected === true || errorStateMarkers.length > 0,
689
759
  errorStateMarkers,
@@ -694,6 +764,20 @@ async function readRenderedPaneState(session: CdpSession): Promise<DesktopPaneSh
694
764
  };
695
765
  }
696
766
 
767
+ /** Draws the first variant that fits the pane footer, and returns what it drew. */
768
+ async function fitShotStatusLine(session: CdpSession, variants: readonly string[]): Promise<string | null> {
769
+ const result = await session.send("Runtime.evaluate", {
770
+ expression: `(() => {
771
+ const fit = window.__GLOOM_CLI_SHOT_FIT_STATUS__;
772
+ return fit ? fit(${JSON.stringify(variants)}) : null;
773
+ })()`,
774
+ awaitPromise: true,
775
+ returnByValue: true,
776
+ }) as { result?: { value?: unknown } };
777
+ const value = result.result?.value;
778
+ return typeof value === "string" ? value : null;
779
+ }
780
+
697
781
  async function waitForPageWebSocket(port: number, targetUrl: string): Promise<string> {
698
782
  for (let attempt = 0; attempt < CHROME_POLL_ATTEMPTS; attempt += 1) {
699
783
  try {
package/src/cli/errors.ts CHANGED
@@ -1,13 +1,19 @@
1
1
  import type { AppPersistence } from "../data/app-persistence";
2
+ import { ApiRequestError } from "../api-client/errors";
2
3
  import { DEFAULT_CLI_OPTIONS, type CliGlobalOptions } from "./options";
3
4
  import { serializeCliError, type CliErrorObject } from "./result";
4
5
  import { cliStyles, cliTerminalWidth, wrapText } from "../utils/cli-output";
6
+ import { withSearchHints } from "./not-a-ticker";
5
7
 
6
8
  const USAGE_PREFIX = "Usage: ";
9
+ /** The code of every error a command fails with: bad input, nothing found, an option it does not take. */
10
+ const CLI_ERROR_CODE = "cli_error";
7
11
 
8
12
  export interface CliErrorContext {
9
13
  /** The command that failed, used to point at its help. */
10
14
  command?: string;
15
+ /** What the user typed after the command, so a hint can repeat their own spelling of a symbol. */
16
+ args?: readonly string[];
11
17
  }
12
18
 
13
19
  class CliFailure extends Error {
@@ -15,7 +21,7 @@ class CliFailure extends Error {
15
21
  readonly details?: unknown;
16
22
  readonly retryable?: boolean;
17
23
 
18
- constructor(message: string, details?: unknown, code = "cli_error", retryable?: boolean) {
24
+ constructor(message: string, details?: unknown, code = CLI_ERROR_CODE, retryable?: boolean) {
19
25
  super(message);
20
26
  this.name = "CliFailure";
21
27
  this.code = code;
@@ -37,6 +43,11 @@ function isCliFailure(error: unknown): error is CliFailure {
37
43
  return error instanceof CliFailure;
38
44
  }
39
45
 
46
+ /** A global option that does not parse is a usage error like any other, not an unexpected one. */
47
+ export function asUsageError(error: unknown): Error {
48
+ return isCliFailure(error) ? error : new CliFailure(error instanceof Error ? error.message : String(error));
49
+ }
50
+
40
51
  function cliErrorObject(error: unknown): CliErrorObject {
41
52
  if (isCliFailure(error)) {
42
53
  return {
@@ -46,6 +57,10 @@ function cliErrorObject(error: unknown): CliErrorObject {
46
57
  retryable: error.retryable,
47
58
  };
48
59
  }
60
+ // Gloom Cloud refused what was asked (an unknown series, a bad date): the input was wrong, not the program.
61
+ if (error instanceof ApiRequestError && error.status === 400) {
62
+ return { code: CLI_ERROR_CODE, message: error.message };
63
+ }
49
64
  return {
50
65
  code: "unexpected_error",
51
66
  message: error instanceof Error ? error.message : String(error),
@@ -87,10 +102,18 @@ function formatCliErrorText(error: CliErrorObject, context: CliErrorContext): st
87
102
  ].join("\n");
88
103
  }
89
104
 
105
+ function hintedError(error: CliErrorObject, args: readonly string[]): CliErrorObject {
106
+ return {
107
+ ...error,
108
+ message: withSearchHints(error.message, args),
109
+ ...(typeof error.details === "string" ? { details: withSearchHints(error.details, args) } : {}),
110
+ };
111
+ }
112
+
90
113
  export function printCliError(error: unknown, options: CliGlobalOptions, context: CliErrorContext = {}): void {
91
114
  if (options.quiet && options.format === "text") return;
92
115
  const errorObject = cliErrorObject(error);
93
116
  console.error(options.format === "text"
94
- ? formatCliErrorText(errorObject, context)
117
+ ? formatCliErrorText(hintedError(errorObject, context.args ?? []), context)
95
118
  : serializeCliError(errorObject, options));
96
119
  }
@@ -25,17 +25,20 @@ export function rowsFreshness(
25
25
  }
26
26
 
27
27
  /** One row per quote shown, as `fn` quote reports carry them: live, delayed or stale for its session. */
28
- function quoteFreshnessRow(quote: Quote): HeadlessPaneRow {
29
- return { ...quoteFreshnessFields(quote), updatedAt: quote.lastUpdated };
28
+ function quoteFreshnessRow(quote: Quote, now: number): HeadlessPaneRow {
29
+ return { ...quoteFreshnessFields(quote, now), updatedAt: quote.lastUpdated };
30
30
  }
31
31
 
32
- /** Worst of across the quotes shown, dated by the newest. Quotes that failed to load say nothing. */
32
+ /**
33
+ * Worst of across the quotes shown, dated by the newest, with their delay and
34
+ * where their markets stand. Quotes that failed to load say nothing.
35
+ */
33
36
  export function quotesFreshness(
34
37
  quotes: Iterable<Quote | null | undefined>,
35
38
  declared?: HeadlessPaneFreshness,
36
39
  now = Date.now(),
37
40
  ): ReportFreshness | undefined {
38
- const rows = [...quotes].filter((quote): quote is Quote => quote != null).map(quoteFreshnessRow);
41
+ const rows = [...quotes].filter((quote): quote is Quote => quote != null).map((quote) => quoteFreshnessRow(quote, now));
39
42
  return rows.length > 0 ? rowsFreshness(rows, declared, undefined, now) : undefined;
40
43
  }
41
44
 
@@ -0,0 +1,57 @@
1
+ /**
2
+ * What `gloomberb fx` is asked for. A bare code is one unit of it in the base
3
+ * currency (`NGN`: USD per 1 NGN). A pair is market convention, BASE/QUOTE:
4
+ * `USD/NGN` is how many NGN one USD buys, and `ZAR/NGN` a cross of two codes
5
+ * with neither in USD.
6
+ */
7
+ export interface FxRequest {
8
+ /** The currency one unit of which is priced: NGN in `NGN`, USD in `USD/NGN`. */
9
+ currency: string;
10
+ /** The currency the price is in: the base currency for a bare code, the quote of a pair. */
11
+ baseCurrency: string;
12
+ }
13
+
14
+ const CURRENCY_CODE = /^[A-Z]{3}$/;
15
+
16
+ /** The request, or the sentence saying what is wrong with the argument. */
17
+ export function parseFxRequest(raw: string, baseCurrency: string): FxRequest | { error: string } {
18
+ const text = raw.trim().toUpperCase();
19
+ const parts = text.split("/").map((part) => part.trim());
20
+ if (parts.length > 2 || parts.some((part) => !part)) return { error: `"${raw}" is not a currency or a pair such as USD/NGN.` };
21
+ const bad = parts.filter((part) => !CURRENCY_CODE.test(part));
22
+ if (bad.length > 0) return { error: `${bad.join(" and ")} ${bad.length > 1 ? "are not currency codes" : "is not a currency code"}; use three letters such as NGN.` };
23
+ return parts.length === 2
24
+ ? { currency: parts[0]!, baseCurrency: parts[1]! }
25
+ : { currency: parts[0]!, baseCurrency: baseCurrency.trim().toUpperCase() };
26
+ }
27
+
28
+ /**
29
+ * The rate of `currency` in `baseCurrency` from each one's USD value (USD per
30
+ * unit), the way every cross is built. NaN when a leg is missing or unusable.
31
+ */
32
+ export function crossRate(currencyInUsd: number | null | undefined, baseInUsd: number | null | undefined): number {
33
+ if (!isUsableRate(currencyInUsd) || !isUsableRate(baseInUsd)) return Number.NaN;
34
+ return currencyInUsd / baseInUsd;
35
+ }
36
+
37
+ export function isUsableRate(value: number | null | undefined): value is number {
38
+ return typeof value === "number" && Number.isFinite(value) && value > 0;
39
+ }
40
+
41
+ /**
42
+ * A rate to six significant digits, so a tiny one keeps its figures
43
+ * (0.000752791) and float noise goes; from 1000 up, to the cent (1328.39).
44
+ * Never in exponent notation, and without trailing zeros.
45
+ */
46
+ export function formatFxRate(rate: number): string {
47
+ if (!Number.isFinite(rate)) return String(rate);
48
+ const size = Math.abs(rate);
49
+ const decimals = size >= 1000 || size === 0 ? 2 : Math.min(15, Math.max(0, 5 - Math.floor(Math.log10(size))));
50
+ const fixed = rate.toFixed(decimals);
51
+ return fixed.includes(".") ? fixed.replace(/\.?0+$/, "") : fixed;
52
+ }
53
+
54
+ /** Both directions on one line: `1 NGN = 0.000752791 USD (USD/NGN 1328.39)`. */
55
+ export function describeFxRate(request: FxRequest, rate: number): string {
56
+ return `1 ${request.currency} = ${formatFxRate(rate)} ${request.baseCurrency} (${request.baseCurrency}/${request.currency} ${formatFxRate(1 / rate)})`;
57
+ }
package/src/cli/help.ts CHANGED
@@ -10,6 +10,12 @@ import {
10
10
  wrapText,
11
11
  } from "../utils/cli-output";
12
12
 
13
+ /** `--section` of a command whose report has several tables. */
14
+ export const TABLE_SECTION_OPTION = {
15
+ flags: "--section <title|n>",
16
+ description: "With --csv or --ndjson, write only that table, by its title or number",
17
+ };
18
+
13
19
  /** Headings of `gloomberb help`, in display order. */
14
20
  export const CLI_COMMAND_GROUPS = {
15
21
  research: "Research",
@@ -28,13 +34,25 @@ const INDENT = 2;
28
34
  const MAX_HELP_WIDTH = 100;
29
35
  const PIPED_HELP_WIDTH = 80;
30
36
 
37
+ /** Commands that run as written, for someone who has just installed it. */
38
+ const START_HERE: Array<[string, string]> = [
39
+ ["gloomberb search apple", "Find a ticker from a company name"],
40
+ ["gloomberb quote AAPL", "Show the latest price"],
41
+ ["gloomberb ticker AAPL", "Open the full research report"],
42
+ ["gloomberb catalog earnings", "Find a function, then run it with fn"],
43
+ ];
44
+
31
45
  const GLOBAL_OPTIONS: Array<[string, string]> = [
32
- ["--json, --csv, --ndjson", "Print machine-readable output instead of text"],
33
- ["--limit <n>", "Show at most n rows"],
46
+ ["--json", "Print the full result as JSON"],
47
+ ["--csv", "Print rows as CSV under the columns the text shows; fn reports and fundamentals put each table under # section: and end with # source lines"],
48
+ ["--ndjson", "Print one JSON object per table row"],
49
+ ["--limit <n>", "Show at most n rows, the first n as printed"],
50
+ ["--tail <n>", "Show the newest n rows of a dated series (history, fred, econ) in their printed order, or the last n of another list"],
34
51
  ["--refresh", "Fetch fresh data instead of reading the cache"],
35
52
  ["--dry-run", "Preview config, cache, notes, alerts, plugin on/off, and remote changes without saving"],
36
53
  ["-q, --quiet", "Print no results or errors in text mode, for commands that print results"],
37
54
  ["--color, --no-color", "Force or turn off colors (NO_COLOR=1 also turns them off)"],
55
+ ["--width <n>", "Fit tables to n columns (default: the terminal width; piped output is not fitted). shot takes its own --width in pixels"],
38
56
  ];
39
57
 
40
58
  export interface CliHelpEntry {
@@ -90,6 +108,9 @@ export function renderCliHelp(entries: CliHelpEntry[], version: string, descript
90
108
  `${cliStyles.bold("gloomberb")} ${cliStyles.muted(version)}`,
91
109
  ...wrapText(description, width),
92
110
  "",
111
+ renderSection("Start here"),
112
+ ...renderDefinitions(START_HERE, { width, termStyle: cliStyles.command }),
113
+ "",
93
114
  renderSection("Usage"),
94
115
  ...renderDefinitions([
95
116
  ["gloomberb", "Open the terminal UI"],
@@ -178,14 +199,10 @@ function editDistance(left: string, right: string): number {
178
199
  return previous[right.length]!;
179
200
  }
180
201
 
181
- /** The closest command token to a mistyped one, or null when nothing is plausibly meant. */
182
- export function suggestCliCommand(token: string, candidates: Iterable<string>): string | null {
183
- const needle = token.trim().toLowerCase();
184
- if (!needle || needle.startsWith("-")) return null;
202
+ function closestName(needle: string, candidates: Iterable<string>): string | null {
185
203
  const allowed = needle.length <= 4 ? 1 : 2;
186
204
  let best: { name: string; score: number } | null = null;
187
205
  for (const candidate of candidates) {
188
- if (candidate.startsWith("-")) continue;
189
206
  // A clear prefix ("tick" for "ticker") beats an edit of the same size.
190
207
  const score = needle.length >= 3 && candidate.startsWith(needle)
191
208
  ? 0.5
@@ -195,3 +212,19 @@ export function suggestCliCommand(token: string, candidates: Iterable<string>):
195
212
  }
196
213
  return best?.name ?? null;
197
214
  }
215
+
216
+ /** The closest command token to a mistyped one, or null when nothing is plausibly meant. */
217
+ export function suggestCliCommand(token: string, candidates: Iterable<string>): string | null {
218
+ const needle = token.trim().toLowerCase();
219
+ if (!needle || needle.startsWith("-")) return null;
220
+ return closestName(needle, [...candidates].filter((candidate) => !candidate.startsWith("-")));
221
+ }
222
+
223
+ /** The closest of a command's options to a mistyped one (`--rnage` for `--range`), or null. */
224
+ export function suggestCliOption(flag: string, options: Iterable<string>): string | null {
225
+ const needle = flag.trim().toLowerCase().replace(/^-+/, "");
226
+ if (!needle) return null;
227
+ const byName = new Map([...options].map((option) => [option.replace(/^-+/, ""), option] as const));
228
+ const name = closestName(needle, byName.keys());
229
+ return name == null ? null : byName.get(name)!;
230
+ }