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
@@ -1,3 +1,4 @@
1
+ import { logFailure } from "../utils/log-failure";
1
2
  import type { ResolvedPane } from "../layout/pane-manager";
2
3
  import type { PluginRegistry } from "../plugins/registry";
3
4
  import { panePersonalData } from "../shares/personal-pane";
@@ -122,7 +123,8 @@ export function buildAskGloomPaneAttachment({
122
123
  function showsPersonalData(source: Parameters<typeof panePersonalData>[0]): boolean {
123
124
  try {
124
125
  return panePersonalData(source) !== null;
125
- } catch {
126
+ } catch (error) {
127
+ logFailure("ask-gloom.pane-context", "Checking pane personal data failed")(error);
126
128
  return true;
127
129
  }
128
130
  }
@@ -6,6 +6,7 @@
6
6
  * restart everywhere) so the directory still lists them offline. The desktop's
7
7
  * Bun half never fetches; it picks up the list the view saved.
8
8
  */
9
+ import { logFailure } from "../../utils/log-failure";
9
10
  import type { PluginPersistence } from "../../types/plugin";
10
11
  import { listSignedInBrokers, type SignedInBroker } from "./client";
11
12
 
@@ -102,7 +103,7 @@ export function createSignedInBrokerCatalog(
102
103
  persistence?.setState(STATE_KEY, { brokers: next, fetchedAt }, { schemaVersion: SCHEMA_VERSION });
103
104
  })
104
105
  // Offline or Cloud down: the last list stays, and the next open retries.
105
- .catch(() => {})
106
+ .catch(logFailure("signed-in-brokers", "Broker catalog refresh failed"))
106
107
  .finally(() => {
107
108
  inFlight = null;
108
109
  });
@@ -52,11 +52,16 @@ export function fetchSignedInBrokerConnection(brokerId: string): Promise<SignedI
52
52
  return apiClient.brokerRequest(brokerId, "");
53
53
  }
54
54
 
55
+ /**
56
+ * Opens a connect ticket. This app shows no code: it asks for a ticket that
57
+ * only the account's own Gloom session in the browser can continue. A Cloud
58
+ * that predates that still answers with a code, which the dialog then shows.
59
+ */
55
60
  export function startSignedInBrokerConnect(
56
61
  brokerId: string,
57
62
  write: boolean,
58
- ): Promise<{ connectUrl: string; code: string; expiresAt: string }> {
59
- return apiClient.brokerRequest(brokerId, "/connect", { method: "POST", body: { write } });
63
+ ): Promise<{ connectUrl: string; code?: string | null; expiresAt: string }> {
64
+ return apiClient.brokerRequest(brokerId, "/connect", { method: "POST", body: { write, code: false } });
60
65
  }
61
66
 
