feat(paint-slots): unified slot defaults + paint for slab, ceiling, wall (phase 5)
Brings slab, ceiling, and wall onto the unified slot contract the shelf established, so each declares its paintable slots with a declarative default and (slab/ceiling) is painted through the registry capabilities.paint dispatch. - Shared helper packages/nodes/src/shared/slot-paint.ts: a node.slots-based PaintCapability factory (commit/resolve/effective generic; preview injected). Distinct from surface-paint.ts, which writes the legacy inline node.material. - slab: schema slots; def.geometry resolves node.slots.surface -> legacy material -> declared default, tags the mesh userData.slotId; slabPaint + capabilities.slots. Retires DEFAULT_SLAB_MATERIAL in the slab path. - ceiling: schema slots; material builders extracted to ceiling/materials.ts (shared by renderer + paint preview, built BackSide so the hover preview is visible from below); renderer resolves the slot; ceilingPaint + slots. - wall: WALL_SLOT_DEFAULT in core; the viewer's getMaterialsForWall renders an unpainted face with its declared default instead of the themed wall role; capabilities.slots (interior/exterior). wallPaint's inline interior/exterior fields are unchanged (node.slots migration is a later step). - selection-manager + material-paint: drop slab/ceiling from the legacy single-surface arms (now registry-driven). Behavior change (intended, matches the shelf precedent + the phase-5 plan): colored-mode UNPAINTED slab/ceiling/wall surfaces now render their fixed slot default (#e5e5e5 / #f5f5dc / #ffffff) instead of the theme role colour. The textures-off (monochrome) role collapse is unchanged — the escape hatch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
b0f4e1b8ee
commit
967a905e3b
@@ -6,6 +6,7 @@ import { wallFloorplanSiblingOverrides } from './floorplan-overrides'
|
||||
import { wallPaint } from './paint'
|
||||
import { wallParametrics } from './parametrics'
|
||||
import { WallNode } from './schema'
|
||||
import { wallSlots } from './slots'
|
||||
|
||||
/**
|
||||
* Wall — the Phase 3 stress test of the registry-driven node model.
|
||||
@@ -56,6 +57,11 @@ export const wallDefinition: NodeDefinition<typeof WallNode> = {
|
||||
// preview through this entry rather than carrying a kind-name
|
||||
// arm.
|
||||
paint: wallPaint,
|
||||
// Declared paintable slots (interior / exterior) with their default
|
||||
// appearance — the same `{ slotId, label, default }` contract every other
|
||||
// paintable kind exposes. Paint still writes the legacy inline fields via
|
||||
// `wallPaint`; migrating those into `node.slots` is a later step.
|
||||
slots: () => wallSlots(),
|
||||
},
|
||||
|
||||
relations: {
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
import { type SlotDeclaration, WALL_SLOT_DEFAULT } from '@pascal-app/core'
|
||||
|
||||
/**
|
||||
* A wall exposes two paintable faces — interior + exterior. Painting still
|
||||
* writes the legacy `interiorMaterial*` / `exteriorMaterial*` fields via
|
||||
* `wallPaint` (the inline model isn't migrated to `node.slots` yet); this
|
||||
* declaration surfaces the slot list + declared defaults for the picker and
|
||||
* keeps walls on the same `{ slotId, label, default }` contract as every other
|
||||
* paintable kind. The defaults come from core so the viewer's material
|
||||
* resolver renders the identical value.
|
||||
*/
|
||||
export function wallSlots(): SlotDeclaration[] {
|
||||
return [
|
||||
{ slotId: 'interior', label: 'Interior', default: WALL_SLOT_DEFAULT.interior },
|
||||
{ slotId: 'exterior', label: 'Exterior', default: WALL_SLOT_DEFAULT.exterior },
|
||||
]
|
||||
}
|
||||
Reference in New Issue
Block a user