Files
kreios/CLAUDE.md
T
2026-07-13 11:58:30 +02:00

83 lines
5.5 KiB
Markdown

# CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
## Project overview
Web-based 3D configurator for infissi (windows/doors) — "infissionline-vite". It renders a Three.js scene
(GLB models) driven by JSON product data and a jQuery-based menu UI, and is meant to run **inside an
`<iframe>`** embedded on `https://www.infissionline.it` (or `https://infissionline.test` in dev), with the
final "add to cart" action posted back to the parent window via `postMessage`.
## Commands
Package manager is **pnpm** (`pnpm-lock.yaml` is the lockfile of record even though `package-lock.json` is
also present).
```bash
pnpm install # install deps
pnpm dev # vite dev server on 0.0.0.0:5173
pnpm build # production build -> dist/ (root is src/, see vite.config.js)
pnpm preview # preview the production build
```
There is no test suite (`pnpm test` is a stub that exits with an error) and no linter configured.
### Regenerating PVC price lists
`scripts/extract_listini_pvc.py` parses `PVC_LISTINO.xls` and regenerates
`src/assets/data/modelli/pvc/<modello>/listino.json` files. Requires a Python venv with `xlrd`
(`pip install -r scripts/requirements.txt`). Run with `python3 scripts/extract_listini_pvc.py` from repo root.
Any new title in the spreadsheet needs an entry in that script's `KNOWN_FOLDERS` map or it won't be matched
to an existing model folder.
## Architecture
Entry point is `src/js/starter.js`, loaded from `src/index.html`. Flow on load (`start()`):
1. **`ParamsParser.getQueryParams()`** (`src/js/params_parser.js`) reads the URL query string. Two params
drive everything: `sistema` (`pvc` | `alluminio` | `legno` | `acciaio`) and `type` (model id). See
`docs/URL_PARAMS.md` for the full param contract and the list of valid `type` values per `sistema`.
*Note: that doc describes a more modular refactor (`Application.js`, `communication/UrlParamsParser.js`,
`communication/IframeBridge.js`) that does not exist yet in `src/js` — the current implementation is the
flatter `starter.js`/`params_parser.js` described here.*
2. Product/catalog data is fetched as JSON from `src/assets/data/sistemi/<sistema>/modelli.json` and
`src/assets/data/modelli/<sistema>/<cartella>/{form,modello,maniglia,avvolgibile,vetro,listino}.json`
(which JSON files are fetched depends on the `moduli` array in the matched `modelli.json` entry).
3. **`MenuBuilder.build()`** (`src/js/menu_builder.js`) injects an HTML template (`assets/_menu.html`, or
`assets/_menu-cassonetto.html` when `type=cassonetto`) into `#left-container`, builds the model-picker
list, handles the mobile-portrait/landscape layout reflow, and initializes `PriceCalculator`. It returns
the price calculator instance used by the rest of the app.
4. **`Configurator.init()` / `.enable()`** (`src/js/configurator.js`) wires a single delegated `input` handler
on the size fields, selects and radios. On every change it recomputes price via `PriceCalculator`, builds
`window.configurazione` (the object that gets posted to the parent on "add to cart"), and updates the DOM.
5. **`APP.Player`** (`src/js/app.js`) wraps a Three.js renderer/scene/camera (scene graph loaded from
`src/assets/app.json`, a three.js-editor-style export) plus the actual product GLB
(`src/assets/models/<sistema>/<modello.modello>`). Two players are created: `player` (visible canvas) and
`playerOff` (offscreen, used only to render the PNG snapshot sent to the cart). Key methods: `swapModel`/
`hideModel` (toggle between pre-loaded variants in the same scene), `changeTexture`/`changeColor` (applied
per `userData.categoria` mesh tag — categories like `interno`, `esterno`, `maniglia`, `vetro`, `cerniere`),
`flipView` (180° GSAP-animated turn between front/back view), `addMarker` (in-scene hotspot with a video
popup).
6. **`PriceCalculator`** (`src/js/price_calculator.js`) is a stateless-ish module (single module-level
`listini` cache set via `init`) that looks up a base price from a width/height grid (`listino.json`,
rounded up to the nearest 10cm) and layers on percentage/flat supplements for finish, glass, hinges, handle
height, and actuator type, then adds 22% VAT.
### Data conventions
- Every 3D-visible part is tagged with `userData.categoria` in the GLB/scene so JS can show/hide/retexture it
without knowing mesh names (`interno`, `esterno`, `maniglia`, `cerniere`, `vetro`, ...).
- Each model folder under `src/assets/data/modelli/<sistema>/<cartella>/` follows the same file set:
`form.json` (UI radios/selects/finiture wiring — declares which named `<select>`/radio groups exist and
which categories/texture folders they map to), `modello.json` (finish options), `maniglia.json` (handle
options), `avvolgibile.json` (shutter, cassonetto-only), `vetro.json` (glass options), `listino.json`
(price grid).
- Texture images live under `src/assets/data/immagini/<sistema>/<folder>/<texture_folder>/<id>/...` and are
loaded lazily per selection (`APP.Player.changeTexture`); a monotonically-checked `isStale()` guard discards
a texture load if the user changed the selection again before it finished.
- `window.*` globals (`window.configurazione`, `window.cambiaTexture`, `window.caricaModello`,
`window.rotate`, `window.sistema`, etc.) are the integration surface between the jQuery-driven menu markup
(inline `onclick`/`onchange` in the `_menu*.html` templates) and the JS modules — expect to grep for a
`window.<name> =` assignment when tracing menu behavior, not for exported module functions.