gloomberb 0.17.4 → 0.17.6

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 (384) hide show
  1. package/package.json +3 -1
  2. package/patches/electrobun@1.18.1.patch +80 -0
  3. package/src/api-client/chat.ts +9 -0
  4. package/src/api-client/commodity-balances.ts +77 -0
  5. package/src/api-client/data.ts +35 -3
  6. package/src/api-client/earnings.ts +10 -0
  7. package/src/api-client/index.ts +5 -0
  8. package/src/api-client/insider-signals.ts +75 -0
  9. package/src/api-client/market-discovery.ts +1 -1
  10. package/src/api-client/normalizers.ts +1 -0
  11. package/src/api-client/paths.ts +9 -0
  12. package/src/api-client/research-activity.ts +10 -3
  13. package/src/api-client/socket.ts +10 -0
  14. package/src/api-client/types.ts +25 -1
  15. package/src/app/pane-runtime/ticker-open-runtime.ts +2 -1
  16. package/src/app/runtime/broker-import.ts +23 -21
  17. package/src/app/runtime/plugin-bindings.ts +13 -7
  18. package/src/app/runtime/update.ts +64 -4
  19. package/src/app.tsx +2 -0
  20. package/src/architecture/core-builtin-allowlist.json +2 -10
  21. package/src/ask-gloom/pane-context.ts +11 -1
  22. package/src/brokers/broker-portfolio-sync.ts +66 -0
  23. package/src/brokers/sync-broker-instance.ts +75 -41
  24. package/src/cli/commands/market.ts +101 -34
  25. package/src/cli/commands/options-chain.ts +20 -6
  26. package/src/cli/commands/overview.ts +54 -13
  27. package/src/cli/commands/search.ts +1 -1
  28. package/src/cli/commands/system.ts +19 -1
  29. package/src/cli/commands/ticker.ts +24 -4
  30. package/src/cli/context.ts +13 -0
  31. package/src/cli/errors.ts +15 -2
  32. package/src/cli/fx-pair.ts +77 -0
  33. package/src/cli/glossary.ts +34 -4
  34. package/src/cli/history-rows.ts +25 -11
  35. package/src/cli/index.ts +30 -12
  36. package/src/cli/listing-arg.ts +61 -138
  37. package/src/cli/listing-suggestion.ts +3 -21
  38. package/src/cli/pane-functions/catalog.ts +50 -0
  39. package/src/cli/pane-functions/freshness.ts +44 -9
  40. package/src/cli/pane-functions/function-help.ts +9 -9
  41. package/src/cli/pane-functions/headless.ts +19 -7
  42. package/src/cli/pane-functions/index.ts +63 -5
  43. package/src/cli/pane-functions/market-session.ts +18 -3
  44. package/src/cli/pane-functions/resolver.ts +29 -8
  45. package/src/cli/pane-functions/screenshot.ts +2 -0
  46. package/src/cli/report-tables.ts +11 -4
  47. package/src/cli/result.ts +2 -0
  48. package/src/cli/series-search.ts +6 -1
  49. package/src/cli/ticker-resolution.ts +1 -2
  50. package/src/cli/unknown-command.ts +18 -0
  51. package/src/components/chart/composite/composite-chart.tsx +80 -19
  52. package/src/components/chart/composite/format.ts +17 -0
  53. package/src/components/chart/composite/legend.tsx +26 -15
  54. package/src/components/chart/composite/panel-labels.ts +38 -0
  55. package/src/components/chart/composite/panel-series.ts +1 -0
  56. package/src/components/chart/composite/panel-surface.tsx +25 -7
  57. package/src/components/chart/composite/price-axis-labels.tsx +16 -3
  58. package/src/components/chart/composite/scene.ts +60 -2
  59. package/src/components/chart/composite/series-axis-tags.tsx +67 -0
  60. package/src/components/chart/composite/tools.ts +2 -2
  61. package/src/components/chart/composite/types.ts +28 -0
  62. package/src/components/chart/core/view-reset.ts +46 -0
  63. package/src/components/command-bar/commands/collection.ts +1 -1
  64. package/src/components/command-bar/commands/direct/index.ts +48 -1
  65. package/src/components/command-bar/commands/registry.ts +8 -0
  66. package/src/components/command-bar/list/model.ts +7 -0
  67. package/src/components/command-bar/pane-templates/workflow.ts +5 -0
  68. package/src/components/command-bar/panel/prompt-binding.ts +8 -0
  69. package/src/components/command-bar/routes/root/command-items.ts +17 -2
  70. package/src/components/command-bar/routes/root/pick-prefetch.ts +38 -0
  71. package/src/components/command-bar/routes/root/provider-search.ts +6 -0
  72. package/src/components/command-bar/routes/root/runtime.ts +3 -0
  73. package/src/components/command-bar/routes/root/shortcut-feedback.ts +3 -1
  74. package/src/components/command-bar/routes/root/shortcut-items.ts +1 -1
  75. package/src/components/command-bar/routes/ticker-search/actions.ts +34 -0
  76. package/src/components/command-bar/surface/index.tsx +7 -1
  77. package/src/components/command-bar/workflow/collection-actions.ts +6 -5
  78. package/src/components/command-bar/workflow/ops.ts +8 -5
  79. package/src/components/command-bar/workflow/pane-reuse.ts +3 -2
  80. package/src/components/command-bar/workflow/tickers.ts +1 -1
  81. package/src/components/form-modal/confirm.tsx +1 -0
  82. package/src/components/form-modal/request.ts +2 -0
  83. package/src/components/layout/detached-pane-shell.tsx +6 -1
  84. package/src/components/layout/pane/content.tsx +11 -8
  85. package/src/components/layout/pane/sidebar.tsx +24 -11
  86. package/src/components/layout/pane/ticker-fate-gate.tsx +56 -0
  87. package/src/components/layout/shell/index.tsx +5 -1
  88. package/src/components/layout/shell/menu.ts +8 -0
  89. package/src/components/layout/shell/pane/capture.ts +29 -12
  90. package/src/components/onboarding/wizard-model.ts +1 -1
  91. package/src/components/onboarding/wizard-positions.ts +18 -10
  92. package/src/components/ticker/badge/format.ts +64 -15
  93. package/src/components/ticker/badge/index.tsx +32 -11
  94. package/src/components/ticker/badge/list.tsx +4 -3
  95. package/src/components/ticker/badge/text.tsx +2 -2
  96. package/src/components/ticker/list-table-view.tsx +8 -1
  97. package/src/components/ui/confirm-dialog.tsx +30 -4
  98. package/src/components/ui/data-table/opentui/index.tsx +61 -20
  99. package/src/components/ui/data-table/opentui/model.ts +104 -0
  100. package/src/components/ui/fields.tsx +5 -0
  101. package/src/components/ui/inline-quick-add.tsx +4 -1
  102. package/src/components/ui/query-bar-keys.tsx +29 -5
  103. package/src/components/ui/query-bar.tsx +29 -4
  104. package/src/components/ui/windowed-list.tsx +27 -0
  105. package/src/data/config/store/normalize.ts +8 -0
  106. package/src/docs/snapshot.json +248 -5
  107. package/src/i18n/es.ts +3 -1
  108. package/src/i18n/ja.ts +3 -1
  109. package/src/i18n/ko.ts +3 -1
  110. package/src/i18n/zh-cn.ts +3 -1
  111. package/src/i18n/zh-tw.ts +3 -1
  112. package/src/layout-marketplace/cloud.ts +16 -5
  113. package/src/layout-marketplace/gallery.tsx +4 -4
  114. package/src/layout-marketplace/linked-sync.ts +9 -3
  115. package/src/layout-marketplace/linked.ts +24 -3
  116. package/src/layout-marketplace/model.ts +39 -4
  117. package/src/market-data/coordinator/events.ts +5 -1
  118. package/src/market-data/coordinator/index.ts +38 -1
  119. package/src/market-data/coordinator/quotes.ts +4 -0
  120. package/src/market-data/futures-contract.ts +53 -0
  121. package/src/market-data/hooks.tsx +225 -30
  122. package/src/market-data/market/bar-period.ts +71 -0
  123. package/src/market-data/market/format.ts +14 -2
  124. package/src/market-data/market/freshness.ts +28 -1
  125. package/src/market-data/market/status.ts +152 -13
  126. package/src/market-data/model-dividend-yield.ts +76 -0
  127. package/src/market-data/quotes/freshness.ts +30 -1
  128. package/src/{plugins/builtin/portfolio-list → market-data/quotes}/live-valuation.ts +6 -6
  129. package/src/market-data/quotes/regular-session-valuation.ts +32 -1
  130. package/src/plugins/builtin/analytics/metrics.ts +8 -4
  131. package/src/plugins/builtin/analytics/risk-headless.ts +12 -5
  132. package/src/plugins/builtin/analytics/risk-model.ts +74 -15
  133. package/src/plugins/builtin/analytics/risk-pane.tsx +6 -6
  134. package/src/plugins/builtin/broker-manager/detail.tsx +2 -2
  135. package/src/plugins/builtin/broker-manager/model.ts +2 -2
  136. package/src/plugins/builtin/builtin-plugin-meta.ts +0 -1
  137. package/src/plugins/builtin/chart-composer/editor/controller.ts +14 -10
  138. package/src/plugins/builtin/chart-composer/formula.ts +233 -0
  139. package/src/plugins/builtin/chart-composer/headless.ts +27 -2
  140. package/src/plugins/builtin/chart-composer/index.tsx +3 -2
  141. package/src/plugins/builtin/chart-composer/pane.tsx +41 -10
  142. package/src/plugins/builtin/chart-composer/presets.ts +91 -2
  143. package/src/plugins/builtin/chart-composer/quick-add.tsx +9 -2
  144. package/src/plugins/builtin/chart-composer/series-catalog.ts +44 -11
  145. package/src/plugins/builtin/chart-composer/series-expression.ts +1 -1
  146. package/src/plugins/builtin/chart-composer/settings.ts +16 -4
  147. package/src/plugins/builtin/chart-composer/studies.ts +1 -1
  148. package/src/plugins/builtin/chat/content/channel-navigation.ts +17 -6
  149. package/src/plugins/builtin/chat/content/footer.tsx +5 -2
  150. package/src/plugins/builtin/chat/content/index.tsx +12 -5
  151. package/src/plugins/builtin/chat/content/mentions.ts +50 -21
  152. package/src/plugins/builtin/chat/content/scroll.ts +12 -5
  153. package/src/plugins/builtin/chat/content/snapshot.ts +11 -0
  154. package/src/plugins/builtin/chat/content/transcript.tsx +48 -37
  155. package/src/plugins/builtin/chat/content/whole-message-copy.ts +44 -2
  156. package/src/plugins/builtin/chat/controller/index.ts +88 -17
  157. package/src/plugins/builtin/chat/controller/mention-candidates.ts +104 -0
  158. package/src/plugins/builtin/chat/controller/messages.ts +14 -2
  159. package/src/plugins/builtin/chat/controller/realtime.ts +9 -0
  160. package/src/plugins/builtin/chat/controller/state.ts +25 -0
  161. package/src/plugins/builtin/chat/controller/unread-people.ts +240 -0
  162. package/src/plugins/builtin/chat/controller/view.ts +4 -0
  163. package/src/plugins/builtin/chat/conversation-recency.ts +73 -0
  164. package/src/plugins/builtin/chat/conversations.ts +12 -5
  165. package/src/plugins/builtin/chat/index.tsx +1 -1
  166. package/src/plugins/builtin/chat/layout.ts +23 -5
  167. package/src/plugins/builtin/chat/message/desktop.tsx +2 -0
  168. package/src/plugins/builtin/chat/share/flow.tsx +18 -9
  169. package/src/plugins/builtin/chat/sidebar-rows.ts +18 -5
  170. package/src/plugins/builtin/chat/sidebar.tsx +6 -1
  171. package/src/plugins/builtin/chat/unread-badge.ts +38 -7
  172. package/src/plugins/builtin/cloud/askg/manifest.ts +4 -3
  173. package/src/plugins/builtin/cloud/askg/pane-attachment.ts +2 -3
  174. package/src/plugins/builtin/cloud/terminal-relay/manifest.ts +16 -5
  175. package/src/plugins/builtin/commodities-energy/index.tsx +23 -4
  176. package/src/plugins/builtin/commodity-balances/client.ts +51 -193
  177. package/src/plugins/builtin/commodity-balances/headless.ts +10 -3
  178. package/src/plugins/builtin/commodity-balances/model.ts +5 -180
  179. package/src/plugins/builtin/commodity-balances/pane.tsx +3 -0
  180. package/src/plugins/builtin/custom-view/index.tsx +1 -1
  181. package/src/plugins/builtin/custom-view/loader.ts +57 -7
  182. package/src/plugins/builtin/custom-view/sources.ts +146 -0
  183. package/src/plugins/builtin/custom-view/view-spec.ts +37 -1
  184. package/src/plugins/builtin/earnings/board-model.ts +33 -3
  185. package/src/plugins/builtin/earnings/board-table.ts +59 -14
  186. package/src/plugins/builtin/earnings/board.tsx +36 -3
  187. package/src/plugins/builtin/earnings/headless.ts +24 -5
  188. package/src/plugins/builtin/earnings/history-model.ts +9 -1
  189. package/src/plugins/builtin/earnings/history-table.ts +16 -5
  190. package/src/plugins/builtin/earnings/history.tsx +40 -10
  191. package/src/plugins/builtin/earnings/index.tsx +3 -1
  192. package/src/plugins/builtin/earnings/no-earnings.ts +52 -0
  193. package/src/plugins/builtin/econ-statistics/detail.tsx +4 -1
  194. package/src/plugins/builtin/econ-statistics/headless.ts +5 -0
  195. package/src/plugins/builtin/econ-statistics/pane.tsx +1 -1
  196. package/src/plugins/builtin/econ-statistics/view.ts +4 -0
  197. package/src/plugins/builtin/futures/model.ts +5 -4
  198. package/src/plugins/builtin/futures/series.ts +38 -1
  199. package/src/plugins/builtin/futures-curve/client.ts +4 -4
  200. package/src/plugins/builtin/futures-curve/headless.ts +33 -8
  201. package/src/plugins/builtin/futures-curve/index.tsx +4 -4
  202. package/src/plugins/builtin/futures-curve/model.ts +109 -6
  203. package/src/plugins/builtin/futures-curve/pane.tsx +24 -13
  204. package/src/plugins/builtin/insider/headless.ts +74 -4
  205. package/src/plugins/builtin/insider/index.tsx +99 -5
  206. package/src/plugins/builtin/insider/signals-client.ts +69 -0
  207. package/src/plugins/builtin/insider/signals-model.ts +146 -0
  208. package/src/plugins/builtin/insider/signals.tsx +176 -0
  209. package/src/plugins/builtin/iv-history/client.ts +41 -2
  210. package/src/plugins/builtin/iv-history/headless.ts +24 -16
  211. package/src/plugins/builtin/iv-history/screen-pane.tsx +25 -7
  212. package/src/plugins/builtin/jodi/client.ts +42 -205
  213. package/src/plugins/builtin/jodi/headless.ts +13 -4
  214. package/src/plugins/builtin/jodi/model.ts +18 -43
  215. package/src/plugins/builtin/jodi/pane.tsx +5 -0
  216. package/src/plugins/builtin/kelly-sizer/headless.ts +1 -1
  217. package/src/plugins/builtin/kelly-sizer/history.ts +5 -3
  218. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  219. package/src/plugins/builtin/macro-day/release-table.ts +3 -3
  220. package/src/plugins/builtin/market-movers/client.ts +32 -6
  221. package/src/plugins/builtin/market-movers/headless.ts +7 -2
  222. package/src/plugins/builtin/market-movers/model.ts +64 -3
  223. package/src/plugins/builtin/market-movers/screener.ts +20 -9
  224. package/src/plugins/builtin/market-valuation/detail.tsx +7 -2
  225. package/src/plugins/builtin/market-valuation/headless.ts +17 -4
  226. package/src/plugins/builtin/market-valuation/pane.tsx +7 -3
  227. package/src/plugins/builtin/market-valuation/view.ts +10 -0
  228. package/src/plugins/builtin/mna/model.ts +2 -1
  229. package/src/plugins/builtin/options-positioning/client.ts +10 -0
  230. package/src/plugins/builtin/options-positioning/headless.ts +88 -13
  231. package/src/plugins/builtin/options-positioning/index.tsx +2 -2
  232. package/src/plugins/builtin/options-positioning/model.ts +47 -0
  233. package/src/plugins/builtin/options-positioning/pane.tsx +55 -26
  234. package/src/plugins/builtin/options-scenario/charts.tsx +13 -4
  235. package/src/plugins/builtin/options-scenario/client.ts +111 -17
  236. package/src/plugins/builtin/options-scenario/collar.ts +141 -0
  237. package/src/plugins/builtin/options-scenario/csp.ts +1 -1
  238. package/src/plugins/builtin/options-scenario/freshness.ts +27 -0
  239. package/src/plugins/builtin/options-scenario/headless.ts +28 -12
  240. package/src/plugins/builtin/options-scenario/index.tsx +1 -1
  241. package/src/plugins/builtin/options-scenario/model.ts +55 -5
  242. package/src/plugins/builtin/options-scenario/pane.tsx +24 -5
  243. package/src/plugins/builtin/perps/client.ts +77 -6
  244. package/src/plugins/builtin/perps/headless.ts +25 -14
  245. package/src/plugins/builtin/perps/index.tsx +1 -1
  246. package/src/plugins/builtin/perps/model.ts +32 -0
  247. package/src/plugins/builtin/perps/pane.tsx +5 -4
  248. package/src/plugins/builtin/portfolio-list/allocation.ts +9 -4
  249. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +39 -20
  250. package/src/plugins/builtin/portfolio-list/cli/render.ts +5 -3
  251. package/src/plugins/builtin/portfolio-list/cli/shared.ts +15 -7
  252. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +1 -1
  253. package/src/plugins/builtin/portfolio-list/column-values.ts +4 -3
  254. package/src/plugins/builtin/portfolio-list/command-bar.ts +1 -1
  255. package/src/plugins/builtin/portfolio-list/headless.ts +10 -3
  256. package/src/plugins/builtin/portfolio-list/pane/index.tsx +36 -4
  257. package/src/plugins/builtin/portfolio-list/pane/remove-ticker.ts +1 -1
  258. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +35 -6
  259. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +2 -1
  260. package/src/plugins/builtin/portfolio-list/settings.ts +22 -0
  261. package/src/plugins/builtin/portfolio-list/since-last-seen.ts +197 -0
  262. package/src/plugins/builtin/portfolio-list/summary/totals.ts +1 -1
  263. package/src/plugins/builtin/portfolio-list/table.tsx +14 -2
  264. package/src/plugins/builtin/rate-path/model.ts +1 -1
  265. package/src/plugins/builtin/rate-path/pane.tsx +2 -1
  266. package/src/plugins/builtin/research/corporate-actions-pane.tsx +14 -5
  267. package/src/plugins/builtin/research/event-model.ts +41 -49
  268. package/src/plugins/builtin/research/event-sources.ts +1 -1
  269. package/src/plugins/builtin/research/headless.ts +1 -1
  270. package/src/plugins/builtin/research/relative-valuation-model.ts +6 -0
  271. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  272. package/src/plugins/builtin/reverse-dcf/headless.ts +25 -9
  273. package/src/plugins/builtin/reverse-dcf/index.tsx +4 -1
  274. package/src/plugins/builtin/reverse-dcf/model.ts +39 -5
  275. package/src/plugins/builtin/reverse-dcf/pane.tsx +16 -6
  276. package/src/plugins/builtin/sectors/client.ts +46 -19
  277. package/src/plugins/builtin/sectors/sector-model.ts +22 -10
  278. package/src/plugins/builtin/shared/earnings-report.ts +48 -1
  279. package/src/plugins/builtin/shared/history-span.ts +55 -0
  280. package/src/plugins/builtin/shared/report-freshness.ts +20 -11
  281. package/src/plugins/builtin/shared/series-detail-chart.tsx +5 -2
  282. package/src/plugins/builtin/shared/series-list-detail.tsx +7 -3
  283. package/src/plugins/builtin/shared/ticker-surface.ts +16 -2
  284. package/src/plugins/builtin/shared/use-quote-board.ts +12 -5
  285. package/src/plugins/builtin/themes/headless.ts +15 -2
  286. package/src/plugins/builtin/thirteenf/api.ts +60 -43
  287. package/src/plugins/builtin/thirteenf/data.ts +76 -35
  288. package/src/plugins/builtin/thirteenf/headless.ts +2 -0
  289. package/src/plugins/builtin/thirteenf/pane.tsx +10 -2
  290. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +85 -17
  291. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +92 -0
  292. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +26 -8
  293. package/src/plugins/builtin/ticker-detail/headless-schema.ts +4 -2
  294. package/src/plugins/builtin/ticker-detail/headless.ts +95 -22
  295. package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
  296. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +1 -1
  297. package/src/plugins/builtin/world-venue-map/headless.ts +17 -3
  298. package/src/plugins/builtin/yield-curve/curves.ts +15 -1
  299. package/src/plugins/builtin/yield-curve/headless.ts +17 -7
  300. package/src/plugins/builtin/yield-curve/history.ts +2 -0
  301. package/src/plugins/builtin/yield-curve/index.tsx +23 -18
  302. package/src/plugins/builtin/yield-curve/series.ts +19 -9
  303. package/src/plugins/builtin/yield-curve/treasury-data.ts +2 -0
  304. package/src/plugins/builtin/yield-curve/world.ts +4 -0
  305. package/src/plugins/registry/host-actions.ts +3 -0
  306. package/src/{plugins/builtin/portfolio-list → portfolio}/mutations.ts +6 -6
  307. package/src/portfolio/valuation-basis.ts +4 -21
  308. package/src/public/__snapshots__/public-api.test.ts.snap +2 -0
  309. package/src/remote/controller.ts +27 -7
  310. package/src/remote/schema.ts +25 -9
  311. package/src/renderers/browser/app-services.ts +7 -1
  312. package/src/renderers/browser/config-host.ts +55 -2
  313. package/src/renderers/browser/first-screen.ts +193 -0
  314. package/src/renderers/browser/main.tsx +59 -7
  315. package/src/renderers/cloudflare/http-proxy.ts +21 -13
  316. package/src/renderers/dom/desktop/controls.tsx +2 -0
  317. package/src/renderers/dom/desktop/query-bar.tsx +1 -1
  318. package/src/renderers/dom/dom-ui-host.tsx +2 -0
  319. package/src/renderers/dom/windowed-list.tsx +201 -0
  320. package/src/renderers/dom/windowed-rows.ts +65 -0
  321. package/src/renderers/electrobun/bun/application-menu/index.ts +1 -0
  322. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -7
  323. package/src/renderers/electrobun/bun/desktop/http-stream.ts +21 -6
  324. package/src/renderers/electrobun/bun/desktop/update.ts +132 -14
  325. package/src/renderers/electrobun/shared/protocol.ts +7 -4
  326. package/src/renderers/electrobun/view/http-fetch.ts +12 -1
  327. package/src/renderers/electrobun/view/update-host.ts +2 -2
  328. package/src/shares/bug-report-dialog.tsx +162 -0
  329. package/src/shares/bug-report-flow.tsx +84 -0
  330. package/src/shares/bug-report.ts +42 -0
  331. package/src/shares/capture-flow.ts +19 -0
  332. package/src/shares/capture-preview.tsx +129 -0
  333. package/src/shares/chat.ts +10 -1
  334. package/src/shares/dialog.tsx +16 -91
  335. package/src/shares/flow.tsx +12 -16
  336. package/src/shares/personal-pane.ts +35 -1
  337. package/src/sources/listing-symbols.ts +8 -4
  338. package/src/state/app/context/index.tsx +13 -0
  339. package/src/state/app/tab-cursor.ts +85 -0
  340. package/src/sync/core-contributors.ts +206 -59
  341. package/src/telemetry/usage-counts.ts +16 -0
  342. package/src/test-support/config-store.ts +31 -0
  343. package/src/test-support/moved-symbols.ts +64 -0
  344. package/src/theme/colors.ts +30 -0
  345. package/src/tickers/listing-fate.ts +242 -0
  346. package/src/tickers/open-target.ts +61 -11
  347. package/src/tickers/search/index.ts +45 -3
  348. package/src/tickers/search/ranking.ts +19 -2
  349. package/src/tickers/search/types.ts +3 -0
  350. package/src/tickers/search/upsert.ts +22 -7
  351. package/src/tickers/ticker-changes.ts +26 -9
  352. package/src/time-series/bar-spacing.ts +19 -0
  353. package/src/time-series/chart-data.ts +37 -13
  354. package/src/time-series/field-catalog.ts +5 -2
  355. package/src/time-series/forward-valuation.ts +10 -7
  356. package/src/time-series/fundamentals.ts +49 -11
  357. package/src/time-series/hooks.ts +2 -0
  358. package/src/time-series/live-bars.ts +3 -1
  359. package/src/time-series/parsed-history-cache.ts +2 -0
  360. package/src/time-series/price-units.ts +69 -0
  361. package/src/time-series/resolve.ts +71 -11
  362. package/src/time-series/session-bar.ts +138 -0
  363. package/src/time-series/spec.ts +20 -3
  364. package/src/time-series/studies.ts +211 -12
  365. package/src/time-series/transforms.ts +2 -0
  366. package/src/time-series/types.ts +11 -1
  367. package/src/time-series/valuation-price.ts +13 -0
  368. package/src/types/config.ts +24 -1
  369. package/src/types/financials.ts +2 -0
  370. package/src/types/headless.ts +27 -0
  371. package/src/types/plugin.ts +7 -0
  372. package/src/ui/host.tsx +5 -0
  373. package/src/updater/channel.ts +51 -0
  374. package/src/updater/github-releases.ts +85 -16
  375. package/src/updater/index.ts +128 -17
  376. package/src/utils/asset-category.ts +20 -0
  377. package/src/utils/datetime-format.ts +7 -1
  378. package/src/utils/decimal-comma.ts +83 -3
  379. package/src/utils/fundamentals.ts +7 -2
  380. package/src/utils/http-proxy-response.ts +66 -9
  381. package/src/utils/plugin-proxy-hosts.json +1 -5
  382. package/src/utils/semver.ts +47 -2
  383. package/src/utils/typo-distance.ts +22 -0
  384. package/src/plugins/builtin/shared/zip-text.ts +0 -46
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gloomberb",
3
- "version": "0.17.4",
3
+ "version": "0.17.6",
4
4
  "packageManager": "bun@1.3.11",
