@gloom-sh/terminal 0.19.1 → 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 (554) hide show
  1. package/README.md +7 -1
  2. package/package.json +2 -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 +58 -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/runtime/startup.ts +3 -1
  20. package/src/app.tsx +23 -1
  21. package/src/architecture/core-builtin-allowlist.json +2 -1
  22. package/src/capabilities/chart-series.ts +2 -1
  23. package/src/capabilities/factories.ts +1 -0
  24. package/src/cli/ascii-output.ts +42 -0
  25. package/src/cli/base-converter.ts +10 -2
  26. package/src/cli/command-options.ts +16 -1
  27. package/src/cli/commands/crypto-hints.ts +23 -4
  28. package/src/cli/commands/market.ts +101 -27
  29. package/src/cli/commands/news-search.ts +24 -5
  30. package/src/cli/commands/overview.ts +14 -10
  31. package/src/cli/commands/search.ts +84 -25
  32. package/src/cli/commands/system.ts +22 -30
  33. package/src/cli/commands/ticker.ts +49 -9
  34. package/src/cli/commands/version.ts +33 -0
  35. package/src/cli/context.ts +6 -9
  36. package/src/cli/data-dir.ts +7 -0
  37. package/src/cli/desktop-pane-shot-api-policy.ts +85 -0
  38. package/src/cli/desktop-pane-shot-browser.ts +320 -0
  39. package/src/cli/desktop-pane-shot-routes.ts +26 -1
  40. package/src/cli/desktop-pane-shot.ts +196 -256
  41. package/src/cli/early-command.ts +42 -0
  42. package/src/cli/earnings-calendar.ts +9 -0
  43. package/src/cli/entry.ts +19 -13
  44. package/src/cli/errors.ts +12 -2
  45. package/src/cli/glossary.ts +2 -1
  46. package/src/cli/help.ts +1 -1
  47. package/src/cli/history-export.ts +22 -2
  48. package/src/cli/history-rows.ts +189 -11
  49. package/src/cli/index.ts +18 -1
  50. package/src/cli/known-gaps.ts +51 -5
  51. package/src/cli/listing-arg.ts +63 -10
  52. package/src/cli/options.ts +1 -1
  53. package/src/cli/output-file.ts +60 -13
  54. package/src/cli/pane-functions/catalog.ts +14 -3
  55. package/src/cli/pane-functions/data.ts +1 -1
  56. package/src/cli/pane-functions/dom.ts +45 -0
  57. package/src/cli/pane-functions/freshness.ts +46 -9
  58. package/src/cli/pane-functions/function-help.ts +17 -9
  59. package/src/cli/pane-functions/headless.ts +22 -14
  60. package/src/cli/pane-functions/index.ts +6 -2
  61. package/src/cli/pane-functions/options.ts +6 -2
  62. package/src/cli/pane-functions/resolver.ts +27 -5
  63. package/src/cli/pane-functions/screenshot-assessment.ts +33 -0
  64. package/src/cli/pane-functions/screenshot-bridge.ts +94 -13
  65. package/src/cli/pane-functions/screenshot-http.ts +70 -0
  66. package/src/cli/pane-functions/screenshot-render.ts +19 -5
  67. package/src/cli/quote-answer.ts +49 -6
  68. package/src/cli/registry.ts +41 -13
  69. package/src/cli/result.ts +12 -6
  70. package/src/cli/ticker-resolution.ts +2 -1
  71. package/src/cli/types.ts +2 -0
  72. package/src/components/chart/composite/format.ts +3 -0
  73. package/src/components/chart-table/header.tsx +1 -1
  74. package/src/components/chart-table/layout.ts +28 -11
  75. package/src/components/command-bar/action-runtime.ts +4 -0
  76. package/src/components/command-bar/commands/direct/index.ts +18 -33
  77. package/src/components/command-bar/commands/direct/runtime.ts +5 -0
  78. package/src/components/command-bar/commands/registry.ts +10 -1
  79. package/src/components/command-bar/routes/root/command-items.ts +9 -4
  80. package/src/components/command-bar/routes/root/runtime.ts +51 -6
  81. package/src/components/command-bar/selection-runtime.ts +12 -29
  82. package/src/components/command-bar/surface/index.tsx +9 -3
  83. package/src/components/company-logo.tsx +2 -1
  84. package/src/components/layout/detached-pane-shell.tsx +2 -2
  85. package/src/components/layout/header.tsx +10 -5
  86. package/src/components/layout/market-summary.ts +25 -7
  87. package/src/components/layout/pane/footer/index.tsx +8 -2
  88. package/src/components/layout/pane/footer/model.ts +73 -6
  89. package/src/components/layout/pane/header.tsx +2 -2
  90. package/src/components/layout/pane/status-footer.ts +2 -1
  91. package/src/components/market-board/index.tsx +3 -2
  92. package/src/components/onboarding/desks-step.tsx +12 -4
  93. package/src/components/onboarding/first-run-workspace.ts +81 -4
  94. package/src/components/onboarding/onboarding-frame.tsx +4 -2
  95. package/src/components/onboarding/onboarding-keyboard.ts +8 -5
  96. package/src/components/onboarding/onboarding-wizard.tsx +60 -26
  97. package/src/components/onboarding/portfolio-step/positions-panel.tsx +7 -2
  98. package/src/components/onboarding/run-setup-again.ts +24 -0
  99. package/src/components/onboarding/use-onboarding-progress.ts +33 -15
  100. package/src/components/onboarding/use-onboarding-upgrade.ts +2 -6
  101. package/src/components/onboarding/wizard-model.ts +57 -3
  102. package/src/components/onboarding/wizard-positions.ts +1 -1
  103. package/src/components/ui/delayed-mark.tsx +56 -0
  104. package/src/components/ui/display.tsx +16 -5
  105. package/src/components/ui/external-link.tsx +3 -6
  106. package/src/components/ui/icon.tsx +16 -3
  107. package/src/components/ui/logo-mark.tsx +8 -5
  108. package/src/components/ui/stat-grid.tsx +263 -19
  109. package/src/core/app-services.ts +11 -2
  110. package/src/data/app-persistence.ts +9 -7
  111. package/src/data/config/store/node.ts +11 -5
  112. package/src/data/config/store/normalize.ts +16 -3
  113. package/src/data/plugin-cache.ts +15 -0
  114. package/src/data/plugin-state-store.ts +81 -24
  115. package/src/data/profile-permissions.ts +47 -0
  116. package/src/data/resource-store.ts +165 -90
  117. package/src/data/session-store.ts +31 -8
  118. package/src/data/sqlite/access.ts +324 -0
  119. package/src/data/sqlite/checkpointer.ts +285 -0
  120. package/src/data/sqlite/database.ts +80 -4
  121. package/src/data/sqlite/retry.ts +50 -9
  122. package/src/data/ticker-store.ts +57 -23
  123. package/src/docs/snapshot.json +191 -78
  124. package/src/i18n/es.ts +18 -2
  125. package/src/i18n/ja.ts +18 -2
  126. package/src/i18n/ko.ts +18 -2
  127. package/src/i18n/zh-cn.ts +18 -2
  128. package/src/i18n/zh-tw.ts +18 -2
  129. package/src/layout/desk-keys.ts +4 -1
  130. package/src/layout/desks.ts +38 -5
  131. package/src/layout-marketplace/gallery-desktop.tsx +45 -16
  132. package/src/market-data/batched-reads.ts +16 -0
  133. package/src/market-data/coordinator/auxiliary.ts +4 -2
  134. package/src/market-data/coordinator/index.ts +11 -2
  135. package/src/market-data/futures-board.ts +1 -1
  136. package/src/market-data/futures-contract.ts +3 -1
  137. package/src/market-data/market/index-level.ts +6 -4
  138. package/src/market-data/published-apac-sessions.ts +6 -0
  139. package/src/market-data/realized-volatility.ts +4 -2
  140. package/src/news/aggregator.ts +7 -4
  141. package/src/news/news-model.ts +11 -0
  142. package/src/news/story-id.ts +22 -0
  143. package/src/news/ticker-symbols.ts +17 -1
  144. package/src/notifications/app-notifier.ts +35 -14
  145. package/src/plugins/auto-update-node.ts +10 -6
  146. package/src/plugins/builtin/account-management/form-components.tsx +1 -1
  147. package/src/plugins/builtin/account-management/index.tsx +2 -3
  148. package/src/plugins/builtin/account-management/model.ts +1 -1
  149. package/src/plugins/builtin/alerts/cli-forms.ts +15 -1
  150. package/src/plugins/builtin/alerts/events-pane.tsx +7 -5
  151. package/src/plugins/builtin/alerts/events.ts +10 -2
  152. package/src/plugins/builtin/alerts/index.tsx +5 -5
  153. package/src/plugins/builtin/alerts/research-builder.ts +5 -1
  154. package/src/plugins/builtin/alerts/research-rules.ts +18 -0
  155. package/src/plugins/builtin/alerts/targets.ts +4 -1
  156. package/src/plugins/builtin/attention/headless.ts +1 -1
  157. package/src/plugins/builtin/attention/index.tsx +2 -4
  158. package/src/plugins/builtin/attention/listing.ts +1 -1
  159. package/src/plugins/builtin/attention/series.ts +1 -1
  160. package/src/plugins/builtin/awards/index.tsx +1 -2
  161. package/src/plugins/builtin/backtest/client.ts +1 -0
  162. package/src/plugins/builtin/backtest/index.tsx +1 -2
  163. package/src/plugins/builtin/backtest/rules.ts +3 -1
  164. package/src/plugins/builtin/backtest/window.ts +8 -7
  165. package/src/plugins/builtin/broker-manager/detail.tsx +16 -3
  166. package/src/plugins/builtin/broker-manager/index.tsx +1 -1
  167. package/src/plugins/builtin/buildout/cells.tsx +5 -0
  168. package/src/plugins/builtin/buildout/company/flow.tsx +367 -0
  169. package/src/plugins/builtin/buildout/company/id.ts +2 -0
  170. package/src/plugins/builtin/buildout/company/model.ts +247 -0
  171. package/src/plugins/builtin/buildout/company/pane.tsx +317 -0
  172. package/src/plugins/builtin/buildout/data-runtime.ts +31 -10
  173. package/src/plugins/builtin/buildout/facilities.ts +309 -0
  174. package/src/plugins/builtin/buildout/format.ts +9 -0
  175. package/src/plugins/builtin/buildout/index.tsx +51 -5
  176. package/src/plugins/builtin/buildout/model/index.ts +62 -10
  177. package/src/plugins/builtin/buildout/model/normalizers.ts +5 -0
  178. package/src/plugins/builtin/buildout/model/types.ts +11 -2
  179. package/src/plugins/builtin/buildout/pane/index.tsx +91 -20
  180. package/src/plugins/builtin/buildout/pane/status.tsx +1 -1
  181. package/src/plugins/builtin/buildout/pane/themes.tsx +110 -0
  182. package/src/plugins/builtin/buildout/table-model.ts +26 -2
  183. package/src/plugins/builtin/builtin-plugin-meta.ts +16 -0
  184. package/src/plugins/builtin/cds/headless.ts +10 -2
  185. package/src/plugins/builtin/cds/model.ts +7 -3
  186. package/src/plugins/builtin/central-bank-rates/index.tsx +1 -2
  187. package/src/plugins/builtin/central-bank-rates/pane.tsx +2 -1
  188. package/src/plugins/builtin/central-bank-rates/series.ts +2 -3
  189. package/src/plugins/builtin/changelog/index.tsx +3 -2
  190. package/src/plugins/builtin/chart-composer/cli-options.ts +16 -0
  191. package/src/plugins/builtin/chart-composer/headless-schema.ts +10 -0
  192. package/src/plugins/builtin/chart-composer/headless.ts +88 -21
  193. package/src/plugins/builtin/chart-composer/index.tsx +2 -3
  194. package/src/plugins/builtin/chart-composer/pane.tsx +30 -2
  195. package/src/plugins/builtin/chart-composer/series-expression.ts +3 -2
  196. package/src/plugins/builtin/chart-composer/session-range.ts +5 -2
  197. package/src/plugins/builtin/chart-composer/studies.ts +1 -1
  198. package/src/plugins/builtin/chat/controller/index.ts +1 -1
  199. package/src/plugins/builtin/chat/controller/messages.ts +15 -4
  200. package/src/plugins/builtin/chat/ghost-user.ts +8 -0
  201. package/src/plugins/builtin/chat/index.tsx +5 -5
  202. package/src/plugins/builtin/clinical-trials/index.tsx +1 -2
  203. package/src/plugins/builtin/cloud/company-picker.tsx +1 -1
  204. package/src/plugins/builtin/cloud/index.tsx +1 -2
  205. package/src/plugins/builtin/cloud/plugin.tsx +6 -4
  206. package/src/plugins/builtin/cloud/team/module.tsx +1 -1
  207. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -1
  208. package/src/plugins/builtin/cloud/trial-offer-status-widget.tsx +24 -1
  209. package/src/plugins/builtin/cloud/upgrade-status-widget.tsx +39 -15
  210. package/src/plugins/builtin/comment-letters/index.tsx +1 -2
  211. package/src/plugins/builtin/commodity-balances/client.ts +4 -1
  212. package/src/plugins/builtin/commodity-balances/index.tsx +3 -3
  213. package/src/plugins/builtin/commodity-balances/pane.tsx +2 -2
  214. package/src/plugins/builtin/congress-trades/aggregates.ts +13 -2
  215. package/src/plugins/builtin/congress-trades/detail.tsx +20 -4
  216. package/src/plugins/builtin/congress-trades/headless.ts +2 -0
  217. package/src/plugins/builtin/congress-trades/model.ts +92 -64
  218. package/src/plugins/builtin/congress-trades/table.tsx +14 -2
  219. package/src/plugins/builtin/connections/index.ts +2 -3
  220. package/src/plugins/builtin/cot/client.ts +28 -2
  221. package/src/plugins/builtin/cot/headless.ts +4 -1
  222. package/src/plugins/builtin/cot/index.tsx +5 -4
  223. package/src/plugins/builtin/cot/model.ts +58 -7
  224. package/src/plugins/builtin/cot/pane.tsx +6 -2
  225. package/src/plugins/builtin/cpi/client.ts +6 -5
  226. package/src/plugins/builtin/cpi/index.tsx +1 -2
  227. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
  228. package/src/plugins/builtin/credit-boards/client.ts +7 -2
  229. package/src/plugins/builtin/credit-boards/headless.ts +25 -12
  230. package/src/plugins/builtin/credit-boards/model.ts +29 -0
  231. package/src/plugins/builtin/credit-boards/series.ts +3 -4
  232. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +9 -4
  233. package/src/plugins/builtin/credit-conditions/client.ts +22 -14
  234. package/src/plugins/builtin/crypto-board/bitcoin-network.ts +143 -0
  235. package/src/plugins/builtin/crypto-board/client.ts +5 -5
  236. package/src/plugins/builtin/crypto-board/headless.ts +4 -3
  237. package/src/plugins/builtin/crypto-board/index.tsx +4 -2
  238. package/src/plugins/builtin/crypto-board/series.ts +6 -4
  239. package/src/plugins/builtin/crypto-etf-flows/client.ts +9 -2
  240. package/src/plugins/builtin/crypto-etf-flows/headless.ts +61 -6
  241. package/src/plugins/builtin/crypto-etf-flows/index.tsx +1 -2
  242. package/src/plugins/builtin/crypto-etf-flows/model.ts +14 -1
  243. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +8 -5
  244. package/src/plugins/builtin/debt-maturities/headless.ts +4 -3
  245. package/src/plugins/builtin/debt-maturities/model.ts +17 -1
  246. package/src/plugins/builtin/debt-maturities/pane.tsx +18 -4
  247. package/src/plugins/builtin/debt-maturities/series.ts +3 -3
  248. package/src/plugins/builtin/debug/index.tsx +1 -1
  249. package/src/plugins/builtin/dividend-yield/client.ts +32 -2
  250. package/src/plugins/builtin/dividend-yield/headless.ts +58 -33
  251. package/src/plugins/builtin/dividend-yield/index.tsx +37 -2
  252. package/src/plugins/builtin/dividend-yield/pane.tsx +32 -13
  253. package/src/plugins/builtin/dividend-yield/ranking-reads.ts +73 -0
  254. package/src/plugins/builtin/dividend-yield/ranking-view.tsx +163 -0
  255. package/src/plugins/builtin/dividend-yield/ranking.ts +1 -1
  256. package/src/plugins/builtin/dividend-yield/view.ts +21 -0
  257. package/src/plugins/builtin/docs/index.tsx +2 -3
  258. package/src/plugins/builtin/doe/client.ts +6 -5
  259. package/src/plugins/builtin/doe/headless.ts +16 -3
  260. package/src/plugins/builtin/doe/index.tsx +5 -4
  261. package/src/plugins/builtin/doe/prices.ts +2 -3
  262. package/src/plugins/builtin/doe/series.ts +4 -4
  263. package/src/plugins/builtin/earnings/board.tsx +2 -1
  264. package/src/plugins/builtin/earnings/history.tsx +2 -1
  265. package/src/plugins/builtin/earnings-calls/calendar.tsx +10 -2
  266. package/src/plugins/builtin/earnings-calls/data.ts +19 -11
  267. package/src/plugins/builtin/earnings-calls/pane.tsx +27 -14
  268. package/src/plugins/builtin/earnings-ripple/client.ts +32 -7
  269. package/src/plugins/builtin/earnings-ripple/index.tsx +1 -2
  270. package/src/plugins/builtin/econ/calendar-model.ts +29 -6
  271. package/src/plugins/builtin/econ/detail-view.tsx +16 -2
  272. package/src/plugins/builtin/econ-statistics/index.tsx +1 -2
  273. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  274. package/src/plugins/builtin/equity-screener/headless.ts +1 -0
  275. package/src/plugins/builtin/equity-screener/ideas.ts +30 -1
  276. package/src/plugins/builtin/equity-screener/index.tsx +1 -2
  277. package/src/plugins/builtin/equity-screener/model.ts +28 -4
  278. package/src/plugins/builtin/equity-screener/pane.tsx +5 -0
  279. package/src/plugins/builtin/equity-screener/screen-check.ts +23 -1
  280. package/src/plugins/builtin/estimate-revisions/index.tsx +1 -2
  281. package/src/plugins/builtin/executives/data.ts +6 -1
  282. package/src/plugins/builtin/executives/pane.tsx +9 -21
  283. package/src/plugins/builtin/exposure/holdings.ts +35 -13
  284. package/src/plugins/builtin/exposure/index.tsx +1 -2
  285. package/src/plugins/builtin/fear-greed/cache.ts +4 -0
  286. package/src/plugins/builtin/fear-greed/cli.ts +44 -0
  287. package/src/plugins/builtin/fear-greed/index.tsx +5 -53
  288. package/src/plugins/builtin/fear-greed/pane.tsx +2 -1
  289. package/src/plugins/builtin/fear-greed/series.ts +6 -4
  290. package/src/plugins/builtin/fixed-income/index.tsx +1 -2
  291. package/src/plugins/builtin/futures/board-reads.ts +25 -0
  292. package/src/plugins/builtin/futures/coalesced-bump.ts +44 -0
  293. package/src/plugins/builtin/futures/index.tsx +15 -0
  294. package/src/plugins/builtin/futures/model.ts +14 -0
  295. package/src/plugins/builtin/futures/use-board-terms.ts +22 -15
  296. package/src/plugins/builtin/futures/use-front-returns.ts +20 -14
  297. package/src/plugins/builtin/futures-curve/headless.ts +6 -1
  298. package/src/plugins/builtin/futures-curve/index.tsx +1 -2
  299. package/src/plugins/builtin/fx-matrix/index.tsx +2 -2
  300. package/src/plugins/builtin/gpu/board.tsx +51 -12
  301. package/src/plugins/builtin/gpu/client.ts +19 -3
  302. package/src/plugins/builtin/gpu/equities.tsx +5 -2
  303. package/src/plugins/builtin/gpu/headless.ts +14 -2
  304. package/src/plugins/builtin/gpu/index.tsx +1 -2
  305. package/src/plugins/builtin/gpu/model.ts +20 -2
  306. package/src/plugins/builtin/gpu/pane.tsx +1 -1
  307. package/src/plugins/builtin/gpu/series.ts +2 -3
  308. package/src/plugins/builtin/help/index.tsx +1 -1
  309. package/src/plugins/builtin/holders/beneficial-view.tsx +1 -2
  310. package/src/plugins/builtin/holders/index-holders.ts +24 -12
  311. package/src/plugins/builtin/holders/index.tsx +2 -3
  312. package/src/plugins/builtin/ipo-calendar/index.tsx +1 -2
  313. package/src/plugins/builtin/iv-history/index.tsx +1 -2
  314. package/src/plugins/builtin/jodi/client.ts +4 -1
  315. package/src/plugins/builtin/jodi/index.tsx +1 -2
  316. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -2
  317. package/src/plugins/builtin/layout-manager/index.tsx +9 -8
  318. package/src/plugins/builtin/macro-day/pane.tsx +4 -2
  319. package/src/plugins/builtin/market-halts/index.tsx +1 -2
  320. package/src/plugins/builtin/market-heatmap/index.tsx +11 -2
  321. package/src/plugins/builtin/market-movers/client.ts +1 -0
  322. package/src/plugins/builtin/market-movers/index.tsx +16 -5
  323. package/src/plugins/builtin/market-movers/screener.ts +23 -6
  324. package/src/plugins/builtin/market-movers/session-body.tsx +6 -2
  325. package/src/plugins/builtin/market-valuation/index.tsx +1 -2
  326. package/src/plugins/builtin/members/index.tsx +1 -2
  327. package/src/plugins/builtin/money-markets/index.tsx +1 -2
  328. package/src/plugins/builtin/money-markets/pane.tsx +2 -1
  329. package/src/plugins/builtin/money-markets/series.ts +3 -5
  330. package/src/plugins/builtin/news/headless.ts +7 -0
  331. package/src/plugins/builtin/news/wire/index.tsx +1 -1
  332. package/src/plugins/builtin/news/wire/news/pop-out.tsx +9 -3
  333. package/src/plugins/builtin/notes/index.tsx +3 -6
  334. package/src/plugins/builtin/openfda/index.tsx +1 -2
  335. package/src/plugins/builtin/options/view.tsx +6 -5
  336. package/src/plugins/builtin/options-calculator/index.tsx +1 -2
  337. package/src/plugins/builtin/options-positioning/index.tsx +1 -2
  338. package/src/plugins/builtin/options-scenario/index.tsx +2 -4
  339. package/src/plugins/builtin/perps/index.tsx +1 -2
  340. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -3
  341. package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -1
  342. package/src/plugins/builtin/plugin-module.ts +16 -2
  343. package/src/plugins/builtin/portfolio-list/cli/render.ts +53 -3
  344. package/src/plugins/builtin/portfolio-list/index.tsx +1 -2
  345. package/src/plugins/builtin/portfolio-list/pane/index.tsx +11 -0
  346. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +2 -2
  347. package/src/plugins/builtin/power/gas-client.ts +2 -1
  348. package/src/plugins/builtin/power/index.tsx +1 -2
  349. package/src/plugins/builtin/power/prices-client.ts +3 -2
  350. package/src/plugins/builtin/rate-path/index.tsx +1 -2
  351. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  352. package/src/plugins/builtin/realized-vol/index.tsx +1 -2
  353. package/src/plugins/builtin/realized-vol/model.ts +16 -2
  354. package/src/plugins/builtin/realized-vol/pane.tsx +3 -3
  355. package/src/plugins/builtin/relative-rotation/index.tsx +1 -2
  356. package/src/plugins/builtin/research/analyst-headless.ts +33 -6
  357. package/src/plugins/builtin/research/analyst-model.ts +37 -2
  358. package/src/plugins/builtin/research/analyst-pane.tsx +25 -5
  359. package/src/plugins/builtin/research/index.tsx +5 -8
  360. package/src/plugins/builtin/research-search/index.tsx +1 -2
  361. package/src/plugins/builtin/revenue-breakdown/headless.ts +22 -8
  362. package/src/plugins/builtin/revenue-breakdown/model.ts +23 -0
  363. package/src/plugins/builtin/reverse-dcf/pane.tsx +4 -2
  364. package/src/plugins/builtin/risk-factors/data.ts +8 -4
  365. package/src/plugins/builtin/risk-factors/index.tsx +3 -1
  366. package/src/plugins/builtin/risk-factors/pane.tsx +5 -8
  367. package/src/plugins/builtin/roadmap/index.tsx +2 -3
  368. package/src/plugins/builtin/roadmap/pane.tsx +49 -2
  369. package/src/plugins/builtin/scanner/hilo-model.ts +18 -1
  370. package/src/plugins/builtin/scanner/hilo-pane.tsx +12 -2
  371. package/src/plugins/builtin/scanner/live-pane.tsx +7 -4
  372. package/src/plugins/builtin/scanner/lock-note.ts +26 -3
  373. package/src/plugins/builtin/sec/filing-content.ts +38 -6
  374. package/src/plugins/builtin/sec/headless.ts +5 -1
  375. package/src/plugins/builtin/sec/index.tsx +4 -2
  376. package/src/plugins/builtin/sec/model.ts +2 -0
  377. package/src/plugins/builtin/sectors/index.tsx +11 -5
  378. package/src/plugins/builtin/series-inventory.ts +2 -0
  379. package/src/plugins/builtin/shared/cloud-resource.ts +73 -8
  380. package/src/plugins/builtin/shared/cloud-upgrade.ts +24 -1
  381. package/src/plugins/builtin/shared/forming-bars.ts +29 -0
  382. package/src/plugins/builtin/shared/fred-points.ts +6 -1
  383. package/src/plugins/builtin/shared/fx-rate-freshness.ts +17 -0
  384. package/src/plugins/builtin/shared/pro-lock-note.ts +23 -8
  385. package/src/plugins/builtin/shared/series-detail-chart.tsx +4 -2
  386. package/src/plugins/builtin/shared/series-warning.ts +13 -4
  387. package/src/plugins/builtin/shared/use-quote-board.ts +5 -0
  388. package/src/plugins/builtin/short-interest/publications-pane.tsx +2 -1
  389. package/src/plugins/builtin/short-volume/pane.tsx +2 -1
  390. package/src/plugins/builtin/short-volume/series.ts +2 -3
  391. package/src/plugins/builtin/social-mentions/client.ts +15 -10
  392. package/src/plugins/builtin/social-mentions/index.tsx +1 -2
  393. package/src/plugins/builtin/social-mentions/pane.tsx +2 -1
  394. package/src/plugins/builtin/social-mentions/series.ts +2 -3
  395. package/src/plugins/builtin/supply-chain/client.ts +22 -1
  396. package/src/plugins/builtin/supply-chain/graph-client.ts +22 -1
  397. package/src/plugins/builtin/supply-chain/index.tsx +1 -2
  398. package/src/plugins/builtin/thirteenf/api.ts +127 -29
  399. package/src/plugins/builtin/thirteenf/data.ts +165 -35
  400. package/src/plugins/builtin/thirteenf/index.tsx +2 -4
  401. package/src/plugins/builtin/thirteenf/model.ts +2 -0
  402. package/src/plugins/builtin/thirteenf/overlap-pane.tsx +5 -1
  403. package/src/plugins/builtin/thirteenf/pane.tsx +41 -16
  404. package/src/plugins/builtin/thirteenf/types.ts +14 -0
  405. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +22 -6
  406. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +81 -11
  407. package/src/plugins/builtin/ticker-detail/financials/model.ts +35 -2
  408. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +53 -18
  409. package/src/plugins/builtin/ticker-detail/headless.ts +70 -11
  410. package/src/plugins/builtin/ticker-detail/index.tsx +5 -10
  411. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +16 -6
  412. package/src/plugins/builtin/time-sales/index.tsx +1 -2
  413. package/src/plugins/builtin/time-sales/pane.tsx +3 -1
  414. package/src/plugins/builtin/trace-bonds/headless.ts +11 -2
  415. package/src/plugins/builtin/trace-bonds/model.ts +44 -10
  416. package/src/plugins/builtin/trace-bonds/pane.tsx +12 -7
  417. package/src/plugins/builtin/treasury-auctions/cache.ts +20 -8
  418. package/src/plugins/builtin/treasury-auctions/client.ts +3 -3
  419. package/src/plugins/builtin/treasury-auctions/headless.ts +3 -1
  420. package/src/plugins/builtin/treasury-auctions/pane.tsx +10 -4
  421. package/src/plugins/builtin/vol-surface/client.ts +18 -3
  422. package/src/plugins/builtin/vol-surface/index.tsx +1 -2
  423. package/src/plugins/builtin/volatility/client.ts +43 -16
  424. package/src/plugins/builtin/volatility/headless.ts +5 -2
  425. package/src/plugins/builtin/volatility/index.tsx +1 -2
  426. package/src/plugins/builtin/volatility/model.ts +17 -6
  427. package/src/plugins/builtin/volatility/pane.tsx +6 -2
  428. package/src/plugins/builtin/volatility/series.ts +2 -1
  429. package/src/plugins/builtin/world-indices/footer.ts +6 -1
  430. package/src/plugins/builtin/world-venue-map/client.ts +10 -1
  431. package/src/plugins/builtin/world-venue-map/index.ts +6 -2
  432. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +48 -7
  433. package/src/plugins/builtin/world-venue-map/layers.ts +17 -0
  434. package/src/plugins/builtin/world-venue-map/local-layers.ts +141 -0
  435. package/src/plugins/builtin/world-venue-map/map-symbols.ts +18 -0
  436. package/src/plugins/builtin/world-venue-map/map.tsx +4 -2
  437. package/src/plugins/builtin/world-venue-map/scope.ts +63 -0
  438. package/src/plugins/builtin/yield-curve/client.ts +4 -6
  439. package/src/plugins/builtin/yield-curve/curves.ts +16 -2
  440. package/src/plugins/builtin/yield-curve/headless.ts +3 -3
  441. package/src/plugins/bundle.ts +52 -2
  442. package/src/plugins/catalog-browser.ts +2 -2
  443. package/src/plugins/catalog-cli.ts +20 -0
  444. package/src/plugins/host-link.ts +13 -1
  445. package/src/plugins/installer.ts +64 -42
  446. package/src/plugins/loader.ts +43 -11
  447. package/src/plugins/peer-imports.ts +201 -0
  448. package/src/plugins/peer-retry.ts +26 -29
  449. package/src/plugins/plugin-process.ts +128 -0
  450. package/src/plugins/redirection-guard.ts +45 -0
  451. package/src/remote/endpoint.ts +16 -3
  452. package/src/renderers/browser/main.tsx +3 -2
  453. package/src/renderers/cloudflare/http-proxy.ts +7 -123
  454. package/src/renderers/dom/desktop/icons.tsx +5 -2
  455. package/src/renderers/dom/host/image-surface.tsx +46 -21
  456. package/src/renderers/dom/styles.css +7 -0
  457. package/src/renderers/dom/toast-host.tsx +2 -0
  458. package/src/renderers/electrobun/bun/desktop/backend.ts +7 -1
  459. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +23 -1
  460. package/src/renderers/electrobun/bun/desktop/initialization.ts +12 -3
  461. package/src/renderers/electrobun/bun/desktop/rpc-timing.ts +88 -0
  462. package/src/renderers/electrobun/bun/desktop/update.ts +20 -4
  463. package/src/renderers/electrobun/bun/desktop/view-ids.ts +8 -4
  464. package/src/renderers/electrobun/bun/external-plugins.ts +4 -0
  465. package/src/renderers/electrobun/bun/window/attention.ts +54 -0
  466. package/src/renderers/electrobun/bun/window/create.ts +6 -2
  467. package/src/renderers/electrobun/bun/window/native-timing.ts +48 -0
  468. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +82 -4
  469. package/src/renderers/electrobun/view/cli-pane-shot-frame.tsx +13 -5
  470. package/src/renderers/electrobun/view/desktop/window/snapshot.ts +3 -0
  471. package/src/renderers/electrobun/view/main.tsx +7 -2
  472. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -0
  473. package/src/renderers/electrobun/view/ui-host.tsx +9 -2
  474. package/src/renderers/opentui/browser-open.ts +10 -4
  475. package/src/renderers/opentui/image/surface.tsx +1 -1
  476. package/src/renderers/opentui/start.tsx +12 -6
  477. package/src/renderers/opentui/toast-host.tsx +2 -1
  478. package/src/series/registry.ts +18 -0
  479. package/src/series/resolve.ts +2 -1
  480. package/src/series/standalone.ts +10 -1
  481. package/src/series/types.ts +12 -0
  482. package/src/series/validate.ts +11 -2
  483. package/src/sources/gloomberb-cloud/fred-series.ts +5 -3
  484. package/src/sources/gloomberb-cloud/index.ts +28 -5
  485. package/src/sources/gloomberb-cloud/news.ts +24 -3
  486. package/src/sources/gloomberb-cloud/normalizers.ts +17 -2
  487. package/src/sources/provider-errors.ts +26 -3
  488. package/src/sources/provider-router/history.ts +5 -0
  489. package/src/sources/provider-router/index.ts +13 -0
  490. package/src/sync/core-contributors.ts +32 -0
  491. package/src/telemetry/attention-counts.ts +64 -6
  492. package/src/telemetry/attention-setting.ts +33 -0
  493. package/src/telemetry/crash-reports-node.ts +3 -1
  494. package/src/telemetry/stall-clocks.ts +93 -0
  495. package/src/telemetry/stall-reports-node.ts +25 -9
  496. package/src/telemetry/stall-reports.ts +218 -13
  497. package/src/test-support/outage.ts +8 -0
  498. package/src/test-support/preload.ts +9 -0
  499. package/src/tickers/company-name-listing.ts +59 -29
  500. package/src/tickers/company-name.ts +84 -1
  501. package/src/tickers/listing-fate.ts +14 -3
  502. package/src/tickers/saved-instrument.ts +17 -0
  503. package/src/tickers/search/home-listing.ts +104 -8
  504. package/src/tickers/search/index.ts +5 -2
  505. package/src/tickers/search/types.ts +6 -1
  506. package/src/time-series/bar-aggregation.ts +7 -1
  507. package/src/time-series/base-series.ts +3 -3
  508. package/src/time-series/chart-bounds.ts +1 -1
  509. package/src/time-series/chart-data.ts +5 -2
  510. package/src/time-series/chart-seed.ts +1 -1
  511. package/src/time-series/coin-session-close.ts +211 -0
  512. package/src/time-series/live-bars.ts +13 -2
  513. package/src/time-series/live-quotes.ts +1 -1
  514. package/src/time-series/market.ts +5 -3
  515. package/src/time-series/official-close.ts +159 -0
  516. package/src/time-series/price-comparison.ts +75 -8
  517. package/src/time-series/resolve.ts +32 -4
  518. package/src/time-series/series-presentation.ts +1 -1
  519. package/src/time-series/studies/formula.ts +205 -0
  520. package/src/time-series/studies/indicators.ts +299 -0
  521. package/src/time-series/studies/leverage.ts +75 -0
  522. package/src/time-series/studies/pair.ts +138 -0
  523. package/src/time-series/studies/resolve.ts +333 -0
  524. package/src/time-series/studies/shared.ts +141 -0
  525. package/src/time-series/studies/units.ts +243 -0
  526. package/src/time-series/studies/vwap.ts +274 -0
  527. package/src/time-series/types.ts +7 -0
  528. package/src/types/config.ts +18 -2
  529. package/src/types/data-provider.ts +23 -0
  530. package/src/types/financials.ts +40 -3
  531. package/src/types/headless.ts +42 -3
  532. package/src/types/plugin.ts +32 -3
  533. package/src/ui/host.tsx +17 -1
  534. package/src/ui/toast.tsx +26 -1
  535. package/src/utils/ascii-text.ts +140 -0
  536. package/src/utils/cli-output.ts +91 -18
  537. package/src/utils/concurrency.ts +24 -0
  538. package/src/utils/exchanges.ts +6 -0
  539. package/src/utils/external-url.ts +20 -7
  540. package/src/utils/financial-statements.ts +73 -16
  541. package/src/utils/fundamentals.ts +17 -5
  542. package/src/utils/futures-generic.ts +4 -4
  543. package/src/utils/http-proxy-upstream.ts +124 -0
  544. package/src/utils/lease-liabilities.ts +54 -0
  545. package/src/utils/main-thread-stall.ts +122 -5
  546. package/src/utils/perf-marks.ts +58 -12
  547. package/src/utils/price-history-integrity.ts +47 -1
  548. package/src/utils/private-files.ts +141 -0
  549. package/src/utils/public-address.ts +99 -0
  550. package/src/account/upgrade-personalization.ts +0 -57
  551. package/src/account/upgrade-tickers.ts +0 -124
  552. package/src/api-client/upgrade-experiment.ts +0 -91
  553. package/src/plugins/builtin/cloud/browser.tsx +0 -3
  554. package/src/time-series/studies.ts +0 -1628
