gloomberb 0.16.0 → 0.16.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 (691) hide show
  1. package/README.md +2 -1
  2. package/package.json +2 -1
  3. package/src/api-client/beneficial-owners.ts +92 -0
  4. package/src/api-client/data.ts +44 -11
  5. package/src/api-client/errors.ts +12 -0
  6. package/src/api-client/geo.ts +363 -0
  7. package/src/api-client/index.ts +5 -0
  8. package/src/api-client/market-discovery.ts +10 -0
  9. package/src/api-client/members.ts +26 -0
  10. package/src/api-client/paths.ts +30 -1
  11. package/src/api-client/perps.ts +41 -0
  12. package/src/api-client/request.ts +5 -2
  13. package/src/api-client/types.ts +9 -1
  14. package/src/app/global-shortcuts.ts +14 -6
  15. package/src/app/keybindings/actions.ts +1 -1
  16. package/src/app/runtime/update.ts +38 -2
  17. package/src/app.tsx +10 -6
  18. package/src/brokers/account-cache.ts +24 -0
  19. package/src/cli/commands/command-utils.ts +8 -0
  20. package/src/cli/commands/crypto-hints.ts +75 -0
  21. package/src/cli/commands/market.ts +414 -106
  22. package/src/cli/commands/overview.ts +77 -32
  23. package/src/cli/commands/ticker.ts +101 -43
  24. package/src/cli/desktop-pane-shot.ts +34 -4
  25. package/src/cli/freshness.ts +61 -0
  26. package/src/cli/help.ts +1 -0
  27. package/src/cli/helpers.ts +4 -8
  28. package/src/cli/index.ts +12 -2
  29. package/src/cli/listing-arg.ts +321 -0
  30. package/src/cli/pane-functions/access-gate.ts +42 -0
  31. package/src/cli/pane-functions/catalog.ts +28 -1
  32. package/src/cli/pane-functions/data.ts +3 -1
  33. package/src/cli/pane-functions/dom.ts +20 -2
  34. package/src/cli/pane-functions/freshness.ts +400 -0
  35. package/src/cli/pane-functions/function-help.ts +36 -22
  36. package/src/cli/pane-functions/headless.ts +39 -11
  37. package/src/cli/pane-functions/index.ts +15 -4
  38. package/src/cli/pane-functions/options.ts +3 -0
  39. package/src/cli/pane-functions/report.ts +4 -0
  40. package/src/cli/pane-functions/resolver.ts +35 -7
  41. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  42. package/src/cli/pane-functions/screenshot.ts +7 -3
  43. package/src/cli/result.ts +41 -27
  44. package/src/cli/ticker-resolution.ts +7 -5
  45. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  46. package/src/components/chart/composite/drawing-store.ts +59 -0
  47. package/src/components/chart/composite/legend.tsx +319 -0
  48. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  49. package/src/components/chart/composite/panel-gestures.ts +59 -0
  50. package/src/components/chart/composite/panel-labels.ts +107 -0
  51. package/src/components/chart/composite/panel-surface.tsx +876 -0
  52. package/src/components/chart/composite/tool-catalog.ts +98 -0
  53. package/src/components/chart/composite/toolbar.tsx +186 -0
  54. package/src/components/chart/composite/tools.ts +8 -0
  55. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  56. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  57. package/src/components/command-bar/commands/collection.ts +5 -1
  58. package/src/components/command-bar/list/model.ts +5 -0
  59. package/src/components/command-bar/list/view.tsx +16 -3
  60. package/src/components/command-bar/pane-templates/workflow.ts +1 -0
  61. package/src/components/command-bar/routes/root/command-items.ts +9 -2
  62. package/src/components/command-bar/routes/root/plugin-install.ts +2 -2
  63. package/src/components/command-bar/routes/root/provider-search.ts +19 -5
  64. package/src/components/command-bar/routes/root/runtime.ts +14 -5
  65. package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
  66. package/src/components/command-bar/routes/ticker-search/results.ts +31 -5
  67. package/src/components/command-bar/routes/ticker-search/route.ts +2 -2
  68. package/src/components/command-bar/routing/actions.ts +2 -0
  69. package/src/components/command-bar/selection-runtime.ts +38 -0
  70. package/src/components/command-bar/surface/index.tsx +7 -2
  71. package/src/components/command-bar/workflow/ops.ts +1 -1
  72. package/src/components/command-bar/workflow/tickers.ts +6 -0
  73. package/src/components/data-table/view.tsx +29 -10
  74. package/src/components/feedback-dialog.tsx +2 -1
  75. package/src/components/form-modal/field-row.tsx +2 -2
  76. package/src/components/index.ts +2 -2
  77. package/src/components/layout/detached-pane-shell.tsx +6 -3
  78. package/src/components/layout/floating-pane.tsx +6 -0
  79. package/src/components/layout/header.tsx +24 -7
  80. package/src/components/layout/missing-pane.tsx +2 -2
  81. package/src/components/layout/pane/content.tsx +23 -10
  82. package/src/components/layout/pane/error-boundary.tsx +103 -0
  83. package/src/components/layout/pane/footer/registration.tsx +68 -23
  84. package/src/components/layout/pane/header.tsx +61 -28
  85. package/src/components/layout/pane/index.tsx +6 -0
  86. package/src/components/layout/pane/title.ts +31 -7
  87. package/src/components/layout/shell/drag/index.ts +6 -5
  88. package/src/components/layout/shell/drag/runtime.ts +3 -0
  89. package/src/components/layout/shell/index.tsx +6 -0
  90. package/src/components/layout/shell/menu.ts +2 -1
  91. package/src/components/layout/shell/pane/actions.ts +2 -1
  92. package/src/components/layout/shell/pane/layers.tsx +29 -10
  93. package/src/components/layout/shell/terminal-pointer-runtime.ts +24 -3
  94. package/src/components/layout/shell/ticker-link-menu.ts +23 -9
  95. package/src/components/metric-treemap/heat-surface.tsx +745 -0
  96. package/src/components/metric-treemap/heat-tiles.ts +228 -0
  97. package/src/components/metric-treemap/index.tsx +2 -0
  98. package/src/components/metric-treemap/layout.ts +8 -0
  99. package/src/components/metric-treemap/scene.ts +432 -0
  100. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  101. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  102. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  103. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  104. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  105. package/src/components/pane-settings-dialog/field-dialogs.tsx +1 -0
  106. package/src/components/ticker/list-table-view.tsx +13 -1
  107. package/src/components/toggle-list.tsx +38 -12
  108. package/src/components/ui/country-flag-art.tsx +449 -0
  109. package/src/components/ui/country-flag.tsx +41 -0
  110. package/src/components/ui/data-table/types.ts +13 -0
  111. package/src/components/ui/fields.tsx +8 -0
  112. package/src/components/ui/icon.tsx +2 -0
  113. package/src/components/ui/index.ts +4 -0
  114. package/src/components/ui/multi-select/dialog.tsx +108 -22
  115. package/src/components/ui/range-track.tsx +53 -0
  116. package/src/components/ui/status.tsx +8 -2
  117. package/src/components/ui/tabs.tsx +11 -4
  118. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  119. package/src/core/state/app/types.ts +10 -1
  120. package/src/data/config/store/normalize.ts +17 -1
  121. package/src/i18n/es.ts +24 -4
  122. package/src/i18n/ja.ts +24 -4
  123. package/src/i18n/ko.ts +24 -4
  124. package/src/i18n/zh-cn.ts +24 -4
  125. package/src/i18n/zh-tw.ts +24 -4
  126. package/src/layout/ticker-navigation.ts +28 -2
  127. package/src/market-data/coordinator/chart.ts +1 -1
  128. package/src/market-data/coordinator/quotes.ts +23 -4
  129. package/src/market-data/fx-closes.ts +72 -0
  130. package/src/market-data/market/freshness.ts +78 -6
  131. package/src/market-data/market/session-close-note.ts +57 -0
  132. package/src/market-data/market/status.ts +151 -0
  133. package/src/market-data/published-apac-sessions.ts +66 -0
  134. package/src/market-data/quotes/contributions.ts +8 -0
  135. package/src/market-data/quotes/freshness.ts +36 -0
  136. package/src/market-data/quotes/resolution.ts +24 -8
  137. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  138. package/src/market-data/quotes/time.ts +16 -0
  139. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  140. package/src/plugins/builtin/alerts/cli.ts +10 -5
  141. package/src/plugins/builtin/alerts/index.tsx +1 -0
  142. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  143. package/src/plugins/builtin/analytics/risk-client.ts +330 -79
  144. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  145. package/src/plugins/builtin/analytics/risk-headless.ts +61 -49
  146. package/src/plugins/builtin/analytics/risk-math.ts +33 -13
  147. package/src/plugins/builtin/analytics/risk-model.ts +282 -64
  148. package/src/plugins/builtin/analytics/risk-pane.tsx +62 -30
  149. package/src/plugins/builtin/analytics/risk-test-data.ts +206 -0
  150. package/src/plugins/builtin/attention/headless.ts +6 -4
  151. package/src/plugins/builtin/attention/index.tsx +1 -1
  152. package/src/plugins/builtin/attention/pane.tsx +1 -1
  153. package/src/plugins/builtin/awards/headless.ts +1 -0
  154. package/src/plugins/builtin/awards/pane.tsx +1 -1
  155. package/src/plugins/builtin/backtest/headless.ts +1 -0
  156. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  157. package/src/plugins/builtin/buildout/cells.tsx +15 -12
  158. package/src/plugins/builtin/buildout/detail/company.tsx +3 -3
  159. package/src/plugins/builtin/buildout/detail/index.tsx +0 -1
  160. package/src/plugins/builtin/buildout/detail/intel.tsx +6 -6
  161. package/src/plugins/builtin/buildout/detail/site.tsx +4 -7
  162. package/src/plugins/builtin/buildout/detail/ui.tsx +0 -19
  163. package/src/plugins/builtin/buildout/index.tsx +1 -0
  164. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  165. package/src/plugins/builtin/catalysts/index.tsx +2 -2
  166. package/src/plugins/builtin/catalysts/pane.tsx +3 -1
  167. package/src/plugins/builtin/cds/headless.ts +2 -0
  168. package/src/plugins/builtin/cds/index.tsx +1 -0
  169. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  170. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  171. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  172. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  173. package/src/plugins/builtin/chat/channels.ts +19 -3
  174. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  175. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  176. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  177. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  178. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  179. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  180. package/src/plugins/builtin/chat/index.tsx +1 -0
  181. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  182. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  183. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  184. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  185. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  186. package/src/plugins/builtin/chat/message/types.ts +14 -2
  187. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  188. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  189. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  190. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  191. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  192. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  193. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  194. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  195. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  196. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  197. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  198. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  199. package/src/plugins/builtin/correlation/compute.ts +59 -16
  200. package/src/plugins/builtin/correlation/geo.ts +34 -0
  201. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  202. package/src/plugins/builtin/correlation/headless.ts +42 -8
  203. package/src/plugins/builtin/correlation/history.ts +2 -0
  204. package/src/plugins/builtin/correlation/index.tsx +68 -14
  205. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  206. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  207. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  208. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  209. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  210. package/src/plugins/builtin/correlation/settings.ts +1 -1
  211. package/src/plugins/builtin/cot/headless.ts +6 -3
  212. package/src/plugins/builtin/cot/index.tsx +3 -3
  213. package/src/plugins/builtin/cot/model.ts +36 -3
  214. package/src/plugins/builtin/cpi/headless.ts +4 -0
  215. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  216. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  217. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  218. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  219. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  220. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  221. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  222. package/src/plugins/builtin/debt-maturities/wall-bar.tsx +14 -41
  223. package/src/plugins/builtin/dividend-yield/client.ts +4 -2
  224. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  225. package/src/plugins/builtin/doe/headless.ts +7 -0
  226. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  227. package/src/plugins/builtin/earnings/client.ts +2 -2
  228. package/src/plugins/builtin/earnings/headless.ts +5 -0
  229. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  230. package/src/plugins/builtin/earnings-calls/index.tsx +1 -0
  231. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  232. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  233. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  234. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  235. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  236. package/src/plugins/builtin/econ/index.tsx +1 -0
  237. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  238. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  239. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  240. package/src/plugins/builtin/executives/index.tsx +1 -0
  241. package/src/plugins/builtin/exposure/headless.ts +3 -1
  242. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  243. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  244. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  245. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  246. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  247. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  248. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  249. package/src/plugins/builtin/fx-matrix/direction.ts +81 -0
  250. package/src/plugins/builtin/fx-matrix/index.tsx +71 -33
  251. package/src/plugins/builtin/fx-matrix/live-legs.ts +17 -0
  252. package/src/plugins/builtin/fx-matrix/pairs.ts +68 -14
  253. package/src/plugins/builtin/fx-matrix/settings.ts +62 -0
  254. package/src/plugins/builtin/gpu/headless.ts +7 -3
  255. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  256. package/src/plugins/builtin/gpu/model.ts +27 -1
  257. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  258. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  259. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  260. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  261. package/src/plugins/builtin/holders/headless.ts +139 -5
  262. package/src/plugins/builtin/holders/index.tsx +10 -3
  263. package/src/plugins/builtin/holders/pane.tsx +20 -3
  264. package/src/plugins/builtin/holders/report-header.ts +95 -0
  265. package/src/plugins/builtin/holders/table-model.ts +20 -5
  266. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  267. package/src/plugins/builtin/holders/types.ts +2 -1
  268. package/src/plugins/builtin/insider/headless.ts +2 -0
  269. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  270. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  271. package/src/plugins/builtin/jobs/headless.ts +1 -0
  272. package/src/plugins/builtin/jobs/index.tsx +1 -0
  273. package/src/plugins/builtin/jobs/share-bars.tsx +5 -28
  274. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  275. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  276. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  277. package/src/plugins/builtin/market-heatmap/data.ts +30 -2
  278. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  279. package/src/plugins/builtin/market-heatmap/index.tsx +245 -81
  280. package/src/plugins/builtin/market-heatmap/live.ts +88 -0
  281. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  282. package/src/plugins/builtin/market-heatmap/portfolio.ts +50 -11
  283. package/src/plugins/builtin/market-movers/client.ts +3 -2
  284. package/src/plugins/builtin/market-movers/headless.ts +10 -1
  285. package/src/plugins/builtin/market-movers/screener.ts +18 -5
  286. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  287. package/src/plugins/builtin/members/client.ts +53 -0
  288. package/src/plugins/builtin/members/headless.ts +35 -0
  289. package/src/plugins/builtin/members/index.tsx +20 -0
  290. package/src/plugins/builtin/members/model.ts +68 -0
  291. package/src/plugins/builtin/members/pane.tsx +175 -0
  292. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  293. package/src/plugins/builtin/mna/headless.ts +1 -0
  294. package/src/plugins/builtin/mna/index.tsx +2 -1
  295. package/src/plugins/builtin/mna/pane.tsx +5 -2
  296. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  297. package/src/plugins/builtin/news/headless.ts +2 -0
  298. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  299. package/src/plugins/builtin/notes/cli.ts +14 -6
  300. package/src/plugins/builtin/notes/index.tsx +1 -0
  301. package/src/plugins/builtin/openfda/index.tsx +1 -0
  302. package/src/plugins/builtin/options/index.tsx +2 -1
  303. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  304. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  305. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  306. package/src/plugins/builtin/pe-band/client.ts +74 -2
  307. package/src/plugins/builtin/pe-band/headless.ts +26 -8
  308. package/src/plugins/builtin/pe-band/model.ts +102 -22
  309. package/src/plugins/builtin/pe-band/pane.tsx +34 -16
  310. package/src/plugins/builtin/pe-band/report-dates.ts +78 -0
  311. package/src/plugins/builtin/perps/client.ts +66 -5
  312. package/src/plugins/builtin/perps/equity-identity.ts +16 -0
  313. package/src/plugins/builtin/perps/equity-row.tsx +6 -4
  314. package/src/plugins/builtin/perps/headless.ts +144 -16
  315. package/src/plugins/builtin/perps/index.tsx +6 -6
  316. package/src/plugins/builtin/perps/model.ts +189 -12
  317. package/src/plugins/builtin/perps/pane.tsx +248 -47
  318. package/src/plugins/builtin/plugin-marketplace/activation.ts +19 -0
  319. package/src/plugins/builtin/plugin-marketplace/pane.tsx +2 -2
  320. package/src/plugins/builtin/plugin-marketplace/store.ts +2 -0
  321. package/src/plugins/builtin/portfolio-list/allocation.ts +245 -0
  322. package/src/plugins/builtin/portfolio-list/cached-account.ts +33 -0
  323. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  324. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  325. package/src/plugins/builtin/portfolio-list/cli/render.ts +230 -39
  326. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  327. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  328. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  329. package/src/plugins/builtin/portfolio-list/headless.ts +98 -16
  330. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  331. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  332. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  333. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  334. package/src/plugins/builtin/portfolio-list/pane/index.tsx +47 -13
  335. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +43 -2
  336. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +48 -5
  337. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  338. package/src/plugins/builtin/portfolio-list/summary/index.tsx +24 -1
  339. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  340. package/src/plugins/builtin/power/headless.ts +3 -1
  341. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  342. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  343. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  344. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  345. package/src/plugins/builtin/research/events-headless.ts +2 -0
  346. package/src/plugins/builtin/research/headless.ts +2 -0
  347. package/src/plugins/builtin/research/index.tsx +1 -0
  348. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  349. package/src/plugins/builtin/research/relative-valuation-model.ts +11 -3
  350. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  351. package/src/plugins/builtin/research-search/index.tsx +1 -0
  352. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  353. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  354. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  355. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  356. package/src/plugins/builtin/scanner/headless.ts +14 -0
  357. package/src/plugins/builtin/scanner/hilo-bars.tsx +12 -70
  358. package/src/plugins/builtin/scanner/hilo-model.ts +0 -18
  359. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  360. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  361. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  362. package/src/plugins/builtin/sec/forms.ts +12 -0
  363. package/src/plugins/builtin/sec/headless.ts +2 -0
  364. package/src/plugins/builtin/sectors/client.ts +1 -0
  365. package/src/plugins/builtin/sectors/headless.ts +9 -1
  366. package/src/plugins/builtin/sectors/move-bar.tsx +10 -32
  367. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  368. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  369. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  370. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  371. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  372. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  373. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  374. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  375. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  376. package/src/plugins/builtin/social-mentions/index.tsx +1 -1
  377. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  378. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  379. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  380. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  381. package/src/plugins/builtin/themes/headless.ts +3 -0
  382. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  383. package/src/plugins/builtin/thirteenf/index.tsx +2 -1
  384. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  385. package/src/plugins/builtin/ticker-detail/headless.ts +7 -1
  386. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  387. package/src/plugins/builtin/ticker-detail/overview/components.tsx +5 -60
  388. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -2
  389. package/src/plugins/builtin/ticker-detail/overview/pane.tsx +1 -1
  390. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +26 -26
  391. package/src/plugins/builtin/ticker-detail/pane.tsx +1 -1
  392. package/src/plugins/builtin/ticker-detail/quote-footer.ts +7 -1
  393. package/src/plugins/builtin/ticker-detail/quote-monitor/card.tsx +20 -4
  394. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +2 -2
  395. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  396. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  397. package/src/plugins/builtin/vol-surface/index.tsx +1 -1
  398. package/src/plugins/builtin/volatility/headless.ts +6 -1
  399. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  400. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +157 -0
  401. package/src/plugins/builtin/world-venue-map/basemap.ts +214 -0
  402. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  403. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  404. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  405. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  406. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  407. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  408. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  409. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +447 -0
  410. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  411. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  412. package/src/plugins/builtin/world-venue-map/index.ts +67 -4
  413. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +552 -0
  414. package/src/plugins/builtin/world-venue-map/layers.ts +348 -0
  415. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  416. package/src/plugins/builtin/world-venue-map/map.tsx +381 -45
  417. package/src/plugins/builtin/world-venue-map/model.ts +7 -4
  418. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +199 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  524. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  525. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  526. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  527. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  528. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  529. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  530. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  531. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  532. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  533. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  534. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  535. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  536. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  537. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  538. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  539. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  540. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  541. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  542. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  543. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  544. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  545. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  546. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  547. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  548. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  549. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  550. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  551. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  552. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  553. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  554. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  555. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  556. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  557. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  558. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  559. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  560. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  561. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  562. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  563. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  564. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  565. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  566. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  567. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  568. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  569. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  570. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  571. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  572. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  573. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  574. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  575. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  576. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  577. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  578. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  579. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  580. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  581. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  582. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  583. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  584. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  585. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  586. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  587. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  588. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  589. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  590. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  591. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  592. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  593. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  594. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  595. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  596. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  597. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  598. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  599. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  600. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  601. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  602. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  603. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  604. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  605. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  606. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  607. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  608. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  609. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  610. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  611. package/src/plugins/builtin/world-venue-map/pane.tsx +28 -43
  612. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  613. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  614. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  615. package/src/plugins/catalog-browser.ts +2 -0
  616. package/src/plugins/catalog-manifest.ts +1 -0
  617. package/src/plugins/catalog-ui.ts +3 -1
  618. package/src/plugins/catalog.ts +1 -1
  619. package/src/plugins/host-link.ts +33 -1
  620. package/src/plugins/installer.ts +48 -12
  621. package/src/plugins/loader.ts +30 -3
  622. package/src/plugins/manager-node.ts +15 -5
  623. package/src/plugins/registry/index.ts +1 -1
  624. package/src/plugins/registry/pane-settings.ts +26 -7
  625. package/src/public/__snapshots__/public-api.test.ts.snap +2 -0
  626. package/src/renderers/dom/build-assets.ts +81 -3
  627. package/src/renderers/dom/data-table/row.tsx +52 -16
  628. package/src/renderers/dom/desktop/controls.tsx +7 -3
  629. package/src/renderers/dom/desktop/icons.tsx +1 -0
  630. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  631. package/src/renderers/dom/desktop/popover.tsx +73 -14
  632. package/src/renderers/dom/host/box.tsx +69 -26
  633. package/src/renderers/dom/host/input.tsx +14 -3
  634. package/src/renderers/dom/host/tabs.tsx +37 -1
  635. package/src/renderers/dom/styles.css +41 -7
  636. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +11 -6
  637. package/src/renderers/electrobun/bun/desktop/backend.ts +1 -0
  638. package/src/renderers/electrobun/bun/desktop/update.ts +181 -87
  639. package/src/renderers/electrobun/bun/index.ts +73 -6
  640. package/src/renderers/electrobun/bun/window/remembered-frame.ts +122 -0
  641. package/src/renderers/electrobun/shared/protocol.ts +3 -0
  642. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  643. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +3 -1
  644. package/src/renderers/electrobun/view/main.tsx +7 -1
  645. package/src/renderers/electrobun/view/renderer-window-error.ts +18 -0
  646. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  647. package/src/renderers/electrobun/view/update-host.ts +45 -21
  648. package/src/renderers/opentui/host.tsx +5 -0
  649. package/src/sources/gloomberb-cloud/index.ts +25 -4
  650. package/src/sources/gloomberb-cloud/normalizers.ts +36 -5
  651. package/src/sources/provider-router/cached-routes.ts +4 -1
  652. package/src/sources/provider-router/financials.ts +11 -8
  653. package/src/sources/provider-router/history.ts +15 -10
  654. package/src/sources/sec-registrant.ts +75 -0
  655. package/src/state/app/context/index.tsx +24 -8
  656. package/src/state/hooks/quote-streaming.ts +41 -26
  657. package/src/sync/core-contributors.ts +4 -0
  658. package/src/telemetry/benign-window-error.ts +7 -0
  659. package/src/telemetry/crash-reports-dom.ts +2 -0
  660. package/src/telemetry/crash-reports.ts +4 -1
  661. package/src/test-support/cli-context.ts +10 -2
  662. package/src/test-support/daily-nav.ts +25 -0
  663. package/src/theme/heat-colors.ts +357 -2
  664. package/src/tickers/instrument-kind.ts +34 -7
  665. package/src/tickers/search/asset-classes.ts +24 -9
  666. package/src/tickers/search/index.ts +44 -11
  667. package/src/tickers/search/ranking.ts +23 -3
  668. package/src/time-series/resolve.ts +1 -1
  669. package/src/time-series/use-chart-resolution.ts +1 -1
  670. package/src/types/context-menu.ts +6 -0
  671. package/src/types/data-provider.ts +2 -0
  672. package/src/types/financials.ts +5 -0
  673. package/src/types/headless.ts +79 -2
  674. package/src/types/plugin.ts +38 -6
  675. package/src/types/ticker.ts +15 -0
  676. package/src/ui/context-menu.tsx +1 -0
  677. package/src/ui/host.tsx +16 -0
  678. package/src/updater/index.ts +38 -13
  679. package/src/utils/async-deadline.ts +21 -0
  680. package/src/utils/cli-output.ts +5 -1
  681. package/src/utils/crypto-pair.ts +41 -0
  682. package/src/utils/currency-units.ts +14 -0
  683. package/src/utils/depositary-receipt.ts +26 -0
  684. package/src/utils/exchanges.ts +72 -0
  685. package/src/utils/fire-and-forget.ts +24 -0
  686. package/src/utils/format.ts +13 -0
  687. package/src/utils/price-earnings.ts +70 -3
  688. package/src/utils/price-history.ts +58 -13
  689. package/src/utils/rpc-timeout-error.ts +27 -0
  690. package/src/utils/sec.ts +13 -1
  691. package/src/utils/utc-time.ts +79 -0
