gloomberb 0.16.1 → 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 (574) 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 +26 -8
  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 +2 -0
  8. package/src/api-client/market-discovery.ts +10 -0
  9. package/src/api-client/paths.ts +30 -1
  10. package/src/api-client/perps.ts +37 -0
  11. package/src/api-client/request.ts +5 -2
  12. package/src/api-client/types.ts +2 -0
  13. package/src/brokers/account-cache.ts +24 -0
  14. package/src/cli/commands/command-utils.ts +8 -0
  15. package/src/cli/commands/crypto-hints.ts +75 -0
  16. package/src/cli/commands/market.ts +414 -106
  17. package/src/cli/commands/overview.ts +77 -32
  18. package/src/cli/commands/ticker.ts +101 -43
  19. package/src/cli/desktop-pane-shot.ts +34 -4
  20. package/src/cli/freshness.ts +61 -0
  21. package/src/cli/help.ts +1 -0
  22. package/src/cli/helpers.ts +4 -8
  23. package/src/cli/index.ts +12 -2
  24. package/src/cli/listing-arg.ts +321 -0
  25. package/src/cli/pane-functions/access-gate.ts +42 -0
  26. package/src/cli/pane-functions/catalog.ts +28 -1
  27. package/src/cli/pane-functions/data.ts +3 -1
  28. package/src/cli/pane-functions/dom.ts +20 -2
  29. package/src/cli/pane-functions/freshness.ts +400 -0
  30. package/src/cli/pane-functions/function-help.ts +27 -20
  31. package/src/cli/pane-functions/headless.ts +39 -11
  32. package/src/cli/pane-functions/index.ts +15 -4
  33. package/src/cli/pane-functions/options.ts +3 -0
  34. package/src/cli/pane-functions/report.ts +4 -0
  35. package/src/cli/pane-functions/resolver.ts +35 -7
  36. package/src/cli/pane-functions/screenshot-evidence.ts +2 -0
  37. package/src/cli/pane-functions/screenshot.ts +7 -3
  38. package/src/cli/result.ts +41 -27
  39. package/src/cli/ticker-resolution.ts +7 -5
  40. package/src/components/chart/composite/composite-chart.tsx +88 -2536
  41. package/src/components/chart/composite/drawing-store.ts +59 -0
  42. package/src/components/chart/composite/legend.tsx +319 -0
  43. package/src/components/chart/composite/panel-bitmap.ts +139 -0
  44. package/src/components/chart/composite/panel-gestures.ts +59 -0
  45. package/src/components/chart/composite/panel-labels.ts +107 -0
  46. package/src/components/chart/composite/panel-surface.tsx +876 -0
  47. package/src/components/chart/composite/tool-catalog.ts +98 -0
  48. package/src/components/chart/composite/toolbar.tsx +186 -0
  49. package/src/components/chart/composite/tools.ts +8 -0
  50. package/src/components/chart/composite/use-composite-chart-keyboard.ts +703 -0
  51. package/src/components/chart/composite/use-composite-viewport.ts +143 -0
  52. package/src/components/command-bar/list/model.ts +5 -0
  53. package/src/components/command-bar/list/view.tsx +16 -3
  54. package/src/components/command-bar/pane-templates/workflow.ts +1 -0
  55. package/src/components/command-bar/routes/ticker-search/actions.ts +5 -0
  56. package/src/components/command-bar/workflow/ops.ts +1 -1
  57. package/src/components/command-bar/workflow/tickers.ts +6 -0
  58. package/src/components/data-table/view.tsx +29 -10
  59. package/src/components/feedback-dialog.tsx +2 -1
  60. package/src/components/form-modal/field-row.tsx +2 -2
  61. package/src/components/layout/detached-pane-shell.tsx +6 -3
  62. package/src/components/layout/pane/content.tsx +23 -10
  63. package/src/components/layout/pane/error-boundary.tsx +103 -0
  64. package/src/components/layout/pane/footer/registration.tsx +68 -23
  65. package/src/components/layout/pane/header.tsx +11 -11
  66. package/src/components/layout/shell/menu.ts +2 -1
  67. package/src/components/layout/shell/pane/actions.ts +2 -1
  68. package/src/components/layout/shell/pane/layers.tsx +14 -3
  69. package/src/components/metric-treemap/heat-surface.tsx +43 -16
  70. package/src/components/metric-treemap/heat-tiles.ts +18 -0
  71. package/src/components/metric-treemap/layout.ts +2 -0
  72. package/src/components/onboarding/onboarding-keyboard.ts +311 -0
  73. package/src/components/onboarding/onboarding-wizard.tsx +101 -788
  74. package/src/components/onboarding/use-onboarding-broker-form.ts +263 -0
  75. package/src/components/onboarding/use-onboarding-progress.ts +298 -0
  76. package/src/components/onboarding/use-onboarding-upgrade.ts +83 -0
  77. package/src/components/ticker/list-table-view.tsx +13 -1
  78. package/src/components/ui/fields.tsx +8 -0
  79. package/src/components/ui/range-track.tsx +10 -3
  80. package/src/components/ui/status.tsx +8 -2
  81. package/src/components/ui/tabs.tsx +7 -4
  82. package/src/components/ui/text-prompt-dialog.tsx +4 -2
  83. package/src/data/config/store/normalize.ts +17 -1
  84. package/src/i18n/es.ts +19 -1
  85. package/src/i18n/ja.ts +19 -1
  86. package/src/i18n/ko.ts +19 -1
  87. package/src/i18n/zh-cn.ts +19 -1
  88. package/src/i18n/zh-tw.ts +19 -1
  89. package/src/market-data/coordinator/chart.ts +1 -1
  90. package/src/market-data/coordinator/quotes.ts +23 -4
  91. package/src/market-data/fx-closes.ts +72 -0
  92. package/src/market-data/market/freshness.ts +78 -6
  93. package/src/market-data/market/session-close-note.ts +57 -0
  94. package/src/market-data/market/status.ts +151 -0
  95. package/src/market-data/published-apac-sessions.ts +66 -0
  96. package/src/market-data/quotes/contributions.ts +2 -0
  97. package/src/market-data/quotes/resolution.ts +2 -0
  98. package/src/market-data/quotes/screener-live-quotes.ts +49 -4
  99. package/src/plugins/builtin/account-management/pane.tsx +4 -0
  100. package/src/plugins/builtin/alerts/cli.ts +10 -5
  101. package/src/plugins/builtin/alerts/index.tsx +1 -0
  102. package/src/plugins/builtin/analytics/metrics.ts +8 -3
  103. package/src/plugins/builtin/analytics/risk-client.ts +191 -66
  104. package/src/plugins/builtin/analytics/risk-fx.ts +102 -0
  105. package/src/plugins/builtin/analytics/risk-headless.ts +19 -13
  106. package/src/plugins/builtin/analytics/risk-model.ts +45 -17
  107. package/src/plugins/builtin/analytics/risk-pane.tsx +1 -0
  108. package/src/plugins/builtin/analytics/risk-test-data.ts +25 -1
  109. package/src/plugins/builtin/attention/headless.ts +6 -4
  110. package/src/plugins/builtin/attention/index.tsx +1 -1
  111. package/src/plugins/builtin/attention/pane.tsx +1 -1
  112. package/src/plugins/builtin/awards/headless.ts +1 -0
  113. package/src/plugins/builtin/awards/pane.tsx +1 -1
  114. package/src/plugins/builtin/backtest/headless.ts +1 -0
  115. package/src/plugins/builtin/bond-calculator/headless.ts +1 -0
  116. package/src/plugins/builtin/buildout/index.tsx +1 -0
  117. package/src/plugins/builtin/catalysts/headless.ts +3 -1
  118. package/src/plugins/builtin/cds/headless.ts +2 -0
  119. package/src/plugins/builtin/central-bank-rates/headless.ts +1 -0
  120. package/src/plugins/builtin/chart-composer/headless.ts +38 -3
  121. package/src/plugins/builtin/chart-composer/index.tsx +3 -1
  122. package/src/plugins/builtin/chart-composer/series-expression.ts +18 -2
  123. package/src/plugins/builtin/chat/channels.ts +19 -3
  124. package/src/plugins/builtin/chat/content/composer-runtime.ts +28 -13
  125. package/src/plugins/builtin/chat/content/footer.tsx +15 -6
  126. package/src/plugins/builtin/chat/content/index.tsx +120 -21
  127. package/src/plugins/builtin/chat/content/new-dm-dialog.tsx +261 -113
  128. package/src/plugins/builtin/chat/content/transcript.tsx +20 -2
  129. package/src/plugins/builtin/chat/direct-messages.ts +200 -0
  130. package/src/plugins/builtin/chat/index.tsx +1 -0
  131. package/src/plugins/builtin/chat/message/desktop.tsx +3 -0
  132. package/src/plugins/builtin/chat/message/header.tsx +16 -6
  133. package/src/plugins/builtin/chat/message/inline-tokens.tsx +72 -32
  134. package/src/plugins/builtin/chat/message/profile-popover.tsx +96 -12
  135. package/src/plugins/builtin/chat/message/terminal.tsx +3 -0
  136. package/src/plugins/builtin/chat/message/types.ts +14 -2
  137. package/src/plugins/builtin/chat/profile-popover.ts +18 -13
  138. package/src/plugins/builtin/clinical-trials/index.tsx +1 -0
  139. package/src/plugins/builtin/cloud/askg/executor.ts +2 -1
  140. package/src/plugins/builtin/cloud/plugin.tsx +1 -0
  141. package/src/plugins/builtin/cloud/team/module.tsx +1 -0
  142. package/src/plugins/builtin/cloud/thesis/module.tsx +1 -0
  143. package/src/plugins/builtin/cloud-tweets/registration.ts +1 -0
  144. package/src/plugins/builtin/comment-letters/index.tsx +1 -0
  145. package/src/plugins/builtin/company-attention/headless.ts +5 -1
  146. package/src/plugins/builtin/company-attention/pane.tsx +1 -1
  147. package/src/plugins/builtin/company-kpis/headless.ts +1 -0
  148. package/src/plugins/builtin/congress-trades/headless.ts +1 -0
  149. package/src/plugins/builtin/correlation/compute.ts +59 -16
  150. package/src/plugins/builtin/correlation/geo.ts +34 -0
  151. package/src/plugins/builtin/correlation/headless-schema.ts +4 -1
  152. package/src/plugins/builtin/correlation/headless.ts +42 -8
  153. package/src/plugins/builtin/correlation/history.ts +2 -0
  154. package/src/plugins/builtin/correlation/index.tsx +68 -14
  155. package/src/plugins/builtin/correlation/matrix/model.ts +17 -2
  156. package/src/plugins/builtin/correlation/matrix/selection.ts +5 -3
  157. package/src/plugins/builtin/correlation/relationship/model.ts +1 -1
  158. package/src/plugins/builtin/correlation/relationship/pane.tsx +4 -2
  159. package/src/plugins/builtin/correlation/relationship/view-model.ts +16 -1
  160. package/src/plugins/builtin/correlation/settings.ts +1 -1
  161. package/src/plugins/builtin/cot/headless.ts +6 -3
  162. package/src/plugins/builtin/cot/index.tsx +3 -3
  163. package/src/plugins/builtin/cot/model.ts +36 -3
  164. package/src/plugins/builtin/cpi/headless.ts +4 -0
  165. package/src/plugins/builtin/credit-boards/headless.ts +2 -0
  166. package/src/plugins/builtin/credit-conditions/headless.ts +1 -0
  167. package/src/plugins/builtin/credit-documents/gauge.tsx +5 -8
  168. package/src/plugins/builtin/credit-documents/headless.ts +2 -0
  169. package/src/plugins/builtin/crypto-board/headless.ts +3 -0
  170. package/src/plugins/builtin/custom-view/loader.ts +1 -1
  171. package/src/plugins/builtin/debt-maturities/headless.ts +4 -0
  172. package/src/plugins/builtin/dividend-yield/headless.ts +1 -0
  173. package/src/plugins/builtin/doe/headless.ts +7 -0
  174. package/src/plugins/builtin/doe/range-bar.tsx +7 -37
  175. package/src/plugins/builtin/earnings/headless.ts +5 -0
  176. package/src/plugins/builtin/earnings-calls/headless.ts +1 -0
  177. package/src/plugins/builtin/earnings-ripple/client.ts +91 -18
  178. package/src/plugins/builtin/earnings-ripple/headless.ts +28 -8
  179. package/src/plugins/builtin/earnings-ripple/index.tsx +2 -2
  180. package/src/plugins/builtin/earnings-ripple/model.ts +97 -7
  181. package/src/plugins/builtin/earnings-ripple/pane.tsx +148 -23
  182. package/src/plugins/builtin/econ/index.tsx +1 -0
  183. package/src/plugins/builtin/econ-statistics/headless.ts +2 -0
  184. package/src/plugins/builtin/equity-screener/headless.ts +2 -0
  185. package/src/plugins/builtin/estimate-revisions/headless.ts +1 -0
  186. package/src/plugins/builtin/executives/index.tsx +1 -0
  187. package/src/plugins/builtin/exposure/headless.ts +3 -1
  188. package/src/plugins/builtin/fear-greed/headless.ts +2 -0
  189. package/src/plugins/builtin/filing-events/index.tsx +1 -0
  190. package/src/plugins/builtin/futures-curve/client.ts +12 -3
  191. package/src/plugins/builtin/futures-curve/headless.ts +39 -10
  192. package/src/plugins/builtin/futures-curve/index.tsx +3 -3
  193. package/src/plugins/builtin/futures-curve/model.ts +188 -4
  194. package/src/plugins/builtin/futures-curve/pane.tsx +51 -9
  195. package/src/plugins/builtin/gpu/headless.ts +7 -3
  196. package/src/plugins/builtin/gpu/ladder.tsx +26 -30
  197. package/src/plugins/builtin/gpu/model.ts +27 -1
  198. package/src/plugins/builtin/holders/beneficial-client.ts +209 -0
  199. package/src/plugins/builtin/holders/beneficial-model.ts +279 -0
  200. package/src/plugins/builtin/holders/beneficial-report.ts +126 -0
  201. package/src/plugins/builtin/holders/beneficial-view.tsx +241 -0
  202. package/src/plugins/builtin/holders/headless.ts +139 -5
  203. package/src/plugins/builtin/holders/index.tsx +10 -3
  204. package/src/plugins/builtin/holders/pane.tsx +20 -3
  205. package/src/plugins/builtin/holders/report-header.ts +95 -0
  206. package/src/plugins/builtin/holders/table-model.ts +20 -5
  207. package/src/plugins/builtin/holders/thirteenf-match.ts +29 -1
  208. package/src/plugins/builtin/holders/types.ts +2 -1
  209. package/src/plugins/builtin/insider/headless.ts +2 -0
  210. package/src/plugins/builtin/ipo-calendar/headless.ts +1 -0
  211. package/src/plugins/builtin/iv-history/headless.ts +4 -1
  212. package/src/plugins/builtin/jobs/headless.ts +1 -0
  213. package/src/plugins/builtin/kelly-sizer/index.tsx +1 -0
  214. package/src/plugins/builtin/macro-day/headless.ts +2 -0
  215. package/src/plugins/builtin/market-halts/index.tsx +1 -0
  216. package/src/plugins/builtin/market-heatmap/data.ts +2 -0
  217. package/src/plugins/builtin/market-heatmap/headless.ts +135 -0
  218. package/src/plugins/builtin/market-heatmap/index.tsx +39 -46
  219. package/src/plugins/builtin/market-heatmap/model.ts +146 -0
  220. package/src/plugins/builtin/market-heatmap/portfolio.ts +23 -4
  221. package/src/plugins/builtin/market-movers/headless.ts +2 -0
  222. package/src/plugins/builtin/market-valuation/headless.ts +2 -0
  223. package/src/plugins/builtin/members/client.ts +20 -3
  224. package/src/plugins/builtin/members/headless.ts +5 -3
  225. package/src/plugins/builtin/members/model.ts +10 -2
  226. package/src/plugins/builtin/members/pane.tsx +13 -6
  227. package/src/plugins/builtin/mna/distress/headless.ts +1 -0
  228. package/src/plugins/builtin/mna/headless.ts +1 -0
  229. package/src/plugins/builtin/money-markets/headless.ts +1 -0
  230. package/src/plugins/builtin/news/headless.ts +2 -0
  231. package/src/plugins/builtin/news/wire/index.tsx +8 -1
  232. package/src/plugins/builtin/notes/cli.ts +14 -6
  233. package/src/plugins/builtin/notes/index.tsx +1 -0
  234. package/src/plugins/builtin/openfda/index.tsx +1 -0
  235. package/src/plugins/builtin/options-calculator/headless.ts +2 -0
  236. package/src/plugins/builtin/options-positioning/headless.ts +3 -0
  237. package/src/plugins/builtin/options-scenario/headless.ts +5 -0
  238. package/src/plugins/builtin/pe-band/client.ts +48 -3
  239. package/src/plugins/builtin/pe-band/headless.ts +25 -7
  240. package/src/plugins/builtin/pe-band/model.ts +92 -16
  241. package/src/plugins/builtin/pe-band/pane.tsx +33 -16
  242. package/src/plugins/builtin/perps/client.ts +50 -3
  243. package/src/plugins/builtin/perps/headless.ts +144 -16
  244. package/src/plugins/builtin/perps/index.tsx +6 -6
  245. package/src/plugins/builtin/perps/model.ts +189 -12
  246. package/src/plugins/builtin/perps/pane.tsx +248 -47
  247. package/src/plugins/builtin/portfolio-list/allocation.ts +245 -0
  248. package/src/plugins/builtin/portfolio-list/cached-account.ts +33 -0
  249. package/src/plugins/builtin/portfolio-list/cell-version.ts +19 -1
  250. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +328 -29
  251. package/src/plugins/builtin/portfolio-list/cli/render.ts +230 -39
  252. package/src/plugins/builtin/portfolio-list/cli/shared.ts +67 -2
  253. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +35 -31
  254. package/src/plugins/builtin/portfolio-list/column-values.ts +124 -47
  255. package/src/plugins/builtin/portfolio-list/headless.ts +98 -16
  256. package/src/plugins/builtin/portfolio-list/index.tsx +14 -3
  257. package/src/plugins/builtin/portfolio-list/live-valuation.ts +7 -5
  258. package/src/plugins/builtin/portfolio-list/mutations.ts +55 -1
  259. package/src/plugins/builtin/portfolio-list/pane/data.ts +6 -0
  260. package/src/plugins/builtin/portfolio-list/pane/index.tsx +47 -13
  261. package/src/plugins/builtin/portfolio-list/settings.ts +33 -2
  262. package/src/plugins/builtin/portfolio-list/summary/index.tsx +24 -1
  263. package/src/plugins/builtin/portfolio-list/summary/totals.ts +26 -7
  264. package/src/plugins/builtin/power/headless.ts +3 -1
  265. package/src/plugins/builtin/rate-path/headless.ts +2 -0
  266. package/src/plugins/builtin/realized-vol/headless.ts +2 -0
  267. package/src/plugins/builtin/relative-rotation/headless.ts +2 -0
  268. package/src/plugins/builtin/research/analyst-headless.ts +3 -0
  269. package/src/plugins/builtin/research/events-headless.ts +2 -0
  270. package/src/plugins/builtin/research/headless.ts +2 -0
  271. package/src/plugins/builtin/research/index.tsx +1 -0
  272. package/src/plugins/builtin/research/relative-valuation-headless.ts +10 -5
  273. package/src/plugins/builtin/research/relative-valuation-model.ts +11 -3
  274. package/src/plugins/builtin/research/relative-valuation-pane.tsx +3 -3
  275. package/src/plugins/builtin/research-search/index.tsx +1 -0
  276. package/src/plugins/builtin/revenue-breakdown/headless.ts +2 -0
  277. package/src/plugins/builtin/reverse-dcf/headless.ts +2 -0
  278. package/src/plugins/builtin/risk-factors/headless.ts +2 -0
  279. package/src/plugins/builtin/scanner/flow-history.ts +2 -13
  280. package/src/plugins/builtin/scanner/headless.ts +14 -0
  281. package/src/plugins/builtin/seasonality/headless.ts +2 -0
  282. package/src/plugins/builtin/sec/command-bar-search.ts +14 -4
  283. package/src/plugins/builtin/sec/filing-content.ts +42 -17
  284. package/src/plugins/builtin/sec/forms.ts +12 -0
  285. package/src/plugins/builtin/sec/headless.ts +2 -0
  286. package/src/plugins/builtin/sectors/client.ts +1 -0
  287. package/src/plugins/builtin/sectors/headless.ts +9 -1
  288. package/src/plugins/builtin/sectors/sector-model.ts +2 -0
  289. package/src/plugins/builtin/shared/crypto-pair.ts +14 -0
  290. package/src/plugins/builtin/shared/locked-rows.tsx +1 -1
  291. package/src/plugins/builtin/shared/report-freshness.ts +57 -0
  292. package/src/plugins/builtin/shared/research-cells.tsx +4 -9
  293. package/src/plugins/builtin/short-interest/headless.ts +2 -0
  294. package/src/plugins/builtin/short-interest/watch-headless.ts +1 -0
  295. package/src/plugins/builtin/short-volume/headless.ts +1 -0
  296. package/src/plugins/builtin/social-mentions/headless.ts +1 -0
  297. package/src/plugins/builtin/supply-chain/headless.ts +7 -1
  298. package/src/plugins/builtin/supply-chain/index.tsx +10 -1
  299. package/src/plugins/builtin/supply-chain/model.ts +27 -2
  300. package/src/plugins/builtin/supply-chain/pane.tsx +71 -32
  301. package/src/plugins/builtin/themes/headless.ts +3 -0
  302. package/src/plugins/builtin/thirteenf/headless.ts +110 -25
  303. package/src/plugins/builtin/thirteenf/report-notes.ts +149 -0
  304. package/src/plugins/builtin/ticker-detail/headless.ts +7 -1
  305. package/src/plugins/builtin/ticker-detail/index.tsx +1 -0
  306. package/src/plugins/builtin/ticker-detail/overview/model.ts +9 -2
  307. package/src/plugins/builtin/ticker-detail/overview-tab.tsx +26 -26
  308. package/src/plugins/builtin/time-sales/headless.ts +5 -1
  309. package/src/plugins/builtin/treasury-auctions/headless.ts +1 -0
  310. package/src/plugins/builtin/volatility/headless.ts +6 -1
  311. package/src/plugins/builtin/world-indices/headless.ts +2 -0
  312. package/src/plugins/builtin/world-venue-map/basemap-layer.tsx +157 -0
  313. package/src/plugins/builtin/world-venue-map/basemap.ts +214 -0
  314. package/src/plugins/builtin/world-venue-map/client.ts +45 -0
  315. package/src/plugins/builtin/world-venue-map/entity-detail.tsx +147 -0
  316. package/src/plugins/builtin/world-venue-map/entity-table.ts +59 -0
  317. package/src/plugins/builtin/world-venue-map/evidence.ts +77 -0
  318. package/src/plugins/builtin/world-venue-map/feed.ts +293 -0
  319. package/src/plugins/builtin/world-venue-map/geo-draw.tsx +248 -0
  320. package/src/plugins/builtin/world-venue-map/geo-series.ts +142 -0
  321. package/src/plugins/builtin/world-venue-map/geo-svg.tsx +447 -0
  322. package/src/plugins/builtin/world-venue-map/headless.ts +146 -0
  323. package/src/plugins/builtin/world-venue-map/ids.ts +1 -0
  324. package/src/plugins/builtin/world-venue-map/index.ts +67 -4
  325. package/src/plugins/builtin/world-venue-map/layered-pane.tsx +552 -0
  326. package/src/plugins/builtin/world-venue-map/layers.ts +348 -0
  327. package/src/plugins/builtin/world-venue-map/map-symbols.ts +220 -0
  328. package/src/plugins/builtin/world-venue-map/map.tsx +381 -45
  329. package/src/plugins/builtin/world-venue-map/model.ts +7 -4
  330. package/src/plugins/builtin/world-venue-map/natural-earth/index.chunk.ts +199 -0
  331. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n00.chunk.ts +4 -0
  332. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n15.chunk.ts +4 -0
  333. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n30.chunk.ts +4 -0
  334. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n45.chunk.ts +4 -0
  335. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n60.chunk.ts +4 -0
  336. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000n75.chunk.ts +4 -0
  337. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s15.chunk.ts +4 -0
  338. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s30.chunk.ts +4 -0
  339. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e000s60.chunk.ts +4 -0
  340. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n00.chunk.ts +4 -0
  341. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n15.chunk.ts +4 -0
  342. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n30.chunk.ts +4 -0
  343. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n45.chunk.ts +4 -0
  344. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n60.chunk.ts +4 -0
  345. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015n75.chunk.ts +4 -0
  346. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s15.chunk.ts +4 -0
  347. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s30.chunk.ts +4 -0
  348. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e015s45.chunk.ts +4 -0
  349. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n00.chunk.ts +4 -0
  350. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n15.chunk.ts +4 -0
  351. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n30.chunk.ts +4 -0
  352. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n45.chunk.ts +4 -0
  353. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n60.chunk.ts +4 -0
  354. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030n75.chunk.ts +4 -0
  355. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s15.chunk.ts +4 -0
  356. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s30.chunk.ts +4 -0
  357. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s45.chunk.ts +4 -0
  358. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e030s60.chunk.ts +4 -0
  359. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n00.chunk.ts +4 -0
  360. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n15.chunk.ts +4 -0
  361. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n30.chunk.ts +4 -0
  362. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n45.chunk.ts +4 -0
  363. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n60.chunk.ts +4 -0
  364. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045n75.chunk.ts +4 -0
  365. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s15.chunk.ts +4 -0
  366. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s30.chunk.ts +4 -0
  367. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e045s60.chunk.ts +4 -0
  368. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n00.chunk.ts +4 -0
  369. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n15.chunk.ts +4 -0
  370. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n30.chunk.ts +4 -0
  371. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n45.chunk.ts +4 -0
  372. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n60.chunk.ts +4 -0
  373. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060n75.chunk.ts +4 -0
  374. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s15.chunk.ts +4 -0
  375. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s30.chunk.ts +4 -0
  376. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e060s60.chunk.ts +4 -0
  377. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n00.chunk.ts +4 -0
  378. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n15.chunk.ts +4 -0
  379. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n30.chunk.ts +4 -0
  380. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n45.chunk.ts +4 -0
  381. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n60.chunk.ts +4 -0
  382. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075n75.chunk.ts +4 -0
  383. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e075s45.chunk.ts +4 -0
  384. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n00.chunk.ts +4 -0
  385. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n15.chunk.ts +4 -0
  386. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n30.chunk.ts +4 -0
  387. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n45.chunk.ts +4 -0
  388. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n60.chunk.ts +4 -0
  389. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090n75.chunk.ts +4 -0
  390. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e090s15.chunk.ts +4 -0
  391. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n00.chunk.ts +4 -0
  392. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n15.chunk.ts +4 -0
  393. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n30.chunk.ts +4 -0
  394. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n45.chunk.ts +4 -0
  395. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n60.chunk.ts +4 -0
  396. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105n75.chunk.ts +4 -0
  397. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s15.chunk.ts +4 -0
  398. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s30.chunk.ts +4 -0
  399. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e105s45.chunk.ts +4 -0
  400. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n00.chunk.ts +4 -0
  401. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n15.chunk.ts +4 -0
  402. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n30.chunk.ts +4 -0
  403. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n45.chunk.ts +4 -0
  404. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120n60.chunk.ts +4 -0
  405. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s15.chunk.ts +4 -0
  406. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s30.chunk.ts +4 -0
  407. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e120s45.chunk.ts +4 -0
  408. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n00.chunk.ts +4 -0
  409. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n15.chunk.ts +4 -0
  410. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n30.chunk.ts +4 -0
  411. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n45.chunk.ts +4 -0
  412. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n60.chunk.ts +4 -0
  413. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135n75.chunk.ts +4 -0
  414. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s15.chunk.ts +4 -0
  415. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s30.chunk.ts +4 -0
  416. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e135s45.chunk.ts +4 -0
  417. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n00.chunk.ts +4 -0
  418. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n15.chunk.ts +4 -0
  419. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n45.chunk.ts +4 -0
  420. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n60.chunk.ts +4 -0
  421. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150n75.chunk.ts +4 -0
  422. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s15.chunk.ts +4 -0
  423. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s30.chunk.ts +4 -0
  424. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s45.chunk.ts +4 -0
  425. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e150s60.chunk.ts +4 -0
  426. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n00.chunk.ts +4 -0
  427. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n15.chunk.ts +4 -0
  428. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n45.chunk.ts +4 -0
  429. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165n60.chunk.ts +4 -0
  430. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s15.chunk.ts +4 -0
  431. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s30.chunk.ts +4 -0
  432. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s45.chunk.ts +4 -0
  433. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/e165s60.chunk.ts +4 -0
  434. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n00.chunk.ts +4 -0
  435. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n15.chunk.ts +4 -0
  436. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n30.chunk.ts +4 -0
  437. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n45.chunk.ts +4 -0
  438. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n60.chunk.ts +4 -0
  439. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015n75.chunk.ts +4 -0
  440. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s15.chunk.ts +4 -0
  441. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s30.chunk.ts +4 -0
  442. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w015s45.chunk.ts +4 -0
  443. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n00.chunk.ts +4 -0
  444. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n15.chunk.ts +4 -0
  445. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n30.chunk.ts +4 -0
  446. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n60.chunk.ts +4 -0
  447. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030n75.chunk.ts +4 -0
  448. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s30.chunk.ts +4 -0
  449. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w030s60.chunk.ts +4 -0
  450. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n30.chunk.ts +4 -0
  451. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n45.chunk.ts +4 -0
  452. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n60.chunk.ts +4 -0
  453. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045n75.chunk.ts +4 -0
  454. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s15.chunk.ts +4 -0
  455. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s30.chunk.ts +4 -0
  456. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w045s60.chunk.ts +4 -0
  457. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n00.chunk.ts +4 -0
  458. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n30.chunk.ts +4 -0
  459. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n45.chunk.ts +4 -0
  460. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n60.chunk.ts +4 -0
  461. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060n75.chunk.ts +4 -0
  462. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s15.chunk.ts +4 -0
  463. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s30.chunk.ts +4 -0
  464. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s45.chunk.ts +4 -0
  465. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w060s60.chunk.ts +4 -0
  466. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n00.chunk.ts +4 -0
  467. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n15.chunk.ts +4 -0
  468. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n30.chunk.ts +4 -0
  469. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n45.chunk.ts +4 -0
  470. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n60.chunk.ts +4 -0
  471. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075n75.chunk.ts +4 -0
  472. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s15.chunk.ts +4 -0
  473. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s30.chunk.ts +4 -0
  474. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s45.chunk.ts +4 -0
  475. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w075s60.chunk.ts +4 -0
  476. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n00.chunk.ts +4 -0
  477. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n15.chunk.ts +4 -0
  478. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n30.chunk.ts +4 -0
  479. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n45.chunk.ts +4 -0
  480. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n60.chunk.ts +4 -0
  481. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090n75.chunk.ts +4 -0
  482. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s15.chunk.ts +4 -0
  483. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s30.chunk.ts +4 -0
  484. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s45.chunk.ts +4 -0
  485. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w090s60.chunk.ts +4 -0
  486. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n00.chunk.ts +4 -0
  487. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n15.chunk.ts +4 -0
  488. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n30.chunk.ts +4 -0
  489. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n45.chunk.ts +4 -0
  490. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n60.chunk.ts +4 -0
  491. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105n75.chunk.ts +4 -0
  492. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w105s15.chunk.ts +4 -0
  493. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n00.chunk.ts +4 -0
  494. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n15.chunk.ts +4 -0
  495. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n30.chunk.ts +4 -0
  496. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n45.chunk.ts +4 -0
  497. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n60.chunk.ts +4 -0
  498. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120n75.chunk.ts +4 -0
  499. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w120s30.chunk.ts +4 -0
  500. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n30.chunk.ts +4 -0
  501. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n45.chunk.ts +4 -0
  502. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n60.chunk.ts +4 -0
  503. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135n75.chunk.ts +4 -0
  504. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w135s30.chunk.ts +4 -0
  505. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n45.chunk.ts +4 -0
  506. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150n60.chunk.ts +4 -0
  507. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s15.chunk.ts +4 -0
  508. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w150s30.chunk.ts +4 -0
  509. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n00.chunk.ts +4 -0
  510. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n15.chunk.ts +4 -0
  511. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n45.chunk.ts +4 -0
  512. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165n60.chunk.ts +4 -0
  513. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s15.chunk.ts +4 -0
  514. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w165s30.chunk.ts +4 -0
  515. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n00.chunk.ts +4 -0
  516. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n15.chunk.ts +4 -0
  517. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n45.chunk.ts +4 -0
  518. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180n60.chunk.ts +4 -0
  519. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s15.chunk.ts +4 -0
  520. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s30.chunk.ts +4 -0
  521. package/src/plugins/builtin/world-venue-map/natural-earth/tiles/w180s45.chunk.ts +4 -0
  522. package/src/plugins/builtin/world-venue-map/natural-earth/world-50m.chunk.ts +4 -0
  523. package/src/plugins/builtin/world-venue-map/pane.tsx +28 -43
  524. package/src/plugins/builtin/world-venue-map/use-geo.ts +55 -0
  525. package/src/plugins/builtin/world-venue-map/venue-header.tsx +32 -0
  526. package/src/plugins/builtin/yield-curve/headless.ts +16 -0
  527. package/src/plugins/loader.ts +13 -1
  528. package/src/plugins/registry/pane-settings.ts +1 -0
  529. package/src/renderers/dom/build-assets.ts +81 -3
  530. package/src/renderers/dom/data-table/row.tsx +2 -0
  531. package/src/renderers/dom/desktop/controls.tsx +7 -3
  532. package/src/renderers/dom/desktop/list-view.tsx +35 -10
  533. package/src/renderers/dom/desktop/popover.tsx +73 -14
  534. package/src/renderers/dom/host/input.tsx +14 -3
  535. package/src/renderers/dom/styles.css +6 -7
  536. package/src/renderers/electrobun/view/backend-rpc.ts +10 -4
  537. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +1 -0
  538. package/src/renderers/electrobun/view/rpc-timeout.ts +14 -3
  539. package/src/renderers/opentui/host.tsx +5 -0
  540. package/src/sources/gloomberb-cloud/index.ts +25 -4
  541. package/src/sources/gloomberb-cloud/normalizers.ts +27 -3
  542. package/src/sources/provider-router/cached-routes.ts +4 -1
  543. package/src/sources/provider-router/financials.ts +8 -7
  544. package/src/sources/provider-router/history.ts +15 -10
  545. package/src/sources/sec-registrant.ts +75 -0
  546. package/src/state/app/context/index.tsx +24 -8
  547. package/src/state/hooks/quote-streaming.ts +41 -26
  548. package/src/sync/core-contributors.ts +4 -0
  549. package/src/telemetry/crash-reports.ts +4 -1
  550. package/src/test-support/cli-context.ts +10 -2
  551. package/src/theme/heat-colors.ts +266 -84
  552. package/src/time-series/resolve.ts +1 -1
  553. package/src/time-series/use-chart-resolution.ts +1 -1
  554. package/src/types/context-menu.ts +6 -0
  555. package/src/types/data-provider.ts +2 -0
  556. package/src/types/financials.ts +3 -0
  557. package/src/types/headless.ts +79 -2
  558. package/src/types/plugin.ts +20 -1
  559. package/src/types/ticker.ts +15 -0
  560. package/src/ui/host.tsx +14 -0
  561. package/src/updater/index.ts +9 -11
  562. package/src/utils/async-deadline.ts +21 -0
  563. package/src/utils/cli-output.ts +5 -1
  564. package/src/utils/crypto-pair.ts +41 -0
  565. package/src/utils/currency-units.ts +14 -0
  566. package/src/utils/depositary-receipt.ts +26 -0
  567. package/src/utils/exchanges.ts +72 -0
  568. package/src/utils/fire-and-forget.ts +24 -0
  569. package/src/utils/format.ts +13 -0
  570. package/src/utils/price-earnings.ts +70 -3
  571. package/src/utils/price-history.ts +58 -13
  572. package/src/utils/rpc-timeout-error.ts +27 -0
  573. package/src/utils/sec.ts +13 -1
  574. package/src/utils/utc-time.ts +79 -0
