gloomberb 0.11.0 → 0.11.2

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 (392) hide show
  1. package/README.md +38 -15
  2. package/README.zh-CN.md +12 -12
  3. package/package.json +10 -3
  4. package/src/api-client/auth.ts +14 -10
  5. package/src/api-client/data.ts +54 -0
  6. package/src/api-client/index.test.ts +124 -0
  7. package/src/api-client/index.ts +139 -4
  8. package/src/api-client/paths.ts +89 -0
  9. package/src/api-client/request.ts +15 -2
  10. package/src/api-client/socket-health.test.ts +1 -0
  11. package/src/api-client/socket.ts +2 -1
  12. package/src/api-client/types.ts +163 -0
  13. package/src/app/pane-runtime/index.tsx +4 -1
  14. package/src/app/pane-runtime/plugin-bindings.test.ts +117 -0
  15. package/src/app/pane-runtime/plugin-bindings.ts +25 -4
  16. package/src/app/pane-runtime/ticker-inspector-runtime.ts +25 -14
  17. package/src/app/runtime/desktop-deeplink.test.ts +16 -0
  18. package/src/app/runtime/desktop-deeplink.ts +66 -2
  19. package/src/app/runtime/desktop-menu.ts +3 -0
  20. package/src/app/runtime/plugin-bindings.ts +30 -28
  21. package/src/app/runtime/update.ts +8 -6
  22. package/src/app.tsx +21 -7
  23. package/src/architecture/import-boundaries.test.ts +5 -1
  24. package/src/capabilities/factories.ts +4 -0
  25. package/src/cli/commands/automation.test.ts +9 -0
  26. package/src/cli/commands/automation.ts +22 -73
  27. package/src/cli/index.ts +10 -6
  28. package/src/cli/registry.ts +1 -1
  29. package/src/components/chart/composite/composite-chart.test.tsx +45 -2
  30. package/src/components/chart/composite/composite-chart.tsx +16 -12
  31. package/src/components/chart/composite/format.test.ts +34 -0
  32. package/src/components/chart/composite/format.ts +33 -10
  33. package/src/components/chart/composite/scene.ts +0 -21
  34. package/src/components/chart/composite/tools.ts +49 -0
  35. package/src/components/chart/core/axis-format.ts +13 -3
  36. package/src/components/chart/core/index.test.ts +7 -0
  37. package/src/components/chart/core/renderer.ts +3 -4
  38. package/src/components/command-bar/commands/direct/index.ts +4 -0
  39. package/src/components/command-bar/commands/registry.ts +8 -1
  40. package/src/components/command-bar/layout-items/current-layout.ts +1 -1
  41. package/src/components/command-bar/pane-templates/workflow.ts +4 -0
  42. package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -0
  43. package/src/components/command-bar/routes/root/shortcuts.test.ts +19 -0
  44. package/src/components/command-bar/routes/root/shortcuts.ts +3 -1
  45. package/src/components/command-bar/surface/index.test.tsx +6 -6
  46. package/src/components/command-bar/surface/index.tsx +4 -1
  47. package/src/components/command-bar/surface/pane.test.tsx +66 -2
  48. package/src/components/command-bar/view-model.test.ts +2 -1
  49. package/src/components/command-bar/workflow/ops.test.ts +54 -0
  50. package/src/components/command-bar/workflow/ops.ts +1 -1
  51. package/src/components/company-logo.test.ts +19 -0
  52. package/src/components/company-logo.tsx +60 -0
  53. package/src/components/feed-data-table/stack-view.tsx +7 -1
  54. package/src/components/index.ts +1 -0
  55. package/src/components/layout/detached-pane-shell.tsx +85 -15
  56. package/src/components/layout/header.tsx +5 -5
  57. package/src/components/layout/pane/title.ts +31 -3
  58. package/src/components/layout/shell/active-drag.ts +4 -11
  59. package/src/components/layout/shell/drag/index.ts +4 -20
  60. package/src/components/layout/shell/drag/runtime.ts +3 -4
  61. package/src/components/layout/shell/index.test.tsx +17 -3
  62. package/src/components/layout/shell/index.tsx +89 -31
  63. package/src/components/layout/shell/menu.test.ts +37 -1
  64. package/src/components/layout/shell/menu.ts +14 -0
  65. package/src/components/layout/shell/native/pointer-runtime.ts +22 -8
  66. package/src/components/layout/shell/pane/actions.ts +0 -5
  67. package/src/components/layout/shell/pane/management-shortcuts.ts +9 -4
  68. package/src/components/layout/shell/shortcuts.ts +6 -3
  69. package/src/components/layout/shell/ticker-link-menu.test.ts +82 -0
  70. package/src/components/layout/shell/ticker-link-menu.ts +78 -0
  71. package/src/components/layout/status-bar.test.tsx +62 -61
  72. package/src/components/layout/status-bar.tsx +53 -75
  73. package/src/components/onboarding/account-step/form-panel.tsx +4 -2
  74. package/src/components/onboarding/account-step/index.tsx +1 -1
  75. package/src/components/onboarding/account-step/signed-in-panel.tsx +1 -1
  76. package/src/components/onboarding/onboarding-wizard.test.tsx +44 -1
  77. package/src/components/onboarding/onboarding-wizard.tsx +13 -5
  78. package/src/components/onboarding/wizard-account.ts +4 -23
  79. package/src/components/table-view-shared.test.ts +11 -0
  80. package/src/components/table-view-shared.tsx +26 -0
  81. package/src/components/ui/data-table/index.tsx +19 -1
  82. package/src/components/ui/fields.tsx +5 -1
  83. package/src/core/state/app/layout-reducer.ts +36 -0
  84. package/src/core/state/app/layout.ts +10 -5
  85. package/src/core/state/app/state.ts +0 -13
  86. package/src/core/state/app/types.ts +6 -4
  87. package/src/data/config/chart-settings.ts +1 -1
  88. package/src/data/config/layout.ts +2 -0
  89. package/src/data/config/store/index.test.ts +27 -6
  90. package/src/data/config/store/normalize.ts +3 -5
  91. package/src/data/plugin-state-store.ts +11 -0
  92. package/src/data/sqlite/cache.test.ts +10 -0
  93. package/src/i18n/es.ts +13 -5
  94. package/src/i18n/ja.ts +13 -5
  95. package/src/i18n/ko.ts +13 -5
  96. package/src/i18n/zh-cn.ts +13 -5
  97. package/src/i18n/zh-tw.ts +13 -5
  98. package/src/layout-marketplace/api.test.ts +54 -0
  99. package/src/layout-marketplace/api.ts +65 -0
  100. package/src/layout-marketplace/gallery-desktop.test.tsx +262 -0
  101. package/src/layout-marketplace/gallery-desktop.tsx +375 -0
  102. package/src/layout-marketplace/gallery-terminal.tsx +376 -0
  103. package/src/layout-marketplace/gallery.test.tsx +84 -0
  104. package/src/layout-marketplace/gallery.tsx +323 -0
  105. package/src/layout-marketplace/geometry.test.ts +47 -0
  106. package/src/layout-marketplace/geometry.ts +80 -0
  107. package/src/layout-marketplace/mini-workspace.test.tsx +122 -0
  108. package/src/layout-marketplace/mini-workspace.tsx +428 -0
  109. package/src/layout-marketplace/model.test.ts +118 -0
  110. package/src/layout-marketplace/model.ts +171 -0
  111. package/src/layout-marketplace/name-dialog.tsx +60 -0
  112. package/src/layout-marketplace/pane-imagery.ts +85 -0
  113. package/src/layout-marketplace/pane.tsx +20 -0
  114. package/src/layout-marketplace/payload.test.ts +246 -0
  115. package/src/layout-marketplace/payload.ts +624 -0
  116. package/src/layout-marketplace/use-marketplace.ts +63 -0
  117. package/src/market-data/coordinator/index.test.ts +2 -2
  118. package/src/market-data/coordinator/index.ts +1 -1
  119. package/src/market-data/hooks.tsx +0 -41
  120. package/src/market-data/market/format.test.ts +7 -0
  121. package/src/market-data/market/freshness.ts +5 -2
  122. package/src/market-data/quotes/freshness.test.ts +16 -0
  123. package/src/news/aggregator.test.ts +92 -3
  124. package/src/news/aggregator.ts +98 -18
  125. package/src/news/hooks.test.tsx +2 -0
  126. package/src/news/hooks.ts +27 -1
  127. package/src/news/news-model.ts +5 -2
  128. package/src/news/types.ts +7 -0
  129. package/src/plugins/broker-sync/broker-sync.test.ts +58 -0
  130. package/src/plugins/broker-sync/normalize.ts +42 -5
  131. package/src/plugins/builtin/account-management/pane.tsx +5 -5
  132. package/src/plugins/builtin/ai/index.tsx +18 -2
  133. package/src/plugins/builtin/ai/screener/pane.test.tsx +47 -4
  134. package/src/plugins/builtin/ai/screener/pane.tsx +35 -12
  135. package/src/plugins/builtin/alerts/alert-engine.test.ts +53 -1
  136. package/src/plugins/builtin/alerts/alert-engine.ts +34 -1
  137. package/src/plugins/builtin/alerts/index.test.tsx +49 -6
  138. package/src/plugins/builtin/alerts/index.tsx +11 -6
  139. package/src/plugins/builtin/alerts/pane.tsx +51 -9
  140. package/src/plugins/builtin/alerts/quotes.ts +11 -3
  141. package/src/plugins/builtin/alerts/types.ts +1 -0
  142. package/src/plugins/builtin/analytics/index.tsx +11 -1
  143. package/src/plugins/builtin/analytics/pane-model.ts +6 -4
  144. package/src/plugins/builtin/broker-manager/index.tsx +3 -0
  145. package/src/plugins/builtin/buildout/model/index.ts +1 -1
  146. package/src/plugins/builtin/buildout/pane/index.tsx +17 -18
  147. package/src/plugins/builtin/cds/client.test.ts +83 -0
  148. package/src/plugins/builtin/cds/client.ts +67 -0
  149. package/src/plugins/builtin/cds/index.tsx +41 -0
  150. package/src/plugins/builtin/cds/model.test.ts +232 -0
  151. package/src/plugins/builtin/cds/model.ts +401 -0
  152. package/src/plugins/builtin/cds/pane.test.tsx +132 -0
  153. package/src/plugins/builtin/cds/pane.tsx +358 -0
  154. package/src/plugins/builtin/chart-composer/chart-spec.ts +31 -0
  155. package/src/plugins/builtin/chart-composer/index.tsx +88 -2
  156. package/src/plugins/builtin/chart-composer/pane-share.test.ts +71 -0
  157. package/src/plugins/builtin/chart-composer/pane.tsx +103 -15
  158. package/src/plugins/builtin/chat/content/index.tsx +4 -1
  159. package/src/plugins/builtin/chat/controller/channels.ts +7 -1
  160. package/src/plugins/builtin/chat/controller/index.ts +14 -9
  161. package/src/plugins/builtin/chat/controller/notifications.ts +13 -3
  162. package/src/plugins/builtin/chat/controller/persistence.ts +0 -3
  163. package/src/plugins/builtin/chat/controller/realtime.ts +3 -3
  164. package/src/plugins/builtin/chat/controller/send.ts +1 -3
  165. package/src/plugins/builtin/chat/controller/session-runtime.ts +18 -11
  166. package/src/plugins/builtin/chat/controller/state.ts +1 -3
  167. package/src/plugins/builtin/chat/controller/storage.ts +0 -2
  168. package/src/plugins/builtin/chat/controller/utils.ts +4 -6
  169. package/src/plugins/builtin/chat/controller/view.ts +2 -2
  170. package/src/plugins/builtin/chat/controller.test.ts +142 -14
  171. package/src/plugins/builtin/chat/message/desktop.test.tsx +88 -0
  172. package/src/plugins/builtin/chat/message/desktop.tsx +3 -1
  173. package/src/plugins/builtin/chat/message/profile-popover.test.tsx +1 -29
  174. package/src/plugins/builtin/chat/profile-popover.ts +53 -22
  175. package/src/plugins/builtin/chat/sidebar.test.tsx +81 -0
  176. package/src/plugins/builtin/chat/sidebar.tsx +63 -0
  177. package/src/plugins/builtin/chat/test-harness.tsx +29 -1
  178. package/src/plugins/builtin/cloud/auth-actions.tsx +14 -7
  179. package/src/plugins/builtin/cloud/auth-commands.ts +21 -51
  180. package/src/plugins/builtin/cloud/auth-dialog.tsx +296 -0
  181. package/src/{components/onboarding/account-step/model.test.ts → plugins/builtin/cloud/auth-model.test.ts} +1 -1
  182. package/src/{components/onboarding/account-step/model.ts → plugins/builtin/cloud/auth-model.ts} +37 -3
  183. package/src/plugins/builtin/cloud/browser.tsx +9 -0
  184. package/src/plugins/builtin/cloud/buildout-module.tsx +23 -0
  185. package/src/plugins/builtin/cloud/device-signin.ts +2 -8
  186. package/src/plugins/builtin/cloud/index.tsx +2 -0
  187. package/src/plugins/builtin/cloud/plugin.tsx +14 -24
  188. package/src/plugins/builtin/cloud/upgrade-command.ts +13 -6
  189. package/src/plugins/builtin/cloud-tweets/pane-share.test.ts +56 -0
  190. package/src/plugins/builtin/cloud-tweets/pane.tsx +20 -5
  191. package/src/plugins/builtin/cloud-tweets/registration.ts +50 -6
  192. package/src/plugins/builtin/composite-plugins.ts +3 -1
  193. package/src/plugins/builtin/congress-trades/detail.tsx +43 -1
  194. package/src/plugins/builtin/congress-trades/model.test.ts +85 -0
  195. package/src/plugins/builtin/congress-trades/model.ts +69 -1
  196. package/src/plugins/builtin/congress-trades/pane.tsx +51 -2
  197. package/src/plugins/builtin/connections/index.ts +3 -0
  198. package/src/plugins/builtin/dividend-yield/client.ts +18 -0
  199. package/src/plugins/builtin/dividend-yield/pane.tsx +7 -6
  200. package/src/plugins/builtin/earnings/index.tsx +5 -3
  201. package/src/plugins/builtin/earnings/model.test.ts +19 -1
  202. package/src/plugins/builtin/earnings/model.ts +17 -15
  203. package/src/plugins/builtin/econ/calendar-model.ts +0 -5
  204. package/src/plugins/builtin/econ/index.test.tsx +6 -1
  205. package/src/plugins/builtin/fear-greed/charts.tsx +12 -8
  206. package/src/plugins/builtin/help/index.tsx +2 -2
  207. package/src/plugins/builtin/holders/thirteenf-match.ts +1 -2
  208. package/src/plugins/builtin/insider/index.tsx +27 -11
  209. package/src/plugins/builtin/kelly-sizer/index.tsx +4 -1
  210. package/src/plugins/builtin/layout-manager/index.tsx +17 -4
  211. package/src/plugins/builtin/news/index.tsx +11 -6
  212. package/src/plugins/builtin/news/wire/breaking/notifications.test.ts +2 -0
  213. package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +2 -0
  214. package/src/plugins/builtin/news/wire/breaking/pane.tsx +4 -1
  215. package/src/plugins/builtin/news/wire/feed-config.test.ts +15 -1
  216. package/src/plugins/builtin/news/wire/feed-config.ts +25 -5
  217. package/src/plugins/builtin/news/wire/index.tsx +3 -3
  218. package/src/plugins/builtin/news/wire/industry-pane.tsx +7 -2
  219. package/src/plugins/builtin/news/wire/news/footer.ts +34 -1
  220. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +4 -1
  221. package/src/plugins/builtin/news/wire/news/table.tsx +8 -2
  222. package/src/plugins/builtin/news/wire/rss/source.test.ts +12 -0
  223. package/src/plugins/builtin/news/wire/rss/source.ts +1 -1
  224. package/src/plugins/builtin/notes/files.ts +80 -0
  225. package/src/plugins/builtin/notes/index.tsx +3 -0
  226. package/src/plugins/builtin/notes/sync.test.ts +49 -0
  227. package/src/plugins/builtin/notes/sync.ts +98 -0
  228. package/src/plugins/builtin/options/index.tsx +1 -0
  229. package/src/plugins/builtin/options/view.test.tsx +25 -3
  230. package/src/plugins/builtin/options/view.tsx +1 -0
  231. package/src/plugins/builtin/portfolio-list/account-metrics.test.ts +22 -0
  232. package/src/plugins/builtin/portfolio-list/account-metrics.ts +12 -7
  233. package/src/plugins/builtin/portfolio-list/cli/render.ts +5 -3
  234. package/src/plugins/builtin/portfolio-list/column-values.ts +15 -4
  235. package/src/plugins/builtin/portfolio-list/header.tsx +13 -3
  236. package/src/plugins/builtin/portfolio-list/index.tsx +8 -0
  237. package/src/plugins/builtin/portfolio-list/metrics.test.ts +31 -1
  238. package/src/plugins/builtin/portfolio-list/pane/index.test.tsx +1 -0
  239. package/src/plugins/builtin/portfolio-list/pane/index.tsx +10 -7
  240. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +6 -1
  241. package/src/plugins/builtin/portfolio-list/position-metrics.ts +25 -6
  242. package/src/plugins/builtin/portfolio-list/summary/index.tsx +35 -21
  243. package/src/plugins/builtin/portfolio-list/summary/totals.ts +20 -6
  244. package/src/plugins/builtin/research/corporate-actions-pane.tsx +5 -12
  245. package/src/plugins/builtin/research/equity-diagnostic-pane.test.tsx +329 -0
  246. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +582 -0
  247. package/src/plugins/builtin/research/index.test.ts +14 -0
  248. package/src/plugins/builtin/research/index.tsx +29 -0
  249. package/src/plugins/builtin/sec/index.tsx +24 -9
  250. package/src/plugins/builtin/sectors/index.tsx +14 -0
  251. package/src/plugins/builtin/shared/cloud-upgrade.ts +18 -11
  252. package/src/plugins/builtin/shared/public-pane.test.ts +12 -0
  253. package/src/plugins/builtin/shared/public-pane.ts +9 -0
  254. package/src/plugins/builtin/shared/public-share.ts +23 -0
  255. package/src/plugins/builtin/shared/ticker-source.ts +32 -0
  256. package/src/plugins/builtin/shared/ticker-surface.test.ts +27 -0
  257. package/src/plugins/builtin/shared/ticker-surface.ts +17 -0
  258. package/src/plugins/builtin/substack/api/cache.ts +7 -3
  259. package/src/plugins/builtin/substack/api/loaders.ts +36 -12
  260. package/src/plugins/builtin/substack/api/types.ts +2 -0
  261. package/src/plugins/builtin/substack/pane-state.ts +1 -1
  262. package/src/plugins/builtin/substack/pane.tsx +55 -12
  263. package/src/plugins/builtin/thirteenf/api.test.ts +1 -1
  264. package/src/plugins/builtin/thirteenf/api.ts +40 -18
  265. package/src/plugins/builtin/thirteenf/data.ts +15 -7
  266. package/src/plugins/builtin/thirteenf/pane.tsx +45 -10
  267. package/src/plugins/builtin/ticker-detail/index.tsx +10 -0
  268. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +12 -4
  269. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +8 -2
  270. package/src/plugins/builtin/ticker-detail/settings.ts +12 -0
  271. package/src/plugins/builtin/treasury-auctions/index.tsx +2 -0
  272. package/src/plugins/builtin/world-indices/index.tsx +2 -0
  273. package/src/plugins/catalog-browser.test.ts +78 -0
  274. package/src/plugins/catalog-browser.ts +136 -0
  275. package/src/plugins/gridlock-notification.ts +2 -1
  276. package/src/plugins/ibkr/flex/index.test.ts +19 -0
  277. package/src/plugins/ibkr/flex/index.ts +1 -1
  278. package/src/plugins/pane-manager/gridlock.ts +87 -3
  279. package/src/plugins/pane-manager.test.ts +30 -0
  280. package/src/plugins/pane-manager.ts +6 -1
  281. package/src/plugins/prediction-markets/chart.tsx +2 -2
  282. package/src/plugins/prediction-markets/controller/catalog.ts +77 -2
  283. package/src/plugins/prediction-markets/controller/data.ts +7 -1
  284. package/src/plugins/prediction-markets/controller/index.ts +9 -2
  285. package/src/plugins/prediction-markets/controller/keyboard.ts +10 -14
  286. package/src/plugins/prediction-markets/detail/overview.tsx +21 -0
  287. package/src/plugins/prediction-markets/detail/pane.tsx +43 -34
  288. package/src/plugins/prediction-markets/detail/trades.tsx +34 -9
  289. package/src/plugins/prediction-markets/detail.test.tsx +29 -2
  290. package/src/plugins/prediction-markets/index.tsx +8 -4
  291. package/src/plugins/prediction-markets/navigation.ts +0 -1
  292. package/src/plugins/prediction-markets/pane.test.tsx +77 -0
  293. package/src/plugins/prediction-markets/pane.tsx +12 -0
  294. package/src/plugins/prediction-markets/plugin.test.tsx +36 -0
  295. package/src/plugins/prediction-markets/services/kalshi/adapter.ts +49 -9
  296. package/src/plugins/prediction-markets/services/polymarket/adapter.ts +31 -0
  297. package/src/plugins/prediction-markets/test-helpers.tsx +9 -0
  298. package/src/plugins/registry/index.ts +2 -0
  299. package/src/plugins/ticker-navigation.test.ts +24 -0
  300. package/src/plugins/ticker-navigation.ts +40 -0
  301. package/src/remote/schema.ts +1 -1
  302. package/src/renderers/browser/app-services.ts +61 -0
  303. package/src/renderers/browser/cloud-transport.test.ts +68 -0
  304. package/src/renderers/browser/cloud-transport.ts +48 -0
  305. package/src/renderers/browser/config-host.ts +58 -0
  306. package/src/renderers/browser/deeplink-bridge.test.ts +41 -0
  307. package/src/renderers/browser/deeplink-bridge.ts +22 -0
  308. package/src/renderers/browser/error-boundary.tsx +23 -0
  309. package/src/renderers/browser/main.tsx +64 -0
  310. package/src/renderers/browser/persistence.ts +74 -0
  311. package/src/renderers/browser/storage.test.ts +66 -0
  312. package/src/renderers/browser/storage.ts +50 -0
  313. package/src/renderers/browser/ticker-repository.ts +59 -0
  314. package/src/renderers/browser/ui-host.tsx +40 -0
  315. package/src/renderers/cloudflare/worker.test.ts +136 -0
  316. package/src/renderers/cloudflare/worker.ts +116 -0
  317. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -2
  318. package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
  319. package/src/renderers/electrobun/bun/desktop/plugin-state.ts +7 -1
  320. package/src/renderers/electrobun/view/app-services.ts +14 -3
  321. package/src/renderers/electrobun/view/build-assets.ts +12 -10
  322. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  323. package/src/renderers/electrobun/view/data-table/index.tsx +10 -1
  324. package/src/renderers/electrobun/view/desktop/controls.tsx +2 -0
  325. package/src/renderers/electrobun/view/dom-ui-host.tsx +143 -0
  326. package/src/renderers/electrobun/view/host/box.test.tsx +33 -0
  327. package/src/renderers/electrobun/view/host/box.tsx +11 -6
  328. package/src/renderers/electrobun/view/host/chart-surface.tsx +7 -1
  329. package/src/renderers/electrobun/view/host/input.tsx +11 -7
  330. package/src/renderers/electrobun/view/host/native.ts +0 -5
  331. package/src/renderers/electrobun/view/main.tsx +2 -0
  332. package/src/renderers/electrobun/view/remote/persistence.ts +5 -0
  333. package/src/renderers/electrobun/view/styles.css +107 -7
  334. package/src/renderers/electrobun/view/toast-host.test.tsx +78 -0
  335. package/src/renderers/electrobun/view/toast-host.tsx +69 -43
  336. package/src/renderers/electrobun/view/ui-host.tsx +6 -137
  337. package/src/renderers/opentui/start.tsx +2 -0
  338. package/src/renderers/share/layout-view.test.tsx +58 -0
  339. package/src/renderers/share/layout-view.tsx +133 -0
  340. package/src/renderers/share/main.tsx +102 -0
  341. package/src/renderers/share/styles.css +54 -0
  342. package/src/renderers/share/view.test.tsx +100 -0
  343. package/src/renderers/share/view.tsx +150 -0
  344. package/src/shares/api.test.ts +105 -0
  345. package/src/shares/api.ts +127 -0
  346. package/src/shares/chart-snapshot.test.ts +34 -0
  347. package/src/shares/chart-snapshot.ts +28 -0
  348. package/src/shares/location.test.ts +19 -0
  349. package/src/shares/location.ts +11 -0
  350. package/src/shares/pane.test.ts +110 -0
  351. package/src/shares/pane.ts +54 -0
  352. package/src/shares/payload.ts +177 -0
  353. package/src/sources/gloomberb-cloud/index.test.ts +61 -1
  354. package/src/sources/gloomberb-cloud/index.ts +86 -14
  355. package/src/sources/gloomberb-cloud/normalizers.test.ts +34 -0
  356. package/src/sources/gloomberb-cloud/normalizers.ts +5 -5
  357. package/src/sources/provider-router/history.test.ts +2 -2
  358. package/src/sources/provider-router/news.test.ts +34 -0
  359. package/src/sources/provider-router/news.ts +15 -1
  360. package/src/sources/sec-edgar.test.ts +59 -0
  361. package/src/sources/sec-edgar.ts +63 -23
  362. package/src/sources/yahoo-finance/mappers.ts +3 -0
  363. package/src/sources/yahoo-finance/snapshots.ts +10 -2
  364. package/src/sources/yahoo-finance/symbols.test.ts +10 -0
  365. package/src/sources/yahoo-finance/symbols.ts +13 -8
  366. package/src/sources/yahoo-finance.test.ts +1 -1
  367. package/src/sources/yahoo-finance.ts +6 -2
  368. package/src/state/app/bootstrap.ts +6 -7
  369. package/src/state/app/context/index.test.ts +41 -1
  370. package/src/state/app/context/index.test.tsx +34 -16
  371. package/src/state/selectors-ui.ts +0 -8
  372. package/src/sync/baseline.ts +32 -0
  373. package/src/sync/controller.test.ts +124 -1
  374. package/src/sync/controller.ts +54 -12
  375. package/src/sync/core-contributors.test.ts +44 -0
  376. package/src/sync/core-contributors.ts +59 -10
  377. package/src/sync/react.ts +22 -2
  378. package/src/sync/types.ts +12 -0
  379. package/src/theme/font-scale.ts +0 -11
  380. package/src/tickers/search/index.test.ts +27 -0
  381. package/src/tickers/search/upsert.ts +10 -0
  382. package/src/types/capability-route-source.ts +2 -1
  383. package/src/types/config.ts +35 -5
  384. package/src/types/desktop-menu.ts +1 -0
  385. package/src/types/plugin.ts +36 -0
  386. package/src/ui/context-menu.tsx +1 -1
  387. package/src/ui/host.tsx +4 -0
  388. package/src/ui/toast.tsx +2 -0
  389. package/src/utils/back-navigation.ts +0 -4
  390. package/src/utils/price-history.ts +11 -10
  391. package/src/utils/sec.test.ts +6 -0
  392. package/src/utils/sec.ts +1 -1
