@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sequent-org/moodboard",
3
- "version": "1.2.1",
3
+ "version": "1.2.3",
4
4
  "type": "module",
5
5
  "description": "Interactive moodboard",
6
6
  "main": "./src/index.js",
@@ -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
  }
@@ -22,7 +22,6 @@ export class RotateObjectCommand extends BaseCommand {
22
22
  objectId: this.objectId,
23
23
  angle: this.newAngle
24
24
  });
25
- console.log(`🔄 Поворачиваем объект ${this.objectId} на ${this.newAngle}°`);
26
25
  }
27
26
 
28
27
  undo() {
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
 
@@ -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
- this.showFileGhost();
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
- const pendingFont = (this.pending.properties?.fontFamily) || 'Caveat, Arial, cursive';
567
- const primaryFont = String(pendingFont).split(',')[0].trim().replace(/^['"]|['"]$/g, '') || 'Caveat';
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 maxWidth = this.selectedImage.properties.width || 300;
727
- const maxHeight = this.selectedImage.properties.height || 200;
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
- const maxWidth = this.pending.size?.width || this.pending.properties?.width || 56;
820
- const maxHeight = this.pending.size?.height || this.pending.properties?.height || 56;
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
- try {
858
- if (this.app && this.app.view && src) {
859
- const cursorSize = 24;
860
- const url = encodeURI(src);
861
- // Используем CSS cursor с изображением, если поддерживается
862
- this.app.view.style.cursor = `url(${url}) ${Math.floor(cursorSize/2)} ${Math.floor(cursorSize/2)}, default`;
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
- } catch (_) {}
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
- // Снимем флаг сразу после клика, чтобы click мог отфильтроваться
998
- setTimeout(() => { btn.__dragActive = false; }, 0);
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
- if (btn.__dragActive) return; // не обрабатываем клик после drag
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
- return `${window.MOODBOARD_BASE_PATH}src/assets/emodji/`;
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
- const scripts = document.querySelectorAll('script[src]');
1089
- for (const script of scripts) {
1090
- if (script.src.includes('moodboard') || script.src.includes('node_modules')) {
1091
- const baseUrl = new URL(script.src).origin;
1092
- return `${baseUrl}/node_modules/moodboard-futurello/src/assets/emodji/`;
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
- // Fallback: относительный путь
1097
- return './src/assets/emodji/';
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 (собирается Vite'ом)
71
- // Ключи вида '../assets/emodji/1f600.svg' / '../assets/emodji/1f600.png' URL
72
- const _localEmojiModules = import.meta && typeof import.meta.glob === 'function'
73
- ? {
74
- ...import.meta.glob('../assets/emodji/**/*.{svg,SVG,png,PNG}', { eager: true, query: '?url', import: 'default' })
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
+
@@ -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
  /**