Skip to content

Commit 34d07db

Browse files
author
cranm
committed
instrucciones persistentes para claude, y de estilo para humanos y claude.
1 parent 3b516b7 commit 34d07db

3 files changed

Lines changed: 582 additions & 0 deletions

File tree

CLAUDE.md

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,211 @@
1+
# CLAUDE.md
2+
3+
Operating manual for Claude Code sessions in this repo. Read this in full before making changes.
4+
5+
---
6+
7+
## What this project is
8+
9+
**BarroCode** — a web app that turns SVG curves into G-code for clay 3D printers. The SVG paths are sampled, modulated by a Lissajous wave (normal + tangent oscillation along arc length), stacked into layers, and emitted as G-code with clay-specific behaviour (no retract, soft layer joins, optional dwell/priming, concentric skirt travel, parabolic z-hop at self-intersections).
10+
11+
The whole app runs in the browser. There is no backend, no native shell.
12+
13+
---
14+
15+
## Stack and commands
16+
17+
- **Vite 5** + **React 18** + **TypeScript** (strict mode via `tsc &&` before `vite build`)
18+
- **No UI library**, no state manager, no router. All styles live in `src/index.css`. State is plain React `useState` in `App.tsx`.
19+
- Custom variable-weight font `GSCode` loaded from `public/fonts/`.
20+
21+
| Command | Purpose |
22+
|---|---|
23+
| `npm install` | Install deps |
24+
| `npm run dev` | Vite dev server (typically `http://localhost:5173`) |
25+
| `npm run build` | `tsc` type-check then `vite build``dist/` |
26+
| `npm run preview` | Local static preview of `dist/` |
27+
28+
There is **no** test runner, no linter config, no formatter config wired into npm scripts. Don't add one without being asked.
29+
30+
---
31+
32+
## Deployment
33+
34+
GitHub Pages, one workflow only: [.github/workflows/deploy.yml](.github/workflows/deploy.yml). On push to `main`: `npm install`, `npm run build`, upload `dist/` artifact, deploy.
35+
36+
`vite.config.ts` uses `base: './'` so the build also works opened directly from `file://` or a USB sub-path.
37+
38+
There is **no** desktop / Electron / portable distribution. It was removed; don't re-introduce it without explicit request.
39+
40+
---
41+
42+
## Data pipeline
43+
44+
```
45+
SVG string
46+
└─ parseSVG() [lib/svgParser.ts]
47+
• inserts raw SVG into a hidden 1000×1000 DOM div
48+
• queries path / polyline / polygon / line / circle / ellipse / rect
49+
• arc-length sampling via getTotalLength + getPointAtLength
50+
• finite-difference tangent/normal per sample
51+
• getScreenCTM() for transforms
52+
→ ParsedSVG { paths: SampledPath[], viewBox, raw }
53+
54+
SampledPath[] + PrintParams + WaveKeyframe[]
55+
└─ generateWaveLayers() [lib/waveGenerator.ts]
56+
• filters enabled paths
57+
• per layer: phaseBase = lissPhaseOffset + li * phaseShiftPerLayer
58+
• per point: getParamsAtT() lerps between keyframes
59+
• lissajousPoint(): offsetN = ampN*sin(2π·s/wlN + δ + phase)
60+
offsetT = ampT*sin(2π·s/wlT + phase)
61+
• applyScaleSVG: scale around pivot in SVG space
62+
• alternateDirection: reverse every other layer
63+
• closePath: append first point if not closed
64+
→ WaveLayer[] { index, z (mm), paths: WavePoint[][] (SVG units) }
65+
66+
WaveLayer[] + PrintParams + SVGViewBox
67+
└─ generateGcode() [lib/gcodeGenerator.ts]
68+
• svgToMM() at emit time (NOT before)
69+
• reorderPaths(): nearest-neighbour O(n²) per layer
70+
• computeCentroid + skirtArcPoints for inter-path travel
71+
• buildArcPath + findCrossings + hopAtArc for z-hop
72+
• buildTransition for softJoin: smoothstep XY + linear Z, no retract
73+
• E accumulates: E += dist * extrusionMultiplier
74+
→ string
75+
```
76+
77+
The master regeneration is a single `useEffect([parsedSVG, params, keyframes])` in `App.tsx` — every param change re-runs the entire pipeline. Re-sampling (re-parsing the raw SVG) only happens when `params.sampleSpacing` changes.
78+
79+
---
80+
81+
## File map
82+
83+
```
84+
src/
85+
main.tsx React root, StrictMode
86+
App.tsx Master state + layout (≈325 lines)
87+
index.css All styles (≈885 lines, design tokens in :root)
88+
types/index.ts All shared types
89+
components/
90+
Preview2D.tsx 3D ortho canvas + timeline + kf editor (≈892 lines, LARGEST)
91+
LissajousParams.tsx Right panel: wave sliders + presets
92+
LissajousPreview.tsx Bottom center: animated Lissajous canvas
93+
PathParams.tsx Left panel: layers, speeds, options
94+
PathList.tsx Per-path overrides + collapse
95+
CenterScaleParams.tsx Bottom left: pivot + scale + z-hop
96+
CenterPad.tsx 56×56 canvas pivot picker
97+
GcodeOutput.tsx G-code textarea + copy + download
98+
NumInput.tsx Controlled number input with wheel-to-change
99+
lib/
100+
svgParser.ts DOM-based SVG sampling
101+
waveGenerator.ts Lissajous math + keyframe interp + scale
102+
gcodeGenerator.ts G-code assembly (≈380 lines)
103+
hopUtils.ts Z-hop crossing detection
104+
skirtUtils.ts Concentric arc travel math
105+
```
106+
107+
---
108+
109+
## State and types
110+
111+
`PrintParams` is a single **flat** object with ≈50 fields covering sampling, Lissajous wave, layers, soft-join, SVG→mm transform, shape transform, z-hop, travel, speeds, extrusion, path options, and clay-specific behaviour. **Do not nest it, do not split it into multiple stores.**
112+
113+
Keyframes (`WaveKeyframe[]`) override Lissajous fields at given `t ∈ [0,1]`. Between keyframes, all fields are linearly interpolated (`wlN/wlT` floor-clamped at 0.1). When `keyframes.length > 0`, the per-path overrides (`ampNOverride` etc. on `SampledPath`) are **silently ignored by `waveGenerator`** — the UI disables them in this case (see `PathList.tsx`).
114+
115+
`App.tsx` state:
116+
- `params: PrintParams` — the print config
117+
- `parsedSVG: ParsedSVG | null` — sampled SVG
118+
- `layers: WaveLayer[]` — output of `generateWaveLayers`
119+
- `gcode: string` — output of `generateGcode`
120+
- `keyframes: WaveKeyframe[]`
121+
- `timelineProgress: number ∈ [0,1]` — scrubber on `Preview2D`
122+
- `gcodeFilename: string` — derived from uploaded SVG filename
123+
- `lastRawRef: { raw, spacing } | null` — kept for re-parse on spacing change
124+
125+
---
126+
127+
## Coordinate / unit conventions
128+
129+
- `WaveLayer.paths` stores points in **SVG user units**.
130+
- `svgToMM()` (in `waveGenerator.ts`) converts to **mm** only at G-code emit time.
131+
- `params.lissAmpN` / `lissAmpT` are in **mm** and divided by `scaleFactor` before use inside the wave math (so the visual amplitude tracks the real-world mm regardless of SVG unit scale).
132+
- Transform order: `scaleFactor` (SVG→mm) is separate from `scaleX/Y` (shape scale around pivot). `applyScaleSVG` handles `scaleX/Y` in SVG space before `svgToMM` converts.
133+
- `flipY`: most printers want Y growing upward; SVG Y grows downward.
134+
135+
---
136+
137+
## Conventions to follow
138+
139+
1. **All print params live in one flat `PrintParams`.** No nested objects. No context, no zustand, no redux.
140+
2. **Panels receive `params` + `onChange`.** Update with the spread pattern: `onChange({ ...params, [k]: v })`.
141+
3. **Component-local helpers.** `Num`, `Slider`, `Check`, `Sec` are defined inside each panel file. Don't extract them into a shared module.
142+
4. **Canvas rendering** lives inside `useEffect` with the full dependency array — the canvas redraws on every relevant change. Don't memoize drawing.
143+
5. **Stable-refs pattern for window-level handlers.** Refs are updated in a separate `useEffect([dep])`, then read inside a `useEffect([])` that attaches `window` listeners once. See `Preview2D.tsx` keyframe drag code for the canonical example.
144+
6. **Spanish UI throughout.** Labels, tooltips, section titles, error messages — all Spanish. Code, identifiers, and comments — English. (Existing files mix in some Spanish comments; keep new comments English.)
145+
7. **No undo/redo.** State changes are permanent until the user changes them again.
146+
8. **Default to writing no comments.** Only add a comment when the WHY is non-obvious (a hidden constraint, a workaround, a subtle invariant). Never explain WHAT well-named code already says, and never reference the current task / fix / caller.
147+
9. **Don't add error handling for impossible scenarios.** Trust internal code. Only validate at boundaries (user input, SVG parsing).
148+
10. **No backwards-compat shims.** This project has no public API; just change the code.
149+
150+
---
151+
152+
## Architectural sharp edges and known issues
153+
154+
These are documented hazards. Fix them when you touch the area, but don't go on cleanup sweeps without being asked.
155+
156+
- **`Preview2D.tsx` is ≈892 lines** and overdue for a split. The canvas draw code is the obvious extraction (→ `lib/draw3D.ts`). Don't refactor it preemptively; do it when adding a feature that would otherwise inflate the file further.
157+
- **`ParsedSVG.raw` duplicates `App.tsx`'s `lastRawRef.current.raw`.** Either could be removed.
158+
- **`bottomSplit.size`** is computed via `useResize` in `App.tsx` but never applied to layout. The drag handle is wired; the consumer isn't.
159+
- **`resampleSVG` in `lib/svgParser.ts`** is dead code.
160+
- **Z-hop is silently skipped for layer arc paths > 600 points** (`hopUtils.ts` MAX_PTS). No user-facing warning.
161+
- **`CenterPad`'s drag has no window-level handler** — drag stops abruptly at the 56×56 canvas boundary on fast moves.
162+
- **`skirtThreshold` lives under the "Velocidades" UI section** in `PathParams.tsx` but conceptually belongs with travel options.
163+
164+
---
165+
166+
## When working on G-code generation
167+
168+
This is the most subtle area. Before changing `lib/gcodeGenerator.ts`:
169+
170+
- Generate a sample with a 2- or 3-path SVG and read the output. The header comment block lists every parameter — use it to sanity-check.
171+
- Inter-path travel logic depends on three conditions: `isFirstMove`, `params.softJoin`, and `pi > 0` (path index within the layer). Layer→layer transitions only happen on the **last** path of a layer when `softJoin` is true.
172+
- Coordinate conversion via `svgToMM()` happens at emit time. Never pre-convert `WaveLayer.paths` to mm; downstream code expects SVG units there.
173+
- E accumulates monotonically (`E += dist * extrusionMultiplier`). If you add new motion, account for E unless `params.generateE` is false.
174+
- Clay printers don't retract. Don't introduce retraction moves.
175+
176+
---
177+
178+
## When working on the canvas previews
179+
180+
- `Preview2D` uses orthographic projection: `project(x, y, z, azimuth, elevation)`. Screen coords are `[offsetX + px*scale, offsetY + py*scale]`.
181+
- Layer colors are an HSL gradient from cobalt to terracotta: `hsl(218→20, 72%→88%, 50%→62%)`.
182+
- `LissajousPreview` cancels its `requestAnimationFrame` when both `ampN` and `ampT` are ≈0. Don't remove this — it's a battery and CPU optimization.
183+
- Auto-fit triggers in `LissajousPreview` are explicit and key off specific param changes (currently `lissAmpN`, `lissAmpT`). Pan/zoom are user-only.
184+
185+
---
186+
187+
## CSS
188+
189+
- Design tokens in `:root` at the top of `src/index.css`. Use them; don't hardcode colours.
190+
- Smallest text size is **9px**. Don't go below.
191+
- `--muted` is `#6A6762`. `--accent` is `#4F46E5` (indigo). The art direction is "Swiss warm-paper" — restrained, off-white background, single accent.
192+
- Sliders apply a dynamic `linear-gradient` fill inline (see `LissajousParams.Slider`). Keep this pattern when adding new sliders.
193+
- `body.dragging-h` / `body.dragging-v` are added during resize drags to override the cursor globally.
194+
195+
---
196+
197+
## What's gitignored (and why it matters)
198+
199+
`.gitignore` excludes `node_modules/`, **`package-lock.json`**, `dist/`, `release/`, `.vite/`, env files, editor settings, OS junk, and logs.
200+
201+
The lockfile being gitignored is unusual — accept it; don't try to commit it.
202+
203+
`release/` was the Electron build output and is gitignored for legacy reasons; nothing writes to it any more.
204+
205+
---
206+
207+
## Commit and review hygiene
208+
209+
- Conventional-commit prefixes are welcome but not enforced. The existing log mixes `feat:`, `fix:`, `chore:`, `ci:`, and plain prose.
210+
- Keep commits scoped to one logical change. If a change spans entangled files (rename + bug fix in the same function, etc.), split it manually — don't lump everything into a "misc" commit.
211+
- The remote is at [Cranmellar/curvabarro](https://github.com/Cranmellar/curvabarro) on GitHub. The repo name on GitHub has not been updated to match the new project name (`barrocode`); leave that for the user to do.

0 commit comments

Comments
 (0)