feat(editor): roof features in standalone Build tab + thumbnail flag fix

- registry: getRoofAccessoryKinds() enumerates kinds declaring the
  roofAccessory capability, in deterministic builtin-list order (mirrors
  getSelectableKinds); exported from the registry barrel.
- apps/editor build-tab: a "Features" group under the Roof tile, discovered
  from the registry (no DB) and activating each kind's roof-attach tool —
  parity with the community editor's roof features.
- site-boundary-editor: hide the flag handle group around thumbnail captures
  (they render on SCENE_LAYER so the thumbnail camera can't layer-filter them),
  matching handle-arrow.tsx — keeps flags out of preset/snapshot thumbnails.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Aymeric Rabot
2026-06-09 19:55:53 -04:00
co-authored by Claude Fable 5
parent eb00f9f477
commit 478f0910f7
4 changed files with 162 additions and 25 deletions
+1
View File
@@ -17,6 +17,7 @@ export type {
export {
discoverPlugins,
getHostRefFields,
getRoofAccessoryKinds,
getSelectableKinds,
isDrawnViaTool,
isDrawnViaToolKind,
+22
View File
@@ -114,6 +114,28 @@ export function isRegistrySelectable(kind: string): boolean {
return nodeRegistry.get(kind)?.capabilities.selectable !== undefined
}
/**
* Kinds whose definition declares the `roofAccessory` capability — the roof
* accessories (dormer, chimney, vents, gutter, …) that mount onto a roof
* segment via their own attach tool. Lets host UIs surface a "Features" group
* under the roof category without hardcoding the kind list (the standalone
* editor's Build tab; the roof inspector's add menu). Returned in builtin
* registration order (`packages/nodes/src/index.ts`), which is deterministic.
*
* Call at render time, not module-import time: the registry is populated by
* the host's bootstrap (`loadPlugin`), so a top-level `const` would race it
* and see an empty registry.
*/
export function getRoofAccessoryKinds(): string[] {
const result: string[] = []
for (const [kind, def] of nodeRegistry.entries()) {
if (def.capabilities.roofAccessory !== undefined) {
result.push(kind)
}
}
return result
}
/**
* Kinds whose `def.floorplanScope` matches the requested scope. Used by
* `FloorplanRegistryLayer` to discover building-scoped kinds (e.g.