Files
editor/wiki/architecture/tools.md
T
Wassim SAMADandClaude Opus 4.7 cf07fcaa97 Restructure agent config: unified .agents/skills + wiki/architecture
Replace per-tool rule trees (.cursor/rules, .claude/rules, .codex/rules)
with a single wiki/architecture/ source — 11 pages + README — readable
as plain markdown by any agent. Canonical skills live in .agents/skills/;
.claude/skills, .cursor/skills, .codex/skills are directory symlinks.

AGENTS.md is the entrypoint (rewritten as a lean overview, no per-tool
path lists). CLAUDE.md, GEMINI.md, and .github/copilot-instructions.md
all point to it.

Add open-pr skill (uses .github/pull_request_template.md as the source
of truth for the PR body) and remove the dangling .claude/CLAUDE.md
relative symlink.

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

3.9 KiB

Tools

Editor tools structure in apps/editor.

Applies to: apps/editor/components/tools/**.

Tools are React components that capture user input (pointer, keyboard) and translate it into useScene mutations. They live exclusively in apps/editor/components/tools/.

Lifecycle

ToolManager reads useEditor (phase + mode + tool) and mounts the active tool component. When the tool changes, the old component unmounts, cleaning up any transient state.

See apps/editor/components/tools/tool-manager.tsx.

Tool Categories by Phase

Site

  • site-boundary-editor — draw/edit property boundary polygon

Structure

  • wall-tool — draw walls segment by segment
  • slab-tool + slab-boundary-editor + slab-hole-editor
  • ceiling-tool + ceiling-boundary-editor + ceiling-hole-editor
  • roof-tool
  • door-tool + door-move-tool
  • window-tool + window-move-tool
  • item-tool + item-move-tool
  • zone-tool + zone-boundary-editor

Furnish

  • item-tool — place furniture

Shared utilities

  • polygon-editor — reusable boundary/hole editing logic
  • cursor-sphere — 3D cursor visualisation

Pattern

// apps/editor/components/tools/my-tool/index.tsx
import { useScene } from '@pascal-app/core'
import { useEditor } from '../../store/use-editor'

export function MyTool() {
  const createNode = useScene(s => s.createNode)
  const setTool = useEditor(s => s.setTool)

  // Pointer handlers mutate the scene store directly.
  // No local geometry — use a renderer for any preview mesh.

  return (
    <mesh onPointerDown={handleDown} onPointerMove={handleMove}>
      {/* ghost / preview geometry only */}
    </mesh>
  )
}

Rules

  • Tools mutate useScene for committed changes and useLiveTransforms for ephemeral drag state. A tool's end-of-interaction write (click-to-commit, release-to-commit) goes to useScene and is captured in undo history. Per-mouse-move previews go to useLiveTransforms so history and subscribers aren't spammed.
  • Live-drag exception for direct mesh transforms. During an active drag a tool may apply a transform offset directly to sceneRegistry.nodes.get(id).position/rotation/scale when and only when the same offset is mirrored into useLiveTransforms for that node. This exception exists because the 3D renderers don't reconcile useLiveTransforms onto mesh.position yet; once a LiveTransformSystem does that, this exception goes away. Conditions:
    • The mesh offset must mirror the useLiveTransforms entry (same delta on both), so anything reading useLiveTransforms sees the same preview as the 3D view.
    • The offset must be cleared on tool unmount, cancel, and commit — both mesh.position.set(0, 0, 0) and useLiveTransforms.clear(id).
    • The tool must not generate or mutate geometry in this path — only transform writes. Geometry generation still belongs in a core system.
  • No business logic in tools — delegate geometry/constraint rules to core systems.
  • Preview geometry is local — transient meshes shown while a tool is active live in the tool component, not in the scene store.
  • Clean up on unmount — remove any pending/incomplete nodes and any live transforms/mesh offsets when the tool unmounts.
  • Tools must not import from @pascal-app/viewer — use the scene store and core hooks only. sceneRegistry is exported from @pascal-app/core and is the allowed door into the Three.js graph for the narrow purposes above.
  • Each tool should handle a single, well-scoped interaction. Split complex tools (e.g. "draw + move") into separate components selected by useEditor.

Adding a New Tool

  1. Create apps/editor/components/tools/<name>/index.tsx.
  2. Register the tool in ToolManager under the correct phase and mode.
  3. Add the tool identifier to the useEditor tool union type.
  4. If the tool requires new node types, add schema + renderer + system first.