gloomberb 0.18.3 → 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 (886) 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/src/account/checkout-handoff.tsx +225 -0
  6. package/src/account/device-signin-dialog.tsx +2 -2
  7. package/src/account/device-signin.ts +2 -2
  8. package/src/account/upgrade-dialog.tsx +1 -1
  9. package/src/api-client/askg.ts +2 -2
  10. package/src/api-client/auth.ts +2 -2
  11. package/src/api-client/central-bank-rates.ts +6 -0
  12. package/src/api-client/collections.ts +5 -1
  13. package/src/api-client/cot.ts +2 -0
  14. package/src/api-client/crypto-etf-flows.ts +37 -0
  15. package/src/api-client/crypto-markets.ts +60 -0
  16. package/src/api-client/data.ts +84 -5
  17. package/src/api-client/doe.ts +15 -0
  18. package/src/api-client/equity-screener.ts +28 -0
  19. package/src/api-client/exchange-announcements.ts +38 -0
  20. package/src/api-client/fund-facts.ts +39 -0
  21. package/src/api-client/futures-curve.ts +9 -1
  22. package/src/api-client/hiring.ts +301 -0
  23. package/src/api-client/index.ts +22 -5
  24. package/src/api-client/market-discovery.ts +14 -0
  25. package/src/api-client/members.ts +6 -0
  26. package/src/api-client/mna.ts +6 -0
  27. package/src/api-client/paths.ts +18 -1
  28. package/src/api-client/request.ts +21 -4
  29. package/src/api-client/short-side-publications.ts +37 -0
  30. package/src/api-client/socket.ts +64 -4
  31. package/src/api-client/swap-curve.ts +35 -0
  32. package/src/api-client/themes.ts +7 -1
  33. package/src/api-client/types.ts +90 -2
  34. package/src/app/pane-runtime/plugin-bindings.ts +9 -2
  35. package/src/app/runtime/desktop-deeplink.ts +5 -5
  36. package/src/app/runtime/plugin-bindings.ts +2 -2
  37. package/src/app/runtime/startup.ts +13 -0
  38. package/src/app/star-prompt/status-line.tsx +2 -2
  39. package/src/architecture/core-builtin-allowlist.json +5 -2
  40. package/src/architecture/ui-literals-allowlist.json +3 -3
  41. package/src/architecture/ui-tokens-allowlist.json +1 -1
  42. package/src/brokers/broker-portfolio-sync.ts +37 -0
  43. package/src/brokers/directory.ts +11 -0
  44. package/src/brokers/logos.ts +3 -1
  45. package/src/brokers/signed-in/adapter.ts +304 -26
  46. package/src/brokers/signed-in/client.ts +14 -2
  47. package/src/brokers/signed-in/connect.ts +10 -2
  48. package/src/brokers/sync-broker-instance.ts +9 -1
  49. package/src/capabilities/chart-series.ts +22 -6
  50. package/src/cli/bank-section.ts +1 -1
  51. package/src/cli/base-converter.ts +23 -14
  52. package/src/cli/central-bank-events.ts +1 -1
  53. package/src/cli/command-options.ts +59 -4
  54. package/src/cli/commands/api.ts +9 -6
  55. package/src/cli/commands/broker.ts +2 -2
  56. package/src/cli/commands/command-utils.ts +2 -2
  57. package/src/cli/commands/crypto-hints.ts +7 -20
  58. package/src/cli/commands/fred-series.ts +4 -2
  59. package/src/cli/commands/keybindings.ts +2 -2
  60. package/src/cli/commands/market.ts +1117 -264
  61. package/src/cli/commands/news-hints.ts +2 -2
  62. package/src/cli/commands/news-search.ts +59 -6
  63. package/src/cli/commands/options-leaps.ts +41 -26
  64. package/src/cli/commands/overview.ts +121 -48
  65. package/src/cli/commands/plugins.ts +9 -9
  66. package/src/cli/commands/remote.ts +13 -10
  67. package/src/cli/commands/search.ts +64 -44
  68. package/src/cli/commands/system.ts +27 -19
  69. package/src/cli/commands/themes.ts +1 -1
  70. package/src/cli/commands/ticker.ts +192 -47
  71. package/src/cli/context.ts +3 -2
  72. package/src/cli/desktop-pane-shot-view.ts +2 -2
  73. package/src/cli/desktop-pane-shot.ts +1 -1
  74. package/src/cli/earnings-calendar.ts +4 -4
  75. package/src/cli/entry.ts +18 -1
  76. package/src/cli/errors.ts +20 -2
  77. package/src/cli/exit-codes.ts +1 -1
  78. package/src/cli/export-source.ts +10 -4
  79. package/src/cli/freshness.ts +2 -8
  80. package/src/cli/fx-forward.ts +130 -0
  81. package/src/cli/fx-pair.ts +10 -9
  82. package/src/cli/glossary.ts +64 -20
  83. package/src/cli/help.ts +19 -13
  84. package/src/cli/history-export.ts +126 -0
  85. package/src/cli/history-rows.ts +196 -43
  86. package/src/cli/index-roots.ts +5 -45
  87. package/src/cli/index.ts +24 -18
  88. package/src/cli/known-gaps.ts +118 -25
  89. package/src/cli/listing-arg.ts +300 -41
  90. package/src/cli/name-entries.ts +30 -18
  91. package/src/cli/native-smoke.ts +2 -0
  92. package/src/cli/not-a-ticker.ts +19 -9
  93. package/src/cli/opentui-libc.ts +5 -0
  94. package/src/cli/options.ts +12 -0
  95. package/src/cli/pane-functions/capabilities.ts +75 -20
  96. package/src/cli/pane-functions/catalog.ts +24 -23
  97. package/src/cli/pane-functions/discovery.ts +1 -1
  98. package/src/cli/pane-functions/dom.ts +10 -7
  99. package/src/cli/pane-functions/financial-ratio-shot.ts +2 -2
  100. package/src/cli/pane-functions/freshness.ts +174 -23
  101. package/src/cli/pane-functions/function-help.ts +66 -27
  102. package/src/cli/pane-functions/headless.ts +24 -11
  103. package/src/cli/pane-functions/index.ts +18 -16
  104. package/src/cli/pane-functions/options.ts +22 -2
  105. package/src/cli/pane-functions/report-notices.ts +12 -4
  106. package/src/cli/pane-functions/report.ts +5 -5
  107. package/src/cli/pane-functions/resolver.ts +47 -10
  108. package/src/cli/pane-functions/screenshot-assessment.ts +322 -0
  109. package/src/cli/pane-functions/screenshot-bridge.ts +152 -0
  110. package/src/cli/pane-functions/screenshot-chart-evidence.ts +396 -0
  111. package/src/cli/pane-functions/screenshot-data-evidence.ts +383 -0
  112. package/src/cli/pane-functions/screenshot-evidence.ts +1 -1
  113. package/src/cli/pane-functions/screenshot-payload.ts +453 -0
  114. package/src/cli/pane-functions/screenshot-render.ts +316 -0
  115. package/src/cli/pane-functions/screenshot-types.ts +162 -0
  116. package/src/cli/pane-functions/shot-options.ts +8 -3
  117. package/src/cli/peso-dollar-rates.ts +79 -0
  118. package/src/cli/quote-answer.ts +38 -3
  119. package/src/cli/report-tables.ts +61 -16
  120. package/src/cli/restore-plugins.ts +29 -14
  121. package/src/cli/result.ts +111 -22
  122. package/src/cli/series-search.ts +5 -5
  123. package/src/cli/statement-filings.ts +19 -0
  124. package/src/cli/suffixed-listing-rows.ts +49 -0
  125. package/src/cli/symbol-args.ts +50 -15
  126. package/src/cli/symbol-cells.ts +120 -0
  127. package/src/cli/symbol-list.ts +198 -68
  128. package/src/cli/symbol-rows.ts +131 -25
  129. package/src/cli/ticker-resolution.ts +108 -11
  130. package/src/cli/treasury-yields.ts +5 -5
  131. package/src/cli/types.ts +2 -0
  132. package/src/cli/uncovered-instrument.ts +3 -3
  133. package/src/cli/unknown-command.ts +6 -6
  134. package/src/components/chart/composite/chart-layout.ts +131 -0
  135. package/src/components/chart/composite/composite-chart.tsx +373 -16
  136. package/src/components/chart/composite/interactions.ts +7 -2
  137. package/src/components/chart/composite/legend.tsx +75 -3
  138. package/src/components/chart/composite/levels.ts +5 -0
  139. package/src/components/chart/composite/panel-gestures.ts +41 -0
  140. package/src/components/chart/composite/panel-labels.ts +2 -1
  141. package/src/components/chart/composite/panel-surface.tsx +305 -11
  142. package/src/components/chart/composite/price-axis-labels.tsx +27 -3
  143. package/src/components/chart/composite/rasterizer.ts +44 -19
  144. package/src/components/chart/composite/scene.ts +16 -4
  145. package/src/components/chart/composite/text-renderer.ts +48 -21
  146. package/src/components/chart/composite/toolbar.tsx +52 -5
  147. package/src/components/chart/composite/types.ts +13 -0
  148. package/src/components/chart/composite/use-composite-chart-keyboard.ts +120 -13
  149. package/src/components/chart/composite/use-composite-value-scales.ts +44 -0
  150. package/src/components/chart/composite/use-composite-viewport.ts +12 -3
  151. package/src/components/chart/composite/value-scale.ts +149 -0
  152. package/src/components/chart/composite/volume-profile-paint.ts +6 -3
  153. package/src/components/chart/core/pointer.ts +2 -0
  154. package/src/components/chart-table/index.ts +2 -0
  155. package/src/components/chart-table/read.tsx +74 -0
  156. package/src/components/command-bar/commands/collection-list.ts +41 -19
  157. package/src/components/command-bar/commands/collection.ts +9 -3
  158. package/src/components/command-bar/commands/direct/index.ts +1 -1
  159. package/src/components/command-bar/commands/direct/runtime.ts +3 -2
  160. package/src/components/command-bar/pane-templates/items.ts +59 -6
  161. package/src/components/command-bar/pane-templates/workflow.ts +10 -1
  162. package/src/components/command-bar/routes/root/plugin-install.ts +1 -1
  163. package/src/components/command-bar/routes/root/provider-search.ts +42 -11
  164. package/src/components/command-bar/routes/root/results.ts +18 -4
  165. package/src/components/command-bar/routes/root/runtime.ts +15 -3
  166. package/src/components/command-bar/routes/root/search-providers.ts +25 -2
  167. package/src/components/command-bar/routes/root/selection.ts +6 -1
  168. package/src/components/command-bar/routes/root/shortcut-feedback.ts +4 -1
  169. package/src/components/command-bar/routes/root/shortcuts.ts +30 -3
  170. package/src/components/command-bar/routes/ticker-search/results.ts +73 -4
  171. package/src/components/command-bar/selection-runtime.ts +50 -10
  172. package/src/components/command-bar/surface/index.tsx +24 -2
  173. package/src/components/command-bar/view-model.ts +7 -0
  174. package/src/components/command-bar/workflow/broker.ts +2 -2
  175. package/src/components/command-bar/workflow/collection-actions.ts +46 -7
  176. package/src/components/command-bar/workflow/collection-ticker.ts +32 -4
  177. package/src/components/command-bar/workflow/fields.ts +8 -2
  178. package/src/components/command-bar/workflow/ops.ts +18 -4
  179. package/src/components/command-bar/workflow/portfolio-position.ts +53 -4
  180. package/src/components/command-bar/workflow/submit.ts +3 -2
  181. package/src/components/command-bar/workflow/tickers.ts +5 -1
  182. package/src/components/company-logo.tsx +39 -0
  183. package/src/components/data-table/sections.ts +4 -2
  184. package/src/components/form-modal/confirm.tsx +2 -1
  185. package/src/components/form-modal/request.ts +5 -0
  186. package/src/components/form-modal/routes.ts +4 -1
  187. package/src/components/index.ts +12 -6
  188. package/src/components/layout/header.tsx +7 -6
  189. package/src/components/layout/market-summary.ts +9 -6
  190. package/src/components/layout/pane/capture-status.ts +1 -1
  191. package/src/components/layout/pane/footer/model.ts +11 -5
  192. package/src/components/layout/shell/chrome.ts +19 -6
  193. package/src/components/layout/shell/index.tsx +2 -1
  194. package/src/components/layout/shell/pane/layers.tsx +19 -1
  195. package/src/components/layout/status-bar.tsx +2 -2
  196. package/src/components/market-board/index.tsx +11 -3
  197. package/src/components/onboarding/onboarding-wizard.tsx +1 -0
  198. package/src/components/onboarding/portfolio-step/broker-setup-panel.tsx +2 -2
  199. package/src/components/onboarding/wizard-broker-sync.ts +4 -1
  200. package/src/components/pane-settings-dialog/desktop.tsx +20 -7
  201. package/src/components/pane-settings-dialog/tui.tsx +17 -9
  202. package/src/components/pane-settings-dialog/value.ts +8 -1
  203. package/src/components/pane-settings-dialog.tsx +44 -13
  204. package/src/components/sign-in-code-panel.tsx +27 -19
  205. package/src/components/ui/column-grid.tsx +48 -4
  206. package/src/components/ui/country-flag-art.tsx +180 -0
  207. package/src/components/ui/country-flag.tsx +118 -12
  208. package/src/components/ui/data-table/opentui/index.tsx +61 -2
  209. package/src/components/ui/data-table/opentui/model.ts +109 -2
  210. package/src/components/ui/data-table/types.ts +1 -1
  211. package/src/components/ui/display.tsx +5 -1
  212. package/src/components/ui/index.ts +3 -1
  213. package/src/components/ui/logo-mark.tsx +11 -2
  214. package/src/components/ui/query-bar.tsx +1 -1
  215. package/src/components/ui/row-notes.ts +79 -0
  216. package/src/core/state/app/layout.ts +4 -4
  217. package/src/data/cached-query.ts +8 -1
  218. package/src/data/config/chart-settings.ts +3 -3
  219. package/src/data/config/home.ts +10 -2
  220. package/src/data/config/layout.ts +4 -1
  221. package/src/data/config/store/node.ts +4 -0
  222. package/src/data/config/store/normalize.ts +13 -3
  223. package/src/data/config/store/portfolio-as-of-column.ts +2 -2
  224. package/src/data/json-storage.ts +2 -0
  225. package/src/data/saved-copies.ts +84 -0
  226. package/src/docs/snapshot.json +65 -32
  227. package/src/i18n/es.ts +29 -12
  228. package/src/i18n/ja.ts +29 -12
  229. package/src/i18n/ko.ts +29 -12
  230. package/src/i18n/zh-cn.ts +29 -12
  231. package/src/i18n/zh-tw.ts +29 -12
  232. package/src/layout/desks.ts +1 -1
  233. package/src/layout/launch-layout.ts +1 -1
  234. package/src/layout/pane-manager/floating-actions.ts +5 -3
  235. package/src/layout/pane-manager/floating.ts +43 -1
  236. package/src/layout/pane-manager/index.ts +2 -0
  237. package/src/layout/selected-ticker.ts +74 -0
  238. package/src/market-data/depositary-pair.ts +251 -0
  239. package/src/market-data/futures-board.ts +1 -1
  240. package/src/market-data/futures-contract.ts +124 -40
  241. package/src/market-data/fx-closes.ts +17 -2
  242. package/src/market-data/hooks.tsx +19 -11
  243. package/src/market-data/market/change-basis.ts +49 -7
  244. package/src/market-data/market/freshness.ts +34 -5
  245. package/src/market-data/market/period-return.ts +163 -0
  246. package/src/market-data/market/trading-sessions.ts +13 -0
  247. package/src/market-data/market/year-to-date.ts +8 -13
  248. package/src/market-data/performance.ts +20 -14
  249. package/src/market-data/perpetual-premium.ts +32 -0
  250. package/src/market-data/published-apac-sessions.ts +2 -1
  251. package/src/market-data/published-futures-sessions.ts +69 -0
  252. package/src/market-data/quotes/contributions.ts +5 -1
  253. package/src/market-data/quotes/freshness.ts +42 -0
  254. package/src/market-data/quotes/intraday-bar.ts +37 -0
  255. package/src/market-data/quotes/regular-session-valuation.ts +13 -8
  256. package/src/market-data/quotes/resolution.ts +4 -2
  257. package/src/market-data/tax-equivalent.ts +1 -1
  258. package/src/market-data/ticker-price-returns.ts +3 -1
  259. package/src/news/aggregator.ts +5 -1
  260. package/src/news/news-model.ts +4 -0
  261. package/src/news/types.ts +11 -0
  262. package/src/notifications/app-notifier.ts +1 -1
  263. package/src/plugins/auto-update.ts +2 -2
  264. package/src/plugins/builtin/account-management/index.tsx +1 -0
  265. package/src/plugins/builtin/account-management/pane.tsx +9 -8
  266. package/src/plugins/builtin/alerts/alert-engine.ts +29 -19
  267. package/src/plugins/builtin/alerts/calendar-events.ts +378 -0
  268. package/src/plugins/builtin/alerts/cli.ts +485 -76
  269. package/src/plugins/builtin/alerts/command.ts +5 -10
  270. package/src/plugins/builtin/alerts/event-rows.ts +9 -5
  271. package/src/plugins/builtin/alerts/events-pane.tsx +71 -56
  272. package/src/plugins/builtin/alerts/events.ts +107 -5
  273. package/src/plugins/builtin/alerts/fired-log.ts +78 -0
  274. package/src/plugins/builtin/alerts/format.ts +2 -1
  275. package/src/plugins/builtin/alerts/headless.ts +100 -39
  276. package/src/plugins/builtin/alerts/history-pane.tsx +105 -62
  277. package/src/plugins/builtin/alerts/history-rows.ts +89 -0
  278. package/src/plugins/builtin/alerts/index.tsx +45 -18
  279. package/src/plugins/builtin/alerts/iv-level.ts +365 -0
  280. package/src/plugins/builtin/alerts/levels.ts +3 -3
  281. package/src/plugins/builtin/alerts/pane.tsx +97 -29
  282. package/src/plugins/builtin/alerts/price.ts +35 -0
  283. package/src/plugins/builtin/alerts/quotes.ts +24 -0
  284. package/src/plugins/builtin/alerts/research-builder.ts +84 -11
  285. package/src/plugins/builtin/alerts/research-rules.ts +76 -3
  286. package/src/plugins/builtin/alerts/scheduled-check.ts +206 -32
  287. package/src/plugins/builtin/alerts/scheduled-sources.ts +26 -3
  288. package/src/plugins/builtin/alerts/scheduled.ts +188 -29
  289. package/src/plugins/builtin/alerts/storage.ts +13 -10
  290. package/src/plugins/builtin/alerts/targets.ts +12 -3
  291. package/src/plugins/builtin/analytics/index.tsx +8 -2
  292. package/src/plugins/builtin/analytics/metrics.ts +1 -1
  293. package/src/plugins/builtin/analytics/risk-book.ts +215 -0
  294. package/src/plugins/builtin/analytics/risk-client.ts +13 -6
  295. package/src/plugins/builtin/analytics/risk-headless.ts +55 -21
  296. package/src/plugins/builtin/analytics/risk-math.ts +6 -3
  297. package/src/plugins/builtin/analytics/risk-model.ts +297 -104
  298. package/src/plugins/builtin/analytics/risk-options.ts +17 -2
  299. package/src/plugins/builtin/analytics/risk-pane.tsx +97 -36
  300. package/src/plugins/builtin/attention/pane.tsx +6 -2
  301. package/src/plugins/builtin/awards/headless.ts +7 -3
  302. package/src/plugins/builtin/awards/model.ts +18 -0
  303. package/src/plugins/builtin/awards/pane.tsx +6 -3
  304. package/src/plugins/builtin/backtest/headless.ts +3 -3
  305. package/src/plugins/builtin/backtest/rules.ts +41 -5
  306. package/src/plugins/builtin/broker-manager/add-flow.tsx +12 -7
  307. package/src/plugins/builtin/broker-manager/detail.tsx +47 -19
  308. package/src/plugins/builtin/broker-manager/footer.ts +27 -11
  309. package/src/plugins/builtin/broker-manager/index.tsx +21 -12
  310. package/src/plugins/builtin/broker-manager/keyboard.ts +1 -1
  311. package/src/plugins/builtin/broker-manager/model.ts +12 -2
  312. package/src/plugins/builtin/broker-manager/pane-actions.tsx +183 -57
  313. package/src/plugins/builtin/broker-manager/table.ts +28 -12
  314. package/src/plugins/builtin/broker-trading/account-picker.tsx +2 -1
  315. package/src/plugins/builtin/broker-trading/chart-levels.ts +82 -13
  316. package/src/plugins/builtin/broker-trading/controller.ts +45 -13
  317. package/src/plugins/builtin/broker-trading/dock.tsx +22 -4
  318. package/src/plugins/builtin/broker-trading/headless.ts +2 -1
  319. package/src/plugins/builtin/broker-trading/index.tsx +7 -1
  320. package/src/plugins/builtin/broker-trading/linked-orders.ts +148 -0
  321. package/src/plugins/builtin/broker-trading/order-feed.ts +75 -7
  322. package/src/plugins/builtin/broker-trading/orders.tsx +109 -31
  323. package/src/plugins/builtin/broker-trading/overview-orders.tsx +9 -3
  324. package/src/plugins/builtin/broker-trading/pane-model.ts +30 -2
  325. package/src/plugins/builtin/broker-trading/pane.tsx +110 -23
  326. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +36 -7
  327. package/src/plugins/builtin/broker-trading/ticket.tsx +112 -31
  328. package/src/plugins/builtin/buildout/cells.tsx +4 -0
  329. package/src/plugins/builtin/buildout/detail/index.tsx +5 -1
  330. package/src/plugins/builtin/buildout/detail/ui.tsx +7 -4
  331. package/src/plugins/builtin/buildout/pane/index.tsx +25 -12
  332. package/src/plugins/builtin/buildout/pane/status.tsx +0 -4
  333. package/src/plugins/builtin/builtin-plugin-meta.ts +1 -1
  334. package/src/plugins/builtin/catalysts/detail.tsx +3 -2
  335. package/src/plugins/builtin/catalysts/index.tsx +1 -1
  336. package/src/plugins/builtin/catalysts/model.ts +49 -4
  337. package/src/plugins/builtin/catalysts/pane.tsx +6 -8
  338. package/src/plugins/builtin/central-bank-rates/client.ts +5 -0
  339. package/src/plugins/builtin/central-bank-rates/headless.ts +36 -10
  340. package/src/plugins/builtin/central-bank-rates/model.ts +33 -8
  341. package/src/plugins/builtin/changelog/index.tsx +1 -0
  342. package/src/plugins/builtin/chart-composer/catalog-inventory.ts +1 -0
  343. package/src/plugins/builtin/chart-composer/chart-spec-edit.ts +2 -1
  344. package/src/plugins/builtin/chart-composer/chart-spec.ts +7 -0
  345. package/src/plugins/builtin/chart-composer/cli-options.ts +2 -5
  346. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  347. package/src/plugins/builtin/chart-composer/editor/controller.ts +2 -0
  348. package/src/plugins/builtin/chart-composer/editor.tsx +4 -1
  349. package/src/plugins/builtin/chart-composer/headless-schema.ts +28 -26
  350. package/src/plugins/builtin/chart-composer/headless.ts +253 -35
  351. package/src/plugins/builtin/chart-composer/index.tsx +12 -2
  352. package/src/plugins/builtin/chart-composer/leverage-overlay.ts +142 -0
  353. package/src/plugins/builtin/chart-composer/pane.tsx +127 -28
  354. package/src/plugins/builtin/chart-composer/series-catalog.ts +1 -0
  355. package/src/plugins/builtin/chart-composer/series-expression.ts +65 -6
  356. package/src/plugins/builtin/chart-composer/session-history.ts +15 -21
  357. package/src/plugins/builtin/chart-composer/session-range.ts +4 -2
  358. package/src/plugins/builtin/chart-composer/settings.ts +31 -19
  359. package/src/plugins/builtin/chart-composer/use-series-catalog.ts +5 -6
  360. package/src/plugins/builtin/chart-composer/viewport-labels.ts +18 -8
  361. package/src/plugins/builtin/chat/attachments/model.ts +3 -1
  362. package/src/plugins/builtin/chat/content/index.tsx +1 -1
  363. package/src/plugins/builtin/chat/pane-links/open.ts +1 -1
  364. package/src/plugins/builtin/chat/people.ts +1 -1
  365. package/src/plugins/builtin/clinical-trials/client.ts +5 -2
  366. package/src/plugins/builtin/clinical-trials/model.ts +13 -4
  367. package/src/plugins/builtin/clinical-trials/pane.tsx +32 -12
  368. package/src/plugins/builtin/clinical-trials/ticker-sponsor.ts +17 -0
  369. package/src/plugins/builtin/cloud/askg/controller.ts +1 -1
  370. package/src/plugins/builtin/cloud/askg/lock-note.ts +1 -1
  371. package/src/plugins/builtin/cloud/askg/tool-display.ts +1 -1
  372. package/src/plugins/builtin/cloud/auth-commands.ts +3 -3
  373. package/src/plugins/builtin/cloud/team/pane-sections.tsx +105 -47
  374. package/src/plugins/builtin/cloud/team/pane-ui.tsx +42 -4
  375. package/src/plugins/builtin/cloud/team/pane.tsx +204 -64
  376. package/src/plugins/builtin/cloud/thesis/store.ts +45 -22
  377. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  378. package/src/plugins/builtin/commodities-energy/index.tsx +29 -2
  379. package/src/plugins/builtin/commodities-energy/view-state.ts +2 -0
  380. package/src/plugins/builtin/commodity-balances/pane.tsx +11 -4
  381. package/src/plugins/builtin/company-attention/app-rank.tsx +7 -5
  382. package/src/plugins/builtin/company-attention/building-model.ts +222 -0
  383. package/src/plugins/builtin/company-attention/building.tsx +172 -0
  384. package/src/plugins/builtin/company-attention/client.ts +117 -1
  385. package/src/plugins/builtin/company-attention/evidence.ts +16 -3
  386. package/src/plugins/builtin/company-attention/headless.ts +101 -5
  387. package/src/plugins/builtin/company-attention/index.tsx +6 -3
  388. package/src/plugins/builtin/company-attention/model.ts +33 -7
  389. package/src/plugins/builtin/company-attention/moves-model.ts +253 -0
  390. package/src/plugins/builtin/company-attention/moves.tsx +159 -0
  391. package/src/plugins/builtin/company-attention/pane.tsx +79 -23
  392. package/src/plugins/builtin/company-attention/signals-model.ts +229 -0
  393. package/src/plugins/builtin/company-attention/signals.tsx +160 -0
  394. package/src/plugins/builtin/company-kpis/headless.ts +3 -2
  395. package/src/plugins/builtin/company-kpis/model.ts +2 -2
  396. package/src/plugins/builtin/company-kpis/pane.tsx +1 -1
  397. package/src/plugins/builtin/composite-plugins.ts +2 -1
  398. package/src/plugins/builtin/congress-trades/headless.ts +1 -1
  399. package/src/plugins/builtin/connections/index.ts +1 -0
  400. package/src/plugins/builtin/correlation/headless-schema.ts +3 -2
  401. package/src/plugins/builtin/correlation/headless.ts +5 -3
  402. package/src/plugins/builtin/cot/headless.ts +28 -6
  403. package/src/plugins/builtin/cot/model.ts +12 -0
  404. package/src/plugins/builtin/cot/pane.tsx +3 -2
  405. package/src/plugins/builtin/cpi/headless.ts +5 -1
  406. package/src/plugins/builtin/cpi/pane.tsx +11 -9
  407. package/src/plugins/builtin/credit-boards/model.ts +2 -0
  408. package/src/plugins/builtin/credit-documents/headless.ts +1 -1
  409. package/src/plugins/builtin/crypto-board/client.ts +86 -0
  410. package/src/plugins/builtin/crypto-board/defi-view.tsx +218 -0
  411. package/src/plugins/builtin/crypto-board/defi.ts +239 -0
  412. package/src/plugins/builtin/crypto-board/headless.ts +117 -6
  413. package/src/plugins/builtin/crypto-board/index.tsx +8 -3
  414. package/src/plugins/builtin/crypto-board/model.ts +40 -8
  415. package/src/plugins/builtin/crypto-board/pane.tsx +55 -21
  416. package/src/plugins/builtin/crypto-board/series.ts +19 -1
  417. package/src/plugins/builtin/crypto-etf-flows/client.ts +60 -0
  418. package/src/plugins/builtin/crypto-etf-flows/headless.ts +132 -0
  419. package/src/plugins/builtin/crypto-etf-flows/index.tsx +44 -0
  420. package/src/plugins/builtin/crypto-etf-flows/model.ts +121 -0
  421. package/src/plugins/builtin/crypto-etf-flows/pane.tsx +193 -0
  422. package/src/plugins/builtin/debug/index.tsx +1 -0
  423. package/src/plugins/builtin/dividend-yield/index.tsx +1 -1
  424. package/src/plugins/builtin/docs/index.tsx +1 -1
  425. package/src/plugins/builtin/docs/pane.tsx +2 -2
  426. package/src/plugins/builtin/docs/targets.ts +1 -1
  427. package/src/plugins/builtin/doe/client.ts +5 -1
  428. package/src/plugins/builtin/doe/headless.ts +12 -6
  429. package/src/plugins/builtin/doe/model.ts +43 -6
  430. package/src/plugins/builtin/doe/pane.tsx +18 -11
  431. package/src/plugins/builtin/earnings/board-model.ts +13 -6
  432. package/src/plugins/builtin/earnings/board-table.ts +4 -1
  433. package/src/plugins/builtin/earnings/board.tsx +253 -50
  434. package/src/plugins/builtin/earnings/calendar-model.ts +188 -0
  435. package/src/plugins/builtin/earnings/calendar.tsx +338 -0
  436. package/src/plugins/builtin/earnings/client.ts +4 -3
  437. package/src/plugins/builtin/earnings/headless.ts +53 -15
  438. package/src/plugins/builtin/earnings/history-model.ts +53 -36
  439. package/src/plugins/builtin/earnings/index.tsx +15 -22
  440. package/src/plugins/builtin/earnings/model.ts +17 -12
  441. package/src/plugins/builtin/earnings/scope.ts +89 -0
  442. package/src/plugins/builtin/earnings-calls/calendar.tsx +310 -0
  443. package/src/plugins/builtin/earnings-calls/data.ts +146 -2
  444. package/src/plugins/builtin/earnings-calls/headless.ts +151 -9
  445. package/src/plugins/builtin/earnings-calls/index.tsx +3 -3
  446. package/src/plugins/builtin/earnings-calls/lock-note.ts +1 -1
  447. package/src/plugins/builtin/earnings-calls/pane.tsx +149 -31
  448. package/src/plugins/builtin/earnings-calls/upcoming.ts +195 -0
  449. package/src/plugins/builtin/earnings-ripple/pane.tsx +10 -5
  450. package/src/plugins/builtin/econ/calendar-model.ts +132 -20
  451. package/src/plugins/builtin/econ/detail-view.tsx +48 -10
  452. package/src/plugins/builtin/econ/fred-catalog.ts +23 -6
  453. package/src/plugins/builtin/econ/fred-series-map.ts +4 -0
  454. package/src/plugins/builtin/econ/index.tsx +112 -25
  455. package/src/plugins/builtin/econ/print-watch.ts +2 -1
  456. package/src/plugins/builtin/econ/types.ts +32 -1
  457. package/src/plugins/builtin/econ-statistics/client.ts +16 -3
  458. package/src/plugins/builtin/econ-statistics/defs.ts +4 -0
  459. package/src/plugins/builtin/econ-statistics/detail.tsx +2 -2
  460. package/src/plugins/builtin/econ-statistics/headless.ts +13 -2
  461. package/src/plugins/builtin/econ-statistics/index.tsx +3 -3
  462. package/src/plugins/builtin/econ-statistics/pane.tsx +2 -1
  463. package/src/plugins/builtin/econ-statistics/stats.ts +103 -0
  464. package/src/plugins/builtin/econ-statistics/view.ts +16 -1
  465. package/src/plugins/builtin/equity-screener/client.ts +21 -3
  466. package/src/plugins/builtin/equity-screener/headless.ts +68 -6
  467. package/src/plugins/builtin/equity-screener/live.ts +1 -1
  468. package/src/plugins/builtin/equity-screener/model.ts +6 -0
  469. package/src/plugins/builtin/equity-screener/pane.tsx +45 -10
  470. package/src/plugins/builtin/equity-screener/screen-check.ts +90 -0
  471. package/src/plugins/builtin/equity-screener/ticker-check.tsx +148 -0
  472. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -1
  473. package/src/plugins/builtin/estimate-revisions/lock-note.ts +3 -3
  474. package/src/plugins/builtin/estimate-revisions/model.ts +8 -10
  475. package/src/plugins/builtin/exposure/headless.ts +51 -6
  476. package/src/plugins/builtin/exposure/index.tsx +28 -2
  477. package/src/plugins/builtin/exposure/model.ts +7 -0
  478. package/src/plugins/builtin/exposure/pane.tsx +6 -1
  479. package/src/plugins/builtin/fear-greed/index.tsx +1 -1
  480. package/src/plugins/builtin/filing-events/index.tsx +1 -1
  481. package/src/plugins/builtin/futures/model.ts +10 -9
  482. package/src/plugins/builtin/futures/returns.ts +8 -36
  483. package/src/plugins/builtin/futures/table.tsx +1 -1
  484. package/src/plugins/builtin/futures-curve/client.ts +40 -9
  485. package/src/plugins/builtin/futures-curve/headless.ts +39 -17
  486. package/src/plugins/builtin/futures-curve/model.ts +258 -69
  487. package/src/plugins/builtin/futures-curve/pane.tsx +46 -17
  488. package/src/plugins/builtin/fx-matrix/client.ts +15 -8
  489. package/src/plugins/builtin/fx-matrix/headless.ts +32 -14
  490. package/src/plugins/builtin/fx-matrix/index.tsx +13 -11
  491. package/src/plugins/builtin/fx-matrix/live-legs.ts +22 -0
  492. package/src/plugins/builtin/fx-matrix/settings.ts +2 -1
  493. package/src/plugins/builtin/gpu/headless.ts +9 -4
  494. package/src/plugins/builtin/help/index.tsx +13 -3
  495. package/src/plugins/builtin/holders/beneficial-model.ts +8 -11
  496. package/src/plugins/builtin/holders/beneficial-report.ts +6 -5
  497. package/src/plugins/builtin/holders/beneficial-view.tsx +7 -3
  498. package/src/plugins/builtin/holders/client.ts +13 -1
  499. package/src/plugins/builtin/holders/format.ts +28 -17
  500. package/src/plugins/builtin/holders/headless.ts +46 -7
  501. package/src/plugins/builtin/holders/index-holders.ts +264 -0
  502. package/src/plugins/builtin/holders/pane.tsx +79 -15
  503. package/src/plugins/builtin/holders/report-rows.ts +35 -7
  504. package/src/plugins/builtin/holders/table-model.ts +2 -1
  505. package/src/plugins/builtin/holders/treemap.tsx +2 -2
  506. package/src/plugins/builtin/insider/headless.ts +1 -1
  507. package/src/plugins/builtin/insider/signals-client.ts +1 -1
  508. package/src/plugins/builtin/insider/signals.tsx +1 -1
  509. package/src/plugins/builtin/ipo-calendar/pane.tsx +7 -4
  510. package/src/plugins/builtin/iv-history/client.ts +20 -0
  511. package/src/plugins/builtin/iv-history/headless.ts +4 -4
  512. package/src/plugins/builtin/iv-history/index.tsx +1 -1
  513. package/src/plugins/builtin/iv-history/model.ts +13 -4
  514. package/src/plugins/builtin/iv-history/series.ts +3 -1
  515. package/src/plugins/builtin/jobs/lock-note.ts +1 -1
  516. package/src/plugins/builtin/jobs/model.ts +4 -15
  517. package/src/plugins/builtin/jobs/pane.tsx +7 -5
  518. package/src/plugins/builtin/jobs/share-bars.tsx +11 -5
  519. package/src/plugins/builtin/jodi/pane.tsx +10 -4
  520. package/src/plugins/builtin/layout-manager/index.tsx +1 -0
  521. package/src/plugins/builtin/macro-day/headless.ts +20 -7
  522. package/src/plugins/builtin/macro-day/index.tsx +3 -2
  523. package/src/plugins/builtin/macro-day/model.ts +29 -8
  524. package/src/plugins/builtin/macro-day/pane.tsx +46 -10
  525. package/src/plugins/builtin/macro-day/releases.ts +55 -13
  526. package/src/plugins/builtin/market-heatmap/data.ts +9 -2
  527. package/src/plugins/builtin/market-heatmap/evidence.ts +1 -1
  528. package/src/plugins/builtin/market-heatmap/headless.ts +9 -6
  529. package/src/plugins/builtin/market-heatmap/index.tsx +3 -3
  530. package/src/plugins/builtin/market-heatmap/model.ts +10 -7
  531. package/src/plugins/builtin/market-movers/client.ts +33 -19
  532. package/src/plugins/builtin/market-movers/headless.ts +8 -2
  533. package/src/plugins/builtin/market-movers/index.tsx +16 -10
  534. package/src/plugins/builtin/market-movers/screener.ts +29 -8
  535. package/src/plugins/builtin/market-movers/session-body.tsx +4 -2
  536. package/src/plugins/builtin/market-movers/session-table.ts +4 -2
  537. package/src/plugins/builtin/market-movers/table.ts +4 -1
  538. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  539. package/src/plugins/builtin/market-valuation/view.ts +5 -0
  540. package/src/plugins/builtin/members/headless.ts +6 -5
  541. package/src/plugins/builtin/members/pane.tsx +11 -4
  542. package/src/plugins/builtin/mna/client.ts +1 -0
  543. package/src/plugins/builtin/mna/detail.tsx +20 -32
  544. package/src/plugins/builtin/mna/distress/detail.tsx +16 -4
  545. package/src/plugins/builtin/mna/distress/model.ts +3 -2
  546. package/src/plugins/builtin/mna/distress/pane.tsx +7 -4
  547. package/src/plugins/builtin/mna/headless.ts +6 -13
  548. package/src/plugins/builtin/mna/model.ts +93 -15
  549. package/src/plugins/builtin/mna/pane.tsx +5 -4
  550. package/src/plugins/builtin/news/headless.ts +12 -6
  551. package/src/plugins/builtin/news/index.tsx +10 -1
  552. package/src/plugins/builtin/news/wire/index.tsx +3 -2
  553. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +26 -10
  554. package/src/plugins/builtin/news/wire/news/query-presets.ts +6 -2
  555. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -3
  556. package/src/plugins/builtin/notes/cli.ts +49 -14
  557. package/src/plugins/builtin/openfda/pane.tsx +1 -1
  558. package/src/plugins/builtin/options/carry.ts +2 -2
  559. package/src/plugins/builtin/options/index.tsx +1 -1
  560. package/src/plugins/builtin/options/view.tsx +10 -10
  561. package/src/plugins/builtin/options-calculator/headless.ts +46 -7
  562. package/src/plugins/builtin/options-scenario/client.ts +123 -28
  563. package/src/plugins/builtin/options-scenario/collar.ts +8 -6
  564. package/src/plugins/builtin/options-scenario/csp-screen-headless.ts +127 -0
  565. package/src/plugins/builtin/options-scenario/csp-screen-pane.tsx +165 -0
  566. package/src/plugins/builtin/options-scenario/csp-screen.ts +258 -0
  567. package/src/plugins/builtin/options-scenario/csp.ts +101 -21
  568. package/src/plugins/builtin/options-scenario/headless.ts +36 -15
  569. package/src/plugins/builtin/options-scenario/hedge.ts +20 -14
  570. package/src/plugins/builtin/options-scenario/index.tsx +28 -3
  571. package/src/plugins/builtin/options-scenario/model.ts +13 -0
  572. package/src/plugins/builtin/options-scenario/pane.tsx +9 -5
  573. package/src/plugins/builtin/perps/equity-row.tsx +11 -3
  574. package/src/plugins/builtin/perps/freshness.ts +8 -5
  575. package/src/plugins/builtin/perps/headless.ts +3 -1
  576. package/src/plugins/builtin/perps/list-perpetuals.ts +25 -4
  577. package/src/plugins/builtin/perps/live-rows.ts +161 -0
  578. package/src/plugins/builtin/perps/markets-section.tsx +154 -0
  579. package/src/plugins/builtin/perps/pane.tsx +36 -17
  580. package/src/plugins/builtin/perps/venue-markets.ts +96 -0
  581. package/src/plugins/builtin/plugin-marketplace/index.tsx +2 -1
  582. package/src/plugins/builtin/plugin-marketplace/pane.tsx +143 -59
  583. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +72 -24
  584. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +158 -141
  585. package/src/plugins/builtin/portfolio-list/cli/render.ts +49 -20
  586. package/src/plugins/builtin/portfolio-list/cli/shared.ts +3 -3
  587. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +29 -24
  588. package/src/plugins/builtin/portfolio-list/headless.ts +6 -3
  589. package/src/plugins/builtin/portfolio-list/income.ts +13 -2
  590. package/src/plugins/builtin/portfolio-list/index.tsx +9 -2
  591. package/src/plugins/builtin/portfolio-list/pane/index.tsx +21 -8
  592. package/src/plugins/builtin/portfolio-list/pane/purchase-date-fx.ts +46 -0
  593. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +16 -9
  594. package/src/plugins/builtin/portfolio-list/summary/totals.ts +2 -15
  595. package/src/plugins/builtin/power/client.ts +4 -1
  596. package/src/plugins/builtin/power/evidence.ts +22 -7
  597. package/src/plugins/builtin/power/gas.tsx +5 -1
  598. package/src/plugins/builtin/power/pane.tsx +8 -1
  599. package/src/plugins/builtin/power/prices.tsx +10 -3
  600. package/src/plugins/builtin/research/analyst-headless.ts +25 -8
  601. package/src/plugins/builtin/research/analyst-model.ts +68 -3
  602. package/src/plugins/builtin/research/corporate-actions-pane.tsx +63 -14
  603. package/src/plugins/builtin/research/event-model.ts +3 -1
  604. package/src/plugins/builtin/research/event-sources.ts +12 -4
  605. package/src/plugins/builtin/research/index.tsx +2 -1
  606. package/src/plugins/builtin/research/relative-valuation-basis.ts +58 -0
  607. package/src/plugins/builtin/research/relative-valuation-columns.ts +31 -2
  608. package/src/plugins/builtin/research/relative-valuation-currency.ts +12 -5
  609. package/src/plugins/builtin/research/relative-valuation-headless.ts +84 -26
  610. package/src/plugins/builtin/research/relative-valuation-model.ts +88 -19
  611. package/src/plugins/builtin/research/relative-valuation-pane.tsx +76 -9
  612. package/src/plugins/builtin/research/relative-valuation-peers.ts +1 -1
  613. package/src/plugins/builtin/reverse-dcf/model.ts +6 -2
  614. package/src/plugins/builtin/risk-factors/index.tsx +2 -2
  615. package/src/plugins/builtin/scanner/lock-note.ts +1 -1
  616. package/src/plugins/builtin/sec/forms.ts +13 -0
  617. package/src/plugins/builtin/sec/fund-abroad.ts +32 -0
  618. package/src/plugins/builtin/sec/fund-facts.ts +68 -8
  619. package/src/plugins/builtin/sec/headless.ts +85 -8
  620. package/src/plugins/builtin/sec/index.tsx +58 -12
  621. package/src/plugins/builtin/sectors/client.ts +2 -1
  622. package/src/plugins/builtin/sectors/sector-model.ts +11 -15
  623. package/src/plugins/builtin/series-inventory.ts +2 -0
  624. package/src/plugins/builtin/shared/broker-reconnect.ts +107 -22
  625. package/src/plugins/builtin/shared/cloud-upgrade.ts +34 -6
  626. package/src/plugins/builtin/shared/earnings-calendar.ts +375 -0
  627. package/src/plugins/builtin/{earnings/estimate-basis.ts → shared/earnings-estimate-basis.ts} +1 -1
  628. package/src/plugins/builtin/shared/earnings-report.ts +3 -3
  629. package/src/plugins/builtin/shared/fx-rate-freshness.ts +23 -1
  630. package/src/plugins/builtin/shared/job-posting.ts +129 -0
  631. package/src/plugins/builtin/shared/pro-lock-note.ts +3 -3
  632. package/src/plugins/builtin/shared/report-freshness.ts +3 -0
  633. package/src/plugins/builtin/shared/research-trading.tsx +21 -0
  634. package/src/plugins/builtin/shared/series-cache.ts +2 -0
  635. package/src/plugins/builtin/shared/ticker-surface.ts +2 -1
  636. package/src/plugins/builtin/short-interest/client.ts +2 -0
  637. package/src/plugins/builtin/short-interest/headless.ts +41 -12
  638. package/src/plugins/builtin/short-interest/index.tsx +23 -1
  639. package/src/plugins/builtin/short-interest/model.ts +70 -19
  640. package/src/plugins/builtin/short-interest/pane.tsx +19 -2
  641. package/src/plugins/builtin/short-interest/publications-client.ts +82 -0
  642. package/src/plugins/builtin/short-interest/publications-headless.ts +29 -0
  643. package/src/plugins/builtin/short-interest/publications-model.ts +50 -0
  644. package/src/plugins/builtin/short-interest/publications-pane.tsx +66 -0
  645. package/src/plugins/builtin/short-interest/schedule.ts +35 -2
  646. package/src/plugins/builtin/short-interest/surface.tsx +11 -4
  647. package/src/plugins/builtin/short-interest/types.ts +9 -0
  648. package/src/plugins/builtin/short-interest/watch-client.ts +14 -8
  649. package/src/plugins/builtin/short-interest/watch-model.ts +12 -25
  650. package/src/plugins/builtin/short-interest/watch-pane.tsx +5 -1
  651. package/src/plugins/builtin/supply-chain/flow.tsx +33 -9
  652. package/src/plugins/builtin/supply-chain/model.ts +51 -3
  653. package/src/plugins/builtin/supply-chain/pane.tsx +78 -39
  654. package/src/plugins/builtin/supply-chain/row-evidence.tsx +1 -1
  655. package/src/plugins/builtin/themes/headless.ts +8 -5
  656. package/src/plugins/builtin/themes/model.ts +14 -4
  657. package/src/plugins/builtin/themes/pane.tsx +24 -5
  658. package/src/plugins/builtin/thirteenf/headless.ts +5 -5
  659. package/src/plugins/builtin/thirteenf/signals.ts +3 -1
  660. package/src/plugins/builtin/ticker-detail/adr-pair/headless.ts +123 -0
  661. package/src/plugins/builtin/ticker-detail/adr-pair/model.ts +132 -0
  662. package/src/plugins/builtin/ticker-detail/adr-pair/tab.tsx +87 -0
  663. package/src/plugins/builtin/ticker-detail/data-panes/historical-prices.tsx +40 -26
  664. package/src/plugins/builtin/ticker-detail/data-panes/history-bars.ts +8 -4
  665. package/src/plugins/builtin/ticker-detail/data-panes/returns-history.ts +67 -3
  666. package/src/plugins/builtin/ticker-detail/data-panes/returns-model.ts +112 -5
  667. package/src/plugins/builtin/ticker-detail/data-panes/returns-pane.tsx +17 -2
  668. package/src/plugins/builtin/ticker-detail/financials/aggregation.ts +21 -7
  669. package/src/plugins/builtin/ticker-detail/financials/model.ts +36 -1
  670. package/src/plugins/builtin/ticker-detail/financials/offerings.ts +56 -0
  671. package/src/plugins/builtin/ticker-detail/financials/ratios.ts +62 -13
  672. package/src/plugins/builtin/ticker-detail/financials/tab.tsx +30 -4
  673. package/src/plugins/builtin/ticker-detail/headless-schema.ts +14 -2
  674. package/src/plugins/builtin/ticker-detail/headless.ts +148 -35
  675. package/src/plugins/builtin/ticker-detail/index.tsx +37 -5
  676. package/src/plugins/builtin/ticker-detail/overview/model.ts +65 -10
  677. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +9 -1
  678. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +68 -11
  679. package/src/plugins/builtin/ticker-detail/pane.tsx +11 -0
  680. package/src/plugins/builtin/ticker-detail/quote-footer.ts +8 -3
  681. package/src/plugins/builtin/ticker-detail/research-tabs.ts +11 -0
  682. package/src/plugins/builtin/vol-surface/client.ts +5 -4
  683. package/src/plugins/builtin/vol-surface/headless.ts +12 -3
  684. package/src/plugins/builtin/vol-surface/model.ts +4 -2
  685. package/src/plugins/builtin/volatility/model.ts +5 -10
  686. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +4 -3
  687. package/src/plugins/builtin/world-venue-map/basemap.ts +3 -1
  688. package/src/plugins/builtin/world-venue-map/chokepoint-chart.ts +40 -0
  689. package/src/plugins/builtin/world-venue-map/evidence.ts +1 -1
  690. package/src/plugins/builtin/world-venue-map/geo-series.ts +90 -11
  691. package/src/plugins/builtin/world-venue-map/headless.ts +1 -1
  692. package/src/plugins/builtin/world-venue-map/index.ts +3 -26
  693. package/src/plugins/builtin/world-venue-map/layers.ts +1 -1
  694. package/src/plugins/builtin/world-venue-map/map.tsx +2 -1
  695. package/src/plugins/builtin/world-venue-map/pane.tsx +4 -2
  696. package/src/plugins/builtin/world-venue-map/venue-header.tsx +15 -4
  697. package/src/plugins/builtin/yield-curve/client.ts +25 -0
  698. package/src/plugins/builtin/yield-curve/curves.ts +7 -4
  699. package/src/plugins/builtin/yield-curve/headless.ts +38 -4
  700. package/src/plugins/builtin/yield-curve/history.ts +4 -3
  701. package/src/plugins/builtin/yield-curve/index.tsx +12 -6
  702. package/src/plugins/builtin/yield-curve/swaps-model.ts +93 -0
  703. package/src/plugins/builtin/yield-curve/swaps.tsx +99 -0
  704. package/src/plugins/bundle.ts +1 -1
  705. package/src/plugins/catalog-browser.ts +2 -1
  706. package/src/plugins/compat.ts +3 -3
  707. package/src/plugins/dependencies.ts +75 -14
  708. package/src/plugins/extracted-plugins.ts +132 -34
  709. package/src/plugins/host-link.ts +149 -34
  710. package/src/plugins/installer.ts +35 -10
  711. package/src/plugins/loader.ts +206 -38
  712. package/src/plugins/manager-node.ts +2 -2
  713. package/src/plugins/marketplace/deactivation.ts +22 -0
  714. package/src/plugins/marketplace/model.ts +2 -2
  715. package/src/plugins/marketplace/packs.ts +1 -1
  716. package/src/plugins/ownership.ts +1 -1
  717. package/src/plugins/peer-retry.ts +155 -0
  718. package/src/plugins/registry/contributions.ts +3 -1
  719. package/src/plugins/registry/index.ts +4 -3
  720. package/src/plugins/registry/plugin-state.ts +3 -0
  721. package/src/plugins/seed-notice.ts +20 -0
  722. package/src/plugins/shortcut-argument.ts +39 -0
  723. package/src/portfolio/allocation.ts +1 -1
  724. package/src/portfolio/collection-name.ts +31 -0
  725. package/src/portfolio/column-values.ts +10 -8
  726. package/src/portfolio/cost-fx.ts +296 -19
  727. package/src/portfolio/manual-lots.ts +75 -0
  728. package/src/portfolio/mutations.ts +60 -22
  729. package/src/portfolio/suffixed-listings.ts +209 -0
  730. package/src/portfolio/totals-currency.ts +24 -0
  731. package/src/public/__snapshots__/public-api.test.ts.snap +14 -0
  732. package/src/public/time-series.ts +11 -8
  733. package/src/remote/schema.ts +1 -1
  734. package/src/renderers/browser/config-host.ts +3 -0
  735. package/src/renderers/browser/main.tsx +23 -3
  736. package/src/renderers/dom/boot-frame-page.ts +187 -0
  737. package/src/renderers/dom/boot-frame.ts +237 -0
  738. package/src/renderers/dom/build-assets.ts +46 -8
  739. package/src/renderers/dom/dom-ui-host.tsx +4 -29
  740. package/src/renderers/dom/host/ascii-text.ts +16 -2
  741. package/src/renderers/dom/host/box.tsx +43 -2
  742. package/src/renderers/dom/host/chart-surface.tsx +2 -0
  743. package/src/renderers/dom/host/hls.chunk.ts +3 -0
  744. package/src/renderers/dom/host/image-surface.tsx +49 -0
  745. package/src/renderers/dom/host/media-surface.tsx +24 -18
  746. package/src/renderers/dom/host/mouse.ts +30 -0
  747. package/src/renderers/dom/host/size-observer.ts +1 -1
  748. package/src/renderers/dom/host/style.ts +1 -1
  749. package/src/renderers/dom/styles.css +76 -0
  750. package/src/renderers/dom/test-utils.tsx +2 -1
  751. package/src/renderers/electrobun/bun/application-menu/index.ts +3 -3
  752. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +3 -0
  753. package/src/renderers/electrobun/bun/desktop/backend.ts +46 -3
  754. package/src/renderers/electrobun/bun/desktop/detached-windows.ts +1 -1
  755. package/src/renderers/electrobun/bun/desktop/host-requests.ts +9 -3
  756. package/src/renderers/electrobun/bun/desktop/initialization.ts +81 -18
  757. package/src/renderers/electrobun/bun/external-plugins.ts +29 -7
  758. package/src/renderers/electrobun/bun/index.ts +11 -1
  759. package/src/renderers/electrobun/bun/window/boot-frame.ts +67 -0
  760. package/src/renderers/electrobun/bun/window/create.ts +4 -1
  761. package/src/renderers/electrobun/shared/boot-frame.ts +7 -0
  762. package/src/renderers/electrobun/shared/protocol.ts +6 -1
  763. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -1
  764. package/src/renderers/electrobun/view/config-host.ts +3 -0
  765. package/src/renderers/electrobun/view/fatal-screen.tsx +1 -1
  766. package/src/renderers/electrobun/view/main.tsx +30 -4
  767. package/src/renderers/electrobun/view/news-source.ts +5 -5
  768. package/src/renderers/electrobun/view/remote/asset-data-client.ts +1 -1
  769. package/src/renderers/electrobun/view/ui-host.tsx +1 -1
  770. package/src/renderers/opentui/browser-open.ts +88 -0
  771. package/src/renderers/opentui/host.tsx +5 -14
  772. package/src/renderers/opentui/image/loader.ts +3 -2
  773. package/src/renderers/opentui/native-smoke.ts +10 -1
  774. package/src/renderers/opentui/pane-image/index.ts +2 -1
  775. package/src/renderers/opentui/render-lib.ts +253 -0
  776. package/src/renderers/opentui/render-timing-smoke.ts +1 -1
  777. package/src/renderers/opentui/start.tsx +1 -1
  778. package/src/renderers/share/layout-view.tsx +2 -2
  779. package/src/renderers/share/main.tsx +4 -4
  780. package/src/renderers/share/view.tsx +1 -1
  781. package/src/series/resolve.ts +22 -6
  782. package/src/series/standalone.ts +30 -2
  783. package/src/series/types.ts +8 -1
  784. package/src/shares/api.ts +4 -1
  785. package/src/shares/pane.ts +1 -1
  786. package/src/sources/gloomberb-cloud/fred-series.ts +46 -1
  787. package/src/sources/gloomberb-cloud/index.ts +32 -16
  788. package/src/sources/gloomberb-cloud/news.ts +39 -0
  789. package/src/sources/gloomberb-cloud/normalizers.ts +14 -1
  790. package/src/sources/gloomberb-cloud/perpetuals.ts +49 -1
  791. package/src/sources/listing-symbols.ts +37 -0
  792. package/src/sources/provider-errors.ts +16 -2
  793. package/src/sources/provider-router/financial-routes.ts +21 -3
  794. package/src/sources/provider-router/financials.ts +15 -2
  795. package/src/sources/provider-router/history.ts +30 -8
  796. package/src/sources/provider-router/primary.ts +1 -1
  797. package/src/state/app/bootstrap.ts +23 -0
  798. package/src/state/hooks/inline-tickers.ts +30 -5
  799. package/src/sync/controller.ts +1 -1
  800. package/src/sync/core-contributors.ts +46 -14
  801. package/src/team/collections-sync.ts +2 -1
  802. package/src/team/collections.ts +178 -17
  803. package/src/telemetry/crash-reports.ts +17 -4
  804. package/src/test-support/builtin-panes.ts +1 -1
  805. package/src/theme/colors.ts +3 -30
  806. package/src/theme/ui-tokens.ts +37 -1
  807. package/src/tickers/company-name-listing.ts +210 -33
  808. package/src/tickers/index-roots.ts +47 -0
  809. package/src/tickers/instrument-label.ts +5 -1
  810. package/src/tickers/metadata.ts +10 -0
  811. package/src/tickers/perpetual-listing.ts +5 -2
  812. package/src/tickers/search/home-listing.ts +75 -0
  813. package/src/tickers/search/index.ts +76 -27
  814. package/src/tickers/search/ranking.ts +54 -4
  815. package/src/tickers/search/upsert.ts +29 -0
  816. package/src/tickers/terminal-codes.ts +416 -40
  817. package/src/time-series/bar-aggregation.ts +243 -0
  818. package/src/time-series/base-series.ts +307 -0
  819. package/src/time-series/chart-bounds.ts +349 -0
  820. package/src/time-series/chart-seed.ts +142 -0
  821. package/src/time-series/economic.ts +5 -0
  822. package/src/time-series/fundamentals.ts +18 -3
  823. package/src/time-series/history-acquisition.ts +499 -0
  824. package/src/time-series/leverage.ts +100 -0
  825. package/src/time-series/market-resolution.ts +4 -2
  826. package/src/time-series/market.ts +2 -1
  827. package/src/time-series/price-comparison.ts +175 -30
  828. package/src/time-series/range.ts +9 -1
  829. package/src/time-series/resolution.ts +174 -16
  830. package/src/time-series/resolve-context.ts +157 -0
  831. package/src/time-series/resolve.ts +126 -1577
  832. package/src/time-series/series-name.ts +38 -0
  833. package/src/time-series/series-presentation.ts +208 -0
  834. package/src/time-series/spec.ts +11 -6
  835. package/src/time-series/studies.ts +109 -12
  836. package/src/time-series/tail-reconcile.ts +121 -0
  837. package/src/time-series/types.ts +29 -2
  838. package/src/time-series/use-chart-resolution.ts +4 -5
  839. package/src/types/broker.ts +10 -0
  840. package/src/types/config.ts +14 -0
  841. package/src/types/data-provider.ts +14 -0
  842. package/src/types/exchange-rate.ts +6 -0
  843. package/src/types/financials.ts +104 -7
  844. package/src/types/headless.ts +76 -6
  845. package/src/types/instrument.ts +3 -0
  846. package/src/types/plugin.ts +65 -8
  847. package/src/types/price-history.ts +2 -0
  848. package/src/types/ticker.ts +13 -0
  849. package/src/types/trading.ts +59 -1
  850. package/src/ui/ascii-font.ts +13 -1
  851. package/src/ui/host.tsx +6 -1
  852. package/src/updater/command-names.ts +175 -0
  853. package/src/updater/index.ts +10 -5
  854. package/src/utils/calendar-date.ts +32 -15
  855. package/src/utils/cash-runway.ts +241 -0
  856. package/src/utils/central-banks.ts +42 -0
  857. package/src/utils/cli-output.ts +259 -80
  858. package/src/utils/contract-month.ts +16 -0
  859. package/src/utils/crypto-pair.ts +61 -3
  860. package/src/utils/currency-units.ts +29 -0
  861. package/src/utils/datetime-format.ts +10 -0
  862. package/src/utils/debug-log.ts +1 -1
  863. package/src/utils/decimal-comma.ts +39 -6
  864. package/src/utils/exchange-announcements.ts +71 -0
  865. package/src/utils/exchange-rate-snapshot.ts +10 -0
  866. package/src/utils/financial-statements.ts +83 -4
  867. package/src/utils/format.ts +34 -16
  868. package/src/utils/fundamentals.ts +150 -34
  869. package/src/utils/fx-market-hours.ts +10 -1
  870. package/src/utils/latest-financial-period.ts +8 -0
  871. package/src/utils/linux-libc.ts +54 -0
  872. package/src/utils/market-capitalization.ts +111 -15
  873. package/src/utils/max-drawdown.ts +64 -0
  874. package/src/utils/network-failure.ts +83 -0
  875. package/src/utils/option-expiry.ts +139 -7
  876. package/src/utils/price-earnings.ts +11 -5
  877. package/src/utils/price-history.ts +18 -8
  878. package/src/utils/price-window.ts +131 -0
  879. package/src/utils/release-date.ts +47 -0
  880. package/src/utils/sec.ts +3 -0
  881. package/src/utils/throttled-fetch.ts +11 -4
  882. package/src/utils/trailing-eps.ts +67 -0
  883. package/src/utils/typed-text.ts +21 -0
  884. package/src/utils/utc-time.ts +24 -1
  885. package/src/version.ts +6 -0
  886. package/src/cli/pane-functions/screenshot.ts +0 -2122
