@nice2dev/ui-graphics 1.2.0 → 1.2.2

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 (98) hide show
  1. package/CHANGELOG.md +142 -0
  2. package/dist/cjs/animation/AnimatedPerson.js +3 -1
  3. package/dist/cjs/animation/Audience.js +3 -1
  4. package/dist/cjs/animation/rig/RigPlayer.js +1 -1
  5. package/dist/cjs/animation/rig/presets.js +67 -55
  6. package/dist/cjs/animation/shapes/headwear.js +4 -1
  7. package/dist/cjs/core/NiceBrushEngine.js +8 -5
  8. package/dist/cjs/core/collaboration.js +11 -6
  9. package/dist/cjs/core/editorAsk.js +48 -0
  10. package/dist/cjs/core/i18n.js +7 -3
  11. package/dist/cjs/core/minimap.js +21 -20
  12. package/dist/cjs/core/plugins.js +1 -1
  13. package/dist/cjs/core/rtl.js +1 -1
  14. package/dist/cjs/core/theme.js +1 -1
  15. package/dist/cjs/font/NiceFontEditor.js +14 -12
  16. package/dist/cjs/game/GameAsset2dEditor.js +75 -44
  17. package/dist/cjs/nice2dev-ui-graphics.css +1 -1
  18. package/dist/cjs/photo/FilterThumb.js +3 -3
  19. package/dist/cjs/photo/NiceAITools.js +6 -4
  20. package/dist/cjs/photo/PhotoEditor.js +3 -1
  21. package/dist/cjs/photo/PhotoEditorCanvas.js +8 -6
  22. package/dist/cjs/photo/PhotoEditorLeftPanel.js +29 -29
  23. package/dist/cjs/photo/PhotoEditorToolbar.js +4 -4
  24. package/dist/cjs/photo/photoFilters.js +1 -0
  25. package/dist/cjs/pixel/PixelEditor.js +6 -3
  26. package/dist/cjs/pixel/PixelEditorCanvas.js +10 -6
  27. package/dist/cjs/pixel/PixelEditorMenuBar.js +95 -30
  28. package/dist/cjs/pixel/PixelEditorRightPanel.js +32 -24
  29. package/dist/cjs/pixel/PixelEditorStatusBar.js +12 -4
  30. package/dist/cjs/pixel/PixelEditorTimeline.js +15 -2
  31. package/dist/cjs/pixel/PixelEditorToolbar.js +12 -6
  32. package/dist/cjs/pixel/SpriteGeneratorPanel.js +28 -17
  33. package/dist/cjs/pixel/animalGeneratorPresets.js +31 -31
  34. package/dist/cjs/pixel/buildingGeneratorPresets.js +24 -24
  35. package/dist/cjs/pixel/characterGeneratorPresets.js +25 -25
  36. package/dist/cjs/pixel/usePixelEditor.js +13 -10
  37. package/dist/cjs/texture/Nice3DTexturePainter.js +16 -10
  38. package/dist/cjs/ui/NiceUIDesigner.js +17 -13
  39. package/dist/cjs/vector/NiceSvgToCode.js +7 -5
  40. package/dist/cjs/vector/VectorEditor.js +3 -2
  41. package/dist/cjs/vector/VectorEditorMenuBar.js +7 -1
  42. package/dist/cjs/vector/VectorEditorRightPanel.js +1 -1
  43. package/dist/cjs/vector/VectorEditorShapeRenderer.js +3 -1
  44. package/dist/cjs/vector/useVectorEditor.js +38 -29
  45. package/dist/cjs/vector/vectorEditorImport.js +1 -0
  46. package/dist/cjs/vector/vectorIconImport.js +1 -0
  47. package/dist/esm/animation/AnimatedPerson.js +3 -1
  48. package/dist/esm/animation/Audience.js +3 -1
  49. package/dist/esm/animation/rig/RigPlayer.js +1 -1
  50. package/dist/esm/animation/rig/presets.js +68 -56
  51. package/dist/esm/animation/shapes/headwear.js +4 -1
  52. package/dist/esm/animation/spineExport.js +1 -1
  53. package/dist/esm/core/NiceBrushEngine.js +8 -5
  54. package/dist/esm/core/collaboration.js +11 -6
  55. package/dist/esm/core/editorAsk.js +45 -0
  56. package/dist/esm/core/i18n.js +8 -4
  57. package/dist/esm/core/minimap.js +21 -20
  58. package/dist/esm/core/plugins.js +1 -1
  59. package/dist/esm/core/rtl.js +1 -1
  60. package/dist/esm/core/theme.js +1 -1
  61. package/dist/esm/font/NiceFontEditor.js +14 -12
  62. package/dist/esm/game/GameAsset2dEditor.js +75 -44
  63. package/dist/esm/nice2dev-ui-graphics.css +1 -1
  64. package/dist/esm/photo/FilterThumb.js +3 -3
  65. package/dist/esm/photo/NiceAITools.js +6 -4
  66. package/dist/esm/photo/PhotoEditor.js +3 -1
  67. package/dist/esm/photo/PhotoEditorCanvas.js +8 -6
  68. package/dist/esm/photo/PhotoEditorLeftPanel.js +29 -29
  69. package/dist/esm/photo/PhotoEditorToolbar.js +4 -4
  70. package/dist/esm/photo/photoFilters.js +1 -0
  71. package/dist/esm/pixel/PixelEditor.js +6 -3
  72. package/dist/esm/pixel/PixelEditorCanvas.js +10 -6
  73. package/dist/esm/pixel/PixelEditorMenuBar.js +95 -30
  74. package/dist/esm/pixel/PixelEditorRightPanel.js +32 -24
  75. package/dist/esm/pixel/PixelEditorStatusBar.js +12 -4
  76. package/dist/esm/pixel/PixelEditorTimeline.js +15 -2
  77. package/dist/esm/pixel/PixelEditorToolbar.js +12 -6
  78. package/dist/esm/pixel/SpriteGeneratorPanel.js +28 -17
  79. package/dist/esm/pixel/animalGeneratorPresets.js +32 -32
  80. package/dist/esm/pixel/buildingGeneratorPresets.js +25 -25
  81. package/dist/esm/pixel/characterGeneratorPresets.js +26 -26
  82. package/dist/esm/pixel/usePixelEditor.js +13 -10
  83. package/dist/esm/texture/Nice3DTexturePainter.js +16 -10
  84. package/dist/esm/ui/NiceUIDesigner.js +17 -13
  85. package/dist/esm/vector/NiceSvgToCode.js +7 -5
  86. package/dist/esm/vector/VectorEditor.js +3 -2
  87. package/dist/esm/vector/VectorEditorMenuBar.js +7 -1
  88. package/dist/esm/vector/VectorEditorRightPanel.js +1 -1
  89. package/dist/esm/vector/VectorEditorShapeRenderer.js +3 -1
  90. package/dist/esm/vector/useVectorEditor.js +38 -29
  91. package/dist/esm/vector/vectorEditorImport.js +1 -0
  92. package/dist/esm/vector/vectorIconImport.js +1 -0
  93. package/dist/types/core/LocalUI.d.ts +1 -2
  94. package/dist/types/core/editorAsk.d.ts +26 -0
  95. package/dist/types/core/i18n.d.ts +1 -17
  96. package/dist/types/pixel/usePixelEditor.d.ts +3 -0
  97. package/dist/types/vector/useVectorEditor.d.ts +2 -0
  98. package/package.json +5 -5
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
4
4
  var ui = require('@nice2dev/ui');
5
5
  var uiCore = require('@nice2dev/ui-core');
6
6
  var React = require('react');
7
+ var i18n = require('../core/i18n.js');
7
8
 
8
9
  /** Built-in guided tour (EN inline + i18n keys → PL and other locales). */
