From 3a9aef425c05c3c5c095238558900c2fe8046ed8 Mon Sep 17 00:00:00 2001 From: batz Date: Fri, 6 Mar 2026 11:35:23 +0100 Subject: [PATCH 1/2] Add Electrobun hybrid WGPU showcase --- .github/release/RELEASE-GATE-CHECKLIST.md | 2 +- .github/workflows/ci.yml | 6 +- .github/workflows/electrobun-dogfood.yml | 2 +- ELECTROBUN-SHOWCASE.md | 18 +- RUNTIME-PORTABILITY-READINESS.md | 5 +- examples/electrobun-counter/README.md | 19 +- examples/electrobun-counter/index.html | 241 ++++++++++++++---- examples/electrobun-counter/package.json | 10 +- .../electrobun-counter/src/hybrid-wgpu.ts | 133 ++++++++++ examples/electrobun-counter/src/index.ts | 9 +- 10 files changed, 378 insertions(+), 67 deletions(-) create mode 100644 examples/electrobun-counter/src/hybrid-wgpu.ts diff --git a/.github/release/RELEASE-GATE-CHECKLIST.md b/.github/release/RELEASE-GATE-CHECKLIST.md index 102f369..ae39692 100644 --- a/.github/release/RELEASE-GATE-CHECKLIST.md +++ b/.github/release/RELEASE-GATE-CHECKLIST.md @@ -34,7 +34,7 @@ Use this as the canonical gate artifact for `0.3.0+` releases. It complements - [ ] Candidate was promoted from `development` after successful staging deploy (`gametau-dev`) and smoke checks. - [ ] Scope issues are closed (or explicitly deferred with notes). -- [ ] `CI` run on `master` is green for all required jobs (`MSRV`, `Rust`, `TypeScript`, `Lint`, `API Docs`, `Scaffold & Build Smoke`, `Electrobun Browser + GPU Smoke`, `Publish Preflight`, `Release Gate Contract`). +- [ ] `CI` run on `master` is green for all required jobs (`MSRV`, `Rust`, `TypeScript`, `Lint`, `API Docs`, `Scaffold & Build Smoke`, `Electrobun Hybrid + GPU Smoke`, `Publish Preflight`, `Release Gate Contract`). - [ ] `create-gametau` template architecture checks pass (service seams + scaffold tests). - [ ] `CHANGELOG.md`, `README.md`, and docs reflect the release narrative and compatibility notes. - [ ] Version manifests are aligned across workspace crates, npm packages, and templates. diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ec9a5a..e78d97e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -231,7 +231,7 @@ jobs: test -f dist/index.html electrobun-smoke: - name: Electrobun Browser + GPU Smoke + name: Electrobun Hybrid + GPU Smoke runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -256,10 +256,10 @@ jobs: bun run build:web test -d dist test -f dist/index.html - - name: Build Electrobun counter (BrowserWindow package target) + - name: Build Electrobun counter (hybrid BrowserWindow package target) run: | cd examples/electrobun-counter - bun run build:electrobun:browser + bun run build:electrobun:hybrid test -d build - name: Build Electrobun counter (GpuWindow package target) run: | diff --git a/.github/workflows/electrobun-dogfood.yml b/.github/workflows/electrobun-dogfood.yml index 29e1016..538c059 100644 --- a/.github/workflows/electrobun-dogfood.yml +++ b/.github/workflows/electrobun-dogfood.yml @@ -66,7 +66,7 @@ jobs: cd examples/electrobun-counter bun install bun run build:web 2>&1 | tee ../../electrobun-smoke-results.log - bun run build:electrobun:browser 2>&1 | tee -a ../../electrobun-smoke-results.log + bun run build:electrobun:hybrid 2>&1 | tee -a ../../electrobun-smoke-results.log bun run build:electrobun:gpu 2>&1 | tee -a ../../electrobun-smoke-results.log else echo "Electrobun counter example not found, skipping." | tee electrobun-smoke-results.log diff --git a/ELECTROBUN-SHOWCASE.md b/ELECTROBUN-SHOWCASE.md index d7c7b72..0b6f26c 100644 --- a/ELECTROBUN-SHOWCASE.md +++ b/ELECTROBUN-SHOWCASE.md @@ -6,7 +6,7 @@ This is the quickest way to try gametau running in Electrobun after the upstream gametau now supports two Electrobun shell shapes: -- `BrowserWindow` for the current web-first app path +- `BrowserWindow` + embedded `` for the hybrid path - `GpuWindow` for a native WGPU shell that still reuses the shared Rust/WASM backend loop The fastest reference example is [`examples/electrobun-counter`](./examples/electrobun-counter). @@ -21,10 +21,10 @@ bun run --cwd packages/webtau build bun run --cwd packages/webtau-vite build ``` -Launch the BrowserWindow path: +Launch the hybrid BrowserWindow path: ```bash -bun run --cwd examples/electrobun-counter dev:electrobun:browser +bun run --cwd examples/electrobun-counter dev:electrobun:hybrid ``` Launch the GPUWindow path: @@ -35,9 +35,17 @@ bun run --cwd examples/electrobun-counter dev:electrobun:gpu What you should see: -- Browser mode: a native Electrobun window loads the Vite app and the counter UI works normally. +- Hybrid mode: a native Electrobun window loads the Vite app, the counter UI works normally, and an embedded native WGPU surface runs inside the same BrowserWindow. - GPU mode: a native `GpuWindow` opens and the shared counter state advances through the WASM-backed game loop. +## Why hybrid first + +This path is sequenced before pure `GpuWindow` because it proves the render/UI split with lower risk: + +- existing DOM HUD and controls stay intact +- native WGPU rendering is exercised in the same app shell +- resize, masking, passthrough, and overlay behavior can be validated before broader renderer refactors + ## Scaffold a project ```bash @@ -55,7 +63,7 @@ The generated scaffold keeps the web/Tauri path intact and adds: ## Build desktop packages ```bash -bun run --cwd examples/electrobun-counter build:electrobun:browser +bun run --cwd examples/electrobun-counter build:electrobun:hybrid bun run --cwd examples/electrobun-counter build:electrobun:gpu ``` diff --git a/RUNTIME-PORTABILITY-READINESS.md b/RUNTIME-PORTABILITY-READINESS.md index 38c95a6..a5ae06c 100644 --- a/RUNTIME-PORTABILITY-READINESS.md +++ b/RUNTIME-PORTABILITY-READINESS.md @@ -32,13 +32,13 @@ bunx create-gametau my-game --desktop-shell electrobun --electrobun-mode dual Current supported shapes: -- `BrowserWindow` shell for the existing web-first app structure +- `BrowserWindow` shell with embedded `` for hybrid UI/native-render composition - `GpuWindow` shell for native WGPU windows that still reuse the shared WASM backend loop Reference example: ```bash -bun run --cwd examples/electrobun-counter dev:electrobun:browser +bun run --cwd examples/electrobun-counter dev:electrobun:hybrid bun run --cwd examples/electrobun-counter dev:electrobun:gpu ``` @@ -59,7 +59,6 @@ bun run --cwd examples/electrobun-counter dev:electrobun:gpu ## Known gaps - `resolveResource()` in `webtau/path` is not yet implemented for web mode. -- BrowserWindow + embedded `` is not yet covered by a gametau showcase. - The current GPUWindow proof path validates shared backend reuse, not a full renderer abstraction. ## Validating integration readiness diff --git a/examples/electrobun-counter/README.md b/examples/electrobun-counter/README.md index 9ed7b12..c8743a9 100644 --- a/examples/electrobun-counter/README.md +++ b/examples/electrobun-counter/README.md @@ -4,7 +4,7 @@ This example is the reference Electrobun showcase for gametau. It covers two shell shapes: -- `BrowserWindow` for the web-first counter UI +- `BrowserWindow` + embedded `` for the hybrid UI/render split - `GpuWindow` for a native WGPU shell that still reuses the shared counter WASM backend ## Run @@ -15,10 +15,10 @@ Install dependencies: bun install ``` -Start the BrowserWindow path: +Start the hybrid BrowserWindow path: ```bash -bun run dev:electrobun:browser +bun run dev:electrobun:hybrid ``` Start the GPUWindow path: @@ -29,14 +29,23 @@ bun run dev:electrobun:gpu ## Runtime behavior -- Browser mode loads the normal Vite app and auto-checks for `window.__ELECTROBUN__`. +- Hybrid mode loads the normal Vite app, auto-checks for `window.__ELECTROBUN__`, and embeds a native WGPU surface via ``. +- The example keeps HTML HUD/buttons in the webview while the native surface is resized and masked underneath it. - If no Electrobun bridge is exposed, the browser shell still falls back cleanly to the normal WASM path. - GPU mode configures `webtau` directly in the Bun runtime, loads the same counter WASM module, and advances the shared counter state inside a native `GpuWindow`. +## Why this exists + +This hybrid path is the lowest-risk step before deeper GPU-only renderer work: + +- it proves DOM UI and native rendering can coexist in one BrowserWindow +- it exercises resize, masking, and click-routing without abandoning current web-first app structure +- it gives a migration step for richer apps before pure `GpuWindow` renderer abstractions + ## Build ```bash bun run build:web -bun run build:electrobun:browser +bun run build:electrobun:hybrid bun run build:electrobun:gpu ``` diff --git a/examples/electrobun-counter/index.html b/examples/electrobun-counter/index.html index 027d0c5..1ff81da 100644 --- a/examples/electrobun-counter/index.html +++ b/examples/electrobun-counter/index.html @@ -3,74 +3,227 @@ - Electrobun Counter — gametau experimental example + Electrobun Counter - gametau hybrid showcase -
-

