Skip to content

Add Electrobun hybrid WGPU showcase#165

Merged
devallibus merged 2 commits intomasterfrom
feat/electrobun-hybrid-showcase
Mar 6, 2026
Merged

Add Electrobun hybrid WGPU showcase#165
devallibus merged 2 commits intomasterfrom
feat/electrobun-hybrid-showcase

Conversation

@devallibus
Copy link
Owner

Summary

  • turn the Electrobun counter BrowserWindow path into a true hybrid showcase with an embedded <electrobun-wgpu> surface
  • add hybrid controls and docs explaining the render/UI split and why this path comes before deeper GpuWindow work
  • update CI and dogfood naming to exercise the hybrid BrowserWindow lane explicitly

What changed

  • the Electrobun counter example now renders an embedded native WGPU surface inside the BrowserWindow shell
  • the example exposes native test / transparency / passthrough / HTML mask controls
  • package scripts now expose dev:electrobun:hybrid and build:electrobun:hybrid while keeping browser aliases
  • runtime/readiness/showcase docs now describe the hybrid path explicitly
  • the Electrobun smoke lane is renamed to hybrid + GPU wording

Validation

  • bunx tsc --noEmit -p examples/electrobun-counter/tsconfig.json
  • bun run build:web in examples/electrobun-counter
  • bun run build:electrobun:hybrid in examples/electrobun-counter
  • bun run build:electrobun:gpu in examples/electrobun-counter

Tracking

@devallibus
Copy link
Owner Author

PR Review — claude-opus-4-6 (Claude Opus 4.6, 1M context)

Overall

Clean, well-structured PR. The hybrid showcase is a sensible incremental step — proving DOM/native coexistence before committing to full GpuWindow renderer work. Docs, CI, scripts, and code all move in lockstep.

Verdict: Approve with minor nits.


Strengths

  • Clean separation: hybrid-wgpu.ts is self-contained with proper TypeScript typing for the <electrobun-wgpu> custom element and a well-defined HybridShowcaseHandle return type.
  • Graceful degradation: isHybridTagReady() runtime duck-type check means non-Electrobun environments see no hybrid panel — the hidden attribute stays set and null is returned cleanly.
  • Proper cleanup: destroy() removes all listeners, wired up via beforeunload. Good hygiene.
  • Backwards compat: dev:electrobun:browser and build:electrobun:browser kept as aliases pointing to the new :hybrid targets. Nothing breaks for anyone using the old names.
  • Consistent CI/docs sweep: Release gate checklist, ci.yml, dogfood workflow, showcase doc, portability doc, and example README all updated atomically. The known gap in RUNTIME-PORTABILITY-READINESS.md is correctly removed.
  • Responsive design: Media query at 900px collapses the two-column grid gracefully.

Nits

  1. Redundant variable aliasing (hybrid-wgpu.ts:51–56): After the null guard narrows all variables, they're re-aliased (const wgpuTag = tag, const runButton = rerunButton, etc.). TypeScript already narrows these in the block after the guard. The aliases add 6 lines of noise without adding clarity — the original names are already descriptive.

  2. Inconsistent hidden attribute setting (hybrid-wgpu.ts:47 vs :58): The early-return path uses panel?.setAttribute("hidden", "true") while the happy path uses panel.hidden = false. Both work, but mixing DOM property vs attribute access for the same purpose is a minor inconsistency. Prefer panel.hidden = true for symmetry.

  3. HybridWgpuTagElement.hidden redeclares HTMLElement.hidden (hybrid-wgpu.ts:6): hidden already exists on HTMLElement. The redeclaration is harmless but unnecessary.

  4. Custom element upgrade timing (index.ts:16): setupElectrobunHybridWgpu() is called synchronously. If <electrobun-wgpu> hasn't been upgraded by the time this runs (custom element definitions may load async), isHybridTagReady() returns false and the panel stays hidden. The code handles this gracefully (returns null), but if Electrobun injects its CE definitions lazily, users may never see the hybrid panel. Worth a comment or a whenDefined guard if this becomes an issue in practice.

  5. No tests for hybrid-wgpu.ts: Understandable since the module depends on a native custom element that can't be easily mocked in unit tests, but worth noting given the project's 256-test bar for webtau. A minimal test asserting setupElectrobunHybridWgpu() === null when the DOM has no upgraded <electrobun-wgpu> element would exercise the guard path cheaply.


No Issues Found

  • CI commands are consistent with package.json script names.
  • No security concerns (no user input handling, no injection vectors).
  • The pointer-events: none on .hybrid-overlay correctly keeps the informational overlay non-interactive.
  • The renderMode fallback chain (hybridHandle?.renderMode ?? capabilities?.renderMode ?? "browser") is correct — hybrid takes priority when available.

LGTM pending the nits above (all optional).


Reviewed by claude-opus-4-6 (Claude Opus 4.6, 1M context)

@devallibus
Copy link
Owner Author

Re-review after update — claude-opus-4-6 (Claude Opus 4.6, 1M context)

All 5 nits from the initial review have been addressed. Here's what changed and the current assessment.


Nits resolved

# Original nit Resolution
1 Redundant variable aliasing Removed — only const wgpuTag = tag remains (needed because TS can't narrow a type guard result across closure boundaries).
2 Inconsistent hidden attribute setting Now uses panel.hidden = true consistently with proper if (panel) guard.
3 hidden redeclared on HybridWgpuTagElement Removed from the interface.
4 Custom element upgrade race condition Addressed with new setupElectrobunHybridWgpuWhenReady() that awaits customElements.whenDefined("electrobun-wgpu") before retrying.
5 No tests hybrid-wgpu.test.ts added, covering the null-return guard path for both sync and async variants. CI step added.

New code assessment

setupElectrobunHybridWgpuWhenReady() (hybrid-wgpu.ts:134–150): Clean two-phase approach — try sync first, if the tag isn't upgraded yet, await whenDefined then retry. The guard chain (typeof customElements === "undefined"!document.querySelector(...)typeof whenDefined !== "function") is defensive in the right places.

Non-null assertions in closures (hybrid-wgpu.ts:68–70): transparentButton!, passthroughButton!, maskButton!, mask! — these are safe since the closures only execute after the null guard at lines 37–48 succeeds. TS can't narrow const bindings into closures, so ! is the correct workaround here.

Test (hybrid-wgpu.test.ts): The globalThis.document/globalThis.customElements mocking with afterEach restore is solid for a Bun test environment without a real DOM. The mock correctly returns null for all elements except hybrid-panel, triggering the early-return path. The async variant test also validates that whenDefined resolving doesn't cause a false positive when the element still isn't present.

CI (ci.yml): bun run test added to the Electrobun smoke job — tests now run in CI before the build steps.

One minor observation (non-blocking)

setupElectrobunHybridWgpuWhenReady calls setupElectrobunHybridWgpu() twice (once eagerly, once after whenDefined). The first call sets panel.hidden = true if the tag isn't ready. The second call, on success, sets panel.hidden = false. This means there's a brief state where the panel is hidden then shown. For a showcase example this is perfectly fine — just noting it in case a future consumer needs instant visibility.

Verdict

Approve. The update is thorough and directly responsive. No remaining concerns.


Re-reviewed by claude-opus-4-6 (Claude Opus 4.6, 1M context)

@devallibus devallibus merged commit b8ecea3 into master Mar 6, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[P1] Add a BrowserWindow + <electrobun-wgpu> hybrid showcase and smoke lane

1 participant