@@ -6,16 +6,19 @@ import { useShortcut } from "../../../react/input";
6
6
  import { afterLayout, revealInScrollBox } from "../../../components/ui/reveal-in-scroll-box";
7
7
  import { useThemeColors } from "../../../theme/theme-context";
8
8
  import type { Quote } from "../../../types/financials";
9
+ import { quoteAgeText } from "../../../market-data/quotes/freshness";
9
10
  import { formatPercentRaw } from "../../../utils/format";
10
11
  import type { BrokerAccount, BrokerOrder, BrokerOrderPreview, BrokerOrderRequest, BrokerTradingCapabilities } from "../../../types/trading";
11
12
  import { Box, ScrollBox, Text, TextAttributes, useUiCapabilities, type BoxRenderable, type ScrollBoxRenderable } from "../../../ui";
12
13
  import { useDialogState } from "../../../ui/dialog";
13
14
  import { BrokerAccountPicker, type BrokerAccountChoice } from "./account-picker";
14
- import { TicketLegs, TicketSection, TicketOrderHeadline, TicketOrderSummary, TicketReceiptRow, TicketStatus, ticketMoney as money, ticketNumber as number, ticketTifName, ticketTypeName as typeName } from "./ticket-parts";
15
+ import { TicketExits, TicketLegs, TicketSection, TicketOrderHeadline, TicketOrderSummary, TicketReceiptRow, TicketStatus, ticketImpactMoney, ticketImpactSource, ticketMoney as money, ticketNumber as number, ticketTifName, ticketTypeName as typeName } from "./ticket-parts";
15
16
  import { brokerOrderDone, orderEstimatePrice, ticketEditingPriceStep } from "./orders-data";
