/** * Screen Flow - Layout and graph traversal helpers */ import type { ScreenDefinition, ConnectionDef, FlowType } from './types' /** * BFS traversal to find all connected nodes from a start node. */ export function findConnectedNodes( startNodeId: string, connections: ConnectionDef[], direction: 'children' | 'parents' | 'both' = 'children' ): Set { const connected = new Set() connected.add(startNodeId) const queue = [startNodeId] while (queue.length > 0) { const current = queue.shift()! connections.forEach(conn => { if ((direction === 'children' || direction === 'both') && conn.source === current) { if (!connected.has(conn.target)) { connected.add(conn.target) queue.push(conn.target) } } if ((direction === 'parents' || direction === 'both') && conn.target === current) { if (!connected.has(conn.source)) { connected.add(conn.source) queue.push(conn.source) } } }) } return connected } const STUDIO_POSITIONS: Record = { navigation: { x: 400, y: 50 }, content: { x: 50, y: 250 }, communication: { x: 750, y: 250 }, school: { x: 50, y: 500 }, admin: { x: 750, y: 500 }, ai: { x: 400, y: 380 }, } const ADMIN_POSITIONS: Record = { overview: { x: 400, y: 30 }, dsgvo: { x: 50, y: 150 }, compliance: { x: 700, y: 150 }, ai: { x: 50, y: 350 }, communication: { x: 400, y: 350 }, infrastructure: { x: 700, y: 350 }, education: { x: 50, y: 550 }, development: { x: 400, y: 550 }, } /** * Compute the x/y position for a node based on its category and index. */ export function getNodePosition( id: string, category: string, screens: ScreenDefinition[], flowType: FlowType ) { const positions = flowType === 'studio' ? STUDIO_POSITIONS : ADMIN_POSITIONS const base = positions[category] || { x: 400, y: 300 } const categoryScreens = screens.filter(s => s.category === category) const categoryIndex = categoryScreens.findIndex(s => s.id === id) const cols = Math.ceil(Math.sqrt(categoryScreens.length + 1)) const row = Math.floor(categoryIndex / cols) const col = categoryIndex % cols return { x: base.x + col * 160, y: base.y + row * 90, } }