gloomberb 0.15.7 → 0.16.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 (524) hide show
  1. package/README.md +11 -2
  2. package/package.json +1 -1
  3. package/src/api-client/app-attention.ts +147 -0
  4. package/src/api-client/attention.ts +26 -0
  5. package/src/api-client/awards.ts +215 -0
  6. package/src/api-client/catalysts.ts +160 -0
  7. package/src/api-client/chat.ts +137 -14
  8. package/src/api-client/company-kpis.ts +153 -0
  9. package/src/api-client/cpi.ts +52 -0
  10. package/src/api-client/credit-documents.ts +107 -0
  11. package/src/api-client/data.ts +192 -3
  12. package/src/api-client/distress.ts +164 -0
  13. package/src/api-client/doe.ts +75 -0
  14. package/src/api-client/equity-screener.ts +11 -0
  15. package/src/api-client/exposure.ts +202 -0
  16. package/src/api-client/gpu.ts +93 -0
  17. package/src/api-client/hiring.ts +110 -0
  18. package/src/api-client/index.ts +78 -3
  19. package/src/api-client/normalizers.ts +31 -1
  20. package/src/api-client/paths.ts +3 -0
  21. package/src/api-client/perps.ts +127 -0
  22. package/src/api-client/power.ts +148 -0
  23. package/src/api-client/request.ts +58 -1
  24. package/src/api-client/research-activity.ts +172 -13
  25. package/src/api-client/socket.ts +75 -27
  26. package/src/api-client/supply-chain-graph.ts +40 -0
  27. package/src/api-client/supply-chain.ts +98 -0
  28. package/src/api-client/telemetry.ts +29 -0
  29. package/src/api-client/themes.ts +28 -0
  30. package/src/api-client/types.ts +33 -1
  31. package/src/api-client/wall-experiments.ts +100 -0
  32. package/src/api-client/wall-summary.ts +49 -0
  33. package/src/api-client/web-experiments.ts +136 -0
  34. package/src/app/pane-runtime/plugin-bindings.ts +5 -0
  35. package/src/app/pane-runtime/ticker-open-runtime.ts +8 -2
  36. package/src/app/runtime/update.ts +13 -1
  37. package/src/cli/commands/system.ts +2 -0
  38. package/src/cli/desktop-pane-shot.ts +3 -4
  39. package/src/cli/pane-functions/data.ts +78 -0
  40. package/src/cli/pane-functions/financial-ratio-shot.ts +160 -0
  41. package/src/cli/pane-functions/function-help.ts +220 -8
  42. package/src/cli/pane-functions/headless.ts +4 -0
  43. package/src/cli/pane-functions/screenshot-evidence.ts +21 -0
  44. package/src/cli/pane-functions/screenshot.ts +49 -5
  45. package/src/components/chart/composite/composite-chart.tsx +15 -2
  46. package/src/components/chart/composite/format.ts +24 -11
  47. package/src/components/chart/composite/rasterizer.ts +67 -16
  48. package/src/components/chart/composite/scene.ts +53 -2
  49. package/src/components/chart/composite/session-shading.ts +3 -2
  50. package/src/components/chart/composite/text-renderer.ts +52 -1
  51. package/src/components/chart/composite/time-axis.ts +100 -30
  52. package/src/components/chart/composite/tools.ts +2 -1
  53. package/src/components/chart/composite/types.ts +4 -0
  54. package/src/components/chart-table/index.ts +1 -0
  55. package/src/components/chart-table/layout.ts +15 -0
  56. package/src/components/command-bar/action-runtime.ts +1 -0
  57. package/src/components/command-bar/commands/direct/index.ts +32 -0
  58. package/src/components/command-bar/commands/registry.ts +7 -0
  59. package/src/components/command-bar/pane-templates/items.ts +11 -0
  60. package/src/components/command-bar/pane-templates/workflow.ts +19 -4
  61. package/src/components/command-bar/routes/root/command-items.ts +6 -0
  62. package/src/components/command-bar/routes/root/provider-search.ts +2 -0
  63. package/src/components/command-bar/routes/root/results.ts +64 -0
  64. package/src/components/command-bar/routes/root/runtime.ts +8 -0
  65. package/src/components/command-bar/routes/ticker-search/results.ts +89 -24
  66. package/src/components/command-bar/routes/ticker-search/route.ts +2 -0
  67. package/src/components/command-bar/surface/index.tsx +15 -0
  68. package/src/components/command-bar/view-model.ts +3 -0
  69. package/src/components/command-bar/workflow/collection-actions.ts +9 -7
  70. package/src/components/command-bar/workflow/ops.ts +19 -0
  71. package/src/components/command-bar/workflow/tickers.ts +5 -0
  72. package/src/components/company-logo.tsx +15 -2
  73. package/src/components/data-table/table-pane.ts +2 -1
  74. package/src/components/index.ts +1 -0
  75. package/src/components/layout/detached-pane-shell.tsx +12 -1
  76. package/src/components/layout/missing-pane.tsx +87 -23
  77. package/src/components/layout/pane/footer/index.tsx +64 -18
  78. package/src/components/layout/pane/footer/model.ts +85 -0
  79. package/src/components/layout/pane/sidebar.tsx +19 -1
  80. package/src/components/layout/shell/index.tsx +38 -28
  81. package/src/components/layout/shell/pane/management-shortcuts.ts +13 -1
  82. package/src/components/layout/status-bar.tsx +93 -28
  83. package/src/components/layout/status-widget-space.ts +72 -0
  84. package/src/components/onboarding/first-run-workspace.ts +8 -2
  85. package/src/components/onboarding/onboarding-wizard.tsx +35 -16
  86. package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -5
  87. package/src/components/onboarding/wizard-model.ts +13 -5
  88. package/src/components/onboarding/wizard-positions.ts +56 -7
  89. package/src/components/price-performance.tsx +31 -34
  90. package/src/components/ui/data-table/index.tsx +11 -8
  91. package/src/components/ui/display.tsx +15 -6
  92. package/src/components/ui/fields.tsx +5 -0
  93. package/src/components/ui/figure.tsx +94 -0
  94. package/src/components/ui/header-case.ts +25 -3
  95. package/src/components/ui/icon.tsx +3 -1
  96. package/src/components/ui/message-composer.tsx +8 -1
  97. package/src/components/ui/query-bar.tsx +8 -1
  98. package/src/components/ui/ratio-bar.tsx +84 -0
  99. package/src/components/ui/toggle.tsx +3 -1
  100. package/src/components/use-scrollbox-layout.ts +17 -0
  101. package/src/core/state/app/layout.ts +35 -1
  102. package/src/core/state/app/state.ts +15 -0
  103. package/src/core/state/app/types.ts +1 -0
  104. package/src/data/config/store/normalize.ts +24 -0
  105. package/src/i18n/es.ts +2 -0
  106. package/src/i18n/ja.ts +2 -0
  107. package/src/i18n/ko.ts +2 -0
  108. package/src/i18n/zh-cn.ts +2 -0
  109. package/src/i18n/zh-tw.ts +2 -0
  110. package/src/layout-marketplace/pane-imagery.ts +1 -0
  111. package/src/market-data/coordinator/entries.ts +13 -0
  112. package/src/market-data/coordinator/financials.ts +33 -8
  113. package/src/market-data/market/freshness.ts +16 -5
  114. package/src/market-data/market/trading-sessions.ts +65 -1
  115. package/src/market-data/published-cn-sessions.ts +25 -0
  116. package/src/market-data/published-nse-sessions.ts +20 -0
  117. package/src/news/aggregator.ts +4 -0
  118. package/src/plugins/builtin/account-management/model.ts +6 -1
  119. package/src/plugins/builtin/account-management/pane.tsx +36 -25
  120. package/src/plugins/builtin/alerts/catalysts.ts +77 -0
  121. package/src/plugins/builtin/alerts/events-pane.tsx +3 -0
  122. package/src/plugins/builtin/alerts/events.ts +10 -4
  123. package/src/plugins/builtin/alerts/history-pane.tsx +1 -1
  124. package/src/plugins/builtin/alerts/history.ts +1 -0
  125. package/src/plugins/builtin/alerts/index.tsx +6 -0
  126. package/src/plugins/builtin/alerts/research-builder.ts +10 -3
  127. package/src/plugins/builtin/alerts/research-rules.ts +15 -1
  128. package/src/plugins/builtin/analytics/risk-client.ts +15 -3
  129. package/src/plugins/builtin/analytics/risk-headless.ts +123 -18
  130. package/src/plugins/builtin/attention/client.ts +80 -0
  131. package/src/plugins/builtin/attention/evidence.ts +30 -0
  132. package/src/plugins/builtin/attention/headless.ts +33 -0
  133. package/src/plugins/builtin/attention/index.tsx +18 -0
  134. package/src/plugins/builtin/attention/model.ts +59 -0
  135. package/src/plugins/builtin/attention/pane.tsx +211 -0
  136. package/src/plugins/builtin/attention/trending.tsx +59 -0
  137. package/src/plugins/builtin/awards/alerts.ts +32 -0
  138. package/src/plugins/builtin/awards/client.ts +82 -0
  139. package/src/plugins/builtin/awards/detail.tsx +107 -0
  140. package/src/plugins/builtin/awards/evidence.ts +36 -0
  141. package/src/plugins/builtin/awards/headless.ts +55 -0
  142. package/src/plugins/builtin/awards/index.tsx +20 -0
  143. package/src/plugins/builtin/awards/model.ts +42 -0
  144. package/src/plugins/builtin/awards/pane.tsx +263 -0
  145. package/src/plugins/builtin/catalysts/client.ts +28 -0
  146. package/src/plugins/builtin/catalysts/detail.tsx +88 -0
  147. package/src/plugins/builtin/catalysts/evidence.ts +47 -0
  148. package/src/plugins/builtin/catalysts/headless.ts +42 -0
  149. package/src/plugins/builtin/catalysts/history.ts +26 -0
  150. package/src/plugins/builtin/catalysts/index.tsx +29 -0
  151. package/src/plugins/builtin/catalysts/model.ts +135 -0
  152. package/src/plugins/builtin/catalysts/pane.tsx +159 -0
  153. package/src/plugins/builtin/chart-composer/cli-options.ts +4 -0
  154. package/src/plugins/builtin/chart-composer/headless-schema.ts +6 -0
  155. package/src/plugins/builtin/chart-composer/index.tsx +8 -1
  156. package/src/plugins/builtin/chart-composer/pane.tsx +28 -1
  157. package/src/plugins/builtin/chat/attachments/desktop.tsx +610 -0
  158. package/src/plugins/builtin/chat/attachments/files.ts +45 -0
  159. package/src/plugins/builtin/chat/attachments/model.ts +164 -0
  160. package/src/plugins/builtin/chat/attachments/terminal.tsx +72 -0
  161. package/src/plugins/builtin/chat/attachments/transfer.ts +165 -0
  162. package/src/plugins/builtin/chat/content/channel-navigation.ts +13 -12
  163. package/src/plugins/builtin/chat/content/composer-runtime.ts +25 -4
  164. package/src/plugins/builtin/chat/content/composer.tsx +46 -4
  165. package/src/plugins/builtin/chat/content/footer.tsx +46 -8
  166. package/src/plugins/builtin/chat/content/index.tsx +263 -62
  167. package/src/plugins/builtin/chat/content/layout-metrics.ts +10 -2
  168. package/src/plugins/builtin/chat/content/open-targets.ts +17 -3
  169. package/src/plugins/builtin/chat/content/scroll.ts +7 -1
  170. package/src/plugins/builtin/chat/content/shortcuts.ts +36 -11
  171. package/src/plugins/builtin/chat/content/snapshot.ts +3 -0
  172. package/src/plugins/builtin/chat/content/transcript.tsx +17 -1
  173. package/src/plugins/builtin/chat/content/types.ts +7 -0
  174. package/src/plugins/builtin/chat/controller/attachments.ts +213 -0
  175. package/src/plugins/builtin/chat/controller/connections.ts +3 -1
  176. package/src/plugins/builtin/chat/controller/index.ts +114 -2
  177. package/src/plugins/builtin/chat/controller/lifecycle.ts +9 -0
  178. package/src/plugins/builtin/chat/controller/messages.ts +35 -2
  179. package/src/plugins/builtin/chat/controller/send.ts +128 -25
  180. package/src/plugins/builtin/chat/controller/state.ts +12 -1
  181. package/src/plugins/builtin/chat/controller/utils.ts +7 -5
  182. package/src/plugins/builtin/chat/controller/view.ts +2 -0
  183. package/src/plugins/builtin/chat/index.tsx +21 -0
  184. package/src/plugins/builtin/chat/layout.ts +9 -4
  185. package/src/plugins/builtin/chat/message/desktop.tsx +55 -20
  186. package/src/plugins/builtin/chat/message/header.tsx +30 -5
  187. package/src/plugins/builtin/chat/message/inline-tokens.tsx +9 -0
  188. package/src/plugins/builtin/chat/message/pending-send.ts +35 -0
  189. package/src/plugins/builtin/chat/message/profile-popover.tsx +5 -1
  190. package/src/plugins/builtin/chat/message/render-state.ts +12 -2
  191. package/src/plugins/builtin/chat/message/terminal.tsx +51 -4
  192. package/src/plugins/builtin/chat/message/types.ts +6 -1
  193. package/src/plugins/builtin/chat/pane-state.ts +59 -1
  194. package/src/plugins/builtin/chat/profile-popover.ts +50 -1
  195. package/src/plugins/builtin/chat/sidebar.tsx +8 -3
  196. package/src/plugins/builtin/chat/status-widget.tsx +29 -12
  197. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +143 -0
  198. package/src/plugins/builtin/chat/unread-inbox.ts +91 -0
  199. package/src/plugins/builtin/clinical-trials/client.ts +237 -0
  200. package/src/plugins/builtin/clinical-trials/index.tsx +74 -0
  201. package/src/plugins/builtin/clinical-trials/pane.tsx +225 -0
  202. package/src/plugins/builtin/clinical-trials/types.ts +2 -0
  203. package/src/plugins/builtin/cloud/askg/controller.ts +37 -6
  204. package/src/plugins/builtin/cloud/askg/executor.ts +59 -44
  205. package/src/plugins/builtin/cloud/askg/host.ts +8 -1
  206. package/src/plugins/builtin/cloud/askg/manifest.ts +29 -2
  207. package/src/plugins/builtin/cloud/askg/model.ts +47 -13
  208. package/src/plugins/builtin/cloud/askg/notes.ts +100 -0
  209. package/src/plugins/builtin/cloud/askg/pane.tsx +50 -14
  210. package/src/plugins/builtin/cloud/askg/protocol.ts +39 -1
  211. package/src/plugins/builtin/cloud/askg/result-budget.ts +156 -0
  212. package/src/plugins/builtin/cloud/askg/user-data.ts +89 -0
  213. package/src/plugins/builtin/cloud/auth-actions.tsx +55 -31
  214. package/src/plugins/builtin/cloud/plugin.tsx +4 -1
  215. package/src/plugins/builtin/cloud/team/pane-sections.tsx +10 -1
  216. package/src/plugins/builtin/cloud/team/pane.tsx +3 -2
  217. package/src/plugins/builtin/cloud/thesis/board-pane.tsx +2 -2
  218. package/src/plugins/builtin/cloud/thesis/detail.tsx +1 -1
  219. package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -2
  220. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +202 -0
  221. package/src/plugins/builtin/cloud/upgrade-command.ts +1 -1
  222. package/src/plugins/builtin/cloud/upgrade-dialog.tsx +29 -3
  223. package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +1 -1
  224. package/src/plugins/builtin/cloud-tweets/table.tsx +1 -1
  225. package/src/plugins/builtin/comment-letters/client.ts +342 -0
  226. package/src/plugins/builtin/comment-letters/index.tsx +74 -0
  227. package/src/plugins/builtin/comment-letters/pane.tsx +295 -0
  228. package/src/plugins/builtin/comment-letters/types.ts +43 -0
  229. package/src/plugins/builtin/company-attention/app-rank.tsx +79 -0
  230. package/src/plugins/builtin/company-attention/client.ts +69 -0
  231. package/src/plugins/builtin/company-attention/evidence.ts +45 -0
  232. package/src/plugins/builtin/company-attention/headless.ts +47 -0
  233. package/src/plugins/builtin/company-attention/index.tsx +30 -0
  234. package/src/plugins/builtin/company-attention/model.ts +129 -0
  235. package/src/plugins/builtin/company-attention/pane.tsx +183 -0
  236. package/src/plugins/builtin/company-kpis/client.ts +92 -0
  237. package/src/plugins/builtin/company-kpis/headless.ts +32 -0
  238. package/src/plugins/builtin/company-kpis/index.tsx +26 -0
  239. package/src/plugins/builtin/company-kpis/model.ts +124 -0
  240. package/src/plugins/builtin/company-kpis/pane.tsx +226 -0
  241. package/src/plugins/builtin/company-kpis/related.ts +11 -0
  242. package/src/plugins/builtin/company-kpis/screenshot-evidence.ts +44 -0
  243. package/src/plugins/builtin/composite-plugins.ts +34 -0
  244. package/src/plugins/builtin/correlation/history.ts +4 -1
  245. package/src/plugins/builtin/correlation/index.tsx +139 -80
  246. package/src/plugins/builtin/correlation/matrix/pair-chart.tsx +48 -0
  247. package/src/plugins/builtin/correlation/matrix/selection.ts +77 -0
  248. package/src/plugins/builtin/correlation/relationship/model.ts +2 -2
  249. package/src/plugins/builtin/cpi/client.ts +66 -0
  250. package/src/plugins/builtin/cpi/headless.ts +78 -0
  251. package/src/plugins/builtin/cpi/index.tsx +31 -0
  252. package/src/plugins/builtin/cpi/model.ts +198 -0
  253. package/src/plugins/builtin/cpi/pane.tsx +157 -0
  254. package/src/plugins/builtin/credit-documents/client.ts +99 -0
  255. package/src/plugins/builtin/credit-documents/detail.tsx +109 -0
  256. package/src/plugins/builtin/credit-documents/evidence.ts +64 -0
  257. package/src/plugins/builtin/credit-documents/gauge.tsx +55 -0
  258. package/src/plugins/builtin/credit-documents/headless.ts +40 -0
  259. package/src/plugins/builtin/credit-documents/index.tsx +19 -0
  260. package/src/plugins/builtin/credit-documents/model.ts +336 -0
  261. package/src/plugins/builtin/credit-documents/pane.tsx +185 -0
  262. package/src/plugins/builtin/custom-view/view-spec.ts +14 -2
  263. package/src/plugins/builtin/debt-maturities/pane.tsx +1 -0
  264. package/src/plugins/builtin/dividend-yield/pane.tsx +1 -1
  265. package/src/plugins/builtin/doe/client.ts +73 -0
  266. package/src/plugins/builtin/doe/headless.ts +93 -0
  267. package/src/plugins/builtin/doe/index.tsx +41 -0
  268. package/src/plugins/builtin/doe/model.ts +215 -0
  269. package/src/plugins/builtin/doe/pane.tsx +148 -0
  270. package/src/plugins/builtin/doe/range-bar.tsx +45 -0
  271. package/src/plugins/builtin/earnings/board.tsx +3 -0
  272. package/src/plugins/builtin/earnings/index.tsx +2 -0
  273. package/src/plugins/builtin/earnings-calls/pane.tsx +7 -1
  274. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +7 -7
  275. package/src/plugins/builtin/earnings-ripple/client.ts +60 -0
  276. package/src/plugins/builtin/earnings-ripple/headless.ts +35 -0
  277. package/src/plugins/builtin/earnings-ripple/index.tsx +35 -0
  278. package/src/plugins/builtin/earnings-ripple/model.ts +98 -0
  279. package/src/plugins/builtin/earnings-ripple/pane.tsx +103 -0
  280. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  281. package/src/plugins/builtin/equity-screener/criterion-editor.tsx +1 -1
  282. package/src/plugins/builtin/equity-screener/model.ts +74 -9
  283. package/src/plugins/builtin/equity-screener/pane.tsx +2 -0
  284. package/src/plugins/builtin/estimate-revisions/pane.tsx +3 -0
  285. package/src/plugins/builtin/executives/pane.tsx +7 -5
  286. package/src/plugins/builtin/exposure/charts.tsx +66 -0
  287. package/src/plugins/builtin/exposure/client.ts +53 -0
  288. package/src/plugins/builtin/exposure/drivers.ts +29 -0
  289. package/src/plugins/builtin/exposure/editor.tsx +61 -0
  290. package/src/plugins/builtin/exposure/evidence.ts +39 -0
  291. package/src/plugins/builtin/exposure/headless.ts +51 -0
  292. package/src/plugins/builtin/exposure/holdings.ts +56 -0
  293. package/src/plugins/builtin/exposure/index.tsx +13 -0
  294. package/src/plugins/builtin/exposure/model.ts +104 -0
  295. package/src/plugins/builtin/exposure/pane.tsx +247 -0
  296. package/src/plugins/builtin/fear-greed/charts.tsx +1 -1
  297. package/src/plugins/builtin/fear-greed/pane.tsx +2 -9
  298. package/src/plugins/builtin/filing-events/pane.tsx +2 -1
  299. package/src/plugins/builtin/gpu/board.tsx +163 -0
  300. package/src/plugins/builtin/gpu/changes.tsx +94 -0
  301. package/src/plugins/builtin/gpu/client.ts +130 -0
  302. package/src/plugins/builtin/gpu/equities.tsx +112 -0
  303. package/src/plugins/builtin/gpu/headless.ts +67 -0
  304. package/src/plugins/builtin/gpu/history.tsx +140 -0
  305. package/src/plugins/builtin/gpu/index.tsx +18 -0
  306. package/src/plugins/builtin/gpu/ladder.tsx +127 -0
  307. package/src/plugins/builtin/gpu/model.ts +317 -0
  308. package/src/plugins/builtin/gpu/pane.tsx +63 -0
  309. package/src/plugins/builtin/help/index.tsx +19 -2
  310. package/src/plugins/builtin/holders/pane.tsx +5 -9
  311. package/src/plugins/builtin/insider/detail.tsx +119 -0
  312. package/src/plugins/builtin/insider/display.ts +221 -0
  313. package/src/plugins/builtin/insider/headless.ts +24 -3
  314. package/src/plugins/builtin/insider/index.tsx +206 -108
  315. package/src/plugins/builtin/insider/insider-data.ts +31 -3
  316. package/src/plugins/builtin/insider/model.ts +53 -4
  317. package/src/plugins/builtin/insider/table-model.ts +226 -0
  318. package/src/plugins/builtin/insider/table.tsx +79 -0
  319. package/src/plugins/builtin/iv-history/screen-pane.tsx +1 -1
  320. package/src/plugins/builtin/jobs/pane.tsx +14 -7
  321. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  322. package/src/plugins/builtin/macro-day/client.ts +50 -0
  323. package/src/plugins/builtin/macro-day/headless.ts +51 -0
  324. package/src/plugins/builtin/macro-day/index.tsx +21 -0
  325. package/src/plugins/builtin/macro-day/model.ts +137 -0
  326. package/src/plugins/builtin/macro-day/pane.tsx +118 -0
  327. package/src/plugins/builtin/macro-day/releases.ts +58 -0
  328. package/src/plugins/builtin/market-halts/model.ts +30 -17
  329. package/src/plugins/builtin/market-halts/pane.tsx +15 -21
  330. package/src/plugins/builtin/market-heatmap/data.ts +1 -1
  331. package/src/plugins/builtin/market-heatmap/index.tsx +198 -50
  332. package/src/plugins/builtin/market-heatmap/portfolio.ts +220 -0
  333. package/src/plugins/builtin/market-movers/index.tsx +1 -0
  334. package/src/plugins/builtin/market-movers/session-body.tsx +5 -2
  335. package/src/plugins/builtin/mna/distress/client.ts +326 -0
  336. package/src/plugins/builtin/mna/distress/detail.tsx +247 -0
  337. package/src/plugins/builtin/mna/distress/headless.ts +257 -0
  338. package/src/plugins/builtin/mna/distress/model.ts +427 -0
  339. package/src/plugins/builtin/mna/distress/pane.tsx +606 -0
  340. package/src/plugins/builtin/mna/index.tsx +15 -0
  341. package/src/plugins/builtin/mna/pane.tsx +42 -1
  342. package/src/plugins/builtin/news/index.tsx +62 -16
  343. package/src/plugins/builtin/news/wire/filter-articles.ts +54 -0
  344. package/src/plugins/builtin/news/wire/index.tsx +37 -3
  345. package/src/plugins/builtin/news/wire/industry-pane.tsx +29 -13
  346. package/src/plugins/builtin/news/wire/mutes.ts +253 -0
  347. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +117 -0
  348. package/src/plugins/builtin/news/wire/news/footer.ts +4 -0
  349. package/src/plugins/builtin/news/wire/news/list-search.ts +34 -0
  350. package/src/plugins/builtin/news/wire/news/pop-out.tsx +158 -0
  351. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +64 -19
  352. package/src/plugins/builtin/openfda/client.ts +300 -0
  353. package/src/plugins/builtin/openfda/index.tsx +71 -0
  354. package/src/plugins/builtin/openfda/pane.tsx +382 -0
  355. package/src/plugins/builtin/openfda/types.ts +76 -0
  356. package/src/plugins/builtin/options/footer.ts +1 -0
  357. package/src/plugins/builtin/options/view.tsx +1 -1
  358. package/src/plugins/builtin/options-calculator/pane.tsx +0 -1
  359. package/src/plugins/builtin/options-positioning/index.tsx +4 -2
  360. package/src/plugins/builtin/options-positioning/pane.tsx +1 -1
  361. package/src/plugins/builtin/pe-band/client.ts +61 -0
  362. package/src/plugins/builtin/pe-band/headless.ts +51 -0
  363. package/src/plugins/builtin/pe-band/index.tsx +20 -0
  364. package/src/plugins/builtin/pe-band/model.ts +267 -0
  365. package/src/plugins/builtin/pe-band/pane.tsx +159 -0
  366. package/src/plugins/builtin/perps/client.ts +66 -0
  367. package/src/plugins/builtin/perps/equity-row.tsx +28 -0
  368. package/src/plugins/builtin/perps/headless.ts +34 -0
  369. package/src/plugins/builtin/perps/index.tsx +14 -0
  370. package/src/plugins/builtin/perps/model.ts +58 -0
  371. package/src/plugins/builtin/perps/pane.tsx +185 -0
  372. package/src/plugins/builtin/plugin-marketplace/pane.tsx +0 -1
  373. package/src/plugins/builtin/portfolio-list/cli/render.ts +177 -87
  374. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +6 -20
  375. package/src/plugins/builtin/portfolio-list/command-bar.ts +6 -2
  376. package/src/plugins/builtin/portfolio-list/headless.ts +237 -0
  377. package/src/plugins/builtin/portfolio-list/index.tsx +2 -0
  378. package/src/plugins/builtin/portfolio-list/mutations.ts +70 -1
  379. package/src/plugins/builtin/portfolio-list/pane/data.ts +17 -0
  380. package/src/plugins/builtin/portfolio-list/pane/index.tsx +88 -5
  381. package/src/plugins/builtin/portfolio-list/pane/remove-ticker.ts +85 -0
  382. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +18 -12
  383. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +6 -0
  384. package/src/plugins/builtin/power/client.ts +100 -0
  385. package/src/plugins/builtin/power/detail.tsx +82 -0
  386. package/src/plugins/builtin/power/evidence.ts +56 -0
  387. package/src/plugins/builtin/power/generation.tsx +37 -0
  388. package/src/plugins/builtin/power/headless.ts +43 -0
  389. package/src/plugins/builtin/power/index.tsx +16 -0
  390. package/src/plugins/builtin/power/model.ts +89 -0
  391. package/src/plugins/builtin/power/pane.tsx +209 -0
  392. package/src/plugins/builtin/power/query.ts +25 -0
  393. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -3
  394. package/src/plugins/builtin/research/analyst-pane.tsx +1 -1
  395. package/src/plugins/builtin/research/corporate-actions-pane.tsx +13 -4
  396. package/src/plugins/builtin/research/equity-diagnostic-pane.tsx +11 -9
  397. package/src/plugins/builtin/research-search/pane.tsx +21 -7
  398. package/src/plugins/builtin/revenue-breakdown/pane.tsx +1 -1
  399. package/src/plugins/builtin/reverse-dcf/client.ts +35 -0
  400. package/src/plugins/builtin/reverse-dcf/headless.ts +44 -0
  401. package/src/plugins/builtin/reverse-dcf/index.tsx +20 -0
  402. package/src/plugins/builtin/reverse-dcf/model.ts +87 -0
  403. package/src/plugins/builtin/reverse-dcf/pane.tsx +107 -0
  404. package/src/plugins/builtin/risk-factors/pane.tsx +6 -5
  405. package/src/plugins/builtin/scanner/denied.tsx +4 -2
  406. package/src/plugins/builtin/scanner/feed.ts +1 -0
  407. package/src/plugins/builtin/scanner/flow-pane.tsx +3 -2
  408. package/src/plugins/builtin/scanner/hilo-pane.tsx +1 -1
  409. package/src/plugins/builtin/seasonality/client.ts +4 -1
  410. package/src/plugins/builtin/sec/command-bar-search.ts +217 -0
  411. package/src/plugins/builtin/sec/forms.ts +54 -0
  412. package/src/plugins/builtin/sec/headless.ts +31 -5
  413. package/src/plugins/builtin/sec/index.tsx +67 -11
  414. package/src/plugins/builtin/sec/model.ts +35 -5
  415. package/src/plugins/builtin/sectors/headless.ts +1 -0
  416. package/src/plugins/builtin/sectors/index.tsx +72 -26
  417. package/src/plugins/builtin/shared/cloud-upgrade.ts +143 -39
  418. package/src/plugins/builtin/shared/pro-feature-wall.tsx +8 -4
  419. package/src/plugins/builtin/shared/research-cells.tsx +107 -0
  420. package/src/plugins/builtin/shared/series-list-detail.tsx +5 -1
  421. package/src/plugins/builtin/shared/ticker-request.ts +1 -1
  422. package/src/plugins/builtin/shared/wall-teaser-catalog.ts +80 -0
  423. package/src/plugins/builtin/shared/wall-teaser.tsx +192 -0
  424. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +43 -0
  425. package/src/plugins/builtin/short-interest/index.tsx +31 -0
  426. package/src/plugins/builtin/short-interest/watch-client.ts +70 -0
  427. package/src/plugins/builtin/short-interest/watch-headless.ts +49 -0
  428. package/src/plugins/builtin/short-interest/watch-model.ts +185 -0
  429. package/src/plugins/builtin/short-interest/watch-pane.tsx +169 -0
  430. package/src/plugins/builtin/short-volume/pane.tsx +1 -1
  431. package/src/plugins/builtin/social-mentions/pane.tsx +1 -1
  432. package/src/plugins/builtin/supply-chain/client.ts +104 -0
  433. package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +17 -0
  434. package/src/plugins/builtin/supply-chain/evidence.ts +88 -0
  435. package/src/plugins/builtin/supply-chain/flow-layout.ts +133 -0
  436. package/src/plugins/builtin/supply-chain/flow.tsx +323 -0
  437. package/src/plugins/builtin/supply-chain/graph-client.ts +83 -0
  438. package/src/plugins/builtin/supply-chain/graph-evidence.ts +25 -0
  439. package/src/plugins/builtin/supply-chain/graph-model.ts +45 -0
  440. package/src/plugins/builtin/supply-chain/graph-pane.tsx +218 -0
  441. package/src/plugins/builtin/supply-chain/graph.tsx +163 -0
  442. package/src/plugins/builtin/supply-chain/headless.ts +105 -0
  443. package/src/plugins/builtin/supply-chain/index.tsx +17 -0
  444. package/src/plugins/builtin/supply-chain/model.ts +145 -0
  445. package/src/plugins/builtin/supply-chain/pane.tsx +276 -0
  446. package/src/plugins/builtin/supply-chain/row-evidence.tsx +96 -0
  447. package/src/plugins/builtin/supply-chain/trust.ts +34 -0
  448. package/src/plugins/builtin/themes/client.ts +39 -0
  449. package/src/plugins/builtin/themes/headless.ts +38 -0
  450. package/src/plugins/builtin/themes/model.ts +92 -0
  451. package/src/plugins/builtin/themes/pane.tsx +125 -0
  452. package/src/plugins/builtin/thirteenf/pane.tsx +6 -15
  453. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +5 -6
  454. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +201 -0
  455. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +327 -0
  456. package/src/plugins/builtin/ticker-detail/financials/period-end-history.ts +5 -2
  457. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +60 -12
  458. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +2 -1
  459. package/src/plugins/builtin/ticker-detail/headless.ts +7 -2
  460. package/src/plugins/builtin/ticker-detail/index.tsx +22 -0
  461. package/src/plugins/builtin/ticker-detail/overview/components.tsx +53 -30
  462. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +2 -0
  463. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +112 -47
  464. package/src/plugins/builtin/ticker-detail/pane.tsx +50 -6
  465. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +21 -42
  466. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +3 -0
  467. package/src/plugins/builtin/time-sales/pane.tsx +1 -1
  468. package/src/plugins/builtin/world-indices/indices.ts +1 -0
  469. package/src/plugins/catalog-browser.ts +40 -0
  470. package/src/plugins/catalog-manifest.ts +3 -0
  471. package/src/plugins/catalog-ui.ts +6 -0
  472. package/src/plugins/current-target.ts +0 -9
  473. package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
  474. package/src/renderers/browser/cloud-transport.ts +66 -1
  475. package/src/renderers/browser/main.tsx +2 -0
  476. package/src/renderers/dom/data-table/row.tsx +2 -0
  477. package/src/renderers/dom/desktop/controls.tsx +38 -25
  478. package/src/renderers/dom/desktop/icons.tsx +8 -0
  479. package/src/renderers/dom/host/chart-surface.tsx +36 -2
  480. package/src/renderers/dom/host/focus-scope.ts +11 -1
  481. package/src/renderers/dom/styles.css +28 -0
  482. package/src/renderers/electrobun/view/backend-rpc.ts +3 -2
  483. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  484. package/src/renderers/electrobun/view/http-fetch.ts +2 -0
  485. package/src/renderers/electrobun/view/main.tsx +3 -1
  486. package/src/renderers/electrobun/view/rpc-timeout.ts +59 -0
  487. package/src/renderers/opentui/start.tsx +3 -0
  488. package/src/shares/api.ts +3 -1
  489. package/src/sources/gloomberb-cloud/index.ts +47 -13
  490. package/src/sources/history-result.ts +1 -0
  491. package/src/sources/history-retention.ts +16 -0
  492. package/src/sources/provider-router/history.ts +4 -2
  493. package/src/sources/sec-edgar/identity.ts +36 -0
  494. package/src/sources/sec-edgar.ts +1 -34
  495. package/src/state/app/context/index.tsx +8 -0
  496. package/src/sync/core-contributors.ts +20 -2
  497. package/src/telemetry/attention-counts.ts +164 -0
  498. package/src/telemetry/live-config.ts +7 -0
  499. package/src/test-support/rpc-loopback.ts +7 -3
  500. package/src/test-support/shell-pane-keys.tsx +34 -0
  501. package/src/tickers/instrument-kind.ts +12 -6
  502. package/src/tickers/open-target.ts +1 -1
  503. package/src/tickers/search/asset-classes.ts +91 -0
  504. package/src/tickers/search/index.ts +62 -15
  505. package/src/tickers/search/ranking.ts +22 -3
  506. package/src/tickers/search/upsert.ts +4 -1
  507. package/src/time-series/live-bars.ts +8 -3
  508. package/src/time-series/resolve.ts +224 -17
  509. package/src/time-series/spec.ts +1 -0
  510. package/src/time-series/types.ts +9 -1
  511. package/src/types/config.ts +23 -1
  512. package/src/types/data-provider.ts +2 -0
  513. package/src/types/headless.ts +17 -0
  514. package/src/types/price-history.ts +2 -0
  515. package/src/ui/host.tsx +6 -0
  516. package/src/utils/double-escape-close.ts +20 -0
  517. package/src/utils/exchanges.ts +1 -0
  518. package/src/utils/html-entities.ts +15 -0
  519. package/src/utils/http-proxy-response.ts +44 -7
  520. package/src/utils/plugin-proxy-hosts.json +3 -0
  521. package/src/utils/price-history.ts +35 -1
  522. package/src/utils/sec.ts +21 -2
  523. package/src/utils/text-wrap.ts +13 -8
  524. package/src/utils/zoned-date-time.ts +67 -0