17
+ import { linkedFeature, trailingText, trailingUnit, type LinkedFeature } from "./linked-orders";
16
18
 
17
- export type TicketAction = "enable" | "review" | "submit" | "back" | "confirm" | "refresh" | "orders" | "new" | "cancel" | "all" | "quantity25" | "quantity50" | "priceBid" | "priceMid" | "priceAsk" | "priceLast";
18
- export type TicketField = "action" | "orderType" | "quantity" | "limitPrice" | "stopPrice" | "tif" | "outsideRth" | "typedConfirmation" | "confirmOrders";
19
+ export type TicketAction = "enable" | "review" | "submit" | "back" | "confirm" | "refresh" | "orders" | "new" | "cancel" | "all" | "quantity25" | "quantity50" | "priceBid" | "priceMid" | "priceAsk" | "priceLast" | "priceChart";
20
+ export type TicketField = "action" | "orderType" | "quantity" | "limitPrice" | "stopPrice" | "tif" | "outsideRth" | "typedConfirmation" | "confirmOrders"
21
+ | "bracket" | "takeProfitPrice" | "stopLossPrice" | "trailing" | "trailingUnit";
19
22
  export interface TicketViewModel {
20
23
  brokerName: string; symbol: string; accounts: BrokerAccount[]; accountId?: string;
21
24
  accountChoices?: readonly BrokerAccountChoice[]; accountChoiceValue?: string;
@@ -51,21 +54,26 @@ export interface BrokerTicketViewProps {
51
54
  onClose?(): void;
52
55
  /** Terminal content measurement; the DOM uses intrinsic flex layout. */
53
56
  onContentHeight?(height: number): void;
57
+ /** With a chart beside the ticket: pick a price on it for this field, or for the entry's without one. */
58
+ onPickPrice?(field?: TicketField): void;
54
59
  }
55
- /** "Limit at $759.50 · Day", "Stop $750.00 · Day": an order's terms, each named once. */
56
- function orderTerms(order: Pick<BrokerOrder, "orderType" | "limitPrice" | "stopPrice" | "tif"> & { outsideRth?: boolean }, limitWord: string, money: (value: number) => string): string {
60
+ /**
61
+ * "Limit at $759.50 · Day", "Stop $750.00 · Day", "Trailing $1.50 · Day", "Limit $790.00 · stop $740.00, one cancels
62
+ * the other · GTC": an order's terms, each named once.
63
+ */
64
+ function orderTerms(order: Pick<BrokerOrder, "orderType" | "limitPrice" | "stopPrice" | "tif" | "trailingAmount" | "trailingPercent"> & Pick<BrokerOrderRequest, "orderClass" | "stopLossPrice"> & { outsideRth?: boolean },
65
+ limitWord: string, money: (value: number) => string): string {
57
66
  const limit = order.limitPrice !== undefined ? `${limitWord} ${money(order.limitPrice)}` : "";
58
67
  const stop = order.stopPrice !== undefined ? `Stop ${money(order.stopPrice)}` : "";
59
- const price = order.orderType === "STP" && stop ? [stop] : order.orderType === "STP LMT" && stop ? [stop, ...(order.limitPrice !== undefined ? [`limit ${money(order.limitPrice)}`] : [])]
68
+ const price = order.orderType === "TRAIL" ? [trailingText(order, money)]
69
+ : order.orderClass === "oco" ? [`Limit ${money(order.limitPrice ?? NaN)}`, `stop ${money(order.stopLossPrice ?? NaN)}, one cancels the other`]
70
+ : order.orderType === "STP" && stop ? [stop] : order.orderType === "STP LMT" && stop ? [stop, ...(order.limitPrice !== undefined ? [`limit ${money(order.limitPrice)}`] : [])]
60
71
  : [`${typeName[order.orderType] ?? order.orderType}${limit ? ` ${limit}` : ""}`, ...(stop ? [stop] : [])];
61
72
  return [...price, ...(order.tif ? [ticketTifName[order.tif] ?? order.tif] : []), ...(order.outsideRth ? ["Outside RTH"] : [])].join(" · ");
62
73
  }
63
74
 
64
75
  export function ticketQuoteAge(quote: Quote | undefined, now = Date.now()): string {
65
- const at = quote?.lastTradeTime ?? quote?.lastUpdated;
66
- if (!at || at > now) return "Age unknown";
67
- const seconds = Math.max(0, Math.floor((now - at) / 1000));
68
- return seconds < 60 ? `${seconds}s old` : seconds < 3600 ? `${Math.floor(seconds / 60)}m old` : `${Math.floor(seconds / 3600)}h ${Math.floor(seconds % 3600 / 60)}m old`;
76
+ return quoteAgeText(quote?.lastTradeTime ?? quote?.lastUpdated, now);
69
77
  }
70
78
  /** The decimals a price step needs: 0.01 and 0.05 take two, 0.0001 four. Never fewer than two. */
71
79
  function stepDecimals(step: number): number {
@@ -86,6 +94,8 @@ function replacementChanges(original: BrokerOrder, draft: BrokerOrderRequest, pr
86
94
  original.quantity !== draft.quantity ? `Quantity ${number(original.quantity)} → ${number(draft.quantity)}` : "",
87
95
  original.limitPrice !== draft.limitPrice && draft.limitPrice !== undefined ? `Limit ${price(original.limitPrice)} → ${price(draft.limitPrice)}` : "",
88
96
  original.stopPrice !== draft.stopPrice && draft.stopPrice !== undefined ? `Stop ${price(original.stopPrice)} → ${price(draft.stopPrice)}` : "",
97
+ original.trailingAmount !== draft.trailingAmount && draft.trailingAmount !== undefined ? `Trail ${price(original.trailingAmount)} → ${price(draft.trailingAmount)}` : "",
98
+ original.trailingPercent !== draft.trailingPercent && draft.trailingPercent !== undefined ? `Trail ${number(original.trailingPercent)}% → ${number(draft.trailingPercent)}%` : "",
89
99
  ].filter(Boolean);
90
100
  return changes.length ? changes.join(" · ") : "No change from the working order";
91
101
  }
@@ -151,8 +161,11 @@ function DockedQuote({ quote, delayed, stale, source, updated, price, change, wi
151
161
  </Box>;
152
162
  }
153
163
 
164
+ /** The ticket prices a pick on the chart can set. */
165
+ const PRICE_FIELDS: readonly string[] = ["limitPrice", "stopPrice", "takeProfitPrice", "stopLossPrice"];
166
+
154
167
  /** One ticket presentation, with the same review and focus gates on every renderer. */
155
- export function BrokerTicketView({ model: m, width, height, focused, onEdit, onAction, onAccountChange, onCapture, docked = false, blocked = false, asking = false, onClose, onContentHeight }: BrokerTicketViewProps) {
168
+ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onAction, onAccountChange, onCapture, docked = false, blocked = false, asking = false, onClose, onContentHeight, onPickPrice }: BrokerTicketViewProps) {
156
169
  const c = useThemeColors();
157
170
  const native = useUiCapabilities().nativePaneChrome;
158
171
  const dialogOpen = useDialogState((state) => state.isOpen);
@@ -171,6 +184,7 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
171
184
  // A credit spread's preview reports what it receives as a negative cost.
172
185
  const previewCredit = (m.preview?.estimatedCost ?? 0) < 0;
173
186
  const previewCost = m.preview?.estimatedCost === undefined ? undefined : Math.abs(m.preview.estimatedCost);
187
+ const impactSource = ticketImpactSource(m.preview);
174
188
  const unit = strategy ? "spreads" : draft?.contract.secType === "OPT" ? "contracts" : "shares";
175
189
  const busy = blocked || ["previewing", "submitting", "cancelling"].includes(m.phase);
176
190
  const fast = mode === "simulation" && m.confirmOrders === false && !m.modifying;
@@ -179,6 +193,21 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
179
193
  const editing = m.phase === "editing" || m.phase === "previewing" || fastSubmitting;
180
194
  const showLimit = draft?.orderType === "LMT" || draft?.orderType === "STP LMT";
181
195
  const showStop = draft?.orderType === "STP" || draft?.orderType === "STP LMT";
196
+ // An OCO is a limit and a stop on one side; a bracket adds exits to an entry; a trailing stop follows the market.
197
+ const oco = draft?.orderClass === "oco";
198
+ const bracket = draft?.orderClass === "bracket";
199
+ const trailing = draft?.orderType === "TRAIL";
200
+ // A price can come from the chart beside the ticket; a spread's net premium is not on it.
201
+ const chartPick = !!onPickPrice && !strategy && (showLimit || showStop);
202
+ const trailUnit = draft ? trailingUnit(draft) : "amount";
203
+ const trailValue = trailUnit === "percent" ? draft?.trailingPercent : draft?.trailingAmount;
204
+ // What the broker cannot take is not offered. A draft that already holds one still shows it, to be taken off;
205
+ // review refuses it with the reason.
206
+ const offers = (feature: LinkedFeature, held: boolean) => held || linkedFeature(m.capabilities, feature, m.brokerName).available;
207
+ const showBracket = !!draft && !strategy && !m.modifying && !trailing && !oco && offers("bracket", bracket);
208
+ // One unit needs no switch: the field's suffix says which it is.
209
+ const trailUnits = m.capabilities?.trailing ?? [];
210
+ const showTrailUnit = trailUnits.length > 1 || !trailUnits.includes(trailUnit);
182
211
  const extendedHours = m.capabilities?.extendedHours && (!m.capabilities.extendedHoursTif || m.capabilities.extendedHoursTif.includes(draft?.tif ?? "DAY"));
183
212
  const inner = Math.max(28, width - (docked ? 2 : native ? 6 : 2));
184
213
  const formWidth = wide ? Math.min(74, Math.floor((inner - 3) * 0.58)) : inner;
@@ -207,8 +236,12 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
207
236
  if (m.phase === "enable") return ["enable", "back"];
208
237
  if (editing) return [
209
238
  "account", "action", "orderType", "quantity", "quantity25", "quantity50", "all",
210
- ...(showLimit ? ["limitPrice", "priceBid", "priceMid", "priceAsk", "priceLast"] : []),
211
- ...(showStop ? ["stopPrice"] : []),
239
+ ...(showLimit ? ["limitPrice", "priceBid", "priceMid", "priceAsk", "priceLast", ...(chartPick ? ["priceChart"] : [])] : []),
240
+ ...(showStop ? ["stopPrice", ...(chartPick && !showLimit ? ["priceChart"] : [])] : []),
241
+ ...(oco ? ["stopLossPrice"] : []),
242
+ ...(trailing ? ["trailing", ...(showTrailUnit ? ["trailingUnit"] : [])] : []),
243
+ ...(showBracket ? ["bracket"] : []),
244
+ ...(bracket ? ["takeProfitPrice", "stopLossPrice"] : []),
212
245
  "tif",
213
246
  ...(extendedHours ? ["outsideRth"] : []),
214
247
  ...(mode === "simulation" && !m.modifying ? ["confirmOrders"] : []),
@@ -220,7 +253,7 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
220
253
  if (finished) return ["new", "orders"];
221
254
  if (resultStatus === "UNKNOWN") return ["refresh", "orders"];
222
255
  return ["refresh", "orders", "new"];
223
- }, [m.phase, editing, review, live, showLimit, showStop, extendedHours, resultStatus, finished, m.tradingEnabled, mode, m.modifying]);
256
+ }, [m.phase, editing, review, live, showLimit, showStop, oco, trailing, showTrailUnit, showBracket, bracket, extendedHours, resultStatus, finished, m.tradingEnabled, mode, m.modifying, chartPick]);
224
257
  const previousFocus = useRef<{ phase: TicketViewModel["phase"]; revision?: number } | undefined>(undefined);
