Repository navigation
Add desktop viewer example - #425
Conversation
React + Vite + Tailwind v4 app that demonstrates the desktop environment API with noVNC streaming and viewport-aware resolution.
|
There was a problem hiding this comment.
OpenCode Review
This is a well-structured example that demonstrates the desktop capabilities of the Sandbox SDK. The implementation shows good architectural decisions and follows established patterns from the existing codebase.
Strong points:
- Clean architecture: The React frontend is well-organized with proper separation of concerns (API client, hooks, components)
- Viewport-aware resolution: Smart approach to dynamically measure container dimensions and pass resolution to avoid UI issues
- Robust error handling: Proper error propagation from the Worker through to the UI with user-friendly error display
- Container configuration: Appropriate use of
standard-4instance type for desktop environments that need more CPU - Autostart wallpaper setup: Creative solution to handle variable monitor naming across Xvfb versions
Technical implementation quality is solid:
- Uses modern React patterns (hooks, proper state management)
- Appropriate polling strategy for status updates
- Clean API design with proper HTTP method usage
- Good use of Tailwind v4 and Kumo components for UI
Minor observations:
The base64 decoding in the screenshot handler (workers/app.ts:498-500) uses a manual loop when Uint8Array.from(atob(data), c => c.charCodeAt(0)) would be more concise, but the current approach is clear and works correctly.
The example properly demonstrates several advanced Sandbox features:
- Desktop environment management
- Port exposure for noVNC streaming
- Screenshot capture
- Dynamic resolution configuration
This adds significant value to the SDK examples by showing how to build interactive desktop applications on Cloudflare's edge infrastructure.
Verdict: Looks good to merge. This is a comprehensive example that will help developers understand the full capabilities of the Sandbox SDK for desktop environments.
commit: |
🐳 Docker Images PublishedDefault: FROM cloudflare/sandbox:0.0.0-pr-425-72bd0e1With Python: FROM cloudflare/sandbox:0.0.0-pr-425-72bd0e1-pythonWith OpenCode: FROM cloudflare/sandbox:0.0.0-pr-425-72bd0e1-opencodeVersion: Use the 📦 Standalone BinaryFor arbitrary Dockerfiles: COPY --from=cloudflare/sandbox:0.0.0-pr-425-72bd0e1 /container-server/sandbox /sandbox
ENTRYPOINT ["/sandbox"]Download via GitHub CLI: gh run download 22408398053 -n sandbox-binaryExtract from Docker: docker run --rm cloudflare/sandbox:0.0.0-pr-425-72bd0e1 cat /container-server/sandbox > sandbox && chmod +x sandbox |
* Add desktop environment tests Unit tests for the container handler, service, and SDK client, plus an E2E test that exercises the full desktop lifecycle through a real deployed worker. * Add desktop viewer example (#425) React + Vite + Tailwind v4 app that demonstrates the desktop environment API with noVNC streaming and viewport-aware resolution.
* Add desktop environment SDK client Sandboxes need a public API for desktop environments so Workers can manage desktops, capture screenshots, and stream VNC. The Dockerfile gains a desktop build stage with the required system dependencies. * Add desktop environment tests (#424) * Add desktop environment tests Unit tests for the container handler, service, and SDK client, plus an E2E test that exercises the full desktop lifecycle through a real deployed worker. * Add desktop viewer example (#425) React + Vite + Tailwind v4 app that demonstrates the desktop environment API with noVNC streaming and viewport-aware resolution.
* Add desktop environment container runtime Enables running a full Linux desktop inside sandbox containers with programmatic screenshot and input control via native FFI. * Add desktop environment SDK client (#423) * Add desktop environment SDK client Sandboxes need a public API for desktop environments so Workers can manage desktops, capture screenshots, and stream VNC. The Dockerfile gains a desktop build stage with the required system dependencies. * Add desktop environment tests (#424) * Add desktop environment tests Unit tests for the container handler, service, and SDK client, plus an E2E test that exercises the full desktop lifecycle through a real deployed worker. * Add desktop viewer example (#425) React + Vite + Tailwind v4 app that demonstrates the desktop environment API with noVNC streaming and viewport-aware resolution. * Fix lint errors in desktop example and biome config * Add desktop E2E test Dockerfile and config generation * Add desktop image to CI build, push, and cleanup workflows * Fix Go build: pin golang.org/x/net to Go 1.24-compatible version go mod tidy resolved golang.org/x/net@v0.51.0 which requires go >= 1.25, breaking the go-builder stage using golang:1.24-bookworm. Pin to v0.50.0 (last Go 1.24-compatible release) and update go directive to match builder. * Fix FFI type mismatch and clickCount handling The Click FFI binding declared 'bool' (1-byte C _Bool) but the Go function expects C.int (4 bytes), causing undefined ABI behavior. Changed to 'int' and pass clickCount through directly so tripleClick emits three rapid single clicks instead of silently degrading to doubleClick. * Guard desktop stop in destroy() on container state desktop.stop() goes through containerFetch which auto-starts sleeping containers. Check ctx.container.running first so destroy() does not wake a container just to immediately tear it down. * Revert accidental backup and token doc changes The desktop branch commit inadvertently changed backup curl from streaming -T to --data-binary (loads full archive into memory), reduced timeouts from 1800s to 300s, removed the local-dev mismatch diagnostic, and changed token docs to show hyphens which the validation regex rejects. Restore all to match main. * Add error resilience to desktop worker and manager Catch stop() failures during start() error recovery so the original error propagates. Add onerror handler to the worker thread so pending promises reject instead of hanging if the worker crashes. * Fix FFI out-pointer semantics and skip stream-url in CI koffi requires koffi.out() annotation on pointer parameters to copy values back from C to JS after the call. Without it, GetScreenSize and GetMousePos always returned zeros because koffi treated int* as input-only. The stream-url E2E test requires preview URL infrastructure (custom domain with wildcard DNS) that CI workers.dev doesn't provide, so skip it with the same pattern used by other port-exposure tests. * Reset manager state on start failure DesktopManager.start() sets state to 'starting' but the catch block relied solely on stop() to reset it to 'inactive'. When stop() itself fails, state remains 'starting' permanently, blocking all subsequent start attempts. Explicitly set state to 'inactive' after cleanup. * Use pure-Go xgb path for GetScreenSize robotgo.GetScreenSize() delegates to C-based XGetMainDisplay() which holds an unsynchronized static Display pointer. In Go's c-shared build mode CGo dispatches from varying OS threads, causing the singleton to silently return zero dimensions. Switch to robotgo.GetDisplayBounds(0) which uses the github.com/kbinani/screenshot pure-Go xgb implementation, matching the existing workaround for the SaveCapture segfault. * Upgrade robotgo to v1.0.1 with uniform error handling Use dedicated v1.0.1 APIs (MouseDown/Up, KeyDown/Up, Type, MultiClick) instead of Toggle/KeyToggle/TypeStr. All Go FFI exports now return error strings via *C.char, and the koffi bindings use HeapStr with a checkError() helper for uniform error propagation. Rename TypeStr→TypeText and SaveCapture→Screenshot to match v1.0.1 naming. Click now takes a count parameter — single, double, and multi-click are handled in Go. The worker-side triple-click loop is removed since Go handles it natively via robotgo.MultiClick.
Summary
A React + Vite + Tailwind v4 example app that demonstrates the desktop environment API. The viewer starts a desktop session, streams it via noVNC in an iframe, and supports on-demand screenshots. The frontend measures its own viewport and passes the resolution to the start call so the remote desktop fills the available space.
Design decisions
Viewport-aware resolution: The frontend uses a ref-based measurement of the viewer container and sends
width/heightin the start request body. This avoids hardcoding a resolution and ensures the remote desktop matches what the user actually sees, eliminating scrollbars or letterboxing.Autostart script for wallpaper instead of static XFCE XML config: The XFCE4 desktop config uses a monitor name (
monitor0vsmonitorscreen) that varies depending on the Xvfb version. A.desktopautostart entry runsxfconf-queryafter xfdesktop initializes, dynamically finding and setting alllast-imageproperties regardless of monitor naming.standard-4instance type: Desktop environments benefit from the extra CPU (4 vCPU) for compositor rendering and VNC encoding. The max_instances is set to 4 to allow deployment rollover without hitting container limits.This is part 4 of 4 in the desktop environment stack:
main