@pixerate/editor 0.113.0 → 0.114.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (124) hide show
  1. package/THIRD_PARTY_NOTICES.md +5 -0
  2. package/dist/animation-editor/index.cjs +60 -54
  3. package/dist/animation-editor/index.cjs.map +1 -1
  4. package/dist/animation-editor/index.d.cts +3 -3
  5. package/dist/animation-editor/index.d.ts +3 -3
  6. package/dist/animation-editor/index.js +14 -8
  7. package/dist/animation-editor/index.js.map +1 -1
  8. package/dist/builtin-CY97ppKM.d.cts +354 -0
  9. package/dist/builtin-CY97ppKM.d.ts +354 -0
  10. package/dist/canvas/index.cjs +3 -3
  11. package/dist/canvas/index.cjs.map +1 -1
  12. package/dist/canvas/index.js +9 -9
  13. package/dist/chunk-26PGXHY7.cjs +753 -0
  14. package/dist/chunk-26PGXHY7.cjs.map +1 -0
  15. package/dist/chunk-2OJACTPR.cjs +929 -0
  16. package/dist/chunk-2OJACTPR.cjs.map +1 -0
  17. package/dist/chunk-3UFGNHSP.js +929 -0
  18. package/dist/chunk-3UFGNHSP.js.map +1 -0
  19. package/dist/{chunk-YGIMK4NX.cjs → chunk-5OVJHMZQ.cjs} +757 -825
  20. package/dist/chunk-5OVJHMZQ.cjs.map +1 -0
  21. package/dist/chunk-643B2HKC.cjs +534 -0
  22. package/dist/chunk-643B2HKC.cjs.map +1 -0
  23. package/dist/chunk-6FCKOS2B.js +534 -0
  24. package/dist/chunk-6FCKOS2B.js.map +1 -0
  25. package/dist/chunk-BRLYOPLT.js +2574 -0
  26. package/dist/chunk-BRLYOPLT.js.map +1 -0
  27. package/dist/{chunk-LQEEDHOP.js → chunk-C6C5PDKU.js} +759 -761
  28. package/dist/chunk-C6C5PDKU.js.map +1 -0
  29. package/dist/{chunk-4KLT7ZYS.cjs → chunk-DFQEX3AM.cjs} +29 -29
  30. package/dist/{chunk-4KLT7ZYS.cjs.map → chunk-DFQEX3AM.cjs.map} +1 -1
  31. package/dist/{chunk-CZOXTEMN.js → chunk-EUVSQ25B.js} +336 -404
  32. package/dist/chunk-EUVSQ25B.js.map +1 -0
  33. package/dist/chunk-G5NMLUVF.cjs +2574 -0
  34. package/dist/chunk-G5NMLUVF.cjs.map +1 -0
  35. package/dist/{chunk-LPYOIVLA.js → chunk-LKCYANMW.js} +3 -3
  36. package/dist/{chunk-W3GXSUSQ.cjs → chunk-LPLCWZTM.cjs} +5087 -1283
  37. package/dist/chunk-LPLCWZTM.cjs.map +1 -0
  38. package/dist/chunk-MMVHBKMM.js +753 -0
  39. package/dist/chunk-MMVHBKMM.js.map +1 -0
  40. package/dist/{chunk-POGBUYWE.cjs → chunk-ST7E3NEE.cjs} +857 -859
  41. package/dist/chunk-ST7E3NEE.cjs.map +1 -0
  42. package/dist/{chunk-7VOHOPLW.cjs → chunk-TCKIXI46.cjs} +59 -59
  43. package/dist/{chunk-7VOHOPLW.cjs.map → chunk-TCKIXI46.cjs.map} +1 -1
  44. package/dist/{chunk-XIOMMGPD.cjs → chunk-TFOPHZ3U.cjs} +1 -1
  45. package/dist/chunk-TFOPHZ3U.cjs.map +1 -0
  46. package/dist/{chunk-Z4BC6M74.js → chunk-UBIXVXPR.js} +2 -2
  47. package/dist/{chunk-BLWCAQCW.js → chunk-VXR7XWMF.js} +4574 -770
  48. package/dist/chunk-VXR7XWMF.js.map +1 -0
  49. package/dist/{chunk-HNS7UHCM.js → chunk-XK5LZKBD.js} +1 -1
  50. package/dist/chunk-XK5LZKBD.js.map +1 -0
  51. package/dist/cms/index.cjs +146 -0
  52. package/dist/cms/index.cjs.map +1 -0
  53. package/dist/cms/index.d.cts +51 -0
  54. package/dist/cms/index.d.ts +51 -0
  55. package/dist/cms/index.js +146 -0
  56. package/dist/cms/index.js.map +1 -0
  57. package/dist/{export-DIkoK8-r.d.ts → export-_1dUKwpF.d.ts} +217 -191
  58. package/dist/{export-CEXEzqBf.d.cts → export-b6NjCZ5-.d.cts} +217 -191
  59. package/dist/image-editor/index.cjs +1779 -1280
  60. package/dist/image-editor/index.cjs.map +1 -1
  61. package/dist/image-editor/index.d.cts +77 -93
  62. package/dist/image-editor/index.d.ts +77 -93
  63. package/dist/image-editor/index.js +740 -241
  64. package/dist/image-editor/index.js.map +1 -1
  65. package/dist/opentype-BZEOSOX3.d.ts +188 -0
  66. package/dist/opentype-Dpe75vL5.d.cts +188 -0
  67. package/dist/paint/index.cjs +4 -4
  68. package/dist/paint/index.d.cts +2 -2
  69. package/dist/paint/index.d.ts +2 -2
  70. package/dist/paint/index.js +3 -3
  71. package/dist/paint-editor/index.cjs +146 -145
  72. package/dist/paint-editor/index.cjs.map +1 -1
  73. package/dist/paint-editor/index.d.cts +9 -8
  74. package/dist/paint-editor/index.d.ts +9 -8
  75. package/dist/paint-editor/index.js +6 -5
  76. package/dist/paint-editor/index.js.map +1 -1
  77. package/dist/raster/index.cjs +53 -3
  78. package/dist/raster/index.cjs.map +1 -1
  79. package/dist/raster/index.d.cts +62 -91
  80. package/dist/raster/index.d.ts +62 -91
  81. package/dist/raster/index.js +59 -9
  82. package/dist/{render-BEriFuqK.d.cts → render-B1K_OoLy.d.cts} +87 -2
  83. package/dist/{render-tjzKaik-.d.ts → render-CrmHOgpo.d.ts} +87 -2
  84. package/dist/{retouch-CEIKR22W.d.cts → retouch-Bxkuc0KH.d.cts} +1 -1
  85. package/dist/{retouch-BXmeSCaB.d.ts → retouch-Do6tBs2H.d.ts} +1 -1
  86. package/dist/{surface-C8RRg_Bh.d.cts → surface-Cn30xIV6.d.cts} +6 -1
  87. package/dist/{surface-C8RRg_Bh.d.ts → surface-Cn30xIV6.d.ts} +6 -1
  88. package/dist/{svg-D_cfkhlB.d.cts → svg-BnRj0q8i.d.cts} +16 -4
  89. package/dist/{svg-wIaKLrU0.d.ts → svg-CmmBFfGT.d.ts} +16 -4
  90. package/dist/{types-DtL_qLFz.d.ts → types-1SUQKDAw.d.ts} +273 -4
  91. package/dist/{types-DyQq-WPY.d.cts → types-yINF17Ta.d.cts} +273 -4
  92. package/dist/vector/index.cjs +12170 -7343
  93. package/dist/vector/index.cjs.map +1 -1
  94. package/dist/vector/index.d.cts +1579 -16
  95. package/dist/vector/index.d.ts +1579 -16
  96. package/dist/vector/index.js +6303 -1476
  97. package/dist/vector/index.js.map +1 -1
  98. package/dist/warp-B39VsOtd.d.cts +348 -0
  99. package/dist/warp-DMN30aY6.d.ts +348 -0
  100. package/dist/zlib-dxQdgHyN.d.cts +95 -0
  101. package/dist/zlib-dxQdgHyN.d.ts +95 -0
  102. package/package.json +6 -1
  103. package/skills/pixerate-image-editor/SKILL.md +12 -36
  104. package/skills/pixerate-image-editor/references/color-management.md +37 -0
  105. package/skills/pixerate-image-editor/references/photoshop-files.md +39 -0
  106. package/skills/pixerate-vector/SKILL.md +54 -2
  107. package/dist/chunk-BLWCAQCW.js.map +0 -1
  108. package/dist/chunk-CZOXTEMN.js.map +0 -1
  109. package/dist/chunk-HNS7UHCM.js.map +0 -1
  110. package/dist/chunk-JLYX4V57.cjs +0 -2001
  111. package/dist/chunk-JLYX4V57.cjs.map +0 -1
  112. package/dist/chunk-KB2YBCVS.js +0 -2001
  113. package/dist/chunk-KB2YBCVS.js.map +0 -1
  114. package/dist/chunk-LQEEDHOP.js.map +0 -1
  115. package/dist/chunk-POGBUYWE.cjs.map +0 -1
  116. package/dist/chunk-W3GXSUSQ.cjs.map +0 -1
  117. package/dist/chunk-XIOMMGPD.cjs.map +0 -1
  118. package/dist/chunk-YGIMK4NX.cjs.map +0 -1
  119. package/dist/homography-CZ-tFjay.d.cts +0 -50
  120. package/dist/homography-Ctfjoc1b.d.ts +0 -50
  121. package/dist/warp-CWHZzE3x.d.ts +0 -118
  122. package/dist/warp-D5jz_tN7.d.cts +0 -118
  123. /package/dist/{chunk-LPYOIVLA.js.map → chunk-LKCYANMW.js.map} +0 -0
  124. /package/dist/{chunk-Z4BC6M74.js.map → chunk-UBIXVXPR.js.map} +0 -0