225
258
  useEffect(() => {
226
259
  if (previousFocus.current?.phase !== m.phase || previousFocus.current?.revision !== m.focusRevision) {
@@ -241,11 +274,15 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
241
274
  const typedMatches = !live || m.typedConfirmation?.trim().toUpperCase() === confirmationSymbol;
242
275
  const holdingBlocked = sell && !strategy && !m.capabilities?.shortSelling && (m.position === undefined || (draft?.quantity ?? 0) > Math.max(0, m.position));
243
276
  const validPrice = (value: number | undefined) => value !== undefined && Number.isFinite(value) && value > 0;
277
+ // A bracket needs one exit at least; each one shown is a price or left empty.
278
+ const exitOk = (value: number | undefined) => value === undefined || validPrice(value);
244
279
  const canReview = m.connected && m.tradingEnabled && !!account && !!draft && validPrice(draft.quantity) && !holdingBlocked && !busy
245
- && (!showLimit || validPrice(draft.limitPrice)) && (!showStop || validPrice(draft.stopPrice));
280
+ && (!showLimit || validPrice(draft.limitPrice)) && (!showStop || validPrice(draft.stopPrice)) && (!oco || validPrice(draft.stopLossPrice))
281
+ && (!trailing || validPrice(trailValue))
282
+ && (!bracket || exitOk(draft.takeProfitPrice) && exitOk(draft.stopLossPrice) && (validPrice(draft.takeProfitPrice) || validPrice(draft.stopLossPrice)));
246
283
  const blockingPreview = !!m.preview?.errors?.length;
247
284
  const canConfirm = review && typedMatches && !blockingPreview && m.connected && m.tradingEnabled && !busy;
248
- const textActive = ["quantity", "limitPrice", "stopPrice", "typedConfirmation"].includes(active);
285
+ const textActive = ["quantity", "limitPrice", "stopPrice", "takeProfitPrice", "stopLossPrice", "trailing", "typedConfirmation"].includes(active);
249
286
  // Leaving a field must return h/l to the research strip, including after a command.
250
287
  useEffect(() => {
251
288
  onCapture?.(focused && textActive);
@@ -263,8 +300,15 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
263
300
  if (active === "confirmOrders") { if (mode === "simulation" && !m.modifying) onEdit("confirmOrders", m.confirmOrders === false); return; }
264
301
  if (active === "tif") { if (!m.modifying) tifControl.current?.open(); return; }
265
302
  if (active === "outsideRth") { if (!m.modifying && extendedHours) onEdit("outsideRth", !draft?.outsideRth); return; }
303
+ if (active === "bracket") { onEdit("bracket", !bracket); return; }
304
+ if (active === "trailingUnit") {
305
+ const other = trailUnit === "percent" ? "amount" : "percent";
306
+ if (m.capabilities?.trailing?.includes(other)) onEdit("trailingUnit", other);
307
+ return;
308
+ }
266
309
  if (active === "review") { if (canReview) onAction("submit"); return; }
267
310
  if (active === "confirm") { if (canConfirm) onAction("confirm"); return; }
311
+ if (active === "priceChart") { onPickPrice?.(showLimit ? "limitPrice" : "stopPrice"); return; }
268
312
  if (quickActions.includes(active as TicketAction)) { if (!quickDisabled(active as TicketAction)) onAction(active as TicketAction); return; }
269
313
  if (active === "enable" && !m.connected) return;
270
314
  if (["back", "refresh", "orders", "new", "enable", "cancel"].includes(active)) onAction(active as TicketAction);
@@ -283,6 +327,8 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
283
327
  ...(showLimit || showStop ? ([["8", "Bid", "priceBid"], ["9", "Mid", "priceMid"], ["0", "Ask", "priceAsk"], ...(strategy ? [] : [["p", "Last", "priceLast"]] as const)] as const)
284
328
  .map(([key, label, action]) => actionShortcut(key, label, action, quickDisabled(action))) : []),
285
329
  ...(extendedHours ? [editShortcut("x", "Extended", "outsideRth", !draft?.outsideRth, !!m.modifying)] : []),
330
+ // The price being typed, or the entry's, from the chart beside the ticket.
331
+ ...(chartPick ? [keyAction("c", "hart price", () => onPickPrice!(PRICE_FIELDS.includes(active) ? active as TicketField : undefined))] : []),
286
332
  ] : review ? [actionShortcut("e", "dit", "back"), actionShortcut("o", "rders", "orders")]
287
333
  : m.phase === "enable" ? [actionShortcut("e", "nable", "enable", !m.connected)]
288
334
  : m.phase === "cancel-review" ? [actionShortcut("e", "dit", "back")]
@@ -305,6 +351,12 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
305
351
  if (!event.repeat) activate();
306
352
  return;
307
353
  }
354
+ // A price holds no letters: c in a price field picks that price on the chart beside the ticket.
355
+ if (chartPick && editing && !busy && event.name === "c" && !event.shift && PRICE_FIELDS.includes(active)) {
356
+ event.preventDefault(); event.stopPropagation();
357
+ if (!event.repeat) onPickPrice!(active as TicketField);
358
+ return;
359
+ }
308
360
  if (event.shift || event.targetEditable || textActive) return;
309
361
  const shortcut = shortcuts.find((item) => item.key === event.name);
310
362
  if (!shortcut) return;
@@ -348,8 +400,9 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
348
400
  };
349
401
  const quick = (key: TicketAction, label: string, disabled = false) => <Box ref={nodeRef(key)} key={key}>
350
402
  <Button label={label} displayLabel={!native && marker(key) ? `> ${label}` : undefined} variant="quiet" compact active={active === key && focused}
351
- disabled={disabled || busy} onPress={() => { setActive(key); onAction(key); }} />
403
+ disabled={disabled || busy} onPress={() => { setActive(key); if (key === "priceChart") onPickPrice?.(showLimit ? "limitPrice" : "stopPrice"); else onAction(key); }} />
352
404
  </Box>;
405
+ const numericLabels: Partial<Record<TicketField, string>> = { limitPrice: "Limit price", stopPrice: "Stop price", takeProfitPrice: "Take profit price", stopLossPrice: oco ? "Stop price" : "Stop loss price", trailing: trailUnit === "percent" ? "Trailing percent" : "Trailing amount" };
353
406
  const numeric = (key: TicketField, label: string, value: number | undefined, suffix: string, picks?: ReactNode) => {
354
407
  const inlinePicks = !!picks && formWidth >= 60;
355
408
  const quantity = key === "quantity";
@@ -357,12 +410,13 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
357
410
  const quantityStep = m.capabilities?.fractionalQuantity ? reportedStep : wholeNumberStep(reportedStep) ?? NaN;
358
411
  // Decimal precision is not a declared exchange tick. The fallback is an editing convenience only.
359
412
  const priceStep = m.capabilities?.priceStep ?? ticketEditingPriceStep(value ?? m.quote?.price, m.capabilities?.priceDecimals);
360
- const step = quantity ? quantityStep : priceStep;
413
+ // A trailing percent steps by a tenth of a point.
414
+ const step = quantity ? quantityStep : key === "trailing" && trailUnit === "percent" ? 0.1 : priceStep;
361
415
  return <Box flexDirection="column" gap={0}>
362
416
  <Box flexDirection="row" alignItems="center" gap={1}>
363
417
  <Box ref={nodeRef(key)} onMouseDown={() => { if (!busy) setActive(key); }} flexDirection="row" alignItems="center" gap={1} flexGrow={1}>
364
418
  <FieldLabel label={label} width={labelWidth} active={active === key} />
365
- <Box flexGrow={1}><TicketNumberInput accessibleLabel={key === "limitPrice" ? "Limit price" : key === "stopPrice" ? "Stop price" : label} value={value}
419
+ <Box flexGrow={1}><TicketNumberInput accessibleLabel={numericLabels[key] ?? label} value={value}
366
420
  width={Math.max(14, formWidth - (docked ? 11 : native ? 21 : 24) - (inlinePicks ? 20 : 0))} focused={focused && active === key && !busy} active={active === key} disabled={busy}
367
421
  step={step} min={quantity ? Math.max(step, m.capabilities?.minQuantity ?? step) : step} max={quantity && sell && !m.capabilities?.shortSelling ? Math.max(0, m.position ?? 0) : undefined}
368
422
  onChange={(text) => { if (!busy) onEdit(key, text); }} onSubmit={() => setActive(m.tradingEnabled ? "review" : "enable")} /></Box>
@@ -452,7 +506,7 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
452
506
  <TicketSection width={formWidth} title={m.modifying ? "Modify order" : docked ? undefined : "Order details"}>
453
507
  <Box flexDirection="column" gap={rowGap}>
454
508
  {!m.connected ? m.reconnecting ? <Notice tone="muted">{`Reconnecting to ${m.brokerName}...`}</Notice> : <Notice variant="callout">Broker disconnected. Connect in Brokers.</Notice>
455
- : !m.tradingEnabled ? <Notice variant="callout">Trading is off for this profile.</Notice> : null}
509
+ : !m.tradingEnabled ? <Notice variant="callout">{m.capabilities?.disabledReason ?? "Trading is off for this profile."}</Notice> : null}
456
510
  <Box ref={nodeRef("action")} flexDirection="row" onMouseDown={() => setActive("action")}>
457
511
  {!native ? <FieldLabel label="" width={2} active={focused && active === "action"} /> : null}
458
512
  <Box flexGrow={1}><SegmentedControl variant="flat" accessibleLabel="Order side" value={draft.action} size="large" width="100%"
@@ -461,8 +515,13 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
461
515
  </Box>
462
516
  <Box ref={nodeRef("orderType")} flexDirection="row" gap={1} alignItems="center" onMouseDown={() => { if (!busy) setActive("orderType"); }}>
463
517
  <FieldLabel label={docked ? "Type" : "Order type"} width={labelWidth} active={active === "orderType"} />
464
- <SelectButton label="Order type" showLabel={false} variant="well" width={selectWidth} value={draft.orderType}
465
- options={(m.capabilities?.orderTypes ?? ["LMT"]).map((t) => ({ value: t, label: typeName[t] ?? t }))} disabled={busy || m.modifying}
518
+ <SelectButton label="Order type" showLabel={false} variant="well" width={selectWidth} value={oco ? "OCO" : draft.orderType}
519
+ options={[...(m.capabilities?.orderTypes ?? ["LMT"]).filter((t) => t !== "TRAIL").map((t) => ({ value: t, label: typeName[t] ?? t })),
520
+ // A spread takes neither; elsewhere each is listed where the broker takes it.
521
+ ...(strategy ? [] : [
522
+ ...(offers("trailing", trailing) ? [{ value: "TRAIL", label: typeName.TRAIL! }] : []),
523
+ ...(offers("oco", oco) ? [{ value: "OCO", label: "OCO", description: "A limit and a stop: one filling cancels the other" }] : []),
524
+ ])]} disabled={busy || m.modifying}
466
525
  emphasized={focused && active === "orderType"} controlRef={orderTypeControl} onFocus={() => setActive("orderType")} onChange={(v) => onEdit("orderType", v)} />
467
526
  </Box>
468
527
  {strategy ? <TicketLegs model={m} width={formWidth} inset /> : null}
@@ -476,8 +535,23 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
476
535
  {quick("priceMid", "Mid", !(m.quote?.bid! > 0) || !(m.quote?.ask! > 0))}
477
536
  {quick("priceAsk", "Ask", !(m.quote?.ask! > 0))}
478
537
  {strategy ? null : quick("priceLast", "Last", !m.quote?.price)}
538
+ {chartPick ? quick("priceChart", "Chart") : null}
479
539
  </>) : null}
