Claude-skill-registry canvas-debugging
Debug canvas layout issues, grid positioning problems, and component overlap. Use when troubleshooting Konva canvas rendering, grid constraints, snap-to-grid behavior, or visual layout mismatches. Includes Canvas→CSS Grid conversion debugging.
git clone https://github.com/majiayu000/claude-skill-registry
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/canvas-debugging" ~/.claude/skills/majiayu000-claude-skill-registry-canvas-debugging && rm -rf "$T"
skills/data/canvas-debugging/SKILL.mdCanvas Debugging Skill
Visual Layout Builder의 Konva 기반 Canvas 시스템 디버깅을 위한 전문 스킬입니다. Grid 위치 계산, 컴포넌트 충돌, 스냅 동작 문제를 해결합니다.
When to Use
- Canvas에서 컴포넌트 위치가 잘못 표시될 때
- Grid 스냅이 예상대로 동작하지 않을 때
- 컴포넌트 겹침(overlap) 문제 발생 시
- Canvas→CSS Grid 변환 오류 디버깅
- Breakpoint 전환 시 레이아웃 문제
- 드래그 앤 드롭 동작 이상
관련 이슈components/canvas/
Canvas System Architecture
Core Components
components/canvas/ ├── KonvaCanvas.tsx # Main canvas with Konva Stage/Layer ├── ComponentNode.tsx # Individual component rendering ├── ComponentPreview.tsx # Drag preview ├── Canvas.tsx # Canvas + component management └── index.ts # Public exports
Canvas Coordinate System
Canvas Grid (0-based coordinates) ┌───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┬───┐ │0,0│1,0│2,0│3,0│4,0│5,0│6,0│7,0│8,0│9,0│10 │11 │ Row 0 ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ │0,1│1,1│2,1│...│...│...│...│...│...│...│...│11,1 Row 1 ├───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┼───┤ │...│...│...│...│...│...│...│...│...│...│...│...│ ... └───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┴───┘ CanvasLayout: - x: Grid column start (0-based) - y: Grid row start (0-based) - width: Number of columns to span - height: Number of rows to span
CSS Grid Conversion (1-based)
// Canvas (0-based) → CSS Grid (1-based) gridColumn: `${x + 1} / ${x + width + 1}` gridRow: `${y + 1} / ${y + height + 1}` gridArea: `${y + 1} / ${x + 1} / ${y + height + 1} / ${x + width + 1}` // Example: Canvas { x: 0, y: 0, width: 12, height: 1 } // → CSS Grid: "1 / 1 / 2 / 13" (grid-area format)
Common Issues & Solutions
Issue 1: Component Position Mismatch
Symptom: Component appears in wrong position after drag
Debug Steps:
// 1. Check pixel-to-grid conversion const cellWidth = canvasWidth / gridCols const cellHeight = canvasHeight / gridRows console.log('Cell dimensions:', { cellWidth, cellHeight }) // 2. Verify snap calculation const gridX = Math.floor(pixelX / cellWidth) const gridY = Math.floor(pixelY / cellHeight) console.log('Grid position:', { gridX, gridY }) // 3. Check responsiveCanvasLayout console.log('Component layout:', component.responsiveCanvasLayout?.[breakpoint])
Common Causes:
vsMath.round()
inconsistencyMath.floor()- Canvas padding/margin not accounted for
- Breakpoint mismatch (editing mobile, viewing desktop)
Issue 2: Component Overlap Detection
Symptom: Components overlap but no warning shown
Debug with schema-validation.ts:
import { validateSchema } from '@/lib/schema-validation' const result = validateSchema(schema) // Check for overlap warnings const overlapWarnings = result.warnings?.filter( w => w.code === 'CANVAS_COMPONENTS_OVERLAP' ) console.log('Overlapping components:', overlapWarnings)
Overlap Detection Logic:
function checkOverlap(a: CanvasLayout, b: CanvasLayout): boolean { // Check if rectangles overlap return !( a.x + a.width <= b.x || // a is left of b b.x + b.width <= a.x || // b is left of a a.y + a.height <= b.y || // a is above b b.y + b.height <= a.y // b is above a ) }
Issue 3: Grid Snap Not Working
Symptom: Components don't snap to grid when dragged
Debug Steps:
// lib/snap-to-grid.ts export function snapToGrid( x: number, y: number, cellWidth: number, cellHeight: number ): { x: number; y: number } { console.log('Input:', { x, y }) console.log('Cell size:', { cellWidth, cellHeight }) const snappedX = Math.round(x / cellWidth) * cellWidth const snappedY = Math.round(y / cellHeight) * cellHeight console.log('Snapped:', { snappedX, snappedY }) return { x: snappedX, y: snappedY } }
Check Grid Constraints:
import { GRID_CONSTRAINTS } from '@/lib/schema-utils' console.log('Grid constraints:', { minCols: GRID_CONSTRAINTS.minCols, // 4 maxCols: GRID_CONSTRAINTS.maxCols, // 24 minRows: GRID_CONSTRAINTS.minRows, // 4 maxRows: GRID_CONSTRAINTS.maxRows // 50 })
Issue 4: Canvas Out of Bounds
Symptom: Component placed outside grid bounds
Validation Code:
CANVAS_OUT_OF_BOUNDS
function validateBounds(layout: CanvasLayout, gridCols: number, gridRows: number) { const issues = [] if (layout.x < 0 || layout.y < 0) { issues.push('CANVAS_NEGATIVE_COORDINATE') } if (layout.x + layout.width > gridCols) { issues.push(`Out of bounds: x(${layout.x}) + width(${layout.width}) > gridCols(${gridCols})`) } if (layout.y + layout.height > gridRows) { issues.push(`Out of bounds: y(${layout.y}) + height(${layout.height}) > gridRows(${gridRows})`) } return issues }
Issue 5: Breakpoint Layout Inheritance
Symptom: Component has layout in mobile but not in desktop
Debug:
// Check responsiveCanvasLayout for all breakpoints const component = schema.components.find(c => c.id === 'c1') console.log('Canvas layouts by breakpoint:') for (const bp of schema.breakpoints) { const layout = component?.responsiveCanvasLayout?.[bp.name] console.log(` ${bp.name}:`, layout || 'MISSING') } // After normalization const normalized = normalizeSchema(schema) const normalizedComponent = normalized.components.find(c => c.id === 'c1') console.log('After normalization:') for (const bp of normalized.breakpoints) { const layout = normalizedComponent?.responsiveCanvasLayout?.[bp.name] console.log(` ${bp.name}:`, layout || 'STILL MISSING') }
Issue 6: CSS Grid Conversion Errors
Symptom: AI-generated code doesn't match canvas layout
Debug canvas-to-grid.ts:
import { canvasToGridPositions } from '@/lib/canvas-to-grid' const positions = canvasToGridPositions( schema.components, 'desktop', 12, // gridCols 8 // gridRows ) console.log('Grid positions:') positions.forEach(p => { console.log(` ${p.componentId}:`) console.log(` gridColumn: ${p.gridColumn}`) console.log(` gridRow: ${p.gridRow}`) console.log(` gridArea: ${p.gridArea}`) })
Expected Format:
c1 (Header): gridColumn: "1 / 13" // Full width (12 cols) gridRow: "1 / 2" // 1 row height gridArea: "1 / 1 / 2 / 13"
Visual Layout Descriptor Debugging
import { describeVisualLayout } from '@/lib/visual-layout-descriptor' const description = describeVisualLayout( schema.components, 'desktop', 12, 8 ) console.log('Summary:', description.summary) console.log('\nRow by row:') description.rowByRow.forEach(row => console.log(' ', row)) console.log('\nSpatial relationships:') description.spatialRelationships.forEach(rel => console.log(' ', rel)) console.log('\nImplementation hints:') description.implementationHints.forEach(hint => console.log(' ', hint))
Canvas Validation Codes
| Code | Type | Description |
|---|---|---|
| Warning | Canvas order ≠ DOM order |
| Warning | Side-by-side components |
| Warning | Components overlapping |
| Warning | Outside grid bounds |
| Warning | width=0 or height=0 |
| Error | x<0 or y<0 |
| Warning | Non-integer coordinates |
| Warning | Not in layout.components |
| Warning | No canvas position |
Debugging Konva Rendering
Stage/Layer Issues
// In KonvaCanvas.tsx useEffect(() => { console.log('Stage dimensions:', { width: stageRef.current?.width(), height: stageRef.current?.height() }) console.log('Layer children:', layerRef.current?.children?.length) }, [])
Component Node Position
// In ComponentNode.tsx const handleDragEnd = (e: Konva.KonvaEventObject<DragEvent>) => { const node = e.target console.log('Drag end position:', { x: node.x(), y: node.y(), absolutePosition: node.absolutePosition() }) // Convert to grid const gridX = Math.floor(node.x() / cellWidth) const gridY = Math.floor(node.y() / cellHeight) console.log('Grid position:', { gridX, gridY }) }
Grid Constraint Validation
import { calculateMinimumGridSize } from '@/lib/grid-constraints' // Check if grid can be reduced const { minCols, minRows } = calculateMinimumGridSize( schema.components, 'desktop' ) console.log('Minimum required grid:', { minCols, minRows }) console.log('Current grid:', { cols: breakpoint.gridCols, rows: breakpoint.gridRows }) if (breakpoint.gridCols < minCols || breakpoint.gridRows < minRows) { console.warn('Grid too small for components!') }
Quick Debug Checklist
# 1. Check current breakpoint console.log('Current breakpoint:', currentBreakpoint) # 2. Verify grid configuration console.log('Grid config:', breakpoint) # 3. Log all component positions schema.components.forEach(c => { const layout = c.responsiveCanvasLayout?.[currentBreakpoint] console.log(`${c.id} (${c.name}):`, layout || 'NO LAYOUT') }) # 4. Run validation const result = validateSchema(schema) console.log('Validation:', result) # 5. Check CSS Grid output const gridPositions = canvasToGridPositions( schema.components, currentBreakpoint, breakpoint.gridCols, breakpoint.gridRows ) console.log('CSS Grid positions:', gridPositions)
Reference Files
- Canvas 렌더링components/canvas/KonvaCanvas.tsx
- 컴포넌트 노드components/canvas/ComponentNode.tsx
- Canvas→CSS Grid 변환lib/canvas-to-grid.ts
- Canvas 유틸리티lib/canvas-utils.ts
- Grid 제약 조건lib/grid-constraints.ts
- 스냅 로직lib/snap-to-grid.ts
- Canvas 검증 로직lib/schema-validation.ts
- 시각적 레이아웃 설명lib/visual-layout-descriptor.ts