gloomberb 0.18.2 → 0.18.4

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 (954) hide show
  1. package/README.md +8 -8
  2. package/README.zh-CN.md +11 -11
  3. package/bin/{gloomberb → gloom} +3 -1
  4. package/package.json +6 -2
  5. package/patches/electrobun@1.18.1.patch +106 -5
  6. package/src/account/checkout-handoff.tsx +225 -0
  7. package/src/account/device-signin-dialog.tsx +2 -2
  8. package/src/account/device-signin.ts +2 -2
  9. package/src/account/upgrade-dialog.tsx +1 -1
  10. package/src/api-client/askg.ts +2 -2
  11. package/src/api-client/auth.ts +2 -2
  12. package/src/api-client/central-bank-rates.ts +6 -0
  13. package/src/api-client/collections.ts +5 -1
  14. package/src/api-client/cot.ts +2 -0
  15. package/src/api-client/crypto-etf-flows.ts +37 -0
  16. package/src/api-client/crypto-markets.ts +60 -0
  17. package/src/api-client/data.ts +99 -5
  18. package/src/api-client/doe.ts +15 -0
  19. package/src/api-client/equity-screener.ts +28 -0
  20. package/src/api-client/exchange-announcements.ts +38 -0
  21. package/src/api-client/fund-facts.ts +39 -0
  22. package/src/api-client/futures-curve.ts +9 -1
  23. package/src/api-client/hiring.ts +301 -0
  24. package/src/api-client/index.ts +24 -5
  25. package/src/api-client/market-discovery.ts +14 -0
  26. package/src/api-client/members.ts +6 -0
  27. package/src/api-client/mna.ts +6 -0
  28. package/src/api-client/paths.ts +18 -1
  29. package/src/api-client/request.ts +26 -7
  30. package/src/api-client/short-side-publications.ts +37 -0
  31. package/src/api-client/socket.ts +64 -4
  32. package/src/api-client/swap-curve.ts +35 -0
  33. package/src/api-client/telemetry.ts +17 -0
  34. package/src/api-client/themes.ts +7 -1
  35. package/src/api-client/types.ts +90 -2
  36. package/src/app/global-shortcuts.ts +1 -16
  37. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  38. package/src/app/runtime/desktop-deeplink.ts +5 -5
  39. package/src/app/runtime/plugin-bindings.ts +2 -2
  40. package/src/app/runtime/startup.ts +13 -0
  41. package/src/app/runtime/update.ts +28 -8
  42. package/src/app/star-prompt/status-line.tsx +2 -2
  43. package/src/architecture/core-builtin-allowlist.json +5 -2
  44. package/src/architecture/pane-tabs-allowlist.json +65 -0
  45. package/src/architecture/ui-literals-allowlist.json +3 -3
  46. package/src/architecture/ui-tokens-allowlist.json +1 -1
  47. package/src/brokers/broker-portfolio-sync.ts +37 -0
  48. package/src/brokers/directory.ts +11 -0
  49. package/src/brokers/logos.ts +3 -1
  50. package/src/brokers/signed-in/adapter.ts +304 -26
  51. package/src/brokers/signed-in/client.ts +14 -2
  52. package/src/brokers/signed-in/connect.ts +10 -2
  53. package/src/brokers/sync-broker-instance.ts +9 -1
  54. package/src/capabilities/chart-series.ts +22 -6
  55. package/src/cli/bank-section.ts +1 -1
  56. package/src/cli/base-converter.ts +23 -14
  57. package/src/cli/central-bank-events.ts +1 -1
  58. package/src/cli/command-options.ts +59 -4
  59. package/src/cli/commands/api.ts +9 -6
  60. package/src/cli/commands/broker.ts +2 -2
  61. package/src/cli/commands/command-utils.ts +2 -2
  62. package/src/cli/commands/crypto-hints.ts +7 -20
  63. package/src/cli/commands/fred-series.ts +4 -2
  64. package/src/cli/commands/keybindings.ts +2 -2
  65. package/src/cli/commands/market.ts +1189 -296
  66. package/src/cli/commands/news-hints.ts +2 -2
  67. package/src/cli/commands/news-search.ts +59 -6
  68. package/src/cli/commands/options-leaps.ts +41 -26
  69. package/src/cli/commands/overview.ts +121 -48
  70. package/src/cli/commands/plugins.ts +9 -9
  71. package/src/cli/commands/remote.ts +13 -10
  72. package/src/cli/commands/search.ts +64 -44
  73. package/src/cli/commands/system.ts +27 -19
  74. package/src/cli/commands/themes.ts +1 -1
  75. package/src/cli/commands/ticker.ts +272 -70
  76. package/src/cli/context.ts +3 -2
  77. package/src/cli/desktop-pane-shot-view.ts +2 -2
  78. package/src/cli/desktop-pane-shot.ts +1 -1
  79. package/src/cli/earnings-calendar.ts +4 -4
  80. package/src/cli/entry.ts +18 -1
  81. package/src/cli/errors.ts +20 -2
  82. package/src/cli/exit-codes.ts +1 -1
  83. package/src/cli/export-source.ts +10 -4
  84. package/src/cli/freshness.ts +2 -8
  85. package/src/cli/fx-forward.ts +130 -0
  86. package/src/cli/fx-pair.ts +10 -9
  87. package/src/cli/glossary.ts +64 -20
  88. package/src/cli/help.ts +20 -14
  89. package/src/cli/history-export.ts +126 -0
  90. package/src/cli/history-rows.ts +196 -43
  91. package/src/cli/index-roots.ts +5 -45
  92. package/src/cli/index.ts +32 -18
  93. package/src/cli/issuer-membership.ts +103 -0
  94. package/src/cli/known-gaps.ts +118 -25
  95. package/src/cli/listing-arg.ts +315 -43
  96. package/src/cli/name-entries.ts +73 -22
  97. package/src/cli/native-smoke.ts +2 -0
  98. package/src/cli/not-a-ticker.ts +19 -9
  99. package/src/cli/opentui-libc.ts +5 -0
  100. package/src/cli/options.ts +12 -0
  101. package/src/cli/pane-functions/capabilities.ts +77 -20
  102. package/src/cli/pane-functions/catalog.ts +40 -23
  103. package/src/cli/pane-functions/discovery.ts +1 -1
  104. package/src/cli/pane-functions/dom.ts +12 -8
  105. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  106. package/src/cli/pane-functions/freshness.ts +174 -23
  107. package/src/cli/pane-functions/function-help.ts +66 -27
  108. package/src/cli/pane-functions/headless.ts +48 -16
  109. package/src/cli/pane-functions/index.ts +82 -21
  110. package/src/cli/pane-functions/options.ts +22 -2
  111. package/src/cli/pane-functions/report-notices.ts +12 -4
  112. package/src/cli/pane-functions/report.ts +5 -5
  113. package/src/cli/pane-functions/resolver.ts +65 -14
  114. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  115. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  116. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  117. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  118. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  119. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  120. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  121. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  122. package/src/cli/pane-functions/shot-options.ts +8 -3
  123. package/src/cli/peso-dollar-rates.ts +79 -0
  124. package/src/cli/quote-answer.ts +38 -3
  125. package/src/cli/report-tables.ts +190 -27
  126. package/src/cli/restore-plugins.ts +29 -14
  127. package/src/cli/result.ts +122 -25
  128. package/src/cli/series-search.ts +5 -5
  129. package/src/cli/statement-filings.ts +19 -0
  130. package/src/cli/suffixed-listing-rows.ts +49 -0
  131. package/src/cli/symbol-args.ts +50 -15
  132. package/src/cli/symbol-cells.ts +120 -0
  133. package/src/cli/symbol-list.ts +226 -60
  134. package/src/cli/symbol-rows.ts +131 -25
  135. package/src/cli/ticker-resolution.ts +108 -11
  136. package/src/cli/treasury-yields.ts +5 -5
  137. package/src/cli/types.ts +2 -0
  138. package/src/cli/uncovered-instrument.ts +3 -3
  139. package/src/cli/unknown-command.ts +6 -6
  140. package/src/components/chart/composite/chart-layout.ts +131 -0
  141. package/src/components/chart/composite/composite-chart.tsx +387 -21
  142. package/src/components/chart/composite/drawing-store.ts +26 -6
  143. package/src/components/chart/composite/interactions.ts +9 -3
  144. package/src/components/chart/composite/legend.tsx +75 -3
  145. package/src/components/chart/composite/levels.ts +5 -0
  146. package/src/components/chart/composite/panel-gestures.ts +41 -0
  147. package/src/components/chart/composite/panel-labels.ts +2 -1
  148. package/src/components/chart/composite/panel-surface.tsx +316 -11
  149. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  150. package/src/components/chart/composite/rasterizer.ts +44 -19
  151. package/src/components/chart/composite/scene.ts +16 -4
  152. package/src/components/chart/composite/text-renderer.ts +48 -21
  153. package/src/components/chart/composite/toolbar.tsx +52 -5
  154. package/src/components/chart/composite/types.ts +19 -0
  155. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  156. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  157. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  158. package/src/components/chart/composite/value-scale.ts +149 -0
  159. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  160. package/src/components/chart/core/pointer.ts +2 -0
  161. package/src/components/chart-table/index.ts +2 -0
  162. package/src/components/chart-table/read.tsx +74 -0
  163. package/src/components/command-bar/commands/collection-list.ts +41 -19
  164. package/src/components/command-bar/commands/collection.ts +9 -3
  165. package/src/components/command-bar/commands/direct/index.ts +1 -1
  166. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  167. package/src/components/command-bar/pane-templates/items.ts +59 -6
  168. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  169. package/src/components/command-bar/routes/root/command-items.ts +32 -6
  170. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  171. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  172. package/src/components/command-bar/routes/root/results.ts +18 -4
  173. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  174. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  175. package/src/components/command-bar/routes/root/selection.ts +6 -1
  176. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  177. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  178. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  179. package/src/components/command-bar/selection-runtime.ts +50 -10
  180. package/src/components/command-bar/surface/index.tsx +24 -2
  181. package/src/components/command-bar/view-model.ts +7 -0
  182. package/src/components/command-bar/workflow/broker.ts +2 -2
  183. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  184. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  185. package/src/components/command-bar/workflow/fields.ts +8 -2
  186. package/src/components/command-bar/workflow/ops.ts +18 -4
  187. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  188. package/src/components/command-bar/workflow/submit.ts +3 -2
  189. package/src/components/command-bar/workflow/tickers.ts +5 -1
  190. package/src/components/company-logo.tsx +39 -0
  191. package/src/components/data-table/sections.ts +4 -2
  192. package/src/components/data-table/view.tsx +23 -3
  193. package/src/components/form-modal/confirm.tsx +2 -1
  194. package/src/components/form-modal/request.ts +5 -0
  195. package/src/components/form-modal/routes.ts +4 -1
  196. package/src/components/index.ts +13 -6
  197. package/src/components/layout/detached-pane-shell.tsx +1 -0
  198. package/src/components/layout/floating-pane.tsx +1 -0
  199. package/src/components/layout/header.tsx +234 -60
  200. package/src/components/layout/market-summary.ts +9 -6
  201. package/src/components/layout/pane/capture-status.ts +1 -1
  202. package/src/components/layout/pane/footer/model.ts +11 -5
  203. package/src/components/layout/pane/header.tsx +10 -3
  204. package/src/components/layout/pane/index.tsx +1 -0
  205. package/src/components/layout/pane/pane-tabs.tsx +9 -1
  206. package/src/components/layout/shell/chrome.ts +19 -6
  207. package/src/components/layout/shell/index.tsx +2 -1
  208. package/src/components/layout/shell/pane/layers.tsx +19 -1
  209. package/src/components/layout/status-bar.tsx +2 -2
  210. package/src/components/market-board/index.tsx +11 -3
  211. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  212. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  213. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  214. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  215. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  216. package/src/components/pane-settings-dialog/value.ts +8 -1
  217. package/src/components/pane-settings-dialog.tsx +44 -13
  218. package/src/components/sign-in-code-panel.tsx +27 -19
  219. package/src/components/ui/column-grid.tsx +48 -4
  220. package/src/components/ui/country-flag-art.tsx +180 -0
  221. package/src/components/ui/country-flag.tsx +118 -12
  222. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  223. package/src/components/ui/data-table/opentui/model.ts +109 -2
  224. package/src/components/ui/data-table/types.ts +1 -1
  225. package/src/components/ui/display.tsx +5 -1
  226. package/src/components/ui/index.ts +3 -1
  227. package/src/components/ui/logo-mark.tsx +11 -2
  228. package/src/components/ui/query-bar.tsx +1 -1
  229. package/src/components/ui/row-notes.ts +79 -0
  230. package/src/components/ui/stat-grid.tsx +28 -3
  231. package/src/components/ui/status.tsx +3 -1
  232. package/src/core/app-services.ts +4 -0
  233. package/src/core/state/app/layout.ts +4 -4
  234. package/src/data/cached-query.ts +8 -1
  235. package/src/data/config/chart-settings.ts +3 -3
  236. package/src/data/config/home.ts +10 -2
  237. package/src/data/config/layout.ts +4 -1
  238. package/src/data/config/store/node.ts +4 -0
  239. package/src/data/config/store/normalize.ts +13 -3
  240. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  241. package/src/data/json-storage.ts +2 -0
  242. package/src/data/saved-copies.ts +84 -0
  243. package/src/docs/snapshot.json +65 -32
  244. package/src/i18n/es.ts +51 -12
  245. package/src/i18n/ja.ts +51 -12
  246. package/src/i18n/ko.ts +51 -12
  247. package/src/i18n/zh-cn.ts +51 -12
  248. package/src/i18n/zh-tw.ts +51 -12
  249. package/src/layout/desks.ts +1 -1
  250. package/src/layout/launch-layout.ts +1 -1
  251. package/src/layout/pane-manager/floating-actions.ts +5 -3
  252. package/src/layout/pane-manager/floating.ts +43 -1
  253. package/src/layout/pane-manager/index.ts +2 -0
  254. package/src/layout/selected-ticker.ts +74 -0
  255. package/src/market-data/depositary-pair.ts +251 -0
  256. package/src/market-data/futures-board.ts +1 -1
  257. package/src/market-data/futures-contract.ts +124 -40
  258. package/src/market-data/fx-closes.ts +17 -2
  259. package/src/market-data/hooks.tsx +19 -11
  260. package/src/market-data/market/change-basis.ts +49 -7
  261. package/src/market-data/market/freshness.ts +34 -5
  262. package/src/market-data/market/period-return.ts +163 -0
  263. package/src/market-data/market/trading-sessions.ts +13 -0
  264. package/src/market-data/market/year-to-date.ts +8 -13
  265. package/src/market-data/performance.ts +20 -14
  266. package/src/market-data/perpetual-premium.ts +32 -0
  267. package/src/market-data/published-apac-sessions.ts +2 -1
  268. package/src/market-data/published-futures-sessions.ts +69 -0
  269. package/src/market-data/quotes/contributions.ts +5 -1
  270. package/src/market-data/quotes/freshness.ts +42 -0
  271. package/src/market-data/quotes/intraday-bar.ts +37 -0
  272. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  273. package/src/market-data/quotes/resolution.ts +4 -2
  274. package/src/market-data/tax-equivalent.ts +1 -1
  275. package/src/market-data/ticker-price-returns.ts +3 -1
  276. package/src/news/aggregator.ts +5 -1
  277. package/src/news/news-model.ts +4 -0
  278. package/src/news/types.ts +11 -0
  279. package/src/notifications/app-notifier.ts +1 -1
  280. package/src/plugins/auto-update.ts +2 -2
  281. package/src/plugins/builtin/account-management/index.tsx +1 -0
  282. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  283. package/src/plugins/builtin/alerts/alert-engine.ts +30 -18
  284. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  285. package/src/plugins/builtin/alerts/cli.ts +485 -76
  286. package/src/plugins/builtin/alerts/command.ts +5 -10
  287. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  288. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  289. package/src/plugins/builtin/alerts/events.ts +108 -5
  290. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  291. package/src/plugins/builtin/alerts/format.ts +2 -1
  292. package/src/plugins/builtin/alerts/headless.ts +100 -39
  293. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  294. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  295. package/src/plugins/builtin/alerts/index.tsx +45 -18
  296. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  297. package/src/plugins/builtin/alerts/levels.ts +3 -3
  298. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  299. package/src/plugins/builtin/alerts/price.ts +35 -0
  300. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  301. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  302. package/src/plugins/builtin/alerts/research-rules.ts +77 -3
  303. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  304. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  305. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  306. package/src/plugins/builtin/alerts/storage.ts +13 -10
  307. package/src/plugins/builtin/alerts/targets.ts +12 -3
  308. package/src/plugins/builtin/analytics/index.tsx +8 -2
  309. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  310. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  311. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  312. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  313. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  314. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  315. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  316. package/src/plugins/builtin/analytics/risk-pane.tsx +107 -43
  317. package/src/plugins/builtin/attention/pane.tsx +22 -16
  318. package/src/plugins/builtin/awards/headless.ts +7 -3
  319. package/src/plugins/builtin/awards/model.ts +18 -0
  320. package/src/plugins/builtin/awards/pane.tsx +12 -9
  321. package/src/plugins/builtin/backtest/export.ts +1 -1
  322. package/src/plugins/builtin/backtest/headless.ts +3 -3
  323. package/src/plugins/builtin/backtest/rules.ts +41 -5
  324. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  325. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  326. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  327. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  328. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  329. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  330. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  331. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  332. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  333. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  334. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  335. package/src/plugins/builtin/broker-trading/dock.tsx +24 -4
  336. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  337. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  338. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  339. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  340. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  341. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  342. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  343. package/src/plugins/builtin/broker-trading/pane.tsx +115 -26
  344. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  345. package/src/plugins/builtin/broker-trading/ticket.tsx +122 -36
  346. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  347. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  348. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  349. package/src/plugins/builtin/buildout/pane/index.tsx +34 -18
  350. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  351. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  352. package/src/plugins/builtin/catalysts/client.ts +2 -2
  353. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  354. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  355. package/src/plugins/builtin/catalysts/model.ts +49 -4
  356. package/src/plugins/builtin/catalysts/pane.tsx +9 -11
  357. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  358. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  359. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  360. package/src/plugins/builtin/changelog/index.tsx +1 -0
  361. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  362. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  363. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  364. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  365. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  366. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  367. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  368. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  369. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  370. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  371. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  372. package/src/plugins/builtin/chart-composer/pane.tsx +128 -28
  373. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  374. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  375. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  376. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  377. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  378. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  379. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  380. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  381. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  382. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  383. package/src/plugins/builtin/chat/people.ts +1 -1
  384. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  385. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  386. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  387. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  388. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  389. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  390. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  391. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  392. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  393. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  394. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  395. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  396. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  397. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  398. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  399. package/src/plugins/builtin/commodity-balances/pane.tsx +14 -8
  400. package/src/plugins/builtin/company-attention/app-rank.tsx +11 -10
  401. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  402. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  403. package/src/plugins/builtin/company-attention/client.ts +117 -1
  404. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  405. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  406. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  407. package/src/plugins/builtin/company-attention/model.ts +33 -7
  408. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  409. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  410. package/src/plugins/builtin/company-attention/pane.tsx +89 -31
  411. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  412. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  413. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  414. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  415. package/src/plugins/builtin/company-kpis/pane.tsx +5 -4
  416. package/src/plugins/builtin/composite-plugins.ts +2 -1
  417. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  418. package/src/plugins/builtin/connections/index.ts +1 -0
  419. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  420. package/src/plugins/builtin/correlation/headless.ts +7 -4
  421. package/src/plugins/builtin/correlation/relationship/model.ts +1 -0
  422. package/src/plugins/builtin/correlation/settings.ts +1 -0
  423. package/src/plugins/builtin/cot/headless.ts +28 -6
  424. package/src/plugins/builtin/cot/model.ts +12 -0
  425. package/src/plugins/builtin/cot/pane.tsx +7 -5
  426. package/src/plugins/builtin/cpi/headless.ts +5 -1
  427. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  428. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  429. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  430. package/src/plugins/builtin/credit-documents/pane.tsx +8 -7
  431. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  432. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  433. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  434. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  435. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  436. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  437. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  438. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  439. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  440. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  441. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  442. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  443. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  444. package/src/plugins/builtin/debt-maturities/pane.tsx +4 -3
  445. package/src/plugins/builtin/debug/index.tsx +1 -0
  446. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  447. package/src/plugins/builtin/docs/index.tsx +1 -1
  448. package/src/plugins/builtin/docs/pane.tsx +2 -2
  449. package/src/plugins/builtin/docs/targets.ts +1 -1
  450. package/src/plugins/builtin/doe/client.ts +5 -1
  451. package/src/plugins/builtin/doe/headless.ts +12 -6
  452. package/src/plugins/builtin/doe/model.ts +43 -6
  453. package/src/plugins/builtin/doe/pane.tsx +19 -12
  454. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  455. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  456. package/src/plugins/builtin/earnings/board.tsx +253 -50
  457. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  458. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  459. package/src/plugins/builtin/earnings/client.ts +4 -3
  460. package/src/plugins/builtin/earnings/headless.ts +53 -15
  461. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  462. package/src/plugins/builtin/earnings/index.tsx +15 -22
  463. package/src/plugins/builtin/earnings/model.ts +17 -12
  464. package/src/plugins/builtin/earnings/scope.ts +89 -0
  465. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  466. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  467. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  468. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  469. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  470. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  471. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  472. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  473. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  474. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  475. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  476. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  477. package/src/plugins/builtin/econ/index.tsx +112 -25
  478. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  479. package/src/plugins/builtin/econ/types.ts +32 -1
  480. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  481. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  482. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  483. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  484. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  485. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  486. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  487. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  488. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  489. package/src/plugins/builtin/equity-screener/headless.ts +77 -5
  490. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  491. package/src/plugins/builtin/equity-screener/model.ts +34 -0
  492. package/src/plugins/builtin/equity-screener/pane.tsx +148 -106
  493. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  494. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  495. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  496. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  497. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  498. package/src/plugins/builtin/exposure/headless.ts +51 -6
  499. package/src/plugins/builtin/exposure/index.tsx +28 -2
  500. package/src/plugins/builtin/exposure/model.ts +7 -0
  501. package/src/plugins/builtin/exposure/pane.tsx +16 -10
  502. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  503. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  504. package/src/plugins/builtin/futures/model.ts +10 -9
  505. package/src/plugins/builtin/futures/returns.ts +8 -36
  506. package/src/plugins/builtin/futures/table.tsx +1 -1
  507. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  508. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  509. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  510. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  511. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  512. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  513. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  514. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  515. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  516. package/src/plugins/builtin/gpu/board.tsx +4 -1
  517. package/src/plugins/builtin/gpu/changes.tsx +10 -10
  518. package/src/plugins/builtin/gpu/equities.tsx +4 -1
  519. package/src/plugins/builtin/gpu/headless.ts +9 -4
  520. package/src/plugins/builtin/gpu/history.tsx +4 -2
  521. package/src/plugins/builtin/gpu/pane.tsx +5 -3
  522. package/src/plugins/builtin/help/index.tsx +13 -3
  523. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  524. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  525. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  526. package/src/plugins/builtin/holders/client.ts +15 -2
  527. package/src/plugins/builtin/holders/format.ts +28 -17
  528. package/src/plugins/builtin/holders/headless.ts +48 -8
  529. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  530. package/src/plugins/builtin/holders/pane.tsx +79 -15
  531. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  532. package/src/plugins/builtin/holders/table-model.ts +2 -1
  533. package/src/plugins/builtin/holders/thirteenf-match.ts +3 -1
  534. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  535. package/src/plugins/builtin/insider/base-values.ts +11 -7
  536. package/src/plugins/builtin/insider/client.ts +3 -0
  537. package/src/plugins/builtin/insider/headless.ts +20 -7
  538. package/src/plugins/builtin/insider/index.tsx +17 -8
  539. package/src/plugins/builtin/insider/issuer.ts +34 -4
  540. package/src/plugins/builtin/insider/signals-client.ts +3 -3
  541. package/src/plugins/builtin/insider/signals.tsx +1 -1
  542. package/src/plugins/builtin/insider/table-model.ts +35 -13
  543. package/src/plugins/builtin/insider/table.tsx +5 -2
  544. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  545. package/src/plugins/builtin/iv-history/client.ts +20 -0
  546. package/src/plugins/builtin/iv-history/events.ts +145 -0
  547. package/src/plugins/builtin/iv-history/format.ts +3 -0
  548. package/src/plugins/builtin/iv-history/headless.ts +91 -7
  549. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  550. package/src/plugins/builtin/iv-history/model.ts +13 -4
  551. package/src/plugins/builtin/iv-history/series.ts +3 -1
  552. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  553. package/src/plugins/builtin/jobs/model.ts +4 -15
  554. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  555. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  556. package/src/plugins/builtin/jodi/pane.tsx +14 -8
  557. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  558. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  559. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  560. package/src/plugins/builtin/macro-day/model.ts +29 -8
  561. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  562. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  563. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  564. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  565. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  566. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  567. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  568. package/src/plugins/builtin/market-movers/client.ts +36 -21
  569. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  570. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  571. package/src/plugins/builtin/market-movers/screener.ts +33 -11
  572. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  573. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  574. package/src/plugins/builtin/market-movers/table.ts +4 -1
  575. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  576. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  577. package/src/plugins/builtin/members/headless.ts +6 -5
  578. package/src/plugins/builtin/members/pane.tsx +11 -4
  579. package/src/plugins/builtin/mna/client.ts +1 -0
  580. package/src/plugins/builtin/mna/detail.tsx +20 -32
  581. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  582. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  583. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  584. package/src/plugins/builtin/mna/headless.ts +6 -13
  585. package/src/plugins/builtin/mna/model.ts +93 -15
  586. package/src/plugins/builtin/mna/pane.tsx +5 -4
  587. package/src/plugins/builtin/news/headless.ts +12 -6
  588. package/src/plugins/builtin/news/index.tsx +10 -1
  589. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  590. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  591. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  592. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  593. package/src/plugins/builtin/notes/cli.ts +50 -15
  594. package/src/plugins/builtin/notes/export.ts +17 -6
  595. package/src/plugins/builtin/notes/files.ts +16 -5
  596. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  597. package/src/plugins/builtin/options/carry.ts +2 -2
  598. package/src/plugins/builtin/options/enrichment-model.ts +10 -9
  599. package/src/plugins/builtin/options/index.tsx +1 -1
  600. package/src/plugins/builtin/options/model-spot.ts +70 -4
  601. package/src/plugins/builtin/options/view.tsx +51 -23
  602. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  603. package/src/plugins/builtin/options-calculator/pane.tsx +3 -2
  604. package/src/plugins/builtin/options-positioning/headless.ts +11 -3
  605. package/src/plugins/builtin/options-positioning/model.ts +24 -0
  606. package/src/plugins/builtin/options-positioning/pane.tsx +10 -12
  607. package/src/plugins/builtin/options-scenario/client.ts +142 -29
  608. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  609. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  610. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  611. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  612. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  613. package/src/plugins/builtin/options-scenario/headless.ts +49 -21
  614. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  615. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  616. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  617. package/src/plugins/builtin/options-scenario/pane.tsx +22 -10
  618. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  619. package/src/plugins/builtin/perps/freshness.ts +8 -5
  620. package/src/plugins/builtin/perps/headless.ts +3 -1
  621. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  622. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  623. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  624. package/src/plugins/builtin/perps/pane.tsx +36 -17
  625. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  626. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  627. package/src/plugins/builtin/plugin-marketplace/pane.tsx +145 -60
  628. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +78 -27
  629. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  630. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  631. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  632. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  633. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  634. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  635. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  636. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  637. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  638. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  639. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  640. package/src/plugins/builtin/power/client.ts +6 -3
  641. package/src/plugins/builtin/power/evidence.ts +22 -7
  642. package/src/plugins/builtin/power/gas.tsx +5 -1
  643. package/src/plugins/builtin/power/pane.tsx +44 -32
  644. package/src/plugins/builtin/power/prices.tsx +10 -3
  645. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  646. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  647. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  648. package/src/plugins/builtin/research/event-model.ts +3 -1
  649. package/src/plugins/builtin/research/event-sources.ts +12 -4
  650. package/src/plugins/builtin/research/index.tsx +2 -1
  651. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  652. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  653. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  654. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  655. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  656. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  657. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  658. package/src/plugins/builtin/reverse-dcf/client.ts +2 -1
  659. package/src/plugins/builtin/reverse-dcf/model.ts +10 -5
  660. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  661. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  662. package/src/plugins/builtin/seasonality/client.ts +2 -1
  663. package/src/plugins/builtin/sec/forms.ts +13 -0
  664. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  665. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  666. package/src/plugins/builtin/sec/headless.ts +85 -8
  667. package/src/plugins/builtin/sec/index.tsx +58 -12
  668. package/src/plugins/builtin/sectors/client.ts +6 -4
  669. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  670. package/src/plugins/builtin/series-inventory.ts +2 -0
  671. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  672. package/src/plugins/builtin/shared/cloud-resource.ts +15 -2
  673. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  674. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  675. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  676. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  677. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  678. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  679. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  680. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  681. package/src/plugins/builtin/shared/research-trading.tsx +23 -0
  682. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  683. package/src/plugins/builtin/shared/series-list-detail.tsx +3 -1
  684. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  685. package/src/plugins/builtin/shared/volatility/options.ts +2 -1
  686. package/src/plugins/builtin/short-interest/client.ts +2 -0
  687. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  688. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  689. package/src/plugins/builtin/short-interest/model.ts +70 -19
  690. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  691. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  692. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  693. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  694. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  695. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  696. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  697. package/src/plugins/builtin/short-interest/types.ts +9 -0
  698. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  699. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  700. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  701. package/src/plugins/builtin/social-mentions/client.ts +2 -1
  702. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  703. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  704. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  705. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  706. package/src/plugins/builtin/themes/headless.ts +8 -5
  707. package/src/plugins/builtin/themes/model.ts +14 -4
  708. package/src/plugins/builtin/themes/pane.tsx +24 -5
  709. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  710. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  711. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  712. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  713. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  714. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  715. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  716. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  717. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  718. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  719. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  720. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  721. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  722. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  723. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  724. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  725. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  726. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  727. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  728. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  729. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  730. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  731. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  732. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  733. package/src/plugins/builtin/vol-surface/client.ts +28 -7
  734. package/src/plugins/builtin/vol-surface/headless.ts +43 -15
  735. package/src/plugins/builtin/vol-surface/model.ts +34 -12
  736. package/src/plugins/builtin/vol-surface/pane.tsx +33 -13
  737. package/src/plugins/builtin/vol-surface/stored.ts +34 -3
  738. package/src/plugins/builtin/vol-surface/tables.ts +14 -0
  739. package/src/plugins/builtin/volatility/model.ts +5 -10
  740. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  741. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  742. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  743. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  744. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  745. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  746. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  747. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  748. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  749. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  750. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  751. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  752. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  753. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  754. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  755. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  756. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  757. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  758. package/src/plugins/bundle.ts +1 -1
  759. package/src/plugins/catalog-browser.ts +2 -1
  760. package/src/plugins/compat.ts +3 -3
  761. package/src/plugins/dependencies.ts +75 -14
  762. package/src/plugins/extracted-plugins.ts +132 -34
  763. package/src/plugins/host-link.ts +149 -34
  764. package/src/plugins/installer.ts +35 -10
  765. package/src/plugins/loader.ts +206 -38
  766. package/src/plugins/manager-node.ts +2 -2
  767. package/src/plugins/marketplace/deactivation.ts +22 -0
  768. package/src/plugins/marketplace/model.ts +2 -2
  769. package/src/plugins/marketplace/packs.ts +1 -1
  770. package/src/plugins/ownership.ts +1 -1
  771. package/src/plugins/peer-retry.ts +155 -0
  772. package/src/plugins/registry/contributions.ts +3 -1
  773. package/src/plugins/registry/index.ts +4 -3
  774. package/src/plugins/registry/plugin-state.ts +3 -0
  775. package/src/plugins/seed-notice.ts +20 -0
  776. package/src/plugins/shortcut-argument.ts +39 -0
  777. package/src/portfolio/allocation.ts +1 -1
  778. package/src/portfolio/collection-name.ts +31 -0
  779. package/src/portfolio/column-values.ts +10 -8
  780. package/src/portfolio/cost-fx.ts +296 -19
  781. package/src/portfolio/manual-lots.ts +75 -0
  782. package/src/portfolio/mutations.ts +60 -22
  783. package/src/portfolio/suffixed-listings.ts +209 -0
  784. package/src/portfolio/totals-currency.ts +24 -0
  785. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  786. package/src/public/time-series.ts +11 -8
  787. package/src/remote/schema.ts +1 -1
  788. package/src/renderers/browser/config-host.ts +3 -0
  789. package/src/renderers/browser/main.tsx +23 -3
  790. package/src/renderers/dom/boot-frame-page.ts +187 -0
  791. package/src/renderers/dom/boot-frame.ts +237 -0
  792. package/src/renderers/dom/build-assets.ts +46 -8
  793. package/src/renderers/dom/desktop/controls.tsx +1 -1
  794. package/src/renderers/dom/desktop/query-bar.tsx +2 -0
  795. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  796. package/src/renderers/dom/host/ascii-text.ts +16 -2
  797. package/src/renderers/dom/host/box.tsx +43 -2
  798. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  799. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  800. package/src/renderers/dom/host/image-surface.tsx +49 -0
  801. package/src/renderers/dom/host/media-surface.tsx +24 -18
  802. package/src/renderers/dom/host/mouse.ts +30 -0
  803. package/src/renderers/dom/host/size-observer.ts +1 -1
  804. package/src/renderers/dom/host/style.ts +1 -1
  805. package/src/renderers/dom/host/tabs.tsx +16 -5
  806. package/src/renderers/dom/host/top-surface.ts +127 -0
  807. package/src/renderers/dom/styles.css +115 -0
  808. package/src/renderers/dom/test-utils.tsx +2 -1
  809. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  810. package/src/renderers/electrobun/bun/core-capabilities.ts +9 -1
  811. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  812. package/src/renderers/electrobun/bun/desktop/backend.ts +56 -3
  813. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  814. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  815. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  816. package/src/renderers/electrobun/bun/desktop/update.ts +210 -30
  817. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  818. package/src/renderers/electrobun/bun/index.ts +11 -1
  819. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  820. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  821. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  822. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  823. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  824. package/src/renderers/electrobun/view/config-host.ts +3 -0
  825. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  826. package/src/renderers/electrobun/view/main.tsx +30 -4
  827. package/src/renderers/electrobun/view/news-source.ts +9 -7
  828. package/src/renderers/electrobun/view/remote/asset-data-client.ts +9 -2
  829. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  830. package/src/renderers/opentui/browser-open.ts +88 -0
  831. package/src/renderers/opentui/host.tsx +5 -14
  832. package/src/renderers/opentui/image/loader.ts +3 -2
  833. package/src/renderers/opentui/native-smoke.ts +10 -1
  834. package/src/renderers/opentui/pane-image/index.ts +2 -1
  835. package/src/renderers/opentui/render-lib.ts +253 -0
  836. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  837. package/src/renderers/opentui/start.tsx +8 -1
  838. package/src/renderers/share/layout-view.tsx +2 -2
  839. package/src/renderers/share/main.tsx +4 -4
  840. package/src/renderers/share/view.tsx +1 -1
  841. package/src/series/resolve.ts +22 -6
  842. package/src/series/standalone.ts +30 -2
  843. package/src/series/types.ts +8 -1
  844. package/src/shares/api.ts +4 -1
  845. package/src/shares/pane.ts +1 -1
  846. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  847. package/src/sources/gloomberb-cloud/index.ts +39 -16
  848. package/src/sources/gloomberb-cloud/news.ts +39 -0
  849. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  850. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  851. package/src/sources/listing-symbols.ts +37 -0
  852. package/src/sources/provider-errors.ts +16 -2
  853. package/src/sources/provider-router/financial-routes.ts +21 -3
  854. package/src/sources/provider-router/financials.ts +15 -2
  855. package/src/sources/provider-router/history.ts +30 -8
  856. package/src/sources/provider-router/index.ts +16 -0
  857. package/src/sources/provider-router/primary.ts +1 -1
  858. package/src/state/app/bootstrap.ts +23 -0
  859. package/src/state/hooks/inline-tickers.ts +30 -5
  860. package/src/sync/controller.ts +1 -1
  861. package/src/sync/core-contributors.ts +46 -14
  862. package/src/team/collections-sync.ts +2 -1
  863. package/src/team/collections.ts +178 -17
  864. package/src/telemetry/crash-reports.ts +21 -4
  865. package/src/telemetry/update-failures-node.ts +59 -0
  866. package/src/telemetry/update-failures.ts +307 -0
  867. package/src/test-support/builtin-panes.ts +1 -1
  868. package/src/theme/colors.ts +3 -30
  869. package/src/theme/ui-tokens.ts +37 -1
  870. package/src/tickers/company-name-listing.ts +215 -36
  871. package/src/tickers/company-name.ts +43 -0
  872. package/src/tickers/index-roots.ts +47 -0
  873. package/src/tickers/instrument-label.ts +5 -1
  874. package/src/tickers/listing-fate.ts +30 -0
  875. package/src/tickers/metadata.ts +10 -0
  876. package/src/tickers/perpetual-listing.ts +5 -2
  877. package/src/tickers/search/home-listing.ts +75 -0
  878. package/src/tickers/search/index.ts +76 -27
  879. package/src/tickers/search/ranking.ts +54 -4
  880. package/src/tickers/search/upsert.ts +29 -0
  881. package/src/tickers/terminal-codes.ts +416 -40
  882. package/src/time-series/bar-aggregation.ts +243 -0
  883. package/src/time-series/base-series.ts +307 -0
  884. package/src/time-series/chart-bounds.ts +349 -0
  885. package/src/time-series/chart-seed.ts +142 -0
  886. package/src/time-series/economic.ts +5 -0
  887. package/src/time-series/fundamentals.ts +18 -3
  888. package/src/time-series/history-acquisition.ts +499 -0
  889. package/src/time-series/leverage.ts +100 -0
  890. package/src/time-series/market-resolution.ts +4 -2
  891. package/src/time-series/market.ts +2 -1
  892. package/src/time-series/price-comparison.ts +175 -30
  893. package/src/time-series/range.ts +9 -1
  894. package/src/time-series/resolution.ts +174 -16
  895. package/src/time-series/resolve-context.ts +157 -0
  896. package/src/time-series/resolve.ts +126 -1577
  897. package/src/time-series/series-name.ts +38 -0
  898. package/src/time-series/series-presentation.ts +208 -0
  899. package/src/time-series/spec.ts +11 -6
  900. package/src/time-series/studies.ts +109 -12
  901. package/src/time-series/tail-reconcile.ts +121 -0
  902. package/src/time-series/types.ts +29 -2
  903. package/src/time-series/use-chart-resolution.ts +4 -5
  904. package/src/types/broker.ts +10 -0
  905. package/src/types/config.ts +14 -0
  906. package/src/types/data-provider.ts +20 -0
  907. package/src/types/exchange-rate.ts +6 -0
  908. package/src/types/financials.ts +104 -7
  909. package/src/types/headless.ts +83 -6
  910. package/src/types/instrument.ts +3 -0
  911. package/src/types/issuer-listings.ts +28 -0
  912. package/src/types/plugin.ts +65 -8
  913. package/src/types/price-history.ts +2 -0
  914. package/src/types/ticker.ts +13 -0
  915. package/src/types/trading.ts +59 -1
  916. package/src/ui/ascii-font.ts +13 -1
  917. package/src/ui/host.tsx +6 -1
  918. package/src/updater/command-names.ts +175 -0
  919. package/src/updater/failure.ts +163 -0
  920. package/src/updater/index.ts +97 -28
  921. package/src/utils/calendar-date.ts +32 -15
  922. package/src/utils/cash-runway.ts +241 -0
  923. package/src/utils/central-banks.ts +42 -0
  924. package/src/utils/cli-output.ts +259 -80
  925. package/src/utils/contract-month.ts +16 -0
  926. package/src/utils/crypto-pair.ts +61 -3
  927. package/src/utils/currency-pair.ts +26 -0
  928. package/src/utils/currency-units.ts +29 -0
  929. package/src/utils/datetime-format.ts +10 -0
  930. package/src/utils/debug-log.ts +1 -1
  931. package/src/utils/decimal-comma.ts +39 -6
  932. package/src/utils/exchange-announcements.ts +71 -0
  933. package/src/utils/exchange-rate-snapshot.ts +10 -0
  934. package/src/utils/financial-statements.ts +83 -4
  935. package/src/utils/format.ts +34 -16
  936. package/src/utils/fundamentals.ts +150 -34
  937. package/src/utils/fx-market-hours.ts +10 -1
  938. package/src/utils/latest-financial-period.ts +8 -0
  939. package/src/utils/linux-libc.ts +54 -0
  940. package/src/utils/market-capitalization.ts +111 -15
  941. package/src/utils/max-drawdown.ts +64 -0
  942. package/src/utils/network-failure.ts +83 -0
  943. package/src/utils/option-expiry.ts +139 -7
  944. package/src/utils/price-earnings.ts +11 -5
  945. package/src/utils/price-history.ts +18 -8
  946. package/src/utils/price-window.ts +131 -0
  947. package/src/utils/release-date.ts +47 -0
  948. package/src/utils/sec.ts +71 -0
  949. package/src/utils/throttled-fetch.ts +11 -4
  950. package/src/utils/trailing-eps.ts +67 -0
  951. package/src/utils/typed-text.ts +21 -0
  952. package/src/utils/utc-time.ts +24 -1
  953. package/src/version.ts +6 -0
  954. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -22,6 +22,7 @@ import {
22
22
  type PaneHint,
23
23
  } from "../../../components";