@@ -3,11 +3,15 @@ import type { InstrumentSearchResult } from "../types/instrument";
3
3
  import { CloudAuthApi } from "./auth";
4
4
  import { CloudChatApi } from "./chat";
5
5
  import { CloudDataApi } from "./data";
6
+ import { ApiRequestError } from "./errors";
6
7
  import { CloudApiRequestTransport } from "./request";
7
8
  import { CloudApiSocket } from "./socket";
8
9
  import type {
10
+ CloudCdsParams,
9
11
  CloudCongressHouseParams,
10
12
  CloudFredSeriesParams,
13
+ CloudSecFilingParams,
14
+ CloudSecFilingsParams,
11
15
  CloudHistoryParams,
12
16
  CloudNewsParams,
13
17
  CloudTickerTweetsParams,
@@ -40,10 +44,16 @@ import type {
40
44
  CloudCorporateActionsPayload,
41
45
  CloudPricePointPayload,
42
46
  CloudEconEventPayload,
47
+ CloudEquityDiagnosticMode,
48
+ CloudEquityDiagnosticResult,
49
+ CloudCdsResponse,
43
50
  CloudFredSeriesPayload,
44
51
  CloudYieldPointPayload,
45
52
  CloudCongressHousePayload,
46
53
  CloudNewsPayload,
54
+ CloudSecContentResponse,
55
+ CloudSecDocumentsResponse,
56
+ CloudSecFilingsResponse,
47
57
  CloudNewsListResponse,
48
58
  CloudTweetSearchResponse,
49
59
  CloudMarketResponse,
@@ -62,6 +72,13 @@ import type {
62
72
  ScannerKind,
63
73
  } from "./types";
64
74
  import type { SyncSettings, SyncSnapshot } from "../sync/types";
75
+ import {
76
+ isMarketplaceLayoutId,
77
+ parseMarketplaceLayoutEntry,
78
+ parseMarketplaceLayoutList,
79
+ type LayoutMarketplaceEntry,
80
+ type LayoutMarketplacePayload,
81
+ } from "../layout-marketplace/payload";
65
82
 
66
83
  export type * from "./types";
67
84
  export { setCloudApiFetchTransport } from "./request";
@@ -73,6 +90,8 @@ const ASSIST_REQUEST_TIMEOUT_MS = 6_000;
73
90
 
74
91
  class GloomApiClient {
75
92
  private currentUser: AuthUser | null = null;
93
+ private sessionChecked = false;
94
+ private sessionRequest: Promise<AuthUser | null> | null = null;
76
95
  private readonly currentUserListeners = new Set<() => void>();
77
96
  private readonly transport = new CloudApiRequestTransport();
78
97
  private readonly auth: CloudAuthApi;
@@ -84,6 +103,7 @@ class GloomApiClient {
84
103
  this.auth = new CloudAuthApi({
85
104
  getCurrentUser: () => this.currentUser,
86
105
  getSessionToken: () => this.transport.getSessionToken(),
106
+ hasSessionCredential: () => this.transport.hasSessionCredential(),
87
107
  request: (path, options) => this.request(path, options),
88
108
  requireCapturedSession: (message) => this.requireCapturedSession(message),
89
109
  setCurrentUser: (user) => this.setCurrentUser(user),
@@ -93,6 +113,7 @@ class GloomApiClient {
93
113
  this.socket = new CloudApiSocket({
94
114
  getBaseUrl: () => this.transport.baseUrl,
95
115
  getSocketAuthToken: () => this.getSocketAuthToken(),
116
+ hasSessionCredential: () => this.transport.hasSessionCredential(),
96
117
  hasVerifiedUser: () => this.currentUser?.emailVerified === true,
97
118
  isUsingWebSocketToken: () => !!this.transport.getWebSocketToken(),
98
119
  clearWebSocketTokenForFallback: () => this.transport.clearWebSocketTokenForFallback(),
@@ -123,8 +144,14 @@ class GloomApiClient {
123
144
  return this.transport.getWebSocketToken();
124
145
  }
125
146
 
147
+ setCookieSessionMode(enabled: boolean): void {
148
+ this.sessionChecked = false;
149
+ this.transport.setCookieSessionMode(enabled);
150
+ }
151
+
126
152
  setSessionToken(token: string | null): void {
127
153
  const changed = this.transport.getSessionToken() !== token;
154
+ this.sessionChecked = false;
128
155
  this.transport.setSessionToken(token);
129
156
  if (!token) {
130
157
  this.currentUser = null;
@@ -143,6 +170,15 @@ class GloomApiClient {
143
170
  return this.currentUser;
144
171
  }
145
172
 
173
+ /**
174
+ * Whether a signed-in session exists on this surface. Browser builds keep the
175
+ * session in an HttpOnly cookie, so the raw token is deliberately null there
176
+ * and the restored user is the only signal.
177
+ */
178
+ isSignedIn(): boolean {
179
+ return !!this.transport.getSessionToken() || !!this.currentUser;
180
+ }
181
+
146
182
  /** Notifies when the signed-in user changes, including plan and trial entitlement. */
147
183
  subscribeCurrentUser(listener: () => void): () => void {
148
184
  this.currentUserListeners.add(listener);
@@ -156,7 +192,7 @@ class GloomApiClient {
156
192
  }
157
193
 
158
194
  isVerified(): boolean {
159
- return !!this.transport.getSessionToken() && !!this.currentUser?.emailVerified;
195
+ return this.transport.hasSessionCredential() && !!this.currentUser?.emailVerified;
160
196
  }
161
197
 
162
198
  private setCurrentUser(user: AuthUser | null): void {
@@ -187,7 +223,7 @@ class GloomApiClient {
187
223
  }
188
224
 
189
225
  private requireCapturedSession(message: string): void {
190
- if (this.transport.getSessionToken()) return;
226
+ if (this.transport.hasSessionCredential()) return;
191
227
  this.transport.setWebSocketToken(null);
192
228
  this.setCurrentUser(null);
193
229
  throw new Error(message);
@@ -202,7 +238,9 @@ class GloomApiClient {
202
238
  }
203
239
 
204
240
  async ensureVerifiedSession(): Promise<AuthUser | null> {
205
- return this.auth.ensureVerifiedSession();
241
+ if (!this.transport.hasSessionCredential()) return null;
242
+ if (!this.currentUser && !this.sessionChecked) await this.getSession();
243
+ return this.currentUser?.emailVerified ? this.currentUser : null;
206
244
  }
207
245
 
208
246
  async signUp(email: string, username: string, name: string, password: string): Promise<AuthUser> {
@@ -226,17 +264,47 @@ class GloomApiClient {
226
264
  }
227
265
 
228
266
  async getSession(): Promise<AuthUser | null> {
229
- return this.auth.getSession();
267
+ if (this.sessionRequest) return this.sessionRequest;
268
+ this.sessionRequest = this.auth.getSession();
269
+ try {
270
+ const user = await this.sessionRequest;
271
+ this.sessionChecked = true;
272
+ return user;
273
+ } finally {
274
+ this.sessionRequest = null;
275
+ }
230
276
  }
231
277
 
232
278
  async sendVerification(): Promise<CloudVerificationResponse> {
233
279
  return this.auth.sendVerification();
234
280
  }
235
281
 
282
+ async requestPasswordReset(email: string): Promise<void> {
283
+ return this.auth.requestPasswordReset(email);
284
+ }
285
+
236
286
  async createBrowserHandoff(): Promise<CloudBrowserHandoffResponse> {
237
287
  return this.auth.createBrowserHandoff();
238
288
  }
239
289
 
290
+ /** Creates a Stripe checkout session for Cloud Pro; the URL opens in a browser. */
291
+ async createCloudCheckout(): Promise<{ url: string }> {
292
+ return this.request<{ url: string }>("/stripe/checkout", { method: "POST", body: JSON.stringify({}) });
293
+ }
294
+
295
+ /** Stores a verified user's public terminal snapshot or pane handoff. */
296
+ async createTerminalShare(payload: unknown): Promise<{ id: string; expiresAt: string }> {
297
+ return this.request<{ id: string; expiresAt: string }>("/shares", {
298
+ method: "POST",
299
+ body: JSON.stringify(payload),
300
+ });
301
+ }
302
+
303
+ /** Stripe billing portal for an account that already has a subscription. */
304
+ async createBillingPortal(): Promise<{ url: string }> {
305
+ return this.request<{ url: string }>("/stripe/portal", { method: "POST", body: JSON.stringify({}) });
306
+ }
307
+
240
308
  async getAccountProfile(): Promise<AccountProfile> {
241
309
  return this.auth.getAccountProfile();
242
310
  }
@@ -271,6 +339,45 @@ class GloomApiClient {
271
339
  });
272
340
  }
273
341
 
342
+ async getMarketplaceLayout(
343
+ id: string,
344
+ options?: { signal?: AbortSignal },
345
+ ): Promise<LayoutMarketplaceEntry | null> {
346
+ if (!isMarketplaceLayoutId(id)) return null;
347
+ try {
348
+ return parseMarketplaceLayoutEntry(await this.request<unknown>(`/layouts/${encodeURIComponent(id)}`, {
349
+ method: "GET",
350
+ signal: options?.signal,
351
+ }));
352
+ } catch (error) {
353
+ if (error instanceof ApiRequestError && error.status === 404) return null;
354
+ throw error;
355
+ }
356
+ }
357
+
358
+ async listMarketplaceLayouts(options?: { signal?: AbortSignal }): Promise<LayoutMarketplaceEntry[]> {
359
+ const items = parseMarketplaceLayoutList(await this.request<unknown>("/layouts", {
360
+ method: "GET",
361
+ signal: options?.signal,
362
+ }));
363
+ if (!items) throw new Error("The layout marketplace returned invalid data.");
364
+ return items;
365
+ }
366
+
367
+ async publishMarketplaceLayout(
368
+ name: string,
369
+ payload: LayoutMarketplacePayload,
370
+ options?: { signal?: AbortSignal },
371
+ ): Promise<LayoutMarketplaceEntry> {
372
+ const item = parseMarketplaceLayoutEntry(await this.request<unknown>("/layouts", {
373
+ method: "POST",
374
+ body: JSON.stringify({ name: name.trim(), ...payload }),
375
+ signal: options?.signal,
376
+ }));
377
+ if (!item) throw new Error("The layout marketplace returned invalid data.");
378
+ return item;
379
+ }
380
+
274
381
  async updateSyncSettings(update: Partial<SyncSettings>): Promise<SyncSettings> {
275
382
  const result = await this.request<{ settings: SyncSettings }>("/sync/settings", {
276
383
  method: "PATCH",
@@ -513,6 +620,14 @@ class GloomApiClient {
513
620
  return this.data.getCloudEconomicCalendar();
514
621
  }
515
622
 
623
+ async getCloudEquityDiagnostic(
624
+ symbol: string,
625
+ exchange?: string,
626
+ mode: CloudEquityDiagnosticMode = "cache-first",
627
+ ): Promise<CloudEquityDiagnosticResult> {
628
+ return this.data.getCloudEquityDiagnostic(symbol, exchange, mode);
629
+ }
630
+
516
631
  async getCloudFredSeries(
517
632
  seriesId: string,
518
633
  params: CloudFredSeriesParams = {},
@@ -524,10 +639,30 @@ class GloomApiClient {
524
639
  return this.data.getCloudYieldCurve();
525
640
  }
526
641
 
642
+ async getCloudCds(params: CloudCdsParams = {}): Promise<CloudCdsResponse> {
643
+ return this.data.getCloudCds(params);
644
+ }
645
+
527
646
  async getCloudCongressHouse(params: CloudCongressHouseParams = {}): Promise<CloudCongressHousePayload> {
528
647
  return this.data.getCloudCongressHouse(params);
529
648
  }
530
649
 
650
+ async getCloudSecFilings(params: CloudSecFilingsParams): Promise<CloudSecFilingsResponse> {
651
+ return this.data.getCloudSecFilings(params);
652
+ }
653
+
654
+ async getCloudSecFilingDocuments(params: CloudSecFilingParams): Promise<CloudSecDocumentsResponse> {
655
+ return this.data.getCloudSecFilingDocuments(params);
656
+ }
657
+
658
+ async getCloudSecFilingContent(params: CloudSecFilingParams): Promise<CloudSecContentResponse> {
659
+ return this.data.getCloudSecFilingContent(params);
660
+ }
661
+
662
+ async getCloudSec13F(path: string, params: Record<string, string | number | undefined> = {}): Promise<unknown> {
663
+ return this.data.getCloudSec13F(path, params);
664
+ }
665
+
531
666
  async getCloudNews(params: CloudNewsParams = {}): Promise<CloudNewsListResponse> {
532
667
  return this.data.getCloudNews(params);
533
668
  }
@@ -16,10 +16,19 @@ export type CloudFredSeriesParams = {
16
16
  sortOrder?: "asc" | "desc";
17
17
  };
18
18
 
19
+ export type CloudCdsParams = {
20
+ /** Reference entity name, matched by the backend against DTCC issuer names. */
21
+ issuer?: string;
22
+ days?: number;
23
+ limit?: number;
24
+ };
25
+
19
26
  export type CloudCongressHouseParams = {
20
27
  year?: number;
21
28
  limit?: number;
29
+ offset?: number;
22
30
  filingLimit?: number;
31
+ filingOffset?: number;
23
32
  member?: string;
24
33
  ticker?: string;
25
34
  refresh?: boolean;
@@ -60,6 +69,23 @@ export type CloudTweetSearchParams = {
60
69
  hours?: number;
61
70
  };
62
71
 
72
+ const LOGO_SYMBOL_RE = /^[A-Z0-9][A-Z0-9._^:-]{0,23}$/;
73
+ const CRYPTO_QUOTE = /[-/](USD|USDT|USDC|EUR|GBP|BTC)$/;
74
+
75
+ export type CloudLogoKind = "ticker" | "crypto";
76
+
77
+ export function normalizeCloudLogoSymbol(kind: CloudLogoKind, symbol: string): string | null {
78
+ let normalized = symbol.trim().toUpperCase();
79
+ if (kind === "crypto") normalized = normalized.replace(CRYPTO_QUOTE, "");
80
+ return LOGO_SYMBOL_RE.test(normalized) ? normalized : null;
81
+ }
82
+
83
+ export function cloudLogoPath(kind: CloudLogoKind, symbol: string): string | null {
84
+ const normalized = normalizeCloudLogoSymbol(kind, symbol);
85
+ if (!normalized) return null;
86
+ return `/cloud/logos/${kind}/${encodeURIComponent(normalized)}`;
87
+ }
88
+
63
89
  function appendQuery(path: string, search: URLSearchParams): string {
64
90
  const query = search.toString();
65
91
  return `${path}${query ? `?${query}` : ""}`;
@@ -118,17 +144,80 @@ export function cloudFredSeriesPath(seriesId: string, params: CloudFredSeriesPar
118
144
  return appendQuery(`/cloud/econ/series/${encodeURIComponent(seriesId)}`, search);
119
145
  }
120
146
 
147
+ export function cloudCdsPath(params: CloudCdsParams = {}): string {
148
+ const search = new URLSearchParams();
149
+ const issuer = params.issuer?.trim();
150
+ if (issuer) search.set("issuer", issuer);
151
+ if (params.days != null) search.set("days", String(params.days));
152
+ if (params.limit != null) search.set("limit", String(params.limit));
153
+ return appendQuery("/cloud/credit/cds", search);
154
+ }
155
+
156
+ export type CloudSecFilingsParams = {
157
+ ticker: string;
158
+ limit?: number;
159
+ offset?: number;
160
+ };
161
+
162
+ export type CloudSecFilingParams = {
163
+ cik?: string;
164
+ accession?: string;
165
+ form?: string;
166
+ primaryDocument?: string;
167
+ primaryDocumentUrl?: string;
168
+ filingUrl?: string;
169
+ };
170
+
121
171
  export function cloudCongressHousePath(params: CloudCongressHouseParams = {}): string {
122
172
  const search = new URLSearchParams();
123
173
  if (params.year != null) search.set("year", String(params.year));
124
174
  if (params.limit != null) search.set("limit", String(params.limit));
175
+ if (params.offset != null) search.set("offset", String(params.offset));
125
176
  if (params.filingLimit != null) search.set("filingLimit", String(params.filingLimit));
177
+ if (params.filingOffset != null) search.set("filingOffset", String(params.filingOffset));
126
178
  if (params.member) search.set("member", params.member);
127
179
  if (params.ticker) search.set("ticker", params.ticker);
128
180
  if (params.refresh != null) search.set("refresh", String(params.refresh));
129
181
  return appendQuery("/cloud/congress/house", search);
130
182
  }
131
183
 
184
+ export function cloudSecFilingsPath(params: CloudSecFilingsParams): string {
185
+ const search = new URLSearchParams({ ticker: params.ticker });
186
+ if (params.limit != null) search.set("limit", String(params.limit));
187
+ if (params.offset != null) search.set("offset", String(params.offset));
188
+ return appendQuery("/cloud/sec/filings", search);
189
+ }
190
+
191
+ export function cloudSecFilingDocumentsPath(params: CloudSecFilingParams): string {
192
+ const search = new URLSearchParams();
193
+ if (params.cik) search.set("cik", params.cik);
194
+ if (params.accession) search.set("accession", params.accession);
195
+ if (params.form) search.set("form", params.form);
196
+ if (params.primaryDocument) search.set("primaryDocument", params.primaryDocument);
197
+ if (params.filingUrl) search.set("filingUrl", params.filingUrl);
198
+ return appendQuery("/cloud/sec/filing/documents", search);
199
+ }
200
+
201
+ export function cloudSecFilingContentPath(params: CloudSecFilingParams): string {
202
+ const search = new URLSearchParams();
203
+ if (params.cik) search.set("cik", params.cik);
204
+ if (params.accession) search.set("accession", params.accession);
205
+ if (params.form) search.set("form", params.form);
206
+ if (params.primaryDocument) search.set("primaryDocument", params.primaryDocument);
207
+ if (params.primaryDocumentUrl) search.set("primaryDocumentUrl", params.primaryDocumentUrl);
208
+ if (params.filingUrl) search.set("filingUrl", params.filingUrl);
209
+ return appendQuery("/cloud/sec/filing/content", search);
210
+ }
211
+
212
+ export function cloudSec13FPath(path: string, params: Record<string, string | number | undefined> = {}): string {
213
+ const search = new URLSearchParams();
214
+ for (const [key, value] of Object.entries(params)) {
215
+ if (value !== undefined) search.set(key, String(value));
216
+ }
217
+ const normalized = path.startsWith("/") ? path.slice(1) : path;
218
+ return appendQuery(`/cloud/sec/13f/${normalized}`, search);
219
+ }
220
+
132
221
  export function cloudNewsPath(params: CloudNewsParams = {}): string {
133
222
  const search = new URLSearchParams();
134
223
  if (params.feed) search.set("feed", params.feed);
@@ -24,10 +24,14 @@ export function setCloudApiFetchTransport(transport: CloudApiFetchTransport | nu
24
24
 
25
25
  declare const __GLOOMBERB_API_URL__: string | undefined;
26
26
 
27
- function getCloudApiBaseUrl(): string {
27
+ export function getCloudApiBaseUrl(): string {
28
28
  // Browser bundles have no `process`; the build replaces this with a literal.
29
29
  const bundled = typeof __GLOOMBERB_API_URL__ === "string" ? __GLOOMBERB_API_URL__ : "";
30
- if (bundled) return bundled;
30
+ if (bundled) {
31
+ return typeof location !== "undefined" && bundled === location.origin
32
+ ? `${bundled}/api`
33
+ : bundled;
34
+ }
31
35
  if (typeof process === "undefined") {
32
36
  return DEFAULT_API_URL;
33
37
  }
@@ -43,6 +47,7 @@ export class CloudApiRequestTransport {
43
47
  private sessionToken: string | null = null;
44
48
  private sessionCookieName: SessionCookieName | null = null;
45
49
  private websocketToken: string | null = null;
50
+ private cookieSessionMode = false;
46
51
  private readonly fetchTransport: CloudApiFetchTransport | null;
47
52
  private readonly marketRequestTimeoutMs: number;
48
53
  private readonly connectionHealth: ConnectionHealthRegistry;
@@ -67,6 +72,14 @@ export class CloudApiRequestTransport {
67
72
  return this.websocketToken;
68
73
  }
69
74
 
75
+ hasSessionCredential(): boolean {
76
+ return this.cookieSessionMode || !!this.sessionToken;
77
+ }
78
+
79
+ setCookieSessionMode(enabled: boolean): void {
80
+ this.cookieSessionMode = enabled;
81
+ }
82
+
70
83
  setSessionToken(token: string | null): void {
71
84
  if (this.sessionToken !== token) {
72
85
  this.sessionCookieName = null;
@@ -50,6 +50,7 @@ describe("CloudApiSocket connection health", () => {
50
50
  const socket = new CloudApiSocket({
51
51
  getBaseUrl: () => `http://127.0.0.1:${server.port}`,
52
52
  getSocketAuthToken: () => null,
53
+ hasSessionCredential: () => false,
53
54
  hasVerifiedUser: () => false,
54
55
  isUsingWebSocketToken: () => false,
55
56
  clearWebSocketTokenForFallback: () => false,
@@ -49,6 +49,7 @@ function mergeQuoteStreamSubscriptions(
49
49
  type CloudApiSocketDelegate = {
50
50
  getBaseUrl: () => string;
51
51
  getSocketAuthToken: () => string | null;
52
+ hasSessionCredential: () => boolean;
52
53
  hasVerifiedUser: () => boolean;
53
54
  isUsingWebSocketToken: () => boolean;
54
55
  clearWebSocketTokenForFallback: () => boolean;
@@ -427,7 +428,7 @@ export class CloudApiSocket {
427
428
 
428
429
  private shouldKeepSocketOpen(): boolean {
429
430
  if (this.quoteTargets.size > 0 || this.scannerListeners.size > 0) return true;
430
- return !!this.delegate.getSocketAuthToken()
431
+ return this.delegate.hasSessionCredential()
431
432
  && this.delegate.hasVerifiedUser()
432
433
  && this.channelListeners.size > 0;
433
434
  }
@@ -322,6 +322,40 @@ export interface CloudFredSeriesPayload {
322
322
  info: CloudFredSeriesInfoPayload | null;
323
323
  }
324
324
 
325
+ /** One publicly disseminated single-name CDS transaction report. */
326
+ export interface CloudCdsTradePayload {
327
+ disseminationId: string;
328
+ originalDisseminationId: string | null;
329
+ actionType: string;
330
+ eventTimestamp: string;
331
+ executionTimestamp: string | null;
332
+ effectiveDate: string | null;
333
+ expirationDate: string | null;
334
+ maturityDate: string | null;
335
+ issuerName: string | null;
336
+ underlierId: string | null;
337
+ underlierIdSource: string | null;
338
+ upi: string | null;
339
+ upiFisn: string | null;
340
+ upiUnderlierName: string | null;
341
+ notionalAmount: number | null;
342
+ /** Reported notional is a regulatory cap, so the real trade was at least this size. */
343
+ notionalCapped: boolean;
344
+ notionalCurrency: string | null;
345
+ fixedRate: number | null;
346
+ reportedSpread: number | null;
347
+ /** Unit of `reportedSpread`, e.g. basis points or percent. */
348
+ spreadNotation: string | null;
349
+ upfrontAmount: number | null;
350
+ upfrontCurrency: string | null;
351
+ }
352
+
353
+ export interface CloudCdsResponse {
354
+ source: string;
355
+ asOf: string | null;
356
+ trades: CloudCdsTradePayload[];
357
+ }
358
+
325
359
  export interface CloudShortInterestPointPayload {
326
360
  settlementDate: string;
327
361
  sharesShort: number;
@@ -399,10 +433,66 @@ export interface CloudCongressHousePayload {
399
433
  indexUpdatedAt: string | null;
400
434
  filingsScanned: number;
401
435
  filingCount: number;
436
+ filingOffset?: number;
437
+ hasMore?: boolean;
438
+ hasMoreFilings?: boolean;
439
+ nextOffset?: number;
440
+ nextFilingOffset?: number;
402
441
  trades: CloudCongressTradePayload[];
403
442
  members: CloudCongressMemberPayload[];
404
443
  }
405
444
 
445
+ export interface CloudSecFilingPayload {
446
+ accessionNumber: string;
447
+ form: string;
448
+ filingDate: string;
449
+ acceptedAt?: string;
450
+ primaryDocument?: string;
451
+ primaryDocDescription?: string;
452
+ items?: string;
453
+ cik: string;
454
+ companyName?: string;
455
+ filingUrl: string;
456
+ primaryDocumentUrl?: string;
457
+ }
458
+
459
+ export interface CloudSecDocumentPayload {
460
+ sequence?: string;
461
+ type: string;
462
+ description?: string;
463
+ document: string;
464
+ url: string;
465
+ size?: string;
466
+ isPrimary: boolean;
467
+ }
468
+
469
+ export interface CloudSecFilingsResponse {
470
+ filings: CloudSecFilingPayload[];
471
+ hasMore: boolean;
472
+ nextOffset: number;
473
+ }
474
+
475
+ export interface CloudSecDocumentsResponse {
476
+ documents: CloudSecDocumentPayload[];
477
+ }
478
+
479
+ export interface CloudSecForm4Payload {
480
+ filingDate: string;
481
+ reportedName: string;
482
+ title: string;
483
+ transactionType: "P" | "S" | "A" | "D" | "";
484
+ shares: number;
485
+ pricePerShare: number | null;
486
+ totalValue: number | null;
487
+ sharesOwned: number | null;
488
+ form: string;
489
+ }
490
+
491
+ export interface CloudSecContentResponse {
492
+ content: string | null;
493
+ form4: CloudSecForm4Payload | null;
494
+ }
495
+
406
496
  interface CloudNewsEntityPayload {
407
497
  id: string;
408
498
  entityType: string;
@@ -617,6 +707,79 @@ export interface CloudMarketScreenerPayload {
617
707
  items: CloudMarketScreenerItem[];
618
708
  }
619
709
 
710
+ /** Cache reuse policy for one Equity Diagnostic request. */
711
+ export type CloudEquityDiagnosticMode = "cache-first" | "refresh";
712
+
713
+ export type CloudEquityDiagnosticFindingKind = "red_flag" | "green_flag" | "anomaly";
714
+
715
+ /** 3 is the most severe. */
716
+ export type CloudEquityDiagnosticSeverity = 1 | 2 | 3;
717
+
718
+ export interface CloudEquityDiagnosticFinding {
719
+ id: string;
720
+ kind: CloudEquityDiagnosticFindingKind;
721
+ severity: CloudEquityDiagnosticSeverity;
722
+ confidence: number;
723
+ title: string;
724
+ /** What the data says, with no reading attached. */
725
+ observation: string;
726
+ /** What the observation may mean; kept apart from the observation on purpose. */
727
+ interpretation: string;
728
+ /** Ids into `evidence`; unresolved ids are dropped by the client. */
729
+ evidenceIds: string[];
730
+ }
731
+
732
+ export interface CloudEquityDiagnosticCoverage {
733
+ dataset: string;
734
+ status: "available" | "no_data" | "unsupported" | "failed";
735
+ asOf?: string;
736
+ provider?: string;
737
+ note?: string;
738
+ }
739
+
740
+ /** The server owns citation URLs; the client never builds them. */
741
+ export interface CloudEquityDiagnosticEvidence {
742
+ id: string;
743
+ dataset: string;
744
+ label: string;
745
+ asOf?: string;
746
+ provider?: string;
747
+ url?: string;
748
+ }
749
+
750
+ export interface CloudEquityDiagnosticPending {
751
+ status: "generating";
752
+ retryAfterMs: number;
753
+ }
754
+
755
+ export interface CloudEquityDiagnosticResponse {
756
+ schemaVersion: 1;
757
+ access: "preview" | "full";
758
+ symbol: string;
759
+ exchange: string;
760
+ companyName?: string;
761
+ status: "complete" | "partial" | "insufficient_data";
762
+ verdict: "risk_skewed" | "balanced" | "opportunity_skewed" | "unclear";
763
+ summary: string;
764
+ confidence: number;
765
+ findings: CloudEquityDiagnosticFinding[];
766
+ watchItems: string[];
767
+ coverage: CloudEquityDiagnosticCoverage[];
768
+ evidence: CloudEquityDiagnosticEvidence[];
769
+ generatedAt: string;
770
+ expiresAt: string;
771
+ /** Before this instant a `refresh` request may still answer from cache. */
772
+ refreshAllowedAt: string;
773
+ cached: boolean;
774
+ stale: boolean;
775
+ promptVersion: 1;
776
+ model: "gpt-5.6-luna";
777
+ }
778
+
779
+ export type CloudEquityDiagnosticResult =
780
+ | CloudEquityDiagnosticPending
781
+ | CloudEquityDiagnosticResponse;
782
+
620
783
  export interface CloudVerificationResponse {
621
784
  sent: boolean;
622
785
  email?: string;
@@ -11,6 +11,7 @@ import {
11
11
  import type { PluginRegistry } from "../../plugins/registry";
12
12
  import {
13
13
  getFocusedCollectionId,
14
+ resolveTickerForPane,
14
15
  syncConfigActiveLayoutState,
15
16
  type AppAction,
16
17
  type AppState,
@@ -75,7 +76,9 @@ export function useAppPaneRuntime({
75
76
 
76
77
  const persistLayout = useCallback((layout: LayoutConfig, options?: { pushHistory?: boolean; focusedPaneId?: string | null }) => {
77
78
  const currentState = stateRef.current;
78
- const normalizedLayout = normalizePaneLayout(layout);
79
+ const normalizedLayout = normalizePaneLayout(layout, {
80
+ resolveOrphanSymbol: (instanceId) => resolveTickerForPane(currentState, instanceId),
81
+ });
79
82
  if (options?.pushHistory !== false) {
80
83
  dispatch({ type: "PUSH_LAYOUT_HISTORY" });
81
84
  }