9
10
  const UI_DESIGNER_TOUR = [
@@ -312,6 +313,7 @@ function updateInTree(nodes, id, updates) {
312
313
  /** Visual drag & drop UI builder that generates React code. */
313
314
  const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
314
315
  var _a, _b, _c, _d, _e;
316
+ const { t } = i18n.useNiceTranslation();
315
317
  const designer = useUIDesigner();
316
318
  const [breakpoint, setBreakpoint] = React.useState('desktop');
317
319
  const [dragType, setDragType] = React.useState(null);
@@ -395,7 +397,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
395
397
  color: 'var(--nice-text-secondary, #868e96)',
396
398
  fontSize: 11,
397
399
  gap: 4,
398
- }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "image", size: 14 }), " Image"] })) : node.type === 'List' ? (jsxRuntime.jsx("ul", { style: { margin: 0, ...node.style }, children: ((_d = node.props.items) !== null && _d !== void 0 ? _d : []).map((item, i) => (jsxRuntime.jsx("li", { children: item }, i))) })) : node.type === 'Divider' ? (jsxRuntime.jsx("hr", { style: {
400
+ }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "image", size: 14 }), " ", t('uiDesigner.image', 'Image')] })) : node.type === 'List' ? (jsxRuntime.jsx("ul", { style: { margin: 0, ...node.style }, children: ((_d = node.props.items) !== null && _d !== void 0 ? _d : []).map((item, i) => (jsxRuntime.jsx("li", { children: item }, i))) })) : node.type === 'Divider' ? (jsxRuntime.jsx("hr", { style: {
399
401
  margin: 0,
400
402
  border: 'none',
401
403
  borderTop: '1px solid var(--nice-border, #dee2e6)',
@@ -421,7 +423,9 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
421
423
  display: 'flex',
422
424
  alignItems: 'center',
423
425
  gap: 4,
424
- background: designer.selectedId === node.id ? 'rgba(51,154,240,0.2)' : 'transparent',
426
+ background: designer.selectedId === node.id
427
+ ? 'color-mix(in srgb, var(--nice-color-primary, #339af0) 20%, transparent)'
428
+ : 'transparent',
425
429
  borderRadius: 2,
426
430
  }, children: [(def === null || def === void 0 ? void 0 : def.icon) && jsxRuntime.jsx(uiCore.NiceIcon, { name: def.icon, size: 12 }), jsxRuntime.jsxs("span", { style: { flex: 1, overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap' }, children: [node.type, node.props.text ? `: ${String(node.props.text).slice(0, 15)}` : ''] })] }), node.children.map((c) => renderTreeItem(c, depth + 1))] }, node.id));
427
431
  };
@@ -435,7 +439,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
435
439
  height: '100%',
436
440
  overflowY: 'auto',
437
441
  boxSizing: 'border-box',
438
- }, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { style: { fontSize: 12, display: 'block', marginBottom: 4 }, children: "Components" }), jsxRuntime.jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 2 }, children: COMPONENT_DEFS.map((def) => (jsxRuntime.jsxs("button", { draggable: true, onDragStart: () => setDragType(def.type), onDragEnd: () => setDragType(null), onClick: () => { var _a; return designer.addNode(def.type, (_a = designer.selectedId) !== null && _a !== void 0 ? _a : undefined); }, style: {
442
+ }, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { style: { fontSize: 12, display: 'block', marginBottom: 4 }, children: t('uiDesigner.components', 'Components') }), jsxRuntime.jsx("div", { style: { display: 'flex', flexDirection: 'column', gap: 2 }, children: COMPONENT_DEFS.map((def) => (jsxRuntime.jsxs("button", { draggable: true, onDragStart: () => setDragType(def.type), onDragEnd: () => setDragType(null), onClick: () => { var _a; return designer.addNode(def.type, (_a = designer.selectedId) !== null && _a !== void 0 ? _a : undefined); }, style: {
439
443
  display: 'flex',
440
444
  alignItems: 'center',
441
445
  gap: 4,
@@ -447,8 +451,8 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
447
451
  cursor: 'grab',
448
452
  textAlign: 'left',
449
453
  fontSize: 11,
450
- }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: def.icon, size: 14 }), " ", def.label] }, def.type))) })] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { style: { fontSize: 12, display: 'block', marginBottom: 4 }, children: "Tree" }), jsxRuntime.jsx("div", { style: { maxHeight: 200, overflowY: 'auto' }, children: designer.tree.length === 0 ? (jsxRuntime.jsx("div", { style: { color: 'var(--nice-text-secondary, #5c5f66)', padding: 4 }, children: "Empty" })) : (designer.tree.map((n) => renderTreeItem(n))) })] })] }));
451
- const toolbarContent = (jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 4, alignItems: 'center', fontSize: 11, padding: '4px 8px' }, children: [jsxRuntime.jsx("button", { onClick: () => designer.undo(), disabled: !designer.canUndo, style: { ...btnStyle(false), opacity: designer.canUndo ? 1 : 0.4 }, title: "Undo (Ctrl+Z)", "aria-label": "Undo", children: jsxRuntime.jsx(uiCore.NiceIcon, { name: "reply", size: 14 }) }), jsxRuntime.jsx("button", { onClick: () => designer.redo(), disabled: !designer.canRedo, style: { ...btnStyle(false), opacity: designer.canRedo ? 1 : 0.4 }, title: "Redo (Ctrl+Y)", "aria-label": "Redo", children: jsxRuntime.jsx(uiCore.NiceIcon, { name: "forward", size: 14 }) }), jsxRuntime.jsx("div", { style: { width: 1, height: 16, background: 'var(--nice-border, #373a40)' } }), jsxRuntime.jsxs("button", { onClick: () => setBreakpoint('desktop'), style: btnStyle(breakpoint === 'desktop'), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "monitor", size: 14 }), " Desktop"] }), jsxRuntime.jsxs("button", { onClick: () => setBreakpoint('tablet'), style: btnStyle(breakpoint === 'tablet'), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "window", size: 14 }), " Tablet"] }), jsxRuntime.jsxs("button", { onClick: () => setBreakpoint('mobile'), style: btnStyle(breakpoint === 'mobile'), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "phone", size: 14 }), " Mobile"] }), jsxRuntime.jsx("div", { style: { flex: 1 } }), designer.selectedId && (jsxRuntime.jsxs("button", { onClick: () => designer.removeNode(designer.selectedId), style: { ...btnStyle(false), color: 'var(--nice-danger, #ff6b6b)' }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "trash", size: 14 }), " Delete"] })), jsxRuntime.jsxs("button", { onClick: () => setShowCode((v) => !v), style: btnStyle(showCode), children: ['</>', " Code"] }), jsxRuntime.jsx("button", { onClick: () => onExport === null || onExport === void 0 ? void 0 : onExport(designer.exportCode()), style: {
454
+ }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: def.icon, size: 14 }), " ", def.label] }, def.type))) })] }), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("strong", { style: { fontSize: 12, display: 'block', marginBottom: 4 }, children: t('uiDesigner.tree', 'Tree') }), jsxRuntime.jsx("div", { style: { maxHeight: 200, overflowY: 'auto' }, children: designer.tree.length === 0 ? (jsxRuntime.jsx("div", { style: { color: 'var(--nice-text-secondary, #5c5f66)', padding: 4 }, children: t('uiDesigner.empty', 'Empty') })) : (designer.tree.map((n) => renderTreeItem(n))) })] })] }));
455
+ const toolbarContent = (jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 4, alignItems: 'center', fontSize: 11, padding: '4px 8px' }, children: [jsxRuntime.jsx("button", { onClick: () => designer.undo(), disabled: !designer.canUndo, style: { ...btnStyle(false), opacity: designer.canUndo ? 1 : 0.4 }, title: t('uiDesigner.undoCtrlZ', 'Undo (Ctrl+Z)'), "aria-label": t('uiDesigner.undo', 'Undo'), children: jsxRuntime.jsx(uiCore.NiceIcon, { name: "reply", size: 14 }) }), jsxRuntime.jsx("button", { onClick: () => designer.redo(), disabled: !designer.canRedo, style: { ...btnStyle(false), opacity: designer.canRedo ? 1 : 0.4 }, title: t('uiDesigner.redoCtrlY', 'Redo (Ctrl+Y)'), "aria-label": t('uiDesigner.redo', 'Redo'), children: jsxRuntime.jsx(uiCore.NiceIcon, { name: "forward", size: 14 }) }), jsxRuntime.jsx("div", { style: { width: 1, height: 16, background: 'var(--nice-border, #373a40)' } }), jsxRuntime.jsxs("button", { onClick: () => setBreakpoint('desktop'), style: btnStyle(breakpoint === 'desktop'), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "monitor", size: 14 }), " ", t('uiDesigner.desktop', 'Desktop')] }), jsxRuntime.jsxs("button", { onClick: () => setBreakpoint('tablet'), style: btnStyle(breakpoint === 'tablet'), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "window", size: 14 }), " ", t('uiDesigner.tablet', 'Tablet')] }), jsxRuntime.jsxs("button", { onClick: () => setBreakpoint('mobile'), style: btnStyle(breakpoint === 'mobile'), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "phone", size: 14 }), " ", t('uiDesigner.mobile', 'Mobile')] }), jsxRuntime.jsx("div", { style: { flex: 1 } }), designer.selectedId && (jsxRuntime.jsxs("button", { onClick: () => designer.removeNode(designer.selectedId), style: { ...btnStyle(false), color: 'var(--nice-danger, #ff6b6b)' }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "trash", size: 14 }), " ", t('uiDesigner.delete', 'Delete')] })), jsxRuntime.jsxs("button", { onClick: () => setShowCode((v) => !v), style: btnStyle(showCode), children: ['</>', " ", t('uiDesigner.code', 'Code')] }), jsxRuntime.jsx("button", { onClick: () => onExport === null || onExport === void 0 ? void 0 : onExport(designer.exportCode()), style: {
452
456
  padding: '4px 8px',
453
457
  background: 'var(--nice-primary, #228be6)',
454
458
  color: 'var(--nice-text-on-primary, #fff)',
@@ -456,7 +460,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
456
460
  borderRadius: 3,
457
461
  cursor: 'pointer',
458
462
  fontSize: 11,
459
- }, children: "Export" })] }));
463
+ }, children: t('uiDesigner.export', 'Export') })] }));
460
464
  const mainContent = (jsxRuntime.jsx("div", { style: {
461
465
  flex: 1,
462
466
  display: 'flex',
@@ -472,7 +476,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
472
476
  margin: 0,
473
477
  overflow: 'auto',
474
478
  fontSize: 11,
475
- color: '#a6e3a1',
479
+ color: 'var(--nice-color-success, #a6e3a1)',
476
480
  fontFamily: 'monospace',
477
481
  }, children: designer.exportCode() })) : (jsxRuntime.jsx("div", { style: {
478
482
  flex: 1,
@@ -489,7 +493,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
489
493
  textAlign: 'center',
490
494
  padding: 40,
491
495
  color: 'var(--nice-text-secondary, #868e96)',
492
- }, children: "Drag or click a component to add it" })) : (designer.tree.map((n) => renderNode(n))) })) }));
496
+ }, children: t('uiDesigner.dragOrClickAComponent', 'Drag or click a component to add it') })) : (designer.tree.map((n) => renderNode(n))) })) }));
493
497
  const rightPanel = (jsxRuntime.jsxs("div", { style: {
494
498
  width: 200,
495
499
  fontSize: 11,
@@ -500,7 +504,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
500
504
  overflowY: 'auto',
501
505
  padding: 8,
502
506
  boxSizing: 'border-box',
503
- }, children: [jsxRuntime.jsx("strong", { style: { fontSize: 12 }, children: "Properties" }), selectedNode ? (jsxRuntime.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [jsxRuntime.jsxs("div", { style: {
507
+ }, children: [jsxRuntime.jsx("strong", { style: { fontSize: 12 }, children: t('uiDesigner.properties', 'Properties') }), selectedNode ? (jsxRuntime.jsxs("div", { style: { display: 'flex', flexDirection: 'column', gap: 6 }, children: [jsxRuntime.jsxs("div", { style: {
504
508
  color: 'var(--nice-text-secondary, #868e96)',
505
509
  display: 'flex',
506
510
  alignItems: 'center',
@@ -509,13 +513,13 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
509
513
  var _a;
510
514
  const di = (_a = COMPONENT_DEFS.find((d) => d.type === selectedNode.type)) === null || _a === void 0 ? void 0 : _a.icon;
511
515
  return di ? jsxRuntime.jsx(uiCore.NiceIcon, { name: di, size: 14 }) : null;
512
- })(), ' ', selectedNode.type] }), selectedNode.props.text !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: "Text" }), jsxRuntime.jsx("input", { value: String(selectedNode.props.text), onChange: (e) => designer.updateNode(selectedNode.id, { props: { text: e.target.value } }), style: fieldStyle })] })), selectedNode.props.placeholder !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: "Placeholder" }), jsxRuntime.jsx("input", { value: String(selectedNode.props.placeholder), onChange: (e) => designer.updateNode(selectedNode.id, { props: { placeholder: e.target.value } }), style: fieldStyle })] })), selectedNode.props.level !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: "Heading Level" }), jsxRuntime.jsx("select", { value: Number(selectedNode.props.level), onChange: (e) => designer.updateNode(selectedNode.id, {
516
+ })(), ' ', selectedNode.type] }), selectedNode.props.text !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.text', 'Text') }), jsxRuntime.jsx("input", { value: String(selectedNode.props.text), onChange: (e) => designer.updateNode(selectedNode.id, { props: { text: e.target.value } }), style: fieldStyle })] })), selectedNode.props.placeholder !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.placeholder', 'Placeholder') }), jsxRuntime.jsx("input", { value: String(selectedNode.props.placeholder), onChange: (e) => designer.updateNode(selectedNode.id, { props: { placeholder: e.target.value } }), style: fieldStyle })] })), selectedNode.props.level !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.headingLevel', 'Heading Level') }), jsxRuntime.jsx("select", { value: Number(selectedNode.props.level), onChange: (e) => designer.updateNode(selectedNode.id, {
513
517
  props: { level: Number(e.target.value) },
