diff --git a/apps/web/src/app/api/dev/save-crop/route.ts b/apps/web/src/app/api/dev/save-crop/route.ts index 1c037a20..5f53301c 100644 --- a/apps/web/src/app/api/dev/save-crop/route.ts +++ b/apps/web/src/app/api/dev/save-crop/route.ts @@ -121,8 +121,14 @@ function writeCropsFile(crops: Record>): void { formattedCrops = '{}' } else { formattedCrops = JSON.stringify(crops, null, 2) - .replace(/"([^"]+)":/g, '$1:') // Remove quotes from keys + // Only remove quotes from keys that are valid JS identifiers (no hyphens, spaces, etc.) + // Valid identifiers: start with letter/$/_, contain only letters/digits/$/_ + .replace(/"([a-zA-Z_$][a-zA-Z0-9_$]*)":/g, '$1:') + // Keep quotes but convert to single quotes for keys with special chars (like hyphens) + .replace(/"([^"]+)":/g, "'$1':") .replace(/"/g, "'") // Use single quotes for values + // Add trailing commas before closing braces/brackets + .replace(/([^,{\s])\n(\s*[}\]])/g, '$1,\n$2') } // Replace the object 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 023b9ec0..0bf739a8 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 @@ -2402,7 +2402,8 @@ export function MapRenderer({ ) })()} - {/* Debug: Bounding boxes for detected regions in magnifier */} + {/* Debug: Bounding boxes with labels for detected regions in magnifier */} + {/* Labels are rendered as SVG text inside the SVG to ensure perfect alignment */} {SHOW_DEBUG_BOUNDING_BOXES && debugBoundingBoxes.map((bbox) => { const importance = bbox.importance ?? 0 @@ -2417,114 +2418,53 @@ export function MapRenderer({ strokeColor = '#ffcc00' // Yellow for medium importance } + // Calculate bbox center for label positioning + const bboxCenterX = bbox.x + bbox.width / 2 + const bboxCenterY = bbox.y + bbox.height / 2 + return ( - + + + {/* Label text - uses vectorEffect to maintain readable size */} + + + {bbox.regionId} + + + {importance.toFixed(2)} + + + ) })} - {/* Debug: Bounding box labels in magnifier as HTML overlays */} - {SHOW_DEBUG_BOUNDING_BOXES && - containerRef.current && - svgRef.current && - debugBoundingBoxes.map((bbox) => { - const importance = bbox.importance ?? 0 - let strokeColor = '#888888' - - if (bbox.wasAccepted) { - strokeColor = '#00ff00' - } else if (importance > 1.5) { - strokeColor = '#ff6600' - } else if (importance > 0.5) { - strokeColor = '#ffcc00' - } - - // Get magnifier dimensions - const containerRect = containerRef.current!.getBoundingClientRect() - const magnifierWidth = containerRect.width * 0.5 - const magnifierHeight = magnifierWidth / 2 - - // Parse viewBox - const viewBoxParts = mapData.viewBox.split(' ').map(Number) - const viewBoxX = viewBoxParts[0] || 0 - const viewBoxY = viewBoxParts[1] || 0 - const viewBoxWidth = viewBoxParts[2] || 1000 - const viewBoxHeight = viewBoxParts[3] || 1000 - - // Get current zoom (use animated value) - const currentZoom = getCurrentZoom() - - // Calculate magnified viewBox (same logic as the magnifier's viewBox calculation) - const svgRect = svgRef.current!.getBoundingClientRect() - const scaleX = viewBoxWidth / svgRect.width - const scaleY = viewBoxHeight / svgRect.height - const svgOffsetX = svgRect.left - containerRect.left - const svgOffsetY = svgRect.top - containerRect.top - - // cursorPosition is always defined in this scope (IIFE ensures it) - const cursorSvgX = (cursorPosition.x - svgOffsetX) * scaleX + viewBoxX - const cursorSvgY = (cursorPosition.y - svgOffsetY) * scaleY + viewBoxY - - const magnifiedWidth = viewBoxWidth / currentZoom - const magnifiedHeight = viewBoxHeight / currentZoom - - const magnifiedViewBoxX = cursorSvgX - magnifiedWidth / 2 - const magnifiedViewBoxY = cursorSvgY - magnifiedHeight / 2 - - // Convert bbox center from SVG coords to magnifier pixel coords - const bboxCenterSvgX = bbox.x + bbox.width / 2 - const bboxCenterSvgY = bbox.y + bbox.height / 2 - - // Calculate position within magnified viewBox - const relativeX = (bboxCenterSvgX - magnifiedViewBoxX) / magnifiedWidth - const relativeY = (bboxCenterSvgY - magnifiedViewBoxY) / magnifiedHeight - - // Check if bbox is within magnified viewport - if (relativeX < 0 || relativeX > 1 || relativeY < 0 || relativeY > 1) { - return null // Don't show labels for regions outside magnifier viewport - } - - // Convert to pixel position within magnifier - const labelX = relativeX * magnifierWidth - const labelY = relativeY * magnifierHeight - - return ( -
-
{bbox.regionId}
-
- {importance.toFixed(2)} -
-
- ) - })} - {/* Magnifier label */}