domotion-svg 0.27.0 → 0.27.2
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/assets/fonts/fixture/DomotionProfileDevanagariOne-Regular.ttf +0 -0
- package/assets/fonts/fixture/DomotionProfileDevanagariTwo-Regular.ttf +0 -0
- package/assets/fonts/fixture/README.md +16 -2
- package/dist/animation/embed-namespace.d.ts +2 -0
- package/dist/animation/embed-namespace.js +7 -3
- package/dist/animation/embed-timeline.d.ts +2 -0
- package/dist/animation/embed-timeline.js +9 -3
- package/dist/animation/svg-generator.d.ts +2 -0
- package/dist/animation/svg-generator.js +1 -0
- package/dist/cli/animate-orchestrator.js +15 -1
- package/dist/cli/common.js +1 -1
- package/dist/cli/composite.d.ts +131 -18
- package/dist/cli/composite.js +18 -16
- package/dist/cli/index.d.ts +1 -0
- package/dist/cli/index.js +8 -0
- package/dist/cli/storyboard.d.ts +891 -19
- package/dist/cli/storyboard.js +36 -21
- package/dist/cli/studio.d.ts +2 -0
- package/dist/cli/studio.js +68 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +6 -0
- package/dist/scroll/composer.js +12 -5
- package/dist/scrubber/client.bundle.generated.js +1 -1
- package/dist/scrubber/client.d.ts +1 -0
- package/dist/scrubber/client.js +124 -19
- package/dist/scrubber/embed.d.ts +47 -0
- package/dist/scrubber/embed.js +71 -0
- package/dist/studio/agent-tools.d.ts +2833 -0
- package/dist/studio/agent-tools.js +495 -0
- package/dist/studio/annotations.d.ts +300 -0
- package/dist/studio/annotations.js +370 -0
- package/dist/studio/app-projects.d.ts +31 -0
- package/dist/studio/app-projects.js +137 -0
- package/dist/studio/authoring.d.ts +62 -0
- package/dist/studio/authoring.js +235 -0
- package/dist/studio/client.bundle.generated.d.ts +1 -0
- package/dist/studio/client.bundle.generated.js +3 -0
- package/dist/studio/client.d.ts +23 -0
- package/dist/studio/client.js +921 -0
- package/dist/studio/compile.d.ts +24 -0
- package/dist/studio/compile.js +165 -0
- package/dist/studio/cursor-choreography.d.ts +62 -0
- package/dist/studio/cursor-choreography.js +209 -0
- package/dist/studio/healing.d.ts +113 -0
- package/dist/studio/healing.js +279 -0
- package/dist/studio/index.d.ts +17 -0
- package/dist/studio/index.js +17 -0
- package/dist/studio/interaction-observer.d.ts +132 -0
- package/dist/studio/interaction-observer.js +417 -0
- package/dist/studio/interactions.d.ts +43 -0
- package/dist/studio/interactions.js +255 -0
- package/dist/studio/interactive-compile.d.ts +89 -0
- package/dist/studio/interactive-compile.js +399 -0
- package/dist/studio/project-json-schema.d.ts +3 -0
- package/dist/studio/project-json-schema.js +22 -0
- package/dist/studio/project-schema.d.ts +4225 -0
- package/dist/studio/project-schema.js +513 -0
- package/dist/studio/project.d.ts +32 -0
- package/dist/studio/project.js +183 -0
- package/dist/studio/recording.d.ts +3121 -0
- package/dist/studio/recording.js +579 -0
- package/dist/studio/server.d.ts +57 -0
- package/dist/studio/server.js +416 -0
- package/dist/studio/timeline.d.ts +67 -0
- package/dist/studio/timeline.js +413 -0
- package/dist/studio/treatment-schema.d.ts +619 -0
- package/dist/studio/treatment-schema.js +89 -0
- package/dist/studio/treatments.d.ts +31 -0
- package/dist/studio/treatments.js +222 -0
- package/dist/studio/video-review.d.ts +345 -0
- package/dist/studio/video-review.js +307 -0
- package/dist/utils/region-feedback.d.ts +2 -14
- package/dist/utils/region-feedback.js +1 -47
- package/dist/utils/regions-parser.d.ts +15 -0
- package/dist/utils/regions-parser.js +47 -0
- package/package.json +10 -4
- package/schemas/domotion-studio-project.schema.json +3736 -0
- package/schemas/storyboard-config.schema.json +8 -0
|
Binary file
|
|
Binary file
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
# Deterministic fixture faces
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
Deterministic fonts used by cross-platform and platform-native test fixtures.
|
|
4
|
+
|
|
5
|
+
`DomotionFixtureMono-Regular.ttf` and `DomotionFixtureSerif-Regular.ttf` are
|
|
6
|
+
the subsetted faces the frame-sequence compressor's e2e fixtures render with,
|
|
7
|
+
so those fixtures paint the same glyph outlines on macOS, Linux, and Windows.
|
|
5
8
|
|
|
6
9
|
They exist because the compressor e2e tests assert pixel parity between a
|
|
7
10
|
compressed run and the uncompressed flipbook of the same state, bounded by the
|
|
@@ -31,9 +34,20 @@ paint. The OFL permits redistribution of modified copies under a reserved-name
|
|
|
31
34
|
rule, which is why the outputs carry neutral `DomotionFixture*` names rather
|
|
32
35
|
than the upstream family names.
|
|
33
36
|
|
|
37
|
+
`DomotionProfileDevanagariOne-Regular.ttf` and
|
|
38
|
+
`DomotionProfileDevanagariTwo-Regular.ttf` are original, generated test fonts.
|
|
39
|
+
Each contains only `.notdef` and U+0905. The Windows profile/target authority
|
|
40
|
+
workflow installs them into the runner's real per-user font collection, then
|
|
41
|
+
authenticates their painted family identities through
|
|
42
|
+
`CSS.getPlatformFontsForNode`. They replace the Windows supplemental
|
|
43
|
+
language-capability installation without weakening the non-inert mutation gate.
|
|
44
|
+
|
|
34
45
|
## Regenerating
|
|
35
46
|
|
|
36
47
|
`tools/build-fixture-fonts.mjs` rebuilds both from the upstream originals (it
|
|
37
48
|
prints the download URLs). The subset is deterministic — same inputs and the
|
|
38
49
|
same harfbuzzjs version produce byte-identical output, so a re-run must leave
|
|
39
50
|
`git status` clean unless an input or the charset actually changed.
|
|
51
|
+
|
|
52
|
+
Run `node tools/build-generic-profile-fixture-fonts.mjs` to rebuild the two
|
|
53
|
+
original profile-authority faces.
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* - **embedded-font families** — `font-family:"dmf0"` in `@font-face` + the
|
|
13
13
|
* `<text font-family="dmf0">` attrs. A duplicate family name makes a later
|
|
14
14
|
* `@font-face` win, so text in OTHER frames reshapes to the wrong glyphs.
|
|
15
|
+
* Already-composed input can carry a prior namespace such as `sb0_dmf0`;
|
|
16
|
+
* nesting it again must namespace that whole family as one identifier.
|
|
15
17
|
* - **frame / animation classes** — `class="f f-0"`, `class="anim-…"` and their
|
|
16
18
|
* `.f` / `.f-0` / `.anim-…` style selectors.
|
|
17
19
|
* - **`@keyframes` names** — `fv-0`, the intra-frame `f0-…` names — and the
|
|
@@ -12,6 +12,8 @@
|
|
|
12
12
|
* - **embedded-font families** — `font-family:"dmf0"` in `@font-face` + the
|
|
13
13
|
* `<text font-family="dmf0">` attrs. A duplicate family name makes a later
|
|
14
14
|
* `@font-face` win, so text in OTHER frames reshapes to the wrong glyphs.
|
|
15
|
+
* Already-composed input can carry a prior namespace such as `sb0_dmf0`;
|
|
16
|
+
* nesting it again must namespace that whole family as one identifier.
|
|
15
17
|
* - **frame / animation classes** — `class="f f-0"`, `class="anim-…"` and their
|
|
16
18
|
* `.f` / `.f-0` / `.anim-…` style selectors.
|
|
17
19
|
* - **`@keyframes` names** — `fv-0`, the intra-frame `f0-…` names — and the
|
|
@@ -49,14 +51,16 @@ export function namespaceEmbeddedAnimatedSvg(svg, token, opts = {}) {
|
|
|
49
51
|
.replace(new RegExp(`url\\(#${e}\\)`, "g"), `url(#${token}${id})`)
|
|
50
52
|
.replace(new RegExp(`((?:xlink:)?href=")#${e}(")`, "g"), `$1#${token}${id}$2`);
|
|
51
53
|
}
|
|
52
|
-
// 2. Embedded-font families (`dmfN
|
|
54
|
+
// 2. Embedded-font families (`dmfN`, including already-prefixed families from a
|
|
55
|
+
// previously composed SVG). Only the `@font-face` declaration and the
|
|
53
56
|
// `font-family` attr/decl contexts — never a bare global replace, since the
|
|
54
57
|
// base64 `src` payload could contain the same byte sequence. Skipped when the
|
|
55
58
|
// fonts are deferred to a shared outer builder (see NamespaceEmbedOptions).
|
|
56
59
|
if (opts.namespaceFonts !== false) {
|
|
60
|
+
const embeddedFamily = "(?:[A-Za-z][A-Za-z0-9_-]*_)?dmf\\d+";
|
|
57
61
|
out = out
|
|
58
|
-
.replace(
|
|
59
|
-
.replace(
|
|
62
|
+
.replace(new RegExp(`(font-family:\\s*(?:"|"))(${embeddedFamily})((?:"|"))`, "g"), `$1${token}$2$3`)
|
|
63
|
+
.replace(new RegExp(`(font-family=")(${embeddedFamily})(")`, "g"), `$1${token}$2$3`);
|
|
60
64
|
}
|
|
61
65
|
// 3. `@keyframes` names + their `animation:` references. A keyframe name is a
|
|
62
66
|
// domotion-generated token (`fv-0`, `f0-f0a0-0`, …) that never appears as an
|
|
@@ -60,6 +60,8 @@ export interface OffsetTimelineOptions {
|
|
|
60
60
|
startMs: number;
|
|
61
61
|
/** The master loop's total period (ms). */
|
|
62
62
|
masterMs: number;
|
|
63
|
+
/** Begin at this source-local time while preserving browser interpolation. */
|
|
64
|
+
contentOffsetMs?: number;
|
|
63
65
|
/**
|
|
64
66
|
* Timeline mode (default `"hold"`). See {@link EmbeddedTimelineMode}.
|
|
65
67
|
*/
|
|
@@ -154,6 +154,7 @@ export function offsetEmbeddedAnimatedSvgTimeline(svg, opts) {
|
|
|
154
154
|
const windowMs = mode === "stretch" ? (opts.windowMs ?? periodMs) : periodMs;
|
|
155
155
|
const scale = windowMs / masterMs;
|
|
156
156
|
const offsetPct = (startMs / masterMs) * 100;
|
|
157
|
+
const contentOffsetMs = Math.max(0, opts.contentOffsetMs ?? 0);
|
|
157
158
|
// Duration match tolerance — generous enough to absorb the animator's 2- vs
|
|
158
159
|
// 3-decimal formatting, tight enough not to catch a distinct fixed period.
|
|
159
160
|
const tol = Math.max(0.02, periodSec * 0.01);
|
|
@@ -175,7 +176,7 @@ export function offsetEmbeddedAnimatedSvgTimeline(svg, opts) {
|
|
|
175
176
|
.replace(/style="([^"]*animation:[^"]*)"/g, (_full, css) => `style="${delayCss(css)}"`);
|
|
176
177
|
}
|
|
177
178
|
// Nothing to do: content already starts at the origin and fills the loop.
|
|
178
|
-
if (offsetPct <= 1e-6 && Math.abs(scale - 1) <= 1e-6)
|
|
179
|
+
if (contentOffsetMs <= 0 && offsetPct <= 1e-6 && Math.abs(scale - 1) <= 1e-6)
|
|
179
180
|
return svg;
|
|
180
181
|
// Pass 1: retime matching animations + collect the keyframe names they drive.
|
|
181
182
|
// Untouched declarations (e.g. a fixed-period cursor blink) are returned
|
|
@@ -203,13 +204,18 @@ export function offsetEmbeddedAnimatedSvgTimeline(svg, opts) {
|
|
|
203
204
|
}
|
|
204
205
|
return tokens.join(" ");
|
|
205
206
|
});
|
|
206
|
-
|
|
207
|
+
if (!changed)
|
|
208
|
+
return full;
|
|
209
|
+
const shiftedDelay = contentOffsetMs > 0
|
|
210
|
+
? `;animation-delay:${fmt((startMs - contentOffsetMs) / 1000)}s;animation-fill-mode:both`
|
|
211
|
+
: "";
|
|
212
|
+
return `animation:${entries.join(",")}${shiftedDelay}`;
|
|
207
213
|
});
|
|
208
214
|
let out = svg
|
|
209
215
|
.replace(/<style>([\s\S]*?)<\/style>/g, (_full, css) => `<style>${retimeCss(css)}</style>`)
|
|
210
216
|
.replace(/style="([^"]*animation:[^"]*)"/g, (_full, css) => `style="${retimeCss(css)}"`);
|
|
211
217
|
// Pass 2: remap the keyframes those animations drive.
|
|
212
|
-
out = out.replace(/@keyframes\s+([\w-]+)\s*\{((?:[^{}]*\{[^{}]*\})*[^{}]*)\}/g, (full, name, inner) => retimed.has(name) ? `@keyframes ${name} { ${remapKeyframeBody(inner, offsetPct, scale)} }` : full);
|
|
218
|
+
out = out.replace(/@keyframes\s+([\w-]+)\s*\{((?:[^{}]*\{[^{}]*\})*[^{}]*)\}/g, (full, name, inner) => retimed.has(name) ? `@keyframes ${name} { ${remapKeyframeBody(inner, contentOffsetMs > 0 ? 0 : offsetPct, scale)} }` : full);
|
|
213
219
|
return out;
|
|
214
220
|
}
|
|
215
221
|
/** Animation-shorthand keywords that are never the `animation-name`. */
|
|
@@ -36,6 +36,8 @@ export interface AnimationFrame {
|
|
|
36
36
|
* `offsetEmbeddedAnimatedSvgTimeline`). Omit for ordinary captured frames.
|
|
37
37
|
*/
|
|
38
38
|
embeddedAnimationPeriodMs?: number;
|
|
39
|
+
/** Start this embedded animation at a source-local offset (used by storyboard trim). */
|
|
40
|
+
embeddedAnimationOffsetMs?: number;
|
|
39
41
|
/** Transition to the next frame. The type is derived from the canonical zod schema. */
|
|
40
42
|
transition?: Transition;
|
|
41
43
|
/**
|
|
@@ -1461,7 +1461,21 @@ async function buildCapturedFrame(fc, i, ctx) {
|
|
|
1461
1461
|
cullElementsOutsideViewBox(seg.tree, scrollClip[2], scrollClip[3], undefined, 0, 1);
|
|
1462
1462
|
}
|
|
1463
1463
|
rootBg = segments[0]?.tree?.[0]?.styles?.rootBgComputed;
|
|
1464
|
-
|
|
1464
|
+
// The scroll composer owns a complete nested document and starts its own
|
|
1465
|
+
// generation, so isolate that reset from the outer animation's accumulated
|
|
1466
|
+
// glyph/font registries. Otherwise a scroll frame after an ordinary capture
|
|
1467
|
+
// rewinds gN/dmfN and replaces the earlier frame's addressing. Namespace the
|
|
1468
|
+
// finished document too: Blink resolves local SVG IRIs through the shared
|
|
1469
|
+
// TreeScope, so a nested <svg> is not an id boundary.
|
|
1470
|
+
const outerGeneration = snapshotGeneration();
|
|
1471
|
+
let composed;
|
|
1472
|
+
try {
|
|
1473
|
+
composed = composeScrollSvg(segments, { viewportW: scrollClip[2], viewportH: scrollClip[3] });
|
|
1474
|
+
}
|
|
1475
|
+
finally {
|
|
1476
|
+
restoreGeneration(outerGeneration);
|
|
1477
|
+
}
|
|
1478
|
+
composed = namespaceEmbeddedAnimatedSvg(composed, `sf${i}_`);
|
|
1465
1479
|
// The composer emits a full `<?xml ...><svg>...</svg>` document. The
|
|
1466
1480
|
// outer animator wraps `svgContent` in a `<g class="f f-N">`, which
|
|
1467
1481
|
// happily contains a nested `<svg>` element — strip just the XML
|
package/dist/cli/common.js
CHANGED
|
@@ -11,7 +11,7 @@ import { existsSync, readFileSync, writeFileSync } from "node:fs";
|
|
|
11
11
|
import { basename, dirname, resolve } from "node:path";
|
|
12
12
|
import { pathToFileURL } from "node:url";
|
|
13
13
|
import { promisify } from "node:util";
|
|
14
|
-
import { gzipSvg } from "../index.js";
|
|
14
|
+
import { gzipSvg } from "../post-processing/index.js";
|
|
15
15
|
const execFileP = promisify(execFile);
|
|
16
16
|
/** Parse a positive-integer flag, falling back to `def` when the flag is absent.
|
|
17
17
|
* Thin default-applying wrapper over `parsePositiveInt` so the two share one
|
package/dist/cli/composite.d.ts
CHANGED
|
@@ -19,6 +19,117 @@
|
|
|
19
19
|
*/
|
|
20
20
|
import type { Browser } from "@playwright/test";
|
|
21
21
|
import { z } from "zod";
|
|
22
|
+
export declare const compositeLayerAnimationSchema: z.ZodObject<{
|
|
23
|
+
property: z.ZodEnum<{
|
|
24
|
+
opacity: "opacity";
|
|
25
|
+
transform: "transform";
|
|
26
|
+
scale: "scale";
|
|
27
|
+
translateX: "translateX";
|
|
28
|
+
translateY: "translateY";
|
|
29
|
+
clipScaleX: "clipScaleX";
|
|
30
|
+
clipScaleY: "clipScaleY";
|
|
31
|
+
}>;
|
|
32
|
+
from: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
33
|
+
to: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
34
|
+
start: z.ZodOptional<z.ZodNumber>;
|
|
35
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
36
|
+
easing: z.ZodOptional<z.ZodString>;
|
|
37
|
+
transformOrigin: z.ZodOptional<z.ZodString>;
|
|
38
|
+
}, z.core.$strip>;
|
|
39
|
+
export declare const compositeLayerChromeSchema: z.ZodObject<{
|
|
40
|
+
device: z.ZodDefault<z.ZodEnum<{
|
|
41
|
+
browser: "browser";
|
|
42
|
+
phone: "phone";
|
|
43
|
+
window: "window";
|
|
44
|
+
}>>;
|
|
45
|
+
label: z.ZodOptional<z.ZodString>;
|
|
46
|
+
theme: z.ZodDefault<z.ZodEnum<{
|
|
47
|
+
light: "light";
|
|
48
|
+
dark: "dark";
|
|
49
|
+
}>>;
|
|
50
|
+
}, z.core.$strip>;
|
|
51
|
+
export declare const compositeLayerPlacementSchema: z.ZodObject<{
|
|
52
|
+
x: z.ZodOptional<z.ZodNumber>;
|
|
53
|
+
y: z.ZodOptional<z.ZodNumber>;
|
|
54
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
55
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
56
|
+
clip: z.ZodOptional<z.ZodBoolean>;
|
|
57
|
+
clipRadius: z.ZodOptional<z.ZodNumber>;
|
|
58
|
+
start: z.ZodOptional<z.ZodNumber>;
|
|
59
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
60
|
+
stretch: "stretch";
|
|
61
|
+
hold: "hold";
|
|
62
|
+
loop: "loop";
|
|
63
|
+
}>>;
|
|
64
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
65
|
+
animations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
66
|
+
property: z.ZodEnum<{
|
|
67
|
+
opacity: "opacity";
|
|
68
|
+
transform: "transform";
|
|
69
|
+
scale: "scale";
|
|
70
|
+
translateX: "translateX";
|
|
71
|
+
translateY: "translateY";
|
|
72
|
+
clipScaleX: "clipScaleX";
|
|
73
|
+
clipScaleY: "clipScaleY";
|
|
74
|
+
}>;
|
|
75
|
+
from: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
76
|
+
to: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
77
|
+
start: z.ZodOptional<z.ZodNumber>;
|
|
78
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
79
|
+
easing: z.ZodOptional<z.ZodString>;
|
|
80
|
+
transformOrigin: z.ZodOptional<z.ZodString>;
|
|
81
|
+
}, z.core.$strip>>>;
|
|
82
|
+
}, z.core.$strip>;
|
|
83
|
+
export declare const compositeLayerSchema: z.ZodObject<{
|
|
84
|
+
x: z.ZodOptional<z.ZodNumber>;
|
|
85
|
+
y: z.ZodOptional<z.ZodNumber>;
|
|
86
|
+
width: z.ZodOptional<z.ZodNumber>;
|
|
87
|
+
height: z.ZodOptional<z.ZodNumber>;
|
|
88
|
+
clip: z.ZodOptional<z.ZodBoolean>;
|
|
89
|
+
clipRadius: z.ZodOptional<z.ZodNumber>;
|
|
90
|
+
start: z.ZodOptional<z.ZodNumber>;
|
|
91
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
92
|
+
stretch: "stretch";
|
|
93
|
+
hold: "hold";
|
|
94
|
+
loop: "loop";
|
|
95
|
+
}>>;
|
|
96
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
97
|
+
animations: z.ZodOptional<z.ZodArray<z.ZodObject<{
|
|
98
|
+
property: z.ZodEnum<{
|
|
99
|
+
opacity: "opacity";
|
|
100
|
+
transform: "transform";
|
|
101
|
+
scale: "scale";
|
|
102
|
+
translateX: "translateX";
|
|
103
|
+
translateY: "translateY";
|
|
104
|
+
clipScaleX: "clipScaleX";
|
|
105
|
+
clipScaleY: "clipScaleY";
|
|
106
|
+
}>;
|
|
107
|
+
from: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
108
|
+
to: z.ZodUnion<readonly [z.ZodString, z.ZodNumber]>;
|
|
109
|
+
start: z.ZodOptional<z.ZodNumber>;
|
|
110
|
+
duration: z.ZodOptional<z.ZodNumber>;
|
|
111
|
+
easing: z.ZodOptional<z.ZodString>;
|
|
112
|
+
transformOrigin: z.ZodOptional<z.ZodString>;
|
|
113
|
+
}, z.core.$strip>>>;
|
|
114
|
+
svg: z.ZodOptional<z.ZodString>;
|
|
115
|
+
cast: z.ZodOptional<z.ZodString>;
|
|
116
|
+
template: z.ZodOptional<z.ZodString>;
|
|
117
|
+
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
118
|
+
term: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
119
|
+
period: z.ZodOptional<z.ZodNumber>;
|
|
120
|
+
chrome: z.ZodOptional<z.ZodObject<{
|
|
121
|
+
device: z.ZodDefault<z.ZodEnum<{
|
|
122
|
+
browser: "browser";
|
|
123
|
+
phone: "phone";
|
|
124
|
+
window: "window";
|
|
125
|
+
}>>;
|
|
126
|
+
label: z.ZodOptional<z.ZodString>;
|
|
127
|
+
theme: z.ZodDefault<z.ZodEnum<{
|
|
128
|
+
light: "light";
|
|
129
|
+
dark: "dark";
|
|
130
|
+
}>>;
|
|
131
|
+
}, z.core.$strip>>;
|
|
132
|
+
}, z.core.$strip>;
|
|
22
133
|
export declare const compositeConfigSchema: z.ZodObject<{
|
|
23
134
|
width: z.ZodNumber;
|
|
24
135
|
height: z.ZodNumber;
|
|
@@ -26,24 +137,6 @@ export declare const compositeConfigSchema: z.ZodObject<{
|
|
|
26
137
|
background: z.ZodOptional<z.ZodString>;
|
|
27
138
|
duration: z.ZodOptional<z.ZodNumber>;
|
|
28
139
|
layers: z.ZodArray<z.ZodObject<{
|
|
29
|
-
svg: z.ZodOptional<z.ZodString>;
|
|
30
|
-
cast: z.ZodOptional<z.ZodString>;
|
|
31
|
-
template: z.ZodOptional<z.ZodString>;
|
|
32
|
-
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
33
|
-
term: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
34
|
-
period: z.ZodOptional<z.ZodNumber>;
|
|
35
|
-
chrome: z.ZodOptional<z.ZodObject<{
|
|
36
|
-
device: z.ZodDefault<z.ZodEnum<{
|
|
37
|
-
browser: "browser";
|
|
38
|
-
phone: "phone";
|
|
39
|
-
window: "window";
|
|
40
|
-
}>>;
|
|
41
|
-
label: z.ZodOptional<z.ZodString>;
|
|
42
|
-
theme: z.ZodDefault<z.ZodEnum<{
|
|
43
|
-
light: "light";
|
|
44
|
-
dark: "dark";
|
|
45
|
-
}>>;
|
|
46
|
-
}, z.core.$strip>>;
|
|
47
140
|
x: z.ZodOptional<z.ZodNumber>;
|
|
48
141
|
y: z.ZodOptional<z.ZodNumber>;
|
|
49
142
|
width: z.ZodOptional<z.ZodNumber>;
|
|
@@ -74,9 +167,29 @@ export declare const compositeConfigSchema: z.ZodObject<{
|
|
|
74
167
|
easing: z.ZodOptional<z.ZodString>;
|
|
75
168
|
transformOrigin: z.ZodOptional<z.ZodString>;
|
|
76
169
|
}, z.core.$strip>>>;
|
|
170
|
+
svg: z.ZodOptional<z.ZodString>;
|
|
171
|
+
cast: z.ZodOptional<z.ZodString>;
|
|
172
|
+
template: z.ZodOptional<z.ZodString>;
|
|
173
|
+
params: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
174
|
+
term: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
175
|
+
period: z.ZodOptional<z.ZodNumber>;
|
|
176
|
+
chrome: z.ZodOptional<z.ZodObject<{
|
|
177
|
+
device: z.ZodDefault<z.ZodEnum<{
|
|
178
|
+
browser: "browser";
|
|
179
|
+
phone: "phone";
|
|
180
|
+
window: "window";
|
|
181
|
+
}>>;
|
|
182
|
+
label: z.ZodOptional<z.ZodString>;
|
|
183
|
+
theme: z.ZodDefault<z.ZodEnum<{
|
|
184
|
+
light: "light";
|
|
185
|
+
dark: "dark";
|
|
186
|
+
}>>;
|
|
187
|
+
}, z.core.$strip>>;
|
|
77
188
|
}, z.core.$strip>>;
|
|
78
189
|
}, z.core.$strip>;
|
|
79
190
|
export type CompositeConfig = z.infer<typeof compositeConfigSchema>;
|
|
191
|
+
export type CompositeLayerConfig = z.infer<typeof compositeLayerSchema>;
|
|
192
|
+
export type CompositeLayerPlacement = z.infer<typeof compositeLayerPlacementSchema>;
|
|
80
193
|
export declare function validateCompositeConfig(raw: unknown): CompositeConfig;
|
|
81
194
|
/** Render + composite a validated config into one animated SVG. */
|
|
82
195
|
export declare function composeCompositeConfig(browser: Browser, cfg: CompositeConfig, configDir: string, log?: (m: string) => void): Promise<string>;
|
package/dist/cli/composite.js
CHANGED
|
@@ -27,7 +27,7 @@ import { DEVICE_CHROMES, CHROME_THEMES, wrapInDeviceChrome, clearEmbeddedFonts,
|
|
|
27
27
|
import { composeAnimatedLayers } from "../animation/composite.js";
|
|
28
28
|
import { parseSvgIntrinsicSize, detectAnimationPeriodMs } from "../animation/svg-meta.js";
|
|
29
29
|
import { cliFail } from "./common.js";
|
|
30
|
-
const
|
|
30
|
+
export const compositeLayerAnimationSchema = z.object({
|
|
31
31
|
property: z.enum(["scale", "translateX", "translateY", "opacity", "transform", "clipScaleX", "clipScaleY"]),
|
|
32
32
|
from: z.union([z.string(), z.number()]),
|
|
33
33
|
to: z.union([z.string(), z.number()]),
|
|
@@ -36,33 +36,35 @@ const layerAnimationSchema = z.object({
|
|
|
36
36
|
easing: z.string().optional(),
|
|
37
37
|
transformOrigin: z.string().optional(),
|
|
38
38
|
});
|
|
39
|
-
const
|
|
39
|
+
export const compositeLayerChromeSchema = z.object({
|
|
40
40
|
device: z.enum(DEVICE_CHROMES).default("window"),
|
|
41
41
|
label: z.string().optional(),
|
|
42
42
|
theme: z.enum(CHROME_THEMES).default("dark"),
|
|
43
43
|
});
|
|
44
|
-
const
|
|
45
|
-
// Exactly one source — enforced in the superRefine below.
|
|
46
|
-
svg: z.string().optional().describe("Path to a pre-rendered SVG (static or animated)."),
|
|
47
|
-
cast: z.string().optional().describe("Path to an asciinema v2 .cast (rendered as an animated terminal)."),
|
|
48
|
-
template: z.string().optional().describe("A template name to render as the source."),
|
|
49
|
-
params: z.record(z.string(), z.unknown()).optional().describe("Params for a `template` source."),
|
|
50
|
-
term: z.record(z.string(), z.unknown()).optional().describe("Terminal options for a `cast` source."),
|
|
51
|
-
// Period of an animated `svg` source (ms), when it can't be auto-detected.
|
|
52
|
-
period: z.number().positive().optional(),
|
|
53
|
-
chrome: chromeSchema.optional(),
|
|
54
|
-
// Placement.
|
|
44
|
+
export const compositeLayerPlacementSchema = z.object({
|
|
55
45
|
x: z.number().optional(),
|
|
56
46
|
y: z.number().optional(),
|
|
57
47
|
width: z.number().positive().optional(),
|
|
58
48
|
height: z.number().positive().optional(),
|
|
59
49
|
clip: z.boolean().optional(),
|
|
60
50
|
clipRadius: z.number().nonnegative().optional(),
|
|
61
|
-
// Timeline of the layer's own animation.
|
|
62
51
|
start: z.number().nonnegative().optional(),
|
|
63
52
|
mode: z.enum(["hold", "stretch", "loop"]).optional(),
|
|
64
53
|
duration: z.number().positive().optional(),
|
|
65
|
-
animations: z.array(
|
|
54
|
+
animations: z.array(compositeLayerAnimationSchema).optional(),
|
|
55
|
+
});
|
|
56
|
+
export const compositeLayerSchema = z.object({
|
|
57
|
+
// Exactly one source — enforced in the superRefine below.
|
|
58
|
+
svg: z.string().optional().describe("Path to a pre-rendered SVG (static or animated)."),
|
|
59
|
+
cast: z.string().optional().describe("Path to an asciinema v2 .cast (rendered as an animated terminal)."),
|
|
60
|
+
template: z.string().optional().describe("A template name to render as the source."),
|
|
61
|
+
params: z.record(z.string(), z.unknown()).optional().describe("Params for a `template` source."),
|
|
62
|
+
term: z.record(z.string(), z.unknown()).optional().describe("Terminal options for a `cast` source."),
|
|
63
|
+
// Period of an animated `svg` source (ms), when it can't be auto-detected.
|
|
64
|
+
period: z.number().positive().optional(),
|
|
65
|
+
chrome: compositeLayerChromeSchema.optional(),
|
|
66
|
+
// Placement.
|
|
67
|
+
...compositeLayerPlacementSchema.shape,
|
|
66
68
|
}).superRefine((l, ctx) => {
|
|
67
69
|
const sources = [l.svg, l.cast, l.template].filter((s) => s != null);
|
|
68
70
|
if (sources.length !== 1) {
|
|
@@ -75,7 +77,7 @@ export const compositeConfigSchema = z.object({
|
|
|
75
77
|
output: z.string().optional(),
|
|
76
78
|
background: z.string().optional(),
|
|
77
79
|
duration: z.number().positive().optional(),
|
|
78
|
-
layers: z.array(
|
|
80
|
+
layers: z.array(compositeLayerSchema).min(1),
|
|
79
81
|
});
|
|
80
82
|
export function validateCompositeConfig(raw) {
|
|
81
83
|
const parsed = compositeConfigSchema.safeParse(raw);
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* domotion capture <input> [options] single-frame capture
|
|
7
7
|
* domotion animate <config.json> multi-frame animated capture
|
|
8
8
|
* domotion term --cast <file> terminal session → animated SVG (DM-1225)
|
|
9
|
+
* domotion studio [project.json] standalone Studio authoring app
|
|
9
10
|
*
|
|
10
11
|
* `<input>` for `capture` may be:
|
|
11
12
|
* - a URL (`https://...`, `http://...`)
|
package/dist/cli/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* domotion capture <input> [options] single-frame capture
|
|
7
7
|
* domotion animate <config.json> multi-frame animated capture
|
|
8
8
|
* domotion term --cast <file> terminal session → animated SVG (DM-1225)
|
|
9
|
+
* domotion studio [project.json] standalone Studio authoring app
|
|
9
10
|
*
|
|
10
11
|
* `<input>` for `capture` may be:
|
|
11
12
|
* - a URL (`https://...`, `http://...`)
|
|
@@ -22,6 +23,7 @@ import { runTerm } from "./term.js";
|
|
|
22
23
|
import { runTemplate } from "./template.js";
|
|
23
24
|
import { runComposite } from "./composite.js";
|
|
24
25
|
import { runStoryboard } from "./storyboard.js";
|
|
26
|
+
import { runStudio } from "./studio.js";
|
|
25
27
|
// Read the version from package.json at runtime rather than hardcoding it, so
|
|
26
28
|
// `domotion --version` always matches the installed package (the literal had
|
|
27
29
|
// drifted to 0.1.0 while the package was at 0.5.0). Resolved relative to this
|
|
@@ -38,6 +40,7 @@ Usage:
|
|
|
38
40
|
domotion template <name> [--param …] -o out.svg
|
|
39
41
|
domotion composite <config.json> [-o out.svg]
|
|
40
42
|
domotion storyboard <config.json> [-o out.svg]
|
|
43
|
+
domotion studio [project.json] [--workspace <dir>]
|
|
41
44
|
domotion --help | --version
|
|
42
45
|
|
|
43
46
|
Commands:
|
|
@@ -56,6 +59,8 @@ Commands:
|
|
|
56
59
|
storyboard Sequence distinct SCENES (template / capture / cast / svg) end-to-end
|
|
57
60
|
into one animated SVG with an inter-scene transition between each
|
|
58
61
|
(title card → demo → CTA). Run 'domotion storyboard --help'.
|
|
62
|
+
studio Open the standalone Domotion Studio local app to create, inspect,
|
|
63
|
+
edit, validate, save, and reopen versioned Studio project files.
|
|
59
64
|
|
|
60
65
|
capture options:
|
|
61
66
|
-o, --output <path> Output SVG path (default: stdout, or <input>.svg
|
|
@@ -315,6 +320,9 @@ async function main() {
|
|
|
315
320
|
else if (cmd === "storyboard") {
|
|
316
321
|
await runStoryboard(rest);
|
|
317
322
|
}
|
|
323
|
+
else if (cmd === "studio") {
|
|
324
|
+
await runStudio(rest);
|
|
325
|
+
}
|
|
318
326
|
else {
|
|
319
327
|
process.stderr.write(`domotion: unknown command "${cmd}"\n\n`);
|
|
320
328
|
process.stderr.write(HELP);
|