514
- }), style: fieldStyle, children: [1, 2, 3, 4, 5, 6].map((v) => (jsxRuntime.jsxs("option", { value: v, children: ["H", v] }, v))) })] })), selectedNode.props.variant !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: "Variant" }), jsxRuntime.jsx("select", { value: String(selectedNode.props.variant), onChange: (e) => designer.updateNode(selectedNode.id, { props: { variant: e.target.value } }), style: fieldStyle, children: ['primary', 'secondary', 'outline', 'ghost'].map((v) => (jsxRuntime.jsx("option", { value: v, children: v }, v))) })] })), selectedNode.props.src !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: "Image URL" }), jsxRuntime.jsx("input", { value: String(selectedNode.props.src), onChange: (e) => designer.updateNode(selectedNode.id, { props: { src: e.target.value } }), style: fieldStyle })] })), selectedNode.props.alt !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: "Alt Text" }), jsxRuntime.jsx("input", { value: String(selectedNode.props.alt), onChange: (e) => designer.updateNode(selectedNode.id, { props: { alt: e.target.value } }), style: fieldStyle })] })), jsxRuntime.jsx("hr", { style: {
518
+ }), style: fieldStyle, children: [1, 2, 3, 4, 5, 6].map((v) => (jsxRuntime.jsxs("option", { value: v, children: ["H", v] }, v))) })] })), selectedNode.props.variant !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.variant', 'Variant') }), jsxRuntime.jsx("select", { value: String(selectedNode.props.variant), onChange: (e) => designer.updateNode(selectedNode.id, { props: { variant: e.target.value } }), style: fieldStyle, children: ['primary', 'secondary', 'outline', 'ghost'].map((v) => (jsxRuntime.jsx("option", { value: v, children: v }, v))) })] })), selectedNode.props.src !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.imageUrl', 'Image URL') }), jsxRuntime.jsx("input", { value: String(selectedNode.props.src), onChange: (e) => designer.updateNode(selectedNode.id, { props: { src: e.target.value } }), style: fieldStyle })] })), selectedNode.props.alt !== undefined && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("label", { children: t('uiDesigner.altText', 'Alt Text') }), jsxRuntime.jsx("input", { value: String(selectedNode.props.alt), onChange: (e) => designer.updateNode(selectedNode.id, { props: { alt: e.target.value } }), style: fieldStyle })] })), jsxRuntime.jsx("hr", { style: {
515
519
  border: 'none',
516
520
  borderTop: '1px solid var(--nice-border, #373a40)',
517
521
  margin: '4px 0',
518
- } }), jsxRuntime.jsx("strong", { style: { color: 'var(--nice-text-secondary, #868e96)' }, children: "Style" }), ['padding', 'margin', 'gap', 'borderRadius'].map((prop) => (jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [jsxRuntime.jsx("label", { style: { minWidth: 60 }, children: prop }), jsxRuntime.jsx("input", { type: "number", value: typeof selectedNode.style[prop] === 'number'
522
+ } }), jsxRuntime.jsx("strong", { style: { color: 'var(--nice-text-secondary, #868e96)' }, children: t('uiDesigner.style', 'Style') }), ['padding', 'margin', 'gap', 'borderRadius'].map((prop) => (jsxRuntime.jsxs("div", { style: { display: 'flex', alignItems: 'center', gap: 4 }, children: [jsxRuntime.jsx("label", { style: { minWidth: 60 }, children: prop }), jsxRuntime.jsx("input", { type: "number", value: typeof selectedNode.style[prop] === 'number'
519
523
  ? selectedNode.style[prop]
520
524
  : 0, onChange: (e) => designer.updateNode(selectedNode.id, {
521
525
  style: { [prop]: Number(e.target.value) },
@@ -545,7 +549,7 @@ const NiceUIDesigner = ({ onChange, onExport, className = '', tutorial, }) => {
545
549
  color: 'var(--nice-text-secondary, #5c5f66)',
546
550
  padding: 8,
547
551
  textAlign: 'center',
548
- }, children: "Select an element" }))] }));
552
+ }, children: t('uiDesigner.selectAnElement', 'Select an element') }))] }));
549
553
  return (jsxRuntime.jsxs("div", { className: `nice-ui-designer ${className}`, style: { position: 'relative', height: '100%' }, children: [jsxRuntime.jsx("span", { style: { position: 'absolute', top: 6, right: 6, zIndex: 20 }, children: jsxRuntime.jsx(ui.NiceTutorialButton, { steps: ui.resolveTutorial(tutorial, UI_DESIGNER_TOUR) }) }), jsxRuntime.jsx(ui.NiceAppShell, { flush: true, toolbar: toolbarContent, sidebar: leftPanel, rightSidebar: rightPanel, children: mainContent })] }));
