# Finish and harden the editor

## What you will make

Review the completed 960 by 600 workflow editor: selected cards glow, dragging and panning preserve their separate state, wheel zoom stays anchored, and keyboard controls remain available.

## How it works

The final source puts pure model and coordinate helpers before the components that consume them. `updateNodePosition`, `rootToWorkspace`, and `zoomAroundPoint` make the mutable-looking gestures ordinary immutable model updates. [useConst](/reference/hooks/use-const/) owns the expensive static grid path for the mounted `GridLayer`; each `NodeCard` owns its drag ref and cursor state, while `Workspace` separately owns its pan ref and cursor state.

## Build the change

Each local terminator clears a gesture only when that exact pointer owns it. Pointer up, cancellation, and capture loss all pass through the corresponding terminator. Removing a card unmounts the component that owns its drag session and cursor, so application ownership disappears naturally before any later pointer-up; it does not need a synthetic loss callback. There is deliberately no [useReaction](/reference/hooks/use-reaction/): this editor owns no external resource requiring signal-driven synchronization. The refs are appropriate because they represent in-flight pointer sessions, not visual state.

Stable card and edge keys preserve identity among their same parent siblings. Reparenting or changing a key remounts a component, which is why the source keeps the component tree deliberate. [useRef](/reference/hooks/use-ref/) and [state and lifecycle](/guides/state-and-lifecycle/) explain those ownership boundaries.

## Predict and try

Change `INITIAL_NODES`—for example, move Build or revise a subtitle. Predict that edges, selection, dragging, focus bounds, and filter placement derive from the model rather than from inspected pixels.

## Check your result

Select and drag Idea outside its initial card, pan empty workspace, zoom around a pointer, then reset only the view. Tab to Sketch and activate it. Dispose the controller twice: later browser events do not repaint. If a render fails, Pibbl releases runtime-owned resources and refs; pixels painted earlier in that frame are not rolled back.

## What you learned

This tutorial is complete, not a production diagramming product. Browser Canvas and `Path2D` behavior remain authoritative; nonempty filters and retained layers require their browser capabilities. Production work still needs an application-owned representation of the same nodes and relationships, synchronized with the Canvas model and offering equivalent selection, traversal, activation, editing, and relationship operations to assistive technology. Do not bolt on a separate keyboard implementation that can diverge from the Canvas actions; route both surfaces through the same model commands. Persistence, undo, recovery UX, and product-specific testing also remain application work. Review [focus and keyboard navigation](/guides/focus-and-keyboard/) and [style and draw](/guides/style-and-draw/) before extending it.

[Open the interactive workbench](/playground/#/workbench/diagram-editor-finished)

## Implementation guidance for agents

Read the [Authoring, signals, and lifecycle companion](/agents/topics/lifecycle/) for ownership, adaptation, failure modes, and verification. [Agent start](/agents/) provides the version-selection workflow.

## Documentation version

Documentation built with @pibbl/core 0.0.2, revision 2dccb19. ALPHA — NOT FOR PRODUCTION USE.
