antri_cli 1.57.0 → 1.57.2
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/dist/cli/shortcuts.js +1 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +15 -7
- package/dist/core/agent.js.map +1 -1
- package/dist/core/artifactManager.d.ts.map +1 -1
- package/dist/core/artifactManager.js +548 -41
- package/dist/core/artifactManager.js.map +1 -1
- package/dist/core/config.js +1 -1
- package/dist/core/updater.d.ts +1 -1
- package/dist/core/updater.js +1 -1
- package/dist/desktop/public/index.html +1 -1
- package/dist/desktop/server.d.ts.map +1 -1
- package/dist/desktop/server.js +542 -47
- package/dist/desktop/server.js.map +1 -1
- package/package.json +1 -1
package/dist/desktop/server.js
CHANGED
|
@@ -264,12 +264,348 @@ export class DesktopServer {
|
|
|
264
264
|
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
265
265
|
res.end(content);
|
|
266
266
|
}
|
|
267
|
+
else if (artifact.type === 'mindmap') {
|
|
268
|
+
const htmlMindmap = `<!DOCTYPE html>
|
|
269
|
+
<html lang="en">
|
|
270
|
+
<head>
|
|
271
|
+
<meta charset="UTF-8">
|
|
272
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
273
|
+
<title>${artifact.title}</title>
|
|
274
|
+
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
|
|
275
|
+
<script src="https://cdn.jsdelivr.net/npm/markmap-view@0.17.2"></script>
|
|
276
|
+
<script src="https://cdn.jsdelivr.net/npm/markmap-lib@0.17.2"></script>
|
|
277
|
+
<style>
|
|
278
|
+
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
279
|
+
:root {
|
|
280
|
+
--bg-page: radial-gradient(circle at 15% 15%, rgba(99, 102, 241, 0.08) 0%, transparent 40%), radial-gradient(circle at 85% 85%, rgba(236, 72, 153, 0.08) 0%, transparent 40%), #f8fafc;
|
|
281
|
+
--bg-viewport: #ffffff;
|
|
282
|
+
--border-viewport: rgba(226, 232, 240, 0.9);
|
|
283
|
+
--text-main: #0f172a;
|
|
284
|
+
--text-muted: #64748b;
|
|
285
|
+
--btn-bg: #ffffff;
|
|
286
|
+
--btn-border: #e2e8f0;
|
|
287
|
+
--btn-text: #1e293b;
|
|
288
|
+
--btn-hover: #f1f5f9;
|
|
289
|
+
--shadow-viewport: 0 20px 45px -15px rgba(0, 0, 0, 0.06), 0 0 1px 1px rgba(0, 0, 0, 0.04);
|
|
290
|
+
--node-bg: #ffffff;
|
|
291
|
+
--node-border: #cbd5e1;
|
|
292
|
+
--node-text: #0f172a;
|
|
293
|
+
--root-bg: #4f46e5;
|
|
294
|
+
--root-border: #4338ca;
|
|
295
|
+
--root-text: #ffffff;
|
|
296
|
+
--link-stroke: #818cf8;
|
|
297
|
+
--badge-bg: rgba(168, 85, 247, 0.12);
|
|
298
|
+
--badge-color: #7e22ce;
|
|
299
|
+
--badge-border: rgba(168, 85, 247, 0.25);
|
|
300
|
+
}
|
|
301
|
+
body.theme-dark {
|
|
302
|
+
--bg-page: radial-gradient(circle at 10% 20%, rgba(99, 102, 241, 0.25) 0%, transparent 50%), radial-gradient(circle at 90% 80%, rgba(236, 72, 153, 0.22) 0%, transparent 50%), radial-gradient(circle at 50% 50%, rgba(6, 182, 212, 0.18) 0%, transparent 60%), #0d1322;
|
|
303
|
+
--bg-viewport: rgba(18, 24, 38, 0.9);
|
|
304
|
+
--border-viewport: rgba(255, 255, 255, 0.1);
|
|
305
|
+
--text-main: #f8fafc;
|
|
306
|
+
--text-muted: #94a3b8;
|
|
307
|
+
--btn-bg: rgba(30, 41, 59, 0.85);
|
|
308
|
+
--btn-border: rgba(255, 255, 255, 0.12);
|
|
309
|
+
--btn-text: #f8fafc;
|
|
310
|
+
--btn-hover: rgba(51, 65, 85, 0.95);
|
|
311
|
+
--shadow-viewport: 0 20px 45px -15px rgba(0, 0, 0, 0.5);
|
|
312
|
+
--node-bg: #1e293b;
|
|
313
|
+
--node-border: #334155;
|
|
314
|
+
--node-text: #f8fafc;
|
|
315
|
+
--root-bg: #6366f1;
|
|
316
|
+
--root-border: #818cf8;
|
|
317
|
+
--root-text: #ffffff;
|
|
318
|
+
--link-stroke: #6366f1;
|
|
319
|
+
--badge-bg: rgba(168, 85, 247, 0.2);
|
|
320
|
+
--badge-color: #c084fc;
|
|
321
|
+
--badge-border: rgba(168, 85, 247, 0.35);
|
|
322
|
+
}
|
|
323
|
+
body {
|
|
324
|
+
margin: 0; padding: 20px;
|
|
325
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
326
|
+
background: var(--bg-page);
|
|
327
|
+
color: var(--text-main);
|
|
328
|
+
display: flex; flex-direction: column; align-items: center; min-height: 100vh;
|
|
329
|
+
overflow-x: hidden;
|
|
330
|
+
transition: background 0.3s ease;
|
|
331
|
+
}
|
|
332
|
+
.header { text-align: center; margin-bottom: 14px; }
|
|
333
|
+
h1 { font-size: 19px; color: var(--text-main); margin: 0 0 6px 0; font-weight: 800; letter-spacing: -0.3px; }
|
|
334
|
+
.badge {
|
|
335
|
+
font-size: 11px;
|
|
336
|
+
background: var(--badge-bg);
|
|
337
|
+
color: var(--badge-color);
|
|
338
|
+
padding: 4px 12px; border-radius: 9999px;
|
|
339
|
+
border: 1px solid var(--badge-border);
|
|
340
|
+
text-transform: uppercase; font-weight: 700; letter-spacing: 0.5px;
|
|
341
|
+
display: inline-block; margin-bottom: 6px;
|
|
342
|
+
}
|
|
343
|
+
.toolbar {
|
|
344
|
+
display: flex; gap: 8px; margin-bottom: 14px; align-items: center; flex-wrap: wrap; justify-content: center;
|
|
345
|
+
}
|
|
346
|
+
.tool-btn {
|
|
347
|
+
background: var(--btn-bg); border: 1px solid var(--btn-border);
|
|
348
|
+
color: var(--btn-text); padding: 7px 14px; border-radius: 9px; font-size: 12px;
|
|
349
|
+
font-weight: 700; cursor: pointer; transition: all 0.15s; display: flex; align-items: center; gap: 6px;
|
|
350
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.03);
|
|
351
|
+
}
|
|
352
|
+
.tool-btn:hover { background: var(--btn-hover); transform: translateY(-1px); }
|
|
353
|
+
.tool-btn:active { transform: translateY(0); }
|
|
354
|
+
.viewport {
|
|
355
|
+
width: 100%; max-width: 1200px; height: 75vh; min-height: 540px;
|
|
356
|
+
position: relative; overflow: hidden; border-radius: 18px;
|
|
357
|
+
background: var(--bg-viewport); backdrop-filter: blur(20px);
|
|
358
|
+
border: 1px solid var(--border-viewport);
|
|
359
|
+
box-shadow: var(--shadow-viewport);
|
|
360
|
+
touch-action: none;
|
|
361
|
+
}
|
|
362
|
+
#mindmapSvg {
|
|
363
|
+
width: 100%; height: 100%;
|
|
364
|
+
display: block;
|
|
365
|
+
cursor: grab;
|
|
366
|
+
}
|
|
367
|
+
#mindmapSvg:active { cursor: grabbing; }
|
|
368
|
+
.markmap-foreign {
|
|
369
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
|
|
370
|
+
font-size: 13.5px !important;
|
|
371
|
+
font-weight: 600 !important;
|
|
372
|
+
line-height: 1.4 !important;
|
|
373
|
+
color: var(--node-text) !important;
|
|
374
|
+
background: var(--node-bg) !important;
|
|
375
|
+
border: 1.5px solid var(--node-border) !important;
|
|
376
|
+
border-radius: 8px !important;
|
|
377
|
+
padding: 6px 14px !important;
|
|
378
|
+
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04) !important;
|
|
379
|
+
display: inline-flex !important;
|
|
380
|
+
align-items: center !important;
|
|
381
|
+
white-space: nowrap !important;
|
|
382
|
+
transition: all 0.2s ease !important;
|
|
383
|
+
}
|
|
384
|
+
.markmap-node[data-depth="0"] .markmap-foreign {
|
|
385
|
+
background: var(--root-bg) !important;
|
|
386
|
+
color: var(--root-text) !important;
|
|
387
|
+
border-color: var(--root-border) !important;
|
|
388
|
+
font-size: 16px !important;
|
|
389
|
+
font-weight: 800 !important;
|
|
390
|
+
border-radius: 10px !important;
|
|
391
|
+
padding: 8px 18px !important;
|
|
392
|
+
box-shadow: 0 4px 14px rgba(79, 70, 229, 0.35) !important;
|
|
393
|
+
}
|
|
394
|
+
.markmap-node[data-depth="1"] .markmap-foreign {
|
|
395
|
+
font-weight: 700 !important;
|
|
396
|
+
border-width: 1.8px !important;
|
|
397
|
+
}
|
|
398
|
+
.markmap-link {
|
|
399
|
+
fill: none !important;
|
|
400
|
+
stroke: var(--link-stroke) !important;
|
|
401
|
+
stroke-width: 2px !important;
|
|
402
|
+
stroke-linecap: round !important;
|
|
403
|
+
}
|
|
404
|
+
.markmap-node > circle {
|
|
405
|
+
fill: var(--root-bg) !important;
|
|
406
|
+
stroke: var(--root-border) !important;
|
|
407
|
+
stroke-width: 2px !important;
|
|
408
|
+
r: 6 !important;
|
|
409
|
+
cursor: pointer !important;
|
|
410
|
+
}
|
|
411
|
+
.help-hint {
|
|
412
|
+
position: absolute; bottom: 12px; right: 14px;
|
|
413
|
+
font-size: 11px; color: var(--text-muted); pointer-events: none;
|
|
414
|
+
background: rgba(255, 255, 255, 0.75); padding: 3px 8px; border-radius: 6px;
|
|
415
|
+
border: 1px solid rgba(0,0,0,0.05);
|
|
416
|
+
}
|
|
417
|
+
body.theme-dark .help-hint {
|
|
418
|
+
background: rgba(18, 24, 38, 0.7);
|
|
419
|
+
border-color: rgba(255, 255, 255, 0.08);
|
|
420
|
+
}
|
|
421
|
+
</style>
|
|
422
|
+
</head>
|
|
423
|
+
<body>
|
|
424
|
+
<div class="header">
|
|
425
|
+
<span class="badge">🧠 ANTRI Interactive Mind Map</span>
|
|
426
|
+
<h1>${artifact.title}</h1>
|
|
427
|
+
</div>
|
|
428
|
+
<div class="toolbar">
|
|
429
|
+
<button class="tool-btn" onclick="zoomIn()">🔍 Zoom +</button>
|
|
430
|
+
<button class="tool-btn" onclick="zoomOut()">🔍 Zoom -</button>
|
|
431
|
+
<button class="tool-btn" onclick="fitView()">⛶ Fit / Reset</button>
|
|
432
|
+
<button class="tool-btn" onclick="toggleTheme()" id="themeToggleBtn">🌙 Poster Dark</button>
|
|
433
|
+
<button class="tool-btn" onclick="copySource()">📋 Copy Code</button>
|
|
434
|
+
</div>
|
|
435
|
+
<div class="viewer-card" id="viewerCard">
|
|
436
|
+
<svg id="mindmapSvg"></svg>
|
|
437
|
+
<div class="help-hint">🖱️ Drag to pan · Scroll to zoom · Click nodes with circle handles to collapse/expand</div>
|
|
438
|
+
</div>
|
|
439
|
+
<script>
|
|
440
|
+
const rawContent = ${JSON.stringify(artifact.content.trim())};
|
|
441
|
+
|
|
442
|
+
function parseToMarkdown(content) {
|
|
443
|
+
if (content.startsWith('#') || content.startsWith('-') || content.includes('\\n#') || content.includes('\\n-')) {
|
|
444
|
+
return content;
|
|
445
|
+
}
|
|
446
|
+
const lines = content.split('\\n');
|
|
447
|
+
const mdLines = [];
|
|
448
|
+
let baseIndent = -1;
|
|
449
|
+
for (let line of lines) {
|
|
450
|
+
const trimmed = line.trim();
|
|
451
|
+
if (!trimmed || trimmed === 'mindmap') continue;
|
|
452
|
+
const indent = line.search(/\\S/);
|
|
453
|
+
if (baseIndent === -1) baseIndent = indent;
|
|
454
|
+
const rel = Math.max(0, indent - baseIndent);
|
|
455
|
+
const depth = Math.floor(rel / 2);
|
|
456
|
+
|
|
457
|
+
let clean = trimmed
|
|
458
|
+
.replace(/^root\\(\\((.*?)\\)\\)$/, '$1')
|
|
459
|
+
.replace(/^\\(\\((.*?)\\)\\)$/, '$1')
|
|
460
|
+
.replace(/^\\[(.*?)\\]$/, '$1')
|
|
461
|
+
.replace(/^\\((.*?)\\)$/, '$1')
|
|
462
|
+
.replace(/^\\)\\)(.*?)\\(\\($/, '$1')
|
|
463
|
+
.replace(/^\\)(.*?)\\($/, '$1')
|
|
464
|
+
.replace(/^\\{\\{(.*?)\\}\\}/, '$1');
|
|
465
|
+
|
|
466
|
+
if (depth === 0) {
|
|
467
|
+
mdLines.push('# ' + clean);
|
|
468
|
+
} else if (depth === 1) {
|
|
469
|
+
mdLines.push('## ' + clean);
|
|
470
|
+
} else {
|
|
471
|
+
mdLines.push(' '.repeat(depth - 1) + '- ' + clean);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return mdLines.join('\\n');
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
const markdownText = parseToMarkdown(rawContent);
|
|
478
|
+
let mmInstance = null;
|
|
479
|
+
|
|
480
|
+
function renderMarkmap() {
|
|
481
|
+
try {
|
|
482
|
+
if (window.markmap && window.markmap.Transformer) {
|
|
483
|
+
const { Transformer, Markmap } = window.markmap;
|
|
484
|
+
const transformer = new Transformer();
|
|
485
|
+
const { root } = transformer.transform(markdownText);
|
|
486
|
+
const svgEl = document.getElementById('mindmapSvg');
|
|
487
|
+
svgEl.innerHTML = '';
|
|
488
|
+
mmInstance = Markmap.create(svgEl, {
|
|
489
|
+
autoFit: true,
|
|
490
|
+
duration: 250,
|
|
491
|
+
nodeMinHeight: 28,
|
|
492
|
+
spacingVertical: 14,
|
|
493
|
+
spacingHorizontal: 85,
|
|
494
|
+
paddingX: 16
|
|
495
|
+
}, root);
|
|
496
|
+
return;
|
|
497
|
+
}
|
|
498
|
+
} catch (err) {
|
|
499
|
+
console.warn('Markmap load error:', err);
|
|
500
|
+
}
|
|
501
|
+
renderFallbackTree();
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
function renderFallbackTree() {
|
|
505
|
+
const svg = document.getElementById('mindmapSvg');
|
|
506
|
+
svg.innerHTML = '';
|
|
507
|
+
const lines = markdownText.split('\\n').filter(l => l.trim().length > 0);
|
|
508
|
+
const rootNode = { name: '${artifact.title}', children: [] };
|
|
509
|
+
const stack = [{ node: rootNode, depth: 0 }];
|
|
510
|
+
|
|
511
|
+
lines.forEach((line) => {
|
|
512
|
+
let depth = 1;
|
|
513
|
+
let text = line.trim();
|
|
514
|
+
if (text.startsWith('# ')) {
|
|
515
|
+
rootNode.name = text.replace('# ', '').trim();
|
|
516
|
+
return;
|
|
517
|
+
} else if (text.startsWith('## ')) {
|
|
518
|
+
depth = 1;
|
|
519
|
+
text = text.replace('## ', '').trim();
|
|
520
|
+
} else {
|
|
521
|
+
const leadSpaces = line.search(/\\S/);
|
|
522
|
+
depth = 2 + Math.floor(leadSpaces / 2);
|
|
523
|
+
text = text.replace(/^[-*]\\s*/, '').trim();
|
|
524
|
+
}
|
|
525
|
+
const newNode = { name: text, children: [] };
|
|
526
|
+
while (stack.length > depth) stack.pop();
|
|
527
|
+
stack[stack.length - 1].node.children.push(newNode);
|
|
528
|
+
stack.push({ node: newNode, depth: depth });
|
|
529
|
+
});
|
|
530
|
+
|
|
531
|
+
let curY = 40;
|
|
532
|
+
function layout(node, depth = 0) {
|
|
533
|
+
node.depth = depth;
|
|
534
|
+
node.x = 60 + depth * 220;
|
|
535
|
+
if (!node.children || node.children.length === 0) {
|
|
536
|
+
node.y = curY;
|
|
537
|
+
curY += 46;
|
|
538
|
+
} else {
|
|
539
|
+
node.children.forEach(c => layout(c, depth + 1));
|
|
540
|
+
const firstY = node.children[0].y;
|
|
541
|
+
const lastY = node.children[node.children.length - 1].y;
|
|
542
|
+
node.y = (firstY + lastY) / 2;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
layout(rootNode);
|
|
546
|
+
|
|
547
|
+
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
548
|
+
g.setAttribute('id', 'treeGroup');
|
|
549
|
+
svg.appendChild(g);
|
|
550
|
+
|
|
551
|
+
function draw(node) {
|
|
552
|
+
if (node.children) {
|
|
553
|
+
node.children.forEach(c => {
|
|
554
|
+
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
555
|
+
const x1 = node.x + 130, y1 = node.y;
|
|
556
|
+
const x2 = c.x, y2 = c.y;
|
|
557
|
+
const dx = (x2 - x1) * 0.5;
|
|
558
|
+
path.setAttribute('d', 'M ' + x1 + ' ' + y1 + ' C ' + (x1 + dx) + ' ' + y1 + ', ' + (x2 - dx) + ' ' + y2 + ', ' + x2 + ' ' + y2);
|
|
559
|
+
path.setAttribute('class', 'markmap-link');
|
|
560
|
+
g.appendChild(path);
|
|
561
|
+
draw(c);
|
|
562
|
+
});
|
|
563
|
+
}
|
|
564
|
+
const foreign = document.createElementNS('http://www.w3.org/2000/svg', 'foreignObject');
|
|
565
|
+
foreign.setAttribute('x', node.x - 10);
|
|
566
|
+
foreign.setAttribute('y', node.y - 18);
|
|
567
|
+
foreign.setAttribute('width', '200');
|
|
568
|
+
foreign.setAttribute('height', '40');
|
|
569
|
+
foreign.innerHTML = '<div class="markmap-node" data-depth="' + node.depth + '"><div class="markmap-foreign">' + node.name + '</div></div>';
|
|
570
|
+
g.appendChild(foreign);
|
|
571
|
+
}
|
|
572
|
+
draw(rootNode);
|
|
573
|
+
svg.setAttribute('viewBox', '0 0 ' + Math.max(800, curY * 1.8) + ' ' + Math.max(500, curY + 60));
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
function zoomIn() {
|
|
577
|
+
if (mmInstance) mmInstance.rescale(1.25);
|
|
578
|
+
}
|
|
579
|
+
function zoomOut() {
|
|
580
|
+
if (mmInstance) mmInstance.rescale(0.8);
|
|
581
|
+
}
|
|
582
|
+
function fitView() {
|
|
583
|
+
if (mmInstance) mmInstance.fit();
|
|
584
|
+
}
|
|
585
|
+
function toggleTheme() {
|
|
586
|
+
document.body.classList.toggle('theme-dark');
|
|
587
|
+
const isDark = document.body.classList.contains('theme-dark');
|
|
588
|
+
const btn = document.getElementById('themeToggleBtn');
|
|
589
|
+
if (btn) btn.textContent = isDark ? '☀️ Light Mode' : '🌙 Poster Dark';
|
|
590
|
+
}
|
|
591
|
+
function copySource() {
|
|
592
|
+
navigator.clipboard.writeText(rawContent).then(() => {
|
|
593
|
+
alert('Mind map source copied to clipboard!');
|
|
594
|
+
});
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
window.addEventListener('load', () => {
|
|
598
|
+
setTimeout(renderMarkmap, 60);
|
|
599
|
+
});
|
|
600
|
+
if (document.readyState === 'complete') renderMarkmap();
|
|
601
|
+
</script>
|
|
602
|
+
</body>
|
|
603
|
+
</html>`;
|
|
604
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
605
|
+
res.end(htmlMindmap);
|
|
606
|
+
}
|
|
267
607
|
else {
|
|
268
|
-
const
|
|
269
|
-
const badgeLabel = isMindmap ? '🧠 Interactive Mind Map' : '📊 Architecture Graph';
|
|
270
|
-
const badgeColor = isMindmap ? '#c084fc' : '#38bdf8';
|
|
271
|
-
const badgeBg = isMindmap ? 'rgba(168, 85, 247, 0.15)' : 'rgba(56, 189, 248, 0.15)';
|
|
272
|
-
const badgeBorder = isMindmap ? 'rgba(168, 85, 247, 0.3)' : 'rgba(56, 189, 248, 0.3)';
|
|
608
|
+
const badgeLabel = '📊 Architecture Graph';
|
|
273
609
|
const htmlGraph = `<!DOCTYPE html>
|
|
274
610
|
<html lang="en">
|
|
275
611
|
<head>
|
|
@@ -279,22 +615,53 @@ export class DesktopServer {
|
|
|
279
615
|
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
280
616
|
<style>
|
|
281
617
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
618
|
+
:root {
|
|
619
|
+
--bg-page: radial-gradient(circle at 15% 15%, rgba(99, 102, 241, 0.08) 0%, transparent 40%), radial-gradient(circle at 85% 85%, rgba(236, 72, 153, 0.08) 0%, transparent 40%), #f8fafc;
|
|
620
|
+
--bg-viewport: #ffffff;
|
|
621
|
+
--border-viewport: rgba(226, 232, 240, 0.9);
|
|
622
|
+
--text-main: #0f172a;
|
|
623
|
+
--text-muted: #64748b;
|
|
624
|
+
--btn-bg: #ffffff;
|
|
625
|
+
--btn-border: #e2e8f0;
|
|
626
|
+
--btn-text: #1e293b;
|
|
627
|
+
--btn-hover: #f1f5f9;
|
|
628
|
+
--shadow-viewport: 0 20px 45px -15px rgba(0, 0, 0, 0.06), 0 0 1px 1px rgba(0, 0, 0, 0.04);
|
|
629
|
+
--badge-bg: rgba(99, 102, 241, 0.1);
|
|
630
|
+
--badge-color: #4f46e5;
|
|
631
|
+
--badge-border: rgba(99, 102, 241, 0.25);
|
|
632
|
+
}
|
|
633
|
+
body.theme-dark {
|
|
634
|
+
--bg-page: radial-gradient(circle at 10% 20%, rgba(99, 102, 241, 0.25) 0%, transparent 50%), radial-gradient(circle at 90% 80%, rgba(236, 72, 153, 0.22) 0%, transparent 50%), radial-gradient(circle at 50% 50%, rgba(6, 182, 212, 0.18) 0%, transparent 60%), #0d1322;
|
|
635
|
+
--bg-viewport: rgba(18, 24, 38, 0.85);
|
|
636
|
+
--border-viewport: rgba(255, 255, 255, 0.1);
|
|
637
|
+
--text-main: #f8fafc;
|
|
638
|
+
--text-muted: #94a3b8;
|
|
639
|
+
--btn-bg: rgba(30, 41, 59, 0.85);
|
|
640
|
+
--btn-border: rgba(255, 255, 255, 0.12);
|
|
641
|
+
--btn-text: #f8fafc;
|
|
642
|
+
--btn-hover: rgba(51, 65, 85, 0.95);
|
|
643
|
+
--shadow-viewport: 0 20px 45px -15px rgba(0, 0, 0, 0.5);
|
|
644
|
+
--badge-bg: rgba(168, 85, 247, 0.2);
|
|
645
|
+
--badge-color: #c084fc;
|
|
646
|
+
--badge-border: rgba(168, 85, 247, 0.35);
|
|
647
|
+
}
|
|
282
648
|
body {
|
|
283
649
|
margin: 0; padding: 20px;
|
|
284
650
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
285
|
-
background:
|
|
286
|
-
color:
|
|
651
|
+
background: var(--bg-page);
|
|
652
|
+
color: var(--text-main);
|
|
287
653
|
display: flex; flex-direction: column; align-items: center; min-height: 100vh;
|
|
288
654
|
overflow-x: hidden;
|
|
655
|
+
transition: background 0.3s ease;
|
|
289
656
|
}
|
|
290
|
-
.header { text-align: center; margin-bottom:
|
|
291
|
-
h1 { font-size: 19px; color:
|
|
657
|
+
.header { text-align: center; margin-bottom: 14px; }
|
|
658
|
+
h1 { font-size: 19px; color: var(--text-main); margin: 0 0 6px 0; font-weight: 800; letter-spacing: -0.3px; }
|
|
292
659
|
.badge {
|
|
293
660
|
font-size: 11px;
|
|
294
|
-
background:
|
|
295
|
-
color:
|
|
661
|
+
background: var(--badge-bg);
|
|
662
|
+
color: var(--badge-color);
|
|
296
663
|
padding: 4px 12px; border-radius: 9999px;
|
|
297
|
-
border: 1px solid
|
|
664
|
+
border: 1px solid var(--badge-border);
|
|
298
665
|
text-transform: uppercase; font-weight: 700; letter-spacing: 0.5px;
|
|
299
666
|
display: inline-block; margin-bottom: 6px;
|
|
300
667
|
}
|
|
@@ -302,24 +669,43 @@ export class DesktopServer {
|
|
|
302
669
|
display: flex; gap: 8px; margin-bottom: 14px; align-items: center; flex-wrap: wrap; justify-content: center;
|
|
303
670
|
}
|
|
304
671
|
.tool-btn {
|
|
305
|
-
background:
|
|
306
|
-
color:
|
|
307
|
-
font-weight:
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
.
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
672
|
+
background: var(--btn-bg); border: 1px solid var(--btn-border);
|
|
673
|
+
color: var(--btn-text); padding: 7px 14px; border-radius: 9px; font-size: 12px;
|
|
674
|
+
font-weight: 700; cursor: pointer; transition: all 0.15s; display: flex; align-items: center; gap: 6px;
|
|
675
|
+
box-shadow: 0 2px 4px rgba(0,0,0,0.03);
|
|
676
|
+
}
|
|
677
|
+
.tool-btn:hover { background: var(--btn-hover); transform: translateY(-1px); }
|
|
678
|
+
.tool-btn:active { transform: translateY(0); }
|
|
679
|
+
.viewport {
|
|
680
|
+
width: 100%; max-width: 1150px; height: 72vh; min-height: 520px;
|
|
681
|
+
position: relative; overflow: hidden; border-radius: 18px;
|
|
682
|
+
background: var(--bg-viewport); backdrop-filter: blur(20px);
|
|
683
|
+
border: 1px solid var(--border-viewport);
|
|
684
|
+
box-shadow: var(--shadow-viewport);
|
|
685
|
+
cursor: grab; user-select: none; touch-action: none;
|
|
686
|
+
}
|
|
687
|
+
.viewport.grabbing { cursor: grabbing; }
|
|
688
|
+
.canvas {
|
|
689
|
+
width: 100%; height: 100%;
|
|
690
|
+
display: flex; align-items: center; justify-content: center;
|
|
691
|
+
transform-origin: center center;
|
|
692
|
+
will-change: transform;
|
|
693
|
+
}
|
|
694
|
+
.canvas.animating {
|
|
695
|
+
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
696
|
+
}
|
|
697
|
+
.mermaid { width: 100%; text-align: center; }
|
|
698
|
+
.mermaid svg { max-width: 100%; height: auto; }
|
|
699
|
+
.help-hint {
|
|
700
|
+
position: absolute; bottom: 12px; right: 14px;
|
|
701
|
+
font-size: 11px; color: var(--text-muted); pointer-events: none;
|
|
702
|
+
background: rgba(255, 255, 255, 0.75); padding: 3px 8px; border-radius: 6px;
|
|
703
|
+
border: 1px solid rgba(0,0,0,0.05);
|
|
704
|
+
}
|
|
705
|
+
body.theme-dark .help-hint {
|
|
706
|
+
background: rgba(18, 24, 38, 0.7);
|
|
707
|
+
border-color: rgba(255, 255, 255, 0.08);
|
|
708
|
+
}
|
|
323
709
|
</style>
|
|
324
710
|
</head>
|
|
325
711
|
<body>
|
|
@@ -331,32 +717,138 @@ export class DesktopServer {
|
|
|
331
717
|
<button class="tool-btn" onclick="zoomIn()">🔍 Zoom +</button>
|
|
332
718
|
<button class="tool-btn" onclick="zoomOut()">🔍 Zoom -</button>
|
|
333
719
|
<button class="tool-btn" onclick="resetZoom()">⛶ Reset</button>
|
|
720
|
+
<button class="tool-btn" onclick="toggleTheme()" id="themeToggleBtn">🌙 Poster Dark</button>
|
|
334
721
|
<button class="tool-btn" onclick="copySource()">📋 Copy Code</button>
|
|
335
722
|
</div>
|
|
336
|
-
<div class="
|
|
337
|
-
<div class="
|
|
723
|
+
<div class="viewport" id="viewport">
|
|
724
|
+
<div class="canvas" id="canvas">
|
|
338
725
|
<pre class="mermaid" id="mermaidCode">
|
|
339
726
|
${artifact.content.trim()}
|
|
340
727
|
</pre>
|
|
341
728
|
</div>
|
|
729
|
+
<div class="help-hint">🖱️ Drag to pan · Scroll to zoom</div>
|
|
342
730
|
</div>
|
|
343
731
|
<script>
|
|
344
732
|
let scale = 1.0;
|
|
345
|
-
|
|
733
|
+
let posX = 0;
|
|
734
|
+
let posY = 0;
|
|
735
|
+
let isDragging = false;
|
|
736
|
+
let startPointerX = 0;
|
|
737
|
+
let startPointerY = 0;
|
|
738
|
+
let startPosX = 0;
|
|
739
|
+
let startPosY = 0;
|
|
740
|
+
|
|
741
|
+
const activeTouches = new Map();
|
|
742
|
+
let initialPinchDist = 0;
|
|
743
|
+
let initialPinchScale = 1.0;
|
|
744
|
+
|
|
745
|
+
const viewport = document.getElementById('viewport');
|
|
746
|
+
const canvas = document.getElementById('canvas');
|
|
747
|
+
|
|
748
|
+
function updateTransform(animate = false) {
|
|
749
|
+
if (animate) {
|
|
750
|
+
canvas.classList.add('animating');
|
|
751
|
+
setTimeout(() => canvas.classList.remove('animating'), 320);
|
|
752
|
+
} else {
|
|
753
|
+
canvas.classList.remove('animating');
|
|
754
|
+
}
|
|
755
|
+
canvas.style.transform = 'translate3d(' + posX + 'px, ' + posY + 'px, 0) scale(' + scale + ')';
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
viewport.addEventListener('pointerdown', (e) => {
|
|
759
|
+
activeTouches.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
760
|
+
try { viewport.setPointerCapture(e.pointerId); } catch {}
|
|
761
|
+
|
|
762
|
+
if (activeTouches.size === 1) {
|
|
763
|
+
isDragging = true;
|
|
764
|
+
viewport.classList.add('grabbing');
|
|
765
|
+
startPointerX = e.clientX;
|
|
766
|
+
startPointerY = e.clientY;
|
|
767
|
+
startPosX = posX;
|
|
768
|
+
startPosY = posY;
|
|
769
|
+
} else if (activeTouches.size === 2) {
|
|
770
|
+
isDragging = false;
|
|
771
|
+
viewport.classList.remove('grabbing');
|
|
772
|
+
const pts = Array.from(activeTouches.values());
|
|
773
|
+
initialPinchDist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
|
|
774
|
+
initialPinchScale = scale;
|
|
775
|
+
}
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
viewport.addEventListener('pointermove', (e) => {
|
|
779
|
+
if (!activeTouches.has(e.pointerId)) return;
|
|
780
|
+
activeTouches.set(e.pointerId, { x: e.clientX, y: e.clientY });
|
|
781
|
+
|
|
782
|
+
if (activeTouches.size === 1 && isDragging) {
|
|
783
|
+
const dx = e.clientX - startPointerX;
|
|
784
|
+
const dy = e.clientY - startPointerY;
|
|
785
|
+
posX = startPosX + dx;
|
|
786
|
+
posY = startPosY + dy;
|
|
787
|
+
updateTransform(false);
|
|
788
|
+
} else if (activeTouches.size === 2) {
|
|
789
|
+
const pts = Array.from(activeTouches.values());
|
|
790
|
+
const currentDist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
|
|
791
|
+
if (initialPinchDist > 0) {
|
|
792
|
+
scale = Math.min(Math.max(initialPinchScale * (currentDist / initialPinchDist), 0.2), 4.0);
|
|
793
|
+
updateTransform(false);
|
|
794
|
+
}
|
|
795
|
+
}
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
function endPointer(e) {
|
|
799
|
+
activeTouches.delete(e.pointerId);
|
|
800
|
+
try { viewport.releasePointerCapture(e.pointerId); } catch {}
|
|
801
|
+
if (activeTouches.size === 0) {
|
|
802
|
+
isDragging = false;
|
|
803
|
+
viewport.classList.remove('grabbing');
|
|
804
|
+
} else if (activeTouches.size === 1) {
|
|
805
|
+
isDragging = true;
|
|
806
|
+
viewport.classList.add('grabbing');
|
|
807
|
+
const remaining = Array.from(activeTouches.values())[0];
|
|
808
|
+
startPointerX = remaining.x;
|
|
809
|
+
startPointerY = remaining.y;
|
|
810
|
+
startPosX = posX;
|
|
811
|
+
startPosY = posY;
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
|
|
815
|
+
viewport.addEventListener('pointerup', endPointer);
|
|
816
|
+
viewport.addEventListener('pointercancel', endPointer);
|
|
817
|
+
|
|
818
|
+
viewport.addEventListener('wheel', (e) => {
|
|
819
|
+
e.preventDefault();
|
|
820
|
+
const delta = e.deltaY < 0 ? 1.15 : 0.87;
|
|
821
|
+
const newScale = Math.min(Math.max(scale * delta, 0.2), 4.0);
|
|
822
|
+
|
|
823
|
+
const rect = viewport.getBoundingClientRect();
|
|
824
|
+
const mouseX = e.clientX - rect.left - rect.width / 2;
|
|
825
|
+
const mouseY = e.clientY - rect.top - rect.height / 2;
|
|
826
|
+
|
|
827
|
+
posX = mouseX - (mouseX - posX) * (newScale / scale);
|
|
828
|
+
posY = mouseY - (mouseY - posY) * (newScale / scale);
|
|
829
|
+
scale = newScale;
|
|
830
|
+
updateTransform(false);
|
|
831
|
+
}, { passive: false });
|
|
832
|
+
|
|
346
833
|
function zoomIn() {
|
|
347
|
-
scale = Math.min(scale
|
|
348
|
-
|
|
834
|
+
scale = Math.min(scale * 1.25, 4.0);
|
|
835
|
+
updateTransform(true);
|
|
349
836
|
}
|
|
350
837
|
function zoomOut() {
|
|
351
|
-
scale = Math.max(scale
|
|
352
|
-
|
|
838
|
+
scale = Math.max(scale * 0.8, 0.2);
|
|
839
|
+
updateTransform(true);
|
|
353
840
|
}
|
|
354
841
|
function resetZoom() {
|
|
355
842
|
scale = 1.0;
|
|
356
|
-
|
|
843
|
+
posX = 0;
|
|
844
|
+
posY = 0;
|
|
845
|
+
updateTransform(true);
|
|
357
846
|
}
|
|
358
|
-
function
|
|
359
|
-
|
|
847
|
+
function toggleTheme() {
|
|
848
|
+
document.body.classList.toggle('theme-dark');
|
|
849
|
+
const isDark = document.body.classList.contains('theme-dark');
|
|
850
|
+
const btn = document.getElementById('themeToggleBtn');
|
|
851
|
+
if (btn) btn.textContent = isDark ? '☀️ Light Mode' : '🌙 Poster Dark';
|
|
360
852
|
}
|
|
361
853
|
function copySource() {
|
|
362
854
|
const code = ${JSON.stringify(artifact.content.trim())};
|
|
@@ -364,19 +856,22 @@ ${artifact.content.trim()}
|
|
|
364
856
|
alert('Mind map source code copied to clipboard!');
|
|
365
857
|
});
|
|
366
858
|
}
|
|
859
|
+
|
|
367
860
|
mermaid.initialize({
|
|
368
861
|
startOnLoad: true,
|
|
369
|
-
theme: '
|
|
862
|
+
theme: 'base',
|
|
370
863
|
securityLevel: 'loose',
|
|
371
864
|
themeVariables: {
|
|
372
|
-
darkMode:
|
|
373
|
-
background: '#
|
|
865
|
+
darkMode: false,
|
|
866
|
+
background: '#ffffff',
|
|
374
867
|
primaryColor: '#6366f1',
|
|
375
|
-
primaryTextColor: '#
|
|
376
|
-
primaryBorderColor: '#
|
|
377
|
-
lineColor: '
|
|
868
|
+
primaryTextColor: '#0f172a',
|
|
869
|
+
primaryBorderColor: '#4f46e5',
|
|
870
|
+
lineColor: '#6366f1',
|
|
378
871
|
secondaryColor: '#0ea5e9',
|
|
379
|
-
tertiaryColor: '#10b981'
|
|
872
|
+
tertiaryColor: '#10b981',
|
|
873
|
+
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
874
|
+
fontSize: '14px'
|
|
380
875
|
}
|
|
381
876
|
});
|
|
382
877
|
</script>
|