From a92f5977577d087c632cc40595e23b110db6f466 Mon Sep 17 00:00:00 2001 From: GuGuGuGun Date: Wed, 1 Apr 2026 09:45:26 +0800 Subject: [PATCH 1/8] =?UTF-8?q?feat:=20=E6=9E=84=E5=BB=BA=E7=BD=91?= =?UTF-8?q?=E9=A1=B5=E7=89=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-pages.yml | 53 ++ .gitignore | 2 + README.md | 173 ++--- index.html | 15 + package-lock.json | 1136 ++++++++++++++++++++++++++++ package.json | 18 + src/bookManifest.js | 86 +++ src/main.js | 407 ++++++++++ src/style.css | 634 ++++++++++++++++ vite.config.js | 5 + 10 files changed, 2415 insertions(+), 114 deletions(-) create mode 100644 .github/workflows/deploy-pages.yml create mode 100644 index.html create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/bookManifest.js create mode 100644 src/main.js create mode 100644 src/style.css create mode 100644 vite.config.js diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml new file mode 100644 index 0000000..7f7bc27 --- /dev/null +++ b/.github/workflows/deploy-pages.yml @@ -0,0 +1,53 @@ +name: Deploy to GitHub Pages + +on: + push: + branches: + - main + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: true + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: npm + + - name: Install Dependencies + run: npm ci + + - name: Build Site + env: + VITE_BASE_PATH: /${{ github.event.repository.name }}/ + run: npm run build + + - name: Upload Pages Artifact + uses: actions/upload-pages-artifact@v3 + with: + path: dist + + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + needs: build + runs-on: ubuntu-latest + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/.gitignore b/.gitignore index e43b0f9..bf56190 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,3 @@ .DS_Store +node_modules/ +dist/ diff --git a/README.md b/README.md index 8b8d164..eb19259 100644 --- a/README.md +++ b/README.md @@ -1,140 +1,85 @@ -

解码 Agent Harness——Claude Code 架构深度剖析

+# Claude Code Book Web Reader -

- - Stars - - - License - - Language - Chapters -

+基于 Vite 构建的《解码 Agent Harness》网页版阅读站。 -

- 从零到精通,深入理解 AI 编程助手的核心架构与最佳实践 -

