gloomberb 0.11.3 → 0.12.0

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 (489) hide show
  1. package/README.md +6 -4
  2. package/README.zh-CN.md +4 -2
  3. package/package.json +28 -1
  4. package/src/api-client/auth.ts +10 -0
  5. package/src/api-client/data.ts +103 -1
  6. package/src/api-client/index.test.ts +127 -0
  7. package/src/api-client/index.ts +129 -1
  8. package/src/api-client/normalizers.ts +39 -0
  9. package/src/api-client/paths.ts +85 -1
  10. package/src/api-client/types.ts +196 -0
  11. package/src/app/pane-runtime/index.tsx +15 -12
  12. package/src/app/pane-runtime/layout-placement.test.ts +27 -0
  13. package/src/app/pane-runtime/layout-placement.ts +13 -0
  14. package/src/app/pane-runtime/plugin-bindings.ts +62 -0
  15. package/src/app.tsx +60 -18
  16. package/src/architecture/browser-entry-graph.test.ts +96 -0
  17. package/src/brokers/profile-form.test.ts +25 -27
  18. package/src/brokers/test-broker.ts +68 -0
  19. package/src/cli/commands/plugins.ts +39 -11
  20. package/src/cli/desktop-pane-shot.ts +285 -16
  21. package/src/cli/index.ts +1 -1
  22. package/src/cli/pane-functions/capabilities.ts +58 -28
  23. package/src/cli/pane-functions/catalog.ts +16 -4
  24. package/src/cli/pane-functions/discovery.ts +1 -0
  25. package/src/cli/pane-functions/headless.test.ts +247 -0
  26. package/src/cli/pane-functions/headless.ts +423 -0
  27. package/src/cli/pane-functions/index.test.ts +9 -0
  28. package/src/cli/pane-functions/index.ts +6 -2
  29. package/src/cli/pane-functions/options.ts +27 -2
  30. package/src/cli/pane-functions/report.ts +4 -0
  31. package/src/cli/pane-functions/resolver.ts +26 -8
  32. package/src/cli/pane-functions/screenshot.test.ts +76 -0
  33. package/src/cli/pane-functions/screenshot.ts +236 -20
  34. package/src/cli/restore-plugins.ts +34 -0
  35. package/src/components/chart/{static/chart → composite}/axis-overlays.test.tsx +3 -3
  36. package/src/components/chart/{static/chart → composite}/axis-overlays.tsx +6 -11
  37. package/src/components/chart/{static/chart → composite}/bitmap.ts +17 -3
  38. package/src/components/chart/composite/composite-chart.test.tsx +68 -20
  39. package/src/components/chart/composite/composite-chart.tsx +301 -204
  40. package/src/components/chart/composite/format.ts +8 -2
  41. package/src/components/chart/composite/interactions.test.ts +68 -102
  42. package/src/components/chart/composite/interactions.ts +214 -264
  43. package/src/components/chart/composite/price-axis-labels.test.ts +21 -0
  44. package/src/components/chart/{price-axis-labels.tsx → composite/price-axis-labels.tsx} +9 -4
  45. package/src/components/chart/composite/scene.ts +57 -3
  46. package/src/components/chart/composite/text-renderer.ts +3 -2
  47. package/src/components/chart/composite/time-scale.ts +138 -57
  48. package/src/components/chart/composite/types.ts +25 -1
  49. package/src/components/chart/core/data.ts +9 -13
  50. package/src/components/chart/core/date-window.ts +7 -1
  51. package/src/components/chart/core/pointer.ts +3 -0
  52. package/src/components/chart/core/types.ts +0 -45
  53. package/src/components/chart/native/chart-rasterizer.ts +0 -1
  54. package/src/components/chart/native/kitty/index.test.ts +0 -188
  55. package/src/components/chart/native/raster/crosshair.ts +6 -5
  56. package/src/components/chart/native/raster/primitives.ts +0 -23
  57. package/src/components/chart/native/renderer-selection.ts +81 -31
  58. package/src/components/chart/static/{chart/surface.test.tsx → chart-surface.test.tsx} +28 -80
  59. package/src/components/chart/static/chart-surface.tsx +150 -0
  60. package/src/components/chart/static/index.ts +8 -0
  61. package/src/components/chart/static/multi-line-chart-surface.tsx +51 -166
  62. package/src/components/chart/static/scatter-chart-surface.test.ts +30 -0
  63. package/src/components/chart/static/scatter-chart-surface.tsx +135 -34
  64. package/src/components/chart/static/series.ts +49 -0
  65. package/src/components/command-bar/action-runtime.ts +1 -3
  66. package/src/components/command-bar/assist/model.test.ts +17 -2
  67. package/src/components/command-bar/assist/model.ts +40 -9
  68. package/src/components/command-bar/commands/direct/index.ts +1 -1
  69. package/src/components/command-bar/commands/direct/runtime.ts +1 -1
  70. package/src/components/command-bar/commands/registry.ts +0 -11
  71. package/src/components/command-bar/helpers.test.ts +2 -2
  72. package/src/components/command-bar/helpers.ts +2 -6
  73. package/src/components/command-bar/keyboard-shortcuts.ts +0 -11
  74. package/src/components/command-bar/list/badge.test.ts +54 -0
  75. package/src/components/command-bar/list/badge.ts +62 -0
  76. package/src/components/command-bar/list/model.test.ts +103 -0
  77. package/src/components/command-bar/list/model.ts +61 -4
  78. package/src/components/command-bar/list/view.test.tsx +113 -0
  79. package/src/components/command-bar/list/view.tsx +147 -131
  80. package/src/components/command-bar/pane-templates/workflow.ts +1 -1
  81. package/src/components/command-bar/panel/body.tsx +51 -65
  82. package/src/components/command-bar/panel/index.tsx +48 -13
  83. package/src/components/command-bar/panel/layout.test.ts +139 -0
  84. package/src/components/command-bar/panel/layout.ts +65 -28
  85. package/src/components/command-bar/panel/native-surface.ts +24 -0
  86. package/src/components/command-bar/panel/prompt-binding.ts +54 -0
  87. package/src/components/command-bar/panel/runtime.ts +6 -3
  88. package/src/components/command-bar/panel/state.ts +41 -7
  89. package/src/components/command-bar/panel/types.ts +7 -1
  90. package/src/components/command-bar/routes/root/provider-search.ts +34 -15
  91. package/src/components/command-bar/routes/root/results.test.ts +161 -0
  92. package/src/components/command-bar/routes/root/results.ts +30 -28
  93. package/src/components/command-bar/routes/root/runtime.ts +37 -20
  94. package/src/components/command-bar/routes/root/search-providers.test.tsx +224 -0
  95. package/src/components/command-bar/routes/root/search-providers.ts +205 -0
  96. package/src/components/command-bar/routes/root/selection.ts +1 -12
  97. package/src/components/command-bar/routes/root/shortcuts.test.ts +8 -8
  98. package/src/components/command-bar/routes/ticker-search/actions.ts +10 -9
  99. package/src/components/command-bar/routes/ticker-search/results.test.ts +54 -1
  100. package/src/components/command-bar/routes/ticker-search/results.ts +60 -19
  101. package/src/components/command-bar/routes/ticker-search/route.ts +1 -0
  102. package/src/components/command-bar/routing/actions.ts +1 -81
  103. package/src/components/command-bar/routing/launch-request.ts +1 -1
  104. package/src/components/command-bar/routing/list-state.ts +6 -21
  105. package/src/components/command-bar/selection-runtime.ts +1 -1
  106. package/src/components/command-bar/surface/assist.test.tsx +14 -10
  107. package/src/components/command-bar/surface/environment.ts +8 -1
  108. package/src/components/command-bar/surface/index.test.tsx +84 -68
  109. package/src/components/command-bar/surface/index.tsx +33 -36
  110. package/src/components/command-bar/surface/test-harness.tsx +15 -3
  111. package/src/components/command-bar/theme-picker.tsx +43 -15
  112. package/src/components/command-bar/view-model.test.ts +53 -8
  113. package/src/components/command-bar/view-model.ts +43 -14
  114. package/src/components/command-bar/workflow/tickers.ts +1 -0
  115. package/src/components/command-bar/workflow/types.ts +1 -1
  116. package/src/components/data-table/view.test.tsx +45 -6
  117. package/src/components/data-table/view.tsx +55 -9
  118. package/src/components/index.ts +10 -2
  119. package/src/components/input-search-bar.test.tsx +36 -2
  120. package/src/components/input-search-bar.tsx +18 -1
  121. package/src/components/layout/header.test.tsx +57 -10
  122. package/src/components/layout/header.tsx +292 -149
  123. package/src/components/layout/market-summary.test.ts +81 -0
  124. package/src/components/layout/market-summary.ts +109 -0
  125. package/src/components/layout/shell/chrome.ts +96 -1
  126. package/src/components/layout/shell/index.tsx +11 -3
  127. package/src/components/layout/shell/menu.ts +9 -0
  128. package/src/components/layout/shell/pane/actions.ts +29 -0
  129. package/src/components/layout/shell/pane/management-shortcuts.ts +5 -0
  130. package/src/components/layout/shell/shortcuts.ts +3 -0
  131. package/src/components/layout/status-bar.test.tsx +9 -8
  132. package/src/components/layout/status-bar.tsx +85 -54
  133. package/src/components/onboarding/onboarding-frame.tsx +2 -26
  134. package/src/components/price-sparkline/index.ts +61 -34
  135. package/src/components/price-sparkline/view.tsx +1 -1
  136. package/src/components/sign-in-gate.test.tsx +128 -0
  137. package/src/components/sign-in-gate.tsx +149 -0
  138. package/src/components/speedometer-gauge/desktop.tsx +2 -12
  139. package/src/components/ticker/badge/list.tsx +17 -3
  140. package/src/components/ticker/list-table-view.tsx +6 -3
  141. package/src/components/ui/data-table/index.tsx +3 -2
  142. package/src/components/ui/data-table/opentui/index.tsx +165 -120
  143. package/src/components/ui/frame.tsx +28 -0
  144. package/src/components/ui/index.ts +3 -0
  145. package/src/components/ui/native-select.tsx +20 -10
  146. package/src/components/ui/select-button.tsx +138 -0
  147. package/src/components/ui/status.tsx +2 -2
  148. package/src/components/ui/table-layout.ts +1 -1
  149. package/src/components/use-double-click-activation.ts +7 -3
  150. package/src/core/app-services.ts +3 -3
  151. package/src/data/config/layout.ts +12 -6
  152. package/src/data/config/store/index.test.ts +97 -0
  153. package/src/data/config/store/migrations.ts +41 -0
  154. package/src/data/config/store/normalize.ts +5 -3
  155. package/src/i18n/es.ts +6 -2
  156. package/src/i18n/ja.ts +6 -2
  157. package/src/i18n/ko.ts +6 -2
  158. package/src/i18n/zh-cn.ts +6 -2
  159. package/src/i18n/zh-tw.ts +6 -2
  160. package/src/layout-marketplace/pane-imagery.ts +1 -0
  161. package/src/layout-marketplace/payload.test.ts +17 -0
  162. package/src/layout-marketplace/payload.ts +40 -5
  163. package/src/market-data/coordinator/index.ts +3 -2
  164. package/src/market-data/coordinator/quotes.ts +173 -66
  165. package/src/market-data/coordinator/subscriptions.test.ts +215 -37
  166. package/src/pane-settings.ts +1 -1
  167. package/src/plugins/builtin/analytics/pane-model.ts +1 -1
  168. package/src/plugins/builtin/analytics/view.tsx +1 -1
  169. package/src/plugins/builtin/broker-manager/index.test.tsx +3 -2
  170. package/src/plugins/builtin/broker-manager/model.test.ts +2 -2
  171. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +16 -2
  172. package/src/plugins/builtin/chart-composer/pane.tsx +22 -8
  173. package/src/plugins/builtin/chat/content/composer.tsx +8 -4
  174. package/src/plugins/builtin/cloud/auth-dialog.tsx +14 -228
  175. package/src/plugins/builtin/cloud/auth-form.tsx +277 -0
  176. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  177. package/src/plugins/builtin/composite-plugins.ts +14 -1
  178. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  179. package/src/plugins/builtin/correlation/relationship/pane.tsx +2 -6
  180. package/src/plugins/builtin/correlation/relationship/view-model.ts +2 -2
  181. package/src/plugins/builtin/credit-conditions/index.test.tsx +12 -6
  182. package/src/plugins/builtin/credit-conditions/index.tsx +2 -21
  183. package/src/plugins/builtin/dividend-yield/index.tsx +1 -0
  184. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  185. package/src/plugins/builtin/earnings-calls/data.ts +223 -0
  186. package/src/plugins/builtin/earnings-calls/format.ts +49 -0
  187. package/src/plugins/builtin/earnings-calls/index.tsx +69 -0
  188. package/src/plugins/builtin/earnings-calls/pane.tsx +756 -0
  189. package/src/plugins/builtin/earnings-calls/prose.test.ts +36 -0
  190. package/src/plugins/builtin/earnings-calls/prose.ts +59 -0
  191. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +334 -0
  192. package/src/plugins/builtin/econ/detail-view.tsx +1 -2
  193. package/src/plugins/builtin/econ/fred-series-map.ts +16 -1
  194. package/src/plugins/builtin/econ/index.tsx +2 -1
  195. package/src/plugins/builtin/econ-statistics/cache.ts +4 -0
  196. package/src/plugins/builtin/econ-statistics/client.ts +92 -0
  197. package/src/plugins/builtin/econ-statistics/defs.ts +66 -0
  198. package/src/plugins/builtin/econ-statistics/detail.tsx +154 -0
  199. package/src/plugins/builtin/econ-statistics/headless.test.ts +54 -0
  200. package/src/plugins/builtin/econ-statistics/headless.ts +171 -0
  201. package/src/plugins/builtin/econ-statistics/index.tsx +54 -0
  202. package/src/plugins/builtin/econ-statistics/pane.tsx +356 -0
  203. package/src/plugins/builtin/econ-statistics/stats.ts +522 -0
  204. package/src/plugins/builtin/econ-statistics/transform.test.ts +86 -0
  205. package/src/plugins/builtin/econ-statistics/transform.ts +85 -0
  206. package/src/plugins/builtin/econ-statistics/trend.ts +57 -0
  207. package/src/plugins/builtin/econ-statistics/view.ts +90 -0
  208. package/src/plugins/builtin/fear-greed/charts.tsx +7 -18
  209. package/src/plugins/builtin/fear-greed/data.ts +6 -1
  210. package/src/plugins/builtin/help/index.tsx +4 -0
  211. package/src/plugins/builtin/holders/index.tsx +1 -0
  212. package/src/plugins/builtin/insider/index.tsx +1 -0
  213. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  214. package/src/plugins/builtin/kelly-sizer/sections.tsx +2 -3
  215. package/src/plugins/builtin/market-valuation/align.ts +122 -0
  216. package/src/plugins/builtin/market-valuation/cache.ts +32 -0
  217. package/src/plugins/builtin/market-valuation/chart-projection.ts +217 -0
  218. package/src/plugins/builtin/market-valuation/chart-series.ts +89 -0
  219. package/src/plugins/builtin/market-valuation/client.test.ts +141 -0
  220. package/src/plugins/builtin/market-valuation/client.ts +116 -0
  221. package/src/plugins/builtin/market-valuation/defs.ts +251 -0
  222. package/src/plugins/builtin/market-valuation/detail.tsx +159 -0
  223. package/src/plugins/builtin/market-valuation/headless.test.ts +58 -0
  224. package/src/plugins/builtin/market-valuation/headless.ts +170 -0
  225. package/src/plugins/builtin/market-valuation/index.tsx +64 -0
  226. package/src/plugins/builtin/market-valuation/indicators.ts +422 -0
  227. package/src/plugins/builtin/market-valuation/model.test.ts +350 -0
  228. package/src/plugins/builtin/market-valuation/model.ts +8 -0
  229. package/src/plugins/builtin/market-valuation/pane.test.tsx +247 -0
  230. package/src/plugins/builtin/market-valuation/pane.tsx +344 -0
  231. package/src/plugins/builtin/market-valuation/series.ts +12 -0
  232. package/src/plugins/builtin/market-valuation/settings.ts +38 -0
  233. package/src/plugins/builtin/market-valuation/sources.ts +130 -0
  234. package/src/plugins/builtin/market-valuation/trend.ts +89 -0
  235. package/src/plugins/builtin/market-valuation/view.ts +137 -0
  236. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +123 -0
  237. package/src/plugins/builtin/market-valuation/zone-scale/index.tsx +12 -0
  238. package/src/plugins/builtin/market-valuation/zone-scale/model.ts +60 -0
  239. package/src/plugins/builtin/market-valuation/zone-scale/terminal.tsx +94 -0
  240. package/src/plugins/builtin/news/wire/breaking/filters.test.ts +99 -0
  241. package/src/plugins/builtin/news/wire/breaking/filters.ts +110 -0
  242. package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +157 -53
  243. package/src/plugins/builtin/news/wire/breaking/notifications.ts +91 -25
  244. package/src/plugins/builtin/news/wire/index.tsx +31 -44
  245. package/src/plugins/builtin/options/index.tsx +1 -0
  246. package/src/plugins/builtin/options/view.test.tsx +29 -1
  247. package/src/plugins/builtin/options/view.tsx +1 -22
  248. package/src/plugins/builtin/plugin-marketplace/feed.ts +80 -0
  249. package/src/plugins/builtin/plugin-marketplace/index.tsx +31 -0
  250. package/src/plugins/builtin/plugin-marketplace/model.test.ts +200 -0
  251. package/src/plugins/builtin/plugin-marketplace/model.ts +204 -0
  252. package/src/plugins/builtin/plugin-marketplace/pane.tsx +385 -0
  253. package/src/plugins/builtin/plugin-marketplace/store.ts +46 -0
  254. package/src/plugins/builtin/plugin-module.ts +9 -1
  255. package/src/plugins/builtin/research/index.tsx +4 -0
  256. package/src/plugins/builtin/research-search/command-bar-search.test.ts +81 -0
  257. package/src/plugins/builtin/research-search/command-bar-search.ts +175 -0
  258. package/src/plugins/builtin/research-search/data.ts +109 -0
  259. package/src/plugins/builtin/research-search/document-view.test.tsx +75 -0
  260. package/src/plugins/builtin/research-search/document-view.tsx +254 -0
  261. package/src/plugins/builtin/research-search/filter-bar.tsx +62 -0
  262. package/src/plugins/builtin/research-search/focus-handoff.ts +47 -0
  263. package/src/plugins/builtin/research-search/index.tsx +85 -0
  264. package/src/plugins/builtin/research-search/model.test.ts +40 -0
  265. package/src/plugins/builtin/research-search/model.ts +291 -0
  266. package/src/plugins/builtin/research-search/pane.test.tsx +241 -0
  267. package/src/plugins/builtin/research-search/pane.tsx +678 -0
  268. package/src/plugins/builtin/research-search/saved-view.tsx +171 -0
  269. package/src/plugins/builtin/research-search/snippet-text.tsx +35 -0
  270. package/src/plugins/builtin/research-search/snippet.test.ts +78 -0
  271. package/src/plugins/builtin/research-search/snippet.ts +134 -0
  272. package/src/plugins/builtin/scanner/flow-model.test.ts +11 -1
  273. package/src/plugins/builtin/scanner/flow-model.ts +29 -1
  274. package/src/plugins/builtin/scanner/flow-pane.tsx +78 -22
  275. package/src/plugins/builtin/sec/index.tsx +1 -0
  276. package/src/plugins/builtin/shared/series-cache.ts +93 -0
  277. package/src/plugins/builtin/short-interest/index.tsx +1 -0
  278. package/src/plugins/builtin/short-interest/pane.tsx +1 -1
  279. package/src/plugins/builtin/thirteenf/index.tsx +1 -0
  280. package/src/plugins/builtin/ticker-detail/index.tsx +0 -14
  281. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
  282. package/src/plugins/builtin/volatility/index.tsx +1 -1
  283. package/src/plugins/builtin/world-venue-map/map.desktop.test.tsx +106 -0
  284. package/src/plugins/builtin/world-venue-map/map.tsx +213 -69
  285. package/src/plugins/builtin/world-venue-map/model.test.ts +32 -0
  286. package/src/plugins/builtin/world-venue-map/model.ts +134 -13
  287. package/src/plugins/builtin/yield-curve/index.tsx +1 -1
  288. package/src/plugins/bundle.test.ts +109 -0
  289. package/src/plugins/bundle.ts +139 -0
  290. package/src/plugins/catalog-browser.test.ts +1 -0
  291. package/src/plugins/catalog-browser.ts +10 -0
  292. package/src/plugins/catalog-manifest.test.ts +43 -0
  293. package/src/plugins/catalog-manifest.ts +92 -0
  294. package/src/plugins/catalog-ui.ts +20 -4
  295. package/src/plugins/catalog.ts +9 -2
  296. package/src/plugins/current-target.ts +28 -0
  297. package/src/plugins/host-contract.ts +37 -0
  298. package/src/plugins/host-link.ts +158 -0
  299. package/src/plugins/host-modules.ts +61 -0
  300. package/src/plugins/loader.ts +41 -22
  301. package/src/plugins/ownership.ts +1 -0
  302. package/src/plugins/prediction-markets/index.tsx +1 -0
  303. package/src/plugins/registry/context.ts +3 -0
  304. package/src/plugins/registry/contributions.ts +27 -0
  305. package/src/plugins/registry/index.test.ts +25 -0
  306. package/src/plugins/registry/index.ts +8 -0
  307. package/src/plugins/registry/pane-settings.ts +6 -13
  308. package/src/plugins/seed.test.ts +83 -0
  309. package/src/plugins/seed.ts +80 -0
  310. package/src/public/broker.ts +31 -0
  311. package/src/public/market-data.ts +9 -0
  312. package/src/public/pane-hooks.ts +57 -0
  313. package/src/public/public-api.test.ts +285 -0
  314. package/src/public/react.ts +62 -0
  315. package/src/public/test-support.ts +23 -0
  316. package/src/public/time-series.ts +8 -0
  317. package/src/public/utils.ts +77 -0
  318. package/src/remote/layout-helpers.ts +6 -8
  319. package/src/remote/resources.ts +2 -0
  320. package/src/remote/schema.ts +1 -0
  321. package/src/remote/semantic-tree.test.tsx +105 -0
  322. package/src/remote/semantic-tree.tsx +53 -35
  323. package/src/renderers/browser/main.tsx +6 -0
  324. package/src/renderers/cloudflare/worker.test.ts +19 -0
  325. package/src/renderers/cloudflare/worker.ts +1 -1
  326. package/src/renderers/electrobun/bun/external-plugins.ts +140 -0
  327. package/src/renderers/electrobun/bun/index.ts +5 -2
  328. package/src/renderers/electrobun/shared/protocol.ts +21 -0
  329. package/src/renderers/electrobun/view/build-assets.ts +0 -1
  330. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +51 -1
  331. package/src/renderers/electrobun/view/external-plugins.test.ts +45 -0
  332. package/src/renderers/electrobun/view/external-plugins.ts +63 -0
  333. package/src/renderers/electrobun/view/host/ascii-text.test.ts +48 -0
  334. package/src/renderers/electrobun/view/host/ascii-text.ts +11 -1
  335. package/src/renderers/electrobun/view/host/chart-surface.tsx +38 -32
  336. package/src/renderers/electrobun/view/host/mouse.ts +16 -2
  337. package/src/renderers/electrobun/view/main.tsx +27 -2
  338. package/src/renderers/electrobun/view/styles.css +5 -0
  339. package/src/renderers/electrobun/view/toast-host.tsx +18 -0
  340. package/src/renderers/opentui/chart-surface.tsx +5 -1
  341. package/src/renderers/opentui/host.tsx +16 -2
  342. package/src/renderers/opentui/interaction-performance.test.ts +79 -0
  343. package/src/renderers/opentui/interaction-performance.ts +188 -0
  344. package/src/renderers/opentui/start.tsx +26 -1
  345. package/src/renderers/opentui/test-utils.tsx +13 -0
  346. package/src/renderers/opentui/toast-host.tsx +26 -2
  347. package/src/renderers/opentui/ui-host.tsx +1 -0
  348. package/src/sources/provider-router/search.test.ts +191 -0
  349. package/src/sources/provider-router/search.ts +92 -39
  350. package/src/state/app/context/index.tsx +2 -0
  351. package/src/state/app/context/selector.test.tsx +33 -1
  352. package/src/state/hooks/quote-streaming.test.tsx +93 -0
  353. package/src/state/hooks/quote-streaming.ts +106 -30
  354. package/src/state/pane-table-export-registry.ts +19 -0
  355. package/src/theme/color-utils.ts +1 -1
  356. package/src/theme/colors.ts +50 -0
  357. package/src/theme/themes.test.ts +15 -2
  358. package/src/theme/themes.ts +19 -1
  359. package/src/tickers/search/index.test.ts +26 -0
  360. package/src/tickers/search/index.ts +42 -17
  361. package/src/tickers/search/ranking.ts +14 -0
  362. package/src/time-series/chart-data.test.ts +216 -0
  363. package/src/time-series/resolution.test.ts +25 -26
  364. package/src/time-series/resolution.ts +41 -10
  365. package/src/time-series/resolve.ts +7 -0
  366. package/src/time-series/types.ts +3 -1
  367. package/src/time-series/use-chart-resolution.ts +5 -0
  368. package/src/types/config.ts +123 -3
  369. package/src/types/data-provider.ts +11 -0
  370. package/src/types/headless.ts +161 -0
  371. package/src/types/plugin.ts +102 -0
  372. package/src/ui/host.tsx +12 -1
  373. package/src/ui/index.tsx +1 -0
  374. package/src/ui/toast.tsx +8 -4
  375. package/src/utils/format.ts +35 -0
  376. package/src/utils/fuzzy-search.test.ts +94 -0
  377. package/src/utils/fuzzy-search.ts +81 -22
  378. package/src/utils/shortcut-labels.ts +14 -0
  379. package/src/components/chart/core/axis-format.ts +0 -157
  380. package/src/components/chart/core/index.test.ts +0 -1075
  381. package/src/components/chart/core/projection.ts +0 -357
  382. package/src/components/chart/core/render-utils.ts +0 -4
  383. package/src/components/chart/core/renderer.ts +0 -244
  384. package/src/components/chart/core/scene.ts +0 -180
  385. package/src/components/chart/core/time-axis.ts +0 -330
  386. package/src/components/chart/cursor-motion.ts +0 -47
  387. package/src/components/chart/indicators/types.ts +0 -26
  388. package/src/components/chart/market-session.ts +0 -176
  389. package/src/components/chart/multi-line/renderer.test.ts +0 -83
  390. package/src/components/chart/multi-line/renderer.ts +0 -161
  391. package/src/components/chart/multi-line/scene.ts +0 -188
  392. package/src/components/chart/native/raster/layout.ts +0 -129
  393. package/src/components/chart/native/raster/price-chart.ts +0 -244
  394. package/src/components/chart/rendering/chart-draw.ts +0 -270
  395. package/src/components/chart/rendering/geometry.ts +0 -95
  396. package/src/components/chart/rendering/pixel-buffer.ts +0 -267
  397. package/src/components/chart/scatter-chart-renderer.test.ts +0 -31
  398. package/src/components/chart/scatter-chart-renderer.ts +0 -223
  399. package/src/components/chart/static/chart/surface.tsx +0 -358
  400. package/src/components/command-bar/routes/root/article-results.test.ts +0 -83
  401. package/src/components/command-bar/routes/root/article-results.ts +0 -55
  402. package/src/plugins/builtin/news/wire/article-search.test.ts +0 -72
  403. package/src/plugins/builtin/news/wire/article-search.ts +0 -140
  404. package/src/plugins/builtin/substack/api/auth.ts +0 -185
  405. package/src/plugins/builtin/substack/api/cache.ts +0 -82
  406. package/src/plugins/builtin/substack/api/loaders.ts +0 -170
  407. package/src/plugins/builtin/substack/api/store.ts +0 -186
  408. package/src/plugins/builtin/substack/api/types.ts +0 -42
  409. package/src/plugins/builtin/substack/api.test.ts +0 -298
  410. package/src/plugins/builtin/substack/article-detail.tsx +0 -397
  411. package/src/plugins/builtin/substack/article-stack.tsx +0 -133
  412. package/src/plugins/builtin/substack/content.ts +0 -478
  413. package/src/plugins/builtin/substack/feed-tabs.tsx +0 -44
  414. package/src/plugins/builtin/substack/index.tsx +0 -49
  415. package/src/plugins/builtin/substack/login-view.tsx +0 -126
  416. package/src/plugins/builtin/substack/model.test.ts +0 -155
  417. package/src/plugins/builtin/substack/normalize.ts +0 -343
  418. package/src/plugins/builtin/substack/pane-footer.ts +0 -52
  419. package/src/plugins/builtin/substack/pane-state.ts +0 -170
  420. package/src/plugins/builtin/substack/pane.tsx +0 -547
  421. package/src/plugins/builtin/substack/read-state.ts +0 -30
  422. package/src/plugins/builtin/substack/table.ts +0 -111
  423. package/src/plugins/builtin/substack/types.ts +0 -71
  424. package/src/plugins/builtin/substack/utils.ts +0 -131
  425. package/src/plugins/builtin/ticker-detail/data-panes/provider-search.tsx +0 -229
  426. package/src/plugins/ibkr/account-cache.test.ts +0 -102
  427. package/src/plugins/ibkr/account-cache.ts +0 -24
  428. package/src/plugins/ibkr/broker-adapter.ts +0 -286
  429. package/src/plugins/ibkr/config.test.ts +0 -79
  430. package/src/plugins/ibkr/config.ts +0 -250
  431. package/src/plugins/ibkr/dialogs.tsx +0 -40
  432. package/src/plugins/ibkr/flex/index.test.ts +0 -264
  433. package/src/plugins/ibkr/flex/index.ts +0 -367
  434. package/src/plugins/ibkr/flex/statement-client.ts +0 -235
  435. package/src/plugins/ibkr/fundamental-parser.ts +0 -149
  436. package/src/plugins/ibkr/gateway/account-loaders.ts +0 -266
  437. package/src/plugins/ibkr/gateway/account-summary.ts +0 -164
  438. package/src/plugins/ibkr/gateway/client-lock.ts +0 -154
  439. package/src/plugins/ibkr/gateway/connection-events.ts +0 -114
  440. package/src/plugins/ibkr/gateway/connection.ts +0 -152
  441. package/src/plugins/ibkr/gateway/contracts.ts +0 -132
  442. package/src/plugins/ibkr/gateway/fundamentals.ts +0 -83
  443. package/src/plugins/ibkr/gateway/helpers.ts +0 -33
  444. package/src/plugins/ibkr/gateway/history-requests.ts +0 -158
  445. package/src/plugins/ibkr/gateway/history.ts +0 -120
  446. package/src/plugins/ibkr/gateway/instrument-lookup.ts +0 -117
  447. package/src/plugins/ibkr/gateway/market-data.ts +0 -345
  448. package/src/plugins/ibkr/gateway/order-execution.ts +0 -218
  449. package/src/plugins/ibkr/gateway/orders.ts +0 -63
  450. package/src/plugins/ibkr/gateway/price-normalization.test.ts +0 -44
  451. package/src/plugins/ibkr/gateway/price-normalization.ts +0 -50
  452. package/src/plugins/ibkr/gateway/quote-stream-controller.ts +0 -203
  453. package/src/plugins/ibkr/gateway/quote-stream.ts +0 -171
  454. package/src/plugins/ibkr/gateway/selection.ts +0 -51
  455. package/src/plugins/ibkr/gateway/service/index.test.ts +0 -675
  456. package/src/plugins/ibkr/gateway/service/index.ts +0 -429
  457. package/src/plugins/ibkr/gateway/service/lifecycle.ts +0 -191
  458. package/src/plugins/ibkr/gateway/service/manager.ts +0 -52
  459. package/src/plugins/ibkr/gateway/service/market-data.ts +0 -48
  460. package/src/plugins/ibkr/gateway/service/native-loader.ts +0 -12
  461. package/src/plugins/ibkr/gateway/service/native.ts +0 -543
  462. package/src/plugins/ibkr/gateway/timeouts.ts +0 -13
  463. package/src/plugins/ibkr/gateway/types.ts +0 -27
  464. package/src/plugins/ibkr/index.test.tsx +0 -162
  465. package/src/plugins/ibkr/index.tsx +0 -159
  466. package/src/plugins/ibkr/instance-selection.test.ts +0 -120
  467. package/src/plugins/ibkr/instance-selection.ts +0 -36
  468. package/src/plugins/ibkr/portfolio-performance.test.ts +0 -32
  469. package/src/plugins/ibkr/portfolio-performance.ts +0 -129
  470. package/src/plugins/ibkr/trade/badge.tsx +0 -28
  471. package/src/plugins/ibkr/trade/dialogs.tsx +0 -44
  472. package/src/plugins/ibkr/trade/preview-panel.tsx +0 -130
  473. package/src/plugins/ibkr/trade/tab/actions.tsx +0 -443
  474. package/src/plugins/ibkr/trade/tab/field-editors.tsx +0 -79
  475. package/src/plugins/ibkr/trade/tab/footer.ts +0 -53
  476. package/src/plugins/ibkr/trade/tab/header.tsx +0 -167
  477. package/src/plugins/ibkr/trade/tab/index.test.tsx +0 -178
  478. package/src/plugins/ibkr/trade/tab/index.tsx +0 -298
  479. package/src/plugins/ibkr/trade/tab/model.ts +0 -159
  480. package/src/plugins/ibkr/trade/tab/order-request.ts +0 -64
  481. package/src/plugins/ibkr/trade/tab/shortcuts.ts +0 -89
  482. package/src/plugins/ibkr/trade/tab/state-sync.ts +0 -86
  483. package/src/plugins/ibkr/trade/ticket-panel.tsx +0 -301
  484. package/src/plugins/ibkr/trade/utils.test.ts +0 -175
  485. package/src/plugins/ibkr/trade/utils.ts +0 -144
  486. package/src/plugins/ibkr/trading/pane-view.tsx +0 -237
  487. package/src/plugins/ibkr/trading/pane.tsx +0 -332
  488. package/src/plugins/ibkr/trading/state.test.ts +0 -109
  489. package/src/plugins/ibkr/trading/state.ts +0 -335
