@nice2dev/ui-graphics 1.3.15 → 1.3.17

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 +13 -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,425 +1,1236 @@
1
- import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
- import { NiceAppShell, NiceButton, NiceTextInput, NiceSelect, NiceNumberInput } from '@nice2dev/ui';
3
- import { NiceIcon, clickOnActivationKey, resolveCssColor } from '@nice2dev/ui-core';
4
- import { useRef, useState, useMemo, useEffect, useCallback } from 'react';
5
- import { useNiceTranslation } from '../core/i18n.js';
6
- import styles from './GameAsset2dEditor.module.css.js';
7
- import { useGameAssetEditor } from './useGameAssetEditor.js';
8
-
9
- /* ────────────────────────────────────────────────────────────────
10
- MENU BAR
11
- ──────────────────────────────────────────────────────────────── */
1
+ import { jsx, jsxs, Fragment } from "react/jsx-runtime";
2
+ import { NiceAppShell, NiceButton, NiceTextInput, NiceSelect, NiceNumberInput } from "@nice2dev/ui";
3
+ import { NiceIcon, clickOnActivationKey, resolveCssColor } from "@nice2dev/ui-core";
4
+ import { useRef, useState, useMemo, useEffect, useCallback } from "react";
5
+ import { useNiceTranslation } from "../core/i18n.js";
6
+ import styles from "./GameAsset2dEditor.module.css.js";
7
+ import { useGameAssetEditor } from "./useGameAssetEditor.js";
12
8
  const MenuBar = ({ api }) => {
13
- const { t } = useNiceTranslation();
14
- const fileInputRef = useRef(null);
15
- return (jsxs("div", { className: styles.menuBar, children: [jsxs("h3", { children: [jsx(NiceIcon, { name: "gamepad", size: 16 }), ' ', t('gameAsset2dEditor.gameAssetEditor', 'Game Asset Editor')] }), jsxs("div", { className: styles.menuGroup, children: [jsxs(NiceButton, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => api.newProject('New Project'), children: [jsx(NiceIcon, { name: "file", size: 14 }), " ", t('gameAsset2dEditor.new', 'New')] }), jsxs(NiceButton, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: [jsx(NiceIcon, { name: "folder", size: 14 }), " ", t('gameAsset2dEditor.open', 'Open')] }), jsxs(NiceButton, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.saveProject, children: [jsx(NiceIcon, { name: "save", size: 14 }), " ", t('gameAsset2dEditor.save', 'Save')] })] }), jsx("div", { className: styles.spacer }), jsxs("div", { className: styles.menuGroup, children: [jsxs(NiceButton, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.undo, disabled: !api.canUndo, children: [jsx(NiceIcon, { name: "arrow-left", size: 14 }), " ", t('gameAsset2dEditor.undo', 'Undo')] }), jsxs(NiceButton, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.redo, disabled: !api.canRedo, children: [jsx(NiceIcon, { name: "arrow-right", size: 14 }), " ", t('gameAsset2dEditor.redo', 'Redo')] })] }), jsx("input", { ref: fileInputRef, type: "file", accept: ".json", style: { display: 'none' }, onChange: (e) => {
16
- var _a;
17
- const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
18
- if (file) {
19
- api.loadProject(file);
20
- }
21
- e.target.value = '';
22
- } })] }));
9
+ const { t } = useNiceTranslation();
10
+ const fileInputRef = useRef(null);
11
+ return /* @__PURE__ */ jsxs("div", { className: styles.menuBar, children: [
12
+ /* @__PURE__ */ jsxs("h3", { children: [
13
+ /* @__PURE__ */ jsx(NiceIcon, { name: "gamepad", size: 16 }),
14
+ " ",
15
+ t("gameAsset2dEditor.gameAssetEditor", "Game Asset Editor")
16
+ ] }),
17
+ /* @__PURE__ */ jsxs("div", { className: styles.menuGroup, children: [
18
+ /* @__PURE__ */ jsxs(
19
+ NiceButton,
20
+ {
21
+ className: styles.menuBtn,
22
+ variant: "ghost",
23
+ size: "sm",
24
+ onClick: () => api.newProject("New Project"),
25
+ children: [
26
+ /* @__PURE__ */ jsx(NiceIcon, { name: "file", size: 14 }),
27
+ " ",
28
+ t("gameAsset2dEditor.new", "New")
29
+ ]
30
+ }
31
+ ),
32
+ /* @__PURE__ */ jsxs(
33
+ NiceButton,
34
+ {
35
+ className: styles.menuBtn,
36
+ variant: "ghost",
37
+ size: "sm",
38
+ onClick: () => fileInputRef.current?.click(),
39
+ children: [
40
+ /* @__PURE__ */ jsx(NiceIcon, { name: "folder", size: 14 }),
41
+ " ",
42
+ t("gameAsset2dEditor.open", "Open")
43
+ ]
44
+ }
45
+ ),
46
+ /* @__PURE__ */ jsxs(NiceButton, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.saveProject, children: [
47
+ /* @__PURE__ */ jsx(NiceIcon, { name: "save", size: 14 }),
48
+ " ",
49
+ t("gameAsset2dEditor.save", "Save")
50
+ ] })
51
+ ] }),
52
+ /* @__PURE__ */ jsx("div", { className: styles.spacer }),
53
+ /* @__PURE__ */ jsxs("div", { className: styles.menuGroup, children: [
54
+ /* @__PURE__ */ jsxs(
55
+ NiceButton,
56
+ {
57
+ className: styles.menuBtn,
58
+ variant: "ghost",
59
+ size: "sm",
60
+ onClick: api.undo,
61
+ disabled: !api.canUndo,
62
+ children: [
63
+ /* @__PURE__ */ jsx(NiceIcon, { name: "arrow-left", size: 14 }),
64
+ " ",
65
+ t("gameAsset2dEditor.undo", "Undo")
66
+ ]
67
+ }
68
+ ),
69
+ /* @__PURE__ */ jsxs(
70
+ NiceButton,
71
+ {
72
+ className: styles.menuBtn,
73
+ variant: "ghost",
74
+ size: "sm",
75
+ onClick: api.redo,
76
+ disabled: !api.canRedo,
77
+ children: [
78
+ /* @__PURE__ */ jsx(NiceIcon, { name: "arrow-right", size: 14 }),
79
+ " ",
80
+ t("gameAsset2dEditor.redo", "Redo")
81
+ ]
82
+ }
83
+ )
84
+ ] }),
85
+ /* @__PURE__ */ jsx(
86
+ "input",
87
+ {
88
+ ref: fileInputRef,
89
+ type: "file",
90
+ accept: ".json",
91
+ style: { display: "none" },
92
+ onChange: (e) => {
93
+ const file = e.target.files?.[0];
94
+ if (file) {
95
+ api.loadProject(file);
96
+ }
97
+ e.target.value = "";
98
+ }
99
+ }
100
+ )
101
+ ] });
23
102
  };
24
- /* ────────────────────────────────────────────────────────────────
25
- MODE TABS
26
- ──────────────────────────────────────────────────────────────── */
27
103
  const ModeTabs = ({ api }) => {
28
- const { t } = useNiceTranslation();
29
- const modes = [
30
- { id: 'tilemap', label: t('gameAsset2dEditor.tileMap', 'Tile Map'), icon: 'grid' },
31
- { id: 'spritesheet', label: t('gameAsset2dEditor.spriteSheet', 'Sprite Sheet'), icon: 'image' },
32
- { id: 'animation', label: t('gameAsset2dEditor.animation', 'Animation'), icon: 'video' },
33
- { id: 'effects', label: t('gameAsset2dEditor.effects2', 'Effects'), icon: 'wand' },
34
- ];
35
- return (jsx("div", { className: styles.modeTabs, children: modes.map((mode) => (jsxs(NiceButton, { className: `${styles.modeTab} ${api.editorState.mode === mode.id ? styles.active : ''}`, variant: api.editorState.mode === mode.id ? 'primary' : 'ghost', onClick: () => api.setMode(mode.id), children: [jsx(NiceIcon, { name: mode.icon, size: 16 }), " ", mode.label] }, mode.id))) }));
104
+ const { t } = useNiceTranslation();
105
+ const modes = [
106
+ { id: "tilemap", label: t("gameAsset2dEditor.tileMap", "Tile Map"), icon: "grid" },
107
+ { id: "spritesheet", label: t("gameAsset2dEditor.spriteSheet", "Sprite Sheet"), icon: "image" },
108
+ { id: "animation", label: t("gameAsset2dEditor.animation", "Animation"), icon: "video" },
109
+ { id: "effects", label: t("gameAsset2dEditor.effects2", "Effects"), icon: "wand" }
110
+ ];
111
+ return /* @__PURE__ */ jsx("div", { className: styles.modeTabs, children: modes.map((mode) => /* @__PURE__ */ jsxs(
112
+ NiceButton,
113
+ {
114
+ className: `${styles.modeTab} ${api.editorState.mode === mode.id ? styles.active : ""}`,
115
+ variant: api.editorState.mode === mode.id ? "primary" : "ghost",
116
+ onClick: () => api.setMode(mode.id),
117
+ children: [
118
+ /* @__PURE__ */ jsx(NiceIcon, { name: mode.icon, size: 16 }),
119
+ " ",
120
+ mode.label
121
+ ]
122
+ },
123
+ mode.id
124
+ )) });
36
125
  };
37
- /* ────────────────────────────────────────────────────────────────
38
- TILE MAP TOOLBAR
39
- ──────────────────────────────────────────────────────────────── */
40
126
  const TileMapToolbar = ({ api }) => {
41
- const { t } = useNiceTranslation();
42
- const tools = [
43
- { id: 'select', icon: 'square', label: t('gameAsset2dEditor.select', 'Select') },
44
- { id: 'brush', icon: 'brush', label: t('gameAsset2dEditor.brush', 'Brush') },
45
- { id: 'eraser', icon: 'eraser', label: t('gameAsset2dEditor.eraser', 'Eraser') },
46
- { id: 'fill', icon: 'bucket', label: t('gameAsset2dEditor.fill', 'Fill') },
47
- { id: 'rectangle', icon: 'square', label: t('gameAsset2dEditor.rectangle', 'Rectangle') },
48
- { id: 'line', icon: 'line', label: t('gameAsset2dEditor.line', 'Line') },
49
- {
50
- id: 'eyedropper',
51
- icon: 'eyedropper',
52
- label: t('gameAsset2dEditor.eyedropper', 'Eyedropper'),
53
- },
54
- ];
55
- return (jsxs("div", { className: styles.toolbar, children: [jsx("div", { className: styles.toolGroup, children: tools.map((tool) => (jsx(NiceButton, { className: `${styles.toolBtn} ${api.editorState.tileMapTool === tool.id ? styles.active : ''}`, variant: api.editorState.tileMapTool === tool.id ? 'primary' : 'ghost', size: "sm", onClick: () => api.setTileMapTool(tool.id), title: tool.label, children: jsx(NiceIcon, { name: tool.icon, size: 16 }) }, tool.id))) }), jsx("div", { className: styles.spacer }), jsx(NiceButton, { className: `${styles.toolBtn} ${api.editorState.showGrid ? styles.active : ''}`, variant: api.editorState.showGrid ? 'primary' : 'ghost', size: "sm", onClick: api.toggleGrid, title: t('gameAsset2dEditor.toggleGrid', 'Toggle Grid'), "aria-label": t('gameAsset2dEditor.toggleGrid', 'Toggle Grid'), children: jsx(NiceIcon, { name: "grid", size: 16 }) }), jsx(NiceButton, { className: `${styles.toolBtn} ${api.editorState.showCollision ? styles.active : ''}`, variant: api.editorState.showCollision ? 'primary' : 'ghost', size: "sm", onClick: api.toggleCollision, title: t('gameAsset2dEditor.showCollision', 'Show Collision'), "aria-label": t('gameAsset2dEditor.showCollision', 'Show Collision'), children: jsx(NiceIcon, { name: "square", size: 16 }) }), jsx(NiceButton, { className: `${styles.toolBtn} ${api.editorState.snapToGrid ? styles.active : ''}`, variant: api.editorState.snapToGrid ? 'primary' : 'ghost', size: "sm", onClick: api.toggleSnap, title: t('gameAsset2dEditor.snapToGrid', 'Snap to Grid'), "aria-label": t('gameAsset2dEditor.snapToGrid', 'Snap to Grid'), children: jsx(NiceIcon, { name: "grid", size: 16 }) })] }));
127
+ const { t } = useNiceTranslation();
128
+ const tools = [
129
+ { id: "select", icon: "square", label: t("gameAsset2dEditor.select", "Select") },
130
+ { id: "brush", icon: "brush", label: t("gameAsset2dEditor.brush", "Brush") },
131
+ { id: "eraser", icon: "eraser", label: t("gameAsset2dEditor.eraser", "Eraser") },
132
+ { id: "fill", icon: "bucket", label: t("gameAsset2dEditor.fill", "Fill") },
133
+ { id: "rectangle", icon: "square", label: t("gameAsset2dEditor.rectangle", "Rectangle") },
134
+ { id: "line", icon: "line", label: t("gameAsset2dEditor.line", "Line") },
135
+ {
136
+ id: "eyedropper",
137
+ icon: "eyedropper",
138
+ label: t("gameAsset2dEditor.eyedropper", "Eyedropper")
139
+ }
140
+ ];
141
+ return /* @__PURE__ */ jsxs("div", { className: styles.toolbar, children: [
142
+ /* @__PURE__ */ jsx("div", { className: styles.toolGroup, children: tools.map((tool) => /* @__PURE__ */ jsx(
143
+ NiceButton,
144
+ {
145
+ className: `${styles.toolBtn} ${api.editorState.tileMapTool === tool.id ? styles.active : ""}`,
146
+ variant: api.editorState.tileMapTool === tool.id ? "primary" : "ghost",
147
+ size: "sm",
148
+ onClick: () => api.setTileMapTool(tool.id),
149
+ title: tool.label,
150
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: tool.icon, size: 16 })
151
+ },
152
+ tool.id
153
+ )) }),
154
+ /* @__PURE__ */ jsx("div", { className: styles.spacer }),
155
+ /* @__PURE__ */ jsx(
156
+ NiceButton,
157
+ {
158
+ className: `${styles.toolBtn} ${api.editorState.showGrid ? styles.active : ""}`,
159
+ variant: api.editorState.showGrid ? "primary" : "ghost",
160
+ size: "sm",
161
+ onClick: api.toggleGrid,
162
+ title: t("gameAsset2dEditor.toggleGrid", "Toggle Grid"),
163
+ "aria-label": t("gameAsset2dEditor.toggleGrid", "Toggle Grid"),
164
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "grid", size: 16 })
165
+ }
166
+ ),
167
+ /* @__PURE__ */ jsx(
168
+ NiceButton,
169
+ {
170
+ className: `${styles.toolBtn} ${api.editorState.showCollision ? styles.active : ""}`,
171
+ variant: api.editorState.showCollision ? "primary" : "ghost",
172
+ size: "sm",
173
+ onClick: api.toggleCollision,
174
+ title: t("gameAsset2dEditor.showCollision", "Show Collision"),
175
+ "aria-label": t("gameAsset2dEditor.showCollision", "Show Collision"),
176
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "square", size: 16 })
177
+ }
178
+ ),
179
+ /* @__PURE__ */ jsx(
180
+ NiceButton,
181
+ {
182
+ className: `${styles.toolBtn} ${api.editorState.snapToGrid ? styles.active : ""}`,
183
+ variant: api.editorState.snapToGrid ? "primary" : "ghost",
184
+ size: "sm",
185
+ onClick: api.toggleSnap,
186
+ title: t("gameAsset2dEditor.snapToGrid", "Snap to Grid"),
187
+ "aria-label": t("gameAsset2dEditor.snapToGrid", "Snap to Grid"),
188
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "grid", size: 16 })
189
+ }
190
+ )
191
+ ] });
56
192
  };
