domotion-svg 0.28.2 → 0.30.1

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 (348) hide show
  1. package/FEATURES.md +12 -0
  2. package/README.md +39 -4
  3. package/assets/fonts/fixture/README.md +4 -4
  4. package/dist/animation/caret-metrics.js +8 -4
  5. package/dist/animation/caret-track.js +16 -8
  6. package/dist/animation/composite.js +23 -12
  7. package/dist/animation/compressed-run.d.ts +11 -0
  8. package/dist/animation/compressed-run.js +134 -33
  9. package/dist/animation/cursor-glyphs.js +24 -5
  10. package/dist/animation/cursor-overlay.js +6 -6
  11. package/dist/animation/embed-namespace.js +6 -1
  12. package/dist/animation/embed-timeline.js +54 -10
  13. package/dist/animation/index.d.ts +3 -3
  14. package/dist/animation/index.js +3 -3
  15. package/dist/animation/magic-move.js +22 -16
  16. package/dist/animation/motion-presets.js +35 -9
  17. package/dist/animation/overlay-schema.d.ts +14 -14
  18. package/dist/animation/overlay-schema.js +9 -4
  19. package/dist/animation/resolve-overlays.js +14 -5
  20. package/dist/animation/svg-generator.d.ts +1 -1
  21. package/dist/animation/svg-generator.js +283 -133
  22. package/dist/animation/text-address.js +13 -4
  23. package/dist/animation/transition-schema.d.ts +2 -2
  24. package/dist/animation/transition-schema.js +264 -60
  25. package/dist/capture/animated-image-static-frame.js +86 -35
  26. package/dist/capture/animation-frame.js +26 -11
  27. package/dist/capture/authenticated-animated-image-bytes.js +106 -44
  28. package/dist/capture/backdrop-composite-raster.js +46 -25
  29. package/dist/capture/backdrop-effect-neutralization.js +17 -11
  30. package/dist/capture/backdrop-effect-space.js +18 -19
  31. package/dist/capture/backdrop-isolation.js +7 -8
  32. package/dist/capture/background-image-sizing.js +84 -43
  33. package/dist/capture/broken-image-fallback.js +43 -34
  34. package/dist/capture/broken-image-icon-raster.js +8 -4
  35. package/dist/capture/collapsed-border-fragment-cdp.js +94 -75
  36. package/dist/capture/collapsed-border-fragment-record.js +111 -91
  37. package/dist/capture/effective-appearance-cdp.js +5 -5
  38. package/dist/capture/effective-appearance.js +70 -25
  39. package/dist/capture/embed.d.ts +4 -2
  40. package/dist/capture/embed.js +27 -14
  41. package/dist/capture/emoji.js +91 -40
  42. package/dist/capture/frame-scroll-state.js +114 -80
  43. package/dist/capture/generic-font-probe.js +23 -13
  44. package/dist/capture/index.d.ts +6 -4
  45. package/dist/capture/index.js +220 -115
  46. package/dist/capture/input-value-geometry.js +4 -4
  47. package/dist/capture/line-clamp.d.ts +2 -2
  48. package/dist/capture/line-clamp.js +63 -58
  49. package/dist/capture/native-control-decoration-raster.js +124 -77
  50. package/dist/capture/native-control-decoration.d.ts +1 -1
  51. package/dist/capture/native-control-decoration.js +21 -14
  52. package/dist/capture/native-control-raster.js +54 -25
  53. package/dist/capture/native-scrollbar-raster.js +24 -23
  54. package/dist/capture/projective-owner.d.ts +1 -10
  55. package/dist/capture/pseudo-backdrop-isolation.js +5 -4
  56. package/dist/capture/pseudo-fragment-cdp.js +111 -68
  57. package/dist/capture/pseudo-fragment-protocol.js +45 -24
  58. package/dist/capture/pseudo-style-cdp.d.ts +1 -1
  59. package/dist/capture/pseudo-style-cdp.js +59 -46
  60. package/dist/capture/raf-clock.js +28 -18
  61. package/dist/capture/replaced-element-raster.js +42 -31
  62. package/dist/capture/replaced-media-frame.js +51 -23
  63. package/dist/capture/replaced-snapshot-geometry.js +19 -13
  64. package/dist/capture/script/color-norm.js +15 -13
  65. package/dist/capture/script/dotted-circle-detect.js +9 -9
  66. package/dist/capture/script/emoji-detect.js +18 -14
  67. package/dist/capture/script/font-family-default.js +70 -39
  68. package/dist/capture/script/font-feature-values.js +13 -9
  69. package/dist/capture/script/font-metrics.js +26 -21
  70. package/dist/capture/script/font-palette.js +17 -9
  71. package/dist/capture/script/index.js +459 -420
  72. package/dist/capture/script/line-clamp.d.ts +4 -4
  73. package/dist/capture/script/line-clamp.js +91 -95
  74. package/dist/capture/script/placeholder-shown.js +11 -7
  75. package/dist/capture/script/pseudo-rules.js +16 -18
  76. package/dist/capture/script/utils.js +24 -25
  77. package/dist/capture/script/walker/background-attachment.d.ts +1 -1
  78. package/dist/capture/script/walker/background-attachment.js +59 -48
  79. package/dist/capture/script/walker/borders-backgrounds.d.ts +1 -1
  80. package/dist/capture/script/walker/borders-backgrounds.js +204 -120
  81. package/dist/capture/script/walker/capture-phases.js +58 -53
  82. package/dist/capture/script/walker/collapsed-border.d.ts +9 -1
  83. package/dist/capture/script/walker/collapsed-border.js +63 -17
  84. package/dist/capture/script/walker/counter-prewalk.js +33 -19
  85. package/dist/capture/script/walker/counter-style-resolver.js +58 -46
  86. package/dist/capture/script/walker/fieldset-legend.js +3 -3
  87. package/dist/capture/script/walker/form-controls.d.ts +1 -1
  88. package/dist/capture/script/walker/form-controls.js +176 -147
  89. package/dist/capture/script/walker/fragmentation.js +53 -37
  90. package/dist/capture/script/walker/inline-svg-decisions.d.ts +8 -0
  91. package/dist/capture/script/walker/inline-svg-decisions.js +37 -0
  92. package/dist/capture/script/walker/inline-svg.js +212 -187
  93. package/dist/capture/script/walker/input-value.d.ts +1 -1
  94. package/dist/capture/script/walker/input-value.js +70 -62
  95. package/dist/capture/script/walker/lists-counters.d.ts +1 -1
  96. package/dist/capture/script/walker/lists-counters.js +17 -15
  97. package/dist/capture/script/walker/masks-clips-decisions.d.ts +15 -0
  98. package/dist/capture/script/walker/masks-clips-decisions.js +105 -0
  99. package/dist/capture/script/walker/masks-clips.js +172 -224
  100. package/dist/capture/script/walker/pseudo-content.d.ts +59 -3
  101. package/dist/capture/script/walker/pseudo-content.js +493 -470
  102. package/dist/capture/script/walker/pseudo-inject.js +36 -24
  103. package/dist/capture/script/walker/replaced-elements.js +18 -13
  104. package/dist/capture/script/walker/resize-handle.js +21 -21
  105. package/dist/capture/script/walker/text-segments.d.ts +139 -0
  106. package/dist/capture/script/walker/text-segments.js +607 -540
  107. package/dist/capture/script/walker/transforms.js +23 -22
  108. package/dist/capture/script/warnings.js +7 -7
  109. package/dist/capture/script.generated.js +1 -1
  110. package/dist/capture/scrollbar-capture.js +119 -87
  111. package/dist/capture/summary-marker-cdp.js +32 -16
  112. package/dist/capture/svg-affine-freeze.js +12 -8
  113. package/dist/capture/text-fragment-geometry.js +9 -9
  114. package/dist/capture/text-fragment-spans.js +87 -40
  115. package/dist/capture/text-line-origin.js +4 -6
  116. package/dist/capture/text-paint-geometry-cdp.d.ts +29 -2
  117. package/dist/capture/text-paint-geometry-cdp.js +105 -53
  118. package/dist/capture/tree-envelope.js +15 -18
  119. package/dist/capture/types.d.ts +6 -24
  120. package/dist/capture/warnings.d.ts +8 -7
  121. package/dist/capture/warnings.js +11 -8
  122. package/dist/cli/animate-artifact.js +1 -3
  123. package/dist/cli/animate-capture-session.js +1 -3
  124. package/dist/cli/animate-captured-frame-stage.d.ts +42 -0
  125. package/dist/cli/animate-captured-frame-stage.js +29 -0
  126. package/dist/cli/animate-command.js +23 -0
  127. package/dist/cli/animate-compression.d.ts +42 -0
  128. package/dist/cli/animate-compression.js +307 -0
  129. package/dist/cli/animate-debug.js +8 -8
  130. package/dist/cli/animate-frame-capture.d.ts +5 -0
  131. package/dist/cli/animate-frame-capture.js +1 -1
  132. package/dist/cli/animate-orchestrator.d.ts +81 -121
  133. package/dist/cli/animate-orchestrator.js +442 -523
  134. package/dist/cli/animate-states-run-stage.d.ts +7 -0
  135. package/dist/cli/animate-states-run-stage.js +6 -0
  136. package/dist/cli/capture.js +53 -8
  137. package/dist/cli/common.js +16 -10
  138. package/dist/cli/composite.js +36 -13
  139. package/dist/cli/hover-detect.js +18 -7
  140. package/dist/cli/index.js +22 -0
  141. package/dist/cli/mutation-detect.d.ts +1 -0
  142. package/dist/cli/mutation-detect.js +31 -10
  143. package/dist/cli/review.d.ts +1 -1
  144. package/dist/cli/review.js +20 -8
  145. package/dist/cli/scrubber.js +6 -2
  146. package/dist/cli/storyboard.d.ts +51 -51
  147. package/dist/cli/storyboard.js +8 -2
  148. package/dist/cli/studio.js +6 -2
  149. package/dist/cli/svg-to-image.js +1 -1
  150. package/dist/cli/svg-to-video-core.js +15 -10
  151. package/dist/cli/template.js +11 -11
  152. package/dist/cli/term.js +3 -5
  153. package/dist/cli/type-resample.js +36 -10
  154. package/dist/font-family-stack.js +8 -8
  155. package/dist/font-feature-values-cascade.js +1 -2
  156. package/dist/index.d.ts +5 -5
  157. package/dist/index.js +4 -4
  158. package/dist/post-processing/hoist-image-payloads.js +11 -11
  159. package/dist/post-processing/optimize.js +5 -2
  160. package/dist/render/advanced-gradient-raster.js +4 -7
  161. package/dist/render/background-attachment.js +7 -5
  162. package/dist/render/background-inline-paint.d.ts +97 -0
  163. package/dist/render/background-inline-paint.js +616 -0
  164. package/dist/render/border-paint.d.ts +44 -0
  165. package/dist/render/border-paint.js +894 -0
  166. package/dist/render/borders.d.ts +4 -4
  167. package/dist/render/borders.js +230 -86
  168. package/dist/render/broken-image-fallback.js +1 -2
  169. package/dist/render/clip-path.js +47 -13
  170. package/dist/render/cluster-fallback.js +96 -37
  171. package/dist/render/colors.js +9 -3
  172. package/dist/render/conic-raster.js +1 -1
  173. package/dist/render/culling-geometry.js +101 -75
  174. package/dist/render/custom-scrollbar.js +1 -3
  175. package/dist/render/decoration-fragment-ownership.js +19 -11
  176. package/dist/render/device-chrome.js +19 -15
  177. package/dist/render/element-tree-to-svg.d.ts +116 -14
  178. package/dist/render/element-tree-to-svg.js +595 -2454
  179. package/dist/render/embedded-font-builder.d.ts +1 -1
  180. package/dist/render/embedded-font-builder.js +50 -35
  181. package/dist/render/embolden-outline.js +10 -12
  182. package/dist/render/emoji-presentation-priority.js +6 -2
  183. package/dist/render/font-resolution.d.ts +10 -5
  184. package/dist/render/font-resolution.js +847 -533
  185. package/dist/render/form-controls.js +93 -57
  186. package/dist/render/format.d.ts +1 -1
  187. package/dist/render/format.js +2 -2
  188. package/dist/render/generic-script-families.js +446 -125
  189. package/dist/render/glyph-helper-font.js +70 -28
  190. package/dist/render/glyph-helper-outline.js +1 -1
  191. package/dist/render/glyph-helper-transport.js +42 -18
  192. package/dist/render/glyph-helper.d.ts +3 -3
  193. package/dist/render/glyph-helper.js +133 -88
  194. package/dist/render/gradient-defs.js +51 -26
  195. package/dist/render/gradients.js +47 -18
  196. package/dist/render/harfbuzz-shaper.d.ts +1 -1
  197. package/dist/render/harfbuzz-shaper.js +91 -27
  198. package/dist/render/hb-subset.js +53 -21
  199. package/dist/render/helper-acquire.js +1 -2
  200. package/dist/render/helper-availability-contract.js +1 -2
  201. package/dist/render/host-platform.js +1 -1
  202. package/dist/render/icu-helper-acquire.js +24 -14
  203. package/dist/render/icu-helper.js +7 -5
  204. package/dist/render/image-pattern.js +21 -25
  205. package/dist/render/index.d.ts +4 -4
  206. package/dist/render/index.js +3 -3
  207. package/dist/render/linux-target-strike.js +16 -9
  208. package/dist/render/list-marker-geometry.js +43 -13
  209. package/dist/render/mask-origin-clip.js +2 -4
  210. package/dist/render/mask-position.js +12 -14
  211. package/dist/render/mask.js +84 -66
  212. package/dist/render/outline-paint.d.ts +20 -0
  213. package/dist/render/outline-paint.js +255 -0
  214. package/dist/render/overflow-clip.js +28 -17
  215. package/dist/render/paint-order.js +2 -5
  216. package/dist/render/pseudo-filter.js +1 -4
  217. package/dist/render/pseudo-fragments.js +71 -37
  218. package/dist/render/real-text-layer.d.ts +15 -0
  219. package/dist/render/real-text-layer.js +186 -0
  220. package/dist/render/render-state-collectors.d.ts +11 -0
  221. package/dist/render/render-state-collectors.js +87 -0
  222. package/dist/render/resize-handle.js +9 -16
  223. package/dist/render/script-segmentation.d.ts +1 -1
  224. package/dist/render/script-segmentation.js +17 -11
  225. package/dist/render/stacking.js +66 -36
  226. package/dist/render/svg-inline.d.ts +17 -0
  227. package/dist/render/svg-inline.js +178 -5
  228. package/dist/render/svg-viewport-matrix.d.ts +72 -0
  229. package/dist/render/svg-viewport-matrix.js +139 -0
  230. package/dist/render/synchronous-scope.js +3 -3
  231. package/dist/render/synth-test-fonts.js +20 -4
  232. package/dist/render/synthesis-decision.js +4 -4
  233. package/dist/render/text-affine.js +31 -27
  234. package/dist/render/text-paint.d.ts +42 -0
  235. package/dist/render/text-paint.js +478 -0
  236. package/dist/render/text-run-provenance.d.ts +1 -1
  237. package/dist/render/text-run-provenance.js +42 -15
  238. package/dist/render/text-to-path.d.ts +25 -3
  239. package/dist/render/text-to-path.js +437 -258
  240. package/dist/render/text.js +334 -147
  241. package/dist/render/unicode-classification.d.ts +2 -2
  242. package/dist/render/unicode-classification.js +327 -130
  243. package/dist/render/unicode-font-routing.darwin.generated.js +1 -1
  244. package/dist/render/vertical-text.d.ts +11 -0
  245. package/dist/render/vertical-text.js +145 -51
  246. package/dist/render/win-font-fallback.js +163 -84
  247. package/dist/review/client.bundle.generated.js +1 -1
  248. package/dist/review/client.js +3 -1
  249. package/dist/review/compare-pngs.js +1 -2
  250. package/dist/review/glyph-compare.js +69 -48
  251. package/dist/review/linux-unicode-evidence.d.ts +1 -1
  252. package/dist/review/linux-unicode-evidence.js +180 -27
  253. package/dist/review/region-overlay.js +10 -2
  254. package/dist/review/roll-differential.js +48 -12
  255. package/dist/review/server.d.ts +1 -1
  256. package/dist/review/server.js +5 -3
  257. package/dist/review/side-digest.js +6 -1
  258. package/dist/review/source-drift-gate.js +25 -8
  259. package/dist/review/stage-evidence.js +10 -9
  260. package/dist/scroll/composer.d.ts +10 -0
  261. package/dist/scroll/composer.js +72 -46
  262. package/dist/scroll/executor.js +30 -17
  263. package/dist/scroll/hoist-fixed.d.ts +1 -1
  264. package/dist/scroll/hoist-fixed.js +1 -1
  265. package/dist/scroll/hoist-sticky.js +1 -3
  266. package/dist/scroll/pattern.js +18 -7
  267. package/dist/scrubber/client.bundle.generated.js +1 -1
  268. package/dist/scrubber/client.js +170 -45
  269. package/dist/scrubber/crop.js +2 -2
  270. package/dist/scrubber/embed.js +21 -23
  271. package/dist/scrubber/server.js +48 -16
  272. package/dist/scrubber/trim.d.ts +1 -1
  273. package/dist/scrubber/trim.js +6 -3
  274. package/dist/studio/agent-tools.d.ts +74 -74
  275. package/dist/studio/agent-tools.js +77 -23
  276. package/dist/studio/annotations.js +41 -26
  277. package/dist/studio/app-projects.js +10 -6
  278. package/dist/studio/authoring.js +137 -96
  279. package/dist/studio/client.bundle.generated.js +1 -1
  280. package/dist/studio/client.js +343 -167
  281. package/dist/studio/compile.js +5 -3
  282. package/dist/studio/cursor-choreography.js +22 -8
  283. package/dist/studio/healing.js +59 -12
  284. package/dist/studio/index.d.ts +1 -1
  285. package/dist/studio/index.js +1 -1
  286. package/dist/studio/interaction-observer.js +157 -33
  287. package/dist/studio/interactions.js +88 -57
  288. package/dist/studio/interactive-compile.js +42 -15
  289. package/dist/studio/project-json-schema.js +1 -1
  290. package/dist/studio/project-schema.d.ts +110 -110
  291. package/dist/studio/project-schema.js +102 -31
  292. package/dist/studio/project.js +4 -2
  293. package/dist/studio/recording.d.ts +72 -72
  294. package/dist/studio/recording.js +141 -38
  295. package/dist/studio/server.js +17 -9
  296. package/dist/studio/timeline.d.ts +1 -1
  297. package/dist/studio/timeline.js +68 -40
  298. package/dist/studio/treatment-schema.d.ts +2 -2
  299. package/dist/studio/treatment-schema.js +12 -3
  300. package/dist/studio/treatments.js +56 -17
  301. package/dist/studio/video-review.d.ts +1 -1
  302. package/dist/studio/video-review.js +18 -6
  303. package/dist/templates/builtin/background-loop.d.ts +6 -6
  304. package/dist/templates/builtin/background-loop.js +70 -35
  305. package/dist/templates/builtin/caption.js +40 -5
  306. package/dist/templates/builtin/chart.js +247 -47
  307. package/dist/templates/builtin/chat.js +56 -12
  308. package/dist/templates/builtin/compare.d.ts +1 -1
  309. package/dist/templates/builtin/compare.js +34 -17
  310. package/dist/templates/builtin/counter.js +11 -7
  311. package/dist/templates/builtin/cta.js +14 -4
  312. package/dist/templates/builtin/device-mockup.js +30 -6
  313. package/dist/templates/builtin/kinetic-text.js +69 -11
  314. package/dist/templates/builtin/lower-third.js +11 -7
  315. package/dist/templates/builtin/odometer.js +12 -6
  316. package/dist/templates/builtin/quote.js +5 -2
  317. package/dist/templates/builtin/stat.js +26 -11
  318. package/dist/templates/builtin/subscribe.js +79 -11
  319. package/dist/templates/builtin/title-card.js +5 -2
  320. package/dist/templates/formats.js +5 -1
  321. package/dist/templates/index.d.ts +7 -7
  322. package/dist/templates/index.js +7 -7
  323. package/dist/templates/registry.js +22 -13
  324. package/dist/templates/render.js +4 -1
  325. package/dist/terminal/emulator.js +9 -2
  326. package/dist/terminal/incremental.js +24 -6
  327. package/dist/terminal/index.d.ts +4 -0
  328. package/dist/terminal/index.js +9 -3
  329. package/dist/terminal/pty.js +7 -5
  330. package/dist/terminal/render.js +19 -4
  331. package/dist/terminal/theme.js +48 -6
  332. package/dist/tree-ops/decoration-propagation.js +9 -12
  333. package/dist/tree-ops/resize-embedded-images.js +11 -5
  334. package/dist/tree-ops/swept-transform-bounds.js +16 -11
  335. package/dist/tree-ops/tree-diff.js +1 -3
  336. package/dist/tree-ops/viewbox-culling.js +9 -7
  337. package/dist/utils/escapeHtml.js +1 -5
  338. package/dist/utils/local-server.js +7 -2
  339. package/dist/utils/region-feedback.js +1 -4
  340. package/dist/utils/transparent-background.js +4 -1
  341. package/dist/vertical-orientation.js +2 -5
  342. package/llms.txt +22 -6
  343. package/package.json +19 -12
  344. package/schemas/animate-config.schema.json +3 -0
  345. package/dist/capture/paged-collapsed-table-cdp.d.ts +0 -67
  346. package/dist/capture/paged-collapsed-table-cdp.js +0 -88
  347. package/dist/capture/paged-collapsed-table-record.d.ts +0 -170
  348. package/dist/capture/paged-collapsed-table-record.js +0 -487
