@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,226 +1,1090 @@
1
- import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import { NiceIcon, clickOnActivationKey } from '@nice2dev/ui-core';
3
- import React from 'react';
4
- import { RangeInput, Button, TextArea } from '../core/LocalUI.js';
5
- import FilterThumb from './FilterThumb.js';
6
- import cs from './PhotoEditor.module.css.js';
7
- import { TAB_DEFS, ADJUSTMENT_SLIDERS, ASPECT_RATIOS } from './photoEditorTypes.js';
8
- import { DEFAULT_ADJUSTMENTS } from './photoFilters.js';
9
- import { EMOJI_CATALOG, SHAPE_CATALOG, FRAME_CATALOG } from './photoOverlays.js';
10
-
11
- // ── Overlay Toolbar (extracted from the original inline function) ──
1
+ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
2
+ import { NiceIcon, clickOnActivationKey } from "@nice2dev/ui-core";
3
+ import React from "react";
4
+ import { RangeInput, Button, TextArea } from "../core/LocalUI.js";
5
+ import FilterThumb from "./FilterThumb.js";
6
+ import cs from "./PhotoEditor.module.css.js";
7
+ import { TAB_DEFS, ADJUSTMENT_SLIDERS, ASPECT_RATIOS } from "./photoEditorTypes.js";
8
+ import { DEFAULT_ADJUSTMENTS } from "./photoFilters.js";
9
+ import { EMOJI_CATALOG, SHAPE_CATALOG, FRAME_CATALOG } from "./photoOverlays.js";
12
10
  const OverlayToolbar = ({ api }) => {
13
- var _a, _b, _c, _d, _e;
14
- const { selectedOverlay, accent, t, updateOverlay, pushHistory, moveOverlayBack, moveOverlayForward, deleteOverlay, } = api;
15
- if (!selectedOverlay) {
16
- return null;
17
- }
18
- return (jsxs("div", { className: cs.overlayToolbar, children: [jsxs("span", { className: cs.overlayToolbarLabel, children: [jsx(NiceIcon, { name: "box", size: 14, style: { marginRight: 4 } }), t('photoEditor.layer', 'Layer')] }), jsx(RangeInput, { min: 0, max: 100, value: ((_a = selectedOverlay.opacity) !== null && _a !== void 0 ? _a : 1) * 100, onChange: (e) => updateOverlay(selectedOverlay.id, { opacity: Number(e.target.value) / 100 }), onMouseUp: pushHistory, accentColor: accent, style: { maxWidth: 70 }, title: t('photoEditor.opacity', 'Opacity') }), jsxs("span", { className: cs.sliderValueSm, style: { color: accent }, children: [Math.round(((_b = selectedOverlay.opacity) !== null && _b !== void 0 ? _b : 1) * 100), "%"] }), selectedOverlay.type !== 'drawing' && selectedOverlay.type !== 'frame' && (jsxs(Fragment, { children: [jsx(RangeInput, { min: 20, max: 500, value: ((_c = selectedOverlay.scaleX) !== null && _c !== void 0 ? _c : 1) * 100, onChange: (e) => {
19
- const s = Number(e.target.value) / 100;
20
- updateOverlay(selectedOverlay.id, { scaleX: s, scaleY: s });
21
- }, onMouseUp: pushHistory, accentColor: accent, style: { maxWidth: 60 }, title: t('photoEditor.scale', 'Scale') }), jsxs("span", { className: cs.sliderValueSm, style: { color: 'var(--nice-text-muted, #aaa)' }, children: [Math.round(((_d = selectedOverlay.scaleX) !== null && _d !== void 0 ? _d : 1) * 100), "%"] })] })), selectedOverlay.type !== 'drawing' && selectedOverlay.type !== 'frame' && (jsx(RangeInput, { min: -180, max: 180, value: (_e = selectedOverlay.rotation) !== null && _e !== void 0 ? _e : 0, onChange: (e) => updateOverlay(selectedOverlay.id, { rotation: Number(e.target.value) }), onMouseUp: pushHistory, accentColor: accent, style: { maxWidth: 60 }, title: t('photoEditor.rotation', 'Rotation') })), jsx(Button, { variant: "dark", size: "sm", onClick: () => moveOverlayBack(selectedOverlay.id), title: t('photoEditor.moveBack', 'Move back'), className: cs.overlayToolbarBtn, children: jsx(NiceIcon, { name: "arrow-down", size: 14 }) }), jsx(Button, { variant: "dark", size: "sm", onClick: () => moveOverlayForward(selectedOverlay.id), title: t('photoEditor.moveForward', 'Move forward'), className: cs.overlayToolbarBtn, children: jsx(NiceIcon, { name: "arrow-up", size: 14 }) }), jsx(Button, { variant: "outline-danger", size: "sm", onClick: () => deleteOverlay(selectedOverlay.id), className: cs.overlayToolbarBtn, title: t('photoEditor.deleteLayer', 'Delete layer'), children: jsx(NiceIcon, { name: "trash", size: 14 }) })] }));
11
+ const {
12
+ selectedOverlay,
13
+ accent,
14
+ t,
15
+ updateOverlay,
16
+ pushHistory,
17
+ moveOverlayBack,
18
+ moveOverlayForward,
19
+ deleteOverlay
20
+ } = api;
21
+ if (!selectedOverlay) {
22
+ return null;
23
+ }
24
+ return /* @__PURE__ */ jsxs("div", { className: cs.overlayToolbar, children: [
25
+ /* @__PURE__ */ jsxs("span", { className: cs.overlayToolbarLabel, children: [
26
+ /* @__PURE__ */ jsx(NiceIcon, { name: "box", size: 14, style: { marginRight: 4 } }),
27
+ t("photoEditor.layer", "Layer")
28
+ ] }),
29
+ /* @__PURE__ */ jsx(
30
+ RangeInput,
31
+ {
32
+ min: 0,
33
+ max: 100,
34
+ value: (selectedOverlay.opacity ?? 1) * 100,
35
+ onChange: (e) => updateOverlay(selectedOverlay.id, { opacity: Number(e.target.value) / 100 }),
36
+ onMouseUp: pushHistory,
37
+ accentColor: accent,
38
+ style: { maxWidth: 70 },
39
+ title: t("photoEditor.opacity", "Opacity")
40
+ }
41
+ ),
42
+ /* @__PURE__ */ jsxs("span", { className: cs.sliderValueSm, style: { color: accent }, children: [
43
+ Math.round((selectedOverlay.opacity ?? 1) * 100),
44
+ "%"
45
+ ] }),
46
+ selectedOverlay.type !== "drawing" && selectedOverlay.type !== "frame" && /* @__PURE__ */ jsxs(Fragment, { children: [
47
+ /* @__PURE__ */ jsx(
48
+ RangeInput,
49
+ {
50
+ min: 20,
51
+ max: 500,
52
+ value: (selectedOverlay.scaleX ?? 1) * 100,
53
+ onChange: (e) => {
54
+ const s = Number(e.target.value) / 100;
55
+ updateOverlay(selectedOverlay.id, { scaleX: s, scaleY: s });
56
+ },
57
+ onMouseUp: pushHistory,
58
+ accentColor: accent,
59
+ style: { maxWidth: 60 },
60
+ title: t("photoEditor.scale", "Scale")
61
+ }
62
+ ),
63
+ /* @__PURE__ */ jsxs("span", { className: cs.sliderValueSm, style: { color: "var(--nice-text-muted, #aaa)" }, children: [
64
+ Math.round((selectedOverlay.scaleX ?? 1) * 100),
65
+ "%"
66
+ ] })
67
+ ] }),
68
+ selectedOverlay.type !== "drawing" && selectedOverlay.type !== "frame" && /* @__PURE__ */ jsx(
69
+ RangeInput,
70
+ {
71
+ min: -180,
72
+ max: 180,
73
+ value: selectedOverlay.rotation ?? 0,
74
+ onChange: (e) => updateOverlay(selectedOverlay.id, { rotation: Number(e.target.value) }),
75
+ onMouseUp: pushHistory,
76
+ accentColor: accent,
77
+ style: { maxWidth: 60 },
78
+ title: t("photoEditor.rotation", "Rotation")
79
+ }
80
+ ),
81
+ /* @__PURE__ */ jsx(
82
+ Button,
83
+ {
84
+ variant: "dark",
85
+ size: "sm",
86
+ onClick: () => moveOverlayBack(selectedOverlay.id),
87
+ title: t("photoEditor.moveBack", "Move back"),
88
+ className: cs.overlayToolbarBtn,
89
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "arrow-down", size: 14 })
90
+ }
91
+ ),
92
+ /* @__PURE__ */ jsx(
93
+ Button,
94
+ {
95
+ variant: "dark",
96
+ size: "sm",
97
+ onClick: () => moveOverlayForward(selectedOverlay.id),
98
+ title: t("photoEditor.moveForward", "Move forward"),
99
+ className: cs.overlayToolbarBtn,
100
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "arrow-up", size: 14 })
101
+ }
102
+ ),
103
+ /* @__PURE__ */ jsx(
104
+ Button,
105
+ {
106
+ variant: "outline-danger",
107
+ size: "sm",
108
+ onClick: () => deleteOverlay(selectedOverlay.id),
109
+ className: cs.overlayToolbarBtn,
110
+ title: t("photoEditor.deleteLayer", "Delete layer"),
111
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "trash", size: 14 })
112
+ }
113
+ )
114
+ ] });
22
115
  };
