Files
bmad-method-template/prototype/view-doc.html
Claude Sonnet 4.6 9a5eb793e8 feat: 添加 Markdown 文档渲染功能
- 在文档中心和首页添加内嵌 Markdown 渲染器
- 使用 markdown-it 库在前端渲染 Markdown 内容
- 添加模态框展示渲染后的文档
- 重命名 {output_folder} 为 planning-artifacts
- 修改 server.js 支持父目录文件服务
- 文档链接现在可以正确显示格式化的 HTML

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-29 02:54:37 +00:00

102 lines
7.1 KiB
HTML

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>文档查看器 - AI 智能抽背助手</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/markdown-it@14.0.0/dist/markdown-it.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.markdown-body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif; font-size: 16px; line-height: 1.6; color: #24292f; }
.markdown-body h1, .markdown-body h2, .markdown-body h3, .markdown-body h4, .markdown-body h5, .markdown-body h6 { margin-top: 24px; margin-bottom: 16px; font-weight: 600; line-height: 1.25; }
.markdown-body h1 { font-size: 2em; border-bottom: 1px solid #d8dee4; padding-bottom: 0.3em; }
.markdown-body h2 { font-size: 1.5em; border-bottom: 1px solid #d8dee4; padding-bottom: 0.3em; }
.markdown-body h3 { font-size: 1.25em; }
.markdown-body h4 { font-size: 1em; }
.markdown-body p { margin-top: 0; margin-bottom: 16px; }
.markdown-body code { padding: 0.2em 0.4em; margin: 0; font-size: 85%; background-color: rgba(175, 184, 193, 0.2); border-radius: 6px; font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace; }
.markdown-body pre { padding: 16px; overflow: auto; font-size: 85%; line-height: 1.45; background-color: #1f2937; border-radius: 6px; margin-bottom: 16px; }
.markdown-body pre code { background-color: transparent; padding: 0; color: #e5e7eb; }
.markdown-body ul, .markdown-body ol { padding-left: 2em; margin-top: 0; margin-bottom: 16px; }
.markdown-body li { margin-top: 0.25em; }
.markdown-body table { border-spacing: 0; border-collapse: collapse; margin-top: 0; margin-bottom: 16px; width: 100%; }
.markdown-body table th { font-weight: 600; background-color: #f6f8fa; border: 1px solid #d8dee4; padding: 6px 13px; }
.markdown-body table td { border: 1px solid #d8dee4; padding: 6px 13px; }
.markdown-body table tr:nth-child(2n) { background-color: #f6f8fa; }
.markdown-body blockquote { padding: 0 1em; color: #57606a; border-left: 0.25em solid #d8dee4; margin: 0 0 16px 0; }
.markdown-body a { color: #0969da; text-decoration: none; }
.markdown-body a:hover { text-decoration: underline; }
.markdown-body img { max-width: 100%; box-sizing: content-box; background-color: #fff; }
.markdown-body hr { height: 0.25em; padding: 0; margin: 24px 0; background-color: #d8dee4; border: 0; }
.loading { display: flex; justify-content: center; align-items: center; height: 200px; }
.loading-spinner { width: 40px; height: 40px; border: 4px solid #f3f3f3; border-top: 4px solid #3498db; border-radius: 50%; animation: spin 1s linear infinite; }
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
.error { background-color: #fee; border: 1px solid #fcc; padding: 16px; border-radius: 6px; color: #c33; }
</style>
</head>
<body class="bg-gray-50">
<nav class="bg-white shadow-sm sticky top-0 z-50">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<div class="flex justify-between items-center h-16">
<div class="flex items-center">
<button onclick="goBack()" class="mr-4 p-2 rounded-lg hover:bg-gray-100 transition"><i class="fas fa-arrow-left text-gray-600"></i></button>
<h1 id="doc-title" class="text-xl font-semibold text-gray-800">文档查看器</h1>
</div>
<div class="flex items-center gap-2">
<button onclick="copyLink()" class="px-3 py-1.5 text-sm bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition"><i class="fas fa-link mr-1"></i>复制链接</button>
<button onclick="printDoc()" class="px-3 py-1.5 text-sm bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition"><i class="fas fa-print mr-1"></i>打印</button>
</div>
</div>
</div>
</nav>
<main class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
<div id="loading" class="loading"><div class="loading-spinner"></div></div>
<div id="error" class="error hidden"></div>
<article id="content" class="markdown-body bg-white rounded-xl shadow-sm p-8 hidden"></article>
</main>
<footer class="bg-white border-t mt-12">
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-6">
<p class="text-center text-sm text-gray-500">© 2026 AI 智能抽背助手 | 基于 <a href="https://markdown-it.github.io/" target="_blank" class="text-blue-600 hover:underline">markdown-it</a> 渲染</p>
</div>
</footer>
<script>
const md = window.markdownit({ html: true, linkify: true, typographer: true, breaks: true });
function getQueryParam(name) { const params = new URLSearchParams(window.location.search); return params.get(name); }
function goBack() { if (window.history.length > 1) { window.history.back(); } else { window.location.href = 'docs-index.html'; } }
function copyLink() { const url = window.location.href; navigator.clipboard.writeText(url).then(() => { alert('链接已复制到剪贴板'); }).catch(err => { console.error('复制失败:', err); }); }
function printDoc() { window.print(); }
async function loadAndRenderDoc() {
const docPath = getQueryParam('doc');
if (!docPath) { showError('未指定文档路径'); return; }
const title = docPath.replace(/\.md$/, '').replace(/-/g, ' ');
document.getElementById('doc-title').textContent = title;
try {
const possiblePaths = [`/planning-artifacts/${docPath}`, `/${docPath}`, `/../${docPath}`];
let markdown = null;
for (const docUrl of possiblePaths) {
try {
const response = await fetch(docUrl);
if (response.ok) { markdown = await response.text(); break; }
} catch (err) { continue; }
}
if (!markdown) { throw new Error(`无法加载文档: ${docPath}`); }
const html = md.render(markdown);
document.getElementById('loading').classList.add('hidden');
document.getElementById('content').classList.remove('hidden');
document.getElementById('content').innerHTML = html;
} catch (error) {
console.error('加载文档失败:', error);
showError(`加载文档失败: ${error.message}`);
}
}
function showError(message) {
document.getElementById('loading').classList.add('hidden');
document.getElementById('error').classList.remove('hidden');
document.getElementById('error').textContent = message;
}
window.addEventListener('DOMContentLoaded', loadAndRenderDoc);
</script>
</body>
</html>