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