@nice2dev/ui-graphics 1.3.15 → 1.3.16

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 (294) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/dist/cjs/animation/AnimatedPerson.js +470 -143
  3. package/dist/cjs/animation/AnimationEditor.js +289 -101
  4. package/dist/cjs/animation/Audience.js +83 -61
  5. package/dist/cjs/animation/BodyRenderer.js +69 -28
  6. package/dist/cjs/animation/animationHelper.js +281 -288
  7. package/dist/cjs/animation/characterTypes.js +200 -203
  8. package/dist/cjs/animation/choreoDSL.js +205 -219
  9. package/dist/cjs/animation/choreography.js +4 -32
  10. package/dist/cjs/animation/karaokeIntegration.js +68 -77
  11. package/dist/cjs/animation/lottieExport.js +108 -114
  12. package/dist/cjs/animation/motionPath.js +225 -248
  13. package/dist/cjs/animation/physics.js +227 -254
  14. package/dist/cjs/animation/rig/RigPlayer.js +53 -56
  15. package/dist/cjs/animation/rig/RiggedBody.js +71 -26
  16. package/dist/cjs/animation/rig/choreoRigBridge.js +72 -77
  17. package/dist/cjs/animation/rig/ik.js +13 -18
  18. package/dist/cjs/animation/rig/poseOps.js +34 -41
  19. package/dist/cjs/animation/rig/presets.js +64 -72
  20. package/dist/cjs/animation/rig/rigMath.js +25 -32
  21. package/dist/cjs/animation/rig/rigTypes.js +59 -63
  22. package/dist/cjs/animation/shapes/arms.js +31 -13
  23. package/dist/cjs/animation/shapes/eyes.js +129 -47
  24. package/dist/cjs/animation/shapes/hair.js +72 -45
  25. package/dist/cjs/animation/shapes/heads.js +131 -57
  26. package/dist/cjs/animation/shapes/headwear.js +47 -38
  27. package/dist/cjs/animation/shapes/legs.js +31 -13
  28. package/dist/cjs/animation/shapes/mouths.js +120 -44
  29. package/dist/cjs/animation/shapes/noses.js +80 -25
  30. package/dist/cjs/animation/shapes/outfits.js +101 -39
  31. package/dist/cjs/animation/shapes/torsos.js +15 -11
  32. package/dist/cjs/animation/spineExport.js +103 -121
  33. package/dist/cjs/core/LocalUI.js +108 -43
  34. package/dist/cjs/core/NiceBrushEngine.js +758 -552
  35. package/dist/cjs/core/collaboration.js +249 -251
  36. package/dist/cjs/core/colorBlindness.js +100 -115
  37. package/dist/cjs/core/dragDrop.js +155 -158
  38. package/dist/cjs/core/editorAsk.js +51 -41
  39. package/dist/cjs/core/fixes.js +140 -171
  40. package/dist/cjs/core/gridGuides.js +166 -201
  41. package/dist/cjs/core/historyVisual.js +156 -135
  42. package/dist/cjs/core/i18n.js +13 -14
  43. package/dist/cjs/core/integrations.js +238 -249
  44. package/dist/cjs/core/minimap.js +149 -151
  45. package/dist/cjs/core/plugins.js +98 -113
  46. package/dist/cjs/core/rtl.js +71 -83
  47. package/dist/cjs/core/shortcuts.js +196 -225
  48. package/dist/cjs/core/theme.js +142 -149
  49. package/dist/cjs/core/yjsCollaboration.js +232 -293
  50. package/dist/cjs/design/design-tools.js +12 -22
  51. package/dist/cjs/font/NiceFontEditor.js +877 -316
  52. package/dist/cjs/font/fontFormatUtils.js +388 -448
  53. package/dist/cjs/game/GameAsset2dEditor.js +1201 -392
  54. package/dist/cjs/game/GameAsset2dEditor.module.css.js +146 -5
  55. package/dist/cjs/game/gameAssetTypes.js +7 -16
  56. package/dist/cjs/game/gameAssetUtils.js +390 -413
  57. package/dist/cjs/game/useGameAssetEditor.js +820 -763
  58. package/dist/cjs/index.js +81 -84
  59. package/dist/cjs/nice2dev-ui-graphics.css +1 -1
  60. package/dist/cjs/node_modules/lib0/array.js +8 -70
  61. package/dist/cjs/node_modules/lib0/binary.js +3 -20
  62. package/dist/cjs/node_modules/lib0/broadcastchannel.js +24 -86
  63. package/dist/cjs/node_modules/lib0/buffer.js +15 -82
  64. package/dist/cjs/node_modules/lib0/conditions.js +3 -16
  65. package/dist/cjs/node_modules/lib0/decoding.js +108 -287
  66. package/dist/cjs/node_modules/lib0/dom.js +14 -44
  67. package/dist/cjs/node_modules/lib0/encoding.js +68 -418
  68. package/dist/cjs/node_modules/lib0/environment.js +26 -92
  69. package/dist/cjs/node_modules/lib0/error.js +5 -29
  70. package/dist/cjs/node_modules/lib0/function.js +27 -66
  71. package/dist/cjs/node_modules/lib0/iterator.js +8 -33
  72. package/dist/cjs/node_modules/lib0/logging.common.js +12 -22
  73. package/dist/cjs/node_modules/lib0/logging.js +32 -71
  74. package/dist/cjs/node_modules/lib0/map.js +16 -86
  75. package/dist/cjs/node_modules/lib0/math.js +3 -32
  76. package/dist/cjs/node_modules/lib0/number.js +4 -15
  77. package/dist/cjs/node_modules/lib0/object.js +14 -88
  78. package/dist/cjs/node_modules/lib0/observable.js +38 -78
  79. package/dist/cjs/node_modules/lib0/pair.js +3 -20
  80. package/dist/cjs/node_modules/lib0/prng.js +8 -77
  81. package/dist/cjs/node_modules/lib0/promise.js +6 -30
  82. package/dist/cjs/node_modules/lib0/random.js +8 -23
  83. package/dist/cjs/node_modules/lib0/schema.js +313 -687
  84. package/dist/cjs/node_modules/lib0/set.js +3 -10
  85. package/dist/cjs/node_modules/lib0/storage.js +20 -55
  86. package/dist/cjs/node_modules/lib0/string.js +17 -72
  87. package/dist/cjs/node_modules/lib0/symbol.js +2 -12
  88. package/dist/cjs/node_modules/lib0/time.js +2 -15
  89. package/dist/cjs/node_modules/lib0/trait/equality.js +3 -28
  90. package/dist/cjs/node_modules/lib0/url.js +4 -18
  91. package/dist/cjs/node_modules/lib0/webcrypto.js +2 -5
  92. package/dist/cjs/node_modules/y-protocols/auth.js +5 -19
  93. package/dist/cjs/node_modules/y-protocols/awareness.js +53 -116
  94. package/dist/cjs/node_modules/y-protocols/sync.js +16 -103
  95. package/dist/cjs/node_modules/y-websocket/src/y-websocket.js +77 -219
  96. package/dist/cjs/node_modules/yjs/dist/yjs.js +2128 -4868
  97. package/dist/cjs/photo/FilterThumb.js +37 -25
  98. package/dist/cjs/photo/NiceAITools.js +679 -467
  99. package/dist/cjs/photo/PhotoEditor.js +34 -20
  100. package/dist/cjs/photo/PhotoEditor.module.css.js +296 -5
  101. package/dist/cjs/photo/PhotoEditorCanvas.js +250 -91
  102. package/dist/cjs/photo/PhotoEditorLeftPanel.js +1086 -224
  103. package/dist/cjs/photo/PhotoEditorToolbar.js +135 -19
  104. package/dist/cjs/photo/photoEditorActions.js +89 -72
  105. package/dist/cjs/photo/photoEditorTypes.js +29 -32
  106. package/dist/cjs/photo/photoFilters.js +959 -985
  107. package/dist/cjs/photo/photoOverlays.js +899 -945
  108. package/dist/cjs/photo/photoSelection.js +427 -499
  109. package/dist/cjs/photo/usePhotoEditor.js +739 -741
  110. package/dist/cjs/pixel/HSVPicker.js +191 -175
  111. package/dist/cjs/pixel/PixelEditor.js +159 -48
  112. package/dist/cjs/pixel/PixelEditor.module.css.js +209 -5
  113. package/dist/cjs/pixel/PixelEditorCanvas.js +29 -16
  114. package/dist/cjs/pixel/PixelEditorMenuBar.js +160 -162
  115. package/dist/cjs/pixel/PixelEditorRightPanel.js +377 -45
  116. package/dist/cjs/pixel/PixelEditorStatusBar.js +47 -20
  117. package/dist/cjs/pixel/PixelEditorTimeline.js +106 -23
  118. package/dist/cjs/pixel/PixelEditorToolbar.js +99 -21
  119. package/dist/cjs/pixel/SpriteGeneratorPanel.js +483 -303
  120. package/dist/cjs/pixel/animalGeneratorPresets.js +776 -601
  121. package/dist/cjs/pixel/asepriteFormat.js +511 -560
  122. package/dist/cjs/pixel/buildingGeneratorPresets.js +693 -520
  123. package/dist/cjs/pixel/characterGeneratorPresets.js +644 -544
  124. package/dist/cjs/pixel/pixel-pro.js +52 -63
  125. package/dist/cjs/pixel/pixelEditorExports.js +388 -428
  126. package/dist/cjs/pixel/pixelEditorTypes.js +91 -108
  127. package/dist/cjs/pixel/pixelEditorUtils.js +519 -561
  128. package/dist/cjs/pixel/spriteGeneratorCore.js +206 -257
  129. package/dist/cjs/pixel/usePixelEditor.js +1321 -1343
  130. package/dist/cjs/texture/Nice3DTexturePainter.js +707 -440
  131. package/dist/cjs/ui/NiceUIDesigner.js +954 -527
  132. package/dist/cjs/vector/NiceSvgToCode.js +586 -446
  133. package/dist/cjs/vector/VectorEditor.js +290 -117
  134. package/dist/cjs/vector/VectorEditor.module.css.js +86 -5
  135. package/dist/cjs/vector/VectorEditorMenuBar.js +333 -275
  136. package/dist/cjs/vector/VectorEditorRightPanel.js +1094 -371
  137. package/dist/cjs/vector/VectorEditorShapeRenderer.js +198 -60
  138. package/dist/cjs/vector/VectorEditorStatusBar.js +39 -13
  139. package/dist/cjs/vector/useVectorEditor.js +848 -889
  140. package/dist/cjs/vector/vectorAnimation.js +110 -137
  141. package/dist/cjs/vector/vectorBooleanOps.js +398 -485
  142. package/dist/cjs/vector/vectorEditorExport.js +201 -203
  143. package/dist/cjs/vector/vectorEditorImport.js +173 -189
  144. package/dist/cjs/vector/vectorEditorTypes.js +42 -49
  145. package/dist/cjs/vector/vectorGradients.js +151 -174
  146. package/dist/cjs/vector/vectorIconExport.js +52 -59
  147. package/dist/cjs/vector/vectorIconImport.js +169 -219
  148. package/dist/esm/animation/AnimatedPerson.js +470 -141
  149. package/dist/esm/animation/AnimationEditor.js +290 -100
  150. package/dist/esm/animation/Audience.js +83 -59
  151. package/dist/esm/animation/BodyRenderer.js +71 -25
  152. package/dist/esm/animation/animationHelper.js +284 -287
  153. package/dist/esm/animation/characterTypes.js +204 -202
  154. package/dist/esm/animation/choreoDSL.js +212 -218
  155. package/dist/esm/animation/choreography.js +8 -31
  156. package/dist/esm/animation/karaokeIntegration.js +74 -76
  157. package/dist/esm/animation/lottieExport.js +112 -113
  158. package/dist/esm/animation/motionPath.js +235 -247
  159. package/dist/esm/animation/physics.js +235 -253
  160. package/dist/esm/animation/rig/RigPlayer.js +53 -54
  161. package/dist/esm/animation/rig/RiggedBody.js +71 -24
  162. package/dist/esm/animation/rig/choreoRigBridge.js +74 -76
  163. package/dist/esm/animation/rig/ik.js +14 -17
  164. package/dist/esm/animation/rig/poseOps.js +37 -40
  165. package/dist/esm/animation/rig/presets.js +70 -71
  166. package/dist/esm/animation/rig/rigMath.js +28 -31
  167. package/dist/esm/animation/rig/rigTypes.js +62 -62
  168. package/dist/esm/animation/shapes/arms.js +32 -12
  169. package/dist/esm/animation/shapes/eyes.js +135 -46
  170. package/dist/esm/animation/shapes/hair.js +80 -44
  171. package/dist/esm/animation/shapes/heads.js +140 -56
  172. package/dist/esm/animation/shapes/headwear.js +54 -37
  173. package/dist/esm/animation/shapes/legs.js +32 -12
  174. package/dist/esm/animation/shapes/mouths.js +127 -43
  175. package/dist/esm/animation/shapes/noses.js +87 -24
  176. package/dist/esm/animation/shapes/outfits.js +106 -38
  177. package/dist/esm/animation/shapes/torsos.js +16 -10
  178. package/dist/esm/animation/spineExport.js +106 -120
  179. package/dist/esm/core/LocalUI.js +111 -42
  180. package/dist/esm/core/NiceBrushEngine.js +761 -551
  181. package/dist/esm/core/collaboration.js +255 -250
  182. package/dist/esm/core/colorBlindness.js +106 -114
  183. package/dist/esm/core/dragDrop.js +157 -157
  184. package/dist/esm/core/editorAsk.js +53 -40
  185. package/dist/esm/core/fixes.js +145 -170
  186. package/dist/esm/core/gridGuides.js +178 -200
  187. package/dist/esm/core/historyVisual.js +158 -134
  188. package/dist/esm/core/i18n.js +15 -13
  189. package/dist/esm/core/integrations.js +244 -248
  190. package/dist/esm/core/minimap.js +151 -150
  191. package/dist/esm/core/plugins.js +102 -112
  192. package/dist/esm/core/rtl.js +77 -82
  193. package/dist/esm/core/shortcuts.js +200 -224
  194. package/dist/esm/core/theme.js +150 -148
  195. package/dist/esm/core/yjsCollaboration.js +240 -292
  196. package/dist/esm/design/design-tools.js +13 -21
  197. package/dist/esm/font/NiceFontEditor.js +880 -315
  198. package/dist/esm/font/fontFormatUtils.js +394 -447
  199. package/dist/esm/game/GameAsset2dEditor.js +1202 -391
  200. package/dist/esm/game/GameAsset2dEditor.module.css.js +146 -3
  201. package/dist/esm/game/gameAssetTypes.js +8 -15
  202. package/dist/esm/game/gameAssetUtils.js +408 -412
  203. package/dist/esm/game/useGameAssetEditor.js +821 -762
  204. package/dist/esm/index.js +332 -69
  205. package/dist/esm/nice2dev-ui-graphics.css +1 -1
  206. package/dist/esm/node_modules/lib0/array.js +15 -69
  207. package/dist/esm/node_modules/lib0/binary.js +14 -19
  208. package/dist/esm/node_modules/lib0/broadcastchannel.js +27 -85
  209. package/dist/esm/node_modules/lib0/buffer.js +20 -80
  210. package/dist/esm/node_modules/lib0/conditions.js +4 -15
  211. package/dist/esm/node_modules/lib0/decoding.js +128 -286
  212. package/dist/esm/node_modules/lib0/dom.js +20 -43
  213. package/dist/esm/node_modules/lib0/encoding.js +90 -415
  214. package/dist/esm/node_modules/lib0/environment.js +33 -91
  215. package/dist/esm/node_modules/lib0/error.js +8 -28
  216. package/dist/esm/node_modules/lib0/function.js +31 -65
  217. package/dist/esm/node_modules/lib0/iterator.js +11 -32
  218. package/dist/esm/node_modules/lib0/logging.common.js +22 -21
  219. package/dist/esm/node_modules/lib0/logging.js +44 -70
  220. package/dist/esm/node_modules/lib0/map.js +21 -85
  221. package/dist/esm/node_modules/lib0/math.js +9 -31
  222. package/dist/esm/node_modules/lib0/number.js +7 -14
  223. package/dist/esm/node_modules/lib0/object.js +26 -87
  224. package/dist/esm/node_modules/lib0/observable.js +40 -77
  225. package/dist/esm/node_modules/lib0/pair.js +5 -19
  226. package/dist/esm/node_modules/lib0/prng.js +15 -76
  227. package/dist/esm/node_modules/lib0/promise.js +7 -29
  228. package/dist/esm/node_modules/lib0/random.js +10 -22
  229. package/dist/esm/node_modules/lib0/schema.js +367 -686
  230. package/dist/esm/node_modules/lib0/set.js +4 -9
  231. package/dist/esm/node_modules/lib0/storage.js +23 -54
  232. package/dist/esm/node_modules/lib0/string.js +26 -71
  233. package/dist/esm/node_modules/lib0/symbol.js +3 -11
  234. package/dist/esm/node_modules/lib0/time.js +3 -14
  235. package/dist/esm/node_modules/lib0/trait/equality.js +5 -27
  236. package/dist/esm/node_modules/lib0/url.js +5 -17
  237. package/dist/esm/node_modules/lib0/webcrypto.js +3 -4
  238. package/dist/esm/node_modules/y-protocols/auth.js +7 -18
  239. package/dist/esm/node_modules/y-protocols/awareness.js +58 -115
  240. package/dist/esm/node_modules/y-protocols/sync.js +26 -102
  241. package/dist/esm/node_modules/y-websocket/src/y-websocket.js +82 -218
  242. package/dist/esm/node_modules/yjs/dist/yjs.js +2263 -4899
  243. package/dist/esm/photo/FilterThumb.js +37 -23
  244. package/dist/esm/photo/NiceAITools.js +685 -466
  245. package/dist/esm/photo/PhotoEditor.js +34 -18
  246. package/dist/esm/photo/PhotoEditor.module.css.js +296 -3
  247. package/dist/esm/photo/PhotoEditorCanvas.js +250 -89
  248. package/dist/esm/photo/PhotoEditorLeftPanel.js +1086 -222
  249. package/dist/esm/photo/PhotoEditorToolbar.js +135 -17
  250. package/dist/esm/photo/photoEditorActions.js +93 -71
  251. package/dist/esm/photo/photoEditorTypes.js +32 -31
  252. package/dist/esm/photo/photoFilters.js +967 -984
  253. package/dist/esm/photo/photoOverlays.js +913 -944
  254. package/dist/esm/photo/photoSelection.js +438 -498
  255. package/dist/esm/photo/usePhotoEditor.js +740 -740
  256. package/dist/esm/pixel/HSVPicker.js +192 -174
  257. package/dist/esm/pixel/PixelEditor.js +160 -47
  258. package/dist/esm/pixel/PixelEditor.module.css.js +209 -3
  259. package/dist/esm/pixel/PixelEditorCanvas.js +29 -14
  260. package/dist/esm/pixel/PixelEditorMenuBar.js +160 -160
  261. package/dist/esm/pixel/PixelEditorRightPanel.js +377 -43
  262. package/dist/esm/pixel/PixelEditorStatusBar.js +47 -18
  263. package/dist/esm/pixel/PixelEditorTimeline.js +106 -21
  264. package/dist/esm/pixel/PixelEditorToolbar.js +99 -19
  265. package/dist/esm/pixel/SpriteGeneratorPanel.js +483 -301
  266. package/dist/esm/pixel/animalGeneratorPresets.js +782 -600
  267. package/dist/esm/pixel/asepriteFormat.js +517 -559
  268. package/dist/esm/pixel/buildingGeneratorPresets.js +699 -519
  269. package/dist/esm/pixel/characterGeneratorPresets.js +650 -543
  270. package/dist/esm/pixel/pixel-pro.js +53 -62
  271. package/dist/esm/pixel/pixelEditorExports.js +397 -427
  272. package/dist/esm/pixel/pixelEditorTypes.js +103 -107
  273. package/dist/esm/pixel/pixelEditorUtils.js +551 -560
  274. package/dist/esm/pixel/spriteGeneratorCore.js +223 -256
  275. package/dist/esm/pixel/usePixelEditor.js +1322 -1342
  276. package/dist/esm/texture/Nice3DTexturePainter.js +709 -439
  277. package/dist/esm/ui/NiceUIDesigner.js +957 -526
  278. package/dist/esm/vector/NiceSvgToCode.js +589 -445
  279. package/dist/esm/vector/VectorEditor.js +291 -116
  280. package/dist/esm/vector/VectorEditor.module.css.js +86 -3
  281. package/dist/esm/vector/VectorEditorMenuBar.js +333 -273
  282. package/dist/esm/vector/VectorEditorRightPanel.js +1094 -369
  283. package/dist/esm/vector/VectorEditorShapeRenderer.js +199 -59
  284. package/dist/esm/vector/VectorEditorStatusBar.js +39 -11
  285. package/dist/esm/vector/useVectorEditor.js +849 -888
  286. package/dist/esm/vector/vectorAnimation.js +115 -136
  287. package/dist/esm/vector/vectorBooleanOps.js +401 -484
  288. package/dist/esm/vector/vectorEditorExport.js +207 -202
  289. package/dist/esm/vector/vectorEditorImport.js +174 -188
  290. package/dist/esm/vector/vectorEditorTypes.js +50 -48
  291. package/dist/esm/vector/vectorGradients.js +158 -173
  292. package/dist/esm/vector/vectorIconExport.js +53 -58
  293. package/dist/esm/vector/vectorIconImport.js +171 -218
  294. package/package.json +9 -15
