gloomberb 0.10.1 → 0.10.3

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 (357) hide show
  1. package/package.json +8 -1
  2. package/src/api-client/auth.ts +1 -0
  3. package/src/api-client/index.test.ts +359 -108
  4. package/src/api-client/index.ts +24 -13
  5. package/src/api-client/socket.ts +70 -35
  6. package/src/api-client/types.ts +1 -1
  7. package/src/app/language-observer.test.tsx +82 -0
  8. package/src/app/language-observer.tsx +13 -0
  9. package/src/app/pane-runtime/index.tsx +2 -2
  10. package/src/app/pane-runtime/pane-settings-runtime.tsx +2 -2
  11. package/src/app/pane-runtime/pane-template-runtime.ts +2 -2
  12. package/src/app/pane-runtime/plugin-bindings.ts +2 -2
  13. package/src/app/pane-runtime/ticker-open-runtime.ts +2 -2
  14. package/src/app/runtime/broker-import.ts +2 -2
  15. package/src/app/runtime/plugin-bindings.ts +2 -2
  16. package/src/app/runtime/startup.ts +2 -2
  17. package/src/app.tsx +16 -7
  18. package/src/brokers/account-cache.ts +6 -6
  19. package/src/brokers/broker-ticker-sync.ts +3 -3
  20. package/src/brokers/remote-broker-adapter.ts +2 -2
  21. package/src/brokers/sync-broker-instance.ts +5 -6
  22. package/src/cli/commands/api.ts +3 -6
  23. package/src/cli/commands/automation.ts +7 -15
  24. package/src/cli/commands/market.ts +26 -61
  25. package/src/cli/commands/overview.ts +34 -45
  26. package/src/cli/commands/search.ts +25 -27
  27. package/src/cli/commands/system.ts +27 -57
  28. package/src/cli/commands/ticker.ts +62 -63
  29. package/src/cli/context.test.ts +63 -0
  30. package/src/cli/context.ts +15 -1
  31. package/src/cli/index.ts +1 -1
  32. package/src/cli/pane-functions/data.ts +2 -19
  33. package/src/cli/pane-functions/index.ts +26 -25
  34. package/src/cli/pane-functions/report.ts +1 -1
  35. package/src/cli/pane-functions/screenshot.ts +3 -3
  36. package/src/cli/scoped-context.ts +41 -0
  37. package/src/components/chart/composite/composite-chart.test.tsx +615 -18
  38. package/src/components/chart/composite/composite-chart.tsx +179 -79
  39. package/src/components/chart/composite/format.test.ts +20 -7
  40. package/src/components/chart/composite/index.ts +0 -21
  41. package/src/components/chart/composite/interactions.test.ts +90 -3
  42. package/src/components/chart/composite/interactions.ts +189 -24
  43. package/src/components/chart/composite/price-series.ts +0 -2
  44. package/src/components/chart/composite/rasterizer.ts +10 -9
  45. package/src/components/chart/composite/renderers.test.ts +108 -2
  46. package/src/components/chart/composite/scene.test.ts +36 -0
  47. package/src/components/chart/composite/scene.ts +5 -2
  48. package/src/components/chart/composite/text-renderer.ts +37 -25
  49. package/src/components/chart/composite/time-axis.test.ts +243 -0
  50. package/src/components/chart/composite/time-axis.ts +608 -0
  51. package/src/components/chart/composite/types.ts +12 -1
  52. package/src/components/chart/core/data.ts +2 -159
  53. package/src/components/chart/core/date-window.ts +6 -225
  54. package/src/components/chart/core/palette.ts +1 -12
  55. package/src/components/chart/core/pointer.ts +0 -142
  56. package/src/components/chart/core/types.ts +1 -58
  57. package/src/components/chart/cursor-motion.ts +2 -4
  58. package/src/components/chart/indicators/types.ts +0 -9
  59. package/src/components/chart/market-session.ts +0 -11
  60. package/src/components/chart/native/kitty/protocol.ts +1 -1
  61. package/src/components/chart/native/raster/layout.ts +3 -10
  62. package/src/components/chart/static/chart/axis-overlays.test.tsx +64 -0
  63. package/src/components/chart/static/chart/axis-overlays.tsx +59 -14
  64. package/src/components/chart/static/chart/surface.tsx +1 -2
  65. package/src/components/command-bar/action-runtime.ts +2 -2
  66. package/src/components/command-bar/commands/direct/index.ts +1 -1
  67. package/src/components/command-bar/panel/body.tsx +2 -2
  68. package/src/components/command-bar/panel/index.tsx +11 -9
  69. package/src/components/command-bar/routes/ticker-search/actions.ts +2 -2
  70. package/src/components/command-bar/surface/index.tsx +2 -2
  71. package/src/components/command-bar/workflow/body.tsx +3 -3
  72. package/src/components/command-bar/workflow/collection-actions.ts +2 -2
  73. package/src/components/command-bar/workflow/coordinator.ts +2 -2
  74. package/src/components/command-bar/workflow/field-row.tsx +25 -11
  75. package/src/components/command-bar/workflow/tickers.ts +2 -2
  76. package/src/components/feed-data-table/stack-view.test.tsx +28 -36
  77. package/src/components/feed-data-table/stack-view.tsx +3 -1
  78. package/src/components/index.ts +1 -1
  79. package/src/components/layout/detached-pane-shell.tsx +6 -6
  80. package/src/components/layout/header.tsx +13 -9
  81. package/src/components/layout/pane/content.test.tsx +42 -3
  82. package/src/components/layout/pane/content.tsx +3 -1
  83. package/src/components/layout/pane/footer/index.test.tsx +45 -2
  84. package/src/components/layout/pane/footer/registration.tsx +3 -1
  85. package/src/components/layout/shell/index.tsx +1 -2
  86. package/src/components/layout/shell/native/surfaces.ts +1 -1
  87. package/src/components/layout/shell/native/window-state.ts +1 -1
  88. package/src/components/layout/status-bar.tsx +12 -8
  89. package/src/components/layout/window-controls.tsx +8 -6
  90. package/src/components/onboarding/onboarding-wizard.tsx +3 -1
  91. package/src/components/pane-settings-dialog/desktop.tsx +0 -2
  92. package/src/components/pane-settings-dialog.tsx +0 -2
  93. package/src/components/pane-template-wizard.tsx +10 -9
  94. package/src/components/price-performance.tsx +2 -113
  95. package/src/components/price-sparkline/model.ts +15 -17
  96. package/src/components/price-sparkline/view.tsx +8 -7
  97. package/src/components/speedometer-gauge/desktop.tsx +2 -1
  98. package/src/components/speedometer-gauge/index.tsx +0 -2
  99. package/src/components/speedometer-gauge/terminal.tsx +24 -5
  100. package/src/components/ticker/badge/index.tsx +1 -18
  101. package/src/components/ui/data-table/index.tsx +1 -0
  102. package/src/components/ui/data-table/opentui/index.tsx +47 -5
  103. package/src/components/ui/data-table/types.ts +10 -0
  104. package/src/components/ui/data-table/visible-range.test.ts +29 -0
  105. package/src/components/ui/data-table/visible-range.ts +21 -0
  106. package/src/components/ui/frame.tsx +1 -2
  107. package/src/components/ui/page-stack-view.test.tsx +126 -0
  108. package/src/components/ui/page-stack-view.tsx +24 -12
  109. package/src/components/ui/tabs.tsx +7 -3
  110. package/src/components/ui/ui.test.tsx +46 -3
  111. package/src/components/use-external-link-footer.ts +4 -2
  112. package/src/core/app-service-ports.ts +46 -0
  113. package/src/core/app-services.test.ts +66 -0
  114. package/src/core/app-services.ts +17 -15
  115. package/src/data/config/chart-settings.ts +3 -10
  116. package/src/data/config/store/index.test.ts +99 -2
  117. package/src/data/config/store/migrations.ts +278 -0
  118. package/src/data/config/store/normalize.ts +44 -186
  119. package/src/data/resource-store.ts +73 -12
  120. package/src/data/sqlite/cache.test.ts +104 -15
  121. package/src/i18n/index.test.ts +17 -0
  122. package/src/i18n/index.ts +23 -6
  123. package/src/i18n/ja.ts +15 -0
  124. package/src/i18n/ko.ts +15 -0
  125. package/src/i18n/react.ts +6 -0
  126. package/src/i18n/zh-cn.ts +15 -0
  127. package/src/i18n/zh-tw.ts +15 -0
  128. package/src/market-data/coordinator/auxiliary.ts +7 -3
  129. package/src/market-data/coordinator/chart.ts +1 -1
  130. package/src/market-data/coordinator/index.test.ts +26 -0
  131. package/src/market-data/coordinator/index.ts +12 -6
  132. package/src/market-data/coordinator/quotes.ts +5 -40
  133. package/src/market-data/coordinator/subscriptions.test.ts +128 -1
  134. package/src/market-data/hooks.tsx +47 -1
  135. package/src/market-data/market/status.ts +6 -6
  136. package/src/market-data/performance.ts +1 -1
  137. package/src/market-data/quote-subscription-target.test.ts +37 -0
  138. package/src/market-data/quote-subscription-target.ts +60 -0
  139. package/src/market-data/quotes/freshness.ts +1 -0
  140. package/src/market-data/quotes/resolution.ts +4 -0
  141. package/src/market-data/request-types.ts +3 -3
  142. package/src/news/aggregator.test.ts +97 -1
  143. package/src/news/aggregator.ts +100 -57
  144. package/src/news/hooks.test.tsx +64 -0
  145. package/src/news/hooks.ts +18 -25
  146. package/src/plugins/builtin/account-management/footer.ts +4 -2
  147. package/src/plugins/builtin/account-management/pane.tsx +9 -4
  148. package/src/plugins/builtin/ai/agent-history.ts +11 -7
  149. package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +0 -5
  150. package/src/plugins/builtin/ai/ask-ai.test.tsx +3 -4
  151. package/src/plugins/builtin/ai/index.test.ts +2 -2
  152. package/src/plugins/builtin/ai/index.tsx +10 -11
  153. package/src/plugins/builtin/ai/pane-settings.ts +1 -1
  154. package/src/plugins/builtin/ai/pi/runtime.ts +6 -5
  155. package/src/plugins/builtin/ai/providers.ts +1 -9
  156. package/src/plugins/builtin/ai/runner.ts +0 -6
  157. package/src/plugins/builtin/ai/screener/footer.ts +4 -1
  158. package/src/plugins/builtin/ai/screener/pane.test.tsx +9 -9
  159. package/src/plugins/builtin/ai/workspace/model.ts +2 -27
  160. package/src/plugins/builtin/ai/workspace/pane.tsx +2 -2
  161. package/src/plugins/builtin/analytics/metrics.ts +1 -7
  162. package/src/plugins/builtin/broker-manager/detail.tsx +3 -2
  163. package/src/plugins/builtin/broker-manager/index.tsx +4 -2
  164. package/src/plugins/builtin/chart-composer/editor/controller.ts +643 -0
  165. package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +134 -0
  166. package/src/plugins/builtin/chart-composer/editor/model.test.ts +85 -0
  167. package/src/plugins/builtin/chart-composer/editor/model.ts +292 -0
  168. package/src/plugins/builtin/chart-composer/editor/opentui-view.tsx +108 -0
  169. package/src/plugins/builtin/chart-composer/editor.tsx +65 -947
  170. package/src/plugins/builtin/chart-composer/index.tsx +0 -6
  171. package/src/plugins/builtin/chart-composer/pane.tsx +69 -36
  172. package/src/plugins/builtin/chart-composer/presets.ts +1 -19
  173. package/src/plugins/builtin/chart-composer/settings.ts +1 -1
  174. package/src/plugins/builtin/chat/content/composer-runtime.ts +1 -1
  175. package/src/plugins/builtin/chat/content/shortcuts.ts +0 -1
  176. package/src/plugins/builtin/chat/controller/fetch.ts +8 -2
  177. package/src/plugins/builtin/chat/controller/persistence.ts +2 -0
  178. package/src/plugins/builtin/chat/controller/session.test.ts +25 -0
  179. package/src/plugins/builtin/chat/controller/session.ts +2 -0
  180. package/src/plugins/builtin/chat/controller/state.ts +7 -0
  181. package/src/plugins/builtin/chat/controller.test.ts +73 -20
  182. package/src/plugins/builtin/chat/message/desktop.tsx +2 -0
  183. package/src/plugins/builtin/cloud-tweets/footer.ts +9 -28
  184. package/src/plugins/builtin/cloud-tweets/pane.tsx +0 -5
  185. package/src/plugins/builtin/cloud-tweets/table.tsx +12 -17
  186. package/src/plugins/builtin/correlation/compute.ts +4 -10
  187. package/src/plugins/builtin/econ/index.tsx +17 -19
  188. package/src/plugins/builtin/holders/types.ts +0 -1
  189. package/src/plugins/builtin/insider/index.tsx +6 -31
  190. package/src/plugins/builtin/news/index.tsx +5 -7
  191. package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +6 -8
  192. package/src/plugins/builtin/news/wire/news/footer.ts +10 -11
  193. package/src/plugins/builtin/news/wire/read-state.test.ts +7 -0
  194. package/src/plugins/builtin/news/wire/read-state.ts +18 -28
  195. package/src/plugins/builtin/news/wire/rss/parser.ts +1 -11
  196. package/src/plugins/builtin/options/footer.test.ts +127 -0
  197. package/src/plugins/builtin/options/footer.ts +125 -0
  198. package/src/plugins/builtin/options/live-quotes.test.ts +341 -0
  199. package/src/plugins/builtin/options/live-quotes.ts +201 -0
  200. package/src/plugins/builtin/options/view.test.tsx +74 -1
  201. package/src/plugins/builtin/options/view.tsx +76 -24
  202. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +103 -117
  203. package/src/plugins/builtin/portfolio-list/cli/render.ts +11 -5
  204. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -135
  205. package/src/plugins/builtin/portfolio-list/header.tsx +9 -4
  206. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +6 -1
  207. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +4 -2
  208. package/src/plugins/builtin/research/corporate-actions-pane.tsx +24 -52
  209. package/src/plugins/builtin/sec/filing-content.test.ts +41 -0
  210. package/src/plugins/builtin/sec/filing-content.ts +94 -0
  211. package/src/plugins/builtin/sec/index.tsx +21 -104
  212. package/src/plugins/builtin/sectors/sector-model.ts +1 -7
  213. package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -0
  214. package/src/plugins/builtin/shared/pane-footer.ts +82 -0
  215. package/src/plugins/builtin/shared/read-state.ts +64 -3
  216. package/src/plugins/builtin/substack/read-state.ts +16 -41
  217. package/src/plugins/builtin/thirteenf/pane.tsx +24 -73
  218. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +10 -10
  219. package/src/plugins/builtin/yield-curve/index.tsx +11 -9
  220. package/src/plugins/ibkr/dialogs.tsx +3 -3
  221. package/src/plugins/ibkr/gateway/fundamentals.ts +1 -1
  222. package/src/plugins/ibkr/gateway/history-requests.ts +2 -2
  223. package/src/plugins/ibkr/gateway/history.ts +2 -2
  224. package/src/plugins/ibkr/gateway/selection.ts +2 -2
  225. package/src/plugins/ibkr/gateway/service/index.ts +2 -2
  226. package/src/plugins/ibkr/gateway/service/native.ts +2 -2
  227. package/src/plugins/plugin-persistence.ts +3 -4
  228. package/src/plugins/prediction-markets/controller/keyboard.ts +1 -1
  229. package/src/plugins/prediction-markets/controller/live-updates.ts +6 -5
  230. package/src/plugins/prediction-markets/detail/book.tsx +2 -2
  231. package/src/plugins/prediction-markets/detail/outcomes.tsx +3 -1
  232. package/src/plugins/prediction-markets/detail/pane.tsx +3 -1
  233. package/src/plugins/prediction-markets/detail/trades.tsx +3 -3
  234. package/src/plugins/registry/context.ts +3 -4
  235. package/src/plugins/registry/index.ts +5 -6
  236. package/src/react/input.test.ts +126 -3
  237. package/src/react/input.ts +120 -1
  238. package/src/remote/app-host.tsx +2 -13
  239. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +44 -60
  240. package/src/renderers/electrobun/bun/desktop/capability-bridge.ts +15 -13
  241. package/src/renderers/electrobun/bun/desktop/host-requests.ts +26 -22
  242. package/src/renderers/electrobun/bun/desktop/http-fetch.ts +5 -1
  243. package/src/renderers/electrobun/bun/desktop/initialization.ts +11 -6
  244. package/src/renderers/electrobun/bun/desktop/media.ts +5 -3
  245. package/src/renderers/electrobun/bun/desktop/plugin-state.ts +21 -22
  246. package/src/renderers/electrobun/bun/desktop/window-controls.ts +3 -1
  247. package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +18 -18
  248. package/src/renderers/electrobun/bun/index.ts +123 -83
  249. package/src/renderers/electrobun/shared/protocol.ts +146 -7
  250. package/src/renderers/electrobun/view/app-services.ts +5 -8
  251. package/src/renderers/electrobun/view/backend-rpc.ts +18 -8
  252. package/src/renderers/electrobun/view/broker-remote-client.ts +2 -2
  253. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +5 -22
  254. package/src/renderers/electrobun/view/config-host.ts +1 -1
  255. package/src/renderers/electrobun/view/context-menu.ts +1 -17
  256. package/src/renderers/electrobun/view/data-table/index.tsx +44 -4
  257. package/src/renderers/electrobun/view/data-table/row.tsx +0 -4
  258. package/src/renderers/electrobun/view/desktop/control-styles.ts +37 -34
  259. package/src/renderers/electrobun/view/desktop/controls.tsx +36 -33
  260. package/src/renderers/electrobun/view/desktop/list-view.tsx +15 -12
  261. package/src/renderers/electrobun/view/desktop/popover.tsx +2 -3
  262. package/src/renderers/electrobun/view/dialog-host.tsx +2 -3
  263. package/src/renderers/electrobun/view/fatal-screen.tsx +0 -1
  264. package/src/renderers/electrobun/view/host/box.tsx +0 -1
  265. package/src/renderers/electrobun/view/host/chart-surface.tsx +0 -1
  266. package/src/renderers/electrobun/view/host/input.tsx +0 -1
  267. package/src/renderers/electrobun/view/host/media-surface.tsx +0 -1
  268. package/src/renderers/electrobun/view/host/native.ts +0 -1
  269. package/src/renderers/electrobun/view/host/scroll-box.tsx +0 -1
  270. package/src/renderers/electrobun/view/host/tabs.tsx +0 -1
  271. package/src/renderers/electrobun/view/host/text.tsx +0 -1
  272. package/src/renderers/electrobun/view/http-fetch.ts +3 -11
  273. package/src/renderers/electrobun/view/input-host.tsx +10 -53
  274. package/src/renderers/electrobun/view/key-event.ts +0 -1
  275. package/src/renderers/electrobun/view/main.tsx +3 -3
  276. package/src/renderers/electrobun/view/native-renderer.ts +0 -1
  277. package/src/renderers/electrobun/view/native-stubs/backend-rpc.ts +13 -1
  278. package/src/renderers/electrobun/view/remote/asset-data-client.ts +6 -44
  279. package/src/renderers/electrobun/view/remote/persistence.ts +6 -2
  280. package/src/renderers/electrobun/view/remote/quote-subscription-registry.test.ts +86 -0
  281. package/src/renderers/electrobun/view/remote/quote-subscription-registry.ts +72 -0
  282. package/src/renderers/electrobun/view/remote/ticker-repository.ts +2 -2
  283. package/src/renderers/electrobun/view/toast-host.tsx +0 -1
  284. package/src/renderers/electrobun/view/ui-host.tsx +1 -2
  285. package/src/renderers/electrobun/view/update-host.ts +1 -1
  286. package/src/renderers/opentui/chart-surface.tsx +2 -1
  287. package/src/renderers/opentui/dialog-host.tsx +20 -4
  288. package/src/renderers/opentui/image/loader.ts +12 -2
  289. package/src/renderers/opentui/input-host.tsx +7 -49
  290. package/src/renderers/opentui/start.tsx +2 -0
  291. package/src/sources/gloomberb-cloud/history-quality.ts +44 -0
  292. package/src/sources/gloomberb-cloud/index.test.ts +173 -0
  293. package/src/sources/gloomberb-cloud/index.ts +42 -20
  294. package/src/sources/gloomberb-cloud/normalizers.ts +9 -3
  295. package/src/sources/provider-router/cache.ts +1 -1
  296. package/src/sources/provider-router/document-routes.ts +10 -5
  297. package/src/sources/provider-router/financials.test.ts +14 -0
  298. package/src/sources/provider-router/financials.ts +6 -1
  299. package/src/sources/provider-router/history.test.ts +163 -1
  300. package/src/sources/provider-router/history.ts +278 -347
  301. package/src/sources/provider-router/index.ts +2 -2
  302. package/src/sources/provider-router/routing.test.ts +39 -0
  303. package/src/sources/provider-router/routing.ts +24 -11
  304. package/src/sources/provider-router/supplemental.ts +37 -17
  305. package/src/sources/sec-edgar/content.ts +2 -12
  306. package/src/sources/sec-edgar.ts +2 -13
  307. package/src/sources/yahoo-finance/history.ts +2 -2
  308. package/src/sources/yahoo-finance/options.ts +5 -0
  309. package/src/sources/yahoo-finance.ts +2 -2
  310. package/src/state/app/bootstrap.ts +2 -2
  311. package/src/state/app/context/index.tsx +2 -2
  312. package/src/state/hooks/quote-streaming.test.tsx +58 -1
  313. package/src/state/hooks/quote-streaming.ts +69 -1
  314. package/src/state/hooks/session-snapshot.ts +2 -2
  315. package/src/state/selectors.ts +0 -9
  316. package/src/sync/controller.test.ts +98 -0
  317. package/src/sync/controller.ts +29 -5
  318. package/src/sync/react.ts +2 -2
  319. package/src/sync/types.ts +2 -2
  320. package/src/theme/colors.test.ts +10 -0
  321. package/src/theme/colors.ts +75 -78
  322. package/src/theme/theme-context.test.tsx +67 -0
  323. package/src/theme/theme-context.tsx +9 -5
  324. package/src/tickers/open-target.ts +2 -2
  325. package/src/tickers/search/upsert.ts +2 -2
  326. package/src/time-series/alignment.ts +0 -7
  327. package/src/time-series/chart-data.ts +145 -0
  328. package/src/time-series/date-window.ts +44 -0
  329. package/src/time-series/history-window.ts +21 -0
  330. package/src/time-series/hooks.test.ts +59 -0
  331. package/src/time-series/hooks.ts +33 -12
  332. package/src/time-series/market-resolution.ts +2 -2
  333. package/src/time-series/market.ts +1 -1
  334. package/src/time-series/range.ts +9 -0
  335. package/src/{components/chart/core → time-series}/resolution.ts +3 -102
  336. package/src/time-series/resolve.test.ts +203 -1
  337. package/src/time-series/resolve.ts +96 -18
  338. package/src/time-series/spec-studies.test.ts +15 -0
  339. package/src/time-series/spec.ts +2 -2
  340. package/src/time-series/studies.ts +5 -2
  341. package/src/time-series/types.ts +3 -1
  342. package/src/time-series/use-chart-resolution.test.tsx +358 -0
  343. package/src/time-series/use-chart-resolution.ts +46 -4
  344. package/src/types/broker.ts +2 -2
  345. package/src/types/data-provider.ts +3 -3
  346. package/src/types/financials.ts +12 -0
  347. package/src/types/plugin.ts +2 -2
  348. package/src/ui/context-menu.tsx +3 -2
  349. package/src/ui/dialog.tsx +2 -2
  350. package/src/utils/html-entities.ts +3 -5
  351. package/src/utils/main-thread-monitor.ts +1 -1
  352. package/src/utils/price-history.ts +5 -5
  353. package/src/version.ts +3 -1
  354. package/src/components/chart/core/viewport.test.ts +0 -8
  355. package/src/components/chart/core/viewport.ts +0 -24
  356. package/src/time-series/index.ts +0 -14
  357. /package/src/{components/chart/core → time-series}/resolution.test.ts +0 -0
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import { useVirtualizer } from "@tanstack/react-virtual";
4
3
  import {
@@ -16,7 +15,9 @@ import { measurePerf } from "../../../../utils/perf-marks";
16
15
  import type {
17
16
  DataTableColumn,
18
17
  DataTableProps,
18
+ DataTableVisibleRange,
19
19
  } from "../../../../components/ui/data-table";
20
+ import { resolveDataTableVisibleRange } from "../../../../components/ui/data-table/visible-range";
20
21
  import {
21
22
  buildTableGridTemplateColumns,
22
23
  getTableWidth,
@@ -58,6 +59,8 @@ export function WebDataTable<T, C extends DataTableColumn = DataTableColumn>({
58
59
  onSelect,
59
60
  onActivate,
60
61
  onTableMouseDown,
62
+ visibleRangeKey,
63
+ onVisibleRangeChange,
61
64
  onRowMouseDown,
62
65
  onRowContextMenu,
63
66
  rowContextMenuSurface = false,
@@ -81,6 +84,10 @@ export function WebDataTable<T, C extends DataTableColumn = DataTableColumn>({
81
84
  const dispatch = useAppDispatch();
82
85
  const paneInstanceId = usePaneInstance()?.instanceId ?? null;
83
86
  const bodyElementRef = useRef<HTMLDivElement | null>(null);
87
+ const lastVisibleRangeRef = useRef<{
88
+ key: string | number | undefined;
89
+ range: DataTableVisibleRange;
90
+ } | null>(null);
84
91
  const headerHorizontal = useScrollbarState(false);
85
92
  const headerVertical = useScrollbarState(false);
86
93
  const bodyHorizontal = useScrollbarState(showHorizontalScrollbar);
@@ -107,10 +114,32 @@ export function WebDataTable<T, C extends DataTableColumn = DataTableColumn>({
107
114
  dispatch({ type: "FOCUS_PANE", paneId: paneInstanceId });
108
115
  }, [dispatch, paneInstanceId]);
109
116
 
117
+ const emitVisibleRange = useCallback(() => {
118
+ if (!onVisibleRangeChange) return;
119
+ const element = bodyElementRef.current;
120
+ if (!element) return;
121
+ const range = resolveDataTableVisibleRange({
122
+ itemCount: items.length,
123
+ rowSize: WEB_CELL_HEIGHT,
124
+ scrollOffset: element.scrollTop,
125
+ viewportSize: Math.max(0, element.clientHeight - WEB_CELL_HEIGHT),
126
+ });
127
+ const previous = lastVisibleRangeRef.current;
128
+ if (
129
+ previous !== null
130
+ && previous.key === visibleRangeKey
131
+ && previous.range.start === range.start
132
+ && previous.range.end === range.end
133
+ ) return;
134
+ lastVisibleRangeRef.current = { key: visibleRangeKey, range };
135
+ onVisibleRangeChange(range);
136
+ }, [items.length, onVisibleRangeChange, visibleRangeKey]);
110
137
  const handleBodyScrollActivity = useCallback(() => {
111
138
  onBodyScrollActivity();
112
- }, [onBodyScrollActivity]);
139
+ emitVisibleRange();
140
+ }, [emitVisibleRange, onBodyScrollActivity]);
113
141
  const scheduleBodyScrollActivity = useRafCallback(handleBodyScrollActivity);
142
+ const scheduleVisibleRangeMeasure = useRafCallback(emitVisibleRange);
114
143
 
115
144
  const rowVirtualizer = useVirtualizer({
116
145
  count: items.length,
@@ -155,12 +184,20 @@ export function WebDataTable<T, C extends DataTableColumn = DataTableColumn>({
155
184
 
156
185
  useEffect(() => {
157
186
  measureViewportWidth();
187
+ scheduleVisibleRangeMeasure();
158
188
  const element = bodyElementRef.current;
159
189
  if (!element || typeof ResizeObserver === "undefined") return;
160
- const observer = new ResizeObserver(scheduleViewportWidthMeasure);
190
+ const observer = new ResizeObserver(() => {
191
+ scheduleViewportWidthMeasure();
192
+ scheduleVisibleRangeMeasure();
193
+ });
161
194
  observer.observe(element);
162
195
  return () => observer.disconnect();
163
- }, [measureViewportWidth, scheduleViewportWidthMeasure]);
196
+ }, [measureViewportWidth, scheduleViewportWidthMeasure, scheduleVisibleRangeMeasure]);
197
+
198
+ useEffect(() => {
199
+ scheduleVisibleRangeMeasure();
200
+ }, [items.length, scheduleVisibleRangeMeasure, visibleRangeKey]);
164
201
 
165
202
  useEffect(() => {
166
203
  if (scrollToIndex == null || items.length === 0) return;
@@ -169,6 +206,7 @@ export function WebDataTable<T, C extends DataTableColumn = DataTableColumn>({
169
206
  rowVirtualizer.scrollToIndex(targetIndex, {
170
207
  align: scrollToIndexAlign === "center" ? "center" : "auto",
171
208
  });
209
+ scheduleVisibleRangeMeasure();
172
210
  return;
173
211
  }
174
212
  const element = bodyElementRef.current;
@@ -186,12 +224,14 @@ export function WebDataTable<T, C extends DataTableColumn = DataTableColumn>({
186
224
  if (nextTop !== currentTop) {
187
225
  element.scrollTop = nextTop * WEB_CELL_HEIGHT;
188
226
  }
227
+ scheduleVisibleRangeMeasure();
189
228
  }, [
190
229
  items.length,
191
230
  rowVirtualizer,
192
231
  scrollToIndex,
193
232
  scrollToIndexAlign,
194
233
  scrollToIndexVersion,
234
+ scheduleVisibleRangeMeasure,
195
235
  virtualize,
196
236
  ]);
197
237
 
@@ -1,8 +1,6 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import { memo, type CSSProperties } from "react";
4
3
  import { TextAttributes } from "../../../../ui/host";
5
- import { useThemeColors } from "../../../../theme/theme-context";
6
4
  import type {
7
5
  DataTableCell,
8
6
  DataTableColumn,
@@ -72,7 +70,6 @@ export function WebDataTableHeader<C extends DataTableColumn>({
72
70
  sortColumnId: string | null;
73
71
  sortDirection: "asc" | "desc";
74
72
  }) {
75
- useThemeColors();
76
73
  return (
77
74
  <div
78
75
  data-gloom-role="data-table-header-row"
@@ -183,7 +180,6 @@ function WebDataTableRowInner<
183
180
  rowContextMenuSurface: boolean;
184
181
  selected: boolean;
185
182
  }) {
186
- useThemeColors();
187
183
  const sectionHeader: DataTableSectionHeader | null =
188
184
  renderSectionHeader?.(item, index) ?? null;
189
185
  const baseRowStyle: CSSProperties = {
@@ -1,77 +1,80 @@
1
1
  import type { ButtonProps } from "../../../../components/ui/button";
2
- import { blendHex, colors } from "../../../../theme/colors";
2
+ import { blendHex, colors, type ThemeColors } from "../../../../theme/colors";
3
3
 
4
4
  export const CONTROL_RADIUS = 6;
5
5
 
6
- export function panelBorder(): string {
7
- return blendHex(colors.border, colors.borderFocused, 0.18);
6
+ export function panelBorder(palette: ThemeColors = colors): string {
7
+ return blendHex(palette.border, palette.borderFocused, 0.18);
8
8
  }
9
9
 
10
- export function panelFill(): string {
11
- return blendHex(colors.panel, colors.bg, 0.22);
10
+ export function panelFill(palette: ThemeColors = colors): string {
11
+ return blendHex(palette.panel, palette.bg, 0.22);
12
12
  }
13
13
 
14
- export function subtlePanelFill(): string {
15
- return blendHex(colors.panel, colors.bg, 0.42);
14
+ export function subtlePanelFill(palette: ThemeColors = colors): string {
15
+ return blendHex(palette.panel, palette.bg, 0.42);
16
16
  }
17
17
 
18
- export function selectedPanelFill(): string {
19
- return blendHex(colors.selected, colors.bg, 0.42);
18
+ export function selectedPanelFill(palette: ThemeColors = colors): string {
19
+ return blendHex(palette.selected, palette.bg, 0.42);
20
20
  }
21
21
 
22
- export function controlBorderColor(focused = false, active = false): string {
23
- if (active) return colors.borderFocused;
24
- if (focused) return blendHex(colors.borderFocused, colors.textBright, 0.24);
25
- return panelBorder();
22
+ export function controlBorderColor(focused = false, active = false, palette: ThemeColors = colors): string {
23
+ if (active) return palette.borderFocused;
24
+ if (focused) return blendHex(palette.borderFocused, palette.textBright, 0.24);
25
+ return panelBorder(palette);
26
26
  }
27
27
 
28
- export function controlShadow(active = false): string {
28
+ export function controlShadow(active = false, palette: ThemeColors = colors): string {
29
29
  return active
30
- ? `0 0 0 1px ${blendHex(colors.bg, colors.borderFocused, 0.18)}, inset 0 1px 0 ${blendHex(colors.bg, colors.textBright, 0.06)}`
31
- : `inset 0 1px 0 ${blendHex(colors.bg, colors.textBright, 0.04)}`;
30
+ ? `0 0 0 1px ${blendHex(palette.bg, palette.borderFocused, 0.18)}, inset 0 1px 0 ${blendHex(palette.bg, palette.textBright, 0.06)}`
31
+ : `inset 0 1px 0 ${blendHex(palette.bg, palette.textBright, 0.04)}`;
32
32
  }
33
33
 
34
- export function buttonPalette(props: Pick<ButtonProps, "variant" | "active" | "disabled">) {
34
+ export function buttonPalette(
35
+ props: Pick<ButtonProps, "variant" | "active" | "disabled">,
36
+ palette: ThemeColors = colors,
37
+ ) {
35
38
  if (props.disabled) {
36
39
  return {
37
- fg: colors.textMuted,
38
- bg: subtlePanelFill(),
39
- border: panelBorder(),
40
+ fg: palette.textMuted,
41
+ bg: subtlePanelFill(palette),
42
+ border: panelBorder(palette),
40
43
  };
41
44
  }
42
45
  if (props.active) {
43
46
  return {
44
- fg: colors.selectedText,
45
- bg: colors.selected,
46
- border: colors.borderFocused,
47
+ fg: palette.selectedText,
48
+ bg: palette.selected,
49
+ border: palette.borderFocused,
47
50
  };
48
51
  }
49
52
 
50
53
  switch (props.variant) {
51
54
  case "primary":
52
55
  return {
53
- fg: colors.bg,
54
- bg: colors.borderFocused,
55
- border: colors.borderFocused,
56
+ fg: palette.bg,
57
+ bg: palette.borderFocused,
58
+ border: palette.borderFocused,
56
59
  };
57
60
  case "danger":
58
61
  return {
59
- fg: colors.bg,
60
- bg: colors.negative,
61
- border: colors.negative,
62
+ fg: palette.bg,
63
+ bg: palette.negative,
64
+ border: palette.negative,
62
65
  };
63
66
  case "ghost":
64
67
  return {
65
- fg: colors.textDim,
68
+ fg: palette.textDim,
66
69
  bg: "transparent",
67
- border: panelBorder(),
70
+ border: panelBorder(palette),
68
71
  };
69
72
  case "secondary":
70
73
  default:
71
74
  return {
72
- fg: colors.text,
73
- bg: panelFill(),
74
- border: panelBorder(),
75
+ fg: palette.text,
76
+ bg: panelFill(palette),
77
+ border: panelBorder(palette),
75
78
  };
76
79
  }
77
80
  }
@@ -3,7 +3,7 @@ import { useRef, type RefObject } from "react";
3
3
  import { Box, Input, Text, Textarea, editableTextContextMenuItems, useRendererHost, useUiCapabilities } from "../../../../ui";
4
4
  import { TextAttributes, type InputRenderable } from "../../../../ui";
5
5
  import { useShortcut } from "../../../../react/input";
6
- import { blendHex, colors } from "../../../../theme/colors";
6
+ import { blendHex, type ThemeColors } from "../../../../theme/colors";
7
7
  import { contrastRatio } from "../../../../theme/color-utils";
8
8
  import { useThemeColors } from "../../../../theme/theme-context";
9
9
  import { isDetailBackNavigationKey } from "../../../../utils/back-navigation";
@@ -35,8 +35,8 @@ export function WebButton({
35
35
  width,
36
36
  height,
37
37
  }: ButtonProps) {
38
- useThemeColors();
39
- const palette = buttonPalette({ variant, active, disabled });
38
+ const colors = useThemeColors();
39
+ const palette = buttonPalette({ variant, active, disabled }, colors);
40
40
 
41
41
  return (
42
42
  <Box
@@ -56,7 +56,7 @@ export function WebButton({
56
56
  borderRadius: CONTROL_RADIUS,
57
57
  paddingLeft: 8,
58
58
  paddingRight: 8,
59
- boxShadow: controlShadow(active),
59
+ boxShadow: controlShadow(active, colors),
60
60
  cursor: disabled ? "default" : "pointer",
61
61
  }}
62
62
  >
@@ -81,7 +81,7 @@ export function WebButton({
81
81
  );
82
82
  }
83
83
 
84
- function checkboxAccentColor(): string {
84
+ function checkboxAccentColor(colors: ThemeColors): string {
85
85
  const base = colors.borderFocused;
86
86
  const nativeCheckmark = "#ffffff";
87
87
  const candidates = [
@@ -112,15 +112,15 @@ export function WebCheckbox({
112
112
  description,
113
113
  width,
114
114
  }: CheckboxProps) {
115
- useThemeColors();
115
+ const colors = useThemeColors();
116
116
  const textColor = disabled
117
117
  ? colors.textMuted
118
118
  : active
119
119
  ? colors.textBright
120
120
  : colors.text;
121
121
  const visibleLabel = displayLabel ?? label;
122
- const accentColor = checkboxAccentColor();
123
- const borderColor = checked ? accentColor : controlBorderColor(active, false);
122
+ const accentColor = checkboxAccentColor(colors);
123
+ const borderColor = checked ? accentColor : controlBorderColor(active, false, colors);
124
124
  return (
125
125
  <Box
126
126
  flexDirection="column"
@@ -164,7 +164,7 @@ export function WebCheckbox({
164
164
  width: 14,
165
165
  height: 14,
166
166
  margin: 0,
167
- backgroundColor: checked ? accentColor : panelFill(),
167
+ backgroundColor: checked ? accentColor : panelFill(colors),
168
168
  backgroundImage: checked ? checkboxCheckImage() : undefined,
169
169
  backgroundPosition: "center",
170
170
  backgroundRepeat: "no-repeat",
@@ -217,12 +217,15 @@ export function WebTextField({
217
217
  hint,
218
218
  type = "text",
219
219
  variant = "default",
220
- backgroundColor = colors.bg,
221
- textColor = colors.text,
222
- placeholderColor = colors.textDim,
220
+ backgroundColor,
221
+ textColor,
222
+ placeholderColor,
223
223
  onMouseDown,
224
224
  }: TextFieldProps) {
225
- useThemeColors();
225
+ const colors = useThemeColors();
226
+ const resolvedBackgroundColor = backgroundColor ?? colors.bg;
227
+ const resolvedTextColor = textColor ?? colors.text;
228
+ const resolvedPlaceholderColor = placeholderColor ?? colors.textDim;
226
229
  const localInputRef = useRef<InputRenderable>(null);
227
230
  const resolvedInputRef = inputRef ?? localInputRef;
228
231
  const renderer = useRendererHost();
@@ -234,7 +237,7 @@ export function WebTextField({
234
237
  {label && (
235
238
  <Box height={1}>
236
239
  <Text
237
- fg={focused ? colors.textBright : placeholderColor}
240
+ fg={focused ? colors.textBright : resolvedPlaceholderColor}
238
241
  style={{ fontWeight: 600 }}
239
242
  >
240
243
  {label}
@@ -245,7 +248,7 @@ export function WebTextField({
245
248
  height={1}
246
249
  width={width}
247
250
  alignItems="center"
248
- backgroundColor={plain ? "transparent" : backgroundColor}
251
+ backgroundColor={plain ? "transparent" : resolvedBackgroundColor}
249
252
  onMouseDown={() => {
250
253
  onMouseDown?.();
251
254
  resolvedInputRef.current?.focus?.();
@@ -259,9 +262,9 @@ export function WebTextField({
259
262
  }}
260
263
  data-gloom-role="desktop-text-field"
261
264
  style={{
262
- border: plain ? "none" : `1px solid ${controlBorderColor(focused, false)}`,
265
+ border: plain ? "none" : `1px solid ${controlBorderColor(focused, false, colors)}`,
263
266
  borderRadius: plain ? 0 : CONTROL_RADIUS,
264
- boxShadow: plain ? undefined : controlShadow(focused),
267
+ boxShadow: plain ? undefined : controlShadow(focused, colors),
265
268
  overflow: "hidden",
266
269
  }}
267
270
  >
@@ -272,11 +275,11 @@ export function WebTextField({
272
275
  type={type}
273
276
  placeholder={placeholder}
274
277
  focused={focused}
275
- textColor={textColor}
276
- focusedTextColor={textColor}
277
- placeholderColor={placeholderColor}
278
- backgroundColor={plain ? "transparent" : backgroundColor}
279
- focusedBackgroundColor={plain ? "transparent" : backgroundColor}
278
+ textColor={resolvedTextColor}
279
+ focusedTextColor={resolvedTextColor}
280
+ placeholderColor={resolvedPlaceholderColor}
281
+ backgroundColor={plain ? "transparent" : resolvedBackgroundColor}
282
+ focusedBackgroundColor={plain ? "transparent" : resolvedBackgroundColor}
280
283
  cursorColor={colors.textBright}
281
284
  style={{
282
285
  paddingLeft: plain ? 0 : 10,
@@ -318,7 +321,7 @@ export function WebMessageComposer({
318
321
  keyBindings,
319
322
  wrapText = false,
320
323
  }: MessageComposerProps) {
321
- useThemeColors();
324
+ const colors = useThemeColors();
322
325
  const borderColor = focused
323
326
  ? blendHex(colors.borderFocused, colors.textBright, 0.24)
324
327
  : colors.border;
@@ -336,7 +339,7 @@ export function WebMessageComposer({
336
339
  flexDirection="row"
337
340
  width={width}
338
341
  height={height}
339
- backgroundColor={panelFill()}
342
+ backgroundColor={panelFill(colors)}
340
343
  onMouseDown={requestFocus}
341
344
  data-gloom-role="desktop-message-composer"
342
345
  style={{
@@ -378,13 +381,13 @@ export function WebSegmentedControl({
378
381
  value,
379
382
  onChange,
380
383
  }: SegmentedControlProps) {
381
- useThemeColors();
384
+ const colors = useThemeColors();
382
385
  return (
383
386
  <Box
384
387
  flexDirection="row"
385
- backgroundColor={panelFill()}
388
+ backgroundColor={panelFill(colors)}
386
389
  style={{
387
- border: `1px solid ${panelBorder()}`,
390
+ border: `1px solid ${panelBorder(colors)}`,
388
391
  borderRadius: CONTROL_RADIUS,
389
392
  padding: 2,
390
393
  }}
@@ -428,7 +431,7 @@ export function WebDialogFrame({
428
431
  footer,
429
432
  showTitleDivider = false,
430
433
  }: DialogFrameProps) {
431
- useThemeColors();
434
+ const colors = useThemeColors();
432
435
  return (
433
436
  <Box flexDirection="column" style={{ padding: 14 }}>
434
437
  <Box
@@ -436,7 +439,7 @@ export function WebDialogFrame({
436
439
  flexDirection="row"
437
440
  alignItems="center"
438
441
  style={{
439
- borderBottom: showTitleDivider ? `1px solid ${panelBorder()}` : "none",
442
+ borderBottom: showTitleDivider ? `1px solid ${panelBorder(colors)}` : "none",
440
443
  paddingBottom: showTitleDivider ? 8 : 0,
441
444
  marginBottom: showTitleDivider ? 10 : 14,
442
445
  }}
@@ -450,7 +453,7 @@ export function WebDialogFrame({
450
453
  <Box
451
454
  height={1}
452
455
  style={{
453
- borderTop: `1px solid ${panelBorder()}`,
456
+ borderTop: `1px solid ${panelBorder(colors)}`,
454
457
  paddingTop: 8,
455
458
  marginTop: 10,
456
459
  }}
@@ -474,7 +477,7 @@ export function WebPageStackView({
474
477
  backLabel = "Back",
475
478
  backHint,
476
479
  }: PageStackViewProps) {
477
- useThemeColors();
480
+ const colors = useThemeColors();
478
481
  useShortcut((event) => {
479
482
  if (!focused || !detailOpen || !isDetailBackNavigationKey(event)) return;
480
483
  event.stopPropagation?.();
@@ -506,7 +509,7 @@ export function WebPageStackView({
506
509
  height={1}
507
510
  flexDirection="row"
508
511
  alignItems="center"
509
- backgroundColor={panelFill()}
512
+ backgroundColor={panelFill(colors)}
510
513
  onMouseDown={(event: { preventDefault?: () => void; stopPropagation?: () => void }) => {
511
514
  event.preventDefault?.();
512
515
  event.stopPropagation?.();
@@ -514,7 +517,7 @@ export function WebPageStackView({
514
517
  }}
515
518
  data-gloom-interactive="true"
516
519
  style={{
517
- border: `1px solid ${panelBorder()}`,
520
+ border: `1px solid ${panelBorder(colors)}`,
518
521
  borderRadius: CONTROL_RADIUS,
519
522
  paddingInline: 8,
520
523
  cursor: "pointer",
@@ -3,7 +3,7 @@ import { useEffect, useRef, useState, type CSSProperties } from "react";
3
3
  import { Box, ScrollBox, Text, type ScrollBoxRenderable } from "../../../../ui";
4
4
  import { TextAttributes } from "../../../../ui";
5
5
  import type { ListRowState, ListViewItem, ListViewProps } from "../../../../components/ui/list-view";
6
- import { blendHex, colors, hoverBg } from "../../../../theme/colors";
6
+ import { blendHex, hoverBg, type ThemeColors } from "../../../../theme/colors";
7
7
  import { useThemeColors } from "../../../../theme/theme-context";
8
8
  import {
9
9
  CONTROL_RADIUS,
@@ -19,6 +19,7 @@ function DefaultDesktopRow({
19
19
  item: ListViewItem;
20
20
  selected: boolean;
21
21
  }) {
22
+ const colors = useThemeColors();
22
23
  return (
23
24
  <Box
24
25
  flexDirection="row"
@@ -50,7 +51,7 @@ function DefaultDesktopRow({
50
51
  );
51
52
  }
52
53
 
53
- function listRowStyle(selected: boolean): CSSProperties {
54
+ function listRowStyle(selected: boolean, colors: ThemeColors): CSSProperties {
54
55
  return {
55
56
  borderRadius: CONTROL_RADIUS,
56
57
  border: `1px solid ${selected ? colors.borderFocused : "transparent"}`,
@@ -85,12 +86,12 @@ export function WebListView({
85
86
  selectOnHover = false,
86
87
  autoScrollToIndex = true,
87
88
  }: ListViewProps) {
88
- useThemeColors();
89
+ const colors = useThemeColors();
89
90
  const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);
90
91
  const scrollRef = useRef<ScrollBoxRenderable>(null);
91
92
  const baseBg = bgColor ?? colors.bg;
92
- const activeBg = selectedBgColor ?? selectedPanelFill();
93
- const rowHoverBg = hoverBgColor ?? hoverBg();
93
+ const activeBg = selectedBgColor ?? selectedPanelFill(colors);
94
+ const rowHoverBg = hoverBgColor ?? hoverBg(colors);
94
95
  const selectedItem = selectedIndex >= 0 ? items[selectedIndex] : undefined;
95
96
  const activeScrollIndex = scrollIndex ?? selectedIndex;
96
97
  const effectiveSurface = surface ?? (scrollable ? "framed" : "plain");
@@ -102,10 +103,10 @@ export function WebListView({
102
103
  backgroundColor: "transparent",
103
104
  }
104
105
  : {
105
- border: `1px solid ${panelBorder()}`,
106
+ border: `1px solid ${panelBorder(colors)}`,
106
107
  borderRadius: CONTROL_RADIUS,
107
108
  padding: 4,
108
- backgroundColor: subtlePanelFill(),
109
+ backgroundColor: subtlePanelFill(colors),
109
110
  };
110
111
 
111
112
  useEffect(() => {
@@ -113,7 +114,7 @@ export function WebListView({
113
114
  const scrollBox = scrollRef.current;
114
115
  if (!scrollBox) return;
115
116
  const safeIndex = Math.min(activeScrollIndex, items.length - 1);
116
- const viewportHeight = Math.max(scrollBox.viewport.height, 1);
117
+ const viewportHeight = Math.max(scrollBox.viewport?.height ?? 0, 1);
117
118
  const rowTop = safeIndex * rowHeight;
118
119
  const rowBottom = rowTop + rowHeight;
119
120
  if (rowTop < scrollBox.scrollTop) {
@@ -127,7 +128,9 @@ export function WebListView({
127
128
  if (!scrollable) return;
128
129
  const scrollBox = scrollRef.current;
129
130
  if (!scrollBox) return;
130
- scrollBox.verticalScrollBar.visible = items.length * rowHeight > scrollBox.viewport.height;
131
+ if (scrollBox.verticalScrollBar) {
132
+ scrollBox.verticalScrollBar.visible = items.length * rowHeight > (scrollBox.viewport?.height ?? 0);
133
+ }
131
134
  }, [items.length, height, flexGrow, rowHeight, scrollable]);
132
135
 
133
136
  const rows = items.length === 0
@@ -163,7 +166,7 @@ export function WebListView({
163
166
  onActivate?.(item, index);
164
167
  }}
165
168
  data-gloom-role="desktop-list-row"
166
- style={listRowStyle(selected)}
169
+ style={listRowStyle(selected, colors)}
167
170
  >
168
171
  {renderRow
169
172
  ? renderRow(item, state, index)
@@ -196,9 +199,9 @@ export function WebListView({
196
199
  {showSelectedDescription && selectedItem?.description && (
197
200
  <Box
198
201
  flexDirection="row"
199
- backgroundColor={subtlePanelFill()}
202
+ backgroundColor={subtlePanelFill(colors)}
200
203
  style={{
201
- border: `1px solid ${panelBorder()}`,
204
+ border: `1px solid ${panelBorder(colors)}`,
202
205
  borderRadius: CONTROL_RADIUS,
203
206
  paddingInline: 10,
204
207
  }}
@@ -1,10 +1,9 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
 
4
3
  import { useCallback, useEffect, useLayoutEffect, useRef, useState } from "react";
5
4
  import { createPortal } from "react-dom";
6
5
  import type { HostPopoverProps } from "../../../../ui";
7
- import { blendHex, colors } from "../../../../theme/colors";
6
+ import { blendHex } from "../../../../theme/colors";
8
7
  import { useThemeColors } from "../../../../theme/theme-context";
9
8
 
10
9
  const VIEWPORT_MARGIN = 10;
@@ -31,7 +30,7 @@ export function WebPopover({
31
30
  maxWidth = "min(420px, calc(100vw - 20px))",
32
31
  label,
33
32
  }: HostPopoverProps) {
34
- useThemeColors();
33
+ const colors = useThemeColors();
35
34
  const anchorRef = useRef<HTMLDivElement | null>(null);
36
35
  const popoverRef = useRef<HTMLDivElement | null>(null);
37
36
  const [position, setPosition] = useState<PopoverPosition>({ left: 0, top: 0, visible: false });
@@ -1,8 +1,7 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import { useCallback, useEffect, useMemo, useRef, useState, type ReactNode } from "react";
4
3
  import { DialogHostProvider, type DialogApi } from "../../../ui/dialog";
5
- import { blendHex, colors } from "../../../theme/colors";
4
+ import { blendHex } from "../../../theme/colors";
6
5
  import { useThemeColors } from "../../../theme/theme-context";
7
6
 
8
7
  interface DialogState {
@@ -27,7 +26,7 @@ export function shouldFocusDialogContainer(
27
26
  }
28
27
 
29
28
  export function WebDialogHostProvider({ children }: { children: ReactNode }) {
30
- useThemeColors();
29
+ const colors = useThemeColors();
31
30
  const [dialogState, setDialogState] = useState<DialogState | null>(null);
32
31
  const dialogStateRef = useRef<DialogState | null>(null);
33
32
  const dialogElementRef = useRef<HTMLDivElement | null>(null);
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import { Component, useMemo, useState, type ErrorInfo, type ReactNode } from "react";
4
3
  import { backendRequest, requestElectrobunRestart } from "./backend-rpc";
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import {
4
3
  forwardRef,
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import {
4
3
  forwardRef,
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import {
4
3
  forwardRef,
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import Hls from "hls.js";
4
3
  import { forwardRef, useEffect, useImperativeHandle, useRef, useState, type CSSProperties, type ReactNode } from "react";
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  import type { ContextMenuItem } from "../../../../types/context-menu";
3
2
  import { editableTextContextMenuItems } from "../../../../ui/context-menu";
4
3
  import { backendRequest, onContextMenuSelect } from "../backend-rpc";
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import {
4
3
  forwardRef,
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import {
4
3
  useEffect,
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import type { CSSProperties, ReactNode } from "react";
4
3
  import {