5
5
  "description": "Market research and portfolio tracker for the terminal",
6
6
  "module": "src/public/index.ts",
@@ -83,6 +83,7 @@
83
83
  "benchmark:tui:compare": "bun run scripts/benchmark-tui-compare.ts",
84
84
  "benchmark:tui:memory": "bun run scripts/benchmark-tui-memory.ts",
85
85
  "check:pane-chrome": "bun run scripts/pane-chrome/checks.ts",
86
+ "check:chat-mentions": "bun scripts/pane-chrome/chat-mentions.ts",
86
87
  "check:pane-screenshot": "bun run scripts/pane-chrome/screenshot.ts",
87
88
  "check:pane-rest": "bun run scripts/pane-chrome/rest-diff.ts",
88
89
  "check:pane-layout": "bun run scripts/pane-chrome/layout-sweep.ts",
@@ -111,6 +112,7 @@
111
112
  "gloom-polls": "github:gloom-sh/gloom-polls",
112
113
  "gloom-prediction-markets": "github:gloom-sh/gloom-prediction-markets",
113
114
  "happy-dom": "^20.11.1",
115
+ "playwright-core": "1.60.0",
114
116
  "png-to-ico": "^2.1.8",
115
117
  "rcedit": "^4.0.1",
116
118
  "typescript": "^5.9.2",
