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
@@ -154,6 +154,7 @@ export type { WallSurfaceMaterialSpec, WallSurfaceSide } from './nodes/wall'
|
||||
export {
|
||||
getEffectiveWallSurfaceMaterial,
|
||||
getWallSurfaceMaterialSignature,
|
||||
WALL_SLOT_DEFAULT,
|
||||
WallNode,
|
||||
} from './nodes/wall'
|
||||
export { WindowNode, WindowType } from './nodes/window'
|
||||
|
||||
@@ -11,6 +11,10 @@ export const CeilingNode = BaseNode.extend({
|
||||
children: z.array(ItemNode.shape.id).default([]),
|
||||
material: MaterialSchema.optional(),
|
||||
materialPreset: z.string().optional(),
|
||||
// Per-slot material overrides on the unified slot model, mirroring
|
||||
// `ShelfNode.slots`. Key = slot id (`surface`), value = a `MaterialRef`
|
||||
// (`library:<id>` / `scene:<id>`). Absent = the declared slot default.
|
||||
slots: z.record(z.string(), z.string()).optional(),
|
||||
polygon: z.array(z.tuple([z.number(), z.number()])),
|
||||
holes: z.array(z.array(z.tuple([z.number(), z.number()]))).default([]),
|
||||
holeMetadata: z.array(SurfaceHoleMetadata).default([]),
|
||||
|
||||
@@ -9,6 +9,10 @@ export const SlabNode = BaseNode.extend({
|
||||
type: nodeType('slab'),
|
||||
material: MaterialSchema.optional(),
|
||||
materialPreset: z.string().optional(),
|
||||
// Per-slot material overrides on the unified slot model, mirroring
|
||||
// `ShelfNode.slots`. Key = slot id (`surface`), value = a `MaterialRef`
|
||||
// (`library:<id>` / `scene:<id>`). Absent = the declared slot default.
|
||||
slots: z.record(z.string(), z.string()).optional(),
|
||||
polygon: z.array(z.tuple([z.number(), z.number()])),
|
||||
holes: z.array(z.array(z.tuple([z.number(), z.number()]))).default([]),
|
||||
holeMetadata: z.array(SurfaceHoleMetadata).default([]),
|
||||
|
||||
@@ -46,6 +46,16 @@ export type WallNode = z.infer<typeof WallNode>
|
||||
|
||||
export type WallSurfaceSide = 'interior' | 'exterior'
|
||||
|
||||
// Declared default appearance for an unpainted wall face in colored mode —
|
||||
// visual parity with the retired DEFAULT_WALL_MATERIAL. Lives in core so the
|
||||
// slot declaration (nodes) and the material resolver (viewer) share one value.
|
||||
// May be a `#rrggbb` colour or a `library:<id>` ref. Textures-off still
|
||||
// collapses to the themed wall role (the escape hatch).
|
||||
export const WALL_SLOT_DEFAULT: Record<WallSurfaceSide, string> = {
|
||||
interior: '#ffffff',
|
||||
exterior: '#ffffff',
|
||||
}
|
||||
|
||||
export type WallSurfaceMaterialSpec = {
|
||||
material?: z.infer<typeof MaterialSchema>
|
||||
materialPreset?: string
|
||||
|
||||
Reference in New Issue
Block a user