gloomberb 0.19.2 → 0.19.3

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 (472) hide show
  1. package/README.md +7 -1
  2. package/package.json +1 -1
  3. package/patches/electrobun@1.18.1.patch +108 -1
  4. package/src/account/upgrade-dialog.tsx +4 -23
  5. package/src/api-client/coalesce.ts +118 -0
  6. package/src/api-client/credit-boards.ts +13 -0
  7. package/src/api-client/crypto-etf-flows.ts +6 -0
  8. package/src/api-client/crypto-markets.ts +40 -0
  9. package/src/api-client/data.ts +201 -14
  10. package/src/api-client/equity-screener.ts +10 -0
  11. package/src/api-client/errors.ts +20 -0
  12. package/src/api-client/gpu.ts +29 -0
  13. package/src/api-client/index.ts +5 -2
  14. package/src/api-client/plan-access.ts +10 -1
  15. package/src/api-client/plan-rules.ts +35 -0
  16. package/src/api-client/research-activity.ts +1 -32
  17. package/src/api-client/types.ts +21 -1
  18. package/src/app/attention-notice.tsx +94 -0
  19. package/src/app.tsx +23 -1
  20. package/src/architecture/core-builtin-allowlist.json +2 -1
  21. package/src/capabilities/factories.ts +1 -0
  22. package/src/cli/ascii-output.ts +42 -0
  23. package/src/cli/base-converter.ts +10 -2
  24. package/src/cli/command-options.ts +16 -1
  25. package/src/cli/commands/crypto-hints.ts +23 -4
  26. package/src/cli/commands/market.ts +92 -23
  27. package/src/cli/commands/news-search.ts +24 -5
  28. package/src/cli/commands/overview.ts +9 -4
  29. package/src/cli/commands/search.ts +71 -12
  30. package/src/cli/commands/system.ts +22 -30
  31. package/src/cli/commands/ticker.ts +49 -9
  32. package/src/cli/commands/version.ts +33 -0
  33. package/src/cli/context.ts +5 -9
  34. package/src/cli/data-dir.ts +7 -0
  35. package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
  36. package/src/cli/desktop-pane-shot-browser.ts +320 -0
  37. package/src/cli/desktop-pane-shot-routes.ts +26 -1
  38. package/src/cli/desktop-pane-shot.ts +196 -256
  39. package/src/cli/early-command.ts +42 -0
  40. package/src/cli/earnings-calendar.ts +9 -0
  41. package/src/cli/entry.ts +19 -13
  42. package/src/cli/errors.ts +12 -2
  43. package/src/cli/help.ts +1 -1
  44. package/src/cli/history-export.ts +22 -2
  45. package/src/cli/history-rows.ts +154 -4
  46. package/src/cli/index.ts +18 -1
  47. package/src/cli/known-gaps.ts +40 -5
  48. package/src/cli/listing-arg.ts +46 -9
  49. package/src/cli/options.ts +1 -1
  50. package/src/cli/output-file.ts +60 -13
  51. package/src/cli/pane-functions/catalog.ts +14 -3
  52. package/src/cli/pane-functions/data.ts +1 -1
  53. package/src/cli/pane-functions/dom.ts +45 -0
  54. package/src/cli/pane-functions/freshness.ts +46 -9
  55. package/src/cli/pane-functions/function-help.ts +17 -9
  56. package/src/cli/pane-functions/headless.ts +22 -14
  57. package/src/cli/pane-functions/index.ts +6 -2
  58. package/src/cli/pane-functions/options.ts +6 -2
  59. package/src/cli/pane-functions/resolver.ts +27 -5
  60. package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
  61. package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
  62. package/src/cli/pane-functions/screenshot-http.ts +70 -0
  63. package/src/cli/pane-functions/screenshot-render.ts +19 -5
  64. package/src/cli/quote-answer.ts +20 -0
  65. package/src/cli/registry.ts +41 -13
  66. package/src/cli/result.ts +12 -6
  67. package/src/cli/ticker-resolution.ts +2 -1
  68. package/src/cli/types.ts +2 -0
  69. package/src/components/chart/composite/format.ts +3 -0
  70. package/src/components/chart-table/header.tsx +1 -1
  71. package/src/components/chart-table/layout.ts +28 -11
  72. package/src/components/command-bar/action-runtime.ts +4 -0
  73. package/src/components/command-bar/commands/direct/index.ts +18 -33
  74. package/src/components/command-bar/commands/direct/runtime.ts +5 -0
  75. package/src/components/command-bar/commands/registry.ts +10 -1
  76. package/src/components/command-bar/routes/root/command-items.ts +9 -4
  77. package/src/components/command-bar/routes/root/runtime.ts +24 -1
  78. package/src/components/command-bar/surface/index.tsx +9 -3
  79. package/src/components/company-logo.tsx +2 -1
  80. package/src/components/layout/header.tsx +8 -3
  81. package/src/components/layout/market-summary.ts +25 -7
  82. package/src/components/layout/pane/footer/index.tsx +7 -2
  83. package/src/components/layout/pane/footer/model.ts +40 -5
  84. package/src/components/market-board/index.tsx +3 -2
  85. package/src/components/onboarding/desks-step.tsx +12 -4
  86. package/src/components/onboarding/first-run-workspace.ts +81 -4
  87. package/src/components/onboarding/onboarding-frame.tsx +2 -1
  88. package/src/components/onboarding/onboarding-keyboard.ts +8 -5
  89. package/src/components/onboarding/onboarding-wizard.tsx +54 -23
  90. package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
  91. package/src/components/onboarding/run-setup-again.ts +24 -0
  92. package/src/components/onboarding/use-onboarding-progress.ts +33 -15
  93. package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
  94. package/src/components/onboarding/wizard-model.ts +57 -3
  95. package/src/components/onboarding/wizard-positions.ts +1 -1
  96. package/src/components/ui/delayed-mark.tsx +56 -0
  97. package/src/components/ui/external-link.tsx +3 -6
  98. package/src/components/ui/icon.tsx +8 -1
  99. package/src/components/ui/logo-mark.tsx +8 -5
  100. package/src/components/ui/stat-grid.tsx +263 -19
  101. package/src/data/config/store/node.ts +11 -5
  102. package/src/data/config/store/normalize.ts +16 -3
  103. package/src/data/profile-permissions.ts +47 -0
  104. package/src/data/resource-store.ts +34 -31
  105. package/src/data/sqlite/access.ts +19 -8
  106. package/src/data/sqlite/checkpointer.ts +95 -8
  107. package/src/data/sqlite/database.ts +40 -7
  108. package/src/docs/snapshot.json +191 -78
  109. package/src/i18n/es.ts +18 -2
  110. package/src/i18n/ja.ts +18 -2
  111. package/src/i18n/ko.ts +18 -2
  112. package/src/i18n/zh-cn.ts +18 -2
  113. package/src/i18n/zh-tw.ts +18 -2
  114. package/src/layout/desk-keys.ts +4 -1
  115. package/src/layout/desks.ts +38 -5
  116. package/src/layout-marketplace/gallery-desktop.tsx +45 -16
  117. package/src/market-data/batched-reads.ts +16 -0
  118. package/src/market-data/coordinator/auxiliary.ts +4 -2
  119. package/src/market-data/coordinator/index.ts +11 -2
  120. package/src/market-data/futures-board.ts +1 -1
  121. package/src/market-data/futures-contract.ts +3 -1
  122. package/src/market-data/market/index-level.ts +6 -4
  123. package/src/market-data/published-apac-sessions.ts +6 -0
  124. package/src/news/aggregator.ts +7 -4
  125. package/src/news/news-model.ts +11 -0
  126. package/src/news/story-id.ts +22 -0
  127. package/src/news/ticker-symbols.ts +17 -1
  128. package/src/notifications/app-notifier.ts +35 -14
  129. package/src/plugins/builtin/account-management/form-components.tsx +1 -1
  130. package/src/plugins/builtin/account-management/index.tsx +2 -3
  131. package/src/plugins/builtin/account-management/model.ts +1 -1
  132. package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
  133. package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
  134. package/src/plugins/builtin/alerts/events.ts +10 -2
  135. package/src/plugins/builtin/alerts/index.tsx +5 -5
  136. package/src/plugins/builtin/alerts/research-builder.ts +5 -1
  137. package/src/plugins/builtin/alerts/research-rules.ts +18 -0
  138. package/src/plugins/builtin/alerts/targets.ts +4 -1
  139. package/src/plugins/builtin/attention/headless.ts +1 -1
  140. package/src/plugins/builtin/attention/index.tsx +2 -4
  141. package/src/plugins/builtin/attention/listing.ts +1 -1
  142. package/src/plugins/builtin/attention/series.ts +1 -1
  143. package/src/plugins/builtin/awards/index.tsx +1 -2
  144. package/src/plugins/builtin/backtest/client.ts +1 -0
  145. package/src/plugins/builtin/backtest/index.tsx +1 -2
  146. package/src/plugins/builtin/backtest/rules.ts +3 -1
  147. package/src/plugins/builtin/backtest/window.ts +8 -7
  148. package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
  149. package/src/plugins/builtin/broker-manager/index.tsx +1 -1
  150. package/src/plugins/builtin/buildout/cells.tsx +5 -0
  151. package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
  152. package/src/plugins/builtin/buildout/company/id.ts +2 -0
  153. package/src/plugins/builtin/buildout/company/model.ts +247 -0
  154. package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
  155. package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
  156. package/src/plugins/builtin/buildout/facilities.ts +309 -0
  157. package/src/plugins/builtin/buildout/format.ts +9 -0
  158. package/src/plugins/builtin/buildout/index.tsx +51 -5
  159. package/src/plugins/builtin/buildout/model/index.ts +62 -10
  160. package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
  161. package/src/plugins/builtin/buildout/model/types.ts +11 -2
  162. package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
  163. package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
  164. package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
  165. package/src/plugins/builtin/buildout/table-model.ts +26 -2
  166. package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
  167. package/src/plugins/builtin/cds/headless.ts +10 -2
  168. package/src/plugins/builtin/cds/model.ts +7 -3
  169. package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
  170. package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
  171. package/src/plugins/builtin/changelog/index.tsx +3 -2
  172. package/src/plugins/builtin/chart-composer/headless-schema.ts +7 -2
  173. package/src/plugins/builtin/chart-composer/headless.ts +88 -21
  174. package/src/plugins/builtin/chart-composer/index.tsx +2 -3
  175. package/src/plugins/builtin/chart-composer/pane.tsx +26 -2
  176. package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
  177. package/src/plugins/builtin/chart-composer/studies.ts +1 -1
  178. package/src/plugins/builtin/chat/controller/index.ts +1 -1
  179. package/src/plugins/builtin/chat/controller/messages.ts +15 -4
  180. package/src/plugins/builtin/chat/ghost-user.ts +8 -0
  181. package/src/plugins/builtin/chat/index.tsx +5 -5
  182. package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
  183. package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
  184. package/src/plugins/builtin/cloud/index.tsx +1 -2
  185. package/src/plugins/builtin/cloud/plugin.tsx +6 -4
  186. package/src/plugins/builtin/cloud/team/module.tsx +1 -1
  187. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
  188. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
  189. package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
  190. package/src/plugins/builtin/comment-letters/index.tsx +1 -2
  191. package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
  192. package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
  193. package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
  194. package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
  195. package/src/plugins/builtin/congress-trades/headless.ts +2 -0
  196. package/src/plugins/builtin/congress-trades/model.ts +92 -64
  197. package/src/plugins/builtin/congress-trades/table.tsx +14 -2
  198. package/src/plugins/builtin/connections/index.ts +2 -3
  199. package/src/plugins/builtin/cot/index.tsx +1 -2
  200. package/src/plugins/builtin/cpi/index.tsx +1 -2
  201. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
  202. package/src/plugins/builtin/credit-boards/client.ts +7 -2
  203. package/src/plugins/builtin/credit-boards/headless.ts +25 -12
  204. package/src/plugins/builtin/credit-boards/model.ts +29 -0
  205. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
  206. package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
  207. package/src/plugins/builtin/crypto-board/headless.ts +4 -3
  208. package/src/plugins/builtin/crypto-board/index.tsx +4 -2
  209. package/src/plugins/builtin/crypto-board/series.ts +2 -0
  210. package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
  211. package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
  212. package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
  213. package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
  214. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
  215. package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
  216. package/src/plugins/builtin/debt-maturities/model.ts +17 -1
  217. package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
  218. package/src/plugins/builtin/debug/index.tsx +1 -1
  219. package/src/plugins/builtin/dividend-yield/client.ts +32 -2
  220. package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
  221. package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
  222. package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
  223. package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
  224. package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
  225. package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
  226. package/src/plugins/builtin/dividend-yield/view.ts +21 -0
  227. package/src/plugins/builtin/docs/index.tsx +2 -3
  228. package/src/plugins/builtin/doe/headless.ts +16 -3
  229. package/src/plugins/builtin/doe/index.tsx +5 -4
  230. package/src/plugins/builtin/earnings/board.tsx +2 -1
  231. package/src/plugins/builtin/earnings/history.tsx +2 -1
  232. package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
  233. package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
  234. package/src/plugins/builtin/econ/calendar-model.ts +29 -6
  235. package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
  236. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  237. package/src/plugins/builtin/equity-screener/headless.ts +1 -0
  238. package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
  239. package/src/plugins/builtin/equity-screener/index.tsx +1 -2
  240. package/src/plugins/builtin/equity-screener/model.ts +28 -4
  241. package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
  242. package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
  243. package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
  244. package/src/plugins/builtin/exposure/holdings.ts +35 -13
  245. package/src/plugins/builtin/exposure/index.tsx +1 -2
  246. package/src/plugins/builtin/fear-greed/cli.ts +44 -0
  247. package/src/plugins/builtin/fear-greed/index.tsx +5 -53
  248. package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
  249. package/src/plugins/builtin/fixed-income/index.tsx +1 -2
  250. package/src/plugins/builtin/futures/board-reads.ts +25 -0
  251. package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
  252. package/src/plugins/builtin/futures/index.tsx +15 -0
  253. package/src/plugins/builtin/futures/model.ts +14 -0
  254. package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
  255. package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
  256. package/src/plugins/builtin/futures-curve/headless.ts +6 -1
  257. package/src/plugins/builtin/futures-curve/index.tsx +1 -2
  258. package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
  259. package/src/plugins/builtin/gpu/board.tsx +51 -12
  260. package/src/plugins/builtin/gpu/client.ts +15 -2
  261. package/src/plugins/builtin/gpu/equities.tsx +5 -2
  262. package/src/plugins/builtin/gpu/headless.ts +14 -2
  263. package/src/plugins/builtin/gpu/index.tsx +1 -2
  264. package/src/plugins/builtin/gpu/model.ts +20 -2
  265. package/src/plugins/builtin/gpu/pane.tsx +1 -1
  266. package/src/plugins/builtin/help/index.tsx +1 -1
  267. package/src/plugins/builtin/holders/index-holders.ts +24 -12
  268. package/src/plugins/builtin/holders/index.tsx +2 -3
  269. package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
  270. package/src/plugins/builtin/iv-history/index.tsx +1 -2
  271. package/src/plugins/builtin/jodi/index.tsx +1 -2
  272. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
  273. package/src/plugins/builtin/layout-manager/index.tsx +9 -8
  274. package/src/plugins/builtin/macro-day/pane.tsx +4 -2
  275. package/src/plugins/builtin/market-halts/index.tsx +1 -2
  276. package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
  277. package/src/plugins/builtin/market-movers/index.tsx +7 -1
  278. package/src/plugins/builtin/market-movers/session-body.tsx +4 -1
  279. package/src/plugins/builtin/market-valuation/index.tsx +1 -2
  280. package/src/plugins/builtin/members/index.tsx +1 -2
  281. package/src/plugins/builtin/money-markets/index.tsx +1 -2
  282. package/src/plugins/builtin/money-markets/pane.tsx +2 -1
  283. package/src/plugins/builtin/news/headless.ts +7 -0
  284. package/src/plugins/builtin/news/wire/index.tsx +1 -1
  285. package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
  286. package/src/plugins/builtin/notes/index.tsx +3 -6
  287. package/src/plugins/builtin/openfda/index.tsx +1 -2
  288. package/src/plugins/builtin/options/view.tsx +6 -5
  289. package/src/plugins/builtin/options-calculator/index.tsx +1 -2
  290. package/src/plugins/builtin/options-positioning/index.tsx +1 -2
  291. package/src/plugins/builtin/options-scenario/index.tsx +2 -4
  292. package/src/plugins/builtin/perps/index.tsx +1 -2
  293. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
  294. package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
  295. package/src/plugins/builtin/plugin-module.ts +16 -2
  296. package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
  297. package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
  298. package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
  299. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
  300. package/src/plugins/builtin/power/index.tsx +1 -2
  301. package/src/plugins/builtin/rate-path/index.tsx +1 -2
  302. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  303. package/src/plugins/builtin/realized-vol/index.tsx +1 -2
  304. package/src/plugins/builtin/realized-vol/model.ts +16 -2
  305. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  306. package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
  307. package/src/plugins/builtin/research/analyst-headless.ts +33 -6
  308. package/src/plugins/builtin/research/analyst-model.ts +37 -2
  309. package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
  310. package/src/plugins/builtin/research/index.tsx +5 -8
  311. package/src/plugins/builtin/research-search/index.tsx +1 -2
  312. package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
  313. package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
  314. package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
  315. package/src/plugins/builtin/roadmap/index.tsx +2 -3
  316. package/src/plugins/builtin/roadmap/pane.tsx +49 -2
  317. package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
  318. package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
  319. package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
  320. package/src/plugins/builtin/sec/filing-content.ts +38 -6
  321. package/src/plugins/builtin/sec/headless.ts +5 -1
  322. package/src/plugins/builtin/sec/index.tsx +4 -2
  323. package/src/plugins/builtin/sec/model.ts +2 -0
  324. package/src/plugins/builtin/sectors/index.tsx +11 -5
  325. package/src/plugins/builtin/series-inventory.ts +2 -0
  326. package/src/plugins/builtin/shared/cloud-resource.ts +60 -0
  327. package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
  328. package/src/plugins/builtin/shared/forming-bars.ts +29 -0
  329. package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
  330. package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
  331. package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
  332. package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
  333. package/src/plugins/builtin/short-volume/pane.tsx +2 -1
  334. package/src/plugins/builtin/social-mentions/index.tsx +1 -2
  335. package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
  336. package/src/plugins/builtin/supply-chain/client.ts +22 -1
  337. package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
  338. package/src/plugins/builtin/supply-chain/index.tsx +1 -2
  339. package/src/plugins/builtin/thirteenf/api.ts +116 -13
  340. package/src/plugins/builtin/thirteenf/data.ts +132 -30
  341. package/src/plugins/builtin/thirteenf/index.tsx +2 -4
  342. package/src/plugins/builtin/thirteenf/model.ts +2 -0
  343. package/src/plugins/builtin/thirteenf/pane.tsx +20 -11
  344. package/src/plugins/builtin/thirteenf/types.ts +12 -0
  345. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +17 -4
  346. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
  347. package/src/plugins/builtin/ticker-detail/headless.ts +11 -3
  348. package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
  349. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
  350. package/src/plugins/builtin/time-sales/index.tsx +1 -2
  351. package/src/plugins/builtin/time-sales/pane.tsx +3 -1
  352. package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
  353. package/src/plugins/builtin/trace-bonds/model.ts +44 -10
  354. package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
  355. package/src/plugins/builtin/treasury-auctions/pane.tsx +2 -2
  356. package/src/plugins/builtin/vol-surface/client.ts +18 -3
  357. package/src/plugins/builtin/vol-surface/index.tsx +1 -2
  358. package/src/plugins/builtin/volatility/index.tsx +1 -2
  359. package/src/plugins/builtin/world-indices/footer.ts +6 -1
  360. package/src/plugins/builtin/world-venue-map/client.ts +10 -1
  361. package/src/plugins/builtin/world-venue-map/index.ts +6 -2
  362. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
  363. package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
  364. package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
  365. package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
  366. package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
  367. package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
  368. package/src/plugins/builtin/yield-curve/curves.ts +16 -2
  369. package/src/plugins/builtin/yield-curve/headless.ts +3 -3
  370. package/src/plugins/bundle.ts +52 -2
  371. package/src/plugins/catalog-browser.ts +2 -2
  372. package/src/plugins/catalog-cli.ts +20 -0
  373. package/src/plugins/host-link.ts +13 -1
  374. package/src/plugins/installer.ts +15 -4
  375. package/src/plugins/loader.ts +43 -11
  376. package/src/plugins/peer-imports.ts +201 -0
  377. package/src/plugins/peer-retry.ts +26 -29
  378. package/src/plugins/plugin-process.ts +128 -0
  379. package/src/plugins/redirection-guard.ts +45 -0
  380. package/src/remote/endpoint.ts +16 -3
  381. package/src/renderers/browser/main.tsx +3 -2
  382. package/src/renderers/cloudflare/http-proxy.ts +7 -123
  383. package/src/renderers/dom/desktop/icons.tsx +2 -0
  384. package/src/renderers/dom/host/image-surface.tsx +46 -21
  385. package/src/renderers/dom/styles.css +7 -0
  386. package/src/renderers/dom/toast-host.tsx +2 -0
  387. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  388. package/src/renderers/electrobun/bun/desktop/initialization.ts +3 -2
  389. package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
  390. package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
  391. package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
  392. package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
  393. package/src/renderers/electrobun/bun/window/create.ts +4 -2
  394. package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
  395. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
  396. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
  397. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
  398. package/src/renderers/electrobun/view/main.tsx +7 -2
  399. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
  400. package/src/renderers/opentui/browser-open.ts +10 -4
  401. package/src/renderers/opentui/image/surface.tsx +1 -1
  402. package/src/renderers/opentui/start.tsx +12 -6
  403. package/src/renderers/opentui/toast-host.tsx +2 -1
  404. package/src/series/registry.ts +18 -0
  405. package/src/series/standalone.ts +10 -1
  406. package/src/series/types.ts +7 -0
  407. package/src/series/validate.ts +11 -2
  408. package/src/sources/gloomberb-cloud/index.ts +28 -5
  409. package/src/sources/gloomberb-cloud/news.ts +24 -3
  410. package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
  411. package/src/sources/provider-errors.ts +26 -3
  412. package/src/sources/provider-router/history.ts +5 -0
  413. package/src/sources/provider-router/index.ts +13 -0
  414. package/src/sync/core-contributors.ts +32 -0
  415. package/src/telemetry/attention-counts.ts +64 -6
  416. package/src/telemetry/attention-setting.ts +33 -0
  417. package/src/telemetry/crash-reports-node.ts +3 -1
  418. package/src/telemetry/stall-clocks.ts +93 -0
  419. package/src/telemetry/stall-reports-node.ts +12 -7
  420. package/src/telemetry/stall-reports.ts +34 -5
  421. package/src/test-support/preload.ts +9 -0
  422. package/src/tickers/company-name.ts +19 -0
  423. package/src/tickers/listing-fate.ts +14 -3
  424. package/src/tickers/saved-instrument.ts +17 -0
  425. package/src/tickers/search/home-listing.ts +13 -2
  426. package/src/tickers/search/index.ts +4 -1
  427. package/src/tickers/search/types.ts +6 -1
  428. package/src/time-series/bar-aggregation.ts +7 -1
  429. package/src/time-series/base-series.ts +3 -3
  430. package/src/time-series/chart-bounds.ts +1 -1
  431. package/src/time-series/chart-data.ts +5 -2
  432. package/src/time-series/chart-seed.ts +1 -1
  433. package/src/time-series/coin-session-close.ts +211 -0
  434. package/src/time-series/live-bars.ts +13 -2
  435. package/src/time-series/live-quotes.ts +1 -1
  436. package/src/time-series/official-close.ts +159 -0
  437. package/src/time-series/price-comparison.ts +55 -6
  438. package/src/time-series/resolve.ts +12 -3
  439. package/src/time-series/series-presentation.ts +1 -1
  440. package/src/time-series/studies/formula.ts +205 -0
  441. package/src/time-series/studies/indicators.ts +299 -0
  442. package/src/time-series/studies/leverage.ts +75 -0
  443. package/src/time-series/studies/pair.ts +138 -0
  444. package/src/time-series/studies/resolve.ts +333 -0
  445. package/src/time-series/studies/shared.ts +141 -0
  446. package/src/time-series/studies/units.ts +243 -0
  447. package/src/time-series/studies/vwap.ts +274 -0
  448. package/src/types/config.ts +18 -2
  449. package/src/types/data-provider.ts +23 -0
  450. package/src/types/financials.ts +22 -0
  451. package/src/types/headless.ts +42 -3
  452. package/src/types/plugin.ts +32 -3
  453. package/src/ui/host.tsx +11 -0
  454. package/src/ui/toast.tsx +26 -1
  455. package/src/utils/ascii-text.ts +140 -0
  456. package/src/utils/cli-output.ts +91 -18
  457. package/src/utils/concurrency.ts +24 -0
  458. package/src/utils/exchanges.ts +6 -0
  459. package/src/utils/external-url.ts +20 -7
  460. package/src/utils/futures-generic.ts +4 -4
  461. package/src/utils/http-proxy-upstream.ts +124 -0
  462. package/src/utils/main-thread-stall.ts +75 -3
  463. package/src/utils/perf-marks.ts +36 -2
  464. package/src/utils/private-files.ts +141 -0
  465. package/src/utils/public-address.ts +99 -0
  466. package/src/account/upgrade-personalization.ts +0 -57
  467. package/src/account/upgrade-tickers.ts +0 -124
  468. package/src/api-client/upgrade-experiment.ts +0 -91
  469. package/src/plugins/builtin/cloud/browser.tsx +0 -3
  470. package/src/renderers/electrobun/launcher/preload.ts +0 -13
  471. package/src/renderers/electrobun/launcher/windows-emulation.ts +0 -50
  472. package/src/time-series/studies.ts +0 -1628