package/README.md CHANGED
@@ -35,7 +35,7 @@ The desktop app and TUI share the full command language and plugin system. The b
35
35
 
36
36
  ## Browser App
37
37
 
38
- [term.gloom.sh](https://term.gloom.sh) works anonymously with configuration, tickers, layouts, session state, and plugin state stored in the browser. Anonymous sessions receive rate-limited, 15-minute-delayed Gloom Cloud market data and read-only chat. Login remains optional and enables sync and chat posting; Pro accounts receive realtime market data. Cloud REST and WebSocket traffic uses the same-origin `/api` path, which the Worker forwards only to `https://api.gloom.sh`; it is not an arbitrary network proxy.
38
+ [term.gloom.sh](https://term.gloom.sh) requires a free Gloom Cloud account. The workspace loads behind a sign-in panel that cannot be dismissed, and a session opens it; configuration, tickers, layouts, session state, and plugin state are stored in the browser. Free accounts receive rate-limited, 15-minute-delayed Gloom Cloud market data and read-only chat until the email is verified; Pro accounts receive realtime market data. Public share pages stay open to everyone with no account. Cloud REST and WebSocket traffic uses the same-origin `/api` path, which the Worker forwards only to `https://api.gloom.sh`; it is not an arbitrary network proxy.
39
39
 
40
40
  The browser build intentionally omits brokers and native integrations, filesystem notes, local AI, external plugins, updater/debug tools, application menus, native window controls, pop-out native windows, and native context menus. Modules that still depend on desktop-only or CORS-blocked feeds are also absent for now: RSS/Substack, prediction markets and polls, market halts/heatmap/movers, dividend/ownership/SEC panes, earnings/IPO, and TV. Public shares open under `/s/:id` in a separate slim bundle. Share creation and owner deletion use the signed-in Gloom Cloud session through the same fixed API path; public reads require no account.
41
41
 
@@ -202,6 +202,7 @@ See [PLUGINS.md](PLUGINS.md) for the plugin API and the shared UI surface availa
202
202
  | `Ctrl+W` | Close focused pane |
203
203
  | `Ctrl+Shift+M` | Move focused window (`WIN resize` starts resize mode) |
204
204
  | `Ctrl+Shift+D` | Dock or float focused pane |
205
+ | `Ctrl+Shift+E` | Export focused pane table as CSV |
205
206
  | `Ctrl+Shift+L` | Layout actions |
206
207
  | `Ctrl+Shift+G` | Tidy windows |
207
208
  | `Tab` | Switch panes |
@@ -232,7 +233,7 @@ Use `HELP` inside Gloomberb for the live shortcut list. The common command-bar p
232
233
  | `GR <tickers>` | Security relationship graph |
233
234
  | `EE <ticker>` | Events view with earnings and revenue estimates |
234
235
  | `EM [tickers]` | Earnings monitor |
235
- | `SRCH <query>` | Provider symbol search |
236
+ | `SRCH [query]` | Full-text search across earnings call transcripts, news, and SEC filings |
236
237
  | `QQ <tickers>` | Ticker quote monitor |
237
238
  | `CMP <tickers>` | Normalized price comparison |
238
239
  | `CORR <tickers>` | Ticker return correlations |
@@ -282,11 +283,13 @@ The toolbar controls preset or exact date ranges, intervals from one minute thro
282
283
  | `WEI` | Global equity indices |
283
284
  | `MAP` | Live world venue map with local market status and clocks |
284
285
  | `FUT` | Front-month futures across index, rates, energy, metals, grains, and FX |
285
- | `ECON` | Economic events and releases |
286
+ | `ECO` | Economic events and releases |
287
+ | `ECST [statistic]` | Economic statistics: inflation, labour, growth, consumer, housing, rates |
286
288
  | `GC` | Yield curve |
287
289
  | `AUCT` | Treasury auction results: high rate, bid-to-cover, indirect share, and size |
288
290
  | `VIX` | VIX 30-day/3-month implied-volatility curve |
289
291
  | `CRD` | Credit spreads |
292
+ | `VAL [indicator]` | Whole-market valuation: Buffett, CAPE, excess CAPE yield, Tobin Q, investor equity allocation, dividend yield, margin debt, cap/profits, cap/M2 |
290
293
  | `CDS [ticker]` | Single-name corporate CDS activity: most-active issuers, or one issuer's trades |
291
294
  | `ERN` | Earnings calendar |
292
295
  | `IPO` | Upcoming and recent IPOs |
@@ -327,7 +330,6 @@ The toolbar controls preset or exact date ranges, intervals from one minute thro
327
330
  | `FONT+` / `FONT-` | Increase or decrease desktop font size |
328
331
  | `CONN` | Connection health |
329
332
  | `POLL` | Prediction-market polls |
330
- | `ART` | Loaded article lookup |
331
333
  | `UPGRADE` | Account upgrade |
332
334
  | `CR` | Cycle chart renderer |
333
335
  | `LANG <locale>` | Change interface language (`auto`, `en`, `es`, `zh-CN`, `zh-TW`, `ja`, or `ko`) |
package/README.zh-CN.md CHANGED
@@ -172,6 +172,7 @@ gloomberb
172
172
  | `Ctrl+W` | 关闭聚焦面板 |
173
173
  | `Ctrl+Shift+M` | 移动聚焦窗口(`WIN resize` 进入缩放模式) |
174
174
  | `Ctrl+Shift+D` | 停靠或浮动聚焦面板 |
175
+ | `Ctrl+Shift+E` | 将聚焦面板的表格导出为 CSV |
175
176
  | `Ctrl+Shift+L` | 布局操作 |
176
177
  | `Ctrl+Shift+G` | 所有窗口网格对齐 |
177
178
  | `Tab` | 切换面板 |
@@ -200,7 +201,7 @@ gloomberb
200
201
  | `GR <tickers>` | 证券关系图 |
201
202
  | `EE <ticker>` | 含 EPS 与营收预估的事件视图 |
202
203
  | `EM [tickers]` | 财报监控 |
203
- | `SRCH <query>` | 数据源符号搜索 |
204
+ | `SRCH [query]` | 跨财报电话会记录、新闻与 SEC 文件的全文检索 |
204
205
  | `QQ <tickers>` | 股票行情监控 |
205
206
  | `CMP <tickers>` | 股票图表对比 |
206
207
  | `CORR <tickers>` | 股票收益率相关性 |
@@ -230,7 +231,8 @@ gloomberb
230
231
  | `TBO` | TheBuildout 基础设施情报 |
231
232
  | `CG` | 国会交易披露 |
232
233
  | `WEI` | 全球股指 |
233
- | `ECON` | 经济事件与数据发布 |
234
+ | `ECO` | 经济事件与数据发布 |
235
+ | `ECST [统计指标]` | 经济统计:通胀、就业、增长、消费、住房、利率 |
234
236
  | `GC` | 收益率曲线 |
235
237
  | `ERN` | 财报日历 |
236
238
  | `BI` / `SP` | 标普 500 板块表现 |
package/package.json CHANGED
@@ -1,10 +1,34 @@
1
1
  {
2
2
  "name": "gloomberb",
3
- "version": "0.11.3",
3
+ "version": "0.12.0",
4
4
  "packageManager": "bun@1.3.11",
5
5
  "description": "Market research and portfolio tracker for the terminal",
6
6
  "module": "src/index.tsx",
7
7
  "type": "module",
8
+ "exports": {
9
+ ".": "./src/index.tsx",
10
+ "./types/plugin": "./src/types/plugin.ts",
11
+ "./types/persistence": "./src/types/persistence.ts",
12
+ "./ui": "./src/ui/index.tsx",
13
+ "./components": "./src/components/index.ts",
14
+ "./theme": "./src/theme/colors.ts",
15
+ "./capabilities": "./src/capabilities/index.ts",
16
+ "./utils": "./src/public/utils.ts",
17
+ "./react": "./src/public/react.ts",
18
+ "./test-support": "./src/public/test-support.ts",
19
+ "./package.json": "./package.json",
20
+ "./types/broker": "./src/types/broker.ts",
21
+ "./types/config": "./src/types/config.ts",
22
+ "./types/data-provider": "./src/types/data-provider.ts",
23
+ "./types/financials": "./src/types/financials.ts",
24
+ "./types/instrument": "./src/types/instrument.ts",
25
+ "./types/ticker": "./src/types/ticker.ts",
26
+ "./types/trading": "./src/types/trading.ts",
27
+ "./time-series": "./src/public/time-series.ts",
28
+ "./market-data": "./src/public/market-data.ts",
29
+ "./dialog": "./src/ui/dialog.tsx",
30
+ "./broker": "./src/public/broker.ts"
31
+ },
8
32
  "bin": {
9
33
  "gloomberb": "bin/gloomberb"
10
34
  },
@@ -28,6 +52,8 @@
28
52
  "desktop:release": "electrobun build --env=stable",
29
53
  "desktop:release:windows": "electrobun build --env=stable --targets=win-x64",
30
54
  "i18n:audit": "bun run scripts/i18n-audit.ts",
55
+ "plugins:manifest": "bun run scripts/generate-plugin-manifest.ts",
56
+ "plugins:manifest:check": "bun run scripts/generate-plugin-manifest.ts --check",
31
57
  "typecheck": "bun run typecheck:opentui && bun run typecheck:electrobun-bun && bun run typecheck:electrobun-view && bun run typecheck:browser && bun run typecheck:cloudflare && bun run typecheck:scripts",
32
58
  "typecheck:opentui": "tsc --project tsconfig.opentui.json",
33
59
  "typecheck:electrobun-bun": "tsc --project tsconfig.electrobun-bun.json",
@@ -36,6 +62,7 @@
36
62
  "typecheck:cloudflare": "tsc --project tsconfig.cloudflare.json",
37
63
  "typecheck:scripts": "tsc --project tsconfig.scripts.json",
38
64
  "test": "bun test",
65
+ "benchmark:tui:navigation": "bun run scripts/benchmark-tui-navigation.ts",
39
66
  "test:global-upgrade": "bun run scripts/smoke-global-upgrade.ts",
40
67
  "release": "./scripts/release.sh"
41
68
  },
@@ -110,14 +110,24 @@ export class CloudAuthApi {
110
110
  }
111
111
 
112
112
  async getSession(): Promise<AuthUser | null> {
113
+ // The answer describes whichever credential was on the wire when the
114
+ // request left. On boot a check can go out before the persisted token is
115
+ // installed; by the time "no session" comes back, the token and the cached
116
+ // user are in place, and applying that answer wiped them. Re-check with the
117
+ // credential that exists now instead of trusting an answer about one that
118
+ // no longer does.
119
+ const credential = this.options.getSessionToken();
120
+ const credentialChanged = () => this.options.getSessionToken() !== credential;
113
121
  try {
114
122
  const result = await this.options.request<{ user: AuthUser }>("/auth/get-session", {
115
123
  method: "GET",
116
124
  });
125
+ if (credentialChanged()) return this.getSession();
117
126
  const user = result?.user ?? null;
118
127
  this.options.setCurrentUser(user);
119
128
  return user;
120
129
  } catch (error) {
130
+ if (credentialChanged()) return this.getSession();
121
131
  if (error instanceof ApiRequestError && isHardSessionInvalidMessage(error.message)) {
122
132
  this.options.setSessionToken(null);
123
133
  return null;
@@ -1,28 +1,43 @@
1
1
  import type { TickerFinancials } from "../types/financials";
2
2
  import type { InstrumentSearchResult } from "../types/instrument";
3
- import { normalizeTweetSearchResponse } from "./normalizers";
3
+ import {
4
+ normalizeSavedSearchHits,
5
+ normalizeSavedSearchResponse,
6
+ normalizeSearchResponse,
7
+ normalizeTweetSearchResponse,
8
+ } from "./normalizers";
4
9
  import {
5
10
  cloudCdsPath,
6
11
  cloudCongressHousePath,
12
+ cloudEarningsCallsPath,
13
+ cloudEarningsTranscriptPath,
7
14
  cloudExchangeRatePath,
8
15
  cloudSec13FPath,
9
16
  cloudSecFilingContentPath,
10
17
  cloudSecFilingDocumentsPath,
11
18
  cloudSecFilingsPath,
12
19
  cloudFredSeriesPath,
20
+ cloudShillerPath,
13
21
  cloudHistoryPath,
14
22
  cloudMarketSearchPath,
15
23
  cloudMarketSymbolPath,
16
24
  cloudNewsPath,
17
25
  cloudOptionsChainPath,
26
+ cloudSavedSearchHitsPath,
27
+ cloudSavedSearchPath,
28
+ cloudSavedSearchesPath,
29
+ cloudSearchDocumentPath,
30
+ cloudSearchPath,
18
31
  cloudStatementsPath,
19
32
  cloudTickerTweetsPath,
20
33
  cloudTweetSearchPath,
21
34
  type CloudCdsParams,
22
35
  type CloudCongressHouseParams,
36
+ type CloudEarningsCallsParams,
23
37
  type CloudFredSeriesParams,
24
38
  type CloudHistoryParams,
25
39
  type CloudNewsParams,
40
+ type CloudSearchParams,
26
41
  type CloudSecFilingParams,
27
42
  type CloudSecFilingsParams,
28
43
  type CloudTickerTweetsParams,
@@ -34,11 +49,14 @@ import type {
34
49
  CloudCdsResponse,
35
50
  CloudCompanyProfile,
36
51
  CloudCongressHousePayload,
52
+ CloudEarningsCallListPayload,
53
+ CloudEarningsTranscriptPayload,
37
54
  CloudCorporateActionsPayload,
38
55
  CloudEconEventPayload,
39
56
  CloudEquityDiagnosticMode,
40
57
  CloudEquityDiagnosticResult,
41
58
  CloudFredSeriesPayload,
59
+ CloudShillerPayload,
42
60
  CloudFundamentals,
43
61
  CloudHoldersPayload,
44
62
  CloudMarketBatchPayload,
@@ -48,6 +66,14 @@ import type {
48
66
  CloudMarketScreenerPayload,
49
67
  CloudNewsListResponse,
50
68
  CloudNewsPayload,
69
+ CloudSavedSearch,
70
+ CloudSavedSearchInput,
71
+ CloudSavedSearchListResponse,
72
+ CloudSearchDocType,
73
+ CloudSearchDocument,
74
+ CloudSearchDocumentResponse,
75
+ CloudSearchHit,
76
+ CloudSearchResponse,
51
77
  CloudSecContentResponse,
52
78
  CloudSecDocumentsResponse,
53
79
  CloudSecFilingsResponse,
@@ -213,6 +239,10 @@ export class CloudDataApi {
213
239
  return this.request<CloudFredSeriesPayload>(cloudFredSeriesPath(seriesId, params));
214
240
  }
215
241
 
242
+ async getCloudShiller(): Promise<CloudShillerPayload> {
243
+ return this.request<CloudShillerPayload>(cloudShillerPath());
244
+ }
245
+
216
246
  async getCloudYieldCurve(): Promise<CloudYieldPointPayload[]> {
217
247
  return this.request<CloudYieldPointPayload[]>("/cloud/econ/yield-curve");
218
248
  }
@@ -225,6 +255,16 @@ export class CloudDataApi {
225
255
  return this.request<CloudCongressHousePayload>(cloudCongressHousePath(params));
226
256
  }
227
257
 
258
+ async getCloudEarningsCalls(
259
+ params: CloudEarningsCallsParams = {},
260
+ ): Promise<CloudEarningsCallListPayload> {
261
+ return this.request<CloudEarningsCallListPayload>(cloudEarningsCallsPath(params));
262
+ }
263
+
264
+ async getCloudEarningsTranscript(id: string): Promise<CloudEarningsTranscriptPayload> {
265
+ return this.request<CloudEarningsTranscriptPayload>(cloudEarningsTranscriptPath(id));
266
+ }
267
+
228
268
  async getCloudSecFilings(params: CloudSecFilingsParams): Promise<CloudSecFilingsResponse> {
229
269
  return this.request<CloudSecFilingsResponse>(cloudSecFilingsPath(params));
230
270
  }
@@ -241,6 +281,68 @@ export class CloudDataApi {
241
281
  return this.request<unknown>(cloudSec13FPath(path, params));
242
282
  }
243
283
 
284
+ /**
285
+ * Cross-document full-text search. Pro-gated: unentitled accounts get a 402,
286
+ * which the caller turns into the access gate rather than an empty result.
287
+ */
288
+ async searchCloudDocuments(
289
+ params: CloudSearchParams,
290
+ options?: { signal?: AbortSignal },
291
+ ): Promise<CloudSearchResponse> {
292
+ return normalizeSearchResponse(
293
+ await this.request<CloudSearchResponse>(cloudSearchPath(params), { signal: options?.signal }),
294
+ );
295
+ }
296
+
297
+ async getCloudSearchDocument(
298
+ docType: CloudSearchDocType,
299
+ sourceId: string,
300
+ options?: { signal?: AbortSignal },
301
+ ): Promise<CloudSearchDocument> {
302
+ const response = await this.request<CloudSearchDocumentResponse>(
303
+ cloudSearchDocumentPath(docType, sourceId),
304
+ { signal: options?.signal },
305
+ );
306
+ return response.document;
307
+ }
308
+
309
+ async getCloudSavedSearches(options?: { signal?: AbortSignal }): Promise<CloudSavedSearch[]> {
310
+ const response = await this.request<CloudSavedSearchListResponse>(cloudSavedSearchesPath(), {
311
+ signal: options?.signal,
312
+ });
313
+ return response.searches ?? [];
314
+ }
315
+
316
+ async createCloudSavedSearch(input: CloudSavedSearchInput): Promise<CloudSavedSearch> {
317
+ return normalizeSavedSearchResponse(await this.request<unknown>(cloudSavedSearchesPath(), {
318
+ method: "POST",
319
+ body: JSON.stringify(input),
320
+ }));
321
+ }
322
+
323
+ async updateCloudSavedSearch(
324
+ id: string,
325
+ update: Partial<CloudSavedSearchInput>,
326
+ ): Promise<CloudSavedSearch> {
327
+ return normalizeSavedSearchResponse(await this.request<unknown>(cloudSavedSearchPath(id), {
328
+ method: "PATCH",
329
+ body: JSON.stringify(update),
330
+ }));
331
+ }
332
+
333
+ async deleteCloudSavedSearch(id: string): Promise<void> {
334
+ await this.request<void>(cloudSavedSearchPath(id), { method: "DELETE" });
335
+ }
336
+
337
+ async getCloudSavedSearchHits(
338
+ id: string,
339
+ options?: { signal?: AbortSignal },
340
+ ): Promise<CloudSearchHit[]> {
341
+ return normalizeSavedSearchHits(await this.request<unknown>(cloudSavedSearchHitsPath(id), {
342
+ signal: options?.signal,
343
+ }));
344
+ }
345
+
244
346
  async getCloudNews(params: CloudNewsParams = {}): Promise<CloudNewsListResponse> {
245
347
  return this.request<CloudNewsListResponse>(cloudNewsPath(params));
246
348
  }
@@ -198,6 +198,44 @@ describe("apiClient auth cookies", () => {
198
198
  expect(requests).toBe(1);
199
199
  });
200
200
 
201
+ /**
202
+ * On boot a session check can leave before the persisted token is installed.
203
+ * Its "no session" answer must not overwrite the verified user that hydrate
204
+ * restored in the meantime; that exact sequence left the app authenticated
205
+ * for chat while every plan-gated surface reported signed out.
206
+ */
207
+ test("a session check that predates the restored token does not wipe the restored user", async () => {
208
+ const seen: Array<string | null> = [];
209
+ let releaseFirst!: () => void;
210
+ setCloudApiFetchTransport(async (_url, init) => {
211
+ const cookie = new Headers(init?.headers).get("cookie");
212
+ seen.push(cookie);
213
+ if (seen.length === 1) {
214
+ await new Promise<void>((resolve) => { releaseFirst = resolve; });
215
+ return createResponse({ user: null });
216
+ }
217
+ return createResponse({ user: verifiedUser });
218
+ });
219
+
220
+ // A cookie-less check goes out first.
221
+ const early = apiClient.getSession();
222
+ expect(seen).toHaveLength(1);
223
+ expect(seen[0]).toBeNull();
224
+
225
+ // Hydrate installs the token and the cached user while it is in flight.
226
+ apiClient.setSessionToken("restored-token");
227
+ apiClient.restoreCachedUser(verifiedUser);
228
+ expect(apiClient.getCurrentUser()?.emailVerified).toBe(true);
229
+
230
+ releaseFirst();
231
+ await expect(early).resolves.toMatchObject({ id: verifiedUser.id });
232
+
233
+ // The stale answer was discarded and a second check went out with the real cookie.
234
+ expect(seen).toHaveLength(2);
235
+ expect(seen[1]).toContain("restored-token");
236
+ expect(apiClient.getCurrentUser()?.emailVerified).toBe(true);
237
+ });
238
+
201
239
  test("captures secure session cookies after login and reuses them on session refresh", async () => {
202
240
  const seenCookies: Array<string | null> = [];
203
241
 
@@ -1163,3 +1201,92 @@ describe("apiClient equity diagnostic", () => {
1163
1201
  expect(JSON.parse(String(seenInit?.body))).toEqual({ symbol: "AAPL", mode: "cache-first" });
1164
1202
  });
1165
1203
  });
1204
+
1205
+ describe("apiClient document search", () => {
1206
+ test("builds the search query from filters and omits empty ones", async () => {
1207
+ let seenUrl = "";
1208
+ globalThis.fetch = mockFetch(async (input: Request | string | URL) => {
1209
+ seenUrl = String(input);
1210
+ return createResponse({
1211
+ hits: [],
1212
+ total: 0,
1213
+ countCapped: false,
1214
+ hasMore: false,
1215
+ nextOffset: 0,
1216
+ tookMs: 3,
1217
+ });
1218
+ });
1219
+
1220
+ await apiClient.searchCloudDocuments({
1221
+ query: " margin pressure ",
1222
+ tickers: [" aapl ", "msft"],
1223
+ docTypes: ["transcript", "filing"],
1224
+ sources: [],
1225
+ from: "2026-01-01T00:00:00.000Z",
1226
+ sort: "newest",
1227
+ limit: 40,
1228
+ offset: 0,
1229
+ });
1230
+
1231
+ const url = new URL(seenUrl);
1232
+ expect(url.pathname).toBe("/cloud/search");
1233
+ expect(url.searchParams.get("q")).toBe("margin pressure");
1234
+ expect(url.searchParams.get("tickers")).toBe("AAPL,MSFT");
1235
+ expect(url.searchParams.get("docTypes")).toBe("transcript,filing");
1236
+ expect(url.searchParams.get("from")).toBe("2026-01-01T00:00:00.000Z");
1237
+ expect(url.searchParams.get("sort")).toBe("newest");
1238
+ expect(url.searchParams.get("limit")).toBe("40");
1239
+ expect(url.searchParams.has("sources")).toBe(false);
1240
+ expect(url.searchParams.has("to")).toBe(false);
1241
+ // Offset 0 is the first page; sending it only lengthens the cache key.
1242
+ expect(url.searchParams.has("offset")).toBe(false);
1243
+ // Counting is the server default, so only opting out travels.
1244
+ expect(url.searchParams.has("count")).toBe(false);
1245
+
1246
+ await apiClient.searchCloudDocuments({ query: "margin", limit: 3, count: false });
1247
+ expect(new URL(seenUrl).searchParams.get("count")).toBe("false");
1248
+ });
1249
+
1250
+ test("escapes the document route segments", async () => {
1251
+ let seenUrl = "";
1252
+ globalThis.fetch = mockFetch(async (input: Request | string | URL) => {
1253
+ seenUrl = String(input);
1254
+ return createResponse({ document: { chunks: [] } });
1255
+ });
1256
+
1257
+ await apiClient.getCloudSearchDocument("filing", "0000320193-26-000042/a b");
1258
+
1259
+ expect(new URL(seenUrl).pathname).toBe(
1260
+ "/cloud/search/documents/filing/0000320193-26-000042%2Fa%20b",
1261
+ );
1262
+ });
1263
+
1264
+ test("accepts a saved-search write with or without an envelope", async () => {
1265
+ const record = {
1266
+ id: "saved-1",
1267
+ name: "margin pressure",
1268
+ query: "margin pressure",
1269
+ filters: {},
1270
+ alertEnabled: true,
1271
+ alertChannels: ["email"],
1272
+ lastRunAt: null,
1273
+ lastMatchAt: null,
1274
+ matchCount: 0,
1275
+ createdAt: "2026-05-01T00:00:00.000Z",
1276
+ };
1277
+
1278
+ globalThis.fetch = mockFetch(async () => createResponse({ search: record }));
1279
+ expect((await apiClient.createCloudSavedSearch({
1280
+ name: record.name,
1281
+ query: record.query,
1282
+ })).id).toBe("saved-1");
1283
+
1284
+ globalThis.fetch = mockFetch(async () => createResponse(record));
1285
+ expect((await apiClient.updateCloudSavedSearch("saved-1", { alertEnabled: false })).id)
1286
+ .toBe("saved-1");
1287
+
1288
+ globalThis.fetch = mockFetch(async () => createResponse({}));
1289
+ await expect(apiClient.updateCloudSavedSearch("saved-1", { alertEnabled: false }))
1290
+ .rejects.toThrow("missing a record");
1291
+ });
1292
+ });
@@ -9,7 +9,9 @@ import { CloudApiSocket } from "./socket";
9
9
  import type {
10
10
  CloudCdsParams,
11
11
  CloudCongressHouseParams,
12
+ CloudEarningsCallsParams,
12
13
  CloudFredSeriesParams,
14
+ CloudSearchParams,
13
15
  CloudSecFilingParams,
14
16
  CloudSecFilingsParams,
15
17
  CloudHistoryParams,
@@ -48,9 +50,19 @@ import type {
48
50
  CloudEquityDiagnosticResult,
49
51
  CloudCdsResponse,
50
52
  CloudFredSeriesPayload,
53
+ CloudShillerPayload,
51
54
  CloudYieldPointPayload,
52
55
  CloudCongressHousePayload,
56
+ CloudEarningsCallListPayload,
57
+ CloudEarningsTranscriptPayload,
53
58
  CloudNewsPayload,
59
+ CloudSavedSearch,
60
+ CloudSavedSearchInput,
61
+ CloudSearchDocType,
62
+ CloudSearchDocument,
63
+ CloudSearchDocumentResponse,
64
+ CloudSearchHit,
65
+ CloudSearchResponse,
54
66
  CloudSecContentResponse,
55
67
  CloudSecDocumentsResponse,
56
68
  CloudSecFilingsResponse,
@@ -92,6 +104,8 @@ const ASSIST_REQUEST_TIMEOUT_MS = 6_000;
92
104
  class GloomApiClient {
93
105
  private currentUser: AuthUser | null = null;
94
106
  private sessionChecked = false;
107
+ /** Last few session transitions, content-free, for app://auth. */
108
+ private authTrace: Array<{ at: number; event: string; token: boolean; user: string }> = [];
95
109
  private sessionRequest: Promise<AuthUser | null> | null = null;
96
110
  private readonly currentUserListeners = new Set<() => void>();
97
111
  private readonly transport = new CloudApiRequestTransport();
@@ -154,6 +168,7 @@ class GloomApiClient {
154
168
  const changed = this.transport.getSessionToken() !== token;
155
169
  this.sessionChecked = false;
156
170
  this.transport.setSessionToken(token);
171
+ this.traceAuth(changed ? "setSessionToken:changed" : "setSessionToken:same");
157
172
  if (!token) {
158
173
  this.currentUser = null;
159
174
  this.emitCurrentUserChange();
@@ -196,8 +211,58 @@ class GloomApiClient {
196
211
  return this.transport.hasSessionCredential() && !!this.currentUser?.emailVerified;
197
212
  }
198
213
 
214
+ /**
215
+ * What this client currently believes about its session, with no secrets.
216
+ * Exposed over remote control so a "shows my username but not my
217
+ * subscription" report can be answered from the running app instead of
218
+ * from guesses about it.
219
+ */
220
+ describeAuthState(): {
221
+ hasSessionCredential: boolean;
222
+ hasSessionToken: boolean;
223
+ sessionChecked: boolean;
224
+ sessionRequestInFlight: boolean;
225
+ trace: Array<{ at: number; event: string; token: boolean; user: string }>;
226
+ currentUser: {
227
+ id: string;
228
+ emailVerified: boolean;
229
+ plan: string | null;
230
+ effectivePlan: string | null;
231
+ trialEndsAt: string | null;
232
+ } | null;
233
+ } {
234
+ const user = this.currentUser;
235
+ return {
236
+ hasSessionCredential: this.transport.hasSessionCredential(),
237
+ hasSessionToken: !!this.transport.getSessionToken(),
238
+ sessionChecked: this.sessionChecked,
239
+ sessionRequestInFlight: !!this.sessionRequest,
240
+ trace: [...this.authTrace],
241
+ currentUser: user
242
+ ? {
243
+ id: user.id,
244
+ emailVerified: user.emailVerified === true,
245
+ plan: user.plan ?? null,
246
+ effectivePlan: user.effectivePlan ?? null,
247
+ trialEndsAt: user.trialEndsAt ?? null,
248
+ }
249
+ : null,
250
+ };
251
+ }
252
+
253
+ private traceAuth(event: string, user: AuthUser | null = this.currentUser): void {
254
+ this.authTrace.push({
255
+ at: Date.now(),
256
+ event,
257
+ token: !!this.transport.getSessionToken(),
258
+ user: user ? (user.emailVerified ? "verified" : "unverified") : "none",
259
+ });
260
+ if (this.authTrace.length > 24) this.authTrace.shift();
261
+ }
262
+
199
263
  private setCurrentUser(user: AuthUser | null): void {
200
264
  const changed = this.socketEntitlementKey(this.currentUser) !== this.socketEntitlementKey(user);
265
+ this.traceAuth("setCurrentUser", user);
201
266
  this.currentUser = user;
202
267
  this.socket.syncAuthState({ reconnect: changed });
203
268
  this.emitCurrentUserChange();
@@ -265,12 +330,20 @@ class GloomApiClient {
265
330
  }
266
331
 
267
332
  async getSession(): Promise<AuthUser | null> {
268
- if (this.sessionRequest) return this.sessionRequest;
333
+ if (this.sessionRequest) {
334
+ this.traceAuth("getSession:joined-inflight");
335
+ return this.sessionRequest;
336
+ }
337
+ this.traceAuth("getSession:start");
269
338
  this.sessionRequest = this.auth.getSession();
270
339
  try {
271
340
  const user = await this.sessionRequest;
272
341
  this.sessionChecked = true;
342
+ this.traceAuth("getSession:done", user);
273
343
  return user;
344
+ } catch (error) {
345
+ this.traceAuth(`getSession:error:${error instanceof Error ? error.message.slice(0, 60) : "unknown"}`);
346
+ throw error;
274
347
  } finally {
275
348
  this.sessionRequest = null;
276
349
  }
@@ -640,6 +713,10 @@ class GloomApiClient {
640
713
  return this.data.getCloudFredSeries(seriesId, params);
641
714
  }
642
715
 
716
+ async getCloudShiller(): Promise<CloudShillerPayload> {
717
+ return this.data.getCloudShiller();
718
+ }
719
+
643
720
  async getCloudYieldCurve(): Promise<CloudYieldPointPayload[]> {
644
721
  return this.data.getCloudYieldCurve();
645
722
  }
@@ -652,6 +729,16 @@ class GloomApiClient {
652
729
  return this.data.getCloudCongressHouse(params);
653
730
  }
654
731
 
732
+ async getCloudEarningsCalls(
733
+ params: CloudEarningsCallsParams = {},
734
+ ): Promise<CloudEarningsCallListPayload> {
735
+ return this.data.getCloudEarningsCalls(params);
736
+ }
737
+
738
+ async getCloudEarningsTranscript(id: string): Promise<CloudEarningsTranscriptPayload> {
739
+ return this.data.getCloudEarningsTranscript(id);
740
+ }
741
+
655
742
  async getCloudSecFilings(params: CloudSecFilingsParams): Promise<CloudSecFilingsResponse> {
656
743
  return this.data.getCloudSecFilings(params);
657
744
  }
@@ -668,6 +755,47 @@ class GloomApiClient {
668
755
  return this.data.getCloudSec13F(path, params);
669
756
  }
670
757
 
758
+ async searchCloudDocuments(
759
+ params: CloudSearchParams,
760
+ options?: { signal?: AbortSignal },
761
+ ): Promise<CloudSearchResponse> {
762
+ return this.data.searchCloudDocuments(params, options);
763
+ }
764
+
765
+ async getCloudSearchDocument(
766
+ docType: CloudSearchDocType,
767
+ sourceId: string,
768
+ options?: { signal?: AbortSignal },
769
+ ): Promise<CloudSearchDocument> {
770
+ return this.data.getCloudSearchDocument(docType, sourceId, options);
771
+ }
772
+
773
+ async getCloudSavedSearches(options?: { signal?: AbortSignal }): Promise<CloudSavedSearch[]> {
774
+ return this.data.getCloudSavedSearches(options);
775
+ }
776
+
777
+ async createCloudSavedSearch(input: CloudSavedSearchInput): Promise<CloudSavedSearch> {
778
+ return this.data.createCloudSavedSearch(input);
779
+ }
780
+
781
+ async updateCloudSavedSearch(
782
+ id: string,
783
+ update: Partial<CloudSavedSearchInput>,
784
+ ): Promise<CloudSavedSearch> {
785
+ return this.data.updateCloudSavedSearch(id, update);
786
+ }
787
+
788
+ async deleteCloudSavedSearch(id: string): Promise<void> {
789
+ return this.data.deleteCloudSavedSearch(id);
790
+ }
791
+
792
+ async getCloudSavedSearchHits(
793
+ id: string,
794
+ options?: { signal?: AbortSignal },
795
+ ): Promise<CloudSearchHit[]> {
796
+ return this.data.getCloudSavedSearchHits(id, options);
797
+ }
798
+
671
799
  async getCloudNews(params: CloudNewsParams = {}): Promise<CloudNewsListResponse> {
672
800
  return this.data.getCloudNews(params);
673
801
  }