@sequent-org/moodboard 1.4.30 → 1.4.31
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 +3 -1
- package/src/core/PixiEngine.js +34 -5
- package/src/core/bootstrap/CoreInitializer.js +4 -0
- package/src/core/commands/CreateConnectorCommand.js +25 -0
- package/src/core/commands/GroupMoveCommand.js +2 -2
- package/src/core/commands/MoveObjectCommand.js +1 -1
- package/src/core/commands/UpdateConnectorCommand.js +38 -0
- package/src/core/events/Events.js +1 -0
- package/src/mindmap/MindmapCompoundContract.js +1 -0
- package/src/moodboard/bootstrap/MoodBoardUiFactory.js +14 -0
- package/src/moodboard/lifecycle/MoodBoardDestroyer.js +18 -0
- package/src/objects/ConnectorObject.js +85 -0
- package/src/objects/DrawingObject.js +47 -0
- package/src/objects/MindmapObject.js +21 -3
- package/src/objects/NoteObject.js +16 -8
- package/src/objects/ObjectFactory.js +3 -1
- package/src/objects/ShapeObject.js +1 -1
- package/src/services/ConnectorBindingResolver.js +204 -0
- package/src/services/ai/AiClient.js +30 -2
- package/src/services/ai/ChatSessionController.js +1 -0
- package/src/tools/ToolManager.js +3 -0
- package/src/tools/manager/PointerGestureController.js +206 -0
- package/src/tools/manager/ToolEventRouter.js +10 -0
- package/src/tools/manager/ToolManagerGuards.js +3 -1
- package/src/tools/manager/ToolManagerLifecycle.js +70 -58
- package/src/tools/object-tools/ConnectorTool.js +147 -0
- package/src/tools/object-tools/PlacementTool.js +2 -2
- package/src/tools/object-tools/connector/ConnectorDragController.js +296 -0
- package/src/tools/object-tools/connector/connectorGesture.js +108 -0
- package/src/tools/object-tools/placement/GhostController.js +4 -4
- package/src/tools/object-tools/placement/PlacementEventsBridge.js +2 -2
- package/src/tools/object-tools/placement/PlacementInputRouter.js +5 -5
- package/src/tools/object-tools/selection/MindmapInlineEditorController.js +11 -2
- package/src/tools/object-tools/selection/SelectInputRouter.js +33 -4
- package/src/tools/object-tools/selection/SelectToolLifecycleController.js +12 -0
- package/src/tools/object-tools/selection/SelectToolSetup.js +3 -0
- package/src/tools/object-tools/selection/TextEditorDomFactory.js +1 -2
- package/src/tools/object-tools/selection/TextEditorSyncService.js +4 -4
- package/src/tools/object-tools/selection/TextInlineEditorController.js +21 -3
- package/src/tools/object-tools/selection/TransformInteractionController.js +4 -6
- package/src/ui/HtmlTextLayer.js +212 -5
- package/src/ui/animation/HoverLiftController.js +395 -0
- package/src/ui/chat/ChatComposer.js +0 -5
- package/src/ui/chat/ChatWindow.js +167 -35
- package/src/ui/chat/icons.js +17 -1
- package/src/ui/connectors/ConnectionAnchorsLayer.js +231 -0
- package/src/ui/connectors/ConnectorLayer.js +251 -0
- package/src/ui/handles/HandlesDomRenderer.js +11 -7
- package/src/ui/handles/HandlesInteractionController.js +65 -34
- package/src/ui/handles/HandlesPositioningService.js +41 -6
- package/src/ui/mindmap/MindmapCollapseGraph.js +169 -0
- package/src/ui/mindmap/MindmapCollapseLayer.js +380 -0
- package/src/ui/mindmap/MindmapConnectionLayer.js +50 -25
- package/src/ui/mindmap/MindmapHtmlTextLayer.js +223 -2
- package/src/ui/mindmap/MindmapLayoutConfig.js +12 -0
- package/src/ui/styles/chat.css +1 -0
- package/src/ui/styles/toolbar.css +6 -0
- package/src/ui/styles/workspace.css +83 -21
- package/src/ui/toolbar/ToolbarPopupsController.js +1 -1
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { Events } from '../../core/events/Events.js';
|
|
2
|
+
import gsap from 'gsap';
|
|
2
3
|
import * as PIXI from 'pixi.js';
|
|
3
4
|
import { MindmapTextOverlayAdapter } from './MindmapTextOverlayAdapter.js';
|
|
4
|
-
import { MINDMAP_LAYOUT } from './MindmapLayoutConfig.js';
|
|
5
|
+
import { MINDMAP_LAYOUT, MINDMAP_AUTOFIT } from './MindmapLayoutConfig.js';
|
|
5
6
|
|
|
6
7
|
const MINDMAP_PLACEHOLDER = 'Напишите что-нибудь';
|
|
7
8
|
const MINDMAP_MAX_LINE_CHARS = MINDMAP_LAYOUT.maxLineChars;
|
|
8
9
|
|
|
10
|
+
// Hover-lift канон «маленькие» — идентичен HtmlTextLayer.js
|
|
11
|
+
const MM_HOVER_TY = -2;
|
|
12
|
+
const MM_HOVER_SC = 1.06;
|
|
13
|
+
const MM_HOVER_DUR = 0.22;
|
|
14
|
+
const MM_BACK_DUR = 0.18;
|
|
15
|
+
const mmPrefersReducedMotion =
|
|
16
|
+
typeof window !== 'undefined' &&
|
|
17
|
+
window.matchMedia?.('(prefers-reduced-motion: reduce)').matches;
|
|
18
|
+
|
|
9
19
|
function normalizeMindmapLineLength(value, maxLineChars = MINDMAP_MAX_LINE_CHARS) {
|
|
10
20
|
const text = (typeof value === 'string')
|
|
11
21
|
? value.replace(/\r/g, '').replace(/\n/g, '')
|
|
@@ -32,6 +42,12 @@ export class MindmapHtmlTextLayer {
|
|
|
32
42
|
this.idToCleanup = new Map();
|
|
33
43
|
this.idToContentEl = new Map();
|
|
34
44
|
this.overlayAdapter = new MindmapTextOverlayAdapter();
|
|
45
|
+
// hover-lift state: objectId → { ty: 0, sc: 1 }
|
|
46
|
+
this._hoverStates = new Map();
|
|
47
|
+
this._hoveredId = null;
|
|
48
|
+
this._selectedIds = new Set();
|
|
49
|
+
this._pixiHoverHandlers = new Map();
|
|
50
|
+
this._transformActive = false;
|
|
35
51
|
}
|
|
36
52
|
|
|
37
53
|
attach() {
|
|
@@ -50,6 +66,7 @@ export class MindmapHtmlTextLayer {
|
|
|
50
66
|
if (!this.overlayAdapter.supportsObject(objectData)) return;
|
|
51
67
|
this._ensureTextEl(objectId, objectData);
|
|
52
68
|
this.updateOne(objectId);
|
|
69
|
+
this._scheduleAutoFit(objectId);
|
|
53
70
|
});
|
|
54
71
|
|
|
55
72
|
this.eventBus.on(Events.Object.Deleted, ({ objectId }) => {
|
|
@@ -74,6 +91,7 @@ export class MindmapHtmlTextLayer {
|
|
|
74
91
|
const containerEl = this.idToEl.get(objectId);
|
|
75
92
|
if (contentEl && containerEl && typeof content === 'string') {
|
|
76
93
|
this._applyContentValue(containerEl, contentEl, content);
|
|
94
|
+
this._autoFitNodeWidth(objectId);
|
|
77
95
|
}
|
|
78
96
|
});
|
|
79
97
|
|
|
@@ -91,10 +109,14 @@ export class MindmapHtmlTextLayer {
|
|
|
91
109
|
el.style.color = updates.color || updates.properties?.textColor;
|
|
92
110
|
}
|
|
93
111
|
this.updateOne(objectId);
|
|
112
|
+
this._autoFitNodeWidth(objectId);
|
|
94
113
|
});
|
|
95
114
|
|
|
96
115
|
this.eventBus.on(Events.UI.ZoomPercent, () => this.updateAll());
|
|
97
116
|
this.eventBus.on(Events.Tool.PanUpdate, () => this.updateAll());
|
|
117
|
+
this.eventBus.on(Events.UI.TextEditEnd, ({ objectId }) => {
|
|
118
|
+
if (objectId && this.idToEl.has(objectId)) this._scheduleAutoFit(objectId);
|
|
119
|
+
});
|
|
98
120
|
this.eventBus.on(Events.Tool.DragUpdate, ({ object }) => this.updateOne(object));
|
|
99
121
|
this.eventBus.on(Events.Tool.ResizeUpdate, ({ object }) => this.updateOne(object));
|
|
100
122
|
this.eventBus.on(Events.Tool.RotateUpdate, ({ object }) => this.updateOne(object));
|
|
@@ -108,11 +130,61 @@ export class MindmapHtmlTextLayer {
|
|
|
108
130
|
(Array.isArray(objects) ? objects : []).forEach((id) => this.updateOne(id));
|
|
109
131
|
});
|
|
110
132
|
|
|
133
|
+
// Блокировка hover во время drag/resize/rotate (канон HtmlTextLayer)
|
|
134
|
+
this._onTransformStart = () => {
|
|
135
|
+
this._transformActive = true;
|
|
136
|
+
if (this._hoveredId) {
|
|
137
|
+
const id = this._hoveredId;
|
|
138
|
+
this._hoveredId = null;
|
|
139
|
+
this._animHoverOut(id);
|
|
140
|
+
}
|
|
141
|
+
};
|
|
142
|
+
this._onTransformEnd = () => { this._transformActive = false; };
|
|
143
|
+
this.eventBus.on(Events.Tool.DragStart, this._onTransformStart);
|
|
144
|
+
this.eventBus.on(Events.Tool.GroupDragStart, this._onTransformStart);
|
|
145
|
+
this.eventBus.on(Events.Tool.DragEnd, this._onTransformEnd);
|
|
146
|
+
this.eventBus.on(Events.Tool.GroupDragEnd, this._onTransformEnd);
|
|
147
|
+
this.eventBus.on(Events.Tool.ResizeStart, this._onTransformStart);
|
|
148
|
+
this.eventBus.on(Events.Tool.GroupResizeStart, this._onTransformStart);
|
|
149
|
+
this.eventBus.on(Events.Tool.ResizeEnd, this._onTransformEnd);
|
|
150
|
+
this.eventBus.on(Events.Tool.GroupResizeEnd, this._onTransformEnd);
|
|
151
|
+
this.eventBus.on(Events.Tool.RotateStart, this._onTransformStart);
|
|
152
|
+
this.eventBus.on(Events.Tool.GroupRotateStart, this._onTransformStart);
|
|
153
|
+
this.eventBus.on(Events.Tool.RotateEnd, this._onTransformEnd);
|
|
154
|
+
this.eventBus.on(Events.Tool.GroupRotateEnd, this._onTransformEnd);
|
|
155
|
+
|
|
156
|
+
// Выделение: не показываем hover у выделенных узлов
|
|
157
|
+
this._onSelectionAdd = (data) => {
|
|
158
|
+
const id = data?.object ?? data?.objectId ?? data?.id ?? data;
|
|
159
|
+
if (id) {
|
|
160
|
+
this._selectedIds.add(String(id));
|
|
161
|
+
if (this._hoveredId === String(id)) {
|
|
162
|
+
this._hoveredId = null;
|
|
163
|
+
this._animHoverOut(String(id));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
this._onSelectionRemove = (data) => {
|
|
168
|
+
const id = data?.object ?? data?.objectId ?? data?.id ?? data;
|
|
169
|
+
if (id) this._selectedIds.delete(String(id));
|
|
170
|
+
};
|
|
171
|
+
this._onSelectionClear = () => { this._selectedIds.clear(); };
|
|
172
|
+
this.eventBus.on(Events.Tool.SelectionAdd, this._onSelectionAdd);
|
|
173
|
+
this.eventBus.on(Events.Tool.SelectionRemove, this._onSelectionRemove);
|
|
174
|
+
this.eventBus.on(Events.Tool.SelectionClear, this._onSelectionClear);
|
|
175
|
+
|
|
111
176
|
this.rebuildFromState();
|
|
112
177
|
this.updateAll();
|
|
113
178
|
}
|
|
114
179
|
|
|
115
180
|
destroy() {
|
|
181
|
+
for (const [id, state] of this._hoverStates) {
|
|
182
|
+
gsap.killTweensOf(state);
|
|
183
|
+
this._detachPixiHover(id);
|
|
184
|
+
}
|
|
185
|
+
this._hoverStates.clear();
|
|
186
|
+
this._pixiHoverHandlers.clear();
|
|
187
|
+
this._selectedIds.clear();
|
|
116
188
|
if (this.layer) this.layer.remove();
|
|
117
189
|
this.layer = null;
|
|
118
190
|
this.idToEl.clear();
|
|
@@ -128,6 +200,7 @@ export class MindmapHtmlTextLayer {
|
|
|
128
200
|
this._ensureTextEl(objectData.id, objectData);
|
|
129
201
|
});
|
|
130
202
|
this.updateAll();
|
|
203
|
+
this._scheduleAutoFitAll();
|
|
131
204
|
}
|
|
132
205
|
|
|
133
206
|
_ensureTextEl(objectId, objectData) {
|
|
@@ -183,6 +256,7 @@ export class MindmapHtmlTextLayer {
|
|
|
183
256
|
});
|
|
184
257
|
this.idToCleanup.set(objectId, cleanup);
|
|
185
258
|
|
|
259
|
+
this._hoverStates.set(objectId, { ty: 0, sc: 1 });
|
|
186
260
|
el.appendChild(contentEl);
|
|
187
261
|
this.layer.appendChild(el);
|
|
188
262
|
this.idToEl.set(objectId, el);
|
|
@@ -194,6 +268,10 @@ export class MindmapHtmlTextLayer {
|
|
|
194
268
|
const cleanup = this.idToCleanup.get(objectId);
|
|
195
269
|
if (typeof cleanup === 'function') cleanup();
|
|
196
270
|
if (el) el.remove();
|
|
271
|
+
this._detachPixiHover(objectId);
|
|
272
|
+
const state = this._hoverStates.get(objectId);
|
|
273
|
+
if (state) gsap.killTweensOf(state);
|
|
274
|
+
this._hoverStates.delete(objectId);
|
|
197
275
|
this.idToEl.delete(objectId);
|
|
198
276
|
this.idToCleanup.delete(objectId);
|
|
199
277
|
this.idToContentEl.delete(objectId);
|
|
@@ -268,7 +346,78 @@ export class MindmapHtmlTextLayer {
|
|
|
268
346
|
}
|
|
269
347
|
|
|
270
348
|
el.style.transformOrigin = 'center center';
|
|
271
|
-
|
|
349
|
+
const hover = this._hoverStates.get(objectId);
|
|
350
|
+
const hoverTy = hover?.ty ?? 0;
|
|
351
|
+
const hoverSc = hover?.sc ?? 1;
|
|
352
|
+
const hoverPart = (Math.abs(hoverTy) > 0.001 || Math.abs(hoverSc - 1) > 0.001)
|
|
353
|
+
? `translate3d(0, ${hoverTy}px, 0) scale(${hoverSc})`
|
|
354
|
+
: '';
|
|
355
|
+
const rotatePart = angle ? `rotate(${angle}deg)` : '';
|
|
356
|
+
el.style.transform = [hoverPart, rotatePart].filter(Boolean).join(' ');
|
|
357
|
+
this._ensurePixiHover(objectId);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/** Лениво вешает pointerover/pointerout на PIXI-капсулу узла */
|
|
361
|
+
_ensurePixiHover(objectId) {
|
|
362
|
+
if (this._pixiHoverHandlers.has(objectId)) return;
|
|
363
|
+
const cap = this.core?.pixi?.objects?.get ? this.core.pixi.objects.get(objectId) : null;
|
|
364
|
+
if (!cap || typeof cap.on !== 'function') return;
|
|
365
|
+
const onOver = () => this._onPointerOver(objectId);
|
|
366
|
+
const onOut = () => this._onPointerOut(objectId);
|
|
367
|
+
cap.on('pointerover', onOver);
|
|
368
|
+
cap.on('pointerout', onOut);
|
|
369
|
+
this._pixiHoverHandlers.set(objectId, { cap, onOver, onOut });
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
_detachPixiHover(objectId) {
|
|
373
|
+
const h = this._pixiHoverHandlers.get(objectId);
|
|
374
|
+
if (!h) return;
|
|
375
|
+
try { h.cap.off('pointerover', h.onOver); } catch (_) {}
|
|
376
|
+
try { h.cap.off('pointerout', h.onOut); } catch (_) {}
|
|
377
|
+
this._pixiHoverHandlers.delete(objectId);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
_onPointerOver(objectId) {
|
|
381
|
+
if (mmPrefersReducedMotion) return;
|
|
382
|
+
if (this._transformActive) return;
|
|
383
|
+
if (this._selectedIds.has(objectId) || this._selectedIds.has(String(objectId))) return;
|
|
384
|
+
if (this._hoveredId === objectId) return;
|
|
385
|
+
this._hoveredId = objectId;
|
|
386
|
+
this._animHoverIn(objectId);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
_onPointerOut(objectId) {
|
|
390
|
+
if (this._hoveredId === objectId) this._hoveredId = null;
|
|
391
|
+
this._animHoverOut(objectId);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
_animHoverIn(objectId) {
|
|
395
|
+
if (mmPrefersReducedMotion) return;
|
|
396
|
+
const state = this._hoverStates.get(objectId);
|
|
397
|
+
if (!state) return;
|
|
398
|
+
gsap.killTweensOf(state);
|
|
399
|
+
gsap.to(state, {
|
|
400
|
+
ty: MM_HOVER_TY,
|
|
401
|
+
sc: MM_HOVER_SC,
|
|
402
|
+
duration: MM_HOVER_DUR,
|
|
403
|
+
ease: 'hoverLiftSpring',
|
|
404
|
+
onUpdate: () => this.updateOne(objectId),
|
|
405
|
+
onComplete: () => this.updateOne(objectId),
|
|
406
|
+
});
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
_animHoverOut(objectId) {
|
|
410
|
+
const state = this._hoverStates.get(objectId);
|
|
411
|
+
if (!state) return;
|
|
412
|
+
gsap.killTweensOf(state);
|
|
413
|
+
gsap.to(state, {
|
|
414
|
+
ty: 0,
|
|
415
|
+
sc: 1,
|
|
416
|
+
duration: MM_BACK_DUR,
|
|
417
|
+
ease: 'power2.out',
|
|
418
|
+
onUpdate: () => this.updateOne(objectId),
|
|
419
|
+
onComplete: () => this.updateOne(objectId),
|
|
420
|
+
});
|
|
272
421
|
}
|
|
273
422
|
|
|
274
423
|
_applyContentValue(containerEl, contentEl, rawContent) {
|
|
@@ -282,4 +431,76 @@ export class MindmapHtmlTextLayer {
|
|
|
282
431
|
contentEl.textContent = isPlaceholder ? MINDMAP_PLACEHOLDER : actual;
|
|
283
432
|
contentEl.classList.toggle('is-placeholder', isPlaceholder);
|
|
284
433
|
}
|
|
434
|
+
|
|
435
|
+
_scheduleAutoFit(objectId) {
|
|
436
|
+
const doFit = () => this._autoFitNodeWidth(objectId);
|
|
437
|
+
if (typeof document !== 'undefined' && document.fonts?.ready) {
|
|
438
|
+
document.fonts.ready.then(doFit);
|
|
439
|
+
} else {
|
|
440
|
+
doFit();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
_scheduleAutoFitAll() {
|
|
445
|
+
const doFit = () => { for (const id of this.idToEl.keys()) this._autoFitNodeWidth(id); };
|
|
446
|
+
if (typeof document !== 'undefined' && document.fonts?.ready) {
|
|
447
|
+
document.fonts.ready.then(doFit);
|
|
448
|
+
} else {
|
|
449
|
+
doFit();
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
_autoFitNodeWidth(objectId) {
|
|
454
|
+
const el = this.idToEl.get(objectId);
|
|
455
|
+
const contentEl = this.idToContentEl.get(objectId);
|
|
456
|
+
if (!el || !contentEl || !this.core) return;
|
|
457
|
+
// Skip while the static element is hidden during inline editing.
|
|
458
|
+
if (el.style.visibility === 'hidden') return;
|
|
459
|
+
try {
|
|
460
|
+
const objectData = (this.core.state.state.objects || []).find(o => o.id === objectId);
|
|
461
|
+
if (!objectData || !objectData.position) return;
|
|
462
|
+
|
|
463
|
+
const world = this.core.pixi.worldLayer || this.core.pixi.app.stage;
|
|
464
|
+
const res = (this.core?.pixi?.app?.renderer?.resolution) || 1;
|
|
465
|
+
const worldScale = world?.scale?.x || 1;
|
|
466
|
+
|
|
467
|
+
// Measure rendered text width via scrollWidth of the <span> (forces layout reflow).
|
|
468
|
+
const scrollWidthCss = contentEl.scrollWidth;
|
|
469
|
+
if (scrollWidthCss <= 0) return;
|
|
470
|
+
|
|
471
|
+
const paddingX = Math.max(0, Math.round(
|
|
472
|
+
objectData.properties?.paddingX ?? MINDMAP_LAYOUT.paddingX
|
|
473
|
+
));
|
|
474
|
+
// css → world: same formula as _autoFitTextHeight in HtmlTextLayer.js
|
|
475
|
+
const contentWorldW = (scrollWidthCss * res) / worldScale;
|
|
476
|
+
const rawWorldW = contentWorldW + 2 * paddingX;
|
|
477
|
+
|
|
478
|
+
const level = objectData.properties?.mindmap?.level ?? 0;
|
|
479
|
+
const isRoot = level === 0;
|
|
480
|
+
const minW = isRoot ? MINDMAP_AUTOFIT.ROOT_MIN_WIDTH : MINDMAP_AUTOFIT.CHILD_MIN_WIDTH;
|
|
481
|
+
const maxW = isRoot ? MINDMAP_AUTOFIT.ROOT_MAX_WIDTH : MINDMAP_AUTOFIT.CHILD_MAX_WIDTH;
|
|
482
|
+
const newWorldW = Math.max(minW, Math.min(maxW, Math.round(rawWorldW)));
|
|
483
|
+
|
|
484
|
+
// Measure natural height at the fitted width to handle multi-line wrapping.
|
|
485
|
+
const cssFitW = Math.max(1, Math.round(newWorldW * worldScale / res));
|
|
486
|
+
const prevW = el.style.width;
|
|
487
|
+
const prevH = el.style.height;
|
|
488
|
+
el.style.width = `${cssFitW}px`;
|
|
489
|
+
el.style.height = 'auto';
|
|
490
|
+
const scrollHeightCss = Math.max(1, Math.round(el.scrollHeight));
|
|
491
|
+
el.style.width = prevW;
|
|
492
|
+
el.style.height = prevH;
|
|
493
|
+
const newWorldH = Math.max(1, Math.round((scrollHeightCss * res) / worldScale));
|
|
494
|
+
|
|
495
|
+
const currentW = Math.round(objectData.width || objectData.properties?.width || MINDMAP_LAYOUT.width);
|
|
496
|
+
const currentH = Math.round(objectData.height || objectData.properties?.height || MINDMAP_LAYOUT.height);
|
|
497
|
+
if (newWorldW === currentW && newWorldH === currentH) return;
|
|
498
|
+
|
|
499
|
+
this.core.eventBus.emit(Events.Tool.ResizeUpdate, {
|
|
500
|
+
object: objectId,
|
|
501
|
+
size: { width: newWorldW, height: newWorldH },
|
|
502
|
+
position: objectData.position,
|
|
503
|
+
});
|
|
504
|
+
} catch (_) {}
|
|
505
|
+
}
|
|
285
506
|
}
|
|
@@ -27,3 +27,15 @@ export const MINDMAP_LAYOUT = Object.freeze({
|
|
|
27
27
|
maxLineChars: MINDMAP_BASE_LAYOUT.maxLineChars,
|
|
28
28
|
});
|
|
29
29
|
|
|
30
|
+
// Auto-fit width bounds (world units).
|
|
31
|
+
// ROOT: current fixed width ~179 → min 100 keeps pill shape for short words;
|
|
32
|
+
// max 440 ≈ 50-char line in Roboto 14px (~375px) + 40px padding.
|
|
33
|
+
// CHILD: slightly narrower pill baseline; same maxLineChars as root but
|
|
34
|
+
// typically smaller padding, so max is a bit tighter.
|
|
35
|
+
export const MINDMAP_AUTOFIT = Object.freeze({
|
|
36
|
+
ROOT_MIN_WIDTH: 100,
|
|
37
|
+
ROOT_MAX_WIDTH: 440,
|
|
38
|
+
CHILD_MIN_WIDTH: 80,
|
|
39
|
+
CHILD_MAX_WIDTH: 360,
|
|
40
|
+
});
|
|
41
|
+
|
package/src/ui/styles/chat.css
CHANGED
|
@@ -307,8 +307,8 @@
|
|
|
307
307
|
.mb-text--mindmap {
|
|
308
308
|
display: flex;
|
|
309
309
|
align-items: center;
|
|
310
|
-
justify-content:
|
|
311
|
-
text-align:
|
|
310
|
+
justify-content: center;
|
|
311
|
+
text-align: center;
|
|
312
312
|
pointer-events: none;
|
|
313
313
|
cursor: default;
|
|
314
314
|
color: #212121;
|
|
@@ -340,6 +340,7 @@
|
|
|
340
340
|
position: absolute;
|
|
341
341
|
inset: 0;
|
|
342
342
|
pointer-events: none;
|
|
343
|
+
touch-action: none;
|
|
343
344
|
z-index: 12;
|
|
344
345
|
}
|
|
345
346
|
|
|
@@ -357,23 +358,18 @@
|
|
|
357
358
|
position: absolute;
|
|
358
359
|
width: 12px;
|
|
359
360
|
height: 12px;
|
|
360
|
-
background: #
|
|
361
|
+
background: #ffffff;
|
|
361
362
|
border: 2px solid #80D8FF;
|
|
362
363
|
border-radius: 50%;
|
|
363
364
|
box-sizing: border-box;
|
|
364
365
|
pointer-events: auto;
|
|
365
366
|
z-index: 10;
|
|
367
|
+
box-shadow: 0 1px 2px rgba(15, 23, 42, 0.18);
|
|
368
|
+
transition: background-color 120ms ease, border-color 120ms ease;
|
|
366
369
|
}
|
|
367
370
|
|
|
368
371
|
.mb-handle-inner {
|
|
369
|
-
|
|
370
|
-
top: 1px;
|
|
371
|
-
left: 1px;
|
|
372
|
-
width: 6px;
|
|
373
|
-
height: 6px;
|
|
374
|
-
background: #fff;
|
|
375
|
-
border-radius: 50%;
|
|
376
|
-
pointer-events: none;
|
|
372
|
+
display: none;
|
|
377
373
|
}
|
|
378
374
|
|
|
379
375
|
.mb-edge {
|
|
@@ -576,6 +572,10 @@
|
|
|
576
572
|
display: block;
|
|
577
573
|
width: 100%;
|
|
578
574
|
height: 100%;
|
|
575
|
+
touch-action: none;
|
|
576
|
+
user-select: none;
|
|
577
|
+
-webkit-user-select: none;
|
|
578
|
+
-webkit-tap-highlight-color: transparent;
|
|
579
579
|
}
|
|
580
580
|
|
|
581
581
|
.moodboard-workspace {
|
|
@@ -741,6 +741,7 @@
|
|
|
741
741
|
position: absolute;
|
|
742
742
|
inset: 0;
|
|
743
743
|
overflow: hidden;
|
|
744
|
+
touch-action: none;
|
|
744
745
|
}
|
|
745
746
|
|
|
746
747
|
/* Toolbar Styles */
|
|
@@ -777,9 +778,9 @@
|
|
|
777
778
|
/* Frame popup (grid 2x2 + header row) */
|
|
778
779
|
.moodboard-root, :root {
|
|
779
780
|
/* CSS variables for frame style */
|
|
780
|
-
--frame-border-color: #
|
|
781
|
-
--frame-border-width:
|
|
782
|
-
--frame-corner-radius:
|
|
781
|
+
--frame-border-color: #C7CDD6;
|
|
782
|
+
--frame-border-width: 1; /* px */
|
|
783
|
+
--frame-corner-radius: 6; /* px */
|
|
783
784
|
}
|
|
784
785
|
.frame-popup {
|
|
785
786
|
display: grid;
|
|
@@ -1002,27 +1003,27 @@
|
|
|
1002
1003
|
/* Иконки фигур — рисуем CSSом */
|
|
1003
1004
|
.moodboard-shapes__icon { display: inline-block; }
|
|
1004
1005
|
.shape-square {
|
|
1005
|
-
width: 15px; height: 15px; background: #
|
|
1006
|
+
width: 15px; height: 15px; background: #ffffff; border-radius: 2px; border: 1.5px solid #94a3b8;
|
|
1006
1007
|
}
|
|
1007
1008
|
.shape-rounded-square {
|
|
1008
|
-
width: 15px; height: 15px; background: #
|
|
1009
|
+
width: 15px; height: 15px; background: #ffffff; border-radius: 4px; border: 1.5px solid #94a3b8;
|
|
1009
1010
|
}
|
|
1010
1011
|
.shape-circle {
|
|
1011
|
-
width: 15px; height: 15px; background: #
|
|
1012
|
+
width: 15px; height: 15px; background: #ffffff; border-radius: 50%; border: 1.5px solid #94a3b8;
|
|
1012
1013
|
}
|
|
1013
1014
|
.shape-triangle {
|
|
1014
1015
|
width: 0; height: 0;
|
|
1015
1016
|
border-left: 8px solid transparent;
|
|
1016
1017
|
border-right: 8px solid transparent;
|
|
1017
|
-
border-bottom: 14px solid #
|
|
1018
|
+
border-bottom: 14px solid #94a3b8;
|
|
1018
1019
|
}
|
|
1019
1020
|
.shape-diamond {
|
|
1020
|
-
width: 11px; height: 11px; background: #
|
|
1021
|
+
width: 11px; height: 11px; background: #ffffff; border: 1.5px solid #94a3b8; transform: rotate(45deg);
|
|
1021
1022
|
}
|
|
1022
1023
|
.shape-parallelogram {
|
|
1023
|
-
width: 17px; height: 11px; background: #
|
|
1024
|
+
width: 17px; height: 11px; background: #ffffff; border: 1.5px solid #94a3b8; transform: skewX(-20deg);
|
|
1024
1025
|
}
|
|
1025
|
-
.shape-arrow { font-size: 16px; color: #
|
|
1026
|
+
.shape-arrow { font-size: 16px; color: #94a3b8; line-height: 1; }
|
|
1026
1027
|
|
|
1027
1028
|
|
|
1028
1029
|
|
|
@@ -1300,4 +1301,65 @@
|
|
|
1300
1301
|
line-height: inherit;
|
|
1301
1302
|
}
|
|
1302
1303
|
|
|
1304
|
+
@media (pointer: coarse) {
|
|
1305
|
+
.mb-handle::before,
|
|
1306
|
+
.mb-edge::before {
|
|
1307
|
+
content: '';
|
|
1308
|
+
position: absolute;
|
|
1309
|
+
inset: -16px;
|
|
1310
|
+
pointer-events: auto;
|
|
1311
|
+
}
|
|
1312
|
+
|
|
1313
|
+
.mb-rotate-handle::before {
|
|
1314
|
+
content: '';
|
|
1315
|
+
position: absolute;
|
|
1316
|
+
inset: -12px;
|
|
1317
|
+
pointer-events: auto;
|
|
1318
|
+
}
|
|
1319
|
+
|
|
1320
|
+
.mb-rotate-handle {
|
|
1321
|
+
transform: translate(-16px, 16px) !important;
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
.mb-edge[data-edge="top"], .mb-edge[data-edge="bottom"] {
|
|
1325
|
+
left: 50% !important;
|
|
1326
|
+
transform: translateX(-50%) !important;
|
|
1327
|
+
width: max(32px, calc(var(--box-w) - 44px)) !important;
|
|
1328
|
+
z-index: 11 !important;
|
|
1329
|
+
}
|
|
1330
|
+
.mb-edge[data-edge="left"], .mb-edge[data-edge="right"] {
|
|
1331
|
+
top: 50% !important;
|
|
1332
|
+
transform: translateY(-50%) !important;
|
|
1333
|
+
height: max(32px, calc(var(--box-h) - 44px)) !important;
|
|
1334
|
+
z-index: 11 !important;
|
|
1335
|
+
}
|
|
1336
|
+
|
|
1337
|
+
.mb-handle[data-dir="nw"]::before, .mb-handle[data-dir="sw"]::before {
|
|
1338
|
+
right: max(-16px, calc(22px - var(--box-w) / 2));
|
|
1339
|
+
}
|
|
1340
|
+
.mb-handle[data-dir="ne"]::before, .mb-handle[data-dir="se"]::before {
|
|
1341
|
+
left: max(-16px, calc(22px - var(--box-w) / 2));
|
|
1342
|
+
}
|
|
1343
|
+
.mb-handle[data-dir="nw"]::before, .mb-handle[data-dir="ne"]::before {
|
|
1344
|
+
bottom: max(-16px, calc(22px - var(--box-h) / 2));
|
|
1345
|
+
}
|
|
1346
|
+
.mb-handle[data-dir="sw"]::before, .mb-handle[data-dir="se"]::before {
|
|
1347
|
+
top: max(-16px, calc(22px - var(--box-h) / 2));
|
|
1348
|
+
}
|
|
1349
|
+
|
|
1350
|
+
.moodboard-zoombar__button {
|
|
1351
|
+
width: 44px;
|
|
1352
|
+
height: 44px;
|
|
1353
|
+
}
|
|
1354
|
+
|
|
1355
|
+
.moodboard-mapbar__button {
|
|
1356
|
+
width: 44px;
|
|
1357
|
+
height: 44px;
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
.mb-mindmap-side-btn {
|
|
1361
|
+
width: 44px;
|
|
1362
|
+
height: 44px;
|
|
1363
|
+
}
|
|
1364
|
+
}
|
|
1303
1365
|
|
|
@@ -138,7 +138,7 @@ export class ToolbarPopupsController {
|
|
|
138
138
|
} else {
|
|
139
139
|
icon.className = `moodboard-shapes__icon shape-${s.id}`;
|
|
140
140
|
if (s.id === 'arrow') {
|
|
141
|
-
icon.innerHTML = '<svg width="18" height="12" viewBox="0 0 18 12" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><rect x="0" y="5" width="12" height="2" rx="1" fill="#
|
|
141
|
+
icon.innerHTML = '<svg width="18" height="12" viewBox="0 0 18 12" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"><rect x="0" y="5" width="12" height="2" rx="1" fill="#94a3b8"/><path d="M12 0 L18 6 L12 12 Z" fill="#94a3b8"/></svg>';
|
|
142
142
|
}
|
|
143
143
|
}
|
|
144
144
|
btn.appendChild(icon);
|