@@ -3,6 +3,7 @@ import { isSubMinuteInterval, subMinuteDataMessage } from "../../../cli/known-ga
3
3
  import { CHART_BAR_SIZE_PRESETS, parseChartBarSize } from "../../../time-series/resolution";
4
4
  import { FINANCIAL_PERIOD_OPTION, FINANCIAL_PERIOD_COUNT_OPTION, HISTORY_RANGE_VALUES, RETURN_RANGE_VALUES } from "../shared/headless-options";
5
5
  import { DATE_OPTION_EXAMPLES } from "../../../utils/calendar-date";
6
+ import { PRICE_WINDOW_FROM_HELP, PRICE_WINDOW_TO_HELP } from "../../../utils/price-window";
6
7
 
7
8
  type PaneSchema = Pick<HeadlessPaneDefinition, "argument" | "options" | "discovery">;
8
9
 
@@ -51,6 +52,11 @@ export const paneSchemas = {
51
52
  values: [{ value: "auto" }, ...BAR_SIZES],
52
53
  normalize: (value) => normalizeBarSize(value, true),
53
54
  },
55
+ {
56
+ key: "exchange",
57
+ description: "Exchange for every ticker that names none: fn G BDI --exchange TSX is fn G BDI:TSX.",
58
+ type: "string",
59
+ },
54
60
  ],
