@jamesyong42/infinite-canvas 1.0.0 → 1.2.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.
- package/README.md +170 -9
- package/dist/SelectionRenderer-CR2PBQwx.d.cts +105 -0
- package/dist/SelectionRenderer-CR2PBQwx.d.cts.map +1 -0
- package/dist/SelectionRenderer-DlsBstAq.d.mts +105 -0
- package/dist/SelectionRenderer-DlsBstAq.d.mts.map +1 -0
- package/dist/WebGLWidgetLayer-BBMuwzHq.cjs +3560 -0
- package/dist/WebGLWidgetLayer-BBMuwzHq.cjs.map +1 -0
- package/dist/WebGLWidgetLayer-C3p1tnpm.mjs +3375 -0
- package/dist/WebGLWidgetLayer-C3p1tnpm.mjs.map +1 -0
- package/dist/advanced.cjs +110 -165
- package/dist/advanced.cjs.map +1 -1
- package/dist/advanced.d.cts +58 -40
- package/dist/advanced.d.cts.map +1 -0
- package/dist/advanced.d.mts +99 -0
- package/dist/advanced.d.mts.map +1 -0
- package/dist/advanced.mjs +105 -0
- package/dist/advanced.mjs.map +1 -0
- package/dist/devtools.cjs +654 -0
- package/dist/devtools.cjs.map +1 -0
- package/dist/devtools.d.cts +23 -0
- package/dist/devtools.d.cts.map +1 -0
- package/dist/devtools.d.mts +23 -0
- package/dist/devtools.d.mts.map +1 -0
- package/dist/devtools.mjs +652 -0
- package/dist/devtools.mjs.map +1 -0
- package/dist/engine-BfbvWXSk.d.mts +982 -0
- package/dist/engine-BfbvWXSk.d.mts.map +1 -0
- package/dist/engine-CCjuFMC-.d.cts +982 -0
- package/dist/engine-CCjuFMC-.d.cts.map +1 -0
- package/dist/hooks-BwY7rRHg.mjs +425 -0
- package/dist/hooks-BwY7rRHg.mjs.map +1 -0
- package/dist/hooks-DHShH86C.cjs +707 -0
- package/dist/hooks-DHShH86C.cjs.map +1 -0
- package/dist/index.cjs +909 -803
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +199 -67
- package/dist/index.d.cts.map +1 -0
- package/dist/index.d.mts +258 -0
- package/dist/index.d.mts.map +1 -0
- package/dist/index.mjs +855 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +47 -15
- package/dist/SelectionRenderer-CeWSNZT8.d.cts +0 -891
- package/dist/SelectionRenderer-CeWSNZT8.d.ts +0 -891
- package/dist/advanced.d.ts +0 -81
- package/dist/advanced.js +0 -124
- package/dist/advanced.js.map +0 -1
- package/dist/chunk-VSHXWTJH.cjs +0 -3228
- package/dist/chunk-VSHXWTJH.cjs.map +0 -1
- package/dist/chunk-Z6JQQOWL.js +0 -3142
- package/dist/chunk-Z6JQQOWL.js.map +0 -1
- package/dist/index.d.ts +0 -126
- package/dist/index.js +0 -602
- package/dist/index.js.map +0 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["DEFAULT_CARD_PRESET_SIZES"],"sources":["../src/react/widget-hooks.ts","../src/react/card.tsx","../src/react/geometry-card.tsx","../src/react/WidgetProvider.tsx","../src/react/InfiniteCanvas.tsx"],"sourcesContent":["import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport { useCallback } from 'react';\nimport { Children, Selected, WidgetBreakpoint, WidgetData } from '../components.js';\nimport type { Breakpoint } from '../resources.js';\nimport { useLayoutEngine } from './context.js';\nimport { useComponent, useTag } from './hooks.js';\n\n/**\n * Returns the custom data attached to a widget entity.\n * Use the generic parameter for type safety: `useWidgetData<MyData>(entityId)`. Re-renders when data changes.\n */\nexport function useWidgetData<T = Record<string, unknown>>(entityId: EntityId): T {\n\tconst comp = useComponent(entityId, WidgetData);\n\treturn (comp?.data ?? {}) as T;\n}\n\n/**\n * Returns the current responsive breakpoint for a widget based on its screen-space size.\n * Re-renders when the breakpoint changes.\n */\nexport function useBreakpoint(entityId: EntityId): Breakpoint {\n\tconst comp = useComponent(entityId, WidgetBreakpoint);\n\treturn comp?.current ?? 'normal';\n}\n\n/**\n * Returns child entity IDs of a container entity.\n * Re-renders when children are added or removed.\n */\nexport function useChildren(entityId: EntityId): EntityId[] {\n\tconst comp = useComponent(entityId, Children);\n\treturn comp?.ids ?? [];\n}\n\n/**\n * Returns whether the entity is currently selected.\n * Re-renders when the entity's selection state changes.\n */\nexport function useIsSelected(entityId: EntityId): boolean {\n\treturn useTag(entityId, Selected);\n}\n\n/**\n * Returns a function to update the widget's custom data.\n * Merges the patch into existing data via shallow spread.\n */\nexport function useUpdateWidget(entityId: EntityId): (patch: Record<string, unknown>) => void {\n\tconst engine = useLayoutEngine();\n\treturn useCallback(\n\t\t(patch: Record<string, unknown>) => {\n\t\t\tconst existing = engine.get(entityId, WidgetData);\n\t\t\tif (existing) {\n\t\t\t\tengine.set(entityId, WidgetData, {\n\t\t\t\t\tdata: { ...existing.data, ...patch },\n\t\t\t\t});\n\t\t\t}\n\t\t},\n\t\t[engine, entityId],\n\t);\n}\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport type * as React from 'react';\nimport type { Archetype } from '../archetype.js';\nimport type { CardPreset } from '../components.js';\nimport { Card, Dragging } from '../components.js';\nimport type { StandardSchemaV1 } from '../schema.js';\nimport { useTag } from './hooks.js';\nimport type { DomWidget, DomWidgetProps } from './registry.js';\nimport { useWidgetData } from './widget-hooks.js';\n\n/**\n * Built-in preset sizes, matching `CardPresetsResource` defaults.\n * Used by `createCardWidget` to set `defaultSize` at widget-registration\n * time (before the engine is constructed).\n */\nconst DEFAULT_CARD_PRESET_SIZES: Record<CardPreset, { width: number; height: number }> = {\n\tsmall: { width: 155, height: 155 },\n\tmedium: { width: 329, height: 155 },\n\tlarge: { width: 329, height: 345 },\n\txl: { width: 329, height: 535 },\n};\n\n/** Props accepted by `<CardFrame>`. */\nexport interface CardFrameProps {\n\tentityId: EntityId;\n\tchildren?: React.ReactNode;\n\tclassName?: string;\n\t/** Merged into the frame div's style (wins over defaults). */\n\tstyle?: React.CSSProperties;\n}\n\n/**\n * Visual chrome for an iOS-style card: rounded corners, hairline ring,\n * soft drop shadow, and a subtle lift (scale + stronger shadow) while\n * the entity carries the `Dragging` tag.\n *\n * Uses CSS transitions — no animation library dependency.\n */\nexport function CardFrame({ entityId, children, className, style }: CardFrameProps) {\n\tconst dragging = useTag(entityId, Dragging);\n\n\tconst baseStyle: React.CSSProperties = {\n\t\twidth: '100%',\n\t\theight: '100%',\n\t\tborderRadius: '21.67px',\n\t\toverflow: 'hidden',\n\t\tboxShadow: dragging\n\t\t\t? '0 30px 60px rgba(0,0,0,0.22), 0 0 0 1px rgba(0,0,0,0.06)'\n\t\t\t: '0 20px 40px rgba(0,0,0,0.15), 0 0 0 1px rgba(0,0,0,0.05)',\n\t\ttransform: dragging ? 'scale(1.05)' : 'scale(1)',\n\t\ttransformOrigin: 'center center',\n\t\ttransition:\n\t\t\t'transform 180ms cubic-bezier(0.2, 0.9, 0.3, 1.2), box-shadow 180ms cubic-bezier(0.2, 0.9, 0.3, 1.2)',\n\t\twillChange: dragging ? 'transform, box-shadow' : undefined,\n\t\t...style,\n\t};\n\n\treturn (\n\t\t<div className={className} style={baseStyle}>\n\t\t\t{children}\n\t\t</div>\n\t);\n}\n\n/** Options passed to `createCardWidget`. */\nexport interface CreateCardWidgetOptions<T> {\n\t/** Unique widget type id. Doubles as the archetype id. */\n\ttype: string;\n\t/** Which iOS preset the card sits at. Fixed for the widget's lifetime (change via `engine.set(id, Card, { preset })`). */\n\tsize: CardPreset;\n\t/** Standard Schema v1-compatible validator for the widget's data. */\n\t// biome-ignore lint/suspicious/noExplicitAny: schema Input is intentionally permissive\n\tschema: StandardSchemaV1<any, T>;\n\t/** Default data for new instances; merged with user-supplied data at spawn. */\n\tdefaultData: T;\n\t/** The card's rendered content. Receives entityId + typed data. */\n\trender: React.ComponentType<{ entityId: EntityId; data: T }>;\n}\n\n/**\n * Returns a paired widget + archetype for an iOS-style card. Register both\n * with `createLayoutEngine({ widgets: [card.widget], archetypes: [card.archetype] })`\n * (or via `engine.registerWidget` / `engine.registerArchetype`) and spawn with\n * `engine.spawn('your-card-type', { at, data })`.\n *\n * The produced widget is non-resizable (Selectable + Draggable only), wrapped\n * in `<CardFrame>`, and spawns with a `Card` component so `cardSystem` enforces\n * the preset size each tick.\n */\nexport function createCardWidget<T>(opts: CreateCardWidgetOptions<T>): {\n\twidget: DomWidget<T>;\n\tarchetype: Archetype;\n} {\n\tconst defaultSize = DEFAULT_CARD_PRESET_SIZES[opts.size];\n\tconst Render = opts.render;\n\n\tconst Component: React.ComponentType<DomWidgetProps> = ({ entityId }) => {\n\t\tconst data = useWidgetData<T>(entityId);\n\t\treturn (\n\t\t\t<CardFrame entityId={entityId}>\n\t\t\t\t<Render entityId={entityId} data={data} />\n\t\t\t</CardFrame>\n\t\t);\n\t};\n\n\tconst widget: DomWidget<T> = {\n\t\ttype: opts.type,\n\t\tschema: opts.schema,\n\t\tdefaultData: opts.defaultData,\n\t\tdefaultSize,\n\t\tcomponent: Component,\n\t};\n\n\tconst archetype: Archetype = {\n\t\tid: opts.type,\n\t\twidget: opts.type,\n\t\tcomponents: [[Card, { preset: opts.size }]],\n\t\tinteractive: {\n\t\t\tselectable: true,\n\t\t\tdraggable: true,\n\t\t\tresizable: false,\n\t\t\t// Cards render their own chrome via CardFrame — opt out of the\n\t\t\t// engine-drawn selection/hover outline.\n\t\t\tselectionFrame: false,\n\t\t},\n\t\tdefaultSize,\n\t};\n\n\treturn { widget, archetype };\n}\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport { useFrame } from '@react-three/fiber';\nimport type * as React from 'react';\nimport { useMemo, useRef } from 'react';\nimport type { Group } from 'three';\nimport { ExtrudeGeometry, Shape } from 'three';\nimport type { Archetype } from '../archetype.js';\nimport type { CardPreset } from '../components.js';\nimport { Card, Dragging } from '../components.js';\nimport type { StandardSchemaV1 } from '../schema.js';\nimport { useTag } from './hooks.js';\nimport type { R3FWidget, R3FWidgetProps } from './registry.js';\nimport { useWidgetData } from './widget-hooks.js';\n\n/** Must match {@link CardPresetsResource} defaults. */\nconst DEFAULT_CARD_PRESET_SIZES: Record<CardPreset, { width: number; height: number }> = {\n\tsmall: { width: 155, height: 155 },\n\tmedium: { width: 329, height: 155 },\n\tlarge: { width: 329, height: 345 },\n\txl: { width: 329, height: 535 },\n};\n\n/**\n * Pure-three rounded-rect extrude geometry — avoids a drei dependency.\n * Rounded corners match the DOM CardFrame radius (21.67 px).\n */\nfunction makeRoundedCardGeometry(\n\twidth: number,\n\theight: number,\n\tradius: number,\n\tdepth: number,\n): ExtrudeGeometry {\n\tconst shape = new Shape();\n\tconst r = Math.min(radius, Math.min(width, height) / 2);\n\tconst x = -width / 2;\n\tconst y = -height / 2;\n\tshape.moveTo(x, y + r);\n\tshape.lineTo(x, y + height - r);\n\tshape.quadraticCurveTo(x, y + height, x + r, y + height);\n\tshape.lineTo(x + width - r, y + height);\n\tshape.quadraticCurveTo(x + width, y + height, x + width, y + height - r);\n\tshape.lineTo(x + width, y + r);\n\tshape.quadraticCurveTo(x + width, y, x + width - r, y);\n\tshape.lineTo(x + r, y);\n\tshape.quadraticCurveTo(x, y, x, y + r);\n\n\treturn new ExtrudeGeometry(shape, {\n\t\tdepth,\n\t\tbevelEnabled: true,\n\t\tbevelSegments: 3,\n\t\tbevelSize: 0.6,\n\t\tbevelThickness: 0.6,\n\t});\n}\n\ninterface CardBackProps {\n\twidth: number;\n\theight: number;\n\tcolor: string;\n\troughness: number;\n\tmetalness: number;\n}\n\nfunction CardBack({ width, height, color, roughness, metalness }: CardBackProps) {\n\tconst geometry = useMemo(() => makeRoundedCardGeometry(width, height, 21.67, 3), [width, height]);\n\treturn (\n\t\t<mesh geometry={geometry} position={[0, 0, -6]} receiveShadow>\n\t\t\t<meshStandardMaterial color={color} roughness={roughness} metalness={metalness} />\n\t\t</mesh>\n\t);\n}\n\n/** Background options for a geometry card widget. */\nexport type GeometryCardBackground =\n\t| 'card'\n\t| 'transparent'\n\t| {\n\t\t\t/** Hex color for the card back (e.g. '#1C1C1E' dark, '#F2F2F7' light). */\n\t\t\tcolor: string;\n\t\t\t/** PBR roughness. Default 0.55. */\n\t\t\troughness?: number;\n\t\t\t/** PBR metalness. Default 0. */\n\t\t\tmetalness?: number;\n\t };\n\n/** Props passed to the user's geometry component. */\nexport interface GeometryCardRenderProps<T> {\n\tentityId: EntityId;\n\tdata: T;\n\t/** Widget width in world units. */\n\twidth: number;\n\t/** Widget height in world units. */\n\theight: number;\n}\n\n/** Options for `createGeometryCardWidget`. */\nexport interface CreateGeometryCardWidgetOptions<T> {\n\t/** Unique widget type id. Doubles as the archetype id. */\n\ttype: string;\n\t/** iOS card preset size. */\n\tsize: CardPreset;\n\t/** Standard Schema v1-compatible validator for the widget's data. */\n\t// biome-ignore lint/suspicious/noExplicitAny: schema Input is intentionally permissive\n\tschema: StandardSchemaV1<any, T>;\n\t/** Default data for new instances. */\n\tdefaultData: T;\n\t/**\n\t * `'card'` (default) renders a dark iOS-style card back behind the geometry.\n\t * `'transparent'` skips the card so the geometry floats over the canvas.\n\t * Object form customises the back's color and PBR parameters.\n\t */\n\tbackground?: GeometryCardBackground;\n\t/** The 3D content rendered in local space (origin at centre). */\n\tgeometry: React.ComponentType<GeometryCardRenderProps<T>>;\n}\n\n/**\n * Returns a paired R3F widget + archetype for a card-shaped 3D widget.\n * Behaves like {@link createCardWidget} — fixed preset size, non-resizable,\n * no engine-drawn selection frame, and lifts on drag (scale + z) — but\n * renders a three.js scene instead of DOM content.\n *\n * Lighting: this helper adds no lights. Declare your own in the `geometry`\n * component (typically a local `pointLight` scoped with `distance`).\n */\nexport function createGeometryCardWidget<T>(opts: CreateGeometryCardWidgetOptions<T>): {\n\twidget: R3FWidget<T>;\n\tarchetype: Archetype;\n} {\n\tconst defaultSize = DEFAULT_CARD_PRESET_SIZES[opts.size];\n\tconst Render = opts.geometry;\n\tconst backgroundConfig = opts.background ?? 'card';\n\n\tconst resolvedBack =\n\t\tbackgroundConfig === 'transparent'\n\t\t\t? null\n\t\t\t: backgroundConfig === 'card'\n\t\t\t\t? { color: '#1C1C1E', roughness: 0.55, metalness: 0 }\n\t\t\t\t: {\n\t\t\t\t\t\tcolor: backgroundConfig.color,\n\t\t\t\t\t\troughness: backgroundConfig.roughness ?? 0.55,\n\t\t\t\t\t\tmetalness: backgroundConfig.metalness ?? 0,\n\t\t\t\t\t};\n\n\tconst Component: React.ComponentType<R3FWidgetProps> = ({ entityId, width, height }) => {\n\t\tconst data = useWidgetData<T>(entityId);\n\t\tconst dragging = useTag(entityId, Dragging);\n\t\tconst groupRef = useRef<Group>(null);\n\n\t\t// Spring-lerp the group scale + z on drag for the iOS lift feel.\n\t\tuseFrame(() => {\n\t\t\tconst g = groupRef.current;\n\t\t\tif (!g) return;\n\t\t\tconst targetScale = dragging ? 1.05 : 1;\n\t\t\tconst targetZ = dragging ? 8 : 0;\n\t\t\tconst s = g.scale.x;\n\t\t\tg.scale.setScalar(s + (targetScale - s) * 0.2);\n\t\t\tg.position.z += (targetZ - g.position.z) * 0.2;\n\t\t});\n\n\t\treturn (\n\t\t\t<group ref={groupRef}>\n\t\t\t\t{resolvedBack && (\n\t\t\t\t\t<CardBack\n\t\t\t\t\t\twidth={width}\n\t\t\t\t\t\theight={height}\n\t\t\t\t\t\tcolor={resolvedBack.color}\n\t\t\t\t\t\troughness={resolvedBack.roughness}\n\t\t\t\t\t\tmetalness={resolvedBack.metalness}\n\t\t\t\t\t/>\n\t\t\t\t)}\n\t\t\t\t<Render entityId={entityId} data={data} width={width} height={height} />\n\t\t\t</group>\n\t\t);\n\t};\n\n\tconst widget: R3FWidget<T> = {\n\t\ttype: opts.type,\n\t\tsurface: 'webgl',\n\t\tschema: opts.schema,\n\t\tdefaultData: opts.defaultData,\n\t\tdefaultSize,\n\t\tcomponent: Component,\n\t};\n\n\tconst archetype: Archetype = {\n\t\tid: opts.type,\n\t\twidget: opts.type,\n\t\tcomponents: [[Card, { preset: opts.size }]],\n\t\tinteractive: {\n\t\t\tselectable: true,\n\t\t\tdraggable: true,\n\t\t\tresizable: false,\n\t\t\tselectionFrame: false,\n\t\t},\n\t\tdefaultSize,\n\t};\n\n\treturn { widget, archetype };\n}\n","import type { ReactNode } from 'react';\nimport { useCallback } from 'react';\nimport type { LayoutEngine } from '../engine.js';\nimport type { ResolvedWidget } from './context.js';\nimport { WidgetResolverProvider } from './context.js';\nimport { isR3FWidget } from './registry.js';\n\ninterface WidgetProviderProps {\n\tengine: LayoutEngine;\n\tchildren?: ReactNode;\n}\n\n/**\n * Bridges the engine's widget registry to React context so WidgetSlot /\n * WebGLWidgetLayer can resolve components by type.\n */\nexport function WidgetProvider({ engine, children }: WidgetProviderProps) {\n\tconst resolver = useCallback(\n\t\t(_entityId: number, widgetType: string): ResolvedWidget | null => {\n\t\t\tconst def = engine.getWidget(widgetType);\n\t\t\tif (!def) return null;\n\t\t\tif (isR3FWidget(def)) {\n\t\t\t\treturn { surface: 'webgl', component: def.component };\n\t\t\t}\n\t\t\treturn { surface: 'dom', component: def.component };\n\t\t},\n\t\t[engine],\n\t);\n\n\treturn <WidgetResolverProvider value={resolver}>{children}</WidgetResolverProvider>;\n}\n","import type { EntityId } from '@jamesyong42/reactive-ecs';\nimport React, {\n\tuseCallback,\n\tuseEffect,\n\tuseImperativeHandle,\n\tuseLayoutEffect,\n\tuseMemo,\n\tuseRef,\n\tuseState,\n} from 'react';\nimport { Vector2 } from 'three';\nimport { SelectionFrame, Widget, WorldBounds } from '../components.js';\nimport type { LayoutEngine } from '../engine.js';\nimport { DEAD_ZONE_TOUCH_PX } from '../interaction-constants.js';\nimport { CursorResource, NavigationStackResource } from '../resources.js';\nimport { ContainerRefProvider, EngineProvider, useWidgetResolver } from './context.js';\nimport { SelectionOverlaySlot } from './SelectionOverlaySlot.js';\nimport { WidgetProvider } from './WidgetProvider.js';\nimport { WidgetSlot } from './WidgetSlot.js';\nimport type { GridConfig } from './webgl/GridRenderer.js';\nimport { GridRenderer } from './webgl/GridRenderer.js';\nimport type { SelectionBounds, SelectionConfig } from './webgl/SelectionRenderer.js';\nimport { SelectionRenderer } from './webgl/SelectionRenderer.js';\nimport { WebGLWidgetLayer } from './webgl/WebGLWidgetLayer.js';\n\n/** Imperative handle exposed via `ref` on InfiniteCanvas for programmatic control. */\nexport interface InfiniteCanvasHandle {\n\t/** Moves the camera to the specified world coordinates. */\n\tpanTo(worldX: number, worldY: number): void;\n\t/** Sets the zoom level directly. */\n\tzoomTo(zoom: number): void;\n\t/** Adjusts camera to fit all entities in the viewport. */\n\tzoomToFit(padding?: number): void;\n\t/** Undoes the last command or command group. */\n\tundo(): void;\n\t/** Redoes the last undone command. */\n\tredo(): void;\n\t/** Returns the underlying LayoutEngine instance. */\n\tgetEngine(): LayoutEngine;\n}\n\n/** Props for the InfiniteCanvas component. */\ninterface InfiniteCanvasProps {\n\t/**\n\t * The LayoutEngine instance powering this canvas. Create with `createLayoutEngine()`.\n\t * Widgets and archetypes must be registered on the engine — via config or\n\t * `engine.registerWidget` / `engine.registerArchetype`.\n\t */\n\tengine: LayoutEngine;\n\t/** Grid configuration. Pass `false` to disable the grid entirely. */\n\tgrid?: Partial<GridConfig> | false;\n\t/** Selection highlight style configuration. */\n\tselection?: Partial<SelectionConfig>;\n\t/** Called when the set of selected entities changes. */\n\tonSelectionChange?: (entityIds: EntityId[]) => void;\n\t/** Called when the camera (pan/zoom) changes. */\n\tonCameraChange?: (camera: { x: number; y: number; zoom: number }) => void;\n\t/** Called when navigation depth changes (entering/exiting containers). */\n\tonNavigationChange?: (depth: number, containerId: EntityId | null) => void;\n\t/** CSS class name applied to the root container div. */\n\tclassName?: string;\n\t/** Inline styles applied to the root container div. */\n\tstyle?: React.CSSProperties;\n\t/** Overlay children (toolbars, panels) rendered on top of the canvas. */\n\tchildren?: React.ReactNode;\n}\n\nexport const InfiniteCanvas = React.forwardRef<InfiniteCanvasHandle, InfiniteCanvasProps>(\n\tfunction InfiniteCanvas(\n\t\t{\n\t\t\tengine,\n\t\t\tgrid,\n\t\t\tselection,\n\t\t\tonSelectionChange,\n\t\t\tonCameraChange,\n\t\t\tonNavigationChange,\n\t\t\tclassName,\n\t\t\tstyle,\n\t\t\tchildren,\n\t\t},\n\t\tref,\n\t) {\n\t\tconst containerRef = useRef<HTMLDivElement>(null);\n\n\t\t// Keep latest callback refs to avoid stale closures in the rAF loop\n\t\tconst onSelectionChangeRef = useRef(onSelectionChange);\n\t\tconst onCameraChangeRef = useRef(onCameraChange);\n\t\tconst onNavigationChangeRef = useRef(onNavigationChange);\n\t\tuseEffect(() => {\n\t\t\tonSelectionChangeRef.current = onSelectionChange;\n\t\t}, [onSelectionChange]);\n\t\tuseEffect(() => {\n\t\t\tonCameraChangeRef.current = onCameraChange;\n\t\t}, [onCameraChange]);\n\t\tuseEffect(() => {\n\t\t\tonNavigationChangeRef.current = onNavigationChange;\n\t\t}, [onNavigationChange]);\n\n\t\t// Imperative handle\n\t\tuseImperativeHandle(\n\t\t\tref,\n\t\t\t() => ({\n\t\t\t\tpanTo: (x, y) => {\n\t\t\t\t\tengine.panTo(x, y);\n\t\t\t\t\tengine.markDirty();\n\t\t\t\t},\n\t\t\t\tzoomTo: (zoom) => {\n\t\t\t\t\tengine.zoomTo(zoom);\n\t\t\t\t\tengine.markDirty();\n\t\t\t\t},\n\t\t\t\tzoomToFit: (padding) => {\n\t\t\t\t\tengine.zoomToFit(undefined, padding);\n\t\t\t\t\tengine.markDirty();\n\t\t\t\t},\n\t\t\t\tundo: () => {\n\t\t\t\t\tengine.undo();\n\t\t\t\t\tengine.markDirty();\n\t\t\t\t},\n\t\t\t\tredo: () => {\n\t\t\t\t\tengine.redo();\n\t\t\t\t\tengine.markDirty();\n\t\t\t\t},\n\t\t\t\tgetEngine: () => engine,\n\t\t\t}),\n\t\t\t[engine],\n\t\t);\n\n\t\tconst webglCanvasRef = useRef<HTMLCanvasElement>(null);\n\t\tconst gridRendererRef = useRef<GridRenderer | null>(null);\n\t\tconst selectionRendererRef = useRef<SelectionRenderer | null>(null);\n\t\tconst cameraLayerRef = useRef<HTMLDivElement>(null);\n\t\tconst slotRefs = useRef(new Map<EntityId, HTMLDivElement>());\n\t\tconst [visibleEntities, setVisibleEntities] = useState<EntityId[]>([]);\n\n\t\t// Register slot ref for batch updater\n\t\tconst registerSlotRef = useCallback((entityId: EntityId, el: HTMLDivElement | null) => {\n\t\t\tif (el) {\n\t\t\t\tslotRefs.current.set(entityId, el);\n\t\t\t} else {\n\t\t\t\tslotRefs.current.delete(entityId);\n\t\t\t}\n\t\t}, []);\n\n\t\t// Initialize GridRenderer + set viewport size on mount/resize\n\t\tuseLayoutEffect(() => {\n\t\t\tconst container = containerRef.current;\n\t\t\tconst canvas = webglCanvasRef.current;\n\t\t\tif (!container || !canvas) return;\n\n\t\t\tconst gridEnabled = grid !== false;\n\t\t\tlet gridInst: GridRenderer | null = null;\n\t\t\tif (gridEnabled) {\n\t\t\t\tgridInst = new GridRenderer(canvas);\n\t\t\t\tgridRendererRef.current = gridInst;\n\t\t\t}\n\n\t\t\t// SelectionRenderer shares the WebGLRenderer from GridRenderer\n\t\t\tconst selInst = new SelectionRenderer();\n\t\t\tselectionRendererRef.current = selInst;\n\n\t\t\tconst updateSize = () => {\n\t\t\t\tconst rect = container.getBoundingClientRect();\n\t\t\t\tconst dpr = window.devicePixelRatio;\n\t\t\t\tengine.setViewport(rect.width, rect.height, dpr);\n\t\t\t\tcanvas.style.width = `${rect.width}px`;\n\t\t\t\tcanvas.style.height = `${rect.height}px`;\n\t\t\t\tif (gridInst) {\n\t\t\t\t\tgridInst.setSize(rect.width, rect.height, dpr);\n\t\t\t\t}\n\t\t\t\tselInst.setSize(new Vector2(rect.width * dpr, rect.height * dpr), dpr);\n\t\t\t};\n\n\t\t\tupdateSize();\n\t\t\tconst observer = new ResizeObserver(updateSize);\n\t\t\tobserver.observe(container);\n\t\t\treturn () => {\n\t\t\t\tobserver.disconnect();\n\t\t\t\tif (gridInst) {\n\t\t\t\t\tgridInst.dispose();\n\t\t\t\t\tgridRendererRef.current = null;\n\t\t\t\t}\n\t\t\t\tselInst.dispose();\n\t\t\t\tselectionRendererRef.current = null;\n\t\t\t};\n\t\t}, [engine, grid]);\n\n\t\t// Apply grid + selection config on every render\n\t\tuseEffect(() => {\n\t\t\tconst gridR = gridRendererRef.current;\n\t\t\tif (gridR && grid !== false) {\n\t\t\t\tconst isDark = document.documentElement.classList.contains('dark');\n\t\t\t\tgridR.setConfig({\n\t\t\t\t\tdotColor: isDark ? [1, 1, 1] : [0, 0, 0],\n\t\t\t\t\tdotAlpha: isDark ? 0.12 : 0.18,\n\t\t\t\t\t...grid,\n\t\t\t\t});\n\t\t\t}\n\t\t\tconst selR = selectionRendererRef.current;\n\t\t\tif (selR && selection) {\n\t\t\t\tselR.setConfig(selection);\n\t\t\t}\n\t\t\tengine.markDirty();\n\t\t}, [engine, grid, selection]);\n\n\t\t// Wheel handler — pan/zoom (gesture channel, always active)\n\t\tuseEffect(() => {\n\t\t\tconst container = containerRef.current;\n\t\t\tif (!container) return;\n\n\t\t\tconst onWheel = (e: WheelEvent) => {\n\t\t\t\te.preventDefault();\n\t\t\t\tif (e.ctrlKey || e.metaKey) {\n\t\t\t\t\t// Pinch zoom or ctrl+scroll\n\t\t\t\t\tconst rect = container.getBoundingClientRect();\n\t\t\t\t\tengine.zoomAtPoint(e.clientX - rect.left, e.clientY - rect.top, -e.deltaY * 0.01);\n\t\t\t\t} else {\n\t\t\t\t\t// Two-finger scroll pan\n\t\t\t\t\tengine.panBy(-e.deltaX, -e.deltaY);\n\t\t\t\t}\n\t\t\t};\n\n\t\t\tcontainer.addEventListener('wheel', onWheel, { passive: false });\n\t\t\treturn () => container.removeEventListener('wheel', onWheel);\n\t\t}, [engine]);\n\n\t\t// Touch gesture handler — iOS Freeform-style interactions\n\t\t// 1 finger on background → pan; 1 finger on entity → select/drag;\n\t\t// 2 fingers → pinch-to-zoom + pan; double-tap → zoom step / enter container\n\t\tuseEffect(() => {\n\t\t\tconst container = containerRef.current;\n\t\t\tif (!container) return;\n\n\t\t\ttype TouchGesture =\n\t\t\t\t| { type: 'idle' }\n\t\t\t\t| { type: 'pending-pan'; x: number; y: number; time: number }\n\t\t\t\t| { type: 'panning'; lastX: number; lastY: number }\n\t\t\t\t| { type: 'pending-entity'; x: number; y: number; time: number }\n\t\t\t\t| { type: 'entity-dragging' }\n\t\t\t\t| { type: 'pinching'; lastDist: number; lastCx: number; lastCy: number };\n\n\t\t\tlet gesture: TouchGesture = { type: 'idle' };\n\t\t\tlet lastTapTime = 0;\n\t\t\tlet lastTapX = 0;\n\t\t\tlet lastTapY = 0;\n\t\t\tconst DOUBLE_TAP_MS = 300;\n\t\t\tconst DOUBLE_TAP_DIST = 30;\n\n\t\t\tfunction isOnWidget(target: EventTarget | null): boolean {\n\t\t\t\tlet el = target as HTMLElement | null;\n\t\t\t\twhile (el && el !== container) {\n\t\t\t\t\tif (el.hasAttribute('data-widget-slot')) return true;\n\t\t\t\t\tel = el.parentElement;\n\t\t\t\t}\n\t\t\t\treturn false;\n\t\t\t}\n\n\t\t\tfunction isInteractive(target: EventTarget | null): boolean {\n\t\t\t\tconst el = target as HTMLElement | null;\n\t\t\t\tif (!el) return false;\n\t\t\t\tconst tag = el.tagName;\n\t\t\t\treturn (\n\t\t\t\t\ttag === 'INPUT' ||\n\t\t\t\t\ttag === 'TEXTAREA' ||\n\t\t\t\t\ttag === 'BUTTON' ||\n\t\t\t\t\ttag === 'SELECT' ||\n\t\t\t\t\tel.isContentEditable ||\n\t\t\t\t\tel.closest('button') !== null\n\t\t\t\t);\n\t\t\t}\n\n\t\t\tfunction getRect() {\n\t\t\t\treturn container?.getBoundingClientRect() ?? new DOMRect();\n\t\t\t}\n\n\t\t\tfunction touchDist(t1: Touch, t2: Touch) {\n\t\t\t\tconst dx = t1.clientX - t2.clientX;\n\t\t\t\tconst dy = t1.clientY - t2.clientY;\n\t\t\t\treturn Math.sqrt(dx * dx + dy * dy);\n\t\t\t}\n\n\t\t\tfunction touchCenter(t1: Touch, t2: Touch, rect: DOMRect) {\n\t\t\t\treturn {\n\t\t\t\t\tx: (t1.clientX + t2.clientX) / 2 - rect.left,\n\t\t\t\t\ty: (t1.clientY + t2.clientY) / 2 - rect.top,\n\t\t\t\t};\n\t\t\t}\n\n\t\t\tfunction cancelEngineGesture() {\n\t\t\t\tif (gesture.type === 'pending-entity' || gesture.type === 'entity-dragging') {\n\t\t\t\t\tengine.handlePointerUp();\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst noMods = { shift: false, ctrl: false, alt: false, meta: false };\n\n\t\t\tfunction onTouchStart(e: TouchEvent) {\n\t\t\t\tconst rect = getRect();\n\t\t\t\tconst touches = e.touches;\n\n\t\t\t\t// --- 2+ fingers → pinch (override everything) ---\n\t\t\t\tif (touches.length >= 2) {\n\t\t\t\t\te.preventDefault();\n\t\t\t\t\tcancelEngineGesture();\n\t\t\t\t\tconst dist = touchDist(touches[0], touches[1]);\n\t\t\t\t\tconst center = touchCenter(touches[0], touches[1], rect);\n\t\t\t\t\tgesture = { type: 'pinching', lastDist: dist, lastCx: center.x, lastCy: center.y };\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// --- 1 finger ---\n\t\t\t\tconst touch = touches[0];\n\t\t\t\tconst x = touch.clientX - rect.left;\n\t\t\t\tconst y = touch.clientY - rect.top;\n\n\t\t\t\t// Let interactive elements (buttons, inputs) handle their own touch\n\t\t\t\tif (isInteractive(e.target)) return;\n\n\t\t\t\te.preventDefault();\n\n\t\t\t\t// Double-tap detection\n\t\t\t\tconst now = Date.now();\n\t\t\t\tif (\n\t\t\t\t\tnow - lastTapTime < DOUBLE_TAP_MS &&\n\t\t\t\t\tMath.abs(x - lastTapX) < DOUBLE_TAP_DIST &&\n\t\t\t\t\tMath.abs(y - lastTapY) < DOUBLE_TAP_DIST\n\t\t\t\t) {\n\t\t\t\t\tlastTapTime = 0;\n\t\t\t\t\t// Hit test to check for entity\n\t\t\t\t\tconst directive = engine.handlePointerDown(x, y, 0, noMods);\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (directive.action === 'passthrough-track-drag') {\n\t\t\t\t\t\t\t// Double-tap on entity → enter container\n\t\t\t\t\t\t\tconst selected = engine.getSelectedEntities();\n\t\t\t\t\t\t\tif (selected.length === 1) {\n\t\t\t\t\t\t\t\tengine.enterContainer(selected[0]);\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\t// Double-tap on empty → zoom step\n\t\t\t\t\t\t\tconst camera = engine.getCamera();\n\t\t\t\t\t\t\tconst target = camera.zoom < 0.9 ? 1 : camera.zoom < 1.8 ? 2 : 1;\n\t\t\t\t\t\t\tengine.zoomAtPoint(x, y, (target - camera.zoom) / camera.zoom);\n\t\t\t\t\t\t}\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tengine.handlePointerUp();\n\t\t\t\t\t\tengine.markDirty();\n\t\t\t\t\t}\n\t\t\t\t\tgesture = { type: 'idle' };\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (isOnWidget(e.target)) {\n\t\t\t\t\t// Touch on entity → delegate to engine\n\t\t\t\t\tengine.handlePointerDown(x, y, 0, noMods);\n\t\t\t\t\tgesture = { type: 'pending-entity', x, y, time: now };\n\t\t\t\t} else {\n\t\t\t\t\t// Touch on empty space → prepare to pan (don't tell engine yet)\n\t\t\t\t\tgesture = { type: 'pending-pan', x, y, time: now };\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onTouchMove(e: TouchEvent) {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst rect = getRect();\n\t\t\t\tconst touches = e.touches;\n\n\t\t\t\t// --- Pinch ---\n\t\t\t\tif (gesture.type === 'pinching' && touches.length >= 2) {\n\t\t\t\t\tconst dist = touchDist(touches[0], touches[1]);\n\t\t\t\t\tconst center = touchCenter(touches[0], touches[1], rect);\n\t\t\t\t\tconst scale = dist / gesture.lastDist;\n\t\t\t\t\tengine.zoomAtPoint(center.x, center.y, scale - 1);\n\t\t\t\t\tengine.panBy(center.x - gesture.lastCx, center.y - gesture.lastCy);\n\t\t\t\t\tgesture.lastDist = dist;\n\t\t\t\t\tgesture.lastCx = center.x;\n\t\t\t\t\tgesture.lastCy = center.y;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Transition to pinch if second finger added\n\t\t\t\tif (touches.length >= 2) {\n\t\t\t\t\tcancelEngineGesture();\n\t\t\t\t\tconst dist = touchDist(touches[0], touches[1]);\n\t\t\t\t\tconst center = touchCenter(touches[0], touches[1], rect);\n\t\t\t\t\tgesture = { type: 'pinching', lastDist: dist, lastCx: center.x, lastCy: center.y };\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (touches.length < 1) return;\n\t\t\t\tconst touch = touches[0];\n\t\t\t\tconst x = touch.clientX - rect.left;\n\t\t\t\tconst y = touch.clientY - rect.top;\n\n\t\t\t\t// Pending pan → check dead zone\n\t\t\t\tif (gesture.type === 'pending-pan') {\n\t\t\t\t\tif (\n\t\t\t\t\t\tMath.abs(x - gesture.x) > DEAD_ZONE_TOUCH_PX ||\n\t\t\t\t\t\tMath.abs(y - gesture.y) > DEAD_ZONE_TOUCH_PX\n\t\t\t\t\t) {\n\t\t\t\t\t\tgesture = { type: 'panning', lastX: x, lastY: y };\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Active panning\n\t\t\t\tif (gesture.type === 'panning') {\n\t\t\t\t\tengine.panBy(x - gesture.lastX, y - gesture.lastY);\n\t\t\t\t\tgesture.lastX = x;\n\t\t\t\t\tgesture.lastY = y;\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\t// Entity drag → delegate to engine\n\t\t\t\tif (gesture.type === 'pending-entity' || gesture.type === 'entity-dragging') {\n\t\t\t\t\tengine.handlePointerMove(x, y, noMods);\n\t\t\t\t\tif (gesture.type === 'pending-entity') {\n\t\t\t\t\t\tif (\n\t\t\t\t\t\t\tMath.abs(x - gesture.x) > DEAD_ZONE_TOUCH_PX ||\n\t\t\t\t\t\t\tMath.abs(y - gesture.y) > DEAD_ZONE_TOUCH_PX\n\t\t\t\t\t\t) {\n\t\t\t\t\t\t\tgesture = { type: 'entity-dragging' };\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tfunction onTouchEnd(e: TouchEvent) {\n\t\t\t\te.preventDefault();\n\t\t\t\tconst remaining = e.touches.length;\n\t\t\t\tconst rect = getRect();\n\n\t\t\t\t// Pinch → transition based on remaining fingers\n\t\t\t\tif (gesture.type === 'pinching') {\n\t\t\t\t\tif (remaining === 1) {\n\t\t\t\t\t\tconst t = e.touches[0];\n\t\t\t\t\t\tgesture = {\n\t\t\t\t\t\t\ttype: 'panning',\n\t\t\t\t\t\t\tlastX: t.clientX - rect.left,\n\t\t\t\t\t\t\tlastY: t.clientY - rect.top,\n\t\t\t\t\t\t};\n\t\t\t\t\t} else if (remaining === 0) {\n\t\t\t\t\t\tgesture = { type: 'idle' };\n\t\t\t\t\t}\n\t\t\t\t\treturn;\n\t\t\t\t}\n\n\t\t\t\tif (remaining > 0) return;\n\n\t\t\t\t// Tap on empty space → deselect\n\t\t\t\tif (gesture.type === 'pending-pan') {\n\t\t\t\t\tengine.handlePointerDown(gesture.x, gesture.y, 0, noMods);\n\t\t\t\t\tengine.handlePointerUp();\n\t\t\t\t\tengine.markDirty();\n\t\t\t\t\tlastTapTime = Date.now();\n\t\t\t\t\tlastTapX = gesture.x;\n\t\t\t\t\tlastTapY = gesture.y;\n\t\t\t\t}\n\n\t\t\t\t// Tap on entity (no drag) → selection already happened\n\t\t\t\tif (gesture.type === 'pending-entity') {\n\t\t\t\t\tengine.handlePointerUp();\n\t\t\t\t\tengine.markDirty();\n\t\t\t\t\tlastTapTime = Date.now();\n\t\t\t\t\tlastTapX = gesture.x;\n\t\t\t\t\tlastTapY = gesture.y;\n\t\t\t\t}\n\n\t\t\t\t// Entity drag end\n\t\t\t\tif (gesture.type === 'entity-dragging') {\n\t\t\t\t\tengine.handlePointerUp();\n\t\t\t\t\tengine.markDirty();\n\t\t\t\t}\n\n\t\t\t\tgesture = { type: 'idle' };\n\t\t\t}\n\n\t\t\tfunction onTouchCancel(_e: TouchEvent) {\n\t\t\t\t// Reset gesture state unconditionally — the browser cancelled the touch.\n\t\t\t\t// Unlike onTouchEnd we don't inspect e.touches (which may be empty).\n\t\t\t\tgesture = { type: 'idle' };\n\t\t\t\tengine.handlePointerCancel();\n\t\t\t}\n\n\t\t\tcontainer.addEventListener('touchstart', onTouchStart, { passive: false });\n\t\t\tcontainer.addEventListener('touchmove', onTouchMove, { passive: false });\n\t\t\tcontainer.addEventListener('touchend', onTouchEnd, { passive: false });\n\t\t\tcontainer.addEventListener('touchcancel', onTouchCancel, { passive: true });\n\n\t\t\treturn () => {\n\t\t\t\tcontainer.removeEventListener('touchstart', onTouchStart);\n\t\t\t\tcontainer.removeEventListener('touchmove', onTouchMove);\n\t\t\t\tcontainer.removeEventListener('touchend', onTouchEnd);\n\t\t\t\tcontainer.removeEventListener('touchcancel', onTouchCancel);\n\t\t\t};\n\t\t}, [engine]);\n\n\t\t// Canvas-level pointer handlers — attached to the root container div so\n\t\t// pointer events in the \"outside handle strip\" (handle hit zone that\n\t\t// extends beyond a widget slot's DOM bounds) reach the engine. Widget\n\t\t// slots still have their own handlers and stopPropagation, so events\n\t\t// inside widgets never reach these fallbacks.\n\t\tconst onCanvasPointerDown = useCallback(\n\t\t\t(e: React.PointerEvent) => {\n\t\t\t\tconst target = e.target as HTMLElement | null;\n\t\t\t\t// Respect interactive form elements inside widget children.\n\t\t\t\tif (target?.closest('button, input, textarea, select, [contenteditable]')) return;\n\t\t\t\tconst rect = containerRef.current?.getBoundingClientRect();\n\t\t\t\tif (!rect) return;\n\t\t\t\tconst directive = engine.handlePointerDown(\n\t\t\t\t\te.clientX - rect.left,\n\t\t\t\t\te.clientY - rect.top,\n\t\t\t\t\te.button,\n\t\t\t\t\t{\n\t\t\t\t\t\tshift: e.shiftKey,\n\t\t\t\t\t\tctrl: e.ctrlKey,\n\t\t\t\t\t\talt: e.altKey,\n\t\t\t\t\t\tmeta: e.metaKey,\n\t\t\t\t\t},\n\t\t\t\t);\n\t\t\t\t// Capture on the container so subsequent moves route here even when\n\t\t\t\t// the pointer leaves the viewport. Widget slot already captures for\n\t\t\t\t// events that reach it first; this is for empty-space / outside-strip\n\t\t\t\t// events that bypass widget slots.\n\t\t\t\tif (\n\t\t\t\t\tdirective.action === 'capture-resize' ||\n\t\t\t\t\tdirective.action === 'passthrough-track-drag'\n\t\t\t\t) {\n\t\t\t\t\tcontainerRef.current?.setPointerCapture(e.pointerId);\n\t\t\t\t}\n\t\t\t\tif (directive.action === 'capture-resize') e.preventDefault();\n\t\t\t},\n\t\t\t[engine],\n\t\t);\n\n\t\tconst onCanvasPointerMove = useCallback(\n\t\t\t(e: React.PointerEvent) => {\n\t\t\t\t// Skip if a widget slot is handling this pointer — avoids double\n\t\t\t\t// handlePointerMove when the event bubbles from a captured widget.\n\t\t\t\tconst target = e.target as HTMLElement;\n\t\t\t\tif (target.closest?.('[data-widget-slot]') && target !== containerRef.current) {\n\t\t\t\t\treturn;\n\t\t\t\t}\n\t\t\t\tconst rect = containerRef.current?.getBoundingClientRect();\n\t\t\t\tif (!rect) return;\n\t\t\t\tengine.handlePointerMove(e.clientX - rect.left, e.clientY - rect.top, {\n\t\t\t\t\tshift: e.shiftKey,\n\t\t\t\t\tctrl: e.ctrlKey,\n\t\t\t\t\talt: e.altKey,\n\t\t\t\t\tmeta: e.metaKey,\n\t\t\t\t});\n\t\t\t},\n\t\t\t[engine],\n\t\t);\n\n\t\tconst onCanvasPointerUp = useCallback(\n\t\t\t(e: React.PointerEvent) => {\n\t\t\t\tif (containerRef.current?.hasPointerCapture(e.pointerId)) {\n\t\t\t\t\tcontainerRef.current.releasePointerCapture(e.pointerId);\n\t\t\t\t}\n\t\t\t\tengine.handlePointerUp();\n\t\t\t},\n\t\t\t[engine],\n\t\t);\n\n\t\t// rAF tick loop — flushes the engine when dirty, then applies updates.\n\t\t// This is THE render loop. Input handlers set engine dirty; this loop ticks.\n\t\tuseEffect(() => {\n\t\t\tlet rafId: number;\n\t\t\tlet running = true;\n\n\t\t\tfunction loop() {\n\t\t\t\tif (!running) return;\n\n\t\t\t\tconst didTick = engine.flushIfDirty();\n\t\t\t\tif (didTick) {\n\t\t\t\t\tconst camera = engine.getCamera();\n\t\t\t\t\tconst changes = engine.getFrameChanges();\n\n\t\t\t\t\t// 1. Update camera layer CSS transform (O(1) for pan/zoom)\n\t\t\t\t\tif (cameraLayerRef.current) {\n\t\t\t\t\t\tcameraLayerRef.current.style.transform = `scale(${camera.zoom}) translate(${-camera.x}px, ${-camera.y}px)`;\n\t\t\t\t\t}\n\n\t\t\t\t\t// RFC-001 Phase 7: apply derived cursor to root container.\n\t\t\t\t\t// Equality guard avoids redundant DOM writes in devtools diffs.\n\t\t\t\t\tconst cursor = engine.world.getResource(CursorResource).cursor;\n\t\t\t\t\tif (containerRef.current && containerRef.current.style.cursor !== cursor) {\n\t\t\t\t\t\tcontainerRef.current.style.cursor = cursor;\n\t\t\t\t\t}\n\n\t\t\t\t\t// 1b. Render WebGL dot grid + selection, with profiler probes.\n\t\t\t\t\tconst profiler = engine.profiler;\n\t\t\t\t\tconst profilerOn = profiler.isEnabled();\n\t\t\t\t\tlet selectionFramesDrawn = 0;\n\t\t\t\t\tlet snapGuidesDrawn = 0;\n\t\t\t\t\tlet spacingIndicatorsDrawn = 0;\n\n\t\t\t\t\t// Zero the engine renderer.info counters at the top of the\n\t\t\t\t\t// tick. `autoReset` is set to false in GridRenderer so that\n\t\t\t\t\t// grid + selection calls accumulate into one tick total.\n\t\t\t\t\tif (gridRendererRef.current) {\n\t\t\t\t\t\tgridRendererRef.current.getWebGLRenderer().info.reset();\n\t\t\t\t\t}\n\n\t\t\t\t\tif (gridRendererRef.current) {\n\t\t\t\t\t\tprofiler.beginWebGL('grid');\n\t\t\t\t\t\tgridRendererRef.current.render(camera.x, camera.y, camera.zoom);\n\t\t\t\t\t\tprofiler.endWebGL('grid');\n\t\t\t\t\t}\n\t\t\t\t\tif (selectionRendererRef.current && gridRendererRef.current) {\n\t\t\t\t\t\tconst selected = engine.getSelectedEntities();\n\t\t\t\t\t\tconst selBounds: SelectionBounds[] = [];\n\t\t\t\t\t\tfor (const id of selected) {\n\t\t\t\t\t\t\t// Widgets that render their own chrome (e.g. cards) opt out\n\t\t\t\t\t\t\t// of the engine-drawn frame by lacking the SelectionFrame tag.\n\t\t\t\t\t\t\tif (!engine.has(id, SelectionFrame)) continue;\n\t\t\t\t\t\t\tconst wb = engine.get(id, WorldBounds);\n\t\t\t\t\t\t\tif (wb)\n\t\t\t\t\t\t\t\tselBounds.push({\n\t\t\t\t\t\t\t\t\tx: wb.worldX,\n\t\t\t\t\t\t\t\t\ty: wb.worldY,\n\t\t\t\t\t\t\t\t\twidth: wb.worldWidth,\n\t\t\t\t\t\t\t\t\theight: wb.worldHeight,\n\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst hovId = engine.getHoveredEntity();\n\t\t\t\t\t\tlet hovBounds: SelectionBounds | null = null;\n\t\t\t\t\t\tif (hovId !== null && engine.has(hovId, SelectionFrame)) {\n\t\t\t\t\t\t\tconst wb = engine.get(hovId, WorldBounds);\n\t\t\t\t\t\t\tif (wb)\n\t\t\t\t\t\t\t\thovBounds = {\n\t\t\t\t\t\t\t\t\tx: wb.worldX,\n\t\t\t\t\t\t\t\t\ty: wb.worldY,\n\t\t\t\t\t\t\t\t\twidth: wb.worldWidth,\n\t\t\t\t\t\t\t\t\theight: wb.worldHeight,\n\t\t\t\t\t\t\t\t};\n\t\t\t\t\t\t}\n\t\t\t\t\t\tconst snapGuides = engine.getSnapGuides();\n\t\t\t\t\t\tconst equalSpacing = engine.getEqualSpacing();\n\t\t\t\t\t\tselectionFramesDrawn = selBounds.length + (hovBounds ? 1 : 0);\n\t\t\t\t\t\tsnapGuidesDrawn = snapGuides.length;\n\t\t\t\t\t\tspacingIndicatorsDrawn = equalSpacing.length;\n\n\t\t\t\t\t\tprofiler.beginWebGL('selection');\n\t\t\t\t\t\tselectionRendererRef.current.render(\n\t\t\t\t\t\t\tgridRendererRef.current.getWebGLRenderer(),\n\t\t\t\t\t\t\tcamera.x,\n\t\t\t\t\t\t\tcamera.y,\n\t\t\t\t\t\t\tcamera.zoom,\n\t\t\t\t\t\t\tselBounds,\n\t\t\t\t\t\t\thovBounds,\n\t\t\t\t\t\t\tsnapGuides,\n\t\t\t\t\t\t\tequalSpacing,\n\t\t\t\t\t\t);\n\t\t\t\t\t\tprofiler.endWebGL('selection');\n\t\t\t\t\t}\n\n\t\t\t\t\t// 1c. Capture renderer.info + counts from the engine WebGL\n\t\t\t\t\t// renderer. Because GridRenderer sets autoReset=false and we\n\t\t\t\t\t// reset at the top of this block, info.render now holds the\n\t\t\t\t\t// accumulated total across grid + selection passes.\n\t\t\t\t\tif (profilerOn && gridRendererRef.current) {\n\t\t\t\t\t\tconst info = gridRendererRef.current.getWebGLRenderer().info;\n\t\t\t\t\t\tprofiler.recordWebGLStats({\n\t\t\t\t\t\t\tdrawCalls: info.render.calls,\n\t\t\t\t\t\t\ttriangles: info.render.triangles,\n\t\t\t\t\t\t\tselectionFrames: selectionFramesDrawn,\n\t\t\t\t\t\t\tsnapGuides: snapGuidesDrawn,\n\t\t\t\t\t\t\tspacingIndicators: spacingIndicatorsDrawn,\n\t\t\t\t\t\t\tdomPositionsUpdated: changes.positionsChanged.length,\n\t\t\t\t\t\t});\n\t\t\t\t\t}\n\n\t\t\t\t\t// 2. Fix #1: Use WorldBounds (world-space) not Transform2D (local/parent-relative)\n\t\t\t\t\tfor (const entityId of changes.positionsChanged) {\n\t\t\t\t\t\tconst el = slotRefs.current.get(entityId);\n\t\t\t\t\t\tif (!el) continue;\n\t\t\t\t\t\tconst wb = engine.get(entityId, WorldBounds);\n\t\t\t\t\t\tif (!wb) continue;\n\t\t\t\t\t\tel.style.transform = `translate(${wb.worldX}px, ${wb.worldY}px)`;\n\t\t\t\t\t\tel.style.width = `${wb.worldWidth}px`;\n\t\t\t\t\t\tel.style.height = `${wb.worldHeight}px`;\n\t\t\t\t\t}\n\n\t\t\t\t\t// 3. Update visible entity list if entities entered/exited\n\t\t\t\t\tif (changes.entered.length > 0 || changes.exited.length > 0) {\n\t\t\t\t\t\tconst visible = engine.getVisibleEntities();\n\t\t\t\t\t\tsetVisibleEntities(visible.map((v) => v.entityId));\n\t\t\t\t\t}\n\n\t\t\t\t\t// 4. Fire event callbacks\n\t\t\t\t\tif (changes.selectionChanged && onSelectionChangeRef.current) {\n\t\t\t\t\t\tonSelectionChangeRef.current(engine.getSelectedEntities());\n\t\t\t\t\t}\n\t\t\t\t\tif (changes.cameraChanged && onCameraChangeRef.current) {\n\t\t\t\t\t\tonCameraChangeRef.current({ x: camera.x, y: camera.y, zoom: camera.zoom });\n\t\t\t\t\t}\n\t\t\t\t\tif (changes.navigationChanged && onNavigationChangeRef.current) {\n\t\t\t\t\t\tconst navStack = engine.world.getResource(NavigationStackResource);\n\t\t\t\t\t\tconst depth = navStack.frames.length - 1;\n\t\t\t\t\t\tconst containerId = navStack.frames[navStack.frames.length - 1].containerId;\n\t\t\t\t\t\tonNavigationChangeRef.current(depth, containerId);\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\trafId = requestAnimationFrame(loop);\n\t\t\t}\n\n\t\t\t// Initial tick on mount\n\t\t\tengine.tick();\n\t\t\tconst visible = engine.getVisibleEntities();\n\t\t\tsetVisibleEntities(visible.map((v) => v.entityId));\n\n\t\t\t// Set initial camera transform + grid\n\t\t\tconst camera = engine.getCamera();\n\t\t\tif (cameraLayerRef.current) {\n\t\t\t\tcameraLayerRef.current.style.transform = `scale(${camera.zoom}) translate(${-camera.x}px, ${-camera.y}px)`;\n\t\t\t}\n\t\t\t// Initial WebGL grid render\n\t\t\tif (gridRendererRef.current) {\n\t\t\t\tgridRendererRef.current.render(camera.x, camera.y, camera.zoom);\n\t\t\t}\n\n\t\t\t// Set initial slot positions\n\t\t\tfor (const v of visible) {\n\t\t\t\tconst el = slotRefs.current.get(v.entityId);\n\t\t\t\tif (!el) continue;\n\t\t\t\tel.style.transform = `translate(${v.worldX}px, ${v.worldY}px)`;\n\t\t\t\tel.style.width = `${v.worldWidth}px`;\n\t\t\t\tel.style.height = `${v.worldHeight}px`;\n\t\t\t}\n\n\t\t\t// Start the loop\n\t\t\trafId = requestAnimationFrame(loop);\n\n\t\t\treturn () => {\n\t\t\t\trunning = false;\n\t\t\t\tcancelAnimationFrame(rafId);\n\t\t\t};\n\t\t}, [engine]);\n\n\t\t// Fix #4: useLayoutEffect to set initial positions BEFORE browser paint\n\t\t// Prevents one-frame flash at (0,0) when new widgets enter the viewport\n\t\tuseLayoutEffect(() => {\n\t\t\tfor (const entityId of visibleEntities) {\n\t\t\t\tconst el = slotRefs.current.get(entityId);\n\t\t\t\tif (!el) continue;\n\t\t\t\tconst wb = engine.get(entityId, WorldBounds);\n\t\t\t\tif (!wb) continue;\n\t\t\t\tel.style.transform = `translate(${wb.worldX}px, ${wb.worldY}px)`;\n\t\t\t\tel.style.width = `${wb.worldWidth}px`;\n\t\t\t\tel.style.height = `${wb.worldHeight}px`;\n\t\t\t}\n\t\t}, [visibleEntities, engine]);\n\n\t\t// Split visible entities by surface\n\t\tconst { domEntities, webglEntities } = useMemo(() => {\n\t\t\tconst dom: EntityId[] = [];\n\t\t\tconst webgl: EntityId[] = [];\n\t\t\tfor (const id of visibleEntities) {\n\t\t\t\tconst w = engine.get(id, Widget);\n\t\t\t\tif (w?.surface === 'webgl') {\n\t\t\t\t\twebgl.push(id);\n\t\t\t\t} else {\n\t\t\t\t\tdom.push(id);\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn { domEntities: dom, webglEntities: webgl };\n\t\t}, [visibleEntities, engine]);\n\n\t\tconst canvasContent = (\n\t\t\t<div\n\t\t\t\tref={containerRef}\n\t\t\t\tclassName={`relative overflow-hidden ${className ?? ''}`}\n\t\t\t\tstyle={{\n\t\t\t\t\t...style,\n\t\t\t\t\ttouchAction: 'none',\n\t\t\t\t\tbackgroundColor: 'var(--canvas-bg, #fafafa)',\n\t\t\t\t}}\n\t\t\t\tonPointerDown={onCanvasPointerDown}\n\t\t\t\tonPointerMove={onCanvasPointerMove}\n\t\t\t\tonPointerUp={onCanvasPointerUp}\n\t\t\t>\n\t\t\t\t{/* WebGL layer — dot grid, selection overlays, connections */}\n\t\t\t\t<canvas ref={webglCanvasRef} className=\"absolute inset-0 pointer-events-none\" />\n\n\t\t\t\t{/* R3F layer — WebGL widgets (lazy, only when webgl entities exist) */}\n\t\t\t\t{webglEntities.length > 0 && <WebGLWidgetBridge engine={engine} entities={webglEntities} />}\n\n\t\t\t\t{/* Background — purely visual; pointer handlers live on the container.\n\t\t\t Kept as a div so the paint order (canvas, background, camera layer)\n\t\t\t is stable and future background visuals have a dedicated layer. */}\n\t\t\t\t<div className=\"absolute inset-0 pointer-events-none\" />\n\n\t\t\t\t{/* Camera transform layer — DOM widgets + selection overlays for WebGL widgets */}\n\t\t\t\t<div\n\t\t\t\t\tref={cameraLayerRef}\n\t\t\t\t\tclassName=\"absolute left-0 top-0 origin-top-left will-change-transform\"\n\t\t\t\t>\n\t\t\t\t\t{domEntities.map((entityId) => (\n\t\t\t\t\t\t<WidgetSlot key={entityId} entityId={entityId} slotRef={registerSlotRef} />\n\t\t\t\t\t))}\n\t\t\t\t\t{webglEntities.map((entityId) => (\n\t\t\t\t\t\t<SelectionOverlaySlot key={entityId} entityId={entityId} slotRef={registerSlotRef} />\n\t\t\t\t\t))}\n\t\t\t\t</div>\n\n\t\t\t\t{/* Children: toolbars, panels, etc. */}\n\t\t\t\t{children}\n\t\t\t</div>\n\t\t);\n\n\t\treturn (\n\t\t\t<EngineProvider value={engine}>\n\t\t\t\t<ContainerRefProvider value={containerRef}>\n\t\t\t\t\t<WidgetProvider engine={engine}>{canvasContent}</WidgetProvider>\n\t\t\t\t</ContainerRefProvider>\n\t\t\t</EngineProvider>\n\t\t);\n\t},\n);\n\n/** Bridge component — reads widget resolver from context and passes to WebGLWidgetLayer */\nfunction WebGLWidgetBridge({ engine, entities }: { engine: LayoutEngine; entities: EntityId[] }) {\n\tconst resolver = useWidgetResolver();\n\tconst resolve = useCallback(\n\t\t(entityId: EntityId) => {\n\t\t\tif (!resolver) return null;\n\t\t\tconst w = engine.get(entityId, Widget);\n\t\t\treturn resolver(entityId, w?.type ?? '');\n\t\t},\n\t\t[resolver, engine],\n\t);\n\n\tif (!resolver) return null;\n\n\treturn <WebGLWidgetLayer engine={engine} entities={entities} resolve={resolve} />;\n}\n"],"mappings":";;;;;;;;;;;AAWA,SAAgB,cAA2C,UAAuB;AAEjF,QADa,aAAa,UAAU,WACxB,EAAE,QAAQ,EAAE;;;;;;AAOzB,SAAgB,cAAc,UAAgC;AAE7D,QADa,aAAa,UAAU,iBACzB,EAAE,WAAW;;;;;;AAOzB,SAAgB,YAAY,UAAgC;AAE3D,QADa,aAAa,UAAU,SACzB,EAAE,OAAO,EAAE;;;;;;AAOvB,SAAgB,cAAc,UAA6B;AAC1D,QAAO,OAAO,UAAU,SAAS;;;;;;AAOlC,SAAgB,gBAAgB,UAA8D;CAC7F,MAAM,SAAS,iBAAiB;AAChC,QAAO,aACL,UAAmC;EACnC,MAAM,WAAW,OAAO,IAAI,UAAU,WAAW;AACjD,MAAI,SACH,QAAO,IAAI,UAAU,YAAY,EAChC,MAAM;GAAE,GAAG,SAAS;GAAM,GAAG;GAAO,EACpC,CAAC;IAGJ,CAAC,QAAQ,SAAS,CAClB;;;;;;;;;AC3CF,MAAMA,8BAAmF;CACxF,OAAO;EAAE,OAAO;EAAK,QAAQ;EAAK;CAClC,QAAQ;EAAE,OAAO;EAAK,QAAQ;EAAK;CACnC,OAAO;EAAE,OAAO;EAAK,QAAQ;EAAK;CAClC,IAAI;EAAE,OAAO;EAAK,QAAQ;EAAK;CAC/B;;;;;;;;AAkBD,SAAgB,UAAU,EAAE,UAAU,UAAU,WAAW,SAAyB;CACnF,MAAM,WAAW,OAAO,UAAU,SAAS;AAkB3C,QACC,oBAAC,OAAD;EAAgB;EAAW,OAAO;GAhBlC,OAAO;GACP,QAAQ;GACR,cAAc;GACd,UAAU;GACV,WAAW,WACR,6DACA;GACH,WAAW,WAAW,gBAAgB;GACtC,iBAAiB;GACjB,YACC;GACD,YAAY,WAAW,0BAA0B,KAAA;GACjD,GAAG;GAIwC;EACzC;EACI,CAAA;;;;;;;;;;;;AA6BR,SAAgB,iBAAoB,MAGlC;CACD,MAAM,cAAcA,4BAA0B,KAAK;CACnD,MAAM,SAAS,KAAK;CAEpB,MAAM,aAAkD,EAAE,eAAe;AAExE,SACC,oBAAC,WAAD;GAAqB;aACpB,oBAAC,QAAD;IAAkB;IAAU,MAHjB,cAAiB,SAGU;IAAI,CAAA;GAC/B,CAAA;;AA2Bd,QAAO;EAAE,QAAA;GAtBR,MAAM,KAAK;GACX,QAAQ,KAAK;GACb,aAAa,KAAK;GAClB;GACA,WAAW;GAkBG;EAAE,WAAA;GAdhB,IAAI,KAAK;GACT,QAAQ,KAAK;GACb,YAAY,CAAC,CAAC,MAAM,EAAE,QAAQ,KAAK,MAAM,CAAC,CAAC;GAC3C,aAAa;IACZ,YAAY;IACZ,WAAW;IACX,WAAW;IAGX,gBAAgB;IAChB;GACD;GAGyB;EAAE;;;;;ACjH7B,MAAM,4BAAmF;CACxF,OAAO;EAAE,OAAO;EAAK,QAAQ;EAAK;CAClC,QAAQ;EAAE,OAAO;EAAK,QAAQ;EAAK;CACnC,OAAO;EAAE,OAAO;EAAK,QAAQ;EAAK;CAClC,IAAI;EAAE,OAAO;EAAK,QAAQ;EAAK;CAC/B;;;;;AAMD,SAAS,wBACR,OACA,QACA,QACA,OACkB;CAClB,MAAM,QAAQ,IAAI,OAAO;CACzB,MAAM,IAAI,KAAK,IAAI,QAAQ,KAAK,IAAI,OAAO,OAAO,GAAG,EAAE;CACvD,MAAM,IAAI,CAAC,QAAQ;CACnB,MAAM,IAAI,CAAC,SAAS;AACpB,OAAM,OAAO,GAAG,IAAI,EAAE;AACtB,OAAM,OAAO,GAAG,IAAI,SAAS,EAAE;AAC/B,OAAM,iBAAiB,GAAG,IAAI,QAAQ,IAAI,GAAG,IAAI,OAAO;AACxD,OAAM,OAAO,IAAI,QAAQ,GAAG,IAAI,OAAO;AACvC,OAAM,iBAAiB,IAAI,OAAO,IAAI,QAAQ,IAAI,OAAO,IAAI,SAAS,EAAE;AACxE,OAAM,OAAO,IAAI,OAAO,IAAI,EAAE;AAC9B,OAAM,iBAAiB,IAAI,OAAO,GAAG,IAAI,QAAQ,GAAG,EAAE;AACtD,OAAM,OAAO,IAAI,GAAG,EAAE;AACtB,OAAM,iBAAiB,GAAG,GAAG,GAAG,IAAI,EAAE;AAEtC,QAAO,IAAI,gBAAgB,OAAO;EACjC;EACA,cAAc;EACd,eAAe;EACf,WAAW;EACX,gBAAgB;EAChB,CAAC;;AAWH,SAAS,SAAS,EAAE,OAAO,QAAQ,OAAO,WAAW,aAA4B;AAEhF,QACC,oBAAC,QAAD;EAAM,UAFU,cAAc,wBAAwB,OAAO,QAAQ,OAAO,EAAE,EAAE,CAAC,OAAO,OAAO,CAEvE;EAAE,UAAU;GAAC;GAAG;GAAG;GAAG;EAAE,eAAA;YAC/C,oBAAC,wBAAD;GAA6B;GAAkB;GAAsB;GAAa,CAAA;EAC5E,CAAA;;;;;;;;;;;AAyDT,SAAgB,yBAA4B,MAG1C;CACD,MAAM,cAAc,0BAA0B,KAAK;CACnD,MAAM,SAAS,KAAK;CACpB,MAAM,mBAAmB,KAAK,cAAc;CAE5C,MAAM,eACL,qBAAqB,gBAClB,OACA,qBAAqB,SACpB;EAAE,OAAO;EAAW,WAAW;EAAM,WAAW;EAAG,GACnD;EACA,OAAO,iBAAiB;EACxB,WAAW,iBAAiB,aAAa;EACzC,WAAW,iBAAiB,aAAa;EACzC;CAEL,MAAM,aAAkD,EAAE,UAAU,OAAO,aAAa;EACvF,MAAM,OAAO,cAAiB,SAAS;EACvC,MAAM,WAAW,OAAO,UAAU,SAAS;EAC3C,MAAM,WAAW,OAAc,KAAK;AAGpC,iBAAe;GACd,MAAM,IAAI,SAAS;AACnB,OAAI,CAAC,EAAG;GACR,MAAM,cAAc,WAAW,OAAO;GACtC,MAAM,UAAU,WAAW,IAAI;GAC/B,MAAM,IAAI,EAAE,MAAM;AAClB,KAAE,MAAM,UAAU,KAAK,cAAc,KAAK,GAAI;AAC9C,KAAE,SAAS,MAAM,UAAU,EAAE,SAAS,KAAK;IAC1C;AAEF,SACC,qBAAC,SAAD;GAAO,KAAK;aAAZ,CACE,gBACA,oBAAC,UAAD;IACQ;IACC;IACR,OAAO,aAAa;IACpB,WAAW,aAAa;IACxB,WAAW,aAAa;IACvB,CAAA,EAEH,oBAAC,QAAD;IAAkB;IAAgB;IAAa;IAAe;IAAU,CAAA,CACjE;;;AA0BV,QAAO;EAAE,QAAA;GArBR,MAAM,KAAK;GACX,SAAS;GACT,QAAQ,KAAK;GACb,aAAa,KAAK;GAClB;GACA,WAAW;GAgBG;EAAE,WAAA;GAZhB,IAAI,KAAK;GACT,QAAQ,KAAK;GACb,YAAY,CAAC,CAAC,MAAM,EAAE,QAAQ,KAAK,MAAM,CAAC,CAAC;GAC3C,aAAa;IACZ,YAAY;IACZ,WAAW;IACX,WAAW;IACX,gBAAgB;IAChB;GACD;GAGyB;EAAE;;;;;;;;ACtL7B,SAAgB,eAAe,EAAE,QAAQ,YAAiC;AAazE,QAAO,oBAAC,wBAAD;EAAwB,OAZd,aACf,WAAmB,eAA8C;GACjE,MAAM,MAAM,OAAO,UAAU,WAAW;AACxC,OAAI,CAAC,IAAK,QAAO;AACjB,OAAI,YAAY,IAAI,CACnB,QAAO;IAAE,SAAS;IAAS,WAAW,IAAI;IAAW;AAEtD,UAAO;IAAE,SAAS;IAAO,WAAW,IAAI;IAAW;KAEpD,CAAC,OAAO,CAGqC;EAAG;EAAkC,CAAA;;;;ACsCpF,MAAa,iBAAiB,MAAM,WACnC,SAAS,eACR,EACC,QACA,MACA,WACA,mBACA,gBACA,oBACA,WACA,OACA,YAED,KACC;CACD,MAAM,eAAe,OAAuB,KAAK;CAGjD,MAAM,uBAAuB,OAAO,kBAAkB;CACtD,MAAM,oBAAoB,OAAO,eAAe;CAChD,MAAM,wBAAwB,OAAO,mBAAmB;AACxD,iBAAgB;AACf,uBAAqB,UAAU;IAC7B,CAAC,kBAAkB,CAAC;AACvB,iBAAgB;AACf,oBAAkB,UAAU;IAC1B,CAAC,eAAe,CAAC;AACpB,iBAAgB;AACf,wBAAsB,UAAU;IAC9B,CAAC,mBAAmB,CAAC;AAGxB,qBACC,YACO;EACN,QAAQ,GAAG,MAAM;AAChB,UAAO,MAAM,GAAG,EAAE;AAClB,UAAO,WAAW;;EAEnB,SAAS,SAAS;AACjB,UAAO,OAAO,KAAK;AACnB,UAAO,WAAW;;EAEnB,YAAY,YAAY;AACvB,UAAO,UAAU,KAAA,GAAW,QAAQ;AACpC,UAAO,WAAW;;EAEnB,YAAY;AACX,UAAO,MAAM;AACb,UAAO,WAAW;;EAEnB,YAAY;AACX,UAAO,MAAM;AACb,UAAO,WAAW;;EAEnB,iBAAiB;EACjB,GACD,CAAC,OAAO,CACR;CAED,MAAM,iBAAiB,OAA0B,KAAK;CACtD,MAAM,kBAAkB,OAA4B,KAAK;CACzD,MAAM,uBAAuB,OAAiC,KAAK;CACnE,MAAM,iBAAiB,OAAuB,KAAK;CACnD,MAAM,WAAW,uBAAO,IAAI,KAA+B,CAAC;CAC5D,MAAM,CAAC,iBAAiB,sBAAsB,SAAqB,EAAE,CAAC;CAGtE,MAAM,kBAAkB,aAAa,UAAoB,OAA8B;AACtF,MAAI,GACH,UAAS,QAAQ,IAAI,UAAU,GAAG;MAElC,UAAS,QAAQ,OAAO,SAAS;IAEhC,EAAE,CAAC;AAGN,uBAAsB;EACrB,MAAM,YAAY,aAAa;EAC/B,MAAM,SAAS,eAAe;AAC9B,MAAI,CAAC,aAAa,CAAC,OAAQ;EAE3B,MAAM,cAAc,SAAS;EAC7B,IAAI,WAAgC;AACpC,MAAI,aAAa;AAChB,cAAW,IAAI,aAAa,OAAO;AACnC,mBAAgB,UAAU;;EAI3B,MAAM,UAAU,IAAI,mBAAmB;AACvC,uBAAqB,UAAU;EAE/B,MAAM,mBAAmB;GACxB,MAAM,OAAO,UAAU,uBAAuB;GAC9C,MAAM,MAAM,OAAO;AACnB,UAAO,YAAY,KAAK,OAAO,KAAK,QAAQ,IAAI;AAChD,UAAO,MAAM,QAAQ,GAAG,KAAK,MAAM;AACnC,UAAO,MAAM,SAAS,GAAG,KAAK,OAAO;AACrC,OAAI,SACH,UAAS,QAAQ,KAAK,OAAO,KAAK,QAAQ,IAAI;AAE/C,WAAQ,QAAQ,IAAI,QAAQ,KAAK,QAAQ,KAAK,KAAK,SAAS,IAAI,EAAE,IAAI;;AAGvE,cAAY;EACZ,MAAM,WAAW,IAAI,eAAe,WAAW;AAC/C,WAAS,QAAQ,UAAU;AAC3B,eAAa;AACZ,YAAS,YAAY;AACrB,OAAI,UAAU;AACb,aAAS,SAAS;AAClB,oBAAgB,UAAU;;AAE3B,WAAQ,SAAS;AACjB,wBAAqB,UAAU;;IAE9B,CAAC,QAAQ,KAAK,CAAC;AAGlB,iBAAgB;EACf,MAAM,QAAQ,gBAAgB;AAC9B,MAAI,SAAS,SAAS,OAAO;GAC5B,MAAM,SAAS,SAAS,gBAAgB,UAAU,SAAS,OAAO;AAClE,SAAM,UAAU;IACf,UAAU,SAAS;KAAC;KAAG;KAAG;KAAE,GAAG;KAAC;KAAG;KAAG;KAAE;IACxC,UAAU,SAAS,MAAO;IAC1B,GAAG;IACH,CAAC;;EAEH,MAAM,OAAO,qBAAqB;AAClC,MAAI,QAAQ,UACX,MAAK,UAAU,UAAU;AAE1B,SAAO,WAAW;IAChB;EAAC;EAAQ;EAAM;EAAU,CAAC;AAG7B,iBAAgB;EACf,MAAM,YAAY,aAAa;AAC/B,MAAI,CAAC,UAAW;EAEhB,MAAM,WAAW,MAAkB;AAClC,KAAE,gBAAgB;AAClB,OAAI,EAAE,WAAW,EAAE,SAAS;IAE3B,MAAM,OAAO,UAAU,uBAAuB;AAC9C,WAAO,YAAY,EAAE,UAAU,KAAK,MAAM,EAAE,UAAU,KAAK,KAAK,CAAC,EAAE,SAAS,IAAK;SAGjF,QAAO,MAAM,CAAC,EAAE,QAAQ,CAAC,EAAE,OAAO;;AAIpC,YAAU,iBAAiB,SAAS,SAAS,EAAE,SAAS,OAAO,CAAC;AAChE,eAAa,UAAU,oBAAoB,SAAS,QAAQ;IAC1D,CAAC,OAAO,CAAC;AAKZ,iBAAgB;EACf,MAAM,YAAY,aAAa;AAC/B,MAAI,CAAC,UAAW;EAUhB,IAAI,UAAwB,EAAE,MAAM,QAAQ;EAC5C,IAAI,cAAc;EAClB,IAAI,WAAW;EACf,IAAI,WAAW;EACf,MAAM,gBAAgB;EACtB,MAAM,kBAAkB;EAExB,SAAS,WAAW,QAAqC;GACxD,IAAI,KAAK;AACT,UAAO,MAAM,OAAO,WAAW;AAC9B,QAAI,GAAG,aAAa,mBAAmB,CAAE,QAAO;AAChD,SAAK,GAAG;;AAET,UAAO;;EAGR,SAAS,cAAc,QAAqC;GAC3D,MAAM,KAAK;AACX,OAAI,CAAC,GAAI,QAAO;GAChB,MAAM,MAAM,GAAG;AACf,UACC,QAAQ,WACR,QAAQ,cACR,QAAQ,YACR,QAAQ,YACR,GAAG,qBACH,GAAG,QAAQ,SAAS,KAAK;;EAI3B,SAAS,UAAU;AAClB,UAAO,WAAW,uBAAuB,IAAI,IAAI,SAAS;;EAG3D,SAAS,UAAU,IAAW,IAAW;GACxC,MAAM,KAAK,GAAG,UAAU,GAAG;GAC3B,MAAM,KAAK,GAAG,UAAU,GAAG;AAC3B,UAAO,KAAK,KAAK,KAAK,KAAK,KAAK,GAAG;;EAGpC,SAAS,YAAY,IAAW,IAAW,MAAe;AACzD,UAAO;IACN,IAAI,GAAG,UAAU,GAAG,WAAW,IAAI,KAAK;IACxC,IAAI,GAAG,UAAU,GAAG,WAAW,IAAI,KAAK;IACxC;;EAGF,SAAS,sBAAsB;AAC9B,OAAI,QAAQ,SAAS,oBAAoB,QAAQ,SAAS,kBACzD,QAAO,iBAAiB;;EAI1B,MAAM,SAAS;GAAE,OAAO;GAAO,MAAM;GAAO,KAAK;GAAO,MAAM;GAAO;EAErE,SAAS,aAAa,GAAe;GACpC,MAAM,OAAO,SAAS;GACtB,MAAM,UAAU,EAAE;AAGlB,OAAI,QAAQ,UAAU,GAAG;AACxB,MAAE,gBAAgB;AAClB,yBAAqB;IACrB,MAAM,OAAO,UAAU,QAAQ,IAAI,QAAQ,GAAG;IAC9C,MAAM,SAAS,YAAY,QAAQ,IAAI,QAAQ,IAAI,KAAK;AACxD,cAAU;KAAE,MAAM;KAAY,UAAU;KAAM,QAAQ,OAAO;KAAG,QAAQ,OAAO;KAAG;AAClF;;GAID,MAAM,QAAQ,QAAQ;GACtB,MAAM,IAAI,MAAM,UAAU,KAAK;GAC/B,MAAM,IAAI,MAAM,UAAU,KAAK;AAG/B,OAAI,cAAc,EAAE,OAAO,CAAE;AAE7B,KAAE,gBAAgB;GAGlB,MAAM,MAAM,KAAK,KAAK;AACtB,OACC,MAAM,cAAc,iBACpB,KAAK,IAAI,IAAI,SAAS,GAAG,mBACzB,KAAK,IAAI,IAAI,SAAS,GAAG,iBACxB;AACD,kBAAc;IAEd,MAAM,YAAY,OAAO,kBAAkB,GAAG,GAAG,GAAG,OAAO;AAC3D,QAAI;AACH,SAAI,UAAU,WAAW,0BAA0B;MAElD,MAAM,WAAW,OAAO,qBAAqB;AAC7C,UAAI,SAAS,WAAW,EACvB,QAAO,eAAe,SAAS,GAAG;YAE7B;MAEN,MAAM,SAAS,OAAO,WAAW;MACjC,MAAM,SAAS,OAAO,OAAO,KAAM,IAAI,OAAO,OAAO,MAAM,IAAI;AAC/D,aAAO,YAAY,GAAG,IAAI,SAAS,OAAO,QAAQ,OAAO,KAAK;;cAEtD;AACT,YAAO,iBAAiB;AACxB,YAAO,WAAW;;AAEnB,cAAU,EAAE,MAAM,QAAQ;AAC1B;;AAGD,OAAI,WAAW,EAAE,OAAO,EAAE;AAEzB,WAAO,kBAAkB,GAAG,GAAG,GAAG,OAAO;AACzC,cAAU;KAAE,MAAM;KAAkB;KAAG;KAAG,MAAM;KAAK;SAGrD,WAAU;IAAE,MAAM;IAAe;IAAG;IAAG,MAAM;IAAK;;EAIpD,SAAS,YAAY,GAAe;AACnC,KAAE,gBAAgB;GAClB,MAAM,OAAO,SAAS;GACtB,MAAM,UAAU,EAAE;AAGlB,OAAI,QAAQ,SAAS,cAAc,QAAQ,UAAU,GAAG;IACvD,MAAM,OAAO,UAAU,QAAQ,IAAI,QAAQ,GAAG;IAC9C,MAAM,SAAS,YAAY,QAAQ,IAAI,QAAQ,IAAI,KAAK;IACxD,MAAM,QAAQ,OAAO,QAAQ;AAC7B,WAAO,YAAY,OAAO,GAAG,OAAO,GAAG,QAAQ,EAAE;AACjD,WAAO,MAAM,OAAO,IAAI,QAAQ,QAAQ,OAAO,IAAI,QAAQ,OAAO;AAClE,YAAQ,WAAW;AACnB,YAAQ,SAAS,OAAO;AACxB,YAAQ,SAAS,OAAO;AACxB;;AAID,OAAI,QAAQ,UAAU,GAAG;AACxB,yBAAqB;IACrB,MAAM,OAAO,UAAU,QAAQ,IAAI,QAAQ,GAAG;IAC9C,MAAM,SAAS,YAAY,QAAQ,IAAI,QAAQ,IAAI,KAAK;AACxD,cAAU;KAAE,MAAM;KAAY,UAAU;KAAM,QAAQ,OAAO;KAAG,QAAQ,OAAO;KAAG;AAClF;;AAGD,OAAI,QAAQ,SAAS,EAAG;GACxB,MAAM,QAAQ,QAAQ;GACtB,MAAM,IAAI,MAAM,UAAU,KAAK;GAC/B,MAAM,IAAI,MAAM,UAAU,KAAK;AAG/B,OAAI,QAAQ,SAAS,eAAe;AACnC,QACC,KAAK,IAAI,IAAI,QAAQ,EAAE,GAAA,KACvB,KAAK,IAAI,IAAI,QAAQ,EAAE,GAAA,EAEvB,WAAU;KAAE,MAAM;KAAW,OAAO;KAAG,OAAO;KAAG;AAElD;;AAID,OAAI,QAAQ,SAAS,WAAW;AAC/B,WAAO,MAAM,IAAI,QAAQ,OAAO,IAAI,QAAQ,MAAM;AAClD,YAAQ,QAAQ;AAChB,YAAQ,QAAQ;AAChB;;AAID,OAAI,QAAQ,SAAS,oBAAoB,QAAQ,SAAS,mBAAmB;AAC5E,WAAO,kBAAkB,GAAG,GAAG,OAAO;AACtC,QAAI,QAAQ,SAAS;SAEnB,KAAK,IAAI,IAAI,QAAQ,EAAE,GAAA,KACvB,KAAK,IAAI,IAAI,QAAQ,EAAE,GAAA,EAEvB,WAAU,EAAE,MAAM,mBAAmB;;;;EAMzC,SAAS,WAAW,GAAe;AAClC,KAAE,gBAAgB;GAClB,MAAM,YAAY,EAAE,QAAQ;GAC5B,MAAM,OAAO,SAAS;AAGtB,OAAI,QAAQ,SAAS,YAAY;AAChC,QAAI,cAAc,GAAG;KACpB,MAAM,IAAI,EAAE,QAAQ;AACpB,eAAU;MACT,MAAM;MACN,OAAO,EAAE,UAAU,KAAK;MACxB,OAAO,EAAE,UAAU,KAAK;MACxB;eACS,cAAc,EACxB,WAAU,EAAE,MAAM,QAAQ;AAE3B;;AAGD,OAAI,YAAY,EAAG;AAGnB,OAAI,QAAQ,SAAS,eAAe;AACnC,WAAO,kBAAkB,QAAQ,GAAG,QAAQ,GAAG,GAAG,OAAO;AACzD,WAAO,iBAAiB;AACxB,WAAO,WAAW;AAClB,kBAAc,KAAK,KAAK;AACxB,eAAW,QAAQ;AACnB,eAAW,QAAQ;;AAIpB,OAAI,QAAQ,SAAS,kBAAkB;AACtC,WAAO,iBAAiB;AACxB,WAAO,WAAW;AAClB,kBAAc,KAAK,KAAK;AACxB,eAAW,QAAQ;AACnB,eAAW,QAAQ;;AAIpB,OAAI,QAAQ,SAAS,mBAAmB;AACvC,WAAO,iBAAiB;AACxB,WAAO,WAAW;;AAGnB,aAAU,EAAE,MAAM,QAAQ;;EAG3B,SAAS,cAAc,IAAgB;AAGtC,aAAU,EAAE,MAAM,QAAQ;AAC1B,UAAO,qBAAqB;;AAG7B,YAAU,iBAAiB,cAAc,cAAc,EAAE,SAAS,OAAO,CAAC;AAC1E,YAAU,iBAAiB,aAAa,aAAa,EAAE,SAAS,OAAO,CAAC;AACxE,YAAU,iBAAiB,YAAY,YAAY,EAAE,SAAS,OAAO,CAAC;AACtE,YAAU,iBAAiB,eAAe,eAAe,EAAE,SAAS,MAAM,CAAC;AAE3E,eAAa;AACZ,aAAU,oBAAoB,cAAc,aAAa;AACzD,aAAU,oBAAoB,aAAa,YAAY;AACvD,aAAU,oBAAoB,YAAY,WAAW;AACrD,aAAU,oBAAoB,eAAe,cAAc;;IAE1D,CAAC,OAAO,CAAC;CAOZ,MAAM,sBAAsB,aAC1B,MAA0B;AAG1B,MAFe,EAAE,QAEL,QAAQ,qDAAqD,CAAE;EAC3E,MAAM,OAAO,aAAa,SAAS,uBAAuB;AAC1D,MAAI,CAAC,KAAM;EACX,MAAM,YAAY,OAAO,kBACxB,EAAE,UAAU,KAAK,MACjB,EAAE,UAAU,KAAK,KACjB,EAAE,QACF;GACC,OAAO,EAAE;GACT,MAAM,EAAE;GACR,KAAK,EAAE;GACP,MAAM,EAAE;GACR,CACD;AAKD,MACC,UAAU,WAAW,oBACrB,UAAU,WAAW,yBAErB,cAAa,SAAS,kBAAkB,EAAE,UAAU;AAErD,MAAI,UAAU,WAAW,iBAAkB,GAAE,gBAAgB;IAE9D,CAAC,OAAO,CACR;CAED,MAAM,sBAAsB,aAC1B,MAA0B;EAG1B,MAAM,SAAS,EAAE;AACjB,MAAI,OAAO,UAAU,qBAAqB,IAAI,WAAW,aAAa,QACrE;EAED,MAAM,OAAO,aAAa,SAAS,uBAAuB;AAC1D,MAAI,CAAC,KAAM;AACX,SAAO,kBAAkB,EAAE,UAAU,KAAK,MAAM,EAAE,UAAU,KAAK,KAAK;GACrE,OAAO,EAAE;GACT,MAAM,EAAE;GACR,KAAK,EAAE;GACP,MAAM,EAAE;GACR,CAAC;IAEH,CAAC,OAAO,CACR;CAED,MAAM,oBAAoB,aACxB,MAA0B;AAC1B,MAAI,aAAa,SAAS,kBAAkB,EAAE,UAAU,CACvD,cAAa,QAAQ,sBAAsB,EAAE,UAAU;AAExD,SAAO,iBAAiB;IAEzB,CAAC,OAAO,CACR;AAID,iBAAgB;EACf,IAAI;EACJ,IAAI,UAAU;EAEd,SAAS,OAAO;AACf,OAAI,CAAC,QAAS;AAGd,OADgB,OAAO,cACZ,EAAE;IACZ,MAAM,SAAS,OAAO,WAAW;IACjC,MAAM,UAAU,OAAO,iBAAiB;AAGxC,QAAI,eAAe,QAClB,gBAAe,QAAQ,MAAM,YAAY,SAAS,OAAO,KAAK,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE;IAKvG,MAAM,SAAS,OAAO,MAAM,YAAY,eAAe,CAAC;AACxD,QAAI,aAAa,WAAW,aAAa,QAAQ,MAAM,WAAW,OACjE,cAAa,QAAQ,MAAM,SAAS;IAIrC,MAAM,WAAW,OAAO;IACxB,MAAM,aAAa,SAAS,WAAW;IACvC,IAAI,uBAAuB;IAC3B,IAAI,kBAAkB;IACtB,IAAI,yBAAyB;AAK7B,QAAI,gBAAgB,QACnB,iBAAgB,QAAQ,kBAAkB,CAAC,KAAK,OAAO;AAGxD,QAAI,gBAAgB,SAAS;AAC5B,cAAS,WAAW,OAAO;AAC3B,qBAAgB,QAAQ,OAAO,OAAO,GAAG,OAAO,GAAG,OAAO,KAAK;AAC/D,cAAS,SAAS,OAAO;;AAE1B,QAAI,qBAAqB,WAAW,gBAAgB,SAAS;KAC5D,MAAM,WAAW,OAAO,qBAAqB;KAC7C,MAAM,YAA+B,EAAE;AACvC,UAAK,MAAM,MAAM,UAAU;AAG1B,UAAI,CAAC,OAAO,IAAI,IAAI,eAAe,CAAE;MACrC,MAAM,KAAK,OAAO,IAAI,IAAI,YAAY;AACtC,UAAI,GACH,WAAU,KAAK;OACd,GAAG,GAAG;OACN,GAAG,GAAG;OACN,OAAO,GAAG;OACV,QAAQ,GAAG;OACX,CAAC;;KAEJ,MAAM,QAAQ,OAAO,kBAAkB;KACvC,IAAI,YAAoC;AACxC,SAAI,UAAU,QAAQ,OAAO,IAAI,OAAO,eAAe,EAAE;MACxD,MAAM,KAAK,OAAO,IAAI,OAAO,YAAY;AACzC,UAAI,GACH,aAAY;OACX,GAAG,GAAG;OACN,GAAG,GAAG;OACN,OAAO,GAAG;OACV,QAAQ,GAAG;OACX;;KAEH,MAAM,aAAa,OAAO,eAAe;KACzC,MAAM,eAAe,OAAO,iBAAiB;AAC7C,4BAAuB,UAAU,UAAU,YAAY,IAAI;AAC3D,uBAAkB,WAAW;AAC7B,8BAAyB,aAAa;AAEtC,cAAS,WAAW,YAAY;AAChC,0BAAqB,QAAQ,OAC5B,gBAAgB,QAAQ,kBAAkB,EAC1C,OAAO,GACP,OAAO,GACP,OAAO,MACP,WACA,WACA,YACA,aACA;AACD,cAAS,SAAS,YAAY;;AAO/B,QAAI,cAAc,gBAAgB,SAAS;KAC1C,MAAM,OAAO,gBAAgB,QAAQ,kBAAkB,CAAC;AACxD,cAAS,iBAAiB;MACzB,WAAW,KAAK,OAAO;MACvB,WAAW,KAAK,OAAO;MACvB,iBAAiB;MACjB,YAAY;MACZ,mBAAmB;MACnB,qBAAqB,QAAQ,iBAAiB;MAC9C,CAAC;;AAIH,SAAK,MAAM,YAAY,QAAQ,kBAAkB;KAChD,MAAM,KAAK,SAAS,QAAQ,IAAI,SAAS;AACzC,SAAI,CAAC,GAAI;KACT,MAAM,KAAK,OAAO,IAAI,UAAU,YAAY;AAC5C,SAAI,CAAC,GAAI;AACT,QAAG,MAAM,YAAY,aAAa,GAAG,OAAO,MAAM,GAAG,OAAO;AAC5D,QAAG,MAAM,QAAQ,GAAG,GAAG,WAAW;AAClC,QAAG,MAAM,SAAS,GAAG,GAAG,YAAY;;AAIrC,QAAI,QAAQ,QAAQ,SAAS,KAAK,QAAQ,OAAO,SAAS,EAEzD,oBADgB,OAAO,oBACG,CAAC,KAAK,MAAM,EAAE,SAAS,CAAC;AAInD,QAAI,QAAQ,oBAAoB,qBAAqB,QACpD,sBAAqB,QAAQ,OAAO,qBAAqB,CAAC;AAE3D,QAAI,QAAQ,iBAAiB,kBAAkB,QAC9C,mBAAkB,QAAQ;KAAE,GAAG,OAAO;KAAG,GAAG,OAAO;KAAG,MAAM,OAAO;KAAM,CAAC;AAE3E,QAAI,QAAQ,qBAAqB,sBAAsB,SAAS;KAC/D,MAAM,WAAW,OAAO,MAAM,YAAY,wBAAwB;KAClE,MAAM,QAAQ,SAAS,OAAO,SAAS;KACvC,MAAM,cAAc,SAAS,OAAO,SAAS,OAAO,SAAS,GAAG;AAChE,2BAAsB,QAAQ,OAAO,YAAY;;;AAInD,WAAQ,sBAAsB,KAAK;;AAIpC,SAAO,MAAM;EACb,MAAM,UAAU,OAAO,oBAAoB;AAC3C,qBAAmB,QAAQ,KAAK,MAAM,EAAE,SAAS,CAAC;EAGlD,MAAM,SAAS,OAAO,WAAW;AACjC,MAAI,eAAe,QAClB,gBAAe,QAAQ,MAAM,YAAY,SAAS,OAAO,KAAK,cAAc,CAAC,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE;AAGvG,MAAI,gBAAgB,QACnB,iBAAgB,QAAQ,OAAO,OAAO,GAAG,OAAO,GAAG,OAAO,KAAK;AAIhE,OAAK,MAAM,KAAK,SAAS;GACxB,MAAM,KAAK,SAAS,QAAQ,IAAI,EAAE,SAAS;AAC3C,OAAI,CAAC,GAAI;AACT,MAAG,MAAM,YAAY,aAAa,EAAE,OAAO,MAAM,EAAE,OAAO;AAC1D,MAAG,MAAM,QAAQ,GAAG,EAAE,WAAW;AACjC,MAAG,MAAM,SAAS,GAAG,EAAE,YAAY;;AAIpC,UAAQ,sBAAsB,KAAK;AAEnC,eAAa;AACZ,aAAU;AACV,wBAAqB,MAAM;;IAE1B,CAAC,OAAO,CAAC;AAIZ,uBAAsB;AACrB,OAAK,MAAM,YAAY,iBAAiB;GACvC,MAAM,KAAK,SAAS,QAAQ,IAAI,SAAS;AACzC,OAAI,CAAC,GAAI;GACT,MAAM,KAAK,OAAO,IAAI,UAAU,YAAY;AAC5C,OAAI,CAAC,GAAI;AACT,MAAG,MAAM,YAAY,aAAa,GAAG,OAAO,MAAM,GAAG,OAAO;AAC5D,MAAG,MAAM,QAAQ,GAAG,GAAG,WAAW;AAClC,MAAG,MAAM,SAAS,GAAG,GAAG,YAAY;;IAEnC,CAAC,iBAAiB,OAAO,CAAC;CAG7B,MAAM,EAAE,aAAa,kBAAkB,cAAc;EACpD,MAAM,MAAkB,EAAE;EAC1B,MAAM,QAAoB,EAAE;AAC5B,OAAK,MAAM,MAAM,gBAEhB,KADU,OAAO,IAAI,IAAI,OACpB,EAAE,YAAY,QAClB,OAAM,KAAK,GAAG;MAEd,KAAI,KAAK,GAAG;AAGd,SAAO;GAAE,aAAa;GAAK,eAAe;GAAO;IAC/C,CAAC,iBAAiB,OAAO,CAAC;AA4C7B,QACC,oBAAC,gBAAD;EAAgB,OAAO;YACtB,oBAAC,sBAAD;GAAsB,OAAO;aAC5B,oBAAC,gBAAD;IAAwB;cAAS,qBA5ClC,OAAD;KACC,KAAK;KACL,WAAW,4BAA4B,aAAa;KACpD,OAAO;MACN,GAAG;MACH,aAAa;MACb,iBAAiB;MACjB;KACD,eAAe;KACf,eAAe;KACf,aAAa;eAVd;MAaC,oBAAC,UAAD;OAAQ,KAAK;OAAgB,WAAU;OAAyC,CAAA;MAG/E,cAAc,SAAS,KAAK,oBAAC,mBAAD;OAA2B;OAAQ,UAAU;OAAiB,CAAA;MAK3F,oBAAC,OAAD,EAAK,WAAU,wCAAyC,CAAA;MAGxD,qBAAC,OAAD;OACC,KAAK;OACL,WAAU;iBAFX,CAIE,YAAY,KAAK,aACjB,oBAAC,YAAD;QAAqC;QAAU,SAAS;QAAmB,EAA1D,SAA0D,CAC1E,EACD,cAAc,KAAK,aACnB,oBAAC,sBAAD;QAA+C;QAAU,SAAS;QAAmB,EAA1D,SAA0D,CACpF,CACG;;MAGL;MACI;MAM0C;IAAkB,CAAA;GAC1C,CAAA;EACP,CAAA;EAGnB;;AAGD,SAAS,kBAAkB,EAAE,QAAQ,YAA4D;CAChG,MAAM,WAAW,mBAAmB;CACpC,MAAM,UAAU,aACd,aAAuB;AACvB,MAAI,CAAC,SAAU,QAAO;AAEtB,SAAO,SAAS,UADN,OAAO,IAAI,UAAU,OACJ,EAAE,QAAQ,GAAG;IAEzC,CAAC,UAAU,OAAO,CAClB;AAED,KAAI,CAAC,SAAU,QAAO;AAEtB,QAAO,oBAAC,kBAAD;EAA0B;EAAkB;EAAmB;EAAW,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jamesyong42/infinite-canvas",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Infinite canvas library with ECS layout engine, React components, and WebGL rendering",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "James Yong",
|
|
7
7
|
"repository": {
|
|
8
8
|
"type": "git",
|
|
9
|
-
"url": "https://github.com/jamesyong-42/infinite-canvas.git",
|
|
9
|
+
"url": "git+https://github.com/jamesyong-42/infinite-canvas.git",
|
|
10
10
|
"directory": "packages/infinite-canvas"
|
|
11
11
|
},
|
|
12
12
|
"bugs": {
|
|
@@ -45,28 +45,58 @@
|
|
|
45
45
|
"type": "module",
|
|
46
46
|
"exports": {
|
|
47
47
|
".": {
|
|
48
|
-
"
|
|
49
|
-
|
|
50
|
-
|
|
48
|
+
"import": {
|
|
49
|
+
"types": "./dist/index.d.mts",
|
|
50
|
+
"default": "./dist/index.mjs"
|
|
51
|
+
},
|
|
52
|
+
"require": {
|
|
53
|
+
"types": "./dist/index.d.cts",
|
|
54
|
+
"default": "./dist/index.cjs"
|
|
55
|
+
}
|
|
51
56
|
},
|
|
52
57
|
"./advanced": {
|
|
53
|
-
"
|
|
54
|
-
|
|
55
|
-
|
|
58
|
+
"import": {
|
|
59
|
+
"types": "./dist/advanced.d.mts",
|
|
60
|
+
"default": "./dist/advanced.mjs"
|
|
61
|
+
},
|
|
62
|
+
"require": {
|
|
63
|
+
"types": "./dist/advanced.d.cts",
|
|
64
|
+
"default": "./dist/advanced.cjs"
|
|
65
|
+
}
|
|
66
|
+
},
|
|
67
|
+
"./devtools": {
|
|
68
|
+
"import": {
|
|
69
|
+
"types": "./dist/devtools.d.mts",
|
|
70
|
+
"default": "./dist/devtools.mjs"
|
|
71
|
+
},
|
|
72
|
+
"require": {
|
|
73
|
+
"types": "./dist/devtools.d.cts",
|
|
74
|
+
"default": "./dist/devtools.cjs"
|
|
75
|
+
}
|
|
56
76
|
}
|
|
57
77
|
},
|
|
58
78
|
"main": "./dist/index.cjs",
|
|
59
|
-
"module": "./dist/index.
|
|
60
|
-
"types": "./dist/index.d.
|
|
79
|
+
"module": "./dist/index.mjs",
|
|
80
|
+
"types": "./dist/index.d.mts",
|
|
81
|
+
"typesVersions": {
|
|
82
|
+
"*": {
|
|
83
|
+
"advanced": [
|
|
84
|
+
"./dist/advanced.d.mts"
|
|
85
|
+
],
|
|
86
|
+
"devtools": [
|
|
87
|
+
"./dist/devtools.d.mts"
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
},
|
|
61
91
|
"scripts": {
|
|
62
|
-
"build": "
|
|
63
|
-
"dev": "
|
|
92
|
+
"build": "tsdown",
|
|
93
|
+
"dev": "tsdown --watch",
|
|
64
94
|
"test": "vitest run",
|
|
65
95
|
"prepack": "cp ../../README.md ./README.md && cp ../../LICENSE ./LICENSE",
|
|
66
96
|
"postpack": "rm -f ./README.md ./LICENSE"
|
|
67
97
|
},
|
|
68
98
|
"dependencies": {
|
|
69
|
-
"@jamesyong42/reactive-ecs": "^0.
|
|
99
|
+
"@jamesyong42/reactive-ecs": "^0.2.0"
|
|
70
100
|
},
|
|
71
101
|
"peerDependencies": {
|
|
72
102
|
"@react-three/fiber": "^9.0.0",
|
|
@@ -82,16 +112,18 @@
|
|
|
82
112
|
}
|
|
83
113
|
},
|
|
84
114
|
"devDependencies": {
|
|
115
|
+
"@arethetypeswrong/cli": "^0.18.2",
|
|
85
116
|
"@react-three/fiber": "^9.5.0",
|
|
86
117
|
"@types/rbush": "^4.0.0",
|
|
87
|
-
"rbush": "^4.0.1",
|
|
88
118
|
"@types/react": "^19.0.0",
|
|
89
119
|
"@types/react-dom": "^19.0.0",
|
|
90
120
|
"@types/three": "^0.183.1",
|
|
121
|
+
"publint": "^0.3.18",
|
|
122
|
+
"rbush": "^4.0.1",
|
|
91
123
|
"react": "^19.0.0",
|
|
92
124
|
"react-dom": "^19.0.0",
|
|
93
125
|
"three": "^0.183.2",
|
|
94
|
-
"
|
|
126
|
+
"tsdown": "^0.21.10",
|
|
95
127
|
"vitest": "^4.1.4"
|
|
96
128
|
},
|
|
97
129
|
"files": [
|