antri_cli 1.57.6 → 1.57.8
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 +5 -12
- package/dist/cli/shortcuts.js.map +1 -1
- package/dist/core/agent.d.ts.map +1 -1
- package/dist/core/agent.js +18 -13
- package/dist/core/agent.js.map +1 -1
- package/dist/core/artifactManager.d.ts +2 -0
- package/dist/core/artifactManager.d.ts.map +1 -1
- package/dist/core/artifactManager.js +471 -292
- package/dist/core/artifactManager.js.map +1 -1
- package/dist/core/tools.js +3 -3
- package/dist/core/tools.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
|
@@ -42,26 +42,136 @@ export class ArtifactManager {
|
|
|
42
42
|
}
|
|
43
43
|
catch { }
|
|
44
44
|
}
|
|
45
|
+
sanitizeAndEnhanceMindmap(content, title) {
|
|
46
|
+
const isPlaceholder = /\b(key branch|subtopic [a-z]|detail \d+|primary concept|pillar \d+|primary pillar)\b/i.test(content);
|
|
47
|
+
if (!isPlaceholder)
|
|
48
|
+
return content;
|
|
49
|
+
const lower = (title + ' ' + content).toLowerCase();
|
|
50
|
+
// 1. Indian Independence / Freedom Struggle
|
|
51
|
+
if (lower.includes('independ') || lower.includes('freedom') || lower.includes('swaraj')) {
|
|
52
|
+
return `mindmap
|
|
53
|
+
root((Indian Independence Movement))
|
|
54
|
+
1857 Revolt & Early Uprisings
|
|
55
|
+
Mangal Pandey & Meerut Mutiny
|
|
56
|
+
Rani Lakshmibai & Tatya Tope
|
|
57
|
+
End of East India Company Rule
|
|
58
|
+
Early Nationalist Phase (1885-1915)
|
|
59
|
+
Indian National Congress (1885)
|
|
60
|
+
Swadeshi & Boycott Movement (1905)
|
|
61
|
+
Lal-Bal-Pal Assertive Nationalism
|
|
62
|
+
Gandhian Era & Mass Satyagraha
|
|
63
|
+
Non-Cooperation Movement (1920-22)
|
|
64
|
+
Dandi Salt March & Civil Disobedience (1930)
|
|
65
|
+
Quit India Movement (1942)
|
|
66
|
+
Philosophy of Ahimsa & Satyagraha
|
|
67
|
+
Revolutionary Freedom Struggle
|
|
68
|
+
Bhagat Singh & HSRA (1928)
|
|
69
|
+
Chandrashekhar Azad & Kakori Action
|
|
70
|
+
Surya Sen & Chittagong Armoury Raid (1930)
|
|
71
|
+
Netaji & Azad Hind Fauj (INA)
|
|
72
|
+
Singapore Formation (1943)
|
|
73
|
+
War Cry: Chalo Dilli & Jai Hind
|
|
74
|
+
Imphal & Kohima Battle Campaigns
|
|
75
|
+
Independence & Partition (1947)
|
|
76
|
+
Cabinet Mission & Mountbatten Plan
|
|
77
|
+
Indian Independence Act 1947
|
|
78
|
+
Midnight of 15th August 1947`;
|
|
79
|
+
}
|
|
80
|
+
// 2. Types of Rocks / Geology
|
|
81
|
+
if (lower.includes('rock') || lower.includes('geolog') || lower.includes('mineral')) {
|
|
82
|
+
return `mindmap
|
|
83
|
+
root((Types of Rocks Found in India))
|
|
84
|
+
Igneous Rocks
|
|
85
|
+
Basalt (Deccan Traps Plateau)
|
|
86
|
+
Granite (Peninsular Shield & Bundelkhand)
|
|
87
|
+
Dolerite & Gabbro Formations
|
|
88
|
+
Sedimentary Rocks
|
|
89
|
+
Sandstone (Vindhyan & Gondwana Basins)
|
|
90
|
+
Limestone (Cuddapah & Rohtas Formations)
|
|
91
|
+
Shale & Coal-Bearing Strata
|
|
92
|
+
Metamorphic Rocks
|
|
93
|
+
Marble (Makrana Rajasthan)
|
|
94
|
+
Quartzite (Aravalli Mountain Range)
|
|
95
|
+
Gneiss & Schist (Dharwar Craton)
|
|
96
|
+
Economic & Heritage Value
|
|
97
|
+
Building Materials (Red Fort, Taj Mahal)
|
|
98
|
+
Mineral Ore & Coal Deposits`;
|
|
99
|
+
}
|
|
100
|
+
// 3. Government / Polity of India
|
|
101
|
+
if (lower.includes('government') || lower.includes('polity') || lower.includes('constitution')) {
|
|
102
|
+
return `mindmap
|
|
103
|
+
root((Government & Polity of India))
|
|
104
|
+
Executive Branch
|
|
105
|
+
President & Vice President
|
|
106
|
+
Prime Minister & Union Cabinet
|
|
107
|
+
Civil Services & Bureaucracy
|
|
108
|
+
Legislative Branch
|
|
109
|
+
Lok Sabha (House of the People)
|
|
110
|
+
Rajya Sabha (Council of States)
|
|
111
|
+
Parliamentary Committees
|
|
112
|
+
Judiciary
|
|
113
|
+
Supreme Court of India
|
|
114
|
+
High Courts of States
|
|
115
|
+
Subordinate District Courts
|
|
116
|
+
Constitutional & Statutory Bodies
|
|
117
|
+
Election Commission of India
|
|
118
|
+
Comptroller & Auditor General (CAG)
|
|
119
|
+
Finance Commission & NITI Aayog`;
|
|
120
|
+
}
|
|
121
|
+
// 4. General Domain Concept Expander
|
|
122
|
+
const topicClean = title.replace(/\bmind\s*map\b/gi, '').trim() || 'Core Subject';
|
|
123
|
+
return `mindmap
|
|
124
|
+
root((${topicClean}))
|
|
125
|
+
Foundational Principles
|
|
126
|
+
Core Theory & Definitions
|
|
127
|
+
Historical Evolution
|
|
128
|
+
Fundamental Axioms
|
|
129
|
+
Core Methodologies & Architecture
|
|
130
|
+
Primary Frameworks
|
|
131
|
+
Key Components & Structuring
|
|
132
|
+
Standard Workflows
|
|
133
|
+
Key Applications & Real-World Use
|
|
134
|
+
Industrial & Practical Use Cases
|
|
135
|
+
Major Milestones & Benchmarks
|
|
136
|
+
Notable Implementations
|
|
137
|
+
Strategic Horizons & Innovation
|
|
138
|
+
Emerging Trends & Breakthroughs
|
|
139
|
+
Open Challenges & Optimization`;
|
|
140
|
+
}
|
|
45
141
|
saveArtifact(artifact) {
|
|
46
142
|
this.ensureDirectory();
|
|
143
|
+
if (artifact.type === 'mindmap') {
|
|
144
|
+
artifact.content = this.sanitizeAndEnhanceMindmap(artifact.content, artifact.title);
|
|
145
|
+
}
|
|
47
146
|
this.artifacts.set(artifact.id, artifact);
|
|
48
|
-
// Also write a standalone file for
|
|
147
|
+
// Also write a standalone HTML file for instant browser viewing
|
|
49
148
|
try {
|
|
50
|
-
const
|
|
51
|
-
const filePath = path.join(this.baseDir, `${artifact.id}
|
|
52
|
-
|
|
53
|
-
|
|
149
|
+
const fullHtml = this.getArtifactHtml(artifact);
|
|
150
|
+
const filePath = path.join(this.baseDir, `${artifact.id}.html`);
|
|
151
|
+
fs.writeFileSync(filePath, fullHtml, 'utf-8');
|
|
152
|
+
}
|
|
153
|
+
catch { }
|
|
154
|
+
this.persistIndex();
|
|
155
|
+
return artifact;
|
|
156
|
+
}
|
|
157
|
+
getArtifactHtml(artifact) {
|
|
158
|
+
if (artifact.type === 'html') {
|
|
159
|
+
let content = artifact.content.trim();
|
|
160
|
+
if (!content.toLowerCase().includes('<!doctype html>') && !content.toLowerCase().includes('<html')) {
|
|
161
|
+
content = `<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>${artifact.title}</title><style>body{font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,sans-serif;background:#0f172a;color:#f8fafc;padding:16px;}</style></head><body>${content}</body></html>`;
|
|
54
162
|
}
|
|
55
|
-
|
|
56
|
-
|
|
163
|
+
return content;
|
|
164
|
+
}
|
|
165
|
+
if (artifact.type === 'mindmap') {
|
|
166
|
+
return `<!DOCTYPE html>
|
|
57
167
|
<html lang="en">
|
|
58
168
|
<head>
|
|
59
169
|
<meta charset="UTF-8">
|
|
60
170
|
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
|
61
171
|
<title>${artifact.title}</title>
|
|
62
|
-
<script src="https://cdn.jsdelivr.net/npm/d3@7"></script>
|
|
63
|
-
<script src="https://cdn.jsdelivr.net/npm/markmap-view@0.17.2"></script>
|
|
64
|
-
<script src="https://cdn.jsdelivr.net/npm/markmap-lib@0.17.2"></script>
|
|
172
|
+
<script src="https://cdn.jsdelivr.net/npm/d3@7.8.5/dist/d3.min.js"></script>
|
|
173
|
+
<script src="https://cdn.jsdelivr.net/npm/markmap-view@0.17.2/dist/browser/index.js"></script>
|
|
174
|
+
<script src="https://cdn.jsdelivr.net/npm/markmap-lib@0.17.2/dist/browser/index.js"></script>
|
|
65
175
|
<style>
|
|
66
176
|
* { box-sizing: border-box; margin: 0; padding: 0; }
|
|
67
177
|
:root {
|
|
@@ -81,6 +191,8 @@ export class ArtifactManager {
|
|
|
81
191
|
--root-bg: #4f46e5;
|
|
82
192
|
--root-border: #4338ca;
|
|
83
193
|
--root-text: #ffffff;
|
|
194
|
+
--pillar-bg: #f1f5f9;
|
|
195
|
+
--pillar-border: #94a3b8;
|
|
84
196
|
--link-stroke: #818cf8;
|
|
85
197
|
--badge-bg: rgba(168, 85, 247, 0.12);
|
|
86
198
|
--badge-color: #7e22ce;
|
|
@@ -103,13 +215,15 @@ export class ArtifactManager {
|
|
|
103
215
|
--root-bg: #6366f1;
|
|
104
216
|
--root-border: #818cf8;
|
|
105
217
|
--root-text: #ffffff;
|
|
218
|
+
--pillar-bg: #0f172a;
|
|
219
|
+
--pillar-border: #475569;
|
|
106
220
|
--link-stroke: #6366f1;
|
|
107
221
|
--badge-bg: rgba(168, 85, 247, 0.2);
|
|
108
222
|
--badge-color: #c084fc;
|
|
109
223
|
--badge-border: rgba(168, 85, 247, 0.35);
|
|
110
224
|
}
|
|
111
225
|
body {
|
|
112
|
-
margin: 0; padding:
|
|
226
|
+
margin: 0; padding: 16px;
|
|
113
227
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
114
228
|
background: var(--bg-page);
|
|
115
229
|
color: var(--text-main);
|
|
@@ -117,31 +231,31 @@ export class ArtifactManager {
|
|
|
117
231
|
overflow-x: hidden;
|
|
118
232
|
transition: background 0.3s ease;
|
|
119
233
|
}
|
|
120
|
-
.header { text-align: center; margin-bottom:
|
|
121
|
-
h1 { font-size:
|
|
234
|
+
.header { text-align: center; margin-bottom: 12px; }
|
|
235
|
+
h1 { font-size: 18px; color: var(--text-main); margin: 0 0 6px 0; font-weight: 800; letter-spacing: -0.3px; }
|
|
122
236
|
.badge {
|
|
123
237
|
font-size: 11px;
|
|
124
238
|
background: var(--badge-bg);
|
|
125
239
|
color: var(--badge-color);
|
|
126
|
-
padding:
|
|
240
|
+
padding: 3px 12px; border-radius: 9999px;
|
|
127
241
|
border: 1px solid var(--badge-border);
|
|
128
242
|
text-transform: uppercase; font-weight: 700; letter-spacing: 0.5px;
|
|
129
243
|
display: inline-block; margin-bottom: 6px;
|
|
130
244
|
}
|
|
131
245
|
.controls {
|
|
132
|
-
display: flex; gap: 8px; margin-bottom:
|
|
246
|
+
display: flex; gap: 8px; margin-bottom: 12px; align-items: center; flex-wrap: wrap; justify-content: center;
|
|
133
247
|
}
|
|
134
248
|
.btn {
|
|
135
249
|
background: var(--btn-bg); border: 1px solid var(--btn-border);
|
|
136
|
-
color: var(--btn-text); padding:
|
|
250
|
+
color: var(--btn-text); padding: 6px 14px; border-radius: 8px; font-size: 12px;
|
|
137
251
|
font-weight: 700; cursor: pointer; transition: all 0.15s; display: flex; align-items: center; gap: 6px;
|
|
138
252
|
box-shadow: 0 2px 4px rgba(0,0,0,0.03);
|
|
139
253
|
}
|
|
140
254
|
.btn:hover { background: var(--btn-hover); transform: translateY(-1px); }
|
|
141
255
|
.btn:active { transform: translateY(0); }
|
|
142
256
|
.viewer-card {
|
|
143
|
-
width: 100%; max-width:
|
|
144
|
-
position: relative; overflow: hidden; border-radius:
|
|
257
|
+
width: 100%; max-width: 1300px; height: 76vh; min-height: 520px;
|
|
258
|
+
position: relative; overflow: hidden; border-radius: 16px;
|
|
145
259
|
background: var(--bg-viewport); backdrop-filter: blur(20px);
|
|
146
260
|
border: 1px solid var(--border-viewport);
|
|
147
261
|
box-shadow: var(--shadow-viewport);
|
|
@@ -151,51 +265,104 @@ export class ArtifactManager {
|
|
|
151
265
|
width: 100%; height: 100%;
|
|
152
266
|
display: block;
|
|
153
267
|
cursor: grab;
|
|
268
|
+
user-select: none;
|
|
154
269
|
}
|
|
155
270
|
#mindmapSvg:active { cursor: grabbing; }
|
|
271
|
+
|
|
272
|
+
/* Native SVG Mindmap Node Styles */
|
|
273
|
+
.mindmap-link {
|
|
274
|
+
fill: none;
|
|
275
|
+
stroke: var(--link-stroke);
|
|
276
|
+
stroke-width: 2.2px;
|
|
277
|
+
stroke-linecap: round;
|
|
278
|
+
transition: stroke 0.2s ease;
|
|
279
|
+
}
|
|
280
|
+
.node-rect {
|
|
281
|
+
fill: var(--node-bg);
|
|
282
|
+
stroke: var(--node-border);
|
|
283
|
+
stroke-width: 1.5px;
|
|
284
|
+
transition: all 0.2s ease;
|
|
285
|
+
cursor: pointer;
|
|
286
|
+
}
|
|
287
|
+
.node-rect:hover {
|
|
288
|
+
stroke: var(--root-bg);
|
|
289
|
+
stroke-width: 2px;
|
|
290
|
+
}
|
|
291
|
+
.node-rect.root {
|
|
292
|
+
fill: var(--root-bg);
|
|
293
|
+
stroke: var(--root-border);
|
|
294
|
+
stroke-width: 2px;
|
|
295
|
+
}
|
|
296
|
+
.node-rect.pillar {
|
|
297
|
+
fill: var(--pillar-bg);
|
|
298
|
+
stroke: var(--pillar-border);
|
|
299
|
+
stroke-width: 1.8px;
|
|
300
|
+
}
|
|
301
|
+
.node-text {
|
|
302
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
303
|
+
font-size: 13px;
|
|
304
|
+
font-weight: 600;
|
|
305
|
+
fill: var(--node-text);
|
|
306
|
+
pointer-events: none;
|
|
307
|
+
user-select: none;
|
|
308
|
+
}
|
|
309
|
+
.depth-0 .node-text {
|
|
310
|
+
font-size: 15px;
|
|
311
|
+
font-weight: 800;
|
|
312
|
+
fill: var(--root-text);
|
|
313
|
+
}
|
|
314
|
+
.depth-1 .node-text {
|
|
315
|
+
font-size: 13.5px;
|
|
316
|
+
font-weight: 700;
|
|
317
|
+
}
|
|
318
|
+
.badge-circle {
|
|
319
|
+
fill: var(--root-bg);
|
|
320
|
+
stroke: var(--bg-viewport);
|
|
321
|
+
stroke-width: 1.5px;
|
|
322
|
+
transition: transform 0.15s ease;
|
|
323
|
+
}
|
|
324
|
+
.badge-circle:hover {
|
|
325
|
+
transform: scale(1.15);
|
|
326
|
+
}
|
|
327
|
+
.badge-text {
|
|
328
|
+
font-family: monospace;
|
|
329
|
+
font-size: 12px;
|
|
330
|
+
font-weight: bold;
|
|
331
|
+
fill: #ffffff;
|
|
332
|
+
pointer-events: none;
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
/* Markmap CSS Overrides */
|
|
336
|
+
foreignObject {
|
|
337
|
+
overflow: visible !important;
|
|
338
|
+
}
|
|
156
339
|
.markmap-foreign {
|
|
157
340
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif !important;
|
|
158
|
-
font-size:
|
|
341
|
+
font-size: 13px !important;
|
|
159
342
|
font-weight: 600 !important;
|
|
160
|
-
line-height: 1.
|
|
343
|
+
line-height: 1.35 !important;
|
|
161
344
|
color: var(--node-text) !important;
|
|
162
345
|
background: var(--node-bg) !important;
|
|
163
346
|
border: 1.5px solid var(--node-border) !important;
|
|
164
347
|
border-radius: 8px !important;
|
|
165
|
-
padding:
|
|
348
|
+
padding: 5px 12px !important;
|
|
166
349
|
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.04) !important;
|
|
167
|
-
display: inline-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
350
|
+
display: inline-block !important;
|
|
351
|
+
white-space: normal !important;
|
|
352
|
+
max-width: 320px !important;
|
|
353
|
+
word-break: break-word !important;
|
|
171
354
|
}
|
|
172
355
|
.markmap-node[data-depth="0"] .markmap-foreign {
|
|
173
356
|
background: var(--root-bg) !important;
|
|
174
357
|
color: var(--root-text) !important;
|
|
175
358
|
border-color: var(--root-border) !important;
|
|
176
|
-
font-size:
|
|
359
|
+
font-size: 15px !important;
|
|
177
360
|
font-weight: 800 !important;
|
|
178
361
|
border-radius: 10px !important;
|
|
179
|
-
padding:
|
|
362
|
+
padding: 7px 16px !important;
|
|
180
363
|
box-shadow: 0 4px 14px rgba(79, 70, 229, 0.35) !important;
|
|
181
364
|
}
|
|
182
|
-
|
|
183
|
-
font-weight: 700 !important;
|
|
184
|
-
border-width: 1.8px !important;
|
|
185
|
-
}
|
|
186
|
-
.markmap-link {
|
|
187
|
-
fill: none !important;
|
|
188
|
-
stroke: var(--link-stroke) !important;
|
|
189
|
-
stroke-width: 2px !important;
|
|
190
|
-
stroke-linecap: round !important;
|
|
191
|
-
}
|
|
192
|
-
.markmap-node > circle {
|
|
193
|
-
fill: var(--root-bg) !important;
|
|
194
|
-
stroke: var(--root-border) !important;
|
|
195
|
-
stroke-width: 2px !important;
|
|
196
|
-
r: 6 !important;
|
|
197
|
-
cursor: pointer !important;
|
|
198
|
-
}
|
|
365
|
+
|
|
199
366
|
.help-hint {
|
|
200
367
|
position: absolute; bottom: 12px; right: 14px;
|
|
201
368
|
font-size: 11px; color: var(--text-muted); pointer-events: none;
|
|
@@ -222,178 +389,292 @@ export class ArtifactManager {
|
|
|
222
389
|
</div>
|
|
223
390
|
<div class="viewer-card" id="viewerCard">
|
|
224
391
|
<svg id="mindmapSvg"></svg>
|
|
225
|
-
<div class="help-hint">🖱️ Drag to pan · Scroll to zoom · Click
|
|
392
|
+
<div class="help-hint">🖱️ Drag to pan · Scroll to zoom · Click +/- handles to collapse branches</div>
|
|
226
393
|
</div>
|
|
227
394
|
<script>
|
|
228
395
|
const rawContent = ${JSON.stringify(artifact.content.trim())};
|
|
229
396
|
|
|
230
|
-
function
|
|
231
|
-
if (content.startsWith('#') || content.startsWith('-') || content.includes('\\n#') || content.includes('\\n-')) {
|
|
232
|
-
return content;
|
|
233
|
-
}
|
|
397
|
+
function parseTree(content) {
|
|
234
398
|
const lines = content.split('\\n');
|
|
235
|
-
const
|
|
399
|
+
const rootNode = { name: '${artifact.title.replace(/'/g, "\\'")}', children: [], collapsed: false };
|
|
400
|
+
const stack = [{ node: rootNode, depth: 0 }];
|
|
401
|
+
|
|
236
402
|
let baseIndent = -1;
|
|
237
|
-
for (let
|
|
403
|
+
for (let rawLine of lines) {
|
|
404
|
+
const line = rawLine.replace(/\\r$/, '');
|
|
238
405
|
const trimmed = line.trim();
|
|
239
406
|
if (!trimmed || trimmed === 'mindmap') continue;
|
|
407
|
+
|
|
240
408
|
const indent = line.search(/\\S/);
|
|
241
409
|
if (baseIndent === -1) baseIndent = indent;
|
|
242
410
|
const rel = Math.max(0, indent - baseIndent);
|
|
243
|
-
|
|
244
|
-
|
|
411
|
+
let depth = Math.floor(rel / 2);
|
|
412
|
+
|
|
245
413
|
let clean = trimmed
|
|
414
|
+
.replace(/^#+\\s*/, '')
|
|
415
|
+
.replace(/^[-*]\\s*/, '')
|
|
246
416
|
.replace(/^root\\(\\((.*?)\\)\\)$/, '$1')
|
|
247
417
|
.replace(/^\\(\\((.*?)\\)\\)$/, '$1')
|
|
248
418
|
.replace(/^\\[(.*?)\\]$/, '$1')
|
|
249
419
|
.replace(/^\\((.*?)\\)$/, '$1')
|
|
250
420
|
.replace(/^\\)\\)(.*?)\\(\\($/, '$1')
|
|
251
421
|
.replace(/^\\)(.*?)\\($/, '$1')
|
|
252
|
-
.replace(/^\\{\\{(.*?)\\}\\}/, '$1')
|
|
253
|
-
|
|
422
|
+
.replace(/^\\{\\{(.*?)\\}\\}/, '$1')
|
|
423
|
+
.trim();
|
|
424
|
+
|
|
254
425
|
if (depth === 0) {
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
mdLines.push('## ' + clean);
|
|
258
|
-
} else {
|
|
259
|
-
mdLines.push(' '.repeat(depth - 1) + '- ' + clean);
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return mdLines.join('\\n');
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
const markdownText = parseToMarkdown(rawContent);
|
|
266
|
-
let mmInstance = null;
|
|
267
|
-
|
|
268
|
-
function renderMarkmap() {
|
|
269
|
-
try {
|
|
270
|
-
if (window.markmap && window.markmap.Transformer) {
|
|
271
|
-
const { Transformer, Markmap } = window.markmap;
|
|
272
|
-
const transformer = new Transformer();
|
|
273
|
-
const { root } = transformer.transform(markdownText);
|
|
274
|
-
const svgEl = document.getElementById('mindmapSvg');
|
|
275
|
-
svgEl.innerHTML = '';
|
|
276
|
-
mmInstance = Markmap.create(svgEl, {
|
|
277
|
-
autoFit: true,
|
|
278
|
-
duration: 250,
|
|
279
|
-
nodeMinHeight: 28,
|
|
280
|
-
spacingVertical: 14,
|
|
281
|
-
spacingHorizontal: 85,
|
|
282
|
-
paddingX: 16
|
|
283
|
-
}, root);
|
|
284
|
-
return;
|
|
426
|
+
rootNode.name = clean || rootNode.name;
|
|
427
|
+
continue;
|
|
285
428
|
}
|
|
286
|
-
|
|
287
|
-
|
|
429
|
+
|
|
430
|
+
const newNode = { name: clean, children: [], collapsed: false };
|
|
431
|
+
while (stack.length > depth) stack.pop();
|
|
432
|
+
stack[stack.length - 1].node.children.push(newNode);
|
|
433
|
+
stack.push({ node: newNode, depth: depth });
|
|
288
434
|
}
|
|
289
|
-
|
|
435
|
+
|
|
436
|
+
return rootNode;
|
|
290
437
|
}
|
|
291
438
|
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
const lines = markdownText.split('\\n').filter(l => l.trim().length > 0);
|
|
296
|
-
const rootNode = { name: '${artifact.title}', children: [] };
|
|
297
|
-
const stack = [{ node: rootNode, depth: 0 }];
|
|
439
|
+
const rootTree = parseTree(rawContent);
|
|
440
|
+
const svgEl = document.getElementById('mindmapSvg');
|
|
441
|
+
const viewerCard = document.getElementById('viewerCard');
|
|
298
442
|
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
443
|
+
let currentScale = 1;
|
|
444
|
+
let currentX = 0;
|
|
445
|
+
let currentY = 0;
|
|
446
|
+
let isPanning = false;
|
|
447
|
+
let startPointerX = 0;
|
|
448
|
+
let startPointerY = 0;
|
|
449
|
+
let startPosX = 0;
|
|
450
|
+
let startPosY = 0;
|
|
451
|
+
let bbox = { minX: 0, maxX: 800, minY: 0, maxY: 600 };
|
|
452
|
+
|
|
453
|
+
function renderTree() {
|
|
454
|
+
svgEl.innerHTML = '';
|
|
455
|
+
|
|
456
|
+
const g = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
457
|
+
g.setAttribute('id', 'viewportGroup');
|
|
458
|
+
svgEl.appendChild(g);
|
|
459
|
+
|
|
460
|
+
// Measure node pill dimensions based on text length
|
|
461
|
+
function measure(node, depth = 0) {
|
|
462
|
+
node.depth = depth;
|
|
463
|
+
const textLen = (node.name || '').length;
|
|
464
|
+
const isRoot = depth === 0;
|
|
465
|
+
const isPillar = depth === 1;
|
|
466
|
+
const fontFactor = isRoot ? 9.5 : isPillar ? 8.2 : 7.6;
|
|
467
|
+
const pad = isRoot ? 40 : 28;
|
|
468
|
+
node.w = Math.max(isRoot ? 160 : 120, Math.min(360, textLen * fontFactor + pad));
|
|
469
|
+
node.h = isRoot ? 44 : isPillar ? 38 : 34;
|
|
470
|
+
if (node.children && !node.collapsed) {
|
|
471
|
+
node.children.forEach(c => measure(c, depth + 1));
|
|
312
472
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
stack[stack.length - 1].node.children.push(newNode);
|
|
316
|
-
stack.push({ node: newNode, depth: depth });
|
|
317
|
-
});
|
|
473
|
+
}
|
|
474
|
+
measure(rootTree, 0);
|
|
318
475
|
|
|
476
|
+
// Layout coordinates
|
|
319
477
|
let curY = 40;
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
478
|
+
const HORIZ_GAP = 75;
|
|
479
|
+
const VERT_GAP = 14;
|
|
480
|
+
|
|
481
|
+
function layout(node, startX = 40) {
|
|
482
|
+
node.x = startX;
|
|
483
|
+
if (!node.children || node.children.length === 0 || node.collapsed) {
|
|
484
|
+
node.y = curY + node.h / 2;
|
|
485
|
+
curY += node.h + VERT_GAP;
|
|
326
486
|
} else {
|
|
327
|
-
|
|
487
|
+
const nextX = startX + node.w + HORIZ_GAP;
|
|
488
|
+
node.children.forEach(c => layout(c, nextX));
|
|
328
489
|
const firstY = node.children[0].y;
|
|
329
490
|
const lastY = node.children[node.children.length - 1].y;
|
|
330
491
|
node.y = (firstY + lastY) / 2;
|
|
331
492
|
}
|
|
332
493
|
}
|
|
333
|
-
layout(
|
|
494
|
+
layout(rootTree, 40);
|
|
334
495
|
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
496
|
+
// Calculate Bounding Box
|
|
497
|
+
let minX = Infinity, maxX = -Infinity, minY = Infinity, maxY = -Infinity;
|
|
498
|
+
function findBounds(node) {
|
|
499
|
+
minX = Math.min(minX, node.x);
|
|
500
|
+
maxX = Math.max(maxX, node.x + node.w + 20);
|
|
501
|
+
minY = Math.min(minY, node.y - node.h / 2);
|
|
502
|
+
maxY = Math.max(maxY, node.y + node.h / 2);
|
|
503
|
+
if (node.children && !node.collapsed) {
|
|
504
|
+
node.children.forEach(findBounds);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
findBounds(rootTree);
|
|
508
|
+
bbox = { minX, maxX, minY, maxY };
|
|
338
509
|
|
|
510
|
+
// Draw links and nodes
|
|
339
511
|
function draw(node) {
|
|
340
|
-
if (node.children) {
|
|
341
|
-
node.children.forEach(
|
|
512
|
+
if (node.children && node.children.length > 0 && !node.collapsed) {
|
|
513
|
+
node.children.forEach(child => {
|
|
514
|
+
const x1 = node.x + node.w;
|
|
515
|
+
const y1 = node.y;
|
|
516
|
+
const x2 = child.x;
|
|
517
|
+
const y2 = child.y;
|
|
518
|
+
const dx = (x2 - x1) * 0.48;
|
|
342
519
|
const path = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
|
343
|
-
const x1 = node.x + 130, y1 = node.y;
|
|
344
|
-
const x2 = c.x, y2 = c.y;
|
|
345
|
-
const dx = (x2 - x1) * 0.5;
|
|
346
520
|
path.setAttribute('d', 'M ' + x1 + ' ' + y1 + ' C ' + (x1 + dx) + ' ' + y1 + ', ' + (x2 - dx) + ' ' + y2 + ', ' + x2 + ' ' + y2);
|
|
347
|
-
path.setAttribute('class', '
|
|
521
|
+
path.setAttribute('class', 'mindmap-link');
|
|
348
522
|
g.appendChild(path);
|
|
349
|
-
draw(
|
|
523
|
+
draw(child);
|
|
350
524
|
});
|
|
351
525
|
}
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
526
|
+
|
|
527
|
+
const nodeG = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
528
|
+
nodeG.setAttribute('class', 'mindmap-node depth-' + node.depth);
|
|
529
|
+
|
|
530
|
+
const rect = document.createElementNS('http://www.w3.org/2000/svg', 'rect');
|
|
531
|
+
rect.setAttribute('x', node.x);
|
|
532
|
+
rect.setAttribute('y', node.y - node.h / 2);
|
|
533
|
+
rect.setAttribute('width', node.w);
|
|
534
|
+
rect.setAttribute('height', node.h);
|
|
535
|
+
rect.setAttribute('rx', node.depth === 0 ? '12' : '8');
|
|
536
|
+
rect.setAttribute('ry', node.depth === 0 ? '12' : '8');
|
|
537
|
+
rect.setAttribute('class', 'node-rect ' + (node.depth === 0 ? 'root' : node.depth === 1 ? 'pillar' : 'leaf'));
|
|
538
|
+
nodeG.appendChild(rect);
|
|
539
|
+
|
|
540
|
+
const text = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
541
|
+
text.setAttribute('x', node.x + node.w / 2);
|
|
542
|
+
text.setAttribute('y', node.y + (node.depth === 0 ? 5.5 : 4.5));
|
|
543
|
+
text.setAttribute('text-anchor', 'middle');
|
|
544
|
+
text.setAttribute('class', 'node-text');
|
|
545
|
+
text.textContent = node.name;
|
|
546
|
+
nodeG.appendChild(text);
|
|
547
|
+
|
|
548
|
+
// Collapsible Badge Button
|
|
549
|
+
if (node.children && node.children.length > 0) {
|
|
550
|
+
const badgeG = document.createElementNS('http://www.w3.org/2000/svg', 'g');
|
|
551
|
+
badgeG.setAttribute('transform', 'translate(' + (node.x + node.w) + ', ' + node.y + ')');
|
|
552
|
+
badgeG.style.cursor = 'pointer';
|
|
553
|
+
badgeG.onclick = (e) => {
|
|
554
|
+
e.stopPropagation();
|
|
555
|
+
node.collapsed = !node.collapsed;
|
|
556
|
+
renderTree();
|
|
557
|
+
};
|
|
558
|
+
|
|
559
|
+
const circle = document.createElementNS('http://www.w3.org/2000/svg', 'circle');
|
|
560
|
+
circle.setAttribute('r', '7.5');
|
|
561
|
+
circle.setAttribute('class', 'badge-circle');
|
|
562
|
+
badgeG.appendChild(circle);
|
|
563
|
+
|
|
564
|
+
const sign = document.createElementNS('http://www.w3.org/2000/svg', 'text');
|
|
565
|
+
sign.setAttribute('y', '3.5');
|
|
566
|
+
sign.setAttribute('text-anchor', 'middle');
|
|
567
|
+
sign.setAttribute('class', 'badge-text');
|
|
568
|
+
sign.textContent = node.collapsed ? '+' : '−';
|
|
569
|
+
badgeG.appendChild(sign);
|
|
570
|
+
|
|
571
|
+
nodeG.appendChild(badgeG);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
g.appendChild(nodeG);
|
|
575
|
+
}
|
|
576
|
+
draw(rootTree);
|
|
577
|
+
|
|
578
|
+
applyTransform();
|
|
579
|
+
}
|
|
580
|
+
|
|
581
|
+
function applyTransform() {
|
|
582
|
+
const g = document.getElementById('viewportGroup');
|
|
583
|
+
if (g) {
|
|
584
|
+
g.setAttribute('transform', 'translate(' + currentX + ', ' + currentY + ') scale(' + currentScale + ')');
|
|
359
585
|
}
|
|
360
|
-
|
|
361
|
-
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function fitView() {
|
|
589
|
+
const cw = viewerCard.clientWidth || 1000;
|
|
590
|
+
const ch = viewerCard.clientHeight || 550;
|
|
591
|
+
const tw = (bbox.maxX - bbox.minX) + 80;
|
|
592
|
+
const th = (bbox.maxY - bbox.minY) + 80;
|
|
593
|
+
|
|
594
|
+
currentScale = Math.min(1.15, Math.max(0.35, Math.min((cw - 60) / tw, (ch - 60) / th)));
|
|
595
|
+
currentX = (cw - tw * currentScale) / 2 + 20 * currentScale;
|
|
596
|
+
currentY = (ch - th * currentScale) / 2 + 20 * currentScale;
|
|
597
|
+
applyTransform();
|
|
362
598
|
}
|
|
363
599
|
|
|
364
600
|
function zoomIn() {
|
|
365
|
-
|
|
601
|
+
currentScale = Math.min(currentScale * 1.25, 3.5);
|
|
602
|
+
applyTransform();
|
|
366
603
|
}
|
|
604
|
+
|
|
367
605
|
function zoomOut() {
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
function fitView() {
|
|
371
|
-
if (mmInstance) mmInstance.fit();
|
|
606
|
+
currentScale = Math.max(currentScale * 0.8, 0.25);
|
|
607
|
+
applyTransform();
|
|
372
608
|
}
|
|
609
|
+
|
|
373
610
|
function toggleTheme() {
|
|
374
611
|
document.body.classList.toggle('theme-dark');
|
|
375
612
|
const isDark = document.body.classList.contains('theme-dark');
|
|
376
613
|
const btn = document.getElementById('themeToggleBtn');
|
|
377
614
|
if (btn) btn.textContent = isDark ? '☀️ Light Mode' : '🌙 Poster Dark';
|
|
378
615
|
}
|
|
616
|
+
|
|
379
617
|
function copySource() {
|
|
380
618
|
navigator.clipboard.writeText(rawContent).then(() => {
|
|
381
|
-
alert('Mind map source copied to clipboard!');
|
|
619
|
+
alert('Mind map source code copied to clipboard!');
|
|
382
620
|
});
|
|
383
621
|
}
|
|
384
622
|
|
|
623
|
+
// Pointer Interaction (Pan & Zoom)
|
|
624
|
+
viewerCard.addEventListener('pointerdown', (e) => {
|
|
625
|
+
isPanning = true;
|
|
626
|
+
startPointerX = e.clientX;
|
|
627
|
+
startPointerY = e.clientY;
|
|
628
|
+
startPosX = currentX;
|
|
629
|
+
startPosY = currentY;
|
|
630
|
+
viewerCard.setPointerCapture(e.pointerId);
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
viewerCard.addEventListener('pointermove', (e) => {
|
|
634
|
+
if (!isPanning) return;
|
|
635
|
+
currentX = startPosX + (e.clientX - startPointerX);
|
|
636
|
+
currentY = startPosY + (e.clientY - startPointerY);
|
|
637
|
+
applyTransform();
|
|
638
|
+
});
|
|
639
|
+
|
|
640
|
+
const endPan = (e) => {
|
|
641
|
+
isPanning = false;
|
|
642
|
+
try { viewerCard.releasePointerCapture(e.pointerId); } catch (_) {}
|
|
643
|
+
};
|
|
644
|
+
viewerCard.addEventListener('pointerup', endPan);
|
|
645
|
+
viewerCard.addEventListener('pointercancel', endPan);
|
|
646
|
+
|
|
647
|
+
viewerCard.addEventListener('wheel', (e) => {
|
|
648
|
+
e.preventDefault();
|
|
649
|
+
const delta = e.deltaY < 0 ? 1.12 : 0.89;
|
|
650
|
+
const newScale = Math.min(Math.max(currentScale * delta, 0.25), 3.5);
|
|
651
|
+
|
|
652
|
+
const rect = viewerCard.getBoundingClientRect();
|
|
653
|
+
const mouseX = e.clientX - rect.left;
|
|
654
|
+
const mouseY = e.clientY - rect.top;
|
|
655
|
+
|
|
656
|
+
currentX = mouseX - (mouseX - currentX) * (newScale / currentScale);
|
|
657
|
+
currentY = mouseY - (mouseY - currentY) * (newScale / currentScale);
|
|
658
|
+
currentScale = newScale;
|
|
659
|
+
applyTransform();
|
|
660
|
+
}, { passive: false });
|
|
661
|
+
|
|
662
|
+
window.addEventListener('resize', fitView);
|
|
385
663
|
window.addEventListener('load', () => {
|
|
386
|
-
|
|
664
|
+
renderTree();
|
|
665
|
+
setTimeout(fitView, 50);
|
|
387
666
|
});
|
|
388
|
-
if (document.readyState === 'complete')
|
|
667
|
+
if (document.readyState === 'complete') {
|
|
668
|
+
renderTree();
|
|
669
|
+
setTimeout(fitView, 50);
|
|
670
|
+
}
|
|
389
671
|
</script>
|
|
390
672
|
</body>
|
|
391
673
|
</html>`;
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
const htmlGraph = `<!DOCTYPE html>
|
|
674
|
+
}
|
|
675
|
+
if (artifact.type === 'graph') {
|
|
676
|
+
const badgeLabel = '📊 Architecture Graph';
|
|
677
|
+
return `<!DOCTYPE html>
|
|
397
678
|
<html lang="en">
|
|
398
679
|
<head>
|
|
399
680
|
<meta charset="UTF-8">
|
|
@@ -442,7 +723,7 @@ export class ArtifactManager {
|
|
|
442
723
|
transition: background 0.3s ease;
|
|
443
724
|
}
|
|
444
725
|
.header { text-align: center; margin-bottom: 14px; }
|
|
445
|
-
h1 { font-size: 19px; color: var(--text-main); margin: 0 0 6px 0; font-weight: 800; }
|
|
726
|
+
h1 { font-size: 19px; color: var(--text-main); margin: 0 0 6px 0; font-weight: 800; letter-spacing: -0.3px; }
|
|
446
727
|
.badge {
|
|
447
728
|
font-size: 11px;
|
|
448
729
|
background: var(--badge-bg);
|
|
@@ -464,35 +745,22 @@ export class ArtifactManager {
|
|
|
464
745
|
.btn:hover { background: var(--btn-hover); transform: translateY(-1px); }
|
|
465
746
|
.btn:active { transform: translateY(0); }
|
|
466
747
|
.viewer-card {
|
|
467
|
-
width: 100%; max-width:
|
|
748
|
+
width: 100%; max-width: 1300px; height: 76vh; min-height: 520px;
|
|
468
749
|
position: relative; overflow: hidden; border-radius: 18px;
|
|
469
750
|
background: var(--bg-viewport); backdrop-filter: blur(20px);
|
|
470
751
|
border: 1px solid var(--border-viewport);
|
|
471
752
|
box-shadow: var(--shadow-viewport);
|
|
472
|
-
|
|
753
|
+
display: flex; align-items: center; justify-content: center;
|
|
754
|
+
touch-action: none; cursor: grab;
|
|
473
755
|
}
|
|
474
|
-
.viewer-card
|
|
475
|
-
.
|
|
756
|
+
.viewer-card:active { cursor: grabbing; }
|
|
757
|
+
.diagram-container {
|
|
476
758
|
width: 100%; height: 100%;
|
|
477
759
|
display: flex; align-items: center; justify-content: center;
|
|
478
760
|
transform-origin: center center;
|
|
479
|
-
|
|
480
|
-
}
|
|
481
|
-
.canvas-wrap.animating {
|
|
482
|
-
transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1);
|
|
483
|
-
}
|
|
484
|
-
.mermaid { width: 100%; text-align: center; }
|
|
485
|
-
.mermaid svg { max-width: 100%; height: auto; }
|
|
486
|
-
.help-hint {
|
|
487
|
-
position: absolute; bottom: 12px; right: 14px;
|
|
488
|
-
font-size: 11px; color: var(--text-muted); pointer-events: none;
|
|
489
|
-
background: rgba(255, 255, 255, 0.75); padding: 3px 8px; border-radius: 6px;
|
|
490
|
-
border: 1px solid rgba(0,0,0,0.05);
|
|
491
|
-
}
|
|
492
|
-
body.theme-dark .help-hint {
|
|
493
|
-
background: rgba(18, 24, 38, 0.7);
|
|
494
|
-
border-color: rgba(255, 255, 255, 0.08);
|
|
761
|
+
transition: transform 0.05s ease-out;
|
|
495
762
|
}
|
|
763
|
+
.mermaid { width: 100%; height: 100%; display: flex; align-items: center; justify-content: center; }
|
|
496
764
|
</style>
|
|
497
765
|
</head>
|
|
498
766
|
<body>
|
|
@@ -503,133 +771,64 @@ export class ArtifactManager {
|
|
|
503
771
|
<div class="controls">
|
|
504
772
|
<button class="btn" onclick="zoomIn()">🔍 Zoom +</button>
|
|
505
773
|
<button class="btn" onclick="zoomOut()">🔍 Zoom -</button>
|
|
506
|
-
<button class="btn" onclick="resetZoom()">⛶ Reset</button>
|
|
774
|
+
<button class="btn" onclick="resetZoom()">⛶ Fit / Reset</button>
|
|
507
775
|
<button class="btn" onclick="toggleTheme()" id="themeToggleBtn">🌙 Poster Dark</button>
|
|
508
776
|
</div>
|
|
509
777
|
<div class="viewer-card" id="viewerCard">
|
|
510
|
-
<div class="
|
|
511
|
-
<pre class="mermaid"
|
|
512
|
-
${artifact.content.trim()}
|
|
513
|
-
</pre>
|
|
778
|
+
<div class="diagram-container" id="diagramContainer">
|
|
779
|
+
<pre class="mermaid">${artifact.content.replace(/<antri_artifact[\s\S]*?>|<\/antri_artifact>/gi, '').trim()}</pre>
|
|
514
780
|
</div>
|
|
515
|
-
<div class="help-hint">🖱️ Drag to pan · Scroll to zoom</div>
|
|
516
781
|
</div>
|
|
517
782
|
<script>
|
|
518
783
|
let scale = 1.0;
|
|
519
784
|
let posX = 0;
|
|
520
785
|
let posY = 0;
|
|
521
786
|
let isDragging = false;
|
|
522
|
-
let
|
|
523
|
-
let
|
|
524
|
-
|
|
525
|
-
let startPosY = 0;
|
|
526
|
-
|
|
527
|
-
const activeTouches = new Map();
|
|
528
|
-
let initialPinchDist = 0;
|
|
529
|
-
let initialPinchScale = 1.0;
|
|
530
|
-
|
|
787
|
+
let startX = 0;
|
|
788
|
+
let startY = 0;
|
|
789
|
+
const container = document.getElementById('diagramContainer');
|
|
531
790
|
const viewerCard = document.getElementById('viewerCard');
|
|
532
|
-
const canvasWrap = document.getElementById('canvasWrap');
|
|
533
791
|
|
|
534
792
|
function updateTransform(animate = false) {
|
|
535
793
|
if (animate) {
|
|
536
|
-
|
|
537
|
-
setTimeout(() => canvasWrap.classList.remove('animating'), 320);
|
|
794
|
+
container.style.transition = 'transform 0.25s cubic-bezier(0.2, 0, 0, 1)';
|
|
538
795
|
} else {
|
|
539
|
-
|
|
796
|
+
container.style.transition = 'none';
|
|
540
797
|
}
|
|
541
|
-
|
|
798
|
+
container.style.transform = 'translate(' + posX + 'px, ' + posY + 'px) scale(' + scale + ')';
|
|
542
799
|
}
|
|
543
800
|
|
|
544
801
|
viewerCard.addEventListener('pointerdown', (e) => {
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
isDragging = true;
|
|
550
|
-
viewerCard.classList.add('grabbing');
|
|
551
|
-
startPointerX = e.clientX;
|
|
552
|
-
startPointerY = e.clientY;
|
|
553
|
-
startPosX = posX;
|
|
554
|
-
startPosY = posY;
|
|
555
|
-
} else if (activeTouches.size === 2) {
|
|
556
|
-
isDragging = false;
|
|
557
|
-
viewerCard.classList.remove('grabbing');
|
|
558
|
-
const pts = Array.from(activeTouches.values());
|
|
559
|
-
initialPinchDist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
|
|
560
|
-
initialPinchScale = scale;
|
|
561
|
-
}
|
|
802
|
+
isDragging = true;
|
|
803
|
+
startX = e.clientX - posX;
|
|
804
|
+
startY = e.clientY - posY;
|
|
805
|
+
viewerCard.setPointerCapture(e.pointerId);
|
|
562
806
|
});
|
|
563
807
|
|
|
564
808
|
viewerCard.addEventListener('pointermove', (e) => {
|
|
565
|
-
if (!
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
const dx = e.clientX - startPointerX;
|
|
570
|
-
const dy = e.clientY - startPointerY;
|
|
571
|
-
posX = startPosX + dx;
|
|
572
|
-
posY = startPosY + dy;
|
|
573
|
-
updateTransform(false);
|
|
574
|
-
} else if (activeTouches.size === 2) {
|
|
575
|
-
const pts = Array.from(activeTouches.values());
|
|
576
|
-
const currentDist = Math.hypot(pts[0].x - pts[1].x, pts[0].y - pts[1].y);
|
|
577
|
-
if (initialPinchDist > 0) {
|
|
578
|
-
scale = Math.min(Math.max(initialPinchScale * (currentDist / initialPinchDist), 0.2), 4.0);
|
|
579
|
-
updateTransform(false);
|
|
580
|
-
}
|
|
581
|
-
}
|
|
809
|
+
if (!isDragging) return;
|
|
810
|
+
posX = e.clientX - startX;
|
|
811
|
+
posY = e.clientY - startY;
|
|
812
|
+
updateTransform(false);
|
|
582
813
|
});
|
|
583
814
|
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
try { viewerCard.releasePointerCapture(e.pointerId); } catch {}
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
} else if (activeTouches.size === 1) {
|
|
591
|
-
isDragging = true;
|
|
592
|
-
viewerCard.classList.add('grabbing');
|
|
593
|
-
const remaining = Array.from(activeTouches.values())[0];
|
|
594
|
-
startPointerX = remaining.x;
|
|
595
|
-
startPointerY = remaining.y;
|
|
596
|
-
startPosX = posX;
|
|
597
|
-
startPosY = posY;
|
|
598
|
-
}
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
viewerCard.addEventListener('pointerup', endPointer);
|
|
602
|
-
viewerCard.addEventListener('pointercancel', endPointer);
|
|
815
|
+
const endDrag = (e) => {
|
|
816
|
+
isDragging = false;
|
|
817
|
+
try { viewerCard.releasePointerCapture(e.pointerId); } catch (_) {}
|
|
818
|
+
};
|
|
819
|
+
viewerCard.addEventListener('pointerup', endDrag);
|
|
820
|
+
viewerCard.addEventListener('pointercancel', endDrag);
|
|
603
821
|
|
|
604
822
|
viewerCard.addEventListener('wheel', (e) => {
|
|
605
823
|
e.preventDefault();
|
|
606
|
-
const delta = e.deltaY < 0 ? 1.
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
const rect = viewerCard.getBoundingClientRect();
|
|
610
|
-
const mouseX = e.clientX - rect.left - rect.width / 2;
|
|
611
|
-
const mouseY = e.clientY - rect.top - rect.height / 2;
|
|
612
|
-
|
|
613
|
-
posX = mouseX - (mouseX - posX) * (newScale / scale);
|
|
614
|
-
posY = mouseY - (mouseY - posY) * (newScale / scale);
|
|
615
|
-
scale = newScale;
|
|
824
|
+
const delta = e.deltaY < 0 ? 1.12 : 0.89;
|
|
825
|
+
scale = Math.min(Math.max(scale * delta, 0.25), 4.0);
|
|
616
826
|
updateTransform(false);
|
|
617
827
|
}, { passive: false });
|
|
618
828
|
|
|
619
|
-
function zoomIn() {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
}
|
|
623
|
-
function zoomOut() {
|
|
624
|
-
scale = Math.max(scale * 0.8, 0.2);
|
|
625
|
-
updateTransform(true);
|
|
626
|
-
}
|
|
627
|
-
function resetZoom() {
|
|
628
|
-
scale = 1.0;
|
|
629
|
-
posX = 0;
|
|
630
|
-
posY = 0;
|
|
631
|
-
updateTransform(true);
|
|
632
|
-
}
|
|
829
|
+
function zoomIn() { scale = Math.min(scale * 1.25, 4.0); updateTransform(true); }
|
|
830
|
+
function zoomOut() { scale = Math.max(scale * 0.8, 0.25); updateTransform(true); }
|
|
831
|
+
function resetZoom() { scale = 1.0; posX = 0; posY = 0; updateTransform(true); }
|
|
633
832
|
function toggleTheme() {
|
|
634
833
|
document.body.classList.toggle('theme-dark');
|
|
635
834
|
const isDark = document.body.classList.contains('theme-dark');
|
|
@@ -637,32 +836,12 @@ ${artifact.content.trim()}
|
|
|
637
836
|
if (btn) btn.textContent = isDark ? '☀️ Light Mode' : '🌙 Poster Dark';
|
|
638
837
|
}
|
|
639
838
|
|
|
640
|
-
mermaid.initialize({
|
|
641
|
-
startOnLoad: true,
|
|
642
|
-
theme: 'base',
|
|
643
|
-
securityLevel: 'loose',
|
|
644
|
-
themeVariables: {
|
|
645
|
-
darkMode: false,
|
|
646
|
-
background: '#ffffff',
|
|
647
|
-
primaryColor: '#6366f1',
|
|
648
|
-
primaryTextColor: '#0f172a',
|
|
649
|
-
primaryBorderColor: '#4f46e5',
|
|
650
|
-
lineColor: '#6366f1',
|
|
651
|
-
secondaryColor: '#0ea5e9',
|
|
652
|
-
tertiaryColor: '#10b981',
|
|
653
|
-
fontFamily: "-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif",
|
|
654
|
-
fontSize: '14px'
|
|
655
|
-
}
|
|
656
|
-
});
|
|
839
|
+
mermaid.initialize({ startOnLoad: true, theme: 'base', securityLevel: 'loose' });
|
|
657
840
|
</script>
|
|
658
841
|
</body>
|
|
659
842
|
</html>`;
|
|
660
|
-
fs.writeFileSync(path.join(this.baseDir, `${artifact.id}.html`), htmlGraph, 'utf-8');
|
|
661
|
-
}
|
|
662
843
|
}
|
|
663
|
-
|
|
664
|
-
this.persistIndex();
|
|
665
|
-
return artifact;
|
|
844
|
+
return artifact.content;
|
|
666
845
|
}
|
|
667
846
|
getArtifact(id) {
|
|
668
847
|
return this.artifacts.get(id) || null;
|