@@ -1,592 +1,543 @@
1
- 'use strict';
2
-
3
- /**
4
- * asepriteFormat.ts — Aseprite .ase/.aseprite binary format parser & writer.
5
- * Spec: https://github.com/aseprite/aseprite/blob/main/docs/ase-file-specs.md
6
- *
7
- * Supports:
8
- * - RGBA color depth (32-bit)
9
- * - Layers (normal, groups)
10
- * - Cels (raw + zlib-compressed read, raw write)
11
- * - Palette chunks
12
- * - Frame tags
13
- * - Round-trip: parseAse → writeAse produces valid .ase files
14
- */
15
- // ── Binary helpers ───────────────────────────────────────────
1
+ "use strict";
2
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
16
3
  class BinaryReader {
17
- constructor(buffer) {
18
- this.pos = 0;
19
- this.view = new DataView(buffer);
20
- }
21
- get offset() {
22
- return this.pos;
23
- }
24
- set offset(v) {
25
- this.pos = v;
26
- }
27
- get length() {
28
- return this.view.byteLength;
29
- }
30
- byte() {
31
- return this.view.getUint8(this.pos++);
32
- }
33
- word() {
34
- const v = this.view.getUint16(this.pos, true);
35
- this.pos += 2;
36
- return v;
37
- }
38
- short() {
39
- const v = this.view.getInt16(this.pos, true);
40
- this.pos += 2;
41
- return v;
42
- }
43
- dword() {
44
- const v = this.view.getUint32(this.pos, true);
45
- this.pos += 4;
46
- return v;
47
- }
48
- long() {
49
- const v = this.view.getInt32(this.pos, true);
50
- this.pos += 4;
51
- return v;
52
- }
53
- bytes(n) {
54
- const data = new Uint8Array(this.view.buffer, this.pos, n);
55
- this.pos += n;
56
- return data;
57
- }
58
- skip(n) {
59
- this.pos += n;
60
- }
61
- string() {
62
- const len = this.word();
63
- const bytes = this.bytes(len);
64
- return new TextDecoder('utf-8').decode(bytes);
65
- }
4
+ constructor(buffer) {
5
+ this.pos = 0;
6
+ this.view = new DataView(buffer);
7
+ }
8
+ get offset() {
9
+ return this.pos;
10
+ }
11
+ set offset(v) {
12
+ this.pos = v;
13
+ }
14
+ get length() {
15
+ return this.view.byteLength;
16
+ }
17
+ byte() {
18
+ return this.view.getUint8(this.pos++);
19
+ }
20
+ word() {
21
+ const v = this.view.getUint16(this.pos, true);
22
+ this.pos += 2;
23
+ return v;
24
+ }
25
+ short() {
26
+ const v = this.view.getInt16(this.pos, true);
27
+ this.pos += 2;
28
+ return v;
29
+ }
30
+ dword() {
31
+ const v = this.view.getUint32(this.pos, true);
32
+ this.pos += 4;
33
+ return v;
34
+ }
35
+ long() {
36
+ const v = this.view.getInt32(this.pos, true);
37
+ this.pos += 4;
38
+ return v;
39
+ }
40
+ bytes(n) {
41
+ const data = new Uint8Array(this.view.buffer, this.pos, n);
42
+ this.pos += n;
43
+ return data;
44
+ }
45
+ skip(n) {
46
+ this.pos += n;
47
+ }
48
+ string() {
49
+ const len = this.word();
50
+ const bytes = this.bytes(len);
51
+ return new TextDecoder("utf-8").decode(bytes);
52
+ }
66
53
  }
