@jamesyong42/infinite-canvas 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +308 -0
- package/dist/advanced.cjs +1578 -0
- package/dist/advanced.cjs.map +1 -0
- package/dist/advanced.d.cts +93 -0
- package/dist/advanced.d.ts +93 -0
- package/dist/advanced.js +194 -0
- package/dist/advanced.js.map +1 -0
- package/dist/chunk-LQMLG3P5.js +456 -0
- package/dist/chunk-LQMLG3P5.js.map +1 -0
- package/dist/chunk-YVWTBG7H.js +1447 -0
- package/dist/chunk-YVWTBG7H.js.map +1 -0
- package/dist/context-BEyR4AhJ.d.ts +441 -0
- package/dist/context-tR7KjG_v.d.cts +441 -0
- package/dist/ecs.cjs +487 -0
- package/dist/ecs.cjs.map +1 -0
- package/dist/ecs.d.cts +65 -0
- package/dist/ecs.d.ts +65 -0
- package/dist/ecs.js +17 -0
- package/dist/ecs.js.map +1 -0
- package/dist/index.cjs +3550 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +154 -0
- package/dist/index.d.ts +154 -0
- package/dist/index.js +1652 -0
- package/dist/index.js.map +1 -0
- package/dist/profiler-DKuXy4MW.d.cts +137 -0
- package/dist/profiler-DKuXy4MW.d.ts +137 -0
- package/package.json +79 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands.ts","../src/math.ts","../src/resources.ts","../src/systems.ts","../src/engine.ts","../src/react/InfiniteCanvas.tsx","../src/react/WidgetProvider.tsx","../src/react/registry.ts","../src/react/widget-hooks.ts"],"sourcesContent":["import type { ComponentType, EntityId, World } from './ecs/index.js';\n\n// === Command Interface ===\n\nexport interface Command {\n\texecute(world: World): void;\n\tundo(world: World): void;\n}\n\n// === Command Buffer with Undo/Redo ===\n\nexport class CommandBuffer {\n\tprivate undoStack: Command[][] = [];\n\tprivate redoStack: Command[][] = [];\n\tprivate currentGroup: Command[] | null = null;\n\n\t/** Start grouping commands (e.g., on pointerdown). All commands until endGroup() are one undo step. */\n\tbeginGroup() {\n\t\tthis.currentGroup = [];\n\t}\n\n\t/** Execute a command and record it for undo. */\n\texecute(command: Command, world: World) {\n\t\tcommand.execute(world);\n\n\t\tif (this.currentGroup) {\n\t\t\tthis.currentGroup.push(command);\n\t\t} else {\n\t\t\t// Single command = its own undo group\n\t\t\tthis.undoStack.push([command]);\n\t\t\tthis.redoStack.length = 0;\n\t\t}\n\t}\n\n\t/** Close the current group — all commands since beginGroup() become one undo step. */\n\tendGroup() {\n\t\tif (this.currentGroup && this.currentGroup.length > 0) {\n\t\t\tthis.undoStack.push(this.currentGroup);\n\t\t\tthis.redoStack.length = 0;\n\t\t}\n\t\tthis.currentGroup = null;\n\t}\n\n\t/** Undo the last command group. */\n\tundo(world: World): boolean {\n\t\t// Close any open group first\n\t\tif (this.currentGroup) {\n\t\t\tthis.endGroup();\n\t\t}\n\n\t\tconst group = this.undoStack.pop();\n\t\tif (!group) return false;\n\n\t\t// Undo in reverse order\n\t\tfor (let i = group.length - 1; i >= 0; i--) {\n\t\t\tgroup[i].undo(world);\n\t\t}\n\t\tthis.redoStack.push(group);\n\t\treturn true;\n\t}\n\n\t/** Redo the last undone command group. */\n\tredo(world: World): boolean {\n\t\tconst group = this.redoStack.pop();\n\t\tif (!group) return false;\n\n\t\tfor (const cmd of group) {\n\t\t\tcmd.execute(world);\n\t\t}\n\t\tthis.undoStack.push(group);\n\t\treturn true;\n\t}\n\n\tcanUndo(): boolean {\n\t\treturn (\n\t\t\tthis.undoStack.length > 0 || (this.currentGroup !== null && this.currentGroup.length > 0)\n\t\t);\n\t}\n\n\tcanRedo(): boolean {\n\t\treturn this.redoStack.length > 0;\n\t}\n\n\tclear() {\n\t\tthis.undoStack.length = 0;\n\t\tthis.redoStack.length = 0;\n\t\tthis.currentGroup = null;\n\t}\n\n\tget undoSize(): number {\n\t\treturn this.undoStack.length;\n\t}\n\tget redoSize(): number {\n\t\treturn this.redoStack.length;\n\t}\n}\n\n// === Built-in Commands ===\n\nexport class MoveCommand implements Command {\n\tconstructor(\n\t\tprivate entityIds: EntityId[],\n\t\tprivate dx: number,\n\t\tprivate dy: number,\n\t\tprivate transformType: ComponentType<{ x: number; y: number }>,\n\t) {}\n\n\texecute(world: World) {\n\t\tfor (const id of this.entityIds) {\n\t\t\tconst t = world.getComponent(id, this.transformType);\n\t\t\tif (t) {\n\t\t\t\tworld.setComponent(id, this.transformType, { x: t.x + this.dx, y: t.y + this.dy });\n\t\t\t}\n\t\t}\n\t}\n\n\tundo(world: World) {\n\t\tfor (const id of this.entityIds) {\n\t\t\tconst t = world.getComponent(id, this.transformType);\n\t\t\tif (t) {\n\t\t\t\tworld.setComponent(id, this.transformType, { x: t.x - this.dx, y: t.y - this.dy });\n\t\t\t}\n\t\t}\n\t}\n}\n\nexport class ResizeCommand implements Command {\n\tstatic readonly MIN_SIZE = 20;\n\n\tconstructor(\n\t\tprivate entityId: EntityId,\n\t\tprivate before: { x: number; y: number; width: number; height: number },\n\t\tprivate after: { x: number; y: number; width: number; height: number },\n\t\tprivate transformType: ComponentType<{ x: number; y: number; width: number; height: number }>,\n\t) {\n\t\t// Enforce min-size so redo always produces valid bounds\n\t\tthis.after = {\n\t\t\t...after,\n\t\t\twidth: Math.max(ResizeCommand.MIN_SIZE, after.width),\n\t\t\theight: Math.max(ResizeCommand.MIN_SIZE, after.height),\n\t\t};\n\t}\n\n\texecute(world: World) {\n\t\tworld.setComponent(this.entityId, this.transformType, this.after);\n\t}\n\n\tundo(world: World) {\n\t\tworld.setComponent(this.entityId, this.transformType, this.before);\n\t}\n}\n\nexport class SetComponentCommand<T> implements Command {\n\tconstructor(\n\t\tprivate entityId: EntityId,\n\t\tprivate type: ComponentType<T>,\n\t\tprivate before: Partial<T>,\n\t\tprivate after: Partial<T>,\n\t) {}\n\n\texecute(world: World) {\n\t\tworld.setComponent(this.entityId, this.type, this.after);\n\t}\n\n\tundo(world: World) {\n\t\tworld.setComponent(this.entityId, this.type, this.before);\n\t}\n}\n","export interface Vec2 {\n\tx: number;\n\ty: number;\n}\n\nexport interface Rect {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\nexport interface AABB {\n\tminX: number;\n\tminY: number;\n\tmaxX: number;\n\tmaxY: number;\n}\n\n/** Convert WorldBounds-shaped data to AABB */\nexport function worldBoundsToAABB(wb: {\n\tworldX: number;\n\tworldY: number;\n\tworldWidth: number;\n\tworldHeight: number;\n}): AABB {\n\treturn {\n\t\tminX: wb.worldX,\n\t\tminY: wb.worldY,\n\t\tmaxX: wb.worldX + wb.worldWidth,\n\t\tmaxY: wb.worldY + wb.worldHeight,\n\t};\n}\n\n/** Convert a Rect to AABB */\nexport function rectToAABB(r: Rect): AABB {\n\treturn {\n\t\tminX: r.x,\n\t\tminY: r.y,\n\t\tmaxX: r.x + r.width,\n\t\tmaxY: r.y + r.height,\n\t};\n}\n\n/** Convert AABB to Rect */\nexport function aabbToRect(a: AABB): Rect {\n\treturn {\n\t\tx: a.minX,\n\t\ty: a.minY,\n\t\twidth: a.maxX - a.minX,\n\t\theight: a.maxY - a.minY,\n\t};\n}\n\n/** Test if two AABBs overlap */\nexport function intersectsAABB(a: AABB, b: AABB): boolean {\n\treturn a.maxX >= b.minX && a.minX <= b.maxX && a.maxY >= b.minY && a.minY <= b.maxY;\n}\n\n/** Test if a point is inside an AABB */\nexport function pointInAABB(px: number, py: number, a: AABB): boolean {\n\treturn px >= a.minX && px <= a.maxX && py >= a.minY && py <= a.maxY;\n}\n\n/** Convert screen coordinates to world coordinates */\nexport function screenToWorld(\n\tscreenX: number,\n\tscreenY: number,\n\tcamera: { x: number; y: number; zoom: number },\n): Vec2 {\n\treturn {\n\t\tx: screenX / camera.zoom + camera.x,\n\t\ty: screenY / camera.zoom + camera.y,\n\t};\n}\n\n/** Convert world coordinates to screen coordinates */\nexport function worldToScreen(\n\tworldX: number,\n\tworldY: number,\n\tcamera: { x: number; y: number; zoom: number },\n): Vec2 {\n\treturn {\n\t\tx: (worldX - camera.x) * camera.zoom,\n\t\ty: (worldY - camera.y) * camera.zoom,\n\t};\n}\n\n/** Clamp a value between min and max */\nexport function clamp(value: number, min: number, max: number): number {\n\treturn Math.max(min, Math.min(max, value));\n}\n","import { defineResource } from './ecs/index.js';\nimport type { EntityId } from './ecs/index.js';\n\nexport interface NavigationFrame {\n\tcontainerId: EntityId | null;\n\tcamera: { x: number; y: number; zoom: number };\n}\n\nexport const CameraResource = defineResource('Camera', {\n\tx: 0,\n\ty: 0,\n\tzoom: 1,\n});\n\nexport const ViewportResource = defineResource('Viewport', {\n\twidth: 0,\n\theight: 0,\n\tdpr: 1,\n});\n\nexport const ZoomConfigResource = defineResource('ZoomConfig', {\n\tmin: 0.1,\n\tmax: 5.0,\n});\n\nexport const BreakpointConfigResource = defineResource('BreakpointConfig', {\n\tmicro: 40,\n\tcompact: 120,\n\tnormal: 500,\n\texpanded: 1200,\n});\n\nexport const NavigationStackResource = defineResource('NavigationStack', {\n\tframes: [{ containerId: null, camera: { x: 0, y: 0, zoom: 1 } }] as NavigationFrame[],\n\tchanged: false,\n});\n\nexport type Breakpoint = 'micro' | 'compact' | 'normal' | 'expanded' | 'detailed';\n","import {\n\tActive,\n\tChildren,\n\tParent,\n\tTransform2D,\n\tVisible,\n\tWidget,\n\tWidgetBreakpoint,\n\tWorldBounds,\n} from './components.js';\nimport { defineSystem } from './ecs/index.js';\nimport type { World } from './ecs/index.js';\nimport { SpatialIndexResource } from './engine.js';\nimport { intersectsAABB, worldBoundsToAABB } from './math.js';\nimport {\n\tBreakpointConfigResource,\n\tCameraResource,\n\tNavigationStackResource,\n\tViewportResource,\n} from './resources.js';\nimport type { Breakpoint } from './resources.js';\n\n/**\n * Propagate transforms down the parent-child hierarchy.\n * Computes WorldBounds for every entity with Transform2D.\n * Uses change detection — only processes dirty entities and their descendants.\n */\nexport const transformPropagateSystem = defineSystem({\n\tname: 'transformPropagate',\n\texecute: (world: World) => {\n\t\tconst changed = world.queryChanged(Transform2D);\n\t\tconst processed = new Set<number>();\n\n\t\tfor (const entity of changed) {\n\t\t\tpropagateEntity(world, entity, processed);\n\t\t}\n\n\t\tfor (const entity of world.queryAdded(Transform2D)) {\n\t\t\tif (!processed.has(entity)) {\n\t\t\t\tpropagateEntity(world, entity, processed);\n\t\t\t}\n\t\t}\n\t},\n});\n\nfunction propagateEntity(world: World, entity: number, processed: Set<number>) {\n\tif (processed.has(entity)) return;\n\tprocessed.add(entity);\n\n\tconst transform = world.getComponent(entity, Transform2D);\n\tif (!transform) return;\n\n\tlet worldX = transform.x;\n\tlet worldY = transform.y;\n\n\tconst parentComp = world.getComponent(entity, Parent);\n\tif (parentComp && world.entityExists(parentComp.id)) {\n\t\tconst parentBounds = world.getComponent(parentComp.id, WorldBounds);\n\t\tif (parentBounds) {\n\t\t\tworldX += parentBounds.worldX;\n\t\t\tworldY += parentBounds.worldY;\n\t\t}\n\t}\n\n\tif (!world.hasComponent(entity, WorldBounds)) {\n\t\tworld.addComponent(entity, WorldBounds, {\n\t\t\tworldX,\n\t\t\tworldY,\n\t\t\tworldWidth: transform.width,\n\t\t\tworldHeight: transform.height,\n\t\t});\n\t} else {\n\t\tworld.setComponent(entity, WorldBounds, {\n\t\t\tworldX,\n\t\t\tworldY,\n\t\t\tworldWidth: transform.width,\n\t\t\tworldHeight: transform.height,\n\t\t});\n\t}\n\n\tconst children = world.getComponent(entity, Children);\n\tif (children) {\n\t\tfor (const childId of children.ids) {\n\t\t\tpropagateEntity(world, childId, processed);\n\t\t}\n\t}\n}\n\n/**\n * Filter entities to the active navigation layer.\n * Only runs when navigation stack changes.\n */\nexport const navigationFilterSystem = defineSystem({\n\tname: 'navigationFilter',\n\tafter: 'transformPropagate',\n\texecute: (world: World) => {\n\t\tconst navStack = world.getResource(NavigationStackResource);\n\t\tif (!navStack.changed) return;\n\n\t\tconst currentFrame = navStack.frames[navStack.frames.length - 1];\n\n\t\tfor (const entity of world.queryTagged(Active)) {\n\t\t\tworld.removeTag(entity, Active);\n\t\t}\n\n\t\tif (currentFrame.containerId === null) {\n\t\t\tfor (const entity of world.query(Transform2D)) {\n\t\t\t\tif (!world.hasComponent(entity, Parent)) {\n\t\t\t\t\tworld.addTag(entity, Active);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tconst children = world.getComponent(currentFrame.containerId, Children);\n\t\t\tif (children) {\n\t\t\t\tfor (const childId of children.ids) {\n\t\t\t\t\tworld.addTag(childId, Active);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tnavStack.changed = false;\n\t},\n});\n\n/**\n * Viewport culling — mark Active entities that intersect the viewport as Visible.\n */\nexport const cullSystem = defineSystem({\n\tname: 'cull',\n\tafter: 'navigationFilter',\n\texecute: (world: World) => {\n\t\tconst camera = world.getResource(CameraResource);\n\t\tconst viewport = world.getResource(ViewportResource);\n\t\tif (viewport.width === 0 || viewport.height === 0) return;\n\n\t\tconst res = world.getResource(SpatialIndexResource);\n\t\tconst spatialIndex = res.instance;\n\n\t\tconst overscan = 200 / camera.zoom;\n\t\tconst vpWorldAABB = {\n\t\t\tminX: camera.x - overscan,\n\t\t\tminY: camera.y - overscan,\n\t\t\tmaxX: camera.x + viewport.width / camera.zoom + overscan,\n\t\t\tmaxY: camera.y + viewport.height / camera.zoom + overscan,\n\t\t};\n\n\t\tfor (const entity of world.queryTagged(Visible)) {\n\t\t\tworld.removeTag(entity, Visible);\n\t\t}\n\n\t\tif (spatialIndex && spatialIndex.size > 0) {\n\t\t\tconst candidates = spatialIndex.search(vpWorldAABB);\n\t\t\tfor (const entry of candidates) {\n\t\t\t\tif (world.hasTag(entry.entityId, Active)) {\n\t\t\t\t\tworld.addTag(entry.entityId, Visible);\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tfor (const entity of world.queryTagged(Active)) {\n\t\t\t\tconst wb = world.getComponent(entity, WorldBounds);\n\t\t\t\tif (wb && intersectsAABB(worldBoundsToAABB(wb), vpWorldAABB)) {\n\t\t\t\t\tworld.addTag(entity, Visible);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n});\n\n/**\n * Compute breakpoints for visible widgets based on screen size.\n * Fix #10: Always update screenWidth/screenHeight even if breakpoint tier doesn't change.\n */\nexport const breakpointSystem = defineSystem({\n\tname: 'breakpoint',\n\tafter: 'cull',\n\texecute: (world: World) => {\n\t\tconst camera = world.getResource(CameraResource);\n\t\tconst config = world.getResource(BreakpointConfigResource);\n\n\t\tfor (const entity of world.query(Widget, Visible)) {\n\t\t\tconst transform = world.getComponent(entity, Transform2D);\n\t\t\tif (!transform) continue;\n\n\t\t\tconst screenWidth = transform.width * camera.zoom;\n\t\t\tconst screenHeight = transform.height * camera.zoom;\n\n\t\t\tlet bp: Breakpoint;\n\t\t\tif (screenWidth < config.micro) bp = 'micro';\n\t\t\telse if (screenWidth < config.compact) bp = 'compact';\n\t\t\telse if (screenWidth < config.normal) bp = 'normal';\n\t\t\telse if (screenWidth < config.expanded) bp = 'expanded';\n\t\t\telse bp = 'detailed';\n\n\t\t\tconst existing = world.getComponent(entity, WidgetBreakpoint);\n\t\t\tif (!existing) {\n\t\t\t\tworld.addComponent(entity, WidgetBreakpoint, {\n\t\t\t\t\tcurrent: bp,\n\t\t\t\t\tscreenWidth,\n\t\t\t\t\tscreenHeight,\n\t\t\t\t});\n\t\t\t} else {\n\t\t\t\t// Fix #10: Update if breakpoint tier changed OR screen dimensions changed significantly\n\t\t\t\tconst bpChanged = existing.current !== bp;\n\t\t\t\tconst sizeChanged =\n\t\t\t\t\tMath.abs(existing.screenWidth - screenWidth) > 1 ||\n\t\t\t\t\tMath.abs(existing.screenHeight - screenHeight) > 1;\n\n\t\t\t\tif (bpChanged || sizeChanged) {\n\t\t\t\t\tworld.setComponent(entity, WidgetBreakpoint, {\n\t\t\t\t\t\tcurrent: bp,\n\t\t\t\t\t\tscreenWidth,\n\t\t\t\t\t\tscreenHeight,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t},\n});\n\n/**\n * Sort visible entities by z-index (handled in engine.tick()).\n */\nexport const sortSystem = defineSystem({\n\tname: 'sort',\n\tafter: 'breakpoint',\n\texecute: (_world: World) => {\n\t\t// Sorting is done in engine.tick() after systems run\n\t},\n});\n","import { CommandBuffer, MoveCommand, ResizeCommand } from './commands.js';\nimport type { Command } from './commands.js';\nimport {\n\tActive,\n\tChildren,\n\tContainer,\n\tDraggable,\n\tParent,\n\tResizable,\n\tSelectable,\n\tSelected,\n\tTransform2D,\n\tVisible,\n\tWidget,\n\tWidgetBreakpoint,\n\tWidgetData,\n\tWorldBounds,\n\tZIndex,\n} from './components.js';\nimport type {\n\tComponentInit,\n\tComponentType,\n\tEntityId,\n\tSystemDef,\n\tTagType,\n\tUnsubscribe,\n\tWorld,\n} from './ecs/index.js';\nimport { SystemScheduler, createWorld, defineResource } from './ecs/index.js';\nimport { clamp, pointInAABB, screenToWorld, worldBoundsToAABB } from './math.js';\nimport { Profiler } from './profiler.js';\nimport {\n\tBreakpointConfigResource,\n\tCameraResource,\n\tNavigationStackResource,\n\tViewportResource,\n\tZoomConfigResource,\n} from './resources.js';\nimport type { Breakpoint } from './resources.js';\nimport { computeSnapGuides } from './snap.js';\nimport type { EqualSpacingIndicator, SnapGuide, SnapResult } from './snap.js';\nimport { SpatialIndex } from './spatial.js';\nimport {\n\tbreakpointSystem,\n\tcullSystem,\n\tnavigationFilterSystem,\n\tsortSystem,\n\ttransformPropagateSystem,\n} from './systems.js';\n\n// === Fix #11: SpatialIndex as a proper resource ===\nexport const SpatialIndexResource = defineResource('SpatialIndex', {\n\tinstance: null as SpatialIndex | null,\n});\n\n// === Pointer Directives ===\n\nexport type PointerDirective =\n\t| { action: 'passthrough' }\n\t| { action: 'passthrough-track-drag' }\n\t| { action: 'capture-drag' }\n\t| { action: 'capture-resize'; handle: ResizeHandlePos }\n\t| { action: 'capture-marquee' };\n\nexport type ResizeHandlePos = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw';\n\nexport interface Modifiers {\n\tshift: boolean;\n\tctrl: boolean;\n\talt: boolean;\n\tmeta: boolean;\n}\n\n// === Input state machine ===\n\ntype InputState =\n\t| { mode: 'idle' }\n\t| { mode: 'tracking'; entityId: EntityId; startX: number; startY: number }\n\t| {\n\t\t\tmode: 'dragging';\n\t\t\tentityId: EntityId;\n\t\t\tstartScreenX: number;\n\t\t\tstartScreenY: number;\n\t\t\tstartPositions: Map<EntityId, { x: number; y: number }>;\n\t\t\toriginalZIndices: Map<EntityId, number>;\n\t }\n\t| {\n\t\t\tmode: 'resizing';\n\t\t\tentityId: EntityId;\n\t\t\thandle: ResizeHandlePos;\n\t\t\tstartX: number;\n\t\t\tstartY: number;\n\t\t\tstartBounds: { x: number; y: number; width: number; height: number };\n\t }\n\t| { mode: 'marquee'; startX: number; startY: number };\n\nconst DEAD_ZONE_PX = 4;\n\n// === Visible entity for renderers ===\n\nexport interface VisibleEntity {\n\tentityId: EntityId;\n\tworldX: number;\n\tworldY: number;\n\tworldWidth: number;\n\tworldHeight: number;\n\tbreakpoint: Breakpoint;\n\tzIndex: number;\n\tsurface: string;\n\twidgetType: string;\n}\n\n// === Frame changes ===\n\nexport interface FrameChanges {\n\tpositionsChanged: EntityId[];\n\tbreakpointsChanged: EntityId[];\n\tentered: EntityId[];\n\texited: EntityId[];\n\tcameraChanged: boolean;\n\tnavigationChanged: boolean;\n\tselectionChanged: boolean;\n}\n\n// === Engine Config ===\n\nexport interface AddWidgetOptions {\n\ttype: string;\n\tposition: { x: number; y: number };\n\tsize: { width: number; height: number };\n\trotation?: number;\n\tdata?: Record<string, unknown>;\n\tsurface?: 'dom' | 'webgl';\n\tzIndex?: number;\n\tselectable?: boolean;\n\tdraggable?: boolean;\n\tresizable?: boolean;\n\tparent?: EntityId;\n}\n\nexport interface LayoutEngineConfig {\n\tmaxEntities?: number;\n\tzoom?: { min: number; max: number };\n\tbreakpoints?: { micro: number; compact: number; normal: number; expanded: number };\n}\n\n// === Engine ===\n\nexport interface LayoutEngine {\n\treadonly world: World;\n\n\t// Entity CRUD\n\tcreateEntity(inits?: ComponentInit[]): EntityId;\n\taddWidget(opts: AddWidgetOptions): EntityId;\n\tdestroyEntity(id: EntityId): void;\n\n\t// Shorthand\n\tget<T>(entity: EntityId, type: ComponentType<T>): T | undefined;\n\tset<T>(entity: EntityId, type: ComponentType<T>, data: Partial<T>): void;\n\thas(entity: EntityId, type: ComponentType | TagType): boolean;\n\n\t// Extensions\n\tregisterSystem(system: SystemDef): void;\n\tremoveSystem(name: string): void;\n\n\t// Camera\n\tgetCamera(): { x: number; y: number; zoom: number };\n\tpanBy(dx: number, dy: number): void;\n\tpanTo(worldX: number, worldY: number): void;\n\tzoomAtPoint(screenX: number, screenY: number, delta: number): void;\n\tzoomTo(zoom: number): void;\n\tzoomToFit(entityIds?: EntityId[], padding?: number): void;\n\n\t// Viewport\n\tsetViewport(width: number, height: number, dpr?: number): void;\n\n\t// Pointer input\n\thandlePointerDown(\n\t\tscreenX: number,\n\t\tscreenY: number,\n\t\tbutton: number,\n\t\tmodifiers: Modifiers,\n\t): PointerDirective;\n\thandlePointerMove(screenX: number, screenY: number, modifiers: Modifiers): PointerDirective;\n\thandlePointerUp(): PointerDirective;\n\n\t// Selection & Hover\n\tgetSelectedEntities(): EntityId[];\n\tgetHoveredEntity(): EntityId | null;\n\n\t// Navigation\n\tenterContainer(entity: EntityId): void;\n\texitContainer(): void;\n\tgetActiveContainer(): EntityId | null;\n\tgetNavigationDepth(): number;\n\n\t// Commands + Undo/Redo\n\texecute(command: Command): void;\n\tbeginCommandGroup(): void;\n\tendCommandGroup(): void;\n\tundo(): boolean;\n\tredo(): boolean;\n\tcanUndo(): boolean;\n\tcanRedo(): boolean;\n\n\t// Frame\n\tmarkDirty(): void;\n\ttick(): void;\n\tflushIfDirty(): boolean;\n\n\t// Output\n\tgetVisibleEntities(): VisibleEntity[];\n\tgetFrameChanges(): FrameChanges;\n\n\t// Spatial index (exposed for systems)\n\tgetSpatialIndex(): SpatialIndex;\n\n\t// Snap guides\n\tgetSnapGuides(): SnapGuide[];\n\tgetEqualSpacing(): EqualSpacingIndicator[];\n\tsetSnapEnabled(on: boolean): void;\n\tsetSnapThreshold(worldPx: number): void;\n\n\t// Performance profiling\n\treadonly profiler: Profiler;\n\n\t// Events\n\tonFrame(handler: () => void): Unsubscribe;\n\n\t// Lifecycle\n\tdestroy(): void;\n}\n\nexport function createLayoutEngine(config?: LayoutEngineConfig): LayoutEngine {\n\tconst world = createWorld();\n\tconst scheduler = new SystemScheduler();\n\tconst spatialIndex = new SpatialIndex();\n\tconst profiler = new Profiler();\n\tscheduler.profiler = profiler;\n\n\t// Fix #11: Store spatial index as a proper resource\n\tworld.setResource(SpatialIndexResource, { instance: spatialIndex });\n\n\tconst commandBuffer = new CommandBuffer();\n\n\t// Apply config\n\tif (config?.zoom) {\n\t\tworld.setResource(ZoomConfigResource, config.zoom);\n\t}\n\tif (config?.breakpoints) {\n\t\tworld.setResource(BreakpointConfigResource, config.breakpoints);\n\t}\n\n\t// Register built-in systems\n\tscheduler.register(transformPropagateSystem);\n\tscheduler.register(navigationFilterSystem);\n\tscheduler.register(cullSystem);\n\tscheduler.register(breakpointSystem);\n\tscheduler.register(sortSystem);\n\n\t// P3: Wire spatial index reactively via observer instead of per-frame system scan\n\tworld.onComponentChanged(WorldBounds, (entityId, _prev, wb) => {\n\t\tif (wb) {\n\t\t\tspatialIndex.upsert(entityId, worldBoundsToAABB(wb));\n\t\t}\n\t});\n\n\t// Initialize navigation — mark root entities as Active on first tick\n\tworld.setResource(NavigationStackResource, { changed: true });\n\n\t// State\n\tlet inputState: InputState = { mode: 'idle' };\n\tlet hoveredEntity: EntityId | null = null;\n\tlet snapEnabled = true;\n\tlet snapThreshold = 5; // world units\n\tlet currentSnap: SnapResult = { snapDx: 0, snapDy: 0, guides: [], spacings: [] };\n\tlet dirty = false;\n\tlet cameraChangedThisTick = false;\n\tlet selectionChangedThisTick = false; // Fix #2: proper selection tracking\n\tlet prevVisible = new Set<EntityId>();\n\tlet currentVisible: VisibleEntity[] = [];\n\tlet frameChanges: FrameChanges = {\n\t\tpositionsChanged: [],\n\t\tbreakpointsChanged: [],\n\t\tentered: [],\n\t\texited: [],\n\t\tcameraChanged: false,\n\t\tnavigationChanged: false,\n\t\tselectionChanged: false,\n\t};\n\n\tfunction markDirtyInternal() {\n\t\tdirty = true;\n\t}\n\n\tfunction hitTest(screenX: number, screenY: number): EntityId | null {\n\t\tconst camera = world.getResource(CameraResource);\n\t\tconst worldPos = screenToWorld(screenX, screenY, camera);\n\t\tconst tolerance = 2 / camera.zoom;\n\t\tconst candidates = spatialIndex.searchPoint(worldPos.x, worldPos.y, tolerance);\n\n\t\t// Filter to Active entities, sort by z-index (highest first)\n\t\tconst active = candidates\n\t\t\t.filter((c) => world.hasTag(c.entityId, Active))\n\t\t\t.sort((a, b) => {\n\t\t\t\tconst zA = world.getComponent(a.entityId, ZIndex)?.value ?? 0;\n\t\t\t\tconst zB = world.getComponent(b.entityId, ZIndex)?.value ?? 0;\n\t\t\t\treturn zB - zA;\n\t\t\t});\n\n\t\tfor (const candidate of active) {\n\t\t\tconst wb = world.getComponent(candidate.entityId, WorldBounds);\n\t\t\tif (wb && pointInAABB(worldPos.x, worldPos.y, worldBoundsToAABB(wb))) {\n\t\t\t\treturn candidate.entityId;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\tfunction hitTestResizeHandle(\n\t\tscreenX: number,\n\t\tscreenY: number,\n\t): { entityId: EntityId; handle: ResizeHandlePos } | null {\n\t\tconst selected = engine.getSelectedEntities();\n\t\tif (selected.length !== 1) return null;\n\n\t\tconst entity = selected[0];\n\t\tconst wb = world.getComponent(entity, WorldBounds);\n\t\tif (!wb || !world.hasTag(entity, Resizable)) return null;\n\n\t\tconst camera = world.getResource(CameraResource);\n\t\tconst worldPos = screenToWorld(screenX, screenY, camera);\n\t\tconst handleSize = 8 / camera.zoom;\n\n\t\tconst x = wb.worldX;\n\t\tconst y = wb.worldY;\n\t\tconst w = wb.worldWidth;\n\t\tconst h = wb.worldHeight;\n\n\t\tconst handles: { pos: ResizeHandlePos; cx: number; cy: number }[] = [\n\t\t\t{ pos: 'nw', cx: x, cy: y },\n\t\t\t{ pos: 'n', cx: x + w / 2, cy: y },\n\t\t\t{ pos: 'ne', cx: x + w, cy: y },\n\t\t\t{ pos: 'e', cx: x + w, cy: y + h / 2 },\n\t\t\t{ pos: 'se', cx: x + w, cy: y + h },\n\t\t\t{ pos: 's', cx: x + w / 2, cy: y + h },\n\t\t\t{ pos: 'sw', cx: x, cy: y + h },\n\t\t\t{ pos: 'w', cx: x, cy: y + h / 2 },\n\t\t];\n\n\t\tfor (const handle of handles) {\n\t\t\tif (\n\t\t\t\tMath.abs(worldPos.x - handle.cx) <= handleSize &&\n\t\t\t\tMath.abs(worldPos.y - handle.cy) <= handleSize\n\t\t\t) {\n\t\t\t\treturn { entityId: entity, handle: handle.pos };\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t}\n\n\t// Fix #2: track selection changes explicitly\n\tfunction selectEntity(entity: EntityId, additive: boolean) {\n\t\tif (!world.hasTag(entity, Selectable)) return;\n\n\t\tif (additive) {\n\t\t\tif (world.hasTag(entity, Selected)) {\n\t\t\t\tworld.removeTag(entity, Selected);\n\t\t\t} else {\n\t\t\t\tworld.addTag(entity, Selected);\n\t\t\t}\n\t\t} else {\n\t\t\tfor (const e of world.queryTagged(Selected)) {\n\t\t\t\tif (e !== entity) world.removeTag(e, Selected);\n\t\t\t}\n\t\t\tworld.addTag(entity, Selected);\n\t\t}\n\t\tselectionChangedThisTick = true;\n\t}\n\n\tfunction clearSelection() {\n\t\tconst selected = world.queryTagged(Selected);\n\t\tif (selected.length > 0) {\n\t\t\tfor (const e of selected) {\n\t\t\t\tworld.removeTag(e, Selected);\n\t\t\t}\n\t\t\tselectionChangedThisTick = true;\n\t\t}\n\t}\n\n\tconst engine: LayoutEngine = {\n\t\tworld,\n\n\t\t// === Entity CRUD ===\n\n\t\tcreateEntity(inits?: ComponentInit[]): EntityId {\n\t\t\tconst entity = world.createEntity();\n\t\t\tif (inits) {\n\t\t\t\tfor (const init of inits) {\n\t\t\t\t\tconst type = init[0];\n\t\t\t\t\tif (type.__kind === 'tag') {\n\t\t\t\t\t\tworld.addTag(entity, type as TagType);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tworld.addComponent(entity, type as ComponentType, init[1] ?? {});\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tmarkDirtyInternal();\n\t\t\treturn entity;\n\t\t},\n\n\t\taddWidget(opts: AddWidgetOptions): EntityId {\n\t\t\tconst inits: ComponentInit[] = [\n\t\t\t\t[\n\t\t\t\t\tTransform2D,\n\t\t\t\t\t{\n\t\t\t\t\t\tx: opts.position.x,\n\t\t\t\t\t\ty: opts.position.y,\n\t\t\t\t\t\twidth: opts.size.width,\n\t\t\t\t\t\theight: opts.size.height,\n\t\t\t\t\t\trotation: opts.rotation ?? 0,\n\t\t\t\t\t},\n\t\t\t\t],\n\t\t\t\t[Widget, { surface: opts.surface ?? 'dom', type: opts.type }],\n\t\t\t];\n\t\t\tif (opts.data !== undefined) {\n\t\t\t\tinits.push([WidgetData, { data: opts.data }]);\n\t\t\t}\n\t\t\tinits.push([ZIndex, { value: opts.zIndex ?? 0 }]);\n\t\t\tif (opts.selectable !== false) inits.push([Selectable]);\n\t\t\tif (opts.draggable !== false) inits.push([Draggable]);\n\t\t\tif (opts.resizable !== false) inits.push([Resizable]);\n\t\t\tif (opts.parent !== undefined) {\n\t\t\t\tinits.push([Parent, { id: opts.parent }]);\n\t\t\t}\n\t\t\treturn engine.createEntity(inits);\n\t\t},\n\n\t\tdestroyEntity(id: EntityId) {\n\t\t\tspatialIndex.remove(id);\n\t\t\tworld.destroyEntity(id);\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\tget<T>(entity: EntityId, type: ComponentType<T>): T | undefined {\n\t\t\treturn world.getComponent(entity, type);\n\t\t},\n\n\t\tset<T>(entity: EntityId, type: ComponentType<T>, data: Partial<T>) {\n\t\t\tworld.setComponent(entity, type, data);\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\thas(entity: EntityId, type: ComponentType | TagType): boolean {\n\t\t\tif (type.__kind === 'tag') return world.hasTag(entity, type as TagType);\n\t\t\treturn world.hasComponent(entity, type as ComponentType);\n\t\t},\n\n\t\t// === Extensions ===\n\n\t\tregisterSystem(system: SystemDef) {\n\t\t\tscheduler.register(system);\n\t\t},\n\n\t\tremoveSystem(name: string) {\n\t\t\tscheduler.remove(name);\n\t\t},\n\n\t\t// === Camera ===\n\n\t\tgetCamera() {\n\t\t\treturn world.getResource(CameraResource);\n\t\t},\n\n\t\tpanBy(dx: number, dy: number) {\n\t\t\tconst camera = world.getResource(CameraResource);\n\t\t\tcamera.x -= dx / camera.zoom;\n\t\t\tcamera.y -= dy / camera.zoom;\n\t\t\tcameraChangedThisTick = true;\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\tpanTo(worldX: number, worldY: number) {\n\t\t\tconst camera = world.getResource(CameraResource);\n\t\t\tconst viewport = world.getResource(ViewportResource);\n\t\t\tcamera.x = worldX - viewport.width / (2 * camera.zoom);\n\t\t\tcamera.y = worldY - viewport.height / (2 * camera.zoom);\n\t\t\tcameraChangedThisTick = true;\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\tzoomAtPoint(screenX: number, screenY: number, delta: number) {\n\t\t\tconst camera = world.getResource(CameraResource);\n\t\t\tconst zoomConfig = world.getResource(ZoomConfigResource);\n\n\t\t\tconst worldBefore = screenToWorld(screenX, screenY, camera);\n\t\t\tconst newZoom = clamp(camera.zoom * (1 + delta), zoomConfig.min, zoomConfig.max);\n\t\t\tcamera.zoom = newZoom;\n\t\t\tcamera.x = worldBefore.x - screenX / newZoom;\n\t\t\tcamera.y = worldBefore.y - screenY / newZoom;\n\t\t\tcameraChangedThisTick = true;\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\tzoomTo(zoom: number) {\n\t\t\tconst camera = world.getResource(CameraResource);\n\t\t\tconst zoomConfig = world.getResource(ZoomConfigResource);\n\t\t\tconst viewport = world.getResource(ViewportResource);\n\t\t\tconst centerWorldX = camera.x + viewport.width / (2 * camera.zoom);\n\t\t\tconst centerWorldY = camera.y + viewport.height / (2 * camera.zoom);\n\t\t\tcamera.zoom = clamp(zoom, zoomConfig.min, zoomConfig.max);\n\t\t\tcamera.x = centerWorldX - viewport.width / (2 * camera.zoom);\n\t\t\tcamera.y = centerWorldY - viewport.height / (2 * camera.zoom);\n\t\t\tcameraChangedThisTick = true;\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\tzoomToFit(entityIds?: EntityId[], padding = 50) {\n\t\t\tconst viewport = world.getResource(ViewportResource);\n\t\t\tif (viewport.width === 0) return;\n\n\t\t\tconst entities = entityIds ?? world.queryTagged(Active);\n\t\t\tif (entities.length === 0) return;\n\n\t\t\tlet minX = Number.POSITIVE_INFINITY,\n\t\t\t\tminY = Number.POSITIVE_INFINITY,\n\t\t\t\tmaxX = Number.NEGATIVE_INFINITY,\n\t\t\t\tmaxY = Number.NEGATIVE_INFINITY;\n\t\t\tfor (const e of entities) {\n\t\t\t\tconst wb = world.getComponent(e, WorldBounds);\n\t\t\t\tif (!wb) continue;\n\t\t\t\tminX = Math.min(minX, wb.worldX);\n\t\t\t\tminY = Math.min(minY, wb.worldY);\n\t\t\t\tmaxX = Math.max(maxX, wb.worldX + wb.worldWidth);\n\t\t\t\tmaxY = Math.max(maxY, wb.worldY + wb.worldHeight);\n\t\t\t}\n\t\t\tif (!isFinite(minX)) return;\n\n\t\t\tconst contentWidth = maxX - minX + padding * 2;\n\t\t\tconst contentHeight = maxY - minY + padding * 2;\n\t\t\tconst zoomConfig = world.getResource(ZoomConfigResource);\n\t\t\tconst zoom = clamp(\n\t\t\t\tMath.min(viewport.width / contentWidth, viewport.height / contentHeight),\n\t\t\t\tzoomConfig.min,\n\t\t\t\tzoomConfig.max,\n\t\t\t);\n\n\t\t\tconst camera = world.getResource(CameraResource);\n\t\t\tcamera.zoom = zoom;\n\t\t\tcamera.x = minX - padding - (viewport.width / zoom - contentWidth) / 2;\n\t\t\tcamera.y = minY - padding - (viewport.height / zoom - contentHeight) / 2;\n\t\t\tcameraChangedThisTick = true;\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\t// === Viewport ===\n\n\t\tsetViewport(width: number, height: number, dpr?: number) {\n\t\t\tworld.setResource(ViewportResource, { width, height, dpr: dpr ?? 1 });\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\t// === Commands + Undo/Redo ===\n\n\t\texecute(command: Command) {\n\t\t\tcommandBuffer.execute(command, world);\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\tbeginCommandGroup() {\n\t\t\tcommandBuffer.beginGroup();\n\t\t},\n\n\t\tendCommandGroup() {\n\t\t\tcommandBuffer.endGroup();\n\t\t},\n\n\t\tundo(): boolean {\n\t\t\tconst did = commandBuffer.undo(world);\n\t\t\tif (did) markDirtyInternal();\n\t\t\treturn did;\n\t\t},\n\n\t\tredo(): boolean {\n\t\t\tconst did = commandBuffer.redo(world);\n\t\t\tif (did) markDirtyInternal();\n\t\t\treturn did;\n\t\t},\n\n\t\tcanUndo(): boolean {\n\t\t\treturn commandBuffer.canUndo();\n\t\t},\n\n\t\tcanRedo(): boolean {\n\t\t\treturn commandBuffer.canRedo();\n\t\t},\n\n\t\t// === Pointer Input ===\n\n\t\thandlePointerDown(screenX, screenY, _button, modifiers): PointerDirective {\n\t\t\t// Check resize handles first\n\t\t\tconst handleHit = hitTestResizeHandle(screenX, screenY);\n\t\t\tif (handleHit) {\n\t\t\t\tconst t = world.getComponent(handleHit.entityId, Transform2D)!;\n\t\t\t\tcommandBuffer.beginGroup(); // undo group for entire resize\n\t\t\t\tinputState = {\n\t\t\t\t\tmode: 'resizing',\n\t\t\t\t\tentityId: handleHit.entityId,\n\t\t\t\t\thandle: handleHit.handle,\n\t\t\t\t\tstartX: screenX,\n\t\t\t\t\tstartY: screenY,\n\t\t\t\t\tstartBounds: { x: t.x, y: t.y, width: t.width, height: t.height },\n\t\t\t\t};\n\t\t\t\tmarkDirtyInternal();\n\t\t\t\treturn { action: 'capture-resize', handle: handleHit.handle };\n\t\t\t}\n\n\t\t\t// Hit test entities\n\t\t\tconst hitEntity = hitTest(screenX, screenY);\n\n\t\t\tif (hitEntity !== null) {\n\t\t\t\tselectEntity(hitEntity, modifiers.shift);\n\t\t\t\tif (world.hasTag(hitEntity, Draggable)) {\n\t\t\t\t\tinputState = { mode: 'tracking', entityId: hitEntity, startX: screenX, startY: screenY };\n\t\t\t\t}\n\t\t\t\tmarkDirtyInternal();\n\t\t\t\treturn { action: 'passthrough-track-drag' };\n\t\t\t}\n\n\t\t\t// Empty canvas\n\t\t\tclearSelection();\n\t\t\tinputState = { mode: 'marquee', startX: screenX, startY: screenY };\n\t\t\tmarkDirtyInternal();\n\t\t\treturn { action: 'capture-marquee' };\n\t\t},\n\n\t\thandlePointerMove(screenX, screenY, _modifiers): PointerDirective {\n\t\t\tif (inputState.mode === 'tracking') {\n\t\t\t\tconst dx = screenX - inputState.startX;\n\t\t\t\tconst dy = screenY - inputState.startY;\n\t\t\t\tif (Math.abs(dx) > DEAD_ZONE_PX || Math.abs(dy) > DEAD_ZONE_PX) {\n\t\t\t\t\t// Fix #5: Save original z-indices, temporarily bring to top\n\t\t\t\t\tconst originalZIndices = new Map<EntityId, number>();\n\t\t\t\t\tlet maxZ = 0;\n\t\t\t\t\tfor (const e of world.queryTagged(Active)) {\n\t\t\t\t\t\tconst z = world.getComponent(e, ZIndex);\n\t\t\t\t\t\tif (z && z.value > maxZ) maxZ = z.value;\n\t\t\t\t\t}\n\t\t\t\t\tfor (const e of world.queryTagged(Selected)) {\n\t\t\t\t\t\tconst z = world.getComponent(e, ZIndex);\n\t\t\t\t\t\toriginalZIndices.set(e, z?.value ?? 0);\n\t\t\t\t\t\tworld.setComponent(e, ZIndex, { value: maxZ + 1 });\n\t\t\t\t\t}\n\n\t\t\t\t\t// Capture start positions for all selected entities\n\t\t\t\t\tconst startPositions = new Map<EntityId, { x: number; y: number }>();\n\t\t\t\t\tfor (const e of world.queryTagged(Selected)) {\n\t\t\t\t\t\tconst t = world.getComponent(e, Transform2D);\n\t\t\t\t\t\tif (t) startPositions.set(e, { x: t.x, y: t.y });\n\t\t\t\t\t}\n\n\t\t\t\t\t// Start undo group for the entire drag operation\n\t\t\t\t\tcommandBuffer.beginGroup();\n\n\t\t\t\t\tinputState = {\n\t\t\t\t\t\tmode: 'dragging',\n\t\t\t\t\t\tentityId: inputState.entityId,\n\t\t\t\t\t\tstartScreenX: screenX,\n\t\t\t\t\t\tstartScreenY: screenY,\n\t\t\t\t\t\tstartPositions,\n\t\t\t\t\t\toriginalZIndices,\n\t\t\t\t\t};\n\t\t\t\t\tmarkDirtyInternal();\n\t\t\t\t\treturn { action: 'capture-drag' };\n\t\t\t\t}\n\t\t\t\treturn { action: 'passthrough' };\n\t\t\t}\n\n\t\t\tif (inputState.mode === 'dragging') {\n\t\t\t\tconst camera = world.getResource(CameraResource);\n\t\t\t\tconst totalDx = (screenX - inputState.startScreenX) / camera.zoom;\n\t\t\t\tconst totalDy = (screenY - inputState.startScreenY) / camera.zoom;\n\n\t\t\t\t// Compute snap guides against visible non-dragged entities\n\t\t\t\tif (snapEnabled && inputState.startPositions.size > 0) {\n\t\t\t\t\t// Build dragged bounds (use first entity as reference for snap)\n\t\t\t\t\tconst draggedIds = new Set(inputState.startPositions.keys());\n\t\t\t\t\tconst firstId = inputState.startPositions.keys().next().value!;\n\t\t\t\t\tconst firstStart = inputState.startPositions.get(firstId)!;\n\t\t\t\t\tconst firstT = world.getComponent(firstId, Transform2D);\n\t\t\t\t\tif (firstT) {\n\t\t\t\t\t\tconst draggedBounds = {\n\t\t\t\t\t\t\tx: firstStart.x + totalDx,\n\t\t\t\t\t\t\ty: firstStart.y + totalDy,\n\t\t\t\t\t\t\twidth: firstT.width,\n\t\t\t\t\t\t\theight: firstT.height,\n\t\t\t\t\t\t};\n\n\t\t\t\t\t\t// Collect reference bounds from visible entities\n\t\t\t\t\t\tconst refs = [];\n\t\t\t\t\t\tfor (const entity of world.queryTagged(Active)) {\n\t\t\t\t\t\t\tif (draggedIds.has(entity)) continue;\n\t\t\t\t\t\t\tconst wb = world.getComponent(entity, WorldBounds);\n\t\t\t\t\t\t\tif (wb) {\n\t\t\t\t\t\t\t\trefs.push({\n\t\t\t\t\t\t\t\t\tx: wb.worldX,\n\t\t\t\t\t\t\t\t\ty: wb.worldY,\n\t\t\t\t\t\t\t\t\twidth: wb.worldWidth,\n\t\t\t\t\t\t\t\t\theight: wb.worldHeight,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\tcurrentSnap = computeSnapGuides(draggedBounds, refs, snapThreshold / camera.zoom);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tcurrentSnap = { snapDx: 0, snapDy: 0, guides: [], spacings: [] };\n\t\t\t\t}\n\n\t\t\t\t// Apply snap-corrected positions\n\t\t\t\tconst finalDx = totalDx + currentSnap.snapDx;\n\t\t\t\tconst finalDy = totalDy + currentSnap.snapDy;\n\t\t\t\tfor (const [e, start] of inputState.startPositions) {\n\t\t\t\t\tworld.setComponent(e, Transform2D, {\n\t\t\t\t\t\tx: start.x + finalDx,\n\t\t\t\t\t\ty: start.y + finalDy,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\tmarkDirtyInternal();\n\t\t\t\treturn { action: 'capture-drag' };\n\t\t\t}\n\n\t\t\tif (inputState.mode === 'resizing') {\n\t\t\t\tconst camera = world.getResource(CameraResource);\n\t\t\t\tconst dx = (screenX - inputState.startX) / camera.zoom;\n\t\t\t\tconst dy = (screenY - inputState.startY) / camera.zoom;\n\t\t\t\tconst { x, y, width: w, height: h } = inputState.startBounds;\n\t\t\t\tconst handle = inputState.handle;\n\t\t\t\tconst MIN_SIZE = 20;\n\n\t\t\t\tlet newX = x,\n\t\t\t\t\tnewY = y,\n\t\t\t\t\tnewW = w,\n\t\t\t\t\tnewH = h;\n\n\t\t\t\tif (handle.includes('e')) {\n\t\t\t\t\tnewW = Math.max(MIN_SIZE, w + dx);\n\t\t\t\t}\n\t\t\t\tif (handle.includes('w')) {\n\t\t\t\t\tnewX = x + dx;\n\t\t\t\t\tnewW = Math.max(MIN_SIZE, w - dx);\n\t\t\t\t}\n\t\t\t\tif (handle.includes('s')) {\n\t\t\t\t\tnewH = Math.max(MIN_SIZE, h + dy);\n\t\t\t\t}\n\t\t\t\tif (handle.includes('n')) {\n\t\t\t\t\tnewY = y + dy;\n\t\t\t\t\tnewH = Math.max(MIN_SIZE, h - dy);\n\t\t\t\t}\n\n\t\t\t\tworld.setComponent(inputState.entityId, Transform2D, {\n\t\t\t\t\tx: newX,\n\t\t\t\t\ty: newY,\n\t\t\t\t\twidth: newW,\n\t\t\t\t\theight: newH,\n\t\t\t\t});\n\t\t\t\tmarkDirtyInternal();\n\t\t\t\treturn { action: 'capture-resize', handle: inputState.handle };\n\t\t\t}\n\n\t\t\tif (inputState.mode === 'marquee') {\n\t\t\t\t// TODO: marquee selection in Phase 7\n\t\t\t\treturn { action: 'capture-marquee' };\n\t\t\t}\n\n\t\t\t// Hover tracking in idle mode\n\t\t\tif (inputState.mode === 'idle') {\n\t\t\t\tconst hit = hitTest(screenX, screenY);\n\t\t\t\tif (hit !== hoveredEntity) {\n\t\t\t\t\thoveredEntity = hit;\n\t\t\t\t\tmarkDirtyInternal();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\treturn { action: 'passthrough' };\n\t\t},\n\n\t\thandlePointerUp(): PointerDirective {\n\t\t\tconst prevState = inputState;\n\n\t\t\tif (prevState.mode === 'dragging') {\n\t\t\t\t// Fix #5: Restore original z-indices on drag end\n\t\t\t\tfor (const [entity, originalZ] of prevState.originalZIndices) {\n\t\t\t\t\tworld.setComponent(entity, ZIndex, { value: originalZ });\n\t\t\t\t}\n\t\t\t\t// Compute total delta from any moved entity\n\t\t\t\tconst entityIds = [...prevState.startPositions.keys()];\n\t\t\t\tif (entityIds.length > 0) {\n\t\t\t\t\tconst firstId = entityIds[0];\n\t\t\t\t\tconst start = prevState.startPositions.get(firstId)!;\n\t\t\t\t\tconst current = world.getComponent(firstId, Transform2D);\n\t\t\t\t\tif (current) {\n\t\t\t\t\t\tconst totalDx = current.x - start.x;\n\t\t\t\t\t\tconst totalDy = current.y - start.y;\n\t\t\t\t\t\tif (totalDx !== 0 || totalDy !== 0) {\n\t\t\t\t\t\t\t// Revert all to start positions, then commit as single command\n\t\t\t\t\t\t\tfor (const [e, s] of prevState.startPositions) {\n\t\t\t\t\t\t\t\tworld.setComponent(e, Transform2D, { x: s.x, y: s.y });\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tcommandBuffer.execute(\n\t\t\t\t\t\t\t\tnew MoveCommand(entityIds, totalDx, totalDy, Transform2D),\n\t\t\t\t\t\t\t\tworld,\n\t\t\t\t\t\t\t);\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tcommandBuffer.endGroup();\n\t\t\t\tcurrentSnap = { snapDx: 0, snapDy: 0, guides: [], spacings: [] };\n\t\t\t}\n\n\t\t\tif (prevState.mode === 'resizing') {\n\t\t\t\t// Capture final bounds before reverting\n\t\t\t\tconst t = world.getComponent(prevState.entityId, Transform2D);\n\t\t\t\tif (t) {\n\t\t\t\t\tconst finalBounds = { x: t.x, y: t.y, width: t.width, height: t.height };\n\t\t\t\t\tconst sb = prevState.startBounds;\n\t\t\t\t\t// Revert to start bounds so the command's execute() applies cleanly\n\t\t\t\t\tworld.setComponent(prevState.entityId, Transform2D, sb);\n\t\t\t\t\tcommandBuffer.execute(\n\t\t\t\t\t\tnew ResizeCommand(prevState.entityId, sb, finalBounds, Transform2D),\n\t\t\t\t\t\tworld,\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tcommandBuffer.endGroup();\n\t\t\t}\n\n\t\t\t// Fix #12: TODO — emit a 'widget-clicked' event when prevState.mode === 'tracking'\n\n\t\t\tinputState = { mode: 'idle' };\n\n\t\t\tif (prevState.mode === 'dragging' || prevState.mode === 'resizing') {\n\t\t\t\tmarkDirtyInternal();\n\t\t\t}\n\n\t\t\treturn { action: 'passthrough' };\n\t\t},\n\n\t\t// === Selection ===\n\n\t\tgetSelectedEntities(): EntityId[] {\n\t\t\treturn world.queryTagged(Selected);\n\t\t},\n\n\t\tgetHoveredEntity(): EntityId | null {\n\t\t\treturn hoveredEntity;\n\t\t},\n\n\t\t// === Snap Guides ===\n\n\t\tgetSnapGuides(): SnapGuide[] {\n\t\t\treturn currentSnap.guides;\n\t\t},\n\n\t\tgetEqualSpacing(): EqualSpacingIndicator[] {\n\t\t\treturn currentSnap.spacings;\n\t\t},\n\n\t\tsetSnapEnabled(on: boolean) {\n\t\t\tsnapEnabled = on;\n\t\t},\n\n\t\tsetSnapThreshold(worldPx: number) {\n\t\t\tsnapThreshold = worldPx;\n\t\t},\n\n\t\t// === Navigation ===\n\n\t\tenterContainer(entity: EntityId) {\n\t\t\tif (!world.hasComponent(entity, Container)) return;\n\t\t\tif (!world.hasComponent(entity, Children)) return;\n\n\t\t\tconst navStack = world.getResource(NavigationStackResource);\n\t\t\tconst camera = world.getResource(CameraResource);\n\n\t\t\tconst currentFrame = navStack.frames[navStack.frames.length - 1];\n\t\t\tcurrentFrame.camera = { x: camera.x, y: camera.y, zoom: camera.zoom };\n\n\t\t\tnavStack.frames.push({\n\t\t\t\tcontainerId: entity,\n\t\t\t\tcamera: { x: camera.x, y: camera.y, zoom: camera.zoom },\n\t\t\t});\n\t\t\tnavStack.changed = true;\n\n\t\t\tclearSelection();\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\texitContainer() {\n\t\t\tconst navStack = world.getResource(NavigationStackResource);\n\t\t\tif (navStack.frames.length <= 1) return;\n\n\t\t\tnavStack.frames.pop();\n\t\t\tnavStack.changed = true;\n\n\t\t\tconst parentFrame = navStack.frames[navStack.frames.length - 1];\n\t\t\tconst camera = world.getResource(CameraResource);\n\t\t\tcamera.x = parentFrame.camera.x;\n\t\t\tcamera.y = parentFrame.camera.y;\n\t\t\tcamera.zoom = parentFrame.camera.zoom;\n\n\t\t\tclearSelection();\n\t\t\tcameraChangedThisTick = true;\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\tgetActiveContainer(): EntityId | null {\n\t\t\tconst navStack = world.getResource(NavigationStackResource);\n\t\t\treturn navStack.frames[navStack.frames.length - 1].containerId;\n\t\t},\n\n\t\tgetNavigationDepth(): number {\n\t\t\treturn world.getResource(NavigationStackResource).frames.length - 1;\n\t\t},\n\n\t\t// === Frame ===\n\n\t\tmarkDirty() {\n\t\t\tmarkDirtyInternal();\n\t\t},\n\n\t\tprofiler,\n\n\t\ttick() {\n\t\t\tprofiler.beginFrame(world.currentTick);\n\n\t\t\t// Run all systems\n\t\t\tscheduler.execute(world);\n\n\t\t\t// Compute visible entities for renderers\n\t\t\tprofiler.beginVisibility();\n\t\t\tconst newVisible: VisibleEntity[] = [];\n\t\t\tconst newVisibleSet = new Set<EntityId>();\n\n\t\t\tfor (const entity of world.query(Widget, Visible)) {\n\t\t\t\tconst wb = world.getComponent(entity, WorldBounds);\n\t\t\t\tconst widget = world.getComponent(entity, Widget);\n\t\t\t\tconst bp = world.getComponent(entity, WidgetBreakpoint);\n\t\t\t\tconst zIdx = world.getComponent(entity, ZIndex);\n\t\t\t\tif (!wb || !widget) continue;\n\n\t\t\t\tnewVisibleSet.add(entity);\n\t\t\t\tnewVisible.push({\n\t\t\t\t\tentityId: entity,\n\t\t\t\t\tworldX: wb.worldX,\n\t\t\t\t\tworldY: wb.worldY,\n\t\t\t\t\tworldWidth: wb.worldWidth,\n\t\t\t\t\tworldHeight: wb.worldHeight,\n\t\t\t\t\tbreakpoint: bp?.current ?? 'normal',\n\t\t\t\t\tzIndex: zIdx?.value ?? 0,\n\t\t\t\t\tsurface: widget.surface,\n\t\t\t\t\twidgetType: widget.type,\n\t\t\t\t});\n\t\t\t}\n\n\t\t\t// Sort by z-index\n\t\t\tnewVisible.sort((a, b) => a.zIndex - b.zIndex);\n\t\t\tprofiler.endVisibility();\n\n\t\t\t// Compute frame changes\n\t\t\tconst entered: EntityId[] = [];\n\t\t\tconst exited: EntityId[] = [];\n\t\t\tfor (const entity of newVisibleSet) {\n\t\t\t\tif (!prevVisible.has(entity)) entered.push(entity);\n\t\t\t}\n\t\t\tfor (const entity of prevVisible) {\n\t\t\t\tif (!newVisibleSet.has(entity)) exited.push(entity);\n\t\t\t}\n\n\t\t\t// Fix #2: Use selectionChangedThisTick instead of wrong breakpoint check\n\t\t\tframeChanges = {\n\t\t\t\tpositionsChanged: world.queryChanged(WorldBounds),\n\t\t\t\tbreakpointsChanged: world.queryChanged(WidgetBreakpoint),\n\t\t\t\tentered,\n\t\t\t\texited,\n\t\t\t\tcameraChanged: cameraChangedThisTick,\n\t\t\t\tnavigationChanged: false,\n\t\t\t\tselectionChanged: selectionChangedThisTick,\n\t\t\t};\n\n\t\t\tcurrentVisible = newVisible;\n\t\t\tprevVisible = newVisibleSet;\n\t\t\tcameraChangedThisTick = false;\n\t\t\tselectionChangedThisTick = false;\n\n\t\t\tprofiler.endFrame(world.entityCount, newVisible.length);\n\n\t\t\t// Clear dirty sets and increment tick\n\t\t\tworld.clearDirty();\n\t\t\tworld.incrementTick();\n\t\t\tworld.emitFrame();\n\n\t\t\tdirty = false;\n\t\t},\n\n\t\tflushIfDirty(): boolean {\n\t\t\tif (!dirty) return false;\n\t\t\tengine.tick();\n\t\t\treturn true;\n\t\t},\n\n\t\t// === Output ===\n\n\t\tgetVisibleEntities(): VisibleEntity[] {\n\t\t\treturn currentVisible;\n\t\t},\n\n\t\tgetFrameChanges(): FrameChanges {\n\t\t\treturn frameChanges;\n\t\t},\n\n\t\t// Fix #11: Expose spatial index properly\n\t\tgetSpatialIndex(): SpatialIndex {\n\t\t\treturn spatialIndex;\n\t\t},\n\n\t\t// === Events ===\n\n\t\tonFrame(handler: () => void): Unsubscribe {\n\t\t\treturn world.onFrame(handler);\n\t\t},\n\n\t\t// === Lifecycle ===\n\n\t\tdestroy() {\n\t\t\tspatialIndex.clear();\n\t\t},\n\t};\n\n\treturn engine;\n}\n\n/** @deprecated Use LayoutEngine instead */\nexport type CanvasEngine = LayoutEngine;\n/** @deprecated Use LayoutEngineConfig instead */\nexport type CanvasEngineConfig = LayoutEngineConfig;\n/** @deprecated Use createLayoutEngine instead */\nexport const createCanvasEngine = createLayoutEngine;\n","import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from 'react';\nimport { Vector2 } from 'three';\nimport { Widget, WorldBounds } from '../components.js';\nimport type { EntityId } from '../ecs/types.js';\nimport type { LayoutEngine } from '../engine.js';\nimport { SelectionOverlaySlot } from './SelectionOverlaySlot.js';\nimport { WidgetProvider } from './WidgetProvider.js';\nimport { WidgetSlot } from './WidgetSlot.js';\nimport { ContainerRefProvider, EngineProvider, useWidgetResolver } from './context.js';\nimport { createWidgetRegistry } from './registry.js';\nimport type { WidgetDef } from './registry.js';\nimport { GridRenderer } from './webgl/GridRenderer.js';\nimport type { GridConfig } from './webgl/GridRenderer.js';\nimport { SelectionRenderer } from './webgl/SelectionRenderer.js';\nimport type { SelectionBounds, SelectionConfig } from './webgl/SelectionRenderer.js';\nimport { WebGLWidgetLayer } from './webgl/WebGLWidgetLayer.js';\n\ninterface InfiniteCanvasProps {\n\tengine: LayoutEngine;\n\t/** Widget definitions. When provided, WidgetProvider is created internally. */\n\twidgets?: WidgetDef[];\n\t/** Grid configuration. Pass `false` to disable the grid entirely. */\n\tgrid?: Partial<GridConfig> | false;\n\t/** Selection style configuration. */\n\tselection?: Partial<SelectionConfig>;\n\tclassName?: string;\n\tstyle?: React.CSSProperties;\n\tchildren?: React.ReactNode;\n}\n\nexport function InfiniteCanvas({\n\tengine,\n\twidgets,\n\tgrid,\n\tselection,\n\tclassName,\n\tstyle,\n\tchildren,\n}: InfiniteCanvasProps) {\n\tconst containerRef = useRef<HTMLDivElement>(null);\n\n\t// When widgets prop is provided, create an internal registry\n\tconst internalRegistry = useMemo(\n\t\t() => (widgets ? createWidgetRegistry(widgets) : null),\n\t\t[widgets],\n\t);\n\tconst webglCanvasRef = useRef<HTMLCanvasElement>(null);\n\tconst gridRendererRef = useRef<GridRenderer | null>(null);\n\tconst selectionRendererRef = useRef<SelectionRenderer | null>(null);\n\tconst cameraLayerRef = useRef<HTMLDivElement>(null);\n\tconst slotRefs = useRef(new Map<EntityId, HTMLDivElement>());\n\tconst [visibleEntities, setVisibleEntities] = useState<EntityId[]>([]);\n\n\t// Register slot ref for batch updater\n\tconst registerSlotRef = useCallback((entityId: EntityId, el: HTMLDivElement | null) => {\n\t\tif (el) {\n\t\t\tslotRefs.current.set(entityId, el);\n\t\t} else {\n\t\t\tslotRefs.current.delete(entityId);\n\t\t}\n\t}, []);\n\n\t// Initialize GridRenderer + set viewport size on mount/resize\n\tuseLayoutEffect(() => {\n\t\tconst container = containerRef.current;\n\t\tconst canvas = webglCanvasRef.current;\n\t\tif (!container || !canvas) return;\n\n\t\tconst gridEnabled = grid !== false;\n\t\tlet gridInst: GridRenderer | null = null;\n\t\tif (gridEnabled) {\n\t\t\tgridInst = new GridRenderer(canvas);\n\t\t\tgridRendererRef.current = gridInst;\n\t\t}\n\n\t\t// SelectionRenderer shares the WebGLRenderer from GridRenderer\n\t\tconst selInst = new SelectionRenderer();\n\t\tselectionRendererRef.current = selInst;\n\n\t\tconst updateSize = () => {\n\t\t\tconst rect = container.getBoundingClientRect();\n\t\t\tconst dpr = window.devicePixelRatio;\n\t\t\tengine.setViewport(rect.width, rect.height, dpr);\n\t\t\tcanvas.style.width = `${rect.width}px`;\n\t\t\tcanvas.style.height = `${rect.height}px`;\n\t\t\tif (gridInst) {\n\t\t\t\tgridInst.setSize(rect.width, rect.height, dpr);\n\t\t\t}\n\t\t\tselInst.setSize(new Vector2(rect.width * dpr, rect.height * dpr), dpr);\n\t\t};\n\n\t\tupdateSize();\n\t\tconst observer = new ResizeObserver(updateSize);\n\t\tobserver.observe(container);\n\t\treturn () => {\n\t\t\tobserver.disconnect();\n\t\t\tif (gridInst) {\n\t\t\t\tgridInst.dispose();\n\t\t\t\tgridRendererRef.current = null;\n\t\t\t}\n\t\t\tselInst.dispose();\n\t\t\tselectionRendererRef.current = null;\n\t\t};\n\t}, [engine, grid]);\n\n\t// Apply grid + selection config on every render\n\tuseEffect(() => {\n\t\tconst gridR = gridRendererRef.current;\n\t\tif (gridR && grid !== false) {\n\t\t\tconst isDark = document.documentElement.classList.contains('dark');\n\t\t\tgridR.setConfig({\n\t\t\t\tdotColor: isDark ? [1, 1, 1] : [0, 0, 0],\n\t\t\t\tdotAlpha: isDark ? 0.12 : 0.18,\n\t\t\t\t...grid,\n\t\t\t});\n\t\t}\n\t\tconst selR = selectionRendererRef.current;\n\t\tif (selR && selection) {\n\t\t\tselR.setConfig(selection);\n\t\t}\n\t\tengine.markDirty();\n\t});\n\n\t// Wheel handler — pan/zoom (gesture channel, always active)\n\tuseEffect(() => {\n\t\tconst container = containerRef.current;\n\t\tif (!container) return;\n\n\t\tconst onWheel = (e: WheelEvent) => {\n\t\t\te.preventDefault();\n\t\t\tif (e.ctrlKey || e.metaKey) {\n\t\t\t\t// Pinch zoom or ctrl+scroll\n\t\t\t\tconst rect = container.getBoundingClientRect();\n\t\t\t\tengine.zoomAtPoint(e.clientX - rect.left, e.clientY - rect.top, -e.deltaY * 0.01);\n\t\t\t} else {\n\t\t\t\t// Two-finger scroll pan\n\t\t\t\tengine.panBy(-e.deltaX, -e.deltaY);\n\t\t\t}\n\t\t};\n\n\t\tcontainer.addEventListener('wheel', onWheel, { passive: false });\n\t\treturn () => container.removeEventListener('wheel', onWheel);\n\t}, [engine]);\n\n\t// Touch gesture handler — iOS Freeform-style interactions\n\t// 1 finger on background → pan; 1 finger on entity → select/drag;\n\t// 2 fingers → pinch-to-zoom + pan; double-tap → zoom step / enter container\n\tuseEffect(() => {\n\t\tconst container = containerRef.current;\n\t\tif (!container) return;\n\n\t\ttype TouchGesture =\n\t\t\t| { type: 'idle' }\n\t\t\t| { type: 'pending-pan'; x: number; y: number; time: number }\n\t\t\t| { type: 'panning'; lastX: number; lastY: number }\n\t\t\t| { type: 'pending-entity'; x: number; y: number; time: number }\n\t\t\t| { type: 'entity-dragging' }\n\t\t\t| { type: 'pinching'; lastDist: number; lastCx: number; lastCy: number };\n\n\t\tlet gesture: TouchGesture = { type: 'idle' };\n\t\tlet lastTapTime = 0;\n\t\tlet lastTapX = 0;\n\t\tlet lastTapY = 0;\n\t\tconst DEAD_ZONE = 8;\n\t\tconst DOUBLE_TAP_MS = 300;\n\t\tconst DOUBLE_TAP_DIST = 30;\n\n\t\tfunction isOnWidget(target: EventTarget | null): boolean {\n\t\t\tlet el = target as HTMLElement | null;\n\t\t\twhile (el && el !== container) {\n\t\t\t\tif (el.hasAttribute('data-widget-slot')) return true;\n\t\t\t\tel = el.parentElement;\n\t\t\t}\n\t\t\treturn false;\n\t\t}\n\n\t\tfunction isInteractive(target: EventTarget | null): boolean {\n\t\t\tconst el = target as HTMLElement | null;\n\t\t\tif (!el) return false;\n\t\t\tconst tag = el.tagName;\n\t\t\treturn (\n\t\t\t\ttag === 'INPUT' ||\n\t\t\t\ttag === 'TEXTAREA' ||\n\t\t\t\ttag === 'BUTTON' ||\n\t\t\t\ttag === 'SELECT' ||\n\t\t\t\tel.isContentEditable ||\n\t\t\t\tel.closest('button') !== null\n\t\t\t);\n\t\t}\n\n\t\tfunction getRect() {\n\t\t\treturn container!.getBoundingClientRect();\n\t\t}\n\n\t\tfunction touchDist(t1: Touch, t2: Touch) {\n\t\t\tconst dx = t1.clientX - t2.clientX;\n\t\t\tconst dy = t1.clientY - t2.clientY;\n\t\t\treturn Math.sqrt(dx * dx + dy * dy);\n\t\t}\n\n\t\tfunction touchCenter(t1: Touch, t2: Touch, rect: DOMRect) {\n\t\t\treturn {\n\t\t\t\tx: (t1.clientX + t2.clientX) / 2 - rect.left,\n\t\t\t\ty: (t1.clientY + t2.clientY) / 2 - rect.top,\n\t\t\t};\n\t\t}\n\n\t\tfunction cancelEngineGesture() {\n\t\t\tif (gesture.type === 'pending-entity' || gesture.type === 'entity-dragging') {\n\t\t\t\tengine.handlePointerUp();\n\t\t\t}\n\t\t}\n\n\t\tconst noMods = { shift: false, ctrl: false, alt: false, meta: false };\n\n\t\tfunction onTouchStart(e: TouchEvent) {\n\t\t\tconst rect = getRect();\n\t\t\tconst touches = e.touches;\n\n\t\t\t// --- 2+ fingers → pinch (override everything) ---\n\t\t\tif (touches.length >= 2) {\n\t\t\t\te.preventDefault();\n\t\t\t\tcancelEngineGesture();\n\t\t\t\tconst dist = touchDist(touches[0], touches[1]);\n\t\t\t\tconst center = touchCenter(touches[0], touches[1], rect);\n\t\t\t\tgesture = { type: 'pinching', lastDist: dist, lastCx: center.x, lastCy: center.y };\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// --- 1 finger ---\n\t\t\tconst touch = touches[0];\n\t\t\tconst x = touch.clientX - rect.left;\n\t\t\tconst y = touch.clientY - rect.top;\n\n\t\t\t// Let interactive elements (buttons, inputs) handle their own touch\n\t\t\tif (isInteractive(e.target)) return;\n\n\t\t\te.preventDefault();\n\n\t\t\t// Double-tap detection\n\t\t\tconst now = Date.now();\n\t\t\tif (\n\t\t\t\tnow - lastTapTime < DOUBLE_TAP_MS &&\n\t\t\t\tMath.abs(x - lastTapX) < DOUBLE_TAP_DIST &&\n\t\t\t\tMath.abs(y - lastTapY) < DOUBLE_TAP_DIST\n\t\t\t) {\n\t\t\t\tlastTapTime = 0;\n\t\t\t\t// Hit test to check for entity\n\t\t\t\tconst directive = engine.handlePointerDown(x, y, 0, noMods);\n\t\t\t\tif (directive.action === 'passthrough-track-drag') {\n\t\t\t\t\t// Double-tap on entity → enter container\n\t\t\t\t\tconst selected = engine.getSelectedEntities();\n\t\t\t\t\tengine.handlePointerUp();\n\t\t\t\t\tif (selected.length === 1) {\n\t\t\t\t\t\tengine.enterContainer(selected[0]);\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// Double-tap on empty → zoom step\n\t\t\t\t\tengine.handlePointerUp();\n\t\t\t\t\tconst camera = engine.getCamera();\n\t\t\t\t\tconst target = camera.zoom < 0.9 ? 1 : camera.zoom < 1.8 ? 2 : 1;\n\t\t\t\t\tengine.zoomAtPoint(x, y, (target - camera.zoom) / camera.zoom);\n\t\t\t\t}\n\t\t\t\tengine.markDirty();\n\t\t\t\tgesture = { type: 'idle' };\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (isOnWidget(e.target)) {\n\t\t\t\t// Touch on entity → delegate to engine\n\t\t\t\tengine.handlePointerDown(x, y, 0, noMods);\n\t\t\t\tgesture = { type: 'pending-entity', x, y, time: now };\n\t\t\t} else {\n\t\t\t\t// Touch on empty space → prepare to pan (don't tell engine yet)\n\t\t\t\tgesture = { type: 'pending-pan', x, y, time: now };\n\t\t\t}\n\t\t}\n\n\t\tfunction onTouchMove(e: TouchEvent) {\n\t\t\te.preventDefault();\n\t\t\tconst rect = getRect();\n\t\t\tconst touches = e.touches;\n\n\t\t\t// --- Pinch ---\n\t\t\tif (gesture.type === 'pinching' && touches.length >= 2) {\n\t\t\t\tconst dist = touchDist(touches[0], touches[1]);\n\t\t\t\tconst center = touchCenter(touches[0], touches[1], rect);\n\t\t\t\tconst scale = dist / gesture.lastDist;\n\t\t\t\tengine.zoomAtPoint(center.x, center.y, scale - 1);\n\t\t\t\tengine.panBy(center.x - gesture.lastCx, center.y - gesture.lastCy);\n\t\t\t\tgesture.lastDist = dist;\n\t\t\t\tgesture.lastCx = center.x;\n\t\t\t\tgesture.lastCy = center.y;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Transition to pinch if second finger added\n\t\t\tif (touches.length >= 2) {\n\t\t\t\tcancelEngineGesture();\n\t\t\t\tconst dist = touchDist(touches[0], touches[1]);\n\t\t\t\tconst center = touchCenter(touches[0], touches[1], rect);\n\t\t\t\tgesture = { type: 'pinching', lastDist: dist, lastCx: center.x, lastCy: center.y };\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (touches.length < 1) return;\n\t\t\tconst touch = touches[0];\n\t\t\tconst x = touch.clientX - rect.left;\n\t\t\tconst y = touch.clientY - rect.top;\n\n\t\t\t// Pending pan → check dead zone\n\t\t\tif (gesture.type === 'pending-pan') {\n\t\t\t\tif (Math.abs(x - gesture.x) > DEAD_ZONE || Math.abs(y - gesture.y) > DEAD_ZONE) {\n\t\t\t\t\tgesture = { type: 'panning', lastX: x, lastY: y };\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Active panning\n\t\t\tif (gesture.type === 'panning') {\n\t\t\t\tengine.panBy(x - gesture.lastX, y - gesture.lastY);\n\t\t\t\tgesture.lastX = x;\n\t\t\t\tgesture.lastY = y;\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\t// Entity drag → delegate to engine\n\t\t\tif (gesture.type === 'pending-entity' || gesture.type === 'entity-dragging') {\n\t\t\t\tengine.handlePointerMove(x, y, noMods);\n\t\t\t\tif (gesture.type === 'pending-entity') {\n\t\t\t\t\tif (Math.abs(x - gesture.x) > DEAD_ZONE || Math.abs(y - gesture.y) > DEAD_ZONE) {\n\t\t\t\t\t\tgesture = { type: 'entity-dragging' };\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfunction onTouchEnd(e: TouchEvent) {\n\t\t\te.preventDefault();\n\t\t\tconst remaining = e.touches.length;\n\t\t\tconst rect = getRect();\n\n\t\t\t// Pinch → transition based on remaining fingers\n\t\t\tif (gesture.type === 'pinching') {\n\t\t\t\tif (remaining === 1) {\n\t\t\t\t\tconst t = e.touches[0];\n\t\t\t\t\tgesture = { type: 'panning', lastX: t.clientX - rect.left, lastY: t.clientY - rect.top };\n\t\t\t\t} else if (remaining === 0) {\n\t\t\t\t\tgesture = { type: 'idle' };\n\t\t\t\t}\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tif (remaining > 0) return;\n\n\t\t\t// Tap on empty space → deselect\n\t\t\tif (gesture.type === 'pending-pan') {\n\t\t\t\tengine.handlePointerDown(gesture.x, gesture.y, 0, noMods);\n\t\t\t\tengine.handlePointerUp();\n\t\t\t\tengine.markDirty();\n\t\t\t\tlastTapTime = Date.now();\n\t\t\t\tlastTapX = gesture.x;\n\t\t\t\tlastTapY = gesture.y;\n\t\t\t}\n\n\t\t\t// Tap on entity (no drag) → selection already happened\n\t\t\tif (gesture.type === 'pending-entity') {\n\t\t\t\tengine.handlePointerUp();\n\t\t\t\tengine.markDirty();\n\t\t\t\tlastTapTime = Date.now();\n\t\t\t\tlastTapX = gesture.x;\n\t\t\t\tlastTapY = gesture.y;\n\t\t\t}\n\n\t\t\t// Entity drag end\n\t\t\tif (gesture.type === 'entity-dragging') {\n\t\t\t\tengine.handlePointerUp();\n\t\t\t\tengine.markDirty();\n\t\t\t}\n\n\t\t\tgesture = { type: 'idle' };\n\t\t}\n\n\t\tcontainer.addEventListener('touchstart', onTouchStart, { passive: false });\n\t\tcontainer.addEventListener('touchmove', onTouchMove, { passive: false });\n\t\tcontainer.addEventListener('touchend', onTouchEnd, { passive: false });\n\t\tcontainer.addEventListener('touchcancel', onTouchEnd, { passive: false });\n\n\t\treturn () => {\n\t\t\tcontainer.removeEventListener('touchstart', onTouchStart);\n\t\t\tcontainer.removeEventListener('touchmove', onTouchMove);\n\t\t\tcontainer.removeEventListener('touchend', onTouchEnd);\n\t\t\tcontainer.removeEventListener('touchcancel', onTouchEnd);\n\t\t};\n\t}, [engine]);\n\n\t// Canvas background pointer — empty-space clicks\n\tconst onBackgroundPointerDown = useCallback(\n\t\t(e: React.PointerEvent) => {\n\t\t\tif (e.target !== e.currentTarget) return; // ignore bubbled events from widgets\n\t\t\tconst rect = containerRef.current?.getBoundingClientRect();\n\t\t\tif (!rect) return;\n\t\t\tengine.handlePointerDown(e.clientX - rect.left, e.clientY - rect.top, e.button, {\n\t\t\t\tshift: e.shiftKey,\n\t\t\t\tctrl: e.ctrlKey,\n\t\t\t\talt: e.altKey,\n\t\t\t\tmeta: e.metaKey,\n\t\t\t});\n\t\t},\n\t\t[engine],\n\t);\n\n\tconst onBackgroundPointerMove = useCallback(\n\t\t(e: React.PointerEvent) => {\n\t\t\tconst rect = containerRef.current?.getBoundingClientRect();\n\t\t\tif (!rect) return;\n\t\t\tengine.handlePointerMove(e.clientX - rect.left, e.clientY - rect.top, {\n\t\t\t\tshift: e.shiftKey,\n\t\t\t\tctrl: e.ctrlKey,\n\t\t\t\talt: e.altKey,\n\t\t\t\tmeta: e.metaKey,\n\t\t\t});\n\t\t},\n\t\t[engine],\n\t);\n\n\tconst onBackgroundPointerUp = useCallback(\n\t\t(_e: React.PointerEvent) => {\n\t\t\tengine.handlePointerUp();\n\t\t},\n\t\t[engine],\n\t);\n\n\t// rAF tick loop — flushes the engine when dirty, then applies updates.\n\t// This is THE render loop. Input handlers set engine dirty; this loop ticks.\n\tuseEffect(() => {\n\t\tlet rafId: number;\n\t\tlet running = true;\n\n\t\tfunction loop() {\n\t\t\tif (!running) return;\n\n\t\t\tconst didTick = engine.flushIfDirty();\n\t\t\tif (didTick) {\n\t\t\t\tconst camera = engine.getCamera();\n\t\t\t\tconst changes = engine.getFrameChanges();\n\n\t\t\t\t// 1. Update camera layer CSS transform (O(1) for pan/zoom)\n\t\t\t\tif (cameraLayerRef.current) {\n\t\t\t\t\tcameraLayerRef.current.style.transform = `scale(${camera.zoom}) translate(${-camera.x}px, ${-camera.y}px)`;\n\t\t\t\t}\n\n\t\t\t\t// 1b. Render WebGL dot grid + selection\n\t\t\t\tif (gridRendererRef.current) {\n\t\t\t\t\tgridRendererRef.current.render(camera.x, camera.y, camera.zoom);\n\t\t\t\t}\n\t\t\t\tif (selectionRendererRef.current && gridRendererRef.current) {\n\t\t\t\t\tconst selected = engine.getSelectedEntities();\n\t\t\t\t\tconst selBounds: SelectionBounds[] = [];\n\t\t\t\t\tfor (const id of selected) {\n\t\t\t\t\t\tconst wb = engine.get(id, WorldBounds);\n\t\t\t\t\t\tif (wb)\n\t\t\t\t\t\t\tselBounds.push({\n\t\t\t\t\t\t\t\tx: wb.worldX,\n\t\t\t\t\t\t\t\ty: wb.worldY,\n\t\t\t\t\t\t\t\twidth: wb.worldWidth,\n\t\t\t\t\t\t\t\theight: wb.worldHeight,\n\t\t\t\t\t\t\t});\n\t\t\t\t\t}\n\t\t\t\t\tconst hovId = engine.getHoveredEntity();\n\t\t\t\t\tlet hovBounds: SelectionBounds | null = null;\n\t\t\t\t\tif (hovId !== null) {\n\t\t\t\t\t\tconst wb = engine.get(hovId, WorldBounds);\n\t\t\t\t\t\tif (wb)\n\t\t\t\t\t\t\thovBounds = {\n\t\t\t\t\t\t\t\tx: wb.worldX,\n\t\t\t\t\t\t\t\ty: wb.worldY,\n\t\t\t\t\t\t\t\twidth: wb.worldWidth,\n\t\t\t\t\t\t\t\theight: wb.worldHeight,\n\t\t\t\t\t\t\t};\n\t\t\t\t\t}\n\t\t\t\t\tselectionRendererRef.current.render(\n\t\t\t\t\t\tgridRendererRef.current.getWebGLRenderer(),\n\t\t\t\t\t\tcamera.x,\n\t\t\t\t\t\tcamera.y,\n\t\t\t\t\t\tcamera.zoom,\n\t\t\t\t\t\tselBounds,\n\t\t\t\t\t\thovBounds,\n\t\t\t\t\t\tengine.getSnapGuides(),\n\t\t\t\t\t\tengine.getEqualSpacing(),\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// 2. Fix #1: Use WorldBounds (world-space) not Transform2D (local/parent-relative)\n\t\t\t\tfor (const entityId of changes.positionsChanged) {\n\t\t\t\t\tconst el = slotRefs.current.get(entityId);\n\t\t\t\t\tif (!el) continue;\n\t\t\t\t\tconst wb = engine.get(entityId, WorldBounds);\n\t\t\t\t\tif (!wb) continue;\n\t\t\t\t\tel.style.transform = `translate(${wb.worldX}px, ${wb.worldY}px)`;\n\t\t\t\t\tel.style.width = `${wb.worldWidth}px`;\n\t\t\t\t\tel.style.height = `${wb.worldHeight}px`;\n\t\t\t\t}\n\n\t\t\t\t// 3. Update visible entity list if entities entered/exited\n\t\t\t\tif (changes.entered.length > 0 || changes.exited.length > 0) {\n\t\t\t\t\tconst visible = engine.getVisibleEntities();\n\t\t\t\t\tsetVisibleEntities(visible.map((v) => v.entityId));\n\t\t\t\t}\n\t\t\t}\n\n\t\t\trafId = requestAnimationFrame(loop);\n\t\t}\n\n\t\t// Initial tick on mount\n\t\tengine.tick();\n\t\tconst visible = engine.getVisibleEntities();\n\t\tsetVisibleEntities(visible.map((v) => v.entityId));\n\n\t\t// Set initial camera transform + grid\n\t\tconst camera = engine.getCamera();\n\t\tif (cameraLayerRef.current) {\n\t\t\tcameraLayerRef.current.style.transform = `scale(${camera.zoom}) translate(${-camera.x}px, ${-camera.y}px)`;\n\t\t}\n\t\t// Initial WebGL grid render\n\t\tif (gridRendererRef.current) {\n\t\t\tgridRendererRef.current.render(camera.x, camera.y, camera.zoom);\n\t\t}\n\n\t\t// Set initial slot positions\n\t\tfor (const v of visible) {\n\t\t\tconst el = slotRefs.current.get(v.entityId);\n\t\t\tif (!el) continue;\n\t\t\tel.style.transform = `translate(${v.worldX}px, ${v.worldY}px)`;\n\t\t\tel.style.width = `${v.worldWidth}px`;\n\t\t\tel.style.height = `${v.worldHeight}px`;\n\t\t}\n\n\t\t// Start the loop\n\t\trafId = requestAnimationFrame(loop);\n\n\t\treturn () => {\n\t\t\trunning = false;\n\t\t\tcancelAnimationFrame(rafId);\n\t\t};\n\t}, [engine]);\n\n\t// Fix #4: useLayoutEffect to set initial positions BEFORE browser paint\n\t// Prevents one-frame flash at (0,0) when new widgets enter the viewport\n\tuseLayoutEffect(() => {\n\t\tfor (const entityId of visibleEntities) {\n\t\t\tconst el = slotRefs.current.get(entityId);\n\t\t\tif (!el) continue;\n\t\t\tconst wb = engine.get(entityId, WorldBounds);\n\t\t\tif (!wb) continue;\n\t\t\tel.style.transform = `translate(${wb.worldX}px, ${wb.worldY}px)`;\n\t\t\tel.style.width = `${wb.worldWidth}px`;\n\t\t\tel.style.height = `${wb.worldHeight}px`;\n\t\t}\n\t}, [visibleEntities, engine]);\n\n\t// Split visible entities by surface\n\tconst { domEntities, webglEntities } = useMemo(() => {\n\t\tconst dom: EntityId[] = [];\n\t\tconst webgl: EntityId[] = [];\n\t\tfor (const id of visibleEntities) {\n\t\t\tconst w = engine.get(id, Widget);\n\t\t\tif (w?.surface === 'webgl') {\n\t\t\t\twebgl.push(id);\n\t\t\t} else {\n\t\t\t\tdom.push(id);\n\t\t\t}\n\t\t}\n\t\treturn { domEntities: dom, webglEntities: webgl };\n\t}, [visibleEntities, engine]);\n\n\tconst canvasContent = (\n\t\t<div\n\t\t\tref={containerRef}\n\t\t\tclassName={`relative overflow-hidden ${className ?? ''}`}\n\t\t\tstyle={{\n\t\t\t\t...style,\n\t\t\t\ttouchAction: 'none',\n\t\t\t\tbackgroundColor: 'var(--canvas-bg, #fafafa)',\n\t\t\t}}\n\t\t>\n\t\t\t{/* WebGL layer — dot grid, selection overlays, connections */}\n\t\t\t<canvas ref={webglCanvasRef} className=\"absolute inset-0 pointer-events-none\" />\n\n\t\t\t{/* R3F layer — WebGL widgets (lazy, only when webgl entities exist) */}\n\t\t\t{webglEntities.length > 0 && <WebGLWidgetBridge engine={engine} entities={webglEntities} />}\n\n\t\t\t{/* Background — handles empty-space pointer events (deselect, marquee) */}\n\t\t\t<div\n\t\t\t\tclassName=\"absolute inset-0\"\n\t\t\t\tonPointerDown={onBackgroundPointerDown}\n\t\t\t\tonPointerMove={onBackgroundPointerMove}\n\t\t\t\tonPointerUp={onBackgroundPointerUp}\n\t\t\t/>\n\n\t\t\t{/* Camera transform layer — DOM widgets + selection overlays for WebGL widgets */}\n\t\t\t<div\n\t\t\t\tref={cameraLayerRef}\n\t\t\t\tclassName=\"absolute left-0 top-0 origin-top-left will-change-transform\"\n\t\t\t>\n\t\t\t\t{domEntities.map((entityId) => (\n\t\t\t\t\t<WidgetSlot key={entityId} entityId={entityId} slotRef={registerSlotRef} />\n\t\t\t\t))}\n\t\t\t\t{webglEntities.map((entityId) => (\n\t\t\t\t\t<SelectionOverlaySlot key={entityId} entityId={entityId} slotRef={registerSlotRef} />\n\t\t\t\t))}\n\t\t\t</div>\n\n\t\t\t{/* Children: toolbars, panels, etc. */}\n\t\t\t{children}\n\t\t</div>\n\t);\n\n\treturn (\n\t\t<EngineProvider value={engine}>\n\t\t\t<ContainerRefProvider value={containerRef}>\n\t\t\t\t{internalRegistry ? (\n\t\t\t\t\t<WidgetProvider registry={internalRegistry}>{canvasContent}</WidgetProvider>\n\t\t\t\t) : (\n\t\t\t\t\tcanvasContent\n\t\t\t\t)}\n\t\t\t</ContainerRefProvider>\n\t\t</EngineProvider>\n\t);\n}\n\n/** Bridge component — reads widget resolver from context and passes to WebGLWidgetLayer */\nfunction WebGLWidgetBridge({ engine, entities }: { engine: LayoutEngine; entities: EntityId[] }) {\n\tconst resolver = useWidgetResolver();\n\tconst resolve = useCallback(\n\t\t(entityId: EntityId) => {\n\t\t\tif (!resolver) return null;\n\t\t\tconst w = engine.get(entityId, Widget);\n\t\t\treturn resolver(entityId, w?.type ?? '');\n\t\t},\n\t\t[resolver, engine],\n\t);\n\n\tif (!resolver) return null;\n\n\treturn <WebGLWidgetLayer engine={engine} entities={entities} resolve={resolve} />;\n}\n","import { useCallback } from 'react';\nimport type { ReactNode } from 'react';\nimport { WidgetResolverProvider } from './context.js';\nimport type { WidgetRegistry } from './registry.js';\n\ninterface WidgetProviderProps {\n\tregistry: WidgetRegistry;\n\tchildren?: ReactNode;\n}\n\n/**\n * Connects a WidgetRegistry to the InfiniteCanvas.\n * Fix #9: Memoize the resolver so context consumers don't re-render on every parent render.\n */\nexport function WidgetProvider({ registry, children }: WidgetProviderProps) {\n\tconst resolver = useCallback(\n\t\t(_entityId: number, widgetType: string) => {\n\t\t\tconst def = registry.get(widgetType);\n\t\t\tif (!def) return null;\n\t\t\treturn { component: def.component, surface: def.surface ?? ('dom' as const) };\n\t\t},\n\t\t[registry],\n\t);\n\n\treturn <WidgetResolverProvider value={resolver}>{children}</WidgetResolverProvider>;\n}\n","import type { EntityId } from '../ecs/types.js';\n\nexport type WidgetSurface = 'dom' | 'webgl';\n\nexport interface WidgetDef {\n\ttype: string;\n\t/** Rendering surface — 'dom' (default) or 'webgl' (R3F). */\n\tsurface?: WidgetSurface;\n\tcomponent: React.ComponentType<{ entityId: EntityId }>;\n\tdefaultSize?: { width: number; height: number };\n\tminSize?: { width: number; height: number };\n}\n\nexport interface WidgetRegistry {\n\tregister(def: WidgetDef): void;\n\tget(type: string): WidgetDef | null;\n\tgetAll(): WidgetDef[];\n}\n\nexport function createWidgetRegistry(defs: WidgetDef[] = []): WidgetRegistry {\n\tconst map = new Map<string, WidgetDef>();\n\n\tfor (const def of defs) {\n\t\tmap.set(def.type, def);\n\t}\n\n\treturn {\n\t\tregister(def: WidgetDef) {\n\t\t\tmap.set(def.type, def);\n\t\t},\n\t\tget(type: string) {\n\t\t\treturn map.get(type) ?? null;\n\t\t},\n\t\tgetAll() {\n\t\t\treturn [...map.values()];\n\t\t},\n\t};\n}\n","import { useCallback } from 'react';\nimport { Children, Selected, WidgetBreakpoint, WidgetData } from '../components.js';\nimport type { EntityId } from '../ecs/types.js';\nimport type { Breakpoint } from '../resources.js';\nimport { useLayoutEngine } from './context.js';\nimport { useComponent, useTag } from './hooks.js';\n\n/**\n * Read widget data for an entity. Re-renders when data changes.\n */\nexport function useWidgetData(entityId: EntityId): Record<string, any> {\n\tconst comp = useComponent(entityId, WidgetData);\n\treturn comp?.data ?? {};\n}\n\n/**\n * Read the current breakpoint for an entity. Re-renders when breakpoint changes.\n */\nexport function useBreakpoint(entityId: EntityId): Breakpoint {\n\tconst comp = useComponent(entityId, WidgetBreakpoint);\n\treturn comp?.current ?? 'normal';\n}\n\n/**\n * Read child entity IDs. Re-renders when children change.\n */\nexport function useChildren(entityId: EntityId): EntityId[] {\n\tconst comp = useComponent(entityId, Children);\n\treturn comp?.ids ?? [];\n}\n\n/** @deprecated Use useChildren instead */\nexport const useWidgetChildren = useChildren;\n\n/**\n * Check if this entity is selected. Re-renders when selection changes.\n */\nexport function useIsSelected(entityId: EntityId): boolean {\n\treturn useTag(entityId, Selected);\n}\n\n/**\n * Returns a function to update widget data via the engine.\n */\nexport function useUpdateWidget(entityId: EntityId): (patch: Record<string, any>) => void {\n\tconst engine = useLayoutEngine();\n\treturn useCallback(\n\t\t(patch: Record<string, any>) => {\n\t\t\tconst existing = engine.get(entityId, WidgetData);\n\t\t\tif (existing) {\n\t\t\t\tengine.set(entityId, WidgetData, {\n\t\t\t\t\tdata: { ...existing.data, ...patch },\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\t[engine, entityId],\n\t);\n}\n\n/** @deprecated Use useUpdateWidget instead */\nexport const useUpdateData = useUpdateWidget;\n"],"mappings":"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","names":["useCallback","jsx","useCallback","camera","visible","useCallback","useCallback"]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/** Opaque entity identifier — sequential integer internally */
|
|
2
|
+
type EntityId = number;
|
|
3
|
+
/** Component type definition created by defineComponent() */
|
|
4
|
+
interface ComponentType<T = any> {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly defaults: T;
|
|
7
|
+
/** Internal brand to distinguish components from tags */
|
|
8
|
+
readonly __kind: 'component';
|
|
9
|
+
}
|
|
10
|
+
/** Tag type definition created by defineTag() — marker with no data */
|
|
11
|
+
interface TagType {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly __kind: 'tag';
|
|
14
|
+
}
|
|
15
|
+
/** Resource type definition created by defineResource() */
|
|
16
|
+
interface ResourceType<T = any> {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly defaults: T;
|
|
19
|
+
readonly __kind: 'resource';
|
|
20
|
+
}
|
|
21
|
+
/** System definition created by defineSystem() */
|
|
22
|
+
interface SystemDef {
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly reads?: ReadonlyArray<ComponentType | TagType>;
|
|
25
|
+
readonly writes?: ReadonlyArray<ComponentType | TagType>;
|
|
26
|
+
readonly after?: string | string[];
|
|
27
|
+
readonly before?: string | string[];
|
|
28
|
+
execute: (world: World) => void;
|
|
29
|
+
}
|
|
30
|
+
/** Query result — array of entity IDs */
|
|
31
|
+
type QueryResult = EntityId[];
|
|
32
|
+
/** Component initializer for entity creation */
|
|
33
|
+
type ComponentInit = [ComponentType<any>, any] | [TagType];
|
|
34
|
+
/** Event handler types */
|
|
35
|
+
type ComponentChangedHandler<T = any> = (entityId: EntityId, prev: T | undefined, next: T) => void;
|
|
36
|
+
type TagChangedHandler = (entityId: EntityId) => void;
|
|
37
|
+
type FrameHandler = () => void;
|
|
38
|
+
type Unsubscribe = () => void;
|
|
39
|
+
/** The World interface — core ECS container */
|
|
40
|
+
interface World {
|
|
41
|
+
readonly currentTick: number;
|
|
42
|
+
readonly entityCount: number;
|
|
43
|
+
createEntity(): EntityId;
|
|
44
|
+
destroyEntity(id: EntityId): void;
|
|
45
|
+
entityExists(id: EntityId): boolean;
|
|
46
|
+
addComponent<T>(entity: EntityId, type: ComponentType<T>, data: T): void;
|
|
47
|
+
removeComponent<T>(entity: EntityId, type: ComponentType<T>): void;
|
|
48
|
+
getComponent<T>(entity: EntityId, type: ComponentType<T>): T | undefined;
|
|
49
|
+
hasComponent(entity: EntityId, type: ComponentType): boolean;
|
|
50
|
+
setComponent<T>(entity: EntityId, type: ComponentType<T>, data: Partial<T>): void;
|
|
51
|
+
addTag(entity: EntityId, type: TagType): void;
|
|
52
|
+
removeTag(entity: EntityId, type: TagType): void;
|
|
53
|
+
hasTag(entity: EntityId, type: TagType): boolean;
|
|
54
|
+
query(...types: (ComponentType | TagType)[]): QueryResult;
|
|
55
|
+
queryChanged(type: ComponentType): QueryResult;
|
|
56
|
+
queryAdded(type: ComponentType): QueryResult;
|
|
57
|
+
queryTagged(type: TagType): QueryResult;
|
|
58
|
+
getResource<T>(type: ResourceType<T>): T;
|
|
59
|
+
setResource<T>(type: ResourceType<T>, data: Partial<T>): void;
|
|
60
|
+
onComponentChanged<T>(type: ComponentType<T>, handler: ComponentChangedHandler<T>, entityId?: EntityId): Unsubscribe;
|
|
61
|
+
onTagAdded(type: TagType, handler: TagChangedHandler, entityId?: EntityId): Unsubscribe;
|
|
62
|
+
onTagRemoved(type: TagType, handler: TagChangedHandler, entityId?: EntityId): Unsubscribe;
|
|
63
|
+
onFrame(handler: FrameHandler): Unsubscribe;
|
|
64
|
+
clearDirty(): void;
|
|
65
|
+
incrementTick(): void;
|
|
66
|
+
emitFrame(): void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Performance profiler with User Timing API integration.
|
|
71
|
+
* Zero-cost when disabled — all methods are no-ops.
|
|
72
|
+
* When enabled, data shows in Chrome DevTools Performance tab.
|
|
73
|
+
*/
|
|
74
|
+
interface FrameSample {
|
|
75
|
+
tick: number;
|
|
76
|
+
timestamp: number;
|
|
77
|
+
/** Total tick duration (ms) */
|
|
78
|
+
totalMs: number;
|
|
79
|
+
/** Per-system durations (ms) */
|
|
80
|
+
systems: Record<string, number>;
|
|
81
|
+
/** Visible entity computation (ms) */
|
|
82
|
+
visibilityMs: number;
|
|
83
|
+
/** Entity counts at this frame */
|
|
84
|
+
entityCount: number;
|
|
85
|
+
visibleCount: number;
|
|
86
|
+
}
|
|
87
|
+
interface ProfilerStats {
|
|
88
|
+
/** Frames per second (based on tick rate, not rAF) */
|
|
89
|
+
fps: number;
|
|
90
|
+
/** Frame time stats (ms) */
|
|
91
|
+
frameTime: {
|
|
92
|
+
avg: number;
|
|
93
|
+
p50: number;
|
|
94
|
+
p95: number;
|
|
95
|
+
p99: number;
|
|
96
|
+
max: number;
|
|
97
|
+
};
|
|
98
|
+
/** Per-system average time (ms) */
|
|
99
|
+
systemAvg: Record<string, number>;
|
|
100
|
+
/** Per-system p95 time (ms) */
|
|
101
|
+
systemP95: Record<string, number>;
|
|
102
|
+
/** Frame budget utilization at 60fps (%) */
|
|
103
|
+
budgetUsed: number;
|
|
104
|
+
/** Total samples in buffer */
|
|
105
|
+
sampleCount: number;
|
|
106
|
+
}
|
|
107
|
+
declare class Profiler {
|
|
108
|
+
private enabled;
|
|
109
|
+
private ring;
|
|
110
|
+
private writeIdx;
|
|
111
|
+
private filled;
|
|
112
|
+
private frameStart;
|
|
113
|
+
private currentSystems;
|
|
114
|
+
private visibilityMs;
|
|
115
|
+
private currentTick;
|
|
116
|
+
/** Enable/disable profiling. When disabled, all methods are no-ops. */
|
|
117
|
+
setEnabled(on: boolean): void;
|
|
118
|
+
isEnabled(): boolean;
|
|
119
|
+
/** Call at the start of engine.tick() */
|
|
120
|
+
beginFrame(tick: number): void;
|
|
121
|
+
/** Call around each system execution */
|
|
122
|
+
beginSystem(name: string): void;
|
|
123
|
+
endSystem(name: string): void;
|
|
124
|
+
/** Call around the visibility computation phase */
|
|
125
|
+
beginVisibility(): void;
|
|
126
|
+
endVisibility(): void;
|
|
127
|
+
/** Call at the end of engine.tick() */
|
|
128
|
+
endFrame(entityCount: number, visibleCount: number): void;
|
|
129
|
+
/** Get the last N frame samples (newest first) */
|
|
130
|
+
getSamples(count?: number): FrameSample[];
|
|
131
|
+
/** Compute rolling statistics from the ring buffer */
|
|
132
|
+
getStats(): ProfilerStats;
|
|
133
|
+
/** Clear all collected data */
|
|
134
|
+
clear(): void;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export { type ComponentType as C, type EntityId as E, type FrameSample as F, Profiler as P, type QueryResult as Q, type ResourceType as R, type SystemDef as S, type TagType as T, type Unsubscribe as U, type World as W, type ProfilerStats as a, type ComponentInit as b };
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
/** Opaque entity identifier — sequential integer internally */
|
|
2
|
+
type EntityId = number;
|
|
3
|
+
/** Component type definition created by defineComponent() */
|
|
4
|
+
interface ComponentType<T = any> {
|
|
5
|
+
readonly name: string;
|
|
6
|
+
readonly defaults: T;
|
|
7
|
+
/** Internal brand to distinguish components from tags */
|
|
8
|
+
readonly __kind: 'component';
|
|
9
|
+
}
|
|
10
|
+
/** Tag type definition created by defineTag() — marker with no data */
|
|
11
|
+
interface TagType {
|
|
12
|
+
readonly name: string;
|
|
13
|
+
readonly __kind: 'tag';
|
|
14
|
+
}
|
|
15
|
+
/** Resource type definition created by defineResource() */
|
|
16
|
+
interface ResourceType<T = any> {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
readonly defaults: T;
|
|
19
|
+
readonly __kind: 'resource';
|
|
20
|
+
}
|
|
21
|
+
/** System definition created by defineSystem() */
|
|
22
|
+
interface SystemDef {
|
|
23
|
+
readonly name: string;
|
|
24
|
+
readonly reads?: ReadonlyArray<ComponentType | TagType>;
|
|
25
|
+
readonly writes?: ReadonlyArray<ComponentType | TagType>;
|
|
26
|
+
readonly after?: string | string[];
|
|
27
|
+
readonly before?: string | string[];
|
|
28
|
+
execute: (world: World) => void;
|
|
29
|
+
}
|
|
30
|
+
/** Query result — array of entity IDs */
|
|
31
|
+
type QueryResult = EntityId[];
|
|
32
|
+
/** Component initializer for entity creation */
|
|
33
|
+
type ComponentInit = [ComponentType<any>, any] | [TagType];
|
|
34
|
+
/** Event handler types */
|
|
35
|
+
type ComponentChangedHandler<T = any> = (entityId: EntityId, prev: T | undefined, next: T) => void;
|
|
36
|
+
type TagChangedHandler = (entityId: EntityId) => void;
|
|
37
|
+
type FrameHandler = () => void;
|
|
38
|
+
type Unsubscribe = () => void;
|
|
39
|
+
/** The World interface — core ECS container */
|
|
40
|
+
interface World {
|
|
41
|
+
readonly currentTick: number;
|
|
42
|
+
readonly entityCount: number;
|
|
43
|
+
createEntity(): EntityId;
|
|
44
|
+
destroyEntity(id: EntityId): void;
|
|
45
|
+
entityExists(id: EntityId): boolean;
|
|
46
|
+
addComponent<T>(entity: EntityId, type: ComponentType<T>, data: T): void;
|
|
47
|
+
removeComponent<T>(entity: EntityId, type: ComponentType<T>): void;
|
|
48
|
+
getComponent<T>(entity: EntityId, type: ComponentType<T>): T | undefined;
|
|
49
|
+
hasComponent(entity: EntityId, type: ComponentType): boolean;
|
|
50
|
+
setComponent<T>(entity: EntityId, type: ComponentType<T>, data: Partial<T>): void;
|
|
51
|
+
addTag(entity: EntityId, type: TagType): void;
|
|
52
|
+
removeTag(entity: EntityId, type: TagType): void;
|
|
53
|
+
hasTag(entity: EntityId, type: TagType): boolean;
|
|
54
|
+
query(...types: (ComponentType | TagType)[]): QueryResult;
|
|
55
|
+
queryChanged(type: ComponentType): QueryResult;
|
|
56
|
+
queryAdded(type: ComponentType): QueryResult;
|
|
57
|
+
queryTagged(type: TagType): QueryResult;
|
|
58
|
+
getResource<T>(type: ResourceType<T>): T;
|
|
59
|
+
setResource<T>(type: ResourceType<T>, data: Partial<T>): void;
|
|
60
|
+
onComponentChanged<T>(type: ComponentType<T>, handler: ComponentChangedHandler<T>, entityId?: EntityId): Unsubscribe;
|
|
61
|
+
onTagAdded(type: TagType, handler: TagChangedHandler, entityId?: EntityId): Unsubscribe;
|
|
62
|
+
onTagRemoved(type: TagType, handler: TagChangedHandler, entityId?: EntityId): Unsubscribe;
|
|
63
|
+
onFrame(handler: FrameHandler): Unsubscribe;
|
|
64
|
+
clearDirty(): void;
|
|
65
|
+
incrementTick(): void;
|
|
66
|
+
emitFrame(): void;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Performance profiler with User Timing API integration.
|
|
71
|
+
* Zero-cost when disabled — all methods are no-ops.
|
|
72
|
+
* When enabled, data shows in Chrome DevTools Performance tab.
|
|
73
|
+
*/
|
|
74
|
+
interface FrameSample {
|
|
75
|
+
tick: number;
|
|
76
|
+
timestamp: number;
|
|
77
|
+
/** Total tick duration (ms) */
|
|
78
|
+
totalMs: number;
|
|
79
|
+
/** Per-system durations (ms) */
|
|
80
|
+
systems: Record<string, number>;
|
|
81
|
+
/** Visible entity computation (ms) */
|
|
82
|
+
visibilityMs: number;
|
|
83
|
+
/** Entity counts at this frame */
|
|
84
|
+
entityCount: number;
|
|
85
|
+
visibleCount: number;
|
|
86
|
+
}
|
|
87
|
+
interface ProfilerStats {
|
|
88
|
+
/** Frames per second (based on tick rate, not rAF) */
|
|
89
|
+
fps: number;
|
|
90
|
+
/** Frame time stats (ms) */
|
|
91
|
+
frameTime: {
|
|
92
|
+
avg: number;
|
|
93
|
+
p50: number;
|
|
94
|
+
p95: number;
|
|
95
|
+
p99: number;
|
|
96
|
+
max: number;
|
|
97
|
+
};
|
|
98
|
+
/** Per-system average time (ms) */
|
|
99
|
+
systemAvg: Record<string, number>;
|
|
100
|
+
/** Per-system p95 time (ms) */
|
|
101
|
+
systemP95: Record<string, number>;
|
|
102
|
+
/** Frame budget utilization at 60fps (%) */
|
|
103
|
+
budgetUsed: number;
|
|
104
|
+
/** Total samples in buffer */
|
|
105
|
+
sampleCount: number;
|
|
106
|
+
}
|
|
107
|
+
declare class Profiler {
|
|
108
|
+
private enabled;
|
|
109
|
+
private ring;
|
|
110
|
+
private writeIdx;
|
|
111
|
+
private filled;
|
|
112
|
+
private frameStart;
|
|
113
|
+
private currentSystems;
|
|
114
|
+
private visibilityMs;
|
|
115
|
+
private currentTick;
|
|
116
|
+
/** Enable/disable profiling. When disabled, all methods are no-ops. */
|
|
117
|
+
setEnabled(on: boolean): void;
|
|
118
|
+
isEnabled(): boolean;
|
|
119
|
+
/** Call at the start of engine.tick() */
|
|
120
|
+
beginFrame(tick: number): void;
|
|
121
|
+
/** Call around each system execution */
|
|
122
|
+
beginSystem(name: string): void;
|
|
123
|
+
endSystem(name: string): void;
|
|
124
|
+
/** Call around the visibility computation phase */
|
|
125
|
+
beginVisibility(): void;
|
|
126
|
+
endVisibility(): void;
|
|
127
|
+
/** Call at the end of engine.tick() */
|
|
128
|
+
endFrame(entityCount: number, visibleCount: number): void;
|
|
129
|
+
/** Get the last N frame samples (newest first) */
|
|
130
|
+
getSamples(count?: number): FrameSample[];
|
|
131
|
+
/** Compute rolling statistics from the ring buffer */
|
|
132
|
+
getStats(): ProfilerStats;
|
|
133
|
+
/** Clear all collected data */
|
|
134
|
+
clear(): void;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export { type ComponentType as C, type EntityId as E, type FrameSample as F, Profiler as P, type QueryResult as Q, type ResourceType as R, type SystemDef as S, type TagType as T, type Unsubscribe as U, type World as W, type ProfilerStats as a, type ComponentInit as b };
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@jamesyong42/infinite-canvas",
|
|
3
|
+
"version": "0.0.1",
|
|
4
|
+
"description": "Infinite canvas library with ECS layout engine, React components, and WebGL rendering",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "James Yong",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/jamesyong-42/infinite-canvas.git",
|
|
10
|
+
"directory": "packages/infinite-canvas"
|
|
11
|
+
},
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://github.com/jamesyong-42/infinite-canvas/issues"
|
|
14
|
+
},
|
|
15
|
+
"homepage": "https://github.com/jamesyong-42/infinite-canvas#readme",
|
|
16
|
+
"keywords": ["infinite-canvas", "canvas", "ecs", "react", "webgl", "layout-engine"],
|
|
17
|
+
"publishConfig": {
|
|
18
|
+
"access": "public"
|
|
19
|
+
},
|
|
20
|
+
"sideEffects": false,
|
|
21
|
+
"type": "module",
|
|
22
|
+
"exports": {
|
|
23
|
+
".": {
|
|
24
|
+
"types": "./dist/index.d.ts",
|
|
25
|
+
"import": "./dist/index.js",
|
|
26
|
+
"require": "./dist/index.cjs"
|
|
27
|
+
},
|
|
28
|
+
"./ecs": {
|
|
29
|
+
"types": "./dist/ecs.d.ts",
|
|
30
|
+
"import": "./dist/ecs.js",
|
|
31
|
+
"require": "./dist/ecs.cjs"
|
|
32
|
+
},
|
|
33
|
+
"./advanced": {
|
|
34
|
+
"types": "./dist/advanced.d.ts",
|
|
35
|
+
"import": "./dist/advanced.js",
|
|
36
|
+
"require": "./dist/advanced.cjs"
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
"main": "./dist/index.cjs",
|
|
40
|
+
"module": "./dist/index.js",
|
|
41
|
+
"types": "./dist/index.d.ts",
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "tsup",
|
|
44
|
+
"dev": "tsup --watch",
|
|
45
|
+
"test": "vitest run",
|
|
46
|
+
"prepack": "cp ../../README.md ./README.md && cp ../../LICENSE ./LICENSE",
|
|
47
|
+
"postpack": "rm -f ./README.md ./LICENSE"
|
|
48
|
+
},
|
|
49
|
+
"dependencies": {
|
|
50
|
+
"rbush": "^4.0.1"
|
|
51
|
+
},
|
|
52
|
+
"peerDependencies": {
|
|
53
|
+
"@react-three/fiber": "^9.0.0",
|
|
54
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
55
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
56
|
+
"three": ">=0.170.0"
|
|
57
|
+
},
|
|
58
|
+
"peerDependenciesMeta": {
|
|
59
|
+
"three": {
|
|
60
|
+
"optional": true
|
|
61
|
+
},
|
|
62
|
+
"@react-three/fiber": {
|
|
63
|
+
"optional": true
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"devDependencies": {
|
|
67
|
+
"@react-three/fiber": "^9.5.0",
|
|
68
|
+
"@types/rbush": "^4.0.0",
|
|
69
|
+
"@types/react": "^19.0.0",
|
|
70
|
+
"@types/react-dom": "^19.0.0",
|
|
71
|
+
"@types/three": "^0.183.1",
|
|
72
|
+
"react": "^19.0.0",
|
|
73
|
+
"react-dom": "^19.0.0",
|
|
74
|
+
"three": "^0.183.2",
|
|
75
|
+
"tsup": "^8.0.0",
|
|
76
|
+
"vitest": "^3.0.0"
|
|
77
|
+
},
|
|
78
|
+
"files": ["dist"]
|
|
79
|
+
}
|