gloomberb 0.17.6 → 0.18.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 (768) hide show
  1. package/README.md +2 -0
  2. package/README.zh-CN.md +2 -0
  3. package/package.json +6 -1
  4. package/patches/electrobun@1.18.1.patch +74 -0
  5. package/src/account/auth-model.ts +3 -2
  6. package/src/account/device-signin-dialog.tsx +38 -13
  7. package/src/account/device-signin.ts +2 -1
  8. package/src/account/upgrade-dialog.tsx +6 -2
  9. package/src/api-client/askg.ts +5 -3
  10. package/src/api-client/bank-figures.ts +73 -0
  11. package/src/api-client/credit-boards.ts +21 -1
  12. package/src/api-client/crypto-markets.ts +88 -1
  13. package/src/api-client/data.ts +104 -4
  14. package/src/api-client/debt-maturities.ts +18 -0
  15. package/src/api-client/equity-screener.ts +3 -0
  16. package/src/api-client/errors.ts +13 -0
  17. package/src/api-client/fund-facts.ts +72 -0
  18. package/src/api-client/futures-curve.ts +26 -3
  19. package/src/api-client/geo.ts +11 -1
  20. package/src/api-client/index.ts +25 -2
  21. package/src/api-client/insider-signals.ts +95 -11
  22. package/src/api-client/normalizers.ts +1 -0
  23. package/src/api-client/paths.ts +20 -1
  24. package/src/api-client/request.ts +11 -3
  25. package/src/api-client/research-activity.ts +15 -8
  26. package/src/api-client/socket.ts +6 -3
  27. package/src/api-client/supply-chain-graph.ts +2 -2
  28. package/src/api-client/supply-chain.ts +8 -0
  29. package/src/api-client/tape.ts +12 -0
  30. package/src/api-client/types.ts +66 -2
  31. package/src/api-client/upgrade-experiment.ts +4 -3
  32. package/src/api-client/wall-experiments.ts +5 -3
  33. package/src/api-client/wall-summary.ts +3 -1
  34. package/src/api-client/web-experiments.ts +5 -2
  35. package/src/api-client/yield-curves.ts +17 -0
  36. package/src/app/command-bar-intro.ts +38 -0
  37. package/src/app/keybindings/actions.ts +1 -1
  38. package/src/app/pane-runtime/pane-template-runtime.ts +35 -7
  39. package/src/app/pane-runtime/plugin-bindings.ts +4 -0
  40. package/src/app/pane-runtime/ticker-open-runtime.ts +4 -14
  41. package/src/app/pane-runtime/ticker-open-target.ts +31 -0
  42. package/src/app/runtime/desktop-deeplink.ts +3 -4
  43. package/src/app/runtime/plugin-bindings.ts +3 -2
  44. package/src/app/runtime/startup.ts +3 -4
  45. package/src/app/runtime/ticker-refresh.ts +13 -12
  46. package/src/app/runtime/update.ts +3 -2
  47. package/src/architecture/core-builtin-allowlist.json +0 -3
  48. package/src/architecture/silent-catch-allowlist.json +3 -0
  49. package/src/architecture/ui-literals-allowlist.json +186 -0
  50. package/src/architecture/ui-tokens-allowlist.json +40 -0
  51. package/src/ask-gloom/pane-context.ts +3 -1
  52. package/src/brokers/signed-in/catalog.ts +2 -1
  53. package/src/brokers/signed-in/client.ts +7 -2
  54. package/src/brokers/signed-in/sign-in-dialog.tsx +120 -26
  55. package/src/brokers/signed-in/sign-in.ts +22 -20
  56. package/src/brokers/sync-broker-instance.ts +2 -1
  57. package/src/capabilities/chart-series.ts +2 -0
  58. package/src/capabilities/registry.ts +1 -3
  59. package/src/cli/bank-section.ts +147 -0
  60. package/src/cli/base-converter.ts +5 -2
  61. package/src/cli/board-coins.ts +70 -0
  62. package/src/cli/commands/fred-series.ts +90 -0
  63. package/src/cli/commands/market.ts +340 -69
  64. package/src/cli/commands/news-search.ts +35 -2
  65. package/src/cli/commands/options-chain.ts +7 -5
  66. package/src/cli/commands/options-leaps.ts +3 -3
  67. package/src/cli/commands/overview.ts +78 -29
  68. package/src/cli/commands/plugins.ts +1 -0
  69. package/src/cli/commands/search.ts +87 -24
  70. package/src/cli/commands/system.ts +5 -2
  71. package/src/cli/commands/ticker.ts +337 -60
  72. package/src/cli/context.ts +2 -1
  73. package/src/cli/desktop-pane-shot.ts +11 -3
  74. package/src/cli/earnings-calendar.ts +2 -1
  75. package/src/cli/errors.ts +10 -0
  76. package/src/cli/export-source.ts +18 -3
  77. package/src/cli/glossary.ts +196 -14
  78. package/src/cli/history-rows.ts +60 -9
  79. package/src/cli/index-roots.ts +26 -0
  80. package/src/cli/index.ts +34 -9
  81. package/src/cli/known-gaps.ts +156 -22
  82. package/src/cli/listing-arg.ts +156 -12
  83. package/src/cli/listing-suggestion.ts +4 -2
  84. package/src/cli/name-entries.ts +76 -0
  85. package/src/cli/not-a-ticker.ts +15 -6
  86. package/src/cli/pane-functions/access-gate.ts +22 -0
  87. package/src/cli/pane-functions/capabilities.ts +62 -4
  88. package/src/cli/pane-functions/catalog.ts +94 -39
  89. package/src/cli/pane-functions/data.ts +6 -5
  90. package/src/cli/pane-functions/discovery.ts +1 -1
  91. package/src/cli/pane-functions/dom.ts +3 -3
  92. package/src/cli/pane-functions/freshness.ts +49 -8
  93. package/src/cli/pane-functions/function-help.ts +13 -10
  94. package/src/cli/pane-functions/headless.ts +177 -42
  95. package/src/cli/pane-functions/index.ts +64 -8
  96. package/src/cli/pane-functions/options.ts +17 -3
  97. package/src/cli/pane-functions/report-notices.ts +17 -3
  98. package/src/cli/pane-functions/resolver.ts +48 -13
  99. package/src/cli/pane-functions/screenshot.ts +124 -23
  100. package/src/cli/pane-functions/shot-options.ts +68 -0
  101. package/src/cli/perpetual-contract.ts +3 -1
  102. package/src/cli/report-tables.ts +10 -2
  103. package/src/cli/staged-tickers.ts +5 -0
  104. package/src/cli/statement-filings.ts +261 -0
  105. package/src/cli/symbol-args.ts +47 -8
  106. package/src/cli/symbol-list.ts +9 -2
  107. package/src/cli/symbol-rows.ts +269 -29
  108. package/src/cli/ticker-resolution.ts +148 -34
  109. package/src/cli/treasury-yields.ts +86 -0
  110. package/src/cli/types.ts +3 -0
  111. package/src/cli/uncovered-instrument.ts +20 -0
  112. package/src/components/chart/composite/axis-overlays.tsx +16 -9
  113. package/src/components/chart/composite/composite-chart.tsx +85 -6
  114. package/src/components/chart/composite/downsample.ts +19 -3
  115. package/src/components/chart/composite/format.ts +3 -0
  116. package/src/components/chart/composite/legend.tsx +155 -76
  117. package/src/components/chart/composite/panel-bitmap.ts +8 -2
  118. package/src/components/chart/composite/panel-surface.tsx +3 -3
  119. package/src/components/chart/composite/toolbar.tsx +12 -7
  120. package/src/components/chart/composite/types.ts +6 -0
  121. package/src/components/chart/composite/use-composite-chart-keyboard.ts +28 -1
  122. package/src/components/chart/curve/curve-surface.tsx +1 -1
  123. package/src/components/chart/static/series.ts +39 -0
  124. package/src/components/chart/static/trail-chart-surface.tsx +2 -1
  125. package/src/components/command-bar/commands/collection-list.ts +45 -15
  126. package/src/components/command-bar/commands/collection.ts +10 -3
  127. package/src/components/command-bar/commands/registry.ts +2 -2
  128. package/src/components/command-bar/list/badge.ts +4 -3
  129. package/src/components/command-bar/list/model.ts +5 -0
  130. package/src/components/command-bar/list/view.tsx +1 -1
  131. package/src/components/command-bar/panel/index.tsx +19 -8
  132. package/src/components/command-bar/panel/native-surface.ts +6 -4
  133. package/src/components/command-bar/panel/prompt-example.ts +22 -0
  134. package/src/components/command-bar/routes/root/provider-search.ts +3 -9
  135. package/src/components/command-bar/routes/root/results.ts +13 -0
  136. package/src/components/command-bar/routes/root/start-here.ts +90 -0
  137. package/src/components/command-bar/routes/ticker-search/results.ts +42 -3
  138. package/src/components/command-bar/routes/ticker-search/route.ts +3 -10
  139. package/src/components/command-bar/theme-picker.tsx +3 -1
  140. package/src/components/command-bar/workflow/builtin.ts +5 -1
  141. package/src/components/command-bar/workflow/collection-actions.ts +21 -12
  142. package/src/components/command-bar/workflow/ops.ts +21 -1
  143. package/src/{plugins/builtin/portfolio-list/command-bar.ts → components/command-bar/workflow/portfolio-position.ts} +42 -9
  144. package/src/components/command-bar/workflow/tickers.ts +6 -4
  145. package/src/components/feedback-dialog.tsx +1 -1
  146. package/src/components/form-modal/broker-step.tsx +7 -63
  147. package/src/components/form-modal/content.tsx +7 -2
  148. package/src/components/form-modal/field-row.tsx +6 -8
  149. package/src/components/form-modal/host.tsx +3 -3
  150. package/src/components/form-modal/model.ts +3 -0
  151. package/src/components/form-modal/routes.ts +17 -2
  152. package/src/components/index.ts +6 -6
  153. package/src/components/input-search-bar.tsx +43 -6
  154. package/src/components/layout/detached-pane-shell.tsx +0 -1
  155. package/src/components/layout/floating-pane.tsx +0 -1
  156. package/src/components/layout/header.tsx +8 -7
  157. package/src/components/layout/pane/footer/index.tsx +7 -4
  158. package/src/components/layout/pane/footer/model.ts +28 -2
  159. package/src/components/layout/pane/header.tsx +9 -19
  160. package/src/components/layout/pane/index.tsx +0 -1
  161. package/src/components/layout/pane/instrument-coverage-gate.tsx +57 -0
  162. package/src/components/layout/pane/pane-tabs.tsx +4 -3
  163. package/src/components/layout/pane/sizing.ts +9 -0
  164. package/src/components/layout/pane/ticker-fate-gate.tsx +8 -2
  165. package/src/components/layout/shell/pane/capture.ts +44 -20
  166. package/src/components/layout/status-bar.tsx +3 -6
  167. package/src/components/markdown-text.tsx +17 -2
  168. package/src/components/market-board/index.tsx +19 -8
  169. package/src/components/metric-treemap/heat-surface.tsx +2 -7
  170. package/src/components/onboarding/account-step/qr-panel.tsx +5 -2
  171. package/src/components/onboarding/onboarding-frame.tsx +24 -21
  172. package/src/components/onboarding/portfolio-step/positions-panel.tsx +20 -10
  173. package/src/components/onboarding/wizard-positions.ts +40 -11
  174. package/src/components/pane-settings-dialog/desktop.tsx +12 -50
  175. package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -1
  176. package/src/components/price-performance.tsx +18 -2
  177. package/src/components/reader/article-reader.tsx +11 -10
  178. package/src/components/reader/detail-reader.tsx +9 -5
  179. package/src/components/reader/reader-text.tsx +51 -24
  180. package/src/components/sign-in-code-panel.tsx +129 -75
  181. package/src/components/ticker/badge/index.tsx +2 -1
  182. package/src/components/ui/action-row.tsx +5 -2
  183. package/src/components/ui/audio-player.tsx +70 -0
  184. package/src/components/ui/card.tsx +75 -0
  185. package/src/components/ui/data-table/index.tsx +4 -2
  186. package/src/components/ui/data-table/types.ts +11 -0
  187. package/src/components/ui/detail-scroll-body.tsx +1 -1
  188. package/src/components/ui/dialog-actions.tsx +1 -21
  189. package/src/components/ui/display.tsx +13 -5
  190. package/src/components/ui/external-link.tsx +4 -2
  191. package/src/components/ui/fields.tsx +12 -0
  192. package/src/components/ui/figure.tsx +5 -3
  193. package/src/components/ui/frame.tsx +40 -6
  194. package/src/components/ui/icon.tsx +4 -0
  195. package/src/components/ui/index.ts +4 -1
  196. package/src/components/ui/list-view.tsx +8 -0
  197. package/src/components/ui/prose.tsx +97 -25
  198. package/src/components/ui/query-bar-keys.tsx +3 -1
  199. package/src/components/ui/query-bar.tsx +6 -0
  200. package/src/components/ui/quote.ts +42 -0
  201. package/src/components/ui/range-track.tsx +15 -2
  202. package/src/components/ui/reveal-in-scroll-box.ts +21 -3
  203. package/src/components/ui/select-button.tsx +38 -3
  204. package/src/components/ui/status.tsx +73 -4
  205. package/src/components/ui/table-layout.ts +20 -0
  206. package/src/components/use-pinned-moment.ts +19 -0
  207. package/src/core/app-runtime.ts +2 -2
  208. package/src/core/state/app/instrument.ts +18 -1
  209. package/src/core/state/app/layout.ts +6 -0
  210. package/src/core/state/app/state.ts +14 -2
  211. package/src/data/cached-query.ts +1 -1
  212. package/src/data/config/chart-settings.ts +3 -1
  213. package/src/data/config/home.ts +4 -1
  214. package/src/data/config/store/node.ts +9 -4
  215. package/src/data/config/store/normalize.ts +9 -0
  216. package/src/data/json-storage.ts +6 -3
  217. package/src/data/json-ticker-repository.ts +3 -1
  218. package/src/data/plugin-cache.ts +5 -1
  219. package/src/data/sqlite/json.ts +3 -1
  220. package/src/data/ticker-repository.ts +5 -2
  221. package/src/feedback/diagnostics.ts +2 -6
  222. package/src/i18n/es.ts +86 -6
  223. package/src/i18n/index.ts +3 -9
  224. package/src/i18n/ja.ts +86 -6
  225. package/src/i18n/ko.ts +86 -6
  226. package/src/i18n/zh-cn.ts +86 -6
  227. package/src/i18n/zh-tw.ts +86 -6
  228. package/src/layout-marketplace/gallery-desktop.tsx +8 -4
  229. package/src/market-data/bars-per-year.ts +43 -0
  230. package/src/market-data/bond-funds.ts +85 -0
  231. package/src/market-data/coordinator/auxiliary.ts +5 -2
  232. package/src/market-data/coordinator/index.ts +6 -5
  233. package/src/market-data/futures-contract.ts +102 -5
  234. package/src/market-data/hooks.tsx +20 -11
  235. package/src/market-data/market/bar-period.ts +36 -0
  236. package/src/market-data/market/format.ts +5 -13
  237. package/src/market-data/market/freshness.ts +1 -3
  238. package/src/market-data/market/index-level.ts +51 -0
  239. package/src/market-data/model-dividend-yield.ts +57 -16
  240. package/src/market-data/quotes/live-valuation.ts +48 -2
  241. package/src/market-data/realized-volatility.ts +16 -7
  242. package/src/market-data/tax-equivalent.ts +344 -0
  243. package/src/news/aggregator.ts +4 -2
  244. package/src/notifications/notification-log.ts +5 -3
  245. package/src/plugins/auto-update-node.ts +1 -0
  246. package/src/plugins/builtin/account-management/footer.ts +1 -1
  247. package/src/plugins/builtin/account-management/keyboard.ts +3 -0
  248. package/src/plugins/builtin/account-management/model.ts +1 -0
  249. package/src/plugins/builtin/account-management/pane.tsx +71 -19
  250. package/src/plugins/builtin/account-management/trial-cancel.ts +40 -0
  251. package/src/plugins/builtin/alerts/cli.ts +7 -18
  252. package/src/plugins/builtin/alerts/constants.ts +8 -0
  253. package/src/plugins/builtin/alerts/event-rows.ts +16 -0
  254. package/src/plugins/builtin/alerts/events-pane.tsx +2 -14
  255. package/src/plugins/builtin/alerts/headless.ts +182 -0
  256. package/src/plugins/builtin/alerts/index.tsx +3 -0
  257. package/src/plugins/builtin/alerts/pane.tsx +1 -7
  258. package/src/plugins/builtin/alerts/storage.ts +17 -1
  259. package/src/plugins/builtin/analytics/index.tsx +6 -1
  260. package/src/plugins/builtin/analytics/metrics.ts +47 -9
  261. package/src/plugins/builtin/analytics/risk-headless.ts +14 -2
  262. package/src/plugins/builtin/analytics/risk-math.ts +83 -0
  263. package/src/plugins/builtin/analytics/risk-model.ts +97 -4
  264. package/src/plugins/builtin/analytics/risk-pane.tsx +11 -6
  265. package/src/plugins/builtin/analytics/sector-model.ts +14 -3
  266. package/src/plugins/builtin/awards/headless.ts +6 -5
  267. package/src/plugins/builtin/awards/index.tsx +1 -1
  268. package/src/plugins/builtin/backtest/client.ts +38 -8
  269. package/src/plugins/builtin/backtest/engine.ts +25 -13
  270. package/src/plugins/builtin/backtest/export.ts +27 -0
  271. package/src/plugins/builtin/backtest/format.ts +19 -3
  272. package/src/plugins/builtin/backtest/headless.ts +42 -28
  273. package/src/plugins/builtin/backtest/index.tsx +3 -0
  274. package/src/plugins/builtin/backtest/pane.tsx +39 -10
  275. package/src/plugins/builtin/backtest/report.ts +160 -0
  276. package/src/plugins/builtin/backtest/rules.ts +2 -0
  277. package/src/plugins/builtin/backtest/window.ts +112 -0
  278. package/src/plugins/builtin/bond-calculator/headless.ts +3 -2
  279. package/src/plugins/builtin/broker-manager/add-flow.tsx +4 -4
  280. package/src/plugins/builtin/catalysts/headless.ts +24 -13
  281. package/src/plugins/builtin/catalysts/index.tsx +4 -2
  282. package/src/plugins/builtin/catalysts/model.ts +18 -1
  283. package/src/plugins/builtin/catalysts/pane.tsx +2 -2
  284. package/src/plugins/builtin/cds/client.ts +28 -5
  285. package/src/plugins/builtin/cds/headless.ts +183 -31
  286. package/src/plugins/builtin/cds/index.tsx +1 -1
  287. package/src/plugins/builtin/cds/model.ts +229 -53
  288. package/src/plugins/builtin/cds/pane.tsx +82 -26
  289. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -1
  290. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +39 -1
  291. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +77 -0
  292. package/src/plugins/builtin/chart-composer/chart-spec.ts +15 -0
  293. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +22 -2
  294. package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +2 -2
  295. package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -2
  296. package/src/plugins/builtin/chart-composer/headless.ts +99 -21
  297. package/src/plugins/builtin/chart-composer/index.tsx +70 -13
  298. package/src/plugins/builtin/chart-composer/pane.tsx +65 -15
  299. package/src/plugins/builtin/chart-composer/presets.ts +79 -11
  300. package/src/plugins/builtin/chart-composer/series-catalog.ts +50 -14
  301. package/src/plugins/builtin/chart-composer/series-expression.ts +9 -7
  302. package/src/plugins/builtin/chart-composer/series-listings.ts +178 -0
  303. package/src/plugins/builtin/chart-composer/session-range.ts +87 -0
  304. package/src/plugins/builtin/chat/attachments/desktop.tsx +4 -1
  305. package/src/plugins/builtin/chat/content/composer-runtime.ts +49 -14
  306. package/src/plugins/builtin/chat/content/composer.tsx +22 -1
  307. package/src/plugins/builtin/chat/content/index.tsx +35 -5
  308. package/src/plugins/builtin/chat/content/layout-metrics.ts +3 -0
  309. package/src/plugins/builtin/chat/content/shortcuts.ts +32 -13
  310. package/src/plugins/builtin/chat/content/snapshot.ts +29 -6
  311. package/src/plugins/builtin/chat/ghost-user.ts +11 -3
  312. package/src/plugins/builtin/chat/layout.ts +2 -0
  313. package/src/plugins/builtin/chat/message/desktop.tsx +13 -5
  314. package/src/plugins/builtin/chat/message/header.tsx +26 -4
  315. package/src/plugins/builtin/chat/message/pane-chip.tsx +131 -35
  316. package/src/plugins/builtin/chat/message/render-state.ts +28 -1
  317. package/src/plugins/builtin/chat/message/terminal.tsx +9 -3
  318. package/src/plugins/builtin/chat/pane-links/composer.ts +87 -0
  319. package/src/plugins/builtin/chat/pane-links/model.ts +1 -1
  320. package/src/plugins/builtin/chat/share/flow.tsx +9 -73
  321. package/src/plugins/builtin/cloud/askg/controller.ts +5 -3
  322. package/src/plugins/builtin/cloud/askg/executor.ts +3 -1
  323. package/src/plugins/builtin/cloud/askg/lock-note.ts +16 -0
  324. package/src/plugins/builtin/cloud/askg/pane.tsx +6 -2
  325. package/src/plugins/builtin/cloud/auth-actions.tsx +4 -2
  326. package/src/plugins/builtin/cloud/company-picker.tsx +21 -26
  327. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +5 -3
  328. package/src/plugins/builtin/cloud/plugin.tsx +2 -0
  329. package/src/plugins/builtin/cloud/team/module.tsx +5 -1
  330. package/src/plugins/builtin/cloud/team/pane.tsx +11 -4
  331. package/src/plugins/builtin/cloud/team/views.ts +11 -4
  332. package/src/plugins/builtin/cloud/terminal-relay/engine.ts +9 -4
  333. package/src/plugins/builtin/cloud/terminal-relay/prompts.tsx +3 -2
  334. package/src/plugins/builtin/cloud/thesis/detail.tsx +2 -1
  335. package/src/plugins/builtin/cloud/thesis/exposure.ts +3 -0
  336. package/src/plugins/builtin/cloud/thesis/prompts.tsx +3 -2
  337. package/src/plugins/builtin/cloud/thesis/ticker-tab.tsx +2 -1
  338. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +2 -1
  339. package/src/plugins/builtin/cloud/verification-status-widget.tsx +2 -1
  340. package/src/plugins/builtin/commodities-energy/index.tsx +8 -5
  341. package/src/plugins/builtin/commodities-energy/view-state.ts +11 -0
  342. package/src/plugins/builtin/company-attention/headless.ts +15 -7
  343. package/src/plugins/builtin/company-attention/index.tsx +1 -1
  344. package/src/plugins/builtin/company-attention/model.ts +49 -4
  345. package/src/plugins/builtin/company-attention/pane.tsx +11 -9
  346. package/src/plugins/builtin/company-kpis/headless.ts +4 -3
  347. package/src/plugins/builtin/company-kpis/index.tsx +2 -2
  348. package/src/plugins/builtin/company-kpis/pane.tsx +9 -3
  349. package/src/plugins/builtin/congress-trades/headless.ts +23 -2
  350. package/src/plugins/builtin/correlation/relationship/pane.tsx +18 -46
  351. package/src/plugins/builtin/cot/headless.ts +5 -2
  352. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +41 -13
  353. package/src/plugins/builtin/credit-boards/client.ts +9 -2
  354. package/src/plugins/builtin/credit-boards/headless.ts +93 -27
  355. package/src/plugins/builtin/credit-boards/index.tsx +15 -4
  356. package/src/plugins/builtin/credit-boards/model.ts +105 -6
  357. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +14 -3
  358. package/src/plugins/builtin/credit-documents/detail.tsx +2 -1
  359. package/src/plugins/builtin/credit-documents/index.tsx +3 -0
  360. package/src/plugins/builtin/credit-documents/model.ts +3 -10
  361. package/src/plugins/builtin/crypto-board/client.ts +152 -2
  362. package/src/plugins/builtin/crypto-board/headless.ts +138 -19
  363. package/src/plugins/builtin/crypto-board/index.tsx +5 -3
  364. package/src/plugins/builtin/crypto-board/model.ts +210 -12
  365. package/src/plugins/builtin/crypto-board/pane.tsx +148 -9
  366. package/src/plugins/builtin/crypto-board/series.ts +52 -1
  367. package/src/plugins/builtin/crypto-board/supply.ts +123 -0
  368. package/src/plugins/builtin/debt-maturities/client.ts +54 -1
  369. package/src/plugins/builtin/debt-maturities/headless.ts +27 -3
  370. package/src/plugins/builtin/debt-maturities/index.tsx +2 -0
  371. package/src/plugins/builtin/debt-maturities/model.ts +58 -13
  372. package/src/plugins/builtin/debt-maturities/pane.tsx +47 -7
  373. package/src/plugins/builtin/dividend-yield/client.ts +42 -36
  374. package/src/plugins/builtin/dividend-yield/headless.ts +113 -18
  375. package/src/plugins/builtin/dividend-yield/index.tsx +5 -2
  376. package/src/plugins/builtin/dividend-yield/pane.tsx +37 -13
  377. package/src/plugins/builtin/dividend-yield/provider-client.ts +7 -3
  378. package/src/plugins/builtin/dividend-yield/ranking.ts +187 -0
  379. package/src/plugins/builtin/dividend-yield/reference-price.ts +28 -5
  380. package/src/plugins/builtin/dividend-yield/types.ts +6 -1
  381. package/src/plugins/builtin/dividend-yield/view.ts +1 -1
  382. package/src/plugins/builtin/docs/blocks.tsx +54 -8
  383. package/src/plugins/builtin/docs/pane.tsx +8 -13
  384. package/src/plugins/builtin/earnings/headless.ts +3 -2
  385. package/src/plugins/builtin/earnings-calls/data.ts +51 -0
  386. package/src/plugins/builtin/earnings-calls/headless.ts +7 -0
  387. package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
  388. package/src/plugins/builtin/earnings-calls/model.ts +97 -0
  389. package/src/plugins/builtin/earnings-calls/pane.tsx +107 -7
  390. package/src/plugins/builtin/earnings-calls/transcript-view.tsx +140 -22
  391. package/src/plugins/builtin/earnings-calls/use-call-playback.ts +194 -0
  392. package/src/plugins/builtin/econ/calendar-model.ts +22 -0
  393. package/src/plugins/builtin/econ/calendar-source.ts +35 -0
  394. package/src/plugins/builtin/econ/index.tsx +40 -8
  395. package/src/plugins/builtin/econ-statistics/defs.ts +7 -0
  396. package/src/plugins/builtin/econ-statistics/detail.tsx +8 -1
  397. package/src/plugins/builtin/econ-statistics/headless.ts +26 -5
  398. package/src/plugins/builtin/econ-statistics/pane.tsx +4 -2
  399. package/src/plugins/builtin/econ-statistics/stats.ts +10 -0
  400. package/src/plugins/builtin/econ-statistics/view.ts +19 -0
  401. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  402. package/src/plugins/builtin/equity-screener/model.ts +46 -4
  403. package/src/plugins/builtin/equity-screener/pane.tsx +3 -0
  404. package/src/plugins/builtin/estimate-revisions/client.ts +32 -22
  405. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  406. package/src/plugins/builtin/estimate-revisions/index.tsx +1 -0
  407. package/src/plugins/builtin/estimate-revisions/pane.tsx +5 -3
  408. package/src/plugins/builtin/executives/index.tsx +1 -0
  409. package/src/plugins/builtin/exposure/editor.tsx +3 -3
  410. package/src/plugins/builtin/fear-greed/index.tsx +9 -0
  411. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  412. package/src/plugins/builtin/futures/index.tsx +2 -2
  413. package/src/plugins/builtin/futures/model.ts +34 -21
  414. package/src/plugins/builtin/futures/table.tsx +1 -1
  415. package/src/plugins/builtin/futures/use-board-terms.ts +2 -2
  416. package/src/plugins/builtin/futures-curve/headless.ts +4 -2
  417. package/src/plugins/builtin/futures-curve/model.ts +5 -7
  418. package/src/plugins/builtin/help/index.tsx +55 -8
  419. package/src/plugins/builtin/help/shortcut-table.tsx +15 -2
  420. package/src/plugins/builtin/holders/headless.ts +12 -0
  421. package/src/plugins/builtin/insider/headless.ts +311 -41
  422. package/src/plugins/builtin/insider/index.tsx +280 -106
  423. package/src/plugins/builtin/insider/insiders.tsx +224 -0
  424. package/src/plugins/builtin/insider/signals-cells.tsx +52 -0
  425. package/src/plugins/builtin/insider/signals-client.ts +135 -11
  426. package/src/plugins/builtin/insider/signals-model.ts +628 -56
  427. package/src/plugins/builtin/insider/signals.tsx +283 -83
  428. package/src/plugins/builtin/ipo-calendar/headless.ts +17 -7
  429. package/src/plugins/builtin/ipo-calendar/index.tsx +23 -3
  430. package/src/plugins/builtin/ipo-calendar/model.ts +84 -36
  431. package/src/plugins/builtin/ipo-calendar/pane.tsx +20 -8
  432. package/src/plugins/builtin/iv-history/headless.ts +2 -0
  433. package/src/plugins/builtin/jobs/headless.ts +1 -0
  434. package/src/plugins/builtin/jobs/index.tsx +1 -0
  435. package/src/plugins/builtin/jodi/headless.ts +2 -0
  436. package/src/plugins/builtin/kelly-sizer/pane.tsx +1 -1
  437. package/src/plugins/builtin/kelly-sizer/portfolio.ts +1 -1
  438. package/src/plugins/builtin/market-heatmap/headless.ts +1 -0
  439. package/src/plugins/builtin/market-heatmap/portfolio.ts +3 -1
  440. package/src/plugins/builtin/market-valuation/pane.tsx +1 -1
  441. package/src/plugins/builtin/market-valuation/zone-scale/desktop.tsx +31 -57
  442. package/src/plugins/builtin/mna/distress/headless.ts +2 -1
  443. package/src/plugins/builtin/mna/headless.ts +14 -2
  444. package/src/plugins/builtin/mna/index.tsx +1 -0
  445. package/src/plugins/builtin/news/headless.ts +4 -0
  446. package/src/plugins/builtin/news/wire/news/footer.ts +5 -3
  447. package/src/plugins/builtin/news/wire/news/table.tsx +57 -11
  448. package/src/plugins/builtin/notes/cli.ts +8 -4
  449. package/src/plugins/builtin/notes/files.ts +66 -20
  450. package/src/plugins/builtin/notes/migration.ts +18 -6
  451. package/src/plugins/builtin/notes/owner.tsx +3 -2
  452. package/src/plugins/builtin/notes/quick-notes-pane.tsx +3 -2
  453. package/src/plugins/builtin/options-scenario/client.ts +7 -4
  454. package/src/plugins/builtin/options-scenario/headless.ts +5 -3
  455. package/src/plugins/builtin/pe-band/index.tsx +1 -1
  456. package/src/plugins/builtin/pe-band/model.ts +1 -15
  457. package/src/plugins/builtin/perps/headless.ts +30 -13
  458. package/src/plugins/builtin/perps/model.ts +45 -3
  459. package/src/plugins/builtin/perps/pane.tsx +7 -7
  460. package/src/plugins/builtin/portfolio-list/cell-version.ts +10 -4
  461. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +128 -14
  462. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +108 -46
  463. package/src/plugins/builtin/portfolio-list/cli/render.ts +368 -45
  464. package/src/plugins/builtin/portfolio-list/cli/shared.ts +8 -2
  465. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +24 -34
  466. package/src/plugins/builtin/portfolio-list/grid.tsx +3 -1
  467. package/src/plugins/builtin/portfolio-list/headless.ts +151 -29
  468. package/src/plugins/builtin/portfolio-list/income.ts +296 -0
  469. package/src/plugins/builtin/portfolio-list/metrics.ts +2 -2
  470. package/src/plugins/builtin/portfolio-list/pane/futures-terms.ts +58 -0
  471. package/src/plugins/builtin/portfolio-list/pane/index.tsx +27 -10
  472. package/src/plugins/builtin/portfolio-list/pane/supplemental.ts +47 -28
  473. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +3 -2
  474. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +10 -3
  475. package/src/plugins/builtin/portfolio-list/summary/index.tsx +26 -4
  476. package/src/plugins/builtin/portfolio-list/summary/totals.ts +31 -3
  477. package/src/plugins/builtin/portfolio-list/table.tsx +25 -5
  478. package/src/plugins/builtin/power/prices-headless.ts +2 -1
  479. package/src/plugins/builtin/realized-vol/chart-model.ts +29 -5
  480. package/src/plugins/builtin/realized-vol/charts.tsx +14 -8
  481. package/src/plugins/builtin/realized-vol/evidence.ts +2 -0
  482. package/src/plugins/builtin/realized-vol/headless.ts +6 -4
  483. package/src/plugins/builtin/realized-vol/model.ts +5 -1
  484. package/src/plugins/builtin/realized-vol/pane.tsx +31 -11
  485. package/src/plugins/builtin/realized-vol/settings.ts +1 -1
  486. package/src/plugins/builtin/research/analyst-headless.ts +4 -0
  487. package/src/plugins/builtin/research/corporate-actions-pane.tsx +2 -0
  488. package/src/plugins/builtin/research/event-model.ts +22 -2
  489. package/src/plugins/builtin/research/headless-schema.ts +1 -0
  490. package/src/plugins/builtin/research/headless.ts +3 -0
  491. package/src/plugins/builtin/research/index.tsx +3 -0
  492. package/src/plugins/builtin/research/relative-valuation-columns.ts +27 -5
  493. package/src/plugins/builtin/research/relative-valuation-headless.ts +2 -0
  494. package/src/plugins/builtin/revenue-breakdown/index.tsx +1 -0
  495. package/src/plugins/builtin/reverse-dcf/headless.ts +9 -6
  496. package/src/plugins/builtin/reverse-dcf/index.tsx +2 -2
  497. package/src/plugins/builtin/reverse-dcf/model.ts +51 -4
  498. package/src/plugins/builtin/reverse-dcf/pane.tsx +23 -10
  499. package/src/plugins/builtin/risk-factors/headless.ts +7 -0
  500. package/src/plugins/builtin/risk-factors/index.tsx +1 -0
  501. package/src/plugins/builtin/scanner/feed.ts +5 -3
  502. package/src/plugins/builtin/scanner/headless.ts +9 -57
  503. package/src/plugins/builtin/scanner/index.ts +1 -2
  504. package/src/plugins/builtin/sec/forms.ts +2 -6
  505. package/src/plugins/builtin/sec/fund-facts-view.tsx +53 -0
  506. package/src/plugins/builtin/sec/fund-facts.ts +328 -0
  507. package/src/plugins/builtin/sec/headless.ts +140 -44
  508. package/src/plugins/builtin/sec/index.tsx +95 -30
  509. package/src/plugins/builtin/sec/model.ts +2 -44
  510. package/src/plugins/builtin/shared/headless-options.ts +10 -0
  511. package/src/plugins/builtin/shared/locked-rows.tsx +3 -1
  512. package/src/plugins/builtin/shared/move-rank.ts +108 -0
  513. package/src/plugins/builtin/shared/report-freshness.ts +25 -9
  514. package/src/plugins/builtin/shared/scanner-capture.ts +95 -0
  515. package/src/plugins/builtin/shared/treasury-cutoffs.ts +15 -0
  516. package/src/plugins/builtin/shared/use-quote-board.ts +1 -0
  517. package/src/plugins/builtin/shared/volatility/live-session.ts +34 -0
  518. package/src/plugins/builtin/shared/volatility-indices.ts +32 -0
  519. package/src/plugins/builtin/short-interest/headless.ts +3 -0
  520. package/src/plugins/builtin/short-interest/watch-model.ts +2 -2
  521. package/src/plugins/builtin/social-mentions/client.ts +8 -1
  522. package/src/plugins/builtin/social-mentions/pane.tsx +8 -6
  523. package/src/plugins/builtin/supply-chain/client.ts +12 -3
  524. package/src/plugins/builtin/supply-chain/disclosure-quote.tsx +9 -5
  525. package/src/plugins/builtin/supply-chain/evidence.ts +3 -1
  526. package/src/plugins/builtin/supply-chain/flow-layout.ts +2 -1
  527. package/src/plugins/builtin/supply-chain/flow.tsx +23 -16
  528. package/src/plugins/builtin/supply-chain/graph-client.ts +8 -5
  529. package/src/plugins/builtin/supply-chain/graph-evidence.ts +1 -1
  530. package/src/plugins/builtin/supply-chain/graph-model.ts +10 -2
  531. package/src/plugins/builtin/supply-chain/graph-pane.tsx +9 -7
  532. package/src/plugins/builtin/supply-chain/graph.tsx +8 -6
  533. package/src/plugins/builtin/supply-chain/headless.ts +69 -27
  534. package/src/plugins/builtin/supply-chain/index.tsx +1 -1
  535. package/src/plugins/builtin/supply-chain/model.ts +76 -15
  536. package/src/plugins/builtin/supply-chain/pane.tsx +29 -15
  537. package/src/plugins/builtin/supply-chain/row-evidence.tsx +5 -4
  538. package/src/plugins/builtin/themes/model.ts +2 -5
  539. package/src/plugins/builtin/thirteenf/api.ts +50 -2
  540. package/src/plugins/builtin/thirteenf/data.ts +250 -11
  541. package/src/plugins/builtin/thirteenf/headless.ts +311 -28
  542. package/src/plugins/builtin/thirteenf/index.tsx +4 -1
  543. package/src/plugins/builtin/thirteenf/model.ts +244 -19
  544. package/src/plugins/builtin/thirteenf/overlap-pane.tsx +133 -33
  545. package/src/plugins/builtin/thirteenf/overlap.ts +1 -1
  546. package/src/plugins/builtin/thirteenf/pane.tsx +24 -5
  547. package/src/plugins/builtin/thirteenf/report-notes.ts +78 -0
  548. package/src/plugins/builtin/thirteenf/types.ts +49 -0
  549. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -8
  550. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +17 -1
  551. package/src/plugins/builtin/ticker-detail/financials/model.ts +2 -42
  552. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
  553. package/src/plugins/builtin/ticker-detail/headless.ts +23 -10
  554. package/src/plugins/builtin/ticker-detail/overview/components.tsx +27 -3
  555. package/src/plugins/builtin/ticker-detail/overview/dividend.ts +60 -0
  556. package/src/plugins/builtin/ticker-detail/overview/model.ts +46 -9
  557. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +3 -0
  558. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +36 -3
  559. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +18 -19
  560. package/src/plugins/builtin/time-sales/client.ts +7 -4
  561. package/src/plugins/builtin/time-sales/headless.ts +71 -22
  562. package/src/plugins/builtin/time-sales/model.ts +12 -0
  563. package/src/plugins/builtin/time-sales/pane.tsx +31 -7
  564. package/src/plugins/builtin/time-sales/window.ts +128 -0
  565. package/src/plugins/builtin/trace-bonds/client.ts +351 -33
  566. package/src/plugins/builtin/trace-bonds/headless.ts +60 -22
  567. package/src/plugins/builtin/trace-bonds/index.tsx +4 -2
  568. package/src/plugins/builtin/trace-bonds/model.ts +27 -25
  569. package/src/plugins/builtin/trace-bonds/pane.tsx +63 -45
  570. package/src/plugins/builtin/treasury-auctions/cache.ts +61 -15
  571. package/src/plugins/builtin/treasury-auctions/client.ts +45 -10
  572. package/src/plugins/builtin/treasury-auctions/headless.ts +35 -13
  573. package/src/plugins/builtin/treasury-auctions/index.tsx +5 -2
  574. package/src/plugins/builtin/treasury-auctions/model.ts +20 -16
  575. package/src/plugins/builtin/treasury-auctions/pane.tsx +29 -12
  576. package/src/plugins/builtin/vol-surface/charts.tsx +2 -1
  577. package/src/plugins/builtin/vol-surface/headless.ts +3 -2
  578. package/src/plugins/builtin/vol-surface/pane.tsx +2 -1
  579. package/src/plugins/builtin/vol-surface/stored.ts +4 -7
  580. package/src/plugins/builtin/volatility/chart-model.ts +16 -15
  581. package/src/plugins/builtin/volatility/charts.tsx +5 -5
  582. package/src/plugins/builtin/volatility/client.ts +73 -2
  583. package/src/plugins/builtin/volatility/evidence.ts +13 -14
  584. package/src/plugins/builtin/volatility/headless.ts +13 -7
  585. package/src/plugins/builtin/volatility/index.tsx +1 -1
  586. package/src/plugins/builtin/volatility/model.ts +104 -38
  587. package/src/plugins/builtin/volatility/pane.tsx +42 -24
  588. package/src/plugins/builtin/volatility/series.ts +22 -13
  589. package/src/plugins/builtin/world-venue-map/geo-series.ts +9 -0
  590. package/src/plugins/builtin/world-venue-map/headless.ts +8 -5
  591. package/src/plugins/builtin/world-venue-map/index.ts +16 -2
  592. package/src/plugins/builtin/world-venue-map/map.tsx +13 -17
  593. package/src/plugins/builtin/yield-curve/curves.ts +29 -5
  594. package/src/plugins/builtin/yield-curve/headless.ts +41 -8
  595. package/src/plugins/builtin/yield-curve/history.ts +5 -5
  596. package/src/plugins/builtin/yield-curve/index.tsx +30 -13
  597. package/src/plugins/compat.ts +3 -1
  598. package/src/plugins/dependencies.ts +8 -2
  599. package/src/plugins/host-link.ts +12 -5
  600. package/src/plugins/installer.ts +16 -7
  601. package/src/plugins/loader.ts +9 -2
  602. package/src/plugins/marketplace/activation.ts +10 -4
  603. package/src/plugins/registry/host-actions.ts +3 -0
  604. package/src/plugins/registry/index.ts +3 -1
  605. package/src/{plugins/builtin/portfolio-list → portfolio}/allocation.ts +74 -12
  606. package/src/{plugins/builtin/portfolio-list → portfolio}/column-values.ts +140 -53
  607. package/src/portfolio/futures-positions.ts +213 -0
  608. package/src/portfolio/futures-terms.ts +75 -0
  609. package/src/portfolio/manual-lots.ts +78 -0
  610. package/src/portfolio/mutations.ts +54 -10
  611. package/src/portfolio/position-metrics.ts +44 -10
  612. package/src/portfolio/valuation-basis.ts +31 -4
  613. package/src/public/__snapshots__/public-api.test.ts.snap +4 -0
  614. package/src/public/time-series.ts +2 -1
  615. package/src/remote/confirm-change.tsx +2 -1
  616. package/src/remote/server.ts +1 -1
  617. package/src/remote/watchlist.ts +9 -1
  618. package/src/renderers/dom/data-table/row.tsx +1 -1
  619. package/src/renderers/dom/desktop/audio-player.tsx +312 -0
  620. package/src/renderers/dom/desktop/control-styles.ts +17 -82
  621. package/src/renderers/dom/desktop/controls.tsx +39 -63
  622. package/src/renderers/dom/desktop/dialog-actions.tsx +13 -53
  623. package/src/renderers/dom/desktop/icons.tsx +8 -0
  624. package/src/renderers/dom/desktop/list-view.tsx +6 -6
  625. package/src/renderers/dom/desktop/popover.tsx +0 -3
  626. package/src/renderers/dom/desktop/query-bar.tsx +6 -4
  627. package/src/renderers/dom/desktop/select-field.tsx +9 -8
  628. package/src/renderers/dom/desktop/stack-header.ts +3 -0
  629. package/src/renderers/dom/dom-ui-host.tsx +3 -2
  630. package/src/renderers/dom/host/chart-surface.tsx +3 -3
  631. package/src/renderers/dom/host/size-observer.ts +33 -4
  632. package/src/renderers/dom/host/surface-3d.tsx +3 -2
  633. package/src/renderers/dom/host/surface-fit.ts +4 -1
  634. package/src/renderers/dom/host/tabs.tsx +22 -132
  635. package/src/renderers/dom/styles.css +358 -160
  636. package/src/renderers/electrobun/bun/core-capabilities.ts +1 -1
  637. package/src/renderers/electrobun/bun/desktop/backend.ts +1 -1
  638. package/src/renderers/electrobun/bun/desktop/initialization.ts +1 -0
  639. package/src/renderers/electrobun/bun/desktop/state-broadcaster.ts +43 -1
  640. package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +5 -1
  641. package/src/renderers/electrobun/shared/rpc-codec.ts +7 -0
  642. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +21 -17
  643. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +3 -3
  644. package/src/renderers/electrobun/view/desktop/window/bridge.ts +6 -8
  645. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +18 -0
  646. package/src/series/capability.ts +1 -0
  647. package/src/series/files.ts +9 -3
  648. package/src/series/registry.ts +40 -14
  649. package/src/series/resolve.ts +121 -18
  650. package/src/series/standalone.ts +3 -2
  651. package/src/series/types.ts +55 -1
  652. package/src/series/validate.ts +39 -0
  653. package/src/shares/api.ts +26 -7
  654. package/src/shares/bug-report-dialog.tsx +2 -2
  655. package/src/shares/bug-report-flow.tsx +4 -7
  656. package/src/shares/capture-flow.ts +5 -4
  657. package/src/shares/capture-preview.tsx +2 -2
  658. package/src/shares/chat.ts +7 -27
  659. package/src/shares/flow.tsx +86 -40
  660. package/src/shares/location.ts +1 -3
  661. package/src/shares/pane-image.ts +11 -9
  662. package/src/shares/pane.ts +5 -2
  663. package/src/shares/payload.ts +1 -1
  664. package/src/shares/personal-pane.ts +3 -3
  665. package/src/shares/portable-layout.ts +9 -2
  666. package/src/shares/ready.ts +42 -0
  667. package/src/shares/test-harness.ts +62 -0
  668. package/src/sources/gloomberb-cloud/batched-lookups.ts +73 -0
  669. package/src/sources/gloomberb-cloud/index.ts +64 -12
  670. package/src/sources/gloomberb-cloud/normalizers.ts +31 -2
  671. package/src/sources/gloomberb-cloud/perpetuals.ts +20 -1
  672. package/src/sources/history-omitted.ts +34 -0
  673. package/src/sources/history-result.ts +3 -0
  674. package/src/sources/history-retention.ts +1 -1
  675. package/src/sources/provider-errors.ts +43 -1
  676. package/src/sources/provider-router/batches.ts +3 -2
  677. package/src/sources/provider-router/cache.ts +2 -1
  678. package/src/sources/provider-router/cached-routes.ts +9 -3
  679. package/src/sources/provider-router/history.ts +9 -8
  680. package/src/sources/provider-router/index.ts +18 -3
  681. package/src/sources/provider-router/primary.ts +5 -4
  682. package/src/sources/provider-router/quote-route.ts +3 -1
  683. package/src/sources/provider-router/routing.ts +2 -1
  684. package/src/sources/provider-router/search.ts +47 -13
  685. package/src/sources/provider-router/streaming.ts +7 -4
  686. package/src/sources/sec-edgar.ts +1 -3
  687. package/src/state/app/context/index.tsx +9 -13
  688. package/src/state/app/tab-cursor.ts +149 -12
  689. package/src/state/hooks/quote-streaming.ts +3 -2
  690. package/src/state/hooks/session-snapshot.ts +3 -2
  691. package/src/state/persist-scheduler.ts +12 -4
  692. package/src/sync/controller.ts +22 -1
  693. package/src/sync/core-contributors.ts +58 -15
  694. package/src/team/collections-sync.ts +14 -4
  695. package/src/team/collections.ts +40 -11
  696. package/src/team/store.ts +5 -4
  697. package/src/telemetry/attention-counts.ts +8 -2
  698. package/src/telemetry/crash-reports-dom.ts +3 -0
  699. package/src/telemetry/crash-reports-node.ts +7 -4
  700. package/src/telemetry/crash-reports.ts +11 -11
  701. package/src/telemetry/usage-counts.ts +16 -12
  702. package/src/theme/colors.ts +2 -0
  703. package/src/theme/font-scale.ts +2 -0
  704. package/src/theme/ui-tokens.ts +71 -0
  705. package/src/tickers/batched-lookups.ts +186 -0
  706. package/src/tickers/company-name-listing.ts +97 -0
  707. package/src/tickers/company-name.ts +13 -0
  708. package/src/tickers/instrument-coverage.ts +112 -0
  709. package/src/tickers/listing-fate.ts +49 -9
  710. package/src/tickers/listing-venues.ts +24 -5
  711. package/src/tickers/open-target.ts +13 -5
  712. package/src/tickers/search/index.ts +112 -35
  713. package/src/tickers/search/ranking.ts +49 -1
  714. package/src/tickers/terminal-codes.ts +79 -0
  715. package/src/tickers/ticker-changes.ts +74 -3
  716. package/src/tickers/us-listing.ts +2 -1
  717. package/src/time-series/chart-data.ts +79 -8
  718. package/src/time-series/fundamentals.ts +55 -7
  719. package/src/time-series/ice-credit.ts +17 -2
  720. package/src/time-series/live-bars.ts +21 -8
  721. package/src/time-series/live-quotes.ts +2 -3
  722. package/src/time-series/price-comparison.ts +6 -1
  723. package/src/time-series/resolve.ts +65 -10
  724. package/src/time-series/spec.ts +2 -1
  725. package/src/time-series/studies.ts +3 -3
  726. package/src/time-series/types.ts +22 -0
  727. package/src/time-series/use-chart-resolution.ts +2 -1
  728. package/src/types/config.ts +7 -0
  729. package/src/types/data-provider.ts +20 -0
  730. package/src/types/headless.ts +142 -1
  731. package/src/types/instrument.ts +6 -0
  732. package/src/types/plugin.ts +32 -9
  733. package/src/types/price-history.ts +5 -0
  734. package/src/types/ticker-changes.ts +19 -0
  735. package/src/types/ticker.ts +10 -2
  736. package/src/ui/context-menu.tsx +2 -0
  737. package/src/ui/host.tsx +40 -4
  738. package/src/ui/measured-cells.ts +13 -7
  739. package/src/ui/qr.ts +7 -5
  740. package/src/updater/index.ts +5 -5
  741. package/src/utils/calendar-date.ts +86 -27
  742. package/src/utils/currency-units.ts +17 -0
  743. package/src/utils/debug-log.ts +1 -1
  744. package/src/utils/dividend-cash.ts +48 -0
  745. package/src/utils/dom-screenshot-rasters.ts +3 -1
  746. package/src/utils/dom-screenshot.ts +14 -10
  747. package/src/utils/exchange-rate-snapshot.ts +9 -4
  748. package/src/utils/external-url.ts +1 -0
  749. package/src/utils/fiscal-period.ts +47 -0
  750. package/src/utils/format.ts +5 -2
  751. package/src/utils/forward-dividend.ts +160 -11
  752. package/src/utils/futures-generic.ts +34 -8
  753. package/src/utils/http-proxy-response.ts +2 -1
  754. package/src/utils/local-utc-offset.ts +2 -0
  755. package/src/utils/log-failure.ts +31 -0
  756. package/src/utils/option-expiry.ts +12 -12
  757. package/src/utils/price-history-integrity.ts +56 -0
  758. package/src/utils/price-history.ts +13 -1
  759. package/src/utils/sec-filings.ts +245 -0
  760. package/src/utils/selection-clipboard.ts +5 -2
  761. package/src/utils/throttled-fetch.ts +1 -0
  762. package/src/{plugins/builtin/dividend-yield/trailing-cash.ts → utils/trailing-dividend.ts} +55 -8
  763. package/src/utils/url.ts +1 -0
  764. package/src/utils/utc-time.ts +11 -0
  765. package/src/plugins/builtin/chat/share/targets.ts +0 -27
  766. package/src/renderers/dom/host/top-surface.ts +0 -119
  767. package/src/shares/dialog.tsx +0 -221
  768. package/src/shares/live.ts +0 -19
