gloomberb 0.10.0 → 0.10.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 (353) 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/layout/detached-pane-shell.tsx +6 -6
  79. package/src/components/layout/header.tsx +13 -9
  80. package/src/components/layout/pane/content.test.tsx +42 -3
  81. package/src/components/layout/pane/content.tsx +3 -1
  82. package/src/components/layout/pane/footer/index.test.tsx +45 -2
  83. package/src/components/layout/pane/footer/registration.tsx +3 -1
  84. package/src/components/layout/shell/index.tsx +1 -2
  85. package/src/components/layout/shell/native/surfaces.ts +1 -1
  86. package/src/components/layout/shell/native/window-state.ts +1 -1
  87. package/src/components/layout/status-bar.tsx +12 -8
  88. package/src/components/layout/window-controls.tsx +8 -6
  89. package/src/components/onboarding/onboarding-wizard.tsx +3 -1
  90. package/src/components/pane-settings-dialog/desktop.tsx +0 -2
  91. package/src/components/pane-settings-dialog.tsx +0 -2
  92. package/src/components/pane-template-wizard.tsx +10 -9
  93. package/src/components/price-performance.tsx +2 -113
  94. package/src/components/price-sparkline/model.ts +15 -17
  95. package/src/components/price-sparkline/view.tsx +8 -7
  96. package/src/components/speedometer-gauge/desktop.tsx +2 -1
  97. package/src/components/speedometer-gauge/index.tsx +0 -2
  98. package/src/components/speedometer-gauge/terminal.tsx +24 -5
  99. package/src/components/ticker/badge/index.tsx +1 -18
  100. package/src/components/ui/data-table/opentui/index.tsx +3 -3
  101. package/src/components/ui/frame.tsx +1 -2
  102. package/src/components/ui/page-stack-view.test.tsx +126 -0
  103. package/src/components/ui/page-stack-view.tsx +24 -12
  104. package/src/components/ui/tabs.tsx +7 -3
  105. package/src/components/ui/ui.test.tsx +23 -2
  106. package/src/components/use-external-link-footer.ts +4 -2
  107. package/src/core/app-service-ports.ts +46 -0
  108. package/src/core/app-services.test.ts +66 -0
  109. package/src/core/app-services.ts +17 -15
  110. package/src/data/config/chart-settings.ts +3 -10
  111. package/src/data/config/store/index.test.ts +99 -2
  112. package/src/data/config/store/migrations.ts +278 -0
  113. package/src/data/config/store/normalize.ts +44 -186
  114. package/src/data/resource-store.ts +73 -12
  115. package/src/data/sqlite/cache.test.ts +104 -15
  116. package/src/i18n/index.test.ts +17 -0
  117. package/src/i18n/index.ts +23 -6
  118. package/src/i18n/ja.ts +13 -0
  119. package/src/i18n/ko.ts +13 -0
  120. package/src/i18n/react.ts +6 -0
  121. package/src/i18n/zh-cn.ts +13 -0
  122. package/src/i18n/zh-tw.ts +13 -0
  123. package/src/market-data/coordinator/auxiliary.ts +7 -3
  124. package/src/market-data/coordinator/chart.ts +1 -1
  125. package/src/market-data/coordinator/index.test.ts +26 -0
  126. package/src/market-data/coordinator/index.ts +11 -5
  127. package/src/market-data/coordinator/quotes.ts +4 -40
  128. package/src/market-data/coordinator/subscriptions.test.ts +89 -1
  129. package/src/market-data/hooks.tsx +47 -1
  130. package/src/market-data/market/status.ts +6 -6
  131. package/src/market-data/performance.ts +1 -1
  132. package/src/market-data/quote-subscription-target.test.ts +37 -0
  133. package/src/market-data/quote-subscription-target.ts +60 -0
  134. package/src/market-data/quotes/freshness.ts +1 -0
  135. package/src/market-data/quotes/resolution.ts +4 -0
  136. package/src/market-data/request-types.ts +3 -3
  137. package/src/news/aggregator.test.ts +97 -1
  138. package/src/news/aggregator.ts +100 -57
  139. package/src/news/hooks.test.tsx +64 -0
  140. package/src/news/hooks.ts +18 -25
  141. package/src/plugins/builtin/account-management/footer.ts +4 -2
  142. package/src/plugins/builtin/account-management/pane.tsx +9 -4
  143. package/src/plugins/builtin/ai/agent-history.ts +11 -7
  144. package/src/plugins/builtin/ai/ask-ai-detail-tab.tsx +0 -5
  145. package/src/plugins/builtin/ai/ask-ai.test.tsx +3 -4
  146. package/src/plugins/builtin/ai/index.test.ts +2 -2
  147. package/src/plugins/builtin/ai/index.tsx +10 -11
  148. package/src/plugins/builtin/ai/pane-settings.ts +1 -1
  149. package/src/plugins/builtin/ai/pi/runtime.ts +6 -5
  150. package/src/plugins/builtin/ai/providers.ts +1 -9
  151. package/src/plugins/builtin/ai/runner.ts +0 -6
  152. package/src/plugins/builtin/ai/screener/footer.ts +4 -1
  153. package/src/plugins/builtin/ai/screener/pane.test.tsx +9 -9
  154. package/src/plugins/builtin/ai/workspace/model.ts +2 -27
  155. package/src/plugins/builtin/ai/workspace/pane.tsx +2 -2
  156. package/src/plugins/builtin/analytics/metrics.ts +1 -7
  157. package/src/plugins/builtin/broker-manager/detail.tsx +3 -2
  158. package/src/plugins/builtin/broker-manager/index.tsx +4 -2
  159. package/src/plugins/builtin/chart-composer/editor/controller.ts +643 -0
  160. package/src/plugins/builtin/chart-composer/editor/desktop-view.tsx +134 -0
  161. package/src/plugins/builtin/chart-composer/editor/model.test.ts +85 -0
  162. package/src/plugins/builtin/chart-composer/editor/model.ts +292 -0
  163. package/src/plugins/builtin/chart-composer/editor/opentui-view.tsx +108 -0
  164. package/src/plugins/builtin/chart-composer/editor.tsx +65 -947
  165. package/src/plugins/builtin/chart-composer/index.tsx +0 -6
  166. package/src/plugins/builtin/chart-composer/pane.tsx +69 -36
  167. package/src/plugins/builtin/chart-composer/presets.ts +1 -19
  168. package/src/plugins/builtin/chart-composer/settings.ts +1 -1
  169. package/src/plugins/builtin/chat/content/composer-runtime.ts +1 -1
  170. package/src/plugins/builtin/chat/content/shortcuts.ts +0 -1
  171. package/src/plugins/builtin/chat/controller/fetch.ts +8 -2
  172. package/src/plugins/builtin/chat/controller/persistence.ts +2 -0
  173. package/src/plugins/builtin/chat/controller/session.test.ts +25 -0
  174. package/src/plugins/builtin/chat/controller/session.ts +2 -0
  175. package/src/plugins/builtin/chat/controller/state.ts +7 -0
  176. package/src/plugins/builtin/chat/controller.test.ts +73 -20
  177. package/src/plugins/builtin/chat/message/desktop.tsx +2 -0
  178. package/src/plugins/builtin/cloud-tweets/footer.ts +9 -28
  179. package/src/plugins/builtin/cloud-tweets/pane.tsx +0 -5
  180. package/src/plugins/builtin/cloud-tweets/table.tsx +12 -17
  181. package/src/plugins/builtin/correlation/compute.ts +4 -10
  182. package/src/plugins/builtin/econ/index.tsx +17 -19
  183. package/src/plugins/builtin/holders/types.ts +0 -1
  184. package/src/plugins/builtin/insider/index.tsx +6 -31
  185. package/src/plugins/builtin/news/index.tsx +5 -7
  186. package/src/plugins/builtin/news/wire/breaking/pane.test.tsx +6 -8
  187. package/src/plugins/builtin/news/wire/news/footer.ts +10 -11
  188. package/src/plugins/builtin/news/wire/read-state.test.ts +7 -0
  189. package/src/plugins/builtin/news/wire/read-state.ts +18 -28
  190. package/src/plugins/builtin/news/wire/rss/parser.ts +1 -11
  191. package/src/plugins/builtin/options/footer.test.ts +115 -0
  192. package/src/plugins/builtin/options/footer.ts +110 -0
  193. package/src/plugins/builtin/options/live-quotes.test.ts +238 -0
  194. package/src/plugins/builtin/options/live-quotes.ts +147 -0
  195. package/src/plugins/builtin/options/view.test.tsx +71 -1
  196. package/src/plugins/builtin/options/view.tsx +39 -24
  197. package/src/plugins/builtin/portfolio-list/cli/portfolio-command.ts +103 -117
  198. package/src/plugins/builtin/portfolio-list/cli/render.ts +11 -5
  199. package/src/plugins/builtin/portfolio-list/cli/watchlist-command.ts +120 -135
  200. package/src/plugins/builtin/portfolio-list/header.tsx +9 -4
  201. package/src/plugins/builtin/portfolio-list/quick-add/index.tsx +6 -1
  202. package/src/plugins/builtin/portfolio-list/quick-add/resolution.ts +4 -2
  203. package/src/plugins/builtin/research/corporate-actions-pane.tsx +24 -52
  204. package/src/plugins/builtin/sec/filing-content.test.ts +41 -0
  205. package/src/plugins/builtin/sec/filing-content.ts +94 -0
  206. package/src/plugins/builtin/sec/index.tsx +21 -104
  207. package/src/plugins/builtin/sectors/sector-model.ts +1 -7
  208. package/src/plugins/builtin/shared/cloud-upgrade.ts +1 -0
  209. package/src/plugins/builtin/shared/pane-footer.ts +82 -0
  210. package/src/plugins/builtin/shared/read-state.ts +64 -3
  211. package/src/plugins/builtin/substack/read-state.ts +16 -41
  212. package/src/plugins/builtin/thirteenf/pane.tsx +24 -73
  213. package/src/plugins/builtin/ticker-detail/quote-monitor/index.tsx +10 -10
  214. package/src/plugins/builtin/yield-curve/index.tsx +11 -9
  215. package/src/plugins/ibkr/dialogs.tsx +3 -3
  216. package/src/plugins/ibkr/gateway/fundamentals.ts +1 -1
  217. package/src/plugins/ibkr/gateway/history-requests.ts +2 -2
  218. package/src/plugins/ibkr/gateway/history.ts +2 -2
  219. package/src/plugins/ibkr/gateway/selection.ts +2 -2
  220. package/src/plugins/ibkr/gateway/service/index.ts +2 -2
  221. package/src/plugins/ibkr/gateway/service/native.ts +2 -2
  222. package/src/plugins/plugin-persistence.ts +3 -4
  223. package/src/plugins/prediction-markets/controller/keyboard.ts +1 -1
  224. package/src/plugins/prediction-markets/controller/live-updates.ts +6 -5
  225. package/src/plugins/prediction-markets/detail/book.tsx +2 -2
  226. package/src/plugins/prediction-markets/detail/outcomes.tsx +3 -1
  227. package/src/plugins/prediction-markets/detail/pane.tsx +3 -1
  228. package/src/plugins/prediction-markets/detail/trades.tsx +3 -3
  229. package/src/plugins/registry/context.ts +3 -4
  230. package/src/plugins/registry/index.ts +5 -6
  231. package/src/react/input.test.ts +126 -3
  232. package/src/react/input.ts +120 -1
  233. package/src/remote/app-host.tsx +2 -13
  234. package/src/renderers/electrobun/bun/desktop/backend-requests.ts +44 -60
  235. package/src/renderers/electrobun/bun/desktop/capability-bridge.ts +15 -13
  236. package/src/renderers/electrobun/bun/desktop/host-requests.ts +26 -22
  237. package/src/renderers/electrobun/bun/desktop/http-fetch.ts +5 -1
  238. package/src/renderers/electrobun/bun/desktop/initialization.ts +11 -6
  239. package/src/renderers/electrobun/bun/desktop/media.ts +5 -3
  240. package/src/renderers/electrobun/bun/desktop/plugin-state.ts +21 -22
  241. package/src/renderers/electrobun/bun/desktop/window-controls.ts +3 -1
  242. package/src/renderers/electrobun/bun/desktop/workspace/requests.ts +18 -18
  243. package/src/renderers/electrobun/bun/index.ts +123 -83
  244. package/src/renderers/electrobun/shared/protocol.ts +146 -7
  245. package/src/renderers/electrobun/view/app-services.ts +5 -8
  246. package/src/renderers/electrobun/view/backend-rpc.ts +18 -8
  247. package/src/renderers/electrobun/view/broker-remote-client.ts +2 -2
  248. package/src/renderers/electrobun/view/cli-pane-shot-entry.tsx +5 -22
  249. package/src/renderers/electrobun/view/config-host.ts +1 -1
  250. package/src/renderers/electrobun/view/context-menu.ts +1 -17
  251. package/src/renderers/electrobun/view/data-table/index.tsx +0 -1
  252. package/src/renderers/electrobun/view/data-table/row.tsx +0 -4
  253. package/src/renderers/electrobun/view/desktop/control-styles.ts +37 -34
  254. package/src/renderers/electrobun/view/desktop/controls.tsx +36 -33
  255. package/src/renderers/electrobun/view/desktop/list-view.tsx +15 -12
  256. package/src/renderers/electrobun/view/desktop/popover.tsx +2 -3
  257. package/src/renderers/electrobun/view/dialog-host.tsx +2 -3
  258. package/src/renderers/electrobun/view/fatal-screen.tsx +0 -1
  259. package/src/renderers/electrobun/view/host/box.tsx +0 -1
  260. package/src/renderers/electrobun/view/host/chart-surface.tsx +0 -1
  261. package/src/renderers/electrobun/view/host/input.tsx +0 -1
  262. package/src/renderers/electrobun/view/host/media-surface.tsx +0 -1
  263. package/src/renderers/electrobun/view/host/native.ts +0 -1
  264. package/src/renderers/electrobun/view/host/scroll-box.tsx +0 -1
  265. package/src/renderers/electrobun/view/host/tabs.tsx +0 -1
  266. package/src/renderers/electrobun/view/host/text.tsx +0 -1
  267. package/src/renderers/electrobun/view/http-fetch.ts +3 -11
  268. package/src/renderers/electrobun/view/input-host.tsx +10 -53
  269. package/src/renderers/electrobun/view/key-event.ts +0 -1
  270. package/src/renderers/electrobun/view/main.tsx +3 -3
  271. package/src/renderers/electrobun/view/native-renderer.ts +0 -1
  272. package/src/renderers/electrobun/view/native-stubs/backend-rpc.ts +13 -1
  273. package/src/renderers/electrobun/view/native-stubs/chart/surface-manager.ts +11 -0
  274. package/src/renderers/electrobun/view/native-stubs/native-stubs.test.ts +47 -0
  275. package/src/renderers/electrobun/view/remote/asset-data-client.ts +6 -44
  276. package/src/renderers/electrobun/view/remote/persistence.ts +6 -2
  277. package/src/renderers/electrobun/view/remote/quote-subscription-registry.test.ts +86 -0
  278. package/src/renderers/electrobun/view/remote/quote-subscription-registry.ts +72 -0
  279. package/src/renderers/electrobun/view/remote/ticker-repository.ts +2 -2
  280. package/src/renderers/electrobun/view/toast-host.tsx +0 -1
  281. package/src/renderers/electrobun/view/ui-host.tsx +1 -2
  282. package/src/renderers/electrobun/view/update-host.ts +1 -1
  283. package/src/renderers/opentui/chart-surface.tsx +2 -1
  284. package/src/renderers/opentui/dialog-host.tsx +20 -4
  285. package/src/renderers/opentui/image/loader.ts +12 -2
  286. package/src/renderers/opentui/input-host.tsx +7 -49
  287. package/src/renderers/opentui/start.tsx +2 -0
  288. package/src/sources/gloomberb-cloud/history-quality.ts +44 -0
  289. package/src/sources/gloomberb-cloud/index.test.ts +173 -0
  290. package/src/sources/gloomberb-cloud/index.ts +42 -20
  291. package/src/sources/gloomberb-cloud/normalizers.ts +9 -3
  292. package/src/sources/provider-router/cache.ts +1 -1
  293. package/src/sources/provider-router/document-routes.ts +10 -5
  294. package/src/sources/provider-router/financials.test.ts +14 -0
  295. package/src/sources/provider-router/financials.ts +6 -1
  296. package/src/sources/provider-router/history.test.ts +163 -1
  297. package/src/sources/provider-router/history.ts +278 -347
  298. package/src/sources/provider-router/index.ts +2 -2
  299. package/src/sources/provider-router/routing.test.ts +39 -0
  300. package/src/sources/provider-router/routing.ts +24 -11
  301. package/src/sources/provider-router/supplemental.ts +37 -17
  302. package/src/sources/sec-edgar/content.ts +2 -12
  303. package/src/sources/sec-edgar.ts +2 -13
  304. package/src/sources/yahoo-finance/history.ts +2 -2
  305. package/src/sources/yahoo-finance/options.ts +5 -0
  306. package/src/sources/yahoo-finance.ts +2 -2
  307. package/src/state/app/bootstrap.ts +2 -2
  308. package/src/state/app/context/index.tsx +2 -2
  309. package/src/state/hooks/quote-streaming.ts +66 -1
  310. package/src/state/hooks/session-snapshot.ts +2 -2
  311. package/src/state/selectors.ts +0 -9
  312. package/src/sync/controller.test.ts +98 -0
  313. package/src/sync/controller.ts +29 -5
  314. package/src/sync/react.ts +2 -2
  315. package/src/sync/types.ts +2 -2
  316. package/src/theme/colors.test.ts +10 -0
  317. package/src/theme/colors.ts +75 -78
  318. package/src/theme/theme-context.test.tsx +67 -0
  319. package/src/theme/theme-context.tsx +9 -5
  320. package/src/tickers/open-target.ts +2 -2
  321. package/src/tickers/search/upsert.ts +2 -2
  322. package/src/time-series/alignment.ts +0 -7
  323. package/src/time-series/chart-data.ts +145 -0
  324. package/src/time-series/date-window.ts +44 -0
  325. package/src/time-series/history-window.ts +21 -0
  326. package/src/time-series/hooks.test.ts +59 -0
  327. package/src/time-series/hooks.ts +33 -12
  328. package/src/time-series/market-resolution.ts +2 -2
  329. package/src/time-series/market.ts +1 -1
  330. package/src/time-series/range.ts +9 -0
  331. package/src/{components/chart/core → time-series}/resolution.ts +3 -102
  332. package/src/time-series/resolve.test.ts +203 -1
  333. package/src/time-series/resolve.ts +96 -18
  334. package/src/time-series/spec-studies.test.ts +15 -0
  335. package/src/time-series/spec.ts +2 -2
  336. package/src/time-series/studies.ts +5 -2
  337. package/src/time-series/types.ts +3 -1
  338. package/src/time-series/use-chart-resolution.test.tsx +358 -0
  339. package/src/time-series/use-chart-resolution.ts +46 -4
  340. package/src/types/broker.ts +2 -2
  341. package/src/types/data-provider.ts +3 -3
  342. package/src/types/financials.ts +12 -0
  343. package/src/types/plugin.ts +2 -2
  344. package/src/ui/context-menu.tsx +3 -2
  345. package/src/ui/dialog.tsx +2 -2
  346. package/src/utils/html-entities.ts +3 -5
  347. package/src/utils/main-thread-monitor.ts +1 -1
  348. package/src/utils/price-history.ts +5 -5
  349. package/src/version.ts +3 -1
  350. package/src/components/chart/core/viewport.test.ts +0 -8
  351. package/src/components/chart/core/viewport.ts +0 -24
  352. package/src/time-series/index.ts +0 -14
  353. /package/src/{components/chart/core → time-series}/resolution.test.ts +0 -0
