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
@@ -209,7 +209,7 @@ function createNotesFilesCapability(): PluginCapability {
209
209
  await io(input).delete(file(input));
210
210
  return null;
211
211
  }, "action"),
212
- listKeys: op((input: any) => io(input).listKeys()),
212
+ listKeys: op((input: any) => io(input).listKeys({ strict: input.strict === true })),
213
213
  },
214
214
  };
215
215
  }
@@ -470,7 +470,7 @@ export class DesktopBackend {
470
470
  case "desktop.dockDetachedPane":
471
471
  case "desktop.closeDetachedPane":
472
472
  case "desktop.focusDetachedPane":
473
- return handleDesktopWorkspaceRequest(this, request);
473
+ return handleDesktopWorkspaceRequest(this, rpc, request);
474
474
  case "pluginState.setMany":
475
475
  case "pluginState.delete":
476
476
  return handleDesktopPluginStateRequest(this.requireServices().persistence.pluginState, request);
@@ -108,6 +108,7 @@ export async function initializeDesktopBackend(
108
108
  ): Promise<ElectrobunBackendInit> {
109
109
  const windowTarget = normalizeInitWindowTarget(backend.rpcs.getRpcWindowKey(rpc), payload);
110
110
  backend.rpcs.markWindowRpcReady(rpc);
111
+ backend.stateBroadcaster.forgetWindowState(rpc);
111
112
 
112
113
  const currentConfig = backend.config;
113
114
  const currentServices = backend.services;
@@ -17,9 +17,25 @@ interface DesktopStateBroadcasterOptions<Rpc extends DesktopStateRpc> {
17
17
  forEachReadyWindowRpc: (callback: (rpc: Rpc) => void) => void;
18
18
  }
19
19
 
20
+ /**
21
+ * The parts of a snapshot a window applies, as the text the windows compare;
22
+ * the revision and layout flag only qualify them. Being text, it keeps no
23
+ * reference to objects this process may change later.
24
+ */
25
+ function desktopStateKey(snapshot: DesktopSharedStateSnapshot): string {
26
+ return JSON.stringify({
27
+ config: snapshot.config,
28
+ paneState: snapshot.paneState,
29
+ focusedPaneId: snapshot.focusedPaneId,
30
+ statusBarVisible: snapshot.statusBarVisible,
31
+ });
32
+ }
33
+
20
34
  export class DesktopStateBroadcaster<Rpc extends DesktopStateRpc> {
21
35
  private dockPreview: DesktopDockPreviewState = { paneId: null, edge: null };
22
36
  private themePreview: DesktopThemePreviewState = { theme: null };
37
+ /** The state held by each window that sends its whole state: what it last sent here or was sent. */
38
+ private readonly heldStateKeys = new WeakMap<Rpc, string>();
23
39
 
24
40
  constructor(private readonly options: DesktopStateBroadcasterOptions<Rpc>) {}
25
41
 
@@ -35,10 +51,36 @@ export class DesktopStateBroadcaster<Rpc extends DesktopStateRpc> {
35
51
  this.dockPreview = { paneId: null, edge: null };
36
52
  }
37
53
 
54
+ /** A window that sent its whole state holds it, and is not sent it back. */
55
+ noteWindowState(rpc: Rpc, snapshot: DesktopSharedStateSnapshot): void {
56
+ this.heldStateKeys.set(rpc, desktopStateKey(snapshot));
57
+ }
58
+
59
+ /** A window that starts over (a load or reload) gets every state until it sends its own. */
60
+ forgetWindowState(rpc: Rpc): void {
61
+ this.heldStateKeys.delete(rpc);
62
+ }
63
+
64
+ /**
65
+ * Sends the state to every ready window in the order the changes happen,
66
+ * except a window that sent its whole state and already holds this one: it
67
+ * would decode and compare the state only to drop it. A popped-out window
68
+ * gets every state. It applies one only when the part for its own pane
69
+ * changed, and then whole, so the state its own change produces is what
70
+ * brings its copy of the tabs and settings up to date.
71
+ */
38
72
  sendDesktopState(snapshot: DesktopSharedStateSnapshot | null): void {
39
73
  if (!snapshot) return;
40
- const encodedSnapshot = encodeRpcValue(snapshot) as DesktopSharedStateSnapshot;
74
+ let encodedSnapshot: DesktopSharedStateSnapshot | null = null;
75
+ let snapshotKey: string | null = null;
41
76
  this.options.forEachReadyWindowRpc((rpc) => {
77
+ const heldKey = this.heldStateKeys.get(rpc);
78
+ if (heldKey !== undefined) {
79
+ snapshotKey ??= desktopStateKey(snapshot);
80
+ if (heldKey === snapshotKey) return;
81
+ this.heldStateKeys.set(rpc, snapshotKey);
82
+ }
83
+ encodedSnapshot ??= encodeRpcValue(snapshot) as DesktopSharedStateSnapshot;
42
84
  rpc.send["desktop.state"]({
43
85
  snapshot: encodedSnapshot,
44
86
  });
@@ -1,5 +1,5 @@
1
1
  import type { DesktopWorkspaceRequest } from "../../../shared/protocol";
2
- import type { DesktopBackend } from "../backend";
2
+ import type { DesktopBackend, DesktopRpc } from "../backend";
3
3
 
4
4
  type DockEdge = "left" | "right" | "top" | "bottom";
5
5
 
@@ -18,6 +18,7 @@ function normalizeDockEdge(edge: unknown): DockEdge | undefined {
18
18
 
19
19
  export async function handleDesktopWorkspaceRequest(
20
20
  backend: DesktopBackend,
21
+ rpc: DesktopRpc,
21
22
  request: DesktopWorkspaceRequest,
22
23
  ): Promise<null> {
23
24
  const workspace = backend.requireWorkspace();
@@ -27,6 +28,9 @@ export async function handleDesktopWorkspaceRequest(
27
28
  const snapshot = workspace.syncMainState(request.payload.snapshot);
28
29
  backend.setConfig(snapshot.config);
29
30
  detachedWindows.reconcile();
31
+ // The main window holds what it sent. It gets the state back only when
32
+ // this process changed it: a popped-out window's frame, a stale revision.
33
+ stateBroadcaster.noteWindowState(rpc, request.payload.snapshot);
30
34
  stateBroadcaster.sendDesktopState(snapshot);
31
35
  return null;
32
36
  }
@@ -1,5 +1,6 @@
1
1
  import { ApiRequestError } from "../../../api-client/errors";
2
2
  import { isHistoryRetentionError, parseHistoryRetentionError } from "../../../sources/history-retention";
3
+ import { isProviderBusy, ProviderBusyError } from "../../../sources/provider-errors";
3
4
 
4
5
  const DATE_MARKER = "__gloomDate";
5
6
  const MAP_MARKER = "__gloomMap";
@@ -15,6 +16,8 @@ export async function encodeRpcResponse(load: () => unknown | Promise<unknown>):
15
16
  ok: false,
16
17
  error: { kind: "history-retention", retention: error.retention, candidates: error.candidates, outcomes: error.outcomes },
17
18
  };
19
+ // A search the view runs must tell a busy source from one that found nothing.
20
+ if (isProviderBusy(error)) return { [RESPONSE_MARKER]: 1, ok: false, error: { kind: "provider-busy", message: error.message } };
18
21
  if (!(error instanceof ApiRequestError)) throw error;
19
22
  return {
20
23
  [RESPONSE_MARKER]: 1,
@@ -36,6 +39,10 @@ export function decodeRpcResponse<T = unknown>(value: unknown): T {
36
39
  if (!retention) throw new Error("Invalid desktop response");
37
40
  throw retention;
38
41
  }
42
+ if (response.ok === false && error && typeof error === "object" && error.kind === "provider-busy") {
43
+ if (typeof error.message !== "string") throw new Error("Invalid desktop response");
44
+ throw new ProviderBusyError(error.message);
45
+ }
39
46
  if (response.ok !== false || !error || typeof error !== "object"
40
47
  || (error.kind !== undefined && error.kind !== "api")
41
48
  || typeof error.message !== "string"
@@ -3,6 +3,7 @@ import { createRoot } from "react-dom/client";
3
3
  import { useEffect, useState, type ReactNode } from "react";
4
4
  import { TapeClientContext } from "../../../plugins/builtin/time-sales/use-tape";
5
5
  import { createSnapshotTapeClient } from "../../../plugins/builtin/time-sales/snapshot-client";
6
+ import { createSnapshotScannerClient, ScannerClientContext } from "../../../plugins/builtin/shared/scanner-capture";
6
7
  import type { TapeSnapshot } from "../../../api-client/tape";
7
8
  import { AppProvider, useAppDispatch } from "../../../state/app/context";
8
9
  import { createCliPaneShotConnectionHealth } from "./cli-pane-shot-health";
@@ -513,25 +514,28 @@ async function render() {
513
514
 
514
515
  const tapeClient = createSnapshotTapeClient(payload.tapeSnapshots ?? [],
515
516
  (symbol, exchange) => requestShotMarketData<TapeSnapshot>("getCloudTape", [symbol, exchange]));
517
+ const scannerClient = createSnapshotScannerClient(payload.scannerSnapshots ?? []);
516
518
  createRoot(rootElement).render(
517
519
  <TapeClientContext.Provider value={tapeClient}>
518
- <RemoteUiRegistryProvider>
519
- <CaptureShotSemanticUi />
520
- <DomHostProviders ui={webUiHost} renderer={rendererHost}>
521
- <AppProvider config={payload.config} desktopSnapshot={{
522
- config: payload.config,
523
- paneState: payload.paneState,
524
- focusedPaneId: payload.paneId,
525
- statusBarVisible: false,
526
- }}>
527
- <HydratePayload payload={payload}>
528
- <ChartSnapshotContext.Provider value={payload.chartModel ?? null}>
529
- <ShotPane payload={payload} registry={services.pluginRegistry} />
530
- </ChartSnapshotContext.Provider>
531
- </HydratePayload>
532
- </AppProvider>
533
- </DomHostProviders>
534
- </RemoteUiRegistryProvider>
520
+ <ScannerClientContext.Provider value={scannerClient}>
521
+ <RemoteUiRegistryProvider>
522
+ <CaptureShotSemanticUi />
523
+ <DomHostProviders ui={webUiHost} renderer={rendererHost}>
524
+ <AppProvider config={payload.config} desktopSnapshot={{
525
+ config: payload.config,
526
+ paneState: payload.paneState,
527
+ focusedPaneId: payload.paneId,
528
+ statusBarVisible: false,
529
+ }}>
530
+ <HydratePayload payload={payload}>
531
+ <ChartSnapshotContext.Provider value={payload.chartModel ?? null}>
532
+ <ShotPane payload={payload} registry={services.pluginRegistry} />
533
+ </ChartSnapshotContext.Provider>
534
+ </HydratePayload>
535
+ </AppProvider>
536
+ </DomHostProviders>
537
+ </RemoteUiRegistryProvider>
538
+ </ScannerClientContext.Provider>
535
539
  </TapeClientContext.Provider>,
536
540
  );
537
541
  }
@@ -2,7 +2,7 @@
2
2
  import { useCallback, useEffect, useRef, useState, type ReactNode } from "react";
3
3
  import { FloatingPaneWrapper } from "../../../components/layout/floating-pane";
4
4
  import { CaptureDatesDataContext } from "../../../components/layout/pane/capture-status";
5
- import { PaneFooterProvider, hasPaneFooterContent, type PaneFooterSegment } from "../../../components/layout/pane/footer";
5
+ import { PaneFooterProvider, datesPaneData, hasPaneFooterContent, type PaneFooterSegment } from "../../../components/layout/pane/footer";
6
6
  import { resolvePaneBodyFrame } from "../../../components/layout/pane/sizing";
7
7
 
8
8
  /** What a capture's status line dates (`CaptureStatusLine`): the pane's own data, the quotes on screen, or neither. */
@@ -150,9 +150,9 @@ export function PaneShotFrame({ paneId, title, width, height, preserveStatus = f
150
150
  return <PaneFooterProvider>{(registeredFooter) => {
151
151
  const status: PaneFooterSegment[] = statusLine ? [{ id: "shot-status", parts: [{ text: statusText, tone: "label" }] }] : [];
152
152
  const info = registeredFooter.info.filter((segment) => (preserveStatus || segment.icon === "warning")
153
- && !(segment.asOf && (datesData || crowded))
153
+ && !(datesPaneData(segment) && (datesData || crowded))
154
154
  && !(segment.quoteStatus && statusLine && dates !== null));
155
- shownAsOf.current = info.some((segment) => segment.asOf);
155
+ shownAsOf.current = info.some(datesPaneData);
156
156
  const footer = { info: [...info, ...status], hints: [], menu: [], keys: [] };
157
157
  const bodyFrame = resolvePaneBodyFrame({
158
158
  width,
@@ -6,12 +6,12 @@ import type {
6
6
  DesktopWindowBridge,
7
7
  } from "../../../../../types/desktop-window";
8
8
  import { backendRequest, getElectrobunBackendInitSnapshot, onBackendMessage } from "../../backend-rpc";
9
- import { detachedSnapshotKey, prepareDetachedSnapshot } from "./snapshot";
9
+ import { createDetachedSnapshotFilter } from "./snapshot";
10
10
 
11
11
  export function createDesktopWindowBridge(kind: "main" | "detached", paneId?: string): DesktopWindowBridge {
12
12
  const initialDesktopSnapshot = getElectrobunBackendInitSnapshot()?.desktopSnapshot ?? null;
13
- let lastDetachedSnapshotKey = kind === "detached" && paneId && initialDesktopSnapshot
14
- ? detachedSnapshotKey(initialDesktopSnapshot, paneId)
13
+ const takeDetachedSnapshot = kind === "detached" && paneId
14
+ ? createDetachedSnapshotFilter(paneId, initialDesktopSnapshot)
15
15
  : null;
16
16
 
17
17
  return {
@@ -46,11 +46,9 @@ export function createDesktopWindowBridge(kind: "main" | "detached", paneId?: st
46
46
  },
47
47
  subscribeState(listener: (snapshot: DesktopSharedStateSnapshot) => void) {
48
48
  return onBackendMessage("desktop.state", (message) => {
49
- if (kind === "detached" && paneId) {
50
- const nextKey = detachedSnapshotKey(message.snapshot, paneId);
51
- if (nextKey === lastDetachedSnapshotKey) return;
52
- lastDetachedSnapshotKey = nextKey;
53
- listener(prepareDetachedSnapshot(message.snapshot, paneId));
49
+ if (takeDetachedSnapshot) {
50
+ const snapshot = takeDetachedSnapshot(message.snapshot);
51
+ if (snapshot) listener(snapshot);
54
52
  return;
55
53
  }
56
54
  listener(message.snapshot);
@@ -39,3 +39,21 @@ export function detachedSnapshotKey(snapshot: DesktopSharedStateSnapshot, paneId
39
39
  paneState: Object.fromEntries(paneStateIds.map((id) => [id, snapshot.paneState[id] ?? {}])),
40
40
  });
41
41
  }
42
+
43
+ /**
44
+ * What a popped-out window takes of each state it is sent: nothing unless the
45
+ * part for its pane and the panes it follows changed, and then all of it,
46
+ * config included.
47
+ */
48
+ export function createDetachedSnapshotFilter(
49
+ paneId: string,
50
+ initialSnapshot: DesktopSharedStateSnapshot | null,
51
+ ): (snapshot: DesktopSharedStateSnapshot) => DesktopSharedStateSnapshot | null {
52
+ let lastKey = initialSnapshot ? detachedSnapshotKey(initialSnapshot, paneId) : null;
53
+ return (snapshot) => {
54
+ const nextKey = detachedSnapshotKey(snapshot, paneId);
55
+ if (nextKey === lastKey) return null;
56
+ lastKey = nextKey;
57
+ return prepareDetachedSnapshot(snapshot, paneId);
58
+ };
59
+ }
@@ -21,6 +21,7 @@ export async function resolveRegisteredSeries(
21
21
  const resolved = await resolveLoadedSeries(found.series.definition as LoadedSeriesDefinition, {
22
22
  viewport,
23
23
  ...(found.ticker ? { ticker: found.ticker } : {}),
24
+ ...(found.line ? { line: found.line } : {}),
24
25
  ...(signal ? { signal } : {}),
25
26
  });
26
27
  // The same checks a series from another process passes.
@@ -1,3 +1,4 @@
1
+ import { logFailure } from "../utils/log-failure";
1
2
  import { inferSeriesFrequency, parseSeriesCsv } from "./csv";
2
3
  import type { SeriesDefinition, SeriesFrequency } from "./types";
3
4
  import { SERIES_FREQUENCIES, isValidSeriesUnit } from "./validate";
@@ -78,7 +79,9 @@ export async function listSeriesFiles(dataDir: string): Promise<SeriesFolderFile
78
79
  let names: string[];
79
80
  try {
80
81
  names = (await readdir(folder)).filter((name) => name.toLowerCase().endsWith(".csv"));
81
- } catch {
82
+ } catch (error) {
83
+ // An absent optional file or directory is expected.
84
+ if ((error as { code?: unknown } | null)?.code !== "ENOENT") logFailure("series.files", "Listing series files failed")(error);
82
85
  return [];
83
86
  }
84
87
  const files = await Promise.all(names.map(async (name): Promise<SeriesFolderFile | null> => {
@@ -86,7 +89,9 @@ export async function listSeriesFiles(dataDir: string): Promise<SeriesFolderFile
86
89
  try {
87
90
  const { mtimeMs } = await stat(path);
88
91
  return { name, mtimeMs, readText: () => readFile(path, "utf-8") };
89
- } catch {
92
+ } catch (error) {
93
+ // An absent optional file or directory is expected.
94
+ if ((error as { code?: unknown } | null)?.code !== "ENOENT") logFailure("series.files", "Reading series file metadata failed")(error);
90
95
  return null;
91
96
  }
92
97
  }));
@@ -99,7 +104,8 @@ export async function readSeriesFolder(dataDir: string): Promise<SeriesDefinitio
99
104
  const definitions = await Promise.all(files.map(async (file) => {
100
105
  try {
101
106
  return seriesFromFile(file.name, await file.readText(), file.readText);
102
- } catch {
107
+ } catch (error) {
108
+ logFailure("series.files", "Reading series definitions failed")(error);
103
109
  return [];
104
110
  }
105
111
  }));
@@ -8,11 +8,15 @@ import type {
8
8
  RegisteredSeries,
9
9
  SeriesCatalogEntry,
10
10
  SeriesDefinition,
11
+ SeriesLine,
11
12
  } from "./types";
12
13
 
14
+ /** Between a series' name and one of its lines in a capability series id: `bands~upper/AAPL`. */
15
+ const LINE_SEPARATOR = "~";
16
+
13
17
  /** The expression a loaded series charts as: the namespace is its chart-series capability. */
14
- function loadedSeriesExpression(definition: Pick<SeriesDefinition, "id" | "input">): string {
15
- return `CAP:${definition.id}${definition.input ? `/${TICKER_PLACEHOLDER}` : ""}`;
18
+ function loadedSeriesExpression(definition: Pick<SeriesDefinition, "id" | "input">, lineId?: string): string {
19
+ return `CAP:${definition.id}${lineId ? `${LINE_SEPARATOR}${lineId}` : ""}${definition.input ? `/${TICKER_PLACEHOLDER}` : ""}`;
16
20
  }
17
21
 
18
22
  export function seriesExpression(definition: SeriesDefinition): string {
@@ -20,25 +24,31 @@ export function seriesExpression(definition: SeriesDefinition): string {
20
24
  }
21
25
 
22
26
  /**
23
- * The capability series id a loaded series charts under: its name, and for a
24
- * series with an input, the symbol after a slash (`days-to-cover/AAPL`).
27
+ * The capability series id a loaded series charts under: its name, one of its
28
+ * lines after a tilde, and for a series with an input, the symbol after a
29
+ * slash (`days-to-cover/AAPL`, `bands~upper/AAPL`).
25
30
  */
26
- export function capabilitySeriesId(definition: Pick<SeriesDefinition, "id">, ticker?: string): string {
27
- const name = splitSeriesId(definition.id)?.name ?? definition.id;
31
+ export function capabilitySeriesId(definition: Pick<SeriesDefinition, "id">, ticker?: string, lineId?: string): string {
32
+ const name = `${splitSeriesId(definition.id)?.name ?? definition.id}${lineId ? `${LINE_SEPARATOR}${lineId}` : ""}`;
28
33
  return ticker ? `${name}/${encodeURIComponent(ticker.trim().toUpperCase())}` : name;
29
34
  }
30
35
 
31
- /** The registered id and the typed symbol a capability series id names. */
32
- function parseCapabilitySeriesId(namespace: string, seriesId: string): { id: string; ticker?: string } {
36
+ /** The registered id, the line and the typed symbol a capability series id names. */
37
+ function parseCapabilitySeriesId(namespace: string, seriesId: string): { id: string; ticker?: string; line?: string } {
33
38
  const slash = seriesId.indexOf("/");
34
- if (slash < 0) return { id: `${namespace}:${seriesId}` };
39
+ const named = slash < 0 ? seriesId : seriesId.slice(0, slash);
40
+ const tilde = named.indexOf(LINE_SEPARATOR);
41
+ const name = tilde < 0 ? named : named.slice(0, tilde);
42
+ const line = tilde < 0 ? undefined : named.slice(tilde + 1);
43
+ const base = { id: `${namespace}:${name}`, ...(line ? { line } : {}) };
44
+ if (slash < 0) return base;
35
45
  let ticker: string;
36
46
  try {
37
47
  ticker = decodeURIComponent(seriesId.slice(slash + 1));
38
48
  } catch {
39
49
  ticker = seriesId.slice(slash + 1);
40
50
  }
41
- return { id: `${namespace}:${seriesId.slice(0, slash)}`, ticker };
51
+ return { ...base, ticker };
42
52
  }
43
53
 
44
54
  export function seriesCatalogEntry(series: RegisteredSeries): SeriesCatalogEntry {
@@ -54,6 +64,16 @@ export function seriesCatalogEntry(series: RegisteredSeries): SeriesCatalogEntry
54
64
  ...(definition.publisher ? { publisher: definition.publisher } : {}),
55
65
  ...(definition.url ? { url: definition.url } : {}),
56
66
  ...(definition.input ? { input: definition.input } : {}),
67
+ ...(definition.placement ? { placement: definition.placement } : {}),
68
+ ...(definition.lines?.length ? {
69
+ lines: definition.lines.map((line) => ({
70
+ id: line.id,
71
+ name: line.name,
72
+ // A line shading the area to another is drawn as a band: its line inside the shade.
73
+ ...(line.fill ? { style: "band" as const } : line.style ? { style: line.style } : {}),
74
+ expression: loadedSeriesExpression(definition, line.id),
75
+ })),
76
+ } : {}),
57
77
  expression: seriesExpression(definition),
58
78
  pluginId,
59
79
  };
@@ -131,14 +151,20 @@ export class SeriesRegistry {
131
151
  return this.namespaces.get(namespace)?.pluginId;
132
152
  }
133
153
 
134
- /** The loaded series a chart-series capability source names, with the typed symbol. */
135
- findLoaded(capabilityId: string, seriesId: string): { series: RegisteredSeries; ticker?: string } | null {
154
+ /**
155
+ * The loaded series a chart-series capability source names, with the typed
156
+ * symbol and the line. A series with lines named without one is its first line.
157
+ */
158
+ findLoaded(capabilityId: string, seriesId: string): { series: RegisteredSeries; ticker?: string; line?: SeriesLine } | null {
136
159
  if (!this.namespaces.has(capabilityId)) return null;
137
- const { id, ticker } = parseCapabilitySeriesId(capabilityId, seriesId);
160
+ const { id, ticker, line: lineId } = parseCapabilitySeriesId(capabilityId, seriesId);
138
161
  const series = this.entries.get(id);
139
162
  if (!series?.definition.load) return null;
140
163
  if (!!series.definition.input !== !!ticker) return null;
141
- return { series, ...(ticker ? { ticker } : {}) };
164
+ const lines = series.definition.lines;
165
+ const line = lines?.length ? lineId ? lines.find((entry) => entry.id === lineId) : lines[0] : undefined;
166
+ if (lineId && !line) return null;
167
+ return { series, ...(ticker ? { ticker } : {}), ...(line ? { line } : {}) };
142
168
  }
143
169
 
144
170
  /** The series that charts as this exact expression (`FRED:UNRATE`), if one without an input does. */
@@ -6,12 +6,16 @@ import type {
6
6
  SeriesPeriod,
7
7
  TimeSeriesPoint,
8
8
  } from "../time-series/types";
9
+ import { getPresetResolution, type ManualChartResolution } from "../time-series/resolution";
9
10
  import { SERIES_COLORS } from "../theme/series-colors";
10
11
  import type {
11
12
  LoadedSeriesDefinition,
12
13
  SeriesFrequency,
14
+ SeriesLine,
15
+ SeriesLinesLoadResult,
13
16
  SeriesLoadOutput,
14
17
  SeriesLoadRequest,
18
+ SeriesLoadResult,
15
19
  SeriesPoint,
16
20
  } from "./types";
17
21
 
@@ -25,8 +29,7 @@ function seriesPointDate(value: SeriesPoint["date"]): Date | null {
25
29
  return Number.isFinite(date.getTime()) ? date : null;
26
30
  }
27
31
 
28
- /** Oldest first, one point per instant (the last one given wins), at most the chart's point limit. */
29
- export function normalizeSeriesPoints(points: readonly SeriesPoint[]): TimeSeriesPoint[] {
32
+ function valuesByTime(points: readonly SeriesPoint[]): Map<number, number | null> {
30
33
  const byTime = new Map<number, number | null>();
31
34
  for (const point of points) {
32
35
  const date = point ? seriesPointDate(point.date) : null;
@@ -34,10 +37,24 @@ export function normalizeSeriesPoints(points: readonly SeriesPoint[]): TimeSerie
34
37
  const value = typeof point.value === "number" && Number.isFinite(point.value) ? point.value : null;
35
38
  byTime.set(date.getTime(), value);
36
39
  }
40
+ return byTime;
41
+ }
42
+
43
+ /**
44
+ * Oldest first, one point per instant (the last one given wins), at most the
45
+ * chart's point limit. With `edge`, another line's points, each point spans
46
+ * from its value to that line's value at the same instant: a band's range.
47
+ */
48
+ export function normalizeSeriesPoints(points: readonly SeriesPoint[], edge?: readonly SeriesPoint[]): TimeSeriesPoint[] {
49
+ const byTime = valuesByTime(points);
50
+ const edges = edge ? valuesByTime(edge) : null;
37
51
  const sorted = [...byTime].sort(([left], [right]) => left - right);
38
52
  return sorted.slice(-MAX_CHART_SERIES_POINTS).map(([time, value]) => {
39
53
  const date = new Date(time);
40
- return { date, observedAt: date, value };
54
+ const other = edges?.get(time);
55
+ return value !== null && other != null
56
+ ? { date, observedAt: date, value, low: Math.min(value, other), high: Math.max(value, other) }
57
+ : { date, observedAt: date, value };
41
58
  });
42
59
  }
43
60
 
@@ -50,11 +67,16 @@ function seriesUnitGroup(unit: string): string {
50
67
  return unit === "%" ? "percent" : `series:${unit.toLowerCase()}`;
51
68
  }
52
69
 
53
- /** One colour per id, so two loaded series on a chart are told apart. */
54
- function seriesColor(id: string): string {
70
+ /** One colour per id, so two loaded series on a chart are told apart; a series' lines take the colours after it. */
71
+ function seriesColor(id: string, lineIndex = 0): string {
55
72
  let hash = 0;
56
73
  for (const char of id) hash = (hash * 31 + char.charCodeAt(0)) >>> 0;
57
- return SERIES_COLORS[hash % SERIES_COLORS.length]!;
74
+ return SERIES_COLORS[(hash + lineIndex) % SERIES_COLORS.length]!;
75
+ }
76
+
77
+ /** The bar size a chart draws prices in: the one it was set to, else its range's default. */
78
+ function viewportResolution(viewport: ChartViewportSpec): ManualChartResolution {
79
+ return viewport.resolution === "auto" ? getPresetResolution(viewport.range) : viewport.resolution;
58
80
  }
59
81
 
60
82
  /** The dates a chart viewport covers, as a series load request takes them. */
@@ -68,32 +90,113 @@ export function seriesLoadWindow(viewport: ChartViewportSpec, now = new Date()):
68
90
  return { start: subtractTimeRange(now, viewport.range) };
69
91
  }
70
92
 
71
- function loadResult(output: SeriesLoadOutput): Exclude<SeriesLoadOutput, readonly SeriesPoint[]> {
72
- if (Array.isArray(output)) return { points: output };
73
- const result = output as Exclude<SeriesLoadOutput, readonly SeriesPoint[]>;
93
+ function loadResult(output: SeriesLoadOutput, line: SeriesLine | undefined): SeriesLoadResult & { edge?: readonly SeriesPoint[] } {
94
+ if (Array.isArray(output)) {
95
+ if (line) throw new Error("Series loader returned points where its lines were expected.");
96
+ return { points: output };
97
+ }
98
+ const result = output as Partial<SeriesLoadResult & SeriesLinesLoadResult> | null;
99
+ if (line) {
100
+ if (!result || !result.lines || typeof result.lines !== "object") throw new Error("Series loader returned no lines.");
101
+ const { lines, ...rest } = result;
102
+ const points = lines[line.id];
103
+ const edge = line.fill ? lines[line.fill] : undefined;
104
+ // A line the loaded data no longer has (the series changed under a saved chart) draws nothing.
105
+ return { ...rest, points: Array.isArray(points) ? points : [], ...(Array.isArray(edge) ? { edge } : {}) };
106
+ }
74
107
  if (!result || !Array.isArray(result.points)) throw new Error("Series loader returned no points.");
75
- return result;
108
+ return result as SeriesLoadResult;
109
+ }
110
+
111
+ /** How long one load is shared by the lines of a series that ask for it together. */
112
+ const SHARED_LOAD_MS = 2_000;
113
+
114
+ interface SharedLoad {
115
+ output: Promise<SeriesLoadOutput>;
116
+ controller: AbortController;
117
+ /** Requests still waiting that can be called off. */
118
+ waiting: number;
119
+ /** A request that cannot be called off shares the load, so it runs to the end. */
120
+ kept: boolean;
121
+ }
122
+
123
+ const sharedLoads = new WeakMap<LoadedSeriesDefinition, Map<string, SharedLoad>>();
124
+
125
+ /**
126
+ * Loads a series once for all of its lines: a chart asks for each line on its
127
+ * own, at the same time and for the same dates, and the loader runs once. The
128
+ * loader's signal aborts when every request sharing the load has.
129
+ */
130
+ function loadSeries(definition: LoadedSeriesDefinition, request: SeriesLoadRequest): Promise<SeriesLoadOutput> {
131
+ if (!definition.lines?.length) return Promise.resolve(definition.load(request));
132
+ const { signal, ...shared } = request;
133
+ if (signal?.aborted) return Promise.reject(signal.reason);
134
+ // A window measured back from now moves by milliseconds between the lines' requests.
135
+ const minute = (date: Date | undefined) => date ? Math.floor(date.getTime() / 60_000) : null;
136
+ const key = JSON.stringify([shared.ticker ?? null, minute(shared.start), minute(shared.end), shared.resolution ?? null]);
137
+ let byKey = sharedLoads.get(definition);
138
+ if (!byKey) sharedLoads.set(definition, byKey = new Map());
139
+ let load = byKey.get(key);
140
+ if (!load) {
141
+ const controller = new AbortController();
142
+ const output = Promise.resolve().then(() => {
143
+ controller.signal.throwIfAborted();
144
+ return definition.load({ ...shared, signal: controller.signal });
145
+ });
146
+ const entry: SharedLoad = { output, controller, waiting: 0, kept: false };
147
+ load = entry;
148
+ byKey.set(key, entry);
149
+ const forget = () => {
150
+ const timer = setTimeout(() => { if (byKey.get(key) === entry) byKey.delete(key); }, SHARED_LOAD_MS);
151
+ (timer as { unref?: () => void }).unref?.();
152
+ };
153
+ output.then(forget, forget);
154
+ }
155
+ const entry = load;
156
+ if (!signal) {
157
+ entry.kept = true;
158
+ return entry.output;
159
+ }
160
+ entry.waiting += 1;
161
+ return new Promise<SeriesLoadOutput>((resolve, reject) => {
162
+ const onAbort = () => {
163
+ entry.waiting -= 1;
164
+ if (entry.waiting === 0 && !entry.kept) {
165
+ if (byKey.get(key) === entry) byKey.delete(key);
166
+ entry.controller.abort(signal.reason);
167
+ }
168
+ reject(signal.reason);
169
+ };
170
+ signal.addEventListener("abort", onAbort, { once: true });
171
+ entry.output.then(resolve, reject).finally(() => signal.removeEventListener("abort", onAbort));
172
+ });
76
173
  }
77
174
 
78
175
  export async function resolveLoadedSeries(
79
176
  definition: LoadedSeriesDefinition,
80
- request: { viewport: ChartViewportSpec; ticker?: string; signal?: AbortSignal },
177
+ request: { viewport: ChartViewportSpec; ticker?: string; line?: SeriesLine; signal?: AbortSignal },
81
178
  now = new Date(),
82
179
  ): Promise<ResolvedSeries> {
83
180
  const ticker = request.ticker?.trim().toUpperCase() || undefined;
84
- const { points, warning, attribution } = loadResult(await definition.load({
181
+ const line = definition.lines?.length ? request.line ?? definition.lines[0] : undefined;
182
+ const { points, edge, warning, attribution } = loadResult(await loadSeries(definition, {
85
183
  ...seriesLoadWindow(request.viewport, now),
86
184
  ...(ticker ? { ticker } : {}),
185
+ resolution: viewportResolution(request.viewport),
87
186
  ...(request.signal ? { signal: request.signal } : {}),
88
- }));
89
- const id = ticker ? `${definition.id}/${ticker}` : definition.id;
90
- const style = definition.style ?? "line";
187
+ }), line);
188
+ const seriesId = ticker ? `${definition.id}/${ticker}` : definition.id;
189
+ const id = line ? `${definition.id}~${line.id}${ticker ? `/${ticker}` : ""}` : seriesId;
190
+ const name = line ? `${definition.name} · ${line.name}` : definition.name;
191
+ const style = line?.style ?? definition.style ?? "line";
91
192
  return {
92
193
  id,
93
- label: ticker ? `${ticker} · ${definition.name}` : definition.name,
94
- color: seriesColor(id),
194
+ label: ticker ? `${ticker} · ${name}` : name,
195
+ color: line?.color ?? definition.color ?? seriesColor(seriesId, line ? definition.lines!.indexOf(line) : 0),
95
196
  unit: definition.unit,
96
197
  unitGroup: seriesUnitGroup(definition.unit),
198
+ // Drawn in the ticker's price: the chart puts it on that price's axis.
199
+ ...(definition.placement === "price" && ticker ? { priceOf: ticker } : {}),
97
200
  nativeFrequency: nativeFrequency(definition.frequency),
98
201
  dataShape: "scalar",
99
202
  style,
@@ -103,6 +206,6 @@ export async function resolveLoadedSeries(
103
206
  interpolation: style === "step" ? "step-after" : "none",
104
207
  ...(warning ? { warning } : {}),
105
208
  ...(attribution ? { attribution: { ...attribution } } : {}),
106
- points: normalizeSeriesPoints(points),
209
+ points: normalizeSeriesPoints(points, edge),
107
210
  };
108
211
  }
@@ -1,3 +1,4 @@
1
+ import { logFailure } from "../utils/log-failure";
1
2
  import { chartSeriesResolveRequestSchema } from "../capabilities/chart-series";
2
3
  import type { CapabilityInvoker } from "../capabilities/types";
3
4
  import type { GloomPlugin } from "../types/plugin";
@@ -23,8 +24,8 @@ export function standaloneSeriesInvoker(
23
24
  for (const definition of definitions) {
24
25
  try {
25
26
  built.register(pluginId, definition);
26
- } catch {
27
- // The app logs a definition it refuses; a report charts the rest.
27
+ } catch (error) {
28
+ logFailure("series.standalone", "Registering a standalone series failed")(error);
28
29
  }
29
30
  }
30
31
  };