@sequent-org/moodboard 1.2.1 → 1.2.3
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.
- package/package.json +1 -1
- package/src/core/PixiEngine.js +0 -2
- package/src/core/commands/DeleteObjectCommand.js +0 -37
- package/src/core/commands/EditFileNameCommand.js +0 -1
- package/src/core/commands/ResizeObjectCommand.js +0 -12
- package/src/core/commands/RotateObjectCommand.js +0 -1
- package/src/core/index.js +0 -6
- package/src/initNoBundler.js +3 -3
- package/src/moodboard/MoodBoard.js +4 -5
- package/src/tools/AlignmentGuides.js +0 -3
- package/src/tools/ResizeHandles.js +0 -1
- package/src/tools/object-tools/PlacementTool.js +33 -17
- package/src/tools/object-tools/SelectTool.js +0 -19
- package/src/ui/FilePropertiesPanel.js +0 -3
- package/src/ui/FramePropertiesPanel.js +0 -12
- package/src/ui/HtmlHandlesLayer.js +0 -1
- package/src/ui/NotePropertiesPanel.js +0 -5
- package/src/ui/TextPropertiesPanel.js +0 -4
- package/src/ui/Toolbar.js +55 -24
- package/src/ui/Topbar.js +0 -15
- package/src/utils/emojiResolver.js +51 -6
- package/src/utils/styleLoader.js +0 -3
package/package.json
CHANGED
package/src/core/PixiEngine.js
CHANGED
|
@@ -189,7 +189,6 @@ export class PixiEngine {
|
|
|
189
189
|
// Сохраняем позицию (центр) на случай, если инстанс пересоздаст геометрию
|
|
190
190
|
const position = { x: pixiObject.x, y: pixiObject.y };
|
|
191
191
|
|
|
192
|
-
console.log(`🎨 Обновляем размер объекта ${objectId}, тип: ${objectType}`);
|
|
193
192
|
|
|
194
193
|
// Для Graphics объектов (рамки, фигуры) нужно пересоздать геометрию
|
|
195
194
|
// Делегируем изменение размера объекту, если есть инстанс с updateSize
|
|
@@ -209,7 +208,6 @@ export class PixiEngine {
|
|
|
209
208
|
// Очищаем графику
|
|
210
209
|
pixiObject.clear();
|
|
211
210
|
|
|
212
|
-
console.log(`🔄 Пересоздаем Graphics объект, тип: ${objectType}`);
|
|
213
211
|
|
|
214
212
|
// Определяем что рисовать по типу объекта
|
|
215
213
|
if (objectType === 'drawing') {
|
|
@@ -23,14 +23,6 @@ export class DeleteObjectCommand extends BaseCommand {
|
|
|
23
23
|
|
|
24
24
|
// Для изображений убедимся, что есть src URL для восстановления
|
|
25
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
26
|
|
|
35
27
|
if (this.objectData.imageId) {
|
|
36
28
|
const imageUrl = `/api/images/${this.objectData.imageId}/file`;
|
|
@@ -44,29 +36,16 @@ export class DeleteObjectCommand extends BaseCommand {
|
|
|
44
36
|
}
|
|
45
37
|
this.objectData.properties.src = imageUrl;
|
|
46
38
|
|
|
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
39
|
} else {
|
|
54
|
-
console.warn('🔧 DEBUG DeleteObjectCommand: у изображения нет imageId, оставляем как есть');
|
|
55
40
|
}
|
|
56
41
|
}
|
|
57
42
|
|
|
58
43
|
// Для файлов сохраняем информацию для возможной очистки с сервера
|
|
59
44
|
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
45
|
|
|
66
46
|
if (this.objectData.fileId) {
|
|
67
47
|
// Сохраняем fileId для удаления с сервера
|
|
68
48
|
this.fileIdToDelete = this.objectData.fileId;
|
|
69
|
-
console.log('🔧 DEBUG DeleteObjectCommand: файл будет удален с сервера:', this.fileIdToDelete);
|
|
70
49
|
}
|
|
71
50
|
}
|
|
72
51
|
|
|
@@ -97,25 +76,9 @@ export class DeleteObjectCommand extends BaseCommand {
|
|
|
97
76
|
}
|
|
98
77
|
|
|
99
78
|
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
79
|
|
|
112
80
|
// Специальная обработка для файловых объектов
|
|
113
81
|
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
82
|
|
|
120
83
|
// Файл был удален с сервера, создаем объект с предупреждением
|
|
121
84
|
const restoredObjectData = { ...this.objectData };
|
|
@@ -68,7 +68,6 @@ export class EditFileNameCommand extends BaseCommand {
|
|
|
68
68
|
// Синхронизируем с сервером, если есть fileId
|
|
69
69
|
if (objectData.fileId && this.coreMoodboard.fileUploadService) {
|
|
70
70
|
try {
|
|
71
|
-
console.log('🔄 Синхронизируем название файла с сервером:', { fileId: objectData.fileId, fileName });
|
|
72
71
|
await this.coreMoodboard.fileUploadService.updateFileMetadata(objectData.fileId, {
|
|
73
72
|
fileName: fileName
|
|
74
73
|
});
|
|
@@ -38,10 +38,6 @@ export class ResizeObjectCommand extends BaseCommand {
|
|
|
38
38
|
|
|
39
39
|
|
|
40
40
|
|
|
41
|
-
// Отладка ресайза
|
|
42
|
-
if (position && object) {
|
|
43
|
-
console.log(`🔧 Ресайз объекта ${this.objectId}: newSize(${size.width}, ${size.height}), newPosition(${position.x}, ${position.y})`);
|
|
44
|
-
}
|
|
45
41
|
|
|
46
42
|
// Обновляем размер в PIXI с указанием типа
|
|
47
43
|
this.coreMoodboard.pixi.updateObjectSize(this.objectId, size, objectType);
|
|
@@ -57,14 +53,7 @@ export class ResizeObjectCommand extends BaseCommand {
|
|
|
57
53
|
pixiObject.y = position.y + halfH;
|
|
58
54
|
object.position.x = position.x;
|
|
59
55
|
object.position.y = position.y;
|
|
60
|
-
console.log(`🎯 После ресайза: center(${pixiObject.x}, ${pixiObject.y}), leftTop(${position.x}, ${position.y})`);
|
|
61
56
|
|
|
62
|
-
// Проверим, где находятся границы объекта
|
|
63
|
-
const leftEdge = pixiObject.x - size.width / 2;
|
|
64
|
-
const rightEdge = pixiObject.x + size.width / 2;
|
|
65
|
-
const topEdge = pixiObject.y - size.height / 2;
|
|
66
|
-
const bottomEdge = pixiObject.y + size.height / 2;
|
|
67
|
-
console.log(`📏 Границы объекта: left=${leftEdge}, right=${rightEdge}, top=${topEdge}, bottom=${bottomEdge}`);
|
|
68
57
|
|
|
69
58
|
}
|
|
70
59
|
}
|
|
@@ -103,7 +92,6 @@ export class ResizeObjectCommand extends BaseCommand {
|
|
|
103
92
|
|
|
104
93
|
// Если этот объект выделен, обновляем ручки
|
|
105
94
|
if (activeTool.selectedObjects && activeTool.selectedObjects.has(this.objectId)) {
|
|
106
|
-
console.log(`🔄 Обновляем ручки для объекта ${this.objectId} после Undo/Redo`);
|
|
107
95
|
activeTool.updateResizeHandles();
|
|
108
96
|
}
|
|
109
97
|
}
|
package/src/core/index.js
CHANGED
|
@@ -1402,7 +1402,6 @@ export class CoreMoodBoard {
|
|
|
1402
1402
|
this.eventBus.on(Events.Object.TransformUpdated, (data) => {
|
|
1403
1403
|
// Обновляем ручки если объект выделен
|
|
1404
1404
|
if (this.selectTool && this.selectTool.selection && this.selectTool.selection.has(data.objectId)) {
|
|
1405
|
-
console.log(`🔄 Core: Объект ${data.objectId} изменен через команду, обновляем ручки SelectTool`);
|
|
1406
1405
|
this.selectTool.updateResizeHandles();
|
|
1407
1406
|
}
|
|
1408
1407
|
});
|
|
@@ -1567,7 +1566,6 @@ export class CoreMoodBoard {
|
|
|
1567
1566
|
|
|
1568
1567
|
// Сохраняем изменения
|
|
1569
1568
|
this.state.markDirty();
|
|
1570
|
-
console.log(`✅ Состояние объекта ${objectId} обновлено`);
|
|
1571
1569
|
} else {
|
|
1572
1570
|
console.warn(`❌ Объект ${objectId} не найден в состоянии`);
|
|
1573
1571
|
}
|
|
@@ -1590,7 +1588,6 @@ export class CoreMoodBoard {
|
|
|
1590
1588
|
this.eventBus.on('file:metadata:updated', (data) => {
|
|
1591
1589
|
const { objectId, fileId, metadata } = data;
|
|
1592
1590
|
if (objectId && metadata) {
|
|
1593
|
-
console.log(`🔄 Обновляем метаданные файла ${objectId} с сервера:`, metadata);
|
|
1594
1591
|
|
|
1595
1592
|
// Обновляем объект в состоянии
|
|
1596
1593
|
const objects = this.state.getObjects();
|
|
@@ -1619,7 +1616,6 @@ export class CoreMoodBoard {
|
|
|
1619
1616
|
|
|
1620
1617
|
// Обновляем состояние
|
|
1621
1618
|
this.state.markDirty();
|
|
1622
|
-
console.log(`✅ Метаданные файла ${objectId} синхронизированы с сервером`);
|
|
1623
1619
|
}
|
|
1624
1620
|
}
|
|
1625
1621
|
}
|
|
@@ -1848,7 +1844,6 @@ export class CoreMoodBoard {
|
|
|
1848
1844
|
try {
|
|
1849
1845
|
const result = await this.cleanupUnusedImages();
|
|
1850
1846
|
if (result.deletedCount > 0) {
|
|
1851
|
-
console.log(`✅ Автоматически очищено ${result.deletedCount} неиспользуемых изображений`);
|
|
1852
1847
|
}
|
|
1853
1848
|
} catch (error) {
|
|
1854
1849
|
// Не прерываем выполнение при ошибке cleanup
|
|
@@ -1923,7 +1918,6 @@ export class CoreMoodBoard {
|
|
|
1923
1918
|
if (object) {
|
|
1924
1919
|
object.rotation = angle;
|
|
1925
1920
|
this.state.markDirty();
|
|
1926
|
-
console.log(`🔄 Угол объекта ${objectId} обновлен: ${angle}°`);
|
|
1927
1921
|
}
|
|
1928
1922
|
}
|
|
1929
1923
|
|
package/src/initNoBundler.js
CHANGED
|
@@ -13,7 +13,6 @@ import { EmojiLoaderNoBundler } from './utils/emojiLoaderNoBundler.js';
|
|
|
13
13
|
* @returns {Promise<MoodBoard>} готовый экземпляр MoodBoard
|
|
14
14
|
*/
|
|
15
15
|
export async function initMoodBoardNoBundler(container, options = {}, basePath = '') {
|
|
16
|
-
console.log('🚀 Инициализация MoodBoard без bundler...');
|
|
17
16
|
|
|
18
17
|
// 1. Загружаем стили
|
|
19
18
|
const styleLoader = new StyleLoader();
|
|
@@ -31,13 +30,13 @@ export async function initMoodBoardNoBundler(container, options = {}, basePath =
|
|
|
31
30
|
...options,
|
|
32
31
|
emojiLoader: emojiLoader,
|
|
33
32
|
emojiGroups: emojiGroups,
|
|
33
|
+
emojiBasePath: basePath ? `${basePath}src/assets/emodji/` : null,
|
|
34
34
|
noBundler: true
|
|
35
35
|
};
|
|
36
36
|
|
|
37
37
|
// 5. Создаем MoodBoard
|
|
38
38
|
const moodboard = new MoodBoard(container, enhancedOptions);
|
|
39
39
|
|
|
40
|
-
console.log('✅ MoodBoard инициализирован без bundler');
|
|
41
40
|
|
|
42
41
|
return moodboard;
|
|
43
42
|
}
|
|
@@ -138,7 +137,8 @@ export function quickInitMoodBoard(container, options = {}, basePath = '') {
|
|
|
138
137
|
const moodboard = new MoodBoard(container, {
|
|
139
138
|
...options,
|
|
140
139
|
noBundler: true,
|
|
141
|
-
skipEmojiLoader: true // Пропускаем автозагрузку эмоджи
|
|
140
|
+
skipEmojiLoader: true, // Пропускаем автозагрузку эмоджи
|
|
141
|
+
emojiBasePath: basePath ? `${basePath}src/assets/emodji/` : null
|
|
142
142
|
});
|
|
143
143
|
|
|
144
144
|
return moodboard;
|
|
@@ -124,7 +124,6 @@ export class MoodBoard {
|
|
|
124
124
|
// Загружаем данные (сначала пробуем загрузить с сервера, потом дефолтные)
|
|
125
125
|
await this.loadExistingBoard();
|
|
126
126
|
|
|
127
|
-
console.log('MoodBoard initialized');
|
|
128
127
|
} catch (error) {
|
|
129
128
|
console.error('MoodBoard init failed:', error);
|
|
130
129
|
throw error;
|
|
@@ -159,7 +158,10 @@ export class MoodBoard {
|
|
|
159
158
|
this.toolbar = new Toolbar(
|
|
160
159
|
this.toolbarContainer,
|
|
161
160
|
this.coreMoodboard.eventBus,
|
|
162
|
-
this.options.theme
|
|
161
|
+
this.options.theme,
|
|
162
|
+
{
|
|
163
|
+
emojiBasePath: this.options.emojiBasePath || null
|
|
164
|
+
}
|
|
163
165
|
);
|
|
164
166
|
|
|
165
167
|
// Добавляем функцию для отладки иконок в window
|
|
@@ -350,8 +352,6 @@ export class MoodBoard {
|
|
|
350
352
|
|
|
351
353
|
} catch (error) {
|
|
352
354
|
console.warn('⚠️ Ошибка загрузки доски, создаем новую:', error.message);
|
|
353
|
-
console.debug('ApiClient доступен:', !!this.coreMoodboard.apiClient);
|
|
354
|
-
console.debug('Метод restoreObjectUrls доступен:', !!(this.coreMoodboard.apiClient && typeof this.coreMoodboard.apiClient.restoreObjectUrls === 'function'));
|
|
355
355
|
// Если загрузка не удалась, используем дефолтные данные
|
|
356
356
|
this.dataManager.loadData(this.data);
|
|
357
357
|
}
|
|
@@ -435,6 +435,5 @@ export class MoodBoard {
|
|
|
435
435
|
// Очищаем ссылку на контейнер
|
|
436
436
|
this.container = null;
|
|
437
437
|
|
|
438
|
-
console.log('MoodBoard успешно уничтожен');
|
|
439
438
|
}
|
|
440
439
|
}
|
|
@@ -36,13 +36,11 @@ export class AlignmentGuides {
|
|
|
36
36
|
|
|
37
37
|
this._attachEvents();
|
|
38
38
|
|
|
39
|
-
console.log('AlignmentGuides: Инициализированы направляющие линии выравнивания');
|
|
40
39
|
}
|
|
41
40
|
|
|
42
41
|
_attachEvents() {
|
|
43
42
|
// Одиночное перетаскивание
|
|
44
43
|
this.eventBus.on(Events.Tool.DragStart, (data) => {
|
|
45
|
-
console.log('AlignmentGuides: Начало перетаскивания объекта:', data.object);
|
|
46
44
|
this.isDragging = true;
|
|
47
45
|
this.currentDragObject = data.object;
|
|
48
46
|
this.currentDragObjects = [];
|
|
@@ -122,7 +120,6 @@ export class AlignmentGuides {
|
|
|
122
120
|
|
|
123
121
|
// Отображаем найденные направляющие
|
|
124
122
|
if (guides.length > 0) {
|
|
125
|
-
console.log('AlignmentGuides: Найдено направляющих:', guides.length);
|
|
126
123
|
this._showGuides(guides);
|
|
127
124
|
}
|
|
128
125
|
}
|
|
@@ -96,7 +96,6 @@ export class ResizeHandles {
|
|
|
96
96
|
this.container.y = objectCenterY;
|
|
97
97
|
this.container.pivot.set(objectPivotX, objectPivotY);
|
|
98
98
|
|
|
99
|
-
console.log(`🔄 Поворот ручек: ${(this.targetObject.rotation * 180 / Math.PI).toFixed(1)}°, центр: (${objectCenterX}, ${objectCenterY}), pivot: (${objectPivotX}, ${objectPivotY})`);
|
|
100
99
|
|
|
101
100
|
// Используем локальные границы для создания ручек
|
|
102
101
|
this.workingBounds = {
|
|
@@ -105,7 +105,11 @@ export class PlacementTool extends BaseTool {
|
|
|
105
105
|
this.eventBus.on(Events.Place.FileSelected, (fileData) => {
|
|
106
106
|
this.selectedFile = fileData;
|
|
107
107
|
this.selectedImage = null;
|
|
108
|
-
|
|
108
|
+
|
|
109
|
+
// Если PlacementTool уже активен - показываем призрак сразу
|
|
110
|
+
if (this.world) {
|
|
111
|
+
this.showFileGhost();
|
|
112
|
+
}
|
|
109
113
|
});
|
|
110
114
|
|
|
111
115
|
// Обработка отмены выбора файла
|
|
@@ -562,9 +566,10 @@ export class PlacementTool extends BaseTool {
|
|
|
562
566
|
const worldPoint = this._toWorld(cursorX, cursorY);
|
|
563
567
|
this.updateGhostPosition(worldPoint.x, worldPoint.y);
|
|
564
568
|
}
|
|
565
|
-
// Попробуем дождаться загрузки веб-шрифта Caveat до отрисовки
|
|
566
|
-
|
|
567
|
-
const
|
|
569
|
+
// Попробуем дождаться загрузки веб-шрифта Caveat до отрисовки
|
|
570
|
+
// Для файлов используем selectedFile, а не pending
|
|
571
|
+
const fileFont = (this.selectedFile.properties?.fontFamily) || 'Caveat, Arial, cursive';
|
|
572
|
+
const primaryFont = String(fileFont).split(',')[0].trim().replace(/^['"]|['"]$/g, '') || 'Caveat';
|
|
568
573
|
|
|
569
574
|
// Размеры
|
|
570
575
|
const width = this.selectedFile.properties.width || 120;
|
|
@@ -722,9 +727,10 @@ export class PlacementTool extends BaseTool {
|
|
|
722
727
|
this.ghostContainer = new PIXI.Container();
|
|
723
728
|
this.ghostContainer.alpha = 0.6; // Полупрозрачность
|
|
724
729
|
|
|
725
|
-
// Размеры призрака
|
|
726
|
-
const
|
|
727
|
-
const
|
|
730
|
+
// Размеры призрака - используем размеры из pending/selected, если есть
|
|
731
|
+
const isEmojiIcon = this.selectedImage.properties?.isEmojiIcon;
|
|
732
|
+
const maxWidth = this.selectedImage.properties.width || (isEmojiIcon ? 64 : 300);
|
|
733
|
+
const maxHeight = this.selectedImage.properties.height || (isEmojiIcon ? 64 : 200);
|
|
728
734
|
|
|
729
735
|
try {
|
|
730
736
|
// Создаем превью изображения
|
|
@@ -816,8 +822,10 @@ export class PlacementTool extends BaseTool {
|
|
|
816
822
|
this.ghostContainer = new PIXI.Container();
|
|
817
823
|
this.ghostContainer.alpha = 0.6;
|
|
818
824
|
|
|
819
|
-
|
|
820
|
-
const
|
|
825
|
+
// Для эмоджи используем точные размеры из pending для согласованности
|
|
826
|
+
const isEmojiIcon = this.pending.properties?.isEmojiIcon;
|
|
827
|
+
const maxWidth = this.pending.size?.width || this.pending.properties?.width || (isEmojiIcon ? 64 : 56);
|
|
828
|
+
const maxHeight = this.pending.size?.height || this.pending.properties?.height || (isEmojiIcon ? 64 : 56);
|
|
821
829
|
|
|
822
830
|
try {
|
|
823
831
|
const texture = await PIXI.Texture.fromURL(src);
|
|
@@ -853,15 +861,23 @@ export class PlacementTool extends BaseTool {
|
|
|
853
861
|
|
|
854
862
|
this.world.addChild(this.ghostContainer);
|
|
855
863
|
|
|
856
|
-
//
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
864
|
+
// Для эмоджи не используем кастомный курсор, чтобы избежать дублирования призраков
|
|
865
|
+
if (!isEmojiIcon) {
|
|
866
|
+
// Кастомный курсор только для обычных изображений
|
|
867
|
+
try {
|
|
868
|
+
if (this.app && this.app.view && src) {
|
|
869
|
+
const cursorSize = 24;
|
|
870
|
+
const url = encodeURI(src);
|
|
871
|
+
// Используем CSS cursor с изображением, если поддерживается
|
|
872
|
+
this.app.view.style.cursor = `url(${url}) ${Math.floor(cursorSize/2)} ${Math.floor(cursorSize/2)}, default`;
|
|
873
|
+
}
|
|
874
|
+
} catch (_) {}
|
|
875
|
+
} else {
|
|
876
|
+
// Для эмоджи используем стандартный курсор
|
|
877
|
+
if (this.app && this.app.view) {
|
|
878
|
+
this.app.view.style.cursor = 'crosshair';
|
|
863
879
|
}
|
|
864
|
-
}
|
|
880
|
+
}
|
|
865
881
|
}
|
|
866
882
|
|
|
867
883
|
/**
|
|
@@ -136,7 +136,6 @@ export class SelectTool extends BaseTool {
|
|
|
136
136
|
this.eventBus.on(Events.Object.Deleted, (data) => {
|
|
137
137
|
const objectId = data?.objectId || data;
|
|
138
138
|
if (objectId && this.selection.has(objectId)) {
|
|
139
|
-
console.log(`🗑️ SelectTool: Объект ${objectId} удален, убираем из выделения`);
|
|
140
139
|
this.removeFromSelection(objectId);
|
|
141
140
|
|
|
142
141
|
// Если выделение стало пустым, скрываем ручки
|
|
@@ -164,7 +163,6 @@ export class SelectTool extends BaseTool {
|
|
|
164
163
|
*/
|
|
165
164
|
activate(app) {
|
|
166
165
|
super.activate();
|
|
167
|
-
console.log('🔧 SelectTool активирован, app:', !!app);
|
|
168
166
|
// Сохраняем ссылку на PIXI app для оверлеев (рамка выделения)
|
|
169
167
|
this.app = app;
|
|
170
168
|
|
|
@@ -224,7 +222,6 @@ export class SelectTool extends BaseTool {
|
|
|
224
222
|
} else if (!app) {
|
|
225
223
|
console.log('❌ PIXI app не передан в activate');
|
|
226
224
|
} else {
|
|
227
|
-
console.log('ℹ️ ResizeHandles уже созданы');
|
|
228
225
|
}
|
|
229
226
|
}
|
|
230
227
|
|
|
@@ -272,7 +269,6 @@ export class SelectTool extends BaseTool {
|
|
|
272
269
|
|
|
273
270
|
// Если активен текстовый редактор, закрываем его при клике вне
|
|
274
271
|
if (this.textEditor.active) {
|
|
275
|
-
console.log('🔧 SelectTool: closing text editor on mouse down, objectType:', this.textEditor.objectType, 'objectId:', this.textEditor.objectId);
|
|
276
272
|
if (this.textEditor.objectType === 'file') {
|
|
277
273
|
this._closeFileNameEditor(true);
|
|
278
274
|
} else {
|
|
@@ -529,7 +525,6 @@ export class SelectTool extends BaseTool {
|
|
|
529
525
|
onKeyDown(event) {
|
|
530
526
|
// Проверяем, не активен ли текстовый редактор (редактирование названия файла или текста)
|
|
531
527
|
if (this.textEditor && this.textEditor.active) {
|
|
532
|
-
console.log('🔒 SelectTool: Текстовый редактор активен, пропускаем обработку клавиш');
|
|
533
528
|
return; // Не обрабатываем клавиши во время редактирования
|
|
534
529
|
}
|
|
535
530
|
|
|
@@ -793,7 +788,6 @@ export class SelectTool extends BaseTool {
|
|
|
793
788
|
* Начало изменения размера
|
|
794
789
|
*/
|
|
795
790
|
startResize(handle, objectId) {
|
|
796
|
-
console.log(`🔧 Начинаем resize: ручка ${handle}, объект ${objectId}`);
|
|
797
791
|
// Групповой resize
|
|
798
792
|
if (objectId === this.groupId && this.selection.size() > 1) {
|
|
799
793
|
this.isGroupResizing = true;
|
|
@@ -1263,7 +1257,6 @@ export class SelectTool extends BaseTool {
|
|
|
1263
1257
|
*/
|
|
1264
1258
|
|
|
1265
1259
|
addToSelection(object) {
|
|
1266
|
-
console.log(`➕ Добавляем в выделение: ${object}`);
|
|
1267
1260
|
this.selection.add(object);
|
|
1268
1261
|
this.emit(Events.Tool.SelectionAdd, { object });
|
|
1269
1262
|
this.updateResizeHandles();
|
|
@@ -2061,13 +2054,11 @@ export class SelectTool extends BaseTool {
|
|
|
2061
2054
|
// Завершение
|
|
2062
2055
|
const isNewCreation = !!create;
|
|
2063
2056
|
const finalize = (commit) => {
|
|
2064
|
-
console.log('🔧 SelectTool: finalize called with commit:', commit, 'objectId:', objectId, 'objectType:', this.textEditor.objectType);
|
|
2065
2057
|
const value = textarea.value.trim();
|
|
2066
2058
|
const commitValue = commit && value.length > 0;
|
|
2067
2059
|
|
|
2068
2060
|
// Сохраняем objectType ДО сброса this.textEditor
|
|
2069
2061
|
const currentObjectType = this.textEditor.objectType;
|
|
2070
|
-
console.log('🔧 SelectTool: finalize - saved objectType:', currentObjectType);
|
|
2071
2062
|
|
|
2072
2063
|
// Показываем статичный текст только если не отменяем создание нового пустого
|
|
2073
2064
|
if (objectId && (commitValue || !isNewCreation)) {
|
|
@@ -2140,11 +2131,9 @@ export class SelectTool extends BaseTool {
|
|
|
2140
2131
|
if (isNewCreation && objectId) {
|
|
2141
2132
|
this.eventBus.emit(Events.Tool.ObjectsDelete, { objects: [objectId] });
|
|
2142
2133
|
}
|
|
2143
|
-
console.log('🔧 SelectTool: finalize - no commit, returning');
|
|
2144
2134
|
return;
|
|
2145
2135
|
}
|
|
2146
2136
|
if (objectId == null) {
|
|
2147
|
-
console.log('🔧 SelectTool: finalize - creating new object');
|
|
2148
2137
|
// Создаем объект с правильным типом
|
|
2149
2138
|
const objectType = currentObjectType || 'text';
|
|
2150
2139
|
// Конвертируем размеры редактора (px) в мировые единицы
|
|
@@ -2163,7 +2152,6 @@ export class SelectTool extends BaseTool {
|
|
|
2163
2152
|
} else {
|
|
2164
2153
|
// Обновление существующего: используем команду обновления содержимого
|
|
2165
2154
|
if (currentObjectType === 'note') {
|
|
2166
|
-
console.log('🔧 SelectTool: updating note content via UpdateObjectContent');
|
|
2167
2155
|
// Для записок обновляем содержимое через PixiEngine
|
|
2168
2156
|
this.eventBus.emit(Events.Tool.UpdateObjectContent, {
|
|
2169
2157
|
objectId: objectId,
|
|
@@ -2179,7 +2167,6 @@ export class SelectTool extends BaseTool {
|
|
|
2179
2167
|
});
|
|
2180
2168
|
} else {
|
|
2181
2169
|
// Для обычного текста тоже используем обновление содержимого
|
|
2182
|
-
console.log('🔧 SelectTool: finalize - updating text content via UpdateObjectContent');
|
|
2183
2170
|
this.eventBus.emit(Events.Tool.UpdateObjectContent, {
|
|
2184
2171
|
objectId: objectId,
|
|
2185
2172
|
content: value
|
|
@@ -2429,7 +2416,6 @@ export class SelectTool extends BaseTool {
|
|
|
2429
2416
|
* Закрывает редактор названия файла
|
|
2430
2417
|
*/
|
|
2431
2418
|
_closeFileNameEditor(commit) {
|
|
2432
|
-
console.log('🔧 SelectTool: _closeFileNameEditor called with commit:', commit);
|
|
2433
2419
|
|
|
2434
2420
|
// Проверяем, что редактор существует и не закрыт
|
|
2435
2421
|
if (!this.textEditor || !this.textEditor.textarea || this.textEditor.closing) {
|
|
@@ -2444,7 +2430,6 @@ export class SelectTool extends BaseTool {
|
|
|
2444
2430
|
const commitValue = commit && value.length > 0;
|
|
2445
2431
|
const objectId = this.textEditor.objectId;
|
|
2446
2432
|
|
|
2447
|
-
console.log('🔧 SelectTool: _closeFileNameEditor - objectId:', objectId, 'commitValue:', commitValue, 'newName:', value);
|
|
2448
2433
|
|
|
2449
2434
|
// Убираем wrapper из DOM
|
|
2450
2435
|
if (this.textEditor.wrapper && this.textEditor.wrapper.parentNode) {
|
|
@@ -2464,7 +2449,6 @@ export class SelectTool extends BaseTool {
|
|
|
2464
2449
|
|
|
2465
2450
|
// Применяем изменения если нужно
|
|
2466
2451
|
if (commitValue && value !== this.textEditor.properties.fileName) {
|
|
2467
|
-
console.log('🔧 Применяем новое название файла:', value);
|
|
2468
2452
|
|
|
2469
2453
|
// Создаем команду изменения названия файла
|
|
2470
2454
|
const oldName = this.textEditor.properties.fileName || 'Untitled';
|
|
@@ -2492,7 +2476,6 @@ export class SelectTool extends BaseTool {
|
|
|
2492
2476
|
}
|
|
2493
2477
|
|
|
2494
2478
|
_closeTextEditor(commit) {
|
|
2495
|
-
console.log('🔧 SelectTool: _closeTextEditor called with commit:', commit);
|
|
2496
2479
|
const textarea = this.textEditor.textarea;
|
|
2497
2480
|
if (!textarea) return;
|
|
2498
2481
|
const value = textarea.value.trim();
|
|
@@ -2502,7 +2485,6 @@ export class SelectTool extends BaseTool {
|
|
|
2502
2485
|
const position = this.textEditor.position;
|
|
2503
2486
|
const properties = this.textEditor.properties;
|
|
2504
2487
|
|
|
2505
|
-
console.log('🔧 SelectTool: _closeTextEditor - objectType:', objectType, 'objectId:', objectId, 'commitValue:', commitValue);
|
|
2506
2488
|
|
|
2507
2489
|
// Показываем статичный текст после завершения редактирования для всех типов объектов
|
|
2508
2490
|
if (objectId) {
|
|
@@ -2524,7 +2506,6 @@ export class SelectTool extends BaseTool {
|
|
|
2524
2506
|
if (!commitValue) return;
|
|
2525
2507
|
if (objectId == null) {
|
|
2526
2508
|
// Создаём новый объект через ToolbarAction
|
|
2527
|
-
console.log('🔧 SelectTool: creating new object via ToolbarAction, type:', objectType);
|
|
2528
2509
|
this.eventBus.emit(Events.UI.ToolbarAction, {
|
|
2529
2510
|
type: objectType,
|
|
2530
2511
|
id: objectType,
|
|
@@ -74,7 +74,6 @@ export class FilePropertiesPanel {
|
|
|
74
74
|
const pixi = this.core?.pixi?.objects?.get ? this.core.pixi.objects.get(id) : null;
|
|
75
75
|
const isFile = !!(pixi && pixi._mb && pixi._mb.type === 'file');
|
|
76
76
|
|
|
77
|
-
console.log('📎 FilePropertiesPanel: updateFromSelection - id=', id, 'isFile=', isFile);
|
|
78
77
|
|
|
79
78
|
if (isFile) {
|
|
80
79
|
this.showFor(id);
|
|
@@ -84,7 +83,6 @@ export class FilePropertiesPanel {
|
|
|
84
83
|
}
|
|
85
84
|
|
|
86
85
|
showFor(objectId) {
|
|
87
|
-
console.log('📎 FilePropertiesPanel: Showing panel for objectId:', objectId);
|
|
88
86
|
this.currentId = objectId;
|
|
89
87
|
if (this.panel) {
|
|
90
88
|
this.panel.style.display = 'flex';
|
|
@@ -223,7 +221,6 @@ export class FilePropertiesPanel {
|
|
|
223
221
|
|
|
224
222
|
// Скачиваем файл
|
|
225
223
|
await this.core.fileUploadService.downloadFile(fileId, fileName);
|
|
226
|
-
console.log('✅ Файл скачан:', fileName);
|
|
227
224
|
|
|
228
225
|
// Восстанавливаем кнопку
|
|
229
226
|
setTimeout(() => {
|
|
@@ -73,7 +73,6 @@ export class FramePropertiesPanel {
|
|
|
73
73
|
const pixi = this.core?.pixi?.objects?.get ? this.core.pixi.objects.get(id) : null;
|
|
74
74
|
const isFrame = !!(pixi && pixi._mb && pixi._mb.type === 'frame');
|
|
75
75
|
|
|
76
|
-
console.log('🖼️ FramePropertiesPanel: updateFromSelection - id=', id, 'isFrame=', isFrame);
|
|
77
76
|
|
|
78
77
|
if (isFrame) {
|
|
79
78
|
this.showFor(id);
|
|
@@ -83,7 +82,6 @@ export class FramePropertiesPanel {
|
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
showFor(objectId) {
|
|
86
|
-
console.log('🖼️ FramePropertiesPanel: Showing panel for objectId:', objectId);
|
|
87
85
|
this.currentId = objectId;
|
|
88
86
|
if (this.panel) {
|
|
89
87
|
this.panel.style.display = 'flex';
|
|
@@ -164,18 +162,10 @@ export class FramePropertiesPanel {
|
|
|
164
162
|
panelY = y + height + 40;
|
|
165
163
|
}
|
|
166
164
|
|
|
167
|
-
console.log('🖼️ FramePropertiesPanel: Positioning above frame:', {
|
|
168
|
-
frameX: x, frameY: y, frameWidth: width, frameHeight: height,
|
|
169
|
-
panelX, panelY
|
|
170
|
-
});
|
|
171
165
|
|
|
172
166
|
this.panel.style.left = `${Math.round(panelX)}px`;
|
|
173
167
|
this.panel.style.top = `${Math.round(panelY)}px`;
|
|
174
168
|
|
|
175
|
-
console.log('🖼️ FramePropertiesPanel: Panel CSS applied:', {
|
|
176
|
-
left: this.panel.style.left,
|
|
177
|
-
top: this.panel.style.top
|
|
178
|
-
});
|
|
179
169
|
}
|
|
180
170
|
|
|
181
171
|
_createFrameControls(panel) {
|
|
@@ -302,7 +292,6 @@ export class FramePropertiesPanel {
|
|
|
302
292
|
_changeFrameTitle(newTitle) {
|
|
303
293
|
if (!this.currentId) return;
|
|
304
294
|
|
|
305
|
-
console.log('🖼️ FramePropertiesPanel: Changing frame title to:', newTitle);
|
|
306
295
|
|
|
307
296
|
// Обновляем свойства объекта
|
|
308
297
|
this.eventBus.emit(Events.Object.StateChanged, {
|
|
@@ -455,7 +444,6 @@ export class FramePropertiesPanel {
|
|
|
455
444
|
_selectColor(color) {
|
|
456
445
|
if (!this.currentId) return;
|
|
457
446
|
|
|
458
|
-
console.log('🖼️ FramePropertiesPanel: Selecting color:', color);
|
|
459
447
|
|
|
460
448
|
// Обновляем визуальное отображение кнопки
|
|
461
449
|
this.colorButton.style.backgroundColor = color.hex;
|
|
@@ -61,7 +61,6 @@ export class HtmlHandlesLayer {
|
|
|
61
61
|
if (this.core?.selectTool && data.objectId) {
|
|
62
62
|
const isSelected = this.core.selectTool.selectedObjects.has(data.objectId);
|
|
63
63
|
if (isSelected) {
|
|
64
|
-
console.log(`🔄 HtmlHandlesLayer: Объект ${data.objectId} изменен через команду, обновляем рамку`);
|
|
65
64
|
this.update();
|
|
66
65
|
}
|
|
67
66
|
}
|
|
@@ -73,7 +73,6 @@ export class NotePropertiesPanel {
|
|
|
73
73
|
const pixi = this.core?.pixi?.objects?.get ? this.core.pixi.objects.get(id) : null;
|
|
74
74
|
const isNote = !!(pixi && pixi._mb && pixi._mb.type === 'note');
|
|
75
75
|
|
|
76
|
-
console.log('📝 NotePropertiesPanel: updateFromSelection - id=', id, 'isNote=', isNote);
|
|
77
76
|
|
|
78
77
|
if (isNote) {
|
|
79
78
|
this.showFor(id);
|
|
@@ -83,7 +82,6 @@ export class NotePropertiesPanel {
|
|
|
83
82
|
}
|
|
84
83
|
|
|
85
84
|
showFor(objectId) {
|
|
86
|
-
console.log('📝 NotePropertiesPanel: Showing panel for objectId:', objectId);
|
|
87
85
|
this.currentId = objectId;
|
|
88
86
|
if (this.panel) {
|
|
89
87
|
this.panel.style.display = 'flex';
|
|
@@ -471,7 +469,6 @@ export class NotePropertiesPanel {
|
|
|
471
469
|
_selectColor(color, propertyName) {
|
|
472
470
|
if (!this.currentId) return;
|
|
473
471
|
|
|
474
|
-
console.log(`📝 NotePropertiesPanel: Selecting ${propertyName}:`, color);
|
|
475
472
|
|
|
476
473
|
// Обновляем соответствующую кнопку
|
|
477
474
|
if (propertyName === 'backgroundColor' && this.backgroundColorButton) {
|
|
@@ -497,7 +494,6 @@ export class NotePropertiesPanel {
|
|
|
497
494
|
_changeFontSize(fontSize) {
|
|
498
495
|
if (!this.currentId) return;
|
|
499
496
|
|
|
500
|
-
console.log('📝 NotePropertiesPanel: Changing font size to:', fontSize);
|
|
501
497
|
|
|
502
498
|
// Отправляем событие изменения размера шрифта
|
|
503
499
|
this.eventBus.emit(Events.Object.StateChanged, {
|
|
@@ -509,7 +505,6 @@ export class NotePropertiesPanel {
|
|
|
509
505
|
_changeFontFamily(fontFamily) {
|
|
510
506
|
if (!this.currentId) return;
|
|
511
507
|
|
|
512
|
-
console.log('📝 NotePropertiesPanel: Changing font family to:', fontFamily);
|
|
513
508
|
|
|
514
509
|
this.eventBus.emit(Events.Object.StateChanged, {
|
|
515
510
|
objectId: this.currentId,
|
|
@@ -681,7 +681,6 @@ export class TextPropertiesPanel {
|
|
|
681
681
|
_changeFontFamily(fontFamily) {
|
|
682
682
|
if (!this.currentId) return;
|
|
683
683
|
|
|
684
|
-
console.log('🔧 TextPropertiesPanel: Changing font family to:', fontFamily);
|
|
685
684
|
|
|
686
685
|
// Обновляем свойства объекта через StateManager (в properties)
|
|
687
686
|
this.eventBus.emit(Events.Object.StateChanged, {
|
|
@@ -698,7 +697,6 @@ export class TextPropertiesPanel {
|
|
|
698
697
|
_changeFontSize(fontSize) {
|
|
699
698
|
if (!this.currentId) return;
|
|
700
699
|
|
|
701
|
-
console.log('🔧 TextPropertiesPanel: Changing font size to:', fontSize);
|
|
702
700
|
|
|
703
701
|
// Обновляем свойства объекта через StateManager
|
|
704
702
|
this.eventBus.emit(Events.Object.StateChanged, {
|
|
@@ -715,7 +713,6 @@ export class TextPropertiesPanel {
|
|
|
715
713
|
_changeTextColor(color) {
|
|
716
714
|
if (!this.currentId) return;
|
|
717
715
|
|
|
718
|
-
console.log('🔧 TextPropertiesPanel: Changing text color to:', color);
|
|
719
716
|
|
|
720
717
|
// Обновляем свойства объекта через StateManager
|
|
721
718
|
this.eventBus.emit(Events.Object.StateChanged, {
|
|
@@ -732,7 +729,6 @@ export class TextPropertiesPanel {
|
|
|
732
729
|
_changeBackgroundColor(backgroundColor) {
|
|
733
730
|
if (!this.currentId) return;
|
|
734
731
|
|
|
735
|
-
console.log('🔧 TextPropertiesPanel: Changing background color to:', backgroundColor);
|
|
736
732
|
|
|
737
733
|
// Обновляем свойства объекта через StateManager
|
|
738
734
|
this.eventBus.emit(Events.Object.StateChanged, {
|
package/src/ui/Toolbar.js
CHANGED
|
@@ -5,11 +5,14 @@ import { Events } from '../core/events/Events.js';
|
|
|
5
5
|
import { IconLoader } from '../utils/iconLoader.js';
|
|
6
6
|
|
|
7
7
|
export class Toolbar {
|
|
8
|
-
constructor(container, eventBus, theme = 'light') {
|
|
8
|
+
constructor(container, eventBus, theme = 'light', options = {}) {
|
|
9
9
|
this.container = container;
|
|
10
10
|
this.eventBus = eventBus;
|
|
11
11
|
this.theme = theme;
|
|
12
12
|
|
|
13
|
+
// Базовый путь для ассетов (эмоджи и другие ресурсы)
|
|
14
|
+
this.emojiBasePath = options.emojiBasePath || null;
|
|
15
|
+
|
|
13
16
|
// Инициализируем IconLoader
|
|
14
17
|
this.iconLoader = new IconLoader();
|
|
15
18
|
|
|
@@ -575,11 +578,9 @@ export class Toolbar {
|
|
|
575
578
|
setActiveToolbarButton(toolName) {
|
|
576
579
|
if (!this.element) return;
|
|
577
580
|
|
|
578
|
-
console.log('🎯 Toolbar: Установка активной кнопки для инструмента:', toolName, 'placeSelectedButtonId:', this.placeSelectedButtonId);
|
|
579
581
|
|
|
580
582
|
// Сбрасываем активные классы
|
|
581
583
|
this.element.querySelectorAll('.moodboard-toolbar__button--active').forEach(el => {
|
|
582
|
-
console.log('🔄 Deactivating button:', el.dataset.toolId);
|
|
583
584
|
el.classList.remove('moodboard-toolbar__button--active');
|
|
584
585
|
});
|
|
585
586
|
|
|
@@ -611,16 +612,13 @@ export class Toolbar {
|
|
|
611
612
|
}
|
|
612
613
|
|
|
613
614
|
if (!btnId) {
|
|
614
|
-
console.warn('⚠️ Toolbar: Не найден btnId для инструмента:', toolName);
|
|
615
615
|
return;
|
|
616
616
|
}
|
|
617
617
|
|
|
618
618
|
const btn = this.element.querySelector(`.moodboard-toolbar__button--${btnId}`);
|
|
619
619
|
if (btn) {
|
|
620
620
|
btn.classList.add('moodboard-toolbar__button--active');
|
|
621
|
-
console.log('✅ Toolbar: Активирована кнопка:', btnId);
|
|
622
621
|
} else {
|
|
623
|
-
console.warn('⚠️ Toolbar: Не найдена кнопка с селектором:', `.moodboard-toolbar__button--${btnId}`);
|
|
624
622
|
}
|
|
625
623
|
}
|
|
626
624
|
|
|
@@ -930,7 +928,6 @@ export class Toolbar {
|
|
|
930
928
|
});
|
|
931
929
|
} else {
|
|
932
930
|
// Режим без bundler - используем статичный список
|
|
933
|
-
console.log('🎭 Toolbar: Режим без bundler, используем статичные эмоджи');
|
|
934
931
|
groups = this.getFallbackEmojiGroups();
|
|
935
932
|
}
|
|
936
933
|
|
|
@@ -966,9 +963,13 @@ export class Toolbar {
|
|
|
966
963
|
|
|
967
964
|
// Перетаскивание: начинаем только если был реальный drag (движение > 4px)
|
|
968
965
|
btn.addEventListener('mousedown', (e) => {
|
|
966
|
+
// Блокируем одновременную обработку
|
|
967
|
+
if (btn.__clickProcessing || btn.__dragActive) return;
|
|
968
|
+
|
|
969
969
|
const startX = e.clientX;
|
|
970
970
|
const startY = e.clientY;
|
|
971
971
|
let startedDrag = false;
|
|
972
|
+
|
|
972
973
|
const onMove = (ev) => {
|
|
973
974
|
if (startedDrag) return;
|
|
974
975
|
const dx = Math.abs(ev.clientX - startX);
|
|
@@ -976,6 +977,10 @@ export class Toolbar {
|
|
|
976
977
|
if (dx > 4 || dy > 4) {
|
|
977
978
|
startedDrag = true;
|
|
978
979
|
btn.__dragActive = true;
|
|
980
|
+
|
|
981
|
+
// Блокируем click handler
|
|
982
|
+
btn.__clickProcessing = true;
|
|
983
|
+
|
|
979
984
|
const target = 64;
|
|
980
985
|
const targetW = target;
|
|
981
986
|
const targetH = target;
|
|
@@ -994,8 +999,11 @@ export class Toolbar {
|
|
|
994
999
|
};
|
|
995
1000
|
const onUp = () => {
|
|
996
1001
|
cleanup();
|
|
997
|
-
//
|
|
998
|
-
setTimeout(() => {
|
|
1002
|
+
// Снимаем флаги с задержкой
|
|
1003
|
+
setTimeout(() => {
|
|
1004
|
+
btn.__dragActive = false;
|
|
1005
|
+
btn.__clickProcessing = false;
|
|
1006
|
+
}, 50);
|
|
999
1007
|
};
|
|
1000
1008
|
const cleanup = () => {
|
|
1001
1009
|
document.removeEventListener('mousemove', onMove);
|
|
@@ -1005,8 +1013,13 @@ export class Toolbar {
|
|
|
1005
1013
|
document.addEventListener('mouseup', onUp, { once: true });
|
|
1006
1014
|
});
|
|
1007
1015
|
|
|
1008
|
-
btn.addEventListener('click', () => {
|
|
1009
|
-
|
|
1016
|
+
btn.addEventListener('click', (e) => {
|
|
1017
|
+
// Блокируем обработку клика если был drag или если уже обрабатывается
|
|
1018
|
+
if (btn.__dragActive || btn.__clickProcessing) return;
|
|
1019
|
+
|
|
1020
|
+
btn.__clickProcessing = true;
|
|
1021
|
+
setTimeout(() => { btn.__clickProcessing = false; }, 100);
|
|
1022
|
+
|
|
1010
1023
|
this.animateButton(btn);
|
|
1011
1024
|
const target = 64; // кратно 128 для лучшей четкости при даунскейле
|
|
1012
1025
|
const targetW = target;
|
|
@@ -1079,22 +1092,43 @@ export class Toolbar {
|
|
|
1079
1092
|
* Определяет базовый путь для эмоджи в зависимости от режима
|
|
1080
1093
|
*/
|
|
1081
1094
|
getEmojiBasePath() {
|
|
1082
|
-
//
|
|
1095
|
+
// 1. Приоритет: опция basePath из конструктора
|
|
1096
|
+
if (this.emojiBasePath) {
|
|
1097
|
+
return this.emojiBasePath.endsWith('/') ? this.emojiBasePath : this.emojiBasePath + '/';
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
// 2. Глобальная настройка (абсолютный URL)
|
|
1083
1101
|
if (window.MOODBOARD_BASE_PATH) {
|
|
1084
|
-
|
|
1102
|
+
const basePath = window.MOODBOARD_BASE_PATH.endsWith('/') ? window.MOODBOARD_BASE_PATH : window.MOODBOARD_BASE_PATH + '/';
|
|
1103
|
+
return `${basePath}src/assets/emodji/`;
|
|
1104
|
+
}
|
|
1105
|
+
|
|
1106
|
+
// 3. Вычисление от URL текущего модуля (import.meta.url)
|
|
1107
|
+
try {
|
|
1108
|
+
// Используем import.meta.url для получения абсолютного пути к ассетам
|
|
1109
|
+
const currentModuleUrl = import.meta.url;
|
|
1110
|
+
// От текущего модуля (ui/Toolbar.js) поднимаемся к корню пакета и идем к assets
|
|
1111
|
+
const emojiUrl = new URL('../assets/emodji/', currentModuleUrl).href;
|
|
1112
|
+
return emojiUrl;
|
|
1113
|
+
} catch (error) {
|
|
1114
|
+
console.warn('⚠️ Не удалось определить путь через import.meta.url:', error);
|
|
1085
1115
|
}
|
|
1086
1116
|
|
|
1087
|
-
//
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
if (
|
|
1091
|
-
|
|
1092
|
-
|
|
1117
|
+
// 4. Fallback: поиск script тега для определения базового URL
|
|
1118
|
+
try {
|
|
1119
|
+
const currentScript = document.currentScript;
|
|
1120
|
+
if (currentScript && currentScript.src) {
|
|
1121
|
+
// Пытаемся определить от текущего скрипта
|
|
1122
|
+
const scriptUrl = new URL(currentScript.src);
|
|
1123
|
+
const baseUrl = new URL('../assets/emodji/', scriptUrl).href;
|
|
1124
|
+
return baseUrl;
|
|
1093
1125
|
}
|
|
1126
|
+
} catch (error) {
|
|
1127
|
+
console.warn('⚠️ Не удалось определить путь через currentScript:', error);
|
|
1094
1128
|
}
|
|
1095
1129
|
|
|
1096
|
-
//
|
|
1097
|
-
return '
|
|
1130
|
+
// 5. Последний fallback: абсолютный путь от корня домена
|
|
1131
|
+
return '/src/assets/emodji/';
|
|
1098
1132
|
}
|
|
1099
1133
|
|
|
1100
1134
|
toggleEmojiPopup(anchorButton) {
|
|
@@ -1357,7 +1391,6 @@ export class Toolbar {
|
|
|
1357
1391
|
* @param {string} iconName - имя иконки
|
|
1358
1392
|
*/
|
|
1359
1393
|
async reloadToolbarIcon(iconName) {
|
|
1360
|
-
console.log(`🔄 Начинаем обновление иконки ${iconName} в тулбаре...`);
|
|
1361
1394
|
try {
|
|
1362
1395
|
// Перезагружаем иконку
|
|
1363
1396
|
const newSvgContent = await this.iconLoader.reloadIcon(iconName);
|
|
@@ -1374,9 +1407,7 @@ export class Toolbar {
|
|
|
1374
1407
|
|
|
1375
1408
|
// Добавляем новый SVG
|
|
1376
1409
|
this.createSvgIcon(button, iconName);
|
|
1377
|
-
console.log(`✅ Иконка ${iconName} обновлена в интерфейсе!`);
|
|
1378
1410
|
} else {
|
|
1379
|
-
console.warn(`⚠️ Кнопка с иконкой ${iconName} не найдена`);
|
|
1380
1411
|
}
|
|
1381
1412
|
} catch (error) {
|
|
1382
1413
|
console.error(`❌ Ошибка обновления иконки ${iconName}:`, error);
|
package/src/ui/Topbar.js
CHANGED
|
@@ -64,9 +64,6 @@ export class Topbar {
|
|
|
64
64
|
this.element = document.createElement('div');
|
|
65
65
|
this.element.className = `moodboard-topbar moodboard-topbar--${this.theme}`;
|
|
66
66
|
|
|
67
|
-
console.log('🔍 Topbar: создаю верхнюю панель');
|
|
68
|
-
console.log('🔍 Topbar: this.icons содержит:', this.icons);
|
|
69
|
-
console.log('🔍 Topbar: количество иконок:', Object.keys(this.icons).length);
|
|
70
67
|
|
|
71
68
|
// Кнопки выбора вида сетки (без функциональности)
|
|
72
69
|
const buttons = [
|
|
@@ -80,14 +77,11 @@ export class Topbar {
|
|
|
80
77
|
const btn = document.createElement('button');
|
|
81
78
|
btn.className = 'moodboard-topbar__button';
|
|
82
79
|
|
|
83
|
-
console.log(`🔍 Topbar: создаю кнопку для иконки "${cfg.icon}"`);
|
|
84
80
|
|
|
85
81
|
// Создаем SVG иконку
|
|
86
82
|
if (this.icons[cfg.icon]) {
|
|
87
|
-
console.log(`✅ Topbar: иконка "${cfg.icon}" найдена, создаю SVG`);
|
|
88
83
|
this.createSvgIcon(btn, cfg.icon);
|
|
89
84
|
} else {
|
|
90
|
-
console.warn(`⚠️ Topbar: иконка "${cfg.icon}" не найдена, создаю fallback`);
|
|
91
85
|
// Fallback: создаем простую текстовую иконку
|
|
92
86
|
const fallbackIcon = document.createElement('span');
|
|
93
87
|
fallbackIcon.textContent = cfg.icon.charAt(0).toUpperCase();
|
|
@@ -111,14 +105,11 @@ export class Topbar {
|
|
|
111
105
|
paintBtn.className = 'moodboard-topbar__button moodboard-topbar__button--paint';
|
|
112
106
|
paintBtn.title = 'Палитра фона';
|
|
113
107
|
|
|
114
|
-
console.log('🔍 Topbar: создаю кнопку краски');
|
|
115
108
|
|
|
116
109
|
// Создаем SVG иконку
|
|
117
110
|
if (this.icons['paint']) {
|
|
118
|
-
console.log('✅ Topbar: иконка paint найдена, создаю SVG');
|
|
119
111
|
this.createSvgIcon(paintBtn, 'paint');
|
|
120
112
|
} else {
|
|
121
|
-
console.warn('⚠️ Topbar: иконка paint не найдена, создаю fallback');
|
|
122
113
|
// Fallback: создаем простую текстовую иконку
|
|
123
114
|
const fallbackIcon = document.createElement('span');
|
|
124
115
|
fallbackIcon.textContent = '🎨';
|
|
@@ -144,11 +135,8 @@ export class Topbar {
|
|
|
144
135
|
* Создает SVG иконку для кнопки
|
|
145
136
|
*/
|
|
146
137
|
createSvgIcon(button, iconName) {
|
|
147
|
-
console.log(`🔍 Topbar: создаю SVG иконку "${iconName}"`);
|
|
148
|
-
console.log(`🔍 Topbar: доступные иконки:`, Object.keys(this.icons));
|
|
149
138
|
|
|
150
139
|
if (this.icons[iconName]) {
|
|
151
|
-
console.log(`✅ Topbar: иконка "${iconName}" найдена, создаю SVG элемент`);
|
|
152
140
|
// Создаем SVG элемент из загруженного содержимого
|
|
153
141
|
const tempDiv = document.createElement('div');
|
|
154
142
|
tempDiv.innerHTML = this.icons[iconName];
|
|
@@ -162,12 +150,9 @@ export class Topbar {
|
|
|
162
150
|
|
|
163
151
|
// Добавляем SVG в кнопку
|
|
164
152
|
button.appendChild(svg);
|
|
165
|
-
console.log(`✅ Topbar: SVG иконка "${iconName}" успешно добавлена в кнопку`);
|
|
166
153
|
} else {
|
|
167
|
-
console.warn(`⚠️ Topbar: не удалось найти SVG элемент в содержимом иконки "${iconName}"`);
|
|
168
154
|
}
|
|
169
155
|
} else {
|
|
170
|
-
console.warn(`⚠️ Topbar: иконка "${iconName}" не найдена в this.icons`);
|
|
171
156
|
}
|
|
172
157
|
}
|
|
173
158
|
|
|
@@ -67,13 +67,18 @@ export function emojiToLocalUrl(emoji) {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
// Карта локальных изображений (SVG и PNG) из src/assets/emodji
|
|
71
|
-
//
|
|
72
|
-
const _localEmojiModules =
|
|
73
|
-
|
|
74
|
-
|
|
70
|
+
// Карта локальных изображений (SVG и PNG) из src/assets/emodji
|
|
71
|
+
// В режиме с bundler используем import.meta.glob, иначе fallback
|
|
72
|
+
const _localEmojiModules = (() => {
|
|
73
|
+
if (typeof import.meta !== 'undefined' && import.meta.glob) {
|
|
74
|
+
try {
|
|
75
|
+
return import.meta.glob('../assets/emodji/**/*.{svg,SVG,png,PNG}', { eager: true, query: '?url', import: 'default' });
|
|
76
|
+
} catch (error) {
|
|
77
|
+
return {};
|
|
75
78
|
}
|
|
76
|
-
|
|
79
|
+
}
|
|
80
|
+
return {};
|
|
81
|
+
})();
|
|
77
82
|
|
|
78
83
|
// Индекс по имени файла (без пути)
|
|
79
84
|
const _localEmojiIndex = (() => {
|
|
@@ -119,3 +124,43 @@ export function buildLocalPaths(emoji) {
|
|
|
119
124
|
}
|
|
120
125
|
}
|
|
121
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Возвращает абсолютный URL для эмоджи с учетом базового пути
|
|
129
|
+
* @param {string} emoji - эмоджи символ
|
|
130
|
+
* @param {string} basePath - базовый путь к ассетам
|
|
131
|
+
* @returns {string|null} абсолютный URL или null
|
|
132
|
+
*/
|
|
133
|
+
export function resolveEmojiAbsoluteUrl(emoji, basePath = null) {
|
|
134
|
+
try {
|
|
135
|
+
const base = emojiFilenameBase(emoji);
|
|
136
|
+
if (!base) return null;
|
|
137
|
+
|
|
138
|
+
// Определяем базовый путь
|
|
139
|
+
let resolvedBasePath = basePath;
|
|
140
|
+
|
|
141
|
+
if (!resolvedBasePath) {
|
|
142
|
+
// Пытаемся определить от import.meta.url
|
|
143
|
+
try {
|
|
144
|
+
resolvedBasePath = new URL('../assets/emodji/', import.meta.url).href;
|
|
145
|
+
} catch (error) {
|
|
146
|
+
// Fallback на глобальную настройку
|
|
147
|
+
if (window.MOODBOARD_BASE_PATH) {
|
|
148
|
+
const globalPath = window.MOODBOARD_BASE_PATH.endsWith('/') ? window.MOODBOARD_BASE_PATH : window.MOODBOARD_BASE_PATH + '/';
|
|
149
|
+
resolvedBasePath = `${globalPath}src/assets/emodji/`;
|
|
150
|
+
} else {
|
|
151
|
+
resolvedBasePath = '/src/assets/emodji/';
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Формируем URL (приоритет PNG, потом SVG)
|
|
157
|
+
if (!resolvedBasePath.endsWith('/')) resolvedBasePath += '/';
|
|
158
|
+
|
|
159
|
+
// Возвращаем URL в формате готовом для использования
|
|
160
|
+
return `${resolvedBasePath}Смайлики/${base}.png`; // Большинство эмоджи в папке Смайлики
|
|
161
|
+
|
|
162
|
+
} catch (error) {
|
|
163
|
+
return null;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
package/src/utils/styleLoader.js
CHANGED
|
@@ -19,18 +19,15 @@ export class StyleLoader {
|
|
|
19
19
|
'src/ui/styles/panels.css'
|
|
20
20
|
];
|
|
21
21
|
|
|
22
|
-
console.log('🎨 StyleLoader: Загружаем стили MoodBoard...');
|
|
23
22
|
|
|
24
23
|
for (const stylePath of styles) {
|
|
25
24
|
try {
|
|
26
25
|
await this.loadStyle(basePath + stylePath);
|
|
27
|
-
console.log(`✅ Стиль загружен: ${stylePath}`);
|
|
28
26
|
} catch (error) {
|
|
29
27
|
console.warn(`⚠️ Ошибка загрузки стиля ${stylePath}:`, error);
|
|
30
28
|
}
|
|
31
29
|
}
|
|
32
30
|
|
|
33
|
-
console.log('🎨 StyleLoader: Все стили загружены');
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
/**
|