fix: resolve Three.js Text component error and modal close issues

- Downgrade three.js from 0.182 to 0.168 to fix customDepthMaterial
  getter-only property breaking change (drei issue #2403)
- Add stable useCallback for modal close handler to prevent
  reference instability
- Improve ESC key handler with ref pattern and capture phase
  to ensure reliable modal closing

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Jeff Emmett 2025-12-19 15:08:55 -05:00
parent 6d3716a059
commit c76f453042
4 changed files with 23 additions and 13 deletions

8
package-lock.json generated
View File

@ -60,7 +60,7 @@
"react-router-dom": "^7.0.2", "react-router-dom": "^7.0.2",
"recoil": "^0.7.7", "recoil": "^0.7.7",
"sharp": "^0.33.5", "sharp": "^0.33.5",
"three": "^0.182.0", "three": "^0.168.0",
"tldraw": "^3.15.4", "tldraw": "^3.15.4",
"use-whisper": "^0.0.1", "use-whisper": "^0.0.1",
"webcola": "^3.4.0" "webcola": "^3.4.0"
@ -20865,9 +20865,9 @@
} }
}, },
"node_modules/three": { "node_modules/three": {
"version": "0.182.0", "version": "0.168.0",
"resolved": "https://registry.npmjs.org/three/-/three-0.182.0.tgz", "resolved": "https://registry.npmjs.org/three/-/three-0.168.0.tgz",
"integrity": "sha512-GbHabT+Irv+ihI1/f5kIIsZ+Ef9Sl5A1Y7imvS5RQjWgtTPfPnZ43JmlYI7NtCRDK9zir20lQpfg8/9Yd02OvQ==", "integrity": "sha512-6m6jXtDwMJEK/GGMbAOTSAmxNdzKvvBzgd7q8bE/7Tr6m7PaBh5kKLrN7faWtlglXbzj7sVba48Idwx+NRsZXw==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/three-mesh-bvh": { "node_modules/three-mesh-bvh": {

View File

@ -86,7 +86,7 @@
"react-router-dom": "^7.0.2", "react-router-dom": "^7.0.2",
"recoil": "^0.7.7", "recoil": "^0.7.7",
"sharp": "^0.33.5", "sharp": "^0.33.5",
"three": "^0.182.0", "three": "^0.168.0",
"tldraw": "^3.15.4", "tldraw": "^3.15.4",
"use-whisper": "^0.0.1", "use-whisper": "^0.0.1",
"webcola": "^3.4.0" "webcola": "^3.4.0"

View File

@ -249,6 +249,11 @@ export function NetworkGraphMinimap({
const [selectedNode, setSelectedNode] = useState<{ node: GraphNode; x: number; y: number } | null>(null); const [selectedNode, setSelectedNode] = useState<{ node: GraphNode; x: number; y: number } | null>(null);
const [isConnecting, setIsConnecting] = useState(false); const [isConnecting, setIsConnecting] = useState(false);
// Stable callback for closing search modal (prevents ESC handler issues)
const handleCloseSearch = useCallback(() => {
setIsSearchOpen(false);
}, []);
// Three-state display mode: minimized, normal, maximized // Three-state display mode: minimized, normal, maximized
const [displayMode, setDisplayMode] = useState<DisplayMode>(isCollapsed ? 'minimized' : 'normal'); const [displayMode, setDisplayMode] = useState<DisplayMode>(isCollapsed ? 'minimized' : 'normal');
@ -963,7 +968,7 @@ export function NetworkGraphMinimap({
<UserSearchModal <UserSearchModal
isOpen={isSearchOpen} isOpen={isSearchOpen}
onClose={() => setIsSearchOpen(false)} onClose={handleCloseSearch}
onConnect={onConnect} onConnect={onConnect}
onDisconnect={onDisconnect ? (userId) => { onDisconnect={onDisconnect ? (userId) => {
// Find the connection ID for this user // Find the connection ID for this user

View File

@ -256,19 +256,24 @@ export function UserSearchModal({
} }
}, [onConnect, onDisconnect]); }, [onConnect, onDisconnect]);
// Handle escape key // Handle escape key - use a ref to avoid stale closure issues
const onCloseRef = useRef(onClose);
onCloseRef.current = onClose;
useEffect(() => { useEffect(() => {
if (!isOpen) return;
const handleKeyDown = (e: KeyboardEvent) => { const handleKeyDown = (e: KeyboardEvent) => {
if (e.key === 'Escape') { if (e.key === 'Escape') {
onClose(); e.preventDefault();
e.stopPropagation();
onCloseRef.current();
} }
}; };
if (isOpen) { window.addEventListener('keydown', handleKeyDown, true); // Use capture phase
window.addEventListener('keydown', handleKeyDown); return () => window.removeEventListener('keydown', handleKeyDown, true);
return () => window.removeEventListener('keydown', handleKeyDown); }, [isOpen]);
}
}, [isOpen, onClose]);
if (!isOpen) return null; if (!isOpen) return null;