@@ -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 {
@@ -1,16 +1,8 @@
1
- /// <reference lib="dom" />
2
1
  import { setCloudApiFetchTransport } from "../../../api-client";
3
2
  import { setHttpFetchTransport } from "../../../utils/http-transport";
3
+ import type { DesktopHttpFetchResponse } from "../shared/protocol";
4
4
  import { backendRequest } from "./backend-rpc";
5
5
 
6
- interface ElectrobunHttpFetchResponse {
7
- status: number;
8
- statusText: string;
9
- headers: Record<string, string>;
10
- setCookie?: string[];
11
- body: string;
12
- }
13
-
14
6
  const CLOUD_MARKET_HTTP_TIMEOUT_MS = 10_000;
15
7
 
16
8
  function normalizeHeaders(headers: HeadersInit | undefined): Record<string, string> {
@@ -81,8 +73,8 @@ async function requestBackendHttpFetch(
81
73
  url: string,
82
74
  init?: RequestInit,
83
75
  timeoutMs?: number,
84
- ): Promise<ElectrobunHttpFetchResponse> {
85
- return backendRequest<ElectrobunHttpFetchResponse>("http.fetch", {
76
+ ): Promise<DesktopHttpFetchResponse> {
77
+ return backendRequest("http.fetch", {
86
78
  url,
87
79
  init: {
88
80
  method: init?.method,
@@ -1,12 +1,11 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
- import { useEffect, useLayoutEffect, useMemo, useRef, useSyncExternalStore, type ReactNode } from "react";
2
+ import { useEffect, useMemo, useSyncExternalStore, type ReactNode } from "react";
4
3
  import {
4
+ createShortcutRegistry,
5
5
  InputHostProvider,
6
- shouldDeliverShortcut,
6
+ useRegisteredShortcut,
7
7
  type InputHost,
8
8
  type KeyEventLike,
9
- type ShortcutOptions,
10
9
  } from "../../../react/input";
11
10
  import {
12
11
  isMouseBackNavigationButton,
@@ -76,16 +75,6 @@ function toMouseBackKeyEventLike(event: MouseEvent): KeyEventLike {
76
75
  };
77
76
  }
78
77
 
79
- interface ShortcutEntry {
80
- handlerRef: { current: (event: KeyEventLike) => void };
81
- enabledRef: { current: boolean };
82
- allowEditableRef: { current: boolean };
83
- phase: NonNullable<ShortcutOptions["phase"]>;
84
- order: number;
85
- }
86
-
87
- let nextShortcutOrder = 1;
88
-
89
78
  function subscribeViewport(listener: () => void): () => void {
90
79
  window.addEventListener("resize", listener);
91
80
  return () => window.removeEventListener("resize", listener);
@@ -103,30 +92,17 @@ function getViewport() {
103
92
  }
104
93
 
105
94
  export function WebInputHostProvider({ children }: { children: ReactNode }) {
106
- const shortcutsRef = useRef<ShortcutEntry[]>([]);
107
-
108
- const dispatchShortcut = (shortcutEvent: KeyEventLike) => {
109
- for (const phase of ["before", "normal", "after"] as const) {
110
- if (phase === "after" && (shortcutEvent.defaultPrevented || shortcutEvent.propagationStopped)) break;
111
- for (const entry of shortcutsRef.current) {
112
- if (entry.phase !== phase || !entry.enabledRef.current) continue;
113
- if (!shouldDeliverShortcut(shortcutEvent, entry.allowEditableRef.current)) continue;
114
- entry.handlerRef.current(shortcutEvent);
115
- if (shortcutEvent.propagationStopped) break;
116
- }
117
- if (shortcutEvent.propagationStopped) break;
118
- }
119
- };
95
+ const shortcutRegistry = useMemo(() => createShortcutRegistry(), []);
120
96
 
121
97
  useEffect(() => {
122
98
  const onKeyDown = (event: KeyboardEvent) => {
123
99
  const shortcutEvent = toKeyEventLike(event);
124
- dispatchShortcut(shortcutEvent);
100
+ shortcutRegistry.dispatch(shortcutEvent);
125
101
  if (shouldConsumeWebAppKeyDown(event)) event.preventDefault();
126
102
  };
127
103
  window.addEventListener("keydown", onKeyDown);
128
104
  return () => window.removeEventListener("keydown", onKeyDown);
129
- }, []);
105
+ }, [shortcutRegistry]);
130
106
 
131
107
  useEffect(() => {
132
108
  const preventBrowserBack = (event: MouseEvent) => {
@@ -138,7 +114,7 @@ export function WebInputHostProvider({ children }: { children: ReactNode }) {
138
114
  if (!isMouseBackNavigationButton(event.button)) return;
139
115
  event.preventDefault();
140
116
  event.stopPropagation();
141
- dispatchShortcut(toMouseBackKeyEventLike(event));
117
+ shortcutRegistry.dispatch(toMouseBackKeyEventLike(event));
142
118
  };
143
119
 
144
120
  window.addEventListener("mousedown", preventBrowserBack, true);
@@ -149,35 +125,16 @@ export function WebInputHostProvider({ children }: { children: ReactNode }) {
149
125
  window.removeEventListener("auxclick", preventBrowserBack, true);
150
126
  window.removeEventListener("mouseup", onMouseUp, true);
151
127
  };
152
- }, []);
128
+ }, [shortcutRegistry]);
153
129
 
154
130
  const host = useMemo<InputHost>(() => ({
155
131
  useShortcut(handler, options) {
156
- const handlerRef = useRef(handler);
157
- const enabledRef = useRef(options?.enabled !== false);
158
- const allowEditableRef = useRef(options?.allowEditable === true);
159
- handlerRef.current = handler;
160
- enabledRef.current = options?.enabled !== false;
161
- allowEditableRef.current = options?.allowEditable === true;
162
-
163
- useLayoutEffect(() => {
164
- const entry: ShortcutEntry = {
165
- handlerRef,
166
- enabledRef,
167
- allowEditableRef,
168
- phase: options?.phase ?? "normal",
169
- order: nextShortcutOrder++,
170
- };
171
- shortcutsRef.current = [...shortcutsRef.current, entry].sort((a, b) => a.order - b.order);
172
- return () => {
173
- shortcutsRef.current = shortcutsRef.current.filter((current) => current !== entry);
174
- };
175
- }, [options?.phase, options?.scope]);
132
+ useRegisteredShortcut(shortcutRegistry, handler, options);
176
133
  },
177
134
  useViewport() {
178
135
  return useSyncExternalStore(subscribeViewport, getViewport, getViewport);
179
136
  },
180
- }), []);
137
+ }), [shortcutRegistry]);
181
138
 
182
139
  return (
183
140
  <InputHostProvider host={host}>
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
 
3
2
  type KeyboardTargetLike = EventTarget & {
4
3
  tagName?: string;
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  /** @jsxImportSource react */
3
2
  import { createRoot } from "react-dom/client";
4
3
  import { App } from "../../../app";
@@ -9,7 +8,6 @@ import { measurePerfAsync } from "../../../utils/perf-marks";
9
8
  import {
10
9
  backendRequest,
11
10
  initElectrobunBackend,
12
- requestMainWindowRemoteControl,
13
11
  setElectrobunRemoteRequestHandler,
14
12
  } from "./backend-rpc";
15
13
  import { installElectrobunAiHost } from "./ai-host";
@@ -30,6 +28,7 @@ import { createApplicationMenuBridge } from "./application-menu-bridge";
30
28
  import { createDesktopDeepLinkBridge } from "./desktop-deeplink-bridge";
31
29
  import { createDesktopWindowBridge } from "./desktop/window/bridge";
32
30
  import { prepareDetachedSnapshot } from "./desktop/window/snapshot";
31
+ import { createElectrobunAppServices } from "./app-services";
33
32
 
34
33
  const rootElement = document.getElementById("root");
35
34
  if (!rootElement) {
@@ -100,7 +99,7 @@ async function boot() {
100
99
  const webUiHost = createWebUiHost(init.desktopPlatform);
101
100
  const remoteControlAdapter = init.windowKind === "main"
102
101
  ? { registerHandler: setElectrobunRemoteRequestHandler }
103
- : { handleRequest: requestMainWindowRemoteControl };
102
+ : undefined;
104
103
  measurePerfAsync("startup.electrobun.root-render", async () => {
105
104
  root.render(
106
105
  <ElectrobunErrorBoundary>
@@ -110,6 +109,7 @@ async function boot() {
110
109
  <WebDialogHostProvider>
111
110
  <App
112
111
  config={config}
112
+ servicesFactory={createElectrobunAppServices}
113
113
  desktopWindowBridge={desktopWindowBridge}
114
114
  desktopApplicationMenuBridge={desktopApplicationMenuBridge}
115
115
  desktopDeepLinkBridge={desktopDeepLinkBridge}
@@ -1,4 +1,3 @@
1
- /// <reference lib="dom" />
2
1
  import type { NativeRendererHost } from "../../../ui";
3
2
  import { WEB_CELL_HEIGHT, WEB_CELL_WIDTH } from "./input-host";
4
3
  import { hasWebCtrlModifier, normalizeWebKeyName, shouldConsumeWebAppKeyDown, webKeySequence } from "./key-event";
@@ -2,6 +2,10 @@ import type {
2
2
  ApplicationMenuSelectMessage,
3
3
  CapabilityEventMessage,
4
4
  ContextMenuSelectMessage,
5
+ DesktopBackendRequestArgs,
6
+ DesktopBackendRequestMethod,
7
+ DesktopBackendRequestPayload,
8
+ DesktopBackendRequestResponse,
5
9
  DesktopDockPreviewMessage,
6
10
  DesktopRestartMessage,
7
11
  DesktopStateMessage,
@@ -16,7 +20,15 @@ function unsubscribe(): () => void {
16
20
  return () => {};
17
21
  }
18
22
 
19
- export async function backendRequest<T = unknown>(): Promise<T> {
23
+ export function backendRequest<T = unknown>(
24
+ method: "capability.invoke",
25
+ payload: DesktopBackendRequestPayload<"capability.invoke">,
26
+ ): Promise<T>;
27
+ export function backendRequest<K extends Exclude<DesktopBackendRequestMethod, "capability.invoke">>(
28
+ method: K,
29
+ ...args: DesktopBackendRequestArgs<K>
30
+ ): Promise<DesktopBackendRequestResponse<K>>;
31
+ export async function backendRequest(): Promise<unknown> {
20
32
  throw new Error("Electrobun backend requests are unavailable in the CLI screenshot renderer.");
21
33
  }
22
34
 
@@ -10,11 +10,22 @@ export interface NativeOccluder {
10
10
  zIndex: number;
11
11
  }
12
12
 
13
+ export interface NativeLocalOccluder {
14
+ id: string;
15
+ paneId: string;
16
+ rect: { x: number; y: number; width: number; height: number };
17
+ }
18
+
19
+ // Must expose every public method of the terminal manager: the desktop view
20
+ // bundle swaps this module in, so a missing method is a runtime crash.
21
+ // native-stubs.test.ts guards the drift.
13
22
  export class NativeSurfaceManager {
14
23
  setWindowState(): void {}
15
24
  upsertSurface(): void {}
16
25
  updateSurfaceGeometry(): void {}
17
26
  removeSurface(): void {}
27
+ upsertLocalOccluder(): void {}
28
+ removeLocalOccluder(): void {}
18
29
  destroy(): void {}
19
30
  }
20
31