@@ -1,4 +1,4 @@
1
- import { useCallback, useMemo, useRef, useState } from "react";
1
+ import { useCallback, useMemo, useRef, useState, type ReactNode } from "react";
2
2
  import {
3
3
  DataTableView,
4
4
  usePaneFooter,
@@ -8,34 +8,42 @@ import {
8
8
  type PaneFooterSegment,
9
9
  } from "../../../components";
10
10
  import { handleRefreshKey } from "../../../components/data-table/table-pane";
11
+ import { CountryFlag } from "../../../components/ui/country-flag";
11
12
  import { getSharedMarketDataCoordinator } from "../../../market-data/coordinator";
12
13
  import { useFxRatesMap } from "../../../market-data/hooks";
13
14
  import { resolveEntryData } from "../../../market-data/selectors";
14
15
  import { usePaneSettingValue } from "../../../state/app/context";
15
16
  import { colors } from "../../../theme/colors";
16
17
  import type { PaneProps } from "../../../types/plugin";
17
- import { TextAttributes } from "../../../ui";
18
+ import { TextAttributes, useUiCapabilities } from "../../../ui";
18
19
  import { useAssetData } from "../../runtime";
19
20
  import { summarizeFxRates, fxStatusLabel } from "../../../utils/fx-status";
20
21
  import type { PluginModule } from "../plugin-module";
21
22
  import { useLiveQuoteEntries } from "../../../state/hooks/quote-streaming";
22
23
  import { useAutoRefresh, useUpdatedAgo } from "../../../react/auto-refresh";
23
24
  import { useLiveStreamingSetting } from "../../../state/hooks/live-streaming";
24
- import { isStreamCarryingQuote } from "../shared/use-quote-board";
25
- import { fxLegQuoteKey, fxLegReferenceRate, fxLegTargets, fxLegs, liveFxLegEntry } from "./live-legs";
26
- import { MAJOR_CURRENCIES, formatRate, resolveCurrencies, type MajorCurrency } from "./pairs";
25
+ import { fxLegQuoteKey, fxLegReferenceRate, fxLegTargets, fxLegs, fxLegsBehind, liveFxLegEntry } from "./live-legs";
26
+ import { CURRENCY_FLAG_REGIONS, FX_CURRENCIES, formatRate, resolveCurrencies, type FxCurrency } from "./pairs";
27
+ import { crossMovePercent, directionTint, nextTintLevel } from "./direction";
28
+ import { fxMatrixSettings } from "./settings";
27
29
  import { createFxExportMetadata } from "./export";
28
30
 
29
31
  const FX_MATRIX_PANE_ID = "fx-matrix";
30
32
  /** Stable identity: a fresh literal here would reload the board every render. */
31
33
  const NO_SAVED_CURRENCIES: string[] = [];
32
34
  const BASE_COLUMN_WIDTH = 5;
35
+ /** Room for a flag, a space and the code once the desktop draws the flag. */
36
+ const FLAGGED_BASE_COLUMN_WIDTH = 6;
37
+ const CURRENCY_FLAGS = new Map<string, ReactNode>(
38
+ FX_CURRENCIES.map((currency) => [currency, <CountryFlag region={CURRENCY_FLAG_REGIONS[currency]} />]),
39
+ );
33
40
  const RATE_COLUMN_WIDTH = 10;
34
41
  /** Snapshot rates reload at this pace while the feed is not carrying every leg. */
35
42
  const FX_FALLBACK_REFRESH_MS = 60_000;
36
43
 
37
44
  function FxMatrixPane({ focused, width, height }: PaneProps) {
38
45
  const dataProvider = useAssetData();
46
+ const flagged = useUiCapabilities().nativePaneChrome === true;
39
47
  const [savedCurrencies] = usePaneSettingValue<string[]>("currencies", NO_SAVED_CURRENCIES);
40
48
  const currencies = useMemo(() => resolveCurrencies(savedCurrencies), [savedCurrencies]);
41
49
  const [selectedCurrency, setSelectedCurrency] = useState<string | null>(null);
@@ -80,6 +88,22 @@ function FxMatrixPane({ focused, width, height }: PaneProps) {
80
88
  }
81
89
  return references;
82
90
  }, [legEntries, legs, rates]);
