parametrics: add custom field kind + restore fence Length / Curve

User noticed the auto-derived fence inspector was missing the legacy
panel's Length + Curve sliders, and the section labelling was wrong
(Posts → Structure). Both Length and Curve are awkward for the
parametrics field model:

- **Length** doesn't map to a single node key — it's derived from
  `start`/`end`, and editing it moves `end` along the existing
  direction.
- **Curve** maps to `curveOffset` but the slider's min/max are
  bounded per-node by the chord length, plus updates need
  `normalizeWallCurveOffset`.

Adds a `kind: 'custom'` field with a kind-supplied
`component: ComponentType<{ node, onUpdate }>`. The inspector mounts
it and lets the kind own rendering + update logic. `key` becomes a
free-form React key/label since it no longer needs to map to a node
property.

Fence parametrics now mirrors the legacy layout 1:1:
- Style (segmented controls + showInfill toggle).
- Dimensions (Length, Curve, Height, Thickness).
- Structure (Base Height, Top Rail, Post Spacing, Post Size, Ground
  Clear, Edge Inset).

Length + Curve live in fence/inspector-editors.tsx.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Wassim SAMAD
2026-05-18 10:27:40 -04:00
co-authored by Claude Opus 4.7
parent 80199dc890
commit 282cb22585
4 changed files with 150 additions and 23 deletions
+11
View File
@@ -385,6 +385,17 @@ export type ParamField<N> =
| { key: keyof N; kind: 'color'; visibleIf?: (n: N) => boolean }
| { key: keyof N; kind: 'material'; visibleIf?: (n: N) => boolean }
| { key: keyof N; kind: 'ref'; refKind: string; visibleIf?: (n: N) => boolean }
/** Escape hatch for fields that don't map to a single node key —
* derived values (`length` from `start`/`end`), sliders with
* dynamic min/max (curve sagitta bounded by chord length),
* composed editors, etc. The kind owns the rendering and the
* update logic. `key` here is just a stable React key/label. */
| {
key: string
kind: 'custom'
component: ComponentType<{ node: N; onUpdate: (patch: Partial<N>) => void }>
visibleIf?: (n: N) => boolean
}
export type Issue = { field?: string; msg: string; severity?: 'error' | 'warning' }