67
54
  class BinaryWriter {
68
- constructor() {
69
- this.chunks = [];
70
- this.tempBuf = new ArrayBuffer(8);
71
- this.tempView = new DataView(this.tempBuf);
72
- }
73
- get byteLength() {
74
- return this.chunks.reduce((s, c) => s + c.length, 0);
75
- }
76
- byte(v) {
77
- this.chunks.push(new Uint8Array([v & 0xff]));
78
- }
79
- word(v) {
80
- this.tempView.setUint16(0, v, true);
81
- this.chunks.push(new Uint8Array(this.tempBuf.slice(0, 2)));
82
- }
83
- short(v) {
84
- this.tempView.setInt16(0, v, true);
85
- this.chunks.push(new Uint8Array(this.tempBuf.slice(0, 2)));
86
- }
87
- dword(v) {
88
- this.tempView.setUint32(0, v, true);
89
- this.chunks.push(new Uint8Array(this.tempBuf.slice(0, 4)));
90
- }
91
- bytes(data) {
92
- this.chunks.push(new Uint8Array(data));
93
- }
94
- string(s) {
95
- const encoded = new TextEncoder().encode(s);
96
- this.word(encoded.length);
97
- this.bytes(encoded);
98
- }
99
- zeros(n) {
100
- this.chunks.push(new Uint8Array(n));
101
- }
102
- toArrayBuffer() {
103
- const total = this.byteLength;
104
- const result = new Uint8Array(total);
105
- let off = 0;
106
- for (const chunk of this.chunks) {
107
- result.set(chunk, off);
108
- off += chunk.length;
109
- }
110
- return result.buffer;
111
- }
55
+ constructor() {
56
+ this.chunks = [];
57
+ this.tempBuf = new ArrayBuffer(8);
58
+ this.tempView = new DataView(this.tempBuf);
59
+ }
60
+ get byteLength() {
61
+ return this.chunks.reduce((s, c) => s + c.length, 0);
62
+ }
63
+ byte(v) {
64
+ this.chunks.push(new Uint8Array([v & 255]));
65
+ }
66
+ word(v) {
67
+ this.tempView.setUint16(0, v, true);
68
+ this.chunks.push(new Uint8Array(this.tempBuf.slice(0, 2)));
69
+ }
70
+ short(v) {
71
+ this.tempView.setInt16(0, v, true);
72
+ this.chunks.push(new Uint8Array(this.tempBuf.slice(0, 2)));
73
+ }
74
+ dword(v) {
75
+ this.tempView.setUint32(0, v, true);
76
+ this.chunks.push(new Uint8Array(this.tempBuf.slice(0, 4)));
77
+ }
78
+ bytes(data) {
79
+ this.chunks.push(new Uint8Array(data));
80
+ }
81
+ string(s) {
82
+ const encoded = new TextEncoder().encode(s);
83
+ this.word(encoded.length);
84
+ this.bytes(encoded);
85
+ }
86
+ zeros(n) {
87
+ this.chunks.push(new Uint8Array(n));
88
+ }
89
+ toArrayBuffer() {
90
+ const total = this.byteLength;
91
+ const result = new Uint8Array(total);
92
+ let off = 0;
93
+ for (const chunk of this.chunks) {
94
+ result.set(chunk, off);
95
+ off += chunk.length;
96
+ }
97
+ return result.buffer;
98
+ }
112
99
  }
