Commit Graph
7 Commits
Author SHA1 Message Date
Wassim SAMADandClaude Opus 4.7 6d97a87547 Selection: registry-driven, drop spawn flag, restore green color
Two concerns from the spike:

1) Selection / floating-action-menu had hardcoded kind lists scattered
   across 4 files. Adding 'shelf' to each one per migration was the
   wrong abstraction — the user's question "did you make it generic
   from the noderegistry?" was the right one. Done now.

   Added to @pascal-app/core/registry:
   - getSelectableKinds(): string[] — returns all registered kinds
     whose definition declares `capabilities.selectable`.
   - isRegistrySelectable(kind): boolean — predicate for OR-chains.

   Refactored hardcoded sites to merge registry kinds at runtime,
   keeping legacy hardcoded lists intact so existing kinds keep
   working unchanged:
   - editor SelectionManager: 4 subscription loops (enter/leave/click)
     + structure.isValid + getSelectionTarget — all augment with
     registry kinds. Phase 6 deletes the hardcoded lists.
   - viewer SelectionManager: subscription loop + SelectableNodeType
     broadened with `(string & {})` to accept registry kinds.
   - floating-action-menu: ALLOWED_TYPES OR'd with isRegistrySelectable.
   - Removed the manually-added 'shelf' entries from previous commit
     857ddd4; they were redundant once the registry-driven path landed.

   Future built-in nodes that declare `capabilities.selectable` get
   click-selection + hover + the floating action menu (move/delete
   icons) for free, no editing of these 4 files.

2) Spawn parity is signed off. Drop the
   NEXT_PUBLIC_USE_REGISTRY_FOR_SPAWN flag entirely; spawn registers
   unconditionally in builtinPlugin.nodes. Restored SPAWN_COLOR to
   the original #22c55e green (was #ef4444 red as a Phase 2
   verification marker).

Pre-existing typecheck errors in editor (ceiling/fence/slab tree-node,
scene.ts buildingId) are unchanged.

630 tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 15:22:51 -04:00
Wassim SAMADandClaude Opus 4.7 d17e083c77 Add sfx:grid-snap on cursor cell-cross for shelf + spawn
Matches the wall / slab / curve-wall tool pattern: emit
sfx:grid-snap only when the snapped position changes (cursor crosses
a grid cell), not every frame of mouse movement within the same cell.
Tracked via a `previousSnapRef` per tool, reset when the tool
re-activates.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 15:01:56 -04:00
Wassim SAMADandClaude Opus 4.7 76794ceb7d Wire shelf + spawn placement polish: SFX, cursor, sidebar, selection
User-visible follow-ups after first running the Phase 2 spike.

Spawn tool now matches legacy UX:
- CursorSphere from @pascal-app/editor for the placement indicator
  (ring + line + tool-icon tooltip) — was a plain sphere mesh.
- Emits sfx:structure-build on commit + setTool(null) + setMode
  ('select') to exit build mode, matching legacy spawn-tool.

Shelf tool placement:
- Emits sfx:structure-build on commit.
- Cursor preview now shows top board + brackets (was just the top),
  matching what gets placed.

Shelf selectable from the 3D canvas:
- ShelfEvent type added to @pascal-app/core/events/bus.
- 'shelf' added to NodeConfig in useNodeEvents.
- ShelfRenderer wires `useNodeEvents(node, 'shelf')` handlers onto
  every mesh. Clicks/hovers now bubble through the editor's selection
  manager and update useViewer.selection.

Shelf appears in the sidebar:
- ShelfTreeNode component (mirrors spawn-tree-node's shape +
  selection/hover/rename wiring; lucide Layers icon).
- TreeNode dispatcher adds a `case 'shelf':` arm.

Framework changes:
- @pascal-app/editor exports CursorSphere alongside triggerSFX.
- @pascal-app/nodes now declares @pascal-app/editor as peer/dev dep.

Pre-existing typecheck errors in @pascal-app/editor (ceiling-tree-node,
fence-tree-node, slab-tree-node, scene.ts) are unchanged — present on
main and not introduced by this commit.

630 tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:55:03 -04:00
Wassim SAMADandClaude Opus 4.7 a89a1efccf Fix spawn flag inlining + shelf cursor frame + simpler renderer
Three concrete bugs surfaced when first-running the spike in community:

1) NEXT_PUBLIC_USE_REGISTRY_FOR_SPAWN flag never detected:
   The previous readEnvFlag used dynamic bracket access
   (`env?.[name]`), which Next.js / Turbopack does NOT substitute at
   build time. Only literal `process.env.NEXT_PUBLIC_FOO` references
   get inlined into the client bundle. Switched to literal access
   plus a `typeof process` guard. Spawn now toggles via the flag as
   designed.

2) Shelf cursor appeared offset from the mouse:
   The cursor mesh lives inside the ToolManager's building-local
   group, but the tool was setting `cursorRef.current.position` to
   level-local coordinates (computed via `worldToLocal(level)`).
   Result: cursor shifted by (building-pos − level-pos) in worst
   case. Switched cursor display to use `event.localPosition`
   (already building-local) with grid snap — matches the legacy
   spawn-tool pattern. The commit path keeps the worldToLocal(level)
   conversion since the shelf node's `position` field is stored
   relative to its level parent.

3) Shelf rendered invisibly after click (suspected):
   The renderer used a useEffect-swap pattern where it mounted an
   empty <group> and imperatively added Three.js children from a
   buildShelfGeometry() Group. Plausibly fragile under StrictMode
   double-invoke or fast HMR. Switched to inline R3F JSX — top
   board + brackets as plain <mesh> primitives. The pure geometry
   function still exists in geometry.ts for tests and AI-authored
   consumers; renderer just doesn't go through it.

