@sequent-org/moodboard 1.4.31 → 1.4.33

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/package.json +5 -1
  2. package/src/assets/fonts/inter/inter-cyrillic-400-normal.woff2 +0 -0
  3. package/src/assets/fonts/inter/inter-cyrillic-500-normal.woff2 +0 -0
  4. package/src/assets/fonts/inter/inter-latin-400-normal.woff2 +0 -0
  5. package/src/assets/fonts/inter/inter-latin-500-normal.woff2 +0 -0
  6. package/src/assets/icons/attachments.svg +3 -1
  7. package/src/assets/icons/comments.svg +2 -2
  8. package/src/assets/icons/connector.svg +6 -0
  9. package/src/assets/icons/emoji.svg +6 -1
  10. package/src/assets/icons/frame.svg +4 -1
  11. package/src/assets/icons/image.svg +5 -1
  12. package/src/assets/icons/laser.svg +1 -0
  13. package/src/assets/icons/lasso.svg +5 -0
  14. package/src/assets/icons/mindmap.svg +10 -2
  15. package/src/assets/icons/note.svg +4 -1
  16. package/src/assets/icons/pan.svg +5 -2
  17. package/src/assets/icons/pencil.svg +4 -1
  18. package/src/assets/icons/reactions.svg +5 -0
  19. package/src/assets/icons/redo.svg +3 -2
  20. package/src/assets/icons/select.svg +2 -8
  21. package/src/assets/icons/shapes.svg +5 -1
  22. package/src/assets/icons/text-add.svg +15 -1
  23. package/src/assets/icons/undo.svg +3 -2
  24. package/src/assets/reactions/1f44d.svg +20 -0
  25. package/src/assets/reactions/1f44e.svg +20 -0
  26. package/src/assets/reactions/2705.svg +20 -0
  27. package/src/assets/reactions/274c.svg +19 -0
  28. package/src/assets/reactions/2753.svg +20 -0
  29. package/src/assets/reactions/2764.svg +22 -0
  30. package/src/assets/reactions/2b50.svg +19 -0
  31. package/src/assets/reactions/plus-one.svg +25 -0
  32. package/src/core/PixiEngine.js +23 -0
  33. package/src/core/bootstrap/CoreInitializer.js +43 -0
  34. package/src/core/commands/GroupDeleteCommand.js +13 -1
  35. package/src/core/commands/UpdateShapeStyleCommand.js +121 -0
  36. package/src/core/commands/UpdateTextStyleCommand.js +17 -6
  37. package/src/core/commands/index.js +3 -0
  38. package/src/core/events/Events.js +22 -0
  39. package/src/core/flows/LayerAndViewportFlow.js +1 -0
  40. package/src/core/flows/ObjectLifecycleFlow.js +155 -7
  41. package/src/core/index.js +28 -1
  42. package/src/grid/CrossGridZoomPhases.js +3 -3
  43. package/src/initNoBundler.js +1 -1
  44. package/src/moodboard/DataManager.js +28 -0
  45. package/src/moodboard/MoodBoard.js +27 -0
  46. package/src/moodboard/bootstrap/MoodBoardInitializer.js +69 -1
  47. package/src/moodboard/bootstrap/MoodBoardUiFactory.js +22 -4
  48. package/src/moodboard/integration/MoodBoardEventBindings.js +5 -1
  49. package/src/moodboard/integration/MoodBoardLoadApi.js +10 -1
  50. package/src/moodboard/lifecycle/MoodBoardDestroyer.js +9 -0
  51. package/src/objects/ConnectorObject.js +2 -2
  52. package/src/objects/FrameObject.js +119 -59
  53. package/src/objects/ShapeObject.js +49 -74
  54. package/src/objects/shape/ShapeDrawer.js +210 -0
  55. package/src/services/ConnectorBindingResolver.js +112 -0
  56. package/src/services/ConnectorRouter.js +210 -0
  57. package/src/services/comments/CommentService.js +344 -0
  58. package/src/tools/object-tools/CommentTool.js +85 -0
  59. package/src/tools/object-tools/DrawingTool.js +110 -10
  60. package/src/tools/object-tools/LaserPointerTool.js +121 -0
  61. package/src/tools/object-tools/SelectTool.js +25 -1
  62. package/src/tools/object-tools/TextTool.js +6 -1
  63. package/src/tools/object-tools/connector/ConnectorDragController.js +50 -3
  64. package/src/tools/object-tools/connector/connectorGesture.js +33 -19
  65. package/src/tools/object-tools/placement/PlacementInputRouter.js +22 -1
  66. package/src/tools/object-tools/selection/BoxSelectController.js +24 -2
  67. package/src/tools/object-tools/selection/FrameTitleInlineEditorController.js +139 -0
  68. package/src/tools/object-tools/selection/InlineEditorController.js +12 -0
  69. package/src/tools/object-tools/selection/InlineEditorDomFactory.js +36 -0
  70. package/src/tools/object-tools/selection/LassoSelectController.js +125 -0
  71. package/src/tools/object-tools/selection/MindmapInlineEditorController.js +1 -0
  72. package/src/tools/object-tools/selection/SelectInputRouter.js +64 -5
  73. package/src/tools/object-tools/selection/SelectToolLifecycleController.js +11 -1
  74. package/src/tools/object-tools/selection/SelectToolSetup.js +13 -1
  75. package/src/tools/object-tools/selection/TextEditorInteractionController.js +46 -12
  76. package/src/tools/object-tools/selection/TextEditorSyncService.js +1 -0
  77. package/src/tools/object-tools/selection/TextInlineEditorController.js +65 -6
  78. package/src/ui/CommentPopover.js +6 -0
  79. package/src/ui/CommentsBar.js +91 -0
  80. package/src/ui/ConnectorPropertiesPanel.js +150 -0
  81. package/src/ui/ContextMenu.js +25 -0
  82. package/src/ui/DrawingPropertiesPanel.js +362 -0
  83. package/src/ui/FilePropertiesPanel.js +5 -0
  84. package/src/ui/FramePropertiesPanel.js +5 -0
  85. package/src/ui/HtmlTextLayer.js +246 -66
  86. package/src/ui/NotePropertiesPanel.js +6 -0
  87. package/src/ui/ShapePropertiesPanel.js +307 -0
  88. package/src/ui/TextPropertiesPanel.js +100 -1
  89. package/src/ui/Toolbar.js +25 -2
  90. package/src/ui/Topbar.js +2 -2
  91. package/src/ui/animation/HoverLiftController.js +6 -7
  92. package/src/ui/chat/ChatComposer.js +59 -12
  93. package/src/ui/chat/ChatExtendedPromptModal.js +1 -12
  94. package/src/ui/chat/ChatWindow.js +60 -144
  95. package/src/ui/chat/ChatWindowRenderer.js +1 -8
  96. package/src/ui/chat/icons.js +0 -4
  97. package/src/ui/comments/CommentListPanel.js +213 -0
  98. package/src/ui/comments/CommentPinLayer.js +448 -0
  99. package/src/ui/comments/CommentThreadPopover.js +539 -0
  100. package/src/ui/comments/commentFormat.js +32 -0
  101. package/src/ui/connector-properties/ConnectorPropertiesPanelBindings.js +223 -0
  102. package/src/ui/connector-properties/ConnectorPropertiesPanelEventBridge.js +114 -0
  103. package/src/ui/connector-properties/ConnectorPropertiesPanelMapper.js +144 -0
  104. package/src/ui/connector-properties/ConnectorPropertiesPanelRenderer.js +447 -0
  105. package/src/ui/connector-properties/ConnectorPropertiesPanelState.js +61 -0
  106. package/src/ui/connectors/ConnectionAnchorsLayer.js +1 -0
  107. package/src/ui/connectors/ConnectorHandlesLayer.js +321 -0
  108. package/src/ui/connectors/ConnectorLabelLayer.js +334 -0
  109. package/src/ui/connectors/ConnectorLayer.js +264 -57
  110. package/src/ui/handles/HandlesDomRenderer.js +5 -13
  111. package/src/ui/handles/HandlesEventBridge.js +1 -0
  112. package/src/ui/handles/SingleSelectionHandlesController.js +4 -0
  113. package/src/ui/mindmap/MindmapCollapseLayer.js +1 -0
  114. package/src/ui/mindmap/MindmapConnectionLayer.js +1 -0
  115. package/src/ui/mindmap/MindmapHtmlTextLayer.js +6 -0
  116. package/src/ui/shape-properties/ShapePropertiesPanelDom.js +533 -0
  117. package/src/ui/shape-properties/ShapePropertiesPanelSync.js +132 -0
  118. package/src/ui/styles/chat.css +682 -28
  119. package/src/ui/styles/index.css +1 -0
  120. package/src/ui/styles/panels.css +112 -2
  121. package/src/ui/styles/shape-properties-panel.css +250 -0
  122. package/src/ui/styles/toolbar.css +7 -2
  123. package/src/ui/styles/topbar.css +1 -1
  124. package/src/ui/styles/workspace.css +257 -6
  125. package/src/ui/text-properties/TextFormatControls.js +88 -0
  126. package/src/ui/text-properties/TextListRenderer.js +137 -0
  127. package/src/ui/text-properties/TextPropertiesPanelBindings.js +27 -0
  128. package/src/ui/text-properties/TextPropertiesPanelEventBridge.js +3 -1
  129. package/src/ui/text-properties/TextPropertiesPanelMapper.js +56 -0
  130. package/src/ui/text-properties/TextPropertiesPanelRenderer.js +24 -0
  131. package/src/ui/text-properties/TextPropertiesPanelState.js +8 -0
  132. package/src/ui/toolbar/ReactionsPopupController.js +88 -0
  133. package/src/ui/toolbar/ToolbarActionRouter.js +71 -5
  134. package/src/ui/toolbar/ToolbarPopupsController.js +120 -118
  135. package/src/ui/toolbar/ToolbarRenderer.js +9 -1
  136. package/src/ui/toolbar/ToolbarStateController.js +4 -1
  137. package/src/utils/iconLoader.js +17 -16
  138. package/src/utils/markdown.js +14 -0
  139. package/src/utils/richText.js +125 -0