24
24
  import { loadingErrorFooterInfo } from "../../../components/data-table/table-pane";
25
+ import { useRowNotes } from "../../../components/ui/row-notes";
25
26
  import { colors } from "../../../theme/colors";
26
27
  import { t, tf } from "../../../i18n";
27
28
  import type { PaneProps } from "../../../types/plugin";
@@ -31,6 +32,7 @@ import { isPlainKeyboardEvent } from "../../../utils/keyboard";
31
32
  import { formatRelativeAge } from "../../../utils/datetime-format";
32
33
  import { displayWidth } from "../../../utils/format";
33
34
  import { requiredGloomberb } from "../../../utils/semver";
35
+ import { logFailure } from "../../../utils/log-failure";
34
36
  import { VERSION } from "../../../version";
35
37
  import { getCurrentPluginTarget, runsExternalPlugins } from "../../current-target";
36
38
  import { pluginSetupCommandId } from "../../registry/setup-command";
@@ -67,6 +69,7 @@ import {
67
69
  versionLabel,
68
70
  type MarketplaceEntry,
69
71
  type MarketplaceRow,
72
+ type MarketplaceStatus,
70
73
  type MarketplaceStatusKind,
71
74
  type RegistryPlugin,
72
75
  } from "../../marketplace/model";
@@ -164,7 +167,7 @@ function renderCell(
164
167
  row: MarketplaceRow,
165
168
  column: Column,
166
169
  rowState: { selected: boolean },
167
- busyId: string | null,
170
+ rowStatus: (entry: MarketplaceEntry) => MarketplaceStatus,
168
171
  facts: (entry: MarketplaceEntry) => EntryFacts,
169
172
  ): DataTableCell {
170
173
  if (row.type === "header") return { text: "" };
@@ -191,8 +194,7 @@ function renderCell(
191
194
  case "tagline":
192
195
  return { text: entry.tagline, color: selected ?? colors.textDim };
193
196
  case "status": {
194
- if (busyId === entry.id) return { text: "working", color: selected ?? colors.textDim };
195
- const status = statusOf(entry);
197
+ const status = rowStatus(entry);
196
198
  return { text: status.text, color: selected ?? STATUS_COLORS[status.kind] };
197
199
  }
198
200
  }
@@ -211,14 +213,21 @@ function describeContributions(entry: MarketplaceEntry, host: MarketplaceHost |
211
213
  return parts;
212
214
  }
213
215
 
214
- function EntryDetail({ entry, width, host }: { entry: MarketplaceEntry; width: number; host: MarketplaceHost | null }) {
216
+ function EntryDetail({ entry, width, host, status, failure }: {
217
+ entry: MarketplaceEntry;
218
+ width: number;
219
+ host: MarketplaceHost | null;
220
+ /** The row's STATUS. */
221
+ status: MarketplaceStatus;
222
+ /** What the last action on this plugin ran into, said in full. */
223
+ failure: ActionFailure | null;
224
+ }) {
215
225
  const contributes = describeContributions(entry, host);
216
226
  const live = entry.installed && host ? host.contributions(entry.id) : null;
217
227
  const opensWith = live
218
228
  ? live.templates.map((template) => template.prefix ?? template.label).filter(Boolean)
219
229
  : [];
220
230
  const rowWidth = Math.max(1, width - 2);
221
- const status = statusOf(entry);
222
231
 
223
232
  return (
224
233
  <ScrollBox flexDirection="column" width={width} flexGrow={1} flexBasis={0} minHeight={0} paddingLeft={1} paddingRight={1} scrollY focusable={false}>
@@ -235,6 +244,12 @@ function EntryDetail({ entry, width, host }: { entry: MarketplaceEntry; width: n
235
244
  </Box>
236
245
  ) : null}
237
246
 
247
+ {failure ? (
248
+ <Box paddingTop={1} flexDirection="column">
249
+ <Text fg={STATUS_COLORS[failure.kind ?? "failed"]} wrapText>{failure.text}</Text>
250
+ </Box>
251
+ ) : null}
252
+
238
253
  <Box paddingTop={1} flexDirection="column">
239
254
  {contributes.length > 0 ? <KeyValueRow labelWidth={10} width={rowWidth} emphasis={false} label="Adds" value={contributes.join(", ")} /> : null}
240
255
  {opensWith.length > 0 ? <KeyValueRow labelWidth={10} width={rowWidth} emphasis={false} label="Opens with" value={opensWith.join(", ")} /> : null}
@@ -245,7 +260,7 @@ function EntryDetail({ entry, width, host }: { entry: MarketplaceEntry; width: n
245
260
  */}
246
261
  {entry.hosts.length > 0 ? <KeyValueRow labelWidth={10} width={rowWidth} emphasis={false} label="Declares" value={entry.hosts.join(", ")} /> : null}
247
262
  {entry.repo ? <KeyValueRow labelWidth={10} width={rowWidth} emphasis={false} label="Source" value={`github.com/${entry.repo}`} /> : null}
248
- {entry.minGloomberb ? <KeyValueRow labelWidth={10} width={rowWidth} emphasis={false} label="Requires" value={requiredGloomberb(entry.minGloomberb) ? `Gloomberb ${entry.minGloomberb}, this is ${VERSION}` : `Gloomberb ${entry.minGloomberb}`} /> : null}
263
+ {entry.minGloomberb ? <KeyValueRow labelWidth={10} width={rowWidth} emphasis={false} label="Requires" value={requiredGloomberb(entry.minGloomberb) ? `Gloom ${entry.minGloomberb}, this is ${VERSION}` : `Gloom ${entry.minGloomberb}`} /> : null}
249
264
  {entry.linked ? <KeyValueRow labelWidth={10} width={rowWidth} emphasis={false} label="Linked" value={entry.directory ?? "local checkout"} /> : null}
250
265
  {entry.installedCommit ? <KeyValueRow labelWidth={10} width={rowWidth} emphasis={false} label="Commit" value={entry.installedCommit.slice(0, 7)} /> : null}
251
266
  {!entry.installed && entry.availableCommit ? <KeyValueRow labelWidth={10} width={rowWidth} emphasis={false} label="Pinned" value={`${entry.availableVersion ?? ""} ${entry.availableCommit.slice(0, 7)}`.trim()} /> : null}
@@ -258,7 +273,7 @@ function EntryDetail({ entry, width, host }: { entry: MarketplaceEntry; width: n
258
273
  runsExternalPlugins() ? (
259
274
  <Box paddingTop={1} flexDirection="column">
260
275
  <Text fg={colors.textDim}>Runs with your full permissions. Read the source first.</Text>
261
- <Text fg={colors.textBright}>{`gloomberb install ${entry.repo}`}</Text>
276
+ <Text fg={colors.textBright}>{`gloom install ${entry.repo}`}</Text>
262
277
  </Box>
263
278
  ) : (
264
279
  // No shell here, so the install command would be a dead end. Say
@@ -320,7 +335,7 @@ function BuiltinDetail({ entry, width, facts, scrollRef }: {
320
335
  <RemoteImage
321
336
  key={code}
322
337
  src={functionShotUrl(code)}
323
- alt={`${code} in Gloomberb`}
338
+ alt={`${code} in Gloom`}
324
339
  label={code}
325
340
  width={shotWidth}
326
341
  height={Math.max(8, Math.floor(shotWidth * 0.3))}
@@ -399,6 +414,25 @@ function PackChangeBody({ change, name }: { change: PackChange; name: (id: strin
399
414
 
400
415
  type Busy = { id: string; verb: "installing" | "updating" | "removing" } | null;
401
416
 
417
+ /**
418
+ * What the last action on one plugin ran into, said on its row: `status` in
419
+ * STATUS, `text` in its detail. It stands while the row's own status is what
420
+ * it was when the failure first showed, and goes when that moves: a toggle,
421
+ * a restart, a later install.
422
+ */
423
+ interface ActionFailure {
424
+ status: string;
425
+ text: string;
426
+ /** STATUS's tone; a failure by default. */
427
+ kind?: MarketplaceStatusKind;
428
+ }
429
+
430
+ function statusKey(entry: MarketplaceEntry): string {
431
+ const status = statusOf(entry);
432
+ return `${status.kind}:${status.text}`;
433
+ }
434
+ const entryId = (entry: MarketplaceEntry) => entry.id;
435
+
402
436
  export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
403
437
  const dialog = useDialog();
404
438
  const { createPaneFromTemplate, showPane, openPluginCommandWorkflow, notify } = usePluginAppActions();
@@ -418,14 +452,15 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
418
452
  // Bumped after any local change so the list is re-read from the host.
419
453
  const [localRevision, setLocalRevision] = useState(0);
420
454
  const [busy, setBusy] = useState<Busy>(null);
421
- const [lastError, setLastError] = useState<string | null>(null);
422
455
  // Remote heads for plugins the registry does not pin, so a private or
423
456
  // side-loaded install can report an update like every other row.
424
457
  const [remoteHeads, setRemoteHeads] = useState<Record<string, string>>({});
425
458
  const [checkingRemotes, setCheckingRemotes] = useState(false);
426
- const busyId = busy?.id ?? null;
459
+ const [remoteCheckError, setRemoteCheckError] = useState<string | null>(null);
460
+ const [remoteRevision, setRemoteRevision] = useState(0);
427
461
 
428
462
  const refresh = useCallback((force: boolean) => {
463
+ if (force) setRemoteRevision((value) => value + 1);
429
464
  setStatus((current) => (current === "ready" ? current : "loading"));
430
465
  void loadRegistry({ force }).then((result) => {
431
466
  setRegistry(result.plugins);
@@ -460,12 +495,22 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
460
495
  return facts;
461
496
  };
462
497
  }, [entries, host]);
498
+ // By plugin id, until the next action on the plugin. A failure takes the
499
+ // row's status as the list shows it after the action, and goes once that
500
+ // status moves on.
501
+ const { noteFor: failureFor, setNote: setFailure } = useRowNotes<MarketplaceEntry, ActionFailure>(entries, { key: entryId, signature: statusKey });
502
+ // What an action on one plugin is doing, or ran into, is said on its row.
503
+ const rowStatus = useCallback((entry: MarketplaceEntry): MarketplaceStatus => {
504
+ if (busy?.id === entry.id) return { kind: "none", text: busy.verb };
505
+ const failure = failureFor(entry);
506
+ return failure ? { kind: failure.kind ?? "failed", text: failure.status } : statusOf(entry);
507
+ }, [busy, failureFor]);
463
508
  const renderRow = useCallback((
464
509
  row: MarketplaceRow,
465
510
  column: Column,
466
511
  _index: number,
467
512
  rowState: { selected: boolean },
468
- ) => renderCell(row, column, rowState, busyId, factsFor), [busyId, factsFor]);
513
+ ) => renderCell(row, column, rowState, rowStatus, factsFor), [factsFor, rowStatus]);
469
514
 
470
515
  // Keyed by the folders themselves: the entries array is rebuilt whenever an
471
516
  // answer lands, and re-running the check on its own result would never stop.
@@ -476,24 +521,42 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
476
521
  const unlistedKey = unlistedDirectories.join(",");
477
522
 
478
523
  useEffect(() => {
524
+ const directories = unlistedKey ? unlistedKey.split(",") : [];
525
+ const eligible = new Set(directories);
526
+ // Forget removed or newly pinned/linked installs, including the last one.
527
+ setRemoteHeads((previous) => {
528
+ const retained = Object.entries(previous).filter(([directory]) => eligible.has(directory));
529
+ return retained.length === Object.keys(previous).length ? previous : Object.fromEntries(retained);
530
+ });
531
+ setRemoteCheckError(null);
479
532
  const check = manager?.remoteHeads;
480
- if (!check || unlistedKey.length === 0) return;
533
+ if (!check || directories.length === 0) {
534
+ setCheckingRemotes(false);
535
+ return;
536
+ }
481
537
  let cancelled = false;
482
538
  setCheckingRemotes(true);
483
- void check(unlistedKey.split(","))
484
- // Offline, or a private repository this machine has no credentials for:
485
- // the row keeps whatever it knew, without an error in the way.
486
- .catch(() => ({}))
487
- .then((heads) => {
539
+ void (async () => {
540
+ try {
541
+ const heads = await check(directories);
488
542
  if (cancelled) return;
489
- setRemoteHeads(heads);
490
- setCheckingRemotes(false);
491
- });
492
- return () => {
493
- cancelled = true;
494
- setCheckingRemotes(false);
495
- };
496
- }, [manager, unlistedKey, localRevision]);
543
+ // An omitted folder is unanswered, not proof that it is up to date.
544
+ const answers = directories.filter((directory) => !!heads[directory]);
545
+ setRemoteHeads((previous) => ({
546
+ ...previous,
547
+ ...Object.fromEntries(answers.map((directory) => [directory, heads[directory]!])),
548
+ }));
549
+ setRemoteCheckError(answers.length < directories.length ? "Update check incomplete" : null);
550
+ } catch (error) {
551
+ if (cancelled) return;
552
+ logFailure("plugin-marketplace.pane", "Check installed plugin remote heads failed")(error);
553
+ setRemoteCheckError("Plugin update check failed");
554
+ } finally {
555
+ if (!cancelled) setCheckingRemotes(false);
556
+ }
557
+ })();
558
+ return () => { cancelled = true; };
559
+ }, [manager, unlistedKey, localRevision, remoteRevision]);
497
560
 
498
561
  const visible = useMemo(
499
562
  () => filterEntries(entries, { query, category, showBuiltin }),
@@ -545,16 +608,23 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
545
608
  /**
546
609
  * The registry's code for this plugin needs a newer Gloomberb. Installing or
547
610
  * updating would land it anyway and it would then fail to compile, so say
548
- * what to do instead. Also leaves a working older checkout alone, and does
549
- * not update one that already needs a newer Gloomberb: updates only move
550
- * forward, so that would not help either.
611
+ * what to do instead, on the row: its STATUS already says `needs …` unless
612
+ * something outranks it there (`failed`, `needs restart`). Also leaves a
613
+ * working older checkout alone, and does not update one that already needs
614
+ * a newer Gloomberb: updates only move forward, so that would not help either.
551
615
  */
552
616
  const refuseTooNew = useCallback((entry: MarketplaceEntry): boolean => {
553
617
  const required = requiredGloomberb(entry.minGloomberb) ?? entry.needsGloomberb;
554
618
  if (!required) return false;
555
- notify({ body: `${entry.name} needs Gloomberb ${required}. Update Gloomberb first.`, type: "error" });
619
+ if (statusOf(entry).kind !== "needs-gloomberb") {
620
+ setFailure(entry.id, {
621
+ kind: "needs-gloomberb",
622
+ status: `needs ${required}`,
623
+ text: `${entry.name} needs Gloom ${required}. Update Gloom first.`,
624
+ });
625
+ }
556
626
  return true;
557
- }, [notify]);
627
+ }, [setFailure]);
558
628
 
559
629
  const installSelected = useCallback(async () => {
560
630
  if (!selected || !isInstallable(selected) || !manager || !host || busy) return;
@@ -576,12 +646,11 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
576
646
 
577
647
  const entry = selected;
578
648
  setBusy({ id: entry.id, verb: "installing" });
579
- setLastError(null);
649
+ setFailure(entry.id, null);
580
650
  const result = await manager.install(repo, pin);
581
651
  if (!result.ok) {
582
652
  setBusy(null);
583
- setLastError(result.error);
584
- notify({ body: `Could not install ${entry.name}: ${result.error}`, type: "error" });
653
+ setFailure(entry.id, { status: "install failed", text: `Could not install ${entry.name}: ${result.error}` });
585
654
  return;
586
655
  }
587
656
  const activated = await activateInstall(result, host, manager);
@@ -589,8 +658,8 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
589
658
  bump();
590
659
  if (activated.ok && activated.restart) notify({ body: `Restart to finish installing ${entry.name}.`, type: "info" });
591
660
  else if (activated.ok) announceAdded(activated.pluginId, activated.name, "Installed", host);
592
- else notify({ body: `${entry.name} installed but did not load: ${activated.error}`, type: "error" });
593
- }, [announceAdded, bump, busy, confirmWidth, dialog, host, manager, notify, refuseTooNew, selected]);
661
+ else setFailure(entry.id, { status: "load failed", text: `${entry.name} installed but did not load: ${activated.error}` });
662
+ }, [announceAdded, bump, busy, confirmWidth, dialog, host, manager, notify, refuseTooNew, selected, setFailure]);
594
663
 
595
664
  const updateSelected = useCallback(async () => {
596
665
  if (!selected || !isManaged(selected) || !manager || !host || busy || selected.linked) return;
@@ -599,12 +668,11 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
599
668
  const entry = selected;
600
669
  const reinstall = !!entry.loadError;
601
670
  setBusy({ id: entry.id, verb: "updating" });
602
- setLastError(null);
671
+ setFailure(entry.id, null);
603
672
  const result = await manager.update(directory, registryPin(entry));
604
673
  if (!result.ok) {
605
674
  setBusy(null);
606
- setLastError(result.error);
607
- notify({ body: `Could not update ${entry.name}: ${result.error}`, type: "error" });
675
+ setFailure(entry.id, { status: "update failed", text: `Could not update ${entry.name}: ${result.error}` });
608
676
  return;
609
677
  }
610
678
  if (result.kept) {
@@ -620,9 +688,21 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
620
688
  } else if (activated.ok) {
621
689
  announceAdded(activated.pluginId, activated.name, reinstall ? "Reloaded" : "Updated", host);
622
690
  } else {
623
- notify({ body: `${entry.name} updated but did not load: ${activated.error}`, type: "error" });
691
+ setFailure(entry.id, { status: "load failed", text: `${entry.name} updated but did not load: ${activated.error}` });
624
692
  }
625
- }, [announceAdded, bump, busy, host, manager, notify, refuseTooNew, selected]);
693
+ }, [announceAdded, bump, busy, host, manager, notify, refuseTooNew, selected, setFailure]);
694
+
695
+ /**
696
+ * A removal that failed is said on the plugin's row while the list still
697
+ * has one. A stopped plugin can leave the list, files and all; then only a
698
+ * toast is left to say it.
699
+ */
700
+ const failRemoval = useCallback((entry: MarketplaceEntry, failure: ActionFailure) => {
701
+ const installed = host?.listInstalled() ?? [];
702
+ const listed = mergeCatalog({ registry, installed, target, remoteHeads }).some((candidate) => candidate.id === entry.id);
703
+ if (listed) setFailure(entry.id, failure);
704
+ else notify({ body: failure.text, type: "error" });
705
+ }, [host, notify, registry, remoteHeads, setFailure, target]);
626
706
 
627
707
  const removeSelected = useCallback(async () => {
628
708
  if (!selected || !isManaged(selected) || !manager || !host || busy) return;
@@ -637,18 +717,27 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
637
717
  });
638
718
  if (!confirmed) return;
639
719
  setBusy({ id: entry.id, verb: "removing" });
640
- setLastError(null);
641
- await host.deactivate(entry.id, entry.directory).catch(() => {});
642
- await manager.deactivate?.(entry.id).catch(() => {});
643
- const result = await manager.remove(entry.directory!);
644
- setBusy(null);
645
- bump();
646
- if (result.ok) notify({ body: `Removed ${entry.name}.`, type: "success" });
647
- else {
648
- setLastError(result.error);
649
- notify({ body: `Could not remove ${entry.name}: ${result.error}`, type: "error" });
720
+ setFailure(entry.id, null);
721
+ let removalStarted = false;
722
+ try {
723
+ // Stop the native side first: a successful view stop drops its catalog
724
+ // entry, so it must remain available if the native stop fails.
725
+ await manager.deactivate?.(entry.id);
726
+ await host.deactivate(entry.id, entry.directory);
727
+ removalStarted = true;
728
+ const result = await manager.remove(entry.directory!);
729
+ if (result.ok) notify({ body: `Removed ${entry.name}.`, type: "success" });
730
+ else failRemoval(entry, { status: "remove failed", text: `Could not remove ${entry.name}: ${result.error}` });
731
+ } catch (error) {
732
+ logFailure("plugin-marketplace.pane", "Remove plugin failed")(error);
733
+ failRemoval(entry, removalStarted
734
+ ? { status: "remove failed", text: `Could not remove ${entry.name}. Removal failed. Restart and try again.` }
735
+ : { status: "stop failed", text: `Could not remove ${entry.name}. Plugin could not be stopped. Files kept. Restart before removing it.` });
736
+ } finally {
737
+ setBusy(null);
738
+ bump();
650
739
  }
651
- }, [bump, busy, confirmWidth, dialog, host, manager, notify, selected]);
740
+ }, [bump, busy, confirmWidth, dialog, failRemoval, host, manager, notify, selected]);
652
741
 
653
742
  const toggleSelected = useCallback(async () => {
654
743
  if (!host || !selected || !selected.installed || !selected.toggleable || selected.loadError) return;
@@ -735,7 +824,7 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
735
824
  }))}