480
- {showStop ? numeric("stopPrice", docked ? "Stop" : "Stop price", draft.stopPrice, currency) : null}
540
+ {showStop ? numeric("stopPrice", docked ? "Stop" : "Stop price", draft.stopPrice, currency, chartPick && !showLimit ? quick("priceChart", "Chart") : undefined) : null}
541
+ {oco ? numeric("stopLossPrice", docked ? "Stop" : "Stop price", draft.stopLossPrice, currency) : null}
542
+ {trailing ? numeric("trailing", "Trail", trailValue, trailUnit === "percent" ? "%" : currency, showTrailUnit ? <Box ref={nodeRef("trailingUnit")} onMouseDown={() => { if (!busy) setActive("trailingUnit"); }}>
543
+ <SegmentedControl accessibleLabel="Trailing unit" value={trailUnit} focused={focused && active === "trailingUnit"} shortcutScope={scope}
544
+ options={[{ value: "amount", label: "Price", disabled: busy || !m.capabilities?.trailing?.includes("amount") },
545
+ { value: "percent", label: "%", disabled: busy || !m.capabilities?.trailing?.includes("percent") }]}
546
+ onChange={(value) => { setActive("trailingUnit"); onEdit("trailingUnit", value); }} />
547
+ </Box> : undefined) : null}
548
+ {showBracket ? <Box ref={nodeRef("bracket")} flexDirection="column" onMouseDown={() => { if (!busy) setActive("bracket"); }}>
549
+ <Checkbox label="Take profit / stop loss" checked={bracket} disabled={busy} active={focused && active === "bracket"}
550
+ onChange={(value) => { setActive("bracket"); onEdit("bracket", value); }} />
551
+ </Box> : null}
552
+ {/* Beside the chart, under "Take profit / stop loss", as the order rows tag them. */}
553
+ {bracket ? numeric("takeProfitPrice", docked ? "TP" : "Take profit", draft.takeProfitPrice, currency) : null}
554
+ {bracket ? numeric("stopLossPrice", docked ? "SL" : "Stop loss", draft.stopLossPrice, currency) : null}
481
555
  <Box flexDirection="row" flexWrap="wrap" alignItems="center" gap={docked ? rowGap : 1}>
