From 7977b019aa44f6b79e1aad3720af638cb211d051 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Tue, 28 Apr 2026 17:13:57 +0200 Subject: [PATCH 1/9] fix: canvas-level onClick/onClickMissed not firing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two bugs in createMissableEventRegistry: 1. The early-return guard `if (registry.array.length === 0) return` blocked canvas-level handlers (onClick, onDoubleClick, onContextMenu) whenever no mesh in the scene had that same prop. Fixed to only skip when there are also no canvas-level handlers. 2. Canvas onClickMissed (and siblings) fired on `visitedObjects.size > 0`, i.e. when something was hit — exactly backwards. Fixed to fire on `intersections.length === 0` (nothing was hit). Adds 50 tests in tests/core/canvas events.test.tsx covering all canvas-level event handlers: missable, default, and hover variants. see https://github.com/solidjs-community/solid-three/issues/49 --- src/create-events.ts | 4 +- tests/core/canvas-events.test.tsx | 713 ++++++++++++++++++++++++++++++ 2 files changed, 715 insertions(+), 2 deletions(-) create mode 100644 tests/core/canvas-events.test.tsx diff --git a/src/create-events.ts b/src/create-events.ts index 720e227..d4851c1 100644 --- a/src/create-events.ts +++ b/src/create-events.ts @@ -150,8 +150,8 @@ function createMissableEventRegistry( const registry = createRegistry() context.canvas.addEventListener(eventNameMap[type], nativeEvent => { - if (registry.array.length === 0) return const missedType = `${type}Missed` as const + if (registry.array.length === 0 && !context.props[type] && !context.props[missedType]) return // Track which objects have been visited during event processing const missedObjects = new Set(registry.array) @@ -214,7 +214,7 @@ function createMissableEventRegistry( getMeta(object)?.props[missedType]?.(missedEvent) } - if (visitedObjects.size > 0) { + if (intersections.length === 0) { context.props[`${type}Missed`]?.(missedEvent) } }) diff --git a/tests/core/canvas-events.test.tsx b/tests/core/canvas-events.test.tsx new file mode 100644 index 0000000..c87d5e1 --- /dev/null +++ b/tests/core/canvas-events.test.tsx @@ -0,0 +1,713 @@ +import { fireEvent } from "@solidjs/testing-library" +import * as THREE from "three" +import { describe, expect, it, vi } from "vitest" +import { createT } from "../../src/index.ts" +import { test } from "../../src/testing/index.tsx" + +const T = createT(THREE) + +// offsetX/Y that hits the 2×2 BoxGeometry centred at origin (camera at z=5) +const HIT_X = 640 +const HIT_Y = 400 + +// offsetX/Y that misses the mesh (top-left corner of canvas) +const MISS_X = 0 +const MISS_Y = 0 + +function makeEvent(type: string, offsetX: number, offsetY: number) { + const event = new Event(type) + Object.defineProperty(event, "offsetX", { get: () => offsetX }) + Object.defineProperty(event, "offsetY", { get: () => offsetY }) + return event +} + +function hitEvent(type: string) { + return makeEvent(type, HIT_X, HIT_Y) +} + +function missEvent(type: string) { + return makeEvent(type, MISS_X, MISS_Y) +} + +/** A plain 2×2 mesh at origin with no event handlers. */ +const BasicMesh = () => ( + + + + +) + +/** A 2×2 mesh at origin whose onClick stops propagation. */ +const StoppingMesh = (props: { eventType: string; handler?: (e: any) => void }) => { + const handlerProp = { [props.eventType]: (e: any) => { e.stopPropagation(); props.handler?.(e) } } + return ( + + + + + ) +} + +/** A 2×2 mesh at origin that registers for an event without stopping propagation. */ +const ListeningMesh = (props: { eventType: string; handler?: (e: any) => void }) => { + const handlerProp = { [props.eventType]: (e: any) => props.handler?.(e) } + return ( + + + + + ) +} + +/**********************************************************************************/ +/* */ +/* Missable Events */ +/* */ +/**********************************************************************************/ + +describe("canvas missable events", () => { + // + // onClick + // + describe("onClick", () => { + it("fires when canvas is clicked and no meshes are in the scene", () => { + const handleClick = vi.fn() + const { canvas } = test(() => null, { onClick: handleClick }) + + fireEvent(canvas, hitEvent("click")) + + expect(handleClick).toHaveBeenCalledTimes(1) + }) + + it("fires when click propagates through a mesh that does not stop it", () => { + const handleClick = vi.fn() + const { canvas } = test( + () => , + { onClick: handleClick }, + ) + + fireEvent(canvas, hitEvent("click")) + + expect(handleClick).toHaveBeenCalledTimes(1) + }) + + it("fires when click misses all meshes", () => { + const handleClick = vi.fn() + const { canvas } = test( + () => , + { onClick: handleClick }, + ) + + fireEvent(canvas, missEvent("click")) + + expect(handleClick).toHaveBeenCalledTimes(1) + }) + + it("does not fire when a mesh stops propagation", () => { + const handleClick = vi.fn() + const { canvas } = test( + () => , + { onClick: handleClick }, + ) + + fireEvent(canvas, hitEvent("click")) + + expect(handleClick).not.toHaveBeenCalled() + }) + }) + + // + // onClickMissed + // + describe("onClickMissed", () => { + it("fires when click misses all registered meshes", () => { + const handleClickMissed = vi.fn() + const { canvas } = test( + () => , + { onClickMissed: handleClickMissed }, + ) + + fireEvent(canvas, missEvent("click")) + + expect(handleClickMissed).toHaveBeenCalledTimes(1) + }) + + it("fires when canvas is clicked with no meshes in the scene", () => { + const handleClickMissed = vi.fn() + const { canvas } = test(() => null, { onClickMissed: handleClickMissed }) + + fireEvent(canvas, hitEvent("click")) + + expect(handleClickMissed).toHaveBeenCalledTimes(1) + }) + + it("does not fire when click hits a registered mesh", () => { + const handleClickMissed = vi.fn() + const { canvas } = test( + () => , + { onClickMissed: handleClickMissed }, + ) + + fireEvent(canvas, hitEvent("click")) + + expect(handleClickMissed).not.toHaveBeenCalled() + }) + }) + + // + // onDoubleClick + // + describe("onDoubleClick", () => { + it("fires when canvas is double-clicked and no meshes are in the scene", () => { + const handleDoubleClick = vi.fn() + const { canvas } = test(() => null, { onDoubleClick: handleDoubleClick }) + + fireEvent(canvas, hitEvent("dblclick")) + + expect(handleDoubleClick).toHaveBeenCalledTimes(1) + }) + + it("fires when double-click propagates through a mesh that does not stop it", () => { + const handleDoubleClick = vi.fn() + const { canvas } = test( + () => , + { onDoubleClick: handleDoubleClick }, + ) + + fireEvent(canvas, hitEvent("dblclick")) + + expect(handleDoubleClick).toHaveBeenCalledTimes(1) + }) + + it("fires when double-click misses all meshes", () => { + const handleDoubleClick = vi.fn() + const { canvas } = test( + () => , + { onDoubleClick: handleDoubleClick }, + ) + + fireEvent(canvas, missEvent("dblclick")) + + expect(handleDoubleClick).toHaveBeenCalledTimes(1) + }) + + it("does not fire when a mesh stops propagation", () => { + const handleDoubleClick = vi.fn() + const { canvas } = test( + () => , + { onDoubleClick: handleDoubleClick }, + ) + + fireEvent(canvas, hitEvent("dblclick")) + + expect(handleDoubleClick).not.toHaveBeenCalled() + }) + }) + + // + // onDoubleClickMissed + // + describe("onDoubleClickMissed", () => { + it("fires when double-click misses all registered meshes", () => { + const handleMissed = vi.fn() + const { canvas } = test( + () => , + { onDoubleClickMissed: handleMissed }, + ) + + fireEvent(canvas, missEvent("dblclick")) + + expect(handleMissed).toHaveBeenCalledTimes(1) + }) + + it("fires when canvas is double-clicked with no meshes in the scene", () => { + const handleMissed = vi.fn() + const { canvas } = test(() => null, { onDoubleClickMissed: handleMissed }) + + fireEvent(canvas, hitEvent("dblclick")) + + expect(handleMissed).toHaveBeenCalledTimes(1) + }) + + it("does not fire when double-click hits a registered mesh", () => { + const handleMissed = vi.fn() + const { canvas } = test( + () => , + { onDoubleClickMissed: handleMissed }, + ) + + fireEvent(canvas, hitEvent("dblclick")) + + expect(handleMissed).not.toHaveBeenCalled() + }) + }) + + // + // onContextMenu + // + describe("onContextMenu", () => { + it("fires when canvas receives contextmenu and no meshes are in the scene", () => { + const handleContextMenu = vi.fn() + const { canvas } = test(() => null, { onContextMenu: handleContextMenu }) + + fireEvent(canvas, hitEvent("contextmenu")) + + expect(handleContextMenu).toHaveBeenCalledTimes(1) + }) + + it("fires when contextmenu propagates through a mesh that does not stop it", () => { + const handleContextMenu = vi.fn() + const { canvas } = test( + () => , + { onContextMenu: handleContextMenu }, + ) + + fireEvent(canvas, hitEvent("contextmenu")) + + expect(handleContextMenu).toHaveBeenCalledTimes(1) + }) + + it("fires when contextmenu misses all meshes", () => { + const handleContextMenu = vi.fn() + const { canvas } = test( + () => , + { onContextMenu: handleContextMenu }, + ) + + fireEvent(canvas, missEvent("contextmenu")) + + expect(handleContextMenu).toHaveBeenCalledTimes(1) + }) + + it("does not fire when a mesh stops propagation", () => { + const handleContextMenu = vi.fn() + const { canvas } = test( + () => , + { onContextMenu: handleContextMenu }, + ) + + fireEvent(canvas, hitEvent("contextmenu")) + + expect(handleContextMenu).not.toHaveBeenCalled() + }) + }) + + // + // onContextMenuMissed + // + describe("onContextMenuMissed", () => { + it("fires when contextmenu misses all registered meshes", () => { + const handleMissed = vi.fn() + const { canvas } = test( + () => , + { onContextMenuMissed: handleMissed }, + ) + + fireEvent(canvas, missEvent("contextmenu")) + + expect(handleMissed).toHaveBeenCalledTimes(1) + }) + + it("fires when canvas receives contextmenu with no meshes in the scene", () => { + const handleMissed = vi.fn() + const { canvas } = test(() => null, { onContextMenuMissed: handleMissed }) + + fireEvent(canvas, hitEvent("contextmenu")) + + expect(handleMissed).toHaveBeenCalledTimes(1) + }) + + it("does not fire when contextmenu hits a registered mesh", () => { + const handleMissed = vi.fn() + const { canvas } = test( + () => , + { onContextMenuMissed: handleMissed }, + ) + + fireEvent(canvas, hitEvent("contextmenu")) + + expect(handleMissed).not.toHaveBeenCalled() + }) + }) +}) + +/**********************************************************************************/ +/* */ +/* Default Events */ +/* */ +/**********************************************************************************/ + +describe("canvas default events", () => { + // + // onMouseDown + // + describe("onMouseDown", () => { + it("fires when mousedown occurs with no meshes in the scene", () => { + const handleMouseDown = vi.fn() + const { canvas } = test(() => null, { onMouseDown: handleMouseDown }) + + fireEvent(canvas, hitEvent("mousedown")) + + expect(handleMouseDown).toHaveBeenCalledTimes(1) + }) + + it("fires when mousedown propagates through a mesh that does not stop it", () => { + const handleMouseDown = vi.fn() + const { canvas } = test( + () => , + { onMouseDown: handleMouseDown }, + ) + + fireEvent(canvas, hitEvent("mousedown")) + + expect(handleMouseDown).toHaveBeenCalledTimes(1) + }) + + it("does not fire when a mesh stops propagation", () => { + const handleMouseDown = vi.fn() + const { canvas } = test( + () => , + { onMouseDown: handleMouseDown }, + ) + + fireEvent(canvas, hitEvent("mousedown")) + + expect(handleMouseDown).not.toHaveBeenCalled() + }) + }) + + // + // onMouseUp + // + describe("onMouseUp", () => { + it("fires when mouseup occurs with no meshes in the scene", () => { + const handleMouseUp = vi.fn() + const { canvas } = test(() => null, { onMouseUp: handleMouseUp }) + + fireEvent(canvas, hitEvent("mouseup")) + + expect(handleMouseUp).toHaveBeenCalledTimes(1) + }) + + it("fires when mouseup propagates through a mesh that does not stop it", () => { + const handleMouseUp = vi.fn() + const { canvas } = test( + () => , + { onMouseUp: handleMouseUp }, + ) + + fireEvent(canvas, hitEvent("mouseup")) + + expect(handleMouseUp).toHaveBeenCalledTimes(1) + }) + + it("does not fire when a mesh stops propagation", () => { + const handleMouseUp = vi.fn() + const { canvas } = test( + () => , + { onMouseUp: handleMouseUp }, + ) + + fireEvent(canvas, hitEvent("mouseup")) + + expect(handleMouseUp).not.toHaveBeenCalled() + }) + }) + + // + // onPointerDown + // + describe("onPointerDown", () => { + it("fires when pointerdown occurs with no meshes in the scene", () => { + const handlePointerDown = vi.fn() + const { canvas } = test(() => null, { onPointerDown: handlePointerDown }) + + fireEvent(canvas, hitEvent("pointerdown")) + + expect(handlePointerDown).toHaveBeenCalledTimes(1) + }) + + it("fires when pointerdown propagates through a mesh that does not stop it", () => { + const handlePointerDown = vi.fn() + const { canvas } = test( + () => , + { onPointerDown: handlePointerDown }, + ) + + fireEvent(canvas, hitEvent("pointerdown")) + + expect(handlePointerDown).toHaveBeenCalledTimes(1) + }) + + it("does not fire when a mesh stops propagation", () => { + const handlePointerDown = vi.fn() + const { canvas } = test( + () => , + { onPointerDown: handlePointerDown }, + ) + + fireEvent(canvas, hitEvent("pointerdown")) + + expect(handlePointerDown).not.toHaveBeenCalled() + }) + }) + + // + // onPointerUp + // + describe("onPointerUp", () => { + it("fires when pointerup occurs with no meshes in the scene", () => { + const handlePointerUp = vi.fn() + const { canvas } = test(() => null, { onPointerUp: handlePointerUp }) + + fireEvent(canvas, hitEvent("pointerup")) + + expect(handlePointerUp).toHaveBeenCalledTimes(1) + }) + + it("fires when pointerup propagates through a mesh that does not stop it", () => { + const handlePointerUp = vi.fn() + const { canvas } = test( + () => , + { onPointerUp: handlePointerUp }, + ) + + fireEvent(canvas, hitEvent("pointerup")) + + expect(handlePointerUp).toHaveBeenCalledTimes(1) + }) + + it("does not fire when a mesh stops propagation", () => { + const handlePointerUp = vi.fn() + const { canvas } = test( + () => , + { onPointerUp: handlePointerUp }, + ) + + fireEvent(canvas, hitEvent("pointerup")) + + expect(handlePointerUp).not.toHaveBeenCalled() + }) + }) + + // + // onWheel + // + describe("onWheel", () => { + it("fires when wheel event occurs with no meshes in the scene", () => { + const handleWheel = vi.fn() + const { canvas } = test(() => null, { onWheel: handleWheel }) + + const event = new WheelEvent("wheel", { deltaY: 100 }) + Object.defineProperty(event, "offsetX", { get: () => HIT_X }) + Object.defineProperty(event, "offsetY", { get: () => HIT_Y }) + fireEvent(canvas, event) + + expect(handleWheel).toHaveBeenCalledTimes(1) + }) + + it("fires when wheel event propagates through a mesh that does not stop it", () => { + const handleWheel = vi.fn() + const { canvas } = test( + () => , + { onWheel: handleWheel }, + ) + + const event = new WheelEvent("wheel", { deltaY: 100 }) + Object.defineProperty(event, "offsetX", { get: () => HIT_X }) + Object.defineProperty(event, "offsetY", { get: () => HIT_Y }) + fireEvent(canvas, event) + + expect(handleWheel).toHaveBeenCalledTimes(1) + }) + + it("does not fire when a mesh stops propagation", () => { + const handleWheel = vi.fn() + const { canvas } = test( + () => , + { onWheel: handleWheel }, + ) + + const event = new WheelEvent("wheel", { deltaY: 100 }) + Object.defineProperty(event, "offsetX", { get: () => HIT_X }) + Object.defineProperty(event, "offsetY", { get: () => HIT_Y }) + fireEvent(canvas, event) + + expect(handleWheel).not.toHaveBeenCalled() + }) + }) +}) + +/**********************************************************************************/ +/* */ +/* Hover Events */ +/* */ +/**********************************************************************************/ + +describe("canvas hover events", () => { + // + // onPointerEnter / onPointerLeave / onPointerMove + // + describe("onPointerEnter", () => { + it("fires when the pointer first moves over the canvas", () => { + const handlePointerEnter = vi.fn() + const { canvas } = test(() => null, { onPointerEnter: handlePointerEnter }) + + fireEvent(canvas, hitEvent("pointermove")) + + expect(handlePointerEnter).toHaveBeenCalledTimes(1) + }) + + it("fires only once per canvas hover session", () => { + const handlePointerEnter = vi.fn() + const { canvas } = test(() => null, { onPointerEnter: handlePointerEnter }) + + fireEvent(canvas, hitEvent("pointermove")) + fireEvent(canvas, hitEvent("pointermove")) + fireEvent(canvas, hitEvent("pointermove")) + + expect(handlePointerEnter).toHaveBeenCalledTimes(1) + }) + + it("fires again after the pointer has left and re-entered", () => { + const handlePointerEnter = vi.fn() + const { canvas } = test(() => null, { onPointerEnter: handlePointerEnter }) + + fireEvent(canvas, hitEvent("pointermove")) + fireEvent(canvas, makeEvent("pointerleave", HIT_X, HIT_Y)) + fireEvent(canvas, hitEvent("pointermove")) + + expect(handlePointerEnter).toHaveBeenCalledTimes(2) + }) + }) + + describe("onPointerLeave", () => { + it("fires when the pointer leaves the canvas", () => { + const handlePointerLeave = vi.fn() + const { canvas } = test(() => null, { onPointerLeave: handlePointerLeave }) + + fireEvent(canvas, hitEvent("pointermove")) + fireEvent(canvas, makeEvent("pointerleave", HIT_X, HIT_Y)) + + expect(handlePointerLeave).toHaveBeenCalledTimes(1) + }) + }) + + describe("onPointerMove", () => { + it("fires when the pointer moves over the canvas with no meshes", () => { + const handlePointerMove = vi.fn() + const { canvas } = test(() => null, { onPointerMove: handlePointerMove }) + + fireEvent(canvas, hitEvent("pointermove")) + + expect(handlePointerMove).toHaveBeenCalledTimes(1) + }) + + it("fires when pointer move propagates through a mesh that does not stop it", () => { + const handlePointerMove = vi.fn() + const { canvas } = test( + () => , + { onPointerMove: handlePointerMove }, + ) + + fireEvent(canvas, hitEvent("pointermove")) + + expect(handlePointerMove).toHaveBeenCalledTimes(1) + }) + + it("does not fire when a mesh stops propagation", () => { + const handlePointerMove = vi.fn() + const { canvas } = test( + () => , + { onPointerMove: handlePointerMove }, + ) + + fireEvent(canvas, hitEvent("pointermove")) + + expect(handlePointerMove).not.toHaveBeenCalled() + }) + }) + + // + // onMouseEnter / onMouseLeave / onMouseMove + // + describe("onMouseEnter", () => { + it("fires when the mouse first moves over the canvas", () => { + const handleMouseEnter = vi.fn() + const { canvas } = test(() => null, { onMouseEnter: handleMouseEnter }) + + fireEvent(canvas, hitEvent("mousemove")) + + expect(handleMouseEnter).toHaveBeenCalledTimes(1) + }) + + it("fires only once per canvas hover session", () => { + const handleMouseEnter = vi.fn() + const { canvas } = test(() => null, { onMouseEnter: handleMouseEnter }) + + fireEvent(canvas, hitEvent("mousemove")) + fireEvent(canvas, hitEvent("mousemove")) + fireEvent(canvas, hitEvent("mousemove")) + + expect(handleMouseEnter).toHaveBeenCalledTimes(1) + }) + + it("fires again after the mouse has left and re-entered", () => { + const handleMouseEnter = vi.fn() + const { canvas } = test(() => null, { onMouseEnter: handleMouseEnter }) + + fireEvent(canvas, hitEvent("mousemove")) + fireEvent(canvas, makeEvent("mouseleave", HIT_X, HIT_Y)) + fireEvent(canvas, hitEvent("mousemove")) + + expect(handleMouseEnter).toHaveBeenCalledTimes(2) + }) + }) + + describe("onMouseLeave", () => { + it("fires when the mouse leaves the canvas", () => { + const handleMouseLeave = vi.fn() + const { canvas } = test(() => null, { onMouseLeave: handleMouseLeave }) + + fireEvent(canvas, hitEvent("mousemove")) + fireEvent(canvas, makeEvent("mouseleave", HIT_X, HIT_Y)) + + expect(handleMouseLeave).toHaveBeenCalledTimes(1) + }) + }) + + describe("onMouseMove", () => { + it("fires when the mouse moves over the canvas with no meshes", () => { + const handleMouseMove = vi.fn() + const { canvas } = test(() => null, { onMouseMove: handleMouseMove }) + + fireEvent(canvas, hitEvent("mousemove")) + + expect(handleMouseMove).toHaveBeenCalledTimes(1) + }) + + it("fires when mouse move propagates through a mesh that does not stop it", () => { + const handleMouseMove = vi.fn() + const { canvas } = test( + () => , + { onMouseMove: handleMouseMove }, + ) + + fireEvent(canvas, hitEvent("mousemove")) + + expect(handleMouseMove).toHaveBeenCalledTimes(1) + }) + + it("does not fire when a mesh stops propagation", () => { + const handleMouseMove = vi.fn() + const { canvas } = test( + () => , + { onMouseMove: handleMouseMove }, + ) + + fireEvent(canvas, hitEvent("mousemove")) + + expect(handleMouseMove).not.toHaveBeenCalled() + }) + }) +}) From d5bba3356cca114f0db7692b5b72bf70a811423b Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Tue, 28 Apr 2026 18:08:19 +0200 Subject: [PATCH 2/9] fix: autodispose T-component instances on unmount; add disposal and useLoader tests T-component constructors never called autodispose, so THREE geometries and materials created via etc. leaked on unmount. Fix by wrapping the constructed instance in autodispose inside createEntity, and change autodispose to call dispose() through the live property reference (arrow fn) rather than binding at construction time so post-hoc mocks work. Also adds tests for disposal behaviour (autodispose utility, Entity, and T components) and useLoader (caching, reactive URL, onLoad callback, record input). --- package.json | 2 +- src/create-t.tsx | 4 +- src/utils.ts | 2 +- tests/core/canvas-events.test.tsx | 14 +++ tests/core/disposal.test.tsx | 139 +++++++++++++++++++++++ tests/core/events.test.tsx | 153 +++++++++++++++++++++++++ tests/core/use-loader.test.tsx | 183 ++++++++++++++++++++++++++++++ 7 files changed, 493 insertions(+), 4 deletions(-) create mode 100644 tests/core/disposal.test.tsx create mode 100644 tests/core/use-loader.test.tsx diff --git a/package.json b/package.json index d77f262..aa74917 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ "lint:code": "eslint --max-warnings 0 src/**/*.{ts,tsx}", "lint:types": "tsc --noEmit", "prepublishOnly": "pnpm lint && pnpm build", - "test": "vitest", + "test": "vitest run", "types": "tsc --emitDeclarationOnly --declarationDir types" }, "exports": { diff --git a/src/create-t.tsx b/src/create-t.tsx index c8757e6..143fb4a 100644 --- a/src/create-t.tsx +++ b/src/create-t.tsx @@ -1,7 +1,7 @@ import { createMemo, type Component, type JSX } from "solid-js" import { useProps } from "./props.ts" import type { Props } from "./types.ts" -import { meta } from "./utils.ts" +import { autodispose, meta } from "./utils.ts" /**********************************************************************************/ /* */ @@ -47,7 +47,7 @@ export function createEntity( // listen to key changes props.key try { - return meta(new (Constructor as any)(...(props.args ?? [])), { props }) + return meta(autodispose(new (Constructor as any)(...(props.args ?? []))), { props }) } catch (e) { console.error(e) throw new Error("") diff --git a/src/utils.ts b/src/utils.ts index c45d93f..89064e0 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -56,7 +56,7 @@ export const isVector3 = (def: object): def is Vector3 => "isVector3" in def && export function autodispose void }>(object: T): T { if (object.dispose) { - onCleanup(object.dispose.bind(object)) + onCleanup(() => object.dispose?.()) } return object } diff --git a/tests/core/canvas-events.test.tsx b/tests/core/canvas-events.test.tsx index c87d5e1..8856632 100644 --- a/tests/core/canvas-events.test.tsx +++ b/tests/core/canvas-events.test.tsx @@ -152,6 +152,20 @@ describe("canvas missable events", () => { expect(handleClickMissed).not.toHaveBeenCalled() }) + + it("does not fire when onClick is also registered and click hits a mesh", () => { + const handleClick = vi.fn() + const handleClickMissed = vi.fn() + const { canvas } = test( + () => , + { onClick: handleClick, onClickMissed: handleClickMissed }, + ) + + fireEvent(canvas, hitEvent("click")) + + expect(handleClick).toHaveBeenCalledTimes(1) + expect(handleClickMissed).not.toHaveBeenCalled() + }) }) // diff --git a/tests/core/disposal.test.tsx b/tests/core/disposal.test.tsx new file mode 100644 index 0000000..442811f --- /dev/null +++ b/tests/core/disposal.test.tsx @@ -0,0 +1,139 @@ +import { Show, createSignal } from "solid-js" +import * as THREE from "three" +import { beforeEach, describe, expect, it, vi } from "vitest" +import { Entity, createT } from "../../src/index.ts" +import { autodispose } from "../../src/index.ts" +import { test } from "../../src/testing/index.tsx" + +const T = createT(THREE) + +describe("autodispose", () => { + it("calls dispose() on the object when the owner scope is cleaned up", async () => { + const disposable = { dispose: vi.fn() } + + const { unmount } = await test(() => { + autodispose(disposable) + return null + }) + + unmount() + + + expect(disposable.dispose).toHaveBeenCalledTimes(1) + }) + + it("does not throw when the object has no dispose method", async () => { + const noDispose = {} + + await expect(async () => { + const { unmount } = await test(() => { + autodispose(noDispose as any) + return null + }) + unmount() + + }).not.toThrow() + }) +}) + +describe("Entity disposal", () => { + it("disposes a constructor-created instance when the component unmounts", async () => { + class DisposableGeometry extends THREE.BoxGeometry { + dispose = vi.fn(() => super.dispose()) + } + + const [visible, setVisible] = createSignal(true) + + let geometry!: DisposableGeometry + + await test(() => ( + + { + geometry = g as DisposableGeometry + }} + /> + + )) + + expect(geometry).toBeDefined() + expect(geometry.dispose).not.toHaveBeenCalled() + + setVisible(false) + + + expect(geometry.dispose).toHaveBeenCalledTimes(1) + }) + + it("does not dispose an existing instance passed via from=instance", async () => { + const geometry = new THREE.BoxGeometry() + geometry.dispose = vi.fn(geometry.dispose.bind(geometry)) + + const [visible, setVisible] = createSignal(true) + + await test(() => ( + + + + )) + + setVisible(false) + + + expect(geometry.dispose).not.toHaveBeenCalled() + }) +}) + +describe("T component disposal", () => { + it("disposes geometry created via T when the component unmounts", async () => { + const [visible, setVisible] = createSignal(true) + let geometry!: THREE.BoxGeometry + + await test(() => ( + + + { + geometry = g + }} + /> + + + + )) + + geometry.dispose = vi.fn(geometry.dispose.bind(geometry)) + + setVisible(false) + + + expect(geometry.dispose).toHaveBeenCalledTimes(1) + }) + + it("disposes material created via T when the component unmounts", async () => { + const [visible, setVisible] = createSignal(true) + let material!: THREE.MeshBasicMaterial + + await test(() => ( + + + + { + material = m + }} + /> + + + )) + + material.dispose = vi.fn(material.dispose.bind(material)) + + setVisible(false) + + + expect(material.dispose).toHaveBeenCalledTimes(1) + }) +}) diff --git a/tests/core/events.test.tsx b/tests/core/events.test.tsx index e5bd0bc..d3d910e 100644 --- a/tests/core/events.test.tsx +++ b/tests/core/events.test.tsx @@ -388,3 +388,156 @@ describe("events", () => { }) }) }) + +/**********************************************************************************/ +/* */ +/* Mesh-level onClickMissed */ +/* */ +/**********************************************************************************/ + +const HIT_X = 640 +const HIT_Y = 400 +const MISS_X = 0 +const MISS_Y = 0 + +function makeClickAt(offsetX: number, offsetY: number) { + const event = new Event("click") + Object.defineProperty(event, "offsetX", { get: () => offsetX }) + Object.defineProperty(event, "offsetY", { get: () => offsetY }) + return event +} + +describe("mesh onClickMissed", () => { + it("fires when a click misses the mesh", async () => { + const handleClickMissed = vi.fn() + + const { canvas } = await test(() => ( + + + + + )) + + fireEvent(canvas, makeClickAt(MISS_X, MISS_Y)) + + + expect(handleClickMissed).toHaveBeenCalledTimes(1) + }) + + it("does not fire when the mesh itself is clicked", async () => { + const handleClickMissed = vi.fn() + + const { canvas } = await test(() => ( + + + + + )) + + fireEvent(canvas, makeClickAt(HIT_X, HIT_Y)) + + + expect(handleClickMissed).not.toHaveBeenCalled() + }) + + it("does not fire when a different mesh in the scene is clicked", async () => { + const handleClickMissed = vi.fn() + + // Mesh A: off-center (far right), has onClickMissed + // Mesh B: at origin (center of screen), gets clicked + const { canvas } = await test(() => ( + <> + + + + + + + + + + )) + + fireEvent(canvas, makeClickAt(HIT_X, HIT_Y)) + + expect(handleClickMissed).not.toHaveBeenCalled() + }) + + it("does not fire on a parent when its child is clicked", async () => { + const handleParentClickMissed = vi.fn() + const handleChildClick = vi.fn() + + const { canvas } = await test(() => ( + + + + + + + )) + + fireEvent(canvas, makeClickAt(HIT_X, HIT_Y)) + + + expect(handleChildClick).toHaveBeenCalledTimes(1) + expect(handleParentClickMissed).not.toHaveBeenCalled() + }) +}) + +/**********************************************************************************/ +/* */ +/* Event handler reactivity */ +/* */ +/**********************************************************************************/ + +describe("event handler reactivity", () => { + it("registers object in interaction list when event prop is added", async () => { + const handleClick = vi.fn() + const [onClick, setOnClick] = createSignal<((e: any) => void) | undefined>(undefined) + + const { canvas } = await test(() => ( + + + + + )) + + // No handler yet — click should not fire + fireEvent(canvas, makeClickAt(HIT_X, HIT_Y)) + + expect(handleClick).not.toHaveBeenCalled() + + // Add the handler reactively + setOnClick(() => handleClick) + + + fireEvent(canvas, makeClickAt(HIT_X, HIT_Y)) + + expect(handleClick).toHaveBeenCalledTimes(1) + }) + + it("removes object from interaction list when event prop is removed", async () => { + const handleClick = vi.fn() + const [onClick, setOnClick] = createSignal<((e: any) => void) | undefined>(handleClick) + + const { canvas } = await test(() => ( + + + + + )) + + // Handler active — click fires + fireEvent(canvas, makeClickAt(HIT_X, HIT_Y)) + + expect(handleClick).toHaveBeenCalledTimes(1) + + // Remove handler reactively + setOnClick(undefined) + + + fireEvent(canvas, makeClickAt(HIT_X, HIT_Y)) + + expect(handleClick).toHaveBeenCalledTimes(1) // no new call + }) +}) diff --git a/tests/core/use-loader.test.tsx b/tests/core/use-loader.test.tsx new file mode 100644 index 0000000..8e999d9 --- /dev/null +++ b/tests/core/use-loader.test.tsx @@ -0,0 +1,183 @@ +import { createSignal } from "solid-js" +import * as THREE from "three" +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest" +import { useLoader } from "../../src/index.ts" +import { LoaderCache } from "../../src/data-structure/loader-cache.ts" +import { test } from "../../src/testing/index.tsx" +import { asyncUtils } from "../utils/async-utils.ts" + +/**********************************************************************************/ +/* */ +/* Mock Loader */ +/* */ +/**********************************************************************************/ + +class MockResource { + constructor(public readonly url: string) {} + dispose = vi.fn() +} + +class MockLoader extends THREE.Loader { + load( + url: string, + onLoad: (result: MockResource) => void, + _onProgress?: (event: ProgressEvent) => void, + _onError?: (event: unknown) => void, + ) { + onLoad(new MockResource(url)) + } +} + +/**********************************************************************************/ +/* */ +/* Async helpers */ +/* */ +/**********************************************************************************/ + +const resolvers: (() => void)[] = [] +const { waitFor } = asyncUtils(resolver => resolvers.push(resolver)) + +/**********************************************************************************/ +/* */ +/* Tests */ +/* */ +/**********************************************************************************/ + +beforeEach(() => { + useLoader.cache = new LoaderCache() +}) + +afterEach(() => { + useLoader.cache = new LoaderCache() +}) + +describe("useLoader", () => { + it("returns the loaded resource", async () => { + let resource: (() => MockResource | undefined) | undefined + + function Component() { + resource = useLoader(MockLoader, "texture.png") as () => MockResource | undefined + return null + } + + test(() => ) + + await waitFor(() => resource?.() !== undefined) + + expect(resource?.()).toBeInstanceOf(MockResource) + expect(resource?.()?.url).toBe("texture.png") + }) + + it("caches the resource — same loader and URL return the same instance", async () => { + let first: (() => MockResource | undefined) | undefined + let second: (() => MockResource | undefined) | undefined + + function Component() { + first = useLoader(MockLoader, "texture.png") as () => MockResource | undefined + second = useLoader(MockLoader, "texture.png") as () => MockResource | undefined + return null + } + + test(() => ) + + await waitFor(() => first?.() !== undefined && second?.() !== undefined) + + expect(first?.()).toBeDefined() + expect(second?.()).toBeDefined() + expect(first?.()).toBe(second?.()) + }) + + it("returns distinct instances for different URLs", async () => { + let first: (() => MockResource | undefined) | undefined + let second: (() => MockResource | undefined) | undefined + + function Component() { + first = useLoader(MockLoader, "a.png") as () => MockResource | undefined + second = useLoader(MockLoader, "b.png") as () => MockResource | undefined + return null + } + + test(() => ) + + await waitFor(() => first?.() !== undefined && second?.() !== undefined) + + expect(first?.()?.url).toBe("a.png") + expect(second?.()?.url).toBe("b.png") + expect(first?.()).not.toBe(second?.()) + }) + + it("reloads when the URL changes reactively", async () => { + const [url, setUrl] = createSignal("first.png") + let resource: (() => MockResource | undefined) | undefined + + function Component() { + resource = useLoader(MockLoader, url) as () => MockResource | undefined + return null + } + + test(() => ) + + await waitFor(() => resource?.()?.url === "first.png") + + setUrl("second.png") + + await waitFor(() => resource?.()?.url === "second.png") + + expect(resource?.()?.url).toBe("second.png") + }) + + it("bypasses the cache when cache: false", async () => { + let first: (() => MockResource | undefined) | undefined + let second: (() => MockResource | undefined) | undefined + + function Component() { + first = useLoader(MockLoader, "texture.png", { cache: false }) as () => MockResource | undefined + second = useLoader(MockLoader, "texture.png", { cache: false }) as () => MockResource | undefined + return null + } + + test(() => ) + + await waitFor(() => first?.() !== undefined && second?.() !== undefined) + + expect(first?.()).toBeDefined() + expect(second?.()).toBeDefined() + expect(first?.()).not.toBe(second?.()) + }) + + it("calls onLoad callback after the resource is loaded", async () => { + const handleLoad = vi.fn() + let resource: (() => MockResource | undefined) | undefined + + function Component() { + resource = useLoader(MockLoader, "texture.png", { onLoad: handleLoad }) as () => MockResource | undefined + return null + } + + test(() => ) + + await waitFor(() => resource?.() !== undefined) + + expect(handleLoad).toHaveBeenCalledTimes(1) + expect(handleLoad).toHaveBeenCalledWith(expect.objectContaining({ url: "texture.png" })) + }) + + it("loads a record of URLs and returns a matching record of resources", async () => { + let resource: (() => Record | undefined) | undefined + + function Component() { + resource = useLoader(MockLoader, { diffuse: "diffuse.png", normal: "normal.png" }) as () => + Record | undefined + return null + } + + test(() => ) + + await waitFor(() => resource?.()?.diffuse !== undefined && resource?.()?.normal !== undefined) + + expect(resource?.()?.diffuse).toBeInstanceOf(MockResource) + expect(resource?.()?.normal).toBeInstanceOf(MockResource) + expect(resource?.()?.diffuse.url).toBe("diffuse.png") + expect(resource?.()?.normal.url).toBe("normal.png") + }) +}) From f1784662b3ff57b8ca9547c306723e42094b5718 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 29 Apr 2026 13:00:27 +0200 Subject: [PATCH 3/9] chore: add minimal demo for pkg.pr.new stackblitz template --- .github/workflows/pkg-pr-new.yml | 2 +- demo/App.tsx | 23 +++++++++++++++++++++++ demo/index.html | 16 ++++++++++++++++ demo/main.tsx | 4 ++++ demo/package.json | 21 +++++++++++++++++++++ demo/tsconfig.json | 18 ++++++++++++++++++ demo/vite.config.ts | 7 +++++++ 7 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 demo/App.tsx create mode 100644 demo/index.html create mode 100644 demo/main.tsx create mode 100644 demo/package.json create mode 100644 demo/tsconfig.json create mode 100644 demo/vite.config.ts diff --git a/.github/workflows/pkg-pr-new.yml b/.github/workflows/pkg-pr-new.yml index 2ed6d3e..eb20bb3 100644 --- a/.github/workflows/pkg-pr-new.yml +++ b/.github/workflows/pkg-pr-new.yml @@ -36,4 +36,4 @@ jobs: run: pnpm build - name: Publish preview - run: pnpx pkg-pr-new publish --compact + run: pnpx pkg-pr-new publish --compact --template ./demo diff --git a/demo/App.tsx b/demo/App.tsx new file mode 100644 index 0000000..112cc97 --- /dev/null +++ b/demo/App.tsx @@ -0,0 +1,23 @@ +import { createSignal } from "solid-js" +import * as THREE from "three" +import { Canvas, createT } from "solid-three" + +const T = createT(THREE) + +export function App() { + const [hovered, setHovered] = createSignal(false) + + return ( + + + + setHovered(true)} + onPointerLeave={() => setHovered(false)} + > + + + + + ) +} diff --git a/demo/index.html b/demo/index.html new file mode 100644 index 0000000..ac4b8a2 --- /dev/null +++ b/demo/index.html @@ -0,0 +1,16 @@ + + + + + + solid-three + + + +
+ + + diff --git a/demo/main.tsx b/demo/main.tsx new file mode 100644 index 0000000..bbf662e --- /dev/null +++ b/demo/main.tsx @@ -0,0 +1,4 @@ +import { render } from "solid-js/web" +import { App } from "./App.tsx" + +render(() => , document.getElementById("root")!) diff --git a/demo/package.json b/demo/package.json new file mode 100644 index 0000000..70570db --- /dev/null +++ b/demo/package.json @@ -0,0 +1,21 @@ +{ + "name": "solid-three-demo", + "private": true, + "version": "0.0.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build" + }, + "dependencies": { + "solid-js": "^1.8.0", + "solid-three": "latest", + "three": "^0.164.0" + }, + "devDependencies": { + "@types/three": "^0.164.0", + "typescript": "^5.4.0", + "vite": "^5.0.0", + "vite-plugin-solid": "^2.10.0" + } +} diff --git a/demo/tsconfig.json b/demo/tsconfig.json new file mode 100644 index 0000000..e6c6eec --- /dev/null +++ b/demo/tsconfig.json @@ -0,0 +1,18 @@ +{ + "compilerOptions": { + "allowImportingTsExtensions": true, + "allowSyntheticDefaultImports": true, + "esModuleInterop": true, + "isolatedModules": true, + "jsx": "preserve", + "jsxImportSource": "solid-js", + "lib": ["DOM", "DOM.Iterable", "ESNext"], + "module": "ESNext", + "moduleResolution": "bundler", + "noEmit": true, + "skipLibCheck": true, + "strict": true, + "target": "ESNext", + "types": ["vite/client"] + } +} diff --git a/demo/vite.config.ts b/demo/vite.config.ts new file mode 100644 index 0000000..490f0e3 --- /dev/null +++ b/demo/vite.config.ts @@ -0,0 +1,7 @@ +import { defineConfig } from "vite" +import solid from "vite-plugin-solid" + +export default defineConfig({ + base: "./", + plugins: [solid()], +}) From 31d10f8b6962215d6692a8a5865c74725eef00f8 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 29 Apr 2026 13:05:12 +0200 Subject: [PATCH 4/9] fix: exclude demo from root tsconfig; fix Loader constraints for TypeScript 5.9 --- src/data-structure/loader-cache.ts | 18 +++++++++--------- src/hooks.ts | 4 ++-- src/types.ts | 4 ++-- tsconfig.json | 2 +- 4 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/data-structure/loader-cache.ts b/src/data-structure/loader-cache.ts index a2dcb38..725c9b2 100644 --- a/src/data-structure/loader-cache.ts +++ b/src/data-structure/loader-cache.ts @@ -20,7 +20,7 @@ export interface LoaderRegistry { * @param url The URL or path to the resource * @param data The resource promise or resolved data */ - set>( + set>( loader: TLoader, url: LoaderUrl, data: PromiseMaybe>, @@ -32,7 +32,7 @@ export interface LoaderRegistry { * @param url The URL or path to the resource * @returns The resource promise, resolved data, or undefined if not found */ - get>( + get>( loader: TLoader, url: LoaderUrl, warn?: boolean, @@ -46,11 +46,11 @@ export interface LoaderRegistry { /**********************************************************************************/ interface LoaderTreeRegistryMap extends Map, any> { - get>(loader: TLoader): LoaderTreeRegistry | undefined - set>(loader: TLoader, data: LoaderTreeRegistry): this + get>(loader: TLoader): LoaderTreeRegistry | undefined + set>(loader: TLoader, data: LoaderTreeRegistry): this } -interface LoaderTreeRegistry> extends TreeRegistry { +interface LoaderTreeRegistry> extends TreeRegistry { get(paths: LoaderUrl, warn?: boolean): CacheNode set(paths: LoaderUrl, data: CacheNode): void } @@ -70,7 +70,7 @@ export class LoaderCache implements LoaderRegistry { * @returns The tree registry for this loader * @private */ - #registry>(loader: TLoader) { + #registry>(loader: TLoader) { let registry = this.#treeRegistryMap.get(loader) if (!registry) { this.#treeRegistryMap.set( @@ -129,7 +129,7 @@ export class LoaderCache implements LoaderRegistry { * @param url The URL or path to the resource * @param options.force Force deletion even if resource has active references */ - delete>( + delete>( loader: TLoader, url: LoaderUrl, options?: { force?: boolean }, @@ -151,7 +151,7 @@ export class LoaderCache implements LoaderRegistry { * @param url The URL or path to the resource * @returns The resource promise, resolved data, or undefined if not found */ - get>( + get>( loader: TLoader, url: LoaderUrl, warn?: boolean, @@ -171,7 +171,7 @@ export class LoaderCache implements LoaderRegistry { * @param options.force Force update even if resource already exists * @returns The stored promise */ - set>( + set>( loader: TLoader, path: LoaderUrl, data: PromiseMaybe>, diff --git a/src/hooks.ts b/src/hooks.ts index 4e9f459..5a5a9af 100644 --- a/src/hooks.ts +++ b/src/hooks.ts @@ -85,8 +85,8 @@ export function useThree(callback?: (value: Context) => any) { /** Global cache of loader instances to prevent duplicates */ const LOADER_CACHE = new Map< - Constructor>, - Loader + Constructor>, + Loader >() /** diff --git a/src/types.ts b/src/types.ts index 0da8d44..9f4c12b 100644 --- a/src/types.ts +++ b/src/types.ts @@ -120,11 +120,11 @@ export type ConstructorOverloadParameters = T extends { ? U : never -export type LoaderData> = T extends Loader +export type LoaderData> = T extends Loader ? TData : never -export type LoaderUrl> = T extends Loader +export type LoaderUrl> = T extends Loader ? TUrl : never diff --git a/tsconfig.json b/tsconfig.json index e4c2bdd..4c26156 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -18,5 +18,5 @@ "target": "ESNext", "verbatimModuleSyntax": true }, - "exclude": ["dist", "build", "node_modules"] + "exclude": ["dist", "build", "node_modules", "demo"] } From b0508153f062b370d3cc5947d3d40c53a9f82ae3 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 29 Apr 2026 13:05:40 +0200 Subject: [PATCH 5/9] chore: move demo source files into src/ --- demo/index.html | 2 +- demo/{ => src}/App.tsx | 0 demo/{ => src}/main.tsx | 0 3 files changed, 1 insertion(+), 1 deletion(-) rename demo/{ => src}/App.tsx (100%) rename demo/{ => src}/main.tsx (100%) diff --git a/demo/index.html b/demo/index.html index ac4b8a2..8599812 100644 --- a/demo/index.html +++ b/demo/index.html @@ -11,6 +11,6 @@
- + diff --git a/demo/App.tsx b/demo/src/App.tsx similarity index 100% rename from demo/App.tsx rename to demo/src/App.tsx diff --git a/demo/main.tsx b/demo/src/main.tsx similarity index 100% rename from demo/main.tsx rename to demo/src/main.tsx From f94a1aec41d83579cab38d185a45ebacbf14825d Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 29 Apr 2026 13:13:20 +0200 Subject: [PATCH 6/9] chore: add demo package-lock.json for stackblitz --- demo/package-lock.json | 1904 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1904 insertions(+) create mode 100644 demo/package-lock.json diff --git a/demo/package-lock.json b/demo/package-lock.json new file mode 100644 index 0000000..eeb4983 --- /dev/null +++ b/demo/package-lock.json @@ -0,0 +1,1904 @@ +{ + "name": "solid-three-demo", + "version": "0.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "solid-three-demo", + "version": "0.0.0", + "dependencies": { + "solid-js": "^1.8.0", + "solid-three": "latest", + "three": "^0.164.0" + }, + "devDependencies": { + "@types/three": "^0.164.0", + "typescript": "^5.4.0", + "vite": "^5.0.0", + "vite-plugin-solid": "^2.10.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.0.tgz", + "integrity": "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/compat-data": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.29.0.tgz", + "integrity": "sha512-T1NCJqT/j9+cn8fvkt7jtwbLBfLC/1y1c7NtCeXFRgzGTsafi68MRv8yzkYSapBnFA6L3U2VSc02ciDzoAJhJg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/core": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.29.0.tgz", + "integrity": "sha512-CGOfOJqWjg2qW/Mb6zNsDm+u5vFQ8DxXfbM09z69p5Z6+mE1ikP2jUXw+j42Pf1XTYED2Rni5f95npYeuwMDQA==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", + "debug": "^4.1.0", + "gensync": "^1.0.0-beta.2", + "json5": "^2.2.3", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/babel" + } + }, + "node_modules/@babel/generator": { + "version": "7.29.1", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.29.1.tgz", + "integrity": "sha512-qsaF+9Qcm2Qv8SRIMMscAvG4O3lJ0F1GuMo5HR/Bp02LopNgnZBC/EkbevHFeGs4ls/oPz9v+Bsmzbkbe+0dUw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.29.0", + "@babel/types": "^7.29.0", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-imports": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-module-transforms": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0" + } + }, + "node_modules/@babel/helper-plugin-utils": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-string-parser": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-option": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helpers": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.29.2.tgz", + "integrity": "sha512-HoGuUs4sCZNezVEKdVcwqmZN8GoHirLUcLaYVNBK2J0DadGtdcqgr3BCbvH8+XUo4NGjNl3VOtSjEKNzqfFgKw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/parser": { + "version": "7.29.2", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.29.2.tgz", + "integrity": "sha512-4GgRzy/+fsBa72/RZVJmGKPmZu9Byn8o4MoLpmNe1m8ZfYnz5emHLQz3U4gLud6Zwl0RZIcgiLD7Uq7ySFuDLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.29.0" + }, + "bin": { + "parser": "bin/babel-parser.js" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@babel/plugin-syntax-jsx": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/traverse": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.29.0.tgz", + "integrity": "sha512-4HPiQr0X7+waHfyXPZpWPfWL/J7dcN1mx9gL6WdQVMbPnF3+ZhSMs8tCxN7oHddJE9fhNE7+lxdnlyemKfJRuA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.29.0", + "@babel/generator": "^7.29.0", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.29.0", + "@babel/template": "^7.28.6", + "@babel/types": "^7.29.0", + "debug": "^4.3.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/types": { + "version": "7.29.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.29.0.tgz", + "integrity": "sha512-LwdZHpScM4Qz8Xw2iKSzS+cfglZzJGvofQICy7W7v4caru4EaAmyUuO6BGrbyQ2mYV11W0U8j5mBhd14dd3B0A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.2.tgz", + "integrity": "sha512-dnlp69efPPg6Uaw2dVqzWRfAWRnYVb1XJ8CyyhIbZeaq4CA5/mLeZ1IEt9QqQxmbdvagjLIm2ZL8BxXv5lH4Yw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.2.tgz", + "integrity": "sha512-OqZTwDRDchGRHHm/hwLOL7uVPB9aUvI0am/eQuWMNyFHf5PSEQmyEeYYheA0EPPKUO/l0uigCp+iaTjoLjVoHg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.2.tgz", + "integrity": "sha512-UwRE7CGpvSVEQS8gUMBe1uADWjNnVgP3Iusyda1nSRwNDCsRjnGc7w6El6WLQsXmZTbLZx9cecegumcitNfpmA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.2.tgz", + "integrity": "sha512-gjEtURKLCC5VXm1I+2i1u9OhxFsKAQJKTVB8WvDAHF+oZlq0GTVFOlTlO1q3AlCTE/DF32c16ESvfgqR7343/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.2.tgz", + "integrity": "sha512-Bcl6CYDeAgE70cqZaMojOi/eK63h5Me97ZqAQoh77VPjMysA/4ORQBRGo3rRy45x4MzVlU9uZxs8Uwy7ZaKnBw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.2.tgz", + "integrity": "sha512-LU+TPda3mAE2QB0/Hp5VyeKJivpC6+tlOXd1VMoXV/YFMvk/MNk5iXeBfB4MQGRWyOYVJ01625vjkr0Az98OJQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.2.tgz", + "integrity": "sha512-2QxQrM+KQ7DAW4o22j+XZ6RKdxjLD7BOWTP0Bv0tmjdyhXSsr2Ul1oJDQqh9Zf5qOwTuTc7Ek83mOFaKnodPjg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.2.tgz", + "integrity": "sha512-TbziEu2DVsTEOPif2mKWkMeDMLoYjx95oESa9fkQQK7r/Orta0gnkcDpzwufEcAO2BLBsD7mZkXGFqEdMRRwfw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.2.tgz", + "integrity": "sha512-bO/rVDiDUuM2YfuCUwZ1t1cP+/yqjqz+Xf2VtkdppefuOFS2OSeAfgafaHNkFn0t02hEyXngZkxtGqXcXwO8Rg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.2.tgz", + "integrity": "sha512-hr26p7e93Rl0Za+JwW7EAnwAvKkehh12BU1Llm9Ykiibg4uIr2rbpxG9WCf56GuvidlTG9KiiQT/TXT1yAWxTA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.2.tgz", + "integrity": "sha512-pOjB/uSIyDt+ow3k/RcLvUAOGpysT2phDn7TTUB3n75SlIgZzM6NKAqlErPhoFU+npgY3/n+2HYIQVbF70P9/A==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.2.tgz", + "integrity": "sha512-2/w+q8jszv9Ww1c+6uJT3OwqhdmGP2/4T17cu8WuwyUuuaCDDJ2ojdyYwZzCxx0GcsZBhzi3HmH+J5pZNXnd+Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.2.tgz", + "integrity": "sha512-11+aL5vKheYgczxtPVVRhdptAM2H7fcDR5Gw4/bTcteuZBlH4oP9f5s9zYO9aGZvoGeBpqXI/9TZZihZ609wKw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.2.tgz", + "integrity": "sha512-i16fokAGK46IVZuV8LIIwMdtqhin9hfYkCh8pf8iC3QU3LpwL+1FSFGej+O7l3E/AoknL6Dclh2oTdnRMpTzFQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.2.tgz", + "integrity": "sha512-49FkKS6RGQoriDSK/6E2GkAsAuU5kETFCh7pG4yD/ylj9rKhTmO3elsnmBvRD4PgJPds5W2PkhC82aVwmUcJ7A==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.2.tgz", + "integrity": "sha512-mjYNkHPfGpUR00DuM1ZZIgs64Hpf4bWcz9Z41+4Q+pgDx73UwWdAYyf6EG/lRFldmdHHzgrYyge5akFUW0D3mQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.2.tgz", + "integrity": "sha512-ALyvJz965BQk8E9Al/JDKKDLH2kfKFLTGMlgkAbbYtZuJt9LU8DW3ZoDMCtQpXAltZxwBHevXz5u+gf0yA0YoA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.2.tgz", + "integrity": "sha512-UQjrkIdWrKI626Du8lCQ6MJp/6V1LAo2bOK9OTu4mSn8GGXIkPXk/Vsp4bLHCd9Z9Iz2OTEaokUE90VweJgIYQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.2.tgz", + "integrity": "sha512-bTsRGj6VlSdn/XD4CGyzMnzaBs9bsRxy79eTqTCBsA8TMIEky7qg48aPkvJvFe1HyzQ5oMZdg7AnVlWQSKLTnw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.2.tgz", + "integrity": "sha512-6d4Z3534xitaA1FcMWP7mQPq5zGwBmGbhphh2DwaA1aNIXUu3KTOfwrWpbwI4/Gr0uANo7NTtaykFyO2hPuFLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.2.tgz", + "integrity": "sha512-NetAg5iO2uN7eB8zE5qrZ3CSil+7IJt4WDFLcC75Ymywq1VZVD6qJ6EvNLjZ3rEm6gB7XW5JdT60c6MN35Z85Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.2.tgz", + "integrity": "sha512-NCYhOotpgWZ5kdxCZsv6Iudx0wX8980Q/oW4pNFNihpBKsDbEA1zpkfxJGC0yugsUuyDZ7gL37dbzwhR0VI7pQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.2.tgz", + "integrity": "sha512-RXsaOqXxfoUBQoOgvmmijVxJnW2IGB0eoMO7F8FAjaj0UTywUO/luSqimWBJn04WNgUkeNhh7fs7pESXajWmkg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.2.tgz", + "integrity": "sha512-qdAzEULD+/hzObedtmV6iBpdL5TIbKVztGiK7O3/KYSf+HIzU257+MX1EXJcyIiDbMAqmbwaufcYPvyRryeZtA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.2.tgz", + "integrity": "sha512-Nd/SgG27WoA9e+/TdK74KnHz852TLa94ovOYySo/yMPuTmpckK/jIF2jSwS3g7ELSKXK13/cVdmg1Z/DaCWKxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tweenjs/tween.js": { + "version": "23.1.3", + "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.3.tgz", + "integrity": "sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/babel__core": { + "version": "7.20.5", + "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", + "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.20.7", + "@babel/types": "^7.20.7", + "@types/babel__generator": "*", + "@types/babel__template": "*", + "@types/babel__traverse": "*" + } + }, + "node_modules/@types/babel__generator": { + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__template": { + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/parser": "^7.1.0", + "@babel/types": "^7.0.0" + } + }, + "node_modules/@types/babel__traverse": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.28.2" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/stats.js": { + "version": "0.17.4", + "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.4.tgz", + "integrity": "sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/three": { + "version": "0.164.1", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.164.1.tgz", + "integrity": "sha512-dR/trWDhyaNqJV38rl1TonlCA9DpnX7OPYDWD81bmBGn/+uEc3+zNalFxQcV4FlPTeDBhCY3SFWKvK6EJwL88g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tweenjs/tween.js": "~23.1.1", + "@types/stats.js": "*", + "@types/webxr": "*", + "fflate": "~0.8.2", + "meshoptimizer": "~0.18.1" + } + }, + "node_modules/@types/webxr": { + "version": "0.5.24", + "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.24.tgz", + "integrity": "sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==", + "license": "MIT" + }, + "node_modules/babel-plugin-jsx-dom-expressions": { + "version": "0.40.6", + "resolved": "https://registry.npmjs.org/babel-plugin-jsx-dom-expressions/-/babel-plugin-jsx-dom-expressions-0.40.6.tgz", + "integrity": "sha512-v3P1MW46Lm7VMpAkq0QfyzLWWkC8fh+0aE5Km4msIgDx5kjenHU0pF2s+4/NH8CQn/kla6+Hvws+2AF7bfV5qQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-module-imports": "7.18.6", + "@babel/plugin-syntax-jsx": "^7.18.6", + "@babel/types": "^7.20.7", + "html-entities": "2.3.3", + "parse5": "^7.1.2" + }, + "peerDependencies": { + "@babel/core": "^7.20.12" + } + }, + "node_modules/babel-plugin-jsx-dom-expressions/node_modules/@babel/helper-module-imports": { + "version": "7.18.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", + "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/types": "^7.18.6" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/babel-preset-solid": { + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/babel-preset-solid/-/babel-preset-solid-1.9.12.tgz", + "integrity": "sha512-LLqnuKVDlKpyBlMPcH6qEvs/wmS9a+NczppxJ3ryS/c0O5IiSFOIBQi9GzyiGDSbcJpx4Gr87jyFTos1MyEuWg==", + "dev": true, + "license": "MIT", + "dependencies": { + "babel-plugin-jsx-dom-expressions": "^0.40.6" + }, + "peerDependencies": { + "@babel/core": "^7.0.0", + "solid-js": "^1.9.12" + }, + "peerDependenciesMeta": { + "solid-js": { + "optional": true + } + } + }, + "node_modules/baseline-browser-mapping": { + "version": "2.10.24", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.10.24.tgz", + "integrity": "sha512-I2NkZOOrj2XuguvWCK6OVh9GavsNjZjK908Rq3mIBK25+GD8vPX5w2WdxVqnQ7xx3SrZJiCiZFu+/Oz50oSYSA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.cjs" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/browserslist": { + "version": "4.28.2", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.2.tgz", + "integrity": "sha512-48xSriZYYg+8qXna9kwqjIVzuQxi+KYWp2+5nCYnYKPTr0LvD89Jqk2Or5ogxz0NUMfIjhh2lIUX/LyX9B4oIg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "baseline-browser-mapping": "^2.10.12", + "caniuse-lite": "^1.0.30001782", + "electron-to-chromium": "^1.5.328", + "node-releases": "^2.0.36", + "update-browserslist-db": "^1.2.3" + }, + "bin": { + "browserslist": "cli.js" + }, + "engines": { + "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" + } + }, + "node_modules/caniuse-lite": { + "version": "1.0.30001791", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001791.tgz", + "integrity": "sha512-yk0l/YSrOnFZk3UROpDLQD9+kC1l4meK/wed583AXrzoarMGJcbRi2Q4RaUYbKxYAsZ8sWmaSa/DsLmdBeI1vQ==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/caniuse-lite" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "CC-BY-4.0" + }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/electron-to-chromium": { + "version": "1.5.344", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.344.tgz", + "integrity": "sha512-4MxfbmNDm+KPh066EZy+eUnkcDPcZ35wNmOWzFuh/ijvHsve6kbLTLURy88uCNK5FbpN+yk2nQY6BYh1GEt+wg==", + "dev": true, + "license": "ISC" + }, + "node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/fflate": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz", + "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", + "dev": true, + "license": "MIT" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/gensync": { + "version": "1.0.0-beta.2", + "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", + "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/html-entities": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.3.3.tgz", + "integrity": "sha512-DV5Ln36z34NNTDgnz0EWGBLZENelNAtkiFA4kyNOG2tDI6Mz1uSWiq1wAKdyjnJwyDiDO7Fa2SO1CTxPXL8VxA==", + "dev": true, + "license": "MIT" + }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/jsesc": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", + "dev": true, + "license": "MIT", + "bin": { + "jsesc": "bin/jsesc" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "license": "MIT", + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dev": true, + "license": "ISC", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/merge-anything": { + "version": "5.1.7", + "resolved": "https://registry.npmjs.org/merge-anything/-/merge-anything-5.1.7.tgz", + "integrity": "sha512-eRtbOb1N5iyH0tkQDAoQ4Ipsp/5qSR79Dzrz8hEPxRX10RWWR/iQXdoKmBSRCThY1Fh5EhISDtpSc93fpxUniQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, + "node_modules/meshoptimizer": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz", + "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==", + "dev": true, + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/node-releases": { + "version": "2.0.38", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.38.tgz", + "integrity": "sha512-3qT/88Y3FbH/Kx4szpQQ4HzUbVrHPKTLVpVocKiLfoYvw9XSGOX2FmD2d6DrXbVYyAQTF2HeF6My8jmzx7/CRw==", + "dev": true, + "license": "MIT" + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.12", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.12.tgz", + "integrity": "sha512-W62t/Se6rA0Az3DfCL0AqJwXuKwBeYg6nOaIgzP+xZ7N5BFCI7DYi1qs6ygUYT6rvfi6t9k65UMLJC+PHZpDAA==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.60.2", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.2.tgz", + "integrity": "sha512-J9qZyW++QK/09NyN/zeO0dG/1GdGfyp9lV8ajHnRVLfo/uFsbji5mHnDgn/qYdUHyCkM2N+8VyspgZclfAh0eQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.2", + "@rollup/rollup-android-arm64": "4.60.2", + "@rollup/rollup-darwin-arm64": "4.60.2", + "@rollup/rollup-darwin-x64": "4.60.2", + "@rollup/rollup-freebsd-arm64": "4.60.2", + "@rollup/rollup-freebsd-x64": "4.60.2", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.2", + "@rollup/rollup-linux-arm-musleabihf": "4.60.2", + "@rollup/rollup-linux-arm64-gnu": "4.60.2", + "@rollup/rollup-linux-arm64-musl": "4.60.2", + "@rollup/rollup-linux-loong64-gnu": "4.60.2", + "@rollup/rollup-linux-loong64-musl": "4.60.2", + "@rollup/rollup-linux-ppc64-gnu": "4.60.2", + "@rollup/rollup-linux-ppc64-musl": "4.60.2", + "@rollup/rollup-linux-riscv64-gnu": "4.60.2", + "@rollup/rollup-linux-riscv64-musl": "4.60.2", + "@rollup/rollup-linux-s390x-gnu": "4.60.2", + "@rollup/rollup-linux-x64-gnu": "4.60.2", + "@rollup/rollup-linux-x64-musl": "4.60.2", + "@rollup/rollup-openbsd-x64": "4.60.2", + "@rollup/rollup-openharmony-arm64": "4.60.2", + "@rollup/rollup-win32-arm64-msvc": "4.60.2", + "@rollup/rollup-win32-ia32-msvc": "4.60.2", + "@rollup/rollup-win32-x64-gnu": "4.60.2", + "@rollup/rollup-win32-x64-msvc": "4.60.2", + "fsevents": "~2.3.2" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/seroval": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/seroval/-/seroval-1.5.2.tgz", + "integrity": "sha512-xcRN39BdsnO9Tf+VzsE7b3JyTJASItIV1FVFewJKCFcW4s4haIKS3e6vj8PGB9qBwC7tnuOywQMdv5N4qkzi7Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=10" + } + }, + "node_modules/seroval-plugins": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/seroval-plugins/-/seroval-plugins-1.5.2.tgz", + "integrity": "sha512-qpY0Cl+fKYFn4GOf3cMiq6l72CpuVaawb6ILjubOQ+diJ54LfOWaSSPsaswN8DRPIPW4Yq+tE1k5aKd7ILyaFg==", + "license": "MIT", + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "seroval": "^1.0" + } + }, + "node_modules/solid-js": { + "version": "1.9.12", + "resolved": "https://registry.npmjs.org/solid-js/-/solid-js-1.9.12.tgz", + "integrity": "sha512-QzKaSJq2/iDrWR1As6MHZQ8fQkdOBf8GReYb7L5iKwMGceg7HxDcaOHk0at66tNgn9U2U7dXo8ZZpLIAmGMzgw==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.1.0", + "seroval": "~1.5.0", + "seroval-plugins": "~1.5.0" + } + }, + "node_modules/solid-refresh": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/solid-refresh/-/solid-refresh-0.6.3.tgz", + "integrity": "sha512-F3aPsX6hVw9ttm5LYlth8Q15x6MlI/J3Dn+o3EQyRTtTxidepSTwAYdozt01/YA+7ObcciagGEyXIopGZzQtbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/generator": "^7.23.6", + "@babel/helper-module-imports": "^7.22.15", + "@babel/types": "^7.23.6" + }, + "peerDependencies": { + "solid-js": "^1.3" + } + }, + "node_modules/solid-three": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/solid-three/-/solid-three-0.2.0.tgz", + "integrity": "sha512-CW3a8OFG8fGdEekdvri/Tx1wu1Mm6gUKI73bOxX0rA349WPoCYq3h1FNGjNS68ec5i2TvPpn7ZC4a3bq/S42fw==", + "license": "MIT", + "dependencies": { + "@types/three": "0.149.0", + "zustand": "^3.7.2" + }, + "peerDependencies": { + "solid-js": "*", + "three": "*" + } + }, + "node_modules/solid-three/node_modules/@types/three": { + "version": "0.149.0", + "resolved": "https://registry.npmjs.org/@types/three/-/three-0.149.0.tgz", + "integrity": "sha512-fgNBm9LWc65ER/W0cvoXdC0iMy7Ke9e2CONmEr6Jt8sDSY3sw4DgOubZfmdZ747dkPhbQrgRQAWwDEr2S/7IEg==", + "license": "MIT", + "dependencies": { + "@types/webxr": "*" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/three": { + "version": "0.164.1", + "resolved": "https://registry.npmjs.org/three/-/three-0.164.1.tgz", + "integrity": "sha512-iC/hUBbl1vzFny7f5GtqzVXYjMJKaTPxiCxXfrvVdBi1Sf+jhd1CAkitiFwC7mIBFCo3MrDLJG97yisoaWig0w==", + "license": "MIT", + "peer": true + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/update-browserslist-db": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/browserslist" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/browserslist" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "escalade": "^3.2.0", + "picocolors": "^1.1.1" + }, + "bin": { + "update-browserslist-db": "cli.js" + }, + "peerDependencies": { + "browserslist": ">= 4.21.0" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + }, + "node_modules/vite-plugin-solid": { + "version": "2.11.12", + "resolved": "https://registry.npmjs.org/vite-plugin-solid/-/vite-plugin-solid-2.11.12.tgz", + "integrity": "sha512-FgjPcx2OwX9h6f28jli7A4bG7PP3te8uyakE5iqsmpq3Jqi1TWLgSroC9N6cMfGRU2zXsl4Q6ISvTr2VL0QHpA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.23.3", + "@types/babel__core": "^7.20.4", + "babel-preset-solid": "^1.8.4", + "merge-anything": "^5.1.7", + "solid-refresh": "^0.6.3", + "vitefu": "^1.0.4" + }, + "peerDependencies": { + "@testing-library/jest-dom": "^5.16.6 || ^5.17.0 || ^6.*", + "solid-js": "^1.7.2", + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "@testing-library/jest-dom": { + "optional": true + } + } + }, + "node_modules/vitefu": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/vitefu/-/vitefu-1.1.3.tgz", + "integrity": "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg==", + "dev": true, + "license": "MIT", + "workspaces": [ + "tests/deps/*", + "tests/projects/*", + "tests/projects/workspace/packages/*" + ], + "peerDependencies": { + "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", + "dev": true, + "license": "ISC" + }, + "node_modules/zustand": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/zustand/-/zustand-3.7.2.tgz", + "integrity": "sha512-PIJDIZKtokhof+9+60cpockVOq05sJzHCriyvaLBmEJixseQ1a5Kdov6fWZfWOu5SK9c+FhH1jU0tntLxRJYMA==", + "license": "MIT", + "engines": { + "node": ">=12.7.0" + }, + "peerDependencies": { + "react": ">=16.8" + }, + "peerDependenciesMeta": { + "react": { + "optional": true + } + } + } + } +} From d03e0a0ec22cd372ea493868a61a9865e5b4c573 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 29 Apr 2026 13:16:29 +0200 Subject: [PATCH 7/9] demo: add stackblitzrc --- demo/.stackblitzrc | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 demo/.stackblitzrc diff --git a/demo/.stackblitzrc b/demo/.stackblitzrc new file mode 100644 index 0000000..375ca13 --- /dev/null +++ b/demo/.stackblitzrc @@ -0,0 +1,4 @@ +{ + "installCommand": "pnpm install", + "startCommand": "pnpm run dev" +} From 3d0d678b4b014c0724899e61a0f0d3e4144d1258 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 29 Apr 2026 13:34:15 +0200 Subject: [PATCH 8/9] chore: add demo/node_modules to .gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 6fa23c6..dfac84e 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ dist types node_modules +demo/node_modules packed/ From 6a9f2d64ac04289da1831adab12c269846e14270 Mon Sep 17 00:00:00 2001 From: bigmistqke Date: Wed, 29 Apr 2026 21:20:43 +0200 Subject: [PATCH 9/9] docs: fix README API docs - Rename defaultCamera/defaultRaycaster Canvas props to camera/raycaster - Add useThree selector overload signature - Add useFrame return type (() => void) - Add createEntity section under T --- README.md | 38 ++++++++++++++++++++++++++++++++++++-- src/index.ts | 2 +- 2 files changed, 37 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 39fac07..1a99f4c 100644 --- a/README.md +++ b/README.md @@ -20,6 +20,7 @@ 3. [Components](#components) - [Canvas](#canvas) - [T](#t) + - [createEntity](#createentity) - [Entity](#entity) - [Portal](#portal) - [Resource](#resource) @@ -312,6 +313,22 @@ const T = createT({ Mesh, BoxGeometry, MeshBasicMaterial }) - **In Libraries**: create multiple `T` to allow for treeshaking or use [``](#entity) instead - **Multiple Ts**: Create multiple T instances for lazy loading different parts of three.js +#### createEntity + +`createT` is built on top of `createEntity`, which creates a single typed component from one Three.js constructor. Use it directly when you need a one-off component without building a full namespace: + +```tsx +import { createEntity } from "solid-three" +import { Mesh } from "three" + +const MeshComponent = createEntity(Mesh) + +// Equivalent to but without the full namespace + + ... + +``` + ### Portal The `Portal` component allows you to place children outside the regular scene graph while maintaining reactive updates. This is useful for rendering objects into different scenes or bypassing the normal parent-child relationships. @@ -390,7 +407,24 @@ Wrapper-component around ['useLoader'](#useloader). ### useThree -Provides access to the `three.js` context, including the renderer, scene, camera, and more. This hook can be used with or without a selector function for optimized access to specific properties. +Provides access to the `three.js` context, including the renderer, scene, camera, and more. + +**Signatures:** + +```tsx +// Returns the full context object directly +useThree(): Context + +// Returns a reactive accessor for a derived value +useThree(callback: (value: Context) => T): Accessor +``` + +Use the selector form to derive a specific value reactively: + +```tsx +const camera = useThree(ctx => ctx.camera) +// camera() is an Accessor +``` **Returns:** @@ -485,7 +519,7 @@ useFrame( priority?: number stage?: "before" | "after" } -) +): () => void ``` diff --git a/src/index.ts b/src/index.ts index f83ab62..888d451 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,4 +6,4 @@ export { useFrame, useLoader, useThree } from "./hooks.ts" export { useProps } from "./props.ts" export * from "./raycasters.tsx" export * as S3 from "./types.ts" -export { autodispose, getMeta, hasMeta as hasMeta, load, meta } from "./utils.ts" +export { autodispose, getMeta, hasMeta, load, meta } from "./utils.ts"