gloomberb 0.18.1 → 0.18.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 (416) hide show
  1. package/package.json +1 -1
  2. package/patches/electrobun@1.18.1.patch +106 -5
  3. package/src/api-client/commodity-balances.ts +6 -0
  4. package/src/api-client/company-kpis.ts +9 -0
  5. package/src/api-client/crypto-markets.ts +9 -0
  6. package/src/api-client/data.ts +21 -5
  7. package/src/api-client/equity-screener.ts +2 -0
  8. package/src/api-client/index.ts +2 -0
  9. package/src/api-client/request.ts +5 -3
  10. package/src/api-client/telemetry.ts +17 -0
  11. package/src/api-client/types.ts +5 -0
  12. package/src/app/global-shortcuts.ts +1 -16
  13. package/src/app/pane-runtime/plugin-bindings.ts +3 -1
  14. package/src/app/pane-runtime/ticker-open-runtime.ts +17 -5
  15. package/src/app/runtime/plugin-bindings.ts +6 -2
  16. package/src/app/runtime/update.ts +28 -8
  17. package/src/architecture/pane-tabs-allowlist.json +65 -0
  18. package/src/brokers/logos.ts +66 -0
  19. package/src/brokers/profile-form.ts +3 -0
  20. package/src/brokers/remote-broker-adapter.ts +13 -3
  21. package/src/capabilities/index.ts +1 -1
  22. package/src/capabilities/service-capabilities.ts +7 -1
  23. package/src/cli/base-converter.ts +60 -18
  24. package/src/cli/commands/market.ts +354 -187
  25. package/src/cli/commands/news-search.ts +35 -8
  26. package/src/cli/commands/system.ts +35 -1
  27. package/src/cli/commands/ticker.ts +167 -60
  28. package/src/cli/currency-hints.ts +27 -0
  29. package/src/cli/fx-pair.ts +41 -4
  30. package/src/cli/glossary.ts +57 -4
  31. package/src/cli/help.ts +1 -1
  32. package/src/cli/helpers.ts +36 -3
  33. package/src/cli/index.ts +12 -1
  34. package/src/cli/issuer-membership.ts +103 -0
  35. package/src/cli/known-gaps.ts +37 -1
  36. package/src/cli/listing-arg.ts +51 -3
  37. package/src/cli/name-entries.ts +51 -12
  38. package/src/cli/pane-functions/capabilities.ts +2 -0
  39. package/src/cli/pane-functions/catalog.ts +19 -1
  40. package/src/cli/pane-functions/discovery.ts +15 -1
  41. package/src/cli/pane-functions/dom.ts +11 -5
  42. package/src/cli/pane-functions/freshness.ts +4 -4
  43. package/src/cli/pane-functions/function-help.ts +27 -3
  44. package/src/cli/pane-functions/headless.ts +31 -11
  45. package/src/cli/pane-functions/index.ts +67 -7
  46. package/src/cli/pane-functions/lock-note.ts +7 -4
  47. package/src/cli/pane-functions/resolver.ts +21 -6
  48. package/src/cli/quote-answer.ts +179 -0
  49. package/src/cli/report-tables.ts +136 -18
  50. package/src/cli/result.ts +12 -4
  51. package/src/cli/series-search.ts +2 -1
  52. package/src/cli/symbol-list.ts +47 -11
  53. package/src/cli/types.ts +4 -0
  54. package/src/components/chart/composite/composite-chart.tsx +25 -7
  55. package/src/components/chart/composite/drawing-store.ts +26 -6
  56. package/src/components/chart/composite/format.ts +13 -1
  57. package/src/components/chart/composite/interactions.ts +2 -1
  58. package/src/components/chart/composite/level-labels.ts +30 -0
  59. package/src/components/chart/composite/levels.ts +17 -6
  60. package/src/components/chart/composite/panel-surface.tsx +69 -8
  61. package/src/components/chart/composite/types.ts +6 -0
  62. package/src/components/chart/composite/use-composite-chart-keyboard.ts +1 -1
  63. package/src/components/command-bar/routes/root/command-items.ts +32 -6
  64. package/src/components/command-bar/routes/ticker-search/actions.ts +6 -0
  65. package/src/components/data-table/view.tsx +23 -3
  66. package/src/components/index.ts +3 -0
  67. package/src/components/layout/detached-pane-shell.tsx +1 -0
  68. package/src/components/layout/floating-pane.tsx +1 -0
  69. package/src/components/layout/header.tsx +228 -55
  70. package/src/components/layout/pane/content.tsx +16 -14
  71. package/src/components/layout/pane/footer/index.tsx +22 -3
  72. package/src/components/layout/pane/footer/model.ts +99 -4
  73. package/src/components/layout/pane/header-tabs.tsx +15 -1
  74. package/src/components/layout/pane/header.tsx +10 -3
  75. package/src/components/layout/pane/index.tsx +1 -0
  76. package/src/components/layout/pane/pane-tabs.tsx +9 -1
  77. package/src/components/onboarding/onboarding-frame.tsx +9 -11
  78. package/src/components/onboarding/use-onboarding-broker-form.ts +5 -2
  79. package/src/components/onboarding/wizard-model.ts +10 -3
  80. package/src/components/ui/button.tsx +11 -4
  81. package/src/components/ui/choice-dialog.tsx +8 -5
  82. package/src/components/ui/display.tsx +11 -6
  83. package/src/components/ui/fields.tsx +100 -20
  84. package/src/components/ui/icon.tsx +6 -1
  85. package/src/components/ui/list-row-lines.tsx +87 -0
  86. package/src/components/ui/list-view.tsx +14 -0
  87. package/src/components/ui/logo-mark.tsx +64 -0
  88. package/src/components/ui/number-step.ts +54 -0
  89. package/src/components/ui/select-button.tsx +5 -4
  90. package/src/components/ui/select-field.tsx +3 -3
  91. package/src/components/ui/stat-grid.tsx +28 -3
  92. package/src/components/ui/status.tsx +20 -4
  93. package/src/components/ui/tabs.tsx +9 -1
  94. package/src/components/ui/toggle.tsx +17 -3
  95. package/src/core/app-services.ts +4 -0
  96. package/src/core/state/app/instrument.ts +8 -3
  97. package/src/data/config/store/normalize.ts +4 -1
  98. package/src/data/plugin-cache.ts +32 -6
  99. package/src/docs/snapshot.json +5 -5
  100. package/src/i18n/es.ts +31 -3
  101. package/src/i18n/ja.ts +31 -3
  102. package/src/i18n/ko.ts +31 -3
  103. package/src/i18n/zh-cn.ts +32 -4
  104. package/src/i18n/zh-tw.ts +32 -4
  105. package/src/market-data/commodity-names.ts +80 -0
  106. package/src/market-data/coordinator/auxiliary.ts +3 -1
  107. package/src/market-data/coordinator/chart.ts +3 -1
  108. package/src/market-data/futures-board.ts +38 -1
  109. package/src/market-data/futures-contract.ts +91 -0
  110. package/src/market-data/fx-closes.ts +78 -1
  111. package/src/market-data/market/change-basis.ts +219 -0
  112. package/src/market-data/market/format.ts +28 -12
  113. package/src/market-data/market/freshness.ts +38 -0
  114. package/src/market-data/market/status.ts +55 -9
  115. package/src/market-data/market/year-to-date.ts +46 -0
  116. package/src/market-data/quotes/contributions.ts +12 -0
  117. package/src/market-data/quotes/delisting.ts +34 -0
  118. package/src/market-data/quotes/live-valuation.ts +23 -0
  119. package/src/market-data/quotes/resolution.ts +8 -2
  120. package/src/market-data/tax-equivalent.ts +10 -2
  121. package/src/notifications/app-notifier.ts +2 -2
  122. package/src/notifications/notification-log.ts +8 -6
  123. package/src/plugins/builtin/alerts/alert-engine.ts +3 -1
  124. package/src/plugins/builtin/alerts/cli.ts +26 -18
  125. package/src/plugins/builtin/alerts/events.ts +1 -0
  126. package/src/plugins/builtin/alerts/headless.ts +1 -1
  127. package/src/plugins/builtin/alerts/index.tsx +13 -4
  128. package/src/plugins/builtin/alerts/research-rules.ts +1 -0
  129. package/src/plugins/builtin/analytics/risk-pane.tsx +10 -7
  130. package/src/plugins/builtin/attention/pane.tsx +16 -14
  131. package/src/plugins/builtin/attention/trending.tsx +8 -6
  132. package/src/plugins/builtin/awards/pane.tsx +6 -6
  133. package/src/plugins/builtin/backtest/client.ts +5 -2
  134. package/src/plugins/builtin/backtest/export.ts +1 -1
  135. package/src/plugins/builtin/backtest/window.ts +16 -3
  136. package/src/plugins/builtin/broker-manager/add-flow.tsx +40 -8
  137. package/src/plugins/builtin/broker-manager/detail.tsx +26 -1
  138. package/src/plugins/builtin/broker-manager/index.tsx +20 -9
  139. package/src/plugins/builtin/broker-manager/model.ts +5 -0
  140. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -1
  141. package/src/plugins/builtin/broker-manager/table.ts +5 -1
  142. package/src/plugins/builtin/broker-trading/account-picker.tsx +91 -0
  143. package/src/plugins/builtin/broker-trading/chart-levels.ts +54 -0
  144. package/src/plugins/builtin/broker-trading/command.ts +103 -0
  145. package/src/plugins/builtin/broker-trading/controller.ts +688 -0
  146. package/src/plugins/builtin/broker-trading/dock.tsx +184 -0
  147. package/src/plugins/builtin/broker-trading/headless.ts +76 -0
  148. package/src/plugins/builtin/broker-trading/index.tsx +77 -0
  149. package/src/plugins/builtin/broker-trading/order-feed.ts +383 -0
  150. package/src/plugins/builtin/broker-trading/orders-data.ts +133 -0
  151. package/src/plugins/builtin/broker-trading/orders.tsx +437 -0
  152. package/src/plugins/builtin/broker-trading/overview-orders.tsx +121 -0
  153. package/src/plugins/builtin/broker-trading/pane-model.ts +207 -0
  154. package/src/plugins/builtin/broker-trading/pane.tsx +507 -0
  155. package/src/plugins/builtin/broker-trading/strategy.ts +153 -0
  156. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +117 -0
  157. package/src/plugins/builtin/broker-trading/ticket.tsx +578 -0
  158. package/src/plugins/builtin/buildout/data-runtime.ts +1 -1
  159. package/src/plugins/builtin/buildout/format.ts +1 -0
  160. package/src/plugins/builtin/buildout/model/index.ts +16 -7
  161. package/src/plugins/builtin/buildout/pane/index.tsx +11 -7
  162. package/src/plugins/builtin/buildout/pane/status.tsx +3 -3
  163. package/src/plugins/builtin/catalysts/client.ts +2 -2
  164. package/src/plugins/builtin/catalysts/pane.tsx +3 -3
  165. package/src/plugins/builtin/chart-composer/cli-options.ts +19 -0
  166. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  167. package/src/plugins/builtin/chart-composer/headless-schema.ts +3 -3
  168. package/src/plugins/builtin/chart-composer/headless.ts +2 -1
  169. package/src/plugins/builtin/chart-composer/option-range.ts +39 -0
  170. package/src/plugins/builtin/chart-composer/pane.tsx +32 -11
  171. package/src/plugins/builtin/chart-composer/presets.ts +2 -2
  172. package/src/plugins/builtin/chart-composer/series-catalog.ts +4 -2
  173. package/src/plugins/builtin/chart-composer/shortcuts.ts +3 -3
  174. package/src/plugins/builtin/chart-composer/viewport-labels.ts +5 -2
  175. package/src/plugins/builtin/chat/index.tsx +1 -1
  176. package/src/plugins/builtin/chat/status-widget.tsx +1 -1
  177. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +6 -1
  178. package/src/plugins/builtin/chat/unread-inbox.ts +1 -1
  179. package/src/plugins/builtin/commodity-balances/client.ts +6 -4
  180. package/src/plugins/builtin/commodity-balances/headless.ts +41 -7
  181. package/src/plugins/builtin/commodity-balances/index.tsx +19 -4
  182. package/src/plugins/builtin/commodity-balances/model.ts +22 -0
  183. package/src/plugins/builtin/commodity-balances/pane.tsx +39 -9
  184. package/src/plugins/builtin/company-attention/app-rank.tsx +4 -5
  185. package/src/plugins/builtin/company-attention/pane.tsx +13 -11
  186. package/src/plugins/builtin/company-kpis/client.ts +3 -2
  187. package/src/plugins/builtin/company-kpis/headless.ts +5 -7
  188. package/src/plugins/builtin/company-kpis/model.ts +85 -2
  189. package/src/plugins/builtin/company-kpis/pane.tsx +30 -15
  190. package/src/plugins/builtin/composite-plugins.ts +3 -2
  191. package/src/plugins/builtin/correlation/headless.ts +2 -1
  192. package/src/plugins/builtin/correlation/relationship/model.ts +1 -0
  193. package/src/plugins/builtin/correlation/settings.ts +1 -0
  194. package/src/plugins/builtin/cot/headless.ts +1 -1
  195. package/src/plugins/builtin/cot/index.tsx +1 -1
  196. package/src/plugins/builtin/cot/model.ts +35 -12
  197. package/src/plugins/builtin/cot/pane.tsx +4 -3
  198. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
  199. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +2 -1
  200. package/src/plugins/builtin/credit-conditions/index.tsx +2 -1
  201. package/src/plugins/builtin/credit-documents/pane.tsx +8 -7
  202. package/src/plugins/builtin/crypto-board/client.ts +33 -2
  203. package/src/plugins/builtin/crypto-board/headless.ts +5 -2
  204. package/src/plugins/builtin/crypto-board/model.ts +28 -8
  205. package/src/plugins/builtin/debt-maturities/pane.tsx +4 -3
  206. package/src/plugins/builtin/dividend-yield/headless.ts +30 -8
  207. package/src/plugins/builtin/dividend-yield/provider-client.ts +3 -1
  208. package/src/plugins/builtin/doe/pane.tsx +1 -1
  209. package/src/plugins/builtin/earnings-calls/headless.ts +4 -4
  210. package/src/plugins/builtin/earnings-calls/lock-note.ts +12 -3
  211. package/src/plugins/builtin/econ/calendar-model.ts +35 -0
  212. package/src/plugins/builtin/econ/index.tsx +31 -4
  213. package/src/plugins/builtin/econ/print-watch.ts +279 -0
  214. package/src/plugins/builtin/equity-screener/headless.ts +30 -5
  215. package/src/plugins/builtin/equity-screener/model.ts +68 -2
  216. package/src/plugins/builtin/equity-screener/pane.tsx +129 -119
  217. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -2
  218. package/src/plugins/builtin/estimate-revisions/lock-note.ts +29 -7
  219. package/src/plugins/builtin/exposure/client.ts +4 -1
  220. package/src/plugins/builtin/exposure/editor.tsx +3 -3
  221. package/src/plugins/builtin/exposure/evidence.ts +4 -1
  222. package/src/plugins/builtin/exposure/holdings.ts +3 -1
  223. package/src/plugins/builtin/exposure/pane.tsx +43 -21
  224. package/src/plugins/builtin/filing-events/index.tsx +9 -3
  225. package/src/plugins/builtin/futures/table.tsx +6 -3
  226. package/src/plugins/builtin/futures-curve/pane.tsx +2 -2
  227. package/src/plugins/builtin/gpu/board.tsx +4 -1
  228. package/src/plugins/builtin/gpu/changes.tsx +10 -10
  229. package/src/plugins/builtin/gpu/equities.tsx +4 -1
  230. package/src/plugins/builtin/gpu/history.tsx +4 -2
  231. package/src/plugins/builtin/gpu/pane.tsx +5 -3
  232. package/src/plugins/builtin/holders/client.ts +2 -1
  233. package/src/plugins/builtin/holders/headless.ts +2 -1
  234. package/src/plugins/builtin/holders/thirteenf-match.ts +3 -1
  235. package/src/plugins/builtin/insider/base-values.ts +76 -0
  236. package/src/plugins/builtin/insider/client.ts +3 -0
  237. package/src/plugins/builtin/insider/headless.ts +48 -10
  238. package/src/plugins/builtin/insider/index.tsx +17 -8
  239. package/src/plugins/builtin/insider/issuer.ts +34 -4
  240. package/src/plugins/builtin/insider/signals-client.ts +2 -2
  241. package/src/plugins/builtin/insider/table-model.ts +35 -13
  242. package/src/plugins/builtin/insider/table.tsx +5 -2
  243. package/src/plugins/builtin/iv-history/events.ts +145 -0
  244. package/src/plugins/builtin/iv-history/format.ts +3 -0
  245. package/src/plugins/builtin/iv-history/headless.ts +91 -7
  246. package/src/plugins/builtin/jobs/headless.ts +2 -2
  247. package/src/plugins/builtin/jobs/lock-note.ts +14 -3
  248. package/src/plugins/builtin/jodi/pane.tsx +4 -4
  249. package/src/plugins/builtin/market-halts/model.ts +31 -6
  250. package/src/plugins/builtin/market-halts/pane.tsx +3 -0
  251. package/src/plugins/builtin/market-movers/client.ts +4 -3
  252. package/src/plugins/builtin/market-movers/screener.ts +4 -3
  253. package/src/plugins/builtin/market-valuation/pane.tsx +2 -1
  254. package/src/plugins/builtin/members/pane.tsx +8 -3
  255. package/src/plugins/builtin/news/wire/feed-config.ts +29 -12
  256. package/src/plugins/builtin/news/wire/index.tsx +7 -2
  257. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +11 -3
  258. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -2
  259. package/src/plugins/builtin/news/wire/rss/source.ts +13 -4
  260. package/src/plugins/builtin/notes/cli.ts +1 -1
  261. package/src/plugins/builtin/notes/export.ts +17 -6
  262. package/src/plugins/builtin/notes/files.ts +16 -5
  263. package/src/plugins/builtin/options/enrichment-model.ts +10 -9
  264. package/src/plugins/builtin/options/footer.ts +25 -20
  265. package/src/plugins/builtin/options/live-quotes.ts +33 -18
  266. package/src/plugins/builtin/options/model-spot.ts +70 -4
  267. package/src/plugins/builtin/options/view.tsx +118 -35
  268. package/src/plugins/builtin/options-calculator/pane.tsx +7 -4
  269. package/src/plugins/builtin/options-positioning/headless.ts +11 -3
  270. package/src/plugins/builtin/options-positioning/model.ts +24 -0
  271. package/src/plugins/builtin/options-positioning/pane.tsx +10 -12
  272. package/src/plugins/builtin/options-scenario/client.ts +19 -1
  273. package/src/plugins/builtin/options-scenario/headless.ts +13 -6
  274. package/src/plugins/builtin/options-scenario/pane.tsx +54 -11
  275. package/src/plugins/builtin/plugin-marketplace/pane.tsx +5 -4
  276. package/src/plugins/builtin/portfolio-list/account-metrics.ts +3 -0
  277. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +8 -5
  278. package/src/plugins/builtin/portfolio-list/cli/render.ts +34 -10
  279. package/src/plugins/builtin/portfolio-list/headless.ts +1 -1
  280. package/src/plugins/builtin/portfolio-list/pane/data.ts +23 -1
  281. package/src/plugins/builtin/portfolio-list/pane/index.tsx +33 -14
  282. package/src/plugins/builtin/portfolio-list/settings.ts +20 -2
  283. package/src/plugins/builtin/portfolio-list/summary/index.tsx +12 -0
  284. package/src/plugins/builtin/portfolio-list/summary/totals.ts +24 -3
  285. package/src/plugins/builtin/power/client.ts +2 -2
  286. package/src/plugins/builtin/power/pane.tsx +36 -31
  287. package/src/plugins/builtin/primary-dealers/client.ts +137 -62
  288. package/src/plugins/builtin/primary-dealers/headless.ts +30 -5
  289. package/src/plugins/builtin/primary-dealers/index.tsx +3 -2
  290. package/src/plugins/builtin/primary-dealers/model.ts +179 -129
  291. package/src/plugins/builtin/primary-dealers/pane.tsx +135 -15
  292. package/src/plugins/builtin/research/analyst-model.ts +5 -1
  293. package/src/plugins/builtin/reverse-dcf/client.ts +2 -1
  294. package/src/plugins/builtin/reverse-dcf/model.ts +4 -3
  295. package/src/plugins/builtin/risk-factors/index.tsx +12 -3
  296. package/src/plugins/builtin/scanner/feed.ts +6 -1
  297. package/src/plugins/builtin/scanner/headless.ts +1 -1
  298. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  299. package/src/plugins/builtin/seasonality/client.ts +2 -1
  300. package/src/plugins/builtin/sectors/client.ts +4 -3
  301. package/src/plugins/builtin/series-inventory.ts +1 -0
  302. package/src/plugins/builtin/shared/cloud-denied.ts +7 -6
  303. package/src/plugins/builtin/shared/cloud-resource.ts +15 -2
  304. package/src/plugins/builtin/shared/cloud-upgrade.ts +10 -11
  305. package/src/plugins/builtin/shared/free-routes.ts +24 -0
  306. package/src/plugins/builtin/shared/headless-options.ts +11 -0
  307. package/src/plugins/builtin/shared/pro-lock-note.ts +89 -7
  308. package/src/plugins/builtin/shared/research-trading.tsx +64 -0
  309. package/src/plugins/builtin/shared/series-list-detail.tsx +3 -1
  310. package/src/plugins/builtin/shared/volatility/options.ts +2 -1
  311. package/src/plugins/builtin/social-mentions/client.ts +2 -1
  312. package/src/plugins/builtin/social-mentions/headless.ts +1 -1
  313. package/src/plugins/builtin/social-mentions/lock-note.ts +1 -1
  314. package/src/plugins/builtin/themes/headless.ts +8 -2
  315. package/src/plugins/builtin/themes/model.ts +43 -10
  316. package/src/plugins/builtin/themes/pane.tsx +8 -6
  317. package/src/plugins/builtin/ticker-detail/overview/model.ts +44 -10
  318. package/src/plugins/builtin/ticker-detail/overview/option-contract.ts +64 -0
  319. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +82 -21
  320. package/src/plugins/builtin/ticker-detail/pane.tsx +21 -14
  321. package/src/plugins/builtin/ticker-detail/quote-footer.ts +5 -1
  322. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +37 -8
  323. package/src/plugins/builtin/time-sales/pane.tsx +2 -2
  324. package/src/plugins/builtin/trace-bonds/client.ts +14 -3
  325. package/src/plugins/builtin/trace-bonds/headless.ts +1 -1
  326. package/src/plugins/builtin/vol-surface/client.ts +23 -3
  327. package/src/plugins/builtin/vol-surface/headless.ts +33 -14
  328. package/src/plugins/builtin/vol-surface/model.ts +30 -10
  329. package/src/plugins/builtin/vol-surface/pane.tsx +35 -15
  330. package/src/plugins/builtin/vol-surface/stored.ts +34 -3
  331. package/src/plugins/builtin/vol-surface/tables.ts +14 -0
  332. package/src/plugins/builtin-editorial.ts +1 -1
  333. package/src/plugins/compat.ts +3 -0
  334. package/src/plugins/external-runtime.ts +5 -0
  335. package/src/plugins/loader.ts +6 -0
  336. package/src/plugins/plugin-icon.ts +61 -0
  337. package/src/plugins/registry/host-actions.ts +2 -1
  338. package/src/plugins/registry/index.ts +9 -1
  339. package/src/plugins/runtime/context.tsx +5 -0
  340. package/src/plugins/runtime/index.tsx +3 -0
  341. package/src/portfolio/column-values.ts +13 -4
  342. package/src/portfolio/cost-fx.ts +5 -28
  343. package/src/public/__snapshots__/public-api.test.ts.snap +5 -0
  344. package/src/public/broker.ts +8 -0
  345. package/src/public/market-data.ts +5 -0
  346. package/src/renderers/dom/desktop/control-styles.ts +7 -1
  347. package/src/renderers/dom/desktop/controls.tsx +40 -8
  348. package/src/renderers/dom/desktop/icons.tsx +14 -0
  349. package/src/renderers/dom/desktop/list-view.tsx +51 -19
  350. package/src/renderers/dom/desktop/menu.tsx +10 -1
  351. package/src/renderers/dom/desktop/query-bar.tsx +2 -0
  352. package/src/renderers/dom/desktop/select-field.tsx +8 -6
  353. package/src/renderers/dom/host/style.ts +6 -1
  354. package/src/renderers/dom/host/tabs.tsx +22 -5
  355. package/src/renderers/dom/host/top-surface.ts +127 -0
  356. package/src/renderers/dom/styles.css +51 -5
  357. package/src/renderers/electrobun/bun/core-capabilities.ts +21 -1
  358. package/src/renderers/electrobun/bun/desktop/backend.ts +10 -0
  359. package/src/renderers/electrobun/bun/desktop/update.ts +210 -30
  360. package/src/renderers/electrobun/bun/external-plugins.ts +3 -0
  361. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  362. package/src/renderers/electrobun/view/broker-remote-client.ts +16 -0
  363. package/src/renderers/electrobun/view/external-plugins.ts +1 -0
  364. package/src/renderers/electrobun/view/news-source.ts +4 -2
  365. package/src/renderers/electrobun/view/remote/asset-data-client.ts +8 -1
  366. package/src/renderers/opentui/start.tsx +7 -0
  367. package/src/series/validate.ts +2 -2
  368. package/src/sources/gloomberb-cloud/index.ts +57 -17
  369. package/src/sources/gloomberb-cloud/normalizers.ts +4 -0
  370. package/src/sources/history-result.ts +3 -0
  371. package/src/sources/provider-errors.ts +3 -1
  372. package/src/sources/provider-router/history.ts +49 -6
  373. package/src/sources/provider-router/index.ts +18 -1
  374. package/src/state/app/activity.ts +13 -0
  375. package/src/sync/controller.ts +4 -0
  376. package/src/sync/core-contributors.ts +62 -11
  377. package/src/sync/local-only.ts +121 -0
  378. package/src/sync/react.ts +6 -0
  379. package/src/sync/types.ts +3 -0
  380. package/src/telemetry/crash-reports.ts +4 -0
  381. package/src/telemetry/update-failures-node.ts +59 -0
  382. package/src/telemetry/update-failures.ts +307 -0
  383. package/src/tickers/company-name-listing.ts +9 -7
  384. package/src/tickers/company-name.ts +43 -0
  385. package/src/tickers/listing-fate.ts +57 -8
  386. package/src/tickers/search/index.ts +24 -3
  387. package/src/time-series/price-comparison.ts +35 -0
  388. package/src/time-series/resolve.ts +52 -11
  389. package/src/time-series/spec.ts +1 -1
  390. package/src/time-series/types.ts +5 -0
  391. package/src/types/broker.ts +56 -1
  392. package/src/types/config.ts +6 -0
  393. package/src/types/data-provider.ts +11 -1
  394. package/src/types/financials.ts +23 -0
  395. package/src/types/headless.ts +19 -2
  396. package/src/types/instrument.ts +4 -0
  397. package/src/types/issuer-listings.ts +28 -0
  398. package/src/types/plugin.ts +24 -6
  399. package/src/types/price-history.ts +6 -1
  400. package/src/types/trading.ts +67 -0
  401. package/src/ui/host.tsx +4 -0
  402. package/src/updater/failure.ts +163 -0
  403. package/src/updater/index.ts +87 -23
  404. package/src/utils/base-currency.ts +108 -0
  405. package/src/utils/currency-names.ts +128 -0
  406. package/src/utils/currency-pair.ts +28 -1
  407. package/src/utils/currency-units.ts +32 -1
  408. package/src/utils/dom-screenshot.ts +22 -9
  409. package/src/utils/exchange-notation.generated.ts +360 -0
  410. package/src/utils/exchanges.ts +15 -322
  411. package/src/utils/format.ts +31 -1
  412. package/src/utils/instrument-identity.ts +38 -1
  413. package/src/utils/options.ts +46 -1
  414. package/src/utils/price-history.ts +4 -0
  415. package/src/utils/reported-money.ts +8 -4
  416. package/src/utils/sec.ts +68 -0
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gloomberb",
3
- "version": "0.18.1",
3
+ "version": "0.18.3",
4
4
  "packageManager": "bun@1.3.11",