@@ -4,14 +4,9 @@ import { holdsActiveTicker } from "../../../shares/pane-image";
4
4
  import { TICKER_RESEARCH_PANE_ID } from "../../../types/config";
5
5
  import { normalizeTickerInput } from "../../../tickers/search";
6
6
  import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
7
- import { FinancialAnalysisPane } from "./financials/pane";
8
- import { HistoricalPricesPane } from "./data-panes/historical-prices";
9
- import { ReturnsPane } from "./data-panes/returns-pane";
10
- import { TickerResearchPane } from "./pane";
11
7
  import { futuresContractCache } from "./overview/futures-contract";
12
8
  import { offeringFilingsCache } from "./financials/offerings";
13
9
  import { TICKER_RESEARCH_BUILTIN_TABS } from "./research-tabs";
14
- import { QuoteMonitorPane } from "./quote-monitor";
15
10
  import { financialStatementsHeadless, historicalPricesHeadless, quoteComparisonHeadless, returnsHeadless } from "./headless";
16
11
  import {
17
12
  buildQuoteMonitorSettingsDef,
@@ -47,7 +42,7 @@ export const tickerDetailModule: PluginModule = {
47
42
  id: TICKER_RESEARCH_PANE_ID,
48
43
  name: "Research",
49
44
  icon: "D",
50
- component: TickerResearchPane,
45
+ get component(): typeof import("./pane").TickerResearchPane { const { TickerResearchPane } = require("./pane"); return TickerResearchPane; },
51
46
  defaultPosition: "right",
52
47
  tickerFollower: true,
53
48
  // DES and ADR open this pane; ADR's window is named by its params (the tab it asked for), so settings never say whose a window is.
@@ -70,7 +65,7 @@ export const tickerDetailModule: PluginModule = {
70
65
  id: "financial-analysis",
71
66
  name: "Financials",
72
67
  icon: "F",
73
- component: FinancialAnalysisPane,
68
+ get component(): typeof import("./financials/pane").FinancialAnalysisPane { const { FinancialAnalysisPane } = require("./financials/pane"); return FinancialAnalysisPane; },
74
69
  headless: financialStatementsHeadless,
75
70
  defaultPosition: "right",
76
71
  tickerFollower: true,
@@ -82,7 +77,7 @@ export const tickerDetailModule: PluginModule = {
82
77
  id: "quote-monitor",
83
78
  name: "Quote Monitor",
84
79
  icon: "Q",
85
- component: QuoteMonitorPane,
80
+ get component(): typeof import("./quote-monitor").QuoteMonitorPane { const { QuoteMonitorPane } = require("./quote-monitor"); return QuoteMonitorPane; },
86
81
  headless: quoteComparisonHeadless,
87
82
  defaultPosition: "right",
88
83
  defaultMode: "floating",
@@ -97,7 +92,7 @@ export const tickerDetailModule: PluginModule = {
97
92
  id: "historical-prices",
98
93
  name: "Historical Prices",
99
94
  icon: "H",
100
- component: HistoricalPricesPane,
95
+ get component(): typeof import("./data-panes/historical-prices").HistoricalPricesPane { const { HistoricalPricesPane } = require("./data-panes/historical-prices"); return HistoricalPricesPane; },
101
96
  headless: historicalPricesHeadless,
102
97
  defaultPosition: "right",
103
98
  tickerFollower: true,
@@ -110,7 +105,7 @@ export const tickerDetailModule: PluginModule = {
110
105
  reportFreshness: { status: "not-a-feed", basis: "price history" },
111
106
  name: "Returns",
112
107
  icon: "R",
113
- component: ReturnsPane,
108
+ get component(): typeof import("./data-panes/returns-pane").ReturnsPane { const { ReturnsPane } = require("./data-panes/returns-pane"); return ReturnsPane; },
114
109
  headless: returnsHeadless,
115
110
  defaultPosition: "right",
116
111
  tickerFollower: true,
@@ -62,6 +62,8 @@ import { describeFundamentalMarketCap } from "../../../utils/market-capitalizati
62
62
  import { liveFiftyTwoWeekRange, liveMarketCapitalization } from "../../../market-data/quotes/live-valuation";
63
63
  import { instrumentTypeLabel } from "../../../tickers/instrument-kind";
64
64
  import { ClippedText } from "../../../components/ui/display";
65
+ import { DelayedMark, delayedMarkWidth } from "../../../components/ui/delayed-mark";
66
+ import { useFreeQuoteDelay } from "../../../api-client/plan-access";
65
67
  import { safeExternalUrl } from "../../../utils/external-url";
66
68
  import { cashRunway, SHORT_RUNWAY_HISTORY } from "../../../utils/cash-runway";
67
69
  import { useOfferingNote } from "./financials/offerings";
@@ -126,6 +128,7 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
126
128
  const figureCells = useFigureCells();
127
129
 
128
130
  const quote = financials?.quote;
131
+ const quoteDelayMinutes = useFreeQuoteDelay(quote);
129
132
  const fundamentals = financials?.fundamentals;
130
133
  const capitalization = liveMarketCapitalization(quote, fundamentals);
131
134
  const profile = financials?.profile;
@@ -195,8 +198,7 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
195
198
  id: `${ticker.metadata.ticker}:price`,
196
199
  label: `${ticker.metadata.ticker} Price`,
197
200
  color: priceColor(chartDelta),
198
- // An index's axis reads in points: ^VIX at 20, never $20.
199
- unit: unknownHistoryBasis ? "unknown" : priceSeriesUnit(quoteCurrency, instrumentType),
201
+ unit: unknownHistoryBasis ? "unknown" : priceSeriesUnit(quoteCurrency, instrumentType, quote?.priceBasis),
200
202
  style: "area",
201
203
  axis: "right",
202
204
  panelId: "price",
@@ -251,7 +253,10 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
251
253
  const venueText = listingVenue ? (companyName ? ` (${listingVenue})` : listingVenue) : "";
252
254
  const sessionLabel = quoteSessionLabel(quote);
253
255
  const marketStateText = sessionLabel ? t(sessionLabel) : "";
254
- const hasIdentityLine = Boolean(companyName || venueText || marketStateText);
256
+ // Signed out or free, the headline price is the delayed one: the mark says so after the session.
257
+ const delayMinutes = sessionQuote ? quoteDelayMinutes : null;
258
+ const delayCells = delayMinutes != null ? delayedMarkWidth(delayMinutes, nativePaneChrome === true) + 1 : 0;
259
+ const hasIdentityLine = Boolean(companyName || venueText || marketStateText || delayMinutes != null);
255
260
  // The desktop logo is a square from the top of the company name's capitals down to the
256
261
  // price's baseline, so its edges meet the text's and the three read as one block. The
257
262
  // extended-hours line hangs below, as it comes and goes with the session. Without a
@@ -290,7 +295,8 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
290
295
  + quoteChangeCells + 1 + displayWidth(changeBasisText) <= quoteTextWidth;
291
296
  const companyNameWidth = Math.max(8, quoteTextWidth
292
297
  - displayWidth(venueText)
293
- - (marketStateText ? displayWidth(marketStateText) + 1 : 0));
298
+ - (marketStateText ? displayWidth(marketStateText) + 1 : 0)
299
+ - delayCells);
294
300
  // A listing that no longer trades keeps its delisting date whole on a narrow pane: the venue gives way, then the name.
295
301
  const delistedRoom = quote?.delisting && marketStateText ? quoteTextWidth - displayWidth(marketStateText) - 1 : null;
296
302
  const identityVenue = delistedRoom !== null && delistedRoom - displayWidth(venueText) < 8 ? "" : venueText;
@@ -401,7 +407,7 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
401
407
  />
402
408
  <Box flexDirection="column" flexGrow={1} flexShrink={1} minWidth={0}>
403
409
  {hasIdentityLine && (
404
- <Box flexDirection="row" minWidth={0} overflow="hidden">
410
+ <Box flexDirection="row" alignItems="center" minWidth={0} overflow="hidden">
405
411
  {identityName && (
406
412
  <ClippedText fg={colors.textDim} text={identityName} width={identityNameWidth} />
407
413
  )}
@@ -411,6 +417,11 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
411
417
  {identityName || identityVenue ? " " : ""}{marketStateText}
412
418
  </Text>
413
419
  )}
420
+ {delayMinutes != null && (
421
+ <Box flexShrink={0} paddingLeft={identityName || identityVenue || marketStateText ? 1 : 0}>
422
+ <DelayedMark minutes={delayMinutes} />
423
+ </Box>
424
+ )}
414
425
  </Box>
415
426
  )}
416
427
 
@@ -546,7 +557,6 @@ function ResolvedOverviewTab({ width, height, focused = false, ticker, financial
546
557
  series={[priceSeries]}
547
558
  panels={[{ id: "price" }]}
548
559
  levels={trade && researchTradeMatches(trade, instrumentFromTicker(ticker)) ? { seriesId: priceSeries.id, items: trade.levels } : undefined}
549
- axisWidth={8}
550
560
  showLegend={false}
551
561
  />
552
562
  </Box>
@@ -1,9 +1,8 @@
1
1
  import type { PluginModule } from "../plugin-module";
2
2
  import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
3
3
  import { timeSalesHeadless } from "./headless";
4
- import { TimeSalesPane } from "./pane";
5
4
  export const timeSalesModule: PluginModule = {
6
- panes: [{ id: "time-sales", name: "Time and Sales", icon: "T", component: TimeSalesPane,
5
+ panes: [{ id: "time-sales", name: "Time and Sales", icon: "T", get component(): typeof import("./pane").TimeSalesPane { const { TimeSalesPane } = require("./pane"); return TimeSalesPane; },
7
6
  defaultPosition: "right", tickerFollower: true, defaultMode: "floating", defaultFloatingSize: { width: 104, height: 28 },
8
7
  tableExport: true, headless: timeSalesHeadless, settings: { title: "Time and Sales", fields: [{ key: "tab", label: "View", type: "select", options: [{ value: "trades", label: "Trades" }, { value: "quotes", label: "NBBO" }] }] } }],
9
8
  paneTemplates: [
@@ -100,8 +100,10 @@ function TimeSalesView({ width, height, focused, symbol, exchange }: PaneProps &
100
100
  }, [data]);
101
101
  const tradeDecision = useRef<TapeDigits | null>(null);
102
102
  const quoteDecision = useRef<TapeDigits | null>(null);
103
+ // Decimals come from the prints alone: a session extreme can sit a fraction of a cent off a tick (NVDA's 200.9199 low)
104
+ // and must not turn every trade into four decimals. The session range reads at the prints' decimals instead.
103
105
  const tradeDigits = useMemo(() => (tradeDecision.current = stickyTapePriceDigits(tradeDecision.current, symbol,
104
- [...(data?.trades.map((trade) => trade.price) ?? []), data?.session.low ?? null, data?.session.high ?? null]))?.digits ?? 2, [data, symbol]);
106
+ data?.trades.map((trade) => trade.price) ?? []))?.digits ?? 2, [data, symbol]);
105
107
  const quoteDigits = useMemo(() => (quoteDecision.current = stickyTapePriceDigits(quoteDecision.current, symbol,
106
108
  data?.quotes.flatMap((quote) => [quote.bid, quote.ask]) ?? []))?.digits ?? 2, [data, symbol]);
107
109
  // The session range stands beside the tab's own prices, so it reads at their decimals: the quote tab's bid and ask, not the trades'.
@@ -8,7 +8,7 @@ import {
8
8
  quotedCompanyName,
9
9
  type CorporateBond,
10
10
  } from "./client";
11
- import { bondStackPriority, formatBondChange, formatBondDecimal, type BondColumnId } from "./model";
11
+ import { bondStackPriority, formatBondChange, formatBondDecimal, sharesOneIssuer, type BondColumnId } from "./model";
12
12
 
13
13
  const decimal = (digits: number) => (value: unknown) => (
14
14
  typeof value === "number" ? formatBondDecimal(value, digits) : "—"
@@ -33,6 +33,15 @@ const COLUMNS: HeadlessPaneColumn[] = [
33
33
  column("grade", { key: "traceGradeCode", header: "Grade" }),
34
34
  ];
35
35
 
36
+ /**
37
+ * One issuer's tape (`sharesOneIssuer`) leaves the issuer column out of the
38
+ * text table, as the pane does; exports and piped output keep it.
39
+ */
40
+ function reportColumns(bonds: readonly CorporateBond[]): HeadlessPaneColumn[] {
41
+ if (!sharesOneIssuer(bonds)) return COLUMNS;
42
+ return COLUMNS.map((spec) => spec.key === "issuerName" ? { ...spec, minTableWidth: Number.MAX_SAFE_INTEGER } : spec);
43
+ }
44
+
36
45
  function reportRow(bond: CorporateBond) {
37
46
  return {
38
47
  id: bond.id,
@@ -85,7 +94,7 @@ export const traceBondsHeadless: HeadlessPaneDefinition<"bundle"> = {
85
94
  sections: [{
86
95
  // An empty answer is its message alone, dated in its own words.
87
96
  title: bonds.length > 0 ? (tradeDate ? `Last sales ${tradeDate}` : "Last sales") : query ? "" : "Last sales",
88
- columns: COLUMNS,
97
+ columns: reportColumns(bonds),
89
98
  rows: bonds.map(reportRow),
90
99
  ...(query ? { empty: noPrintsText(query, tradeDate, issuer) } : {}),
91
100
  }],
@@ -85,26 +85,32 @@ export function sortBonds(bonds: readonly CorporateBond[], sort: BondSort): Corp
85
85
  });
86
86
  }
87
87
 
88
+ /**
89
+ * What a sale says leads: its price, yield and CUSIP, after the issuer when
90
+ * the rows are several issuers', before what describes the bond (coupon,
91
+ * maturity) and the rest of the sale.
92
+ */
88
93
  const BOND_COLUMNS: readonly BondColumn[] = [
89
94
  { id: "issuer", label: "Issuer", width: 18, align: "left", flexGrow: 1 },
95
+ { id: "price", label: "Price", width: 9, align: "right" },
96
+ { id: "yield", label: "Yield", width: 8, align: "right" },
90
97
  { id: "cusip", label: "CUSIP", width: 10, align: "left" },
91
98
  { id: "coupon", label: "Coupon", width: 7, align: "right" },
92
99
  { id: "maturity", label: "Maturity", width: 11, align: "left" },
93
- { id: "price", label: "Price", width: 9, align: "right" },
94
- { id: "yield", label: "Yield", width: 8, align: "right" },
95
100
  { id: "change", label: "Chg", width: 8, align: "right" },
96
101
  { id: "traded", label: "Traded", width: 11, align: "left" },
97
102
  { id: "grade", label: "Grade", width: 6, align: "left" },
98
103
  ];
99
104
 
100
105
  /**
101
- * What a narrow tape gives up first. The issuer, coupon and maturity name a
102
- * bond, so two of one issuer's issues stay told apart, and its price is
103
- * what was printed; they never go. The trade date, the default sort and
104
- * how old a last sale is, outlasts the CUSIP and yield. The headless report
105
- * moves its columns under the rows in the same order (`bondStackPriority`).
106
+ * What a narrow tape gives up first. The price and yield are what was printed
107
+ * and the CUSIP tells two issues with one coupon and maturity apart, so they
108
+ * never go, nor does the issuer while it names the row; the descriptive
109
+ * columns go before them, the coupon last as it is the narrowest. The
110
+ * headless report moves its columns under the rows in the same order
111
+ * (`bondStackPriority`).
106
112
  */
107
- const BOND_NARROW_DROP_ORDER: readonly BondColumnId[] = ["grade", "change", "cusip", "yield", "traded"];
113
+ const BOND_NARROW_DROP_ORDER: readonly BondColumnId[] = ["grade", "change", "traded", "maturity", "coupon"];
108
114
 
109
115
  /** `stackPriority` of each column in the text report: the higher, the sooner it moves under its row. */
110
116
  export function bondStackPriority(columnId: BondColumnId): number {
@@ -112,6 +118,34 @@ export function bondStackPriority(columnId: BondColumnId): number {
112
118
  return index < 0 ? 0 : BOND_NARROW_DROP_ORDER.length - index;
113
119
  }
114
120
 
115
- export function buildBondColumns(width: number): BondColumn[] {
116
- return fitChartTableColumns(BOND_COLUMNS, width, BOND_NARROW_DROP_ORDER);
121
+ /** The tape's symbol root of an issue (`WHR` of `WHR4800338` and `WHR.GM`), or null without one. */
122
+ function symbolRoot(symbol: string | null): string | null {
123
+ return symbol?.match(/^[A-Z]+/)?.[0] ?? null;
124
+ }
125
+
126
+ /**
127
+ * Whether every row is the same issuer's, so an issuer column would only
128
+ * repeat it: more than one row, all under one symbol root, or all one name
129
+ * where a row has no symbol. One row keeps the name, the only thing naming it.
130
+ */
131
+ export function sharesOneIssuer(bonds: readonly Pick<CorporateBond, "issuerName" | "symbol">[]): boolean {
132
+ if (bonds.length < 2) return false;
133
+ const roots = bonds.map((bond) => symbolRoot(bond.symbol));
134
+ if (roots.every((root) => root != null)) return new Set(roots).size === 1;
135
+ return new Set(bonds.map((bond) => bond.issuerName.trim().toUpperCase())).size === 1;
136
+ }
137
+
138
+ /**
139
+ * The sort the tape shows: a saved issuer sort, while one issuer's tape leaves
140
+ * the issuer column out, sorts as the default, which the header can show; the
141
+ * saved sort comes back with the column.
142
+ */
143
+ export function shownBondSort(sort: BondSort, sharedIssuer: boolean): BondSort {
144
+ return sharedIssuer && sort.columnId === "issuer" ? DEFAULT_BOND_SORT : sort;
145
+ }
146
+
147
+ /** The columns at `width`, without the issuer's when every row shares it (`sharesOneIssuer`). */
148
+ export function buildBondColumns(width: number, { sharedIssuer = false }: { sharedIssuer?: boolean } = {}): BondColumn[] {
149
+ const columns = sharedIssuer ? BOND_COLUMNS.filter((column) => column.id !== "issuer") : BOND_COLUMNS;
150
+ return fitChartTableColumns(columns, width, BOND_NARROW_DROP_ORDER);
117
151
  }
@@ -40,6 +40,8 @@ import {
40
40
  formatBondChange,
41
41
  formatBondDecimal,
42
42
  isBondColumnId,
43
+ sharesOneIssuer,
44
+ shownBondSort,
43
45
  sortBonds,
44
46
  type BondColumn,
45
47
  type BondSort,
@@ -73,8 +75,11 @@ export function TraceBondsPane({ focused, width, height }: PaneProps) {
73
75
  useAutoRefresh(updatedAt, load);
74
76
  usePaneRefreshKey(() => void reload(), { focused, enabled: !searchActive });
75
77
 
76
- const rows = useMemo(() => sortBonds(bonds, sort), [bonds, sort]);
77
- const columns = useMemo(() => buildBondColumns(width), [width]);
78
+ // One issuer's tape says its name once, in what was asked, not on every row.
79
+ const sharedIssuer = useMemo(() => sharesOneIssuer(bonds), [bonds]);
80
+ const columns = useMemo(() => buildBondColumns(width, { sharedIssuer }), [sharedIssuer, width]);
81
+ const shownSort = shownBondSort(sort, sharedIssuer);
82
+ const rows = useMemo(() => sortBonds(bonds, shownSort), [bonds, shownSort]);
78
83
  const tradeDate = answered ? data.tradeDate : null;
79
84
  const footerInfo = useMemo(
80
85
  () => (tradeDate ? [{ id: "trade-date", parts: [{ text: tradeDate }] }] : []),
@@ -96,11 +101,11 @@ export function TraceBondsPane({ focused, width, height }: PaneProps) {
96
101
 
97
102
  const onHeaderClick = useCallback((columnId: string) => {
98
103
  if (!isBondColumnId(columnId)) return;
99
- setSort((current) => nextHeaderSort(current, columnId, {
104
+ setSort((current) => nextHeaderSort(shownBondSort(current, sharedIssuer), columnId, {
100
105
  firstDirection: firstBondSortDirection,
101
106
  resetTo: DEFAULT_BOND_SORT,
102
107
  }));
103
- }, [setSort]);
108
+ }, [setSort, sharedIssuer]);
104
109
 
105
110
  const handleRootKeyDown = useCallback((
106
111
  event: DataTableKeyEvent,
@@ -190,13 +195,13 @@ export function TraceBondsPane({ focused, width, height }: PaneProps) {
190
195
  }}
191
196
  columns={columns}
192
197
  items={rows}
193
- sortColumnId={sort.columnId}
194
- sortDirection={sort.direction}
198
+ sortColumnId={shownSort.columnId}
199
+ sortDirection={shownSort.direction}
195
200
  onHeaderClick={onHeaderClick}
196
201
  onSortChange={(columnId, direction) => {
197
202
  if (isBondColumnId(columnId)) setSort({ columnId, direction });
198
203
  }}
199
- resetScrollKey={`${needle}:${sort.columnId ?? ""}:${sort.direction}`}
204
+ resetScrollKey={`${needle}:${shownSort.columnId ?? ""}:${shownSort.direction}`}
200
205
  getItemKey={(row) => row.id}
201
206
  renderCell={renderCell}
202
207
  selectedTextOverridesCellColor
@@ -13,7 +13,7 @@ import {
13
13
  type DataTableRootKeyContext,
14
14
  } from "../../../components";
15
15
  import { loadingErrorFooterInfo, usePaneRefreshKey } from "../../../components/data-table/table-pane";
16
- import { savedCopyFooterInfo } from "../../../components/layout/pane/footer";
16
+ import { ageFooterSegment, savedCopyFooterInfo } from "../../../components/layout/pane/footer";
17
17
  import { STALE_SEGMENT } from "../../../components/layout/pane/status-footer";
18
18
  import { usePaneInstance } from "../../../state/app/context";
19
19
  import { colors } from "../../../theme/colors";
@@ -309,7 +309,7 @@ export function TreasuryAuctionsPane({ focused, width, height }: PaneProps) {
309
309
  if (savedAt != null) info.push(...savedCopyFooterInfo([savedAt]));
310
310
  else if (stale) info.push(STALE_SEGMENT);
311
311
  if (fetchedAt && savedAt == null) {
312
- info.push({ id: "updated", parts: [{ text: formatRelativeAge(fetchedAt), tone: "muted" }] });
312
+ info.push(ageFooterSegment(formatRelativeAge(fetchedAt)));
313
313
  }
314
314
  return {
315
315
  info,
@@ -17,7 +17,14 @@ import { expiredExpiryText, findListedExpiry, isLiveOptionExpiry, liveOptionExpi
17
17
  import { optionModelSpot } from "../options/model-spot";
18
18
 
19
19
  const DEFAULT_SURFACE_EXPIRY_LIMIT = 18;
20
+ /** Expiries read at a time from a source that takes them one by one. */
20
21
  const SURFACE_LOAD_CONCURRENCY = 4;
22
+ /**
23
+ * Expiries asked at once from a source that batches them (`batchesExpiries`):
24
+ * the API client sends one listing's expiries asked in one moment in batch
25
+ * requests of six, side by side.
26
+ */
27
+ const SURFACE_BATCHED_CONCURRENCY = 48;
21
28
  /** Each kept expiry must be at least this much further out than the previous one. */
22
29
  const SURFACE_TENOR_RATIO = 1.35;
23
30
 
@@ -43,8 +50,10 @@ export function selectSurfaceExpiries(catalogue: readonly number[], limit: numbe
43
50
  }
44
51
 
45
52
  export interface SurfaceLoaderDependencies {
46
- loadOptions(request: OptionsRequest, options?: { forceRefresh?: boolean }): Promise<QueryEntry<OptionsChain>>;
53
+ loadOptions(request: OptionsRequest, options?: { forceRefresh?: boolean; batch?: boolean }): Promise<QueryEntry<OptionsChain>>;
47
54
  loadYieldCurve(): Promise<YieldPoint[]>;
55
+ /** Whether `loadOptions` reaches a source that batches expiries asked together; absent, four are read at a time. */
56
+ batchesExpiries?(): boolean | Promise<boolean>;
48
57
  now?: () => number;
49
58
  }
50
59
 
@@ -58,6 +67,7 @@ export function createSurfaceDependencies(
58
67
  loadOptions: (request, options) => coordinator
59
68
  ? coordinator.loadOptions(request, options)
60
69
  : Promise.reject(new Error("Market data coordinator unavailable")),
70
+ batchesExpiries: () => coordinator?.batchesOptions() ?? false,
61
71
  loadYieldCurve: () => cloudApi.getCloudYieldCurve(),
62
72
  };
63
73
  }
@@ -179,6 +189,8 @@ export async function loadVolatilitySurface(
179
189
  };
180
190
  checkAbort();
181
191
  publish();
192
+ // Asked while the catalogue loads, so it costs the expiries no wait.
193
+ const batched = Promise.resolve().then(() => dependencies.batchesExpiries?.()).then((answer) => answer === true, () => false);
182
194
  const treasury = Promise.resolve().then(() => dependencies.loadYieldCurve()).then((value) => {
183
195
  if (request.signal?.aborted) return;
184
196
  curve = value;
@@ -233,6 +245,9 @@ export async function loadVolatilitySurface(
233
245
  publish();
234
246
  }
235
247
 
248
+ // A source that takes expiries one by one is asked four at a time, and a cancelled load asks for no more.
249
+ const batch = await abortable(batched, request.signal, CANCELLED);
250
+ const concurrency = batch ? SURFACE_BATCHED_CONCURRENCY : SURFACE_LOAD_CONCURRENCY;
236
251
  let next = 0;
237
252
  const worker = async () => {
238
253
  while (!request.signal?.aborted && next < selected.length) {
@@ -240,7 +255,7 @@ export async function loadVolatilitySurface(
240
255
  try {
241
256
  // The catalogue response already refreshed its own slice, unless it came from cache.
242
257
  const entry = await dependencies.loadOptions({ instrument: request.instrument, expirationDate: expiration },
243
- { forceRefresh: !!request.forceRefresh && (!!request.expiries || expiration !== catalogueExpiration) });
258
+ { forceRefresh: !!request.forceRefresh && (!!request.expiries || expiration !== catalogueExpiration), ...(batch ? { batch } : {}) });
244
259
  if (request.signal?.aborted) return;
245
260
  entries.set(expiration, entry);
246
261
  if (!resolveEntryValue(entry) && !entry.error) errors.set(expiration, "Options chain unavailable");
@@ -251,7 +266,7 @@ export async function loadVolatilitySurface(
251
266
  publish();
252
267
  }
253
268
  };
254
- await abortable(Promise.all([treasury, ...Array.from({ length: Math.min(SURFACE_LOAD_CONCURRENCY, selected.length) }, worker)]), request.signal, CANCELLED);
269
+ await abortable(Promise.all([treasury, ...Array.from({ length: Math.min(concurrency, selected.length) }, worker)]), request.signal, CANCELLED);
255
270
  checkAbort();
256
271
  finished = true;
257
272
  const result = snapshot();
@@ -1,11 +1,10 @@
1
1
  import type { PluginModule } from "../plugin-module";
2
2
  import { createTickerSurfacePaneTemplate } from "../shared/ticker-surface";
3
- import { VolSurfacePane } from "./pane";
4
3
  import { volSurfaceHeadless } from "./headless";
5
4
 
6
5
  export const volSurfaceModule: PluginModule = {
7
6
  panes: [{
8
- id: "vol-surface", name: "Volatility Surface", icon: "V", component: VolSurfacePane,
7
+ id: "vol-surface", name: "Volatility Surface", icon: "V", get component(): typeof import("./pane").VolSurfacePane { const { VolSurfacePane } = require("./pane"); return VolSurfacePane; },
9
8
  defaultPosition: "right", tickerFollower: true, defaultMode: "floating", defaultFloatingSize: { width: 126, height: 36 },
10
9
  tableExport: true, headless: volSurfaceHeadless,
11
10
  settings: {
@@ -1,13 +1,12 @@
1
1
  import type { PluginModule } from "../plugin-module";
2
2
  import { volatilityHeadless } from "./headless";
3
- import { VolatilityPane } from "./pane";
4
3
  import { VOLATILITY_SERIES } from "./series";
5
4
 
6
5
 
7
6
  export const volatilityModule: PluginModule = {
8
7
  series: VOLATILITY_SERIES,
9
8
  panes: [{
10
- id: "volatility-term-structure", name: "Volatility", icon: "V", component: VolatilityPane,
9
+ id: "volatility-term-structure", name: "Volatility", icon: "V", get component(): typeof import("./pane").VolatilityPane { const { VolatilityPane } = require("./pane"); return VolatilityPane; },
11
10
  defaultPosition: "right", defaultMode: "floating", defaultFloatingSize: { width: 110, height: 34 },
12
11
  tableExport: true, headless: volatilityHeadless,
13
12
  }],
@@ -1,7 +1,9 @@
1
1
  import { usePaneFooter, type PaneFooterSegment } from "../../../components";
2
2
  import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
3
+ import { useQuoteDelayFooter } from "../shared/cloud-upgrade";
3
4
  import {
4
5
  boardErrorMessage,
6
+ boardQuotes,
5
7
  quoteBoardFooterInfo,
6
8
  quoteBoardStatus,
7
9
  type BoardQuoteMap,
@@ -12,14 +14,17 @@ export function useWorldIndicesFooter(quotes: BoardQuoteMap, onRefresh: () => vo
12
14
  const errorMessage = boardErrorMessage(quotes);
13
15
 
14
16
  usePaneRefreshKey(onRefresh, { focused });
17
+ // Signed out or free, the indices' quotes are delayed ones.
18
+ const quoteDelay = useQuoteDelayFooter(boardQuotes(quotes), "indices-delay");
15
19
 
16
20
  usePaneFooter(
17
21
  "world-indices",
18
22
  () => {
19
23
  const info: PaneFooterSegment[] = quoteBoardFooterInfo(status);
20
24
  if (errorMessage) info.push({ id: "reason", parts: [{ text: errorMessage, tone: "warning" }] });
25
+ if (quoteDelay) info.push(quoteDelay);
21
26
  return { info };
22
27
  },
23
- [errorMessage, focused, onRefresh, status.latestTs, status.loading, status.savedAt, status.stale, status.unavailable],
28
+ [errorMessage, focused, onRefresh, quoteDelay, status.latestTs, status.loading, status.savedAt, status.stale, status.unavailable],
24
29
  );
25
30
  }
@@ -10,11 +10,20 @@ import {
10
10
  type GeoLayersPayload,
11
11
  type GeoRequest,
12
12
  } from "../../../api-client/geo";
13
+ import { withLocalGeoLayers } from "./local-layers";
13
14
 
14
15
  export const ENTITY_PAGE_SIZE = 100;
15
16
 
16
17
  /** Requests under /cloud/geo/ through the shared client, resolved late so tests can stub it. */
17
- export const cloudGeoRequest: GeoRequest = (path, init) => apiClient.geo(path, init);
18
+ const serverGeoRequest: GeoRequest = (path, init) => apiClient.geo(path, init);
19
+
20
+ /** The server's layers and the app's own (`local-layers.ts`). */
21
+ export const cloudGeoRequest: GeoRequest = withLocalGeoLayers(serverGeoRequest);
22
+
23
+ /** The same, with the app's own layers narrowed to what a linked pane selects. */
24
+ export function scopedGeoRequest(scope: string | null): GeoRequest {
25
+ return scope ? withLocalGeoLayers(serverGeoRequest, scope) : cloudGeoRequest;
26
+ }
18
27
 
19
28
  export async function loadWorldVenues(): Promise<CloudWorldVenueMapPayload> {
20
29
  const response = await apiClient.getCloudWorldVenues();
@@ -9,6 +9,7 @@ import { CHOKEPOINT_SERIES, createGeoChartSeriesCapability } from "./geo-series"
9
9
  import { mapHeadless } from "./headless";
10
10
  import { buildMapSettingsDef, groupTitle, MAP_PRESET_OPTIONS, parseMapPreset } from "./layers";
11
11
  import { WORLD_VENUE_MAP_PANE_ID, WorldVenueMapPane } from "./pane";
12
+ import { mapFollowsTicker } from "./scope";
12
13
 
13
14
  const chartComposerHeadless = chartHeadless("chart-composer-pane");
14
15
 
@@ -33,6 +34,7 @@ const chokepointTemplate: PaneTemplateDef = {
33
34
  keywords: ["chokepoint", "chokepoints", "transits", "suez", "panama", "hormuz", "malacca", "shipping", "strait", "canal"],
34
35
  shortcut: { prefix: "CHOKE", argPlaceholder: "chokepoint", argKind: "text", argOptional: true },
35
36
  canCreate: () => true,
37
+ catalogSettings: {},
36
38
  createInstance: (_context, options) => createChokepointInstance(cloudGeoRequest, options?.arg),
37
39
  };
38
40
 
@@ -46,6 +48,8 @@ export const worldVenueMapModule: PluginModule = {
46
48
  defaultPosition: "right",
47
49
  defaultMode: "floating",
48
50
  defaultFloatingSize: { width: 124, height: 36 },
51
+ // Linked to a list, a map with the app's own layers (facilities) shows the selected company's.
52
+ tickerFollower: mapFollowsTicker,
49
53
  // The layer picker comes from the server's catalog; without one the pane keeps its old settings.
50
54
  settings: (context) => buildMapSettingsDef(context.settings, peekGeoCatalog()?.layers) ?? { fields: [] },
51
55
  },
@@ -55,8 +59,8 @@ export const worldVenueMapModule: PluginModule = {
55
59
  id: "world-venue-map-pane",
56
60
  paneId: WORLD_VENUE_MAP_PANE_ID,
57
61
  label: "World Map",
58
- description: "A world map with trading venues and their hours; ships, ports, energy and airports as layers.",
59
- keywords: ["world", "map", "globe", "countries", "venue", "venues", "exchange", "mic", "market hours", "open markets", "ships", "ship", "vessels", "vessel", "shipping", "ais", "ports", "port", "chokepoints", "pipelines", "airports", "energy", "layers"],
62
+ description: "A world map with trading venues and their hours; ships, ports, energy, airports and AI buildout facilities as layers.",
63
+ keywords: ["world", "map", "globe", "countries", "venue", "venues", "exchange", "mic", "market hours", "open markets", "ships", "ship", "vessels", "vessel", "shipping", "ais", "ports", "port", "chokepoints", "pipelines", "airports", "energy", "facilities", "data centers", "fabs", "layers"],
60
64
  shortcut: {
61
65
  prefix: "MAP",
62
66
  argPlaceholder: "layers",