Files

105 lines
6.0 KiB
Markdown

# CLAUDE.md — hmwebviewer (Three.js 3D Viewer)
Hybrid SSR+CSR web 3D model viewer. Spec: `3d_viewer_architecture_spec.pdf`.
## ⚠️ First action on EVERY session / agent start
Read these two files before doing anything:
1. [`PLAN.md`](./PLAN.md) — what work remains, who owns what, task breakdown.
2. [`PROGRESS.md`](./PROGRESS.md) — what is done, current state, blockers, decisions log.
If either file is missing or empty, treat the project as not started and bootstrap from the spec.
## What we are building
A browser 3D model viewer with **two load paths**:
- **Path A — Server asset (SSR + CSR)**: server ships a pre-rendered 360° animated WebP/WebM placeholder + HTML/CSS skeleton. Three.js loads + decodes the real Draco/KTX2 model in the background. On ready, fade WebP → canvas (opacity 0.5s).
- **Path B — Local file (Drag & Drop, CSR only)**: user drops a `.glb`/`.gltf`. `URL.createObjectURL(file)``GLTFLoader.load` → decode → add to scene → `URL.revokeObjectURL`.
Target: perceived load < 3s, no frame drops, smooth GPU.
## Locked technical decisions
These are decided. Do not relitigate without explicit user approval.
- **Three.js** + `GLTFLoader` + `DRACOLoader` + `KTX2Loader` on ONE shared loader instance each (multiple DRACOLoader instances crash — three.js #22445).
- **Geometry**: Draco. **Textures**: KTX2 / Basis Universal. Both compatible on same loader.
- **Decoder hosting**: use Vite hashed assets generated from the installed Three.js loader imports. Stage only single-thread `web-ifc.wasm` under `public/web-ifc`. Pin decoder version to the Three.js version in use.
- **Local file**: `createObjectURL` + revoke after load. For repeat/cached loads, `FileReader``ArrayBuffer``GLTFLoader.parse()`.
- **Build**: Vite. Draco worker spawns via BLOB URL — decoder files must be reachable at the configured path at runtime.
- **Asset pre-processing (offline)**: `gltf-transform` (Draco + KTX2) — preferred over `gltf-pipeline`. Meshopt is a viable Draco alternative.
- **Pre-render pipeline (offline)**: Blender headless CLI 360° turntable frames → assemble to animated WebP (alpha) / WebM VP9. OR Puppeteer + headless Three.js `page.screenshot({type:'webp'})` per rotation step.
- **Renderer**: WebGLRenderer (stable baseline). WebGPU renderer = future option, not now.
Full rationale in user memory: `~/.claude/projects/d--MYCLAUDE-PROJECT-hmwebviewer/memory/`.
## Architecture map (target)
```
src/
viewer/
ThreeDViewer.ts # core class: init, loadServerAsset, loadLocalFile, executeHydration, toggleUI
loaders.ts # singleton GLTFLoader + DRACOLoader + KTX2Loader setup
hydration.ts # WebP placeholder -> canvas fade coordination
dnd/
dropzone.ts # HTML5 drag&drop, file validation, createObjectURL/revoke
ui/
progress.ts # load progress bar
scenes/ # per-model scene configs
public/
web-ifc/ # single-thread IFC decoder WASM
samples/ # 배포 demo model
previews/ # pre-rendered WebP/WebM placeholders
dist/assets/ # Vite hashed Draco/Basis decoder assets
tools/
preprocess.mjs # gltf-transform Draco+KTX2 pipeline
prerender/ # Blender/Puppeteer turntable -> WebP
```
## How to work here (rules)
- **Surgical changes.** Touch only what a task requires. Match existing style.
- **Simplicity first.** Minimum code that solves the task. No speculative features/config.
- **Verify before claiming done.** Run the relevant check (build / typecheck / load test) and quote real output.
- **Keep PLAN.md + PROGRESS.md current.** Update the task status when you start/finish a unit of work. Other agents depend on it.
- **Memory is durable; PLAN/PROGRESS are working state.** Stable decisions → memory (via the memory dir). Transient task state → PLAN/PROGRESS.
## Commands (see `.claude/commands/`)
- `/bootstrap` — scaffold project from spec (run once, when PLAN says not started).
- `/next-task` — read PLAN + PROGRESS, pick the next ready task, assign self.
- `/report` — append current status to PROGRESS.md.
- `/optimize-asset <file>` — run gltf-transform Draco+KTX2 on an asset.
- `/verify-viewer` — build + load smoke test.
## Agents (see `.claude/agents/`)
Specialized subagents for parallelizable work — `viewer-core`, `dnd-handler`, `asset-pipeline`, `hydration`, `reviewer`. Invoke via the Agent tool / Task delegation. Details in each agent file.
## Reinforced structure — agent ↔ skill ↔ command ↔ task map
| PLAN task(s) | Owning agent | Skill to load | Command |
|---|---|---|---|
| P0 | task-lead → setup | threejs-viewer | `/bootstrap` |
| P1-1..P1-4 | viewer-core | threejs-viewer | `/verify-viewer` |
| P2-1,P2-2 | dnd-handler | threejs-viewer | `/verify-viewer` |
| P3-1..P3-3 | hydration | threejs-viewer | `/verify-viewer` |
| P4-1..P4-3 | asset-pipeline | threejs-viewer | `/optimize-asset` |
| P5-* | reviewer | threejs-viewer | `/verify-viewer`, `/report` |
| (any) | task-lead | task-graph | `/next-task`, `/report` |
**Load the `threejs-viewer` skill** whenever touching runtime viewer/dnd/asset/hydration code — it holds the locked patterns (singleton loaders, Blob URL lifecycle, hydration gating). **Load `task-graph`** when picking up or dispatching work.
## Runbook — how a work session goes
1. SessionStart hook reminds you. Read `PLAN.md` + `PROGRESS.md`.
2. Run `/next-task` (or have `task-lead` agent do it) → get the next ready task + owning agent.
3. Dispatch the owning agent with task ID, scope, acceptance criteria. Dispatch independent agents in ONE message (parallel).
4. On completion: owning agent flips PLAN row → `done`, runs `/report` to append PROGRESS.md.
5. `reviewer` runs after functional waves; `/verify-viewer` gates release.
## Hooks
- `SessionStart``.claude/hooks/session-start.sh` prints the "read PLAN + PROGRESS" reminder.
- `progress-nudge.sh` exists under `.claude/hooks/` for PostToolUse nudge to update PROGRESS after runtime edits — add its `PostToolUse` entry to `settings.json` if desired (not auto-wired).