@@ -53,6 +53,86 @@ index c26c2c977ee1fc25a8dc144b85bac8626a45f572..8920a2905380228c23cba66c20d9bfd1
53
53
  frame: {
54
54
  width: this.frame.width,
55
55
  height: this.frame.height,
56
+ diff --git a/dist/api/bun/core/Updater.ts b/dist/api/bun/core/Updater.ts
57
+ index 39daf6d341780af8e30c2c50327162863f5977ec..bf4a4b615f84904759d4bcd57be0d899c9464a5f 100644
58
+ --- a/dist/api/bun/core/Updater.ts
59
+ +++ b/dist/api/bun/core/Updater.ts
60
+ @@ -132,6 +132,19 @@ let updateInfo: {
61
+ error: string;
62
+ };
63
+
64
+ +// Where updates are fetched from when it is not the channel and baseUrl that
65
+ +// version.json names. Only the download URLs read it: the app data folder and
66
+ +// the "dev" check keep the installed build's own channel.
67
+ +let updateSource: { channel: string; baseUrl: string } | null = null;
68
+ +
69
+ +function updateSourceChannel(): string {
70
+ + return updateSource?.channel ?? localInfo.channel;
71
+ +}
72
+ +
73
+ +function updateSourceBaseUrl(): string {
74
+ + return updateSource?.baseUrl ?? localInfo.baseUrl;
75
+ +}
76
+ +
77
+ function cleanupExtractionFolder(
78
+ extractionFolder: string,
79
+ keepTarHash: string,
80
+ @@ -176,6 +189,12 @@ const Updater = {
81
+ onStatusChangeCallback = callback;
82
+ },
83
+
84
+ + // Checks and downloads from `source` (a channel prefix and the URL its
85
+ + // files sit under) until it is set again; null goes back to version.json.
86
+ + setUpdateSource: (source: { channel: string; baseUrl: string } | null) => {
87
+ + updateSource = source;
88
+ + },
89
+ +
90
+ // todo: allow switching channels, by default will check the current channel
91
+ checkForUpdate: async () => {
92
+ emitStatus("checking", "Checking for updates...");
93
+ @@ -196,11 +215,11 @@ const Updater = {
94
+
95
+ const cacheBuster = Math.random().toString(36).substring(7);
96
+ const platformPrefix = getPlatformPrefix(
97
+ - localInfo.channel,
98
+ + updateSourceChannel(),
99
+ currentOS,
100
+ currentArch,
101
+ );
102
+ - const updateInfoUrl = `${localInfo.baseUrl.replace(/\/+$/, "")}/${platformPrefix}-update.json?${cacheBuster}`;
103
+ + const updateInfoUrl = `${updateSourceBaseUrl().replace(/\/+$/, "")}/${platformPrefix}-update.json?${cacheBuster}`;
104
+
105
+ try {
106
+ const updateInfoResponse = await fetch(updateInfoUrl);
107
+ @@ -329,11 +348,11 @@ const Updater = {
108
+
109
+ // check if there's a patch file for it
110
+ const platformPrefix = getPlatformPrefix(
111
+ - localInfo.channel,
112
+ + updateSourceChannel(),
113
+ currentOS,
114
+ currentArch,
115
+ );
116
+ - const patchUrl = `${localInfo.baseUrl.replace(/\/+$/, "")}/${platformPrefix}-${currentHash}.patch`;
117
+ + const patchUrl = `${updateSourceBaseUrl().replace(/\/+$/, "")}/${platformPrefix}-${currentHash}.patch`;
118
+ emitStatus(
119
+ "fetching-patch",
120
+ `Checking for patch: ${currentHash.slice(0, 8)}`,
121
+ @@ -612,12 +631,12 @@ const Updater = {
122
+
123
+ const cacheBuster = Math.random().toString(36).substring(7);
124
+ const platformPrefix = getPlatformPrefix(
125
+ - localInfo.channel,
126
+ + updateSourceChannel(),
127
+ currentOS,
128
+ currentArch,
129
+ );
130
+ const tarballName = getTarballFileName(appFileName, currentOS);
131
+ - const urlToLatestTarball = `${localInfo.baseUrl.replace(/\/+$/, "")}/${platformPrefix}-${tarballName}`;
132
+ + const urlToLatestTarball = `${updateSourceBaseUrl().replace(/\/+$/, "")}/${platformPrefix}-${tarballName}`;
133
+ const prevVersionCompressedTarballPath = join(
134
+ appDataFolder,
135
+ "self-extraction",
56
136
  diff --git a/dist/api/bun/proc/native.ts b/dist/api/bun/proc/native.ts
57
137
  index f0f6c2532ba2eb467ec1d452a25c835b6db72661..ae77cf58d32392ad4cc8e2103ac7ed27baae8bea 100644
58
138
  --- a/dist/api/bun/proc/native.ts
@@ -12,6 +12,7 @@ import type {
12
12
  ChatAttachment,
13
13
  ChatChannel,
14
14
  ChatChannelState,
15
+ ChatMentionCandidate,
15
16
  ChatDiscordLink,
16
17
  ChatMessage,
17
18
  ChatNotification,
@@ -118,6 +119,14 @@ export class CloudChatApi {
118
119
  this.emitAttachmentSupport();
119
120
  }
120
121
 
122
+ getMentionCandidates(channelId: string): Promise<ChatMentionCandidate[]> {
123
+ return this.request(`/chat/channels/${encodeURIComponent(channelId)}/mention-candidates`);
124
+ }
125
+
126
+ subscribeMentionCandidatesChanged(listener: () => void): () => void {
127
+ return this.options.socket.subscribeChatMentionCandidatesChanged(listener);
128
+ }
129
+
121
130
  async getChannels(): Promise<ChatChannel[]> {
122
131
  const channels =
123
132
  await this.request<ChatChannel[]>("/chat/channels");
@@ -0,0 +1,77 @@
1
+ /** Whether a board is whole, missing a part, or not loaded on the server yet. */
2
+ type BalanceBoardStatus = "available" | "partial" | "unavailable";
3
+
4
+ /** One country's crude or natural gas balance, as GET /cloud/jodi/board serves it. */
5
+ export interface JodiBalanceRow {
6
+ country: string;
7
+ /** The month the figures are for, YYYY-MM: each country's own latest report. Null when it never reported one. */
8
+ month: string | null;
9
+ production: number | null;
10
+ demand: number | null;
11
+ imports: number | null;
12
+ exports: number | null;
13
+ stocks: number | null;
14
+ unit: string;
15
+ }
16
+
17
+ export interface JodiBalanceBoard {
18
+ /** The newest month on the board, YYYY-MM. */
19
+ month: string;
20
+ commodity: "oil" | "gas";
21
+ rows: JodiBalanceRow[];
22
+ }
23
+
24
+ /** The Oil & Gas board: JODI's monthly crude and natural gas balances. */
25
+ export interface JodiBoardPayload {
26
+ source: "JODI";
27
+ status: BalanceBoardStatus;
28
+ /** When JODI last confirmed the oldest copy on the board, ISO. */
29
+ checkedAt: string | null;
30
+ /** The server has not been able to read JODI for days: these are the last copies it read. */
31
+ stale: boolean;
32
+ gaps: string[];
33
+ oil: JodiBalanceBoard | null;
34
+ gas: JodiBalanceBoard | null;
35
+ }
36
+
37
+ /** One crop, country and attribute of the latest market year, as GET /cloud/psd/board serves it. */
38
+ export interface PsdBalanceRow {
39
+ id: string;
40
+ crop: string;
41
+ country: string;
42
+ attribute: string;
43
+ value: number;
44
+ unit: string;
45
+ /** Market year, 2026/27. */
46
+ year: string;
47
+ cropOrder: number;
48
+ countryOrder: number;
49
+ attributeOrder: number;
50
+ }
51
+
52
+ /** A season-average farm price forecast. */
53
+ export interface PsdPriceForecast {
54
+ id: string;
55
+ crop: string;
56
+ forecast: number;
57
+ unit: string;
58
+ season: string;
59
+ cropOrder: number;
60
+ seasonYear: number;
61
+ }
62
+
63
+ /** The Crops board: USDA's world crop balances and season-average price forecasts. */
64
+ export interface PsdBoardPayload {
65
+ source: "USDA";
66
+ status: BalanceBoardStatus;
67
+ checkedAt: string | null;
68
+ stale: boolean;
69
+ gaps: string[];
70
+ /** YYYY-MM of the newest estimate on the board: the monthly supply and demand report that set it. */
71
+ month: string | null;
72
+ marketYear: string | null;
73
+ rows: PsdBalanceRow[];
74
+ prices: PsdPriceForecast[];
75
+ /** The model forecast date of the prices, YYYY-MM-DD. */
76
+ pricesDate: string | null;
77
+ }
@@ -35,10 +35,12 @@ import type { CloudCurveHistory, CloudCurveId, CloudCurveView, CloudWorldCurves
35
35
  import type { CdxBoardPayload, CloudCreditBoardParams, SovrBoardPayload } from "./credit-boards";
36
36
  import type { ShortVolumePayload, ShortVolumeScope } from "./short-volume";
37
37
  import type { BeneficialOwnersPayload, CloudBeneficialOwnersParams } from "./beneficial-owners";
38
+ import type { CloudInsiderSignalsParams, InsiderSignalsPayload } from "./insider-signals";
38
39
  import type { SocialMentionDayPosts, SocialMentionsPayload, SocialMentionsRange } from "./social-mentions";
39
40
  import type { FuturesContractTermsPayload, FuturesCurveAsOfPayload, FuturesCurvePayload } from "./futures-curve";
40
41
  import type { CotBoardPayload, CotContractPayload, CotFamily, CotClass } from "./cot";
41
42
  import type { DoeBoardPayload, DoePricesPayload } from "./doe";
43
+ import type { JodiBoardPayload, PsdBoardPayload } from "./commodity-balances";
42
44
  import type { CarbonBoard, CarbonHistory, EuropePowerBoard, EuropePowerHistory, GasStorageBoard, GasStorageHistory, GasStorageSeasonal } from "./europe-energy";
43
45
  import type { GpuBoardPayload, GpuEventsPayload, GpuHistoryPayload, GpuHistoryQuery } from "./gpu";
44
46
  import type { CpiBoardPayload } from "./cpi";
@@ -81,6 +83,7 @@ import {
81
83
  cloudExchangeRatePath,
82
84
  cloudSec13FPath,
83
85
  cloudSecBeneficialOwnersPath,
86
+ cloudSecInsiderSignalsPath,
84
87
  cloudSecFilingContentPath,
85
88
  cloudSecFilingDocumentsPath,
86
89
  cloudSecFilingsPath,
@@ -143,6 +146,7 @@ import type {
143
146
  CloudMarketResponse,
144
147
  CloudMarketScreenerCategory,
145
148
  CloudMarketScreenerPayload,
149
+ CloudMoverSession,
146
150
  CloudNewsListResponse,
147
151
  CloudNewsPayload,
148
152
  CloudSavedSearch,
@@ -283,8 +287,10 @@ export class CloudDataApi {
283
287
  return this.request(`/news/article-summary?${new URLSearchParams({ url })}`);
284
288
  }
285
289
 
286
- getMarketMovers(category: "day_gainers" | "day_losers" | "most_actives", count = 25, refresh = false): Promise<CloudMarketResponse<MarketMoversPayload>> {
290
+ /** `session` is what Gainers and Losers are ranked by (see CloudMoverSession). */
291
+ getMarketMovers(category: "day_gainers" | "day_losers" | "most_actives", count = 25, refresh = false, session?: CloudMoverSession): Promise<CloudMarketResponse<MarketMoversPayload>> {
287
292
  const params = new URLSearchParams({ category, count: String(count), mode: refresh ? "refresh" : "cache-first" });
293
+ if (session && category !== "most_actives") params.set("session", session);
288
294
  return this.request(`/market/movers?${params}`);
289
295
  }
290
296
 
@@ -300,12 +306,17 @@ export class CloudDataApi {
300
306
  return this.requestMarketSymbol("/market/dividends", symbol, exchange);
301
307
  }
302
308
 
303
- /** Gainers, losers and most active, or the pre-market, after-hours and gap lists with a `side`. */
309
+ /**
310
+ * Gainers, losers and most active, or the pre-market, after-hours and gap
311
+ * lists with a `side`. `session` is what Gainers and Losers are ranked by
312
+ * (see CloudMoverSession).
313
+ */
304
314
  async getCloudMarketScreener<Category extends CloudMarketScreenerCategory | CloudSessionMoversCategory>(
305
315
  category: Category,
306
316
  count = 25,
307
317
  mode: "cache-first" | "refresh" = "cache-first",
308
318
  side?: CloudSessionMoversSide,
319
+ session?: CloudMoverSession,
309
320
  ): Promise<CloudMarketResponse<Category extends CloudSessionMoversCategory ? CloudSessionMoversPayload : CloudMarketScreenerPayload>> {
310
321
  const requestedCount = Number.isFinite(count) ? Math.round(count) : 25;
311
322
  const params = new URLSearchParams({
@@ -314,6 +325,7 @@ export class CloudDataApi {
314
325
  mode,
315
326
  });
316
327
  if (side && isSessionMoversCategory(category)) params.set("side", side);
328
+ if (session && (category === "gainers" || category === "losers")) params.set("session", session);
317
329
  return this.request(`/market/screener?${params.toString()}`);
318
330
  }
319
331
 
@@ -590,6 +602,14 @@ export class CloudDataApi {
590
602
  return this.request<DoePricesPayload>("/cloud/doe/prices");
591
603
  }
592
604
 
605
+ getCloudJodiBoard(signal?: AbortSignal): Promise<JodiBoardPayload> {
606
+ return this.request<JodiBoardPayload>("/cloud/jodi/board", { signal });
607
+ }
608
+
609
+ getCloudPsdBoard(signal?: AbortSignal): Promise<PsdBoardPayload> {
610
+ return this.request<PsdBoardPayload>("/cloud/psd/board", { signal });
611
+ }
612
+
593
613
  async getCloudAttention(window: AttentionWindow = "now", symbol?: string): Promise<AttentionPayload> {
594
614
  const path = symbol ? `/cloud/attention/${encodeURIComponent(symbol)}` : "/cloud/attention";
595
615
  return this.request<AttentionPayload>(`${path}?window=${window}`);
@@ -934,6 +954,15 @@ export class CloudDataApi {
934
954
  });
935
955
  }
936
956
 
957
+ async getCloudSecInsiderSignals(
958
+ params: CloudInsiderSignalsParams = {},
959
+ options?: { signal?: AbortSignal },
960
+ ): Promise<InsiderSignalsPayload> {
961
+ return this.request<InsiderSignalsPayload>(cloudSecInsiderSignalsPath(params), {
962
+ signal: options?.signal,
963
+ });
964
+ }
965
+
937
966
  async getCloudSecFilingDocuments(
938
967
  params: CloudSecFilingParams,
939
968
  ): Promise<CloudSecDocumentsResponse> {
@@ -953,8 +982,11 @@ export class CloudDataApi {
953
982
  async getCloudSec13F(
954
983
  path: string,
955
984
  params: Record<string, string | number | undefined> = {},
985
+ options?: { signal?: AbortSignal },
956
986
  ): Promise<unknown> {
957
- return this.request<unknown>(cloudSec13FPath(path, params));
987
+ return this.request<unknown>(cloudSec13FPath(path, params), {
988
+ signal: options?.signal,
989
+ });
958
990
  }
959
991
 
960
992
  /**
@@ -27,6 +27,8 @@ interface EarningsRealizedMove {
27
27
  afterClose: number;
28
28
  }
29
29
 
30
+ type EarningsActualSource = "calendar" | "press-release";
31
+
30
32
  export interface EarningsReport {
31
33
  symbol: string;
32
34
  name: string | null;
@@ -43,6 +45,12 @@ export interface EarningsReport {
43
45
  /** For an upcoming report, the consensus every earnings view shows, with `epsAnalysts` behind it. */
44
46
  epsEstimate: number | null;
45
47
  epsActual: number | null;
48
+ /**
49
+ * Where `epsActual` comes from: `press-release` is the company's own
50
+ * release, read the day it reports and provisional until the calendar's
51
+ * figure replaces it. Absent from older servers.
52
+ */
53
+ epsActualSource?: EarningsActualSource | null;
46
54
  epsAnalysts: number | null;
47
55
  revenueEstimate: number | null;
48
56
  revenueAnalysts: number | null;
@@ -69,6 +77,8 @@ export interface EarningsCalendarPayload {
69
77
 
70
78
  interface EarningsHistoryReport extends EarningsReport {
71
79
  revenueActual: number | null;
80
+ /** `press-release` until the statements have the quarter; absent from older servers. */
81
+ revenueActualSource?: "statements" | "press-release" | null;
72
82
  }
73
83
 
74
84
  export interface EarningsHistoryPayload {
@@ -645,6 +645,8 @@ class GloomApiClient {
645
645
  getChannels = this.chat.getChannels.bind(this.chat);
646
646
  getChatPresence = this.chat.getPresence.bind(this.chat);
647
647
  getChatState = this.chat.getState.bind(this.chat);
648
+ getChatMentionCandidates = this.chat.getMentionCandidates.bind(this.chat);
649
+ subscribeChatMentionCandidatesChanged = this.chat.subscribeMentionCandidatesChanged.bind(this.chat);
648
650
  updateChatChannelState = this.chat.updateChannelState.bind(this.chat);
649
651
  markChatNotificationsDelivered = this.chat.markNotificationsDelivered.bind(this.chat);
650
652
  getUnseenChatNotificationIds = this.chat.getUnseenNotificationIds.bind(this.chat);
@@ -826,6 +828,8 @@ class GloomApiClient {
826
828
  getCloudAward = this.data.getCloudAward.bind(this.data);
827
829
  getCloudDoeBoard = this.data.getCloudDoeBoard.bind(this.data);
828
830
  getCloudDoePrices = this.data.getCloudDoePrices.bind(this.data);
831
+ getCloudJodiBoard = this.data.getCloudJodiBoard.bind(this.data);
832
+ getCloudPsdBoard = this.data.getCloudPsdBoard.bind(this.data);
829
833
  getCloudAttention = this.data.getCloudAttention.bind(this.data);
830
834
  getCloudPowerBoard = this.data.getCloudPowerBoard.bind(this.data);
831
835
  getCloudPowerHistory = this.data.getCloudPowerHistory.bind(this.data);
@@ -864,6 +868,7 @@ class GloomApiClient {
864
868
  getRiskReport = this.data.getRiskReport.bind(this.data);
865
869
  getCloudSecFilings = this.data.getCloudSecFilings.bind(this.data);
866
870
  getCloudSecBeneficialOwners = this.data.getCloudSecBeneficialOwners.bind(this.data);
871
+ getCloudSecInsiderSignals = this.data.getCloudSecInsiderSignals.bind(this.data);
867
872
  getCloudSecFilingDocuments = this.data.getCloudSecFilingDocuments.bind(this.data);
868
873
  getCloudSecFilingContent = this.data.getCloudSecFilingContent.bind(this.data);
869
874
  getCloudSec13F = this.data.getCloudSec13F.bind(this.data);
@@ -0,0 +1,75 @@
1
+ /**
2
+ * Companies ranked by recent open-market insider buying, as
3
+ * `GET /cloud/sec/insider-signals` returns them. Only Form 4 purchases (code
4
+ * P) by officers, directors and 10% owners count. Dates are `YYYY-MM-DD`,
5
+ * values USD.
6
+ */
7
+
8
+ type InsiderSignalRole =
9
+ | "CEO"
10
+ | "CFO"
11
+ | "COO"
12
+ | "President"
13
+ | "Chair"
14
+ | "Officer"
15
+ | "Director"
16
+ | "10% owner";
17
+
18
+ interface InsiderSignalBuyer {
19
+ name: string;
20
+ role: InsiderSignalRole;
21
+ value: number;
22
+ /** Days the insider bought in the window. */
23
+ buys: number;
24
+ lastBuy: string;
25
+ }
26
+
27
+ export interface InsiderSignalRow {
28
+ ticker: string;
29
+ issuerCik: string;
30
+ company: string;
31
+ /** 0 to 100. */
32
+ score: number;
33
+ /** Why the company ranks, strongest first, in plain words. */
34
+ reasons: string[];
35
+ /** Distinct insiders who bought in the window. */
36
+ insiders: number;
37
+ /** Insider buy days in the window. */
38
+ buys: number;
39
+ value: number;
40
+ shares: number;
41
+ firstBuy: string;
42
+ lastBuy: string;
43
+ lastFiled: string;
44
+ signals: {
45
+ clusterInsiders: number;
46
+ repeatBuys: number;
47
+ usualRatio: number | null;
48
+ stakeIncreasePct: number | null;
49
+ newPosition: boolean;
50
+ };
51
+ /** The biggest buyers, up to five. */
52
+ buyers: InsiderSignalBuyer[];
53
+ }
54
+
55
+ export interface InsiderSignalsPayload {
56
+ /** Days of buying each row covers, ending `through`. */
57
+ window: number;
58
+ through: string;
59
+ /** The newest filing day read. */
60
+ asOf: string | null;
61
+ /**
62
+ * Days whose filings have all been read. `missingDays`: weekdays since
63
+ * `from` the server no longer reads on its own and has not read whole, a gap
64
+ * an outage left (left out by servers that predate it).
65
+ */
66
+ coverage: { from: string | null; through: string | null; missingDays?: string[] };
67
+ rows: InsiderSignalRow[];
68
+ }
69
+
70
+ export interface CloudInsiderSignalsParams {
71
+ /** Days, 1 to 90; the server reads 30 when left out. */
72
+ window?: number;
73
+ /** Rows, 1 to 200; the server reads 50 when left out. */
74
+ limit?: number;
75
+ }
@@ -19,7 +19,7 @@ export interface ScreenerQuote {
19
19
  lastUpdated?: number;
20
20
  /** Session-fixed, so the price column reads its decimals from it rather than from each tick. */
21
21
  previousClose?: number;
22
- /** The quote's sessions beside the live print the list ranks by, when the server sends them. */
22
+ /** The quote's sessions beside the live print the server ranked the row by, when the server sends them. */
23
23
  sessions?: CloudMoverSessionFields;
24
24
  }
25
25
 
@@ -220,6 +220,7 @@ export function normalizeChatChannel(
220
220
  ...channel,
221
221
  kind: channel.kind ?? fallbackKind,
222
222
  created_at: normalizeTimestamp(channel.created_at),
223
+ ...(typeof channel.lastMessageAt === "string" ? { lastMessageAt: normalizeTimestamp(channel.lastMessageAt) } : {}),
223
224
  ...(channel.dmUser ? { dmUser: normalizeChatUser(channel.dmUser) } : {}),
224
225
  ...(Array.isArray(channel.members) ? { members: channel.members.map(normalizeChatUser) } : {}),
225
226
  };
@@ -7,6 +7,7 @@ import { isUsListingExchange, normalizeSymbol, parsePublicTickerKey, publicTicke
7
7
  import { nonUsSecListingVenue } from "../utils/sec";
8
8
  import type { HistoryRetention } from "../sources/history-retention";
9
9
  import type { CloudBeneficialOwnersParams } from "./beneficial-owners";
10
+ import type { CloudInsiderSignalsParams } from "./insider-signals";
10
11
  import type { RevenueBreakdownView } from "./revenue-breakdown";
11
12
 
12
13
  export type CloudHistoryParams = {
@@ -444,6 +445,14 @@ export function cloudSecBeneficialOwnersPath(params: CloudBeneficialOwnersParams
444
445
  return appendQuery("/cloud/sec/beneficial-owners", search);
445
446
  }
446
447
 
448
+ /** Companies ranked by recent open-market insider buying. */
449
+ export function cloudSecInsiderSignalsPath(params: CloudInsiderSignalsParams = {}): string {
450
+ const search = new URLSearchParams();
451
+ if (params.window != null) search.set("window", String(params.window));
452
+ if (params.limit != null) search.set("limit", String(params.limit));
453
+ return appendQuery("/cloud/sec/insider-signals", search);
454
+ }
455
+
447
456
  export function cloudSecFilingDocumentsPath(
448
457
  params: CloudSecFilingParams,
449
458
  ): string {
@@ -28,7 +28,10 @@ export type ResearchActivity =
28
28
  | "onboarding_signed_in"
29
29
  | "onboarding_pro_viewed"
30
30
  | "onboarding_completed"
31
- | "onboarding_skipped";
31
+ | "onboarding_skipped"
32
+ // The first action of a web visitor counted in the first-screen experiment,
33
+ // in that first session. Keep in sync with the server's list.
34
+ | "first_screen_engaged";
32
35
  export type ResearchFeature =
33
36
  | "overview"
34
37
  | "chart"
@@ -451,7 +454,10 @@ function webExperimentAttribution(): { experiments?: string } {
451
454
  * visitors on term.gloom.sh with an analytics id (no Do Not Track or Global
452
455
  * Privacy Control) and not driven by automation are asked. See web-experiments.ts.
453
456
  */
454
- export function exposeWebExperiment(experiment: string): Promise<string | null> {
457
+ export function exposeWebExperiment(
458
+ experiment: string,
459
+ { placement, signal }: { placement?: string; signal?: AbortSignal } = {},
460
+ ): Promise<string | null> {
455
461
  if (!researchTelemetryAllowed()) return Promise.resolve(null);
456
462
  if (getCurrentPluginTarget() !== "web") return Promise.resolve(null);
457
463
  try {
@@ -468,7 +474,8 @@ export function exposeWebExperiment(experiment: string): Promise<string | null>
468
474
  attribution: attributionPayload(),
469
475
  experiment: key,
470
476
  variant,
471
- }),
477
+ ...(placement && ACTIVITY_ID.test(placement) ? { placement } : {}),
478
+ }, signal),
472
479
  });
473
480
  } catch {
474
481
  return Promise.resolve(null);
@@ -109,6 +109,7 @@ export class CloudApiSocket {
109
109
  private reconnectDelayMs = 1000;
110
110
 
111
111
  private readonly channelListeners = new Map<string, Set<ChannelListener>>();
112
+ private readonly chatMentionCandidatesListeners = new Set<() => void>();
112
113
  private readonly chatNotificationListeners =
113
114
  new Set<ChatNotificationListener>();
114
115
  private readonly chatChannelStateListeners =
@@ -254,6 +255,11 @@ export class CloudApiSocket {
254
255
  };
255
256
  }
256
257
 
258
+ subscribeChatMentionCandidatesChanged(listener: () => void): () => void {
259
+ this.chatMentionCandidatesListeners.add(listener);
260
+ return () => { this.chatMentionCandidatesListeners.delete(listener); };
261
+ }
262
+
257
263
  /**
258
264
  * The reader's read state or mute changed on another device or window. Like
259
265
  * notifications, it never opens the socket by itself.
@@ -631,6 +637,10 @@ export class CloudApiSocket {
631
637
  return;
632
638
  }
633
639
 
640
+ if (parsed?.type === "chat.mentions_changed") {
641
+ for (const listener of this.chatMentionCandidatesListeners) listener();
642
+ return;
643
+ }
634
644
  if (parsed?.type === "chat.channel_state") {
635
645
  const state = normalizeChatChannelStateFrame(parsed.data);
636
646
  if (state) {
@@ -143,6 +143,21 @@ export interface ChatChannel {
143
143
  teamId?: string;
144
144
  dmUser?: ChatUserSummary | null;
145
145
  members?: ChatUserSummary[];
146
+ /**
147
+ * When the newest message you can see in a direct or group conversation was
148
+ * sent; null when it has none. Absent from channels of other kinds and from
149
+ * servers that do not report it.
150
+ */
151
+ lastMessageAt?: string | null;
152
+ }
153
+
154
+ /** Minimal identities scoped by the server to this channel and existing DMs. */
155
+ export interface ChatMentionCandidate {
156
+ id: string;
157
+ username: string;
158
+ displayName: string | null;
159
+ channelActivityAt: string | null;
160
+ conversationActivityAt: string | null;
146
161
  }
147
162
 
148
163
  export interface ChatChannelState {
@@ -1517,6 +1532,15 @@ export interface CloudMarketBatchPayload<T> {
1517
1532
 
1518
1533
  export type CloudMarketScreenerCategory = "gainers" | "losers" | "most-active";
1519
1534
 
1535
+ /**
1536
+ * What Gloom Cloud selects and ranks Gainers and Losers by: `regular` the last
1537
+ * completed regular session's move, the Chg% a row shows; `extended` the
1538
+ * latest print, after-hours or pre-market included. While the regular session
1539
+ * trades the two are one list. A server that predates the choice ignores it
1540
+ * and ranks by the latest print.
1541
+ */
1542
+ export type CloudMoverSession = "regular" | "extended";
1543
+
1520
1544
  /**
1521
1545
  * A movers row's quote sessions, under the quote's field names and meaning:
1522
1546
  * the session it was read in, the day its change is dated to, the last
@@ -1529,7 +1553,7 @@ export type CloudMoverSessionFields = Partial<Pick<Quote,
1529
1553
  | "preMarketPrice" | "preMarketChange" | "preMarketChangePercent"
1530
1554
  | "postMarketPrice" | "postMarketChange" | "postMarketChangePercent">>;
1531
1555
 
1532
- /** price, change and changePercent are the live print the list is ranked by; the session fields say how it reads. */
1556
+ /** price, change and changePercent are the live print the server ranks the list by; the session fields say how it reads. */
1533
1557
  export interface CloudMarketScreenerItem extends CloudMoverSessionFields {
1534
1558
  rank: number;
1535
1559
  symbol: string;
@@ -21,6 +21,7 @@ import type { DataProvider } from "../../types/data-provider";
21
21
  import type { PinTickerOptions } from "../../types/plugin";
22
22
  import {
23
23
  resolveTickerOpenTarget,
24
+ tickerOpenFailureMessage,
24
25
  type TickerOpenTarget,
25
26
  } from "../../tickers/open-target";
26
27
  import { AmbiguousTickerError, findExactTickerSearchMatch } from "../../tickers/search";
@@ -73,7 +74,7 @@ export function useAppTickerOpenRuntime({
73
74
  tickerRepository,
74
75
  });
75
76
  if (!target && canPresentFeedback()) {
76
- pluginRegistry.notify({ body: `Could not open ${rawSymbol}.`, type: "error" });
77
+ pluginRegistry.notify({ body: tickerOpenFailureMessage(rawSymbol), type: "error" });
77
78
  }
78
79
  return target;
79
80
  } catch (err) {