Architecture and ownership
The framework separates authored content, commands, derived geometry and Flutter presentation. A domain kit composes these layers instead of replacing their physical editing rules.
| Package | Responsibility |
|---|---|
vyuh_diagram_types | Immutable document values: shapes, connectors, endpoints, text, styles and geometry. |
vyuh_diagram_engine | Canonical state, transactions, history, creation, connections and editing commands. |
vyuh_diagram_layout | Shape registry, admission and target-independent resolved geometry. |
vyuh_diagram_editor | Flutter input, painting, text-input bridge, selection chrome, camera and extensions. |
vyuh_diagram_codecs | Versioned serialization adapters. |
vyuh_diagram_kit | Public composition of a drawing session and embeddable surface. |
One document, derived geometry
The document stores durable authored intent. A registered definition explains how that intent becomes an outline, text regions, ports and capability declarations. The resolver produces the scene consumed by painting and interaction.
The store validates publication and coordinates state with its resolved scene. Editing code dispatches commands and transactions; it should not mutate resolved objects or maintain alternate hit-test geometry inside a host widget.
Framed mechanics and semantic properties
FramedElement holds rectangular placement (frame, rotation), parent ownership and z-order. Its immutable copyWith preserves concrete content. It declares no fill, border, ports, image, clipping or text properties. ShapeElement supplies shape-specific properties separately. Its sibling TextElement supplies a required rich-text story, sizing intent and content scroll offset, with no shape decoration fields.
ResolvedFramedElement shares coordinate transforms, outline hit testing and selection geometry. ResolvedShape adds shape layers, ports and its definition. These bases let semantic element types share mechanics without inheriting shape appearance. Standalone text resolves as ResolvedTextElement through ShapeRegistry.text; it never constructs a temporary shape. Codec v12 stores it as kind: text and migrates compatible older text-shape records.
Shared transform operations
DiagramElementTransform in the pure Dart engine provides immutable translation and rotation for both headless commands and interactive previews. DiagramResizeTransform owns the shared affine resize mapping, using resolved frames captured at gesture start so auto-grown text and other elements agree. It changes shape placement or connector free geometry while preserving text, local paths, styles, and bound endpoint identities. The scene resolver derives the resulting bound connection geometry.
These operations return an element; they do not publish a document. Commands submit their results through validated transactions, so selection, scene revision, and undo/redo retain the same owner. Ownership closure and permissions belong to the calling command, rather than the geometric operation.
Definitions versus instances
ShapeDefinition owns the reusable production for a type. ShapeElement owns one instance's frame, style, text and other authored values. ShapeRegistry rejects duplicate definitions and requires explicit overrides.
Ports retain stable IDs and declarative placement. Bound connector endpoints refer to element and optional port identity; they do not persist a second independently editable copy of the attachment's world position.
Presentation versus content
Camera navigation and runtime effects are presentation state. They do not rewrite document geometry. Custom overlays consume resolved geometry and the same camera projection as the editor.
Host-side domain state must make its authority explicit. A workflow service may own execution records while a canvas projects those records onto stable element IDs. Canvas animation is not evidence that an operation executed successfully.
Extension boundary
Prefer a new registered shape, tool, effect or policy when the existing grammar supports the behavior. Extend the shared grammar when a reusable primitive is missing. Avoid branching editor internals on an application-specific node type.
The intended model is broader than the completed implementation. See the capability status for current limitations around labels, embedded widgets, locking, output and performance proof.
Why engine and layout are separate
The engine answers what changed: create, connect, mutate, select, admit, undo and redo. Layout answers where it is: transform nesting, outline paths, connector routing, text regions, ports, clipping, bounds and spatial queries. Here, layout means geometry resolution; it is not an automatic flowchart-arrangement service.
Both packages are pure Dart. The engine depends on layout so a proposed edit is resolved and validated before one atomic publication. Layout depends on immutable types, not editor state or Flutter widgets. This lets headless tools and agents use canonical commands and lets geometry be tested independently from pointer input or painting. Agents still need the appropriate text-layout adapter for exact rendered text; the pure-Dart layer alone does not provide Flutter font shaping.
vyuh_diagram_editor is a reusable Flutter adapter, not the demo application. It owns pointer/keyboard/IME handling, painting, focus, chrome and Flutter text shaping. vyuh_diagram_kit composes these into DrawingSession and DrawingCanvas; applications usually start there.
Rendering and output
The same resolved scene should feed interactive painting and output adapters. PNG output exists today through the Flutter renderer. SVG and PDF export are not implemented yet. Shared resolved geometry makes those adapters possible, but vector paths, text/font embedding, images, clipping and unsupported custom content still need explicit format-specific handling. The package split enables that reuse; it does not mean those formats already work.
The target is a complete server-side path: an agent creates and edits canonical content through the engine, layout resolves it without a Flutter application, and output adapters produce shareable artifacts. Deterministic server text shaping and SVG/PDF adapters are required parts of that target. Flutter-backed PNG export is an existing adapter, not the final headless-server solution.