@sequent-org/moodboard 1.0.0

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 (123) hide show
  1. package/package.json +44 -0
  2. package/src/assets/icons/README.md +105 -0
  3. package/src/assets/icons/attachments.svg +3 -0
  4. package/src/assets/icons/clear.svg +5 -0
  5. package/src/assets/icons/comments.svg +3 -0
  6. package/src/assets/icons/emoji.svg +6 -0
  7. package/src/assets/icons/frame.svg +3 -0
  8. package/src/assets/icons/image.svg +3 -0
  9. package/src/assets/icons/note.svg +3 -0
  10. package/src/assets/icons/pan.svg +3 -0
  11. package/src/assets/icons/pencil.svg +3 -0
  12. package/src/assets/icons/redo.svg +3 -0
  13. package/src/assets/icons/select.svg +9 -0
  14. package/src/assets/icons/shapes.svg +3 -0
  15. package/src/assets/icons/text-add.svg +3 -0
  16. package/src/assets/icons/topbar/README.md +39 -0
  17. package/src/assets/icons/topbar/grid-cross.svg +6 -0
  18. package/src/assets/icons/topbar/grid-dot.svg +3 -0
  19. package/src/assets/icons/topbar/grid-line.svg +3 -0
  20. package/src/assets/icons/topbar/grid-off.svg +3 -0
  21. package/src/assets/icons/topbar/paint.svg +3 -0
  22. package/src/assets/icons/undo.svg +3 -0
  23. package/src/core/ApiClient.js +309 -0
  24. package/src/core/EventBus.js +42 -0
  25. package/src/core/HistoryManager.js +261 -0
  26. package/src/core/KeyboardManager.js +710 -0
  27. package/src/core/PixiEngine.js +439 -0
  28. package/src/core/SaveManager.js +381 -0
  29. package/src/core/StateManager.js +64 -0
  30. package/src/core/commands/BaseCommand.js +68 -0
  31. package/src/core/commands/CopyObjectCommand.js +44 -0
  32. package/src/core/commands/CreateObjectCommand.js +46 -0
  33. package/src/core/commands/DeleteObjectCommand.js +146 -0
  34. package/src/core/commands/EditFileNameCommand.js +107 -0
  35. package/src/core/commands/GroupMoveCommand.js +47 -0
  36. package/src/core/commands/GroupReorderZCommand.js +74 -0
  37. package/src/core/commands/GroupResizeCommand.js +37 -0
  38. package/src/core/commands/GroupRotateCommand.js +41 -0
  39. package/src/core/commands/MoveObjectCommand.js +89 -0
  40. package/src/core/commands/PasteObjectCommand.js +103 -0
  41. package/src/core/commands/ReorderZCommand.js +45 -0
  42. package/src/core/commands/ResizeObjectCommand.js +135 -0
  43. package/src/core/commands/RotateObjectCommand.js +70 -0
  44. package/src/core/commands/index.js +14 -0
  45. package/src/core/events/Events.js +147 -0
  46. package/src/core/index.js +1632 -0
  47. package/src/core/rendering/GeometryUtils.js +89 -0
  48. package/src/core/rendering/HitTestManager.js +186 -0
  49. package/src/core/rendering/LayerManager.js +137 -0
  50. package/src/core/rendering/ObjectRenderer.js +363 -0
  51. package/src/core/rendering/PixiRenderer.js +140 -0
  52. package/src/core/rendering/index.js +9 -0
  53. package/src/grid/BaseGrid.js +164 -0
  54. package/src/grid/CrossGrid.js +75 -0
  55. package/src/grid/DotGrid.js +148 -0
  56. package/src/grid/GridFactory.js +173 -0
  57. package/src/grid/LineGrid.js +115 -0
  58. package/src/index.js +2 -0
  59. package/src/moodboard/ActionHandler.js +114 -0
  60. package/src/moodboard/DataManager.js +114 -0
  61. package/src/moodboard/MoodBoard.js +359 -0
  62. package/src/moodboard/WorkspaceManager.js +103 -0
  63. package/src/objects/BaseObject.js +1 -0
  64. package/src/objects/CommentObject.js +115 -0
  65. package/src/objects/DrawingObject.js +114 -0
  66. package/src/objects/EmojiObject.js +98 -0
  67. package/src/objects/FileObject.js +318 -0
  68. package/src/objects/FrameObject.js +127 -0
  69. package/src/objects/ImageObject.js +72 -0
  70. package/src/objects/NoteObject.js +227 -0
  71. package/src/objects/ObjectFactory.js +61 -0
  72. package/src/objects/ShapeObject.js +134 -0
  73. package/src/objects/StampObject.js +0 -0
  74. package/src/objects/StickerObject.js +0 -0
  75. package/src/objects/TextObject.js +123 -0
  76. package/src/services/BoardService.js +85 -0
  77. package/src/services/FileUploadService.js +398 -0
  78. package/src/services/FrameService.js +138 -0
  79. package/src/services/ImageUploadService.js +246 -0
  80. package/src/services/ZOrderManager.js +50 -0
  81. package/src/services/ZoomPanController.js +78 -0
  82. package/src/src.7z +0 -0
  83. package/src/src.zip +0 -0
  84. package/src/src2.zip +0 -0
  85. package/src/tools/AlignmentGuides.js +326 -0
  86. package/src/tools/BaseTool.js +257 -0
  87. package/src/tools/ResizeHandles.js +381 -0
  88. package/src/tools/ToolManager.js +580 -0
  89. package/src/tools/board-tools/PanTool.js +43 -0
  90. package/src/tools/board-tools/ZoomTool.js +393 -0
  91. package/src/tools/object-tools/DrawingTool.js +404 -0
  92. package/src/tools/object-tools/PlacementTool.js +1005 -0
  93. package/src/tools/object-tools/SelectTool.js +2183 -0
  94. package/src/tools/object-tools/TextTool.js +416 -0
  95. package/src/tools/object-tools/selection/BoxSelectController.js +105 -0
  96. package/src/tools/object-tools/selection/GeometryUtils.js +101 -0
  97. package/src/tools/object-tools/selection/GroupDragController.js +61 -0
  98. package/src/tools/object-tools/selection/GroupResizeController.js +90 -0
  99. package/src/tools/object-tools/selection/GroupRotateController.js +61 -0
  100. package/src/tools/object-tools/selection/HandlesSync.js +96 -0
  101. package/src/tools/object-tools/selection/ResizeController.js +68 -0
  102. package/src/tools/object-tools/selection/RotateController.js +58 -0
  103. package/src/tools/object-tools/selection/SelectionModel.js +42 -0
  104. package/src/tools/object-tools/selection/SimpleDragController.js +45 -0
  105. package/src/ui/CommentPopover.js +187 -0
  106. package/src/ui/ContextMenu.js +340 -0
  107. package/src/ui/FilePropertiesPanel.js +298 -0
  108. package/src/ui/FramePropertiesPanel.js +462 -0
  109. package/src/ui/HtmlHandlesLayer.js +778 -0
  110. package/src/ui/HtmlTextLayer.js +279 -0
  111. package/src/ui/MapPanel.js +290 -0
  112. package/src/ui/NotePropertiesPanel.js +502 -0
  113. package/src/ui/SaveStatus.js +250 -0
  114. package/src/ui/TextPropertiesPanel.js +911 -0
  115. package/src/ui/Toolbar.js +1118 -0
  116. package/src/ui/Topbar.js +220 -0
  117. package/src/ui/ZoomPanel.js +116 -0
  118. package/src/ui/styles/workspace.css +854 -0
  119. package/src/utils/colors.js +0 -0
  120. package/src/utils/geometry.js +0 -0
  121. package/src/utils/iconLoader.js +270 -0
  122. package/src/utils/objectIdGenerator.js +17 -0
  123. package/src/utils/topbarIconLoader.js +114 -0
