Refactor `TokenMesh` to use a custom `useTrace` hook that suspends
during the alpha channel tracing process. This allows the `Bounds`
component in `Scene` to correctly fit the camera to the token's
geometry once it has been fully loaded and traced.
Cards in a deck share the deck's GUID, so GUID-based selection highlighted
every card with that GUID and rendered the first match. Key selection by the
node's unique index path instead.
Split extrudeShapeParts into front, back, and walls so each face can carry
its own material. Flip back textures left/right on the material so they are
not mirrored, and slice card faces from the deck sprite sheet via CardID.
Resolve deck config from the parent deck, which is authoritative over a
card's own CustomDeck. Add unit tests for card resolution, sprite UVs, and
the flip helper.
TokenViewer now traces the image's alpha channel via /trace and extrudes the silhouette with @tts/mesh, aligning the texture using the full image as the UV framing. The texture is applied to the whole mesh (top, back, and walls) instead of separate solid-white walls.
Replace the per-shape tile geometries with @tts/mesh extrusion, honoring
the tile type and stretch aspect ratio. Add extrudeShapeParts so the
caps carry the texture while the walls are solid white, matching TTS
tinting. Fix bottom-face UVs to mirror horizontally and make wall UVs
inherit the front mapping independent of z.
Detect the model format from file contents instead of the URL extension,
since TTS model URLs are often extension-less. Move the diffuse texture
hook into a child component so the hook count stays consistent across
renders.
Move useTexture calls in the tile, token, and card viewers into child
components rendered inside the Canvas. R3F hooks must run within the
Canvas store, so calling them at the viewer's top level threw a runtime
error.
Wrap the resolved viewer in an error boundary so a render failure (e.g.
WebGL context loss or a model/texture load error) is contained to the
inspector pane instead of crashing the page. The fallback shows the
error message and stack trace.
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.
Replace the text class tag in the tree and inspector with one or more
MDI icons (hover shows the class name). Bundle only the used icons via
a generator script instead of the full set.
Key tree nodes by their index path instead of GUID, since TTS saves can
contain duplicate GUIDs. Skip the Parent back-reference in the default
viewer and stringify with a circular-reference guard.
Add a containment-tree sidebar with class tags and display labels, plus
an inspector pane backed by a viewer registry with a default viewer.
Custom per-class viewers can be registered later.
Add fetchModFromUrl/fetchModFileFromUrl and accept a fileUrl query param on /items routes so the frontend can download saves directly from a search result's file_url, with no key required.
Add apps/web (Vite + React Router + Tailwind v4 + Zustand) with a search page and a mod page that analyzes saves via @tts/extract. Document the frontend in the README and docs.