57
- /* ────────────────────────────────────────────────────────────────
58
- SPRITE SHEET TOOLBAR
59
- ──────────────────────────────────────────────────────────────── */
60
193
  const SpriteSheetToolbar = ({ api }) => {
61
- const { t } = useNiceTranslation();
62
- const [sliceWidth, setSliceWidth] = useState(16);
63
- const [sliceHeight, setSliceHeight] = useState(16);
64
- return (jsxs("div", { className: styles.toolbar, children: [jsxs("div", { className: styles.toolGroup, children: [jsx(NiceButton, { className: `${styles.toolBtn} ${api.editorState.spriteSheetTool === 'select' ? styles.active : ''}`, variant: api.editorState.spriteSheetTool === 'select' ? 'primary' : 'ghost', size: "sm", onClick: () => api.setSpriteSheetTool('select'), title: t('gameAsset2dEditor.select', 'Select'), "aria-label": t('gameAsset2dEditor.select', 'Select'), children: jsx(NiceIcon, { name: "square", size: 16 }) }), jsx(NiceButton, { className: `${styles.toolBtn} ${api.editorState.spriteSheetTool === 'slice' ? styles.active : ''}`, variant: api.editorState.spriteSheetTool === 'slice' ? 'primary' : 'ghost', size: "sm", onClick: () => api.setSpriteSheetTool('slice'), title: t('gameAsset2dEditor.manualSlice', 'Manual Slice'), "aria-label": t('gameAsset2dEditor.manualSlice', 'Manual Slice'), children: jsx(NiceIcon, { name: "scissors", size: 16 }) })] }), jsx("div", { className: styles.spacer }), jsxs("label", { style: { fontSize: 11, color: 'var(--nice-text-secondary, #8892a8)' }, children: [t('gameAsset2dEditor.frameSize', 'Frame size:'), jsx(NiceNumberInput, { value: sliceWidth, onChange: (val) => setSliceWidth(val !== null && val !== void 0 ? val : 16), style: { width: 40, margin: '0 4px' }, className: styles.panelInput }), "\u00D7", jsx(NiceNumberInput, { value: sliceHeight, onChange: (val) => setSliceHeight(val !== null && val !== void 0 ? val : 16), style: { width: 40, marginLeft: 4 }, className: styles.panelInput })] }), jsx(NiceButton, { className: styles.btnSecondary, variant: "ghost", onClick: () => api.autoSlice(sliceWidth, sliceHeight), style: { marginLeft: 8 }, children: t('gameAsset2dEditor.autoSlice', 'Auto Slice') }), jsx(NiceButton, { className: styles.btnSecondary, variant: "ghost", onClick: api.detectSprites, style: { marginLeft: 4 }, children: t('gameAsset2dEditor.detectSprites', 'Detect Sprites') })] }));
65
- };
66
- /* ────────────────────────────────────────────────────────────────
67
- TILE PALETTE (LEFT PANEL)
68
- ──────────────────────────────────────────────────────────────── */
69
- const TilePalette = ({ api }) => {
70
- const { t } = useNiceTranslation();
71
- const fileInputRef = useRef(null);
72
- return (jsxs("div", { className: styles.leftPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: t('gameAsset2dEditor.tiles', 'Tiles') }), jsx(NiceButton, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: t('gameAsset2dEditor.add', '+ Add') })] }), jsx("div", { className: styles.panelContent, children: api.project.tileSets.length === 0 ? (jsxs("div", { className: styles.emptyState, children: [jsx("div", { className: styles.emptyStateText, children: t('gameAsset2dEditor.noTilesYet', 'No tiles yet') }), jsx(NiceButton, { className: styles.btnSecondary, variant: "ghost", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: t('gameAsset2dEditor.importTiles', 'Import Tiles') })] })) : (jsx("div", { className: styles.tilePalette, children: api.project.tileSets.map((tile) => (jsx("div", { role: "button", tabIndex: 0, onKeyDown: clickOnActivationKey, className: `${styles.tileItem} ${api.editorState.selectedTileId === tile.id ? styles.selected : ''}`, onClick: () => api.selectTile(tile.id), title: tile.name, children: jsx("img", { src: tile.thumbnail, alt: tile.name }) }, tile.id))) })) }), jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: (e) => {
73
- var _a;
74
- const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
75
- if (file) {
76
- api.importTileSet(file);
77
- }
78
- e.target.value = '';
79
- } })] }));
80
- };
81
- /* ────────────────────────────────────────────────────────────────
82
- LAYERS PANEL (RIGHT PANEL)
83
- ──────────────────────────────────────────────────────────────── */
84
- const LayersPanel = ({ api }) => {
85
- const { t } = useNiceTranslation();
86
- const activeMap = api.project.tileMaps.find((m) => m.id === api.editorState.activeTileMapId);
87
- return (jsxs("div", { className: styles.rightPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: t('gameAsset2dEditor.layers', 'Layers') }), jsx(NiceButton, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: api.addLayer, disabled: !activeMap, children: t('gameAsset2dEditor.add', '+ Add') })] }), jsx("div", { className: styles.panelContent, children: activeMap ? (jsx("div", { className: styles.layersList, children: [...activeMap.layers].reverse().map((layer) => (jsxs("div", { role: "button", tabIndex: 0, onKeyDown: clickOnActivationKey, className: `${styles.layerItem} ${api.editorState.activeLayerId === layer.id ? styles.selected : ''}`, onClick: () => api.selectLayer(layer.id), children: [jsx(NiceButton, { className: `${styles.layerVisibility} ${!layer.visible ? styles.hidden : ''}`, variant: "ghost", size: "sm", onClick: (e) => {
88
- e.stopPropagation();
89
- api.toggleLayerVisibility(layer.id);
90
- }, "aria-label": layer.visible
91
- ? t('gameAsset2dEditor.hideLayer', 'Hide layer')
92
- : t('gameAsset2dEditor.showLayer', 'Show layer'), children: jsx(NiceIcon, { name: layer.visible ? 'eye' : 'eye-off', size: 16 }) }), jsx("span", { className: styles.layerName, children: layer.name }), jsxs("span", { className: styles.layerOpacity, children: [Math.round(layer.opacity * 100), "%"] })] }, layer.id))) })) : (jsx("div", { className: styles.emptyState, children: jsx("div", { className: styles.emptyStateText, children: t('gameAsset2dEditor.noTileMapSelected', 'No tile map selected') }) })) }), jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: t('gameAsset2dEditor.maps', 'Maps') }), jsx(NiceButton, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => api.createTileMap('New Map', 32, 32), children: t('gameAsset2dEditor.new2', '+ New') })] }), jsx("div", { className: styles.panelContent, children: api.project.tileMaps.map((map) => (jsxs("div", { role: "button", tabIndex: 0, onKeyDown: clickOnActivationKey, className: `${styles.layerItem} ${api.editorState.activeTileMapId === map.id ? styles.selected : ''}`, onClick: () => api.selectTileMap(map.id), children: [jsx("span", { className: styles.layerName, children: map.name }), jsxs("span", { className: styles.layerOpacity, children: [map.width, "\u00D7", map.height] })] }, map.id))) })] }));
93
- };
94
- /* ────────────────────────────────────────────────────────────────
95
- TILE MAP CANVAS
96
- ──────────────────────────────────────────────────────────────── */
97
- const TileMapCanvas = ({ api }) => {
98
- const { t } = useNiceTranslation();
99
- const canvasRef = useRef(null);
100
- const containerRef = useRef(null);
101
- const [isDragging, setIsDragging] = useState(false);
102
- const activeMap = api.project.tileMaps.find((m) => m.id === api.editorState.activeTileMapId);
103
- // Render the tile map
104
- useEffect(() => {
105
- var _a;
106
- const canvas = canvasRef.current;
107
- if (!canvas || !activeMap) {
108
- return;
109
- }
110
- const ctx = canvas.getContext('2d');
111
- if (!ctx) {
112
- return;
113
- }
114
- canvas.width = activeMap.width * activeMap.tileWidth;
115
- canvas.height = activeMap.height * activeMap.tileHeight;
116
- // Background
117
- ctx.fillStyle = activeMap.backgroundColor;
118
- ctx.fillRect(0, 0, canvas.width, canvas.height);
119
- // Render layers
120
- for (const layer of activeMap.layers.filter((l) => l.visible)) {
121
- ctx.globalAlpha = layer.opacity;
122
- for (let y = 0; y < activeMap.height; y++) {
123
- for (let x = 0; x < activeMap.width; x++) {
124
- const tileId = (_a = layer.tiles[y]) === null || _a === void 0 ? void 0 : _a[x];
125
- if (tileId) {
126
- const tile = api.project.tileSets.find((t) => t.id === tileId);
127
- if (tile === null || tile === void 0 ? void 0 : tile.thumbnail) {
128
- const img = new Image();
129
- img.src = tile.thumbnail;
130
- ctx.drawImage(img, x * activeMap.tileWidth, y * activeMap.tileHeight, activeMap.tileWidth, activeMap.tileHeight);
131
- }
132
- }
133
- }
134
- }
194
+ const { t } = useNiceTranslation();
195
+ const [sliceWidth, setSliceWidth] = useState(16);
196
+ const [sliceHeight, setSliceHeight] = useState(16);
197
+ return /* @__PURE__ */ jsxs("div", { className: styles.toolbar, children: [
198
+ /* @__PURE__ */ jsxs("div", { className: styles.toolGroup, children: [
199
+ /* @__PURE__ */ jsx(
200
+ NiceButton,
201
+ {
202
+ className: `${styles.toolBtn} ${api.editorState.spriteSheetTool === "select" ? styles.active : ""}`,
203
+ variant: api.editorState.spriteSheetTool === "select" ? "primary" : "ghost",
204
+ size: "sm",
205
+ onClick: () => api.setSpriteSheetTool("select"),
206
+ title: t("gameAsset2dEditor.select", "Select"),
207
+ "aria-label": t("gameAsset2dEditor.select", "Select"),
208
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "square", size: 16 })
135
209
  }
136
- // Grid overlay
137
- if (api.editorState.showGrid) {
138
- ctx.globalAlpha = 0.3;
139
- ctx.strokeStyle = resolveCssColor('var(--nice-bg, #fff)', ctx.canvas);
140
- ctx.lineWidth = 1;
141
- for (let x = 0; x <= activeMap.width; x++) {
142
- ctx.beginPath();
143
- ctx.moveTo(x * activeMap.tileWidth, 0);
144
- ctx.lineTo(x * activeMap.tileWidth, canvas.height);
145
- ctx.stroke();
146
- }
147
- for (let y = 0; y <= activeMap.height; y++) {
148
- ctx.beginPath();
149
- ctx.moveTo(0, y * activeMap.tileHeight);
150
- ctx.lineTo(canvas.width, y * activeMap.tileHeight);
151
- ctx.stroke();
152
- }
210
+ ),
211
+ /* @__PURE__ */ jsx(
212
+ NiceButton,
213
+ {
214
+ className: `${styles.toolBtn} ${api.editorState.spriteSheetTool === "slice" ? styles.active : ""}`,
215
+ variant: api.editorState.spriteSheetTool === "slice" ? "primary" : "ghost",
216
+ size: "sm",
217
+ onClick: () => api.setSpriteSheetTool("slice"),
218
+ title: t("gameAsset2dEditor.manualSlice", "Manual Slice"),
219
+ "aria-label": t("gameAsset2dEditor.manualSlice", "Manual Slice"),
220
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "scissors", size: 16 })
153
221
  }
154
- ctx.globalAlpha = 1;
155
- }, [activeMap, api.project.tileSets, api.editorState.showGrid]);
156
- const handleCanvasClick = useCallback((e) => {
157
- if (!activeMap || !api.editorState.activeLayerId || !api.editorState.selectedTileId) {
158
- return;
222
+ )
223
+ ] }),
224
+ /* @__PURE__ */ jsx("div", { className: styles.spacer }),
225
+ /* @__PURE__ */ jsxs("label", { style: { fontSize: 11, color: "var(--nice-text-secondary, #8892a8)" }, children: [
226
+ t("gameAsset2dEditor.frameSize", "Frame size:"),
227
+ /* @__PURE__ */ jsx(
228
+ NiceNumberInput,
229
+ {
230
+ value: sliceWidth,
231
+ onChange: (val) => setSliceWidth(val ?? 16),
232
+ style: { width: 40, margin: "0 4px" },
233
+ className: styles.panelInput
159
234
  }
160
- const canvas = canvasRef.current;
161
- if (!canvas) {
162
- return;
235
+ ),
236
+ "×",
237
+ /* @__PURE__ */ jsx(
238
+ NiceNumberInput,
239
+ {
240
+ value: sliceHeight,
241
+ onChange: (val) => setSliceHeight(val ?? 16),
242
+ style: { width: 40, marginLeft: 4 },
243
+ className: styles.panelInput
163
244
  }
164
- const rect = canvas.getBoundingClientRect();
165
- const scaleX = canvas.width / rect.width;
166
- const scaleY = canvas.height / rect.height;
167
- const x = Math.floor(((e.clientX - rect.left) * scaleX) / activeMap.tileWidth);
168
- const y = Math.floor(((e.clientY - rect.top) * scaleY) / activeMap.tileHeight);
169
- if (api.editorState.tileMapTool === 'brush') {
170
- api.setTile(api.editorState.activeLayerId, x, y, api.editorState.selectedTileId);
245
+ )
246
+ ] }),
247
+ /* @__PURE__ */ jsx(
248
+ NiceButton,
249
+ {
250
+ className: styles.btnSecondary,
251
+ variant: "ghost",
252
+ onClick: () => api.autoSlice(sliceWidth, sliceHeight),
253
+ style: { marginLeft: 8 },
254
+ children: t("gameAsset2dEditor.autoSlice", "Auto Slice")
255
+ }
256
+ ),
257
+ /* @__PURE__ */ jsx(
258
+ NiceButton,
259
+ {
260
+ className: styles.btnSecondary,
261
+ variant: "ghost",
262
+ onClick: api.detectSprites,
263
+ style: { marginLeft: 4 },
264
+ children: t("gameAsset2dEditor.detectSprites", "Detect Sprites")
265
+ }
266
+ )
267
+ ] });
268
+ };
269
+ const TilePalette = ({ api }) => {
270
+ const { t } = useNiceTranslation();
271
+ const fileInputRef = useRef(null);
272
+ return /* @__PURE__ */ jsxs("div", { className: styles.leftPanel, children: [
273
+ /* @__PURE__ */ jsxs("div", { className: styles.panelHeader, children: [
274
+ /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.tiles", "Tiles") }),
275
+ /* @__PURE__ */ jsx(
276
+ NiceButton,
277
+ {
278
+ className: styles.menuBtn,
279
+ variant: "ghost",
280
+ size: "sm",
281
+ onClick: () => fileInputRef.current?.click(),
282
+ children: t("gameAsset2dEditor.add", "+ Add")
171
283
  }
172
- else if (api.editorState.tileMapTool === 'eraser') {
173
- api.setTile(api.editorState.activeLayerId, x, y, null);
284
+ )
285
+ ] }),
286
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: api.project.tileSets.length === 0 ? /* @__PURE__ */ jsxs("div", { className: styles.emptyState, children: [
287
+ /* @__PURE__ */ jsx("div", { className: styles.emptyStateText, children: t("gameAsset2dEditor.noTilesYet", "No tiles yet") }),
288
+ /* @__PURE__ */ jsx(
289
+ NiceButton,
290
+ {
291
+ className: styles.btnSecondary,
292
+ variant: "ghost",
293
+ onClick: () => fileInputRef.current?.click(),
294
+ children: t("gameAsset2dEditor.importTiles", "Import Tiles")
174
295
  }
175
- else if (api.editorState.tileMapTool === 'fill') {
176
- api.fillTiles(api.editorState.activeLayerId, x, y, api.editorState.selectedTileId);
296
+ )
297
+ ] }) : /* @__PURE__ */ jsx("div", { className: styles.tilePalette, children: api.project.tileSets.map((tile) => /* @__PURE__ */ jsx(
298
+ "div",
299
+ {
300
+ role: "button",
301
+ tabIndex: 0,
302
+ onKeyDown: clickOnActivationKey,
303
+ className: `${styles.tileItem} ${api.editorState.selectedTileId === tile.id ? styles.selected : ""}`,
304
+ onClick: () => api.selectTile(tile.id),
305
+ title: tile.name,
306
+ children: /* @__PURE__ */ jsx("img", { src: tile.thumbnail, alt: tile.name })
307
+ },
308
+ tile.id
309
+ )) }) }),
310
+ /* @__PURE__ */ jsx(
311
+ "input",
312
+ {
313
+ ref: fileInputRef,
314
+ type: "file",
315
+ accept: "image/*",
316
+ style: { display: "none" },
317
+ onChange: (e) => {
318
+ const file = e.target.files?.[0];
319
+ if (file) {
320
+ api.importTileSet(file);
321
+ }
322
+ e.target.value = "";
177
323
  }
178
- }, [activeMap, api]);
179
- return (jsx("div", { className: styles.viewport, ref: containerRef, children: activeMap ? (jsx("canvas", { ref: canvasRef, className: styles.viewportCanvas, style: {
180
- transform: `translate(-50%, -50%) scale(${api.editorState.zoom})`,
181
- }, onClick: handleCanvasClick, onMouseDown: () => setIsDragging(true), onMouseUp: () => setIsDragging(false), onMouseMove: (e) => {
182
- if (isDragging &&
183
- (api.editorState.tileMapTool === 'brush' || api.editorState.tileMapTool === 'eraser')) {
184
- handleCanvasClick(e);
185
- }
186
- } })) : (jsxs("div", { className: styles.emptyState, children: [jsx("div", { className: styles.emptyStateText, children: t('gameAsset2dEditor.createOrSelectATile', 'Create or select a tile map to start editing') }), jsx(NiceButton, { className: styles.btnPrimary, variant: "primary", onClick: () => api.createTileMap('New Map', 32, 32), children: t('gameAsset2dEditor.createTileMap', 'Create Tile Map') })] })) }));
324
+ }
325
+ )
326
+ ] });
187
327
  };
188
- /* ────────────────────────────────────────────────────────────────
189
- SPRITE SHEET CANVAS
190
- ──────────────────────────────────────────────────────────────── */
191
- const SpriteSheetCanvas = ({ api }) => {
192
- const { t } = useNiceTranslation();
193
- const canvasRef = useRef(null);
194
- const fileInputRef = useRef(null);
195
- const activeSheet = api.project.spriteSheets.find((s) => s.id === api.editorState.activeSpriteSheetId);
196
- // Render sprite sheet with frame overlays
197
- useEffect(() => {
198
- const canvas = canvasRef.current;
199
- if (!canvas || !(activeSheet === null || activeSheet === void 0 ? void 0 : activeSheet.image)) {
200
- return;
201
- }
202
- const ctx = canvas.getContext('2d');
203
- if (!ctx) {
204
- return;
328
+ const LayersPanel = ({ api }) => {
329
+ const { t } = useNiceTranslation();
330
+ const activeMap = api.project.tileMaps.find((m) => m.id === api.editorState.activeTileMapId);
331
+ return /* @__PURE__ */ jsxs("div", { className: styles.rightPanel, children: [
332
+ /* @__PURE__ */ jsxs("div", { className: styles.panelHeader, children: [
333
+ /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.layers", "Layers") }),
334
+ /* @__PURE__ */ jsx(
335
+ NiceButton,
336
+ {
337
+ className: styles.menuBtn,
338
+ variant: "ghost",
339
+ size: "sm",
340
+ onClick: api.addLayer,
341
+ disabled: !activeMap,
342
+ children: t("gameAsset2dEditor.add", "+ Add")
205
343
  }
206
- canvas.width = activeSheet.width;
207
- canvas.height = activeSheet.height;
208
- // Draw image
209
- ctx.drawImage(activeSheet.image, 0, 0);
210
- // Draw frame overlays
211
- ctx.strokeStyle = resolveCssColor('var(--nice-warning, #ffd369)', ctx.canvas);
212
- ctx.lineWidth = 2;
213
- for (const frame of activeSheet.frames) {
214
- const isSelected = api.editorState.selectedFrameIds.includes(frame.id);
215
- ctx.strokeStyle = resolveCssColor(isSelected ? 'var(--nice-warning, #ffd369)' : 'var(--nice-text-secondary, #4a5a7e)', ctx.canvas);
216
- ctx.strokeRect(frame.x, frame.y, frame.width, frame.height);
217
- // Draw pivot point
218
- const pivotX = frame.x + frame.width * frame.pivotX;
219
- const pivotY = frame.y + frame.height * frame.pivotY;
220
- ctx.fillStyle = resolveCssColor(isSelected ? 'var(--nice-warning, #ffd369)' : 'var(--nice-text-secondary, #4a5a7e)', ctx.canvas);
221
- ctx.beginPath();
222
- ctx.arc(pivotX, pivotY, 3, 0, Math.PI * 2);
223
- ctx.fill();
344
+ )
345
+ ] }),
346
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: activeMap ? /* @__PURE__ */ jsx("div", { className: styles.layersList, children: [...activeMap.layers].reverse().map((layer) => /* @__PURE__ */ jsxs(
347
+ "div",
348
+ {
349
+ role: "button",
350
+ tabIndex: 0,
351
+ onKeyDown: clickOnActivationKey,
352
+ className: `${styles.layerItem} ${api.editorState.activeLayerId === layer.id ? styles.selected : ""}`,
353
+ onClick: () => api.selectLayer(layer.id),
354
+ children: [
355
+ /* @__PURE__ */ jsx(
356
+ NiceButton,
357
+ {
358
+ className: `${styles.layerVisibility} ${!layer.visible ? styles.hidden : ""}`,
359
+ variant: "ghost",
360
+ size: "sm",
361
+ onClick: (e) => {
362
+ e.stopPropagation();
363
+ api.toggleLayerVisibility(layer.id);
364
+ },
365
+ "aria-label": layer.visible ? t("gameAsset2dEditor.hideLayer", "Hide layer") : t("gameAsset2dEditor.showLayer", "Show layer"),
366
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: layer.visible ? "eye" : "eye-off", size: 16 })
367
+ }
368
+ ),
369
+ /* @__PURE__ */ jsx("span", { className: styles.layerName, children: layer.name }),
370
+ /* @__PURE__ */ jsxs("span", { className: styles.layerOpacity, children: [
371
+ Math.round(layer.opacity * 100),
372
+ "%"
373
+ ] })
374
+ ]
375
+ },
376
+ layer.id
377
+ )) }) : /* @__PURE__ */ jsx("div", { className: styles.emptyState, children: /* @__PURE__ */ jsx("div", { className: styles.emptyStateText, children: t("gameAsset2dEditor.noTileMapSelected", "No tile map selected") }) }) }),
378
+ /* @__PURE__ */ jsxs("div", { className: styles.panelHeader, children: [
379
+ /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.maps", "Maps") }),
380
+ /* @__PURE__ */ jsx(
381
+ NiceButton,
382
+ {
383
+ className: styles.menuBtn,
384
+ variant: "ghost",
385
+ size: "sm",
386
+ onClick: () => api.createTileMap("New Map", 32, 32),
387
+ children: t("gameAsset2dEditor.new2", "+ New")
224
388
  }
225
- }, [activeSheet, api.editorState.selectedFrameIds]);
226
- const handleFrameClick = useCallback((e) => {
227
- if (!activeSheet) {
228
- return;
389
+ )
390
+ ] }),
391
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: api.project.tileMaps.map((map) => /* @__PURE__ */ jsxs(
392
+ "div",
393
+ {
394
+ role: "button",
395
+ tabIndex: 0,
396
+ onKeyDown: clickOnActivationKey,
397
+ className: `${styles.layerItem} ${api.editorState.activeTileMapId === map.id ? styles.selected : ""}`,
398
+ onClick: () => api.selectTileMap(map.id),
399
+ children: [
400
+ /* @__PURE__ */ jsx("span", { className: styles.layerName, children: map.name }),
401
+ /* @__PURE__ */ jsxs("span", { className: styles.layerOpacity, children: [
402
+ map.width,
403
+ "×",
404
+ map.height
405
+ ] })
406
+ ]
407
+ },
408
+ map.id
409
+ )) })
410
+ ] });
411
+ };
412
+ const TileMapCanvas = ({ api }) => {
413
+ const { t } = useNiceTranslation();
414
+ const canvasRef = useRef(null);
415
+ const containerRef = useRef(null);
416
+ const [isDragging, setIsDragging] = useState(false);
417
+ const activeMap = api.project.tileMaps.find((m) => m.id === api.editorState.activeTileMapId);
418
+ useEffect(() => {
419
+ const canvas = canvasRef.current;
420
+ if (!canvas || !activeMap) {
421
+ return;
422
+ }
423
+ const ctx = canvas.getContext("2d");
424
+ if (!ctx) {
425
+ return;
426
+ }
427
+ canvas.width = activeMap.width * activeMap.tileWidth;
428
+ canvas.height = activeMap.height * activeMap.tileHeight;
429
+ ctx.fillStyle = activeMap.backgroundColor;
430
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
431
+ for (const layer of activeMap.layers.filter((l) => l.visible)) {
432
+ ctx.globalAlpha = layer.opacity;
433
+ for (let y = 0; y < activeMap.height; y++) {
434
+ for (let x = 0; x < activeMap.width; x++) {
435
+ const tileId = layer.tiles[y]?.[x];
436
+ if (tileId) {
437
+ const tile = api.project.tileSets.find((t2) => t2.id === tileId);
438
+ if (tile?.thumbnail) {
439
+ const img = new Image();
440
+ img.src = tile.thumbnail;
441
+ ctx.drawImage(
442
+ img,
443
+ x * activeMap.tileWidth,
444
+ y * activeMap.tileHeight,
445
+ activeMap.tileWidth,
446
+ activeMap.tileHeight
447
+ );
448
+ }
449
+ }
229
450
  }
230
- const canvas = canvasRef.current;
231
- if (!canvas) {
232
- return;
451
+ }
452
+ }
453
+ if (api.editorState.showGrid) {
454
+ ctx.globalAlpha = 0.3;
455
+ ctx.strokeStyle = resolveCssColor("var(--nice-bg, #fff)", ctx.canvas);
456
+ ctx.lineWidth = 1;
457
+ for (let x = 0; x <= activeMap.width; x++) {
458
+ ctx.beginPath();
459
+ ctx.moveTo(x * activeMap.tileWidth, 0);
460
+ ctx.lineTo(x * activeMap.tileWidth, canvas.height);
461
+ ctx.stroke();
462
+ }
463
+ for (let y = 0; y <= activeMap.height; y++) {
464
+ ctx.beginPath();
465
+ ctx.moveTo(0, y * activeMap.tileHeight);
466
+ ctx.lineTo(canvas.width, y * activeMap.tileHeight);
467
+ ctx.stroke();
468
+ }
469
+ }
470
+ ctx.globalAlpha = 1;
471
+ }, [activeMap, api.project.tileSets, api.editorState.showGrid]);
472
+ const handleCanvasClick = useCallback(
473
+ (e) => {
474
+ if (!activeMap || !api.editorState.activeLayerId || !api.editorState.selectedTileId) {
475
+ return;
476
+ }
477
+ const canvas = canvasRef.current;
478
+ if (!canvas) {
479
+ return;
480
+ }
481
+ const rect = canvas.getBoundingClientRect();
482
+ const scaleX = canvas.width / rect.width;
483
+ const scaleY = canvas.height / rect.height;
484
+ const x = Math.floor((e.clientX - rect.left) * scaleX / activeMap.tileWidth);
485
+ const y = Math.floor((e.clientY - rect.top) * scaleY / activeMap.tileHeight);
486
+ if (api.editorState.tileMapTool === "brush") {
487
+ api.setTile(api.editorState.activeLayerId, x, y, api.editorState.selectedTileId);
488
+ } else if (api.editorState.tileMapTool === "eraser") {
489
+ api.setTile(api.editorState.activeLayerId, x, y, null);
490
+ } else if (api.editorState.tileMapTool === "fill") {
491
+ api.fillTiles(api.editorState.activeLayerId, x, y, api.editorState.selectedTileId);
492
+ }
493
+ },
494
+ [activeMap, api]
495
+ );
496
+ return /* @__PURE__ */ jsx("div", { className: styles.viewport, ref: containerRef, children: activeMap ? /* @__PURE__ */ jsx(
497
+ "canvas",
498
+ {
499
+ ref: canvasRef,
500
+ className: styles.viewportCanvas,
501
+ style: {
502
+ transform: `translate(-50%, -50%) scale(${api.editorState.zoom})`
503
+ },
504
+ onClick: handleCanvasClick,
505
+ onMouseDown: () => setIsDragging(true),
506
+ onMouseUp: () => setIsDragging(false),
507
+ onMouseMove: (e) => {
508
+ if (isDragging && (api.editorState.tileMapTool === "brush" || api.editorState.tileMapTool === "eraser")) {
509
+ handleCanvasClick(e);
233
510
  }
234
- const rect = canvas.getBoundingClientRect();
235
- const scaleX = canvas.width / rect.width;
236
- const scaleY = canvas.height / rect.height;
237
- const x = (e.clientX - rect.left) * scaleX;
238
- const y = (e.clientY - rect.top) * scaleY;
239
- // Find clicked frame
240
- const clickedFrame = activeSheet.frames.find((f) => x >= f.x && x <= f.x + f.width && y >= f.y && y <= f.y + f.height);
241
- if (clickedFrame) {
242
- if (e.ctrlKey || e.metaKey) {
243
- // Toggle selection
244
- const newSelection = api.editorState.selectedFrameIds.includes(clickedFrame.id)
245
- ? api.editorState.selectedFrameIds.filter((id) => id !== clickedFrame.id)
246
- : [...api.editorState.selectedFrameIds, clickedFrame.id];
247
- api.selectFrames(newSelection);
248
- }
249
- else {
250
- api.selectFrames([clickedFrame.id]);
251
- }
511
+ }
512
+ }
513
+ ) : /* @__PURE__ */ jsxs("div", { className: styles.emptyState, children: [
514
+ /* @__PURE__ */ jsx("div", { className: styles.emptyStateText, children: t(
515
+ "gameAsset2dEditor.createOrSelectATile",
516
+ "Create or select a tile map to start editing"
517
+ ) }),
518
+ /* @__PURE__ */ jsx(
519
+ NiceButton,
520
+ {
521
+ className: styles.btnPrimary,
522
+ variant: "primary",
523
+ onClick: () => api.createTileMap("New Map", 32, 32),
524
+ children: t("gameAsset2dEditor.createTileMap", "Create Tile Map")
525
+ }
526
+ )
527
+ ] }) });
528
+ };
529
+ const SpriteSheetCanvas = ({ api }) => {
530
+ const { t } = useNiceTranslation();
531
+ const canvasRef = useRef(null);
532
+ const fileInputRef = useRef(null);
533
+ const activeSheet = api.project.spriteSheets.find(
534
+ (s) => s.id === api.editorState.activeSpriteSheetId
535
+ );
536
+ useEffect(() => {
537
+ const canvas = canvasRef.current;
538
+ if (!canvas || !activeSheet?.image) {
539
+ return;
540
+ }
541
+ const ctx = canvas.getContext("2d");
542
+ if (!ctx) {
543
+ return;
544
+ }
545
+ canvas.width = activeSheet.width;
546
+ canvas.height = activeSheet.height;
547
+ ctx.drawImage(activeSheet.image, 0, 0);
548
+ ctx.strokeStyle = resolveCssColor("var(--nice-warning, #ffd369)", ctx.canvas);
549
+ ctx.lineWidth = 2;
550
+ for (const frame of activeSheet.frames) {
551
+ const isSelected = api.editorState.selectedFrameIds.includes(frame.id);
552
+ ctx.strokeStyle = resolveCssColor(
553
+ isSelected ? "var(--nice-warning, #ffd369)" : "var(--nice-text-secondary, #4a5a7e)",
554
+ ctx.canvas
555
+ );
556
+ ctx.strokeRect(frame.x, frame.y, frame.width, frame.height);
557
+ const pivotX = frame.x + frame.width * frame.pivotX;
558
+ const pivotY = frame.y + frame.height * frame.pivotY;
559
+ ctx.fillStyle = resolveCssColor(
560
+ isSelected ? "var(--nice-warning, #ffd369)" : "var(--nice-text-secondary, #4a5a7e)",
561
+ ctx.canvas
562
+ );
563
+ ctx.beginPath();
564
+ ctx.arc(pivotX, pivotY, 3, 0, Math.PI * 2);
565
+ ctx.fill();
566
+ }
567
+ }, [activeSheet, api.editorState.selectedFrameIds]);
568
+ const handleFrameClick = useCallback(
569
+ (e) => {
570
+ if (!activeSheet) {
571
+ return;
572
+ }
573
+ const canvas = canvasRef.current;
574
+ if (!canvas) {
575
+ return;
576
+ }
577
+ const rect = canvas.getBoundingClientRect();
578
+ const scaleX = canvas.width / rect.width;
579
+ const scaleY = canvas.height / rect.height;
580
+ const x = (e.clientX - rect.left) * scaleX;
581
+ const y = (e.clientY - rect.top) * scaleY;
582
+ const clickedFrame = activeSheet.frames.find(
583
+ (f) => x >= f.x && x <= f.x + f.width && y >= f.y && y <= f.y + f.height
584
+ );
585
+ if (clickedFrame) {
586
+ if (e.ctrlKey || e.metaKey) {
587
+ const newSelection = api.editorState.selectedFrameIds.includes(clickedFrame.id) ? api.editorState.selectedFrameIds.filter((id) => id !== clickedFrame.id) : [...api.editorState.selectedFrameIds, clickedFrame.id];
588
+ api.selectFrames(newSelection);
589
+ } else {
590
+ api.selectFrames([clickedFrame.id]);
252
591
  }
253
- else {
254
- api.selectFrames([]);
592
+ } else {
593
+ api.selectFrames([]);
594
+ }
595
+ },
596
+ [activeSheet, api]
597
+ );
598
+ return /* @__PURE__ */ jsx("div", { className: styles.viewport, children: activeSheet ? /* @__PURE__ */ jsx(
599
+ "canvas",
600
+ {
601
+ ref: canvasRef,
602
+ className: styles.viewportCanvas,
603
+ style: {
604
+ transform: `translate(-50%, -50%) scale(${api.editorState.zoom})`
605
+ },
606
+ onClick: handleFrameClick
607
+ }
608
+ ) : /* @__PURE__ */ jsxs("div", { className: styles.emptyState, children: [
609
+ /* @__PURE__ */ jsx("div", { className: styles.emptyStateText, children: t("gameAsset2dEditor.importASpriteSheetTo", "Import a sprite sheet to start") }),
610
+ /* @__PURE__ */ jsx(
611
+ NiceButton,
612
+ {
613
+ className: styles.btnPrimary,
614
+ variant: "primary",
615
+ onClick: () => fileInputRef.current?.click(),
616
+ children: t("gameAsset2dEditor.importSpriteSheet", "Import Sprite Sheet")
617
+ }
618
+ ),
619
+ /* @__PURE__ */ jsx(
620
+ "input",
621
+ {
622
+ ref: fileInputRef,
623
+ type: "file",
624
+ accept: "image/*",
625
+ style: { display: "none" },
626
+ onChange: (e) => {
627
+ const file = e.target.files?.[0];
628
+ if (file) {
629
+ api.importSpriteSheet(file);
630
+ }
631
+ e.target.value = "";
255
632
  }
256
- }, [activeSheet, api]);
257
- return (jsx("div", { className: styles.viewport, children: activeSheet ? (jsx("canvas", { ref: canvasRef, className: styles.viewportCanvas, style: {
258
- transform: `translate(-50%, -50%) scale(${api.editorState.zoom})`,
259
- }, onClick: handleFrameClick })) : (jsxs("div", { className: styles.emptyState, children: [jsx("div", { className: styles.emptyStateText, children: t('gameAsset2dEditor.importASpriteSheetTo', 'Import a sprite sheet to start') }), jsx(NiceButton, { className: styles.btnPrimary, variant: "primary", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: t('gameAsset2dEditor.importSpriteSheet', 'Import Sprite Sheet') }), jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: (e) => {
260
- var _a;
261
- const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
262
- if (file) {
263
- api.importSpriteSheet(file);
264
- }
265
- e.target.value = '';
266
- } })] })) }));
633
+ }
634
+ )
635
+ ] }) });
267
636
  };
