@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/react/webgl/GridRenderer.ts","../src/react/webgl/SelectionRenderer.ts","../src/react/context.ts","../src/components.ts","../src/react/hooks.ts","../src/react/webgl/WebGLWidgetSlot.tsx","../src/react/webgl/WebGLWidgetLayer.tsx","../src/react/WidgetSlot.tsx","../src/react/SelectionOverlaySlot.tsx","../src/snap.ts","../src/profiler.ts","../src/spatial.ts"],"sourcesContent":["import * as THREE from 'three';\n\n// === Public config ===\n\nexport interface GridConfig {\n\t/** World-unit spacings for up to 3 grid levels [fine, medium, coarse]. */\n\tspacings: [number, number, number];\n\t/** Dot RGB color as [r, g, b] in 0–1 range. */\n\tdotColor: [number, number, number];\n\t/** Base dot opacity multiplier (0–1). */\n\tdotAlpha: number;\n\t/** CSS-pixel range where a grid level fades in: [start, end]. */\n\tfadeIn: [number, number];\n\t/** CSS-pixel range where a grid level fades out: [start, end]. */\n\tfadeOut: [number, number];\n\t/** Dot radius range in CSS pixels [min, max]. Scaled by DPR internally. */\n\tdotRadius: [number, number];\n\t/** Per-level opacity weight: level i gets (base + i * step). */\n\tlevelWeight: [number, number];\n}\n\nexport const DEFAULT_GRID_CONFIG: GridConfig = {\n\tspacings: [8, 64, 512],\n\tdotColor: [0, 0, 0],\n\tdotAlpha: 0.18,\n\tfadeIn: [4, 12],\n\tfadeOut: [250, 500],\n\tdotRadius: [0.5, 1.4],\n\tlevelWeight: [1.0, 0.4],\n};\n\n// === Shader source ===\n\nconst vertexShader = /* glsl */ `\nvoid main() {\n\tgl_Position = vec4(position.xy, 0.0, 1.0);\n}\n`;\n\nconst fragmentShader = /* glsl */ `\nprecision highp float;\n\nuniform vec2 u_resolution; // device pixels\nuniform vec2 u_camera; // world-space top-left\nuniform float u_zoom; // CSS zoom\nuniform float u_dpr; // device pixel ratio\nuniform vec3 u_spacings; // world-unit grid spacings\nuniform vec3 u_dotColor; // dot RGB\nuniform float u_dotAlpha; // dot base alpha\nuniform vec2 u_fadeIn; // CSS-px [start, end]\nuniform vec2 u_fadeOut; // CSS-px [start, end]\nuniform vec2 u_dotRadius; // CSS-px [min, max]\nuniform vec2 u_levelWeight; // [base, step]\n\nvoid main() {\n\tvec2 devicePos = gl_FragCoord.xy;\n\tdevicePos.y = u_resolution.y - devicePos.y;\n\n\tfloat effectiveZoom = u_zoom * u_dpr;\n\tvec2 worldPos = devicePos / effectiveZoom + u_camera;\n\n\tfloat totalAlpha = 0.0;\n\n\tfor (int i = 0; i < 3; i++) {\n\t\tfloat spacing;\n\t\tif (i == 0) spacing = u_spacings.x;\n\t\telse if (i == 1) spacing = u_spacings.y;\n\t\telse spacing = u_spacings.z;\n\n\t\t// Screen spacing in CSS pixels (DPR-independent for consistent fading)\n\t\tfloat cssSpacing = spacing * u_zoom;\n\n\t\t// Fade curve\n\t\tfloat opacity = 0.0;\n\t\tif (cssSpacing >= u_fadeIn.x && cssSpacing < u_fadeIn.y) {\n\t\t\topacity = (cssSpacing - u_fadeIn.x) / (u_fadeIn.y - u_fadeIn.x);\n\t\t} else if (cssSpacing >= u_fadeIn.y && cssSpacing < u_fadeOut.x) {\n\t\t\topacity = 1.0;\n\t\t} else if (cssSpacing >= u_fadeOut.x && cssSpacing < u_fadeOut.y) {\n\t\t\topacity = 1.0 - (cssSpacing - u_fadeOut.x) / (u_fadeOut.y - u_fadeOut.x);\n\t\t}\n\t\tif (opacity <= 0.001) continue;\n\n\t\t// Distance to nearest grid intersection in device pixels\n\t\tvec2 f = fract(worldPos / spacing + 0.5) - 0.5;\n\t\tfloat dist = length(f) * spacing * effectiveZoom;\n\n\t\t// Dot radius in device pixels — grows as grid becomes sparser\n\t\tfloat t = clamp((cssSpacing - u_fadeIn.x) / 40.0, 0.0, 1.0);\n\t\tfloat radius = mix(u_dotRadius.x, u_dotRadius.y, t) * u_dpr;\n\n\t\t// Anti-aliased dot (0.5 device pixel smoothstep)\n\t\tfloat dot = 1.0 - smoothstep(radius - 0.5, radius + 0.5, dist);\n\n\t\t// Larger grid levels get progressively stronger dots\n\t\tfloat weight = u_levelWeight.x + float(i) * u_levelWeight.y;\n\t\ttotalAlpha += dot * opacity * weight;\n\t}\n\n\tgl_FragColor = vec4(u_dotColor, clamp(totalAlpha * u_dotAlpha, 0.0, 1.0));\n}\n`;\n\n// === Renderer class ===\n\nexport class GridRenderer {\n\tprivate renderer: THREE.WebGLRenderer;\n\tprivate scene: THREE.Scene;\n\tprivate camera: THREE.OrthographicCamera;\n\tprivate material: THREE.ShaderMaterial;\n\tprivate mesh: THREE.Mesh;\n\n\tconstructor(canvas: HTMLCanvasElement) {\n\t\tthis.renderer = new THREE.WebGLRenderer({\n\t\t\tcanvas,\n\t\t\talpha: true,\n\t\t\tantialias: false,\n\t\t\tpremultipliedAlpha: false,\n\t\t});\n\t\tthis.renderer.setClearColor(0x000000, 0);\n\n\t\tthis.scene = new THREE.Scene();\n\t\tthis.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);\n\n\t\tthis.material = new THREE.ShaderMaterial({\n\t\t\tvertexShader,\n\t\t\tfragmentShader,\n\t\t\tuniforms: {\n\t\t\t\tu_resolution: { value: new THREE.Vector2(1, 1) },\n\t\t\t\tu_camera: { value: new THREE.Vector2(0, 0) },\n\t\t\t\tu_zoom: { value: 1 },\n\t\t\t\tu_dpr: { value: 1 },\n\t\t\t\tu_spacings: { value: new THREE.Vector3(8, 64, 512) },\n\t\t\t\tu_dotColor: { value: new THREE.Vector3(0, 0, 0) },\n\t\t\t\tu_dotAlpha: { value: 0.18 },\n\t\t\t\tu_fadeIn: { value: new THREE.Vector2(4, 12) },\n\t\t\t\tu_fadeOut: { value: new THREE.Vector2(250, 500) },\n\t\t\t\tu_dotRadius: { value: new THREE.Vector2(0.5, 1.4) },\n\t\t\t\tu_levelWeight: { value: new THREE.Vector2(1.0, 0.4) },\n\t\t\t},\n\t\t\ttransparent: true,\n\t\t\tdepthTest: false,\n\t\t\tdepthWrite: false,\n\t\t});\n\n\t\t// Fullscreen triangle (more efficient than quad — no diagonal seam)\n\t\tconst geometry = new THREE.BufferGeometry();\n\t\tconst vertices = new Float32Array([-1, -1, 0, 3, -1, 0, -1, 3, 0]);\n\t\tgeometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));\n\n\t\tthis.mesh = new THREE.Mesh(geometry, this.material);\n\t\tthis.scene.add(this.mesh);\n\t}\n\n\t/** Apply a (partial) grid config. Only provided fields are updated. */\n\tsetConfig(config: Partial<GridConfig>) {\n\t\tconst u = this.material.uniforms;\n\t\tif (config.spacings) u.u_spacings.value.set(...config.spacings);\n\t\tif (config.dotColor) u.u_dotColor.value.set(...config.dotColor);\n\t\tif (config.dotAlpha !== undefined) u.u_dotAlpha.value = config.dotAlpha;\n\t\tif (config.fadeIn) u.u_fadeIn.value.set(...config.fadeIn);\n\t\tif (config.fadeOut) u.u_fadeOut.value.set(...config.fadeOut);\n\t\tif (config.dotRadius) u.u_dotRadius.value.set(...config.dotRadius);\n\t\tif (config.levelWeight) u.u_levelWeight.value.set(...config.levelWeight);\n\t}\n\n\tsetSize(width: number, height: number, dpr = 1) {\n\t\tthis.renderer.setSize(width, height, false);\n\t\tthis.renderer.setPixelRatio(dpr);\n\t\tconst u = this.material.uniforms;\n\t\tu.u_resolution.value.set(width * dpr, height * dpr);\n\t\tu.u_dpr.value = dpr;\n\t}\n\n\trender(cameraX: number, cameraY: number, zoom: number) {\n\t\tconst u = this.material.uniforms;\n\t\tu.u_camera.value.set(cameraX, cameraY);\n\t\tu.u_zoom.value = zoom;\n\t\tthis.renderer.render(this.scene, this.camera);\n\t}\n\n\tdispose() {\n\t\tthis.mesh.geometry.dispose();\n\t\tthis.material.dispose();\n\t\tthis.renderer.dispose();\n\t}\n\n\t/** Expose for future WebGL widget rendering */\n\tgetWebGLRenderer(): THREE.WebGLRenderer {\n\t\treturn this.renderer;\n\t}\n}\n","import * as THREE from 'three';\nimport type { EqualSpacingIndicator, SnapGuide } from '../../snap.js';\n\n// === Public config (Figma-style defaults) ===\n\nexport interface SelectionConfig {\n\t/** Selection outline color [r,g,b] 0-1. Default: Figma blue. */\n\toutlineColor: [number, number, number];\n\t/** Selection outline width in screen px. */\n\toutlineWidth: number;\n\t/** Hover outline color [r,g,b] 0-1. */\n\thoverColor: [number, number, number];\n\t/** Hover outline width in screen px. */\n\thoverWidth: number;\n\t/** Handle size in screen px. */\n\thandleSize: number;\n\t/** Handle fill color [r,g,b] 0-1 (white). */\n\thandleFill: [number, number, number];\n\t/** Handle border color [r,g,b] 0-1 (same as outline). */\n\thandleBorder: [number, number, number];\n\t/** Handle border width in screen px. */\n\thandleBorderWidth: number;\n\t/** Group bbox dash length in screen px (0 = solid). */\n\tgroupDash: number;\n}\n\nexport const DEFAULT_SELECTION_CONFIG: SelectionConfig = {\n\toutlineColor: [0.051, 0.6, 1.0], // #0d99ff (Figma blue)\n\toutlineWidth: 1.5,\n\thoverColor: [0.051, 0.6, 1.0],\n\thoverWidth: 1.0,\n\thandleSize: 8,\n\thandleFill: [1, 1, 1],\n\thandleBorder: [0.051, 0.6, 1.0],\n\thandleBorderWidth: 1.5,\n\tgroupDash: 4,\n};\n\n// === Bounds data passed per frame ===\n\nexport interface SelectionBounds {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\n// === Shader ===\n\nconst MAX_ENTITIES = 32;\n\nconst vertexShader = /* glsl */ `\nvoid main() {\n\tgl_Position = vec4(position.xy, 0.0, 1.0);\n}\n`;\n\nconst fragmentShader = /* glsl */ `\nprecision highp float;\n\nuniform vec2 u_resolution;\nuniform vec2 u_camera;\nuniform float u_zoom;\nuniform float u_dpr;\n\n// Selection data\nuniform int u_count;\nuniform vec4 u_bounds[${MAX_ENTITIES}]; // (worldX, worldY, width, height)\nuniform int u_hoverIdx; // -1 = none\nuniform vec4 u_groupBounds; // group bbox (0 if count <= 1)\nuniform int u_hasGroup;\n\n// Snap guides\nuniform int u_guideCount;\nuniform vec4 u_guides[16]; // (axis: 0=x/1=y, position, 0, 0)\nuniform int u_spacingCount;\nuniform vec4 u_spacings[8]; // equal-spacing segments: (axis, from, to, perpPos)\nuniform vec3 u_guideColor;\n\n// Style\nuniform vec3 u_outlineColor;\nuniform float u_outlineWidth;\nuniform vec3 u_hoverColor;\nuniform float u_hoverWidth;\nuniform float u_handleSize;\nuniform vec3 u_handleFill;\nuniform vec3 u_handleBorder;\nuniform float u_handleBorderWidth;\nuniform float u_groupDash;\n\n// SDF for axis-aligned rectangle outline (returns distance to edge)\nfloat sdRectOutline(vec2 p, vec2 center, vec2 halfSize) {\n\tvec2 d = abs(p - center) - halfSize;\n\tfloat outside = length(max(d, 0.0));\n\tfloat inside = min(max(d.x, d.y), 0.0);\n\treturn abs(outside + inside);\n}\n\n// SDF for filled square\nfloat sdSquare(vec2 p, vec2 center, float halfSize) {\n\tvec2 d = abs(p - center) - vec2(halfSize);\n\treturn max(d.x, d.y);\n}\n\nvoid main() {\n\tif (u_count == 0 && u_hoverIdx < 0) discard;\n\n\tvec2 devicePos = gl_FragCoord.xy;\n\tdevicePos.y = u_resolution.y - devicePos.y;\n\n\tfloat effectiveZoom = u_zoom * u_dpr;\n\tvec2 worldPos = devicePos / effectiveZoom + u_camera;\n\n\t// Screen-space conversion factor\n\tfloat pxToWorld = 1.0 / effectiveZoom;\n\n\tvec4 color = vec4(0.0);\n\n\t// --- Hover outline ---\n\tif (u_hoverIdx >= 0 && u_hoverIdx < ${MAX_ENTITIES}) {\n\t\tvec4 b = u_bounds[u_hoverIdx];\n\t\tvec2 center = vec2(b.x + b.z * 0.5, b.y + b.w * 0.5);\n\t\tvec2 halfSize = vec2(b.z, b.w) * 0.5;\n\t\tfloat dist = sdRectOutline(worldPos, center, halfSize);\n\t\tfloat width = u_hoverWidth * pxToWorld;\n\t\tfloat alpha = 1.0 - smoothstep(width - pxToWorld * 0.5, width + pxToWorld * 0.5, dist);\n\t\tcolor = max(color, vec4(u_hoverColor, alpha * 0.6));\n\t}\n\n\t// --- Selection outlines ---\n\tfor (int i = 0; i < ${MAX_ENTITIES}; i++) {\n\t\tif (i >= u_count) break;\n\t\tvec4 b = u_bounds[i];\n\t\tvec2 center = vec2(b.x + b.z * 0.5, b.y + b.w * 0.5);\n\t\tvec2 halfSize = vec2(b.z, b.w) * 0.5;\n\n\t\t// Outline\n\t\tfloat dist = sdRectOutline(worldPos, center, halfSize);\n\t\tfloat width = u_outlineWidth * pxToWorld;\n\t\tfloat outlineAlpha = 1.0 - smoothstep(width - pxToWorld * 0.5, width + pxToWorld * 0.5, dist);\n\t\tcolor = max(color, vec4(u_outlineColor, outlineAlpha));\n\n\t\t// 8 resize handles\n\t\tfloat hs = u_handleSize * 0.5 * pxToWorld;\n\t\tfloat bw = u_handleBorderWidth * pxToWorld;\n\t\tvec2 corners[8];\n\t\tcorners[0] = vec2(b.x, b.y); // nw\n\t\tcorners[1] = vec2(b.x + b.z * 0.5, b.y); // n\n\t\tcorners[2] = vec2(b.x + b.z, b.y); // ne\n\t\tcorners[3] = vec2(b.x + b.z, b.y + b.w * 0.5); // e\n\t\tcorners[4] = vec2(b.x + b.z, b.y + b.w); // se\n\t\tcorners[5] = vec2(b.x + b.z * 0.5, b.y + b.w); // s\n\t\tcorners[6] = vec2(b.x, b.y + b.w); // sw\n\t\tcorners[7] = vec2(b.x, b.y + b.w * 0.5); // w\n\n\t\tfor (int h = 0; h < 8; h++) {\n\t\t\tfloat d = sdSquare(worldPos, corners[h], hs);\n\t\t\t// Fill (white)\n\t\t\tfloat fillAlpha = 1.0 - smoothstep(-pxToWorld * 0.5, pxToWorld * 0.5, d);\n\t\t\t// Border\n\t\t\tfloat borderDist = abs(d + bw * 0.5) - bw * 0.5;\n\t\t\tfloat borderAlpha = 1.0 - smoothstep(-pxToWorld * 0.5, pxToWorld * 0.5, borderDist);\n\n\t\t\tif (fillAlpha > 0.01) {\n\t\t\t\t// Composite: border color on top of fill\n\t\t\t\tvec3 handleColor = mix(u_handleFill, u_handleBorder, borderAlpha);\n\t\t\t\tcolor = vec4(handleColor, max(fillAlpha, color.a));\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Group bounding box (dashed) ---\n\tif (u_hasGroup == 1 && u_count > 1) {\n\t\tvec4 gb = u_groupBounds;\n\t\tvec2 center = vec2(gb.x + gb.z * 0.5, gb.y + gb.w * 0.5);\n\t\tvec2 halfSize = vec2(gb.z, gb.w) * 0.5;\n\t\tfloat dist = sdRectOutline(worldPos, center, halfSize);\n\t\tfloat width = u_outlineWidth * 0.75 * pxToWorld;\n\t\tfloat lineAlpha = 1.0 - smoothstep(width - pxToWorld * 0.5, width + pxToWorld * 0.5, dist);\n\n\t\t// Dash pattern along the rectangle perimeter\n\t\tif (u_groupDash > 0.0 && lineAlpha > 0.01) {\n\t\t\tvec2 rel = worldPos - vec2(gb.x, gb.y);\n\t\t\tfloat perim;\n\t\t\t// Approximate perimeter position for dash\n\t\t\tif (abs(rel.y) < width || abs(rel.y - gb.w) < width) {\n\t\t\t\tperim = rel.x;\n\t\t\t} else {\n\t\t\t\tperim = rel.y;\n\t\t\t}\n\t\t\tfloat dashWorld = u_groupDash * pxToWorld;\n\t\t\tfloat dashPattern = step(0.5, fract(perim / (dashWorld * 2.0)));\n\t\t\tlineAlpha *= dashPattern;\n\t\t}\n\n\t\tcolor = max(color, vec4(u_outlineColor, lineAlpha * 0.5));\n\t}\n\n\t// --- Snap guide lines ---\n\tfor (int i = 0; i < 16; i++) {\n\t\tif (i >= u_guideCount) break;\n\t\tvec4 g = u_guides[i];\n\t\tfloat guideWidth = 0.5 * pxToWorld;\n\t\tfloat dist;\n\t\tif (g.x < 0.5) {\n\t\t\t// Vertical line (x-axis alignment)\n\t\t\tdist = abs(worldPos.x - g.y);\n\t\t} else {\n\t\t\t// Horizontal line (y-axis alignment)\n\t\t\tdist = abs(worldPos.y - g.y);\n\t\t}\n\t\tfloat guideAlpha = 1.0 - smoothstep(guideWidth - pxToWorld * 0.3, guideWidth + pxToWorld * 0.3, dist);\n\t\tcolor = max(color, vec4(u_guideColor, guideAlpha * 0.8));\n\t}\n\n\t// --- Equal spacing indicators ---\n\tfor (int i = 0; i < 8; i++) {\n\t\tif (i >= u_spacingCount) break;\n\t\tvec4 s = u_spacings[i];\n\t\tfloat lineWidth = 0.5 * pxToWorld;\n\t\tfloat segAlpha = 0.0;\n\t\tif (s.x < 0.5) {\n\t\t\t// Horizontal segment (x-axis gap)\n\t\t\tfloat yDist = abs(worldPos.y - s.w);\n\t\t\tfloat xInRange = step(s.y, worldPos.x) * step(worldPos.x, s.z);\n\t\t\t// Center line\n\t\t\tsegAlpha = (1.0 - smoothstep(lineWidth, lineWidth + pxToWorld, yDist)) * xInRange;\n\t\t\t// End bars (perpendicular marks at from and to)\n\t\t\tfloat barHeight = 4.0 * pxToWorld;\n\t\t\tfloat barFromDist = abs(worldPos.x - s.y);\n\t\t\tfloat barFromAlpha = (1.0 - smoothstep(lineWidth, lineWidth + pxToWorld, barFromDist))\n\t\t\t\t* (1.0 - smoothstep(barHeight, barHeight + pxToWorld, abs(worldPos.y - s.w)));\n\t\t\tfloat barToDist = abs(worldPos.x - s.z);\n\t\t\tfloat barToAlpha = (1.0 - smoothstep(lineWidth, lineWidth + pxToWorld, barToDist))\n\t\t\t\t* (1.0 - smoothstep(barHeight, barHeight + pxToWorld, abs(worldPos.y - s.w)));\n\t\t\tsegAlpha = max(segAlpha, max(barFromAlpha, barToAlpha));\n\t\t} else {\n\t\t\t// Vertical segment (y-axis gap)\n\t\t\tfloat xDist = abs(worldPos.x - s.w);\n\t\t\tfloat yInRange = step(s.y, worldPos.y) * step(worldPos.y, s.z);\n\t\t\tsegAlpha = (1.0 - smoothstep(lineWidth, lineWidth + pxToWorld, xDist)) * yInRange;\n\t\t\tfloat barWidth = 4.0 * pxToWorld;\n\t\t\tfloat barFromAlpha = (1.0 - smoothstep(lineWidth, lineWidth + pxToWorld, abs(worldPos.y - s.y)))\n\t\t\t\t* (1.0 - smoothstep(barWidth, barWidth + pxToWorld, abs(worldPos.x - s.w)));\n\t\t\tfloat barToAlpha = (1.0 - smoothstep(lineWidth, lineWidth + pxToWorld, abs(worldPos.y - s.z)))\n\t\t\t\t* (1.0 - smoothstep(barWidth, barWidth + pxToWorld, abs(worldPos.x - s.w)));\n\t\t\tsegAlpha = max(segAlpha, max(barFromAlpha, barToAlpha));\n\t\t}\n\t\tcolor = max(color, vec4(u_guideColor, segAlpha * 0.7));\n\t}\n\n\tif (color.a < 0.01) discard;\n\tgl_FragColor = color;\n}\n`;\n\n// === Renderer class ===\n\nexport class SelectionRenderer {\n\tprivate material: THREE.ShaderMaterial;\n\tprivate mesh: THREE.Mesh;\n\tprivate scene: THREE.Scene;\n\tprivate camera: THREE.OrthographicCamera;\n\n\tconstructor() {\n\t\tthis.scene = new THREE.Scene();\n\t\tthis.camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);\n\n\t\tconst boundsDefault = [];\n\t\tfor (let i = 0; i < MAX_ENTITIES; i++) {\n\t\t\tboundsDefault.push(new THREE.Vector4(0, 0, 0, 0));\n\t\t}\n\n\t\tthis.material = new THREE.ShaderMaterial({\n\t\t\tvertexShader,\n\t\t\tfragmentShader,\n\t\t\tuniforms: {\n\t\t\t\tu_resolution: { value: new THREE.Vector2(1, 1) },\n\t\t\t\tu_camera: { value: new THREE.Vector2(0, 0) },\n\t\t\t\tu_zoom: { value: 1 },\n\t\t\t\tu_dpr: { value: 1 },\n\t\t\t\tu_count: { value: 0 },\n\t\t\t\tu_bounds: { value: boundsDefault },\n\t\t\t\tu_hoverIdx: { value: -1 },\n\t\t\t\tu_groupBounds: { value: new THREE.Vector4(0, 0, 0, 0) },\n\t\t\t\tu_hasGroup: { value: 0 },\n\t\t\t\t// Style (Figma defaults)\n\t\t\t\tu_outlineColor: { value: new THREE.Vector3(...DEFAULT_SELECTION_CONFIG.outlineColor) },\n\t\t\t\tu_outlineWidth: { value: DEFAULT_SELECTION_CONFIG.outlineWidth },\n\t\t\t\tu_hoverColor: { value: new THREE.Vector3(...DEFAULT_SELECTION_CONFIG.hoverColor) },\n\t\t\t\tu_hoverWidth: { value: DEFAULT_SELECTION_CONFIG.hoverWidth },\n\t\t\t\tu_handleSize: { value: DEFAULT_SELECTION_CONFIG.handleSize },\n\t\t\t\tu_handleFill: { value: new THREE.Vector3(...DEFAULT_SELECTION_CONFIG.handleFill) },\n\t\t\t\tu_handleBorder: { value: new THREE.Vector3(...DEFAULT_SELECTION_CONFIG.handleBorder) },\n\t\t\t\tu_handleBorderWidth: { value: DEFAULT_SELECTION_CONFIG.handleBorderWidth },\n\t\t\t\tu_groupDash: { value: DEFAULT_SELECTION_CONFIG.groupDash },\n\t\t\t\t// Snap guides\n\t\t\t\tu_guideCount: { value: 0 },\n\t\t\t\tu_guides: { value: Array.from({ length: 16 }, () => new THREE.Vector4(0, 0, 0, 0)) },\n\t\t\t\tu_spacingCount: { value: 0 },\n\t\t\t\tu_spacings: { value: Array.from({ length: 8 }, () => new THREE.Vector4(0, 0, 0, 0)) },\n\t\t\t\tu_guideColor: { value: new THREE.Vector3(1.0, 0.0, 0.55) }, // magenta/pink\n\t\t\t},\n\t\t\ttransparent: true,\n\t\t\tdepthTest: false,\n\t\t\tdepthWrite: false,\n\t\t});\n\n\t\tconst geometry = new THREE.BufferGeometry();\n\t\tconst vertices = new Float32Array([-1, -1, 0, 3, -1, 0, -1, 3, 0]);\n\t\tgeometry.setAttribute('position', new THREE.BufferAttribute(vertices, 3));\n\n\t\tthis.mesh = new THREE.Mesh(geometry, this.material);\n\t\tthis.scene.add(this.mesh);\n\t}\n\n\tsetConfig(config: Partial<SelectionConfig>) {\n\t\tconst u = this.material.uniforms;\n\t\tif (config.outlineColor) u.u_outlineColor.value.set(...config.outlineColor);\n\t\tif (config.outlineWidth !== undefined) u.u_outlineWidth.value = config.outlineWidth;\n\t\tif (config.hoverColor) u.u_hoverColor.value.set(...config.hoverColor);\n\t\tif (config.hoverWidth !== undefined) u.u_hoverWidth.value = config.hoverWidth;\n\t\tif (config.handleSize !== undefined) u.u_handleSize.value = config.handleSize;\n\t\tif (config.handleFill) u.u_handleFill.value.set(...config.handleFill);\n\t\tif (config.handleBorder) u.u_handleBorder.value.set(...config.handleBorder);\n\t\tif (config.handleBorderWidth !== undefined)\n\t\t\tu.u_handleBorderWidth.value = config.handleBorderWidth;\n\t\tif (config.groupDash !== undefined) u.u_groupDash.value = config.groupDash;\n\t}\n\n\tsetSize(resolution: THREE.Vector2, dpr: number) {\n\t\tthis.material.uniforms.u_resolution.value.copy(resolution);\n\t\tthis.material.uniforms.u_dpr.value = dpr;\n\t}\n\n\trender(\n\t\trenderer: THREE.WebGLRenderer,\n\t\tcameraX: number,\n\t\tcameraY: number,\n\t\tzoom: number,\n\t\tselected: SelectionBounds[],\n\t\thovered: SelectionBounds | null,\n\t\tguides: SnapGuide[] = [],\n\t\tspacings: EqualSpacingIndicator[] = [],\n\t) {\n\t\tconst u = this.material.uniforms;\n\t\tu.u_camera.value.set(cameraX, cameraY);\n\t\tu.u_zoom.value = zoom;\n\n\t\t// Upload selected bounds\n\t\tconst count = Math.min(selected.length, MAX_ENTITIES);\n\t\tu.u_count.value = count;\n\t\tfor (let i = 0; i < count; i++) {\n\t\t\tconst b = selected[i];\n\t\t\tu.u_bounds.value[i].set(b.x, b.y, b.width, b.height);\n\t\t}\n\n\t\t// Upload hover\n\t\tif (hovered && count < MAX_ENTITIES) {\n\t\t\t// Find if hovered is in the selected list\n\t\t\tlet hoverIdx = -1;\n\t\t\tfor (let i = 0; i < count; i++) {\n\t\t\t\tconst b = selected[i];\n\t\t\t\tif (b.x === hovered.x && b.y === hovered.y) {\n\t\t\t\t\thoverIdx = i;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (hoverIdx < 0) {\n\t\t\t\t// Hovered but not selected — add to bounds array\n\t\t\t\tu.u_bounds.value[count].set(hovered.x, hovered.y, hovered.width, hovered.height);\n\t\t\t\tu.u_hoverIdx.value = count;\n\t\t\t} else {\n\t\t\t\tu.u_hoverIdx.value = -1; // already selected, no separate hover\n\t\t\t}\n\t\t} else {\n\t\t\tu.u_hoverIdx.value = -1;\n\t\t}\n\n\t\t// Group bounding box\n\t\tif (count > 1) {\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 (let i = 0; i < count; i++) {\n\t\t\t\tconst b = selected[i];\n\t\t\t\tminX = Math.min(minX, b.x);\n\t\t\t\tminY = Math.min(minY, b.y);\n\t\t\t\tmaxX = Math.max(maxX, b.x + b.width);\n\t\t\t\tmaxY = Math.max(maxY, b.y + b.height);\n\t\t\t}\n\t\t\tu.u_groupBounds.value.set(minX, minY, maxX - minX, maxY - minY);\n\t\t\tu.u_hasGroup.value = 1;\n\t\t} else {\n\t\t\tu.u_hasGroup.value = 0;\n\t\t}\n\n\t\t// Upload snap guides\n\t\tconst gCount = Math.min(guides.length, 16);\n\t\tu.u_guideCount.value = gCount;\n\t\tfor (let i = 0; i < gCount; i++) {\n\t\t\tconst g = guides[i];\n\t\t\tu.u_guides.value[i].set(g.axis === 'x' ? 0 : 1, g.position, 0, 0);\n\t\t}\n\n\t\t// Upload equal spacing segments\n\t\tlet sIdx = 0;\n\t\tfor (const sp of spacings) {\n\t\t\tfor (const seg of sp.segments) {\n\t\t\t\tif (sIdx >= 8) break;\n\t\t\t\tu.u_spacings.value[sIdx].set(sp.axis === 'x' ? 0 : 1, seg.from, seg.to, sp.perpPosition);\n\t\t\t\tsIdx++;\n\t\t\t}\n\t\t}\n\t\tu.u_spacingCount.value = sIdx;\n\n\t\t// Render without clearing (composites on top of grid)\n\t\tconst prevAutoClear = renderer.autoClear;\n\t\trenderer.autoClear = false;\n\t\trenderer.render(this.scene, this.camera);\n\t\trenderer.autoClear = prevAutoClear;\n\t}\n\n\tdispose() {\n\t\tthis.mesh.geometry.dispose();\n\t\tthis.material.dispose();\n\t}\n}\n","import { createContext, useContext } from 'react';\nimport type { EntityId } from '../ecs/types.js';\nimport type { LayoutEngine } from '../engine.js';\n\n// === Engine Context ===\n\nconst EngineContext = createContext<LayoutEngine | null>(null);\n\nexport const EngineProvider = EngineContext.Provider;\n\n// === Container Ref Context ===\n// Shared so WidgetSlot can compute container-relative pointer coordinates.\n\nconst ContainerRefContext = createContext<React.RefObject<HTMLDivElement | null> | null>(null);\n\nexport const ContainerRefProvider = ContainerRefContext.Provider;\n\nexport function useContainerRef(): React.RefObject<HTMLDivElement | null> | null {\n\treturn useContext(ContainerRefContext);\n}\n\nexport function useLayoutEngine(): LayoutEngine {\n\tconst engine = useContext(EngineContext);\n\tif (!engine) {\n\t\tthrow new Error('useLayoutEngine must be used within an <InfiniteCanvas>');\n\t}\n\treturn engine;\n}\n\n/** @deprecated Use useLayoutEngine instead */\nexport const useEngine = useLayoutEngine;\n\n// === Widget Resolver Context ===\n\nexport type WidgetSurface = 'dom' | 'webgl';\n\nexport interface ResolvedWidget {\n\tcomponent: React.ComponentType<{ entityId: EntityId }>;\n\tsurface: WidgetSurface;\n}\n\nexport type WidgetResolver = (entityId: EntityId, widgetType: string) => ResolvedWidget | null;\n\nconst WidgetResolverContext = createContext<WidgetResolver | null>(null);\n\nexport const WidgetResolverProvider = WidgetResolverContext.Provider;\n\nexport function useWidgetResolver(): WidgetResolver | null {\n\treturn useContext(WidgetResolverContext);\n}\n","import { defineComponent, defineTag } from './ecs/index.js';\nimport type { EntityId } from './ecs/index.js';\n\n// === Spatial ===\nexport const Transform2D = defineComponent('Transform2D', {\n\tx: 0,\n\ty: 0,\n\twidth: 100,\n\theight: 100,\n\trotation: 0,\n});\n\nexport const WorldBounds = defineComponent('WorldBounds', {\n\tworldX: 0,\n\tworldY: 0,\n\tworldWidth: 0,\n\tworldHeight: 0,\n});\n\nexport const ZIndex = defineComponent('ZIndex', { value: 0 });\n\n// === Hierarchy ===\nexport const Parent = defineComponent('Parent', { id: 0 as EntityId });\nexport const Children = defineComponent('Children', { ids: [] as EntityId[] });\n\n// === Widget ===\nexport const Widget = defineComponent('Widget', {\n\tsurface: 'dom' as 'dom' | 'webgl' | 'webview',\n\ttype: '' as string,\n});\n\nexport const WidgetData = defineComponent('WidgetData', {\n\tdata: {} as Record<string, any>,\n});\n\nexport const WidgetBreakpoint = defineComponent('WidgetBreakpoint', {\n\tcurrent: 'normal' as 'micro' | 'compact' | 'normal' | 'expanded' | 'detailed',\n\tscreenWidth: 0,\n\tscreenHeight: 0,\n});\n\n// === Container ===\nexport const Container = defineComponent('Container', { enterable: true });\n\n// === Tags ===\nexport const Selectable = defineTag('Selectable');\nexport const Draggable = defineTag('Draggable');\nexport const Resizable = defineTag('Resizable');\nexport const Locked = defineTag('Locked');\nexport const Selected = defineTag('Selected');\nexport const Active = defineTag('Active');\nexport const Visible = defineTag('Visible');\n","import { useEffect, useRef, useState } from 'react';\nimport type { ComponentType, EntityId, ResourceType, TagType } from '../ecs/types.js';\nimport { useLayoutEngine } from './context.js';\n\n/**\n * Subscribe to a component on a specific entity.\n * Re-renders only when this component on this entity changes.\n */\nexport function useComponent<T>(entity: EntityId, type: ComponentType<T>): T | undefined {\n\tconst engine = useLayoutEngine();\n\tconst [value, setValue] = useState<T | undefined>(() => engine.get(entity, type));\n\n\tuseEffect(() => {\n\t\tsetValue(engine.get(entity, type));\n\n\t\tconst unsub = engine.world.onComponentChanged(\n\t\t\ttype,\n\t\t\t(_id, _prev, next) => {\n\t\t\t\tsetValue({ ...next });\n\t\t\t},\n\t\t\tentity,\n\t\t);\n\n\t\treturn unsub;\n\t}, [engine, entity, type]);\n\n\treturn value;\n}\n\n/**\n * Subscribe to a tag on a specific entity.\n * Re-renders when the tag is added or removed.\n */\nexport function useTag(entity: EntityId, type: TagType): boolean {\n\tconst engine = useLayoutEngine();\n\tconst [has, setHas] = useState(() => engine.world.hasTag(entity, type));\n\n\tuseEffect(() => {\n\t\tsetHas(engine.world.hasTag(entity, type));\n\n\t\tconst unsub1 = engine.world.onTagAdded(type, () => setHas(true), entity);\n\t\tconst unsub2 = engine.world.onTagRemoved(type, () => setHas(false), entity);\n\n\t\treturn () => {\n\t\t\tunsub1();\n\t\t\tunsub2();\n\t\t};\n\t}, [engine, entity, type]);\n\n\treturn has;\n}\n\n/**\n * Subscribe to a resource. Re-renders only when the resource actually changes.\n * Fix #6: Caches previous value and compares before triggering re-render.\n */\nexport function useResource<T>(type: ResourceType<T>): T {\n\tconst engine = useLayoutEngine();\n\tconst [value, setValue] = useState<T>(() => ({ ...engine.world.getResource(type) }));\n\tconst prevRef = useRef<string>('');\n\n\tuseEffect(() => {\n\t\tconst unsub = engine.onFrame(() => {\n\t\t\tconst current = engine.world.getResource(type);\n\t\t\tconst serialized = JSON.stringify(current);\n\t\t\tif (serialized !== prevRef.current) {\n\t\t\t\tprevRef.current = serialized;\n\t\t\t\tsetValue({ ...current });\n\t\t\t}\n\t\t});\n\t\treturn unsub;\n\t}, [engine, type]);\n\n\treturn value;\n}\n\n/**\n * Query entities matching component/tag types.\n * Re-renders when the result set changes.\n * Fix #7: Uses a stable key instead of spreading types into deps array.\n */\nexport function useQuery(...types: (ComponentType | TagType)[]): EntityId[] {\n\tconst engine = useLayoutEngine();\n\t// Create a stable key from type names for the dependency\n\tconst typeKey = types.map((t) => t.name).join(',');\n\tconst [result, setResult] = useState<EntityId[]>(() => engine.world.query(...types));\n\n\tuseEffect(() => {\n\t\tconst unsub = engine.onFrame(() => {\n\t\t\tconst next = engine.world.query(...types);\n\t\t\tsetResult((prev) => {\n\t\t\t\tif (prev.length !== next.length || prev.some((id, i) => id !== next[i])) {\n\t\t\t\t\treturn next;\n\t\t\t\t}\n\t\t\t\treturn prev;\n\t\t\t});\n\t\t});\n\t\treturn unsub;\n\t\t// eslint-disable-next-line react-hooks/exhaustive-deps\n\t}, [engine, typeKey]);\n\n\treturn result;\n}\n\n/**\n * Get all entities with a specific tag.\n * Re-renders when the set changes.\n */\nexport function useTaggedEntities(type: TagType): EntityId[] {\n\tconst engine = useLayoutEngine();\n\tconst [result, setResult] = useState<EntityId[]>(() => engine.world.queryTagged(type));\n\n\tuseEffect(() => {\n\t\tconst update = () => setResult([...engine.world.queryTagged(type)]);\n\t\tconst unsub1 = engine.world.onTagAdded(type, update);\n\t\tconst unsub2 = engine.world.onTagRemoved(type, update);\n\t\treturn () => {\n\t\t\tunsub1();\n\t\t\tunsub2();\n\t\t};\n\t}, [engine, type]);\n\n\treturn result;\n}\n","import { useFrame } from '@react-three/fiber';\nimport { useRef } from 'react';\nimport type { Group } from 'three';\nimport { WorldBounds } from '../../components.js';\nimport type { EntityId } from '../../ecs/types.js';\nimport { useLayoutEngine } from '../context.js';\nimport { useComponent } from '../hooks.js';\n\ninterface WebGLWidgetSlotProps {\n\tentityId: EntityId;\n\tcomponent: React.ComponentType<{ entityId: EntityId; width: number; height: number }>;\n}\n\n/**\n * Positions a Three.js Group at the entity's world-space center.\n * The widget component renders in local space: origin at center,\n * X right, Y up, dimensions = (width, height) in world units.\n */\nexport function WebGLWidgetSlot({ entityId, component: WidgetComponent }: WebGLWidgetSlotProps) {\n\tconst groupRef = useRef<Group>(null);\n\tconst engine = useLayoutEngine();\n\n\t// Read WorldBounds reactively for initial render\n\tconst wb = useComponent(entityId, WorldBounds);\n\n\t// Update position every frame (camera may have moved)\n\tuseFrame(() => {\n\t\tif (!groupRef.current) return;\n\t\tconst bounds = engine.get(entityId, WorldBounds);\n\t\tif (!bounds) return;\n\t\t// Position at center of bounding box; flip Y for Three.js\n\t\tgroupRef.current.position.set(\n\t\t\tbounds.worldX + bounds.worldWidth / 2,\n\t\t\t-(bounds.worldY + bounds.worldHeight / 2),\n\t\t\t0,\n\t\t);\n\t});\n\n\tif (!wb) return null;\n\n\treturn (\n\t\t<group ref={groupRef}>\n\t\t\t<WidgetComponent entityId={entityId} width={wb.worldWidth} height={wb.worldHeight} />\n\t\t</group>\n\t);\n}\n","import { Canvas, useFrame, useThree } from '@react-three/fiber';\nimport { useMemo, useRef } from 'react';\nimport * as THREE from 'three';\nimport type { EntityId } from '../../ecs/types.js';\nimport type { LayoutEngine } from '../../engine.js';\nimport type { ResolvedWidget } from '../context.js';\nimport { EngineProvider } from '../context.js';\nimport { WebGLWidgetSlot } from './WebGLWidgetSlot.js';\n\n// === Camera sync component (runs inside R3F) ===\n\nfunction CameraSync({ engine }: { engine: LayoutEngine }) {\n\tconst { camera, size } = useThree();\n\n\tuseFrame(() => {\n\t\tconst cam = engine.getCamera();\n\t\tconst ortho = camera as THREE.OrthographicCamera;\n\n\t\t// Frustum in world units — matches our engine coordinate system\n\t\tortho.left = 0;\n\t\tortho.right = size.width / cam.zoom;\n\t\tortho.top = 0;\n\t\tortho.bottom = -(size.height / cam.zoom);\n\t\tortho.near = 0.1;\n\t\tortho.far = 10000;\n\n\t\t// Position camera at engine camera origin; flip Y for Three.js\n\t\tortho.position.set(cam.x, -cam.y, 1000);\n\t\tortho.updateProjectionMatrix();\n\t});\n\n\treturn null;\n}\n\n// === Main layer component ===\n\ninterface WebGLWidgetLayerProps {\n\tengine: LayoutEngine;\n\tentities: EntityId[];\n\tresolve: (entityId: EntityId) => ResolvedWidget | null;\n}\n\nexport function WebGLWidgetLayer({ engine, entities, resolve }: WebGLWidgetLayerProps) {\n\tconst canvasRef = useRef<HTMLCanvasElement>(null);\n\n\t// Create a stable orthographic camera (R3F needs one at init)\n\tconst initialCamera = useMemo(() => {\n\t\tconst cam = new THREE.OrthographicCamera(0, 1, 0, -1, 0.1, 10000);\n\t\tcam.position.set(0, 0, 1000);\n\t\treturn cam;\n\t}, []);\n\n\t// Build a map of entityId → component for rendering\n\tconst widgetEntries = useMemo(() => {\n\t\tconst result: {\n\t\t\tentityId: EntityId;\n\t\t\tcomponent: React.ComponentType<{ entityId: EntityId; width: number; height: number }>;\n\t\t}[] = [];\n\t\tfor (const id of entities) {\n\t\t\tconst resolved = resolve(id);\n\t\t\tif (resolved && resolved.surface === 'webgl') {\n\t\t\t\tresult.push({\n\t\t\t\t\tentityId: id,\n\t\t\t\t\tcomponent: resolved.component as React.ComponentType<{\n\t\t\t\t\t\tentityId: EntityId;\n\t\t\t\t\t\twidth: number;\n\t\t\t\t\t\theight: number;\n\t\t\t\t\t}>,\n\t\t\t\t});\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}, [entities, resolve]);\n\n\tif (widgetEntries.length === 0) return null;\n\n\treturn (\n\t\t<Canvas\n\t\t\tref={canvasRef}\n\t\t\tcamera={initialCamera}\n\t\t\tframeloop=\"always\"\n\t\t\tgl={{ alpha: true, antialias: true }}\n\t\t\tstyle={{\n\t\t\t\tposition: 'absolute',\n\t\t\t\tinset: 0,\n\t\t\t\tpointerEvents: 'none',\n\t\t\t\tzIndex: 1,\n\t\t\t}}\n\t\t>\n\t\t\t<EngineProvider value={engine}>\n\t\t\t\t<CameraSync engine={engine} />\n\t\t\t\t{widgetEntries.map(({ entityId, component }) => (\n\t\t\t\t\t<WebGLWidgetSlot key={entityId} entityId={entityId} component={component} />\n\t\t\t\t))}\n\t\t\t</EngineProvider>\n\t\t</Canvas>\n\t);\n}\n","import { memo, useCallback, useEffect, useRef } from 'react';\nimport { Widget, WorldBounds } from '../components.js';\nimport type { EntityId } from '../ecs/types.js';\nimport type { Modifiers } from '../engine.js';\nimport { useContainerRef, useLayoutEngine, useWidgetResolver } from './context.js';\nimport { useComponent } from './hooks.js';\n\ninterface WidgetSlotProps {\n\tentityId: EntityId;\n\tslotRef: (entityId: EntityId, el: HTMLDivElement | null) => void;\n}\n\nfunction getMods(e: React.PointerEvent): Modifiers {\n\treturn { shift: e.shiftKey, ctrl: e.ctrlKey, alt: e.altKey, meta: e.metaKey };\n}\n\nexport const WidgetSlot = memo(function WidgetSlot({ entityId, slotRef }: WidgetSlotProps) {\n\tconst wrapperRef = useRef<HTMLDivElement>(null);\n\tconst engine = useLayoutEngine();\n\tconst containerRefObj = useContainerRef();\n\tconst resolve = useWidgetResolver();\n\n\tconst widgetComp = useComponent(entityId, Widget);\n\n\tconst resolved = resolve?.(entityId, widgetComp?.type ?? '');\n\tconst WidgetComponent = resolved?.component ?? null;\n\n\t// Register wrapper ref with the batch updater\n\tuseEffect(() => {\n\t\tslotRef(entityId, wrapperRef.current);\n\t\treturn () => slotRef(entityId, null);\n\t}, [entityId, slotRef]);\n\n\t// Convert clientX/Y to container-relative coords\n\tconst toLocal = useCallback(\n\t\t(e: React.PointerEvent): { x: number; y: number } => {\n\t\t\tconst rect = containerRefObj?.current?.getBoundingClientRect();\n\t\t\tif (!rect) return { x: e.clientX, y: e.clientY };\n\t\t\treturn { x: e.clientX - rect.left, y: e.clientY - rect.top };\n\t\t},\n\t\t[containerRefObj],\n\t);\n\n\t// Fix #3: Capture pointer immediately on pointerdown for any widget interaction.\n\t// This ensures we always get pointerup even if the cursor leaves the window.\n\tconst onPointerDown = useCallback(\n\t\t(e: React.PointerEvent) => {\n\t\t\t// Don't intercept clicks on interactive elements inside widgets\n\t\t\tconst target = e.target as HTMLElement;\n\t\t\tif (target.closest('button, input, textarea, select, [contenteditable]')) {\n\t\t\t\te.stopPropagation(); // still prevent background handler\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst { x, y } = toLocal(e);\n\t\t\tconst directive = engine.handlePointerDown(x, y, e.button, getMods(e));\n\n\t\t\t// Always stop propagation — prevent background handler from double-processing\n\t\t\te.stopPropagation();\n\n\t\t\tif (directive.action === 'capture-resize' || directive.action === 'passthrough-track-drag') {\n\t\t\t\t// Capture pointer for both resize AND tracking (so we get pointerup reliably)\n\t\t\t\twrapperRef.current?.setPointerCapture(e.pointerId);\n\t\t\t}\n\t\t\tif (directive.action === 'capture-resize') {\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t},\n\t\t[engine, toLocal],\n\t);\n\n\tconst capturedRef = useRef(false);\n\n\tconst onPointerMove = useCallback(\n\t\t(e: React.PointerEvent) => {\n\t\t\tconst { x, y } = toLocal(e);\n\t\t\tconst directive = engine.handlePointerMove(x, y, getMods(e));\n\n\t\t\t// Fix #3: Only call setPointerCapture once when drag starts (not every move)\n\t\t\tif (directive.action === 'capture-drag' && !capturedRef.current) {\n\t\t\t\tcapturedRef.current = true;\n\t\t\t\te.stopPropagation();\n\t\t\t}\n\t\t},\n\t\t[engine, toLocal],\n\t);\n\n\tconst onPointerUp = useCallback(\n\t\t(e: React.PointerEvent) => {\n\t\t\te.stopPropagation();\n\t\t\tcapturedRef.current = false;\n\t\t\t// Release pointer capture (browser does this automatically, but be explicit)\n\t\t\tif (wrapperRef.current?.hasPointerCapture(e.pointerId)) {\n\t\t\t\twrapperRef.current.releasePointerCapture(e.pointerId);\n\t\t\t}\n\t\t\tengine.handlePointerUp();\n\t\t},\n\t\t[engine],\n\t);\n\n\tconst onDoubleClick = useCallback(\n\t\t(e: React.MouseEvent) => {\n\t\t\te.stopPropagation();\n\t\t\tengine.enterContainer(entityId);\n\t\t},\n\t\t[engine, entityId],\n\t);\n\n\t// Read WorldBounds at render time for initial inline position.\n\t// This ensures the div has the correct position on its very first paint —\n\t// no flash at (0,0). Subsequent updates come from the batch updater (rAF).\n\tconst wb = engine.get(entityId, WorldBounds);\n\tconst initialStyle: React.CSSProperties = wb\n\t\t? {\n\t\t\t\ttransform: `translate(${wb.worldX}px, ${wb.worldY}px)`,\n\t\t\t\twidth: `${wb.worldWidth}px`,\n\t\t\t\theight: `${wb.worldHeight}px`,\n\t\t\t}\n\t\t: {};\n\n\tconst content = WidgetComponent ? (\n\t\t<WidgetComponent entityId={entityId} />\n\t) : (\n\t\t<div className=\"h-full w-full rounded border border-dashed border-gray-300 bg-gray-50\" />\n\t);\n\n\treturn (\n\t\t<div\n\t\t\tref={wrapperRef}\n\t\t\tdata-widget-slot=\"\"\n\t\t\tclassName=\"absolute left-0 top-0 origin-top-left will-change-transform\"\n\t\t\tstyle={initialStyle}\n\t\t\tonPointerDown={onPointerDown}\n\t\t\tonPointerMove={onPointerMove}\n\t\t\tonPointerUp={onPointerUp}\n\t\t\tonDoubleClick={onDoubleClick}\n\t\t>\n\t\t\t{content}\n\t\t</div>\n\t);\n});\n","import { memo, useCallback, useEffect, useRef } from 'react';\nimport { WorldBounds } from '../components.js';\nimport type { EntityId } from '../ecs/types.js';\nimport type { Modifiers } from '../engine.js';\nimport { useContainerRef, useLayoutEngine } from './context.js';\n\ninterface SelectionOverlaySlotProps {\n\tentityId: EntityId;\n\tslotRef: (entityId: EntityId, el: HTMLDivElement | null) => void;\n}\n\nfunction getMods(e: React.PointerEvent): Modifiers {\n\treturn { shift: e.shiftKey, ctrl: e.ctrlKey, alt: e.altKey, meta: e.metaKey };\n}\n\n/**\n * DOM overlay for WebGL widgets — provides selection frame and pointer\n * interaction (select, drag, resize) without rendering widget content.\n */\nexport const SelectionOverlaySlot = memo(function SelectionOverlaySlot({\n\tentityId,\n\tslotRef,\n}: SelectionOverlaySlotProps) {\n\tconst wrapperRef = useRef<HTMLDivElement>(null);\n\tconst engine = useLayoutEngine();\n\tconst containerRefObj = useContainerRef();\n\n\tuseEffect(() => {\n\t\tslotRef(entityId, wrapperRef.current);\n\t\treturn () => slotRef(entityId, null);\n\t}, [entityId, slotRef]);\n\n\tconst toLocal = useCallback(\n\t\t(e: React.PointerEvent) => {\n\t\t\tconst rect = containerRefObj?.current?.getBoundingClientRect();\n\t\t\tif (!rect) return { x: e.clientX, y: e.clientY };\n\t\t\treturn { x: e.clientX - rect.left, y: e.clientY - rect.top };\n\t\t},\n\t\t[containerRefObj],\n\t);\n\n\tconst capturedRef = useRef(false);\n\n\tconst onPointerDown = useCallback(\n\t\t(e: React.PointerEvent) => {\n\t\t\te.stopPropagation();\n\t\t\tconst { x, y } = toLocal(e);\n\t\t\tconst directive = engine.handlePointerDown(x, y, e.button, getMods(e));\n\t\t\tif (directive.action === 'capture-resize' || directive.action === 'passthrough-track-drag') {\n\t\t\t\twrapperRef.current?.setPointerCapture(e.pointerId);\n\t\t\t}\n\t\t\tif (directive.action === 'capture-resize') {\n\t\t\t\te.preventDefault();\n\t\t\t}\n\t\t},\n\t\t[engine, toLocal],\n\t);\n\n\tconst onPointerMove = useCallback(\n\t\t(e: React.PointerEvent) => {\n\t\t\tconst { x, y } = toLocal(e);\n\t\t\tconst directive = engine.handlePointerMove(x, y, getMods(e));\n\t\t\tif (directive.action === 'capture-drag' && !capturedRef.current) {\n\t\t\t\tcapturedRef.current = true;\n\t\t\t\te.stopPropagation();\n\t\t\t}\n\t\t},\n\t\t[engine, toLocal],\n\t);\n\n\tconst onPointerUp = useCallback(\n\t\t(e: React.PointerEvent) => {\n\t\t\te.stopPropagation();\n\t\t\tcapturedRef.current = false;\n\t\t\tif (wrapperRef.current?.hasPointerCapture(e.pointerId)) {\n\t\t\t\twrapperRef.current.releasePointerCapture(e.pointerId);\n\t\t\t}\n\t\t\tengine.handlePointerUp();\n\t\t},\n\t\t[engine],\n\t);\n\n\tconst onDoubleClick = useCallback(\n\t\t(e: React.MouseEvent) => {\n\t\t\te.stopPropagation();\n\t\t\tengine.enterContainer(entityId);\n\t\t},\n\t\t[engine, entityId],\n\t);\n\n\tconst wb = engine.get(entityId, WorldBounds);\n\tconst initialStyle: React.CSSProperties = wb\n\t\t? {\n\t\t\t\ttransform: `translate(${wb.worldX}px, ${wb.worldY}px)`,\n\t\t\t\twidth: `${wb.worldWidth}px`,\n\t\t\t\theight: `${wb.worldHeight}px`,\n\t\t\t}\n\t\t: {};\n\n\treturn (\n\t\t<div\n\t\t\tref={wrapperRef}\n\t\t\tclassName=\"absolute left-0 top-0 origin-top-left will-change-transform\"\n\t\t\tstyle={initialStyle}\n\t\t\tonPointerDown={onPointerDown}\n\t\t\tonPointerMove={onPointerMove}\n\t\t\tonPointerUp={onPointerUp}\n\t\t\tonDoubleClick={onDoubleClick}\n\t\t/>\n\t);\n});\n","/**\n * Snap guide computation for alignment during drag operations.\n * Implements Figma-style snapping:\n * 1. Edge/center alignment guides\n * 2. Equal spacing snap + indicators\n */\n\nexport interface SnapGuide {\n\t/** Axis this guide aligns on */\n\taxis: 'x' | 'y';\n\t/** World-space coordinate of the alignment line */\n\tposition: number;\n\t/** What kind of alignment */\n\ttype: 'edge' | 'center';\n}\n\nexport interface EqualSpacingIndicator {\n\t/** Axis along which the equal gaps run */\n\taxis: 'x' | 'y';\n\t/** The equal gap value (world units) */\n\tgap: number;\n\t/** Pairs of (from, to) marking each equal gap segment */\n\tsegments: { from: number; to: number }[];\n\t/** Position on the perpendicular axis (for rendering) */\n\tperpPosition: number;\n}\n\nexport interface SnapResult {\n\t/** Snap-corrected delta (world units). Apply to entity position. */\n\tsnapDx: number;\n\tsnapDy: number;\n\t/** Active alignment guide lines to render */\n\tguides: SnapGuide[];\n\t/** Equal spacing indicators */\n\tspacings: EqualSpacingIndicator[];\n}\n\nexport interface EntityBounds {\n\tx: number;\n\ty: number;\n\twidth: number;\n\theight: number;\n}\n\n/**\n * Compute snap guides for a dragged entity against reference entities.\n */\nexport function computeSnapGuides(\n\tdragged: EntityBounds,\n\treferences: EntityBounds[],\n\tthreshold: number,\n): SnapResult {\n\tconst guides: SnapGuide[] = [];\n\tconst spacings: EqualSpacingIndicator[] = [];\n\tlet snapDx = 0;\n\tlet snapDy = 0;\n\n\t// Dragged entity edges and center\n\tconst dLeft = dragged.x;\n\tconst dRight = dragged.x + dragged.width;\n\tconst dCenterX = dragged.x + dragged.width / 2;\n\tconst dTop = dragged.y;\n\tconst dBottom = dragged.y + dragged.height;\n\tconst dCenterY = dragged.y + dragged.height / 2;\n\n\tlet bestSnapX = Number.POSITIVE_INFINITY;\n\tlet bestSnapY = Number.POSITIVE_INFINITY;\n\tlet bestDx = 0;\n\tlet bestDy = 0;\n\tconst xGuides: SnapGuide[] = [];\n\tconst yGuides: SnapGuide[] = [];\n\n\t// --- Phase 1: Edge/center alignment ---\n\n\tfor (const ref of references) {\n\t\tconst rLeft = ref.x;\n\t\tconst rRight = ref.x + ref.width;\n\t\tconst rCenterX = ref.x + ref.width / 2;\n\t\tconst rTop = ref.y;\n\t\tconst rBottom = ref.y + ref.height;\n\t\tconst rCenterY = ref.y + ref.height / 2;\n\n\t\t// X-axis alignment (vertical guide lines)\n\t\tconst xPairs: [number, number, 'edge' | 'center'][] = [\n\t\t\t[dLeft, rLeft, 'edge'],\n\t\t\t[dLeft, rRight, 'edge'],\n\t\t\t[dRight, rLeft, 'edge'],\n\t\t\t[dRight, rRight, 'edge'],\n\t\t\t[dCenterX, rCenterX, 'center'],\n\t\t\t[dLeft, rCenterX, 'edge'],\n\t\t\t[dRight, rCenterX, 'edge'],\n\t\t];\n\n\t\tfor (const [dVal, rVal, type] of xPairs) {\n\t\t\tconst dist = Math.abs(dVal - rVal);\n\t\t\tif (dist <= threshold) {\n\t\t\t\tconst dx = rVal - dVal;\n\t\t\t\tif (dist < bestSnapX) {\n\t\t\t\t\tbestSnapX = dist;\n\t\t\t\t\tbestDx = dx;\n\t\t\t\t\txGuides.length = 0;\n\t\t\t\t}\n\t\t\t\tif (dist <= bestSnapX + 0.01) {\n\t\t\t\t\txGuides.push({ axis: 'x', position: rVal, type });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Y-axis alignment (horizontal guide lines)\n\t\tconst yPairs: [number, number, 'edge' | 'center'][] = [\n\t\t\t[dTop, rTop, 'edge'],\n\t\t\t[dTop, rBottom, 'edge'],\n\t\t\t[dBottom, rTop, 'edge'],\n\t\t\t[dBottom, rBottom, 'edge'],\n\t\t\t[dCenterY, rCenterY, 'center'],\n\t\t\t[dTop, rCenterY, 'edge'],\n\t\t\t[dBottom, rCenterY, 'edge'],\n\t\t];\n\n\t\tfor (const [dVal, rVal, type] of yPairs) {\n\t\t\tconst dist = Math.abs(dVal - rVal);\n\t\t\tif (dist <= threshold) {\n\t\t\t\tconst dy = rVal - dVal;\n\t\t\t\tif (dist < bestSnapY) {\n\t\t\t\t\tbestSnapY = dist;\n\t\t\t\t\tbestDy = dy;\n\t\t\t\t\tyGuides.length = 0;\n\t\t\t\t}\n\t\t\t\tif (dist <= bestSnapY + 0.01) {\n\t\t\t\t\tyGuides.push({ axis: 'y', position: rVal, type });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// --- Phase 2: Equal spacing snap ---\n\t// Check if we can place the dragged entity so that the gap to its\n\t// left and right (or top and bottom) neighbors are equal.\n\n\tconst eqResult = computeEqualSpacing(dragged, references, threshold);\n\n\t// Merge: alignment snap takes priority, equal spacing fills in the other axis\n\tif (bestSnapX <= threshold) {\n\t\tsnapDx = bestDx;\n\t} else if (eqResult.snapDx !== undefined) {\n\t\tsnapDx = eqResult.snapDx;\n\t}\n\tif (bestSnapY <= threshold) {\n\t\tsnapDy = bestDy;\n\t} else if (eqResult.snapDy !== undefined) {\n\t\tsnapDy = eqResult.snapDy;\n\t}\n\n\t// Collect alignment guides\n\tif (bestSnapX <= threshold) {\n\t\tconst seen = new Set<number>();\n\t\tfor (const g of xGuides) {\n\t\t\tif (!seen.has(g.position)) {\n\t\t\t\tseen.add(g.position);\n\t\t\t\tguides.push(g);\n\t\t\t}\n\t\t}\n\t}\n\tif (bestSnapY <= threshold) {\n\t\tconst seen = new Set<number>();\n\t\tfor (const g of yGuides) {\n\t\t\tif (!seen.has(g.position)) {\n\t\t\t\tseen.add(g.position);\n\t\t\t\tguides.push(g);\n\t\t\t}\n\t\t}\n\t}\n\n\t// Collect equal spacing indicators (after applying snap)\n\tconst snappedBounds: EntityBounds = {\n\t\tx: dragged.x + snapDx,\n\t\ty: dragged.y + snapDy,\n\t\twidth: dragged.width,\n\t\theight: dragged.height,\n\t};\n\tconst eqFinal = computeEqualSpacing(snappedBounds, references, threshold * 0.5);\n\tspacings.push(...eqFinal.indicators);\n\n\treturn { snapDx, snapDy, guides, spacings };\n}\n\n// --- Equal spacing computation ---\n\ninterface EqualSpacingResult {\n\tsnapDx?: number;\n\tsnapDy?: number;\n\tindicators: EqualSpacingIndicator[];\n}\n\nfunction computeEqualSpacing(\n\tdragged: EntityBounds,\n\treferences: EntityBounds[],\n\tthreshold: number,\n): EqualSpacingResult {\n\tconst indicators: EqualSpacingIndicator[] = [];\n\tlet snapDx: number | undefined;\n\tlet snapDy: number | undefined;\n\n\t// Check X-axis (horizontal spacing)\n\tconst xResult = checkAxisSpacing(dragged, references, threshold, 'x');\n\tif (xResult) {\n\t\tsnapDx = xResult.snap;\n\t\tindicators.push(...xResult.indicators);\n\t}\n\n\t// Check Y-axis (vertical spacing)\n\tconst yResult = checkAxisSpacing(dragged, references, threshold, 'y');\n\tif (yResult) {\n\t\tsnapDy = yResult.snap;\n\t\tindicators.push(...yResult.indicators);\n\t}\n\n\treturn { snapDx, snapDy, indicators };\n}\n\nfunction checkAxisSpacing(\n\tdragged: EntityBounds,\n\treferences: EntityBounds[],\n\tthreshold: number,\n\taxis: 'x' | 'y',\n): { snap: number; indicators: EqualSpacingIndicator[] } | null {\n\tconst isX = axis === 'x';\n\n\t// Get position/size accessors based on axis\n\tconst pos = (b: EntityBounds) => (isX ? b.x : b.y);\n\tconst size = (b: EntityBounds) => (isX ? b.width : b.height);\n\tconst perpPos = (b: EntityBounds) => (isX ? b.y : b.x);\n\tconst perpSize = (b: EntityBounds) => (isX ? b.height : b.width);\n\tconst end = (b: EntityBounds) => pos(b) + size(b);\n\n\t// Filter to entities on the same perpendicular band (overlapping)\n\tconst neighbors = references.filter(\n\t\t(ref) =>\n\t\t\tperpPos(ref) < perpPos(dragged) + perpSize(dragged) &&\n\t\t\tperpPos(ref) + perpSize(ref) > perpPos(dragged),\n\t);\n\n\tif (neighbors.length < 1) return null;\n\n\t// Sort neighbors by position on this axis\n\tconst sorted = [...neighbors].sort((a, b) => pos(a) - pos(b));\n\n\t// Find existing gaps between consecutive reference entities\n\tconst refGaps: { from: EntityBounds; to: EntityBounds; gap: number }[] = [];\n\tfor (let i = 0; i < sorted.length - 1; i++) {\n\t\tconst gap = pos(sorted[i + 1]) - end(sorted[i]);\n\t\tif (gap > 0.1) {\n\t\t\trefGaps.push({ from: sorted[i], to: sorted[i + 1], gap });\n\t\t}\n\t}\n\n\tlet bestSnap: number | null = null;\n\tlet bestIndicators: EqualSpacingIndicator[] = [];\n\tlet bestDiff = Number.POSITIVE_INFINITY;\n\n\t// Case 1: Between two neighbors (left gap ≈ right gap)\n\t// Find the nearest left and right neighbors\n\tlet leftN: EntityBounds | null = null;\n\tlet rightN: EntityBounds | null = null;\n\tfor (const ref of sorted) {\n\t\tif (end(ref) <= pos(dragged) + threshold) {\n\t\t\tif (!leftN || end(ref) > end(leftN)) leftN = ref;\n\t\t}\n\t\tif (pos(ref) >= end(dragged) - threshold) {\n\t\t\tif (!rightN || pos(ref) < pos(rightN)) rightN = ref;\n\t\t}\n\t}\n\n\tif (leftN && rightN) {\n\t\tconst lGap = pos(dragged) - end(leftN);\n\t\tconst rGap = pos(rightN) - end(dragged);\n\t\tconst diff = Math.abs(lGap - rGap);\n\t\tif (diff <= threshold && diff < bestDiff) {\n\t\t\tconst idealPos = (end(leftN) + pos(rightN) - size(dragged)) / 2;\n\t\t\tconst snap = idealPos - pos(dragged);\n\t\t\tconst equalGap = (pos(rightN) - end(leftN) - size(dragged)) / 2;\n\t\t\tif (equalGap > 0.1) {\n\t\t\t\tconst perpY = computePerpCenter(dragged, [leftN, rightN], isX);\n\t\t\t\tbestSnap = snap;\n\t\t\t\tbestDiff = diff;\n\t\t\t\tbestIndicators = [\n\t\t\t\t\t{\n\t\t\t\t\t\taxis,\n\t\t\t\t\t\tgap: equalGap,\n\t\t\t\t\t\tsegments: [\n\t\t\t\t\t\t\t{ from: end(leftN), to: idealPos },\n\t\t\t\t\t\t\t{ from: idealPos + size(dragged), to: pos(rightN) },\n\t\t\t\t\t\t],\n\t\t\t\t\t\tperpPosition: perpY,\n\t\t\t\t\t},\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\t}\n\n\t// Case 2: Extend pattern — dragged at the end or beginning of a row\n\t// Find gaps in the existing reference layout and try to match\n\tfor (const refGap of refGaps) {\n\t\tconst patternGap = refGap.gap;\n\n\t\t// Try placing dragged to the right of the rightmost entity in this pattern\n\t\tif (rightN === null || pos(refGap.to) >= end(dragged) - threshold * 2) {\n\t\t\t// Find the rightmost entity in the chain with this gap\n\t\t\tconst chainEnd = refGap.to;\n\t\t\t// Check: dragged gap to chainEnd matches patternGap?\n\t\t\tconst dragGap = pos(dragged) - end(chainEnd);\n\t\t\tconst diff = Math.abs(dragGap - patternGap);\n\t\t\tif (diff <= threshold && diff < bestDiff) {\n\t\t\t\tconst idealPos = end(chainEnd) + patternGap;\n\t\t\t\tconst snap = idealPos - pos(dragged);\n\t\t\t\tconst perpY = computePerpCenter(dragged, [refGap.from, refGap.to], isX);\n\t\t\t\tbestSnap = snap;\n\t\t\t\tbestDiff = diff;\n\t\t\t\t// Show all equal gaps: the existing one + the new one\n\t\t\t\tbestIndicators = [\n\t\t\t\t\t{\n\t\t\t\t\t\taxis,\n\t\t\t\t\t\tgap: patternGap,\n\t\t\t\t\t\tsegments: [\n\t\t\t\t\t\t\t{ from: end(refGap.from), to: pos(refGap.to) },\n\t\t\t\t\t\t\t{ from: end(chainEnd), to: idealPos },\n\t\t\t\t\t\t],\n\t\t\t\t\t\tperpPosition: perpY,\n\t\t\t\t\t},\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\n\t\t// Try placing dragged to the left of the leftmost entity in this pattern\n\t\tif (leftN === null || end(refGap.from) <= pos(dragged) + threshold * 2) {\n\t\t\tconst chainStart = refGap.from;\n\t\t\tconst dragGap = pos(chainStart) - end(dragged);\n\t\t\tconst diff = Math.abs(dragGap - patternGap);\n\t\t\tif (diff <= threshold && diff < bestDiff) {\n\t\t\t\tconst idealPos = pos(chainStart) - patternGap - size(dragged);\n\t\t\t\tconst snap = idealPos - pos(dragged);\n\t\t\t\tconst perpY = computePerpCenter(dragged, [refGap.from, refGap.to], isX);\n\t\t\t\tbestSnap = snap;\n\t\t\t\tbestDiff = diff;\n\t\t\t\tbestIndicators = [\n\t\t\t\t\t{\n\t\t\t\t\t\taxis,\n\t\t\t\t\t\tgap: patternGap,\n\t\t\t\t\t\tsegments: [\n\t\t\t\t\t\t\t{ from: idealPos + size(dragged), to: pos(chainStart) },\n\t\t\t\t\t\t\t{ from: end(refGap.from), to: pos(refGap.to) },\n\t\t\t\t\t\t],\n\t\t\t\t\t\tperpPosition: perpY,\n\t\t\t\t\t},\n\t\t\t\t];\n\t\t\t}\n\t\t}\n\t}\n\n\tif (bestSnap !== null) {\n\t\treturn { snap: bestSnap, indicators: bestIndicators };\n\t}\n\treturn null;\n}\n\nfunction computePerpCenter(dragged: EntityBounds, refs: EntityBounds[], isX: boolean): number {\n\tconst perpPos = (b: EntityBounds) => (isX ? b.y : b.x);\n\tconst perpSize = (b: EntityBounds) => (isX ? b.height : b.width);\n\tconst allBounds = [dragged, ...refs];\n\tconst maxStart = Math.max(...allBounds.map(perpPos));\n\tconst minEnd = Math.min(...allBounds.map((b) => perpPos(b) + perpSize(b)));\n\treturn maxStart + (minEnd - maxStart) / 2;\n}\n","/**\n * Performance profiler with User Timing API integration.\n * Zero-cost when disabled — all methods are no-ops.\n * When enabled, data shows in Chrome DevTools Performance tab.\n */\n\nexport interface FrameSample {\n\ttick: number;\n\ttimestamp: number;\n\t/** Total tick duration (ms) */\n\ttotalMs: number;\n\t/** Per-system durations (ms) */\n\tsystems: Record<string, number>;\n\t/** Visible entity computation (ms) */\n\tvisibilityMs: number;\n\t/** Entity counts at this frame */\n\tentityCount: number;\n\tvisibleCount: number;\n}\n\nexport interface ProfilerStats {\n\t/** Frames per second (based on tick rate, not rAF) */\n\tfps: number;\n\t/** Frame time stats (ms) */\n\tframeTime: { avg: number; p50: number; p95: number; p99: number; max: number };\n\t/** Per-system average time (ms) */\n\tsystemAvg: Record<string, number>;\n\t/** Per-system p95 time (ms) */\n\tsystemP95: Record<string, number>;\n\t/** Frame budget utilization at 60fps (%) */\n\tbudgetUsed: number;\n\t/** Total samples in buffer */\n\tsampleCount: number;\n}\n\nconst RING_SIZE = 300; // ~5 seconds at 60fps\n\nexport class Profiler {\n\tprivate enabled = false;\n\tprivate ring: FrameSample[] = [];\n\tprivate writeIdx = 0;\n\tprivate filled = false;\n\n\t// Scratch state for current frame\n\tprivate frameStart = 0;\n\tprivate currentSystems: Record<string, number> = {};\n\tprivate visibilityMs = 0;\n\tprivate currentTick = 0;\n\n\t/** Enable/disable profiling. When disabled, all methods are no-ops. */\n\tsetEnabled(on: boolean) {\n\t\tthis.enabled = on;\n\t\tif (!on) {\n\t\t\tthis.ring = [];\n\t\t\tthis.writeIdx = 0;\n\t\t\tthis.filled = false;\n\t\t}\n\t}\n\n\tisEnabled(): boolean {\n\t\treturn this.enabled;\n\t}\n\n\t/** Call at the start of engine.tick() */\n\tbeginFrame(tick: number) {\n\t\tif (!this.enabled) return;\n\t\tthis.currentTick = tick;\n\t\tthis.currentSystems = {};\n\t\tthis.visibilityMs = 0;\n\t\tthis.frameStart = performance.now();\n\t\tperformance.mark('ic-frame-start');\n\t}\n\n\t/** Call around each system execution */\n\tbeginSystem(name: string) {\n\t\tif (!this.enabled) return;\n\t\tperformance.mark(`ic-sys-${name}-start`);\n\t}\n\n\tendSystem(name: string) {\n\t\tif (!this.enabled) return;\n\t\tperformance.mark(`ic-sys-${name}-end`);\n\t\ttry {\n\t\t\tconst measure = performance.measure(\n\t\t\t\t`ic:${name}`,\n\t\t\t\t`ic-sys-${name}-start`,\n\t\t\t\t`ic-sys-${name}-end`,\n\t\t\t);\n\t\t\tthis.currentSystems[name] = measure.duration;\n\t\t} catch {\n\t\t\t// marks may be cleared\n\t\t}\n\t\tperformance.clearMarks(`ic-sys-${name}-start`);\n\t\tperformance.clearMarks(`ic-sys-${name}-end`);\n\t}\n\n\t/** Call around the visibility computation phase */\n\tbeginVisibility() {\n\t\tif (!this.enabled) return;\n\t\tperformance.mark('ic-vis-start');\n\t}\n\n\tendVisibility() {\n\t\tif (!this.enabled) return;\n\t\tperformance.mark('ic-vis-end');\n\t\ttry {\n\t\t\tconst measure = performance.measure('ic:visibility', 'ic-vis-start', 'ic-vis-end');\n\t\t\tthis.visibilityMs = measure.duration;\n\t\t} catch {\n\t\t\t// marks may be cleared\n\t\t}\n\t\tperformance.clearMarks('ic-vis-start');\n\t\tperformance.clearMarks('ic-vis-end');\n\t}\n\n\t/** Call at the end of engine.tick() */\n\tendFrame(entityCount: number, visibleCount: number) {\n\t\tif (!this.enabled) return;\n\t\tperformance.mark('ic-frame-end');\n\n\t\tlet totalMs = 0;\n\t\ttry {\n\t\t\tconst measure = performance.measure('ic:frame', 'ic-frame-start', 'ic-frame-end');\n\t\t\ttotalMs = measure.duration;\n\t\t} catch {\n\t\t\ttotalMs = performance.now() - this.frameStart;\n\t\t}\n\t\tperformance.clearMarks('ic-frame-start');\n\t\tperformance.clearMarks('ic-frame-end');\n\n\t\tconst sample: FrameSample = {\n\t\t\ttick: this.currentTick,\n\t\t\ttimestamp: performance.now(),\n\t\t\ttotalMs,\n\t\t\tsystems: { ...this.currentSystems },\n\t\t\tvisibilityMs: this.visibilityMs,\n\t\t\tentityCount,\n\t\t\tvisibleCount,\n\t\t};\n\n\t\t// Write to ring buffer\n\t\tif (this.ring.length < RING_SIZE) {\n\t\t\tthis.ring.push(sample);\n\t\t} else {\n\t\t\tthis.ring[this.writeIdx] = sample;\n\t\t}\n\t\tthis.writeIdx = (this.writeIdx + 1) % RING_SIZE;\n\t\tif (this.ring.length >= RING_SIZE) this.filled = true;\n\t}\n\n\t/** Get the last N frame samples (newest first) */\n\tgetSamples(count?: number): FrameSample[] {\n\t\tconst n = Math.min(count ?? this.ring.length, this.ring.length);\n\t\tconst result: FrameSample[] = [];\n\t\tfor (let i = 0; i < n; i++) {\n\t\t\tconst idx = (this.writeIdx - 1 - i + this.ring.length) % this.ring.length;\n\t\t\tif (idx >= 0 && idx < this.ring.length) {\n\t\t\t\tresult.push(this.ring[idx]);\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}\n\n\t/** Compute rolling statistics from the ring buffer */\n\tgetStats(): ProfilerStats {\n\t\tconst samples = this.ring;\n\t\tconst n = samples.length;\n\n\t\tif (n === 0) {\n\t\t\treturn {\n\t\t\t\tfps: 0,\n\t\t\t\tframeTime: { avg: 0, p50: 0, p95: 0, p99: 0, max: 0 },\n\t\t\t\tsystemAvg: {},\n\t\t\t\tsystemP95: {},\n\t\t\t\tbudgetUsed: 0,\n\t\t\t\tsampleCount: 0,\n\t\t\t};\n\t\t}\n\n\t\t// Frame times sorted for percentiles\n\t\tconst frameTimes = samples.map((s) => s.totalMs).sort((a, b) => a - b);\n\n\t\t// FPS from time span\n\t\tconst newest = samples[this.filled ? (this.writeIdx - 1 + RING_SIZE) % RING_SIZE : n - 1];\n\t\tconst oldest = samples[this.filled ? this.writeIdx : 0];\n\t\tconst spanMs = newest.timestamp - oldest.timestamp;\n\t\tconst fps = spanMs > 0 ? Math.round(((n - 1) / spanMs) * 1000) : 0;\n\n\t\t// Percentiles\n\t\tconst percentile = (sorted: number[], p: number) => {\n\t\t\tconst idx = Math.floor((p / 100) * (sorted.length - 1));\n\t\t\treturn sorted[idx] ?? 0;\n\t\t};\n\n\t\tconst avg = frameTimes.reduce((a, b) => a + b, 0) / n;\n\n\t\t// Per-system stats\n\t\tconst systemNames = new Set<string>();\n\t\tfor (const s of samples) {\n\t\t\tfor (const name of Object.keys(s.systems)) systemNames.add(name);\n\t\t}\n\n\t\tconst systemAvg: Record<string, number> = {};\n\t\tconst systemP95: Record<string, number> = {};\n\t\tfor (const name of systemNames) {\n\t\t\tconst times = samples.map((s) => s.systems[name] ?? 0).sort((a, b) => a - b);\n\t\t\tsystemAvg[name] = times.reduce((a, b) => a + b, 0) / n;\n\t\t\tsystemP95[name] = percentile(times, 95);\n\t\t}\n\n\t\treturn {\n\t\t\tfps,\n\t\t\tframeTime: {\n\t\t\t\tavg,\n\t\t\t\tp50: percentile(frameTimes, 50),\n\t\t\t\tp95: percentile(frameTimes, 95),\n\t\t\t\tp99: percentile(frameTimes, 99),\n\t\t\t\tmax: frameTimes[frameTimes.length - 1],\n\t\t\t},\n\t\t\tsystemAvg,\n\t\t\tsystemP95,\n\t\t\tbudgetUsed: (avg / 16.67) * 100,\n\t\t\tsampleCount: n,\n\t\t};\n\t}\n\n\t/** Clear all collected data */\n\tclear() {\n\t\tthis.ring = [];\n\t\tthis.writeIdx = 0;\n\t\tthis.filled = false;\n\t}\n}\n","import RBushImport from 'rbush';\n// Handle CJS/ESM interop — rbush exports differently depending on context\nconst RBush = (\n\ttypeof (RBushImport as any).default === 'function' ? (RBushImport as any).default : RBushImport\n) as typeof RBushImport;\nimport type { EntityId } from './ecs/index.js';\nimport type { AABB } from './math.js';\n\nexport interface SpatialEntry extends AABB {\n\tentityId: EntityId;\n}\n\n/**\n * Spatial index backed by an R-tree (rbush).\n * Stores world-space AABBs for fast viewport culling and hit testing.\n */\nexport class SpatialIndex {\n\tprivate tree = new RBush<SpatialEntry>();\n\tprivate entries = new Map<EntityId, SpatialEntry>();\n\n\tupsert(entityId: EntityId, bounds: AABB) {\n\t\tconst existing = this.entries.get(entityId);\n\t\tif (existing) {\n\t\t\t// Fix #8: Pass known reference directly — O(log n) instead of O(n) comparator scan\n\t\t\tthis.tree.remove(existing);\n\t\t}\n\t\tconst entry: SpatialEntry = { ...bounds, entityId };\n\t\tthis.entries.set(entityId, entry);\n\t\tthis.tree.insert(entry);\n\t}\n\n\tremove(entityId: EntityId) {\n\t\tconst existing = this.entries.get(entityId);\n\t\tif (existing) {\n\t\t\tthis.tree.remove(existing);\n\t\t\tthis.entries.delete(entityId);\n\t\t}\n\t}\n\n\t/** Query all entries intersecting the given AABB */\n\tsearch(bounds: AABB): SpatialEntry[] {\n\t\treturn this.tree.search(bounds);\n\t}\n\n\t/** Find the topmost entity at a point (by z-order — caller sorts) */\n\tsearchPoint(x: number, y: number, tolerance = 0): SpatialEntry[] {\n\t\treturn this.tree.search({\n\t\t\tminX: x - tolerance,\n\t\t\tminY: y - tolerance,\n\t\t\tmaxX: x + tolerance,\n\t\t\tmaxY: y + tolerance,\n\t\t});\n\t}\n\n\tclear() {\n\t\tthis.tree.clear();\n\t\tthis.entries.clear();\n\t}\n\n\tget size(): number {\n\t\treturn this.entries.size;\n\t}\n}\n"],"mappings":";;;;;;AAAA,YAAY,WAAW;AAqBhB,IAAM,sBAAkC;AAAA,EAC9C,UAAU,CAAC,GAAG,IAAI,GAAG;AAAA,EACrB,UAAU,CAAC,GAAG,GAAG,CAAC;AAAA,EAClB,UAAU;AAAA,EACV,QAAQ,CAAC,GAAG,EAAE;AAAA,EACd,SAAS,CAAC,KAAK,GAAG;AAAA,EAClB,WAAW,CAAC,KAAK,GAAG;AAAA,EACpB,aAAa,CAAC,GAAK,GAAG;AACvB;AAIA,IAAM;AAAA;AAAA,EAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAMhC,IAAM;AAAA;AAAA,EAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAkE3B,IAAM,eAAN,MAAmB;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,YAAY,QAA2B;AACtC,SAAK,WAAW,IAAU,oBAAc;AAAA,MACvC;AAAA,MACA,OAAO;AAAA,MACP,WAAW;AAAA,MACX,oBAAoB;AAAA,IACrB,CAAC;AACD,SAAK,SAAS,cAAc,GAAU,CAAC;AAEvC,SAAK,QAAQ,IAAU,YAAM;AAC7B,SAAK,SAAS,IAAU,yBAAmB,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC;AAE7D,SAAK,WAAW,IAAU,qBAAe;AAAA,MACxC;AAAA,MACA;AAAA,MACA,UAAU;AAAA,QACT,cAAc,EAAE,OAAO,IAAU,cAAQ,GAAG,CAAC,EAAE;AAAA,QAC/C,UAAU,EAAE,OAAO,IAAU,cAAQ,GAAG,CAAC,EAAE;AAAA,QAC3C,QAAQ,EAAE,OAAO,EAAE;AAAA,QACnB,OAAO,EAAE,OAAO,EAAE;AAAA,QAClB,YAAY,EAAE,OAAO,IAAU,cAAQ,GAAG,IAAI,GAAG,EAAE;AAAA,QACnD,YAAY,EAAE,OAAO,IAAU,cAAQ,GAAG,GAAG,CAAC,EAAE;AAAA,QAChD,YAAY,EAAE,OAAO,KAAK;AAAA,QAC1B,UAAU,EAAE,OAAO,IAAU,cAAQ,GAAG,EAAE,EAAE;AAAA,QAC5C,WAAW,EAAE,OAAO,IAAU,cAAQ,KAAK,GAAG,EAAE;AAAA,QAChD,aAAa,EAAE,OAAO,IAAU,cAAQ,KAAK,GAAG,EAAE;AAAA,QAClD,eAAe,EAAE,OAAO,IAAU,cAAQ,GAAK,GAAG,EAAE;AAAA,MACrD;AAAA,MACA,aAAa;AAAA,MACb,WAAW;AAAA,MACX,YAAY;AAAA,IACb,CAAC;AAGD,UAAM,WAAW,IAAU,qBAAe;AAC1C,UAAM,WAAW,IAAI,aAAa,CAAC,IAAI,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC;AACjE,aAAS,aAAa,YAAY,IAAU,sBAAgB,UAAU,CAAC,CAAC;AAExE,SAAK,OAAO,IAAU,WAAK,UAAU,KAAK,QAAQ;AAClD,SAAK,MAAM,IAAI,KAAK,IAAI;AAAA,EACzB;AAAA;AAAA,EAGA,UAAU,QAA6B;AACtC,UAAM,IAAI,KAAK,SAAS;AACxB,QAAI,OAAO,SAAU,GAAE,WAAW,MAAM,IAAI,GAAG,OAAO,QAAQ;AAC9D,QAAI,OAAO,SAAU,GAAE,WAAW,MAAM,IAAI,GAAG,OAAO,QAAQ;AAC9D,QAAI,OAAO,aAAa,OAAW,GAAE,WAAW,QAAQ,OAAO;AAC/D,QAAI,OAAO,OAAQ,GAAE,SAAS,MAAM,IAAI,GAAG,OAAO,MAAM;AACxD,QAAI,OAAO,QAAS,GAAE,UAAU,MAAM,IAAI,GAAG,OAAO,OAAO;AAC3D,QAAI,OAAO,UAAW,GAAE,YAAY,MAAM,IAAI,GAAG,OAAO,SAAS;AACjE,QAAI,OAAO,YAAa,GAAE,cAAc,MAAM,IAAI,GAAG,OAAO,WAAW;AAAA,EACxE;AAAA,EAEA,QAAQ,OAAe,QAAgB,MAAM,GAAG;AAC/C,SAAK,SAAS,QAAQ,OAAO,QAAQ,KAAK;AAC1C,SAAK,SAAS,cAAc,GAAG;AAC/B,UAAM,IAAI,KAAK,SAAS;AACxB,MAAE,aAAa,MAAM,IAAI,QAAQ,KAAK,SAAS,GAAG;AAClD,MAAE,MAAM,QAAQ;AAAA,EACjB;AAAA,EAEA,OAAO,SAAiB,SAAiB,MAAc;AACtD,UAAM,IAAI,KAAK,SAAS;AACxB,MAAE,SAAS,MAAM,IAAI,SAAS,OAAO;AACrC,MAAE,OAAO,QAAQ;AACjB,SAAK,SAAS,OAAO,KAAK,OAAO,KAAK,MAAM;AAAA,EAC7C;AAAA,EAEA,UAAU;AACT,SAAK,KAAK,SAAS,QAAQ;AAC3B,SAAK,SAAS,QAAQ;AACtB,SAAK,SAAS,QAAQ;AAAA,EACvB;AAAA;AAAA,EAGA,mBAAwC;AACvC,WAAO,KAAK;AAAA,EACb;AACD;;;AC/LA,YAAYA,YAAW;AA0BhB,IAAM,2BAA4C;AAAA,EACxD,cAAc,CAAC,OAAO,KAAK,CAAG;AAAA;AAAA,EAC9B,cAAc;AAAA,EACd,YAAY,CAAC,OAAO,KAAK,CAAG;AAAA,EAC5B,YAAY;AAAA,EACZ,YAAY;AAAA,EACZ,YAAY,CAAC,GAAG,GAAG,CAAC;AAAA,EACpB,cAAc,CAAC,OAAO,KAAK,CAAG;AAAA,EAC9B,mBAAmB;AAAA,EACnB,WAAW;AACZ;AAaA,IAAM,eAAe;AAErB,IAAMC;AAAA;AAAA,EAA0B;AAAA;AAAA;AAAA;AAAA;AAAA;AAMhC,IAAMC;AAAA;AAAA,EAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,wBAUV,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uCAoDG,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,uBAW5B,YAAY;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgI5B,IAAM,oBAAN,MAAwB;AAAA,EACtB;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EAER,cAAc;AACb,SAAK,QAAQ,IAAU,aAAM;AAC7B,SAAK,SAAS,IAAU,0BAAmB,IAAI,GAAG,GAAG,IAAI,GAAG,CAAC;AAE7D,UAAM,gBAAgB,CAAC;AACvB,aAAS,IAAI,GAAG,IAAI,cAAc,KAAK;AACtC,oBAAc,KAAK,IAAU,eAAQ,GAAG,GAAG,GAAG,CAAC,CAAC;AAAA,IACjD;AAEA,SAAK,WAAW,IAAU,sBAAe;AAAA,MACxC,cAAAD;AAAA,MACA,gBAAAC;AAAA,MACA,UAAU;AAAA,QACT,cAAc,EAAE,OAAO,IAAU,eAAQ,GAAG,CAAC,EAAE;AAAA,QAC/C,UAAU,EAAE,OAAO,IAAU,eAAQ,GAAG,CAAC,EAAE;AAAA,QAC3C,QAAQ,EAAE,OAAO,EAAE;AAAA,QACnB,OAAO,EAAE,OAAO,EAAE;AAAA,QAClB,SAAS,EAAE,OAAO,EAAE;AAAA,QACpB,UAAU,EAAE,OAAO,cAAc;AAAA,QACjC,YAAY,EAAE,OAAO,GAAG;AAAA,QACxB,eAAe,EAAE,OAAO,IAAU,eAAQ,GAAG,GAAG,GAAG,CAAC,EAAE;AAAA,QACtD,YAAY,EAAE,OAAO,EAAE;AAAA;AAAA,QAEvB,gBAAgB,EAAE,OAAO,IAAU,eAAQ,GAAG,yBAAyB,YAAY,EAAE;AAAA,QACrF,gBAAgB,EAAE,OAAO,yBAAyB,aAAa;AAAA,QAC/D,cAAc,EAAE,OAAO,IAAU,eAAQ,GAAG,yBAAyB,UAAU,EAAE;AAAA,QACjF,cAAc,EAAE,OAAO,yBAAyB,WAAW;AAAA,QAC3D,cAAc,EAAE,OAAO,yBAAyB,WAAW;AAAA,QAC3D,cAAc,EAAE,OAAO,IAAU,eAAQ,GAAG,yBAAyB,UAAU,EAAE;AAAA,QACjF,gBAAgB,EAAE,OAAO,IAAU,eAAQ,GAAG,yBAAyB,YAAY,EAAE;AAAA,QACrF,qBAAqB,EAAE,OAAO,yBAAyB,kBAAkB;AAAA,QACzE,aAAa,EAAE,OAAO,yBAAyB,UAAU;AAAA;AAAA,QAEzD,cAAc,EAAE,OAAO,EAAE;AAAA,QACzB,UAAU,EAAE,OAAO,MAAM,KAAK,EAAE,QAAQ,GAAG,GAAG,MAAM,IAAU,eAAQ,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE;AAAA,QACnF,gBAAgB,EAAE,OAAO,EAAE;AAAA,QAC3B,YAAY,EAAE,OAAO,MAAM,KAAK,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAU,eAAQ,GAAG,GAAG,GAAG,CAAC,CAAC,EAAE;AAAA,QACpF,cAAc,EAAE,OAAO,IAAU,eAAQ,GAAK,GAAK,IAAI,EAAE;AAAA;AAAA,MAC1D;AAAA,MACA,aAAa;AAAA,MACb,WAAW;AAAA,MACX,YAAY;AAAA,IACb,CAAC;AAED,UAAM,WAAW,IAAU,sBAAe;AAC1C,UAAM,WAAW,IAAI,aAAa,CAAC,IAAI,IAAI,GAAG,GAAG,IAAI,GAAG,IAAI,GAAG,CAAC,CAAC;AACjE,aAAS,aAAa,YAAY,IAAU,uBAAgB,UAAU,CAAC,CAAC;AAExE,SAAK,OAAO,IAAU,YAAK,UAAU,KAAK,QAAQ;AAClD,SAAK,MAAM,IAAI,KAAK,IAAI;AAAA,EACzB;AAAA,EAEA,UAAU,QAAkC;AAC3C,UAAM,IAAI,KAAK,SAAS;AACxB,QAAI,OAAO,aAAc,GAAE,eAAe,MAAM,IAAI,GAAG,OAAO,YAAY;AAC1E,QAAI,OAAO,iBAAiB,OAAW,GAAE,eAAe,QAAQ,OAAO;AACvE,QAAI,OAAO,WAAY,GAAE,aAAa,MAAM,IAAI,GAAG,OAAO,UAAU;AACpE,QAAI,OAAO,eAAe,OAAW,GAAE,aAAa,QAAQ,OAAO;AACnE,QAAI,OAAO,eAAe,OAAW,GAAE,aAAa,QAAQ,OAAO;AACnE,QAAI,OAAO,WAAY,GAAE,aAAa,MAAM,IAAI,GAAG,OAAO,UAAU;AACpE,QAAI,OAAO,aAAc,GAAE,eAAe,MAAM,IAAI,GAAG,OAAO,YAAY;AAC1E,QAAI,OAAO,sBAAsB;AAChC,QAAE,oBAAoB,QAAQ,OAAO;AACtC,QAAI,OAAO,cAAc,OAAW,GAAE,YAAY,QAAQ,OAAO;AAAA,EAClE;AAAA,EAEA,QAAQ,YAA2B,KAAa;AAC/C,SAAK,SAAS,SAAS,aAAa,MAAM,KAAK,UAAU;AACzD,SAAK,SAAS,SAAS,MAAM,QAAQ;AAAA,EACtC;AAAA,EAEA,OACC,UACA,SACA,SACA,MACA,UACA,SACA,SAAsB,CAAC,GACvB,WAAoC,CAAC,GACpC;AACD,UAAM,IAAI,KAAK,SAAS;AACxB,MAAE,SAAS,MAAM,IAAI,SAAS,OAAO;AACrC,MAAE,OAAO,QAAQ;AAGjB,UAAM,QAAQ,KAAK,IAAI,SAAS,QAAQ,YAAY;AACpD,MAAE,QAAQ,QAAQ;AAClB,aAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC/B,YAAM,IAAI,SAAS,CAAC;AACpB,QAAE,SAAS,MAAM,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,MAAM;AAAA,IACpD;AAGA,QAAI,WAAW,QAAQ,cAAc;AAEpC,UAAI,WAAW;AACf,eAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC/B,cAAM,IAAI,SAAS,CAAC;AACpB,YAAI,EAAE,MAAM,QAAQ,KAAK,EAAE,MAAM,QAAQ,GAAG;AAC3C,qBAAW;AACX;AAAA,QACD;AAAA,MACD;AACA,UAAI,WAAW,GAAG;AAEjB,UAAE,SAAS,MAAM,KAAK,EAAE,IAAI,QAAQ,GAAG,QAAQ,GAAG,QAAQ,OAAO,QAAQ,MAAM;AAC/E,UAAE,WAAW,QAAQ;AAAA,MACtB,OAAO;AACN,UAAE,WAAW,QAAQ;AAAA,MACtB;AAAA,IACD,OAAO;AACN,QAAE,WAAW,QAAQ;AAAA,IACtB;AAGA,QAAI,QAAQ,GAAG;AACd,UAAI,OAAO,OAAO,mBACjB,OAAO,OAAO,mBACd,OAAO,OAAO,mBACd,OAAO,OAAO;AACf,eAAS,IAAI,GAAG,IAAI,OAAO,KAAK;AAC/B,cAAM,IAAI,SAAS,CAAC;AACpB,eAAO,KAAK,IAAI,MAAM,EAAE,CAAC;AACzB,eAAO,KAAK,IAAI,MAAM,EAAE,CAAC;AACzB,eAAO,KAAK,IAAI,MAAM,EAAE,IAAI,EAAE,KAAK;AACnC,eAAO,KAAK,IAAI,MAAM,EAAE,IAAI,EAAE,MAAM;AAAA,MACrC;AACA,QAAE,cAAc,MAAM,IAAI,MAAM,MAAM,OAAO,MAAM,OAAO,IAAI;AAC9D,QAAE,WAAW,QAAQ;AAAA,IACtB,OAAO;AACN,QAAE,WAAW,QAAQ;AAAA,IACtB;AAGA,UAAM,SAAS,KAAK,IAAI,OAAO,QAAQ,EAAE;AACzC,MAAE,aAAa,QAAQ;AACvB,aAAS,IAAI,GAAG,IAAI,QAAQ,KAAK;AAChC,YAAM,IAAI,OAAO,CAAC;AAClB,QAAE,SAAS,MAAM,CAAC,EAAE,IAAI,EAAE,SAAS,MAAM,IAAI,GAAG,EAAE,UAAU,GAAG,CAAC;AAAA,IACjE;AAGA,QAAI,OAAO;AACX,eAAW,MAAM,UAAU;AAC1B,iBAAW,OAAO,GAAG,UAAU;AAC9B,YAAI,QAAQ,EAAG;AACf,UAAE,WAAW,MAAM,IAAI,EAAE,IAAI,GAAG,SAAS,MAAM,IAAI,GAAG,IAAI,MAAM,IAAI,IAAI,GAAG,YAAY;AACvF;AAAA,MACD;AAAA,IACD;AACA,MAAE,eAAe,QAAQ;AAGzB,UAAM,gBAAgB,SAAS;AAC/B,aAAS,YAAY;AACrB,aAAS,OAAO,KAAK,OAAO,KAAK,MAAM;AACvC,aAAS,YAAY;AAAA,EACtB;AAAA,EAEA,UAAU;AACT,SAAK,KAAK,SAAS,QAAQ;AAC3B,SAAK,SAAS,QAAQ;AAAA,EACvB;AACD;;;AC5aA,SAAS,eAAe,kBAAkB;AAM1C,IAAM,gBAAgB,cAAmC,IAAI;AAEtD,IAAM,iBAAiB,cAAc;AAK5C,IAAM,sBAAsB,cAA6D,IAAI;AAEtF,IAAM,uBAAuB,oBAAoB;AAEjD,SAAS,kBAAiE;AAChF,SAAO,WAAW,mBAAmB;AACtC;AAEO,SAAS,kBAAgC;AAC/C,QAAM,SAAS,WAAW,aAAa;AACvC,MAAI,CAAC,QAAQ;AACZ,UAAM,IAAI,MAAM,yDAAyD;AAAA,EAC1E;AACA,SAAO;AACR;AAGO,IAAM,YAAY;AAazB,IAAM,wBAAwB,cAAqC,IAAI;AAEhE,IAAM,yBAAyB,sBAAsB;AAErD,SAAS,oBAA2C;AAC1D,SAAO,WAAW,qBAAqB;AACxC;;;AC7CO,IAAM,cAAc,gBAAgB,eAAe;AAAA,EACzD,GAAG;AAAA,EACH,GAAG;AAAA,EACH,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,UAAU;AACX,CAAC;AAEM,IAAM,cAAc,gBAAgB,eAAe;AAAA,EACzD,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,YAAY;AAAA,EACZ,aAAa;AACd,CAAC;AAEM,IAAM,SAAS,gBAAgB,UAAU,EAAE,OAAO,EAAE,CAAC;AAGrD,IAAM,SAAS,gBAAgB,UAAU,EAAE,IAAI,EAAc,CAAC;AAC9D,IAAM,WAAW,gBAAgB,YAAY,EAAE,KAAK,CAAC,EAAgB,CAAC;AAGtE,IAAM,SAAS,gBAAgB,UAAU;AAAA,EAC/C,SAAS;AAAA,EACT,MAAM;AACP,CAAC;AAEM,IAAM,aAAa,gBAAgB,cAAc;AAAA,EACvD,MAAM,CAAC;AACR,CAAC;AAEM,IAAM,mBAAmB,gBAAgB,oBAAoB;AAAA,EACnE,SAAS;AAAA,EACT,aAAa;AAAA,EACb,cAAc;AACf,CAAC;AAGM,IAAM,YAAY,gBAAgB,aAAa,EAAE,WAAW,KAAK,CAAC;AAGlE,IAAM,aAAa,UAAU,YAAY;AACzC,IAAM,YAAY,UAAU,WAAW;AACvC,IAAM,YAAY,UAAU,WAAW;AACvC,IAAM,SAAS,UAAU,QAAQ;AACjC,IAAM,WAAW,UAAU,UAAU;AACrC,IAAM,SAAS,UAAU,QAAQ;AACjC,IAAM,UAAU,UAAU,SAAS;;;ACnD1C,SAAS,WAAW,QAAQ,gBAAgB;AAQrC,SAAS,aAAgB,QAAkB,MAAuC;AACxF,QAAM,SAAS,gBAAgB;AAC/B,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAwB,MAAM,OAAO,IAAI,QAAQ,IAAI,CAAC;AAEhF,YAAU,MAAM;AACf,aAAS,OAAO,IAAI,QAAQ,IAAI,CAAC;AAEjC,UAAM,QAAQ,OAAO,MAAM;AAAA,MAC1B;AAAA,MACA,CAAC,KAAK,OAAO,SAAS;AACrB,iBAAS,EAAE,GAAG,KAAK,CAAC;AAAA,MACrB;AAAA,MACA;AAAA,IACD;AAEA,WAAO;AAAA,EACR,GAAG,CAAC,QAAQ,QAAQ,IAAI,CAAC;AAEzB,SAAO;AACR;AAMO,SAAS,OAAO,QAAkB,MAAwB;AAChE,QAAM,SAAS,gBAAgB;AAC/B,QAAM,CAAC,KAAK,MAAM,IAAI,SAAS,MAAM,OAAO,MAAM,OAAO,QAAQ,IAAI,CAAC;AAEtE,YAAU,MAAM;AACf,WAAO,OAAO,MAAM,OAAO,QAAQ,IAAI,CAAC;AAExC,UAAM,SAAS,OAAO,MAAM,WAAW,MAAM,MAAM,OAAO,IAAI,GAAG,MAAM;AACvE,UAAM,SAAS,OAAO,MAAM,aAAa,MAAM,MAAM,OAAO,KAAK,GAAG,MAAM;AAE1E,WAAO,MAAM;AACZ,aAAO;AACP,aAAO;AAAA,IACR;AAAA,EACD,GAAG,CAAC,QAAQ,QAAQ,IAAI,CAAC;AAEzB,SAAO;AACR;AAMO,SAAS,YAAe,MAA0B;AACxD,QAAM,SAAS,gBAAgB;AAC/B,QAAM,CAAC,OAAO,QAAQ,IAAI,SAAY,OAAO,EAAE,GAAG,OAAO,MAAM,YAAY,IAAI,EAAE,EAAE;AACnF,QAAM,UAAU,OAAe,EAAE;AAEjC,YAAU,MAAM;AACf,UAAM,QAAQ,OAAO,QAAQ,MAAM;AAClC,YAAM,UAAU,OAAO,MAAM,YAAY,IAAI;AAC7C,YAAM,aAAa,KAAK,UAAU,OAAO;AACzC,UAAI,eAAe,QAAQ,SAAS;AACnC,gBAAQ,UAAU;AAClB,iBAAS,EAAE,GAAG,QAAQ,CAAC;AAAA,MACxB;AAAA,IACD,CAAC;AACD,WAAO;AAAA,EACR,GAAG,CAAC,QAAQ,IAAI,CAAC;AAEjB,SAAO;AACR;AAOO,SAAS,YAAY,OAAgD;AAC3E,QAAM,SAAS,gBAAgB;AAE/B,QAAM,UAAU,MAAM,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,KAAK,GAAG;AACjD,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAqB,MAAM,OAAO,MAAM,MAAM,GAAG,KAAK,CAAC;AAEnF,YAAU,MAAM;AACf,UAAM,QAAQ,OAAO,QAAQ,MAAM;AAClC,YAAM,OAAO,OAAO,MAAM,MAAM,GAAG,KAAK;AACxC,gBAAU,CAAC,SAAS;AACnB,YAAI,KAAK,WAAW,KAAK,UAAU,KAAK,KAAK,CAAC,IAAI,MAAM,OAAO,KAAK,CAAC,CAAC,GAAG;AACxE,iBAAO;AAAA,QACR;AACA,eAAO;AAAA,MACR,CAAC;AAAA,IACF,CAAC;AACD,WAAO;AAAA,EAER,GAAG,CAAC,QAAQ,OAAO,CAAC;AAEpB,SAAO;AACR;AAMO,SAAS,kBAAkB,MAA2B;AAC5D,QAAM,SAAS,gBAAgB;AAC/B,QAAM,CAAC,QAAQ,SAAS,IAAI,SAAqB,MAAM,OAAO,MAAM,YAAY,IAAI,CAAC;AAErF,YAAU,MAAM;AACf,UAAM,SAAS,MAAM,UAAU,CAAC,GAAG,OAAO,MAAM,YAAY,IAAI,CAAC,CAAC;AAClE,UAAM,SAAS,OAAO,MAAM,WAAW,MAAM,MAAM;AACnD,UAAM,SAAS,OAAO,MAAM,aAAa,MAAM,MAAM;AACrD,WAAO,MAAM;AACZ,aAAO;AACP,aAAO;AAAA,IACR;AAAA,EACD,GAAG,CAAC,QAAQ,IAAI,CAAC;AAEjB,SAAO;AACR;;;AC3HA,SAAS,gBAAgB;AACzB,SAAS,UAAAC,eAAc;AAyCpB;AAxBI,SAAS,gBAAgB,EAAE,UAAU,WAAW,gBAAgB,GAAyB;AAC/F,QAAM,WAAWC,QAAc,IAAI;AACnC,QAAM,SAAS,gBAAgB;AAG/B,QAAM,KAAK,aAAa,UAAU,WAAW;AAG7C,WAAS,MAAM;AACd,QAAI,CAAC,SAAS,QAAS;AACvB,UAAM,SAAS,OAAO,IAAI,UAAU,WAAW;AAC/C,QAAI,CAAC,OAAQ;AAEb,aAAS,QAAQ,SAAS;AAAA,MACzB,OAAO,SAAS,OAAO,aAAa;AAAA,MACpC,EAAE,OAAO,SAAS,OAAO,cAAc;AAAA,MACvC;AAAA,IACD;AAAA,EACD,CAAC;AAED,MAAI,CAAC,GAAI,QAAO;AAEhB,SACC,oBAAC,WAAM,KAAK,UACX,8BAAC,mBAAgB,UAAoB,OAAO,GAAG,YAAY,QAAQ,GAAG,aAAa,GACpF;AAEF;;;AC7CA,SAAS,QAAQ,YAAAC,WAAU,gBAAgB;AAC3C,SAAS,SAAS,UAAAC,eAAc;AAChC,YAAYC,YAAW;AAuFpB,SACC,OAAAC,MADD;AA9EH,SAAS,WAAW,EAAE,OAAO,GAA6B;AACzD,QAAM,EAAE,QAAQ,KAAK,IAAI,SAAS;AAElC,EAAAC,UAAS,MAAM;AACd,UAAM,MAAM,OAAO,UAAU;AAC7B,UAAM,QAAQ;AAGd,UAAM,OAAO;AACb,UAAM,QAAQ,KAAK,QAAQ,IAAI;AAC/B,UAAM,MAAM;AACZ,UAAM,SAAS,EAAE,KAAK,SAAS,IAAI;AACnC,UAAM,OAAO;AACb,UAAM,MAAM;AAGZ,UAAM,SAAS,IAAI,IAAI,GAAG,CAAC,IAAI,GAAG,GAAI;AACtC,UAAM,uBAAuB;AAAA,EAC9B,CAAC;AAED,SAAO;AACR;AAUO,SAAS,iBAAiB,EAAE,QAAQ,UAAU,QAAQ,GAA0B;AACtF,QAAM,YAAYC,QAA0B,IAAI;AAGhD,QAAM,gBAAgB,QAAQ,MAAM;AACnC,UAAM,MAAM,IAAU,0BAAmB,GAAG,GAAG,GAAG,IAAI,KAAK,GAAK;AAChE,QAAI,SAAS,IAAI,GAAG,GAAG,GAAI;AAC3B,WAAO;AAAA,EACR,GAAG,CAAC,CAAC;AAGL,QAAM,gBAAgB,QAAQ,MAAM;AACnC,UAAM,SAGA,CAAC;AACP,eAAW,MAAM,UAAU;AAC1B,YAAM,WAAW,QAAQ,EAAE;AAC3B,UAAI,YAAY,SAAS,YAAY,SAAS;AAC7C,eAAO,KAAK;AAAA,UACX,UAAU;AAAA,UACV,WAAW,SAAS;AAAA,QAKrB,CAAC;AAAA,MACF;AAAA,IACD;AACA,WAAO;AAAA,EACR,GAAG,CAAC,UAAU,OAAO,CAAC;AAEtB,MAAI,cAAc,WAAW,EAAG,QAAO;AAEvC,SACC,gBAAAF;AAAA,IAAC;AAAA;AAAA,MACA,KAAK;AAAA,MACL,QAAQ;AAAA,MACR,WAAU;AAAA,MACV,IAAI,EAAE,OAAO,MAAM,WAAW,KAAK;AAAA,MACnC,OAAO;AAAA,QACN,UAAU;AAAA,QACV,OAAO;AAAA,QACP,eAAe;AAAA,QACf,QAAQ;AAAA,MACT;AAAA,MAEA,+BAAC,kBAAe,OAAO,QACtB;AAAA,wBAAAA,KAAC,cAAW,QAAgB;AAAA,QAC3B,cAAc,IAAI,CAAC,EAAE,UAAU,UAAU,MACzC,gBAAAA,KAAC,mBAA+B,UAAoB,aAA9B,QAAoD,CAC1E;AAAA,SACF;AAAA;AAAA,EACD;AAEF;;;ACjGA,SAAS,MAAM,aAAa,aAAAG,YAAW,UAAAC,eAAc;AAyHnD,gBAAAC,YAAA;AA7GF,SAAS,QAAQ,GAAkC;AAClD,SAAO,EAAE,OAAO,EAAE,UAAU,MAAM,EAAE,SAAS,KAAK,EAAE,QAAQ,MAAM,EAAE,QAAQ;AAC7E;AAEO,IAAM,aAAa,KAAK,SAASC,YAAW,EAAE,UAAU,QAAQ,GAAoB;AAC1F,QAAM,aAAaC,QAAuB,IAAI;AAC9C,QAAM,SAAS,gBAAgB;AAC/B,QAAM,kBAAkB,gBAAgB;AACxC,QAAM,UAAU,kBAAkB;AAElC,QAAM,aAAa,aAAa,UAAU,MAAM;AAEhD,QAAM,WAAW,UAAU,UAAU,YAAY,QAAQ,EAAE;AAC3D,QAAM,kBAAkB,UAAU,aAAa;AAG/C,EAAAC,WAAU,MAAM;AACf,YAAQ,UAAU,WAAW,OAAO;AACpC,WAAO,MAAM,QAAQ,UAAU,IAAI;AAAA,EACpC,GAAG,CAAC,UAAU,OAAO,CAAC;AAGtB,QAAM,UAAU;AAAA,IACf,CAAC,MAAoD;AACpD,YAAM,OAAO,iBAAiB,SAAS,sBAAsB;AAC7D,UAAI,CAAC,KAAM,QAAO,EAAE,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ;AAC/C,aAAO,EAAE,GAAG,EAAE,UAAU,KAAK,MAAM,GAAG,EAAE,UAAU,KAAK,IAAI;AAAA,IAC5D;AAAA,IACA,CAAC,eAAe;AAAA,EACjB;AAIA,QAAM,gBAAgB;AAAA,IACrB,CAAC,MAA0B;AAE1B,YAAM,SAAS,EAAE;AACjB,UAAI,OAAO,QAAQ,oDAAoD,GAAG;AACzE,UAAE,gBAAgB;AAClB;AAAA,MACD;AAEA,YAAM,EAAE,GAAG,EAAE,IAAI,QAAQ,CAAC;AAC1B,YAAM,YAAY,OAAO,kBAAkB,GAAG,GAAG,EAAE,QAAQ,QAAQ,CAAC,CAAC;AAGrE,QAAE,gBAAgB;AAElB,UAAI,UAAU,WAAW,oBAAoB,UAAU,WAAW,0BAA0B;AAE3F,mBAAW,SAAS,kBAAkB,EAAE,SAAS;AAAA,MAClD;AACA,UAAI,UAAU,WAAW,kBAAkB;AAC1C,UAAE,eAAe;AAAA,MAClB;AAAA,IACD;AAAA,IACA,CAAC,QAAQ,OAAO;AAAA,EACjB;AAEA,QAAM,cAAcD,QAAO,KAAK;AAEhC,QAAM,gBAAgB;AAAA,IACrB,CAAC,MAA0B;AAC1B,YAAM,EAAE,GAAG,EAAE,IAAI,QAAQ,CAAC;AAC1B,YAAM,YAAY,OAAO,kBAAkB,GAAG,GAAG,QAAQ,CAAC,CAAC;AAG3D,UAAI,UAAU,WAAW,kBAAkB,CAAC,YAAY,SAAS;AAChE,oBAAY,UAAU;AACtB,UAAE,gBAAgB;AAAA,MACnB;AAAA,IACD;AAAA,IACA,CAAC,QAAQ,OAAO;AAAA,EACjB;AAEA,QAAM,cAAc;AAAA,IACnB,CAAC,MAA0B;AAC1B,QAAE,gBAAgB;AAClB,kBAAY,UAAU;AAEtB,UAAI,WAAW,SAAS,kBAAkB,EAAE,SAAS,GAAG;AACvD,mBAAW,QAAQ,sBAAsB,EAAE,SAAS;AAAA,MACrD;AACA,aAAO,gBAAgB;AAAA,IACxB;AAAA,IACA,CAAC,MAAM;AAAA,EACR;AAEA,QAAM,gBAAgB;AAAA,IACrB,CAAC,MAAwB;AACxB,QAAE,gBAAgB;AAClB,aAAO,eAAe,QAAQ;AAAA,IAC/B;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,EAClB;AAKA,QAAM,KAAK,OAAO,IAAI,UAAU,WAAW;AAC3C,QAAM,eAAoC,KACvC;AAAA,IACA,WAAW,aAAa,GAAG,MAAM,OAAO,GAAG,MAAM;AAAA,IACjD,OAAO,GAAG,GAAG,UAAU;AAAA,IACvB,QAAQ,GAAG,GAAG,WAAW;AAAA,EAC1B,IACC,CAAC;AAEJ,QAAM,UAAU,kBACf,gBAAAF,KAAC,mBAAgB,UAAoB,IAErC,gBAAAA,KAAC,SAAI,WAAU,yEAAwE;AAGxF,SACC,gBAAAA;AAAA,IAAC;AAAA;AAAA,MACA,KAAK;AAAA,MACL,oBAAiB;AAAA,MACjB,WAAU;AAAA,MACV,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MAEC;AAAA;AAAA,EACF;AAEF,CAAC;;;AC5ID,SAAS,QAAAI,OAAM,eAAAC,cAAa,aAAAC,YAAW,UAAAC,eAAc;AAoGnD,gBAAAC,YAAA;AAzFF,SAASC,SAAQ,GAAkC;AAClD,SAAO,EAAE,OAAO,EAAE,UAAU,MAAM,EAAE,SAAS,KAAK,EAAE,QAAQ,MAAM,EAAE,QAAQ;AAC7E;AAMO,IAAM,uBAAuBC,MAAK,SAASC,sBAAqB;AAAA,EACtE;AAAA,EACA;AACD,GAA8B;AAC7B,QAAM,aAAaC,QAAuB,IAAI;AAC9C,QAAM,SAAS,gBAAgB;AAC/B,QAAM,kBAAkB,gBAAgB;AAExC,EAAAC,WAAU,MAAM;AACf,YAAQ,UAAU,WAAW,OAAO;AACpC,WAAO,MAAM,QAAQ,UAAU,IAAI;AAAA,EACpC,GAAG,CAAC,UAAU,OAAO,CAAC;AAEtB,QAAM,UAAUC;AAAA,IACf,CAAC,MAA0B;AAC1B,YAAM,OAAO,iBAAiB,SAAS,sBAAsB;AAC7D,UAAI,CAAC,KAAM,QAAO,EAAE,GAAG,EAAE,SAAS,GAAG,EAAE,QAAQ;AAC/C,aAAO,EAAE,GAAG,EAAE,UAAU,KAAK,MAAM,GAAG,EAAE,UAAU,KAAK,IAAI;AAAA,IAC5D;AAAA,IACA,CAAC,eAAe;AAAA,EACjB;AAEA,QAAM,cAAcF,QAAO,KAAK;AAEhC,QAAM,gBAAgBE;AAAA,IACrB,CAAC,MAA0B;AAC1B,QAAE,gBAAgB;AAClB,YAAM,EAAE,GAAG,EAAE,IAAI,QAAQ,CAAC;AAC1B,YAAM,YAAY,OAAO,kBAAkB,GAAG,GAAG,EAAE,QAAQL,SAAQ,CAAC,CAAC;AACrE,UAAI,UAAU,WAAW,oBAAoB,UAAU,WAAW,0BAA0B;AAC3F,mBAAW,SAAS,kBAAkB,EAAE,SAAS;AAAA,MAClD;AACA,UAAI,UAAU,WAAW,kBAAkB;AAC1C,UAAE,eAAe;AAAA,MAClB;AAAA,IACD;AAAA,IACA,CAAC,QAAQ,OAAO;AAAA,EACjB;AAEA,QAAM,gBAAgBK;AAAA,IACrB,CAAC,MAA0B;AAC1B,YAAM,EAAE,GAAG,EAAE,IAAI,QAAQ,CAAC;AAC1B,YAAM,YAAY,OAAO,kBAAkB,GAAG,GAAGL,SAAQ,CAAC,CAAC;AAC3D,UAAI,UAAU,WAAW,kBAAkB,CAAC,YAAY,SAAS;AAChE,oBAAY,UAAU;AACtB,UAAE,gBAAgB;AAAA,MACnB;AAAA,IACD;AAAA,IACA,CAAC,QAAQ,OAAO;AAAA,EACjB;AAEA,QAAM,cAAcK;AAAA,IACnB,CAAC,MAA0B;AAC1B,QAAE,gBAAgB;AAClB,kBAAY,UAAU;AACtB,UAAI,WAAW,SAAS,kBAAkB,EAAE,SAAS,GAAG;AACvD,mBAAW,QAAQ,sBAAsB,EAAE,SAAS;AAAA,MACrD;AACA,aAAO,gBAAgB;AAAA,IACxB;AAAA,IACA,CAAC,MAAM;AAAA,EACR;AAEA,QAAM,gBAAgBA;AAAA,IACrB,CAAC,MAAwB;AACxB,QAAE,gBAAgB;AAClB,aAAO,eAAe,QAAQ;AAAA,IAC/B;AAAA,IACA,CAAC,QAAQ,QAAQ;AAAA,EAClB;AAEA,QAAM,KAAK,OAAO,IAAI,UAAU,WAAW;AAC3C,QAAM,eAAoC,KACvC;AAAA,IACA,WAAW,aAAa,GAAG,MAAM,OAAO,GAAG,MAAM;AAAA,IACjD,OAAO,GAAG,GAAG,UAAU;AAAA,IACvB,QAAQ,GAAG,GAAG,WAAW;AAAA,EAC1B,IACC,CAAC;AAEJ,SACC,gBAAAN;AAAA,IAAC;AAAA;AAAA,MACA,KAAK;AAAA,MACL,WAAU;AAAA,MACV,OAAO;AAAA,MACP;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA;AAAA,EACD;AAEF,CAAC;;;AC/DM,SAAS,kBACf,SACA,YACA,WACa;AACb,QAAM,SAAsB,CAAC;AAC7B,QAAM,WAAoC,CAAC;AAC3C,MAAI,SAAS;AACb,MAAI,SAAS;AAGb,QAAM,QAAQ,QAAQ;AACtB,QAAM,SAAS,QAAQ,IAAI,QAAQ;AACnC,QAAM,WAAW,QAAQ,IAAI,QAAQ,QAAQ;AAC7C,QAAM,OAAO,QAAQ;AACrB,QAAM,UAAU,QAAQ,IAAI,QAAQ;AACpC,QAAM,WAAW,QAAQ,IAAI,QAAQ,SAAS;AAE9C,MAAI,YAAY,OAAO;AACvB,MAAI,YAAY,OAAO;AACvB,MAAI,SAAS;AACb,MAAI,SAAS;AACb,QAAM,UAAuB,CAAC;AAC9B,QAAM,UAAuB,CAAC;AAI9B,aAAW,OAAO,YAAY;AAC7B,UAAM,QAAQ,IAAI;AAClB,UAAM,SAAS,IAAI,IAAI,IAAI;AAC3B,UAAM,WAAW,IAAI,IAAI,IAAI,QAAQ;AACrC,UAAM,OAAO,IAAI;AACjB,UAAM,UAAU,IAAI,IAAI,IAAI;AAC5B,UAAM,WAAW,IAAI,IAAI,IAAI,SAAS;AAGtC,UAAM,SAAgD;AAAA,MACrD,CAAC,OAAO,OAAO,MAAM;AAAA,MACrB,CAAC,OAAO,QAAQ,MAAM;AAAA,MACtB,CAAC,QAAQ,OAAO,MAAM;AAAA,MACtB,CAAC,QAAQ,QAAQ,MAAM;AAAA,MACvB,CAAC,UAAU,UAAU,QAAQ;AAAA,MAC7B,CAAC,OAAO,UAAU,MAAM;AAAA,MACxB,CAAC,QAAQ,UAAU,MAAM;AAAA,IAC1B;AAEA,eAAW,CAAC,MAAM,MAAM,IAAI,KAAK,QAAQ;AACxC,YAAM,OAAO,KAAK,IAAI,OAAO,IAAI;AACjC,UAAI,QAAQ,WAAW;AACtB,cAAM,KAAK,OAAO;AAClB,YAAI,OAAO,WAAW;AACrB,sBAAY;AACZ,mBAAS;AACT,kBAAQ,SAAS;AAAA,QAClB;AACA,YAAI,QAAQ,YAAY,MAAM;AAC7B,kBAAQ,KAAK,EAAE,MAAM,KAAK,UAAU,MAAM,KAAK,CAAC;AAAA,QACjD;AAAA,MACD;AAAA,IACD;AAGA,UAAM,SAAgD;AAAA,MACrD,CAAC,MAAM,MAAM,MAAM;AAAA,MACnB,CAAC,MAAM,SAAS,MAAM;AAAA,MACtB,CAAC,SAAS,MAAM,MAAM;AAAA,MACtB,CAAC,SAAS,SAAS,MAAM;AAAA,MACzB,CAAC,UAAU,UAAU,QAAQ;AAAA,MAC7B,CAAC,MAAM,UAAU,MAAM;AAAA,MACvB,CAAC,SAAS,UAAU,MAAM;AAAA,IAC3B;AAEA,eAAW,CAAC,MAAM,MAAM,IAAI,KAAK,QAAQ;AACxC,YAAM,OAAO,KAAK,IAAI,OAAO,IAAI;AACjC,UAAI,QAAQ,WAAW;AACtB,cAAM,KAAK,OAAO;AAClB,YAAI,OAAO,WAAW;AACrB,sBAAY;AACZ,mBAAS;AACT,kBAAQ,SAAS;AAAA,QAClB;AACA,YAAI,QAAQ,YAAY,MAAM;AAC7B,kBAAQ,KAAK,EAAE,MAAM,KAAK,UAAU,MAAM,KAAK,CAAC;AAAA,QACjD;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAMA,QAAM,WAAW,oBAAoB,SAAS,YAAY,SAAS;AAGnE,MAAI,aAAa,WAAW;AAC3B,aAAS;AAAA,EACV,WAAW,SAAS,WAAW,QAAW;AACzC,aAAS,SAAS;AAAA,EACnB;AACA,MAAI,aAAa,WAAW;AAC3B,aAAS;AAAA,EACV,WAAW,SAAS,WAAW,QAAW;AACzC,aAAS,SAAS;AAAA,EACnB;AAGA,MAAI,aAAa,WAAW;AAC3B,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,KAAK,SAAS;AACxB,UAAI,CAAC,KAAK,IAAI,EAAE,QAAQ,GAAG;AAC1B,aAAK,IAAI,EAAE,QAAQ;AACnB,eAAO,KAAK,CAAC;AAAA,MACd;AAAA,IACD;AAAA,EACD;AACA,MAAI,aAAa,WAAW;AAC3B,UAAM,OAAO,oBAAI,IAAY;AAC7B,eAAW,KAAK,SAAS;AACxB,UAAI,CAAC,KAAK,IAAI,EAAE,QAAQ,GAAG;AAC1B,aAAK,IAAI,EAAE,QAAQ;AACnB,eAAO,KAAK,CAAC;AAAA,MACd;AAAA,IACD;AAAA,EACD;AAGA,QAAM,gBAA8B;AAAA,IACnC,GAAG,QAAQ,IAAI;AAAA,IACf,GAAG,QAAQ,IAAI;AAAA,IACf,OAAO,QAAQ;AAAA,IACf,QAAQ,QAAQ;AAAA,EACjB;AACA,QAAM,UAAU,oBAAoB,eAAe,YAAY,YAAY,GAAG;AAC9E,WAAS,KAAK,GAAG,QAAQ,UAAU;AAEnC,SAAO,EAAE,QAAQ,QAAQ,QAAQ,SAAS;AAC3C;AAUA,SAAS,oBACR,SACA,YACA,WACqB;AACrB,QAAM,aAAsC,CAAC;AAC7C,MAAI;AACJ,MAAI;AAGJ,QAAM,UAAU,iBAAiB,SAAS,YAAY,WAAW,GAAG;AACpE,MAAI,SAAS;AACZ,aAAS,QAAQ;AACjB,eAAW,KAAK,GAAG,QAAQ,UAAU;AAAA,EACtC;AAGA,QAAM,UAAU,iBAAiB,SAAS,YAAY,WAAW,GAAG;AACpE,MAAI,SAAS;AACZ,aAAS,QAAQ;AACjB,eAAW,KAAK,GAAG,QAAQ,UAAU;AAAA,EACtC;AAEA,SAAO,EAAE,QAAQ,QAAQ,WAAW;AACrC;AAEA,SAAS,iBACR,SACA,YACA,WACA,MAC+D;AAC/D,QAAM,MAAM,SAAS;AAGrB,QAAM,MAAM,CAAC,MAAqB,MAAM,EAAE,IAAI,EAAE;AAChD,QAAM,OAAO,CAAC,MAAqB,MAAM,EAAE,QAAQ,EAAE;AACrD,QAAM,UAAU,CAAC,MAAqB,MAAM,EAAE,IAAI,EAAE;AACpD,QAAM,WAAW,CAAC,MAAqB,MAAM,EAAE,SAAS,EAAE;AAC1D,QAAM,MAAM,CAAC,MAAoB,IAAI,CAAC,IAAI,KAAK,CAAC;AAGhD,QAAM,YAAY,WAAW;AAAA,IAC5B,CAAC,QACA,QAAQ,GAAG,IAAI,QAAQ,OAAO,IAAI,SAAS,OAAO,KAClD,QAAQ,GAAG,IAAI,SAAS,GAAG,IAAI,QAAQ,OAAO;AAAA,EAChD;AAEA,MAAI,UAAU,SAAS,EAAG,QAAO;AAGjC,QAAM,SAAS,CAAC,GAAG,SAAS,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC,IAAI,IAAI,CAAC,CAAC;AAG5D,QAAM,UAAmE,CAAC;AAC1E,WAAS,IAAI,GAAG,IAAI,OAAO,SAAS,GAAG,KAAK;AAC3C,UAAM,MAAM,IAAI,OAAO,IAAI,CAAC,CAAC,IAAI,IAAI,OAAO,CAAC,CAAC;AAC9C,QAAI,MAAM,KAAK;AACd,cAAQ,KAAK,EAAE,MAAM,OAAO,CAAC,GAAG,IAAI,OAAO,IAAI,CAAC,GAAG,IAAI,CAAC;AAAA,IACzD;AAAA,EACD;AAEA,MAAI,WAA0B;AAC9B,MAAI,iBAA0C,CAAC;AAC/C,MAAI,WAAW,OAAO;AAItB,MAAI,QAA6B;AACjC,MAAI,SAA8B;AAClC,aAAW,OAAO,QAAQ;AACzB,QAAI,IAAI,GAAG,KAAK,IAAI,OAAO,IAAI,WAAW;AACzC,UAAI,CAAC,SAAS,IAAI,GAAG,IAAI,IAAI,KAAK,EAAG,SAAQ;AAAA,IAC9C;AACA,QAAI,IAAI,GAAG,KAAK,IAAI,OAAO,IAAI,WAAW;AACzC,UAAI,CAAC,UAAU,IAAI,GAAG,IAAI,IAAI,MAAM,EAAG,UAAS;AAAA,IACjD;AAAA,EACD;AAEA,MAAI,SAAS,QAAQ;AACpB,UAAM,OAAO,IAAI,OAAO,IAAI,IAAI,KAAK;AACrC,UAAM,OAAO,IAAI,MAAM,IAAI,IAAI,OAAO;AACtC,UAAM,OAAO,KAAK,IAAI,OAAO,IAAI;AACjC,QAAI,QAAQ,aAAa,OAAO,UAAU;AACzC,YAAM,YAAY,IAAI,KAAK,IAAI,IAAI,MAAM,IAAI,KAAK,OAAO,KAAK;AAC9D,YAAM,OAAO,WAAW,IAAI,OAAO;AACnC,YAAM,YAAY,IAAI,MAAM,IAAI,IAAI,KAAK,IAAI,KAAK,OAAO,KAAK;AAC9D,UAAI,WAAW,KAAK;AACnB,cAAM,QAAQ,kBAAkB,SAAS,CAAC,OAAO,MAAM,GAAG,GAAG;AAC7D,mBAAW;AACX,mBAAW;AACX,yBAAiB;AAAA,UAChB;AAAA,YACC;AAAA,YACA,KAAK;AAAA,YACL,UAAU;AAAA,cACT,EAAE,MAAM,IAAI,KAAK,GAAG,IAAI,SAAS;AAAA,cACjC,EAAE,MAAM,WAAW,KAAK,OAAO,GAAG,IAAI,IAAI,MAAM,EAAE;AAAA,YACnD;AAAA,YACA,cAAc;AAAA,UACf;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAIA,aAAW,UAAU,SAAS;AAC7B,UAAM,aAAa,OAAO;AAG1B,QAAI,WAAW,QAAQ,IAAI,OAAO,EAAE,KAAK,IAAI,OAAO,IAAI,YAAY,GAAG;AAEtE,YAAM,WAAW,OAAO;AAExB,YAAM,UAAU,IAAI,OAAO,IAAI,IAAI,QAAQ;AAC3C,YAAM,OAAO,KAAK,IAAI,UAAU,UAAU;AAC1C,UAAI,QAAQ,aAAa,OAAO,UAAU;AACzC,cAAM,WAAW,IAAI,QAAQ,IAAI;AACjC,cAAM,OAAO,WAAW,IAAI,OAAO;AACnC,cAAM,QAAQ,kBAAkB,SAAS,CAAC,OAAO,MAAM,OAAO,EAAE,GAAG,GAAG;AACtE,mBAAW;AACX,mBAAW;AAEX,yBAAiB;AAAA,UAChB;AAAA,YACC;AAAA,YACA,KAAK;AAAA,YACL,UAAU;AAAA,cACT,EAAE,MAAM,IAAI,OAAO,IAAI,GAAG,IAAI,IAAI,OAAO,EAAE,EAAE;AAAA,cAC7C,EAAE,MAAM,IAAI,QAAQ,GAAG,IAAI,SAAS;AAAA,YACrC;AAAA,YACA,cAAc;AAAA,UACf;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAGA,QAAI,UAAU,QAAQ,IAAI,OAAO,IAAI,KAAK,IAAI,OAAO,IAAI,YAAY,GAAG;AACvE,YAAM,aAAa,OAAO;AAC1B,YAAM,UAAU,IAAI,UAAU,IAAI,IAAI,OAAO;AAC7C,YAAM,OAAO,KAAK,IAAI,UAAU,UAAU;AAC1C,UAAI,QAAQ,aAAa,OAAO,UAAU;AACzC,cAAM,WAAW,IAAI,UAAU,IAAI,aAAa,KAAK,OAAO;AAC5D,cAAM,OAAO,WAAW,IAAI,OAAO;AACnC,cAAM,QAAQ,kBAAkB,SAAS,CAAC,OAAO,MAAM,OAAO,EAAE,GAAG,GAAG;AACtE,mBAAW;AACX,mBAAW;AACX,yBAAiB;AAAA,UAChB;AAAA,YACC;AAAA,YACA,KAAK;AAAA,YACL,UAAU;AAAA,cACT,EAAE,MAAM,WAAW,KAAK,OAAO,GAAG,IAAI,IAAI,UAAU,EAAE;AAAA,cACtD,EAAE,MAAM,IAAI,OAAO,IAAI,GAAG,IAAI,IAAI,OAAO,EAAE,EAAE;AAAA,YAC9C;AAAA,YACA,cAAc;AAAA,UACf;AAAA,QACD;AAAA,MACD;AAAA,IACD;AAAA,EACD;AAEA,MAAI,aAAa,MAAM;AACtB,WAAO,EAAE,MAAM,UAAU,YAAY,eAAe;AAAA,EACrD;AACA,SAAO;AACR;AAEA,SAAS,kBAAkB,SAAuB,MAAsB,KAAsB;AAC7F,QAAM,UAAU,CAAC,MAAqB,MAAM,EAAE,IAAI,EAAE;AACpD,QAAM,WAAW,CAAC,MAAqB,MAAM,EAAE,SAAS,EAAE;AAC1D,QAAM,YAAY,CAAC,SAAS,GAAG,IAAI;AACnC,QAAM,WAAW,KAAK,IAAI,GAAG,UAAU,IAAI,OAAO,CAAC;AACnD,QAAM,SAAS,KAAK,IAAI,GAAG,UAAU,IAAI,CAAC,MAAM,QAAQ,CAAC,IAAI,SAAS,CAAC,CAAC,CAAC;AACzE,SAAO,YAAY,SAAS,YAAY;AACzC;;;ACjVA,IAAM,YAAY;AAEX,IAAM,WAAN,MAAe;AAAA,EACb,UAAU;AAAA,EACV,OAAsB,CAAC;AAAA,EACvB,WAAW;AAAA,EACX,SAAS;AAAA;AAAA,EAGT,aAAa;AAAA,EACb,iBAAyC,CAAC;AAAA,EAC1C,eAAe;AAAA,EACf,cAAc;AAAA;AAAA,EAGtB,WAAW,IAAa;AACvB,SAAK,UAAU;AACf,QAAI,CAAC,IAAI;AACR,WAAK,OAAO,CAAC;AACb,WAAK,WAAW;AAChB,WAAK,SAAS;AAAA,IACf;AAAA,EACD;AAAA,EAEA,YAAqB;AACpB,WAAO,KAAK;AAAA,EACb;AAAA;AAAA,EAGA,WAAW,MAAc;AACxB,QAAI,CAAC,KAAK,QAAS;AACnB,SAAK,cAAc;AACnB,SAAK,iBAAiB,CAAC;AACvB,SAAK,eAAe;AACpB,SAAK,aAAa,YAAY,IAAI;AAClC,gBAAY,KAAK,gBAAgB;AAAA,EAClC;AAAA;AAAA,EAGA,YAAY,MAAc;AACzB,QAAI,CAAC,KAAK,QAAS;AACnB,gBAAY,KAAK,UAAU,IAAI,QAAQ;AAAA,EACxC;AAAA,EAEA,UAAU,MAAc;AACvB,QAAI,CAAC,KAAK,QAAS;AACnB,gBAAY,KAAK,UAAU,IAAI,MAAM;AACrC,QAAI;AACH,YAAM,UAAU,YAAY;AAAA,QAC3B,MAAM,IAAI;AAAA,QACV,UAAU,IAAI;AAAA,QACd,UAAU,IAAI;AAAA,MACf;AACA,WAAK,eAAe,IAAI,IAAI,QAAQ;AAAA,IACrC,QAAQ;AAAA,IAER;AACA,gBAAY,WAAW,UAAU,IAAI,QAAQ;AAC7C,gBAAY,WAAW,UAAU,IAAI,MAAM;AAAA,EAC5C;AAAA;AAAA,EAGA,kBAAkB;AACjB,QAAI,CAAC,KAAK,QAAS;AACnB,gBAAY,KAAK,cAAc;AAAA,EAChC;AAAA,EAEA,gBAAgB;AACf,QAAI,CAAC,KAAK,QAAS;AACnB,gBAAY,KAAK,YAAY;AAC7B,QAAI;AACH,YAAM,UAAU,YAAY,QAAQ,iBAAiB,gBAAgB,YAAY;AACjF,WAAK,eAAe,QAAQ;AAAA,IAC7B,QAAQ;AAAA,IAER;AACA,gBAAY,WAAW,cAAc;AACrC,gBAAY,WAAW,YAAY;AAAA,EACpC;AAAA;AAAA,EAGA,SAAS,aAAqB,cAAsB;AACnD,QAAI,CAAC,KAAK,QAAS;AACnB,gBAAY,KAAK,cAAc;AAE/B,QAAI,UAAU;AACd,QAAI;AACH,YAAM,UAAU,YAAY,QAAQ,YAAY,kBAAkB,cAAc;AAChF,gBAAU,QAAQ;AAAA,IACnB,QAAQ;AACP,gBAAU,YAAY,IAAI,IAAI,KAAK;AAAA,IACpC;AACA,gBAAY,WAAW,gBAAgB;AACvC,gBAAY,WAAW,cAAc;AAErC,UAAM,SAAsB;AAAA,MAC3B,MAAM,KAAK;AAAA,MACX,WAAW,YAAY,IAAI;AAAA,MAC3B;AAAA,MACA,SAAS,EAAE,GAAG,KAAK,eAAe;AAAA,MAClC,cAAc,KAAK;AAAA,MACnB;AAAA,MACA;AAAA,IACD;AAGA,QAAI,KAAK,KAAK,SAAS,WAAW;AACjC,WAAK,KAAK,KAAK,MAAM;AAAA,IACtB,OAAO;AACN,WAAK,KAAK,KAAK,QAAQ,IAAI;AAAA,IAC5B;AACA,SAAK,YAAY,KAAK,WAAW,KAAK;AACtC,QAAI,KAAK,KAAK,UAAU,UAAW,MAAK,SAAS;AAAA,EAClD;AAAA;AAAA,EAGA,WAAW,OAA+B;AACzC,UAAM,IAAI,KAAK,IAAI,SAAS,KAAK,KAAK,QAAQ,KAAK,KAAK,MAAM;AAC9D,UAAM,SAAwB,CAAC;AAC/B,aAAS,IAAI,GAAG,IAAI,GAAG,KAAK;AAC3B,YAAM,OAAO,KAAK,WAAW,IAAI,IAAI,KAAK,KAAK,UAAU,KAAK,KAAK;AACnE,UAAI,OAAO,KAAK,MAAM,KAAK,KAAK,QAAQ;AACvC,eAAO,KAAK,KAAK,KAAK,GAAG,CAAC;AAAA,MAC3B;AAAA,IACD;AACA,WAAO;AAAA,EACR;AAAA;AAAA,EAGA,WAA0B;AACzB,UAAM,UAAU,KAAK;AACrB,UAAM,IAAI,QAAQ;AAElB,QAAI,MAAM,GAAG;AACZ,aAAO;AAAA,QACN,KAAK;AAAA,QACL,WAAW,EAAE,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,EAAE;AAAA,QACpD,WAAW,CAAC;AAAA,QACZ,WAAW,CAAC;AAAA,QACZ,YAAY;AAAA,QACZ,aAAa;AAAA,MACd;AAAA,IACD;AAGA,UAAM,aAAa,QAAQ,IAAI,CAAC,MAAM,EAAE,OAAO,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAGrE,UAAM,SAAS,QAAQ,KAAK,UAAU,KAAK,WAAW,IAAI,aAAa,YAAY,IAAI,CAAC;AACxF,UAAM,SAAS,QAAQ,KAAK,SAAS,KAAK,WAAW,CAAC;AACtD,UAAM,SAAS,OAAO,YAAY,OAAO;AACzC,UAAM,MAAM,SAAS,IAAI,KAAK,OAAQ,IAAI,KAAK,SAAU,GAAI,IAAI;AAGjE,UAAM,aAAa,CAAC,QAAkB,MAAc;AACnD,YAAM,MAAM,KAAK,MAAO,IAAI,OAAQ,OAAO,SAAS,EAAE;AACtD,aAAO,OAAO,GAAG,KAAK;AAAA,IACvB;AAEA,UAAM,MAAM,WAAW,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI;AAGpD,UAAM,cAAc,oBAAI,IAAY;AACpC,eAAW,KAAK,SAAS;AACxB,iBAAW,QAAQ,OAAO,KAAK,EAAE,OAAO,EAAG,aAAY,IAAI,IAAI;AAAA,IAChE;AAEA,UAAM,YAAoC,CAAC;AAC3C,UAAM,YAAoC,CAAC;AAC3C,eAAW,QAAQ,aAAa;AAC/B,YAAM,QAAQ,QAAQ,IAAI,CAAC,MAAM,EAAE,QAAQ,IAAI,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,MAAM,IAAI,CAAC;AAC3E,gBAAU,IAAI,IAAI,MAAM,OAAO,CAAC,GAAG,MAAM,IAAI,GAAG,CAAC,IAAI;AACrD,gBAAU,IAAI,IAAI,WAAW,OAAO,EAAE;AAAA,IACvC;AAEA,WAAO;AAAA,MACN;AAAA,MACA,WAAW;AAAA,QACV;AAAA,QACA,KAAK,WAAW,YAAY,EAAE;AAAA,QAC9B,KAAK,WAAW,YAAY,EAAE;AAAA,QAC9B,KAAK,WAAW,YAAY,EAAE;AAAA,QAC9B,KAAK,WAAW,WAAW,SAAS,CAAC;AAAA,MACtC;AAAA,MACA;AAAA,MACA;AAAA,MACA,YAAa,MAAM,QAAS;AAAA,MAC5B,aAAa;AAAA,IACd;AAAA,EACD;AAAA;AAAA,EAGA,QAAQ;AACP,SAAK,OAAO,CAAC;AACb,SAAK,WAAW;AAChB,SAAK,SAAS;AAAA,EACf;AACD;;;ACxOA,OAAO,iBAAiB;AAExB,IAAM,QACL,OAAQ,YAAoB,YAAY,aAAc,YAAoB,UAAU;AAa9E,IAAM,eAAN,MAAmB;AAAA,EACjB,OAAO,IAAI,MAAoB;AAAA,EAC/B,UAAU,oBAAI,IAA4B;AAAA,EAElD,OAAO,UAAoB,QAAc;AACxC,UAAM,WAAW,KAAK,QAAQ,IAAI,QAAQ;AAC1C,QAAI,UAAU;AAEb,WAAK,KAAK,OAAO,QAAQ;AAAA,IAC1B;AACA,UAAM,QAAsB,EAAE,GAAG,QAAQ,SAAS;AAClD,SAAK,QAAQ,IAAI,UAAU,KAAK;AAChC,SAAK,KAAK,OAAO,KAAK;AAAA,EACvB;AAAA,EAEA,OAAO,UAAoB;AAC1B,UAAM,WAAW,KAAK,QAAQ,IAAI,QAAQ;AAC1C,QAAI,UAAU;AACb,WAAK,KAAK,OAAO,QAAQ;AACzB,WAAK,QAAQ,OAAO,QAAQ;AAAA,IAC7B;AAAA,EACD;AAAA;AAAA,EAGA,OAAO,QAA8B;AACpC,WAAO,KAAK,KAAK,OAAO,MAAM;AAAA,EAC/B;AAAA;AAAA,EAGA,YAAY,GAAW,GAAW,YAAY,GAAmB;AAChE,WAAO,KAAK,KAAK,OAAO;AAAA,MACvB,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,MACV,MAAM,IAAI;AAAA,IACX,CAAC;AAAA,EACF;AAAA,EAEA,QAAQ;AACP,SAAK,KAAK,MAAM;AAChB,SAAK,QAAQ,MAAM;AAAA,EACpB;AAAA,EAEA,IAAI,OAAe;AAClB,WAAO,KAAK,QAAQ;AAAA,EACrB;AACD;","names":["THREE","vertexShader","fragmentShader","useRef","useRef","useFrame","useRef","THREE","jsx","useFrame","useRef","useEffect","useRef","jsx","WidgetSlot","useRef","useEffect","memo","useCallback","useEffect","useRef","jsx","getMods","memo","SelectionOverlaySlot","useRef","useEffect","useCallback"]}
|
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
import * as THREE from 'three';
|
|
2
|
+
import * as react from 'react';
|
|
3
|
+
import { W as World, E as EntityId, C as ComponentType, R as ResourceType, b as ComponentInit, T as TagType, S as SystemDef, P as Profiler, U as Unsubscribe } from './profiler-DKuXy4MW.js';
|
|
4
|
+
|
|
5
|
+
interface GridConfig {
|
|
6
|
+
/** World-unit spacings for up to 3 grid levels [fine, medium, coarse]. */
|
|
7
|
+
spacings: [number, number, number];
|
|
8
|
+
/** Dot RGB color as [r, g, b] in 0–1 range. */
|
|
9
|
+
dotColor: [number, number, number];
|
|
10
|
+
/** Base dot opacity multiplier (0–1). */
|
|
11
|
+
dotAlpha: number;
|
|
12
|
+
/** CSS-pixel range where a grid level fades in: [start, end]. */
|
|
13
|
+
fadeIn: [number, number];
|
|
14
|
+
/** CSS-pixel range where a grid level fades out: [start, end]. */
|
|
15
|
+
fadeOut: [number, number];
|
|
16
|
+
/** Dot radius range in CSS pixels [min, max]. Scaled by DPR internally. */
|
|
17
|
+
dotRadius: [number, number];
|
|
18
|
+
/** Per-level opacity weight: level i gets (base + i * step). */
|
|
19
|
+
levelWeight: [number, number];
|
|
20
|
+
}
|
|
21
|
+
declare const DEFAULT_GRID_CONFIG: GridConfig;
|
|
22
|
+
declare class GridRenderer {
|
|
23
|
+
private renderer;
|
|
24
|
+
private scene;
|
|
25
|
+
private camera;
|
|
26
|
+
private material;
|
|
27
|
+
private mesh;
|
|
28
|
+
constructor(canvas: HTMLCanvasElement);
|
|
29
|
+
/** Apply a (partial) grid config. Only provided fields are updated. */
|
|
30
|
+
setConfig(config: Partial<GridConfig>): void;
|
|
31
|
+
setSize(width: number, height: number, dpr?: number): void;
|
|
32
|
+
render(cameraX: number, cameraY: number, zoom: number): void;
|
|
33
|
+
dispose(): void;
|
|
34
|
+
/** Expose for future WebGL widget rendering */
|
|
35
|
+
getWebGLRenderer(): THREE.WebGLRenderer;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Snap guide computation for alignment during drag operations.
|
|
40
|
+
* Implements Figma-style snapping:
|
|
41
|
+
* 1. Edge/center alignment guides
|
|
42
|
+
* 2. Equal spacing snap + indicators
|
|
43
|
+
*/
|
|
44
|
+
interface SnapGuide {
|
|
45
|
+
/** Axis this guide aligns on */
|
|
46
|
+
axis: 'x' | 'y';
|
|
47
|
+
/** World-space coordinate of the alignment line */
|
|
48
|
+
position: number;
|
|
49
|
+
/** What kind of alignment */
|
|
50
|
+
type: 'edge' | 'center';
|
|
51
|
+
}
|
|
52
|
+
interface EqualSpacingIndicator {
|
|
53
|
+
/** Axis along which the equal gaps run */
|
|
54
|
+
axis: 'x' | 'y';
|
|
55
|
+
/** The equal gap value (world units) */
|
|
56
|
+
gap: number;
|
|
57
|
+
/** Pairs of (from, to) marking each equal gap segment */
|
|
58
|
+
segments: {
|
|
59
|
+
from: number;
|
|
60
|
+
to: number;
|
|
61
|
+
}[];
|
|
62
|
+
/** Position on the perpendicular axis (for rendering) */
|
|
63
|
+
perpPosition: number;
|
|
64
|
+
}
|
|
65
|
+
interface SnapResult {
|
|
66
|
+
/** Snap-corrected delta (world units). Apply to entity position. */
|
|
67
|
+
snapDx: number;
|
|
68
|
+
snapDy: number;
|
|
69
|
+
/** Active alignment guide lines to render */
|
|
70
|
+
guides: SnapGuide[];
|
|
71
|
+
/** Equal spacing indicators */
|
|
72
|
+
spacings: EqualSpacingIndicator[];
|
|
73
|
+
}
|
|
74
|
+
interface EntityBounds {
|
|
75
|
+
x: number;
|
|
76
|
+
y: number;
|
|
77
|
+
width: number;
|
|
78
|
+
height: number;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Compute snap guides for a dragged entity against reference entities.
|
|
82
|
+
*/
|
|
83
|
+
declare function computeSnapGuides(dragged: EntityBounds, references: EntityBounds[], threshold: number): SnapResult;
|
|
84
|
+
|
|
85
|
+
interface SelectionConfig {
|
|
86
|
+
/** Selection outline color [r,g,b] 0-1. Default: Figma blue. */
|
|
87
|
+
outlineColor: [number, number, number];
|
|
88
|
+
/** Selection outline width in screen px. */
|
|
89
|
+
outlineWidth: number;
|
|
90
|
+
/** Hover outline color [r,g,b] 0-1. */
|
|
91
|
+
hoverColor: [number, number, number];
|
|
92
|
+
/** Hover outline width in screen px. */
|
|
93
|
+
hoverWidth: number;
|
|
94
|
+
/** Handle size in screen px. */
|
|
95
|
+
handleSize: number;
|
|
96
|
+
/** Handle fill color [r,g,b] 0-1 (white). */
|
|
97
|
+
handleFill: [number, number, number];
|
|
98
|
+
/** Handle border color [r,g,b] 0-1 (same as outline). */
|
|
99
|
+
handleBorder: [number, number, number];
|
|
100
|
+
/** Handle border width in screen px. */
|
|
101
|
+
handleBorderWidth: number;
|
|
102
|
+
/** Group bbox dash length in screen px (0 = solid). */
|
|
103
|
+
groupDash: number;
|
|
104
|
+
}
|
|
105
|
+
declare const DEFAULT_SELECTION_CONFIG: SelectionConfig;
|
|
106
|
+
interface SelectionBounds {
|
|
107
|
+
x: number;
|
|
108
|
+
y: number;
|
|
109
|
+
width: number;
|
|
110
|
+
height: number;
|
|
111
|
+
}
|
|
112
|
+
declare class SelectionRenderer {
|
|
113
|
+
private material;
|
|
114
|
+
private mesh;
|
|
115
|
+
private scene;
|
|
116
|
+
private camera;
|
|
117
|
+
constructor();
|
|
118
|
+
setConfig(config: Partial<SelectionConfig>): void;
|
|
119
|
+
setSize(resolution: THREE.Vector2, dpr: number): void;
|
|
120
|
+
render(renderer: THREE.WebGLRenderer, cameraX: number, cameraY: number, zoom: number, selected: SelectionBounds[], hovered: SelectionBounds | null, guides?: SnapGuide[], spacings?: EqualSpacingIndicator[]): void;
|
|
121
|
+
dispose(): void;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
interface Command {
|
|
125
|
+
execute(world: World): void;
|
|
126
|
+
undo(world: World): void;
|
|
127
|
+
}
|
|
128
|
+
declare class CommandBuffer {
|
|
129
|
+
private undoStack;
|
|
130
|
+
private redoStack;
|
|
131
|
+
private currentGroup;
|
|
132
|
+
/** Start grouping commands (e.g., on pointerdown). All commands until endGroup() are one undo step. */
|
|
133
|
+
beginGroup(): void;
|
|
134
|
+
/** Execute a command and record it for undo. */
|
|
135
|
+
execute(command: Command, world: World): void;
|
|
136
|
+
/** Close the current group — all commands since beginGroup() become one undo step. */
|
|
137
|
+
endGroup(): void;
|
|
138
|
+
/** Undo the last command group. */
|
|
139
|
+
undo(world: World): boolean;
|
|
140
|
+
/** Redo the last undone command group. */
|
|
141
|
+
redo(world: World): boolean;
|
|
142
|
+
canUndo(): boolean;
|
|
143
|
+
canRedo(): boolean;
|
|
144
|
+
clear(): void;
|
|
145
|
+
get undoSize(): number;
|
|
146
|
+
get redoSize(): number;
|
|
147
|
+
}
|
|
148
|
+
declare class MoveCommand implements Command {
|
|
149
|
+
private entityIds;
|
|
150
|
+
private dx;
|
|
151
|
+
private dy;
|
|
152
|
+
private transformType;
|
|
153
|
+
constructor(entityIds: EntityId[], dx: number, dy: number, transformType: ComponentType<{
|
|
154
|
+
x: number;
|
|
155
|
+
y: number;
|
|
156
|
+
}>);
|
|
157
|
+
execute(world: World): void;
|
|
158
|
+
undo(world: World): void;
|
|
159
|
+
}
|
|
160
|
+
declare class ResizeCommand implements Command {
|
|
161
|
+
private entityId;
|
|
162
|
+
private before;
|
|
163
|
+
private after;
|
|
164
|
+
private transformType;
|
|
165
|
+
static readonly MIN_SIZE = 20;
|
|
166
|
+
constructor(entityId: EntityId, before: {
|
|
167
|
+
x: number;
|
|
168
|
+
y: number;
|
|
169
|
+
width: number;
|
|
170
|
+
height: number;
|
|
171
|
+
}, after: {
|
|
172
|
+
x: number;
|
|
173
|
+
y: number;
|
|
174
|
+
width: number;
|
|
175
|
+
height: number;
|
|
176
|
+
}, transformType: ComponentType<{
|
|
177
|
+
x: number;
|
|
178
|
+
y: number;
|
|
179
|
+
width: number;
|
|
180
|
+
height: number;
|
|
181
|
+
}>);
|
|
182
|
+
execute(world: World): void;
|
|
183
|
+
undo(world: World): void;
|
|
184
|
+
}
|
|
185
|
+
declare class SetComponentCommand<T> implements Command {
|
|
186
|
+
private entityId;
|
|
187
|
+
private type;
|
|
188
|
+
private before;
|
|
189
|
+
private after;
|
|
190
|
+
constructor(entityId: EntityId, type: ComponentType<T>, before: Partial<T>, after: Partial<T>);
|
|
191
|
+
execute(world: World): void;
|
|
192
|
+
undo(world: World): void;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
interface NavigationFrame {
|
|
196
|
+
containerId: EntityId | null;
|
|
197
|
+
camera: {
|
|
198
|
+
x: number;
|
|
199
|
+
y: number;
|
|
200
|
+
zoom: number;
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
declare const CameraResource: ResourceType<{
|
|
204
|
+
x: number;
|
|
205
|
+
y: number;
|
|
206
|
+
zoom: number;
|
|
207
|
+
}>;
|
|
208
|
+
declare const ViewportResource: ResourceType<{
|
|
209
|
+
width: number;
|
|
210
|
+
height: number;
|
|
211
|
+
dpr: number;
|
|
212
|
+
}>;
|
|
213
|
+
declare const ZoomConfigResource: ResourceType<{
|
|
214
|
+
min: number;
|
|
215
|
+
max: number;
|
|
216
|
+
}>;
|
|
217
|
+
declare const BreakpointConfigResource: ResourceType<{
|
|
218
|
+
micro: number;
|
|
219
|
+
compact: number;
|
|
220
|
+
normal: number;
|
|
221
|
+
expanded: number;
|
|
222
|
+
}>;
|
|
223
|
+
declare const NavigationStackResource: ResourceType<{
|
|
224
|
+
frames: NavigationFrame[];
|
|
225
|
+
changed: boolean;
|
|
226
|
+
}>;
|
|
227
|
+
type Breakpoint = 'micro' | 'compact' | 'normal' | 'expanded' | 'detailed';
|
|
228
|
+
|
|
229
|
+
interface Vec2 {
|
|
230
|
+
x: number;
|
|
231
|
+
y: number;
|
|
232
|
+
}
|
|
233
|
+
interface Rect {
|
|
234
|
+
x: number;
|
|
235
|
+
y: number;
|
|
236
|
+
width: number;
|
|
237
|
+
height: number;
|
|
238
|
+
}
|
|
239
|
+
interface AABB {
|
|
240
|
+
minX: number;
|
|
241
|
+
minY: number;
|
|
242
|
+
maxX: number;
|
|
243
|
+
maxY: number;
|
|
244
|
+
}
|
|
245
|
+
/** Convert WorldBounds-shaped data to AABB */
|
|
246
|
+
declare function worldBoundsToAABB(wb: {
|
|
247
|
+
worldX: number;
|
|
248
|
+
worldY: number;
|
|
249
|
+
worldWidth: number;
|
|
250
|
+
worldHeight: number;
|
|
251
|
+
}): AABB;
|
|
252
|
+
/** Test if two AABBs overlap */
|
|
253
|
+
declare function intersectsAABB(a: AABB, b: AABB): boolean;
|
|
254
|
+
/** Test if a point is inside an AABB */
|
|
255
|
+
declare function pointInAABB(px: number, py: number, a: AABB): boolean;
|
|
256
|
+
/** Convert screen coordinates to world coordinates */
|
|
257
|
+
declare function screenToWorld(screenX: number, screenY: number, camera: {
|
|
258
|
+
x: number;
|
|
259
|
+
y: number;
|
|
260
|
+
zoom: number;
|
|
261
|
+
}): Vec2;
|
|
262
|
+
/** Convert world coordinates to screen coordinates */
|
|
263
|
+
declare function worldToScreen(worldX: number, worldY: number, camera: {
|
|
264
|
+
x: number;
|
|
265
|
+
y: number;
|
|
266
|
+
zoom: number;
|
|
267
|
+
}): Vec2;
|
|
268
|
+
/** Clamp a value between min and max */
|
|
269
|
+
declare function clamp(value: number, min: number, max: number): number;
|
|
270
|
+
|
|
271
|
+
interface SpatialEntry extends AABB {
|
|
272
|
+
entityId: EntityId;
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Spatial index backed by an R-tree (rbush).
|
|
276
|
+
* Stores world-space AABBs for fast viewport culling and hit testing.
|
|
277
|
+
*/
|
|
278
|
+
declare class SpatialIndex {
|
|
279
|
+
private tree;
|
|
280
|
+
private entries;
|
|
281
|
+
upsert(entityId: EntityId, bounds: AABB): void;
|
|
282
|
+
remove(entityId: EntityId): void;
|
|
283
|
+
/** Query all entries intersecting the given AABB */
|
|
284
|
+
search(bounds: AABB): SpatialEntry[];
|
|
285
|
+
/** Find the topmost entity at a point (by z-order — caller sorts) */
|
|
286
|
+
searchPoint(x: number, y: number, tolerance?: number): SpatialEntry[];
|
|
287
|
+
clear(): void;
|
|
288
|
+
get size(): number;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
declare const SpatialIndexResource: ResourceType<{
|
|
292
|
+
instance: SpatialIndex | null;
|
|
293
|
+
}>;
|
|
294
|
+
type PointerDirective = {
|
|
295
|
+
action: 'passthrough';
|
|
296
|
+
} | {
|
|
297
|
+
action: 'passthrough-track-drag';
|
|
298
|
+
} | {
|
|
299
|
+
action: 'capture-drag';
|
|
300
|
+
} | {
|
|
301
|
+
action: 'capture-resize';
|
|
302
|
+
handle: ResizeHandlePos;
|
|
303
|
+
} | {
|
|
304
|
+
action: 'capture-marquee';
|
|
305
|
+
};
|
|
306
|
+
type ResizeHandlePos = 'n' | 's' | 'e' | 'w' | 'ne' | 'nw' | 'se' | 'sw';
|
|
307
|
+
interface Modifiers {
|
|
308
|
+
shift: boolean;
|
|
309
|
+
ctrl: boolean;
|
|
310
|
+
alt: boolean;
|
|
311
|
+
meta: boolean;
|
|
312
|
+
}
|
|
313
|
+
interface VisibleEntity {
|
|
314
|
+
entityId: EntityId;
|
|
315
|
+
worldX: number;
|
|
316
|
+
worldY: number;
|
|
317
|
+
worldWidth: number;
|
|
318
|
+
worldHeight: number;
|
|
319
|
+
breakpoint: Breakpoint;
|
|
320
|
+
zIndex: number;
|
|
321
|
+
surface: string;
|
|
322
|
+
widgetType: string;
|
|
323
|
+
}
|
|
324
|
+
interface FrameChanges {
|
|
325
|
+
positionsChanged: EntityId[];
|
|
326
|
+
breakpointsChanged: EntityId[];
|
|
327
|
+
entered: EntityId[];
|
|
328
|
+
exited: EntityId[];
|
|
329
|
+
cameraChanged: boolean;
|
|
330
|
+
navigationChanged: boolean;
|
|
331
|
+
selectionChanged: boolean;
|
|
332
|
+
}
|
|
333
|
+
interface AddWidgetOptions {
|
|
334
|
+
type: string;
|
|
335
|
+
position: {
|
|
336
|
+
x: number;
|
|
337
|
+
y: number;
|
|
338
|
+
};
|
|
339
|
+
size: {
|
|
340
|
+
width: number;
|
|
341
|
+
height: number;
|
|
342
|
+
};
|
|
343
|
+
rotation?: number;
|
|
344
|
+
data?: Record<string, unknown>;
|
|
345
|
+
surface?: 'dom' | 'webgl';
|
|
346
|
+
zIndex?: number;
|
|
347
|
+
selectable?: boolean;
|
|
348
|
+
draggable?: boolean;
|
|
349
|
+
resizable?: boolean;
|
|
350
|
+
parent?: EntityId;
|
|
351
|
+
}
|
|
352
|
+
interface LayoutEngineConfig {
|
|
353
|
+
maxEntities?: number;
|
|
354
|
+
zoom?: {
|
|
355
|
+
min: number;
|
|
356
|
+
max: number;
|
|
357
|
+
};
|
|
358
|
+
breakpoints?: {
|
|
359
|
+
micro: number;
|
|
360
|
+
compact: number;
|
|
361
|
+
normal: number;
|
|
362
|
+
expanded: number;
|
|
363
|
+
};
|
|
364
|
+
}
|
|
365
|
+
interface LayoutEngine {
|
|
366
|
+
readonly world: World;
|
|
367
|
+
createEntity(inits?: ComponentInit[]): EntityId;
|
|
368
|
+
addWidget(opts: AddWidgetOptions): EntityId;
|
|
369
|
+
destroyEntity(id: EntityId): void;
|
|
370
|
+
get<T>(entity: EntityId, type: ComponentType<T>): T | undefined;
|
|
371
|
+
set<T>(entity: EntityId, type: ComponentType<T>, data: Partial<T>): void;
|
|
372
|
+
has(entity: EntityId, type: ComponentType | TagType): boolean;
|
|
373
|
+
registerSystem(system: SystemDef): void;
|
|
374
|
+
removeSystem(name: string): void;
|
|
375
|
+
getCamera(): {
|
|
376
|
+
x: number;
|
|
377
|
+
y: number;
|
|
378
|
+
zoom: number;
|
|
379
|
+
};
|
|
380
|
+
panBy(dx: number, dy: number): void;
|
|
381
|
+
panTo(worldX: number, worldY: number): void;
|
|
382
|
+
zoomAtPoint(screenX: number, screenY: number, delta: number): void;
|
|
383
|
+
zoomTo(zoom: number): void;
|
|
384
|
+
zoomToFit(entityIds?: EntityId[], padding?: number): void;
|
|
385
|
+
setViewport(width: number, height: number, dpr?: number): void;
|
|
386
|
+
handlePointerDown(screenX: number, screenY: number, button: number, modifiers: Modifiers): PointerDirective;
|
|
387
|
+
handlePointerMove(screenX: number, screenY: number, modifiers: Modifiers): PointerDirective;
|
|
388
|
+
handlePointerUp(): PointerDirective;
|
|
389
|
+
getSelectedEntities(): EntityId[];
|
|
390
|
+
getHoveredEntity(): EntityId | null;
|
|
391
|
+
enterContainer(entity: EntityId): void;
|
|
392
|
+
exitContainer(): void;
|
|
393
|
+
getActiveContainer(): EntityId | null;
|
|
394
|
+
getNavigationDepth(): number;
|
|
395
|
+
execute(command: Command): void;
|
|
396
|
+
beginCommandGroup(): void;
|
|
397
|
+
endCommandGroup(): void;
|
|
398
|
+
undo(): boolean;
|
|
399
|
+
redo(): boolean;
|
|
400
|
+
canUndo(): boolean;
|
|
401
|
+
canRedo(): boolean;
|
|
402
|
+
markDirty(): void;
|
|
403
|
+
tick(): void;
|
|
404
|
+
flushIfDirty(): boolean;
|
|
405
|
+
getVisibleEntities(): VisibleEntity[];
|
|
406
|
+
getFrameChanges(): FrameChanges;
|
|
407
|
+
getSpatialIndex(): SpatialIndex;
|
|
408
|
+
getSnapGuides(): SnapGuide[];
|
|
409
|
+
getEqualSpacing(): EqualSpacingIndicator[];
|
|
410
|
+
setSnapEnabled(on: boolean): void;
|
|
411
|
+
setSnapThreshold(worldPx: number): void;
|
|
412
|
+
readonly profiler: Profiler;
|
|
413
|
+
onFrame(handler: () => void): Unsubscribe;
|
|
414
|
+
destroy(): void;
|
|
415
|
+
}
|
|
416
|
+
declare function createLayoutEngine(config?: LayoutEngineConfig): LayoutEngine;
|
|
417
|
+
/** @deprecated Use LayoutEngine instead */
|
|
418
|
+
type CanvasEngine = LayoutEngine;
|
|
419
|
+
/** @deprecated Use LayoutEngineConfig instead */
|
|
420
|
+
type CanvasEngineConfig = LayoutEngineConfig;
|
|
421
|
+
/** @deprecated Use createLayoutEngine instead */
|
|
422
|
+
declare const createCanvasEngine: typeof createLayoutEngine;
|
|
423
|
+
|
|
424
|
+
declare const EngineProvider: react.Provider<LayoutEngine | null>;
|
|
425
|
+
declare const ContainerRefProvider: react.Provider<react.RefObject<HTMLDivElement | null> | null>;
|
|
426
|
+
declare function useContainerRef(): React.RefObject<HTMLDivElement | null> | null;
|
|
427
|
+
declare function useLayoutEngine(): LayoutEngine;
|
|
428
|
+
/** @deprecated Use useLayoutEngine instead */
|
|
429
|
+
declare const useEngine: typeof useLayoutEngine;
|
|
430
|
+
type WidgetSurface = 'dom' | 'webgl';
|
|
431
|
+
interface ResolvedWidget {
|
|
432
|
+
component: React.ComponentType<{
|
|
433
|
+
entityId: EntityId;
|
|
434
|
+
}>;
|
|
435
|
+
surface: WidgetSurface;
|
|
436
|
+
}
|
|
437
|
+
type WidgetResolver = (entityId: EntityId, widgetType: string) => ResolvedWidget | null;
|
|
438
|
+
declare const WidgetResolverProvider: react.Provider<WidgetResolver | null>;
|
|
439
|
+
declare function useWidgetResolver(): WidgetResolver | null;
|
|
440
|
+
|
|
441
|
+
export { useLayoutEngine as $, type AABB as A, type Breakpoint as B, ContainerRefProvider as C, DEFAULT_GRID_CONFIG as D, EngineProvider as E, type FrameChanges as F, type GridConfig as G, type VisibleEntity as H, WidgetResolverProvider as I, type WidgetSurface as J, clamp as K, type LayoutEngine as L, type Modifiers as M, type NavigationFrame as N, createCanvasEngine as O, type PointerDirective as P, createLayoutEngine as Q, type ResolvedWidget as R, type SelectionBounds as S, intersectsAABB as T, pointInAABB as U, type Vec2 as V, type WidgetResolver as W, screenToWorld as X, useContainerRef as Y, ZoomConfigResource as Z, useEngine as _, DEFAULT_SELECTION_CONFIG as a, useWidgetResolver as a0, worldBoundsToAABB as a1, worldToScreen as a2, GridRenderer as b, type SelectionConfig as c, SelectionRenderer as d, SpatialIndex as e, computeSnapGuides as f, type AddWidgetOptions as g, BreakpointConfigResource as h, CameraResource as i, type CanvasEngine as j, type CanvasEngineConfig as k, type Command as l, CommandBuffer as m, type EntityBounds as n, type EqualSpacingIndicator as o, type LayoutEngineConfig as p, MoveCommand as q, NavigationStackResource as r, type Rect as s, ResizeCommand as t, type ResizeHandlePos as u, SetComponentCommand as v, type SnapGuide as w, type SnapResult as x, SpatialIndexResource as y, ViewportResource as z };
|