apero-kit-cli 1.4.2 ā 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/commands/help.js +9 -37
- package/src/utils/paths.js +64 -4
- package/templates/scripts/plan-preview.cjs +271 -57
package/package.json
CHANGED
package/src/commands/help.js
CHANGED
|
@@ -1,11 +1,5 @@
|
|
|
1
|
-
import http from 'http';
|
|
2
|
-
import { join } from 'path';
|
|
3
1
|
import { exec } from 'child_process';
|
|
4
|
-
import fs from 'fs-extra';
|
|
5
2
|
import chalk from 'chalk';
|
|
6
|
-
import { CLI_ROOT, TEMPLATES_DIR, resolveSource } from '../utils/paths.js';
|
|
7
|
-
|
|
8
|
-
const PORT = 3457;
|
|
9
3
|
|
|
10
4
|
// Bilingual content
|
|
11
5
|
const i18n = {
|
|
@@ -1403,38 +1397,16 @@ function generateWorkflowsSection(t, lang) {
|
|
|
1403
1397
|
`;
|
|
1404
1398
|
}
|
|
1405
1399
|
|
|
1400
|
+
const HELP_URL = 'https://www.vividkit.dev/vi/guides/what-is-claudekit';
|
|
1401
|
+
|
|
1406
1402
|
/**
|
|
1407
|
-
* Help command - open
|
|
1403
|
+
* Help command - open VividKit documentation in browser
|
|
1408
1404
|
*/
|
|
1409
1405
|
export async function helpCommand(options) {
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
const section = url.searchParams.get('section') || 'overview';
|
|
1417
|
-
const lang = url.searchParams.get('lang') || 'vi';
|
|
1418
|
-
|
|
1419
|
-
const html = generateHelpPage(section, lang, source);
|
|
1420
|
-
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
1421
|
-
res.end(html);
|
|
1422
|
-
});
|
|
1423
|
-
|
|
1424
|
-
server.listen(PORT, () => {
|
|
1425
|
-
const url = `http://localhost:${PORT}`;
|
|
1426
|
-
console.log(chalk.green(` Help server running at: ${url}`));
|
|
1427
|
-
console.log(chalk.gray(' Press Ctrl+C to stop\n'));
|
|
1428
|
-
|
|
1429
|
-
// Open browser
|
|
1430
|
-
const openCommand = process.platform === 'darwin' ? 'open' :
|
|
1431
|
-
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
1432
|
-
exec(`${openCommand} ${url}`);
|
|
1433
|
-
});
|
|
1434
|
-
|
|
1435
|
-
// Handle shutdown
|
|
1436
|
-
process.on('SIGINT', () => {
|
|
1437
|
-
console.log(chalk.yellow('\nš Help server stopped'));
|
|
1438
|
-
process.exit(0);
|
|
1439
|
-
});
|
|
1406
|
+
console.log(chalk.cyan('\nš Opening VividKit documentation...\n'));
|
|
1407
|
+
console.log(chalk.green(` ${HELP_URL}\n`));
|
|
1408
|
+
|
|
1409
|
+
const openCommand = process.platform === 'darwin' ? 'open' :
|
|
1410
|
+
process.platform === 'win32' ? 'start' : 'xdg-open';
|
|
1411
|
+
exec(`${openCommand} ${HELP_URL}`);
|
|
1440
1412
|
}
|
package/src/utils/paths.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { fileURLToPath } from 'url';
|
|
2
2
|
import { dirname, join, resolve } from 'path';
|
|
3
|
-
import { existsSync, statSync } from 'fs';
|
|
3
|
+
import { existsSync, statSync, mkdirSync } from 'fs';
|
|
4
4
|
import { execSync } from 'child_process';
|
|
5
|
+
import { homedir } from 'os';
|
|
5
6
|
|
|
6
7
|
const __filename = fileURLToPath(import.meta.url);
|
|
7
8
|
const __dirname = dirname(__filename);
|
|
@@ -12,6 +13,10 @@ export const CLI_ROOT = resolve(__dirname, '../..');
|
|
|
12
13
|
// Embedded templates directory (inside CLI package)
|
|
13
14
|
export const TEMPLATES_DIR = join(CLI_ROOT, 'templates');
|
|
14
15
|
|
|
16
|
+
// Remote templates config
|
|
17
|
+
const REMOTE_REPO_URL = 'https://github.com/Thanhnguyen6702/CK-Internal.git';
|
|
18
|
+
const CACHE_DIR = join(homedir(), '.apero-kit', 'CK-Internal');
|
|
19
|
+
|
|
15
20
|
// Target folder mappings
|
|
16
21
|
export const TARGETS = {
|
|
17
22
|
claude: '.claude',
|
|
@@ -19,6 +24,55 @@ export const TARGETS = {
|
|
|
19
24
|
generic: '.agent'
|
|
20
25
|
};
|
|
21
26
|
|
|
27
|
+
/**
|
|
28
|
+
* Fetch or update remote templates from CK-Internal GitHub repo.
|
|
29
|
+
* Clones on first run, pulls on subsequent runs.
|
|
30
|
+
* Returns source object or null on failure.
|
|
31
|
+
*/
|
|
32
|
+
export function fetchRemoteTemplates() {
|
|
33
|
+
try {
|
|
34
|
+
const cacheParent = join(homedir(), '.apero-kit');
|
|
35
|
+
if (!existsSync(cacheParent)) {
|
|
36
|
+
mkdirSync(cacheParent, { recursive: true });
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (existsSync(join(CACHE_DIR, '.git'))) {
|
|
40
|
+
// Already cloned ā pull latest
|
|
41
|
+
try {
|
|
42
|
+
execSync('git pull --ff-only', {
|
|
43
|
+
cwd: CACHE_DIR,
|
|
44
|
+
encoding: 'utf-8',
|
|
45
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
46
|
+
timeout: 30000
|
|
47
|
+
});
|
|
48
|
+
} catch {
|
|
49
|
+
// pull failed (offline, etc.) ā use cached version
|
|
50
|
+
}
|
|
51
|
+
} else {
|
|
52
|
+
// First time ā clone
|
|
53
|
+
execSync(`git clone --depth 1 "${REMOTE_REPO_URL}" "${CACHE_DIR}"`, {
|
|
54
|
+
encoding: 'utf-8',
|
|
55
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
56
|
+
timeout: 60000
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const claudeDir = join(CACHE_DIR, '.claude');
|
|
61
|
+
if (existsSync(claudeDir) && statSync(claudeDir).isDirectory()) {
|
|
62
|
+
const agentsMd = join(CACHE_DIR, 'AGENTS.md');
|
|
63
|
+
return {
|
|
64
|
+
path: CACHE_DIR,
|
|
65
|
+
type: 'remote',
|
|
66
|
+
claudeDir,
|
|
67
|
+
agentsMd: existsSync(agentsMd) ? agentsMd : null
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
} catch {
|
|
71
|
+
// Clone failed (no network, etc.) ā fall through
|
|
72
|
+
}
|
|
73
|
+
return null;
|
|
74
|
+
}
|
|
75
|
+
|
|
22
76
|
/**
|
|
23
77
|
* Get embedded templates (bundled with CLI)
|
|
24
78
|
*/
|
|
@@ -177,19 +231,25 @@ export function resolveSource(sourceFlag) {
|
|
|
177
231
|
return { error: `No .claude/ or .opencode/ found in: ${sourceFlag}` };
|
|
178
232
|
}
|
|
179
233
|
|
|
180
|
-
// 2.
|
|
234
|
+
// 2. Fetch from remote CK-Internal repo - PREFERRED
|
|
235
|
+
const remote = fetchRemoteTemplates();
|
|
236
|
+
if (remote) {
|
|
237
|
+
return remote;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// 3. Use embedded templates (bundled with CLI) - FALLBACK
|
|
181
241
|
const embedded = getEmbeddedTemplates();
|
|
182
242
|
if (embedded) {
|
|
183
243
|
return embedded;
|
|
184
244
|
}
|
|
185
245
|
|
|
186
|
-
//
|
|
246
|
+
// 4. Fallback: auto-detect in parent directories
|
|
187
247
|
const found = findSource();
|
|
188
248
|
if (found) {
|
|
189
249
|
return found;
|
|
190
250
|
}
|
|
191
251
|
|
|
192
252
|
return {
|
|
193
|
-
error: 'No templates found.
|
|
253
|
+
error: 'No templates found. Check your network connection or try reinstalling: npm install -g apero-kit-cli'
|
|
194
254
|
};
|
|
195
255
|
}
|
|
@@ -33,44 +33,13 @@ if (!fs.existsSync(fullPlanPath)) {
|
|
|
33
33
|
process.exit(1);
|
|
34
34
|
}
|
|
35
35
|
|
|
36
|
-
//
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
})
|
|
44
|
-
// Inline code
|
|
45
|
-
.replace(/`([^`]+)`/g, '<code class="inline">$1</code>')
|
|
46
|
-
// Headers
|
|
47
|
-
.replace(/^### (.*$)/gm, '<h3>$1</h3>')
|
|
48
|
-
.replace(/^## (.*$)/gm, '<h2>$1</h2>')
|
|
49
|
-
.replace(/^# (.*$)/gm, '<h1>$1</h1>')
|
|
50
|
-
// Bold and italic
|
|
51
|
-
.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
52
|
-
.replace(/\*([^*]+)\*/g, '<em>$1</em>')
|
|
53
|
-
// Links
|
|
54
|
-
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2" target="_blank">$1</a>')
|
|
55
|
-
// Lists
|
|
56
|
-
.replace(/^\s*[-*] (.*$)/gm, '<li>$1</li>')
|
|
57
|
-
.replace(/(<li>.*<\/li>)\n(?!<li>)/g, '$1</ul>\n')
|
|
58
|
-
.replace(/(?<!<\/li>\n)(<li>)/g, '<ul>$1')
|
|
59
|
-
// Numbered lists
|
|
60
|
-
.replace(/^\s*\d+\. (.*$)/gm, '<li class="numbered">$1</li>')
|
|
61
|
-
// Blockquotes
|
|
62
|
-
.replace(/^> (.*$)/gm, '<blockquote>$1</blockquote>')
|
|
63
|
-
// Horizontal rules
|
|
64
|
-
.replace(/^---+$/gm, '<hr>')
|
|
65
|
-
// Paragraphs
|
|
66
|
-
.replace(/\n\n/g, '</p><p>')
|
|
67
|
-
// Tables (simple)
|
|
68
|
-
.replace(/\|(.+)\|/g, (match) => {
|
|
69
|
-
const cells = match.split('|').filter(c => c.trim());
|
|
70
|
-
if (cells.every(c => /^[-:]+$/.test(c.trim()))) return ''; // Skip separator
|
|
71
|
-
const tag = 'td';
|
|
72
|
-
return '<tr>' + cells.map(c => `<${tag}>${c.trim()}</${tag}>`).join('') + '</tr>';
|
|
73
|
-
});
|
|
36
|
+
// Markdown to HTML - Uses marked.js on client-side for full GFM support
|
|
37
|
+
// Server just passes raw markdown, client renders with marked.js
|
|
38
|
+
function markdownToHtml(md, forEditor = false) {
|
|
39
|
+
// For editor preview pane, we'll render client-side with marked.js
|
|
40
|
+
// For static preview, we embed raw markdown and render on page load
|
|
41
|
+
const encodedMd = encodeURIComponent(md);
|
|
42
|
+
return `<div class="markdown-body" data-markdown="${encodedMd}"><noscript>${escapeHtml(md)}</noscript><div class="loading">Loading...</div></div>`;
|
|
74
43
|
}
|
|
75
44
|
|
|
76
45
|
function escapeHtml(text) {
|
|
@@ -134,6 +103,22 @@ function generatePage(files, currentFile, mode = 'preview') {
|
|
|
134
103
|
<meta charset="UTF-8">
|
|
135
104
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
136
105
|
<title>Plan Preview - ${path.basename(fullPlanPath)}</title>
|
|
106
|
+
|
|
107
|
+
<!-- marked.js - Markdown Parser -->
|
|
108
|
+
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
|
|
109
|
+
|
|
110
|
+
<!-- Prism.js - Syntax Highlighting -->
|
|
111
|
+
<link href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css" rel="stylesheet" />
|
|
112
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
|
|
113
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-javascript.min.js"></script>
|
|
114
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-typescript.min.js"></script>
|
|
115
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-python.min.js"></script>
|
|
116
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-bash.min.js"></script>
|
|
117
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-json.min.js"></script>
|
|
118
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-yaml.min.js"></script>
|
|
119
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-css.min.js"></script>
|
|
120
|
+
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-markdown.min.js"></script>
|
|
121
|
+
|
|
137
122
|
<style>
|
|
138
123
|
:root {
|
|
139
124
|
--bg: #0d1117;
|
|
@@ -467,6 +452,171 @@ function generatePage(files, currentFile, mode = 'preview') {
|
|
|
467
452
|
justify-content: space-between;
|
|
468
453
|
}
|
|
469
454
|
|
|
455
|
+
/* GitHub-style Markdown Body */
|
|
456
|
+
.markdown-body {
|
|
457
|
+
line-height: 1.7;
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
.markdown-body .loading {
|
|
461
|
+
color: var(--text-muted);
|
|
462
|
+
font-style: italic;
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
.markdown-body h1 {
|
|
466
|
+
font-size: 2em;
|
|
467
|
+
border-bottom: 1px solid var(--border);
|
|
468
|
+
padding-bottom: 0.3em;
|
|
469
|
+
margin: 24px 0 16px;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
.markdown-body h2 {
|
|
473
|
+
font-size: 1.5em;
|
|
474
|
+
border-bottom: 1px solid var(--border);
|
|
475
|
+
padding-bottom: 0.3em;
|
|
476
|
+
margin: 24px 0 16px;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
.markdown-body h3 {
|
|
480
|
+
font-size: 1.25em;
|
|
481
|
+
margin: 24px 0 16px;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
.markdown-body h4 {
|
|
485
|
+
font-size: 1em;
|
|
486
|
+
margin: 24px 0 16px;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
.markdown-body p {
|
|
490
|
+
margin: 0 0 16px;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
.markdown-body ul, .markdown-body ol {
|
|
494
|
+
margin: 0 0 16px;
|
|
495
|
+
padding-left: 2em;
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
.markdown-body li {
|
|
499
|
+
margin: 4px 0;
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
.markdown-body li > p {
|
|
503
|
+
margin: 0;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/* Task Lists */
|
|
507
|
+
.markdown-body input[type="checkbox"] {
|
|
508
|
+
margin-right: 8px;
|
|
509
|
+
vertical-align: middle;
|
|
510
|
+
}
|
|
511
|
+
|
|
512
|
+
.markdown-body li.task-list-item {
|
|
513
|
+
list-style: none;
|
|
514
|
+
margin-left: -1.5em;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/* Tables - GitHub style */
|
|
518
|
+
.markdown-body table {
|
|
519
|
+
width: 100%;
|
|
520
|
+
border-collapse: collapse;
|
|
521
|
+
margin: 16px 0;
|
|
522
|
+
display: block;
|
|
523
|
+
overflow-x: auto;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
.markdown-body thead {
|
|
527
|
+
background: var(--bg-tertiary);
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
.markdown-body th, .markdown-body td {
|
|
531
|
+
padding: 12px 16px;
|
|
532
|
+
border: 1px solid var(--border);
|
|
533
|
+
text-align: left;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
.markdown-body th {
|
|
537
|
+
font-weight: 600;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
.markdown-body tbody tr:nth-child(even) {
|
|
541
|
+
background: var(--bg-secondary);
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
.markdown-body tbody tr:hover {
|
|
545
|
+
background: rgba(88, 166, 255, 0.05);
|
|
546
|
+
}
|
|
547
|
+
|
|
548
|
+
/* Code blocks with Prism */
|
|
549
|
+
.markdown-body pre {
|
|
550
|
+
background: var(--code-bg);
|
|
551
|
+
border: 1px solid var(--border);
|
|
552
|
+
border-radius: 8px;
|
|
553
|
+
padding: 16px;
|
|
554
|
+
overflow-x: auto;
|
|
555
|
+
margin: 16px 0;
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
.markdown-body pre code {
|
|
559
|
+
background: none;
|
|
560
|
+
padding: 0;
|
|
561
|
+
border-radius: 0;
|
|
562
|
+
font-size: 13px;
|
|
563
|
+
color: var(--text);
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
.markdown-body code {
|
|
567
|
+
font-family: 'SF Mono', Monaco, 'Cascadia Code', monospace;
|
|
568
|
+
font-size: 85%;
|
|
569
|
+
background: var(--code-bg);
|
|
570
|
+
padding: 0.2em 0.4em;
|
|
571
|
+
border-radius: 4px;
|
|
572
|
+
color: var(--accent);
|
|
573
|
+
}
|
|
574
|
+
|
|
575
|
+
/* Blockquotes */
|
|
576
|
+
.markdown-body blockquote {
|
|
577
|
+
border-left: 4px solid var(--accent);
|
|
578
|
+
padding: 0 16px;
|
|
579
|
+
margin: 16px 0;
|
|
580
|
+
color: var(--text-muted);
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
.markdown-body blockquote > :first-child {
|
|
584
|
+
margin-top: 0;
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
.markdown-body blockquote > :last-child {
|
|
588
|
+
margin-bottom: 0;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
/* Images */
|
|
592
|
+
.markdown-body img {
|
|
593
|
+
max-width: 100%;
|
|
594
|
+
height: auto;
|
|
595
|
+
border-radius: 8px;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/* Horizontal Rule */
|
|
599
|
+
.markdown-body hr {
|
|
600
|
+
border: none;
|
|
601
|
+
border-top: 2px solid var(--border);
|
|
602
|
+
margin: 32px 0;
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/* Links */
|
|
606
|
+
.markdown-body a {
|
|
607
|
+
color: var(--accent);
|
|
608
|
+
text-decoration: none;
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
.markdown-body a:hover {
|
|
612
|
+
text-decoration: underline;
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
/* Strikethrough */
|
|
616
|
+
.markdown-body del {
|
|
617
|
+
color: var(--text-muted);
|
|
618
|
+
}
|
|
619
|
+
|
|
470
620
|
/* Responsive */
|
|
471
621
|
@media (max-width: 768px) {
|
|
472
622
|
.sidebar { width: 100%; height: auto; position: relative; }
|
|
@@ -476,6 +626,16 @@ function generatePage(files, currentFile, mode = 'preview') {
|
|
|
476
626
|
.editor { min-height: 300px; }
|
|
477
627
|
.preview-pane { min-height: 300px; }
|
|
478
628
|
}
|
|
629
|
+
|
|
630
|
+
/* Print styles */
|
|
631
|
+
@media print {
|
|
632
|
+
.sidebar, .toolbar, .mode-toggle, .footer { display: none !important; }
|
|
633
|
+
.main { margin-left: 0; padding: 20px; }
|
|
634
|
+
body { background: white; color: black; }
|
|
635
|
+
.markdown-body pre { border: 1px solid #ddd; background: #f5f5f5; }
|
|
636
|
+
.markdown-body code { background: #f5f5f5; color: #333; }
|
|
637
|
+
.markdown-body a { color: #0366d6; }
|
|
638
|
+
}
|
|
479
639
|
</style>
|
|
480
640
|
</head>
|
|
481
641
|
<body>
|
|
@@ -535,6 +695,36 @@ function generatePage(files, currentFile, mode = 'preview') {
|
|
|
535
695
|
|
|
536
696
|
originalContent = editor.value;
|
|
537
697
|
|
|
698
|
+
// Configure marked.js for GitHub Flavored Markdown
|
|
699
|
+
marked.setOptions({
|
|
700
|
+
gfm: true,
|
|
701
|
+
breaks: true,
|
|
702
|
+
headerIds: true,
|
|
703
|
+
mangle: false
|
|
704
|
+
});
|
|
705
|
+
|
|
706
|
+
// Custom renderer for syntax highlighting with Prism
|
|
707
|
+
const renderer = new marked.Renderer();
|
|
708
|
+
renderer.code = function(code, language) {
|
|
709
|
+
const lang = language || '';
|
|
710
|
+
const validLang = Prism.languages[lang] ? lang : 'plaintext';
|
|
711
|
+
let highlighted;
|
|
712
|
+
try {
|
|
713
|
+
highlighted = Prism.languages[validLang]
|
|
714
|
+
? Prism.highlight(code, Prism.languages[validLang], validLang)
|
|
715
|
+
: code;
|
|
716
|
+
} catch (e) {
|
|
717
|
+
highlighted = code;
|
|
718
|
+
}
|
|
719
|
+
return '<pre class="language-' + validLang + '"><code class="language-' + validLang + '">' + highlighted + '</code></pre>';
|
|
720
|
+
};
|
|
721
|
+
marked.setOptions({ renderer });
|
|
722
|
+
|
|
723
|
+
// Render markdown with marked.js
|
|
724
|
+
function renderMarkdown(md) {
|
|
725
|
+
return marked.parse(md);
|
|
726
|
+
}
|
|
727
|
+
|
|
538
728
|
// Live preview update
|
|
539
729
|
let updateTimeout;
|
|
540
730
|
editor.addEventListener('input', () => {
|
|
@@ -543,26 +733,15 @@ function generatePage(files, currentFile, mode = 'preview') {
|
|
|
543
733
|
|
|
544
734
|
clearTimeout(updateTimeout);
|
|
545
735
|
updateTimeout = setTimeout(() => {
|
|
546
|
-
preview.innerHTML =
|
|
547
|
-
|
|
736
|
+
preview.innerHTML = renderMarkdown(editor.value);
|
|
737
|
+
// Re-run Prism highlighting for any code blocks
|
|
738
|
+
Prism.highlightAllUnder(preview);
|
|
739
|
+
}, 150);
|
|
548
740
|
});
|
|
549
741
|
|
|
550
|
-
//
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
.replace(/\`\`\`(\\w+)?\\n([\\s\\S]*?)\`\`\`/g, '<pre><code>$2</code></pre>')
|
|
554
|
-
.replace(/\`([^\`]+)\`/g, '<code class="inline">$1</code>')
|
|
555
|
-
.replace(/^### (.*$)/gm, '<h3>$1</h3>')
|
|
556
|
-
.replace(/^## (.*$)/gm, '<h2>$1</h2>')
|
|
557
|
-
.replace(/^# (.*$)/gm, '<h1>$1</h1>')
|
|
558
|
-
.replace(/\\*\\*([^*]+)\\*\\*/g, '<strong>$1</strong>')
|
|
559
|
-
.replace(/\\*([^*]+)\\*/g, '<em>$1</em>')
|
|
560
|
-
.replace(/\\[([^\\]]+)\\]\\(([^)]+)\\)/g, '<a href="$2">$1</a>')
|
|
561
|
-
.replace(/^\\s*[-*] (.*$)/gm, '<li>$1</li>')
|
|
562
|
-
.replace(/^> (.*$)/gm, '<blockquote>$1</blockquote>')
|
|
563
|
-
.replace(/^---+$/gm, '<hr>')
|
|
564
|
-
.replace(/\\n\\n/g, '</p><p>');
|
|
565
|
-
}
|
|
742
|
+
// Initial render
|
|
743
|
+
preview.innerHTML = renderMarkdown(editor.value);
|
|
744
|
+
Prism.highlightAllUnder(preview);
|
|
566
745
|
|
|
567
746
|
// Update status indicator
|
|
568
747
|
function updateStatus() {
|
|
@@ -632,7 +811,42 @@ function generatePage(files, currentFile, mode = 'preview') {
|
|
|
632
811
|
e.returnValue = '';
|
|
633
812
|
}
|
|
634
813
|
});
|
|
635
|
-
` :
|
|
814
|
+
` : `
|
|
815
|
+
// Preview mode: Render markdown on page load
|
|
816
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
817
|
+
// Configure marked.js
|
|
818
|
+
marked.setOptions({
|
|
819
|
+
gfm: true,
|
|
820
|
+
breaks: true,
|
|
821
|
+
headerIds: true,
|
|
822
|
+
mangle: false
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
// Custom renderer for syntax highlighting with Prism
|
|
826
|
+
const renderer = new marked.Renderer();
|
|
827
|
+
renderer.code = function(code, language) {
|
|
828
|
+
const lang = language || '';
|
|
829
|
+
const validLang = Prism.languages[lang] ? lang : 'plaintext';
|
|
830
|
+
let highlighted;
|
|
831
|
+
try {
|
|
832
|
+
highlighted = Prism.languages[validLang]
|
|
833
|
+
? Prism.highlight(code, Prism.languages[validLang], validLang)
|
|
834
|
+
: code;
|
|
835
|
+
} catch (e) {
|
|
836
|
+
highlighted = code;
|
|
837
|
+
}
|
|
838
|
+
return '<pre class="language-' + validLang + '"><code class="language-' + validLang + '">' + highlighted + '</code></pre>';
|
|
839
|
+
};
|
|
840
|
+
marked.setOptions({ renderer });
|
|
841
|
+
|
|
842
|
+
// Find all markdown bodies and render them
|
|
843
|
+
document.querySelectorAll('.markdown-body[data-markdown]').forEach(el => {
|
|
844
|
+
const rawMd = decodeURIComponent(el.dataset.markdown);
|
|
845
|
+
el.innerHTML = marked.parse(rawMd);
|
|
846
|
+
Prism.highlightAllUnder(el);
|
|
847
|
+
});
|
|
848
|
+
});
|
|
849
|
+
`}
|
|
636
850
|
</script>
|
|
637
851
|
</body>
|
|
638
852
|
</html>`;
|