91
+ // A cell's direction is its cross against the pair's previous close. Only a
92
+ // leg whose rate is the live quote's and whose quote carries a previous close
93
+ // counts, USD (the base) as 1: the first rate this pane drew is no daily
94
+ // reference, and a snapshot rate or a stale quote has no previous close to
95
+ // read against.
96
+ const directionReferences = useMemo(() => {
97
+ const references = new Map<string, number>([["USD", 1]]);
98
+ for (const leg of legs) {
99
+ if (!liveEntries.has(leg.currency)) continue;
100
+ const reference = fxLegReferenceRate(leg, legEntries.get(fxLegQuoteKey(leg)));
101
+ if (reference != null) references.set(leg.currency, reference);
102
+ }
103
+ return references;
104
+ }, [legEntries, legs, liveEntries]);
105
+ // The step each cell showed last, which a tick near a boundary keeps.
106
+ const tintLevels = useRef(new Map<string, number>());
83
107
  // Export the provenance of this render's rates, even if a provider response
84
108
  // arrives before React commits the next render and the user exports now.
85
109
  const rateEntries = new Map(currencies.map((currency) => {
@@ -99,47 +123,59 @@ function FxMatrixPane({ focused, width, height }: PaneProps) {
99
123
  // current relaxes the snapshot reload: a quote another pane loaded, or one
100
124
  // the stream stopped sending, would otherwise freeze the matrix. With
101
125
  // streaming off, the legs' own quote poll is the feed.
102
- const allLegsLive = legs.every((leg) => {
103
- const entry = legEntries.get(fxLegQuoteKey(leg));
104
- if (!liveStreaming) return liveEntries.has(leg.currency) && (entry?.fetchedAt ?? 0) >= subscriptionStartedAt;
105
- return isStreamCarryingQuote(entry, subscriptionStartedAt, freshnessNow);
126
+ const legsBehind = fxLegsBehind(legs, legEntries, {
127
+ liveStreaming,
128
+ liveCurrencies: new Set(liveEntries.keys()),
129
+ startedAt: subscriptionStartedAt,
130
+ now: freshnessNow,
106
131
  });
132
+ const allLegsLive = legsBehind.length === 0;
107
133
 
108
- const refresh = useCallback(() => {
134
+ const reload = useCallback((codes: readonly string[]) => {
109
135
  const coordinator = getSharedMarketDataCoordinator();
110
136
  if (!coordinator) return;
111
- for (const currency of currencies) {
137
+ for (const currency of codes) {
112
138
  if (currency === "USD") continue;
113
139
  void coordinator.loadFxRate(currency, { forceRefresh: true }).catch(() => {});
114
140
  }
115
- }, [currencies]);
141
+ }, []);
142
+ const refresh = useCallback(() => reload(currencies), [currencies, reload]);
116
143
 
117
- useAutoRefresh(snapshotFetchedAt || null, refresh, { intervalMs: allLegsLive ? null : FX_FALLBACK_REFRESH_MS });
144
+ // The reload that stands in for the feed asks only for the legs it is
145
+ // behind on: a thin pair that rarely ticks must not make every selected
146
+ // currency reload each minute.
147
+ useAutoRefresh(snapshotFetchedAt || null, () => reload(allLegsLive ? currencies : legsBehind), {
148
+ intervalMs: allLegsLive ? null : FX_FALLBACK_REFRESH_MS,
149
+ });
118
150
 
119
151
  const columns = useMemo<DataTableColumn[]>(() => [
120
- { id: "base", label: "", width: BASE_COLUMN_WIDTH, align: "left" },
152
+ { id: "base", label: "", width: flagged ? FLAGGED_BASE_COLUMN_WIDTH : BASE_COLUMN_WIDTH, align: "left" },
121
153
  ...currencies.map((currency) => ({
122
154
  id: currency,
123
155
  label: currency,
124
156
  width: RATE_COLUMN_WIDTH,
125
157
  align: "right" as const,
158
+ headerLeading: flagged ? CURRENCY_FLAGS.get(currency) : undefined,
126
159
  })),
127
- ], [currencies]);
160
+ ], [currencies, flagged]);
128
161
 
129
162
  const renderCell = useCallback((
130
- row: MajorCurrency,
163
+ row: FxCurrency,
131
164
  column: DataTableColumn,
165
+ _index: number,
166
+ rowState: { selected: boolean },
132
167
  ): DataTableCell => {
133
168
  if (column.id === "base") {
134
169
  return {
135
170
  text: row,
136
171
  color: colors.textBright,
137
172
  attributes: TextAttributes.BOLD,
173
+ leading: flagged ? CURRENCY_FLAGS.get(row) : undefined,
138
174
  };
139
175
  }
140
176
 
141
- const quoteCurrency = column.id as MajorCurrency;
142
- if (row === quoteCurrency) return { text: formatRate(1, quoteCurrency), color: colors.textDim };
177
+ const quoteCurrency = column.id as FxCurrency;
178
+ if (row === quoteCurrency) return { text: formatRate(1), color: colors.textDim };
143
179
 
144
180
  const base = rates.get(row);
145
181
  const quote = rates.get(quoteCurrency);
@@ -150,8 +186,21 @@ function FxMatrixPane({ focused, width, height }: PaneProps) {
150
186
  return { text: pending ? "…" : "—", color: colors.textDim };
151
187
  }
152
188
  const reference = (referenceRates.get(row) ?? base) / (referenceRates.get(quoteCurrency) ?? quote);
153
- return { text: formatRate(base / quote, quoteCurrency, reference), color: colors.text };
154
- }, [rates, referenceRates, status.loading]);
189
+ const rowReference = directionReferences.get(row);
190
+ const columnReference = directionReferences.get(quoteCurrency);
191
+ const level = nextTintLevel(
192
+ tintLevels.current,
193
+ `${row}/${quoteCurrency}`,
194
+ rowReference != null && columnReference != null
195
+ ? crossMovePercent(base, rowReference, quote, columnReference)
196
+ : null,
197
+ );
198
+ // The cursor row keeps its selection colours; the tint leans on them.
199
+ const backgroundColor = rowState.selected
200
+ ? directionTint(level, { background: colors.selected, text: colors.selectedText })
201
+ : directionTint(level, { background: colors.bg, text: colors.text });
202
+ return { text: formatRate(base / quote, reference), color: colors.text, backgroundColor };
203
+ }, [directionReferences, flagged, rates, referenceRates, status.loading]);
155
204
 
156
205
  const handleKeyDown = useCallback((event: DataTableKeyEvent) => handleRefreshKey(event, refresh), [refresh]);
157
206
 
@@ -166,7 +215,7 @@ function FxMatrixPane({ focused, width, height }: PaneProps) {
166
215
  }, [status.loading, statusText, updatedAgo]);
167
216
 
168
217
  return (
169
- <DataTableView<MajorCurrency>
218
+ <DataTableView<FxCurrency>
170
219
  focused={focused}
171
220
  selection={{
172
221
  kind: "id",
@@ -203,18 +252,7 @@ export const fxMatrixModule: PluginModule = {
203
252
  defaultMode: "floating",
204
253
  defaultFloatingSize: { width: 105, height: 14 },
205
254
  tableExport: true,
206
- settings: (context) => ({
207
- title: "FX Cross Rates Settings",
208
- values: {
209
- currencies: resolveCurrencies(context.settings.currencies as string[] | undefined),
210
- },
211
- fields: [{
212
- key: "currencies",
213
- label: "Currencies",
214
- type: "ordered-multi-select",
215
- options: MAJOR_CURRENCIES.map((currency) => ({ value: currency, label: currency })),
216
- }],
217
- }),
255
+ settings: fxMatrixSettings,
218
256
  },
219
257
  ],
220
258
 
@@ -2,6 +2,7 @@ import type { QueryEntry } from "../../../market-data/result-types";
2
2
  import { buildQuoteKey, resolveEntryData } from "../../../market-data/selectors";
3
3
  import type { QuoteSubscriptionTarget } from "../../../types/data-provider";
4
4
  import type { Quote } from "../../../types/financials";
5
+ import { isStreamCarryingQuote } from "../shared/use-quote-board";
5
6
 
6
7
  /** Currencies the market quotes as USD per unit; the rest quote as units per USD. */
7
8
  const USD_QUOTED = new Set(["EUR", "GBP", "AUD", "NZD"]);
@@ -69,6 +70,22 @@ export function liveFxLegEntry(
69
70
  };
70
71
  }
71
72
 
73
+ /**
74
+ * The legs the feed is not carrying, whose snapshot rates a reload has to
75
+ * stand in for. With streaming off the legs' own quote poll is the feed.
76
+ */
77
+ export function fxLegsBehind(
78
+ legs: readonly FxLeg[],
79
+ entries: ReadonlyMap<string, QueryEntry<Quote>>,
80
+ feed: { liveStreaming: boolean; liveCurrencies: ReadonlySet<string>; startedAt: number; now: number },
81
+ ): string[] {
82
+ return legs.filter((leg) => {
83
+ const entry = entries.get(fxLegQuoteKey(leg));
84
+ if (!feed.liveStreaming) return !(feed.liveCurrencies.has(leg.currency) && (entry?.fetchedAt ?? 0) >= feed.startedAt);
85
+ return !isStreamCarryingQuote(entry, feed.startedAt, feed.now);
86
+ }).map((leg) => leg.currency);
87
+ }
88
+
72
89
  /** USD per unit at the pair's previous close: a rate that holds still for the session. */
73
90
  export function fxLegReferenceRate(leg: FxLeg, entry: QueryEntry<Quote> | undefined): number | null {
74
91
  const close = resolveEntryData(entry)?.previousClose;
@@ -1,27 +1,81 @@
1
-
1
+ /** The default board, in its default order. */
2
2
  export const MAJOR_CURRENCIES = ["USD", "EUR", "GBP", "JPY", "CHF", "CAD", "AUD", "NZD"] as const;
3
- export type MajorCurrency = typeof MAJOR_CURRENCIES[number];
4
3
 
5
4
  /**
6
- * Preserve conventional quote precision and meaningful digits on inverse JPY
7
- * crosses. The digits are counted at `referenceRate` (the cross at a rate that
8
- * holds still) when there is one, so a live rate ticking across a power of ten,
9
- * such as JPY/AUD near 0.0100, keeps its decimals.
5
+ * Every currency the board can carry, in the order its settings list them: the
6
+ * majors, the other liquid currencies, then emerging markets by region.
7
+ */
8
+ export const FX_CURRENCIES = [
9
+ ...MAJOR_CURRENCIES,
10
+ "SEK", "NOK", "DKK", "HKD", "SGD", "CNY", "CNH",
11
+ "KRW", "TWD", "INR", "IDR", "MYR", "PHP", "THB", "VND", "PKR",
12
+ "PLN", "CZK", "HUF", "RON", "UAH", "TRY", "KZT",
13
+ "ILS", "SAR", "AED", "QAR", "EGP", "ZAR", "NGN", "KES",
14
+ "MXN", "BRL", "CLP", "COP", "PEN", "ARS",
15
+ ] as const;
16
+ export type FxCurrency = typeof FX_CURRENCIES[number];
17
+
18
+ /** The flag each currency wears on the desktop and web; the euro is the EU's, not one member's. */
19
+ export const CURRENCY_FLAG_REGIONS: Record<FxCurrency, string> = {
20
+ USD: "US", EUR: "EU", GBP: "GB", JPY: "JP", CHF: "CH", CAD: "CA", AUD: "AU", NZD: "NZ",
21
+ SEK: "SE", NOK: "NO", DKK: "DK", HKD: "HK", SGD: "SG", CNY: "CN", CNH: "CN",
22
+ KRW: "KR", TWD: "TW", INR: "IN", IDR: "ID", MYR: "MY", PHP: "PH", THB: "TH", VND: "VN", PKR: "PK",
23
+ PLN: "PL", CZK: "CZ", HUF: "HU", RON: "RO", UAH: "UA", TRY: "TR", KZT: "KZ",
24
+ ILS: "IL", SAR: "SA", AED: "AE", QAR: "QA", EGP: "EG", ZAR: "ZA", NGN: "NG", KES: "KE",
25
+ MXN: "MX", BRL: "BR", CLP: "CL", COP: "CO", PEN: "PE", ARS: "AR",
26
+ };
27
+
28
+ /** One-pick selections in the pane settings. Each fills the currency list, which stays editable. */
29
+ export const CURRENCY_SETS: readonly { id: string; label: string; currencies: readonly FxCurrency[] }[] = [
30
+ { id: "majors", label: "Majors", currencies: MAJOR_CURRENCIES },
31
+ { id: "g10", label: "G10", currencies: [...MAJOR_CURRENCIES, "SEK", "NOK"] },
32
+ {
33
+ id: "asia",
34
+ label: "Asia",
35
+ currencies: ["USD", "JPY", "CNY", "CNH", "HKD", "SGD", "TWD", "KRW", "INR", "IDR", "MYR", "PHP", "THB", "VND", "PKR"],
36
+ },
37
+ {
38
+ id: "emea",
39
+ label: "EMEA",
40
+ currencies: [
41
+ "USD", "EUR", "GBP", "CHF", "SEK", "NOK", "DKK", "PLN", "CZK", "HUF", "RON", "UAH", "TRY", "KZT",
42
+ "ILS", "SAR", "AED", "QAR", "EGP", "ZAR", "NGN", "KES",
43
+ ],
44
+ },
45
+ { id: "americas", label: "Americas", currencies: ["USD", "CAD", "MXN", "BRL", "CLP", "COP", "PEN", "ARS"] },
46
+ { id: "all", label: "All", currencies: FX_CURRENCIES },
47
+ ];
48
+
49
+ /**
50
+ * Decimals follow the rate's size, counted at `referenceRate` (the cross at a
51
+ * rate that holds still) when there is one, so a live rate ticking across a
52
+ * power of ten, such as JPY/AUD near 0.0100, keeps its decimals. From 50 up a
53
+ * rate reads two decimals (149.25, 1340.50, 33000.12); below 0.1 it keeps five
54
+ * significant digits, to eight decimals; everything between reads four.
10
55
  */
11
- export function formatRate(rate: number, toCurrency: string, referenceRate = rate): string {
56
+ export function formatRate(rate: number, referenceRate = rate): string {
12
57
  if (!Number.isFinite(rate) || rate <= 0) return "—";
13
58
  const basis = Number.isFinite(referenceRate) && referenceRate > 0 ? referenceRate : rate;
14
- const decimals = basis < 0.1 ? Math.min(8, 4 - Math.floor(Math.log10(basis))) : toCurrency === "JPY" ? 2 : 4;
59
+ const decimals = basis < 0.1 ? Math.min(8, 4 - Math.floor(Math.log10(basis))) : basis >= 50 ? 2 : 4;
15
60
  return rate.toFixed(decimals);
16
61
  }
17
62
 
63
+ function isFxCurrency(code: unknown): code is FxCurrency {
64
+ return typeof code === "string" && FX_CURRENCIES.includes(code as FxCurrency);
65
+ }
66
+
18
67
  /**
19
- * The saved currency selection, falling back to the majors when it is empty or
20
- * holds codes this board does not carry.
68
+ * The saved currency selection in its saved order, falling back to the majors
69
+ * when it is empty or holds no code this board carries. Unknown codes and
70
+ * repeats drop out. A string is a typed list, as in `--currencies USD,EUR` or
71
+ * `--currencies asia`: codes and set names, in any case.
21
72
  */
22
- export function resolveCurrencies(saved?: readonly string[]): MajorCurrency[] {
23
- const resolved = (saved ?? []).filter(
24
- (code): code is MajorCurrency => MAJOR_CURRENCIES.includes(code as MajorCurrency),
25
- );
73
+ export function resolveCurrencies(saved?: unknown): FxCurrency[] {
74
+ const codes: readonly unknown[] = typeof saved === "string"
75
+ ? saved.split(/[\s,]+/).flatMap((token): readonly string[] => (
76
+ CURRENCY_SETS.find((set) => set.id === token.toLowerCase())?.currencies ?? [token.toUpperCase()]
77
+ ))
78
+ : Array.isArray(saved) ? saved : [];
79
+ const resolved = [...new Set(codes.filter(isFxCurrency))];
26
80
  return resolved.length > 0 ? resolved : [...MAJOR_CURRENCIES];
27
81
  }
@@ -0,0 +1,62 @@
1
+ import type { PaneSettingsContext, PaneSettingsDef } from "../../../types/plugin";
2
+ import { CURRENCY_SETS, FX_CURRENCIES, resolveCurrencies } from "./pairs";
3
+
4
+ const CUSTOM_SET = "custom";
5
+
6
+ let currencyNames: Intl.DisplayNames | null | undefined;
7
+
8
+ /** ISO has no code for the offshore yuan, so the runtime has no name for it. */
9
+ const CURRENCY_NAMES: Partial<Record<string, string>> = { CNH: "Chinese Yuan (Offshore)" };
10
+
11
+ /** A currency's English name for the picker, or null where the runtime has no names. */
12
+ function currencyName(code: string): string | null {
13
+ if (CURRENCY_NAMES[code]) return CURRENCY_NAMES[code]!;
14
+ try {
15
+ currencyNames ??= new Intl.DisplayNames(["en"], { type: "currency" });
16
+ const name = currencyNames.of(code);
17
+ return name && name !== code ? name : null;
18
+ } catch {
19
+ currencyNames = null;
20
+ return null;
21
+ }
22
+ }
23
+
24
+ /**
25
+ * Currencies are chosen as an ordered list. A set fills that list in one pick;
26
+ * the list stays editable, and reads as Custom once it stops matching a set.
27
+ */
28
+ export function fxMatrixSettings(context: PaneSettingsContext): PaneSettingsDef {
29
+ const currencies = resolveCurrencies(context.settings.currencies);
30
+ const matching = CURRENCY_SETS.find((set) => (
31
+ set.currencies.length === currencies.length && set.currencies.every((code, index) => code === currencies[index])
32
+ ));
33
+ return {
34
+ title: "FX Cross Rates Settings",
35
+ values: { currencies, currencySet: matching?.id ?? CUSTOM_SET },
36
+ fields: [
37
+ {
38
+ key: "currencySet",
39
+ label: "Currency set",
40
+ type: "select",
41
+ options: [
42
+ ...CURRENCY_SETS.map((set) => ({ value: set.id, label: set.label })),
43
+ { value: CUSTOM_SET, label: "Custom" },
44
+ ],
45
+ },
46
+ {
47
+ key: "currencies",
48
+ label: "Currencies",
49
+ type: "ordered-multi-select",
50
+ searchable: true,
51
+ options: FX_CURRENCIES.map((code) => ({ value: code, label: code, description: currencyName(code) ?? undefined })),
52
+ },
53
+ ],
54
+ applyValue: (settings, field, value) => {
55
+ if (field.key !== "currencySet") return { ...settings, [field.key]: value };
56
+ // The set is a way to fill the list, never a setting of its own.
57
+ const { currencySet: _unused, ...rest } = settings;
58
+ const set = CURRENCY_SETS.find((candidate) => candidate.id === value);
59
+ return set ? { ...rest, currencies: [...set.currencies] } : rest;
60
+ },
61
+ };
62
+ }
@@ -2,6 +2,7 @@ import type { GpuObservation } from "../../../api-client/gpu";
2
2
  import type { HeadlessPaneDefinition } from "../../../types/plugin";
3
3
  import { fetchGpuBoard, fetchGpuEvents, fetchGpuHistory, loadGpuEquityHistory } from "./client";
4
4
  import { GPU_TABS, gpuArgument, gpuBasisLabel, gpuChange, gpuEquityRows, gpuEventDate, gpuProvenanceLabel, gpuLabel, gpuRows, gpuSource, gpuTab } from "./model";
5
+ import { quoteFreshnessFields } from "../shared/report-freshness";
5
6
 
6
7
  const observationRow = (row: GpuObservation) => ({ gpu: gpuLabel(row), source: gpuSource(row), basis: gpuBasisLabel(row.basis),
7
8
  price: row.pricePerGpuHr, availability: row.availability, observedAt: row.observedAt, effectiveAt: row.effectiveAt, provenance: row.provenance ?? "live", provenanceLabel: gpuProvenanceLabel(row, true),
@@ -26,9 +27,11 @@ export const gpuHeadless: HeadlessPaneDefinition<"bundle"> = {
26
27
  const tab = gpuTab(args.options.tab);
27
28
  const board = await fetchGpuBoard(ctx.apiClient);
28
29
  const metadata = { asOf: board.asOf, stale: board.stale, complete: board.status === "available" && !board.access?.locked, unit: "USD/GPU-hour", access: board.access };
30
+ // Prices observed on providers' pages, not a feed; each board row says whether its source has gone quiet.
31
+ const freshness = { source: "GPU cloud providers", status: "not-a-feed" as const, basis: "observed prices" };
29
32
  if (tab === "history") {
30
33
  const history = await fetchGpuHistory({ ...(model ? { gpuModel: model } : {}), limit: 10_000 }, ctx.apiClient);
31
- return { sections: [{ title: "Dated observations", columns: [...columns.filter((column) => !column.key.startsWith("change")), { key: "provenanceLabel", header: "Record" }], rows: history.points.map(observationRow) }], complete: !history.access?.locked, metadata: { ...metadata, access: history.access ?? board.access }, errors: board.gaps };
34
+ return { sections: [{ title: "Dated observations", columns: [...columns.filter((column) => !column.key.startsWith("change")), { key: "provenanceLabel", header: "Record" }], rows: history.points.map(observationRow) }], freshness: { ...freshness, oldest: null }, complete: !history.access?.locked, metadata: { ...metadata, access: history.access ?? board.access }, errors: board.gaps };
32
35
  }
33
36
  if (tab === "changes") {
34
37
  const payload = await fetchGpuEvents(model || undefined, ctx.apiClient);
@@ -41,7 +44,7 @@ export const gpuHeadless: HeadlessPaneDefinition<"bundle"> = {
41
44
  oldPrice: event.oldPrice, newPrice: event.newPrice, changePct: event.changePct, kind: event.kind,
42
45
  oldMembers: event.oldMembers, newMembers: event.newMembers, provenance: event.provenance ?? "live",
43
46
  provenanceLabel: gpuProvenanceLabel(event, true), evidenceUrl: event.evidenceUrl ?? null, sourceUrl: event.sourceUrl ?? null,
44
- oldAvailability: event.oldAvailability ?? null, newAvailability: event.newAvailability ?? null })) }], complete: !payload.access?.locked, metadata: { ...metadata, access: payload.access ?? board.access }, errors: board.gaps };
47
+ oldAvailability: event.oldAvailability ?? null, newAvailability: event.newAvailability ?? null })) }], freshness: { ...freshness, oldest: null }, complete: !payload.access?.locked, metadata: { ...metadata, access: payload.access ?? board.access }, errors: board.gaps };
45
48
  }