@@ -0,0 +1,146 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ import { Events } from '../events/Events.js';
3
+
4
+ /**
5
+ * Команда удалСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°
6
+ */
7
+ export class DeleteObjectCommand extends BaseCommand {
8
+ constructor(coreMoodboard, objectId) {
9
+ super('delete_object', `Π£Π΄Π°Π»ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚`);
10
+ this.coreMoodboard = coreMoodboard;
11
+ this.objectId = objectId;
12
+
13
+ // БохраняСм Π΄Π°Π½Π½Ρ‹Π΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° для возмоТности восстановлСния
14
+ const objects = this.coreMoodboard.state.getObjects();
15
+ const originalData = objects.find(obj => obj.id === objectId);
16
+
17
+ if (!originalData) {
18
+ throw new Error(`Object with id ${objectId} not found`);
19
+ }
20
+
21
+ // Π”Π΅Π»Π°Π΅ΠΌ Π³Π»ΡƒΠ±ΠΎΠΊΡƒΡŽ копию Π΄Π°Π½Π½Ρ‹Ρ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°
22
+ this.objectData = JSON.parse(JSON.stringify(originalData));
23
+
24
+ // Для ΠΈΠ·ΠΎΠ±Ρ€Π°ΠΆΠ΅Π½ΠΈΠΉ убСдимся, Ρ‡Ρ‚ΠΎ Π΅ΡΡ‚ΡŒ src URL для восстановлСния
25
+ if (this.objectData.type === 'image') {
26
+ console.log('πŸ”§ DEBUG DeleteObjectCommand: исходныС Π΄Π°Π½Π½Ρ‹Π΅ изобраТСния:', {
27
+ id: this.objectData.id,
28
+ imageId: this.objectData.imageId,
29
+ src: this.objectData.src,
30
+ propertiesSrc: this.objectData.properties?.src,
31
+ hasBase64Src: !!(this.objectData.src && this.objectData.src.startsWith('data:')),
32
+ hasBase64Props: !!(this.objectData.properties?.src && this.objectData.properties.src.startsWith('data:'))
33
+ });
34
+
35
+ if (this.objectData.imageId) {
36
+ const imageUrl = `/api/images/${this.objectData.imageId}/file`;
37
+
38
+ // ВсСгда восстанавливаСм URL ΠΈΠ· imageId для Π³Π°Ρ€Π°Π½Ρ‚ΠΈΠΈ
39
+ // (ΠΌΠΎΠΆΠ΅Ρ‚ Π±Ρ‹Ρ‚ΡŒ ΡƒΠ΄Π°Π»Π΅Π½ ΠΏΡ€ΠΈ ΠΏΡ€Π΅Π΄Ρ‹Π΄ΡƒΡ‰ΠΈΡ… сохранСниях)
40
+ this.objectData.src = imageUrl;
41
+
42
+ if (!this.objectData.properties) {
43
+ this.objectData.properties = {};
44
+ }
45
+ this.objectData.properties.src = imageUrl;
46
+
47
+ console.log('πŸ”§ DEBUG DeleteObjectCommand: ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½Π½Ρ‹Π΅ Π΄Π°Π½Π½Ρ‹Π΅ изобраТСния:', {
48
+ id: this.objectData.id,
49
+ imageId: this.objectData.imageId,
50
+ src: this.objectData.src,
51
+ propertiesSrc: this.objectData.properties?.src
52
+ });
53
+ } else {
54
+ console.warn('πŸ”§ DEBUG DeleteObjectCommand: Ρƒ изобраТСния Π½Π΅Ρ‚ imageId, оставляСм ΠΊΠ°ΠΊ Π΅ΡΡ‚ΡŒ');
55
+ }
56
+ }
57
+
58
+ // Для Ρ„Π°ΠΉΠ»ΠΎΠ² сохраняСм ΠΈΠ½Ρ„ΠΎΡ€ΠΌΠ°Ρ†ΠΈΡŽ для Π²ΠΎΠ·ΠΌΠΎΠΆΠ½ΠΎΠΉ очистки с сСрвСра
59
+ if (this.objectData.type === 'file') {
60
+ console.log('πŸ”§ DEBUG DeleteObjectCommand: исходныС Π΄Π°Π½Π½Ρ‹Π΅ Ρ„Π°ΠΉΠ»Π°:', {
61
+ id: this.objectData.id,
62
+ fileId: this.objectData.fileId,
63
+ fileName: this.objectData.properties?.fileName
64
+ });
65
+
66
+ if (this.objectData.fileId) {
67
+ // БохраняСм fileId для удалСния с сСрвСра
68
+ this.fileIdToDelete = this.objectData.fileId;
69
+ console.log('πŸ”§ DEBUG DeleteObjectCommand: Ρ„Π°ΠΉΠ» Π±ΡƒΠ΄Π΅Ρ‚ ΡƒΠ΄Π°Π»Π΅Π½ с сСрвСра:', this.fileIdToDelete);
70
+ }
71
+ }
72
+
73
+ // ОбновляСм описаниС с Ρ‚ΠΈΠΏΠΎΠΌ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°
74
+ this.description = `Π£Π΄Π°Π»ΠΈΡ‚ΡŒ ${this.objectData.type}`;
75
+ }
76
+
77
+ async execute() {
78
+ // УдаляСм ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ΠΈΠ· состояния ΠΈ PIXI
79
+ this.coreMoodboard.state.removeObject(this.objectId);
80
+ this.coreMoodboard.pixi.removeObject(this.objectId);
81
+
82
+ // Если это Ρ„Π°ΠΉΠ»ΠΎΠ²Ρ‹ΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с fileId, удаляСм Ρ„Π°ΠΉΠ» с сСрвСра
83
+ if (this.fileIdToDelete && this.coreMoodboard.fileUploadService) {
84
+ try {
85
+ console.log('πŸ—‘οΈ УдаляСм Ρ„Π°ΠΉΠ» с сСрвСра:', this.fileIdToDelete);
86
+ await this.coreMoodboard.fileUploadService.deleteFile(this.fileIdToDelete);
87
+ console.log('βœ… Π€Π°ΠΉΠ» ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ ΡƒΠ΄Π°Π»Π΅Π½ с сСрвСра:', this.fileIdToDelete);
88
+ } catch (error) {
89
+ console.warn('⚠️ Ошибка удалСния Ρ„Π°ΠΉΠ»Π° с сСрвСра:', error);
90
+ // НС останавливаСм Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹, Ρ‚Π°ΠΊ ΠΊΠ°ΠΊ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ ΡƒΠΆΠ΅ ΡƒΠ΄Π°Π»Π΅Π½ ΠΈΠ· UI
91
+ }
92
+ }
93
+
94
+ this.coreMoodboard.eventBus.emit(Events.Object.Deleted, {
95
+ objectId: this.objectId
96
+ });
97
+ }
98
+
99
+ undo() {
100
+ // DEBUG: Π›ΠΎΠ³ΠΈΡ€ΡƒΠ΅ΠΌ состояниС ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° ΠΏΡ€ΠΈ Undo
101
+ if (this.objectData.type === 'image') {
102
+ console.log('πŸ”„ DEBUG Undo изобраТСния:', {
103
+ id: this.objectData.id,
104
+ imageId: this.objectData.imageId,
105
+ src: this.objectData.src,
106
+ propertiesSrc: this.objectData.properties?.src,
107
+ hasBase64Src: !!(this.objectData.src && this.objectData.src.startsWith('data:')),
108
+ hasBase64Props: !!(this.objectData.properties?.src && this.objectData.properties.src.startsWith('data:'))
109
+ });
110
+ }
111
+
112
+ // Π‘ΠΏΠ΅Ρ†ΠΈΠ°Π»ΡŒΠ½Π°Ρ ΠΎΠ±Ρ€Π°Π±ΠΎΡ‚ΠΊΠ° для Ρ„Π°ΠΉΠ»ΠΎΠ²Ρ‹Ρ… ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²
113
+ if (this.objectData.type === 'file' && this.fileIdToDelete) {
114
+ console.log('πŸ”„ DEBUG Undo Ρ„Π°ΠΉΠ»Π°:', {
115
+ id: this.objectData.id,
116
+ fileId: this.objectData.fileId,
117
+ fileName: this.objectData.properties?.fileName
118
+ });
119
+
120
+ // Π€Π°ΠΉΠ» Π±Ρ‹Π» ΡƒΠ΄Π°Π»Π΅Π½ с сСрвСра, создаСм ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с ΠΏΡ€Π΅Π΄ΡƒΠΏΡ€Π΅ΠΆΠ΄Π΅Π½ΠΈΠ΅ΠΌ
121
+ const restoredObjectData = { ...this.objectData };
122
+ if (restoredObjectData.properties) {
123
+ restoredObjectData.properties = {
124
+ ...restoredObjectData.properties,
125
+ fileName: `[УДАЛЕН] ${restoredObjectData.properties.fileName || 'Ρ„Π°ΠΉΠ»'}`,
126
+ isDeleted: true // Π€Π»Π°Π³ для FileObject Ρ‡Ρ‚ΠΎΠ±Ρ‹ ΠΏΠΎΠΊΠ°Π·Π°Ρ‚ΡŒ Π΄Ρ€ΡƒΠ³ΡƒΡŽ ΠΈΠΊΠΎΠ½ΠΊΡƒ
127
+ };
128
+ }
129
+
130
+ // ВосстанавливаСм ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с ΠΈΠ·ΠΌΠ΅Π½Π΅Π½Π½Ρ‹ΠΌΠΈ Π΄Π°Π½Π½Ρ‹ΠΌΠΈ
131
+ this.coreMoodboard.state.addObject(restoredObjectData);
132
+ this.coreMoodboard.pixi.createObject(restoredObjectData);
133
+
134
+ console.warn('⚠️ Π€Π°ΠΉΠ» восстановлСн Π½Π° холстС, Π½ΠΎ Π±Ρ‹Π» ΡƒΠ΄Π°Π»Π΅Π½ с сСрвСра');
135
+ } else {
136
+ // ВосстанавливаСм ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с сохранСнными Π΄Π°Π½Π½Ρ‹ΠΌΠΈ (для всСх ΠΎΡΡ‚Π°Π»ΡŒΠ½Ρ‹Ρ… Ρ‚ΠΈΠΏΠΎΠ²)
137
+ this.coreMoodboard.state.addObject(this.objectData);
138
+ this.coreMoodboard.pixi.createObject(this.objectData);
139
+ }
140
+
141
+ this.coreMoodboard.eventBus.emit(Events.Object.Created, {
142
+ objectId: this.objectId,
143
+ objectData: this.objectData
144
+ });
145
+ }
146
+ }
@@ -0,0 +1,107 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ import { Events } from '../events/Events.js';
3
+
4
+ /**
5
+ * Команда измСнСния названия Ρ„Π°ΠΉΠ»Π°
6
+ */
7
+ export class EditFileNameCommand extends BaseCommand {
8
+ constructor(coreMoodboard, objectId, oldName, newName) {
9
+ super('edit_file_name', `ΠŸΠ΅Ρ€Π΅ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ»`);
10
+ this.coreMoodboard = coreMoodboard;
11
+ this.objectId = objectId;
12
+ this.oldName = oldName;
13
+ this.newName = newName;
14
+
15
+ // ОбновляСм описаниС с названиями
16
+ this.description = `ΠŸΠ΅Ρ€Π΅ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ» "${this.oldName}" β†’ "${this.newName}"`;
17
+ }
18
+
19
+ async execute() {
20
+ // УстанавливаСм Π½ΠΎΠ²ΠΎΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅
21
+ await this._setFileName(this.newName);
22
+ }
23
+
24
+ async undo() {
25
+ // Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ староС Π½Π°Π·Π²Π°Π½ΠΈΠ΅
26
+ await this._setFileName(this.oldName);
27
+ }
28
+
29
+ /**
30
+ * МоТно ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΠΈΡ‚ΡŒ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ пСрСимСнования ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΈ Ρ‚ΠΎΠ³ΠΎ ΠΆΠ΅ Ρ„Π°ΠΉΠ»Π°
31
+ */
32
+ canMergeWith(otherCommand) {
33
+ return otherCommand instanceof EditFileNameCommand &&
34
+ otherCommand.objectId === this.objectId;
35
+ }
36
+
37
+ /**
38
+ * ΠžΠ±ΡŠΠ΅Π΄ΠΈΠ½ΡΠ΅Ρ‚ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹ пСрСимСнования - Π±Π΅Ρ€Π΅Ρ‚ Π½Π°Ρ‡Π°Π»ΡŒΠ½ΠΎΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΈΠ· ΠΏΠ΅Ρ€Π²ΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄Ρ‹
39
+ * ΠΈ ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ ΠΈΠ· Π²Ρ‚ΠΎΡ€ΠΎΠΉ
40
+ */
41
+ mergeWith(otherCommand) {
42
+ if (!this.canMergeWith(otherCommand)) {
43
+ throw new Error('Cannot merge commands');
44
+ }
45
+
46
+ // ОбновляСм ΠΊΠΎΠ½Π΅Ρ‡Π½ΠΎΠ΅ Π½Π°Π·Π²Π°Π½ΠΈΠ΅
47
+ this.newName = otherCommand.newName;
48
+ this.description = `ΠŸΠ΅Ρ€Π΅ΠΈΠΌΠ΅Π½ΠΎΠ²Π°Ρ‚ΡŒ Ρ„Π°ΠΉΠ» "${this.oldName}" β†’ "${this.newName}"`;
49
+ this.timestamp = otherCommand.timestamp;
50
+ }
51
+
52
+ /**
53
+ * УстанавливаСт Π½Π°Π·Π²Π°Π½ΠΈΠ΅ Ρ„Π°ΠΉΠ»Π°
54
+ * @private
55
+ */
56
+ async _setFileName(fileName) {
57
+ // ОбновляСм Π² состоянии
58
+ const objects = this.coreMoodboard.state.getObjects();
59
+ const objectData = objects.find(obj => obj.id === this.objectId);
60
+
61
+ if (objectData) {
62
+ // ОбновляСм properties ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°
63
+ if (!objectData.properties) {
64
+ objectData.properties = {};
65
+ }
66
+ objectData.properties.fileName = fileName;
67
+
68
+ // Π‘ΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·ΠΈΡ€ΡƒΠ΅ΠΌ с сСрвСром, Ссли Π΅ΡΡ‚ΡŒ fileId
69
+ if (objectData.fileId && this.coreMoodboard.fileUploadService) {
70
+ try {
71
+ console.log('πŸ”„ Π‘ΠΈΠ½Ρ…Ρ€ΠΎΠ½ΠΈΠ·ΠΈΡ€ΡƒΠ΅ΠΌ Π½Π°Π·Π²Π°Π½ΠΈΠ΅ Ρ„Π°ΠΉΠ»Π° с сСрвСром:', { fileId: objectData.fileId, fileName });
72
+ await this.coreMoodboard.fileUploadService.updateFileMetadata(objectData.fileId, {
73
+ fileName: fileName
74
+ });
75
+ console.log('βœ… НазваниС Ρ„Π°ΠΉΠ»Π° ΡƒΡΠΏΠ΅ΡˆΠ½ΠΎ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΎ Π½Π° сСрвСрС');
76
+ } catch (error) {
77
+ console.warn('⚠️ Ошибка синхронизации названия Ρ„Π°ΠΉΠ»Π° с сСрвСром:', error);
78
+ // НС останавливаСм Π²Ρ‹ΠΏΠΎΠ»Π½Π΅Π½ΠΈΠ΅, ΠΏΡ€ΠΎΠ΄ΠΎΠ»ΠΆΠ°Π΅ΠΌ с Π»ΠΎΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΌ ΠΎΠ±Π½ΠΎΠ²Π»Π΅Π½ΠΈΠ΅ΠΌ
79
+ }
80
+ }
81
+
82
+ // ОбновляСм состояниС
83
+ this.coreMoodboard.state.markDirty();
84
+
85
+ // ОбновляСм Π²ΠΈΠ·ΡƒΠ°Π»ΡŒΠ½ΠΎΠ΅ прСдставлСниС
86
+ const pixiReq = { objectId: this.objectId, pixiObject: null };
87
+ this.coreMoodboard.eventBus.emit(Events.Tool.GetObjectPixi, pixiReq);
88
+
89
+ if (pixiReq.pixiObject && pixiReq.pixiObject._mb && pixiReq.pixiObject._mb.instance) {
90
+ const fileInstance = pixiReq.pixiObject._mb.instance;
91
+ if (typeof fileInstance.setFileName === 'function') {
92
+ fileInstance.setFileName(fileName);
93
+ }
94
+ }
95
+
96
+ // Π­ΠΌΠΈΡ‚ΠΈΠΌ событиС обновлСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°
97
+ this.coreMoodboard.eventBus.emit(Events.Object.Updated, {
98
+ objectId: this.objectId,
99
+ type: 'fileName',
100
+ oldValue: this.oldName,
101
+ newValue: fileName
102
+ });
103
+ } else {
104
+ console.warn(`EditFileNameCommand: ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ с ID ${this.objectId} Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½`);
105
+ }
106
+ }
107
+ }
@@ -0,0 +1,47 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ import { Events } from '../events/Events.js';
3
+
4
+ /**
5
+ * Команда пСрСмСщСния Π³Ρ€ΡƒΠΏΠΏΡ‹ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΎΠ΄Π½ΠΎΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠ΅ΠΉ (для Undo/Redo)
6
+ */
7
+ export class GroupMoveCommand extends BaseCommand {
8
+ /**
9
+ * @param {CoreMoodBoard} core
10
+ * @param {Array<{id:string, from:{x:number,y:number}, to:{x:number,y:number}}>} moves
11
+ */
12
+ constructor(core, moves) {
13
+ super('group-move', 'ΠŸΠ΅Ρ€Π΅ΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ Π³Ρ€ΡƒΠΏΠΏΡƒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²');
14
+ this.core = core;
15
+ this.moves = moves || [];
16
+ }
17
+
18
+ execute() {
19
+ // ΠŸΡ€ΠΈΠΌΠ΅Π½ΡΠ΅ΠΌ ΠΊΠΎΠ½Π΅Ρ‡Π½Ρ‹Π΅ ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ ΠΊΠΎ всСм ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°ΠΌ
20
+ for (const item of this.moves) {
21
+ this.core.updateObjectPositionDirect(item.id, item.to);
22
+ this.emit(Events.Object.TransformUpdated, {
23
+ objectId: item.id,
24
+ type: 'position',
25
+ position: item.to
26
+ });
27
+ }
28
+ }
29
+
30
+ undo() {
31
+ // Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ исходныС ΠΏΠΎΠ·ΠΈΡ†ΠΈΠΈ
32
+ for (const item of this.moves) {
33
+ this.core.updateObjectPositionDirect(item.id, item.from);
34
+ this.emit(Events.Object.TransformUpdated, {
35
+ objectId: item.id,
36
+ type: 'position',
37
+ position: item.from
38
+ });
39
+ }
40
+ }
41
+
42
+ getDescription() {
43
+ return `ΠŸΠ΅Ρ€Π΅ΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ Π³Ρ€ΡƒΠΏΠΏΡƒ (${this.moves.length})`;
44
+ }
45
+ }
46
+
47
+
@@ -0,0 +1,74 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ import { Events } from '../events/Events.js';
3
+
4
+ /**
5
+ * Команда измСнСния порядка (z-order) Π³Ρ€ΡƒΠΏΠΏΡ‹ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΊΠ°ΠΊ Π΅Π΄ΠΈΠ½ΠΎΠ³ΠΎ Π±Π»ΠΎΠΊΠ°
6
+ * БохраняСт Π²Π½ΡƒΡ‚Ρ€Π΅Π½Π½ΠΈΠΉ порядок Π³Ρ€ΡƒΠΏΠΏΡ‹
7
+ */
8
+ export class GroupReorderZCommand extends BaseCommand {
9
+ constructor(core, objectIds, mode) {
10
+ super('group-reorder-z', 'Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ порядок слоя Π³Ρ€ΡƒΠΏΠΏΡ‹');
11
+ this.core = core;
12
+ this.objectIds = Array.from(objectIds || []);
13
+ this.mode = mode; // 'front' | 'back' | 'forward' | 'backward'
14
+ this.beforeOrder = null; // снимок порядка ids массива state
15
+ this.afterOrder = null;
16
+ }
17
+
18
+ execute() {
19
+ this.beforeOrder = (this.core.state.state.objects || []).map(o => o.id);
20
+ this.reorder(this.mode);
21
+ this.afterOrder = (this.core.state.state.objects || []).map(o => o.id);
22
+ }
23
+
24
+ undo() {
25
+ if (!this.beforeOrder) return;
26
+ this.applyOrder(this.beforeOrder);
27
+ }
28
+
29
+ applyOrder(idOrder) {
30
+ const map = new Map((this.core.state.state.objects || []).map(o => [o.id, o]));
31
+ this.core.state.state.objects = idOrder.map(id => map.get(id)).filter(Boolean);
32
+ // ΠŸΠ΅Ρ€Π΅ΡΡ‡Π΅Ρ‚ zIndex
33
+ const app = this.core.pixi?.app;
34
+ if (app) {
35
+ app.stage.sortableChildren = true;
36
+ (this.core.state.state.objects || []).forEach((o, i) => {
37
+ const pixi = this.core.pixi.objects.get(o.id);
38
+ if (pixi) pixi.zIndex = i;
39
+ });
40
+ }
41
+ this.core.state.markDirty();
42
+ this.emit(Events.Object.Reordered, { ids: this.objectIds });
43
+ }
44
+
45
+ reorder(mode) {
46
+ const arr = this.core.state.state.objects || [];
47
+ const ids = new Set(this.objectIds);
48
+ const selectedItems = arr.filter(o => ids.has(o.id));
49
+ const others = arr.filter(o => !ids.has(o.id));
50
+ const indices = arr.map((o, i) => ({ id: o.id, i })).filter(p => ids.has(p.id)).map(p => p.i).sort((a,b)=>a-b);
51
+ const minIdx = indices[0] ?? 0;
52
+ const othersBefore = arr.slice(0, minIdx).filter(o => !ids.has(o.id)).length;
53
+ let insertPos = othersBefore;
54
+ switch (mode) {
55
+ case 'front': insertPos = others.length; break;
56
+ case 'back': insertPos = 0; break;
57
+ case 'forward': insertPos = Math.min(othersBefore + 1, others.length); break;
58
+ case 'backward': insertPos = Math.max(othersBefore - 1, 0); break;
59
+ }
60
+ this.core.state.state.objects = [...others.slice(0, insertPos), ...selectedItems, ...others.slice(insertPos)];
61
+ // ΠŸΠ΅Ρ€Π΅ΡΡ‡Π΅Ρ‚ zIndex
62
+ const app = this.core.pixi?.app;
63
+ if (app) {
64
+ app.stage.sortableChildren = true;
65
+ (this.core.state.state.objects || []).forEach((o, i) => {
66
+ const pixi = this.core.pixi.objects.get(o.id);
67
+ if (pixi) pixi.zIndex = i;
68
+ });
69
+ }
70
+ this.core.state.markDirty();
71
+ }
72
+ }
73
+
74
+
@@ -0,0 +1,37 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ import { Events } from '../events/Events.js';
3
+
4
+ /**
5
+ * Команда измСнСния Ρ€Π°Π·ΠΌΠ΅Ρ€Π° Π³Ρ€ΡƒΠΏΠΏΡ‹ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΎΠ΄Π½ΠΎΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠ΅ΠΉ (Undo/Redo)
6
+ */
7
+ export class GroupResizeCommand extends BaseCommand {
8
+ /**
9
+ * @param {CoreMoodBoard} core
10
+ * @param {Array<{id:string, fromSize:{width:number,height:number}, toSize:{width:number,height:number}, fromPos:{x:number,y:number}, toPos:{x:number,y:number}, type?:string}>} changes
11
+ */
12
+ constructor(core, changes) {
13
+ super('group-resize', 'Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Ρ€Π°Π·ΠΌΠ΅Ρ€ Π³Ρ€ΡƒΠΏΠΏΡ‹ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²');
14
+ this.core = core;
15
+ this.changes = changes || [];
16
+ }
17
+
18
+ execute() {
19
+ for (const c of this.changes) {
20
+ this.core.updateObjectSizeAndPositionDirect(c.id, c.toSize, c.toPos, c.type || null);
21
+ this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'resize', size: c.toSize, position: c.toPos });
22
+ }
23
+ }
24
+
25
+ undo() {
26
+ for (const c of this.changes) {
27
+ this.core.updateObjectSizeAndPositionDirect(c.id, c.fromSize, c.fromPos, c.type || null);
28
+ this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'resize', size: c.fromSize, position: c.fromPos });
29
+ }
30
+ }
31
+
32
+ getDescription() {
33
+ return `Π˜Π·ΠΌΠ΅Π½ΠΈΡ‚ΡŒ Ρ€Π°Π·ΠΌΠ΅Ρ€ Π³Ρ€ΡƒΠΏΠΏΡ‹ (${this.changes.length})`;
34
+ }
35
+ }
36
+
37
+
@@ -0,0 +1,41 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ import { Events } from '../events/Events.js';
3
+
4
+ /**
5
+ * Команда ΠΏΠΎΠ²ΠΎΡ€ΠΎΡ‚Π° Π³Ρ€ΡƒΠΏΠΏΡ‹ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ² ΠΎΠ΄Π½ΠΎΠΉ ΠΎΠΏΠ΅Ρ€Π°Ρ†ΠΈΠ΅ΠΉ (для Undo/Redo)
6
+ */
7
+ export class GroupRotateCommand extends BaseCommand {
8
+ /**
9
+ * @param {CoreMoodBoard} core
10
+ * @param {Array<{id:string, fromAngle:number, toAngle:number, fromPos:{x:number,y:number}, toPos:{x:number,y:number}}>} changes
11
+ */
12
+ constructor(core, changes) {
13
+ super('group-rotate', 'ΠŸΠΎΠ²Π΅Ρ€Π½ΡƒΡ‚ΡŒ Π³Ρ€ΡƒΠΏΠΏΡƒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ΠΎΠ²');
14
+ this.core = core;
15
+ this.changes = changes || [];
16
+ }
17
+
18
+ execute() {
19
+ for (const c of this.changes) {
20
+ this.core.updateObjectRotationDirect(c.id, c.toAngle);
21
+ this.core.updateObjectPositionDirect(c.id, c.toPos);
22
+ this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'rotation', angle: c.toAngle });
23
+ this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'position', position: c.toPos });
24
+ }
25
+ }
26
+
27
+ undo() {
28
+ for (const c of this.changes) {
29
+ this.core.updateObjectRotationDirect(c.id, c.fromAngle);
30
+ this.core.updateObjectPositionDirect(c.id, c.fromPos);
31
+ this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'rotation', angle: c.fromAngle });
32
+ this.emit(Events.Object.TransformUpdated, { objectId: c.id, type: 'position', position: c.fromPos });
33
+ }
34
+ }
35
+
36
+ getDescription() {
37
+ return `ΠŸΠΎΠ²Π΅Ρ€Π½ΡƒΡ‚ΡŒ Π³Ρ€ΡƒΠΏΠΏΡƒ (${this.changes.length})`;
38
+ }
39
+ }
40
+
41
+
@@ -0,0 +1,89 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ import { Events } from '../events/Events.js';
3
+
4
+ /**
5
+ * Команда пСрСмСщСния ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°
6
+ */
7
+ export class MoveObjectCommand extends BaseCommand {
8
+ constructor(coreMoodboard, objectId, oldPosition, newPosition) {
9
+ super('move_object', `ΠŸΠ΅Ρ€Π΅ΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚`);
10
+ this.coreMoodboard = coreMoodboard;
11
+ this.objectId = objectId;
12
+ this.oldPosition = { ...oldPosition };
13
+ this.newPosition = { ...newPosition };
14
+
15
+ // ОбновляСм описаниС с ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Π°ΠΌΠΈ
16
+ this.description = `ΠŸΠ΅Ρ€Π΅ΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ (${Math.round(this.oldPosition.x)}, ${Math.round(this.oldPosition.y)}) β†’ (${Math.round(this.newPosition.x)}, ${Math.round(this.newPosition.y)})`;
17
+ }
18
+
19
+ execute() {
20
+ // УстанавливаСм Π½ΠΎΠ²ΡƒΡŽ ΠΏΠΎΠ·ΠΈΡ†ΠΈΡŽ
21
+ this._setPosition(this.newPosition);
22
+
23
+ }
24
+
25
+ undo() {
26
+ // Π’ΠΎΠ·Π²Ρ€Π°Ρ‰Π°Π΅ΠΌ ΡΡ‚Π°Ρ€ΡƒΡŽ ΠΏΠΎΠ·ΠΈΡ†ΠΈΡŽ
27
+ this._setPosition(this.oldPosition);
28
+
29
+ }
30
+
31
+ _setPosition(position) {
32
+ // ОбновляСм ΠΏΠΎΠ·ΠΈΡ†ΠΈΡŽ Π² PIXI (x/y β€” Ρ†Π΅Π½Ρ‚Ρ€), position β€” Π»Π΅Π²Ρ‹ΠΉ-Π²Π΅Ρ€Ρ…
33
+ const pixiObject = this.coreMoodboard.pixi.objects.get(this.objectId);
34
+ if (pixiObject) {
35
+ const halfW = (pixiObject.width || 0) / 2;
36
+ const halfH = (pixiObject.height || 0) / 2;
37
+ pixiObject.x = position.x + halfW;
38
+ pixiObject.y = position.y + halfH;
39
+ }
40
+
41
+ // ОбновляСм ΠΏΠΎΠ·ΠΈΡ†ΠΈΡŽ Π² состоянии (Π»Π΅Π²Ρ‹ΠΉ-Π²Π΅Ρ€Ρ…; Π±Π΅Π· эмита события)
42
+ const objects = this.coreMoodboard.state.state.objects;
43
+ const object = objects.find(obj => obj.id === this.objectId);
44
+ if (object) {
45
+ object.position = { ...position };
46
+
47
+ // ΠŸΠΎΠΌΠ΅Ρ‡Π°Π΅ΠΌ, Ρ‡Ρ‚ΠΎ ΠΊΠΎΠΎΡ€Π΄ΠΈΠ½Π°Ρ‚Ρ‹ ΡƒΠΆΠ΅ скомпСнсированы для pivot
48
+ if (!object.transform) {
49
+ object.transform = {};
50
+ }
51
+ object.transform.pivotCompensated = true;
52
+
53
+ this.coreMoodboard.state.markDirty();
54
+ }
55
+
56
+ // УвСдомляСм ΠΎ Ρ‚ΠΎΠΌ, Ρ‡Ρ‚ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ Π±Ρ‹Π» ΠΈΠ·ΠΌΠ΅Π½Π΅Π½ (для обновлСния Ρ€ΡƒΡ‡Π΅ΠΊ)
57
+ if (this.eventBus) {
58
+ this.eventBus.emit(Events.Object.TransformUpdated, {
59
+ objectId: this.objectId,
60
+ type: 'position',
61
+ position: position
62
+ });
63
+ }
64
+ }
65
+
66
+ /**
67
+ * МоТно Π»ΠΈ ΠΎΠ±ΡŠΠ΅Π΄ΠΈΠ½ΠΈΡ‚ΡŒ с Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ пСрСмСщСния Ρ‚ΠΎΠ³ΠΎ ΠΆΠ΅ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°
68
+ */
69
+ canMergeWith(otherCommand) {
70
+ return otherCommand instanceof MoveObjectCommand &&
71
+ otherCommand.objectId === this.objectId;
72
+ }
73
+
74
+ /**
75
+ * ΠžΠ±ΡŠΠ΅Π΄ΠΈΠ½ΠΈΡ‚ΡŒ с Π΄Ρ€ΡƒΠ³ΠΎΠΉ ΠΊΠΎΠΌΠ°Π½Π΄ΠΎΠΉ пСрСмСщСния
76
+ */
77
+ mergeWith(otherCommand) {
78
+ if (!this.canMergeWith(otherCommand)) {
79
+ throw new Error('Cannot merge with this command');
80
+ }
81
+
82
+ // ОбновляСм ΠΊΠΎΠ½Π΅Ρ‡Π½ΡƒΡŽ ΠΏΠΎΠ·ΠΈΡ†ΠΈΡŽ
83
+ this.newPosition = { ...otherCommand.newPosition };
84
+ this.description = `ΠŸΠ΅Ρ€Π΅ΠΌΠ΅ΡΡ‚ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ (${Math.round(this.oldPosition.x)}, ${Math.round(this.oldPosition.y)}) β†’ (${Math.round(this.newPosition.x)}, ${Math.round(this.newPosition.y)})`;
85
+ this.timestamp = otherCommand.timestamp; // ОбновляСм врСмя послСднСго измСнСния
86
+
87
+
88
+ }
89
+ }
@@ -0,0 +1,103 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ import { Events } from '../events/Events.js';
3
+ import { generateObjectId } from '../../utils/objectIdGenerator.js';
4
+
5
+ /**
6
+ * Команда вставки ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°
7
+ */
8
+ export class PasteObjectCommand extends BaseCommand {
9
+ constructor(coreMoodboard, pastePosition = null) {
10
+ super();
11
+ this.coreMoodboard = coreMoodboard;
12
+ this.pastePosition = pastePosition;
13
+ this.newObjectId = null;
14
+ this.newObjectData = null;
15
+ }
16
+
17
+ execute() {
18
+ // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ, Π΅ΡΡ‚ΡŒ Π»ΠΈ Ρ‡Ρ‚ΠΎ-Ρ‚ΠΎ Π² Π±ΡƒΡ„Π΅Ρ€Π΅ ΠΎΠ±ΠΌΠ΅Π½Π°
19
+ if (!this.coreMoodboard.clipboard || this.coreMoodboard.clipboard.type !== 'object') {
20
+ return;
21
+ }
22
+
23
+ // ΠŸΠΎΠ»ΡƒΡ‡Π°Π΅ΠΌ Π΄Π°Π½Π½Ρ‹Π΅ ΠΈΠ· Π±ΡƒΡ„Π΅Ρ€Π° ΠΎΠ±ΠΌΠ΅Π½Π°
24
+ const originalData = this.coreMoodboard.clipboard.data;
25
+
26
+ // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ Π½ΠΎΠ²Ρ‹ΠΉ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚ Π½Π° основС скопированного
27
+ this.newObjectData = JSON.parse(JSON.stringify(originalData));
28
+
29
+ // Π“Π΅Π½Π΅Ρ€ΠΈΡ€ΡƒΠ΅ΠΌ ΡƒΠ½ΠΈΠΊΠ°Π»ΡŒΠ½Ρ‹ΠΉ ID с ΠΏΡ€ΠΎΠ²Π΅Ρ€ΠΊΠΎΠΉ ΠΊΠΎΠ»Π»ΠΈΠ·ΠΈΠΉ
30
+ const exists = (id) => {
31
+ const inState = (this.coreMoodboard.state.state.objects || []).some(o => o.id === id);
32
+ const inPixi = this.coreMoodboard.pixi?.objects?.has ? this.coreMoodboard.pixi.objects.has(id) : false;
33
+ return inState || inPixi;
34
+ };
35
+ this.newObjectId = generateObjectId(exists);
36
+ this.newObjectData.id = this.newObjectId;
37
+
38
+ // БохраняСм ID ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° для отслСТивания ΠΊΠΎΠΏΠΈΠΉ
39
+ this.newObjectData.originalId = originalData.id;
40
+
41
+ // УстанавливаСм ΠΏΠΎΠ·ΠΈΡ†ΠΈΡŽ вставки
42
+ if (this.pastePosition) {
43
+ this.newObjectData.position = { ...this.pastePosition };
44
+ } else {
45
+ // Если позиция Π½Π΅ ΡƒΠΊΠ°Π·Π°Π½Π°, смСщаСм ΠΎΡ‚Π½ΠΎΡΠΈΡ‚Π΅Π»ΡŒΠ½ΠΎ ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»Π°
46
+ // ΠŸΡ€ΠΎΠ²Π΅Ρ€ΡΠ΅ΠΌ, сколько ΠΊΠΎΠΏΠΈΠΉ этого ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° ΡƒΠΆΠ΅ создано
47
+ const existingObjects = this.coreMoodboard.state.state.objects;
48
+ const originalId = originalData.id;
49
+
50
+ // Π˜Ρ‰Π΅ΠΌ всС ΠΊΠΎΠΏΠΈΠΈ этого ΠΊΠΎΠ½ΠΊΡ€Π΅Ρ‚Π½ΠΎΠ³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°
51
+ const copies = existingObjects.filter(obj =>
52
+ obj.originalId === originalId || // Копии ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π»ΡŒΠ½ΠΎΠ³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°
53
+ (obj.id === originalId && obj.originalId) // Если ΠΎΡ€ΠΈΠ³ΠΈΠ½Π°Π» сам являСтся ΠΊΠΎΠΏΠΈΠ΅ΠΉ
54
+ );
55
+
56
+ // РассчитываСм смСщСниС Π½Π° основС количСства ΠΊΠΎΠΏΠΈΠΉ
57
+ const offsetMultiplier = copies.length + 1;
58
+ const offsetStep = 25; // Π¨Π°Π³ смСщСния Π² пиксСлях
59
+
60
+ console.log(`πŸ“‹ Вставка ΠΊΠΎΠΏΠΈΠΈ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π° ${originalId}: Π½Π°ΠΉΠ΄Π΅Π½ΠΎ ${copies.length} ΡΡƒΡ‰Π΅ΡΡ‚Π²ΡƒΡŽΡ‰ΠΈΡ… ΠΊΠΎΠΏΠΈΠΉ, смСщСниС ${offsetStep * offsetMultiplier}px`);
61
+
62
+ this.newObjectData.position = {
63
+ x: originalData.position.x + (offsetStep * offsetMultiplier),
64
+ y: originalData.position.y + (offsetStep * offsetMultiplier)
65
+ };
66
+ }
67
+
68
+ // БбрасываСм Ρ„Π»Π°Π³ компСнсации pivot для Π½ΠΎΠ²ΠΎΠ³ΠΎ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚Π°
69
+ if (!this.newObjectData.transform) {
70
+ this.newObjectData.transform = {};
71
+ }
72
+ this.newObjectData.transform.pivotCompensated = false;
73
+
74
+ // ДобавляСм Π² состояниС
75
+ this.coreMoodboard.state.addObject(this.newObjectData);
76
+
77
+ // Π‘ΠΎΠ·Π΄Π°Π΅ΠΌ PIXI ΠΎΠ±ΡŠΠ΅ΠΊΡ‚
78
+ this.coreMoodboard.pixi.createObject(this.newObjectData);
79
+
80
+ this.emit(Events.Object.Pasted, {
81
+ originalId: originalData.id,
82
+ newId: this.newObjectId,
83
+ objectData: this.newObjectData
84
+ });
85
+ }
86
+
87
+ undo() {
88
+ if (this.newObjectId) {
89
+ // УдаляСм созданный ΠΎΠ±ΡŠΠ΅ΠΊΡ‚
90
+ this.coreMoodboard.state.removeObject(this.newObjectId);
91
+ this.coreMoodboard.pixi.removeObject(this.newObjectId);
92
+
93
+ // Π‘ΠΎΠΎΡ‚Π²Π΅Ρ‚ΡΡ‚Π²ΡƒΡŽΡ‰Π΅Π³ΠΎ константного события Π½Π΅Ρ‚ β€” остаёмся Π±Π΅Π· эмита ΠΈΠ»ΠΈ ΠΈΡΠΏΠΎΠ»ΡŒΠ·ΡƒΠ΅ΠΌ Object.Deleted, Ссли Π½Π°Π΄ΠΎ глобально
94
+ this.emit(Events.Object.Deleted, {
95
+ objectId: this.newObjectId
96
+ });
97
+ }
98
+ }
99
+
100
+ getDescription() {
101
+ return `Π’ΡΡ‚Π°Π²ΠΈΡ‚ΡŒ ΠΎΠ±ΡŠΠ΅ΠΊΡ‚`;
102
+ }
103
+ }