package/FEATURES.md CHANGED
@@ -5,6 +5,7 @@ Each feature has a visual regression test that compares HTML-to-PNG with SVG-to-
5
5
  ## Rendering Features
6
6
 
7
7
  ### Text
8
+
8
9
  - [x] **text-basic**: Plain text rendering (font-size, font-family, color)
9
10
  - [x] **text-bold**: Bold/weight text
10
11
  - [x] **text-center**: Centered text (text-align: center)
@@ -18,6 +19,7 @@ Each feature has a visual regression test that compares HTML-to-PNG with SVG-to-
18
19
  - [x] **text-font-stretch-underline**: CSS `font-stretch` end to end — condensed/expanded cut selection for declared families (Blink's trait-based style matcher), the `wdth` variation axis on the macOS `system-ui` face and variable webfonts, and decoration metrics resolved on the same width-matched face as the glyphs
19
20
 
20
21
  ### Backgrounds & Colors
22
+
21
23
  - [x] **bg-solid**: Solid background colors
22
24
  - [x] **bg-transparent**: Semi-transparent backgrounds (rgba)
23
25
  - [x] **bg-nested**: Nested backgrounds (child on top of parent)
@@ -26,12 +28,14 @@ Each feature has a visual regression test that compares HTML-to-PNG with SVG-to-
26
28
  - [ ] **bg-dark-mode** (DM-455, doc 29): Dark-mode capture support — caller-chosen `colorScheme`, dark form-control palette, scheme-aware transparent-root fallback
27
29
 
28
30
  ### Borders
31
+
29
32
  - [x] **border-solid**: Solid borders with color
30
33
  - [x] **border-radius**: Rounded corners
31
34
  - [x] **border-radius-pill**: Fully rounded (pill shape)
32
35
  - [x] **inline-box-decoration-break**: wrapped inline backgrounds / borders paint per line-fragment (`slice` + `clone`); first/last fragment own the start/end edges in slice mode, every fragment paints a full box in clone mode
33
36
 
34
37
  ### Layout
38
+
35
39
  - [x] **layout-flex-row**: Horizontal flex layout with gap
36
40
  - [x] **layout-flex-col**: Vertical flex layout
37
41
  - [x] **layout-flex-center**: Centered content (justify-content/align-items)
@@ -43,6 +47,7 @@ Each feature has a visual regression test that compares HTML-to-PNG with SVG-to-
43
47
  - [x] **animated-projective-frame-state** (DM-2359/DM-2356/DM-2492/DM-2493, docs 186/189): `animationTimeMs` exactly pauses document timelines before all capture prepasses and exposes the CDP quad, composed 3D state, residual, and selected atomic raster owner without a fitted 2D approximation. Same-frame used-preserve facts select Blink's direct-parent rendering-context root; a strict 25-family/four-profile native three-OS DPR-1/2 gate rejects larger crops, warnings, restoration drift, incomplete evidence, and nine ownership mutations.
44
48
 
45
49
  ### Components
50
+
46
51
  - [x] **comp-button**: Button with background, border, padding, text
47
52
  - [x] **comp-badge**: Small badge with colored background and text
48
53
  - [x] **comp-card**: Card with background, border, radius, content
@@ -54,17 +59,21 @@ Each feature has a visual regression test that compares HTML-to-PNG with SVG-to-
54
59
  - [ ] **comp-nav**: Navigation bar with logo and links
55
60
 
56
61
  ### SVG
62
+
57
63
  - [x] **svg-inline**: Inline SVG elements are cloned as self-contained vectors; CSS-only clip/mask effects preserve native fill-box, stroke-box, and view-box resolution (DM-2328)
58
64
  - [x] **clip-path-url-reference-ownership** (DM-2362, docs 39/130): bare same-document/external URL clips preserve HTML border/object-bounding-box and SVG forced-fill ownership; Blink-invalid URL-plus-box declarations stay inactive, including stale captured trees and nested frames.
59
65
 
60
66
  ### Masks
67
+
61
68
  - [x] **mask-contain-cover-arbitrary-position** (DM-2379, doc 20): URL masks capture Chromium-decoded intrinsic ratios and emit concrete Blink-fitted rectangles for percentage, length, and calc positions; zoom/DPR, physical writing axes, multilayer, and slice/clone fragment controls reject the retired SVG Min/Mid/Max buckets.
62
69
 
63
70
  ### Text effects
71
+
64
72
  - [x] **text-bg-clip-gradient** (DM-462): `background-clip: text` + `-webkit-text-fill-color: transparent` — gradient fills the glyph shapes via SVG `<mask>` over the bg-color rect
65
73
  - [x] **pseudo-filter-functions** (DM-2367, doc 38): generated empty/text/image paint supports blur, drop-shadow, brightness, contrast, grayscale, hue-rotate, invert, opacity, saturate, sepia, and ordered lists through Chromium's native SVG CSS-filter pipeline; identity/none, transform, clip, stack slot, zoom, DPR, and filter-stripped mutation controls are browser-gated.
66
74
 
67
75
  ### Replaced elements (rasterized as static snapshot — DM-457)
76
+
68
77
  - [x] **replaced-canvas-shape**: `<canvas>` with drawn shapes — bitmap survives via `page.screenshot`
69
78
  - [x] **replaced-video-poster**: `<video poster=…>` paused — poster image captured
70
79
  - [x] **replaced-canvas-overlay**: `<canvas>` under a positioned `<div z-index:10>` overlay — overlay does NOT bleed into the canvas snapshot
@@ -79,11 +88,13 @@ Each feature has a visual regression test that compares HTML-to-PNG with SVG-to-
79
88
  - [x] **snapshot-isolation-pseudo-overlay** (DM-458, `tests/snapshot-isolation.tsx`): canvas covered by a sibling's `::after` pseudo overlay. Inspection-style — decodes the captured snapshot's PNG data URI and asserts no overlay-color pixels leaked through. Catches regressions in the hide-everything-else stylesheet that a comparison-style fixture wouldn't.
80
89
 
81
90
  ### Showcase Integration Tests
91
+
82
92
  - [x] **showcase-typography**: Full-page layout with headings, badges, code blocks, inline code, status text
83
93
  - [x] **showcase-cards**: Card list with badges, buttons, metadata
84
94
  - [x] **showcase-forms**: Publish form with labels, inputs, pre block, button
85
95
 
86
96
  ### Composed Parity Integration Tests
97
+
87
98
  - [x] **composed-multilingual-flex-grid**: Platform fallback and bidi isolation inside flex/grid messaging UI, with neutral-wrapper and node-split relations
88
99
  - [x] **composed-responsive-fragmented-controls**: Native controls inside responsive multicol layout with equivalent grid syntax
89
100
  - [x] **composed-gradient-mask-clip-stacking**: Gradients, masks, and clips under explicit stacking ownership and reversed DOM order
@@ -97,6 +108,7 @@ Each feature has a visual regression test that compares HTML-to-PNG with SVG-to-
97
108
  Tests are defined in `tests/features.ts` (26 feature tests) and `tests/showcase.ts` (3 integration tests). Both use the shared runner in `tests/runner.ts`.
98
109
 
99
110
  For each test case:
111
+
100
112
  1. Define an HTML snippet (feature tests) or full-page HTML (showcase tests) exercising the feature
101
113
  2. Render the HTML in Playwright (Chromium) and capture as PNG ("expected")
102
114
  3. Capture the DOM via `captureElementTree` on the Playwright page
package/README.md CHANGED
@@ -14,7 +14,7 @@
14
14
 
15
15
  **Domotion turns real HTML/CSS into one self-contained, animated SVG** — an accurate reproduction of the rendered page, with optional animation and simulated interaction built in. It embeds the exact captured glyph data and positioning, so playback never falls back to a viewer's system fonts; the output scales crisply at any size and embeds anywhere with a plain `<img>`, no external assets.
16
16
 
17
- Beyond raw capture it ships a **template library** that turns a few flags into a polished animated SVG, **terminal-session capture** (a recording → an animated terminal), **scroll capture** (a long page replayed as one self-contained scrolling SVG), multi-frame **animation** with parameterized and custom transitions, overlays, and simulated interaction, **scene storyboarding** (sequence distinct scenes end-to-end), **brand kits** and **social-format presets** (reel / square / portrait / landscape), **device-chrome** framing, **nested compositing** (animated layers inside animated layers), one-command **SVG → MP4/WebM**, and a fidelity **review** tool.
17
+ Beyond raw capture it ships **Domotion Studio**, a local visual workspace for authoring and reviewing versioned projects; a **template library** that turns a few flags into a polished animated SVG; **terminal-session capture** (a recording → an animated terminal); **scroll capture** (a long page replayed as one self-contained scrolling SVG); multi-frame **animation** with parameterized and custom transitions, overlays, and simulated interaction; **scene storyboarding** (sequence distinct scenes end-to-end); **brand kits** and **social-format presets** (reel / square / portrait / landscape); **device-chrome** framing; **nested compositing** (animated layers inside animated layers); one-command **SVG → MP4/WebM**; and fidelity **review** tools.
18
18
 
19
19
  <p align="center">
20
20
  <img src="examples/output/hero-product-demo.svg" alt="An analytics dashboard assembling itself inside a browser window — KPI cards rise in, a bar chart grows with its peak highlighted, a search query types itself, and a nav item is clicked — all in one self-contained animated SVG" width="760">
@@ -65,6 +65,8 @@ predictable. For a zero-install trial, run
65
65
  [animation guide](https://brianwestphal.github.io/domotion/usage/animate/).
66
66
  - **Generate a polished asset from flags:** use `domotion template`; browse the
67
67
  [template guide](https://brianwestphal.github.io/domotion/usage/templates/).
68
+ - **Author and review a versioned project visually:** use `domotion-studio`; see
69
+ the [Studio guide](https://brianwestphal.github.io/domotion/usage/studio/).
68
70
 
69
71
  The [Quick start](https://brianwestphal.github.io/domotion/start/quickstart/)
70
72
  walks through the first capture and animation end to end.
@@ -97,6 +99,12 @@ never become idle.
97
99
 
98
100
  Same-origin `<iframe>` content is recursed into the capture as native SVG rather than flattened to a screenshot; opt into cross-origin frames you trust with `--cross-origin-frames "<hosts>"`.
99
101
 
102
+ Text defaults to self-contained, pixel-faithful embedded fonts. For inline SVG,
103
+ `--real-text` adds a paintless authored-text layer for search, selection, copy,
104
+ and assistive technology. `--text-mode system-font` instead paints authored
105
+ `<text>` using the viewer's installed fonts; it can be smaller, but it is not
106
+ pixel-faithful and should be used only when those fonts are guaranteed.
107
+
100
108
  For a multi-frame animated SVG, write a JSON config and run:
101
109
 
102
110
  ```bash
@@ -126,6 +134,24 @@ domotion template kinetic-text --text "Ship it" --variant pop --by char -o title
126
134
 
127
135
  Built-ins (14): **lower-third** (broadcast banner) · **kinetic-text** (animated typography) · **chart** (column / bar / line / pie / donut) · **chat** (message thread) · **subscribe** (follow pop-up) · **background-loop** (seamless looping background) · **device-mockup** (wrap a page in a phone / browser / window bezel) · and a **creative-template pack** of full-bleed text/number cards: **title-card**, **quote**, **caption**, **cta**, **counter**, **stat**, **compare**. Every template adapts to a `--format` social preset (reel / square / portrait / landscape) and a `--brand` kit (palette / type / logo). Third-party templates are npm packages named `domotion-template-<name>`.
128
136
 
137
+ ### Domotion Studio — visual project authoring
138
+
139
+ Domotion Studio is a local, browser-based workspace for building and reviewing
140
+ versioned Domotion projects. It provides story and scene authoring, a detailed
141
+ multitrack timeline, embedded playback and review annotations, real-interaction
142
+ import, and generation/review hooks while keeping project files as portable,
143
+ validated JSON inside an explicitly scoped workspace.
144
+
145
+ ```bash
146
+ # Open a workspace, or pass an existing Studio project JSON file.
147
+ domotion-studio --workspace ./demo-project
148
+ domotion-studio ./demo-project/product-tour.json
149
+ ```
150
+
151
+ Studio opens locally by default. In automation, pass `--no-open` and use the
152
+ printed loopback URL. Run `domotion-studio --help` or read the
153
+ [Studio guide](https://brianwestphal.github.io/domotion/usage/studio/).
154
+
129
155
  ### Terminal sessions
130
156
 
131
157
  Turn a recorded terminal session into a self-contained animated SVG — real text, real color, native SVG (no raster frames). Record with [asciinema](https://asciinema.org), then convert:
@@ -186,7 +212,7 @@ writes the final `actual.svg`, one shared HAR, and an `expected.png` plus
186
212
  `captured-tree.json` for each composed frame. Use `--debug-dir <path>` to choose
187
213
  the bundle location.
188
214
 
189
- For an *animated* SVG, the package also ships `svg-scrubber` — a local video-style bench to play / pause / scrub / mark an in-out range, export the current frame as PNG, export the range as MP4, or trim it to a new self-contained animated SVG. Add `--review` to file a focused issue against a moment in the timeline: it writes an importable `.ticket` (frame time, range, and drawn regions) the same way `svg-review` builds a report for a still.
215
+ For an _animated_ SVG, the package also ships `svg-scrubber` — a local video-style bench to play / pause / scrub / mark an in-out range, export the current frame as PNG, export the range as MP4, or trim it to a new self-contained animated SVG. Add `--review` to file a focused issue against a moment in the timeline: it writes an importable `.ticket` (frame time, range, and drawn regions) the same way `svg-review` builds a report for a still.
190
216
 
191
217
  ### Scripting API
192
218
 
@@ -200,7 +226,16 @@ const page = await browser.newPage();
200
226
  await page.setContent(`<div style="padding:20px;color:white;background:#0d1117">Hello</div>`);
201
227
 
202
228
  const tree = await captureElementTree(page, "body", { x: 0, y: 0, width: 800, height: 200 });
203
- const svg = elementTreeToSvg(tree, 800, 200);
229
+ const svg = elementTreeToSvg(tree, 800, 200, {
230
+ // Optional: searchable/selectable text when this SVG is embedded inline.
231
+ // This cannot expose text through <img src="…">.
232
+ realTextLayer: true,
233
+ // Optional: how text is emitted. Default "embedded-font" is self-contained
234
+ // and pixel-faithful. "system-font" instead emits authored <text> painted
235
+ // by the VIEWER's installed fonts — smaller output, but not pixel-faithful
236
+ // and only correct where the fonts are present (CLI: --text-mode system-font).
237
+ // renderTextMode: "system-font",
238
+ });
204
239
 
205
240
  console.log(svg);
206
241
  await browser.close();
@@ -229,7 +264,7 @@ npm run demos:examples # run the bundled example demo scripts
229
264
  - `FEATURES.md` — per-feature support checklist with links to test fixtures.
230
265
  - `docs/` — requirements docs covering rendering fidelity, supported CSS features, and known caveats.
231
266
  - [`llms.txt`](llms.txt) — a concise, self-contained guide for **AI agents using Domotion as a tool** (Claude, Cursor, etc.): the CLIs, config schema, template library, API, gotchas, and a required rendered-pixel/video review gate before an SVG is handed back. Point your agent at it.
232
- - `CLAUDE.md` — guidance for AI assistants working *on this repo's* source (different audience from `llms.txt`).
267
+ - `CLAUDE.md` — guidance for AI assistants working _on this repo's_ source (different audience from `llms.txt`).
233
268
 
234
269
  ## License
235
270
 
@@ -24,10 +24,10 @@ Chrome's layout, `registerWebfont` for Domotion's outlines).
24
24
 
25
25
  ## Provenance
26
26
 
27
- | File | Upstream | License |
28
- |---|---|---|
29
- | `DomotionFixtureMono-Regular.ttf` | [JetBrains Mono](https://github.com/JetBrains/JetBrainsMono) Regular | SIL OFL 1.1 — `LICENSE-jetbrains-mono.txt` |
30
- | `DomotionFixtureSerif-Regular.ttf` | [IBM Plex Serif](https://github.com/IBM/plex) Regular | SIL OFL 1.1 — `LICENSE-ibm-plex-serif.txt` |
27
+ | File | Upstream | License |
28
+ | ---------------------------------- | -------------------------------------------------------------------- | ------------------------------------------ |
29
+ | `DomotionFixtureMono-Regular.ttf` | [JetBrains Mono](https://github.com/JetBrains/JetBrainsMono) Regular | SIL OFL 1.1 — `LICENSE-jetbrains-mono.txt` |
30
+ | `DomotionFixtureSerif-Regular.ttf` | [IBM Plex Serif](https://github.com/IBM/plex) Regular | SIL OFL 1.1 — `LICENSE-ibm-plex-serif.txt` |
31
31
 
32
32
  Both are unmodified apart from being subsetted to the characters the fixtures
33
33
  paint. The OFL permits redistribution of modified copies under a reserved-name
@@ -90,7 +90,13 @@ export function caretShapeRect(inp) {
90
90
  case "block":
91
91
  return { x: leftOf(cellW), y: boxTop, width: cellW, height: boxHeight, opacity: BLOCK_CARET_ALPHA };
92
92
  case "underscore":
93
- return { x: leftOf(cellW), y: inp.baselineY, width: cellW, height: underscoreCaretThicknessPx(inp.fontSize), opacity: 1 };
93
+ return {
94
+ x: leftOf(cellW),
95
+ y: inp.baselineY,
96
+ width: cellW,
97
+ height: underscoreCaretThicknessPx(inp.fontSize),
98
+ opacity: 1,
99
+ };
94
100
  case "bar":
95
101
  default: {
96
102
  const barW = inp.barWidthPx ?? DEFAULT_CARET_WIDTH_PX;
@@ -148,9 +154,7 @@ export function firstLineBaseline(m) {
148
154
  const descentPx = fontBox > 0 ? Math.round(m.fontDescentPx) : m.fontSize * 0.25;
149
155
  const textBoxHeight = ascentPx + descentPx;
150
156
  const lineH = m.lineHeightPx || fontBox || m.fontSize * 1.2;
151
- const lineTop = m.centerInContentBox
152
- ? m.contentTop + Math.max(0, (m.contentHeight - lineH) / 2)
153
- : m.contentTop;
157
+ const lineTop = m.centerInContentBox ? m.contentTop + Math.max(0, (m.contentHeight - lineH) / 2) : m.contentTop;
154
158
  const boxTop = lineTop + Math.floor((lineH - textBoxHeight) / 2);
155
159
  return { baselineY: boxTop + ascentPx, ascentPx, descentPx };
156
160
  }
@@ -39,7 +39,7 @@
39
39
  * (docs/100, Primitive 1), which owns the glyphs and can interleave layers.
40
40
  */
41
41
  import { caretShapeRect, BLOCK_CARET_ALPHA, DEFAULT_CARET_WIDTH_PX } from "./caret-metrics.js";
42
- import { resolveCaretPoint, resolveRangeRects, findAddressedElement } from "./text-address.js";
42
+ import { resolveCaretPoint, resolveRangeRects, findAddressedElement, } from "./text-address.js";
43
43
  import { renderTextAsPath } from "../render/text-to-path.js";
44
44
  import { withRenderTextMode } from "../render/font-resolution.js";
45
45
  /** The standard caret blink period (ms) — the terminal cursor's 1.06 s cycle. */
@@ -169,7 +169,13 @@ function resolveCoveredGlyph(roots, target, charOffset) {
169
169
  for (const run of runs) {
170
170
  for (const ch of run.text) {
171
171
  if (remaining === 0) {
172
- return { char: ch, fontFamily: run.fontFamily, fontWeight: run.fontWeight, fontStyle: run.fontStyle, fontSize: run.fontSize };
172
+ return {
173
+ char: ch,
174
+ fontFamily: run.fontFamily,
175
+ fontWeight: run.fontWeight,
176
+ fontStyle: run.fontStyle,
177
+ fontSize: run.fontSize,
178
+ };
173
179
  }
174
180
  remaining--;
175
181
  }
@@ -330,8 +336,12 @@ function invertedCaretMarkup(track, uid, kf, totalDurationMs, totalSec) {
330
336
  // The glyph is repainted at the CELL's left edge, which is the caret x
331
337
  // for an LTR insertion point and `x − cellWidth` for an RTL one.
332
338
  renderTextAsPath(cg.char, g.x, g.y, {
333
- fontSize: cg.fontSize, fontFamily: cg.fontFamily, fontWeight: cg.fontWeight,
334
- fill: glyphInk, fontStyle: cg.fontStyle, ascentOverride: wp.point.ascentPx,
339
+ fontSize: cg.fontSize,
340
+ fontFamily: cg.fontFamily,
341
+ fontWeight: cg.fontWeight,
342
+ fill: glyphInk,
343
+ fontStyle: cg.fontStyle,
344
+ ascentOverride: wp.point.ascentPx,
335
345
  }));
336
346
  glyphMarkup = gm;
337
347
  }
@@ -410,7 +420,7 @@ function selectionMarkup(sel, si, uid, kf, totalDurationMs, totalSec) {
410
420
  // Sweep anchor: the rect's leading edge in reading order. `edges` are the
411
421
  // successive trailing edges, so the swept extent is their distance from it.
412
422
  const rtl = rect.rtl === true;
413
- const anchor = vertical ? rect.y : (rtl ? rect.x + rect.width : rect.x);
423
+ const anchor = vertical ? rect.y : rtl ? rect.x + rect.width : rect.x;
414
424
  const sweptTo = (edge) => (rtl && !vertical ? anchor - edge : edge - anchor);
415
425
  const stops = [`0%{${prop}:0.01px}`];
416
426
  if (startMs > 0)
@@ -448,9 +458,7 @@ function selectionMarkup(sel, si, uid, kf, totalDurationMs, totalSec) {
448
458
  const place = rtl && !vertical
449
459
  ? `x="0" y="${num(rect.y)}" transform="translate(${num(anchor)},0) scale(-1,1)"`
450
460
  : `x="${num(rect.x)}" y="${num(rect.y)}"`;
451
- const size = vertical
452
- ? `width="${num(rect.width)}" height="0.01"`
453
- : `width="0.01" height="${num(rect.height)}"`;
461
+ const size = vertical ? `width="${num(rect.width)}" height="0.01"` : `width="0.01" height="${num(rect.height)}"`;
454
462
  parts.push(` <rect class="tt-sel" ${place} ${size} fill="${sel.color}" style="animation:${name} ${totalSec.toFixed(2)}s ${timing} infinite"/>`);
455
463
  sweptBefore += rect.edges.length;
456
464
  }
@@ -55,10 +55,13 @@ function innerOf(svg) {
55
55
  function resolveClipOriginPx(origin, base, size) {
56
56
  switch (origin) {
57
57
  case "left":
58
- case "top": return base;
58
+ case "top":
59
+ return base;
59
60
  case "right":
60
- case "bottom": return base + size;
61
- case "center": return base + size / 2;
61
+ case "bottom":
62
+ return base + size;
63
+ case "center":
64
+ return base + size / 2;
62
65
  }
63
66
  const pct = /^([-\d.]+)%$/.exec(origin);
64
67
  if (pct != null)
@@ -71,11 +74,16 @@ function resolveClipOriginPx(origin, base, size) {
71
74
  /** Render a non-clip layer-animation endpoint to its CSS value (transform string / opacity). */
72
75
  function renderAnimValue(property, v) {
73
76
  switch (property) {
74
- case "scale": return `scale(${v})`;
75
- case "translateX": return `translateX(${typeof v === "number" ? `${v}px` : v})`;
76
- case "translateY": return `translateY(${typeof v === "number" ? `${v}px` : v})`;
77
- case "opacity": return `${v}`;
78
- case "transform": return `${v}`;
77
+ case "scale":
78
+ return `scale(${v})`;
79
+ case "translateX":
80
+ return `translateX(${typeof v === "number" ? `${v}px` : v})`;
81
+ case "translateY":
82
+ return `translateY(${typeof v === "number" ? `${v}px` : v})`;
83
+ case "opacity":
84
+ return `${v}`;
85
+ case "transform":
86
+ return `${v}`;
79
87
  }
80
88
  }
81
89
  /**
@@ -100,9 +108,9 @@ export function composeAnimatedLayers(layers, opts) {
100
108
  });
101
109
  // Resolve each layer's geometry + intrinsic size first so we can size the master.
102
110
  const resolved = layers.map((layer, i) => {
103
- const intrinsic = (layer.contentWidth != null && layer.contentHeight != null)
111
+ const intrinsic = layer.contentWidth != null && layer.contentHeight != null
104
112
  ? { w: layer.contentWidth, h: layer.contentHeight }
105
- : parseSvgIntrinsicSize(layer.svg) ?? { w: 0, h: 0 };
113
+ : (parseSvgIntrinsicSize(layer.svg) ?? { w: 0, h: 0 });
106
114
  const w = layer.width ?? intrinsic.w;
107
115
  const h = layer.height ?? intrinsic.h;
108
116
  const x = layer.x ?? 0;
@@ -140,8 +148,11 @@ export function composeAnimatedLayers(layers, opts) {
140
148
  // 2. …then re-anchor its internal timeline to its own start within the master.
141
149
  if (r.period > 0) {
142
150
  content = offsetEmbeddedAnimatedSvgTimeline(content, {
143
- periodMs: r.period, startMs: r.start, masterMs: master,
144
- mode: r.mode, windowMs: r.windowMs,
151
+ periodMs: r.period,
152
+ startMs: r.start,
153
+ masterMs: master,
154
+ mode: r.mode,
155
+ windowMs: r.windowMs,
145
156
  });
146
157
  }
147
158
  // 3. Strip the outer wrapper and re-nest at the layer's box (a nested <svg>
@@ -117,6 +117,17 @@ export interface CompressedRunOptions {
117
117
  /** Paint a root background rect first (e.g. the captured
118
118
  * `styles.rootBgComputed`). Omit for a transparent run. */
119
119
  background?: string;
120
+ /**
121
+ * DM-6SQXGF: append the paintless real-text layer (doc 260) to the run. A
122
+ * compressed run flattens N states into ONE emission (shared content once,
123
+ * step-end birth/shift/recolor tracks) — there are no per-frame visibility
124
+ * groups to gate per-state text, and the glyph births use `opacity` (still
125
+ * find-in-page-visible). So the run emits ONE layer from the FINAL state's
126
+ * tree (the completed content — a stable deduplicated story flow), and the
127
+ * visible chrome+glyph runs are `aria-hidden` while it owns semantics.
128
+ * Intermediate typing prefixes are deliberately not exposed. Default off.
129
+ */
130
+ realText?: boolean;
120
131
  /** Emit the auto-caret track derived from the per-state edit points
121
132
  * (docs/101 machinery). Default false — the config surface decides
122
133
  * defaults later. */