+## 声明 ---- +本项目基于原仓库 [lintsinghua/claude-code-book](https://github.com/lintsinghua/claude-code-book) 进行二次开发,目标是提供更友好的网页阅读体验。 -## 背景 +## 项目目标 -2026 年 3 月 31 日,安全研究员 [Chaofan Shou (@Fried_rice)](https://x.com/Fried_rice) 发现 Anthropic 发布在 npm registry 中的 `@anthropic-ai/claude-code` 包存在构建配置失误:source map 文件引用了未设访问控制的 Cloudflare R2 存储桶。披露推文获得超 1700 万次浏览,引发了技术社区对 Agent 架构的空前讨论。Anthropic 随后修补了该配置。 +- 在不改动原书稿目录结构的前提下,提供在线阅读能力 +- 提供章节导航、上下篇跳转与阅读进度记忆 +- 以轻量、可维护的前端工程方式持续演进 -本书写作的初衷正是受到这场讨论的启发——当 Agent 架构成为开发者社区的热门话题,我们意识到需要一本系统性的书来讲解 Agent Harness 的设计原理。 +## 功能特性 -> **⚠️ 免责声明 / Disclaimer** -> -> 本书基于对 Claude Code 公开文档和产品行为的架构分析编写,**未引用、未使用任何未公开或未授权的源码**。Claude Code 为 Anthropic PBC 产品,本书不隶属于、未获授权于、也不代表 Anthropic。 -> -> This book is based on analysis of Claude Code's public documentation and product behavior. No unpublished or unauthorized source code is referenced or used. +- 章节树导航:按书籍结构展示全部章节 +- Markdown 渲染:实时加载并渲染各章节内容 +- 上一篇/下一篇:支持连续阅读 +- 主题切换:支持明暗主题偏好 +- 阅读记忆:自动记录最近阅读章节 ---- +## 项目结构 -## 这本书讲什么 - -**不做使用教程,不列 Prompt 技巧。** 本书带你拆解 Claude Code 的架构——对话循环如何驱动、工具权限为何采用四阶段管线、上下文压缩怎样在 token 预算内运转。 - -读懂了 Claude Code 的设计,你就拥有了一套可迁移到任何 Agent 框架的心智模型。 - -## 为什么值得读 - -- **架构代表性** — 涵盖 Agent Harness 全部核心子系统:工具类型、权限管线、上下文压缩、MCP 集成、子智能体调度 -- **工程决策可追溯** — 为什么用流式异步生成器而非回调?为什么权限是四阶段管线而非黑白名单?每个决策背后都是真实生产场景的洞察 -- **可迁移的认知** — 每章提炼通用设计模式,无论你用 LangChain、AutoGen 还是从零构建 - -## 适合谁读 - -| 读者 | 收获 | -|------|------| -| 想构建 Agent Harness 的**架构师** | 完整的设计空间地图与工程权衡 | -| 不满足于调 API 的**高级工程师** | 工具调用、流式处理、权限管控的底层机制 | -| 对 Agent 工程感兴趣的**研究者** | 从实现角度理解 Agent 系统的运作方式 | -| 希望最大化利用 Claude Code 的**实践者** | 理解设计意图,用得更准、调得更深 | - -## 目录 - -### [前言](00-前言.md) — 为什么写这本书 - -### 第一部分:基础篇 -| 章节 | 标题 | 核心内容 | -|:---:|------|---------| -| [第 1 章](第一部分-基础篇/01-智能体编程的新范式.md) | 智能体编程的新范式 | Agent 范式转移、Harness 定义、全景架构 | -| [第 2 章](第一部分-基础篇/02-对话循环-Agent的心跳.md) | 对话循环 — Agent 的心跳 | 流式异步主循环、错误恢复、turn 状态机 | -| [第 3 章](第一部分-基础篇/03-工具系统-Agent的双手.md) | 工具系统 — Agent 的双手 | 23 种工具、Schema 定义、并发安全、注册表 | -| [第 4 章](第一部分-基础篇/04-权限管线-Agent的护栏.md) | 权限管线 — Agent 的护栏 | 四阶段权限检查、权限模式、用户审批流 | - -### 第二部分:核心系统篇 -| 章节 | 标题 | 核心内容 | -|:---:|------|---------| -| [第 5 章](第二部分-核心系统篇/05-设置与配置-Agent的基因.md) | 设置与配置 — Agent 的基因 | 分层配置、settings.json 体系、远程托管设置 | -| [第 6 章](第二部分-核心系统篇/06-记忆系统-Agent的长期记忆.md) | 记忆系统 — Agent 的长期记忆 | 持久化记忆、索引机制、自动提取、跨会话保持 | -| [第 7 章](第二部分-核心系统篇/07-上下文管理-Agent的工作记忆.md) | 上下文管理 — Agent 的工作记忆 | Token 预算、上下文压缩、优先级排序 | -| [第 8 章](第二部分-核心系统篇/08-钩子系统-Agent的生命周期扩展点.md) | 钩子系统 — Agent 的生命周期扩展点 | Hook 规范、pre/post 执行、阻断控制 | - -### 第三部分:高级模式篇 -| 章节 | 标题 | 核心内容 | -|:---:|------|---------| -| [第 9 章](第三部分-高级模式篇/09-子智能体与Fork模式.md) | 子智能体与 Fork 模式 | worktree 隔离、上下文继承、并行调度 | -| [第 10 章](第三部分-高级模式篇/10-协调器模式-多智能体编排.md) | 协调器模式 — 多智能体编排 | Coordinator 架构、Team 机制、任务分配 | -| [第 11 章](第三部分-高级模式篇/11-技能系统与插件架构.md) | 技能系统与插件架构 | Skill 协议、Plugin 加载、扩展点设计 | -| [第 12 章](第三部分-高级模式篇/12-MCP集成与外部协议.md) | MCP 集成与外部协议 | Model Context Protocol、Server 管理、协议桥接 | +``` +. +├─ src/ +│ ├─ main.js +│ ├─ style.css +│ └─ bookManifest.js +├─ docs/plans/ +│ └─ 2026-04-01-book-web-design.md +├─ 第一部分-基础篇/ +├─ 第二部分-核心系统篇/ +├─ 第三部分-高级模式篇/ +├─ 第四部分-工程实践篇/ +├─ 附录/ +├─ index.html +├─ vite.config.js +└─ package.json +``` -### 第四部分:工程实践篇 -| 章节 | 标题 | 核心内容 | -|:---:|------|---------| -| [第 13 章](第四部分-工程实践篇/13-流式架构与性能优化.md) | 流式架构与性能优化 | 并行预取、惰性加载、Token 估算、启动优化 | -| [第 14 章](第四部分-工程实践篇/14-Plan模式与结构化工作流.md) | Plan 模式与结构化工作流 | Plan/Act 分离、结构化审批、定时触发 | -| [第 15 章](第四部分-工程实践篇/15-构建你自己的Agent-Harness.md) | 构建你自己的 Agent Harness | 从零实现 Mini Harness,融会贯通全书 | +## 快速开始 -### 附录 -| 附录 | 标题 | -|:---:|------| -| [A](附录/A-源码导航地图.md) | 架构导航地图 | -| [B](附录/B-工具完整清单.md) | 工具完整清单 | -| [C](附录/C-功能标志速查表.md) | 功能标志速查表 | -| [D](附录/D-术语表.md) | 术语表 | +### 1) 安装依赖 ---- +```bash +npm install +``` -## 阅读路径 +### 2) 本地开发 -``` -┌─────────────────────────────────────────────────────────┐ -│ 时间有限? │ -│ 第 1-2 章(心智模型)→ 第 3-4 章(核心机制)→ 收工 │ -├─────────────────────────────────────────────────────────┤ -│ 有经验的工程师? │ -│ 直接第二部分 → 遇到概念缺口回溯第一部分 → 按需跳第三部分 │ -├─────────────────────────────────────────────────────────┤ -│ 完整学习? │ -│ 按顺序阅读 → 完成每章实战练习 → 第 15 章动手构建 │ -└─────────────────────────────────────────────────────────┘ +```bash +npm run dev ``` -## 约定 +### 3) 生产构建 -- 中文写作,技术术语保留英文原文 -- 每章结构:学习目标 → 核心概念 → 架构图 → 实战练习 → 关键要点 -- 代码示例为说明设计模式的示意代码,非产品源码 +```bash +npm run build +``` -## 贡献 +### 4) 本地预览构建产物 -欢迎提交 Issue 和 Pull Request: +```bash +npm run preview +``` -- 修正技术错误或表述不准确之处 -- 补充实战练习和案例分析 -- 改进章节结构和可读性 +## 内容维护说明 -## 致谢 -[Linux.Do](https://linux.do/) +- 书籍内容来源于仓库内各 Markdown 章节文件 +- 章节目录由 src/bookManifest.js 统一维护 +- 新增章节时,请同步更新 src/bookManifest.js -## Star History +## 贡献方式 -[![Star History Chart](https://api.star-history.com/svg?repos=lintsinghua/claude-code-book&type=Date)](https://star-history.com/#lintsinghua/claude-code-book&Date) +欢迎通过 Issue 或 Pull Request 参与改进: -## License +- 修复页面显示问题或阅读体验问题 +- 优化导航、样式与交互 +- 补充构建与部署文档 -CC BY-NC-SA 4.0 +## 许可证 -本书内容采用 [CC BY-NC-SA 4.0](https://creativecommons.org/licenses/by-nc-sa/4.0/) 协议发布——可自由分享和改编,但须署名、非商业使用、并以相同协议共享。 +书籍内容与相关版权遵循原项目及其许可证约定;如需转载或二次发布,请先阅读并遵守原仓库许可条款。 diff --git a/index.html b/index.html new file mode 100644 index 0000000..ee21fe9 --- /dev/null +++ b/index.html @@ -0,0 +1,15 @@ + + + + + + Claude Code Book 在线阅读 + + + + + +
+ + + diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..08d816f --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1136 @@ +{ + "name": "claude-code-book-web-reader", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "claude-code-book-web-reader", + "version": "0.1.0", + "dependencies": { + "dompurify": "^3.2.6", + "marked": "^15.0.7" + }, + "devDependencies": { + "vite": "^7.1.3" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.27.4.tgz", + "integrity": "sha512-cQPwL2mp2nSmHHJlCyoXgHGhbEPMrEEU5xhkcy3Hs/O7nGZqEpZ2sUtLaL9MORLtDfRvVl2/3PAuEkYZH0Ty8Q==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.27.4.tgz", + "integrity": "sha512-X9bUgvxiC8CHAGKYufLIHGXPJWnr0OCdR0anD2e21vdvgCI8lIfqFbnoeOz7lBjdrAGUhqLZLcQo6MLhTO2DKQ==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.27.4.tgz", + "integrity": "sha512-gdLscB7v75wRfu7QSm/zg6Rx29VLdy9eTr2t44sfTW7CxwAtQghZ4ZnqHk3/ogz7xao0QAgrkradbBzcqFPasw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.27.4.tgz", + "integrity": "sha512-PzPFnBNVF292sfpfhiyiXCGSn9HZg5BcAz+ivBuSsl6Rk4ga1oEXAamhOXRFyMcjwr2DVtm40G65N3GLeH1Lvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.27.4.tgz", + "integrity": "sha512-b7xaGIwdJlht8ZFCvMkpDN6uiSmnxxK56N2GDTMYPr2/gzvfdQN8rTfBsvVKmIVY/X7EM+/hJKEIbbHs9oA4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.27.4.tgz", + "integrity": "sha512-sR+OiKLwd15nmCdqpXMnuJ9W2kpy0KigzqScqHI3Hqwr7IXxBp3Yva+yJwoqh7rE8V77tdoheRYataNKL4QrPw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.4.tgz", + "integrity": "sha512-jnfpKe+p79tCnm4GVav68A7tUFeKQwQyLgESwEAUzyxk/TJr4QdGog9sqWNcUbr/bZt/O/HXouspuQDd9JxFSw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.27.4.tgz", + "integrity": "sha512-2kb4ceA/CpfUrIcTUl1wrP/9ad9Atrp5J94Lq69w7UwOMolPIGrfLSvAKJp0RTvkPPyn6CIWrNy13kyLikZRZQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.27.4.tgz", + "integrity": "sha512-aBYgcIxX/wd5n2ys0yESGeYMGF+pv6g0DhZr3G1ZG4jMfruU9Tl1i2Z+Wnj9/KjGz1lTLCcorqE2viePZqj4Eg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.27.4.tgz", + "integrity": "sha512-7nQOttdzVGth1iz57kxg9uCz57dxQLHWxopL6mYuYthohPKEK0vU0C3O21CcBK6KDlkYVcnDXY099HcCDXd9dA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.27.4.tgz", + "integrity": "sha512-oPtixtAIzgvzYcKBQM/qZ3R+9TEUd1aNJQu0HhGyqtx6oS7qTpvjheIWBbes4+qu1bNlo2V4cbkISr8q6gRBFA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.27.4.tgz", + "integrity": "sha512-8mL/vh8qeCoRcFH2nM8wm5uJP+ZcVYGGayMavi8GmRJjuI3g1v6Z7Ni0JJKAJW+m0EtUuARb6Lmp4hMjzCBWzA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.27.4.tgz", + "integrity": "sha512-1RdrWFFiiLIW7LQq9Q2NES+HiD4NyT8Itj9AUeCl0IVCA459WnPhREKgwrpaIfTOe+/2rdntisegiPWn/r/aAw==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.27.4.tgz", + "integrity": "sha512-tLCwNG47l3sd9lpfyx9LAGEGItCUeRCWeAx6x2Jmbav65nAwoPXfewtAdtbtit/pJFLUWOhpv0FpS6GQAmPrHA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.27.4.tgz", + "integrity": "sha512-BnASypppbUWyqjd1KIpU4AUBiIhVr6YlHx/cnPgqEkNoVOhHg+YiSVxM1RLfiy4t9cAulbRGTNCKOcqHrEQLIw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.27.4.tgz", + "integrity": "sha512-+eUqgb/Z7vxVLezG8bVB9SfBie89gMueS+I0xYh2tJdw3vqA/0ImZJ2ROeWwVJN59ihBeZ7Tu92dF/5dy5FttA==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.27.4.tgz", + "integrity": "sha512-S5qOXrKV8BQEzJPVxAwnryi2+Iq5pB40gTEIT69BQONqR7JH1EPIcQ/Uiv9mCnn05jff9umq/5nqzxlqTOg9NA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.4.tgz", + "integrity": "sha512-xHT8X4sb0GS8qTqiwzHqpY00C95DPAq7nAwX35Ie/s+LO9830hrMd3oX0ZMKLvy7vsonee73x0lmcdOVXFzd6Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.27.4.tgz", + "integrity": "sha512-RugOvOdXfdyi5Tyv40kgQnI0byv66BFgAqjdgtAKqHoZTbTF2QqfQrFwa7cHEORJf6X2ht+l9ABLMP0dnKYsgg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.4.tgz", + "integrity": "sha512-2MyL3IAaTX+1/qP0O1SwskwcwCoOI4kV2IBX1xYnDDqthmq5ArrW94qSIKCAuRraMgPOmG0RDTA74mzYNQA9ow==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.27.4.tgz", + "integrity": "sha512-u8fg/jQ5aQDfsnIV6+KwLOf1CmJnfu1ShpwqdwC0uA7ZPwFws55Ngc12vBdeUdnuWoQYx/SOQLGDcdlfXhYmXQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.4.tgz", + "integrity": "sha512-JkTZrl6VbyO8lDQO3yv26nNr2RM2yZzNrNHEsj9bm6dOwwu9OYN28CjzZkH57bh4w0I2F7IodpQvUAEd1mbWXg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.27.4.tgz", + "integrity": "sha512-/gOzgaewZJfeJTlsWhvUEmUG4tWEY2Spp5M20INYRg2ZKl9QPO3QEEgPeRtLjEWSW8FilRNacPOg8R1uaYkA6g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.27.4.tgz", + "integrity": "sha512-Z9SExBg2y32smoDQdf1HRwHRt6vAHLXcxD2uGgO/v2jK7Y718Ix4ndsbNMU/+1Qiem9OiOdaqitioZwxivhXYg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.27.4.tgz", + "integrity": "sha512-DAyGLS0Jz5G5iixEbMHi5KdiApqHBWMGzTtMiJ72ZOLhbu/bzxgAe8Ue8CTS3n3HbIUHQz/L51yMdGMeoxXNJw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.27.4.tgz", + "integrity": "sha512-+knoa0BDoeXgkNvvV1vvbZX4+hizelrkwmGJBdT17t8FNPwG2lKemmuMZlmaNQ3ws3DKKCxpb4zRZEIp3UxFCg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=18" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.1.tgz", + "integrity": "sha512-d6FinEBLdIiK+1uACUttJKfgZREXrF0Qc2SmLII7W2AD8FfiZ9Wjd+rD/iRuf5s5dWrr1GgwXCvPqOuDquOowA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.1.tgz", + "integrity": "sha512-YjG/EwIDvvYI1YvYbHvDz/BYHtkY4ygUIXHnTdLhG+hKIQFBiosfWiACWortsKPKU/+dUwQQCKQM3qrDe8c9BA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.1.tgz", + "integrity": "sha512-mjCpF7GmkRtSJwon+Rq1N8+pI+8l7w5g9Z3vWj4T7abguC4Czwi3Yu/pFaLvA3TTeMVjnu3ctigusqWUfjZzvw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.1.tgz", + "integrity": "sha512-haZ7hJ1JT4e9hqkoT9R/19XW2QKqjfJVv+i5AGg57S+nLk9lQnJ1F/eZloRO3o9Scy9CM3wQ9l+dkXtcBgN5Ew==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.1.tgz", + "integrity": "sha512-czw90wpQq3ZsAVBlinZjAYTKduOjTywlG7fEeWKUA7oCmpA8xdTkxZZlwNJKWqILlq0wehoZcJYfBvOyhPTQ6w==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.1.tgz", + "integrity": "sha512-KVB2rqsxTHuBtfOeySEyzEOB7ltlB/ux38iu2rBQzkjbwRVlkhAGIEDiiYnO2kFOkJp+Z7pUXKyrRRFuFUKt+g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.1.tgz", + "integrity": "sha512-L+34Qqil+v5uC0zEubW7uByo78WOCIrBvci69E7sFASRl0X7b/MB6Cqd1lky/CtcSVTydWa2WZwFuWexjS5o6g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.1.tgz", + "integrity": "sha512-n83O8rt4v34hgFzlkb1ycniJh7IR5RCIqt6mz1VRJD6pmhRi0CXdmfnLu9dIUS6buzh60IvACM842Ffb3xd6Gg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.1.tgz", + "integrity": "sha512-Nql7sTeAzhTAja3QXeAI48+/+GjBJ+QmAH13snn0AJSNL50JsDqotyudHyMbO2RbJkskbMbFJfIJKWA6R1LCJQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.1.tgz", + "integrity": "sha512-+pUymDhd0ys9GcKZPPWlFiZ67sTWV5UU6zOJat02M1+PiuSGDziyRuI/pPue3hoUwm2uGfxdL+trT6Z9rxnlMA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.1.tgz", + "integrity": "sha512-VSvgvQeIcsEvY4bKDHEDWcpW4Yw7BtlKG1GUT4FzBUlEKQK0rWHYBqQt6Fm2taXS+1bXvJT6kICu5ZwqKCnvlQ==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.1.tgz", + "integrity": "sha512-4LqhUomJqwe641gsPp6xLfhqWMbQV04KtPp7/dIp0nzPxAkNY1AbwL5W0MQpcalLYk07vaW9Kp1PBhdpZYYcEw==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.1.tgz", + "integrity": "sha512-tLQQ9aPvkBxOc/EUT6j3pyeMD6Hb8QF2BTBnCQWP/uu1lhc9AIrIjKnLYMEroIz/JvtGYgI9dF3AxHZNaEH0rw==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.1.tgz", + "integrity": "sha512-RMxFhJwc9fSXP6PqmAz4cbv3kAyvD1etJFjTx4ONqFP9DkTkXsAMU4v3Vyc5BgzC+anz7nS/9tp4obsKfqkDHg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.1.tgz", + "integrity": "sha512-QKgFl+Yc1eEk6MmOBfRHYF6lTxiiiV3/z/BRrbSiW2I7AFTXoBFvdMEyglohPj//2mZS4hDOqeB0H1ACh3sBbg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.1.tgz", + "integrity": "sha512-RAjXjP/8c6ZtzatZcA1RaQr6O1TRhzC+adn8YZDnChliZHviqIjmvFwHcxi4JKPSDAt6Uhf/7vqcBzQJy0PDJg==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.1.tgz", + "integrity": "sha512-wcuocpaOlaL1COBYiA89O6yfjlp3RwKDeTIA0hM7OpmhR1Bjo9j31G1uQVpDlTvwxGn2nQs65fBFL5UFd76FcQ==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.1.tgz", + "integrity": "sha512-77PpsFQUCOiZR9+LQEFg9GClyfkNXj1MP6wRnzYs0EeWbPcHs02AXu4xuUbM1zhwn3wqaizle3AEYg5aeoohhg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.1.tgz", + "integrity": "sha512-5cIATbk5vynAjqqmyBjlciMJl1+R/CwX9oLk/EyiFXDWd95KpHdrOJT//rnUl4cUcskrd0jCCw3wpZnhIHdD9w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.1.tgz", + "integrity": "sha512-cl0w09WsCi17mcmWqqglez9Gk8isgeWvoUZ3WiJFYSR3zjBQc2J5/ihSjpl+VLjPqjQ/1hJRcqBfLjssREQILw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.1.tgz", + "integrity": "sha512-4Cv23ZrONRbNtbZa37mLSueXUCtN7MXccChtKpUnQNgF010rjrjfHx3QxkS2PI7LqGT5xXyYs1a7LbzAwT0iCA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.1.tgz", + "integrity": "sha512-i1okWYkA4FJICtr7KpYzFpRTHgy5jdDbZiWfvny21iIKky5YExiDXP+zbXzm3dUcFpkEeYNHgQ5fuG236JPq0g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.1.tgz", + "integrity": "sha512-u09m3CuwLzShA0EYKMNiFgcjjzwqtUMLmuCJLeZWjjOYA3IT2Di09KaxGBTP9xVztWyIWjVdsB2E9goMjZvTQg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.1.tgz", + "integrity": "sha512-k+600V9Zl1CM7eZxJgMyTUzmrmhB/0XZnF4pRypKAlAgxmedUA+1v9R+XOFv56W4SlHEzfeMtzujLJD22Uz5zg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.1.tgz", + "integrity": "sha512-lWMnixq/QzxyhTV6NjQJ4SFo1J6PvOX8vUx5Wb4bBPsEb+8xZ89Bz6kOXpfXj9ak9AHTQVQzlgzBEc1SyM27xQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, + "node_modules/dompurify": { + "version": "3.3.3", + "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.3.3.tgz", + "integrity": "sha512-Oj6pzI2+RqBfFG+qOaOLbFXLQ90ARpcGG6UePL82bJLtdsa6CYJD7nmiU8MW9nQNOtCHV3lZ/Bzq1X0QYbBZCA==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, + "node_modules/esbuild": { + "version": "0.27.4", + "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.27.4.tgz", + "integrity": "sha512-Rq4vbHnYkK5fws5NF7MYTU68FPRE1ajX7heQ/8QXXWqNgqqJ/GkmmyxIzUnf2Sr/bakf8l54716CcMGHYhMrrQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.27.4", + "@esbuild/android-arm": "0.27.4", + "@esbuild/android-arm64": "0.27.4", + "@esbuild/android-x64": "0.27.4", + "@esbuild/darwin-arm64": "0.27.4", + "@esbuild/darwin-x64": "0.27.4", + "@esbuild/freebsd-arm64": "0.27.4", + "@esbuild/freebsd-x64": "0.27.4", + "@esbuild/linux-arm": "0.27.4", + "@esbuild/linux-arm64": "0.27.4", + "@esbuild/linux-ia32": "0.27.4", + "@esbuild/linux-loong64": "0.27.4", + "@esbuild/linux-mips64el": "0.27.4", + "@esbuild/linux-ppc64": "0.27.4", + "@esbuild/linux-riscv64": "0.27.4", + "@esbuild/linux-s390x": "0.27.4", + "@esbuild/linux-x64": "0.27.4", + "@esbuild/netbsd-arm64": "0.27.4", + "@esbuild/netbsd-x64": "0.27.4", + "@esbuild/openbsd-arm64": "0.27.4", + "@esbuild/openbsd-x64": "0.27.4", + "@esbuild/openharmony-arm64": "0.27.4", + "@esbuild/sunos-x64": "0.27.4", + "@esbuild/win32-arm64": "0.27.4", + "@esbuild/win32-ia32": "0.27.4", + "@esbuild/win32-x64": "0.27.4" + } + }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/marked": { + "version": "15.0.12", + "resolved": "https://registry.npmmirror.com/marked/-/marked-15.0.12.tgz", + "integrity": "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==", + "license": "MIT", + "bin": { + "marked": "bin/marked.js" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/nanoid": { + "version": "3.3.11", + "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/picomatch": { + "version": "4.0.4", + "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.4.tgz", + "integrity": "sha512-QP88BAKvMam/3NxH6vj2o21R6MjxZUAd6nlwAS/pnGvN9IVLocLHxGYIzFhg6fUQ+5th6P4dv4eW9jX3DSIj7A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/postcss": { + "version": "8.5.8", + "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.8.tgz", + "integrity": "sha512-OW/rX8O/jXnm82Ey1k44pObPtdblfiuWnrd8X7GJ7emImCOstunGbXUpp7HdBrFQX6rJzn3sPT397Wp5aCwCHg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.60.1", + "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.60.1.tgz", + "integrity": "sha512-VmtB2rFU/GroZ4oL8+ZqXgSA38O6GR8KSIvWmEFv63pQ0G6KaBH9s07PO8XTXP4vI+3UJUEypOfjkGfmSBBR0w==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.1", + "@rollup/rollup-android-arm64": "4.60.1", + "@rollup/rollup-darwin-arm64": "4.60.1", + "@rollup/rollup-darwin-x64": "4.60.1", + "@rollup/rollup-freebsd-arm64": "4.60.1", + "@rollup/rollup-freebsd-x64": "4.60.1", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.1", + "@rollup/rollup-linux-arm-musleabihf": "4.60.1", + "@rollup/rollup-linux-arm64-gnu": "4.60.1", + "@rollup/rollup-linux-arm64-musl": "4.60.1", + "@rollup/rollup-linux-loong64-gnu": "4.60.1", + "@rollup/rollup-linux-loong64-musl": "4.60.1", + "@rollup/rollup-linux-ppc64-gnu": "4.60.1", + "@rollup/rollup-linux-ppc64-musl": "4.60.1", + "@rollup/rollup-linux-riscv64-gnu": "4.60.1", + "@rollup/rollup-linux-riscv64-musl": "4.60.1", + "@rollup/rollup-linux-s390x-gnu": "4.60.1", + "@rollup/rollup-linux-x64-gnu": "4.60.1", + "@rollup/rollup-linux-x64-musl": "4.60.1", + "@rollup/rollup-openbsd-x64": "4.60.1", + "@rollup/rollup-openharmony-arm64": "4.60.1", + "@rollup/rollup-win32-arm64-msvc": "4.60.1", + "@rollup/rollup-win32-ia32-msvc": "4.60.1", + "@rollup/rollup-win32-x64-gnu": "4.60.1", + "@rollup/rollup-win32-x64-msvc": "4.60.1", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fdir": "^6.5.0", + "picomatch": "^4.0.3" + }, + "engines": { + "node": ">=12.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" + } + }, + "node_modules/vite": { + "version": "7.3.1", + "resolved": "https://registry.npmmirror.com/vite/-/vite-7.3.1.tgz", + "integrity": "sha512-w+N7Hifpc3gRjZ63vYBXA56dvvRlNWRczTdmCBBa+CotUzAPf5b7YMdMR/8CQoeYE5LX3W4wj6RYTgonm1b9DA==", + "dev": true, + "license": "MIT", + "dependencies": { + "esbuild": "^0.27.0", + "fdir": "^6.5.0", + "picomatch": "^4.0.3", + "postcss": "^8.5.6", + "rollup": "^4.43.0", + "tinyglobby": "^0.2.15" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^20.19.0 || >=22.12.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^20.19.0 || >=22.12.0", + "jiti": ">=1.21.0", + "less": "^4.0.0", + "lightningcss": "^1.21.0", + "sass": "^1.70.0", + "sass-embedded": "^1.70.0", + "stylus": ">=0.54.8", + "sugarss": "^5.0.0", + "terser": "^5.16.0", + "tsx": "^4.8.1", + "yaml": "^2.4.2" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "jiti": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + }, + "tsx": { + "optional": true + }, + "yaml": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c917148 --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "claude-code-book-web-reader", + "private": true, + "version": "0.1.0", + "type": "module", + "scripts": { + "dev": "vite", + "build": "vite build", + "preview": "vite preview" + }, + "dependencies": { + "dompurify": "^3.2.6", + "marked": "^15.0.7" + }, + "devDependencies": { + "vite": "^7.1.3" + } +} diff --git a/src/bookManifest.js b/src/bookManifest.js new file mode 100644 index 0000000..2d2ce35 --- /dev/null +++ b/src/bookManifest.js @@ -0,0 +1,86 @@ +export const bookTree = [ + { + title: "前言", + path: "00-前言.md" + }, + { + title: "第一部分 基础篇", + children: [ + { title: "01 智能体编程的新范式", path: "第一部分-基础篇/01-智能体编程的新范式.md" }, + { title: "02 对话循环 Agent的心跳", path: "第一部分-基础篇/02-对话循环-Agent的心跳.md" }, + { title: "03 工具系统 Agent的双手", path: "第一部分-基础篇/03-工具系统-Agent的双手.md" }, + { title: "04 权限管线 Agent的护栏", path: "第一部分-基础篇/04-权限管线-Agent的护栏.md" } + ] + }, + { + title: "第二部分 核心系统篇", + children: [ + { title: "05 设置与配置 Agent的基因", path: "第二部分-核心系统篇/05-设置与配置-Agent的基因.md" }, + { title: "06 记忆系统 Agent的长期记忆", path: "第二部分-核心系统篇/06-记忆系统-Agent的长期记忆.md" }, + { title: "07 上下文管理 Agent的工作记忆", path: "第二部分-核心系统篇/07-上下文管理-Agent的工作记忆.md" }, + { title: "08 钩子系统 Agent的生命周期扩展点", path: "第二部分-核心系统篇/08-钩子系统-Agent的生命周期扩展点.md" } + ] + }, + { + title: "第三部分 高级模式篇", + children: [ + { title: "09 子智能体与 Fork 模式", path: "第三部分-高级模式篇/09-子智能体与Fork模式.md" }, + { title: "10 协调器模式 多智能体编排", path: "第三部分-高级模式篇/10-协调器模式-多智能体编排.md" }, + { title: "11 技能系统与插件架构", path: "第三部分-高级模式篇/11-技能系统与插件架构.md" }, + { title: "12 MCP 集成与外部协议", path: "第三部分-高级模式篇/12-MCP集成与外部协议.md" } + ] + }, + { + title: "第四部分 工程实践篇", + children: [ + { title: "13 流式架构与性能优化", path: "第四部分-工程实践篇/13-流式架构与性能优化.md" }, + { title: "14 Plan 模式与结构化工作流", path: "第四部分-工程实践篇/14-Plan模式与结构化工作流.md" }, + { title: "15 构建你自己的 Agent Harness", path: "第四部分-工程实践篇/15-构建你自己的Agent-Harness.md" } + ] + }, + { + title: "附录", + children: [ + { title: "A 源码导航地图", path: "附录/A-源码导航地图.md" }, + { title: "B 工具完整清单", path: "附录/B-工具完整清单.md" }, + { title: "C 功能标志速查表", path: "附录/C-功能标志速查表.md" }, + { title: "D 术语表", path: "附录/D-术语表.md" } + ] + } +]; + +export function flattenBook(tree = bookTree) { + const result = []; + + function visit(node) { + if (node.path) { + result.push({ title: node.title, path: node.path }); + return; + } + + for (const child of node.children || []) { + visit(child); + } + } + + for (const item of tree) { + visit(item); + } + + return result; +} + +export function findNodeByPath(path, tree = bookTree) { + for (const node of tree) { + if (node.path === path) { + return node; + } + if (node.children) { + const hit = findNodeByPath(path, node.children); + if (hit) { + return hit; + } + } + } + return null; +} diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..4a8ea2b --- /dev/null +++ b/src/main.js @@ -0,0 +1,407 @@ +import './style.css'; +import { marked } from 'marked'; +import DOMPurify from 'dompurify'; +import { bookTree, flattenBook, findNodeByPath } from './bookManifest.js'; + +const STORAGE_THEME_KEY = 'book.reader.theme'; +const STORAGE_LAST_PAGE_KEY = 'book.reader.last-page'; +const STORAGE_SIDEBAR_COLLAPSED_KEY = 'book.reader.sidebar-collapsed'; + +const markdownModules = import.meta.glob('/**/*.md', { + query: '?raw', + import: 'default' +}); + +const flatBook = flattenBook(bookTree); + +marked.setOptions({ + gfm: true, + breaks: false +}); + +const app = document.querySelector('#app'); + +app.innerHTML = ` +
+ + +
+ + +
+ + Claude Code Book + +
+ +
+
+
+

正在阅读

+

加载中...

+
+
+ + +
+
+ +
+
+ + + + + +
+ + +
+`; + +const elements = { + nav: document.querySelector('#bookNav'), + article: document.querySelector('#article'), + articleTitle: document.querySelector('#articleTitle'), + searchInput: document.querySelector('#searchInput'), + themeToggle: [...document.querySelectorAll('.theme-toggle-btn')], + sidebarToggle: document.querySelector('#sidebarToggle'), + mobileMenuBtn: document.querySelector('#mobileMenuBtn'), + sidebarBackdrop: document.querySelector('#sidebarBackdrop'), + prevBtn: document.querySelector('#prevBtn'), + nextBtn: document.querySelector('#nextBtn'), + toc: document.querySelector('#toc'), + tocToggle: document.querySelector('#tocToggle'), + floatingTocBtn: document.querySelector('#floatingTocBtn'), + progressBar: document.querySelector('#progressBar') +}; + +let currentPath = null; + +function isMobileViewport() { + return window.matchMedia('(max-width: 960px)').matches; +} + +function applySidebarCollapsed(collapsed) { + document.body.classList.toggle('sidebar-collapsed', collapsed); + localStorage.setItem(STORAGE_SIDEBAR_COLLAPSED_KEY, String(collapsed)); +} + +function setSidebarOpen(open) { + document.body.classList.toggle('sidebar-open', open); +} + +function initSidebarState() { + const collapsed = localStorage.getItem(STORAGE_SIDEBAR_COLLAPSED_KEY) === 'true'; + applySidebarCollapsed(collapsed); +} + +function normalizePath(path) { + return path.startsWith('/') ? path : `/${path}`; +} + +function renderThemeIcon(isDark) { + const html = isDark + ? `` + : ``; + elements.themeToggle.forEach(btn => btn.innerHTML = html); +} + +function applyTheme(theme) { + const value = theme === 'dark' ? 'dark' : 'light'; + document.documentElement.dataset.theme = value; + localStorage.setItem(STORAGE_THEME_KEY, value); + renderThemeIcon(value === 'dark'); +} + +function initTheme() { + const stored = localStorage.getItem(STORAGE_THEME_KEY); + if (stored) { + applyTheme(stored); + return; + } + + const prefersDark = window.matchMedia('(prefers-color-scheme: dark)').matches; + applyTheme(prefersDark ? 'dark' : 'light'); +} + +function slugify(text) { + return text + .toLowerCase() + .trim() + .replace(/[^\w\u4e00-\u9fa5\-\s]/g, '') + .replace(/\s+/g, '-'); +} + +function renderToc() { + const headings = [...elements.article.querySelectorAll('h1, h2, h3, h4')]; + if (!headings.length) { + elements.toc.innerHTML = '

本页暂无目录

'; + return; + } + + elements.toc.innerHTML = ` + + `; +} + +function updateReadingProgress() { + const doc = document.documentElement; + const total = doc.scrollHeight - doc.clientHeight; + const ratio = total > 0 ? Math.min(1, Math.max(0, window.scrollY / total)) : 0; + elements.progressBar.style.transform = `scaleX(${ratio})`; +} + +function getCurrentIndex() { + return flatBook.findIndex((item) => item.path === currentPath); +} + +function updatePager() { + const index = getCurrentIndex(); + elements.prevBtn.disabled = index <= 0; + elements.nextBtn.disabled = index < 0 || index >= flatBook.length - 1; +} + +function renderNav(filterText = '') { + const keyword = filterText.trim().toLowerCase(); + + function renderNodes(nodes, level = 0) { + const items = []; + + for (const node of nodes) { + if (node.path) { + const match = !keyword || node.title.toLowerCase().includes(keyword); + if (!match) continue; + const isActive = node.path === currentPath; + items.push(` +
  • + +
  • + `); + } else { + const childHtml = renderNodes(node.children || [], level + 1); + if (!childHtml) continue; + items.push(` +
  • +

    ${node.title}

    + +
  • + `); + } + } + + return items.join(''); + } + + const html = renderNodes(bookTree); + elements.nav.innerHTML = html ? `` : '

    没有匹配章节

    '; +} + +async function loadMarkdown(path) { + const normalized = normalizePath(path); + const loader = markdownModules[normalized]; + + if (!loader) { + throw new Error(`未找到 Markdown 文件: ${normalized}`); + } + + const markdown = await loader(); + return markdown; +} + +async function openArticle(path, fromHistory = false) { + currentPath = path; + + const node = findNodeByPath(path); + elements.articleTitle.textContent = node ? node.title : path; + + elements.article.style.animation = 'none'; + elements.article.offsetHeight; /* trigger reflow */ + elements.article.style.animation = null; + + try { + const markdown = await loadMarkdown(path); + const rawHtml = marked.parse(markdown); + const safeHtml = DOMPurify.sanitize(rawHtml); + elements.article.innerHTML = safeHtml; + renderToc(); + renderNav(elements.searchInput.value); + updatePager(); + updateReadingProgress(); + + const hash = `#${encodeURIComponent(path)}`; + if (!fromHistory && window.location.hash !== hash) { + history.pushState({ path }, '', hash); + } + + localStorage.setItem(STORAGE_LAST_PAGE_KEY, path); + window.scrollTo({ top: 0, behavior: 'smooth' }); + } catch (error) { + elements.article.innerHTML = `

    加载失败:${error.message}

    `; + } +} + +function bindEvents() { + elements.nav.addEventListener('click', (event) => { + const target = event.target.closest('button[data-path]'); + if (!target) return; + + openArticle(target.dataset.path); + if (isMobileViewport()) { + setSidebarOpen(false); + } + }); + + elements.searchInput.addEventListener('input', () => { + renderNav(elements.searchInput.value); + }); + + elements.themeToggle.forEach(btn => { + btn.addEventListener('click', () => { + const next = document.documentElement.dataset.theme === 'dark' ? 'light' : 'dark'; + applyTheme(next); + }); + }); + + elements.sidebarToggle.addEventListener('click', () => { + const nextCollapsed = !document.body.classList.contains('sidebar-collapsed'); + applySidebarCollapsed(nextCollapsed); + }); + + if (elements.mobileMenuBtn) { + elements.mobileMenuBtn.addEventListener('click', () => { + setSidebarOpen(!document.body.classList.contains('sidebar-open')); + }); + } + + elements.sidebarBackdrop.addEventListener('click', () => { + setSidebarOpen(false); + }); + + elements.prevBtn.addEventListener('click', () => { + const index = getCurrentIndex(); + if (index > 0) openArticle(flatBook[index - 1].path); + }); + + elements.nextBtn.addEventListener('click', () => { + const index = getCurrentIndex(); + if (index >= 0 && index < flatBook.length - 1) openArticle(flatBook[index + 1].path); + }); + + if (elements.tocToggle) { + elements.tocToggle.addEventListener('click', () => { + document.body.classList.add('toc-collapsed'); + }); + } + + if (elements.floatingTocBtn) { + elements.floatingTocBtn.addEventListener('click', () => { + document.body.classList.remove('toc-collapsed'); + }); + } + + window.addEventListener('scroll', updateReadingProgress, { passive: true }); + window.addEventListener('resize', () => { + if (!isMobileViewport()) { + setSidebarOpen(false); + } + }); + + window.addEventListener('popstate', (event) => { + const target = event.state?.path; + if (target) { + openArticle(target, true); + return; + } + + const fromHash = decodeURIComponent(window.location.hash.replace(/^#/, '')); + if (fromHash) { + openArticle(fromHash, true); + } + }); +} + +function resolveInitialPath() { + const fromHash = decodeURIComponent(window.location.hash.replace(/^#/, '')); + if (flatBook.some((item) => item.path === fromHash)) return fromHash; + + const lastPage = localStorage.getItem(STORAGE_LAST_PAGE_KEY); + if (lastPage && flatBook.some((item) => item.path === lastPage)) return lastPage; + + return flatBook[0]?.path; +} + +async function bootstrap() { + initTheme(); + initSidebarState(); + bindEvents(); + renderNav(); + + const initialPath = resolveInitialPath(); + if (initialPath) { + await openArticle(initialPath, true); + } +} + +bootstrap(); diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..689e4f6 --- /dev/null +++ b/src/style.css @@ -0,0 +1,634 @@ +:root { + /* Warm, parchment-like light mode palette */ + --bg: #F9F5EC; + --surface: rgba(253, 248, 237, 0.75); + --surface-solid: #FDF8ED; + --text: #3E3124; + --heading: #2C2219; + --muted: #7E6C56; + --line: #E8DDCA; + --accent: #D35400; + --accent-hover: #A04000; + --accent-soft: rgba(211, 84, 0, 0.08); + --accent-glow: rgba(211, 84, 0, 0.15); + + --radius-sm: 8px; + --radius-md: 12px; + --radius-lg: 16px; + --shadow-sm: 0 1px 2px 0 rgba(70, 50, 30, 0.05); + --shadow-md: 0 4px 6px -1px rgba(70, 50, 30, 0.08), 0 2px 4px -1px rgba(70, 50, 30, 0.04); + --shadow-glow: 0 0 20px var(--accent-glow); + --glass-blur: blur(12px); +} + +:root[data-theme='dark'] { + /* Warm dark/sepia mode palette for reading */ + --bg: #1E1A17; + --surface: rgba(43, 38, 33, 0.7); + --surface-solid: #2B2621; + --text: #CFC3AD; + --heading: #EAE2D1; + --muted: #8E8271; + --line: #453E35; + --accent: #D98850; + --accent-hover: #ED9A5E; + --accent-soft: rgba(217, 136, 80, 0.12); + --accent-glow: rgba(217, 136, 80, 0.15); + + --shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.4); + --shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.5), 0 2px 4px -1px rgba(0, 0, 0, 0.3); +} + +* { box-sizing: border-box; } +html, body { + margin: 0; min-height: 100vh; +} +body { + background-color: var(--bg); + background-image: + radial-gradient(circle at 15% 50%, var(--accent-glow), transparent 25%), + radial-gradient(circle at 85% 30%, var(--accent-glow), transparent 25%); + background-attachment: fixed; + color: var(--text); + font-family: 'Outfit', 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; + line-height: 1.6; + -webkit-font-smoothing: antialiased; + transition: background-color 0.4s ease, color 0.4s ease; +} + +h1, h2, h3, h4, h5, h6 { + font-family: 'ZCOOL XiaoWei', 'STKaiti', serif; + color: var(--heading); + margin-top: 0; +} + +.site-shell { + display: flex; + min-height: 100vh; +} + + +.sidebar { + width: 300px; + flex-shrink: 0; + border-right: 1px solid var(--line); + background: var(--surface); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + padding: 24px 20px; + position: sticky; + top: 0; + max-height: 100vh; + overflow-y: auto; + overflow-x: hidden; + display: flex; + flex-direction: column; + transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1), padding 0.3s ease; + will-change: width, transform; + transform: translateZ(0); /* force GPU acceleration */ + z-index: 40; +} + +.sidebar::-webkit-scrollbar { width: 4px; } +.sidebar::-webkit-scrollbar-track { background: transparent; } +.sidebar::-webkit-scrollbar-thumb { background: var(--line); border-radius: 4px; } +.sidebar:hover::-webkit-scrollbar-thumb { background: var(--muted); } + +.sidebar-top-actions { + display: flex; + justify-content: flex-end; + margin-bottom: 20px; +} + +.icon-btn { + background: transparent; + border: 1px solid transparent; + color: var(--muted); + width: 36px; + height: 36px; + border-radius: var(--radius-sm); + display: flex; + align-items: center; + justify-content: center; + cursor: pointer; + transition: all 0.2s ease; + padding: 0; +} +.icon-btn svg { width: 20px; height: 20px; fill: currentColor; } + +.icon-btn:hover { + background: var(--surface-solid); + border-color: var(--line); + color: var(--accent); + transform: translateY(-1px); + box-shadow: var(--shadow-sm); +} + +body.sidebar-collapsed .sidebar { + width: 80px; + padding: 24px 12px; + align-items: center; +} + +body.sidebar-collapsed .brand h1, +body.sidebar-collapsed .brand-kicker, +body.sidebar-collapsed .sidebar-actions, +body.sidebar-collapsed .book-nav { + display: none; +} + +.brand { + margin-bottom: 24px; +} +.brand-kicker { + margin: 0 0 4px; + font-size: 0.75rem; + text-transform: uppercase; + letter-spacing: 0.15em; + color: var(--accent); + font-weight: 700; +} +.brand h1 { + margin: 0; + line-height: 1.2; + font-size: 1.75rem; + background: linear-gradient(135deg, var(--heading) 0%, var(--muted) 100%); + -webkit-background-clip: text; + background-clip: text; + -webkit-text-fill-color: transparent; +} + +.sidebar-actions { + display: flex; + gap: 8px; + margin-bottom: 24px; +} + +.search-wrapper { + position: relative; + flex: 1; +} +.search-wrapper svg { + position: absolute; + left: 12px; + top: 50%; + transform: translateY(-50%); + width: 16px; + height: 16px; + fill: var(--muted); +} +input[type="search"] { + width: 100%; + background: var(--surface-solid); + border: 1px solid var(--line); + color: var(--text); + border-radius: var(--radius-sm); + padding: 10px 12px 10px 36px; + font: inherit; + font-size: 0.9rem; + transition: all 0.2s ease; + outline: none; +} +input[type="search"]:focus { + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-soft); +} + +.book-nav ul { + list-style: none; + padding: 0; + margin: 0; +} +.group-title { + margin: 16px 0 8px; + font-size: 0.8rem; + color: var(--muted); + text-transform: uppercase; + letter-spacing: 0.05em; + font-weight: 600; + padding-left: calc(var(--level, 0) * 12px + 12px); +} +.nav-item { + width: 100%; + text-align: left; + border: none; + background: transparent; + color: var(--text); + border-radius: var(--radius-sm); + padding: 8px 12px; + padding-left: calc(12px + var(--level, 0) * 12px); + margin: 2px 0; + font-size: 0.95rem; + cursor: pointer; + transition: all 0.2s ease; + position: relative; + display: flex; + align-items: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.nav-item::before { + content: ''; + position: absolute; + left: 0; + top: 50%; + transform: translateY(-50%) scaleY(0); + width: 3px; + height: 16px; + background: var(--accent); + border-radius: 0 4px 4px 0; + transition: transform 0.2s ease; +} +.nav-item:hover { + background: var(--accent-soft); + color: var(--accent); +} +.nav-item.active { + background: var(--accent-soft); + color: var(--accent); + font-weight: 600; +} +.nav-item.active::before { + transform: translateY(-50%) scaleY(1); +} + +.content { + padding: 0; + position: relative; + display: flex; + flex-direction: column; + flex: 1; + min-width: 0; /* Prevents flex flex grow bug */ +} + +.reading-progress { + position: sticky; + top: 0; + z-index: 50; + height: 3px; + background: transparent; + width: 100%; +} +.reading-progress span { + display: block; + width: 100%; + height: 100%; + transform-origin: left center; + transform: scaleX(0); + background: linear-gradient(90deg, var(--accent), #10B981); + box-shadow: 0 0 10px var(--accent-glow); + transition: transform 0.1s linear; +} + +.content-inner { + padding: clamp(24px, 5vw, 60px); + max-width: 900px; + width: 100%; + margin: 0 auto; + flex: 1; +} + +.article-toolbar { + display: flex; + justify-content: space-between; + align-items: flex-end; + border-bottom: 1px solid var(--line); + padding-bottom: 20px; + margin-bottom: 32px; + gap: 16px; +} +.kicker { + margin: 0 0 8px; + font-size: 0.8rem; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--muted); + font-weight: 500; +} +.article-toolbar h2 { + margin: 0; + font-size: clamp(2rem, 4vw, 2.75rem); + line-height: 1.1; +} + +.pager { display: flex; gap: 12px; } +.btn-secondary { + background: var(--surface-solid); + border: 1px solid var(--line); + color: var(--text); + padding: 8px 16px; + border-radius: var(--radius-sm); + font-size: 0.9rem; + font-weight: 500; + cursor: pointer; + transition: all 0.2s ease; + display: flex; + align-items: center; + gap: 6px; +} +.btn-secondary:disabled { + opacity: 0.5; + cursor: not-allowed; + transform: none; +} +.btn-secondary:not(:disabled):hover { + background: var(--surface); + border-color: var(--accent); + color: var(--accent); + transform: translateY(-1px); + box-shadow: var(--shadow-sm); +} + +.article { + font-size: 1.1rem; + line-height: 1.8; + color: var(--text); + animation: fade-up 0.5s cubic-bezier(0.16, 1, 0.3, 1) forwards; + opacity: 0; + transform: translateY(10px); +} +.article h1, .article h2, .article h3, .article h4 { + color: var(--heading); + font-weight: 700; + margin-top: 2em; + margin-bottom: 0.75em; +} +.article h2 { font-size: 1.8rem; border-bottom: 1px solid var(--line); padding-bottom: 0.5em; } +.article h3 { font-size: 1.4rem; } +.article h4 { font-size: 1.2rem; } + +.article p { + margin-top: 0; + margin-bottom: 1.5em; +} +.article a { + color: var(--accent); + text-decoration: none; + font-weight: 500; + border-bottom: 1px solid transparent; + transition: border-color 0.2s; +} +.article a:hover { + border-color: var(--accent); +} + +.article ul, .article ol { + margin-top: 0; + margin-bottom: 1.5em; + padding-left: 1.5em; +} +.article li { margin-bottom: 0.5em; } + +.article blockquote { + margin: 1.5em 0; + padding: 1rem 1.5rem; + border-left: 4px solid var(--accent); + background: var(--accent-soft); + border-radius: 0 var(--radius-md) var(--radius-md) 0; + color: var(--text); + font-style: italic; +} +.article blockquote p:last-child { margin-bottom: 0; } + +.article code { + font-family: 'JetBrains Mono', 'Fira Code', Consolas, monospace; + background: var(--surface-solid); + border: 1px solid var(--line); + padding: 0.2em 0.4em; + border-radius: 4px; + font-size: 0.85em; + color: var(--accent); +} + +.article pre { + margin: 1.5em 0; + padding: 1.25rem; + background: #0f172a; + color: #e2e8f0; + border-radius: var(--radius-lg); + overflow-x: auto; + box-shadow: inset 0 0 0 1px rgba(255,255,255,0.1), var(--shadow-md); +} +.article pre code { + background: transparent; + border: none; + padding: 0; + color: inherit; + font-size: 0.9em; +} + +.article img { + max-width: 100%; + height: auto; + border-radius: var(--radius-md); + box-shadow: var(--shadow-md); + margin: 1.5em auto; + display: block; +} + +.article table { + width: 100%; + border-collapse: collapse; + margin: 1.5em 0; +} +.article th, .article td { + border: 1px solid var(--line); + padding: 0.75rem 1rem; + text-align: left; +} +.article th { + background: var(--surface-solid); + color: var(--heading); + font-weight: 600; +} + +.toc-panel { + position: fixed; + top: 100px; + right: max(20px, calc((100vw - 320px - 900px) / 2 - 280px)); + width: 260px; + max-height: calc(100vh - 140px); + background: var(--surface); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + border: 1px solid var(--line); + border-radius: var(--radius-md); + padding: 16px 20px; + box-shadow: var(--shadow-md); + overflow-y: auto; + z-index: 30; + transition: opacity 0.3s, transform 0.3s; +} + +.toc-head { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 12px; + padding-bottom: 8px; + border-bottom: 1px solid var(--line); +} +.toc-title { + margin: 0; + font-size: 0.75rem; + font-weight: 700; + text-transform: uppercase; + letter-spacing: 0.1em; + color: var(--muted); +} +.toc-empty { + color: var(--muted); + font-size: 0.9rem; + font-style: italic; +} + +body.toc-collapsed .toc-panel { + transform: translateX(calc(100% + 40px)); + opacity: 0; + pointer-events: none; +} + +.floating-toc-btn { + position: fixed; + bottom: 30px; + right: 30px; + width: 48px; + height: 48px; + border-radius: 50%; + background: var(--accent); + color: white; + display: flex; + align-items: center; + justify-content: center; + box-shadow: var(--shadow-md), var(--shadow-glow); + cursor: pointer; + border: none; + z-index: 29; + transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1); + opacity: 0; + pointer-events: none; + transform: scale(0.8); +} +.floating-toc-btn svg { width: 24px; height: 24px; fill: currentColor; } +body.toc-collapsed .floating-toc-btn { + opacity: 1; + pointer-events: auto; + transform: scale(1); +} +.floating-toc-btn:hover { + transform: scale(1.1); + background: var(--accent-hover); +} + +.toc ul { + list-style: none; + padding: 0; + margin: 0; +} +.toc li { + margin: 8px 0; + font-size: 0.9rem; + line-height: 1.4; +} +.toc a { + color: var(--muted); + text-decoration: none; + display: block; + transition: color 0.2s, transform 0.2s; + border-left: 2px solid transparent; + padding-left: 8px; +} +.toc a:hover { + color: var(--accent); + border-left-color: var(--accent); + transform: translateX(2px); +} + +.lv-h2 { margin-left: 0; font-weight: 500; } +.lv-h3 { margin-left: 12px; font-size: 0.85rem; } +.lv-h4 { margin-left: 24px; font-size: 0.8rem; } + +.mobile-header { + display: none; +} +.sidebar-backdrop { + display: none; +} + +@keyframes fade-up { + 0% { opacity: 0; transform: translateY(15px); } + 100% { opacity: 1; transform: translateY(0); } +} + +@media (max-width: 1280px) { + .toc-panel { + display: none; + } +} + +@media (max-width: 960px) { + .site-shell { + grid-template-columns: 1fr; + } + body.sidebar-collapsed .site-shell { + grid-template-columns: 1fr; + } + + .sidebar { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 320px; + transform: translateX(-100%); + box-shadow: var(--shadow-md); + border-right: none; + background: var(--surface-solid); + } + body.sidebar-open .sidebar { + transform: translateX(0); + } + + .sidebar-backdrop { + position: fixed; + inset: 0; + z-index: 35; + background: rgba(0, 0, 0, 0.4); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + opacity: 0; + pointer-events: none; + transition: opacity 0.3s; + display: block; + } + body.sidebar-open .sidebar-backdrop { + opacity: 1; + pointer-events: auto; + } + + .mobile-header { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px 20px; + background: var(--surface); + backdrop-filter: var(--glass-blur); + -webkit-backdrop-filter: var(--glass-blur); + border-bottom: 1px solid var(--line); + position: sticky; + top: 0; + z-index: 30; + } + + .content-inner { + padding: 24px 20px; + } + .article-toolbar { + flex-direction: column; + align-items: flex-start; + } + .article-toolbar h2 { + font-size: 1.8rem; + } +} diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..39ec092 --- /dev/null +++ b/vite.config.js @@ -0,0 +1,5 @@ +import { defineConfig } from 'vite'; + +export default defineConfig({ + base: process.env.VITE_BASE_PATH || '/' +}); From 2d6ffad7646db0f85fd97586cd4443131653c506 Mon Sep 17 00:00:00 2001 From: GuGuGuGun Date: Wed, 1 Apr 2026 09:50:35 +0800 Subject: [PATCH 2/8] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8Ddepoly?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/deploy-pages.yml | 3 +++ README.md | 9 --------- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/.github/workflows/deploy-pages.yml b/.github/workflows/deploy-pages.yml index 7f7bc27..d5fffe5 100644 --- a/.github/workflows/deploy-pages.yml +++ b/.github/workflows/deploy-pages.yml @@ -22,6 +22,9 @@ jobs: - name: Checkout uses: actions/checkout@v4 + - name: Setup Pages + uses: actions/configure-pages@v5 + - name: Setup Node uses: actions/setup-node@v4 with: diff --git a/README.md b/README.md index eb19259..2c2bc1a 100644 --- a/README.md +++ b/README.md @@ -28,8 +28,6 @@ │ ├─ main.js │ ├─ style.css │ └─ bookManifest.js -├─ docs/plans/ -│ └─ 2026-04-01-book-web-design.md ├─ 第一部分-基础篇/ ├─ 第二部分-核心系统篇/ ├─ 第三部分-高级模式篇/ @@ -72,13 +70,6 @@ npm run preview - 章节目录由 src/bookManifest.js 统一维护 - 新增章节时,请同步更新 src/bookManifest.js -## 贡献方式 - -欢迎通过 Issue 或 Pull Request 参与改进: - -- 修复页面显示问题或阅读体验问题 -- 优化导航、样式与交互 -- 补充构建与部署文档 ## 许可证 From f43b02991c6e22fc596f958c8761184a9f31df3f Mon Sep 17 00:00:00 2001 From: GuGuGuGun <107943071+GuGuGuGun@users.noreply.github.com> Date: Wed, 1 Apr 2026 09:55:11 +0800 Subject: [PATCH 3/8] =?UTF-8?q?docs:=20=E6=B7=BB=E5=8A=A0L=E7=AB=99?= =?UTF-8?q?=E5=8F=8B=E9=93=BE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Added a new section for friendly links in the README. --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 2c2bc1a..67bcbf8 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,8 @@ npm run preview - 章节目录由 src/bookManifest.js 统一维护 - 新增章节时,请同步更新 src/bookManifest.js +## 友链 +- 灌注L站喵,https://linux.do/ ## 许可证 From c9e80ebd5d563c4e4a724800bf9c19255b125b0e Mon Sep 17 00:00:00 2001 From: GuGuGuGun <107943071+GuGuGuGun@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:41:52 +0800 Subject: [PATCH 4/8] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0readme?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 5e33e17..4e644de 100644 --- a/README.md +++ b/README.md @@ -2,17 +2,8 @@ # 解码 Agent Harness -### Claude Code 架构深度剖析 -**不做使用教程,不列 Prompt 技巧——拆解 Agent 的骨架与神经。** - -[![Stars](https://img.shields.io/github/stars/lintsinghua/claude-code-book?style=social)](https://github.com/lintsinghua/claude-code-book/stargazers) -[![Website](https://img.shields.io/website?url=https%3A%2F%2Flintsinghua.github.io&label=在线阅读)](https://lintsinghua.github.io/) -[![License](https://img.shields.io/badge/license-CC%20BY--NC--SA%204.0-lightgrey)](LICENSE) -[![Chapters](https://img.shields.io/badge/章节-15-blue)]() -[![Language](https://img.shields.io/badge/语言-中文-informational)]() - -### [在线阅读 →](https://lintsinghua.github.io/) +### [在线阅读 →](https://gugugugun.github.io) @@ -195,14 +186,9 @@ npm run preview ## 致谢 [Linux.Do](https://linux.do/) 社区 - +[lintsinghua/claude-code-book](https://github.com/lintsinghua/claude-code-book) [![CC BY-NC-SA 4.0](https://img.shields.io/badge/license-CC%20BY--NC--SA%204.0-lightgrey)](https://creativecommons.org/licenses/by-nc-sa/4.0/) -## 许可证 - -书籍内容与相关版权遵循原项目及其许可证约定;如需转载或二次发布,请先阅读并遵守原仓库许可条款。 -- 章节目录由 src/bookManifest.js 统一维护 -- 新增章节时,请同步更新 src/bookManifest.js ## 友链 - 灌注L站喵,https://linux.do/ From f3a5942a176475fe6572a6091e8db4780a34d7bf Mon Sep 17 00:00:00 2001 From: GuGuGuGun <107943071+GuGuGuGun@users.noreply.github.com> Date: Wed, 1 Apr 2026 10:58:51 +0800 Subject: [PATCH 5/8] =?UTF-8?q?docs:=20=E7=A7=BB=E9=99=A4=E5=9B=BE?= =?UTF-8?q?=E7=89=87?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Remove image from README --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 4e644de..be92646 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,6 @@ -image ## 这本书讲什么 From c1bd05d3b18ad3929ad53b3126e5c2fca0505815 Mon Sep 17 00:00:00 2001 From: GuGuGuGun <107943071+GuGuGuGun@users.noreply.github.com> Date: Wed, 1 Apr 2026 11:12:51 +0800 Subject: [PATCH 6/8] =?UTF-8?q?docs:=20=E6=9B=B4=E6=96=B0=E9=93=BE?= =?UTF-8?q?=E6=8E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index be92646..88a437f 100644 --- a/README.md +++ b/README.md @@ -3,7 +3,7 @@ # 解码 Agent Harness -### [在线阅读 →](https://gugugugun.github.io) +### [在线阅读 →](https://gugugugun.github.io/claude-code-book) From aae71df2cfe2400d95c6b3594eddbae3b9ad3404 Mon Sep 17 00:00:00 2001 From: GuGuGuGun Date: Wed, 1 Apr 2026 16:14:27 +0800 Subject: [PATCH 7/8] =?UTF-8?q?fix:=20=E4=BF=AE=E5=A4=8D=E5=8F=B3=E4=BE=A7?= =?UTF-8?q?=E9=A1=B5=E5=86=85=E7=9B=AE=E5=BD=95=E6=98=A0=E5=B0=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 17 +++++++++++++++++ src/style.css | 1 + 2 files changed, 18 insertions(+) diff --git a/src/main.js b/src/main.js index 4a8ea2b..a44e776 100644 --- a/src/main.js +++ b/src/main.js @@ -349,6 +349,23 @@ function bindEvents() { if (index >= 0 && index < flatBook.length - 1) openArticle(flatBook[index + 1].path); }); + if (elements.toc) { + elements.toc.addEventListener('click', (event) => { + const link = event.target.closest('a'); + if (!link) return; + + const href = link.getAttribute('href'); + if (href && href.startsWith('#')) { + event.preventDefault(); + const targetId = href.substring(1); + const targetEl = document.getElementById(targetId); + if (targetEl) { + targetEl.scrollIntoView({ behavior: 'smooth' }); + } + } + }); + } + if (elements.tocToggle) { elements.tocToggle.addEventListener('click', () => { document.body.classList.add('toc-collapsed'); diff --git a/src/style.css b/src/style.css index 689e4f6..7360e46 100644 --- a/src/style.css +++ b/src/style.css @@ -353,6 +353,7 @@ input[type="search"]:focus { font-weight: 700; margin-top: 2em; margin-bottom: 0.75em; + scroll-margin-top: 80px; } .article h2 { font-size: 1.8rem; border-bottom: 1px solid var(--line); padding-bottom: 0.5em; } .article h3 { font-size: 1.4rem; } From 39013d97511ff404709120a2ff6be448ad8ba01a Mon Sep 17 00:00:00 2001 From: GuGuGuGun Date: Wed, 1 Apr 2026 16:24:10 +0800 Subject: [PATCH 8/8] =?UTF-8?q?feat:=20=E4=BC=98=E5=8C=96=E5=8F=B3?= =?UTF-8?q?=E4=BE=A7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main.js | 37 ++++++++++++++++++++++++++++++------- 1 file changed, 30 insertions(+), 7 deletions(-) diff --git a/src/main.js b/src/main.js index a44e776..f1e4ecc 100644 --- a/src/main.js +++ b/src/main.js @@ -192,7 +192,7 @@ function renderToc() { if (!heading.id) { heading.id = slugify(heading.textContent || 'section'); } - return `
  • ${heading.textContent}
  • `; + return `
  • ${heading.textContent}
  • `; }) .join('')} @@ -313,6 +313,24 @@ function bindEvents() { } }); + elements.article.addEventListener('click', (event) => { + const link = event.target.closest('a'); + if (!link) return; + + const href = link.getAttribute('href'); + if (href && href.startsWith('#')) { + event.preventDefault(); + const targetId = href.substring(1); + let targetEl = document.getElementById(targetId); + if (!targetEl) { + try { targetEl = document.getElementById(decodeURIComponent(targetId)); } catch(e) {} + } + if (targetEl) { + targetEl.scrollIntoView({ behavior: 'smooth' }); + } + } + }); + elements.searchInput.addEventListener('input', () => { renderNav(elements.searchInput.value); }); @@ -351,14 +369,19 @@ function bindEvents() { if (elements.toc) { elements.toc.addEventListener('click', (event) => { - const link = event.target.closest('a'); + const link = event.target.closest('a[data-target]'); if (!link) return; - const href = link.getAttribute('href'); - if (href && href.startsWith('#')) { - event.preventDefault(); - const targetId = href.substring(1); - const targetEl = document.getElementById(targetId); + event.preventDefault(); + const targetId = link.getAttribute('data-target'); + if (targetId) { + let targetEl = document.getElementById(targetId); + if (!targetEl) { + try { + targetEl = document.getElementById(decodeURIComponent(targetId)); + } catch(e) {} + } + if (targetEl) { targetEl.scrollIntoView({ behavior: 'smooth' }); }