gloomberb 0.10.1 → 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 (351) 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/remote/asset-data-client.ts +6 -44
  274. package/src/renderers/electrobun/view/remote/persistence.ts +6 -2
  275. package/src/renderers/electrobun/view/remote/quote-subscription-registry.test.ts +86 -0
  276. package/src/renderers/electrobun/view/remote/quote-subscription-registry.ts +72 -0
  277. package/src/renderers/electrobun/view/remote/ticker-repository.ts +2 -2
  278. package/src/renderers/electrobun/view/toast-host.tsx +0 -1
  279. package/src/renderers/electrobun/view/ui-host.tsx +1 -2
  280. package/src/renderers/electrobun/view/update-host.ts +1 -1
  281. package/src/renderers/opentui/chart-surface.tsx +2 -1
  282. package/src/renderers/opentui/dialog-host.tsx +20 -4
  283. package/src/renderers/opentui/image/loader.ts +12 -2
  284. package/src/renderers/opentui/input-host.tsx +7 -49
  285. package/src/renderers/opentui/start.tsx +2 -0
  286. package/src/sources/gloomberb-cloud/history-quality.ts +44 -0
  287. package/src/sources/gloomberb-cloud/index.test.ts +173 -0
  288. package/src/sources/gloomberb-cloud/index.ts +42 -20
  289. package/src/sources/gloomberb-cloud/normalizers.ts +9 -3
  290. package/src/sources/provider-router/cache.ts +1 -1
  291. package/src/sources/provider-router/document-routes.ts +10 -5
  292. package/src/sources/provider-router/financials.test.ts +14 -0
  293. package/src/sources/provider-router/financials.ts +6 -1
  294. package/src/sources/provider-router/history.test.ts +163 -1
  295. package/src/sources/provider-router/history.ts +278 -347
  296. package/src/sources/provider-router/index.ts +2 -2
  297. package/src/sources/provider-router/routing.test.ts +39 -0
  298. package/src/sources/provider-router/routing.ts +24 -11
  299. package/src/sources/provider-router/supplemental.ts +37 -17
  300. package/src/sources/sec-edgar/content.ts +2 -12
  301. package/src/sources/sec-edgar.ts +2 -13
  302. package/src/sources/yahoo-finance/history.ts +2 -2
  303. package/src/sources/yahoo-finance/options.ts +5 -0
  304. package/src/sources/yahoo-finance.ts +2 -2
  305. package/src/state/app/bootstrap.ts +2 -2
  306. package/src/state/app/context/index.tsx +2 -2
  307. package/src/state/hooks/quote-streaming.ts +66 -1
  308. package/src/state/hooks/session-snapshot.ts +2 -2
  309. package/src/state/selectors.ts +0 -9
  310. package/src/sync/controller.test.ts +98 -0
  311. package/src/sync/controller.ts +29 -5
  312. package/src/sync/react.ts +2 -2
  313. package/src/sync/types.ts +2 -2
  314. package/src/theme/colors.test.ts +10 -0
  315. package/src/theme/colors.ts +75 -78
  316. package/src/theme/theme-context.test.tsx +67 -0
  317. package/src/theme/theme-context.tsx +9 -5
  318. package/src/tickers/open-target.ts +2 -2
  319. package/src/tickers/search/upsert.ts +2 -2
  320. package/src/time-series/alignment.ts +0 -7
  321. package/src/time-series/chart-data.ts +145 -0
  322. package/src/time-series/date-window.ts +44 -0
  323. package/src/time-series/history-window.ts +21 -0
  324. package/src/time-series/hooks.test.ts +59 -0
  325. package/src/time-series/hooks.ts +33 -12
  326. package/src/time-series/market-resolution.ts +2 -2
  327. package/src/time-series/market.ts +1 -1
  328. package/src/time-series/range.ts +9 -0
  329. package/src/{components/chart/core → time-series}/resolution.ts +3 -102
  330. package/src/time-series/resolve.test.ts +203 -1
  331. package/src/time-series/resolve.ts +96 -18
  332. package/src/time-series/spec-studies.test.ts +15 -0
  333. package/src/time-series/spec.ts +2 -2
  334. package/src/time-series/studies.ts +5 -2
  335. package/src/time-series/types.ts +3 -1
  336. package/src/time-series/use-chart-resolution.test.tsx +358 -0
  337. package/src/time-series/use-chart-resolution.ts +46 -4
  338. package/src/types/broker.ts +2 -2
  339. package/src/types/data-provider.ts +3 -3
  340. package/src/types/financials.ts +12 -0
  341. package/src/types/plugin.ts +2 -2
  342. package/src/ui/context-menu.tsx +3 -2
  343. package/src/ui/dialog.tsx +2 -2
  344. package/src/utils/html-entities.ts +3 -5
  345. package/src/utils/main-thread-monitor.ts +1 -1
  346. package/src/utils/price-history.ts +5 -5
  347. package/src/version.ts +3 -1
  348. package/src/components/chart/core/viewport.test.ts +0 -8
  349. package/src/components/chart/core/viewport.ts +0 -24
  350. package/src/time-series/index.ts +0 -14
  351. /package/src/{components/chart/core → time-series}/resolution.test.ts +0 -0
