From c8a4ebd93085176ee1607440fd7ad1841963557c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AI=E4=B8=8D=E6=AD=A2=E8=AF=AD?= <12096460+jnMetaCode@users.noreply.github.com> Date: Sat, 20 Jun 2026 04:06:02 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat(site):=20=E6=96=B0=E5=A2=9E=E9=A1=B9?= =?UTF-8?q?=E7=9B=AE=E5=AE=98=E7=BD=91=EF=BC=88=E9=9D=99=E6=80=81=E7=AB=99?= =?UTF-8?q?=E7=82=B9=E7=94=9F=E6=88=90=20+=20Cloudflare=20Pages=20?= =?UTF-8?q?=E8=87=AA=E5=8A=A8=E9=83=A8=E7=BD=B2=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 为 superpowers-zh 增加官方站点:用 site/build.mjs 从 skills/ 与模板生成 中英双语静态站(42 页:中/英首页 + 20 skill × 2 语言详情),部署到 Cloudflare Pages。 - site/build.mjs + site/md.mjs + site/template/:站点生成器源码 - site/README.md:站点说明 - .gitignore:忽略 site/dist/(构建产物不入库,CI 重建) - .github/workflows/deploy-site.yml:push 到 main 且 site/ 或 skills/ 变更时,构建并部署到 Cloudflare Pages 项目 superpowers-zh-site (需仓库 Secrets:CLOUDFLARE_API_TOKEN + CLOUDFLARE_ACCOUNT_ID) - package.json:加 site / site:deploy 脚本 验证:node site/build.mjs 构建成功,生成 42 页到 site/dist; site/dist 已确认被 gitignore 排除、未入库;build.mjs 无硬编码密钥。 --- .github/workflows/deploy-site.yml | 32 ++ .gitignore | 3 + package.json | 2 + site/README.md | 77 ++++ site/build.mjs | 559 ++++++++++++++++++++++++++++++ site/md.mjs | Bin 0 -> 5034 bytes site/template/app.js | 92 +++++ site/template/styles.css | 448 ++++++++++++++++++++++++ 8 files changed, 1213 insertions(+) create mode 100644 .github/workflows/deploy-site.yml create mode 100644 site/README.md create mode 100644 site/build.mjs create mode 100644 site/md.mjs create mode 100644 site/template/app.js create mode 100644 site/template/styles.css diff --git a/.github/workflows/deploy-site.yml b/.github/workflows/deploy-site.yml new file mode 100644 index 0000000..2992663 --- /dev/null +++ b/.github/workflows/deploy-site.yml @@ -0,0 +1,32 @@ +name: Deploy site to Cloudflare Pages + +# 官网自动部署:site/ 或 skills/ 变更时重建并发布。 +# 需在仓库 Settings → Secrets 配置 CLOUDFLARE_API_TOKEN 与 CLOUDFLARE_ACCOUNT_ID。 +on: + push: + branches: [main] + paths: + - 'site/**' + - 'skills/**' + - '.github/workflows/deploy-site.yml' + workflow_dispatch: + +jobs: + deploy: + runs-on: ubuntu-latest + permissions: + contents: read + deployments: write + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Build site + run: node site/build.mjs + - name: Deploy to Cloudflare Pages + uses: cloudflare/wrangler-action@v3 + with: + apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }} + accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }} + command: pages deploy site/dist --project-name=superpowers-zh-site diff --git a/.gitignore b/.gitignore index 39c70c5..8f6e23d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,9 @@ node_modules/ inspo triage/ +# 官网生成产物(源在 site/build.mjs + site/template/) +site/dist/ + # 安装器生成的 skills 副本(源在 skills/ 目录) .codex/skills/ .gemini/skills/ diff --git a/package.json b/package.json index 3fd26ae..c012d9f 100644 --- a/package.json +++ b/package.json @@ -11,6 +11,8 @@ "superpowers-zh": "bin/superpowers-zh.js" }, "scripts": { + "site": "node site/build.mjs", + "site:deploy": "node site/build.mjs && wrangler pages deploy site/dist --project-name=superpowers-zh-site", "version": "node scripts/sync-plugin-version.js && git add .claude-plugin/plugin.json .claude-plugin/marketplace.json .cursor-plugin/plugin.json .codex-plugin/plugin.json gemini-extension.json" }, "files": [ diff --git a/site/README.md b/site/README.md new file mode 100644 index 0000000..fd1a52f --- /dev/null +++ b/site/README.md @@ -0,0 +1,77 @@ +# superpowers-zh 官网 + +零依赖静态站生成器。一条命令产出**中/英双语**站点 + 每个 skill 的**详情(操作文档)页**,内容全部来自 `../skills/*/SKILL.md`,与源文件同步、不会漂移。 + +## 构建 / 预览 + +```bash +node site/build.mjs # 生成 site/dist/(42 个页面) +npx serve site/dist # 本地预览(或 python3 -m http.server) +``` + +产物结构: + +``` +dist/ + index.html # 中文首页 + en/index.html # 英文首页 + skills/.html # 中文 skill 详情(操作文档)× 20 + en/skills/.html # 英文 skill 详情 × 20 + styles.css app.js # 深/浅主题 + 交互(原生 JS,零依赖) + assets/ # 图标 + 赞助商 logo + _headers # Cloudflare Pages 缓存策略 +``` + +## 特性 + +- **中英双语**:服务端生成两套真实页面(非前端切换),SEO 友好、无闪烁。导航栏「中文 / EN」互切。 +- **深 / 浅主题**:CSS 变量 + localStorage 记忆,内联脚本在首屏前生效,无白屏闪烁。导航栏 ◐ 按钮切换。 +- **Skill 详情页**:内置零依赖 Markdown 渲染器(`md.mjs`),把每个 `SKILL.md` 渲染成完整操作文档,支持标题/代码块/表格/列表/引用。 +- **安装命令生成器、Skill 搜索 / 分类筛选、一键复制** —— 全部原生 JS。 + +## 改内容改哪里 + +| 想改什么 | 改哪里 | +|---|---| +| Skill 正文 / 文档内容 | **源头** `skills//SKILL.md`(改完重新 `node site/build.mjs`) | +| Skill 中/英标题、英文简介、分组 | `site/build.mjs` 的 `SKILL_META` | +| 首页所有文案(中/英) | `site/build.mjs` 的 `T.zh` / `T.en` | +| 支持工具 / 安装命令 | `site/build.mjs` 的 `TOOLS`(与 `bin/superpowers-zh.js` 的 `TARGETS` 对齐) | +| 样式 / 主题 | `site/template/styles.css` | +| 交互逻辑 | `site/template/app.js` | +| Markdown 渲染规则 | `site/md.mjs` | + +## 部署到 Cloudflare Pages + +发布目录 `site/dist`,三选一: + +### 方式 A — 连接仓库自动构建(推荐,无需密钥) + +Cloudflare Dashboard → Pages → 连接 GitHub 仓库 `jnMetaCode/superpowers-zh`: + +- **Build command**:`node site/build.mjs` +- **Build output directory**:`site/dist` + +之后每次 push 自动重建发布。绑自定义域名(建议 `superpowers.aibuzhiyu.com`)。 + +### 方式 B — 命令行一键发布 + +```bash +npx wrangler login # 浏览器登录(交互,仅首次) +npm run site:deploy # = 构建 + wrangler pages deploy site/dist +``` + +> 在本会话中可用 `!` 前缀直接登录:`!npx wrangler login` + +### 方式 C — GitHub Actions 自动部署 + +仓库已含 `.github/workflows/deploy-site.yml`。在 **Settings → Secrets and variables → Actions** 配置: + +- `CLOUDFLARE_API_TOKEN`(Pages 编辑权限) +- `CLOUDFLARE_ACCOUNT_ID` + +之后 `site/` 或 `skills/` 变更 push 到 `main` 即自动部署。 + +--- + +> 国内访问优先用 Cloudflare Pages + 自定义域名;GitHub Pages 在国内访问较慢,不建议作为主站。 diff --git a/site/build.mjs b/site/build.mjs new file mode 100644 index 0000000..e6472c0 --- /dev/null +++ b/site/build.mjs @@ -0,0 +1,559 @@ +#!/usr/bin/env node +// superpowers-zh 官网生成器 —— 零依赖。 +// 服务端生成中/英两套静态页 + 每个 skill 的详情(操作文档)页。 +// skill 卡片与详情正文均直接读取 ../skills/*/SKILL.md,与源文件同步、不漂移。 + +import { + readFileSync, writeFileSync, mkdirSync, readdirSync, + existsSync, copyFileSync, rmSync, +} from 'fs'; +import { resolve, dirname, join } from 'path'; +import { fileURLToPath } from 'url'; +import { renderMarkdown } from './md.mjs'; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const ROOT = resolve(__dirname, '..'); +const SKILLS_DIR = join(ROOT, 'skills'); +const DIST = join(__dirname, 'dist'); +const TEMPLATE = join(__dirname, 'template'); +const PKG = JSON.parse(readFileSync(join(ROOT, 'package.json'), 'utf8')); + +const esc = s => String(s).replace(/&/g, '&').replace(//g, '>'); + +// ---- frontmatter 解析 ---- +function parseFrontmatter(md) { + const m = md.match(/^---\n([\s\S]*?)\n---/); + if (!m) return {}; + const out = {}; + for (const line of m[1].split('\n')) { + const kv = line.match(/^(\w[\w-]*):\s*(.*)$/); + if (!kv) continue; + let val = kv[2].trim(); + if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) { + val = val.slice(1, -1); + } + out[kv[1]] = val; + } + return out; +} + +// ---- skill 展示元数据:中/英标题 + 英文简介 + 分组 ---- +const SKILL_META = { + 'using-superpowers': { group: 'meta', title: '使用 Superpowers · 引导', titleEn: 'Using Superpowers · Bootstrap', descEn: 'The bootstrap skill — establishes how to discover and invoke skills at the start of every conversation.' }, + 'brainstorming': { group: 'flow', title: '头脑风暴', titleEn: 'Brainstorming', descEn: 'Explore intent, requirements and design before any creative work — feature, component or behavior change.' }, + 'writing-plans': { group: 'flow', title: '编写实现计划', titleEn: 'Writing Plans', descEn: 'Turn a spec into a step-by-step implementation plan before writing any code.' }, + 'executing-plans': { group: 'flow', title: '执行计划', titleEn: 'Executing Plans', descEn: 'Execute a written plan in a separate session with review checkpoints.' }, + 'subagent-driven-development': { group: 'flow', title: '子代理驱动开发', titleEn: 'Subagent-Driven Dev', descEn: 'Run a plan of independent tasks within the current session via subagents.' }, + 'dispatching-parallel-agents': { group: 'flow', title: '并行代理调度', titleEn: 'Dispatching Parallel Agents', descEn: 'Fan out 2+ independent tasks with no shared state or ordering dependency.' }, + 'workflow-runner': { group: 'flow', title: '工作流运行器', titleEn: 'Workflow Runner', descEn: 'Run agency-orchestrator YAML workflows directly using the current session LLM — no API key.' }, + 'test-driven-development': { group: 'quality', title: '测试驱动开发 · TDD', titleEn: 'Test-Driven Development', descEn: 'Write the test before the implementation, for every feature and bug fix.' }, + 'systematic-debugging': { group: 'quality', title: '系统化调试', titleEn: 'Systematic Debugging', descEn: 'Reproduce and locate the root cause before proposing any fix.' }, + 'verification-before-completion':{ group: 'quality', title: '完成前验证', titleEn: 'Verification Before Completion', descEn: 'Run verification and back every claim with evidence before saying it is done.' }, + 'requesting-code-review': { group: 'review', title: '发起代码审查', titleEn: 'Requesting Code Review', descEn: 'Validate work against requirements before merging or shipping.' }, + 'receiving-code-review': { group: 'review', title: '接收代码审查', titleEn: 'Receiving Code Review', descEn: 'Apply review feedback with technical rigor — verify, don\'t blindly comply.' }, + 'using-git-worktrees': { group: 'git', title: 'Git Worktree 隔离', titleEn: 'Using Git Worktrees', descEn: 'Start isolated feature work in a dedicated git worktree.' }, + 'finishing-a-development-branch':{ group: 'git', title: '收尾开发分支', titleEn: 'Finishing a Branch', descEn: 'Wrap up finished work via structured merge / PR / cleanup options.' }, + 'writing-skills': { group: 'meta', title: '编写 Skill', titleEn: 'Writing Skills', descEn: 'Create, edit and validate skills before deploying them.' }, + 'mcp-builder': { group: 'meta', title: 'MCP 服务器构建', titleEn: 'MCP Builder', descEn: 'Methodology for building production-grade MCP servers that connect AI to external tools.' }, + 'chinese-code-review': { group: 'china', title: '中文代码审查', titleEn: 'Chinese Code Review', descEn: 'Chinese review phrasing, severity tiers, and common anti-patterns in domestic teams.' }, + 'chinese-commit-conventions': { group: 'china', title: '中文提交规范', titleEn: 'Chinese Commit Conventions', descEn: 'Conventional Commits adapted for Chinese, with commitlint / husky / changelog templates.' }, + 'chinese-documentation': { group: 'china', title: '中文文档排版', titleEn: 'Chinese Documentation', descEn: 'CN/EN spacing, punctuation, term preservation and Chinese typography conventions.' }, + 'chinese-git-workflow': { group: 'china', title: '国内 Git 平台', titleEn: 'Chinese Git Workflow', descEn: 'Gitee / Coding.net / JiHu GitLab / CNB access, credentials, CI and mirror sync.' }, +}; + +const GROUPS = [ + { id: 'all', zh: '全部', en: 'All' }, + { id: 'flow', zh: '工作流程', en: 'Workflow' }, + { id: 'quality', zh: '质量 · 测试 · 调试', en: 'Quality' }, + { id: 'review', zh: '代码审查', en: 'Code Review' }, + { id: 'git', zh: 'Git · 分支', en: 'Git' }, + { id: 'meta', zh: '元 · 构建', en: 'Meta' }, + { id: 'china', zh: '🇨🇳 中国原创', en: '🇨🇳 China-native' }, +]; + +// ---- 支持的工具(与 bin/superpowers-zh.js 的 TARGETS 对齐) ---- +const TOOLS = [ + { name: 'Claude Code', type: 'CLI', cmd: 'npx superpowers-zh', auto: true }, + { name: 'Cursor', type: 'IDE', cmd: 'npx superpowers-zh', auto: true }, + { name: 'Windsurf', type: 'IDE', cmd: 'npx superpowers-zh', auto: true }, + { name: 'Codex CLI', type: 'CLI', cmd: 'npx superpowers-zh', auto: true }, + { name: 'Gemini CLI', type: 'CLI', cmd: 'npx superpowers-zh', auto: true }, + { name: 'Kiro', type: 'IDE', cmd: 'npx superpowers-zh', auto: true }, + { name: 'Trae', type: 'IDE', cmd: 'npx superpowers-zh', auto: true }, + { name: 'Qoder', type: 'IDE', cmd: 'npx superpowers-zh', auto: true }, + { name: 'Aider', type: 'CLI', cmd: 'npx superpowers-zh', auto: true }, + { name: 'OpenCode', type: 'CLI', cmd: 'npx superpowers-zh', auto: true }, + { name: 'Qwen Code', type: 'IDE', cmd: 'npx superpowers-zh', auto: true }, + { name: 'Antigravity', type: 'CLI', cmd: 'npx superpowers-zh', auto: true }, + { name: 'DeerFlow 2.0', type: 'Agent', cmd: 'npx superpowers-zh', auto: true }, + { name: 'VS Code · Copilot', type: 'IDE', cmd: 'npx superpowers-zh', auto: true }, + { name: 'Copilot CLI', type: 'CLI', cmd: 'npx superpowers-zh --tool copilot', auto: false }, + { name: 'Hermes Agent', type: 'CLI', cmd: 'npx superpowers-zh --tool hermes', auto: false }, + { name: 'Claw Code', type: 'CLI', cmd: 'npx superpowers-zh --tool claw', auto: false }, + { name: 'OpenClaw', type: 'CLI', cmd: 'npx superpowers-zh', auto: true }, +]; + +// ---- 双语文案 ---- +const T = { + zh: { + htmlLang: 'zh-CN', + title: 'superpowers-zh · AI 编程超能力中文增强版', + desc: 'superpowers(159k+ ⭐)完整汉化 + 4 个中国原创 skills,一条 npx 命令为 18 款 AI 编程工具装上系统化工作方法论。', + nav: { why: '特性', install: '安装', skills: 'Skills', tools: '支持工具', faq: 'FAQ', github: 'GitHub ↗' }, + heroBadge: 'superpowers 159k+ ⭐ · 完整汉化 + 中国原创', + heroH1: '给你的 AI 编程工具
装上真正会干活的超能力', + heroLead: '{n} 个经过实战验证的工作方法论 skill —— 从头脑风暴到 TDD,从系统化调试到代码审查。
一条命令,自动识别项目里的工具并安装。', + heroBtn1: '查看安装命令', heroBtn2: 'GitHub 源码', + stats: ['Skills', '中国原创', '支持工具', '当前版本'], + whyTitle: '为什么选择 superpowers-zh?', + whySub: '不是又一套提示词模板 —— 是让 AI 真正按工程方法干活的系统化能力。', + plTitle: '一条龙工作流,每一步都有 skill 把关', + plSub: 'skill 之间彼此衔接,AI 会在合适的节点自动触发对应方法论。', + cmpTitle: '装上之后,AI 不再"上来就写"', + cmpBad: '❌ 没装', cmpGood: '✅ 装了 superpowers-zh', + cmpBadPre: '你:给用户模块加个批量导出功能\nAI:好的,我来实现……(直接开写)\n export async function exportUsers() { … }\n你:等等,格式不对,没分页,\n 大数据量会 OOM……', + cmpGoodPre: '你:给用户模块加个批量导出功能\nAI:先理清需求 —— 导出格式?数据量级?\n 要分页/流式吗?要权限校验吗?\n (触发 brainstorming)\n → 写计划 → TDD → 验证 → 审查', + instTitle: '选你的工具,拿到安装命令', + instSub: '大多数工具 npx superpowers-zh 会自动识别项目目录并安装;识别不出的用 --tool 指定。', + instLabel: '我用的是', + instNoteAuto: '在你的项目根目录运行,自动识别 {name} 并安装。安装后重启工具即可生效。', + instNoteManual: '{name} 无法自动识别,需用 --tool 显式指定。在项目根目录运行,安装后重启工具即可生效。', + skTitle: '{n} 个 Skill,覆盖开发全流程', + skSub: '点击任意卡片查看完整操作文档。', + skSearch: '搜索 skill…(如 调试 / review / TDD)', + skEmpty: '没有匹配的 skill。', + skDetail: '查看文档 →', + tagCn: '中国原创', + ucTitle: '典型使用场景', ucSub: '每个场景背后都是一组协同工作的 skill。', + toolsTitle: '一套 skill,18 款工具通用', toolsSub: '换工具不用换习惯,方法论跟着你走。', + faqTitle: '常见问题', + bookTitle: '装好之后,配上方法论效率翻倍', + bookDesc: '《AI 编程实战 · 方法论三卷书》—— 10 个 AI 编程工具完整教程 + 真实踩坑。在线书 + PDF,永久免费。', + bookBtn: '免费阅读 ↗', + sponsorTitle: '赞助商', + sponsorDesc: '稳定高速的 API 中继服务,为 Claude Code、Codex 等平台提供 API 中继与 AI 生图服务。', + sponsorCta: '🙏 想出现在这里?联系 jnMetaCode@qq.com', + ctaTitle: '准备好让 AI 真正会干活了吗?', + ctaDesc: '一条命令,{n} 个实战方法论装进你的工具。免费、开源、零依赖。', + ctaBtn1: '查看安装命令', ctaBtn2: '⭐ Star on GitHub', + footCols: [ + { h: '产品', links: [['特性', '#why'], ['Skills', '#skills'], ['支持工具', '#tools'], ['FAQ', '#faq']] }, + { h: '资源', links: [['GitHub', 'https://github.com/jnMetaCode/superpowers-zh'], ['npm', 'https://www.npmjs.com/package/superpowers-zh'], ['上游 obra/superpowers', 'https://github.com/obra/superpowers'], ['方法论三卷书', 'https://book.aibuzhiyu.com/']] }, + { h: '社区', links: [['提交 Issue', 'https://github.com/jnMetaCode/superpowers-zh/issues'], ['贡献指南', 'https://github.com/jnMetaCode/superpowers-zh/blob/main/CLAUDE.md']] }, + ], + footTag: 'AI 编程超能力 · 中文增强版 · MIT License', + copyright: '© 2026 superpowers-zh · 基于 obra/superpowers 的中文增强 fork', + copy: '复制', copied: '已复制 ✓', + backToSkills: '← 返回全部 Skill', + detailInstall: '安装此 skill', + detailSource: '在 GitHub 查看源文件 ↗', + features: [ + { icon: '🧠', t: '20 个实战方法论', d: '不是 prompt 模板,是经过跨会话对抗式压力测试调优的工作方法论 —— 从头脑风暴到 TDD、调试、代码审查。' }, + { icon: '🔌', t: '18 款工具通用', d: '一套 skill,Claude Code / Cursor / Codex / Gemini CLI / Windsurf… 全适配,换工具不用换习惯。' }, + { icon: '⚡', t: '一条命令安装', d: 'npx superpowers-zh 自动识别项目里用的是哪款工具并安装,零配置,装完重启即生效。' }, + { icon: '🇨🇳', t: '中国原创 Skills', d: '中文代码审查话术、中文提交规范、中文文档排版、国内 Git 平台(Gitee/Coding/极狐)配置 —— 上游没有。' }, + { icon: '📖', t: '完整汉化上游', d: '同步 obra/superpowers(159k+ ⭐),核心 skill 全部中文母语化,不是机翻,是逐条校准。' }, + { icon: '🔓', t: '零依赖 · MIT 开源', d: '纯 Markdown skill,不引入任何外部依赖、不联网、不上传代码,按需触发零运行时开销。' }, + ], + pipeline: [ + { n: '头脑风暴', d: '动手前先理清意图与需求' }, + { n: '写计划', d: '把需求拆成可执行步骤' }, + { n: 'TDD', d: '先写测试再写实现' }, + { n: '系统化调试', d: '先复现定位再改' }, + { n: '代码审查', d: '合并前严谨验收' }, + { n: '完成前验证', d: '用证据证明真的好了' }, + ], + usecases: [ + { tag: '开发新功能', skills: 'brainstorming → writing-plans → TDD', desc: 'AI 先反问需求、写出实现计划,再用测试驱动落地,而不是上来就糊一坨代码。' }, + { tag: '修 Bug', skills: 'systematic-debugging', desc: '强制先复现、定位根因,再提修复方案 —— 杜绝"猜一个改一下"的瞎试循环。' }, + { tag: '提交 / 合并前', skills: 'verification-before-completion → code-review', desc: '必须跑验证命令、拿证据说话,再走一轮代码审查,才允许声称"完成"。' }, + { tag: '国内团队协作', skills: 'chinese-commit-conventions → chinese-code-review', desc: '中文 commit 规范 + 分级 review 话术,配 Gitee / Coding / 极狐 GitLab 工作流。' }, + ], + faq: [ + { q: 'superpowers-zh 是免费的吗?', a: '完全免费。MIT 协议开源,永久免费,不含任何付费墙或订阅。' }, + { q: '支持哪些 AI 编程工具?', a: '共 18 款:Claude Code、Cursor、Windsurf、Codex CLI、Gemini CLI、Kiro、Trae、Qoder、Aider、OpenCode、Qwen Code、Antigravity、DeerFlow、VS Code(Copilot)、Copilot CLI、Hermes Agent、Claw Code、OpenClaw。' }, + { q: '和上游 obra/superpowers 是什么关系?', a: '本项目是 obra/superpowers(159k+ ⭐)的中文增强 fork:完整汉化上游全部核心 skill,并叠加 4 个面向中国开发者的原创 skill。中文化与中国特色内容只在本 fork 提供。' }, + { q: '安装后怎么生效?', a: 'npx 会把 skill 文件装到你项目对应工具的目录(如 .claude/skills/),重启 AI 工具后,它会在恰当时机自动触发相应 skill —— 无需你每次手动调用。' }, + { q: '会拖慢我的 AI 吗?会上传代码吗?', a: '不会。skill 是按需触发的纯 Markdown,零运行时、不联网、不上传任何代码或数据,全程在本地。' }, + { q: '怎么更新或卸载?', a: '更新:重新运行 npx superpowers-zh 覆盖即可。卸载:npx superpowers-zh --uninstall 清理已安装的 skill 与 bootstrap 文件。' }, + ], + }, + en: { + htmlLang: 'en', + title: 'superpowers-zh · Battle-tested AI coding skills (CN-enhanced)', + desc: 'Full Chinese localization of superpowers (159k+ ⭐) plus 4 China-native skills. One npx command installs systematic workflow methodology into 18 AI coding tools.', + nav: { why: 'Features', install: 'Install', skills: 'Skills', tools: 'Tools', faq: 'FAQ', github: 'GitHub ↗' }, + heroBadge: 'superpowers 159k+ ⭐ · Full CN localization + China-native skills', + heroH1: 'Give your AI coding tools
superpowers that actually ship', + heroLead: '{n} battle-tested workflow skills — from brainstorming to TDD, systematic debugging to code review.
One command auto-detects your tool and installs.', + heroBtn1: 'Get the command', heroBtn2: 'GitHub', + stats: ['Skills', 'China-native', 'Tools', 'Version'], + whyTitle: 'Why superpowers-zh?', + whySub: 'Not another prompt-template pack — real engineering methodology that makes AI work properly.', + plTitle: 'An end-to-end workflow, every step guarded by a skill', + plSub: 'Skills chain together; the AI triggers the right methodology at the right moment.', + cmpTitle: 'After install, AI stops "coding before thinking"', + cmpBad: '❌ Without', cmpGood: '✅ With superpowers-zh', + cmpBadPre: 'You: Add bulk export to the users module\nAI: Sure, implementing… (starts coding)\n export async function exportUsers() { … }\nYou: Wait — wrong format, no paging,\n it OOMs on large data…', + cmpGoodPre: 'You: Add bulk export to the users module\nAI: First, the requirements — what format?\n What data volume? Paging/streaming?\n Permission checks? (triggers brainstorming)\n → plan → TDD → verify → review', + instTitle: 'Pick your tool, get the command', + instSub: 'For most tools npx superpowers-zh auto-detects the project and installs; otherwise pass --tool.', + instLabel: "I'm using", + instNoteAuto: 'Run it in your project root — it auto-detects {name} and installs. Restart the tool to take effect.', + instNoteManual: '{name} can\'t be auto-detected; pass --tool explicitly. Run in the project root, then restart the tool.', + skTitle: '{n} skills, covering the whole dev workflow', + skSub: 'Click any card for the full operating doc.', + skSearch: 'Search skills… (e.g. debug / review / TDD)', + skEmpty: 'No matching skills.', + skDetail: 'Read docs →', + tagCn: 'China-native', + ucTitle: 'Typical use cases', ucSub: 'Each scenario is backed by a set of cooperating skills.', + toolsTitle: 'One skill set, 18 tools', toolsSub: 'Switch tools without switching habits — the methodology follows you.', + faqTitle: 'FAQ', + bookTitle: 'Pair it with the methodology for 2× efficiency', + bookDesc: '"AI Coding in Practice · The Three-Volume Methodology" — full tutorials for 10 AI coding tools plus real-world pitfalls. Online book + PDF, free forever.', + bookBtn: 'Read free ↗', + sponsorTitle: 'Sponsors', + sponsorDesc: 'A fast, reliable API relay for Claude Code, Codex and more — API relay and AI image generation.', + sponsorCta: '🙏 Want to appear here? Contact jnMetaCode@qq.com', + ctaTitle: 'Ready to make your AI actually ship?', + ctaDesc: 'One command installs {n} battle-tested skills into your tool. Free, open-source, zero-dependency.', + ctaBtn1: 'Get the command', ctaBtn2: '⭐ Star on GitHub', + footCols: [ + { h: 'Product', links: [['Features', '#why'], ['Skills', '#skills'], ['Tools', '#tools'], ['FAQ', '#faq']] }, + { h: 'Resources', links: [['GitHub', 'https://github.com/jnMetaCode/superpowers-zh'], ['npm', 'https://www.npmjs.com/package/superpowers-zh'], ['Upstream obra/superpowers', 'https://github.com/obra/superpowers'], ['Methodology book', 'https://book.aibuzhiyu.com/']] }, + { h: 'Community', links: [['Open an Issue', 'https://github.com/jnMetaCode/superpowers-zh/issues'], ['Contributing', 'https://github.com/jnMetaCode/superpowers-zh/blob/main/CLAUDE.md']] }, + ], + footTag: 'AI coding superpowers · Chinese-enhanced · MIT License', + copyright: '© 2026 superpowers-zh · A Chinese-enhanced fork of obra/superpowers', + copy: 'Copy', copied: 'Copied ✓', + backToSkills: '← Back to all skills', + detailInstall: 'Install this skill set', + detailSource: 'View source on GitHub ↗', + features: [ + { icon: '🧠', t: '20 battle-tested methods', d: 'Not prompt templates — workflow methodology hardened by cross-session adversarial testing, from brainstorming to TDD, debugging and review.' }, + { icon: '🔌', t: 'Works in 18 tools', d: 'One skill set for Claude Code / Cursor / Codex / Gemini CLI / Windsurf and more. Switch tools, keep your habits.' }, + { icon: '⚡', t: 'One-command install', d: 'npx superpowers-zh auto-detects your tool and installs. Zero config; restart to take effect.' }, + { icon: '🇨🇳', t: 'China-native skills', d: 'Chinese code-review phrasing, commit conventions, doc typography, and domestic Git platforms (Gitee/Coding/JiHu) — not in upstream.' }, + { icon: '📖', t: 'Fully localized upstream', d: 'Tracks obra/superpowers (159k+ ⭐); every core skill localized into native Chinese — calibrated, not machine-translated.' }, + { icon: '🔓', t: 'Zero-dep · MIT', d: 'Pure Markdown skills. No external deps, no network, no code upload. Triggered on demand with zero runtime cost.' }, + ], + pipeline: [ + { n: 'Brainstorm', d: 'Clarify intent before coding' }, + { n: 'Plan', d: 'Break work into steps' }, + { n: 'TDD', d: 'Test first, then implement' }, + { n: 'Debug', d: 'Reproduce & locate first' }, + { n: 'Review', d: 'Rigorous pre-merge check' }, + { n: 'Verify', d: 'Prove it with evidence' }, + ], + usecases: [ + { tag: 'New feature', skills: 'brainstorming → writing-plans → TDD', desc: 'AI questions the requirements, writes a plan, then builds test-first — instead of dumping code immediately.' }, + { tag: 'Fixing a bug', skills: 'systematic-debugging', desc: 'Forces reproduce-and-locate-root-cause before any fix — no more "guess and tweak" loops.' }, + { tag: 'Before merge', skills: 'verification-before-completion → code-review', desc: 'Must run verification with evidence, then a review pass, before claiming "done".' }, + { tag: 'CN team workflow', skills: 'chinese-commit-conventions → chinese-code-review', desc: 'Chinese commit conventions + tiered review phrasing, wired for Gitee / Coding / JiHu GitLab.' }, + ], + faq: [ + { q: 'Is superpowers-zh free?', a: 'Completely free. MIT-licensed open source, forever, with no paywall or subscription.' }, + { q: 'Which AI coding tools are supported?', a: '18 tools: Claude Code, Cursor, Windsurf, Codex CLI, Gemini CLI, Kiro, Trae, Qoder, Aider, OpenCode, Qwen Code, Antigravity, DeerFlow, VS Code (Copilot), Copilot CLI, Hermes Agent, Claw Code, OpenClaw.' }, + { q: 'How does it relate to obra/superpowers?', a: 'This is a Chinese-enhanced fork of obra/superpowers (159k+ ⭐): a full localization of all core skills plus 4 China-native skills. The localization and China-specific content live only in this fork.' }, + { q: 'How does it take effect after install?', a: 'npx installs skill files into your tool\'s directory (e.g. .claude/skills/). After restarting your AI tool, it auto-triggers the right skill at the right moment — no manual invocation needed.' }, + { q: 'Will it slow my AI down or upload my code?', a: 'No. Skills are on-demand Markdown: zero runtime, no network, no code or data upload — everything stays local.' }, + { q: 'How do I update or uninstall?', a: 'Update: re-run npx superpowers-zh to overwrite. Uninstall: npx superpowers-zh --uninstall removes installed skills and bootstrap files.' }, + ], + }, +}; + +// ---- 读取 skills(含正文) ---- +function loadSkills() { + const skills = []; + for (const entry of readdirSync(SKILLS_DIR, { withFileTypes: true })) { + if (!entry.isDirectory()) continue; + const file = join(SKILLS_DIR, entry.name, 'SKILL.md'); + if (!existsSync(file)) continue; + const raw = readFileSync(file, 'utf8'); + const fm = parseFrontmatter(raw); + const meta = SKILL_META[entry.name] || { title: entry.name, titleEn: entry.name, descEn: '', group: 'flow' }; + skills.push({ + name: fm.name || entry.name, + title: meta.title, titleEn: meta.titleEn, + group: meta.group, + desc: (fm.description || '').trim(), + descEn: meta.descEn || '', + china: meta.group === 'china', + raw, + }); + } + const order = GROUPS.map(g => g.id); + skills.sort((a, b) => { + if (a.name === 'using-superpowers') return -1; + if (b.name === 'using-superpowers') return 1; + return order.indexOf(a.group) - order.indexOf(b.group); + }); + return skills; +} + +// ---- 公共布局 ---- +// base: 资源相对前缀('' / '../' / '../../');langHref: 切换语言的目标 URL +function layout({ lang, base, title, desc, body, langHref, extraHead = '' }) { + const t = T[lang]; + const other = lang === 'zh' ? 'EN' : '中文'; + return ` + + + + +${esc(title)} + + + + + + + +${extraHead} + + +${body} +
+
+
+ superpowers-zh + ${t.footTag} +
+ ${t.footCols.map(col => `

${col.h}

${col.links.map(l => `${l[0]}`).join('')}
`).join('')} +
+ +
+ + +`; +} + +// ---- 首页正文 ---- +function renderLanding(skills, lang) { + const t = T[lang]; + const total = skills.length; + const cnCount = skills.filter(s => s.china).length; + const toolData = JSON.stringify(TOOLS.map(x => ({ name: x.name, cmd: x.cmd, auto: x.auto }))); + const fill = (s, map) => s.replace(/\{(\w+)\}/g, (_, k) => map[k]); + + const cards = skills.map(s => { + const title = lang === 'zh' ? s.title : s.titleEn; + const d = lang === 'zh' ? s.desc : (s.descEn || s.desc); + return ` + +