482
556
  <Box ref={nodeRef("tif")} flexDirection="row" alignItems="center" gap={1} onMouseDown={() => setActive("tif")}>
483
557
  <FieldLabel label="TIF" width={labelWidth} active={active === "tif"} />
@@ -518,17 +592,22 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
518
592
  <TicketOrderHeadline model={m} large />
519
593
  {strategy ? <TicketLegs model={m} width={formWidth} /> : null}
520
594
  <Text fg={c.textDim}>{orderTerms({ orderType: draft?.orderType ?? "LMT", limitPrice: showLimit ? draft?.limitPrice : undefined,
521
- stopPrice: showStop ? draft?.stopPrice : undefined, tif: draft?.tif ?? "DAY", outsideRth: draft?.outsideRth }, strategy ? sell ? "net credit" : "net debit" : "at", (value) => money(value, currency))}</Text>
595
+ stopPrice: showStop ? draft?.stopPrice : undefined, tif: draft?.tif ?? "DAY", outsideRth: draft?.outsideRth, trailingAmount: draft?.trailingAmount, trailingPercent: draft?.trailingPercent,
596
+ orderClass: draft?.orderClass, stopLossPrice: draft?.stopLossPrice }, strategy ? sell ? "net credit" : "net debit" : "at", (value) => money(value, currency))}</Text>
522
597
  {m.original && draft ? <Text fg={c.warning}>{replacementChanges(m.original, draft, (value) => money(value, currency))}</Text> : null}
