Files
tts-workshop/docs/architecture.md
T
hypercross 9094ad58e0 feat(proxy): add offset param to inset or outset traced shapes
Trace results can now be inset (negative) or outset (positive) by a
pixel amount. Offset the outline and holes in opposite directions with
clipper-lib's miter joins, then recombine with a boolean difference so
holes grow on inset and shrink on outset. Collapsed shapes return an
empty outline; a split outline keeps the largest ring. Document the
parameter and the new dependency.
2026-08-08 15:37:27 +08:00

6.4 KiB

Architecture & Dependencies

Scope: The system's architecture and dependency graph. For implementation details (files, endpoints, build order), see implementation-plan.md. For the rationale behind key decisions, see decisions.md.

Overview

A lightweight, client-only pnpm monorepo that lets a user search the Tabletop Simulator Steam Workshop, fetch full TTS save files, and analyze their contents. It is split into four packages with a strict layering: a thin HTTP proxy on top, a low-level fetcher, an isomorphic analysis layer, and a shared types/validation package.

Design principles

  • Client-only & lightweight — no caching layer, no shared server state.
  • Fetch vs analyze separationpackages/tts only fetches and parses; packages/extract only analyzes. Neither depends on the other's concerns.
  • Isomorphic analysispackages/extract runs in browser and Node, using only fetch, Blob, and typed arrays (no Buffer, no Node-only packages).
  • Thin proxy — the HTTP layer exposes search and fetch only; traversal is intentionally not exposed as endpoints.

Package responsibilities

Package Role Runtime
apps/web React frontend: search + mod pages Browser
apps/proxy Hono HTTP server: search + fetch endpoints Node
packages/tts Fetch save from Steam, BSON-parse to TTSMod Node
packages/extract Analyze a TTSMod: objects, refs, assets Isomorphic
packages/mesh 2D shapes + extrusion into 3D mesh geometry Isomorphic
packages/shared Shared types + zod schemas Isomorphic

Dependency graph

apps/web ──► apps/proxy ──► packages/tts ──► packages/shared
      │            │            │
      │            │            └──► (fetchMod → TTSMod)
      │            ▼
      ├──► packages/mesh ──► packages/shared (types)
      └──► packages/extract ──► packages/shared (types)

Edges

  • apps/webapps/proxy — calls /search, /items/:id, /items/:id/file, /asset (CORS-safe asset proxy), and /trace (image → vector shape) over HTTP.
  • apps/webpackages/extract — uses buildTree / collectRefs to analyze a loaded TTSMod in the browser (tree sidebar + asset refs).
  • apps/webpackages/mesh — extrudes 2D shapes into 3D geometry for the tile and token viewers.
  • apps/proxypackages/tts — calls fetchMod / getFileName to serve item requests.
  • apps/proxypackages/shared — uses shared types and zod schemas for request/response validation.
  • packages/ttspackages/shared — consumes TTSMod / TTSObject types.
  • packages/extractpackages/tts — reuses traverseMod (traversal logic lives in extract; see note below).
  • packages/extractpackages/shared — consumes shared types.

Note on traverseMod: traversal is analysis, so it lives in packages/extract. packages/tts is fetch-only. The graph edge extract → tts reflects that extract imports the traversal helper that was originally authored alongside the fetcher; tts does not depend on extract.

Layering rules

  • No upward dependenciespackages/* never import apps/*.
  • No sibling coupling beyond the graph aboveextract and tts do not depend on each other's analysis/fetch concerns.
  • packages/shared is the leaf — everything depends on it; it depends on nothing internal.

External dependencies

Package Purpose Used by
hono HTTP framework apps/proxy
@hono/node-server Node adapter for Hono apps/proxy
@hono/cors CORS middleware apps/proxy
bson BSON deserialization of TTS save files packages/tts
@visioncortex/vtracer Raster-to-SVG vectorization (wasm) apps/proxy
clipper-lib Polygon offsetting (inset/outset) for traced shapes apps/proxy
sharp Image decoding to RGBA apps/proxy
svgpath SVG path parsing for traced shapes apps/proxy
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

  • cheerio is backend-only — it never appears in packages/extract, which must stay isomorphic.
  • bson is Node-only — used by the fetcher and the trace route, not the analysis layer.
  • @visioncortex/vtracer, sharp, svgpath, clipper-lib are backend-only — the trace route lives in apps/proxy; they never appear in packages/extract, which must stay isomorphic.
  • packages/extract has zero external runtime deps — it relies only on platform fetch / Blob, keeping it portable to a future frontend.

Tooling dependencies

  • typescript (strict), tsx (dev runner), eslint, prettier.
  • vitest (unit tests), colocated as *.test.ts next to sources.
  • vite, @vitejs/plugin-react, tailwindcss, @tailwindcss/vite — frontend build/dev tooling.
  • pnpm workspaces for package management.

Deployment / runtime shape

  • 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, /health, /asset, and /trace 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.