@pixodesk/svg-animator-core 1.0.44 → 1.0.45
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/dist/{PxDocumentDiagnostic-n8yiyL0k.d.cts → PxDocumentDiagnostic-CBhVyNEi.d.cts} +22 -22
- package/dist/{PxDocumentDiagnostic-n8yiyL0k.d.ts → PxDocumentDiagnostic-CBhVyNEi.d.ts} +22 -22
- package/dist/chunk-5HEVTP2N.min.js +1 -0
- package/dist/{chunk-EFQLDGFY.js → chunk-YVKMPBHE.js} +1969 -64
- package/dist/chunk-YVKMPBHE.js.map +1 -0
- package/dist/index.cjs +116 -35
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +5 -1873
- package/dist/index.js.map +1 -1
- package/dist/index.min.cjs +1 -1
- package/dist/index.min.js +1 -1
- package/dist/internal.cjs +3209 -139
- package/dist/internal.cjs.map +1 -1
- package/dist/internal.d.cts +60 -3
- package/dist/internal.d.ts +60 -3
- package/dist/internal.js +53 -1
- package/dist/internal.js.map +1 -1
- package/dist/internal.min.cjs +1 -1
- package/dist/internal.min.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-EFQLDGFY.js.map +0 -1
- package/dist/chunk-YWBPS6E4.min.js +0 -1
package/dist/internal.d.cts
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
|
-
import { e as PxNode, Q as PxGlyphFont, bo as PxAnimatable, c as PxAnimatorConfig, ai as PxScroll, al as PxScrollPhase, s as PxBezierPath, aE as PxTransformParts, ag as PxSchema, aS as PxWireVersionStep, aP as PxWireStepKind } from './PxDocumentDiagnostic-
|
|
2
|
-
export { bp as PX_ANIM_ATTR_NAME, bq as PX_ANIM_SRC_ATTR_NAME, br as PX_CSS_ONLY_STYLE_PROPS, bs as PX_DISALLOWED_SVG_TAGS_LOWER, bt as PX_LOOP_JUMP_SHIFT_MS, bu as PX_TEXT_CONTENT_ATTR, bv as PX_UNKNOWN_KEY_ERROR, bw as PxAnyKeyframe, bx as PxMaterializeAllOptions, by as PxNormalizedKeyframe, bz as PxNormalizedPropertyAnimation, bA as createDiagnostics, bB as deepClone, bC as generateUniqueId, bD as interpolateValue, bE as keyframeEasing, bF as keyframeValue, bG as materializeMotionPathInPropAnim, bH as
|
|
1
|
+
import { n as PxAnimationDefinition, e as PxNode, Q as PxGlyphFont, bo as PxAnimatable, F as PxDiagnostics, c as PxAnimatorConfig, ai as PxScroll, al as PxScrollPhase, s as PxBezierPath, aE as PxTransformParts, ag as PxSchema, aS as PxWireVersionStep, aP as PxWireStepKind } from './PxDocumentDiagnostic-CBhVyNEi.cjs';
|
|
2
|
+
export { bp as PX_ANIM_ATTR_NAME, bq as PX_ANIM_SRC_ATTR_NAME, br as PX_CSS_ONLY_STYLE_PROPS, bs as PX_DISALLOWED_SVG_TAGS_LOWER, bt as PX_LOOP_JUMP_SHIFT_MS, bu as PX_TEXT_CONTENT_ATTR, bv as PX_UNKNOWN_KEY_ERROR, bw as PxAnyKeyframe, bx as PxMaterializeAllOptions, by as PxNormalizedKeyframe, bz as PxNormalizedPropertyAnimation, bA as createDiagnostics, bB as deepClone, bC as generateUniqueId, bD as interpolateValue, bE as keyframeEasing, bF as keyframeValue, bG as materializeMotionPathInPropAnim, bH as prepareDocumentForRender, bI as reportDocumentDiagnostics, bJ as sanitizeAttributeValue } from './PxDocumentDiagnostic-CBhVyNEi.cjs';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TRANSFORM PRECEDENCE (review §0.4/§1.6) — CSS's own composition rule, applied at READ:
|
|
6
|
+
* a static `transform` on the element composes UNDER the animated transform, instead of
|
|
7
|
+
* being silently clobbered by it. Implemented as a keyframe-value MERGE during
|
|
8
|
+
* normalization, so both engines (and every consumer downstream) see complete parts:
|
|
9
|
+
*
|
|
10
|
+
* 1. `animate.transform` with PARTIAL parts records — every keyframe value (and the
|
|
11
|
+
* base `value`) inherits the static parts it does not set:
|
|
12
|
+
* static `{rotate: 45}` + kf `{translate: [80, 0]}` → kf `{rotate: 45, translate: [80, 0]}`.
|
|
13
|
+
* 2. ONE individual channel (`translate` / `rotate` / `scale` / `skew`) and no
|
|
14
|
+
* `transform` channel — the channel is REWRITTEN as a unified `transform` channel
|
|
15
|
+
* whose values carry the static parts: the rect stays rotated 45° AND slides.
|
|
16
|
+
*
|
|
17
|
+
* The static transform may be a parts record or an attribute string (parsed by the
|
|
18
|
+
* conservative {@link parseTransformParts} — unparseable strings skip the merge).
|
|
19
|
+
* NOT merged (documented limitations): several individual channels animated at once
|
|
20
|
+
* (they still last-write-wins against each other), and an individual channel next to an
|
|
21
|
+
* animated `transform` (the `transform` channel wins, as before).
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
declare function mergeStaticTransformIntoAnimDef(animDef: PxAnimationDefinition, staticTransform: unknown): PxAnimationDefinition;
|
|
3
25
|
|
|
4
26
|
interface VmNode {
|
|
5
27
|
type?: string;
|
|
@@ -166,6 +188,41 @@ interface ExtendedPath {
|
|
|
166
188
|
*/
|
|
167
189
|
declare function extendedPathForBrowser(pathD: string, opts: ExtendPathOptions): ExtendedPath;
|
|
168
190
|
|
|
191
|
+
/**
|
|
192
|
+
* One element, fully decided. Everything a factory needs and nothing it has to think about.
|
|
193
|
+
* @internal
|
|
194
|
+
*/
|
|
195
|
+
interface PxElementSpec<T> {
|
|
196
|
+
/** The SVG tag name. */
|
|
197
|
+
tag: string;
|
|
198
|
+
/** Attributes under the names SVG reads (`stroke-width`, `viewBox`, `class`), sanitized. */
|
|
199
|
+
attrs: Record<string, string>;
|
|
200
|
+
/** Inline style, camelCase property → value; `undefined` when there is none. */
|
|
201
|
+
style: Record<string, string> | undefined;
|
|
202
|
+
/** Rendered children, blocked ones already dropped. Empty when the node has none. */
|
|
203
|
+
children: Array<T>;
|
|
204
|
+
/** The node's text — set ONLY when `children` is empty (a line `<tspan>` can carry both,
|
|
205
|
+
* and then the styled child spans are what renders). */
|
|
206
|
+
text: string | undefined;
|
|
207
|
+
/** The source node — for the node's `id` (refs) and nothing about rendering. */
|
|
208
|
+
node: PxNode;
|
|
209
|
+
/** True for the tree's root `<svg>`. */
|
|
210
|
+
isRoot: boolean;
|
|
211
|
+
/** Position among its siblings — a stable list key for frameworks that want one. */
|
|
212
|
+
index: number;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* The ONE thing a framework supplies: make an element from a finished spec.
|
|
216
|
+
* @internal
|
|
217
|
+
*/
|
|
218
|
+
type PxElementFactory<T> = (spec: PxElementSpec<T>) => T;
|
|
219
|
+
/**
|
|
220
|
+
* Renders a `PxNode` tree through `factory`. Returns `null` for a missing node or a blocked
|
|
221
|
+
* root tag; blocked descendants are dropped and reported.
|
|
222
|
+
* @internal
|
|
223
|
+
*/
|
|
224
|
+
declare function renderPxTree<T>(node: PxNode | undefined, factory: PxElementFactory<T>, diag?: PxDiagnostics): T | null;
|
|
225
|
+
|
|
169
226
|
/** Is this document scroll-driven? (`animator.timelineSource === 'scroll'`) @internal */
|
|
170
227
|
declare function isScrollTimeline(config: PxAnimatorConfig | undefined): boolean;
|
|
171
228
|
/**
|
|
@@ -375,4 +432,4 @@ declare function planSchemaRelease(p: {
|
|
|
375
432
|
*/
|
|
376
433
|
declare function releaseLogProblems(releases: ReadonlyArray<SchemaReleaseRecord>, steps: ReadonlyArray<PxWireVersionStep>, declared: string, baseline: string): Array<string>;
|
|
377
434
|
|
|
378
|
-
export { PX_COLOR_ATTR_NAMES, PX_DEFAULT_DURATION_MS, PX_PCT_BASED_ATTR_NAMES, PX_STYLE_ATTR_NAMES, PX_TRANSFORM_FN_NAMES, PxAnimatable, type PxCreateElement, type PxGlyphCharBox, bezierToSvgPath, camelCaseToKebabWordIfNeeded, clamp, composeTransformParts, createPathSampler, cubicBezier, diffFieldUniverse, diffInEffect, extendedPathForBrowser, isScrollTimeline, kebabToCamelCaseWord, layoutGlyphTextChars, materializeGlyphText, materializeGlyphTextAlongPath, planSchemaRelease, releaseLogProblems, reverseEasing, schemaFieldUniverse, scrollOffsetProgress, scrollPhaseInterval, scrollResolveAxis, scrollTotalDurationMs, scrollViewProgress, splitEasing, subdivideCubicBezier, toRGBA };
|
|
435
|
+
export { PX_COLOR_ATTR_NAMES, PX_DEFAULT_DURATION_MS, PX_PCT_BASED_ATTR_NAMES, PX_STYLE_ATTR_NAMES, PX_TRANSFORM_FN_NAMES, PxAnimatable, type PxCreateElement, type PxElementFactory, type PxElementSpec, type PxGlyphCharBox, bezierToSvgPath, camelCaseToKebabWordIfNeeded, clamp, composeTransformParts, createPathSampler, cubicBezier, diffFieldUniverse, diffInEffect, extendedPathForBrowser, isScrollTimeline, kebabToCamelCaseWord, layoutGlyphTextChars, materializeGlyphText, materializeGlyphTextAlongPath, mergeStaticTransformIntoAnimDef, planSchemaRelease, releaseLogProblems, renderPxTree, reverseEasing, schemaFieldUniverse, scrollOffsetProgress, scrollPhaseInterval, scrollResolveAxis, scrollTotalDurationMs, scrollViewProgress, splitEasing, subdivideCubicBezier, toRGBA };
|
package/dist/internal.d.ts
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
|
-
import { e as PxNode, Q as PxGlyphFont, bo as PxAnimatable, c as PxAnimatorConfig, ai as PxScroll, al as PxScrollPhase, s as PxBezierPath, aE as PxTransformParts, ag as PxSchema, aS as PxWireVersionStep, aP as PxWireStepKind } from './PxDocumentDiagnostic-
|
|
2
|
-
export { bp as PX_ANIM_ATTR_NAME, bq as PX_ANIM_SRC_ATTR_NAME, br as PX_CSS_ONLY_STYLE_PROPS, bs as PX_DISALLOWED_SVG_TAGS_LOWER, bt as PX_LOOP_JUMP_SHIFT_MS, bu as PX_TEXT_CONTENT_ATTR, bv as PX_UNKNOWN_KEY_ERROR, bw as PxAnyKeyframe, bx as PxMaterializeAllOptions, by as PxNormalizedKeyframe, bz as PxNormalizedPropertyAnimation, bA as createDiagnostics, bB as deepClone, bC as generateUniqueId, bD as interpolateValue, bE as keyframeEasing, bF as keyframeValue, bG as materializeMotionPathInPropAnim, bH as
|
|
1
|
+
import { n as PxAnimationDefinition, e as PxNode, Q as PxGlyphFont, bo as PxAnimatable, F as PxDiagnostics, c as PxAnimatorConfig, ai as PxScroll, al as PxScrollPhase, s as PxBezierPath, aE as PxTransformParts, ag as PxSchema, aS as PxWireVersionStep, aP as PxWireStepKind } from './PxDocumentDiagnostic-CBhVyNEi.js';
|
|
2
|
+
export { bp as PX_ANIM_ATTR_NAME, bq as PX_ANIM_SRC_ATTR_NAME, br as PX_CSS_ONLY_STYLE_PROPS, bs as PX_DISALLOWED_SVG_TAGS_LOWER, bt as PX_LOOP_JUMP_SHIFT_MS, bu as PX_TEXT_CONTENT_ATTR, bv as PX_UNKNOWN_KEY_ERROR, bw as PxAnyKeyframe, bx as PxMaterializeAllOptions, by as PxNormalizedKeyframe, bz as PxNormalizedPropertyAnimation, bA as createDiagnostics, bB as deepClone, bC as generateUniqueId, bD as interpolateValue, bE as keyframeEasing, bF as keyframeValue, bG as materializeMotionPathInPropAnim, bH as prepareDocumentForRender, bI as reportDocumentDiagnostics, bJ as sanitizeAttributeValue } from './PxDocumentDiagnostic-CBhVyNEi.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* TRANSFORM PRECEDENCE (review §0.4/§1.6) — CSS's own composition rule, applied at READ:
|
|
6
|
+
* a static `transform` on the element composes UNDER the animated transform, instead of
|
|
7
|
+
* being silently clobbered by it. Implemented as a keyframe-value MERGE during
|
|
8
|
+
* normalization, so both engines (and every consumer downstream) see complete parts:
|
|
9
|
+
*
|
|
10
|
+
* 1. `animate.transform` with PARTIAL parts records — every keyframe value (and the
|
|
11
|
+
* base `value`) inherits the static parts it does not set:
|
|
12
|
+
* static `{rotate: 45}` + kf `{translate: [80, 0]}` → kf `{rotate: 45, translate: [80, 0]}`.
|
|
13
|
+
* 2. ONE individual channel (`translate` / `rotate` / `scale` / `skew`) and no
|
|
14
|
+
* `transform` channel — the channel is REWRITTEN as a unified `transform` channel
|
|
15
|
+
* whose values carry the static parts: the rect stays rotated 45° AND slides.
|
|
16
|
+
*
|
|
17
|
+
* The static transform may be a parts record or an attribute string (parsed by the
|
|
18
|
+
* conservative {@link parseTransformParts} — unparseable strings skip the merge).
|
|
19
|
+
* NOT merged (documented limitations): several individual channels animated at once
|
|
20
|
+
* (they still last-write-wins against each other), and an individual channel next to an
|
|
21
|
+
* animated `transform` (the `transform` channel wins, as before).
|
|
22
|
+
* @internal
|
|
23
|
+
*/
|
|
24
|
+
declare function mergeStaticTransformIntoAnimDef(animDef: PxAnimationDefinition, staticTransform: unknown): PxAnimationDefinition;
|
|
3
25
|
|
|
4
26
|
interface VmNode {
|
|
5
27
|
type?: string;
|
|
@@ -166,6 +188,41 @@ interface ExtendedPath {
|
|
|
166
188
|
*/
|
|
167
189
|
declare function extendedPathForBrowser(pathD: string, opts: ExtendPathOptions): ExtendedPath;
|
|
168
190
|
|
|
191
|
+
/**
|
|
192
|
+
* One element, fully decided. Everything a factory needs and nothing it has to think about.
|
|
193
|
+
* @internal
|
|
194
|
+
*/
|
|
195
|
+
interface PxElementSpec<T> {
|
|
196
|
+
/** The SVG tag name. */
|
|
197
|
+
tag: string;
|
|
198
|
+
/** Attributes under the names SVG reads (`stroke-width`, `viewBox`, `class`), sanitized. */
|
|
199
|
+
attrs: Record<string, string>;
|
|
200
|
+
/** Inline style, camelCase property → value; `undefined` when there is none. */
|
|
201
|
+
style: Record<string, string> | undefined;
|
|
202
|
+
/** Rendered children, blocked ones already dropped. Empty when the node has none. */
|
|
203
|
+
children: Array<T>;
|
|
204
|
+
/** The node's text — set ONLY when `children` is empty (a line `<tspan>` can carry both,
|
|
205
|
+
* and then the styled child spans are what renders). */
|
|
206
|
+
text: string | undefined;
|
|
207
|
+
/** The source node — for the node's `id` (refs) and nothing about rendering. */
|
|
208
|
+
node: PxNode;
|
|
209
|
+
/** True for the tree's root `<svg>`. */
|
|
210
|
+
isRoot: boolean;
|
|
211
|
+
/** Position among its siblings — a stable list key for frameworks that want one. */
|
|
212
|
+
index: number;
|
|
213
|
+
}
|
|
214
|
+
/**
|
|
215
|
+
* The ONE thing a framework supplies: make an element from a finished spec.
|
|
216
|
+
* @internal
|
|
217
|
+
*/
|
|
218
|
+
type PxElementFactory<T> = (spec: PxElementSpec<T>) => T;
|
|
219
|
+
/**
|
|
220
|
+
* Renders a `PxNode` tree through `factory`. Returns `null` for a missing node or a blocked
|
|
221
|
+
* root tag; blocked descendants are dropped and reported.
|
|
222
|
+
* @internal
|
|
223
|
+
*/
|
|
224
|
+
declare function renderPxTree<T>(node: PxNode | undefined, factory: PxElementFactory<T>, diag?: PxDiagnostics): T | null;
|
|
225
|
+
|
|
169
226
|
/** Is this document scroll-driven? (`animator.timelineSource === 'scroll'`) @internal */
|
|
170
227
|
declare function isScrollTimeline(config: PxAnimatorConfig | undefined): boolean;
|
|
171
228
|
/**
|
|
@@ -375,4 +432,4 @@ declare function planSchemaRelease(p: {
|
|
|
375
432
|
*/
|
|
376
433
|
declare function releaseLogProblems(releases: ReadonlyArray<SchemaReleaseRecord>, steps: ReadonlyArray<PxWireVersionStep>, declared: string, baseline: string): Array<string>;
|
|
377
434
|
|
|
378
|
-
export { PX_COLOR_ATTR_NAMES, PX_DEFAULT_DURATION_MS, PX_PCT_BASED_ATTR_NAMES, PX_STYLE_ATTR_NAMES, PX_TRANSFORM_FN_NAMES, PxAnimatable, type PxCreateElement, type PxGlyphCharBox, bezierToSvgPath, camelCaseToKebabWordIfNeeded, clamp, composeTransformParts, createPathSampler, cubicBezier, diffFieldUniverse, diffInEffect, extendedPathForBrowser, isScrollTimeline, kebabToCamelCaseWord, layoutGlyphTextChars, materializeGlyphText, materializeGlyphTextAlongPath, planSchemaRelease, releaseLogProblems, reverseEasing, schemaFieldUniverse, scrollOffsetProgress, scrollPhaseInterval, scrollResolveAxis, scrollTotalDurationMs, scrollViewProgress, splitEasing, subdivideCubicBezier, toRGBA };
|
|
435
|
+
export { PX_COLOR_ATTR_NAMES, PX_DEFAULT_DURATION_MS, PX_PCT_BASED_ATTR_NAMES, PX_STYLE_ATTR_NAMES, PX_TRANSFORM_FN_NAMES, PxAnimatable, type PxCreateElement, type PxElementFactory, type PxElementSpec, type PxGlyphCharBox, bezierToSvgPath, camelCaseToKebabWordIfNeeded, clamp, composeTransformParts, createPathSampler, cubicBezier, diffFieldUniverse, diffInEffect, extendedPathForBrowser, isScrollTimeline, kebabToCamelCaseWord, layoutGlyphTextChars, materializeGlyphText, materializeGlyphTextAlongPath, mergeStaticTransformIntoAnimDef, planSchemaRelease, releaseLogProblems, renderPxTree, reverseEasing, schemaFieldUniverse, scrollOffsetProgress, scrollPhaseInterval, scrollResolveAxis, scrollTotalDurationMs, scrollViewProgress, splitEasing, subdivideCubicBezier, toRGBA };
|
package/dist/internal.js
CHANGED
|
@@ -11,6 +11,8 @@ import {
|
|
|
11
11
|
PX_TEXT_CONTENT_ATTR,
|
|
12
12
|
PX_TRANSFORM_FN_NAMES,
|
|
13
13
|
PX_UNKNOWN_KEY_ERROR,
|
|
14
|
+
PxDiagnosticKind,
|
|
15
|
+
__objRest,
|
|
14
16
|
bezierToSvgPath,
|
|
15
17
|
camelCaseToKebabWordIfNeeded,
|
|
16
18
|
clamp,
|
|
@@ -32,13 +34,15 @@ import {
|
|
|
32
34
|
materializeMotionPathInPropAnim,
|
|
33
35
|
mergeStaticTransformIntoAnimDef,
|
|
34
36
|
parseWireVersion,
|
|
37
|
+
prepareDocumentForRender,
|
|
35
38
|
reportDocumentDiagnostics,
|
|
36
39
|
reverseEasing,
|
|
37
40
|
sanitizeAttributeValue,
|
|
38
41
|
splitEasing,
|
|
39
42
|
subdivideCubicBezier,
|
|
43
|
+
toDomProps,
|
|
40
44
|
toRGBA
|
|
41
|
-
} from "./chunk-
|
|
45
|
+
} from "./chunk-YVKMPBHE.js";
|
|
42
46
|
|
|
43
47
|
// src/effects/PlayerEffectsUtil.visualModel.ts
|
|
44
48
|
var IDENTITY = [1, 0, 0, 1, 0, 0];
|
|
@@ -249,6 +253,52 @@ function subtractMultiset(a, b) {
|
|
|
249
253
|
return extra;
|
|
250
254
|
}
|
|
251
255
|
|
|
256
|
+
// src/render/PxRenderTree.ts
|
|
257
|
+
var CLASS_NAME_KEY = "className";
|
|
258
|
+
var CLASS_ATTR = "class";
|
|
259
|
+
var DOM_TYPE_KEY = "domType";
|
|
260
|
+
var TYPE_ATTR = "type";
|
|
261
|
+
var DEFAULT_TAG = "g";
|
|
262
|
+
function renderPxTree(node, factory, diag) {
|
|
263
|
+
return node ? renderOne(node, factory, diag, true, 0) : null;
|
|
264
|
+
}
|
|
265
|
+
function renderOne(node, factory, diag, isRoot, index) {
|
|
266
|
+
const _a = node, { type, children, style } = _a, props = __objRest(_a, ["type", "children", "style"]);
|
|
267
|
+
const tag = type || DEFAULT_TAG;
|
|
268
|
+
if (PX_DISALLOWED_SVG_TAGS_LOWER.has(tag.toLowerCase())) {
|
|
269
|
+
(diag != null ? diag : createDiagnostics(void 0, "[PxAnimator]")).warn(PxDiagnosticKind.document, 1103 /* blockedTag */, tag);
|
|
270
|
+
return null;
|
|
271
|
+
}
|
|
272
|
+
const domType = props[DOM_TYPE_KEY];
|
|
273
|
+
if (domType !== void 0) delete props[DOM_TYPE_KEY];
|
|
274
|
+
const attrs = {};
|
|
275
|
+
let inlineStyle;
|
|
276
|
+
const domProps = toDomProps(props);
|
|
277
|
+
for (const propName of Object.keys(domProps)) {
|
|
278
|
+
const sanitized = sanitizeAttributeValue(propName, domProps[propName]);
|
|
279
|
+
if (sanitized === void 0) continue;
|
|
280
|
+
if (PX_CSS_ONLY_STYLE_PROPS.has(propName)) {
|
|
281
|
+
(inlineStyle != null ? inlineStyle : inlineStyle = {})[propName] = String(sanitized);
|
|
282
|
+
continue;
|
|
283
|
+
}
|
|
284
|
+
attrs[propName === CLASS_NAME_KEY ? CLASS_ATTR : camelCaseToKebabWordIfNeeded(propName)] = sanitized;
|
|
285
|
+
}
|
|
286
|
+
if (domType !== void 0) attrs[TYPE_ATTR] = String(domType);
|
|
287
|
+
if (style) {
|
|
288
|
+
for (const styleProp of Object.keys(style)) (inlineStyle != null ? inlineStyle : inlineStyle = {})[styleProp] = String(style[styleProp]);
|
|
289
|
+
}
|
|
290
|
+
const rendered = [];
|
|
291
|
+
if (children) {
|
|
292
|
+
children.forEach((child, i) => {
|
|
293
|
+
const el = renderOne(child, factory, diag, false, i);
|
|
294
|
+
if (el !== null) rendered.push(el);
|
|
295
|
+
});
|
|
296
|
+
}
|
|
297
|
+
const ownText = props[PX_TEXT_CONTENT_ATTR];
|
|
298
|
+
const text = !rendered.length && typeof ownText === "string" && ownText ? ownText : void 0;
|
|
299
|
+
return factory({ tag, attrs, style: inlineStyle, children: rendered, text, node, isRoot, index });
|
|
300
|
+
}
|
|
301
|
+
|
|
252
302
|
// src/playback/PxScrollMath.ts
|
|
253
303
|
function isScrollTimeline(config) {
|
|
254
304
|
return (config == null ? void 0 : config.timelineSource) === "scroll";
|
|
@@ -463,7 +513,9 @@ export {
|
|
|
463
513
|
materializeMotionPathInPropAnim,
|
|
464
514
|
mergeStaticTransformIntoAnimDef,
|
|
465
515
|
planSchemaRelease,
|
|
516
|
+
prepareDocumentForRender,
|
|
466
517
|
releaseLogProblems,
|
|
518
|
+
renderPxTree,
|
|
467
519
|
reportDocumentDiagnostics,
|
|
468
520
|
reverseEasing,
|
|
469
521
|
sanitizeAttributeValue,
|
package/dist/internal.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/effects/PlayerEffectsUtil.visualModel.ts","../src/playback/PxScrollMath.ts","../src/version/PxSchemaFieldUniverse.ts","../src/version/PxSchemaRelease.ts"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * \"Equal-in-effect\" comparator for SVGA node trees.\n *\n * Two trees can render identically while differing structurally — extra `<g>`\n * wrappers, different ids, `<use>` clones vs inline copies, a transform expressed\n * as a baked string vs a parts record. This module reduces a tree to what is\n * actually painted: every drawable leaf, with its CUMULATIVE transform matrix\n * (resolved at a given time) and key visual attributes. Comparing those leaf\n * sets ignores representation and catches genuine visual differences.\n *\n * It is a deterministic function applied identically to both trees, so equal\n * inputs (in effect) yield equal output regardless of how each was encoded.\n *\n * Dependency-free on purpose (mirrors the applier's isolation). Scope: the node\n * types and transform forms the player-effects / heavy serializers emit. Color\n * animation is not interpolated — leaves are compared at keyframe instants where\n * sampled values are exact.\n */\n\ntype Mat = [number, number, number, number, number, number];\n\ninterface VmNode {\n type?: string;\n children?: Array<VmNode>;\n [attr: string]: any;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// MATRIX MATH (2x3 affine, SVG order: [a b c d e f])\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst IDENTITY: Mat = [1, 0, 0, 1, 0, 0];\n\nfunction mul(m: Mat, n: Mat): Mat {\n return [\n m[0] * n[0] + m[2] * n[1],\n m[1] * n[0] + m[3] * n[1],\n m[0] * n[2] + m[2] * n[3],\n m[1] * n[2] + m[3] * n[3],\n m[0] * n[4] + m[2] * n[5] + m[4],\n m[1] * n[4] + m[3] * n[5] + m[5],\n ];\n}\n\nconst translateM = (x: number, y: number): Mat => [1, 0, 0, 1, x, y];\nconst scaleM = (sx: number, sy: number): Mat => [sx, 0, 0, sy, 0, 0];\nfunction rotateM(deg: number): Mat {\n const r = (deg * Math.PI) / 180;\n return [Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0];\n}\nconst skewXM = (deg: number): Mat => [1, 0, Math.tan((deg * Math.PI) / 180), 1, 0, 0];\nconst skewYM = (deg: number): Mat => [1, Math.tan((deg * Math.PI) / 180), 0, 1, 0, 0];\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// TRANSFORM EVALUATION\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Parses an SVG transform string (\"translate(..)rotate(..)…\") into a matrix. */\nfunction parseTransformString(s: string): Mat {\n let m = IDENTITY;\n const re = /(translate|rotate|scale|matrix|skewX|skewY)\\(([^)]*)\\)/g;\n let hit: RegExpExecArray | null;\n while ((hit = re.exec(s))) {\n const fn = hit[1];\n const a = hit[2].split(/[\\s,]+/).filter(Boolean).map(Number);\n if (fn === 'translate') m = mul(m, translateM(a[0] || 0, a[1] || 0));\n else if (fn === 'scale') m = mul(m, scaleM(a[0], a.length > 1 ? a[1] : a[0]));\n else if (fn === 'rotate') m = mul(m, rotateM(a[0]));\n else if (fn === 'skewX') m = mul(m, skewXM(a[0]));\n else if (fn === 'skewY') m = mul(m, skewYM(a[0]));\n else if (fn === 'matrix') m = mul(m, [a[0], a[1], a[2], a[3], a[4], a[5]]);\n }\n return m;\n}\n\ninterface Parts { translate?: [number, number]; rotate?: number; scale?: [number, number]; origin?: [number, number]; }\n\n/** Composes a `PxTransformParts` record (player canonical order, origin-pivoted). */\nfunction partsToMatrix(p: Parts): Mat {\n let m = IDENTITY;\n if (p.translate) m = mul(m, translateM(p.translate[0], p.translate[1]));\n const pivot = p.origin && (p.rotate !== undefined || p.scale);\n if (pivot) m = mul(m, translateM(p.origin![0], p.origin![1]));\n if (p.rotate !== undefined) m = mul(m, rotateM(p.rotate));\n if (p.scale) m = mul(m, scaleM(p.scale[0], p.scale[1]));\n if (pivot) m = mul(m, translateM(-p.origin![0], -p.origin![1]));\n return m;\n}\n\nfunction lerp(a: number, b: number, f: number): number { return a + (b - a) * f; }\n\n/** Linear-interpolates a parts record between keyframes at time `t` (ms). */\nfunction interpParts(kfs: Array<any>, t: number): Parts {\n if (!kfs.length) return {};\n if (t <= (kfs[0].time ?? 0)) return kfs[0].value || {};\n if (t >= (kfs[kfs.length - 1].time ?? 0)) return kfs[kfs.length - 1].value || {};\n\n let i = 0;\n while (i < kfs.length - 1 && (kfs[i + 1].time ?? 0) < t) i++;\n const a = kfs[i], b = kfs[i + 1];\n const f = (t - (a.time ?? 0)) / ((b.time ?? 0) - (a.time ?? 0) || 1);\n const va: Parts = a.value || {}, vb: Parts = b.value || {};\n\n const out: Parts = {};\n if (va.translate && vb.translate) out.translate = [lerp(va.translate[0], vb.translate[0], f), lerp(va.translate[1], vb.translate[1], f)];\n else out.translate = va.translate || vb.translate;\n if (va.rotate !== undefined && vb.rotate !== undefined) out.rotate = lerp(va.rotate, vb.rotate, f);\n else out.rotate = va.rotate ?? vb.rotate;\n if (va.scale && vb.scale) out.scale = [lerp(va.scale[0], vb.scale[0], f), lerp(va.scale[1], vb.scale[1], f)];\n else out.scale = va.scale || vb.scale;\n out.origin = va.origin || vb.origin;\n return out;\n}\n\n/** Resolves any transform-slot form (string | bare record | {value} | {keyframes}) to a matrix at `t`. */\nfunction evalTransformValue(v: any, t: number): Mat {\n if (v === undefined || v === null) return IDENTITY;\n if (typeof v === 'string') return parseTransformString(v);\n if (v.keyframes) return partsToMatrix(interpParts(v.keyframes, t));\n if (v.value) return partsToMatrix(v.value);\n if (typeof v === 'object' && !Array.isArray(v)) return partsToMatrix(v); // bare parts record\n return IDENTITY;\n}\n\n/** The node's own transform at `t`: animated slot wins over the static baseline. */\nfunction nodeMatrix(node: VmNode, t: number): Mat {\n if (node.animate && node.animate.transform) return evalTransformValue(node.animate.transform, t);\n if (node.transform !== undefined) return evalTransformValue(node.transform, t);\n return IDENTITY;\n}\n\n/** Linear-interpolates a scalar animation (e.g. opacity); falls back to static / default. */\nfunction evalScalar(animated: any, staticVal: any, fallback: number, t: number): number {\n if (animated && animated.keyframes && animated.keyframes.length) {\n const kfs = animated.keyframes;\n if (t <= (kfs[0].time ?? 0)) return kfs[0].value;\n if (t >= (kfs[kfs.length - 1].time ?? 0)) return kfs[kfs.length - 1].value;\n let i = 0;\n while (i < kfs.length - 1 && (kfs[i + 1].time ?? 0) < t) i++;\n const a = kfs[i], b = kfs[i + 1];\n const f = (t - (a.time ?? 0)) / ((b.time ?? 0) - (a.time ?? 0) || 1);\n return lerp(a.value, b.value, f);\n }\n return staticVal !== undefined ? Number(staticVal) : fallback;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FLATTEN → multiset of painted primitives\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst CONTAINER_TYPES = new Set(['svg', 'g', 'symbol']);\nconst SKIP_TYPES = new Set(['defs', 'mask', 'clipPath', 'title']);\n\nfunction buildIdMap(node: VmNode, map: Map<string, VmNode>): void {\n if (typeof node.id === 'string') map.set(node.id, node);\n node.children?.forEach(c => buildIdMap(c, map));\n}\n\nfunction num(v: any): number { return v === undefined || v === null ? 0 : Number(v); }\n\n// 2-decimal rounding (sub-pixel). The heavy path bakes matrices into strings\n// rounded to ~4 decimals; comparing at 2 decimals avoids straddling a rounding\n// boundary against the applier's full-precision values, while staying visually exact.\nfunction round(n: number): number { return Math.round(n * 100) / 100 + 0; }\n\nfunction geomKey(node: VmNode): string {\n switch (node.type) {\n case 'rect': return num(node.width) + ',' + num(node.height) + ',' + num(node.x) + ',' + num(node.y);\n case 'ellipse': return num(node.rx) + ',' + num(node.ry) + ',' + num(node.cx) + ',' + num(node.cy);\n case 'circle': return num(node.r) + ',' + num(node.cx) + ',' + num(node.cy);\n case 'path': return String(node.d ?? '');\n default: return '';\n }\n}\n\nfunction describePrimitive(node: VmNode, m: Mat, t: number): string {\n const fill = node.fill ?? '';\n const stroke = node.stroke ?? '';\n const sw = node['stroke-width'] ?? node.strokeWidth ?? '';\n const opacity = round(evalScalar(node.animate?.opacity, node.opacity, 1, t));\n const masked = node.mask ? 1 : 0;\n const mat = m.map(round).join(',');\n return node.type + '|' + geomKey(node) + '|[' + mat + ']|f:' + fill + '|s:' + stroke + '|sw:' + (num(sw) || '') + '|o:' + opacity + '|m:' + masked;\n}\n\nfunction flatten(node: VmNode, parent: Mat, t: number, idMap: Map<string, VmNode>, out: Array<string>): void {\n const type = node.type || '';\n if (SKIP_TYPES.has(type)) return;\n\n const m = mul(parent, nodeMatrix(node, t));\n\n if (type === 'use') {\n const targetId = typeof node.href === 'string' ? node.href.replace(/^#/, '') : '';\n const target = idMap.get(targetId);\n const useM = mul(m, translateM(num(node.x), num(node.y)));\n if (target) flatten(target, useM, t, idMap, out);\n else out.push('UNRESOLVED_USE:#' + targetId);\n return;\n }\n\n if (CONTAINER_TYPES.has(type)) {\n node.children?.forEach(c => flatten(c, m, t, idMap, out));\n return;\n }\n\n out.push(describePrimitive(node, m, t));\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PUBLIC: sample times + visual model + comparison\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Every keyframe `time` found anywhere in the tree, plus 0. @internal */\nexport function collectSampleTimes(node: VmNode, into: Set<number>): void {\n into.add(0);\n const scanAnim = (anim: any) => {\n if (!anim || typeof anim !== 'object') return;\n for (const key of Object.keys(anim)) {\n const kfs = anim[key]?.keyframes;\n if (Array.isArray(kfs)) kfs.forEach(kf => into.add(kf.time ?? 0));\n }\n };\n scanAnim(node.animate);\n if (node.transform && typeof node.transform === 'object' && (node.transform as any).keyframes) {\n (node.transform as any).keyframes.forEach((kf: any) => into.add(kf.time ?? 0));\n }\n node.children?.forEach(c => collectSampleTimes(c, into));\n}\n\n/** Sorted, painted-primitive multiset for the tree at time `t`. @internal */\nexport function visualModelAt(root: VmNode, t: number): Array<string> {\n const idMap = new Map<string, VmNode>();\n buildIdMap(root, idMap);\n const out: Array<string> = [];\n flatten(root, IDENTITY, t, idMap, out);\n return out.sort();\n}\n\nexport interface EffectDiff {\n time: number;\n onlyInA: Array<string>;\n onlyInB: Array<string>;\n}\n\n/**\n * Compares two trees \"in effect\" across all keyframe instants found in either.\n * Returns one entry per time where the painted-primitive multisets differ.\n * @internal\n */\nexport function diffInEffect(a: VmNode, b: VmNode): Array<EffectDiff> {\n const times = new Set<number>();\n collectSampleTimes(a, times);\n collectSampleTimes(b, times);\n\n const diffs: Array<EffectDiff> = [];\n for (const t of Array.from(times).sort((x, y) => x - y)) {\n const ma = visualModelAt(a, t);\n const mb = visualModelAt(b, t);\n const onlyInA = subtractMultiset(ma, mb);\n const onlyInB = subtractMultiset(mb, ma);\n if (onlyInA.length || onlyInB.length) diffs.push({ time: t, onlyInA, onlyInB });\n }\n return diffs;\n}\n\n/** Items in `a` not matched one-for-one in `b` (multiset difference). */\nfunction subtractMultiset(a: Array<string>, b: Array<string>): Array<string> {\n const counts = new Map<string, number>();\n for (const x of b) counts.set(x, (counts.get(x) || 0) + 1);\n const extra: Array<string> = [];\n for (const x of a) {\n const c = counts.get(x) || 0;\n if (c > 0) counts.set(x, c - 1);\n else extra.push(x);\n }\n return extra;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// Scroll-timeline PROGRESS MATH — pure functions, no DOM. This is the reference\n// implementation for `animator.timelineSource: 'scroll'` (the player-measured path — `timeline.engine: 'js'`, and the fallback of `auto`,\n// both engines): the DOM driver in svg-animator-web measures rects/offsets and calls in\n// here; the editor's design doc (`app/src/svgeditor/animation/scroll-timeline.design.md`\n// §4) documents the same formulas — keep them in sync.\n\nimport { clamp, PX_DEFAULT_DURATION_MS } from '../util/PxAnimatorUtil';\nimport type { PxAnimatorConfig, PxScroll, PxScrollPhase, PxScrollRangePoint } from '../format/PxAnimatorTypes';\n\n\n/** Is this document scroll-driven? (`animator.timelineSource === 'scroll'`) @internal */\nexport function isScrollTimeline(config: PxAnimatorConfig | undefined): boolean {\n return config?.timelineSource === 'scroll';\n}\n\n/**\n * The seek-space length (ms) a scroll progress of 1 maps to: duration × finite\n * iterations. `'infinite'` is meaningless on a finite progress timeline (see design doc\n * D4) — treated as 1 with the read-side warning left to the consumer.\n * @internal\n */\nexport function scrollTotalDurationMs(config: PxAnimatorConfig | undefined): number {\n const duration = (typeof config?.duration === 'number' && config.duration > 0)\n ? config.duration : PX_DEFAULT_DURATION_MS;\n const iterations = (typeof config?.iterations === 'number' && config.iterations > 0)\n ? config.iterations : 1;\n return duration * iterations;\n}\n\n/**\n * A named phase's interval in `u`-space.\n *\n * `u` is the subject's \"journey distance\": with `sTop` = subject's leading edge in\n * scrollport coordinates, `u = vpSize − sTop` — 0 exactly when the subject is about to\n * enter (leading edge at the scrollport's trailing edge), growing as the user scrolls.\n * The `min`/`max` pairs make every formula valid BOTH for a subject smaller than the\n * scrollport and one larger than it (where \"fully visible\" flips to \"covers the\n * scrollport\") — the same case split CSS specifies for its named timeline ranges.\n * @internal\n */\nexport function scrollPhaseInterval(\n phase: PxScrollPhase, subjectSize: number, scrollportSize: number\n): [number, number] {\n const s = subjectSize, vp = scrollportSize;\n switch (phase) {\n case 'cover': return [0, s + vp];\n case 'entry': return [0, Math.min(s, vp)];\n case 'contain': return [Math.min(s, vp), Math.max(s, vp)];\n case 'exit': return [Math.max(s, vp), s + vp];\n case 'entry-crossing': return [0, s];\n case 'exit-crossing': return [vp, s + vp];\n }\n}\n\nconst DEFAULT_PHASE: PxScrollPhase = 'cover';\n\n/** One range endpoint resolved to a `u`-space value. */\nfunction resolveRangePointU(\n point: PxScrollRangePoint | undefined, defaultFraction: number,\n subjectSize: number, scrollportSize: number\n): number {\n const [u0, u1] = scrollPhaseInterval(point?.phase ?? DEFAULT_PHASE, subjectSize, scrollportSize);\n const fraction = typeof point?.fraction === 'number' ? point.fraction : defaultFraction;\n return u0 + fraction * (u1 - u0);\n}\n\n/**\n * `kind: 'view'` progress ∈ [0, 1]: where the subject's journey sits within the\n * configured range.\n *\n * @param subjectStart subject's leading edge in scrollport coordinates\n * (`subjectRect.top − scrollportRect.top` on the resolved axis)\n * @param subjectSize subject size on the axis\n * @param scrollportSize scrollport size on the axis\n *\n * A degenerate/inverted range (uStart ≥ uEnd — e.g. zero-size subject with an `entry`\n * range) reports 1 once the point is passed, 0 before — never NaN.\n * @internal\n */\nexport function scrollViewProgress(\n subjectStart: number, subjectSize: number, scrollportSize: number,\n range: PxScroll['range'] | undefined\n): number {\n const u = scrollportSize - subjectStart;\n const uStart = resolveRangePointU(range?.start, 0, subjectSize, scrollportSize);\n const uEnd = resolveRangePointU(range?.end, 1, subjectSize, scrollportSize);\n if (uEnd <= uStart) return u >= uEnd ? 1 : 0;\n return clamp((u - uStart) / (uEnd - uStart), 0, 1);\n}\n\n/**\n * `kind: 'scroll'` progress ∈ [0, 1]: the scroller's offset ratio mapped through the\n * range (phases don't exist here — `fraction` is of the total scroll range).\n *\n * `maxOffset === 0` (nothing to scroll) reports 1, matching the CSS spec's rule that a\n * zero-length timeline is at 100%.\n * @internal\n */\nexport function scrollOffsetProgress(\n offset: number, maxOffset: number,\n range: PxScroll['range'] | undefined\n): number {\n const raw = maxOffset > 0 ? clamp(offset / maxOffset, 0, 1) : 1;\n const start = typeof range?.start?.fraction === 'number' ? range.start.fraction : 0;\n const end = typeof range?.end?.fraction === 'number' ? range.end.fraction : 1;\n if (end <= start) return raw >= end ? 1 : 0;\n return clamp((raw - start) / (end - start), 0, 1);\n}\n\n/**\n * Resolve a logical axis to a physical one. `block`/`inline` are writing-mode relative:\n * in horizontal writing (`horizontal-tb`, the default) block flows vertically; in\n * vertical writing modes it flows horizontally.\n * @internal\n */\nexport function scrollResolveAxis(\n axis: PxScroll['axis'] | undefined, writingMode: string | undefined\n): 'x' | 'y' {\n const a = axis ?? 'block';\n if (a === 'x' || a === 'y') return a;\n const vertical = !!writingMode && writingMode.startsWith('vertical');\n if (a === 'inline') return vertical ? 'y' : 'x';\n return vertical ? 'x' : 'y'; // block\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ============================================================================\n// THE PLAYER'S WIRE-KEY INVENTORY — every field the player schema can carry, as a canonical\n// path, sorted. The library half of the editor's ship-guard (task 5.1).\n//\n// Why a committed list at all: A RENAME IS LENGTH-NEUTRAL. One name out, one in, same count,\n// green suite — which is how three renames shipped with no compat read, the last one wiping\n// the geometry of every shape-preset path on open. Diffing the list names the key that left.\n//\n// It lives HERE, not only in the editor, because the library ships on its own: a player\n// released with a key quietly gone breaks every document using it, editor or not.\n//\n// CANONICAL PATHS — the same rule as the editor's `collectSchemaFields`, so the two lists speak\n// one dialect: each object schema is enumerated ONCE, at the first path it is reached by\n// (breadth-first); `[]` marks an array item, `{*}` a record value or open-object value, `|i` /\n// `|tag` a union member. Without the once-only rule every recursive `children` re-lists the\n// whole node schema and the inventory explodes into thousands of paths that say nothing new.\n// ============================================================================\n\nimport { describeSchema, type PxSchema } from '../schema/PxSchema';\n\n/** Every field identity `root` can carry, as canonical paths, sorted. @internal */\nexport function schemaFieldUniverse(root: PxSchema<any, any>): Array<string> {\n const fields = new Set<string>();\n const enumerated = new Set<unknown>();\n const queue: Array<{ schema: PxSchema<any, any>; path: string }> = [{ schema: root, path: '' }];\n\n while (queue.length) {\n const { schema, path } = queue.shift()!;\n const d = describeSchema(schema);\n switch (d.kind) {\n case 'shape': {\n if (enumerated.has(schema)) break; // already listed under its canonical path\n enumerated.add(schema);\n for (const key of Object.keys(d.shape)) {\n const id = path ? path + '.' + key : key;\n fields.add(id);\n queue.push({ schema: d.shape[key], path: id });\n }\n if (d.openValue) queue.push({ schema: d.openValue, path: path + '{*}' });\n break;\n }\n case 'optional': queue.push({ schema: d.inner, path }); break;\n case 'array': queue.push({ schema: d.item, path: path + '[]' }); break;\n case 'lazy': queue.push({ schema: d.resolved, path }); break;\n case 'record': queue.push({ schema: d.value, path: path + '{*}' }); break;\n case 'union':\n d.members.forEach((member, i) => queue.push({ schema: member, path: path + '|' + i }));\n break;\n case 'discriminatedUnion':\n for (const member of d.members) queue.push({ schema: member, path: path + '|' + discriminantOf(member, d.key) });\n break;\n default: break; // tuple / leaf: no named fields\n }\n }\n return [...fields].sort();\n}\n\n/** The literal a discriminated-union member carries at its discriminant, unwrapping an\n * OPTIONAL discriminant (`type?: 'time'`). `?` when it carries none. */\nfunction discriminantOf(member: PxSchema<any, any>, key: string): string {\n const d = describeSchema(member);\n if (d.kind !== 'shape') return '?';\n const keySchema = d.shape[key];\n if (keySchema === undefined) return '?';\n const kd = describeSchema(keySchema);\n return String((kd.kind === 'optional' ? kd.inner : keySchema)._default);\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ============================================================================\n// SCHEMA RELEASES (tasks 5.3 / 5.4) — the decision \"does this release move the schema\n// version, and which part?\" made by the DIFF, not by a person, plus the dated log of every\n// release kept beside the step table (`schema-releases.player.json`).\n//\n// Pure functions, so the release CLI (`scripts/schema-release.mjs`) and the guard test run the\n// SAME rule — a CLI and a test that each re-implement it are two rules that drift apart.\n//\n// The diff is over the canonical field inventory (`schemaFieldUniverse`): key paths only, so\n// descriptions and comments can never make two identical schemas look different.\n// ============================================================================\n\nimport { parseWireVersion, PxWireStepKind, type PxWireVersionStep } from './PxWireVersion';\n\n/** One entry of the release log — what shipped, when, and what it changed. @internal */\nexport interface SchemaReleaseRecord {\n readonly version: string;\n /** ISO date, `YYYY-MM-DD`. */\n readonly date: string;\n /** The first release: nothing to compare it against. */\n readonly baseline?: boolean;\n readonly added: ReadonlyArray<string>;\n readonly removed: ReadonlyArray<string>;\n readonly note?: string;\n}\n\n/** Keys that appeared and keys that left between two inventories, each sorted. @internal */\nexport function diffFieldUniverse(\n previous: ReadonlyArray<string>, current: ReadonlyArray<string>,\n): { added: Array<string>; removed: Array<string> } {\n const prev = new Set(previous);\n const cur = new Set(current);\n return {\n added: current.filter(k => !prev.has(k)).sort(),\n removed: previous.filter(k => !cur.has(k)).sort(),\n };\n}\n\n/** What a release must do about the version. `refuse` set means: do not release as-is. @internal */\nexport interface SchemaReleasePlan {\n /** Did any key appear or leave since the last release? */\n readonly changed: boolean;\n /** Which kind of step the change requires — a removal is never additive. */\n readonly requiredKind?: PxWireStepKind;\n /** The version this release must carry. */\n readonly requiredVersion?: string;\n readonly refuse?: string;\n}\n\n/**\n * THE BUMP RULE. A key change requires `b + 1` and a step that explains it; no key change\n * requires nothing. The rule never picks the number by taste — the inventory diff does.\n * @internal\n */\nexport function planSchemaRelease(p: {\n readonly added: ReadonlyArray<string>;\n readonly removed: ReadonlyArray<string>;\n /** `PX_WIRE_SCHEMA_VERSION` — what the source says now. */\n readonly declared: string;\n /** The version of the last release record. */\n readonly lastReleased: string;\n readonly steps: ReadonlyArray<PxWireVersionStep>;\n}): SchemaReleasePlan {\n const changed = p.added.length > 0 || p.removed.length > 0;\n const last = parseWireVersion(p.lastReleased);\n const declared = parseWireVersion(p.declared);\n if (!last || !declared) return { changed, refuse: 'Unparseable version: ' + p.lastReleased + ' / ' + p.declared + '.' };\n\n const requiredVersion = last.a + '.' + (last.b + 1);\n if (!changed) {\n // Same keys: nothing to bump. A bump anyway is a SEMANTIC change and still needs its step.\n if (declared.b === last.b && declared.a === last.a) return { changed };\n const step = p.steps.find(s => s.to === p.declared);\n return step ? { changed, requiredVersion: p.declared }\n : { changed, refuse: 'The version moved to ' + p.declared + ' with no key change and no step explaining it.' };\n }\n\n const requiredKind = p.removed.length ? PxWireStepKind.converted : PxWireStepKind.additive;\n const summary = p.added.length + ' key(s) added, ' + p.removed.length + ' removed';\n if (declared.a !== last.a || declared.b !== last.b + 1) {\n return {\n changed, requiredKind, requiredVersion,\n refuse: 'The player schema changed (' + summary + ') but PX_WIRE_SCHEMA_VERSION is '\n + p.declared + '. Set it to ' + requiredVersion + ' and add the PX_WIRE_STEPS entry.',\n };\n }\n const step = p.steps.find(s => s.to === p.declared);\n if (!step) {\n return { changed, requiredKind, requiredVersion, refuse: 'No PX_WIRE_STEPS entry reaches ' + p.declared + '.' };\n }\n if (requiredKind === PxWireStepKind.converted && step.kind !== PxWireStepKind.converted) {\n return {\n changed, requiredKind, requiredVersion,\n refuse: 'Keys were REMOVED (' + p.removed.join(', ') + '), which is never additive — the '\n + p.declared + ' step must be `converted`, with an up().',\n };\n }\n return { changed, requiredKind, requiredVersion };\n}\n\n/**\n * Everything wrong with the release log, as sentences — empty when it is consistent. The log\n * must start at the baseline, move strictly forward, END at the version the source declares,\n * and every release after the baseline must have the step that explains it.\n * @internal\n */\nexport function releaseLogProblems(\n releases: ReadonlyArray<SchemaReleaseRecord>, steps: ReadonlyArray<PxWireVersionStep>,\n declared: string, baseline: string,\n): Array<string> {\n const problems: Array<string> = [];\n if (!releases.length) return ['The release log is empty.'];\n if (!releases[0].baseline || releases[0].version !== baseline) {\n problems.push('The first release must be the baseline ' + baseline + '.');\n }\n for (let i = 1; i < releases.length; i++) {\n const prev = parseWireVersion(releases[i - 1].version);\n const cur = parseWireVersion(releases[i].version);\n if (!prev || !cur || cur.a !== prev.a || cur.b !== prev.b + 1) {\n problems.push('Release ' + releases[i].version + ' does not follow ' + releases[i - 1].version + ' by one `b` step.');\n }\n if (releases[i].date < releases[i - 1].date) problems.push('Release ' + releases[i].version + ' is dated before its predecessor.');\n const step = steps.find(s => s.to === releases[i].version);\n if (!step) problems.push('Release ' + releases[i].version + ' has no PX_WIRE_STEPS entry.');\n else if (releases[i].removed.length && step.kind !== PxWireStepKind.converted) {\n problems.push('Release ' + releases[i].version + ' removed keys, but its step is not `converted`.');\n }\n }\n const latest = releases[releases.length - 1].version;\n if (latest !== declared) {\n problems.push('PX_WIRE_SCHEMA_VERSION is ' + declared + ' but the last release record is ' + latest\n + ' — a bump needs its changelog entry (run scripts/schema-release.mjs --apply).');\n }\n return problems;\n}\n"],"mappings":"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","names":["_a","step"]}
|
|
1
|
+
{"version":3,"sources":["../src/effects/PlayerEffectsUtil.visualModel.ts","../src/render/PxRenderTree.ts","../src/playback/PxScrollMath.ts","../src/version/PxSchemaFieldUniverse.ts","../src/version/PxSchemaRelease.ts"],"sourcesContent":["/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n\n/**\n * \"Equal-in-effect\" comparator for SVGA node trees.\n *\n * Two trees can render identically while differing structurally — extra `<g>`\n * wrappers, different ids, `<use>` clones vs inline copies, a transform expressed\n * as a baked string vs a parts record. This module reduces a tree to what is\n * actually painted: every drawable leaf, with its CUMULATIVE transform matrix\n * (resolved at a given time) and key visual attributes. Comparing those leaf\n * sets ignores representation and catches genuine visual differences.\n *\n * It is a deterministic function applied identically to both trees, so equal\n * inputs (in effect) yield equal output regardless of how each was encoded.\n *\n * Dependency-free on purpose (mirrors the applier's isolation). Scope: the node\n * types and transform forms the player-effects / heavy serializers emit. Color\n * animation is not interpolated — leaves are compared at keyframe instants where\n * sampled values are exact.\n */\n\ntype Mat = [number, number, number, number, number, number];\n\ninterface VmNode {\n type?: string;\n children?: Array<VmNode>;\n [attr: string]: any;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// MATRIX MATH (2x3 affine, SVG order: [a b c d e f])\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst IDENTITY: Mat = [1, 0, 0, 1, 0, 0];\n\nfunction mul(m: Mat, n: Mat): Mat {\n return [\n m[0] * n[0] + m[2] * n[1],\n m[1] * n[0] + m[3] * n[1],\n m[0] * n[2] + m[2] * n[3],\n m[1] * n[2] + m[3] * n[3],\n m[0] * n[4] + m[2] * n[5] + m[4],\n m[1] * n[4] + m[3] * n[5] + m[5],\n ];\n}\n\nconst translateM = (x: number, y: number): Mat => [1, 0, 0, 1, x, y];\nconst scaleM = (sx: number, sy: number): Mat => [sx, 0, 0, sy, 0, 0];\nfunction rotateM(deg: number): Mat {\n const r = (deg * Math.PI) / 180;\n return [Math.cos(r), Math.sin(r), -Math.sin(r), Math.cos(r), 0, 0];\n}\nconst skewXM = (deg: number): Mat => [1, 0, Math.tan((deg * Math.PI) / 180), 1, 0, 0];\nconst skewYM = (deg: number): Mat => [1, Math.tan((deg * Math.PI) / 180), 0, 1, 0, 0];\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// TRANSFORM EVALUATION\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Parses an SVG transform string (\"translate(..)rotate(..)…\") into a matrix. */\nfunction parseTransformString(s: string): Mat {\n let m = IDENTITY;\n const re = /(translate|rotate|scale|matrix|skewX|skewY)\\(([^)]*)\\)/g;\n let hit: RegExpExecArray | null;\n while ((hit = re.exec(s))) {\n const fn = hit[1];\n const a = hit[2].split(/[\\s,]+/).filter(Boolean).map(Number);\n if (fn === 'translate') m = mul(m, translateM(a[0] || 0, a[1] || 0));\n else if (fn === 'scale') m = mul(m, scaleM(a[0], a.length > 1 ? a[1] : a[0]));\n else if (fn === 'rotate') m = mul(m, rotateM(a[0]));\n else if (fn === 'skewX') m = mul(m, skewXM(a[0]));\n else if (fn === 'skewY') m = mul(m, skewYM(a[0]));\n else if (fn === 'matrix') m = mul(m, [a[0], a[1], a[2], a[3], a[4], a[5]]);\n }\n return m;\n}\n\ninterface Parts { translate?: [number, number]; rotate?: number; scale?: [number, number]; origin?: [number, number]; }\n\n/** Composes a `PxTransformParts` record (player canonical order, origin-pivoted). */\nfunction partsToMatrix(p: Parts): Mat {\n let m = IDENTITY;\n if (p.translate) m = mul(m, translateM(p.translate[0], p.translate[1]));\n const pivot = p.origin && (p.rotate !== undefined || p.scale);\n if (pivot) m = mul(m, translateM(p.origin![0], p.origin![1]));\n if (p.rotate !== undefined) m = mul(m, rotateM(p.rotate));\n if (p.scale) m = mul(m, scaleM(p.scale[0], p.scale[1]));\n if (pivot) m = mul(m, translateM(-p.origin![0], -p.origin![1]));\n return m;\n}\n\nfunction lerp(a: number, b: number, f: number): number { return a + (b - a) * f; }\n\n/** Linear-interpolates a parts record between keyframes at time `t` (ms). */\nfunction interpParts(kfs: Array<any>, t: number): Parts {\n if (!kfs.length) return {};\n if (t <= (kfs[0].time ?? 0)) return kfs[0].value || {};\n if (t >= (kfs[kfs.length - 1].time ?? 0)) return kfs[kfs.length - 1].value || {};\n\n let i = 0;\n while (i < kfs.length - 1 && (kfs[i + 1].time ?? 0) < t) i++;\n const a = kfs[i], b = kfs[i + 1];\n const f = (t - (a.time ?? 0)) / ((b.time ?? 0) - (a.time ?? 0) || 1);\n const va: Parts = a.value || {}, vb: Parts = b.value || {};\n\n const out: Parts = {};\n if (va.translate && vb.translate) out.translate = [lerp(va.translate[0], vb.translate[0], f), lerp(va.translate[1], vb.translate[1], f)];\n else out.translate = va.translate || vb.translate;\n if (va.rotate !== undefined && vb.rotate !== undefined) out.rotate = lerp(va.rotate, vb.rotate, f);\n else out.rotate = va.rotate ?? vb.rotate;\n if (va.scale && vb.scale) out.scale = [lerp(va.scale[0], vb.scale[0], f), lerp(va.scale[1], vb.scale[1], f)];\n else out.scale = va.scale || vb.scale;\n out.origin = va.origin || vb.origin;\n return out;\n}\n\n/** Resolves any transform-slot form (string | bare record | {value} | {keyframes}) to a matrix at `t`. */\nfunction evalTransformValue(v: any, t: number): Mat {\n if (v === undefined || v === null) return IDENTITY;\n if (typeof v === 'string') return parseTransformString(v);\n if (v.keyframes) return partsToMatrix(interpParts(v.keyframes, t));\n if (v.value) return partsToMatrix(v.value);\n if (typeof v === 'object' && !Array.isArray(v)) return partsToMatrix(v); // bare parts record\n return IDENTITY;\n}\n\n/** The node's own transform at `t`: animated slot wins over the static baseline. */\nfunction nodeMatrix(node: VmNode, t: number): Mat {\n if (node.animate && node.animate.transform) return evalTransformValue(node.animate.transform, t);\n if (node.transform !== undefined) return evalTransformValue(node.transform, t);\n return IDENTITY;\n}\n\n/** Linear-interpolates a scalar animation (e.g. opacity); falls back to static / default. */\nfunction evalScalar(animated: any, staticVal: any, fallback: number, t: number): number {\n if (animated && animated.keyframes && animated.keyframes.length) {\n const kfs = animated.keyframes;\n if (t <= (kfs[0].time ?? 0)) return kfs[0].value;\n if (t >= (kfs[kfs.length - 1].time ?? 0)) return kfs[kfs.length - 1].value;\n let i = 0;\n while (i < kfs.length - 1 && (kfs[i + 1].time ?? 0) < t) i++;\n const a = kfs[i], b = kfs[i + 1];\n const f = (t - (a.time ?? 0)) / ((b.time ?? 0) - (a.time ?? 0) || 1);\n return lerp(a.value, b.value, f);\n }\n return staticVal !== undefined ? Number(staticVal) : fallback;\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// FLATTEN → multiset of painted primitives\n// ─────────────────────────────────────────────────────────────────────────────\n\nconst CONTAINER_TYPES = new Set(['svg', 'g', 'symbol']);\nconst SKIP_TYPES = new Set(['defs', 'mask', 'clipPath', 'title']);\n\nfunction buildIdMap(node: VmNode, map: Map<string, VmNode>): void {\n if (typeof node.id === 'string') map.set(node.id, node);\n node.children?.forEach(c => buildIdMap(c, map));\n}\n\nfunction num(v: any): number { return v === undefined || v === null ? 0 : Number(v); }\n\n// 2-decimal rounding (sub-pixel). The heavy path bakes matrices into strings\n// rounded to ~4 decimals; comparing at 2 decimals avoids straddling a rounding\n// boundary against the applier's full-precision values, while staying visually exact.\nfunction round(n: number): number { return Math.round(n * 100) / 100 + 0; }\n\nfunction geomKey(node: VmNode): string {\n switch (node.type) {\n case 'rect': return num(node.width) + ',' + num(node.height) + ',' + num(node.x) + ',' + num(node.y);\n case 'ellipse': return num(node.rx) + ',' + num(node.ry) + ',' + num(node.cx) + ',' + num(node.cy);\n case 'circle': return num(node.r) + ',' + num(node.cx) + ',' + num(node.cy);\n case 'path': return String(node.d ?? '');\n default: return '';\n }\n}\n\nfunction describePrimitive(node: VmNode, m: Mat, t: number): string {\n const fill = node.fill ?? '';\n const stroke = node.stroke ?? '';\n const sw = node['stroke-width'] ?? node.strokeWidth ?? '';\n const opacity = round(evalScalar(node.animate?.opacity, node.opacity, 1, t));\n const masked = node.mask ? 1 : 0;\n const mat = m.map(round).join(',');\n return node.type + '|' + geomKey(node) + '|[' + mat + ']|f:' + fill + '|s:' + stroke + '|sw:' + (num(sw) || '') + '|o:' + opacity + '|m:' + masked;\n}\n\nfunction flatten(node: VmNode, parent: Mat, t: number, idMap: Map<string, VmNode>, out: Array<string>): void {\n const type = node.type || '';\n if (SKIP_TYPES.has(type)) return;\n\n const m = mul(parent, nodeMatrix(node, t));\n\n if (type === 'use') {\n const targetId = typeof node.href === 'string' ? node.href.replace(/^#/, '') : '';\n const target = idMap.get(targetId);\n const useM = mul(m, translateM(num(node.x), num(node.y)));\n if (target) flatten(target, useM, t, idMap, out);\n else out.push('UNRESOLVED_USE:#' + targetId);\n return;\n }\n\n if (CONTAINER_TYPES.has(type)) {\n node.children?.forEach(c => flatten(c, m, t, idMap, out));\n return;\n }\n\n out.push(describePrimitive(node, m, t));\n}\n\n\n// ─────────────────────────────────────────────────────────────────────────────\n// PUBLIC: sample times + visual model + comparison\n// ─────────────────────────────────────────────────────────────────────────────\n\n/** Every keyframe `time` found anywhere in the tree, plus 0. @internal */\nexport function collectSampleTimes(node: VmNode, into: Set<number>): void {\n into.add(0);\n const scanAnim = (anim: any) => {\n if (!anim || typeof anim !== 'object') return;\n for (const key of Object.keys(anim)) {\n const kfs = anim[key]?.keyframes;\n if (Array.isArray(kfs)) kfs.forEach(kf => into.add(kf.time ?? 0));\n }\n };\n scanAnim(node.animate);\n if (node.transform && typeof node.transform === 'object' && (node.transform as any).keyframes) {\n (node.transform as any).keyframes.forEach((kf: any) => into.add(kf.time ?? 0));\n }\n node.children?.forEach(c => collectSampleTimes(c, into));\n}\n\n/** Sorted, painted-primitive multiset for the tree at time `t`. @internal */\nexport function visualModelAt(root: VmNode, t: number): Array<string> {\n const idMap = new Map<string, VmNode>();\n buildIdMap(root, idMap);\n const out: Array<string> = [];\n flatten(root, IDENTITY, t, idMap, out);\n return out.sort();\n}\n\nexport interface EffectDiff {\n time: number;\n onlyInA: Array<string>;\n onlyInB: Array<string>;\n}\n\n/**\n * Compares two trees \"in effect\" across all keyframe instants found in either.\n * Returns one entry per time where the painted-primitive multisets differ.\n * @internal\n */\nexport function diffInEffect(a: VmNode, b: VmNode): Array<EffectDiff> {\n const times = new Set<number>();\n collectSampleTimes(a, times);\n collectSampleTimes(b, times);\n\n const diffs: Array<EffectDiff> = [];\n for (const t of Array.from(times).sort((x, y) => x - y)) {\n const ma = visualModelAt(a, t);\n const mb = visualModelAt(b, t);\n const onlyInA = subtractMultiset(ma, mb);\n const onlyInB = subtractMultiset(mb, ma);\n if (onlyInA.length || onlyInB.length) diffs.push({ time: t, onlyInA, onlyInB });\n }\n return diffs;\n}\n\n/** Items in `a` not matched one-for-one in `b` (multiset difference). */\nfunction subtractMultiset(a: Array<string>, b: Array<string>): Array<string> {\n const counts = new Map<string, number>();\n for (const x of b) counts.set(x, (counts.get(x) || 0) + 1);\n const extra: Array<string> = [];\n for (const x of a) {\n const c = counts.get(x) || 0;\n if (c > 0) counts.set(x, c - 1);\n else extra.push(x);\n }\n return extra;\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// THE renderer — the one place a `PxNode` tree becomes elements, for EVERY DOM player.\n//\n// The web player, the React component and the Vue component used to carry a renderer each.\n// Three copies drifted: React and Vue skipped sanitization, wrote camelCase names SVG ignores\n// (`maskType`, and in Vue every presentation attribute — gradients lost their colours), wrote\n// the `domType` escape key as a literal attribute (so `<feColorMatrix>` lost its `type` and the\n// filter painted its target black), and put CSS-only properties out as dead attributes.\n//\n// So every DECISION lives here, once:\n// - which tag (`type`, default `g`) and whether it is allowed at all;\n// - the `domType` escape key → the real `type` attribute;\n// - which keys are attributes (`toDomProps`) and what their values may be\n// (`sanitizeAttributeValue`);\n// - the NAME each attribute has in SVG (`camelCaseToKebabWordIfNeeded`, `className` → `class`);\n// - which properties are CSS-only and so go to `style`, plus the node's own `style` record;\n// - children, or the node's `textContent` when it has none.\n//\n// A framework supplies exactly one thing — `PxElementFactory`: given the finished spec, make\n// an element (DOM node / React element / Vue vnode). It decides nothing about the document.\n// Platform-neutral on purpose: no DOM here, so it also runs on a server.\n\nimport { PX_TEXT_CONTENT_ATTR } from '../format/PxAnimatorConstants';\nimport type { PxNode } from '../format/PxAnimatorTypes';\nimport { PxDiagnosticCode } from '../playback/PxDiagnosticCode';\nimport { createDiagnostics, PxDiagnosticKind, type PxDiagnostics } from '../playback/PxDiagnostics';\nimport { camelCaseToKebabWordIfNeeded } from '../util/PxAnimatorUtil';\nimport { PX_CSS_ONLY_STYLE_PROPS, PX_DISALLOWED_SVG_TAGS_LOWER, sanitizeAttributeValue, toDomProps } from '../util/PxNodeProps';\n\n\n/**\n * One element, fully decided. Everything a factory needs and nothing it has to think about.\n * @internal\n */\nexport interface PxElementSpec<T> {\n /** The SVG tag name. */\n tag: string;\n /** Attributes under the names SVG reads (`stroke-width`, `viewBox`, `class`), sanitized. */\n attrs: Record<string, string>;\n /** Inline style, camelCase property → value; `undefined` when there is none. */\n style: Record<string, string> | undefined;\n /** Rendered children, blocked ones already dropped. Empty when the node has none. */\n children: Array<T>;\n /** The node's text — set ONLY when `children` is empty (a line `<tspan>` can carry both,\n * and then the styled child spans are what renders). */\n text: string | undefined;\n /** The source node — for the node's `id` (refs) and nothing about rendering. */\n node: PxNode;\n /** True for the tree's root `<svg>`. */\n isRoot: boolean;\n /** Position among its siblings — a stable list key for frameworks that want one. */\n index: number;\n}\n\n/**\n * The ONE thing a framework supplies: make an element from a finished spec.\n * @internal\n */\nexport type PxElementFactory<T> = (spec: PxElementSpec<T>) => T;\n\n/** The attribute SVG calls `class`; the wire and the DOM property call it `className`. */\nconst CLASS_NAME_KEY = 'className';\nconst CLASS_ATTR = 'class';\n\n/** The escape key for an element whose own `type` ATTRIBUTE collides with the node-tag key —\n * `feColorMatrix`, `feTurbulence`, `feFunc*` (written by the editor at one choke point). */\nconst DOM_TYPE_KEY = 'domType';\nconst TYPE_ATTR = 'type';\n\nconst DEFAULT_TAG = 'g';\n\n\n/**\n * Renders a `PxNode` tree through `factory`. Returns `null` for a missing node or a blocked\n * root tag; blocked descendants are dropped and reported.\n * @internal\n */\nexport function renderPxTree<T>(node: PxNode | undefined, factory: PxElementFactory<T>, diag?: PxDiagnostics): T | null {\n return node ? renderOne(node, factory, diag, true, 0) : null;\n}\n\nfunction renderOne<T>(node: PxNode, factory: PxElementFactory<T>, diag: PxDiagnostics | undefined, isRoot: boolean, index: number): T | null {\n const { type, children, style, ...props } = node;\n const tag = type || DEFAULT_TAG;\n\n if (PX_DISALLOWED_SVG_TAGS_LOWER.has(tag.toLowerCase())) {\n // `document`: a blocked tag is content the FILE asked for, so the file is what changes.\n (diag ?? createDiagnostics(undefined, '[PxAnimator]')).warn(PxDiagnosticKind.document, PxDiagnosticCode.blockedTag, tag);\n return null;\n }\n\n // `type` is reserved for the tag and `toDomProps` drops it, so the escaped value is lifted\n // out first and written back as the real attribute below. Without it the primitive is lost\n // and an EMPTY `<filter>` paints its target transparent black.\n const domType = props[DOM_TYPE_KEY];\n if (domType !== undefined) delete props[DOM_TYPE_KEY];\n\n const attrs: Record<string, string> = {};\n let inlineStyle: Record<string, string> | undefined;\n\n const domProps = toDomProps(props);\n for (const propName of Object.keys(domProps)) {\n // `undefined` means \"do not emit\" (whitelist miss, blocked dangerous value, …) — a\n // writer would otherwise coerce it to the literal string \"undefined\".\n const sanitized = sanitizeAttributeValue(propName, domProps[propName]);\n if (sanitized === undefined) continue;\n\n // CSS-only properties (mix-blend-mode, isolation) are not SVG presentation attributes;\n // as attributes the browser ignores them, so they go through `style`.\n if (PX_CSS_ONLY_STYLE_PROPS.has(propName)) {\n (inlineStyle ??= {})[propName] = String(sanitized);\n continue;\n }\n attrs[propName === CLASS_NAME_KEY ? CLASS_ATTR : camelCaseToKebabWordIfNeeded(propName)] = sanitized;\n }\n if (domType !== undefined) attrs[TYPE_ATTR] = String(domType);\n\n // The node's own `style` record — declarations, not attributes. Written after the CSS-only\n // properties so an explicit declaration wins over the same property given as an attribute.\n if (style) {\n for (const styleProp of Object.keys(style)) (inlineStyle ??= {})[styleProp] = String(style[styleProp]);\n }\n\n const rendered: Array<T> = [];\n if (children) {\n children.forEach((child, i) => {\n const el = renderOne(child, factory, diag, false, i);\n if (el !== null) rendered.push(el);\n });\n }\n\n const ownText = props[PX_TEXT_CONTENT_ATTR];\n const text = !rendered.length && typeof ownText === 'string' && ownText ? ownText : undefined;\n\n return factory({ tag, attrs, style: inlineStyle, children: rendered, text, node, isRoot, index });\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// Scroll-timeline PROGRESS MATH — pure functions, no DOM. This is the reference\n// implementation for `animator.timelineSource: 'scroll'` (the player-measured path — `timeline.engine: 'js'`, and the fallback of `auto`,\n// both engines): the DOM driver in svg-animator-web measures rects/offsets and calls in\n// here; the editor's design doc (`app/src/svgeditor/animation/scroll-timeline.design.md`\n// §4) documents the same formulas — keep them in sync.\n\nimport { clamp, PX_DEFAULT_DURATION_MS } from '../util/PxAnimatorUtil';\nimport type { PxAnimatorConfig, PxScroll, PxScrollPhase, PxScrollRangePoint } from '../format/PxAnimatorTypes';\n\n\n/** Is this document scroll-driven? (`animator.timelineSource === 'scroll'`) @internal */\nexport function isScrollTimeline(config: PxAnimatorConfig | undefined): boolean {\n return config?.timelineSource === 'scroll';\n}\n\n/**\n * The seek-space length (ms) a scroll progress of 1 maps to: duration × finite\n * iterations. `'infinite'` is meaningless on a finite progress timeline (see design doc\n * D4) — treated as 1 with the read-side warning left to the consumer.\n * @internal\n */\nexport function scrollTotalDurationMs(config: PxAnimatorConfig | undefined): number {\n const duration = (typeof config?.duration === 'number' && config.duration > 0)\n ? config.duration : PX_DEFAULT_DURATION_MS;\n const iterations = (typeof config?.iterations === 'number' && config.iterations > 0)\n ? config.iterations : 1;\n return duration * iterations;\n}\n\n/**\n * A named phase's interval in `u`-space.\n *\n * `u` is the subject's \"journey distance\": with `sTop` = subject's leading edge in\n * scrollport coordinates, `u = vpSize − sTop` — 0 exactly when the subject is about to\n * enter (leading edge at the scrollport's trailing edge), growing as the user scrolls.\n * The `min`/`max` pairs make every formula valid BOTH for a subject smaller than the\n * scrollport and one larger than it (where \"fully visible\" flips to \"covers the\n * scrollport\") — the same case split CSS specifies for its named timeline ranges.\n * @internal\n */\nexport function scrollPhaseInterval(\n phase: PxScrollPhase, subjectSize: number, scrollportSize: number\n): [number, number] {\n const s = subjectSize, vp = scrollportSize;\n switch (phase) {\n case 'cover': return [0, s + vp];\n case 'entry': return [0, Math.min(s, vp)];\n case 'contain': return [Math.min(s, vp), Math.max(s, vp)];\n case 'exit': return [Math.max(s, vp), s + vp];\n case 'entry-crossing': return [0, s];\n case 'exit-crossing': return [vp, s + vp];\n }\n}\n\nconst DEFAULT_PHASE: PxScrollPhase = 'cover';\n\n/** One range endpoint resolved to a `u`-space value. */\nfunction resolveRangePointU(\n point: PxScrollRangePoint | undefined, defaultFraction: number,\n subjectSize: number, scrollportSize: number\n): number {\n const [u0, u1] = scrollPhaseInterval(point?.phase ?? DEFAULT_PHASE, subjectSize, scrollportSize);\n const fraction = typeof point?.fraction === 'number' ? point.fraction : defaultFraction;\n return u0 + fraction * (u1 - u0);\n}\n\n/**\n * `kind: 'view'` progress ∈ [0, 1]: where the subject's journey sits within the\n * configured range.\n *\n * @param subjectStart subject's leading edge in scrollport coordinates\n * (`subjectRect.top − scrollportRect.top` on the resolved axis)\n * @param subjectSize subject size on the axis\n * @param scrollportSize scrollport size on the axis\n *\n * A degenerate/inverted range (uStart ≥ uEnd — e.g. zero-size subject with an `entry`\n * range) reports 1 once the point is passed, 0 before — never NaN.\n * @internal\n */\nexport function scrollViewProgress(\n subjectStart: number, subjectSize: number, scrollportSize: number,\n range: PxScroll['range'] | undefined\n): number {\n const u = scrollportSize - subjectStart;\n const uStart = resolveRangePointU(range?.start, 0, subjectSize, scrollportSize);\n const uEnd = resolveRangePointU(range?.end, 1, subjectSize, scrollportSize);\n if (uEnd <= uStart) return u >= uEnd ? 1 : 0;\n return clamp((u - uStart) / (uEnd - uStart), 0, 1);\n}\n\n/**\n * `kind: 'scroll'` progress ∈ [0, 1]: the scroller's offset ratio mapped through the\n * range (phases don't exist here — `fraction` is of the total scroll range).\n *\n * `maxOffset === 0` (nothing to scroll) reports 1, matching the CSS spec's rule that a\n * zero-length timeline is at 100%.\n * @internal\n */\nexport function scrollOffsetProgress(\n offset: number, maxOffset: number,\n range: PxScroll['range'] | undefined\n): number {\n const raw = maxOffset > 0 ? clamp(offset / maxOffset, 0, 1) : 1;\n const start = typeof range?.start?.fraction === 'number' ? range.start.fraction : 0;\n const end = typeof range?.end?.fraction === 'number' ? range.end.fraction : 1;\n if (end <= start) return raw >= end ? 1 : 0;\n return clamp((raw - start) / (end - start), 0, 1);\n}\n\n/**\n * Resolve a logical axis to a physical one. `block`/`inline` are writing-mode relative:\n * in horizontal writing (`horizontal-tb`, the default) block flows vertically; in\n * vertical writing modes it flows horizontally.\n * @internal\n */\nexport function scrollResolveAxis(\n axis: PxScroll['axis'] | undefined, writingMode: string | undefined\n): 'x' | 'y' {\n const a = axis ?? 'block';\n if (a === 'x' || a === 'y') return a;\n const vertical = !!writingMode && writingMode.startsWith('vertical');\n if (a === 'inline') return vertical ? 'y' : 'x';\n return vertical ? 'x' : 'y'; // block\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ============================================================================\n// THE PLAYER'S WIRE-KEY INVENTORY — every field the player schema can carry, as a canonical\n// path, sorted. The library half of the editor's ship-guard (task 5.1).\n//\n// Why a committed list at all: A RENAME IS LENGTH-NEUTRAL. One name out, one in, same count,\n// green suite — which is how three renames shipped with no compat read, the last one wiping\n// the geometry of every shape-preset path on open. Diffing the list names the key that left.\n//\n// It lives HERE, not only in the editor, because the library ships on its own: a player\n// released with a key quietly gone breaks every document using it, editor or not.\n//\n// CANONICAL PATHS — the same rule as the editor's `collectSchemaFields`, so the two lists speak\n// one dialect: each object schema is enumerated ONCE, at the first path it is reached by\n// (breadth-first); `[]` marks an array item, `{*}` a record value or open-object value, `|i` /\n// `|tag` a union member. Without the once-only rule every recursive `children` re-lists the\n// whole node schema and the inventory explodes into thousands of paths that say nothing new.\n// ============================================================================\n\nimport { describeSchema, type PxSchema } from '../schema/PxSchema';\n\n/** Every field identity `root` can carry, as canonical paths, sorted. @internal */\nexport function schemaFieldUniverse(root: PxSchema<any, any>): Array<string> {\n const fields = new Set<string>();\n const enumerated = new Set<unknown>();\n const queue: Array<{ schema: PxSchema<any, any>; path: string }> = [{ schema: root, path: '' }];\n\n while (queue.length) {\n const { schema, path } = queue.shift()!;\n const d = describeSchema(schema);\n switch (d.kind) {\n case 'shape': {\n if (enumerated.has(schema)) break; // already listed under its canonical path\n enumerated.add(schema);\n for (const key of Object.keys(d.shape)) {\n const id = path ? path + '.' + key : key;\n fields.add(id);\n queue.push({ schema: d.shape[key], path: id });\n }\n if (d.openValue) queue.push({ schema: d.openValue, path: path + '{*}' });\n break;\n }\n case 'optional': queue.push({ schema: d.inner, path }); break;\n case 'array': queue.push({ schema: d.item, path: path + '[]' }); break;\n case 'lazy': queue.push({ schema: d.resolved, path }); break;\n case 'record': queue.push({ schema: d.value, path: path + '{*}' }); break;\n case 'union':\n d.members.forEach((member, i) => queue.push({ schema: member, path: path + '|' + i }));\n break;\n case 'discriminatedUnion':\n for (const member of d.members) queue.push({ schema: member, path: path + '|' + discriminantOf(member, d.key) });\n break;\n default: break; // tuple / leaf: no named fields\n }\n }\n return [...fields].sort();\n}\n\n/** The literal a discriminated-union member carries at its discriminant, unwrapping an\n * OPTIONAL discriminant (`type?: 'time'`). `?` when it carries none. */\nfunction discriminantOf(member: PxSchema<any, any>, key: string): string {\n const d = describeSchema(member);\n if (d.kind !== 'shape') return '?';\n const keySchema = d.shape[key];\n if (keySchema === undefined) return '?';\n const kd = describeSchema(keySchema);\n return String((kd.kind === 'optional' ? kd.inner : keySchema)._default);\n}\n","/*---------------------------------------------------------------------------------------\n * Copyright (c) Pixodesk LTD.\n * Licensed under the MIT License. See the LICENSE file in the project root for details.\n *---------------------------------------------------------------------------------------*/\n\n// ============================================================================\n// SCHEMA RELEASES (tasks 5.3 / 5.4) — the decision \"does this release move the schema\n// version, and which part?\" made by the DIFF, not by a person, plus the dated log of every\n// release kept beside the step table (`schema-releases.player.json`).\n//\n// Pure functions, so the release CLI (`scripts/schema-release.mjs`) and the guard test run the\n// SAME rule — a CLI and a test that each re-implement it are two rules that drift apart.\n//\n// The diff is over the canonical field inventory (`schemaFieldUniverse`): key paths only, so\n// descriptions and comments can never make two identical schemas look different.\n// ============================================================================\n\nimport { parseWireVersion, PxWireStepKind, type PxWireVersionStep } from './PxWireVersion';\n\n/** One entry of the release log — what shipped, when, and what it changed. @internal */\nexport interface SchemaReleaseRecord {\n readonly version: string;\n /** ISO date, `YYYY-MM-DD`. */\n readonly date: string;\n /** The first release: nothing to compare it against. */\n readonly baseline?: boolean;\n readonly added: ReadonlyArray<string>;\n readonly removed: ReadonlyArray<string>;\n readonly note?: string;\n}\n\n/** Keys that appeared and keys that left between two inventories, each sorted. @internal */\nexport function diffFieldUniverse(\n previous: ReadonlyArray<string>, current: ReadonlyArray<string>,\n): { added: Array<string>; removed: Array<string> } {\n const prev = new Set(previous);\n const cur = new Set(current);\n return {\n added: current.filter(k => !prev.has(k)).sort(),\n removed: previous.filter(k => !cur.has(k)).sort(),\n };\n}\n\n/** What a release must do about the version. `refuse` set means: do not release as-is. @internal */\nexport interface SchemaReleasePlan {\n /** Did any key appear or leave since the last release? */\n readonly changed: boolean;\n /** Which kind of step the change requires — a removal is never additive. */\n readonly requiredKind?: PxWireStepKind;\n /** The version this release must carry. */\n readonly requiredVersion?: string;\n readonly refuse?: string;\n}\n\n/**\n * THE BUMP RULE. A key change requires `b + 1` and a step that explains it; no key change\n * requires nothing. The rule never picks the number by taste — the inventory diff does.\n * @internal\n */\nexport function planSchemaRelease(p: {\n readonly added: ReadonlyArray<string>;\n readonly removed: ReadonlyArray<string>;\n /** `PX_WIRE_SCHEMA_VERSION` — what the source says now. */\n readonly declared: string;\n /** The version of the last release record. */\n readonly lastReleased: string;\n readonly steps: ReadonlyArray<PxWireVersionStep>;\n}): SchemaReleasePlan {\n const changed = p.added.length > 0 || p.removed.length > 0;\n const last = parseWireVersion(p.lastReleased);\n const declared = parseWireVersion(p.declared);\n if (!last || !declared) return { changed, refuse: 'Unparseable version: ' + p.lastReleased + ' / ' + p.declared + '.' };\n\n const requiredVersion = last.a + '.' + (last.b + 1);\n if (!changed) {\n // Same keys: nothing to bump. A bump anyway is a SEMANTIC change and still needs its step.\n if (declared.b === last.b && declared.a === last.a) return { changed };\n const step = p.steps.find(s => s.to === p.declared);\n return step ? { changed, requiredVersion: p.declared }\n : { changed, refuse: 'The version moved to ' + p.declared + ' with no key change and no step explaining it.' };\n }\n\n const requiredKind = p.removed.length ? PxWireStepKind.converted : PxWireStepKind.additive;\n const summary = p.added.length + ' key(s) added, ' + p.removed.length + ' removed';\n if (declared.a !== last.a || declared.b !== last.b + 1) {\n return {\n changed, requiredKind, requiredVersion,\n refuse: 'The player schema changed (' + summary + ') but PX_WIRE_SCHEMA_VERSION is '\n + p.declared + '. Set it to ' + requiredVersion + ' and add the PX_WIRE_STEPS entry.',\n };\n }\n const step = p.steps.find(s => s.to === p.declared);\n if (!step) {\n return { changed, requiredKind, requiredVersion, refuse: 'No PX_WIRE_STEPS entry reaches ' + p.declared + '.' };\n }\n if (requiredKind === PxWireStepKind.converted && step.kind !== PxWireStepKind.converted) {\n return {\n changed, requiredKind, requiredVersion,\n refuse: 'Keys were REMOVED (' + p.removed.join(', ') + '), which is never additive — the '\n + p.declared + ' step must be `converted`, with an up().',\n };\n }\n return { changed, requiredKind, requiredVersion };\n}\n\n/**\n * Everything wrong with the release log, as sentences — empty when it is consistent. The log\n * must start at the baseline, move strictly forward, END at the version the source declares,\n * and every release after the baseline must have the step that explains it.\n * @internal\n */\nexport function releaseLogProblems(\n releases: ReadonlyArray<SchemaReleaseRecord>, steps: ReadonlyArray<PxWireVersionStep>,\n declared: string, baseline: string,\n): Array<string> {\n const problems: Array<string> = [];\n if (!releases.length) return ['The release log is empty.'];\n if (!releases[0].baseline || releases[0].version !== baseline) {\n problems.push('The first release must be the baseline ' + baseline + '.');\n }\n for (let i = 1; i < releases.length; i++) {\n const prev = parseWireVersion(releases[i - 1].version);\n const cur = parseWireVersion(releases[i].version);\n if (!prev || !cur || cur.a !== prev.a || cur.b !== prev.b + 1) {\n problems.push('Release ' + releases[i].version + ' does not follow ' + releases[i - 1].version + ' by one `b` step.');\n }\n if (releases[i].date < releases[i - 1].date) problems.push('Release ' + releases[i].version + ' is dated before its predecessor.');\n const step = steps.find(s => s.to === releases[i].version);\n if (!step) problems.push('Release ' + releases[i].version + ' has no PX_WIRE_STEPS entry.');\n else if (releases[i].removed.length && step.kind !== PxWireStepKind.converted) {\n problems.push('Release ' + releases[i].version + ' removed keys, but its step is not `converted`.');\n }\n }\n const latest = releases[releases.length - 1].version;\n if (latest !== declared) {\n problems.push('PX_WIRE_SCHEMA_VERSION is ' + declared + ' but the last release record is ' + latest\n + ' — a bump needs its changelog entry (run scripts/schema-release.mjs --apply).');\n }\n return problems;\n}\n"],"mappings":"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","names":["_a","step"]}
|