62
67
  export function disconnectSignedInBroker(brokerId: string): Promise<{ disconnected: boolean }> {
@@ -9,14 +9,18 @@
9
9
  import { useEffect, useRef, useState } from "react";
10
10
  import { apiClient, type AuthUser } from "../../api-client";
11
11
  import { SignInCodePanel } from "../../components/sign-in-code-panel";
12
+ import { Button } from "../../components/ui/button";
13
+ import { DialogActions } from "../../components/ui/dialog-actions";
14
+ import { FORM_DIALOG_WIDTH, DialogFrame } from "../../components/ui/frame";
12
15
  import { t, tf } from "../../i18n";
13
16
  import { useAppLanguage } from "../../i18n/react";
14
17
  import { useViewport } from "../../react/input";
15
18
  import { colors } from "../../theme/colors";
19
+ import { useThemeColors } from "../../theme/theme-context";
16
20
  import { Box, Text, TextAttributes, useUiCapabilities } from "../../ui";
17
- import { DialogHints } from "../../components/ui/dialog-actions";
18
21
  import { useDialog, useDialogKeyboard, type DialogApi, type PromptContext } from "../../ui/dialog";
19
22
  import { isPlainKey } from "../../utils/keyboard";
23
+ import { wrapTextLines } from "../../utils/text-wrap";
20
24
  import { DeviceSignInDialog } from "../../account/device-signin-dialog";
21
25
  import type { SignedInBroker } from "./client";
22
26
  import {
@@ -43,7 +47,13 @@ export function brokerSignInStatus(snapshot: BrokerSignInSnapshot, broker: Signe
43
47
  }
44
48
  }
45
49
 
46
- /** Under the code: the broker's own note, or what connecting a single-connection broker does to other apps. */
50
+ /** What every connect surface says under its title: the browser does the rest. */
51
+ export const BROKER_CONNECT_SUBTITLE = "Continue in your browser.";
52
+
53
+ /** The key every connect surface takes for a fresh link, as the desktop shows it. */
54
+ export const BROKER_CONNECT_NEW_LINK = { key: "r", label: "new link" };
55
+
56
+ /** Under the panel: the broker's own note, or what connecting a single-connection broker does to other apps. */
47
57
  export function brokerSignInNote(broker: SignedInBroker): string | null {
48
58
  return broker.capabilities.signupNote
49
59
  ?? (broker.capabilities.singleConnection
@@ -108,6 +118,18 @@ export async function promptGloomSignIn(dialog: DialogApi): Promise<boolean> {
108
118
  });
109
119
  }
110
120
 
121
+ /**
122
+ * Rows the dialog's own lines take around the connect view: the terminal's
123
+ * margins, border and padding (7), title, subtitle and key line with the
124
+ * blank rows between them, and the desktop's frame, title block and Cancel.
125
+ */
126
+ const TERMINAL_DIALOG_ROWS = 12;
127
+ const DESKTOP_DIALOG_ROWS = 10;
128
+ /** Columns the terminal's full dialog takes from the window: margins, border and padding. */
129
+ const TERMINAL_DIALOG_INSET = 8;
130
+ /** Cells the desktop dialog's padding takes from its width. */
131
+ const DESKTOP_DIALOG_INSET = 4;
132
+
111
133
  function BrokerSignInDialog({
112
134
  resolve,
113
135
  dismiss,
@@ -117,7 +139,7 @@ function BrokerSignInDialog({
117
139
  }: PromptContext<BrokerSignInOutcome> & { broker: SignedInBroker; write: boolean; renew: boolean }) {
118
140
  useAppLanguage();
119
141
  const { nativePaneChrome } = useUiCapabilities();
120
- const { height: termHeight } = useViewport();
142
+ const { width: termWidth, height: termHeight } = useViewport();
121
143
  // Close after a beat so "Connected" is visible; enter skips the wait. Signed
122
144
  // out closes at once, for the host to sign in to Gloom.
123
145
  const { snapshot, restart, finish } = useBrokerSignInAttempt(broker, write, resolve, { renew });
@@ -133,44 +155,116 @@ function BrokerSignInDialog({
133
155
  }
134
156
  });
135
157
 
136
- const note = brokerSignInNote(broker);
137
- // Title, spacing, note, and footer take about eleven rows around the panel.
138
- const panelHeight = Math.max(4, termHeight - (note ? 12 : 10));
158
+ const title = tf("Connect {broker}", { broker: broker.name });
159
+ const subtitle = t(BROKER_CONNECT_SUBTITLE);
160
+
161
+ // The desktop frames it as New Portfolio frames its connect step: as wide,
162
+ // with Cancel at the foot and the key for a new link beside it.
163
+ if (nativePaneChrome) {
164
+ return (
165
+ <Box width={FORM_DIALOG_WIDTH} maxWidth="calc(100vw - 72px)" flexDirection="column">
166
+ <DialogFrame title={title} subtitle={subtitle} onClose={dismiss} desktopFooter={null}>
167
+ <BrokerConnectView
168
+ broker={broker}
169
+ snapshot={snapshot}
170
+ height={Math.max(4, termHeight - DESKTOP_DIALOG_ROWS)}
171
+ width={FORM_DIALOG_WIDTH - DESKTOP_DIALOG_INSET}
172
+ />
173
+ <Box style={{ marginTop: "var(--gloom-space-md)" }}>
174
+ <DialogActions alignEnd hints={{ keys: [BROKER_CONNECT_NEW_LINK] }}>
175
+ <Button label="Cancel" variant="secondary" desktopShortcut="Esc" onPress={dismiss} />
176
+ </DialogActions>
177
+ </Box>
178
+ </DialogFrame>
179
+ </Box>
180
+ );
181
+ }
139
182
 
140
183
  return (
141
184
  <Box flexDirection="column" alignItems="center">
142
185
  <Box height={1}>
143
- <Text fg={colors.textBright} attributes={TextAttributes.BOLD}>
144
- {tf("Connect {broker}", { broker: broker.name })}
145
- </Text>
186
+ <Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{title}</Text>
146
187
  </Box>
147
188
  <Box height={1}>
148
- <Text fg={colors.textDim}>{t("Open the link and enter the code there.")}</Text>
189
+ <Text fg={colors.textDim}>{subtitle}</Text>
149
190
  </Box>
150
191
  <Box height={1} />
192
+ <BrokerConnectView
193
+ broker={broker}
194
+ snapshot={snapshot}
195
+ height={Math.max(4, termHeight - TERMINAL_DIALOG_ROWS)}
196
+ width={Math.max(10, termWidth - TERMINAL_DIALOG_INSET)}
197
+ />
198
+ <Box height={1} />
199
+ <Box height={1}>
200
+ <Text fg={colors.textMuted}>{t("r for a new link · esc to cancel")}</Text>
201
+ </Box>
202
+ </Box>
203
+ );
204
+ }
205
+
206
+ /**
207
+ * On the desktop a connect's note, under the panel, is set to a reading
208
+ * measure rather than the width of the dialog or pane. The terminal wraps it
209
+ * to the width it has, since every row it takes is one the QR code may need.
210
+ */
211
+ const NOTE_MEASURE = 56;
212
+
213
+ /**
214
+ * One attempt at connecting a signed-in broker as the user sees it: the link,
215
+ * QR code and status, with the broker's note wrapped under them. The connect
216
+ * dialog, Add Broker's step in the Brokers pane and New Portfolio's show it.
217
+ */
218
+ export function BrokerConnectView({
219
+ broker,
220
+ snapshot,
221
+ height,
222
+ width,
223
+ browserKey = true,
224
+ }: {
225
+ broker: SignedInBroker;
226
+ snapshot: BrokerSignInSnapshot;
227
+ /** Terminal rows for the panel and the note. */
228
+ height: number;
229
+ /** Terminal columns the note wraps to. */
230
+ width: number;
231
+ /** Whether `b` opens the link now. */
232
+ browserKey?: boolean;
233
+ }) {
234
+ useAppLanguage();
235
+ const colors = useThemeColors();
236
+ const desktop = useUiCapabilities().nativePaneChrome === true;
237
+ const note = brokerSignInNote(broker);
238
+ const noteLines = note ? wrapTextLines(note, Math.max(10, desktop ? Math.min(width, NOTE_MEASURE) : width)) : [];
239
+ // The note goes under the panel, which gets the rows left.
240
+ const noteRows = note ? 1 + noteLines.length : 0;
241
+
242
+ return (
243
+ <Box flexDirection="column" alignItems="center">
151
244
  <SignInCodePanel
152
245
  url={snapshot.connectUrl}
153
246
  code={snapshot.code}
154
247
  status={brokerSignInStatus(snapshot, broker)}
155
- height={panelHeight}
248
+ height={Math.max(4, height - noteRows)}
156
249
  shortcutScope="broker-signin:browser"
250
+ browserKey={browserKey}
157
251
  />
158
- {note && (
159
- <>
160
- <Box height={1} />
161
- <Box height={1}>
162
- <Text fg={colors.textMuted}>{note}</Text>
252
+ {note && (desktop
253
+ ? (
254
+ <Box marginTop={1} maxWidth={NOTE_MEASURE}>
255
+ <Text fg={colors.textMuted} wrapText style={{ textAlign: "center" }}>{note}</Text>
163
256
  </Box>
164
- </>
165
- )}
166
- <Box height={1} />
167
- {nativePaneChrome ? (
168
- <DialogHints keys={[{ key: "r", label: "new code" }, { key: "Esc", label: "cancel" }]} />
169
- ) : (
170
- <Box height={1}>
171
- <Text fg={colors.textMuted}>{t("r for a new code · esc to cancel")}</Text>
172
- </Box>
173
- )}
257
+ )
258
+ : (
259
+ <>
260
+ <Box height={1} />
261
+ {noteLines.map((line, index) => (
262
+ <Box key={index} height={1}>
263
+ <Text fg={colors.textMuted}>{line}</Text>
264
+ </Box>
265
+ ))}
266
+ </>
267
+ ))}
174
268
  </Box>
175
269
  );
176
270
  }
@@ -1,10 +1,11 @@
1
1
  /**
2
- * Connecting a signed-in broker: Gloom Cloud hands out a link and a short code,
3
- * the user opens the link, types the code, and signs in to the broker there.
4
- * The code is what ties the browser to this app, so a link someone else started
5
- * cannot attach their brokerage to this account. The connection itself lives in
6
- * the Cloud account, shared by every device and by the user's agents.
2
+ * Connecting a signed-in broker: Gloom Cloud hands out a link, the user opens
3
+ * it and signs in to the broker there. The link goes on only in a browser
4
+ * signed in to Gloom as this account, so a link someone else started cannot
5
+ * attach their brokerage to it. The connection itself lives in the Cloud
6
+ * account, shared by every device and by the user's agents.
7
7
  */
8
+ import { logFailure } from "../../utils/log-failure";
8
9
  import { ApiRequestError } from "../../api-client/errors";
9
10
  import {
10
11
  fetchSignedInBrokerConnection,
@@ -19,6 +20,7 @@ type BrokerSignInPhase = "starting" | "waiting" | "connected" | "signed-out" | "
19
20
  export interface BrokerSignInSnapshot {
20
21
  phase: BrokerSignInPhase;
21
22
  connectUrl: string | null;
23
+ /** Only from a Cloud that still asks the browser for a code; none otherwise. */
22
24
  code: string | null;
23
25
  /** A transient poll failure while waiting, or why starting failed. */
24
26
  error: string | null;
@@ -37,7 +39,7 @@ export interface BrokerSignInOptions {
37
39
  }
38
40
 
39
41
  export interface BrokerSignInIo {
40
- start(brokerId: string, write: boolean): Promise<{ connectUrl: string; code: string; expiresAt: string }>;
42
+ start(brokerId: string, write: boolean): Promise<{ connectUrl: string; code?: string | null; expiresAt: string }>;
41
43
  fetchConnection(brokerId: string): Promise<BrokerConnectionRead>;
42
44
  now(): number;
43
45
  delay(ms: number): Promise<void>;
@@ -46,7 +48,7 @@ export interface BrokerSignInIo {
46
48
  const POLL_INTERVAL_MS = 2_000;
47
49
  const RETRY_MAX_MS = 30_000;
48
50
  /** Used when the expiry is missing or unreadable, so polling can never run forever. */
49
- const DEFAULT_CODE_TTL_MS = 15 * 60_000;
51
+ const DEFAULT_LINK_TTL_MS = 15 * 60_000;
50
52
 
51
53
  const defaultIo: BrokerSignInIo = {
52
54
  start: (brokerId, write) => startSignedInBrokerConnect(brokerId, write),
@@ -61,13 +63,13 @@ function describeStartError(error: unknown, broker: SignedInBroker): string {
61
63
  }
62
64
 
63
65
  /**
64
- * When a code stops working. An expiry this device's clock already sees as
65
- * past (the clock runs fast) counts as missing, or every code would expire
66
+ * When a link stops working. An expiry this device's clock already sees as
67
+ * past (the clock runs fast) counts as missing, or every link would expire
66
68
  * the moment it arrived.
67
69
  */
68
- function codeDeadline(expiresAt: string, now: number): number {
70
+ function linkDeadline(expiresAt: string, now: number): number {
69
71
  const parsed = Date.parse(expiresAt);
70
- return Number.isFinite(parsed) && parsed > now ? parsed : now + DEFAULT_CODE_TTL_MS;
72
+ return Number.isFinite(parsed) && parsed > now ? parsed : now + DEFAULT_LINK_TTL_MS;
71
73
  }
72
74
 
73
75
  function stamp(value: string | null | undefined): number | null {
@@ -96,7 +98,7 @@ export class BrokerSignInController {
96
98
  private generation = 0;
97
99
  /**
98
100
  * Renewing only: the account's connection as first seen. It outlives a
99
- * restarted code, so a sign-in finished while "r" asked for a new one still
101
+ * restarted link, so a sign-in finished while "r" asked for a new one still
100
102
  * counts.
101
103
  */
102
104
  private before: BrokerConnectionRead | null = null;
@@ -125,7 +127,7 @@ export class BrokerSignInController {
125
127
  };
126
128
  }
127
129
 
128
- /** Starts, or restarts with a fresh code. Safe to call repeatedly. */
130
+ /** Starts, or restarts with a fresh link. Safe to call repeatedly. */
129
131
  start(): void {
130
132
  const generation = ++this.generation;
131
133
  void this.run(generation);
@@ -143,16 +145,16 @@ export class BrokerSignInController {
143
145
  private async run(generation: number): Promise<void> {
144
146
  const stale = () => this.generation !== generation;
145
147
  let retryMs = POLL_INTERVAL_MS;
146
- // Renewing: read before a code exists, so nothing the user does can come
148
+ // Renewing: read before a link exists, so nothing the user does can come
147
149
  // first. A failed read leaves the first poll to stand in for it: no
148
150
  // sign-in finishes within a poll interval.
149
151
  if (this.renew && !this.before) {
150
- const read = await this.io.fetchConnection(this.broker.id).catch(() => null);
152
+ const read = await this.io.fetchConnection(this.broker.id).catch(logFailure("broker-signin", "Broker connection refresh failed", null));
151
153
  if (stale()) return;
152
154
  this.before ??= read;
153
155
  }
154
- // Each iteration is one code: start, poll until connected, and loop again
155
- // when the code expired so the link refreshes instead of dead-ending.
156
+ // Each iteration is one link: start, poll until connected, and loop again
157
+ // when the link expired so it refreshes instead of dead-ending.
156
158
  while (!stale()) {
157
159
  this.update({ phase: "starting", connectUrl: null, code: null, error: null });
158
160
  let started: Awaited<ReturnType<BrokerSignInIo["start"]>>;
@@ -174,9 +176,9 @@ export class BrokerSignInController {
174
176
  }
175
177
  if (stale()) return;
176
178
  retryMs = POLL_INTERVAL_MS;
177
- this.update({ phase: "waiting", connectUrl: started.connectUrl, code: started.code, error: null });
178
- const deadline = codeDeadline(started.expiresAt, this.io.now());
179
- // At least one poll per code, so the next code is never asked for at once.
179
+ this.update({ phase: "waiting", connectUrl: started.connectUrl, code: started.code ?? null, error: null });
180
+ const deadline = linkDeadline(started.expiresAt, this.io.now());
181
+ // At least one poll per link, so the next link is never asked for at once.
180
182
  do {
181
183
  await this.io.delay(POLL_INTERVAL_MS);
182
184
  if (stale()) return;
@@ -1,3 +1,4 @@
1
+ import { logFailure } from "../utils/log-failure";
1
2
  import type { AppResourceStorePort, AppTickerRepositoryPort } from "../core/app-service-ports";
2
3
  import { persistBrokerAccounts } from "./account-cache";
3
4
  import type { BrokerAdapter, BrokerPosition } from "../types/broker";
@@ -218,7 +219,7 @@ export async function syncBrokerInstance({
218
219
  throw new Error(`Broker "${instance.brokerType}" is not available.`);
219
220
  }
220
221
 
221
- const valid = await broker.validate(instance).catch(() => false);
222
+ const valid = await broker.validate(instance).catch(logFailure("broker-sync", "Broker validation failed", false));
222
223
  throwIfBrokerImportCancelled(signal);
223
224
  if (!valid) {
224
225
  throw new Error(`${broker.name} setup is incomplete.`);
@@ -203,6 +203,7 @@ function resolvedOutput(value: unknown): ResolvedSeries {
203
203
  onlyKeys(input, "resolve output", [
204
204
  "id", "label", "color", "unit", "unitGroup", "nativeFrequency", "timestampMode", "dataShape", "style", "transform",
205
205
  "axis", "panelId", "interpolation", "timeBasis", "latestChangePercent", "points", "warning", "hidden", "attribution",
206
+ "priceOf",
206
207
  ]);
207
208
  if (!Array.isArray(input.points)) fail("resolve output points", "expected an array.");
208
209
  if (input.points.length > MAX_CHART_SERIES_POINTS) {
@@ -252,6 +253,7 @@ function resolvedOutput(value: unknown): ResolvedSeries {
252
253
  ...(input.hidden !== undefined
253
254
  ? typeof input.hidden === "boolean" ? { hidden: input.hidden } : fail("resolve output hidden", "expected a boolean.")
254
255
  : {}),
256
+ ...(input.priceOf !== undefined ? { priceOf: boundedString(input.priceOf, "resolve output price ticker", 64) } : {}),
255
257
  };
256
258
  }
257
259
 
@@ -150,9 +150,7 @@ export class CapabilityRegistry {
150
150
  for (const subscriptionId of [...this.subscriptions.keys()]) {
151
151
  try {
152
152
  this.unsubscribe(subscriptionId);
153
- } catch {
154
- // Continue releasing the remaining subscriptions.
155
- }
153
+ } catch { /* Intentional ignore: Teardown must attempt every subscription disposer even if one has already closed. */ }
156
154
  }
157
155
  this.capabilities.clear();
158
156
  }
@@ -0,0 +1,147 @@
1
+ import type { BankFiguresPayload } from "../api-client/bank-figures";
2
+ import type { TickerFinancials } from "../types/financials";
3
+ import type { CliReportEntry } from "./report-tables";
4
+ import { isForeignIssuerForm, type StatementReportSources } from "./statement-filings";
5
+ import { formatCompact, formatPercent } from "../utils/format";
6
+ import { logFailure } from "../utils/log-failure";
7
+ import { nonUsSecListingVenue } from "../utils/sec";
8
+
9
+ /**
10
+ * The Bank section of `fundamentals` for a bank holding company: its deposits
11
+ * and their change on the quarter from its own filings, and its lead bank's
12
+ * uninsured share and CRE concentration from the FDIC call report. Every line
13
+ * names the quarter and the filing or report date it is from. Methodology is
14
+ * in docs/usage.md (Statement filings and bank figures).
15
+ */
16
+ export interface BankSection {
17
+ /** Label and text, as the report prints them. */
18
+ lines: Array<[string, string]>;
19
+ /** The same figures for --csv and --ndjson. */
20
+ entries: CliReportEntry[];
21
+ /** Muted lines under the section: what is missing and why, and where NIM is. */
22
+ notes: string[];
23
+ }
24
+
25
+ const money = (value: number) => `${formatCompact(value)} USD`;
26
+ const share = (value: number) => `${(value * 100).toFixed(1)}%`;
27
+ const day = (stamp: string | null) => stamp?.slice(0, 10) ?? null;
28
+
29
+ /** The screener on one company with its net interest margin, a Pro figure. */
30
+ export function nimCommand(symbol: string): string {
31
+ const definition = JSON.stringify({
32
+ version: 1, currency: null, criteria: [{ field: "symbol", op: "in", value: [symbol] }], sort: { field: "symbol", direction: "asc" },
33
+ });
34
+ return `gloomberb fn EQS --metric kpiNimPercent --definition '${definition}'`;
35
+ }
36
+
37
+ /** Gloom Cloud's note for a company with no US GAAP deposits tag, which reads as if it filed 10-Qs; a foreign issuer files neither. */
38
+ const NO_DEPOSITS_TAG_NOTE = "The company's 10-Q and 10-K tag no total deposits.";
39
+
40
+ export function bankSection(bank: BankFiguresPayload, symbol: string, { latestStatementDate, annualReportForm }: {
41
+ /** The newest statement period, YYYY-MM-DD. */
42
+ latestStatementDate: string | null;
43
+ /** The company's annual report form: a 20-F or 40-F names a foreign group, whose FDIC-insured bank is only its US arm. */
44
+ annualReportForm: string | null;
45
+ }): BankSection | null {
46
+ if (!bank.bank) return null;
47
+ const foreignGroup = isForeignIssuerForm(annualReportForm);
48
+ const lines: Array<[string, string]> = [];
49
+ const entries: CliReportEntry[] = [];
50
+ const notes = foreignGroup ? bank.notes.filter((note) => note !== NO_DEPOSITS_TAG_NOTE) : [...bank.notes];
51
+ const { deposits, leadBank, callReport } = bank;
52
+
53
+ if (leadBank) {
54
+ const largest = leadBank.otherBanks > 0
55
+ ? `largest of ${leadBank.otherBanks + 1} insured banks by assets${leadBank.reportDate ? ` at ${leadBank.reportDate}` : ""}`
56
+ : null;
57
+ const role = foreignGroup
58
+ ? ["US bank subsidiary only", largest].filter(Boolean).join(", ")
59
+ : !leadBank.holdingCompany ? "the company is the bank" : largest ?? "its insured bank";
60
+ const asOf = day(leadBank.source.datasetAsOf);
61
+ lines.push(["Lead Bank", `${leadBank.name} (FDIC cert ${leadBank.cert}; ${role}${asOf ? `; FDIC data of ${asOf}` : ""})`]);
62
+ entries.push({ label: "Lead Bank", value: leadBank.name }, { label: "Lead Bank FDIC Cert", value: leadBank.cert });
63
+ if (foreignGroup) {
64
+ entries.push({ label: "Lead Bank Scope", value: "US bank subsidiary of a foreign group" });
65
+ notes.push(`${symbol} reports as a foreign issuer (${annualReportForm}): the FDIC figures are those of its US bank subsidiary, ${leadBank.name}, `
66
+ + `not the group's. Group deposits are in its ${annualReportForm} and not read here.`);
67
+ }
68
+ }
69
+
70
+ if (deposits) {
71
+ const { latest, previous } = deposits;
72
+ const parts = latest.basis === "sum" ? "; interest-bearing plus noninterest-bearing" : "";
73
+ lines.push(["Deposits", `${money(latest.value)} at ${latest.date} (${latest.form} filed ${latest.filed}${parts})`]);
74
+ entries.push(
75
+ { label: "Deposits", value: latest.value, unit: "USD" },
76
+ { label: "Deposits Date", value: latest.date },
77
+ { label: "Deposits Filing", value: `${latest.form} filed ${latest.filed}` },
78
+ );
79
+ if (previous && previous.value > 0) {
80
+ const depositsChange = latest.value / previous.value - 1;
81
+ lines.push(["Deposits QoQ", `${formatPercent(depositsChange)} from ${money(previous.value)} at ${previous.date} (${previous.form} filed ${previous.filed})`]);
82
+ entries.push({ label: "Deposits QoQ", value: depositsChange * 100, unit: "%" });
83
+ }
84
+ if (latestStatementDate && latest.date < latestStatementDate) {
85
+ notes.push(`Deposits are as of ${latest.date}: the company's later filings do not tag total deposits.`);
86
+ }
87
+ }
88
+
89
+ if (callReport) {
90
+ const asOf = day(callReport.source.datasetAsOf);
91
+ const report = `call report ${callReport.reportDate}${asOf ? `, FDIC data of ${asOf}` : ""}`;
92
+ // The FDIC's estimate covers deposits in domestic offices: its uninsured part over insured plus uninsured.
93
+ const { uninsuredShare, uninsuredDeposits, insuredDeposits } = callReport;
94
+ const estimated = uninsuredDeposits != null && insuredDeposits != null ? uninsuredDeposits + insuredDeposits : null;
95
+ lines.push(["Uninsured Deposits", uninsuredShare != null && uninsuredDeposits != null && estimated != null
96
+ ? `${share(uninsuredShare)} of domestic deposits (${formatCompact(uninsuredDeposits)} uninsured of ${money(estimated)} estimated insured plus uninsured; ${report})`
97
+ : `not reported: banks under $1B in assets do not file the estimate (${report})`]);
98
+ if (uninsuredShare != null) entries.push({ label: "Uninsured Share of Domestic Deposits", value: uninsuredShare * 100, unit: "%" });
99
+ if (uninsuredDeposits != null) entries.push({ label: "Uninsured Deposits", value: uninsuredDeposits, unit: "USD" });
100
+ // Deposits in foreign offices are outside the estimate, and FDIC insurance does not cover them.
101
+ const foreignOffices = callReport.totalDeposits != null && callReport.domesticDeposits != null
102
+ ? callReport.totalDeposits - callReport.domesticDeposits
103
+ : 0;
104
+ if (uninsuredShare != null && callReport.totalDeposits && foreignOffices / callReport.totalDeposits >= 0.005) {
105
+ entries.push({ label: "Foreign Office Deposits", value: foreignOffices, unit: "USD" });
106
+ notes.push(`The uninsured share covers domestic offices; another ${money(foreignOffices)} of deposits sits in foreign offices, which FDIC insurance does not cover.`);
107
+ }
108
+ if (callReport.cre && callReport.capital && callReport.creToCapital != null) {
109
+ lines.push(["CRE / Capital", `${share(callReport.creToCapital)}: CRE ${money(callReport.cre.total)} over ${callReport.capital.basis} ${money(callReport.capital.value)} (${report})`]);
110
+ entries.push(
111
+ { label: "CRE / Capital", value: callReport.creToCapital * 100, unit: "%" },
112
+ { label: "CRE Loans", value: callReport.cre.total, unit: "USD" },
113
+ );
114
+ }
115
+ entries.push({ label: "Call Report Date", value: callReport.reportDate });
116
+ if (asOf) entries.push({ label: "FDIC Data As Of", value: asOf });
117
+ }
118
+
119
+ notes.push(`NIM is a Pro figure in EQS: ${nimCommand(symbol)}`);
120
+ return { lines, entries, notes };
121
+ }
122
+
123
+ /** The line for a bank whose figures could not be read. */
124
+ const BANK_FIGURES_UNAVAILABLE = "Bank figures (deposits, uninsured share, CRE) are unavailable right now.";
125
+
126
+ /**
127
+ * A US-listed financial company's bank figures, or null: a company outside
128
+ * financial services is never asked about (Gloom Cloud answers a non-bank with
129
+ * `bank: false` anyway). A bank whose figures could not be read gets a one-line
130
+ * `note` in their place.
131
+ */
132
+ export async function loadBankFigures(
133
+ sources: StatementReportSources | undefined,
134
+ listing: { symbol: string; exchange?: string },
135
+ financials: Pick<TickerFinancials, "profile">,
136
+ ): Promise<{ bank: BankFiguresPayload | null; note: string | null }> {
137
+ const profile = `${financials.profile?.sector ?? ""} ${financials.profile?.industry ?? ""}`;
138
+ if (!sources || !/financ|bank/i.test(profile) || nonUsSecListingVenue(listing.symbol, listing.exchange)) return { bank: null, note: null };
139
+ try {
140
+ const bank = await sources.bankFigures(listing.symbol);
141
+ return { bank: bank.bank ? bank : null, note: null };
142
+ } catch (error) {
143
+ logFailure("cli.bank-section", "Reading bank figures failed")(error);
144
+ // Only a bank's report misses them; another financial company's says nothing.
145
+ return { bank: null, note: /bank/i.test(financials.profile?.industry ?? "") ? BANK_FIGURES_UNAVAILABLE : null };
146
+ }
147
+ }
@@ -1,18 +1,21 @@
1
1
  import type { DataProvider } from "../types/data-provider";
2
+ import { fixedUsdRate } from "../utils/currency-units";
3
+ import { logFailure } from "../utils/log-failure";
2
4
 
3
5
  export function createBaseConverter(dataProvider: Pick<DataProvider, "getExchangeRate">, baseCurrency: string) {
4
6
  const rateCache = new Map<string, number>([["USD", 1]]);
5
7
 
6
8
  const getRate = async (currency: string): Promise<number> => {
7
9
  const normalizedCurrency = currency.toUpperCase();
8
- const cached = rateCache.get(normalizedCurrency);
10
+ const cached = rateCache.get(normalizedCurrency) ?? fixedUsdRate(normalizedCurrency);
9
11
  if (cached != null) return cached;
10
12
  try {
11
13
  const rate = await dataProvider.getExchangeRate(normalizedCurrency);
12
14
  const validRate = Number.isFinite(rate) && rate > 0 ? rate : Number.NaN;
13
15
  rateCache.set(normalizedCurrency, validRate);
14
16
  return validRate;
15
- } catch {
17
+ } catch (error) {
18
+ logFailure("cli-base-converter", "Could not load base conversion exchange rate")(error);
16
19
  return Number.NaN;
17
20
  }
18
21
  };
@@ -0,0 +1,70 @@
1
+ import { apiClient } from "../api-client";
2
+ import type { CryptoMarketAsset } from "../api-client/crypto-markets";
3
+ import { logFailure } from "../utils/log-failure";
4
+
5
+ /** A coin on the crypto board as a hint names it: its one symbol and its name. */
6
+ export interface BoardCoin {
7
+ symbol: string;
8
+ name: string;
9
+ }
10
+
11
+ type BoardEntry = Pick<CryptoMarketAsset, "symbol" | "code" | "name" | "marketCap">;
12
+
13
+ /** How long a hint waits for the board; a slow board leaves the plain search hint. */
14
+ const BOARD_TIMEOUT_MS = 4_000;
15
+ const BARE_NOT_A_TICKER = /Not a ticker: ([A-Za-z0-9]+)\./;
16
+
17
+ let board: readonly BoardEntry[] | null = null;
18
+ let loading: Promise<void> | null = null;
19
+
20
+ /**
21
+ * Reads the crypto board once, when some text says a bare word is not a
22
+ * ticker, so `boardCoinFor` can name the coin it may be. Never throws; a
23
+ * board that fails or is slow leaves the hints as they were.
24
+ */
25
+ export async function primeBoardCoins(
26
+ texts: ReadonlyArray<string | null | undefined>,
27
+ load: () => Promise<{ assets: readonly BoardEntry[] }> = () => apiClient.getCloudCryptoMarkets(),
28
+ ): Promise<void> {
29
+ if (board || !texts.some((text) => !!text && BARE_NOT_A_TICKER.test(text))) return;
30
+ let timer: ReturnType<typeof setTimeout> | undefined;
31
+ loading ??= Promise.race([
32
+ load().then((data) => { board = boardEntries(data.assets); }),
33
+ new Promise<void>((resolve) => { timer = setTimeout(resolve, BOARD_TIMEOUT_MS); }),
34
+ ]).catch(logFailure("cli.board-coins", "Read the crypto board for a coin hint failed")).finally(() => clearTimeout(timer));
35
+ await loading;
36
+ }
37
+
38
+ /** The board's coins a hint can name: a symbol, a code and a name each. */
39
+ function boardEntries(assets: unknown): BoardEntry[] {
40
+ return (Array.isArray(assets) ? assets : []).filter((asset): asset is BoardEntry => !!asset
41
+ && typeof asset.symbol === "string" && /^[A-Z0-9]+-USD$/.test(asset.symbol)
42
+ && typeof asset.code === "string" && !!asset.code && typeof asset.name === "string");
43
+ }
44
+
45
+ const compact = (text: string) => text.toUpperCase().replace(/[^\p{L}\p{N}]/gu, "");
46
+
47
+ /**
48
+ * The board coin a bare word that is no ticker most likely means: its code
49
+ * (USDT, USDS, USDE) or its name (TETHER for Tether USDt, BITCOIN), the
50
+ * largest by market cap when several answer. Null before the board is read,
51
+ * and for anything else.
52
+ */
53
+ export function boardCoinFor(typed: string): BoardCoin | null {
54
+ const id = typed.trim().replace(/^\$/, "").toUpperCase();
55
+ if (!board || !/^[A-Z0-9]+$/.test(id)) return null;
56
+ const byCode = board.filter((coin) => coin.code.toUpperCase() === id);
57
+ const byName = byCode.length ? [] : board.filter((coin) => {
58
+ const name = compact(coin.name);
59
+ const first = compact(coin.name.split(/\s+/)[0] ?? "");
60
+ return name === id || (id.length >= 5 && first === id);
61
+ });
62
+ const best = [...byCode.length ? byCode : byName].sort((left, right) => (right.marketCap ?? 0) - (left.marketCap ?? 0))[0];
63
+ return best ? { symbol: best.symbol, name: best.name } : null;
64
+ }
65
+
66
+ /** For tests: forget the board read. */
67
+ export function resetBoardCoins(entries: readonly BoardEntry[] | null = null): void {
68
+ board = entries;
69
+ loading = null;
70
+ }