Diagnostics added (dev-only; removed once spawn parity ships):
- `[shelf] placed <id> level-local <pos> parent <levelId>` on click
- `[shelf] rendered <id> at <pos>` on mount

Also: types: ["node"] in nodes/tsconfig.json so the typeof process
guard typechecks cleanly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 14:27:08 -04:00
Wassim SAMADandClaude Opus 4.7 ac71c1a83b Fix shelf cursor tracking + bigger snap step; tint registry spawn red
Three small follow-ups after the first user-visible spike test.

shelf cursor stuck at origin:
  The placeholder cursor is a translucent slab inside a <group ref>.
  The tool was calling setCursor(state) on every grid:move, which
  triggered a React re-render. R3F re-applies props on each render,
  and since the <group> had no `position` prop, the implicit default
  [0,0,0] clobbered the imperative `position.set` from the previous
  tick. Result: cursor stuck at level origin instead of following
  the mouse.

  Fix: drop the unused useState entirely. Pure imperative position
  updates via the ref. No re-renders, no clobbering. (The legacy
  spawn tool gets away with the same pattern because CursorSphere
  buffers its position prop differently — but for the spike, the
  simpler model is fine.)

shelf snap step:
  Was 0.1 (10cm) — much finer than the editor's default 0.5 grid.
  Bumped to 0.5 (matches the toolbar grid setting and the legacy
  half-meter snap pattern used by spawn/column).

registry-driven spawn renderer paints red:
  Temporary verification marker. With NEXT_PUBLIC_USE_REGISTRY_FOR_
  SPAWN=1, spawns rendered via the new path now appear in #ef4444
  red. Legacy renderer stays in #22c55e green. Easy visual check
  for "which dispatch path is this spawn on?" Reverted in the PR
  that signs off spawn parity (alongside legacy file deletion).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:59:49 -04:00
Wassim SAMADandClaude Opus 4.7 b6d77206b4 Phase 2 spike: spawn migration (flagged) + new shelf node
The first time registry-driven nodes actually run in the editor.

Spawn migration (under NEXT_PUBLIC_USE_REGISTRY_FOR_SPAWN flag):
- New packages/nodes/src/spawn/ folder with renderer, tool, schema
  (re-exported from core), parametrics, definition, index.
- Spawn definition appended to builtinPlugin.nodes only when the flag
  is set. With the flag off, the Phase 0 shims fall through and the
  legacy SpawnRenderer / SpawnTool keep ownership.
- New no-props SpawnTool reads activeLevelId from useViewer directly,
  matches legacy placement behavior (half-meter snap, singleton-per-
  level, replace-on-reclick).
- Structural parity test (9 cases) validates definition shape +
  schema identity. Pixel-diff defers to Phase 4 alongside more nodes.

New shelf node (no legacy — registered unconditionally):
- ShelfNode schema in core/schema/nodes/shelf.ts (hand-maintained
  AnyNode union for now; Phase 6 derives the union from the registry
  and moves the schema fully into nodes/shelf/).
- packages/nodes/src/shelf/ folder: pure geometry builder
  (buildShelfGeometry returns a Three.js Group of top board +
  brackets), R3F renderer that mounts the built group, no-props
  placement tool, parametrics descriptor (width/depth/thickness/
  height/bracketStyle/color), definition with surfaces.top stackable
  surface for future stacking, and presentation metadata for the
  palette.
- 13 unit tests across schema bounds and geometry behavior.
- Palette wiring: 'shelf' added to StructureTool union + an entry in
  the structure-tools array (placeholder icon, replaced in Phase 4
  when palette is registry-driven).

Framework changes:
- @pascal-app/viewer now exports useNodeEvents from its public barrel
  so node bundles in @pascal-app/nodes can subscribe to node-specific
  pointer events. (Used by spawn renderer; shelf renderer skips it
  for now since useNodeEvents has a hardcoded kind list — Phase 4
  generalizes it via the registry.)
- @pascal-app/nodes gains @pascal-app/viewer as a peer + dev dep so
  node bundles can import from it.

630 tests pass across 76 files (22 new this phase). Editor app
continues to ship green with both legacy spawn and the new shelf
node co-existing through the Phase 0 dispatch shims.

To validate end-to-end in dev:
- bun dev:community → open editor → click 'Shelf' in structure
  toolbar → click to place. Confirms full registry path
  (NodeRenderer dispatch + ToolManager dispatch + sceneRegistry
  byType Proxy).
- Set NEXT_PUBLIC_USE_REGISTRY_FOR_SPAWN=1, restart dev, place spawn
  → visually identical to legacy. Confirms parity.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-14 13:07:38 -04:00
Wassim SAMADandClaude Opus 4.7 fc36b5b600 Add @pascal-app/nodes package skeleton with empty builtinPlugin
New workspace package that owns built-in node bundles, one folder per
kind. Today the plugin is empty — no behavior change. Future commits
will add column/, shelf/, wall/, etc. and append each definition to
builtinPlugin.nodes.

The package depends on core (registry types) at v1; viewer, editor,
react, three are declared as peer deps so future node bundles can use
them without bumping their own version on every monorepo bump.

External plugins land as separate packages with the exact same shape
— this package is the dogfooded reference.

Tests: builtinPlugin shape + loadPlugin succeeds with zero kinds.

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