gloomberb 0.18.4 → 0.19.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 (600) hide show
  1. package/README.md +1 -1
  2. package/README.zh-CN.md +1 -1
  3. package/package.json +1 -1
  4. package/patches/electrobun@1.18.1.patch +169 -5
  5. package/src/account/auth-model.ts +7 -3
  6. package/src/api-client/auth.ts +9 -4
  7. package/src/api-client/catalysts.ts +2 -1
  8. package/src/api-client/cot.ts +6 -2
  9. package/src/api-client/credit-documents.ts +6 -0
  10. package/src/api-client/data.ts +31 -6
  11. package/src/api-client/equity-screener.ts +24 -0
  12. package/src/api-client/fund-facts.ts +45 -4
  13. package/src/api-client/index.ts +171 -11
  14. package/src/api-client/live-scan.ts +91 -0
  15. package/src/api-client/market-discovery.ts +10 -0
  16. package/src/api-client/market-movers.ts +7 -0
  17. package/src/api-client/members.ts +13 -1
  18. package/src/api-client/perps.ts +43 -0
  19. package/src/api-client/request.ts +222 -41
  20. package/src/api-client/research-activity.ts +6 -12
  21. package/src/api-client/types.ts +74 -0
  22. package/src/app/keybindings/index.ts +3 -0
  23. package/src/app/keybindings/labels.ts +18 -1
  24. package/src/app/keybindings/resolve.ts +14 -0
  25. package/src/app/pane-runtime/plugin-bindings.ts +9 -5
  26. package/src/app/runtime/broker-import.ts +42 -27
  27. package/src/app/runtime/plugin-bindings.ts +73 -56
  28. package/src/architecture/core-builtin-allowlist.json +1 -1
  29. package/src/architecture/pane-tabs-allowlist.json +3 -0
  30. package/src/architecture/ttl-caches-allowlist.json +19 -0
  31. package/src/brokers/sync-broker-instance.ts +2 -8
  32. package/src/capabilities/chart-series.ts +1 -1
  33. package/src/cli/commands/crypto-hints.ts +40 -24
  34. package/src/cli/commands/fred-series.ts +5 -0
  35. package/src/cli/commands/keybindings.ts +23 -0
  36. package/src/cli/commands/market.ts +138 -46
  37. package/src/cli/commands/options-chain.ts +107 -10
  38. package/src/cli/commands/options-leaps.ts +9 -3
  39. package/src/cli/commands/overview.ts +176 -39
  40. package/src/cli/commands/search.ts +39 -3
  41. package/src/cli/commands/themes.ts +11 -2
  42. package/src/cli/commands/ticker.ts +57 -35
  43. package/src/cli/company-name-hint.ts +41 -0
  44. package/src/cli/earnings-calendar.ts +1 -1
  45. package/src/cli/errors.ts +10 -2
  46. package/src/cli/export-source.ts +29 -1
  47. package/src/cli/glossary.ts +73 -1
  48. package/src/cli/history-rows.ts +87 -16
  49. package/src/cli/index.ts +2 -0
  50. package/src/cli/known-gaps.ts +5 -2
  51. package/src/cli/listing-arg.ts +195 -56
  52. package/src/cli/output-file.ts +68 -0
  53. package/src/cli/pane-functions/capabilities.ts +3 -0
  54. package/src/cli/pane-functions/catalog.ts +80 -7
  55. package/src/cli/pane-functions/dom.ts +13 -3
  56. package/src/cli/pane-functions/freshness.ts +111 -16
  57. package/src/cli/pane-functions/function-help.ts +23 -13
  58. package/src/cli/pane-functions/headless.ts +56 -12
  59. package/src/cli/pane-functions/index.ts +58 -16
  60. package/src/cli/pane-functions/market-session.ts +81 -9
  61. package/src/cli/pane-functions/options.ts +19 -1
  62. package/src/cli/pane-functions/report.ts +5 -1
  63. package/src/cli/pane-functions/resolver.ts +35 -8
  64. package/src/cli/pane-functions/screenshot-assessment.ts +6 -4
  65. package/src/cli/pane-functions/screenshot-types.ts +5 -0
  66. package/src/cli/quote-answer.ts +30 -13
  67. package/src/cli/report-tables.ts +18 -7
  68. package/src/cli/restore-plugins.ts +13 -1
  69. package/src/cli/result.ts +57 -5
  70. package/src/cli/ticker-resolution.ts +3 -2
  71. package/src/components/chart/composite/composite-chart.tsx +26 -4
  72. package/src/components/chart/composite/format.ts +27 -0
  73. package/src/components/chart/composite/interactions.ts +6 -0
  74. package/src/components/chart/composite/legend.tsx +45 -7
  75. package/src/components/chart/composite/levels.ts +2 -0
  76. package/src/components/chart/composite/panel-gestures.ts +33 -0
  77. package/src/components/chart/composite/panel-series.ts +1 -0
  78. package/src/components/chart/composite/panel-surface.tsx +61 -5
  79. package/src/components/chart/composite/range-statistics.ts +62 -0
  80. package/src/components/chart/composite/rasterizer.ts +24 -13
  81. package/src/components/chart/composite/scene.ts +46 -7
  82. package/src/components/chart/composite/series-axis-tags.tsx +16 -9
  83. package/src/components/chart/composite/series-cues.ts +14 -0
  84. package/src/components/chart/composite/text-renderer.ts +9 -2
  85. package/src/components/chart/composite/tool-catalog.ts +36 -0
  86. package/src/components/chart/composite/tools.ts +203 -44
  87. package/src/components/chart/composite/types.ts +4 -0
  88. package/src/components/chart/composite/use-composite-chart-keyboard.ts +11 -4
  89. package/src/components/command-bar/assist/inventory.ts +2 -1
  90. package/src/components/command-bar/commands/direct/index.ts +28 -13
  91. package/src/components/command-bar/pane-templates/items.ts +3 -2
  92. package/src/components/command-bar/panel/prompt-binding.ts +0 -8
  93. package/src/components/command-bar/workflow/alert.ts +1 -1
  94. package/src/components/command-bar/workflow/collection-actions.ts +3 -3
  95. package/src/components/layout/header.tsx +26 -40
  96. package/src/components/layout/pane/footer/index.tsx +1 -0
  97. package/src/components/layout/pane/footer/model.ts +27 -0
  98. package/src/components/onboarding/onboarding-frame.tsx +1 -1
  99. package/src/components/onboarding/onboarding-wizard.tsx +4 -1
  100. package/src/components/onboarding/portfolio-step/positions-panel.tsx +14 -9
  101. package/src/components/onboarding/wizard-broker-sync.ts +0 -1
  102. package/src/components/onboarding/wizard-positions.ts +3 -3
  103. package/src/components/ticker/list-table-view.tsx +4 -1
  104. package/src/components/ui/choice-dialog.tsx +23 -18
  105. package/src/components/ui/choice-groups.ts +18 -0
  106. package/src/components/ui/data-table/opentui/index.tsx +9 -7
  107. package/src/components/ui/data-table/opentui/model.ts +26 -1
  108. package/src/components/ui/data-table/types.ts +2 -0
  109. package/src/components/ui/display.tsx +5 -1
  110. package/src/components/ui/list-view.tsx +8 -4
  111. package/src/components/ui/query-bar-keys.tsx +2 -0
  112. package/src/components/ui/query-bar.tsx +1 -0
  113. package/src/components/ui/select-button.tsx +5 -1
  114. package/src/components/ui/select-field.tsx +1 -0
  115. package/src/components/ui/table-layout.ts +8 -5
  116. package/src/core/app-runtime.ts +2 -0
  117. package/src/core/app-services.ts +2 -2
  118. package/src/core/state/app/layout.ts +2 -0
  119. package/src/core/state/app/state.ts +9 -0
  120. package/src/core/state/app/types.ts +3 -0
  121. package/src/data/config/chart-settings.ts +1 -1
  122. package/src/data/json-ticker-repository.ts +17 -8
  123. package/src/data/saved-copies.ts +55 -16
  124. package/src/data/ticker-repository.ts +16 -9
  125. package/src/data/ticker-writes.ts +81 -0
  126. package/src/data/ttl-cache.ts +165 -0
  127. package/src/docs/snapshot.json +427 -216
  128. package/src/i18n/es.ts +19 -3
  129. package/src/i18n/ja.ts +19 -3
  130. package/src/i18n/ko.ts +19 -3
  131. package/src/i18n/zh-cn.ts +19 -3
  132. package/src/i18n/zh-tw.ts +19 -3
  133. package/src/market-data/coordinator/financials.ts +10 -2
  134. package/src/market-data/coordinator/index.ts +3 -1
  135. package/src/market-data/coordinator/quotes.ts +4 -2
  136. package/src/market-data/depositary-pair.ts +2 -2
  137. package/src/market-data/fund-facts.ts +50 -0
  138. package/src/market-data/market/change-basis.ts +48 -7
  139. package/src/market-data/market/dated-previous-close.ts +203 -0
  140. package/src/market-data/market/format.ts +25 -0
  141. package/src/market-data/market/freshness.ts +2 -1
  142. package/src/market-data/market/futures-week.ts +68 -0
  143. package/src/market-data/market/status.ts +16 -4
  144. package/src/market-data/options-alternatives.ts +141 -4
  145. package/src/market-data/quotes/contributions.ts +20 -2
  146. package/src/market-data/quotes/resolution.ts +3 -0
  147. package/src/market-data/selectors.ts +3 -0
  148. package/src/news/aggregator.ts +113 -9
  149. package/src/news/cloud-session.ts +21 -0
  150. package/src/news/hooks.ts +1 -0
  151. package/src/news/news-model.ts +1 -0
  152. package/src/news/types.ts +11 -0
  153. package/src/notifications/notification-log.ts +6 -4
  154. package/src/plugins/builtin/alerts/alert-engine.ts +25 -2
  155. package/src/plugins/builtin/alerts/alert-log.ts +33 -0
  156. package/src/plugins/builtin/alerts/calendar-events.ts +57 -6
  157. package/src/plugins/builtin/alerts/cli-forms.ts +216 -0
  158. package/src/plugins/builtin/alerts/cli.ts +380 -82
  159. package/src/plugins/builtin/alerts/command.ts +31 -0
  160. package/src/plugins/builtin/alerts/constants.ts +1 -1
  161. package/src/plugins/builtin/alerts/events-pane.tsx +19 -4
  162. package/src/plugins/builtin/alerts/fired-log.ts +113 -8
  163. package/src/plugins/builtin/alerts/format.ts +80 -4
  164. package/src/plugins/builtin/alerts/headless.ts +30 -7
  165. package/src/plugins/builtin/alerts/history-rows.ts +7 -3
  166. package/src/plugins/builtin/alerts/history.ts +2 -0
  167. package/src/plugins/builtin/alerts/index.tsx +135 -22
  168. package/src/plugins/builtin/alerts/live.ts +3 -3
  169. package/src/plugins/builtin/alerts/metric-command.ts +134 -0
  170. package/src/plugins/builtin/alerts/metric-data.ts +111 -0
  171. package/src/plugins/builtin/alerts/metric-model.ts +324 -0
  172. package/src/plugins/builtin/alerts/metric-parity-fixture.json +725 -0
  173. package/src/plugins/builtin/alerts/pane.tsx +90 -39
  174. package/src/plugins/builtin/alerts/quotes.ts +6 -0
  175. package/src/plugins/builtin/alerts/storage.ts +37 -10
  176. package/src/plugins/builtin/alerts/types.ts +7 -0
  177. package/src/plugins/builtin/analytics/index.tsx +2 -1
  178. package/src/plugins/builtin/analytics/risk-client.ts +16 -5
  179. package/src/plugins/builtin/analytics/risk-fx.ts +70 -15
  180. package/src/plugins/builtin/analytics/risk-headless.ts +27 -0
  181. package/src/plugins/builtin/analytics/risk-model.ts +55 -9
  182. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  183. package/src/plugins/builtin/attention/index.tsx +2 -1
  184. package/src/plugins/builtin/attention/listing.ts +14 -0
  185. package/src/plugins/builtin/backtest/headless.ts +10 -5
  186. package/src/plugins/builtin/backtest/pane.tsx +9 -4
  187. package/src/plugins/builtin/backtest/presets.ts +29 -3
  188. package/src/plugins/builtin/broker-manager/index.tsx +10 -5
  189. package/src/plugins/builtin/broker-manager/model.ts +6 -1
  190. package/src/plugins/builtin/broker-trading/controller.ts +22 -12
  191. package/src/plugins/builtin/broker-trading/index.tsx +13 -1
  192. package/src/plugins/builtin/broker-trading/order-feed.ts +9 -4
  193. package/src/plugins/builtin/broker-trading/pane.tsx +1 -1
  194. package/src/plugins/builtin/buildout/cells.tsx +12 -4
  195. package/src/plugins/builtin/buildout/format.ts +38 -0
  196. package/src/plugins/builtin/buildout/model/normalizers.ts +36 -1
  197. package/src/plugins/builtin/buildout/pane/index.tsx +4 -1
  198. package/src/plugins/builtin/buildout/table-model.ts +23 -2
  199. package/src/plugins/builtin/builtin-plugin-meta.ts +7 -3
  200. package/src/plugins/builtin/catalysts/model.ts +2 -1
  201. package/src/plugins/builtin/changelog/index.tsx +9 -6
  202. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +6 -1
  203. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +1 -1
  204. package/src/plugins/builtin/chart-composer/cli-options.ts +9 -5
  205. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +12 -2
  206. package/src/plugins/builtin/chart-composer/editor/controller.ts +4 -2
  207. package/src/plugins/builtin/chart-composer/editor/model.ts +2 -0
  208. package/src/plugins/builtin/chart-composer/editor.tsx +7 -5
  209. package/src/plugins/builtin/chart-composer/formula.ts +71 -18
  210. package/src/plugins/builtin/chart-composer/headless-schema.ts +21 -4
  211. package/src/plugins/builtin/chart-composer/headless.ts +19 -3
  212. package/src/plugins/builtin/chart-composer/index.tsx +1 -1
  213. package/src/plugins/builtin/chart-composer/pane.tsx +53 -9
  214. package/src/plugins/builtin/chart-composer/presets.ts +50 -15
  215. package/src/plugins/builtin/chart-composer/quick-add.tsx +40 -25
  216. package/src/plugins/builtin/chart-composer/session-history.ts +18 -9
  217. package/src/plugins/builtin/chart-composer/settings.ts +27 -0
  218. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +40 -26
  219. package/src/plugins/builtin/chat/content/command-suggestions.ts +93 -0
  220. package/src/plugins/builtin/chat/content/composer.tsx +13 -6
  221. package/src/plugins/builtin/chat/content/footer.tsx +6 -3
  222. package/src/plugins/builtin/chat/content/index.tsx +50 -21
  223. package/src/plugins/builtin/chat/content/open-targets.ts +14 -3
  224. package/src/plugins/builtin/chat/controller/session-runtime.ts +4 -2
  225. package/src/plugins/builtin/chat/index.tsx +23 -0
  226. package/src/plugins/builtin/chat/message/desktop.tsx +1 -0
  227. package/src/plugins/builtin/chat/message/inline-tokens.tsx +23 -0
  228. package/src/plugins/builtin/chat/message/pane-chip.tsx +50 -20
  229. package/src/plugins/builtin/chat/pane-links/commands.ts +271 -0
  230. package/src/plugins/builtin/chat/pane-links/model.ts +12 -3
  231. package/src/plugins/builtin/chat/pane-links/open.ts +8 -1
  232. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +10 -1
  233. package/src/plugins/builtin/clinical-trials/model.ts +0 -9
  234. package/src/plugins/builtin/clinical-trials/pane.tsx +15 -22
  235. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +8 -6
  236. package/src/plugins/builtin/cloud/askg/manifest.ts +2 -0
  237. package/src/plugins/builtin/cloud/plugin.tsx +1 -1
  238. package/src/plugins/builtin/company-attention/building.tsx +5 -1
  239. package/src/plugins/builtin/company-attention/client.ts +8 -9
  240. package/src/plugins/builtin/company-attention/evidence.ts +4 -1
  241. package/src/plugins/builtin/company-attention/moves.tsx +5 -1
  242. package/src/plugins/builtin/company-attention/pane.tsx +9 -2
  243. package/src/plugins/builtin/company-attention/signals.tsx +5 -1
  244. package/src/plugins/builtin/correlation/headless.ts +13 -5
  245. package/src/plugins/builtin/correlation/history.ts +22 -0
  246. package/src/plugins/builtin/cot/client.ts +22 -18
  247. package/src/plugins/builtin/cot/headless.ts +15 -10
  248. package/src/plugins/builtin/cot/model.ts +14 -1
  249. package/src/plugins/builtin/cot/pane.tsx +22 -14
  250. package/src/plugins/builtin/credit-boards/client.ts +6 -10
  251. package/src/plugins/builtin/credit-conditions/headless.ts +8 -2
  252. package/src/plugins/builtin/credit-conditions/index.tsx +5 -1
  253. package/src/plugins/builtin/credit-conditions/model.ts +4 -0
  254. package/src/plugins/builtin/credit-conditions/series.ts +3 -1
  255. package/src/plugins/builtin/credit-documents/detail.tsx +2 -2
  256. package/src/plugins/builtin/credit-documents/export.ts +173 -0
  257. package/src/plugins/builtin/credit-documents/headless.ts +7 -7
  258. package/src/plugins/builtin/credit-documents/model.ts +104 -14
  259. package/src/plugins/builtin/credit-documents/pane.tsx +12 -5
  260. package/src/plugins/builtin/custom-view/sources.ts +2 -0
  261. package/src/plugins/builtin/dividend-yield/client.ts +131 -23
  262. package/src/plugins/builtin/dividend-yield/headless.ts +44 -16
  263. package/src/plugins/builtin/dividend-yield/model.ts +8 -5
  264. package/src/plugins/builtin/dividend-yield/pane.tsx +38 -13
  265. package/src/plugins/builtin/dividend-yield/ranking.ts +37 -14
  266. package/src/plugins/builtin/dividend-yield/types.ts +20 -2
  267. package/src/plugins/builtin/dividend-yield/view.ts +30 -2
  268. package/src/plugins/builtin/docs/targets.ts +5 -1
  269. package/src/plugins/builtin/earnings/board-model.ts +16 -15
  270. package/src/plugins/builtin/earnings/board.tsx +62 -71
  271. package/src/plugins/builtin/earnings/headless.ts +40 -4
  272. package/src/plugins/builtin/earnings/scope.ts +31 -43
  273. package/src/plugins/builtin/earnings-calls/calendar.tsx +2 -2
  274. package/src/plugins/builtin/earnings-calls/format.ts +31 -2
  275. package/src/plugins/builtin/earnings-calls/headless.ts +7 -2
  276. package/src/plugins/builtin/earnings-calls/model.ts +6 -1
  277. package/src/plugins/builtin/earnings-calls/pane.tsx +5 -4
  278. package/src/plugins/builtin/econ/calendar-model.ts +14 -9
  279. package/src/plugins/builtin/econ/figures.ts +72 -0
  280. package/src/plugins/builtin/econ/index.tsx +33 -3
  281. package/src/plugins/builtin/econ/types.ts +4 -0
  282. package/src/plugins/builtin/econ-statistics/defs.ts +6 -0
  283. package/src/plugins/builtin/econ-statistics/headless.ts +85 -35
  284. package/src/plugins/builtin/econ-statistics/stats.ts +37 -9
  285. package/src/plugins/builtin/econ-statistics/view.ts +3 -0
  286. package/src/plugins/builtin/equity-screener/categories.ts +179 -0
  287. package/src/plugins/builtin/equity-screener/client.ts +4 -2
  288. package/src/plugins/builtin/equity-screener/headless.ts +144 -13
  289. package/src/plugins/builtin/equity-screener/ideas.ts +49 -0
  290. package/src/plugins/builtin/equity-screener/model.ts +99 -10
  291. package/src/plugins/builtin/equity-screener/pane.tsx +70 -15
  292. package/src/plugins/builtin/exposure/charts.tsx +2 -2
  293. package/src/plugins/builtin/exposure/model.ts +13 -1
  294. package/src/plugins/builtin/exposure/pane.tsx +4 -4
  295. package/src/plugins/builtin/fixed-income/pane.tsx +2 -1
  296. package/src/plugins/builtin/futures/index.tsx +1 -0
  297. package/src/plugins/builtin/futures/model.ts +2 -1
  298. package/src/plugins/builtin/futures/table.tsx +2 -2
  299. package/src/plugins/builtin/futures/use-board-terms.ts +42 -28
  300. package/src/plugins/builtin/futures/use-front-returns.ts +45 -28
  301. package/src/plugins/builtin/fx-matrix/client.ts +11 -3
  302. package/src/plugins/builtin/fx-matrix/headless.ts +6 -3
  303. package/src/plugins/builtin/fx-matrix/index.tsx +16 -4
  304. package/src/plugins/builtin/fx-matrix/live-legs.ts +54 -3
  305. package/src/plugins/builtin/gpu/headless.ts +8 -2
  306. package/src/plugins/builtin/help/function-index.ts +4 -1
  307. package/src/plugins/builtin/help/index.tsx +1 -0
  308. package/src/plugins/builtin/help/keybindings-editor.tsx +18 -14
  309. package/src/plugins/builtin/help/shortcut-model.ts +1 -1
  310. package/src/plugins/builtin/holders/fund-holders.ts +51 -0
  311. package/src/plugins/builtin/holders/headless.ts +18 -1
  312. package/src/plugins/builtin/iv-history/client.ts +52 -11
  313. package/src/plugins/builtin/iv-history/format.ts +36 -23
  314. package/src/plugins/builtin/iv-history/headless.ts +36 -18
  315. package/src/plugins/builtin/iv-history/index.tsx +2 -2
  316. package/src/plugins/builtin/iv-history/model.ts +163 -27
  317. package/src/plugins/builtin/iv-history/pane.tsx +10 -6
  318. package/src/plugins/builtin/iv-history/screen-pane.tsx +22 -15
  319. package/src/plugins/builtin/iv-history/universe.ts +5 -1
  320. package/src/plugins/builtin/jobs/client.ts +25 -29
  321. package/src/plugins/builtin/kelly-sizer/model.ts +12 -8
  322. package/src/plugins/builtin/kelly-sizer/pane.tsx +9 -9
  323. package/src/plugins/builtin/kelly-sizer/sections.tsx +19 -10
  324. package/src/plugins/builtin/kelly-sizer/types.ts +10 -4
  325. package/src/plugins/builtin/macro-day/pane.tsx +7 -5
  326. package/src/plugins/builtin/macro-day/release-table.ts +2 -2
  327. package/src/plugins/builtin/market-heatmap/data.ts +8 -33
  328. package/src/plugins/builtin/market-movers/client.ts +25 -7
  329. package/src/plugins/builtin/market-movers/footer.ts +2 -2
  330. package/src/plugins/builtin/market-movers/headless.ts +32 -12
  331. package/src/plugins/builtin/market-movers/index.tsx +26 -14
  332. package/src/plugins/builtin/market-movers/min-float.ts +74 -0
  333. package/src/plugins/builtin/market-movers/model.ts +4 -1
  334. package/src/plugins/builtin/market-movers/screener.ts +14 -0
  335. package/src/plugins/builtin/market-movers/session-body.tsx +13 -8
  336. package/src/plugins/builtin/market-movers/session-table.ts +4 -3
  337. package/src/plugins/builtin/market-movers/table.ts +17 -5
  338. package/src/plugins/builtin/members/breadth-pane.tsx +82 -0
  339. package/src/plugins/builtin/members/client.ts +19 -4
  340. package/src/plugins/builtin/members/headless.ts +20 -7
  341. package/src/plugins/builtin/members/index.tsx +9 -9
  342. package/src/plugins/builtin/members/model.ts +23 -4
  343. package/src/plugins/builtin/members/pane.tsx +25 -15
  344. package/src/plugins/builtin/news/index.tsx +1 -0
  345. package/src/plugins/builtin/news/wire/categories.ts +19 -3
  346. package/src/plugins/builtin/news/wire/industry-pane.tsx +1 -0
  347. package/src/plugins/builtin/news/wire/news/footer.ts +31 -5
  348. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +6 -1
  349. package/src/plugins/builtin/news/wire/news/table.tsx +2 -2
  350. package/src/plugins/builtin/openfda/client.ts +14 -2
  351. package/src/plugins/builtin/openfda/index.tsx +3 -2
  352. package/src/plugins/builtin/openfda/model.ts +9 -0
  353. package/src/plugins/builtin/openfda/pane.tsx +42 -6
  354. package/src/plugins/builtin/options/analytics.ts +54 -6
  355. package/src/plugins/builtin/options/enrichment.ts +17 -5
  356. package/src/plugins/builtin/options/model-spot.ts +5 -2
  357. package/src/plugins/builtin/options/strike-window.ts +2 -1
  358. package/src/plugins/builtin/options/table.ts +6 -9
  359. package/src/plugins/builtin/options/types.ts +2 -0
  360. package/src/plugins/builtin/options/view.tsx +42 -12
  361. package/src/plugins/builtin/options-positioning/client.ts +30 -0
  362. package/src/plugins/builtin/options-positioning/headless.ts +74 -21
  363. package/src/plugins/builtin/options-positioning/index.tsx +4 -4
  364. package/src/plugins/builtin/options-positioning/model.ts +122 -14
  365. package/src/plugins/builtin/options-positioning/pane.tsx +87 -42
  366. package/src/plugins/builtin/options-scenario/client.ts +116 -47
  367. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +142 -10
  368. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +206 -55
  369. package/src/plugins/builtin/options-scenario/csp-screen.ts +63 -23
  370. package/src/plugins/builtin/options-scenario/csp.ts +8 -2
  371. package/src/plugins/builtin/options-scenario/index.tsx +9 -7
  372. package/src/plugins/builtin/options-scenario/strategy-screen.ts +532 -0
  373. package/src/plugins/builtin/pe-band/model.ts +12 -49
  374. package/src/plugins/builtin/perps/cascade-tabs.tsx +161 -0
  375. package/src/plugins/builtin/perps/cascade.ts +236 -0
  376. package/src/plugins/builtin/perps/client.ts +56 -2
  377. package/src/plugins/builtin/perps/freshness.ts +12 -10
  378. package/src/plugins/builtin/perps/headless.ts +65 -9
  379. package/src/plugins/builtin/perps/index.tsx +7 -5
  380. package/src/plugins/builtin/perps/live-rows.ts +10 -3
  381. package/src/plugins/builtin/perps/model.ts +18 -4
  382. package/src/plugins/builtin/perps/pane.tsx +32 -31
  383. package/src/plugins/builtin/perps/status-line.ts +25 -0
  384. package/src/plugins/builtin/plugin-module.ts +5 -1
  385. package/src/plugins/builtin/portfolio-list/cached-account.ts +3 -1
  386. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +2 -8
  387. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +4 -4
  388. package/src/plugins/builtin/portfolio-list/cli/render.ts +12 -7
  389. package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
  390. package/src/plugins/builtin/portfolio-list/pane/futures-front.ts +40 -0
  391. package/src/plugins/builtin/portfolio-list/pane/index.tsx +4 -1
  392. package/src/plugins/builtin/portfolio-list/pane/streaming.ts +5 -2
  393. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +3 -2
  394. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +3 -1
  395. package/src/plugins/builtin/portfolio-list/summary/index.tsx +11 -8
  396. package/src/plugins/builtin/portfolio-list/table.tsx +11 -3
  397. package/src/plugins/builtin/primary-dealers/client.ts +13 -38
  398. package/src/plugins/builtin/rate-path/pane.tsx +2 -2
  399. package/src/plugins/builtin/realized-vol/index.tsx +2 -2
  400. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  401. package/src/plugins/builtin/relative-rotation/client.ts +18 -4
  402. package/src/plugins/builtin/relative-rotation/headless.ts +3 -3
  403. package/src/plugins/builtin/relative-rotation/index.tsx +2 -1
  404. package/src/plugins/builtin/relative-rotation/model.ts +16 -40
  405. package/src/plugins/builtin/relative-rotation/pane.tsx +3 -2
  406. package/src/plugins/builtin/research/corporate-actions-pane.tsx +3 -2
  407. package/src/plugins/builtin/research/event-model.ts +10 -8
  408. package/src/plugins/builtin/research/headless.ts +1 -1
  409. package/src/plugins/builtin/research/relative-valuation-headless.ts +5 -1
  410. package/src/plugins/builtin/research/relative-valuation-model.ts +31 -3
  411. package/src/plugins/builtin/research/relative-valuation-pane.tsx +11 -9
  412. package/src/plugins/builtin/reverse-dcf/headless.ts +5 -3
  413. package/src/plugins/builtin/reverse-dcf/model.ts +61 -6
  414. package/src/plugins/builtin/reverse-dcf/pane.tsx +7 -4
  415. package/src/plugins/builtin/scanner/feed.ts +2 -2
  416. package/src/plugins/builtin/scanner/flow-model.ts +50 -4
  417. package/src/plugins/builtin/scanner/flow-pane.tsx +367 -65
  418. package/src/plugins/builtin/scanner/flow-session.ts +146 -0
  419. package/src/plugins/builtin/scanner/headless.ts +204 -35
  420. package/src/plugins/builtin/scanner/index.ts +40 -6
  421. package/src/plugins/builtin/scanner/live-condition-editor.tsx +222 -0
  422. package/src/plugins/builtin/scanner/live-feed.ts +168 -0
  423. package/src/plugins/builtin/scanner/live-headless.ts +138 -0
  424. package/src/plugins/builtin/scanner/live-model.ts +362 -0
  425. package/src/plugins/builtin/scanner/live-pane.tsx +677 -0
  426. package/src/plugins/builtin/scanner/live-track.ts +186 -0
  427. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  428. package/src/plugins/builtin/seasonality/headless.ts +10 -1
  429. package/src/plugins/builtin/sec/fund-facts-view.tsx +5 -2
  430. package/src/plugins/builtin/sec/fund-facts.ts +116 -34
  431. package/src/plugins/builtin/sec/headless.ts +2 -1
  432. package/src/plugins/builtin/series-inventory.ts +3 -0
  433. package/src/plugins/builtin/shared/cloud-resource.ts +3 -2
  434. package/src/plugins/builtin/shared/cloud-upgrade.ts +4 -3
  435. package/src/plugins/builtin/shared/earnings-calendar.ts +6 -2
  436. package/src/plugins/builtin/shared/earnings-report.ts +2 -2
  437. package/src/plugins/builtin/shared/job-posting.ts +1 -0
  438. package/src/plugins/builtin/shared/macro-releases.ts +65 -0
  439. package/src/plugins/builtin/shared/members-menu.tsx +3 -2
  440. package/src/plugins/builtin/shared/pro-lock-note.ts +12 -12
  441. package/src/plugins/builtin/shared/report-freshness.ts +3 -2
  442. package/src/plugins/builtin/shared/scanner-capture.ts +3 -0
  443. package/src/plugins/builtin/shared/ticker-choice.tsx +2 -1
  444. package/src/plugins/builtin/shared/ticker-company.ts +77 -0
  445. package/src/plugins/builtin/shared/use-quote-board.ts +10 -3
  446. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -8
  447. package/src/plugins/builtin/shared/volatility/pricing.ts +26 -1
  448. package/src/plugins/builtin/shared/wall-view-lifecycle.ts +3 -1
  449. package/src/plugins/builtin/themes/headless.ts +6 -4
  450. package/src/plugins/builtin/themes/model.ts +17 -8
  451. package/src/plugins/builtin/themes/pane.tsx +7 -7
  452. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +43 -18
  453. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +3 -2
  454. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +62 -0
  455. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +83 -46
  456. package/src/plugins/builtin/ticker-detail/headless.ts +31 -16
  457. package/src/plugins/builtin/ticker-detail/overview/model.ts +1 -1
  458. package/src/plugins/builtin/ticker-detail/pane.tsx +3 -2
  459. package/src/plugins/builtin/ticker-detail/quote-footer.ts +6 -1
  460. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -1
  461. package/src/plugins/builtin/ticker-detail/settings.ts +1 -0
  462. package/src/plugins/builtin/time-sales/pane.tsx +31 -19
  463. package/src/plugins/builtin/treasury-daily/client.ts +41 -5
  464. package/src/plugins/builtin/treasury-daily/headless.ts +2 -2
  465. package/src/plugins/builtin/treasury-daily/index.tsx +1 -1
  466. package/src/plugins/builtin/treasury-daily/model.ts +50 -3
  467. package/src/plugins/builtin/treasury-daily/pane.tsx +81 -2
  468. package/src/plugins/builtin/vol-surface/charts.tsx +9 -4
  469. package/src/plugins/builtin/vol-surface/client.ts +9 -17
  470. package/src/plugins/builtin/vol-surface/headless.ts +63 -14
  471. package/src/plugins/builtin/vol-surface/index.tsx +2 -2
  472. package/src/plugins/builtin/vol-surface/model.ts +22 -7
  473. package/src/plugins/builtin/vol-surface/pane.tsx +48 -9
  474. package/src/plugins/builtin/vol-surface/tables.ts +31 -3
  475. package/src/plugins/builtin/volatility/dvol.ts +46 -0
  476. package/src/plugins/builtin/volatility/index.tsx +2 -2
  477. package/src/plugins/builtin/volatility/pane.tsx +4 -4
  478. package/src/plugins/builtin/volatility/series.ts +26 -0
  479. package/src/plugins/builtin/world-indices/footer.ts +1 -1
  480. package/src/plugins/builtin/world-venue-map/headless.ts +3 -3
  481. package/src/plugins/catalog-browser.ts +13 -8
  482. package/src/plugins/catalog-manifest.ts +2 -1
  483. package/src/plugins/marketplace/model.ts +18 -5
  484. package/src/portfolio/column-values.ts +5 -5
  485. package/src/portfolio/futures-positions.ts +6 -3
  486. package/src/portfolio/position-quantity.ts +15 -9
  487. package/src/public/__snapshots__/public-api.test.ts.snap +1 -0
  488. package/src/remote/resources.ts +2 -1
  489. package/src/renderers/browser/app-services.ts +1 -7
  490. package/src/renderers/browser/config-host.ts +2 -55
  491. package/src/renderers/browser/main.tsx +7 -57
  492. package/src/renderers/dom/data-table/row.tsx +26 -0
  493. package/src/renderers/dom/desktop/list-view.tsx +13 -10
  494. package/src/renderers/dom/desktop/query-bar.tsx +4 -2
  495. package/src/renderers/dom/desktop/select-field.tsx +4 -2
  496. package/src/renderers/dom/host/chart-surface.tsx +21 -0
  497. package/src/renderers/electrobun/bun/desktop/app-name.ts +222 -0
  498. package/src/renderers/electrobun/bun/desktop/backend.ts +29 -7
  499. package/src/renderers/electrobun/bun/desktop/host-requests.ts +7 -0
  500. package/src/renderers/electrobun/bun/desktop/initialization.ts +2 -0
  501. package/src/renderers/electrobun/bun/desktop/relaunch.ts +74 -25
  502. package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +1 -0
  503. package/src/renderers/electrobun/bun/desktop/update.ts +173 -5
  504. package/src/renderers/electrobun/bun/desktop/view-ids.ts +52 -0
  505. package/src/renderers/electrobun/bun/desktop/workspace/index.ts +2 -0
  506. package/src/renderers/electrobun/bun/index.ts +25 -1
  507. package/src/renderers/electrobun/shared/app-name.ts +30 -0
  508. package/src/renderers/electrobun/shared/protocol.ts +10 -0
  509. package/src/renderers/electrobun/view/backend-rpc.ts +6 -0
  510. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
  511. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +1 -0
  512. package/src/renderers/electrobun/view/main.tsx +24 -1
  513. package/src/renderers/electrobun/view/news-source.ts +3 -1
  514. package/src/renderers/electrobun/view/remote/ticker-repository.ts +14 -7
  515. package/src/renderers/electrobun/view/view-ids.ts +85 -0
  516. package/src/renderers/opentui/start.tsx +5 -0
  517. package/src/renderers/opentui/toast-host.tsx +71 -43
  518. package/src/series/registry.ts +1 -0
  519. package/src/series/types.ts +6 -0
  520. package/src/series/validate.ts +1 -0
  521. package/src/sources/gloomberb-cloud/index.ts +1 -0
  522. package/src/sources/gloomberb-cloud/news.ts +47 -7
  523. package/src/sources/gloomberb-cloud/normalizers.ts +2 -0
  524. package/src/sources/provider-router/batches.ts +10 -2
  525. package/src/sources/provider-router/financial-routes.ts +3 -3
  526. package/src/sources/provider-router/financials.ts +15 -3
  527. package/src/sources/provider-router/history.ts +28 -7
  528. package/src/sources/provider-router/index.ts +149 -10
  529. package/src/sources/provider-router/search.ts +13 -2
  530. package/src/state/app/context/index.tsx +3 -0
  531. package/src/state/ticker-cleanup.ts +50 -0
  532. package/src/sync/auth-transition.ts +19 -9
  533. package/src/sync/baseline.ts +10 -4
  534. package/src/sync/controller.ts +70 -19
  535. package/src/sync/core-contributors.ts +90 -31
  536. package/src/sync/react.ts +4 -1
  537. package/src/sync/types.ts +3 -0
  538. package/src/telemetry/stall-reports-dom.ts +38 -0
  539. package/src/telemetry/stall-reports-node.ts +75 -0
  540. package/src/telemetry/stall-reports.ts +214 -0
  541. package/src/telemetry/usage-counts.ts +0 -18
  542. package/src/test-support/preload.ts +14 -0
  543. package/src/theme/colors.ts +8 -0
  544. package/src/theme/themes.ts +10 -0
  545. package/src/tickers/company-name.ts +103 -3
  546. package/src/tickers/listing-venues.ts +38 -3
  547. package/src/tickers/search/bare-symbol.ts +36 -0
  548. package/src/tickers/search/index.ts +36 -7
  549. package/src/tickers/search/ranking.ts +10 -1
  550. package/src/tickers/search/types.ts +5 -0
  551. package/src/tickers/terminal-codes.ts +19 -0
  552. package/src/time-series/alignment.ts +1 -1
  553. package/src/time-series/base-series.ts +13 -5
  554. package/src/time-series/daily-valuation.ts +62 -0
  555. package/src/time-series/economic.ts +18 -4
  556. package/src/time-series/field-catalog.ts +2 -4
  557. package/src/time-series/filed-trailing-eps.ts +197 -0
  558. package/src/time-series/forward-valuation.ts +32 -0
  559. package/src/time-series/fundamentals.ts +198 -7
  560. package/src/time-series/hooks.ts +1 -0
  561. package/src/time-series/ice-credit.ts +3 -0
  562. package/src/time-series/price-comparison.ts +30 -2
  563. package/src/time-series/price-units.ts +72 -2
  564. package/src/time-series/resolve-context.ts +7 -1
  565. package/src/time-series/resolve.ts +116 -15
  566. package/src/time-series/series-presentation.ts +40 -6
  567. package/src/time-series/session-bar.ts +11 -0
  568. package/src/time-series/spec.ts +5 -3
  569. package/src/time-series/studies.ts +278 -74
  570. package/src/time-series/total-return.ts +160 -0
  571. package/src/time-series/transforms.ts +3 -3
  572. package/src/time-series/types.ts +27 -1
  573. package/src/time-series/valuation-releases.ts +39 -0
  574. package/src/types/data-provider.ts +9 -1
  575. package/src/types/desktop-window.ts +2 -0
  576. package/src/types/financials.ts +73 -2
  577. package/src/types/headless.ts +61 -5
  578. package/src/types/instrument.ts +3 -0
  579. package/src/types/news-source.ts +1 -0
  580. package/src/types/plugin.ts +10 -1
  581. package/src/ui/host.tsx +3 -0
  582. package/src/updater/command-names.ts +12 -7
  583. package/src/updater/failure.ts +4 -1
  584. package/src/utils/cli-output.ts +42 -1
  585. package/src/utils/crypto-pair.ts +44 -0
  586. package/src/utils/financial-statements.ts +6 -1
  587. package/src/utils/forward-dividend.ts +2 -0
  588. package/src/utils/fundamentals.ts +24 -3
  589. package/src/utils/fx-market-hours.ts +26 -3
  590. package/src/utils/main-thread-stall.ts +35 -2
  591. package/src/utils/market-capitalization.ts +24 -1
  592. package/src/utils/network-failure.ts +9 -6
  593. package/src/utils/option-expiry.ts +48 -1
  594. package/src/utils/options.ts +10 -0
  595. package/src/utils/plugin-proxy-hosts.json +1 -0
  596. package/src/utils/price-history.ts +12 -1
  597. package/src/utils/shell-word.ts +8 -0
  598. package/src/utils/trailing-dividend.ts +53 -4
  599. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +0 -17
  600. package/src/renderers/browser/first-screen.ts +0 -193
