@vvfx/dsl-ir 0.0.1-beta.10 → 0.0.1-beta.12

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/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  * Description: Editable DSL and Animation IR bidirectional converter
4
4
  * Author: Ant Group CO., Ltd.
5
5
  * Contributors: 澄弈
6
- * Version: v0.0.1-beta.10
6
+ * Version: v0.0.1-beta.12
7
7
  */
8
8
 
9
9
  'use strict';
@@ -311,6 +311,7 @@ var DSL_CODE_LABELS = {
311
311
  R002: '缺少 meta',
312
312
  R003: '缺少 layers',
313
313
  R004: 'layers 不是数组',
314
+ R005: 'meta 不是对象',
314
315
  M001: 'meta 必填字段缺失',
315
316
  M002a: 'meta.name 类型错误',
316
317
  M002b: 'meta.duration 类型错误',
@@ -398,7 +399,7 @@ var DSL_CODE_LABELS = {
398
399
  A011: '文字层缺 text 块',
399
400
  A012: 'text 非对象',
400
401
  A013: 'text.content 缺失/非字符串',
401
- A014: 'text.size 缺失/非数值',
402
+ A014: 'text.size 缺失/非法',
402
403
  A015: 'text.color 缺失/非字符串',
403
404
  A016: '图片层有不必要的 text',
404
405
  A017: '文字层有不必要的 asset',
@@ -411,6 +412,7 @@ var DSL_CODE_LABELS = {
411
412
  A024: '非粒子层有不必要的 particle 块',
412
413
  A025: '粒子层有不必要的 asset',
413
414
  A026: '粒子层有不必要的 text',
415
+ A027: 'line_height 必须 > 0',
414
416
  PT001: 'particle.emission 缺失或不是对象',
415
417
  PT002: 'rateOverTime 非法',
416
418
  PT003: 'bursts 不是数组',
@@ -821,8 +823,7 @@ var EPSILON = 1e-9;
821
823
  * Validate DSL and return the public validation result.
822
824
  * Strings are interpreted as raw JSON. File-system paths are intentionally not
823
825
  * accepted so the same validator works unchanged in Node.js and browsers.
824
- */ function validateDSL(dsl, options) {
825
- if (options === void 0) options = {};
826
+ */ function validateDSL(dsl) {
826
827
  var coerced = coerceInput(dsl);
827
828
  if ('error' in coerced) {
828
829
  return createInputErrorResult(coerced.error);
@@ -834,7 +835,7 @@ var EPSILON = 1e-9;
834
835
  var warnings = report.issues.filter(function(issue) {
835
836
  return issue.severity === 'WARNING';
836
837
  }).map(convertToValidationIssue);
837
- var hasFailed = errors.length > 0 || options.strict === true && warnings.length > 0;
838
+ var hasFailed = errors.length > 0;
838
839
  return {
839
840
  result: hasFailed ? 'fail' : 'pass',
840
841
  summary: errors.length + " errors, " + warnings.length + " warnings",
@@ -948,6 +949,8 @@ function checkRoot(dsl, context) {
948
949
  }
949
950
  if (!hasValue(dsl, 'meta')) {
950
951
  addIssue(context, 'ERROR', 'R002', "missing 'meta' object", 'meta');
952
+ } else if (!isRecord$1(dsl['meta'])) {
953
+ addIssue(context, 'ERROR', 'R005', 'meta must be an object', 'meta');
951
954
  }
952
955
  if (!hasValue(dsl, 'layers')) {
953
956
  addIssue(context, 'ERROR', 'R003', "missing 'layers' array", 'layers');
@@ -978,20 +981,20 @@ function checkMeta(dsl, context) {
978
981
  ],
979
982
  [
980
983
  'fps',
981
- 'integer',
982
- isIntLike,
984
+ 'number',
985
+ isNumber,
983
986
  'M002c'
984
987
  ],
985
988
  [
986
989
  'width',
987
- 'integer',
988
- isIntLike,
990
+ 'number',
991
+ isNumber,
989
992
  'M002d'
990
993
  ],
991
994
  [
992
995
  'height',
993
- 'integer',
994
- isIntLike,
996
+ 'number',
997
+ isNumber,
995
998
  'M002e'
996
999
  ],
997
1000
  [
@@ -1023,13 +1026,13 @@ function checkMeta(dsl, context) {
1023
1026
  var width = meta['width'];
1024
1027
  var height = meta['height'];
1025
1028
  var duration = meta['duration'];
1026
- if (isIntLike(fps) && fps <= 0) {
1029
+ if (isNumber(fps) && fps <= 0) {
1027
1030
  addIssue(context, 'ERROR', 'M005', "meta.fps is " + formatNumber(fps) + ", must be > 0", 'meta.fps');
1028
1031
  }
1029
- if (isIntLike(width) && width <= 0) {
1032
+ if (isNumber(width) && width <= 0) {
1030
1033
  addIssue(context, 'ERROR', 'M006', "meta.width is " + formatNumber(width) + ", must be > 0", 'meta.width');
1031
1034
  }
1032
- if (isIntLike(height) && height <= 0) {
1035
+ if (isNumber(height) && height <= 0) {
1033
1036
  addIssue(context, 'ERROR', 'M006', "meta.height is " + formatNumber(height) + ", must be > 0", 'meta.height');
1034
1037
  }
1035
1038
  if (isNumber(duration)) {
@@ -1468,8 +1471,8 @@ function checkLayerContent(entry, context) {
1468
1471
  addIssue(context, 'ERROR', code, "text." + field + " is " + describeValueType(value) + ", expected a string", path + ".text." + field, layerId);
1469
1472
  }
1470
1473
  }
1471
- if (isNumber(text['size']) && text['size'] < 0) {
1472
- addIssue(context, 'ERROR', 'A014', "text.size is " + formatNumber(text['size']) + ", expected a number greater than or equal to 0", "" + path + ".text.size", layerId);
1474
+ if (isNumber(text['size']) && text['size'] <= 0) {
1475
+ addIssue(context, 'ERROR', 'A014', "text.size is " + formatNumber(text['size']) + ", expected a number greater than 0", "" + path + ".text.size", layerId);
1473
1476
  }
1474
1477
  if (typeof text['color'] === 'string' && !isValidHexColor(text['color'])) {
1475
1478
  addIssue(context, 'ERROR', 'A015', "text.color " + formatValue(text['color']) + " is not a supported hex color", "" + path + ".text.color", layerId);
@@ -1480,6 +1483,9 @@ function checkLayerContent(entry, context) {
1480
1483
  if (hasValue(text, 'align') && text['align'] !== 'left' && text['align'] !== 'center' && text['align'] !== 'right') {
1481
1484
  addIssue(context, 'ERROR', 'A019', 'text.align must be left, center, or right', "" + path + ".text.align", layerId);
1482
1485
  }
1486
+ if (isNumber(text['line_height']) && text['line_height'] <= 0) {
1487
+ addIssue(context, 'ERROR', 'A027', 'text.line_height must be greater than 0', "" + path + ".text.line_height", layerId);
1488
+ }
1483
1489
  for(var _i1 = 0, _iter1 = [
1484
1490
  'line_height',
1485
1491
  'letter_spacing'
@@ -1654,9 +1660,6 @@ function hasValue(object, key) {
1654
1660
  function isNumber(value) {
1655
1661
  return typeof value === 'number' && Number.isFinite(value);
1656
1662
  }
1657
- function isIntLike(value) {
1658
- return isNumber(value) && Number.isInteger(value);
1659
- }
1660
1663
  function isAbsoluteResourceUrl(value) {
1661
1664
  try {
1662
1665
  new URL(value);
@@ -1931,10 +1934,16 @@ function convertIRLayersToDSL(baselineComposition, assets, frameRate) {
1931
1934
  var layerPath = "$.compositions[id=" + JSON.stringify(baselineComposition.id) + "].layers[" + layerIndex + "]";
1932
1935
  var convertedLayer = convertIRLayerToDSL(layer, layerId, imageAssetById, fontAssetById, frameRate, layerPath, diagnostics);
1933
1936
  convertedLayer.position = withPositionZ(convertedLayer.position, layerIndex);
1934
- layerMapping.layers[layerId] = {
1935
- irCompositionId: baselineComposition.id,
1936
- irLayerId: layer.id
1937
- };
1937
+ // Define an own data property even for IDs such as __proto__.
1938
+ Object.defineProperty(layerMapping.layers, layerId, {
1939
+ value: {
1940
+ irCompositionId: baselineComposition.id,
1941
+ irLayerId: layer.id
1942
+ },
1943
+ enumerable: true,
1944
+ writable: true,
1945
+ configurable: true
1946
+ });
1938
1947
  dslLayerByIRId.set(layer.id, convertedLayer);
1939
1948
  collectDiagnostics(layer, layerId, layerPath, diagnostics);
1940
1949
  if (convertedLayer.type === 'null' && isVisualLayerWithoutAsset(layer, convertedLayer.asset)) {
@@ -1956,8 +1965,8 @@ function convertIRLayersToDSL(baselineComposition, assets, frameRate) {
1956
1965
  };
1957
1966
  }
1958
1967
  function convertIRLayerToDSL(layer, layerId, imageAssetById, fontAssetById, frameRate, layerPath, diagnostics) {
1959
- var _layer_parentId, _layer_name, _layer_name1;
1960
- var _layer_transform, _layer_transform1, _layer_transform2, _layer_transform3;
1968
+ var _layer_parentId, _layer_name, _layer_name1, _ref;
1969
+ var _layer_transform, _layer_transform1, _layer_transform_anchor, _layer_transform2, _layer_transform3;
1961
1970
  var size = resolveLayerSize(layer);
1962
1971
  var asset = convertIRAssetToDSL(layer, imageAssetById);
1963
1972
  var layerType = mapLayerType$1(layer, asset);
@@ -1965,7 +1974,6 @@ function convertIRLayerToDSL(layer, layerId, imageAssetById, fontAssetById, fram
1965
1974
  var convertedLayer = {
1966
1975
  id: layerId,
1967
1976
  name: (_layer_name = layer.name) != null ? _layer_name : layer.id,
1968
- type: layerType,
1969
1977
  width: size.width,
1970
1978
  height: size.height,
1971
1979
  description: {
@@ -1978,22 +1986,36 @@ function convertIRLayerToDSL(layer, layerId, imageAssetById, fontAssetById, fram
1978
1986
  layer.time.outFrame / frameRate
1979
1987
  ],
1980
1988
  position: convertIRVec3PropertyToDSL((_layer_transform = layer.transform) == null ? void 0 : _layer_transform.position, TRANSFORM_FALLBACK.position, frameRate, true),
1981
- rotation: convertIRLayerRotationToDSL(layer, frameRate),
1989
+ rotation: convertIRLayerRotationToDSL(layer, frameRate, layerPath, diagnostics),
1982
1990
  scale: convertIRVec3PropertyToDSL((_layer_transform1 = layer.transform) == null ? void 0 : _layer_transform1.scale, TRANSFORM_FALLBACK.scale, frameRate, false),
1983
- anchor: convertIRVec3PropertyToDSL((_layer_transform2 = layer.transform) == null ? void 0 : _layer_transform2.anchor, TRANSFORM_FALLBACK.anchor, frameRate, false),
1991
+ anchor: {
1992
+ a: false,
1993
+ value: [].concat((_ref = (_layer_transform2 = layer.transform) == null ? void 0 : (_layer_transform_anchor = _layer_transform2.anchor) == null ? void 0 : _layer_transform_anchor.value) != null ? _ref : TRANSFORM_FALLBACK.anchor)
1994
+ },
1984
1995
  opacity: convertIRNumberPropertyToDSL((_layer_transform3 = layer.transform) == null ? void 0 : _layer_transform3.opacity, TRANSFORM_FALLBACK.opacity, frameRate),
1985
1996
  children: []
1986
1997
  };
1987
1998
  if (layerType === 'layer' && asset != null) {
1988
- convertedLayer.asset = asset;
1999
+ return _extends({}, convertedLayer, {
2000
+ type: 'layer',
2001
+ asset: asset
2002
+ });
1989
2003
  }
1990
2004
  if (layer.type === 'text') {
1991
- convertedLayer.text = convertIRTextToDSL(layer, fontAssetById);
2005
+ return _extends({}, convertedLayer, {
2006
+ type: 'text',
2007
+ text: convertIRTextToDSL(layer, fontAssetById)
2008
+ });
1992
2009
  }
1993
2010
  if (layer.type === 'particle') {
1994
- convertedLayer.particle = convertIRParticleToDSL(layer, frameRate, layerPath, diagnostics);
2011
+ return _extends({}, convertedLayer, {
2012
+ type: 'particle',
2013
+ particle: convertIRParticleToDSL(layer, frameRate, layerPath, diagnostics)
2014
+ });
1995
2015
  }
1996
- return convertedLayer;
2016
+ return _extends({}, convertedLayer, {
2017
+ type: 'null'
2018
+ });
1997
2019
  }
1998
2020
  function mapLayerType$1(layer, asset) {
1999
2021
  if (layer.type === 'particle') {
@@ -2056,20 +2078,20 @@ function convertIRTextToDSL(layer, fontAssetById) {
2056
2078
  letter_spacing: text.letterSpacing
2057
2079
  });
2058
2080
  }
2059
- function convertIRLayerRotationToDSL(layer, frameRate) {
2081
+ function convertIRLayerRotationToDSL(layer, frameRate, path, diagnostics) {
2060
2082
  var _layer_transform;
2061
2083
  var rotation = (_layer_transform = layer.transform) == null ? void 0 : _layer_transform.rotation;
2062
2084
  if (rotation == null) {
2063
2085
  return convertIRVec3PropertyToDSL(undefined, TRANSFORM_FALLBACK.rotation, frameRate, false);
2064
2086
  }
2065
- return convertIRRotationToDSL(rotation, frameRate);
2087
+ return convertIRRotationToDSL(rotation, frameRate, path, layer.id, diagnostics);
2066
2088
  }
2067
2089
  /**
2068
2090
  * Merge per-axis rotation into the DSL's 3D vector track.
2069
2091
  *
2070
2092
  * Aligned channels retain their shared easing. Independent axes fall back to
2071
2093
  * a union-of-keyframe-times projection.
2072
- */ function convertIRRotationToDSL(rotation, frameRate) {
2094
+ */ function convertIRRotationToDSL(rotation, frameRate, path, layerId, diagnostics) {
2073
2095
  var rotationChannels = [
2074
2096
  rotation.x,
2075
2097
  rotation.y,
@@ -2110,6 +2132,14 @@ function convertIRLayerRotationToDSL(layer, frameRate) {
2110
2132
  });
2111
2133
  return convertIRKeyframesToDSLProperty(keyframes, TRANSFORM_FALLBACK.rotation, frameRate, false);
2112
2134
  }
2135
+ diagnostics.push({
2136
+ code: 'ROTATION_CHANNELS_APPROXIMATED',
2137
+ severity: 'warning',
2138
+ action: 'approximated',
2139
+ path: "" + path + ".transform.rotation",
2140
+ sourceId: layerId,
2141
+ message: 'Independent rotation channels are sampled at their combined keyframe times with linear interpolation; ' + 'the original curves remain in preserved data until rotation is edited.'
2142
+ });
2113
2143
  var keyframeFrameSet = new Set();
2114
2144
  for(var _iterator = _create_for_of_iterator_helper_loose(rotationChannels), _step; !(_step = _iterator()).done;){
2115
2145
  var channel = _step.value;
@@ -2124,15 +2154,21 @@ function convertIRLayerRotationToDSL(layer, frameRate) {
2124
2154
  var sortedFrames = Array.from(keyframeFrameSet).sort(function(leftFrame, rightFrame) {
2125
2155
  return leftFrame - rightFrame;
2126
2156
  });
2127
- var keyframes1 = sortedFrames.map(function(frame) {
2128
- return {
2157
+ var keyframes1 = sortedFrames.map(function(frame, index) {
2158
+ return _extends({
2129
2159
  frame: frame,
2130
2160
  value: [
2131
2161
  readNumberAtFrame(rotation.x, frame, 0),
2132
2162
  readNumberAtFrame(rotation.y, frame, 0),
2133
2163
  readNumberAtFrame(rotation.z, frame, 0)
2134
2164
  ]
2135
- };
2165
+ }, index + 1 < sortedFrames.length && {
2166
+ endValue: [
2167
+ readNumberAtFrame(rotation.x, sortedFrames[index + 1], 0, true),
2168
+ readNumberAtFrame(rotation.y, sortedFrames[index + 1], 0, true),
2169
+ readNumberAtFrame(rotation.z, sortedFrames[index + 1], 0, true)
2170
+ ]
2171
+ });
2136
2172
  });
2137
2173
  return convertIRKeyframesToDSLProperty(keyframes1, TRANSFORM_FALLBACK.rotation, frameRate, false);
2138
2174
  }
@@ -2206,7 +2242,7 @@ function convertIRKeyframesToDSLProperty(keyframes, fallback, frameRate, include
2206
2242
  var _keyframes_;
2207
2243
  return {
2208
2244
  a: false,
2209
- value: (_ref = (_keyframes_ = keyframes[0]) == null ? void 0 : _keyframes_.value) != null ? _ref : fallback
2245
+ value: structuredClone((_ref = (_keyframes_ = keyframes[0]) == null ? void 0 : _keyframes_.value) != null ? _ref : fallback)
2210
2246
  };
2211
2247
  }
2212
2248
  var animation = [];
@@ -2261,29 +2297,67 @@ function readNumberStatic(property, fallback) {
2261
2297
  }
2262
2298
  return (_property_value = property.value) != null ? _property_value : fallback;
2263
2299
  }
2264
- function readNumberAtFrame(property, frame, fallback) {
2265
- var _ref;
2266
- var _property_keyframes;
2300
+ function readNumberAtFrame(property, frame, fallback, beforeFrame) {
2301
+ if (beforeFrame === void 0) beforeFrame = false;
2267
2302
  if (property == null) {
2268
2303
  return fallback;
2269
2304
  }
2270
2305
  if (property.type === 'static') {
2271
- var _property_value;
2272
- return (_property_value = property.value) != null ? _property_value : fallback;
2306
+ return property.value;
2307
+ }
2308
+ var keyframes = property.keyframes;
2309
+ var first = keyframes[0];
2310
+ if (frame <= first.frame) {
2311
+ return first.value;
2312
+ }
2313
+ for(var index = 0; index < keyframes.length - 1; index++){
2314
+ var _left_outTangent, _right_inTangent, _left_endValue;
2315
+ var left = keyframes[index];
2316
+ var right = keyframes[index + 1];
2317
+ if (frame > right.frame || frame === right.frame && !beforeFrame) {
2318
+ continue;
2319
+ }
2320
+ if (left.hold === true) {
2321
+ return left.value;
2322
+ }
2323
+ var progress = (frame - left.frame) / (right.frame - left.frame);
2324
+ var out = (_left_outTangent = left.outTangent) != null ? _left_outTangent : {
2325
+ x: 0,
2326
+ y: 0
2327
+ };
2328
+ var incoming = (_right_inTangent = right.inTangent) != null ? _right_inTangent : {
2329
+ x: 1,
2330
+ y: 1
2331
+ };
2332
+ // Temporal Bezier X is time, so invert it before evaluating the value curve.
2333
+ var low = 0;
2334
+ var high = 1;
2335
+ for(var iteration = 0; iteration < 40; iteration++){
2336
+ var mid = (low + high) / 2;
2337
+ if (cubicBezier(mid, out.x, incoming.x) < progress) {
2338
+ low = mid;
2339
+ } else {
2340
+ high = mid;
2341
+ }
2342
+ }
2343
+ var eased = out.x === out.y && incoming.x === incoming.y ? progress : cubicBezier((low + high) / 2, out.y, incoming.y);
2344
+ return left.value + (((_left_endValue = left.endValue) != null ? _left_endValue : right.value) - left.value) * eased;
2273
2345
  }
2274
- var keyframe = (_property_keyframes = property.keyframes) == null ? void 0 : _property_keyframes.find(function(candidate) {
2275
- return Math.abs(candidate.frame - frame) < EPSILON$1;
2276
- });
2277
- return (_ref = keyframe == null ? void 0 : keyframe.value) != null ? _ref : fallback;
2346
+ return keyframes[keyframes.length - 1].value;
2347
+ }
2348
+ function cubicBezier(time, first, second) {
2349
+ var inverse = 1 - time;
2350
+ return 3 * inverse * inverse * time * first + 3 * inverse * time * time * second + time * time * time;
2278
2351
  }
2279
2352
  function collectDiagnostics(layer, layerId, layerPath, diagnostics) {
2280
- var _layer_content;
2353
+ var _ref;
2354
+ var _layer_content, _layer_masks;
2281
2355
  var preservedFields = [];
2282
2356
  var unsupportedFeatures = [];
2283
2357
  collectPreservedField(preservedFields, layer.visible === false ? layer.visible : undefined, 'visible');
2284
2358
  collectPreservedField(preservedFields, layer.endBehavior != null && layer.endBehavior !== 'destroy' ? layer.endBehavior : undefined, 'endBehavior');
2285
2359
  collectPreservedField(preservedFields, layer.blendMode, 'blendMode');
2286
- collectPreservedField(preservedFields, layer.mask, 'mask');
2360
+ collectPreservedField(preservedFields, layer.masks, 'masks');
2287
2361
  collectPreservedField(preservedFields, layer.matte, 'matte');
2288
2362
  collectPreservedField(preservedFields, layer.localBounds != null && layer.localBounds.x !== 0 ? layer.localBounds.x : undefined, 'localBounds.x');
2289
2363
  collectPreservedField(preservedFields, layer.localBounds != null && layer.localBounds.y !== 0 ? layer.localBounds.y : undefined, 'localBounds.y');
@@ -2302,7 +2376,7 @@ function collectDiagnostics(layer, layerId, layerPath, diagnostics) {
2302
2376
  if (layer.type === 'precomp') {
2303
2377
  unsupportedFeatures.push(layer.type);
2304
2378
  }
2305
- if (layer.mask != null || layer.matte != null) {
2379
+ if (((_ref = (_layer_masks = layer.masks) == null ? void 0 : _layer_masks.length) != null ? _ref : 0) > 0 || layer.matte != null) {
2306
2380
  unsupportedFeatures.push('mask');
2307
2381
  }
2308
2382
  var uniquePreservedFields = Array.from(new Set(preservedFields));
@@ -2491,7 +2565,11 @@ function sanitizeIdPart(value) {
2491
2565
  return value.replace(/[^a-zA-Z0-9_-]/g, '_');
2492
2566
  }
2493
2567
 
2494
- /** Convert Animation IR to editable DSL and retain source-neutral data for the reverse conversion. */ function convertIRToDSL(baselineScene) {
2568
+ /**
2569
+ * Project validated IR into editable DSL, diagnostics, and a paired JSON-serializable snapshot.
2570
+ * The input scene is not mutated. Invalid IR throws IRValidationError; an invalid
2571
+ * DSL projection throws DSLValidationError. Shared precompositions remain linked on write-back.
2572
+ */ function convertIRToDSL(baselineScene) {
2495
2573
  animationIr.assertIRScene(baselineScene);
2496
2574
  var baselineSnapshot = structuredClone(baselineScene);
2497
2575
  var result = convertIRSceneEntryToDSL(baselineSnapshot);
@@ -2638,7 +2716,7 @@ function convertDSLCurvesToIRKeyframes(animationCurves, frameRate, convertValue,
2638
2716
  });
2639
2717
  var endKeyframe = _extends({
2640
2718
  frame: secondsToFrames(curve.t[1], frameRate),
2641
- value: endValue,
2719
+ value: structuredClone(endValue),
2642
2720
  inTangent: {
2643
2721
  x: curve.easing[2],
2644
2722
  y: curve.easing[3]
@@ -2933,8 +3011,21 @@ function areParticleBurstsEqual(left, right) {
2933
3011
  });
2934
3012
  }
2935
3013
 
2936
- function updateIRLayer(irLayer, irScene, editedLayer, baselineLayer, dslPath, frameRate, strict, reportWarning, reportChange) {
3014
+ function updateIRLayer(irLayer, irScene, editedLayer, baselineLayer, dslPath, frameRate, reportWarning, reportChange, applySupportedEdits) {
3015
+ if (applySupportedEdits === void 0) applySupportedEdits = true;
2937
3016
  var _editedLayer_parent, _baselineLayer_parent;
3017
+ if (editedLayer.type !== baselineLayer.type) {
3018
+ reportUnsupportedLayerEdit(reportWarning, 'LAYER_TYPE_CHANGE_IGNORED', "type change on layer '" + editedLayer.id + "' ('" + baselineLayer.type + "' -> '" + editedLayer.type + "') is ignored.", editedLayer.id, "" + dslPath + ".type");
3019
+ }
3020
+ if (editedLayer.description.self !== baselineLayer.description.self || editedLayer.description.composite !== baselineLayer.description.composite) {
3021
+ reportUnsupportedLayerEdit(reportWarning, 'LAYER_DESCRIPTION_CHANGE_IGNORED', "description change on layer '" + editedLayer.id + "' is not represented in Animation IR and is ignored.", editedLayer.id, "" + dslPath + ".description");
3022
+ }
3023
+ if (((_editedLayer_parent = editedLayer.parent) != null ? _editedLayer_parent : null) !== ((_baselineLayer_parent = baselineLayer.parent) != null ? _baselineLayer_parent : null)) {
3024
+ reportUnsupportedLayerEdit(reportWarning, 'PARENT_CHANGE_IGNORED', "parent change on layer '" + editedLayer.id + "' " + ("('" + baselineLayer.parent + "' -> '" + editedLayer.parent + "') is ignored in this mode."), editedLayer.id, "" + dslPath + ".parent");
3025
+ }
3026
+ if (!applySupportedEdits) {
3027
+ return;
3028
+ }
2938
3029
  if (editedLayer.name !== baselineLayer.name) {
2939
3030
  var previousName = irLayer.name;
2940
3031
  irLayer.name = editedLayer.name;
@@ -2947,15 +3038,6 @@ function updateIRLayer(irLayer, irScene, editedLayer, baselineLayer, dslPath, fr
2947
3038
  newValue: editedLayer.name
2948
3039
  });
2949
3040
  }
2950
- if (editedLayer.type !== baselineLayer.type) {
2951
- reportUnsupportedLayerEdit(strict, reportWarning, 'LAYER_TYPE_CHANGE_IGNORED', "type change on layer '" + editedLayer.id + "' ('" + baselineLayer.type + "' -> '" + editedLayer.type + "') is ignored.", editedLayer.id, "" + dslPath + ".type");
2952
- }
2953
- if (editedLayer.description.self !== baselineLayer.description.self || editedLayer.description.composite !== baselineLayer.description.composite) {
2954
- reportUnsupportedLayerEdit(strict, reportWarning, 'LAYER_DESCRIPTION_CHANGE_IGNORED', "description change on layer '" + editedLayer.id + "' is not represented in Animation IR and is ignored.", editedLayer.id, "" + dslPath + ".description");
2955
- }
2956
- if (((_editedLayer_parent = editedLayer.parent) != null ? _editedLayer_parent : null) !== ((_baselineLayer_parent = baselineLayer.parent) != null ? _baselineLayer_parent : null)) {
2957
- reportUnsupportedLayerEdit(strict, reportWarning, 'PARENT_CHANGE_IGNORED', "parent change on layer '" + editedLayer.id + "' " + ("('" + baselineLayer.parent + "' -> '" + editedLayer.parent + "') is ignored in this mode."), editedLayer.id, "" + dslPath + ".parent");
2958
- }
2959
3041
  updateIRTransform(irLayer, editedLayer, baselineLayer, frameRate, reportChange);
2960
3042
  updateIRTiming(irLayer, editedLayer, baselineLayer, frameRate, reportChange);
2961
3043
  updateIRText(irLayer, irScene, editedLayer, baselineLayer, reportChange);
@@ -2991,11 +3073,8 @@ function updateIRLocalBounds(irLayer, editedLayer, reportChange) {
2991
3073
  newValue: updatedBounds
2992
3074
  });
2993
3075
  }
2994
- function reportUnsupportedLayerEdit(strict, reportWarning, code, message, layerId, path) {
3076
+ function reportUnsupportedLayerEdit(reportWarning, code, message, layerId, path) {
2995
3077
  var formattedMessage = "[dsl-ir:preserved-data] " + message;
2996
- if (strict) {
2997
- throw new Error(formattedMessage);
2998
- }
2999
3078
  reportWarning({
3000
3079
  code: code,
3001
3080
  message: formattedMessage,
@@ -3044,17 +3123,19 @@ function updateIRTransform(irLayer, editedLayer, baselineLayer, frameRate, repor
3044
3123
  var _loop = function() {
3045
3124
  var keyframe = _step1.value;
3046
3125
  var _ref, _ref1;
3047
- var source = sourcePosition.keyframes.find(function(candidate) {
3126
+ var sourceIndex = sourcePosition.keyframes.findIndex(function(candidate) {
3048
3127
  return candidate.frame === keyframe.frame;
3049
3128
  });
3129
+ var source = sourcePosition.keyframes[sourceIndex];
3050
3130
  if (source == null && !constantDepth) {
3051
3131
  throw new Error('[dsl-ir] Position timing edits on layers with animated source depth are unsupported.');
3052
3132
  }
3053
3133
  keyframe.value[2] = (_ref = (_ref1 = source == null ? void 0 : source.value[2]) != null ? _ref1 : depths[0]) != null ? _ref : 0;
3054
3134
  if (keyframe.endValue != null) {
3055
- var _ref2, _ref3, _ref4;
3056
- var _this;
3057
- keyframe.endValue[2] = (_ref2 = (_ref3 = (_this = (_ref4 = source == null ? void 0 : source.endValue) != null ? _ref4 : source == null ? void 0 : source.value) == null ? void 0 : _this[2]) != null ? _ref3 : depths[0]) != null ? _ref2 : 0;
3135
+ var _ref2, _ref3, _ref4, _ref5;
3136
+ var _sourcePosition_keyframes_;
3137
+ var sourceEnd = (source == null ? void 0 : source.hold) === true ? source.value : (_ref2 = (_ref3 = source == null ? void 0 : source.endValue) != null ? _ref3 : (_sourcePosition_keyframes_ = sourcePosition.keyframes[sourceIndex + 1]) == null ? void 0 : _sourcePosition_keyframes_.value) != null ? _ref2 : source == null ? void 0 : source.value;
3138
+ keyframe.endValue[2] = (_ref4 = (_ref5 = sourceEnd == null ? void 0 : sourceEnd[2]) != null ? _ref5 : depths[0]) != null ? _ref4 : 0;
3058
3139
  }
3059
3140
  };
3060
3141
  for(var _iterator1 = _create_for_of_iterator_helper_loose(position.keyframes), _step1; !(_step1 = _iterator1()).done;)_loop();
@@ -3143,7 +3224,7 @@ function updateIRText(irLayer, irScene, editedLayer, baselineLayer, reportChange
3143
3224
  if (irLayer.type !== 'text' || editedText == null) {
3144
3225
  return;
3145
3226
  }
3146
- var normalizedEditedText = normalizeTextPropertiesForIR(editedText, irLayer.content);
3227
+ var normalizedEditedText = normalizeTextPropertiesForIR(editedText);
3147
3228
  var textChanges = baselineText != null ? collectTextPropertyChanges(baselineText, normalizedEditedText) : [];
3148
3229
  if (baselineText != null && textChanges.length === 0) {
3149
3230
  return;
@@ -3154,6 +3235,9 @@ function updateIRText(irLayer, irScene, editedLayer, baselineLayer, reportChange
3154
3235
  return change.field === 'font';
3155
3236
  }) ? ensureSystemFontAsset(irScene.assets, (_normalizedEditedText_font = normalizedEditedText.font) != null ? _normalizedEditedText_font : currentFontAsset.family, currentFontAsset.style, currentFontAsset.weight) : irLayer.content.fontAssetId;
3156
3237
  irLayer.content = buildUpdatedTextData(irLayer.content, normalizedEditedText, fontAssetId);
3238
+ if (baselineText != null && isHexEqual(normalizedEditedText.color, baselineText.color)) {
3239
+ irLayer.content.color = previousText.color;
3240
+ }
3157
3241
  if (baselineText == null) {
3158
3242
  reportChange({
3159
3243
  kind: 'text',
@@ -3177,10 +3261,10 @@ function updateIRText(irLayer, irScene, editedLayer, baselineLayer, reportChange
3177
3261
  }
3178
3262
  }
3179
3263
  }
3180
- function normalizeTextPropertiesForIR(text, currentText) {
3181
- var currentFontSize = Number.isFinite(currentText.fontSize) && currentText.fontSize > 0 ? currentText.fontSize : 16;
3182
- var fontSize = Number.isFinite(text.size) && text.size > 0 ? text.size : currentFontSize;
3183
- var lineHeight = text.line_height != null && Number.isFinite(text.line_height) && text.line_height > 0 ? text.line_height : fontSize;
3264
+ function normalizeTextPropertiesForIR(text) {
3265
+ var _text_line_height;
3266
+ var fontSize = text.size;
3267
+ var lineHeight = (_text_line_height = text.line_height) != null ? _text_line_height : fontSize;
3184
3268
  var fontFamily = text.font != null ? normalizeDSLFontFamily(text.font) : undefined;
3185
3269
  return _extends({}, text, {
3186
3270
  size: fontSize,
@@ -3247,11 +3331,27 @@ function updateIRAsset(irLayer, irScene, editedLayer, baselineLayer, reportChang
3247
3331
  }
3248
3332
  }
3249
3333
  function countImageAssetReferences(irScene, assetId) {
3250
- return irScene.compositions.reduce(function(count, composition) {
3251
- return count + composition.layers.filter(function(layer) {
3252
- return layer.type === 'image' && layer.content.assetId === assetId;
3253
- }).length;
3254
- }, 0);
3334
+ var count = 0;
3335
+ for(var _iterator = _create_for_of_iterator_helper_loose(irScene.compositions), _step; !(_step = _iterator()).done;){
3336
+ var composition = _step.value;
3337
+ for(var _iterator1 = _create_for_of_iterator_helper_loose(composition.layers), _step1; !(_step1 = _iterator1()).done;){
3338
+ var layer = _step1.value;
3339
+ if (layer.type === 'image' && layer.content.assetId === assetId) {
3340
+ count++;
3341
+ } else if (layer.type === 'shape') {
3342
+ for(var _iterator2 = _create_for_of_iterator_helper_loose(layer.content.shapes), _step2; !(_step2 = _iterator2()).done;){
3343
+ var shape = _step2.value;
3344
+ var paints = [].concat(shape.fills, shape.strokes.map(function(stroke) {
3345
+ return stroke.paint;
3346
+ }));
3347
+ count += paints.filter(function(paint) {
3348
+ return paint.type === 'texture' && paint.assetId === assetId;
3349
+ }).length;
3350
+ }
3351
+ }
3352
+ }
3353
+ }
3354
+ return count;
3255
3355
  }
3256
3356
  function createAvailableRecombineAssetId(irScene) {
3257
3357
  var existingAssetIdSet = new Set(irScene.assets.map(function(asset) {
@@ -3265,6 +3365,109 @@ function createAvailableRecombineAssetId(irScene) {
3265
3365
  }
3266
3366
  }
3267
3367
 
3368
+ var SHARED_FIELDS = [
3369
+ 'name',
3370
+ 'width',
3371
+ 'height',
3372
+ 'time_range',
3373
+ 'position',
3374
+ 'rotation',
3375
+ 'scale',
3376
+ 'anchor',
3377
+ 'opacity',
3378
+ 'asset',
3379
+ 'text',
3380
+ 'particle'
3381
+ ];
3382
+ /** Resolve linked edits before mutating IR; conflicting fields retain their baseline values. */ function resolveSharedPrecompEdits(entries, mapping, reportWarning) {
3383
+ var groups = new Map();
3384
+ for(var _iterator = _create_for_of_iterator_helper_loose(entries), _step; !(_step = _iterator()).done;){
3385
+ var entry = _step.value;
3386
+ var _groups_get;
3387
+ var source = mapping.layers[entry.baseline.id];
3388
+ if ((source == null ? void 0 : source.expandedSource) == null) {
3389
+ continue;
3390
+ }
3391
+ var key = JSON.stringify([
3392
+ source.irCompositionId,
3393
+ source.irLayerId
3394
+ ]);
3395
+ var group = (_groups_get = groups.get(key)) != null ? _groups_get : [];
3396
+ group.push(entry);
3397
+ groups.set(key, group);
3398
+ }
3399
+ for(var _iterator1 = _create_for_of_iterator_helper_loose(groups.values()), _step1; !(_step1 = _iterator1()).done;){
3400
+ var _loop = function() {
3401
+ var field = _step2.value;
3402
+ var changes = group1.filter(function(entry) {
3403
+ var _entry_edited;
3404
+ return ((_entry_edited = entry.edited) == null ? void 0 : _entry_edited[field]) !== undefined && !areSharedValuesEqual(entry.edited[field], entry.baseline[field]);
3405
+ });
3406
+ var first = changes[0];
3407
+ if ((first == null ? void 0 : first.edited) == null) {
3408
+ return "continue";
3409
+ }
3410
+ var value = first.edited[field];
3411
+ var conflict = changes.some(function(entry) {
3412
+ var _entry_edited;
3413
+ return !areSharedValuesEqual((_entry_edited = entry.edited) == null ? void 0 : _entry_edited[field], value);
3414
+ });
3415
+ if (conflict) {
3416
+ reportWarning({
3417
+ code: 'SHARED_PRECOMP_EDIT_CONFLICT',
3418
+ path: first.path + "." + field,
3419
+ layerId: first.baseline.id,
3420
+ message: "Conflicting '" + field + "' edits on shared precomposition instances were ignored; " + 'the original field is retained.'
3421
+ });
3422
+ }
3423
+ for(var _iterator = _create_for_of_iterator_helper_loose(group1), _step; !(_step = _iterator()).done;){
3424
+ var entry = _step.value;
3425
+ if (entry.edited != null) {
3426
+ var _obj;
3427
+ Object.assign(entry.edited, (_obj = {}, _obj[field] = structuredClone(conflict ? entry.baseline[field] : value), _obj));
3428
+ }
3429
+ }
3430
+ };
3431
+ var group1 = _step1.value;
3432
+ if (group1.length < 2) {
3433
+ continue;
3434
+ }
3435
+ var deleted = group1.find(function(entry) {
3436
+ return entry.edited == null;
3437
+ });
3438
+ if (deleted != null) {
3439
+ reportWarning({
3440
+ code: 'SHARED_PRECOMP_LAYER_DELETED',
3441
+ path: deleted.path,
3442
+ layerId: deleted.baseline.id,
3443
+ message: 'Deleting this shared precomposition layer removes it from every instance; deletion takes precedence.'
3444
+ });
3445
+ continue;
3446
+ }
3447
+ for(var _iterator2 = _create_for_of_iterator_helper_loose(SHARED_FIELDS), _step2; !(_step2 = _iterator2()).done;)_loop();
3448
+ }
3449
+ }
3450
+ function areSharedValuesEqual(left, right) {
3451
+ if (typeof left === 'number' && typeof right === 'number') {
3452
+ return isScalarEqual(left, right);
3453
+ }
3454
+ if (left === right) {
3455
+ return true;
3456
+ }
3457
+ if (Array.isArray(left) && Array.isArray(right)) {
3458
+ return left.length === right.length && left.every(function(value, index) {
3459
+ return areSharedValuesEqual(value, right[index]);
3460
+ });
3461
+ }
3462
+ if (isRecord$1(left) && isRecord$1(right)) {
3463
+ var keys = new Set([].concat(Object.keys(left), Object.keys(right)));
3464
+ return Array.from(keys).every(function(key) {
3465
+ return areSharedValuesEqual(left[key], right[key]);
3466
+ });
3467
+ }
3468
+ return false;
3469
+ }
3470
+
3268
3471
  /** Apply edited sibling order without changing parent relationships. */ function updateLayerOrder(updatedIR, editedLayers, irLayerIdByEditedLayer, reportChange, rootDSLParentId) {
3269
3472
  var irLayerById = new Map(updatedIR.layers.map(function(layer) {
3270
3473
  return [
@@ -3433,8 +3636,8 @@ function reorderOwnedEntries(entries, orderedOwners, resolveOwner) {
3433
3636
 
3434
3637
  /** Apply previously validated DSL to a clone of the baseline IR. */ function updateValidatedIRFromDSL(baselineScene, editedDocument, layerMapping, options) {
3435
3638
  var _loop = function() {
3436
- var _step_value = _step3.value, baselineLayer = _step_value.layer, path = _step_value.path;
3437
- var _layerMapping_layers_baselineLayer_id;
3639
+ var _step_value = _step4.value, baselineLayer = _step_value.layer, path = _step_value.path;
3640
+ var _layerMapping_layers_baselineLayer_id, _layerMapping_layers_baselineLayer_id1, _layerMapping_layers_baselineLayer_id2;
3438
3641
  var irLayerId = (_layerMapping_layers_baselineLayer_id = layerMapping.layers[baselineLayer.id]) == null ? void 0 : _layerMapping_layers_baselineLayer_id.irLayerId;
3439
3642
  if (irLayerId == null) {
3440
3643
  return "continue";
@@ -3442,11 +3645,15 @@ function reorderOwnedEntries(entries, orderedOwners, resolveOwner) {
3442
3645
  var editedLayer = matchEditedLayer(baselineLayer, path, {
3443
3646
  editedLayerById: editedLayerById,
3444
3647
  editedLayerByPath: editedLayerByPath,
3445
- matchedEditedLayerSet: matchedEditedLayerSet
3648
+ matchedEditedLayerSet: matchedEditedLayerSet,
3649
+ baselineLayerIds: baselineLayerIds
3446
3650
  });
3447
3651
  if (editedLayer == null) {
3448
- var _layerMapping_layers_baselineLayer_id1;
3449
- var expandedSource = (_layerMapping_layers_baselineLayer_id1 = layerMapping.layers[baselineLayer.id]) == null ? void 0 : _layerMapping_layers_baselineLayer_id1.expandedSource;
3652
+ var _layerMapping_layers_baselineLayer_id3;
3653
+ var expandedSource = (_layerMapping_layers_baselineLayer_id3 = layerMapping.layers[baselineLayer.id]) == null ? void 0 : _layerMapping_layers_baselineLayer_id3.expandedSource;
3654
+ if (expandedSource != null && !editedLayerById.has(expandedSource.parentLayerId)) {
3655
+ return "continue";
3656
+ }
3450
3657
  var expandedComposition = expandedSource != null ? compositionById.get(expandedSource.irCompositionId) : undefined;
3451
3658
  if (expandedSource != null && (expandedComposition == null ? void 0 : expandedComposition.layers.some(function(layer) {
3452
3659
  return layer.id === irLayerId;
@@ -3466,15 +3673,18 @@ function reorderOwnedEntries(entries, orderedOwners, resolveOwner) {
3466
3673
  });
3467
3674
  return "continue";
3468
3675
  }
3676
+ if (((_layerMapping_layers_baselineLayer_id1 = layerMapping.layers[baselineLayer.id]) == null ? void 0 : _layerMapping_layers_baselineLayer_id1.irCompositionId) !== updatedIR.id || ((_layerMapping_layers_baselineLayer_id2 = layerMapping.layers[baselineLayer.id]) == null ? void 0 : _layerMapping_layers_baselineLayer_id2.expandedSource) != null) {
3677
+ return "continue";
3678
+ }
3469
3679
  var irLayer = clonedLayerByIRId.get(irLayerId);
3470
3680
  if (irLayer == null) {
3471
3681
  return "continue";
3472
3682
  }
3473
3683
  matchedEditedLayerSet.add(editedLayer);
3474
3684
  irLayerIdByEditedLayer.set(editedLayer, irLayerId);
3475
- updateIRLayer(irLayer, updatedScene, editedLayer, baselineLayer, path, targetFrameRate, strict, reportWarning, reportChange);
3685
+ updateIRLayer(irLayer, updatedScene, editedLayer, baselineLayer, path, targetFrameRate, reportWarning, reportChange);
3476
3686
  }, _loop1 = function() {
3477
- var editedLayer = _step4.value;
3687
+ var editedLayer = _step5.value;
3478
3688
  var _editedPathByLayer_get;
3479
3689
  var _layerMapping_layers_editedLayer_id;
3480
3690
  if (matchedEditedLayerSet.has(editedLayer)) {
@@ -3488,17 +3698,22 @@ function reorderOwnedEntries(entries, orderedOwners, resolveOwner) {
3488
3698
  return layer.id === mappingEntry.irLayerId;
3489
3699
  }) : undefined;
3490
3700
  if (targetComposition != null && targetLayer != null && (mappingEntry == null ? void 0 : mappingEntry.irLayerId) != null) {
3491
- var _editedPathByLayer_get1;
3492
- updateIRLayer(targetLayer, updatedScene, editedLayer, buildExpandedBaselineLayer(editedLayer, expandedSource), (_editedPathByLayer_get1 = editedPathByLayer.get(editedLayer)) != null ? _editedPathByLayer_get1 : '$.layers', targetFrameRate, strict, reportWarning, reportChange);
3701
+ var _deletedExpandedIRLayerIdsByCompositionId_get;
3702
+ var key = JSON.stringify([
3703
+ targetComposition.id,
3704
+ mappingEntry.irLayerId
3705
+ ]);
3706
+ if (((_deletedExpandedIRLayerIdsByCompositionId_get = deletedExpandedIRLayerIdsByCompositionId.get(targetComposition.id)) == null ? void 0 : _deletedExpandedIRLayerIdsByCompositionId_get.has(mappingEntry.irLayerId)) !== true) {
3707
+ var _editedPathByLayer_get1;
3708
+ updateIRLayer(targetLayer, updatedScene, editedLayer, buildExpandedBaselineLayer(editedLayer, expandedSource), (_editedPathByLayer_get1 = editedPathByLayer.get(editedLayer)) != null ? _editedPathByLayer_get1 : '$.layers', targetComposition.frameRate, reportWarning, reportChange, !updatedExpandedLayerKeys.has(key));
3709
+ updatedExpandedLayerKeys.add(key);
3710
+ }
3493
3711
  expandedIRLayerIdByEditedLayer.set(editedLayer, mappingEntry.irLayerId);
3494
3712
  editedExpandedCompositionById.set(expandedSource.irCompositionId, targetComposition);
3495
3713
  }
3496
3714
  return "continue";
3497
3715
  }
3498
3716
  var message = "[dsl-ir:preserved-data] edited layer '" + editedLayer.id + "' matches no baseline scene layer; new layers are not supported in this mode.";
3499
- if (strict) {
3500
- throw new Error(message);
3501
- }
3502
3717
  reportWarning({
3503
3718
  code: 'INVENTED_LAYER',
3504
3719
  message: message,
@@ -3506,7 +3721,7 @@ function reorderOwnedEntries(entries, orderedOwners, resolveOwner) {
3506
3721
  layerId: editedLayer.id
3507
3722
  });
3508
3723
  }, _loop2 = function() {
3509
- var _step_value = _step5.value, dslLayerId = _step_value[0], mappingEntry = _step_value[1];
3724
+ var _step_value = _step6.value, dslLayerId = _step_value[0], mappingEntry = _step_value[1];
3510
3725
  var _deletedExpandedIRLayerIdsByCompositionId_get;
3511
3726
  var expandedSource = mappingEntry.expandedSource;
3512
3727
  var irLayerId = mappingEntry.irLayerId;
@@ -3530,7 +3745,6 @@ function reorderOwnedEntries(entries, orderedOwners, resolveOwner) {
3530
3745
  });
3531
3746
  };
3532
3747
  if (options === void 0) options = {};
3533
- var strict = options.strict === true;
3534
3748
  var reportWarning = createWarningReporter(options.onWarning);
3535
3749
  var reportChange = createChangeReporter(options.onChange);
3536
3750
  var baselineDSLResult = convertIRToDSL(baselineScene);
@@ -3545,13 +3759,33 @@ function reorderOwnedEntries(entries, orderedOwners, resolveOwner) {
3545
3759
  ];
3546
3760
  }));
3547
3761
  var targetFrameRate = editedDSL.meta.fps;
3548
- // Reframe the clone first; comparisons still use the untouched baseline DSL in seconds.
3549
- for(var _iterator = _create_for_of_iterator_helper_loose(updatedScene.compositions), _step; !(_step = _iterator()).done;){
3550
- var composition = _step.value;
3551
- updateIRFrameRate(composition, targetFrameRate, reportChange, composition.id === updatedIR.id);
3762
+ // Preserve independent source rates unless the document FPS is explicitly edited.
3763
+ if (!isScalarEqual(targetFrameRate, baselineDSL.meta.fps)) {
3764
+ var sourceFrameRates = new Map(baselineScene.compositions.map(function(composition) {
3765
+ return [
3766
+ composition.id,
3767
+ composition.frameRate
3768
+ ];
3769
+ }));
3770
+ for(var _iterator = _create_for_of_iterator_helper_loose(updatedScene.compositions), _step; !(_step = _iterator()).done;){
3771
+ var composition = _step.value;
3772
+ for(var _iterator1 = _create_for_of_iterator_helper_loose(composition.layers), _step1; !(_step1 = _iterator1()).done;){
3773
+ var layer = _step1.value;
3774
+ if (layer.type === 'precomp' && layer.time.sourceStartFrame != null) {
3775
+ var sourceFrameRate = sourceFrameRates.get(layer.content.compositionId);
3776
+ if (sourceFrameRate != null && !isScalarEqual(targetFrameRate, sourceFrameRate)) {
3777
+ layer.time.sourceStartFrame *= targetFrameRate / sourceFrameRate;
3778
+ }
3779
+ }
3780
+ }
3781
+ updateIRFrameRate(composition, targetFrameRate, reportChange, composition.id === updatedIR.id);
3782
+ }
3552
3783
  }
3553
3784
  updateIRComposition(updatedIR, editedDSL, baselineDSL, targetFrameRate, reportChange);
3554
3785
  var baselineLayers = collectLayersWithPaths(baselineDSL.layers);
3786
+ var baselineLayerIds = new Set(baselineLayers.map(function(entry) {
3787
+ return entry.layer.id;
3788
+ }));
3555
3789
  var editedLayerEntries = collectLayersWithPaths(editedDSL.layers);
3556
3790
  var editedLayers = editedLayerEntries.map(function(entry) {
3557
3791
  return entry.layer;
@@ -3564,22 +3798,22 @@ function reorderOwnedEntries(entries, orderedOwners, resolveOwner) {
3564
3798
  }));
3565
3799
  var editedLayerById = new Map();
3566
3800
  var editedLayerByPath = new Map();
3567
- for(var _iterator1 = _create_for_of_iterator_helper_loose(editedLayerEntries), _step1; !(_step1 = _iterator1()).done;){
3568
- var _step_value = _step1.value, layer = _step_value.layer, path = _step_value.path;
3569
- var previousEntry = editedLayerById.get(layer.id);
3801
+ for(var _iterator2 = _create_for_of_iterator_helper_loose(editedLayerEntries), _step2; !(_step2 = _iterator2()).done;){
3802
+ var _step_value = _step2.value, layer1 = _step_value.layer, path = _step_value.path;
3803
+ var previousEntry = editedLayerById.get(layer1.id);
3570
3804
  if (previousEntry != null) {
3571
- throw new Error("[dsl-ir:preserved-data] Duplicate layer id '" + layer.id + "'; earlier occurrence at path '" + previousEntry.path + "'.");
3805
+ throw new Error("[dsl-ir:preserved-data] Duplicate layer id '" + layer1.id + "'; earlier occurrence at path '" + previousEntry.path + "'.");
3572
3806
  }
3573
- editedLayerById.set(layer.id, {
3574
- layer: layer,
3807
+ editedLayerById.set(layer1.id, {
3808
+ layer: layer1,
3575
3809
  path: path
3576
3810
  });
3577
- editedLayerByPath.set(path, layer);
3811
+ editedLayerByPath.set(path, layer1);
3578
3812
  }
3579
3813
  var clonedLayerByIRId = new Map();
3580
- for(var _iterator2 = _create_for_of_iterator_helper_loose(updatedIR.layers), _step2; !(_step2 = _iterator2()).done;){
3581
- var layer1 = _step2.value;
3582
- clonedLayerByIRId.set(layer1.id, layer1);
3814
+ for(var _iterator3 = _create_for_of_iterator_helper_loose(updatedIR.layers), _step3; !(_step3 = _iterator3()).done;){
3815
+ var layer2 = _step3.value;
3816
+ clonedLayerByIRId.set(layer2.id, layer2);
3583
3817
  }
3584
3818
  // Track object identity because edited IDs may have changed.
3585
3819
  var matchedEditedLayerSet = new Set();
@@ -3587,14 +3821,35 @@ function reorderOwnedEntries(entries, orderedOwners, resolveOwner) {
3587
3821
  var deletedIRLayerIdSet = new Set();
3588
3822
  var deletedExpandedIRLayerIdsByCompositionId = new Map();
3589
3823
  var semanticallyDeletedExpandedDSLIds = new Set();
3590
- for(var _iterator3 = _create_for_of_iterator_helper_loose(baselineLayers), _step3; !(_step3 = _iterator3()).done;)_loop();
3824
+ resolveSharedPrecompEdits(baselineLayers.flatMap(function(param) {
3825
+ var layer = param.layer, path = param.path;
3826
+ var _layerMapping_layers_layer_id_expandedSource, _layerMapping_layers_layer_id;
3827
+ var parentId = (_layerMapping_layers_layer_id = layerMapping.layers[layer.id]) == null ? void 0 : (_layerMapping_layers_layer_id_expandedSource = _layerMapping_layers_layer_id.expandedSource) == null ? void 0 : _layerMapping_layers_layer_id_expandedSource.parentLayerId;
3828
+ if (parentId != null && !editedLayerById.has(parentId)) {
3829
+ return [];
3830
+ }
3831
+ return [
3832
+ {
3833
+ baseline: layer,
3834
+ path: path,
3835
+ edited: matchEditedLayer(layer, path, {
3836
+ editedLayerById: editedLayerById,
3837
+ editedLayerByPath: editedLayerByPath,
3838
+ matchedEditedLayerSet: matchedEditedLayerSet,
3839
+ baselineLayerIds: baselineLayerIds
3840
+ })
3841
+ }
3842
+ ];
3843
+ }), layerMapping, reportWarning);
3844
+ for(var _iterator4 = _create_for_of_iterator_helper_loose(baselineLayers), _step4; !(_step4 = _iterator4()).done;)_loop();
3591
3845
  var expandedIRLayerIdByEditedLayer = new Map();
3592
3846
  var editedExpandedCompositionById = new Map();
3593
- for(var _iterator4 = _create_for_of_iterator_helper_loose(editedLayers), _step4; !(_step4 = _iterator4()).done;)_loop1();
3594
- for(var _iterator5 = _create_for_of_iterator_helper_loose(Object.entries(layerMapping.layers)), _step5; !(_step5 = _iterator5()).done;)_loop2();
3847
+ var updatedExpandedLayerKeys = new Set();
3848
+ for(var _iterator5 = _create_for_of_iterator_helper_loose(editedLayers), _step5; !(_step5 = _iterator5()).done;)_loop1();
3849
+ for(var _iterator6 = _create_for_of_iterator_helper_loose(Object.entries(layerMapping.layers)), _step6; !(_step6 = _iterator6()).done;)_loop2();
3595
3850
  removeIRLayers(updatedIR, deletedIRLayerIdSet, reportChange);
3596
- for(var _iterator6 = _create_for_of_iterator_helper_loose(deletedExpandedIRLayerIdsByCompositionId), _step6; !(_step6 = _iterator6()).done;){
3597
- var _step_value1 = _step6.value, compositionId = _step_value1[0], deletedLayerIds = _step_value1[1];
3851
+ for(var _iterator7 = _create_for_of_iterator_helper_loose(deletedExpandedIRLayerIdsByCompositionId), _step7; !(_step7 = _iterator7()).done;){
3852
+ var _step_value1 = _step7.value, compositionId = _step_value1[0], deletedLayerIds = _step_value1[1];
3598
3853
  var composition1 = compositionById.get(compositionId);
3599
3854
  if (composition1 != null) {
3600
3855
  removeIRLayers(composition1, deletedLayerIds, reportChange);
@@ -3611,9 +3866,8 @@ function buildExpandedBaselineLayer(editedLayer, expandedSource) {
3611
3866
  if (baseline == null || transform == null) {
3612
3867
  return structuredClone(editedLayer);
3613
3868
  }
3614
- return _extends({}, structuredClone(editedLayer), {
3869
+ var restored = _extends({}, structuredClone(editedLayer), {
3615
3870
  name: baseline.name,
3616
- type: baseline.type,
3617
3871
  width: baseline.width,
3618
3872
  height: baseline.height,
3619
3873
  description: structuredClone(baseline.description),
@@ -3631,6 +3885,30 @@ function buildExpandedBaselineLayer(editedLayer, expandedSource) {
3631
3885
  }, baseline.particle != null && {
3632
3886
  particle: structuredClone(baseline.particle)
3633
3887
  });
3888
+ if (baseline.type === 'text' && baseline.text != null) {
3889
+ return _extends({}, restored, {
3890
+ type: 'text',
3891
+ text: structuredClone(baseline.text)
3892
+ });
3893
+ }
3894
+ if (baseline.type === 'particle' && baseline.particle != null) {
3895
+ return _extends({}, restored, {
3896
+ type: 'particle',
3897
+ particle: structuredClone(baseline.particle)
3898
+ });
3899
+ }
3900
+ if (baseline.type === 'layer' && baseline.asset != null) {
3901
+ return _extends({}, restored, {
3902
+ type: 'layer',
3903
+ asset: structuredClone(baseline.asset)
3904
+ });
3905
+ }
3906
+ if (baseline.type === 'null') {
3907
+ return _extends({}, restored, {
3908
+ type: 'null'
3909
+ });
3910
+ }
3911
+ throw new TypeError('[dsl-ir:preserved-data] Expanded baseline layer is missing its required content.');
3634
3912
  }
3635
3913
  function updateExpandedCompositionLayerOrder(layers, compositionById, layerMapping, irLayerIdByEditedLayer, reportChange) {
3636
3914
  for(var _iterator = _create_for_of_iterator_helper_loose(layers), _step; !(_step = _iterator()).done;){
@@ -3657,7 +3935,7 @@ function matchEditedLayer(baselineLayer, path, context) {
3657
3935
  return layerById;
3658
3936
  }
3659
3937
  var layerByPath = context.editedLayerByPath.get(path);
3660
- if (layerByPath != null && !context.matchedEditedLayerSet.has(layerByPath) && layerByPath.name === baselineLayer.name) {
3938
+ if (layerByPath != null && !context.matchedEditedLayerSet.has(layerByPath) && !context.baselineLayerIds.has(layerByPath.id) && layerByPath.name === baselineLayer.name) {
3661
3939
  return layerByPath;
3662
3940
  }
3663
3941
  return undefined;
@@ -3764,9 +4042,6 @@ function removeIRLayers(updatedIR, deletedIRLayerIdSet, reportChange) {
3764
4042
  var _layer_transform, _layer_transform1, _layer_transform_rotation, _layer_transform2, _layer_transform_rotation1, _layer_transform3, _layer_transform_rotation2, _layer_transform4, _layer_transform5;
3765
4043
  layer.time.inFrame *= frameRateScale;
3766
4044
  layer.time.outFrame *= frameRateScale;
3767
- if (layer.type === 'precomp' && layer.time.sourceStartFrame != null) {
3768
- layer.time.sourceStartFrame *= frameRateScale;
3769
- }
3770
4045
  reframeAnimatedProperty((_layer_transform = layer.transform) == null ? void 0 : _layer_transform.position, frameRateScale);
3771
4046
  reframeAnimatedProperty((_layer_transform1 = layer.transform) == null ? void 0 : _layer_transform1.scale, frameRateScale);
3772
4047
  reframeAnimatedProperty((_layer_transform2 = layer.transform) == null ? void 0 : (_layer_transform_rotation = _layer_transform2.rotation) == null ? void 0 : _layer_transform_rotation.x, frameRateScale);
@@ -4135,31 +4410,405 @@ function convertLayerTime(layer, compositionDurationSeconds, frameRate) {
4135
4410
  };
4136
4411
  }
4137
4412
  function convertTextData(text, fontAssets) {
4138
- var _text_align;
4413
+ var _text_line_height, _text_align;
4139
4414
  var fontFamily = normalizeDSLFontFamily(text.font);
4140
4415
  // DSL text cannot express style or weight, so new IR fonts use the canonical normal/400 defaults.
4141
4416
  var fontAssetId = ensureSystemFontAsset(fontAssets, fontFamily, 'normal', 400);
4142
- var fontSize = Number.isFinite(text.size) && text.size > 0 ? text.size : 16;
4417
+ var fontSize = text.size;
4143
4418
  return {
4144
4419
  text: text.content,
4145
4420
  fontAssetId: fontAssetId,
4146
4421
  fontSize: fontSize,
4147
4422
  color: hexToIRColor(text.color !== '' ? text.color : '#000000'),
4148
4423
  letterSpacing: resolveFiniteNumber(text.letter_spacing, 0),
4149
- lineHeight: text.line_height != null && Number.isFinite(text.line_height) && text.line_height > 0 ? text.line_height : fontSize,
4424
+ lineHeight: (_text_line_height = text.line_height) != null ? _text_line_height : fontSize,
4150
4425
  textAlign: (_text_align = text.align) != null ? _text_align : 'left'
4151
4426
  };
4152
4427
  }
4153
4428
 
4154
- /** Convert an editable DSL composition into a shared Animation IR scene. */ function convertDSLToIR(composition, options) {
4155
- if (options === void 0) options = {};
4156
- var validationResult = validateDSL(composition, {
4157
- strict: options.strict
4429
+ /** Source bindings distinguish target layout from edits to source-local emitter transforms. */ function convertDSLWithParticleSources(document, options) {
4430
+ var _loop = function() {
4431
+ var _step_value = _step2.value, sourceIndex = _step_value[0], source = _step_value[1];
4432
+ var _result_scene_assets, _result_diagnostics;
4433
+ animationIr.assertIRScene(source.preservedData.baselineScene);
4434
+ var sourceDSL = convertIRToDSL(source.preservedData.baselineScene).dsl;
4435
+ var sourceLayerById = new Map(flattenLayers(sourceDSL.layers).map(function(layer) {
4436
+ return [
4437
+ layer.id,
4438
+ layer
4439
+ ];
4440
+ }));
4441
+ var targets = Object.keys(source.layers).map(function(id) {
4442
+ var layer = targetById.get(id);
4443
+ if (layer == null) {
4444
+ throw new TypeError("[dsl-ir:sources] Missing target layer '" + id + "'.");
4445
+ }
4446
+ return layer;
4447
+ });
4448
+ var target = targets[0];
4449
+ if (targets.some(function(layer) {
4450
+ return layer.width !== target.width || layer.height !== target.height || JSON.stringify(layer.position) !== JSON.stringify(target.position);
4451
+ })) {
4452
+ throw new TypeError('[dsl-ir:sources] A shared source instance requires one target region and stacking rank.');
4453
+ }
4454
+ var selectedIds = new Set();
4455
+ var sourceCompositionId = void 0;
4456
+ for(var _iterator = _create_for_of_iterator_helper_loose(targets), _step; !(_step = _iterator()).done;){
4457
+ var edited = _step.value;
4458
+ var sourceId = source.layers[edited.id];
4459
+ var baselineLayer = sourceLayerById.get(sourceId);
4460
+ var mapping = source.preservedData.layerMapping.layers[sourceId];
4461
+ if (edited.type !== 'particle' || (baselineLayer == null ? void 0 : baselineLayer.type) !== 'particle' || mapping == null || mapping.expandedSource != null || selectedIds.has(mapping.irLayerId) || sourceCompositionId != null && sourceCompositionId !== mapping.irCompositionId) {
4462
+ throw new TypeError("[dsl-ir:sources] '" + sourceId + "' must identify a distinct source particle layer.");
4463
+ }
4464
+ sourceCompositionId = mapping.irCompositionId;
4465
+ selectedIds.add(mapping.irLayerId);
4466
+ baselineLayer.name = edited.name;
4467
+ baselineLayer.particle = structuredClone(edited.particle);
4468
+ for(var _i = 0, _iter = [
4469
+ 'rotation',
4470
+ 'scale',
4471
+ 'anchor',
4472
+ 'opacity',
4473
+ 'time_range'
4474
+ ]; _i < _iter.length; _i++){
4475
+ var field = _iter[_i];
4476
+ if (edited[field] !== undefined) {
4477
+ var _obj;
4478
+ Object.assign(baselineLayer, (_obj = {}, _obj[field] = structuredClone(edited[field]), _obj));
4479
+ }
4480
+ }
4481
+ if (edited.time_range == null) {
4482
+ var current = baselineLayer;
4483
+ while(current != null){
4484
+ if (current.time_range != null) {
4485
+ current.time_range[1] = Math.max(current.time_range[1], document.meta.duration);
4486
+ }
4487
+ current = current.parent == null ? undefined : sourceLayerById.get(current.parent);
4488
+ }
4489
+ }
4490
+ }
4491
+ var converted = convertDSLToIRWithPreservedData(sourceDSL, source.preservedData, options);
4492
+ var composition = converted.scene.compositions.find(function(value) {
4493
+ return value.id === sourceCompositionId;
4494
+ });
4495
+ if (composition == null || composition.layers.some(function(layer) {
4496
+ return layer.type === 'precomp';
4497
+ })) {
4498
+ throw new TypeError('[dsl-ir:sources] Particle sources must have a self-contained composition.');
4499
+ }
4500
+ var originalParticles = composition.layers.filter(function(layer) {
4501
+ return layer.type === 'particle';
4502
+ });
4503
+ if (selectedIds.size !== 1 && selectedIds.size !== originalParticles.length) {
4504
+ throw new TypeError('[dsl-ir:sources] Bind either one particle system or the complete source group.');
4505
+ }
4506
+ composition.layers = selectSourceLayers(composition, selectedIds);
4507
+ var duration = Math.max(document.meta.duration, (composition.outPoint - composition.inPoint) / composition.frameRate);
4508
+ composition.outPoint = composition.inPoint + duration * composition.frameRate;
4509
+ host.outPoint = Math.max(host.outPoint, host.inPoint + duration * host.frameRate);
4510
+ var instanceScene = _extends({}, converted.scene, {
4511
+ entryCompositionId: composition.id,
4512
+ compositions: [
4513
+ composition
4514
+ ]
4515
+ });
4516
+ var idMap = namespaceSourceScene(instanceScene, source.preservedData.baselineScene);
4517
+ var wrapper = buildSourceInstance(target, host, composition, source.authoringBounds);
4518
+ ranks.set(wrapper.id, {
4519
+ rank: getLayerStackingOrder(target),
4520
+ tie: target.id
4521
+ });
4522
+ host.layers.push(wrapper);
4523
+ result.scene.compositions.push(composition);
4524
+ (_result_scene_assets = result.scene.assets).push.apply(_result_scene_assets, [].concat(instanceScene.assets));
4525
+ (_result_diagnostics = result.diagnostics).push.apply(_result_diagnostics, [].concat(converted.diagnostics));
4526
+ result.sourceInstances.push({
4527
+ sourceIndex: sourceIndex,
4528
+ idMap: idMap
4529
+ });
4530
+ }, _loop1 = function() {
4531
+ var layer = _step3.value;
4532
+ if (layer.type === 'precomp' && ranks.get(layer.id) != null && !hostRoots.includes(layer)) {
4533
+ layer.time.outFrame = host.outPoint;
4534
+ var source = result.scene.compositions.find(function(value) {
4535
+ return value.id === layer.content.compositionId;
4536
+ });
4537
+ if (source != null) {
4538
+ source.outPoint = source.inPoint + (host.outPoint - host.inPoint) / host.frameRate * source.frameRate;
4539
+ }
4540
+ }
4541
+ };
4542
+ var _result_scene_assets;
4543
+ var _options_particleSources;
4544
+ var _options_preservedData;
4545
+ var sources = (_options_particleSources = options.particleSources) != null ? _options_particleSources : [];
4546
+ var claimedIds = new Set();
4547
+ var targetById = new Map(document.layers.map(function(layer) {
4548
+ return [
4549
+ layer.id,
4550
+ layer
4551
+ ];
4552
+ }));
4553
+ for(var _iterator = _create_for_of_iterator_helper_loose(sources), _step; !(_step = _iterator()).done;){
4554
+ var source = _step.value;
4555
+ if (Object.keys(source.layers).length === 0) {
4556
+ throw new TypeError('[dsl-ir:sources] A particle source must bind at least one target layer.');
4557
+ }
4558
+ for(var _iterator1 = _create_for_of_iterator_helper_loose(Object.keys(source.layers)), _step1; !(_step1 = _iterator1()).done;){
4559
+ var id = _step1.value;
4560
+ var _ref;
4561
+ var _layer_children;
4562
+ var layer = targetById.get(id);
4563
+ if (claimedIds.has(id) || (layer == null ? void 0 : layer.type) !== 'particle' || layer.parent != null || ((_ref = (_layer_children = layer.children) == null ? void 0 : _layer_children.length) != null ? _ref : 0) !== 0 || layer.position.a) {
4564
+ throw new TypeError("[dsl-ir:sources] '" + id + "' must bind one root particle leaf with a static position.");
4565
+ }
4566
+ claimedIds.add(id);
4567
+ }
4568
+ }
4569
+ if (flattenLayers(document.layers).some(function(layer) {
4570
+ return layer.parent != null && claimedIds.has(layer.parent);
4571
+ })) {
4572
+ throw new TypeError('[dsl-ir:sources] A bound target must remain a root particle leaf.');
4573
+ }
4574
+ var ordinaryDSL = _extends({}, document, {
4575
+ layers: document.layers.filter(function(layer) {
4576
+ return !claimedIds.has(layer.id);
4577
+ })
4578
+ });
4579
+ var imageAssets = [];
4580
+ var fontAssets = [];
4581
+ var result = options.preservedData != null ? convertDSLToIRWithPreservedData(ordinaryDSL, options.preservedData, options) : {
4582
+ scene: adapter.createIRScene(assembleIRComposition(ordinaryDSL, convertDSLLayersToIR(ordinaryDSL, imageAssets, fontAssets))),
4583
+ diagnostics: []
4584
+ };
4585
+ (_result_scene_assets = result.scene.assets).push.apply(_result_scene_assets, [].concat(imageAssets, fontAssets));
4586
+ var host = adapter.getIRSceneEntryComposition(result.scene);
4587
+ var ordinaryMapping = (_options_preservedData = options.preservedData) == null ? void 0 : _options_preservedData.layerMapping.layers;
4588
+ var ordinaryRoots = ordinaryDSL.layers.filter(function(layer) {
4589
+ return layer.parent == null;
4590
+ }).sort(compareLayers);
4591
+ var hostRoots = host.layers.filter(function(layer) {
4592
+ return layer.parentId == null;
4593
+ });
4594
+ var ranks = new Map();
4595
+ ordinaryRoots.forEach(function(layer, index) {
4596
+ var _ordinaryMapping_layer_id, _hostRoots_index;
4597
+ var id = ordinaryMapping != null ? (_ordinaryMapping_layer_id = ordinaryMapping[layer.id]) == null ? void 0 : _ordinaryMapping_layer_id.irLayerId : (_hostRoots_index = hostRoots[index]) == null ? void 0 : _hostRoots_index.id;
4598
+ if (id != null) {
4599
+ ranks.set(id, {
4600
+ rank: getLayerStackingOrder(layer),
4601
+ tie: layer.id
4602
+ });
4603
+ }
4604
+ });
4605
+ result.sourceInstances = [];
4606
+ for(var _iterator2 = _create_for_of_iterator_helper_loose(sources.entries()), _step2; !(_step2 = _iterator2()).done;)_loop();
4607
+ for(var _iterator3 = _create_for_of_iterator_helper_loose(host.layers), _step3; !(_step3 = _iterator3()).done;)_loop1();
4608
+ var children = new Map();
4609
+ for(var _iterator4 = _create_for_of_iterator_helper_loose(host.layers), _step4; !(_step4 = _iterator4()).done;){
4610
+ var layer1 = _step4.value;
4611
+ if (layer1.parentId != null) {
4612
+ var _children_get;
4613
+ var group = (_children_get = children.get(layer1.parentId)) != null ? _children_get : [];
4614
+ group.push(layer1);
4615
+ children.set(layer1.parentId, group);
4616
+ }
4617
+ }
4618
+ var ordered = [];
4619
+ var append = function append1(layer) {
4620
+ var _children_get;
4621
+ ordered.push(layer);
4622
+ (_children_get = children.get(layer.id)) == null ? void 0 : _children_get.forEach(append);
4623
+ };
4624
+ host.layers.filter(function(layer) {
4625
+ return layer.parentId == null;
4626
+ }).sort(function(leftLayer, rightLayer) {
4627
+ var _ref, _ref1, _ref2, _ref3;
4628
+ var left = ranks.get(leftLayer.id);
4629
+ var right = ranks.get(rightLayer.id);
4630
+ return ((_ref = left == null ? void 0 : left.rank) != null ? _ref : 0) - ((_ref1 = right == null ? void 0 : right.rank) != null ? _ref1 : 0) || (options.preservedData != null ? 0 : ((_ref2 = left == null ? void 0 : left.tie) != null ? _ref2 : leftLayer.id).localeCompare((_ref3 = right == null ? void 0 : right.tie) != null ? _ref3 : rightLayer.id));
4631
+ }).forEach(append);
4632
+ host.layers = ordered;
4633
+ animationIr.assertIRScene(result.scene);
4634
+ return result;
4635
+ }
4636
+ function compareLayers(left, right) {
4637
+ return getLayerStackingOrder(left) - getLayerStackingOrder(right) || left.id.localeCompare(right.id);
4638
+ }
4639
+ function flattenLayers(layers) {
4640
+ return layers.flatMap(function(layer) {
4641
+ var _layer_children;
4642
+ return [].concat([
4643
+ layer
4644
+ ], flattenLayers((_layer_children = layer.children) != null ? _layer_children : []));
4645
+ });
4646
+ }
4647
+ function selectSourceLayers(composition, selected) {
4648
+ var _composition_layers_find;
4649
+ var byId = new Map(composition.layers.map(function(layer) {
4650
+ return [
4651
+ layer.id,
4652
+ layer
4653
+ ];
4654
+ }));
4655
+ var firstParticleId = (_composition_layers_find = composition.layers.find(function(layer) {
4656
+ return layer.type === 'particle';
4657
+ })) == null ? void 0 : _composition_layers_find.id;
4658
+ var owned = new Set();
4659
+ var retained = new Set();
4660
+ for(var _iterator = _create_for_of_iterator_helper_loose(composition.layers), _step; !(_step = _iterator()).done;){
4661
+ var layer = _step.value;
4662
+ var _ref;
4663
+ var _ancestors_find;
4664
+ var ancestors = [];
4665
+ var current = layer;
4666
+ while(current != null){
4667
+ ancestors.push(current);
4668
+ current = current.parentId == null ? undefined : byId.get(current.parentId);
4669
+ }
4670
+ var owner = (_ref = (_ancestors_find = ancestors.find(function(value) {
4671
+ return value.type === 'particle';
4672
+ })) == null ? void 0 : _ancestors_find.id) != null ? _ref : firstParticleId;
4673
+ if (owner != null && selected.has(owner)) {
4674
+ owned.add(layer.id);
4675
+ ancestors.forEach(function(value) {
4676
+ return retained.add(value.id);
4677
+ });
4678
+ }
4679
+ }
4680
+ return composition.layers.filter(function(layer) {
4681
+ return retained.has(layer.id);
4682
+ }).map(function(layer) {
4683
+ return owned.has(layer.id) ? layer : {
4684
+ id: layer.id,
4685
+ name: layer.name,
4686
+ type: 'null',
4687
+ time: layer.time,
4688
+ visible: layer.visible,
4689
+ parentId: layer.parentId,
4690
+ transform: layer.transform
4691
+ };
4158
4692
  });
4693
+ }
4694
+ function namespaceSourceScene(scene, baseline) {
4695
+ var ids = [].concat(baseline.assets.map(function(asset) {
4696
+ return asset.id;
4697
+ }), baseline.compositions.flatMap(function(composition) {
4698
+ return [].concat([
4699
+ composition.id
4700
+ ], composition.layers.map(function(layer) {
4701
+ return layer.id;
4702
+ }));
4703
+ }), scene.assets.map(function(asset) {
4704
+ return asset.id;
4705
+ }));
4706
+ var idMap = Object.fromEntries(ids.map(function(id) {
4707
+ return [
4708
+ id,
4709
+ createId()
4710
+ ];
4711
+ }));
4712
+ var fields = new Set([
4713
+ 'id',
4714
+ 'entryCompositionId',
4715
+ 'compositionId',
4716
+ 'parentId',
4717
+ 'assetId',
4718
+ 'fontAssetId',
4719
+ 'sourceLayerId'
4720
+ ]);
4721
+ var remap = function remap1(value) {
4722
+ if (Array.isArray(value)) {
4723
+ value.forEach(remap);
4724
+ } else if (value != null && (typeof value === "undefined" ? "undefined" : _type_of(value)) === 'object') {
4725
+ for(var _iterator = _create_for_of_iterator_helper_loose(Object.entries(value)), _step; !(_step = _iterator()).done;){
4726
+ var _step_value = _step.value, key = _step_value[0], entry = _step_value[1];
4727
+ if (fields.has(key) && typeof entry === 'string' && idMap[entry] != null) {
4728
+ var _obj;
4729
+ Object.assign(value, (_obj = {}, _obj[key] = idMap[entry], _obj));
4730
+ } else {
4731
+ remap(entry);
4732
+ }
4733
+ }
4734
+ }
4735
+ };
4736
+ remap(scene);
4737
+ return idMap;
4738
+ }
4739
+ function buildSourceInstance(target, host, source, authoringBounds) {
4740
+ var _ref = authoringBounds != null ? authoringBounds : [
4741
+ 0,
4742
+ 0,
4743
+ source.width,
4744
+ source.height
4745
+ ], left = _ref[0], top = _ref[1], width = _ref[2], height = _ref[3];
4746
+ var position = target.position.a ? undefined : target.position.value;
4747
+ if (!Array.isArray(position) || position.length !== 3 || target.width <= 0 || target.height <= 0 || ![
4748
+ left,
4749
+ top,
4750
+ width,
4751
+ height
4752
+ ].every(Number.isFinite) || left < 0 || top < 0 || width <= 0 || height <= 0 || left + width > source.width || top + height > source.height) {
4753
+ throw new TypeError('[dsl-ir:sources] Invalid source bounds or target region.');
4754
+ }
4755
+ var scale = Math.min(target.width / width, target.height / height);
4756
+ return {
4757
+ id: createId(),
4758
+ name: target.name,
4759
+ type: 'precomp',
4760
+ visible: true,
4761
+ time: {
4762
+ inFrame: host.inPoint,
4763
+ outFrame: host.outPoint,
4764
+ sourceStartFrame: source.inPoint
4765
+ },
4766
+ content: {
4767
+ compositionId: source.id,
4768
+ clipToBounds: false,
4769
+ endBehavior: animationIr.IREndBehavior.Freeze
4770
+ },
4771
+ transform: {
4772
+ position: {
4773
+ type: 'static',
4774
+ value: [
4775
+ position[0] - (left + width / 2 - source.width / 2) * scale,
4776
+ position[1] - (top + height / 2 - source.height / 2) * scale,
4777
+ 0
4778
+ ]
4779
+ },
4780
+ scale: {
4781
+ type: 'static',
4782
+ value: [
4783
+ scale,
4784
+ scale,
4785
+ 1
4786
+ ]
4787
+ }
4788
+ }
4789
+ };
4790
+ }
4791
+
4792
+ /**
4793
+ * Convert DSL into a new IR scene, or apply edits to its paired preserved snapshot.
4794
+ * Inputs are not mutated. Recoverable unsupported edits produce diagnostics.
4795
+ * Invalid DSL throws DSLValidationError; IR validation, invalid sidecars, unsafe
4796
+ * depth edits, and caller callbacks may throw other errors.
4797
+ */ function convertDSLToIR(composition, options) {
4798
+ if (options === void 0) options = {};
4799
+ var _ref;
4800
+ var _options_particleSources;
4801
+ var validationResult = validateDSL(composition);
4159
4802
  if (validationResult.result === 'fail') {
4160
4803
  throw DSLValidationError.fromValidationResult(validationResult);
4161
4804
  }
4162
4805
  var diagnostics = reportDSLValidationWarnings(validationResult.warnings, options);
4806
+ if (((_ref = (_options_particleSources = options.particleSources) == null ? void 0 : _options_particleSources.length) != null ? _ref : 0) > 0) {
4807
+ var result = convertDSLWithParticleSources(composition, options);
4808
+ return _extends({}, result, {
4809
+ diagnostics: [].concat(diagnostics, result.diagnostics)
4810
+ });
4811
+ }
4163
4812
  if (options.preservedData != null) {
4164
4813
  var preservedResult = convertDSLToIRWithPreservedData(composition, options.preservedData, options);
4165
4814
  animationIr.assertIRScene(preservedResult.scene);
@@ -4202,7 +4851,13 @@ function reportDSLValidationWarnings(warnings, options) {
4202
4851
  });
4203
4852
  }
4204
4853
 
4205
- function parseParticleShape(value) {
4854
+ /**
4855
+ * Project a GE-style emitter shape (numeric type 0..9) into read-only DSL context.
4856
+ * Shape names come from the type tag; missing arcMode defaults to 0. Other numeric
4857
+ * fields retain source units. Unsupported types or invalid required fields return
4858
+ * undefined; invalid optional fields are omitted. Direction expressions are cloned,
4859
+ * so their payloads must be structured-cloneable. No geometry is written into IR.
4860
+ */ function parseParticleShape(value) {
4206
4861
  if (!isRecord(value) || !isIntegerInRange(value['type'], 0, 9)) {
4207
4862
  return undefined;
4208
4863
  }
@@ -4417,6 +5072,7 @@ function isRecord(value) {
4417
5072
  }
4418
5073
 
4419
5074
  exports.DSLValidationError = DSLValidationError;
5075
+ exports.ValueType = ValueType;
4420
5076
  exports.convertDSLToIR = convertDSLToIR;
4421
5077
  exports.convertIRToDSL = convertIRToDSL;
4422
5078
  exports.parseParticleShape = parseParticleShape;