gloomberb 0.16.6 → 0.17.1

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 (429) hide show
  1. package/package.json +2 -1
  2. package/src/api-client/chat-pane-link.ts +47 -0
  3. package/src/api-client/chat.ts +9 -1
  4. package/src/api-client/data.ts +42 -2
  5. package/src/api-client/doe.ts +19 -0
  6. package/src/api-client/equity-screener.ts +3 -0
  7. package/src/api-client/europe-energy.ts +91 -0
  8. package/src/api-client/index.ts +7 -0
  9. package/src/api-client/letters.ts +41 -0
  10. package/src/api-client/normalizers.ts +36 -6
  11. package/src/api-client/organization-tickers.ts +44 -0
  12. package/src/api-client/request.ts +3 -1
  13. package/src/api-client/types.ts +27 -1
  14. package/src/app/runtime/desktop-deeplink.ts +48 -15
  15. package/src/app/runtime/update.ts +16 -0
  16. package/src/architecture/core-builtin-allowlist.json +1 -1
  17. package/src/architecture/dom-cell-metrics-allowlist.json +102 -0
  18. package/src/ask-gloom/pane-context.ts +23 -19
  19. package/src/ask-gloom/pane-request.ts +2 -2
  20. package/src/cli/commands/fred-series.ts +77 -11
  21. package/src/cli/commands/market.ts +134 -25
  22. package/src/cli/commands/options-chain.ts +75 -1
  23. package/src/cli/commands/overview.ts +29 -20
  24. package/src/cli/commands/ticker.ts +82 -20
  25. package/src/cli/desktop-pane-shot-view.ts +114 -0
  26. package/src/cli/desktop-pane-shot.ts +30 -31
  27. package/src/cli/entry.ts +7 -0
  28. package/src/cli/export-source.ts +43 -0
  29. package/src/cli/freshness.ts +18 -3
  30. package/src/cli/glossary.ts +17 -0
  31. package/src/cli/history-rows.ts +174 -13
  32. package/src/cli/index.ts +14 -6
  33. package/src/cli/known-gaps.ts +14 -3
  34. package/src/cli/listing-arg.ts +20 -2
  35. package/src/cli/listing-suggestion.ts +159 -0
  36. package/src/cli/native-smoke.ts +13 -0
  37. package/src/cli/not-a-ticker.ts +13 -1
  38. package/src/cli/pane-functions/capabilities.ts +16 -8
  39. package/src/cli/pane-functions/catalog.ts +39 -7
  40. package/src/cli/pane-functions/dom.ts +39 -13
  41. package/src/cli/pane-functions/function-help.ts +54 -7
  42. package/src/cli/pane-functions/headless.ts +38 -8
  43. package/src/cli/pane-functions/index.ts +85 -3
  44. package/src/cli/pane-functions/lock-note.ts +56 -0
  45. package/src/cli/pane-functions/report-notices.ts +6 -3
  46. package/src/cli/pane-functions/report.ts +16 -2
  47. package/src/cli/pane-functions/resolver.ts +7 -4
  48. package/src/cli/report-tables.ts +31 -7
  49. package/src/cli/result.ts +54 -11
  50. package/src/cli/symbol-rows.ts +18 -1
  51. package/src/components/chart/composite/axis-overlays.tsx +2 -0
  52. package/src/components/chart/composite/composite-chart.tsx +4 -21
  53. package/src/components/chart/composite/legend.tsx +10 -6
  54. package/src/components/chart/composite/price-axis-labels.tsx +1 -0
  55. package/src/components/command-bar/list/model.ts +2 -0
  56. package/src/components/command-bar/list/view.tsx +2 -1
  57. package/src/components/command-bar/pane-templates/items.ts +1 -1
  58. package/src/components/command-bar/routes/ticker-search/actions.ts +1 -0
  59. package/src/components/command-bar/routes/ticker-search/results.ts +3 -1
  60. package/src/components/command-bar/workflow/ops.ts +8 -97
  61. package/src/components/command-bar/workflow/pane-reuse.ts +177 -0
  62. package/src/components/data-table/view.tsx +1 -0
  63. package/src/components/feed-data-table/stack-view.tsx +18 -42
  64. package/src/components/index.ts +3 -2
  65. package/src/components/layout/header.tsx +58 -14
  66. package/src/components/layout/missing-pane.tsx +2 -0
  67. package/src/components/layout/shell/chrome.ts +10 -4
  68. package/src/components/layout/shell/index.tsx +1 -0
  69. package/src/components/layout/shell/pane/ask.ts +12 -7
  70. package/src/components/layout/shell/pane/capture.ts +8 -34
  71. package/src/components/markdown-text.tsx +37 -6
  72. package/src/components/reader/article-reader.tsx +91 -266
  73. package/src/components/reader/detail-reader.tsx +210 -0
  74. package/src/components/reader/reader-text.tsx +239 -0
  75. package/src/components/table-view-shared.tsx +8 -4
  76. package/src/components/ticker/badge/list.tsx +15 -4
  77. package/src/components/ui/choice-dialog.tsx +3 -0
  78. package/src/components/ui/column-grid.tsx +206 -0
  79. package/src/components/ui/data-table/types.ts +7 -0
  80. package/src/components/ui/detail-scroll-body.tsx +31 -4
  81. package/src/components/ui/display.tsx +86 -4
  82. package/src/components/ui/fill.ts +8 -0
  83. package/src/components/ui/icon.tsx +11 -1
  84. package/src/components/ui/index.ts +2 -0
  85. package/src/components/ui/list-view.tsx +3 -1
  86. package/src/components/ui/query-bar-keys.tsx +2 -0
  87. package/src/components/ui/query-bar.tsx +9 -1
  88. package/src/components/ui/select-button.tsx +3 -0
  89. package/src/data/config/store/fold-panes.ts +202 -11
  90. package/src/data/config/store/migrations.ts +13 -1
  91. package/src/docs/snapshot.json +29 -0
  92. package/src/i18n/es.ts +14 -0
  93. package/src/i18n/ja.ts +14 -0
  94. package/src/i18n/ko.ts +14 -0
  95. package/src/i18n/zh-cn.ts +14 -0
  96. package/src/i18n/zh-tw.ts +14 -0
  97. package/src/layout/desk-keys.ts +7 -0
  98. package/src/layout/desks.ts +2 -2
  99. package/src/market-data/market/status.ts +67 -6
  100. package/src/market-data/quotes/contributions.ts +15 -0
  101. package/src/market-data/quotes/regular-session-valuation.ts +131 -0
  102. package/src/market-data/quotes/resolution.ts +4 -0
  103. package/src/market-data/quotes/screener-live-quotes.ts +31 -7
  104. package/src/market-data/snapshot-provider.ts +6 -3
  105. package/src/notifications/app-badge.ts +30 -0
  106. package/src/plugins/builtin/account-management/calendar-tab.tsx +1 -1
  107. package/src/plugins/builtin/account-management/form-components.tsx +57 -8
  108. package/src/plugins/builtin/account-management/keyboard.ts +5 -0
  109. package/src/plugins/builtin/account-management/model.ts +27 -0
  110. package/src/plugins/builtin/account-management/pane.tsx +62 -2
  111. package/src/plugins/builtin/account-management/password-dialog.tsx +3 -2
  112. package/src/plugins/builtin/alerts/cli.ts +6 -4
  113. package/src/plugins/builtin/analytics/index.tsx +1 -1
  114. package/src/plugins/builtin/awards/pane.tsx +29 -18
  115. package/src/plugins/builtin/backtest/pane.tsx +11 -4
  116. package/src/plugins/builtin/bond-calculator/index.tsx +3 -5
  117. package/src/plugins/builtin/buildout/detail/company.tsx +11 -7
  118. package/src/plugins/builtin/buildout/detail/ui.tsx +9 -12
  119. package/src/plugins/builtin/builtin-plugin-meta.ts +5 -4
  120. package/src/plugins/builtin/catalysts/model.ts +92 -10
  121. package/src/plugins/builtin/catalysts/pane.tsx +6 -4
  122. package/src/plugins/builtin/cds/index.tsx +21 -30
  123. package/src/plugins/builtin/changelog/index.tsx +11 -12
  124. package/src/plugins/builtin/chart-composer/headless-schema.ts +2 -0
  125. package/src/plugins/builtin/chart-composer/headless.ts +87 -5
  126. package/src/plugins/builtin/chart-composer/pane.tsx +3 -2
  127. package/src/plugins/builtin/chart-composer/session-history.ts +20 -5
  128. package/src/plugins/builtin/chat/command-bar.ts +6 -8
  129. package/src/plugins/builtin/chat/content/composer-runtime.ts +2 -2
  130. package/src/plugins/builtin/chat/content/composer.tsx +13 -4
  131. package/src/plugins/builtin/chat/content/footer.tsx +15 -5
  132. package/src/plugins/builtin/chat/content/handle-prompt.tsx +326 -0
  133. package/src/plugins/builtin/chat/content/index.tsx +113 -9
  134. package/src/plugins/builtin/chat/content/layout-metrics.ts +4 -0
  135. package/src/plugins/builtin/chat/content/open-targets.ts +20 -4
  136. package/src/plugins/builtin/chat/content/scroll.ts +6 -1
  137. package/src/plugins/builtin/chat/content/transcript.tsx +11 -2
  138. package/src/plugins/builtin/chat/ghost-user.ts +1 -1
  139. package/src/plugins/builtin/chat/handle-prompt.ts +128 -0
  140. package/src/plugins/builtin/chat/index.tsx +44 -21
  141. package/src/plugins/builtin/chat/layout.ts +7 -14
  142. package/src/plugins/builtin/chat/message/desktop.tsx +59 -11
  143. package/src/plugins/builtin/chat/message/header.tsx +69 -21
  144. package/src/plugins/builtin/chat/message/pane-chip.tsx +223 -0
  145. package/src/plugins/builtin/chat/message/person-icons.tsx +53 -0
  146. package/src/plugins/builtin/chat/message/profile-popover.tsx +73 -9
  147. package/src/plugins/builtin/chat/message/render-state.ts +15 -2
  148. package/src/plugins/builtin/chat/message/terminal.tsx +33 -8
  149. package/src/plugins/builtin/chat/message/types.ts +3 -0
  150. package/src/plugins/builtin/chat/pane-links/model.ts +149 -0
  151. package/src/plugins/builtin/chat/pane-links/open.ts +29 -0
  152. package/src/plugins/builtin/chat/pane-links/previews.ts +119 -0
  153. package/src/plugins/builtin/chat/pane-state.ts +5 -8
  154. package/src/plugins/builtin/chat/people.ts +202 -0
  155. package/src/plugins/builtin/chat/profile-popover.ts +18 -2
  156. package/src/plugins/builtin/chat/status-widget.tsx +3 -8
  157. package/src/plugins/builtin/chat/unread-badge.ts +65 -0
  158. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +9 -6
  159. package/src/plugins/builtin/clinical-trials/client.ts +26 -1
  160. package/src/plugins/builtin/clinical-trials/index.tsx +10 -2
  161. package/src/plugins/builtin/clinical-trials/model.ts +269 -0
  162. package/src/plugins/builtin/clinical-trials/pane.tsx +247 -62
  163. package/src/plugins/builtin/clinical-trials/sponsor-tickers.ts +156 -0
  164. package/src/plugins/builtin/cloud/askg/attached-pane.tsx +3 -3
  165. package/src/plugins/builtin/cloud/askg/pane-attachment.ts +3 -3
  166. package/src/plugins/builtin/cloud/askg/pane.tsx +2 -1
  167. package/src/plugins/builtin/cloud/askg/tool-timeline.tsx +3 -2
  168. package/src/plugins/builtin/cloud/mcp-connect/dialog.tsx +1 -0
  169. package/src/plugins/builtin/cloud/thesis/detail.tsx +11 -9
  170. package/src/plugins/builtin/comment-letters/pane.tsx +30 -11
  171. package/src/plugins/builtin/commodities-energy/index.tsx +179 -0
  172. package/src/plugins/builtin/commodity-balances/client.ts +208 -0
  173. package/src/plugins/builtin/commodity-balances/headless.ts +66 -0
  174. package/src/plugins/builtin/commodity-balances/index.tsx +32 -0
  175. package/src/plugins/builtin/commodity-balances/model.ts +261 -0
  176. package/src/plugins/builtin/commodity-balances/pane.tsx +227 -0
  177. package/src/plugins/builtin/composite-plugins.ts +26 -1
  178. package/src/plugins/builtin/congress-trades/detail.tsx +8 -4
  179. package/src/plugins/builtin/connections/pane.tsx +16 -5
  180. package/src/plugins/builtin/cpi/pane.tsx +5 -4
  181. package/src/plugins/builtin/credit-boards/index.tsx +21 -42
  182. package/src/plugins/builtin/credit-boards/model.ts +0 -2
  183. package/src/plugins/builtin/credit-conditions/index.tsx +10 -19
  184. package/src/plugins/builtin/credit-documents/index.tsx +27 -10
  185. package/src/plugins/builtin/debt-maturities/index.tsx +3 -17
  186. package/src/plugins/builtin/debug/index.tsx +2 -2
  187. package/src/plugins/builtin/dividend-yield/client.ts +3 -1
  188. package/src/plugins/builtin/dividend-yield/headless.ts +3 -1
  189. package/src/plugins/builtin/dividend-yield/pane.tsx +3 -1
  190. package/src/plugins/builtin/dividend-yield/provider-client.ts +3 -1
  191. package/src/plugins/builtin/docs/blocks.tsx +4 -3
  192. package/src/plugins/builtin/docs/pane.tsx +16 -8
  193. package/src/plugins/builtin/doe/index.tsx +11 -4
  194. package/src/plugins/builtin/doe/pane.tsx +5 -4
  195. package/src/plugins/builtin/doe/prices.ts +84 -0
  196. package/src/plugins/builtin/doe/series.ts +4 -1
  197. package/src/plugins/builtin/earnings/index.tsx +2 -0
  198. package/src/plugins/builtin/earnings-calls/headless.ts +7 -21
  199. package/src/plugins/builtin/earnings-calls/index.tsx +2 -0
  200. package/src/plugins/builtin/earnings-calls/lock-note.ts +17 -0
  201. package/src/plugins/builtin/earnings-calls/pane.tsx +2 -12
  202. package/src/plugins/builtin/equity-screener/client.ts +1 -1
  203. package/src/plugins/builtin/equity-screener/headless.ts +95 -3
  204. package/src/plugins/builtin/equity-screener/live.ts +82 -31
  205. package/src/plugins/builtin/equity-screener/model.ts +121 -68
  206. package/src/plugins/builtin/equity-screener/pane.tsx +60 -23
  207. package/src/plugins/builtin/estimate-revisions/client.ts +1 -1
  208. package/src/plugins/builtin/estimate-revisions/headless.ts +9 -2
  209. package/src/plugins/builtin/estimate-revisions/index.tsx +3 -0
  210. package/src/plugins/builtin/estimate-revisions/lock-note.ts +23 -0
  211. package/src/plugins/builtin/executives/index.tsx +18 -0
  212. package/src/plugins/builtin/executives/pane.tsx +3 -1
  213. package/src/plugins/builtin/exposure/charts.tsx +4 -3
  214. package/src/plugins/builtin/filing-events/index.tsx +18 -0
  215. package/src/plugins/builtin/fixed-income/index.tsx +46 -0
  216. package/src/plugins/builtin/fixed-income/model.ts +27 -0
  217. package/src/plugins/builtin/fixed-income/pane.tsx +163 -0
  218. package/src/plugins/builtin/fixed-income/shortcut.ts +52 -0
  219. package/src/plugins/builtin/futures/index.tsx +24 -7
  220. package/src/plugins/builtin/futures/series.ts +23 -2
  221. package/src/plugins/builtin/futures/use-front-returns.ts +3 -2
  222. package/src/plugins/builtin/futures-curve/pane.tsx +6 -4
  223. package/src/plugins/builtin/gpu/equities.tsx +9 -6
  224. package/src/plugins/builtin/help/shortcut-table.tsx +4 -2
  225. package/src/plugins/builtin/insider/detail.tsx +20 -12
  226. package/src/plugins/builtin/insider/index.tsx +2 -2
  227. package/src/plugins/builtin/iv-history/format.ts +3 -0
  228. package/src/plugins/builtin/iv-history/headless.ts +3 -1
  229. package/src/plugins/builtin/iv-history/index.tsx +6 -1
  230. package/src/plugins/builtin/iv-history/model.ts +3 -1
  231. package/src/plugins/builtin/iv-history/screen-pane.tsx +16 -6
  232. package/src/plugins/builtin/iv-history/universe.ts +1 -1
  233. package/src/plugins/builtin/jobs/headless.ts +6 -20
  234. package/src/plugins/builtin/jobs/index.tsx +2 -0
  235. package/src/plugins/builtin/jobs/lock-note.ts +16 -0
  236. package/src/plugins/builtin/jodi/client.ts +220 -0
  237. package/src/plugins/builtin/jodi/headless.ts +63 -0
  238. package/src/plugins/builtin/jodi/index.tsx +32 -0
  239. package/src/plugins/builtin/jodi/model.ts +75 -0
  240. package/src/plugins/builtin/jodi/pane.tsx +141 -0
  241. package/src/plugins/builtin/market-heatmap/evidence.ts +13 -4
  242. package/src/plugins/builtin/market-heatmap/headless.ts +3 -3
  243. package/src/plugins/builtin/market-heatmap/index.tsx +41 -23
  244. package/src/plugins/builtin/market-heatmap/live.ts +2 -1
  245. package/src/plugins/builtin/market-heatmap/portfolio.ts +67 -21
  246. package/src/plugins/builtin/market-movers/table.ts +2 -2
  247. package/src/plugins/builtin/market-valuation/align.ts +10 -9
  248. package/src/plugins/builtin/mna/distress/model.ts +52 -1
  249. package/src/plugins/builtin/mna/distress/pane.tsx +17 -12
  250. package/src/plugins/builtin/mna/index.tsx +2 -0
  251. package/src/plugins/builtin/news/wire/index.tsx +2 -2
  252. package/src/plugins/builtin/news/wire/industry-pane.tsx +28 -14
  253. package/src/plugins/builtin/news/wire/letters/client.ts +108 -0
  254. package/src/plugins/builtin/news/wire/letters/model.ts +69 -0
  255. package/src/plugins/builtin/news/wire/letters/pane.tsx +155 -0
  256. package/src/plugins/builtin/news/wire/mutes.ts +2 -1
  257. package/src/plugins/builtin/news/wire/news/query-presets.ts +4 -1
  258. package/src/plugins/builtin/openfda/client.ts +123 -15
  259. package/src/plugins/builtin/openfda/model.ts +163 -0
  260. package/src/plugins/builtin/openfda/pane.tsx +109 -65
  261. package/src/plugins/builtin/openfda/types.ts +25 -0
  262. package/src/plugins/builtin/options/index.tsx +2 -1
  263. package/src/plugins/builtin/options/model-spot.ts +94 -0
  264. package/src/plugins/builtin/options/open-interest-as-of.ts +63 -0
  265. package/src/plugins/builtin/options/view.tsx +56 -10
  266. package/src/plugins/builtin/options-scenario/client.ts +33 -9
  267. package/src/plugins/builtin/options-scenario/csp.ts +168 -0
  268. package/src/plugins/builtin/options-scenario/evidence.ts +3 -0
  269. package/src/plugins/builtin/options-scenario/headless.ts +24 -8
  270. package/src/plugins/builtin/options-scenario/index.tsx +3 -1
  271. package/src/plugins/builtin/options-scenario/pane.tsx +32 -6
  272. package/src/plugins/builtin/pe-band/model.ts +3 -2
  273. package/src/plugins/builtin/plugin-marketplace/pane.tsx +1 -1
  274. package/src/plugins/builtin/plugin-module.ts +3 -1
  275. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +1 -1
  276. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +19 -4
  277. package/src/plugins/builtin/portfolio-list/cli/render.ts +2 -2
  278. package/src/plugins/builtin/portfolio-list/cli/shared.ts +39 -0
  279. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +18 -5
  280. package/src/plugins/builtin/portfolio-list/live-valuation.ts +25 -13
  281. package/src/plugins/builtin/power/evidence.ts +32 -3
  282. package/src/plugins/builtin/power/gas-client.ts +110 -0
  283. package/src/plugins/builtin/power/gas-model.ts +194 -0
  284. package/src/plugins/builtin/power/gas.tsx +145 -0
  285. package/src/plugins/builtin/power/headless.ts +4 -1
  286. package/src/plugins/builtin/power/index.tsx +19 -9
  287. package/src/plugins/builtin/power/pane.tsx +2 -3
  288. package/src/plugins/builtin/power/prices-headless.ts +75 -7
  289. package/src/plugins/builtin/power/prices.tsx +36 -13
  290. package/src/plugins/builtin/primary-dealers/client.ts +108 -0
  291. package/src/plugins/builtin/primary-dealers/headless.ts +53 -0
  292. package/src/plugins/builtin/primary-dealers/index.tsx +17 -0
  293. package/src/plugins/builtin/primary-dealers/model.ts +206 -0
  294. package/src/plugins/builtin/primary-dealers/pane.tsx +149 -0
  295. package/src/plugins/builtin/relative-rotation/index.tsx +3 -0
  296. package/src/plugins/builtin/research/analyst-model.ts +9 -5
  297. package/src/plugins/builtin/research/headless-schema.ts +2 -1
  298. package/src/plugins/builtin/research/relative-valuation-headless.ts +68 -32
  299. package/src/plugins/builtin/research/relative-valuation-model.ts +45 -24
  300. package/src/plugins/builtin/research/relative-valuation-pane.tsx +39 -4
  301. package/src/plugins/builtin/risk-factors/index.tsx +18 -0
  302. package/src/plugins/builtin/risk-factors/pane.tsx +2 -1
  303. package/src/plugins/builtin/scanner/flow-pane.tsx +8 -1
  304. package/src/plugins/builtin/scanner/headless.ts +9 -2
  305. package/src/plugins/builtin/scanner/index.ts +3 -0
  306. package/src/plugins/builtin/scanner/lock-note.ts +17 -0
  307. package/src/plugins/builtin/sec/filing-display.tsx +3 -36
  308. package/src/plugins/builtin/sec/index.tsx +131 -43
  309. package/src/plugins/builtin/sec/model.ts +148 -2
  310. package/src/plugins/builtin/sec/row-summaries.ts +105 -0
  311. package/src/plugins/builtin/sectors/index.tsx +2 -0
  312. package/src/plugins/builtin/sectors/sector-model.ts +2 -2
  313. package/src/plugins/builtin/series-inventory.ts +2 -1
  314. package/src/plugins/builtin/shared/cloud-denied.ts +39 -8
  315. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  316. package/src/plugins/builtin/shared/pro-lock-note.ts +58 -0
  317. package/src/plugins/builtin/shared/series-list-detail.tsx +10 -5
  318. package/src/plugins/builtin/shared/use-quote-board.ts +179 -43
  319. package/src/plugins/builtin/shared/wall-teaser.tsx +2 -2
  320. package/src/plugins/builtin/shared/zip-text.ts +46 -0
  321. package/src/plugins/builtin/short-interest/watch-model.ts +11 -2
  322. package/src/plugins/builtin/social-mentions/client.ts +3 -3
  323. package/src/plugins/builtin/social-mentions/headless.ts +6 -2
  324. package/src/plugins/builtin/social-mentions/index.tsx +2 -1
  325. package/src/plugins/builtin/social-mentions/lock-note.ts +15 -0
  326. package/src/plugins/builtin/supply-chain/flow.tsx +15 -13
  327. package/src/plugins/builtin/supply-chain/graph.tsx +6 -4
  328. package/src/plugins/builtin/supply-chain/pane.tsx +8 -5
  329. package/src/plugins/builtin/themes/model.ts +2 -2
  330. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +25 -1
  331. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +87 -23
  332. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +1 -1
  333. package/src/plugins/builtin/ticker-detail/headless.ts +6 -3
  334. package/src/plugins/builtin/ticker-detail/overview/components.tsx +105 -104
  335. package/src/plugins/builtin/ticker-detail/overview/model.ts +12 -3
  336. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +41 -13
  337. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +8 -6
  338. package/src/plugins/builtin/time-sales/headless.ts +22 -0
  339. package/src/plugins/builtin/trace-bonds/client.ts +196 -0
  340. package/src/plugins/builtin/trace-bonds/headless.ts +69 -0
  341. package/src/plugins/builtin/trace-bonds/index.tsx +33 -0
  342. package/src/plugins/builtin/trace-bonds/model.ts +115 -0
  343. package/src/plugins/builtin/trace-bonds/pane.tsx +191 -0
  344. package/src/plugins/builtin/treasury-auctions/index.tsx +33 -47
  345. package/src/plugins/builtin/treasury-daily/client.ts +96 -0
  346. package/src/plugins/builtin/treasury-daily/headless.ts +69 -0
  347. package/src/plugins/builtin/treasury-daily/index.tsx +17 -0
  348. package/src/plugins/builtin/treasury-daily/model.ts +69 -0
  349. package/src/plugins/builtin/treasury-daily/pane.tsx +140 -0
  350. package/src/plugins/builtin/vol-surface/pane.tsx +6 -3
  351. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +15 -2
  352. package/src/plugins/builtin/world-venue-map/map.tsx +1 -0
  353. package/src/plugins/builtin-editorial.ts +1 -1
  354. package/src/plugins/catalog-browser.ts +25 -2
  355. package/src/plugins/marketplace/model.ts +57 -21
  356. package/src/public/__snapshots__/public-api.test.ts.snap +2 -0
  357. package/src/renderers/dom/build-assets.ts +14 -5
  358. package/src/renderers/dom/data-table/index.tsx +53 -18
  359. package/src/renderers/dom/data-table/row.tsx +1 -1
  360. package/src/renderers/dom/desktop/controls.tsx +2 -1
  361. package/src/renderers/dom/desktop/icons.tsx +25 -0
  362. package/src/renderers/dom/desktop/list-view.tsx +1 -1
  363. package/src/renderers/dom/desktop/menu.tsx +1 -1
  364. package/src/renderers/dom/desktop/popover.tsx +6 -15
  365. package/src/renderers/dom/desktop/query-bar.tsx +3 -1
  366. package/src/renderers/dom/host/chart-surface.tsx +7 -2
  367. package/src/renderers/dom/host/input.tsx +27 -3
  368. package/src/renderers/dom/host/resize-rows.ts +111 -0
  369. package/src/renderers/dom/host/sticky-scroll.ts +22 -3
  370. package/src/renderers/dom/host/style.ts +14 -0
  371. package/src/renderers/dom/host/surface-3d.tsx +8 -5
  372. package/src/renderers/dom/host/surface-fit.ts +10 -6
  373. package/src/renderers/dom/host/text.tsx +2 -2
  374. package/src/renderers/dom/styles.css +85 -10
  375. package/src/renderers/electrobun/bun/desktop/app-badge.ts +37 -0
  376. package/src/renderers/electrobun/bun/desktop/backend.ts +1 -0
  377. package/src/renderers/electrobun/bun/desktop/host-requests.ts +8 -0
  378. package/src/renderers/electrobun/bun/desktop/macos-dock-badge.ts +37 -0
  379. package/src/renderers/electrobun/bun/desktop/macos-dock-icon.ts +2 -40
  380. package/src/renderers/electrobun/bun/desktop/macos-objc.ts +41 -0
  381. package/src/renderers/electrobun/bun/tui-entry.ts +18 -1
  382. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  383. package/src/renderers/electrobun/view/app-badge-host.ts +17 -0
  384. package/src/renderers/electrobun/view/app-services.ts +2 -22
  385. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +8 -3
  386. package/src/renderers/electrobun/view/main.tsx +2 -0
  387. package/src/renderers/electrobun/view/news-source.ts +42 -0
  388. package/src/renderers/electrobun/view/remote/asset-data-client.ts +5 -1
  389. package/src/series/validate.ts +6 -1
  390. package/src/shares/api.ts +5 -1
  391. package/src/shares/dialog.tsx +2 -1
  392. package/src/shares/location.ts +31 -0
  393. package/src/shares/open.ts +26 -0
  394. package/src/shares/pane-image.ts +18 -6
  395. package/src/shares/personal-pane.ts +42 -54
  396. package/src/sources/gloomberb-cloud/index.ts +7 -2
  397. package/src/sources/gloomberb-cloud/normalizers.ts +5 -0
  398. package/src/sources/history-coverage.ts +22 -4
  399. package/src/sources/history-result.ts +3 -1
  400. package/src/sources/provider-errors.ts +2 -1
  401. package/src/sources/provider-router/batches.ts +18 -2
  402. package/src/sources/provider-router/financials.ts +5 -0
  403. package/src/sources/provider-router/index.ts +2 -1
  404. package/src/state/app/context/index.tsx +3 -2
  405. package/src/test-support/builtin-panes.ts +31 -0
  406. package/src/tickers/listing-venues.ts +22 -0
  407. package/src/tickers/search/index.ts +7 -2
  408. package/src/tickers/search/ranking.ts +19 -0
  409. package/src/time-series/chart-data.ts +2 -0
  410. package/src/time-series/parsed-history-cache.ts +41 -11
  411. package/src/time-series/resolve.ts +47 -15
  412. package/src/types/config.ts +1 -1
  413. package/src/types/data-provider.ts +13 -1
  414. package/src/types/financials.ts +35 -0
  415. package/src/types/headless.ts +22 -0
  416. package/src/types/plugin.ts +46 -6
  417. package/src/types/price-history.ts +2 -0
  418. package/src/ui/host.tsx +6 -0
  419. package/src/utils/crypto-pair.ts +1 -1
  420. package/src/utils/currency-pair.ts +66 -0
  421. package/src/utils/decimal-comma.ts +57 -0
  422. package/src/utils/financial-statements.ts +25 -0
  423. package/src/utils/fundamentals.ts +41 -1
  424. package/src/utils/guards.ts +25 -0
  425. package/src/utils/local-utc-offset.ts +37 -0
  426. package/src/utils/plugin-proxy-hosts.json +8 -1
  427. package/src/components/chart/composite/crosshair-readout.tsx +0 -150
  428. package/src/plugins/builtin/chat/message/ai-badge.tsx +0 -18
  429. package/src/plugins/builtin/credit-boards/pane.tsx +0 -30