@@ -0,0 +1,127 @@
1
+ const ASSET_CLASSES = ["stocks", "indices", "energy", "metals", "fx", "crypto", "other"] as const
2
+ type AssetClass = (typeof ASSET_CLASSES)[number]
3
+ type PerpVenue = "hyperliquid" | "binance" | "bybit" | "okx" | "deribit" | "coinbase" | "kraken" | "dydx"
4
+
5
+ /** Rates and premiums are fractions, timestamps UTC; null always means unavailable. */
6
+ interface PerpMarket {
7
+ marketId: string
8
+ venue: PerpVenue
9
+ dex: string
10
+ symbol: string
11
+ baseAsset: string
12
+ displayName: string
13
+ assetClass: AssetClass
14
+ underlyingSymbol: string | null
15
+ underlyingCurrency: string | null
16
+ quoteCurrency: string
17
+ marginCurrency: string
18
+ contractType: "linear" | "inverse" | "quanto"
19
+ priceMultiplier: number
20
+ markPrice: number | null
21
+ oraclePrice: number | null
22
+ lastPrice: number | null
23
+ previousDayPrice: number | null
24
+ priceChange24h: number | null
25
+ premium: number | null
26
+ venuePremium: number | null
27
+ fundingRate: number | null
28
+ fundingIntervalHours: number | null
29
+ fundingKind: "current" | "last-paid" | "continuous"
30
+ fundingRate8h: number | null
31
+ fundingApr: number | null
32
+ predictedFundingRate: number | null
33
+ predictedFundingIntervalHours: number | null
34
+ nextFundingAt: string | null
35
+ openInterestBase: number | null
36
+ openInterestUsd: number | null
37
+ volume24hUsd: number | null
38
+ maxLeverage: number | null
39
+ isolatedOnly: boolean | null
40
+ delisted: boolean
41
+ observedAt: string
42
+ sourceAsOf: string | null
43
+ sourceUrl: string
44
+ confidence: "high" | "medium" | "low"
45
+ qualityFlags: string[]
46
+ }
47
+
48
+ interface PerpFunding {
49
+ marketId: string
50
+ time: string
51
+ rate: number
52
+ intervalHours: number
53
+ premium: number | null
54
+ observedAt: string
55
+ sourceUrl: string
56
+ }
57
+
58
+ interface PerpCandle {
59
+ marketId: string
60
+ time: string
61
+ interval: "1h"
62
+ open: number
63
+ high: number
64
+ low: number
65
+ close: number
66
+ volumeBase: number | null
67
+ trades: number | null
68
+ observedAt: string
69
+ sourceUrl: string
70
+ }
71
+
72
+ interface UnderlyingReference {
73
+ symbol: string
74
+ price: number
75
+ currency: string
76
+ asOf: string
77
+ marketState: string | null
78
+ sourceUrl: string
79
+ }
80
+
81
+ export interface PerpBoardRow extends PerpMarket {
82
+ stale: boolean
83
+ oiChange1h: number | null
84
+ oiChange24h: number | null
85
+ oiUsdChange1h: number | null
86
+ oiUsdChange24h: number | null
87
+ underlying: UnderlyingReference | null
88
+ underlyingPremium: number | null
89
+ closedMarketPremium: number | null
90
+ }
91
+
92
+ type PerpHistoryPoint = {
93
+ time: string
94
+ resolution: "minute" | "hour" | "day"
95
+ markPrice: number | null
96
+ oraclePrice: number | null
97
+ premium: number | null
98
+ fundingRate: number | null
99
+ fundingIntervalHours: number | null
100
+ openInterestBase: number | null
101
+ openInterestUsd: number | null
102
+ sampleCount: number
103
+ firstObservedAt: string
104
+ lastObservedAt: string
105
+ }
106
+
107
+ export interface PerpBoardPayload {
108
+ status: string; asOf: string | null; rows: PerpBoardRow[]; total: number; locked: number;
109
+ access: "pro" | "preview"; sources?: unknown[];
110
+ }
111
+ export interface PerpHistoryPayload {
112
+ status: string; marketId: string; rows: PerpHistoryPoint[]; funding: PerpFunding[]; candles: PerpCandle[];
113
+ locked: boolean; access: "pro" | "preview"; asOf: string | null;
114
+ from?: string; to?: string; resolution?: "minute" | "hour" | "day"; truncated?: boolean;
115
+ }
116
+ export interface PerpRankingsPayload {
117
+ status: string; asOf: string | null; access: "pro" | "preview"; locked: boolean;
118
+ fundingPositive: PerpBoardRow[]; fundingNegative: PerpBoardRow[]; oiSurges: PerpBoardRow[];
119
+ premiumDislocations: PerpBoardRow[]; closedMarketDislocations: PerpBoardRow[];
120
+ }
121
+ export type PerpBoardQuery = { assetClass?: string; search?: string; sort?: string; includeDelisted?: boolean };
122
+ export type PerpHistoryQuery = { marketId: string; from?: string; to?: string; resolution?: "auto" | "minute" | "hour" | "day"; limit?: number };
123
+
124
+ export interface PerpMarketPayload extends PerpBoardPayload {
125
+ evidence: { kind: string; period_at: string; received_at: string; superseded_at: string | null; fingerprint: string; payload: PerpMarket | PerpFunding | PerpCandle }[];
126
+ methodologyUrl: string;
127
+ }
@@ -0,0 +1,148 @@
1
+ /** Power quantities are MW unless explicitly qualified in metrics; dates retain source precision. */
2
+ type PowerKind = "queue" | "load" | "capacity" | "utility"
3
+ type PowerStatus = "active" | "completed" | "withdrawn" | "approved" | "operating" | "unknown"
4
+ type PowerFuel = "solar" | "wind" | "storage" | "gas" | "nuclear" | "hydro" | "coal" | "hybrid" | "other" | "load" | "unknown"
5
+ interface PowerRecord {
6
+ id: string
7
+ sourceId: string
8
+ sourceProjectId: string
9
+ kind: PowerKind
10
+ name: string
11
+ country: string
12
+ region: string
13
+ state: string | null
14
+ county: string | null
15
+ zone: string | null
16
+ developer: string | null
17
+ utility: string | null
18
+ fuel: PowerFuel
19
+ technology: string | null
20
+ status: PowerStatus
21
+ statusRaw: string | null
22
+ capacityMw: number | null
23
+ requestedDate: string | null
24
+ proposedDate: string | null
25
+ completedDate: string | null
26
+ withdrawnDate: string | null
27
+ period: string | null
28
+ asOf: string | null
29
+ observedAt: string
30
+ sourceUrl: string
31
+ sourceLocator: string
32
+ confidence: number
33
+ /** Raw source cells or exact source excerpt. Never manufactured prose presented as a quote. */
34
+ evidence: Record<string, unknown>
35
+ /** Optional native facts e.g. generationMwh, customers, salesMwh, loadClass. */
36
+ metrics: Record<string, string | number | boolean | null>
37
+ /** Historical benchmarks never add to current direct-source queue totals. */
38
+ historical: boolean
39
+ }
40
+ interface PowerEntityLink {
41
+ name: string
42
+ entityId: string
43
+ tickers: { ticker: string; exchange?: string }[]
44
+ confidence: number
45
+ role: "developer" | "utility"
46
+ }
47
+ export interface PowerProject extends PowerRecord {
48
+ snapshotId: string
49
+ revision: number
50
+ entities: PowerEntityLink[]
51
+ }
52
+ export interface PowerFilter {
53
+ sort?: "capacityMw" | "name" | "region" | "status" | "proposedDate" | "generationMwh" | "salesMwh" | "revenueUsd" | "summerPeakDemandMw" | "winterPeakDemandMw"
54
+ direction?: "asc" | "desc"
55
+ country?: string
56
+ region?: string
57
+ fuel?: string
58
+ status?: string
59
+ kind?: string
60
+ symbol?: string
61
+ search?: string
62
+ loadClass?: string
63
+ sourceId?: string
64
+ historical?: boolean
65
+ offset?: number
66
+ limit?: number
67
+ from?: string
68
+ to?: string
69
+ }
70
+ export interface PowerAggregate {
71
+ sourceId: string
72
+ country: string
73
+ region: string
74
+ fuel: string
75
+ status: string
76
+ kind: string
77
+ projects: number
78
+ capacityMw: number
79
+ unknownCapacity: number
80
+ }
81
+ export interface PowerHistoryPoint extends PowerAggregate {
82
+ basis: "observed" | "published"
83
+ sourceUrls: string[]
84
+ sourceId: string
85
+ observedAt: string
86
+ asOf: string | null
87
+ period: string | null
88
+ historical: boolean
89
+ }
90
+ export interface PowerCoverage {
91
+ id: string
92
+ country: string
93
+ region: string
94
+ kind: PowerKind
95
+ role: "direct" | "benchmark" | "context"
96
+ status: "current" | "stale" | "pending" | "failed" | "disabled"
97
+ reason: string | null
98
+ sourceUrl: string
99
+ asOf: string | null
100
+ observedAt: string | null
101
+ records: number
102
+ nextPollAt: string | null
103
+ }
104
+ export interface PowerRates {
105
+ sourceId: string
106
+ region: string
107
+ country: string
108
+ cohort: string
109
+ projects: number
110
+ completed: number
111
+ withdrawn: number
112
+ active: number
113
+ unknown: number
114
+ completionRate: number | null
115
+ withdrawalRate: number | null
116
+ }
117
+ export interface PowerExposure {
118
+ sourceId: string
119
+ sourceUrls: string[]
120
+ asOf: string | null
121
+ observedAt: string
122
+ utility: string
123
+ country: string
124
+ region: string
125
+ requestedMw: number
126
+ approvedMw: number
127
+ operatingMw: number
128
+ requests: number
129
+ unknownCapacity: number
130
+ entities: PowerEntityLink[]
131
+ }
132
+ export interface PowerBoard {
133
+ generatedAt: string
134
+ access: "full" | "preview"
135
+ projects: PowerProject[]
136
+ total: number
137
+ hasMore: boolean
138
+ nextOffset: number | null
139
+ aggregates: PowerAggregate[]
140
+ rates: PowerRates[]
141
+ exposure: PowerExposure[]
142
+ coverage: PowerCoverage[]
143
+ filters: { countries: string[]; regions: string[]; fuels: string[]; statuses: string[] }
144
+ locked: { projects: number; aggregates: number; rates: number; exposure: number }
145
+ }
146
+
147
+ export interface PowerHistory { generatedAt: string; access: "full" | "preview"; points: PowerHistoryPoint[]; locked: number; hasMore: boolean; nextOffset: number | null }
148
+ export interface PowerDetail { generatedAt: string; access: "full" | "preview"; project: PowerProject | null; revisions: PowerProject[]; totalRevisions: number; hasMore: boolean; nextOffset: number | null; locked: number }
@@ -53,7 +53,22 @@ export interface CloudApiFetchTransportOptions {
53
53
  streamFetch?: CloudApiStreamFetch;
54
54
  }