736
825
  />
737
826
  ),
738
- }).catch(() => null);
827
+ }).catch(logFailure("plugin-marketplace.pane", "Choose starter pack failed", null));
739
828
  const pack = STARTER_PACKS.find((candidate) => candidate.id === id);
740
829
  if (pack) await applyPack(pack);
741
830
  }, [applyPack, currentPack, dialog, disabledPlugins, host]);
@@ -827,11 +916,7 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
827
916
  const info = loadingErrorFooterInfo(status === "loading", status === "error" ? "catalog unavailable" : null);
828
917
  if (stale) info.push({ id: "stale", parts: [{ text: "stale catalog", tone: "warning" }] });
829
918
  if (checkingRemotes) info.push({ id: "remote-check", parts: [{ text: "checking unlisted plugins", tone: "muted" }] });
830
- if (busy) {
831
- const name = entries.find((entry) => entry.id === busy.id)?.name ?? busy.id;
832
- info.push({ id: "busy", parts: [{ text: `${busy.verb} ${name}`, tone: "muted" }] });
833
- }
834
- if (lastError && !busy) info.push({ id: "op-error", parts: [{ text: lastError, tone: "warning" }] });
919
+ if (remoteCheckError) info.push({ id: "remote-error", parts: [{ text: remoteCheckError, tone: "warning" }] });
835
920
  const restartCount = entries.filter((entry) => entry.needsRestart).length;
