Phase 5 batch: door + window migrate to registry (always-on)
Both kinds share traits — hosted on walls, cuttable, animated open/
close state via a geometry system + animation system. Stage A
migration: register + wrap-export the legacy renderer + bundle both
per-kind systems. Pure geometry + floor-plan ports are later
milestones.
Files added (packages/nodes/src/door/, packages/nodes/src/window/):
- schema.ts: re-export from core.
- parametrics.ts: minimal — dimensions only. Door has 29 sliders +
segmented controls + presets in its legacy panel; window has 15+
sliders. Auto-inspector can't cover them at Stage A — legacy
panel keeps rendering via panel-manager.tsx case fall-through.
Stage E may extend parametrics or use parametrics.customPanel
escape hatch.
- definition.ts: capabilities (no `movable` — wall-bound drag is
bespoke; capability-driven dispatch keeps legacy MoveDoorTool /
MoveWindowTool), parametrics, renderer, system. defaults() uses
`DoorNode.parse({...stub})` to leverage zod's schema-level
`.default()` annotations — door has 40+ fields, window has 20+;
listing them inline duplicates the schema.
- renderer.tsx: wrap-export of legacy DoorRenderer / WindowRenderer
(thin 33-36 lines each).
- system.tsx: bundles each kind's TWO systems — DoorSystem +
DoorAnimationSystem, WindowSystem + WindowAnimationSystem. Both
per-kind systems mount via RegisteredSystems when the kind is
registry-driven; `<LegacySystem kind="door|window">` wrappers
around each individual system short-circuit.
- index.ts: barrel.
Files changed:
- packages/viewer/src/index.ts: new public exports for DoorRenderer,
DoorSystem, DoorAnimationSystem, WindowRenderer, WindowSystem,
WindowAnimationSystem.
- packages/nodes/src/index.ts: appends doorDefinition + windowDefinition.
- packages/editor/src/components/ui/panels/door-panel.tsx + window-
panel.tsx: panel slider-drag fix recipe applied. Drop the
subscribed `updateNode` action, drop the `node` dep from
handleUpdate / previewDoorUpdate / commitDoorPreview useCallbacks.
Use useScene.getState() inside. Door panel has 29 SliderControls,
window 15+ — both at high risk of the Maximum update depth
cascade without the fix.
Phase 5 progress: shelf ✅, spawn ✅, wall ✅, fence ✅, slab ✅, ceiling ✅,
door ✅, window ✅. Eight kinds on the registry. Item / stair / roof /
zone / containers remain.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
2dd50fa5be
commit
9eced06f32
@@ -0,0 +1,59 @@
|
||||
import type { NodeDefinition } from '@pascal-app/core'
|
||||
import { windowParametrics } from './parametrics'
|
||||
import { WindowNode } from './schema'
|
||||
|
||||
/**
|
||||
* Window — Phase 5 batch kind. Mirrors door's shape: hosted on walls,
|
||||
* cuts holes in them, animated open/close state for opening windows.
|
||||
*
|
||||
* Capabilities: no `movable` (wall-bound drag is bespoke). Tool field
|
||||
* absent (legacy WindowTool / MoveWindowTool continue).
|
||||
*/
|
||||
export const windowDefinition: NodeDefinition<typeof WindowNode> = {
|
||||
kind: 'window',
|
||||
schemaVersion: 1,
|
||||
schema: WindowNode,
|
||||
category: 'structure',
|
||||
|
||||
// Same schema-driven defaults trick as door: parse a stub, strip
|
||||
// id/type. Window also has many fields with zod `.default()` set.
|
||||
defaults: () => {
|
||||
const stub = WindowNode.parse({ id: 'window_default' as never, type: 'window' })
|
||||
const { id: _id, type: _type, ...rest } = stub
|
||||
return rest
|
||||
},
|
||||
|
||||
capabilities: {
|
||||
selectable: { hitVolume: 'bbox' },
|
||||
duplicable: true,
|
||||
deletable: true,
|
||||
},
|
||||
|
||||
parametrics: windowParametrics,
|
||||
|
||||
renderer: {
|
||||
kind: 'parametric',
|
||||
module: () => import('./renderer'),
|
||||
},
|
||||
system: {
|
||||
module: () => import('./system'),
|
||||
priority: 3,
|
||||
},
|
||||
|
||||
toolHints: [
|
||||
{ key: 'Left click', label: 'Place window on wall' },
|
||||
{ key: 'Esc', label: 'Cancel' },
|
||||
],
|
||||
|
||||
presentation: {
|
||||
label: 'Window',
|
||||
description: 'A window cut into a wall. Animated open/close for opening windows.',
|
||||
icon: { kind: 'iconify', name: 'lucide:rectangle-horizontal' },
|
||||
paletteSection: 'structure',
|
||||
paletteOrder: 60,
|
||||
},
|
||||
|
||||
mcp: {
|
||||
description: 'A window mounted on a wall, with type / dimensions / opening options.',
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
export { windowDefinition } from './definition'
|
||||
export { WindowNode } from './schema'
|
||||
@@ -0,0 +1,21 @@
|
||||
import type { ParametricDescriptor } from '@pascal-app/core'
|
||||
import type { WindowNode } from './schema'
|
||||
|
||||
/**
|
||||
* Minimal inspector descriptor for window. Like door, the legacy
|
||||
* `<WindowPanel>` has 15+ SliderControls covering sashes, dividers,
|
||||
* sill, frame, opening shape — too elaborate for the auto-inspector
|
||||
* at Stage A. Legacy panel keeps rendering via panel-manager's
|
||||
* `case 'window':`.
|
||||
*/
|
||||
export const windowParametrics: ParametricDescriptor<WindowNode> = {
|
||||
groups: [
|
||||
{
|
||||
label: 'Dimensions',
|
||||
fields: [
|
||||
{ key: 'width', kind: 'number', unit: 'm', min: 0.3, max: 4, step: 0.05 },
|
||||
{ key: 'height', kind: 'number', unit: 'm', min: 0.3, max: 4, step: 0.05 },
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
'use client'
|
||||
|
||||
import { WindowRenderer } from '@pascal-app/viewer'
|
||||
|
||||
/**
|
||||
* Wrap-export of the legacy `WindowRenderer`. Thin (~36 lines); Phase 5
|
||||
* Stage F inlines it here and deletes the viewer-side file.
|
||||
*/
|
||||
export default WindowRenderer
|
||||
@@ -0,0 +1 @@
|
||||
export { WindowNode } from '@pascal-app/core'
|
||||
@@ -0,0 +1,27 @@
|
||||
'use client'
|
||||
|
||||
import { WindowAnimationSystem, WindowSystem } from '@pascal-app/viewer'
|
||||
|
||||
/**
|
||||
* Registry-driven window system bundle. Same shape as door — two
|
||||
* per-frame systems wrapped together:
|
||||
*
|
||||
* - **`WindowSystem`** — rebuilds frame / sash / divider / sill /
|
||||
* muntin geometry. Cascades dirty to parent wall for the cutout.
|
||||
* - **`WindowAnimationSystem`** — advances sash/panel open state at
|
||||
* frame priority 2, then marks the window dirty for the geometry
|
||||
* rebuild at priority 3.
|
||||
*
|
||||
* Both wrapped in `<LegacySystem kind="window">` legacy mounts; with
|
||||
* window registered, those short-circuit and this bundle takes over.
|
||||
*/
|
||||
const WindowSystems = () => {
|
||||
return (
|
||||
<>
|
||||
<WindowAnimationSystem />
|
||||
<WindowSystem />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default WindowSystems
|
||||
Reference in New Issue
Block a user