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
@@ -1,6 +1,50 @@
1
+ import { peekGeoCatalog } from "../../../api-client/geo";
2
+ import { SERIES_COLORS } from "../../../time-series/resolve";
3
+ import { CHART_COMPOSER_PANE_ID } from "../../../types/config";
4
+ import type { PaneTemplateDef } from "../../../types/plugin";
5
+ import { chartHeadless } from "../chart-composer/headless";
6
+ import { buildCustomChartPreset } from "../chart-composer/presets";
7
+ import { formatGeoSeriesExpression } from "../chart-composer/series-expression";
1
8
  import type { PluginModule } from "../plugin-module";
9
+ import { cloudGeoRequest } from "./client";
10
+ import { chokepointSeriesFor, chokepointSeriesIds, createGeoChartSeriesCapability, KEY_CHOKEPOINTS } from "./geo-series";
11
+ import { mapHeadless } from "./headless";
12
+ import { buildMapSettingsDef, groupTitle, MAP_PRESET_OPTIONS, parseMapPreset } from "./layers";
2
13
  import { WORLD_VENUE_MAP_PANE_ID, WorldVenueMapPane } from "./pane";
3
14
 
15
+ const CHOKE_SERIES_LIMIT = 6;
16
+
17
+ const chokepointTemplate: PaneTemplateDef = {
18
+ id: "chokepoint-chart-pane",
19
+ paneId: CHART_COMPOSER_PANE_ID,
20
+ headless: { ...chartHeadless("chart-composer-pane"), description: "Daily vessel transits through the main shipping chokepoints, or one chokepoint by name." },
21
+ label: "Chokepoint Transits",
22
+ description: "Chart daily vessel transits through the main shipping chokepoints, or one of them.",
23
+ keywords: ["chokepoint", "chokepoints", "transits", "suez", "panama", "hormuz", "malacca", "shipping", "strait", "canal"],
24
+ shortcut: { prefix: "CHOKE", argPlaceholder: "chokepoint", argKind: "text", argOptional: true },
25
+ canCreate: () => true,
26
+ createInstance: async (_context, options) => {
27
+ const typed = options?.arg?.trim();
28
+ const named = typed ? await chokepointSeriesFor(cloudGeoRequest, typed) : null;
29
+ const ids = named
30
+ ? [named]
31
+ // Without the series index, CHOKE still asks for the key straits by name.
32
+ : await chokepointSeriesIds(cloudGeoRequest).then((found) => (found.length ? found : KEY_CHOKEPOINTS)).catch(() => KEY_CHOKEPOINTS);
33
+ const base = buildCustomChartPreset(ids.slice(0, CHOKE_SERIES_LIMIT).map(formatGeoSeriesExpression).join(", "));
34
+ // Daily transits read best over a year, each strait in its own colour.
35
+ const spec = {
36
+ ...base,
37
+ viewport: { ...base.viewport, range: "1Y" as const },
38
+ series: base.series.map((series, index) => ({ ...series, color: series.color ?? SERIES_COLORS[index % SERIES_COLORS.length]! })),
39
+ };
40
+ return {
41
+ title: named ? `G ${named} transits` : "G Chokepoint transits",
42
+ placement: "floating",
43
+ settings: { chartSpec: spec },
44
+ };
45
+ },
46
+ };
47
+
4
48
  export const worldVenueMapModule: PluginModule = {
5
49
  panes: [
6
50
  {
@@ -11,6 +55,8 @@ export const worldVenueMapModule: PluginModule = {
11
55
  defaultPosition: "right",
12
56
  defaultMode: "floating",
13
57
  defaultFloatingSize: { width: 124, height: 36 },
58
+ // The layer picker comes from the server's catalog; without one the pane keeps its old settings.
59
+ settings: (context) => buildMapSettingsDef(context.settings, peekGeoCatalog()?.layers) ?? { fields: [] },
14
60
  },
15
61
  ],
16
62
  paneTemplates: [
@@ -18,10 +64,27 @@ export const worldVenueMapModule: PluginModule = {
18
64
  id: "world-venue-map-pane",
19
65
  paneId: WORLD_VENUE_MAP_PANE_ID,
20
66
  label: "World Venue Map",
21
- description: "Live trading venue status, local time, and exchange locations around the world.",
22
- keywords: ["world", "map", "venue", "venues", "exchange", "mic", "market hours", "open markets"],
23
- shortcut: { prefix: "MAP" },
24
- createInstance: () => ({ placement: "floating" }),
67
+ description: "Trading venues with live status and local time; ships, ports, energy and airports as layers on the same map.",
68
+ keywords: ["world", "map", "venue", "venues", "exchange", "mic", "market hours", "open markets", "ships", "vessels", "ports", "chokepoints", "pipelines", "airports", "energy", "layers"],
69
+ shortcut: {
70
+ prefix: "MAP",
71
+ argPlaceholder: "layers",
72
+ argKind: "text",
73
+ argOptional: true,
74
+ argOptions: () => MAP_PRESET_OPTIONS,
75
+ },
76
+ headless: mapHeadless,
77
+ createInstance: (_context, options) => {
78
+ const preset = parseMapPreset(options?.arg ?? options?.values?.layers);
79
+ if (!preset) return { placement: "floating" };
80
+ return {
81
+ title: `Map · ${preset.layers.map(groupTitle).join(", ")}`,
82
+ placement: "floating",
83
+ settings: { layers: preset.layers, venues: preset.venues },
84
+ };
85
+ },
25
86
  },
87
+ chokepointTemplate,
26
88
  ],
89
+ capabilities: [createGeoChartSeriesCapability()],
27
90
  };
@@ -0,0 +1,552 @@
1
+ import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
2
+ import type { CloudWorldVenuePayload } from "../../../api-client";
3
+ import type {
4
+ GeoEntityRow,
5
+ GeoFeature,
6
+ GeoLayerInfo,
7
+ GeoSeriesInfo,
8
+ GeoTickerLink,
9
+ } from "../../../api-client/geo";
10
+ import { usePlanAccess } from "../../../api-client/plan-access";
11
+ import {
12
+ DataTableStackView,
13
+ EmptyState,
14
+ getPaneSidebarWidth,
15
+ getPaneSidebarWidthRange,
16
+ PaneSidebar,
17
+ QueryBar,
18
+ readStoredPaneSidebarWidth,
19
+ shouldShowPaneSidebar,
20
+ usePagedRows,
21
+ usePaneFooter,
22
+ usePaneMenuItems,
23
+ usePaneNoticeFooter,
24
+ useQueryBarSearch,
25
+ useTableLoadMore,
26
+ type DataTableCell,
27
+ type PageRequest,
28
+ type PaneHint,
29
+ } from "../../../components";
30
+ import { usePaneRefreshKey } from "../../../components/data-table/table-pane";
31
+ import {
32
+ useAsyncResource,
33
+ usePaneSettingValue,
34
+ usePaneVisible,
35
+ usePluginAppActions,
36
+ usePluginPaneState,
37
+ } from "../../../public/react";
38
+ import { relativeLuminance } from "../../../theme/color-utils";
39
+ import { useThemeColors } from "../../../theme/theme-context";
40
+ import type { PaneProps } from "../../../types/plugin";
41
+ import { Box, Text, TextAttributes, useUiCapabilities, type ScrollBoxRenderable } from "../../../ui";
42
+ import { formatGeoSeriesExpression } from "../chart-composer/series-expression";
43
+ import { CLOUD_PLAN_KEY, useCloudUpgradeAction } from "../shared/cloud-upgrade";
44
+ import { ENTITY_PAGE_SIZE, loadEntityDetail, loadEntityPage, loadWorldVenues } from "./client";
45
+ import { EntityDetail } from "./entity-detail";
46
+ import { useWorldMapEvidence } from "./evidence";
47
+ import { entityCellText, entityColumns, TICKER_COLUMN_ID, type EntityColumn } from "./entity-table";
48
+ import type { GeoLayerState } from "./feed";
49
+ import { geoLayerColor, type GeoMapLayer, type GeoMapOverlay, type GeoMapSelection } from "./geo-draw";
50
+ import { WORLD_VENUE_MAP_PANE_ID } from "./ids";
51
+ import {
52
+ featureAnchor,
53
+ formatGeoValue,
54
+ formatTickerLinks,
55
+ isChangeColumn,
56
+ readVenuesSetting,
57
+ sameGeoView,
58
+ tickerLinkKey,
59
+ VENUES_SETTING_KEY,
60
+ WORLD_GEO_VIEW,
61
+ type GeoView,
62
+ } from "./layers";
63
+ import { WorldVenueMap, type WorldMapFocus } from "./map";
64
+ import { usesClassTones } from "./map-symbols";
65
+ import { filterWorldVenues } from "./model";
66
+ import { useGeoLayerFeed } from "./use-geo";
67
+ import { SelectedVenueHeader } from "./venue-header";
68
+
69
+ interface LayeredMapViewProps extends PaneProps {
70
+ /** The layers on, already capped. */
71
+ layers: GeoLayerInfo[];
72
+ /** The layer ids or groups the pane was asked for. */
73
+ tokens: readonly string[];
74
+ }
75
+
76
+ interface OpenEntity {
77
+ layer: string;
78
+ id: string;
79
+ }
80
+
81
+ type SortState = { key: string | null; dir: "asc" | "desc" };
82
+
83
+ /**
84
+ * Zoom an opened entity is shown at: 16 times the world (the server's zoom 4,
85
+ * where dense point layers stop clustering) for a ship or a port, wider for a
86
+ * pipeline or a field.
87
+ */
88
+ function openZoom(layer: GeoLayerInfo): number {
89
+ return layer.geometry === "point" ? 16 : 4;
90
+ }
91
+ const NO_SORT: SortState = { key: null, dir: "asc" };
92
+ /** Layers go closer than venues: 64 times the world is the server's zoom 6, where flights and single ports show. */
93
+ const LAYERED_MAX_ZOOM = 64;
94
+
95
+ function featureRow(feature: GeoFeature): GeoEntityRow {
96
+ const [lon, lat] = featureAnchor(feature);
97
+ return { id: feature.id, label: feature.label, lon, lat, props: feature.props, ...(feature.tickers ? { tickers: feature.tickers } : {}) };
98
+ }
99
+
100
+ function ageText(iso: string | null | undefined, now: number): string | null {
101
+ if (!iso) return null;
102
+ const formatted = formatGeoValue(iso, "datetime", now);
103
+ return formatted || null;
104
+ }
105
+
106
+ /** The layer's freshness in footer words: "live · 12s ago", "as of 4 Oct". */
107
+ function layerFreshness(state: GeoLayerState, now: number): { text: string; tone: "positive" | "muted" } | null {
108
+ if (state.phase !== "ready") return null;
109
+ const asOf = state.asOf ?? state.layer.asOf;
110
+ if (state.layer.cadence === "live") {
111
+ const age = ageText(asOf, now);
112
+ return { text: age ? `live · ${age}` : "live", tone: "positive" };
113
+ }
114
+ if (state.layer.cadence === "daily" && asOf) {
115
+ const date = new Date(Date.parse(asOf));
116
+ if (!Number.isFinite(date.getTime())) return null;
117
+ return { text: `as of ${date.toLocaleDateString("en-GB", { day: "numeric", month: "short", timeZone: "UTC" })}`, tone: "muted" };
118
+ }
119
+ return null;
120
+ }
121
+
122
+ function layerNotices(states: readonly GeoLayerState[]): string[] {
123
+ return states.flatMap((state) => {
124
+ const { layer } = state;
125
+ if (state.phase === "unavailable") return [`${layer.name}: ${state.note ?? layer.statusNote ?? "unavailable"}.`];
126
+ if (state.phase === "locked") return [`${layer.name} needs Pro.`];
127
+ const notes: string[] = [];
128
+ const partialNote = state.note ?? layer.statusNote;
129
+ if (layer.status === "partial" || state.note) notes.push(`${layer.name}: partial${partialNote ? `, ${partialNote}` : ""}.`);
130
+ if (state.phase === "error" && state.error) notes.push(`${layer.name} could not load: ${state.error}`);
131
+ else if (state.error) notes.push(`${layer.name} could not refresh: ${state.error}`);
132
+ if (state.truncated && !state.clusters.length) notes.push(`${layer.name}: zoom in to see every feature.`);
133
+ return notes;
134
+ });
135
+ }
136
+
137
+ const SelectedEntityHeader = memo(function SelectedEntityHeader({
138
+ row,
139
+ layer,
140
+ color,
141
+ now,
142
+ width,
143
+ }: {
144
+ row: GeoEntityRow | null;
145
+ layer: GeoLayerInfo;
146
+ color: string;
147
+ now: number;
148
+ width: number;
149
+ }) {
150
+ const colors = useThemeColors();
151
+ if (!row) return <Box height={2} />;
152
+ const seen = typeof row.props.lastSeen === "string" ? ageText(row.props.lastSeen, now) : null;
153
+ const meta = [
154
+ seen ? `seen ${seen}` : null,
155
+ formatTickerLinks(row.tickers) || null,
156
+ ].filter(Boolean).join(" · ");
157
+ return (
158
+ <Box flexDirection="column" height={2} width={width} paddingX={1}>
159
+ <Box flexDirection="row" justifyContent="space-between" width="100%">
160
+ <Text fg={colors.textBright} attributes={TextAttributes.BOLD}>{row.label}</Text>
161
+ <Text fg={color}>{layer.name}</Text>
162
+ </Box>
163
+ <Text fg={colors.textMuted}>{meta}</Text>
164
+ </Box>
165
+ );
166
+ });
167
+
168
+ export function LayeredMapView({ focused, width, height, layers, tokens }: LayeredMapViewProps) {
169
+ const colors = useThemeColors();
170
+ const dark = relativeLuminance(colors.text) > relativeLuminance(colors.bg);
171
+ const { nativePaneChrome } = useUiCapabilities();
172
+ const { createPaneFromTemplate } = usePluginAppActions();
173
+ const visible = usePaneVisible();
174
+ const { hasProAccess } = usePlanAccess();
175
+ const upgrade = useCloudUpgradeAction("map-layers");
176
+ const [venuesSetting] = usePaneSettingValue<boolean | undefined>(VENUES_SETTING_KEY, undefined);
177
+ const venuesOn = readVenuesSetting({ [VENUES_SETTING_KEY]: venuesSetting }, layers.length);
178
+ const { data: venueData } = useAsyncResource(venuesOn ? loadWorldVenues : null);
179
+ const [now, setNow] = useState(Date.now());
180
+ useEffect(() => {
181
+ const timer = setInterval(() => setNow(Date.now()), 5_000);
182
+ return () => clearInterval(timer);
183
+ }, []);
184
+
185
+ const [view, setView] = useState<GeoView>(WORLD_GEO_VIEW);
186
+ const onViewChange = useCallback((next: GeoView) => {
187
+ setView((current) => sameGeoView(current, next) ? current : next);
188
+ }, []);
189
+ const canAccess = useCallback((layer: GeoLayerInfo) => layer.access !== "pro" || hasProAccess, [hasProAccess]);
190
+ const states = useGeoLayerFeed(layers, view, visible, { canAccess });
191
+
192
+ // A layer coloured by class (ships) takes the first colour, so its own never meets the class colours.
193
+ const colorOrder = useMemo(
194
+ () => [...layers].sort((left, right) => Number(usesClassTones(right.id)) - Number(usesClassTones(left.id))),
195
+ [layers],
196
+ );
197
+ const colorFor = useCallback((layerId: string) => {
198
+ const index = colorOrder.findIndex((layer) => layer.id === layerId);
199
+ return geoLayerColor(Math.max(0, index), dark);
200
+ }, [colorOrder, dark]);
201
+
202
+ const [savedTableLayer, setTableLayer] = usePluginPaneState<string | null>("map:table", null);
203
+ const tableLayer = layers.find((layer) => layer.id === savedTableLayer)
204
+ ?? layers.find((layer) => layer.status !== "unavailable")
205
+ ?? layers[0]!;
206
+ const tableState = states.find((state) => state.layer.id === tableLayer.id) ?? null;
207
+ const [queries, setQueries] = usePluginPaneState<Record<string, string>>("map:queries", {});
208
+ const query = queries[tableLayer.id] ?? "";
209
+ const setQuery = useCallback((value: string) => setQueries((current) => ({ ...current, [tableLayer.id]: value })), [setQueries, tableLayer.id]);
210
+ const [sorts, setSorts] = usePluginPaneState<Record<string, SortState>>("map:sorts", {});
211
+ const sort = sorts[tableLayer.id] ?? NO_SORT;
212
+ const [selections, setSelections] = usePluginPaneState<Record<string, string>>("map:selected", {});
213
+ const [open, setOpen] = usePluginPaneState<OpenEntity | null>("map:open", null);
214
+ const [selectedMic, setSelectedMic] = useState<string | null>(null);
215
+ const [headerSubject, setHeaderSubject] = useState<"entity" | "venue">("entity");
216
+ const [focus, setFocus] = useState<WorldMapFocus | null>(null);
217
+ const { active: searchFocused, focus: focusSearch, searchProps } = useQueryBarSearch();
218
+ const scrollRef = useRef<ScrollBoxRenderable>(null);
219
+ const detailScrollRef = useRef<ScrollBoxRenderable>(null);
220
+
221
+ const tableLocked = !canAccess(tableLayer) || tableState?.phase === "locked";
222
+ const tableReadable = tableLayer.status !== "unavailable" && tableState?.phase !== "unavailable" && !tableLocked;
223
+ const loadPage = useCallback(async ({ offset, signal }: PageRequest) => {
224
+ const page = await loadEntityPage(tableLayer.id, {
225
+ q: query,
226
+ ...(sort.key ? { sort: sort.key, dir: sort.dir } : {}),
227
+ offset,
228
+ }, signal);
229
+ const rows = page.rows ?? [];
230
+ const total = typeof page.total === "number" ? page.total : null;
231
+ return {
232
+ ...page,
233
+ rows,
234
+ hasMore: rows.length >= ENTITY_PAGE_SIZE && (total === null || offset + rows.length < total),
235
+ nextOffset: offset + rows.length,
236
+ };
237
+ }, [query, sort.dir, sort.key, tableLayer.id]);
238
+ const pages = usePagedRows(tableReadable ? loadPage : null, { getId: (row) => row.id });
239
+ const rows = pages.rows;
240
+ useWorldMapEvidence(tokens, states, rows.length, pages.loading);
241
+ const refresh = useCallback(() => pages.reload(), [pages.reload]);
242
+ usePaneRefreshKey(refresh, { focused: focused && !searchFocused });
243
+
244
+ const detailOpen = !!open && open.layer === tableLayer.id;
245
+ const detailLoader = useMemo(
246
+ () => (detailOpen && open ? () => loadEntityDetail(open.layer, open.id) : null),
247
+ [detailOpen, open?.id, open?.layer],
248
+ );
249
+ const detail = useAsyncResource(detailLoader);
250
+ const openRow = detailOpen
251
+ ? rows.find((row) => row.id === open!.id) ?? (detail.data ? featureRow(detail.data.feature) : null)
252
+ : null;
253
+
254
+ const selectedId = selections[tableLayer.id] ?? null;
255
+ // The table's cursor starts on its first row, and the map and header follow it from there.
256
+ const selectedRow = rows.find((row) => row.id === selectedId) ?? rows[0] ?? null;
257
+ const cursorId = selectedRow?.id ?? null;
258
+ const headerRow = openRow ?? selectedRow;
259
+
260
+ const selectRow = useCallback((id: string | null, layerId = tableLayer.id) => {
261
+ setSelections((current) => {
262
+ if (id === null) {
263
+ const { [layerId]: _removed, ...rest } = current;
264
+ return rest;
265
+ }
266
+ return current[layerId] === id ? current : { ...current, [layerId]: id };
267
+ });
268
+ }, [setSelections, tableLayer.id]);
269
+
270
+ const openTicker = useCallback((link: GeoTickerLink) => {
271
+ createPaneFromTemplate("new-ticker-detail-pane", { symbol: tickerLinkKey(link) });
272
+ }, [createPaneFromTemplate]);
273
+ const openSeries = useCallback((series: GeoSeriesInfo) => {
274
+ createPaneFromTemplate("chart-composer-pane", { arg: formatGeoSeriesExpression(series.id) });
275
+ }, [createPaneFromTemplate]);
276
+
277
+ // The map: one drawn layer per feed state, kept by identity so a pan redraws nothing.
278
+ const mapLayers = useMemo<GeoMapLayer[]>(() => states.map((state) => ({
279
+ id: state.layer.id,
280
+ name: state.layer.name,
281
+ geometry: state.layer.geometry,
282
+ color: colorFor(state.layer.id),
283
+ features: state.features,
284
+ clusters: state.clusters,
285
+ })), [colorFor, states]);
286
+ const selection = useMemo<GeoMapSelection | null>(() => {
287
+ if (headerSubject !== "entity") return null;
288
+ const target = openRow ?? selectedRow;
289
+ if (!target) return null;
290
+ return { layerId: tableLayer.id, id: target.id, longitude: target.lon, latitude: target.lat };
291
+ }, [headerSubject, openRow, selectedRow, tableLayer.id]);
292
+ const trail = useMemo(
293
+ () => (detailOpen && detail.data?.trail?.length ? detail.data.trail.map(([lon, lat]) => [lon, lat] as const) : null),
294
+ [detail.data?.trail, detailOpen],
295
+ );
296
+ // A track arrives with the detail: zoom so it fills about a third of the view.
297
+ const trailKey = detailOpen && trail && trail.length > 1 ? `trail:${open!.id}` : null;
298
+ useEffect(() => {
299
+ if (!trailKey || !trail) return;
300
+ const lons = trail.map(([lon]) => lon);
301
+ const lats = trail.map(([, lat]) => lat);
302
+ const span = Math.max(Math.max(...lons) - Math.min(...lons), (Math.max(...lats) - Math.min(...lats)) * 2, 0.05);
303
+ const last = trail.at(-1)!;
304
+ setFocus({ key: trailKey, longitude: last[0], latitude: last[1], zoom: Math.min(LAYERED_MAX_ZOOM, Math.max(openZoom(tableLayer), 360 / (span * 3))) });
305
+ }, [trailKey]);
306
+ const overlay = useMemo<GeoMapOverlay>(() => ({ layers: mapLayers, selected: selection, trail }), [mapLayers, selection, trail]);
307
+
308
+ const venues = useMemo(() => (venuesOn ? filterWorldVenues(venueData?.venues ?? [], "") : []), [venueData?.venues, venuesOn]);
309
+ const selectedVenue: CloudWorldVenuePayload | null = headerSubject === "venue"
310
+ ? venues.find((venue) => venue.mic === selectedMic) ?? null
311
+ : null;
312
+
313
+ const onSelectGeo = useCallback(({ layerId, feature }: { layerId: string; feature: GeoFeature }) => {
314
+ setHeaderSubject("entity");
315
+ setTableLayer(layerId);
316
+ selectRow(feature.id, layerId);
317
+ setOpen({ layer: layerId, id: feature.id });
318
+ }, [selectRow, setOpen, setTableLayer]);
319
+
320
+ const activeLinks = (openRow ? detail.data?.feature.tickers ?? openRow.tickers : selectedRow?.tickers) ?? [];
321
+ const primaryLink = activeLinks[0] ?? null;
322
+ // The layer's main series (transits, port calls) lead; `g` charts the first.
323
+ const detailSeries = useMemo(() => {
324
+ const series = detailOpen ? detail.data?.series ?? [] : [];
325
+ const main = new Set(tableLayer.series.map((entry) => entry.id));
326
+ return [...series.filter((entry) => main.has(entry.id)), ...series.filter((entry) => !main.has(entry.id))];
327
+ }, [detail.data?.series, detailOpen, tableLayer.series]);
328
+ const lockedLayer = states.some((state) => state.phase === "locked");
329
+
330
+ const hints: PaneHint[] = [
331
+ ...(tableReadable && !detailOpen ? [{ id: "search", key: "/", label: "search", onPress: focusSearch }] : []),
332
+ ...(primaryLink ? [{ id: "des", key: "d", label: "es", title: `Description of ${primaryLink.symbol}`, onPress: () => openTicker(primaryLink) }] : []),
333
+ ...(detailSeries[0] ? [{ id: "graph", key: "g", label: "raph", title: `Chart ${detailSeries[0].name}`, onPress: () => openSeries(detailSeries[0]!) }] : []),
334
+ ...(lockedLayer ? [{ id: "upgrade", key: CLOUD_PLAN_KEY, label: "upgrade", title: "Upgrade to Pro", onPress: () => void upgrade() }] : []),
335
+ ];
336
+ const loadingLayers = states.filter((state) => state.loading);
337
+ // The terminal map does not zoom, so there it only lists such a layer.
338
+ const zoomLayers = nativePaneChrome ? states.filter((state) => state.phase === "zoom") : [];
339
+ const freshness = tableState ? layerFreshness(tableState, now) : null;
340
+ usePaneFooter(WORLD_VENUE_MAP_PANE_ID, () => ({
341
+ info: [
342
+ ...(loadingLayers.length || (pages.loading && !rows.length) || (detailOpen && detail.loading && !detail.data)
343
+ ? [{ id: "loading", parts: [{ text: "loading", tone: "muted" as const }] }] : []),
344
+ ...(pages.loadingMore ? [{ id: "more", parts: [{ text: "loading more", tone: "muted" as const }] }] : []),
345
+ ...(freshness ? [{ id: "fresh", parts: [{ text: freshness.text, tone: freshness.tone }] }] : []),
346
+ ...(zoomLayers.length ? [{ id: "zoom", parts: [{ text: `zoom in for ${zoomLayers.map((state) => state.layer.name).join(", ")}`, tone: "muted" as const }] }] : []),
347
+ ...(pages.error && rows.length ? [{ id: "error", parts: [{ text: pages.error.message, tone: "warning" as const }] }] : []),
348
+ ],
349
+ hints,
350
+ }), [detail.data, detail.loading, detailOpen, freshness?.text, freshness?.tone, hints.map((hint) => hint.id).join(","), loadingLayers.length, pages.error, pages.loading, pages.loadingMore, rows.length, zoomLayers.map((state) => state.layer.id).join(",")]);
351
+ // The table's own layer says it cannot serve in the body; the footer warns about the others.
352
+ const notices = layerNotices(states.filter((state) => state.layer.id !== tableLayer.id || (state.phase !== "unavailable" && state.phase !== "locked")));
353
+ usePaneNoticeFooter({ registrationId: `${WORLD_VENUE_MAP_PANE_ID}:notices`, focused, notices, enabled: !detailOpen });
354
+ usePaneMenuItems(`${WORLD_VENUE_MAP_PANE_ID}:links`, () => [
355
+ ...activeLinks.slice(1).map((link) => ({ id: `des:${tickerLinkKey(link)}`, label: `Open ${tickerLinkKey(link)}`, onSelect: () => openTicker(link) })),
356
+ ...detailSeries.slice(1).map((series) => ({ id: `graph:${series.id}`, label: `Chart ${series.name}`, onSelect: () => openSeries(series) })),
357
+ ], [activeLinks.map(tickerLinkKey).join(","), detailSeries.map((series) => series.id).join(","), openSeries, openTicker]);
358
+
359
+ const native = !!nativePaneChrome;
360
+ const [storedSidebarWidth, setStoredSidebarWidth] = usePluginPaneState<number | null>("sidebarWidth", null);
361
+ const [draggedSidebarWidth, setDraggedSidebarWidth] = useState<number | null>(null);
362
+ const horizontal = shouldShowPaneSidebar(Math.max(rows.length, 24), width, height);
363
+ // Wider than the venue list: a layer's table carries a few numeric columns beside the name.
364
+ const defaultSidebarWidth = Math.max(38, Math.min(66, Math.round(width * 0.4))) + (native ? 0 : 1);
365
+ const sidebarWidth = horizontal
366
+ ? getPaneSidebarWidth(width, native, draggedSidebarWidth ?? readStoredPaneSidebarWidth(storedSidebarWidth) ?? defaultSidebarWidth)
367
+ : width;
368
+ const sidebarRange = getPaneSidebarWidthRange(width);
369
+ const listWidth = horizontal && !native ? Math.max(1, sidebarWidth - 1) : sidebarWidth;
370
+ const mapWidth = horizontal ? Math.max(1, width - sidebarWidth) : width;
371
+ // Stacked, the map takes the rows the world needs (plus the header and room to zoom), not half the pane.
372
+ const naturalMapRows = nativePaneChrome ? Math.ceil((width * 145) / 360 / 2.12) + 4 : Math.ceil((width * 145) / 360 / 2.25) + 3;
373
+ const mapSectionHeight = horizontal ? height : Math.max(8, Math.min(Math.floor(height * 0.52), naturalMapRows));
374
+ const tableHeight = horizontal ? height : Math.max(4, height - mapSectionHeight);
375
+ const mapHeight = Math.max(2, mapSectionHeight - 2);
376
+
377
+ const layerColumns = pages.pages[0]?.columns?.length ? pages.pages[0].columns : tableLayer.columns;
378
+ const columns = useMemo(() => entityColumns(layerColumns, listWidth), [layerColumns, listWidth]);
379
+ const nowRef = useRef(now);
380
+ nowRef.current = now;
381
+ const renderCell = useCallback((row: GeoEntityRow, column: EntityColumn): DataTableCell => {
382
+ if (column.id === TICKER_COLUMN_ID) {
383
+ const link = row.tickers?.[0];
384
+ return link
385
+ ? { text: formatTickerLinks(row.tickers), value: tickerLinkKey(link), color: colors.textBright, attributes: TextAttributes.BOLD, onMouseDown: () => openTicker(link) }
386
+ : { text: "", value: null };
387
+ }
388
+ const { text, value } = entityCellText(row, column.geo!, nowRef.current);
389
+ if (column.geo?.key === "label") return { text, value, color: colors.textBright };
390
+ if (column.geo && isChangeColumn(column.geo) && typeof value === "number") {
391
+ return { text, value, color: value > 0 ? colors.positive : value < 0 ? colors.negative : colors.text };
392
+ }
393
+ return { text, value, color: column.align === "right" ? colors.text : colors.textDim };
394
+ }, [colors, openTicker]);
395
+
396
+ const onScroll = useTableLoadMore(scrollRef, pages.hasMore && !detailOpen, pages.loadMore);
397
+ // A layer that cannot serve has nothing to search and, alone, nothing to switch to: no bar at all.
398
+ const queryBar = !tableReadable && layers.length < 2 ? undefined : (
399
+ <QueryBar
400
+ width={listWidth}
401
+ search={tableReadable ? {
402
+ value: query,
403
+ onChange: setQuery,
404
+ placeholder: "Filter...",
405
+ focused: focused && !detailOpen,
406
+ ...searchProps,
407
+ debounceMs: 200,
408
+ normalizeValue: (value: string) => value.trim(),
409
+ } : undefined}
410
+ view={layers.length > 1 ? {
411
+ value: tableLayer.id,
412
+ options: layers.map((layer) => ({ value: layer.id, label: layer.name })),
413
+ onChange: (id: string) => {
414
+ setTableLayer(id);
415
+ setOpen(null);
416
+ },
417
+ focused: focused && !searchFocused && !detailOpen,
418
+ } : undefined}
419
+ />
420
+ );
421
+
422
+ // An entities answer can also say the layer cannot serve right now.
423
+ const pageStatus = pages.pages[0]?.status;
424
+ const unavailableNote = pages.pages[0]?.statusNote ?? tableState?.note ?? tableLayer.statusNote;
425
+ const emptyTitle = tableLocked ? `${tableLayer.name} needs Pro.`
426
+ : !tableReadable || pageStatus === "unavailable" ? `${tableLayer.name} unavailable.`
427
+ : pages.error && !rows.length ? `${tableLayer.name} could not load.`
428
+ : query.trim() ? "No matches." : `No ${tableLayer.name.toLowerCase()} in view.`;
429
+ const emptyHint = tableLocked ? undefined
430
+ : !tableReadable || pageStatus === "unavailable" ? unavailableNote
431
+ : pages.error && !rows.length ? pages.error.message : query.trim() ? "Clear search." : undefined;
432
+
433
+ const table = (
434
+ <DataTableStackView<GeoEntityRow, EntityColumn>
435
+ focused={focused && !searchFocused}
436
+ selection={{
437
+ kind: "id",
438
+ selectedId: cursorId,
439
+ getId: (row) => row.id,
440
+ onChange: (id) => {
441
+ setHeaderSubject("entity");
442
+ selectRow(id);
443
+ const row = rows.find((entry) => entry.id === id);
444
+ if (row) setFocus({ key: `select:${tableLayer.id}:${row.id}`, longitude: row.lon, latitude: row.lat });
445
+ },
446
+ }}
447
+ rootWidth={listWidth}
448
+ rootHeight={tableHeight}
449
+ rootBefore={queryBar}
450
+ columns={columns}
451
+ items={rows}
452
+ getItemKey={(row) => row.id}
453
+ renderCell={renderCell}
454
+ selectedTextOverridesCellColor
455
+ sortColumnId={sort.key}
456
+ sortDirection={sort.dir}
457
+ isColumnSortable={(column) => column.id !== TICKER_COLUMN_ID}
458
+ onHeaderClick={(columnId) => {
459
+ if (columnId === TICKER_COLUMN_ID) return;
460
+ setSorts((current) => {
461
+ const previous = current[tableLayer.id] ?? NO_SORT;
462
+ const next: SortState = previous.key === columnId
463
+ ? { key: columnId, dir: previous.dir === "asc" ? "desc" : "asc" }
464
+ : { key: columnId, dir: layerColumns.find((column) => column.key === columnId)?.align === "right" ? "desc" : "asc" };
465
+ return { ...current, [tableLayer.id]: next };
466
+ });
467
+ }}
468
+ scrollRef={scrollRef}
469
+ onBodyScrollActivity={onScroll}
470
+ resetScrollKey={`${tableLayer.id}:${query}:${sort.key}:${sort.dir}`}
471
+ onActivate={(row) => {
472
+ setHeaderSubject("entity");
473
+ selectRow(row.id);
474
+ setOpen({ layer: tableLayer.id, id: row.id });
475
+ setFocus({ key: `open:${tableLayer.id}:${row.id}`, longitude: row.lon, latitude: row.lat, zoom: openZoom(tableLayer) });
476
+ }}
477
+ detailOpen={detailOpen}
478
+ onBack={() => setOpen(null)}
479
+ detailTitle={openRow?.label}
480
+ detailScrollRef={detailScrollRef}
481
+ detailContent={detailOpen ? (
482
+ <EntityDetail
483
+ layer={tableLayer}
484
+ row={openRow}
485
+ detail={detail.data}
486
+ series={detailSeries}
487
+ width={listWidth}
488
+ now={now}
489
+ scrollRef={detailScrollRef}
490
+ onOpenTicker={openTicker}
491
+ onOpenSeries={openSeries}
492
+ />
493
+ ) : null}
494
+ emptyStateTitle={pages.loading && !rows.length && tableReadable ? "Loading..." : emptyTitle}
495
+ emptyStateHint={pages.loading && !rows.length ? undefined : emptyHint}
496
+ />
497
+ );
498
+
499
+ const map = (
500
+ <Box flexDirection="column" width={mapWidth} height={mapSectionHeight} overflow="hidden">
501
+ {headerSubject === "venue" && selectedVenue && venueData ? (
502
+ <SelectedVenueHeader venue={selectedVenue} checkedAt={venueData.checkedAt} now={now} width={mapWidth} />
503
+ ) : (
504
+ <SelectedEntityHeader row={headerRow} layer={tableLayer} color={colorFor(tableLayer.id)} now={now} width={mapWidth} />
505
+ )}
506
+ <WorldVenueMap
507
+ venues={venues}
508
+ selectedMic={headerSubject === "venue" ? selectedMic : null}
509
+ width={mapWidth}
510
+ height={mapHeight}
511
+ onSelect={(venue) => {
512
+ setSelectedMic(venue.mic);
513
+ setHeaderSubject("venue");
514
+ }}
515
+ overlay={overlay}
516
+ maxZoom={LAYERED_MAX_ZOOM}
517
+ onSelectGeo={onSelectGeo}
518
+ onViewChange={onViewChange}
519
+ focus={focus}
520
+ />
521
+ </Box>
522
+ );
523
+
524
+ if (!layers.length) return <EmptyState title="No layers on." />;
525
+
526
+ return horizontal ? (
527
+ <Box flexDirection="row" width={width} height={height}>
528
+ <PaneSidebar
529
+ width={sidebarWidth}
530
+ height={height}
531
+ focused={focused}
532
+ resize={{
533
+ min: sidebarRange.min,
534
+ max: sidebarRange.max,
535
+ onResize: setDraggedSidebarWidth,
536
+ onResizeEnd: (next) => {
537
+ setStoredSidebarWidth(next);
538
+ setDraggedSidebarWidth(null);
539
+ },
540
+ }}
541
+ >
542
+ {table}
543
+ </PaneSidebar>
544
+ {map}
545
+ </Box>
546
+ ) : (
547
+ <Box flexDirection="column" width={width} height={height}>
548
+ {map}
549
+ {table}
550
+ </Box>
551
+ );
552
+ }