diff --git a/README.md b/README.md index 910a757..deb35d5 100644 --- a/README.md +++ b/README.md @@ -54,8 +54,25 @@ docsify serve docs ## 导出 PDF +### 方式一:浏览器打印 在浏览器中打开任意页面,按 `Ctrl+P` / `Cmd+P`,选择"另存为 PDF"。 +### 方式二:批量导出(推荐) +使用脚本将整个文档导出为单个 PDF 文件: + +```bash +# 安装依赖 +npm install puppeteer pdf-lib + +# 启动 docsify 服务 +docsify serve docs + +# 运行导出脚本 +node scripts/export-pdf.js +``` + +输出文件:`claude-code-complete-guide.pdf` + ## 源码来源 - [Claude Code CLI](https://github.com/huangserva/claude-code-cli) - TypeScript 源码 diff --git a/package.json b/package.json new file mode 100644 index 0000000..5ae1ae5 --- /dev/null +++ b/package.json @@ -0,0 +1,6 @@ +{ + "dependencies": { + "pdf-lib": "^1.17.1", + "puppeteer": "^24.40.0" + } +} diff --git a/scripts/README.md b/scripts/README.md new file mode 100644 index 0000000..31e44e9 --- /dev/null +++ b/scripts/README.md @@ -0,0 +1,48 @@ +# PDF 导出脚本 + +## 依赖安装 + +```bash +npm install puppeteer pdf-lib +``` + +> 注意: Puppeteer 需要下载 Chromium(约 150MB),国内可能较慢。可使用镜像: +> ```bash +> PUPPETEER_DOWNLOAD_HOST=https://npmmirror.com/mirrors npm install puppeteer +> ``` + +## 使用方法 + +### 1. 启动 docsify 服务 + +终端 1: +```bash +docsify serve docs +``` + +### 2. 运行导出脚本 + +终端 2: +```bash +node scripts/export-pdf.js +``` + +## 配置修改 + +编辑 `scripts/export-pdf.js` 中的 `CONFIG` 对象: + +```javascript +const CONFIG = { + baseUrl: 'http://localhost:3000', // 修改为你的服务地址 + outputPath: './claude-code-complete-guide.pdf', // 输出路径 + tempDir: './temp-pdfs', + waitTime: 2000, // 页面渲染等待时间(ms) + pageTimeout: 30000, // 单页超时(ms) +}; +``` + +## 输出 + +- 生成单个 PDF 文件,包含所有 114 页内容 +- 自动添加页码 +- 保留原文档样式和图片 diff --git a/scripts/export-pdf.js b/scripts/export-pdf.js new file mode 100644 index 0000000..a559022 --- /dev/null +++ b/scripts/export-pdf.js @@ -0,0 +1,202 @@ +/** + * Claude Code 完全指南 - PDF 导出脚本 + * + * 使用方法: + * 1. 先启动 docsify 服务: docsify serve docs + * 2. 安装依赖: npm i puppeteer pdf-lib + * 3. 运行: node scripts/export-pdf.js + */ + +const puppeteer = require('puppeteer'); +const { PDFDocument } = require('pdf-lib'); +const fs = require('fs'); +const path = require('path'); +const https = require('https'); +const http = require('http'); + +// 等待函数(兼容新版 Puppeteer) +const wait = (ms) => new Promise(resolve => setTimeout(resolve, ms)); + +// ============ 配置 ============ +// 注意: 如果 docsify 不是 3000 端口,请修改 baseUrl +const CONFIG = { + baseUrl: 'http://localhost:52861', // 修改为你的 docsify 端口 + outputPath: './claude-code-complete-guide.pdf', + tempDir: './temp-pdfs', + waitTime: 2000, // 页面渲染等待时间(ms) + pageTimeout: 30000, // 单页超时(ms) +}; + +// ============ 解析侧边栏获取所有页面 ============ +function parseSidebar() { + const sidebarPath = path.join(__dirname, '..', 'docs', '_sidebar.md'); + const content = fs.readFileSync(sidebarPath, 'utf-8'); + const pages = []; + + // 匹配所有 markdown 链接 [标题](路径) + const linkRegex = /\[([^\]]+)\]\(([^)]+)\)/g; + let match; + + while ((match = linkRegex.exec(content)) !== null) { + const title = match[1]; + let url = match[2]; + + // 跳过外部链接和特殊路径 + if (url.startsWith('http') || url.startsWith('//') || url === '/') { + continue; + } + + // 转换为 docsify 路由格式 (#/path/to/page) + // 移除 .md 扩展名 + url = url.replace(/\.md$/, ''); + // 添加 #/ 前缀 + const route = '#/' + url; + + pages.push({ title, route }); + } + + return pages; +} + +// ============ 确保目录存在 ============ +function ensureDir(dirPath) { + if (!fs.existsSync(dirPath)) { + fs.mkdirSync(dirPath, { recursive: true }); + } +} + +// ============ 下载单个页面 PDF ============ +async function downloadPagePdf(page, browser, index, total) { + const url = `${CONFIG.baseUrl}/${page.route}`; + console.log(`[${index + 1}/${total}] 正在处理: ${page.title}`); + + try { + const pageObj = await browser.newPage(); + + // 设置视口 + await pageObj.setViewport({ + width: 1200, + height: 800, + deviceScaleFactor: 2, // 2x 分辨率更清晰 + }); + + // 访问页面 + await pageObj.goto(url, { + waitUntil: 'networkidle0', + timeout: CONFIG.pageTimeout, + }); + + // 等待内容加载 + await wait(CONFIG.waitTime); + + // 生成 PDF + const pdfBytes = await pageObj.pdf({ + format: 'A4', + printBackground: true, + margin: { + top: '20mm', + right: '15mm', + bottom: '20mm', + left: '15mm', + }, + displayHeaderFooter: true, + headerTemplate: ` +