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>
This commit is contained in:
Wassim SAMAD
2026-05-14 14:27:08 -04:00
co-authored by Claude Opus 4.7
parent ac71c1a83b
commit a89a1efccf
4 changed files with 83 additions and 52 deletions
+8 -8
View File
@@ -10,15 +10,15 @@ import { spawnDefinition } from './spawn'
* Removed in the PR that signs off parity (legacy spawn files deleted in the
* same commit). All other built-in node migrations follow the same pattern.
*/
function readEnvFlag(name: string): boolean {
const env = (globalThis as { process?: { env?: Record<string, string | undefined> } }).process
?.env
const flag = env?.[name]
return flag === '1' || flag === 'true'
}
function isSpawnRegistryEnabled(): boolean {
return readEnvFlag('NEXT_PUBLIC_USE_REGISTRY_FOR_SPAWN')
// Next.js / Turbopack inlines `process.env.NEXT_PUBLIC_*` references at
// build time, but ONLY when the access is a literal property — dynamic
// bracket access (`env[name]`) is not substituted and resolves to
// undefined in the browser. Keep this as a literal so the value is baked
// into the client bundle.
if (typeof process === 'undefined') return false
const flag = process.env.NEXT_PUBLIC_USE_REGISTRY_FOR_SPAWN
return flag === '1' || flag === 'true'
}
function getBuiltinNodes(): AnyNodeDefinition[] {