gloomberb 0.18.0 → 0.18.2

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 (365) hide show
  1. package/package.json +1 -1
  2. package/src/api-client/commodity-balances.ts +6 -0
  3. package/src/api-client/company-kpis.ts +9 -0
  4. package/src/api-client/crypto-markets.ts +9 -0
  5. package/src/api-client/data.ts +6 -5
  6. package/src/api-client/equity-screener.ts +2 -0
  7. package/src/api-client/market-movers.ts +7 -0
  8. package/src/api-client/types.ts +14 -0
  9. package/src/app/pane-runtime/plugin-bindings.ts +3 -1
  10. package/src/app/pane-runtime/ticker-open-runtime.ts +17 -5
  11. package/src/app/runtime/plugin-bindings.ts +6 -2
  12. package/src/brokers/logos.ts +66 -0
  13. package/src/brokers/profile-form.ts +3 -0
  14. package/src/brokers/remote-broker-adapter.ts +13 -3
  15. package/src/capabilities/index.ts +1 -1
  16. package/src/capabilities/service-capabilities.ts +7 -1
  17. package/src/cli/base-converter.ts +60 -18
  18. package/src/cli/commands/fred-series.ts +15 -5
  19. package/src/cli/commands/market.ts +290 -159
  20. package/src/cli/commands/news-search.ts +35 -8
  21. package/src/cli/commands/overview.ts +31 -6
  22. package/src/cli/commands/system.ts +35 -1
  23. package/src/cli/commands/ticker.ts +105 -52
  24. package/src/cli/currency-hints.ts +27 -0
  25. package/src/cli/fx-pair.ts +41 -4
  26. package/src/cli/glossary.ts +57 -4
  27. package/src/cli/helpers.ts +36 -3
  28. package/src/cli/index.ts +4 -1
  29. package/src/cli/known-gaps.ts +37 -1
  30. package/src/cli/listing-arg.ts +37 -2
  31. package/src/cli/pane-functions/catalog.ts +3 -1
  32. package/src/cli/pane-functions/discovery.ts +15 -1
  33. package/src/cli/pane-functions/dom.ts +9 -4
  34. package/src/cli/pane-functions/freshness.ts +4 -4
  35. package/src/cli/pane-functions/function-help.ts +31 -7
  36. package/src/cli/pane-functions/headless.ts +6 -5
  37. package/src/cli/pane-functions/index.ts +3 -2
  38. package/src/cli/pane-functions/lock-note.ts +7 -4
  39. package/src/cli/pane-functions/resolver.ts +2 -1
  40. package/src/cli/quote-answer.ts +179 -0
  41. package/src/cli/series-search.ts +2 -1
  42. package/src/cli/symbol-list.ts +51 -16
  43. package/src/cli/types.ts +4 -0
  44. package/src/components/chart/composite/composite-chart.tsx +22 -7
  45. package/src/components/chart/composite/format.ts +13 -1
  46. package/src/components/chart/composite/level-labels.ts +30 -0
  47. package/src/components/chart/composite/levels.ts +17 -6
  48. package/src/components/chart/composite/panel-surface.tsx +64 -16
  49. package/src/components/chart/composite/price-axis-labels.tsx +2 -1
  50. package/src/components/chart/composite/types.ts +4 -0
  51. package/src/components/command-bar/routes/ticker-search/actions.ts +6 -0
  52. package/src/components/command-bar/workflow/collection-actions.ts +4 -0
  53. package/src/components/index.ts +2 -0
  54. package/src/components/layout/pane/content.tsx +16 -14
  55. package/src/components/layout/pane/footer/index.tsx +22 -3
  56. package/src/components/layout/pane/footer/model.ts +99 -4
  57. package/src/components/layout/pane/header-tabs.tsx +15 -1
  58. package/src/components/onboarding/onboarding-frame.tsx +9 -11
  59. package/src/components/onboarding/use-onboarding-broker-form.ts +5 -2
  60. package/src/components/onboarding/wizard-model.ts +10 -3
  61. package/src/components/onboarding/wizard-positions.ts +8 -0
  62. package/src/components/ui/button.tsx +11 -4
  63. package/src/components/ui/choice-dialog.tsx +8 -5
  64. package/src/components/ui/display.tsx +11 -6
  65. package/src/components/ui/fields.tsx +100 -20
  66. package/src/components/ui/icon.tsx +6 -1
  67. package/src/components/ui/list-row-lines.tsx +87 -0
  68. package/src/components/ui/list-view.tsx +14 -0
  69. package/src/components/ui/logo-mark.tsx +64 -0
  70. package/src/components/ui/number-step.ts +54 -0
  71. package/src/components/ui/select-button.tsx +5 -4
  72. package/src/components/ui/select-field.tsx +3 -3
  73. package/src/components/ui/status.tsx +17 -3
  74. package/src/components/ui/tabs.tsx +9 -1
  75. package/src/components/ui/toggle.tsx +17 -3
  76. package/src/core/state/app/instrument.ts +8 -3
  77. package/src/data/config/store/normalize.ts +4 -1
  78. package/src/data/plugin-cache.ts +32 -6
  79. package/src/docs/snapshot.json +57 -22
  80. package/src/i18n/es.ts +9 -3
  81. package/src/i18n/ja.ts +9 -3
  82. package/src/i18n/ko.ts +9 -3
  83. package/src/i18n/zh-cn.ts +10 -4
  84. package/src/i18n/zh-tw.ts +10 -4
  85. package/src/market-data/commodity-names.ts +80 -0
  86. package/src/market-data/coordinator/auxiliary.ts +3 -1
  87. package/src/market-data/coordinator/chart.ts +3 -1
  88. package/src/market-data/futures-board.ts +38 -1
  89. package/src/market-data/futures-contract.ts +91 -0
  90. package/src/market-data/fx-closes.ts +78 -1
  91. package/src/market-data/market/change-basis.ts +219 -0
  92. package/src/market-data/market/format.ts +28 -12
  93. package/src/market-data/market/freshness.ts +38 -0
  94. package/src/market-data/market/status.ts +55 -9
  95. package/src/market-data/market/year-to-date.ts +46 -0
  96. package/src/market-data/quotes/contributions.ts +12 -0
  97. package/src/market-data/quotes/delisting.ts +34 -0
  98. package/src/market-data/quotes/live-valuation.ts +23 -0
  99. package/src/market-data/quotes/resolution.ts +8 -2
  100. package/src/market-data/tax-equivalent.ts +10 -2
  101. package/src/notifications/app-notifier.ts +2 -2
  102. package/src/notifications/notification-log.ts +8 -6
  103. package/src/plugins/builtin/alerts/cli.ts +26 -18
  104. package/src/plugins/builtin/alerts/headless.ts +1 -1
  105. package/src/plugins/builtin/alerts/index.tsx +13 -4
  106. package/src/plugins/builtin/analytics/index.tsx +1 -1
  107. package/src/plugins/builtin/analytics/risk-client.ts +44 -11
  108. package/src/plugins/builtin/analytics/risk-fx.ts +61 -0
  109. package/src/plugins/builtin/analytics/risk-headless.ts +10 -1
  110. package/src/plugins/builtin/analytics/risk-model.ts +79 -16
  111. package/src/plugins/builtin/analytics/risk-pane.tsx +8 -2
  112. package/src/plugins/builtin/analytics/risk-test-data.ts +3 -0
  113. package/src/plugins/builtin/attention/trending.tsx +8 -6
  114. package/src/plugins/builtin/backtest/client.ts +5 -2
  115. package/src/plugins/builtin/backtest/window.ts +16 -3
  116. package/src/plugins/builtin/broker-manager/add-flow.tsx +40 -8
  117. package/src/plugins/builtin/broker-manager/detail.tsx +26 -1
  118. package/src/plugins/builtin/broker-manager/index.tsx +20 -9
  119. package/src/plugins/builtin/broker-manager/model.ts +5 -0
  120. package/src/plugins/builtin/broker-manager/pane-actions.tsx +8 -1
  121. package/src/plugins/builtin/broker-manager/table.ts +5 -1
  122. package/src/plugins/builtin/broker-trading/account-picker.tsx +91 -0
  123. package/src/plugins/builtin/broker-trading/chart-levels.ts +54 -0
  124. package/src/plugins/builtin/broker-trading/command.ts +103 -0
  125. package/src/plugins/builtin/broker-trading/controller.ts +688 -0
  126. package/src/plugins/builtin/broker-trading/dock.tsx +182 -0
  127. package/src/plugins/builtin/broker-trading/headless.ts +76 -0
  128. package/src/plugins/builtin/broker-trading/index.tsx +77 -0
  129. package/src/plugins/builtin/broker-trading/order-feed.ts +383 -0
  130. package/src/plugins/builtin/broker-trading/orders-data.ts +133 -0
  131. package/src/plugins/builtin/broker-trading/orders.tsx +437 -0
  132. package/src/plugins/builtin/broker-trading/overview-orders.tsx +121 -0
  133. package/src/plugins/builtin/broker-trading/pane-model.ts +207 -0
  134. package/src/plugins/builtin/broker-trading/pane.tsx +505 -0
  135. package/src/plugins/builtin/broker-trading/strategy.ts +153 -0
  136. package/src/plugins/builtin/broker-trading/ticket-parts.tsx +117 -0
  137. package/src/plugins/builtin/broker-trading/ticket.tsx +573 -0
  138. package/src/plugins/builtin/buildout/data-runtime.ts +1 -1
  139. package/src/plugins/builtin/buildout/format.ts +1 -0
  140. package/src/plugins/builtin/buildout/model/index.ts +16 -7
  141. package/src/plugins/builtin/buildout/pane/index.tsx +3 -2
  142. package/src/plugins/builtin/buildout/pane/status.tsx +3 -3
  143. package/src/plugins/builtin/chart-composer/cli-options.ts +19 -0
  144. package/src/plugins/builtin/chart-composer/data-catalog-pane.tsx +2 -1
  145. package/src/plugins/builtin/chart-composer/headless-schema.ts +3 -3
  146. package/src/plugins/builtin/chart-composer/headless.ts +2 -1
  147. package/src/plugins/builtin/chart-composer/option-range.ts +39 -0
  148. package/src/plugins/builtin/chart-composer/pane.tsx +31 -11
  149. package/src/plugins/builtin/chart-composer/presets.ts +2 -2
  150. package/src/plugins/builtin/chart-composer/series-catalog.ts +4 -2
  151. package/src/plugins/builtin/chart-composer/shortcuts.ts +3 -3
  152. package/src/plugins/builtin/chart-composer/viewport-labels.ts +5 -2
  153. package/src/plugins/builtin/chat/index.tsx +1 -1
  154. package/src/plugins/builtin/chat/status-widget.tsx +1 -1
  155. package/src/plugins/builtin/chat/unread-inbox-pane.tsx +6 -1
  156. package/src/plugins/builtin/chat/unread-inbox.ts +1 -1
  157. package/src/plugins/builtin/cloud/team/pane-sections.tsx +10 -3
  158. package/src/plugins/builtin/cloud/team/pane.tsx +30 -9
  159. package/src/plugins/builtin/commodity-balances/client.ts +6 -4
  160. package/src/plugins/builtin/commodity-balances/headless.ts +41 -7
  161. package/src/plugins/builtin/commodity-balances/index.tsx +19 -4
  162. package/src/plugins/builtin/commodity-balances/model.ts +22 -0
  163. package/src/plugins/builtin/commodity-balances/pane.tsx +36 -5
  164. package/src/plugins/builtin/company-kpis/client.ts +3 -2
  165. package/src/plugins/builtin/company-kpis/headless.ts +5 -7
  166. package/src/plugins/builtin/company-kpis/model.ts +85 -2
  167. package/src/plugins/builtin/company-kpis/pane.tsx +26 -12
  168. package/src/plugins/builtin/composite-plugins.ts +3 -2
  169. package/src/plugins/builtin/cot/headless.ts +1 -1
  170. package/src/plugins/builtin/cot/index.tsx +1 -1
  171. package/src/plugins/builtin/cot/model.ts +35 -12
  172. package/src/plugins/builtin/credit-boards/cdx-pane.tsx +2 -1
  173. package/src/plugins/builtin/credit-boards/sovr-pane.tsx +2 -1
  174. package/src/plugins/builtin/credit-conditions/index.tsx +2 -1
  175. package/src/plugins/builtin/crypto-board/client.ts +33 -2
  176. package/src/plugins/builtin/crypto-board/headless.ts +5 -2
  177. package/src/plugins/builtin/crypto-board/model.ts +28 -8
  178. package/src/plugins/builtin/custom-view/loader.ts +3 -2
  179. package/src/plugins/builtin/custom-view/pane.tsx +5 -4
  180. package/src/plugins/builtin/custom-view/view-spec.ts +1 -0
  181. package/src/plugins/builtin/dividend-yield/headless.ts +30 -8
  182. package/src/plugins/builtin/dividend-yield/provider-client.ts +3 -1
  183. package/src/plugins/builtin/earnings-calls/headless.ts +4 -4
  184. package/src/plugins/builtin/earnings-calls/lock-note.ts +12 -3
  185. package/src/plugins/builtin/econ/calendar-model.ts +38 -2
  186. package/src/plugins/builtin/econ/fred-catalog.ts +6 -2
  187. package/src/plugins/builtin/econ/fred-series-map.ts +12 -1
  188. package/src/plugins/builtin/econ/index.tsx +31 -4
  189. package/src/plugins/builtin/econ/print-watch.ts +279 -0
  190. package/src/plugins/builtin/equity-screener/headless.ts +20 -5
  191. package/src/plugins/builtin/equity-screener/model.ts +40 -2
  192. package/src/plugins/builtin/equity-screener/pane.tsx +24 -21
  193. package/src/plugins/builtin/estimate-revisions/headless.ts +3 -2
  194. package/src/plugins/builtin/estimate-revisions/lock-note.ts +29 -7
  195. package/src/plugins/builtin/executives/data.ts +24 -8
  196. package/src/plugins/builtin/executives/index.tsx +2 -18
  197. package/src/plugins/builtin/executives/pane.tsx +34 -10
  198. package/src/plugins/builtin/exposure/client.ts +4 -1
  199. package/src/plugins/builtin/exposure/editor.tsx +3 -3
  200. package/src/plugins/builtin/exposure/evidence.ts +4 -1
  201. package/src/plugins/builtin/exposure/holdings.ts +3 -1
  202. package/src/plugins/builtin/exposure/pane.tsx +37 -16
  203. package/src/plugins/builtin/filing-events/index.tsx +9 -3
  204. package/src/plugins/builtin/futures/table.tsx +6 -3
  205. package/src/plugins/builtin/futures-curve/pane.tsx +2 -2
  206. package/src/plugins/builtin/insider/base-values.ts +72 -0
  207. package/src/plugins/builtin/insider/headless.ts +29 -4
  208. package/src/plugins/builtin/jobs/headless.ts +2 -2
  209. package/src/plugins/builtin/jobs/lock-note.ts +14 -3
  210. package/src/plugins/builtin/market-halts/model.ts +31 -6
  211. package/src/plugins/builtin/market-halts/pane.tsx +3 -0
  212. package/src/plugins/builtin/market-movers/client.ts +2 -2
  213. package/src/plugins/builtin/market-movers/index.tsx +0 -3
  214. package/src/plugins/builtin/market-movers/session-body.tsx +27 -11
  215. package/src/plugins/builtin/market-movers/session.ts +25 -10
  216. package/src/plugins/builtin/market-valuation/pane.tsx +2 -1
  217. package/src/plugins/builtin/members/pane.tsx +8 -3
  218. package/src/plugins/builtin/news/wire/feed-config.ts +33 -13
  219. package/src/plugins/builtin/news/wire/index.tsx +7 -2
  220. package/src/plugins/builtin/news/wire/letters/client.ts +1 -0
  221. package/src/plugins/builtin/news/wire/news/command-bar-search.ts +6 -3
  222. package/src/plugins/builtin/news/wire/news/detail-view.tsx +2 -1
  223. package/src/plugins/builtin/news/wire/news/preset-pane.tsx +11 -3
  224. package/src/plugins/builtin/news/wire/news/table.tsx +2 -1
  225. package/src/plugins/builtin/news/wire/rss/cli.ts +3 -2
  226. package/src/plugins/builtin/news/wire/rss/feed-xml.ts +6 -1
  227. package/src/plugins/builtin/news/wire/rss/managed-feed.ts +1 -0
  228. package/src/plugins/builtin/news/wire/rss/source.ts +13 -4
  229. package/src/plugins/builtin/notes/cli.ts +33 -8
  230. package/src/plugins/builtin/notes/export.ts +49 -3
  231. package/src/plugins/builtin/notes/files.ts +14 -3
  232. package/src/plugins/builtin/notes/index.tsx +3 -3
  233. package/src/plugins/builtin/notes/quick-notes-pane.tsx +45 -10
  234. package/src/plugins/builtin/notes/store.ts +13 -3
  235. package/src/plugins/builtin/options/footer.ts +25 -20
  236. package/src/plugins/builtin/options/live-quotes.ts +33 -18
  237. package/src/plugins/builtin/options/view.tsx +77 -22
  238. package/src/plugins/builtin/options-calculator/pane.tsx +4 -2
  239. package/src/plugins/builtin/options-scenario/pane.tsx +41 -6
  240. package/src/plugins/builtin/portfolio-list/account-metrics.ts +3 -0
  241. package/src/plugins/builtin/portfolio-list/cli/bulk-symbols.ts +8 -7
  242. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +166 -15
  243. package/src/plugins/builtin/portfolio-list/cli/render.ts +69 -15
  244. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +2 -2
  245. package/src/plugins/builtin/portfolio-list/headless.ts +3 -1
  246. package/src/plugins/builtin/portfolio-list/pane/data.ts +23 -1
  247. package/src/plugins/builtin/portfolio-list/pane/index.tsx +44 -17
  248. package/src/plugins/builtin/portfolio-list/settings.ts +20 -2
  249. package/src/plugins/builtin/portfolio-list/summary/index.tsx +22 -1
  250. package/src/plugins/builtin/portfolio-list/summary/live-accounts.ts +42 -7
  251. package/src/plugins/builtin/portfolio-list/summary/totals.ts +24 -3
  252. package/src/plugins/builtin/primary-dealers/client.ts +137 -62
  253. package/src/plugins/builtin/primary-dealers/headless.ts +30 -5
  254. package/src/plugins/builtin/primary-dealers/index.tsx +3 -2
  255. package/src/plugins/builtin/primary-dealers/model.ts +179 -129
  256. package/src/plugins/builtin/primary-dealers/pane.tsx +135 -15
  257. package/src/plugins/builtin/research/analyst-model.ts +5 -1
  258. package/src/plugins/builtin/risk-factors/index.tsx +12 -3
  259. package/src/plugins/builtin/scanner/feed.ts +6 -1
  260. package/src/plugins/builtin/scanner/headless.ts +1 -1
  261. package/src/plugins/builtin/scanner/lock-note.ts +3 -2
  262. package/src/plugins/builtin/series-inventory.ts +1 -0
  263. package/src/plugins/builtin/shared/broker-reconnect.ts +105 -0
  264. package/src/plugins/builtin/shared/cloud-denied.ts +7 -6
  265. package/src/plugins/builtin/shared/cloud-upgrade.ts +10 -11
  266. package/src/plugins/builtin/shared/free-routes.ts +24 -0
  267. package/src/plugins/builtin/shared/headless-options.ts +11 -0
  268. package/src/plugins/builtin/shared/pro-feature-wall.tsx +9 -4
  269. package/src/plugins/builtin/shared/pro-lock-note.ts +89 -7
  270. package/src/plugins/builtin/shared/research-trading.tsx +62 -0
  271. package/src/plugins/builtin/social-mentions/headless.ts +1 -1
  272. package/src/plugins/builtin/social-mentions/lock-note.ts +1 -1
  273. package/src/plugins/builtin/themes/headless.ts +8 -2
  274. package/src/plugins/builtin/themes/model.ts +43 -10
  275. package/src/plugins/builtin/themes/pane.tsx +8 -6
  276. package/src/plugins/builtin/thirteenf/api.ts +4 -1
  277. package/src/plugins/builtin/thirteenf/data.ts +5 -1
  278. package/src/plugins/builtin/thirteenf/headless.ts +3 -1
  279. package/src/plugins/builtin/thirteenf/pane.tsx +8 -3
  280. package/src/plugins/builtin/ticker-detail/overview/model.ts +44 -10
  281. package/src/plugins/builtin/ticker-detail/overview/option-contract.ts +64 -0
  282. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +82 -21
  283. package/src/plugins/builtin/ticker-detail/pane.tsx +21 -14
  284. package/src/plugins/builtin/ticker-detail/quote-footer.ts +5 -1
  285. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +37 -8
  286. package/src/plugins/builtin/time-sales/pane.tsx +2 -2
  287. package/src/plugins/builtin/trace-bonds/client.ts +14 -3
  288. package/src/plugins/builtin/trace-bonds/headless.ts +1 -1
  289. package/src/plugins/builtin/vol-surface/pane.tsx +2 -2
  290. package/src/plugins/builtin-editorial.ts +1 -1
  291. package/src/plugins/compat.ts +3 -0
  292. package/src/plugins/external-runtime.ts +5 -0
  293. package/src/plugins/loader.ts +6 -0
  294. package/src/plugins/plugin-icon.ts +61 -0
  295. package/src/plugins/registry/host-actions.ts +2 -1
  296. package/src/plugins/registry/index.ts +9 -1
  297. package/src/plugins/runtime/context.tsx +5 -0
  298. package/src/plugins/runtime/index.tsx +3 -0
  299. package/src/portfolio/column-values.ts +13 -4
  300. package/src/portfolio/cost-fx.ts +61 -0
  301. package/src/portfolio/mutations.ts +16 -2
  302. package/src/portfolio/position-metrics.ts +45 -4
  303. package/src/portfolio/position-quantity.ts +43 -0
  304. package/src/public/__snapshots__/public-api.test.ts.snap +5 -0
  305. package/src/public/broker.ts +8 -0
  306. package/src/public/market-data.ts +5 -0
  307. package/src/renderers/dom/desktop/control-styles.ts +7 -1
  308. package/src/renderers/dom/desktop/controls.tsx +39 -7
  309. package/src/renderers/dom/desktop/icons.tsx +14 -0
  310. package/src/renderers/dom/desktop/list-view.tsx +51 -19
  311. package/src/renderers/dom/desktop/menu.tsx +10 -1
  312. package/src/renderers/dom/desktop/select-field.tsx +8 -6
  313. package/src/renderers/dom/host/style.ts +6 -1
  314. package/src/renderers/dom/host/tabs.tsx +6 -0
  315. package/src/renderers/dom/styles.css +12 -5
  316. package/src/renderers/electrobun/bun/core-capabilities.ts +12 -0
  317. package/src/renderers/electrobun/bun/external-plugins.ts +3 -0
  318. package/src/renderers/electrobun/shared/protocol.ts +2 -0
  319. package/src/renderers/electrobun/view/broker-remote-client.ts +16 -0
  320. package/src/renderers/electrobun/view/external-plugins.ts +1 -0
  321. package/src/series/validate.ts +2 -2
  322. package/src/sources/gloomberb-cloud/fred-series.ts +27 -2
  323. package/src/sources/gloomberb-cloud/index.ts +50 -17
  324. package/src/sources/gloomberb-cloud/normalizers.ts +4 -0
  325. package/src/sources/history-result.ts +3 -0
  326. package/src/sources/provider-errors.ts +3 -1
  327. package/src/sources/provider-router/history.ts +49 -6
  328. package/src/sources/provider-router/index.ts +2 -1
  329. package/src/state/app/activity.ts +13 -0
  330. package/src/sync/controller.ts +4 -0
  331. package/src/sync/core-contributors.ts +63 -11
  332. package/src/sync/local-only.ts +121 -0
  333. package/src/sync/react.ts +6 -0
  334. package/src/sync/types.ts +3 -0
  335. package/src/team/collections.ts +2 -0
  336. package/src/tickers/listing-fate.ts +27 -8
  337. package/src/tickers/metadata.ts +10 -0
  338. package/src/tickers/search/index.ts +24 -3
  339. package/src/time-series/price-comparison.ts +35 -0
  340. package/src/time-series/resolve.ts +52 -11
  341. package/src/time-series/spec.ts +1 -1
  342. package/src/time-series/types.ts +5 -0
  343. package/src/types/broker.ts +56 -1
  344. package/src/types/config.ts +6 -0
  345. package/src/types/data-provider.ts +5 -1
  346. package/src/types/financials.ts +23 -0
  347. package/src/types/headless.ts +12 -2
  348. package/src/types/instrument.ts +4 -0
  349. package/src/types/plugin.ts +24 -6
  350. package/src/types/price-history.ts +6 -1
  351. package/src/types/ticker.ts +9 -0
  352. package/src/types/trading.ts +67 -0
  353. package/src/ui/host.tsx +4 -0
  354. package/src/utils/base-currency.ts +108 -0
  355. package/src/utils/currency-names.ts +128 -0
  356. package/src/utils/currency-pair.ts +2 -1
  357. package/src/utils/currency-units.ts +41 -1
  358. package/src/utils/dom-screenshot.ts +22 -9
  359. package/src/utils/exchange-notation.generated.ts +360 -0
  360. package/src/utils/exchanges.ts +15 -322
  361. package/src/utils/format.ts +31 -1
  362. package/src/utils/instrument-identity.ts +38 -1
  363. package/src/utils/options.ts +46 -1
  364. package/src/utils/price-history.ts +4 -0
  365. package/src/utils/reported-money.ts +8 -4