268
- /* ────────────────────────────────────────────────────────────────
269
- SPRITE SHEETS LIST
270
- ──────────────────────────────────────────────────────────────── */
271
637
  const SpriteSheetsList = ({ api }) => {
272
- const { t } = useNiceTranslation();
273
- const fileInputRef = useRef(null);
274
- return (jsxs("div", { className: styles.leftPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: t('gameAsset2dEditor.spriteSheets', 'Sprite Sheets') }), jsx(NiceButton, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => { var _a; return (_a = fileInputRef.current) === null || _a === void 0 ? void 0 : _a.click(); }, children: t('gameAsset2dEditor.import', '+ Import') })] }), jsx("div", { className: styles.panelContent, children: api.project.spriteSheets.length === 0 ? (jsx("div", { className: styles.emptyState, children: jsx("div", { className: styles.emptyStateText, children: t('gameAsset2dEditor.noSpriteSheets', 'No sprite sheets') }) })) : (jsx("div", { className: styles.layersList, children: api.project.spriteSheets.map((sheet) => (jsxs("div", { role: "button", tabIndex: 0, onKeyDown: clickOnActivationKey, className: `${styles.layerItem} ${api.editorState.activeSpriteSheetId === sheet.id ? styles.selected : ''}`, onClick: () => api.selectSpriteSheet(sheet.id), children: [jsx("span", { className: styles.layerName, children: sheet.name }), jsx("span", { className: styles.layerOpacity, children: t('gameAsset2dEditor.frames', '{frames} frames').replace('{frames}', String(sheet.frames.length)) })] }, sheet.id))) })) }), jsx("input", { ref: fileInputRef, type: "file", accept: "image/*", style: { display: 'none' }, onChange: (e) => {
275
- var _a;
276
- const file = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
277
- if (file) {
278
- api.importSpriteSheet(file);
279
- }
280
- e.target.value = '';
281
- } })] }));
638
+ const { t } = useNiceTranslation();
639
+ const fileInputRef = useRef(null);
640
+ return /* @__PURE__ */ jsxs("div", { className: styles.leftPanel, children: [
641
+ /* @__PURE__ */ jsxs("div", { className: styles.panelHeader, children: [
642
+ /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.spriteSheets", "Sprite Sheets") }),
643
+ /* @__PURE__ */ jsx(
644
+ NiceButton,
645
+ {
646
+ className: styles.menuBtn,
647
+ variant: "ghost",
648
+ size: "sm",
649
+ onClick: () => fileInputRef.current?.click(),
650
+ children: t("gameAsset2dEditor.import", "+ Import")
651
+ }
652
+ )
653
+ ] }),
654
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: api.project.spriteSheets.length === 0 ? /* @__PURE__ */ jsx("div", { className: styles.emptyState, children: /* @__PURE__ */ jsx("div", { className: styles.emptyStateText, children: t("gameAsset2dEditor.noSpriteSheets", "No sprite sheets") }) }) : /* @__PURE__ */ jsx("div", { className: styles.layersList, children: api.project.spriteSheets.map((sheet) => /* @__PURE__ */ jsxs(
655
+ "div",
656
+ {
657
+ role: "button",
658
+ tabIndex: 0,
659
+ onKeyDown: clickOnActivationKey,
660
+ className: `${styles.layerItem} ${api.editorState.activeSpriteSheetId === sheet.id ? styles.selected : ""}`,
661
+ onClick: () => api.selectSpriteSheet(sheet.id),
662
+ children: [
663
+ /* @__PURE__ */ jsx("span", { className: styles.layerName, children: sheet.name }),
664
+ /* @__PURE__ */ jsx("span", { className: styles.layerOpacity, children: t("gameAsset2dEditor.frames", "{frames} frames").replace(
665
+ "{frames}",
666
+ String(sheet.frames.length)
667
+ ) })
668
+ ]
669
+ },
670
+ sheet.id
671
+ )) }) }),
672
+ /* @__PURE__ */ jsx(
673
+ "input",
674
+ {
675
+ ref: fileInputRef,
676
+ type: "file",
677
+ accept: "image/*",
678
+ style: { display: "none" },
679
+ onChange: (e) => {
680
+ const file = e.target.files?.[0];
681
+ if (file) {
682
+ api.importSpriteSheet(file);
683
+ }
684
+ e.target.value = "";
685
+ }
686
+ }
687
+ )
688
+ ] });
282
689
  };
