From 1333818baea76360788b3c30993ab1b54e437e97 Mon Sep 17 00:00:00 2001 From: Thomas Hallock Date: Thu, 27 Nov 2025 20:52:39 -0600 Subject: [PATCH] fix: show hot/cold button on iPad with mouse attached MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace isTouchDevice detection with hasFinePointer using (any-pointer: fine) media query. This properly detects if a mouse is connected, so iPads with attached mice will show the hot/cold button, but pure touch devices won't. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude --- .../components/MapRenderer.tsx | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx b/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx index 9388204d..454e6760 100644 --- a/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx +++ b/apps/web/src/arcade-games/know-your-world/components/MapRenderer.tsx @@ -556,9 +556,10 @@ export function MapRenderer({ return localStorage.getItem('knowYourWorld.hotColdAudio') === 'true' }) - // Detect touch devices (hot/cold feedback is desktop-only) - const isTouchDevice = - typeof window !== 'undefined' && ('ontouchstart' in window || navigator.maxTouchPoints > 0) + // Detect if device has a fine pointer (mouse) - iPads with mice will return true + // This is better than isTouchDevice because iPads with attached mice should show hot/cold + const hasFinePointer = + typeof window !== 'undefined' && window.matchMedia('(any-pointer: fine)').matches // Persist auto-speak setting const handleAutoSpeakChange = useCallback((enabled: boolean) => { @@ -681,7 +682,7 @@ export function MapRenderer({ const hotColdButton = usePointerLockButton({ id: 'hot-cold-button', disabled: false, - active: isSpeechSupported && !isTouchDevice, // Only show on desktop with speech support + active: isSpeechSupported && hasFinePointer, // Show when speech is supported and device has mouse pointerLocked, cursorPosition, containerRef, @@ -803,7 +804,7 @@ export function MapRenderer({ reset: resetHotCold, lastFeedbackType: hotColdFeedbackType, } = useHotColdFeedback({ - enabled: assistanceAllowsHotCold && hotColdEnabled && !isTouchDevice, + enabled: assistanceAllowsHotCold && hotColdEnabled && hasFinePointer, targetRegionId: currentPrompt, isSpeaking, mapName: hotColdMapName, @@ -1988,8 +1989,8 @@ export function MapRenderer({ } // Hot/cold audio feedback - // Only run if enabled and we have a target region - if (hotColdEnabledRef.current && currentPrompt && !isTouchDevice) { + // Only run if enabled, we have a target region, and device has a fine pointer (mouse) + if (hotColdEnabledRef.current && currentPrompt && hasFinePointer) { // Find target region's SVG center const targetRegion = mapData.regions.find((r) => r.id === currentPrompt) if (targetRegion) { @@ -4726,9 +4727,9 @@ export function MapRenderer({ ) })()} - {/* Hot/Cold button - only show on desktop with speech support when assistance level allows */} + {/* Hot/Cold button - show when speech is supported, assistance level allows, and device has mouse */} {isSpeechSupported && - !isTouchDevice && + hasFinePointer && assistanceAllowsHotCold && (() => { if (!svgRef.current || !containerRef.current || svgDimensions.width === 0) return null