113
- // ── Zlib helpers (browser native) ────────────────────────────
114
100
  async function zlibInflate(data) {
115
- try {
116
- const ds = new DecompressionStream('deflate');
117
- const writer = ds.writable.getWriter();
118
- // Use ArrayBuffer.prototype.slice to ensure we get a pure ArrayBuffer
119
- const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
120
- writer.write(new Uint8Array(buffer));
121
- writer.close();
122
- const reader = ds.readable.getReader();
123
- const chunks = [];
124
- for (;;) {
125
- const { done, value } = await reader.read();
126
- if (done) {
127
- break;
128
- }
129
- chunks.push(value);
130
- }
131
- const total = chunks.reduce((s, c) => s + c.length, 0);
132
- const result = new Uint8Array(total);
133
- let off = 0;
134
- for (const c of chunks) {
135
- result.set(c, off);
136
- off += c.length;
137
- }
138
- return result;
139
- }
140
- catch (_a) {
141
- // Fallback: return raw data (may fail downstream)
142
- return data;
143
- }
101
+ try {
102
+ const ds = new DecompressionStream("deflate");
103
+ const writer = ds.writable.getWriter();
104
+ const buffer = data.buffer.slice(
105
+ data.byteOffset,
106
+ data.byteOffset + data.byteLength
107
+ );
108
+ writer.write(new Uint8Array(buffer));
109
+ writer.close();
110
+ const reader = ds.readable.getReader();
111
+ const chunks = [];
112
+ for (; ; ) {
113
+ const { done, value } = await reader.read();
114
+ if (done) {
115
+ break;
116
+ }
117
+ chunks.push(value);
118
+ }
119
+ const total = chunks.reduce((s, c) => s + c.length, 0);
120
+ const result = new Uint8Array(total);
121
+ let off = 0;
122
+ for (const c of chunks) {
123
+ result.set(c, off);
124
+ off += c.length;
125
+ }
126
+ return result;
127
+ } catch {
128
+ return data;
129
+ }
144
130
  }
145
131
  async function zlibDeflate(data) {
146
- try {
147
- const cs = new CompressionStream('deflate');
148
- const writer = cs.writable.getWriter();
149
- // Use ArrayBuffer.prototype.slice to ensure we get a pure ArrayBuffer
150
- const buffer = data.buffer.slice(data.byteOffset, data.byteOffset + data.byteLength);
151
- writer.write(new Uint8Array(buffer));
152
- writer.close();
153
- const reader = cs.readable.getReader();
154
- const chunks = [];
155
- for (;;) {
156
- const { done, value } = await reader.read();
157
- if (done) {
158
- break;
159
- }
160
- chunks.push(value);
161
- }
162
- const total = chunks.reduce((s, c) => s + c.length, 0);
163
- const result = new Uint8Array(total);
164
- let off = 0;
165
- for (const c of chunks) {
166
- result.set(c, off);
167
- off += c.length;
168
- }
169
- return result;
170
- }
171
- catch (_a) {
172
- return data;
173
- }
132
+ try {
133
+ const cs = new CompressionStream("deflate");
134
+ const writer = cs.writable.getWriter();
135
+ const buffer = data.buffer.slice(
136
+ data.byteOffset,
137
+ data.byteOffset + data.byteLength
138
+ );
139
+ writer.write(new Uint8Array(buffer));
140
+ writer.close();
141
+ const reader = cs.readable.getReader();
142
+ const chunks = [];
143
+ for (; ; ) {
144
+ const { done, value } = await reader.read();
145
+ if (done) {
146
+ break;
147
+ }
148
+ chunks.push(value);
149
+ }
150
+ const total = chunks.reduce((s, c) => s + c.length, 0);
151
+ const result = new Uint8Array(total);
152
+ let off = 0;
153
+ for (const c of chunks) {
154
+ result.set(c, off);
155
+ off += c.length;
156
+ }
157
+ return result;
158
+ } catch {
159
+ return data;
160
+ }
174
161
  }
