Scans (LiDAR meshes) and guides (floorplan images) are heavy reference assets
stored elsewhere, not part of the compiled building — and baking them into a
public static GLB would also bypass the per-project show_*_public flags. Strip
both from the export entirely (previously they leaked in as empty identity
nodes, timing-dependent).
The GLB viewer re-adds them at runtime from the scene graph, like lights:
GlbReferenceNodes resolves each scan/guide's registry renderer (no static nodes
import — same nodeRegistry path the viewer already uses) and portals it into its
parent level's baked node, so the node's level-local transform resolves to the
right world pose and rides level stacking. Uses the same GuideRenderer/
ScanRenderer + asset resolver as the parametric viewer, so http-backed assets
show for everyone and local asset:// ones for the owner — exact parity.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Wholesale move of every remaining kind into its own subdirectory under
`packages/nodes/src/`, finishing the registry-driven migration. Each
kind now ships its definition, schema (re-exported from core), and any
of `geometry` / `renderer` / `system` / `floorplan` / `tool` /
`move-tool` / `panel` / `floorplan-move` / `floorplan-affordances` /
`parametrics` / `preview` it needs — no per-kind code remains under
`packages/editor/src/components/tools/` or
`packages/viewer/src/components/renderers/`.
Deleted (replaced by registry-driven equivalents):
- `tools/{ceiling,column,door,fence,item,slab,spawn,wall,window}/...`
(boundary editors, hole editors, placement tools, move tools,
endpoint movers, curve tools, helpers, math libs)
- `ui/helpers/{ceiling,slab,wall}-helper.tsx`
- `ui/panels/{column,door,elevator,item,roof,roof-segment,spawn,
stair,stair-segment,wall,window}-panel.tsx`
- `viewer/src/components/renderers/{building,ceiling,column,door,
elevator,fence,guide,item,level,roof,roof-segment,scan,site,slab,
spawn,stair,stair-segment,wall,window,zone}-renderer.tsx`
- `viewer/src/components/viewer/legacy-system.tsx`
Added under `packages/nodes/src/`:
- `building/`, `column/`, `elevator/`, `guide/`, `level/`, `roof/`,
`roof-segment/`, `scan/`, `shared/`, `site/`, `stair/`,
`stair-segment/` packages with definition + schema + renderer / system
/ floorplan / panel as appropriate.
- New `floorplan-move.ts` for every kind that supports 2D moves
(ceiling, door, item, shelf, slab, window) — single registry-driven
dispatch path via `def.floorplanMoveTarget`.
- New `floorplan-affordances.ts` for kinds with polygon / endpoint
drags (ceiling, fence, slab, wall) — using the shared
`polygon-vertex-affordance` factories.
- New per-kind `panel.tsx` for kinds with custom inspector content
(door, item, shelf, spawn, wall, window).
- New per-kind `tool.tsx` for placement (door, item, shelf, window).
- New per-kind `move-tool.tsx` for kinds with custom 3D move flows
(door, item, slab, window).
Coordinator + manager updates in `packages/editor/`:
- `tool-manager.tsx` resolves tools from the registry only — no
hardcoded type→component map.
- `panel-manager.tsx` resolves inspector panels the same way.
- `placement-{coordinator,strategies,types}.ts` extended with
shelf-surface placement.
- `selection-manager.tsx` adds the registry-selectable fallback.
- `floorplan-panel.tsx`, `floorplan-background-placement.ts`,
`floorplan-render-context.tsx` updated for the registry layer's new
contract (props, affordance dispatch, render context).
Viewer updates:
- `viewer/index.tsx` drops legacy renderer mounts.
- `node-renderer.tsx` resolves by registry only.
- `scene-bvh.tsx`, `use-node-events.ts`, `level-system.tsx`,
`wall-cutout.tsx`, `zone-system.tsx`, `materials.ts` adjusted for
the registry-only world.
Sidebar tree nodes for ceiling / fence / slab / shelf / tree-node
updated to read from the registered nodes instead of the deleted
legacy renderer trees.
Wiki: new `plugin-authoring.md` page, README index updated.
Tests in `packages/nodes/src/index.test.ts` validate every registered
kind has the required shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lands the three-checkbox composition runtime documented in
wiki/architecture/node-definitions.md. A kind with only a pure
geometry function now needs zero per-kind React or system code.
Type-side additions (packages/core/src/registry/types.ts):
- New `GeometryContext` (resolve / children / siblings / parent) — read-
only scene access for builders that reference other nodes by ID
(wall miters, door cutouts). Most kinds ignore it.
- New `geometry?: (node, ctx) => Object3D` field on NodeDefinition,
independent of renderer/system. Three orthogonal opt-ins replace the
v0 RendererSource union.
- Re-exported via packages/core/src/registry/index.ts (consumed by
nodes packages through `export * from './registry'`).
Runtime (packages/viewer):
- New <GeometrySystem> (systems/geometry/geometry-system.tsx) walks
dirtyNodes, builds a GeometryContext per dirty node, calls
def.geometry, disposes old children, attaches new ones, clearDirty.
Frame priority 2 (matches the priority shelf's per-kind system had).
Mounted in viewer/index.tsx alongside <RegisteredSystems>.
- New <ParametricNodeRenderer> (components/renderers/parametric-node-
renderer.tsx) — empty <group> + useRegistry + useNodeEvents +
markDirty-on-mount + useLiveTransforms. Mounts hosted children via
<NodeRenderer> recursively. The default renderer for any registered
kind without a custom def.renderer.
- <NodeRenderer> dispatch updated: custom renderer wins, else
geometry-only kinds fall through to ParametricNodeRenderer, else
null (legacy switch fallback). Documented inline.
Shelf migration (proof of the boilerplate collapse):
- Deleted nodes/src/shelf/renderer.tsx (was 45 lines of registry +
handler boilerplate).
- Deleted nodes/src/shelf/system.tsx (was 60 lines of dirty-loop +
dispose plumbing).
- shelfDefinition now: `geometry: buildShelfGeometry`. One line.
buildShelfGeometry is the pure function from geometry.ts that already
existed.
End-to-end effect: registry-driven shelf now mounts via the framework's
generic renderer + system. Parametric edits flow through the same
dirty-driven rebuild path, but the kind ships ~100 fewer lines of
boilerplate. Every future kind that fits the same shape (item, fence
segment, column, etc. as they migrate in Phase 5) follows the same
"one line, one pure function" pattern.
Wall stays on its dedicated def.renderer + def.system — its mitering
needs level-batch context (`ctx.levelData?.miters`, future extension)
that the generic system doesn't yet provide. Decided at Phase 3+, not
blocking Phase 4 acceptance.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Lays down the wall folder under @pascal-app/nodes with everything needed
to register the kind, but intentionally without runtime wiring:
- schema.ts re-exports WallNode from core (door/window/item still type
their parentId against WallNode.shape.id, so the schema stays canonical
there for now).
- parametrics.ts declares thickness / height / curveOffset for the Phase 4
inspector. Endpoints and host children are edited via affordances, not
number inputs, so they're not in parametrics.
- definition.ts encodes capabilities (surfaces, selectable, duplicable,
deletable — no movable since wall's move is bespoke endpoint-drag),
relations (hosts doors/windows/items, affectsSpatial slabs/ceilings/
zones, linkedBy endpoint-match, cascadeDelete descendants), and the
presentation metadata for the palette. Renderer / system / tool fields
are deliberately absent — the existing wall-renderer.tsx and
wall-system.tsx keep serving wall until milestone B.
- feature-flag.ts gates the eventual registration via
NEXT_PUBLIC_USE_REGISTRY_FOR_WALL (same pattern Phase 2 used for spawn).
- wallDefinition is NOT yet appended to builtinPlugin.nodes — registration
is what flips the Phase 0 dispatch shims, and we don't want that until
the runtime port lands. Until then this file is metadata-only.
Two type-side changes pulled forward from Phase 4 to make a metadata-only
definition compile:
- NodeDefinition.renderer becomes optional (the three-checkbox model
documented in wiki/architecture/node-definitions.md already promises
this). RegistryRenderer in node-renderer.tsx gains a null-guard so an
undefined renderer cleanly falls through to the legacy switch.
No runtime behavior change. Walls render and behave exactly as before.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
NodeRenderer now checks the registry first. Registered kinds load
their renderer module via React.lazy (cached by RendererSource so the
Suspense boundary is stable across re-renders). Unregistered kinds
fall through to the legacy chain below.
Today the registry is empty (Phase 0 builtinPlugin.nodes is []), so
every node still hits the legacy chain — no behavior change. The
moment a kind registers in @pascal-app/nodes (Phase 2 onward), the
registry path takes over without further edits here.
GLB / instanced-GLB RendererSource kinds are typed but not yet
honored — they get their built-in renderers in Phase 5.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat: railing on the straight stairs and new fence
* feat: added spiral and curved stairs with bug fix for fence
* feat:fence are linked to each other ... so moving one move the other sharing the same coordinate
* fix: update stair railing logic to include front-side attachments for terminal landings
* Integrate fence rendering into the fence system
* fix: pass nodeId instead of undefined node to WallTreeNode and FenceTreeNode
TreeNode was passing `node` (undefined variable) instead of `nodeId` to
WallTreeNode and FenceTreeNode, causing a runtime ReferenceError.
Updated FenceTreeNode to accept nodeId and look up the node from the
scene store internally, consistent with all other tree node components.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: update fence icon with new isometric design
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Aymeric Rabot <aymeric@pascal.app>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Stair system (full stack):
- New StairNode + StairSegmentNode schemas with flights, landings, L/U-shapes
- StairSystem: geometry generation with throttled per-frame updates
- Stair tool, edit system, panels, tree node, and renderers
- Event bus types, scene registry, and command palette entries
Scene graph utilities:
- cloneLevelSubtree: deep-clone a level with remapped IDs
- forkSceneGraph: clone + strip scan/guide nodes for project forking
Core improvements:
- Read-only mode on scene store (blocks create/update/delete when locked)
- readOnly guards on node-actions and collection actions
- Upload store for scan/guide file upload handling
Viewer state:
- previewSelectedIds for box-select live preview
- hoverHighlightMode (default/delete) for delete-mode hover outline
Major changes:
- Roof system rewrite with roof-segment support
- Scene store refactor
- Spatial grid improvements
- Item light system
- Post-processing and selection manager updates
- Perf monitor component
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>