836
921
  if (restartCount > 0 && !busy) {
837
922
  info.push({ id: "restart", parts: [{ text: "restart to finish loading", tone: "warning" }] });
@@ -868,8 +953,8 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
868
953
 
869
954
  const fit = useMemo(() => ({
870
955
  functions: Math.min(16, Math.max(9, ...visible.map((entry) => factsFor(entry).codes.join(" ").length))),
871
- status: Math.min(14, Math.max(7, ...visible.map((entry) => statusOf(entry).text.length))),
872
- }), [factsFor, visible]);
956
+ status: Math.min(14, Math.max(7, ...visible.map((entry) => rowStatus(entry).text.length))),
957
+ }), [factsFor, rowStatus, visible]);
873
958
  const columns = useMemo(() => buildColumns(width, fit), [fit, width]);
874
959
  // The pick is saved with the pane, so keep it listed (and resettable) even
875
960
  // when the catalog no longer has plugins in it.
@@ -917,7 +1002,7 @@ export function PluginMarketplacePane({ focused, width, height }: PaneProps) {
917
1002
  detailContent={selected
918
1003
  ? selected.bundled
919
1004
  ? <BuiltinDetail entry={selected} width={width} facts={factsFor(selected)} scrollRef={detailScrollRef} />
920
- : <EntryDetail entry={selected} width={width} host={host} />
1005
+ : <EntryDetail entry={selected} width={width} host={host} status={rowStatus(selected)} failure={failureFor(selected)} />
921
1006
  : null}
922
1007
  detailTitle={selected?.name}
923
1008
  rootBefore={(
@@ -1,10 +1,11 @@
1
1
  import type { CliCommandContext } from "../../../../types/plugin";
2
2
  import type { TickerRecord } from "../../../../types/ticker";
3
3
  import type { MarketContext } from "../../../../cli/types";
4
- import { listingFields, loadSavedListing, parseListingArg, savedListingName } from "../../../../cli/listing-arg";
4
+ import { listingFields, loadSavedListingOf, savedListingCandidates, savedListingName } from "../../../../cli/listing-arg";
5
5
  import { withSuggestion } from "../../../../cli/listing-suggestion";
6
6
  import { StagedTickerRepository } from "../../../../cli/staged-tickers";
7
- import { splitSymbolWords, symbolInputKey } from "../../../../cli/symbol-args";
7
+ import { splitSymbolArgs, splitSymbolWords, symbolInputKey } from "../../../../cli/symbol-args";
8
+ import { notASymbolError } from "../../../../cli/symbol-cells";
8
9
  import { readSymbolList, SymbolListError } from "../../../../cli/symbol-list";
9
10
  import { cliStyles } from "../../../../utils/cli-output";
10
11
  import { dryRunNote } from "../../../../cli/helpers";
@@ -37,23 +38,35 @@ export const FILE_OPTION = {
37
38
  export const SYMBOL_LIST_HELP = {
38
39
  title: "Several symbols",
39
40
  lines: [
40
- "add and remove take any number of symbols split by spaces, commas or newlines, a --file, or piped input (or -). An entry of a list with commas or newlines that has spaces in it is a company's name when search finds one, read as its US listing first with a line under it saying which, and otherwise the symbols it holds. A CSV is read from its Symbol, Ticker or Code column. Without that header every cell is a symbol, unless names, prices or weights sit beside them: then the first column is read and the cells left out are named. Quote a list name with spaces.",
41
- "Each symbol gets a line, then a summary. Exit status 0 when at least one symbol was added (or removed) or already was, 1 when none resolved or the usage is wrong.",
42
- "A bare US-style symbol is not added when the listing it matched stopped trading, is abroad (DFS as DFS:LSE) or was taken by another company in the last two years (BRKL), or when nothing lists under it now and its company trades as another. Those are listed under Needs a choice, with the add for what their company trades as now and the listing that matched, which naming keeps; one symbol alone fails with the same. A symbol another company took longer ago says so under its line.",
41
+ "add and remove take any number of symbols split by spaces, commas or newlines, a --file, or piped input (or -). An entry of a list with commas, semicolons or newlines that has spaces in it is a company's name when search finds one (philip morris intl is PM), read as its US listing first with a line under it saying which, else the symbols it holds when each is written as a symbol or has a quote (AAPL MSFT), and otherwise a name not found. A file or piped list reads each line as such an entry. A CSV is read from its Symbol, Ticker or Code column, else its Name or Company column as names. Without that header every cell is a symbol, unless names, prices or weights sit beside them: then the first column is read and the cells left out are named. Each line is split by its own tab, semicolon, comma or spaces, and a price is never a symbol, written either way (2.582,52 or 1,899.21). A comma between plain digits (0700,25) splits them unless the line also has a tab, a semicolon or spaces between cells. Quote a list name with spaces.",
42
+ "Each symbol gets a line, then a summary. An entry that cannot be a symbol (N/A, $$$, --, a price alone) is named as not a symbol without a lookup. Symbols Gloom Cloud did not answer for are listed together with one command that adds them again. Exit status 0 when at least one symbol was added (or removed) or already was, 1 when none resolved or the usage is wrong.",
43
+ "A bare US-style symbol is not added when the listing it matched stopped trading, is abroad while the symbol also names a US listing (DFS as DFS:LSE, Discover's old symbol) or was taken by another company in the last two years (BRKL), or when nothing lists under it now and its company trades as another. Those are listed under Needs a choice, with the add for what their company trades as now and the listing that matched, which naming keeps; one symbol alone fails with the same. A symbol another company took longer ago says so under its line, and one listed only abroad (GARAN) is added with a line naming its venue and currency.",
44
+ "A vendor's suffix is the venue it names: GARAN.IS is stored as GARAN:BIST, the same row GARAN:BIST and GARAN add to.",
43
45
  "add --dry-run looks every symbol up and prints the same lines without saving.",
44
46
  ],
45
47
  };
46
48
 
49
+ /**
50
+ * One word of a command line as a shell reads it back: as written when it
51
+ * holds nothing a shell treats specially (`NVDA`, `SAN:EPA`, `BRK.B`), else
52
+ * in single quotes (`'Altria Group'`, `'$nvda'`, `'AT&T'`).
53
+ */
54
+ export function shellWord(word: string): string {
55
+ return /^[\p{L}\p{N}._:@%+,/][\p{L}\p{N}._:@%+,/=-]*$/u.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
56
+ }
57
+
47
58
  /**
48
59
  * A row's line, and under it the listing a company's name was read as, the
49
- * company that had the symbol before the listing added, and the coin hint
50
- * when a bare coin ticker was read as something else.
60
+ * company that had the symbol before the listing added, the venue and
61
+ * currency of a bare symbol added on its only listing, abroad, and the coin
62
+ * hint when a bare coin ticker was read as something else.
51
63
  */
52
64
  function rowLines(row: SymbolRow, verb: Verb): string {
53
65
  return [
54
66
  rowLine(row, verb),
55
67
  ...row.readAs ? [` ${cliStyles.muted(row.readAs)}`] : [],
56
68
  ...row.changeNote ? [` ${cliStyles.warning(row.changeNote)}`] : [],
69
+ ...row.abroad ? [` ${cliStyles.warning(row.abroad)}`] : [],
57
70
  ...row.hint ? [` ${cliStyles.warning(row.hint)}`] : [],
58
71
  ].join("\n");
59
72
  }
@@ -71,9 +84,28 @@ function rowLine(row: SymbolRow, verb: Verb): string {
71
84
  case "not_found": return cliStyles.danger(`Not found: ${typed}${row.message && !NO_MATCH.test(row.message) ? ` (${reason(row.message, typed)})` : ""}${row.suggestion ? `; did you mean ${row.suggestion}?` : ""}`);
72
85
  case "needs_choice": return cliStyles.warning(` ${row.message ?? typed}`);
73
86
  case "failed": return cliStyles.danger(`Not ${verb === "add" ? "added" : "removed"}: ${typed} (${failedReason(row, typed)})`);
87
+ // Named as written (`$$$`), and a figure says it is one.
88
+ case "not_a_symbol": return cliStyles.warning(`Not a symbol: ${row.input.trim()}${row.message?.includes("a number") ? " (a number)" : ""}`);
89
+ case "not_looked_up": return cliStyles.danger(`Not looked up: ${typed}`);
74
90
  }
75
91
  }
76
92
 
93
+ /**
94
+ * The symbols of an add Gloom Cloud did not answer for, after the other
95
+ * lines: one line naming them as they were written, and one command that adds
96
+ * them all again, each entry one shell word (`'philip morris intl'`).
97
+ */
98
+ function unansweredLines(rows: readonly SymbolRow[], retry: ((keys: string) => string) | undefined): string[] {
99
+ const unanswered = rows.filter((row) => row.status === "not_looked_up").map((row) => row.input.trim());
100
+ if (unanswered.length === 0) return [];
101
+ const count = unanswered.length === 1 ? "1 symbol" : `${unanswered.length} symbols`;
102
+ return [
103
+ "",
104
+ cliStyles.danger(`Gloom Cloud did not answer for ${count}, so ${unanswered.length === 1 ? "it was" : "they were"} not looked up: ${unanswered.join(", ")}`),
105
+ ...retry ? [`Try again in a minute: ${retry(unanswered.map(shellWord).join(" "))}`] : [],
106
+ ];
107
+ }
108
+
77
109
  /**
78
110
  * What symbols that need a choice offer: the add that takes the companies
79
111
  * they trade as now, and the add that keeps each match as it is by naming its
@@ -82,7 +114,7 @@ function rowLine(row: SymbolRow, verb: Verb): string {
82
114
  function choiceOffers(choices: readonly SymbolRow[], retry: ((keys: string) => string) | undefined): { successors: string | null; matches: string | null } {
83
115
  const successors = [...new Set(choices.flatMap((row) => row.tradesAs ? [row.tradesAs] : []))];
84
116
  const matches = [...new Set(choices.flatMap((row) => row.listing ? [row.listing] : []))];
85
- const command = (keys: string[]) => retry ? retry(keys.join(" ")) : keys.join(" ");
117
+ const command = (keys: string[]) => retry ? retry(keys.map(shellWord).join(" ")) : keys.join(" ");
86
118
  const one = choices.length === 1;
87
119
  return {
88
120
  successors: successors.length > 0 ? `Add what ${one ? "it trades" : "they trade"} as now: ${command(successors)}` : null,
@@ -131,18 +163,21 @@ export function printSymbolRows(
131
163
  ): void {
132
164
  const summary = `${collection.kind === "watchlist" ? "Watchlist" : "Portfolio"} "${collection.name}": ${summarizeSymbolRows(rows, verb, repeated)}`;
133
165
  const count = (status: SymbolStatus) => rows.filter((row) => row.status === status).length;
166
+ const extra = (key: string, status: SymbolStatus) => count(status) > 0 ? { [key]: count(status) } : {};
134
167
  const counts = verb === "add"
135
168
  ? { added: count("added"), alreadyListed: count("already_listed"), notFound: count("not_found"), notAdded: count("failed"),
136
- ...count("needs_choice") > 0 ? { needsChoice: count("needs_choice") } : {} }
137
- : { removed: count("removed"), notListed: count("not_listed"), notFound: count("not_found"), notRemoved: count("failed") };
169
+ ...extra("needsChoice", "needs_choice"), ...extra("notLookedUp", "not_looked_up"), ...extra("notASymbol", "not_a_symbol") }
170
+ : { removed: count("removed"), notListed: count("not_listed"), notFound: count("not_found"), notRemoved: count("failed"),
171
+ ...extra("notASymbol", "not_a_symbol") };
138
172
  ctx.printResult({
139
173
  data: rows,
140
174
  metadata: { [collection.kind]: collection.name, ...counts, repeated, summary, ...dryRun ? { dryRun } : {} },
141
175
  ...notes.length > 0 ? { warnings: [...notes] } : {},
142
176
  }, {
143
177
  text: () => [
144
- ...rows.filter((row) => row.status !== "needs_choice").map((row) => rowLines(row, verb)),
178
+ ...rows.filter((row) => row.status !== "needs_choice" && row.status !== "not_looked_up").map((row) => rowLines(row, verb)),
145
179
  ...choiceLines(rows, retry),
180
+ ...unansweredLines(rows, retry),
146
181
  "",
147
182
  `${summary}${dryRunNote(dryRun)}`,
148
183
  ].join("\n"),
@@ -155,16 +190,18 @@ export function printSymbolRows(
155
190
  /**
156
191
  * `watchlist add` and `portfolio add` given one symbol: added as a symbol of a
157
192
  * list is (`addSymbol`), and said as one listing, added or already there with
158
- * its notes as warnings. A symbol a list would leave for a choice fails the
193
+ * its notes as warnings, after `notes` on what was left out of the input (the
194
+ * price of `ED 2.582,52`). A symbol a list would leave for a choice fails the
159
195
  * command, saying why, with what a list offers under the error; any other
160
196
  * failure fails it as the lookup's error, a busy service's to retry.
161
197
  */
162
198
  export async function addOneSymbol(
163
199
  ctx: CliCommandContext,
164
200
  market: MarketContext,
165
- { symbol, exchange, collection, retry, reject, add }: {
201
+ { symbol, exchange, collection, notes: inputNotes = [], retry, reject, add }: {
166
202
  symbol: string;
167
203
  exchange: string | undefined;
204
+ notes?: readonly string[];
168
205
  collection: CollectionRef;
169
206
  /** The add of one listing, for the choices a failure names. */
170
207
  retry: (key: string) => string;
@@ -189,7 +226,8 @@ export async function addOneSymbol(
189
226
  const { change, formerIssuer, notes } = lookup;
190
227
  const data = { changed, ...listingFields(saved), [collection.kind]: collection.name,
191
228
  ...change ? { tickerChange: change } : {}, ...formerIssuer ? { formerIssuer } : {}, ...dryRun ? { dryRun } : {} };
192
- const warnings = notes.length > 0 ? notes : undefined;
229
+ const said = [...inputNotes, ...notes, ...row.abroad ? [row.abroad] : []];
230
+ const warnings = said.length > 0 ? said : undefined;
193
231
  ctx.printResult({ data, warnings }, {
194
232
  text: () => changed
195
233
  ? `${cliStyles.success(`Added ${saved.label} to "${collection.name}".`)}${dryRunNote(dryRun)}`
@@ -208,7 +246,9 @@ export function retryNotice(ctx: CliCommandContext): (count: number, waitMs: num
208
246
  /**
209
247
  * The symbols an add or remove was given, failing the command when there are
210
248
  * none or the file cannot be read. An add reads a typed company's name with
211
- * `readNameEntries`; without it (a remove) every entry is its words.
249
+ * `readNameEntries`; without it (a remove) every entry is its words. `single`
250
+ * is the one symbol typed alone, and `notes` what the single command says
251
+ * beside it: none when it goes as typed, else what was left out of it.
212
252
  */
213
253
  export async function requireSymbolList(
214
254
  ctx: CliCommandContext,
@@ -218,16 +258,23 @@ export async function requireSymbolList(
218
258
  readEntries?: (entries: string[]) => Promise<string[]>,
219
259
  ): Promise<{ symbols: string[]; repeated: number; notes: string[]; single: string | null }> {
220
260
  try {
221
- let typed: string[] = [];
261
+ // The first entries read are what was typed; a file's and standard input's come after.
262
+ let typed: string[] | null = null;
222
263
  const list = await readSymbolList({ args, file, readEntries: async (entries) => {
223
- typed = readEntries ? await readEntries(entries) : splitSymbolWords(entries);
224
- return typed;
264
+ const read = readEntries ? await readEntries(entries) : splitSymbolWords(entries);
265
+ typed ??= read;
266
+ return read;
225
267
  } });
226
- if (list.symbols.length === 0) ctx.fail(usage);
268
+ // Only numbers or strays says what it left out, not only how to call the command.
269
+ if (list.symbols.length === 0) ctx.fail(list.notes.length > 0 ? ["No symbols.", ...list.notes].join(" ") : usage, list.notes.length > 0 ? usage : undefined);
227
270
  // One symbol typed alone keeps the single-symbol output and errors. It goes as typed, so a separator
228
- // left on it (`UPST;`) is named by the listing it resolves to.
229
- const single = file == null && !args.includes("-") && typed.length === 1 ? (args.length === 1 ? args[0]!.trim() : typed[0]!) : null;
230
- return { ...list, single };
271
+ // left on it (`UPST;`) is named by the listing it resolves to; without the figure typed beside it
272
+ // (`ED 2.582,52`), which the notes name.
273
+ const entries: string[] = typed ?? [];
274
+ const split = splitSymbolArgs(args);
275
+ const asTyped = args.length === 1 && split.length === 1 && split[0] === entries[0];
276
+ const single = file == null && !args.includes("-") && entries.length === 1 ? (asTyped ? args[0]!.trim() : entries[0]!) : null;
277
+ return { ...list, single, notes: single && asTyped ? [] : list.notes };
231
278
  } catch (error) {
232
279
  if (error instanceof SymbolListError) ctx.fail(error.message, usage);
233
280
  throw error;
@@ -245,29 +292,33 @@ export async function removeSymbols({
245
292
  exchange,
246
293
  store,
247
294
  reject,
295
+ holds,
248
296
  remove,
249
297
  }: BulkSymbolsOptions & {
250
298
  reject?: (input: string) => Error | null;
299
+ /** Whether the list being changed holds the row: of two a symbol may be saved as (GARAN:BIST and GARAN.IS), the one it holds. */
300
+ holds?: (ticker: TickerRecord) => boolean;
251
301
  remove: (ticker: TickerRecord) => { changed: boolean; ticker: TickerRecord; extra?: Record<string, unknown> };
252
302
  }): Promise<SymbolRow[]> {
253
303
  const staged = new StagedTickerRepository(store);
254
304
  const rows: SymbolRow[] = [];
255
305
  for (const input of symbols) {
256
- const refusal = reject?.(input);
306
+ const refusal = notASymbolError(input) ?? reject?.(input);
257
307
  if (refusal) {
258
308
  rows.push(failedRow(input, refusal));
259
309
  continue;
260
310
  }
261
- let listing;
311
+ let listings;
262
312
  try {
263
- listing = parseListingArg(input, exchange, { ownExchangeWins: true });
313
+ // GARAN.IS is the row of GARAN:BIST, else a row still saved under that key (a broker's).
314
+ listings = savedListingCandidates(input, exchange, { ownExchangeWins: true });
264
315
  } catch (error) {
265
316
  rows.push(failedRow(input, error instanceof Error ? error : new Error(String(error))));
266
317
  continue;
267
318
  }
268
- const ticker = listing.symbol ? await loadSavedListing(staged, listing) : null;
319
+ const ticker = listings[0]!.symbol ? (await loadSavedListingOf(staged, listings, holds))?.ticker : null;
269
320
  if (!ticker) {
270
- rows.push(failedRow(input, new Error(`Ticker "${listing.key}" was not found in your local data.`), "not_found"));
321
+ rows.push(failedRow(input, new Error(`Ticker "${listings[0]!.key}" was not found in your local data.`), "not_found"));
271
322
  continue;
272
323
  }
273
324
  const result = remove(ticker);