Add 3D object viewers with r3f stack
Add per-class 3D viewers for tiles, tokens, cards, and custom models using React Three Fiber, drei, and postprocessing. Viewers are lazy-loaded and registered through the existing viewer registry, with a shared scene wrapper for lighting, orbit controls, and subtle effects. Add a CORS-safe /asset proxy route so three.js loaders can fetch Workshop-hosted textures and models, and extend TTSObject with the CustomMesh and CustomTile/CustomToken fields the viewers read.
This commit is contained in:
@@ -2,6 +2,7 @@ import { serve } from '@hono/node-server';
|
||||
import { cors } from 'hono/cors';
|
||||
import { Hono } from 'hono';
|
||||
import { loadEnv, type Bindings } from './env.js';
|
||||
import asset from './routes/asset.js';
|
||||
import health from './routes/health.js';
|
||||
import items from './routes/items.js';
|
||||
import search from './routes/search.js';
|
||||
@@ -14,6 +15,7 @@ app.use('*', cors());
|
||||
app.route('/health', health);
|
||||
app.route('/search', search);
|
||||
app.route('/items', items);
|
||||
app.route('/asset', asset);
|
||||
|
||||
serve(
|
||||
{
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import asset from './asset.js';
|
||||
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
describe('asset route', () => {
|
||||
it('rejects a missing url', async () => {
|
||||
const res = await asset.request('/');
|
||||
expect(res.status).toBe(400);
|
||||
expect(await res.json()).toEqual({ error: 'Missing url query param' });
|
||||
});
|
||||
|
||||
it('rejects a non-http url', async () => {
|
||||
const res = await asset.request('/?url=file%3A%2F%2F%2Fetc%2Fpasswd');
|
||||
expect(res.status).toBe(400);
|
||||
});
|
||||
|
||||
it('streams the fetched asset with a content-type header', async () => {
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn().mockResolvedValue(
|
||||
new Response(new Uint8Array([1, 2, 3]), {
|
||||
headers: { 'content-type': 'image/png' },
|
||||
}),
|
||||
),
|
||||
);
|
||||
const res = await asset.request('/?url=https%3A%2F%2Fexample.com%2Fa.png');
|
||||
expect(res.status).toBe(200);
|
||||
expect(res.headers.get('content-type')).toBe('image/png');
|
||||
expect(await res.arrayBuffer()).toEqual(new Uint8Array([1, 2, 3]).buffer);
|
||||
});
|
||||
|
||||
it('returns 502 when the upstream fetch fails', async () => {
|
||||
vi.stubGlobal(
|
||||
'fetch',
|
||||
vi.fn().mockResolvedValue(new Response('error', { status: 500 })),
|
||||
);
|
||||
const res = await asset.request('/?url=https%3A%2F%2Fexample.com%2Fa.png');
|
||||
expect(res.status).toBe(502);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,47 @@
|
||||
import { Hono } from 'hono';
|
||||
|
||||
const app = new Hono();
|
||||
|
||||
/**
|
||||
* Fetch an external asset (texture, model, etc.) and stream it back to the
|
||||
* client. Workshop asset hosts (steamusercontent.com, etc.) often omit CORS
|
||||
* headers, which would block `TextureLoader` / `GLTFLoader` in the browser.
|
||||
* Routing through the proxy makes those assets loadable.
|
||||
*/
|
||||
app.get('/', async (c) => {
|
||||
const raw = c.req.query('url');
|
||||
if (!raw) {
|
||||
return c.json({ error: 'Missing url query param' }, 400);
|
||||
}
|
||||
|
||||
let url: URL;
|
||||
try {
|
||||
url = new URL(raw);
|
||||
} catch {
|
||||
return c.json({ error: 'Invalid url query param' }, 400);
|
||||
}
|
||||
// Only allow http(s) to avoid SSRF via file://, etc.
|
||||
if (url.protocol !== 'http:' && url.protocol !== 'https:') {
|
||||
return c.json({ error: 'Only http(s) urls are allowed' }, 400);
|
||||
}
|
||||
|
||||
let res: Response;
|
||||
try {
|
||||
res = await fetch(url);
|
||||
} catch (err) {
|
||||
return c.json({ error: `Failed to fetch asset: ${String(err)}` }, 502);
|
||||
}
|
||||
if (!res.ok) {
|
||||
return c.json({ error: `Asset responded ${res.status}` }, 502);
|
||||
}
|
||||
|
||||
const contentType = res.headers.get('content-type') ?? 'application/octet-stream';
|
||||
return new Response(res.body, {
|
||||
headers: {
|
||||
'Content-Type': contentType,
|
||||
'Cache-Control': 'public, max-age=86400',
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
export default app;
|
||||
@@ -15,17 +15,22 @@
|
||||
"@iconify-json/mdi": "^1.2.3",
|
||||
"@iconify/react": "^6.0.2",
|
||||
"@iconify/utils": "^3.1.4",
|
||||
"@react-three/drei": "^10.7.8",
|
||||
"@react-three/fiber": "^9.7.0",
|
||||
"@react-three/postprocessing": "^3.0.4",
|
||||
"@tts/extract": "workspace:*",
|
||||
"@tts/shared": "workspace:*",
|
||||
"react": "^19.2.8",
|
||||
"react-dom": "^19.2.8",
|
||||
"react-router-dom": "^7.18.2",
|
||||
"three": "^0.185.1",
|
||||
"zustand": "^5.0.14"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.3.3",
|
||||
"@types/react": "^19.2.18",
|
||||
"@types/react-dom": "^19.2.4",
|
||||
"@types/three": "^0.185.4",
|
||||
"@vitejs/plugin-react": "^6.0.5",
|
||||
"tailwindcss": "^4.3.3",
|
||||
"typescript": "^5.7.2",
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
import { useTexture } from '@react-three/drei';
|
||||
import type { TTSObject } from '@tts/shared';
|
||||
import Scene from './Scene';
|
||||
import { assetUrl } from './assetUrl';
|
||||
|
||||
/**
|
||||
* A playing card: a thin box with the face texture on the front and the back
|
||||
* texture on the rear. Reads `CustomDeck` face/back URLs, falling back to a
|
||||
* neutral color when absent.
|
||||
*/
|
||||
export default function CardViewer({ object }: { object: TTSObject }) {
|
||||
const deck = object.CustomDeck ? Object.values(object.CustomDeck)[0] : undefined;
|
||||
const faceUrl = deck?.FaceURL;
|
||||
const backUrl = deck?.BackURL;
|
||||
const face = faceUrl ? useTexture(assetUrl(faceUrl)) : null;
|
||||
const back = backUrl ? useTexture(assetUrl(backUrl)) : null;
|
||||
|
||||
return (
|
||||
<Scene>
|
||||
<mesh>
|
||||
<boxGeometry args={[1.4, 2, 0.06]} />
|
||||
<meshStandardMaterial
|
||||
color={face || back ? '#ffffff' : '#52525b'}
|
||||
map={face ?? back ?? undefined}
|
||||
roughness={0.6}
|
||||
/>
|
||||
</mesh>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
import { Suspense, useLayoutEffect } from 'react';
|
||||
import { useLoader } from '@react-three/fiber';
|
||||
import { useFBX, useGLTF, useTexture } from '@react-three/drei';
|
||||
import { OBJLoader } from 'three/examples/jsm/loaders/OBJLoader.js';
|
||||
import type { TTSObject } from '@tts/shared';
|
||||
import type * as THREE from 'three';
|
||||
import Scene from './Scene';
|
||||
import { assetUrl } from './assetUrl';
|
||||
|
||||
/**
|
||||
* A custom 3D model loaded from `CustomMesh.MeshURL`. Supports GLTF/GLB, OBJ,
|
||||
* and FBX by sniffing the URL extension; falls back to GLTF for unknown
|
||||
* extensions. `DiffuseURL` is applied to the model's materials when present.
|
||||
*/
|
||||
export default function CustomModelViewer({ object }: { object: TTSObject }) {
|
||||
const meshUrl = object.CustomMesh?.MeshURL;
|
||||
if (!meshUrl) {
|
||||
return (
|
||||
<Scene>
|
||||
<mesh>
|
||||
<boxGeometry args={[1, 1, 1]} />
|
||||
<meshStandardMaterial color="#52525b" />
|
||||
</mesh>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Scene>
|
||||
<Suspense fallback={null}>
|
||||
<Model meshUrl={meshUrl} diffuseUrl={object.CustomMesh?.DiffuseURL} />
|
||||
</Suspense>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
|
||||
function Model({
|
||||
meshUrl,
|
||||
diffuseUrl,
|
||||
}: {
|
||||
meshUrl: string;
|
||||
diffuseUrl?: string;
|
||||
}) {
|
||||
const ext = meshUrl.split('?')[0]!.split('.').pop()!.toLowerCase();
|
||||
const url = assetUrl(meshUrl);
|
||||
|
||||
const diffuse = diffuseUrl ? useTexture(assetUrl(diffuseUrl)) : null;
|
||||
|
||||
let root: THREE.Object3D;
|
||||
if (ext === 'obj') {
|
||||
root = useLoader(OBJLoader, url);
|
||||
} else if (ext === 'fbx') {
|
||||
root = useFBX(url);
|
||||
} else {
|
||||
root = useGLTF(url).scene;
|
||||
}
|
||||
|
||||
// Apply the diffuse texture to every mesh material on the loaded model.
|
||||
useLayoutEffect(() => {
|
||||
if (!diffuse) return;
|
||||
root.traverse((child) => {
|
||||
const mesh = child as THREE.Mesh;
|
||||
if (mesh.isMesh) {
|
||||
const material = Array.isArray(mesh.material)
|
||||
? mesh.material[0]
|
||||
: mesh.material;
|
||||
if (material && 'map' in material) {
|
||||
material.map = diffuse;
|
||||
material.needsUpdate = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
}, [root, diffuse]);
|
||||
|
||||
return <primitive object={root} scale={0.5} />;
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
import { Suspense, type ReactNode } from 'react';
|
||||
import { Canvas } from '@react-three/fiber';
|
||||
import { ContactShadows, OrbitControls } from '@react-three/drei';
|
||||
import { Bloom, EffectComposer, Vignette } from '@react-three/postprocessing';
|
||||
|
||||
/**
|
||||
* Shared 3D scene wrapper for object viewers. Provides a consistent camera,
|
||||
* lighting, orbit controls, a soft contact shadow, and subtle post-processing
|
||||
* (bloom + vignette). Children are wrapped in a Suspense boundary so loading
|
||||
* assets (textures, models) can suspend without blanking the page.
|
||||
*/
|
||||
export default function Scene({ children }: { children: ReactNode }) {
|
||||
return (
|
||||
<div className="h-80 w-full overflow-hidden rounded-lg bg-linear-to-b from-zinc-900 to-zinc-950">
|
||||
<Canvas
|
||||
camera={{ position: [2.2, 1.8, 2.6], fov: 40 }}
|
||||
dpr={[1, 2]}
|
||||
gl={{ antialias: true }}
|
||||
>
|
||||
<ambientLight intensity={0.5} />
|
||||
<directionalLight position={[4, 6, 3]} intensity={1.4} />
|
||||
<directionalLight position={[-4, 2, -3]} intensity={0.4} color="#b3c7ff" />
|
||||
<pointLight position={[0, 3, 0]} intensity={0.3} />
|
||||
|
||||
<Suspense fallback={null}>{children}</Suspense>
|
||||
|
||||
<ContactShadows
|
||||
position={[0, -0.5, 0]}
|
||||
opacity={0.55}
|
||||
scale={8}
|
||||
blur={2.4}
|
||||
far={3}
|
||||
resolution={256}
|
||||
/>
|
||||
<OrbitControls
|
||||
enablePan={false}
|
||||
minDistance={1}
|
||||
maxDistance={8}
|
||||
autoRotate
|
||||
autoRotateSpeed={1.2}
|
||||
/>
|
||||
|
||||
<EffectComposer>
|
||||
<Bloom intensity={0.25} luminanceThreshold={0.85} mipmapBlur />
|
||||
<Vignette eskil={false} offset={0.25} darkness={0.6} />
|
||||
</EffectComposer>
|
||||
</Canvas>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { useTexture } from '@react-three/drei';
|
||||
import type { TTSObject } from '@tts/shared';
|
||||
import Scene from './Scene';
|
||||
import { assetUrl } from './assetUrl';
|
||||
|
||||
/**
|
||||
* A flat tile with a texture on its top face. Uses `CustomImage.ImageURL`
|
||||
* (falling back to `ImageSecondaryURL`), with a neutral color when absent.
|
||||
*/
|
||||
export default function TileViewer({ object }: { object: TTSObject }) {
|
||||
const url = object.CustomImage?.ImageURL ?? object.CustomImage?.ImageSecondaryURL;
|
||||
const texture = url ? useTexture(assetUrl(url)) : null;
|
||||
const thickness = object.CustomImage?.CustomTile?.Thickness ?? 0.1;
|
||||
|
||||
return (
|
||||
<Scene>
|
||||
<mesh rotation={[-Math.PI / 2, 0, 0]}>
|
||||
<boxGeometry args={[1.6, 1.6, thickness]} />
|
||||
<meshStandardMaterial
|
||||
color={texture ? '#ffffff' : '#52525b'}
|
||||
map={texture ?? undefined}
|
||||
roughness={0.8}
|
||||
/>
|
||||
</mesh>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
import { useTexture } from '@react-three/drei';
|
||||
import type { TTSObject } from '@tts/shared';
|
||||
import Scene from './Scene';
|
||||
import { assetUrl } from './assetUrl';
|
||||
|
||||
/**
|
||||
* A round token: a short cylinder with the texture on its top face. Uses
|
||||
* `CustomImage.ImageURL`, with a neutral color when absent.
|
||||
*/
|
||||
export default function TokenViewer({ object }: { object: TTSObject }) {
|
||||
const url = object.CustomImage?.ImageURL ?? object.CustomImage?.ImageSecondaryURL;
|
||||
const texture = url ? useTexture(assetUrl(url)) : null;
|
||||
const thickness = object.CustomImage?.CustomToken?.Thickness ?? 0.1;
|
||||
|
||||
return (
|
||||
<Scene>
|
||||
<mesh rotation={[-Math.PI / 2, 0, 0]}>
|
||||
<cylinderGeometry args={[0.9, 0.9, thickness, 48]} />
|
||||
<meshStandardMaterial
|
||||
color={texture ? '#ffffff' : '#52525b'}
|
||||
map={texture ?? undefined}
|
||||
roughness={0.8}
|
||||
/>
|
||||
</mesh>
|
||||
</Scene>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
/**
|
||||
* Route an external asset URL through the proxy so it can be loaded by
|
||||
* three.js loaders (TextureLoader, GLTFLoader, etc.) despite the upstream host
|
||||
* omitting CORS headers.
|
||||
*/
|
||||
export function assetUrl(url: string): string {
|
||||
return `/asset?url=${encodeURIComponent(url)}`;
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { lazy } from 'react';
|
||||
import { registerViewer } from '../viewers';
|
||||
|
||||
// Register 3D viewers for the object classes that carry renderable assets.
|
||||
// Importing this module has the side effect of populating the viewer registry.
|
||||
// The viewers are lazy-loaded so the three.js stack is code-split out of the
|
||||
// main bundle and only fetched when a 3D-capable object is actually selected.
|
||||
const TileViewer = lazy(() => import('./TileViewer'));
|
||||
const TokenViewer = lazy(() => import('./TokenViewer'));
|
||||
const CardViewer = lazy(() => import('./CardViewer'));
|
||||
const CustomModelViewer = lazy(() => import('./CustomModelViewer'));
|
||||
|
||||
registerViewer('Tile', TileViewer);
|
||||
registerViewer('Custom_Tile', TileViewer);
|
||||
registerViewer('Custom_Token', TokenViewer);
|
||||
registerViewer('Card', CardViewer);
|
||||
registerViewer('Deck', CardViewer);
|
||||
registerViewer('DeckCustom', CardViewer);
|
||||
registerViewer('Custom_Deck', CardViewer);
|
||||
registerViewer('Custom_Model', CustomModelViewer);
|
||||
registerViewer('Custom_Model_Bag', CustomModelViewer);
|
||||
registerViewer('Custom_Model_Infinite_Bag', CustomModelViewer);
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { Suspense, useEffect, useMemo, useState } from 'react';
|
||||
import { Icon } from '@iconify/react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { buildTree, collectRefs } from '@tts/extract';
|
||||
@@ -8,6 +8,8 @@ import { modFileUrl } from '../api';
|
||||
import ObjectTree from '../components/ObjectTree';
|
||||
import { resolveViewer } from '../components/viewers';
|
||||
import { iconsForObject } from '../components/objectIcons';
|
||||
// Register the 3D viewers (side effect) so `resolveViewer` can find them.
|
||||
import '../components/viewers/register';
|
||||
|
||||
export default function ModPage() {
|
||||
const { id } = useParams<{ id: string }>();
|
||||
@@ -75,7 +77,17 @@ export default function ModPage() {
|
||||
{selected.object.GUID}
|
||||
</p>
|
||||
</header>
|
||||
{Viewer && <Viewer object={selected.object} />}
|
||||
{Viewer && (
|
||||
<Suspense
|
||||
fallback={
|
||||
<div className="flex h-80 items-center justify-center text-sm text-zinc-500">
|
||||
Loading viewer…
|
||||
</div>
|
||||
}
|
||||
>
|
||||
<Viewer object={selected.object} />
|
||||
</Suspense>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<p className="text-sm text-zinc-500">
|
||||
|
||||
@@ -10,6 +10,7 @@ export default defineConfig({
|
||||
'/search': 'http://localhost:3000',
|
||||
'/items': 'http://localhost:3000',
|
||||
'/health': 'http://localhost:3000',
|
||||
'/asset': 'http://localhost:3000',
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -45,8 +45,8 @@ apps/web ──► apps/proxy ──► packages/tts ──► packages/shared
|
||||
|
||||
### Edges
|
||||
|
||||
- **`apps/web` → `apps/proxy`** — calls `/search`, `/items/:id`, and
|
||||
`/items/:id/file` over HTTP.
|
||||
- **`apps/web` → `apps/proxy`** — calls `/search`, `/items/:id`,
|
||||
`/items/:id/file`, and `/asset` (CORS-safe asset proxy) over HTTP.
|
||||
- **`apps/web` → `packages/extract`** — uses `buildTree` / `collectRefs`
|
||||
to analyze a loaded `TTSMod` in the browser (tree sidebar + asset refs).
|
||||
- **`apps/proxy` → `packages/tts`** — calls `fetchMod` / `getFileName` to serve
|
||||
@@ -82,6 +82,10 @@ apps/web ──► apps/proxy ──► packages/tts ──► packages/shared
|
||||
| `bson` | BSON deserialization of TTS save files | `packages/tts` |
|
||||
| `cheerio` | Workshop browse page scraping | `apps/proxy` |
|
||||
| `zod` | Runtime validation | `apps/proxy`, `packages/shared` |
|
||||
| `three` | 3D rendering | `apps/web` |
|
||||
| `@react-three/fiber` | React renderer for three.js | `apps/web` |
|
||||
| `@react-three/drei` | three.js helpers (controls, textures) | `apps/web` |
|
||||
| `@react-three/postprocessing` | Post-processing effects | `apps/web` |
|
||||
|
||||
### Runtime constraints
|
||||
|
||||
@@ -103,7 +107,7 @@ apps/web ──► apps/proxy ──► packages/tts ──► packages/shared
|
||||
|
||||
- The proxy runs as a single Node process via `@hono/node-server`.
|
||||
- `apps/web` is a static Vite build served separately; during development it
|
||||
proxies `/search`, `/items`, and `/health` to the proxy.
|
||||
proxies `/search`, `/items`, `/health`, and `/asset` to the proxy.
|
||||
- `packages/extract` is published/consumed as a plain ESM module usable from a
|
||||
browser bundle or Node.
|
||||
- No shared state between requests; each request fetches fresh.
|
||||
@@ -129,3 +129,32 @@ without coupling the page to any one viewer.
|
||||
**Alternatives considered:** A single monolithic inspector component. Rejected —
|
||||
would grow unboundedly as per-class viewers are added; the registry keeps each
|
||||
viewer isolated and swappable.
|
||||
|
||||
## D11 — 3D viewers on the r3f/drei/postprocessing stack
|
||||
|
||||
**Decision:** Per-class 3D viewers for tiles, tokens, cards, and custom models
|
||||
are built on `@react-three/fiber`, `@react-three/drei`, and
|
||||
`@react-three/postprocessing`, registered through the existing viewer registry
|
||||
and lazy-loaded so the three.js stack is code-split out of the main bundle.
|
||||
|
||||
**Context:** The user wants to visualize objects in a save in 3D. The viewer
|
||||
registry (D10) already provides the extension point. Custom models load from
|
||||
`CustomMesh.MeshURL` (GLTF/OBJ/FBX); tiles/tokens/cards use their
|
||||
`CustomImage`/`CustomDeck` textures. Unity asset bundles are out of scope.
|
||||
|
||||
**Alternatives considered:** A single monolithic 3D inspector; a WebGL library
|
||||
other than three.js. Rejected — the registry keeps viewers isolated, and r3f is
|
||||
the de-facto React/three.js integration.
|
||||
|
||||
## D12 — CORS-safe asset proxy
|
||||
|
||||
**Decision:** The proxy exposes `GET /asset?url=...`, which fetches an external
|
||||
asset and streams it back with a `Content-Type` header. Only `http(s)` URLs are
|
||||
allowed.
|
||||
|
||||
**Context:** Workshop asset hosts (steamusercontent.com, etc.) often omit CORS
|
||||
headers, which would block three.js `TextureLoader` / `GLTFLoader` in the
|
||||
browser. Routing asset fetches through the proxy makes them loadable.
|
||||
|
||||
**Alternatives considered:** Loading assets directly in the browser. Rejected —
|
||||
CORS failures on common Workshop hosts would break the viewers.
|
||||
@@ -62,6 +62,7 @@ tts-workshop/
|
||||
│ │ ├── routes/
|
||||
│ │ │ ├── search.ts # GET /search?q=...&page=1
|
||||
│ │ │ ├── items.ts # GET /items/:id, /items/:id/file
|
||||
│ │ │ ├── asset.ts # GET /asset?url=... (CORS-safe asset proxy)
|
||||
│ │ │ └── health.ts # GET /health
|
||||
│ │ └── env.ts # zod env validation
|
||||
│ └── web/
|
||||
@@ -81,6 +82,14 @@ tts-workshop/
|
||||
│ │ ├── SearchResults.tsx
|
||||
│ │ ├── ObjectTree.tsx # containment-tree sidebar
|
||||
│ │ ├── viewers.tsx # viewer registry + default viewer
|
||||
│ │ ├── viewers/ # 3D viewers (r3f/drei/postprocessing)
|
||||
│ │ │ ├── register.ts # registers per-class 3D viewers (lazy)
|
||||
│ │ │ ├── Scene.tsx # shared Canvas: lights, controls, post
|
||||
│ │ │ ├── TileViewer.tsx
|
||||
│ │ │ ├── TokenViewer.tsx
|
||||
│ │ │ ├── CardViewer.tsx
|
||||
│ │ │ ├── CustomModelViewer.tsx
|
||||
│ │ │ └── assetUrl.ts # route asset URLs through the proxy
|
||||
│ │ ├── objectIcons.tsx # class → icon mapping
|
||||
│ │ ├── objectIconsData.ts # generated icon subset (do not edit)
|
||||
│ │ └── objectIcons.test.ts
|
||||
@@ -188,6 +197,11 @@ Hono server exposing search + fetch.
|
||||
otherwise resolves via the Steam API.
|
||||
- `GET /items/:id/file` — raw save bytes, filename from `getFileName`. Also
|
||||
accepts `fileUrl`.
|
||||
- `routes/asset.ts`
|
||||
- `GET /asset?url=...` — fetch an external asset (texture, model) and stream
|
||||
it back with a `Content-Type` header. Workshop hosts often omit CORS
|
||||
headers, which would block three.js loaders in the browser; routing through
|
||||
the proxy makes those assets loadable. Only `http(s)` URLs are allowed.
|
||||
- `routes/health.ts`
|
||||
- `GET /health` — liveness.
|
||||
- `env.ts` — zod validation of `STEAM_API_KEY`, `PORT`.
|
||||
@@ -212,6 +226,16 @@ proxy API and `packages/extract` directly for analysis.
|
||||
- `components/viewers.tsx` — viewer registry (`registerViewer` /
|
||||
`resolveViewer`) plus a `DefaultViewer` that renders an object's fields;
|
||||
custom per-class viewers can be registered later.
|
||||
- `components/viewers/` — 3D viewers built on `@react-three/fiber`,
|
||||
`@react-three/drei`, and `@react-three/postprocessing`. `register.ts`
|
||||
registers lazy-loaded viewers for `Tile`/`Custom_Tile` (flat box),
|
||||
`Custom_Token` (cylinder), `Card`/`Deck`/`Custom_Deck` (thin box with
|
||||
face/back textures), and `Custom_Model`/`Custom_Model_Bag`/
|
||||
`Custom_Model_Infinite_Bag` (GLTF/OBJ/FBX from `CustomMesh.MeshURL`).
|
||||
`Scene.tsx` is a shared canvas with lighting, orbit controls, contact
|
||||
shadows, and subtle bloom/vignette. `assetUrl.ts` routes asset URLs through
|
||||
the proxy for CORS-safe loading. The viewers are lazy-loaded so the three.js
|
||||
stack is code-split out of the main bundle.
|
||||
- `components/objectIcons.tsx` — maps TTS object classes to one or more
|
||||
Iconify icons (`iconsForObject`); unknown classes fall back to a help icon.
|
||||
Icons may come from multiple sets (mdi, material-symbols, file-icons, ...);
|
||||
@@ -234,6 +258,7 @@ proxy API and `packages/extract` directly for analysis.
|
||||
| GET | `/search?q=&page=` | Scrape Workshop browse, return item list | — |
|
||||
| GET | `/items/:id` | Full parsed `TTSMod` (`?fileUrl=` skips key) | key* |
|
||||
| GET | `/items/:id/file` | Raw save bytes, filename from header | key* |
|
||||
| GET | `/asset?url=` | CORS-safe proxy for external assets | — |
|
||||
|
||||
\* `STEAM_API_KEY` is optional; `/items/*` works without it when a `fileUrl`
|
||||
query param is supplied.
|
||||
@@ -259,6 +284,8 @@ packages/extract → flatten objects / extract refs / download assets
|
||||
- `cheerio` — Workshop browse page scraping (backend only).
|
||||
- `zod` — validation.
|
||||
- `react`, `react-dom`, `react-router-dom`, `zustand` — frontend.
|
||||
- `three`, `@react-three/fiber`, `@react-three/drei`,
|
||||
`@react-three/postprocessing` — 3D object viewers.
|
||||
- `@iconify/react`, `@iconify-json/mdi`, `@iconify/utils` — iconify icons for
|
||||
object class tags (subset bundled via `scripts/generate-object-icons.mjs`).
|
||||
- `vite`, `@vitejs/plugin-react`, `tailwindcss`, `@tailwindcss/vite` — frontend tooling.
|
||||
|
||||
@@ -31,6 +31,31 @@ export interface TTSObject {
|
||||
CustomImage?: {
|
||||
ImageURL: string;
|
||||
ImageSecondaryURL: string;
|
||||
ImageScalar?: number;
|
||||
WidthScale?: number;
|
||||
CustomTile?: {
|
||||
Type: number;
|
||||
Thickness: number;
|
||||
Stackable: boolean;
|
||||
Stretch: boolean;
|
||||
};
|
||||
CustomToken?: {
|
||||
Thickness: number;
|
||||
MergeDistancePixels: number;
|
||||
StandUp: boolean;
|
||||
Stackable: boolean;
|
||||
};
|
||||
};
|
||||
/** A custom 3D model, e.g. `Custom_Model`, `Custom_Model_Bag`. */
|
||||
CustomMesh?: {
|
||||
MeshURL: string;
|
||||
DiffuseURL: string;
|
||||
NormalURL: string;
|
||||
ColliderURL: string;
|
||||
Convex: boolean;
|
||||
MaterialIndex: number;
|
||||
TypeIndex: number;
|
||||
CastShadows: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Generated
+590
-2
@@ -54,6 +54,15 @@ importers:
|
||||
'@iconify/utils':
|
||||
specifier: ^3.1.4
|
||||
version: 3.1.4
|
||||
'@react-three/drei':
|
||||
specifier: ^10.7.8
|
||||
version: 10.7.8(@react-three/fiber@9.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(three@0.185.1))(@types/react@19.2.18)(@types/three@0.185.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(three@0.185.1)
|
||||
'@react-three/fiber':
|
||||
specifier: ^9.7.0
|
||||
version: 9.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(three@0.185.1)
|
||||
'@react-three/postprocessing':
|
||||
specifier: ^3.0.4
|
||||
version: 3.0.4(@react-three/fiber@9.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(three@0.185.1))(@types/three@0.185.4)(react@19.2.8)(three@0.185.1)
|
||||
'@tts/extract':
|
||||
specifier: workspace:*
|
||||
version: link:../../packages/extract
|
||||
@@ -69,9 +78,12 @@ importers:
|
||||
react-router-dom:
|
||||
specifier: ^7.18.2
|
||||
version: 7.18.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
|
||||
three:
|
||||
specifier: ^0.185.1
|
||||
version: 0.185.1
|
||||
zustand:
|
||||
specifier: ^5.0.14
|
||||
version: 5.0.14(@types/react@19.2.18)(react@19.2.8)
|
||||
version: 5.0.14(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8))
|
||||
devDependencies:
|
||||
'@tailwindcss/vite':
|
||||
specifier: ^4.3.3
|
||||
@@ -82,6 +94,9 @@ importers:
|
||||
'@types/react-dom':
|
||||
specifier: ^19.2.4
|
||||
version: 19.2.4(@types/react@19.2.18)
|
||||
'@types/three':
|
||||
specifier: ^0.185.4
|
||||
version: 0.185.4
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^6.0.5
|
||||
version: 6.0.5(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11))
|
||||
@@ -136,6 +151,13 @@ packages:
|
||||
'@antfu/install-pkg@1.1.0':
|
||||
resolution: {integrity: sha512-MGQsmw10ZyI+EJo45CdSER4zEb+p31LpDAFp2Z3gkSd1yqVZGi0Ebx++YTEMonJy4oChEMLsxZ64j8FH6sSqtQ==}
|
||||
|
||||
'@babel/runtime@7.29.7':
|
||||
resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
'@dimforge/rapier3d-compat@0.12.0':
|
||||
resolution: {integrity: sha512-uekIGetywIgopfD97oDL5PfeezkFpNhwlzlaEYNOA0N6ghdsOvh/HYjSMek5Q2O1PYvRSDFcqFVJl4r4ZBwOow==}
|
||||
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -328,9 +350,60 @@ packages:
|
||||
'@jridgewell/trace-mapping@0.3.31':
|
||||
resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
|
||||
|
||||
'@mediapipe/tasks-vision@0.10.17':
|
||||
resolution: {integrity: sha512-CZWV/q6TTe8ta61cZXjfnnHsfWIdFhms03M9T7Cnd5y2mdpylJM0rF1qRq+wsQVRMLz1OYPVEBU9ph2Bx8cxrg==}
|
||||
|
||||
'@monogrid/gainmap-js@3.4.0':
|
||||
resolution: {integrity: sha512-2Z0FATFHaoYJ8b+Y4y4Hgfn3FRFwuU5zRrk+9dFWp4uGAdHGqVEdP7HP+gLA3X469KXHmfupJaUbKo1b/aDKIg==}
|
||||
peerDependencies:
|
||||
three: '>= 0.159.0'
|
||||
|
||||
'@oxc-project/types@0.143.0':
|
||||
resolution: {integrity: sha512-u6JZdLBTLotrNC9Vd6vPssINdzcCzleKAH6EJKImQb7GtYvX5keN2dxkoK44stCc4tffE6QQRtZTXVSzsLUlWA==}
|
||||
|
||||
'@react-three/drei@10.7.8':
|
||||
resolution: {integrity: sha512-rJXyuzLm2Xq0kafHuR47ajDGbOe/pEhzIr4m8E8zwzQs0iNjloFDqBwRhrXmP/w+onLeYyN3EYPFW/cwWK/4yA==}
|
||||
peerDependencies:
|
||||
'@react-three/fiber': ^9.0.0
|
||||
react: ^19
|
||||
react-dom: ^19
|
||||
three: '>=0.159'
|
||||
peerDependenciesMeta:
|
||||
react-dom:
|
||||
optional: true
|
||||
|
||||
'@react-three/fiber@9.7.0':
|
||||
resolution: {integrity: sha512-EWm9FwcaOZQu/ExFW5rggoCMM1NJet5YbxVxKaOE+KSncrjU0Wx7017qSyGFvupviK89nMYGCWU3BIK4dI1clw==}
|
||||
peerDependencies:
|
||||
expo: '>=43.0'
|
||||
expo-asset: '>=8.4'
|
||||
expo-file-system: '>=11.0'
|
||||
expo-gl: '>=11.0'
|
||||
react: '>=19 <19.3'
|
||||
react-dom: '>=19 <19.3'
|
||||
react-native: '>=0.78'
|
||||
three: '>=0.156'
|
||||
peerDependenciesMeta:
|
||||
expo:
|
||||
optional: true
|
||||
expo-asset:
|
||||
optional: true
|
||||
expo-file-system:
|
||||
optional: true
|
||||
expo-gl:
|
||||
optional: true
|
||||
react-dom:
|
||||
optional: true
|
||||
react-native:
|
||||
optional: true
|
||||
|
||||
'@react-three/postprocessing@3.0.4':
|
||||
resolution: {integrity: sha512-e4+F5xtudDYvhxx3y0NtWXpZbwvQ0x1zdOXWTbXMK6fFLVDd4qucN90YaaStanZGS4Bd5siQm0lGL/5ogf8iDQ==}
|
||||
peerDependencies:
|
||||
'@react-three/fiber': ^9.0.0
|
||||
react: ^19.0
|
||||
three: '>= 0.156.0'
|
||||
|
||||
'@rolldown/binding-android-arm64@1.2.3':
|
||||
resolution: {integrity: sha512-zrJtHDcaZJ1Fp7xf4hNl+7seH9Cn/N5TwLYkhgXREtBwAd/jaqW3uqeHxpDugJLVICWg4eW44kOQEGJ1r6jCGw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -521,26 +594,57 @@ packages:
|
||||
peerDependencies:
|
||||
vite: ^5.2.0 || ^6 || ^7 || ^8
|
||||
|
||||
'@tweenjs/tween.js@23.1.3':
|
||||
resolution: {integrity: sha512-vJmvvwFxYuGnF2axRtPYocag6Clbb5YS7kLL+SO/TeVFzHqDIWrNKYtcsPMibjDx9O+bu+psAy9NKfWklassUA==}
|
||||
|
||||
'@types/chai@5.2.3':
|
||||
resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==}
|
||||
|
||||
'@types/deep-eql@4.0.2':
|
||||
resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==}
|
||||
|
||||
'@types/draco3d@1.4.10':
|
||||
resolution: {integrity: sha512-AX22jp8Y7wwaBgAixaSvkoG4M/+PlAcm3Qs4OW8yT9DM4xUpWKeFhLueTAyZF39pviAdcDdeJoACapiAceqNcw==}
|
||||
|
||||
'@types/estree@1.0.9':
|
||||
resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==}
|
||||
|
||||
'@types/node@22.20.1':
|
||||
resolution: {integrity: sha512-EANqOCF9QFyra+4pfxUcX9STKJpCLjMbObVzljIJomAWSnuSIEAvyzEU53GaajbXJEgdh0iEcPL+DGvpUd4k1Q==}
|
||||
|
||||
'@types/offscreencanvas@2019.7.3':
|
||||
resolution: {integrity: sha512-ieXiYmgSRXUDeOntE1InxjWyvEelZGP63M+cGuquuRLuIKKT1osnkXjxev9B7d1nXSug5vpunx+gNlbVxMlC9A==}
|
||||
|
||||
'@types/react-dom@19.2.4':
|
||||
resolution: {integrity: sha512-Bsc+QHgp+P/F02XDzNCY9jnZNCUuLki36KT7VKrTXXLdHf+vHMNZnW1rVu5DNW/rCK+fya3DATySbLM4yhtKUw==}
|
||||
peerDependencies:
|
||||
'@types/react': ^19.2.0
|
||||
|
||||
'@types/react-reconciler@0.28.9':
|
||||
resolution: {integrity: sha512-HHM3nxyUZ3zAylX8ZEyrDNd2XZOnQ0D5XfunJF5FLQnZbHHYq4UWvW1QfelQNXv1ICNkwYhfxjwfnqivYB6bFg==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
|
||||
'@types/react@19.2.18':
|
||||
resolution: {integrity: sha512-AnzbBERsrLKtk2XSfTbYRLjQPdy116Sty4q+T+Bp3IC4l6jNBvreVPAHmpq9qhXQM7CXZPjLVmGMw9sy+hxQ3w==}
|
||||
|
||||
'@types/stats.js@0.17.4':
|
||||
resolution: {integrity: sha512-jIBvWWShCvlBqBNIZt0KAshWpvSjhkwkEu4ZUcASoAvhmrgAUI2t1dXrjSL4xXVLB4FznPrIsX3nKXFl/Dt4vA==}
|
||||
|
||||
'@types/three@0.185.4':
|
||||
resolution: {integrity: sha512-gAsBIC07NIFrxjbf7tH2t71c38uulFfk/RFoC7FNBSjMRAQ8J1x/RBvusX0N5PJouaYFJawXQqfCQ0RKUx/1nA==}
|
||||
|
||||
'@types/webxr@0.5.24':
|
||||
resolution: {integrity: sha512-h8fgEd/DpoS9CBrjEQXR+dIDraopAEfu4wYVNY2tEPwk60stPWhvZMf4Foo5FakuQ7HFZoa8WceaWFervK2Ovg==}
|
||||
|
||||
'@use-gesture/core@10.3.1':
|
||||
resolution: {integrity: sha512-WcINiDt8WjqBdUXye25anHiNxPc0VOrlT8F6LLkU6cycrOGUDyY/yyFmsg3k8i5OLvv25llc0QC45GhR/C8llw==}
|
||||
|
||||
'@use-gesture/react@10.3.1':
|
||||
resolution: {integrity: sha512-Yy19y6O2GJq8f7CHf7L0nxL8bf4PZCPaVOCgJrusOeFHY1LvHgYXnmnXg6N5iwAnbgbZCDjo60SiM6IPJi9C5g==}
|
||||
peerDependencies:
|
||||
react: '>= 16.8.0'
|
||||
|
||||
'@vitejs/plugin-react@6.0.5':
|
||||
resolution: {integrity: sha512-BOVzne/NL162sMdResB25mUv+vWMF5NoAjNf09TeGlE7ZpszZWSD3winycicLJw72yeVsoCn/2kOhEuCvEShMA==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -587,10 +691,25 @@ packages:
|
||||
resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
base64-js@1.5.1:
|
||||
resolution: {integrity: sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==}
|
||||
|
||||
bidi-js@1.0.3:
|
||||
resolution: {integrity: sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==}
|
||||
|
||||
bson@6.10.4:
|
||||
resolution: {integrity: sha512-WIsKqkSC0ABoBJuT1LEX+2HEvNmNKKgnTAyd0fL8qzK4SH2i9NXg+t08YtdZp/V9IZ33cxe3iV4yM0qg8lMQng==}
|
||||
engines: {node: '>=16.20.1'}
|
||||
|
||||
buffer@6.0.3:
|
||||
resolution: {integrity: sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==}
|
||||
|
||||
camera-controls@3.1.2:
|
||||
resolution: {integrity: sha512-xkxfpG2ECZ6Ww5/9+kf4mfg1VEYAoe9aDSY+IwF0UEs7qEzwy0aVRfs2grImIECs/PoBtWFrh7RXsQkwG922JA==}
|
||||
engines: {node: '>=22.0.0', npm: '>=10.5.1'}
|
||||
peerDependencies:
|
||||
three: '>=0.126.1'
|
||||
|
||||
chai@6.2.2:
|
||||
resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -602,13 +721,28 @@ packages:
|
||||
resolution: {integrity: sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==}
|
||||
engines: {node: '>=18'}
|
||||
|
||||
cross-env@7.0.3:
|
||||
resolution: {integrity: sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==}
|
||||
engines: {node: '>=10.14', npm: '>=6', yarn: '>=1'}
|
||||
hasBin: true
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==}
|
||||
engines: {node: '>= 8'}
|
||||
|
||||
csstype@3.2.3:
|
||||
resolution: {integrity: sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==}
|
||||
|
||||
detect-gpu@5.0.70:
|
||||
resolution: {integrity: sha512-bqerEP1Ese6nt3rFkwPnGbsUF9a4q+gMmpTVVOEzoCyeCc+y7/RvJnQZJx1JwhgQI5Ntg0Kgat8Uu7XpBqnz1w==}
|
||||
|
||||
detect-libc@2.1.2:
|
||||
resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
draco3d@1.5.7:
|
||||
resolution: {integrity: sha512-m6WCKt/erDXcw+70IJXnG7M3awwQPAsZvJGX5zY7beBqpELw6RDGkYVU0W43AFxye4pDZ5i2Lbyc/NNGqwjUVQ==}
|
||||
|
||||
enhanced-resolve@5.24.5:
|
||||
resolution: {integrity: sha512-L1l8TNvomm6UVW5B253AGxQagSQr+vGwhMlrrfRS2qmhx46AMpMVJKQYLvWYbysTMY8VoicOvzHzoHMbyzB+4A==}
|
||||
engines: {node: '>=10.13.0'}
|
||||
@@ -637,25 +771,57 @@ packages:
|
||||
picomatch:
|
||||
optional: true
|
||||
|
||||
fflate@0.6.11:
|
||||
resolution: {integrity: sha512-3JyEFWGjFn7zHmoa9+zG1BmW7X2okcmAB+0Cnu9UFbVs/jCBnl2A8o065ZlXiw145K3eBM3uLuzrYXC0RK7eDg==}
|
||||
|
||||
fflate@0.8.3:
|
||||
resolution: {integrity: sha512-tbZNuJrLwGUp3zshBtdy4W+ORxZuIh8a5ilyIEQDC5rY1f3U20JMry0Ll3WBzU58EZKsEuJFXhb5gwv8CsPvgA==}
|
||||
|
||||
fsevents@2.3.3:
|
||||
resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==}
|
||||
engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
|
||||
os: [darwin]
|
||||
|
||||
glsl-noise@0.0.0:
|
||||
resolution: {integrity: sha512-b/ZCF6amfAUb7dJM/MxRs7AetQEahYzJ8PtgfrmEdtw6uyGOr+ZSGtgjFm6mfsBkxJ4d2W7kg+Nlqzqvn3Bc0w==}
|
||||
|
||||
graceful-fs@4.2.11:
|
||||
resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==}
|
||||
|
||||
hls.js@1.6.17:
|
||||
resolution: {integrity: sha512-NUplVGVuc1hSPwdB/9/cbRkUmLrYi75/hqiXKdA+l300pJNxDu96R7jRb2imDzWJqIUF4I5ThmAdp9GvOCXsuQ==}
|
||||
|
||||
hono@4.13.1:
|
||||
resolution: {integrity: sha512-kdJoFVv2xmayw6cY09H7AbMJMt8Jn5jdlEdXsP7AGBdF2DIptVlKlOLKXP41yPip4/a3yQPv9gVcJYI8YY04dw==}
|
||||
engines: {node: '>=16.9.0'}
|
||||
|
||||
ieee754@1.2.1:
|
||||
resolution: {integrity: sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==}
|
||||
|
||||
immediate@3.0.6:
|
||||
resolution: {integrity: sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==}
|
||||
|
||||
import-meta-resolve@4.2.0:
|
||||
resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==}
|
||||
|
||||
is-promise@2.2.2:
|
||||
resolution: {integrity: sha512-+lP4/6lKUBfQjZ2pdxThZvLUAafmZb8OAxFb8XXtiQmS35INgr85hdOGoEs124ez1FCnZJt6jau/T+alh58QFQ==}
|
||||
|
||||
isexe@2.0.0:
|
||||
resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==}
|
||||
|
||||
its-fine@2.0.0:
|
||||
resolution: {integrity: sha512-KLViCmWx94zOvpLwSlsx6yOCeMhZYaxrJV87Po5k/FoZzcPSahvK5qJ7fYhS61sZi5ikmh2S3Hz55A2l3U69ng==}
|
||||
peerDependencies:
|
||||
react: ^19.0.0
|
||||
|
||||
jiti@2.7.0:
|
||||
resolution: {integrity: sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==}
|
||||
hasBin: true
|
||||
|
||||
lie@3.3.0:
|
||||
resolution: {integrity: sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==}
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
@@ -804,9 +970,35 @@ packages:
|
||||
resolution: {integrity: sha512-WkUDrojuJs0xkgGf2udWxa3yGBRxPtxUkB79i6aCZLRgc7PM8fZe9TosfPDcvEpQZbuFASnHYmRLBLUbmLOIIA==}
|
||||
engines: {node: '>= 12.0.0'}
|
||||
|
||||
maath@0.10.8:
|
||||
resolution: {integrity: sha512-tRvbDF0Pgqz+9XUa4jjfgAQ8/aPKmQdWXilFu2tMy4GWj4NOsx99HlULO4IeREfbO3a0sA145DZYyvXPkybm0g==}
|
||||
peerDependencies:
|
||||
'@types/three': '>=0.134.0'
|
||||
three: '>=0.134.0'
|
||||
|
||||
maath@0.6.0:
|
||||
resolution: {integrity: sha512-dSb2xQuP7vDnaYqfoKzlApeRcR2xtN8/f7WV/TMAkBC8552TwTLtOO0JTcSygkYMjNDPoo6V01jTw/aPi4JrMw==}
|
||||
peerDependencies:
|
||||
'@types/three': '>=0.144.0'
|
||||
three: '>=0.144.0'
|
||||
|
||||
magic-string@0.30.21:
|
||||
resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==}
|
||||
|
||||
meshline@3.3.1:
|
||||
resolution: {integrity: sha512-/TQj+JdZkeSUOl5Mk2J7eLcYTLiQm2IDzmlSvYm7ov15anEcDJ92GHqqazxTSreeNgfnYu24kiEvvv0WlbCdFQ==}
|
||||
peerDependencies:
|
||||
three: '>=0.137'
|
||||
|
||||
meshoptimizer@1.1.1:
|
||||
resolution: {integrity: sha512-oRFNWJRDA/WTrVj7NWvqa5HqE1t9MYDj2VaWirQCzCCrAd2GHrqR/sQezCxiWATPNlKTcRaPRHPJwIRoPBAp5g==}
|
||||
|
||||
n8ao@1.10.3:
|
||||
resolution: {integrity: sha512-JFRk4WgUAUWO2KdJTqmBT02dP9kG7rE2oQbBy64E9+YwQ3d96KB9QBanqNy/NnYUfUTYU6Z/5OS5t/yLey7+6Q==}
|
||||
peerDependencies:
|
||||
postprocessing: '>=6.30.0'
|
||||
three: '>=0.137'
|
||||
|
||||
nanoid@3.3.18:
|
||||
resolution: {integrity: sha512-DTg4MJbGMWkfi6VZFdNt2/caMbQy4Ou+Op/hJQvGEWcnVfoA1QA+xzRKAzw9jD6+GVOOeYr/mIcuDSdug6F6+w==}
|
||||
engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1}
|
||||
@@ -819,6 +1011,10 @@ packages:
|
||||
package-manager-detector@1.8.0:
|
||||
resolution: {integrity: sha512-yQA4H19AmPEoMUeavPMDIe1higySl/gH/yaQrkT/s07Qp+7pp2hYz30N3z2l5BkjVkF9Ow6o0wjJamm2y7Sn0A==}
|
||||
|
||||
path-key@3.1.1:
|
||||
resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
@@ -833,6 +1029,17 @@ packages:
|
||||
resolution: {integrity: sha512-u82N74LFzG8ca+dD8puPnplTXoGH4fTPpVGuIbt36G3qvNlkvfD0lEAZSxaly3KX8TS/L1A1gsCEmvKmBcVbkQ==}
|
||||
engines: {node: ^10 || ^12 || >=14}
|
||||
|
||||
postprocessing@6.39.4:
|
||||
resolution: {integrity: sha512-oAS/PjAbc/xT3OzjUrGsorJ4J064XwhVD2t0OwKLP/E8QwDMUJ0oOv6ZI1SYMtLchv4g0ySEx+JcLy92+48vlA==}
|
||||
peerDependencies:
|
||||
three: '>= 0.168.0 < 0.186.0'
|
||||
|
||||
potpack@1.0.2:
|
||||
resolution: {integrity: sha512-choctRBIV9EMT9WGAZHn3V7t0Z2pMQyl0EZE6pFc/6ml3ssw7Dlf/oAOvFwjm1HVsqfQN8GfeFyJ+d8tRzqueQ==}
|
||||
|
||||
promise-worker-transferable@1.0.4:
|
||||
resolution: {integrity: sha512-bN+0ehEnrXfxV2ZQvU2PetO0n4gqBD4ulq3MI1WOPLgr7/Mg9yRQkX5+0v1vagr74ZTsl7XtzlaYDo2EuCeYJw==}
|
||||
|
||||
react-dom@19.2.8:
|
||||
resolution: {integrity: sha512-rVprimfGBG3DR+Tq0IQG2DT5PxKth1WIGDmj5yPmlzr4YBe7uyE+Du4oVqTDXZSHGGGXRtTJEGSSePyQCMBglQ==}
|
||||
peerDependencies:
|
||||
@@ -855,10 +1062,23 @@ packages:
|
||||
react-dom:
|
||||
optional: true
|
||||
|
||||
react-use-measure@2.1.7:
|
||||
resolution: {integrity: sha512-KrvcAo13I/60HpwGO5jpW7E9DfusKyLPLvuHlUyP5zqnmAPhNc6qTRjUQrdTADl0lpPpDVU2/Gg51UlOGHXbdg==}
|
||||
peerDependencies:
|
||||
react: '>=16.13'
|
||||
react-dom: '>=16.13'
|
||||
peerDependenciesMeta:
|
||||
react-dom:
|
||||
optional: true
|
||||
|
||||
react@19.2.8:
|
||||
resolution: {integrity: sha512-PWaYA1L/q9u2u7xYQi+Y3L3Yfnie7XyLeaJICV1MGD6LprsBxcAqGjYyr0eY3p+QdsA+x/Irkt4Qif8D63+Sbw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
require-from-string@2.0.2:
|
||||
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
rolldown@1.2.3:
|
||||
resolution: {integrity: sha512-rn9wpmxplLf7NLNyCk9FyWh3FM43DbY8jOzCdEPzH7uflhTftRbCEpqi6Ly2osgoU8OwObtmavMbWLaWy4LX7A==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -870,6 +1090,14 @@ packages:
|
||||
set-cookie-parser@2.7.2:
|
||||
resolution: {integrity: sha512-oeM1lpU/UvhTxw+g3cIfxXHyJRc/uidd3yK1P242gzHds0udQBYzs3y8j4gCCW+ZJ7ad0yctld8RYO+bdurlvw==}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
shebang-regex@3.0.0:
|
||||
resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==}
|
||||
engines: {node: '>=8'}
|
||||
|
||||
siginfo@2.0.0:
|
||||
resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==}
|
||||
|
||||
@@ -880,9 +1108,23 @@ packages:
|
||||
stackback@0.0.2:
|
||||
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||
|
||||
stats-gl@2.4.2:
|
||||
resolution: {integrity: sha512-g5O9B0hm9CvnM36+v7SFl39T7hmAlv541tU81ME8YeSb3i1CIP5/QdDeSB3A0la0bKNHpxpwxOVRo2wFTYEosQ==}
|
||||
peerDependencies:
|
||||
'@types/three': '*'
|
||||
three: '*'
|
||||
|
||||
stats.js@0.17.0:
|
||||
resolution: {integrity: sha512-hNKz8phvYLPEcRkeG1rsGmV5ChMjKDAWU7/OJJdDErPBNChQXxCo3WZurGpnWc6gZhAzEPFad1aVgyOANH1sMw==}
|
||||
|
||||
std-env@4.2.0:
|
||||
resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==}
|
||||
|
||||
suspend-react@0.1.3:
|
||||
resolution: {integrity: sha512-aqldKgX9aZqpoDp3e8/BZ8Dm7x1pJl+qI3ZKxDN0i/IQTWUwBx/ManmlVJ3wowqbno6c2bmiIfs+Um6LbsjJyQ==}
|
||||
peerDependencies:
|
||||
react: '>=17.0'
|
||||
|
||||
tailwindcss@4.3.3:
|
||||
resolution: {integrity: sha512-gOhV3P7ufE62QDGg1zVaTgCR+EtPv92k2nIhVcVKcLmxT1sUBsQGhnZj175j+MqRt4zLF7ic+sCYjfhxMxj7YQ==}
|
||||
|
||||
@@ -890,6 +1132,19 @@ packages:
|
||||
resolution: {integrity: sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
three-mesh-bvh@0.8.3:
|
||||
resolution: {integrity: sha512-4G5lBaF+g2auKX3P0yqx+MJC6oVt6sB5k+CchS6Ob0qvH0YIhuUk1eYr7ktsIpY+albCqE80/FVQGV190PmiAg==}
|
||||
peerDependencies:
|
||||
three: '>= 0.159.0'
|
||||
|
||||
three-stdlib@2.36.1:
|
||||
resolution: {integrity: sha512-XyGQrFmNQ5O/IoKm556ftwKsBg11TIb301MB5dWNicziQBEs2g3gtOYIf7pFiLa0zI2gUwhtCjv9fmjnxKZ1Cg==}
|
||||
peerDependencies:
|
||||
three: '>=0.128.0'
|
||||
|
||||
three@0.185.1:
|
||||
resolution: {integrity: sha512-5aojFCXKwnjBRZvUnt3WFfEcvUJgkN5LlijRFN95hMy8WVkG4I0QNcJE+OuWvuJ0bOdStrbfXn0pkd6/QyiAlg==}
|
||||
|
||||
tinybench@2.9.0:
|
||||
resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==}
|
||||
|
||||
@@ -905,11 +1160,27 @@ packages:
|
||||
resolution: {integrity: sha512-yau8yJdTt989Mm0Bd/236QnzEiPf2xLLTqUZRUJOo/3CB078LSwzei343DgtJVmfJKJE3TMINY1u42SQsP6mXw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
|
||||
troika-three-text@0.52.5:
|
||||
resolution: {integrity: sha512-Ry3jRhic9pzcY4JduSvRRyDmVOSqEW19gT4vtK+aCiPNVcDlmkxvGG0YbFd36RTDq1wExOupXnvNF/j1oiHHDA==}
|
||||
peerDependencies:
|
||||
three: '>=0.125.0'
|
||||
|
||||
troika-three-utils@0.52.5:
|
||||
resolution: {integrity: sha512-WsePbcX8RtfidRfsxK1eCZCjF81ZDzAKHH/evLs0hdV2wpoCb0vArGZHdzdOJrSS3k4zfdtbKDaBh8+phkrYnw==}
|
||||
peerDependencies:
|
||||
three: '>=0.125.0'
|
||||
|
||||
troika-worker-utils@0.52.0:
|
||||
resolution: {integrity: sha512-W1CpvTHykaPH5brv5VHLfQo9D1OYuo0cSBEUQFFT/nBUzM8iD6Lq2/tgG/f1OelbAS1WtaTPQzE5uM49egnngw==}
|
||||
|
||||
tsx@4.23.11:
|
||||
resolution: {integrity: sha512-Ry2oTEUnhBdeEdWIztY8kf3/nBGnPnjMLVGL0YfdRXMORuPER5NlKmayqxtxRxwB1xBN+RivRaJfe7PM1rtiyw==}
|
||||
engines: {node: '>=18.0.0'}
|
||||
hasBin: true
|
||||
|
||||
tunnel-rat@0.1.2:
|
||||
resolution: {integrity: sha512-lR5VHmkPhzdhrM092lI2nACsLO4QubF0/yoOhzX7c+wIpbN1GjHNzCc91QlpxBi+cnx8vVJ+Ur6vL5cEoQPFpQ==}
|
||||
|
||||
typescript@5.9.3:
|
||||
resolution: {integrity: sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==}
|
||||
engines: {node: '>=14.17'}
|
||||
@@ -918,6 +1189,15 @@ packages:
|
||||
undici-types@6.21.0:
|
||||
resolution: {integrity: sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==}
|
||||
|
||||
use-sync-external-store@1.6.0:
|
||||
resolution: {integrity: sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==}
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
|
||||
utility-types@3.11.0:
|
||||
resolution: {integrity: sha512-6Z7Ma2aVEWisaL6TvBCy7P8rm2LQoPv6dJ7ecIaIixHcwfbJ0x7mWdbcwlIM5IGQxPZSFYeqRCqlOOeKoJYMkw==}
|
||||
engines: {node: '>= 4'}
|
||||
|
||||
vite@8.2.1:
|
||||
resolution: {integrity: sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==}
|
||||
engines: {node: ^20.19.0 || >=22.12.0}
|
||||
@@ -1002,6 +1282,17 @@ packages:
|
||||
jsdom:
|
||||
optional: true
|
||||
|
||||
webgl-constants@1.1.1:
|
||||
resolution: {integrity: sha512-LkBXKjU5r9vAW7Gcu3T5u+5cvSvh5WwINdr0C+9jpzVB41cjQAP5ePArDtk/WHYdVj0GefCgM73BA7FlIiNtdg==}
|
||||
|
||||
webgl-sdf-generator@1.1.1:
|
||||
resolution: {integrity: sha512-9Z0JcMTFxeE+b2x1LJTdnaT8rT8aEp7MVxkNwoycNmJWwPdzoXzMh0BjJSh/AEFP+KPYZUli814h8bJZFIZ2jA==}
|
||||
|
||||
which@2.0.2:
|
||||
resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==}
|
||||
engines: {node: '>= 8'}
|
||||
hasBin: true
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==}
|
||||
engines: {node: '>=8'}
|
||||
@@ -1010,6 +1301,21 @@ packages:
|
||||
zod@3.25.76:
|
||||
resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==}
|
||||
|
||||
zustand@4.5.7:
|
||||
resolution: {integrity: sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw==}
|
||||
engines: {node: '>=12.7.0'}
|
||||
peerDependencies:
|
||||
'@types/react': '>=16.8'
|
||||
immer: '>=9.0.6'
|
||||
react: '>=16.8'
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
immer:
|
||||
optional: true
|
||||
react:
|
||||
optional: true
|
||||
|
||||
zustand@5.0.14:
|
||||
resolution: {integrity: sha512-/8tAspM5LMPr28b3fwLYrtdj77ECpfZviaP75CMTnwO8ISyaE4GDIG/9rDDYq/cH9D2Xw2A2RXglLInmVBQB/g==}
|
||||
engines: {node: '>=12.20.0'}
|
||||
@@ -1035,6 +1341,10 @@ snapshots:
|
||||
package-manager-detector: 1.8.0
|
||||
tinyexec: 1.3.0
|
||||
|
||||
'@babel/runtime@7.29.7': {}
|
||||
|
||||
'@dimforge/rapier3d-compat@0.12.0': {}
|
||||
|
||||
'@esbuild/aix-ppc64@0.28.1':
|
||||
optional: true
|
||||
|
||||
@@ -1153,8 +1463,79 @@ snapshots:
|
||||
'@jridgewell/resolve-uri': 3.1.2
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
'@mediapipe/tasks-vision@0.10.17': {}
|
||||
|
||||
'@monogrid/gainmap-js@3.4.0(three@0.185.1)':
|
||||
dependencies:
|
||||
promise-worker-transferable: 1.0.4
|
||||
three: 0.185.1
|
||||
|
||||
'@oxc-project/types@0.143.0': {}
|
||||
|
||||
'@react-three/drei@10.7.8(@react-three/fiber@9.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(three@0.185.1))(@types/react@19.2.18)(@types/three@0.185.4)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(three@0.185.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.29.7
|
||||
'@mediapipe/tasks-vision': 0.10.17
|
||||
'@monogrid/gainmap-js': 3.4.0(three@0.185.1)
|
||||
'@react-three/fiber': 9.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(three@0.185.1)
|
||||
'@use-gesture/react': 10.3.1(react@19.2.8)
|
||||
camera-controls: 3.1.2(three@0.185.1)
|
||||
cross-env: 7.0.3
|
||||
detect-gpu: 5.0.70
|
||||
glsl-noise: 0.0.0
|
||||
hls.js: 1.6.17
|
||||
maath: 0.10.8(@types/three@0.185.4)(three@0.185.1)
|
||||
meshline: 3.3.1(three@0.185.1)
|
||||
react: 19.2.8
|
||||
stats-gl: 2.4.2(@types/three@0.185.4)(three@0.185.1)
|
||||
stats.js: 0.17.0
|
||||
suspend-react: 0.1.3(react@19.2.8)
|
||||
three: 0.185.1
|
||||
three-mesh-bvh: 0.8.3(three@0.185.1)
|
||||
three-stdlib: 2.36.1(three@0.185.1)
|
||||
troika-three-text: 0.52.5(three@0.185.1)
|
||||
tunnel-rat: 0.1.2(@types/react@19.2.18)(react@19.2.8)
|
||||
use-sync-external-store: 1.6.0(react@19.2.8)
|
||||
utility-types: 3.11.0
|
||||
zustand: 5.0.14(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8))
|
||||
optionalDependencies:
|
||||
react-dom: 19.2.8(react@19.2.8)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- '@types/three'
|
||||
- immer
|
||||
|
||||
'@react-three/fiber@9.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(three@0.185.1)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.29.7
|
||||
'@types/webxr': 0.5.24
|
||||
base64-js: 1.5.1
|
||||
buffer: 6.0.3
|
||||
its-fine: 2.0.0(@types/react@19.2.18)(react@19.2.8)
|
||||
react: 19.2.8
|
||||
react-use-measure: 2.1.7(react-dom@19.2.8(react@19.2.8))(react@19.2.8)
|
||||
scheduler: 0.27.0
|
||||
suspend-react: 0.1.3(react@19.2.8)
|
||||
three: 0.185.1
|
||||
use-sync-external-store: 1.6.0(react@19.2.8)
|
||||
zustand: 5.0.14(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8))
|
||||
optionalDependencies:
|
||||
react-dom: 19.2.8(react@19.2.8)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
|
||||
'@react-three/postprocessing@3.0.4(@react-three/fiber@9.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(three@0.185.1))(@types/three@0.185.4)(react@19.2.8)(three@0.185.1)':
|
||||
dependencies:
|
||||
'@react-three/fiber': 9.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)(three@0.185.1)
|
||||
maath: 0.6.0(@types/three@0.185.4)(three@0.185.1)
|
||||
n8ao: 1.10.3(postprocessing@6.39.4(three@0.185.1))(three@0.185.1)
|
||||
postprocessing: 6.39.4(three@0.185.1)
|
||||
react: 19.2.8
|
||||
three: 0.185.1
|
||||
transitivePeerDependencies:
|
||||
- '@types/three'
|
||||
|
||||
'@rolldown/binding-android-arm64@1.2.3':
|
||||
optional: true
|
||||
|
||||
@@ -1269,6 +1650,8 @@ snapshots:
|
||||
tailwindcss: 4.3.3
|
||||
vite: 8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11)
|
||||
|
||||
'@tweenjs/tween.js@23.1.3': {}
|
||||
|
||||
'@types/chai@5.2.3':
|
||||
dependencies:
|
||||
'@types/deep-eql': 4.0.2
|
||||
@@ -1276,20 +1659,48 @@ snapshots:
|
||||
|
||||
'@types/deep-eql@4.0.2': {}
|
||||
|
||||
'@types/draco3d@1.4.10': {}
|
||||
|
||||
'@types/estree@1.0.9': {}
|
||||
|
||||
'@types/node@22.20.1':
|
||||
dependencies:
|
||||
undici-types: 6.21.0
|
||||
|
||||
'@types/offscreencanvas@2019.7.3': {}
|
||||
|
||||
'@types/react-dom@19.2.4(@types/react@19.2.18)':
|
||||
dependencies:
|
||||
'@types/react': 19.2.18
|
||||
|
||||
'@types/react-reconciler@0.28.9(@types/react@19.2.18)':
|
||||
dependencies:
|
||||
'@types/react': 19.2.18
|
||||
|
||||
'@types/react@19.2.18':
|
||||
dependencies:
|
||||
csstype: 3.2.3
|
||||
|
||||
'@types/stats.js@0.17.4': {}
|
||||
|
||||
'@types/three@0.185.4':
|
||||
dependencies:
|
||||
'@dimforge/rapier3d-compat': 0.12.0
|
||||
'@tweenjs/tween.js': 23.1.3
|
||||
'@types/stats.js': 0.17.4
|
||||
'@types/webxr': 0.5.24
|
||||
fflate: 0.8.3
|
||||
meshoptimizer: 1.1.1
|
||||
|
||||
'@types/webxr@0.5.24': {}
|
||||
|
||||
'@use-gesture/core@10.3.1': {}
|
||||
|
||||
'@use-gesture/react@10.3.1(react@19.2.8)':
|
||||
dependencies:
|
||||
'@use-gesture/core': 10.3.1
|
||||
react: 19.2.8
|
||||
|
||||
'@vitejs/plugin-react@6.0.5(vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11))':
|
||||
dependencies:
|
||||
'@rolldown/pluginutils': 1.0.1
|
||||
@@ -1338,18 +1749,49 @@ snapshots:
|
||||
|
||||
assertion-error@2.0.1: {}
|
||||
|
||||
base64-js@1.5.1: {}
|
||||
|
||||
bidi-js@1.0.3:
|
||||
dependencies:
|
||||
require-from-string: 2.0.2
|
||||
|
||||
bson@6.10.4: {}
|
||||
|
||||
buffer@6.0.3:
|
||||
dependencies:
|
||||
base64-js: 1.5.1
|
||||
ieee754: 1.2.1
|
||||
|
||||
camera-controls@3.1.2(three@0.185.1):
|
||||
dependencies:
|
||||
three: 0.185.1
|
||||
|
||||
chai@6.2.2: {}
|
||||
|
||||
convert-source-map@2.0.0: {}
|
||||
|
||||
cookie@1.1.1: {}
|
||||
|
||||
cross-env@7.0.3:
|
||||
dependencies:
|
||||
cross-spawn: 7.0.6
|
||||
|
||||
cross-spawn@7.0.6:
|
||||
dependencies:
|
||||
path-key: 3.1.1
|
||||
shebang-command: 2.0.0
|
||||
which: 2.0.2
|
||||
|
||||
csstype@3.2.3: {}
|
||||
|
||||
detect-gpu@5.0.70:
|
||||
dependencies:
|
||||
webgl-constants: 1.1.1
|
||||
|
||||
detect-libc@2.1.2: {}
|
||||
|
||||
draco3d@1.5.7: {}
|
||||
|
||||
enhanced-resolve@5.24.5:
|
||||
dependencies:
|
||||
graceful-fs: 4.2.11
|
||||
@@ -1396,17 +1838,44 @@ snapshots:
|
||||
optionalDependencies:
|
||||
picomatch: 4.0.5
|
||||
|
||||
fflate@0.6.11: {}
|
||||
|
||||
fflate@0.8.3: {}
|
||||
|
||||
fsevents@2.3.3:
|
||||
optional: true
|
||||
|
||||
glsl-noise@0.0.0: {}
|
||||
|
||||
graceful-fs@4.2.11: {}
|
||||
|
||||
hls.js@1.6.17: {}
|
||||
|
||||
hono@4.13.1: {}
|
||||
|
||||
ieee754@1.2.1: {}
|
||||
|
||||
immediate@3.0.6: {}
|
||||
|
||||
import-meta-resolve@4.2.0: {}
|
||||
|
||||
is-promise@2.2.2: {}
|
||||
|
||||
isexe@2.0.0: {}
|
||||
|
||||
its-fine@2.0.0(@types/react@19.2.18)(react@19.2.8):
|
||||
dependencies:
|
||||
'@types/react-reconciler': 0.28.9(@types/react@19.2.18)
|
||||
react: 19.2.8
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
|
||||
jiti@2.7.0: {}
|
||||
|
||||
lie@3.3.0:
|
||||
dependencies:
|
||||
immediate: 3.0.6
|
||||
|
||||
lightningcss-android-arm64@1.32.0:
|
||||
optional: true
|
||||
|
||||
@@ -1505,16 +1974,39 @@ snapshots:
|
||||
lightningcss-win32-arm64-msvc: 1.33.0
|
||||
lightningcss-win32-x64-msvc: 1.33.0
|
||||
|
||||
maath@0.10.8(@types/three@0.185.4)(three@0.185.1):
|
||||
dependencies:
|
||||
'@types/three': 0.185.4
|
||||
three: 0.185.1
|
||||
|
||||
maath@0.6.0(@types/three@0.185.4)(three@0.185.1):
|
||||
dependencies:
|
||||
'@types/three': 0.185.4
|
||||
three: 0.185.1
|
||||
|
||||
magic-string@0.30.21:
|
||||
dependencies:
|
||||
'@jridgewell/sourcemap-codec': 1.5.5
|
||||
|
||||
meshline@3.3.1(three@0.185.1):
|
||||
dependencies:
|
||||
three: 0.185.1
|
||||
|
||||
meshoptimizer@1.1.1: {}
|
||||
|
||||
n8ao@1.10.3(postprocessing@6.39.4(three@0.185.1))(three@0.185.1):
|
||||
dependencies:
|
||||
postprocessing: 6.39.4(three@0.185.1)
|
||||
three: 0.185.1
|
||||
|
||||
nanoid@3.3.18: {}
|
||||
|
||||
obug@2.1.4: {}
|
||||
|
||||
package-manager-detector@1.8.0: {}
|
||||
|
||||
path-key@3.1.1: {}
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
@@ -1527,6 +2019,17 @@ snapshots:
|
||||
picocolors: 1.1.1
|
||||
source-map-js: 1.2.1
|
||||
|
||||
postprocessing@6.39.4(three@0.185.1):
|
||||
dependencies:
|
||||
three: 0.185.1
|
||||
|
||||
potpack@1.0.2: {}
|
||||
|
||||
promise-worker-transferable@1.0.4:
|
||||
dependencies:
|
||||
is-promise: 2.2.2
|
||||
lie: 3.3.0
|
||||
|
||||
react-dom@19.2.8(react@19.2.8):
|
||||
dependencies:
|
||||
react: 19.2.8
|
||||
@@ -1546,8 +2049,16 @@ snapshots:
|
||||
optionalDependencies:
|
||||
react-dom: 19.2.8(react@19.2.8)
|
||||
|
||||
react-use-measure@2.1.7(react-dom@19.2.8(react@19.2.8))(react@19.2.8):
|
||||
dependencies:
|
||||
react: 19.2.8
|
||||
optionalDependencies:
|
||||
react-dom: 19.2.8(react@19.2.8)
|
||||
|
||||
react@19.2.8: {}
|
||||
|
||||
require-from-string@2.0.2: {}
|
||||
|
||||
rolldown@1.2.3:
|
||||
dependencies:
|
||||
'@oxc-project/types': 0.143.0
|
||||
@@ -1572,18 +2083,51 @@ snapshots:
|
||||
|
||||
set-cookie-parser@2.7.2: {}
|
||||
|
||||
shebang-command@2.0.0:
|
||||
dependencies:
|
||||
shebang-regex: 3.0.0
|
||||
|
||||
shebang-regex@3.0.0: {}
|
||||
|
||||
siginfo@2.0.0: {}
|
||||
|
||||
source-map-js@1.2.1: {}
|
||||
|
||||
stackback@0.0.2: {}
|
||||
|
||||
stats-gl@2.4.2(@types/three@0.185.4)(three@0.185.1):
|
||||
dependencies:
|
||||
'@types/three': 0.185.4
|
||||
three: 0.185.1
|
||||
|
||||
stats.js@0.17.0: {}
|
||||
|
||||
std-env@4.2.0: {}
|
||||
|
||||
suspend-react@0.1.3(react@19.2.8):
|
||||
dependencies:
|
||||
react: 19.2.8
|
||||
|
||||
tailwindcss@4.3.3: {}
|
||||
|
||||
tapable@2.3.3: {}
|
||||
|
||||
three-mesh-bvh@0.8.3(three@0.185.1):
|
||||
dependencies:
|
||||
three: 0.185.1
|
||||
|
||||
three-stdlib@2.36.1(three@0.185.1):
|
||||
dependencies:
|
||||
'@types/draco3d': 1.4.10
|
||||
'@types/offscreencanvas': 2019.7.3
|
||||
'@types/webxr': 0.5.24
|
||||
draco3d: 1.5.7
|
||||
fflate: 0.6.11
|
||||
potpack: 1.0.2
|
||||
three: 0.185.1
|
||||
|
||||
three@0.185.1: {}
|
||||
|
||||
tinybench@2.9.0: {}
|
||||
|
||||
tinyexec@1.3.0: {}
|
||||
@@ -1595,16 +2139,44 @@ snapshots:
|
||||
|
||||
tinyrainbow@3.1.1: {}
|
||||
|
||||
troika-three-text@0.52.5(three@0.185.1):
|
||||
dependencies:
|
||||
bidi-js: 1.0.3
|
||||
three: 0.185.1
|
||||
troika-three-utils: 0.52.5(three@0.185.1)
|
||||
troika-worker-utils: 0.52.0
|
||||
webgl-sdf-generator: 1.1.1
|
||||
|
||||
troika-three-utils@0.52.5(three@0.185.1):
|
||||
dependencies:
|
||||
three: 0.185.1
|
||||
|
||||
troika-worker-utils@0.52.0: {}
|
||||
|
||||
tsx@4.23.11:
|
||||
dependencies:
|
||||
esbuild: 0.28.1
|
||||
optionalDependencies:
|
||||
fsevents: 2.3.3
|
||||
|
||||
tunnel-rat@0.1.2(@types/react@19.2.18)(react@19.2.8):
|
||||
dependencies:
|
||||
zustand: 4.5.7(@types/react@19.2.18)(react@19.2.8)
|
||||
transitivePeerDependencies:
|
||||
- '@types/react'
|
||||
- immer
|
||||
- react
|
||||
|
||||
typescript@5.9.3: {}
|
||||
|
||||
undici-types@6.21.0: {}
|
||||
|
||||
use-sync-external-store@1.6.0(react@19.2.8):
|
||||
dependencies:
|
||||
react: 19.2.8
|
||||
|
||||
utility-types@3.11.0: {}
|
||||
|
||||
vite@8.2.1(@types/node@22.20.1)(esbuild@0.28.1)(jiti@2.7.0)(tsx@4.23.11):
|
||||
dependencies:
|
||||
lightningcss: 1.33.0
|
||||
@@ -1646,6 +2218,14 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- msw
|
||||
|
||||
webgl-constants@1.1.1: {}
|
||||
|
||||
webgl-sdf-generator@1.1.1: {}
|
||||
|
||||
which@2.0.2:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
why-is-node-running@2.3.0:
|
||||
dependencies:
|
||||
siginfo: 2.0.0
|
||||
@@ -1653,7 +2233,15 @@ snapshots:
|
||||
|
||||
zod@3.25.76: {}
|
||||
|
||||
zustand@5.0.14(@types/react@19.2.18)(react@19.2.8):
|
||||
zustand@4.5.7(@types/react@19.2.18)(react@19.2.8):
|
||||
dependencies:
|
||||
use-sync-external-store: 1.6.0(react@19.2.8)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.18
|
||||
react: 19.2.8
|
||||
|
||||
zustand@5.0.14(@types/react@19.2.18)(react@19.2.8)(use-sync-external-store@1.6.0(react@19.2.8)):
|
||||
optionalDependencies:
|
||||
'@types/react': 19.2.18
|
||||
react: 19.2.8
|
||||
use-sync-external-store: 1.6.0(react@19.2.8)
|
||||
|
||||
Reference in New Issue
Block a user