Files
editor/packages/nodes/src/shelf/parametrics.ts
T
Wassim SAMADandClaude Opus 4.7 924567293a shelf: v2 — cubby default, withBottom, item hosting, paintable surface
Schema v2 adds style/rows/columns/withBack/withSides/withBottom/bracketStyle
and a `children: ItemNode[]` field for item hosting. Schema-level defaults
preserve the v1 wall-shelf visual so existing scenes load unchanged; the
placement tool spreads `shelfDefinition.defaults()` for fresh shelves
(cubby 3x2 at 1m × 0.5m × 1.8m, thickness 0.05m, back/sides/bottom on).

Four style geometries (wall-shelf / bookshelf / open-rack / cubby) share
the dimensional schema. `shelfRowSurfaceYs` exposes one host surface per
row, plus the bottom-board top when `withBottom` is on for cubby /
bookshelf.

Material is a single paintable surface (same shape walls / slabs / stairs
use); `DEFAULT_SHELF_MATERIAL` aligned with `DEFAULT_WALL_MATERIAL` so
unpainted shelves read as the canonical off-white.

Preview clones each cached material before mutating `transparent / opacity`
on the ghost — without the clone the mutation leaked into the cached
`getShelfMaterial` instance every committed shelf was using, rendering
them all see-through after the first placement preview rendered.

Store hardening: `migrateNodes` patches missing `children: []` on v1
shelves, and `updateNodesAction` reparenting tolerates a missing children
array on the new parent. `MaterialTarget` enum adds `'shelf'` so paint
mode picks up the kind.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 15:11:13 -04:00

81 lines
2.8 KiB
TypeScript

import type { ParametricDescriptor } from '@pascal-app/core'
import type { ShelfNode } from './schema'
/**
* Inspector descriptor for the parametric shelf. Drives both the
* auto-derived inspector UI and the AI/MCP `create_shelf` /
* `update_shelf` tools with bounded JSON-schema parameters.
*
* Fields are grouped by intent: Style first (what kind of shelf), then
* Topology (rows / columns / back / sides / bottom + wall-shelf bracket
* style), then Dimensions. Surface material is paint-tray driven (same
* flow as walls / slabs / stairs) and intentionally not surfaced here.
*/
export const shelfParametrics: ParametricDescriptor<ShelfNode> = {
groups: [
{
label: 'Style',
fields: [
{
key: 'style',
kind: 'enum',
options: ['wall-shelf', 'bookshelf', 'open-rack', 'cubby'],
},
],
},
{
label: 'Topology',
fields: [
{ key: 'rows', kind: 'number', min: 1, max: 8, step: 1 },
// Columns only meaningful for kinds with vertical dividers.
{
key: 'columns',
kind: 'number',
min: 1,
max: 6,
step: 1,
visibleIf: (n) => n.style === 'bookshelf' || n.style === 'cubby',
},
// Sides toggle only applies to bookshelf (cubby always on, the
// others use their own post structure).
{
key: 'withSides',
kind: 'boolean',
visibleIf: (n) => n.style === 'bookshelf',
},
// Back toggle only applies to bookshelf and open-rack (cubby
// always has a back, wall-shelf has no back).
{
key: 'withBack',
kind: 'boolean',
visibleIf: (n) => n.style === 'bookshelf' || n.style === 'open-rack',
},
// Bottom toggle only applies to bookshelf and cubby — closes
// the lowest cell with a floor board so items can host there.
{
key: 'withBottom',
kind: 'boolean',
visibleIf: (n) => n.style === 'bookshelf' || n.style === 'cubby',
},
// Bracket style only matters for wall-shelf — the other styles
// structure themselves through sides / posts / dividers.
{
key: 'bracketStyle',
kind: 'enum',
options: ['minimal', 'industrial', 'hidden'],
visibleIf: (n) => n.style === 'wall-shelf',
},
],
},
{
label: 'Dimensions',
fields: [
{ key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 3.0, step: 0.05 },
{ key: 'depth', kind: 'number', unit: 'm', min: 0.1, max: 1.0, step: 0.05 },
{ key: 'thickness', kind: 'number', unit: 'm', min: 0.01, max: 0.1, step: 0.005 },
{ key: 'height', kind: 'number', unit: 'm', min: 0.05, max: 2.5, step: 0.05 },
],
},
],
}