@@ -2,38 +2,59 @@ import type { Quote } from "../types/financials";
2
2
  import { getCompletedRegularSessionDisplay, type ActiveQuoteDisplay } from "../market-data/market/status";
3
3
  import { regularSessionValuationTime } from "../market-data/quotes/regular-session-valuation";
4
4
  import type { ReportTime } from "../utils/utc-time";
5
+ import { futuresSettlementDisplay, type FuturesSettlement } from "./futures-positions";
5
6
  import { getPortfolioQuoteDisplay, type PortfolioPositionMetrics } from "./position-metrics";
6
7
 
7
8
  /**
8
9
  * What a portfolio's market value is priced at. `latest-print` (the default,
9
10
  * as brokers value a book) takes the last trade, pre-market and after hours
10
11
  * included. `regular-close` takes the price Last shows: the regular session's
11
- * close once it is over, so a value is Last times the shares.
12
+ * close once it is over, so a value is Last times the shares. A future has no
13
+ * regular close but its exchange settlement, which `quote` gives as Settle:
14
+ * on that basis it is valued there, dated by its session.
12
15
  */
13
16
  export type PortfolioValuationBasis = "latest-print" | "regular-close";
14
17
 
15
18
  /** The basis as reports and panes state it, after "Priced at". */
16
- export const VALUATION_BASIS_LABELS: Record<PortfolioValuationBasis, string> = {
19
+ const VALUATION_BASIS_LABELS: Record<PortfolioValuationBasis, string> = {
17
20
  "latest-print": "the latest print, extended hours included",
18
21
  "regular-close": "the regular close",
19
22
  };
20
23
 
24
+ /** The basis as a book holding futures states it: at the close, they are priced at their settlement. */
25
+ export function valuationBasisLabel(basis: PortfolioValuationBasis, { futures = false }: { futures?: boolean } = {}): string {
26
+ return basis === "regular-close" && futures ? `${VALUATION_BASIS_LABELS[basis]}, futures at the settle` : VALUATION_BASIS_LABELS[basis];
27
+ }
28
+
21
29
  /** Whether the regular close values this position: an option keeps its mark on either basis. */
22
30
  function valuesAtClose(metrics: Pick<PortfolioPositionMetrics, "valuesAtMark"> | null, basis: PortfolioValuationBasis): boolean {
23
31
  return basis === "regular-close" && !metrics?.valuesAtMark;
24
32
  }
25
33
 
34
+ /** Whether a position is valued at its exchange settlement on `basis`: a future at the close. */
35
+ export function valuesAtSettlement(metrics: Pick<PortfolioPositionMetrics, "isFuture"> | null, basis: PortfolioValuationBasis): boolean {
36
+ return basis === "regular-close" && !!metrics?.isFuture;
37
+ }
38
+
26
39
  /**
27
40
  * The price a position is valued at on `basis`. The latest print is
28
41
  * getPortfolioQuoteDisplay's. The regular close is the completed regular
29
42
  * session's close and its move (getCompletedRegularSessionDisplay), the
30
43
  * headline Last and Chg show, and the live price while that session trades.
44
+ * A future at the close is its settlement and the move from the one before;
45
+ * without a confirmed settlement it has no price on that basis, never the
46
+ * Globex print in its place.
31
47
  */
32
48
  export function getPortfolioValuationDisplay(
33
49
  metrics: PortfolioPositionMetrics,
34
50
  quote: Quote | null | undefined,
35
51
  basis: PortfolioValuationBasis = "latest-print",
52
+ settlement?: FuturesSettlement | null,
36
53
  ): ActiveQuoteDisplay | null {
54
+ if (valuesAtSettlement(metrics, basis)) {
55
+ return settlement && (metrics.positionCount === 0 || metrics.pnlLots.some((lot) => Number.isFinite(lot.priceUnits)))
56
+ ? futuresSettlementDisplay(settlement) : null;
57
+ }
37
58
  const live = getPortfolioQuoteDisplay(metrics, quote);
38
59
  if (!live || !valuesAtClose(metrics, basis)) return live;
39
60
  return getCompletedRegularSessionDisplay(quote) ?? live;
@@ -42,13 +63,19 @@ export function getPortfolioValuationDisplay(
42
63
  /**
43
64
  * When the price a position is valued at on `basis` is from: the quote's time
44
65
  * for a print, the session's close for a regular close
45
- * (regularSessionValuationTime). Null without a dated quote.
66
+ * (regularSessionValuationTime), and a future's settlement session at the
67
+ * close. Null without a dated quote or settlement.
46
68
  */
47
69
  export function getPortfolioValuationTime(
48
70
  quote: Quote | null | undefined,
49
71
  basis: PortfolioValuationBasis = "latest-print",
50
- metrics: Pick<PortfolioPositionMetrics, "valuesAtMark"> | null = null,
72
+ metrics: Pick<PortfolioPositionMetrics, "valuesAtMark" | "isFuture"> | null = null,
73
+ settlement?: Pick<FuturesSettlement, "date"> | null,
51
74
  ): ReportTime | null {
75
+ if (valuesAtSettlement(metrics, basis)) {
76
+ const day = settlement ? Date.parse(`${settlement.date}T00:00:00Z`) : Number.NaN;
77
+ return Number.isFinite(day) ? { time: day, dateOnly: true } : null;
78
+ }
52
79
  if (!quote) return null;
53
80
  if (valuesAtClose(metrics, basis)) return regularSessionValuationTime(quote);
54
81
  return Number.isFinite(quote.lastUpdated) && quote.lastUpdated > 0 ? { time: quote.lastUpdated, dateOnly: false } : null;
@@ -43,6 +43,7 @@ exports[`the runtime exports of every public subpath are the reviewed ones 1`] =
43
43
  "BulletList",
44
44
  "Button",
45
45
  "ButtonActionScope",
46
+ "Card",
46
47
  "ChartStrip",
47
48
  "ChartTableHeader",
48
49
  "Checkbox",
@@ -63,6 +64,7 @@ exports[`the runtime exports of every public subpath are the reviewed ones 1`] =
63
64
  "EmptyState",
64
65
  "ExternalLink",
65
66
  "ExternalLinkText",
67
+ "FRAMED_TEXTAREA_DESKTOP_STYLE",
66
68
  "FeedDataTableStackView",
67
69
  "FieldGrid",
68
70
  "FieldLabel",
@@ -130,6 +132,7 @@ exports[`the runtime exports of every public subpath are the reviewed ones 1`] =
130
132
  "curveSlope",
131
133
  "curveStrip",
132
134
  "curveSurfaceMinRows",
135
+ "fieldFrameStyle",
133
136
  "fieldGridColumns",
134
137
  "fieldGridRows",
135
138
  "findMetricTreemapNeighbor",
@@ -156,6 +159,7 @@ exports[`the runtime exports of every public subpath are the reviewed ones 1`] =
156
159
  "sortStackItems",
157
160
  "spanAxisFormatter",
158
161
  "spanDigits",
162
+ "stackedSeries",
159
163
  "statGridColumns",
160
164
  "statGridRows",
161
165
  "staticSeries",
@@ -53,5 +53,6 @@ export type {
53
53
  } from "../series/csv";
54
54
  export type {
55
55
  ExpressionSeriesDefinition, LoadedSeriesDefinition, SeriesDefinition, SeriesDrawStyle, SeriesFrequency,
56
- SeriesAttribution, SeriesInput, SeriesLoadOutput, SeriesLoadRequest, SeriesLoadResult, SeriesPoint,
56
+ SeriesAttribution, SeriesInput, SeriesLine, SeriesLinesLoadResult, SeriesLoadOutput, SeriesLoadRequest, SeriesLoadResult, SeriesPlacement,
57
+ SeriesPoint,
57
58
  } from "../series/types";
@@ -1,3 +1,4 @@
1
+ import { logFailure } from "../utils/log-failure";
1
2
  import { useEffect } from "react";
2
3
  import { Box, useUiHost } from "../ui";
3
4
  import { ConfirmDialog } from "../components/ui/confirm-dialog";
@@ -80,6 +81,6 @@ export async function confirmRemoteChange(
80
81
  closeOnClickOutside: false,
81
82
  closeOnEscape: true,
82
83
  content: (context) => <RemoteChangeDialog {...context} prompt={prompt} signal={signal} />,
83
- }).catch(() => false);
84
+ }).catch(logFailure("remote.confirm-change", "Opening remote change confirmation failed", false));
84
85
  return answer === true;
85
86
  }
