@jamesyong42/infinite-canvas 1.2.0 → 1.3.0

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.
Files changed (56) hide show
  1. package/README.md +65 -0
  2. package/dist/advanced.cjs +61 -24
  3. package/dist/advanced.cjs.map +1 -1
  4. package/dist/advanced.d.cts +180 -64
  5. package/dist/advanced.d.cts.map +1 -1
  6. package/dist/advanced.d.mts +180 -64
  7. package/dist/advanced.d.mts.map +1 -1
  8. package/dist/advanced.mjs +29 -12
  9. package/dist/advanced.mjs.map +1 -1
  10. package/dist/devtools.cjs +22 -22
  11. package/dist/devtools.cjs.map +1 -1
  12. package/dist/devtools.d.cts +2 -2
  13. package/dist/devtools.d.cts.map +1 -1
  14. package/dist/devtools.d.mts +2 -2
  15. package/dist/devtools.d.mts.map +1 -1
  16. package/dist/devtools.mjs +2 -2
  17. package/dist/devtools.mjs.map +1 -1
  18. package/dist/{hooks-BwY7rRHg.mjs → ecs-3kimUV5Z.mjs} +238 -74
  19. package/dist/ecs-3kimUV5Z.mjs.map +1 -0
  20. package/dist/{hooks-DHShH86C.cjs → ecs-B4QrqfvQ.cjs} +320 -108
  21. package/dist/ecs-B4QrqfvQ.cjs.map +1 -0
  22. package/dist/hooks-CtP02JNt.cjs +3762 -0
  23. package/dist/hooks-CtP02JNt.cjs.map +1 -0
  24. package/dist/hooks-gsQDDE56.mjs +3494 -0
  25. package/dist/hooks-gsQDDE56.mjs.map +1 -0
  26. package/dist/index-3GY7T8JM.d.mts +480 -0
  27. package/dist/index-3GY7T8JM.d.mts.map +1 -0
  28. package/dist/index-B7B1tRPl.d.cts +480 -0
  29. package/dist/index-B7B1tRPl.d.cts.map +1 -0
  30. package/dist/index-DSdbSQ_t.d.cts +1451 -0
  31. package/dist/index-DSdbSQ_t.d.cts.map +1 -0
  32. package/dist/index-Dj9odADH.d.mts +1451 -0
  33. package/dist/index-Dj9odADH.d.mts.map +1 -0
  34. package/dist/index.cjs +3865 -643
  35. package/dist/index.cjs.map +1 -1
  36. package/dist/index.d.cts +315 -138
  37. package/dist/index.d.cts.map +1 -1
  38. package/dist/index.d.mts +315 -138
  39. package/dist/index.d.mts.map +1 -1
  40. package/dist/index.mjs +3767 -571
  41. package/dist/index.mjs.map +1 -1
  42. package/package.json +1 -1
  43. package/dist/SelectionRenderer-CR2PBQwx.d.cts +0 -105
  44. package/dist/SelectionRenderer-CR2PBQwx.d.cts.map +0 -1
  45. package/dist/SelectionRenderer-DlsBstAq.d.mts +0 -105
  46. package/dist/SelectionRenderer-DlsBstAq.d.mts.map +0 -1
  47. package/dist/WebGLWidgetLayer-BBMuwzHq.cjs +0 -3560
  48. package/dist/WebGLWidgetLayer-BBMuwzHq.cjs.map +0 -1
  49. package/dist/WebGLWidgetLayer-C3p1tnpm.mjs +0 -3375
  50. package/dist/WebGLWidgetLayer-C3p1tnpm.mjs.map +0 -1
  51. package/dist/engine-BfbvWXSk.d.mts +0 -982
  52. package/dist/engine-BfbvWXSk.d.mts.map +0 -1
  53. package/dist/engine-CCjuFMC-.d.cts +0 -982
  54. package/dist/engine-CCjuFMC-.d.cts.map +0 -1
  55. package/dist/hooks-BwY7rRHg.mjs.map +0 -1
  56. package/dist/hooks-DHShH86C.cjs.map +0 -1
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks-CtP02JNt.cjs","names":["RBush","RBushImport","Widget","Transform2D","Vector3","Vector2","ShaderMaterial","Vector4","WebGLRenderTarget","SRGBColorSpace","PlaneGeometry","OrthographicCamera","Mesh","Transform2D","Dragging","Widget","Card","CardOverlapHotPoint","OverlapCandidate","OverlapTarget","useLayoutEngine","Scene","OrthographicCamera","useComponent","Transform2D","Widget","Active","Visible","Culled","Widget","THREE","Canvas","EngineProvider","vertexShader","fragmentShader","THREE","vertexShader","fragmentShader","THREE","THREE","THREE","useLayoutEngine","useTag","Dragging","useComponent","Card","OverlapCandidate","OverlapTarget","CardOverlapHotPoint","Transform2D","useLayoutEngine","useComponent","Widget","Transform2D","useLayoutEngine","useComponent"],"sources":["../src/profiler/Profiler.ts","../../../node_modules/.pnpm/quickselect@3.0.0/node_modules/quickselect/index.js","../../../node_modules/.pnpm/rbush@4.0.1/node_modules/rbush/index.js","../src/ecs/spatial/SpatialIndex.ts","../src/ecs/spatial/snap.ts","../src/react/context/container-ref-context.ts","../src/react/context/widget-resolver-context.ts","../src/react/input/debug.ts","../src/react/input/r3f/createR3FEventManager.ts","../src/r3f/compositor/CompositionMaterial.ts","../src/r3f/compositor/CompositorContext.tsx","../src/r3f/compositor/eviction.ts","../src/r3f/compositor/ResourceRegistry.ts","../src/r3f/compositor/state.ts","../src/r3f/compositor/WidgetRenderTargetPool.ts","../src/r3f/compositor/ZoomBands.ts","../src/r3f/compositor/Compositor.tsx","../src/r3f/compositor/VirtualWidget.tsx","../src/r3f/compositor/WidgetRegistry.ts","../src/r3f/compositor/WidgetStateMachine.tsx","../src/r3f/EngineInvalidator.tsx","../src/r3f/ProfilerProbe.tsx","../src/r3f/R3FManager.tsx","../src/webgl/renderers/GridRenderer.ts","../src/webgl/renderers/SelectionRenderer.ts","../src/webgl/renderers/SnapGuideRenderer.ts","../src/webgl/WebGLManager.ts","../src/react/widgets/CardChrome.tsx","../src/react/overlays/SelectionOverlaySlot.tsx","../src/react/widgets/WidgetSlot.tsx","../src/r3f/compositor/hooks.ts"],"sourcesContent":["/**\n * Multi-layer performance profiler.\n *\n * Tracks three independent rendering concerns:\n *\n * 1. ECS tick — systems, visibility, entity counts. Driven by `engine.tick()`.\n * 2. WebGL engine pass — the library's grid + selection renderers. Runs\n * inside the rAF loop immediately after each ECS tick, so samples share\n * the ECS ring and carry matched tick ids.\n * 3. R3F canvas — the user's 3D widgets inside the shared `<Canvas>`. Runs\n * continuously at rAF cadence regardless of engine ticks, so it has its\n * own ring.\n *\n * All methods are no-ops when disabled — zero cost for production builds.\n * User Timing API marks are emitted when enabled so traces line up with\n * Chrome DevTools' Performance panel.\n */\n\n// === Sample shapes ===\n\nexport interface TickSample {\n\ttick: number;\n\ttimestamp: number;\n\t/** Total tick duration (ms) = ECS tick + WebGL engine pass. */\n\ttotalMs: number;\n\tecs: {\n\t\t/** Per-system durations (ms). */\n\t\tsystems: Record<string, number>;\n\t\t/** Visible entity computation (ms). */\n\t\tvisibilityMs: number;\n\t\t/** Entity counts at this frame. */\n\t\tentityCount: number;\n\t\tvisibleCount: number;\n\t};\n\twebgl: {\n\t\t/** Grid renderer pass duration (ms). */\n\t\tgridMs: number;\n\t\t/** Selection + hover + snap-guide render pass duration (ms). */\n\t\tselectionMs: number;\n\t\t/** Total `renderer.info.render.calls` across all passes this tick. */\n\t\tdrawCalls: number;\n\t\t/** Total `renderer.info.render.triangles` across all passes this tick. */\n\t\ttriangles: number;\n\t\t/** Selection frames drawn this tick. */\n\t\tselectionFrames: number;\n\t\t/** Snap guides drawn this tick. */\n\t\tsnapGuides: number;\n\t\t/** Equal-spacing indicators drawn this tick. */\n\t\tspacingIndicators: number;\n\t\t/** DOM slot.transform writes this tick (from changes.positionsChanged). */\n\t\tdomPositionsUpdated: number;\n\t};\n}\n\n/** Per-phase widget count snapshot for the compositor state machine (RFC-002). */\nexport interface R3FPhaseHistogram {\n\thot: number;\n\twarm: number;\n\tcold: number;\n\twaking: number;\n\tdormant: number;\n}\n\nexport interface R3FSample {\n\ttimestamp: number;\n\t/** Delta since the previous R3F frame (ms). */\n\tdtMs: number;\n\t/** three.js renderer.info snapshot. */\n\tdrawCalls: number;\n\ttriangles: number;\n\tpoints: number;\n\tlines: number;\n\tprograms: number;\n\tgeometries: number;\n\ttextures: number;\n\tactiveWidgets: number;\n\t/**\n\t * Number of per-widget FBO repaints this frame. Zero before the compositor\n\t * lands (RFC-002 Phase 4); zero on idle frames once it has.\n\t */\n\twidgetsRepainted: number;\n\t/** Total bytes consumed by the widget render-target pool. */\n\tfboBytes: number;\n\t/** Compositor state-machine phase histogram across all R3F widgets. */\n\tphases: R3FPhaseHistogram;\n\t/** GPU time spent on per-widget paints this frame (ms). Optional — populated only when timestamp queries are available (WebGL `WEBGL_timer_query` or WebGPU). */\n\tgpuPaintMs?: number;\n\t/** GPU time spent on the composition pass this frame (ms). Optional — same caveat as `gpuPaintMs`. */\n\tgpuCompositeMs?: number;\n}\n\n// === Stats shapes ===\n\nexport interface FrameTimeStats {\n\tavg: number;\n\tp50: number;\n\tp95: number;\n\tp99: number;\n\tmax: number;\n}\n\nexport interface EcsStats {\n\tfps: number;\n\tframeTime: FrameTimeStats;\n\tsystemAvg: Record<string, number>;\n\tsystemP95: Record<string, number>;\n\t/** Avg frame time as % of 16.67ms (60 fps). */\n\tbudgetUsed: number;\n\tsampleCount: number;\n}\n\nexport interface WebGLStats {\n\t/** Tick cadence — matches ECS since both fire together. */\n\tfps: number;\n\t/** Avg combined pass time (ms) and percentile breakdown. */\n\tframeTime: FrameTimeStats;\n\t/** Avg combined pass time as % of 16.67ms budget. */\n\tbudgetUsed: number;\n\tgridAvg: number;\n\tgridP95: number;\n\tselectionAvg: number;\n\tselectionP95: number;\n\tavgDrawCalls: number;\n\tavgTriangles: number;\n\tavgSelectionFrames: number;\n\tavgSnapGuides: number;\n\tavgDomUpdates: number;\n\tsampleCount: number;\n}\n\nexport interface R3FStats {\n\tfps: number;\n\tframeTime: FrameTimeStats;\n\tavgDrawCalls: number;\n\tavgTriangles: number;\n\t/** Latest snapshots — these don't average meaningfully. */\n\tprograms: number;\n\tgeometries: number;\n\ttextures: number;\n\tactiveWidgets: number;\n\t/** Avg per-widget repaints per frame across the sample window. */\n\tavgWidgetsRepainted: number;\n\t/** Latest FBO pool memory usage (bytes). */\n\tfboBytes: number;\n\t/** Latest phase histogram across the R3F widget set. */\n\tphases: R3FPhaseHistogram;\n\t/** Avg GPU paint time per frame (ms). Undefined if no samples carried timestamps. */\n\tavgGpuPaintMs?: number;\n\t/** Avg GPU composition time per frame (ms). */\n\tavgGpuCompositeMs?: number;\n\tsampleCount: number;\n}\n\nexport interface ProfilerStats {\n\tecs: EcsStats;\n\twebgl: WebGLStats;\n\tr3f: R3FStats;\n}\n\n// === Implementation ===\n\nconst TICK_RING_SIZE = 300; // ~5 s at 60 fps\nconst R3F_RING_SIZE = 300;\n\n/** What's being timed inside the library's WebGL engine pass. */\nexport type WebGLPass = 'grid' | 'selection' | 'snap-guides';\n\nexport class Profiler {\n\tprivate enabled = false;\n\n\t// Tick ring (ECS + engine WebGL pass — matched cadence).\n\tprivate tickRing: TickSample[] = [];\n\tprivate tickWrite = 0;\n\tprivate tickFilled = false;\n\n\t// R3F ring (continuous, independent cadence).\n\tprivate r3fRing: R3FSample[] = [];\n\tprivate r3fWrite = 0;\n\tprivate r3fFilled = false;\n\n\t// Scratch state for the currently-building tick sample. Only the ECS\n\t// fields are kept between beginFrame and endFrame; the WebGL pass runs\n\t// AFTER endFrame in the rAF loop, so WebGL stats are written directly\n\t// into the ring's most recent sample via getMostRecentTickSample().\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) this.clear();\n\t}\n\n\tisEnabled(): boolean {\n\t\treturn this.enabled;\n\t}\n\n\t// === ECS tick instrumentation ===\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 ECS 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:sys:${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\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// === WebGL engine pass instrumentation ===\n\n\t/** Call right before the named engine WebGL pass renders. */\n\tbeginWebGL(pass: WebGLPass) {\n\t\tif (!this.enabled) return;\n\t\tperformance.mark(`ic-gl-${pass}-start`);\n\t}\n\n\t/**\n\t * Call right after the named engine WebGL pass renders.\n\t *\n\t * The WebGL pass runs AFTER endFrame has flushed the tick sample to the\n\t * ring, so we mutate the most-recent ring entry in place instead of\n\t * writing to scratch state (which would be wiped by the next beginFrame\n\t * before ever being read).\n\t */\n\tendWebGL(pass: WebGLPass) {\n\t\tif (!this.enabled) return;\n\t\tperformance.mark(`ic-gl-${pass}-end`);\n\t\ttry {\n\t\t\tconst measure = performance.measure(\n\t\t\t\t`ic:gl:${pass}`,\n\t\t\t\t`ic-gl-${pass}-start`,\n\t\t\t\t`ic-gl-${pass}-end`,\n\t\t\t);\n\t\t\tconst sample = this.getMostRecentTickSample();\n\t\t\tif (sample) {\n\t\t\t\tif (pass === 'grid') sample.webgl.gridMs = measure.duration;\n\t\t\t\telse sample.webgl.selectionMs = measure.duration;\n\t\t\t}\n\t\t} catch {\n\t\t\t// marks may be cleared\n\t\t}\n\t\tperformance.clearMarks(`ic-gl-${pass}-start`);\n\t\tperformance.clearMarks(`ic-gl-${pass}-end`);\n\t}\n\n\t/**\n\t * Record WebGL engine pass counters for the current tick. `drawCalls` and\n\t * `triangles` should be the totals from `renderer.info.render` accumulated\n\t * across all engine passes in this tick (grid + selection). Callers must\n\t * reset `renderer.info` at the start of the tick (with `autoReset=false`)\n\t * so these values cover both passes.\n\t *\n\t * Called from the rAF loop AFTER engine.tick() / endFrame, so it targets\n\t * the most-recent ring sample directly (see {@link endWebGL}).\n\t */\n\trecordWebGLStats(stats: {\n\t\tdrawCalls: number;\n\t\ttriangles: number;\n\t\tselectionFrames: number;\n\t\tsnapGuides: number;\n\t\tspacingIndicators: number;\n\t\tdomPositionsUpdated: number;\n\t}) {\n\t\tif (!this.enabled) return;\n\t\tconst sample = this.getMostRecentTickSample();\n\t\tif (!sample) return;\n\t\tsample.webgl.drawCalls = stats.drawCalls;\n\t\tsample.webgl.triangles = stats.triangles;\n\t\tsample.webgl.selectionFrames = stats.selectionFrames;\n\t\tsample.webgl.snapGuides = stats.snapGuides;\n\t\tsample.webgl.spacingIndicators = stats.spacingIndicators;\n\t\tsample.webgl.domPositionsUpdated = stats.domPositionsUpdated;\n\t}\n\n\t/** Returns the most recently pushed tick sample, or null if the ring is empty. */\n\tprivate getMostRecentTickSample(): TickSample | null {\n\t\tconst n = this.tickRing.length;\n\t\tif (n === 0) return null;\n\t\tconst idx = (this.tickWrite - 1 + TICK_RING_SIZE) % TICK_RING_SIZE;\n\t\treturn this.tickRing[idx % n] ?? null;\n\t}\n\n\t/** Call at the end of engine.tick() — flushes a TickSample to the ring. */\n\tendFrame(entityCount: number, visibleCount: number) {\n\t\tif (!this.enabled) return;\n\t\tperformance.mark('ic-frame-end');\n\n\t\tlet totalMs: number;\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\t// `totalMs` is the ECS tick only — the WebGL engine pass runs AFTER\n\t\t// endFrame and populates the webgl fields in place (see endWebGL /\n\t\t// recordWebGLStats). Consumers can combine\n\t\t// (ecs.totalMs + webgl.gridMs + webgl.selectionMs) for a compound\n\t\t// figure.\n\t\tconst sample: TickSample = {\n\t\t\ttick: this.currentTick,\n\t\t\ttimestamp: performance.now(),\n\t\t\ttotalMs,\n\t\t\tecs: {\n\t\t\t\tsystems: { ...this.currentSystems },\n\t\t\t\tvisibilityMs: this.visibilityMs,\n\t\t\t\tentityCount,\n\t\t\t\tvisibleCount,\n\t\t\t},\n\t\t\twebgl: {\n\t\t\t\tgridMs: 0,\n\t\t\t\tselectionMs: 0,\n\t\t\t\tdrawCalls: 0,\n\t\t\t\ttriangles: 0,\n\t\t\t\tselectionFrames: 0,\n\t\t\t\tsnapGuides: 0,\n\t\t\t\tspacingIndicators: 0,\n\t\t\t\tdomPositionsUpdated: 0,\n\t\t\t},\n\t\t};\n\n\t\tif (this.tickRing.length < TICK_RING_SIZE) {\n\t\t\tthis.tickRing.push(sample);\n\t\t} else {\n\t\t\tthis.tickRing[this.tickWrite] = sample;\n\t\t}\n\t\tthis.tickWrite = (this.tickWrite + 1) % TICK_RING_SIZE;\n\t\tif (this.tickRing.length >= TICK_RING_SIZE) this.tickFilled = true;\n\t}\n\n\t// === R3F canvas instrumentation ===\n\n\t/**\n\t * Push one R3F frame sample. Called from the R3F canvas via a probe\n\t * component that has access to `useThree`.\n\t */\n\trecordR3FFrame(sample: Omit<R3FSample, 'timestamp'>) {\n\t\tif (!this.enabled) return;\n\t\tconst full: R3FSample = { ...sample, timestamp: performance.now() };\n\t\tif (this.r3fRing.length < R3F_RING_SIZE) {\n\t\t\tthis.r3fRing.push(full);\n\t\t} else {\n\t\t\tthis.r3fRing[this.r3fWrite] = full;\n\t\t}\n\t\tthis.r3fWrite = (this.r3fWrite + 1) % R3F_RING_SIZE;\n\t\tif (this.r3fRing.length >= R3F_RING_SIZE) this.r3fFilled = true;\n\t}\n\n\t// === Queries ===\n\n\t/** Get the last N tick samples (newest first). */\n\tgetSamples(count?: number): TickSample[] {\n\t\treturn readRing(this.tickRing, this.tickWrite, this.tickFilled, count);\n\t}\n\n\t/** Get the last N R3F samples (newest first). */\n\tgetR3FSamples(count?: number): R3FSample[] {\n\t\treturn readRing(this.r3fRing, this.r3fWrite, this.r3fFilled, count);\n\t}\n\n\t/** Compute rolling statistics across all three layers. */\n\tgetStats(): ProfilerStats {\n\t\treturn {\n\t\t\tecs: this.getEcsStats(),\n\t\t\twebgl: this.getWebGLStats(),\n\t\t\tr3f: this.getR3FStats(),\n\t\t};\n\t}\n\n\tprivate getEcsStats(): EcsStats {\n\t\tconst samples = this.tickRing;\n\t\tconst n = samples.length;\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\tconst frameTimes = samples.map((s) => s.totalMs).toSorted((a, b) => a - b);\n\t\tconst avg = mean(frameTimes);\n\n\t\tconst fps = ringFps(samples, this.tickWrite, this.tickFilled, TICK_RING_SIZE);\n\n\t\tconst systemNames = new Set<string>();\n\t\tfor (const s of samples) for (const k of Object.keys(s.ecs.systems)) systemNames.add(k);\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.ecs.systems[name] ?? 0).toSorted((a, b) => a - b);\n\t\t\tsystemAvg[name] = mean(times);\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\tprivate getWebGLStats(): WebGLStats {\n\t\tconst samples = this.tickRing;\n\t\tconst n = samples.length;\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\tbudgetUsed: 0,\n\t\t\t\tgridAvg: 0,\n\t\t\t\tgridP95: 0,\n\t\t\t\tselectionAvg: 0,\n\t\t\t\tselectionP95: 0,\n\t\t\t\tavgDrawCalls: 0,\n\t\t\t\tavgTriangles: 0,\n\t\t\t\tavgSelectionFrames: 0,\n\t\t\t\tavgSnapGuides: 0,\n\t\t\t\tavgDomUpdates: 0,\n\t\t\t\tsampleCount: 0,\n\t\t\t};\n\t\t}\n\t\tconst gridTimes = samples.map((s) => s.webgl.gridMs).toSorted((a, b) => a - b);\n\t\tconst selTimes = samples.map((s) => s.webgl.selectionMs).toSorted((a, b) => a - b);\n\t\tconst combinedTimes = samples\n\t\t\t.map((s) => s.webgl.gridMs + s.webgl.selectionMs)\n\t\t\t.toSorted((a, b) => a - b);\n\t\tconst combinedAvg = mean(combinedTimes);\n\t\tconst fps = ringFps(samples, this.tickWrite, this.tickFilled, TICK_RING_SIZE);\n\t\treturn {\n\t\t\tfps,\n\t\t\tframeTime: {\n\t\t\t\tavg: combinedAvg,\n\t\t\t\tp50: percentile(combinedTimes, 50),\n\t\t\t\tp95: percentile(combinedTimes, 95),\n\t\t\t\tp99: percentile(combinedTimes, 99),\n\t\t\t\tmax: combinedTimes[combinedTimes.length - 1],\n\t\t\t},\n\t\t\tbudgetUsed: (combinedAvg / 16.67) * 100,\n\t\t\tgridAvg: mean(gridTimes),\n\t\t\tgridP95: percentile(gridTimes, 95),\n\t\t\tselectionAvg: mean(selTimes),\n\t\t\tselectionP95: percentile(selTimes, 95),\n\t\t\tavgDrawCalls: mean(samples.map((s) => s.webgl.drawCalls)),\n\t\t\tavgTriangles: mean(samples.map((s) => s.webgl.triangles)),\n\t\t\tavgSelectionFrames: mean(samples.map((s) => s.webgl.selectionFrames)),\n\t\t\tavgSnapGuides: mean(samples.map((s) => s.webgl.snapGuides)),\n\t\t\tavgDomUpdates: mean(samples.map((s) => s.webgl.domPositionsUpdated)),\n\t\t\tsampleCount: n,\n\t\t};\n\t}\n\n\tprivate getR3FStats(): R3FStats {\n\t\tconst samples = this.r3fRing;\n\t\tconst n = samples.length;\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\tavgDrawCalls: 0,\n\t\t\t\tavgTriangles: 0,\n\t\t\t\tprograms: 0,\n\t\t\t\tgeometries: 0,\n\t\t\t\ttextures: 0,\n\t\t\t\tactiveWidgets: 0,\n\t\t\t\tavgWidgetsRepainted: 0,\n\t\t\t\tfboBytes: 0,\n\t\t\t\tphases: { hot: 0, warm: 0, cold: 0, waking: 0, dormant: 0 },\n\t\t\t\tsampleCount: 0,\n\t\t\t};\n\t\t}\n\t\tconst dts = samples.map((s) => s.dtMs).toSorted((a, b) => a - b);\n\t\tconst fps = ringFps(samples, this.r3fWrite, this.r3fFilled, R3F_RING_SIZE);\n\t\t// Latest snapshot for gauge-style values.\n\t\tconst latestIdx = this.r3fFilled ? (this.r3fWrite - 1 + R3F_RING_SIZE) % R3F_RING_SIZE : n - 1;\n\t\tconst latest = samples[latestIdx];\n\n\t\tconst gpuPaintSamples = samples.filter((s) => s.gpuPaintMs !== undefined);\n\t\tconst gpuCompositeSamples = samples.filter((s) => s.gpuCompositeMs !== undefined);\n\n\t\treturn {\n\t\t\tfps,\n\t\t\tframeTime: {\n\t\t\t\tavg: mean(dts),\n\t\t\t\tp50: percentile(dts, 50),\n\t\t\t\tp95: percentile(dts, 95),\n\t\t\t\tp99: percentile(dts, 99),\n\t\t\t\tmax: dts[dts.length - 1],\n\t\t\t},\n\t\t\tavgDrawCalls: mean(samples.map((s) => s.drawCalls)),\n\t\t\tavgTriangles: mean(samples.map((s) => s.triangles)),\n\t\t\tprograms: latest.programs,\n\t\t\tgeometries: latest.geometries,\n\t\t\ttextures: latest.textures,\n\t\t\tactiveWidgets: latest.activeWidgets,\n\t\t\tavgWidgetsRepainted: mean(samples.map((s) => s.widgetsRepainted)),\n\t\t\tfboBytes: latest.fboBytes,\n\t\t\tphases: latest.phases,\n\t\t\tavgGpuPaintMs:\n\t\t\t\tgpuPaintSamples.length > 0\n\t\t\t\t\t? mean(gpuPaintSamples.map((s) => s.gpuPaintMs as number))\n\t\t\t\t\t: undefined,\n\t\t\tavgGpuCompositeMs:\n\t\t\t\tgpuCompositeSamples.length > 0\n\t\t\t\t\t? mean(gpuCompositeSamples.map((s) => s.gpuCompositeMs as number))\n\t\t\t\t\t: undefined,\n\t\t\tsampleCount: n,\n\t\t};\n\t}\n\n\t/** Clear all collected data. */\n\tclear() {\n\t\tthis.tickRing = [];\n\t\tthis.tickWrite = 0;\n\t\tthis.tickFilled = false;\n\t\tthis.r3fRing = [];\n\t\tthis.r3fWrite = 0;\n\t\tthis.r3fFilled = false;\n\t}\n}\n\n// === Helpers ===\n\nfunction mean(xs: number[]): number {\n\tif (xs.length === 0) return 0;\n\tlet sum = 0;\n\tfor (const x of xs) sum += x;\n\treturn sum / xs.length;\n}\n\nfunction percentile(sorted: number[], p: number): number {\n\tif (sorted.length === 0) return 0;\n\tconst idx = Math.floor((p / 100) * (sorted.length - 1));\n\treturn sorted[idx] ?? 0;\n}\n\nfunction readRing<T extends { timestamp: number }>(\n\tring: T[],\n\twrite: number,\n\tfilled: boolean,\n\tcount: number | undefined,\n): T[] {\n\tconst n = ring.length;\n\tif (n === 0) return [];\n\tconst take = Math.min(count ?? n, n);\n\tconst out: T[] = [];\n\tfor (let i = 0; i < take; i++) {\n\t\tconst idx = (write - 1 - i + n) % n;\n\t\tout.push(ring[idx]);\n\t}\n\t// `filled` unused intentionally — wrap math above handles both phases.\n\tvoid filled;\n\treturn out;\n}\n\nfunction ringFps<T extends { timestamp: number }>(\n\tring: T[],\n\twrite: number,\n\tfilled: boolean,\n\tsize: number,\n): number {\n\tconst n = ring.length;\n\tif (n < 2) return 0;\n\tconst newest = ring[filled ? (write - 1 + size) % size : n - 1];\n\tconst oldest = ring[filled ? write : 0];\n\tconst spanMs = newest.timestamp - oldest.timestamp;\n\treturn spanMs > 0 ? Math.round(((n - 1) / spanMs) * 1000) : 0;\n}\n","\n/**\n * Rearranges items so that all items in the [left, k] are the smallest.\n * The k-th element will have the (k - left + 1)-th smallest value in [left, right].\n *\n * @template T\n * @param {T[]} arr the array to partially sort (in place)\n * @param {number} k middle index for partial sorting (as defined above)\n * @param {number} [left=0] left index of the range to sort\n * @param {number} [right=arr.length-1] right index\n * @param {(a: T, b: T) => number} [compare = (a, b) => a - b] compare function\n */\nexport default function quickselect(arr, k, left = 0, right = arr.length - 1, compare = defaultCompare) {\n\n while (right > left) {\n if (right - left > 600) {\n const n = right - left + 1;\n const m = k - left + 1;\n const z = Math.log(n);\n const s = 0.5 * Math.exp(2 * z / 3);\n const sd = 0.5 * Math.sqrt(z * s * (n - s) / n) * (m - n / 2 < 0 ? -1 : 1);\n const newLeft = Math.max(left, Math.floor(k - m * s / n + sd));\n const newRight = Math.min(right, Math.floor(k + (n - m) * s / n + sd));\n quickselect(arr, k, newLeft, newRight, compare);\n }\n\n const t = arr[k];\n let i = left;\n /** @type {number} */\n let j = right;\n\n swap(arr, left, k);\n if (compare(arr[right], t) > 0) swap(arr, left, right);\n\n while (i < j) {\n swap(arr, i, j);\n i++;\n j--;\n while (compare(arr[i], t) < 0) i++;\n while (compare(arr[j], t) > 0) j--;\n }\n\n if (compare(arr[left], t) === 0) swap(arr, left, j);\n else {\n j++;\n swap(arr, j, right);\n }\n\n if (j <= k) left = j + 1;\n if (k <= j) right = j - 1;\n }\n}\n\n/**\n * @template T\n * @param {T[]} arr\n * @param {number} i\n * @param {number} j\n */\nfunction swap(arr, i, j) {\n const tmp = arr[i];\n arr[i] = arr[j];\n arr[j] = tmp;\n}\n\n/**\n * @template T\n * @param {T} a\n * @param {T} b\n * @returns {number}\n */\nfunction defaultCompare(a, b) {\n return a < b ? -1 : a > b ? 1 : 0;\n}\n","import quickselect from 'quickselect';\n\nexport default class RBush {\n constructor(maxEntries = 9) {\n // max entries in a node is 9 by default; min node fill is 40% for best performance\n this._maxEntries = Math.max(4, maxEntries);\n this._minEntries = Math.max(2, Math.ceil(this._maxEntries * 0.4));\n this.clear();\n }\n\n all() {\n return this._all(this.data, []);\n }\n\n search(bbox) {\n let node = this.data;\n const result = [];\n\n if (!intersects(bbox, node)) return result;\n\n const toBBox = this.toBBox;\n const nodesToSearch = [];\n\n while (node) {\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n const childBBox = node.leaf ? toBBox(child) : child;\n\n if (intersects(bbox, childBBox)) {\n if (node.leaf) result.push(child);\n else if (contains(bbox, childBBox)) this._all(child, result);\n else nodesToSearch.push(child);\n }\n }\n node = nodesToSearch.pop();\n }\n\n return result;\n }\n\n collides(bbox) {\n let node = this.data;\n\n if (!intersects(bbox, node)) return false;\n\n const nodesToSearch = [];\n while (node) {\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n const childBBox = node.leaf ? this.toBBox(child) : child;\n\n if (intersects(bbox, childBBox)) {\n if (node.leaf || contains(bbox, childBBox)) return true;\n nodesToSearch.push(child);\n }\n }\n node = nodesToSearch.pop();\n }\n\n return false;\n }\n\n load(data) {\n if (!(data && data.length)) return this;\n\n if (data.length < this._minEntries) {\n for (let i = 0; i < data.length; i++) {\n this.insert(data[i]);\n }\n return this;\n }\n\n // recursively build the tree with the given data from scratch using OMT algorithm\n let node = this._build(data.slice(), 0, data.length - 1, 0);\n\n if (!this.data.children.length) {\n // save as is if tree is empty\n this.data = node;\n\n } else if (this.data.height === node.height) {\n // split root if trees have the same height\n this._splitRoot(this.data, node);\n\n } else {\n if (this.data.height < node.height) {\n // swap trees if inserted one is bigger\n const tmpNode = this.data;\n this.data = node;\n node = tmpNode;\n }\n\n // insert the small tree into the large tree at appropriate level\n this._insert(node, this.data.height - node.height - 1, true);\n }\n\n return this;\n }\n\n insert(item) {\n if (item) this._insert(item, this.data.height - 1);\n return this;\n }\n\n clear() {\n this.data = createNode([]);\n return this;\n }\n\n remove(item, equalsFn) {\n if (!item) return this;\n\n let node = this.data;\n const bbox = this.toBBox(item);\n const path = [];\n const indexes = [];\n let i, parent, goingUp;\n\n // depth-first iterative tree traversal\n while (node || path.length) {\n\n if (!node) { // go up\n node = path.pop();\n parent = path[path.length - 1];\n i = indexes.pop();\n goingUp = true;\n }\n\n if (node.leaf) { // check current node\n const index = findItem(item, node.children, equalsFn);\n\n if (index !== -1) {\n // item found, remove the item and condense tree upwards\n node.children.splice(index, 1);\n path.push(node);\n this._condense(path);\n return this;\n }\n }\n\n if (!goingUp && !node.leaf && contains(node, bbox)) { // go down\n path.push(node);\n indexes.push(i);\n i = 0;\n parent = node;\n node = node.children[0];\n\n } else if (parent) { // go right\n i++;\n node = parent.children[i];\n goingUp = false;\n\n } else node = null; // nothing found\n }\n\n return this;\n }\n\n toBBox(item) { return item; }\n\n compareMinX(a, b) { return a.minX - b.minX; }\n compareMinY(a, b) { return a.minY - b.minY; }\n\n toJSON() { return this.data; }\n\n fromJSON(data) {\n this.data = data;\n return this;\n }\n\n _all(node, result) {\n const nodesToSearch = [];\n while (node) {\n if (node.leaf) result.push(...node.children);\n else nodesToSearch.push(...node.children);\n\n node = nodesToSearch.pop();\n }\n return result;\n }\n\n _build(items, left, right, height) {\n\n const N = right - left + 1;\n let M = this._maxEntries;\n let node;\n\n if (N <= M) {\n // reached leaf level; return leaf\n node = createNode(items.slice(left, right + 1));\n calcBBox(node, this.toBBox);\n return node;\n }\n\n if (!height) {\n // target height of the bulk-loaded tree\n height = Math.ceil(Math.log(N) / Math.log(M));\n\n // target number of root entries to maximize storage utilization\n M = Math.ceil(N / Math.pow(M, height - 1));\n }\n\n node = createNode([]);\n node.leaf = false;\n node.height = height;\n\n // split the items into M mostly square tiles\n\n const N2 = Math.ceil(N / M);\n const N1 = N2 * Math.ceil(Math.sqrt(M));\n\n multiSelect(items, left, right, N1, this.compareMinX);\n\n for (let i = left; i <= right; i += N1) {\n\n const right2 = Math.min(i + N1 - 1, right);\n\n multiSelect(items, i, right2, N2, this.compareMinY);\n\n for (let j = i; j <= right2; j += N2) {\n\n const right3 = Math.min(j + N2 - 1, right2);\n\n // pack each entry recursively\n node.children.push(this._build(items, j, right3, height - 1));\n }\n }\n\n calcBBox(node, this.toBBox);\n\n return node;\n }\n\n _chooseSubtree(bbox, node, level, path) {\n while (true) {\n path.push(node);\n\n if (node.leaf || path.length - 1 === level) break;\n\n let minArea = Infinity;\n let minEnlargement = Infinity;\n let targetNode;\n\n for (let i = 0; i < node.children.length; i++) {\n const child = node.children[i];\n const area = bboxArea(child);\n const enlargement = enlargedArea(bbox, child) - area;\n\n // choose entry with the least area enlargement\n if (enlargement < minEnlargement) {\n minEnlargement = enlargement;\n minArea = area < minArea ? area : minArea;\n targetNode = child;\n\n } else if (enlargement === minEnlargement) {\n // otherwise choose one with the smallest area\n if (area < minArea) {\n minArea = area;\n targetNode = child;\n }\n }\n }\n\n node = targetNode || node.children[0];\n }\n\n return node;\n }\n\n _insert(item, level, isNode) {\n const bbox = isNode ? item : this.toBBox(item);\n const insertPath = [];\n\n // find the best node for accommodating the item, saving all nodes along the path too\n const node = this._chooseSubtree(bbox, this.data, level, insertPath);\n\n // put the item into the node\n node.children.push(item);\n extend(node, bbox);\n\n // split on node overflow; propagate upwards if necessary\n while (level >= 0) {\n if (insertPath[level].children.length > this._maxEntries) {\n this._split(insertPath, level);\n level--;\n } else break;\n }\n\n // adjust bboxes along the insertion path\n this._adjustParentBBoxes(bbox, insertPath, level);\n }\n\n // split overflowed node into two\n _split(insertPath, level) {\n const node = insertPath[level];\n const M = node.children.length;\n const m = this._minEntries;\n\n this._chooseSplitAxis(node, m, M);\n\n const splitIndex = this._chooseSplitIndex(node, m, M);\n\n const newNode = createNode(node.children.splice(splitIndex, node.children.length - splitIndex));\n newNode.height = node.height;\n newNode.leaf = node.leaf;\n\n calcBBox(node, this.toBBox);\n calcBBox(newNode, this.toBBox);\n\n if (level) insertPath[level - 1].children.push(newNode);\n else this._splitRoot(node, newNode);\n }\n\n _splitRoot(node, newNode) {\n // split root node\n this.data = createNode([node, newNode]);\n this.data.height = node.height + 1;\n this.data.leaf = false;\n calcBBox(this.data, this.toBBox);\n }\n\n _chooseSplitIndex(node, m, M) {\n let index;\n let minOverlap = Infinity;\n let minArea = Infinity;\n\n for (let i = m; i <= M - m; i++) {\n const bbox1 = distBBox(node, 0, i, this.toBBox);\n const bbox2 = distBBox(node, i, M, this.toBBox);\n\n const overlap = intersectionArea(bbox1, bbox2);\n const area = bboxArea(bbox1) + bboxArea(bbox2);\n\n // choose distribution with minimum overlap\n if (overlap < minOverlap) {\n minOverlap = overlap;\n index = i;\n\n minArea = area < minArea ? area : minArea;\n\n } else if (overlap === minOverlap) {\n // otherwise choose distribution with minimum area\n if (area < minArea) {\n minArea = area;\n index = i;\n }\n }\n }\n\n return index || M - m;\n }\n\n // sorts node children by the best axis for split\n _chooseSplitAxis(node, m, M) {\n const compareMinX = node.leaf ? this.compareMinX : compareNodeMinX;\n const compareMinY = node.leaf ? this.compareMinY : compareNodeMinY;\n const xMargin = this._allDistMargin(node, m, M, compareMinX);\n const yMargin = this._allDistMargin(node, m, M, compareMinY);\n\n // if total distributions margin value is minimal for x, sort by minX,\n // otherwise it's already sorted by minY\n if (xMargin < yMargin) node.children.sort(compareMinX);\n }\n\n // total margin of all possible split distributions where each node is at least m full\n _allDistMargin(node, m, M, compare) {\n node.children.sort(compare);\n\n const toBBox = this.toBBox;\n const leftBBox = distBBox(node, 0, m, toBBox);\n const rightBBox = distBBox(node, M - m, M, toBBox);\n let margin = bboxMargin(leftBBox) + bboxMargin(rightBBox);\n\n for (let i = m; i < M - m; i++) {\n const child = node.children[i];\n extend(leftBBox, node.leaf ? toBBox(child) : child);\n margin += bboxMargin(leftBBox);\n }\n\n for (let i = M - m - 1; i >= m; i--) {\n const child = node.children[i];\n extend(rightBBox, node.leaf ? toBBox(child) : child);\n margin += bboxMargin(rightBBox);\n }\n\n return margin;\n }\n\n _adjustParentBBoxes(bbox, path, level) {\n // adjust bboxes along the given tree path\n for (let i = level; i >= 0; i--) {\n extend(path[i], bbox);\n }\n }\n\n _condense(path) {\n // go through the path, removing empty nodes and updating bboxes\n for (let i = path.length - 1, siblings; i >= 0; i--) {\n if (path[i].children.length === 0) {\n if (i > 0) {\n siblings = path[i - 1].children;\n siblings.splice(siblings.indexOf(path[i]), 1);\n\n } else this.clear();\n\n } else calcBBox(path[i], this.toBBox);\n }\n }\n}\n\nfunction findItem(item, items, equalsFn) {\n if (!equalsFn) return items.indexOf(item);\n\n for (let i = 0; i < items.length; i++) {\n if (equalsFn(item, items[i])) return i;\n }\n return -1;\n}\n\n// calculate node's bbox from bboxes of its children\nfunction calcBBox(node, toBBox) {\n distBBox(node, 0, node.children.length, toBBox, node);\n}\n\n// min bounding rectangle of node children from k to p-1\nfunction distBBox(node, k, p, toBBox, destNode) {\n if (!destNode) destNode = createNode(null);\n destNode.minX = Infinity;\n destNode.minY = Infinity;\n destNode.maxX = -Infinity;\n destNode.maxY = -Infinity;\n\n for (let i = k; i < p; i++) {\n const child = node.children[i];\n extend(destNode, node.leaf ? toBBox(child) : child);\n }\n\n return destNode;\n}\n\nfunction extend(a, b) {\n a.minX = Math.min(a.minX, b.minX);\n a.minY = Math.min(a.minY, b.minY);\n a.maxX = Math.max(a.maxX, b.maxX);\n a.maxY = Math.max(a.maxY, b.maxY);\n return a;\n}\n\nfunction compareNodeMinX(a, b) { return a.minX - b.minX; }\nfunction compareNodeMinY(a, b) { return a.minY - b.minY; }\n\nfunction bboxArea(a) { return (a.maxX - a.minX) * (a.maxY - a.minY); }\nfunction bboxMargin(a) { return (a.maxX - a.minX) + (a.maxY - a.minY); }\n\nfunction enlargedArea(a, b) {\n return (Math.max(b.maxX, a.maxX) - Math.min(b.minX, a.minX)) *\n (Math.max(b.maxY, a.maxY) - Math.min(b.minY, a.minY));\n}\n\nfunction intersectionArea(a, b) {\n const minX = Math.max(a.minX, b.minX);\n const minY = Math.max(a.minY, b.minY);\n const maxX = Math.min(a.maxX, b.maxX);\n const maxY = Math.min(a.maxY, b.maxY);\n\n return Math.max(0, maxX - minX) *\n Math.max(0, maxY - minY);\n}\n\nfunction contains(a, b) {\n return a.minX <= b.minX &&\n a.minY <= b.minY &&\n b.maxX <= a.maxX &&\n b.maxY <= a.maxY;\n}\n\nfunction intersects(a, b) {\n return b.minX <= a.maxX &&\n b.minY <= a.maxY &&\n b.maxX >= a.minX &&\n b.maxY >= a.minY;\n}\n\nfunction createNode(children) {\n return {\n children,\n height: 1,\n leaf: true,\n minX: Infinity,\n minY: Infinity,\n maxX: -Infinity,\n maxY: -Infinity\n };\n}\n\n// sort an array so that items come in groups of n unsorted items, with groups sorted between each other;\n// combines selection algorithm with binary divide & conquer approach\n\nfunction multiSelect(arr, left, right, n, compare) {\n const stack = [left, right];\n\n while (stack.length) {\n right = stack.pop();\n left = stack.pop();\n\n if (right - left <= n) continue;\n\n const mid = left + Math.ceil((right - left) / n / 2) * n;\n quickselect(arr, mid, left, right, compare);\n\n stack.push(left, mid, mid, right);\n }\n}\n","import RBushImport from 'rbush';\n\n// Handle CJS/ESM interop — rbush exports differently depending on context\ntype RBushModule = typeof RBushImport & { default?: typeof RBushImport };\nconst rbushModule = RBushImport as RBushModule;\nconst RBush = (\n\ttypeof rbushModule.default === 'function' ? rbushModule.default : RBushImport\n) as typeof RBushImport;\n\nimport type { EntityId } from '@jamesyong42/reactive-ecs';\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","/**\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].toSorted((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\tif (minEnd < maxStart) {\n\t\t// No overlap — fall back to the dragged entity's perpendicular center\n\t\treturn perpPos(allBounds[0]) + perpSize(allBounds[0]) / 2;\n\t}\n\treturn maxStart + (minEnd - maxStart) / 2;\n}\n","import { createContext, useContext } from 'react';\n\n// Shared so WidgetSlot can compute container-relative pointer coordinates.\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","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport { createContext, useContext } from 'react';\nimport type { DomWidgetProps, R3FWidgetProps } from '../widgets/registry.js';\n\n/**\n * Discriminated resolution of a widget by type. The surface determines which\n * layer renders the component and with what prop shape.\n */\nexport type ResolvedWidget =\n\t| { surface: 'dom'; component: React.ComponentType<DomWidgetProps> }\n\t| { surface: 'webgl'; component: React.ComponentType<R3FWidgetProps> };\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","/**\n * RFC-008 input pipeline — diagnostic logger.\n *\n * Single toggle (`INPUT_DEBUG`) wires every layer's \"what just happened\"\n * decision into the browser console with colour-coded tags so the dispatch\n * flow is legible in real time:\n *\n * [Adapter] [InputManager] [Router] [R3F] [Recognizer] [Engine]\n *\n * `INPUT_DEBUG_VERBOSE` extends this to per-frame `move` events, which\n * fire at 60+ Hz and quickly drown the console — leave it `false` unless\n * you're chasing a hover or pan-update issue.\n *\n * Toggle either by editing the constants below or, more conveniently, by\n * setting `window.__INPUT_DEBUG__` / `window.__INPUT_DEBUG_VERBOSE__` from\n * the devtools console at runtime.\n */\n\nconst DEFAULT_DEBUG = true;\nconst DEFAULT_VERBOSE = false;\n\ndeclare global {\n\tinterface Window {\n\t\t__INPUT_DEBUG__?: boolean;\n\t\t__INPUT_DEBUG_VERBOSE__?: boolean;\n\t}\n}\n\nfunction isDebug(): boolean {\n\tif (typeof window === 'undefined') return DEFAULT_DEBUG;\n\treturn window.__INPUT_DEBUG__ ?? DEFAULT_DEBUG;\n}\n\nfunction isVerbose(): boolean {\n\tif (typeof window === 'undefined') return DEFAULT_VERBOSE;\n\treturn window.__INPUT_DEBUG_VERBOSE__ ?? DEFAULT_VERBOSE;\n}\n\nexport type InputLayer = 'Adapter' | 'InputManager' | 'Router' | 'R3F' | 'Recognizer' | 'Engine';\n\nconst STYLES: Record<InputLayer, string> = {\n\tAdapter: 'background:#0288d1; color:#fff; padding:1px 4px; border-radius:2px; font-weight:bold',\n\tInputManager:\n\t\t'background:#f9a825; color:#222; padding:1px 4px; border-radius:2px; font-weight:bold',\n\tRouter: 'background:#7b1fa2; color:#fff; padding:1px 4px; border-radius:2px; font-weight:bold',\n\tR3F: 'background:#00838f; color:#fff; padding:1px 4px; border-radius:2px; font-weight:bold',\n\tRecognizer: 'background:#558b2f; color:#fff; padding:1px 4px; border-radius:2px',\n\tEngine: 'background:#d84315; color:#fff; padding:1px 4px; border-radius:2px; font-weight:bold',\n};\n\n/**\n * Log a single line tagged with a layer. `data` is appended as a single\n * object so devtools can expand it inline.\n *\n * `move`-typed events are gated on `INPUT_DEBUG_VERBOSE` so the default\n * trace stays readable.\n */\nexport function inputLog(layer: InputLayer, message: string, data?: Record<string, unknown>): void {\n\tif (!isDebug()) return;\n\tconst isMove =\n\t\ttypeof data?.type === 'string' && (data.type === 'move' || data.type.endsWith('-update'));\n\tif (isMove && !isVerbose()) return;\n\tif (data !== undefined) {\n\t\tconsole.log(`%c${layer}%c ${message}`, STYLES[layer], 'color:inherit', data);\n\t} else {\n\t\tconsole.log(`%c${layer}%c ${message}`, STYLES[layer], 'color:inherit');\n\t}\n}\n\n/**\n * Group all logs for a single InputManager.dispatch under one collapsible\n * heading. Pass the returned closer to console.groupEnd at the end of\n * dispatch. No-op when debug is off.\n */\nexport function inputGroupStart(label: string): () => void {\n\tif (!isDebug()) return () => {};\n\tconsole.groupCollapsed(`%cInput · ${label}`, 'color:#555; font-weight:bold');\n\treturn () => console.groupEnd();\n}\n","import { events as createPointerEvents } from '@react-three/fiber';\nimport type { Intersection, Object3D, Scene } from 'three';\nimport { Transform2D, Widget } from '../../../ecs/components.js';\nimport type { LayoutEngine } from '../../../ecs/engine/index.js';\nimport type { WidgetRegistry } from '../../../r3f/compositor/WidgetRegistry.js';\nimport { inputLog } from '../debug.js';\n\n/**\n * RFC-008 v6 — R3F event-manager factory tailored for the InputManager\n * pipeline. R3F's bubble + hover diff + click synthesis still run, but\n * R3F is now driven *entirely* by the InputManager: PointerAdapter feeds\n * pointer events, ClickAdapter feeds click / dblclick / contextmenu, and\n * `R3FRouter` invokes `manager.handlers.onPointerDown(nativeEvent)` /\n * `onClick(...)` / etc. from the InputManager dispatch loop.\n *\n * `connect` / `disconnect` are no-ops. v5 used to register a parallel\n * listener set on the canvas container for click / dblclick / contextmenu\n * (because those events lived outside the InputManager pipeline). v6\n * unifies them — no parallel native listeners, one dispatcher entry\n * point. This eliminates the \"click on a captured mesh doesn't select\n * the widget\" coexistence bug (RFC-008 v5 smell 5.1).\n *\n * The `compute` and `filter` overrides are inherited from the v3 EventRouter:\n *\n * - `compute` resolves the widget under the pointer via `engine.pickAt`,\n * then sets up R3F's raycaster against that widget's local scene + ortho\n * camera in widget-local NDC. Identical math to RFC-006's EventRouter.\n *\n * - `filter` discards intersections that landed on meshes outside the\n * active widget's scene tree (per-widget scenes all live at the origin\n * in world space — without the filter, widget A's pointer ray would hit\n * widget B's meshes wherever they overlap in widget-local space).\n *\n * The single closure variable `activeScene` is written by `compute` per\n * event and read by `filter` for the same event. Single-threaded JS makes\n * this safe.\n */\n/**\n * Optional `onCreate` callback fires once R3F's `<Canvas>` invokes the\n * factory and the manager object is constructed — this is the only way\n * to surface the live `EventManager` to outside code (e.g. the\n * `R3FRouter` which needs to call `manager.handlers.onPointerDown(...)`).\n */\ntype R3FManagerLike = ReturnType<typeof createPointerEvents>;\n\nexport function createR3FEventManager(\n\tengine: LayoutEngine,\n\tregistry: WidgetRegistry,\n\tonCreate?: (manager: R3FManagerLike) => void,\n) {\n\tlet activeScene: Scene | null = null;\n\n\tfunction skipEvent(state: {\n\t\traycaster: { camera: unknown };\n\t\tpointer: { set: (x: number, y: number) => void };\n\t}): void {\n\t\tactiveScene = null;\n\t\tstate.raycaster.camera = null;\n\t\tstate.pointer.set(0, 0);\n\t}\n\n\treturn (store: Parameters<typeof createPointerEvents>[0]) => {\n\t\tconst base = createPointerEvents(store);\n\n\t\tconst compute: NonNullable<ReturnType<typeof createPointerEvents>['compute']> = (\n\t\t\tevent,\n\t\t\tstate,\n\t\t) => {\n\t\t\tconst target = state.gl.domElement;\n\t\t\tconst rect = target.getBoundingClientRect();\n\t\t\tconst screenX = event.clientX - rect.left;\n\t\t\tconst screenY = event.clientY - rect.top;\n\n\t\t\tconst entityId = engine.pickAt(screenX, screenY);\n\t\t\tif (entityId === null) {\n\t\t\t\tskipEvent(state);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst w = engine.get(entityId, Widget);\n\t\t\tif (w?.surface !== 'webgl') {\n\t\t\t\tskipEvent(state);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst widget = registry.get(entityId);\n\t\t\tconst t = engine.get(entityId, Transform2D);\n\t\t\tif (!widget || !t) {\n\t\t\t\tskipEvent(state);\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tconst cam = engine.getCamera();\n\t\t\tconst worldX = screenX / cam.zoom + cam.x;\n\t\t\tconst worldY = screenY / cam.zoom + cam.y;\n\t\t\tconst widgetCenterX = t.x + t.width / 2;\n\t\t\tconst widgetCenterY = t.y + t.height / 2;\n\t\t\tconst localX = worldX - widgetCenterX;\n\t\t\tconst localY = -(worldY - widgetCenterY);\n\t\t\tconst ndcX = (2 * localX) / t.width;\n\t\t\tconst ndcY = (2 * localY) / t.height;\n\n\t\t\tactiveScene = widget.scene;\n\t\t\tstate.pointer.set(ndcX, ndcY);\n\t\t\tstate.raycaster.setFromCamera(state.pointer, widget.camera);\n\t\t\tstate.raycaster.camera = widget.camera;\n\t\t\tinputLog('R3F', `compute: ready raycaster for entity ${entityId}`, {\n\t\t\t\ttype: event.type,\n\t\t\t\tentityId,\n\t\t\t\tndc: { x: ndcX, y: ndcY },\n\t\t\t\tsceneChildren: widget.scene.children.length,\n\t\t\t});\n\t\t};\n\n\t\tconst filter = (items: Intersection[]): Intersection[] => {\n\t\t\tconst scene = activeScene;\n\t\t\tif (!scene) return [];\n\t\t\treturn items.filter((hit) => isDescendantOf(hit.object, scene));\n\t\t};\n\n\t\t// R3F populates `internal.capturedMap` synchronously when a mesh\n\t\t// handler calls `e.target.setPointerCapture(pointerId)`. Exposing\n\t\t// this lets `R3FRouter.isPointerClaimed` tell the InputManager to\n\t\t// skip recognizers for claimed pointers — without it, DOM\n\t\t// setPointerCapture alone doesn't shield ancestor listeners\n\t\t// (PointerAdapter on the canvas container) from seeing the\n\t\t// captured events.\n\t\tconst isPointerCaptured = (pointerId: number): boolean => {\n\t\t\t// R3F's store types don't surface `internal` cleanly; the\n\t\t\t// path is `store.getState().internal.capturedMap`.\n\t\t\t// biome-ignore lint/suspicious/noExplicitAny: R3F internal field.\n\t\t\tconst state = store.getState() as any;\n\t\t\treturn state.internal?.capturedMap?.has?.(pointerId) ?? false;\n\t\t};\n\n\t\tconst manager = {\n\t\t\t...base,\n\t\t\tcompute,\n\t\t\tfilter,\n\t\t\tisPointerCaptured,\n\t\t\t// v6: no native listeners. R3F is driven entirely by the\n\t\t\t// InputManager pipeline (PointerAdapter + ClickAdapter +\n\t\t\t// WheelAdapter → R3FRouter → manager.handlers.*). connect /\n\t\t\t// disconnect kept as required by R3F's EventManager contract,\n\t\t\t// but they're empty.\n\t\t\tconnect: (_target: HTMLElement) => {\n\t\t\t\tinputLog('R3F', 'createR3FEventManager.connect: no-op (driven by InputManager)');\n\t\t\t},\n\t\t\tdisconnect: () => {\n\t\t\t\tinputLog('R3F', 'createR3FEventManager.disconnect: no-op');\n\t\t\t},\n\t\t};\n\t\tonCreate?.(manager);\n\t\treturn manager;\n\t};\n}\n\nfunction isDescendantOf(obj: Object3D, ancestor: Object3D): boolean {\n\tlet n: Object3D | null = obj;\n\twhile (n) {\n\t\tif (n === ancestor) return true;\n\t\tn = n.parent;\n\t}\n\treturn false;\n}\n","import { ShaderMaterial, type Texture, Vector2, Vector3, Vector4 } from 'three';\n\n/**\n * Module-level shared uniforms for the overlap glow.\n *\n * Every {@link CompositionMaterial} instance references THESE EXACT\n * Vector2 / Vector3 objects. Mutating any `.value` here propagates to\n * every R3F card simultaneously (Three.js sees them as the same uniform\n * binding). One call to `applyOverlapGlowShaderUniforms(config)` retunes\n * every card without iterating materials.\n */\nexport const sharedGlowUniforms = {\n\tuGlowColor: { value: new Vector3(0.5, 0.5, 0.5) },\n\tuGlowAlpha: { value: new Vector2(0.25, 0.45) }, // [candidate, target]\n\tuGlowFalloff: { value: new Vector2(0.3, 0.4) }, // [candidate, target] in uv\n\tuRimColor: { value: new Vector3(0.5, 0.5, 0.5) },\n\tuRimAlpha: { value: new Vector2(0.55, 0.85) }, // [candidate, target]\n\tuRimRadius: { value: 3 }, // 600px / 200px reference card → 3 in UV\n};\n\n/**\n * Shader pair for the composition pass — sample an sRGB-encoded widget\n * FBO and write it to the sRGB backbuffer unchanged. No tone mapping,\n * no output encoding (the FBO already holds display-ready values, see\n * RFC-002 § sRGB FBO fix).\n *\n * `uDraggedRect` + `uIsDragged` implement the RFC-003 drag-promote\n * clip: when an R3F widget is being dragged, every other widget's\n * fragments inside that widget's screen rect are discarded so the\n * promoted DOM CardChrome (now above the R3F canvas) shows through.\n *\n * `uHotPoint` + `uHotStrength` + `uIsOverlapTarget` drive the\n * single-layer overlap glow — a soft radial gradient at the\n * intersection centroid that fades out via `uGlowFalloff`. Color and\n * alpha are tunable via {@link sharedGlowUniforms}.\n */\nconst VERTEX_SHADER = /* glsl */ `\nvarying vec2 vUv;\nvoid main() {\n vUv = uv;\n gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);\n}\n`;\n\n// Fragment shader. Includes Three's <colorspace_fragment> chunk after\n// writing gl_FragColor — Three.js's texture sampler automatically\n// decodes sRGB textures to linear during the texture2D call (because\n// the FBO declares colorSpace=SRGBColorSpace), so without re-encoding\n// to the renderer's outputColorSpace (also sRGB) the values would land\n// in the backbuffer as linear and read as washed-out / desaturated.\nconst FRAGMENT_SHADER = /* glsl */ `\nuniform sampler2D map;\nuniform vec4 uDraggedRect;\nuniform float uIsDragged;\nuniform vec2 uHotPoint;\nuniform float uHotStrength;\nuniform float uIsOverlapTarget;\n\nuniform vec3 uGlowColor;\nuniform vec2 uGlowAlpha;\nuniform vec2 uGlowFalloff;\nuniform vec3 uRimColor;\nuniform vec2 uRimAlpha;\nuniform float uRimRadius;\n\nvarying vec2 vUv;\n\nvoid main() {\n if (uIsDragged < 0.5) {\n vec2 sp = gl_FragCoord.xy;\n if (sp.x >= uDraggedRect.x && sp.x <= uDraggedRect.z &&\n sp.y >= uDraggedRect.y && sp.y <= uDraggedRect.w) {\n discard;\n }\n }\n vec4 c = texture2D(map, vUv);\n if (c.a < 0.001) discard;\n\n // Two-part overlap highlight, both tinted with uGlowColor:\n // 1. Inner glow — soft radial at the hot point, falling off to\n // transparent at uGlowFalloff.\n // 2. Rim — a thin band along the card edge that pools toward the\n // hot point (rimBand AND rimFocus must both be high).\n if (uHotStrength > 0.001) {\n float d = length(vUv - uHotPoint);\n\n // Inner glow.\n float falloff = mix(uGlowFalloff.x, uGlowFalloff.y, uIsOverlapTarget);\n float alpha = mix(uGlowAlpha.x, uGlowAlpha.y, uIsOverlapTarget);\n float glow = smoothstep(falloff, 0.0, d) * uHotStrength * alpha;\n c.rgb = mix(c.rgb, uGlowColor, glow);\n\n // Rim — band thickness fixed at 0.028 in uv (~3% of card on each\n // axis); brightness pools toward the hot point via rimFocus, with\n // falloff distance derived from uRimRadius (matches the DOM\n // radial-gradient(uRimRadius circle ..., transparent 40%)).\n float edge = min(min(vUv.x, 1.0 - vUv.x), min(vUv.y, 1.0 - vUv.y));\n float rimBand = smoothstep(0.028, 0.0, edge);\n float rimFocus = smoothstep(uRimRadius * 0.4, 0.0, d);\n float rimA = mix(uRimAlpha.x, uRimAlpha.y, uIsOverlapTarget);\n float rim = rimBand * rimFocus * uHotStrength * rimA;\n c.rgb = mix(c.rgb, uRimColor, rim);\n }\n\n gl_FragColor = c;\n #include <colorspace_fragment>\n}\n`;\n\n/**\n * Per-instance composition material. Each widget's quad gets its own\n * instance so the per-quad uniforms (`map`, `uIsDragged`,\n * `uDraggedRect`) are independent. Three.js compiles the shader once\n * and reuses the program across instances since they share\n * vertex/fragment source — verify in dev via\n * `renderer.info.programs.length === 1` for the composition shader.\n */\nexport class CompositionMaterial extends ShaderMaterial {\n\tconstructor() {\n\t\tsuper({\n\t\t\tvertexShader: VERTEX_SHADER,\n\t\t\tfragmentShader: FRAGMENT_SHADER,\n\t\t\tuniforms: {\n\t\t\t\t// Per-instance.\n\t\t\t\tmap: { value: null },\n\t\t\t\tuDraggedRect: { value: new Vector4(0, 0, 0, 0) },\n\t\t\t\tuIsDragged: { value: 0 },\n\t\t\t\tuHotPoint: { value: new Vector2(0.5, 0.5) },\n\t\t\t\tuHotStrength: { value: 0 },\n\t\t\t\tuIsOverlapTarget: { value: 0 },\n\t\t\t\t// Shared across all materials — same object references so\n\t\t\t\t// mutations to `sharedGlowUniforms.X.value` propagate.\n\t\t\t\tuGlowColor: sharedGlowUniforms.uGlowColor,\n\t\t\t\tuGlowAlpha: sharedGlowUniforms.uGlowAlpha,\n\t\t\t\tuGlowFalloff: sharedGlowUniforms.uGlowFalloff,\n\t\t\t\tuRimColor: sharedGlowUniforms.uRimColor,\n\t\t\t\tuRimAlpha: sharedGlowUniforms.uRimAlpha,\n\t\t\t\tuRimRadius: sharedGlowUniforms.uRimRadius,\n\t\t\t},\n\t\t\ttransparent: true,\n\t\t\tdepthWrite: false,\n\t\t});\n\t}\n\n\tsetMap(map: Texture | null): void {\n\t\tthis.uniforms.map.value = map;\n\t}\n\n\tsetDraggedRect(minX: number, minY: number, maxX: number, maxY: number): void {\n\t\t(this.uniforms.uDraggedRect.value as Vector4).set(minX, minY, maxX, maxY);\n\t}\n\n\tsetIsDragged(isDragged: boolean): void {\n\t\tthis.uniforms.uIsDragged.value = isDragged ? 1 : 0;\n\t}\n\n\t/** Set the radial-glow centre in uv space (0..1). */\n\tsetHotPoint(x: number, y: number): void {\n\t\t(this.uniforms.uHotPoint.value as Vector2).set(x, y);\n\t}\n\n\t/** 0..1 — glow opacity; set to 0 to disable. */\n\tsetHotStrength(strength: number): void {\n\t\tthis.uniforms.uHotStrength.value = strength;\n\t}\n\n\t/** Toggle target state (mixes toward the target alpha / falloff). */\n\tsetIsOverlapTarget(on: boolean): void {\n\t\tthis.uniforms.uIsOverlapTarget.value = on ? 1 : 0;\n\t}\n}\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport { createContext, useContext } from 'react';\nimport type { OrthographicCamera, Scene } from 'three';\nimport type { ResourceRegistry } from './ResourceRegistry.js';\nimport type { WidgetRenderTargetPool } from './WidgetRenderTargetPool.js';\n\n/**\n * Per-widget registration entry the Compositor needs in order to paint\n * the widget's scene into its FBO each frame.\n */\nexport type CompositorWidgetEntry = {\n\tscene: Scene;\n\tcamera: OrthographicCamera;\n\t/**\n\t * Bumped by the widget when its content has changed and needs a repaint.\n\t * Compared against fboGeneration in R3FRenderState.\n\t */\n\trequestRepaint: () => void;\n};\n\nexport type CompositorContextValue = {\n\tpool: WidgetRenderTargetPool;\n\tregistry: ResourceRegistry;\n\tregister: (entityId: EntityId, entry: CompositorWidgetEntry) => () => void;\n};\n\nexport const CompositorContext = createContext<CompositorContextValue | null>(null);\n\nexport function useCompositor(): CompositorContextValue {\n\tconst ctx = useContext(CompositorContext);\n\tif (!ctx) {\n\t\tthrow new Error('useCompositor must be used inside <Compositor>');\n\t}\n\treturn ctx;\n}\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport type { R3FPhase } from './state.js';\n\n/**\n * One pool entry as eviction candidate. Built by the Compositor from\n * `pool.entryInfos()` joined with each widget's `R3FRenderState.phase`.\n */\nexport type EvictionCandidate = {\n\tentityId: EntityId;\n\tphase: R3FPhase;\n\tbytes: number;\n\tlastUsedMs: number;\n};\n\n/**\n * Eviction priority — lower numbers evict first. Hot and Waking are never\n * evicted because they are about to be (or are actively) painted.\n *\n * Cold 0 off-screen, eviction-eligible immediately\n * Warm 1 visible + idle, retained while budget allows\n * Dormant 2 inactive but eviction-protected — only released as a\n * last resort because losing the FBO costs the\n * \"instant re-activation\" guarantee\n * Waking ∞ scheduled to repaint imminently — never evict\n * Hot ∞ actively painted every frame — never evict\n */\nconst PHASE_PRIORITY: Record<R3FPhase, number> = {\n\tCold: 0,\n\tWarm: 1,\n\tDormant: 2,\n\tWaking: Number.POSITIVE_INFINITY,\n\tHot: Number.POSITIVE_INFINITY,\n};\n\n/**\n * Returns the ordered list of entity ids to release so total bytes ≤ budget.\n * Sort key: `(phasePriority, lastUsedMs)` — within a phase, oldest goes\n * first (LRU). Hot / Waking widgets are never returned even if the budget\n * is impossible to satisfy without them.\n *\n * Pure function — Compositor calls this in its useFrame and forwards each\n * id to `pool.release()`.\n */\nexport function selectEvictions(\n\tcandidates: EvictionCandidate[],\n\ttotalBytes: number,\n\tmaxBytes: number,\n): EntityId[] {\n\tif (totalBytes <= maxBytes) return [];\n\n\tconst eligible = candidates.filter((c) => Number.isFinite(PHASE_PRIORITY[c.phase]));\n\teligible.sort((a, b) => {\n\t\tconst p = PHASE_PRIORITY[a.phase] - PHASE_PRIORITY[b.phase];\n\t\tif (p !== 0) return p;\n\t\treturn a.lastUsedMs - b.lastUsedMs;\n\t});\n\n\tconst toEvict: EntityId[] = [];\n\tlet remaining = totalBytes;\n\tfor (const c of eligible) {\n\t\tif (remaining <= maxBytes) break;\n\t\ttoEvict.push(c.entityId);\n\t\tremaining -= c.bytes;\n\t}\n\treturn toEvict;\n}\n","import type { BufferGeometry, Material, Texture } from 'three';\n\n/**\n * Disposable resource tracked by the registry.\n *\n * Three.js geometries / materials / textures all expose `.dispose()` and can\n * be safely shared across scenes — sharing avoids the per-widget GPU\n * duplication that the compositor's per-widget scenes would otherwise force.\n */\ntype Disposable = { dispose: () => void };\n\ntype Entry<T extends Disposable> = { resource: T; refCount: number };\n\n/**\n * Archetype-keyed cache of GPU resources shared across per-widget scenes\n * (RFC-002 § Three.js resource sharing).\n *\n * Geometries, materials, and textures returned by `acquire*` are reference\n * counted. The registry disposes the underlying resource only after every\n * holder has called the matching `release*` — so 100 widgets of the same\n * card archetype share one geometry instance and one set of material\n * uniforms, rather than allocating 100 copies.\n */\nexport class ResourceRegistry {\n\tprivate geometries = new Map<string, Entry<BufferGeometry>>();\n\tprivate materials = new Map<string, Entry<Material>>();\n\tprivate textures = new Map<string, Entry<Texture>>();\n\tprivate disposed = false;\n\n\tacquireGeometry<T extends BufferGeometry>(key: string, factory: () => T): T {\n\t\tconst existing = this.geometries.get(key);\n\t\tif (existing) {\n\t\t\texisting.refCount++;\n\t\t\treturn existing.resource as T;\n\t\t}\n\t\tconst resource = factory();\n\t\tthis.geometries.set(key, { resource, refCount: 1 });\n\t\treturn resource;\n\t}\n\treleaseGeometry(key: string): void {\n\t\tthis.release(this.geometries, key);\n\t}\n\n\tacquireMaterial<T extends Material>(key: string, factory: () => T): T {\n\t\tconst existing = this.materials.get(key);\n\t\tif (existing) {\n\t\t\texisting.refCount++;\n\t\t\treturn existing.resource as T;\n\t\t}\n\t\tconst resource = factory();\n\t\tthis.materials.set(key, { resource, refCount: 1 });\n\t\treturn resource;\n\t}\n\treleaseMaterial(key: string): void {\n\t\tthis.release(this.materials, key);\n\t}\n\n\tacquireTexture<T extends Texture>(key: string, factory: () => T): T {\n\t\tconst existing = this.textures.get(key);\n\t\tif (existing) {\n\t\t\texisting.refCount++;\n\t\t\treturn existing.resource as T;\n\t\t}\n\t\tconst resource = factory();\n\t\tthis.textures.set(key, { resource, refCount: 1 });\n\t\treturn resource;\n\t}\n\treleaseTexture(key: string): void {\n\t\tthis.release(this.textures, key);\n\t}\n\n\t/** Number of distinct shared geometries currently held. */\n\tgeometryCount(): number {\n\t\treturn this.geometries.size;\n\t}\n\n\t/** Number of distinct shared materials currently held. */\n\tmaterialCount(): number {\n\t\treturn this.materials.size;\n\t}\n\n\t/** Number of distinct shared textures currently held. */\n\ttextureCount(): number {\n\t\treturn this.textures.size;\n\t}\n\n\t/**\n\t * Estimated GPU bytes for shared geometry attribute buffers. Best-effort —\n\t * actual GPU footprint depends on driver alignment, but this is a useful\n\t * relative metric for the profiler.\n\t */\n\tgeometryBytes(): number {\n\t\tlet total = 0;\n\t\tfor (const { resource } of this.geometries.values()) {\n\t\t\tfor (const attr of Object.values(resource.attributes)) {\n\t\t\t\tif ('array' in attr && (attr.array as ArrayBufferView).byteLength) {\n\t\t\t\t\ttotal += (attr.array as ArrayBufferView).byteLength;\n\t\t\t\t}\n\t\t\t}\n\t\t\tif (resource.index) {\n\t\t\t\ttotal += (resource.index.array as ArrayBufferView).byteLength;\n\t\t\t}\n\t\t}\n\t\treturn total;\n\t}\n\n\t/** Dispose every resource and clear the registry. */\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tfor (const { resource } of this.geometries.values()) resource.dispose();\n\t\tfor (const { resource } of this.materials.values()) resource.dispose();\n\t\tfor (const { resource } of this.textures.values()) resource.dispose();\n\t\tthis.geometries.clear();\n\t\tthis.materials.clear();\n\t\tthis.textures.clear();\n\t\tthis.disposed = true;\n\t}\n\n\t/** True after `dispose()` — callers should re-create the registry instead of using it. */\n\tisDisposed(): boolean {\n\t\treturn this.disposed;\n\t}\n\n\tprivate release<T extends Disposable>(map: Map<string, Entry<T>>, key: string): void {\n\t\tconst entry = map.get(key);\n\t\tif (!entry) return;\n\t\tentry.refCount--;\n\t\tif (entry.refCount <= 0) {\n\t\t\t// Defer disposal by one microtask. Under React StrictMode the\n\t\t\t// effect cleanup fires immediately followed by a remount + new\n\t\t\t// acquire — without the defer, the resource is disposed before\n\t\t\t// the remount can re-acquire it. The microtask gives the\n\t\t\t// reacquire a chance to bump refCount back above 0; if it\n\t\t\t// doesn't, we dispose for real.\n\t\t\tqueueMicrotask(() => {\n\t\t\t\tconst current = map.get(key);\n\t\t\t\tif (current && current.refCount <= 0) {\n\t\t\t\t\tcurrent.resource.dispose();\n\t\t\t\t\tmap.delete(key);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n}\n","import { defineComponent, defineResource, defineTag } from '@jamesyong42/reactive-ecs';\n\n/**\n * Compositor state-machine phase for an R3F widget (RFC-002).\n *\n * - `Hot` — Active + Visible + animating. Ticks + paints every invalidation.\n * - `Warm` — Active + Visible + idle. Texture cached, no tick, no paint.\n * - `Waking` — Active + Visible but no valid texture yet (just un-culled / un-dormant). One-shot paint then → `Warm`.\n * - `Cold` — Active + Culled. Off-screen, texture eviction-eligible.\n * - `Dormant` — Not Active. Eviction-protected so re-activation is instant.\n */\nexport type R3FPhase = 'Hot' | 'Warm' | 'Cold' | 'Waking' | 'Dormant';\n\n/** Pixel resolution + camera zoom at which a widget's texture was last painted. */\nexport type R3FPaintedAt = {\n\twidth: number;\n\theight: number;\n\tdpr: number;\n\tzoom: number;\n};\n\n/** Per-widget state tracked by the compositor. */\nexport type R3FRenderStateData = {\n\tphase: R3FPhase;\n\tpaintedAt: R3FPaintedAt;\n\t/** True while the widget has signalled an animation tick is in progress. */\n\tanimating: boolean;\n\t/** Bumped by widget state changes / animations that invalidate the texture. */\n\tpaintGeneration: number;\n\t/** Generation last successfully painted into the FBO. */\n\tfboGeneration: number;\n};\n\nconst DEFAULT_PAINTED_AT: R3FPaintedAt = { width: 0, height: 0, dpr: 1, zoom: 1 };\n\nexport const R3FRenderState = defineComponent<R3FRenderStateData>('R3FRenderState', {\n\tphase: 'Cold',\n\tpaintedAt: DEFAULT_PAINTED_AT,\n\tanimating: false,\n\tpaintGeneration: 0,\n\tfboGeneration: -1,\n});\n\n/**\n * Opt-in tag — a widget that wants per-frame ticking sets this. The state\n * machine treats `Visible + R3FAnimationSignal` as the trigger for `Hot`.\n * Removing the tag transitions back to `Warm` when the widget settles.\n */\nexport const R3FAnimationSignal = defineTag('R3FAnimationSignal');\n\n/** Global compositor memory + stagger budget. */\nexport type R3FRenderBudgetData = {\n\t/** Max bytes the widget render-target pool may consume. */\n\tmaxBytes: number;\n\t/** Current pool consumption (written by the pool once it lands in Phase 4). */\n\tcurrentBytes: number;\n\t/** Max widgets to repaint per composited frame. */\n\tmaxRepaintsPerFrame: number;\n};\n\nexport const R3FRenderBudget = defineResource<R3FRenderBudgetData>('R3FRenderBudget', {\n\tmaxBytes: 256 * 1024 * 1024,\n\tcurrentBytes: 0,\n\tmaxRepaintsPerFrame: 4,\n});\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport { SRGBColorSpace, WebGLRenderTarget } from 'three';\n\n/**\n * Bytes per pixel for the default render target format (RGBA8 colour +\n * 24-bit depth + 8-bit stencil packed into 32 bits = 8 bytes/pixel).\n */\nconst BYTES_PER_PIXEL = 8;\n\ntype PoolEntry = {\n\trt: WebGLRenderTarget;\n\t/** Pixel width = logicalWidth × dpr, rounded. */\n\tpixelWidth: number;\n\tpixelHeight: number;\n\tdpr: number;\n\tbytes: number;\n\t/** `performance.now()` of the most recent acquire — drives LRU eviction. */\n\tlastUsedMs: number;\n};\n\n/** Snapshot returned by {@link WidgetRenderTargetPool.entries}. */\nexport type PoolEntryInfo = {\n\tentityId: EntityId;\n\tbytes: number;\n\tlastUsedMs: number;\n};\n\n/**\n * Allocates one persistent `WebGLRenderTarget` per R3F widget entity. Used\n * by the compositor (RFC-002 Phase 4) so each widget paints into its own\n * texture instead of into the main canvas backbuffer.\n *\n * Acquire returns an existing FBO if its pixel resolution matches the\n * request; otherwise the old FBO is disposed and a new one created (sized\n * to the new resolution). Eviction under memory pressure is Phase 6 — this\n * pool grows monotonically until widgets are released.\n */\nexport class WidgetRenderTargetPool {\n\tprivate entries = new Map<EntityId, PoolEntry>();\n\tprivate totalBytes = 0;\n\tprivate disposed = false;\n\n\t/**\n\t * Get or create an FBO for `entityId` at the requested logical size and\n\t * device pixel ratio. If an FBO already exists at the same pixel\n\t * dimensions, returns it unchanged.\n\t */\n\tacquire(entityId: EntityId, width: number, height: number, dpr: number): WebGLRenderTarget {\n\t\tif (this.disposed) {\n\t\t\tthrow new Error('WidgetRenderTargetPool: cannot acquire after dispose');\n\t\t}\n\t\tconst pixelWidth = Math.max(1, Math.round(width * dpr));\n\t\tconst pixelHeight = Math.max(1, Math.round(height * dpr));\n\n\t\tconst now =\n\t\t\ttypeof performance !== 'undefined' && typeof performance.now === 'function'\n\t\t\t\t? performance.now()\n\t\t\t\t: 0;\n\n\t\tconst existing = this.entries.get(entityId);\n\t\tif (existing && existing.pixelWidth === pixelWidth && existing.pixelHeight === pixelHeight) {\n\t\t\texisting.lastUsedMs = now;\n\t\t\treturn existing.rt;\n\t\t}\n\n\t\tif (existing) {\n\t\t\texisting.rt.dispose();\n\t\t\tthis.totalBytes -= existing.bytes;\n\t\t}\n\n\t\t// MSAA matches the canvas's antialias setting so widget edges look\n\t\t// the same as the old direct-to-canvas pass.\n\t\tconst rt = new WebGLRenderTarget(pixelWidth, pixelHeight, { samples: 4 });\n\t\t// sRGB color space — without this, Three.js writes tone-mapped\n\t\t// LINEAR values to the FBO. The composition shader samples raw,\n\t\t// outputs raw, and the canvas backbuffer interprets the linear\n\t\t// values as if they were sRGB → washed-out / dark colours. Setting\n\t\t// SRGBColorSpace tells Three's built-in materials to apply sRGB\n\t\t// encoding when writing to this target, so the FBO already holds\n\t\t// display-ready values.\n\t\trt.texture.colorSpace = SRGBColorSpace;\n\t\tconst bytes = pixelWidth * pixelHeight * BYTES_PER_PIXEL;\n\t\tthis.entries.set(entityId, {\n\t\t\trt,\n\t\t\tpixelWidth,\n\t\t\tpixelHeight,\n\t\t\tdpr,\n\t\t\tbytes,\n\t\t\tlastUsedMs: now,\n\t\t});\n\t\tthis.totalBytes += bytes;\n\t\treturn rt;\n\t}\n\n\t/** Look up an FBO without creating one. */\n\tget(entityId: EntityId): WebGLRenderTarget | null {\n\t\treturn this.entries.get(entityId)?.rt ?? null;\n\t}\n\n\t/**\n\t * Refresh `lastUsedMs` without re-acquiring. The Compositor calls this\n\t * for every widget it samples in the composition pass — without it,\n\t * Warm widgets that never repaint would freeze their `lastUsedMs` at\n\t * the time of their last paint, and eviction LRU would treat\n\t * still-visible widgets as stale.\n\t */\n\ttouch(entityId: EntityId): void {\n\t\tconst entry = this.entries.get(entityId);\n\t\tif (!entry) return;\n\t\tentry.lastUsedMs =\n\t\t\ttypeof performance !== 'undefined' && typeof performance.now === 'function'\n\t\t\t\t? performance.now()\n\t\t\t\t: 0;\n\t}\n\n\t/**\n\t * Release `entityId`'s FBO. Returns true if something was released.\n\t * Safe to call after dispose — returns false rather than corrupting the\n\t * byte counter or double-disposing the target.\n\t */\n\trelease(entityId: EntityId): boolean {\n\t\tif (this.disposed) return false;\n\t\tconst entry = this.entries.get(entityId);\n\t\tif (!entry) return false;\n\t\tentry.rt.dispose();\n\t\t// Clamp at zero so out-of-order teardown never makes the counter\n\t\t// negative — entries.delete + dispose() race during Compositor\n\t\t// unmount could otherwise produce a misleading negative gauge.\n\t\tthis.totalBytes = Math.max(0, this.totalBytes - entry.bytes);\n\t\tthis.entries.delete(entityId);\n\t\treturn true;\n\t}\n\n\t/** Total GPU bytes consumed by the pool. */\n\tbytesUsed(): number {\n\t\treturn this.totalBytes;\n\t}\n\n\t/** Number of FBOs currently held. */\n\tsize(): number {\n\t\treturn this.entries.size;\n\t}\n\n\t/** True after `dispose()` — callers should re-create the pool instead of using it. */\n\tisDisposed(): boolean {\n\t\treturn this.disposed;\n\t}\n\n\t/** Iterate live entries. */\n\tforEach(cb: (entityId: EntityId, rt: WebGLRenderTarget) => void): void {\n\t\tfor (const [id, entry] of this.entries) cb(id, entry.rt);\n\t}\n\n\t/**\n\t * Snapshot of every live entry's `bytes` + `lastUsedMs` — input for the\n\t * eviction algorithm in {@link selectEvictions}.\n\t */\n\tentryInfos(): PoolEntryInfo[] {\n\t\tconst out: PoolEntryInfo[] = [];\n\t\tfor (const [id, entry] of this.entries) {\n\t\t\tout.push({ entityId: id, bytes: entry.bytes, lastUsedMs: entry.lastUsedMs });\n\t\t}\n\t\treturn out;\n\t}\n\n\t/** Dispose every FBO. After this, acquire throws and release returns false. */\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tfor (const entry of this.entries.values()) entry.rt.dispose();\n\t\tthis.entries.clear();\n\t\tthis.totalBytes = 0;\n\t\tthis.disposed = true;\n\t}\n}\n","/**\n * Hysteresis-banded zoom-resolution policy (RFC-002 § Zoom handling).\n *\n * A widget's FBO is allocated at `widget bounds × dpr × band(zoom)` pixels.\n * As long as the camera zoom stays within `[band × 0.5, band × 2]` of the\n * band the widget was painted at, no repaint is needed — the composition\n * shader does a small up/down sample. Crossing that gap triggers a repaint\n * at the new band, snapping back to a 1:1 (or near-1:1) sample ratio.\n *\n * Bands are powers of 2 so each band covers a 4× display range. With the\n * default ladder (0.0625 ↔ 16) we span camera zooms 0.03125 ↔ 32 — covering\n * essentially every realistic infinite-canvas zoom level.\n */\nexport const ZOOM_BANDS = [0.0625, 0.125, 0.25, 0.5, 1, 2, 4, 8, 16] as const;\n\n/**\n * Pick the band whose `[band × 0.5, band × 2]` range contains the current\n * zoom. The smallest band ≥ zoom (after rounding to a band edge) is the\n * canonical choice — keeps the texture resolution at or above what the\n * display needs.\n */\nexport function selectBand(zoom: number): number {\n\tif (zoom <= ZOOM_BANDS[0]) return ZOOM_BANDS[0];\n\tif (zoom >= ZOOM_BANDS[ZOOM_BANDS.length - 1]) return ZOOM_BANDS[ZOOM_BANDS.length - 1];\n\tfor (const b of ZOOM_BANDS) {\n\t\tif (zoom <= b) return b;\n\t}\n\treturn ZOOM_BANDS[ZOOM_BANDS.length - 1];\n}\n\n/**\n * Returns true if the widget needs to be repainted because the camera\n * zoom has wandered outside the tolerance window of the band it was\n * painted at.\n *\n * `paintedBand` of 0 (or negative) means the widget has never been\n * painted yet — caller should treat that as \"needs paint\" through other\n * channels (Waking phase / paintGeneration > fboGeneration).\n */\nexport function isOutOfBand(currentZoom: number, paintedBand: number): boolean {\n\tif (paintedBand <= 0) return false;\n\tconst ratio = currentZoom / paintedBand;\n\treturn ratio > 2 || ratio < 0.5;\n}\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport { useFrame, useThree } from '@react-three/fiber';\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport { Mesh, OrthographicCamera, PlaneGeometry, type Scene } from 'three';\nimport {\n\tCard,\n\tCardOverlapHotPoint,\n\tDragging,\n\tOverlapCandidate,\n\tOverlapTarget,\n\tTransform2D,\n\tWidget,\n} from '../../ecs/components.js';\nimport type { LayoutEngine } from '../../ecs/engine/index.js';\nimport { CompositionMaterial } from './CompositionMaterial.js';\nimport { CompositorContext, type CompositorWidgetEntry } from './CompositorContext.js';\nimport { type EvictionCandidate, selectEvictions } from './eviction.js';\nimport { ResourceRegistry } from './ResourceRegistry.js';\nimport { R3FRenderBudget, R3FRenderState } from './state.js';\nimport type { WidgetRegistry } from './WidgetRegistry.js';\nimport { WidgetRenderTargetPool } from './WidgetRenderTargetPool.js';\nimport { isOutOfBand, selectBand } from './ZoomBands.js';\n\n/**\n * Drives the per-widget paint + composition render loop (RFC-002 Phase 4).\n *\n * Lifecycle each invalidation:\n * 1. For every registered widget whose phase is Hot or Waking (or whose\n * paintGeneration > fboGeneration), bind its FBO and render its scene\n * with its widget-local camera.\n * 2. Update each widget's composition quad (position, scale, texture).\n * 3. Render the composition scene to the canvas backbuffer with a\n * world-space orthographic camera matching the engine camera.\n *\n * Owns the `WidgetRenderTargetPool`. Children mount `<VirtualWidget />`\n * instances which register their scene+camera via context.\n *\n * Replaces the old `R3FWidgetSlot` + `CameraSync` pair.\n */\nexport function Compositor({\n\tengine,\n\twidgetRegistry,\n\tchildren,\n}: {\n\tengine: LayoutEngine;\n\t/**\n\t * Stable per-canvas widget registry created in `R3FManager`. Shared\n\t * with the R3F event factory so the EventRouter (RFC-006) can resolve\n\t * which widget owns a pointer event without a separate registration\n\t * path. The Compositor populates it on `register` / unregister; reads\n\t * it back during the paint + composition loop.\n\t */\n\twidgetRegistry: WidgetRegistry;\n\tchildren: React.ReactNode;\n}) {\n\tconst { gl, size, scene: defaultScene, set } = useThree();\n\tconst invalidate = useThree((s) => s.invalidate);\n\n\t// Pool + registry are created lazily and re-created if a previous instance\n\t// was disposed (React StrictMode mounts → cleanup → remount; the cleanup\n\t// disposes the resource but the same component instance is re-used, so\n\t// the ref still points at the disposed object).\n\tconst poolRef = useRef<WidgetRenderTargetPool | null>(null);\n\tif (!poolRef.current || poolRef.current.isDisposed()) {\n\t\tpoolRef.current = new WidgetRenderTargetPool();\n\t}\n\tconst pool = poolRef.current;\n\tconst registryRef = useRef<ResourceRegistry | null>(null);\n\tif (!registryRef.current || registryRef.current.isDisposed()) {\n\t\tregistryRef.current = new ResourceRegistry();\n\t}\n\tconst registry = registryRef.current;\n\n\t// Per-Compositor unit-square geometry — shared across all composition\n\t// quads in this canvas, scaled per-mesh. Not disposed in cleanup: GC\n\t// reclaims it when the Compositor instance is fully released, and\n\t// disposing here would leave a stale BufferGeometry across StrictMode's\n\t// double-mount cycle (Three.js doesn't expose a public \"isDisposed\").\n\tconst quadGeometry = useMemo(() => new PlaneGeometry(1, 1), []);\n\n\t// Per-widget composition quad mesh kept in the default scene. Mounted /\n\t// removed as widgets register / unregister.\n\tconst quadsRef = useRef(new Map<EntityId, Mesh>());\n\n\t// Per-widget lift-scale tracker. CSS chrome scales 1× → 1.05× via a\n\t// 180ms transition on Dragging; mirroring it here keeps the WebGL\n\t// content visually pegged to the chrome's bounds. Same target value,\n\t// independent lerp — close enough that the user perceives them as one\n\t// moving element.\n\tconst liftScaleRef = useRef(new Map<EntityId, number>());\n\n\t// Dynamic DPR: drop the canvas pixel ratio while the user is actively\n\t// gesturing (pinch / wheel) and restore on idle. Cuts composition GPU\n\t// cost during continuous interaction without making the static idle\n\t// view fuzzy. Tracks the last applied DPR so we only call\n\t// gl.setPixelRatio (which reallocates backing buffers) on transitions.\n\tconst lastDprRef = useRef(-1);\n\tconst idleDpr = typeof window !== 'undefined' ? window.devicePixelRatio : 1;\n\tconst gestureDpr = Math.min(idleDpr, 1);\n\n\t// World-space ortho camera that drives the composition pass. Replaces\n\t// the role the previous CameraSync played for the shared scene.\n\tconst compCamera = useMemo(() => new OrthographicCamera(0, 1, 0, -1, 0.1, 10000), []);\n\n\t// Make the compositor's camera the default Canvas camera so any built-in\n\t// R3F utilities (raycaster, etc.) have a sensible reference.\n\tuseEffect(() => {\n\t\tset({ camera: compCamera });\n\t}, [set, compCamera]);\n\n\tconst register = useCallback(\n\t\t(entityId: EntityId, entry: CompositorWidgetEntry) => {\n\t\t\tconst unregisterFromRegistry = widgetRegistry.register(entityId, entry);\n\n\t\t\t// Spawn a composition quad for this widget. CompositionMaterial\n\t\t\t// is a minimal sample-and-write shader (no tone mapping — FBO\n\t\t\t// already holds sRGB-encoded values per RFC-002 § sRGB FBO fix)\n\t\t\t// with uDraggedRect / uIsDragged uniforms for the RFC-003 drag\n\t\t\t// clip. Per-instance so the uniforms are independent.\n\t\t\tconst mesh = new Mesh(quadGeometry, new CompositionMaterial());\n\t\t\tmesh.frustumCulled = false;\n\t\t\tmesh.visible = false; // Hidden until the widget has painted at least once.\n\t\t\tdefaultScene.add(mesh);\n\t\t\tquadsRef.current.set(entityId, mesh);\n\n\t\t\t// Trigger an initial render so the widget can paint.\n\t\t\tentry.requestRepaint();\n\n\t\t\treturn () => {\n\t\t\t\tunregisterFromRegistry();\n\t\t\t\tconst m = quadsRef.current.get(entityId);\n\t\t\t\tif (m) {\n\t\t\t\t\tdefaultScene.remove(m);\n\t\t\t\t\t(m.material as CompositionMaterial).dispose();\n\t\t\t\t\tquadsRef.current.delete(entityId);\n\t\t\t\t}\n\t\t\t\tliftScaleRef.current.delete(entityId);\n\t\t\t\tpool.release(entityId);\n\t\t\t};\n\t\t},\n\t\t[defaultScene, pool, quadGeometry, widgetRegistry],\n\t);\n\n\tconst ctxValue = useMemo(() => ({ pool, registry, register }), [pool, registry, register]);\n\n\t// Dispose pool + registry on unmount (the lazy-init at the top of the\n\t// component re-creates them on the next render if React mounts us\n\t// again — StrictMode cleanup-then-remount, HMR, etc.).\n\tuseEffect(() => {\n\t\treturn () => {\n\t\t\tpool.dispose();\n\t\t\tregistry.dispose();\n\t\t};\n\t}, [pool, registry]);\n\n\t// Custom render loop. Priority > 0 suppresses R3F's default render so we\n\t// own the entire pass.\n\tuseFrame(() => {\n\t\tconst cam = engine.getCamera();\n\n\t\t// Sync composition camera frustum + position to the engine camera.\n\t\tcompCamera.left = 0;\n\t\tcompCamera.right = size.width / cam.zoom;\n\t\tcompCamera.top = 0;\n\t\tcompCamera.bottom = -(size.height / cam.zoom);\n\t\tcompCamera.position.set(cam.x, -cam.y, 1000);\n\t\tcompCamera.updateProjectionMatrix();\n\n\t\t// Apply dynamic DPR for the canvas backbuffer. setPixelRatio\n\t\t// reallocates buffers, so only call it on actual transitions.\n\t\tconst targetDpr = cam.gesturing ? gestureDpr : idleDpr;\n\t\tif (lastDprRef.current !== targetDpr) {\n\t\t\tgl.setPixelRatio(targetDpr);\n\t\t\tlastDprRef.current = targetDpr;\n\t\t}\n\n\t\tconst dpr = gl.getPixelRatio();\n\t\tconst world = engine.world;\n\n\t\t// Share scene.environment across per-widget scenes. In the old\n\t\t// shared-scene architecture, drei's <Environment> mounted inside any\n\t\t// widget set IBL on the single root scene, so every PBR material got\n\t\t// the env reflection for free. With per-widget scenes each portal\n\t\t// sees only its own env; PBR materials in other widgets (especially\n\t\t// metallic ones) would render unlit. Propagate the first env we find\n\t\t// to every widget scene so global IBL behaviour is preserved.\n\t\t//\n\t\t// Root-scene fallback (RFC-004 Phase 5 follow-up): also look at the\n\t\t// Canvas's default scene for an environment. Mounting <Environment>\n\t\t// as a sibling of <Compositor> (i.e. directly in R3FManager's Canvas\n\t\t// tree, not inside any VirtualWidget) gives a canvas-level IBL\n\t\t// provider that survives widget navigation — consuming an env-\n\t\t// bearing card into a folder no longer kills every other card's\n\t\t// lighting when the user returns to root.\n\t\tlet sharedEnv = null as Scene['environment'];\n\t\tif (defaultScene.environment) {\n\t\t\tsharedEnv = defaultScene.environment;\n\t\t} else {\n\t\t\tfor (const [, entry] of widgetRegistry.all()) {\n\t\t\t\tif (entry.scene.environment) {\n\t\t\t\t\tsharedEnv = entry.scene.environment;\n\t\t\t\t\tbreak;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif (sharedEnv) {\n\t\t\tfor (const [eid, entry] of widgetRegistry.all()) {\n\t\t\t\tif (entry.scene.environment === sharedEnv) continue;\n\t\t\t\tentry.scene.environment = sharedEnv;\n\t\t\t\t// Re-render so the new IBL is reflected in this widget's FBO.\n\t\t\t\tconst s = world.getComponent(eid, R3FRenderState);\n\t\t\t\tif (s) {\n\t\t\t\t\tworld.setComponent(eid, R3FRenderState, {\n\t\t\t\t\t\t...s,\n\t\t\t\t\t\tpaintGeneration: s.paintGeneration + 1,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Per-widget paint pass. Each paint is wrapped in try/finally so a\n\t\t// throwing widget can't leave the GL render target bound to its FBO\n\t\t// — that would corrupt the subsequent composition pass into the\n\t\t// canvas backbuffer.\n\t\tconst band = selectBand(cam.zoom);\n\t\tconst effectiveDpr = dpr * band;\n\t\tlet widgetsRepainted = 0;\n\t\tfor (const [entityId, entry] of widgetRegistry.all()) {\n\t\t\tconst wt = world.getComponent(entityId, Transform2D);\n\t\t\tif (!wt) continue;\n\t\t\tconst state = world.getComponent(entityId, R3FRenderState);\n\t\t\tif (!state) continue;\n\n\t\t\tconst phaseWantsPaint = state.phase === 'Hot' || state.phase === 'Waking';\n\t\t\tconst generationDirty = state.paintGeneration > state.fboGeneration;\n\t\t\t// Hysteresis-banded zoom: if camera zoom has wandered outside the\n\t\t\t// band the widget was painted at, repaint at the new band so the\n\t\t\t// composition sample ratio snaps back near 1:1.\n\t\t\t//\n\t\t\t// While the user is actively gesturing (pinch / wheel zoom), we\n\t\t\t// SKIP band-driven repaints — without this, a continuous pinch\n\t\t\t// would trigger a repaint storm across every visible widget at\n\t\t\t// each band crossing. The composition shader's bilinear stretch\n\t\t\t// is \"good enough\" during the gesture; a single batch repaint\n\t\t\t// happens once the user stops.\n\t\t\tconst bandChanged = !cam.gesturing && isOutOfBand(cam.zoom, state.paintedAt.zoom);\n\t\t\tif (!phaseWantsPaint && !generationDirty && !bandChanged && pool.get(entityId) !== null) {\n\t\t\t\tcontinue;\n\t\t\t}\n\n\t\t\tconst fbo = pool.acquire(entityId, wt.width, wt.height, effectiveDpr);\n\t\t\tgl.setRenderTarget(fbo);\n\t\t\ttry {\n\t\t\t\tgl.setClearColor(0x000000, 0);\n\t\t\t\tgl.clear(true, true, false);\n\t\t\t\tgl.render(entry.scene, entry.camera);\n\t\t\t} finally {\n\t\t\t\tgl.setRenderTarget(null);\n\t\t\t}\n\n\t\t\t// Mark the widget as painted at this generation. paintedAt.zoom\n\t\t\t// stores the BAND (not the live camera zoom) so subsequent ticks\n\t\t\t// compare against the same hysteresis edge.\n\t\t\tworld.setComponent(entityId, R3FRenderState, {\n\t\t\t\t...state,\n\t\t\t\tfboGeneration: state.paintGeneration,\n\t\t\t\tpaintedAt: {\n\t\t\t\t\twidth: wt.width,\n\t\t\t\t\theight: wt.height,\n\t\t\t\t\tdpr: effectiveDpr,\n\t\t\t\t\tzoom: band,\n\t\t\t\t},\n\t\t\t});\n\t\t\twidgetsRepainted++;\n\t\t}\n\n\t\t// Eviction pass — if the pool is over budget after this frame's\n\t\t// paints, release FBOs in priority order until back under: Cold (LRU)\n\t\t// → Warm (LRU) → Dormant (LRU). Hot / Waking are never evicted. The\n\t\t// state machine sees fboGeneration=-1 and transitions evicted\n\t\t// widgets to Waking on next paint when they're visible again.\n\t\tconst budget = world.getResource(R3FRenderBudget);\n\t\tif (pool.bytesUsed() > budget.maxBytes) {\n\t\t\tconst candidates: EvictionCandidate[] = [];\n\t\t\tfor (const info of pool.entryInfos()) {\n\t\t\t\tconst s = world.getComponent(info.entityId, R3FRenderState);\n\t\t\t\tif (!s) continue;\n\t\t\t\tcandidates.push({\n\t\t\t\t\tentityId: info.entityId,\n\t\t\t\t\tphase: s.phase,\n\t\t\t\t\tbytes: info.bytes,\n\t\t\t\t\tlastUsedMs: info.lastUsedMs,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst toEvict = selectEvictions(candidates, pool.bytesUsed(), budget.maxBytes);\n\t\t\tfor (const eid of toEvict) {\n\t\t\t\tconst s = world.getComponent(eid, R3FRenderState);\n\t\t\t\t// Dormant evictions are logged at debug level so the memory\n\t\t\t\t// budget can be tuned when they happen — RFC § Phase 6.\n\t\t\t\tif (s?.phase === 'Dormant') {\n\t\t\t\t\tconsole.debug(\n\t\t\t\t\t\t'[r3f-compositor] evicting Dormant widget',\n\t\t\t\t\t\teid,\n\t\t\t\t\t\t'— consider raising R3FRenderBudget.maxBytes',\n\t\t\t\t\t);\n\t\t\t\t}\n\t\t\t\tpool.release(eid);\n\t\t\t\tif (s) {\n\t\t\t\t\tworld.setComponent(eid, R3FRenderState, { ...s, fboGeneration: -1 });\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// First pass: locate the (single) dragged R3F widget if any.\n\t\t//\n\t\t// Compute the discard rect ONLY if the dragged widget has the\n\t\t// `Card` component — only card widgets have CSS chrome that\n\t\t// needs to be defended from being painted over by other R3F\n\t\t// content. Card-less R3F widgets (CrystalWidget, FloatingCube)\n\t\t// just want the renderOrder bump so their own quad draws on\n\t\t// top; they don't need to clip neighbours. RFC-003 § Cases.\n\t\tlet draggedEntityId: EntityId | null = null;\n\t\tlet draggedRectMinX = 0;\n\t\tlet draggedRectMinY = 0;\n\t\tlet draggedRectMaxX = 0;\n\t\tlet draggedRectMaxY = 0;\n\t\tconst canvasHeightPx = size.height * dpr;\n\t\tfor (const entityId of widgetRegistry.keys()) {\n\t\t\tif (!world.hasTag(entityId, Dragging)) continue;\n\t\t\tconst w = world.getComponent(entityId, Widget);\n\t\t\tif (w?.surface !== 'webgl') continue;\n\t\t\tdraggedEntityId = entityId;\n\t\t\t// Only compute the rect for card widgets — non-card widgets\n\t\t\t// leave the rect at zero, which the shader treats as \"no\n\t\t\t// discard\" while still respecting renderOrder.\n\t\t\tif (!world.hasComponent(entityId, Card)) break;\n\t\t\tconst dt = world.getComponent(entityId, Transform2D);\n\t\t\tif (!dt) break;\n\t\t\t// Use the live lift scale so the discard rect tracks the lifted\n\t\t\t// chrome bounds (1.05× during drag).\n\t\t\tconst lift = liftScaleRef.current.get(entityId) ?? 1;\n\t\t\tconst cx = dt.x + dt.width / 2;\n\t\t\tconst cy = dt.y + dt.height / 2;\n\t\t\tconst halfW = (dt.width * lift) / 2;\n\t\t\tconst halfH = (dt.height * lift) / 2;\n\t\t\tconst minWx = cx - halfW;\n\t\t\tconst maxWx = cx + halfW;\n\t\t\tconst minWy = cy - halfH;\n\t\t\tconst maxWy = cy + halfH;\n\t\t\t// World → physical px in gl_FragCoord space (origin bottom-left).\n\t\t\tconst sxMin = (minWx - cam.x) * cam.zoom * dpr;\n\t\t\tconst sxMax = (maxWx - cam.x) * cam.zoom * dpr;\n\t\t\tconst syTop = (minWy - cam.y) * cam.zoom * dpr;\n\t\t\tconst syBot = (maxWy - cam.y) * cam.zoom * dpr;\n\t\t\tdraggedRectMinX = sxMin;\n\t\t\tdraggedRectMinY = canvasHeightPx - syBot;\n\t\t\tdraggedRectMaxX = sxMax;\n\t\t\tdraggedRectMaxY = canvasHeightPx - syTop;\n\t\t\tbreak;\n\t\t}\n\n\t\t// Update composition quads. A quad is only made visible after its\n\t\t// widget's FBO has been painted at least once — checked via\n\t\t// fboGeneration >= 0 so a freshly-acquired (empty) target never gets\n\t\t// sampled into the composition.\n\t\t//\n\t\t// The visual lift (box-shadow + 1.05× scale) is owned by DOM\n\t\t// CardChrome (CSS transition). We mirror only the scale here so the\n\t\t// WebGL content stays pegged to the chrome's bounds during drag.\n\t\t// Lerp toward the same target value — independent timing but close\n\t\t// enough that the user perceives them as one moving element.\n\t\tlet liftSettling = false;\n\t\tfor (const [entityId, mesh] of quadsRef.current) {\n\t\t\tconst qt = world.getComponent(entityId, Transform2D);\n\t\t\tconst state = world.getComponent(entityId, R3FRenderState);\n\t\t\tconst fbo = pool.get(entityId);\n\t\t\tif (!qt || !fbo || !state || state.fboGeneration < 0) {\n\t\t\t\tmesh.visible = false;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\t// Refresh LRU timestamp — eviction would otherwise treat\n\t\t\t// still-composited Warm widgets as stale because pool.acquire is\n\t\t\t// only called on repaint and Warm widgets never repaint.\n\t\t\tpool.touch(entityId);\n\n\t\t\tconst dragging = world.hasTag(entityId, Dragging);\n\t\t\tconst targetScale = dragging ? 1.05 : 1;\n\t\t\tlet scale = liftScaleRef.current.get(entityId) ?? 1;\n\t\t\tscale += (targetScale - scale) * 0.2;\n\t\t\tif (Math.abs(targetScale - scale) > 0.001) {\n\t\t\t\tliftSettling = true;\n\t\t\t} else {\n\t\t\t\tscale = targetScale;\n\t\t\t}\n\t\t\tliftScaleRef.current.set(entityId, scale);\n\n\t\t\tmesh.visible = true;\n\t\t\tmesh.position.set(qt.x + qt.width / 2, -(qt.y + qt.height / 2), 0);\n\t\t\tmesh.scale.set(qt.width * scale, qt.height * scale, 1);\n\t\t\t// renderOrder bump: dragged R3F quad draws last in the\n\t\t\t// composition pass (case C — even without chrome it sits on\n\t\t\t// top of every other R3F widget it overlaps).\n\t\t\tmesh.renderOrder = entityId === draggedEntityId ? 99 : 1;\n\t\t\tconst material = mesh.material as CompositionMaterial;\n\t\t\tmaterial.setMap(fbo.texture);\n\t\t\tmaterial.setIsDragged(entityId === draggedEntityId);\n\t\t\tmaterial.setDraggedRect(draggedRectMinX, draggedRectMinY, draggedRectMaxX, draggedRectMaxY);\n\n\t\t\t// RFC-004 § Phase 3 — overlap glow + match rim.\n\t\t\t// CardOverlapHotPoint.{x,y} are in [0..1] with (0,0) at the\n\t\t\t// card's top-left (Y-down world convention). The quad's vUv has\n\t\t\t// (0,0) at bottom-left (Three's default PlaneGeometry UVs), so\n\t\t\t// flip Y when translating to uv space.\n\t\t\tconst hot = world.getComponent(entityId, CardOverlapHotPoint);\n\t\t\tif (hot && world.hasTag(entityId, OverlapCandidate)) {\n\t\t\t\tmaterial.setHotPoint(hot.x, 1 - hot.y);\n\t\t\t\tmaterial.setHotStrength(hot.strength);\n\t\t\t} else {\n\t\t\t\tmaterial.setHotStrength(0);\n\t\t\t}\n\t\t\tmaterial.setIsOverlapTarget(world.hasTag(entityId, OverlapTarget));\n\t\t}\n\n\t\t// Composition pass to the canvas backbuffer. Explicit setRenderTarget\n\t\t// guards against any future code path leaving an FBO bound.\n\t\tgl.setRenderTarget(null);\n\t\tgl.setClearColor(0x000000, 0);\n\t\tgl.clear(true, true, false);\n\t\tgl.render(defaultScene, compCamera);\n\n\t\t// Stash this frame's repaint count where ProfilerProbe can read it.\n\t\tCOMPOSITOR_TELEMETRY.widgetsRepainted = widgetsRepainted;\n\t\tCOMPOSITOR_TELEMETRY.fboBytes = pool.bytesUsed();\n\n\t\t// Self-sustain the demand loop while: any widget is in Hot phase\n\t\t// (animation-signalled widgets — e.g. rotating mesh — need\n\t\t// continuous frames), OR any widget's lift scale is still settling\n\t\t// toward its target. invalidate() inside a useFrame sets\n\t\t// internal.frames to 2, so the loop keeps spinning.\n\t\tlet anyHot = false;\n\t\tfor (const eid of widgetRegistry.keys()) {\n\t\t\tconst s = world.getComponent(eid, R3FRenderState);\n\t\t\tif (s?.phase === 'Hot') {\n\t\t\t\tanyHot = true;\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tif (anyHot || liftSettling) invalidate();\n\t}, 1);\n\n\treturn <CompositorContext.Provider value={ctxValue}>{children}</CompositorContext.Provider>;\n}\n\n/**\n * Shared between Compositor and ProfilerProbe so the probe can record FBO\n * bytes and per-frame repaint counts without an extra subscription path.\n * Module-scoped because both components live in the same canvas.\n */\nexport const COMPOSITOR_TELEMETRY = {\n\twidgetsRepainted: 0,\n\tfboBytes: 0,\n};\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport { createPortal, useThree } from '@react-three/fiber';\nimport { useEffect, useLayoutEffect, useMemo } from 'react';\nimport { OrthographicCamera, Scene } from 'three';\nimport { Transform2D } from '../../ecs/components.js';\nimport { useLayoutEngine } from '../../react/context/engine-context.js';\nimport { useComponent } from '../../react/hooks/ecs.js';\nimport type { R3FWidgetProps } from '../../react/widgets/registry.js';\nimport { useCompositor } from './CompositorContext.js';\nimport { R3FRenderState } from './state.js';\n\n/**\n * Mounts one R3F widget into its own Three.js scene + ortho camera so it\n * can be painted into a private `WebGLRenderTarget` instead of the main\n * canvas backbuffer.\n *\n * The user component is rendered in widget-local space — origin at centre,\n * X right, Y up, dimensions = (Transform2D.width, Transform2D.height) in\n * frame-local world units.\n * That matches the contract the previous `R3FWidgetSlot` exposed, so user\n * widget code (e.g. `geometry-card`) needs no changes.\n *\n * VirtualWidget itself does not paint or composite — that's the\n * Compositor's job. We just create the scene/camera and register them so\n * the Compositor can iterate widgets in its render loop.\n */\nexport function VirtualWidget({\n\tentityId,\n\tcomponent: Component,\n}: {\n\tentityId: EntityId;\n\tcomponent: React.ComponentType<R3FWidgetProps>;\n}) {\n\tconst { register } = useCompositor();\n\tconst invalidate = useThree((s) => s.invalidate);\n\tconst engine = useLayoutEngine();\n\n\t// One scene + camera per widget, stable across re-renders.\n\tconst scene = useMemo(() => new Scene(), []);\n\tconst camera = useMemo(() => new OrthographicCamera(-1, 1, 1, -1, 0.1, 1000), []);\n\tuseEffect(() => {\n\t\t// Camera looks at origin from +Z so widget content drawn in the XY\n\t\t// plane is visible.\n\t\tcamera.position.set(0, 0, 100);\n\t\tcamera.lookAt(0, 0, 0);\n\t}, [camera]);\n\n\tconst t = useComponent(entityId, Transform2D);\n\tconst w = t?.width ?? 0;\n\tconst h = t?.height ?? 0;\n\n\t// Recompute the camera frustum + signal a repaint when bounds change.\n\t// useLayoutEffect (not render-time) so the mutation runs after commit\n\t// — bumping paintGeneration through setComponent during render would\n\t// trigger a re-entrant update.\n\tuseLayoutEffect(() => {\n\t\tif (!w || !h) return;\n\t\tcamera.left = -w / 2;\n\t\tcamera.right = w / 2;\n\t\tcamera.top = h / 2;\n\t\tcamera.bottom = -h / 2;\n\t\tcamera.updateProjectionMatrix();\n\t\t// The widget's FBO needs to be re-painted at the new dimensions.\n\t\t// Bump paintGeneration so the compositor's generationDirty check\n\t\t// picks it up; without this, a Warm widget on resize would keep\n\t\t// its old-sized FBO sampled into a new-sized quad (stretched).\n\t\tconst current = engine.world.getComponent(entityId, R3FRenderState);\n\t\tif (current) {\n\t\t\tengine.world.setComponent(entityId, R3FRenderState, {\n\t\t\t\t...current,\n\t\t\t\tpaintGeneration: current.paintGeneration + 1,\n\t\t\t});\n\t\t}\n\t\tinvalidate();\n\t}, [camera, w, h, engine, entityId, invalidate]);\n\n\t// Register with the Compositor on mount, deregister on unmount.\n\tuseEffect(() => {\n\t\tconst unregister = register(entityId, {\n\t\t\tscene,\n\t\t\tcamera,\n\t\t\trequestRepaint: invalidate,\n\t\t});\n\t\treturn unregister;\n\t}, [entityId, register, scene, camera, invalidate]);\n\n\tif (!t) return null;\n\n\t// Portal mounts the React tree (and its useFrame / useState / etc.) into\n\t// the widget's own scene rather than the main R3F scene.\n\treturn createPortal(<Component entityId={entityId} width={t.width} height={t.height} />, scene);\n}\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport type { CompositorWidgetEntry } from './CompositorContext.js';\n\n/**\n * Stable per-canvas registry of R3F widget scenes + cameras (RFC-006).\n *\n * Created in `R3FManager` so it's reachable both by the `Compositor`\n * (which adds/removes widgets as `VirtualWidget` mounts) and by the\n * R3F event factory (which resolves the active widget by entityId\n * returned from `engine.pickAt`, then looks up its scene + camera here).\n *\n * Plain Map under the hood — the wrapping class exists to give the\n * registry a stable identity across React renders and to keep the read\n * surface (`get`, `keys`, `all`) discoverable from both consumers.\n */\nexport class WidgetRegistry {\n\tprivate readonly entries = new Map<EntityId, CompositorWidgetEntry>();\n\n\tregister(entityId: EntityId, entry: CompositorWidgetEntry): () => void {\n\t\tthis.entries.set(entityId, entry);\n\t\treturn () => this.entries.delete(entityId);\n\t}\n\n\tget(entityId: EntityId): CompositorWidgetEntry | undefined {\n\t\treturn this.entries.get(entityId);\n\t}\n\n\tall(): IterableIterator<[EntityId, CompositorWidgetEntry]> {\n\t\treturn this.entries.entries();\n\t}\n\n\tkeys(): IterableIterator<EntityId> {\n\t\treturn this.entries.keys();\n\t}\n\n\tvalues(): IterableIterator<CompositorWidgetEntry> {\n\t\treturn this.entries.values();\n\t}\n\n\tclear(): void {\n\t\tthis.entries.clear();\n\t}\n}\n","import { useEffect } from 'react';\nimport { Active, Culled, Visible, Widget } from '../../ecs/components.js';\nimport type { LayoutEngine } from '../../ecs/engine/index.js';\nimport { R3FAnimationSignal, type R3FPhase, R3FRenderState } from './state.js';\n\n/**\n * Updates `R3FRenderState` phases for every R3F (`surface === 'webgl'`)\n * widget after each engine tick.\n *\n * At this phase the state machine is bookkeeping only — it does not\n * allocate or paint render targets (that arrives in Phase 4). Consumers\n * such as widget `useFrame` callbacks read the phase to decide whether\n * to do work this frame.\n *\n * Transition rules match RFC-002 § State machine:\n *\n * Active + Visible + R3FAnimationSignal → Hot\n * Active + Visible (idle) → Warm (or Waking if texture invalid — irrelevant pre-Phase-4)\n * Active + Culled → Cold\n * !Active → Dormant\n */\nexport function WidgetStateMachine({ engine }: { engine: LayoutEngine }) {\n\tuseEffect(() => {\n\t\tconst world = engine.world;\n\n\t\tfunction updatePhases() {\n\t\t\tfor (const entity of world.query(Widget)) {\n\t\t\t\tconst widget = world.getComponent(entity, Widget);\n\t\t\t\tif (!widget || widget.surface !== 'webgl') continue;\n\n\t\t\t\tconst current = world.getComponent(entity, R3FRenderState);\n\t\t\t\tconst animating = world.hasTag(entity, R3FAnimationSignal);\n\t\t\t\t// fboGeneration starts at -1 and is bumped to >= 0 by the\n\t\t\t\t// compositor after a successful paint. \"Has valid FBO\" is the\n\t\t\t\t// signal the state machine needs to distinguish Waking from\n\t\t\t\t// Warm.\n\t\t\t\tconst hasFbo = (current?.fboGeneration ?? -1) >= 0;\n\n\t\t\t\tconst nextPhase = computePhase(\n\t\t\t\t\tworld.hasTag(entity, Active),\n\t\t\t\t\tworld.hasTag(entity, Visible),\n\t\t\t\t\tworld.hasTag(entity, Culled),\n\t\t\t\t\tanimating,\n\t\t\t\t\thasFbo,\n\t\t\t\t);\n\n\t\t\t\tif (!current) {\n\t\t\t\t\tworld.addComponent(entity, R3FRenderState, {\n\t\t\t\t\t\tphase: nextPhase,\n\t\t\t\t\t\tpaintedAt: { width: 0, height: 0, dpr: 1, zoom: 1 },\n\t\t\t\t\t\tanimating,\n\t\t\t\t\t\tpaintGeneration: 0,\n\t\t\t\t\t\tfboGeneration: -1,\n\t\t\t\t\t});\n\t\t\t\t} else if (current.phase !== nextPhase || current.animating !== animating) {\n\t\t\t\t\tworld.setComponent(entity, R3FRenderState, {\n\t\t\t\t\t\t...current,\n\t\t\t\t\t\tphase: nextPhase,\n\t\t\t\t\t\tanimating,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t// Run once on mount to populate phases for pre-existing widgets, then\n\t\t// re-run after each engine frame.\n\t\tupdatePhases();\n\t\treturn engine.onFrame(updatePhases);\n\t}, [engine]);\n\n\treturn null;\n}\n\n/**\n * Pure function version of the state-machine transition rule. Exported so\n * tests can pin the truth table without mounting React.\n *\n * `hasFbo` distinguishes Warm (texture present, can be sampled) from Waking\n * (Visible but no valid texture yet — the compositor must paint before the\n * widget is composited). After eviction (Phase 6) a Cold widget can lose\n * its texture and re-enter as Waking.\n */\nexport function computePhase(\n\tactive: boolean,\n\tvisible: boolean,\n\tculled: boolean,\n\tanimationSignal: boolean,\n\thasFbo: boolean,\n): R3FPhase {\n\tif (!active) return 'Dormant';\n\tif (visible) {\n\t\tif (animationSignal) return 'Hot';\n\t\treturn hasFbo ? 'Warm' : 'Waking';\n\t}\n\tif (culled) return 'Cold';\n\t// No viewport tag yet (e.g. before first cull tick) — treat as Cold so\n\t// we don't spuriously paint before the engine knows where we are.\n\treturn 'Cold';\n}\n","import { useThree } from '@react-three/fiber';\nimport { useEffect } from 'react';\nimport type { LayoutEngine } from '../ecs/engine/index.js';\n\n/**\n * Subscribes to engine frame events and invalidates the R3F canvas only when\n * something has changed that affects R3F output. With `frameloop=\"demand\"` on\n * the parent `<Canvas>`, the canvas only renders in response to these\n * invalidations — idle scenes produce zero R3F frames.\n *\n * Triggers invalidation on: camera change, position change (drag/resize),\n * widgets entering or exiting the visible set.\n */\nexport function EngineInvalidator({ engine }: { engine: LayoutEngine }) {\n\tconst invalidate = useThree((s) => s.invalidate);\n\n\tuseEffect(() => {\n\t\treturn engine.onFrame(() => {\n\t\t\tconst c = engine.getFrameChanges();\n\t\t\tif (\n\t\t\t\tc.cameraChanged ||\n\t\t\t\tc.positionsChanged.length > 0 ||\n\t\t\t\tc.entered.length > 0 ||\n\t\t\t\tc.exited.length > 0\n\t\t\t) {\n\t\t\t\tinvalidate();\n\t\t\t}\n\t\t});\n\t}, [engine, invalidate]);\n\n\treturn null;\n}\n","import { useFrame, useThree } from '@react-three/fiber';\nimport { useRef } from 'react';\nimport { Widget } from '../ecs/components.js';\nimport type { LayoutEngine } from '../ecs/engine/index.js';\nimport { COMPOSITOR_TELEMETRY } from './compositor/Compositor.js';\nimport { R3FRenderState } from './compositor/state.js';\n\n/**\n * Reports one R3F frame sample per animation frame to the engine profiler.\n * Reads `renderer.info` from three.js — draw calls / triangles / memory /\n * programs — which is maintained by R3F's default render loop regardless\n * of whether we opt in. Only samples when the profiler is enabled.\n */\nexport function ProfilerProbe({\n\tengine,\n\twidgetCount,\n}: {\n\tengine: LayoutEngine;\n\twidgetCount: number;\n}) {\n\tconst { gl } = useThree();\n\tconst prevTimeRef = useRef<number | null>(null);\n\tconst prevCallsRef = useRef(0);\n\tconst prevTrianglesRef = useRef(0);\n\tconst prevPointsRef = useRef(0);\n\tconst prevLinesRef = useRef(0);\n\n\t// Priority 2 so the probe samples AFTER the Compositor (priority 1) has\n\t// run its render loop — gives accurate per-frame repaint counts and FBO\n\t// bytes for this exact tick.\n\tuseFrame(() => {\n\t\tconst profiler = engine.profiler;\n\t\tif (!profiler.isEnabled()) {\n\t\t\tprevTimeRef.current = null;\n\t\t\treturn;\n\t\t}\n\t\tconst now = performance.now();\n\t\tconst dtMs = prevTimeRef.current === null ? 0 : now - prevTimeRef.current;\n\t\tprevTimeRef.current = now;\n\n\t\tconst info = gl.info;\n\t\t// renderer.info.render resets per frame when autoReset is true (default).\n\t\t// Read the current values as this frame's counts. But programs/memory\n\t\t// are cumulative gauges. Capture deltas defensively in case a future\n\t\t// change flips autoReset off.\n\t\tconst calls = info.render.calls;\n\t\tconst triangles = info.render.triangles;\n\t\tconst points = info.render.points;\n\t\tconst lines = info.render.lines;\n\t\tconst frameCalls = info.autoReset ? calls : Math.max(0, calls - prevCallsRef.current);\n\t\tconst frameTris = info.autoReset\n\t\t\t? triangles\n\t\t\t: Math.max(0, triangles - prevTrianglesRef.current);\n\t\tconst framePoints = info.autoReset ? points : Math.max(0, points - prevPointsRef.current);\n\t\tconst frameLines = info.autoReset ? lines : Math.max(0, lines - prevLinesRef.current);\n\t\tprevCallsRef.current = calls;\n\t\tprevTrianglesRef.current = triangles;\n\t\tprevPointsRef.current = points;\n\t\tprevLinesRef.current = lines;\n\n\t\tconst phases = { hot: 0, warm: 0, cold: 0, waking: 0, dormant: 0 };\n\t\tconst world = engine.world;\n\t\tfor (const entity of world.query(Widget, R3FRenderState)) {\n\t\t\tconst widget = world.getComponent(entity, Widget);\n\t\t\tif (!widget || widget.surface !== 'webgl') continue;\n\t\t\tconst state = world.getComponent(entity, R3FRenderState);\n\t\t\tif (!state) continue;\n\t\t\tswitch (state.phase) {\n\t\t\t\tcase 'Hot':\n\t\t\t\t\tphases.hot++;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'Warm':\n\t\t\t\t\tphases.warm++;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'Cold':\n\t\t\t\t\tphases.cold++;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'Waking':\n\t\t\t\t\tphases.waking++;\n\t\t\t\t\tbreak;\n\t\t\t\tcase 'Dormant':\n\t\t\t\t\tphases.dormant++;\n\t\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\n\t\tprofiler.recordR3FFrame({\n\t\t\tdtMs,\n\t\t\tdrawCalls: frameCalls,\n\t\t\ttriangles: frameTris,\n\t\t\tpoints: framePoints,\n\t\t\tlines: frameLines,\n\t\t\tprograms: info.programs?.length ?? 0,\n\t\t\tgeometries: info.memory.geometries,\n\t\t\ttextures: info.memory.textures,\n\t\t\tactiveWidgets: widgetCount,\n\t\t\twidgetsRepainted: COMPOSITOR_TELEMETRY.widgetsRepainted,\n\t\t\tfboBytes: COMPOSITOR_TELEMETRY.fboBytes,\n\t\t\tphases,\n\t\t});\n\t}, 2);\n\n\treturn null;\n}\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport { Canvas } from '@react-three/fiber';\nimport type * as React from 'react';\nimport { useMemo, useRef } from 'react';\nimport * as THREE from 'three';\nimport type { LayoutEngine } from '../ecs/engine/index.js';\nimport { useContainerRef } from '../react/context/container-ref-context.js';\nimport { EngineProvider } from '../react/context/engine-context.js';\nimport type { ResolvedWidget } from '../react/context/widget-resolver-context.js';\nimport { createR3FEventManager } from '../react/input/r3f/createR3FEventManager.js';\nimport type { R3FWidgetProps } from '../react/widgets/registry.js';\nimport { Compositor } from './compositor/Compositor.js';\nimport { VirtualWidget } from './compositor/VirtualWidget.js';\nimport { WidgetRegistry } from './compositor/WidgetRegistry.js';\nimport { WidgetStateMachine } from './compositor/WidgetStateMachine.js';\nimport { EngineInvalidator } from './EngineInvalidator.js';\nimport { ProfilerProbe } from './ProfilerProbe.js';\n\ninterface R3FManagerProps {\n\tengine: LayoutEngine;\n\tentities: EntityId[];\n\tresolve: (entityId: EntityId) => ResolvedWidget | null;\n\t/**\n\t * Optional R3F nodes mounted at the Canvas root as siblings of the\n\t * Compositor (so they live in the Canvas's default scene, not inside\n\t * any widget portal). Canonical use: drei's `<Environment>` — the\n\t * Compositor's `sharedEnv` propagation logic checks the root scene\n\t * before iterating widget scenes, so a root-level env stays alive\n\t * across widget navigation (RFC-004 Phase 5 follow-up).\n\t */\n\tr3fRoot?: React.ReactNode;\n\t/**\n\t * Late-bound handle for the R3F event manager. RFC-008's `R3FRouter`\n\t * (constructed in `InfiniteCanvas`) reads this ref to dispatch into\n\t * R3F's mesh handlers from the InputManager pipeline. The factory\n\t * writes the produced manager into `.current` once R3F invokes it.\n\t */\n\t// biome-ignore lint/suspicious/noExplicitAny: R3F manager type isn't\n\t// surfaced cleanly through our types.\n\teventManagerRef?: React.MutableRefObject<any>;\n}\n\n/**\n * Top-level coordinator for the R3F (React Three Fiber) rendering layer.\n *\n * Mounts a single `<Canvas>` and lets the {@link Compositor} drive the\n * render loop — each R3F widget paints into its own `WebGLRenderTarget`\n * via {@link VirtualWidget} and a final composition pass samples those\n * textures into the visible canvas (RFC-002 Phase 4).\n */\nexport function R3FManager({\n\tengine,\n\tentities,\n\tresolve,\n\tr3fRoot,\n\teventManagerRef,\n}: R3FManagerProps) {\n\tconst canvasRef = useRef<HTMLCanvasElement>(null);\n\t// Route R3F's native pointer listeners onto the canvas container, not\n\t// the canvas DOM element. Lets us keep `pointer-events: none` on the\n\t// canvas (so it never occludes DOM widgets stacked underneath at lower\n\t// z-indices) while still feeding R3F every event the bus sees.\n\tconst containerRef = useContainerRef();\n\n\t// R3F needs an initial camera; the Compositor swaps in its own world-space\n\t// ortho camera as the canvas default once mounted.\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// Stable per-canvas registry (RFC-006). The Compositor populates it\n\t// as VirtualWidget instances mount/unmount; the EventRouter reads it\n\t// at intersect time to pick the right scene + camera for raycasting.\n\tconst widgetRegistry = useMemo(() => new WidgetRegistry(), []);\n\n\t// RFC-008 v5 — same dispatch machinery as the default R3F manager\n\t// (bubbling, hover diff, click synthesis, capture, stopPropagation,\n\t// onPointerMissed fan-out), but `connect` / `disconnect` are no-ops:\n\t// the InputManager owns native pointer listeners on the canvas\n\t// container, and `R3FRouter` invokes R3F's mesh dispatch from the\n\t// pipeline instead of letting R3F register its own listeners. The\n\t// `compute` step still targets per-widget scenes for raycasting.\n\tconst eventManager = useMemo(\n\t\t() =>\n\t\t\tcreateR3FEventManager(engine, widgetRegistry, (manager) => {\n\t\t\t\tif (eventManagerRef) eventManagerRef.current = manager;\n\t\t\t}),\n\t\t[engine, widgetRegistry, eventManagerRef],\n\t);\n\n\tconst widgetEntries = useMemo(() => {\n\t\tconst result: {\n\t\t\tentityId: EntityId;\n\t\t\tcomponent: React.ComponentType<R3FWidgetProps>;\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({ entityId: id, component: resolved.component });\n\t\t\t}\n\t\t}\n\t\treturn result;\n\t}, [entities, resolve]);\n\n\treturn (\n\t\t<Canvas\n\t\t\tref={canvasRef}\n\t\t\tcamera={initialCamera}\n\t\t\tframeloop=\"demand\"\n\t\t\tevents={eventManager}\n\t\t\teventSource={\n\t\t\t\t// Cast: R3F's `eventSource` is typed `RefObject<HTMLElement>`\n\t\t\t\t// (non-null current), but our shared container ref is\n\t\t\t\t// `RefObject<HTMLDivElement | null>` because the div mounts\n\t\t\t\t// after first render. R3F reads `.current` lazily on first\n\t\t\t\t// event, by which point the ref is populated.\n\t\t\t\t(containerRef ?? undefined) as React.RefObject<HTMLElement> | undefined\n\t\t\t}\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\t// Canvas itself never receives events — R3F listens on the\n\t\t\t\t// canvas container via `eventSource`, so the canvas can stay\n\t\t\t\t// transparent to pointers without losing widget interactions.\n\t\t\t\t// Keeps DOM widgets at lower z-indices clickable.\n\t\t\t\tpointerEvents: 'none',\n\t\t\t\tzIndex: 1,\n\t\t\t\tdisplay: widgetEntries.length === 0 ? 'none' : 'block',\n\t\t\t}}\n\t\t>\n\t\t\t<EngineProvider value={engine}>\n\t\t\t\t<EngineInvalidator engine={engine} />\n\t\t\t\t<WidgetStateMachine engine={engine} />\n\t\t\t\t<ProfilerProbe engine={engine} widgetCount={widgetEntries.length} />\n\t\t\t\t{r3fRoot}\n\t\t\t\t<Compositor engine={engine} widgetRegistry={widgetRegistry}>\n\t\t\t\t\t{widgetEntries.map(({ entityId, component }) => (\n\t\t\t\t\t\t<VirtualWidget key={entityId} entityId={entityId} component={component} />\n\t\t\t\t\t))}\n\t\t\t\t</Compositor>\n\t\t\t</EngineProvider>\n\t\t</Canvas>\n\t);\n}\n","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\n// Tuned to match Apple Freeform / FigJam:\n// - single perceptually-uniform grid at any zoom (levels hand off cleanly\n// without stacking their intensities)\n// - constant-perceptual-size dots (no CAD-style growth at sparser levels)\n// - soft neutral gray color carrying the lightness, not low-alpha black\nexport const DEFAULT_GRID_CONFIG: GridConfig = {\n\tspacings: [20, 100, 500],\n\tdotColor: [0.75, 0.77, 0.8],\n\tdotAlpha: 1.0,\n\tfadeIn: [8, 16],\n\tfadeOut: [120, 200],\n\tdotRadius: [0.75, 0.75],\n\tlevelWeight: [1.0, 0.0],\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 — optionally grows for sparser levels\n\t\t// (set u_dotRadius.x == u_dotRadius.y for Freeform/FigJam-style\n\t\t// constant-size dots)\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// Per-level weight: base + i * step. Step=0 keeps all levels at equal\n\t\t// intensity; positive step emphasizes coarser levels (CAD feel).\n\t\tfloat weight = u_levelWeight.x + float(i) * u_levelWeight.y;\n\n\t\t// Composite with max, not sum. Additive compositing causes anti-\n\t\t// aliased dot rims to stack at joint intersections (every N-th dot\n\t\t// visibly fatter — a CAD tell). max() guarantees a joint intersection\n\t\t// looks identical to a single-level dot, matching Freeform / FigJam.\n\t\ttotalAlpha = max(totalAlpha, 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\n/**\n * Draws the infinite dot-grid background into a THREE.WebGLRenderer.\n * The renderer is owned by the parent (see {@link WebGLManager}) — this class\n * only contributes a scene, camera, and shader material.\n */\nexport class GridRenderer {\n\tprivate scene: THREE.Scene;\n\tprivate camera: THREE.OrthographicCamera;\n\tprivate material: THREE.ShaderMaterial;\n\tprivate mesh: THREE.Mesh;\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\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(...DEFAULT_GRID_CONFIG.spacings) },\n\t\t\t\tu_dotColor: { value: new THREE.Vector3(...DEFAULT_GRID_CONFIG.dotColor) },\n\t\t\t\tu_dotAlpha: { value: DEFAULT_GRID_CONFIG.dotAlpha },\n\t\t\t\tu_fadeIn: { value: new THREE.Vector2(...DEFAULT_GRID_CONFIG.fadeIn) },\n\t\t\t\tu_fadeOut: { value: new THREE.Vector2(...DEFAULT_GRID_CONFIG.fadeOut) },\n\t\t\t\tu_dotRadius: { value: new THREE.Vector2(...DEFAULT_GRID_CONFIG.dotRadius) },\n\t\t\t\tu_levelWeight: { value: new THREE.Vector2(...DEFAULT_GRID_CONFIG.levelWeight) },\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\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(renderer: THREE.WebGLRenderer, 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\trenderer.render(this.scene, this.camera);\n\t}\n\n\tdispose() {\n\t\tthis.mesh.geometry.dispose();\n\t\tthis.material.dispose();\n\t}\n}\n","import * as THREE from 'three';\nimport { HANDLE_VISUAL_SIZE_PX } from '../../ecs/interaction-constants.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: HANDLE_VISUAL_SIZE_PX,\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}]; // (x, y, width, height) — frame-local Transform2D\nuniform int u_hoverIdx; // -1 = none\nuniform vec4 u_groupBounds; // group bbox (0 if count <= 1)\nuniform int u_hasGroup;\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\t// Proper perimeter arc-length from top-left corner going clockwise\n\t\t\tfloat perim;\n\t\t\tvec2 rel = worldPos - vec2(gb.x, gb.y);\n\t\t\tfloat w = gb.z;\n\t\t\tfloat h = gb.w;\n\n\t\t\t// Determine which edge is nearest and compute cumulative arc length\n\t\t\tfloat dTop = abs(rel.y);\n\t\t\tfloat dRight = abs(rel.x - w);\n\t\t\tfloat dBottom = abs(rel.y - h);\n\t\t\tfloat dLeft = abs(rel.x);\n\n\t\t\tif (dTop <= dBottom && dTop <= dLeft && dTop <= dRight) {\n\t\t\t\tperim = rel.x; // top edge: 0 to w\n\t\t\t} else if (dRight <= dLeft) {\n\t\t\t\tperim = w + rel.y; // right edge: w to w+h\n\t\t\t} else if (dBottom <= dTop) {\n\t\t\t\tperim = w + h + (w - rel.x); // bottom edge: w+h to 2w+h\n\t\t\t} else {\n\t\t\t\tperim = 2.0 * w + h + (h - rel.y); // left edge: 2w+h to 2w+2h\n\t\t\t}\n\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\tif (color.a < 0.01) discard;\n\tgl_FragColor = color;\n}\n`;\n\n// === Renderer class ===\n\n/**\n * Draws selection outlines, 8 resize handles, group-bbox, and the hover\n * outline in a single SDF-based shader pass. Snap guides and equal-\n * spacing indicators are a separate concern — see {@link SnapGuideRenderer}.\n *\n * The `THREE.WebGLRenderer` is owned by the parent ({@link WebGLManager})\n * and passed to each render call so grid + selection can share a single\n * GL context and accumulate `renderer.info` counters for the same tick.\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},\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) {\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\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\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;\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\tlet minY = Number.POSITIVE_INFINITY;\n\t\t\tlet maxX = Number.NEGATIVE_INFINITY;\n\t\t\tlet maxY = 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// 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 * as THREE from 'three';\nimport type { EqualSpacingIndicator, SnapGuide } from '../../ecs/spatial/snap.js';\n\n// === Public config ===\n\nexport interface SnapGuideConfig {\n\t/** Guide line + spacing indicator color [r,g,b] 0-1. */\n\tcolor: [number, number, number];\n\t/** Line width in world pixels (constant across zoom). */\n\tlineWidth: number;\n\t/** Guide alpha (full-canvas alignment lines). */\n\tguideAlpha: number;\n\t/** Equal-spacing indicator alpha. */\n\tspacingAlpha: number;\n}\n\nexport const DEFAULT_SNAP_GUIDE_CONFIG: SnapGuideConfig = {\n\tcolor: [1.0, 0.0, 0.55], // magenta/pink\n\tlineWidth: 0.5,\n\tguideAlpha: 0.8,\n\tspacingAlpha: 0.7,\n};\n\n// Hard caps. Mirror the sized uniform arrays — the shader truncates\n// silently past these, which is the right call for a transient overlay.\nconst MAX_GUIDES = 16;\nconst MAX_SPACINGS = 8;\n\n// === Shader ===\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\nuniform int u_guideCount;\nuniform vec4 u_guides[${MAX_GUIDES}]; // (axis: 0=x/1=y, position, 0, 0)\nuniform int u_spacingCount;\nuniform vec4 u_spacings[${MAX_SPACINGS}]; // (axis, from, to, perpPos)\n\nuniform vec3 u_color;\nuniform float u_lineWidth;\nuniform float u_guideAlpha;\nuniform float u_spacingAlpha;\n\nvoid main() {\n\tif (u_guideCount == 0 && u_spacingCount == 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\tfloat pxToWorld = 1.0 / effectiveZoom;\n\tfloat lineHalf = u_lineWidth * pxToWorld;\n\n\tvec4 color = vec4(0.0);\n\n\t// --- Snap guide lines (full-canvas axis alignment) ---\n\tfor (int i = 0; i < ${MAX_GUIDES}; i++) {\n\t\tif (i >= u_guideCount) break;\n\t\tvec4 g = u_guides[i];\n\t\tfloat dist = g.x < 0.5 ? abs(worldPos.x - g.y) : abs(worldPos.y - g.y);\n\t\tfloat a = 1.0 - smoothstep(lineHalf - pxToWorld * 0.3, lineHalf + pxToWorld * 0.3, dist);\n\t\tcolor = max(color, vec4(u_color, a * u_guideAlpha));\n\t}\n\n\t// --- Equal spacing indicators (segment + end-bars) ---\n\tfor (int i = 0; i < ${MAX_SPACINGS}; i++) {\n\t\tif (i >= u_spacingCount) break;\n\t\tvec4 s = u_spacings[i];\n\t\tfloat segAlpha = 0.0;\n\t\tif (s.x < 0.5) {\n\t\t\t// Horizontal gap segment\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\tsegAlpha = (1.0 - smoothstep(lineHalf, lineHalf + pxToWorld, yDist)) * xInRange;\n\t\t\tfloat barHeight = 4.0 * pxToWorld;\n\t\t\tfloat barFrom = (1.0 - smoothstep(lineHalf, lineHalf + pxToWorld, abs(worldPos.x - s.y)))\n\t\t\t\t* (1.0 - smoothstep(barHeight, barHeight + pxToWorld, abs(worldPos.y - s.w)));\n\t\t\tfloat barTo = (1.0 - smoothstep(lineHalf, lineHalf + pxToWorld, abs(worldPos.x - s.z)))\n\t\t\t\t* (1.0 - smoothstep(barHeight, barHeight + pxToWorld, abs(worldPos.y - s.w)));\n\t\t\tsegAlpha = max(segAlpha, max(barFrom, barTo));\n\t\t} else {\n\t\t\t// Vertical gap segment\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(lineHalf, lineHalf + pxToWorld, xDist)) * yInRange;\n\t\t\tfloat barWidth = 4.0 * pxToWorld;\n\t\t\tfloat barFrom = (1.0 - smoothstep(lineHalf, lineHalf + 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 barTo = (1.0 - smoothstep(lineHalf, lineHalf + 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(barFrom, barTo));\n\t\t}\n\t\tcolor = max(color, vec4(u_color, segAlpha * u_spacingAlpha));\n\t}\n\n\tif (color.a < 0.01) discard;\n\tgl_FragColor = color;\n}\n`;\n\n/**\n * Renders alignment guide lines and equal-spacing indicators in a single\n * SDF pass. Independent of {@link SelectionRenderer} — guides participate\n * regardless of whether anything is selected, and the dragged entity may\n * have its own chrome that opts out of the engine-drawn selection frame.\n */\nexport class SnapGuideRenderer {\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\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_guideCount: { value: 0 },\n\t\t\t\tu_guides: {\n\t\t\t\t\tvalue: Array.from({ length: MAX_GUIDES }, () => new THREE.Vector4(0, 0, 0, 0)),\n\t\t\t\t},\n\t\t\t\tu_spacingCount: { value: 0 },\n\t\t\t\tu_spacings: {\n\t\t\t\t\tvalue: Array.from({ length: MAX_SPACINGS }, () => new THREE.Vector4(0, 0, 0, 0)),\n\t\t\t\t},\n\t\t\t\tu_color: { value: new THREE.Vector3(...DEFAULT_SNAP_GUIDE_CONFIG.color) },\n\t\t\t\tu_lineWidth: { value: DEFAULT_SNAP_GUIDE_CONFIG.lineWidth },\n\t\t\t\tu_guideAlpha: { value: DEFAULT_SNAP_GUIDE_CONFIG.guideAlpha },\n\t\t\t\tu_spacingAlpha: { value: DEFAULT_SNAP_GUIDE_CONFIG.spacingAlpha },\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<SnapGuideConfig>) {\n\t\tconst u = this.material.uniforms;\n\t\tif (config.color) u.u_color.value.set(...config.color);\n\t\tif (config.lineWidth !== undefined) u.u_lineWidth.value = config.lineWidth;\n\t\tif (config.guideAlpha !== undefined) u.u_guideAlpha.value = config.guideAlpha;\n\t\tif (config.spacingAlpha !== undefined) u.u_spacingAlpha.value = config.spacingAlpha;\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\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\tconst gCount = Math.min(guides.length, MAX_GUIDES);\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\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 >= MAX_SPACINGS) 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\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 * as THREE from 'three';\nimport type { EqualSpacingIndicator, SnapGuide } from '../ecs/spatial/snap.js';\nimport type { Profiler } from '../profiler/Profiler.js';\nimport type { GridConfig } from './renderers/GridRenderer.js';\nimport { GridRenderer } from './renderers/GridRenderer.js';\nimport type { SelectionBounds, SelectionConfig } from './renderers/SelectionRenderer.js';\nimport { SelectionRenderer } from './renderers/SelectionRenderer.js';\nimport type { SnapGuideConfig } from './renderers/SnapGuideRenderer.js';\nimport { SnapGuideRenderer } from './renderers/SnapGuideRenderer.js';\n\n/** Construction options for {@link WebGLManager}. */\nexport interface WebGLManagerOptions {\n\t/** Grid rendering config. Pass `false` to disable the grid entirely. */\n\tgrid?: Partial<GridConfig> | false;\n\t/** Selection overlay config (outline color, handle size, etc.). */\n\tselection?: Partial<SelectionConfig>;\n\t/** Snap guide overlay config (color, line width, alpha). */\n\tsnapGuides?: Partial<SnapGuideConfig>;\n}\n\n/** Per-frame input passed to {@link WebGLManager.render}. */\nexport interface WebGLFrameInput {\n\tcamera: { x: number; y: number; zoom: number };\n\t/** Selection outlines + 8 resize handles + hover. */\n\tselection: {\n\t\tbounds: SelectionBounds[];\n\t\thovered: SelectionBounds | null;\n\t};\n\t/**\n\t * Alignment guides + equal-spacing indicators. Independent of\n\t * `selection` — guides display whenever the engine is computing\n\t * snap math, regardless of whether anything is selected. Set\n\t * `visible: false` to skip the snap-guide pass entirely (the\n\t * guides are still computed by the engine; only the render is\n\t * suppressed).\n\t */\n\tsnap: {\n\t\tguides: SnapGuide[];\n\t\tspacings: EqualSpacingIndicator[];\n\t\tvisible: boolean;\n\t};\n\t/** Optional — if supplied, the manager records per-pass timings + GL info. */\n\tprofiler?: Profiler;\n\t/** Forwarded into the profiler sample (count of DOM slot transform writes this tick). */\n\tdomPositionsUpdated?: number;\n}\n\n/**\n * Top-level coordinator for the library's vanilla-WebGL layer.\n *\n * Owns a single `THREE.WebGLRenderer` and drives the built-in renderers\n * (dot grid, selection overlay, snap guides) through it. This replaces\n * the previous pattern where `GridRenderer` owned the renderer and\n * `SelectionRenderer` piggy-backed on it via an implicit side-effect —\n * a manager makes the sharing explicit and gives {@link InfiniteCanvas}\n * a single surface to talk to instead of three.\n */\nexport class WebGLManager {\n\tprivate renderer: THREE.WebGLRenderer;\n\tprivate grid: GridRenderer | null = null;\n\tprivate selection: SelectionRenderer;\n\tprivate snapGuides: SnapGuideRenderer;\n\n\tconstructor(canvas: HTMLCanvasElement, opts: WebGLManagerOptions = {}) {\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\t\t// Accumulate `renderer.info.render.calls/triangles` across multiple\n\t\t// render() calls per tick. The manager resets once per frame in\n\t\t// `render()` so grid + selection + snap counts cover one tick cleanly.\n\t\tthis.renderer.info.autoReset = false;\n\n\t\tif (opts.grid !== false) {\n\t\t\tthis.grid = new GridRenderer();\n\t\t\tif (opts.grid) this.grid.setConfig(opts.grid);\n\t\t}\n\n\t\tthis.selection = new SelectionRenderer();\n\t\tif (opts.selection) this.selection.setConfig(opts.selection);\n\n\t\tthis.snapGuides = new SnapGuideRenderer();\n\t\tif (opts.snapGuides) this.snapGuides.setConfig(opts.snapGuides);\n\t}\n\n\t/** Resize the drawing buffer. Call on mount and ResizeObserver events. */\n\tsetSize(width: number, height: number, dpr = 1): void {\n\t\tthis.renderer.setSize(width, height, false);\n\t\tthis.renderer.setPixelRatio(dpr);\n\t\tconst resolution = new THREE.Vector2(width * dpr, height * dpr);\n\t\tthis.grid?.setSize(width, height, dpr);\n\t\tthis.selection.setSize(resolution, dpr);\n\t\tthis.snapGuides.setSize(resolution, dpr);\n\t}\n\n\t/** Update grid visuals (colors, spacings, fade ranges, etc.). */\n\tsetGridConfig(config: Partial<GridConfig>): void {\n\t\tthis.grid?.setConfig(config);\n\t}\n\n\t/** Update selection overlay visuals (outline color, handle size, etc.). */\n\tsetSelectionConfig(config: Partial<SelectionConfig>): void {\n\t\tthis.selection.setConfig(config);\n\t}\n\n\t/** Update snap-guide visuals (color, line width, alpha). */\n\tsetSnapGuideConfig(config: Partial<SnapGuideConfig>): void {\n\t\tthis.snapGuides.setConfig(config);\n\t}\n\n\t/** Render one frame: grid → selection → snap guides, in stacking order. */\n\trender(input: WebGLFrameInput): void {\n\t\tconst { camera, selection, snap, profiler } = input;\n\n\t\t// Reset once per frame so grid + selection + snap counts accumulate\n\t\t// into the same tick. Safe to call even when the profiler is\n\t\t// disabled — it's cheap and keeps info.render deterministic.\n\t\tthis.renderer.info.reset();\n\n\t\tif (this.grid) {\n\t\t\tprofiler?.beginWebGL('grid');\n\t\t\tthis.grid.render(this.renderer, camera.x, camera.y, camera.zoom);\n\t\t\tprofiler?.endWebGL('grid');\n\t\t}\n\n\t\tprofiler?.beginWebGL('selection');\n\t\tthis.selection.render(\n\t\t\tthis.renderer,\n\t\t\tcamera.x,\n\t\t\tcamera.y,\n\t\t\tcamera.zoom,\n\t\t\tselection.bounds,\n\t\t\tselection.hovered,\n\t\t);\n\t\tprofiler?.endWebGL('selection');\n\n\t\t// Call the snap-guide pass on every visible frame, even when both\n\t\t// arrays are empty. The renderer always uploads the current counts\n\t\t// (zero or non-zero) so its uniforms stay in sync, and the shader's\n\t\t// own `discard` makes the empty case effectively free. This avoids\n\t\t// any class of stale-uniform bugs when guides come and go.\n\t\tif (snap.visible) {\n\t\t\tprofiler?.beginWebGL('snap-guides');\n\t\t\tthis.snapGuides.render(\n\t\t\t\tthis.renderer,\n\t\t\t\tcamera.x,\n\t\t\t\tcamera.y,\n\t\t\t\tcamera.zoom,\n\t\t\t\tsnap.guides,\n\t\t\t\tsnap.spacings,\n\t\t\t);\n\t\t\tprofiler?.endWebGL('snap-guides');\n\t\t}\n\n\t\tif (profiler?.isEnabled()) {\n\t\t\tconst info = this.renderer.info;\n\t\t\tprofiler.recordWebGLStats({\n\t\t\t\tdrawCalls: info.render.calls,\n\t\t\t\ttriangles: info.render.triangles,\n\t\t\t\tselectionFrames: selection.bounds.length + (selection.hovered ? 1 : 0),\n\t\t\t\tsnapGuides: snap.guides.length,\n\t\t\t\tspacingIndicators: snap.spacings.length,\n\t\t\t\tdomPositionsUpdated: input.domPositionsUpdated ?? 0,\n\t\t\t});\n\t\t}\n\t}\n\n\t/** Release GL resources — call on unmount. */\n\tdispose(): void {\n\t\tthis.grid?.dispose();\n\t\tthis.selection.dispose();\n\t\tthis.snapGuides.dispose();\n\t\tthis.renderer.dispose();\n\t}\n\n\t/** Escape hatch if an advanced consumer needs the underlying three renderer. */\n\tgetWebGLRenderer(): THREE.WebGLRenderer {\n\t\treturn this.renderer;\n\t}\n}\n","import type * as React from 'react';\n\n/** Visual variants for the card chrome — matches the iOS card aesthetics. */\nexport interface CardChromeProps {\n\t/**\n\t * True while the user is actively dragging this card. Triggers a CSS\n\t * transition: scale up slightly and deepen the drop shadow.\n\t */\n\tlifted?: boolean;\n\t/** Border radius in CSS pixels. Default 21.67 — the iOS card spec. */\n\tradius?: number;\n\t/**\n\t * Background color (any valid CSS background value). Skip / pass\n\t * `'transparent'` if the children paint their own background.\n\t */\n\tbackground?: string;\n\t/** Extra class name appended to the chrome div. */\n\tclassName?: string;\n\t/** Style overrides — merged after the chrome's defaults. */\n\tstyle?: React.CSSProperties;\n\t/**\n\t * Drag-over highlight state. When `overlapCandidate` is true, a single\n\t * radial gradient glow renders at `(hotX, hotY)` in [0,1] local coords\n\t * with intensity scaled by `hotStrength`. `overlapTarget` swaps between\n\t * the candidate / target alpha + falloff CSS vars (configured via\n\t * `OverlapGlowConfig`). All five props default to a no-op.\n\t */\n\toverlapCandidate?: boolean;\n\toverlapTarget?: boolean;\n\thotX?: number;\n\thotY?: number;\n\thotStrength?: number;\n\t/** Children render *inside* the chrome (clipped to its rounded shape). */\n\tchildren?: React.ReactNode;\n}\n\n/**\n * iOS-style card chrome — rounded background, hairline ring, soft drop\n * shadow, and a smooth lift (scale + stronger shadow) when `lifted` is\n * true. A single soft radial-gradient glow renders at the hot point\n * during overlap (no rim, no bloom, no backdrop-filter). Glow color,\n * alpha, and falloff are tunable via the `--ic-glow-*` CSS vars set by\n * `<InfiniteCanvas overlapGlow={…}>`.\n *\n * Pure presentational component with no ECS or compositor coupling. Used\n * by both the DOM `createCardWidget` (wrapping inner content) and the R3F\n * `createGeometryCardWidget` (rendered via a DOM slot beneath the WebGL\n * canvas, with the 3D content floating on top).\n */\nexport function CardChrome({\n\tlifted = false,\n\tradius = 21.67,\n\tbackground,\n\tclassName,\n\tstyle,\n\toverlapCandidate = false,\n\toverlapTarget = false,\n\thotX = 0.5,\n\thotY = 0.5,\n\thotStrength = 0,\n\tchildren,\n}: CardChromeProps) {\n\tconst baseShadow = lifted\n\t\t? '0 30px 60px rgba(0,0,0,0.22), 0 0 0 1px rgba(0,0,0,0.06)'\n\t\t: '0 20px 40px rgba(0,0,0,0.15), 0 0 0 1px rgba(0,0,0,0.05)';\n\n\tconst baseStyle: React.CSSProperties = {\n\t\tposition: 'relative',\n\t\twidth: '100%',\n\t\theight: '100%',\n\t\tborderRadius: `${radius}px`,\n\t\toverflow: 'hidden',\n\t\tbackground,\n\t\tboxShadow: baseShadow,\n\t\ttransform: lifted ? 'scale(1.05)' : 'scale(1)',\n\t\ttransformOrigin: 'center center',\n\t\ttransition: 'transform 180ms cubic-bezier(0.2, 0.9, 0.3, 1.2), box-shadow 220ms ease',\n\t\twillChange: lifted ? 'transform, box-shadow' : undefined,\n\t\t...style,\n\t};\n\n\tconst hotXPct = hotX * 100;\n\tconst hotYPct = hotY * 100;\n\n\t// Inner glow — `inset` box-shadow with a hot-point-derived offset,\n\t// matching the reference (card-light.html). The reference's JS\n\t// computes `light-x = -deltaX * 20 * sensitivity`, clamps to ±20,\n\t// then uses `calc(var(--light-x) * 0.4)` as the inset offset (so a\n\t// max of ±8px). We use the same range, derived from hotX/hotY:\n\t// offset = -(hotX - 0.5) * 16 → ranges -8..+8 px\n\t// The negative sign puts the bright spot on the SAME side as the\n\t// hot point (positive offsetX → inset shadow on left).\n\tconst offsetX = -(hotX - 0.5) * 16;\n\tconst offsetY = -(hotY - 0.5) * 16;\n\tconst sizeVar = overlapTarget ? 'var(--ic-glow-size-t, 80px)' : 'var(--ic-glow-size-c, 60px)';\n\tconst alphaVar = overlapTarget ? 'var(--ic-glow-alpha-t, 0.45)' : 'var(--ic-glow-alpha-c, 0.25)';\n\tconst glowStyle: React.CSSProperties = {\n\t\tposition: 'absolute',\n\t\tinset: 0,\n\t\tpointerEvents: 'none',\n\t\tborderRadius: 'inherit',\n\t\tboxShadow: `inset ${offsetX}px ${offsetY}px ${sizeVar} rgba(var(--ic-glow-color, 128, 128, 128), ${alphaVar})`,\n\t\topacity: overlapCandidate ? hotStrength : 0,\n\t\ttransition: 'opacity 220ms ease, box-shadow 220ms ease',\n\t};\n\n\t// Rim — radial-gradient circle anchored at the hot point, faded to\n\t// transparent at 40%. Same as reference (card-light.html :115-119).\n\t// Color, width, alpha, and circle radius are all CSS-var driven so\n\t// the playground settings panel can retune them live.\n\tconst rimAlphaVar = overlapTarget ? 'var(--ic-rim-alpha-t, 0.85)' : 'var(--ic-rim-alpha-c, 0.55)';\n\tconst rimColor = `rgba(var(--ic-rim-color, 128, 128, 128), ${rimAlphaVar})`;\n\tconst rimStyle: React.CSSProperties = {\n\t\tposition: 'absolute',\n\t\tinset: 0,\n\t\tpointerEvents: 'none',\n\t\tborderRadius: 'inherit',\n\t\tpadding: 'var(--ic-rim-width, 1.5px)',\n\t\tbackground: `radial-gradient(var(--ic-rim-radius, 600px) circle at ${hotXPct}% ${hotYPct}%, ${rimColor}, transparent 40%)`,\n\t\tWebkitMask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',\n\t\tWebkitMaskComposite: 'xor',\n\t\tmask: 'linear-gradient(#fff 0 0) content-box, linear-gradient(#fff 0 0)',\n\t\tmaskComposite: 'exclude',\n\t\topacity: overlapCandidate ? hotStrength : 0,\n\t\ttransition: 'opacity 220ms ease, background 220ms ease',\n\t};\n\n\treturn (\n\t\t<div\n\t\t\tclassName={className}\n\t\t\tstyle={baseStyle}\n\t\t\tdata-overlap-candidate={overlapCandidate || undefined}\n\t\t\tdata-overlap-target={overlapTarget || undefined}\n\t\t>\n\t\t\t{children}\n\t\t\t<div aria-hidden style={glowStyle} />\n\t\t\t<div aria-hidden style={rimStyle} />\n\t\t</div>\n\t);\n}\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport { memo, useEffect, useRef } from 'react';\nimport {\n\tCard,\n\tCardOverlapHotPoint,\n\tDragging,\n\tOverlapCandidate,\n\tOverlapTarget,\n\tTransform2D,\n} from '../../ecs/components.js';\nimport { useLayoutEngine } from '../context/engine-context.js';\nimport { useComponent, useTag } from '../hooks/ecs.js';\nimport { CardChrome } from '../widgets/CardChrome.js';\n\ninterface SelectionOverlaySlotProps {\n\tentityId: EntityId;\n\tslotRef: (entityId: EntityId, el: HTMLDivElement | null) => void;\n}\n\n/**\n * DOM chrome overlay for R3F widgets — renders the selection frame /\n * card decoration and positions itself at the widget's world AABB.\n *\n * Decoration only. Pointer events bypass this wrapper\n * (`pointer-events: none`) so they reach the R3F canvas underneath,\n * where the `EventRouter` raycasts the widget's local scene (RFC-006).\n * Engine semantics — drag, select, resize, double-click — are dispatched\n * by the canvas-level `PointerEventBus` after the widget's R3F handlers\n * have had a chance to call `event.stopPropagation()`.\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 dragging = useTag(entityId, Dragging);\n\t// Card presence + data drives DOM CardChrome. Subscribed reactively\n\t// so adding/removing/editing Card at runtime updates the slot\n\t// without a refresh.\n\tconst card = useComponent(entityId, Card);\n\t// RFC-004 § Phase 3 — overlap state drives the two-layer glow.\n\tconst overlapCandidate = useTag(entityId, OverlapCandidate);\n\tconst overlapTarget = useTag(entityId, OverlapTarget);\n\tconst hot = useComponent(entityId, CardOverlapHotPoint);\n\n\tuseEffect(() => {\n\t\tslotRef(entityId, wrapperRef.current);\n\t\treturn () => slotRef(entityId, null);\n\t}, [entityId, slotRef]);\n\n\tconst t = engine.get(entityId, Transform2D);\n\tconst initialStyle: React.CSSProperties = t\n\t\t? {\n\t\t\t\ttransform: `translate(${t.x}px, ${t.y}px)`,\n\t\t\t\twidth: `${t.width}px`,\n\t\t\t\theight: `${t.height}px`,\n\t\t\t}\n\t\t: {};\n\n\treturn (\n\t\t<div\n\t\t\tref={wrapperRef}\n\t\t\tclassName=\"pointer-events-none absolute left-0 top-0 origin-top-left will-change-transform\"\n\t\t\tdata-widget-slot=\"\"\n\t\t\tstyle={initialStyle}\n\t\t>\n\t\t\t{card && (\n\t\t\t\t<CardChrome\n\t\t\t\t\tlifted={dragging}\n\t\t\t\t\tbackground={card.background}\n\t\t\t\t\toverlapCandidate={overlapCandidate}\n\t\t\t\t\toverlapTarget={overlapTarget}\n\t\t\t\t\thotX={hot?.x}\n\t\t\t\t\thotY={hot?.y}\n\t\t\t\t\thotStrength={hot?.strength}\n\t\t\t\t/>\n\t\t\t)}\n\t\t</div>\n\t);\n});\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport { memo, useEffect, useRef } from 'react';\nimport { Transform2D, Widget } from '../../ecs/components.js';\nimport { useLayoutEngine } from '../context/engine-context.js';\nimport { useWidgetResolver } from '../context/widget-resolver-context.js';\nimport { useComponent } from '../hooks/ecs.js';\n\ninterface WidgetSlotProps {\n\tentityId: EntityId;\n\tslotRef: (entityId: EntityId, el: HTMLDivElement | null) => void;\n}\n\n/**\n * Wrapper for a DOM widget — owns the slot's positioning, registers its\n * ref with the rAF batcher, and renders the user's widget component.\n *\n * Pointer routing lives in the canvas-level `PointerEventBus` (RFC-006).\n * The slot does not call the engine; user widget React handlers run on\n * the natural DOM event path and bubble to the bus, which decides\n * whether to invoke engine semantics. Authors call `e.stopPropagation()`\n * from inside their widget to opt out of engine drag/select.\n */\nexport const WidgetSlot = memo(function WidgetSlot({ entityId, slotRef }: WidgetSlotProps) {\n\tconst wrapperRef = useRef<HTMLDivElement>(null);\n\tconst engine = useLayoutEngine();\n\tconst resolve = useWidgetResolver();\n\n\tconst widgetComp = useComponent(entityId, Widget);\n\n\t// DOM slot — only renders DOM widgets. R3F widgets go through R3FManager.\n\tconst resolved = resolve?.(entityId, widgetComp?.type ?? '');\n\tconst WidgetComponent = resolved && resolved.surface === 'dom' ? resolved.component : null;\n\n\tuseEffect(() => {\n\t\tslotRef(entityId, wrapperRef.current);\n\t\treturn () => slotRef(entityId, null);\n\t}, [entityId, slotRef]);\n\n\t// Read Transform2D 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 t = engine.get(entityId, Transform2D);\n\tconst initialStyle: React.CSSProperties = t\n\t\t? {\n\t\t\t\ttransform: `translate(${t.x}px, ${t.y}px)`,\n\t\t\t\twidth: `${t.width}px`,\n\t\t\t\theight: `${t.height}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>\n\t\t\t{content}\n\t\t</div>\n\t);\n});\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport { useThree } from '@react-three/fiber';\nimport { useCallback, useEffect, useMemo, useRef } from 'react';\nimport type { BufferGeometry, Material, Texture } from 'three';\nimport { useLayoutEngine } from '../../react/context/engine-context.js';\nimport { useComponent } from '../../react/hooks/ecs.js';\nimport { useCompositor } from './CompositorContext.js';\nimport { R3FAnimationSignal, type R3FPhase, R3FRenderState } from './state.js';\n\n/**\n * Marks the current R3F widget as actively animating. While `active` is true,\n * the state machine places the widget in `Hot`; when false, it returns to\n * `Warm` on the next frame.\n *\n * Widgets should call this whenever they want per-frame ticking (e.g. during\n * a spring settle, hover lerp, or an external animation). Without this\n * signal, `useFrame` bodies may still fire when the canvas re-renders for\n * other reasons — check `useWidgetPhase() === 'Hot'` to early-exit work\n * that's only meaningful during the animation.\n */\nexport function useWidgetAnimation(entityId: EntityId, active: boolean): void {\n\tconst engine = useLayoutEngine();\n\tuseEffect(() => {\n\t\tif (active) {\n\t\t\tengine.world.addTag(entityId, R3FAnimationSignal);\n\t\t\t// Force an engine tick so the state machine sees the new tag and\n\t\t\t// transitions the widget to Hot — without this, the page would\n\t\t\t// stay still until *something else* (drag, pan, etc.) ticks the\n\t\t\t// engine and lets the state machine pick up the signal.\n\t\t\tengine.markDirty();\n\t\t\treturn () => {\n\t\t\t\tengine.world.removeTag(entityId, R3FAnimationSignal);\n\t\t\t\tengine.markDirty();\n\t\t\t};\n\t\t}\n\t\t// Ensure clean-up if the active → inactive transition happens mid-effect.\n\t\tengine.world.removeTag(entityId, R3FAnimationSignal);\n\t\tengine.markDirty();\n\t\treturn undefined;\n\t}, [engine, entityId, active]);\n}\n\n/**\n * Returns the current compositor phase for the widget. Re-renders when the\n * phase changes.\n */\nexport function useWidgetPhase(entityId: EntityId): R3FPhase | null {\n\tconst state = useComponent(entityId, R3FRenderState);\n\treturn state?.phase ?? null;\n}\n\n/**\n * Returns a function that schedules a one-shot repaint of the widget. Use\n * when widget content changes outside of React's render cycle (e.g., a\n * subscription to an external store, an imperative WebSocket message).\n *\n * Internally bumps `paintGeneration` so the compositor's dirty check picks\n * the widget up on the next frame, and invalidates the canvas so that\n * frame is actually scheduled.\n */\nexport function useWidgetInvalidate(entityId: EntityId): () => void {\n\tconst engine = useLayoutEngine();\n\tconst invalidate = useThree((s) => s.invalidate);\n\treturn useCallback(() => {\n\t\tconst current = engine.world.getComponent(entityId, R3FRenderState);\n\t\tif (!current) {\n\t\t\t// State not tracked yet — just kick the canvas; the state machine\n\t\t\t// will create the component on its next pass.\n\t\t\tinvalidate();\n\t\t\treturn;\n\t\t}\n\t\tengine.world.setComponent(entityId, R3FRenderState, {\n\t\t\t...current,\n\t\t\tpaintGeneration: current.paintGeneration + 1,\n\t\t});\n\t\tinvalidate();\n\t}, [engine, entityId, invalidate]);\n}\n\n/**\n * Acquires a shared geometry from the Compositor's `ResourceRegistry`,\n * keyed by `cacheKey`. The factory runs only on first acquisition; later\n * callers with the same key get the same instance. Released automatically\n * on unmount; the registry disposes when the last holder releases.\n *\n * Use for geometries that are expensive to build and frequently identical\n * across widget instances — e.g. preset card backs.\n */\nexport function useSharedGeometry<T extends BufferGeometry>(cacheKey: string, factory: () => T): T {\n\tconst { registry } = useCompositor();\n\t// Stash factory in a ref so re-renders with a fresh closure don't churn\n\t// the useMemo deps. The registry only invokes factory on first acquire\n\t// for `cacheKey`, so the latest closure is what runs when it matters.\n\tconst factoryRef = useRef(factory);\n\tfactoryRef.current = factory;\n\tconst geometry = useMemo(\n\t\t() => registry.acquireGeometry(cacheKey, factoryRef.current),\n\t\t[registry, cacheKey],\n\t);\n\tuseEffect(() => {\n\t\treturn () => registry.releaseGeometry(cacheKey);\n\t}, [registry, cacheKey]);\n\treturn geometry;\n}\n\n/** Same contract as {@link useSharedGeometry}, for materials. */\nexport function useSharedMaterial<T extends Material>(cacheKey: string, factory: () => T): T {\n\tconst { registry } = useCompositor();\n\tconst factoryRef = useRef(factory);\n\tfactoryRef.current = factory;\n\tconst material = useMemo(\n\t\t() => registry.acquireMaterial(cacheKey, factoryRef.current),\n\t\t[registry, cacheKey],\n\t);\n\tuseEffect(() => {\n\t\treturn () => registry.releaseMaterial(cacheKey);\n\t}, [registry, cacheKey]);\n\treturn material;\n}\n\n/** Same contract as {@link useSharedGeometry}, for textures. */\nexport function useSharedTexture<T extends Texture>(cacheKey: string, factory: () => T): T {\n\tconst { registry } = useCompositor();\n\tconst factoryRef = useRef(factory);\n\tfactoryRef.current = factory;\n\tconst texture = useMemo(\n\t\t() => registry.acquireTexture(cacheKey, factoryRef.current),\n\t\t[registry, cacheKey],\n\t);\n\tuseEffect(() => {\n\t\treturn () => registry.releaseTexture(cacheKey);\n\t}, [registry, cacheKey]);\n\treturn texture;\n}\n"],"x_google_ignoreList":[1,2],"mappings":"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"}