598
+ {bracket ? <TicketExits model={m} width={formWidth} currency={currency} /> : null}
523
599
  <Box marginTop={native && wide ? 1 : 0} flexDirection="column" gap={native && wide ? 0.4 : 0}>
524
- <TicketReceiptRow label="Fees" value={money(m.preview?.fees, currency)} width={formWidth} />
525
- <TicketReceiptRow label="Commission" value={money(m.preview?.commission, m.preview?.commissionCurrency ?? currency)} width={formWidth} />
600
+ {impactSource ? <TicketReceiptRow label="Impact source" value={impactSource} width={formWidth} /> : null}
601
+ <TicketReceiptRow label="Fees" value={ticketImpactMoney(m.preview, m.preview?.fees, currency)} width={formWidth} />
602
+ <TicketReceiptRow label="Commission" value={ticketImpactMoney(m.preview, m.preview?.commission, m.preview?.commissionCurrency ?? currency)} width={formWidth} />
526
603
  {!wide ? <TicketOrderSummary model={m} width={formWidth} compact receipt /> : null}
527
604
  </Box>
528
605
  <Box flexDirection="row" justifyContent="space-between" alignItems={native ? "baseline" : "center"} paddingTop={native && wide ? 0.7 : 0}>
529
606
  <Text fg={c.textDim}>{previewCredit ? "Estimated credit" : "Estimated cost"}</Text>
530
- <FigureText>{money(previewCost, currency)}</FigureText>
607
+ <FigureText>{ticketImpactMoney(m.preview, previewCost, currency)}</FigureText>
531
608
  </Box>
609
+ {/* The broker's terms for its figures, before the order is confirmed. */}
610
+ {m.preview?.disclosure ? <Text fg={c.textDim} wrapText>{m.preview.disclosure}</Text> : null}
532
611
  {/* Beside the chart, warnings are lines, so the place button fits without scrolling. */}
533
612
  {warnings.map((warning, index) => <Notice key={index} variant={docked ? undefined : "callout"} tone="warning">{warning}</Notice>)}
534
613
  {m.preview?.errors?.map((error, index) => <Notice key={index} variant={docked ? undefined : "callout"} tone="negative">{error}</Notice>)}