@@ -49,7 +49,7 @@ export async function startRemoteControlServer(options: RemoteControlServerOptio
49
49
  await writeRemoteEndpointFiles(options.dataDir, endpoint);
50
50
  } catch (error) {
51
51
  await closeHttpServer(httpServer);
52
- await removeRemoteEndpointFiles(options.dataDir, endpoint).catch(() => {});
52
+ await removeRemoteEndpointFiles(options.dataDir, endpoint).catch(() => { /* Intentional ignore: Remove partial endpoint files while preserving the startup error rethrown below. */ });
53
53
  throw error;
54
54
  }
55
55
 
@@ -13,6 +13,8 @@ import {
13
13
  resolveCollectionTicker,
14
14
  } from "../components/command-bar/workflow/collection-ticker";
15
15
  import type { RemoteChangePrompt } from "./types";
16
+ import { RemoteOperationError } from "./controller-utils";
17
+ import { isProviderBusy, SEARCH_BUSY_CODE, SEARCH_BUSY_MESSAGE } from "../sources/provider-errors";
16
18
 
17
19
  /**
18
20
  * `watchlist.add` and `watchlist.remove`: one ticker on one of the person's
@@ -126,6 +128,7 @@ function listingLabel(symbol: string, exchange: string): string {
126
128
  }
127
129
 
128
130
  async function resolveListing(
131
+ action: WatchlistAction,
129
132
  input: WatchlistChangeInput,
130
133
  deps: SharedWorkflowDeps,
131
134
  ): Promise<ResolvedTickerSearch | null> {
@@ -147,6 +150,11 @@ async function resolveListing(
147
150
  ? `${query} matches more than one listing: ${error.listings.slice(0, 6).join(", ")}. Pass exchange to pick one.`
148
151
  : error.message);
149
152
  }
153
+ // Search too busy to look the listing up is no miss. A remove has nothing to do: what is not saved is on no list.
154
+ if (isProviderBusy(error)) {
155
+ if (action === "remove") return null;
156
+ throw new RemoteOperationError(SEARCH_BUSY_CODE, SEARCH_BUSY_MESSAGE);
157
+ }
150
158
  throw error;
151
159
  }
152
160
  }
@@ -168,7 +176,7 @@ export async function planWatchlistChange(
168
176
  deps: SharedWorkflowDeps,
169
177
  ): Promise<WatchlistChangePlan> {
170
178
  const watchlist = resolveWatchlistTarget(deps.getState().config, input.watchlist);
171
- const resolved = await resolveListing(input, deps);
179
+ const resolved = await resolveListing(action, input, deps);
172
180
  let listing: ResolvedListing;
173
181
  let symbol: string;
174
182
  let exchange: string;
@@ -159,7 +159,7 @@ export function WebDataTableHeader<C extends DataTableColumn>({
159
159
  >
160
160
  {column.headerLeading !== undefined ? <Leading>{column.headerLeading}</Leading> : null}
161
161
  <span
162
- title={column.label || undefined}
162
+ title={column.title ?? (column.label || undefined)}
163
163
  style={{
164
164
  ...clippedCellTextStyle(column, color, TextAttributes.BOLD),
165
165
  width: "auto",
@@ -0,0 +1,312 @@
1
+ /** @jsxImportSource react */
2
+ import { useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState, type PointerEvent as ReactPointerEvent } from "react";
3
+ import type { AudioPlaybackState, AudioPlayerHandle, HostAudioPlayerProps, HostQueryBarItem } from "../../../ui/host";
4
+ import { releasePointerFocus } from "../host/focus-scope";
5
+ import { WebIcon } from "./icons";
6
+ import { InlineChoices } from "./query-bar";
7
+
8
+ /** "4:05", or "1:02:09" past an hour. */
9
+ function clock(seconds: number): string {
10
+ const total = Math.max(0, Math.floor(Number.isFinite(seconds) ? seconds : 0));
11
+ const hours = Math.floor(total / 3600);
12
+ const minutes = Math.floor((total % 3600) / 60);
13
+ const rest = String(total % 60).padStart(2, "0");
14
+ return hours > 0 ? `${hours}:${String(minutes).padStart(2, "0")}:${rest}` : `${minutes}:${rest}`;
15
+ }
16
+
17
+ /** "1×", "1.5×". */
18
+ function rateLabel(rate: number): string {
19
+ return `${Number(rate.toFixed(2))}\u00d7`;
20
+ }
21
+
22
+ /** The player that is playing; starting another stops it, so two panes never talk over each other. */
23
+ let playingNow: (() => void) | null = null;
24
+
25
+ function mediaErrorMessage(audio: HTMLAudioElement): string {
26
+ switch (audio.error?.code) {
27
+ case 2: return "The recording stopped loading.";
28
+ case 3: return "The recording could not be decoded.";
29
+ case 4: return "The recording could not be opened.";
30
+ default: return "The recording could not be played.";
31
+ }
32
+ }
33
+
34
+ /**
35
+ * The audio player of the desktop and the web: one band in the look of the
36
+ * query bar (play, the time, a seek track, the speed as inline choices) over
37
+ * an `<audio>` element. MP3 plays in Chromium and WebKit alike, and the
38
+ * signed address it plays answers Range requests, so seeking is the
39
+ * browser's own.
40
+ */
41
+ export function WebAudioPlayer({
42
+ src,
43
+ sourceKey,
44
+ label,
45
+ durationSeconds,
46
+ rates,
47
+ rate,
48
+ onRateChange,
49
+ handleRef,
50
+ onTimeUpdate,
51
+ onStateChange,
52
+ onError,
53
+ }: HostAudioPlayerProps) {
54
+ const audioRef = useRef<HTMLAudioElement | null>(null);
55
+ const trackRef = useRef<HTMLDivElement | null>(null);
56
+ const [state, setState] = useState<AudioPlaybackState>("idle");
57
+ const [time, setTime] = useState(0);
58
+ const [duration, setDuration] = useState(durationSeconds ?? 0);
59
+ // Where the pointer holds the track, until it lets go.
60
+ const [scrub, setScrub] = useState<number | null>(null);
61
+ // Whether the listener wants it playing: an address that runs out mid-call
62
+ // comes back playing, and one paused stays paused.
63
+ const wantsPlay = useRef(false);
64
+ const callbacks = useRef({ onTimeUpdate, onStateChange, onError });
65
+ callbacks.current = { onTimeUpdate, onStateChange, onError };
66
+ const rateRef = useRef(rate);
67
+ rateRef.current = rate;
68
+
69
+ const report = useCallback((next: AudioPlaybackState) => {
70
+ setState(next);
71
+ callbacks.current.onStateChange?.(next);
72
+ }, []);
73
+
74
+ const play = useCallback(async () => {
75
+ const audio = audioRef.current;
76
+ if (!audio) return;
77
+ wantsPlay.current = true;
78
+ try {
79
+ await audio.play();
80
+ } catch {
81
+ // Refused (no source yet, or the page has not been touched): the state
82
+ // follows the element's own events.
83
+ }
84
+ }, []);
85
+ const pause = useCallback(() => {
86
+ wantsPlay.current = false;
87
+ audioRef.current?.pause();
88
+ }, []);
89
+ const seek = useCallback((seconds: number, options?: { play?: boolean }) => {
90
+ const audio = audioRef.current;
91
+ if (!audio) return;
92
+ const limit = Number.isFinite(audio.duration) && audio.duration > 0 ? audio.duration : Number.POSITIVE_INFINITY;
93
+ const target = Math.max(0, Math.min(seconds, limit));
94
+ audio.currentTime = target;
95
+ setTime(target);
96
+ callbacks.current.onTimeUpdate?.(target);
97
+ if (options?.play) void play();
98
+ }, [play]);
99
+
100
+ useImperativeHandle(handleRef, (): AudioPlayerHandle => ({
101
+ play,
102
+ pause,
103
+ toggle: async () => {
104
+ const audio = audioRef.current;
105
+ if (!audio) return;
106
+ if (audio.paused) await play();
107
+ else pause();
108
+ },
109
+ seek,
110
+ seekBy: (delta) => seek((audioRef.current?.currentTime ?? 0) + delta),
111
+ currentTime: () => audioRef.current?.currentTime ?? 0,
112
+ }), [pause, play, seek]);
113
+
114
+ useEffect(() => {
115
+ const audio = audioRef.current;
116
+ if (!audio) return;
117
+ const onTime = () => {
118
+ setTime(audio.currentTime);
119
+ callbacks.current.onTimeUpdate?.(audio.currentTime);
120
+ };
121
+ const onDuration = () => {
122
+ if (Number.isFinite(audio.duration) && audio.duration > 0) setDuration(audio.duration);
123
+ };
124
+ const onPlay = () => {
125
+ if (playingNow && playingNow !== pause) playingNow();
126
+ playingNow = pause;
127
+ };
128
+ const onPlaying = () => report("playing");
129
+ const onPause = () => { if (!audio.ended) report("paused"); };
130
+ const onWaiting = () => report("loading");
131
+ const onEnded = () => {
132
+ wantsPlay.current = false;
133
+ report("ended");
134
+ };
135
+ const onFailure = () => {
136
+ report("error");
137
+ callbacks.current.onError?.(mediaErrorMessage(audio));
138
+ };
139
+ // WebKit drops the rate when a source loads.
140
+ const onLoaded = () => { audio.playbackRate = rateRef.current; };
141
+ audio.addEventListener("timeupdate", onTime);
142
+ audio.addEventListener("seeked", onTime);
143
+ audio.addEventListener("durationchange", onDuration);
144
+ audio.addEventListener("loadedmetadata", onDuration);
145
+ audio.addEventListener("loadedmetadata", onLoaded);
146
+ audio.addEventListener("play", onPlay);
147
+ audio.addEventListener("playing", onPlaying);
148
+ audio.addEventListener("pause", onPause);
149
+ audio.addEventListener("waiting", onWaiting);
150
+ audio.addEventListener("ended", onEnded);
151
+ audio.addEventListener("error", onFailure);
152
+ return () => {
153
+ if (playingNow === pause) playingNow = null;
154
+ audio.removeEventListener("timeupdate", onTime);
155
+ audio.removeEventListener("seeked", onTime);
156
+ audio.removeEventListener("durationchange", onDuration);
157
+ audio.removeEventListener("loadedmetadata", onDuration);
158
+ audio.removeEventListener("loadedmetadata", onLoaded);
159
+ audio.removeEventListener("play", onPlay);
160
+ audio.removeEventListener("playing", onPlaying);
161
+ audio.removeEventListener("pause", onPause);
162
+ audio.removeEventListener("waiting", onWaiting);
163
+ audio.removeEventListener("ended", onEnded);
164
+ audio.removeEventListener("error", onFailure);
165
+ };
166
+ }, [pause, report]);
167
+
168
+ // A new recording starts from the top; a new address for the same one (the
169
+ // signed one ran out) picks up where it was.
170
+ const loaded = useRef<{ src: string; key: string } | null>(null);
171
+ useEffect(() => {
172
+ const audio = audioRef.current;
173
+ if (!audio || !src) return;
174
+ const previous = loaded.current;
175
+ if (previous?.src === src && previous.key === sourceKey) return;
176
+ const resume = previous?.key === sourceKey ? { at: audio.currentTime, play: wantsPlay.current } : null;
177
+ loaded.current = { src, key: sourceKey };
178
+ if (!resume) {
179
+ wantsPlay.current = false;
180
+ setTime(0);
181
+ setDuration(durationSeconds ?? 0);
182
+ report("idle");
183
+ }
184
+ audio.src = src;
185
+ audio.load();
186
+ if (!resume) return;
187
+ // A fresh address clears the failure of the old one.
188
+ report(resume.play ? "loading" : "paused");
189
+ const restore = () => {
190
+ audio.currentTime = resume.at;
191
+ if (resume.play) void play();
192
+ };
193
+ audio.addEventListener("loadedmetadata", restore, { once: true });
194
+ return () => audio.removeEventListener("loadedmetadata", restore);
195
+ }, [durationSeconds, play, report, sourceKey, src]);
196
+
197
+ useEffect(() => {
198
+ const audio = audioRef.current;
199
+ if (!audio) return;
200
+ audio.defaultPlaybackRate = rate;
201
+ audio.playbackRate = rate;
202
+ }, [rate]);
203
+
204
+ // Leaving the call stops it. The element is read now: React has cleared the
205
+ // ref by the time the cleanup runs.
206
+ useEffect(() => {
207
+ const audio = audioRef.current;
208
+ return () => {
209
+ if (!audio) return;
210
+ audio.pause();
211
+ audio.removeAttribute("src");
212
+ audio.load();
213
+ };
214
+ }, []);
215
+
216
+ const total = duration > 0 ? duration : (durationSeconds ?? 0);
217
+ const shown = scrub ?? time;
218
+ const fraction = total > 0 ? Math.max(0, Math.min(1, shown / total)) : 0;
219
+
220
+ const secondsAt = (clientX: number): number => {
221
+ const box = trackRef.current?.getBoundingClientRect();
222
+ if (!box || box.width <= 0 || total <= 0) return 0;
223
+ return Math.max(0, Math.min(1, (clientX - box.left) / box.width)) * total;
224
+ };
225
+ const onTrackDown = (event: ReactPointerEvent<HTMLDivElement>) => {
226
+ if (event.button !== 0 || total <= 0) return;
227
+ event.preventDefault();
228
+ event.stopPropagation();
229
+ event.currentTarget.setPointerCapture?.(event.pointerId);
230
+ setScrub(secondsAt(event.clientX));
231
+ };
232
+ const onTrackMove = (event: ReactPointerEvent<HTMLDivElement>) => {
233
+ if (scrub === null) return;
234
+ setScrub(secondsAt(event.clientX));
235
+ };
236
+ const onTrackUp = (event: ReactPointerEvent<HTMLDivElement>) => {
237
+ if (scrub === null) return;
238
+ event.currentTarget.releasePointerCapture?.(event.pointerId);
239
+ const target = secondsAt(event.clientX);
240
+ setScrub(null);
241
+ seek(target);
242
+ };
243
+
244
+ const playing = state === "playing" || (state === "loading" && wantsPlay.current);
245
+ const speed = useMemo<HostQueryBarItem>(() => ({
246
+ id: "speed",
247
+ kind: "select",
248
+ label: "Speed",
249
+ valueLabel: rateLabel(rate),
250
+ narrowing: false,
251
+ inline: true,
252
+ options: rates.map((option) => ({ value: String(option), label: rateLabel(option), selected: option === rate })),
253
+ onSelect: (value) => onRateChange(Number(value)),
254
+ onToggle: () => {},
255
+ onReset: () => {},
256
+ }), [onRateChange, rate, rates]);
257
+
258
+ return (
259
+ <div
260
+ className="gloom-qb gloom-player"
261
+ data-gloom-role="audio-player"
262
+ data-state={state}
263
+ role="group"
264
+ aria-label={label}
265
+ >
266
+ <div className="gloom-qb-scroll">
267
+ <button
268
+ type="button"
269
+ className="gloom-player-toggle"
270
+ aria-label={playing ? "Pause" : "Play"}
271
+ aria-keyshortcuts="Space"
272
+ title={playing ? "Pause (Space)" : "Play (Space)"}
273
+ onMouseDown={(event) => event.stopPropagation()}
274
+ onClick={(event) => {
275
+ const audio = audioRef.current;
276
+ if (audio?.paused) void play();
277
+ else pause();
278
+ releasePointerFocus(event.currentTarget, event.detail);
279
+ }}
280
+ >
281
+ <WebIcon name={playing ? "pause" : "play"} size={11} />
282
+ </button>
283
+ <span className="gloom-player-time" data-gloom-role="audio-elapsed">{clock(shown)}</span>
284
+ <div
285
+ ref={trackRef}
286
+ className="gloom-player-track"
287
+ role="slider"
288
+ aria-label="Position"
289
+ aria-valuemin={0}
290
+ aria-valuemax={Math.round(total)}
291
+ aria-valuenow={Math.round(shown)}
292
+ aria-valuetext={`${clock(shown)} of ${clock(total)}`}
293
+ data-gloom-interactive={total > 0 ? "true" : undefined}
294
+ data-scrubbing={scrub !== null ? "true" : undefined}
295
+ onPointerDown={onTrackDown}
296
+ onPointerMove={onTrackMove}
297
+ onPointerUp={onTrackUp}
298
+ onPointerCancel={() => setScrub(null)}
299
+ onMouseDown={(event) => event.stopPropagation()}
300
+ >
301
+ <div className="gloom-player-rail">
302
+ <div className="gloom-player-fill" style={{ width: `${fraction * 100}%` }} />
303
+ </div>
304
+ <div className="gloom-player-thumb" style={{ left: `${fraction * 100}%` }} />
305
+ </div>
306
+ <span className="gloom-player-time" data-gloom-role="audio-total">{clock(total)}</span>
307
+ <InlineChoices item={speed} />
308
+ </div>
309
+ <audio ref={audioRef} preload="metadata" aria-label={label} />
310
+ </div>
311
+ );
312
+ }
@@ -1,10 +1,13 @@
1
1
  import type { ButtonProps } from "../../../components/ui/button";