@@ -13,7 +13,8 @@ import type { DesktopWindowBridge } from "../../types/desktop-window";
13
13
  import { requestAccountManagementTab } from "../../plugins/builtin/account-management/navigation";
14
14
  import { openTeamPane } from "../../plugins/builtin/cloud/team/pane-request";
15
15
  import { chatController } from "../../plugins/builtin/chat/controller";
16
- import { getShare } from "../../shares/api";
16
+ import { getShare, type ShareRecord } from "../../shares/api";
17
+ import { openShareLink, registerShareOpener } from "../../shares/open";
17
18
  import { openPaneShare } from "../../shares/pane";
18
19
  import { PluginOffError, notifyPluginOff } from "../../plugins/plugin-off";
19
20
  import { materializeMarketplaceLayout } from "../../shares/portable-layout";
@@ -514,15 +515,44 @@ async function handleOpenLayout(
514
515
  notifySuccess(options.pluginRegistry, `Layout "${entry.name}" added.`);
515
516
  }
516
517
 
517
- async function handleOpenShare(
518
- action: Extract<DesktopDeepLinkAction, { type: "open-share" }>,
518
+ /**
519
+ * Opens a pane share in this app: the web app's `?share=` hand-off, a
520
+ * gloomberb://share link and a share link in chat all come through here.
521
+ * With `fallback`, a share this app cannot read or that holds a snapshot
522
+ * goes there (its web page) instead of to an error.
523
+ */
524
+ async function openShareInApp(
525
+ id: string,
519
526
  pluginRegistry: PluginRegistry,
527
+ options: { retry: () => void; fallback?: () => void },
520
528
  ): Promise<void> {
521
- const share = await getShare(action.id, fetch, { trackView: false });
522
- if (!share) throw new Error("This share is unavailable or has expired.");
523
- if (share.kind !== "pane") throw new Error("This link contains a snapshot, not a live pane.");
524
- await openPaneShare(pluginRegistry, share.data);
525
- notifySuccess(pluginRegistry, action.message);
529
+ let share: ShareRecord | null;
530
+ try {
531
+ share = await getShare(id, fetch, { trackView: false });
532
+ } catch (error) {
533
+ if (options.fallback) options.fallback();
534
+ else notifyError(pluginRegistry, error instanceof Error ? error.message : "Could not open shared pane.");
535
+ return;
536
+ }
537
+ if (!share) {
538
+ notifyError(pluginRegistry, "This share is unavailable or has expired.");
539
+ return;
540
+ }
541
+ if (share.kind !== "pane") {
542
+ if (options.fallback) options.fallback();
543
+ else notifyError(pluginRegistry, "This link contains a snapshot, not a live pane.");
544
+ return;
545
+ }
546
+ try {
547
+ await openPaneShare(pluginRegistry, share.data);
548
+ notifySuccess(pluginRegistry, "Opened shared pane.");
549
+ } catch (error) {
550
+ if (error instanceof PluginOffError) {
551
+ notifyPluginOff(pluginRegistry, error, options.retry);
552
+ return;
553
+ }
554
+ notifyError(pluginRegistry, error instanceof Error ? error.message : "Could not open shared pane.");
555
+ }
526
556
  }
527
557
 
528
558
  export function handleDesktopDeepLink(rawUrl: string, options: DesktopDeepLinkHandlerOptions): void {
@@ -569,13 +599,7 @@ export function handleDesktopDeepLink(rawUrl: string, options: DesktopDeepLinkHa
569
599
  handleOpenNews(action, options.pluginRegistry);
570
600
  return;
571
601
  case "open-share":
572
- void handleOpenShare(action, options.pluginRegistry).catch((error) => {
573
- if (error instanceof PluginOffError) {
574
- notifyPluginOff(options.pluginRegistry, error, () => handleDesktopDeepLink(rawUrl, options));
575
- return;
576
- }
577
- notifyError(options.pluginRegistry, error instanceof Error ? error.message : "Could not open shared pane.");
578
- });
602
+ void openShareInApp(action.id, options.pluginRegistry, { retry: () => handleDesktopDeepLink(rawUrl, options) });
579
603
  return;
580
604
  case "open-layout":
581
605
  void handleOpenLayout(action, options).catch((error) => {
@@ -600,6 +624,15 @@ export function useDesktopDeepLinkRuntime({
600
624
  pluginRegistry: PluginRegistry;
601
625
  stateRef: { current: AppState };
602
626
  }) {
627
+ // Share links opened inside the app (chat's share chips) take the same path, in every renderer.
628
+ useEffect(() => {
629
+ if (!initialized) return;
630
+ return registerShareOpener((id, { fallback }) => openShareInApp(id, pluginRegistry, {
631
+ fallback,
632
+ retry: () => openShareLink(id, fallback),
633
+ }));
634
+ }, [initialized, pluginRegistry]);
635
+
603
636
  useEffect(() => {
604
637
  if (!initialized || desktopWindowKind === "detached" || !desktopDeepLinkBridge) return;
605
638
  return desktopDeepLinkBridge.subscribe((deeplink) => {
@@ -10,13 +10,25 @@ import {
10
10
  performUpdate,
11
11
  type ReleaseInfo,
12
12
  type UpdateCheckResult,
13
+ type UpdateProgress,
13
14
  } from "../../updater";
14
15
  import { VERSION } from "../../version";
16
+ import { debugLog } from "../../utils/debug-log";
15
17
  import { reportCrash } from "../../telemetry/crash-reports";
16
18
  import { runAutomated } from "../../telemetry/usage-counts";
17
19
 
18
20
  const UPDATE_CHECK_INTERVAL_MS = 60 * 60_000; // hourly
19
21
 
22
+ const updateLog = debugLog.createLogger("update");
23
+
24
+ /**
25
+ * The header and the command bar show a failed update's message, and the header
26
+ * cuts it to its room: the log keeps what the updater actually said.
27
+ */
28
+ function logUpdateFailure(progress: UpdateProgress): void {
29
+ if (progress.phase === "error") updateLog.error("update failed", { error: progress.error });
30
+ }
31
+
20
32
  /** An update is being downloaded, applied, or is downloaded and waiting for a restart. */
21
33
  function updateInFlight(progress: AppState["updateProgress"]): boolean {
22
34
  return progress?.phase === "downloading" || progress?.phase === "replacing" || progress?.phase === "ready";
@@ -53,6 +65,7 @@ export function useAppUpdateRuntime({
53
65
  const startUpdate = useCallback((release: ReleaseInfo) => {
54
66
  dispatch({ type: "SET_UPDATE_PROGRESS", progress: { phase: "downloading", percent: 0 } });
55
67
  void performUpdate(release, (progress) => {
68
+ logUpdateFailure(progress);
56
69
  dispatch({ type: "SET_UPDATE_PROGRESS", progress });
57
70
  });
58
71
  }, [dispatch]);
@@ -66,6 +79,7 @@ export function useAppUpdateRuntime({
66
79
  dispatch({ type: "SET_UPDATE_PROGRESS", progress: { phase: "replacing" } });
67
80
  void applyUpdate((progress) => {
68
81
  if (progress.phase === "error") restartingRef.current = false;
82
+ logUpdateFailure(progress);
69
83
  dispatch({ type: "SET_UPDATE_PROGRESS", progress });
70
84
  });
71
85
  }, [dispatch, enabled, isDetachedWindow, stateRef]);
@@ -91,6 +105,8 @@ export function useAppUpdateRuntime({
91
105
  return { kind: "error", error: message || "Update check failed" };
92
106
  });
93
107
 
108
+ if (result.kind === "error") updateLog.warn("update check failed", { error: result.error });
109
+
94
110
  if (!manual) {
95
111
  if (result.kind === "available") {
96
112
  dispatch({ type: "SET_UPDATE_AVAILABLE", release: result.release });
@@ -40,7 +40,7 @@
40
40
  "src/layout-marketplace/gallery.tsx": ["cloud"],
41
41
  "src/layout-marketplace/linked-sync.ts": ["cloud"],
42
42
  "src/layout/desks.ts": ["chart-composer"],
43
- "src/plugins/catalog-browser.ts": ["alerts","analytics","attention","awards","backtest","bond-calculator","catalysts","cds","central-bank-rates","changelog","chart-composer","clinical-trials","cloud","comment-letters","company-attention","company-kpis","connections","correlation","cot","cpi","credit-boards","credit-conditions","credit-documents","crypto-board","debt-maturities","dividend-yield","docs","doe","earnings-calls","earnings-ripple","econ","econ-statistics","equity-screener","estimate-revisions","executives","exposure","fear-greed","filing-events","futures","futures-curve","fx-matrix","gpu","help","insider","ipo-calendar","iv-history","jobs","kelly-sizer","layout-manager","macro-day","market-halts","market-heatmap","market-valuation","members","mna","money-markets","news","openfda","options","options-calculator","options-positioning","options-scenario","pe-band","perps","portfolio-list","power","rate-path","realized-vol","relative-rotation","research","research-search","revenue-breakdown","reverse-dcf","risk-factors","scanner","seasonality","sec","sectors","short-interest","short-volume","social-mentions","supply-chain","ticker-detail","time-sales","treasury-auctions","vol-surface","volatility","world-indices","world-venue-map","yield-curve"],
43
+ "src/plugins/catalog-browser.ts": ["alerts","analytics","attention","awards","backtest","bond-calculator","catalysts","cds","central-bank-rates","changelog","chart-composer","clinical-trials","cloud","comment-letters","commodities-energy","commodity-balances","company-attention","company-kpis","connections","correlation","cot","cpi","credit-boards","credit-conditions","credit-documents","crypto-board","debt-maturities","dividend-yield","docs","doe","earnings-calls","earnings-ripple","econ","econ-statistics","equity-screener","estimate-revisions","executives","exposure","fear-greed","filing-events","fixed-income","futures","futures-curve","fx-matrix","gpu","help","insider","ipo-calendar","iv-history","jobs","jodi","kelly-sizer","layout-manager","macro-day","market-halts","market-heatmap","market-valuation","members","mna","money-markets","news","openfda","options","options-calculator","options-positioning","options-scenario","pe-band","perps","portfolio-list","power","primary-dealers","rate-path","realized-vol","relative-rotation","research","research-search","revenue-breakdown","reverse-dcf","risk-factors","scanner","seasonality","sec","sectors","short-interest","short-volume","social-mentions","supply-chain","ticker-detail","time-sales","trace-bonds","treasury-auctions","treasury-daily","vol-surface","volatility","world-indices","world-venue-map","yield-curve"],
44
44
  "src/plugins/catalog-ui.ts": ["alerts","clinical-trials","cloud","comment-letters","custom-view","fear-greed","ipo-calendar","market-halts","market-heatmap","members","news","notes","openfda","research-search"],
45
45
  "src/plugins/catalog.ts": ["debug"],
46
46
  "src/remote/controller.ts": ["custom-view"],
@@ -0,0 +1,102 @@
1
+ {
2
+ "src/components/layout/pane/header.tsx": {
3
+ "uses": 2,
4
+ "why": "The title bar's inset for the window controls comes from the shell's header geometry, which the terminal shares, in cells."
5
+ },
6
+ "src/components/layout/pane/sizing.ts": {
7
+ "uses": 3,
8
+ "why": "Panes receive their size in cells (PaneProps); this gives the desktop's taller chrome row in those cells."
9
+ },
10
+ "src/components/layout/shell/drag/slide.ts": {
11
+ "uses": 4,
12
+ "why": "A dragged pane moves by offsets of the saved layout, which is kept in cells."
13
+ },
14
+ "src/components/layout/shell/pane/layers.tsx": {
15
+ "uses": 6,
16
+ "why": "Pane rectangles in the saved layout are in cells; this is where they become pixels."
17
+ },
18
+ "src/components/ui/select-button.tsx": {
19
+ "uses": 2,
20
+ "why": "A select's width is given in cells by the pane, as its terminal button's is."
21
+ },
22
+ "src/renderers/dom/data-table/dom.ts": {
23
+ "uses": 26,
24
+ "why": "The table's scroll and pointer API speaks rows and columns to callers shared with the terminal (j/k, paging, visible range); a row of text is one line tall."
25
+ },
26
+ "src/renderers/dom/data-table/index.tsx": {
27
+ "uses": 19,
28
+ "why": "Every pane gives its columns in cells (DataTableColumn.width) and rows are one line tall: the horizontal extent, the overflow decision, virtualization and paging follow from them."
29
+ },
30
+ "src/renderers/dom/data-table/row.tsx": {
31
+ "uses": 10,
32
+ "why": "Column widths and gaps are cells in the column model every pane uses; a row is one line tall."
33
+ },
34
+ "src/renderers/dom/desktop/controls.tsx": {
35
+ "uses": 6,
36
+ "why": "Callers give a button's size in cells; a framed field keeps its text one cell off its frame, as the terminal's does."
37
+ },
38
+ "src/renderers/dom/desktop/list-view.tsx": {
39
+ "uses": 2,
40
+ "why": "The caller gives a list's height in rows."
41
+ },
42
+ "src/renderers/dom/desktop/query-bar.tsx": {
43
+ "uses": 1,
44
+ "why": "The pane gives its search field's width in cells."
45
+ },
46
+ "src/renderers/dom/desktop/select-field.tsx": {
47
+ "uses": 2,
48
+ "why": "An inline select is one row tall, the row it sits in."
49
+ },
50
+ "src/renderers/dom/dom-ui-host.tsx": {
51
+ "uses": 6,
52
+ "why": "Exposes the cell size to components that still size in cells (chart bitmaps, the company logo)."
53
+ },
54
+ "src/renderers/dom/host/input.tsx": {
55
+ "uses": 3,
56
+ "why": "Callers give a field's width and height in cells; a one-line field is one row tall."
57
+ },
58
+ "src/renderers/dom/host/live-frame.ts": {
59
+ "uses": 8,
60
+ "why": "A resize's live frame is in cells, the pane's unit, until the pane renders at the new size."
61
+ },
62
+ "src/renderers/dom/host/mouse.ts": {
63
+ "uses": 10,
64
+ "why": "Pointer handlers receive cell coordinates, the event API every renderer shares."
65
+ },
66
+ "src/renderers/dom/host/scroll-box.tsx": {
67
+ "uses": 22,
68
+ "why": "The scroller's API (scrollTop, scrollBy, viewport) is in rows and columns for callers shared with the terminal; content insets are given in cells."
69
+ },
70
+ "src/renderers/dom/host/size-observer.ts": {
71
+ "uses": 6,
72
+ "why": "Observed sizes are reported in cells, as callers expect."
73
+ },
74
+ "src/renderers/dom/host/style.ts": {
75
+ "uses": 27,
76
+ "why": "Maps the shared Box and Text props that components give in cells (width, height, padding, margin, gap, position) to pixels."
77
+ },
78
+ "src/renderers/dom/host/surface-fit.ts": {
79
+ "uses": 4,
80
+ "why": "A chart takes its box when its cells come within a cell of it, because panes still count their space in cells."
81
+ },
82
+ "src/renderers/dom/host/tabs.tsx": {
83
+ "uses": 2,
84
+ "why": "A tab strip is one row tall, the row every caller reserves for it."
85
+ },
86
+ "src/renderers/dom/host/text.tsx": {
87
+ "uses": 2,
88
+ "why": "The ASCII wordmark draws one glyph per cell."
89
+ },
90
+ "src/renderers/dom/input-host.tsx": {
91
+ "uses": 4,
92
+ "why": "The viewport is reported in cells (useViewport), the unit every renderer shares."
93
+ },
94
+ "src/renderers/dom/native-renderer.ts": {
95
+ "uses": 4,
96
+ "why": "The renderer's width and height are reported in cells, the unit every renderer shares."
97
+ },
98
+ "src/renderers/dom/styles.css": {
99
+ "uses": 14,
100
+ "why": "Defines the cell size and the base line height; status, footer and hint lines are one row; a few insets are one cell, as the terminal's (the floating pane's two-cell grip, a query bar segment)."
101
+ }
102
+ }
@@ -1,8 +1,9 @@
1
+ import type { ResolvedPane } from "../layout/pane-manager";
1
2
  import type { PluginRegistry } from "../plugins/registry";
2
- import { paneShowsPersonalData } from "../shares/personal-pane";
3
- import type { AppConfig, PaneInstanceConfig } from "../types/config";
3
+ import { panePersonalData } from "../shares/personal-pane";
4
+ import { resolveTickerForPane, type AppState } from "../state/app/context";
5
+ import type { LayoutConfig } from "../types/config";
4
6
  import type { PaneSharePrivateFields } from "../types/plugin";
5
- import type { TickerRecord } from "../types/ticker";
6
7
  import type { AskGloomPaneAttachment } from "./pane-request";
7
8
 
8
9
  /** A handful of the pane's own settings is enough to say how it is set up. */
@@ -50,14 +51,13 @@ function functionCode(registry: Pick<PluginRegistry, "paneTemplates">, paneType:
50
51
  }
51
52
 
52
53
  export interface PaneAttachmentSource {
53
- registry: Pick<PluginRegistry, "panes" | "paneTemplates" | "brokers" | "getPanePluginId" | "getBrokerPluginId">;
54
- instance: PaneInstanceConfig;
54
+ registry: Pick<PluginRegistry, "paneTemplates" | "brokers" | "getPanePluginId" | "getBrokerPluginId">;
55
+ pane: Pick<ResolvedPane, "instance" | "def">;
56
+ state: AppState;
57
+ /** The layout the pane is in, as the share path reads it for `personalData`. */
58
+ layout: LayoutConfig;
55
59
  /** What the header calls it. */
56
60
  title: string;
57
- symbol: string | null;
58
- paneState?: Readonly<Record<string, unknown>>;
59
- config: Pick<AppConfig, "portfolios" | "brokerInstances">;
60
- tickers?: ReadonlyMap<string, TickerRecord>;
61
61
  /** The tab each of its tab strips has open, outermost first. */
62
62
  activeTabs?: readonly string[];
63
63
  visibleText: string | null;
@@ -70,16 +70,17 @@ export interface PaneAttachmentSource {
70
70
  */
71
71
  export function buildAskGloomPaneAttachment({
72
72
  registry,
73
- instance,
73
+ pane,
74
+ state,
75
+ layout,
74
76
  title,
75
- symbol,
76
- paneState,
77
- config,
78
- tickers,
79
77
  activeTabs = [],
80
78
  visibleText,
81
79
  }: PaneAttachmentSource): AskGloomPaneAttachment {
82
- const privateFields = registry.panes.get(instance.paneId)?.portableShare?.private;
80
+ const { instance } = pane;
81
+ const symbol = resolveTickerForPane(state, instance.instanceId);
82
+ const paneState = state.paneState[instance.instanceId];
83
+ const privateFields = pane.def.portableShare?.private;
83
84
  const args: Record<string, string> = {};
84
85
  const tabs = activeTabs.map((tab) => tab.trim()).filter(Boolean);
85
86
  if (tabs.length > 0) args.tab = tabs.join(" > ").slice(0, MAX_ARG_VALUE_LENGTH);
@@ -100,14 +101,17 @@ export function buildAskGloomPaneAttachment({
100
101
  symbol,
101
102
  args,
102
103
  visibleText,
103
- personal: showsPersonalData({ registry, instance, paneState, symbol, config, tickers }),
104
+ personal: showsPersonalData({ registry, state, layout, pane }),
104
105
  };
105
106
  }
106
107
 
107
- /** A pane that cannot be read for it counts as personal: its contents then wait for a yes. */
108
- function showsPersonalData(source: Parameters<typeof paneShowsPersonalData>[0]): boolean {
108
+ /**
109
+ * Whatever the pane shows as personal data, as sharing its picture reads it.
110
+ * A pane that cannot be read for it counts as personal: its contents then wait for a yes.
111
+ */
112
+ function showsPersonalData(source: Parameters<typeof panePersonalData>[0]): boolean {
109
113
  try {
110
- return paneShowsPersonalData(source);
114
+ return panePersonalData(source) !== null;
111
115
  } catch {
112
116
  return true;
113
117
  }
@@ -23,8 +23,8 @@ export interface AskGloomPaneAttachment {
23
23
  /** Its text as drawn when asked, bounded; null when none could be read. */
24
24
  visibleText: string | null;
25
25
  /**
26
- * It shows holdings, balances or account data, so Ask Gloom asks before
27
- * anything it shows leaves the device.
26
+ * The pane shows personal data (`PaneDef.personalData`), so Ask Gloom
27
+ * asks before anything it shows leaves the device.
28
28
  */
29
29
  personal: boolean;
30
30
  }
@@ -19,18 +19,44 @@ export function isSpreadSeries(seriesId: string): boolean {
19
19
  }
20
20
 
21
21
  /**
22
- * The field a series' observations export under, named by their unit: a
23
- * spread in basis points (`value_bp`), a percent (`value_pct`), any other
24
- * unit slugged from FRED's own (`Index 1982-1984=100` is
25
- * `value_index_1982_1984_100`), and `value_unit_unknown` when FRED's
26
- * metadata did not come back. Never a bare `value`.
22
+ * The unit a series' observations export in, for the `currency_or_unit`
23
+ * column beside its bare `value`: `bp` for a spread (exported in basis
24
+ * points), `pct` for a percent, otherwise FRED's own units, with "annual
25
+ * rate" as the text header says it. Null when FRED's metadata did not come
26
+ * back.
27
27
  */
28
- export function fredValueKey(seriesId: string, units: string | null | undefined): string {
29
- if (isSpreadSeries(seriesId)) return "value_bp";
30
- const unit = units?.trim() ?? "";
31
- if (/^percent$/i.test(unit)) return "value_pct";
32
- const slug = unit.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
33
- return `value_${slug || "unit_unknown"}`;
28
+ export function fredUnit(seriesId: string, info: { units?: string | null; seasonalAdjustment?: string | null } | null | undefined): string | null {
29
+ if (isSpreadSeries(seriesId)) return "bp";
30
+ const units = info?.units?.trim();
31
+ if (!units) return null;
32
+ if (/^percent$/i.test(units)) return "pct";
33
+ const annualRate = /annual rate/i.test(info?.seasonalAdjustment ?? "") && !/annual rate/i.test(units);
34
+ return `${units}${annualRate ? ", annual rate" : ""}`;
35
+ }
36
+
37
+ /** How often a series observes, in FRED's word lower-cased: `Weekly, Ending Friday` is `weekly`. Null when FRED does not say. */
38
+ export function fredInterval(frequency: string | null | undefined): string | null {
39
+ const word = frequency?.split(",")[0]?.trim().toLowerCase();
40
+ return word || null;
41
+ }
42
+
43
+ /**
44
+ * The observations from the series' first one on: Gloom Cloud answers a
45
+ * `--start` before a series begins with a blank row for every day in between
46
+ * (DEXSFUS from 1975 has five years of them before 1980-01-02). FRED's
47
+ * `observationStart` says where it begins; without it the blank rows before
48
+ * the first value go. Blank rows after it (a holiday's ".") stay.
49
+ */
50
+ export function fredObservationsFromStart<T extends { date: string; value: number | null }>(
51
+ observations: readonly T[],
52
+ seriesStart: string | null | undefined,
53
+ ): T[] {
54
+ const first = seriesStart && /^\d{4}-\d{2}-\d{2}$/.test(seriesStart)
55
+ ? seriesStart
56
+ : observations.reduce<string | null>((earliest, observation) => (
57
+ observation.value != null && Number.isFinite(observation.value) && (!earliest || observation.date < earliest) ? observation.date : earliest
58
+ ), null);
59
+ return first ? observations.filter((observation) => observation.date >= first) : [...observations];
34
60
  }
35
61
 
36
62
  /**
@@ -144,3 +170,43 @@ export function fredEmptyMessage(startDate: string, latest: { date: string; valu
144
170
  ? `Latest observation: ${latest.date}${latest.value == null ? "" : ` (${latest.value})`}. Nothing on or after ${startDate}.`
145
171
  : "No results.";
146
172
  }
173
+
174
+ /** What an export of nothing on or after `--start` says to try instead: a start that reaches the latest observation. */
175
+ export function fredEmptyAdvice(seriesId: string, latest: { date: string } | null): string {
176
+ return latest
177
+ ? `Latest observation: ${latest.date}. Try: gloomberb fred ${seriesId} --start ${latest.date.slice(0, 4)}`
178
+ : `No observations came back for ${seriesId}. Try an earlier --start, or gloomberb fred --list for the series served.`;
179
+ }
180
+
181
+ /** A series as `fred --list` shows it. */
182
+ export interface FredListRow {
183
+ id: string;
184
+ title: string;
185
+ group: string;
186
+ }
187
+
188
+ /** How well a word of the filter matches a series, best first: its id or group name, a word of its group or title, the start of its id, then any text. */
189
+ function matchTier(term: string, row: FredListRow, words: readonly string[]): number {
190
+ if (row.id.toLowerCase() === term || row.group.toLowerCase() === term) return 0;
191
+ if (words.some((word) => word.startsWith(term))) return 1;
192
+ if (row.id.toLowerCase().startsWith(term)) return 2;
193
+ return 3;
194
+ }
195
+
196
+ /**
197
+ * The series that carry every word of the filter in their id, title or group,
198
+ * the ones it names first: a group or a word of the title ahead of the id that
199
+ * only has it inside (`fx` is the FX group, not FDEFX, whose id spells it).
200
+ * Catalog order breaks ties; an empty filter keeps the catalog as it is.
201
+ */
202
+ export function filterFredSeries(rows: readonly FredListRow[], filter: string): FredListRow[] {
203
+ const terms = filter.toLowerCase().split(/\s+/).filter(Boolean);
204
+ const ranked = rows.flatMap((row, index) => {
205
+ const text = `${row.id} ${row.title} ${row.group}`.toLowerCase();
206
+ if (!terms.every((term) => text.includes(term))) return [];
207
+ const words = `${row.title} ${row.group}`.toLowerCase().split(/[^a-z0-9]+/).filter(Boolean);
208
+ const tiers = terms.map((term) => matchTier(term, row, words));
209
+ return [{ row, index, worst: Math.max(0, ...tiers), total: tiers.reduce((sum, tier) => sum + tier, 0) }];
210
+ });
211
+ return ranked.sort((a, b) => a.worst - b.worst || a.total - b.total || a.index - b.index).map((entry) => entry.row);
212
+ }