A demo asset-library site for stop-motion puppet production. Pick a character, browse their swappable printed parts (Laika-style upper/lower face plates), and open a 3D viewer showing model status, print counts, and which prints are checked out where. 3D models are streamed via Miris.
new-cydonia/
├── index.html # HTML shell; loads the Miris SDK <script> tag
├── vite.config.ts
├── tsconfig.json / tsconfig.app.json / tsconfig.node.json
├── package.json
└── src/
├── main.tsx # React entry point
├── App.tsx # Top-level state: active character, search, open viewer
├── types.ts # Shared domain types (Asset, Character, Note, Checkout…)
├── vite-env.d.ts # Vite client type references
├── miris-elements.d.ts # JSX typings for the Miris SDK's custom elements
├── config/
│ ├── miris.ts # Viewer key + live/offline streaming helpers
│ ├── miris-secrets.example.json # Template, copy to miris-secrets.json
│ └── miris-secrets.json # Gitignored; your actual viewer key
├── data/
│ └── characters.ts # Character + swappable-part sample data
├── components/
│ ├── TopBar.tsx
│ ├── StreamStatusPill.tsx
│ ├── Sidebar.tsx
│ ├── CharacterView.tsx
│ ├── AssetCard.tsx
│ ├── SearchResults.tsx
│ ├── ViewerModal.tsx
│ ├── MirisStage.tsx # The 3D preview pane (live Miris stream or offline turntable)
│ └── ViewerDetails.tsx # Status / print counts / checkout list / metadata panel
└── styles/
├── index.css # imports the four files below
├── tokens.css # CSS variables, resets, base type
├── layout.css # app shell, topbar, sidebar, main column
├── cards.css # asset grid + card styling
└── viewer.css # viewer modal + 3D stage styling
This is a TypeScript project, npm run build type-checks (tsc -b) before bundling, and npm run typecheck runs just the type check on its own.
npm install
npm run dev # start the dev server (Vite)
npm run build # production build to dist/
npm run preview # preview the production build locallyThe streaming flag lives in src/config/miris.ts. The viewer key itself is not in that file, it's gitignored, so it never ends up in source control:
cp src/config/miris-secrets.example.json src/config/miris-secrets.json
# then edit miris-secrets.json and paste in your viewer keyWithout that file, live streaming just stays off and every asset falls back to the offline preview, nothing breaks. In a real deployment, generate miris-secrets.json at build/deploy time from a proper secrets store (AWS Secrets Manager, a KMS-encrypted parameter, etc.) instead of committing it or hand-copying it onto a server.
Individual parts get a real Miris asset UUID in src/data/characters.ts, via each character's mirisUuids override map, e.g.:
mirisUuids: { 'upper-neutral': 'dabf1c6f-907c-4558-bba3-eab5609b92a6' },Any part still using a STUB-... placeholder UUID automatically falls back to an offline turntable preview (see src/components/MirisStage.tsx), so the app stays fully demoable before every part has a real Miris asset. Parts with a real UUID show a teal LIVE badge on their card.
The Miris Web SDK itself is loaded once, in index.html, as a type="module" script tag pointed at the @miris-inc/components package's CDN build (dist/components.cdn.prod.js), the variant meant for plain script-tag usage without a bundler.