@@ -85,6 +85,7 @@ export const tickerDetailModule: PluginModule = {
85
85
  },
86
86
  {
87
87
  id: "returns",
88
+ reportFreshness: { status: "not-a-feed", basis: "price history" },
88
89
  name: "Returns",
89
90
  icon: "R",
90
91
  component: ReturnsPane,
@@ -72,6 +72,7 @@ export function buildOverviewStats({
72
72
  baseCurrency,
73
73
  marketCapExchangeRates = new Map(),
74
74
  nextEarnings,
75
+ depositaryReceipt = false,
75
76
  today,
76
77
  }: {
77
78
  quote: Quote | undefined;
@@ -81,6 +82,8 @@ export function buildOverviewStats({
81
82
  toBase: CurrencyConverter;
82
83
  marketCapExchangeRates?: ReadonlyMap<string, number>;
83
84
  nextEarnings?: NextEarnings;
85
+ /** The listing is a depositary receipt, so its share count is in receipts. */
86
+ depositaryReceipt?: boolean;
84
87
  /** The listing's calendar day, YYYY-MM-DD; dates before it are left out. */
85
88
  today?: string;
86
89
  }): StatField[] {
@@ -105,7 +108,11 @@ export function buildOverviewStats({
105
108
  });
106
109
  }
107
110
  if (fundamentals?.sharesOutstanding) {
108
- stats.push({ label: "Shares Out", value: formatCompact(fundamentals.sharesOutstanding) });
111
+ stats.push({
112
+ label: "Shares Out",
113
+ value: formatCompact(fundamentals.sharesOutstanding),
114
+ ...(depositaryReceipt ? { detail: ["ADR equivalent", "in ADRs"] } : {}),
115
+ });
109
116
  }
110
117
  if (finite(fundamentals?.floatShares) && fundamentals.floatShares > 0) {
111
118
  stats.push({ label: "Float", value: formatCompact(fundamentals.floatShares) });
@@ -122,7 +129,7 @@ export function buildOverviewStats({
122
129
  stats.push({ label: "EPS", value: money(fundamentals.eps, true) });
123
130
  }
124
131
  if (fundamentals?.pegRatio != null) {
125
- stats.push({ label: "PEG", value: formatNumber(fundamentals.pegRatio, 2) });
132
+ stats.push({ label: "PEG", value: formatPriceEarnings(fundamentals.pegRatio, 2) });
126
133
  }
127
134
  // A report the payload still lists after its day has passed is not the next one.
128
135
  if (today && nextEarnings && ISO_DATE.test(nextEarnings.date) && nextEarnings.date >= today) {
@@ -8,7 +8,13 @@ import { PriceReturnStrip } from "../../../components/price-performance";
8
8
  import { t } from "../../../i18n";
9
9
  import { useFxRatesMap } from "../../../market-data/hooks";
10
10
  import { formatMarketPriceWithCurrency, formatSignedMarketPrice, liveQuoteFormatOptions } from "../../../market-data/market/format";
11
- import { exchangeShortName, marketStateColor, marketStateLabel } from "../../../market-data/market/status";
11
+ import {
12
+ exchangeShortName,
13
+ getExtendedSessionDisplay,
14
+ getRegularSessionDisplay,
15
+ marketStateColor,
16
+ marketStateLabel,
17
+ } from "../../../market-data/market/status";
12
18
  import { appendQuoteToPriceReturnHistory, buildPriceReturnFields } from "../../../market-data/performance";
13
19
  import { useViewport } from "../../../react/input";
14
20
  import { useAppSelector } from "../../../state/app/context";
@@ -20,6 +26,7 @@ import { Box, ScrollBox, Text, useUiCapabilities } from "../../../ui";
20
26
  import { publicTickerKey, resolveExchangeTimeZone } from "../../../utils/exchanges";
21
27
  import { convertCurrency, displayWidth, formatPercentRaw, truncateToDisplayWidth } from "../../../utils/format";
22
28
  import { zonedDateKey } from "../../../utils/zoned-date-time";
29
+ import { isDepositaryReceipt } from "../../../utils/depositary-receipt";
23
30
  import {
24
31
  CompactRangeBar,
25
32
  FundamentalsGrid,
@@ -131,9 +138,12 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
131
138
  const quoteSummaryWidth = quoteBookInline ? Math.max(20, contentWidth - quoteBookWidth - 2) : contentWidth;
132
139
  // Price, change and ranges keep the instrument's decimals on every streamed tick.
133
140
  const moneyOptions = liveQuoteFormatOptions(quote, quote?.currency, ticker.metadata.assetCategory, financials?.quoteMetadata?.instrumentType);
134
- const quotePriceText = quote ? formatMarketPriceWithCurrency(quote.price, quote.currency, moneyOptions) : "";
135
- const quoteChangeText = quote ? formatSignedMarketPrice(quote.change, moneyOptions) : "";
136
- const quotePercentText = quote ? `(${formatPercentRaw(quote.changePercent)})` : "";
141
+ // The headline is the regular session, which stops at the close; extended trading is the line below it.
142
+ const sessionQuote = getRegularSessionDisplay(quote);
143
+ const extendedQuote = getExtendedSessionDisplay(quote);
144
+ const quotePriceText = sessionQuote ? formatMarketPriceWithCurrency(sessionQuote.price, quote?.currency, moneyOptions) : "";
145
+ const quoteChangeText = sessionQuote ? formatSignedMarketPrice(sessionQuote.change, moneyOptions) : "";
146
+ const quotePercentText = sessionQuote ? `(${formatPercentRaw(sessionQuote.changePercent)})` : "";
137
147
  // The pane title already names the ticker, so the line leads with the company and drops a name that only repeats it.
138
148
  const companyName = [ticker.metadata.name, quote?.name].find((name) => name && name !== ticker.metadata.ticker) ?? "";
139
149
  const venueText = listingVenue ? (companyName ? ` (${listingVenue})` : listingVenue) : "";
@@ -184,6 +194,7 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
184
194
  toBase,
185
195
  marketCapExchangeRates: exchangeRates,
186
196
  nextEarnings: financials?.nextEarnings,
197
+ depositaryReceipt: isDepositaryReceipt({ ...quote, description }),
187
198
  // Report and ex-dividend dates are the listing's calendar days.
188
199
  today: zonedDateKey(Date.now(), chartTimeZone ?? "America/New_York"),
189
200
  });
@@ -241,7 +252,7 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
241
252
  </Box>
242
253
  )}
243
254
 
244
- {quote && (
255
+ {sessionQuote && (
245
256
  <Box
246
257
  flexDirection={stackQuoteSummary ? "column" : "row"}
247
258
  gap={stackQuoteSummary ? 0 : 2}
@@ -249,30 +260,19 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
249
260
  >
250
261
  <FigureText>{quotePriceText}</FigureText>
251
262
  <Box flexDirection={stackQuoteChange ? "column" : "row"} gap={stackQuoteChange ? 0 : 1}>
252
- <FigureText part="sub" change={quote.change}>{quoteChangeText}</FigureText>
253
- <FigureText part="sub" change={quote.change}>{quotePercentText}</FigureText>
263
+ <FigureText part="sub" change={sessionQuote.change}>{quoteChangeText}</FigureText>
264
+ <FigureText part="sub" change={sessionQuote.change}>{quotePercentText}</FigureText>
254
265
  </Box>
255
266
  </Box>
256
267
  )}
257
- {quote && (quote.marketState === "PRE" || quote.marketState === "PREPRE") && quote.preMarketPrice != null && (
258
- <Box flexDirection="row" gap={2}>
259
- <Text fg={colors.textDim}>{t("Pre-Market")}:</Text>
260
- <Text fg={priceColor(quote.preMarketChange ?? 0)}>
261
- {formatMarketPriceWithCurrency(quote.preMarketPrice, quote.currency, moneyOptions)}
262
- </Text>
263
- <Text fg={priceColor(quote.preMarketChange ?? 0)}>
264
- {formatSignedMarketPrice(quote.preMarketChange, moneyOptions)} ({formatPercentRaw(quote.preMarketChangePercent)})
265
- </Text>
266
- </Box>
267
- )}
268
- {quote && (quote.marketState === "POST" || quote.marketState === "POSTPOST") && quote.postMarketPrice != null && (
268
+ {extendedQuote && (
269
269
  <Box flexDirection="row" gap={2}>
270
- <Text fg={colors.textDim}>{t("After-Hours")}:</Text>
271
- <Text fg={priceColor(quote.postMarketChange ?? 0)}>
272
- {formatMarketPriceWithCurrency(quote.postMarketPrice, quote.currency, moneyOptions)}
270
+ <Text fg={colors.textDim}>{extendedQuote.session === "PRE" ? t("Pre-Market") : t("After-Hours")}:</Text>
271
+ <Text fg={priceColor(extendedQuote.change ?? 0)}>
272
+ {formatMarketPriceWithCurrency(extendedQuote.price, quote?.currency, moneyOptions)}
273
273
  </Text>
274
- <Text fg={priceColor(quote.postMarketChange ?? 0)}>
275
- {formatSignedMarketPrice(quote.postMarketChange, moneyOptions)} ({formatPercentRaw(quote.postMarketChangePercent)})
274
+ <Text fg={priceColor(extendedQuote.change ?? 0)}>
275
+ {formatSignedMarketPrice(extendedQuote.change, moneyOptions)} ({formatPercentRaw(extendedQuote.changePercent)})
276
276
  </Text>
277
277
  </Box>
278
278
  )}
@@ -293,7 +293,7 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
293
293
  <Box flexDirection={rangeInline ? "row" : "column"} gap={rangeInline ? RANGE_PAIR_GAP : 0} width={contentWidth}>
294
294
  {hasDayRange && (
295
295
  <CompactRangeBar
296
- current={quote.price}
296
+ current={sessionQuote?.price ?? quote.price}
297
297
  low={quote.low!}
298
298
  high={quote.high!}
299
299
  label={dayRangeLabel}
@@ -307,7 +307,7 @@ function ResolvedOverviewTab({ width, focused = false, ticker, financials, onOpe
307
307
  )}
308
308
  {yearRange && (
309
309
  <CompactRangeBar
310
- current={quote.price}
310
+ current={sessionQuote?.price ?? quote.price}
311
311
  low={yearRange.low}
312
312
  high={yearRange.high}
313
313
  label={yearRangeLabel}
@@ -29,7 +29,11 @@ export const timeSalesHeadless: HeadlessPaneDefinition<"bundle"> = {
29
29
  // One decimal count per column, as the pane shows it, so sub-penny prints align.
30
30
  const price = fixedPrice(tapePriceDigits(data.trades.map((row) => row.price)));
31
31
  const trades = newestFirst(data.trades).slice(0, Number(args.options.limit ?? 100)).map((row) => ({ ...row, conditions: row.conditions.join(" ") }));
32
- return { sections: [
32
+ // SIP prints, real-time or fifteen minutes delayed by entitlement, dated by the newest print.
33
+ const freshness = data.feed === "sip"
34
+ ? { status: "live" as const, cadence: "daily" as const }
35
+ : { status: "delayed" as const, delayMinutes: Math.round(data.delaySeconds / 60), cadence: "daily" as const };
36
+ return { freshness, sections: [
33
37
  { title: "Observed window", columns: formattedColumns(window, { vwap: price, low: price, high: price }), rows: window },
34
38
  { title: "Regular session", columns: formattedColumns([data.session], { high: price, low: price }), rows: [data.session] },
35
39
  { title: "Trades", columns: formattedColumns(trades, { price }), rows: trades },
@@ -40,6 +40,7 @@ function createTreasuryAuctionsHeadless(
40
40
  ): HeadlessPaneDefinition<"rows"> {
41
41
  return {
42
42
  shape: "rows",
43
+ freshness: { source: "US Treasury", status: "not-a-feed", basis: "auction results", observedKey: "auctionDate", oldest: null },
43
44
  argument: {
44
45
  kind: "free-text",
45
46
  placeholder: "search",
@@ -1,6 +1,7 @@
1
1
  import type { HeadlessBundleResult, HeadlessPaneContext, HeadlessPaneDefinition, HeadlessPaneLoadArgs } from "../../../types/plugin";
2
2
  import { createVolatilityDependencies, loadVolatilityData, type VolatilityLoadResult } from "./client";
3
3
  import type { VolatilityData } from "./model";
4
+ import { newestReportTime, oldestReportTime } from "../../../utils/utc-time";
4
5
 
5
6
  function formattedValue(value: unknown): string { return typeof value === "number" && Number.isFinite(value) ? value.toFixed(2) : "--"; }
6
7
  function formattedPercentile(value: unknown): string { return typeof value === "number" && Number.isFinite(value) ? value.toFixed(0) : "--"; }
@@ -53,7 +54,11 @@ export function createVolatilityHeadless(dependencies: VolatilityHeadlessDepende
53
54
  async load(args, context) {
54
55
  const result = await dependencies.load(args, context);
55
56
  context.signal.throwIfAborted();
56
- return { ...projectVolatilityHeadless(result.data), errors: result.errors,
57
+ // Daily index closes, dated by the newest index on the board; a sparse index shows as the oldest.
58
+ const boardDates = result.data.board.filter((row) => row.value != null).map((row) => row.date);
59
+ const freshness = { source: "Cboe and FRED", status: "not-a-feed" as const, basis: "daily index closes", cadence: "daily" as const,
60
+ asOf: newestReportTime([...boardDates, result.data.curve.date]), oldest: oldestReportTime(boardDates) };
61
+ return { ...projectVolatilityHeadless(result.data), freshness, errors: result.errors,
57
62
  complete: result.phase === "ready" && !result.stale && result.data.warnings.length === 0,
58
63
  unavailableSymbols: result.data.board.filter((row) => row.value == null).map((row) => row.symbol),
59
64
  metadata: { stale: result.stale, phase: result.phase, loaded: result.loaded, total: result.total,
@@ -15,6 +15,7 @@ import {
15
15
  WORLD_INDICES,
16
16
  type IndexEntry,
17
17
  } from "./indices";
18
+ import { quoteFreshnessFields } from "../shared/report-freshness";
18
19
 
19
20
  const COLUMNS = [
20
21
  { key: "shortName", header: "Index" },
@@ -69,6 +70,7 @@ function projectWorldIndicesHeadless(
69
70
  rows: regionEntries.map((entry) => {
70
71
  const quote = loaded.quotes.get(entry.symbol);
71
72
  return {
73
+ ...quoteFreshnessFields(quote),
72
74
  ...entry,
73
75
  price: quote?.price ?? null,
74
76
  unit: "index points",
@@ -0,0 +1,157 @@
1
+ /**
2
+ * The land under the desktop and web map: a quiet fill, 1 px coastlines and
3
+ * fainter borders, at 1:50m for the world and 1:10m for the tiles in view once
4
+ * zoomed in. Until a level arrives the level below stands in for it: the
5
+ * terminal's outline before the 1:50m chunk, 1:50m over tiles still loading.
6
+ */
7
+ import { memo, useEffect, useId, useMemo, useState } from "react";
8
+ import {
9
+ basemapLevel,
10
+ GRATICULE_MAX_PX_PER_DEGREE,
11
+ loadDetailIndex,
12
+ loadDetailTile,
13
+ loadWorldBasemap,
14
+ TILE_CACHE_SIZE,
15
+ tilesInView,
16
+ type BasemapPaths,
17
+ type DetailTile,
18
+ type GeoBox,
19
+ } from "./basemap";
20
+ import { matrixTransform, type MapMatrix } from "./geo-svg";
21
+ import { WORLD_OUTLINES } from "./world-outlines";
22
+
23
+ export interface BasemapPalette {
24
+ land: string;
25
+ coast: string;
26
+ border: string;
27
+ }
28
+
29
+ interface BasemapLayerProps {
30
+ matrix: MapMatrix;
31
+ view: GeoBox;
32
+ pxPerDegree: number;
33
+ palette: BasemapPalette;
34
+ }
35
+
36
+ let outlinePath: BasemapPaths | null = null;
37
+
38
+ /** The terminal's 1:110m outlines in degrees, shown for the moment before 1:50m arrives. */
39
+ function fallbackPaths(): BasemapPaths {
40
+ if (outlinePath) return outlinePath;
41
+ let land = "";
42
+ for (const outline of WORLD_OUTLINES) {
43
+ let previous: number | null = null;
44
+ for (const [longitude, latitude] of outline) {
45
+ const lat = Math.max(-60, latitude);
46
+ land += `${previous === null || Math.abs(longitude - previous) > 180 ? "M" : "L"}${longitude} ${-lat}`;
47
+ previous = longitude;
48
+ }
49
+ }
50
+ outlinePath = { land: "", coast: land, borders: "" };
51
+ return outlinePath;
52
+ }
53
+
54
+ const GRATICULE = (() => {
55
+ let path = "";
56
+ for (let longitude = -180; longitude <= 180; longitude += 30) path += `M${longitude} 60L${longitude} -85`;
57
+ for (let latitude = -30; latitude <= 60; latitude += 30) path += `M-180 ${-latitude}L180 ${-latitude}`;
58
+ return path;
59
+ })();
60
+
61
+ function useWorldPaths(): BasemapPaths | null {
62
+ const [paths, setPaths] = useState<BasemapPaths | null>(null);
63
+ useEffect(() => {
64
+ let current = true;
65
+ loadWorldBasemap().then((loaded) => {
66
+ if (current) setPaths(loaded);
67
+ }, () => {});
68
+ return () => {
69
+ current = false;
70
+ };
71
+ }, []);
72
+ return paths;
73
+ }
74
+
75
+ function useDetailTiles(active: boolean, view: GeoBox): { tiles: DetailTile[]; loaded: ReadonlyMap<string, BasemapPaths> } {
76
+ const [index, setIndex] = useState<readonly DetailTile[] | null>(null);
77
+ const [loaded, setLoaded] = useState<ReadonlyMap<string, BasemapPaths>>(() => new Map());
78
+ useEffect(() => {
79
+ if (!active || index) return;
80
+ let current = true;
81
+ loadDetailIndex().then((tiles) => {
82
+ if (current) setIndex(tiles);
83
+ }, () => {});
84
+ return () => {
85
+ current = false;
86
+ };
87
+ }, [active, index]);
88
+ const tiles = active && index ? tilesInView(index, view) : [];
89
+ const key = tiles.map((tile) => tile.id).join(",");
90
+ useEffect(() => {
91
+ if (!key || !index) return;
92
+ let current = true;
93
+ for (const tile of index.filter((entry) => key.split(",").includes(entry.id))) {
94
+ loadDetailTile(tile).then((paths) => {
95
+ if (!current) return;
96
+ setLoaded((previous) => {
97
+ if (previous.get(tile.id) === paths) return previous;
98
+ // Newest last; the map keeps no more decoded tiles than the cache does.
99
+ const next = new Map(previous);
100
+ next.delete(tile.id);
101
+ next.set(tile.id, paths);
102
+ while (next.size > TILE_CACHE_SIZE) next.delete(next.keys().next().value!);
103
+ return next;
104
+ });
105
+ }, () => {});
106
+ }
107
+ return () => {
108
+ current = false;
109
+ };
110
+ }, [index, key]);
111
+ return useMemo(() => ({ tiles, loaded }), [key, loaded]);
112
+ }
113
+
114
+ const BasemapPathsView = memo(function BasemapPathsView({ paths, palette, clipPath }: { paths: BasemapPaths; palette: BasemapPalette; clipPath?: string }) {
115
+ return (
116
+ <g clipPath={clipPath}>
117
+ {paths.land ? <path d={paths.land} fill={palette.land} fillRule="evenodd" stroke="none" /> : null}
118
+ {paths.borders ? (
119
+ <path d={paths.borders} fill="none" stroke={palette.border} strokeWidth={0.7} strokeLinejoin="round" vectorEffect="non-scaling-stroke" />
120
+ ) : null}
121
+ {paths.coast ? <path d={paths.coast} fill="none" stroke={palette.coast} strokeWidth={1} strokeLinejoin="round" vectorEffect="non-scaling-stroke" /> : null}
122
+ </g>
123
+ );
124
+ });
125
+
126
+ export function BasemapLayer({ matrix, view, pxPerDegree, palette }: BasemapLayerProps) {
127
+ const clipId = `gloom-basemap-${useId().replace(/[^a-zA-Z0-9_-]/g, "")}`;
128
+ const world = useWorldPaths();
129
+ const level = basemapLevel(pxPerDegree);
130
+ const { tiles, loaded } = useDetailTiles(level === "detail", view);
131
+ const ready = tiles.filter((tile) => loaded.has(tile.id));
132
+ const missing = tiles.filter((tile) => !loaded.has(tile.id));
133
+ // While tiles load, 1:50m fills in only where they are missing.
134
+ const partial = level === "detail" && missing.length > 0 && ready.length > 0;
135
+ const showWorld = level === "world" || !tiles.length || missing.length > 0;
136
+ return (
137
+ <g
138
+ transform={matrixTransform(matrix)}
139
+ data-gloom-role="basemap"
140
+ data-basemap-level={level === "detail" && tiles.length && !missing.length ? "detail" : world ? "world" : "outline"}
141
+ >
142
+ {pxPerDegree <= GRATICULE_MAX_PX_PER_DEGREE ? (
143
+ <path d={GRATICULE} fill="none" stroke={palette.border} strokeOpacity={0.45} strokeWidth={0.5} vectorEffect="non-scaling-stroke" />
144
+ ) : null}
145
+ {partial ? (
146
+ <defs>
147
+ <clipPath id={clipId}>
148
+ {missing.map((tile) => <rect key={tile.id} x={tile.box[0]} y={-tile.box[3]} width={tile.box[2] - tile.box[0]} height={tile.box[3] - tile.box[1]} />)}
149
+ </clipPath>
150
+ </defs>
151
+ ) : null}
152
+ {showWorld ? <BasemapPathsView paths={world ?? fallbackPaths()} palette={palette} clipPath={partial ? `url(#${clipId})` : undefined} /> : null}
153
+ {/* One path per tile: a tile that comes into view adds its own paths and leaves the others alone. */}
154
+ {level === "detail" ? ready.map((tile) => <BasemapPathsView key={tile.id} paths={loaded.get(tile.id)!} palette={palette} />) : null}
155
+ </g>
156
+ );
157
+ }
@@ -0,0 +1,214 @@
1
+ /**
2
+ * Land and borders under the desktop and web map: Natural Earth 1:50m for the
3
+ * whole world, and 1:10m tiles for the part in view once the map is zoomed in.
4
+ * Both are lazy chunks (see `natural-earth/`), so the app bundle carries
5
+ * neither. Only the desktop and web builds say where they are; the terminal,
6
+ * which draws its own outline, has nothing to load and bundles none of it.
7
+ *
8
+ * Paths are in degrees, x east and y south (minus the latitude), so the map
9
+ * places every path with one transform and pan or zoom rebuilds nothing.
10
+ */
11
+
12
+ /** What a generated chunk exports: coordinates as integers of `STEP` degrees, polyline-encoded. */
13
+ export interface BasemapChunk {
14
+ STEP: number;
15
+ LAND: string;
16
+ BORDERS: string;
17
+ }
18
+
19
+ /** West, south, east, north in degrees. */
20
+ export type GeoBox = readonly [number, number, number, number];
21
+
22
+ export interface DetailTile {
23
+ id: string;
24
+ /** What the tile was cut to: its square, a hair wider so neighbours overlap. */
25
+ box: GeoBox;
26
+ load: () => Promise<BasemapChunk>;
27
+ }
28
+
29
+ export interface BasemapPaths {
30
+ /** Land rings, filled even-odd so lakes stay open. */
31
+ land: string;
32
+ /** The same rings without the edges a tile, the date line or the map's southern edge cut. */
33
+ coast: string;
34
+ borders: string;
35
+ }
36
+
37
+ /** The map stops at 60°S; the 1:50m chunk is cut there. */
38
+ const WORLD_BOX: GeoBox = [-180, -60, 180, 90];
39
+
40
+ /**
41
+ * Pixels per degree where 1:10m takes over: a view about 30 degrees wide on
42
+ * a laptop. 1:50m keeps about a fiftieth of a degree, so closer in its
43
+ * coastlines turn into straight runs; further out 1:10m only costs paint.
44
+ */
45
+ export const DETAIL_MIN_PX_PER_DEGREE = 32;
46
+ /** Below this the world is far enough out for a faint graticule to help. */
47
+ export const GRATICULE_MAX_PX_PER_DEGREE = 4.5;
48
+ /** Tiles decoded and kept; a long session over many seas drops the oldest. */
49
+ export const TILE_CACHE_SIZE = 40;
50
+
51
+ export type BasemapLevel = "world" | "detail";
52
+
53
+ export function basemapLevel(pxPerDegree: number): BasemapLevel {
54
+ return pxPerDegree >= DETAIL_MIN_PX_PER_DEGREE ? "detail" : "world";
55
+ }
56
+
57
+ /** Decodes a chunk's rings or lines into flat integer runs: x0, y0, x1, y1... */
58
+ export function decodeParts(text: string): Int32Array[] {
59
+ if (!text) return [];
60
+ return text.split(" ").map((part) => {
61
+ const values: number[] = [];
62
+ let index = 0;
63
+ let lon = 0;
64
+ let lat = 0;
65
+ let axis = 0;
66
+ while (index < part.length) {
67
+ let shift = 0;
68
+ let bits = 0;
69
+ let byte = 0;
70
+ do {
71
+ byte = part.charCodeAt(index++) - 63;
72
+ bits |= (byte & 0x1f) << shift;
73
+ shift += 5;
74
+ } while (byte >= 0x20 && index < part.length);
75
+ const delta = bits & 1 ? ~(bits >> 1) : bits >> 1;
76
+ if (axis === 0) {
77
+ lon += delta;
78
+ values.push(lon);
79
+ } else {
80
+ lat += delta;
81
+ values.push(lat);
82
+ }
83
+ axis ^= 1;
84
+ }
85
+ return Int32Array.from(values);
86
+ });
87
+ }
88
+
89
+ function onCutEdge(x1: number, y1: number, x2: number, y2: number, box: readonly number[]): boolean {
90
+ return (x1 === x2 && (x1 === box[0] || x1 === box[2])) || (y1 === y2 && (y1 === box[1] || y1 === box[3]));
91
+ }
92
+
93
+ /** Builds the land, coast and border paths of one chunk cut to `box`. */
94
+ export function basemapPaths(chunk: BasemapChunk, box: GeoBox): BasemapPaths {
95
+ const scale = Math.round(1 / chunk.STEP);
96
+ const cut = box.map((value) => Math.round(value * scale));
97
+ const x = (value: number) => value / scale;
98
+ const y = (value: number) => -value / scale;
99
+ let land = "";
100
+ let coast = "";
101
+ for (const ring of decodeParts(chunk.LAND)) {
102
+ const count = ring.length / 2;
103
+ if (count < 3) continue;
104
+ land += `M${x(ring[0]!)} ${y(ring[1]!)}`;
105
+ for (let index = 1; index < count; index += 1) land += `L${x(ring[index * 2]!)} ${y(ring[index * 2 + 1]!)}`;
106
+ land += "Z";
107
+ let drawing = false;
108
+ for (let index = 0; index < count; index += 1) {
109
+ const next = (index + 1) % count;
110
+ const x1 = ring[index * 2]!;
111
+ const y1 = ring[index * 2 + 1]!;
112
+ const x2 = ring[next * 2]!;
113
+ const y2 = ring[next * 2 + 1]!;
114
+ if (onCutEdge(x1, y1, x2, y2, cut)) {
115
+ drawing = false;
116
+ continue;
117
+ }
118
+ if (!drawing) coast += `M${x(x1)} ${y(y1)}`;
119
+ coast += `L${x(x2)} ${y(y2)}`;
120
+ drawing = true;
121
+ }
122
+ }
123
+ let borders = "";
124
+ for (const line of decodeParts(chunk.BORDERS)) {
125
+ for (let index = 0; index < line.length; index += 2) borders += `${index ? "L" : "M"}${x(line[index]!)} ${y(line[index + 1]!)}`;
126
+ }
127
+ return { land, coast, borders };
128
+ }
129
+
130
+ /** Tiles under the view, with a margin either side so a pan finds its tiles loaded. */
131
+ export function tilesInView(tiles: readonly DetailTile[], view: GeoBox, margin = 0.15): DetailTile[] {
132
+ const padX = (view[2] - view[0]) * margin;
133
+ const padY = (view[3] - view[1]) * margin;
134
+ const west = view[0] - padX;
135
+ const south = view[1] - padY;
136
+ const east = view[2] + padX;
137
+ const north = view[3] + padY;
138
+ return tiles.filter(({ box }) => box[0] < east && box[2] > west && box[1] < north && box[3] > south);
139
+ }
140
+
141
+ /** What `natural-earth/index.chunk.ts` exports. */
142
+ interface BasemapIndex {
143
+ loadWorld: () => Promise<BasemapChunk>;
144
+ DETAIL_TILES: readonly DetailTile[];
145
+ }
146
+
147
+ /**
148
+ * `./natural-earth/index.chunk.js` in the desktop and web builds, which ship
149
+ * the chunks beside the app; absent everywhere else. Written as the import's
150
+ * own specifier so a build without it has no path to follow into the data.
151
+ */
152
+ declare const __GLOOM_WORLD_MAP_DATA__: string | undefined;
153
+
154
+ let indexModule: Promise<BasemapIndex> | null = null;
155
+ let worldPaths: Promise<BasemapPaths> | null = null;
156
+ const tilePaths = new Map<string, Promise<BasemapPaths>>();
157
+
158
+ function loadIndex(): Promise<BasemapIndex> {
159
+ if (typeof __GLOOM_WORLD_MAP_DATA__ !== "string") return Promise.reject(new Error("No map data in this build"));
160
+ indexModule ??= (import(__GLOOM_WORLD_MAP_DATA__) as Promise<BasemapIndex>).catch((error: unknown) => {
161
+ indexModule = null;
162
+ throw error;
163
+ });
164
+ return indexModule;
165
+ }
166
+
167
+ export function loadWorldBasemap(): Promise<BasemapPaths> {
168
+ worldPaths ??= loadIndex()
169
+ .then((index) => index.loadWorld())
170
+ .then((chunk) => basemapPaths(chunk, WORLD_BOX))
171
+ .catch((error: unknown) => {
172
+ worldPaths = null;
173
+ throw error;
174
+ });
175
+ return worldPaths;
176
+ }
177
+
178
+ export function loadDetailIndex(): Promise<readonly DetailTile[]> {
179
+ return loadIndex().then((index) => index.DETAIL_TILES);
180
+ }
181
+
182
+ /**
183
+ * Decoding a tile takes a few milliseconds; a view that needs twenty would
184
+ * hold a frame for a quarter of a second. They take turns, one per task.
185
+ */
186
+ let decodeQueue: Promise<unknown> = Promise.resolve();
187
+
188
+ function nextTask(): Promise<void> {
189
+ return new Promise((resolve) => setTimeout(resolve, 0));
190
+ }
191
+
192
+ export function loadDetailTile(tile: DetailTile): Promise<BasemapPaths> {
193
+ const cached = tilePaths.get(tile.id);
194
+ if (cached) {
195
+ // Most recently used last, so the oldest is the first to go.
196
+ tilePaths.delete(tile.id);
197
+ tilePaths.set(tile.id, cached);
198
+ return cached;
199
+ }
200
+ const pending = tile.load()
201
+ .then((chunk) => {
202
+ const decoded = decodeQueue.then(nextTask).then(() => basemapPaths(chunk, tile.box));
203
+ decodeQueue = decoded.catch(() => {});
204
+ return decoded;
205
+ })
206
+ .catch((error: unknown) => {
207
+ tilePaths.delete(tile.id);
208
+ throw error;
209
+ });
210
+ tilePaths.set(tile.id, pending);
211
+ while (tilePaths.size > TILE_CACHE_SIZE) tilePaths.delete(tilePaths.keys().next().value!);
212
+ return pending;
213
+ }
214
+
@@ -0,0 +1,45 @@
1
+ /** Map data through the Cloud client: venues, the geo layer catalog, entity pages and details. */
2
+ import { apiClient, type CloudWorldVenueMapPayload } from "../../../api-client";
3
+ import {
4
+ getGeoEntities,
5
+ getGeoEntity,
6
+ loadGeoCatalog,
7
+ type GeoEntitiesPayload,
8
+ type GeoEntitiesQuery,
9
+ type GeoEntityPayload,
10
+ type GeoLayersPayload,
11
+ type GeoRequest,
12
+ } from "../../../api-client/geo";
13
+
14
+ export const ENTITY_PAGE_SIZE = 100;
15
+
16
+ /** Requests under /cloud/geo/ through the shared client, resolved late so tests can stub it. */
17
+ export const cloudGeoRequest: GeoRequest = (path, init) => apiClient.geo(path, init);
18
+
19
+ export async function loadWorldVenues(): Promise<CloudWorldVenueMapPayload> {
20
+ const response = await apiClient.getCloudWorldVenues();
21
+ if (!response.data) throw new Error(response.reasonCode ?? "World venue data unavailable");
22
+ return response.stale ? { ...response.data, stale: true } : response.data;
23
+ }
24
+
25
+ export function loadMapCatalog(request: GeoRequest = cloudGeoRequest, force = false): Promise<GeoLayersPayload | null> {
26
+ return loadGeoCatalog(request, { force });
27
+ }
28
+
29
+ export function loadEntityPage(
30
+ layerId: string,
31
+ query: GeoEntitiesQuery,
32
+ signal: AbortSignal,
33
+ request: GeoRequest = cloudGeoRequest,
34
+ ): Promise<GeoEntitiesPayload> {
35
+ return getGeoEntities(request, layerId, { limit: ENTITY_PAGE_SIZE, ...query }, signal);
36
+ }
37
+
38
+ export function loadEntityDetail(
39
+ layerId: string,
40
+ entityId: string,
41
+ signal?: AbortSignal,
42
+ request: GeoRequest = cloudGeoRequest,
43
+ ): Promise<GeoEntityPayload> {
44
+ return getGeoEntity(request, layerId, entityId, signal);
45
+ }