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>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
857ddd4d95
commit
6d97a87547
+11
-33
@@ -2,52 +2,30 @@ import type { AnyNodeDefinition, Plugin } from '@pascal-app/core'
|
||||
import { shelfDefinition } from './shelf'
|
||||
import { spawnDefinition } from './spawn'
|
||||
|
||||
/**
|
||||
* Feature flag for the Phase 2 spike. When `NEXT_PUBLIC_USE_REGISTRY_FOR_SPAWN`
|
||||
* is truthy, spawn registers through the registry path; otherwise the legacy
|
||||
* `SpawnRenderer` and `SpawnTool` in viewer/editor packages own the kind.
|
||||
*
|
||||
* 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 isSpawnRegistryEnabled(): boolean {
|
||||
// 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[] {
|
||||
const nodes: AnyNodeDefinition[] = [
|
||||
// Shelf is a new kind — no legacy code to flag against. It ships
|
||||
// unconditionally so users can place it from the tool palette.
|
||||
shelfDefinition as unknown as AnyNodeDefinition,
|
||||
]
|
||||
if (isSpawnRegistryEnabled()) {
|
||||
nodes.push(spawnDefinition as unknown as AnyNodeDefinition)
|
||||
}
|
||||
return nodes
|
||||
}
|
||||
|
||||
/**
|
||||
* Built-in plugin bundling every node kind shipped with the Pascal editor.
|
||||
*
|
||||
* Apps load this once at bootstrap (`loadPlugin(builtinPlugin)`) before
|
||||
* mounting the viewer. New built-in nodes are added by creating a folder
|
||||
* here under `src/<kind>/` and appending its `NodeDefinition` to `getBuiltinNodes`.
|
||||
* here under `src/<kind>/` and appending its `NodeDefinition` below.
|
||||
*
|
||||
* External plugins follow the exact same shape — same `Plugin` type, same
|
||||
* `loadPlugin` call path. This is intentional: the API is stress-tested
|
||||
* by built-ins before any third-party plugin lands.
|
||||
*
|
||||
* Phase 2 status: shelf is a brand-new kind. Spawn is migrated to the
|
||||
* registry path — the legacy SpawnRenderer / SpawnTool files are still
|
||||
* present in viewer/editor packages but short-circuited by the Phase 0
|
||||
* dispatch shims (`nodeRegistry.has('spawn')` is true → legacy path
|
||||
* yields). Legacy spawn files are deleted in a follow-up PR.
|
||||
*/
|
||||
export const builtinPlugin: Plugin = {
|
||||
id: 'pascal:core',
|
||||
apiVersion: 1,
|
||||
nodes: getBuiltinNodes(),
|
||||
nodes: [
|
||||
shelfDefinition as unknown as AnyNodeDefinition,
|
||||
spawnDefinition as unknown as AnyNodeDefinition,
|
||||
],
|
||||
}
|
||||
|
||||
export { shelfDefinition } from './shelf'
|
||||
|
||||
@@ -5,13 +5,7 @@ import { useNodeEvents, useViewer } from '@pascal-app/viewer'
|
||||
import { useMemo, useRef } from 'react'
|
||||
import { Color, type Group, Shape } from 'three'
|
||||
|
||||
// TEMPORARY (Phase 2 verification): the registry-driven renderer paints
|
||||
// spawns RED so you can visually tell which dispatch path is live. The
|
||||
// legacy renderer in @pascal-app/viewer is still green. Revert this to
|
||||
// '#22c55e' once the registry path is signed off for parity. Tracked by
|
||||
// the NEXT_PUBLIC_USE_REGISTRY_FOR_SPAWN flag — if a spawn renders red
|
||||
// you're on the new path; green = legacy.
|
||||
const SPAWN_COLOR = new Color('#ef4444')
|
||||
const SPAWN_COLOR = new Color('#22c55e')
|
||||
|
||||
/**
|
||||
* Registry-driven spawn renderer. Behaviorally identical to the legacy
|
||||
|
||||
Reference in New Issue
Block a user