@@ -550,16 +629,18 @@ export function BrokerTicketView({ model: m, width, height, focused, onEdit, onA
550
629
  </TicketSection> : null}
551
630
  {m.phase === "result" ? <Box flexDirection={wide ? "row" : "column"} gap={3}><TicketSection width={formWidth} title={title} selectable>
552
631
  <TicketOrderHeadline model={m} large />{strategy ? <TicketLegs model={m} width={formWidth} /> : null}
553
- {m.result ? <Text fg={c.textDim}>{orderTerms(m.result, strategy ? "net" : "at", (value) => money(value, currency))}</Text> : null}
632
+ {m.result ? <Text fg={c.textDim}>{orderTerms({ ...m.result, orderClass: draft?.orderClass, stopLossPrice: draft?.stopLossPrice }, strategy ? "net" : "at", (value) => money(value, currency))}</Text> : null}
633
+ {bracket ? <TicketExits model={m} width={formWidth} currency={currency} /> : null}
554
634
  <TicketStatus model={m} width={formWidth} />
555
635
  {m.resultQuantities === "last-known" ? <Text fg={c.warning}>Last known quantities</Text> : null}
556
636
  {!["PARTIALLY_FILLED", "FILLED"].includes(resultStatus) ? <TicketReceiptRow label="Filled" value={resultQuantity(m.result?.filled)} width={formWidth} /> : null}
557
637
  <TicketReceiptRow label="Remaining" value={resultQuantity(m.result?.remaining)} width={formWidth} />
558
638
  <Text fg={c.textDim}>Estimates from order preview</Text>
559
- <TicketReceiptRow label={previewCredit ? "Preview credit" : "Preview cost"} value={money(previewCost, currency)} width={formWidth} />
560
- <TicketReceiptRow label="Fees" value={money(m.preview?.fees, currency)} width={formWidth} />
561
- <TicketReceiptRow label="Commission" value={money(m.preview?.commission, m.preview?.commissionCurrency ?? currency)} width={formWidth} />
639
+ <TicketReceiptRow label={previewCredit ? "Preview credit" : "Preview cost"} value={ticketImpactMoney(m.preview, previewCost, currency)} width={formWidth} />
640
+ <TicketReceiptRow label="Fees" value={ticketImpactMoney(m.preview, m.preview?.fees, currency)} width={formWidth} />
641
+ <TicketReceiptRow label="Commission" value={ticketImpactMoney(m.preview, m.preview?.commission, m.preview?.commissionCurrency ?? currency)} width={formWidth} />
562
642
  {m.preview?.buyingPowerAfter !== undefined ? <TicketReceiptRow label="Buying power after" value={money(m.preview.buyingPowerAfter, currency)} width={formWidth} /> : null}
643
+ {m.preview?.cashAfter !== undefined ? <TicketReceiptRow label="Cash after" value={money(m.preview.cashAfter, currency)} width={formWidth} /> : null}
563
644
  {m.error && !["UNKNOWN", "REJECTED"].includes(resultStatus) ? <Notice variant="callout" tone="negative">{m.error}</Notice> : null}
564
645
  {!finished && resultStatus !== "UNKNOWN" ? <Box flexDirection="row" alignItems="center" gap={1}>
565
646
  {action("refresh", "Refresh status", false, false, "neutral", true)}<Text fg={c.textDim}>·</Text>
@@ -19,6 +19,8 @@ import { favoriteKey } from "./table-model";
19
19
 
20
20
  interface BuildoutCellContext {
21
21
  favoriteBusyKey: string | null;
22
+ /** The row's last star did not take. */
23
+ favoriteFailed?: boolean;
22
24
  toggleFavorite: (row: BuildoutRow) => void | Promise<void>;
23
25
  }
24
26
 
@@ -60,6 +62,7 @@ export function renderBuildoutCell(
60
62
  <FavoriteCell
61
63
  starred={company.starred === true}
62
64
  busy={busy}
65
+ failed={context.favoriteFailed}
63
66
  selected={rowState.selected}
64
67
  onPress={() => { void context.toggleFavorite(row); }}
65
68
  />
@@ -120,6 +123,7 @@ export function renderBuildoutCell(
120
123
  <FavoriteCell
121
124
  starred={site.starred === true}
122
125
  busy={busy}
126
+ failed={context.favoriteFailed}
123
127
  selected={rowState.selected}
124
128
  onPress={() => { void context.toggleFavorite(row); }}
125
129
  />
@@ -1,5 +1,5 @@
1
1
  import { Box, ScrollBox } from "../../../../ui";
2
- import { EmptyState } from "../../../../components";
2
+ import { EmptyState, Notice } from "../../../../components";
3
3
  import type { BuildoutRow } from "../model/types";
4
4
  import type { InlineTickerCatalog } from "./ui";
5
5
  import { CompanyDetail } from "./company";
@@ -13,12 +13,15 @@ export {
13
13
 
14
14
  export function BuildoutDetail({
15
15
  row,
16
+ note = null,
16
17
  width,
17
18
  height,
18
19
  catalog,
19
20
  openTicker,
20
21
  }: {
21
22
  row: BuildoutRow | null;
23
+ /** What the row's last star ran into, in full. */
24
+ note?: string | null;
22
25
  width: number;
23
26
  height: number;
24
27
  catalog: InlineTickerCatalog;
@@ -45,6 +48,7 @@ export function BuildoutDetail({
45
48
  focusable={false}
46
49
  >
47
50
  <Box flexDirection="column" paddingX={1} width={bodyWidth}>
51
+ {note ? <Box paddingBottom={1}><Notice tone="negative">{note}</Notice></Box> : null}
48
52
  {row.kind === "company" ? (
49
53
  <CompanyDetail
50
54
  company={row.item}
@@ -5,7 +5,7 @@ import { ExternalLinkText } from "../../../../components/ui/external-link";
5
5
  import { ClippedText } from "../../../../components/ui/display";
6
6
  import type { InlineTickerCatalogEntry } from "../../../../state/hooks/inline-tickers";
7
7
  import { colors } from "../../../../theme/colors";
8
- import { Box, Text } from "../../../../ui";
8
+ import { Box, Text, TextAttributes } from "../../../../ui";
9
9
  import { truncateWithEllipsis } from "../../../../utils/text-wrap";
10
10
  import { sourceDetailEntries, text, textOrNull, tickerSymbol } from "../format";
11
11
  import type { BuildoutCompany, BuildoutRelatedCompany, BuildoutSource } from "../model/types";
@@ -182,18 +182,21 @@ export function MarkdownBlock({
182
182
  export function FavoriteCell({
183
183
  starred,
184
184
  busy,
185
+ failed = false,
185
186
  selected,
186
187
  onPress,
187
188
  }: {
188
189
  starred: boolean;
189
190
  busy: boolean;
191
+ /** The last star did not take: a `!` in the error tone, the reason in the row's detail. */
192
+ failed?: boolean;
190
193
  selected: boolean;
191
194
  onPress?: () => void;
192
195
  }) {
193
196
  return (
194
197
  <Button
195
- label={starred ? "Remove favorite" : "Add favorite"}
196
- displayLabel={busy ? "*" : starred ? "★" : "☆"}
198
+ label={failed ? starred ? "Unstarring failed. Remove favorite" : "Starring failed. Add favorite" : starred ? "Remove favorite" : "Add favorite"}
199
+ displayLabel={busy ? "*" : failed ? "!" : starred ? "★" : "☆"}
197
200
  width={2}
198
201
  compact
199
202
  variant="plain"
@@ -201,7 +204,7 @@ export function FavoriteCell({
201
204
  disabled={busy || !onPress}
202
205
  stopPropagation
203
206
  onPress={onPress}
204
- />
207
+ >{failed && !busy ? <Text fg={colors.negative} attributes={TextAttributes.BOLD}>!</Text> : undefined}</Button>
205
208
  );
206
209
  }
207
210
 
@@ -17,6 +17,7 @@ import type { PaneProps } from "../../../../types/plugin";
17
17
  import { useOptionalDialog, type PromptContext } from "../../../../ui/dialog";
18
18
  import { isPlainKey } from "../../../../utils/keyboard";
19
19
  import { logFailure } from "../../../../utils/log-failure";
20
+ import { useRowNotes } from "../../../../components/ui/row-notes";
20
21
  import { nextHeaderSort, type SortPreference } from "../../../../utils/sort-values";
21
22
  import { usePaneStateValue } from "../../../../state/app/context";
22
23
  import { useInlineTickerQuoteFact, useInlineTickers } from "../../../../state/hooks/inline-tickers";
@@ -63,6 +64,15 @@ import { useBuildoutDataRuntime } from "../data-runtime";
63
64
 
64
65
  const BUILDOUT_UPGRADE_URL = "https://thebuildout.ai/pricing";
65
66
 
67
+ /** A star a row's company or site did not take: said by its star cell, in full at the top of its detail. */
68
+ const favoriteNoteKey = (row: BuildoutRow) => favoriteKey(row) ?? rowKey(row);
69
+
70
+ function favoriteFailureText(row: BuildoutRow, starring: boolean, error: unknown): string {
71
+ // The service may answer with a page of its own; its first line says enough.
72
+ const reason = error instanceof Error ? error.message.trim().split("\n")[0]!.slice(0, 240) : "";
73
+ return `Could not ${starring ? "star" : "unstar"} ${rowTitle(row)}${reason ? `: ${reason}` : "."}`;
74
+ }
75
+
66
76
 
67
77
  const NO_TICKER_TEXTS: string[] = [];
68
78
 
@@ -86,6 +96,7 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
86
96
  const dialog = useOptionalDialog();
87
97
  const tableScrollRef = useRef<ScrollBoxRenderable | null>(null);
88
98
  const favoriteBusyKeysRef = useRef<Set<string>>(new Set());
99
+ const clearFavoriteNotesRef = useRef<() => void>(() => {});
89
100
  const [activeTab, setActiveTab] = usePaneStateValue<BuildoutTabId>("activeTab", "companies");
90
101
  const [selectedList, setSelectedList] = useState<BuildoutList | null>(null);
91
102
  const [selectedIndex, setSelectedIndex] = useState(0);
@@ -94,14 +105,13 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
94
105
  const [upgradeBusy, setUpgradeBusy] = useState(false);
95
106
  const [upgradeMessage, setUpgradeMessage] = useState<string | null>(null);
96
107
  const [favoriteBusyKey, setFavoriteBusyKey] = useState<string | null>(null);
97
- const [favoriteMessage, setFavoriteMessage] = useState<string | null>(null);
98
108
 
99
109
  const handleBeforeLoad = useCallback(() => {
100
110
  favoriteBusyKeysRef.current.clear();
101
111
  setDetailRow(null);
102
112
  setSelectedList(null);
103
113
  setUpgradeMessage(null);
104
- setFavoriteMessage(null);
114
+ clearFavoriteNotesRef.current();
105
115
  setFavoriteBusyKey(null);
106
116
  }, []);
107
117
  const {
@@ -136,7 +146,6 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
136
146
  useEffect(() => {
137
147
  setSelectedIndex(0);
138
148
  setDetailRow(null);
139
- setFavoriteMessage(null);
140
149
  const nextColumn: BuildoutColumnId | null = activeTab === "companies"
141
150
  ? selectedList ? "marketCap" : null
142
151
  : activeTab === "sites" ? "capture" : "time";
@@ -147,6 +156,10 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
147
156
  () => sortRows(activeRows(state, activeTab, selectedList), sort.columnId, sort.direction),
148
157
  [activeTab, selectedList, sort, state],
149
158
  );
159
+ // Until the next star on that row, or a reload.
160
+ const favoriteNotes = useRowNotes<BuildoutRow, string>(rows, { key: favoriteNoteKey });
161
+ const { setNote: setFavoriteNote, noteFor: favoriteNoteFor } = favoriteNotes;
162
+ clearFavoriteNotesRef.current = favoriteNotes.clearNotes;
150
163
  const columns = useMemo(() => columnsForTab(activeTab, selectedList, canFavorite), [activeTab, canFavorite, selectedList]);
151
164
  const selectedRow = rows[selectedIndex] ?? rows[0] ?? null;
152
165
  // The table's cells carry their own badges; this catalog only serves the open
@@ -226,7 +239,6 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
226
239
  setSelectedIndex(0);
227
240
  setDetailRow(null);
228
241
  setUpgradeMessage(null);
229
- setFavoriteMessage(null);
230
242
  setSort({ columnId: "marketCap", direction: "desc" });
231
243
  resetCompanies();
232
244
  }, [resetCompanies]);
@@ -236,7 +248,6 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
236
248
  setSelectedIndex(0);
237
249
  setDetailRow(null);
238
250
  setUpgradeMessage(null);
239
- setFavoriteMessage(null);
240
251
  setSort({ columnId: null, direction: "asc" });
241
252
  }, []);
242
253
 
@@ -265,20 +276,21 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
265
276
  const next = !previous;
266
277
  favoriteBusyKeysRef.current.add(key);
267
278
  setFavoriteBusyKey(key);
268
- setFavoriteMessage(null);
279
+ setFavoriteNote(key, null);
269
280
  updateFavorite(key, next);
270
281
 
271
282
  try {
272
283
  const response = await buildoutApi<{ starred?: boolean }>(path, favoriteToken, { method: "POST" });
273
284
  updateFavorite(key, typeof response.starred === "boolean" ? response.starred : next);
274
- } catch {
285
+ } catch (error) {
286
+ // The star goes back, and the row says it did not take.
275
287
  updateFavorite(key, previous);
276
- setFavoriteMessage("favorite failed");
288
+ setFavoriteNote(key, favoriteFailureText(row, next, error));
277
289
  } finally {
278
290
  favoriteBusyKeysRef.current.delete(key);
279
291
  setFavoriteBusyKey((current) => current === key ? null : current);
280
292
  }
281
- }, [favoriteToken, updateFavorite]);
293
+ }, [favoriteToken, setFavoriteNote, updateFavorite]);
282
294
 
283
295
  const toggleFavoriteRow = useCallback((row: BuildoutRow | null) => {
284
296
  if (!canFavorite || !row || !favoriteKey(row)) return false;
@@ -314,14 +326,13 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
314
326
 
315
327
  usePaneFooter("buildout", () => ({
316
328
  info: updateBuildoutFooterInfo(state, activeTab, selectedList, {
317
- favoriteMessage,
318
329
  upgradeMessage,
319
330
  partialList,
320
331
  onUpgrade: startUpgrade,
321
332
  quoteFreshness: detailQuoteFreshness,
322
333
  }),
323
334
  hints: footerHints,
324
- }), [activeTab, detailQuoteFreshness, favoriteMessage, footerHints, partialList, selectedList, startUpgrade, state, upgradeMessage]);
335
+ }), [activeTab, detailQuoteFreshness, footerHints, partialList, selectedList, startUpgrade, state, upgradeMessage]);
325
336
 
326
337
  const activePage = state.status === "ready" ? activeBuildoutPage(state, activeTab, selectedList) : null;
327
338
  const loadMoreActiveRows = useTableLoadMore(
@@ -367,8 +378,9 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
367
378
  rowState: { selected: boolean },
368
379
  ) => renderBuildoutCell(row, column, rowState, {
369
380
  favoriteBusyKey,
381
+ favoriteFailed: favoriteNoteFor(row) !== null,
370
382
  toggleFavorite,
371
- }), [favoriteBusyKey, toggleFavorite]);
383
+ }), [favoriteBusyKey, favoriteNoteFor, toggleFavorite]);
372
384
 
373
385
  const { strip: tabStrip, rows: tabRows } = usePaneTabs({
374
386
  tabs,
@@ -401,6 +413,7 @@ export function BuildoutPane({ focused, width, height }: PaneProps) {
401
413
  detailContent={(
402
414
  <BuildoutDetail
403
415
  row={detailRow}
416
+ note={detailRow ? favoriteNoteFor(detailRow) : null}
404
417
  width={width}
405
418
  height={height}
406
419
  catalog={tickerCatalog}
@@ -26,7 +26,6 @@ export function updateBuildoutFooterInfo(
26
26
  activeTab: BuildoutTabId,
27
27
  selectedList: BuildoutList | null,
28
28
  messages: {
29
- favoriteMessage: string | null;
30
29
  upgradeMessage: string | null;
31
30
  partialList: boolean;
32
31
  onUpgrade: () => void;
@@ -66,9 +65,6 @@ export function updateBuildoutFooterInfo(
66
65
  if (page?.error) {
67
66
  info.push({ id: "page-error", parts: [{ text: page.error, tone: "negative" }] });
68
67
  }
69
- if (messages.favoriteMessage) {
70
- info.push({ id: "favorite-error", parts: [{ text: messages.favoriteMessage, tone: "negative" }] });
71
- }
72
68
  if (messages.upgradeMessage) {
73
69
  info.push({ id: "upgrade-error", parts: [{ text: messages.upgradeMessage, tone: "negative" }] });
74
70
  }
@@ -107,7 +107,7 @@ export const cryptoPluginMeta = {
107
107
  stateId: "market-overview",
108
108
  name: "Crypto & Perps",
109
109
  version: "1.0.0",
110
- description: "Top crypto assets by market cap, and perpetual funding, open interest and premiums.",
110
+ description: "Top crypto assets by market cap, daily spot bitcoin and ether ETF flows, and perpetual funding, open interest and premiums.",
111
111
  toggleable: true,
112
112
  };
113
113
 
@@ -6,7 +6,7 @@ import { usePluginPaneState } from "../../../public/react";
6
6
  import { useThemeColors } from "../../../theme/theme-context";
7
7
  import { Box, ScrollBox, Text, useRendererHost, useUiCapabilities, type ScrollBoxRenderable } from "../../../ui";
8
8
  import { UpgradeLabel } from "../shared/locked-rows";
9
- import { catalystAgency, catalystChangeText, catalystDate, catalystDateBasis, catalystFacts, catalystLabel } from "./model";
9
+ import { CATALYST_DATE_WITHHELD, catalystAgency, catalystChangeText, catalystDate, catalystDateBasis, catalystDateWithheld, catalystFacts, catalystLabel } from "./model";
10
10
  import { humanLabel } from "../shared/research-cells";
11
11
  import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
12
12
  import { loadCatalystDetail } from "./client";
@@ -23,7 +23,8 @@ function Evidence({ event, width, height }: { event: CatalystEvent; width: numbe
23
23
  <KeyValueRow label="Status" value={humanLabel(event.status)} detail={`Revision ${event.revision}`} labelWidth={18} />
24
24
  <KeyValueRow label="Announced" value={catalystDate(event, "announced")} labelWidth={18} />
25
25
  {event.effectiveDate ? <KeyValueRow label={catalystDateBasis(event, "effective")} value={catalystDate(event, "effective")} labelWidth={18} /> : null}
26
- {event.deadlineDate ? <KeyValueRow label={catalystDateBasis(event, "deadline")} value={catalystDate(event, "deadline")} labelWidth={18} /> : null}
26
+ {event.deadlineDate ? <KeyValueRow label={catalystDateBasis(event, "deadline")} value={catalystDate(event, "deadline")} labelWidth={18} />
27
+ : catalystDateWithheld(event) ? <KeyValueRow label={catalystLabel("deadline")} value={CATALYST_DATE_WITHHELD} labelWidth={18} /> : null}
27
28
  <KeyValueRow label="Observed (UTC)" value={catalystDate(event, "observed")} labelWidth={18} />
28
29
  <KeyValueRow label="Confidence" value={`${Math.round(event.confidence * 100)}%`} labelWidth={18} />
29
30
  {facts.map((fact) => <KeyValueRow key={fact.label} label={fact.label} value={fact.value} labelWidth={18} />)}