Claude-skill-registry dev-debug-canvas-nested-groups

This skill should be used when debugging Vue Flow canvas issues involving nested nodes, parent-child relationships, position coordinate systems, and containment detection. Triggers on nested group problems, section nodes jumping, parent-child synchronization bugs, position persistence issues, and coordinate transformation errors in Vue Flow implementations.

install
source · Clone the upstream repo
git clone https://github.com/majiayu000/claude-skill-registry
Claude Code · Install into ~/.claude/skills/
T=$(mktemp -d) && git clone --depth=1 https://github.com/majiayu000/claude-skill-registry "$T" && mkdir -p ~/.claude/skills && cp -r "$T/skills/data/dev-debug-canvas-nested-groups" ~/.claude/skills/majiayu000-claude-skill-registry-dev-debug-canvas-nested-groups && rm -rf "$T"
manifest: skills/data/dev-debug-canvas-nested-groups/SKILL.md
source content

Vue Flow Nested Nodes Debugging Skill

Overview

Debug Vue Flow nested nodes, parent-child relationships, position coordinate systems, and containment detection issues. This skill provides systematic diagnostic procedures, code patterns, and utilities for troubleshooting complex canvas hierarchy problems.

When to Use This Skill

Use this skill when encountering:

  • Nested groups not moving together when parent is dragged
  • Section nodes jumping to unexpected positions
  • Parent-child relationships not being recognized
  • Tasks inside groups not being detected correctly
  • Position values appearing incorrect after operations
  • Groups resetting or losing their nested structure
  • Coordinate mismatches between UI and stored data

Quick Diagnostic

Before deep debugging, run these quick checks:

// 1. Check if parentNode is properly set
const node = getNode('task-123')
console.log('Parent:', node?.parentNode) // Should be group ID or undefined

// 2. Verify coordinate types
console.log('Position (relative):', node?.position)
console.log('ComputedPosition (absolute):', node?.computedPosition)

// 3. Check extent mode
console.log('Extent:', node?.extent) // 'parent' for constrained children

Core Concept: Coordinate Systems

CRITICAL: Vue Flow uses TWO coordinate systems that must not be confused:

PropertyTypeWhen Used
node.position
Relative to parentChild nodes with
parentNode
set
node.position
Absolute canvas coordsRoot nodes (no parent)
node.computedPosition
Always absoluteUse for canvas operations

Golden Rule: Store relative positions for children, use

computedPosition
for calculations.

Debugging Workflow

Step 1: Identify the Problem Type

Consult the troubleshooting tree in

references/troubleshooting-tree.md
:

  • Position Issues: Node jumping, incorrect placement, reset positions
  • Hierarchy Issues: Parent-child not recognized, nesting lost
  • Movement Issues: Children not moving with parent, group drag problems
  • Containment Issues: Tasks not detected inside groups

Step 2: Enable Debug Logging

Add targeted logging based on problem type:

// For position debugging
watch(() => node.position, (newPos, oldPos) => {
  console.log(`[POSITION] ${node.id}: ${JSON.stringify(oldPos)} → ${JSON.stringify(newPos)}`)
}, { deep: true })

// For hierarchy debugging
watch(() => node.parentNode, (newParent, oldParent) => {
  console.log(`[HIERARCHY] ${node.id}: parent ${oldParent} → ${newParent}`)
})

Step 3: Use Diagnostic Utilities

Load the debugging utilities from

scripts/vueFlowDebug.ts
:

import { logNodeHierarchy, validateParentChild, dumpCanvasState } from './vueFlowDebug'

// Dump full canvas state
dumpCanvasState(getNodes.value, getEdges.value)

// Validate all parent-child relationships
validateParentChild(getNodes.value)

// Log hierarchy tree
logNodeHierarchy(getNodes.value)

Step 4: Apply Fix Patterns

Consult