550
554
  };
551
555
 
@@ -3,6 +3,7 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
  var uiCore = require('@nice2dev/ui-core');
5
5
  var React = require('react');
6
+ var i18n = require('../core/i18n.js');
6
7
 
7
8
  /* ── Default config ── */
8
9
  const DEFAULT_CONFIG = {
@@ -419,6 +420,7 @@ function useSvgToCode() {
419
420
  }
420
421
  /* ── Component ── */
421
422
  const NiceSvgToCode = ({ initialSvg = '', onConvert, className = '', }) => {
423
+ const { t } = i18n.useNiceTranslation();
422
424
  const { svgInput, setSvgInput, config, updateConfig, convert, result } = useSvgToCode();
423
425
  // Initialize with provided SVG
424
426
  React.useEffect(() => {
@@ -437,21 +439,21 @@ const NiceSvgToCode = ({ initialSvg = '', onConvert, className = '', }) => {
437
439
  navigator.clipboard.writeText(result.code);
438
440
  }
439
441
  };
440
- return (jsxRuntime.jsxs("div", { className: `nice-svg-to-code ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsxs("strong", { children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "refresh", size: 14 }), " SVG \u2192 Code"] }), jsxRuntime.jsx("textarea", { value: svgInput, onChange: (e) => setSvgInput(e.target.value), placeholder: "Paste SVG markup here\u2026", rows: 6, style: {
442
+ return (jsxRuntime.jsxs("div", { className: `nice-svg-to-code ${className}`, style: { display: 'grid', gap: 8, fontSize: 13 }, children: [jsxRuntime.jsxs("strong", { children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "refresh", size: 14 }), " ", t('svgToCode.svgCode', 'SVG → Code')] }), jsxRuntime.jsx("textarea", { value: svgInput, onChange: (e) => setSvgInput(e.target.value), placeholder: t('svgToCode.pasteSvgMarkupHere', 'Paste SVG markup here…'), rows: 6, style: {
441
443
  width: '100%',
442
444
  fontFamily: 'monospace',
443
445
  fontSize: 11,
444
446
  padding: 6,
445
447
  borderRadius: 4,
446
448
  border: '1px solid var(--nice-border, #e2e8f0)',
447
- } }), jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: ["Framework:", jsxRuntime.jsxs("select", { value: config.framework, onChange: (e) => updateConfig({ framework: e.target.value }), style: { width: '100%' }, children: [jsxRuntime.jsx("option", { value: "react", children: "React (JSX/TSX)" }), jsxRuntime.jsx("option", { value: "vue", children: "Vue (SFC)" }), jsxRuntime.jsx("option", { value: "web-component", children: "Web Component" }), jsxRuntime.jsx("option", { value: "typescript", children: "TypeScript (utility)" })] })] }), jsxRuntime.jsxs("label", { children: ["Component name:", jsxRuntime.jsx("input", { type: "text", value: config.componentName, onChange: (e) => updateConfig({ componentName: e.target.value.replace(/[^a-zA-Z0-9]/g, '') }), style: { width: '100%', padding: '3px 6px' } })] }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 8, flexWrap: 'wrap', fontSize: 11 }, children: [jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.typescript, onChange: (e) => updateConfig({ typescript: e.target.checked }) }), ' ', "TypeScript"] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.optimize, onChange: (e) => updateConfig({ optimize: e.target.checked }) }), ' ', "Optimize"] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.sizeProps, onChange: (e) => updateConfig({ sizeProps: e.target.checked }) }), ' ', "Size props"] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.useCurrentColor, onChange: (e) => updateConfig({ useCurrentColor: e.target.checked }) }), ' ', "currentColor"] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.memo, onChange: (e) => updateConfig({ memo: e.target.checked }) }), ' ', "memo"] })] })] }), jsxRuntime.jsxs("button", { type: "button", onClick: handleConvert, disabled: !svgInput.trim(), style: {
449
+ } }), jsxRuntime.jsxs("div", { style: { display: 'grid', gap: 4 }, children: [jsxRuntime.jsxs("label", { children: [t('svgToCode.framework', 'Framework:'), jsxRuntime.jsxs("select", { value: config.framework, onChange: (e) => updateConfig({ framework: e.target.value }), style: { width: '100%' }, children: [jsxRuntime.jsx("option", { value: "react", children: "React (JSX/TSX)" }), jsxRuntime.jsx("option", { value: "vue", children: "Vue (SFC)" }), jsxRuntime.jsx("option", { value: "web-component", children: t('svgToCode.webComponent', 'Web Component') }), jsxRuntime.jsx("option", { value: "typescript", children: t('svgToCode.typescriptUtility', 'TypeScript (utility)') })] })] }), jsxRuntime.jsxs("label", { children: [t('svgToCode.componentName', 'Component name:'), jsxRuntime.jsx("input", { type: "text", value: config.componentName, onChange: (e) => updateConfig({ componentName: e.target.value.replace(/[^a-zA-Z0-9]/g, '') }), style: { width: '100%', padding: '3px 6px' } })] }), jsxRuntime.jsxs("div", { style: { display: 'flex', gap: 8, flexWrap: 'wrap', fontSize: 11 }, children: [jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.typescript, onChange: (e) => updateConfig({ typescript: e.target.checked }) }), ' ', t('svgToCode.typescript', 'TypeScript')] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.optimize, onChange: (e) => updateConfig({ optimize: e.target.checked }) }), ' ', t('svgToCode.optimize', 'Optimize')] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.sizeProps, onChange: (e) => updateConfig({ sizeProps: e.target.checked }) }), ' ', t('svgToCode.sizeProps', 'Size props')] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.useCurrentColor, onChange: (e) => updateConfig({ useCurrentColor: e.target.checked }) }), ' ', "currentColor"] }), jsxRuntime.jsxs("label", { children: [jsxRuntime.jsx("input", { type: "checkbox", checked: config.memo, onChange: (e) => updateConfig({ memo: e.target.checked }) }), ' ', "memo"] })] })] }), jsxRuntime.jsxs("button", { type: "button", onClick: handleConvert, disabled: !svgInput.trim(), style: {
448
450
  background: 'var(--nice-color-accent, #6366f1)',
449
451
  color: 'var(--nice-text-on-primary, #fff)',
450
452
  border: 'none',
451
453
  borderRadius: 4,
452
454
  padding: '8px 12px',
453
455
  cursor: 'pointer',
454
- }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "refresh", size: 14 }), " Convert to", ' ', config.framework.charAt(0).toUpperCase() + config.framework.slice(1)] }), result && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { style: {
456
+ }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "refresh", size: 14 }), ' ', t('svgToCode.convertTo', 'Convert to {framework}').replace('{framework}', config.framework.charAt(0).toUpperCase() + config.framework.slice(1))] }), result && (jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { style: {
455
457
  display: 'flex',
456
458
  justifyContent: 'space-between',
457
459
  alignItems: 'center',
@@ -463,7 +465,7 @@ const NiceSvgToCode = ({ initialSvg = '', onConvert, className = '', }) => {
463
465
  borderRadius: 3,
464
466
  border: '1px solid var(--nice-border, #e2e8f0)',
465
467
  background: 'transparent',
466
- }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "clipboard", size: 12 }), " Copy"] })] }), jsxRuntime.jsx("pre", { style: {
468
+ }, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "clipboard", size: 12 }), " ", t('svgToCode.copy', 'Copy')] })] }), jsxRuntime.jsx("pre", { style: {
467
469
  background: 'var(--nice-code-bg, #1e293b)',
468
470
  color: 'var(--nice-code-color, #e2e8f0)',
469
471
  padding: 8,
@@ -472,7 +474,7 @@ const NiceSvgToCode = ({ initialSvg = '', onConvert, className = '', }) => {
472
474
  overflow: 'auto',
473
475
  maxHeight: 300,
474
476
  whiteSpace: 'pre-wrap',
475
- }, children: result.code }), result.extractedColors.length > 0 && (jsxRuntime.jsxs("div", { style: { fontSize: 11, marginTop: 4 }, children: ["Kolory:", ' ', result.extractedColors.map((c) => (jsxRuntime.jsx("span", { style: {
477
+ }, children: result.code }), result.extractedColors.length > 0 && (jsxRuntime.jsxs("div", { style: { fontSize: 11, marginTop: 4 }, children: [t('svgToCode.colors', 'Colors:'), ' ', result.extractedColors.map((c) => (jsxRuntime.jsx("span", { style: {
476
478
  display: 'inline-block',
477
479
  width: 12,
478
480
  height: 12,
@@ -4,6 +4,7 @@ var jsxRuntime = require('react/jsx-runtime');
4
4
  var ui = require('@nice2dev/ui');
5
5
  var uiCore = require('@nice2dev/ui-core');
6
6
  var React = require('react');
7
+ var editorAsk = require('../core/editorAsk.js');
7
8
  var i18n = require('../core/i18n.js');
8
9
  var useVectorEditor = require('./useVectorEditor.js');
9
10
  var vectorAnimation = require('./vectorAnimation.js');
@@ -65,7 +66,7 @@ const NiceVectorEditor = ({ width: artW = 512, height: artH = 512, className, on
65
66
  }, [initialIcon, artW, artH, api]);
66
67
  const { shapes, selId, tool, zoom, px, py, dlgSvg, svgText, gridSize, boxRef, fInp, animations, previewTime, isPlaying, onDown, onMove, onUp, onWheel, onDblClick, openFile, switchTool, importSVGString, setDlgSvg, setSvgText, } = api;
67
68
  const toolRail = (jsxRuntime.jsx("div", { className: VectorEditor_module.toolbar, children: vectorEditorTypes.TOOLS.map((t) => (jsxRuntime.jsx(ui.NiceButton, { className: `${VectorEditor_module.toolBtn} ${tool === t.id ? VectorEditor_module.toolBtnActive : ''}`, onClick: () => switchTool(t.id), "aria-label": `${t.label} (${t.key})`, variant: tool === t.id ? 'primary' : 'ghost', size: "sm", children: jsxRuntime.jsx(uiCore.NiceIcon, { name: t.icon, size: "sm" }) }, t.id))) }));
68
- return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("input", { ref: fInp, type: "file", accept: ".svg", style: { display: 'none' }, onChange: (e) => {
69
+ return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(editorAsk.EditorAskDialog, { ask: api.pendingAsk }), jsxRuntime.jsx("input", { ref: fInp, type: "file", accept: ".svg", style: { display: 'none' }, onChange: (e) => {
69
70
  var _a;
70
71
  const f = (_a = e.target.files) === null || _a === void 0 ? void 0 : _a[0];
71
72
  if (f) {
@@ -111,7 +112,7 @@ const NiceVectorEditor = ({ width: artW = 512, height: artH = 512, className, on
111
112
  }
112
113
  const node = (jsxRuntime.jsx(VectorEditorShapeRenderer.ShapeRenderer, { shape: s, selected: s.id === selId, zoom: zoom }, s.id));
113
114
  return previewTr ? (jsxRuntime.jsx("g", { transform: previewTr, children: node }, s.id)) : (node);
114
- })] }) }) }), jsxRuntime.jsxs(ui.NiceModal, { open: dlgSvg, onClose: () => setDlgSvg(false), title: "Paste SVG", children: [jsxRuntime.jsx("label", { children: "Paste SVG source code below:" }), jsxRuntime.jsx(ui.NiceTextArea, { value: svgText, onChange: (val) => setSvgText(val), placeholder: '<svg xmlns="http://www.w3.org/2000/svg" ...>' }), jsxRuntime.jsxs("div", { className: VectorEditor_module.dialogActions, children: [jsxRuntime.jsx(ui.NiceButton, { variant: "ghost", onClick: () => setDlgSvg(false), children: "Cancel" }), jsxRuntime.jsx(ui.NiceButton, { variant: "primary", onClick: () => importSVGString(svgText), children: "Import" })] })] }), jsxRuntime.jsxs(ui.NiceModal, { open: snippetOpen, onClose: () => setSnippetOpen(false), title: t('vectorEditor.snippet.title', 'Icon snippet copied to clipboard'), children: [jsxRuntime.jsxs("p", { style: { marginTop: 0 }, children: [t('vectorEditor.snippet.pasteHint', 'Paste into'), ' ', jsxRuntime.jsxs("code", { children: ["packages/icons/src/icons/", iconCategory ? `${iconCategory.toLowerCase()}` : '<category>', ".tsx"] }), ":"] }), jsxRuntime.jsx(ui.NiceTextArea, { value: snippetText, onChange: (val) => setSnippetText(val), rows: 16 }), jsxRuntime.jsxs("div", { className: VectorEditor_module.dialogActions, children: [jsxRuntime.jsx(ui.NiceButton, { variant: "primary", onClick: () => {
115
+ })] }) }) }), jsxRuntime.jsxs(ui.NiceModal, { open: dlgSvg, onClose: () => setDlgSvg(false), title: t('vectorEditor.pasteSvg', 'Paste SVG'), children: [jsxRuntime.jsx("label", { children: t('vectorEditor.pasteSvgSourceCodeBelow', 'Paste SVG source code below:') }), jsxRuntime.jsx(ui.NiceTextArea, { value: svgText, onChange: (val) => setSvgText(val), placeholder: t('vectorEditor.svgXmlnsHttpWwwW3', '<svg xmlns="http://www.w3.org/2000/svg" ...>') }), jsxRuntime.jsxs("div", { className: VectorEditor_module.dialogActions, children: [jsxRuntime.jsx(ui.NiceButton, { variant: "ghost", onClick: () => setDlgSvg(false), children: t('vectorEditor.cancel', 'Cancel') }), jsxRuntime.jsx(ui.NiceButton, { variant: "primary", onClick: () => importSVGString(svgText), children: t('vectorEditor.import', 'Import') })] })] }), jsxRuntime.jsxs(ui.NiceModal, { open: snippetOpen, onClose: () => setSnippetOpen(false), title: t('vectorEditor.snippet.title', 'Icon snippet copied to clipboard'), children: [jsxRuntime.jsxs("p", { style: { marginTop: 0 }, children: [t('vectorEditor.snippet.pasteHint', 'Paste into'), ' ', jsxRuntime.jsxs("code", { children: ["packages/icons/src/icons/", iconCategory ? `${iconCategory.toLowerCase()}` : '<category>', ".tsx"] }), ":"] }), jsxRuntime.jsx(ui.NiceTextArea, { value: snippetText, onChange: (val) => setSnippetText(val), rows: 16 }), jsxRuntime.jsxs("div", { className: VectorEditor_module.dialogActions, children: [jsxRuntime.jsx(ui.NiceButton, { variant: "primary", onClick: () => {
115
116
  if (typeof navigator !== 'undefined' && navigator.clipboard) {
116
117
  navigator.clipboard.writeText(snippetText).catch(() => { });
117
118
  }
@@ -264,7 +264,13 @@ const VectorEditorMenuBar = ({ api, hasOnSaveToLibrary, onExportIconSnippet }) =
264
264
  { value: '', label: t('vectorEditor.menu.secondShape', '2nd Shape') },
265
265
  ...shapes
266
266
  .filter((s) => s.id !== selId)
267
- .map((s, i) => { var _a; return ({ value: s.id, label: (_a = s.name) !== null && _a !== void 0 ? _a : `Shape ${i + 1}` }); }),
267
+ .map((s, i) => {
268
+ var _a;
269
+ return ({
270
+ value: s.id,
271
+ label: (_a = s.name) !== null && _a !== void 0 ? _a : t('vectorEditor.shape', 'Shape {i}').replace('{i}', String(i + 1)),
272
+ });
273
+ }),
268
274
  ], style: { minWidth: 100 } })), jsxRuntime.jsxs("label", { className: VectorEditor_module.menuLabel, children: [jsxRuntime.jsx(ui.NiceCheckbox, { checked: snapGrid, onChange: () => setSnapGrid((v) => !v) }), t('vectorEditor.menu.snap', 'Snap')] }), onExportIconSnippet && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { className: VectorEditor_module.menuSep }), jsxRuntime.jsxs(ui.NiceButton, { className: VectorEditor_module.menuBtn, variant: "primary", size: "sm", onClick: onExportIconSnippet, children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: "clipboard", size: 14 }), ' ', t('vectorEditor.menu.exportIcon', 'Export Icon')] })] }))] }));
269
275
  };
270
276
  var VectorEditorMenuBar$1 = React.memo(VectorEditorMenuBar);
@@ -46,7 +46,7 @@ const VectorEditorRightPanel = ({ api, library, artW = 512, artH = 512 }) => {
46
46
  border: (selectedGradient === null || selectedGradient === void 0 ? void 0 : selectedGradient.id) === grad.id
47
47
  ? '2px solid var(--nice-bg, #FFF)'
48
48
  : '1px solid var(--nice-border-color, #444)',
49
- }, onClick: () => applyGradient(grad), title: `${grad.type} gradient` }, i))) }), jsxRuntime.jsx("div", { className: VectorEditor_module.propRow, children: jsxRuntime.jsxs(ui.NiceButton, { className: VectorEditor_module.menuBtn, variant: "ghost", size: "sm", style: { width: '100%', fontSize: 10 }, onClick: () => setShowGradientEditor(!showGradientEditor), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: showGradientEditor ? 'chevron-down' : 'chevron-right', size: 14 }), ' ', showGradientEditor
49
+ }, onClick: () => applyGradient(grad), title: t('vectorEditor.gradient', '{type} gradient').replace('{type}', String(grad.type)) }, i))) }), jsxRuntime.jsx("div", { className: VectorEditor_module.propRow, children: jsxRuntime.jsxs(ui.NiceButton, { className: VectorEditor_module.menuBtn, variant: "ghost", size: "sm", style: { width: '100%', fontSize: 10 }, onClick: () => setShowGradientEditor(!showGradientEditor), children: [jsxRuntime.jsx(uiCore.NiceIcon, { name: showGradientEditor ? 'chevron-down' : 'chevron-right', size: 14 }), ' ', showGradientEditor
50
50
  ? t('vectorEditor.properties.hideEditor', 'Hide Editor')
51
51
  : t('vectorEditor.properties.editGradient', 'Edit Gradient')] }) }), showGradientEditor && selectedGradient && (jsxRuntime.jsxs("div", { style: {
52
52
  padding: 4,
@@ -8,7 +8,9 @@ const ShapeRenderer = ({ shape: s, selected, zoom }) => {
8
8
  if (s.hidden) {
9
9
  return null;
10
10
  }
11
- const selStroke = selected ? 'rgba(85,102,204,0.6)' : undefined;
11
+ const selStroke = selected
12
+ ? 'color-mix(in srgb, var(--nice-color-primary, #5566cc) 60%, transparent)'
13
+ : undefined;
12
14
  const gProps = {};
13
15
  if (s.opacity < 1) {
14
16
  gProps.opacity = s.opacity;
@@ -1,6 +1,7 @@
1
1
  'use strict';
2
2
 
3
3
  var React = require('react');
4
+ var editorAsk = require('../core/editorAsk.js');
4
5
  var vectorBooleanOps = require('./vectorBooleanOps.js');
5
6
  var vectorEditorExport = require('./vectorEditorExport.js');
6
7
  var vectorEditorImport = require('./vectorEditorImport.js');
@@ -12,10 +13,13 @@ var vectorEditorTypes = require('./vectorEditorTypes.js');
12
13
  * so the component file is pure layout.
13
14
  */
14
15
  function useVectorEditor({ artW, artH, onSaveToLibrary }) {
16
+ // the library's dialogs for the text tool and the library name (not window.prompt)
17
+ const { pendingAsk, ask } = editorAsk.useEditorAsk();
15
18
  /* ── state ── */
16
19
  const [shapes, setShapes] = React.useState([]);
17
20
  const [selId, setSelId] = React.useState(null);
18
21
  const [tool, setTool] = React.useState('pointer');
22
+ // audit-allow: literal-color — the first fill of a new shape - document data
19
23
  const [fillCol, setFillCol] = React.useState('#5566cc');
20
24
  const [strokeCol, setStrokeCol] = React.useState('#000000');
21
25
  const [strokeW, setStrokeW] = React.useState(2);
@@ -178,28 +182,30 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
178
182
  return;
179
183
  }
180
184
  if (tool === 'text') {
181
- const txt = prompt('Enter text:', 'Hello');
182
- if (!txt) {
183
- return;
184
- }
185
- const ns = {
186
- id: vectorEditorTypes.sid(),
187
- type: 'text',
188
- x: p.x,
189
- y: p.y,
190
- w: txt.length * fontSize * 0.6,
191
- h: fontSize,
192
- ...defaultBase,
193
- stroke: 'none',
194
- strokeWidth: 0,
195
- text: txt,
196
- fontSize,
197
- fontFamily,
198
- };
199
- const next = [...shapes, ns];
200
- snap(next);
201
- setShapes(next);
202
- setSelId(ns.id);
185
+ // the text comes from a dialog: the shape is added once it is entered
186
+ void ask('prompt', 'Enter text:', 'Hello').then((txt) => {
187
+ if (!txt) {
188
+ return;
189
+ }
190
+ const ns = {
191
+ id: vectorEditorTypes.sid(),
192
+ type: 'text',
193
+ x: p.x,
194
+ y: p.y,
195
+ w: txt.length * fontSize * 0.6,
196
+ h: fontSize,
197
+ ...defaultBase,
198
+ stroke: 'none',
199
+ strokeWidth: 0,
200
+ text: txt,
201
+ fontSize,
202
+ fontFamily,
203
+ };
204
+ const next = [...shapes, ns];
205
+ snap(next);
206
+ setShapes(next);
207
+ setSelId(ns.id);
208
+ });
203
209
  return;
204
210
  }
205
211
  if (tool === 'polygon') {
@@ -285,6 +291,7 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
285
291
  starPoints2,
286
292
  starInnerRatio,
287
293
  cornerRadius,
294
+ ask,
288
295
  ]);
289
296
  const onMove = React.useCallback((e) => {
290
297
  if (panning.current) {
@@ -699,15 +706,15 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
699
706
  const blob = new Blob([svg], { type: 'image/svg+xml' });
700
707
  const reader = new FileReader();
701
708
  reader.onload = () => {
702
- const defaultName = 'drawing.svg';
703
- const name = window.prompt('Save to library as:', defaultName);
704
- if (!name || !name.trim()) {
705
- return;
706
- }
707
- onSaveToLibrary(reader.result, name.trim(), 'image/svg+xml');
709
+ const dataUrl = reader.result;
710
+ void ask('prompt', 'Save to library as:', 'drawing.svg').then((name) => {
711
+ if (name && name.trim()) {
712
+ onSaveToLibrary(dataUrl, name.trim(), 'image/svg+xml');
713
+ }
714
+ });
708
715
  };
709
716
  reader.readAsDataURL(blob);
710
- }, [shapes, artW, artH, onSaveToLibrary]);
717
+ }, [shapes, artW, artH, onSaveToLibrary, ask]);
711
718
  /* ── export shortcuts ── */
712
719
  const exportSVG = React.useCallback(() => vectorEditorExport.exportSVGFile(shapes, artW, artH, animations), [shapes, artW, artH, animations]);
713
720
  const exportPNG = React.useCallback(() => vectorEditorExport.exportPNGFile(shapes, artW, artH), [shapes, artW, artH]);
@@ -793,6 +800,8 @@ function useVectorEditor({ artW, artH, onSaveToLibrary }) {
793
800
  /* ── return ── */
794
801
  return {
795
802
  // state
803
+ /** The question the editor waits on; render it with `EditorAskDialog`. */
804
+ pendingAsk,
796
805
  shapes,
797
806
  selId,
798
807
  tool,
@@ -18,6 +18,7 @@ function parseSVGToShapes(raw) {
18
18
  const elems = doc.querySelectorAll('rect, ellipse, circle, line, polyline, polygon, text, path');
19
19
  elems.forEach((el) => {
20
20
  const tag = el.tagName.toLowerCase();
21
+ // audit-allow: literal-color — an imported shape without a fill gets a visible default - document data
21
22
  const fill = el.getAttribute('fill') || (tag === 'line' ? 'none' : '#cccccc');
22
23
  const stroke = el.getAttribute('stroke') || 'none';
23
24
  const sw = parseFloat(el.getAttribute('stroke-width') || '1');
@@ -39,6 +39,7 @@ function iconComponentToShapes(Icon, options) {
39
39
  // Force a generous size and visible colors so the parser sees concrete strokes/fills.
40
40
  const renderProps = {
41
41
  size: 'lg',
42
+ // audit-allow: literal-color +2 — an imported icon's default colours - document data
42
43
  primaryColor: '#5566cc',
43
44
  secondaryColor: '#a29bfe',
44
45
  strokeWidth: 2,
@@ -1,6 +1,7 @@
1
1
  import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
2
2
  import { motion } from 'framer-motion';
3
3
  import { useMemo, useEffect } from 'react';
4
+ import { useNiceTranslation } from '../core/i18n.js';
4
5
  import { usePersonControls, playPose } from './animationHelper.js';
5
6
  import { DEFAULT_CHARACTER, pick } from './characterTypes.js';
6
7
 
@@ -9,6 +10,7 @@ const NiceAnimatedPerson = ({ id, name, size, score = 10, startPose = 'idle', on
9
10
  // colors prop is destructured for API completeness but not yet fully wired into character defaults.
10
11
  }) => {
11
12
  var _a, _b, _c, _d, _e;
13
+ const { t } = useNiceTranslation();
12
14
  const fx = usePersonControls();
13
15
  const effectiveStartPose = pose !== null && pose !== void 0 ? pose : startPose;
14
16
  const effectiveCharacter = character !== null && character !== void 0 ? character : config;
@@ -20,7 +22,7 @@ const NiceAnimatedPerson = ({ id, name, size, score = 10, startPose = 'idle', on
20
22
  }, [fx, onReady, effectiveStartPose]);
21
23
  const W = (_c = (_b = (_a = cfg.size) !== null && _a !== void 0 ? _a : effectiveSize) !== null && _b !== void 0 ? _b : DEFAULT_CHARACTER.size) !== null && _c !== void 0 ? _c : 180;
22
24
  const skin = pick(cfg.face.colors, 0, 'var(--anim-skin, #FFD2B3)');
23
- return (jsx(motion.div, { id: id, style: { width: W, height: W * 1.35, overflow: 'visible' }, animate: fx.root, children: jsxs("svg", { viewBox: "0 0 180 240", width: "100%", height: "100%", role: "img", "aria-label": (_e = (_d = cfg.name) !== null && _d !== void 0 ? _d : name) !== null && _e !== void 0 ? _e : 'Juror', children: [jsxs(motion.g, { animate: fx.body, children: [jsx("rect", { x: "68", y: "158", width: "14", height: "46", rx: "6", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsx("ellipse", { cx: "75", cy: "204", rx: "10", ry: "5", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') }), jsx("rect", { x: "98", y: "158", width: "14", height: "46", rx: "6", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsx("ellipse", { cx: "105", cy: "204", rx: "10", ry: "5", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') })] }), jsx(motion.g, { animate: fx.body, children: renderOutfit(cfg.outfit) }), jsx("rect", { x: "84", y: "82", width: "12", height: "12", rx: "4", fill: skin }), jsxs(motion.g, { animate: fx.leftArm, style: { transformOrigin: '64px 96px' }, children: [jsx("rect", { x: "36", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsx("circle", { cx: "44", cy: "136", r: "7", fill: skin })] }), jsxs(motion.g, { animate: fx.rightArm, style: { transformOrigin: '116px 96px' }, children: [jsx("rect", { x: "128", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsx("circle", { cx: "136", cy: "136", r: "7", fill: skin }), jsxs(motion.g, { animate: fx.card, children: [jsx("rect", { x: "126", y: "50", width: "42", height: "30", rx: "4", fill: "var(--nice-color-surface, #fff)", stroke: "var(--nice-text-primary, #111)", strokeWidth: 1.2 }), jsx("text", { x: "147", y: "70", textAnchor: "middle", fontSize: "16", fontWeight: 700, fill: "var(--nice-text-primary, #111)", children: String(score) })] }), renderProp(cfg.prop)] }), jsxs(motion.g, { animate: fx.head, children: [renderHairBack(cfg.hair), renderFace(cfg.face), jsx(motion.g, { animate: fx.eyes, children: renderEyes(cfg.eyes) }), renderNose(cfg.nose), jsx(motion.g, { animate: fx.mouth, children: renderMouth(cfg.mouth) }), renderHeadwear(cfg.headwear)] }), jsxs(motion.g, { animate: fx.curse, initial: { opacity: 0 }, children: [jsx("rect", { x: "62", y: "10", width: "56", height: "22", rx: "8", fill: "var(--nice-text-primary, #111)" }), jsx("text", { x: "90", y: "26", textAnchor: "middle", fontSize: "12", fill: "var(--nice-text-inverse, #fff)", children: "#!%&*" }), jsx("path", { d: "M82 32 l4 8 l4 -8", fill: "var(--nice-text-primary, #111)" })] })] }) }));
25
+ return (jsx(motion.div, { id: id, style: { width: W, height: W * 1.35, overflow: 'visible' }, animate: fx.root, children: jsxs("svg", { viewBox: "0 0 180 240", width: "100%", height: "100%", role: "img", "aria-label": (_e = (_d = cfg.name) !== null && _d !== void 0 ? _d : name) !== null && _e !== void 0 ? _e : t('animatedPerson.juror', 'Juror'), children: [jsxs(motion.g, { animate: fx.body, children: [jsx("rect", { x: "68", y: "158", width: "14", height: "46", rx: "6", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsx("ellipse", { cx: "75", cy: "204", rx: "10", ry: "5", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') }), jsx("rect", { x: "98", y: "158", width: "14", height: "46", rx: "6", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg, #334155)') }), jsx("ellipse", { cx: "105", cy: "204", rx: "10", ry: "5", fill: pick(cfg.outfit.colors, 1, 'var(--anim-outfit-leg-shadow, #1E293B)') })] }), jsx(motion.g, { animate: fx.body, children: renderOutfit(cfg.outfit) }), jsx("rect", { x: "84", y: "82", width: "12", height: "12", rx: "4", fill: skin }), jsxs(motion.g, { animate: fx.leftArm, style: { transformOrigin: '64px 96px' }, children: [jsx("rect", { x: "36", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsx("circle", { cx: "44", cy: "136", r: "7", fill: skin })] }), jsxs(motion.g, { animate: fx.rightArm, style: { transformOrigin: '116px 96px' }, children: [jsx("rect", { x: "128", y: "96", width: "16", height: "36", rx: "7", fill: skin }), jsx("circle", { cx: "136", cy: "136", r: "7", fill: skin }), jsxs(motion.g, { animate: fx.card, children: [jsx("rect", { x: "126", y: "50", width: "42", height: "30", rx: "4", fill: "var(--nice-color-surface, #fff)", stroke: "var(--nice-text-primary, #111)", strokeWidth: 1.2 }), jsx("text", { x: "147", y: "70", textAnchor: "middle", fontSize: "16", fontWeight: 700, fill: "var(--nice-text-primary, #111)", children: String(score) })] }), renderProp(cfg.prop)] }), jsxs(motion.g, { animate: fx.head, children: [renderHairBack(cfg.hair), renderFace(cfg.face), jsx(motion.g, { animate: fx.eyes, children: renderEyes(cfg.eyes) }), renderNose(cfg.nose), jsx(motion.g, { animate: fx.mouth, children: renderMouth(cfg.mouth) }), renderHeadwear(cfg.headwear)] }), jsxs(motion.g, { animate: fx.curse, initial: { opacity: 0 }, children: [jsx("rect", { x: "62", y: "10", width: "56", height: "22", rx: "8", fill: "var(--nice-text-primary, #111)" }), jsx("text", { x: "90", y: "26", textAnchor: "middle", fontSize: "12", fill: "var(--nice-text-inverse, #fff)", children: "#!%&*" }), jsx("path", { d: "M82 32 l4 8 l4 -8", fill: "var(--nice-text-primary, #111)" })] })] }) }));
24
26
  };
25
27
  /* ---------------- RENDERERS (redesigned proportions) ---------------- */
26
28
  function renderOutfit(cfg) {
@@ -1,6 +1,7 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { NiceButton } from '@nice2dev/ui';
3
3
  import React from 'react';
4
+ import { useNiceTranslation } from '../core/i18n.js';
4
5
  import NiceAnimatedPerson from './AnimatedPerson.js';
5
6
  import { DEFAULT_CHARACTER } from './characterTypes.js';
6
7
  import { runRounds, seq } from './choreoDSL.js';
@@ -33,6 +34,7 @@ function makeAudience(n) {
33
34
  }
34
35
  /** Grid of animated audience members with wave/cheer actions. */
35
36
  const NiceAudience = ({ count = 36, columns = 12 }) => {
37
+ const { t } = useNiceTranslation();
36
38
  const people = React.useMemo(() => makeAudience(count), [count]);
37
39
  const refs = React.useRef([]);
38
40
  refs.current = [];
@@ -57,7 +59,7 @@ const NiceAudience = ({ count = 36, columns = 12 }) => {
57
59
  display: 'grid',
58
60
  gap: 8,
59
61
  gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,
60
- }, children: people.map((c, i) => (jsx(NiceAnimatedPerson, { character: c, score: 10, startPose: "idle", onReady: (fx) => (refs.current[i] = fx) }, i))) }), jsxs("div", { style: { display: 'flex', gap: 8 }, children: [jsx(NiceButton, { variant: "ghost", size: "sm", onClick: wave, children: "Wave" }), jsx(NiceButton, { variant: "ghost", size: "sm", onClick: cheer, children: "Cheer" })] })] }));
62
+ }, children: people.map((c, i) => (jsx(NiceAnimatedPerson, { character: c, score: 10, startPose: "idle", onReady: (fx) => (refs.current[i] = fx) }, i))) }), jsxs("div", { style: { display: 'flex', gap: 8 }, children: [jsx(NiceButton, { variant: "ghost", size: "sm", onClick: wave, children: t('audience.wave', 'Wave') }), jsx(NiceButton, { variant: "ghost", size: "sm", onClick: cheer, children: t('audience.cheer', 'Cheer') })] })] }));
61
63
  };
62
64
 
63
65
  export { NiceAudience as default };
@@ -7,7 +7,7 @@ import NiceRiggedBody from './RiggedBody.js';
7
7
  import { solveFK } from './rigMath.js';
8
8
  import { DEFAULT_POSE, DEFAULT_RIG } from './rigTypes.js';
9
9
 
10
- const NiceRigPlayer = React.forwardRef(({ character, rig, pose, size, debug }, ref) => {
10
+ const NiceRigPlayer = /* @__PURE__ */ React.forwardRef(({ character, rig, pose, size, debug }, ref) => {
11
11
  var _a;
12
12
  const [current, setCurrent] = React.useState({ ...DEFAULT_POSE, ...(pose !== null && pose !== void 0 ? pose : {}) });
13
13
  const r = rig !== null && rig !== void 0 ? rig : DEFAULT_RIG;