55
61
  discovery: {
56
62
  id: "chart-composer",
@@ -138,6 +144,10 @@ export const paneSchemas = {
138
144
  ],
139
145
  defaultValue: "percent",
140
146
  },
147
+ // A window of dates, measured from the close before it as RETURN is: --from 2026-10-19 compares since the 16th's close.
148
+ { key: "from", type: "string", placeholder: "year|date", description: `${PRICE_WINDOW_FROM_HELP}; the returns are measured from the last close the tickers share before it, as RETURN measures`,
149
+ example: "--from 2019-12-31", replaces: ["rangePreset"] },
150
+ { key: "to", type: "string", placeholder: "year|date", description: PRICE_WINDOW_TO_HELP },
141
151
  {
142
152
  key: "totalReturn",
143
153
  description: "Reinvest each cash distribution at its ex-date close, so the returns are total returns, not price returns.",
@@ -31,9 +31,12 @@ import type { TickerFinancials, PricePoint, Quote } from "../../../types/financi
31
31
  import { chartQuoteOverrideKeyForTarget, getLiveChartQuoteTargets } from "../../../time-series/live-quotes";
32
32
  import { createChartSeriesResolver } from "../../../capabilities";
33
33
  import { loadFuturesContractTerms, loadFuturesPriceUnit } from "../../../market-data/futures-contract";
34
- import { isUsListingExchange, parsePublicTickerKey, publicTickerKey } from "../../../utils/exchanges";
34
+ import { canonicalExchange, isKnownExchangeCode, isUsListingExchange, parsePublicTickerKey, publicTickerKey } from "../../../utils/exchanges";
35
35
  import { zonedDateTimeParts } from "../../../utils/zoned-date-time";
36
36
  import { futuresSessionOpenMinute } from "../../../market-data/market/trading-sessions";
37
+ import { isAlwaysOpenExchange, isRegularSessionTime } from "../../../market-data/market/freshness";
38
+ import { isRoundTheClockCoin } from "../../../utils/crypto-pair";
39
+ import { isCurrencyPairListing } from "../../../utils/fx-market-hours";
37
40
  import { cleanFloat32Price } from "../../../cli/history-rows";
38
41
  import { parseChartSpec } from "./chart-spec";
39
42
  import { paneSchemas } from "./headless-schema";
@@ -46,7 +49,8 @@ import { loadFundFacts } from "../sec/fund-facts";
46
49
  import { comparedFundTickers, loadDailyObjectives, mayDrawDailyObjective, withLeverageOverlay } from "./leverage-overlay";
47
50
  import { classifyInstrumentType } from "../../../tickers/instrument-kind";
48
51
  import { priceComparisonEndsIntraday, type PriceComparison } from "../../../time-series/price-comparison";
49
- import { LATEST_INTRADAY_TEXT } from "../../../market-data/market/period-return";
52
+ import { LATEST_INTRADAY_TEXT, periodDayText } from "../../../market-data/market/period-return";
53
+ import { CLOSING_PRINT_PENDING_TEXT, newestClosedSession, newestSessionClose, withOfficialCloses } from "../../../time-series/official-close";
50
54
 
51
55
  export interface ChartPaneModel extends HeadlessSeriesResult {
52
56
  /** The renderer consumes the full model; generic reports project series and metadata. */
@@ -368,10 +372,19 @@ function chartFreshness(
368
372
  const coarsest = resolutions.reduce((worst, resolution) => (
369
373
  order.indexOf(resolution!) > order.indexOf(worst!) ? resolution : worst
370
374
  ), resolutions[0]);
375
+ const closedDay = closedPriceBarDay(series);
376
+ // A window that ended before today (`--to 2026-10-23`) is the history asked for, which no later close makes stale:
377
+ // `daily closes through Fri 23 Oct`.
378
+ const windowEnd = spec.viewport.dateWindow?.end;
379
+ if (windowEnd && /^\d{4}-\d{2}-\d{2}$/.test(windowEnd) && windowEnd < new Date().toISOString().slice(0, 10)) {
380
+ const through = closedDay ?? series.flatMap((entry) => entry.points.at(-1)?.date ?? []).map((date) => new Date(date).toISOString().slice(0, 10)).sort().at(-1);
381
+ const basis = coarsest === "1d" ? "daily closes" : coarsest === "1wk" ? "weekly closes" : coarsest === "1mo" ? "monthly closes" : "intraday bars";
382
+ return { status: "not-a-feed", basis: through ? `${basis} through ${periodDayText(through, new Date().getUTCFullYear())}` : basis,
383
+ ...(through && closedDay ? { asOf: through } : {}), oldest: null };
384
+ }
371
385
  const forming = formingPriceBar(series);
372
386
  // A history: its first bars are not stale data, so the line dates it by the bar still forming alone.
373
387
  if (forming) return { ...barHistoryFreshness(coarsest), periodToDate: forming, oldest: null };
374
- const closedDay = closedPriceBarDay(series);
375
388
  // A comparison whose newest point is a session still trading is not a day's closes.
376
389
  const basis = coarsest === "1d" && priceComparisonEndsIntraday(comparison) ? { basis: LATEST_INTRADAY_TEXT } : {};
377
390
  // Daily bars are dated by the time of the newest point: a closed session's day, or when a session still trading was last quoted.
@@ -600,30 +613,34 @@ export function argumentWithListing(argument: string, symbol: string, key: strin
600
613
  }
601
614
 
602
615
  /**
603
- * A FRED series id typed where a ticker goes (`fn G DFII10`), as the chart of
604
- * FRED:DFII10: the catalog lists it, no listing answers to it, and the quote
605
- * source says it is not a ticker. A real ticker of the same letters, read as
606
- * a listing or quoted, still charts as the ticker; null then, and for any
607
- * other series.
616
+ * A catalog series typed where a ticker goes, as its chart: a FRED series id
617
+ * (`fn G DFII10` is FRED:DFII10), or a word a series lists as an alias
618
+ * (`fn G hashrate` is CAP:bitcoin:hashrate), when the catalog lists it, no
619
+ * listing answers to it, and the quote source says it is not a ticker. A
620
+ * real ticker of the same letters, read as a listing or quoted, still charts
621
+ * as the ticker; null then, and for any other symbol.
608
622
  */
609
- export async function bareFredSeries(
623
+ export async function bareCatalogSeries(
610
624
  series: ChartSeriesSpec,
611
625
  readListing: () => Promise<unknown>,
612
- context: Pick<HeadlessPaneContext, "marketData" | "catalogSeriesName">,
626
+ context: Pick<HeadlessPaneContext, "marketData" | "catalogSeriesName" | "catalogSeriesNamed">,
613
627
  ): Promise<ChartSeriesSpec | null> {
614
628
  const { source } = series;
615
629
  if (source.kind !== "security" || source.instrument.exchange || source.instrument.instrument || source.fieldId !== CHART_FIELD_IDS.price) return null;
616
630
  const id = source.instrument.symbol.trim().toUpperCase();
617
631
  const expression = `FRED:${id}`;
618
- const label = /^[A-Z0-9]+$/.test(id)
632
+ const fredLabel = /^[A-Z0-9]+$/.test(id)
619
633
  ? context.catalogSeriesName?.(expression) ?? getSharedRegistry()?.series?.findByExpression(expression)?.definition.name
620
634
  : undefined;
635
+ const named = fredLabel ? undefined : context.catalogSeriesNamed?.(id);
636
+ const parsed = named ? parseSeriesExpression(named.expression) : null;
637
+ const label = fredLabel ?? (parsed ? named?.name : undefined);
621
638
  if (!label || await readListing()) return null;
622
639
  const failure = await Promise.resolve().then(() => context.marketData.getQuote(id, ""))
623
640
  .then((quote) => quote ? null : `Not a ticker: ${id}.`, (error: unknown) => error instanceof Error ? error.message : String(error));
624
641
  if (!failure || !notATickerSymbol(failure)) return null;
625
- const fred = buildSeriesSpec({ kind: "economic", provider: "fred", seriesId: id, label }, 0);
626
- return { ...fred, id: series.id, transform: series.transform, axis: series.axis, panelId: series.panelId };
642
+ const spec = parsed ? buildSeriesSpec({ ...parsed, label }, 0) : buildSeriesSpec({ kind: "economic", provider: "fred", seriesId: id, label }, 0);
643
+ return { ...spec, id: series.id, transform: series.transform, axis: series.axis, panelId: series.panelId };
627
644
  }
628
645
 
629
646
  /**
@@ -646,14 +663,28 @@ async function possibleFundTickers(spec: ChartSpec, context: HeadlessPaneContext
646
663
  });
647
664
  }
648
665
 
666
+ /**
667
+ * `--exchange TSX`: every ticker series that names no venue, on that one, as
668
+ * `BDI:TSX` would be. A code the app does not know fails before any request.
669
+ */
670
+ function onExchange(spec: ChartSpec, exchange: string): ChartSpec {
671
+ const code = exchange.trim().toUpperCase();
672
+ if (!isKnownExchangeCode(code)) throw new Error(`Unknown exchange "${exchange.trim()}". Write a venue code such as TSX, LSE or XETRA, or name it in the series: BDI:TSX.`);
673
+ return { ...spec, series: spec.series.map((series) => series.source.kind !== "security" || series.source.instrument.exchange || series.source.instrument.instrument
674
+ ? series
675
+ : { ...series, source: { ...series.source, instrument: { ...series.source.instrument, exchange: canonicalExchange(code) } } }) };
676
+ }
677
+
649
678
  export function chartHeadless(template: keyof typeof paneSchemas): HeadlessPaneDefinition<"series"> {
650
679
  return {
651
680
  ...paneSchemas[template],
652
681
  shape: "series",
653
682
  async load(args, context) {
654
683
  const intradayHistories: ChartPaneModel["snapshot"]["intradayHistories"] = [];
655
- const saved = parseChartSpec(context.settings?.chartSpec);
656
- if (!saved) throw new Error("The chart specification is invalid.");
684
+ const closingPrintsPending: string[] = [];
685
+ const stored = parseChartSpec(context.settings?.chartSpec);
686
+ if (!stored) throw new Error("The chart specification is invalid.");
687
+ const saved = typeof args.options.exchange === "string" ? onExchange(stored, args.options.exchange) : stored;
657
688
  // A leveraged or inverse fund compared with anything draws its daily objective beside it.
658
689
  const parsed = template === "comparison-chart-pane" && mayDrawDailyObjective(saved)
659
690
  ? withLeverageOverlay(saved, await loadDailyObjectives(
@@ -680,9 +711,9 @@ export function chartHeadless(template: keyof typeof paneSchemas): HeadlessPaneD
680
711
  series: await Promise.all(parsed.series.map(async (series) => {
681
712
  if (series.source.kind !== "security" || series.source.instrument.exchange) return series;
682
713
  const { symbol } = series.source.instrument;
683
- // A FRED id no ticker answers to (DFII10) is its FRED series; an intraday chart has none.
684
- const fred = template === "graph-intraday-price-pane" ? null : await bareFredSeries(series, () => readListing(symbol), context);
685
- if (fred) return fred;
714
+ // A FRED id (DFII10) or a series alias (hashrate) no ticker answers to is that series; an intraday chart has none.
715
+ const catalog = template === "graph-intraday-price-pane" ? null : await bareCatalogSeries(series, () => readListing(symbol), context);
716
+ if (catalog) return catalog;
686
717
  if (!reads.has(symbol)) reads.set(symbol, readSeriesListing(symbol));
687
718
  const instrument = await reads.get(symbol)!;
688
719
  return instrument ? { ...series, source: { ...series.source, instrument: { ...series.source.instrument, ...instrument } } } : series;
@@ -709,6 +740,37 @@ export function chartHeadless(template: keyof typeof paneSchemas): HeadlessPaneD
709
740
  ...await loadIntradayWindow({ provider: context.marketData, symbol: target.symbol, exchange: target.exchange ?? "", request, context: { brokerId: target.brokerId, brokerInstanceId: target.brokerInstanceId, instrument: target.instrument } }),
710
741
  })));
711
742
  context.signal.throwIfAborted();
743
+ // Once a session has closed, its last bar ends on the official close (the closing auction's print), as
744
+ // `history` and CMP give it, not on the last trade before it; until the quote has that close, the footer says so.
745
+ await Promise.all(histories.map(async (history) => {
746
+ // A future settles rather than closing on an auction print, and a coin or a currency pair has no session
747
+ // to close: their bars stay as traded, and no quote is read for them.
748
+ if (!history.points.length || futuresSessionOpenMinute(history.symbol, history.exchange) != null
749
+ || isRoundTheClockCoin(history.symbol, history.exchange) || isCurrencyPairListing(history.symbol, history.exchange)) return;
750
+ const now = Date.now();
751
+ const known = history.exchange || history.quote?.listingExchangeName || history.quote?.exchangeName || undefined;
752
+ if (known && isAlwaysOpenExchange(known)) return;
753
+ // While the session trades, no session of the chart has closed since its bars were read.
754
+ if (history.quote?.marketState === "REGULAR" || (known && isRegularSessionTime(known, now) === true)) return;
755
+ if (known && !newestClosedSession(history.points, { venue: known, now })) return;
756
+ // A captured shot's source can have no quotes: its bars are drawn as captured.
757
+ const quote = history.quote ?? (typeof context.marketData.getQuote === "function"
758
+ ? await Promise.resolve(context.marketData.getQuote(history.symbol, history.exchange,
759
+ { brokerId: history.target.brokerId, brokerInstanceId: history.target.brokerInstanceId, instrument: history.target.instrument }))
760
+ .catch(logFailure("chart-composer.headless", "Read the official close of an intraday chart failed", null))
761
+ : null);
762
+ const venue = known || quote?.listingExchangeName || quote?.exchangeName || undefined;
763
+ const newest = newestClosedSession(history.points, { venue, now });
764
+ const decided = newest ? newestSessionClose(quote, { ...newest, venue }) : null;
765
+ if (!newest || !decided) return;
766
+ if ("pending" in decided) {
767
+ closingPrintsPending.push(history.symbol);
768
+ return;
769
+ }
770
+ const closes = new Map([[newest.date, decided.close]]);
771
+ history.points = withOfficialCloses(history.points, closes, { venue, now });
772
+ history.bufferedPoints = withOfficialCloses(history.bufferedPoints, closes, { venue, now });
773
+ }));
712
774
  intradayHistories.push(...histories);
713
775
  const starts = histories.flatMap(({ start }) => start ? [start.getTime()] : []);
714
776
  const ends = histories.flatMap(({ end }) => end ? [end.getTime()] : []);
@@ -725,9 +787,11 @@ export function chartHeadless(template: keyof typeof paneSchemas): HeadlessPaneD
725
787
  }, context.marketData) };
726
788
  }
727
789
  const model = await loadChartPaneModel(spec, context);
728
- // A bare coin ticker charted as another listing (ETH, the Grayscale fund) says where the coin is, as `quote` does.
729
- const coinNotes = (await Promise.all(listings.values())).flatMap((read) => read?.coinNote ?? []);
730
- if (coinNotes.length) model.warnings = [...model.warnings ?? [], ...coinNotes];
790
+ // What became of the company that had a bare symbol (GOGL), and a bare coin ticker charted as another listing
791
+ // (ETH, the Grayscale fund), are said as `quote` says them.
792
+ const readListings = await Promise.all(listings.values());
793
+ const listingNotes = [...readListings.flatMap((read) => read?.successorNote ?? []), ...readListings.flatMap((read) => read?.coinNote ?? [])];
794
+ if (listingNotes.length) model.warnings = [...model.warnings ?? [], ...listingNotes];
731
795
  if (template === "graph-intraday-price-pane" && !intradayHistories.length && model.chart.viewport) {
732
796
  const { start, end } = model.chart.viewport;
733
797
  for (const { target, data } of [
@@ -782,6 +846,9 @@ export function chartHeadless(template: keyof typeof paneSchemas): HeadlessPaneD
782
846
  model.metadata = { ...model.metadata, resolution: dropped.drop };
783
847
  }
784
848
  model.freshness = chartFreshness(model.series, model.spec, model.chart.priceComparison, chartQuoteTimes(model));
849
+ if (closingPrintsPending.length) {
850
+ model.freshness = { ...model.freshness, note: [model.freshness.note, CLOSING_PRINT_PENDING_TEXT].filter(Boolean).join(", ") };
851
+ }
785
852
  const priceDomainFailures = intradayHistories.flatMap(({ symbol, exchange, priceDomainFailure }) =>
786
853
  priceDomainFailure ? [{ symbol, exchange, ...priceDomainFailure }] : []);
787
854
  if (priceDomainFailures.length) {
@@ -11,7 +11,6 @@ import { canonicalExchange, parsePublicTickerKey, publicTickerKey } from "../../
11
11
  import { tickerInstrumentLabel } from "../../../tickers/instrument-label";
12
12
  import type { ChartSpec } from "../../../time-series/types";
13
13
  import { ChartComposerPane, ChartComposerResearchTab } from "./pane";
14
- import { DataCatalogPane } from "./data-catalog-pane";
15
14
  import { listFieldSeries } from "./field-series";
16
15
  import { registeredSeriesName } from "./series-expression";
17
16
  import {
@@ -404,7 +403,7 @@ const chartComposerTemplates: PaneTemplateDef[] = [
404
403
  "positioning",
405
404
  ],
406
405
  shortcut: { prefix: "CAT", argPlaceholder: "query", argKind: "text", argOptional: true },
407
- reportFreshness: { status: "not-a-feed", basis: "series catalog" },
406
+ reportFreshness: { status: "not-a-feed", basis: "series catalog", local: true },
408
407
  canCreate: () => true,
409
408
  createInstance: (_context, options) => {
410
409
  const query = options?.arg?.trim() ?? options?.values?.query?.trim() ?? "";
@@ -525,7 +524,7 @@ export const chartComposerModule: PluginModule = {
525
524
  id: DATA_CATALOG_PANE_ID,
526
525
  name: "Data Catalog",
527
526
  icon: "C",
528
- component: DataCatalogPane,
527
+ get component(): typeof import("./data-catalog-pane").DataCatalogPane { const { DataCatalogPane } = require("./data-catalog-pane"); return DataCatalogPane; },
529
528
  defaultPosition: "right",
530
529
  defaultMode: "floating",
531
530
  defaultFloatingSize: { width: 110, height: 32 },
@@ -20,6 +20,7 @@ import {
20
20
  type MultiSelectRowAction,
21
21
  } from "../../../components/ui";
22
22
  import { CompositeChart } from "../../../components/chart/composite";
23
+ import { savedCopyFooterInfo } from "../../../components/layout/pane/footer";
23
24
  import { useChartViewReset, useChartViewResetListener } from "../../../components/chart/core/view-reset";
24
25
  import { formatCompositeDatasetDate } from "../../../components/chart/composite/format";
25
26
  import type { CompositeChartLevels, CompositeLevelEdit } from "../../../components/chart/composite/levels";
@@ -140,7 +141,10 @@ import { alertInstrument, syncAlertQuoteStream } from "../alerts/live";
140
141
  import { getSharedMarketDataCoordinator } from "../../../market-data/coordinator";
141
142
  import { useOptionChartRange } from "./option-range";
142
143
  import { getActiveQuoteDisplay } from "../../../market-data/market/status";
143
- import { resolveEntryData } from "../../../market-data/selectors";
144
+ import { buildQuoteKey, resolveEntryData } from "../../../market-data/selectors";
145
+ import { useQuoteEntries } from "../../../market-data/hooks";
146
+ import { usePlanAccess } from "../../../api-client/plan-access";
147
+ import { useQuoteDelayFooter } from "../shared/cloud-upgrade";
144
148
  import { editPriceLevels, parsePriceLevels, PRICE_LEVELS_KEY, priceLevelTickerKey } from "./price-levels";
145
149
  import { canonicalExchange } from "../../../utils/exchanges";
146
150
  import { resolveInstrumentForPane } from "../../../core/state/app/instrument";
@@ -217,6 +221,8 @@ interface ChartComposerSurfaceProps {
217
221
  onCapture?: (capturing: boolean) => void;
218
222
  /** The legend names each series' company; a ticker's own research tab already does. */
219
223
  nameSeries?: boolean;
224
+ /** The footer's delayed clock; a ticker's research tab says it in its quote footer instead. */
225
+ delayFooter?: boolean;
220
226
  }
221
227
 
222
228
  const QUICK_ADD_CAPTURE = "quick-add";
@@ -306,6 +312,7 @@ function ChartComposerSurface({
306
312
  footerId,
307
313
  onCapture,
308
314
  nameSeries = true,
315
+ delayFooter = true,
309
316
  }: ChartComposerSurfaceProps) {
310
317
  const trade = useResearchTrading();
311
318
  const dialog = useDialog();
@@ -1116,9 +1123,28 @@ function ChartComposerSurface({
1116
1123
  }
1117
1124
  return newest > end ? formatCompositeDatasetDate(new Date(newest), end) : null;
1118
1125
  }, [activeRuntimeViewport, plottedSeries, spec, viewport]);
1126
+ // A series a failed refresh left on a saved copy says when that copy was saved, as every pane does.
1127
+ const savedAt = resolution.savedAt;
1128
+ // Signed out or free, a market series' latest bars are built from delayed quotes.
1129
+ const marketInstruments = useMemo(() => (delayFooter ? spec.series.flatMap((entry) => (
1130
+ entry.source.kind === "security" && isMarketFieldId(entry.source.fieldId) ? [entry.source.instrument] : []
1131
+ )) : []), [delayFooter, spec.series]);
1132
+ const marketQuotes = useQuoteEntries(marketInstruments);
1133
+ // Signed out no stream feeds a chart's quotes: without Pro the chart reads them once to know which are late.
1134
+ const { hasProAccess } = usePlanAccess();
1135
+ const marketQuoteKey = marketInstruments.map(buildQuoteKey).join("\u001f");
1136
+ useEffect(() => {
1137
+ const coordinator = getSharedMarketDataCoordinator();
1138
+ if (hasProAccess || !coordinator) return;
1139
+ for (const instrument of marketInstruments) {
1140
+ void coordinator.loadQuote(instrument).catch(logFailure("chart-composer.pane", "Loading a chart quote failed"));
1141
+ }
1142
+ }, [hasProAccess, marketQuoteKey]);
1143
+ const quoteDelay = useQuoteDelayFooter(Array.from(marketQuotes.values(), resolveEntryData), "chart-delay");
1119
1144
  usePaneFooter(footerId, () => ({
1120
1145
  info: [
1121
1146
  ...(delistingKey ? [{ id: "delisted", parts: [{ text: delistingKey, tone: "warning" as const }] }] : []),
1147
+ ...savedCopyFooterInfo([savedAt]),
1122
1148
  ...(loading
1123
1149
  ? [{ id: "loading", parts: [{ text: "loading", tone: "muted" as const }] }]
1124
1150
  : pickingAnchor
@@ -1126,6 +1152,7 @@ function ChartComposerSurface({
1126
1152
  : newestBeyondView
1127
1153
  ? [{ id: "newest", asOf: true, parts: [{ text: `newest ${newestBeyondView}`, tone: "muted" as const }] }]
1128
1154
  : []),
1155
+ ...(quoteDelay ? [quoteDelay] : []),
1129
1156
  ],
1130
1157
  hints: [
1131
1158
  // One key opens the ticket, which chooses Buy or Sell; [s]eries keeps its key with trading on.
@@ -1136,7 +1163,7 @@ function ChartComposerSurface({
1136
1163
  { id: "resolution", key: "b", label: "ars", title: "Bar Size", onPress: footerResolution },
1137
1164
  ...(anchorable ? [{ id: "vwap-anchor", key: "v", label: "wap anchor", title: "Anchor VWAP", onPress: footerAnchor }] : []),
1138
1165
  ],
1139
- }), [delistingKey, loading, pickingAnchor, newestBeyondView, footerSeries, openIndicators, indicatorsDisabled, openFormulas, formulasDisabled, footerResolution, anchorable, footerAnchor, trade]);
1166
+ }), [delistingKey, savedAt, loading, pickingAnchor, newestBeyondView, quoteDelay, footerSeries, openIndicators, indicatorsDisabled, openFormulas, formulasDisabled, footerResolution, anchorable, footerAnchor, trade]);
1140
1167
 
1141
1168
  // A fixed window (a GIP session) highlights no range, so the bar names it.
1142
1169
  const dateWindowLabel = useMemo(() => {
@@ -1472,6 +1499,7 @@ export function ChartComposerResearchTab({ focused, width, height, onCapture }:
1472
1499
  footerId="chart-composer:research"
1473
1500
  onCapture={onCapture}
1474
1501
  nameSeries={false}
1502
+ delayFooter={false}
1475
1503
  />
1476
1504
  );
1477
1505
  }
@@ -8,8 +8,8 @@ import {
8
8
  listTimeSeriesFields,
9
9
  } from "../../../time-series/field-catalog";
10
10
  import {
11
- CANONICAL_EXCHANGE_ALIASES,
12
11
  canonicalExchange,
12
+ isKnownExchangeCode,
13
13
  publicTickerKey,
14
14
  } from "../../../utils/exchanges";
15
15
  import { MAX_CHART_COMPOSER_SERIES } from "./chart-spec";
@@ -73,7 +73,8 @@ export function normalizeInstrument(
73
73
  if (parts.length !== 2) return null;
74
74
  const symbol = normalizeBaseSymbol(parts[0]!);
75
75
  const exchangeToken = parts[1]!.trim().toUpperCase();
76
- const knownExchange = Object.prototype.hasOwnProperty.call(CANONICAL_EXCHANGE_ALIASES, exchangeToken);
76
+ // Every venue the app names, as a listing's key writes it (BDI:NEO, Cboe Canada), not only the spellings it reads as another.
77
+ const knownExchange = isKnownExchangeCode(exchangeToken);
77
78
  if (!symbol || !/^[A-Z0-9._-]{1,24}$/.test(exchangeToken) || (!knownExchange && !allowUnknownExchange)) {
78
79
  return null;
79
80
  }
@@ -4,6 +4,7 @@ import { cleanFloat32Price } from "../../../cli/history-rows";
4
4
  import { exportNumber } from "../../../cli/report-tables";
5
5
  import { publicTickerKey } from "../../../utils/exchanges";
6
6
  import { formatUtcTime } from "../../../utils/utc-time";
7
+ import { pricePointWicks } from "../../../utils/price-history-integrity";
7
8
 
8
9
  interface SessionExtreme {
9
10
  price: number;
@@ -26,8 +27,10 @@ function sessionRange(points: readonly PricePoint[]): SessionRange | null {
26
27
  for (const point of points) {
27
28
  const at = point.date;
28
29
  if (!(at instanceof Date) || !Number.isFinite(at.getTime())) continue;
29
- const pointLow = finitePositive(point.low) ?? finitePositive(point.close);
30
- const pointHigh = finitePositive(point.high) ?? finitePositive(point.close);
30
+ // A wick too far from the bar's body to have traded is not a price the bars traded.
31
+ const wicks = pricePointWicks(point);
32
+ const pointLow = finitePositive(wicks.low) ?? finitePositive(point.close);
33
+ const pointHigh = finitePositive(wicks.high) ?? finitePositive(point.close);
31
34
  if (pointLow != null && (!low || pointLow < low.price)) low = { price: pointLow, at };
32
35
  if (pointHigh != null && (!high || pointHigh > high.price)) high = { price: pointHigh, at };
33
36
  }
@@ -1,7 +1,7 @@
1
1
  import type { ChartSpec, ChartStudyKind, ChartStudySpec, ResolvedSeries } from "../../../time-series/types";
2
2
  import { observationDate } from "../../../time-series/price-comparison";
3
3
  import { zonedWallClockToUtcMs } from "../../../utils/zoned-date-time";
4
- import { DEFAULT_PROFILE_ROWS, vwapAnchors, withVwapAnchors } from "../../../time-series/studies";
4
+ import { DEFAULT_PROFILE_ROWS, vwapAnchors, withVwapAnchors } from "../../../time-series/studies/vwap";
5
5
  import { reconcilePanels } from "./chart-spec-edit";
6
6
  import { CHART_FIELD_IDS } from "./series-expression";
7
7
 
@@ -916,7 +916,7 @@ export class ChatController {
916
916
  const channel = this.ensureChannelState(channelId);
917
917
  mergeChatMessages({
918
918
  channel,
919
- currentUserId: this.session.user?.id,
919
+ currentUser: this.session.user,
920
920
  messages,
921
921
  options,
922
922
  viewActive: this.appActive && channel.focusedViewCount > 0,
@@ -1,5 +1,6 @@
1
1
  import type { ChatAttachment, ChatMessage } from "../../../../api-client";
2
2
  import { toTimestampMillis } from "../../../../utils/timestamp";
3
+ import { isGloomWire } from "../ghost-user";
3
4
  import type { ChannelRuntimeState, MergeMessagesOptions } from "./state";
4
5
  import { PENDING_RECONCILE_WINDOW_MS } from "./state";
5
6
  import { chatMessageMentionsUsername, compareMessages, normalizeChatUsername } from "./utils";
@@ -186,16 +187,26 @@ function getMentionMessages(messages: ChatMessage[], user: CurrentChatUser): Cha
186
187
 
187
188
  interface MergeChatMessagesOptions {
188
189
  channel: ChannelRuntimeState;
189
- currentUserId?: string | null;
190
+ currentUser?: CurrentChatUser;
190
191
  messages: ChatMessage[];
191
192
  options?: MergeMessagesOptions;
192
193
  viewActive: boolean;
193
194
  markViewed: (persist?: boolean) => void;
194
195
  }
195
196
 
197
+ /**
198
+ * New messages from others count as unread, as the server counts them: a
199
+ * Gloom Wire post only when it mentions you.
200
+ */
201
+ function countUnread(messages: ChatMessage[], user: CurrentChatUser): number {
202
+ const fromPeople = messages.filter((message) => !isGloomWire(message.user));
203
+ const wireMentions = getMentionMessages(messages.filter((message) => isGloomWire(message.user)), user);
204
+ return fromPeople.length + wireMentions.length;
205
+ }
206
+
196
207
  export function mergeChatMessages({
197
208
  channel,
198
- currentUserId,
209
+ currentUser,
199
210
  messages,
200
211
  options,
201
212
  viewActive,
@@ -203,11 +214,11 @@ export function mergeChatMessages({
203
214
  }: MergeChatMessagesOptions): void {
204
215
  reconcilePendingMessages(channel, messages);
205
216
  const freshIncoming = mergeStoredMessages(channel, messages)
206
- .filter((message) => message.user.id !== currentUserId);
217
+ .filter((message) => message.user.id !== currentUser?.id);
207
218
  if (viewActive) {
208
219
  markViewed(false);
209
220
  } else if (freshIncoming.length > 0 && options?.countUnread !== false) {
210
- channel.unreadCount += freshIncoming.length;
221
+ channel.unreadCount += countUnread(freshIncoming, currentUser);
211
222
  }
212
223
  }
213
224
 
@@ -16,6 +16,14 @@ export function isManagedBot(user: Pick<ChatUserSummary, "accountType"> | null |
16
16
  return user?.accountType === "managed_llm";
17
17
  }
18
18
 
19
+ /** Gloom Wire: the account that posts headlines, the day's calendar and pull requests into the topic channels. */
20
+ const GLOOM_WIRE_USER_ID = "managed-llm-gloomwire";
21
+
22
+ /** A Gloom Wire post: there to read, so unread only when it mentions you. */
23
+ export function isGloomWire(user: Pick<ChatUserSummary, "id"> | null | undefined): boolean {
24
+ return user?.id === GLOOM_WIRE_USER_ID;
25
+ }
26
+
19
27
  /**
20
28
  * A reply quotes its author by name only, so whether that author is a managed
21
29
  * assistant comes from the users the pane has seen. A quoted announcement is
@@ -16,11 +16,9 @@ import { CHAT_KEEP_CONVERSATION_VALUE, createChatSearchProvider } from "./comman
16
16
  import { chatController } from "./controller";
17
17
  import { buildChatConversations, selectChatConversations } from "./conversations";
18
18
  import { pickChatPaneToReuse } from "./pane-state";
19
- import { ChatPane } from "./pane";
20
19
  import { chatSidebarStore } from "./sidebar-store";
21
20
  import { chatHandlePromptStore } from "./handle-prompt";
22
21
  import { ChatStatusWidget } from "./status-widget";
23
- import { UnreadInboxPane } from "./unread-inbox-pane";
24
22
  import { installPaneChatSharing } from "./share/flow";
25
23
  import { attachChatPaneLinkPanes } from "./pane-links/previews";
26
24
  import { attachChatCommands, cachedChatFunctions, chatFunctionsChanged } from "./pane-links/commands";
@@ -62,9 +60,10 @@ let disposeUnreadBadge: (() => void) | undefined;
62
60
  export const chatModule: PluginModule = {
63
61
  panes: [{
64
62
  id: "chat",
63
+ reportFreshness: { local: true },
65
64
  name: "Chat",
66
65
  icon: "C",
67
- component: ChatPane,
66
+ get component(): typeof import("./pane").ChatPane { const { ChatPane } = require("./pane"); return ChatPane; },
68
67
  defaultPosition: "right",
69
68
  defaultMode: "floating",
70
69
  defaultFloatingSize: { width: 80, height: 30 },
@@ -79,12 +78,12 @@ export const chatModule: PluginModule = {
79
78
  },
80
79
  }, {
81
80
  id: UNREAD_INBOX_PANE_ID,
82
- reportFreshness: { status: "not-a-feed", basis: "chat messages and missed alerts" },
81
+ reportFreshness: { status: "not-a-feed", basis: "chat messages and missed alerts", local: true },
83
82
  name: "Unread",
84
83
  // Mentions and replies from every conversation, DMs included.
85
84
  personalData: "messages",
86
85
  icon: "U",
87
- component: UnreadInboxPane,
86
+ get component(): typeof import("./unread-inbox-pane").UnreadInboxPane { const { UnreadInboxPane } = require("./unread-inbox-pane"); return UnreadInboxPane; },
88
87
  defaultPosition: "right",
89
88
  defaultMode: "floating",
90
89
  defaultFloatingSize: { width: 88, height: 16 },
@@ -96,6 +95,7 @@ export const chatModule: PluginModule = {
96
95
  description: "Open the floating chat window for a channel",
97
96
  keywords: ["new", "chat", "pane", "message"],
98
97
  shortcut: { prefix: "CHAT", argPlaceholder: "channel", argKind: "text" },
98
+ catalogSettings: {},
99
99
  createInstance: async (context, options) => {
100
100
  const rawArg = options?.arg?.trim() ?? "";
101
101
  // The command bar's New DM keeps the open pane on its conversation.
@@ -1,6 +1,5 @@
1
1
  import type { GloomPlugin, PaneTemplateCreateOptions } from "../../../types/plugin";
2
2
  import { normalizeTrialFilters, TRIAL_REPORT_OPTIONS, ALL } from "./model";
3
- import { TrialsPane } from "./pane";
4
3
  import { CLINICAL_TRIALS_PANE_ID, CLINICAL_TRIALS_PLUGIN_ID } from "./types";
5
4
 
6
5
  function createTrialsPaneInstance(options?: PaneTemplateCreateOptions) {
@@ -40,7 +39,7 @@ export const clinicalTrialsPlugin: GloomPlugin = {
40
39
  reportOptions: TRIAL_REPORT_OPTIONS,
41
40
  name: "Trials",
42
41
  icon: "C",
43
- component: TrialsPane,
42
+ get component(): typeof import("./pane").TrialsPane { const { TrialsPane } = require("./pane"); return TrialsPane; },
44
43
  defaultPosition: "right",
45
44
  defaultMode: "floating",
46
45
  defaultFloatingSize: { width: 100, height: 30 },
@@ -189,7 +189,7 @@ function LogoTile({ symbol, assetCategory, size }: { symbol: string; assetCatego
189
189
  return <Box alignItems="center" justifyContent="center" style={tile}>{letter}</Box>;
190
190
  }
191
191
  return (
192
- <ImageSurface src={src} alt={symbol} objectFit="contain" alignItems="center" justifyContent="center" style={tile}>
192
+ <ImageSurface src={src} alt={symbol} objectFit="contain" fallbackWhileLoading alignItems="center" justifyContent="center" style={tile}>
193
193
  {letter}
194
194
  </ImageSurface>
195
195
  );
@@ -1,4 +1,3 @@
1
- import { buildoutModule } from "../buildout";
2
1
  import { createGloomberbCloudPlugin } from "./plugin";
3
2
 
4
- export const gloomberbCloudPlugin = createGloomberbCloudPlugin([buildoutModule]);
3
+ export const gloomberbCloudPlugin = createGloomberbCloudPlugin();
@@ -2,6 +2,7 @@ import type { GloomPlugin } from "../../../types/plugin";
2
2
  import { apiClient } from "../../../api-client";
3
3
  import { createGloomberbCloudCapabilities, createGloomberbCloudProvider } from "../../../sources/gloomberb-cloud";
4
4
  import { accountManagementModule } from "../account-management";
5
+ import { buildoutModule } from "../buildout";
5
6
  import { chatModule } from "../chat";
6
7
  import {
7
8
  CONGRESS_TRADES_PANE_ID,
@@ -88,8 +89,10 @@ const askgModule: PluginModule = {
88
89
  },
89
90
  panes: [{
90
91
  id: ASKG_PANE_ID,
91
- reportFreshness: { status: "not-a-feed", basis: "generated answer" },
92
+ reportFreshness: { status: "not-a-feed", basis: "generated answer", local: true },
92
93
  reportLockNote: askgLockNote,
94
+ // Without a session the wall is all there is, so `fn ASKG` prints it at once.
95
+ signInWall: true,
93
96
  name: "Ask Gloom",
94
97
  personalData: "messages",
95
98
  icon: "K",
@@ -177,8 +180,7 @@ const congressTradesModule: PluginModule = {
177
180
  }],
178
181
  };
179
182
 
180
- /** The desktop and browser builds differ only in the modules they add here. */
181
- export function createGloomberbCloudPlugin(extraModules: readonly PluginModule[] = []): GloomPlugin {
183
+ export function createGloomberbCloudPlugin(): GloomPlugin {
182
184
  return composeBuiltinPlugin({
183
185
  id: "gloomberb-cloud",
184
186
  name: "Gloom Cloud",
@@ -195,7 +197,7 @@ export function createGloomberbCloudPlugin(extraModules: readonly PluginModule[]
195
197
  cloudAccountModule,
196
198
  askgModule,
197
199
  terminalRelayModule,
198
- ...extraModules,
200
+ buildoutModule,
199
201
  congressTradesModule,
200
202
  cloudTweetsModule,
201
203
  ],
@@ -24,7 +24,7 @@ export const teamModule: PluginModule = {
24
24
  capabilities: [createCloudTeamCapability(), createCloudViewsCapability()],
25
25
  panes: [{
26
26
  id: TEAM_PANE_ID,
27
- reportFreshness: { status: "not-a-feed", basis: "account data" },
27
+ reportFreshness: { status: "not-a-feed", basis: "account data", local: true },
28
28
  name: "Team",
29
29
  icon: "T",
30
30
  personalData: "account",
@@ -12,7 +12,7 @@ import { ThesisTickerTab } from "./ticker-tab";
12
12
  export const thesisModule: PluginModule = {
13
13
  panes: [{
14
14
  id: THESIS_PANE_ID,
15
- reportFreshness: { source: "Your theses", status: "not-a-feed", basis: "your theses" },
15
+ reportFreshness: { source: "Your theses", status: "not-a-feed", basis: "your theses", local: true },
16
16
  name: "Theses",
17
17
  personalData: "holdings",
18
18
  icon: "Θ",