@@ -1,3 +1,5 @@
1
+ import type { DesktopBackendRequestPayload, DesktopHttpFetchResponse } from "../../shared/protocol";
2
+
1
3
  function normalizeHttpFetchHeaders(headers: unknown): Record<string, string> {
2
4
  if (!headers || typeof headers !== "object" || Array.isArray(headers)) {
3
5
  return {};
@@ -8,7 +10,9 @@ function normalizeHttpFetchHeaders(headers: unknown): Record<string, string> {
8
10
  );
9
11
  }
10
12
 
11
- export async function handleHttpFetch(payload: Record<string, unknown>) {
13
+ export async function handleHttpFetch(
14
+ payload: DesktopBackendRequestPayload<"http.fetch">,
15
+ ): Promise<DesktopHttpFetchResponse> {
12
16
  if (typeof payload.url !== "string") {
13
17
  throw new Error("http.fetch requires a URL.");
14
18
  }
@@ -24,6 +24,7 @@ import {
24
24
  MAIN_WINDOW_RPC_KEY,
25
25
  paneIdFromDetachedRpcKey,
26
26
  } from "../window/focus";
27
+ import type { DesktopBackendRequestPayload, ElectrobunBackendInit } from "../../shared/protocol";
27
28
 
28
29
  interface DesktopWindowTarget {
29
30
  kind: "main" | "detached";
@@ -39,7 +40,7 @@ interface InitializeDesktopBackendOptions<TRpc> {
39
40
  getSessionSnapshot: () => AppSessionSnapshot | null;
40
41
  getThemePreview: () => DesktopThemePreviewState;
41
42
  markWindowRpcReady: (rpc: TRpc) => void;
42
- payload: Record<string, unknown>;
43
+ payload: DesktopBackendRequestPayload<"init">;
43
44
  reconcileDetachedWindows: () => void;
44
45
  registerCoreCapabilities: () => void;
45
46
  rpc: TRpc;
@@ -50,14 +51,14 @@ interface InitializeDesktopBackendOptions<TRpc> {
50
51
  }
51
52
 
52
53
  interface InitializationPayloadOptions {
53
- desktopThemePreview?: DesktopThemePreviewState;
54
+ desktopThemePreview: DesktopThemePreviewState;
54
55
  getDesktopSnapshot: () => DesktopSharedStateSnapshot | null;
55
56
  getSessionSnapshot: () => AppSessionSnapshot | null;
56
57
  }
57
58
 
58
59
  function normalizeInitWindowTarget<TRpc>(
59
60
  rpc: TRpc,
60
- payload: Record<string, unknown>,
61
+ payload: DesktopBackendRequestPayload<"init">,
61
62
  getRpcWindowKey: (rpc: TRpc) => string | undefined,
62
63
  ): DesktopWindowTarget {
63
64
  const rpcKey = getRpcWindowKey(rpc);
@@ -85,7 +86,7 @@ function buildInitializationPayload(
85
86
  services: AppServices,
86
87
  windowTarget: DesktopWindowTarget,
87
88
  options: InitializationPayloadOptions,
88
- ) {
89
+ ): ElectrobunBackendInit {
89
90
  return {
90
91
  config,
91
92
  sessionSnapshot: options.getSessionSnapshot(),
@@ -109,7 +110,7 @@ async function resolveDesktopDataDir(): Promise<string> {
109
110
 
110
111
  export async function initializeDesktopBackend<TRpc>(
111
112
  options: InitializeDesktopBackendOptions<TRpc>,
112
- ) {
113
+ ): Promise<ElectrobunBackendInit> {
113
114
  const windowTarget = normalizeInitWindowTarget(options.rpc, options.payload, options.getRpcWindowKey);
114
115
  options.markWindowRpcReady(options.rpc);
115
116
 
@@ -120,7 +121,11 @@ export async function initializeDesktopBackend<TRpc>(
120
121
  options.setDesktopWorkspace(createDesktopWorkspace(currentConfig, options.getSessionSnapshot()));
121
122
  options.reconcileDetachedWindows();
122
123
  }
123
- return buildInitializationPayload(currentConfig, currentServices, windowTarget, options);
124
+ return buildInitializationPayload(currentConfig, currentServices, windowTarget, {
125
+ getDesktopSnapshot: options.getDesktopSnapshot,
126
+ getSessionSnapshot: options.getSessionSnapshot,
127
+ desktopThemePreview: options.getThemePreview(),
128
+ });
124
129
  }
125
130
 
126
131
  options.setCurrentConfig(await initDataDir(await resolveDesktopDataDir()));
@@ -1,9 +1,11 @@
1
- import type { LiveStreamResolveRequest, ResolvedLiveStream } from "../../../../types/media";
1
+ import type { ResolvedLiveStream } from "../../../../types/media";
2
2
  import { getTvChannel, TV_CHANNELS } from "../../../../plugins/builtin/tv/channels";
3
3
  import { resolveTvStream } from "../../../../plugins/builtin/tv/youtube-stream";
4
+ import type { DesktopBackendRequestPayload } from "../../shared/protocol";
4
5
 
5
- export async function resolveDesktopLiveStream(payload: Record<string, unknown>): Promise<ResolvedLiveStream> {
6
- const request = payload as Partial<LiveStreamResolveRequest>;
6
+ export async function resolveDesktopLiveStream(
7
+ request: DesktopBackendRequestPayload<"media.resolveLiveStream">,
8
+ ): Promise<ResolvedLiveStream> {
7
9
  if (request.provider !== "youtube") {
8
10
  throw new Error("Unsupported live-stream provider.");
9
11
  }
@@ -1,17 +1,11 @@
1
1
  import type { PluginRegistry } from "../../../../plugins/registry";
2
2
  import type { PersistedAuthUser } from "../../../../api-client";
3
3
  import { apiClient } from "../../../../api-client";
4
-
5
- interface PluginStateBackendSetEntry {
6
- pluginId: string;
7
- key: string;
8
- value: unknown;
9
- schemaVersion?: number;
10
- }
4
+ import type { DesktopPluginStateRequest, DesktopPluginStateSetEntry } from "../../shared/protocol";
11
5
 
12
6
  interface PluginStateBackendStore {
13
7
  set(pluginId: string, key: string, value: unknown, schemaVersion?: number): void;
14
- setMany(entries: PluginStateBackendSetEntry[]): void;
8
+ setMany(entries: DesktopPluginStateSetEntry[]): void;
15
9
  delete(pluginId: string, key: string): void;
16
10
  }
17
11
 
@@ -20,7 +14,7 @@ interface PersistedCloudSession {
20
14
  user?: PersistedAuthUser | null;
21
15
  }
22
16
 
23
- function normalizePluginStateSetEntry(entry: unknown): PluginStateBackendSetEntry | null {
17
+ function normalizePluginStateSetEntry(entry: unknown): DesktopPluginStateSetEntry | null {
24
18
  if (!entry || typeof entry !== "object") return null;
25
19
  const record = entry as Record<string, unknown>;
26
20
  if (typeof record.pluginId !== "string" || typeof record.key !== "string") return null;
@@ -47,17 +41,18 @@ function syncBackendCloudAuthState(pluginId: string, key: string, value: unknown
47
41
 
48
42
  export function handleDesktopPluginStateRequest(
49
43
  store: PluginStateBackendStore,
50
- method: string,
51
- payload: Record<string, unknown>,
44
+ request: DesktopPluginStateRequest,
52
45
  ): null {
53
- switch (method) {
54
- case "pluginState.set":
55
- store.set(payload.pluginId as string, payload.key as string, payload.value, payload.schemaVersion as number | undefined);
56
- syncBackendCloudAuthState(payload.pluginId as string, payload.key as string, payload.value);
46
+ switch (request.method) {
47
+ case "pluginState.set": {
48
+ const { pluginId, key, value, schemaVersion } = request.payload;
49
+ store.set(pluginId, key, value, schemaVersion);
50
+ syncBackendCloudAuthState(pluginId, key, value);
57
51
  return null;
52
+ }
58
53
  case "pluginState.setMany": {
59
- const entries = Array.isArray(payload.entries)
60
- ? payload.entries.flatMap((entry) => {
54
+ const entries = Array.isArray(request.payload.entries)
55
+ ? request.payload.entries.flatMap((entry) => {
61
56
  const normalized = normalizePluginStateSetEntry(entry);
62
57
  return normalized ? [normalized] : [];
63
58
  })
@@ -68,12 +63,16 @@ export function handleDesktopPluginStateRequest(
68
63
  }
69
64
  return null;
70
65
  }
71
- case "pluginState.delete":
72
- store.delete(payload.pluginId as string, payload.key as string);
73
- syncBackendCloudAuthState(payload.pluginId as string, payload.key as string, null);
66
+ case "pluginState.delete": {
67
+ const { pluginId, key } = request.payload;
68
+ store.delete(pluginId, key);
69
+ syncBackendCloudAuthState(pluginId, key, null);
74
70
  return null;
75
- default:
76
- throw new Error(`Unknown plugin state method: ${method}`);
71
+ }
72
+ default: {
73
+ const exhaustive: never = request;
74
+ throw new Error(`Unknown plugin state method: ${String(exhaustive)}`);
75
+ }
77
76
  }
78
77
  }
79
78
 
@@ -1,4 +1,6 @@
1
- export type DesktopWindowControlAction = "minimize" | "toggle-maximize" | "close";
1
+ import type { DesktopWindowControlAction } from "../../shared/protocol";
2
+
3
+ export type { DesktopWindowControlAction } from "../../shared/protocol";
2
4
 
3
5
  interface DesktopWindowFrame {
4
6
  x: number;
@@ -1,8 +1,8 @@
1
- import type { PaneRuntimeState } from "../../../../../core/state/app/state";
2
1
  import type {
3
2
  DesktopSharedStateSnapshot,
4
3
  DesktopThemePreviewState,
5
4
  } from "../../../../../types/desktop-window";
5
+ import type { DesktopWorkspaceRequest } from "../../../shared/protocol";
6
6
  import type { DesktopWorkspace } from "./index";
7
7
  import type { WindowFrame } from "../../window/frame";
8
8
 
@@ -10,8 +10,7 @@ type DockEdge = "left" | "right" | "top" | "bottom";
10
10
 
11
11
  interface DesktopWorkspaceRequestOptions {
12
12
  workspace: DesktopWorkspace;
13
- method: string;
14
- payload: Record<string, unknown>;
13
+ request: DesktopWorkspaceRequest;
15
14
  setCurrentConfig: (config: DesktopSharedStateSnapshot["config"]) => void;
16
15
  sendThemePreview: (preview: DesktopThemePreviewState) => void;
17
16
  clearDockPreview: (paneId?: string) => void;
@@ -25,7 +24,7 @@ interface DesktopWorkspaceRequestOptions {
25
24
  focusDetachedPane: (paneId: string) => void;
26
25
  }
27
26
 
28
- function requirePaneId(payload: Record<string, unknown>, method: string): string {
27
+ function requirePaneId(payload: { paneId: string }, method: string): string {
29
28
  if (typeof payload.paneId !== "string") {
30
29
  throw new Error(`${method} requires paneId.`);
31
30
  }
@@ -40,8 +39,7 @@ function normalizeDockEdge(edge: unknown): DockEdge | undefined {
40
39
 
41
40
  export async function handleDesktopWorkspaceRequest({
42
41
  workspace,
43
- method,
44
- payload,
42
+ request,
45
43
  setCurrentConfig,
46
44
  sendThemePreview,
47
45
  clearDockPreview,
@@ -51,47 +49,49 @@ export async function handleDesktopWorkspaceRequest({
51
49
  resolveDetachedFrame,
52
50
  focusDetachedPane,
53
51
  }: DesktopWorkspaceRequestOptions): Promise<null> {
54
- switch (method) {
52
+ switch (request.method) {
55
53
  case "desktop.syncMainState": {
56
- const snapshot = workspace.syncMainState(payload.snapshot as DesktopSharedStateSnapshot);
54
+ const snapshot = workspace.syncMainState(request.payload.snapshot);
57
55
  setCurrentConfig(snapshot.config);
58
56
  reconcileDetachedWindows();
59
57
  sendDesktopState(snapshot);
60
58
  return null;
61
59
  }
62
60
  case "desktop.setThemePreview":
63
- sendThemePreview((payload.preview ?? { theme: null }) as DesktopThemePreviewState);
61
+ sendThemePreview(request.payload.preview ?? { theme: null });
64
62
  return null;
65
63
  case "desktop.replaceDetachedPaneState": {
66
- const paneId = requirePaneId(payload, method);
67
- sendDesktopState(workspace.replaceDetachedPaneState(paneId, payload.paneState as PaneRuntimeState));
64
+ const paneId = requirePaneId(request.payload, request.method);
65
+ sendDesktopState(workspace.replaceDetachedPaneState(paneId, request.payload.paneState));
68
66
  return null;
69
67
  }
70
68
  case "desktop.popOutPane": {
71
- const paneId = requirePaneId(payload, method);
69
+ const paneId = requirePaneId(request.payload, request.method);
72
70
  const snapshot = workspace.popOutPane(paneId, resolveDetachedFrame(paneId));
73
71
  await commitDesktopSnapshot(snapshot);
74
72
  focusDetachedPane(paneId);
75
73
  return null;
76
74
  }
77
75
  case "desktop.dockDetachedPane": {
78
- const paneId = requirePaneId(payload, method);
76
+ const paneId = requirePaneId(request.payload, request.method);
79
77
  clearDockPreview(paneId);
80
- await commitDesktopSnapshot(workspace.dockDetachedPane(paneId, normalizeDockEdge(payload.edge)));
78
+ await commitDesktopSnapshot(workspace.dockDetachedPane(paneId, normalizeDockEdge(request.payload.edge)));
81
79
  return null;
82
80
  }
83
81
  case "desktop.closeDetachedPane": {
84
- const paneId = requirePaneId(payload, method);
82
+ const paneId = requirePaneId(request.payload, request.method);
85
83
  clearDockPreview(paneId);
86
84
  await commitDesktopSnapshot(workspace.closeDetachedPane(paneId));
87
85
  return null;
88
86
  }
89
87
  case "desktop.focusDetachedPane": {
90
- const paneId = requirePaneId(payload, method);
88
+ const paneId = requirePaneId(request.payload, request.method);
91
89
  focusDetachedPane(paneId);
92
90
  return null;
93
91
  }
94
- default:
95
- throw new Error(`Unknown desktop method: ${method}`);
92
+ default: {
93
+ const exhaustive: never = request;
94
+ throw new Error(`Unknown desktop method: ${String(exhaustive)}`);
95
+ }
96
96
  }
97
97
  }
@@ -12,7 +12,13 @@ import type { AppSessionSnapshot } from "../../../core/state/session-persistence
12
12
  import { syncConfigActiveLayoutState, type PaneRuntimeState } from "../../../core/state/app/state";
13
13
  import type { DesktopSharedStateSnapshot, DesktopThemePreviewState } from "../../../types/desktop-window";
14
14
  import type { UpdateProgress } from "../../../updater";
15
- import { ELECTROBUN_CONTEXT_MENU_ACTION, type DesktopRestartMessage, type ElectrobunDesktopRpcSchema } from "../shared/protocol";
15
+ import {
16
+ ELECTROBUN_CONTEXT_MENU_ACTION,
17
+ type DesktopBackendRequest,
18
+ type DesktopBackendRequestPayload,
19
+ type DesktopRestartMessage,
20
+ type ElectrobunDesktopRpcSchema,
21
+ } from "../shared/protocol";
16
22
  import { decodeRpcValue, encodeRpcValue } from "../view/rpc-codec";
17
23
  import { contextMenuSelectionMessage } from "./context-menu/click";
18
24
  import type { DesktopWorkspace } from "./desktop/workspace";
@@ -388,7 +394,7 @@ function controlWindowForRpcKey(windowKey: string | undefined, action: DesktopWi
388
394
 
389
395
  async function initialize(
390
396
  rpc: DesktopRpc,
391
- payload: Record<string, unknown>,
397
+ payload: DesktopBackendRequestPayload<"init">,
392
398
  ) {
393
399
  const init = await initializeDesktopBackend({
394
400
  getCurrentConfig: () => currentConfig,
@@ -423,88 +429,116 @@ async function initialize(
423
429
 
424
430
  async function handleBackendRequest(
425
431
  rpc: DesktopRpc,
426
- method: string,
427
- rawPayload: unknown,
432
+ request: DesktopBackendRequest,
428
433
  ) {
429
- const payload = decodeRpcValue<Record<string, unknown>>(rawPayload ?? {});
430
-
431
- if (method === "init") return initialize(rpc, payload);
432
- if (method === "http.fetch") return handleHttpFetch(payload);
433
- if (method === "media.resolveLiveStream") return resolveDesktopLiveStream(payload);
434
- if (method === "remote.forward") {
435
- return forwardRemoteControlRequest(payload.request as RemoteControlRequest);
436
- }
437
- if (method.startsWith("capability.")) return capabilityBridge.handle(rpc, method, payload);
438
- if (method.startsWith("desktop.")) {
439
- return handleDesktopWorkspaceRequest({
440
- workspace: requireDesktopWorkspace(),
441
- method,
442
- payload,
443
- setCurrentConfig,
444
- sendThemePreview,
445
- clearDockPreview,
446
- sendDesktopState,
447
- reconcileDetachedWindows,
448
- commitDesktopSnapshot,
449
- resolveDetachedFrame: (paneId) => detachedWindowManager.resolveFrame(paneId),
450
- focusDetachedPane: (paneId) => detachedWindowManager.focusDetachedPane(paneId),
451
- });
452
- }
453
- if (method.startsWith("pluginState.")) {
454
- return handleDesktopPluginStateRequest(requireServices().persistence.pluginState, method, payload);
455
- }
456
- if (method.startsWith("host.")) {
457
- return handleDesktopHostRequest({
458
- clearMainWindow: () => {
459
- mainWindow = null;
460
- },
461
- closeAllDetachedWindows,
462
- focusWindowForRpcKey: (windowKey) => detachedWindowManager.focusWindowForRpcKey(windowKey),
463
- getMainWindow: () => mainWindow,
464
- getRpcWindowKey,
465
- method,
466
- payload,
467
- restartDesktopApp,
468
- rpc,
469
- teardownServices,
470
- controlWindowForRpcKey,
471
- trackContextMenuRequest: (requestId, targetRpc) => {
472
- contextMenuRequestRpcs.clear();
473
- contextMenuRequestRpcs.set(requestId, targetRpc);
474
- },
475
- });
434
+ switch (request.method) {
435
+ case "init":
436
+ return initialize(rpc, request.payload);
437
+ case "http.fetch":
438
+ return handleHttpFetch(request.payload);
439
+ case "media.resolveLiveStream":
440
+ return resolveDesktopLiveStream(request.payload);
441
+ case "remote.forward":
442
+ return forwardRemoteControlRequest(request.payload.request);
443
+ case "capability.invoke":
444
+ case "capability.subscribe":
445
+ case "capability.unsubscribe":
446
+ return capabilityBridge.handle(rpc, request);
447
+ case "desktop.syncMainState":
448
+ case "desktop.setThemePreview":
449
+ case "desktop.replaceDetachedPaneState":
450
+ case "desktop.popOutPane":
451
+ case "desktop.dockDetachedPane":
452
+ case "desktop.closeDetachedPane":
453
+ case "desktop.focusDetachedPane":
454
+ return handleDesktopWorkspaceRequest({
455
+ workspace: requireDesktopWorkspace(),
456
+ request,
457
+ setCurrentConfig,
458
+ sendThemePreview,
459
+ clearDockPreview,
460
+ sendDesktopState,
461
+ reconcileDetachedWindows,
462
+ commitDesktopSnapshot,
463
+ resolveDetachedFrame: (paneId) => detachedWindowManager.resolveFrame(paneId),
464
+ focusDetachedPane: (paneId) => detachedWindowManager.focusDetachedPane(paneId),
465
+ });
466
+ case "pluginState.set":
467
+ case "pluginState.setMany":
468
+ case "pluginState.delete":
469
+ return handleDesktopPluginStateRequest(requireServices().persistence.pluginState, request);
470
+ case "host.restart":
471
+ case "host.exit":
472
+ case "host.windowControl":
473
+ case "host.openExternal":
474
+ case "host.copyText":
475
+ case "host.focusWindow":
476
+ case "host.copyPngImage":
477
+ case "host.readText":
478
+ case "host.notify":
479
+ case "host.showContextMenu":
480
+ return handleDesktopHostRequest({
481
+ clearMainWindow: () => {
482
+ mainWindow = null;
483
+ },
484
+ closeAllDetachedWindows,
485
+ focusWindowForRpcKey: (windowKey) => detachedWindowManager.focusWindowForRpcKey(windowKey),
486
+ getMainWindow: () => mainWindow,
487
+ getRpcWindowKey,
488
+ request,
489
+ restartDesktopApp,
490
+ rpc,
491
+ teardownServices,
492
+ controlWindowForRpcKey,
493
+ trackContextMenuRequest: (requestId, targetRpc) => {
494
+ contextMenuRequestRpcs.clear();
495
+ contextMenuRequestRpcs.set(requestId, targetRpc);
496
+ },
497
+ });
498
+ case "update.check":
499
+ case "update.start":
500
+ case "ticker.loadAll":
501
+ case "ticker.load":
502
+ case "ticker.save":
503
+ case "ticker.delete":
504
+ case "config.save":
505
+ case "config.resetAllData":
506
+ case "config.export":
507
+ case "config.import":
508
+ case "session.set":
509
+ case "session.delete":
510
+ return handleDesktopBackendRequest({
511
+ clearCurrentConfig: () => {
512
+ currentConfig = null;
513
+ },
514
+ closeAllDetachedWindows,
515
+ commitDesktopSnapshot,
516
+ getConfig: requireConfig,
517
+ getDesktopWorkspace: () => desktopWorkspace,
518
+ getServices: requireServices,
519
+ getSessionSnapshot,
520
+ request,
521
+ reconcileDetachedWindows,
522
+ registerCoreCapabilities,
523
+ sendDesktopState,
524
+ setCurrentConfig,
525
+ setDesktopWorkspace: (workspace) => {
526
+ desktopWorkspace = workspace;
527
+ },
528
+ setServices: (nextServices) => {
529
+ services = nextServices;
530
+ },
531
+ startUpdate: (currentVersion) => {
532
+ void runDesktopUpdate(rpc, currentVersion);
533
+ },
534
+ syncConfigAccessors,
535
+ teardownServices,
536
+ });
537
+ default: {
538
+ const exhaustive: never = request;
539
+ throw new Error(`Unknown backend request: ${String(exhaustive)}`);
540
+ }
476
541
  }
477
-
478
- const backendResult = await handleDesktopBackendRequest({
479
- clearCurrentConfig: () => {
480
- currentConfig = null;
481
- },
482
- closeAllDetachedWindows,
483
- commitDesktopSnapshot,
484
- getConfig: requireConfig,
485
- getDesktopWorkspace: () => desktopWorkspace,
486
- getServices: requireServices,
487
- getSessionSnapshot,
488
- method,
489
- payload,
490
- reconcileDetachedWindows,
491
- registerCoreCapabilities,
492
- sendDesktopState,
493
- setCurrentConfig,
494
- setDesktopWorkspace: (workspace) => {
495
- desktopWorkspace = workspace;
496
- },
497
- setServices: (nextServices) => {
498
- services = nextServices;
499
- },
500
- startUpdate: (currentVersion) => {
501
- void runDesktopUpdate(rpc, currentVersion);
502
- },
503
- syncConfigAccessors,
504
- teardownServices,
505
- });
506
- if (backendResult.handled) return backendResult.value;
507
- throw new Error(`Unknown backend method: ${method}`);
508
542
  }
509
543
 
510
544
  function installApplicationMenu() {
@@ -516,7 +550,13 @@ function createWindowRpc(key: string): DesktopRpc {
516
550
  rpc = BrowserView.defineRPC<ElectrobunDesktopRpcSchema>({
517
551
  handlers: {
518
552
  requests: {
519
- "backend.request": async ({ method, payload }) => encodeRpcValue(await handleBackendRequest(rpc, method, payload)),
553
+ "backend.request": async ({ method, payload }) => {
554
+ const request = {
555
+ method,
556
+ payload: decodeRpcValue(payload ?? null),
557
+ } as DesktopBackendRequest;
558
+ return encodeRpcValue(await handleBackendRequest(rpc, request));
559
+ },
520
560
  },
521
561
  messages: {
522
562
  "host.restart": (message) => {