175
- // ── Constants ────────────────────────────────────────────────
176
- const ASE_MAGIC = 0xa5e0;
177
- const FRAME_MAGIC = 0xf1fa;
178
- const CHUNK_LAYER = 0x2004;
179
- const CHUNK_CEL = 0x2005;
180
- // Chunk types defined by spec but not yet used:
181
- // CEL_EXTRA=0x2006, OLD_PALETTE_1=0x0004, OLD_PALETTE_2=0x0011,
182
- // USER_DATA=0x2020, SLICE=0x2022, TILESET=0x2023
183
- const CHUNK_PALETTE = 0x2019;
184
- const CHUNK_FRAME_TAGS = 0x2018;
185
- // ── Parse .ase ───────────────────────────────────────────────
162
+ const ASE_MAGIC = 42464;
163
+ const FRAME_MAGIC = 61946;
164
+ const CHUNK_LAYER = 8196;
165
+ const CHUNK_CEL = 8197;
166
+ const CHUNK_PALETTE = 8217;
167
+ const CHUNK_FRAME_TAGS = 8216;
186
168
  async function parseAseFile(buffer) {
187
- const r = new BinaryReader(buffer);
188
- // -- File header (128 bytes) --
189
- r.dword(); // fileSize — read to advance cursor
190
- const magic = r.word();
191
- if (magic !== ASE_MAGIC) {
192
- throw new Error(`Not an Aseprite file (magic: 0x${magic.toString(16)})`);
193
- }
194
- const numFrames = r.word();
195
- const width = r.word();
196
- const height = r.word();
197
- const colorDepth = r.word();
198
- r.dword(); // flags
199
- r.word(); // speed (deprecated)
200
- r.skip(8); // reserved
201
- const transparentIndex = r.byte();
202
- r.skip(3);
203
- r.word(); // numColors
204
- r.byte(); // pixelWidth
205
- r.byte(); // pixelHeight
206
- r.short(); // gridX
207
- r.short(); // gridY
208
- r.word(); // gridW
209
- r.word(); // gridH
210
- r.skip(84); // padding to 128
211
- const layers = [];
212
- const palette = [];
213
- const tags = [];
214
- const frames = [];
215
- // -- Frames --
216
- for (let fr = 0; fr < numFrames; fr++) {
217
- r.dword(); // frameBytes — read to advance cursor
218
- const frameMagic = r.word();
219
- if (frameMagic !== FRAME_MAGIC) {
220
- throw new Error(`Bad frame magic at frame ${fr}`);
169
+ const r = new BinaryReader(buffer);
170
+ r.dword();
171
+ const magic = r.word();
172
+ if (magic !== ASE_MAGIC) {
173
+ throw new Error(`Not an Aseprite file (magic: 0x${magic.toString(16)})`);
174
+ }
175
+ const numFrames = r.word();
176
+ const width = r.word();
177
+ const height = r.word();
178
+ const colorDepth = r.word();
179
+ r.dword();
180
+ r.word();
181
+ r.skip(8);
182
+ const transparentIndex = r.byte();
183
+ r.skip(3);
184
+ r.word();
185
+ r.byte();
186
+ r.byte();
187
+ r.short();
188
+ r.short();
189
+ r.word();
190
+ r.word();
191
+ r.skip(84);
192
+ const layers = [];
193
+ const palette = [];
194
+ const tags = [];
195
+ const frames = [];
196
+ for (let fr = 0; fr < numFrames; fr++) {
197
+ r.dword();
198
+ const frameMagic = r.word();
199
+ if (frameMagic !== FRAME_MAGIC) {
200
+ throw new Error(`Bad frame magic at frame ${fr}`);
201
+ }
202
+ const oldChunks = r.word();
203
+ const duration = r.word();
204
+ r.skip(2);
205
+ let numChunks = r.dword();
206
+ if (numChunks === 0) {
207
+ numChunks = oldChunks;
208
+ }
209
+ const frame = { duration, cels: [] };
210
+ for (let ch = 0; ch < numChunks; ch++) {
211
+ const chunkStart = r.offset;
212
+ const chunkSize = r.dword();
213
+ const chunkType = r.word();
214
+ const chunkDataEnd = chunkStart + chunkSize;
215
+ switch (chunkType) {
216
+ case CHUNK_LAYER: {
217
+ const flags = r.word();
218
+ const layerType = r.word();
219
+ const childLevel = r.word();
220
+ r.skip(2);
221
+ r.skip(2);
222
+ const blendMode = r.word();
223
+ const opacity = r.byte();
224
+ r.skip(3);
225
+ const name = r.string();
226
+ layers.push({
227
+ name,
228
+ visible: !!(flags & 1),
229
+ opacity,
230
+ blendMode,
231
+ childLevel,
232
+ layerType
233
+ });
234
+ break;
221
235
  }
222
- const oldChunks = r.word();
223
- const duration = r.word();
224
- r.skip(2);
225
- let numChunks = r.dword();
226
- if (numChunks === 0) {
227
- numChunks = oldChunks;
236
+ case CHUNK_CEL: {
237
+ const layerIndex = r.word();
238
+ const x = r.short();
239
+ const y = r.short();
240
+ const opacity = r.byte();
241
+ const celType = r.word();
242
+ r.short();
243
+ r.skip(5);
244
+ if (celType === 0) {
245
+ const cw = r.word();
246
+ const ch2 = r.word();
247
+ const pixelBytes = cw * ch2 * (colorDepth / 8);
248
+ const rawPixels = r.bytes(pixelBytes);
249
+ const rgba = colorDepth === 32 ? new Uint8Array(rawPixels) : convertToRGBA(rawPixels, colorDepth, palette, transparentIndex, cw, ch2);
250
+ frame.cels.push({ layerIndex, x, y, opacity, width: cw, height: ch2, pixels: rgba });
251
+ } else if (celType === 2) {
252
+ const cw = r.word();
253
+ const ch2 = r.word();
254
+ const compressedLen = chunkDataEnd - r.offset;
255
+ const compressed = r.bytes(compressedLen);
256
+ const raw = await zlibInflate(compressed);
257
+ const rgba = colorDepth === 32 ? new Uint8Array(raw) : convertToRGBA(raw, colorDepth, palette, transparentIndex, cw, ch2);
258
+ frame.cels.push({ layerIndex, x, y, opacity, width: cw, height: ch2, pixels: rgba });
259
+ } else if (celType === 1) {
260
+ const linkedFrame = r.word();
261
+ if (linkedFrame < frames.length) {
262
+ const linked = frames[linkedFrame].cels.find((c) => c.layerIndex === layerIndex);
263
+ if (linked) {
264
+ frame.cels.push({ ...linked, x, y, opacity });
265
+ }
266
+ }
267
+ }
268
+ break;
228
269
  }
229
- const frame = { duration, cels: [] };
230
- for (let ch = 0; ch < numChunks; ch++) {
231
- const chunkStart = r.offset;
232
- const chunkSize = r.dword();
233
- const chunkType = r.word();
234
- const chunkDataEnd = chunkStart + chunkSize;
235
- switch (chunkType) {
236
- case CHUNK_LAYER: {
237
- const flags = r.word();
238
- const layerType = r.word();
239
- const childLevel = r.word();
240
- r.skip(2); // default width
241
- r.skip(2); // default height
242
- const blendMode = r.word();
243
- const opacity = r.byte();
244
- r.skip(3);
245
- const name = r.string();
246
- layers.push({
247
- name,
248
- visible: !!(flags & 1),
249
- opacity,
250
- blendMode,
251
- childLevel,
252
- layerType,
253
- });
254
- break;
255
- }
256
- case CHUNK_CEL: {
257
- const layerIndex = r.word();
258
- const x = r.short();
259
- const y = r.short();
260
- const opacity = r.byte();
261
- const celType = r.word();
262
- r.short(); // zIndex
263
- r.skip(5);
264
- if (celType === 0) {
265
- // Raw cel
266
- const cw = r.word();
267
- const ch2 = r.word();
268
- const pixelBytes = cw * ch2 * (colorDepth / 8);
269
- const rawPixels = r.bytes(pixelBytes);
270
- const rgba = colorDepth === 32
271
- ? new Uint8Array(rawPixels)
272
- : convertToRGBA(rawPixels, colorDepth, palette, transparentIndex, cw, ch2);
273
- frame.cels.push({ layerIndex, x, y, opacity, width: cw, height: ch2, pixels: rgba });
274
- }
275
- else if (celType === 2) {
276
- // Compressed cel
277
- const cw = r.word();
278
- const ch2 = r.word();
279
- const compressedLen = chunkDataEnd - r.offset;
280
- const compressed = r.bytes(compressedLen);
281
- const raw = await zlibInflate(compressed);
282
- const rgba = colorDepth === 32
283
- ? new Uint8Array(raw)
284
- : convertToRGBA(raw, colorDepth, palette, transparentIndex, cw, ch2);
285
- frame.cels.push({ layerIndex, x, y, opacity, width: cw, height: ch2, pixels: rgba });
286
- }
287
- else if (celType === 1) {
288
- // Linked cel — points to another frame
289
- const linkedFrame = r.word();
290
- if (linkedFrame < frames.length) {
291
- const linked = frames[linkedFrame].cels.find((c) => c.layerIndex === layerIndex);
292
- if (linked) {
293
- frame.cels.push({ ...linked, x, y, opacity });
294
- }
295
- }
296
- }
297
- // Skip tilemap cels (type 3)
298
- break;
299
- }
300
- case CHUNK_PALETTE: {
301
- const palSize = r.dword();
302
- const firstIdx = r.dword();
303
- const lastIdx = r.dword();
304
- r.skip(8);
305
- // Ensure palette array is large enough
306
- while (palette.length < palSize) {
307
- palette.push({ r: 0, g: 0, b: 0, a: 255 });
308
- }
309
- for (let i = firstIdx; i <= lastIdx; i++) {
310
- const hasName = r.word();
311
- const pr = r.byte(), pg = r.byte(), pb = r.byte(), pa = r.byte();
312
- palette[i] = { r: pr, g: pg, b: pb, a: pa };
313
- if (hasName & 1) {
314
- r.string();
315
- } // skip name
316
- }
317
- break;
318
- }
319
- case CHUNK_FRAME_TAGS: {
320
- const numTags = r.word();
321
- r.skip(8);
322
- for (let t = 0; t < numTags; t++) {
323
- const from = r.word();
324
- const to = r.word();
325
- const dir = r.byte();
326
- r.word();
327
- r.skip(6);
328
- r.skip(1); // padding
329
- const tr = r.byte(), tg = r.byte(), tb = r.byte();
330
- const name = r.string();
331
- tags.push({ name, from, to, direction: dir, color: { r: tr, g: tg, b: tb, a: 255 } });
332
- }
333
- break;
334
- }
270
+ case CHUNK_PALETTE: {
271
+ const palSize = r.dword();
272
+ const firstIdx = r.dword();
273
+ const lastIdx = r.dword();
274
+ r.skip(8);
275
+ while (palette.length < palSize) {
276
+ palette.push({ r: 0, g: 0, b: 0, a: 255 });
277
+ }
278
+ for (let i = firstIdx; i <= lastIdx; i++) {
279
+ const hasName = r.word();
280
+ const pr = r.byte(), pg = r.byte(), pb = r.byte(), pa = r.byte();
281
+ palette[i] = { r: pr, g: pg, b: pb, a: pa };
282
+ if (hasName & 1) {
283
+ r.string();
335
284
  }
336
- r.offset = chunkDataEnd;
285
+ }
286
+ break;
287
+ }
288
+ case CHUNK_FRAME_TAGS: {
289
+ const numTags = r.word();
290
+ r.skip(8);
291
+ for (let t = 0; t < numTags; t++) {
292
+ const from = r.word();
293
+ const to = r.word();
294
+ const dir = r.byte();
295
+ r.word();
296
+ r.skip(6);
297
+ r.skip(1);
298
+ const tr = r.byte(), tg = r.byte(), tb = r.byte();
299
+ const name = r.string();
300
+ tags.push({ name, from, to, direction: dir, color: { r: tr, g: tg, b: tb, a: 255 } });
301
+ }
302
+ break;
337
303
  }
338
- frames.push(frame);
304
+ }
305
+ r.offset = chunkDataEnd;
339
306
  }
340
- return { width, height, colorDepth, frames, layers, palette, tags, transparentIndex };
307
+ frames.push(frame);
308
+ }
309
+ return { width, height, colorDepth, frames, layers, palette, tags, transparentIndex };
341
310
  }
342
311
  function convertToRGBA(raw, depth, palette, transIdx, w, h) {
343
- const rgba = new Uint8Array(w * h * 4);
344
- if (depth === 16) {
345
- // Grayscale: 2 bytes per pixel (value, alpha)
346
- for (let i = 0; i < w * h; i++) {
347
- const v = raw[i * 2], a = raw[i * 2 + 1];
348
- rgba[i * 4] = v;
349
- rgba[i * 4 + 1] = v;
350
- rgba[i * 4 + 2] = v;
351
- rgba[i * 4 + 3] = a;
352
- }
353
- }
354
- else if (depth === 8) {
355
- // Indexed: 1 byte per pixel
356
- for (let i = 0; i < w * h; i++) {
357
- const idx = raw[i];
358
- if (idx === transIdx) {
359
- rgba[i * 4 + 3] = 0;
360
- }
361
- else if (idx < palette.length) {
362
- const c = palette[idx];
363
- rgba[i * 4] = c.r;
364
- rgba[i * 4 + 1] = c.g;
365
- rgba[i * 4 + 2] = c.b;
366
- rgba[i * 4 + 3] = c.a;
367
- }
368
- }
369
- }
370
- return rgba;
312
+ const rgba = new Uint8Array(w * h * 4);
313
+ if (depth === 16) {
314
+ for (let i = 0; i < w * h; i++) {
315
+ const v = raw[i * 2], a = raw[i * 2 + 1];
316
+ rgba[i * 4] = v;
317
+ rgba[i * 4 + 1] = v;
318
+ rgba[i * 4 + 2] = v;
319
+ rgba[i * 4 + 3] = a;
320
+ }
321
+ } else if (depth === 8) {
322
+ for (let i = 0; i < w * h; i++) {
323
+ const idx = raw[i];
324
+ if (idx === transIdx) {
325
+ rgba[i * 4 + 3] = 0;
326
+ } else if (idx < palette.length) {
327
+ const c = palette[idx];
328
+ rgba[i * 4] = c.r;
329
+ rgba[i * 4 + 1] = c.g;
330
+ rgba[i * 4 + 2] = c.b;
331
+ rgba[i * 4 + 3] = c.a;
332
+ }
333
+ }
334
+ }
335
+ return rgba;
371
336
  }
372
- // ── Write .ase ───────────────────────────────────────────────
373
337
  async function writeAseFile(doc) {
374
- // We'll build chunks per frame, then assemble the final file.
375
- const frameBuffers = [];
376
- for (let fr = 0; fr < doc.frames.length; fr++) {
377
- const frame = doc.frames[fr];
378
- const fw = new BinaryWriter();
379
- let numChunks = 0;
380
- // In first frame, write layer chunks + palette
381
- if (fr === 0) {
382
- // Layers
383
- for (const layer of doc.layers) {
384
- const chunk = new BinaryWriter();
385
- const flags = layer.visible ? 1 : 0;
386
- chunk.word(flags);
387
- chunk.word(layer.layerType);
388
- chunk.word(layer.childLevel);
389
- chunk.word(0); // default w
390
- chunk.word(0); // default h
391
- chunk.word(layer.blendMode);
392
- chunk.byte(layer.opacity);
393
- chunk.zeros(3);
394
- chunk.string(layer.name);
395
- const chunkData = chunk.toArrayBuffer();
396
- fw.dword(chunkData.byteLength + 6); // +6 for size+type header
397
- fw.word(CHUNK_LAYER);
398
- fw.bytes(new Uint8Array(chunkData));
399
- numChunks++;
400
- }
401
- // Palette
402
- if (doc.palette.length > 0) {
403
- const chunk = new BinaryWriter();
404
- chunk.dword(doc.palette.length);
405
- chunk.dword(0);
406
- chunk.dword(doc.palette.length - 1);
407
- chunk.zeros(8);
408
- for (const c of doc.palette) {
409
- chunk.word(0); // no name
410
- chunk.byte(c.r);
411
- chunk.byte(c.g);
412
- chunk.byte(c.b);
413
- chunk.byte(c.a);
414
- }
415
- const chunkData = chunk.toArrayBuffer();
416
- fw.dword(chunkData.byteLength + 6);
417
- fw.word(CHUNK_PALETTE);
418
- fw.bytes(new Uint8Array(chunkData));
419
- numChunks++;
420
- }
421
- // Frame tags
422
- if (doc.tags.length > 0) {
423
- const chunk = new BinaryWriter();
424
- chunk.word(doc.tags.length);
425
- chunk.zeros(8);
426
- for (const tag of doc.tags) {
427
- chunk.word(tag.from);
428
- chunk.word(tag.to);
429
- chunk.byte(tag.direction);
430
- chunk.word(0); // repeat
431
- chunk.zeros(6);
432
- chunk.byte(0); // padding
433
- chunk.byte(tag.color.r);
434
- chunk.byte(tag.color.g);
435
- chunk.byte(tag.color.b);
436
- chunk.string(tag.name);
437
- }
438
- const chunkData = chunk.toArrayBuffer();
439
- fw.dword(chunkData.byteLength + 6);
440
- fw.word(CHUNK_FRAME_TAGS);
441
- fw.bytes(new Uint8Array(chunkData));
442
- numChunks++;
443
- }
338
+ const frameBuffers = [];
339
+ for (let fr = 0; fr < doc.frames.length; fr++) {
340
+ const frame = doc.frames[fr];
341
+ const fw = new BinaryWriter();
342
+ let numChunks = 0;
343
+ if (fr === 0) {
344
+ for (const layer of doc.layers) {
345
+ const chunk = new BinaryWriter();
346
+ const flags = layer.visible ? 1 : 0;
347
+ chunk.word(flags);
348
+ chunk.word(layer.layerType);
349
+ chunk.word(layer.childLevel);
350
+ chunk.word(0);
351
+ chunk.word(0);
352
+ chunk.word(layer.blendMode);
353
+ chunk.byte(layer.opacity);
354
+ chunk.zeros(3);
355
+ chunk.string(layer.name);
356
+ const chunkData = chunk.toArrayBuffer();
357
+ fw.dword(chunkData.byteLength + 6);
358
+ fw.word(CHUNK_LAYER);
359
+ fw.bytes(new Uint8Array(chunkData));
360
+ numChunks++;
361
+ }
362
+ if (doc.palette.length > 0) {
363
+ const chunk = new BinaryWriter();
364
+ chunk.dword(doc.palette.length);
365
+ chunk.dword(0);
366
+ chunk.dword(doc.palette.length - 1);
367
+ chunk.zeros(8);
368
+ for (const c of doc.palette) {
369
+ chunk.word(0);
370
+ chunk.byte(c.r);
371
+ chunk.byte(c.g);
372
+ chunk.byte(c.b);
373
+ chunk.byte(c.a);
444
374
  }
445
- // Cels — write compressed (type 2)
446
- for (const cel of frame.cels) {
447
- const chunk = new BinaryWriter();
448
- chunk.word(cel.layerIndex);
449
- chunk.short(cel.x);
450
- chunk.short(cel.y);
451
- chunk.byte(cel.opacity);
452
- // Use compressed cels (type 2) for space efficiency
453
- const compressed = await zlibDeflate(cel.pixels);
454
- chunk.word(2); // cel type = compressed
455
- chunk.short(0); // z-index
456
- chunk.zeros(5);
457
- chunk.word(cel.width);
458
- chunk.word(cel.height);
459
- chunk.bytes(compressed);
460
- const chunkData = chunk.toArrayBuffer();
461
- fw.dword(chunkData.byteLength + 6);
462
- fw.word(CHUNK_CEL);
463
- fw.bytes(new Uint8Array(chunkData));
464
- numChunks++;
375
+ const chunkData = chunk.toArrayBuffer();
376
+ fw.dword(chunkData.byteLength + 6);
377
+ fw.word(CHUNK_PALETTE);
378
+ fw.bytes(new Uint8Array(chunkData));
379
+ numChunks++;
380
+ }
381
+ if (doc.tags.length > 0) {
382
+ const chunk = new BinaryWriter();
383
+ chunk.word(doc.tags.length);
384
+ chunk.zeros(8);
385
+ for (const tag of doc.tags) {
386
+ chunk.word(tag.from);
387
+ chunk.word(tag.to);
388
+ chunk.byte(tag.direction);
389
+ chunk.word(0);
390
+ chunk.zeros(6);
391
+ chunk.byte(0);
392
+ chunk.byte(tag.color.r);
393
+ chunk.byte(tag.color.g);
394
+ chunk.byte(tag.color.b);
395
+ chunk.string(tag.name);
465
396
  }
466
- // Build frame header
467
- const frameData = fw.toArrayBuffer();
468
- const fh = new BinaryWriter();
469
- fh.dword(frameData.byteLength + 16); // 16 = frame header size
470
- fh.word(FRAME_MAGIC);
471
- fh.word(numChunks > 0xffff ? 0xffff : numChunks);
472
- fh.word(frame.duration);
473
- fh.zeros(2);
474
- fh.dword(numChunks);
475
- const headerBuf = fh.toArrayBuffer();
476
- // Combine header + data
477
- const combined = new Uint8Array(headerBuf.byteLength + frameData.byteLength);
478
- combined.set(new Uint8Array(headerBuf), 0);
479
- combined.set(new Uint8Array(frameData), headerBuf.byteLength);
480
- frameBuffers.push(combined.buffer);
481
- }
482
- // Assemble final file
483
- const framesTotal = frameBuffers.reduce((s, b) => s + b.byteLength, 0);
484
- const fileSize = 128 + framesTotal;
485
- // File header
486
- const header = new BinaryWriter();
487
- header.dword(fileSize);
488
- header.word(ASE_MAGIC);
489
- header.word(doc.frames.length);
490
- header.word(doc.width);
491
- header.word(doc.height);
492
- header.word(doc.colorDepth);
493
- header.dword(1); // flags: layer opacity valid
494
- header.word(100); // speed (deprecated)
495
- header.dword(0); // reserved
496
- header.dword(0); // reserved
497
- header.byte(doc.transparentIndex);
498
- header.zeros(3);
499
- header.word(doc.palette.length || 0);
500
- header.byte(1); // pixel width
501
- header.byte(1); // pixel height
502
- header.short(0); // grid x
503
- header.short(0); // grid y
504
- header.word(16); // grid w
505
- header.word(16); // grid h
506
- header.zeros(84); // padding to 128
507
- const headerBuf = header.toArrayBuffer();
508
- const file = new Uint8Array(fileSize);
509
- file.set(new Uint8Array(headerBuf), 0);
510
- let off = 128;
511
- for (const fb of frameBuffers) {
512
- file.set(new Uint8Array(fb), off);
513
- off += fb.byteLength;
514
- }
515
- return file.buffer;
397
+ const chunkData = chunk.toArrayBuffer();
398
+ fw.dword(chunkData.byteLength + 6);
399
+ fw.word(CHUNK_FRAME_TAGS);
400
+ fw.bytes(new Uint8Array(chunkData));
401
+ numChunks++;
402
+ }
403
+ }
404
+ for (const cel of frame.cels) {
405
+ const chunk = new BinaryWriter();
406
+ chunk.word(cel.layerIndex);
407
+ chunk.short(cel.x);
408
+ chunk.short(cel.y);
409
+ chunk.byte(cel.opacity);
410
+ const compressed = await zlibDeflate(cel.pixels);
411
+ chunk.word(2);
412
+ chunk.short(0);
413
+ chunk.zeros(5);
414
+ chunk.word(cel.width);
415
+ chunk.word(cel.height);
416
+ chunk.bytes(compressed);
417
+ const chunkData = chunk.toArrayBuffer();
418
+ fw.dword(chunkData.byteLength + 6);
419
+ fw.word(CHUNK_CEL);
420
+ fw.bytes(new Uint8Array(chunkData));
421
+ numChunks++;
422
+ }
423
+ const frameData = fw.toArrayBuffer();
424
+ const fh = new BinaryWriter();
425
+ fh.dword(frameData.byteLength + 16);
426
+ fh.word(FRAME_MAGIC);
427
+ fh.word(numChunks > 65535 ? 65535 : numChunks);
428
+ fh.word(frame.duration);
429
+ fh.zeros(2);
430
+ fh.dword(numChunks);
431
+ const headerBuf2 = fh.toArrayBuffer();
432
+ const combined = new Uint8Array(headerBuf2.byteLength + frameData.byteLength);
433
+ combined.set(new Uint8Array(headerBuf2), 0);
434
+ combined.set(new Uint8Array(frameData), headerBuf2.byteLength);
435
+ frameBuffers.push(combined.buffer);
436
+ }
437
+ const framesTotal = frameBuffers.reduce((s, b) => s + b.byteLength, 0);
438
+ const fileSize = 128 + framesTotal;
439
+ const header = new BinaryWriter();
440
+ header.dword(fileSize);
441
+ header.word(ASE_MAGIC);
442
+ header.word(doc.frames.length);
443
+ header.word(doc.width);
444
+ header.word(doc.height);
445
+ header.word(doc.colorDepth);
446
+ header.dword(1);
447
+ header.word(100);
448
+ header.dword(0);
449
+ header.dword(0);
450
+ header.byte(doc.transparentIndex);
451
+ header.zeros(3);
452
+ header.word(doc.palette.length || 0);
453
+ header.byte(1);
454
+ header.byte(1);
455
+ header.short(0);
456
+ header.short(0);
457
+ header.word(16);
458
+ header.word(16);
459
+ header.zeros(84);
460
+ const headerBuf = header.toArrayBuffer();
461
+ const file = new Uint8Array(fileSize);
462
+ file.set(new Uint8Array(headerBuf), 0);
463
+ let off = 128;
464
+ for (const fb of frameBuffers) {
465
+ file.set(new Uint8Array(fb), off);
466
+ off += fb.byteLength;
467
+ }
468
+ return file.buffer;
516
469
  }
517
- // ── Convenience: create empty AseDocument ────────────────────
518
470
  function createEmptyAseDocument(width = 64, height = 64) {
519
- const pixels = new Uint8Array(width * height * 4); // transparent
520
- return {
521
- width,
522
- height,
523
- colorDepth: 32,
524
- frames: [
525
- { duration: 100, cels: [{ layerIndex: 0, x: 0, y: 0, opacity: 255, width, height, pixels }] },
526
- ],
527
- layers: [
528
- { name: 'Layer 1', visible: true, opacity: 255, blendMode: 0, childLevel: 0, layerType: 0 },
529
- ],
530
- palette: defaultPalette(),
531
- tags: [],
532
- transparentIndex: 0,
533
- };
471
+ const pixels = new Uint8Array(width * height * 4);
472
+ return {
473
+ width,
474
+ height,
475
+ colorDepth: 32,
476
+ frames: [
477
+ { duration: 100, cels: [{ layerIndex: 0, x: 0, y: 0, opacity: 255, width, height, pixels }] }
478
+ ],
479
+ layers: [
480
+ { name: "Layer 1", visible: true, opacity: 255, blendMode: 0, childLevel: 0, layerType: 0 }
481
+ ],
482
+ palette: defaultPalette(),
483
+ tags: [],
484
+ transparentIndex: 0
485
+ };
534
486
  }
535
487
  function defaultPalette() {
536
- const hex = [
537
- 'var(--nice-text, #000000)',
538
- '#1d2b53',
539
- '#7e2553',
540
- '#008751',
541
- '#ab5236',
542
- '#5f574f',
543
- '#c2c3c7',
544
- '#fff1e8',
545
- '#ff004d',
546
- '#ffa300',
547
- '#ffec27',
548
- '#00e436',
549
- '#29adff',
550
- '#83769c',
551
- '#ff77a8',
552
- '#ffccaa',
553
- '#291814',
554
- '#111d35',
555
- '#422136',
556
- '#125359',
557
- '#742f29',
558
- '#49333b',
559
- '#a28879',
560
- '#f2e8cf',
561
- '#be1250',
562
- '#ff6c24',
563
- '#ffd726',
564
- '#44891a',
565
- '#1d6aff',
566
- '#6b3a89',
567
- '#ff5577',
568
- '#eebb77',
569
- ];
570
- return hex.map((h) => {
571
- const r = parseInt(h.slice(1, 3), 16);
572
- const g = parseInt(h.slice(3, 5), 16);
573
- const b = parseInt(h.slice(5, 7), 16);
574
- return { r, g, b, a: 255 };
575
- });
488
+ const hex = [
489
+ "var(--nice-text, #000000)",
490
+ "#1d2b53",
491
+ "#7e2553",
492
+ "#008751",
493
+ "#ab5236",
494
+ "#5f574f",
495
+ "#c2c3c7",
496
+ "#fff1e8",
497
+ "#ff004d",
498
+ "#ffa300",
499
+ "#ffec27",
500
+ "#00e436",
501
+ "#29adff",
502
+ "#83769c",
503
+ "#ff77a8",
504
+ "#ffccaa",
505
+ "#291814",
506
+ "#111d35",
507
+ "#422136",
508
+ "#125359",
509
+ "#742f29",
510
+ "#49333b",
511
+ "#a28879",
512
+ "#f2e8cf",
513
+ "#be1250",
514
+ "#ff6c24",
515
+ "#ffd726",
516
+ "#44891a",
517
+ "#1d6aff",
518
+ "#6b3a89",
519
+ "#ff5577",
520
+ "#eebb77"
521
+ ];
522
+ return hex.map((h) => {
523
+ const r = parseInt(h.slice(1, 3), 16);
524
+ const g = parseInt(h.slice(3, 5), 16);
525
+ const b = parseInt(h.slice(5, 7), 16);
526
+ return { r, g, b, a: 255 };
527
+ });
576
528
  }
577
529
  function aseColorToHex(c) {
578
- return '#' + [c.r, c.g, c.b].map((v) => v.toString(16).padStart(2, '0')).join('');
530
+ return "#" + [c.r, c.g, c.b].map((v) => v.toString(16).padStart(2, "0")).join("");
579
531
  }
580
532
  function hexToAseColor(hex) {
581
- const h = hex.startsWith('#') ? hex.slice(1) : hex;
582
- return {
583
- r: parseInt(h.slice(0, 2), 16),
584
- g: parseInt(h.slice(2, 4), 16),
585
- b: parseInt(h.slice(4, 6), 16),
586
- a: 255,
587
- };
533
+ const h = hex.startsWith("#") ? hex.slice(1) : hex;
534
+ return {
535
+ r: parseInt(h.slice(0, 2), 16),
536
+ g: parseInt(h.slice(2, 4), 16),
537
+ b: parseInt(h.slice(4, 6), 16),
538
+ a: 255
539
+ };
588
540
  }
589
-
590
541
  exports.aseColorToHex = aseColorToHex;
591
542
  exports.createEmptyAseDocument = createEmptyAseDocument;
592
543
  exports.defaultPalette = defaultPalette;