From 79d366b240f60328d2d0ff84d89a49bfc23deb11 Mon Sep 17 00:00:00 2001 From: Chad Curtis Date: Mon, 23 Feb 2026 00:57:42 -0600 Subject: [PATCH] Add virtual gamepad controls and fullscreen toggle for webxdc games --- package-lock.json | 20 ++++++++++++++++++++ src/components/WebxdcEmbed.tsx | 17 ++++++----------- 2 files changed, 26 insertions(+), 11 deletions(-) diff --git a/package-lock.json b/package-lock.json index 93695f6c..6fca13b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3062,6 +3062,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3075,6 +3076,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3088,6 +3090,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3101,6 +3104,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3114,6 +3118,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3127,6 +3132,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3140,6 +3146,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3153,6 +3160,7 @@ "cpu": [ "arm" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3166,6 +3174,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3179,6 +3188,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3192,6 +3202,7 @@ "cpu": [ "loong64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3205,6 +3216,7 @@ "cpu": [ "ppc64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3218,6 +3230,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3231,6 +3244,7 @@ "cpu": [ "riscv64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3244,6 +3258,7 @@ "cpu": [ "s390x" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3257,6 +3272,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3270,6 +3286,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3283,6 +3300,7 @@ "cpu": [ "arm64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3296,6 +3314,7 @@ "cpu": [ "ia32" ], + "dev": true, "license": "MIT", "optional": true, "os": [ @@ -3309,6 +3328,7 @@ "cpu": [ "x64" ], + "dev": true, "license": "MIT", "optional": true, "os": [ diff --git a/src/components/WebxdcEmbed.tsx b/src/components/WebxdcEmbed.tsx index 845ee5bd..d1b33d33 100644 --- a/src/components/WebxdcEmbed.tsx +++ b/src/components/WebxdcEmbed.tsx @@ -28,14 +28,8 @@ export function WebxdcEmbed({ url, uuid, name, icon, className }: WebxdcEmbedPro const [isFullscreen, setIsFullscreen] = useState(false); const [iframeKey, setIframeKey] = useState(0); const [showGamepad, setShowGamepad] = useState(false); - const [webxdcHandle, setWebxdcHandle] = useState(null); const containerRef = useRef(null); - - // Callback ref: when the Webxdc component mounts, store the handle in state - // so GameControls re-renders with a non-null handle. - const webxdcHandleCallback = useCallback((handle: WebxdcHandle | null) => { - setWebxdcHandle(handle); - }, []); + const webxdcHandleRef = useRef(null); // Derive a stable iframe ID from the UUID or URL const iframeId = uuid ?? url.replace(/[^a-zA-Z0-9]/g, '').slice(0, 32); @@ -56,10 +50,10 @@ export function WebxdcEmbed({ url, uuid, name, icon, className }: WebxdcEmbedPro const toggleGamepad = useCallback(() => { setShowGamepad((prev) => { - if (!prev) webxdcHandle?.focus(); + if (!prev) webxdcHandleRef.current?.focus(); return !prev; }); - }, [webxdcHandle]); + }, []); if (!launched) { return ( @@ -208,7 +202,7 @@ export function WebxdcEmbed({ url, uuid, name, icon, className }: WebxdcEmbedPro
- +
)} @@ -247,6 +241,7 @@ const WebxdcIframe = forwardRef