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.
Philosophy: content, rules, commands, projections
A drawing is durable content. A session is the handle through which a host works with that content. A canvas is an interactive projection of it. A domain kit adds meaning to the content without taking over its physical editing mechanics.
The governing rule is one canonical authority, many projections. The saved JSON, visible canvas, minimap, SVG and agent tools must refer to the same authored identities and admitted revision. None should maintain an independently editable copy of the diagram or a competing geometry model.
The conceptual stack
Like a protocol stack, each layer adds a responsibility and relies on contracts below it. This is a conceptual stack, not a claim that package dependencies form one straight chain: layout lives inside the engine, and output adapters consume its resolved geometry directly.
| Layer, from foundation upward | Core objects | Responsibility |
|---|---|---|
| 1. Durable vocabulary | DiagramDocument, elements, endpoints, text stories, geometry/style values | Describe what was authored using immutable values and stable identities. |
| 2. Grammar and geometry | ShapeRegistry, ShapeDefinition, DiagramResolver, ResolvedScene | Declare what each type supports and derive outlines, routes, text lines, ports, clips and hit geometry. |
| 3. Editing authority | DiagramStore, commands, transactions, interaction leases, publications | Validate proposed edits, resolve them, publish one coherent result and own undo/redo. |
| 4. Host and presentation | DrawingSession, DrawingConfiguration, DrawingCanvas | Provide a convenient host API and project canonical state through Flutter input, painting and native controls. |
| 5. Domain language | Starter registries, workflow operations/work/primitives, domain validators | Give physical nodes and connections application meaning, schemas and domain constraints. |
| 6. Applications and participants | Playground, embedded examples, agent integrations, future collaboration services | Compose a product, supply permissions/transport and invoke semantic commands. |
Persistence and output are adapters beside this stack: JSON encodes canonical content; SVG consumes resolved geometry; the existing PNG adapter uses Flutter. A server agent can enter at the engine layer without mounting the Flutter layer. It still needs a suitable text backend for accurate text geometry.
The object model
text
DiagramDocument (id, revision)
└─ DiagramElement[]
├─ ShapeElement : FramedElement
│ ├─ type → ShapeDefinition in ShapeRegistry
│ ├─ placement, appearance, registered values, optional ports/content
│ └─ optional text story
├─ TextElement : FramedElement
│ └─ required text story, sizing and scroll intent
└─ ConnectorElement
├─ start/end: free point or bound element/port identity
├─ routing and appearance intent
└─ labels → text stories and path positions
TextStory → blocks → formatted runsA framed element shares position, size, rotation, parent ownership and stacking. It does not inherently have a painted fill or border. Standalone text therefore shares rectangular manipulation with shapes while remaining a distinct semantic element. Cards, images, drawings and workflow nodes currently use registered shape productions rather than each introducing a new base element class.
A definition is a reusable type contract; an element is one authored instance. A port is an attachment identity belonging to a node. A connector refers to that identity, and layout derives the visible contour termination. A label belongs to its connector rather than being an unrelated floating text box.
Text is shared content machinery. Standalone text, shape regions and connector labels use stories, blocks and runs under the applicable text grammar. Single-line regions constrain that machinery; they do not introduce a second text editor.
How an edit travels
text
Pointer / keyboard / host command / agent proposal
→ semantic command or transaction
→ canonical store admission + geometry resolution
→ one revision-matched publication
→ canvas, observers, history and output consumersA drag uses a lease to preview from a stable starting publication, then commits one edit or cancels. Preview is transient; it does not create an undo entry for every pointer movement. Fixed document validators enforce domain invariants; replaceable hooks express host permissions. Both operate before publication.
Sessions are handles; canvases are surfaces
A normal Flutter host creates one DrawingSession, supplies its document, configuration and registrations, then mounts DrawingCanvas(session: session). Content commands work while detached. Mounted navigation and animation use the canvas's presentation resources. The session owns its store by default; integration.borrowDrawingStore explicitly borrows an externally owned store.
Today a canvas takes one session and a session mounts in one canvas at a time. Two borrowed facades share editor state, including selection, active interaction and history. They are not independent collaborative participants. The intended future collaboration model shares committed content while giving each participant its own selection, preview and permission context; transport, reconciliation and participant-scoped history remain unimplemented. See collaboration.
Domain kits compose the foundation
A workflow operation node is physically a registered shape with text, ports and values. Its domain layer adds an operation identity and input/output schemas. Human work adds response schemas and named outcomes; control primitives add their own configuration and branching contracts. The foundation still owns moving, connecting, selecting, history and output. Runtime execution belongs to the workflow service, not the canvas or its path animations.
The service-map and storyboard starters demonstrate different domain compositions on the same physical contracts. Agents should work through those domain commands and canonical admission, not manipulate pixels or bypass validation by mutating serialized maps. The current workflow kit supports construction and draft input validation; execution and simulation are deferred.
Package ownership
Start with vyuh_diagram_kit for Flutter applications or vyuh_diagram_engine for headless Dart applications. The engine owns the headless application API and its internal command services.
Application entry points
| Package | Responsibility |
|---|---|
vyuh_diagram_kit | Application-facing canvas/configuration facades and integrations over the shared engine session and Flutter renderer. |
vyuh_diagram_engine | Headless Dart API: DrawingSession, transactions, history, shape registry, resolved geometry, routing, spatial queries and adapter-backed output. |
Foundation packages
These are brought in by the application entry points. Add a direct dependency only when importing a package explicitly for advanced integration.
| Package | Responsibility |
|---|---|
vyuh_diagram_types | Immutable document values, text, styles and geometry. |
vyuh_diagram_renderer_flutter | Shared Flutter painting, input/IME, text shaping, camera adapters, caches and mounted session bindings. |
vyuh_diagram_codecs | Document serialization and vector output adapters. |
Optional libraries
| Package | Responsibility |
|---|---|
vyuh_diagram_shapes_flowchart | Flowchart shapes. |
vyuh_diagram_shapes_workflow | Workflow shapes. |
vyuh_diagram_shapes_network | Network shapes. |
vyuh_diagram_shapes_uml | UML shapes. |
vyuh_diagram_workflow | Registry-driven workflow authoring primitives; separate from the workflow shape library. |
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.
Engine, renderer and Kit
The engine owns the shared DrawingSession, semantic editing, transactions, history, observable state and resolved geometry. It remains pure Dart. vyuh_diagram_renderer_flutter owns Flutter painting, pointer/keyboard/IME adapters, focus, text shaping, inspectors, minimaps, overlays and mounted canvas resources. Kit provides thin application-facing facades over this implementation.
The four main SDK packages are Types, Engine, Renderer Flutter and Kit. The workspace also retains three codec packages and five domain packages: 12 SDK libraries in total, plus two example applications. The editor SDK package has been retired; the editor is an example application that composes the SDK.
There is one commercial Kit and no open-source or Free/Pro edition split. Ordinary Flutter hosts import vyuh_diagram_kit.
Rendering and output
The same resolved scene should feed interactive painting and output adapters. PNG output exists today through the Flutter renderer. DiagramSvgScene serializes geometry, transforms, clips, image layers and shadows in pure Dart. session.exportSvg adds Flutter image loading and captures publication identity across asynchronous work. Native SVG text consumes captured line positions, baselines and styles, with host-supplied embedded font faces. The viewer shapes characters; glyph-level parity with Flutter is not guaranteed. This avoids introducing a second paragraph-wrapping authority. PDF output is not supported.