gametau electrobun counter (experimental)

-
0
-
- - - -
-

- Rust logic via ... -

-
+
+
+

Counter

+

gametau x Electrobun hybrid showcase

+

+ The counter UI stays in HTML while the native WGPU surface is embedded + in the same BrowserWindow. +

+
0
+
+ + + +
+

+ Rust logic via ... +

+
+ + +
+ diff --git a/examples/electrobun-counter/package.json b/examples/electrobun-counter/package.json index 251f25b..a732fad 100644 --- a/examples/electrobun-counter/package.json +++ b/examples/electrobun-counter/package.json @@ -5,13 +5,15 @@ "type": "module", "scripts": { "dev": "vite", - "dev:electrobun": "bun run dev:electrobun:browser", - "dev:electrobun:browser": "concurrently -k -n WEB,APP \"vite\" \"node ./node_modules/electrobun/bin/electrobun.cjs dev\"", + "dev:electrobun": "bun run dev:electrobun:hybrid", + "dev:electrobun:hybrid": "concurrently -k -n WEB,APP \"vite\" \"node ./node_modules/electrobun/bin/electrobun.cjs dev\"", + "dev:electrobun:browser": "bun run dev:electrobun:hybrid", "dev:electrobun:gpu": "concurrently -k -n WEB,APP \"vite\" \"cross-env GAMETAU_ELECTROBUN_RENDER_MODE=gpu node ./node_modules/electrobun/bin/electrobun.cjs dev\"", "dev:tauri": "tauri dev", "build:web": "vite build", - "build:electrobun": "bun run build:electrobun:browser", - "build:electrobun:browser": "bun run build:web && node ./node_modules/electrobun/bin/electrobun.cjs build", + "build:electrobun": "bun run build:electrobun:hybrid", + "build:electrobun:hybrid": "bun run build:web && node ./node_modules/electrobun/bin/electrobun.cjs build", + "build:electrobun:browser": "bun run build:electrobun:hybrid", "build:electrobun:gpu": "bun run build:web && cross-env GAMETAU_ELECTROBUN_RENDER_MODE=gpu node ./node_modules/electrobun/bin/electrobun.cjs build", "build:desktop": "tauri build", "preview": "vite preview" diff --git a/examples/electrobun-counter/src/hybrid-wgpu.ts b/examples/electrobun-counter/src/hybrid-wgpu.ts new file mode 100644 index 0000000..e33ef87 --- /dev/null +++ b/examples/electrobun-counter/src/hybrid-wgpu.ts @@ -0,0 +1,133 @@ +type WgpuReadyEvent = CustomEvent<{ id: number }>; + +interface HybridWgpuTagElement extends HTMLElement { + transparent: boolean; + passthroughEnabled: boolean; + hidden: boolean; + toggleTransparent(value?: boolean): void; + togglePassthrough(value?: boolean): void; + toggleHidden(value?: boolean): void; + syncDimensions(force?: boolean): void; + runTest(): void; + on(event: "ready", listener: (event: WgpuReadyEvent) => void): void; + off(event: "ready", listener: (event: WgpuReadyEvent) => void): void; +} + +interface HybridShowcaseHandle { + renderMode: "hybrid"; + destroy(): void; +} + +function isHybridTagReady(tag: Element | null): tag is HybridWgpuTagElement { + return !!tag + && typeof (tag as Partial).runTest === "function" + && typeof (tag as Partial).toggleTransparent === "function"; +} + +export function setupElectrobunHybridWgpu(): HybridShowcaseHandle | null { + const panel = document.getElementById("hybrid-panel") as HTMLElement | null; + const tag = document.querySelector("electrobun-wgpu"); + const statusEl = document.getElementById("hybrid-status"); + const rerunButton = document.getElementById("hybrid-run"); + const transparentButton = document.getElementById("hybrid-transparent"); + const passthroughButton = document.getElementById("hybrid-passthrough"); + const maskButton = document.getElementById("hybrid-mask"); + const mask = document.getElementById("wgpu-mask"); + + if ( + !panel + || !statusEl + || !rerunButton + || !transparentButton + || !passthroughButton + || !maskButton + || !mask + || !isHybridTagReady(tag) + ) { + panel?.setAttribute("hidden", "true"); + return null; + } + + const wgpuTag = tag; + const runButton = rerunButton; + const transparencyButton = transparentButton; + const passthroughToggleButton = passthroughButton; + const maskToggleButton = maskButton; + const maskEl = mask; + + panel.hidden = false; + + let transparent = false; + let passthrough = false; + let maskVisible = true; + + function updateButtonLabels() { + transparencyButton.textContent = transparent ? "Opaque surface" : "Transparent surface"; + passthroughToggleButton.textContent = passthrough ? "Capture clicks" : "Passthrough clicks"; + maskToggleButton.textContent = maskVisible ? "Hide HTML mask" : "Show HTML mask"; + } + + function syncMaskLayout() { + maskEl.hidden = !maskVisible; + wgpuTag.syncDimensions(true); + } + + const onReady = (event: WgpuReadyEvent) => { + statusEl.textContent = `Hybrid WGPU view ready (#${event.detail.id})`; + wgpuTag.runTest(); + }; + + wgpuTag.on("ready", onReady); + + const handleRerun = () => { + statusEl.textContent = "Running native WGPU test renderer"; + wgpuTag.runTest(); + }; + + const handleTransparent = () => { + transparent = !transparent; + wgpuTag.toggleTransparent(transparent); + updateButtonLabels(); + statusEl.textContent = transparent + ? "Surface transparency enabled" + : "Surface opacity restored"; + }; + + const handlePassthrough = () => { + passthrough = !passthrough; + wgpuTag.togglePassthrough(passthrough); + updateButtonLabels(); + statusEl.textContent = passthrough + ? "Native surface click passthrough enabled" + : "Native surface clicks restored"; + }; + + const handleMask = () => { + maskVisible = !maskVisible; + syncMaskLayout(); + updateButtonLabels(); + statusEl.textContent = maskVisible + ? "HTML mask applied over the native surface" + : "HTML mask removed from the native surface"; + }; + + runButton.addEventListener("click", handleRerun); + transparencyButton.addEventListener("click", handleTransparent); + passthroughToggleButton.addEventListener("click", handlePassthrough); + maskToggleButton.addEventListener("click", handleMask); + + updateButtonLabels(); + syncMaskLayout(); + statusEl.textContent = "Waiting for Electrobun WGPU surface"; + + return { + renderMode: "hybrid", + destroy() { + wgpuTag.off("ready", onReady); + runButton.removeEventListener("click", handleRerun); + transparencyButton.removeEventListener("click", handleTransparent); + passthroughToggleButton.removeEventListener("click", handlePassthrough); + maskToggleButton.removeEventListener("click", handleMask); + }, + }; +} diff --git a/examples/electrobun-counter/src/index.ts b/examples/electrobun-counter/src/index.ts index f99aafc..8237595 100644 --- a/examples/electrobun-counter/src/index.ts +++ b/examples/electrobun-counter/src/index.ts @@ -3,15 +3,18 @@ import { bootstrapElectrobunFromWindowBridge, getElectrobunCapabilities, } from "webtau/adapters/electrobun"; +import { setupElectrobunHybridWgpu } from "./hybrid-wgpu"; import { getCounter, increment, decrement, reset } from "./services/backend"; async function main() { const modeEl = document.getElementById("mode")!; const valueEl = document.getElementById("value")!; + let hybridHandle: ReturnType | null = null; if (bootstrapElectrobunFromWindowBridge()) { const capabilities = getElectrobunCapabilities(); - const renderMode = capabilities?.renderMode ?? "browser"; + hybridHandle = setupElectrobunHybridWgpu(); + const renderMode = hybridHandle?.renderMode ?? capabilities?.renderMode ?? "browser"; modeEl.textContent = `Electrobun bridge (${renderMode})`; } else if (!isTauri()) { modeEl.textContent = "WASM (web)"; @@ -44,6 +47,10 @@ async function main() { const result = await reset(); valueEl.textContent = String(result.value); }); + + window.addEventListener("beforeunload", () => { + hybridHandle?.destroy(); + }); } main().catch(console.error); From 769c2b560bf70d8baaae19717bf68a1ce075ea72 Mon Sep 17 00:00:00 2001 From: batz Date: Fri, 6 Mar 2026 11:43:49 +0100 Subject: [PATCH 2/2] Address hybrid showcase review nits --- .github/workflows/ci.yml | 1 + examples/electrobun-counter/package.json | 3 +- .../src/hybrid-wgpu.test.ts | 36 +++++++++++ .../electrobun-counter/src/hybrid-wgpu.ts | 59 ++++++++++++------- examples/electrobun-counter/src/index.ts | 8 +-- 5 files changed, 81 insertions(+), 26 deletions(-) create mode 100644 examples/electrobun-counter/src/hybrid-wgpu.test.ts diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e78d97e..4495b9f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -253,6 +253,7 @@ jobs: - name: Build Electrobun counter (web target) run: | cd examples/electrobun-counter + bun run test bun run build:web test -d dist test -f dist/index.html diff --git a/examples/electrobun-counter/package.json b/examples/electrobun-counter/package.json index a732fad..ffad0a7 100644 --- a/examples/electrobun-counter/package.json +++ b/examples/electrobun-counter/package.json @@ -16,7 +16,8 @@ "build:electrobun:browser": "bun run build:electrobun:hybrid", "build:electrobun:gpu": "bun run build:web && cross-env GAMETAU_ELECTROBUN_RENDER_MODE=gpu node ./node_modules/electrobun/bin/electrobun.cjs build", "build:desktop": "tauri build", - "preview": "vite preview" + "preview": "vite preview", + "test": "bun test src/hybrid-wgpu.test.ts" }, "dependencies": { "electrobun": "^1.15.1", diff --git a/examples/electrobun-counter/src/hybrid-wgpu.test.ts b/examples/electrobun-counter/src/hybrid-wgpu.test.ts new file mode 100644 index 0000000..e3b3c4e --- /dev/null +++ b/examples/electrobun-counter/src/hybrid-wgpu.test.ts @@ -0,0 +1,36 @@ +import { afterEach, describe, expect, test } from "bun:test"; +import { setupElectrobunHybridWgpu, setupElectrobunHybridWgpuWhenReady } from "./hybrid-wgpu"; + +const previousDocument = globalThis.document; +const previousCustomElements = globalThis.customElements; + +afterEach(() => { + if (previousDocument === undefined) { + delete (globalThis as { document?: unknown }).document; + } else { + (globalThis as { document?: unknown }).document = previousDocument; + } + + if (previousCustomElements === undefined) { + delete (globalThis as { customElements?: unknown }).customElements; + } else { + (globalThis as { customElements?: unknown }).customElements = previousCustomElements; + } +}); + +describe("setupElectrobunHybridWgpu", () => { + test("returns null when no upgraded WGPU tag is present", async () => { + (globalThis as { document?: unknown }).document = { + getElementById: (id: string) => ( + id === "hybrid-panel" ? { hidden: false } : null + ), + querySelector: () => null, + }; + (globalThis as { customElements?: unknown }).customElements = { + whenDefined: async () => {}, + }; + + expect(setupElectrobunHybridWgpu()).toBeNull(); + await expect(setupElectrobunHybridWgpuWhenReady()).resolves.toBeNull(); + }); +}); diff --git a/examples/electrobun-counter/src/hybrid-wgpu.ts b/examples/electrobun-counter/src/hybrid-wgpu.ts index e33ef87..41d1aaf 100644 --- a/examples/electrobun-counter/src/hybrid-wgpu.ts +++ b/examples/electrobun-counter/src/hybrid-wgpu.ts @@ -3,7 +3,6 @@ type WgpuReadyEvent = CustomEvent<{ id: number }>; interface HybridWgpuTagElement extends HTMLElement { transparent: boolean; passthroughEnabled: boolean; - hidden: boolean; toggleTransparent(value?: boolean): void; togglePassthrough(value?: boolean): void; toggleHidden(value?: boolean): void; @@ -42,19 +41,19 @@ export function setupElectrobunHybridWgpu(): HybridShowcaseHandle | null { || !passthroughButton || !maskButton || !mask - || !isHybridTagReady(tag) ) { - panel?.setAttribute("hidden", "true"); + if (panel) { + panel.hidden = true; + } return null; } - const wgpuTag = tag; - const runButton = rerunButton; - const transparencyButton = transparentButton; - const passthroughToggleButton = passthroughButton; - const maskToggleButton = maskButton; - const maskEl = mask; + if (!isHybridTagReady(tag)) { + panel.hidden = true; + return null; + } + const wgpuTag = tag; panel.hidden = false; let transparent = false; @@ -62,13 +61,13 @@ export function setupElectrobunHybridWgpu(): HybridShowcaseHandle | null { let maskVisible = true; function updateButtonLabels() { - transparencyButton.textContent = transparent ? "Opaque surface" : "Transparent surface"; - passthroughToggleButton.textContent = passthrough ? "Capture clicks" : "Passthrough clicks"; - maskToggleButton.textContent = maskVisible ? "Hide HTML mask" : "Show HTML mask"; + transparentButton!.textContent = transparent ? "Opaque surface" : "Transparent surface"; + passthroughButton!.textContent = passthrough ? "Capture clicks" : "Passthrough clicks"; + maskButton!.textContent = maskVisible ? "Hide HTML mask" : "Show HTML mask"; } function syncMaskLayout() { - maskEl.hidden = !maskVisible; + mask!.hidden = !maskVisible; wgpuTag.syncDimensions(true); } @@ -111,10 +110,10 @@ export function setupElectrobunHybridWgpu(): HybridShowcaseHandle | null { : "HTML mask removed from the native surface"; }; - runButton.addEventListener("click", handleRerun); - transparencyButton.addEventListener("click", handleTransparent); - passthroughToggleButton.addEventListener("click", handlePassthrough); - maskToggleButton.addEventListener("click", handleMask); + rerunButton.addEventListener("click", handleRerun); + transparentButton.addEventListener("click", handleTransparent); + passthroughButton.addEventListener("click", handlePassthrough); + maskButton.addEventListener("click", handleMask); updateButtonLabels(); syncMaskLayout(); @@ -124,10 +123,28 @@ export function setupElectrobunHybridWgpu(): HybridShowcaseHandle | null { renderMode: "hybrid", destroy() { wgpuTag.off("ready", onReady); - runButton.removeEventListener("click", handleRerun); - transparencyButton.removeEventListener("click", handleTransparent); - passthroughToggleButton.removeEventListener("click", handlePassthrough); - maskToggleButton.removeEventListener("click", handleMask); + rerunButton.removeEventListener("click", handleRerun); + transparentButton.removeEventListener("click", handleTransparent); + passthroughButton.removeEventListener("click", handlePassthrough); + maskButton.removeEventListener("click", handleMask); }, }; } + +export async function setupElectrobunHybridWgpuWhenReady(): Promise { + const initial = setupElectrobunHybridWgpu(); + if (initial) { + return initial; + } + + if ( + typeof customElements === "undefined" + || !document.querySelector("electrobun-wgpu") + || typeof customElements.whenDefined !== "function" + ) { + return null; + } + + await customElements.whenDefined("electrobun-wgpu"); + return setupElectrobunHybridWgpu(); +} diff --git a/examples/electrobun-counter/src/index.ts b/examples/electrobun-counter/src/index.ts index 8237595..a5edb58 100644 --- a/examples/electrobun-counter/src/index.ts +++ b/examples/electrobun-counter/src/index.ts @@ -1,19 +1,19 @@ import { configure, isTauri } from "webtau"; import { - bootstrapElectrobunFromWindowBridge, getElectrobunCapabilities, + bootstrapElectrobunFromWindowBridge, } from "webtau/adapters/electrobun"; -import { setupElectrobunHybridWgpu } from "./hybrid-wgpu"; +import { setupElectrobunHybridWgpuWhenReady } from "./hybrid-wgpu"; import { getCounter, increment, decrement, reset } from "./services/backend"; async function main() { const modeEl = document.getElementById("mode")!; const valueEl = document.getElementById("value")!; - let hybridHandle: ReturnType | null = null; + let hybridHandle: Awaited> = null; if (bootstrapElectrobunFromWindowBridge()) { const capabilities = getElectrobunCapabilities(); - hybridHandle = setupElectrobunHybridWgpu(); + hybridHandle = await setupElectrobunHybridWgpuWhenReady(); const renderMode = hybridHandle?.renderMode ?? capabilities?.renderMode ?? "browser"; modeEl.textContent = `Electrobun bridge (${renderMode})`; } else if (!isTauri()) {