46
49
  if (tab === "equities") {
47
50
  const related = gpuEquityRows(board.rows, model || "H100");
@@ -51,6 +54,7 @@ export const gpuHeadless: HeadlessPaneDefinition<"bundle"> = {
51
54
  const history = histories.find((entry) => entry.symbol === row.symbol);
52
55
  return { symbol: row.symbol, role: row.role, price: quote?.price ?? null, change1d: quote?.changePercent ?? null,
53
56
  change5d: history?.value ?? null, fiveDayAsOf: history?.asOf ?? null, quoteAsOf: quote?.lastUpdated ? new Date(quote.lastUpdated).toISOString() : null,
57
+ ...quoteFreshnessFields(quote),
54
58
  gpu: row.reference ? gpuLabel(row.reference) : row.gpuModel, gpuSource: row.reference ? gpuSource(row.reference) : null, gpuChange7d: row.reference?.change7d ?? null };
55
59
  }));
56
60
  return { sections: [{ title: "Related equities", columns: [
@@ -62,6 +66,6 @@ export const gpuHeadless: HeadlessPaneDefinition<"bundle"> = {
62
66
  }
63
67
  const rows = gpuRows(board.rows, model).map((row) => ({ ...observationRow(row), change1d: row.change1d, change7d: row.change7d,
64
68
  change30d: row.change30d, stale: row.stale, sample: row.stats ?? null }));
65
- return { sections: [{ title: "GPU rental prices", columns, rows }], complete: !board.access?.locked, metadata, errors: board.gaps };
69
+ return { sections: [{ title: "GPU rental prices", columns, rows }], freshness, complete: !board.access?.locked, metadata, errors: board.gaps };
66
70
  },
67
71
  };
@@ -1,8 +1,8 @@
1
1
  import { Box, Span, Text, TextAttributes, useUiCapabilities } from "../../../ui";
2
2
  import { useThemeColors } from "../../../theme/theme-context";
3
3
  import { blendHex } from "../../../theme/colors";
4
- import { displayWidth, truncateToDisplayWidth } from "../../../utils/format";
5
- import { gpuAxisTicks, gpuBasisColor, gpuBasisLabel, gpuPrice, type GpuLadderRow } from "./model";
4
+ import { truncateToDisplayWidth } from "../../../utils/format";
5
+ import { gpuAxisLabels, gpuBasisColor, gpuBasisLabel, gpuPrice, type GpuAxisLabel, type GpuLadderRow } from "./model";
6
6
 
7
7
  const LABEL_CELLS = 28;
8
8
  const VALUE_CELLS = 17;
@@ -10,14 +10,13 @@ const VALUE_CELLS = 17;
10
10
  const position = (value: number, low: number, high: number) => high > low ? (value - low) / (high - low) : 0.5;
11
11
  const percent = (ratio: number) => `${(Math.max(0, Math.min(1, ratio)) * 100).toFixed(2)}%`;
12
12
 
13
- /** The domain every basis shares, padded to whole axis steps so the ends never touch a band. */
14
- function gpuLadderDomain(rows: readonly GpuLadderRow[]): { low: number; high: number; ticks: number[] } | null {
13
+ /** The domain every basis shares, padded so the ends never touch a band. */
14
+ function gpuLadderDomain(rows: readonly GpuLadderRow[]): { low: number; high: number } | null {
15
15
  if (!rows.length) return null;
16
16
  const min = Math.min(...rows.map((row) => row.min));
17
17
  const max = Math.max(...rows.map((row) => row.max));
18
18
  const pad = Math.max((max - min) * 0.04, max * 0.02, 0.05);
19
- const low = Math.max(0, min - pad), high = max + pad;
20
- return { low, high, ticks: gpuAxisTicks(low, high).filter((tick) => tick >= low && tick <= high) };
19
+ return { low: Math.max(0, min - pad), high: max + pad };
21
20
  }
22
21
 
23
22
  /** Terminal cells of one track: thin rule for the range, heavy for the quartiles, a bar at the median. */
@@ -51,7 +50,10 @@ export function GpuPriceLadder({ rows, width, height, selected }: {
51
50
  const valueCells = wide ? VALUE_CELLS : 6;
52
51
  // Two cells of padding, two gaps, and one spare so the value never clips.
53
52
  const trackCellsWidth = Math.max(8, width - labelCells - valueCells - 5);
54
- const { low, high, ticks } = domain;
53
+ const { low, high } = domain;
54
+ // Labels may hang into the empty label and value columns of the axis row, so the end ticks stay centred.
55
+ const room = { left: labelCells + 1, right: valueCells + 1 };
56
+ const labels = gpuAxisLabels(low, high, trackCellsWidth, room);
55
57
  return (
56
58
  <Box flexDirection="column" width={width} height={shown.length + 1} paddingX={1} data-gloom-role="gpu-price-ladder">
57
59
  {shown.map((row) => {
@@ -91,37 +93,31 @@ export function GpuPriceLadder({ rows, width, height, selected }: {
91
93
  </Box>
92
94
  );
93
95
  })}
94
- <Box flexDirection="row" height={1} gap={1}>
95
- <Box width={labelCells} flexShrink={0} />
96
- <GpuLadderAxis ticks={ticks} low={low} high={high} width={trackCellsWidth} desktop={desktop} />
97
- </Box>
96
+ <GpuLadderAxis labels={labels} width={trackCellsWidth} room={room} desktop={desktop} />
98
97
  </Box>
99
98
  );
100
99
  }
101
100
 
102
- function GpuLadderAxis({ ticks, low, high, width, desktop }: { ticks: number[]; low: number; high: number; width: number; desktop: boolean }) {
101
+ /**
102
+ * Each label centred on its tick, on the scale the bands and markers use. The
103
+ * desktop places real elements by percentage, as it does the markers; the
104
+ * terminal writes the whole axis row so an end label can hang past the track.
105
+ */
106
+ function GpuLadderAxis({ labels, width, room, desktop }: { labels: readonly GpuAxisLabel[]; width: number; room: { left: number; right: number }; desktop: boolean }) {
103
107
  const colors = useThemeColors();
104
- const label = (tick: number) => `$${Number.isInteger(tick) ? tick : tick.toFixed(1)}`;
105
108
  if (desktop) {
106
109
  return (
107
- <Box width={width} height={1} flexShrink={0} style={{ position: "relative" }}>
108
- {ticks.map((tick, index) => {
109
- const ratio = position(tick, low, high);
110
- const align = index === 0 ? "0%" : index === ticks.length - 1 ? "-100%" : "-50%";
111
- return <Box key={tick} style={{ position: "absolute", left: percent(ratio), transform: `translateX(${align})` }}>
112
- <Text fg={colors.textDim}>{label(tick)}</Text>
113
- </Box>;
114
- })}
110
+ <Box flexDirection="row" height={1}>
111
+ <Box width={room.left} flexShrink={0} />
112
+ <Box width={width} height={1} flexShrink={0} style={{ position: "relative" }}>
113
+ {labels.map((label) => <Box key={label.value} style={{ position: "absolute", left: percent(label.ratio), transform: "translateX(-50%)", whiteSpace: "nowrap" }}>
114
+ <Text fg={colors.textDim}>{label.text}</Text>
115
+ </Box>)}
116
+ </Box>
115
117
  </Box>
116
118
  );
117
119
  }
118
- const line = Array.from({ length: width }, () => " ");
119
- for (const [index, tick] of ticks.entries()) {
120
- const text = label(tick);
121
- const center = Math.round(position(tick, low, high) * (width - 1));
122
- const start = index === 0 ? 0 : index === ticks.length - 1 ? width - displayWidth(text) : center - Math.floor(text.length / 2);
123
- if (start < 0 || start + text.length > width || line.slice(Math.max(0, start - 1), start + text.length + 1).some((char) => char !== " ")) continue;
124
- for (let i = 0; i < text.length; i++) line[start + i] = text[i]!;
125
- }
126
- return <Box width={width} height={1} flexShrink={0}><Text fg={colors.textDim}>{line.join("")}</Text></Box>;
120
+ const line = Array.from({ length: room.left + width + room.right }, () => " ");
121
+ for (const label of labels) for (let i = 0; i < label.text.length; i++) line[room.left + label.start + i] = label.text[i]!;
122
+ return <Box height={1} flexShrink={0}><Text fg={colors.textDim}>{line.join("").trimEnd()}</Text></Box>;
127
123
  }
@@ -151,7 +151,7 @@ export function gpuPriceLadder(rows: readonly GpuBoardRow[], model: string): Gpu
151
151
  }
152
152
 
153
153
  /** Round dollar ticks for a price axis: one, two or five times a power of ten, four to six of them. */
154
- export function gpuAxisTicks(low: number, high: number): number[] {
154
+ function gpuAxisTicks(low: number, high: number): number[] {
155
155
  if (!(high > low)) return [low];
156
156
  const raw = (high - low) / 5;
157
157
  const power = 10 ** Math.floor(Math.log10(raw));
@@ -161,6 +161,32 @@ export function gpuAxisTicks(low: number, high: number): number[] {
161
161
  return ticks;
162
162
  }
163
163
 
164
+ export interface GpuAxisLabel { value: number; text: string; ratio: number; start: number }
165
+
166
+ /**
167
+ * Labels for a price axis `cells` wide over `low`..`high`: each centred on its
168
+ * tick's own value, on the linear scale the bands and markers use (`ratio`, and
169
+ * `start`, the first cell on the terminal), with the decimals the tick step
170
+ * needs so neighbours never read alike ($2.0 $2.1 $2.2, $2.05 $2.10). A label
171
+ * that would touch its neighbour or run past the room beside the axis is left
172
+ * out, never moved off its value.
173
+ */
174
+ export function gpuAxisLabels(low: number, high: number, cells: number, room: { left: number; right: number } = { left: 0, right: 0 }): GpuAxisLabel[] {
175
+ const ticks = gpuAxisTicks(low, high).filter((tick) => tick >= low && tick <= high);
176
+ const digits = [0, 1, 2, 3].find((places) => ticks.every((tick) => Math.abs(Number(tick.toFixed(places)) - tick) < 1e-9)) ?? 3;
177
+ const labels: GpuAxisLabel[] = [];
178
+ for (const value of ticks) {
179
+ const text = `$${value.toFixed(digits)}`;
180
+ const ratio = high > low ? (value - low) / (high - low) : 0.5;
181
+ // The cell a track draws this value in, so the label's middle sits on the marker's cell.
182
+ const start = Math.round(ratio * (cells - 1)) - Math.floor(text.length / 2);
183
+ const previous = labels.at(-1);
184
+ if (start < -room.left || start + text.length > cells + room.right || (previous && start <= previous.start + previous.text.length)) continue;
185
+ labels.push({ value, text, ratio, start });
186
+ }
187
+ return labels;
188
+ }
189
+
164
190
  /** Published notices use their effective date; observed changes use the dated evidence. */
165
191
  export const gpuEventDate = (event: Pick<GpuEvent, "origin" | "effectiveAt" | "observedAt">) =>
166
192
  event.origin === "published" ? event.effectiveAt ?? event.observedAt : event.observedAt;