@sequent-org/moodboard 1.2.94 → 1.2.95

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.94",
3
+ "version": "1.2.95",
4
4
  "type": "module",
5
5
  "description": "Interactive moodboard",
6
6
  "main": "./src/index.js",
@@ -530,7 +530,6 @@ export class PlacementTool extends BaseTool {
530
530
  const world = this.app.stage.getChildByName && this.app.stage.getChildByName('worldLayer');
531
531
  return world || this.app.stage;
532
532
  }
533
-
534
533
 
535
534
  /**
536
535
  * Обработчик движения мыши для обновления позиции "призрака"
@@ -2032,30 +2032,10 @@ export class SelectTool extends BaseTool {
2032
2032
  }
2033
2033
  } catch (_) {}
2034
2034
 
2035
- // Учитываем масштаб браузера для позиционирования формы редактирования
2036
- const zoomFactor = this._getBrowserZoomFactor();
2037
- const correctedBaseLeftPx = baseLeftPx / zoomFactor;
2038
- const correctedBaseTopPx = baseTopPx / zoomFactor;
2039
-
2040
- console.log('🔍 Browser zoom detection:', {
2041
- zoomFactor,
2042
- basePos: { left: baseLeftPx, top: baseTopPx },
2043
- correctedPos: { left: correctedBaseLeftPx, top: correctedBaseTopPx },
2044
- screenPos,
2045
- window: {
2046
- innerWidth: window.innerWidth,
2047
- outerWidth: window.outerWidth,
2048
- visualViewport: window.visualViewport ? {
2049
- width: window.visualViewport.width,
2050
- height: window.visualViewport.height
2051
- } : null
2052
- }
2053
- });
2054
-
2055
- const leftPx = Math.round(correctedBaseLeftPx - padLeft);
2035
+ const leftPx = Math.round(baseLeftPx - padLeft);
2056
2036
  const topPx = create
2057
- ? Math.round(correctedBaseTopPx - padTop - (lineHeightPx / 2)) // по клику совмещаем центр строки с точкой клика
2058
- : Math.round(correctedBaseTopPx - padTop); // при редактировании совмещаем верх контента
2037
+ ? Math.round(baseTopPx - padTop - (lineHeightPx / 2)) // по клику совмещаем центр строки с точкой клика
2038
+ : Math.round(baseTopPx - padTop); // при редактировании совмещаем верх контента
2059
2039
  wrapper.style.left = `${leftPx}px`;
2060
2040
  wrapper.style.top = `${topPx}px`;
2061
2041
  // Сохраняем CSS-позицию редактора для точной синхронизации при закрытии
@@ -2824,84 +2804,5 @@ export class SelectTool extends BaseTool {
2824
2804
  // Обновляем ручки
2825
2805
  this.updateResizeHandles();
2826
2806
  }
2827
-
2828
- /**
2829
- * Получение коэффициента масштабирования браузера
2830
- */
2831
- _getBrowserZoomFactor() {
2832
- // Новый подход: через измерение canvas размеров
2833
- try {
2834
- if (this.app && this.app.view) {
2835
- const canvas = this.app.view;
2836
-
2837
- // Получаем CSS размеры и реальные размеры canvas
2838
- const cssWidth = canvas.clientWidth;
2839
- const cssHeight = canvas.clientHeight;
2840
- const realWidth = canvas.width;
2841
- const realHeight = canvas.height;
2842
-
2843
- // Проверяем device pixel ratio
2844
- const dpr = window.devicePixelRatio || 1;
2845
-
2846
- // Вычисляем ожидаемые реальные размеры с учетом DPR
2847
- const expectedRealWidth = cssWidth * dpr;
2848
- const expectedRealHeight = cssHeight * dpr;
2849
-
2850
- // Если реальные размеры отличаются от ожидаемых, значит есть zoom
2851
- let zoomFactorW = 1.0;
2852
- let zoomFactorH = 1.0;
2853
-
2854
- if (expectedRealWidth > 0) {
2855
- zoomFactorW = realWidth / expectedRealWidth;
2856
- }
2857
- if (expectedRealHeight > 0) {
2858
- zoomFactorH = realHeight / expectedRealHeight;
2859
- }
2860
-
2861
- // Берем среднее значение или наиболее близкое к 1.0
2862
- const avgZoom = (zoomFactorW + zoomFactorH) / 2;
2863
-
2864
- console.log('🔍 Canvas zoom detection:', {
2865
- css: { width: cssWidth, height: cssHeight },
2866
- real: { width: realWidth, height: realHeight },
2867
- dpr,
2868
- expected: { width: expectedRealWidth, height: expectedRealHeight },
2869
- zoom: { width: zoomFactorW, height: zoomFactorH, avg: avgZoom }
2870
- });
2871
-
2872
- // Если обнаружили разумное отклонение от 1.0, используем его
2873
- if (isFinite(avgZoom) && avgZoom > 0.1 && avgZoom < 10 && Math.abs(avgZoom - 1.0) > 0.01) {
2874
- return avgZoom;
2875
- }
2876
- }
2877
- } catch (_) {}
2878
-
2879
- // Fallback: простой метод через screen размеры
2880
- try {
2881
- // Сравниваем screen.width с window.innerWidth
2882
- const screenRatio = screen.width / window.innerWidth;
2883
- const dpr = window.devicePixelRatio || 1;
2884
- const expectedRatio = dpr;
2885
-
2886
- if (Math.abs(screenRatio - expectedRatio) > 0.1) {
2887
- const zoomFactor = screenRatio / expectedRatio;
2888
-
2889
- console.log('🔍 Screen zoom detection:', {
2890
- screen: { width: screen.width, height: screen.height },
2891
- window: { innerWidth: window.innerWidth, innerHeight: window.innerHeight },
2892
- screenRatio,
2893
- expectedRatio,
2894
- zoomFactor
2895
- });
2896
-
2897
- if (isFinite(zoomFactor) && zoomFactor > 0.1 && zoomFactor < 10) {
2898
- return zoomFactor;
2899
- }
2900
- }
2901
- } catch (_) {}
2902
-
2903
- console.log('🔍 Zoom detection: using fallback (no zoom correction)');
2904
- return 1.0; // Fallback: без коррекции
2905
- }
2906
2807
 