@@ -26,6 +26,9 @@ import type { GloomPlugin } from "../types/plugin";
26
26
  import type { LoadedExternalPlugin } from "./loader";
27
27
  import { portfolioAnalyticsModule } from "./builtin/analytics";
28
28
  import { earningsCallsModule } from "./builtin/earnings-calls";
29
+ import { earningsModule } from "./builtin/earnings";
30
+ import { holdersModule } from "./builtin/holders";
31
+ import { thirteenFModule } from "./builtin/thirteenf";
29
32
  import { browserDividendYieldModule } from "./builtin/dividend-yield/browser";
30
33
  import { executivesModule } from "./builtin/executives";
31
34
  import { filingEventsModule } from "./builtin/filing-events";
@@ -136,9 +139,7 @@ const browserPortfolioPlugin = composeBuiltinPlugin({
136
139
  modules: [portfolioListModule, portfolioAnalyticsModule, positionSizerModule],
137
140
  });
138
141
 
139
- // Ticker Research's successors with the modules it had on the web. Holders,
140
- // 13F funds and the Congress tab are terminal-only, so Ownership & Insiders
141
- // is smaller here.
142
+ // Ticker Research's successors with browser-safe data paths.
142
143
  const browserTickerCorePlugin = composeBuiltinPlugin({
143
144
  ...tickerCorePluginMeta,
144
145
  modules: [
@@ -171,8 +172,12 @@ const browserOptionsVolatilityPlugin = composeBuiltinPlugin({
171
172
 
172
173
  const browserOwnershipPlugin = composeBuiltinPlugin({
173
174
  ...ownershipPluginMeta,
174
- description: "Insider trades, and short interest and volume.",
175
+ description: "Holders, 13F funds, insider trades, and short interest and volume.",
176
+ // The public 13F fallback goes through the same host proxy as other plugins.
177
+ hosts: ["forms13f.com"],
175
178
  modules: [
179
+ holdersModule,
180
+ thirteenFModule,
176
181
  // Form 4s come through Gloom Cloud, behind a sign-in wall.
177
182
  insiderModule,
178
183
  // Daily volume shares open this pane.
@@ -274,13 +279,13 @@ const browserCreditPlugin = composeBuiltinPlugin({
274
279
  });
275
280
  browserCreditPlugin.hosts = [...FIXED_INCOME_HOSTS];
276
281
 
277
- // The web app has never carried the earnings calendar. The call transcripts
278
- // and the ripple ran with Ticker Research here, so their pane state is under
279
- // its namespace, not Macro's as on the terminal.
282
+ // The calendar keeps Macro's state namespace, as on the desktop and terminal.
283
+ // Calls and the ripple keep the Ticker Research namespace they used on the web.
280
284
  const browserEarningsPlugin = composeBuiltinPlugin({
281
285
  ...earningsPluginMeta,
282
- description: "Earnings call transcripts, and the earnings ripple through customers and suppliers.",
286
+ description: "Earnings calendar, call transcripts, and the earnings ripple through customers and suppliers.",
283
287
  modules: [
288
+ earningsModule,
284
289
  { module: earningsCallsModule, stateId: "ticker-research" },
285
290
  { module: earningsRippleModule, stateId: "ticker-research" },
286
291
  ],
@@ -75,7 +75,8 @@ const NATIVE_TARGETS: PluginTarget[] = ["cli", "tui", "desktop"];
75
75
  */
76
76
  function functionsOf(plugin: GloomPlugin) {
77
77
  return pluginFunctions({
78
- templates: (plugin.paneTemplates ?? []).map((template) => ({
78
+ // An unlisted function (`listed: false`) is not published.
79
+ templates: (plugin.paneTemplates ?? []).filter((template) => template.listed !== false).map((template) => ({
79
80
  label: template.label,
80
81
  prefix: template.shortcut?.prefix,
81
82
  description: template.description,
@@ -186,6 +186,19 @@ function repoDirectory(repo: string | undefined): string | null {
186
186
  return name ? name.toLowerCase() : null;
187
187
  }
188
188
 
189
+ /**
190
+ * Built-ins write their copy as sentences and registry plugins as labels, so a
191
+ * row's short line is read without its closing period, whoever wrote it, and
192
+ * the detail's paragraph always has one. An ellipsis stays as written.
193
+ */
194
+ function rowLine(text: string | undefined): string {
195
+ return (text ?? "").trim().replace(/(?<!\.)\.$/, "");
196
+ }
197
+ function detailParagraph(text: string | undefined): string | undefined {
198
+ const paragraph = text?.trim();
199
+ return paragraph ? /[\p{L}\p{N}]$/u.test(paragraph) ? `${paragraph}.` : paragraph : undefined;
200
+ }
201
+
189
202
  /** The short line a built-in's row reads: its editorial tagline, else its description. */
190
203
  function builtinTagline(local: InstalledPlugin): string {
191
204
  return BUILTIN_EDITORIAL[local.id]?.tagline ?? local.description ?? "";
@@ -257,8 +270,8 @@ export function mergeCatalog(options: {
257
270
  entries.push({
258
271
  id: plugin.id,
259
272
  name: builtin?.name ?? plugin.name,
260
- tagline: builtin ? builtinTagline(builtin) : plugin.tagline,
261
- description: builtin?.description ?? plugin.description,
273
+ tagline: rowLine(builtin ? builtinTagline(builtin) : plugin.tagline),
274
+ description: detailParagraph(builtin?.description ?? plugin.description),
262
275
  categories: builtin ? BUILTIN_EDITORIAL[plugin.id]?.categories ?? plugin.categories : plugin.categories,
263
276
  tier: plugin.tier,
264
277
  targets: plugin.targets,
@@ -309,10 +322,10 @@ export function mergeCatalog(options: {
309
322
  entries.push({
310
323
  id: local.id,
311
324
  name: local.name,
312
- tagline: local.source === "builtin"
325
+ tagline: rowLine(local.source === "builtin"
313
326
  ? builtinTagline(local)
314
- : local.description ?? (local.linked ? "Linked from a local checkout" : "Installed outside the registry"),
315
- description: local.description,
327
+ : local.description ?? (local.linked ? "Linked from a local checkout" : "Installed outside the registry")),
328
+ description: detailParagraph(local.description),
316
329
  categories: local.source === "builtin" ? [...BUILTIN_EDITORIAL[local.id]?.categories ?? []] : ["unlisted"],
317
330
  // Shipped by Gloom like every built-in, whether or not the feed lists it.
318
331
  tier: local.source === "builtin" ? "official" : "community",
@@ -6,7 +6,7 @@ import type { ColumnConfig } from "../types/config";
6
6
  import type { AnalystResearchData, CorporateActionsData, MarketState, Quote, TickerFinancials } from "../types/financials";
7
7
  import type { EarningsEvent } from "../types/data-provider";
8
8
  import type { TickerRecord } from "../types/ticker";
9
- import { colors, priceColor } from "../theme/colors";
9
+ import { colors, priceColor, shownPriceColor } from "../theme/colors";
10
10
  import { formatQuoteAgeWithSource, resolveQuoteAgeTimestamp } from "../market-data/quotes/time";
11
11
  import { delistingStateLabel } from "../market-data/quotes/delisting";
12
12
  import { formatFeedTime, formatShortDate, parseDisplayDate, type ShortDateOptions } from "../utils/datetime-format";
@@ -532,7 +532,7 @@ export function getColumnValue(
532
532
  case "ext_hours": {
533
533
  const changePercent = getExtendedSessionDisplay(quote)?.changePercent;
534
534
  if (!isFiniteNumber(changePercent)) return { text: "—" };
535
- return { text: formatPercentRaw(changePercent), color: priceColor(changePercent) };
535
+ return { text: formatPercentRaw(changePercent), color: shownPriceColor(changePercent, 2) };
536
536
  }
537
537
  case "side":
538
538
  return { text: positionSideLabel(ticker, ctx.activeTab) ?? "—" };
@@ -594,13 +594,13 @@ export function getColumnValue(
594
594
  }
595
595
  case "pnl_pct": {
596
596
  const percent = portfolioPnlPercent(positionPnl.value, baseMetrics.totalCost);
597
- return percent === null ? { text: "—" } : { text: formatPercentRaw(percent), color: priceColor(percent) };
597
+ return percent === null ? { text: "—" } : { text: formatPercentRaw(percent), color: shownPriceColor(percent, 2) };
598
598
  }
599
599
  case "mark_delta":
600
600
  if (!liveQuote || brokerMarkPrice == null || liveQuote.price === 0 || positionMetrics.priceBasis !== (quote?.priceBasis ?? "per-unit") || positionCurrency !== quoteCurrency) return { text: "—" };
601
601
  {
602
602
  const percent = ((brokerMarkPrice - liveQuote.price) / Math.abs(liveQuote.price)) * 100;
603
- return { text: formatPercentRaw(percent), color: priceColor(percent) };
603
+ return { text: formatPercentRaw(percent), color: shownPriceColor(percent, 2) };
604
604
  }
605
605
  case "acq_date": {
606
606
  return { text: formatShortDate(earliestDateAcquired(ticker, ctx.activeTab), MONTH_DAY) };
@@ -627,7 +627,7 @@ export function getColumnValue(
627
627
  const current = targetReferencePrice(analyst.data, quoteCurrency, displayQuote?.price);
628
628
  if (value == null || !current) return { text: "—" };
629
629
  const percent = ((value - current) / Math.abs(current)) * 100;
630
- return { text: formatPercentRaw(percent), color: priceColor(percent) };
630
+ return { text: formatPercentRaw(percent), color: shownPriceColor(percent, 2) };
631
631
  }
632
632
  case "rating": {
633
633
  const analyst = mapData(ctx.analystResearch, ticker.metadata.ticker);
@@ -36,10 +36,12 @@ export interface FuturesSettlement {
36
36
  previous: number | null;
37
37
  }
38
38
 
39
- /** What a portfolio reads from a future's terms: its contract size and its settlement. */
39
+ /** A future's contract size, settlement and front reference for collection rows. */
40
40
  export interface PortfolioFuturesTerms {
41
41
  size: FuturesContractSize | null;
42
42
  settlement: FuturesSettlement | null;
43
+ /** A different contract that is currently front, when the terms name one. */
44
+ front?: Pick<NonNullable<FuturesContractTermsPayload["front"]>, "contract" | "contractSymbol">;
43
45
  }
44
46
 
45
47
  /** A grain or a soft quotes in US cents, so a dollar point value is a hundred of its price units. */
@@ -167,7 +169,7 @@ export function previousFuturesSettlement(
167
169
 
168
170
  /** What a portfolio reads from a future's terms, with the settlement before the latest when the history has it. */
169
171
  export function portfolioFuturesTerms(
170
- terms: Pick<FuturesContractTermsPayload, "spec" | "settlement"> | null | undefined,
172
+ terms: (Pick<FuturesContractTermsPayload, "spec" | "settlement"> & Partial<Pick<FuturesContractTermsPayload, "front" | "contract">>) | null | undefined,
171
173
  history?: readonly PricePoint[] | null,
172
174
  ): PortfolioFuturesTerms {
173
175
  const settlement = terms?.settlement && Number.isFinite(terms.settlement.price) && terms.settlement.price > 0
@@ -177,7 +179,8 @@ export function portfolioFuturesTerms(
177
179
  previous: previousFuturesSettlement(history, terms.settlement.date, terms.settlement.price),
178
180
  }
179
181
  : null;
180
- return { size: futuresContractSize(terms?.spec), settlement };
182
+ const front = terms?.front && terms.front.contract !== terms.contract ? terms.front : undefined;
183
+ return { size: futuresContractSize(terms?.spec), settlement, ...(front ? { front } : {}) };
181
184
  }
182
185
 
183
186
  /**
@@ -23,21 +23,27 @@ function tradesInWholeUnits(ticker: TickerRecord): boolean {
23
23
  }
24
24
 
25
25
  /**
26
- * Why a typed size cannot be stored as read: a dot and three digits (`1.250`)
27
- * on a listing held in whole units, where it is far likelier the thousands
28
- * grouping many locales write (1,250 shares) than a quarter of a share, so the
29
- * command asks which, as it does for `1,250`. Null for any other size, and
30
- * for a listing that may be held in fractions, where `1.250` is 1.25.
26
+ * Why a typed size cannot be stored as read: a dot and three digits (`1.000`,
27
+ * `1.250`), which is as likely the thousands grouping many locales write
28
+ * (1,000 shares, as cash reads `1.000`) as a fraction of a share, so the
29
+ * command asks which, as it does for `1,250`. On a listing held in whole
30
+ * units the thousands reading is the only one that can be held, and the
31
+ * message says so. Null for any other size (`0.500`, `12.25`).
31
32
  */
32
- export function wholeUnitQuantityProblem(
33
+ export function ambiguousQuantityProblem(
33
34
  typed: string,
34
35
  ticker: TickerRecord,
35
36
  options: { label?: PositionQuantityLabel; listing?: string } = {},
36
37
  ): string | null {
37
38
  const reading = thousandsDotReadings(typed);
38
- if (!reading || !tradesInWholeUnits(ticker)) return null;
39
+ if (!reading) return null;
39
40
  const label = options.label ?? positionQuantityLabel(ticker);
40
41
  const listing = options.listing ?? ticker.metadata.ticker;
41
- const because = `${listing} trades in whole ${label === "Contracts" ? "contracts" : "shares"}`;
42
- return `${label} ${ambiguousNumberMessage(typed, reading, { because })}.`;
42
+ const because = tradesInWholeUnits(ticker)
43
+ ? `${listing} trades in whole ${label === "Contracts" ? "contracts" : "shares"}`
44
+ : undefined;
45
+ // The fraction typed so it does not read two ways itself: 1.234 asks again, 1.2340 does not.
46
+ const fraction = String(reading.decimal);
47
+ const decimalAs = thousandsDotReadings(fraction) ? `${fraction}0` : fraction;
48
+ return `${label} ${ambiguousNumberMessage(typed, reading, { because, decimalAs })}.`;
43
49
  }
@@ -374,6 +374,7 @@ exports[`the runtime exports of every public subpath are the reviewed ones 1`] =
374
374
  "paneTitleText",
375
375
  "previewTheme",
376
376
  "priceColor",
377
+ "shownPriceColor",
377
378
  "syncTheme",
378
379
  "toneTintBg",
379
380
  ],
@@ -110,7 +110,8 @@ export function createRemoteResources({
110
110
  }));
111
111
  }
112
112
  if (resource === "app://pane-templates") {
113
- return [...pluginRegistry.paneTemplates.values()].map((template) => ({
113
+ // An unlisted function (`listed: false`) is left out, as of every list.
114
+ return [...pluginRegistry.paneTemplates.values()].filter((template) => template.listed !== false).map((template) => ({
114
115
  id: template.id,
115
116
  paneId: template.paneId,
116
117
  label: template.label,
@@ -7,7 +7,6 @@ import { AssetDataRouter } from "../../sources/provider-router";
7
7
  import { JsonPersistence } from "../../data/json-persistence";
8
8
  import { isBrokerAccountSnapshotKey } from "../../brokers/account-cache";
9
9
  import { JsonTickerRepository } from "../../data/json-ticker-repository";
10
- import { noteFirstScreenTicker } from "./first-screen";
11
10
 
12
11
  export function createBrowserAppServices({ config, plugins, externalPlugins }: AppServicesFactoryOptions) {
13
12
  const cloudProvider = createGloomberbCloudProvider();
@@ -28,12 +27,7 @@ export function createBrowserAppServices({ config, plugins, externalPlugins }: A
28
27
  priority: 0,
29
28
  provider: { fetchNews: (query) => cloudNews?.provider.fetchNews(query) ?? Promise.resolve([]) },
30
29
  }));
31
- const offSaved = pluginRegistry.events.on("host:portfolio-ticker-saved", () => recordResearchActivity("ticker_saved"));
32
- const offSelected = pluginRegistry.events.on("ticker:selected", ({ symbol, previous }) => noteFirstScreenTicker(symbol, previous));
33
- return () => {
34
- offSaved();
35
- offSelected();
36
- };
30
+ return pluginRegistry.events.on("host:portfolio-ticker-saved", () => recordResearchActivity("ticker_saved"));
37
31
  },
38
32
  });
39
33
  }
@@ -30,7 +30,6 @@ const BROWSER_RESEARCH_NEWS_ID = "ticker-news:research";
30
30
  const BROWSER_WORLD_INDICES_ID = "world-indices:main";
31
31
  const BROWSER_SECTORS_ID = "sectors:main";
32
32
  const BROWSER_ECON_CALENDAR_ID = "econ-calendar:main";
33
- const BROWSER_HELP_ID = "help:main";
34
33
 
35
34
  function column(first: string, second: string, ratio = 0.5): DockLayoutNode {
36
35
  return {
@@ -82,53 +81,14 @@ export function createBrowserResearchLayout(symbol: string): LayoutConfig {
82
81
  };
83
82
  }
84
83
 
85
- /**
86
- * The calm first screen (`calm` arm of `web_first_screen`): two of the same
87
- * panes, the company's research and Help on its basics, side by side in a
88
- * landscape window and stacked in a portrait one, as on a phone. The research
89
- * pane owns the symbol exactly as in the six-pane workspace.
90
- */
91
- function createBrowserCalmLayout(symbol: string, portrait: boolean): LayoutConfig {
92
- return {
93
- dockRoot: {
94
- kind: "split",
95
- axis: portrait ? "vertical" : "horizontal",
96
- ratio: portrait ? 0.55 : 0.6,
97
- first: { kind: "pane", instanceId: BROWSER_RESEARCH_PANE_ID },
98
- second: { kind: "pane", instanceId: BROWSER_HELP_ID },
99
- },
100
- instances: [
101
- {
102
- instanceId: BROWSER_RESEARCH_PANE_ID,
103
- paneId: TICKER_RESEARCH_PANE_ID,
104
- binding: { kind: "fixed", symbol },
105
- settings: { hideTabs: false },
106
- },
107
- { instanceId: BROWSER_HELP_ID, paneId: "help", binding: { kind: "none" } },
108
- ],
109
- floating: [],
110
- detached: [],
111
- };
112
- }
113
-
114
- /** Which first screen a new visitor opens on; see first-screen.ts. */
115
- export type BrowserFirstScreen = "control" | "calm";
116
-
117
- function createBrowserDefaultConfig(
118
- dataDir: string,
119
- search = "",
120
- firstScreen: BrowserFirstScreen = "control",
121
- portrait = false,
122
- ): AppConfig {
84
+ function createBrowserDefaultConfig(dataDir: string, search = ""): AppConfig {
123
85
  const config = createDefaultConfig(dataDir);
124
86
  const entry = researchEntryFromSearch(search) ?? { symbol: "NVDA", tab: "overview" };
125
87
  // A first visit starts with a research workspace around one company. Saved
126
88
  // layouts retain their existing panes and bindings when a visitor returns.
127
89
  config.layouts[0] = {
128
90
  name: "Research",
129
- layout: firstScreen === "calm"
130
- ? createBrowserCalmLayout(entry.symbol, portrait)
131
- : createBrowserResearchLayout(entry.symbol),
91
+ layout: createBrowserResearchLayout(entry.symbol),
132
92
  paneState: { [BROWSER_RESEARCH_PANE_ID]: { activeTabId: entry.tab } },
133
93
  focusedPaneId: BROWSER_RESEARCH_PANE_ID,
134
94
  };
@@ -136,19 +96,6 @@ function createBrowserDefaultConfig(
136
96
  return browserReady(config);
137
97
  }
138
98
 
139
- /**
140
- * A first visit's workspace for the screen it was given, built the way
141
- * `loadConfig` builds the six-pane one. Only for a browser with no saved
142
- * workspace: a returning visitor keeps theirs.
143
- */
144
- export function createBrowserFirstScreenConfig(
145
- dataDir: string,
146
- firstScreen: BrowserFirstScreen,
147
- { search = "", portrait = false }: { search?: string; portrait?: boolean } = {},
148
- ): AppConfig {
149
- return createBrowserDefaultConfig(dataDir, search, firstScreen, portrait);
150
- }
151
-
152
99
  export function createBrowserConfigStore(storage: StorageLike, search = ""): ConfigStoreHost {
153
100
  const data = new SafeJsonStorage<unknown>(storage, BROWSER_STORAGE_KEYS.config, null);
154
101
  return {
@@ -17,29 +17,10 @@ import {
17
17
  restoreBrowserCloudSession,
18
18
  } from "./cloud-transport";
19
19
  import { loadWebBundledPlugins } from "./bundled-plugins";
20
- import {
21
- BROWSER_DATA_DIR,
22
- createBrowserFirstScreenConfig,
23
- hasSavedBrowserConfig,
24
- installBrowserConfigStore,
25
- } from "./config-host";
20
+ import { BROWSER_DATA_DIR, hasSavedBrowserConfig, installBrowserConfigStore } from "./config-host";
26
21
  import { browserRendererHost, browserUiHost } from "./ui-host";
27
22
  import { createBrowserDeepLinkBridge } from "./deeplink-bridge";
28
- import {
29
- exposeFirstScreen,
30
- FIRST_SCREEN_EXPERIMENT,
31
- FIRST_SCREEN_PLACEMENT,
32
- observeLandingLinks,
33
- watchFirstScreenEngagement,
34
- } from "./first-screen";
35
- import {
36
- exposeWebExperiment,
37
- initializeBrowserResearchActivity,
38
- recordResearchActivity,
39
- } from "../../api-client/research-activity";
40
- import { subscribeCommandBarQuery } from "../../components/command-bar/panel/prompt-binding";
41
- import { marketplaceLayoutIdFromSearch } from "../../layout-marketplace/api";
42
- import { paneShareIdFromSearch } from "../../shares/location";
23
+ import { initializeBrowserResearchActivity, recordResearchActivity } from "../../api-client/research-activity";
43
24
  import { flushPendingPersistence } from "../../state/persist-scheduler";
44
25
  import { loadOfficialPluginIds } from "../../plugins/marketplace/feed";
45
26
  import { crashReportsEnabled, installCrashReporter, reportCrash } from "../../telemetry/crash-reports";
@@ -51,12 +32,7 @@ import {
51
32
  } from "../../telemetry/crash-reports-dom";
52
33
  import { currentTelemetryConfig } from "../../telemetry/live-config";
53
34
  import { attentionCountsEnabled, installAttentionCounter } from "../../telemetry/attention-counts";
54
- import {
55
- installUsageCounter,
56
- recordRestoredFunctions,
57
- subscribeFunctionOpens,
58
- usageCountsEnabled,
59
- } from "../../telemetry/usage-counts";
35
+ import { installUsageCounter, recordRestoredFunctions, usageCountsEnabled } from "../../telemetry/usage-counts";
60
36
  import { installWindowUsageFlush } from "../../telemetry/usage-counts-dom";
61
37
  import type { AppConfig } from "../../types/config";
62
38
 
@@ -78,9 +54,8 @@ const releaseBootFrame = releaseBootFrameWhenDrawn(document, appRootElement);
78
54
  const BOOT_FRAME_RELEASE_FALLBACK_MS = 4_000;
79
55
 
80
56
  async function boot(): Promise<void> {
81
- // Read before anything can save a workspace or tidy the address.
57
+ // Read before anything can save a workspace.
82
58
  const savedLayout = hasSavedBrowserConfig();
83
- const search = location.search;
84
59
  installBrowserConfigStore();
85
60
  let loadedConfig: AppConfig | null = null;
86
61
  installCrashReporter({
@@ -105,7 +80,7 @@ async function boot(): Promise<void> {
105
80
  });
106
81
  installBrowserFetchTransports();
107
82
  // Read the saved Usage switch before any research activity or attribution.
108
- let config = await loadConfig(BROWSER_DATA_DIR);
83
+ const config = await loadConfig(BROWSER_DATA_DIR);
109
84
  loadedConfig = config;
110
85
  initializeBrowserResearchActivity();
111
86
  installFocusScopeRelease();
@@ -120,34 +95,9 @@ async function boot(): Promise<void> {
120
95
  recordResearchActivity("workspace_opened");
121
96
  // Nothing was restored on a first visit, only the starter workspace shown.
122
97
  if (!savedLayout) recordRestoredFunctions([]);
123
- // A first visit's workspace is chosen before the dock paints, while the
124
- // plugins download; see first-screen.ts.
125
- const firstScreen = exposeFirstScreen({
126
- savedLayout,
127
- handoff: !!marketplaceLayoutIdFromSearch(search) || !!paneShareIdFromSearch(search),
128
- expose: (signal) => exposeWebExperiment(FIRST_SCREEN_EXPERIMENT, { placement: FIRST_SCREEN_PLACEMENT, signal }),
129
- });
130
- const [externalPlugins, screen] = await Promise.all([bundledPlugins, firstScreen]);
131
- if (screen === "calm") {
132
- config = createBrowserFirstScreenConfig(BROWSER_DATA_DIR, "calm", {
133
- search,
134
- portrait: window.innerHeight > window.innerWidth,
135
- });
136
- loadedConfig = config;
137
- }
138
98
  applyLanguageFromConfig(config);
139
- try {
140
- watchFirstScreenEngagement({
141
- counted: screen !== null,
142
- session: sessionStorage,
143
- layout: config.layout,
144
- record: () => recordResearchActivity("first_screen_engaged", undefined, undefined, { placement: FIRST_SCREEN_PLACEMENT }),
145
- sources: { subscribeFunctionOpens, subscribeCommandBarQuery },
146
- });
147
- } catch {
148
- /* Blocked storage: the first action is not measured. */
149
- }
150
- const deepLinkBridge = observeLandingLinks(createBrowserDeepLinkBridge());
99
+ const externalPlugins = await bundledPlugins;
100
+ const deepLinkBridge = createBrowserDeepLinkBridge();
151
101
  root.render(
152
102
  <DomErrorBoundary label="Browser renderer crashed" fallback={(error) => (
153
103
  <div className="gloom-fatal">
@@ -457,6 +457,32 @@ function WebDataTableRowInner<
457
457
 
458
458
  function CellText({ cell, column, color }: { cell: DataTableCell; column: DataTableColumn; color: string }) {
459
459
  const style = clippedCellTextStyle(column, color, cell.attributes ?? TextAttributes.NONE);
460
+ if (cell.note) {
461
+ const text = (
462
+ <div
463
+ style={{
464
+ display: "flex",
465
+ flexWrap: "wrap",
466
+ alignContent: "flex-start",
467
+ justifyContent: contentJustifyForAlign(column.align),
468
+ columnGap: "var(--gloom-space-sm)",
469
+ width: "100%",
470
+ height: "100%",
471
+ minWidth: 0,
472
+ overflow: "hidden",
473
+ }}
474
+ >
475
+ <span title={cell.text} style={{ ...style, width: "auto", flex: "0 1 auto", minWidth: 0 }}>{cell.text}</span>
476
+ <span data-gloom-role="data-table-cell-note" style={{ ...cellTextStyle(CSS_TEXT_DIM, TextAttributes.NONE), flex: "none" }}>{cell.note}</span>
477
+ </div>
478
+ );
479
+ return cell.leading === undefined ? text : (
480
+ <div style={{ display: "flex", alignItems: "center", gap: LABEL_GAP_PX, width: "100%", height: "100%", minWidth: 0, overflow: "hidden" }}>
481
+ <Leading>{cell.leading}</Leading>
482
+ {text}
483
+ </div>
484
+ );
485
+ }
460
486
  if (cell.leading === undefined) return <span title={cell.text} style={style}>{cell.text}</span>;
461
487
  return (
462
488
  <div
@@ -27,6 +27,7 @@ function DefaultDesktopRow({
27
27
  stacked: boolean;
28
28
  }) {
29
29
  const colors = useThemeColors();
30
+ if (item.kind === "heading") return <Text fg={colors.textMuted}>{item.label}</Text>;
30
31
  return (
31
32
  <Box
32
33
  flexDirection="row"
@@ -142,6 +143,7 @@ export function WebListView({
142
143
  const rowHoverBg = hoverBgColor ?? hoverBg(colors);
143
144
  const selectedItem = selectedIndex >= 0 ? items[selectedIndex] : undefined;
144
145
  const activeScrollIndex = scrollIndex ?? selectedIndex;
146
+ const headingAboveSelection = items[activeScrollIndex - 1]?.kind === "heading";
145
147
  const rowStride = rowHeight + rowGap;
146
148
  const tabStopIndex = selectedIndex >= 0 && items[selectedIndex] && !items[selectedIndex].disabled
147
149
  ? selectedIndex : items.findIndex((item) => !item.disabled);
@@ -176,14 +178,14 @@ export function WebListView({
176
178
  if (!scrollBox) return;
177
179
  const safeIndex = Math.min(activeScrollIndex, items.length - 1);
178
180
  const viewportHeight = Math.max(scrollBox.viewport?.height ?? 0, 1);
179
- const rowTop = safeIndex * rowStride;
180
- const rowBottom = rowTop + rowHeight;
181
+ const rowTop = Math.max(0, safeIndex - (headingAboveSelection ? 1 : 0)) * rowStride;
182
+ const rowBottom = safeIndex * rowStride + rowHeight;
181
183
  if (rowTop < scrollBox.scrollTop) {
182
184
  scrollBox.scrollTo(rowTop);
183
185
  } else if (rowBottom > scrollBox.scrollTop + viewportHeight) {
184
186
  scrollBox.scrollTo(rowBottom - viewportHeight);
185
187
  }
186
- }, [activeScrollIndex, autoScrollToIndex, items.length, rowHeight, rowStride, scrolls]);
188
+ }, [activeScrollIndex, autoScrollToIndex, headingAboveSelection, items.length, rowHeight, rowStride, scrolls]);
187
189
 
188
190
  useEffect(() => {
189
191
  if (!scrolls) return;
@@ -202,7 +204,7 @@ export function WebListView({
202
204
  const list = document.getElementById(listDomId);
203
205
  const active = document.activeElement;
204
206
  if (!list || !active || !list.contains(active) || isEditableKeyboardTarget(active)) return;
205
- const row = list.querySelectorAll<HTMLElement>('[role="option"]')[selectedIndex];
207
+ const row = list.querySelector<HTMLElement>(`[data-gloom-list-index="${selectedIndex}"]`);
206
208
  if (row && !row.contains(active)) row.focus({ preventScroll: scrolls });
207
209
  }, [listDomId, scrolls, selectedIndex]);
208
210
 
@@ -246,10 +248,11 @@ export function WebListView({
246
248
  onActivate?.(item, index);
247
249
  }}
248
250
  data-gloom-role="desktop-list-row"
249
- role="option"
250
- aria-selected={selected}
251
- aria-disabled={disabled || undefined}
252
- tabIndex={index === tabStopIndex ? 0 : -1}
251
+ data-gloom-list-index={index}
252
+ role={item.kind === "heading" ? "presentation" : "option"}
253
+ aria-selected={item.kind === "heading" ? undefined : selected}
254
+ aria-disabled={item.kind === "heading" ? undefined : disabled || undefined}
255
+ tabIndex={item.kind === "heading" ? undefined : index === tabStopIndex ? 0 : -1}
253
256
  onKeyDown={(event: React.KeyboardEvent<HTMLElement>) => {
254
257
  if (disabled) return;
255
258
  if (event.key === " " || (event.key === "Enter" && !checkboxRows)) {
@@ -267,8 +270,8 @@ export function WebListView({
267
270
  while (items[nextIndex]?.disabled) nextIndex += direction;
268
271
  if (!items[nextIndex]) return;
269
272
  onSelect?.(nextIndex);
270
- const rows = event.currentTarget.parentElement?.querySelectorAll<HTMLElement>('[role="option"]');
271
- rows?.[nextIndex]?.focus();
273
+ const row = event.currentTarget.parentElement?.querySelector<HTMLElement>(`[data-gloom-list-index="${nextIndex}"]`);
274
+ row?.focus();
272
275
  }}
273
276
  style={listRowStyle(selected, disabled, colors, selection === "frame")}
274
277
  >
@@ -1,6 +1,7 @@
1
1
  /** @jsxImportSource react */
2
2
  import { useCallback, useContext, useEffect, useLayoutEffect, useRef, useState, type CSSProperties, type ReactNode } from "react";
3
3
  import type { HostQueryBarItem, HostQueryBarProps } from "../../../ui/host";
4
+ import { withChoiceGroupHeadings } from "../../../components/ui/choice-groups";
4
5
  import { WebPopover } from "./popover";
5
6
  import { WebMenu } from "./menu";
6
7
  import { useHorizontalOverflow } from "../host/overflow-fade";
@@ -75,15 +76,16 @@ function QueryMenu({ item, onClose }: { item: HostQueryBarItem; onClose: () => v
75
76
  <WebMenu
76
77
  label={item.label}
77
78
  selection={item.kind === "multi" ? "multi" : "single"}
78
- items={item.options.map((option) => ({
79
+ items={withChoiceGroupHeadings(item.options.map((option) => ({
79
80
  id: option.value,
80
81
  label: option.label,
82
+ group: option.group,
81
83
  description: option.description,
82
84
  disabled: option.disabled,
83
85
  selected: item.kind === "select" ? option.selected : undefined,
84
86
  checked: item.kind === "multi" ? option.selected : undefined,
85
87
  color: option.color,
86
- }))}
88
+ })))}
87
89
  onSelect={(value) => {
88
90
  item.onSelect(value);
89
91
  if (item.kind === "select") onClose();
@@ -5,6 +5,7 @@ import { WEB_CELL_HEIGHT } from "../../../theme/font-scale";
5
5
  import { Box } from "../../../ui";
6
6
  import { MenuPopover } from "../../../components/ui/menu";
7
7
  import type { SelectFieldHandle, SelectFieldProps } from "../../../components/ui/select-field";
8
+ import { withChoiceGroupHeadings } from "../../../components/ui/choice-groups";
8
9
  import { WebIcon } from "./icons";
9
10
  import { isInsideDialogSurface } from "../host/focus-scope";
10
11
 
@@ -136,13 +137,14 @@ export function WebSelectField({
136
137
  minWidth={typeof resolvedWidth === "number" ? Math.max(160, resolvedWidth) : 170}
137
138
  items={[
138
139
  ...(showUnset ? [{ id: UNSET_VALUE, label: "Unset", selected: true, disabled: true }] : []),
139
- ...options.map((option) => ({
140
+ ...withChoiceGroupHeadings(options.map((option) => ({
140
141
  id: option.value,
141
142
  label: option.label,
143
+ group: option.group,
142
144
  description: option.description,
143
145
  disabled: option.disabled,
144
146
  selected: option.value === value,
145
- })),
147
+ }))),
146
148
  ]}
147
149
  onSelect={(id) => {
148
150
  if (id !== UNSET_VALUE && id !== value) onChange(id);
@@ -168,6 +168,27 @@ const ChartVectors = memo(function ChartVectors({ vectors }: { vectors: readonly
168
168
  />
169
169
  ))
170
170
  : [])}
171
+ {vectors.map((shape) => shape.label && shape.points[0]
172
+ ? (
173
+ <div
174
+ key={`${shape.id}:label`}
175
+ style={{
176
+ position: "absolute",
177
+ left: `${shape.points[0].x * 100}%`,
178
+ top: `${shape.points[0].y * 100}%`,
179
+ transform: "translate(2px, -100%)",
180
+ color: shape.color,
181
+ fontSize: "var(--gloom-type-caption)",
182
+ lineHeight: 1.2,
183
+ whiteSpace: "nowrap",
184
+ pointerEvents: "none",
185
+ zIndex: 10,
186
+ }}
187
+ >
188
+ {shape.label}
189
+ </div>
190
+ )
191
+ : null)}
171
192
  </>
172
193
  );
173
194
  });