@openfairygui/cli 0.2.0-alpha.34 → 0.2.0-alpha.36

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.
Files changed (2) hide show
  1. package/dist/cli.mjs +757 -523
  2. package/package.json +4 -4
package/dist/cli.mjs CHANGED
@@ -1250,13 +1250,11 @@ var Root = class extends ExtensibleProperty {
1250
1250
  return [...this.get("branches")];
1251
1251
  }
1252
1252
  setBranches(branches) {
1253
- return this.set("branches", [...branches]);
1253
+ return this.set("branches", [...new Set(branches)].sort((left, right) => left.localeCompare(right)));
1254
1254
  }
1255
1255
  addBranch(branch) {
1256
1256
  if (!branch) return this;
1257
- const branches = this.listBranches();
1258
- if (!branches.includes(branch)) branches.push(branch);
1259
- return this.set("branches", branches);
1257
+ return this.setBranches([...this.listBranches(), branch]);
1260
1258
  }
1261
1259
  getSettings() {
1262
1260
  return this.get("settings");
@@ -1317,6 +1315,19 @@ var Package = class extends ExtensibleProperty {
1317
1315
  publishPackageCount: 0,
1318
1316
  genCode: false,
1319
1317
  codePath: "",
1318
+ sourceAtlasSettings: {
1319
+ useGlobal: true,
1320
+ maxSize: 2048,
1321
+ sizeOption: "pot",
1322
+ forceSquare: false,
1323
+ allowRotation: false,
1324
+ paging: true,
1325
+ extractAlpha: false,
1326
+ maxIndex: 10,
1327
+ atlases: [],
1328
+ excludedResourceIds: []
1329
+ },
1330
+ branchNames: [],
1320
1331
  resourceFolders: [],
1321
1332
  resources: new RefSet(),
1322
1333
  atlases: new RefSet(),
@@ -1377,6 +1388,31 @@ var Package = class extends ExtensibleProperty {
1377
1388
  setCodePath(path) {
1378
1389
  return this.set("codePath", path);
1379
1390
  }
1391
+ getSourceAtlasSettings() {
1392
+ const settings = this.get("sourceAtlasSettings");
1393
+ return {
1394
+ ...settings,
1395
+ atlases: settings.atlases.map((atlas) => ({ ...atlas })),
1396
+ excludedResourceIds: [...settings.excludedResourceIds]
1397
+ };
1398
+ }
1399
+ setSourceAtlasSettings(settings) {
1400
+ return this.set("sourceAtlasSettings", {
1401
+ ...settings,
1402
+ atlases: settings.atlases.map((atlas) => ({ ...atlas })),
1403
+ excludedResourceIds: [...settings.excludedResourceIds]
1404
+ });
1405
+ }
1406
+ listBranchNames() {
1407
+ return [...this.get("branchNames")];
1408
+ }
1409
+ setBranchNames(names) {
1410
+ return this.set("branchNames", [...names]);
1411
+ }
1412
+ addBranchName(name) {
1413
+ if (!this.get("branchNames").includes(name)) this.set("branchNames", [...this.get("branchNames"), name]);
1414
+ return this;
1415
+ }
1380
1416
  listResourceFolders() {
1381
1417
  return this.get("resourceFolders").map((folder) => ({ ...folder }));
1382
1418
  }
@@ -2268,6 +2304,7 @@ var Component = class extends ExtensibleProperty {
2268
2304
  wholeNumbers: false,
2269
2305
  changeOnClick: true,
2270
2306
  fixedGripSize: false,
2307
+ autoClearItems: false,
2271
2308
  opaque: true,
2272
2309
  customProperties: [],
2273
2310
  childrenRenderOrder: ChildrenRenderOrder.Ascent,
@@ -2651,6 +2688,12 @@ var Component = class extends ExtensibleProperty {
2651
2688
  setFixedGripSize(v) {
2652
2689
  return this.set("fixedGripSize", v);
2653
2690
  }
2691
+ getAutoClearItems() {
2692
+ return this.get("autoClearItems");
2693
+ }
2694
+ setAutoClearItems(v) {
2695
+ return this.set("autoClearItems", v);
2696
+ }
2654
2697
  getOpaque() {
2655
2698
  return this.get("opaque");
2656
2699
  }
@@ -4901,10 +4944,12 @@ var GComponent = class extends GObject {
4901
4944
  instancePromptText: "",
4902
4945
  instanceSelectionController: "",
4903
4946
  instanceVisibleItemCount: 0,
4947
+ instanceAutoClearItems: false,
4904
4948
  instanceValue: 0,
4905
4949
  instanceMax: 0,
4906
4950
  instanceMin: 0,
4907
- instanceComboItems: []
4951
+ instanceComboItems: [],
4952
+ propertyOverrides: []
4908
4953
  });
4909
4954
  }
4910
4955
  getComponentProp(key) {
@@ -5200,6 +5245,12 @@ var GComponent = class extends GObject {
5200
5245
  setInstanceVisibleItemCount(v) {
5201
5246
  return this.setComponentProp("instanceVisibleItemCount", v);
5202
5247
  }
5248
+ getInstanceAutoClearItems() {
5249
+ return this.getComponentProp("instanceAutoClearItems");
5250
+ }
5251
+ setInstanceAutoClearItems(v) {
5252
+ return this.setComponentProp("instanceAutoClearItems", v);
5253
+ }
5203
5254
  getInstanceValue() {
5204
5255
  return this.getComponentProp("instanceValue");
5205
5256
  }
@@ -5224,6 +5275,12 @@ var GComponent = class extends GObject {
5224
5275
  setInstanceComboItems(v) {
5225
5276
  return this.set("instanceComboItems", v);
5226
5277
  }
5278
+ getPropertyOverrides() {
5279
+ return this.getComponentProp("propertyOverrides").map((property) => ({ ...property }));
5280
+ }
5281
+ setPropertyOverrides(v) {
5282
+ return this.setComponentProp("propertyOverrides", v.map((property) => ({ ...property })));
5283
+ }
5227
5284
  getMargin() {
5228
5285
  return this.getComponentProp("margin");
5229
5286
  }
@@ -5291,6 +5348,7 @@ var GListBase = class extends GObject {
5291
5348
  clipSoftness: [0, 0],
5292
5349
  scrollItemToViewOnClick: true,
5293
5350
  foldInvisibleItems: false,
5351
+ autoClearItems: false,
5294
5352
  listItems: [],
5295
5353
  pageController: "",
5296
5354
  controllerOverrides: "",
@@ -5583,6 +5641,12 @@ var GListBase = class extends GObject {
5583
5641
  setFoldInvisibleItems(v) {
5584
5642
  return this.setListProp("foldInvisibleItems", v);
5585
5643
  }
5644
+ getAutoClearItems() {
5645
+ return this.getListProp("autoClearItems");
5646
+ }
5647
+ setAutoClearItems(v) {
5648
+ return this.setListProp("autoClearItems", v);
5649
+ }
5586
5650
  getListItems() {
5587
5651
  return this.get("listItems");
5588
5652
  }
@@ -9548,65 +9612,89 @@ function ensureArray(v) {
9548
9612
  }
9549
9613
  //#endregion
9550
9614
  //#region ../core/src/utils/jta-parser.ts
9551
- /**
9552
- * Parser for FairyGUI `.jta` animation files.
9553
- *
9554
- * Extracts individual frame textures (PNG/JPG byte arrays) from the binary format.
9555
- * Used by the atlas packer to include MovieClip frames in texture atlases.
9556
- *
9557
- * @internal
9558
- */
9559
9615
  const FILE_MARK = "yytou";
9616
+ var JtaCursor = class {
9617
+ offset = 0;
9618
+ view;
9619
+ constructor(data) {
9620
+ this.data = data;
9621
+ this.view = new DataView(data.buffer, data.byteOffset, data.byteLength);
9622
+ }
9623
+ readUint8(label) {
9624
+ this.ensure(1, label);
9625
+ return this.view.getUint8(this.offset++);
9626
+ }
9627
+ readInt8(label) {
9628
+ this.ensure(1, label);
9629
+ return this.view.getInt8(this.offset++);
9630
+ }
9631
+ readUint16(label) {
9632
+ this.ensure(2, label);
9633
+ const value = this.view.getUint16(this.offset, false);
9634
+ this.offset += 2;
9635
+ return value;
9636
+ }
9637
+ readInt16(label) {
9638
+ this.ensure(2, label);
9639
+ const value = this.view.getInt16(this.offset, false);
9640
+ this.offset += 2;
9641
+ return value;
9642
+ }
9643
+ readInt32(label) {
9644
+ this.ensure(4, label);
9645
+ const value = this.view.getInt32(this.offset, false);
9646
+ this.offset += 4;
9647
+ return value;
9648
+ }
9649
+ readBytes(length, label) {
9650
+ if (!Number.isInteger(length) || length < 0) throw new Error(`Invalid .jta file: negative ${label} length`);
9651
+ this.ensure(length, label);
9652
+ const value = this.data.subarray(this.offset, this.offset + length);
9653
+ this.offset += length;
9654
+ return value;
9655
+ }
9656
+ skip(length, label) {
9657
+ this.ensure(length, label);
9658
+ this.offset += length;
9659
+ }
9660
+ ensure(length, label) {
9661
+ if (this.offset + length > this.data.byteLength) throw new Error(`Invalid .jta file: truncated ${label}`);
9662
+ }
9663
+ };
9560
9664
  /**
9561
9665
  * Parse a `.jta` binary buffer into frame and texture data.
9562
9666
  */
9563
9667
  function parseJta(data) {
9564
- const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
9565
- let pos = 0;
9566
- const markLen = view.getUint16(pos);
9567
- pos += 2;
9568
- if (pos + markLen > data.length) throw new Error("Invalid .jta file: truncated file mark");
9569
- const mark = new TextDecoder("utf-8").decode(data.subarray(pos, pos + markLen));
9570
- pos += markLen;
9668
+ const cursor = new JtaCursor(data);
9669
+ const markLen = cursor.readUint16("file mark length");
9670
+ const mark = new TextDecoder("utf-8").decode(cursor.readBytes(markLen, "file mark"));
9571
9671
  if (mark !== FILE_MARK) throw new Error(`Invalid .jta file: expected "${FILE_MARK}", got "${mark}"`);
9572
- const version = view.getInt32(pos);
9573
- pos += 4;
9672
+ const version = cursor.readInt32("version");
9574
9673
  if (version < 100 || version > 102) throw new Error(`Unsupported .jta version: ${version}`);
9575
- let fps = view.getInt8(pos);
9576
- pos += 1;
9674
+ let fps = cursor.readInt8("fps");
9675
+ if (fps < 0) throw new Error(`Invalid .jta file: negative fps ${fps}`);
9577
9676
  if (fps === 0) fps = 24;
9578
- pos += 3;
9677
+ cursor.skip(3, "reserved header");
9579
9678
  let boundsWidth = 0, boundsHeight = 0;
9580
9679
  if (version >= 102) {
9581
- pos += 4;
9582
- boundsWidth = view.getUint16(pos);
9583
- pos += 2;
9584
- boundsHeight = view.getUint16(pos);
9585
- pos += 2;
9586
- }
9587
- const speed = view.getUint8(pos);
9588
- pos += 1;
9589
- const repeatDelay = view.getUint8(pos);
9590
- pos += 1;
9591
- const swing = view.getInt8(pos) === 1;
9592
- pos += 1;
9593
- const frameCount = view.getInt16(pos);
9594
- pos += 2;
9680
+ cursor.skip(4, "bounds origin");
9681
+ boundsWidth = cursor.readUint16("bounds width");
9682
+ boundsHeight = cursor.readUint16("bounds height");
9683
+ }
9684
+ const speed = cursor.readUint8("speed");
9685
+ const repeatDelay = cursor.readUint8("repeat delay");
9686
+ const swing = cursor.readInt8("swing") === 1;
9687
+ const frameCount = cursor.readInt16("frame count");
9595
9688
  if (frameCount < 0) throw new Error("Invalid .jta file: negative frame count");
9596
9689
  const frames = [];
9597
9690
  for (let i = 0; i < frameCount; i++) {
9598
- const delay = view.getInt16(pos);
9599
- pos += 2;
9600
- const rectX = view.getInt16(pos);
9601
- pos += 2;
9602
- const rectY = view.getInt16(pos);
9603
- pos += 2;
9604
- const rectWidth = view.getInt16(pos);
9605
- pos += 2;
9606
- const rectHeight = view.getInt16(pos);
9607
- pos += 2;
9608
- const textureIndex = view.getInt16(pos);
9609
- pos += 2;
9691
+ const delay = cursor.readInt16(`frame ${i} delay`);
9692
+ const rectX = cursor.readInt16(`frame ${i} rect x`);
9693
+ const rectY = cursor.readInt16(`frame ${i} rect y`);
9694
+ const rectWidth = cursor.readInt16(`frame ${i} rect width`);
9695
+ const rectHeight = cursor.readInt16(`frame ${i} rect height`);
9696
+ const textureIndex = cursor.readInt16(`frame ${i} texture index`);
9697
+ if (delay < 0 || rectWidth < 0 || rectHeight < 0) throw new Error(`Invalid .jta file: frame ${i} has negative delay or dimensions`);
9610
9698
  frames.push({
9611
9699
  delay,
9612
9700
  rectX,
@@ -9616,27 +9704,17 @@ function parseJta(data) {
9616
9704
  textureIndex
9617
9705
  });
9618
9706
  }
9619
- const textureCount = view.getInt16(pos);
9620
- pos += 2;
9707
+ const textureCount = cursor.readInt16("texture count");
9621
9708
  if (textureCount < 0) throw new Error("Invalid .jta file: negative texture count");
9622
9709
  const textures = [];
9623
9710
  for (let i = 0; i < textureCount; i++) {
9624
- const rawLen = view.getInt32(pos);
9625
- pos += 4;
9626
- if (rawLen < 0 || pos + rawLen > data.length) throw new Error("Invalid .jta file: truncated texture data");
9627
- let raw;
9628
- if (rawLen > 0) {
9629
- raw = data.subarray(pos, pos + rawLen);
9630
- pos += rawLen;
9631
- } else raw = new Uint8Array(0);
9632
- textures.push({ raw });
9711
+ const rawLen = cursor.readInt32(`texture ${i} length`);
9712
+ textures.push({ raw: cursor.readBytes(rawLen, `texture ${i} data`) });
9633
9713
  }
9634
9714
  if (version === 101) {
9635
- pos += 4;
9636
- boundsWidth = view.getUint16(pos);
9637
- pos += 2;
9638
- boundsHeight = view.getUint16(pos);
9639
- pos += 2;
9715
+ cursor.skip(4, "bounds origin");
9716
+ boundsWidth = cursor.readUint16("bounds width");
9717
+ boundsHeight = cursor.readUint16("bounds height");
9640
9718
  } else if (version === 100) {
9641
9719
  let minX = Number.POSITIVE_INFINITY;
9642
9720
  let minY = Number.POSITIVE_INFINITY;
@@ -9654,6 +9732,10 @@ function parseJta(data) {
9654
9732
  boundsHeight = maxY - Math.min(minY, 0);
9655
9733
  }
9656
9734
  }
9735
+ for (let index = 0; index < frames.length; index += 1) {
9736
+ const textureIndex = frames[index].textureIndex;
9737
+ if (textureIndex < -1 || textureIndex >= textures.length) throw new Error(`Invalid .jta file: frame ${index} texture index ${textureIndex} is outside -1..${textures.length - 1}`);
9738
+ }
9657
9739
  return {
9658
9740
  version,
9659
9741
  fps,
@@ -9666,211 +9748,37 @@ function parseJta(data) {
9666
9748
  textures
9667
9749
  };
9668
9750
  }
9669
- function tryReadJtaSize(data) {
9670
- try {
9671
- const parsed = parseJta(data);
9672
- return {
9751
+ /** Converts parsed JTA frame units to the millisecond-based Document/UAM model. */
9752
+ function deriveMovieClipModel(parsed) {
9753
+ const millisecondsPerFrame = 1e3 / parsed.fps;
9754
+ return {
9755
+ dimensions: {
9673
9756
  width: parsed.boundsWidth,
9674
9757
  height: parsed.boundsHeight
9675
- };
9676
- } catch {
9677
- return null;
9678
- }
9679
- }
9680
- //#endregion
9681
- //#region ../core/src/document.ts
9682
- /**
9683
- * Wraps a FairyGUI project and its resources for easier modification.
9684
- *
9685
- * Documents manage FairyGUI assets and the relationships among dependencies using an
9686
- * internal property graph. A new resource is created by calling 'create' methods on the
9687
- * document. Resources are destroyed by calling {@link Property.dispose}().
9688
- *
9689
- * Usage:
9690
- *
9691
- * ```ts
9692
- * const document = new Document();
9693
- * const pkg = document.createPackage('MyPackage');
9694
- * const component = document.createComponent('Button');
9695
- * const image = document.createGImage('bg');
9696
- * component.addChild(image);
9697
- * pkg.addResource(component);
9698
- * ```
9699
- *
9700
- * @category Documents
9701
- */
9702
- var Document = class Document {
9703
- _graph = new Graph();
9704
- _root = new Root(this._graph);
9705
- _logger = Logger.DEFAULT_INSTANCE;
9706
- _projectDir = "";
9707
- static _GRAPH_DOCUMENTS = /* @__PURE__ */ new WeakMap();
9708
- static fromGraph(graph) {
9709
- return Document._GRAPH_DOCUMENTS.get(graph) || null;
9710
- }
9711
- constructor() {
9712
- Document._GRAPH_DOCUMENTS.set(this._graph, this);
9713
- }
9714
- getRoot() {
9715
- return this._root;
9716
- }
9717
- /** @hidden */
9718
- getGraph() {
9719
- return this._graph;
9720
- }
9721
- getLogger() {
9722
- return this._logger;
9723
- }
9724
- setLogger(logger) {
9725
- this._logger = logger;
9726
- return this;
9727
- }
9728
- getProjectDir() {
9729
- return this._projectDir;
9730
- }
9731
- setProjectDir(projectDir) {
9732
- this._projectDir = projectDir;
9733
- return this;
9734
- }
9735
- async transform(...transforms) {
9736
- const stack = transforms.map((fn) => fn.name);
9737
- for (const transform of transforms) await transform(this, { stack });
9738
- return this;
9739
- }
9740
- /****** Extension factory methods ******/
9741
- createExtension(ctor) {
9742
- const extensionName = ctor.EXTENSION_NAME;
9743
- return this.getRoot().listExtensionsUsed().find((ext) => ext.extensionName === extensionName) || new ctor(this);
9744
- }
9745
- /****** Property factory methods ******/
9746
- createPackage(name = "") {
9747
- return new Package(this._graph, name);
9748
- }
9749
- createImageResource(name = "") {
9750
- return new ImageResource(this._graph, name);
9751
- }
9752
- createSoundResource(name = "") {
9753
- return new SoundResource(this._graph, name);
9754
- }
9755
- createMiscResource(name = "") {
9756
- return new MiscResource(this._graph, name);
9757
- }
9758
- createFontResource(name = "") {
9759
- return new FontResource(this._graph, name);
9760
- }
9761
- createMovieClipResource(name = "") {
9762
- return new MovieClipResource(this._graph, name);
9763
- }
9764
- createSpineResource(name = "") {
9765
- return new SpineResource(this._graph, name);
9766
- }
9767
- createDragonBonesResource(name = "") {
9768
- return new DragonBonesResource(this._graph, name);
9769
- }
9770
- createComponent(name = "") {
9771
- return new Component(this._graph, name);
9772
- }
9773
- createAtlas(name = "") {
9774
- return new Atlas(this._graph, name);
9775
- }
9776
- createSprite(name = "") {
9777
- return new Sprite(this._graph, name);
9778
- }
9779
- createBuffer(name = "") {
9780
- return new FairyBuffer(this._graph, name);
9781
- }
9782
- createGImage(name = "") {
9783
- return new GImage(this._graph, name);
9784
- }
9785
- createGTextField(name = "") {
9786
- return new GTextField(this._graph, name);
9787
- }
9788
- createGRichTextField(name = "") {
9789
- return new GRichTextField(this._graph, name);
9790
- }
9791
- createGTextInput(name = "") {
9792
- return new GTextInput(this._graph, name);
9793
- }
9794
- createGGraph(name = "") {
9795
- return new GGraph(this._graph, name);
9796
- }
9797
- createGGroup(name = "") {
9798
- return new GGroup(this._graph, name);
9799
- }
9800
- createGLoader(name = "") {
9801
- return new GLoader(this._graph, name);
9802
- }
9803
- createGLoader3D(name = "") {
9804
- return new GLoader3D(this._graph, name);
9805
- }
9806
- createGMovieClip(name = "") {
9807
- return new GMovieClip(this._graph, name);
9808
- }
9809
- createGComponent(name = "") {
9810
- return new GComponent(this._graph, name);
9811
- }
9812
- createGList(name = "") {
9813
- return new GList(this._graph, name);
9814
- }
9815
- createGTree(name = "") {
9816
- return new GTree(this._graph, name);
9817
- }
9818
- createGButton(name = "") {
9819
- return new GButton(this._graph, name);
9820
- }
9821
- createGLabel(name = "") {
9822
- return new GLabel(this._graph, name);
9823
- }
9824
- createGComboBox(name = "") {
9825
- return new GComboBox(this._graph, name);
9826
- }
9827
- createGProgressBar(name = "") {
9828
- return new GProgressBar(this._graph, name);
9829
- }
9830
- createGSlider(name = "") {
9831
- return new GSlider(this._graph, name);
9832
- }
9833
- createGScrollBar(name = "") {
9834
- return new GScrollBar(this._graph, name);
9835
- }
9836
- createController(name = "") {
9837
- return new Controller(this._graph, name);
9838
- }
9839
- createControllerPage(name = "") {
9840
- return new ControllerPage(this._graph, name);
9841
- }
9842
- createControllerAction(name = "") {
9843
- return new ControllerAction(this._graph, name);
9844
- }
9845
- createTransition(name = "") {
9846
- return new Transition(this._graph, name);
9847
- }
9848
- createTransitionItem(name = "") {
9849
- return new TransitionItem(this._graph, name);
9850
- }
9851
- createGear(name = "") {
9852
- return new Gear(this._graph, name);
9853
- }
9854
- createFontGlyph(name = "") {
9855
- return new FontGlyph(this._graph, name);
9856
- }
9857
- createMovieFrame(name = "") {
9858
- return new MovieFrame(this._graph, name);
9859
- }
9860
- };
9861
- //#endregion
9862
- //#region ../core/src/utils/resource-folder.ts
9863
- function normalizeResourceFolderPath(value) {
9864
- const segments = value.replace(/\\/g, "/").split("/").filter(Boolean);
9865
- return segments.length > 0 ? `/${segments.join("/")}/` : "/";
9758
+ },
9759
+ interval: Math.trunc(millisecondsPerFrame * (parsed.speed || 1)),
9760
+ repeatDelay: Math.trunc(millisecondsPerFrame * parsed.repeatDelay),
9761
+ swing: parsed.swing,
9762
+ frames: parsed.frames.map((frame) => ({
9763
+ rectX: frame.rectX,
9764
+ rectY: frame.rectY,
9765
+ rectWidth: frame.rectWidth,
9766
+ rectHeight: frame.rectHeight,
9767
+ addDelay: Math.trunc(millisecondsPerFrame * frame.delay),
9768
+ textureIndex: frame.textureIndex
9769
+ }))
9770
+ };
9866
9771
  }
9867
- function resourceFolderParentPath(value) {
9868
- const segments = normalizeResourceFolderPath(value).split("/").filter(Boolean);
9869
- segments.pop();
9870
- return segments.length > 0 ? `/${segments.join("/")}/` : "/";
9772
+ /** Parses JTA bytes and derives the Document/UAM MovieClip model. */
9773
+ function deriveMovieClipModelFromJta(data) {
9774
+ return deriveMovieClipModel(parseJta(data));
9871
9775
  }
9872
- function resourceFolderName(value) {
9873
- return normalizeResourceFolderPath(value).split("/").filter(Boolean).pop() ?? "";
9776
+ /** Applies a fully parsed JTA model without changing XML-owned MovieClip settings such as smoothing. */
9777
+ function applyDerivedMovieClipModel(doc, resource, model) {
9778
+ const frames = model.frames.map((frame, index) => doc.createMovieFrame(`${resource.getId()}_${index}`).setRectX(frame.rectX).setRectY(frame.rectY).setRectWidth(frame.rectWidth).setRectHeight(frame.rectHeight).setAddDelay(frame.addDelay).setSpriteId(""));
9779
+ for (const frame of resource.listFrames()) resource.removeFrame(frame);
9780
+ resource.setWidth(model.dimensions.width).setHeight(model.dimensions.height).setInterval(model.interval).setRepeatDelay(model.repeatDelay).setSwing(model.swing);
9781
+ for (const frame of frames) resource.addFrame(frame);
9874
9782
  }
9875
9783
  //#endregion
9876
9784
  //#region ../../node_modules/.pnpm/jpeg-js@0.4.4/node_modules/jpeg-js/lib/encoder.js
@@ -15743,7 +15651,7 @@ var inflateRaw_1 = inflateRaw;
15743
15651
  //#endregion
15744
15652
  //#region ../core/src/utils/image-info.ts
15745
15653
  var import_jpeg_js = require_jpeg_js();
15746
- const PNG_SIGNATURE$1 = [
15654
+ const PNG_SIGNATURE = [
15747
15655
  137,
15748
15656
  80,
15749
15657
  78,
@@ -15889,9 +15797,9 @@ function validatePngImageData(parts, width, height, bitDepth, colorType, interla
15889
15797
  }
15890
15798
  }
15891
15799
  function readPngInfo(data, validateImageData) {
15892
- if (data.length < (validateImageData ? 45 : 33) || PNG_SIGNATURE$1.some((byte, index) => data[index] !== byte)) return null;
15800
+ if (data.length < (validateImageData ? 45 : 33) || PNG_SIGNATURE.some((byte, index) => data[index] !== byte)) return null;
15893
15801
  const view = new DataView(data.buffer, data.byteOffset, data.byteLength);
15894
- let offset = PNG_SIGNATURE$1.length;
15802
+ let offset = PNG_SIGNATURE.length;
15895
15803
  let width = 0;
15896
15804
  let height = 0;
15897
15805
  let bitDepth = 0;
@@ -16118,30 +16026,237 @@ function readJpegInfo(data, decodePixels) {
16118
16026
  }
16119
16027
  return null;
16120
16028
  }
16029
+ /**
16030
+ * Strictly validate a complete PNG or JPEG source and return its decoded dimensions.
16031
+ *
16032
+ * Unlike `probeRasterImageDimensions`, this validates PNG image data and fully
16033
+ * decodes JPEG pixels. Unsupported formats and malformed inputs return `null`.
16034
+ */
16035
+ function probeRasterImage(data) {
16036
+ if (asyncProbeResults.has(data)) return asyncProbeResults.get(data) ?? null;
16037
+ if (data.byteLength > MAX_SYNC_RASTER_BYTES) return null;
16038
+ return readPngInfo(data, true) ?? readJpegInfo(data, true);
16039
+ }
16121
16040
  function probeRasterImageDimensions(data) {
16122
16041
  if (asyncProbeResults.has(data)) return asyncProbeResults.get(data) ?? null;
16123
16042
  if (data.byteLength > MAX_SYNC_RASTER_BYTES) return null;
16124
16043
  return readPngInfo(data, false) ?? readJpegInfo(data, false);
16125
16044
  }
16126
16045
  //#endregion
16127
- //#region ../core/src/io/project-xml-protocol.ts
16128
- const mergeAttrs = (...parts) => Object.assign({}, ...parts);
16129
- const mergeChildren = (...parts) => Object.assign({}, ...parts);
16130
- const mergeContainers = (...parts) => Object.assign({}, ...parts);
16131
- const defineContainer = (items) => ({
16132
- kind: "orderedVariants",
16133
- items
16134
- });
16135
- const defineNode = (attrs, children, containers) => ({
16136
- attrs,
16137
- ...children ? { children } : {},
16138
- ...containers ? { containers } : {}
16046
+ //#region ../core/src/document.ts
16047
+ /**
16048
+ * Wraps a FairyGUI project and its resources for easier modification.
16049
+ *
16050
+ * Documents manage FairyGUI assets and the relationships among dependencies using an
16051
+ * internal property graph. A new resource is created by calling 'create' methods on the
16052
+ * document. Resources are destroyed by calling {@link Property.dispose}().
16053
+ *
16054
+ * Usage:
16055
+ *
16056
+ * ```ts
16057
+ * const document = new Document();
16058
+ * const pkg = document.createPackage('MyPackage');
16059
+ * const component = document.createComponent('Button');
16060
+ * const image = document.createGImage('bg');
16061
+ * component.addChild(image);
16062
+ * pkg.addResource(component);
16063
+ * ```
16064
+ *
16065
+ * @category Documents
16066
+ */
16067
+ var Document = class Document {
16068
+ _graph = new Graph();
16069
+ _root = new Root(this._graph);
16070
+ _logger = Logger.DEFAULT_INSTANCE;
16071
+ _projectDir = "";
16072
+ static _GRAPH_DOCUMENTS = /* @__PURE__ */ new WeakMap();
16073
+ static fromGraph(graph) {
16074
+ return Document._GRAPH_DOCUMENTS.get(graph) || null;
16075
+ }
16076
+ constructor() {
16077
+ Document._GRAPH_DOCUMENTS.set(this._graph, this);
16078
+ }
16079
+ getRoot() {
16080
+ return this._root;
16081
+ }
16082
+ /** @hidden */
16083
+ getGraph() {
16084
+ return this._graph;
16085
+ }
16086
+ getLogger() {
16087
+ return this._logger;
16088
+ }
16089
+ setLogger(logger) {
16090
+ this._logger = logger;
16091
+ return this;
16092
+ }
16093
+ getProjectDir() {
16094
+ return this._projectDir;
16095
+ }
16096
+ setProjectDir(projectDir) {
16097
+ this._projectDir = projectDir;
16098
+ return this;
16099
+ }
16100
+ async transform(...transforms) {
16101
+ const stack = transforms.map((fn) => fn.name);
16102
+ for (const transform of transforms) await transform(this, { stack });
16103
+ return this;
16104
+ }
16105
+ /****** Extension factory methods ******/
16106
+ createExtension(ctor) {
16107
+ const extensionName = ctor.EXTENSION_NAME;
16108
+ return this.getRoot().listExtensionsUsed().find((ext) => ext.extensionName === extensionName) || new ctor(this);
16109
+ }
16110
+ /****** Property factory methods ******/
16111
+ createPackage(name = "") {
16112
+ return new Package(this._graph, name);
16113
+ }
16114
+ createImageResource(name = "") {
16115
+ return new ImageResource(this._graph, name);
16116
+ }
16117
+ createSoundResource(name = "") {
16118
+ return new SoundResource(this._graph, name);
16119
+ }
16120
+ createMiscResource(name = "") {
16121
+ return new MiscResource(this._graph, name);
16122
+ }
16123
+ createFontResource(name = "") {
16124
+ return new FontResource(this._graph, name);
16125
+ }
16126
+ createMovieClipResource(name = "") {
16127
+ return new MovieClipResource(this._graph, name);
16128
+ }
16129
+ createSpineResource(name = "") {
16130
+ return new SpineResource(this._graph, name);
16131
+ }
16132
+ createDragonBonesResource(name = "") {
16133
+ return new DragonBonesResource(this._graph, name);
16134
+ }
16135
+ createComponent(name = "") {
16136
+ return new Component(this._graph, name);
16137
+ }
16138
+ createAtlas(name = "") {
16139
+ return new Atlas(this._graph, name);
16140
+ }
16141
+ createSprite(name = "") {
16142
+ return new Sprite(this._graph, name);
16143
+ }
16144
+ createBuffer(name = "") {
16145
+ return new FairyBuffer(this._graph, name);
16146
+ }
16147
+ createGImage(name = "") {
16148
+ return new GImage(this._graph, name);
16149
+ }
16150
+ createGTextField(name = "") {
16151
+ return new GTextField(this._graph, name);
16152
+ }
16153
+ createGRichTextField(name = "") {
16154
+ return new GRichTextField(this._graph, name);
16155
+ }
16156
+ createGTextInput(name = "") {
16157
+ return new GTextInput(this._graph, name);
16158
+ }
16159
+ createGGraph(name = "") {
16160
+ return new GGraph(this._graph, name);
16161
+ }
16162
+ createGGroup(name = "") {
16163
+ return new GGroup(this._graph, name);
16164
+ }
16165
+ createGLoader(name = "") {
16166
+ return new GLoader(this._graph, name);
16167
+ }
16168
+ createGLoader3D(name = "") {
16169
+ return new GLoader3D(this._graph, name);
16170
+ }
16171
+ createGMovieClip(name = "") {
16172
+ return new GMovieClip(this._graph, name);
16173
+ }
16174
+ createGComponent(name = "") {
16175
+ return new GComponent(this._graph, name);
16176
+ }
16177
+ createGList(name = "") {
16178
+ return new GList(this._graph, name);
16179
+ }
16180
+ createGTree(name = "") {
16181
+ return new GTree(this._graph, name);
16182
+ }
16183
+ createGButton(name = "") {
16184
+ return new GButton(this._graph, name);
16185
+ }
16186
+ createGLabel(name = "") {
16187
+ return new GLabel(this._graph, name);
16188
+ }
16189
+ createGComboBox(name = "") {
16190
+ return new GComboBox(this._graph, name);
16191
+ }
16192
+ createGProgressBar(name = "") {
16193
+ return new GProgressBar(this._graph, name);
16194
+ }
16195
+ createGSlider(name = "") {
16196
+ return new GSlider(this._graph, name);
16197
+ }
16198
+ createGScrollBar(name = "") {
16199
+ return new GScrollBar(this._graph, name);
16200
+ }
16201
+ createController(name = "") {
16202
+ return new Controller(this._graph, name);
16203
+ }
16204
+ createControllerPage(name = "") {
16205
+ return new ControllerPage(this._graph, name);
16206
+ }
16207
+ createControllerAction(name = "") {
16208
+ return new ControllerAction(this._graph, name);
16209
+ }
16210
+ createTransition(name = "") {
16211
+ return new Transition(this._graph, name);
16212
+ }
16213
+ createTransitionItem(name = "") {
16214
+ return new TransitionItem(this._graph, name);
16215
+ }
16216
+ createGear(name = "") {
16217
+ return new Gear(this._graph, name);
16218
+ }
16219
+ createFontGlyph(name = "") {
16220
+ return new FontGlyph(this._graph, name);
16221
+ }
16222
+ createMovieFrame(name = "") {
16223
+ return new MovieFrame(this._graph, name);
16224
+ }
16225
+ };
16226
+ //#endregion
16227
+ //#region ../core/src/utils/resource-folder.ts
16228
+ function normalizeResourceFolderPath(value) {
16229
+ const segments = value.replace(/\\/g, "/").split("/").filter(Boolean);
16230
+ return segments.length > 0 ? `/${segments.join("/")}/` : "/";
16231
+ }
16232
+ function resourceFolderParentPath(value) {
16233
+ const segments = normalizeResourceFolderPath(value).split("/").filter(Boolean);
16234
+ segments.pop();
16235
+ return segments.length > 0 ? `/${segments.join("/")}/` : "/";
16236
+ }
16237
+ function resourceFolderName(value) {
16238
+ return normalizeResourceFolderPath(value).split("/").filter(Boolean).pop() ?? "";
16239
+ }
16240
+ //#endregion
16241
+ //#region ../core/src/io/project-xml-protocol.ts
16242
+ const mergeAttrs = (...parts) => Object.assign({}, ...parts);
16243
+ const mergeChildren = (...parts) => Object.assign({}, ...parts);
16244
+ const mergeContainers = (...parts) => Object.assign({}, ...parts);
16245
+ const defineContainer = (items) => ({
16246
+ kind: "orderedVariants",
16247
+ items
16248
+ });
16249
+ const defineNode = (attrs, children, containers) => ({
16250
+ attrs,
16251
+ ...children ? { children } : {},
16252
+ ...containers ? { containers } : {}
16139
16253
  });
16140
16254
  const PACKAGE_DESCRIPTION_ATTRS = {
16141
16255
  id: { canonical: "id" },
16142
16256
  hasFavorites: { canonical: "hasFavorites" },
16143
16257
  compressPNG: { canonical: "compressPNG" },
16144
- jpegQuality: { canonical: "jpegQuality" }
16258
+ jpegQuality: { canonical: "jpegQuality" },
16259
+ branchNames: { canonical: "branchNames" }
16145
16260
  };
16146
16261
  const BRANCH_DESCRIPTION_ATTRS = {};
16147
16262
  const PACKAGE_PUBLISH_ATTRS = {
@@ -16150,11 +16265,21 @@ const PACKAGE_PUBLISH_ATTRS = {
16150
16265
  branchPath: { canonical: "branchPath" },
16151
16266
  packageCount: { canonical: "packageCount" },
16152
16267
  genCode: { canonical: "genCode" },
16153
- codePath: { canonical: "codePath" }
16268
+ codePath: { canonical: "codePath" },
16269
+ maxAtlasSize: { canonical: "maxAtlasSize" },
16270
+ sizeOption: { canonical: "sizeOption" },
16271
+ npot: { canonical: "npot" },
16272
+ square: { canonical: "square" },
16273
+ rotation: { canonical: "rotation" },
16274
+ multiPage: { canonical: "multiPage" },
16275
+ extractAlpha: { canonical: "extractAlpha" },
16276
+ maxAtlasIndex: { canonical: "maxAtlasIndex" },
16277
+ excluded: { canonical: "excluded" }
16154
16278
  };
16155
16279
  const PACKAGE_PUBLISH_ATLAS_ATTRS = {
16156
16280
  name: { canonical: "name" },
16157
- index: { canonical: "index" }
16281
+ index: { canonical: "index" },
16282
+ compression: { canonical: "compression" }
16158
16283
  };
16159
16284
  const PACKAGE_RESOURCE_BASE_ATTRS = {
16160
16285
  id: { canonical: "id" },
@@ -16187,7 +16312,10 @@ const PACKAGE_FONT_RESOURCE_ATTRS = {
16187
16312
  renderMode: { canonical: "renderMode" },
16188
16313
  samplePointSize: { canonical: "samplePointSize" }
16189
16314
  };
16190
- const PACKAGE_MOVIE_CLIP_RESOURCE_ATTRS = { atlas: { canonical: "atlas" } };
16315
+ const PACKAGE_MOVIE_CLIP_RESOURCE_ATTRS = {
16316
+ atlas: { canonical: "atlas" },
16317
+ smoothing: { canonical: "smoothing" }
16318
+ };
16191
16319
  const PACKAGE_SKELETON_RESOURCE_ATTRS = {
16192
16320
  width: { canonical: "width" },
16193
16321
  height: { canonical: "height" },
@@ -16453,7 +16581,8 @@ const COMBOBOX_EXTENSION_ATTRS = {
16453
16581
  title: { canonical: "title" },
16454
16582
  icon: { canonical: "icon" },
16455
16583
  visibleItemCount: { canonical: "visibleItemCount" },
16456
- selectionController: { canonical: "selectionController" }
16584
+ selectionController: { canonical: "selectionController" },
16585
+ autoClearItems: { canonical: "autoClearItems" }
16457
16586
  };
16458
16587
  const PROGRESSBAR_EXTENSION_ATTRS = {
16459
16588
  titleType: { canonical: "titleType" },
@@ -16481,6 +16610,11 @@ const CUSTOM_PROPERTY_ATTRS = {
16481
16610
  propertyId: { canonical: "propertyId" },
16482
16611
  label: { canonical: "label" }
16483
16612
  };
16613
+ const PROPERTY_OVERRIDE_ATTRS = {
16614
+ target: { canonical: "target" },
16615
+ propertyId: { canonical: "propertyId" },
16616
+ value: { canonical: "value" }
16617
+ };
16484
16618
  const GEAR_ATTRS = {
16485
16619
  controller: { canonical: "controller" },
16486
16620
  pages: { canonical: "pages" },
@@ -16571,10 +16705,11 @@ const SLIDER_EXTENSION_NODE = defineNode(SLIDER_EXTENSION_ATTRS);
16571
16705
  const SCROLLBAR_EXTENSION_NODE = defineNode(SCROLLBAR_EXTENSION_ATTRS);
16572
16706
  const RELATION_NODE = defineNode(RELATION_ATTRS);
16573
16707
  const CUSTOM_PROPERTY_NODE = defineNode(CUSTOM_PROPERTY_ATTRS);
16708
+ const PROPERTY_OVERRIDE_NODE = defineNode(PROPERTY_OVERRIDE_ATTRS);
16574
16709
  const GEAR_NODE = defineNode(GEAR_ATTRS);
16575
16710
  const CONTROLLER_ACTION_NODE = defineNode(CONTROLLER_ACTION_ATTRS);
16576
16711
  const TRANSITION_ITEM_NODE = defineNode(TRANSITION_ITEM_ATTRS);
16577
- const LIST_ITEM_NODE = defineNode(LIST_ITEM_ATTRS);
16712
+ const LIST_ITEM_NODE = defineNode(LIST_ITEM_ATTRS, { property: PROPERTY_OVERRIDE_NODE });
16578
16713
  const COMBOBOX_ITEM_NODE = defineNode(COMBOBOX_ITEM_ATTRS);
16579
16714
  const WITH_RELATION_CHILDREN = { relation: RELATION_NODE };
16580
16715
  const WITH_GEAR_CHILDREN = {
@@ -16622,7 +16757,7 @@ const TRANSITION_NODE = defineNode(mergeAttrs(TRANSITION_ATTRS), mergeChildren(W
16622
16757
  const IMAGE_NODE = defineNode(mergeAttrs(COMMON_DISPLAY_OBJECT_ATTRS, IMAGE_PANEL_ATTRS, XY_SIZE_ATTRS, LOCKED_ATTRS, ASPECT_ATTRS, PIVOT_ATTRS, ANCHOR_ATTRS, SCALE_ATTRS, SKEW_ATTRS, GROUP_REF_ATTRS, COMMON_DISPLAY_STATE_ATTRS, RESOURCE_LINK_ATTRS, FILTER_ATTRS), mergeChildren(WITH_RELATION_CHILDREN, WITH_GEAR_CHILDREN));
16623
16758
  const GRAPH_NODE = defineNode(mergeAttrs(COMMON_DISPLAY_OBJECT_ATTRS, XY_SIZE_ATTRS, LOCKED_ATTRS, RESTRICT_SIZE_ATTRS, PIVOT_ATTRS, ANCHOR_ATTRS, SKEW_ATTRS, GROUP_REF_ATTRS, COMMON_DISPLAY_STATE_ATTRS, GRAPH_PANEL_ATTRS), mergeChildren(WITH_RELATION_CHILDREN, WITH_GEAR_CHILDREN));
16624
16759
  const MOVIE_CLIP_NODE = defineNode(mergeAttrs(COMMON_DISPLAY_OBJECT_ATTRS, MOVIE_CLIP_PANEL_ATTRS, XY_SIZE_ATTRS, PIVOT_ATTRS, ANCHOR_ATTRS, GROUP_REF_ATTRS, COMMON_DISPLAY_STATE_ATTRS, RESOURCE_LINK_ATTRS, FILTER_ATTRS), mergeChildren(WITH_RELATION_CHILDREN, WITH_GEAR_CHILDREN));
16625
- const COMPONENT_INSTANCE_NODE = defineNode(mergeAttrs(COMMON_DISPLAY_OBJECT_ATTRS, COMPONENT_INSTANCE_PANEL_ATTRS, XY_SIZE_ATTRS, LOCKED_ATTRS, RESTRICT_SIZE_ATTRS, ASPECT_ATTRS, PIVOT_ATTRS, ANCHOR_ATTRS, SCALE_ATTRS, GROUP_REF_ATTRS, COMMON_DISPLAY_STATE_ATTRS, INSTANCE_MISC_PANEL_ATTRS, RESOURCE_LINK_ATTRS, FILTER_ATTRS), mergeChildren(WITH_RELATION_CHILDREN, WITH_GEAR_CHILDREN, WITH_INSTANCE_EXTENSION_CHILDREN));
16760
+ const COMPONENT_INSTANCE_NODE = defineNode(mergeAttrs(COMMON_DISPLAY_OBJECT_ATTRS, COMPONENT_INSTANCE_PANEL_ATTRS, XY_SIZE_ATTRS, LOCKED_ATTRS, RESTRICT_SIZE_ATTRS, ASPECT_ATTRS, PIVOT_ATTRS, ANCHOR_ATTRS, SCALE_ATTRS, GROUP_REF_ATTRS, COMMON_DISPLAY_STATE_ATTRS, INSTANCE_MISC_PANEL_ATTRS, RESOURCE_LINK_ATTRS, FILTER_ATTRS), mergeChildren(WITH_RELATION_CHILDREN, WITH_GEAR_CHILDREN, WITH_INSTANCE_EXTENSION_CHILDREN, { property: PROPERTY_OVERRIDE_NODE }));
16626
16761
  const LOADER_NODE = defineNode(mergeAttrs(COMMON_DISPLAY_OBJECT_ATTRS, XY_SIZE_ATTRS, PIVOT_ATTRS, ANCHOR_ATTRS, SCALE_ATTRS, COMMON_DISPLAY_STATE_ATTRS, LOADER_PANEL_ATTRS, FILTER_ATTRS), mergeChildren(WITH_RELATION_CHILDREN, WITH_GEAR_CHILDREN));
16627
16762
  const LOADER3D_NODE = defineNode(mergeAttrs(COMMON_DISPLAY_OBJECT_ATTRS, LOADER3D_PANEL_ATTRS), mergeChildren(WITH_RELATION_CHILDREN, WITH_GEAR_CHILDREN));
16628
16763
  const TEXT_NODE = defineNode(mergeAttrs(COMMON_DISPLAY_OBJECT_ATTRS, XY_SIZE_ATTRS, RESTRICT_SIZE_ATTRS, PIVOT_ATTRS, ANCHOR_ATTRS, { customData: { canonical: "customData" } }, GROUP_REF_ATTRS, COMMON_DISPLAY_STATE_ATTRS, TEXT_PANEL_ATTRS, TEXT_INPUT_PANEL_ATTRS), mergeChildren(WITH_RELATION_CHILDREN, WITH_GEAR_CHILDREN));
@@ -16686,6 +16821,7 @@ const PROJECT_XML_PROTOCOL = {
16686
16821
  group: GROUP_NODE,
16687
16822
  list: LIST_NODE,
16688
16823
  listItem: LIST_ITEM_NODE,
16824
+ propertyOverride: PROPERTY_OVERRIDE_NODE,
16689
16825
  comboBoxItem: COMBOBOX_ITEM_NODE
16690
16826
  };
16691
16827
  function readXmlAttr(source, spec) {
@@ -16844,6 +16980,24 @@ function getXmlNode$2(value) {
16844
16980
  function getProtocolChildName$2(protocol, childName) {
16845
16981
  return protocol.children?.[childName] ? childName : null;
16846
16982
  }
16983
+ function parsePropertyOverrides(source, protocol) {
16984
+ const childName = getProtocolChildName$2(protocol, "property");
16985
+ if (!childName) return [];
16986
+ return ensureArray(source[childName]).map((raw, index) => {
16987
+ const property = getXmlNode$2(raw);
16988
+ const specs = PROJECT_XML_PROTOCOL.propertyOverride.attrs;
16989
+ const target = property ? readXmlAttr(property, specs.target) : void 0;
16990
+ const rawPropertyId = property ? readXmlAttr(property, specs.propertyId) : void 0;
16991
+ const propertyId = typeof rawPropertyId === "number" ? rawPropertyId : typeof rawPropertyId === "string" && /^\d+$/.test(rawPropertyId) ? Number(rawPropertyId) : NaN;
16992
+ const value = property ? readXmlAttr(property, specs.value) : void 0;
16993
+ if (!target || !Number.isSafeInteger(propertyId) || propertyId < 0 || value === void 0) throw new Error(`Invalid property override at ${childName}[${index}].`);
16994
+ return {
16995
+ target,
16996
+ propertyId,
16997
+ value: String(value)
16998
+ };
16999
+ });
17000
+ }
16847
17001
  function getProtocolGearChildNames(protocol) {
16848
17002
  return Object.keys(protocol.children ?? {}).filter((name) => name in GEAR_TAG_MAP);
16849
17003
  }
@@ -16878,6 +17032,7 @@ function parseListItemXmlNode(item) {
16878
17032
  const specs = PROJECT_XML_PROTOCOL.listItem.attrs;
16879
17033
  const isFolder = readXmlAttr(item, specs.isFolder);
16880
17034
  const controllers = readXmlAttr(item, specs.controllers);
17035
+ const propertyOverrides = parsePropertyOverrides(item, PROJECT_XML_PROTOCOL.listItem);
16881
17036
  return {
16882
17037
  title: readXmlAttr(item, specs.title) ?? null,
16883
17038
  icon: readXmlAttr(item, specs.icon) ?? null,
@@ -16887,7 +17042,8 @@ function parseListItemXmlNode(item) {
16887
17042
  selectedIcon: readXmlAttr(item, specs.selectedIcon) ?? null,
16888
17043
  level: parseInt2(readXmlAttr(item, specs.level)),
16889
17044
  isFolder: isFolder !== void 0 ? parseBool(isFolder) : null,
16890
- ...controllers !== void 0 ? { controllers } : {}
17045
+ ...controllers !== void 0 ? { controllers } : {},
17046
+ ...propertyOverrides.length > 0 ? { propertyOverrides } : {}
16891
17047
  };
16892
17048
  }
16893
17049
  function parseComboBoxItemXmlNode(item) {
@@ -17954,6 +18110,8 @@ function createDisplayObject$1(ctx, doc, tagName, attrs, localControllers) {
17954
18110
  if (scrollItemToViewOnClick !== void 0) g.setScrollItemToViewOnClick?.(parseBool(scrollItemToViewOnClick));
17955
18111
  const foldInvisibleItems = readXmlAttr(attrs, PROJECT_XML_PROTOCOL.list.attrs.foldInvisibleItems);
17956
18112
  if (foldInvisibleItems !== void 0) g.setFoldInvisibleItems?.(parseBool(foldInvisibleItems));
18113
+ const autoClearItems = readXmlAttr(attrs, PROJECT_XML_PROTOCOL.list.attrs.autoClearItems);
18114
+ if (autoClearItems !== void 0) g.setAutoClearItems?.(parseBool(autoClearItems));
17957
18115
  const listItemChildName = getProtocolChildName$2(PROJECT_XML_PROTOCOL.list, "item");
17958
18116
  const items = listItemChildName ? ensureArray(attrs[listItemChildName]) : [];
17959
18117
  if (items.length > 0) {
@@ -17992,6 +18150,7 @@ function createDisplayObject$1(ctx, doc, tagName, attrs, localControllers) {
17992
18150
  obj.addRelation(rel);
17993
18151
  }
17994
18152
  }
18153
+ if (obj.propertyType === "GComponent") obj.setPropertyOverrides(parsePropertyOverrides(attrs, PROJECT_XML_PROTOCOL.componentInstance));
17995
18154
  for (const extTypeName of getProtocolExtensionChildNames(PROJECT_XML_PROTOCOL.componentInstance)) {
17996
18155
  const extElement = attrs[extTypeName];
17997
18156
  if (extElement) {
@@ -18028,6 +18187,8 @@ function createDisplayObject$1(ctx, doc, tagName, attrs, localControllers) {
18028
18187
  if (selectionController !== void 0) componentObj.setInstanceSelectionController?.(selectionController);
18029
18188
  const visibleItemCount = extSpecs.visibleItemCount ? readXmlAttr(extAttrs, extSpecs.visibleItemCount) : void 0;
18030
18189
  if (visibleItemCount !== void 0) componentObj.setInstanceVisibleItemCount?.(parseInt2(visibleItemCount));
18190
+ const autoClearItems = extSpecs.autoClearItems ? readXmlAttr(extAttrs, extSpecs.autoClearItems) : void 0;
18191
+ if (autoClearItems !== void 0) componentObj.setInstanceAutoClearItems?.(parseBool(autoClearItems));
18031
18192
  const value = extSpecs.value ? readXmlAttr(extAttrs, extSpecs.value) : void 0;
18032
18193
  if (value !== void 0) componentObj.setInstanceValue?.(parseInt2(value));
18033
18194
  const max = extSpecs.max ? readXmlAttr(extAttrs, extSpecs.max) : void 0;
@@ -18370,6 +18531,7 @@ function readComponentXml(ctx, comp, xmlContent) {
18370
18531
  case "ComboBox":
18371
18532
  if (readXmlAttr(extAttrs, EXTENSION_PROTOCOL_MAP$1.ComboBox.attrs.dropdown) !== void 0) comp.setDropdown?.(String(readXmlAttr(extAttrs, EXTENSION_PROTOCOL_MAP$1.ComboBox.attrs.dropdown)));
18372
18533
  if (readXmlAttr(extAttrs, EXTENSION_PROTOCOL_MAP$1.ComboBox.attrs.selectionController) !== void 0) comp.setSelectionController?.(String(readXmlAttr(extAttrs, EXTENSION_PROTOCOL_MAP$1.ComboBox.attrs.selectionController)));
18534
+ if (readXmlAttr(extAttrs, EXTENSION_PROTOCOL_MAP$1.ComboBox.attrs.autoClearItems) !== void 0) comp.setAutoClearItems?.(parseBool(readXmlAttr(extAttrs, EXTENSION_PROTOCOL_MAP$1.ComboBox.attrs.autoClearItems)));
18373
18535
  break;
18374
18536
  case "Label":
18375
18537
  if (readXmlAttr(extAttrs, EXTENSION_PROTOCOL_MAP$1.Label.attrs.prompt) !== void 0) comp.setPromptText?.(String(readXmlAttr(extAttrs, EXTENSION_PROTOCOL_MAP$1.Label.attrs.prompt)));
@@ -18572,8 +18734,11 @@ function assignSetting(settings, key, value) {
18572
18734
  case "adaptation":
18573
18735
  settings.adaptation = value;
18574
18736
  break;
18575
- default:
18576
- settings[key] = value;
18737
+ case "customProperties":
18738
+ settings.customProperties = value;
18739
+ break;
18740
+ case "i18n":
18741
+ settings.i18n = value;
18577
18742
  break;
18578
18743
  }
18579
18744
  }
@@ -18617,6 +18782,7 @@ var ProjectReader = class {
18617
18782
  }
18618
18783
  const branchNames = await this._readPackageBranches(ctx, options);
18619
18784
  if (branchNames.length > 0) doc.getRoot().setBranches(branchNames);
18785
+ this._linkPackageBranchItems(doc);
18620
18786
  for (const [_key, resource] of ctx.resourceMap) {
18621
18787
  if (resource.propertyType !== "Component") continue;
18622
18788
  const comp = resource;
@@ -18630,6 +18796,22 @@ var ProjectReader = class {
18630
18796
  }
18631
18797
  return doc;
18632
18798
  }
18799
+ _linkPackageBranchItems(doc) {
18800
+ for (const pkg of doc.getRoot().listPackages()) {
18801
+ const branchNames = pkg.listBranchNames();
18802
+ if (branchNames.length === 0) continue;
18803
+ const variants = /* @__PURE__ */ new Map();
18804
+ for (const resource of pkg.listResources()) {
18805
+ const branchName = resource.getBranch();
18806
+ if (!branchName) continue;
18807
+ variants.set(`${branchName}\0${resource.propertyType}\0${resource.getPath()}\0${resource.getName()}`, resource.getId());
18808
+ }
18809
+ for (const resource of pkg.listResources()) {
18810
+ if (resource.getBranch()) continue;
18811
+ resource.setBranchItemIds(branchNames.map((branchName) => variants.get(`${branchName}\0${resource.propertyType}\0${resource.getPath()}\0${resource.getName()}`) ?? ""));
18812
+ }
18813
+ }
18814
+ }
18633
18815
  async _readPackageBranches(ctx, options) {
18634
18816
  const fs = this._fs;
18635
18817
  let dirNames = [];
@@ -18696,6 +18878,7 @@ var ProjectReader = class {
18696
18878
  if (!desc) return;
18697
18879
  let pkg = ctx.document.getRoot().getPackage(dirName);
18698
18880
  if (!pkg) pkg = ctx.document.createPackage(dirName);
18881
+ if (branchName) pkg.addBranchName(branchName);
18699
18882
  pkg.setExtras({
18700
18883
  ...pkg.getExtras(),
18701
18884
  _preservePackageResourceOrder: true
@@ -18703,6 +18886,17 @@ var ProjectReader = class {
18703
18886
  if (!branchName) {
18704
18887
  const packageId = readXmlAttr(desc, PROJECT_XML_PROTOCOL.packageDescription.attrs.id) || "";
18705
18888
  pkg.setId(packageId);
18889
+ const serializedBranchNames = readXmlAttr(desc, PROJECT_XML_PROTOCOL.packageDescription.attrs.branchNames);
18890
+ if (serializedBranchNames !== void 0) {
18891
+ let parsedBranchNames;
18892
+ try {
18893
+ parsedBranchNames = JSON.parse(serializedBranchNames);
18894
+ } catch {
18895
+ throw new Error(`Invalid package branchNames for "${dirName}".`);
18896
+ }
18897
+ if (!Array.isArray(parsedBranchNames) || !parsedBranchNames.every((name) => typeof name === "string" && name.length > 0) || new Set(parsedBranchNames).size !== parsedBranchNames.length) throw new Error(`Invalid package branchNames for "${dirName}".`);
18898
+ pkg.setBranchNames(parsedBranchNames);
18899
+ }
18706
18900
  const compressPNG = readXmlAttr(desc, PROJECT_XML_PROTOCOL.packageDescription.attrs.compressPNG);
18707
18901
  if (compressPNG !== void 0) pkg.setCompressPNG(parseBool(compressPNG));
18708
18902
  const jpegQuality = readXmlAttr(desc, PROJECT_XML_PROTOCOL.packageDescription.attrs.jpegQuality);
@@ -18717,6 +18911,32 @@ var ProjectReader = class {
18717
18911
  pkg.setPublishPackageCount(parseInt2(readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.packageCount), 0));
18718
18912
  pkg.setGenCode(parseBool(readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.genCode)));
18719
18913
  pkg.setCodePath(readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.codePath) || "");
18914
+ const globalAtlas = ctx.settings.publish?.atlasSetting;
18915
+ const maxAtlasSize = readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.maxAtlasSize);
18916
+ const sizeOption = parseBool(readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.npot)) ? "npot" : readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.sizeOption) || globalAtlas?.sizeOption || "pot";
18917
+ const square = readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.square);
18918
+ const rotation = readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.rotation);
18919
+ const multiPage = readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.multiPage);
18920
+ pkg.setSourceAtlasSettings({
18921
+ useGlobal: maxAtlasSize === void 0,
18922
+ maxSize: parseInt2(maxAtlasSize, globalAtlas?.maxSize ?? 2048),
18923
+ sizeOption: sizeOption === "npot" || sizeOption === "mof" ? sizeOption : "pot",
18924
+ forceSquare: square === void 0 ? globalAtlas?.forceSquare ?? false : parseBool(square),
18925
+ allowRotation: rotation === void 0 ? globalAtlas?.allowRotation ?? false : parseBool(rotation),
18926
+ paging: multiPage === void 0 ? globalAtlas?.paging ?? true : parseBool(multiPage),
18927
+ extractAlpha: parseBool(readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.extractAlpha)),
18928
+ maxIndex: parseInt2(readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.maxAtlasIndex), 10),
18929
+ atlases: ensureArray(publish.atlas).flatMap((value) => {
18930
+ const atlas = getXmlNode(value);
18931
+ if (!atlas) return [];
18932
+ return [{
18933
+ index: parseInt2(readXmlAttr(atlas, PROJECT_XML_PROTOCOL.packagePublishAtlas.attrs.index)),
18934
+ name: readXmlAttr(atlas, PROJECT_XML_PROTOCOL.packagePublishAtlas.attrs.name) || "",
18935
+ compression: parseBool(readXmlAttr(atlas, PROJECT_XML_PROTOCOL.packagePublishAtlas.attrs.compression))
18936
+ }];
18937
+ }),
18938
+ excludedResourceIds: (readXmlAttr(publish, PROJECT_XML_PROTOCOL.packagePublish.attrs.excluded) || "").split(",").filter(Boolean)
18939
+ });
18720
18940
  }
18721
18941
  if (pkg.getId()) ctx.packageMap.set(pkg.getId(), pkg);
18722
18942
  const packageDir = branchName ? fs.join(ctx.basePath, `assets_${branchName}`, dirName) : fs.join(ctx.basePath, "assets", dirName);
@@ -18831,10 +19051,9 @@ var ProjectReader = class {
18831
19051
  if (resource.propertyType === "ImageResource") {
18832
19052
  const size = probeRasterImageDimensions(data);
18833
19053
  if (size) resource.setWidth(size.width).setHeight(size.height);
18834
- } else if (resource.propertyType === "MovieClipResource") {
18835
- const size = tryReadJtaSize(data);
18836
- if (size) resource.setWidth(size.width).setHeight(size.height);
18837
- }
19054
+ } else if (resource.propertyType === "MovieClipResource") try {
19055
+ applyDerivedMovieClipModel(doc, resource, deriveMovieClipModelFromJta(data));
19056
+ } catch {}
18838
19057
  } catch {}
18839
19058
  }
18840
19059
  }
@@ -19014,6 +19233,8 @@ var ProjectReader = class {
19014
19233
  res.setFavorite(favorite);
19015
19234
  const textureSetMode = readXmlAttr(attrs, PROJECT_XML_PROTOCOL.packageMovieClipResource.attrs.atlas);
19016
19235
  if (textureSetMode !== void 0) res.setTextureSetMode(textureSetMode);
19236
+ const smoothing = readXmlAttr(attrs, PROJECT_XML_PROTOCOL.packageMovieClipResource.attrs.smoothing);
19237
+ res.setSmoothing(smoothing !== "false");
19017
19238
  pkg.addResource(res);
19018
19239
  ctx.registerResource(pkg.getId(), id, res);
19019
19240
  return res;
@@ -19393,6 +19614,16 @@ function serializeListItemXmlNode(item, options) {
19393
19614
  if (item.level !== void 0 && item.level !== null && ((options?.forceLevel ?? false) || item.level !== 0 || item.isFolder === true)) writeXmlAttr(attrs, specs.level, String(item.level));
19394
19615
  if (item.isFolder !== void 0 && item.isFolder !== null) writeXmlAttr(attrs, specs.isFolder, item.isFolder ? "true" : "false");
19395
19616
  if (item.controllers !== void 0 && item.controllers !== null) writeXmlAttr(attrs, specs.controllers, item.controllers);
19617
+ const propertyChildName = getProtocolChildName(PROJECT_XML_PROTOCOL.listItem, "property");
19618
+ if (propertyChildName && item.propertyOverrides?.length) attrs[propertyChildName] = item.propertyOverrides.map(serializePropertyOverrideXmlNode);
19619
+ return attrs;
19620
+ }
19621
+ function serializePropertyOverrideXmlNode(property) {
19622
+ const attrs = {};
19623
+ const specs = PROJECT_XML_PROTOCOL.propertyOverride.attrs;
19624
+ writeXmlAttr(attrs, specs.target, property.target);
19625
+ writeXmlAttr(attrs, specs.propertyId, String(property.propertyId));
19626
+ writeXmlAttr(attrs, specs.value, property.value);
19396
19627
  return attrs;
19397
19628
  }
19398
19629
  function serializeComboBoxItemXmlNode(item) {
@@ -19875,6 +20106,7 @@ function serializeChild(obj) {
19875
20106
  if (clipSoftness && ((clipSoftness.x ?? 0) !== 0 || (clipSoftness.y ?? 0) !== 0)) writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.list.attrs.clipSoftness, `${clipSoftness.x ?? 0},${clipSoftness.y ?? 0}`);
19876
20107
  if (typedObj.getScrollItemToViewOnClick?.() === false) writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.list.attrs.scrollItemToViewOnClick, "false");
19877
20108
  if (typedObj.getFoldInvisibleItems?.() === true) writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.list.attrs.foldInvisibleItems, "true");
20109
+ if (typedObj.getAutoClearItems?.() === true) writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.list.attrs.autoClearItems, "true");
19878
20110
  const listItems = typedObj.getListItems?.() ?? [];
19879
20111
  const listItemChildName = getProtocolChildName(PROJECT_XML_PROTOCOL.list, "item");
19880
20112
  if (listItems.length > 0 && listItemChildName) attrs[listItemChildName] = listItems.map((item) => serializeListItemXmlNode(item, { forceLevel: isTree }));
@@ -19942,6 +20174,9 @@ function serializeChild(obj) {
19942
20174
  }
19943
20175
  }
19944
20176
  if (type === "GComponent") {
20177
+ const propertyOverrides = typedObj.getPropertyOverrides?.() ?? [];
20178
+ const propertyChildName = getProtocolChildName(PROJECT_XML_PROTOCOL.componentInstance, "property");
20179
+ if (propertyChildName && propertyOverrides.length > 0) attrs[propertyChildName] = propertyOverrides.map(serializePropertyOverrideXmlNode);
19945
20180
  const instanceExtType = typedObj.getInstanceExtType?.() ?? "";
19946
20181
  if (instanceExtType) {
19947
20182
  const extSpecs = EXTENSION_PROTOCOL_MAP[instanceExtType].attrs;
@@ -19960,6 +20195,7 @@ function serializeChild(obj) {
19960
20195
  if (typedObj.getInstancePromptText?.() && extSpecs.prompt) writeXmlAttr(extAttrs, extSpecs.prompt, typedObj.getInstancePromptText?.());
19961
20196
  if (typedObj.getInstanceSelectionController?.() && extSpecs.selectionController) writeXmlAttr(extAttrs, extSpecs.selectionController, typedObj.getInstanceSelectionController?.());
19962
20197
  if ((typedObj.getInstanceVisibleItemCount?.() ?? 0) > 0 && extSpecs.visibleItemCount) writeXmlAttr(extAttrs, extSpecs.visibleItemCount, String(typedObj.getInstanceVisibleItemCount?.() ?? 0));
20198
+ if (typedObj.getInstanceAutoClearItems?.() && extSpecs.autoClearItems) writeXmlAttr(extAttrs, extSpecs.autoClearItems, "true");
19963
20199
  const instanceValue = typedObj.getInstanceValue?.() ?? 0;
19964
20200
  const instanceMax = typedObj.getInstanceMax?.() ?? 0;
19965
20201
  const instanceMin = typedObj.getInstanceMin?.() ?? 0;
@@ -20229,6 +20465,7 @@ async function writeComponent(fs, comp, pkgDir, sourceRelativePath) {
20229
20465
  case "ComboBox":
20230
20466
  if (typedComp.getDropdown?.()) writeXmlAttr(extAttrs, extSpecs.dropdown, typedComp.getDropdown?.());
20231
20467
  if (typedComp.getSelectionController?.()) writeXmlAttr(extAttrs, extSpecs.selectionController, typedComp.getSelectionController?.());
20468
+ if (typedComp.getAutoClearItems?.()) writeXmlAttr(extAttrs, extSpecs.autoClearItems, "true");
20232
20469
  break;
20233
20470
  case "Label":
20234
20471
  if (typedComp.getPromptText?.()) writeXmlAttr(extAttrs, extSpecs.prompt, typedComp.getPromptText?.());
@@ -20367,25 +20604,44 @@ var ProjectWriter = class {
20367
20604
  const basePath = fs.dirname(projectPath);
20368
20605
  const currentSourceFilePaths = /* @__PURE__ */ new Set();
20369
20606
  const currentResourceFolderPaths = /* @__PURE__ */ new Set();
20607
+ const currentBranchDirectoryPaths = /* @__PURE__ */ new Set();
20370
20608
  const staleSourceFilePaths = new Set((options.staleSourceFiles ?? []).map((source) => this._projectSourceFilePath(basePath, source)));
20609
+ const staleBranchDirectoryPaths = new Set((options.staleBranchDirectories ?? []).map((directory) => this._projectBranchDirectoryPath(basePath, directory)));
20610
+ if (staleBranchDirectoryPaths.size > 0 && !fs.rmdir) throw new Error("Project branch cleanup requires a FileSystem.rmdir() implementation.");
20371
20611
  for (const pkg of root.listPackages()) this._assertPackageOutputTargets(pkg);
20372
- const fairyXml = `<?xml version="1.0" encoding="utf-8"?>\n<projectDescription id="${root.getProjectId()}" type="${this._projectTypeName(root.getProjectType())}" version="${root.getVersion() || "3.0"}"/>\n`;
20373
- await fs.writeFile(projectPath, fairyXml);
20374
20612
  const settings = root.getSettings?.() ?? {};
20375
20613
  const settingsPath = fs.join(basePath, "settings");
20614
+ const staleOptionalSettings = [];
20615
+ for (const [fileName, key] of [["CustomProperties.json", "customProperties"], ["i18n.json", "i18n"]]) {
20616
+ const filePath = fs.join(settingsPath, fileName);
20617
+ if (settings[key] === void 0 && await fs.exists(filePath)) staleOptionalSettings.push(filePath);
20618
+ }
20619
+ if (staleOptionalSettings.length > 0 && !fs.unlink) throw new Error("Project settings cleanup requires a FileSystem.unlink() implementation.");
20620
+ const fairyXml = `<?xml version="1.0" encoding="utf-8"?>\n<projectDescription id="${root.getProjectId()}" type="${this._projectTypeName(root.getProjectType())}" version="${root.getVersion() || "3.0"}"/>\n`;
20621
+ await fs.writeFile(projectPath, fairyXml);
20376
20622
  await fs.mkdir(settingsPath);
20377
20623
  for (const [fileName, key] of Object.entries({
20378
20624
  "Publish.json": "publish",
20379
20625
  "Common.json": "common",
20380
- "Adaptation.json": "adaptation"
20626
+ "Adaptation.json": "adaptation",
20627
+ "CustomProperties.json": "customProperties",
20628
+ "i18n.json": "i18n"
20381
20629
  })) if (settings[key]) await fs.writeFile(fs.join(settingsPath, fileName), JSON.stringify(settings[key], null, " "));
20630
+ for (const filePath of staleOptionalSettings) await fs.unlink(filePath);
20382
20631
  const assetsPath = fs.join(basePath, "assets");
20383
20632
  await fs.mkdir(assetsPath);
20384
- for (const pkg of root.listPackages()) await this._writePackage(doc, pkg, assetsPath, currentSourceFilePaths, currentResourceFolderPaths);
20633
+ for (const branchName of root.listBranches()) {
20634
+ this._assertSafePathSegment(branchName, "branch name");
20635
+ const branchPath = fs.join(basePath, `assets_${branchName}`);
20636
+ await fs.mkdir(branchPath);
20637
+ currentBranchDirectoryPaths.add(branchPath);
20638
+ }
20639
+ for (const pkg of root.listPackages()) await this._writePackage(doc, pkg, assetsPath, currentSourceFilePaths, currentResourceFolderPaths, currentBranchDirectoryPaths);
20385
20640
  await this._removeStaleSourceFiles(currentSourceFilePaths, staleSourceFilePaths);
20386
20641
  await this._removeStaleResourceFolders(currentResourceFolderPaths, new Set((options.staleResourceFolders ?? []).map((folder) => this._projectResourceFolderPath(basePath, folder))));
20642
+ await this._removeStaleBranchDirectories(currentBranchDirectoryPaths, staleBranchDirectoryPaths);
20387
20643
  }
20388
- async _writePackage(_doc, pkg, assetsPath, currentSourceFilePaths, currentResourceFolderPaths) {
20644
+ async _writePackage(_doc, pkg, assetsPath, currentSourceFilePaths, currentResourceFolderPaths, currentBranchDirectoryPaths) {
20389
20645
  const fs = this._fs;
20390
20646
  this._assertSafePathSegment(pkg.getName(), "package name");
20391
20647
  const pkgDir = fs.join(assetsPath, pkg.getName());
@@ -20414,6 +20670,8 @@ var ProjectWriter = class {
20414
20670
  const codePath = pkg.getCodePath();
20415
20671
  const packageDescriptionAttrs = {};
20416
20672
  writeXmlAttr(packageDescriptionAttrs, PROJECT_XML_PROTOCOL.packageDescription.attrs.id, pkg.getId());
20673
+ const packageBranchNames = pkg.listBranchNames();
20674
+ writeXmlAttr(packageDescriptionAttrs, PROJECT_XML_PROTOCOL.packageDescription.attrs.branchNames, packageBranchNames.length > 0 ? JSON.stringify(packageBranchNames) : void 0);
20417
20675
  if (pkg.listResources().some((resource) => resource.getFavorite?.()) || pkg.listResourceFolders().some((folder) => folder.favorite)) writeXmlAttr(packageDescriptionAttrs, PROJECT_XML_PROTOCOL.packageDescription.attrs.hasFavorites, "true");
20418
20676
  const compressPNG = pkg.getCompressPNG();
20419
20677
  if (compressPNG !== null) writeXmlAttr(packageDescriptionAttrs, PROJECT_XML_PROTOCOL.packageDescription.attrs.compressPNG, compressPNG ? "true" : "false");
@@ -20426,11 +20684,22 @@ var ProjectWriter = class {
20426
20684
  writeXmlAttr(publishAttrs, PROJECT_XML_PROTOCOL.packagePublish.attrs.packageCount, publishPackageCount > 0 ? publishPackageCount : void 0);
20427
20685
  writeXmlAttr(publishAttrs, PROJECT_XML_PROTOCOL.packagePublish.attrs.genCode, genCode ? "true" : void 0);
20428
20686
  writeXmlAttr(publishAttrs, PROJECT_XML_PROTOCOL.packagePublish.attrs.codePath, codePath || void 0);
20429
- const publishAtlases = pkg.listAtlases().map((atlas) => {
20687
+ const sourceAtlasSettings = pkg.getSourceAtlasSettings();
20688
+ if (!sourceAtlasSettings.useGlobal) {
20689
+ writeXmlAttr(publishAttrs, PROJECT_XML_PROTOCOL.packagePublish.attrs.maxAtlasSize, String(sourceAtlasSettings.maxSize));
20690
+ writeXmlAttr(publishAttrs, PROJECT_XML_PROTOCOL.packagePublish.attrs.sizeOption, sourceAtlasSettings.sizeOption);
20691
+ writeXmlAttr(publishAttrs, PROJECT_XML_PROTOCOL.packagePublish.attrs.square, sourceAtlasSettings.forceSquare ? "true" : "false");
20692
+ writeXmlAttr(publishAttrs, PROJECT_XML_PROTOCOL.packagePublish.attrs.rotation, sourceAtlasSettings.allowRotation ? "true" : "false");
20693
+ writeXmlAttr(publishAttrs, PROJECT_XML_PROTOCOL.packagePublish.attrs.multiPage, sourceAtlasSettings.paging ? "true" : "false");
20694
+ }
20695
+ writeXmlAttr(publishAttrs, PROJECT_XML_PROTOCOL.packagePublish.attrs.extractAlpha, sourceAtlasSettings.extractAlpha ? "true" : void 0);
20696
+ writeXmlAttr(publishAttrs, PROJECT_XML_PROTOCOL.packagePublish.attrs.maxAtlasIndex, sourceAtlasSettings.maxIndex === 10 ? void 0 : String(sourceAtlasSettings.maxIndex));
20697
+ writeXmlAttr(publishAttrs, PROJECT_XML_PROTOCOL.packagePublish.attrs.excluded, sourceAtlasSettings.excludedResourceIds.length > 0 ? sourceAtlasSettings.excludedResourceIds.join(",") : void 0);
20698
+ const publishAtlases = [...sourceAtlasSettings.atlases].sort((left, right) => left.index - right.index).map((atlas) => {
20430
20699
  const attrs = {};
20431
- const index = atlas.getIndex?.() ?? 0;
20432
- writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.packagePublishAtlas.attrs.name, index === 0 ? "Default" : atlas.getName());
20433
- writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.packagePublishAtlas.attrs.index, String(index));
20700
+ writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.packagePublishAtlas.attrs.name, atlas.name || void 0);
20701
+ writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.packagePublishAtlas.attrs.index, String(atlas.index));
20702
+ writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.packagePublishAtlas.attrs.compression, atlas.compression ? "true" : void 0);
20434
20703
  return attrs;
20435
20704
  });
20436
20705
  if (publishAtlases.length > 0) publishAttrs.atlas = publishAtlases;
@@ -20444,7 +20713,11 @@ var ProjectWriter = class {
20444
20713
  await writeComponent(this._fs, comp, pkgDir, this._componentSourceRelativePath(comp));
20445
20714
  }
20446
20715
  await this._writeResourceSourceFiles(mainResources, pkgDir, currentSourceFilePaths);
20447
- const branchNames = new Set([...resourcesByBranch.keys(), ...foldersByBranch.keys()]);
20716
+ const branchNames = new Set([
20717
+ ...pkg.listBranchNames(),
20718
+ ...resourcesByBranch.keys(),
20719
+ ...foldersByBranch.keys()
20720
+ ]);
20448
20721
  for (const branchName of branchNames) {
20449
20722
  if (!branchName) continue;
20450
20723
  const branchResources = resourcesByBranch.get(branchName) ?? [];
@@ -20452,6 +20725,7 @@ var ProjectWriter = class {
20452
20725
  this._assertSafePathSegment(branchName, "branch name");
20453
20726
  const branchPkgDir = fs.join(basePath, `assets_${branchName}`, pkg.getName());
20454
20727
  await fs.mkdir(branchPkgDir);
20728
+ currentBranchDirectoryPaths.add(branchPkgDir);
20455
20729
  const branchDescriptorPath = fs.join(branchPkgDir, "package_branch.xml");
20456
20730
  await fs.writeFile(branchDescriptorPath, this._renderBranchDescriptionXml(branchFolders, branchResources, preserveResourceOrder));
20457
20731
  currentSourceFilePaths.add(branchDescriptorPath);
@@ -20507,6 +20781,13 @@ var ProjectWriter = class {
20507
20781
  await this._fs.rmdir(folderPath);
20508
20782
  }
20509
20783
  }
20784
+ async _removeStaleBranchDirectories(currentBranchDirectoryPaths, staleBranchDirectoryPaths) {
20785
+ const candidates = [...staleBranchDirectoryPaths].filter((directoryPath) => !currentBranchDirectoryPaths.has(directoryPath)).sort((left, right) => right.length - left.length);
20786
+ for (const directoryPath of candidates) {
20787
+ if (!await this._fs.exists(directoryPath)) continue;
20788
+ await this._fs.rmdir(directoryPath);
20789
+ }
20790
+ }
20510
20791
  _assertPackageOutputTargets(pkg) {
20511
20792
  this._assertSafePathSegment(pkg.getName(), "package name");
20512
20793
  const resourcesByBranch = /* @__PURE__ */ new Map();
@@ -20522,7 +20803,11 @@ var ProjectWriter = class {
20522
20803
  bucket.push(folder);
20523
20804
  foldersByBranch.set(folder.branch, bucket);
20524
20805
  }
20525
- for (const branchName of new Set([...resourcesByBranch.keys(), ...foldersByBranch.keys()])) {
20806
+ for (const branchName of new Set([
20807
+ ...pkg.listBranchNames(),
20808
+ ...resourcesByBranch.keys(),
20809
+ ...foldersByBranch.keys()
20810
+ ])) {
20526
20811
  const resources = resourcesByBranch.get(branchName) ?? [];
20527
20812
  if (branchName) this._assertSafePathSegment(branchName, "branch name");
20528
20813
  const targets = new Map([[branchName ? "package_branch.xml" : "package.xml", "package descriptor"]]);
@@ -20557,6 +20842,13 @@ var ProjectWriter = class {
20557
20842
  const assetRoot = folder.branch ? `assets_${folder.branch}` : "assets";
20558
20843
  return this._fs.join(basePath, assetRoot, folder.packageName, relativePath);
20559
20844
  }
20845
+ _projectBranchDirectoryPath(basePath, directory) {
20846
+ this._assertSafePathSegment(directory.branch, "stale branch name");
20847
+ const branchRoot = this._fs.join(basePath, `assets_${directory.branch}`);
20848
+ if (!directory.packageName) return branchRoot;
20849
+ this._assertSafePathSegment(directory.packageName, "stale branch package name");
20850
+ return this._fs.join(branchRoot, directory.packageName);
20851
+ }
20560
20852
  _resourceSourceRelativePath(resource, fileName) {
20561
20853
  if (!fileName) return "";
20562
20854
  this._assertSafePathSegment(fileName, "resource file name");
@@ -20571,7 +20863,7 @@ var ProjectWriter = class {
20571
20863
  return this._normalizeSourceRelativePath([componentPath, `${name}.xml`].filter(Boolean).join("/"));
20572
20864
  }
20573
20865
  _assertSafePathSegment(value, label) {
20574
- if (!value || value === "." || value === ".." || /[\\/:]/.test(value)) throw new Error(`Invalid ${label} "${value}".`);
20866
+ if (!value || value.trim() !== value || value === "." || value === ".." || /[\\/:]/.test(value) || /[. ]$/.test(value) || /^(?:con|prn|aux|nul|com[1-9]|lpt[1-9])(?:\..*)?$/i.test(value)) throw new Error(`Invalid ${label} "${value}".`);
20575
20867
  }
20576
20868
  _normalizeSourceRelativePath(value) {
20577
20869
  const segments = value.replace(/\\/g, "/").split("/").filter(Boolean);
@@ -20716,8 +21008,10 @@ var ProjectWriter = class {
20716
21008
  if (samplePointSize !== 0) writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.packageFontResource.attrs.samplePointSize, String(samplePointSize));
20717
21009
  }
20718
21010
  if (res.propertyType === "MovieClipResource") {
20719
- const textureSetMode = res.getTextureSetMode?.() ?? "";
21011
+ const movieClipRes = res;
21012
+ const textureSetMode = movieClipRes.getTextureSetMode?.() ?? "";
20720
21013
  if (textureSetMode) writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.packageMovieClipResource.attrs.atlas, textureSetMode);
21014
+ if (movieClipRes.getSmoothing?.() === false) writeXmlAttr(attrs, PROJECT_XML_PROTOCOL.packageMovieClipResource.attrs.smoothing, "false");
20721
21015
  }
20722
21016
  if (res.propertyType === "SpineResource" || res.propertyType === "DragonBonesResource") {
20723
21017
  const skeletonRes = res;
@@ -22050,6 +22344,7 @@ var BinaryReader = class {
22050
22344
  if (packageBranches.length > 0) for (const branchName of packageBranches) doc.getRoot().addBranch(branchName);
22051
22345
  const pkg = getOrCreatePackage(doc, packageId, packageName);
22052
22346
  if (pkg.listResources().length > 0 || pkg.listAtlases().length > 0) throw new Error(`Package "${packageName}" (${packageId}) has already been read.`);
22347
+ pkg.setBranchNames(packageBranches);
22053
22348
  const atlasMap = /* @__PURE__ */ new Map();
22054
22349
  for (const dep of dependencies) {
22055
22350
  if (!dep.id || dep.id === packageId) continue;
@@ -24033,7 +24328,8 @@ var BinaryWriter = class {
24033
24328
  id: dep.getId(),
24034
24329
  name: dep.getName()
24035
24330
  })).filter((dep) => !!dep.id);
24036
- const branchNames = includeBranches ? getPackageBranchNames(doc, resources) : [];
24331
+ const declaredBranchNames = pkg.listBranchNames();
24332
+ const branchNames = includeBranches ? declaredBranchNames.length > 0 ? declaredBranchNames : getPackageBranchNames(doc, resources) : [];
24037
24333
  const branchItemIdsMap = buildBranchItemIdsMap(pkg, branchNames);
24038
24334
  const publishedItemIdMap = new Map(resources.map((resource) => [resource.getId(), getPublishedItemId$1(resource)]));
24039
24335
  const sprites = [];
@@ -24509,9 +24805,11 @@ function getItemBranchName(item) {
24509
24805
  return item.getBranch?.() ?? "";
24510
24806
  }
24511
24807
  function getPackageBranchNames(doc, resources) {
24512
- const packageBranchSet = new Set(resources.map((resource) => getItemBranchName(resource)).filter((branchName) => !!branchName));
24513
- if (packageBranchSet.size === 0) return [];
24514
- return doc.getRoot().listBranches().filter((branchName) => packageBranchSet.has(branchName));
24808
+ const packageBranchNames = new Set(resources.map((resource) => getItemBranchName(resource)).filter((branchName) => !!branchName));
24809
+ const rootBranchNames = doc.getRoot().listBranches();
24810
+ const unknownBranchName = [...packageBranchNames].find((branchName) => !rootBranchNames.includes(branchName));
24811
+ if (unknownBranchName) throw new Error(`Package resource references unknown branch "${unknownBranchName}".`);
24812
+ return rootBranchNames.filter((branchName) => packageBranchNames.has(branchName));
24515
24813
  }
24516
24814
  function buildBranchResourceKey$1(resource) {
24517
24815
  const path = resource.getPath?.() ?? "";
@@ -25170,153 +25468,76 @@ function parseFnt(text) {
25170
25468
  }
25171
25469
  //#endregion
25172
25470
  //#region ../functions/src/atlas/jta.ts
25173
- const PNG_SIGNATURE = new Uint8Array([
25174
- 137,
25175
- 80,
25176
- 78,
25177
- 71,
25178
- 13,
25179
- 10,
25180
- 26,
25181
- 10
25182
- ]);
25183
25471
  function extractJtaFrames(data) {
25184
- const frames = [];
25185
- let offset = 0;
25186
- let firstPngOffset = -1;
25187
- while (offset < data.length) {
25188
- const signatureIndex = findPngSignature(data, offset);
25189
- if (signatureIndex === -1) break;
25190
- if (firstPngOffset === -1) firstPngOffset = signatureIndex;
25191
- const end = findPngEnd(data, signatureIndex);
25192
- if (end === -1) break;
25193
- frames.push(data.subarray(signatureIndex, end));
25194
- offset = end;
25195
- }
25196
- if (firstPngOffset === -1 || frames.length === 0) return { frames: [] };
25472
+ const parsed = parseJta(data);
25473
+ const derived = deriveMovieClipModel(parsed);
25197
25474
  return {
25198
- frames,
25199
- meta: parseJtaHeader(data, firstPngOffset, frames.length)
25200
- };
25201
- }
25202
- function findPngSignature(data, fromIndex) {
25203
- for (let index = fromIndex; index <= data.length - PNG_SIGNATURE.length; index += 1) {
25204
- let matched = true;
25205
- for (let signatureIndex = 0; signatureIndex < PNG_SIGNATURE.length; signatureIndex += 1) if (data[index + signatureIndex] !== PNG_SIGNATURE[signatureIndex]) {
25206
- matched = false;
25207
- break;
25475
+ frames: parsed.textures.map((texture) => texture.raw),
25476
+ meta: {
25477
+ interval: derived.interval,
25478
+ repeatDelay: derived.repeatDelay,
25479
+ swing: derived.swing,
25480
+ width: derived.dimensions.width,
25481
+ height: derived.dimensions.height,
25482
+ frames: derived.frames.map((frame) => ({
25483
+ addDelay: frame.addDelay,
25484
+ offsetX: frame.rectX,
25485
+ offsetY: frame.rectY,
25486
+ width: frame.rectWidth,
25487
+ height: frame.rectHeight,
25488
+ textureIndex: frame.textureIndex
25489
+ }))
25208
25490
  }
25209
- if (matched) return index;
25210
- }
25211
- return -1;
25491
+ };
25212
25492
  }
25213
- function findPngEnd(data, start) {
25214
- let position = start + PNG_SIGNATURE.length;
25215
- while (position + 8 <= data.length) {
25216
- const length = readUint32BE(data, position);
25217
- position += 8;
25218
- if (position + length + 4 > data.length) return -1;
25219
- const isEnd = data[position - 4] === 73 && data[position - 3] === 69 && data[position - 2] === 78 && data[position - 1] === 68;
25220
- position += length + 4;
25221
- if (isEnd) return position;
25222
- }
25223
- return -1;
25493
+ function detectSupportedRasterFormat(data) {
25494
+ if (data.length >= 8 && data[0] === 137 && data[1] === 80 && data[2] === 78 && data[3] === 71 && data[4] === 13 && data[5] === 10 && data[6] === 26 && data[7] === 10) return "png";
25495
+ if (data.length >= 2 && data[0] === 255 && data[1] === 216) return "jpeg";
25496
+ return null;
25224
25497
  }
25225
- function parseJtaHeader(data, firstPngOffset, frameCount) {
25226
- if (data.length < 10) return void 0;
25227
- const state = { offset: 0 };
25228
- const end = Math.min(firstPngOffset, data.length);
25229
- if (!readUtfBE(data, state, end)) return void 0;
25230
- const version = readInt32BEAt(data, state, end);
25231
- if (version == null) return void 0;
25232
- const fpsRaw = readInt8At(data, state, end);
25233
- if (fpsRaw == null) return void 0;
25234
- const fps = fpsRaw > 0 ? fpsRaw : 24;
25235
- if (state.offset + 3 > end) return void 0;
25236
- state.offset += 3;
25237
- if (version < 102) return void 0;
25238
- readUint16BEAt(data, state, end);
25239
- readUint16BEAt(data, state, end);
25240
- const width = readUint16BEAt(data, state, end);
25241
- const height = readUint16BEAt(data, state, end);
25242
- if (width == null || height == null) return void 0;
25243
- const speedRaw = readUint8At(data, state, end);
25244
- const repeatDelayRaw = readUint8At(data, state, end);
25245
- const swingRaw = readInt8At(data, state, end);
25246
- const frameTableCount = readInt16BEAt(data, state, end);
25247
- if (speedRaw == null || repeatDelayRaw == null || swingRaw == null || frameTableCount == null) return void 0;
25248
- const frames = [];
25249
- for (let index = 0; index < frameTableCount; index += 1) {
25250
- const delayRaw = readInt16BEAt(data, state, end);
25251
- const offsetX = readInt16BEAt(data, state, end);
25252
- const offsetY = readInt16BEAt(data, state, end);
25253
- const frameWidth = readInt16BEAt(data, state, end);
25254
- const frameHeight = readInt16BEAt(data, state, end);
25255
- const textureIndex = readInt16BEAt(data, state, end);
25256
- if (delayRaw == null || offsetX == null || offsetY == null || frameWidth == null || frameHeight == null || textureIndex == null) break;
25257
- frames.push({
25258
- addDelay: Math.trunc(1e3 / fps * delayRaw),
25259
- offsetX,
25260
- offsetY,
25261
- width: frameWidth,
25262
- height: frameHeight,
25263
- textureIndex
25498
+ function couldNotDecode(filePath, frameIndex, textureIndex) {
25499
+ return /* @__PURE__ */ new Error(`atlas: Could not decode MovieClip "${filePath}" frame ${frameIndex} (texture ${textureIndex}).`);
25500
+ }
25501
+ async function prepareJtaForPublish(data, encoder, filePath) {
25502
+ const extracted = extractJtaFrames(data);
25503
+ const firstFrameIndexByTextureIndex = /* @__PURE__ */ new Map();
25504
+ for (let frameIndex = 0; frameIndex < extracted.meta.frames.length; frameIndex += 1) {
25505
+ const textureIndex = extracted.meta.frames[frameIndex].textureIndex;
25506
+ if (textureIndex >= 0 && !firstFrameIndexByTextureIndex.has(textureIndex)) firstFrameIndexByTextureIndex.set(textureIndex, frameIndex);
25507
+ }
25508
+ const referencedTextures = [];
25509
+ for (let textureIndex = 0; textureIndex < extracted.frames.length; textureIndex += 1) {
25510
+ const firstFrameIndex = firstFrameIndexByTextureIndex.get(textureIndex);
25511
+ if (firstFrameIndex === void 0) continue;
25512
+ const raw = extracted.frames[textureIndex];
25513
+ if (raw.byteLength === 0) throw new Error(`atlas: MovieClip "${filePath}" frame ${firstFrameIndex} references empty texture ${textureIndex}.`);
25514
+ const detectedFormat = detectSupportedRasterFormat(raw);
25515
+ if (!detectedFormat) throw new Error(`atlas: MovieClip "${filePath}" frame ${firstFrameIndex} (texture ${textureIndex}) uses an unsupported raster format; only PNG and JPEG are supported.`);
25516
+ const imageInfo = probeRasterImage(raw);
25517
+ if (!imageInfo || imageInfo.format !== detectedFormat) throw couldNotDecode(filePath, firstFrameIndex, textureIndex);
25518
+ let buffer = raw;
25519
+ if (encoder) {
25520
+ try {
25521
+ buffer = await encoder(raw).png().toBuffer();
25522
+ } catch {
25523
+ throw couldNotDecode(filePath, firstFrameIndex, textureIndex);
25524
+ }
25525
+ const normalizedInfo = probeRasterImage(buffer);
25526
+ if (!normalizedInfo || normalizedInfo.format !== "png" || normalizedInfo.width !== imageInfo.width || normalizedInfo.height !== imageInfo.height) throw couldNotDecode(filePath, firstFrameIndex, textureIndex);
25527
+ }
25528
+ referencedTextures.push({
25529
+ textureIndex,
25530
+ firstFrameIndex,
25531
+ buffer,
25532
+ width: imageInfo.width,
25533
+ height: imageInfo.height
25264
25534
  });
25265
25535
  }
25266
25536
  return {
25267
- interval: Math.trunc(1e3 / fps * (speedRaw || 1)),
25268
- repeatDelay: Math.trunc(1e3 / fps * repeatDelayRaw),
25269
- swing: swingRaw === 1,
25270
- width,
25271
- height,
25272
- frames: frames.length === 0 && frameCount > 0 ? [] : frames
25537
+ ...extracted,
25538
+ referencedTextures
25273
25539
  };
25274
25540
  }
25275
- function readUtfBE(data, state, end) {
25276
- const length = readUint16BEAt(data, state, end);
25277
- if (length == null || state.offset + length > end) return null;
25278
- const value = new TextDecoder().decode(data.subarray(state.offset, state.offset + length));
25279
- state.offset += length;
25280
- return value;
25281
- }
25282
- function readUint8At(data, state, end) {
25283
- if (state.offset + 1 > end) return null;
25284
- const value = data[state.offset];
25285
- state.offset += 1;
25286
- return value ?? 0;
25287
- }
25288
- function readInt8At(data, state, end) {
25289
- if (state.offset + 1 > end) return null;
25290
- const value = new DataView(data.buffer, data.byteOffset, data.byteLength).getInt8(state.offset);
25291
- state.offset += 1;
25292
- return value;
25293
- }
25294
- function readUint16BEAt(data, state, end) {
25295
- if (state.offset + 2 > end) return null;
25296
- const value = readUint16BE(data, state.offset);
25297
- state.offset += 2;
25298
- return value;
25299
- }
25300
- function readInt16BEAt(data, state, end) {
25301
- if (state.offset + 2 > end) return null;
25302
- const value = new DataView(data.buffer, data.byteOffset, data.byteLength).getInt16(state.offset, false);
25303
- state.offset += 2;
25304
- return value;
25305
- }
25306
- function readInt32BEAt(data, state, end) {
25307
- if (state.offset + 4 > end) return null;
25308
- const value = new DataView(data.buffer, data.byteOffset, data.byteLength).getInt32(state.offset, false);
25309
- state.offset += 4;
25310
- return value;
25311
- }
25312
- function readUint16BE(data, offset) {
25313
- if (offset + 1 >= data.length) return 0;
25314
- return data[offset] << 8 | data[offset + 1];
25315
- }
25316
- function readUint32BE(data, offset) {
25317
- if (offset + 3 >= data.length) return 0;
25318
- return data[offset] * 16777216 + ((data[offset + 1] ?? 0) << 16) + ((data[offset + 2] ?? 0) << 8) + (data[offset + 3] ?? 0);
25319
- }
25320
25541
  //#endregion
25321
25542
  //#region ../functions/src/atlas/inputs.ts
25322
25543
  function getPublishedItemId(resource) {
@@ -25386,6 +25607,27 @@ async function _trimImage(encoder, input, originalWidth, originalHeight) {
25386
25607
  };
25387
25608
  }
25388
25609
  }
25610
+ function resolveMovieClipSourcePath(resource, pkg, basePath) {
25611
+ const fileName = `${resource.getName()}.jta`;
25612
+ const resourcePath = resource.getPath() ?? "/";
25613
+ return `${basePath}/${pkg.getName()}${resourcePath}${fileName}`;
25614
+ }
25615
+ async function prepareMovieClipResource(resource, pkg, encoder, basePath, readFileRaw) {
25616
+ const filePath = resolveMovieClipSourcePath(resource, pkg, basePath);
25617
+ let raw;
25618
+ try {
25619
+ raw = await readFileRaw(filePath);
25620
+ } catch {
25621
+ throw new Error(`atlas: Could not read MovieClip "${filePath}".`);
25622
+ }
25623
+ try {
25624
+ return await prepareJtaForPublish(raw, encoder, filePath);
25625
+ } catch (error) {
25626
+ if (error instanceof Error && error.message.startsWith("atlas:")) throw error;
25627
+ const detail = error instanceof Error ? ` ${error.message}` : "";
25628
+ throw new Error(`atlas: Could not parse MovieClip "${filePath}".${detail}`);
25629
+ }
25630
+ }
25389
25631
  /** Collect a single ImageResource into the inputs array. */
25390
25632
  async function collectImage(resource, pkg, inputs, encoder, options, doTrim, logger) {
25391
25633
  let origW = resource.getWidth() ?? 0;
@@ -25413,8 +25655,11 @@ async function collectImage(resource, pkg, inputs, encoder, options, doTrim, log
25413
25655
  }).png().toBuffer();
25414
25656
  sourceHasAlpha = true;
25415
25657
  }
25416
- } catch {
25417
- if (options.strictOutput) throw new Error(`atlas: Could not read image "${filePath}".`);
25658
+ } catch (error) {
25659
+ if (options.strictOutput) {
25660
+ const detail = error instanceof Error && error.message.startsWith("publishBrowser:") ? ` ${error.message}` : "";
25661
+ throw new Error(`atlas: Could not read image "${filePath}".${detail}`);
25662
+ }
25418
25663
  if (origW === 0 || origH === 0) {
25419
25664
  logger.warn(`atlas: Could not read image "${filePath}", skipping.`);
25420
25665
  return;
@@ -25459,82 +25704,45 @@ async function collectMovieClipFrames(doc, resource, pkg, inputs, encoder, optio
25459
25704
  }
25460
25705
  if (!encoder && options.strictOutput) throw new Error(`atlas: MovieClip "${resource.getId()}" requires an encoder for complete raster output.`);
25461
25706
  const mcId = resource.getId();
25462
- const mcName = resource.getName() + ".jta";
25463
- const mcPath = resource.getPath() ?? "/";
25464
- const filePath = `${options.basePath}/${pkg.getName()}${mcPath}${mcName}`;
25707
+ const filePath = resolveMovieClipSourcePath(resource, pkg, options.basePath);
25465
25708
  try {
25466
- const jta = extractJtaFrames(await options.readFileRaw(filePath));
25467
- if (jta.frames.length === 0) return;
25468
- const frameMetas = jta.meta?.frames ?? [];
25709
+ const jta = options.preparedMovieClips?.get(resource) ?? await prepareMovieClipResource(resource, pkg, encoder, options.basePath, options.readFileRaw);
25469
25710
  for (const frame of resource.listFrames()) resource.removeFrame(frame);
25470
- resource.setInterval(jta.meta?.interval ?? 100).setSwing(jta.meta?.swing ?? false).setRepeatDelay(jta.meta?.repeatDelay ?? 0);
25471
- if (frameMetas.length > 0) {
25472
- const firstFrameIndexByTextureIndex = /* @__PURE__ */ new Map();
25473
- for (let frameIndex = 0; frameIndex < frameMetas.length; frameIndex += 1) {
25474
- const meta = frameMetas[frameIndex];
25475
- const textureIndex = Number.isFinite(meta.textureIndex) ? meta.textureIndex : frameIndex;
25476
- if (!firstFrameIndexByTextureIndex.has(textureIndex)) firstFrameIndexByTextureIndex.set(textureIndex, frameIndex);
25477
- }
25478
- const spriteIdByTextureIndex = /* @__PURE__ */ new Map();
25479
- for (let textureIndex = 0; textureIndex < jta.frames.length; textureIndex += 1) {
25480
- const exportFrameIndex = firstFrameIndexByTextureIndex.get(textureIndex);
25481
- if (exportFrameIndex === void 0) continue;
25482
- const itemId = `${mcId}_${exportFrameIndex}`;
25483
- const input = await createMovieClipFrameInput(jta.frames[textureIndex], itemId, resource, encoder, options.strictOutput);
25484
- if (!input) continue;
25485
- inputs.push(input);
25486
- spriteIdByTextureIndex.set(textureIndex, itemId);
25487
- }
25488
- for (let frameIndex = 0; frameIndex < frameMetas.length; frameIndex += 1) {
25489
- const meta = frameMetas[frameIndex];
25490
- const textureIndex = Number.isFinite(meta.textureIndex) ? meta.textureIndex : frameIndex;
25491
- const frame = doc.createMovieFrame(`${mcId}_${frameIndex}`);
25492
- frame.setRectX(meta.offsetX).setRectY(meta.offsetY).setRectWidth(meta.width).setRectHeight(meta.height).setAddDelay(meta.addDelay).setSpriteId(spriteIdByTextureIndex.get(textureIndex) ?? "");
25493
- resource.addFrame(frame);
25494
- }
25495
- } else for (let frameIndex = 0; frameIndex < jta.frames.length; frameIndex += 1) {
25496
- const itemId = `${mcId}_${frameIndex}`;
25497
- const input = await createMovieClipFrameInput(jta.frames[frameIndex], itemId, resource, encoder, options.strictOutput);
25498
- if (!input) continue;
25499
- inputs.push(input);
25500
- const frame = doc.createMovieFrame(itemId);
25501
- frame.setRectX(0).setRectY(0).setRectWidth(input.originalWidth).setRectHeight(input.originalHeight).setAddDelay(0).setSpriteId(itemId);
25711
+ resource.setInterval(jta.meta.interval).setSwing(jta.meta.swing).setRepeatDelay(jta.meta.repeatDelay);
25712
+ const spriteIdByTextureIndex = /* @__PURE__ */ new Map();
25713
+ for (const texture of jta.referencedTextures) {
25714
+ if (texture.width <= 0 || texture.height <= 0) continue;
25715
+ const itemId = `${mcId}_${texture.firstFrameIndex}`;
25716
+ inputs.push({
25717
+ id: itemId,
25718
+ width: texture.width,
25719
+ height: texture.height,
25720
+ originalWidth: texture.width,
25721
+ originalHeight: texture.height,
25722
+ offsetX: 0,
25723
+ offsetY: 0,
25724
+ resource,
25725
+ trimBuffer: texture.buffer,
25726
+ sourceKind: "movieclip-frame"
25727
+ });
25728
+ spriteIdByTextureIndex.set(texture.textureIndex, itemId);
25729
+ }
25730
+ for (let frameIndex = 0; frameIndex < jta.meta.frames.length; frameIndex += 1) {
25731
+ const meta = jta.meta.frames[frameIndex];
25732
+ const frame = doc.createMovieFrame(`${mcId}_${frameIndex}`);
25733
+ frame.setRectX(meta.offsetX).setRectY(meta.offsetY).setRectWidth(meta.width).setRectHeight(meta.height).setAddDelay(meta.addDelay).setSpriteId(meta.textureIndex === -1 ? "" : spriteIdByTextureIndex.get(meta.textureIndex) ?? "");
25502
25734
  resource.addFrame(frame);
25503
25735
  }
25504
- if ((jta.meta?.width ?? 0) > 0 && (jta.meta?.height ?? 0) > 0) {
25505
- resource.setWidth(jta.meta?.width ?? 0);
25506
- resource.setHeight(jta.meta?.height ?? 0);
25736
+ if (jta.meta.width > 0 && jta.meta.height > 0) {
25737
+ resource.setWidth(jta.meta.width);
25738
+ resource.setHeight(jta.meta.height);
25507
25739
  }
25508
- } catch {
25509
- const message = `atlas: Could not parse MovieClip "${filePath}".`;
25510
- if (options.strictOutput) throw new Error(message);
25740
+ } catch (error) {
25741
+ const message = error instanceof Error ? error.message : `atlas: Could not parse MovieClip "${filePath}".`;
25742
+ if (options.strictOutput) throw error;
25511
25743
  logger.warn(`${message} Skipping frames.`);
25512
25744
  }
25513
25745
  }
25514
- async function createMovieClipFrameInput(buffer, itemId, resource, encoder, strictOutput) {
25515
- if (!encoder || buffer.length === 0) return null;
25516
- try {
25517
- const meta = await encoder(buffer).metadata();
25518
- const width = meta.width ?? 0;
25519
- const height = meta.height ?? 0;
25520
- if (width <= 0 || height <= 0) return null;
25521
- return {
25522
- id: itemId,
25523
- width,
25524
- height,
25525
- originalWidth: width,
25526
- originalHeight: height,
25527
- offsetX: 0,
25528
- offsetY: 0,
25529
- resource,
25530
- trimBuffer: buffer,
25531
- sourceKind: "movieclip-frame"
25532
- };
25533
- } catch {
25534
- if (strictOutput) throw new Error(`atlas: Could not decode MovieClip frame "${itemId}".`);
25535
- return null;
25536
- }
25537
- }
25538
25746
  /** Collect a Bitmap Font's texture image, packed under the font's ID. */
25539
25747
  async function collectFontTexture(doc, fontRes, pkg, options) {
25540
25748
  const textureId = fontRes.getTextureId?.() ?? "";
@@ -27958,6 +28166,14 @@ var RestoreWorkflow = class {
27958
28166
  common: {},
27959
28167
  adaptation: {}
27960
28168
  });
28169
+ for (const pkg of doc.getRoot().listPackages()) pkg.setSourceAtlasSettings({
28170
+ ...pkg.getSourceAtlasSettings(),
28171
+ atlases: pkg.listAtlases().map((atlas) => ({
28172
+ index: atlas.getIndex(),
28173
+ name: atlas.getIndex() === 0 ? "Default" : atlas.getName(),
28174
+ compression: false
28175
+ }))
28176
+ });
27961
28177
  }
27962
28178
  _initializeImageFileNames(doc) {
27963
28179
  for (const pkg of doc.getRoot().listPackages()) for (const resource of pkg.listResources()) {
@@ -28611,7 +28827,7 @@ function publish(options) {
28611
28827
  return paths.join("/");
28612
28828
  }
28613
28829
  });
28614
- const publishPackage = async (plan, writerFs, packageIndex) => {
28830
+ const publishPackage = async (plan, writerFs, packageIndex, preparedMovieClips) => {
28615
28831
  if (options.fs && !plan.outputDir) throw new Error("publish: no output directory resolved. Provide --output, or configure global publish.path / package publishPath.");
28616
28832
  if (options.fs) {
28617
28833
  await options.fs.mkdir(plan.outputDir);
@@ -28629,6 +28845,7 @@ function publish(options) {
28629
28845
  mkdir: options.fs ? options.fs.mkdir : void 0,
28630
28846
  readFileRaw: options.atlas?.readFileRaw ?? options.fs?.readFileRaw,
28631
28847
  strictOutput: options.fs !== void 0,
28848
+ preparedMovieClips,
28632
28849
  packages: [plan.pkg.getName()],
28633
28850
  ...atlasRuntimeOptions
28634
28851
  })(doc);
@@ -28681,8 +28898,25 @@ function publish(options) {
28681
28898
  }
28682
28899
  const unresolvedPlan = plans.find((plan) => !plan.outputDir);
28683
28900
  if (unresolvedPlan) throw new Error(`publish: no output directory resolved for package "${unresolvedPlan.pkg.getName()}". Provide --output, or configure global publish.path / package publishPath.`);
28901
+ const publishedMovieClips = allPackages.flatMap((pkg) => {
28902
+ const publishedResourceIds = getAnnotatedPublishedResourceIds(pkg);
28903
+ return pkg.listResources().filter((resource) => {
28904
+ return publishedResourceIds.has(resource.getId()) && isMovieClipResource(resource);
28905
+ }).map((resource) => ({
28906
+ pkg,
28907
+ resource
28908
+ }));
28909
+ });
28910
+ const preparedMovieClips = /* @__PURE__ */ new Map();
28911
+ if (publishedMovieClips.length > 0) {
28912
+ if (!options.encoder) throw new Error("publish: MovieClip output requires an encoder.");
28913
+ if (!options.basePath) throw new Error("publish: MovieClip output requires basePath.");
28914
+ const readFileRaw = options.atlas?.readFileRaw ?? options.fs.readFileRaw;
28915
+ if (!readFileRaw) throw new Error("publish: MovieClip output requires readFileRaw.");
28916
+ for (const { pkg, resource } of publishedMovieClips) preparedMovieClips.set(resource, await prepareMovieClipResource(resource, pkg, options.encoder, options.basePath, readFileRaw));
28917
+ }
28684
28918
  const writerFs = toBinaryWriterFileSystem(options.fs);
28685
- for (const plan of plans) await publishPackage(plan, writerFs, allDocPackages.indexOf(plan.pkg));
28919
+ for (const plan of plans) await publishPackage(plan, writerFs, allDocPackages.indexOf(plan.pkg), preparedMovieClips);
28686
28920
  if (options.codeGeneration !== false) await publishCodeGeneration(doc, {
28687
28921
  basePath: options.basePath,
28688
28922
  fs: options.fs,