@@ -0,0 +1,362 @@
1
+ import { Events } from '../core/events/Events.js';
2
+
3
+ /**
4
+ * DrawingPropertiesPanel — плавающая панель свойств выделённой линии (рисунка).
5
+ * Появляется при одиночном выделении объекта типа 'drawing'.
6
+ * Позволяет менять толщину и цвет. Изменения эмитятся через Events.Object.StateChanged
7
+ * (применяются в ObjectLifecycleFlow: instance.setStyle + обновление state.properties).
8
+ *
9
+ * Дублирует часть палитры рисования (см. ToolbarPopupsController.createDrawPopup),
10
+ * поэтому переиспользует те же CSS-классы moodboard-draw__*.
11
+ */
12
+
13
+ const PALETTE = [
14
+ '#111827', '#374151', '#9ca3af', '#d1d5db', '#ffffff',
15
+ '#ef4444', '#f97316', '#facc15', '#22c55e', '#3b82f6',
16
+ '#fca5a5', '#fdba74', '#fde68a', '#86efac', '#93c5fd',
17
+ '#f9a8d4', '#e9d5ff', '#c4b5fd', '#a5f3fc', '#bfdbfe',
18
+ ];
19
+
20
+ const MIN_WIDTH = 1;
21
+ const MAX_WIDTH = 24;
22
+
23
+ export class DrawingPropertiesPanel {
24
+ constructor(eventBus, container, core = null) {
25
+ this.eventBus = eventBus;
26
+ this.container = container;
27
+ this.core = core;
28
+ this.panel = null;
29
+ this.currentId = null;
30
+
31
+ this._slider = null;
32
+ this._widthValue = null;
33
+ this._colorGrid = null;
34
+ this._customBtn = null;
35
+ this._colorInput = null;
36
+
37
+ this._attachEvents();
38
+ this._createPanel();
39
+ }
40
+
41
+ // ── Публичное API ──────────────────────────────────────────────────────────
42
+
43
+ updateFromSelection() {
44
+ const ids = this.core?.selectTool
45
+ ? Array.from(this.core.selectTool.selectedObjects || [])
46
+ : [];
47
+
48
+ if (!ids || ids.length !== 1) { this.hide(); return; }
49
+
50
+ const id = ids[0];
51
+ const pixi = this.core?.pixi?.objects?.get ? this.core.pixi.objects.get(id) : null;
52
+ const isDrawing = !!(pixi && pixi._mb && pixi._mb.type === 'drawing');
53
+
54
+ if (!isDrawing) { this.hide(); return; }
55
+
56
+ this.currentId = id;
57
+ if (this.panel) {
58
+ this.panel.style.display = 'flex';
59
+ this._updateControlsFromObject();
60
+ this.reposition();
61
+ }
62
+ }
63
+
64
+ hide() {
65
+ this.currentId = null;
66
+ if (this.panel) this.panel.style.display = 'none';
67
+ }
68
+
69
+ reposition() {
70
+ if (!this.panel || !this.currentId || this.panel.style.display === 'none') return;
71
+
72
+ const ids = this.core?.selectTool
73
+ ? Array.from(this.core.selectTool.selectedObjects || [])
74
+ : [];
75
+ if (!ids.includes(this.currentId)) { this.hide(); return; }
76
+
77
+ const posData = { objectId: this.currentId, position: null };
78
+ const sizeData = { objectId: this.currentId, size: null };
79
+ this.eventBus.emit(Events.Tool.GetObjectPosition, posData);
80
+ this.eventBus.emit(Events.Tool.GetObjectSize, sizeData);
81
+ if (!posData.position || !sizeData.size) return;
82
+
83
+ const worldLayer = this.core?.pixi?.worldLayer;
84
+ const scale = worldLayer?.scale?.x || 1;
85
+ const worldX = worldLayer?.x || 0;
86
+ const worldY = worldLayer?.y || 0;
87
+
88
+ const screenX = posData.position.x * scale + worldX;
89
+ const screenY = posData.position.y * scale + worldY;
90
+ const objectWidth = sizeData.size.width * scale;
91
+ const objectHeight = sizeData.size.height * scale;
92
+
93
+ const panelW = this.panel.offsetWidth || 200;
94
+ const panelH = this.panel.offsetHeight || 120;
95
+ let panelX = screenX + (objectWidth / 2) - (panelW / 2);
96
+ let panelY = screenY - panelH - 16;
97
+
98
+ if (panelY < 0) panelY = screenY + objectHeight + 16;
99
+
100
+ const cw = this.container.offsetWidth || window.innerWidth;
101
+ panelX = Math.max(8, Math.min(panelX, cw - panelW - 8));
102
+ panelY = Math.max(8, panelY);
103
+
104
+ this.panel.style.left = `${Math.round(panelX)}px`;
105
+ this.panel.style.top = `${Math.round(panelY)}px`;
106
+ }
107
+
108
+ destroy() {
109
+ this._cancelRaf();
110
+
111
+ if (this._handlers && this.eventBus?.off) {
112
+ const H = this._handlers;
113
+ this.eventBus.off(Events.Tool.SelectionAdd, H.onSelectionAdd);
114
+ this.eventBus.off(Events.Tool.SelectionRemove, H.onSelectionRemove);
115
+ this.eventBus.off(Events.Tool.SelectionClear, H.onSelectionClear);
116
+ this.eventBus.off(Events.Object.Deleted, H.onDeleted);
117
+ this.eventBus.off(Events.Tool.DragStart, H.onDragStart);
118
+ this.eventBus.off(Events.Tool.DragUpdate, H.onDragUpdate);
119
+ this.eventBus.off(Events.Tool.DragEnd, H.onDragEnd);
120
+ this.eventBus.off(Events.Tool.GroupDragStart, H.onGroupDragStart);
121
+ this.eventBus.off(Events.Tool.GroupDragUpdate, H.onGroupDragUpdate);
122
+ this.eventBus.off(Events.Tool.GroupDragEnd, H.onGroupDragEnd);
123
+ this.eventBus.off(Events.Tool.ResizeUpdate, H.onResizeUpdate);
124
+ this.eventBus.off(Events.Tool.RotateUpdate, H.onRotateUpdate);
125
+ this.eventBus.off(Events.UI.ZoomPercent, H.onZoom);
126
+ this.eventBus.off(Events.Tool.PanUpdate, H.onPan);
127
+ this.eventBus.off(Events.Viewport.Changed, H.onViewportChanged);
128
+ this.eventBus.off(Events.Tool.Activated, H.onActivated);
129
+ this.eventBus.off(Events.Object.TransformUpdated, H.onTransformUpdated);
130
+ this._handlers = null;
131
+ }
132
+
133
+ if (this.panel?.parentNode) this.panel.parentNode.removeChild(this.panel);
134
+ this.panel = null;
135
+ this.currentId = null;
136
+ }
137
+
138
+ // ── Подписки ───────────────────────────────────────────────────────────────
139
+
140
+ _attachEvents() {
141
+ const H = this._handlers = {};
142
+
143
+ H.onSelectionAdd = () => this.updateFromSelection();
144
+ H.onSelectionRemove = () => this.updateFromSelection();
145
+ H.onSelectionClear = () => this.hide();
146
+ H.onDeleted = (objectId) => {
147
+ if (this.currentId && objectId === this.currentId) this.hide();
148
+ };
149
+ H.onDragStart = () => this.hide();
150
+ H.onDragUpdate = () => this._repositionThrottled();
151
+ H.onDragEnd = () => this.updateFromSelection();
152
+ H.onGroupDragStart = () => this.hide();
153
+ H.onGroupDragUpdate = () => this._repositionThrottled();
154
+ H.onGroupDragEnd = () => this.updateFromSelection();
155
+ H.onResizeUpdate = () => this._repositionThrottled();
156
+ H.onRotateUpdate = () => this._repositionThrottled();
157
+ H.onZoom = () => { if (this.currentId) this._repositionThrottled(); };
158
+ H.onPan = () => { if (this.currentId) this._repositionThrottled(); };
159
+ H.onViewportChanged = () => { if (this.currentId) this._repositionThrottled(); };
160
+ H.onActivated = ({ tool }) => { if (tool !== 'select') this.hide(); };
161
+ H.onTransformUpdated = ({ objectId }) => {
162
+ if (this.currentId && objectId === this.currentId &&
163
+ this.panel?.style.display !== 'none') {
164
+ this._repositionThrottled();
165
+ }
166
+ };
167
+
168
+ this.eventBus.on(Events.Tool.SelectionAdd, H.onSelectionAdd);
169
+ this.eventBus.on(Events.Tool.SelectionRemove, H.onSelectionRemove);
170
+ this.eventBus.on(Events.Tool.SelectionClear, H.onSelectionClear);
171
+ this.eventBus.on(Events.Object.Deleted, H.onDeleted);
172
+ this.eventBus.on(Events.Tool.DragStart, H.onDragStart);
173
+ this.eventBus.on(Events.Tool.DragUpdate, H.onDragUpdate);
174
+ this.eventBus.on(Events.Tool.DragEnd, H.onDragEnd);
175
+ this.eventBus.on(Events.Tool.GroupDragStart, H.onGroupDragStart);
176
+ this.eventBus.on(Events.Tool.GroupDragUpdate, H.onGroupDragUpdate);
177
+ this.eventBus.on(Events.Tool.GroupDragEnd, H.onGroupDragEnd);
178
+ this.eventBus.on(Events.Tool.ResizeUpdate, H.onResizeUpdate);
179
+ this.eventBus.on(Events.Tool.RotateUpdate, H.onRotateUpdate);
180
+ this.eventBus.on(Events.UI.ZoomPercent, H.onZoom);
181
+ this.eventBus.on(Events.Tool.PanUpdate, H.onPan);
182
+ this.eventBus.on(Events.Viewport.Changed, H.onViewportChanged);
183
+ this.eventBus.on(Events.Tool.Activated, H.onActivated);
184
+ this.eventBus.on(Events.Object.TransformUpdated, H.onTransformUpdated);
185
+ }
186
+
187
+ // ── Построение DOM ─────────────────────────────────────────────────────────
188
+
189
+ _createPanel() {
190
+ const panel = document.createElement('div');
191
+ panel.className = 'moodboard-toolbar__popup moodboard-toolbar__popup--draw drawing-properties-panel';
192
+ panel.id = 'drawing-properties-panel';
193
+ panel.style.display = 'none';
194
+ // Панель позиционируется абсолютно внутри canvasContainer
195
+ panel.style.left = '0px';
196
+ panel.style.top = '0px';
197
+ panel.addEventListener('pointerdown', (e) => e.stopPropagation());
198
+
199
+ const inner = document.createElement('div');
200
+ inner.className = 'moodboard-draw__panel';
201
+
202
+ inner.appendChild(this._buildThicknessSection());
203
+ inner.appendChild(this._buildColorSection());
204
+
205
+ panel.appendChild(inner);
206
+ this.panel = panel;
207
+ this.container.appendChild(panel);
208
+ }
209
+
210
+ _buildThicknessSection() {
211
+ const section = document.createElement('div');
212
+ section.className = 'moodboard-draw__section moodboard-draw__section--thickness';
213
+
214
+ const header = document.createElement('div');
215
+ header.className = 'moodboard-draw__section-header';
216
+ const label = document.createElement('span');
217
+ label.textContent = 'Толщина';
218
+ label.className = 'moodboard-draw__section-label';
219
+ const value = document.createElement('span');
220
+ value.textContent = '2px';
221
+ value.className = 'moodboard-draw__thickness-value';
222
+ header.appendChild(label);
223
+ header.appendChild(value);
224
+
225
+ const slider = document.createElement('input');
226
+ slider.type = 'range';
227
+ slider.min = String(MIN_WIDTH);
228
+ slider.max = String(MAX_WIDTH);
229
+ slider.value = '2';
230
+ slider.className = 'moodboard-draw__slider';
231
+ slider.addEventListener('input', () => {
232
+ const w = parseInt(slider.value, 10);
233
+ value.textContent = `${w}px`;
234
+ this._emit({ strokeWidth: w });
235
+ });
236
+
237
+ section.appendChild(header);
238
+ section.appendChild(slider);
239
+
240
+ this._slider = slider;
241
+ this._widthValue = value;
242
+ return section;
243
+ }
244
+
245
+ _buildColorSection() {
246
+ const section = document.createElement('div');
247
+ section.className = 'moodboard-draw__section';
248
+
249
+ const grid = document.createElement('div');
250
+ grid.className = 'moodboard-draw__color-grid';
251
+
252
+ PALETTE.forEach((hex) => {
253
+ const btn = document.createElement('button');
254
+ btn.className = 'moodboard-draw__color-btn';
255
+ btn.title = hex;
256
+ btn.dataset.hex = hex.toLowerCase();
257
+ btn.style.background = hex;
258
+ if (hex === '#ffffff') btn.style.border = '1.5px solid #d1d5db';
259
+ btn.addEventListener('click', () => {
260
+ this._setActiveSwatch(btn);
261
+ this._emit({ strokeColor: parseInt(hex.replace('#', ''), 16) });
262
+ });
263
+ grid.appendChild(btn);
264
+ });
265
+
266
+ // Кастомный пикер (радужный кружок)
267
+ const customBtn = document.createElement('button');
268
+ customBtn.className = 'moodboard-draw__color-btn moodboard-draw__color-btn--custom';
269
+ customBtn.title = 'Выбрать цвет';
270
+ const colorInput = document.createElement('input');
271
+ colorInput.type = 'color';
272
+ colorInput.value = '#000000';
273
+ colorInput.className = 'moodboard-draw__color-input';
274
+ colorInput.addEventListener('input', () => {
275
+ this._setActiveSwatch(customBtn);
276
+ this._emit({ strokeColor: parseInt(colorInput.value.replace('#', ''), 16) });
277
+ });
278
+ customBtn.appendChild(colorInput);
279
+ customBtn.addEventListener('click', (e) => {
280
+ e.stopPropagation();
281
+ colorInput.click();
282
+ });
283
+ grid.appendChild(customBtn);
284
+
285
+ section.appendChild(grid);
286
+
287
+ this._colorGrid = grid;
288
+ this._customBtn = customBtn;
289
+ this._colorInput = colorInput;
290
+ return section;
291
+ }
292
+
293
+ // ── Синхронизация контролов с объектом ──────────────────────────────────────
294
+
295
+ _updateControlsFromObject() {
296
+ const props = this._getDrawingProps();
297
+ if (!props) return;
298
+
299
+ const width = Math.max(MIN_WIDTH, Math.min(MAX_WIDTH, Math.round(props.strokeWidth ?? 2)));
300
+ if (this._slider) this._slider.value = String(width);
301
+ if (this._widthValue) this._widthValue.textContent = `${width}px`;
302
+
303
+ const hex = this._pixiToHex(props.strokeColor ?? 0x111827);
304
+ this._syncActiveSwatchByHex(hex);
305
+ if (this._colorInput) this._colorInput.value = hex;
306
+ }
307
+
308
+ _getDrawingProps() {
309
+ const id = this.currentId;
310
+ if (!id) return null;
311
+ const obj = (this.core?.state?.state?.objects ?? this.core?.state?.getObjects?.() ?? [])
312
+ .find(o => o.id === id);
313
+ return obj?.properties ?? null;
314
+ }
315
+
316
+ _setActiveSwatch(activeBtn) {
317
+ if (!this._colorGrid) return;
318
+ this._colorGrid.querySelectorAll('.moodboard-draw__color-btn--active')
319
+ .forEach(el => el.classList.remove('moodboard-draw__color-btn--active'));
320
+ if (activeBtn) activeBtn.classList.add('moodboard-draw__color-btn--active');
321
+ }
322
+
323
+ _syncActiveSwatchByHex(hex) {
324
+ if (!this._colorGrid) return;
325
+ const match = this._colorGrid.querySelector(`.moodboard-draw__color-btn[data-hex="${hex}"]`);
326
+ this._setActiveSwatch(match || this._customBtn);
327
+ }
328
+
329
+ _pixiToHex(pixi) {
330
+ return '#' + (pixi >>> 0).toString(16).padStart(6, '0').slice(-6).toLowerCase();
331
+ }
332
+
333
+ // ── Emit ────────────────────────────────────────────────────────────────────
334
+
335
+ _emit(propsPatch) {
336
+ if (!this.currentId) return;
337
+ this.eventBus.emit(Events.Object.StateChanged, {
338
+ objectId: this.currentId,
339
+ updates: { properties: propsPatch },
340
+ });
341
+ }
342
+
343
+ // ── RAF-позиционирование ───────────────────────────────────────────────────
344
+
345
+ _repositionThrottled() {
346
+ if (this._repositionScheduled) return;
347
+ this._repositionScheduled = true;
348
+ this._repositionRafId = requestAnimationFrame(() => {
349
+ this._repositionScheduled = false;
350
+ this._repositionRafId = null;
351
+ if (this.panel) this.reposition();
352
+ });
353
+ }
354
+
355
+ _cancelRaf() {
356
+ if (this._repositionRafId != null) {
357
+ cancelAnimationFrame(this._repositionRafId);
358
+ this._repositionRafId = null;
359
+ }
360
+ this._repositionScheduled = false;
361
+ }
362
+ }
@@ -39,6 +39,9 @@ export class FilePropertiesPanel {
39
39
  this._handlers.onPanUpdate = () => {
40
40
  if (this.currentId) this.reposition();
41
41
  };
42
+ this._handlers.onViewportChanged = () => {
43
+ if (this.currentId) this.reposition();
44
+ };
42
45
  this._handlers.onActivated = ({ tool }) => {
43
46
  if (tool !== 'select') this.hide();
44
47
  };
@@ -60,6 +63,7 @@ export class FilePropertiesPanel {
60
63
  this.eventBus.on(Events.Tool.RotateUpdate, this._handlers.onRotateUpdate);
61
64
  this.eventBus.on(Events.UI.ZoomPercent, this._handlers.onZoomPercent);
62
65
  this.eventBus.on(Events.Tool.PanUpdate, this._handlers.onPanUpdate);
66
+ this.eventBus.on(Events.Viewport.Changed, this._handlers.onViewportChanged);
63
67
  this.eventBus.on(Events.Tool.Activated, this._handlers.onActivated);
64
68
  this.eventBus.on(Events.Object.TransformUpdated, this._handlers.onTransformUpdated);
65
69
  }
@@ -348,6 +352,7 @@ export class FilePropertiesPanel {
348
352
  this.eventBus.off(Events.Tool.RotateUpdate, this._handlers.onRotateUpdate);
349
353
  this.eventBus.off(Events.UI.ZoomPercent, this._handlers.onZoomPercent);
350
354
  this.eventBus.off(Events.Tool.PanUpdate, this._handlers.onPanUpdate);
355
+ this.eventBus.off(Events.Viewport.Changed, this._handlers.onViewportChanged);
351
356
  this.eventBus.off(Events.Tool.Activated, this._handlers.onActivated);
352
357
  this.eventBus.off(Events.Object.TransformUpdated, this._handlers.onTransformUpdated);
353
358
  this._handlers = null;
@@ -39,6 +39,9 @@ export class FramePropertiesPanel {
39
39
  this._handlers.onPanUpdate = () => {
40
40
  if (this.currentId) this._repositionThrottled();
41
41
  };
42
+ this._handlers.onViewportChanged = () => {
43
+ if (this.currentId) this._repositionThrottled();
44
+ };
42
45
  this._handlers.onActivated = ({ tool }) => {
43
46
  if (tool !== 'select') this.hide();
44
47
  };
@@ -75,6 +78,7 @@ export class FramePropertiesPanel {
75
78
  this.eventBus.on(Events.Tool.RotateUpdate, this._handlers.onRotateUpdate);
76
79
  this.eventBus.on(Events.UI.ZoomPercent, this._handlers.onZoomPercent);
77
80
  this.eventBus.on(Events.Tool.PanUpdate, this._handlers.onPanUpdate);
81
+ this.eventBus.on(Events.Viewport.Changed, this._handlers.onViewportChanged);
78
82
  this.eventBus.on(Events.Tool.Activated, this._handlers.onActivated);
79
83
  this.eventBus.on(Events.Object.StateChanged, this._handlers.onStateChanged);
80
84
  this.eventBus.on(Events.History.Changed, this._handlers.onHistoryChanged);
@@ -659,6 +663,7 @@ export class FramePropertiesPanel {
659
663
  this.eventBus.off(Events.Tool.RotateUpdate, this._handlers.onRotateUpdate);
660
664
  this.eventBus.off(Events.UI.ZoomPercent, this._handlers.onZoomPercent);
661
665
  this.eventBus.off(Events.Tool.PanUpdate, this._handlers.onPanUpdate);
666
+ this.eventBus.off(Events.Viewport.Changed, this._handlers.onViewportChanged);
662
667
  this.eventBus.off(Events.Tool.Activated, this._handlers.onActivated);
663
668
  this.eventBus.off(Events.Object.StateChanged, this._handlers.onStateChanged);
664
669
  this.eventBus.off(Events.History.Changed, this._handlers.onHistoryChanged);