references/code-patterns.md
for proven solutions:

  • Reparenting: Correct way to move node between parents
  • Position Conversion: Converting between coordinate systems
  • Containment Detection: Proper center-point-in-bounds check

Common Bugs and Solutions

Bug 1: Child Not Moving with Parent

Symptom: Dragging parent leaves children behind

Cause:

parentNode
property not set or position stored as absolute

Fix:

// Ensure parentNode is set
updateNode(childId, { parentNode: parentId })

// Convert to relative position
const parent = getNode(parentId)
const relativePos = {
  x: child.computedPosition.x - parent.computedPosition.x,
  y: child.computedPosition.y - parent.computedPosition.y
}
updateNode(childId, { position: relativePos })

Bug 2: Node Jumping When Set as Child

Symptom: Node teleports when assigned parent

Cause: Absolute position interpreted as relative after parent set

Fix: Convert position BEFORE setting parentNode:

const parent = getNode(parentId)
const child = getNode(childId)

// Step 1: Calculate relative position first
const relativePos = {
  x: child.computedPosition.x - parent.computedPosition.x,
  y: child.computedPosition.y - parent.computedPosition.y
}

// Step 2: Update both in same call
updateNode(childId, {
  parentNode: parentId,
  position: relativePos
})

Bug 3: Containment Detection Fails

Symptom: Tasks inside group not recognized

Cause: Comparing relative position against absolute bounds

Fix: Always use

computedPosition
for containment:

function isInsideGroup(task: Node, group: Node): boolean {
  // WRONG: task.position (may be relative)
  // RIGHT: task.computedPosition (always absolute)

  const taskCenter = {
    x: task.computedPosition.x + (task.dimensions?.width ?? 200) / 2,
    y: task.computedPosition.y + (task.dimensions?.height ?? 100) / 2
  }

  return (
    taskCenter.x >= group.computedPosition.x &&
    taskCenter.x <= group.computedPosition.x + (group.dimensions?.width ?? 400) &&
    taskCenter.y >= group.computedPosition.y &&
    taskCenter.y <= group.computedPosition.y + (group.dimensions?.height ?? 300)
  )
}

Bug 4: Position Persistence Issues

Symptom: Positions reset on page reload

Cause: Storing

computedPosition
instead of
position
for children

Fix: Store the correct coordinate type:

function saveNodePosition(node: Node) {
  // For children: store relative position
  // For root nodes: store absolute position
  // node.position is ALWAYS what should be stored

  await saveToDatabase({
    id: node.id,
    position: node.position,  // NOT computedPosition
    parentNode: node.parentNode
  })
}

Bug 5: Stale Parent Reference

Symptom: "Stale parentGroupId" warnings, child outside parent bounds

Cause: Parent moved/deleted but child still references it

Fix: Validate and repair on load:

function validateHierarchy(nodes: Node[]) {
  const nodeMap = new Map(nodes.map(n => [n.id, n]))

  for (const node of nodes) {
    if (node.parentNode && !nodeMap.has(node.parentNode)) {
      console.warn(`Orphan node ${node.id} - parent ${node.parentNode} missing`)
      // Convert to root node
      node.parentNode = undefined
      // Position is already absolute-equivalent, keep as-is
    }
  }
}

Resources

references/

  • coordinate-system.md
    - Deep dive into Vue Flow coordinate systems
  • troubleshooting-tree.md
    - Systematic problem diagnosis flowchart
  • code-patterns.md
    - Proven code patterns for common operations
  • e2e-test-patterns.md
    - Playwright E2E test patterns for nested groups and parent movement

scripts/

  • vueFlowDebug.ts
    - TypeScript debugging utilities for Vue Flow

Verification Checklist

After applying fixes, verify:

  • Dragging parent moves all children together
  • Positions persist correctly across page reloads
  • New tasks dropped in group are detected as children
  • Nested groups maintain hierarchy when moved
  • No position jumping on any operation
  • Console shows no stale parent warnings