vg-coder-cli 2.0.45 → 2.0.47
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/INTEGRATION.md +418 -0
- package/debug.log +42 -0
- package/dist/vg-coder-bundle.js +50 -44
- package/package.json +2 -1
- package/src/server/api-server.js +355 -2
- package/src/server/task-queue.js +705 -0
- package/src/server/task-store.js +112 -0
- package/src/server/task-webhook.js +48 -0
- package/src/server/views/css/agent-panel.css +259 -3
- package/src/server/views/css/code-viewer.css +158 -3
- package/src/server/views/js/features/agent-panel.js +248 -12
- package/src/server/views/js/features/code-viewer.js +18 -3
- package/src/server/views/js/features/git-view.js +1 -1
- package/src/server/views/js/features/mermaid-viewer.js +494 -0
- package/src/server/views/js/features/resize.js +1 -1
- package/src/server/views/js/features/task-worker.js +448 -0
- package/src/server/views/js/main.js +4 -0
- package/src/server/views/vg-coder/background.js +17860 -11946
- package/src/server/views/vg-coder/controller.js +42 -10
- package/src/server/views/vg-coder/manifest.json +2 -1
- package/src/server/views/vg-coder/sidepanel.js +13 -7
- package/test-large/package.json +1 -0
- package/test-large/src/components/Button.tsx +1 -0
- package/test-large/src/index.ts +1 -0
- package/test-small/package.json +1 -0
- package/test-small/src/index.js +1 -0
|
@@ -0,0 +1,494 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mermaid Viewer - Fullscreen modal with zoom, pan, and scale controls
|
|
3
|
+
* Inspired by mermaid.live interface
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
// State for pan and zoom
|
|
7
|
+
let viewerState = {
|
|
8
|
+
scale: 1,
|
|
9
|
+
translateX: 0,
|
|
10
|
+
translateY: 0,
|
|
11
|
+
isDragging: false,
|
|
12
|
+
startX: 0,
|
|
13
|
+
startY: 0
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
// Constants
|
|
17
|
+
const MIN_SCALE = 0.1;
|
|
18
|
+
const MAX_SCALE = 15;
|
|
19
|
+
const SCALE_STEP = 0.25;
|
|
20
|
+
const Z_INDEX = '9999999999';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Create mermaid toolbar buttons for diagram wrapper
|
|
24
|
+
*/
|
|
25
|
+
export function createMermaidToolbar(code, svg) {
|
|
26
|
+
const toolbar = document.createElement('div');
|
|
27
|
+
toolbar.className = 'mermaid-toolbar';
|
|
28
|
+
Object.assign(toolbar.style, {
|
|
29
|
+
display: 'flex',
|
|
30
|
+
justifyContent: 'flex-end',
|
|
31
|
+
gap: '4px',
|
|
32
|
+
padding: '8px 12px',
|
|
33
|
+
background: 'rgba(0, 0, 0, 0.3)',
|
|
34
|
+
borderBottom: '1px solid rgba(255, 255, 255, 0.1)'
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
toolbar.innerHTML = `
|
|
38
|
+
<button class="mermaid-toolbar-btn" data-action="copy" title="Copy Mermaid Code" style="${getButtonStyle()}">
|
|
39
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
40
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
41
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
42
|
+
</svg>
|
|
43
|
+
</button>
|
|
44
|
+
<button class="mermaid-toolbar-btn" data-action="fullscreen" title="Open Fullscreen Viewer" style="${getButtonStyle()}">
|
|
45
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
46
|
+
<polyline points="15 3 21 3 21 9"></polyline>
|
|
47
|
+
<polyline points="9 21 3 21 3 15"></polyline>
|
|
48
|
+
<line x1="21" y1="3" x2="14" y2="10"></line>
|
|
49
|
+
<line x1="3" y1="21" x2="10" y2="14"></line>
|
|
50
|
+
</svg>
|
|
51
|
+
</button>
|
|
52
|
+
`;
|
|
53
|
+
|
|
54
|
+
// Copy button handler
|
|
55
|
+
toolbar.querySelector('[data-action="copy"]').addEventListener('click', async (e) => {
|
|
56
|
+
e.stopPropagation();
|
|
57
|
+
try {
|
|
58
|
+
await navigator.clipboard.writeText(code);
|
|
59
|
+
showToast(toolbar.parentElement, 'Copied!');
|
|
60
|
+
} catch (err) {
|
|
61
|
+
console.error('[MermaidViewer] Copy failed:', err);
|
|
62
|
+
showToast(toolbar.parentElement, 'Copy failed', true);
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Fullscreen button handler
|
|
67
|
+
toolbar.querySelector('[data-action="fullscreen"]').addEventListener('click', (e) => {
|
|
68
|
+
e.stopPropagation();
|
|
69
|
+
openMermaidViewer(svg, code);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
return toolbar;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Open fullscreen mermaid viewer with zoom/pan controls
|
|
77
|
+
*/
|
|
78
|
+
export function openMermaidViewer(svg, code) {
|
|
79
|
+
// Remove any existing modal
|
|
80
|
+
const existingModal = document.querySelector('.mermaid-viewer-modal');
|
|
81
|
+
if (existingModal) {
|
|
82
|
+
existingModal.remove();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
// Reset state
|
|
86
|
+
viewerState = {
|
|
87
|
+
scale: 1,
|
|
88
|
+
translateX: 0,
|
|
89
|
+
translateY: 0,
|
|
90
|
+
isDragging: false,
|
|
91
|
+
startX: 0,
|
|
92
|
+
startY: 0
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
// Create modal
|
|
96
|
+
const modal = document.createElement('div');
|
|
97
|
+
modal.className = 'mermaid-viewer-modal';
|
|
98
|
+
Object.assign(modal.style, {
|
|
99
|
+
position: 'fixed',
|
|
100
|
+
top: '0',
|
|
101
|
+
left: '0',
|
|
102
|
+
right: '0',
|
|
103
|
+
bottom: '0',
|
|
104
|
+
width: '100vw',
|
|
105
|
+
height: '100vh',
|
|
106
|
+
background: 'rgba(10, 10, 15, 0.95)',
|
|
107
|
+
zIndex: Z_INDEX,
|
|
108
|
+
display: 'flex',
|
|
109
|
+
flexDirection: 'column',
|
|
110
|
+
opacity: '0',
|
|
111
|
+
transition: 'opacity 0.2s ease'
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
modal.innerHTML = `
|
|
115
|
+
<div class="mermaid-viewer-header" style="${getHeaderStyle()}">
|
|
116
|
+
<div class="mermaid-viewer-title" style="display: flex; align-items: center; gap: 12px;">
|
|
117
|
+
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="#a855f7" stroke-width="2">
|
|
118
|
+
<polygon points="12 2 2 7 12 12 22 7 12 2"></polygon>
|
|
119
|
+
<polyline points="2 17 12 22 22 17"></polyline>
|
|
120
|
+
<polyline points="2 12 12 17 22 12"></polyline>
|
|
121
|
+
</svg>
|
|
122
|
+
<span style="font-size: 14px; font-weight: 600; color: #ededed;">Mermaid Diagram Viewer</span>
|
|
123
|
+
</div>
|
|
124
|
+
<div class="mermaid-viewer-controls" style="display: flex; align-items: center; gap: 8px;">
|
|
125
|
+
<div class="mermaid-viewer-zoom-controls" style="display: flex; align-items: center; gap: 4px; background: rgba(255,255,255,0.05); border-radius: 6px; padding: 4px;">
|
|
126
|
+
<button data-action="zoom-out" title="Zoom Out" style="${getControlButtonStyle()}">
|
|
127
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
128
|
+
<circle cx="11" cy="11" r="8"></circle>
|
|
129
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
130
|
+
<line x1="8" y1="11" x2="14" y2="11"></line>
|
|
131
|
+
</svg>
|
|
132
|
+
</button>
|
|
133
|
+
<span class="zoom-level" style="min-width: 50px; text-align: center; font-size: 12px; color: #a1a1aa;">100%</span>
|
|
134
|
+
<button data-action="zoom-in" title="Zoom In" style="${getControlButtonStyle()}">
|
|
135
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
136
|
+
<circle cx="11" cy="11" r="8"></circle>
|
|
137
|
+
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
|
|
138
|
+
<line x1="11" y1="8" x2="11" y2="14"></line>
|
|
139
|
+
<line x1="8" y1="11" x2="14" y2="11"></line>
|
|
140
|
+
</svg>
|
|
141
|
+
</button>
|
|
142
|
+
</div>
|
|
143
|
+
<button data-action="fit" title="Fit to Screen" style="${getControlButtonStyle()}">
|
|
144
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
145
|
+
<path d="M8 3H5a2 2 0 0 0-2 2v3m18 0V5a2 2 0 0 0-2-2h-3m0 18h3a2 2 0 0 0 2-2v-3M3 16v3a2 2 0 0 0 2 2h3"></path>
|
|
146
|
+
</svg>
|
|
147
|
+
</button>
|
|
148
|
+
<button data-action="reset" title="Reset View" style="${getControlButtonStyle()}">
|
|
149
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
150
|
+
<polyline points="1 4 1 10 7 10"></polyline>
|
|
151
|
+
<path d="M3.51 15a9 9 0 1 0 2.13-9.36L1 10"></path>
|
|
152
|
+
</svg>
|
|
153
|
+
</button>
|
|
154
|
+
<div style="width: 1px; height: 24px; background: rgba(255,255,255,0.1); margin: 0 8px;"></div>
|
|
155
|
+
<button data-action="copy" title="Copy Code" style="${getControlButtonStyle()}">
|
|
156
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
157
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
158
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
159
|
+
</svg>
|
|
160
|
+
</button>
|
|
161
|
+
<button data-action="close" title="Close (Esc)" style="${getCloseButtonStyle()}">
|
|
162
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
163
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
164
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
165
|
+
</svg>
|
|
166
|
+
</button>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
<div class="mermaid-viewer-canvas" style="${getCanvasStyle()}">
|
|
170
|
+
<div class="mermaid-viewer-content" style="${getContentStyle()}">
|
|
171
|
+
${svg}
|
|
172
|
+
</div>
|
|
173
|
+
</div>
|
|
174
|
+
<div class="mermaid-viewer-footer" style="${getFooterStyle()}">
|
|
175
|
+
<span style="color: #71717a; font-size: 11px;">
|
|
176
|
+
🖱️ Scroll to zoom • Drag to pan • Double-click to reset
|
|
177
|
+
</span>
|
|
178
|
+
</div>
|
|
179
|
+
`;
|
|
180
|
+
|
|
181
|
+
document.body.appendChild(modal);
|
|
182
|
+
|
|
183
|
+
// Get elements
|
|
184
|
+
const canvas = modal.querySelector('.mermaid-viewer-canvas');
|
|
185
|
+
const content = modal.querySelector('.mermaid-viewer-content');
|
|
186
|
+
const zoomLevel = modal.querySelector('.zoom-level');
|
|
187
|
+
|
|
188
|
+
// Update transform
|
|
189
|
+
const updateTransform = () => {
|
|
190
|
+
content.style.transform = `translate(${viewerState.translateX}px, ${viewerState.translateY}px) scale(${viewerState.scale})`;
|
|
191
|
+
zoomLevel.textContent = `${Math.round(viewerState.scale * 100)}%`;
|
|
192
|
+
};
|
|
193
|
+
|
|
194
|
+
// Zoom function
|
|
195
|
+
const zoom = (delta, centerX = null, centerY = null) => {
|
|
196
|
+
const oldScale = viewerState.scale;
|
|
197
|
+
const newScale = Math.max(MIN_SCALE, Math.min(MAX_SCALE, viewerState.scale + delta));
|
|
198
|
+
|
|
199
|
+
if (centerX !== null && centerY !== null) {
|
|
200
|
+
// Zoom towards cursor position
|
|
201
|
+
const rect = canvas.getBoundingClientRect();
|
|
202
|
+
const x = centerX - rect.left - rect.width / 2;
|
|
203
|
+
const y = centerY - rect.top - rect.height / 2;
|
|
204
|
+
|
|
205
|
+
const scaleRatio = newScale / oldScale;
|
|
206
|
+
viewerState.translateX = x - (x - viewerState.translateX) * scaleRatio;
|
|
207
|
+
viewerState.translateY = y - (y - viewerState.translateY) * scaleRatio;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
viewerState.scale = newScale;
|
|
211
|
+
updateTransform();
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
// Fit to screen
|
|
215
|
+
const fitToScreen = () => {
|
|
216
|
+
const svgEl = content.querySelector('svg');
|
|
217
|
+
if (!svgEl) return;
|
|
218
|
+
|
|
219
|
+
const canvasRect = canvas.getBoundingClientRect();
|
|
220
|
+
const svgRect = svgEl.getBoundingClientRect();
|
|
221
|
+
|
|
222
|
+
// Calculate scale to fit with padding
|
|
223
|
+
const padding = 80;
|
|
224
|
+
const scaleX = (canvasRect.width - padding) / (svgRect.width / viewerState.scale);
|
|
225
|
+
const scaleY = (canvasRect.height - padding) / (svgRect.height / viewerState.scale);
|
|
226
|
+
|
|
227
|
+
viewerState.scale = Math.min(scaleX, scaleY, 2); // Max 200% on fit
|
|
228
|
+
viewerState.translateX = 0;
|
|
229
|
+
viewerState.translateY = 0;
|
|
230
|
+
updateTransform();
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Reset view
|
|
234
|
+
const resetView = () => {
|
|
235
|
+
viewerState.scale = 1;
|
|
236
|
+
viewerState.translateX = 0;
|
|
237
|
+
viewerState.translateY = 0;
|
|
238
|
+
updateTransform();
|
|
239
|
+
};
|
|
240
|
+
|
|
241
|
+
// Close modal
|
|
242
|
+
const closeModal = () => {
|
|
243
|
+
modal.style.opacity = '0';
|
|
244
|
+
setTimeout(() => modal.remove(), 200);
|
|
245
|
+
document.removeEventListener('keydown', keyHandler);
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
// Key handler
|
|
249
|
+
const keyHandler = (e) => {
|
|
250
|
+
if (e.key === 'Escape') closeModal();
|
|
251
|
+
if (e.key === '+' || e.key === '=') zoom(SCALE_STEP);
|
|
252
|
+
if (e.key === '-') zoom(-SCALE_STEP);
|
|
253
|
+
if (e.key === '0') resetView();
|
|
254
|
+
if (e.key === 'f') fitToScreen();
|
|
255
|
+
};
|
|
256
|
+
document.addEventListener('keydown', keyHandler);
|
|
257
|
+
|
|
258
|
+
// Wheel zoom
|
|
259
|
+
canvas.addEventListener('wheel', (e) => {
|
|
260
|
+
e.preventDefault();
|
|
261
|
+
const delta = e.deltaY > 0 ? -SCALE_STEP : SCALE_STEP;
|
|
262
|
+
zoom(delta, e.clientX, e.clientY);
|
|
263
|
+
}, { passive: false });
|
|
264
|
+
|
|
265
|
+
// Pan with mouse drag
|
|
266
|
+
canvas.addEventListener('mousedown', (e) => {
|
|
267
|
+
if (e.target.closest('button')) return;
|
|
268
|
+
viewerState.isDragging = true;
|
|
269
|
+
viewerState.startX = e.clientX - viewerState.translateX;
|
|
270
|
+
viewerState.startY = e.clientY - viewerState.translateY;
|
|
271
|
+
canvas.style.cursor = 'grabbing';
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
canvas.addEventListener('mousemove', (e) => {
|
|
275
|
+
if (!viewerState.isDragging) return;
|
|
276
|
+
viewerState.translateX = e.clientX - viewerState.startX;
|
|
277
|
+
viewerState.translateY = e.clientY - viewerState.startY;
|
|
278
|
+
updateTransform();
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
canvas.addEventListener('mouseup', () => {
|
|
282
|
+
viewerState.isDragging = false;
|
|
283
|
+
canvas.style.cursor = 'grab';
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
canvas.addEventListener('mouseleave', () => {
|
|
287
|
+
viewerState.isDragging = false;
|
|
288
|
+
canvas.style.cursor = 'grab';
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
// Double click to reset
|
|
292
|
+
canvas.addEventListener('dblclick', resetView);
|
|
293
|
+
|
|
294
|
+
// Touch support for mobile
|
|
295
|
+
let lastTouchDistance = 0;
|
|
296
|
+
canvas.addEventListener('touchstart', (e) => {
|
|
297
|
+
if (e.touches.length === 2) {
|
|
298
|
+
lastTouchDistance = Math.hypot(
|
|
299
|
+
e.touches[0].clientX - e.touches[1].clientX,
|
|
300
|
+
e.touches[0].clientY - e.touches[1].clientY
|
|
301
|
+
);
|
|
302
|
+
} else if (e.touches.length === 1) {
|
|
303
|
+
viewerState.isDragging = true;
|
|
304
|
+
viewerState.startX = e.touches[0].clientX - viewerState.translateX;
|
|
305
|
+
viewerState.startY = e.touches[0].clientY - viewerState.translateY;
|
|
306
|
+
}
|
|
307
|
+
}, { passive: true });
|
|
308
|
+
|
|
309
|
+
canvas.addEventListener('touchmove', (e) => {
|
|
310
|
+
if (e.touches.length === 2) {
|
|
311
|
+
e.preventDefault();
|
|
312
|
+
const distance = Math.hypot(
|
|
313
|
+
e.touches[0].clientX - e.touches[1].clientX,
|
|
314
|
+
e.touches[0].clientY - e.touches[1].clientY
|
|
315
|
+
);
|
|
316
|
+
const delta = (distance - lastTouchDistance) * 0.01;
|
|
317
|
+
zoom(delta);
|
|
318
|
+
lastTouchDistance = distance;
|
|
319
|
+
} else if (e.touches.length === 1 && viewerState.isDragging) {
|
|
320
|
+
viewerState.translateX = e.touches[0].clientX - viewerState.startX;
|
|
321
|
+
viewerState.translateY = e.touches[0].clientY - viewerState.startY;
|
|
322
|
+
updateTransform();
|
|
323
|
+
}
|
|
324
|
+
}, { passive: false });
|
|
325
|
+
|
|
326
|
+
canvas.addEventListener('touchend', () => {
|
|
327
|
+
viewerState.isDragging = false;
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
// Button handlers
|
|
331
|
+
modal.querySelector('[data-action="zoom-in"]').addEventListener('click', () => zoom(SCALE_STEP));
|
|
332
|
+
modal.querySelector('[data-action="zoom-out"]').addEventListener('click', () => zoom(-SCALE_STEP));
|
|
333
|
+
modal.querySelector('[data-action="fit"]').addEventListener('click', fitToScreen);
|
|
334
|
+
modal.querySelector('[data-action="reset"]').addEventListener('click', resetView);
|
|
335
|
+
modal.querySelector('[data-action="close"]').addEventListener('click', closeModal);
|
|
336
|
+
|
|
337
|
+
// Copy button
|
|
338
|
+
modal.querySelector('[data-action="copy"]').addEventListener('click', async () => {
|
|
339
|
+
try {
|
|
340
|
+
await navigator.clipboard.writeText(code);
|
|
341
|
+
const btn = modal.querySelector('[data-action="copy"]');
|
|
342
|
+
btn.innerHTML = `
|
|
343
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="#4ade80" stroke-width="2">
|
|
344
|
+
<polyline points="20 6 9 17 4 12"></polyline>
|
|
345
|
+
</svg>
|
|
346
|
+
`;
|
|
347
|
+
setTimeout(() => {
|
|
348
|
+
btn.innerHTML = `
|
|
349
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
|
350
|
+
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
|
351
|
+
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
|
352
|
+
</svg>
|
|
353
|
+
`;
|
|
354
|
+
}, 1500);
|
|
355
|
+
} catch (err) {
|
|
356
|
+
console.error('[MermaidViewer] Copy failed:', err);
|
|
357
|
+
}
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
// Click outside to close
|
|
361
|
+
modal.addEventListener('click', (e) => {
|
|
362
|
+
if (e.target === modal) closeModal();
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
// Animate in
|
|
366
|
+
requestAnimationFrame(() => {
|
|
367
|
+
modal.style.opacity = '1';
|
|
368
|
+
// Auto fit after render
|
|
369
|
+
setTimeout(fitToScreen, 100);
|
|
370
|
+
});
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Show toast notification
|
|
375
|
+
*/
|
|
376
|
+
export function showToast(container, message, isError = false) {
|
|
377
|
+
const existingToast = container.querySelector('.mermaid-toast');
|
|
378
|
+
if (existingToast) existingToast.remove();
|
|
379
|
+
|
|
380
|
+
const toast = document.createElement('div');
|
|
381
|
+
toast.className = 'mermaid-toast';
|
|
382
|
+
Object.assign(toast.style, {
|
|
383
|
+
position: 'absolute',
|
|
384
|
+
top: '50%',
|
|
385
|
+
left: '50%',
|
|
386
|
+
transform: 'translate(-50%, -50%)',
|
|
387
|
+
padding: '8px 16px',
|
|
388
|
+
borderRadius: '6px',
|
|
389
|
+
fontSize: '12px',
|
|
390
|
+
fontWeight: '500',
|
|
391
|
+
zIndex: '100',
|
|
392
|
+
background: isError ? 'rgba(239, 68, 68, 0.9)' : 'rgba(74, 222, 128, 0.9)',
|
|
393
|
+
color: isError ? '#fff' : '#000',
|
|
394
|
+
animation: 'mermaid-toast-fade 2s ease-out forwards'
|
|
395
|
+
});
|
|
396
|
+
toast.textContent = message;
|
|
397
|
+
container.appendChild(toast);
|
|
398
|
+
|
|
399
|
+
setTimeout(() => toast.remove(), 2000);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
// Style helper functions
|
|
403
|
+
function getButtonStyle() {
|
|
404
|
+
return `
|
|
405
|
+
display: inline-flex;
|
|
406
|
+
align-items: center;
|
|
407
|
+
gap: 4px;
|
|
408
|
+
padding: 4px 8px;
|
|
409
|
+
background: rgba(255, 255, 255, 0.1);
|
|
410
|
+
border: none;
|
|
411
|
+
border-radius: 4px;
|
|
412
|
+
color: #a1a1aa;
|
|
413
|
+
cursor: pointer;
|
|
414
|
+
font-size: 11px;
|
|
415
|
+
transition: all 0.2s ease;
|
|
416
|
+
`.replace(/\s+/g, ' ').trim();
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
function getHeaderStyle() {
|
|
420
|
+
return `
|
|
421
|
+
display: flex;
|
|
422
|
+
justify-content: space-between;
|
|
423
|
+
align-items: center;
|
|
424
|
+
padding: 12px 20px;
|
|
425
|
+
background: rgba(20, 20, 25, 0.95);
|
|
426
|
+
border-bottom: 1px solid rgba(255, 255, 255, 0.08);
|
|
427
|
+
flex-shrink: 0;
|
|
428
|
+
`.replace(/\s+/g, ' ').trim();
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
function getControlButtonStyle() {
|
|
432
|
+
return `
|
|
433
|
+
display: flex;
|
|
434
|
+
align-items: center;
|
|
435
|
+
justify-content: center;
|
|
436
|
+
width: 32px;
|
|
437
|
+
height: 32px;
|
|
438
|
+
background: transparent;
|
|
439
|
+
border: none;
|
|
440
|
+
border-radius: 6px;
|
|
441
|
+
color: #a1a1aa;
|
|
442
|
+
cursor: pointer;
|
|
443
|
+
transition: all 0.15s ease;
|
|
444
|
+
`.replace(/\s+/g, ' ').trim();
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
function getCloseButtonStyle() {
|
|
448
|
+
return `
|
|
449
|
+
display: flex;
|
|
450
|
+
align-items: center;
|
|
451
|
+
justify-content: center;
|
|
452
|
+
width: 32px;
|
|
453
|
+
height: 32px;
|
|
454
|
+
background: rgba(239, 68, 68, 0.15);
|
|
455
|
+
border: none;
|
|
456
|
+
border-radius: 6px;
|
|
457
|
+
color: #ef4444;
|
|
458
|
+
cursor: pointer;
|
|
459
|
+
transition: all 0.15s ease;
|
|
460
|
+
`.replace(/\s+/g, ' ').trim();
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function getCanvasStyle() {
|
|
464
|
+
return `
|
|
465
|
+
flex: 1;
|
|
466
|
+
overflow: hidden;
|
|
467
|
+
background: linear-gradient(135deg, #0f0f14 0%, #1a1a24 100%);
|
|
468
|
+
position: relative;
|
|
469
|
+
cursor: grab;
|
|
470
|
+
`.replace(/\s+/g, ' ').trim();
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
function getContentStyle() {
|
|
474
|
+
return `
|
|
475
|
+
position: absolute;
|
|
476
|
+
top: 50%;
|
|
477
|
+
left: 50%;
|
|
478
|
+
transform-origin: center center;
|
|
479
|
+
transform: translate(-50%, -50%);
|
|
480
|
+
transition: transform 0.1s ease-out;
|
|
481
|
+
`.replace(/\s+/g, ' ').trim();
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function getFooterStyle() {
|
|
485
|
+
return `
|
|
486
|
+
display: flex;
|
|
487
|
+
justify-content: center;
|
|
488
|
+
align-items: center;
|
|
489
|
+
padding: 8px 20px;
|
|
490
|
+
background: rgba(20, 20, 25, 0.95);
|
|
491
|
+
border-top: 1px solid rgba(255, 255, 255, 0.08);
|
|
492
|
+
flex-shrink: 0;
|
|
493
|
+
`.replace(/\s+/g, ' ').trim();
|
|
494
|
+
}
|
|
@@ -123,7 +123,7 @@ function initRightResizeHandler() {
|
|
|
123
123
|
const currentX = e.clientX;
|
|
124
124
|
// For right panel, dragging left (negative diff) should increase width
|
|
125
125
|
const diffX = startX - currentX; // Inverted direction
|
|
126
|
-
const newWidth = Math.max(250, Math.min(
|
|
126
|
+
const newWidth = Math.max(250, Math.min(1200, startWidth + diffX)); // Min 250px, Max 1200px
|
|
127
127
|
|
|
128
128
|
// Update width of tool-panel-container-right
|
|
129
129
|
toolPanelContainerRight.style.width = `${newWidth}px`;
|