2
2
  import { blendHex, colors, type ThemeColors } from "../../../theme/colors";
3
+ import { RADIUS, controlBorderColour } from "../../../theme/ui-tokens";
3
4
 
4
- export const CONTROL_RADIUS = 6;
5
+ /** Buttons, fields, selects and list cards (docs/pane-conventions.md, "Tokens"). */
6
+ export const CONTROL_RADIUS = RADIUS.md;
5
7
 
8
+ /** The border of a control and of a floating surface: `--gloom-control-border`. */
6
9
  export function panelBorder(palette: ThemeColors = colors): string {
7
- return blendHex(palette.border, palette.borderFocused, 0.18);
10
+ return controlBorderColour(palette);
8
11
  }
9
12
 
10
13
  export function panelFill(palette: ThemeColors = colors): string {
@@ -19,85 +22,23 @@ export function selectedPanelFill(palette: ThemeColors = colors): string {
19
22
  return blendHex(palette.selected, palette.bg, 0.42);
20
23
  }
21
24
 
25
+ /** A control's border: the accent while it has focus or is chosen, the control border otherwise. */
22
26
  export function controlBorderColor(focused = false, active = false, palette: ThemeColors = colors): string {
23
- if (active) return palette.borderFocused;
24
- if (focused) return blendHex(palette.borderFocused, palette.textBright, 0.24);
25
- return panelBorder(palette);
26
- }
27
-
28
- function activeHalo(palette: ThemeColors): string {
29
- return `0 0 0 1px ${blendHex(palette.bg, palette.borderFocused, 0.18)}`;
30
- }
31
-
32
- export function controlShadow(active = false, palette: ThemeColors = colors): string {
33
- return active
34
- ? `${activeHalo(palette)}, inset 0 1px 0 ${blendHex(palette.bg, palette.textBright, 0.06)}`
35
- : `inset 0 1px 0 ${blendHex(palette.bg, palette.textBright, 0.04)}`;
36
- }
37
-
38
- const WHITE = "#ffffff";
39
- const BLACK = "#000000";
40
-
41
- function withAlpha(hex: string, alpha: number): string {
42
- return `${hex}${Math.round(alpha * 255).toString(16).padStart(2, "0")}`;
43
- }
44
-
45
- /** How far a raised button's tones move from its fill, toward white at the top and black below. */
46
- const SOLID_RELIEF = { light: 0.12, shade: 0.06, edge: 0.18, shadow: 0.3 };
47
- const SURFACE_RELIEF = { light: 0.05, shade: 0.03, edge: 0.08, shadow: 0.14 };
48
-
49
- interface ButtonRelief {
50
- backgroundImage?: string;
51
- boxShadow: string;
52
- /** Replaces the border colour, so a solid fill runs to its edge. */
53
- borderColor?: string;
54
- /** What the button shows while it is held down. */
55
- pressedImage?: string;
56
- pressedShadow?: string;
57
- }
58
-
59
- /**
60
- * Light from above, built from the button's own fill so it reads the same on
61
- * any surface and in every theme: a little lighter at the top, a little darker
62
- * toward the bottom, a darker band on the bottom edge and a soft shadow under
63
- * it. Nothing draws a line along the top edge. Held down, the light flips and
64
- * the shadow goes, so the button sinks.
65
- */
66
- function raisedRelief(fill: string, tone: typeof SOLID_RELIEF, ring?: string): ButtonRelief {
67
- const shade = blendHex(fill, BLACK, tone.shade);
68
- const edge = blendHex(fill, BLACK, tone.edge);
69
- const drop = `0 1px 2px ${withAlpha(blendHex(fill, BLACK, 0.75), tone.shadow)}`;
70
- return {
71
- backgroundImage: `linear-gradient(${blendHex(fill, WHITE, tone.light)}, ${shade} calc(100% - 1px), ${edge} calc(100% - 1px))`,
72
- boxShadow: ring ? `${ring}, ${drop}` : drop,
73
- pressedImage: `linear-gradient(${shade}, ${fill})`,
74
- pressedShadow: ring ?? "none",
75
- };
27
+ if (active || focused) return palette.borderFocused;
28
+ return controlBorderColour(palette);
76
29
  }
77
30
 
78
31
  /**
79
- * A button's relief. Primary, danger and a selected button are raised from
80
- * their fill; a secondary one gets the same light, fainter. Ghost and plain
81
- * buttons have no fill to light and stay flat, and so does a disabled one.
32
+ * The four buttons. Primary (and danger, its negative twin) is filled and
33
+ * bold; secondary is a 1px border; ghost (and plain) is text that takes the
34
+ * hover fill; a chosen (`active`) button is the selection fill. None of them
35
+ * casts a shadow. The icon button is `.gloom-icon-button` in styles.css.
82
36
  */
83
- export function buttonRelief(
84
- props: Pick<ButtonProps, "variant" | "active" | "disabled">,
85
- palette: ThemeColors = colors,
86
- ): ButtonRelief {
87
- if (props.variant === "plain" || props.variant === "ghost" || props.disabled) return { boxShadow: "none" };
88
- const fill = buttonPalette(props, palette).bg;
89
- if (props.active) return raisedRelief(fill, SOLID_RELIEF, `0 0 0 1px ${withAlpha(palette.borderFocused, 0.18)}`);
90
- if (props.variant === "primary" || props.variant === "danger") {
91
- return { ...raisedRelief(fill, SOLID_RELIEF), borderColor: "transparent" };
92
- }
93
- return raisedRelief(fill, SURFACE_RELIEF);
94
- }
95
-
96
37
  export function buttonPalette(
97
38
  props: Pick<ButtonProps, "variant" | "active" | "disabled">,
98
39
  palette: ThemeColors = colors,
99
40
  ) {
100
- if (props.variant === "plain") {
41
+ if (props.variant === "plain" || props.variant === "ghost") {
101
42
  return {
102
43
  fg: props.disabled ? palette.textMuted : props.active ? palette.textBright : palette.textDim,
103
44
  bg: "transparent",
@@ -107,8 +48,8 @@ export function buttonPalette(
107
48
  if (props.disabled) {
108
49
  return {
109
50
  fg: palette.textMuted,
110
- bg: subtlePanelFill(palette),
111
- border: panelBorder(palette),
51
+ bg: "transparent",
52
+ border: controlBorderColour(palette),
112
53
  };
113
54
  }
114
55
  if (props.active) {
@@ -132,18 +73,12 @@ export function buttonPalette(
132
73
  bg: palette.negative,
133
74
  border: palette.negative,
134
75
  };
135
- case "ghost":
136
- return {
137
- fg: palette.textDim,
138
- bg: "transparent",
139
- border: panelBorder(palette),
140
- };
141
76
  case "secondary":
142
77
  default:
143
78
  return {
144
79
  fg: palette.text,
145
- bg: panelFill(palette),
146
- border: panelBorder(palette),
80
+ bg: "transparent",
81
+ border: controlBorderColour(palette),
147
82
  };
148
83
  }
149
84
  }