@@ -3,6 +3,36 @@ import type { BrokerContractRef } from "./instrument";
3
3
  type BrokerOrderAction = "BUY" | "SELL";
4
4
  export type BrokerOrderType = "MKT" | "LMT" | "STP" | "STP LMT";
5
5
 
6
+ /** Explicit account classification for order confirmation. Missing values are treated as unknown. */
7
+ export type BrokerTradingMode = "simulation" | "live" | "unknown";
8
+
9
+ /** Synchronous ticket metadata. Adapters may vary this by contract and order type. */
10
+ export interface BrokerTradingCapabilities {
11
+ enabled: boolean;
12
+ disabledReason?: string;
13
+ orderTypes: BrokerOrderType[];
14
+ tif: string[];
15
+ extendedHours?: boolean;
16
+ /** When set, extended hours are available only for these time-in-force values. */
17
+ extendedHoursTif?: string[];
18
+ fractionalQuantity?: boolean;
19
+ /** Explicitly permits sells beyond a known long position. Omission blocks short sales. */
20
+ shortSelling?: boolean;
21
+ minQuantity?: number;
22
+ quantityStep?: number;
23
+ priceDecimals?: number;
24
+ /** Broker-reported price increment for this instrument/order. Omit if unknown. */
25
+ priceStep?: number;
26
+ modify?: boolean;
27
+ cancel?: boolean;
28
+ executionKind?: "fills" | "order-summaries";
29
+ /**
30
+ * Multi-leg option orders take up to this many legs (at least 2). Declared
31
+ * for a combo contract (`secType: "BAG"`); omit when the broker takes none.
32
+ */
33
+ maxLegs?: number;
34
+ }
35
+
6
36
  export interface BrokerCashBalance {
7
37
  currency: string;
8
38
  quantity: number;
@@ -13,6 +43,9 @@ export interface BrokerCashBalance {
13
43
  export interface BrokerAccount {
14
44
  accountId: string;
15
45
  name: string;
46
+ tradingMode?: BrokerTradingMode;
47
+ /** Broker-reported account classification, such as Cash or Margin. Omit when unavailable. */
48
+ accountType?: string;
16
49
  currency?: string;
17
50
  /** "cloud" is a connection held by the user's Gloom Cloud account. */
18
51
  source?: "gateway" | "flex" | "cloud";
@@ -64,10 +97,25 @@ export interface BrokerPortfolioPerformance {
64
97
  points: BrokerPortfolioPerformancePoint[];
65
98
  }
66
99
 
100
+ /**
101
+ * One leg of a multi-leg option order. The action is what happens to this
102
+ * contract when the order fills; the ratio is its contracts per spread.
103
+ */
104
+ export interface BrokerOrderLeg {
105
+ contract: BrokerContractRef;
106
+ action: BrokerOrderAction;
107
+ ratio: number;
108
+ }
109
+
67
110
  export interface BrokerOrderRequest {
68
111
  brokerInstanceId?: string;
69
112
  accountId?: string;
113
+ /** A combo contract (`secType: "BAG"`, the underlying's symbol) when the order has legs. */
70
114
  contract: BrokerContractRef;
115
+ /**
116
+ * BUY pays a net debit and SELL receives a net credit. With legs, the
117
+ * legs' own actions say which contracts are bought and sold.
118
+ */
71
119
  action: BrokerOrderAction;
72
120
  orderType: BrokerOrderType;
73
121
  quantity: number;
@@ -75,9 +123,23 @@ export interface BrokerOrderRequest {
75
123
  stopPrice?: number;
76
124
  tif?: string;
77
125
  outsideRth?: boolean;
126
+ /**
127
+ * 2 or more option legs filled as one order. `quantity` counts spreads and
128
+ * `limitPrice` is the positive net price of one spread.
129
+ */
130
+ legs?: BrokerOrderLeg[];
78
131
  }
79
132
 
80
133
  export interface BrokerOrderPreview {
134
+ currency?: string;
135
+ estimatedCost?: number;
136
+ fees?: number;
137
+ buyingPowerBefore?: number;
138
+ buyingPowerAfter?: number;
139
+ buyingPowerImpact?: number;
140
+ warnings?: string[];
141
+ /** Blocking broker validation errors. A ticket must not submit when these are present. */
142
+ errors?: string[];
81
143
  initMarginBefore?: number;
82
144
  initMarginAfter?: number;
83
145
  maintMarginBefore?: number;
@@ -91,6 +153,8 @@ export interface BrokerOrderPreview {
91
153
 
92
154
  export interface BrokerOrder {
93
155
  orderId: number;
156
+ /** Broker-native identity, scoped to profile and account. Numeric orderId remains the mutation handle. */
157
+ brokerOrderId?: string;
94
158
  brokerInstanceId?: string;
95
159
  accountId?: string;
96
160
  status: string;
@@ -108,12 +172,15 @@ export interface BrokerOrder {
108
172
  reviewUrl?: string;
109
173
  updatedAt: number;
110
174
  contract: BrokerContractRef;
175
+ /** A multi-leg order's legs, as on its request. */
176
+ legs?: BrokerOrderLeg[];
111
177
  }
112
178
 
113
179
  export interface BrokerExecution {
114
180
  execId: string;
115
181
  brokerInstanceId?: string;
116
182
  orderId?: number;
183
+ brokerOrderId?: string;
117
184
  accountId?: string;
118
185
  side: string;
119
186
  shares: number;
package/src/ui/host.tsx CHANGED
@@ -447,6 +447,8 @@ export interface AsciiTextProps extends BoxProps {
447
447
  interface HostTabItem {
448
448
  label: string;
449
449
  value: string;
450
+ /** Artwork before the label, such as a logo; decoration only. */
451
+ leading?: ReactNode;
450
452
  disabled?: boolean;
451
453
  reorderable?: boolean;
452
454
  onClose?: (value: string) => void;
@@ -611,6 +613,8 @@ export interface HostMenuItem {
611
613
  checked?: boolean;
612
614
  /** Tints the label, e.g. a status in its tone; the words carry the meaning. */
613
615
  color?: string;
616
+ /** Artwork before the label, such as a logo; decoration only. */
617
+ leading?: ReactNode;
614
618
  }
615
619
 
616
620
  export interface HostMenuProps {
@@ -0,0 +1,108 @@
1
+ import { currenciesNamed } from "./currency-names";
2
+ import { isCurrencyCode } from "./currency-pair";
3
+ import { isIsoCurrencyCode } from "./crypto-pair";
4
+
5
+ /**
6
+ * Signs people write for a currency, as they read them: C$ and US$ as
7
+ * Canadians and Americans write them, S$, NZ$, R$. These settle a sign the
8
+ * runtime gives another currency too (C$ is also the cordoba's). The runtime
9
+ * adds CA$, A$, HK$, €, £, ₹ and the rest, so a sign several currencies use
10
+ * ($, ¥) names all of them.
11
+ */
12
+ const EXTRA_SIGNS: ReadonlyArray<readonly [sign: string, code: string]> = [
13
+ ["US$", "USD"], ["U$S", "USD"], ["C$", "CAD"], ["CAN$", "CAD"], ["AU$", "AUD"], ["S$", "SGD"], ["SG$", "SGD"],
14
+ ["NZ$", "NZD"], ["R$", "BRL"], ["NT$", "TWD"], ["RMB", "CNY"], ["Fr", "CHF"], ["SFr", "CHF"], ["zł", "PLN"],
15
+ ["kr", "SEK"], ["kr", "NOK"], ["kr", "DKK"], ["kr", "ISK"], ["Kč", "CZK"], ["Ft", "HUF"], ["RM", "MYR"],
16
+ ["£", "GBP"], ["¥", "JPY"], ["¥", "CNY"],
17
+ ["$", "USD"], ["$", "CAD"], ["$", "AUD"], ["$", "NZD"], ["$", "HKD"], ["$", "SGD"], ["$", "MXN"],
18
+ ];
19
+
20
+ /** The most traded currencies, first among those a shared sign ($, ¥, kr) names. */
21
+ const SIGN_ORDER = ["USD", "EUR", "JPY", "GBP", "CNY", "CAD", "AUD", "CHF", "HKD", "SGD", "NZD", "MXN", "SEK", "NOK", "DKK"];
22
+
23
+ let signIndex: ReadonlyMap<string, readonly string[]> | undefined;
24
+
25
+ function signKey(sign: string): string {
26
+ return sign.normalize("NFC").replace(/\s+/g, "").toUpperCase();
27
+ }
28
+
29
+ function signs(): ReadonlyMap<string, readonly string[]> {
30
+ if (signIndex) return signIndex;
31
+ const built = new Map<string, string[]>();
32
+ const curated = new Set(EXTRA_SIGNS.map(([sign]) => signKey(sign)));
33
+ const add = (sign: string, code: string, extra = false) => {
34
+ const key = signKey(sign);
35
+ // A code is its own spelling, not a sign: CHF reads as the code.
36
+ if (!key || key === code || (!extra && curated.has(key))) return;
37
+ const codes = built.get(key) ?? [];
38
+ if (!codes.includes(code)) codes.push(code);
39
+ built.set(key, codes);
40
+ };
41
+ const codes = typeof Intl.supportedValuesOf === "function" ? Intl.supportedValuesOf("currency") : [];
42
+ for (const code of codes) {
43
+ if (!isCurrencyCode(code)) continue;
44
+ for (const currencyDisplay of ["symbol", "narrowSymbol"] as const) {
45
+ try {
46
+ const sign = new Intl.NumberFormat("en-US", { style: "currency", currency: code, currencyDisplay })
47
+ .formatToParts(0).find((part) => part.type === "currency")?.value;
48
+ if (sign) add(sign, code);
49
+ } catch {
50
+ // Intentional ignore: a code the runtime lists but cannot format has no sign to read.
51
+ }
52
+ }
53
+ }
54
+ for (const [sign, code] of EXTRA_SIGNS) add(sign, code, true);
55
+ const rank = (code: string) => {
56
+ const at = SIGN_ORDER.indexOf(code);
57
+ return at < 0 ? SIGN_ORDER.length : at;
58
+ };
59
+ for (const codesForSign of built.values()) codesForSign.sort((left, right) => rank(left) - rank(right));
60
+ signIndex = built;
61
+ return built;
62
+ }
63
+
64
+ /** The currencies a name (`Canadian dollar`, `yen`) or a sign (`CA$`, `C$`, `€`) stands for, most traded first. */
65
+ function currenciesForNameOrSign(typed: string): string[] {
66
+ const named = currenciesNamed(typed).map(({ code }) => code);
67
+ if (named.length > 0) return named;
68
+ return [...signs().get(signKey(typed.trim())) ?? []];
69
+ }
70
+
71
+ /** What a typed base currency is: its ISO 4217 code, or why it is none and the codes it may have meant. */
72
+ export type BaseCurrencyReading =
73
+ | { code: string }
74
+ | { error: string; suggestions: string[] };
75
+
76
+ /**
77
+ * A base currency as `config set baseCurrency` takes it: an ISO 4217 code in
78
+ * any case (`cad` is CAD). A currency's name or sign (`Canadian dollar`,
79
+ * `CA$`) is refused with the code it stands for, so the setting is always a
80
+ * code the conversions can price; so is a code that is no currency (XAU, XYZ).
81
+ */
82
+ export function readBaseCurrency(typed: string): BaseCurrencyReading {
83
+ const text = typed.trim();
84
+ const upper = text.toUpperCase();
85
+ if (/^[A-Z]{3}$/.test(upper) && isCurrencyCode(upper)) return { code: upper };
86
+ if (/^[A-Z]{3}$/.test(upper) && isIsoCurrencyCode(upper)) {
87
+ return { error: `${upper} is a metal, drawing-right or test code, not a currency.`, suggestions: [] };
88
+ }
89
+ const suggestions = text ? currenciesForNameOrSign(text) : [];
90
+ const shown = text ? JSON.stringify(text) : "An empty value";
91
+ if (suggestions.length === 1) return { error: `${shown} is not a currency code. Did you mean ${suggestions[0]}?`, suggestions };
92
+ if (suggestions.length > 1) {
93
+ return { error: `${shown} is not a currency code; it can be ${suggestions.slice(0, 6).join(", ")}.`, suggestions };
94
+ }
95
+ return { error: `${shown} is not an ISO 4217 currency code.`, suggestions };
96
+ }
97
+
98
+ /**
99
+ * A base currency as it was saved, as the setting reads it now: a code in
100
+ * capitals, and a name or sign an older version stored (`Canadian dollar`,
101
+ * `CA$`, `cad`) as the one code it stands for. Anything else stays as saved,
102
+ * for `doctor` and `config get` to flag, rather than turning into USD.
103
+ */
104
+ export function normalizeStoredBaseCurrency(value: string): string {
105
+ const reading = readBaseCurrency(value);
106
+ if ("code" in reading) return reading.code;
107
+ return reading.suggestions.length === 1 ? reading.suggestions[0]! : value.trim();
108
+ }
@@ -0,0 +1,128 @@
1
+ /**
2
+ * Currencies by the names people give them (`peso`, `Swiss franc`, `rand`),
3
+ * for `fx`, which takes ISO codes. Each currency lists its names, the first
4
+ * one saying whose it is. The last word of every name is also the generic
5
+ * word for it (`peso` for the Mexican peso), shared by every currency that
6
+ * uses it, so a word several currencies carry names all of them, most traded
7
+ * first.
8
+ */
9
+ const CURRENCY_NAMES: ReadonlyArray<readonly [code: string, ...names: string[]]> = [
10
+ ["MXN", "Mexican peso"],
11
+ ["ARS", "Argentine peso"],
12
+ ["COP", "Colombian peso"],
13
+ ["CLP", "Chilean peso"],
14
+ ["PHP", "Philippine peso"],
15
+ ["UYU", "Uruguayan peso"],
16
+ ["DOP", "Dominican peso"],
17
+ ["USD", "US dollar", "United States dollar", "American dollar"],
18
+ ["CAD", "Canadian dollar"],
19
+ ["AUD", "Australian dollar"],
20
+ ["NZD", "New Zealand dollar"],
21
+ ["HKD", "Hong Kong dollar"],
22
+ ["SGD", "Singapore dollar"],
23
+ ["TWD", "Taiwan dollar"],
24
+ ["EUR", "euro"],
25
+ ["GBP", "British pound", "pound sterling"],
26
+ ["EGP", "Egyptian pound"],
27
+ ["JPY", "Japanese yen", "yen"],
28
+ ["CNY", "Chinese yuan", "yuan", "renminbi", "rmb"],
29
+ ["CHF", "Swiss franc"],
30
+ ["XOF", "West African CFA franc"],
31
+ ["XAF", "Central African CFA franc"],
32
+ ["INR", "Indian rupee"],
33
+ ["PKR", "Pakistani rupee"],
34
+ ["LKR", "Sri Lankan rupee"],
35
+ ["NPR", "Nepalese rupee"],
36
+ ["IDR", "Indonesian rupiah"],
37
+ ["KRW", "South Korean won", "Korean won", "won"],
38
+ ["BRL", "Brazilian real", "Brazilian reais", "reais"],
39
+ ["ZAR", "South African rand"],
40
+ ["TRY", "Turkish lira"],
41
+ ["NOK", "Norwegian krone", "Norwegian kroner"],
42
+ ["DKK", "Danish krone", "Danish kroner"],
43
+ ["SEK", "Swedish krona", "Swedish kronor"],
44
+ ["ISK", "Icelandic krona", "Icelandic kronur"],
45
+ ["CZK", "Czech koruna", "Czech crown"],
46
+ ["PLN", "Polish zloty", "Polish zlotych"],
47
+ ["HUF", "Hungarian forint"],
48
+ ["RON", "Romanian leu", "Romanian lei"],
49
+ ["BGN", "Bulgarian lev", "Bulgarian leva"],
50
+ ["RUB", "Russian ruble", "Russian rouble"],
51
+ ["UAH", "Ukrainian hryvnia"],
52
+ ["ILS", "Israeli shekel", "new shekel"],
53
+ ["THB", "Thai baht"],
54
+ ["VND", "Vietnamese dong"],
55
+ ["MYR", "Malaysian ringgit"],
56
+ ["BDT", "Bangladeshi taka"],
57
+ ["NGN", "Nigerian naira"],
58
+ ["GHS", "Ghanaian cedi"],
59
+ ["KES", "Kenyan shilling"],
60
+ ["UGX", "Ugandan shilling"],
61
+ ["TZS", "Tanzanian shilling"],
62
+ ["AED", "UAE dirham", "Emirati dirham"],
63
+ ["MAD", "Moroccan dirham"],
64
+ ["SAR", "Saudi riyal", "Saudi rial"],
65
+ ["QAR", "Qatari riyal", "Qatari rial"],
66
+ ["OMR", "Omani rial", "Omani riyal"],
67
+ ["IRR", "Iranian rial", "Iranian riyal"],
68
+ ["KWD", "Kuwaiti dinar"],
69
+ ["BHD", "Bahraini dinar"],
70
+ ["JOD", "Jordanian dinar"],
71
+ ["IQD", "Iraqi dinar"],
72
+ ["RSD", "Serbian dinar"],
73
+ ["DZD", "Algerian dinar"],
74
+ ["TND", "Tunisian dinar"],
75
+ ["LYD", "Libyan dinar"],
76
+ ["KZT", "Kazakhstani tenge"],
77
+ ["PEN", "Peruvian sol"],
78
+ ];
79
+
80
+ /** One currency a name stands for. */
81
+ export interface NamedCurrency {
82
+ code: string;
83
+ /** What tells it from the others that share the word: "Mexican" for peso. Empty when the name is the whole name. */
84
+ qualifier: string;
85
+ }
86
+
87
+ /** The text of a name as it is looked up: lower case, without accents, punctuation or "the". */
88
+ function normalize(text: string): string {
89
+ return text.normalize("NFD").replace(/\p{M}/gu, "").toLowerCase()
90
+ .replace(/[^a-z0-9]+/g, " ").trim()
91
+ .replace(/^the /, "");
92
+ }
93
+
94
+ let index: ReadonlyMap<string, readonly NamedCurrency[]> | undefined;
95
+
96
+ function nameIndex(): ReadonlyMap<string, readonly NamedCurrency[]> {
97
+ if (index) return index;
98
+ const built = new Map<string, NamedCurrency[]>();
99
+ const add = (key: string, entry: NamedCurrency) => {
100
+ const found = built.get(key);
101
+ if (!found) built.set(key, [entry]);
102
+ else if (!found.some((other) => other.code === entry.code)) found.push(entry);
103
+ };
104
+ for (const [code, ...names] of CURRENCY_NAMES) {
105
+ for (const name of names) {
106
+ const words = normalize(name).split(" ");
107
+ const generic = words.at(-1)!;
108
+ // "Mexican" for "Mexican peso"; the whole name when it is one word, which says no more than the word.
109
+ const qualifier = words.length > 1 ? name.slice(0, name.length - generic.length).trim() : "";
110
+ add(normalize(name), { code, qualifier: "" });
111
+ add(generic, { code, qualifier });
112
+ }
113
+ }
114
+ index = built;
115
+ return built;
116
+ }
117
+
118
+ /**
119
+ * The currencies a name stands for: `Swiss franc` is CHF alone, `peso` is
120
+ * MXN, ARS, COP, CLP, PHP and more. A plural is its singular (`pesos`). Empty
121
+ * for text that names no currency.
122
+ */
123
+ export function currenciesNamed(text: string): readonly NamedCurrency[] {
124
+ const key = normalize(text);
125
+ const names = nameIndex();
126
+ if (!key) return [];
127
+ return names.get(key) ?? (key.endsWith("s") ? names.get(key.slice(0, -1)) : undefined) ?? [];
128
+ }
@@ -26,7 +26,8 @@ const NON_CURRENCY_CODES = new Set([
26
26
  "XAU", "XAG", "XPT", "XPD", "XDR", "XSU", "XUA", "XBA", "XBB", "XBC", "XBD", "XTS", "XXX", "XEU", "XFO", "XFU", "XRE",
27
27
  ]);
28
28
 
29
- function isCurrencyCode(code: string): boolean {
29
+ /** Whether `code` is a currency code a pair can be written with: ISO 4217, less metals, drawing rights and testing codes. */
30
+ export function isCurrencyCode(code: string): boolean {
30
31
  return !NON_CURRENCY_CODES.has(code) && isIsoCurrencyCode(code);
31
32
  }
32
33
 
@@ -15,10 +15,13 @@ const SUB_UNIT_NAMES: Record<string, string> = {
15
15
 
16
16
  /**
17
17
  * A currency code as a reader should see it; a sub-unit says which one,
18
- * "GBp (pence)", and with `ofMajor` what it divides: "ILA (agorot, 1/100 ILS)".
18
+ * "GBp (pence)", "US¢ (US cents)", and with `ofMajor` what it divides:
19
+ * "ILA (agorot, 1/100 ILS)".
19
20
  */
20
21
  export function currencyUnitLabel(code?: string | null, { ofMajor = false }: { ofMajor?: boolean } = {}): string {
21
22
  const raw = (code ?? "").trim();
23
+ // US cents have no standard code; the one quotes carry (USX) is shown as US¢.
24
+ if (raw.toUpperCase() === US_CENTS) return ofMajor ? "US¢ (US cents, 1/100 USD)" : "US¢ (US cents)";
22
25
  const unit = SUB_UNIT_NAMES[raw];
23
26
  const major = SUB_UNIT_CURRENCIES[raw];
24
27
  if (!unit) return raw;
@@ -50,6 +53,34 @@ const SUB_UNIT_EXCHANGE_RULES = [
50
53
  */
51
54
  export const US_CENTS = "USX";
52
55
 
56
+ /**
57
+ * Minor units with a sign of their own, by upper-cased code; GBp is read in
58
+ * its own case, as GBP is pounds. Agorot (ILA) have none in common use and
59
+ * keep their code.
60
+ */
61
+ const MINOR_UNIT_SIGNS: Readonly<Record<string, string>> = { USX: "¢", GBX: "p", ZAC: "c" };
62
+
63
+ /**
64
+ * The sign a price in a minor unit is written with, after the amount, where
65
+ * a major currency's symbol goes before it: US cents 314.95¢ (grains and
66
+ * softs), pence 412.50p, South African cents 1,250.00c. Null for a major
67
+ * currency, or a minor unit without one.
68
+ */
69
+ export function minorUnitSign(code: string | null | undefined): string | null {
70
+ const raw = (code ?? "").trim();
71
+ if (raw === "GBp") return "p";
72
+ return MINOR_UNIT_SIGNS[raw.toUpperCase()] ?? null;
73
+ }
74
+
75
+ /**
76
+ * A quote currency's code as a column of codes shows it: US cents, a code no
77
+ * standard has, as US¢; every other code as it is (USD, GBp).
78
+ */
79
+ export function currencyCodeLabel(code: string | null | undefined): string {
80
+ const raw = (code ?? "").trim();
81
+ return raw.toUpperCase() === US_CENTS ? "US¢" : raw;
82
+ }
83
+
53
84
  /**
54
85
  * A currency's rate to USD where it is fixed rather than quoted: the dollar
55
86
  * itself, and US cents. Undefined for any other, whose rate the market sets.
@@ -64,6 +95,15 @@ export interface CurrencyUnitInfo {
64
95
  divisor: number;
65
96
  }
66
97
 
98
+ /**
99
+ * The currency money held in a code is counted in, and how many of the code
100
+ * make one of it: resolveCurrencyUnit's, and US cents as a hundredth of a
101
+ * dollar, which a lot's cost in USX is.
102
+ */
103
+ export function heldCurrencyUnit(currency?: string | null): CurrencyUnitInfo {
104
+ return currency?.trim().toUpperCase() === US_CENTS ? { currency: "USD", divisor: 100 } : resolveCurrencyUnit(currency);
105
+ }
106
+
67
107
  interface ExchangeSubUnitOptions {
68
108
  allowDefaultCurrency?: boolean;
69
109
  allowMissingCurrency?: boolean;
@@ -469,11 +469,16 @@ function drawCanvasElement(
469
469
  }
470
470
  }
471
471
 
472
+ /** Inputs whose value is not text on screen. A button's value is its label, so it is painted. */
473
+ const NON_TEXT_INPUTS = new Set(["checkbox", "radio", "range", "color", "file", "hidden", "image"]);
474
+
472
475
  function drawInputElement(
473
476
  context: CanvasRenderingContext2D,
474
477
  element: HTMLInputElement | HTMLTextAreaElement,
475
478
  origin: CaptureOrigin,
476
479
  ): void {
480
+ // A checkbox's value ("on" unless set) is not on screen; its box is painted from its CSS.
481
+ if (element instanceof HTMLInputElement && NON_TEXT_INPUTS.has(element.type)) return;
477
482
  // Paint what the screen shows: a password field is dots, never its value.
478
483
  const typed = element instanceof HTMLInputElement && element.type === "password"
479
484
  ? "\u2022".repeat(element.value.length)
@@ -593,6 +598,12 @@ function drawElement(pass: PaintPass, element: Element): void {
593
598
 
594
599
  const style = getComputedStyle(element);
595
600
  if (!isVisible(style)) return;
601
+ // A `display: contents` element (a ColumnGrid row) has no box of its own: its children are laid
602
+ // out in its parent's, so its empty rect must not leave them out.
603
+ if (style.display === "contents") {
604
+ drawChildren(pass, element);
605
+ return;
606
+ }
596
607
 
597
608
  const rect = localRect(element.getBoundingClientRect(), origin);
598
609
  if (rect.width <= 0 || rect.height <= 0) return;
@@ -624,19 +635,21 @@ function drawElement(pass: PaintPass, element: Element): void {
624
635
  }
625
636
 
626
637
  // An SVG's shapes and text are in its raster; they are not boxes to walk.
627
- if (!(element instanceof SVGElement)) {
628
- for (const child of paintOrder(element)) {
629
- if (child.nodeType === Node.TEXT_NODE) {
630
- drawTextNode(context, child as Text, origin);
631
- } else {
632
- drawElement(pass, child as Element);
633
- }
634
- }
635
- }
638
+ if (!(element instanceof SVGElement)) drawChildren(pass, element);
636
639
 
637
640
  context.restore();
638
641
  }
639
642
 
643
+ function drawChildren(pass: PaintPass, element: Element): void {
644
+ for (const child of paintOrder(element)) {
645
+ if (child.nodeType === Node.TEXT_NODE) {
646
+ drawTextNode(pass.context, child as Text, pass.origin);
647
+ } else {
648
+ drawElement(pass, child as Element);
649
+ }
650
+ }
651
+ }
652
+
640
653
  /** Where a child falls in its parent's paint: below the flow, in it, or above it at its z-index. */
641
654
  function paintLayer(child: Element): number {
642
655
  const style = getComputedStyle(child);