${esc(title)}

${s.china ? `${t.tagCn}` : ''}
+ ${esc(s.name)} +

${esc(d)}

+ ${t.skDetail} +
`; + }).join(''); + + const filters = GROUPS.map((g, i) => + ``).join(''); + const toolOpts = TOOLS.map((x, i) => ``).join(''); + const toolWall = TOOLS.map(x => `
${esc(x.name)}${esc(x.type)}
`).join(''); + const feats = t.features.map(f => `
${f.icon}

${esc(f.t)}

${esc(f.d)}

`).join(''); + const pipe = t.pipeline.map((p, i) => `
${i + 1}
${esc(p.n)}${esc(p.d)}
${i < t.pipeline.length - 1 ? '
' : ''}`).join(''); + const ucs = t.usecases.map(u => `
${esc(u.tag)}${esc(u.skills)}

${esc(u.desc)}

`).join(''); + const faqs = t.faq.map(f => `
${esc(f.q)}
${esc(f.a)}
`).join(''); + + return ` +
+
+
${t.heroBadge}
+

${t.heroH1}

+

${fill(t.heroLead, { n: total })}

+
$ npx superpowers-zh
+ +
+
${total}${t.stats[0]}
+
${cnCount}${t.stats[1]}
+
18${t.stats[2]}
+
v${PKG.version}${t.stats[3]}
+
+
+ +
+

${t.whyTitle}

+

${t.whySub}

+
${feats}
+
+ +
+

${t.plTitle}

+

${t.plSub}

+
${pipe}
+
+ +
+

${t.cmpTitle}

+
+
${t.cmpBad}
${esc(t.cmpBadPre)}
+
${t.cmpGood}
${esc(t.cmpGoodPre)}
+
+
+ +
+

${t.instTitle}

+

${t.instSub}

+
+ + +
npx superpowers-zh
+

+
+
+ +
+

${fill(t.skTitle, { n: total })}

+

${t.skSub}

+
+ +
${filters}
+
+
${cards}
+ +
+ +
+

${t.ucTitle}

+

${t.ucSub}

+
${ucs}
+
+ +
+

${t.toolsTitle}

+

${t.toolsSub}

+
${toolWall}
+
+ +
+

${t.faqTitle}

+
${faqs}
+
+ +
+

${t.bookTitle}

${t.bookDesc}

+ ${t.bookBtn} +
+ + + +
+

${t.ctaTitle}

${fill(t.ctaDesc, { n: total })}

+
$ npx superpowers-zh
+ +
+
+`; +} + +// ---- skill 详情(操作文档)页正文 ---- +function renderDetail(skill, lang) { + const t = T[lang]; + const title = lang === 'zh' ? skill.title : skill.titleEn; + const bodyHtml = renderMarkdown(skill.raw); + const cnNotice = lang === 'en' + ? '
📖 This skill\'s content is written in Chinese — superpowers-zh is a Chinese-localized toolkit.
' + : ''; + const srcUrl = `https://github.com/jnMetaCode/superpowers-zh/blob/main/skills/${skill.name}/SKILL.md`; + return ` +
+ ${t.backToSkills} +
+
+

${esc(title)}

+ ${esc(skill.name)} + ${skill.china ? `${t.tagCn}` : ''} +
+

${esc(lang === 'zh' ? skill.desc : (skill.descEn || skill.desc))}

+
+
$ npx superpowers-zh
+ ${t.detailSource} +
+
+ ${cnNotice} +
${bodyHtml}
+ ${t.backToSkills} +
+`; +} + +// ---- 构建 ---- +function build() { + const skills = loadSkills(); + rmSync(DIST, { recursive: true, force: true }); + mkdirSync(join(DIST, 'assets', 'sponsors'), { recursive: true }); + mkdirSync(join(DIST, 'skills'), { recursive: true }); + mkdirSync(join(DIST, 'en', 'skills'), { recursive: true }); + + // 资源 + copyFileSync(join(TEMPLATE, 'styles.css'), join(DIST, 'styles.css')); + copyFileSync(join(TEMPLATE, 'app.js'), join(DIST, 'app.js')); + copyFileSync(join(ROOT, 'assets', 'app-icon.png'), join(DIST, 'assets', 'app-icon.png')); + copyFileSync(join(ROOT, 'assets', 'superpowers-small.svg'), join(DIST, 'assets', 'superpowers-small.svg')); + copyFileSync(join(ROOT, 'assets', 'sponsors', '5cookie-code.png'), join(DIST, 'assets', 'sponsors', '5cookie-code.png')); + + // 中文站(根) + writeFileSync(join(DIST, 'index.html'), layout({ + lang: 'zh', base: '', title: T.zh.title, desc: T.zh.desc, + body: renderLanding(skills, 'zh'), langHref: 'en/index.html', + })); + // 英文站(/en/) + writeFileSync(join(DIST, 'en', 'index.html'), layout({ + lang: 'en', base: '../', title: T.en.title, desc: T.en.desc, + body: renderLanding(skills, 'en'), langHref: '../index.html', + })); + + // 详情(操作文档)页 ×2 语言 + for (const s of skills) { + writeFileSync(join(DIST, 'skills', `${s.name}.html`), layout({ + lang: 'zh', base: '../', title: `${s.title} · superpowers-zh`, desc: s.desc, + body: renderDetail(s, 'zh'), langHref: `../en/skills/${s.name}.html`, + })); + writeFileSync(join(DIST, 'en', 'skills', `${s.name}.html`), layout({ + lang: 'en', base: '../../', title: `${s.titleEn} · superpowers-zh`, desc: s.descEn || s.desc, + body: renderDetail(s, 'en'), langHref: `../../skills/${s.name}.html`, + })); + } + + // Cloudflare Pages 缓存策略:静态资源长缓存,HTML 不缓存 + writeFileSync(join(DIST, '_headers'), + '/assets/*\n Cache-Control: public, max-age=31536000, immutable\n' + + '/styles.css\n Cache-Control: public, max-age=86400\n' + + '/app.js\n Cache-Control: public, max-age=86400\n' + + '/*.html\n Cache-Control: public, max-age=0, must-revalidate\n'); + + const pages = 2 + skills.length * 2; + console.log(`✅ 生成 ${pages} 个页面:中/英首页 + ${skills.length} 个 skill × 2 语言详情页 → ${DIST}`); +} + +build(); diff --git a/site/md.mjs b/site/md.mjs new file mode 100644 index 0000000000000000000000000000000000000000..0b9965420ca3db82a488ee0116a4e2bf3fb2e723 GIT binary patch literal 5034 zcmb_gOLN=S74}-c;#k!f04WlYJJW?lQse0+nJSyui%tnDgh)t41pzDoT24vM*qKO{ zT1WO&s^$0*%dI_iC$T+oVq4b3`Y#OuN-O_`o^$U72uh;TMYRxs`#ATU?|kQRsj6^z z|4rxp!-Lm%;8We5t2zrd{AcNJ@bTqOCn4N=6+T)EzIpax_h#_qQFr}WXL~h#@crTA zcisChy7#_&>RAA4Zq>q;ALH-Zs`?6R?n%kqadBr=j-E=(i_h4 zVax9uE%4D<8XX^M50SE5O++PFT<+{Y4FB?kBhp>D5x#g$iY|Q}Jo&17d#U?sqqDcB zhIby&<8bYLxcaoGtZAcRhU^uXzQAw*E;n0mcr{td5yk}x(n-W{aT=tY%IDBr@`}JuIzx+P zn)0%iVbA(C$iNueaYmx0ZMFf`4Hn;Y_Pz=4+(2d>?0w&Pw}M3AI0bKC1<&sW51t{{ z65d28V;05~gwD<)(dX97&i-ShMeyPsl87V1k%S^yUy>WHr3^wuDHzC4Uqe45)HWhnj25WjOA4EO57XZ za(Svn`*AzT(QGD3o;a(e$V8A(iZNdpIiFMU!Q~L8_U|J8N!kvQe@51-Y2{2rD3jsD zXi!X>C8n342swj=h;g;}D8=>}1yJ2t(v0~C7g?d!-*#M!eQdFlkFEx@d zQXv4e%Ej`Xn|@%GKPM<^qvHs`1W->BWgs6Re6!BLAv6Hu{@37LT-?O(`Qu zvebT2DK6ByVxia?azs!$;sBpEt8DgsDeJrDJO!&W=`0xTZ*|YWueg1|a<+pMCi@xdFBS}{JHyepO_T+3)R z2t?inQqQ-y7OZ?tP15b}paq?+_0HB`QDYnl@4nDhX!bOn&)F7_wu4jDaET9Wq>X_Uf-uygXYI>tu zi}98fps3cg&=2!^UB>EFfQoRV$SP5`s2MueLOdoqkwtdA2(`|``9$m@s=W>0+3iTJ=^fV9{Pz}4t!TgG)gB2>uzI8atZ zz)P&p!!fr&6I|l7Q~e?lVW(uWe)qC8My`yqmp}^6=JQm~MY~GG<&jY_EQ`u|A7o5Q z6hR4!OTtuOyZS7w9<{J~qJ_P<>C4nqCh0E$yy`Oy5E8;1p+Qe15E&FH{1(K-@ibV_ zyon_Ldk~yT+M{LVG!IN2Ib(1w>2aLzVH8)66qp1E)0R`2`%}a5`F%pTdVwy9e7!~|_&)7li?H`h#JS-vA(g7ywo2a>j#P8iKdb6icPjE z>U+gCkJh!puskD1woiC}TY5t1P?LxX2)3XbevP?0Hi+7nLB#0(dmF1q96gKZLi7ffw zAi!T44sP$UpI4THcW(~%zwPckKYaKthbAhlCtA<>FER(6$bMu9B%~rB~es~Zd{?RSqP=X r#C`*btP!b=O>?5g9+7hOBuh`qX}Ey-jCP&?xVRdV#9r%6m*oEfW!Z*} literal 0 HcmV?d00001 diff --git a/site/template/app.js b/site/template/app.js new file mode 100644 index 0000000..d68a385 --- /dev/null +++ b/site/template/app.js @@ -0,0 +1,92 @@ +// superpowers-zh 官网交互 —— 零依赖原生 JS + +(function () { + 'use strict'; + + var TOOLS = window.__TOOLS__ || []; + var I18N = window.__I18N__ || { copy: '复制', copied: '已复制 ✓' }; + + // ---------- 主题切换(深 / 浅) ---------- + var themeBtn = document.getElementById('themeBtn'); + if (themeBtn) { + themeBtn.addEventListener('click', function () { + var light = document.documentElement.getAttribute('data-theme') === 'light'; + if (light) { + document.documentElement.removeAttribute('data-theme'); + try { localStorage.setItem('sp-theme', 'dark'); } catch (e) {} + } else { + document.documentElement.setAttribute('data-theme', 'light'); + try { localStorage.setItem('sp-theme', 'light'); } catch (e) {} + } + }); + } + + // ---------- 复制按钮(事件委托) ---------- + document.addEventListener('click', function (e) { + var btn = e.target.closest('.copy-btn'); + if (!btn) return; + e.preventDefault(); + var box = btn.closest('[data-copy]'); + if (!box) return; + navigator.clipboard.writeText(box.getAttribute('data-copy')).then(function () { + var old = btn.textContent; + btn.textContent = I18N.copied; + btn.classList.add('done'); + setTimeout(function () { btn.textContent = old; btn.classList.remove('done'); }, 1600); + }).catch(function () { btn.textContent = '×'; }); + }); + + // ---------- 安装命令生成器 ---------- + var sel = document.getElementById('toolSel'); + var cmdText = document.getElementById('cmdText'); + var cmdBox = cmdText ? cmdText.closest('[data-copy]') : null; + var note = document.getElementById('installNote'); + + function escapeHtml(s) { + return String(s).replace(/&/g, '&').replace(//g, '>'); + } + + function updateCmd() { + var t = TOOLS[sel.value]; + if (!t) return; + cmdText.textContent = t.cmd; + cmdBox.setAttribute('data-copy', t.cmd); + var tpl = t.auto ? I18N.auto : I18N.manual; + if (note && tpl) note.innerHTML = tpl.replace('{name}', escapeHtml(t.name)); + } + if (sel && note) { + sel.addEventListener('change', updateCmd); + updateCmd(); + } + + // ---------- Skill 搜索 + 筛选 ---------- + var grid = document.getElementById('grid'); + var search = document.getElementById('search'); + var empty = document.getElementById('empty'); + var chips = Array.prototype.slice.call(document.querySelectorAll('.chip')); + var cards = grid ? Array.prototype.slice.call(grid.querySelectorAll('.card')) : []; + var activeFilter = 'all'; + + function applyFilter() { + var q = (search ? search.value : '').trim().toLowerCase(); + var visible = 0; + cards.forEach(function (card) { + var group = card.getAttribute('data-group'); + var hay = (card.getAttribute('data-name') + ' ' + card.getAttribute('data-title') + ' ' + card.textContent).toLowerCase(); + var show = (activeFilter === 'all' || group === activeFilter) && (!q || hay.indexOf(q) !== -1); + card.style.display = show ? '' : 'none'; + if (show) visible++; + }); + if (empty) empty.hidden = visible !== 0; + } + + chips.forEach(function (chip) { + chip.addEventListener('click', function () { + chips.forEach(function (c) { c.classList.remove('active'); }); + chip.classList.add('active'); + activeFilter = chip.getAttribute('data-filter'); + applyFilter(); + }); + }); + if (search) search.addEventListener('input', applyFilter); +})(); diff --git a/site/template/styles.css b/site/template/styles.css new file mode 100644 index 0000000..b2ab7cf --- /dev/null +++ b/site/template/styles.css @@ -0,0 +1,448 @@ +/* superpowers-zh 官网样式 —— 深色开发者风格 */ +:root { + --bg: #0a0b10; + --bg-soft: #12141c; + --bg-card: #161924; + --border: #242838; + --border-soft: #1c2030; + --text: #e7e9f0; + --text-dim: #9aa0b4; + --text-faint: #6b7186; + --accent: #7c6cff; + --accent-2: #4dd6c8; + --accent-grad: linear-gradient(110deg, #7c6cff 0%, #4dd6c8 100%); + --good: #3ddc84; + --bad: #ff6b6b; + --radius: 14px; + --maxw: 1080px; +} + +* { box-sizing: border-box; margin: 0; padding: 0; } + +html { scroll-behavior: smooth; } + +body { + background: var(--bg); + color: var(--text); + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "PingFang SC", + "Hiragino Sans GB", "Microsoft YaHei", "Source Han Sans SC", sans-serif; + line-height: 1.7; + -webkit-font-smoothing: antialiased; + overflow-x: hidden; +} + +code, pre, .cmd-hero code, .cmd-out code { + font-family: "SF Mono", "JetBrains Mono", Menlo, Consolas, "Courier New", monospace; +} + +a { color: inherit; text-decoration: none; } + +/* 背景光晕 */ +body::before { + content: ""; + position: fixed; + top: -30%; left: 50%; + transform: translateX(-50%); + width: 900px; height: 900px; + background: radial-gradient(circle, rgba(124,108,255,0.18), transparent 60%); + pointer-events: none; + z-index: 0; +} + +/* ---------- 导航 ---------- */ +.nav { + position: sticky; top: 0; z-index: 50; + display: flex; align-items: center; justify-content: space-between; + max-width: var(--maxw); margin: 0 auto; + padding: 16px 24px; + backdrop-filter: blur(12px); +} +.nav::after { + content: ""; position: absolute; inset: 0; + background: rgba(10,11,16,0.6); + border-bottom: 1px solid var(--border-soft); + z-index: -1; +} +.brand { display: flex; align-items: center; gap: 9px; font-size: 18px; font-weight: 600; } +.brand b { background: var(--accent-grad); -webkit-background-clip: text; background-clip: text; color: transparent; } +.nav nav { display: flex; gap: 24px; font-size: 14px; } +.nav nav a { color: var(--text-dim); transition: color .2s; } +.nav nav a:hover { color: var(--text); } + +main { position: relative; z-index: 1; } +section { max-width: var(--maxw); margin: 0 auto; padding: 80px 24px; } + +.section-title { + font-size: clamp(24px, 4vw, 34px); + font-weight: 700; + text-align: center; + margin-bottom: 12px; + letter-spacing: -0.02em; +} +.section-sub { text-align: center; color: var(--text-dim); margin-bottom: 36px; font-size: 15px; } +.section-sub code { background: var(--bg-soft); padding: 2px 7px; border-radius: 6px; color: var(--accent-2); font-size: 13px; } + +/* ---------- HERO ---------- */ +.hero { text-align: center; padding-top: 70px; padding-bottom: 60px; } +.badge { + display: inline-block; + padding: 6px 16px; + border: 1px solid var(--border); + border-radius: 100px; + font-size: 13px; + color: var(--text-dim); + background: var(--bg-soft); + margin-bottom: 28px; +} +.hero h1 { + font-size: clamp(32px, 6vw, 56px); + font-weight: 800; + line-height: 1.18; + letter-spacing: -0.03em; + margin-bottom: 22px; +} +.grad { background: var(--accent-grad); -webkit-background-clip: text; background-clip: text; color: transparent; } +.lead { color: var(--text-dim); font-size: clamp(15px, 2.2vw, 18px); max-width: 640px; margin: 0 auto 36px; } + +.cmd-hero, .cmd-out { + display: flex; align-items: center; justify-content: space-between; gap: 12px; + max-width: 460px; margin: 0 auto; + background: #06070b; + border: 1px solid var(--border); + border-radius: 12px; + padding: 14px 14px 14px 20px; +} +.cmd-hero code { font-size: 16px; color: var(--accent-2); } +.copy-btn { + border: 1px solid var(--border); + background: var(--bg-card); + color: var(--text-dim); + padding: 7px 14px; + border-radius: 8px; + font-size: 13px; + cursor: pointer; + transition: all .2s; + white-space: nowrap; +} +.copy-btn:hover { color: var(--text); border-color: var(--accent); } +.copy-btn.done { color: var(--good); border-color: var(--good); } + +.hero-cta { display: flex; gap: 14px; justify-content: center; margin: 32px 0 50px; flex-wrap: wrap; } +.btn { + padding: 12px 26px; border-radius: 10px; font-size: 15px; font-weight: 600; + cursor: pointer; transition: all .2s; border: 1px solid transparent; display: inline-block; +} +.btn-primary { background: var(--accent-grad); color: #0a0b10; } +.btn-primary:hover { filter: brightness(1.1); transform: translateY(-1px); } +.btn-ghost { border-color: var(--border); color: var(--text); background: var(--bg-soft); } +.btn-ghost:hover { border-color: var(--accent); } + +.stats { + display: flex; gap: 40px; justify-content: center; flex-wrap: wrap; + padding-top: 20px; +} +.stats div { display: flex; flex-direction: column; align-items: center; } +.stats b { font-size: 30px; font-weight: 800; background: var(--accent-grad); -webkit-background-clip: text; background-clip: text; color: transparent; } +.stats span { font-size: 13px; color: var(--text-faint); margin-top: 2px; } + +/* ---------- 效果对比 ---------- */ +.compare-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; } +.compare-col { border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; background: var(--bg-card); } +.compare-col.bad { border-color: rgba(255,107,107,0.3); } +.compare-col.good { border-color: rgba(61,220,132,0.3); } +.compare-label { padding: 12px 18px; font-weight: 600; font-size: 14px; border-bottom: 1px solid var(--border-soft); } +.compare-col.bad .compare-label { color: var(--bad); background: rgba(255,107,107,0.06); } +.compare-col.good .compare-label { color: var(--good); background: rgba(61,220,132,0.06); } +.compare-col pre { + padding: 18px; font-size: 13px; line-height: 1.75; color: var(--text-dim); + white-space: pre-wrap; word-break: break-word; +} + +/* ---------- 安装命令生成器 ---------- */ +.install-box { + max-width: 560px; margin: 0 auto; + background: var(--bg-card); border: 1px solid var(--border); + border-radius: var(--radius); padding: 28px; +} +.install-box label { display: block; font-size: 14px; color: var(--text-dim); margin-bottom: 8px; } +#toolSel { + width: 100%; padding: 12px 14px; margin-bottom: 18px; + background: #06070b; border: 1px solid var(--border); border-radius: 10px; + color: var(--text); font-size: 15px; cursor: pointer; + appearance: none; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 12 12'%3E%3Cpath fill='%239aa0b4' d='M6 8L2 4h8z'/%3E%3C/svg%3E"); + background-repeat: no-repeat; background-position: right 14px center; +} +.cmd-out { max-width: 100%; } +.cmd-out code { font-size: 15px; color: var(--accent-2); overflow-x: auto; } +.install-note { margin-top: 14px; font-size: 13px; color: var(--text-faint); line-height: 1.6; } + +/* ---------- Skills ---------- */ +.skill-controls { display: flex; flex-direction: column; gap: 16px; align-items: center; margin-bottom: 32px; } +#search { + width: 100%; max-width: 460px; padding: 12px 16px; + background: var(--bg-card); border: 1px solid var(--border); border-radius: 10px; + color: var(--text); font-size: 15px; +} +#search:focus { outline: none; border-color: var(--accent); } +.chips { display: flex; flex-wrap: wrap; gap: 8px; justify-content: center; } +.chip { + padding: 7px 15px; border-radius: 100px; font-size: 13px; cursor: pointer; + background: var(--bg-soft); border: 1px solid var(--border); color: var(--text-dim); + transition: all .2s; +} +.chip:hover { color: var(--text); } +.chip.active { background: var(--accent); border-color: var(--accent); color: #0a0b10; font-weight: 600; } + +.grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(290px, 1fr)); gap: 16px; } +.card { + background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); + padding: 22px; transition: all .2s; position: relative; +} +.card:hover { border-color: var(--accent); transform: translateY(-2px); } +.card-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; } +.card-head h3 { font-size: 17px; font-weight: 700; } +.tag { font-size: 11px; padding: 2px 8px; border-radius: 6px; font-weight: 600; } +.tag-cn { background: rgba(255,107,107,0.12); color: #ff8a8a; border: 1px solid rgba(255,107,107,0.25); } +.card-id { display: inline-block; font-size: 12px; color: var(--text-faint); margin-bottom: 12px; } +.card p { font-size: 13.5px; color: var(--text-dim); line-height: 1.65; } +.empty { text-align: center; color: var(--text-faint); padding: 40px; } + +/* ---------- 工具墙 ---------- */ +.tool-wall { display: grid; grid-template-columns: repeat(auto-fill, minmax(180px, 1fr)); gap: 12px; } +.tool-pill { + display: flex; align-items: center; justify-content: space-between; + padding: 14px 18px; background: var(--bg-card); + border: 1px solid var(--border); border-radius: 10px; +} +.tool-name { font-weight: 600; font-size: 14px; } +.tool-type { font-size: 11px; color: var(--text-faint); border: 1px solid var(--border); padding: 1px 7px; border-radius: 5px; } + +/* ---------- 书 / 赞助 ---------- */ +.book-inner { + background: linear-gradient(135deg, rgba(124,108,255,0.12), rgba(77,214,200,0.08)); + border: 1px solid var(--border); border-radius: 18px; padding: 44px; + text-align: center; +} +.book-inner h2 { font-size: 26px; margin-bottom: 12px; } +.book-inner p { color: var(--text-dim); margin-bottom: 24px; max-width: 560px; margin-left: auto; margin-right: auto; } + +.sponsor-card { + display: flex; align-items: center; gap: 24px; max-width: 620px; margin: 0 auto; + background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); + padding: 24px; transition: border-color .2s; +} +.sponsor-card:hover { border-color: var(--accent); } +.sponsor-card img { border-radius: 10px; flex-shrink: 0; } +.sponsor-card b { display: block; font-size: 17px; margin-bottom: 6px; } +.sponsor-card span { font-size: 13.5px; color: var(--text-dim); line-height: 1.6; } +.sponsor-cta { text-align: center; margin-top: 24px; font-size: 14px; color: var(--text-faint); } +.sponsor-cta b { color: var(--accent-2); } + +/* ---------- 页脚 ---------- */ +footer { border-top: 1px solid var(--border-soft); margin-top: 40px; padding: 40px 24px; } +.foot-inner { + max-width: var(--maxw); margin: 0 auto; + display: flex; justify-content: space-between; align-items: center; flex-wrap: wrap; gap: 20px; +} +.foot-inner strong { display: block; } +.foot-inner span { font-size: 13px; color: var(--text-faint); } +.foot-inner nav { display: flex; gap: 22px; font-size: 14px; } +.foot-inner nav a { color: var(--text-dim); transition: color .2s; } +.foot-inner nav a:hover { color: var(--accent-2); } +.copyright { max-width: var(--maxw); margin: 24px auto 0; font-size: 12.5px; color: var(--text-faint); text-align: center; } +.copyright a { color: var(--text-dim); } + +/* ---------- 响应式 ---------- */ +@media (max-width: 720px) { + section { padding: 56px 18px; } + .nav nav { gap: 16px; font-size: 13px; } + .nav nav a:nth-child(1), .nav nav a:nth-child(2) { display: none; } + .compare-grid { grid-template-columns: 1fr; } + .stats { gap: 28px; } + .sponsor-card { flex-direction: column; text-align: center; } + .foot-inner { flex-direction: column; align-items: flex-start; } +} + +/* ========== 扩充板块 ========== */ + +/* 为什么选择 — 卖点网格 */ +.feat-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 18px; } +.feat { + background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); + padding: 28px; transition: all .2s; +} +.feat:hover { border-color: var(--accent); transform: translateY(-2px); } +.feat-icon { + width: 48px; height: 48px; display: flex; align-items: center; justify-content: center; + font-size: 26px; border-radius: 12px; margin-bottom: 16px; + background: linear-gradient(135deg, rgba(124,108,255,0.18), rgba(77,214,200,0.12)); +} +.feat h3 { font-size: 18px; font-weight: 700; margin-bottom: 10px; } +.feat p { font-size: 14px; color: var(--text-dim); line-height: 1.7; } + +/* 工作流可视化 */ +.pl-track { + display: flex; align-items: stretch; gap: 10px; flex-wrap: wrap; justify-content: center; +} +.pl-step { + display: flex; gap: 12px; align-items: flex-start; + background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; + padding: 16px 18px; flex: 1 1 220px; max-width: 250px; transition: border-color .2s; +} +.pl-step:hover { border-color: var(--accent); } +.pl-num { + flex-shrink: 0; width: 26px; height: 26px; border-radius: 8px; + background: var(--accent-grad); color: #0a0b10; font-weight: 800; font-size: 14px; + display: flex; align-items: center; justify-content: center; +} +.pl-body { display: flex; flex-direction: column; gap: 3px; } +.pl-body b { font-size: 15px; } +.pl-body code { font-size: 11px; color: var(--text-faint); } +.pl-body span { font-size: 12.5px; color: var(--text-dim); } +.pl-arrow { display: flex; align-items: center; color: var(--accent); font-size: 20px; font-weight: 700; } + +/* 使用场景 */ +.uc-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(320px, 1fr)); gap: 16px; } +.usecase { + background: var(--bg-card); border: 1px solid var(--border); border-radius: var(--radius); + padding: 24px; transition: border-color .2s; +} +.usecase:hover { border-color: var(--accent); } +.uc-tag { + display: inline-block; font-size: 13px; font-weight: 700; padding: 4px 12px; + border-radius: 8px; margin-bottom: 12px; + background: linear-gradient(135deg, rgba(124,108,255,0.2), rgba(77,214,200,0.15)); color: var(--text); +} +.uc-skills { display: block; font-size: 12.5px; color: var(--accent-2); margin-bottom: 10px; } +.usecase p { font-size: 14px; color: var(--text-dim); line-height: 1.7; } + +/* FAQ */ +.faq-list { max-width: 760px; margin: 0 auto; display: flex; flex-direction: column; gap: 12px; } +.faq-item { + background: var(--bg-card); border: 1px solid var(--border); border-radius: 12px; + padding: 0 22px; transition: border-color .2s; +} +.faq-item[open] { border-color: var(--accent); } +.faq-item summary { + cursor: pointer; padding: 18px 0; font-size: 16px; font-weight: 600; + list-style: none; display: flex; justify-content: space-between; align-items: center; +} +.faq-item summary::-webkit-details-marker { display: none; } +.faq-item summary::after { content: "+"; color: var(--accent); font-size: 22px; font-weight: 400; transition: transform .2s; } +.faq-item[open] summary::after { transform: rotate(45deg); } +.faq-a { padding: 0 0 20px; font-size: 14.5px; color: var(--text-dim); line-height: 1.75; } + +/* 收尾 CTA */ +.cta-inner { + background: linear-gradient(135deg, rgba(124,108,255,0.16), rgba(77,214,200,0.10)); + border: 1px solid var(--border); border-radius: 20px; padding: 56px 32px; text-align: center; +} +.cta-inner h2 { font-size: clamp(24px, 4vw, 34px); font-weight: 800; margin-bottom: 12px; } +.cta-inner > p { color: var(--text-dim); margin-bottom: 28px; font-size: 16px; } +.cta-cmd { + display: inline-flex; align-items: center; gap: 12px; margin: 0 auto 28px; + background: #06070b; border: 1px solid var(--border); border-radius: 12px; padding: 12px 12px 12px 20px; +} +.cta-cmd code { font-size: 16px; color: var(--accent-2); } +.cta .hero-cta { margin: 0; } + +@media (max-width: 720px) { + .pl-arrow { display: none; } + .pl-step { max-width: none; } +} + +/* ========== 浅色主题 ========== */ +html[data-theme="light"] { + --bg: #f7f8fc; + --bg-soft: #eef0f7; + --bg-card: #ffffff; + --border: #e2e5ee; + --border-soft: #eaecf3; + --text: #1a1d2b; + --text-dim: #545a70; + --text-faint: #8b91a6; +} +html[data-theme="light"] body::before { background: radial-gradient(circle, rgba(124,108,255,0.10), transparent 60%); } +html[data-theme="light"] .nav::after { background: rgba(247,248,252,0.7); } +html[data-theme="light"] .cmd-hero, html[data-theme="light"] .cmd-out, +html[data-theme="light"] #toolSel, html[data-theme="light"] .cta-cmd, +html[data-theme="light"] .doc-body pre, html[data-theme="light"] .doc-body code { background: #f0f2f8; } +html[data-theme="light"] .btn-primary, html[data-theme="light"] .pl-num { color: #fff; } +html[data-theme="light"] .chip.active { color: #fff; } + +/* ========== 导航:语言 / 主题按钮 ========== */ +.lang-switch { font-weight: 600; border: 1px solid var(--border); padding: 3px 10px; border-radius: 7px; } +.lang-switch:hover { border-color: var(--accent); } +.theme-btn { + background: var(--bg-soft); border: 1px solid var(--border); color: var(--text-dim); + width: 30px; height: 28px; border-radius: 7px; cursor: pointer; font-size: 15px; line-height: 1; +} +.theme-btn:hover { color: var(--text); border-color: var(--accent); } + +/* 卡片改为链接 */ +a.card { display: block; color: inherit; } +.card-more { display: inline-block; margin-top: 12px; font-size: 13px; color: var(--accent-2); font-weight: 600; } +a.card:hover .card-more { color: var(--accent); } + +/* ========== 三列页脚 ========== */ +.foot-cols { align-items: flex-start; } +.foot-brand strong { display: block; font-size: 16px; } +.foot-brand strong b { background: var(--accent-grad); -webkit-background-clip: text; background-clip: text; color: transparent; } +.foot-brand span { font-size: 13px; color: var(--text-faint); display: block; margin-top: 4px; max-width: 240px; } +.foot-col { display: flex; flex-direction: column; gap: 9px; } +.foot-col h4 { font-size: 13px; color: var(--text-faint); text-transform: uppercase; letter-spacing: .05em; margin-bottom: 2px; } +.foot-col a { font-size: 14px; color: var(--text-dim); transition: color .2s; } +.foot-col a:hover { color: var(--accent-2); } + +/* ========== Skill 详情(操作文档)页 ========== */ +.doc { max-width: 820px; padding-top: 36px; } +.doc-back { display: inline-block; font-size: 14px; color: var(--text-dim); margin: 8px 0 24px; transition: color .2s; } +.doc-back:hover { color: var(--accent-2); } +.doc-head { border-bottom: 1px solid var(--border-soft); padding-bottom: 28px; margin-bottom: 8px; } +.doc-titles { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; } +.doc-titles h1 { font-size: clamp(26px, 5vw, 38px); font-weight: 800; letter-spacing: -0.02em; } +.doc-titles code { font-size: 13px; color: var(--text-faint); background: var(--bg-soft); padding: 3px 9px; border-radius: 6px; } +.doc-lead { color: var(--text-dim); font-size: 16px; margin-top: 14px; line-height: 1.7; } +.doc-actions { display: flex; gap: 12px; margin-top: 22px; flex-wrap: wrap; align-items: center; } +.doc-cmd { margin: 0; max-width: 320px; } +.doc-notice { + background: rgba(124,108,255,0.1); border: 1px solid rgba(124,108,255,0.25); + border-radius: 10px; padding: 12px 16px; font-size: 14px; color: var(--text-dim); margin: 24px 0; +} + +/* Markdown 正文排版 */ +.doc-body { font-size: 15.5px; line-height: 1.8; color: var(--text); } +.doc-body h1, .doc-body h2, .doc-body h3, .doc-body h4 { font-weight: 700; line-height: 1.35; margin: 1.8em 0 .7em; letter-spacing: -0.01em; } +.doc-body h1 { font-size: 26px; } .doc-body h2 { font-size: 22px; padding-bottom: .3em; border-bottom: 1px solid var(--border-soft); } +.doc-body h3 { font-size: 18px; } .doc-body h4 { font-size: 16px; color: var(--text-dim); } +.doc-body p { margin: .9em 0; } +.doc-body a { color: var(--accent-2); text-decoration: underline; text-underline-offset: 2px; } +.doc-body ul, .doc-body ol { margin: .8em 0; padding-left: 1.5em; } +.doc-body li { margin: .35em 0; } +.doc-body li > ul, .doc-body li > ol { margin: .3em 0; } +.doc-body code { background: var(--bg-soft); padding: 2px 6px; border-radius: 5px; font-size: 13.5px; color: var(--accent-2); } +.doc-body pre { + background: #06070b; border: 1px solid var(--border); border-radius: 12px; + padding: 18px 20px; overflow-x: auto; margin: 1.1em 0; position: relative; +} +.doc-body pre code { background: none; padding: 0; color: #cdd3e6; font-size: 13.5px; line-height: 1.7; } +.doc-body pre[data-lang]:not([data-lang=""])::before { + content: attr(data-lang); position: absolute; top: 8px; right: 12px; + font-size: 11px; color: var(--text-faint); text-transform: uppercase; letter-spacing: .05em; +} +.doc-body blockquote { + border-left: 3px solid var(--accent); background: var(--bg-soft); + padding: 10px 16px; margin: 1.1em 0; border-radius: 0 8px 8px 0; color: var(--text-dim); +} +.doc-body hr { border: none; border-top: 1px solid var(--border-soft); margin: 2em 0; } +.md-table { overflow-x: auto; margin: 1.2em 0; } +.doc-body table { width: 100%; border-collapse: collapse; font-size: 14px; } +.doc-body th, .doc-body td { border: 1px solid var(--border); padding: 9px 13px; text-align: left; } +.doc-body th { background: var(--bg-soft); font-weight: 600; } + +@media (max-width: 720px) { + .nav nav { gap: 12px; } + .nav nav a:nth-child(1), .nav nav a:nth-child(2), .nav nav a:nth-child(3) { display: none; } + .foot-cols { flex-direction: row; flex-wrap: wrap; gap: 32px; } +} From 0aa3344f6fdc175ba4fab7768bcae21562240c36 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?AI=E4=B8=8D=E6=AD=A2=E8=AF=AD?= <12096460+jnMetaCode@users.noreply.github.com> Date: Sat, 20 Jun 2026 04:16:40 +0800 Subject: [PATCH 2/3] =?UTF-8?q?harden(site):=20=E7=AB=99=E7=82=B9=E5=AE=89?= =?UTF-8?q?=E5=85=A8=E5=8A=A0=E5=9B=BA=E2=80=94=E2=80=94=E4=B8=A5=E6=A0=BC?= =?UTF-8?q?=20CSP=20+=20=E5=AE=89=E5=85=A8=E5=93=8D=E5=BA=94=E5=A4=B4=20+?= =?UTF-8?q?=20=E9=93=BE=E6=8E=A5=20scheme=20=E5=87=80=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 安全审核发现两处防御纵深缺口(当前内容可信、非活跃漏洞,但公开站应补齐): 1. 部署站 _headers 此前只有 Cache-Control,缺所有安全响应头。 现新增全站 /* 头: - Content-Security-Policy:default-src 'self';script-src 'self' + 本站 内联脚本的 SHA-256 hash(构建时从生成 HTML 实测,禁用 unsafe-inline/ unsafe-eval 又不误伤自有脚本);style-src 'self';object-src 'none'; base-uri 'self';frame-ancestors 'none';form-action 'self' - X-Content-Type-Options: nosniff / X-Frame-Options: DENY / Referrer-Policy: no-referrer / Cross-Origin-Opener-Policy: same-origin / Permissions-Policy(关闭定位/麦克风/摄像头) 2. md.mjs 链接渲染未校验 scheme。现仅放行 http/https/mailto/锚点/相对 路径,阻断 javascript:/data:/vbscript: 等可执行 scheme,并转义引号防 属性逃逸。 验证:重建 42 页成功;独立核验全站 84 处内联脚本的 hash 全部命中 CSP 白名单(0 缺失 → CSP 不会打断站点);链接净化单测 5/5(正常/锚点/相对 保留,javascript:/data: 中和为 #);站点正常外链完好。外部资源仅 导航链接,不受 default-src 影响。 --- site/build.mjs | 42 +++++++++++++++++++++++++++++++++++++++++- site/md.mjs | Bin 5034 -> 5319 bytes 2 files changed, 41 insertions(+), 1 deletion(-) diff --git a/site/build.mjs b/site/build.mjs index e6472c0..8800b28 100644 --- a/site/build.mjs +++ b/site/build.mjs @@ -9,6 +9,7 @@ import { } from 'fs'; import { resolve, dirname, join } from 'path'; import { fileURLToPath } from 'url'; +import { createHash } from 'crypto'; import { renderMarkdown } from './md.mjs'; const __dirname = dirname(fileURLToPath(import.meta.url)); @@ -545,8 +546,47 @@ function build() { })); } - // Cloudflare Pages 缓存策略:静态资源长缓存,HTML 不缓存 + // 收集所有生成页面里的内联