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.
- package/FEATURES.md +12 -0
- package/README.md +39 -4
- package/assets/fonts/fixture/README.md +4 -4
- package/dist/animation/caret-metrics.js +8 -4
- package/dist/animation/caret-track.js +16 -8
- package/dist/animation/composite.js +23 -12
- package/dist/animation/compressed-run.d.ts +11 -0
- package/dist/animation/compressed-run.js +134 -33
- package/dist/animation/cursor-glyphs.js +24 -5
- package/dist/animation/cursor-overlay.js +6 -6
- package/dist/animation/embed-namespace.js +6 -1
- package/dist/animation/embed-timeline.js +54 -10
- package/dist/animation/index.d.ts +3 -3
- package/dist/animation/index.js +3 -3
- package/dist/animation/magic-move.js +22 -16
- package/dist/animation/motion-presets.js +35 -9
- package/dist/animation/overlay-schema.d.ts +14 -14
- package/dist/animation/overlay-schema.js +9 -4
- package/dist/animation/resolve-overlays.js +14 -5
- package/dist/animation/svg-generator.d.ts +1 -1
- package/dist/animation/svg-generator.js +283 -133
- package/dist/animation/text-address.js +13 -4
- package/dist/animation/transition-schema.d.ts +2 -2
- package/dist/animation/transition-schema.js +264 -60
- package/dist/capture/animated-image-static-frame.js +86 -35
- package/dist/capture/animation-frame.js +26 -11
- package/dist/capture/authenticated-animated-image-bytes.js +106 -44
- package/dist/capture/backdrop-composite-raster.js +46 -25
- package/dist/capture/backdrop-effect-neutralization.js +17 -11
- package/dist/capture/backdrop-effect-space.js +18 -19
- package/dist/capture/backdrop-isolation.js +7 -8
- package/dist/capture/background-image-sizing.js +84 -43
- package/dist/capture/broken-image-fallback.js +43 -34
- package/dist/capture/broken-image-icon-raster.js +8 -4
- package/dist/capture/collapsed-border-fragment-cdp.js +94 -75
- package/dist/capture/collapsed-border-fragment-record.js +111 -91
- package/dist/capture/effective-appearance-cdp.js +5 -5
- package/dist/capture/effective-appearance.js +70 -25
- package/dist/capture/embed.d.ts +4 -2
- package/dist/capture/embed.js +27 -14
- package/dist/capture/emoji.js +91 -40
- package/dist/capture/frame-scroll-state.js +114 -80
- package/dist/capture/generic-font-probe.js +23 -13
- package/dist/capture/index.d.ts +6 -4
- package/dist/capture/index.js +220 -115
- package/dist/capture/input-value-geometry.js +4 -4
- package/dist/capture/line-clamp.d.ts +2 -2
- package/dist/capture/line-clamp.js +63 -58
- package/dist/capture/native-control-decoration-raster.js +124 -77
- package/dist/capture/native-control-decoration.d.ts +1 -1
- package/dist/capture/native-control-decoration.js +21 -14
- package/dist/capture/native-control-raster.js +54 -25
- package/dist/capture/native-scrollbar-raster.js +24 -23
- package/dist/capture/projective-owner.d.ts +1 -10
- package/dist/capture/pseudo-backdrop-isolation.js +5 -4
- package/dist/capture/pseudo-fragment-cdp.js +111 -68
- package/dist/capture/pseudo-fragment-protocol.js +45 -24
- package/dist/capture/pseudo-style-cdp.d.ts +1 -1
- package/dist/capture/pseudo-style-cdp.js +59 -46
- package/dist/capture/raf-clock.js +28 -18
- package/dist/capture/replaced-element-raster.js +42 -31
- package/dist/capture/replaced-media-frame.js +51 -23
- package/dist/capture/replaced-snapshot-geometry.js +19 -13
- package/dist/capture/script/color-norm.js +15 -13
- package/dist/capture/script/dotted-circle-detect.js +9 -9
- package/dist/capture/script/emoji-detect.js +18 -14
- package/dist/capture/script/font-family-default.js +70 -39
- package/dist/capture/script/font-feature-values.js +13 -9
- package/dist/capture/script/font-metrics.js +26 -21
- package/dist/capture/script/font-palette.js +17 -9
- package/dist/capture/script/index.js +459 -420
- package/dist/capture/script/line-clamp.d.ts +4 -4
- package/dist/capture/script/line-clamp.js +91 -95
- package/dist/capture/script/placeholder-shown.js +11 -7
- package/dist/capture/script/pseudo-rules.js +16 -18
- package/dist/capture/script/utils.js +24 -25
- package/dist/capture/script/walker/background-attachment.d.ts +1 -1
- package/dist/capture/script/walker/background-attachment.js +59 -48
- package/dist/capture/script/walker/borders-backgrounds.d.ts +1 -1
- package/dist/capture/script/walker/borders-backgrounds.js +204 -120
- package/dist/capture/script/walker/capture-phases.js +58 -53
- package/dist/capture/script/walker/collapsed-border.d.ts +9 -1
- package/dist/capture/script/walker/collapsed-border.js +63 -17
- package/dist/capture/script/walker/counter-prewalk.js +33 -19
- package/dist/capture/script/walker/counter-style-resolver.js +58 -46
- package/dist/capture/script/walker/fieldset-legend.js +3 -3
- package/dist/capture/script/walker/form-controls.d.ts +1 -1
- package/dist/capture/script/walker/form-controls.js +176 -147
- package/dist/capture/script/walker/fragmentation.js +53 -37
- package/dist/capture/script/walker/inline-svg-decisions.d.ts +8 -0
- package/dist/capture/script/walker/inline-svg-decisions.js +37 -0
- package/dist/capture/script/walker/inline-svg.js +212 -187
- package/dist/capture/script/walker/input-value.d.ts +1 -1
- package/dist/capture/script/walker/input-value.js +70 -62
- package/dist/capture/script/walker/lists-counters.d.ts +1 -1
- package/dist/capture/script/walker/lists-counters.js +17 -15
- package/dist/capture/script/walker/masks-clips-decisions.d.ts +15 -0
- package/dist/capture/script/walker/masks-clips-decisions.js +105 -0
- package/dist/capture/script/walker/masks-clips.js +172 -224
- package/dist/capture/script/walker/pseudo-content.d.ts +59 -3
- package/dist/capture/script/walker/pseudo-content.js +493 -470
- package/dist/capture/script/walker/pseudo-inject.js +36 -24
- package/dist/capture/script/walker/replaced-elements.js +18 -13
- package/dist/capture/script/walker/resize-handle.js +21 -21
- package/dist/capture/script/walker/text-segments.d.ts +139 -0
- package/dist/capture/script/walker/text-segments.js +607 -540
- package/dist/capture/script/walker/transforms.js +23 -22
- package/dist/capture/script/warnings.js +7 -7
- package/dist/capture/script.generated.js +1 -1
- package/dist/capture/scrollbar-capture.js +119 -87
- package/dist/capture/summary-marker-cdp.js +32 -16
- package/dist/capture/svg-affine-freeze.js +12 -8
- package/dist/capture/text-fragment-geometry.js +9 -9
- package/dist/capture/text-fragment-spans.js +87 -40
- package/dist/capture/text-line-origin.js +4 -6
- package/dist/capture/text-paint-geometry-cdp.d.ts +29 -2
- package/dist/capture/text-paint-geometry-cdp.js +105 -53
- package/dist/capture/tree-envelope.js +15 -18
- package/dist/capture/types.d.ts +6 -24
- package/dist/capture/warnings.d.ts +8 -7
- package/dist/capture/warnings.js +11 -8
- package/dist/cli/animate-artifact.js +1 -3
- package/dist/cli/animate-capture-session.js +1 -3
- package/dist/cli/animate-captured-frame-stage.d.ts +42 -0
- package/dist/cli/animate-captured-frame-stage.js +29 -0
- package/dist/cli/animate-command.js +23 -0
- package/dist/cli/animate-compression.d.ts +42 -0
- package/dist/cli/animate-compression.js +307 -0
- package/dist/cli/animate-debug.js +8 -8
- package/dist/cli/animate-frame-capture.d.ts +5 -0
- package/dist/cli/animate-frame-capture.js +1 -1
- package/dist/cli/animate-orchestrator.d.ts +81 -121
- package/dist/cli/animate-orchestrator.js +442 -523
- package/dist/cli/animate-states-run-stage.d.ts +7 -0
- package/dist/cli/animate-states-run-stage.js +6 -0
- package/dist/cli/capture.js +53 -8
- package/dist/cli/common.js +16 -10
- package/dist/cli/composite.js +36 -13
- package/dist/cli/hover-detect.js +18 -7
- package/dist/cli/index.js +22 -0
- package/dist/cli/mutation-detect.d.ts +1 -0
- package/dist/cli/mutation-detect.js +31 -10
- package/dist/cli/review.d.ts +1 -1
- package/dist/cli/review.js +20 -8
- package/dist/cli/scrubber.js +6 -2
- package/dist/cli/storyboard.d.ts +51 -51
- package/dist/cli/storyboard.js +8 -2
- package/dist/cli/studio.js +6 -2
- package/dist/cli/svg-to-image.js +1 -1
- package/dist/cli/svg-to-video-core.js +15 -10
- package/dist/cli/template.js +11 -11
- package/dist/cli/term.js +3 -5
- package/dist/cli/type-resample.js +36 -10
- package/dist/font-family-stack.js +8 -8
- package/dist/font-feature-values-cascade.js +1 -2
- package/dist/index.d.ts +5 -5
- package/dist/index.js +4 -4
- package/dist/post-processing/hoist-image-payloads.js +11 -11
- package/dist/post-processing/optimize.js +5 -2
- package/dist/render/advanced-gradient-raster.js +4 -7
- package/dist/render/background-attachment.js +7 -5
- package/dist/render/background-inline-paint.d.ts +97 -0
- package/dist/render/background-inline-paint.js +616 -0
- package/dist/render/border-paint.d.ts +44 -0
- package/dist/render/border-paint.js +894 -0
- package/dist/render/borders.d.ts +4 -4
- package/dist/render/borders.js +230 -86
- package/dist/render/broken-image-fallback.js +1 -2
- package/dist/render/clip-path.js +47 -13
- package/dist/render/cluster-fallback.js +96 -37
- package/dist/render/colors.js +9 -3
- package/dist/render/conic-raster.js +1 -1
- package/dist/render/culling-geometry.js +101 -75
- package/dist/render/custom-scrollbar.js +1 -3
- package/dist/render/decoration-fragment-ownership.js +19 -11
- package/dist/render/device-chrome.js +19 -15
- package/dist/render/element-tree-to-svg.d.ts +116 -14
- package/dist/render/element-tree-to-svg.js +595 -2454
- package/dist/render/embedded-font-builder.d.ts +1 -1
- package/dist/render/embedded-font-builder.js +50 -35
- package/dist/render/embolden-outline.js +10 -12
- package/dist/render/emoji-presentation-priority.js +6 -2
- package/dist/render/font-resolution.d.ts +10 -5
- package/dist/render/font-resolution.js +847 -533
- package/dist/render/form-controls.js +93 -57
- package/dist/render/format.d.ts +1 -1
- package/dist/render/format.js +2 -2
- package/dist/render/generic-script-families.js +446 -125
- package/dist/render/glyph-helper-font.js +70 -28
- package/dist/render/glyph-helper-outline.js +1 -1
- package/dist/render/glyph-helper-transport.js +42 -18
- package/dist/render/glyph-helper.d.ts +3 -3
- package/dist/render/glyph-helper.js +133 -88
- package/dist/render/gradient-defs.js +51 -26
- package/dist/render/gradients.js +47 -18
- package/dist/render/harfbuzz-shaper.d.ts +1 -1
- package/dist/render/harfbuzz-shaper.js +91 -27
- package/dist/render/hb-subset.js +53 -21
- package/dist/render/helper-acquire.js +1 -2
- package/dist/render/helper-availability-contract.js +1 -2
- package/dist/render/host-platform.js +1 -1
- package/dist/render/icu-helper-acquire.js +24 -14
- package/dist/render/icu-helper.js +7 -5
- package/dist/render/image-pattern.js +21 -25
- package/dist/render/index.d.ts +4 -4
- package/dist/render/index.js +3 -3
- package/dist/render/linux-target-strike.js +16 -9
- package/dist/render/list-marker-geometry.js +43 -13
- package/dist/render/mask-origin-clip.js +2 -4
- package/dist/render/mask-position.js +12 -14
- package/dist/render/mask.js +84 -66
- package/dist/render/outline-paint.d.ts +20 -0
- package/dist/render/outline-paint.js +255 -0
- package/dist/render/overflow-clip.js +28 -17
- package/dist/render/paint-order.js +2 -5
- package/dist/render/pseudo-filter.js +1 -4
- package/dist/render/pseudo-fragments.js +71 -37
- package/dist/render/real-text-layer.d.ts +15 -0
- package/dist/render/real-text-layer.js +186 -0
- package/dist/render/render-state-collectors.d.ts +11 -0
- package/dist/render/render-state-collectors.js +87 -0
- package/dist/render/resize-handle.js +9 -16
- package/dist/render/script-segmentation.d.ts +1 -1
- package/dist/render/script-segmentation.js +17 -11
- package/dist/render/stacking.js +66 -36
- package/dist/render/svg-inline.d.ts +17 -0
- package/dist/render/svg-inline.js +178 -5
- package/dist/render/svg-viewport-matrix.d.ts +72 -0
- package/dist/render/svg-viewport-matrix.js +139 -0
- package/dist/render/synchronous-scope.js +3 -3
- package/dist/render/synth-test-fonts.js +20 -4
- package/dist/render/synthesis-decision.js +4 -4
- package/dist/render/text-affine.js +31 -27
- package/dist/render/text-paint.d.ts +42 -0
- package/dist/render/text-paint.js +478 -0
- package/dist/render/text-run-provenance.d.ts +1 -1
- package/dist/render/text-run-provenance.js +42 -15
- package/dist/render/text-to-path.d.ts +25 -3
- package/dist/render/text-to-path.js +437 -258
- package/dist/render/text.js +334 -147
- package/dist/render/unicode-classification.d.ts +2 -2
- package/dist/render/unicode-classification.js +327 -130
- package/dist/render/unicode-font-routing.darwin.generated.js +1 -1
- package/dist/render/vertical-text.d.ts +11 -0
- package/dist/render/vertical-text.js +145 -51
- package/dist/render/win-font-fallback.js +163 -84
- package/dist/review/client.bundle.generated.js +1 -1
- package/dist/review/client.js +3 -1
- package/dist/review/compare-pngs.js +1 -2
- package/dist/review/glyph-compare.js +69 -48
- package/dist/review/linux-unicode-evidence.d.ts +1 -1
- package/dist/review/linux-unicode-evidence.js +180 -27
- package/dist/review/region-overlay.js +10 -2
- package/dist/review/roll-differential.js +48 -12
- package/dist/review/server.d.ts +1 -1
- package/dist/review/server.js +5 -3
- package/dist/review/side-digest.js +6 -1
- package/dist/review/source-drift-gate.js +25 -8
- package/dist/review/stage-evidence.js +10 -9
- package/dist/scroll/composer.d.ts +10 -0
- package/dist/scroll/composer.js +72 -46
- package/dist/scroll/executor.js +30 -17
- package/dist/scroll/hoist-fixed.d.ts +1 -1
- package/dist/scroll/hoist-fixed.js +1 -1
- package/dist/scroll/hoist-sticky.js +1 -3
- package/dist/scroll/pattern.js +18 -7
- package/dist/scrubber/client.bundle.generated.js +1 -1
- package/dist/scrubber/client.js +170 -45
- package/dist/scrubber/crop.js +2 -2
- package/dist/scrubber/embed.js +21 -23
- package/dist/scrubber/server.js +48 -16
- package/dist/scrubber/trim.d.ts +1 -1
- package/dist/scrubber/trim.js +6 -3
- package/dist/studio/agent-tools.d.ts +74 -74
- package/dist/studio/agent-tools.js +77 -23
- package/dist/studio/annotations.js +41 -26
- package/dist/studio/app-projects.js +10 -6
- package/dist/studio/authoring.js +137 -96
- package/dist/studio/client.bundle.generated.js +1 -1
- package/dist/studio/client.js +343 -167
- package/dist/studio/compile.js +5 -3
- package/dist/studio/cursor-choreography.js +22 -8
- package/dist/studio/healing.js +59 -12
- package/dist/studio/index.d.ts +1 -1
- package/dist/studio/index.js +1 -1
- package/dist/studio/interaction-observer.js +157 -33
- package/dist/studio/interactions.js +88 -57
- package/dist/studio/interactive-compile.js +42 -15
- package/dist/studio/project-json-schema.js +1 -1
- package/dist/studio/project-schema.d.ts +110 -110
- package/dist/studio/project-schema.js +102 -31
- package/dist/studio/project.js +4 -2
- package/dist/studio/recording.d.ts +72 -72
- package/dist/studio/recording.js +141 -38
- package/dist/studio/server.js +17 -9
- package/dist/studio/timeline.d.ts +1 -1
- package/dist/studio/timeline.js +68 -40
- package/dist/studio/treatment-schema.d.ts +2 -2
- package/dist/studio/treatment-schema.js +12 -3
- package/dist/studio/treatments.js +56 -17
- package/dist/studio/video-review.d.ts +1 -1
- package/dist/studio/video-review.js +18 -6
- package/dist/templates/builtin/background-loop.d.ts +6 -6
- package/dist/templates/builtin/background-loop.js +70 -35
- package/dist/templates/builtin/caption.js +40 -5
- package/dist/templates/builtin/chart.js +247 -47
- package/dist/templates/builtin/chat.js +56 -12
- package/dist/templates/builtin/compare.d.ts +1 -1
- package/dist/templates/builtin/compare.js +34 -17
- package/dist/templates/builtin/counter.js +11 -7
- package/dist/templates/builtin/cta.js +14 -4
- package/dist/templates/builtin/device-mockup.js +30 -6
- package/dist/templates/builtin/kinetic-text.js +69 -11
- package/dist/templates/builtin/lower-third.js +11 -7
- package/dist/templates/builtin/odometer.js +12 -6
- package/dist/templates/builtin/quote.js +5 -2
- package/dist/templates/builtin/stat.js +26 -11
- package/dist/templates/builtin/subscribe.js +79 -11
- package/dist/templates/builtin/title-card.js +5 -2
- package/dist/templates/formats.js +5 -1
- package/dist/templates/index.d.ts +7 -7
- package/dist/templates/index.js +7 -7
- package/dist/templates/registry.js +22 -13
- package/dist/templates/render.js +4 -1
- package/dist/terminal/emulator.js +9 -2
- package/dist/terminal/incremental.js +24 -6
- package/dist/terminal/index.d.ts +4 -0
- package/dist/terminal/index.js +9 -3
- package/dist/terminal/pty.js +7 -5
- package/dist/terminal/render.js +19 -4
- package/dist/terminal/theme.js +48 -6
- package/dist/tree-ops/decoration-propagation.js +9 -12
- package/dist/tree-ops/resize-embedded-images.js +11 -5
- package/dist/tree-ops/swept-transform-bounds.js +16 -11
- package/dist/tree-ops/tree-diff.js +1 -3
- package/dist/tree-ops/viewbox-culling.js +9 -7
- package/dist/utils/escapeHtml.js +1 -5
- package/dist/utils/local-server.js +7 -2
- package/dist/utils/region-feedback.js +1 -4
- package/dist/utils/transparent-background.js +4 -1
- package/dist/vertical-orientation.js +2 -5
- package/llms.txt +22 -6
- package/package.json +19 -12
- package/schemas/animate-config.schema.json +3 -0
- package/dist/capture/paged-collapsed-table-cdp.d.ts +0 -67
- package/dist/capture/paged-collapsed-table-cdp.js +0 -88
- package/dist/capture/paged-collapsed-table-record.d.ts +0 -170
- 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
|
|
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
|
|
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
|
|
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
|
|
28
|
-
|
|
29
|
-
| `DomotionFixtureMono-Regular.ttf`
|
|
30
|
-
| `DomotionFixtureSerif-Regular.ttf` | [IBM Plex Serif](https://github.com/IBM/plex) Regular
|
|
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 {
|
|
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 {
|
|
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,
|
|
334
|
-
|
|
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 :
|
|
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":
|
|
58
|
+
case "top":
|
|
59
|
+
return base;
|
|
59
60
|
case "right":
|
|
60
|
-
case "bottom":
|
|
61
|
-
|
|
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":
|
|
75
|
-
|
|
76
|
-
case "
|
|
77
|
-
|
|
78
|
-
case "
|
|
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 =
|
|
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,
|
|
144
|
-
|
|
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. */
|