23
- // ── Left Panel ──
24
116
  const PhotoEditorLeftPanel = /* @__PURE__ */ Object.assign(
25
- /* @__PURE__ */ React.memo(({ api }) => {
26
- var _a;
27
- const { t, accent, accentDim, activeTab, setActiveTab, setCropMode,
28
- // Filters
29
- selectedFilter, setSelectedFilter, filterIntensity, setFilterIntensity, filterCategory, setFilterCategory, filteredList, generateThumbnail, pushHistory,
30
- // Adjustments
31
- adjustments, handleAdjChange, handleAdjChangeEnd,
32
- // Crop
33
- image, cropRect, setCropRect, cropAspect, setCropAspect,
34
- // Transform
35
- rotation, setRotation, flipH, setFlipH, flipV, setFlipV, zoom, setZoom,
36
- // Overlays
37
- overlays, setOverlays, selectedOverlayId, setSelectedOverlayId, addOverlay, deleteOverlay, mainCanvasRef,
38
- // Stickers / Emoji
39
- emojiCat, setEmojiCat,
40
- // Text
41
- textDraft, setTextDraft, textColor, setTextColor, textBold, setTextBold, textItalic, setTextItalic, textOutline, setTextOutline, textFontSize, setTextFontSize, textBg, setTextBg, selectedOverlay, updateOverlay,
42
- // Shapes
43
- shapeStroke, setShapeStroke, shapeFill, setShapeFill, shapeStrokeW, setShapeStrokeW,
44
- // Frame
45
- frameColor, setFrameColor, frameThick, setFrameThick,
46
- // Drawing
47
- drawColor, setDrawColor, drawWidth, setDrawWidth, drawingOverlayRef,
48
- // Creators
49
- createEmojiOverlay, createTextOverlay, createShapeOverlay, createFrameOverlay, } = api;
50
- return (jsxs("div", { className: cs.leftPanel, children: [jsx("div", { className: cs.tabBar, children: TAB_DEFS.map((tab) => (jsxs("button", { onClick: () => {
51
- setActiveTab(tab.key);
52
- if (tab.key === 'crop') {
53
- setCropMode(true);
117
+ /* @__PURE__ */ React.memo(({ api }) => {
118
+ const {
119
+ t,
120
+ accent,
121
+ accentDim,
122
+ activeTab,
123
+ setActiveTab,
124
+ setCropMode,
125
+ // Filters
126
+ selectedFilter,
127
+ setSelectedFilter,
128
+ filterIntensity,
129
+ setFilterIntensity,
130
+ filterCategory,
131
+ setFilterCategory,
132
+ filteredList,
133
+ generateThumbnail,
134
+ pushHistory,
135
+ // Adjustments
136
+ adjustments,
137
+ handleAdjChange,
138
+ handleAdjChangeEnd,
139
+ // Crop
140
+ image,
141
+ cropRect,
142
+ setCropRect,
143
+ cropAspect,
144
+ setCropAspect,
145
+ // Transform
146
+ rotation,
147
+ setRotation,
148
+ flipH,
149
+ setFlipH,
150
+ flipV,
151
+ setFlipV,
152
+ zoom,
153
+ setZoom,
154
+ // Overlays
155
+ overlays,
156
+ setOverlays,
157
+ selectedOverlayId,
158
+ setSelectedOverlayId,
159
+ addOverlay,
160
+ deleteOverlay,
161
+ mainCanvasRef,
162
+ // Stickers / Emoji
163
+ emojiCat,
164
+ setEmojiCat,
165
+ // Text
166
+ textDraft,
167
+ setTextDraft,
168
+ textColor,
169
+ setTextColor,
170
+ textBold,
171
+ setTextBold,
172
+ textItalic,
173
+ setTextItalic,
174
+ textOutline,
175
+ setTextOutline,
176
+ textFontSize,
177
+ setTextFontSize,
178
+ textBg,
179
+ setTextBg,
180
+ selectedOverlay,
181
+ updateOverlay,
182
+ // Shapes
183
+ shapeStroke,
184
+ setShapeStroke,
185
+ shapeFill,
186
+ setShapeFill,
187
+ shapeStrokeW,
188
+ setShapeStrokeW,
189
+ // Frame
190
+ frameColor,
191
+ setFrameColor,
192
+ frameThick,
193
+ setFrameThick,
194
+ // Drawing
195
+ drawColor,
196
+ setDrawColor,
197
+ drawWidth,
198
+ setDrawWidth,
199
+ drawingOverlayRef,
200
+ // Creators
201
+ createEmojiOverlay,
202
+ createTextOverlay,
203
+ createShapeOverlay,
204
+ createFrameOverlay
205
+ } = api;
206
+ return /* @__PURE__ */ jsxs("div", { className: cs.leftPanel, children: [
207
+ /* @__PURE__ */ jsx("div", { className: cs.tabBar, children: TAB_DEFS.map((tab) => /* @__PURE__ */ jsxs(
208
+ "button",
209
+ {
210
+ onClick: () => {
211
+ setActiveTab(tab.key);
212
+ if (tab.key === "crop") {
213
+ setCropMode(true);
214
+ } else {
215
+ setCropMode(false);
216
+ }
217
+ },
218
+ title: tab.label,
219
+ className: activeTab === tab.key ? cs.tabButtonActive : cs.tabButton,
220
+ style: {
221
+ color: activeTab === tab.key ? accent : void 0,
222
+ borderBottomColor: activeTab === tab.key ? accent : void 0
223
+ },
224
+ children: [
225
+ /* @__PURE__ */ jsx(NiceIcon, { name: tab.icon, size: 14 }),
226
+ tab.label
227
+ ]
228
+ },
229
+ tab.key
230
+ )) }),
231
+ /* @__PURE__ */ jsxs("div", { className: cs.panelContent, children: [
232
+ activeTab === "filters" && /* @__PURE__ */ jsxs(Fragment, { children: [
233
+ /* @__PURE__ */ jsx("div", { className: cs.pillRow, children: [
234
+ { key: "all", label: t("photoEditor.all", "All") },
235
+ { key: "color", label: t("photoEditor.color", "Color") },
236
+ { key: "light", label: t("photoEditor.light", "Light") },
237
+ { key: "vintage", label: t("photoEditor.vintage", "Vintage") },
238
+ { key: "artistic", label: t("photoEditor.artistic", "Artistic") },
239
+ { key: "fun", label: t("photoEditor.fun", "Fun") }
240
+ ].map((cat) => /* @__PURE__ */ jsx(
241
+ "button",
242
+ {
243
+ onClick: () => setFilterCategory(cat.key),
244
+ className: filterCategory === cat.key ? cs.pillActive : cs.pill,
245
+ style: {
246
+ borderColor: filterCategory === cat.key ? accent : void 0,
247
+ background: filterCategory === cat.key ? accentDim : void 0
248
+ },
249
+ children: cat.label
250
+ },
251
+ cat.key
252
+ )) }),
253
+ /* @__PURE__ */ jsx("div", { className: cs.filterGrid, children: filteredList.map((filter) => /* @__PURE__ */ jsx(
254
+ FilterThumb,
255
+ {
256
+ filter,
257
+ selected: selectedFilter === filter.id,
258
+ accent,
259
+ onSelect: () => {
260
+ setSelectedFilter(filter.id);
261
+ pushHistory();
262
+ },
263
+ generateThumbnail
264
+ },
265
+ filter.id
266
+ )) }),
267
+ selectedFilter !== "none" && /* @__PURE__ */ jsxs("div", { className: cs.intensitySection, children: [
268
+ /* @__PURE__ */ jsxs("div", { className: cs.intensityHeader, children: [
269
+ /* @__PURE__ */ jsxs("span", { className: cs.sliderLabel, children: [
270
+ /* @__PURE__ */ jsx(NiceIcon, { name: "bar-chart", size: 14, style: { marginRight: 4 } }),
271
+ t("photoEditor.intensity", "Intensity")
272
+ ] }),
273
+ /* @__PURE__ */ jsxs("span", { className: cs.sliderValue, style: { color: accent }, children: [
274
+ Math.round(filterIntensity * 100),
275
+ "%"
276
+ ] })
277
+ ] }),
278
+ /* @__PURE__ */ jsx(
279
+ RangeInput,
280
+ {
281
+ min: 0,
282
+ max: 100,
283
+ value: filterIntensity * 100,
284
+ onChange: (e) => setFilterIntensity(Number(e.target.value) / 100),
285
+ onMouseUp: () => pushHistory(),
286
+ accentColor: accent
287
+ }
288
+ )
289
+ ] })
290
+ ] }),
291
+ activeTab === "adjust" && /* @__PURE__ */ jsxs("div", { className: cs.adjustColumn, children: [
292
+ ADJUSTMENT_SLIDERS.map((sl) => /* @__PURE__ */ jsxs("div", { children: [
293
+ /* @__PURE__ */ jsxs("div", { className: cs.adjustHeader, children: [
294
+ /* @__PURE__ */ jsxs("span", { className: cs.sliderLabel, children: [
295
+ /* @__PURE__ */ jsx(NiceIcon, { name: sl.icon, size: 14, style: { marginRight: 4 } }),
296
+ sl.label
297
+ ] }),
298
+ /* @__PURE__ */ jsx(
299
+ "span",
300
+ {
301
+ role: "button",
302
+ tabIndex: 0,
303
+ "aria-label": t("photoEditor.reset", "Reset"),
304
+ onKeyDown: clickOnActivationKey,
305
+ className: cs.adjustValue,
306
+ style: {
307
+ color: adjustments[sl.key] !== 0 ? accent : "var(--nice-text-secondary, #666)"
308
+ },
309
+ onClick: () => handleAdjChange(sl.key, sl.key === "blur" ? 0 : 0),
310
+ title: t("photoEditor.reset", "Reset"),
311
+ children: adjustments[sl.key]
312
+ }
313
+ )
314
+ ] }),
315
+ /* @__PURE__ */ jsx(
316
+ RangeInput,
317
+ {
318
+ min: sl.min,
319
+ max: sl.max,
320
+ value: adjustments[sl.key],
321
+ onChange: (e) => handleAdjChange(sl.key, Number(e.target.value)),
322
+ onMouseUp: handleAdjChangeEnd,
323
+ accentColor: accent
324
+ }
325
+ )
326
+ ] }, sl.key)),
327
+ /* @__PURE__ */ jsxs(
328
+ Button,
329
+ {
330
+ variant: "outline-secondary",
331
+ size: "sm",
332
+ className: "mt-2",
333
+ onClick: () => {
334
+ handleAdjChange("brightness", 0);
335
+ const defaults = { ...DEFAULT_ADJUSTMENTS };
336
+ for (const key of Object.keys(defaults)) {
337
+ handleAdjChange(key, defaults[key]);
338
+ }
339
+ pushHistory();
340
+ },
341
+ children: [
342
+ /* @__PURE__ */ jsx(NiceIcon, { name: "refresh", size: 14, style: { marginRight: 4 } }),
343
+ t("photoEditor.resetAdjustments", "Reset adjustments")
344
+ ]
345
+ }
346
+ )
347
+ ] }),
348
+ activeTab === "crop" && /* @__PURE__ */ jsxs("div", { children: [
349
+ /* @__PURE__ */ jsxs("div", { className: cs.cropInfo, children: [
350
+ /* @__PURE__ */ jsx(NiceIcon, { name: "info", size: 14, style: { marginRight: 4 } }),
351
+ t("photoEditor.dragCornersOrEdgesTo", "Drag corners or edges to crop the photo.")
352
+ ] }),
353
+ /* @__PURE__ */ jsx("div", { className: cs.cropAspectLabel, children: t("photoEditor.aspectRatio", "Aspect ratio") }),
354
+ /* @__PURE__ */ jsx("div", { className: cs.cropAspectRow, children: ASPECT_RATIOS.map((ar) => /* @__PURE__ */ jsx(
355
+ "button",
356
+ {
357
+ onClick: () => {
358
+ setCropAspect(ar.value);
359
+ if (ar.value && cropRect && image) {
360
+ let newW = cropRect.w;
361
+ let newH = cropRect.w / ar.value;
362
+ if (newH > image.naturalHeight) {
363
+ newH = image.naturalHeight;
364
+ newW = newH * ar.value;
365
+ }
366
+ setCropRect({
367
+ x: Math.max(0, cropRect.x),
368
+ y: Math.max(0, cropRect.y),
369
+ w: Math.min(newW, image.naturalWidth),
370
+ h: Math.min(newH, image.naturalHeight)
371
+ });
372
+ }
373
+ },
374
+ className: cropAspect === ar.value ? cs.pillCropActive : cs.pillCrop,
375
+ style: {
376
+ borderColor: cropAspect === ar.value ? accent : void 0,
377
+ background: cropAspect === ar.value ? accentDim : void 0
378
+ },
379
+ children: ar.label
380
+ },
381
+ ar.label
382
+ )) }),
383
+ /* @__PURE__ */ jsxs(
384
+ Button,
385
+ {
386
+ variant: "outline-secondary",
387
+ size: "sm",
388
+ className: "mt-3",
389
+ onClick: () => {
390
+ if (image) {
391
+ setCropRect({ x: 0, y: 0, w: image.naturalWidth, h: image.naturalHeight });
392
+ setCropAspect(null);
393
+ pushHistory();
394
+ }
395
+ },
396
+ children: [
397
+ /* @__PURE__ */ jsx(NiceIcon, { name: "expand", size: 14, style: { marginRight: 4 } }),
398
+ t("photoEditor.resetCrop", "Reset crop")
399
+ ]
400
+ }
401
+ )
402
+ ] }),
403
+ activeTab === "transform" && /* @__PURE__ */ jsxs("div", { children: [
404
+ /* @__PURE__ */ jsx("div", { className: cs.sectionTitleMb8, children: t("photoEditor.rotation", "Rotation") }),
405
+ /* @__PURE__ */ jsxs("div", { className: cs.transformRow, children: [
406
+ /* @__PURE__ */ jsxs(
407
+ Button,
408
+ {
409
+ variant: "dark",
410
+ size: "sm",
411
+ onClick: () => {
412
+ setRotation((rotation - 90 + 360) % 360);
413
+ pushHistory();
414
+ },
415
+ children: [
416
+ /* @__PURE__ */ jsx(NiceIcon, { name: "reply", size: 14, style: { marginRight: 4 } }),
417
+ " -90°"
418
+ ]
419
+ }
420
+ ),
421
+ /* @__PURE__ */ jsxs(
422
+ Button,
423
+ {
424
+ variant: "dark",
425
+ size: "sm",
426
+ onClick: () => {
427
+ setRotation((rotation + 90) % 360);
428
+ pushHistory();
429
+ },
430
+ children: [
431
+ /* @__PURE__ */ jsx(NiceIcon, { name: "forward", size: 14, style: { marginRight: 4 } }),
432
+ " +90°"
433
+ ]
434
+ }
435
+ ),
436
+ /* @__PURE__ */ jsx(
437
+ Button,
438
+ {
439
+ variant: "dark",
440
+ size: "sm",
441
+ onClick: () => {
442
+ setRotation(0);
443
+ pushHistory();
444
+ },
445
+ children: "0°"
446
+ }
447
+ )
448
+ ] }),
449
+ /* @__PURE__ */ jsx("div", { className: cs.sectionTitleMb8, children: t("photoEditor.flip", "Flip") }),
450
+ /* @__PURE__ */ jsxs("div", { className: cs.transformRow, children: [
451
+ /* @__PURE__ */ jsxs(
452
+ Button,
453
+ {
454
+ variant: flipH ? "outline-info" : "dark",
455
+ size: "sm",
456
+ onClick: () => {
457
+ setFlipH(!flipH);
458
+ pushHistory();
459
+ },
460
+ children: [
461
+ /* @__PURE__ */ jsx(NiceIcon, { name: "move", size: 14, style: { marginRight: 4 } }),
462
+ " ",
463
+ t("photoEditor.horizontal", "Horizontal")
464
+ ]
465
+ }
466
+ ),
467
+ /* @__PURE__ */ jsxs(
468
+ Button,
469
+ {
470
+ variant: flipV ? "outline-info" : "dark",
471
+ size: "sm",
472
+ onClick: () => {
473
+ setFlipV(!flipV);
474
+ pushHistory();
475
+ },
476
+ children: [
477
+ /* @__PURE__ */ jsx(NiceIcon, { name: "move", size: 14, style: { marginRight: 4 } }),
478
+ " ",
479
+ t("photoEditor.vertical", "Vertical")
480
+ ]
481
+ }
482
+ )
483
+ ] }),
484
+ /* @__PURE__ */ jsx("div", { className: cs.sectionTitleMb8, children: t("photoEditor.previewZoom", "Preview zoom") }),
485
+ /* @__PURE__ */ jsxs("div", { className: cs.transformZoomRow, children: [
486
+ /* @__PURE__ */ jsx(
487
+ RangeInput,
488
+ {
489
+ min: 50,
490
+ max: 300,
491
+ value: zoom * 100,
492
+ onChange: (e) => setZoom(Number(e.target.value) / 100),
493
+ accentColor: accent
494
+ }
495
+ ),
496
+ /* @__PURE__ */ jsxs("span", { className: cs.transformZoomValue, style: { color: accent }, children: [
497
+ Math.round(zoom * 100),
498
+ "%"
499
+ ] })
500
+ ] }),
501
+ /* @__PURE__ */ jsxs("div", { className: cs.transformInfo, children: [
502
+ /* @__PURE__ */ jsxs("div", { children: [
503
+ /* @__PURE__ */ jsx("strong", { children: t("photoEditor.rotation2", "Rotation:") }),
504
+ " ",
505
+ rotation,
506
+ "°"
507
+ ] }),
508
+ /* @__PURE__ */ jsxs("div", { children: [
509
+ /* @__PURE__ */ jsx("strong", { children: t("photoEditor.flipH", "Flip H:") }),
510
+ " ",
511
+ flipH ? t("photoEditor.yes", "Yes") : t("photoEditor.no", "No")
512
+ ] }),
513
+ /* @__PURE__ */ jsxs("div", { children: [
514
+ /* @__PURE__ */ jsx("strong", { children: t("photoEditor.flipV", "Flip V:") }),
515
+ " ",
516
+ flipV ? t("photoEditor.yes", "Yes") : t("photoEditor.no", "No")
517
+ ] }),
518
+ cropRect && /* @__PURE__ */ jsxs("div", { children: [
519
+ /* @__PURE__ */ jsx("strong", { children: t("photoEditor.crop", "Crop:") }),
520
+ " ",
521
+ Math.round(cropRect.w),
522
+ " ×",
523
+ " ",
524
+ Math.round(cropRect.h),
525
+ " (",
526
+ "x:",
527
+ Math.round(cropRect.x),
528
+ ", y:",
529
+ Math.round(cropRect.y),
530
+ ")"
531
+ ] })
532
+ ] })
533
+ ] }),
534
+ activeTab === "stickers" && /* @__PURE__ */ jsxs("div", { children: [
535
+ /* @__PURE__ */ jsx(OverlayToolbar, { api }),
536
+ /* @__PURE__ */ jsxs("div", { className: cs.sectionTitle, children: [
537
+ /* @__PURE__ */ jsx(NiceIcon, { name: "circle", size: 14, style: { marginRight: 4 } }),
538
+ " ",
539
+ t("photoEditor.stickersEmoji", "Stickers / Emoji")
540
+ ] }),
541
+ /* @__PURE__ */ jsx("div", { className: cs.pillRowGap3, children: EMOJI_CATALOG.map((cat) => /* @__PURE__ */ jsxs(
542
+ "button",
543
+ {
544
+ onClick: () => setEmojiCat(cat.id),
545
+ className: emojiCat === cat.id ? cs.pillEmojiActive : cs.pillEmoji,
546
+ style: {
547
+ borderColor: emojiCat === cat.id ? accent : void 0,
548
+ background: emojiCat === cat.id ? accentDim : void 0
549
+ },
550
+ children: [
551
+ cat.icon,
552
+ " ",
553
+ cat.label
554
+ ]
555
+ },
556
+ cat.id
557
+ )) }),
558
+ /* @__PURE__ */ jsx("div", { className: cs.emojiGrid, children: EMOJI_CATALOG.find((c) => c.id === emojiCat)?.emojis.map((em, idx) => /* @__PURE__ */ jsx(
559
+ "button",
560
+ {
561
+ onClick: () => {
562
+ const cw = mainCanvasRef.current?.width ?? 400;
563
+ const ch = mainCanvasRef.current?.height ?? 400;
564
+ addOverlay(createEmojiOverlay(em, cw, ch));
565
+ },
566
+ className: cs.emojiBtn,
567
+ title: em,
568
+ children: em
569
+ },
570
+ idx
571
+ )) }),
572
+ overlays.length > 0 && /* @__PURE__ */ jsxs("div", { className: cs.overlaySection, children: [
573
+ /* @__PURE__ */ jsx("div", { className: cs.overlaySectionTitle, children: t("photoEditor.layers", "Layers ({overlays})").replace(
574
+ "{overlays}",
575
+ String(overlays.length)
576
+ ) }),
577
+ overlays.map((ov) => /* @__PURE__ */ jsxs(
578
+ "div",
579
+ {
580
+ role: "button",
581
+ tabIndex: 0,
582
+ onKeyDown: clickOnActivationKey,
583
+ onClick: () => setSelectedOverlayId(ov.id),
584
+ className: cs.overlayItem,
585
+ style: { background: selectedOverlayId === ov.id ? accentDim : void 0 },
586
+ children: [
587
+ /* @__PURE__ */ jsx("span", { className: cs.overlayIcon, children: ov.type === "emoji" ? ov.emoji : ov.type === "text" ? "T" : ov.type === "shape" ? /* @__PURE__ */ jsx(NiceIcon, { name: "star", size: 14 }) : ov.type === "drawing" ? /* @__PURE__ */ jsx(NiceIcon, { name: "pen", size: 14 }) : /* @__PURE__ */ jsx(NiceIcon, { name: "square", size: 14 }) }),
588
+ /* @__PURE__ */ jsx("span", { className: cs.overlayName, children: ov.type === "text" ? ov.text.slice(0, 20) : ov.type === "emoji" ? t("photoEditor.emoji", "Emoji") : ov.type === "shape" ? ov.shapeKind : ov.type === "drawing" ? t("photoEditor.drawing", "Drawing ({strokes})").replace(
589
+ "{strokes}",
590
+ String(ov.strokes.length)
591
+ ) : ov.type === "frame" ? ov.frameStyle : "" }),
592
+ /* @__PURE__ */ jsx(
593
+ "button",
594
+ {
595
+ onClick: (e) => {
596
+ e.stopPropagation();
597
+ deleteOverlay(ov.id);
598
+ },
599
+ className: cs.overlayDeleteBtn,
600
+ "aria-label": t("photoEditor.deleteLayer", "Delete layer"),
601
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "close", size: 14 })
602
+ }
603
+ )
604
+ ]
605
+ },
606
+ ov.id
607
+ ))
608
+ ] })
609
+ ] }),
610
+ activeTab === "text" && /* @__PURE__ */ jsxs("div", { children: [
611
+ /* @__PURE__ */ jsx(OverlayToolbar, { api }),
612
+ /* @__PURE__ */ jsxs("div", { className: cs.sectionTitleMb8, children: [
613
+ /* @__PURE__ */ jsx(NiceIcon, { name: "type", size: 14, style: { marginRight: 4 } }),
614
+ " ",
615
+ t("photoEditor.addCaption", "Add caption")
616
+ ] }),
617
+ /* @__PURE__ */ jsx(
618
+ TextArea,
619
+ {
620
+ rows: 2,
621
+ value: textDraft,
622
+ onChange: (e) => setTextDraft(e.target.value),
623
+ placeholder: t("photoEditor.textPlaceholder", "Enter text…"),
624
+ className: cs.textArea
625
+ }
626
+ ),
627
+ /* @__PURE__ */ jsxs("div", { className: cs.textBtnRow, children: [
628
+ /* @__PURE__ */ jsx(
629
+ Button,
630
+ {
631
+ variant: textBold ? "light" : "dark",
632
+ size: "sm",
633
+ onClick: () => setTextBold(!textBold),
634
+ title: t("photoEditor.bold", "Bold"),
635
+ className: cs.textFormatBtn,
636
+ style: { fontWeight: 700 },
637
+ children: "B"
638
+ }
639
+ ),
640
+ /* @__PURE__ */ jsx(
641
+ Button,
642
+ {
643
+ variant: textItalic ? "light" : "dark",
644
+ size: "sm",
645
+ onClick: () => setTextItalic(!textItalic),
646
+ title: t("photoEditor.italic", "Italic"),
647
+ className: cs.textFormatBtn,
648
+ style: { fontStyle: "italic" },
649
+ children: "I"
650
+ }
651
+ ),
652
+ /* @__PURE__ */ jsx(
653
+ Button,
654
+ {
655
+ variant: textOutline ? "light" : "dark",
656
+ size: "sm",
657
+ onClick: () => setTextOutline(!textOutline),
658
+ title: t("photoEditor.outline", "Outline"),
659
+ className: cs.textFormatBtn,
660
+ children: "O"
661
+ }
662
+ ),
663
+ /* @__PURE__ */ jsx(
664
+ Button,
665
+ {
666
+ variant: textBg ? "light" : "dark",
667
+ size: "sm",
668
+ onClick: () => setTextBg(!textBg),
669
+ title: t("photoEditor.background", "Background"),
670
+ className: cs.textFormatBtn,
671
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "square", size: 14 })
672
+ }
673
+ ),
674
+ /* @__PURE__ */ jsx(
675
+ "input",
676
+ {
677
+ type: "color",
678
+ value: textColor,
679
+ onChange: (e) => setTextColor(e.target.value),
680
+ className: cs.colorInputLg,
681
+ title: t("photoEditor.textColour", "Text colour")
682
+ }
683
+ )
684
+ ] }),
685
+ /* @__PURE__ */ jsxs("div", { className: cs.sliderRow, children: [
686
+ /* @__PURE__ */ jsxs("span", { className: cs.sliderLabel, children: [
687
+ t("photoEditor.size", "Size"),
688
+ ":"
689
+ ] }),
690
+ /* @__PURE__ */ jsx(
691
+ RangeInput,
692
+ {
693
+ min: 12,
694
+ max: 120,
695
+ value: textFontSize,
696
+ onChange: (e) => setTextFontSize(Number(e.target.value)),
697
+ accentColor: accent,
698
+ style: { flex: 1 }
699
+ }
700
+ ),
701
+ /* @__PURE__ */ jsxs("span", { className: cs.sliderValue, style: { color: accent }, children: [
702
+ textFontSize,
703
+ "px"
704
+ ] })
705
+ ] }),
706
+ /* @__PURE__ */ jsxs(
707
+ Button,
708
+ {
709
+ size: "sm",
710
+ className: cs.accentBtn,
711
+ style: { background: accent, borderColor: accent },
712
+ disabled: !textDraft.trim(),
713
+ onClick: () => {
714
+ const ov = createTextOverlay(textDraft);
715
+ ov.color = textColor;
716
+ ov.bold = textBold;
717
+ ov.italic = textItalic;
718
+ ov.fontSize = textFontSize;
719
+ ov.outlineColor = textOutline ? "#000000" : null;
720
+ ov.outlineWidth = textOutline ? 2 : 0;
721
+ ov.background = textBg ? "rgba(0, 0, 0, 0.5)" : null;
722
+ addOverlay(ov);
723
+ },
724
+ children: [
725
+ /* @__PURE__ */ jsx(NiceIcon, { name: "plus", size: 14, style: { marginRight: 4 } }),
726
+ " ",
727
+ t("photoEditor.addText", "Add text")
728
+ ]
729
+ }
730
+ ),
731
+ selectedOverlay?.type === "text" && /* @__PURE__ */ jsxs("div", { className: cs.editSelectedSection, children: [
732
+ /* @__PURE__ */ jsx("div", { className: cs.editSelectedLabel, children: t("photoEditor.editSelectedText", "Edit selected text:") }),
733
+ /* @__PURE__ */ jsx(
734
+ TextArea,
735
+ {
736
+ rows: 2,
737
+ value: selectedOverlay.text,
738
+ onChange: (e) => updateOverlay(selectedOverlay.id, { text: e.target.value }),
739
+ onBlur: pushHistory,
740
+ className: cs.textAreaSm
741
+ }
742
+ ),
743
+ /* @__PURE__ */ jsxs("div", { className: cs.textGapRow, children: [
744
+ /* @__PURE__ */ jsx(
745
+ "input",
746
+ {
747
+ type: "color",
748
+ value: selectedOverlay.color,
749
+ onChange: (e) => {
750
+ updateOverlay(selectedOverlay.id, { color: e.target.value });
751
+ },
752
+ onBlur: pushHistory,
753
+ className: cs.colorInput,
754
+ "aria-label": t("photoEditor.textColor", "Text overlay color")
755
+ }
756
+ ),
757
+ /* @__PURE__ */ jsx(
758
+ Button,
759
+ {
760
+ variant: selectedOverlay.bold ? "light" : "dark",
761
+ size: "sm",
762
+ onClick: () => {
763
+ updateOverlay(selectedOverlay.id, {
764
+ bold: !selectedOverlay.bold
765
+ });
766
+ pushHistory();
767
+ },
768
+ className: cs.textFormatBtnSm,
769
+ style: { fontWeight: 700 },
770
+ children: "B"
771
+ }
772
+ ),
773
+ /* @__PURE__ */ jsx(
774
+ Button,
775
+ {
776
+ variant: selectedOverlay.italic ? "light" : "dark",
777
+ size: "sm",
778
+ onClick: () => {
779
+ updateOverlay(selectedOverlay.id, {
780
+ italic: !selectedOverlay.italic
781
+ });
782
+ pushHistory();
783
+ },
784
+ className: cs.textFormatBtnSm,
785
+ style: { fontStyle: "italic" },
786
+ children: "I"
787
+ }
788
+ )
789
+ ] })
790
+ ] })
791
+ ] }),
792
+ activeTab === "shapes" && /* @__PURE__ */ jsxs("div", { children: [
793
+ /* @__PURE__ */ jsx(OverlayToolbar, { api }),
794
+ /* @__PURE__ */ jsxs("div", { className: cs.sectionTitle, children: [
795
+ /* @__PURE__ */ jsx(NiceIcon, { name: "star", size: 14, style: { marginRight: 4 } }),
796
+ " ",
797
+ t("photoEditor.shapes", "Shapes")
798
+ ] }),
799
+ /* @__PURE__ */ jsxs("div", { className: cs.shapeColorRow, children: [
800
+ /* @__PURE__ */ jsx("span", { className: cs.shapeColorLabel, children: t("photoEditor.stroke", "Stroke:") }),
801
+ /* @__PURE__ */ jsx(
802
+ "input",
803
+ {
804
+ type: "color",
805
+ value: shapeStroke,
806
+ onChange: (e) => setShapeStroke(e.target.value),
807
+ className: cs.colorInput,
808
+ title: t("photoEditor.strokeColor", "Stroke color")
809
+ }
810
+ ),
811
+ /* @__PURE__ */ jsx("span", { className: cs.shapeColorLabelMl, children: t("photoEditor.fill", "Fill:") }),
812
+ /* @__PURE__ */ jsx(
813
+ "input",
814
+ {
815
+ type: "color",
816
+ value: shapeFill || "#ffffff",
817
+ onChange: (e) => setShapeFill(e.target.value),
818
+ className: cs.colorInput,
819
+ title: t("photoEditor.fillColor", "Fill color")
820
+ }
821
+ ),
822
+ /* @__PURE__ */ jsx(
823
+ Button,
824
+ {
825
+ variant: shapeFill ? "light" : "dark",
826
+ size: "sm",
827
+ onClick: () => setShapeFill(shapeFill ? "" : "#ffffff"),
828
+ className: cs.shapeFillToggle,
829
+ children: shapeFill ? t("photoEditor.off", "Off") : t("photoEditor.on", "On")
830
+ }
831
+ )
832
+ ] }),
833
+ /* @__PURE__ */ jsxs("div", { className: cs.sliderRow, children: [
834
+ /* @__PURE__ */ jsx("span", { className: cs.sliderLabel, children: t("photoEditor.width", "Width:") }),
835
+ /* @__PURE__ */ jsx(
836
+ RangeInput,
837
+ {
838
+ min: 1,
839
+ max: 20,
840
+ value: shapeStrokeW,
841
+ onChange: (e) => setShapeStrokeW(Number(e.target.value)),
842
+ accentColor: accent,
843
+ style: { flex: 1 }
844
+ }
845
+ ),
846
+ /* @__PURE__ */ jsxs("span", { className: cs.sliderValue, style: { color: accent }, children: [
847
+ shapeStrokeW,
848
+ "px"
849
+ ] })
850
+ ] }),
851
+ /* @__PURE__ */ jsx("div", { className: cs.shapeGrid, children: SHAPE_CATALOG.map((sh) => /* @__PURE__ */ jsx(
852
+ "button",
853
+ {
854
+ title: sh.label,
855
+ onClick: () => {
856
+ const ov = createShapeOverlay(sh.id);
857
+ ov.strokeColor = shapeStroke;
858
+ ov.fillColor = shapeFill || null;
859
+ ov.strokeWidth = shapeStrokeW;
860
+ addOverlay(ov);
861
+ },
862
+ className: cs.shapeBtn,
863
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: sh.icon, size: 18 })
864
+ },
865
+ sh.id
866
+ )) }),
867
+ /* @__PURE__ */ jsxs("div", { className: cs.sectionTitle, children: [
868
+ /* @__PURE__ */ jsx(NiceIcon, { name: "image", size: 14, style: { marginRight: 4 } }),
869
+ " ",
870
+ t("photoEditor.frames", "Frames")
871
+ ] }),
872
+ /* @__PURE__ */ jsxs("div", { className: cs.frameColorRow, children: [
873
+ /* @__PURE__ */ jsx(
874
+ "input",
875
+ {
876
+ type: "color",
877
+ value: frameColor,
878
+ onChange: (e) => setFrameColor(e.target.value),
879
+ className: cs.colorInput,
880
+ "aria-label": t("photoEditor.frameColor", "Frame color")
881
+ }
882
+ ),
883
+ /* @__PURE__ */ jsxs("div", { className: cs.frameThicknessWrap, children: [
884
+ /* @__PURE__ */ jsx("span", { className: cs.sliderLabel, children: t("photoEditor.thickness", "Thickness:") }),
885
+ /* @__PURE__ */ jsx(
886
+ RangeInput,
887
+ {
888
+ min: 1,
889
+ max: 10,
890
+ value: frameThick,
891
+ onChange: (e) => setFrameThick(Number(e.target.value)),
892
+ accentColor: accent,
893
+ style: { flex: 1 }
894
+ }
895
+ ),
896
+ /* @__PURE__ */ jsx("span", { className: cs.frameThicknessValue, style: { color: accent }, children: frameThick })
897
+ ] })
898
+ ] }),
899
+ /* @__PURE__ */ jsxs("div", { className: cs.frameGrid, children: [
900
+ FRAME_CATALOG.map((fr) => /* @__PURE__ */ jsx(
901
+ "button",
902
+ {
903
+ title: fr.label,
904
+ onClick: () => {
905
+ setOverlays((prev) => prev.filter((o) => o.type !== "frame"));
906
+ const ov = createFrameOverlay(fr.id, frameColor);
907
+ ov.thickness = frameThick / 100;
908
+ addOverlay(ov);
909
+ },
910
+ className: cs.frameBtn,
911
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: fr.icon, size: 18 })
912
+ },
913
+ fr.id
914
+ )),
915
+ /* @__PURE__ */ jsx(
916
+ "button",
917
+ {
918
+ title: t("photoEditor.removeFrame", "Remove frame"),
919
+ "aria-label": t("photoEditor.removeFrame", "Remove frame"),
920
+ onClick: () => {
921
+ setOverlays((prev) => prev.filter((o) => o.type !== "frame"));
922
+ pushHistory();
923
+ },
924
+ className: cs.removeFrameBtn,
925
+ children: /* @__PURE__ */ jsx(NiceIcon, { name: "close", size: 14 })
926
+ }
927
+ )
928
+ ] })
929
+ ] }),
930
+ activeTab === "draw" && /* @__PURE__ */ jsxs("div", { children: [
931
+ /* @__PURE__ */ jsx(OverlayToolbar, { api }),
932
+ /* @__PURE__ */ jsxs("div", { className: cs.sectionTitleMb8, children: [
933
+ /* @__PURE__ */ jsx(NiceIcon, { name: "brush", size: 14, style: { marginRight: 4 } }),
934
+ " ",
935
+ t("photoEditor.freehandDrawing", "Freehand drawing")
936
+ ] }),
937
+ /* @__PURE__ */ jsxs("div", { className: cs.sectionInfoMb, children: [
938
+ /* @__PURE__ */ jsx(NiceIcon, { name: "info", size: 14, style: { marginRight: 4 } }),
939
+ t(
940
+ "photoEditor.drawDirectlyOnThePhoto",
941
+ "Draw directly on the photo by holding the left mouse button."
942
+ )
943
+ ] }),
944
+ /* @__PURE__ */ jsxs("div", { className: cs.quickColorRow, children: [
945
+ /* @__PURE__ */ jsx("span", { className: cs.sliderLabel, children: t("photoEditor.color2", "Color:") }),
946
+ /* @__PURE__ */ jsx(
947
+ "input",
948
+ {
949
+ type: "color",
950
+ value: drawColor,
951
+ onChange: (e) => setDrawColor(e.target.value),
952
+ className: cs.colorInputDraw,
953
+ "aria-label": t("photoEditor.drawColor", "Drawing color")
954
+ }
955
+ ),
956
+ [
957
+ "#ff0000",
958
+ "#ff8800",
959
+ "#ffff00",
960
+ "#00cc00",
961
+ "#0088ff",
962
+ "#8800ff",
963
+ "#ff00ff",
964
+ "#ffffff",
965
+ "#000000"
966
+ ].map((c) => /* @__PURE__ */ jsx(
967
+ "div",
968
+ {
969
+ role: "button",
970
+ tabIndex: 0,
971
+ "aria-label": c,
972
+ "aria-pressed": drawColor === c,
973
+ onKeyDown: clickOnActivationKey,
974
+ onClick: () => setDrawColor(c),
975
+ className: cs.quickColorSwatch,
976
+ style: {
977
+ background: c,
978
+ border: drawColor === c ? `2px solid ${accent}` : void 0
979
+ }
980
+ },
981
+ c
982
+ ))
983
+ ] }),
984
+ /* @__PURE__ */ jsxs("div", { className: cs.sliderRow, style: { marginBottom: 12 }, children: [
985
+ /* @__PURE__ */ jsx("span", { className: cs.sliderLabel, children: t("photoEditor.width", "Width:") }),
986
+ /* @__PURE__ */ jsx(
987
+ RangeInput,
988
+ {
989
+ min: 1,
990
+ max: 30,
991
+ value: drawWidth,
992
+ onChange: (e) => setDrawWidth(Number(e.target.value)),
993
+ accentColor: accent,
994
+ style: { flex: 1 }
995
+ }
996
+ ),
997
+ /* @__PURE__ */ jsxs("span", { className: cs.sliderValue, style: { color: accent }, children: [
998
+ drawWidth,
999
+ "px"
1000
+ ] })
1001
+ ] }),
1002
+ /* @__PURE__ */ jsx("div", { className: cs.drawPreview, children: /* @__PURE__ */ jsx(
1003
+ "div",
1004
+ {
1005
+ style: {
1006
+ width: Math.max(drawWidth, 4),
1007
+ height: Math.max(drawWidth, 4),
1008
+ borderRadius: "50%",
1009
+ background: drawColor,
1010
+ border: "1px solid var(--nice-border-color, #555)"
1011
+ }
1012
+ }
1013
+ ) }),
1014
+ /* @__PURE__ */ jsxs("div", { className: cs.drawActionRow, children: [
1015
+ /* @__PURE__ */ jsxs(
1016
+ Button,
1017
+ {
1018
+ variant: "outline-warning",
1019
+ size: "sm",
1020
+ onClick: () => {
1021
+ if (drawingOverlayRef.current) {
1022
+ const id = drawingOverlayRef.current.id;
1023
+ setOverlays(
1024
+ (prev) => prev.map((o) => {
1025
+ if (o.id === id && o.type === "drawing") {
1026
+ const d = o;
1027
+ if (d.strokes.length <= 1) {
1028
+ return o;
1029
+ }
1030
+ return { ...d, strokes: d.strokes.slice(0, -1) };
54
1031
  }
55
- else {
56
- setCropMode(false);
57
- }
58
- }, title: tab.label, className: activeTab === tab.key ? cs.tabButtonActive : cs.tabButton, style: {
59
- color: activeTab === tab.key ? accent : undefined,
60
- borderBottomColor: activeTab === tab.key ? accent : undefined,
61
- }, children: [jsx(NiceIcon, { name: tab.icon, size: 14 }), tab.label] }, tab.key))) }), jsxs("div", { className: cs.panelContent, children: [activeTab === 'filters' && (jsxs(Fragment, { children: [jsx("div", { className: cs.pillRow, children: [
62
- { key: 'all', label: t('photoEditor.all', 'All') },
63
- { key: 'color', label: t('photoEditor.color', 'Color') },
64
- { key: 'light', label: t('photoEditor.light', 'Light') },
65
- { key: 'vintage', label: t('photoEditor.vintage', 'Vintage') },
66
- { key: 'artistic', label: t('photoEditor.artistic', 'Artistic') },
67
- { key: 'fun', label: t('photoEditor.fun', 'Fun') },
68
- ].map((cat) => (jsx("button", { onClick: () => setFilterCategory(cat.key), className: filterCategory === cat.key ? cs.pillActive : cs.pill, style: {
69
- borderColor: filterCategory === cat.key ? accent : undefined,
70
- background: filterCategory === cat.key ? accentDim : undefined,
71
- }, children: cat.label }, cat.key))) }), jsx("div", { className: cs.filterGrid, children: filteredList.map((filter) => (jsx(FilterThumb, { filter: filter, selected: selectedFilter === filter.id, accent: accent, onSelect: () => {
72
- setSelectedFilter(filter.id);
73
- pushHistory();
74
- }, generateThumbnail: generateThumbnail }, filter.id))) }), selectedFilter !== 'none' && (jsxs("div", { className: cs.intensitySection, children: [jsxs("div", { className: cs.intensityHeader, children: [jsxs("span", { className: cs.sliderLabel, children: [jsx(NiceIcon, { name: "bar-chart", size: 14, style: { marginRight: 4 } }), t('photoEditor.intensity', 'Intensity')] }), jsxs("span", { className: cs.sliderValue, style: { color: accent }, children: [Math.round(filterIntensity * 100), "%"] })] }), jsx(RangeInput, { min: 0, max: 100, value: filterIntensity * 100, onChange: (e) => setFilterIntensity(Number(e.target.value) / 100), onMouseUp: () => pushHistory(), accentColor: accent })] }))] })), activeTab === 'adjust' && (jsxs("div", { className: cs.adjustColumn, children: [ADJUSTMENT_SLIDERS.map((sl) => (jsxs("div", { children: [jsxs("div", { className: cs.adjustHeader, children: [jsxs("span", { className: cs.sliderLabel, children: [jsx(NiceIcon, { name: sl.icon, size: 14, style: { marginRight: 4 } }), sl.label] }), jsx("span", { role: "button", tabIndex: 0, "aria-label": t('photoEditor.reset', 'Reset'), onKeyDown: clickOnActivationKey, className: cs.adjustValue, style: {
75
- color: adjustments[sl.key] !== 0 ? accent : 'var(--nice-text-secondary, #666)',
76
- }, onClick: () => handleAdjChange(sl.key, sl.key === 'blur' ? 0 : 0), title: t('photoEditor.reset', 'Reset'), children: adjustments[sl.key] })] }), jsx(RangeInput, { min: sl.min, max: sl.max, value: adjustments[sl.key], onChange: (e) => handleAdjChange(sl.key, Number(e.target.value)), onMouseUp: handleAdjChangeEnd, accentColor: accent })] }, sl.key))), jsxs(Button, { variant: "outline-secondary", size: "sm", className: "mt-2", onClick: () => {
77
- handleAdjChange('brightness', 0);
78
- const defaults = { ...DEFAULT_ADJUSTMENTS };
79
- for (const key of Object.keys(defaults)) {
80
- handleAdjChange(key, defaults[key]);
81
- }
82
- pushHistory();
83
- }, children: [jsx(NiceIcon, { name: "refresh", size: 14, style: { marginRight: 4 } }), t('photoEditor.resetAdjustments', 'Reset adjustments')] })] })), activeTab === 'crop' && (jsxs("div", { children: [jsxs("div", { className: cs.cropInfo, children: [jsx(NiceIcon, { name: "info", size: 14, style: { marginRight: 4 } }), t('photoEditor.dragCornersOrEdgesTo', 'Drag corners or edges to crop the photo.')] }), jsx("div", { className: cs.cropAspectLabel, children: t('photoEditor.aspectRatio', 'Aspect ratio') }), jsx("div", { className: cs.cropAspectRow, children: ASPECT_RATIOS.map((ar) => (jsx("button", { onClick: () => {
84
- setCropAspect(ar.value);
85
- if (ar.value && cropRect && image) {
86
- let newW = cropRect.w;
87
- let newH = cropRect.w / ar.value;
88
- if (newH > image.naturalHeight) {
89
- newH = image.naturalHeight;
90
- newW = newH * ar.value;
91
- }
92
- setCropRect({
93
- x: Math.max(0, cropRect.x),
94
- y: Math.max(0, cropRect.y),
95
- w: Math.min(newW, image.naturalWidth),
96
- h: Math.min(newH, image.naturalHeight),
97
- });
98
- }
99
- }, className: cropAspect === ar.value ? cs.pillCropActive : cs.pillCrop, style: {
100
- borderColor: cropAspect === ar.value ? accent : undefined,
101
- background: cropAspect === ar.value ? accentDim : undefined,
102
- }, children: ar.label }, ar.label))) }), jsxs(Button, { variant: "outline-secondary", size: "sm", className: "mt-3", onClick: () => {
103
- if (image) {
104
- setCropRect({ x: 0, y: 0, w: image.naturalWidth, h: image.naturalHeight });
105
- setCropAspect(null);
106
- pushHistory();
107
- }
108
- }, children: [jsx(NiceIcon, { name: "expand", size: 14, style: { marginRight: 4 } }), t('photoEditor.resetCrop', 'Reset crop')] })] })), activeTab === 'transform' && (jsxs("div", { children: [jsx("div", { className: cs.sectionTitleMb8, children: t('photoEditor.rotation', 'Rotation') }), jsxs("div", { className: cs.transformRow, children: [jsxs(Button, { variant: "dark", size: "sm", onClick: () => {
109
- setRotation((rotation - 90 + 360) % 360);
110
- pushHistory();
111
- }, children: [jsx(NiceIcon, { name: "reply", size: 14, style: { marginRight: 4 } }), " -90\u00B0"] }), jsxs(Button, { variant: "dark", size: "sm", onClick: () => {
112
- setRotation((rotation + 90) % 360);
113
- pushHistory();
114
- }, children: [jsx(NiceIcon, { name: "forward", size: 14, style: { marginRight: 4 } }), " +90\u00B0"] }), jsx(Button, { variant: "dark", size: "sm", onClick: () => {
115
- setRotation(0);
116
- pushHistory();
117
- }, children: "0\u00B0" })] }), jsx("div", { className: cs.sectionTitleMb8, children: t('photoEditor.flip', 'Flip') }), jsxs("div", { className: cs.transformRow, children: [jsxs(Button, { variant: flipH ? 'outline-info' : 'dark', size: "sm", onClick: () => {
118
- setFlipH(!flipH);
119
- pushHistory();
120
- }, children: [jsx(NiceIcon, { name: "move", size: 14, style: { marginRight: 4 } }), ' ', t('photoEditor.horizontal', 'Horizontal')] }), jsxs(Button, { variant: flipV ? 'outline-info' : 'dark', size: "sm", onClick: () => {
121
- setFlipV(!flipV);
122
- pushHistory();
123
- }, children: [jsx(NiceIcon, { name: "move", size: 14, style: { marginRight: 4 } }), ' ', t('photoEditor.vertical', 'Vertical')] })] }), jsx("div", { className: cs.sectionTitleMb8, children: t('photoEditor.previewZoom', 'Preview zoom') }), jsxs("div", { className: cs.transformZoomRow, children: [jsx(RangeInput, { min: 50, max: 300, value: zoom * 100, onChange: (e) => setZoom(Number(e.target.value) / 100), accentColor: accent }), jsxs("span", { className: cs.transformZoomValue, style: { color: accent }, children: [Math.round(zoom * 100), "%"] })] }), jsxs("div", { className: cs.transformInfo, children: [jsxs("div", { children: [jsx("strong", { children: t('photoEditor.rotation2', 'Rotation:') }), " ", rotation, "\u00B0"] }), jsxs("div", { children: [jsx("strong", { children: t('photoEditor.flipH', 'Flip H:') }), ' ', flipH ? t('photoEditor.yes', 'Yes') : t('photoEditor.no', 'No')] }), jsxs("div", { children: [jsx("strong", { children: t('photoEditor.flipV', 'Flip V:') }), ' ', flipV ? t('photoEditor.yes', 'Yes') : t('photoEditor.no', 'No')] }), cropRect && (jsxs("div", { children: [jsx("strong", { children: t('photoEditor.crop', 'Crop:') }), " ", Math.round(cropRect.w), " \u00D7", ' ', Math.round(cropRect.h), ' (', "x:", Math.round(cropRect.x), ", y:", Math.round(cropRect.y), ")"] }))] })] })), activeTab === 'stickers' && (jsxs("div", { children: [jsx(OverlayToolbar, { api: api }), jsxs("div", { className: cs.sectionTitle, children: [jsx(NiceIcon, { name: "circle", size: 14, style: { marginRight: 4 } }), ' ', t('photoEditor.stickersEmoji', 'Stickers / Emoji')] }), jsx("div", { className: cs.pillRowGap3, children: EMOJI_CATALOG.map((cat) => (jsxs("button", { onClick: () => setEmojiCat(cat.id), className: emojiCat === cat.id ? cs.pillEmojiActive : cs.pillEmoji, style: {
124
- borderColor: emojiCat === cat.id ? accent : undefined,
125
- background: emojiCat === cat.id ? accentDim : undefined,
126
- }, children: [cat.icon, " ", cat.label] }, cat.id))) }), jsx("div", { className: cs.emojiGrid, children: (_a = EMOJI_CATALOG.find((c) => c.id === emojiCat)) === null || _a === void 0 ? void 0 : _a.emojis.map((em, idx) => (jsx("button", { onClick: () => {
127
- var _a, _b, _c, _d;
128
- const cw = (_b = (_a = mainCanvasRef.current) === null || _a === void 0 ? void 0 : _a.width) !== null && _b !== void 0 ? _b : 400;
129
- const ch = (_d = (_c = mainCanvasRef.current) === null || _c === void 0 ? void 0 : _c.height) !== null && _d !== void 0 ? _d : 400;
130
- addOverlay(createEmojiOverlay(em, cw, ch));
131
- }, className: cs.emojiBtn, title: em, children: em }, idx))) }), overlays.length > 0 && (jsxs("div", { className: cs.overlaySection, children: [jsx("div", { className: cs.overlaySectionTitle, children: t('photoEditor.layers', 'Layers ({overlays})').replace('{overlays}', String(overlays.length)) }), overlays.map((ov) => (jsxs("div", { role: "button", tabIndex: 0, onKeyDown: clickOnActivationKey, onClick: () => setSelectedOverlayId(ov.id), className: cs.overlayItem, style: { background: selectedOverlayId === ov.id ? accentDim : undefined }, children: [jsx("span", { className: cs.overlayIcon, children: ov.type === 'emoji' ? (ov.emoji) : ov.type === 'text' ? ('T') : ov.type === 'shape' ? (jsx(NiceIcon, { name: "star", size: 14 })) : ov.type === 'drawing' ? (jsx(NiceIcon, { name: "pen", size: 14 })) : (jsx(NiceIcon, { name: "square", size: 14 })) }), jsx("span", { className: cs.overlayName, children: ov.type === 'text'
132
- ? ov.text.slice(0, 20)
133
- : ov.type === 'emoji'
134
- ? t('photoEditor.emoji', 'Emoji')
135
- : ov.type === 'shape'
136
- ? ov.shapeKind
137
- : ov.type === 'drawing'
138
- ? t('photoEditor.drawing', 'Drawing ({strokes})').replace('{strokes}', String(ov.strokes.length))
139
- : ov.type === 'frame'
140
- ? ov.frameStyle
141
- : '' }), jsx("button", { onClick: (e) => {
142
- e.stopPropagation();
143
- deleteOverlay(ov.id);
144
- }, className: cs.overlayDeleteBtn, "aria-label": t('photoEditor.deleteLayer', 'Delete layer'), children: jsx(NiceIcon, { name: "close", size: 14 }) })] }, ov.id)))] }))] })), activeTab === 'text' && (jsxs("div", { children: [jsx(OverlayToolbar, { api: api }), jsxs("div", { className: cs.sectionTitleMb8, children: [jsx(NiceIcon, { name: "type", size: 14, style: { marginRight: 4 } }), ' ', t('photoEditor.addCaption', 'Add caption')] }), jsx(TextArea, { rows: 2, value: textDraft, onChange: (e) => setTextDraft(e.target.value), placeholder: t('photoEditor.textPlaceholder', 'Enter text…'), className: cs.textArea }), jsxs("div", { className: cs.textBtnRow, children: [jsx(Button, { variant: textBold ? 'light' : 'dark', size: "sm", onClick: () => setTextBold(!textBold), title: t('photoEditor.bold', 'Bold'), className: cs.textFormatBtn, style: { fontWeight: 700 }, children: "B" }), jsx(Button, { variant: textItalic ? 'light' : 'dark', size: "sm", onClick: () => setTextItalic(!textItalic), title: t('photoEditor.italic', 'Italic'), className: cs.textFormatBtn, style: { fontStyle: 'italic' }, children: "I" }), jsx(Button, { variant: textOutline ? 'light' : 'dark', size: "sm", onClick: () => setTextOutline(!textOutline), title: t('photoEditor.outline', 'Outline'), className: cs.textFormatBtn, children: "O" }), jsx(Button, { variant: textBg ? 'light' : 'dark', size: "sm", onClick: () => setTextBg(!textBg), title: t('photoEditor.background', 'Background'), className: cs.textFormatBtn, children: jsx(NiceIcon, { name: "square", size: 14 }) }), jsx("input", { type: "color", value: textColor, onChange: (e) => setTextColor(e.target.value), className: cs.colorInputLg, title: t('photoEditor.textColour', 'Text colour') })] }), jsxs("div", { className: cs.sliderRow, children: [jsxs("span", { className: cs.sliderLabel, children: [t('photoEditor.size', 'Size'), ":"] }), jsx(RangeInput, { min: 12, max: 120, value: textFontSize, onChange: (e) => setTextFontSize(Number(e.target.value)), accentColor: accent, style: { flex: 1 } }), jsxs("span", { className: cs.sliderValue, style: { color: accent }, children: [textFontSize, "px"] })] }), jsxs(Button, { size: "sm", className: cs.accentBtn, style: { background: accent, borderColor: accent }, disabled: !textDraft.trim(), onClick: () => {
145
- const ov = createTextOverlay(textDraft);
146
- ov.color = textColor;
147
- ov.bold = textBold;
148
- ov.italic = textItalic;
149
- ov.fontSize = textFontSize;
150
- // Drawn into the photo on canvas (which ignores var()).
151
- ov.outlineColor = textOutline ? '#000000' : null;
152
- ov.outlineWidth = textOutline ? 2 : 0;
153
- // audit-allow: literal-color — caption box drawn into the photo
154
- ov.background = textBg ? 'rgba(0, 0, 0, 0.5)' : null;
155
- addOverlay(ov);
156
- }, children: [jsx(NiceIcon, { name: "plus", size: 14, style: { marginRight: 4 } }), ' ', t('photoEditor.addText', 'Add text')] }), (selectedOverlay === null || selectedOverlay === void 0 ? void 0 : selectedOverlay.type) === 'text' && (jsxs("div", { className: cs.editSelectedSection, children: [jsx("div", { className: cs.editSelectedLabel, children: t('photoEditor.editSelectedText', 'Edit selected text:') }), jsx(TextArea, { rows: 2, value: selectedOverlay.text, onChange: (e) => updateOverlay(selectedOverlay.id, { text: e.target.value }), onBlur: pushHistory, className: cs.textAreaSm }), jsxs("div", { className: cs.textGapRow, children: [jsx("input", { type: "color", value: selectedOverlay.color, onChange: (e) => {
157
- updateOverlay(selectedOverlay.id, { color: e.target.value });
158
- }, onBlur: pushHistory, className: cs.colorInput, "aria-label": t('photoEditor.textColor', 'Text overlay color') }), jsx(Button, { variant: selectedOverlay.bold ? 'light' : 'dark', size: "sm", onClick: () => {
159
- updateOverlay(selectedOverlay.id, {
160
- bold: !selectedOverlay.bold,
161
- });
162
- pushHistory();
163
- }, className: cs.textFormatBtnSm, style: { fontWeight: 700 }, children: "B" }), jsx(Button, { variant: selectedOverlay.italic ? 'light' : 'dark', size: "sm", onClick: () => {
164
- updateOverlay(selectedOverlay.id, {
165
- italic: !selectedOverlay.italic,
166
- });
167
- pushHistory();
168
- }, className: cs.textFormatBtnSm, style: { fontStyle: 'italic' }, children: "I" })] })] }))] })), activeTab === 'shapes' && (jsxs("div", { children: [jsx(OverlayToolbar, { api: api }), jsxs("div", { className: cs.sectionTitle, children: [jsx(NiceIcon, { name: "star", size: 14, style: { marginRight: 4 } }), ' ', t('photoEditor.shapes', 'Shapes')] }), jsxs("div", { className: cs.shapeColorRow, children: [jsx("span", { className: cs.shapeColorLabel, children: t('photoEditor.stroke', 'Stroke:') }), jsx("input", { type: "color", value: shapeStroke, onChange: (e) => setShapeStroke(e.target.value), className: cs.colorInput, title: t('photoEditor.strokeColor', 'Stroke color') }), jsx("span", { className: cs.shapeColorLabelMl, children: t('photoEditor.fill', 'Fill:') }), jsx("input", { type: "color", value: shapeFill || '#ffffff', onChange: (e) => setShapeFill(e.target.value), className: cs.colorInput, title: t('photoEditor.fillColor', 'Fill color') }), jsx(Button, { variant: shapeFill ? 'light' : 'dark', size: "sm", onClick: () => setShapeFill(shapeFill ? '' : '#ffffff'), className: cs.shapeFillToggle, children: shapeFill ? t('photoEditor.off', 'Off') : t('photoEditor.on', 'On') })] }), jsxs("div", { className: cs.sliderRow, children: [jsx("span", { className: cs.sliderLabel, children: t('photoEditor.width', 'Width:') }), jsx(RangeInput, { min: 1, max: 20, value: shapeStrokeW, onChange: (e) => setShapeStrokeW(Number(e.target.value)), accentColor: accent, style: { flex: 1 } }), jsxs("span", { className: cs.sliderValue, style: { color: accent }, children: [shapeStrokeW, "px"] })] }), jsx("div", { className: cs.shapeGrid, children: SHAPE_CATALOG.map((sh) => (jsx("button", { title: sh.label, onClick: () => {
169
- const ov = createShapeOverlay(sh.id);
170
- ov.strokeColor = shapeStroke;
171
- ov.fillColor = shapeFill || null;
172
- ov.strokeWidth = shapeStrokeW;
173
- addOverlay(ov);
174
- }, className: cs.shapeBtn, children: jsx(NiceIcon, { name: sh.icon, size: 18 }) }, sh.id))) }), jsxs("div", { className: cs.sectionTitle, children: [jsx(NiceIcon, { name: "image", size: 14, style: { marginRight: 4 } }), ' ', t('photoEditor.frames', 'Frames')] }), jsxs("div", { className: cs.frameColorRow, children: [jsx("input", { type: "color", value: frameColor, onChange: (e) => setFrameColor(e.target.value), className: cs.colorInput, "aria-label": t('photoEditor.frameColor', 'Frame color') }), jsxs("div", { className: cs.frameThicknessWrap, children: [jsx("span", { className: cs.sliderLabel, children: t('photoEditor.thickness', 'Thickness:') }), jsx(RangeInput, { min: 1, max: 10, value: frameThick, onChange: (e) => setFrameThick(Number(e.target.value)), accentColor: accent, style: { flex: 1 } }), jsx("span", { className: cs.frameThicknessValue, style: { color: accent }, children: frameThick })] })] }), jsxs("div", { className: cs.frameGrid, children: [FRAME_CATALOG.map((fr) => (jsx("button", { title: fr.label, onClick: () => {
175
- setOverlays((prev) => prev.filter((o) => o.type !== 'frame'));
176
- const ov = createFrameOverlay(fr.id, frameColor);
177
- ov.thickness = frameThick / 100;
178
- addOverlay(ov);
179
- }, className: cs.frameBtn, children: jsx(NiceIcon, { name: fr.icon, size: 18 }) }, fr.id))), jsx("button", { title: t('photoEditor.removeFrame', 'Remove frame'), "aria-label": t('photoEditor.removeFrame', 'Remove frame'), onClick: () => {
180
- setOverlays((prev) => prev.filter((o) => o.type !== 'frame'));
181
- pushHistory();
182
- }, className: cs.removeFrameBtn, children: jsx(NiceIcon, { name: "close", size: 14 }) })] })] })), activeTab === 'draw' && (jsxs("div", { children: [jsx(OverlayToolbar, { api: api }), jsxs("div", { className: cs.sectionTitleMb8, children: [jsx(NiceIcon, { name: "brush", size: 14, style: { marginRight: 4 } }), ' ', t('photoEditor.freehandDrawing', 'Freehand drawing')] }), jsxs("div", { className: cs.sectionInfoMb, children: [jsx(NiceIcon, { name: "info", size: 14, style: { marginRight: 4 } }), t('photoEditor.drawDirectlyOnThePhoto', 'Draw directly on the photo by holding the left mouse button.')] }), jsxs("div", { className: cs.quickColorRow, children: [jsx("span", { className: cs.sliderLabel, children: t('photoEditor.color2', 'Color:') }), jsx("input", { type: "color", value: drawColor, onChange: (e) => setDrawColor(e.target.value), className: cs.colorInputDraw, "aria-label": t('photoEditor.drawColor', 'Drawing color') }), [
183
- '#ff0000',
184
- '#ff8800',
185
- '#ffff00',
186
- '#00cc00',
187
- '#0088ff',
188
- '#8800ff',
189
- '#ff00ff',
190
- '#ffffff',
191
- '#000000',
192
- ].map((c) => (jsx("div", { role: "button", tabIndex: 0, "aria-label": c, "aria-pressed": drawColor === c, onKeyDown: clickOnActivationKey, onClick: () => setDrawColor(c), className: cs.quickColorSwatch, style: {
193
- background: c,
194
- border: drawColor === c ? `2px solid ${accent}` : undefined,
195
- } }, c)))] }), jsxs("div", { className: cs.sliderRow, style: { marginBottom: 12 }, children: [jsx("span", { className: cs.sliderLabel, children: t('photoEditor.width', 'Width:') }), jsx(RangeInput, { min: 1, max: 30, value: drawWidth, onChange: (e) => setDrawWidth(Number(e.target.value)), accentColor: accent, style: { flex: 1 } }), jsxs("span", { className: cs.sliderValue, style: { color: accent }, children: [drawWidth, "px"] })] }), jsx("div", { className: cs.drawPreview, children: jsx("div", { style: {
196
- width: Math.max(drawWidth, 4),
197
- height: Math.max(drawWidth, 4),
198
- borderRadius: '50%',
199
- background: drawColor,
200
- border: '1px solid var(--nice-border-color, #555)',
201
- } }) }), jsxs("div", { className: cs.drawActionRow, children: [jsxs(Button, { variant: "outline-warning", size: "sm", onClick: () => {
202
- if (drawingOverlayRef.current) {
203
- const id = drawingOverlayRef.current.id;
204
- setOverlays((prev) => prev.map((o) => {
205
- if (o.id === id && o.type === 'drawing') {
206
- const d = o;
207
- if (d.strokes.length <= 1) {
208
- return o;
209
- }
210
- return { ...d, strokes: d.strokes.slice(0, -1) };
211
- }
212
- return o;
213
- }));
214
- pushHistory();
215
- }
216
- }, className: cs.drawActionBtn, children: [jsx(NiceIcon, { name: "reply", size: 14, style: { marginRight: 4 } }), ' ', t('photoEditor.undoStroke', 'Undo stroke')] }), jsxs(Button, { variant: "outline-danger", size: "sm", onClick: () => {
217
- if (drawingOverlayRef.current) {
218
- deleteOverlay(drawingOverlayRef.current.id);
219
- drawingOverlayRef.current = null;
220
- }
221
- }, className: cs.drawActionBtn, children: [jsx(NiceIcon, { name: "trash", size: 14, style: { marginRight: 4 } }), ' ', t('photoEditor.clear', 'Clear')] })] }), jsxs(Button, { variant: "dark", size: "sm", className: "mt-2 w-100", onClick: () => {
222
- drawingOverlayRef.current = null;
223
- }, children: [jsx(NiceIcon, { name: "plus", size: 14, style: { marginRight: 4 } }), ' ', t('photoEditor.newDrawingLayer', 'New drawing layer')] })] }))] })] }));
224
- }), { displayName: 'PhotoEditorLeftPanel' });
225
-
226
- export { PhotoEditorLeftPanel as default };
1032
+ return o;
1033
+ })
1034
+ );
1035
+ pushHistory();
1036
+ }
1037
+ },
1038
+ className: cs.drawActionBtn,
1039
+ children: [
1040
+ /* @__PURE__ */ jsx(NiceIcon, { name: "reply", size: 14, style: { marginRight: 4 } }),
1041
+ " ",
1042
+ t("photoEditor.undoStroke", "Undo stroke")
1043
+ ]
1044
+ }
1045
+ ),
1046
+ /* @__PURE__ */ jsxs(
1047
+ Button,
1048
+ {
1049
+ variant: "outline-danger",
1050
+ size: "sm",
1051
+ onClick: () => {
1052
+ if (drawingOverlayRef.current) {
1053
+ deleteOverlay(drawingOverlayRef.current.id);
1054
+ drawingOverlayRef.current = null;
1055
+ }
1056
+ },
1057
+ className: cs.drawActionBtn,
1058
+ children: [
1059
+ /* @__PURE__ */ jsx(NiceIcon, { name: "trash", size: 14, style: { marginRight: 4 } }),
1060
+ " ",
1061
+ t("photoEditor.clear", "Clear")
1062
+ ]
1063
+ }
1064
+ )
1065
+ ] }),
1066
+ /* @__PURE__ */ jsxs(
1067
+ Button,
1068
+ {
1069
+ variant: "dark",
1070
+ size: "sm",
1071
+ className: "mt-2 w-100",
1072
+ onClick: () => {
1073
+ drawingOverlayRef.current = null;
1074
+ },
1075
+ children: [
1076
+ /* @__PURE__ */ jsx(NiceIcon, { name: "plus", size: 14, style: { marginRight: 4 } }),
1077
+ " ",
1078
+ t("photoEditor.newDrawingLayer", "New drawing layer")
1079
+ ]
1080
+ }
1081
+ )
1082
+ ] })
1083
+ ] })
1084
+ ] });
1085
+ }),
1086
+ { displayName: "PhotoEditorLeftPanel" }
1087
+ );
1088
+ export {
1089
+ PhotoEditorLeftPanel as default
1090
+ };