283
- /* ────────────────────────────────────────────────────────────────
284
- FRAMES & ANIMATIONS PANEL
285
- ──────────────────────────────────────────────────────────────── */
286
690
  const FramesPanel = ({ api }) => {
287
- var _a;
288
- const { t } = useNiceTranslation();
289
- const [animName, setAnimName] = useState('');
290
- const activeSheet = api.project.spriteSheets.find((s) => s.id === api.editorState.activeSpriteSheetId);
291
- const createAnimationFromSelection = useCallback(() => {
292
- if (animName && api.editorState.selectedFrameIds.length > 0) {
293
- api.createAnimation(animName, api.editorState.selectedFrameIds);
294
- setAnimName('');
295
- }
296
- }, [animName, api]);
297
- return (jsxs("div", { className: styles.rightPanel, children: [jsx("div", { className: styles.panelHeader, children: jsx("span", { children: t('gameAsset2dEditor.frames2', 'Frames ({frames})').replace('{frames}', String((_a = activeSheet === null || activeSheet === void 0 ? void 0 : activeSheet.frames.length) !== null && _a !== void 0 ? _a : 0)) }) }), jsx("div", { className: styles.panelContent, style: { maxHeight: 200 }, children: activeSheet === null || activeSheet === void 0 ? void 0 : activeSheet.frames.map((frame) => (jsxs("div", { role: "button", tabIndex: 0, onKeyDown: clickOnActivationKey, className: `${styles.layerItem} ${api.editorState.selectedFrameIds.includes(frame.id) ? styles.selected : ''}`, onClick: () => api.selectFrames([frame.id]), children: [jsx("span", { className: styles.layerName, children: frame.name }), jsxs("span", { className: styles.layerOpacity, children: [frame.width, "\u00D7", frame.height] })] }, frame.id))) }), jsx("div", { className: styles.panelHeader, children: jsx("span", { children: t('gameAsset2dEditor.animations', 'Animations') }) }), jsxs("div", { className: styles.panelContent, children: [api.editorState.selectedFrameIds.length > 0 && (jsxs("div", { className: styles.panelSection, children: [jsx(NiceTextInput, { className: styles.panelInput, placeholder: t('gameAsset2dEditor.animationName', 'Animation name...'), value: animName, onChange: (val) => setAnimName(val) }), jsx(NiceButton, { className: styles.btnPrimary, variant: "primary", onClick: createAnimationFromSelection, style: { marginTop: 8, width: '100%' }, disabled: !animName, children: t('gameAsset2dEditor.createFromFrames', 'Create from {count} frames').replace('{count}', String(api.editorState.selectedFrameIds.length)) })] })), activeSheet === null || activeSheet === void 0 ? void 0 : activeSheet.animations.map((anim) => (jsxs("div", { className: styles.layerItem, children: [jsx("span", { className: styles.layerName, children: anim.name }), jsxs("span", { className: styles.layerOpacity, children: [anim.frameIds.length, "f"] }), jsx(NiceButton, { className: styles.effectRemove, variant: "ghost", size: "sm", onClick: () => api.removeAnimation(anim.id), "aria-label": t('gameAsset2dEditor.removeAnimation', 'Remove animation'), children: jsx(NiceIcon, { name: "close", size: 14 }) })] }, anim.id)))] })] }));
691
+ const { t } = useNiceTranslation();
692
+ const [animName, setAnimName] = useState("");
693
+ const activeSheet = api.project.spriteSheets.find(
694
+ (s) => s.id === api.editorState.activeSpriteSheetId
695
+ );
696
+ const createAnimationFromSelection = useCallback(() => {
697
+ if (animName && api.editorState.selectedFrameIds.length > 0) {
698
+ api.createAnimation(animName, api.editorState.selectedFrameIds);
699
+ setAnimName("");
700
+ }
701
+ }, [animName, api]);
702
+ return /* @__PURE__ */ jsxs("div", { className: styles.rightPanel, children: [
703
+ /* @__PURE__ */ jsx("div", { className: styles.panelHeader, children: /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.frames2", "Frames ({frames})").replace(
704
+ "{frames}",
705
+ String(activeSheet?.frames.length ?? 0)
706
+ ) }) }),
707
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, style: { maxHeight: 200 }, children: activeSheet?.frames.map((frame) => /* @__PURE__ */ jsxs(
708
+ "div",
709
+ {
710
+ role: "button",
711
+ tabIndex: 0,
712
+ onKeyDown: clickOnActivationKey,
713
+ className: `${styles.layerItem} ${api.editorState.selectedFrameIds.includes(frame.id) ? styles.selected : ""}`,
714
+ onClick: () => api.selectFrames([frame.id]),
715
+ children: [
716
+ /* @__PURE__ */ jsx("span", { className: styles.layerName, children: frame.name }),
717
+ /* @__PURE__ */ jsxs("span", { className: styles.layerOpacity, children: [
718
+ frame.width,
719
+ "×",
720
+ frame.height
721
+ ] })
722
+ ]
723
+ },
724
+ frame.id
725
+ )) }),
726
+ /* @__PURE__ */ jsx("div", { className: styles.panelHeader, children: /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.animations", "Animations") }) }),
727
+ /* @__PURE__ */ jsxs("div", { className: styles.panelContent, children: [
728
+ api.editorState.selectedFrameIds.length > 0 && /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
729
+ /* @__PURE__ */ jsx(
730
+ NiceTextInput,
731
+ {
732
+ className: styles.panelInput,
733
+ placeholder: t("gameAsset2dEditor.animationName", "Animation name..."),
734
+ value: animName,
735
+ onChange: (val) => setAnimName(val)
736
+ }
737
+ ),
738
+ /* @__PURE__ */ jsx(
739
+ NiceButton,
740
+ {
741
+ className: styles.btnPrimary,
742
+ variant: "primary",
743
+ onClick: createAnimationFromSelection,
744
+ style: { marginTop: 8, width: "100%" },
745
+ disabled: !animName,
746
+ children: t("gameAsset2dEditor.createFromFrames", "Create from {count} frames").replace(
747
+ "{count}",
748
+ String(api.editorState.selectedFrameIds.length)
749
+ )
750
+ }
751
+ )
752
+ ] }),
753
+ activeSheet?.animations.map((anim) => /* @__PURE__ */ jsxs("div", { className: styles.layerItem, children: [
754
+ /* @__PURE__ */ jsx("span", { className: styles.layerName, children: anim.name }),
755
+ /* @__PURE__ */ jsxs("span", { className: styles.layerOpacity, children: [
756
+ anim.frameIds.length,
757
+ "f"
758
+ ] }),
759
+ /* @__PURE__ */ jsx(
760
+ NiceButton,
761
+ {
762
+ className: styles.effectRemove,
763
+ variant: "ghost",
764
+ size: "sm",
765
+ onClick: () => api.removeAnimation(anim.id),
766
+ "aria-label": t("gameAsset2dEditor.removeAnimation", "Remove animation"),
767
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "close", size: 14 })
768
+ }
769
+ )
770
+ ] }, anim.id))
771
+ ] })
772
+ ] });
298
773
  };