@@ -12,6 +12,7 @@ reproduced at the end of this file. Paths are relative to each source repository
12
12
 
13
13
  © 2026 ArtCraft Team and the CadCraft contributors.
14
14
 
15
+ - `crates/engine/src/snap/tracking.rs` (`polar_angles`, `MAX_ACQUIRED`, `DWELL`, `Tracker::toggle` / `hover` / `paths` / `find`: acquired points sending alignment paths and an endpoint's extension, then a crossing of two paths, a path crossing the drawing, or the nearest path) and its direct distance entry → `vectorPolarAngles`, `vectorTrackingPaths` and tracking in `snapFrom` in `packages/core/src/vector/snap.ts`, and `acquireTrackingPoint`, `placePenPoint` and `addPathByDimensions` in `packages/core/src/vector/controller.ts`; parallel and apparent-intersection tracking are not ported; test cases re-expressed → `packages/core/tests/vector-tracking.test.ts`
15
16
  - `crates/engine/src/spatial.rs` (an R-tree over object bounds used by picking, window selection and snaps, built only above 256 objects, reused while the content is the same and rebuilt from unchanged parts after an edit, returning candidates in draw order for the same exact tests a scan runs) → the approach of `VectorRTree`, `vectorSpatialIndex` and `vectorNodesNear` in `packages/core/src/vector/spatial.ts`, used by `hitTestVectorNodes`, marquee selection and snapping to segments; the tree is our own sort-tile-recursive packing (cadcraft uses the `rstar` crate), and unchanged parts are reused by caching each node's bounds by node object; parity tests are our own → `packages/core/tests/vector-spatial.test.ts`
16
17
  - `crates/constraints/src/lm.rs` (damped, weighted minimum-norm Gauss–Newton with a numerical Jacobian, `Δx = −W⁻¹Jᵀ(JW⁻¹Jᵀ + μI)⁻¹r`; Gaussian elimination with partial pivoting; Householder QR with column pivoting on row- and column-balanced matrices for rank and null space), `lib.rs` (constraints grouped into connected components, edited geometry weighted to move least, conflicts located by leaving each constraint out, new constraints refused when they do not raise the Jacobian's rank or when a Fix locks parameters already determined), `residual.rs` (the residual forms: point–point, point on line or circle, collinear, parallel, perpendicular, horizontal / vertical, tangent line–circle and circle–circle, symmetric, equal, distance along an axis, angle with its orientation from the starting geometry, radius, diameter), `dof.rs` (remaining degrees of freedom per component, per object by the null space restricted to its parameters, statuses), `auto.rs` (`infer`: coincident points, concentric and tangent circles, horizontal / vertical lines, collinear, connected perpendicular and parallel pairs, equal lengths and radii, in that order) and `expr.rs` (named expressions with `+ - * /`, parentheses and `pi`, evaluated lazily with cycle detection) → `packages/core/src/vector/constraints/lm.ts`, `solve.ts`, `auto.ts` and `expr.ts`; entities and handles became our anchors with stable ids, segments, handles, G1 / G2 joints at anchors and round closed paths, angle-type residuals are scaled to lengths so one tolerance fits all rows, and a solve after every edit weights what changed since the last solve; test cases re-expressed → `packages/core/tests/vector-constraints.test.ts`
17
18
  - `crates/geom/src/region.rs` (`enclosing_loop`, `boundary_at_gap` and `build_gap`: curves split at their crossings, gaps between loose ends bridged to the nearest other loose end within reach, a ray from the point meeting edges in order and the face to the left of each traced by always taking the sharpest left turn until a counter-clockwise loop round the point comes back; islands — pieces nested in that face — as holes; `interior_point`) → `PlanarMap.regionAt` and `closeGaps` in `packages/core/src/canvas/pathops/planar.ts` and `fillRegion` in `packages/core/src/vector/controller.ts`; islands are taken per face (the smallest face of another piece around each outer boundary) rather than every group inside the loop, so islands inside islands are not holes; test cases re-expressed → `packages/core/tests/canvas-pathfinder.test.ts`
@@ -31,6 +32,7 @@ reproduced at the end of this file. Paths are relative to each source repository
31
32
 
32
33
  © 2026 ArtCraft Team and the DeckCraft contributors.
33
34
 
35
+ - `crates/engine/src/connect.rs` (`sites`, `nearest_site`, `reroute` dropping glue to shapes that no longer exist, `set_glue` turning a plain line into a connector) → `packages/core/src/vector/connectors.ts` (`vectorConnectionSites`, `vectorConnectorTargetAt`, `rerouteVectorConnectors`) and `setConnector` in `packages/core/src/vector/controller.ts`; test cases our own → `packages/core/tests/vector-connectors.test.ts`
34
36
  - `crates/geom/src/morph.rs` (`contours`: every segment as a cubic, lines with handles at thirds; `align`: contours paired in order, one without a partner meeting a point at the same place relative to the other outline's box (`place`); `turn`: a closed contour restarted at another anchor; `match_pair`: the closed side of a pair turned, open paired with closed) → `subpathToMorphContour`, `turnMorphContour` and `correspondMorphContours` in `packages/core/src/vector/blend.ts`; anchors are inserted by arc length rather than split evenly per curve, and the winding of closed shapes follows their areas (effectcraft) instead of a cost comparison; test cases re-expressed → `packages/core/tests/vector-blend.test.ts`
35
37
  - `crates/model/src/anim.rs` (`ANIMATIONS`: PowerPoint's preset IDs per effect class) and `crates/pptx/src/read/timing.rs` (`collect` and `animations`: effects found by their `presetClass`, the target from `p:spTgt`, `nodeType` as on click, with previous or after previous) → `fromPptxAnimation` in `packages/core/src/animation-editor/pptx.ts`; effects map onto the editor's commands, and each click starts a shot
36
38
  - `crates/anim/src/morph.rs` (`morph_pairs`: shapes paired in passes, each used once — by name, then by matching content, then by kind — and unpaired shapes faded out) → `matchMotionNodes` and the `match` command in `packages/core/src/animation-editor/match.ts` and `compile.ts`; the passes are name, then id with the groups' own ids taken out, then kind in stacking order, and paired groups pair their own parts
@@ -74,6 +76,7 @@ reproduced at the end of this file. Paths are relative to each source repository
74
76
  - `crates/engine/src/snap.rs` (`spacings` / `equal_spacing` / `spacing_guides`: centred between neighbours, repeating the gap of the pair before or after, gap labels; threshold in screen pixels divided by the zoom) → `VectorSnapTargets.snapRect` in `packages/core/src/vector/snap.ts`; its equal-spacing cases re-expressed → `packages/core/tests/vector-snap.test.ts`
75
77
  - `crates/layout/src/lib.rs` (`layout_frame`, `distribute`, `main_offsets`, `fit_hug_text`, `enforce_limits`, `item_baseline`, `resize_child`, `grid_place`, `resolve_tracks`, `track_content`, `grid_geometry`, `layout_grid`, `apply_constraints`, `layout_page`, `layout_up`, `coherent_sizing`, `clamp_size`) → `packages/core/src/canvas/autoLayout.ts`
76
78
  - `crates/layout/src/{tests.rs,behaviour_tests.rs,nested_tests.rs,content_align_tests.rs}` (test cases re-expressed) → `packages/core/tests/canvas-auto-layout.test.ts`
79
+ - `crates/layout/src/lib.rs` (`apply_constraints`: a child keeps its left / right / both margins, its offset from the centre, or scales with its frame, per axis) → `vectorFrameChildMatrix` in `packages/core/src/vector/frames.ts`, applied when a vector frame is resized (`bakeFrame` in `packages/core/src/vector/geometry.ts`), with text keeping its font size; test cases our own → `packages/core/tests/vector-frames.test.ts`
77
80
  - `crates/doc/src/styles.rs` (`Collection`, `Mode`, `Variable`, `VariableValue`, `Variables::resolve`, `default_mode`, `current_mode`, `find_mode`, `scopes_for`, `normalize_scopes`, `in_scope`) → `packages/core/src/tokens/types.ts`, `packages/core/src/tokens/model.ts`
78
81
  - `crates/engine/src/cmd/variables.rs` (`alias_target` cycle check, `forget_variables` alias detaching, `remove_mode`, `move_mode`, `rename_collection`, `delete_collection`, `field_scope`) → `packages/core/src/tokens/model.ts`, `packages/core/src/canvas/tokenBindings.ts`
79
82
  - `crates/doc/src/lib.rs` (`modes_for`: modes inherited down the tree) → `getEffectiveTokenModes` in `packages/core/src/canvas/tokenBindings.ts`
@@ -189,6 +192,7 @@ GridCraft. Apache POI is © The Apache Software Foundation, licensed under the A
189
192
  - `crates/raster/src/lib.rs` (`Surface`: a sparse map of 256 px tiles behind shared pointers with a default pixel, copy-on-write writes that share untouched tiles, uniform tiles stored once, `prune` of tiles equal to the default, `content_bounds`, `translated`, region read / write) → `TiledSurface`, `SurfaceDraft`, `readRegion`, `writeRegion`, `fillRect`, `contentBounds` and `translateSurface` in `packages/core/src/raster/surface.ts`; 8-bit RGBA / 1-byte mask tiles instead of photocraft's pixel formats
190
193
  - `crates/paint/src/lib.rs` (`TIP_SHAPE`, `tip_shape`, `soft_exp`, `tip_falloff`, `tip_reach`, `dab_coverage`: Photoshop's measured round tip; `smooth`), `crates/paint/src/dynamics.rs` (`PathWalker`: the first point as a dab, then one every spacing × diameter along the path; `base_diameter` and the Transfer pressure-to-opacity rule) and `crates/paint/src/render.rs` (`rasterize_into` for the round computed tip with a 4 × 4 sub-pixel grid below 3 px; `CoverageMap::accumulate`: flow building up towards each dab's opacity ceiling; `composite`: one composite at the stroke opacity over the pre-stroke pixels, the selection multiplying coverage, the eraser and lock transparency) → `packages/core/src/paint/brush.ts`; only the computed round tip, pressure size and opacity, and simple smoothing are ported (no sampled tips, jitter, texture, dual brush, wet edges or dithering); brush test cases re-expressed → `packages/core/tests/image-layers-paint.test.ts`
191
194
  - `crates/paint/src/mixer.rs` (`MixerSettings`, `MixerState`, `PICKUP_RATE`, `apply_mixer_stroke`: per dab, sample the canvas under the footprint by coverage and alpha, move the pickup colour towards it by the pick-up rate × wet, mix the reservoir (weight amount × (1 − mix·wet)) with the pickup (weight mix·wet), deposit at flow × the total weight, drain the reservoir by 8 % × (1 − load) per dab; `load` / `clean`) → `MixerStroke`, `normalizeMixer`, `loadMixer` and `cleanMixer` in `packages/core/src/paint/mixer.ts`; our additions are the pigment mixing model for pickup, paint and deposit, load / clean after each stroke, and tiles in place of photocraft's surfaces (no Sample All Layers or Transfer › Wetness yet); mixer test cases re-expressed → `packages/core/tests/paint-mixer.test.ts`
195
+ - `crates/cms/src` (`math.rs`, `curve.rs`, `clut.rs`, `pipeline.rs`, `profile.rs`, `write.rs`, `transform.rs`, `gamut.rs`, `lut3d.rs`, `builtin.rs`, `synth.rs`: the ICC v2 / v4 reader for matrix/TRC, grey TRC and `mft1` / `mft2` / `mAB ` / `mBA ` profiles, the v4.3 writer, the float stage pipeline with its optimiser, tetrahedral CLUT evaluation, transforms with per-code shaper tables, x^(1/4)-indexed output tables and 33³ / 17⁴ device links, white fixing, absolute-colorimetric media-white scaling, black point compensation after Adobe's published algorithm, soft-proof chains, gamut checks, 3D display tables, the built-in profiles from published primaries and the synthetic coated CMYK model with its Neugebauer printing model, damped least-squares separation with GCR and gamut-boundary compression) → `packages/core/src/cms`; single-threaded, the CMYK profile generated on first use (a 17³ B2A grid and a coarser gamut-boundary sample instead of photocraft's shipped 21³ profile), no `.cube` / LUT files; test cases re-expressed → `packages/core/tests/cms.test.ts`
192
196
  - `crates/paint/src/retouch.rs` (the two retouching stroke shapes: accumulate a stroke's coverage and composite paint taken from the pre-stroke state through it, or run dabs in order over a working copy; `Smudge`), `crates/algo/src/retouch.rs` (`tone_curve`, `dodge_burn` with Protect Tones on luma and gamut compression towards luma, `saturation`, `sponge` with vibrance), `crates/algo/src/poisson.rs` (`solve_membrane`: cascadic multigrid with over-relaxed Gauss-Seidel sweeps; `seamless_clone` after Pérez, Gangnet and Blake 2003; `membrane_fill`), `crates/algo/src/inpaint.rs` (`best_offset`: the ring-matched proximity search with a coarse grid and a refinement, a hole summed-area table and a mild distance penalty) and `crates/engine/src/retouch_cmds.rs` (`clone_stamp`, `healing_brush`'s padded coverage and Dirichlet ring, `spot_heal_surface`'s dilated fill domain and heal mask, `tone_stroke`'s tone-once-from-the-original under capped coverage, `per_dab`, the aligned / non-aligned offset) → `packages/core/src/paint/retouch.ts` and `heal.ts`; on 8-bit tiles, translation-only clone sources (no scale, rotation or Clone Source panel), Spot Healing with proximity match or a smooth fill (no content-aware synthesis yet), the Smudge on our `SmudgeTip` and no Blur, Sharpen or History Brush yet; test cases re-expressed → `packages/core/tests/image-retouch.test.ts`
193
197
  - `crates/paint/src/procedural.rs` (`smooth`, the tileable `value_noise` on a wrapped lattice, `fbm` doubling the lattice per octave, the `Paper` and `Canvas` pattern styles: fractal noise with per-pixel grain, and an over / under weave of sine threads) and `crates/paint/src/rng.rs` (counter-based randomness: every value a pure hash of seed, index and stream, so a stroke's jitter replays however its points are chunked) → `TileableNoise`, `hash2`, `rand01` in `packages/core/src/paint-editor/noise.ts` and the `cold-pressed` and `canvas` grain patterns in `packages/core/src/paint-editor/paper.ts`; on 32-bit integer hashes instead of SplitMix64, with unequal lattice periods per axis for fibres and streaks; the other paper patterns, the paper library and the grain response of dry media are our own. The paint editor's `PenDabWalker` extends our port of `crates/paint/src/dynamics.rs`'s `PathWalker` (above) with tilt, barrel rotation and velocity
194
198
  - `crates/paint/src/symmetry.rs` (`SymmetryMode` vertical / horizontal / dual / diagonal, `PresetSymmetry::directions` with each mode's initial axis and the rotation added, `reflected_passes`: reflection about the centre along each axis plus the point reflection for dual, `segments`: the axes clipped to the document without assuming the centre is inside, and the coordinate limit) → `symmetryAxisAngles`, `symmetryCopies` and `symmetryAxisSegments` in `packages/core/src/paint-editor/guides.ts`, as affine maps of the stroke's points so one stroke paints every copy; our additions are the kaleidoscope (n rotations and the mirrored segments) and perspective guides; symmetry test cases re-expressed → `packages/core/tests/paint-editor-guides.test.ts`
@@ -271,6 +275,7 @@ GridCraft. Apache POI is © The Apache Software Foundation, licensed under the A
271
275
 
272
276
  © 2026 ArtCraft Team and the WordCraft contributors.
273
277
 
278
+ - `crates/doc/src/connector.rs` (`sites` at the middles of the sides through the shape's turn, `site_point`, `nearest_site`, `connector_segs` straight / elbow / curved, `reroute_group`) → `packages/core/src/vector/connectors.ts`, elbows and curves leaving along the sides' normals with a gap, plus `auto` sides; test cases re-expressed → `packages/core/tests/vector-connectors.test.ts`
274
279
  - `crates/engine/src/cmd/mailings.rs` (`parse_csv` delimiter auto-detection and blank-row skipping, `table_from_rows`, `header_split` padding / truncation and record caps, `address_map` field aliases) → `packages/core/src/grammar/data-source.ts`
275
280
  - `crates/formats/src/sheet.rs` (`field_names`: blank headers as `Column N`, repeated headers numbered) → `packages/core/src/grammar/data-source.ts`
276
281
  - `crates/engine/src/cmd/mailings.rs` (`condition` numeric-or-text comparison with `= <> < > <= >=`, `finish` one copy per record with SKIPIF and a merge cap) → `packages/core/src/grammar/variables.ts`, `packages/core/src/grammar/merge.ts`
@@ -1,17 +1,17 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; } var _class;
2
2
 
3
+ var _chunk26PGXHY7cjs = require('../chunk-26PGXHY7.cjs');
3
4
 
4
5
 
5
6
 
6
- var _chunkJLYX4V57cjs = require('../chunk-JLYX4V57.cjs');
7
7
 
8
8
 
9
9
 
10
10
 
11
+ var _chunkWAJQKWQUcjs = require('../chunk-WAJQKWQU.cjs');
11
12
 
12
13
 
13
14
 
14
- var _chunkWAJQKWQUcjs = require('../chunk-WAJQKWQU.cjs');
15
15
 
16
16
 
17
17
 
@@ -30,7 +30,8 @@ var _chunkWAJQKWQUcjs = require('../chunk-WAJQKWQU.cjs');
30
30
 
31
31
 
32
32
 
33
- var _chunkW3GXSUSQcjs = require('../chunk-W3GXSUSQ.cjs');
33
+
34
+ var _chunkLPLCWZTMcjs = require('../chunk-LPLCWZTM.cjs');
34
35
 
35
36
 
36
37
 
@@ -558,7 +559,7 @@ function smoothMotionTimes(points, start, duration, pacing = "even") {
558
559
  function motionTextLayout(nodes, id) {
559
560
  const group = _optionalChain([nodes, 'access', _5 => _5.get, 'call', _6 => _6(id), 'optionalAccess', _7 => _7.node]);
560
561
  if (!group || group.type !== "group") return null;
561
- const letters = _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, group).map((p) => ({ id: p.id, b: _optionalChain([nodes, 'access', _8 => _8.get, 'call', _9 => _9(p.id), 'optionalAccess', _10 => _10.bounds]) })).filter((l) => !!l.b && l.b.width + l.b.height > 0);
562
+ const letters = _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, group).map((p) => ({ id: p.id, b: _optionalChain([nodes, 'access', _8 => _8.get, 'call', _9 => _9(p.id), 'optionalAccess', _10 => _10.bounds]) })).filter((l) => !!l.b && l.b.width + l.b.height > 0);
562
563
  if (!letters.length) return null;
563
564
  const lines = [];
564
565
  for (const l of [...letters].sort((a, c) => a.b.y + a.b.height / 2 - (c.b.y + c.b.height / 2))) {
@@ -643,21 +644,21 @@ function indexMotionNodes(nodes) {
643
644
  const out = /* @__PURE__ */ new Map();
644
645
  const walk = (list, parentWorld, ancestors) => {
645
646
  for (const node of list) {
646
- const local = _chunkW3GXSUSQcjs.vectorNodeBounds.call(void 0, node);
647
- const bounds = _chunkW3GXSUSQcjs.vectorNodeBounds.call(void 0, node, parentWorld);
647
+ const local = _chunkLPLCWZTMcjs.vectorNodeBounds.call(void 0, node);
648
+ const bounds = _chunkLPLCWZTMcjs.vectorNodeBounds.call(void 0, node, parentWorld);
648
649
  const center = local ? { x: local.x + local.width / 2, y: local.y + local.height / 2 } : { x: 0, y: 0 };
649
650
  if (!out.has(node.id)) out.set(node.id, { node, ancestors, parentWorld, center, bounds, box: local });
650
- if (node.type === "group") walk(node.children, _chunkW3GXSUSQcjs.multiplyMatrices.call(void 0, _chunkW3GXSUSQcjs.nodeMatrix.call(void 0, node), parentWorld), [...ancestors, node.id]);
651
+ if (node.type === "group") walk(node.children, _chunkLPLCWZTMcjs.multiplyMatrices.call(void 0, _chunkLPLCWZTMcjs.nodeMatrix.call(void 0, node), parentWorld), [...ancestors, node.id]);
651
652
  }
652
653
  };
653
- walk(nodes, _chunkW3GXSUSQcjs.IDENTITY_MATRIX, []);
654
+ walk(nodes, _chunkLPLCWZTMcjs.IDENTITY_MATRIX, []);
654
655
  indexCache.set(nodes, out);
655
656
  return out;
656
657
  }
657
658
  var solid = (paint) => typeof paint === "string" && paint !== "none" ? paint : void 0;
658
659
  function firstPaint(node, key) {
659
660
  if (node.type === "path") return solid(node[key]);
660
- for (const p of _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, node)) {
661
+ for (const p of _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, node)) {
661
662
  const s = solid(p[key]);
662
663
  if (s) return s;
663
664
  }
@@ -665,7 +666,7 @@ function firstPaint(node, key) {
665
666
  }
666
667
  function strokeReach(node) {
667
668
  let reach = 0;
668
- for (const p of node.type === "path" ? [node] : _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, node)) {
669
+ for (const p of node.type === "path" ? [node] : _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, node)) {
669
670
  if (p.stroke == null || p.stroke === "none") continue;
670
671
  const width = _nullishCoalesce(p.strokeWidth, () => ( 1));
671
672
  reach = Math.max(reach, p.strokeAlign === "outside" ? width : p.strokeAlign === "inside" ? 0 : width / 2);
@@ -735,7 +736,7 @@ function motionRestValue(entry, property, artwork) {
735
736
  case "stroke":
736
737
  return _nullishCoalesce((node && firstPaint(node, property)), () => ( "transparent"));
737
738
  case "strokeWidth": {
738
- const path = _optionalChain([node, 'optionalAccess', _13 => _13.type]) === "path" ? node : node ? _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, node)[0] : void 0;
739
+ const path = _optionalChain([node, 'optionalAccess', _13 => _13.type]) === "path" ? node : node ? _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, node)[0] : void 0;
739
740
  return _nullishCoalesce(_optionalChain([path, 'optionalAccess', _14 => _14.strokeWidth]), () => ( 1));
740
741
  }
741
742
  case "shape":
@@ -846,12 +847,12 @@ function resolveMotionCamera(camera, scene) {
846
847
  const r = camera;
847
848
  return { x: r.x, y: r.y, width: r.width, height: r.height };
848
849
  }
849
- function motionPathLength(node, parentWorld = _chunkW3GXSUSQcjs.IDENTITY_MATRIX) {
850
+ function motionPathLength(node, parentWorld = _chunkLPLCWZTMcjs.IDENTITY_MATRIX) {
850
851
  let longest = 0;
851
- const paths = node.type === "path" ? [node] : _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, node);
852
+ const paths = node.type === "path" ? [node] : _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, node);
852
853
  for (const p of paths) {
853
854
  try {
854
- longest = Math.max(longest, _chunkXGD3FSNCcjs.bezierPathLength.call(void 0, _chunkW3GXSUSQcjs.vectorPathWorldBezier.call(void 0, p, parentWorld, { effects: false })));
855
+ longest = Math.max(longest, _chunkXGD3FSNCcjs.bezierPathLength.call(void 0, _chunkLPLCWZTMcjs.vectorPathWorldBezier.call(void 0, p, parentWorld, { effects: false })));
855
856
  } catch (e2) {
856
857
  }
857
858
  }
@@ -1104,12 +1105,12 @@ function compileMotionScene(scene) {
1104
1105
  const order2 = _nullishCoalesce(o.order, () => ( "together"));
1105
1106
  const pace = _nullishCoalesce(o.pace, () => ( "time"));
1106
1107
  if (order2 === "together" && pace === "time") return null;
1107
- const paths = entry.node.type === "path" ? [entry.node] : _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, entry.node);
1108
+ const paths = entry.node.type === "path" ? [entry.node] : _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, entry.node);
1108
1109
  let list = paths.map((p) => {
1109
1110
  const at = nodes.get(p.id);
1110
1111
  let first = { x: 0, y: 0 };
1111
1112
  try {
1112
- const m = _chunkW3GXSUSQcjs.vectorPathWorldBezier.call(void 0, p, _nullishCoalesce(_optionalChain([at, 'optionalAccess', _41 => _41.parentWorld]), () => ( _chunkW3GXSUSQcjs.IDENTITY_MATRIX)), { effects: false })[0];
1113
+ const m = _chunkLPLCWZTMcjs.vectorPathWorldBezier.call(void 0, p, _nullishCoalesce(_optionalChain([at, 'optionalAccess', _41 => _41.parentWorld]), () => ( _chunkLPLCWZTMcjs.IDENTITY_MATRIX)), { effects: false })[0];
1113
1114
  if (m && m.type === "M") first = { x: m.x, y: m.y };
1114
1115
  } catch (e3) {
1115
1116
  }
@@ -1221,17 +1222,17 @@ function compileMotionScene(scene) {
1221
1222
  let dx = _nullishCoalesce(_optionalChain([command3, 'access', _44 => _44.by, 'optionalAccess', _45 => _45.x]), () => ( 0));
1222
1223
  let dy = _nullishCoalesce(_optionalChain([command3, 'access', _46 => _46.by, 'optionalAccess', _47 => _47.y]), () => ( 0));
1223
1224
  if (command3.to) {
1224
- const inv = _chunkW3GXSUSQcjs.invertMatrix.call(void 0, entry.parentWorld);
1225
- const target = _chunkW3GXSUSQcjs.applyMatrix.call(void 0, inv, command3.to);
1225
+ const inv = _chunkLPLCWZTMcjs.invertMatrix.call(void 0, entry.parentWorld);
1226
+ const target = _chunkLPLCWZTMcjs.applyMatrix.call(void 0, inv, command3.to);
1226
1227
  dx = target.x - (entry.center.x + num2("x"));
1227
1228
  dy = target.y - (entry.center.y + num2("y"));
1228
1229
  }
1229
1230
  if (_optionalChain([command3, 'access', _48 => _48.via, 'optionalAccess', _49 => _49.length])) {
1230
- const inv = _chunkW3GXSUSQcjs.invertMatrix.call(void 0, entry.parentWorld);
1231
+ const inv = _chunkLPLCWZTMcjs.invertMatrix.call(void 0, entry.parentWorld);
1231
1232
  const x0 = num2("x");
1232
1233
  const y0 = num2("y");
1233
1234
  const add = command3.blend === "add";
1234
- const offsets = [{ x: 0, y: 0 }, ...command3.via.map((p) => _chunkW3GXSUSQcjs.applyMatrix.call(void 0, inv, p)).map((p) => ({ x: p.x - entry.center.x - x0, y: p.y - entry.center.y - y0 })), { x: dx, y: dy }];
1235
+ const offsets = [{ x: 0, y: 0 }, ...command3.via.map((p) => _chunkLPLCWZTMcjs.applyMatrix.call(void 0, inv, p)).map((p) => ({ x: p.x - entry.center.x - x0, y: p.y - entry.center.y - y0 })), { x: dx, y: dy }];
1235
1236
  const times = smoothMotionTimes(offsets, start, end - start, _nullishCoalesce(command3.pacing, () => ( "even")));
1236
1237
  for (const axis of ["x", "y"]) {
1237
1238
  const base = add ? 0 : axis === "x" ? x0 : y0;
@@ -1366,6 +1367,7 @@ function fadePaint(paint, alpha) {
1366
1367
  }
1367
1368
  function mapPaths(node, fn) {
1368
1369
  if (node.type === "path") return fn(node);
1370
+ if (node.type === "text" || node.type === "instance") return node;
1369
1371
  return { ...node, children: node.children.map((c) => mapPaths(c, fn)) };
1370
1372
  }
1371
1373
  var morphCache = /* @__PURE__ */ new WeakMap();
@@ -1373,13 +1375,13 @@ function morphBetween(a, b) {
1373
1375
  let inner = morphCache.get(a);
1374
1376
  if (!inner) morphCache.set(a, inner = /* @__PURE__ */ new WeakMap());
1375
1377
  let morph = inner.get(b);
1376
- if (!morph) inner.set(b, morph = _chunkJLYX4V57cjs.createVectorMorph.call(void 0, a, b, { id: a.id }));
1378
+ if (!morph) inner.set(b, morph = _chunk26PGXHY7cjs.createVectorMorph.call(void 0, a, b, { id: a.id }));
1377
1379
  return morph;
1378
1380
  }
1379
1381
  function shapeIn(entry, shape) {
1380
1382
  if (shape === entry) return entry.node;
1381
- const m = _chunkW3GXSUSQcjs.multiplyMatrices.call(void 0, _chunkW3GXSUSQcjs.multiplyMatrices.call(void 0, _chunkW3GXSUSQcjs.nodeMatrix.call(void 0, shape.node), shape.parentWorld), _chunkW3GXSUSQcjs.invertMatrix.call(void 0, entry.parentWorld));
1382
- return { ...shape.node, ..._chunkW3GXSUSQcjs.isIdentityMatrix.call(void 0, m) ? { transform: void 0 } : { transform: m } };
1383
+ const m = _chunkLPLCWZTMcjs.multiplyMatrices.call(void 0, _chunkLPLCWZTMcjs.multiplyMatrices.call(void 0, _chunkLPLCWZTMcjs.nodeMatrix.call(void 0, shape.node), shape.parentWorld), _chunkLPLCWZTMcjs.invertMatrix.call(void 0, entry.parentWorld));
1384
+ return { ...shape.node, ..._chunkLPLCWZTMcjs.isIdentityMatrix.call(void 0, m) ? { transform: void 0 } : { transform: m } };
1383
1385
  }
1384
1386
  function shapeAt(entry, timeline, nodes, time) {
1385
1387
  const track = timeline.tracks.get(motionTrackKey(entry.node.id, "shape"));
@@ -1399,7 +1401,7 @@ function shapeAt(entry, timeline, nodes, time) {
1399
1401
  function motionFollowRoute(entry, path) {
1400
1402
  if (!path || path.node.type !== "path") return null;
1401
1403
  try {
1402
- return _chunkXGD3FSNCcjs.transformBezierPath.call(void 0, _chunkW3GXSUSQcjs.vectorPathWorldBezier.call(void 0, path.node, path.parentWorld, { effects: false }), _chunkW3GXSUSQcjs.invertMatrix.call(void 0, entry.parentWorld));
1404
+ return _chunkXGD3FSNCcjs.transformBezierPath.call(void 0, _chunkLPLCWZTMcjs.vectorPathWorldBezier.call(void 0, path.node, path.parentWorld, { effects: false }), _chunkLPLCWZTMcjs.invertMatrix.call(void 0, entry.parentWorld));
1403
1405
  } catch (e4) {
1404
1406
  return null;
1405
1407
  }
@@ -1433,9 +1435,9 @@ function motionNodeOffset(entry, timeline, time, nodes = indexMotionNodes([entry
1433
1435
  const scaleX = read("scaleX", 1);
1434
1436
  const scaleY = read("scaleY", 1);
1435
1437
  const c = entry.center;
1436
- let matrix = _chunkW3GXSUSQcjs.multiplyMatrices.call(void 0, _chunkW3GXSUSQcjs.multiplyMatrices.call(void 0, _chunkW3GXSUSQcjs.scaleMatrix.call(void 0, scaleX, scaleY, c), _chunkW3GXSUSQcjs.rotateMatrix.call(void 0, rotation, c)), _chunkW3GXSUSQcjs.translateMatrix.call(void 0, dx, dy));
1438
+ let matrix = _chunkLPLCWZTMcjs.multiplyMatrices.call(void 0, _chunkLPLCWZTMcjs.multiplyMatrices.call(void 0, _chunkLPLCWZTMcjs.scaleMatrix.call(void 0, scaleX, scaleY, c), _chunkLPLCWZTMcjs.rotateMatrix.call(void 0, rotation, c)), _chunkLPLCWZTMcjs.translateMatrix.call(void 0, dx, dy));
1437
1439
  const along = follow ? followAt(entry, timeline, nodes, time) : null;
1438
- if (along) matrix = _chunkW3GXSUSQcjs.multiplyMatrices.call(void 0, matrix, _chunkW3GXSUSQcjs.multiplyMatrices.call(void 0, _chunkW3GXSUSQcjs.multiplyMatrices.call(void 0, _chunkW3GXSUSQcjs.translateMatrix.call(void 0, -c.x, -c.y), _chunkW3GXSUSQcjs.rotateMatrix.call(void 0, along.angle)), _chunkW3GXSUSQcjs.translateMatrix.call(void 0, along.x, along.y)));
1440
+ if (along) matrix = _chunkLPLCWZTMcjs.multiplyMatrices.call(void 0, matrix, _chunkLPLCWZTMcjs.multiplyMatrices.call(void 0, _chunkLPLCWZTMcjs.multiplyMatrices.call(void 0, _chunkLPLCWZTMcjs.translateMatrix.call(void 0, -c.x, -c.y), _chunkLPLCWZTMcjs.rotateMatrix.call(void 0, along.angle)), _chunkLPLCWZTMcjs.translateMatrix.call(void 0, along.x, along.y)));
1439
1441
  return { matrix, dx, dy, rotation, scaleX, scaleY, follow: along };
1440
1442
  }
1441
1443
  function motionNodeClips(id, timeline, time) {
@@ -1502,7 +1504,7 @@ function motionFrameAt(scene, time, options = {}) {
1502
1504
  const start = Math.min(1, Math.max(0, typeof drawStart === "number" ? drawStart : 0));
1503
1505
  const end = Math.min(1, Math.max(0, typeof drawEnd === "number" ? drawEnd : 1));
1504
1506
  if (start > 0 || end < 1) {
1505
- const trim = _chunkW3GXSUSQcjs.createVectorEffect.call(void 0, "trim", MOTION_DRAW_EFFECT_ID, { start: tidy(start * 100), end: tidy(end * 100) });
1507
+ const trim = _chunkLPLCWZTMcjs.createVectorEffect.call(void 0, "trim", MOTION_DRAW_EFFECT_ID, { start: tidy(start * 100), end: tidy(end * 100) });
1506
1508
  p = { ...p, effects: [trim, ...(_nullishCoalesce(p.effects, () => ( []))).filter((e) => e.id !== MOTION_DRAW_EFFECT_ID)] };
1507
1509
  }
1508
1510
  }
@@ -1516,8 +1518,8 @@ function motionFrameAt(scene, time, options = {}) {
1516
1518
  if (o >= 1) delete out.opacity;
1517
1519
  }
1518
1520
  const offset = options.offsets === false ? null : motionNodeOffset(entry, timeline, time, nodes);
1519
- if (offset && !_chunkW3GXSUSQcjs.isIdentityMatrix.call(void 0, offset.matrix)) {
1520
- const m = _chunkW3GXSUSQcjs.multiplyMatrices.call(void 0, _chunkW3GXSUSQcjs.nodeMatrix.call(void 0, out), offset.matrix).map(tidy);
1521
+ if (offset && !_chunkLPLCWZTMcjs.isIdentityMatrix.call(void 0, offset.matrix)) {
1522
+ const m = _chunkLPLCWZTMcjs.multiplyMatrices.call(void 0, _chunkLPLCWZTMcjs.nodeMatrix.call(void 0, out), offset.matrix).map(tidy);
1521
1523
  out = { ...out, transform: m };
1522
1524
  }
1523
1525
  return out;
@@ -1537,7 +1539,7 @@ function motionFrameToSvg(scene, time, options = {}) {
1537
1539
  const wrap = { ...options.wrap };
1538
1540
  for (const [id, clips] of Object.entries(frame.clips))
1539
1541
  wrap[id] = [..._nullishCoalesce(wrap[id], () => ( [])), ...clips.map((clip, i) => ({ clip: { id: `${prefix}reveal-${id}-${i}`, shape: clip.shape, attrs: motionClipAttrs(clip) } }))];
1540
- return withViewBox(_chunkJLYX4V57cjs.vectorDocumentToSvg.call(void 0, frame.document, { ...options, wrap }), frame.camera);
1542
+ return withViewBox(_chunkLPLCWZTMcjs.vectorDocumentToSvg.call(void 0, frame.document, { ...options, wrap }), frame.camera);
1541
1543
  }
1542
1544
  function withViewBox(svg, box) {
1543
1545
  const f = (n) => String(Math.round(n * 1e3) / 1e3);
@@ -1720,8 +1722,8 @@ function visibleAt(timeline, id, time, node) {
1720
1722
  if (Array.isArray(rect) && (rect[2] <= 1e-4 || rect[3] <= 1e-4)) return false;
1721
1723
  const circle = clip("clipCircle");
1722
1724
  if (Array.isArray(circle) && circle[2] <= 1e-4) return false;
1723
- const hasFill = node.type === "path" ? !!node.fill && node.fill !== "none" : _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, node).some((p) => p.fill && p.fill !== "none");
1724
- const drawn = num2("drawEnd", 1) > 0.01 && (node.type === "path" || _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, node).length === 0 || _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, node).some((p) => {
1725
+ const hasFill = node.type === "path" ? !!node.fill && node.fill !== "none" : _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, node).some((p) => p.fill && p.fill !== "none");
1726
+ const drawn = num2("drawEnd", 1) > 0.01 && (node.type === "path" || _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, node).length === 0 || _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, node).some((p) => {
1725
1727
  const v = trackValue(timeline.tracks.get(motionTrackKey(p.id, "drawEnd")), time);
1726
1728
  return typeof v !== "number" || v > 0.01;
1727
1729
  }));
@@ -1807,7 +1809,7 @@ function checkMotionScene(scene) {
1807
1809
  }
1808
1810
  for (const id of targets) {
1809
1811
  const node = nodes.get(id).node;
1810
- const paths = node.type === "path" ? [node] : _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, node);
1812
+ const paths = node.type === "path" ? [node] : _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, node);
1811
1813
  if (paths.some((p) => p[key] !== void 0 && p[key] !== null && typeof p[key] !== "string"))
1812
1814
  issues.push(_chunkWAJQKWQUcjs.motionIssue.call(void 0, "paint-not-color", `"${id}" has a gradient ${key}; it switches to ${color} at the end instead of blending.`, { ...where, ids: [id] }));
1813
1815
  }
@@ -1820,7 +1822,7 @@ function checkMotionScene(scene) {
1820
1822
  else
1821
1823
  for (const id of targets) {
1822
1824
  const node = nodes.get(id).node;
1823
- const paths = node.type === "path" ? [node] : _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, node);
1825
+ const paths = node.type === "path" ? [node] : _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, node);
1824
1826
  if (paths.some((p) => typeof p[key] !== "string"))
1825
1827
  issues.push(_chunkWAJQKWQUcjs.motionIssue.call(void 0, "paint-not-color", `"${id}" has a gradient or no ${key}, so a colour pulse cannot tint it; give it a solid ${key} or pulse the ${key === "fill" ? "stroke" : "fill"}.`, { ...where, ids: [id] }));
1826
1828
  }
@@ -2404,7 +2406,7 @@ function toFlairSvgMotion(scene, options = {}) {
2404
2406
  raw: (t) => transformAt(t, true)
2405
2407
  });
2406
2408
  }
2407
- const paths = node.type === "path" ? [node] : _chunkW3GXSUSQcjs.collectPathNodes.call(void 0, node);
2409
+ const paths = node.type === "path" ? [node] : _chunkLPLCWZTMcjs.collectPathNodes.call(void 0, node);
2408
2410
  for (const path of paths) {
2409
2411
  if (pathsSeen.has(path.id)) continue;
2410
2412
  pathsSeen.add(path.id);
@@ -2426,7 +2428,7 @@ function toFlairSvgMotion(scene, options = {}) {
2426
2428
  if (shapeSegments.length) {
2427
2429
  if (node.type !== "path") notes.push(`"${id}" is a group: its morph or effect animation is not exported (Flair tweens one path's "d").`);
2428
2430
  else {
2429
- const inverse = _chunkW3GXSUSQcjs.invertMatrix.call(void 0, _chunkW3GXSUSQcjs.nodeMatrix.call(void 0, node));
2431
+ const inverse = _chunkLPLCWZTMcjs.invertMatrix.call(void 0, _chunkLPLCWZTMcjs.nodeMatrix.call(void 0, node));
2430
2432
  channels.push({
2431
2433
  id,
2432
2434
  kind: "attr",
@@ -2435,9 +2437,9 @@ function toFlairSvgMotion(scene, options = {}) {
2435
2437
  exact: () => false,
2436
2438
  value: (t) => {
2437
2439
  const frame = motionFrameAt(scene, t, { draw: false, offsets: false });
2438
- const now = _chunkW3GXSUSQcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _108 => _108(frame.document.nodes), 'access', _109 => _109.get, 'call', _110 => _110(id), 'optionalAccess', _111 => _111.node]));
2440
+ const now = _chunkLPLCWZTMcjs.expandVectorAppearance.call(void 0, _optionalChain([indexMotionNodes, 'call', _108 => _108(frame.document.nodes), 'access', _109 => _109.get, 'call', _110 => _110(id), 'optionalAccess', _111 => _111.node]));
2439
2441
  if (!now || now.type !== "path") return "";
2440
- const bezier = _chunkXGD3FSNCcjs.transformBezierPath.call(void 0, _chunkW3GXSUSQcjs.vectorPathWorldBezier.call(void 0, now, _chunkW3GXSUSQcjs.IDENTITY_MATRIX, { effects: false }), inverse);
2442
+ const bezier = _chunkXGD3FSNCcjs.transformBezierPath.call(void 0, _chunkLPLCWZTMcjs.vectorPathWorldBezier.call(void 0, now, _chunkLPLCWZTMcjs.IDENTITY_MATRIX, { effects: false }), inverse);
2441
2443
  return _chunkXGD3FSNCcjs.bezierPathToSvg.call(void 0, bezier, { precision: Math.min(precision, 2) });
2442
2444
  }
2443
2445
  });
@@ -2544,7 +2546,7 @@ function toFlairSvgMotion(scene, options = {}) {
2544
2546
  }
2545
2547
  if (scene.motionBlur) notes.push("Motion blur is drawn on the stage only; Flair plays the scene sharp.");
2546
2548
  if (trigger && (_nullishCoalesce(scene.loop, () => ( "none"))) !== "none") notes.push(`The scene plays on ${trigger}, so its loop does not apply in Flair.`);
2547
- let svg = _chunkJLYX4V57cjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkW3GXSUSQcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
2549
+ let svg = _chunkLPLCWZTMcjs.vectorDocumentToSvg.call(void 0, { ...scene.artwork, nodes: _chunkLPLCWZTMcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) }, { pretty: false, precision, wrap });
2548
2550
  if (routes.length) svg = svg.replace(/^(<svg[^>]*>)/, `$1<defs>${routes.join("")}</defs>`);
2549
2551
  let motion = { svg, animate };
2550
2552
  if (_optionalChain([scene, 'access', _114 => _114.controls, 'optionalAccess', _115 => _115.length])) {
@@ -2628,14 +2630,14 @@ function easingOf(ease, fallback) {
2628
2630
  return fallback;
2629
2631
  }
2630
2632
  function poseOf(value, entry) {
2631
- const t = _chunkJLYX4V57cjs.parseSvgTransform.call(void 0, String(value));
2632
- const o = _chunkW3GXSUSQcjs.multiplyMatrices.call(void 0, _nullishCoalesce(_chunkW3GXSUSQcjs.invertMatrix.call(void 0, _chunkW3GXSUSQcjs.nodeMatrix.call(void 0, entry.node)), () => ( [1, 0, 0, 1, 0, 0])), t);
2633
+ const t = _chunkLPLCWZTMcjs.parseSvgTransform.call(void 0, String(value));
2634
+ const o = _chunkLPLCWZTMcjs.multiplyMatrices.call(void 0, _nullishCoalesce(_chunkLPLCWZTMcjs.invertMatrix.call(void 0, _chunkLPLCWZTMcjs.nodeMatrix.call(void 0, entry.node)), () => ( [1, 0, 0, 1, 0, 0])), t);
2633
2635
  const sx = Math.hypot(o[0], o[1]);
2634
2636
  if (!(sx > 1e-9)) return null;
2635
2637
  const sy = (o[0] * o[3] - o[1] * o[2]) / sx;
2636
2638
  if (Math.abs(o[2] + o[1] / sx * sy) > 1e-6 || Math.abs(o[3] - o[0] / sx * sy) > 1e-6) return null;
2637
2639
  const c = entry.center;
2638
- const moved = _chunkW3GXSUSQcjs.applyMatrix.call(void 0, o, c);
2640
+ const moved = _chunkLPLCWZTMcjs.applyMatrix.call(void 0, o, c);
2639
2641
  return { dx: moved.x - c.x, dy: moved.y - c.y, rotation: Math.atan2(o[1], o[0]) * 180 / Math.PI, sx, sy };
2640
2642
  }
2641
2643
  var near = (a, b, eps = 1e-6) => Math.abs(a - b) < eps;
@@ -2720,7 +2722,7 @@ function fromFlairSvgMotion(input, options = {}) {
2720
2722
  const notes = [];
2721
2723
  if (!input || typeof input.svg !== "string") throw new Error('Give a Flair SvgMotion: { svg: "<svg \u2026>", animate: [ \u2026 ] }.');
2722
2724
  const { motion, controls } = resolvePlaceholders(input, notes);
2723
- const artwork = _chunkJLYX4V57cjs.parseVectorSvg.call(void 0, motion.svg);
2725
+ const artwork = _chunkLPLCWZTMcjs.parseVectorSvg.call(void 0, motion.svg);
2724
2726
  const nodes = indexMotionNodes(artwork.nodes);
2725
2727
  const byStep = /* @__PURE__ */ new Map();
2726
2728
  for (const anim of _nullishCoalesce(motion.animate, () => ( []))) {
@@ -2967,9 +2969,9 @@ function toLottie(scene, options = {}) {
2967
2969
  const items = [];
2968
2970
  const shapeSegments = [...timeline.tracks.entries()].filter(([k]) => k.startsWith(`${path.id}\0`) && (k.endsWith("\0shape") || k.includes("\0effect:"))).flatMap(([, v]) => v);
2969
2971
  const geometryAt = (t) => {
2970
- if (!shapeSegments.length) return _chunkW3GXSUSQcjs.vectorPathWorldBezier.call(void 0, path);
2972
+ if (!shapeSegments.length) return _chunkLPLCWZTMcjs.vectorPathWorldBezier.call(void 0, path);
2971
2973
  const now = _optionalChain([indexMotionNodes, 'call', _124 => _124(motionFrameAt(scene, t, { draw: false, offsets: false }).document.nodes), 'access', _125 => _125.get, 'call', _126 => _126(path.id), 'optionalAccess', _127 => _127.node]);
2972
- return now && now.type === "path" ? _chunkW3GXSUSQcjs.vectorPathWorldBezier.call(void 0, _chunkW3GXSUSQcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkW3GXSUSQcjs.vectorPathWorldBezier.call(void 0, path);
2974
+ return now && now.type === "path" ? _chunkLPLCWZTMcjs.vectorPathWorldBezier.call(void 0, _chunkLPLCWZTMcjs.expandVectorAppearances.call(void 0, [now])[0]) : _chunkLPLCWZTMcjs.vectorPathWorldBezier.call(void 0, path);
2973
2975
  };
2974
2976
  const rest = lottieContours(_chunkXGD3FSNCcjs.toBezierPath.call(void 0, geometryAt(0)), round2);
2975
2977
  rest.forEach((contour, n) => {
@@ -3041,10 +3043,14 @@ function toLottie(scene, options = {}) {
3041
3043
  let inner;
3042
3044
  const innerTr = STATIC_TR();
3043
3045
  if (node.type === "path") inner = [...pathShapes(node)];
3044
- else {
3046
+ else if (node.type === "text" || node.type === "instance") {
3047
+ const drawn = _chunkLPLCWZTMcjs.expandVectorAppearance.call(void 0, node);
3048
+ inner = drawn.type === "path" ? [...pathShapes(drawn)] : drawn.children.filter((c) => c.type === "path").reverse().flatMap((c) => [...pathShapes(c)]);
3049
+ notes.push(`"${node.id}": text is exported as its outlines${_chunkLPLCWZTMcjs.vectorTextHasOutlines.call(void 0, ) ? "" : " (glyph boxes: set an outline text shaper for real glyphs)"}.`);
3050
+ } else {
3045
3051
  inner = shown(node.children).reverse().map(nodeGroup);
3046
- const m = _chunkW3GXSUSQcjs.nodeMatrix.call(void 0, node);
3047
- if (m !== _chunkW3GXSUSQcjs.IDENTITY_MATRIX) {
3052
+ const m = _chunkLPLCWZTMcjs.nodeMatrix.call(void 0, node);
3053
+ if (m !== _chunkLPLCWZTMcjs.IDENTITY_MATRIX) {
3048
3054
  const parts = staticParts(m);
3049
3055
  if (!parts) notes.push(`"${node.id}": its transform skews, which is not exported; it is drawn without the skew.`);
3050
3056
  else {
@@ -3057,7 +3063,7 @@ function toLottie(scene, options = {}) {
3057
3063
  const body = { ty: "gr", nm: `${node.id} (own)`, it: [...inner, innerTr] };
3058
3064
  return { ty: "gr", nm: _nullishCoalesce(node.name, () => ( node.id)), it: [body, poseTransform(entry)] };
3059
3065
  };
3060
- const artwork = { ...scene.artwork, nodes: _chunkW3GXSUSQcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) };
3066
+ const artwork = { ...scene.artwork, nodes: _chunkLPLCWZTMcjs.expandVectorAppearances.call(void 0, scene.artwork.nodes) };
3061
3067
  const content = shown(artwork.nodes).reverse().map(nodeGroup);
3062
3068
  if (artwork.background && rgba(artwork.background))
3063
3069
  content.push({ ty: "gr", nm: "Background", it: [{ ty: "rc", nm: "Background", p: { a: 0, k: [artwork.width / 2, artwork.height / 2] }, s: { a: 0, k: [artwork.width, artwork.height] }, r: { a: 0, k: 0 } }, { ty: "fl", c: { a: 0, k: rgba(artwork.background).map(round2) }, o: { a: 0, k: 100 }, r: 1 }, STATIC_TR()] });
@@ -3156,11 +3162,11 @@ function preset(prst, w, h) {
3156
3162
  const poly = (pts) => [{ closed: true, anchors: pts.map(([x, y]) => ({ x, y })) }];
3157
3163
  switch (prst) {
3158
3164
  case "rect":
3159
- return [_chunkW3GXSUSQcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
3165
+ return [_chunkLPLCWZTMcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
3160
3166
  case "roundRect":
3161
- return [_chunkW3GXSUSQcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h }, Math.min(w, h) * 0.16667)];
3167
+ return [_chunkLPLCWZTMcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h }, Math.min(w, h) * 0.16667)];
3162
3168
  case "ellipse":
3163
- return [_chunkW3GXSUSQcjs.ellipseSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
3169
+ return [_chunkLPLCWZTMcjs.ellipseSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
3164
3170
  case "triangle":
3165
3171
  return poly([[w / 2, 0], [w, h], [0, h]]);
3166
3172
  case "rtTriangle":
@@ -3268,7 +3274,7 @@ function shapes(ctx, tree, map) {
3268
3274
  let subpaths = cust ? custom(cust, w, h) : prst ? preset(prst, w, h) : null;
3269
3275
  if (!subpaths) {
3270
3276
  if (prst) ctx.notes.push(`"${name}" is a ${prst}, drawn as its bounding rectangle.`);
3271
- subpaths = [_chunkW3GXSUSQcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
3277
+ subpaths = [_chunkLPLCWZTMcjs.rectSubpath.call(void 0, { x: 0, y: 0, width: w, height: h })];
3272
3278
  }
3273
3279
  const style = kid(el, "style");
3274
3280
  const fill = kid(spPr, "noFill") ? null : _nullishCoalesce(colour(ctx, kid(spPr, "solidFill")), () => ( (el.localName === "cxnSp" ? null : colour(ctx, kid(style, "fillRef")))));
@@ -4253,7 +4259,7 @@ function createAnimationEditorToolProvider(controller, options = {}) {
4253
4259
  handler: ({ svg }) => {
4254
4260
  let artwork;
4255
4261
  try {
4256
- artwork = _chunkJLYX4V57cjs.parseVectorSvg.call(void 0, svg);
4262
+ artwork = _chunkLPLCWZTMcjs.parseVectorSvg.call(void 0, svg);
4257
4263
  } catch (error) {
4258
4264
  throw new Error(`The SVG could not be read: ${error.message}. Nothing was changed.`);
4259
4265
  }