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
@@ -18,9 +18,10 @@ const CLOUD_UPGRADE_URL = "https://gloom.sh/cloud?upgrade=pro";
18
18
 
19
19
  /**
20
20
  * The renderer host only exists inside React, so UI that uses it publishes an
21
- * opener here for the command bar, which runs outside the tree.
21
+ * opener here for the command bar, which runs outside the tree. Every mounted
22
+ * prompt publishes one, so a pane closing never leaves the command without.
22
23
  */
23
- let cloudUpgradeOpener: (() => void) | null = null;
24
+ const cloudUpgradeOpeners = new Set<(options?: unknown) => void>();
24
25
 
25
26
  /**
26
27
  * Signed-out users get the public Cloud page. Signed-in free accounts go
@@ -30,72 +31,171 @@ let cloudUpgradeOpener: (() => void) | null = null;
30
31
  * re-buys a subscription they already hold. Both URLs are account-bound, so no
31
32
  * session handoff is needed.
32
33
  */
33
- export interface CloudUpgradeOptions {
34
+ interface CloudUpgradeOptions {
35
+ /**
36
+ * The prompt the upgrade came from, sent with `upgrade_intent` so each one's
37
+ * conversion can be read: a short id in the shape of a tab id (`srch-wall`,
38
+ * `status-widget`), never the prompt's text or anything the user typed.
39
+ */
40
+ placement?: string;
34
41
  /** Billing interval for a fresh checkout; ignored when the account already has Pro. */
35
42
  interval?: "month" | "year";
36
43
  /** False when the upgrade sheet already recorded the intent on open. */
37
44
  recordIntent?: boolean;
38
45
  /** False when the caller already made the pitch, like onboarding's Pro step. */
39
46
  sheet?: boolean;
47
+ /**
48
+ * Called once checkout or the billing portal starts opening, after the sheet;
49
+ * never for a press ignored as a repeat.
50
+ */
51
+ onOpening?: () => void;
40
52
  }
41
53
 
42
- async function resolveCloudUpgradeUrl(options: CloudUpgradeOptions = {}): Promise<string> {
43
- if (options.recordIntent !== false) recordResearchActivity("upgrade_intent");
54
+ function recordUpgradeIntent(placement: string | undefined): void {
55
+ recordResearchActivity("upgrade_intent", undefined, undefined, { placement });
56
+ }
57
+
58
+ /**
59
+ * The page an upgrade opens: Stripe checkout for a free account, the billing
60
+ * portal for one that has Pro, and the public Cloud page when signed out or
61
+ * when neither can be created.
62
+ */
63
+ type CloudUpgradePage = "checkout" | "portal" | "cloud";
64
+
65
+ /** How long another open of the same page is ignored once one has opened. */
66
+ const CLOUD_UPGRADE_REOPEN_MS = 3_000;
67
+
68
+ /**
69
+ * How long a page may stay opening before another press tries again. The
70
+ * checkout and portal requests have no deadline of their own, and one that
71
+ * hangs must not block the page until the app restarts.
72
+ */
73
+ const CLOUD_UPGRADE_PENDING_MS = 30_000;
74
+
75
+ /**
76
+ * When each page may open again: not while its URL is being created or
77
+ * opened (up to {@link CLOUD_UPGRADE_PENDING_MS}), and not for
78
+ * {@link CLOUD_UPGRADE_REOPEN_MS} after it opened. Every
79
+ * open creates a Stripe session and a browser tab, and a held Enter repeats a
80
+ * press many times a second. Keyed by page, so a checkout that just opened
81
+ * never swallows the billing portal or the Cloud page.
82
+ */
83
+ const cloudUpgradeBlockedUntil = new Map<CloudUpgradePage, number>();
84
+
85
+ function currentCloudUpgradePage(): CloudUpgradePage {
86
+ if (!apiClient.isSignedIn()) return "cloud";
87
+ return resolvePlanAccess(apiClient.getCurrentUser()).hasProAccess ? "portal" : "checkout";
88
+ }
89
+
90
+ function isCloudUpgradeBlocked(page: CloudUpgradePage): boolean {
91
+ return (cloudUpgradeBlockedUntil.get(page) ?? 0) > Date.now();
92
+ }
93
+
94
+ /**
95
+ * Runs `open` unless `page` is already opening or opened moments ago. A failed
96
+ * open releases the page at once, so a retry works, and rethrows. A call that
97
+ * outlived its pending window leaves the block of the call after it alone.
98
+ */
99
+ async function openCloudUpgradePage(page: CloudUpgradePage, open: () => Promise<void>): Promise<void> {
100
+ if (isCloudUpgradeBlocked(page)) return;
101
+ const pending = Date.now() + CLOUD_UPGRADE_PENDING_MS;
102
+ cloudUpgradeBlockedUntil.set(page, pending);
103
+ const settle = (until: number | null) => {
104
+ if (cloudUpgradeBlockedUntil.get(page) !== pending) return;
105
+ if (until === null) cloudUpgradeBlockedUntil.delete(page);
106
+ else cloudUpgradeBlockedUntil.set(page, until);
107
+ };
108
+ try {
109
+ await open();
110
+ } catch (error) {
111
+ settle(null);
112
+ throw error;
113
+ }
114
+ settle(Date.now() + CLOUD_UPGRADE_REOPEN_MS);
115
+ }
116
+
117
+ /** Forgets every recent open, so one test's checkout does not swallow the next test's. */
118
+ export function resetCloudUpgradeGuardForTests(): void {
119
+ cloudUpgradeBlockedUntil.clear();
120
+ }
121
+
122
+ async function resolveCloudUpgradeUrl(page: CloudUpgradePage, options: CloudUpgradeOptions): Promise<string> {
44
123
  const returnTo = getCurrentPluginTarget() === "web" ? window.location.href : undefined;
45
- if (!apiClient.isSignedIn()) return researchUpgradeUrl(returnTo);
46
- const { url } = resolvePlanAccess(apiClient.getCurrentUser()).hasProAccess
124
+ if (page === "cloud") return researchUpgradeUrl(returnTo);
125
+ const { url } = page === "portal"
47
126
  ? await apiClient.createBillingPortal()
48
127
  : await apiClient.createCloudCheckout(returnTo, options.interval ?? "month");
49
128
  return url;
50
129
  }
51
130
 
52
- /** Opens checkout or the billing portal in the user's browser. */
53
- export async function openCloudUpgrade(
131
+ /** Opens checkout, the billing portal or the Cloud page in the user's browser. */
132
+ async function openCloudUpgrade(
54
133
  rendererHost: Pick<RendererHost, "openExternal">,
55
- options: CloudUpgradeOptions = {},
134
+ page: CloudUpgradePage,
135
+ options: CloudUpgradeOptions,
56
136
  ): Promise<void> {
57
- await rendererHost.openExternal(await resolveCloudUpgradeUrl(options));
137
+ await rendererHost.openExternal(await resolveCloudUpgradeUrl(page, options));
58
138
  }
59
139
 
60
- /** Opens the checkout page when any cloud UI has published an opener. */
61
- export function openCloudUpgradeUrl(): boolean {
62
- if (!cloudUpgradeOpener) return false;
63
- cloudUpgradeOpener();
140
+ /**
141
+ * Opens the checkout page when any cloud UI has published an opener, counted
142
+ * under `placement` rather than the UI that published it.
143
+ */
144
+ export function openCloudUpgradeUrl(placement: string): boolean {
145
+ const opener = [...cloudUpgradeOpeners].at(-1);
146
+ if (!opener) return false;
147
+ opener({ placement });
64
148
  return true;
65
149
  }
66
150
 
151
+ const CLOUD_UPGRADE_OPTION_KEYS: ReadonlyArray<keyof CloudUpgradeOptions> = ["placement", "interval", "recordIntent", "sheet", "onOpening"];
152
+
153
+ /** A press event carries none of these keys, so it never reads as options. */
67
154
  function isCloudUpgradeOptions(value: unknown): value is CloudUpgradeOptions {
68
- return !!value && typeof value === "object" && "interval" in value;
155
+ return !!value && typeof value === "object" && CLOUD_UPGRADE_OPTION_KEYS.some((key) => key in value);
69
156
  }
70
157
 
71
158
  /**
72
- * Opens the Pro checkout page in the user's browser. The action doubles as a
73
- * press handler for buttons and footer segments, which hand it their event,
74
- * so only an explicit options object changes the checkout.
159
+ * Opens the Pro checkout page in the user's browser, counting the intent under
160
+ * `placement` (see {@link CloudUpgradeOptions.placement}). The action doubles
161
+ * as a press handler for buttons and footer segments, which hand it their
162
+ * event, so only an explicit options object changes the checkout. It settles
163
+ * once checkout has opened or the person backed out of the sheet. A call while
164
+ * the same page is opening, or for a few seconds after it opened, is ignored:
165
+ * a held Enter would otherwise open a checkout per keypress.
75
166
  *
76
167
  * A signed-in account without Pro sees the upgrade sheet first (what Pro adds,
77
168
  * what today costs), and checkout opens only once they choose to go on.
78
169
  * Signed-out users get the public Cloud page and Pro accounts the billing
79
170
  * portal, as before.
80
171
  */
81
- export function useCloudUpgradeAction(): (options?: unknown) => void {
172
+ export function useCloudUpgradeAction(placement: string): (options?: unknown) => Promise<void> {
82
173
  const rendererHost = useRendererHost();
83
174
  // Null in isolated renders (tests, previews): checkout then opens directly.
84
175
  const dialog = useOptionalDialog();
85
176
  const viewport = useViewport();
86
177
  const viewportWidthRef = useRef(viewport.width);
87
178
  viewportWidthRef.current = viewport.width;
88
- const openUpgrade = useCallback((options?: unknown) => {
89
- const checkout = isCloudUpgradeOptions(options) ? options : {};
90
- const openCheckout = (resolved: CloudUpgradeOptions) => {
91
- void openCloudUpgrade(rendererHost, resolved).catch(async () => {
179
+ const openUpgrade = useCallback(async (options?: unknown) => {
180
+ const checkout: CloudUpgradeOptions = { placement, ...(isCloudUpgradeOptions(options) ? options : {}) };
181
+ const openCheckout = async (resolved: CloudUpgradeOptions) => {
182
+ if (resolved.recordIntent !== false) recordUpgradeIntent(resolved.placement);
183
+ const page = currentCloudUpgradePage();
184
+ await openCloudUpgradePage(page, async () => {
185
+ resolved.onOpening?.();
186
+ await openCloudUpgrade(rendererHost, page, resolved);
187
+ }).catch(() => (
92
188
  // Keep the Cloud page reachable when checkout cannot be created; a native
93
- // session still rides along on the server's one-time handoff URL.
94
- const url = await apiClient.createBrowserHandoff()
95
- .then((handoff) => handoff.url)
96
- .catch(() => CLOUD_UPGRADE_URL);
97
- void rendererHost.openExternal(url);
98
- });
189
+ // session still rides along on the server's one-time handoff URL. Under
190
+ // the Cloud page's guard, so a retry that fails again moments later
191
+ // opens no second tab.
192
+ openCloudUpgradePage("cloud", async () => {
193
+ const url = await apiClient.createBrowserHandoff()
194
+ .then((handoff) => handoff.url)
195
+ .catch(() => CLOUD_UPGRADE_URL);
196
+ await rendererHost.openExternal(url);
197
+ })
198
+ )).catch(() => {});
99
199
  };
100
200
  if (
101
201
  checkout.sheet === false ||
@@ -103,21 +203,22 @@ export function useCloudUpgradeAction(): (options?: unknown) => void {
103
203
  !apiClient.isSignedIn() ||
104
204
  resolvePlanAccess(apiClient.getCurrentUser()).hasProAccess
105
205
  ) {
106
- openCheckout(checkout);
206
+ await openCheckout(checkout);
107
207
  return;
108
208
  }
109
- recordResearchActivity("upgrade_intent");
110
- void promptCloudUpgrade(dialog, {
209
+ recordUpgradeIntent(checkout.placement);
210
+ // Checkout is opening or just opened: a second sheet would lead nowhere.
211
+ if (isCloudUpgradeBlocked("checkout")) return;
212
+ const interval = await promptCloudUpgrade(dialog, {
111
213
  interval: checkout.interval,
112
214
  viewportWidth: viewportWidthRef.current,
113
- }).then((interval) => {
114
- if (interval) openCheckout({ interval, recordIntent: false });
115
215
  });
116
- }, [dialog, rendererHost]);
216
+ if (interval) await openCheckout({ ...checkout, interval, recordIntent: false });
217
+ }, [dialog, placement, rendererHost]);
117
218
  useEffect(() => {
118
- cloudUpgradeOpener = openUpgrade;
219
+ cloudUpgradeOpeners.add(openUpgrade);
119
220
  return () => {
120
- if (cloudUpgradeOpener === openUpgrade) cloudUpgradeOpener = null;
221
+ cloudUpgradeOpeners.delete(openUpgrade);
121
222
  };
122
223
  }, [openUpgrade]);
123
224
  return openUpgrade;
@@ -150,6 +251,8 @@ export interface CloudAccessFooterOptions {
150
251
  /** Set false while the pane happens to show data that is not cloud-delayed. */
151
252
  degraded?: boolean;
152
253
  segmentId?: string;
254
+ /** The upgrade intent's placement id (see {@link CloudUpgradeOptions.placement}); the segment id when omitted. */
255
+ placement?: string;
153
256
  }
154
257
 
155
258
  export interface CloudAccessFooter {
@@ -175,10 +278,11 @@ export function useCloudAccessFooter({
175
278
  degraded = true,
176
279
  segmentId = "cloud-access",
177
280
  shortcutScope,
281
+ placement = segmentId,
178
282
  }: CloudAccessFooterOptions): CloudAccessFooter {
179
283
  const language = useAppLanguage();
180
284
  const access = usePlanAccess();
181
- const openUpgrade = useCloudUpgradeAction();
285
+ const openUpgrade = useCloudUpgradeAction(placement);
182
286
  const openPlan = useCloudPlanAction();
183
287
 
184
288
  const showTrial = access.isTrialActive;
@@ -2,8 +2,11 @@ import { useCallback, useEffect, useState, type ReactNode } from "react";
2
2
  import { ApiRequestError } from "../../../api-client/errors";
3
3
  import { usePlanAccess } from "../../../api-client/plan-access";
4
4
  import { ProWall, SignInWall } from "../cloud/auth-actions";
5
+ import { useBoundTicker } from "./ticker-request";
5
6
 
6
7
  export interface ProFeatureWallCopy {
8
+ /** The upgrade intent's placement id, e.g. "risk-wall". */
9
+ placement: string;
7
10
  /** Finishes "Sign in to …", e.g. "read risk factors". */
8
11
  action: string;
9
12
  /** The upgrade headline, e.g. "Risk factors are part of Gloom Cloud Pro." */
@@ -21,11 +24,12 @@ export type ReadGuard = <T>(read: Promise<T>) => Promise<T>;
21
24
  * through `guard`, so a refusal from the server (a plan that ended while the
22
25
  * app was open, an expired session) shows the same wall, not an error.
23
26
  */
24
- export function useProFeatureWall(copy: ProFeatureWallCopy): {
27
+ export function useProFeatureWall(copy: ProFeatureWallCopy, size: { width: number; height: number }): {
25
28
  wall: ReactNode;
26
29
  guard: ReadGuard;
27
30
  } {
28
31
  const access = usePlanAccess();
32
+ const { symbol, exchange } = useBoundTicker();
29
33
  const [refused, setRefused] = useState<number | null>(null);
30
34
  // A refusal is about the plan the account had then; a new plan asks again.
31
35
  useEffect(() => setRefused(null), [access.signedIn, access.emailVerified, access.hasProAccess]);
@@ -37,11 +41,11 @@ export function useProFeatureWall(copy: ProFeatureWallCopy): {
37
41
 
38
42
  let wall: ReactNode = null;
39
43
  if (!access.signedIn || refused === 401) {
40
- wall = <SignInWall action={copy.action} />;
44
+ wall = <SignInWall placement={`${copy.placement.replace(/-wall$/, "")}-signin`} {...size} symbol={symbol} exchange={exchange} action={copy.action} />;
41
45
  } else if (!access.emailVerified || refused === 403) {
42
- wall = <SignInWall action={copy.action} needsVerification />;
46
+ wall = <SignInWall placement={`${copy.placement.replace(/-wall$/, "")}-signin`} {...size} symbol={symbol} exchange={exchange} action={copy.action} needsVerification />;
43
47
  } else if (!access.hasProAccess || refused === 402) {
44
- wall = <ProWall title={copy.title} message={copy.message} />;
48
+ wall = <ProWall placement={copy.placement} title={copy.title} message={copy.message} {...size} symbol={symbol} exchange={exchange} />;
45
49
  }
46
50
  return { wall, guard };
47
51
  }
@@ -0,0 +1,107 @@
1
+ import type { DataTableCell, DataTableColumn } from "../../../components";
2
+ import { SplitBar } from "../../../components/ui/split-bar";
3
+ import { blendHex } from "../../../theme/colors";
4
+ import type { ThemeColors } from "../../../theme/colors";
5
+ import { Box, Text, TextAttributes } from "../../../ui";
6
+ import { displayWidth } from "../../../utils/format";
7
+
8
+ /**
9
+ * Cells the research data panes share, so attention, hiring, awards, power and
10
+ * the rest read the same way: one muted placeholder, moves coloured by sign,
11
+ * a listing as its symbol with the venue quiet beside it, and a share as a
12
+ * length next to its number.
13
+ */
14
+
15
+ /** The one placeholder for a value the source does not give. */
16
+ const NO_VALUE = "--";
17
+
18
+ export const missingCell = (colors: ThemeColors): DataTableCell => ({ text: NO_VALUE, value: null, color: colors.textMuted });
19
+
20
+ /** A move or a change: green up, red down, quiet at zero. */
21
+ export function signedColor(value: number | null | undefined, colors: ThemeColors): string {
22
+ return value == null || value === 0 ? colors.textMuted : value > 0 ? colors.positive : colors.negative;
23
+ }
24
+
25
+ /** `6758:JPX` reads as the symbol, with the venue after it in a quieter colour. */
26
+ export function listingCell(key: string | null | undefined, colors: ThemeColors, selected: boolean): DataTableCell {
27
+ if (!key) return missingCell(colors);
28
+ const at = key.indexOf(":");
29
+ const symbol = at > 0 ? key.slice(0, at) : key;
30
+ const venue = at > 0 ? key.slice(at + 1) : "";
31
+ return { text: key, content: <Box flexDirection="row" height={1} overflow="hidden">
32
+ <Text fg={selected ? colors.selectedText : colors.textBright} attributes={TextAttributes.BOLD}>{symbol}</Text>
33
+ {venue ? <Text fg={selected ? colors.selectedText : colors.textDim}>{` ${venue}`}</Text> : null}
34
+ </Box> };
35
+ }
36
+
37
+ /**
38
+ * A share of a whole as its number and a bar measured against the largest row,
39
+ * so the eye compares lengths down the column. The bar gives way first in a
40
+ * narrow column; the number never does.
41
+ */
42
+ export function shareCell(text: string, ratio: number | null, width: number, colors: ThemeColors, selected: boolean, desktop: boolean,
43
+ color = blendHex(colors.bg, colors.borderFocused, 0.6)): DataTableCell {
44
+ const textCells = displayWidth(text);
45
+ const barCells = width - textCells - 1;
46
+ if (ratio == null || !(ratio > 0) || barCells < 4) return { text, color: selected ? colors.selectedText : colors.text };
47
+ const fill = Math.max(0, Math.min(1, ratio));
48
+ const ink = selected ? colors.selectedText : color;
49
+ // The desktop draws the bar on a faint track; terminal cells draw the length alone, at half-cell steps,
50
+ // since a track of block cells would join the rows above and below into one wall.
51
+ const units = Math.max(1, Math.round(fill * barCells * 2));
52
+ return { text, content: <Box flexDirection="row" height={1} width={width} overflow="hidden" alignItems="center">
53
+ <Box width={barCells} flexShrink={0} flexDirection="row" alignItems="center">
54
+ {desktop ? <SplitBar width={barCells} parts={[{ id: "share", value: fill, color: ink }, { id: "rest", value: 1 - fill, color: blendHex(colors.bg, color, 0.2) }]} />
55
+ : <Text fg={ink}>{`${"█".repeat(Math.floor(units / 2))}${units % 2 ? "▌" : ""}`}</Text>}
56
+ </Box>
57
+ <Box flexGrow={1} justifyContent="flex-end"><Text fg={selected ? colors.selectedText : colors.text}>{` ${text}`}</Text></Box>
58
+ </Box> };
59
+ }
60
+
61
+ /**
62
+ * Parts of a whole as one bar, strongest part first. The desktop uses the kit
63
+ * bar; terminal cells step down from solid to light shade, so neighbouring
64
+ * rows read as separate bars rather than one block.
65
+ */
66
+ export function PartsBar({ parts, width, desktop }: { parts: Array<{ id: string; value: number; color: string }>; width: number; desktop: boolean }) {
67
+ if (desktop) return <Box width={width} height={1} alignItems="center"><SplitBar width={width} parts={parts} /></Box>;
68
+ const total = parts.reduce((sum, part) => sum + Math.max(0, part.value), 0);
69
+ if (!(total > 0) || width <= 0) return <Text>{""}</Text>;
70
+ const glyphs = ["█", "▓", "▒", "░"];
71
+ let used = 0;
72
+ return <Box flexDirection="row" width={width} height={1} overflow="hidden">
73
+ {parts.map((part, index) => {
74
+ const cells = index === parts.length - 1 ? Math.max(0, width - used) : Math.round(Math.max(0, part.value) / total * width);
75
+ used += cells;
76
+ return part.value > 0 && cells > 0 ? <Text key={part.id} fg={part.color}>{(glyphs[index] ?? "░").repeat(cells)}</Text> : null;
77
+ })}
78
+ </Box>;
79
+ }
80
+
81
+ /** A state word in its tone: the same status reads the same in every pane. */
82
+ export type StateTone = "positive" | "negative" | "warning" | "accent" | "muted" | "text";
83
+ export function toneColor(tone: StateTone, colors: ThemeColors): string {
84
+ return tone === "accent" ? colors.borderFocused : tone === "muted" ? colors.textMuted : tone === "text" ? colors.text : colors[tone];
85
+ }
86
+
87
+ /** `ACTIVE_NOT_RECRUITING`, `active not recruiting` and `deadlineDate` all read as words. */
88
+ export function humanLabel(value: string): string {
89
+ const spaced = value.trim().replace(/([a-z])([A-Z])/g, "$1 $2").replace(/[_-]+/g, " ").replace(/\s+/g, " ").toLowerCase();
90
+ return spaced.replace(/^./, (first) => first.toUpperCase()).replace(/\b(ai|us|uk|eu|fda|ema|sec|ftc|doj|ofac|usa)\b/gi, (word) => word.toUpperCase());
91
+ }
92
+
93
+ /**
94
+ * Drops the columns whose cells read the same default on every row: a scope
95
+ * that is always "Project", a missing count that is always zero. A row that
96
+ * differs brings the column back; a uniform value that is not a default stays.
97
+ */
98
+ export function withoutQuietColumns<T>(columns: DataTableColumn[], rows: readonly T[], read: (row: T, columnId: string) => unknown,
99
+ quiet: Readonly<Record<string, readonly string[]>>): DataTableColumn[] {
100
+ if (!rows.length) return columns;
101
+ return columns.filter((column) => {
102
+ const defaults = quiet[column.id];
103
+ if (!defaults) return true;
104
+ const first = String(read(rows[0]!, column.id) ?? "");
105
+ return !defaults.includes(first) || rows.some((row) => String(read(row, column.id) ?? "") !== first);
106
+ });
107
+ }
@@ -5,6 +5,7 @@ import {
5
5
  useQueryBarSearch,
6
6
  type DataTableColumn,
7
7
  type DataTableSelectionChangeReason,
8
+ type QueryBarFilter,
8
9
  type QueryBarView,
9
10
  } from "../../../components";
10
11
  import type { DataTableViewProps } from "../../../components/data-table/view";
@@ -127,6 +128,7 @@ export function SeriesListDetail<Row, Column extends DataTableColumn, R extends
127
128
  focused,
128
129
  listWidth: maxListWidth,
129
130
  searchPlaceholder,
131
+ filters,
130
132
  columns: buildColumns,
131
133
  rows,
132
134
  getRowId,
@@ -143,6 +145,8 @@ export function SeriesListDetail<Row, Column extends DataTableColumn, R extends
143
145
  /** The table's width beside the detail. */
144
146
  listWidth: number;
145
147
  searchPlaceholder: string;
148
+ /** Choices beside the search that pick which series the list holds, such as a model. */
149
+ filters?: QueryBarFilter[];
146
150
  columns: (width: number, stacked: boolean) => Column[];
147
151
  /** Table rows for the visible items, with any section headers. */
148
152
  rows: Row[];
@@ -170,7 +174,7 @@ export function SeriesListDetail<Row, Column extends DataTableColumn, R extends
170
174
 
171
175
  return (
172
176
  <Box flexDirection="column" width={width} height={height}>
173
- <QueryBar width={width} search={{ ...list.search, placeholder: searchPlaceholder }} view={list.range} />
177
+ <QueryBar width={width} search={{ ...list.search, placeholder: searchPlaceholder }} filters={filters} view={list.range} />
174
178
  <Box flexDirection={split ? "row" : "column"} flexGrow={1} overflow="hidden">
175
179
  <Box flexDirection="column" width={listWidth} flexShrink={0}>
176
180
  <Box flexDirection="column" width={listWidth} height={tableHeight} flexShrink={0} overflow="hidden">
@@ -32,7 +32,7 @@ export function useBoundTicker() {
32
32
  symbol,
33
33
  ticker,
34
34
  exchange: ticker?.metadata.exchange ?? "",
35
- currency: ticker?.metadata.currency ?? "USD",
35
+ currency: ticker?.metadata.currency || "USD",
36
36
  };
37
37
  }
38
38
 
@@ -0,0 +1,80 @@
1
+ /** Authored placeholders only. No sample contains a provider payload or numeric value. */
2
+ interface SampleColumn {
3
+ label: string;
4
+ width: number;
5
+ /** Lower numbers stay visible first when the pane is narrow. */
6
+ priority: number;
7
+ align: "left" | "right";
8
+ }
9
+
10
+ type WallSample = { summaryHook?: string } & (
11
+ | { layout: "table"; columns: readonly SampleColumn[] }
12
+ | { layout: "prose"; issuer?: string }
13
+ );
14
+
15
+ const column = (label: string, width: number, priority: number, align: "left" | "right" = "left"): SampleColumn =>
16
+ ({ label, width, priority, align });
17
+
18
+ const callHook = "Pro shows the transcript, Q&A and guidance";
19
+
20
+ /** Column labels/order mirror the real panes; lower-priority fields give way at narrow widths. */
21
+ export const WALL_TEASERS: Readonly<Record<string, WallSample>> = {
22
+ "risk-wall": {
23
+ layout: "prose", issuer: "AAPL",
24
+ summaryHook: "Pro shows which were added, dropped or rewritten",
25
+ },
26
+ "exec-wall": {
27
+ layout: "table",
28
+ summaryHook: "Pro shows pay for each executive",
29
+ columns: [column("NAME", 18, 0), column("TITLE", 16, 1), column("EQ%", 4, 3, "right"), column("TOTAL", 8, 2, "right")],
30
+ },
31
+ "ek-wall": {
32
+ layout: "prose", issuer: "AAPL",
33
+ summaryHook: "Pro shows each item and what it said",
34
+ },
35
+ "calls-wall": {
36
+ layout: "table", summaryHook: callHook,
37
+ columns: [column("TICKER", 8, 0), column("COMPANY", 12, 4), column("DATE", 10, 1), column("PERIOD", 8, 2), column("LENGTH", 7, 5, "right"), column("TONE", 11, 3)],
38
+ },
39
+ "calls-transcript-wall": { layout: "prose", issuer: "AAPL", summaryHook: callHook },
40
+ "diag-wall": { layout: "prose", issuer: "AAPL" },
41
+ "jobs-wall": {
42
+ layout: "table",
43
+ summaryHook: "Pro shows roles by function and location",
44
+ columns: [column("TICKER", 8, 0), column("COMPANY", 18, 5), column("OPEN", 8, 1, "right"), column("30D", 14, 4, "right"), column("POSTED 30D", 11, 3, "right"), column("NEW 7D", 8, 2, "right"), column("TOP FUNCTION", 18, 6), column("TOP COUNTRY", 12, 7)],
45
+ },
46
+ "most-wall": {
47
+ layout: "table",
48
+ columns: [column("#", 3, 10, "right"), column("TICKER", 8, 0), column("NAME", 12, 9), column("LAST", 10, 7, "right"), column("GAP%", 8, 1, "right"), column("CHG%", 8, 6, "right"), column("PRE VOL", 8, 2, "right"), column("RVOL", 6, 3, "right"), column("VWAP%", 7, 8, "right"), column("FLOAT", 7, 4, "right"), column("EVENT", 6, 5)],
49
+ },
50
+ "flow-wall": {
51
+ layout: "table",
52
+ columns: [column("TIME", 8, 1), column("TICKER", 8, 0), column("TYPE", 8, 2), column("STRIKE", 8, 4, "right"), column("EXP", 6, 5, "right"), column("SIDE", 5, 7), column("SIZE", 7, 6, "right"), column("PREM", 7, 3, "right"), column("V/OI", 6, 8, "right")],
53
+ },
54
+ "hilo-wall": {
55
+ layout: "table",
56
+ columns: [column("NEW HIGH", 10, 0), column("PRICE", 10, 1, "right"), column("COUNT", 6, 2, "right")],
57
+ },
58
+ "srch-wall": {
59
+ layout: "table",
60
+ columns: [column("TICKER", 8, 0), column("TYPE", 7, 2), column("DATE", 10, 3), column("TITLE", 14, 1), column("MATCH", 18, 4)],
61
+ },
62
+ "team": { layout: "prose" },
63
+ };
64
+
65
+ /** Sign-in placements reuse the existing previews and public summary allowlist. */
66
+ export const SIGNIN_WALL_TEASERS: Readonly<Record<string, string>> = {
67
+ "risk-signin": "risk-wall",
68
+ "exec-signin": "exec-wall",
69
+ "ek-signin": "ek-wall",
70
+ "calls-signin": "calls-wall",
71
+ "calls-transcript-signin": "calls-transcript-wall",
72
+ "diag-signin": "diag-wall",
73
+ "jobs-signin": "jobs-wall",
74
+ "jobs-detail-signin": "jobs-wall",
75
+ "most-signin": "most-wall",
76
+ "flow-signin": "flow-wall",
77
+ "hilo-signin": "hilo-wall",
78
+ "srch-signin": "srch-wall",
79
+ "team-signin": "team",
80
+ };