5
5
  "description": "Market research and portfolio tracker for the terminal",
6
6
  "module": "src/public/index.ts",
@@ -74,7 +74,7 @@ index cdaa2bca0fabe577cb667056aa6f8f84211189c2..3839e5232742c2467180c344f51a30f1
74
74
 
75
75
  // Encrypt function
76
76
  diff --git a/dist/api/bun/core/Updater.ts b/dist/api/bun/core/Updater.ts
77
- index 39daf6d341780af8e30c2c50327162863f5977ec..bf4a4b615f84904759d4bcd57be0d899c9464a5f 100644
77
+ index 39daf6d341780af8e30c2c50327162863f5977ec..0174d2ce1a9a4fa8ae0fd97367293fc5a794286a 100644
78
78
  --- a/dist/api/bun/core/Updater.ts
79
79
  +++ b/dist/api/bun/core/Updater.ts
80
80
  @@ -132,6 +132,19 @@ let updateInfo: {
@@ -97,7 +97,62 @@ index 39daf6d341780af8e30c2c50327162863f5977ec..bf4a4b615f84904759d4bcd57be0d899
97
97
  function cleanupExtractionFolder(
98
98
  extractionFolder: string,
99
99
  keepTarHash: string,
100
- @@ -176,6 +189,12 @@ const Updater = {
100
+ @@ -158,6 +171,54 @@ function cleanupExtractionFolder(
101
+ }
102
+ }
103
+
104
+ +/**
105
+ + * Where the running app goes while the new one moves in: `<app>.previous`, or,
106
+ + * when an older one is still there and cannot be removed (a bundle another
107
+ + * user owns), a name of its own, so a copy left behind once never blocks
108
+ + * every later update. The app removes the copies it left at its next start.
109
+ + */
110
+ +function updateAsidePath(appPath: string): string {
111
+ + const previous = `${appPath}.previous`;
112
+ + if (!statSync(previous, { throwIfNoEntry: false })) return previous;
113
+ + try {
114
+ + rmSync(previous, { recursive: true, force: true });
115
+ + } catch (e) {
116
+ + // Left for the next start to remove; this update takes another name.
117
+ + }
118
+ + if (!statSync(previous, { throwIfNoEntry: false })) return previous;
119
+ + for (let attempt = 0; ; attempt += 1) {
120
+ + const unique = `${previous}-${Date.now().toString(36)}${attempt ? `-${attempt}` : ""}`;
121
+ + if (!statSync(unique, { throwIfNoEntry: false })) return unique;
122
+ + }
123
+ +}
124
+ +
125
+ +/**
126
+ + * Puts the new app where the running one is. The running app is moved aside
127
+ + * rather than deleted first, so a move that fails (another volume, a
128
+ + * read-only one, no permission) puts it back instead of leaving no app at all.
129
+ + * The copy moved aside is removed when it can be; one that cannot goes at the
130
+ + * app's next start.
131
+ + */
132
+ +function replaceAppBundle(runningAppPath: string, newAppPath: string) {
133
+ + const hadRunningApp = !!statSync(runningAppPath, { throwIfNoEntry: false });
134
+ + const asidePath = hadRunningApp ? updateAsidePath(runningAppPath) : null;
135
+ + if (asidePath) renameSync(runningAppPath, asidePath);
136
+ +
137
+ + emitStatus("replacing-app", "Installing new version...");
138
+ + try {
139
+ + renameSync(newAppPath, runningAppPath);
140
+ + } catch (error) {
141
+ + if (asidePath) renameSync(asidePath, runningAppPath);
142
+ + throw error;
143
+ + }
144
+ + if (!asidePath) return;
145
+ + try {
146
+ + rmSync(asidePath, { recursive: true, force: true });
147
+ + } catch (e) {
148
+ + // The new app is in place; the app removes the old copy at its next start.
149
+ + }
150
+ +}
151
+ +
152
+ const Updater = {
153
+ updateInfo: () => {
154
+ return updateInfo;
155
+ @@ -176,6 +237,12 @@ const Updater = {
101
156
  onStatusChangeCallback = callback;
102
157
  },
103
158
 
@@ -110,7 +165,7 @@ index 39daf6d341780af8e30c2c50327162863f5977ec..bf4a4b615f84904759d4bcd57be0d899
110
165
  // todo: allow switching channels, by default will check the current channel
111
166
  checkForUpdate: async () => {
112
167
  emitStatus("checking", "Checking for updates...");
113
- @@ -196,11 +215,11 @@ const Updater = {
168
+ @@ -196,11 +263,11 @@ const Updater = {
114
169
 
115
170
  const cacheBuster = Math.random().toString(36).substring(7);
116
171
  const platformPrefix = getPlatformPrefix(
@@ -124,7 +179,7 @@ index 39daf6d341780af8e30c2c50327162863f5977ec..bf4a4b615f84904759d4bcd57be0d899
124
179
 
125
180
  try {
126
181
  const updateInfoResponse = await fetch(updateInfoUrl);
127
- @@ -329,11 +348,11 @@ const Updater = {
182
+ @@ -329,11 +396,11 @@ const Updater = {
128
183
 
129
184
  // check if there's a patch file for it
130
185
  const platformPrefix = getPlatformPrefix(
@@ -138,7 +193,7 @@ index 39daf6d341780af8e30c2c50327162863f5977ec..bf4a4b615f84904759d4bcd57be0d899
138
193
  emitStatus(
139
194
  "fetching-patch",
140
195
  `Checking for patch: ${currentHash.slice(0, 8)}`,
141
- @@ -612,12 +631,12 @@ const Updater = {
196
+ @@ -612,12 +679,12 @@ const Updater = {
142
197
 
143
198
  const cacheBuster = Math.random().toString(36).substring(7);
144
199
  const platformPrefix = getPlatformPrefix(
@@ -153,6 +208,52 @@ index 39daf6d341780af8e30c2c50327162863f5977ec..bf4a4b615f84904759d4bcd57be0d899
153
208
  const prevVersionCompressedTarballPath = join(
154
209
  appDataFolder,
155
210
  "self-extraction",
211
+ @@ -760,7 +827,12 @@ const Updater = {
212
+ mkdirSync(extractionFolder, { recursive: true });
213
+ }
214
+
215
+ - let latestHash = (await Updater.checkForUpdate()).hash;
216
+ + // Install the build that was downloaded. Checking again here fetched
217
+ + // the newest manifest instead: once a newer release was out (or the
218
+ + // check failed offline) its tar was not there and the install
219
+ + // returned without a word, so a download waiting for a restart could
220
+ + // never be installed.
221
+ + let latestHash = updateInfo.hash;
222
+ const latestTarPath = join(extractionFolder, `${latestHash}.tar`);
223
+
224
+ let appBundleSubpath: string = "";
225
+ @@ -894,14 +966,7 @@ const Updater = {
226
+ emitStatus("replacing-app", "Removing old version...");
227
+
228
+ if (currentOS === "macos") {
229
+ - // Remove existing app before installing the new one
230
+ - if (statSync(runningAppBundlePath, { throwIfNoEntry: false })) {
231
+ - rmSync(runningAppBundlePath, { recursive: true });
232
+ - }
233
+ -
234
+ - emitStatus("replacing-app", "Installing new version...");
235
+ - // Move new app to running location
236
+ - renameSync(newAppBundlePath, runningAppBundlePath);
237
+ + replaceAppBundle(runningAppBundlePath, newAppBundlePath);
238
+
239
+ // Remove quarantine extended attributes to prevent "damaged" error
240
+ // The inner bundle is already signed/notarized, but macOS applies
241
+ @@ -918,14 +983,7 @@ const Updater = {
242
+ // On Linux, we now have directory bundles instead of AppImage files
243
+ // The app is stored in {appDataFolder}/app/
244
+ const appBundleDir = join(appDataFolder, "app");
245
+ -
246
+ - // Remove existing app directory if it exists
247
+ - if (statSync(appBundleDir, { throwIfNoEntry: false })) {
248
+ - rmSync(appBundleDir, { recursive: true });
249
+ - }
250
+ -
251
+ - // Move new app bundle directory to app location
252
+ - renameSync(newAppBundlePath, appBundleDir);
253
+ + replaceAppBundle(appBundleDir, newAppBundlePath);
254
+
255
+ // Ensure launcher binary is executable
256
+ const launcherPath = join(appBundleDir, "bin", "launcher");
156
257
  diff --git a/dist/api/bun/preload/.generated/compiled.ts b/dist/api/bun/preload/.generated/compiled.ts
157
258
  index 3ab1d7d8de8938ae60823650fd3a29b952a76cb6..e0e841e6a87ce41533c6bdda8d29400aaf478e31 100644
158
259
  --- a/dist/api/bun/preload/.generated/compiled.ts
@@ -44,6 +44,12 @@ export interface PsdBalanceRow {
44
44
  unit: string;
45
45
  /** Market year, 2026/27. */
46
46
  year: string;
47
+ /**
48
+ * YYYY-MM of the crop's newest estimate: the WASDE for grains, oilseeds and
49
+ * cotton, USDA's semiannual report for coffee and sugar. Absent from boards
50
+ * served before it was sent.
51
+ */
52
+ month?: string | null;
47
53
  cropOrder: number;
48
54
  countryOrder: number;
49
55
  attributeOrder: number;
@@ -83,6 +83,10 @@ export interface KpiObservation extends Omit<KpiObservationInput, "quote" | "sca
83
83
  conflict: boolean
84
84
  contested?: boolean
85
85
  evidence: KpiEvidence[]
86
+ /** Stored facts shown as this one: the same figure under other labels or periods. */
87
+ merged?: string[]
88
+ /** The labels that tell its series apart, the same on each of its facts; the stored ones stay in `dimensions`. */
89
+ seriesLabels?: Record<string, string>
86
90
  }
87
91
  export interface KpiGuidance extends Omit<KpiGuidanceInput, "quote" | "scale"> {
88
92
  id: string
@@ -97,6 +101,10 @@ export interface KpiGuidance extends Omit<KpiGuidanceInput, "quote" | "scale"> {
97
101
  direction: GuidanceDirection
98
102
  previousId: string | null
99
103
  midpointChangePct: number | null
104
+ /** Stored guides shown as this one. */
105
+ merged?: string[]
106
+ /** The labels that tell its series apart, as on its actuals. */
107
+ seriesLabels?: Record<string, string>
100
108
  actual: { observationId: string; value: number; outcome: "above" | "below" | "within"; favorable: "beat" | "miss" | "in_line" | "neutral"; difference: number; differencePct: number | null } | null
101
109
  }
102
110
  interface KpiCoverage {
@@ -118,6 +126,7 @@ export interface KpiSeries {
118
126
  currency: string | null
119
127
  basis: KpiBasis
120
128
  dimensions: Record<string, string>
129
+ seriesLabels?: Record<string, string>
121
130
  latest: KpiObservation
122
131
  observations: KpiObservation[]
123
132
  }
@@ -38,9 +38,16 @@ export interface CryptoMarketAsset {
38
38
  /** Market-cap rank within its kind. */
39
39
  rank: number;
40
40
  price: number;
41
+ /**
42
+ * The day's move as `quote` gives the coin: from the previous UTC day's
43
+ * close, the price at 00:00 UTC. A coin whose close Gloom Cloud cannot date
44
+ * to the last two days moves over 24 hours (`changeBasis` "24h") or not at
45
+ * all ("none"), its previousClose null.
46
+ */
41
47
  previousClose: number | null;
42
48
  change: number | null;
43
49
  changePercent: number | null;
50
+ changeBasis?: "24h" | "none";
44
51
  dayHigh: number | null;
45
52
  dayLow: number | null;
46
53
  volume24h: number | null;
@@ -62,6 +69,8 @@ export interface CryptoMarketAsset {
62
69
  export interface CryptoMarketsPayload {
63
70
  version: 1;
64
71
  generatedAt: string;
72
+ /** Each asset's change is since 00:00 UTC; a server before it sent the screener's 24-hour move. */
73
+ dayChangeBasis?: "utc-day";
65
74
  asOf: string | null;
66
75
  status: "available" | "partial" | "unavailable";
67
76
  source: {
@@ -27,6 +27,7 @@ import {
27
27
  import type { LettersParams } from "./letters";
28
28
  import type { IpoCalendarParams, IpoCalendarPayload } from "./ipo";
29
29
  import type { TickerChanges } from "../types/ticker-changes";
30
+ import type { IssuerListings } from "../types/issuer-listings";
30
31
  import type { OrganizationTickersResponse } from "./organization-tickers";
31
32
  import type { CryptoMarketsPayload, StablecoinSupplyHistoryPayload, StablecoinSupplyPayload } from "./crypto-markets";
32
33
  import type { CentralBankRatesPayload } from "./central-bank-rates";
@@ -470,6 +471,20 @@ export class CloudDataApi {
470
471
  return answers;
471
472
  }
472
473
 
474
+ /** Every listing of the company a listing belongs to (RCI and RCI.B:TSX are Rogers); empty for one the service does not hold. */
475
+ async getCloudIssuerListings(
476
+ symbol: string,
477
+ exchange = "",
478
+ options: { signal?: AbortSignal } = {},
479
+ ): Promise<CloudMarketResponse<IssuerListings>> {
480
+ const params = new URLSearchParams({ symbol: symbol.trim().toUpperCase() });
481
+ if (exchange.trim()) params.set("exchange", exchange.trim().toUpperCase());
482
+ return this.request<CloudMarketResponse<IssuerListings>>(
483
+ `/market/issuer-listings?${params}`,
484
+ options.signal ? { signal: options.signal } : undefined,
485
+ );
486
+ }
487
+
473
488
  /** The listed company each organization name resolves to, if any: a trial's lead sponsors, a page at a time. */
474
489
  async getCloudOrganizationTickers(names: readonly string[], signal?: AbortSignal): Promise<OrganizationTickersResponse> {
475
490
  return this.request<OrganizationTickersResponse>("/market/organization-tickers", {
@@ -610,12 +625,12 @@ export class CloudDataApi {
610
625
  }
611
626
 
612
627
  /** `listing` names a listing abroad's venue and company; a US listing needs neither. */
613
- async getCloudCompanyKpis(symbol: string, options: KpiQueryOptions = {}, listing: IssuerListingParams = {}): Promise<KpisPayload> {
614
- return this.request<KpisPayload>(companyDisclosurePath("kpis", symbol, options, listing));
628
+ async getCloudCompanyKpis(symbol: string, options: KpiQueryOptions = {}, listing: IssuerListingParams = {}, request?: { signal?: AbortSignal }): Promise<KpisPayload> {
629
+ return this.request<KpisPayload>(companyDisclosurePath("kpis", symbol, options, listing), request);
615
630
  }
616
631
 
617
- async getCloudCompanyGuidance(symbol: string, options: KpiQueryOptions = {}, listing: IssuerListingParams = {}): Promise<GuidancePayload> {
618
- return this.request<GuidancePayload>(companyDisclosurePath("guidance", symbol, options, listing));
632
+ async getCloudCompanyGuidance(symbol: string, options: KpiQueryOptions = {}, listing: IssuerListingParams = {}, request?: { signal?: AbortSignal }): Promise<GuidancePayload> {
633
+ return this.request<GuidancePayload>(companyDisclosurePath("guidance", symbol, options, listing), request);
619
634
  }
620
635
  async getCloudPerpsBoard(query: PerpBoardQuery = {}): Promise<PerpBoardPayload> {
621
636
  return this.request(`/cloud/perps/board?${new URLSearchParams(Object.entries(query).filter(([, value]) => value !== undefined).map(([key, value]) => [key, String(value)]))}`);
@@ -1040,8 +1055,9 @@ export class CloudDataApi {
1040
1055
 
1041
1056
  async getCloudSecFilings(
1042
1057
  params: CloudSecFilingsParams,
1058
+ options?: { signal?: AbortSignal },
1043
1059
  ): Promise<CloudSecFilingsResponse> {
1044
- return this.request<CloudSecFilingsResponse>(cloudSecFilingsPath(params));
1060
+ return this.request<CloudSecFilingsResponse>(cloudSecFilingsPath(params), options);
1045
1061
  }
1046
1062
 
1047
1063
  /** Schedule 13D/13G reports about an issuer. Unknown tickers answer 404; no reports is an empty `owners`. */
@@ -110,6 +110,8 @@ export interface ScreenMetric {
110
110
  scope: "reported" | "derived" | "observed-subset";
111
111
  reason: string | null;
112
112
  sourceUrl: string | null;
113
+ /** Set by the client on a Pro field the server left out for a caller without Gloom Pro; never sent. */
114
+ access?: "pro";
113
115
  percentile: {
114
116
  value: number | null;
115
117
  sampleCount: number;
@@ -54,6 +54,7 @@ export type {
54
54
  CrashReportSurface,
55
55
  CrashReportsPayload,
56
56
  FunctionUsageCount,
57
+ UpdateFailureReport,
57
58
  UsageCountsPayload,
58
59
  UsageCountsSurface,
59
60
  } from "./telemetry";
@@ -774,6 +775,7 @@ class GloomApiClient {
774
775
  getCloudTickerChanges = this.data.getCloudTickerChanges.bind(this.data);
775
776
  getCloudOrganizationTickers = this.data.getCloudOrganizationTickers.bind(this.data);
776
777
  getCloudTickerChangesBatch = this.data.getCloudTickerChangesBatch.bind(this.data);
778
+ getCloudIssuerListings = this.data.getCloudIssuerListings.bind(this.data);
777
779
  searchInstrumentsBatch = this.data.searchInstrumentsBatch.bind(this.data);
778
780
  getCloudHistory = this.data.getCloudHistory.bind(this.data);
779
781
  getCloudExchangeRate = this.data.getCloudExchangeRate.bind(this.data);
@@ -413,9 +413,10 @@ export class CloudApiRequestTransport {
413
413
  // change lookup: a note beside a quote, never one of the refused market
414
414
  // requests a lookup of many symbols waits out and retries. Nor the
415
415
  // tickers beside a list of organization names, which say so in place,
416
- // or a 13F filer's EDGAR filing list, whose report says it went unchecked.
417
- // Nor the batched searches and ticker changes a list of symbols starts
418
- // with: a service without them answers 404, and whatever they leave
416
+ // or a 13F filer's EDGAR filing list, whose report says it went unchecked,
417
+ // or a company's other listings, beside a list check that still answers
418
+ // for the listing itself. Nor the batched searches and ticker changes a
419
+ // list of symbols starts with: a service without them answers 404, and whatever they leave
419
420
  // unanswered is looked up one symbol at a time, which is tracked.
420
421
  if (
421
422
  path.startsWith("/telemetry/")
@@ -423,6 +424,7 @@ export class CloudApiRequestTransport {
423
424
  || path === "/cloud/geo/layers"
424
425
  || path.startsWith("/cloud/sec/13f/filer?")
425
426
  || path.startsWith("/market/ticker-changes?")
427
+ || path.startsWith("/market/issuer-listings?")
426
428
  || path === "/market/ticker-changes/batch"
427
429
  || path === "/market/search/batch"
428
430
  || path === "/market/organization-tickers"
@@ -16,6 +16,23 @@ export interface CrashReportError {
16
16
  stack?: string;
17
17
  plugin?: string;
18
18
  pane?: string;
19
+ /** A failed update step, field by field. Servers that do not know it drop it. */
20
+ update?: UpdateFailureReport;
21
+ }
22
+
23
+ /**
24
+ * A failed update step as error tracking gets it: fixed values and version
25
+ * numbers only, never a path, URL or the error's own text.
26
+ */
27
+ export interface UpdateFailureReport {
28
+ phase: "check" | "download" | "verify" | "apply" | "relaunch";
29
+ code: string;
30
+ appVersion: string;
31
+ targetVersion?: string;
32
+ channel: "stable" | "nightly";
33
+ platform: "macos" | "windows" | "linux" | "other";
34
+ arch: "arm64" | "x64" | "other";
35
+ installKind: string;
19
36
  }
20
37
 
21
38
  /** Body of `POST /telemetry/errors`. The server forwards each error to error tracking. */
@@ -1563,6 +1563,11 @@ export interface CloudMarketResponse<T> {
1563
1563
  omittedSessions?: unknown;
1564
1564
  /** Untrusted until the history adapter validates its boundary dates. */
1565
1565
  coverage?: CloudHistoryCoverage;
1566
+ /**
1567
+ * Untrusted: a US listing that no longer trades, `{ status, lastTradeDate,
1568
+ * historyStart }`. Its history ends on `lastTradeDate`. Read by parseListingDelisting.
1569
+ */
1570
+ delisting?: unknown;
1566
1571
  providerMeta?: {
1567
1572
  provider?: string;
1568
1573
  upstream?: string;
@@ -8,7 +8,7 @@ import type { AppAction, AppState } from "../state/app/context";
8
8
  import type { TickerRecord } from "../types/ticker";
9
9
  import { findPaneInstance } from "../types/config";
10
10
  import type { ReleaseInfo } from "../updater";
11
- import { canRestartToApply, canSelfUpdate } from "../updater";
11
+ import { canRestartToApply, canRetryUpdate } from "../updater";
12
12
  import { getVisiblePaneCycleOrder } from "../components/layout/pane/cycle-order";
13
13
  import { requestFunctionHelp, useFunctionHelpHost } from "../plugins/builtin/help/function-card";
14
14
  import { buildRegistryHelpIndex, helpFunctionForPane } from "../plugins/builtin/help/function-index";
@@ -37,21 +37,6 @@ import {
37
37
  /** Long enough to read a few conflicts and choose Review. */
38
38
  const KEYBINDING_NOTICE_DURATION_MS = 15_000;
39
39
 
40
- /**
41
- * Whether the install-update key has something to retry: a release this build
42
- * can install itself whose last attempt failed. A fresh one downloads on its
43
- * own, and a manual or managed one names its own command. (A downloaded
44
- * desktop update has its own use of the key: restarting into it.)
45
- */
46
- export function canRetryUpdate(
47
- state: Pick<AppState, "updateAvailable" | "updateProgress" | "updateCheckInProgress">,
48
- ): boolean {
49
- return !!state.updateAvailable
50
- && state.updateProgress?.phase === "error"
51
- && !state.updateCheckInProgress
52
- && canSelfUpdate(state.updateAvailable);
53
- }
54
-
55
40
  /**
56
41
  * Tells the user once per launch when `config.json` holds a binding that does
57
42
  * not parse, names no action, or lands on a key another action already has.
@@ -479,9 +479,11 @@ export function bindAppPanePluginRegistry({
479
479
  throw error;
480
480
  }
481
481
  }
482
+ // Recorded before it registers: what redraws as its contributions land
483
+ // (a broker list and its logo) reads the entry this checkout now is.
484
+ upsertExternalPlugin(entry);
482
485
  try {
483
486
  await pluginRegistry.register(entry.plugin);
484
- upsertExternalPlugin(entry);
485
487
  } catch (error) {
486
488
  upsertExternalPlugin({ ...entry, error: error instanceof Error ? error.message : String(error) });
487
489
  reportCrash(error, { kind: "plugin", plugin: pluginId });
@@ -24,7 +24,7 @@ import {
24
24
  tickerOpenFailureMessage,
25
25
  type TickerOpenTarget,
26
26
  } from "../../tickers/open-target";
27
- import { findExactTickerSearchMatch } from "../../tickers/search";
27
+ import { findExactTickerSearchMatch, upsertTickerFromSearchResult } from "../../tickers/search";
28
28
  import { offerTickerListings, publishTickerOpenTarget as publishOpenTarget } from "./ticker-open-target";
29
29
  import { parsePublicTickerKey, publicTickerKey } from "../../utils/exchanges";
30
30
  import { attentionActionForPane, captureAttentionAction } from "../../telemetry/attention-counts";
@@ -158,7 +158,7 @@ export function useAppTickerOpenRuntime({
158
158
  persistLayout(nextLayout);
159
159
  }
160
160
  if (paneType === TICKER_RESEARCH_PANE_ID && options?.tabId) {
161
- dispatch({ type: "UPDATE_PANE_STATE", paneId: existing.instanceId, patch: { activeTabId: options.tabId } });
161
+ dispatch({ type: "UPDATE_PANE_STATE", paneId: existing.instanceId, patch: { ...options.tabState, activeTabId: options.tabId } });
162
162
  }
163
163
  focusVisiblePane(existing.instanceId, nextLayout);
164
164
  return;
@@ -186,7 +186,7 @@ export function useAppTickerOpenRuntime({
186
186
  );
187
187
  persistLayout(nextLayout);
188
188
  if (paneType === TICKER_RESEARCH_PANE_ID && options?.tabId) {
189
- dispatch({ type: "UPDATE_PANE_STATE", paneId: instance.instanceId, patch: { activeTabId: options.tabId } });
189
+ dispatch({ type: "UPDATE_PANE_STATE", paneId: instance.instanceId, patch: { ...options.tabState, activeTabId: options.tabId } });
190
190
  }
191
191
  activatePane(instance.instanceId);
192
192
  }, [
@@ -205,9 +205,21 @@ export function useAppTickerOpenRuntime({
205
205
  // Before resolving, so a refused open adds no ticker either.
206
206
  if (refuseWhileOwnerOff(rawSymbol, options, () => { void openPinnedTicker(rawSymbol, options); })) return;
207
207
  const recordAttention = captureAttentionAction();
208
- const selectedTicker = options?.instrument !== undefined && (options.instrument || options.listing) ? await tickerRepository.loadTicker(rawSymbol) : null;
208
+ let selected = options?.instrument !== undefined && (options.instrument || options.listing)
209
+ ? { ticker: await tickerRepository.loadTicker(rawSymbol), created: false } : null;
210
+ // A listing the caller chose, such as one contract of an options chain,
211
+ // opens as given: no search could name it better than its own chain.
212
+ if (selected && !selected.ticker && options?.listing) {
213
+ const { listing, instrument } = options;
214
+ selected = await upsertTickerFromSearchResult(tickerRepository, {
215
+ providerId: instrument?.brokerId ?? "", ...(instrument?.brokerInstanceId ? { brokerInstanceId: instrument.brokerInstanceId } : {}),
216
+ symbol: rawSymbol, name: listing.name, exchange: listing.exchange, type: listing.type,
217
+ ...(listing.currency ? { currency: listing.currency } : {}), ...(instrument ? { brokerContract: instrument } : {}),
218
+ });
219
+ }
220
+ const selectedTicker = selected?.ticker;
209
221
  const target = selectedTicker
210
- ? { symbol: selectedTicker.metadata.ticker, ticker: selectedTicker, created: false, instrument: options?.instrument, listing: options?.listing }
222
+ ? { symbol: selectedTicker.metadata.ticker, ticker: selectedTicker, created: selected!.created, instrument: options?.instrument, listing: options?.listing }
211
223
  : await resolveOpenTickerTarget(rawSymbol, options?.instrument === null);
212
224
  if (!target) return;
213
225
  placePinnedTickerTarget(target, options, rawSymbol);
@@ -105,7 +105,7 @@ export function bindPluginRegistryRuntimeAccess({
105
105
  pluginRegistry.events.emit("config:changed", { config: nextConfig });
106
106
  },
107
107
 
108
- createBrokerInstance: async (brokerType, label, values) => {
108
+ createBrokerInstance: async (brokerType, label, values, options = {}) => {
109
109
  const instanceId = createBrokerInstanceId(
110
110
  brokerType,
111
111
  label,
@@ -118,6 +118,7 @@ export function bindPluginRegistryRuntimeAccess({
118
118
  connectionMode: typeof values.connectionMode === "string" ? values.connectionMode : undefined,
119
119
  config: values,
120
120
  enabled: true,
121
+ ...(options.localOnly ? { localOnly: true } : {}),
121
122
  };
122
123
  const nextConfig = {
123
124
  ...stateRef.current.config,
@@ -153,12 +154,15 @@ export function bindPluginRegistryRuntimeAccess({
153
154
  instance.id === instanceId
154
155
  ? (() => {
155
156
  const nextValues = options.replaceConfig ? values : { ...instance.config, ...values };
157
+ const { localOnly: wasLocalOnly, ...rest } = instance;
158
+ const localOnly = options.localOnly ?? wasLocalOnly === true;
156
159
  return {
157
- ...instance,
160
+ ...rest,
158
161
  label: options.label ?? instance.label,
159
162
  enabled: options.enabled ?? instance.enabled,
160
163
  connectionMode: typeof nextValues.connectionMode === "string" ? nextValues.connectionMode : instance.connectionMode,
161
164
  config: nextValues,
165
+ ...(localOnly ? { localOnly: true } : {}),
162
166
  };
163
167
  })()
164
168
  : instance,
@@ -6,6 +6,7 @@ import type { AppAction, AppState } from "../../state/app/context";
6
6
  import {
7
7
  applyUpdate,
8
8
  canRestartToApply,
9
+ canRetryUpdate,
9
10
  canSelfUpdate,
10
11
  checkForUpdateDetailed,
11
12
  discardStagedUpdate,
@@ -15,6 +16,8 @@ import {
15
16
  type UpdateProgress,
16
17
  } from "../../updater";
17
18
  import { resolveUpdateChannel, stagedUpdateFitsChannel } from "../../updater/channel";
19
+ import { updateFailureReason } from "../../updater/failure";
20
+ import { t, tf } from "../../i18n";
18
21
  import type { UpdateChannel } from "../../types/config";
19
22
  import { VERSION } from "../../version";
20
23
  import { debugLog } from "../../utils/debug-log";
@@ -27,11 +30,14 @@ const UPDATE_CHECK_INTERVAL_MS = 60 * 60_000; // hourly
27
30
  const updateLog = debugLog.createLogger("update");
28
31
 
29
32
  /**
30
- * The header and the command bar show a failed update's message, and the header
31
- * cuts it to its room: the log keeps what the updater actually said.
33
+ * The header says why an update failed in a few words and the command bar
34
+ * shows the whole error: the log keeps what the updater actually said, with
35
+ * the step and the reason.
32
36
  */
33
37
  function logUpdateFailure(progress: UpdateProgress): void {
34
- if (progress.phase === "error") updateLog.error("update failed", { error: progress.error });
38
+ if (progress.phase === "error") {
39
+ updateLog.error("update failed", { error: progress.error, phase: progress.failure?.phase, code: progress.failure?.code });
40
+ }
35
41
  }
36
42
 
37
43
  /** An update is being downloaded, applied, or is downloaded and waiting for a restart. */
@@ -127,7 +133,9 @@ export function useAppUpdateRuntime({
127
133
  return { kind: "error", error: message || "Update check failed" };
128
134
  });
129
135
 
130
- if (result.kind === "error") updateLog.warn("update check failed", { error: result.error });
136
+ if (result.kind === "error") {
137
+ updateLog.warn("update check failed", { error: result.error, code: result.failure?.code });
138
+ }
131
139
 
132
140
  if (!manual) {
133
141
  if (result.kind === "available" && result.release.tagName !== declinedTagRef.current) {
@@ -154,13 +162,25 @@ export function useAppUpdateRuntime({
154
162
  return;
155
163
  }
156
164
 
157
- dispatch({ type: "SET_UPDATE_NOTICE", notice: `Update check failed: ${result.error}` });
165
+ // The reason in a few words, as the failed banner gives it; the log has the error.
166
+ const reason = result.failure ? t(updateFailureReason(result.failure)) : result.error;
167
+ dispatch({ type: "SET_UPDATE_NOTICE", notice: tf("Update check failed: {reason}", { reason }) });
158
168
  }, [dispatch, stateRef]);
159
169
 
160
170
  const runUpdateCommand = useCallback(() => {
161
- if (canRestartToApply(stateRef.current.updateProgress)) restartToApplyUpdate();
162
- else void runUpdateCheck(true);
163
- }, [restartToApplyUpdate, runUpdateCheck, stateRef]);
171
+ const state = stateRef.current;
172
+ if (canRestartToApply(state.updateProgress)) {
173
+ restartToApplyUpdate();
174
+ return;
175
+ }
176
+ // After a failed download or install, the command tries again, as the key does.
177
+ if (canRetryUpdate(state)) {
178
+ startUpdate(state.updateAvailable!);
179
+ return;
180
+ }
181
+ if (state.updateProgress?.phase === "error") dispatch({ type: "SET_UPDATE_PROGRESS", progress: null });
182
+ void runUpdateCheck(true);
183
+ }, [dispatch, restartToApplyUpdate, runUpdateCheck, startUpdate, stateRef]);
164
184
 
165
185
  useEffect(() => {
166
186
  if (!enabled || isDetachedWindow) return;
@@ -0,0 +1,65 @@
1
+ {
2
+ "src/plugins/builtin/account-management/pane.tsx": {
3
+ "hasSession || apiClient.isSignedIn()": "Signed out, every tab is the same sign-in wall."
4
+ },
5
+ "src/plugins/builtin/analytics/index.tsx": {
6
+ "portfolioTabs.length > 0": "The tabs are the user's portfolios; with none there is nothing to choose between."
7
+ },
8
+ "src/plugins/builtin/analytics/risk-pane.tsx": {
9
+ "portfolio": "No local portfolio yet: the pane says to add holdings instead of offering views of nothing."
10
+ },
11
+ "src/plugins/builtin/attention/pane.tsx": {
12
+ "!signInWall": "Signed out, every tab is the same sign-in wall."
13
+ },
14
+ "src/plugins/builtin/awards/pane.tsx": {
15
+ "!denied": "Access denied draws the sign-in or plan wall for every tab."
16
+ },
17
+ "src/plugins/builtin/backtest/pane.tsx": {
18
+ "symbol": "No ticker yet: the pane asks for one before any view applies."
19
+ },
20
+ "src/plugins/builtin/catalysts/pane.tsx": {
21
+ "!openId": "An open event replaces the list by design and registers its own Evidence and History strip."
22
+ },
23
+ "src/plugins/builtin/cloud/team/pane.tsx": {
24
+ "signedIn": "The tabs are the user's teams; signed out there are none and the pane is a sign-in wall."
25
+ },
26
+ "src/plugins/builtin/company-attention/pane.tsx": {
27
+ "!signInWall && !openCompany && !app": "Signed out it is a sign-in wall; an open company or app replaces the board by design and registers its own strip."
28
+ },
29
+ "src/plugins/builtin/company-kpis/pane.tsx": {
30
+ "symbol && !signInWall": "No ticker yet, or signed out: every tab would be the same prompt or sign-in wall."
31
+ },
32
+ "src/plugins/builtin/congress-trades/pane.tsx": {
33
+ "tickerFilter": "A ticker's own congressional trades are one list; the Trades, Members and Tickers tabs belong to the whole-market view."
34
+ },
35
+ "src/plugins/builtin/debt-maturities/pane.tsx": {
36
+ "symbol && !signInWall": "No ticker yet, or signed out: every tab would be the same prompt or sign-in wall."
37
+ },
38
+ "src/plugins/builtin/earnings-ripple/pane.tsx": {
39
+ "holdings.length": "No holdings to ripple from yet: the pane says how to add them instead of offering views of nothing."
40
+ },
41
+ "src/plugins/builtin/estimate-revisions/pane.tsx": {
42
+ "!symbol || signInWall": "No ticker yet, or signed out: every tab would be the same prompt or sign-in wall."
43
+ },
44
+ "src/plugins/builtin/gpu/pane.tsx": {
45
+ "!signIn": "Signed out or unverified, every tab is the same sign-in wall."
46
+ },
47
+ "src/plugins/builtin/portfolio-list/pane/index.tsx": {
48
+ "showCollectionTabs": "The tabs are the pane's collections; a single collection has nothing to switch to."
49
+ },
50
+ "src/plugins/builtin/power/pane.tsx": {
51
+ "!signInWall": "Signed out, every tab is the same sign-in wall."
52
+ },
53
+ "src/plugins/builtin/research-search/pane.tsx": {
54
+ "signInRequired || verificationRequired": "Signed out or unverified, every mode is the same sign-in wall."
55
+ },
56
+ "src/plugins/builtin/shared/filing-year-reader.tsx": {
57
+ "years.length > 1": "The tabs are the filings on record, known once their list answers; a single filing has no other year to pick."
58
+ },
59
+ "src/plugins/builtin/supply-chain/pane.tsx": {
60
+ "symbol": "No ticker yet: the pane asks for one before any view applies."
61
+ },
62
+ "src/plugins/builtin/ticker-detail/pane.tsx": {
63
+ "!paneSettings.hideTabs && ticker": "The hideTabs setting locks the pane to one view, and with no ticker selected there is nothing to research."
64
+ }
65
+ }