2907
2808
  }
@@ -1,4 +1,21 @@
1
1
  import { BaseTool } from '../BaseTool.js';
2
+ import iCursorSvg from '../../assets/icons/i-cursor.svg?raw';
3
+
4
+ // Масштабируем I-курсор в 2 раза меньше (примерно 16x32 с сохранением пропорций 1:2)
5
+ const _scaledICursorSvg = (() => {
6
+ try {
7
+ if (!/\bwidth="/i.test(iCursorSvg)) {
8
+ return iCursorSvg.replace('<svg ', '<svg width="16px" height="32px" ');
9
+ }
10
+ return iCursorSvg
11
+ .replace(/width="[^"]+"/i, 'width="16px"')
12
+ .replace(/height="[^"]+"/i, 'height="32px"');
13
+ } catch (_) {
14
+ return iCursorSvg;
15
+ }
16
+ })();
17
+
18
+ const TEXT_CURSOR = `url("data:image/svg+xml;charset=utf-8,${encodeURIComponent(_scaledICursorSvg)}") 0 0, text`;
2
19
 
3
20
  /**
4
21
  * Инструмент для создания и редактирования текстовых объектов
@@ -6,7 +23,7 @@ import { BaseTool } from '../BaseTool.js';
6
23
  export class TextTool extends BaseTool {
7
24
  constructor(eventBus) {
8
25
  super('text', eventBus);
9
- this.cursor = 'text';
26
+ this.cursor = 'text';
10
27
  this.hotkey = 't';
11
28
  this.container = null;
12
29
 
@@ -32,35 +49,13 @@ export class TextTool extends BaseTool {
32
49
  onMouseDown(event) {
33
50
  super.onMouseDown(event);
34
51
 
35
- // Учитываем масштаб браузера для корректных координат
36
- const zoomFactor = this._getBrowserZoomFactor();
37
- const correctedX = event.x * zoomFactor;
38
- const correctedY = event.y * zoomFactor;
39
-
40
- // Диагностика координат для решения проблемы с масштабом браузера
41
- console.log('🔍 TextTool onMouseDown диагностика:', {
42
- 'исходные event.x/y': { x: event.x, y: event.y },
43
- 'zoomFactor': zoomFactor,
44
- 'исправленные x/y': { x: correctedX, y: correctedY },
45
- 'window.devicePixelRatio': window.devicePixelRatio,
46
- 'масштаб браузера': window.outerWidth / window.innerWidth
47
- });
48
-
49
- // Преобразуем экранные координаты в мировые координаты PIXI
50
- const worldPoint = this._toWorld(correctedX, correctedY);
51
-
52
- console.log('🔍 Преобразование координат:', {
53
- 'исправленные': { x: correctedX, y: correctedY },
54
- 'мировые': worldPoint
55
- });
56
-
57
52
  // Проверяем, кликнули ли на существующий текстовый объект
58
- const hitObject = this.getTextObjectAt(correctedX, correctedY);
53
+ const hitObject = this.getTextObjectAt(event.x, event.y);
59
54
 
60
55
  if (hitObject) {
61
56
  this.startEditing(hitObject, event);
62
57
  } else {
63
- this.createNewText(worldPoint.x, worldPoint.y);
58
+ this.createNewText(event.x, event.y);
64
59
  }
65
60
  }
66
61
 
@@ -137,19 +132,10 @@ export class TextTool extends BaseTool {
137
132
  /**
138
133
  * Создание HTML input для редактирования текста
139
134
  */