299
- /* ────────────────────────────────────────────────────────────────
300
- ANIMATION STATE MACHINE
301
- ──────────────────────────────────────────────────────────────── */
302
774
  const AnimationStateMachineView = ({ api }) => {
303
- var _a;
304
- const { t } = useNiceTranslation();
305
- const [newStateName, setNewStateName] = useState('');
306
- const activeSM = api.project.stateMachines.find((sm) => sm.id === api.editorState.activeStateMachineId);
307
- // Get all animations from all sprite sheets
308
- const allAnimations = useMemo(() => {
309
- const result = [];
310
- for (const sheet of api.project.spriteSheets) {
311
- for (const anim of sheet.animations) {
312
- result.push({ ...anim, sheetName: sheet.name });
313
- }
775
+ const { t } = useNiceTranslation();
776
+ const [newStateName, setNewStateName] = useState("");
777
+ const activeSM = api.project.stateMachines.find(
778
+ (sm) => sm.id === api.editorState.activeStateMachineId
779
+ );
780
+ const allAnimations = useMemo(() => {
781
+ const result = [];
782
+ for (const sheet of api.project.spriteSheets) {
783
+ for (const anim of sheet.animations) {
784
+ result.push({ ...anim, sheetName: sheet.name });
785
+ }
786
+ }
787
+ return result;
788
+ }, [api.project.spriteSheets]);
789
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flex: 1, overflow: "hidden" }, children: [
790
+ /* @__PURE__ */ jsxs("div", { className: styles.leftPanel, children: [
791
+ /* @__PURE__ */ jsxs("div", { className: styles.panelHeader, children: [
792
+ /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.stateMachines", "State Machines") }),
793
+ /* @__PURE__ */ jsx(
794
+ "button",
795
+ {
796
+ className: styles.menuBtn,
797
+ onClick: () => api.createStateMachine("New State Machine"),
798
+ children: t("gameAsset2dEditor.new2", "+ New")
799
+ }
800
+ )
801
+ ] }),
802
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, style: { maxHeight: 150 }, children: api.project.stateMachines.map((sm) => /* @__PURE__ */ jsxs(
803
+ "div",
804
+ {
805
+ role: "button",
806
+ tabIndex: 0,
807
+ onKeyDown: clickOnActivationKey,
808
+ className: `${styles.layerItem} ${api.editorState.activeStateMachineId === sm.id ? styles.selected : ""}`,
809
+ onClick: () => api.selectStateMachine(sm.id),
810
+ children: [
811
+ /* @__PURE__ */ jsx("span", { className: styles.layerName, children: sm.name }),
812
+ /* @__PURE__ */ jsx("span", { className: styles.layerOpacity, children: t("gameAsset2dEditor.states2", "{states} states").replace(
813
+ "{states}",
814
+ String(sm.states.length)
815
+ ) })
816
+ ]
817
+ },
818
+ sm.id
819
+ )) }),
820
+ activeSM && /* @__PURE__ */ jsxs(Fragment, { children: [
821
+ /* @__PURE__ */ jsx("div", { className: styles.panelHeader, children: /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.states", "States") }) }),
822
+ /* @__PURE__ */ jsxs("div", { className: styles.panelContent, children: [
823
+ /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
824
+ /* @__PURE__ */ jsx(
825
+ NiceTextInput,
826
+ {
827
+ className: styles.panelInput,
828
+ placeholder: t("gameAsset2dEditor.stateName", "State name..."),
829
+ value: newStateName,
830
+ onChange: (val) => setNewStateName(val)
831
+ }
832
+ ),
833
+ allAnimations.length > 0 && newStateName && /* @__PURE__ */ jsx(
834
+ NiceSelect,
835
+ {
836
+ className: styles.panelInput,
837
+ style: { marginTop: 4 },
838
+ onChange: (val) => {
839
+ if (val) {
840
+ api.addState(newStateName, val);
841
+ setNewStateName("");
842
+ }
843
+ },
844
+ options: [
845
+ {
846
+ value: "",
847
+ label: t("gameAsset2dEditor.selectAnimation", "Select animation...")
848
+ },
849
+ ...allAnimations.map((anim) => ({
850
+ value: anim.id,
851
+ label: `${anim.sheetName}: ${anim.name}`
852
+ }))
853
+ ]
854
+ }
855
+ )
856
+ ] }),
857
+ activeSM.states.map((state) => /* @__PURE__ */ jsxs(
858
+ "div",
859
+ {
860
+ role: "button",
861
+ tabIndex: 0,
862
+ onKeyDown: clickOnActivationKey,
863
+ className: `${styles.layerItem} ${api.editorState.selectedStateId === state.id ? styles.selected : ""} ${activeSM.entryStateId === state.id ? styles.entry : ""}`,
864
+ onClick: () => api.selectState(state.id),
865
+ children: [
866
+ /* @__PURE__ */ jsxs("span", { className: styles.layerName, children: [
867
+ activeSM.entryStateId === state.id && /* @__PURE__ */ jsx(NiceIcon, { name: "play", size: 12 }),
868
+ " ",
869
+ state.name
870
+ ] }),
871
+ /* @__PURE__ */ jsx(
872
+ NiceButton,
873
+ {
874
+ className: styles.effectRemove,
875
+ variant: "ghost",
876
+ size: "sm",
877
+ onClick: (e) => {
878
+ e.stopPropagation();
879
+ api.removeState(state.id);
880
+ },
881
+ "aria-label": t("gameAsset2dEditor.removeState", "Remove state"),
882
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "close", size: 14 })
883
+ }
884
+ )
885
+ ]
886
+ },
887
+ state.id
888
+ ))
889
+ ] })
890
+ ] })
891
+ ] }),
892
+ /* @__PURE__ */ jsx("div", { className: styles.stateGraph, children: activeSM ? /* @__PURE__ */ jsxs("svg", { width: "100%", height: "100%", children: [
893
+ /* @__PURE__ */ jsx("defs", { children: /* @__PURE__ */ jsx(
894
+ "marker",
895
+ {
896
+ id: "arrowhead",
897
+ markerWidth: "10",
898
+ markerHeight: "7",
899
+ refX: "9",
900
+ refY: "3.5",
901
+ orient: "auto",
902
+ children: /* @__PURE__ */ jsx("polygon", { points: "0 0, 10 3.5, 0 7", fill: "var(--nice-text-secondary, #4a5a7e)" })
903
+ }
904
+ ) }),
905
+ activeSM.transitions.map((trans) => {
906
+ const from = activeSM.states.find((s) => s.id === trans.fromStateId);
907
+ const to = activeSM.states.find((s) => s.id === trans.toStateId);
908
+ if (!from || !to) {
909
+ return null;
314
910
  }
315
- return result;
316
- }, [api.project.spriteSheets]);
317
- return (jsxs("div", { style: { display: 'flex', flex: 1, overflow: 'hidden' }, children: [jsxs("div", { className: styles.leftPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: t('gameAsset2dEditor.stateMachines', 'State Machines') }), jsx("button", { className: styles.menuBtn, onClick: () => api.createStateMachine('New State Machine'), children: t('gameAsset2dEditor.new2', '+ New') })] }), jsx("div", { className: styles.panelContent, style: { maxHeight: 150 }, children: api.project.stateMachines.map((sm) => (jsxs("div", { role: "button", tabIndex: 0, onKeyDown: clickOnActivationKey, className: `${styles.layerItem} ${api.editorState.activeStateMachineId === sm.id ? styles.selected : ''}`, onClick: () => api.selectStateMachine(sm.id), children: [jsx("span", { className: styles.layerName, children: sm.name }), jsx("span", { className: styles.layerOpacity, children: t('gameAsset2dEditor.states2', '{states} states').replace('{states}', String(sm.states.length)) })] }, sm.id))) }), activeSM && (jsxs(Fragment, { children: [jsx("div", { className: styles.panelHeader, children: jsx("span", { children: t('gameAsset2dEditor.states', 'States') }) }), jsxs("div", { className: styles.panelContent, children: [jsxs("div", { className: styles.panelSection, children: [jsx(NiceTextInput, { className: styles.panelInput, placeholder: t('gameAsset2dEditor.stateName', 'State name...'), value: newStateName, onChange: (val) => setNewStateName(val) }), allAnimations.length > 0 && newStateName && (jsx(NiceSelect, { className: styles.panelInput, style: { marginTop: 4 }, onChange: (val) => {
318
- if (val) {
319
- api.addState(newStateName, val);
320
- setNewStateName('');
321
- }
322
- }, options: [
323
- {
324
- value: '',
325
- label: t('gameAsset2dEditor.selectAnimation', 'Select animation...'),
326
- },
327
- ...allAnimations.map((anim) => ({
328
- value: anim.id,
329
- label: `${anim.sheetName}: ${anim.name}`,
330
- })),
331
- ] }))] }), activeSM.states.map((state) => (jsxs("div", { role: "button", tabIndex: 0, onKeyDown: clickOnActivationKey, className: `${styles.layerItem} ${api.editorState.selectedStateId === state.id ? styles.selected : ''} ${activeSM.entryStateId === state.id ? styles.entry : ''}`, onClick: () => api.selectState(state.id), children: [jsxs("span", { className: styles.layerName, children: [activeSM.entryStateId === state.id && jsx(NiceIcon, { name: "play", size: 12 }), ' ', state.name] }), jsx(NiceButton, { className: styles.effectRemove, variant: "ghost", size: "sm", onClick: (e) => {
332
- e.stopPropagation();
333
- api.removeState(state.id);
334
- }, "aria-label": t('gameAsset2dEditor.removeState', 'Remove state'), children: jsx(NiceIcon, { name: "close", size: 14 }) })] }, state.id)))] })] }))] }), jsx("div", { className: styles.stateGraph, children: activeSM ? (jsxs("svg", { width: "100%", height: "100%", children: [jsx("defs", { children: jsx("marker", { id: "arrowhead", markerWidth: "10", markerHeight: "7", refX: "9", refY: "3.5", orient: "auto", children: jsx("polygon", { points: "0 0, 10 3.5, 0 7", fill: "var(--nice-text-secondary, #4a5a7e)" }) }) }), activeSM.transitions.map((trans) => {
335
- const from = activeSM.states.find((s) => s.id === trans.fromStateId);
336
- const to = activeSM.states.find((s) => s.id === trans.toStateId);
337
- if (!from || !to) {
338
- return null;
339
- }
340
- return (jsx("line", { x1: from.position.x + 60, y1: from.position.y + 20, x2: to.position.x + 60, y2: to.position.y + 20, className: styles.transition, onClick: () => api.removeTransition(trans.id) }, trans.id));
341
- }), activeSM.states.map((state) => {
342
- var _a, _b;
343
- return (jsx("foreignObject", { x: state.position.x, y: state.position.y, width: 120, height: 50, children: jsxs("div", { role: "button", tabIndex: 0, onKeyDown: clickOnActivationKey, className: `${styles.stateNode} ${api.editorState.selectedStateId === state.id ? styles.selected : ''} ${activeSM.entryStateId === state.id ? styles.entry : ''}`, onClick: () => api.selectState(state.id), children: [jsx("div", { className: styles.stateNodeName, children: state.name }), jsx("div", { className: styles.stateNodeAnim, children: (_b = (_a = allAnimations.find((a) => a.id === state.animationId)) === null || _a === void 0 ? void 0 : _a.name) !== null && _b !== void 0 ? _b : t('gameAsset2dEditor.noAnimation', 'No animation') })] }) }, state.id));
344
- })] })) : (jsx("div", { className: styles.emptyState, children: jsx("div", { className: styles.emptyStateText, children: t('gameAsset2dEditor.createAStateMachineTo', 'Create a state machine to design animation flows') }) })) }), jsxs("div", { className: styles.rightPanel, children: [jsxs("div", { className: styles.panelHeader, children: [jsx("span", { children: t('gameAsset2dEditor.parameters', 'Parameters') }), jsx(NiceButton, { className: styles.menuBtn, variant: "ghost", size: "sm", onClick: () => api.addParameter('new_param', 'trigger'), disabled: !activeSM, children: t('gameAsset2dEditor.add', '+ Add') })] }), jsx("div", { className: styles.panelContent, children: activeSM === null || activeSM === void 0 ? void 0 : activeSM.parameters.map((param) => (jsxs("div", { className: styles.layerItem, children: [jsx("span", { className: styles.layerOpacity, children: param.type }), jsx("span", { className: styles.layerName, children: param.name }), jsx(NiceButton, { className: styles.effectRemove, variant: "ghost", size: "sm", onClick: () => api.removeParameter(param.id), "aria-label": t('gameAsset2dEditor.removeParameter', 'Remove parameter'), children: jsx(NiceIcon, { name: "close", size: 14 }) })] }, param.id))) }), jsx("div", { className: styles.panelHeader, children: jsx("span", { children: t('gameAsset2dEditor.transitions', 'Transitions') }) }), jsxs("div", { className: styles.panelContent, children: [activeSM === null || activeSM === void 0 ? void 0 : activeSM.transitions.map((trans) => {
345
- var _a, _b;
346
- const from = activeSM.states.find((s) => s.id === trans.fromStateId);
347
- const to = activeSM.states.find((s) => s.id === trans.toStateId);
348
- return (jsxs("div", { className: styles.layerItem, children: [jsxs("span", { className: styles.layerName, children: [(_a = from === null || from === void 0 ? void 0 : from.name) !== null && _a !== void 0 ? _a : '?', " ", jsx(NiceIcon, { name: "arrow-right", size: 12 }), " ", (_b = to === null || to === void 0 ? void 0 : to.name) !== null && _b !== void 0 ? _b : '?'] }), jsx(NiceButton, { className: styles.effectRemove, variant: "ghost", size: "sm", onClick: () => api.removeTransition(trans.id), "aria-label": t('gameAsset2dEditor.removeTransition', 'Remove transition'), children: jsx(NiceIcon, { name: "close", size: 14 }) })] }, trans.id));
349
- }), api.editorState.selectedStateId && (jsxs("div", { className: styles.panelSection, style: { marginTop: 12 }, children: [jsx("label", { className: styles.panelLabel, children: t('gameAsset2dEditor.addTransitionTo', 'Add transition to:') }), jsx(NiceSelect, { className: styles.panelInput, onChange: (val) => {
350
- if (val && api.editorState.selectedStateId) {
351
- api.addTransition(api.editorState.selectedStateId, val);
352
- }
353
- }, options: [
354
- {
355
- value: '',
356
- label: t('gameAsset2dEditor.selectTargetState', 'Select target state...'),
357
- },
358
- ...((_a = activeSM === null || activeSM === void 0 ? void 0 : activeSM.states.filter((s) => s.id !== api.editorState.selectedStateId).map((s) => ({ value: s.id, label: s.name }))) !== null && _a !== void 0 ? _a : []),
359
- ] })] }))] })] })] }));
911
+ return /* @__PURE__ */ jsx(
912
+ "line",
913
+ {
914
+ x1: from.position.x + 60,
915
+ y1: from.position.y + 20,
916
+ x2: to.position.x + 60,
917
+ y2: to.position.y + 20,
918
+ className: styles.transition,
919
+ onClick: () => api.removeTransition(trans.id)
920
+ },
921
+ trans.id
922
+ );
923
+ }),
924
+ activeSM.states.map((state) => /* @__PURE__ */ jsx(
925
+ "foreignObject",
926
+ {
927
+ x: state.position.x,
928
+ y: state.position.y,
929
+ width: 120,
930
+ height: 50,
931
+ children: /* @__PURE__ */ jsxs(
932
+ "div",
933
+ {
934
+ role: "button",
935
+ tabIndex: 0,
936
+ onKeyDown: clickOnActivationKey,
937
+ className: `${styles.stateNode} ${api.editorState.selectedStateId === state.id ? styles.selected : ""} ${activeSM.entryStateId === state.id ? styles.entry : ""}`,
938
+ onClick: () => api.selectState(state.id),
939
+ children: [
940
+ /* @__PURE__ */ jsx("div", { className: styles.stateNodeName, children: state.name }),
941
+ /* @__PURE__ */ jsx("div", { className: styles.stateNodeAnim, children: allAnimations.find((a) => a.id === state.animationId)?.name ?? t("gameAsset2dEditor.noAnimation", "No animation") })
942
+ ]
943
+ }
944
+ )
945
+ },
946
+ state.id
947
+ ))
948
+ ] }) : /* @__PURE__ */ jsx("div", { className: styles.emptyState, children: /* @__PURE__ */ jsx("div", { className: styles.emptyStateText, children: t(
949
+ "gameAsset2dEditor.createAStateMachineTo",
950
+ "Create a state machine to design animation flows"
951
+ ) }) }) }),
952
+ /* @__PURE__ */ jsxs("div", { className: styles.rightPanel, children: [
953
+ /* @__PURE__ */ jsxs("div", { className: styles.panelHeader, children: [
954
+ /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.parameters", "Parameters") }),
955
+ /* @__PURE__ */ jsx(
956
+ NiceButton,
957
+ {
958
+ className: styles.menuBtn,
959
+ variant: "ghost",
960
+ size: "sm",
961
+ onClick: () => api.addParameter("new_param", "trigger"),
962
+ disabled: !activeSM,
963
+ children: t("gameAsset2dEditor.add", "+ Add")
964
+ }
965
+ )
966
+ ] }),
967
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: activeSM?.parameters.map((param) => /* @__PURE__ */ jsxs("div", { className: styles.layerItem, children: [
968
+ /* @__PURE__ */ jsx("span", { className: styles.layerOpacity, children: param.type }),
969
+ /* @__PURE__ */ jsx("span", { className: styles.layerName, children: param.name }),
970
+ /* @__PURE__ */ jsx(
971
+ NiceButton,
972
+ {
973
+ className: styles.effectRemove,
974
+ variant: "ghost",
975
+ size: "sm",
976
+ onClick: () => api.removeParameter(param.id),
977
+ "aria-label": t("gameAsset2dEditor.removeParameter", "Remove parameter"),
978
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "close", size: 14 })
979
+ }
980
+ )
981
+ ] }, param.id)) }),
982
+ /* @__PURE__ */ jsx("div", { className: styles.panelHeader, children: /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.transitions", "Transitions") }) }),
983
+ /* @__PURE__ */ jsxs("div", { className: styles.panelContent, children: [
984
+ activeSM?.transitions.map((trans) => {
985
+ const from = activeSM.states.find((s) => s.id === trans.fromStateId);
986
+ const to = activeSM.states.find((s) => s.id === trans.toStateId);
987
+ return /* @__PURE__ */ jsxs("div", { className: styles.layerItem, children: [
988
+ /* @__PURE__ */ jsxs("span", { className: styles.layerName, children: [
989
+ from?.name ?? "?",
990
+ " ",
991
+ /* @__PURE__ */ jsx(NiceIcon, { name: "arrow-right", size: 12 }),
992
+ " ",
993
+ to?.name ?? "?"
994
+ ] }),
995
+ /* @__PURE__ */ jsx(
996
+ NiceButton,
997
+ {
998
+ className: styles.effectRemove,
999
+ variant: "ghost",
1000
+ size: "sm",
1001
+ onClick: () => api.removeTransition(trans.id),
1002
+ "aria-label": t("gameAsset2dEditor.removeTransition", "Remove transition"),
1003
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "close", size: 14 })
1004
+ }
1005
+ )
1006
+ ] }, trans.id);
1007
+ }),
1008
+ api.editorState.selectedStateId && /* @__PURE__ */ jsxs("div", { className: styles.panelSection, style: { marginTop: 12 }, children: [
1009
+ /* @__PURE__ */ jsx("label", { className: styles.panelLabel, children: t("gameAsset2dEditor.addTransitionTo", "Add transition to:") }),
1010
+ /* @__PURE__ */ jsx(
1011
+ NiceSelect,
1012
+ {
1013
+ className: styles.panelInput,
1014
+ onChange: (val) => {
1015
+ if (val && api.editorState.selectedStateId) {
1016
+ api.addTransition(api.editorState.selectedStateId, val);
1017
+ }
1018
+ },
1019
+ options: [
1020
+ {
1021
+ value: "",
1022
+ label: t("gameAsset2dEditor.selectTargetState", "Select target state...")
1023
+ },
1024
+ ...activeSM?.states.filter((s) => s.id !== api.editorState.selectedStateId).map((s) => ({ value: s.id, label: s.name })) ?? []
1025
+ ]
1026
+ }
1027
+ )
1028
+ ] })
1029
+ ] })
1030
+ ] })
1031
+ ] });
360
1032
  };
