The MEP run/fitting/terminal tools anchored their cursor readout pill at
~+0.35m above the placement point, so with the tall CursorSphere line
(badge at +2.7m) the pill sat right on the cursor and overlapped it,
especially when zoomed in. Editor main (#438) already raised the duct
pill to +1.45m; this long-lived branch predates that merge. Bring every
MEP tool that uses the tall cursor onto the same +1.45m anchor:
duct/pipe/liquid/lineset runs, duct/pipe fittings, and duct-terminal.
hvac-equipment (height-aware anchor) and pipe-trap (no cursor line) are
left as-is — their HUDs already clear the ghost.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The 5 bespoke MEP movers (duct/pipe-segment, liquid-line, lineset, duct-fitting)
now read the active snapping mode (isGridSnapActive / isMagneticSnapActive)
instead of shiftKey=bypass. The moving scope already carries the node
(setMovingNode → begin('moving')), so the per-kind context resolves with no
extra wiring. Grid and alignment are now independent reads.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Migrate all 9 MEP kinds' placement tools onto the unified snapping model:
declare snapProfile ('item' for point-placed hvac-equipment / duct-terminal /
duct-fitting / pipe-fitting / pipe-trap; 'structural' for directional runs
duct-segment / pipe-segment / liquid-line / lineset), and replace the legacy
shiftKey-bypass reads with mode-driven isGridSnapActive / isMagneticSnapActive /
isAngleSnapActive. For runs the 45° lock becomes the cyclable 'angles' mode;
Alt stays the vertical-riser modifier (run drafting has no validity gate to
force). Port mating gated on "mode != off". Dropped stale "⇧ smooth/free" hints.
The bespoke MEP move-tool/selection (endpoint) tools stay on the legacy model —
they use setMovingNode(null) so no moving-scope context resolves yet; migrating
them needs scope-wiring first (follow-up).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Generify the snapping/modifier HUD off the FSM scope and node declarations
instead of wall-creation-shaped, leaking pills.
- Per-context snapping (`snappingModeByContext`, persisted): wall / item /
polygon mode-sets with exclusive modes (grid | lines | angles | off), each
doing exactly what its chip says. Context is node-declared via the new
`NodeDefinition.snapProfile` ('item' | 'structural'); the resolver maps
(profile × action) → context with no per-kind switch.
- Scope-driven HUD: helper-manager reads the interaction scope; reshaping
(endpoint/curve/boundary) and item move get their own chip, no select-hint
leak. Rotate R/T rounds to 45°; Alt = force-place only (hidden for
structural kinds); Shift = cycle everywhere.
- Slab/ceiling drafting: Shift=cycle, mode-aware grid/angle, Enter finishes
(minDraftVertices); polygon boundary vertex/edge drag begins a reshaping
scope. Fix grid/angle being ignored on boundary edit + slab creation:
make resolveSurfacePlanPointSnap exclusive (alignment gated on magnetic) so
grid/angles keep the snapped fallback instead of the raw cursor.
- Painter application scope: node-derived (single/object/matching/room) from
the hovered node, cyclable via Shift, single-source HUD chip.
- Remove the redundant GridSnapControl from view-toggles (grid step lives in
the contextual HUD now).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The editor's /icons assets were ~10MB of oversized PNGs (a single
toolbar icon up to 1.4MB). Convert every non-PWA icon to WebP (quality
92) and repoint all /icons/*.png references to .webp across
packages/editor, packages/nodes, and apps/editor. PWA/platform icons
(apple-touch-icon, icon-192, icon-512) stay PNG.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>