140
- createTextInput(worldX, worldY, initialText) {
135
+ createTextInput(x, y, initialText) {
141
136
  // Удаляем предыдущий input если есть
142
137
  this.removeTextInput();
143
138
 
144
- // Преобразуем мировые координаты в экранные для HTML элемента
145
- const screenPos = this._toScreen(worldX, worldY);
146
-
147
- console.log('🔍 Позиционирование input:', {
148
- 'мировые координаты': { x: worldX, y: worldY },
149
- 'экранные координаты': screenPos,
150
- 'container': this.getContainer()
151
- });
152
-
153
139
  // Создаем новый input
154
140
  this.textInput = document.createElement('textarea');
155
141
  this.textInput.value = initialText;
@@ -158,8 +144,8 @@ export class TextTool extends BaseTool {
158
144
  // Стили input
159
145
  Object.assign(this.textInput.style, {
160
146
  position: 'absolute',
161
- left: `${screenPos.x}px`,
162
- top: `${screenPos.y}px`,
147
+ left: `${x}px`,
148
+ top: `${y}px`,
163
149
  border: '2px solid #007bff',
164
150
  borderRadius: '4px',
165
151
  padding: '4px 8px',
@@ -415,15 +401,14 @@ export class TextTool extends BaseTool {
415
401
  */
416
402
  activate(pixiApp) {
417
403
  super.activate();
418
- this.pixiApp = pixiApp;
419
404
 
420
405
  // Устанавливаем контейнер для размещения input
421
406
  if (pixiApp && pixiApp.view && pixiApp.view.parentElement) {
422
407
  this.setContainer(pixiApp.view.parentElement);
423
408
  }
424
- // Устанавливаем стандартный текстовый курсор
409
+ // Устанавливаем кастомный курсор на холст
425
410
  if (pixiApp && pixiApp.view) {
426
- pixiApp.view.style.cursor = 'text';
411
+ pixiApp.view.style.cursor = (this.cursor && this.cursor !== 'default') ? this.cursor : '';
427
412
  }
428
413
  }
429
414
 
@@ -436,50 +421,6 @@ export class TextTool extends BaseTool {
436
421
  }
437
422
  }
438
423
 
439
- /**
440
- * Преобразование экранных координат в мировые (worldLayer)
441
- */
442
- _toWorld(x, y) {
443
- if (!this.pixiApp || !this.pixiApp.stage) return { x, y };
444
- const world = this.pixiApp.stage.getChildByName && this.pixiApp.stage.getChildByName('worldLayer');
445
- if (!world || !world.toLocal) return { x, y };
446
- const p = new PIXI.Point(x, y);
447
- const local = world.toLocal(p);
448
- return { x: local.x, y: local.y };
449
- }
450
-
451
- /**
452
- * Преобразование мировых координат в экранные для HTML элементов
453
- */
454
- _toScreen(worldX, worldY) {
455
- if (!this.pixiApp || !this.pixiApp.stage) return { x: worldX, y: worldY };
456
- const world = this.pixiApp.stage.getChildByName && this.pixiApp.stage.getChildByName('worldLayer');
457
- if (!world || !world.toGlobal) return { x: worldX, y: worldY };
458
- const p = new PIXI.Point(worldX, worldY);
459
- const global = world.toGlobal(p);
460
-
461
- // Обратная коррекция для HTML элементов с учетом масштаба браузера
462
- const zoomFactor = this._getBrowserZoomFactor();
463
- return {
464
- x: global.x / zoomFactor,
465
- y: global.y / zoomFactor
466
- };
467
- }
468
-
469
- /**
470
- * Получение коэффициента масштабирования браузера
471
- */
472
- _getBrowserZoomFactor() {
473
- // Определяем масштаб браузера разными способами
474
- const outerInnerRatio = window.outerWidth / window.innerWidth;
475
- const devicePixelRatio = window.devicePixelRatio || 1;
476
-
477
- // Используемый подход: соотношение размеров окна браузера
478
- const zoomFactor = outerInnerRatio;
479
-
480
- return Math.max(0.1, Math.min(5, zoomFactor)); // Ограничиваем разумными пределами
481
- }
482
-
483
424
  /**
484
425
  * Установка настроек текста по умолчанию
485
426
  */
@@ -305,15 +305,14 @@ export class HtmlTextLayer {
305
305
  el.style.lineHeight = newLH;
306
306
  }
307
307
 
308
- // Позиция и габариты в экранных координатах с учетом масштаба браузера
309
- const zoomFactor = this._getBrowserZoomFactor();
310
- const left = ((tx + s * x) / res) / zoomFactor;
311
- const top = ((ty + s * y) / res) / zoomFactor;
308
+ // Позиция и габариты в экранных координатах
309
+ const left = (tx + s * x) / res;
310
+ const top = (ty + s * y) / res;
312
311
  el.style.left = `${left}px`;
313
312
  el.style.top = `${top}px`;
314
313
  if (w && h) {
315
- el.style.width = `${Math.max(1, ((w * s) / res) / zoomFactor)}px`;
316
- el.style.height = `${Math.max(1, ((h * s) / res) / zoomFactor)}px`;
314
+ el.style.width = `${Math.max(1, (w * s) / res)}px`;
315
+ el.style.height = `${Math.max(1, (h * s) / res)}px`;
317
316
  }
318
317
  // Поворот вокруг центра (как у PIXI и HTML-ручек)
319
318
  el.style.transformOrigin = 'center center';
@@ -369,68 +368,7 @@ export class HtmlTextLayer {
369
368
  position
370
369
  });
371
370
  }
372
- } catch (_) { }
373
- }
374
-
375
- /**
376
- * Получение коэффициента масштабирования браузера
377
- */
378
- _getBrowserZoomFactor() {
379
- // Новый подход: через измерение canvas размеров
380
- try {
381
- if (this.core && this.core.pixi && this.core.pixi.app && this.core.pixi.app.view) {
382
- const canvas = this.core.pixi.app.view;
383
-
384
- // Получаем CSS размеры и реальные размеры canvas
385
- const cssWidth = canvas.clientWidth;
386
- const cssHeight = canvas.clientHeight;
387
- const realWidth = canvas.width;
388
- const realHeight = canvas.height;
389
-
390
- // Проверяем device pixel ratio
391
- const dpr = window.devicePixelRatio || 1;
392
-
393
- // Вычисляем ожидаемые реальные размеры с учетом DPR
394
- const expectedRealWidth = cssWidth * dpr;
395
- const expectedRealHeight = cssHeight * dpr;
396
-
397
- // Если реальные размеры отличаются от ожидаемых, значит есть zoom
398
- let zoomFactorW = 1.0;
399
- let zoomFactorH = 1.0;
400
-
401
- if (expectedRealWidth > 0) {
402
- zoomFactorW = realWidth / expectedRealWidth;
403
- }
404
- if (expectedRealHeight > 0) {
405
- zoomFactorH = realHeight / expectedRealHeight;
406
- }
407
-
408
- // Берем среднее значение
409
- const avgZoom = (zoomFactorW + zoomFactorH) / 2;
410
-
411
- // Если обнаружили разумное отклонение от 1.0, используем его
412
- if (isFinite(avgZoom) && avgZoom > 0.1 && avgZoom < 10 && Math.abs(avgZoom - 1.0) > 0.01) {
413
- return avgZoom;
414
- }
415
- }
416
371
  } catch (_) {}
417
-
418
- // Fallback: простой метод через screen размеры
419
- try {
420
- const screenRatio = screen.width / window.innerWidth;
421
- const dpr = window.devicePixelRatio || 1;
422
- const expectedRatio = dpr;
423
-
424
- if (Math.abs(screenRatio - expectedRatio) > 0.1) {
425
- const zoomFactor = screenRatio / expectedRatio;
426
-
427
- if (isFinite(zoomFactor) && zoomFactor > 0.1 && zoomFactor < 10) {
428
- return zoomFactor;
429
- }
430
- }
431
- } catch (_) {}
432
-
433
- return 1.0; // Fallback: без коррекции
434
372
  }
435
373
  }
436
374