361
- /* ────────────────────────────────────────────────────────────────
362
- EFFECTS PANEL
363
- ──────────────────────────────────────────────────────────────── */
364
1033
  const EffectsView = ({ api }) => {
365
- const { t } = useNiceTranslation();
366
- const [_currentEffects, _setCurrentEffects] = useState([]);
367
- const [presetName, setPresetName] = useState('');
368
- const effectTypes = [
369
- { type: 'brightness', label: t('gameAsset2dEditor.brightness', 'Brightness') },
370
- { type: 'contrast', label: t('gameAsset2dEditor.contrast', 'Contrast') },
371
- { type: 'saturation', label: t('gameAsset2dEditor.saturation', 'Saturation') },
372
- { type: 'hue-shift', label: t('gameAsset2dEditor.hueShift', 'Hue Shift') },
373
- { type: 'invert', label: t('gameAsset2dEditor.invert', 'Invert') },
374
- { type: 'pixelate', label: t('gameAsset2dEditor.pixelate', 'Pixelate') },
375
- { type: 'outline', label: t('gameAsset2dEditor.outline', 'Outline') },
376
- { type: 'tint', label: t('gameAsset2dEditor.tint', 'Tint') },
377
- { type: 'blur', label: t('gameAsset2dEditor.blur', 'Blur') },
378
- { type: 'glow', label: t('gameAsset2dEditor.glow', 'Glow') },
379
- { type: 'shadow', label: t('gameAsset2dEditor.shadow', 'Shadow') },
380
- { type: 'palette-swap', label: t('gameAsset2dEditor.paletteSwap', 'Palette Swap') },
381
- ];
382
- return (jsxs("div", { style: { display: 'flex', flex: 1, overflow: 'hidden' }, children: [jsxs("div", { className: styles.leftPanel, children: [jsx("div", { className: styles.panelHeader, children: jsx("span", { children: t('gameAsset2dEditor.addEffect', 'Add Effect') }) }), jsx("div", { className: styles.panelContent, children: effectTypes.map(({ type, label }) => (jsx(NiceButton, { className: styles.layerItem, variant: "ghost", onClick: () => api.addEffect(type), style: { cursor: 'pointer', border: 'none', textAlign: 'left' }, children: jsx("span", { className: styles.layerName, children: label }) }, type))) }), jsx("div", { className: styles.panelHeader, children: jsx("span", { children: t('gameAsset2dEditor.presets', 'Presets') }) }), jsxs("div", { className: styles.panelContent, children: [jsxs("div", { className: styles.panelSection, children: [jsx(NiceTextInput, { className: styles.panelInput, placeholder: t('gameAsset2dEditor.presetName', 'Preset name...'), value: presetName, onChange: (val) => setPresetName(val) }), jsx(NiceButton, { className: styles.btnSecondary, variant: "ghost", style: { marginTop: 4, width: '100%' }, onClick: () => {
383
- if (presetName) {
384
- api.saveEffectPreset(presetName);
385
- setPresetName('');
386
- }
387
- }, disabled: !presetName, children: t('gameAsset2dEditor.saveCurrentEffects', 'Save Current Effects') })] }), api.project.effectPresets.map((preset) => (jsxs("div", { role: "button", tabIndex: 0, onKeyDown: clickOnActivationKey, className: styles.layerItem, onClick: () => api.loadEffectPreset(preset.id), style: { cursor: 'pointer' }, children: [jsx("span", { className: styles.layerName, children: preset.name }), jsx("span", { className: styles.layerOpacity, children: t('gameAsset2dEditor.effects', '{effects} effects').replace('{effects}', String(preset.effects.length)) })] }, preset.id)))] })] }), jsx("div", { className: styles.viewport, children: jsx("div", { className: styles.emptyState, children: jsx("div", { className: styles.emptyStateText, children: t('gameAsset2dEditor.selectASpriteOrTile', 'Select a sprite or tile to preview effects') }) }) }), jsxs("div", { className: styles.rightPanel, children: [jsx("div", { className: styles.panelHeader, children: jsx("span", { children: t('gameAsset2dEditor.activeEffects', 'Active Effects') }) }), jsx("div", { className: styles.panelContent, children: jsx("div", { className: styles.effectsList, children: jsx("div", { className: styles.emptyState, children: jsx("div", { className: styles.emptyStateText, children: t('gameAsset2dEditor.addEffectsFromTheLeft', 'Add effects from the left panel') }) }) }) })] })] }));
1034
+ const { t } = useNiceTranslation();
1035
+ const [_currentEffects, _setCurrentEffects] = useState([]);
1036
+ const [presetName, setPresetName] = useState("");
1037
+ const effectTypes = [
1038
+ { type: "brightness", label: t("gameAsset2dEditor.brightness", "Brightness") },
1039
+ { type: "contrast", label: t("gameAsset2dEditor.contrast", "Contrast") },
1040
+ { type: "saturation", label: t("gameAsset2dEditor.saturation", "Saturation") },
1041
+ { type: "hue-shift", label: t("gameAsset2dEditor.hueShift", "Hue Shift") },
1042
+ { type: "invert", label: t("gameAsset2dEditor.invert", "Invert") },
1043
+ { type: "pixelate", label: t("gameAsset2dEditor.pixelate", "Pixelate") },
1044
+ { type: "outline", label: t("gameAsset2dEditor.outline", "Outline") },
1045
+ { type: "tint", label: t("gameAsset2dEditor.tint", "Tint") },
1046
+ { type: "blur", label: t("gameAsset2dEditor.blur", "Blur") },
1047
+ { type: "glow", label: t("gameAsset2dEditor.glow", "Glow") },
1048
+ { type: "shadow", label: t("gameAsset2dEditor.shadow", "Shadow") },
1049
+ { type: "palette-swap", label: t("gameAsset2dEditor.paletteSwap", "Palette Swap") }
1050
+ ];
1051
+ return /* @__PURE__ */ jsxs("div", { style: { display: "flex", flex: 1, overflow: "hidden" }, children: [
1052
+ /* @__PURE__ */ jsxs("div", { className: styles.leftPanel, children: [
1053
+ /* @__PURE__ */ jsx("div", { className: styles.panelHeader, children: /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.addEffect", "Add Effect") }) }),
1054
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: effectTypes.map(({ type, label }) => /* @__PURE__ */ jsx(
1055
+ NiceButton,
1056
+ {
1057
+ className: styles.layerItem,
1058
+ variant: "ghost",
1059
+ onClick: () => api.addEffect(type),
1060
+ style: { cursor: "pointer", border: "none", textAlign: "left" },
1061
+ children: /* @__PURE__ */ jsx("span", { className: styles.layerName, children: label })
1062
+ },
1063
+ type
1064
+ )) }),
1065
+ /* @__PURE__ */ jsx("div", { className: styles.panelHeader, children: /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.presets", "Presets") }) }),
1066
+ /* @__PURE__ */ jsxs("div", { className: styles.panelContent, children: [
1067
+ /* @__PURE__ */ jsxs("div", { className: styles.panelSection, children: [
1068
+ /* @__PURE__ */ jsx(
1069
+ NiceTextInput,
1070
+ {
1071
+ className: styles.panelInput,
1072
+ placeholder: t("gameAsset2dEditor.presetName", "Preset name..."),
1073
+ value: presetName,
1074
+ onChange: (val) => setPresetName(val)
1075
+ }
1076
+ ),
1077
+ /* @__PURE__ */ jsx(
1078
+ NiceButton,
1079
+ {
1080
+ className: styles.btnSecondary,
1081
+ variant: "ghost",
1082
+ style: { marginTop: 4, width: "100%" },
1083
+ onClick: () => {
1084
+ if (presetName) {
1085
+ api.saveEffectPreset(presetName);
1086
+ setPresetName("");
1087
+ }
1088
+ },
1089
+ disabled: !presetName,
1090
+ children: t("gameAsset2dEditor.saveCurrentEffects", "Save Current Effects")
1091
+ }
1092
+ )
1093
+ ] }),
1094
+ api.project.effectPresets.map((preset) => /* @__PURE__ */ jsxs(
1095
+ "div",
1096
+ {
1097
+ role: "button",
1098
+ tabIndex: 0,
1099
+ onKeyDown: clickOnActivationKey,
1100
+ className: styles.layerItem,
1101
+ onClick: () => api.loadEffectPreset(preset.id),
1102
+ style: { cursor: "pointer" },
1103
+ children: [
1104
+ /* @__PURE__ */ jsx("span", { className: styles.layerName, children: preset.name }),
1105
+ /* @__PURE__ */ jsx("span", { className: styles.layerOpacity, children: t("gameAsset2dEditor.effects", "{effects} effects").replace(
1106
+ "{effects}",
1107
+ String(preset.effects.length)
1108
+ ) })
1109
+ ]
1110
+ },
1111
+ preset.id
1112
+ ))
1113
+ ] })
1114
+ ] }),
1115
+ /* @__PURE__ */ jsx("div", { className: styles.viewport, children: /* @__PURE__ */ jsx("div", { className: styles.emptyState, children: /* @__PURE__ */ jsx("div", { className: styles.emptyStateText, children: t(
1116
+ "gameAsset2dEditor.selectASpriteOrTile",
1117
+ "Select a sprite or tile to preview effects"
1118
+ ) }) }) }),
1119
+ /* @__PURE__ */ jsxs("div", { className: styles.rightPanel, children: [
1120
+ /* @__PURE__ */ jsx("div", { className: styles.panelHeader, children: /* @__PURE__ */ jsx("span", { children: t("gameAsset2dEditor.activeEffects", "Active Effects") }) }),
1121
+ /* @__PURE__ */ jsx("div", { className: styles.panelContent, children: /* @__PURE__ */ jsx("div", { className: styles.effectsList, children: /* @__PURE__ */ jsx("div", { className: styles.emptyState, children: /* @__PURE__ */ jsx("div", { className: styles.emptyStateText, children: t("gameAsset2dEditor.addEffectsFromTheLeft", "Add effects from the left panel") }) }) }) })
1122
+ ] })
1123
+ ] });
388
1124
  };
389
- /* ────────────────────────────────────────────────────────────────
390
- STATUS BAR
391
- ──────────────────────────────────────────────────────────────── */
392
1125
  const StatusBar = ({ api }) => {
393
- const { t } = useNiceTranslation();
394
- const activeMap = api.project.tileMaps.find((m) => m.id === api.editorState.activeTileMapId);
395
- const activeSheet = api.project.spriteSheets.find((s) => s.id === api.editorState.activeSpriteSheetId);
396
- return (jsxs("div", { className: styles.statusBar, children: [jsx("span", { className: styles.statusItem, children: t('gameAsset2dEditor.mode', 'Mode: {mode}').replace('{mode}', String(api.editorState.mode)) }), api.editorState.mode === 'tilemap' && activeMap && (jsxs(Fragment, { children: [jsx("span", { className: styles.statusItem, children: t('gameAsset2dEditor.map', 'Map: {name} ({width}×{height})')
397
- .replace('{name}', String(activeMap.name))
398
- .replace('{width}', String(activeMap.width))
399
- .replace('{height}', String(activeMap.height)) }), jsx("span", { className: styles.statusItem, children: t('gameAsset2dEditor.tilePx', 'Tile: {tileWidth}×{tileHeight}px')
400
- .replace('{tileWidth}', String(activeMap.tileWidth))
401
- .replace('{tileHeight}', String(activeMap.tileHeight)) })] })), api.editorState.mode === 'spritesheet' && activeSheet && (jsxs(Fragment, { children: [jsx("span", { className: styles.statusItem, children: t('gameAsset2dEditor.sheet', 'Sheet: {name}').replace('{name}', String(activeSheet.name)) }), jsx("span", { className: styles.statusItem, children: t('gameAsset2dEditor.selectedFrames', 'Selected: {selectedFrameIds} frames').replace('{selectedFrameIds}', String(api.editorState.selectedFrameIds.length)) })] })), jsxs("div", { className: styles.zoomControl, children: [jsx(NiceButton, { className: styles.zoomBtn, variant: "ghost", size: "sm", onClick: () => api.setZoom(api.editorState.zoom - 0.25), children: "\u2212" }), jsxs("span", { children: [Math.round(api.editorState.zoom * 100), "%"] }), jsx(NiceButton, { className: styles.zoomBtn, variant: "ghost", size: "sm", onClick: () => api.setZoom(api.editorState.zoom + 0.25), children: "+" }), jsx(NiceButton, { className: styles.zoomBtn, variant: "ghost", size: "sm", onClick: () => api.setZoom(1), children: "\u2299" })] })] }));
402
- };
403
- /* ────────────────────────────────────────────────────────────────
404
- MAIN COMPONENT
405
- ──────────────────────────────────────────────────────────────── */
406
- const NiceGameAsset2dEditor = ({ className, onSave, onExport, }) => {
407
- const api = useGameAssetEditor({ onSave, onExport });
408
- const renderModeContent = () => {
409
- switch (api.editorState.mode) {
410
- case 'tilemap':
411
- return (jsxs(Fragment, { children: [jsx(TileMapToolbar, { api: api }), jsxs("div", { className: styles.mainArea, children: [jsx(TilePalette, { api: api }), jsx("div", { className: styles.centerArea, children: jsx(TileMapCanvas, { api: api }) }), jsx(LayersPanel, { api: api })] })] }));
412
- case 'spritesheet':
413
- return (jsxs(Fragment, { children: [jsx(SpriteSheetToolbar, { api: api }), jsxs("div", { className: styles.mainArea, children: [jsx(SpriteSheetsList, { api: api }), jsx("div", { className: styles.centerArea, children: jsx(SpriteSheetCanvas, { api: api }) }), jsx(FramesPanel, { api: api })] })] }));
414
- case 'animation':
415
- return (jsx("div", { className: styles.mainArea, children: jsx(AnimationStateMachineView, { api: api }) }));
416
- case 'effects':
417
- return (jsx("div", { className: styles.mainArea, children: jsx(EffectsView, { api: api }) }));
418
- default:
419
- return null;
1126
+ const { t } = useNiceTranslation();
1127
+ const activeMap = api.project.tileMaps.find((m) => m.id === api.editorState.activeTileMapId);
1128
+ const activeSheet = api.project.spriteSheets.find(
1129
+ (s) => s.id === api.editorState.activeSpriteSheetId
1130
+ );
1131
+ return /* @__PURE__ */ jsxs("div", { className: styles.statusBar, children: [
1132
+ /* @__PURE__ */ jsx("span", { className: styles.statusItem, children: t("gameAsset2dEditor.mode", "Mode: {mode}").replace(
1133
+ "{mode}",
1134
+ String(api.editorState.mode)
1135
+ ) }),
1136
+ api.editorState.mode === "tilemap" && activeMap && /* @__PURE__ */ jsxs(Fragment, { children: [
1137
+ /* @__PURE__ */ jsx("span", { className: styles.statusItem, children: t("gameAsset2dEditor.map", "Map: {name} ({width}×{height})").replace("{name}", String(activeMap.name)).replace("{width}", String(activeMap.width)).replace("{height}", String(activeMap.height)) }),
1138
+ /* @__PURE__ */ jsx("span", { className: styles.statusItem, children: t("gameAsset2dEditor.tilePx", "Tile: {tileWidth}×{tileHeight}px").replace("{tileWidth}", String(activeMap.tileWidth)).replace("{tileHeight}", String(activeMap.tileHeight)) })
1139
+ ] }),
1140
+ api.editorState.mode === "spritesheet" && activeSheet && /* @__PURE__ */ jsxs(Fragment, { children: [
1141
+ /* @__PURE__ */ jsx("span", { className: styles.statusItem, children: t("gameAsset2dEditor.sheet", "Sheet: {name}").replace(
1142
+ "{name}",
1143
+ String(activeSheet.name)
1144
+ ) }),
1145
+ /* @__PURE__ */ jsx("span", { className: styles.statusItem, children: t("gameAsset2dEditor.selectedFrames", "Selected: {selectedFrameIds} frames").replace(
1146
+ "{selectedFrameIds}",
1147
+ String(api.editorState.selectedFrameIds.length)
1148
+ ) })
1149
+ ] }),
1150
+ /* @__PURE__ */ jsxs("div", { className: styles.zoomControl, children: [
1151
+ /* @__PURE__ */ jsx(
1152
+ NiceButton,
1153
+ {
1154
+ className: styles.zoomBtn,
1155
+ variant: "ghost",
1156
+ size: "sm",
1157
+ onClick: () => api.setZoom(api.editorState.zoom - 0.25),
1158
+ children: "−"
1159
+ }
1160
+ ),
1161
+ /* @__PURE__ */ jsxs("span", { children: [
1162
+ Math.round(api.editorState.zoom * 100),
1163
+ "%"
1164
+ ] }),
1165
+ /* @__PURE__ */ jsx(
1166
+ NiceButton,
1167
+ {
1168
+ className: styles.zoomBtn,
1169
+ variant: "ghost",
1170
+ size: "sm",
1171
+ onClick: () => api.setZoom(api.editorState.zoom + 0.25),
1172
+ children: "+"
420
1173
  }
421
- };
422
- return (jsx(NiceAppShell, { className: `${styles.root} ${className || ''}`, flush: true, menubar: jsx(MenuBar, { api: api }), toolbar: jsx(ModeTabs, { api: api }), statusBar: jsx(StatusBar, { api: api }), children: jsx("div", { style: { display: 'flex', flexDirection: 'column', minHeight: 0, height: '100%' }, children: renderModeContent() }) }));
1174
+ ),
1175
+ /* @__PURE__ */ jsx(
1176
+ NiceButton,
1177
+ {
1178
+ className: styles.zoomBtn,
1179
+ variant: "ghost",
1180
+ size: "sm",
1181
+ onClick: () => api.setZoom(1),
1182
+ children: "⊙"
1183
+ }
1184
+ )
1185
+ ] })
1186
+ ] });
1187
+ };
1188
+ const NiceGameAsset2dEditor = ({
1189
+ className,
1190
+ onSave,
1191
+ onExport
1192
+ }) => {
1193
+ const api = useGameAssetEditor({ onSave, onExport });
1194
+ const renderModeContent = () => {
1195
+ switch (api.editorState.mode) {
1196
+ case "tilemap":
1197
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1198
+ /* @__PURE__ */ jsx(TileMapToolbar, { api }),
1199
+ /* @__PURE__ */ jsxs("div", { className: styles.mainArea, children: [
1200
+ /* @__PURE__ */ jsx(TilePalette, { api }),
1201
+ /* @__PURE__ */ jsx("div", { className: styles.centerArea, children: /* @__PURE__ */ jsx(TileMapCanvas, { api }) }),
1202
+ /* @__PURE__ */ jsx(LayersPanel, { api })
1203
+ ] })
1204
+ ] });
1205
+ case "spritesheet":
1206
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
1207
+ /* @__PURE__ */ jsx(SpriteSheetToolbar, { api }),
1208
+ /* @__PURE__ */ jsxs("div", { className: styles.mainArea, children: [
1209
+ /* @__PURE__ */ jsx(SpriteSheetsList, { api }),
1210
+ /* @__PURE__ */ jsx("div", { className: styles.centerArea, children: /* @__PURE__ */ jsx(SpriteSheetCanvas, { api }) }),
1211
+ /* @__PURE__ */ jsx(FramesPanel, { api })
1212
+ ] })
1213
+ ] });
1214
+ case "animation":
1215
+ return /* @__PURE__ */ jsx("div", { className: styles.mainArea, children: /* @__PURE__ */ jsx(AnimationStateMachineView, { api }) });
1216
+ case "effects":
1217
+ return /* @__PURE__ */ jsx("div", { className: styles.mainArea, children: /* @__PURE__ */ jsx(EffectsView, { api }) });
1218
+ default:
1219
+ return null;
1220
+ }
1221
+ };
1222
+ return /* @__PURE__ */ jsx(
1223
+ NiceAppShell,
1224
+ {
1225
+ className: `${styles.root} ${className || ""}`,
1226
+ flush: true,
1227
+ menubar: /* @__PURE__ */ jsx(MenuBar, { api }),
1228
+ toolbar: /* @__PURE__ */ jsx(ModeTabs, { api }),
1229
+ statusBar: /* @__PURE__ */ jsx(StatusBar, { api }),
1230
+ children: /* @__PURE__ */ jsx("div", { style: { display: "flex", flexDirection: "column", minHeight: 0, height: "100%" }, children: renderModeContent() })
1231
+ }
1232
+ );
1233
+ };
1234
+ export {
1235
+ NiceGameAsset2dEditor
423
1236
  };
424
-
425
- export { NiceGameAsset2dEditor };