55
55
 
56
+ /**
57
+ * One upload with its bytes and the headers the client built. A transport that
58
+ * can watch the body go out (a browser XMLHttpRequest) reports `onProgress`
59
+ * with the fraction sent; others never call it.
60
+ */
61
+ export interface CloudApiUploadRequest {
62
+ url: string;
63
+ body: Blob | Uint8Array;
64
+ headers: Headers;
65
+ signal?: AbortSignal;
66
+ onProgress?: (fraction: number) => void;
67
+ }
68
+ type CloudApiUploadTransport = (request: CloudApiUploadRequest) => Promise<CloudApiResponse>;
69
+
56
70
  let cloudApiFetchTransport: CloudApiFetchTransport = httpFetch;
71
+ let cloudApiUploadTransport: CloudApiUploadTransport | null = null;
57
72
  let cloudApiTransportInstalled = false;
58
73
  let cloudApiFetchStreaming = true;
59
74
  let cloudApiStreamFetch: CloudApiStreamFetch | null = null;
@@ -68,6 +83,11 @@ export function setCloudApiFetchTransport(
68
83
  cloudApiStreamFetch = transport ? options.streamFetch ?? null : null;
69
84
  }
70
85
 
86
+ /** Installs the transport uploads go through; without one they use the fetch transport. */
87
+ export function setCloudApiUploadTransport(transport: CloudApiUploadTransport | null): void {
88
+ cloudApiUploadTransport = transport;
89
+ }
90
+
71
91
  /**
72
92
  * The fetch to use for a response that must be read while it arrives, or null
73
93
  * when the installed transport buffers whole responses.
@@ -292,10 +312,47 @@ export class CloudApiRequestTransport {
292
312
  }
293
313
  }
294
314
 
315
+ /**
316
+ * POSTs raw bytes, such as an image, and parses the JSON answer like
317
+ * `request`. Progress is reported where the installed upload transport can
318
+ * see the body go out.
319
+ */
320
+ async upload<T>(
321
+ path: string,
322
+ body: Blob | Uint8Array,
323
+ options: {
324
+ contentType: string;
325
+ headers?: HeadersInit;
326
+ signal?: AbortSignal;
327
+ onProgress?: (fraction: number) => void;
328
+ },
329
+ ): Promise<T> {
330
+ const headers = new Headers(options.headers);
331
+ headers.set("Content-Type", options.contentType);
332
+ const uploadTransport = this.fetchTransport ? null : cloudApiUploadTransport;
333
+ const send: CloudApiFetchTransport | undefined = uploadTransport
334
+ ? (url, init) => uploadTransport({
335
+ url,
336
+ body,
337
+ headers: new Headers(init?.headers),
338
+ signal: options.signal,
339
+ onProgress: options.onProgress,
340
+ })
341
+ : undefined;
342
+ return this.performRequest<T>(path, {
343
+ method: "POST",
344
+ headers,
345
+ // Typed arrays are valid fetch bodies; RequestInit's DOM typing lags behind.
346
+ body: body as BodyInit,
347
+ signal: options.signal,
348
+ }, () => true, send);
349
+ }
350
+
295
351
  private async performRequest<T>(
296
352
  path: string,
297
353
  options: RequestInit | undefined,
298
354
  canApplySession: () => boolean,
355
+ send?: CloudApiFetchTransport,
299
356
  ): Promise<T> {
300
357
  throwIfRequestAborted(options?.signal);
301
358
  const headers = new Headers(options?.headers);
@@ -312,7 +369,7 @@ export class CloudApiRequestTransport {
312
369
 
313
370
  const operation = `${options?.method ?? "GET"} ${path.split("?")[0]}`;
314
371
  const request = async () => {
315
- const res = await (this.fetchTransport ?? cloudApiFetchTransport)(
372
+ const res = await (send ?? this.fetchTransport ?? cloudApiFetchTransport)(
316
373
  `${this.baseUrl}${path}`,
317
374
  {
318
375
  ...options,
@@ -1,6 +1,10 @@
1
1
  import { apiClient } from "./index";
2
2
  import { getCurrentPluginTarget } from "../plugins/current-target";
3
3
  import type { DesktopDeepLinkBridge } from "../types/desktop-deeplink";
4
+ import { exposeExperiment, storedExperimentAssignments } from "./web-experiments";
5
+ import { createWallExperimentSession, type WallExperiment, type WallExperimentContext, type WallTeaserVariant } from "./wall-experiments";
6
+ import { hasProAccess } from "./plan-rules";
7
+ import { withDeadline } from "../utils/async-deadline";
4
8
 
5
9
  export type ResearchActivity =
6
10
  | "workspace_opened"
@@ -10,6 +14,8 @@ export type ResearchActivity =
10
14
  | "ticker_saved"
11
15
  | "pro_feature_used"
12
16
  | "upgrade_intent"
17
+ | "wall_viewed"
18
+ | "wall_cta_clicked"
13
19
  // First-run funnel, one per step reached. Keep in sync with the server's list.
14
20
  | "onboarding_started"
15
21
  | "onboarding_position_added"
@@ -30,6 +36,9 @@ export type ResearchFeature =
30
36
  const sent = new Set<string>();
31
37
  let anonymousId: string | undefined;
32
38
  let attribution: Record<string, string> = {};
39
+ const wallExperiment = createWallExperimentSession();
40
+ const signinWallExperiment = createWallExperimentSession("wall_teaser_signin");
41
+ const WALL_VIEWS_SESSION_KEY = "gloomberb.wall-teaser.views";
33
42
 
34
43
  const ATTRIBUTION_STORAGE_KEY = "gloomberb.web.attribution";
35
44
  const ANONYMOUS_ID_STORAGE_KEY = "gloomberb.web.anonymous-id";
@@ -38,8 +47,8 @@ const ATTRIBUTION_WINDOW_MS = 30 * 24 * 60 * 60 * 1000;
38
47
  /** Another device's clock may run slightly ahead; a touch from a minute in the future is still fresh. */
39
48
  const CLOCK_SKEW_MS = 5 * 60 * 1000;
40
49
  const ANONYMOUS_ID = /^[a-f0-9-]{36}$/;
41
- /** The server accepts a tab id only in this shape. */
42
- const RESEARCH_TAB_ID = /^[a-z0-9][a-z0-9-]{0,39}$/;
50
+ /** The server accepts a tab id or an upgrade placement only in this shape. */
51
+ const ACTIVITY_ID = /^[a-z0-9][a-z0-9-]{0,39}$/;
43
52
  const CAMPAIGN_KEYS = [
44
53
  "utm_source",
45
54
  "utm_medium",
@@ -310,29 +319,67 @@ export function observeDesktopDeepLinks(
310
319
  /** What the server stores against the account: stored touches plus the product marker. */
311
320
  function attributionPayload(): Record<string, string> | undefined {
312
321
  const target = getCurrentPluginTarget();
313
- if (target === "web") return { product: "gloomberb", ...attribution };
314
- if (target === "desktop" && Object.keys(attribution).length > 0) return { product: "gloomberb", ...attribution };
315
- return undefined;
322
+ const context = wallExperimentContext();
323
+ const wallVariant = wallExperiment.variant(context);
324
+ // Keep an already-exposed visitor attributable after signup without enrolling
325
+ // the account or accepting a stored arm from an earlier session.
326
+ const signinVariant = signinWallExperiment.variant({ ...context, accountId: undefined, pro: false });
327
+ const base = target === "web"
328
+ ? { product: "gloomberb", ...attribution, ...webExperimentAttribution() }
329
+ : target === "desktop" && Object.keys(attribution).length > 0
330
+ ? { product: "gloomberb", ...attribution }
331
+ : undefined;
332
+ // A remembered browser arm is sent with the exposure request, but joins
333
+ // milestones only after this session has an accepted answer. Other live
334
+ // experiments keep their existing attribution unchanged.
335
+ const experiments = (base?.experiments ?? "").split(",").filter((part) => part && !part.startsWith("wall_teaser:") && !part.startsWith("wall_teaser_signin:"));
336
+ if (wallVariant) experiments.push(`wall_teaser:${wallVariant}`);
337
+ if (signinVariant) experiments.push(`wall_teaser_signin:${signinVariant}`);
338
+ if (!base && experiments.length === 0) return undefined;
339
+ const next: Record<string, string> = { ...base };
340
+ delete next.experiments;
341
+ if (experiments.length > 0) next.experiments = experiments.join(",");
342
+ return next;
343
+ }
344
+
345
+ export interface ResearchActivityDetails {
346
+ /** The desks picked at "What do you trade?", on the milestone that ends the first run. */
347
+ desks?: readonly string[];
348
+ /**
349
+ * Which prompt raised an `upgrade_intent` (a pane footer, a wall, the
350
+ * command bar), as a short id like a tab id. Never the prompt's text.
351
+ */
352
+ placement?: string;
353
+ /** Only ids describing what a teaser-arm wall rendered, never its content. */
354
+ teaser_kind?: "summary" | "sample" | "none";
355
+ /** The sign-in wall action, never the button copy. */
356
+ cta?: "login" | "signup";
316
357
  }
317
358
 
318
359
  /**
319
- * Counts milestones once per feature/session/account, never their content.
320
- * Native surfaces report anonymously only with an identifier the website
321
- * handed over; they still never mint one of their own. `desks` rides on the
322
- * end of the first-run funnel: the desks picked at "What do you trade?".
360
+ * Counts milestones once per feature/tab/placement, session and account, never
361
+ * their content. Wall views require an account or an identified web visitor;
362
+ * other native milestones can retain an identifier handed over by the website.
323
363
  */
324
364
  export function recordResearchActivity(
325
365
  event: ResearchActivity,
326
366
  feature?: ResearchFeature,
327
367
  tab?: string,
328
- desks?: readonly string[],
368
+ { desks, placement: rawPlacement, teaser_kind, cta }: ResearchActivityDetails = {},
329
369
  ): void {
330
370
  const target = getCurrentPluginTarget();
331
371
  const user = apiClient.getCurrentUser();
372
+ const wallEvent = event === "wall_viewed" || event === "wall_cta_clicked";
373
+ if (wallEvent && (wallPrivacyBlocked() || (!user && target !== "web"))) return;
332
374
  if (!user && !anonymousId) return;
333
375
  if (!user && target !== "web" && target !== "desktop") return;
334
- const key = `${user?.id ?? "guest"}:${event}:${feature ?? ""}:${tab ?? ""}`;
335
- if (sent.has(key)) return;
376
+ // The server refuses the whole event over a malformed id, so one is dropped instead.
377
+ const placement = rawPlacement && ACTIVITY_ID.test(rawPlacement) ? rawPlacement : undefined;
378
+ if (wallEvent && !placement) return;
379
+ if (event === "wall_cta_clicked" && cta !== "login" && cta !== "signup") return;
380
+ const identity = user?.id ?? (wallEvent ? anonymousId : "guest");
381
+ const key = `${identity}:${event}:${feature ?? ""}:${tab ?? ""}:${placement ?? ""}${event === "wall_cta_clicked" ? `:${cta}` : ""}`;
382
+ if (sent.has(key) || (wallEvent && wallSessionViews().has(key))) return;
336
383
  sent.add(key);
337
384
  if (event !== "workspace_opened") recordResearchActivity("workspace_opened");
338
385
  void apiClient
@@ -345,6 +392,15 @@ export function recordResearchActivity(
345
392
  feature,
346
393
  tab,
347
394
  desks,
395
+ placement,
396
+ ...(event === "wall_viewed" && teaser_kind ? { teaser_kind } : {}),
397
+ ...(event === "wall_cta_clicked" ? { cta } : {}),
398
+ })
399
+ .then(() => {
400
+ if (!wallEvent) return;
401
+ const views = wallSessionViews();
402
+ views.add(key);
403
+ try { sessionStorage.setItem(WALL_VIEWS_SESSION_KEY, JSON.stringify([...views])); } catch { /* Native sessions use the in-memory set. */ }
348
404
  })
349
405
  .catch(() => {
350
406
  sent.delete(key);
@@ -357,7 +413,7 @@ export function recordResearchActivity(
357
413
  * someone installed stay private; so is any id the server would refuse.
358
414
  */
359
415
  export function recordResearchTabView(tabId: string, fromExternalPlugin: boolean): void {
360
- const tab = !fromExternalPlugin && RESEARCH_TAB_ID.test(tabId) ? tabId : "plugin";
416
+ const tab = !fromExternalPlugin && ACTIVITY_ID.test(tabId) ? tabId : "plugin";
361
417
  recordResearchActivity("research_tab_viewed", undefined, tab);
362
418
  }
363
419
 
@@ -375,6 +431,109 @@ export function identifyResearchUser(): void {
375
431
  }
376
432
  }
377
433
 
434
+ /** The arms this browser was given, so each milestone carries them as `exp_<key>`. */
435
+ function webExperimentAttribution(): { experiments?: string } {
436
+ if (!anonymousId) return {};
437
+ try {
438
+ const experiments = storedExperimentAssignments(localStorage);
439
+ return experiments ? { experiments } : {};
440
+ } catch {
441
+ return {};
442
+ }
443
+ }
444
+
445
+ /**
446
+ * The arm of a web terminal experiment to show, asked for at the moment it
447
+ * would show and counted then, in every arm; null shows nothing. Only signed-out
448
+ * visitors on term.gloom.sh with an analytics id (no Do Not Track or Global
449
+ * Privacy Control) and not driven by automation are asked. See web-experiments.ts.
450
+ */
451
+ export function exposeWebExperiment(experiment: string): Promise<string | null> {
452
+ if (getCurrentPluginTarget() !== "web") return Promise.resolve(null);
453
+ try {
454
+ return exposeExperiment(experiment, {
455
+ anonymousId,
456
+ signedIn: !!apiClient.getCurrentUser(),
457
+ automated: navigator.webdriver === true,
458
+ local: localStorage,
459
+ session: sessionStorage,
460
+ ask: ({ experiment: key, variant }) => apiClient.recordExperimentExposure({
461
+ eventId: crypto.randomUUID(),
462
+ surface: "web",
463
+ anonymousId,
464
+ attribution: attributionPayload(),
465
+ experiment: key,
466
+ variant,
467
+ }),
468
+ });
469
+ } catch {
470
+ return Promise.resolve(null);
471
+ }
472
+ }
473
+
474
+ function wallPrivacyBlocked(): boolean {
475
+ if (typeof navigator === "undefined") return false;
476
+ return navigator.doNotTrack === "1"
477
+ || (navigator as Navigator & { globalPrivacyControl?: boolean }).globalPrivacyControl === true
478
+ || navigator.webdriver === true;
479
+ }
480
+
481
+ function wallExperimentContext(): WallExperimentContext {
482
+ const user = apiClient.getCurrentUser();
483
+ const surface = getCurrentPluginTarget();
484
+ let local: StorageLike | undefined;
485
+ let session: StorageLike | undefined;
486
+ try {
487
+ if (surface === "web") local = localStorage;
488
+ if (surface === "web" || surface === "desktop") session = sessionStorage;
489
+ } catch { /* Native and private sessions keep their answer in memory. */ }
490
+ return {
491
+ surface,
492
+ accountId: user?.id,
493
+ pro: hasProAccess(user),
494
+ anonymousId,
495
+ optedOut: wallPrivacyBlocked(),
496
+ automated: typeof navigator !== "undefined" && navigator.webdriver === true,
497
+ local,
498
+ session,
499
+ };
500
+ }
501
+
502
+ function wallSessionViews(): Set<string> {
503
+ try {
504
+ const saved: unknown = JSON.parse(sessionStorage.getItem(WALL_VIEWS_SESSION_KEY) ?? "[]");
505
+ if (Array.isArray(saved)) return new Set(saved.filter((value): value is string => typeof value === "string"));
506
+ } catch { /* Native sessions use the in-memory set. */ }
507
+ return new Set();
508
+ }
509
+
510
+ /** A wall's first render asks once per eligible account or web visitor per session. */
511
+ export function exposeWallTeaser(experiment: WallExperiment = "wall_teaser"): Promise<WallTeaserVariant | null> {
512
+ const context = wallExperimentContext();
513
+ const session = experiment === "wall_teaser_signin" ? signinWallExperiment : wallExperiment;
514
+ return session.expose(context, (variant) => {
515
+ const controller = new AbortController();
516
+ return withDeadline(apiClient.recordExperimentExposure({
517
+ eventId: crypto.randomUUID(),
518
+ surface: context.surface,
519
+ anonymousId: context.surface === "web" ? anonymousId : undefined,
520
+ attribution: attributionPayload(),
521
+ experiment,
522
+ variant,
523
+ }, controller.signal), 3_000, "Wall experiment request timed out", (error) => controller.abort(error));
524
+ });
525
+ }
526
+
527
+ /** Records the visible wall once per placement, including visitors outside the experiment. */
528
+ export function recordWallViewed(placement: string, teaserKind?: "summary" | "sample" | "none"): void {
529
+ recordResearchActivity("wall_viewed", undefined, undefined, { placement, teaser_kind: teaserKind });
530
+ }
531
+
532
+ /** Counts each sign-in action once per eligible placement and session. */
533
+ export function recordWallCtaClicked(placement: string, cta: "login" | "signup"): void {
534
+ recordResearchActivity("wall_cta_clicked", undefined, undefined, { placement, cta });
535
+ }
536
+
378
537
  export function researchUpgradeUrl(returnTo?: string): string {
379
538
  const url = new URL("https://gloom.sh/cloud?upgrade=pro");
380
539
  if (returnTo) url.searchParams.set("returnTo", returnTo);