diff --git a/CHANGELOG.md b/CHANGELOG.md
index 33e7e26..db8be4e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,10 +10,14 @@
TypeScript contracts and a common editor projection.
- Native shapes, paths, groups, frames, cards, images, layers, assets, semantic metadata,
relationships, built-in stencils, and deterministic layout operations.
-- Canvas editing with snapping, constraints, nested selection, path anchors and handles, connection
- routing, duplicate-and-connect, typography controls, and a searchable command palette.
-- Static SVG import and export with native geometry, hierarchy, transforms, compound fills,
- embedded images, retained source assets, and warnings for omitted features.
+- Canvas editing with snapping, constraints, nested selection, path anchors and handles,
+ duplicate-and-connect, typography controls, and a searchable command palette.
+- Shared path metrics and straight, curved, and orthogonal connectors with tangent-aware
+ arrowheads, path-relative labels, hit testing, and deterministic export.
+- Boolean paths, gradient paints, clips, masks, filters, variable-width strokes, and text on path,
+ with editor controls and SVG round-trip coverage.
+- SVG import and export with editable native geometry, hierarchy, transforms, compound fills,
+ gradients, effects, text paths, embedded images, sanitized fallbacks, and deterministic workflows.
- Excalidraw and Obsidian Canvas import and export, plus deterministic SVG and PNG rendering.
- CLI workflows for inspection, queries, validation, structured mutations, dry runs, rendering,
schemas, live desktop sessions, and machine-readable output.
@@ -28,6 +32,8 @@
- Consolidated the web and desktop editors into shared UI, runtime, DOM input, renderer, and core
packages.
+- Unified creation styles across editor, CLI, MCP, stencils, and starter documents, with neutral
+ defaults resolved against light and dark canvases.
- Moved canonical browser state and committed geometry operations to Rust while keeping interaction
previews in TypeScript.
- Unified SVG parsing, transactions, and rendering across desktop, web, CLI, and MCP.
diff --git a/ROADMAP.md b/ROADMAP.md
index 39529b7..c651bfc 100644
--- a/ROADMAP.md
+++ b/ROADMAP.md
@@ -10,150 +10,40 @@ primitives should support diagramming, sketching, spatial thinking, visual
collection, and agent-assisted editing without separate internal systems for
each workflow.
-The roadmap prioritizes stronger primitives, interoperability, correctness,
-editor usability, and performance before large libraries of templates or
-format-specific content.
+The roadmap prioritizes interoperability, editor usability, distribution, and
+performance before large libraries of templates or format-specific content.
-## Current: reliable core editor
-
-### Visual defaults and editor polish
-
-A new document should look intentional without requiring immediate styling.
-
-Centralize creation defaults shared by the editor primitives and remove
-hard-coded styling from individual tools. Use restrained neutral styling for
-ordinary geometry and reserve Inkfinite's accent for interaction, emphasis,
-and relationships.
-
-The editor should have coherent defaults for shapes, frames, connectors, pen
-strokes, text, Markdown, and cards across light and dark appearances. Visual
-regression fixtures should make accidental changes to those defaults visible.
-
-### Connector geometry
-
-Treat arrows as semantic relationships whose resolved visual geometry uses the
-same native path representation as other Inkfinite vectors.
-
-Straight, curved, and orthogonal routing should resolve bindings, waypoints,
-and routing parameters into ordinary path geometry. Rendering, SVG export,
-bounds, hit testing, labels, and arrowheads should consume that resolved path
-rather than maintaining separate connector approximations.
-
-Curved arrows should retain compact editable routing state such as bend and
-waypoint information rather than persisting sampled polylines. Arrowheads
-should follow the tangent of the resolved path, and labels should be positioned
-by distance and normal offset along it.
-
-Shared path metrics should provide length, point and tangent at distance,
-nearest-point queries, flattening, and trimming. Keep the canonical geometry
-implementation in Rust and expose it through the existing editor bindings so
-interactive rendering and headless export cannot diverge.
-
-This path-aware connector work should also provide the geometry foundation for
-text on path and other future features that attach content or markers to vector
-paths.
-
-### SVG round-trip
-
-Inkfinite has one validated Rust SVG pipeline across desktop, web, CLI, and
-MCP. It maps supported geometry and embedded images into native shapes,
-preserves hierarchy, transforms, and compound fills, retains source assets,
-and reports unsupported content.
-
-Imported descendants are ordinary document records available to editor, CLI,
-and MCP operations. Native and fallback fixtures cover save/reopen,
-edit/export, undo/redo, CRDT merge, active-document import, and explicit
-creation of a new document from SVG.
-
-Interchange uses deterministic native representation where Inkfinite
-understands the content and sanitized static fallback content where it does
-not.
-
-See [SVG import](apps/web/src/content/docs/development/svg-import.md) and
-[native path geometry](apps/web/src/content/docs/development/native-path-geometry.md).
-
-### Richer interoperability
+## Current: richer interoperability
JSON Canvas support should continue to map external data onto ordinary
-Inkfinite objects, relationships, frames, cards, and assets rather than
-creating a parallel document system.
+Inkfinite objects, relationships, frames, cards, and assets. Mixed-document
+fixtures should exercise native geometry, imported SVG, raster assets, links,
+cards, frames, and relationships together.
-Mixed-document fixtures should exercise native geometry, imported SVG, raster
-assets, links, cards, frames, and relationships together.
-
-Mermaid and D2 are candidates for structured import only if their graphs map
-cleanly onto Inkfinite's native objects and relationships. Avoid a separate
-format-specific rendering architecture.
-
-## Next: stronger vector primitives
-
-Once native SVG round-trip and ordinary editing workflows are dependable,
-expand the vector model itself.
-
-### Boolean geometry
-
-Union, intersection, difference, and exclusion operate on selected native paths
-as one undoable editor command. Rust resolves transformed path geometry through
-one boolean pipeline, stores the result as ordinary native line segments, and
-keeps the first path's transform, paint, metadata, and fill rule. Generated
-geometry survives SVG export and import.
-
-### Rich paint
-
-Linear and radial gradients extend flat colors. Gradient stops, opacity,
-coordinate units, transforms, and spread behavior live in the canonical
-document representation and work through rendering, serialization, SVG
-interoperability, editing, CLI inspection, and export.
-
-### Clipping, masks, and effects
-
-Inkfinite supports editable path clipping, alpha and luminance masks composed
-from path subpaths, and an initial SVG filter subset: blur, colour adjustments,
-opacity, and drop shadows. The editor, canvas renderer, deterministic SVG
-exporter, and canonical document projection use the same effect properties.
-Unsupported SVG effect primitives remain available as sanitized static fallback
-content rather than being discarded.
-
-### Expressive strokes
-
-Freehand strokes store optional hardware pressure samples and editable width
-profiles along normalized path distance. Direct selection exposes width handles
-separately from the sampled path points, while outline rendering uses document
-units so zoom changes do not change the result. SVG export writes the rendered
-outline as a filled path when a variable profile is present.
-
-### Text on path
-
-Text shapes can reference native path shapes without copying their geometry.
-The attachment stores a local distance, alignment, side, and forward or reverse
-direction, then uses the shared path metrics for glyph placement, bounds, hit
-testing, and offset handles. Editing the path updates the attached text while
-preserving the text record; the selection controls can attach, detach, and
-change its layout settings.
-
-The SVG pipeline imports `textPath` references, including paths declared in
-`defs`, and exports native references with the supported alignment, side,
-offset, and reversal settings.
+Mermaid and D2 are candidates for structured import where their graphs map
+cleanly onto Inkfinite's native objects and relationships. Imported nodes,
+edges, labels, groups, and supported styles should remain editable and use the
+shared graph-layout pipeline. Unsupported constructs need clear warnings and a
+predictable fallback rather than a separate format-specific renderer.
## Next: web installation and export workflows
### Clipboard interoperability
-Treat the clipboard as another interchange surface.
-
-Selections and documents should be copyable as canonical SVG markup. Raster
-exports should be copyable as PNG when platform clipboard APIs permit it, with
-predictable browser fallbacks.
+Treat the clipboard as another interchange surface. Selections and documents
+should be copyable as canonical SVG markup. Raster exports should be copyable
+as PNG when platform clipboard APIs permit it, with predictable browser
+fallbacks.
### PWA and offline operation
Make the existing local-first web editor installable rather than creating a
separate offline product.
-The PWA should cache the application shell, WASM runtime, and required static
-assets while continuing to use Inkfinite's normal local document repository.
-Creating, editing, reopening, and exporting local documents should continue to
-work without a network connection.
+The PWA should cache the application shell, WebAssembly runtime, and required
+static assets while continuing to use Inkfinite's normal local document
+repository. Creating, editing, reopening, and exporting local documents should
+continue to work without a network connection.
Application updates must be explicit enough that cached application code does
not become an invisible second deployment state.
@@ -168,19 +58,18 @@ platform matrix, and CLI/MCP binary distribution. Keep a documented manual
release checklist and small reproducible build scripts.
GitHub Releases should eventually contain desktop, CLI, and MCP artifacts with
-checksums. Publish the intended Rust packages to crates.io.
-
-Automation can be introduced where manual repetition becomes expensive; it is
-not a prerequisite for the first useful release candidate.
+checksums. Publish the intended Rust packages to crates.io. Add automation where
+manual repetition becomes expensive rather than making it a prerequisite for
+the first useful release candidate.
## Later: reusable content
### User libraries
Built-in stencils already demonstrate reusable ordinary Inkfinite objects.
-Later library work should let users save, find, insert, update, and remove
-their own selections while retaining nested content, assets, relationships,
-and semantics.
+Later library work should let users save, find, insert, update, and remove their
+own selections while retaining nested content, assets, relationships, and
+semantics.
Libraries remain ordinary document fragments validated by the shared native
document APIs.
diff --git a/TODO.md b/TODO.md
index 8b5d917..42df817 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,48 +1,5 @@
# Inkfinite TODO
-## Editor defaults
-
-Make newly created content look deliberate before the user changes any styling.
-
-- [x] Move creation-time visual defaults into a shared style policy instead of
- hard-coding colors independently in editor tools and UI controls
-- [x] Define defaults for rectangles, ellipses, frames, lines, arrows, pen
- strokes, text, Markdown, and cards
-- [x] Use neutral shape styling by default and reserve the Inkfinite accent for
- selection, arrows, emphasis, and interactive state
-- [x] Increase the default rectangle corner radius and define distinct frame
- styling so frames do not look like ordinary shapes
-- [x] Remove light-canvas assumptions from text and Markdown defaults
-- [x] Resolve automatic creation colors against the document/page canvas at creation time
- and persist the resulting explicit colors; keep application theme colors out of
- canonical document rendering
-- [x] Ensure creation through the editor, CLI, MCP, stencils, and starter
- documents uses the same default style policy where appropriate
-- [x] Add light- and dark-canvas visual fixtures covering every built-in
- primitive
-- [x] Add screenshot regression coverage for a representative mixed document
-
-## SVG round-trip
-
-Run representative native and fallback SVG fixtures through each complete
-document workflow. Compare normalized structure where representation matters
-and rendered output where visual fidelity matters.
-
-- [x] Offer creation of a new document from SVG as a separate editor action
-- [x] Verify and fix TypeScript editor traversal of imported root containers so
- existing child records can be selected, edited, reparented, and deleted
- independently
-- [x] Test import through save/reopen, edit/export, undo/redo, and CRDT merge
-- [x] Test active-document import and explicit new-document creation end to end
-- [x] Test imported child shapes through editor selection and targeted CLI
- query and mutation
-- [x] Verify native vectors export without rasterization
-- [x] Verify nested transforms export deterministically
-- [x] Verify compound fill rules survive import and export
-- [x] Add sanitized static fallback content for unsupported visuals and verify
- that it remains visually stable
-- [x] Add deterministic round-trip fixtures for these workflows
-
## Richer interoperability and embeds
- [ ] Extend the existing TypeScript JSON Canvas round-trip for new card,
@@ -61,136 +18,6 @@ and rendered output where visual fidelity matters.
- [ ] Add end-to-end import, edit, and export comparisons for representative
mixed-format documents
-## Connector geometry
-
-Make arrows semantic connectors backed by the same geometry operations used
-for native paths. Avoid separate approximations for rendering, export,
-selection, labels, and arrowheads.
-
-### Shared resolved geometry
-
-- [x] Define a resolved arrow geometry representation backed by native
- `PathGeometry`
-- [x] Resolve bound endpoints, explicit waypoints, and routing configuration
- before rendering or interaction code consumes the arrow
-- [x] Make straight arrows resolve to ordinary line path segments without
- changing existing document behavior
-- [x] Move canonical arrow geometry resolution into Rust and expose it through
- the existing editor/WASM bindings
-- [x] Use the same resolved geometry for interactive rendering and headless SVG
- export
-- [x] Replace arrow bounds derived only from stored points with bounds from the
- resolved path, including Bézier extrema
-- [x] Add deterministic fixtures comparing interactive and exported geometry
- for free, bound, transformed, and multi-point arrows
-
-### Path metrics
-
-- [x] Add shared path flattening with a geometric tolerance rather than fixed
- samples per curve
-- [x] Add path length and point-at-distance queries
-- [x] Add tangent-at-distance queries for line, quadratic, and cubic segments
-- [x] Add nearest-point and distance-along-path queries for hit testing and
- snapping
-- [x] Add path trimming by start and end distance
-- [x] Reuse the shared path metrics for native path and arrow hit testing
- instead of maintaining separate curve-sampling implementations
-- [x] Add deterministic tests for lines, quadratic curves, cubic curves,
- transformed paths, and degenerate segments
-
-### Curved arrows
-
-- [x] Add persistent curved-arrow bend state without storing sampled curve
- points
-- [x] Resolve two-point curved arrows to native quadratic Bézier geometry
-- [x] Add a direct-manipulation bend handle with a well-defined straight-arrow
- zero state
-- [x] Preserve bound endpoints while the bend handle is edited
-- [x] Define multi-point curved-arrow behavior as rounded waypoint routes
- rather than an unconstrained spline
-- [x] Render native quadratic and cubic path segments directly instead of
- drawing sampled curves with `lineTo`
-- [x] Add undo/redo and save/reopen coverage for curved-arrow edits
-- [x] Add visual fixtures for positive, negative, zero, short, long, bound, and
- multi-point curves
-
-### Path-aware arrowheads and labels
-
-- [x] Orient start and end arrowheads from the tangent of the resolved path
- rather than the first or last sampled line segment
-- [x] Define arrowhead geometry independently from shaft geometry so additional
- head styles can be added without changing routing
-- [x] Trim the visible shaft where required so filled arrowheads do not overlap
- the path beneath them
-- [x] Position arrow labels by distance along the resolved path
-- [x] Apply label offset along the local path normal rather than a global axis
-- [x] Keep label placement stable when bindings, bend, or orthogonal routing
- change
-- [x] Add hit-test and visual coverage for arrowheads and labels on straight,
- curved, and orthogonal routes
-
-## Advanced vector editing
-
-### Boolean paths
-
-- [x] Define union, intersection, difference, and exclusion operations over
- Inkfinite's native path representation
-- [x] Preserve transforms and fill rules when combining selected paths
-- [x] Expose boolean operations through editor commands and selection controls
-- [x] Make each boolean operation one undoable document transaction
-- [x] Verify generated geometry through SVG import/export round trips
-- [x] Add deterministic fixtures for overlapping, nested, compound, and
- self-intersecting paths
-
-### Gradient fills and strokes
-
-- [x] Extend the paint representation beyond flat colors with linear and radial
- gradients
-- [x] Represent gradient stops, positions, transforms, spread behavior, and
- opacity in canonical document data
-- [x] Render gradients consistently in the interactive renderer and exports
-- [x] Import and export supported SVG gradients without flattening them
-- [x] Add gradient controls for stop creation, deletion, position, color, and
- opacity
-- [x] Add fixtures covering transformed gradients and gradient inheritance
-
-### Clips, masks, and filters
-
-- [x] Define which SVG clip, mask, and filter constructs Inkfinite can represent
- natively
-- [x] Preserve unsupported constructs as sanitized fallback content instead of
- silently discarding them
-- [x] Add native clip-path editing before exposing more general mask editing
-- [x] Add basic mask composition once clip-path behavior is stable
-- [x] Define the initial editable filter subset rather than attempting the full
- SVG filter graph at once
-- [x] Verify deterministic import, editing, save/reopen, and SVG export
-
-### Variable-width strokes
-
-- [x] Extend stroke data with a width or pressure profile along the path
-- [x] Capture pointer pressure where available without making pressure input
- mandatory
-- [x] Add editing for width points independently of ordinary path nodes
-- [x] Render width profiles consistently at different zoom levels
-- [x] Define SVG export behavior, including conversion to outlined paths when
- required
-- [x] Add pen, edit, undo/redo, and export fixtures
-
-### Text on path
-
-Build text-on-path behavior on the shared path metrics introduced for
-path-aware connectors rather than creating a second path-placement system.
-
-- [x] Define the relationship between a text object and its supporting path
-- [x] Represent text position as an offset along the supporting path
-- [x] Reuse shared path length, point, tangent, and normal queries for layout
-- [x] Support direction, alignment, side, and path reversal
-- [x] Keep the path independently editable without destroying attached text
-- [x] Add direct manipulation for text offset along the path
-- [x] Import and export representative SVG `textPath` content
-- [x] Add undo/redo and round-trip fixtures
-
## Clipboard and export workflows
### SVG as code
@@ -227,9 +54,6 @@ path-aware connectors rather than creating a second path-placement system.
## Release and distribution
-Keep releases understandable and reproducible before investing in extensive
-automation.
-
- [ ] Decide which Rust crates are public API and which remain workspace-only
- [ ] Define crates.io package metadata and publication order for publishable
crates
@@ -247,8 +71,6 @@ automation.
## Agent skills
-Revisit skills after the CLI and MCP surfaces have stabilized.
-
- [ ] Inventory existing skills and the commands or workflows each depends on
- [ ] Remove duplicated instructions that are better represented by CLI or MCP
capabilities
diff --git a/crates/inkfinite-core/tests/svg_import_fixtures.rs b/crates/inkfinite-core/tests/svg_import_fixtures.rs
index 300a3fe..fd18332 100644
--- a/crates/inkfinite-core/tests/svg_import_fixtures.rs
+++ b/crates/inkfinite-core/tests/svg_import_fixtures.rs
@@ -44,6 +44,12 @@ const VALID_FIXTURES: &[(&str, &str, usize, usize)] = &[
0,
1,
),
+ (
+ "night garden illustration",
+ include_str!("../../../fixtures/svg-import/illustrations/night-garden.svg"),
+ 2,
+ 10,
+ ),
];
fn node_counts(group: &inkfinite_core::svg_import::SvgGroup) -> (usize, usize) {
diff --git a/crates/inkfinite-wasm/tests/svg_import_fixtures.rs b/crates/inkfinite-wasm/tests/svg_import_fixtures.rs
index 7845376..633905b 100644
--- a/crates/inkfinite-wasm/tests/svg_import_fixtures.rs
+++ b/crates/inkfinite-wasm/tests/svg_import_fixtures.rs
@@ -14,6 +14,10 @@ const FIXTURES: &[(&str, &str)] = &[
"compound path",
include_str!("../../../fixtures/svg-import/compound-paths/ring.svg"),
),
+ (
+ "night garden illustration",
+ include_str!("../../../fixtures/svg-import/illustrations/night-garden.svg"),
+ ),
(
"unsupported features",
include_str!("../../../fixtures/svg-import/unsupported/feature-matrix.svg"),
diff --git a/fixtures/svg-import/README.md b/fixtures/svg-import/README.md
index 753abed..fb32e39 100644
--- a/fixtures/svg-import/README.md
+++ b/fixtures/svg-import/README.md
@@ -7,9 +7,14 @@
- `nested-groups/` checks group-local transforms, inherited paint, and opacity.
- `compound-paths/` checks repeated subpaths, elliptical arcs, and even-odd
fill rules.
-- `gradients/` checks transformed linear gradients, radial gradients, inherited
- stops, spread behavior, and stop opacity.
-- `unsupported/` checks warnings for omitted features and active content.
+- `gradients/` uses a layered twilight landscape to check transformed linear
+ gradients, radial gradients, inherited stops, spread behavior, and stop
+ opacity in a complete composition.
+- `illustrations/` combines ordinary geometry, curves, inherited group styles,
+ local transforms, and opacity in an asset that is useful beyond a single
+ syntax regression.
+- `unsupported/` uses a presentable supported fallback while checking warnings
+ for omitted features and active content.
- `malformed/` checks that invalid XML, numbers, paths, and transforms fail
before a partial import is returned.
diff --git a/fixtures/svg-import/compound-paths/ring.svg b/fixtures/svg-import/compound-paths/ring.svg
index c0f821b..fd2af55 100644
--- a/fixtures/svg-import/compound-paths/ring.svg
+++ b/fixtures/svg-import/compound-paths/ring.svg
@@ -1 +1,3 @@
-
+
diff --git a/fixtures/svg-import/gradients/gradient-inheritance.svg b/fixtures/svg-import/gradients/gradient-inheritance.svg
index 143b9eb..fe90fe3 100644
--- a/fixtures/svg-import/gradients/gradient-inheritance.svg
+++ b/fixtures/svg-import/gradients/gradient-inheritance.svg
@@ -1,4 +1,4 @@
-