antri_cli 1.57.6 → 1.57.7
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.d.ts.map +1 -1
- package/dist/cli/shortcuts.js +12 -10
- package/dist/cli/shortcuts.js.map +1 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +17 -12
- package/dist/core/agent.js.map +1 -1
- package/dist/core/artifactManager.d.ts +1 -0
- package/dist/core/artifactManager.d.ts.map +1 -1
- package/dist/core/artifactManager.js +372 -292
- package/dist/core/artifactManager.js.map +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 +3 -624
- package/dist/desktop/server.js.map +1 -1
- package/package.json +1 -1
package/dist/desktop/server.js
CHANGED
|
@@ -256,630 +256,9 @@ export class DesktopServer {
|
|
|
256
256
|
res.end('Artifact not found');
|
|
257
257
|
return;
|
|
258
258
|
}
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
content = `<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><style>body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;background:#0f172a;color:#f8fafc;padding:16px;}</style></head><body>${content}</body></html>`;
|
|
263
|
-
}
|
|
264
|
-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
265
|
-
res.end(content);
|
|
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
|
-
}
|
|
607
|
-
else {
|
|
608
|
-
const badgeLabel = '📊 Architecture Graph';
|
|
609
|
-
const htmlGraph = `<!DOCTYPE html>
|
|
610
|
-
<html lang="en">
|
|
611
|
-
<head>
|
|
612
|
-
<meta charset="UTF-8">
|
|
613
|
-
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
614
|
-
<title>${artifact.title}</title>
|
|
615
|
-
<script src="https://cdn.jsdelivr.net/npm/mermaid@10/dist/mermaid.min.js"></script>
|
|
616
|
-
<style>
|
|
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
|
-
}
|
|
648
|
-
body {
|
|
649
|
-
margin: 0; padding: 20px;
|
|
650
|
-
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
651
|
-
background: var(--bg-page);
|
|
652
|
-
color: var(--text-main);
|
|
653
|
-
display: flex; flex-direction: column; align-items: center; min-height: 100vh;
|
|
654
|
-
overflow-x: hidden;
|
|
655
|
-
transition: background 0.3s ease;
|
|
656
|
-
}
|
|
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; }
|
|
659
|
-
.badge {
|
|
660
|
-
font-size: 11px;
|
|
661
|
-
background: var(--badge-bg);
|
|
662
|
-
color: var(--badge-color);
|
|
663
|
-
padding: 4px 12px; border-radius: 9999px;
|
|
664
|
-
border: 1px solid var(--badge-border);
|
|
665
|
-
text-transform: uppercase; font-weight: 700; letter-spacing: 0.5px;
|
|
666
|
-
display: inline-block; margin-bottom: 6px;
|
|
667
|
-
}
|
|
668
|
-
.toolbar {
|
|
669
|
-
display: flex; gap: 8px; margin-bottom: 14px; align-items: center; flex-wrap: wrap; justify-content: center;
|
|
670
|
-
}
|
|
671
|
-
.tool-btn {
|
|
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
|
-
}
|
|
709
|
-
</style>
|
|
710
|
-
</head>
|
|
711
|
-
<body>
|
|
712
|
-
<div class="header">
|
|
713
|
-
<span class="badge">${badgeLabel}</span>
|
|
714
|
-
<h1>${artifact.title}</h1>
|
|
715
|
-
</div>
|
|
716
|
-
<div class="toolbar">
|
|
717
|
-
<button class="tool-btn" onclick="zoomIn()">🔍 Zoom +</button>
|
|
718
|
-
<button class="tool-btn" onclick="zoomOut()">🔍 Zoom -</button>
|
|
719
|
-
<button class="tool-btn" onclick="resetZoom()">⛶ Reset</button>
|
|
720
|
-
<button class="tool-btn" onclick="toggleTheme()" id="themeToggleBtn">🌙 Poster Dark</button>
|
|
721
|
-
<button class="tool-btn" onclick="copySource()">📋 Copy Code</button>
|
|
722
|
-
</div>
|
|
723
|
-
<div class="viewport" id="viewport">
|
|
724
|
-
<div class="canvas" id="canvas">
|
|
725
|
-
<pre class="mermaid" id="mermaidCode">
|
|
726
|
-
${artifact.content.trim()}
|
|
727
|
-
</pre>
|
|
728
|
-
</div>
|
|
729
|
-
<div class="help-hint">🖱️ Drag to pan · Scroll to zoom</div>
|
|
730
|
-
</div>
|
|
731
|
-
<script>
|
|
732
|
-
let scale = 1.0;
|
|
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
|
-
|
|
833
|
-
function zoomIn() {
|
|
834
|
-
scale = Math.min(scale * 1.25, 4.0);
|
|
835
|
-
updateTransform(true);
|
|
836
|
-
}
|
|
837
|
-
function zoomOut() {
|
|
838
|
-
scale = Math.max(scale * 0.8, 0.2);
|
|
839
|
-
updateTransform(true);
|
|
840
|
-
}
|
|
841
|
-
function resetZoom() {
|
|
842
|
-
scale = 1.0;
|
|
843
|
-
posX = 0;
|
|
844
|
-
posY = 0;
|
|
845
|
-
updateTransform(true);
|
|
846
|
-
}
|
|
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';
|
|
852
|
-
}
|
|
853
|
-
function copySource() {
|
|
854
|
-
const code = ${JSON.stringify(artifact.content.trim())};
|
|
855
|
-
navigator.clipboard.writeText(code).then(() => {
|
|
856
|
-
alert('Mind map source code copied to clipboard!');
|
|
857
|
-
});
|
|
858
|
-
}
|
|
859
|
-
|
|
860
|
-
mermaid.initialize({
|
|
861
|
-
startOnLoad: true,
|
|
862
|
-
theme: 'base',
|
|
863
|
-
securityLevel: 'loose',
|
|
864
|
-
themeVariables: {
|
|
865
|
-
darkMode: false,
|
|
866
|
-
background: '#ffffff',
|
|
867
|
-
primaryColor: '#6366f1',
|
|
868
|
-
primaryTextColor: '#0f172a',
|
|
869
|
-
primaryBorderColor: '#4f46e5',
|
|
870
|
-
lineColor: '#6366f1',
|
|
871
|
-
secondaryColor: '#0ea5e9',
|
|
872
|
-
tertiaryColor: '#10b981',
|
|
873
|
-
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
874
|
-
fontSize: '14px'
|
|
875
|
-
}
|
|
876
|
-
});
|
|
877
|
-
</script>
|
|
878
|
-
</body>
|
|
879
|
-
</html>`;
|
|
880
|
-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
881
|
-
res.end(htmlGraph);
|
|
882
|
-
}
|
|
259
|
+
const fullHtml = artifactManager.getArtifactHtml(artifact);
|
|
260
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
261
|
+
res.end(fullHtml);
|
|
883
262
|
return;
|
|
884
263
|
}
|
|
885
264
|
// Read Body for POST requests
|