Merge pull request #61 from jnMetaCode/feat/site

feat(site): 新增项目官网(静态站点 + Cloudflare Pages 自动部署)
This commit is contained in:
AI不止语
2026-06-20 04:51:53 +08:00
committed by GitHub
8 changed files with 1254 additions and 0 deletions

33
.github/workflows/deploy-site.yml vendored Normal file
View File

@@ -0,0 +1,33 @@
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:
# Action 全部 SHA 固定(防供应链/标签劫持,因本 job 持有可写的 Cloudflare token
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with:
node-version: 20
- name: Build site
run: node site/build.mjs
- name: Deploy to Cloudflare Pages
uses: cloudflare/wrangler-action@9acf94ace14e7dc412b076f2c5c20b8ce93c79cd # v3
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
command: pages deploy site/dist --project-name=superpowers-zh-site

3
.gitignore vendored
View File

@@ -6,6 +6,9 @@ node_modules/
inspo
triage/
# 官网生成产物(源在 site/build.mjs + site/template/
site/dist/
# 安装器生成的 skills 副本(源在 skills/ 目录)
.codex/skills/
.gemini/skills/

View File

@@ -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": [

77
site/README.md Normal file
View File

@@ -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/<name>.html # 中文 skill 详情(操作文档)× 20
en/skills/<name>.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/<name>/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 在国内访问较慢,不建议作为主站。

599
site/build.mjs Normal file
View File

@@ -0,0 +1,599 @@
#!/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 { createHash } from 'crypto';
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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
// ---- 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: 'superpowers159k+ ⭐)完整汉化 + 4 个中国原创 skills一条 npx 命令为 18 款 AI 编程工具装上系统化工作方法论。',
nav: { why: '特性', install: '安装', skills: 'Skills', tools: '支持工具', faq: 'FAQ', github: 'GitHub ↗' },
heroBadge: 'superpowers 159k+ ⭐ · 完整汉化 + 中国原创',
heroH1: '给你的 AI 编程工具<br>装上<span class="grad">真正会干活</span>的超能力',
heroLead: '{n} 个经过实战验证的工作方法论 skill —— 从头脑风暴到 TDD从系统化调试到代码审查。<br>一条命令,自动识别项目里的工具并安装。',
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: '大多数工具 <code>npx superpowers-zh</code> 会自动识别项目目录并安装;识别不出的用 <code>--tool</code> 指定。',
instLabel: '我用的是',
instNoteAuto: '在你的项目根目录运行,<b>自动识别 {name}</b> 并安装。安装后重启工具即可生效。',
instNoteManual: '{name} 无法自动识别,需用 <code>--tool</code> 显式指定。在项目根目录运行,安装后重启工具即可生效。',
skTitle: '{n} 个 Skill覆盖开发全流程',
skSub: '点击任意卡片查看完整操作文档。',
skSearch: '搜索 skill…如 调试 / review / TDD',
skEmpty: '没有匹配的 skill。',
skDetail: '查看文档 →',
tagCn: '中国原创',
ucTitle: '典型使用场景', ucSub: '每个场景背后都是一组协同工作的 skill。',
toolsTitle: '一套 skill18 款工具通用', toolsSub: '换工具不用换习惯,方法论跟着你走。',
faqTitle: '常见问题',
bookTitle: '装好之后,配上方法论效率翻倍',
bookDesc: '《AI 编程实战 · 方法论三卷书》—— 10 个 AI 编程工具完整教程 + 真实踩坑。在线书 + PDF永久免费。',
bookBtn: '免费阅读 ↗',
sponsorTitle: '赞助商',
sponsorDesc: '稳定高速的 API 中继服务,为 Claude Code、Codex 等平台提供 API 中继与 AI 生图服务。',
sponsorCta: '🙏 想出现在这里?联系 <b>jnMetaCode@qq.com</b>',
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 · 基于 <a href="https://github.com/obra/superpowers" target="_blank" rel="noopener">obra/superpowers</a> 的中文增强 fork',
copy: '复制', copied: '已复制 ✓',
backToSkills: '← 返回全部 Skill',
detailInstall: '安装此 skill',
detailSource: '在 GitHub 查看源文件 ↗',
features: [
{ icon: '🧠', t: '20 个实战方法论', d: '不是 prompt 模板,是经过跨会话对抗式压力测试调优的工作方法论 —— 从头脑风暴到 TDD、调试、代码审查。' },
{ icon: '🔌', t: '18 款工具通用', d: '一套 skillClaude Code / Cursor / Codex / Gemini CLI / Windsurf… 全适配,换工具不用换习惯。' },
{ icon: '⚡', t: '一条命令安装', d: 'npx superpowers-zh 自动识别项目里用的是哪款工具并安装,零配置,装完重启即生效。' },
{ icon: '🇨🇳', t: '中国原创 Skills', d: '中文代码审查话术、中文提交规范、中文文档排版、国内 Git 平台Gitee/Coding/极狐)配置 —— 上游没有。' },
{ icon: '📖', t: '完整汉化上游', d: '同步 obra/superpowers159k+ ⭐),核心 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/superpowers159k+ ⭐)的中文增强 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<br>superpowers that <span class="grad">actually ship</span>',
heroLead: '{n} battle-tested workflow skills — from brainstorming to TDD, systematic debugging to code review.<br>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 <code>npx superpowers-zh</code> auto-detects the project and installs; otherwise pass <code>--tool</code>.',
instLabel: "I'm using",
instNoteAuto: 'Run it in your project root — it <b>auto-detects {name}</b> and installs. Restart the tool to take effect.',
instNoteManual: '{name} can\'t be auto-detected; pass <code>--tool</code> 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 <b>jnMetaCode@qq.com</b>',
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 <a href="https://github.com/obra/superpowers" target="_blank" rel="noopener">obra/superpowers</a>',
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 `<!DOCTYPE html>
<html lang="${t.htmlLang}">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>${esc(title)}</title>
<meta name="description" content="${esc(desc)}">
<meta property="og:title" content="${esc(title)}">
<meta property="og:description" content="${esc(desc)}">
<meta property="og:type" content="website">
<link rel="icon" href="${base}assets/app-icon.png">
<link rel="stylesheet" href="${base}styles.css">
<script>(function(){try{var m=localStorage.getItem('sp-theme');if(m==='light')document.documentElement.setAttribute('data-theme','light');}catch(e){}})();</script>
${extraHead}</head>
<body>
<header class="nav">
<a class="brand" href="${base}index.html">
<img src="${base}assets/superpowers-small.svg" alt="" width="26" height="26">
<span>superpowers<b>-zh</b></span>
</a>
<nav>
<a href="${base}index.html#why">${t.nav.why}</a>
<a href="${base}index.html#install">${t.nav.install}</a>
<a href="${base}index.html#skills">${t.nav.skills}</a>
<a href="${base}index.html#faq">${t.nav.faq}</a>
<a href="https://github.com/jnMetaCode/superpowers-zh" target="_blank" rel="noopener">${t.nav.github}</a>
<a class="lang-switch" href="${langHref}">${other}</a>
<button class="theme-btn" id="themeBtn" aria-label="theme" title="切换主题">◐</button>
</nav>
</header>
${body}
<footer>
<div class="foot-inner foot-cols">
<div class="foot-brand">
<strong>superpowers<b>-zh</b></strong>
<span>${t.footTag}</span>
</div>
${t.footCols.map(col => `<div class="foot-col"><h4>${col.h}</h4>${col.links.map(l => `<a href="${l[1]}"${l[1].startsWith('http') ? ' target="_blank" rel="noopener"' : ''}>${l[0]}</a>`).join('')}</div>`).join('')}
</div>
<p class="copyright">${t.copyright}</p>
</footer>
<script src="${base}app.js"></script>
</body>
</html>`;
}
// ---- 首页正文 ----
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 `
<a class="card" href="skills/${esc(s.name)}.html" data-group="${s.group}" data-name="${esc(s.name)}" data-title="${esc(title)}">
<div class="card-head"><h3>${esc(title)}</h3>${s.china ? `<span class="tag tag-cn">${t.tagCn}</span>` : ''}</div>
<code class="card-id">${esc(s.name)}</code>
<p>${esc(d)}</p>
<span class="card-more">${t.skDetail}</span>
</a>`;
}).join('');
const filters = GROUPS.map((g, i) =>
`<button class="chip${i === 0 ? ' active' : ''}" data-filter="${g.id}">${lang === 'zh' ? g.zh : g.en}</button>`).join('');
const toolOpts = TOOLS.map((x, i) => `<option value="${i}">${esc(x.name)}${x.auto ? '' : '--tool'}</option>`).join('');
const toolWall = TOOLS.map(x => `<div class="tool-pill"><span class="tool-name">${esc(x.name)}</span><span class="tool-type">${esc(x.type)}</span></div>`).join('');
const feats = t.features.map(f => `<article class="feat"><div class="feat-icon">${f.icon}</div><h3>${esc(f.t)}</h3><p>${esc(f.d)}</p></article>`).join('');
const pipe = t.pipeline.map((p, i) => `<div class="pl-step"><div class="pl-num">${i + 1}</div><div class="pl-body"><b>${esc(p.n)}</b><span>${esc(p.d)}</span></div></div>${i < t.pipeline.length - 1 ? '<div class="pl-arrow">→</div>' : ''}`).join('');
const ucs = t.usecases.map(u => `<article class="usecase"><span class="uc-tag">${esc(u.tag)}</span><code class="uc-skills">${esc(u.skills)}</code><p>${esc(u.desc)}</p></article>`).join('');
const faqs = t.faq.map(f => `<details class="faq-item"><summary>${esc(f.q)}</summary><div class="faq-a">${esc(f.a)}</div></details>`).join('');
return `
<main id="top">
<section class="hero">
<div class="badge">${t.heroBadge}</div>
<h1>${t.heroH1}</h1>
<p class="lead">${fill(t.heroLead, { n: total })}</p>
<div class="cmd-hero" data-copy="npx superpowers-zh"><code>$ npx superpowers-zh</code><button class="copy-btn">${t.copy}</button></div>
<div class="hero-cta">
<a class="btn btn-primary" href="#install">${t.heroBtn1}</a>
<a class="btn btn-ghost" href="https://github.com/jnMetaCode/superpowers-zh" target="_blank" rel="noopener">${t.heroBtn2}</a>
</div>
<div class="stats">
<div><b>${total}</b><span>${t.stats[0]}</span></div>
<div><b>${cnCount}</b><span>${t.stats[1]}</span></div>
<div><b>18</b><span>${t.stats[2]}</span></div>
<div><b>v${PKG.version}</b><span>${t.stats[3]}</span></div>
</div>
</section>
<section id="why" class="why">
<h2 class="section-title">${t.whyTitle}</h2>
<p class="section-sub">${t.whySub}</p>
<div class="feat-grid">${feats}</div>
</section>
<section class="pipeline">
<h2 class="section-title">${t.plTitle}</h2>
<p class="section-sub">${t.plSub}</p>
<div class="pl-track">${pipe}</div>
</section>
<section class="compare">
<h2 class="section-title">${t.cmpTitle}</h2>
<div class="compare-grid">
<div class="compare-col bad"><div class="compare-label">${t.cmpBad}</div><pre>${esc(t.cmpBadPre)}</pre></div>
<div class="compare-col good"><div class="compare-label">${t.cmpGood}</div><pre>${esc(t.cmpGoodPre)}</pre></div>
</div>
</section>
<section id="install" class="install">
<h2 class="section-title">${t.instTitle}</h2>
<p class="section-sub">${t.instSub}</p>
<div class="install-box">
<label for="toolSel">${t.instLabel}</label>
<select id="toolSel">${toolOpts}</select>
<div class="cmd-out" data-copy="npx superpowers-zh"><code id="cmdText">npx superpowers-zh</code><button class="copy-btn">${t.copy}</button></div>
<p class="install-note" id="installNote"></p>
</div>
</section>
<section id="skills" class="skills">
<h2 class="section-title">${fill(t.skTitle, { n: total })}</h2>
<p class="section-sub">${t.skSub}</p>
<div class="skill-controls">
<input id="search" type="search" placeholder="${esc(t.skSearch)}" autocomplete="off">
<div class="chips">${filters}</div>
</div>
<div class="grid" id="grid">${cards}</div>
<p class="empty" id="empty" hidden>${t.skEmpty}</p>
</section>
<section class="usecases">
<h2 class="section-title">${t.ucTitle}</h2>
<p class="section-sub">${t.ucSub}</p>
<div class="uc-grid">${ucs}</div>
</section>
<section id="tools" class="tools">
<h2 class="section-title">${t.toolsTitle}</h2>
<p class="section-sub">${t.toolsSub}</p>
<div class="tool-wall">${toolWall}</div>
</section>
<section id="faq" class="faq">
<h2 class="section-title">${t.faqTitle}</h2>
<div class="faq-list">${faqs}</div>
</section>
<section class="book"><div class="book-inner"><div>
<h2>${t.bookTitle}</h2><p>${t.bookDesc}</p>
<a class="btn btn-primary" href="https://book.aibuzhiyu.com/" target="_blank" rel="noopener">${t.bookBtn}</a>
</div></div></section>
<section class="sponsor">
<h2 class="section-title">${t.sponsorTitle}</h2>
<a class="sponsor-card" href="https://www.5cookie.cc/sign-up?aff=Pj7u" target="_blank" rel="noopener">
<img src="assets/sponsors/5cookie-code.png" alt="5Cookie Code" width="160">
<div><b>5Cookie Code</b><span>${t.sponsorDesc}</span></div>
</a>
<p class="sponsor-cta">${t.sponsorCta}</p>
</section>
<section class="cta"><div class="cta-inner">
<h2>${t.ctaTitle}</h2><p>${fill(t.ctaDesc, { n: total })}</p>
<div class="cta-cmd" data-copy="npx superpowers-zh"><code>$ npx superpowers-zh</code><button class="copy-btn">${t.copy}</button></div>
<div class="hero-cta">
<a class="btn btn-primary" href="#install">${t.ctaBtn1}</a>
<a class="btn btn-ghost" href="https://github.com/jnMetaCode/superpowers-zh" target="_blank" rel="noopener">${t.ctaBtn2}</a>
</div>
</div></section>
</main>
<script>window.__TOOLS__=${toolData};window.__I18N__={auto:${JSON.stringify(t.instNoteAuto)},manual:${JSON.stringify(t.instNoteManual)},copy:${JSON.stringify(t.copy)},copied:${JSON.stringify(t.copied)}};</script>`;
}
// ---- 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'
? '<div class="doc-notice">📖 This skill\'s content is written in Chinese — superpowers-zh is a Chinese-localized toolkit.</div>'
: '';
const srcUrl = `https://github.com/jnMetaCode/superpowers-zh/blob/main/skills/${skill.name}/SKILL.md`;
return `
<main class="doc">
<a class="doc-back" href="index.html#skills">${t.backToSkills}</a>
<header class="doc-head">
<div class="doc-titles">
<h1>${esc(title)}</h1>
<code>${esc(skill.name)}</code>
${skill.china ? `<span class="tag tag-cn">${t.tagCn}</span>` : ''}
</div>
<p class="doc-lead">${esc(lang === 'zh' ? skill.desc : (skill.descEn || skill.desc))}</p>
<div class="doc-actions">
<div class="cmd-out doc-cmd" data-copy="npx superpowers-zh"><code>$ npx superpowers-zh</code><button class="copy-btn">${t.copy}</button></div>
<a class="btn btn-ghost" href="${srcUrl}" target="_blank" rel="noopener">${t.detailSource}</a>
</div>
</header>
${cnNotice}
<article class="doc-body">${bodyHtml}</article>
<a class="doc-back" href="index.html#skills">${t.backToSkills}</a>
</main>
<script>window.__I18N__={copy:${JSON.stringify(t.copy)},copied:${JSON.stringify(t.copied)}};</script>`;
}
// ---- 构建 ----
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`,
}));
}
// 收集所有生成页面里的内联 <script> 内容,算 SHA-256 作为 CSP hash 白名单。
// 本站脚本由本生成器产出(可信),用 hash 即可严格禁用 'unsafe-inline'/'unsafe-eval'
// 而不误伤自有内联脚本——注入的外来脚本则被 CSP 拦截。
const scriptHashes = new Set();
const collectScriptHashes = (dir) => {
for (const ent of readdirSync(dir, { withFileTypes: true })) {
const p = join(dir, ent.name);
if (ent.isDirectory()) { collectScriptHashes(p); continue; }
if (!ent.name.endsWith('.html')) continue;
const html = readFileSync(p, 'utf8');
for (const m of html.matchAll(/<script>([\s\S]*?)<\/script>/g)) {
scriptHashes.add("'sha256-" + createHash('sha256').update(m[1], 'utf8').digest('base64') + "'");
}
}
};
collectScriptHashes(DIST);
// 严格 CSP脚本仅允许 'self' + 本站内联脚本的 hash样式仅 'self'(无内联样式);
// 禁用插件/内联事件;锁死 base-uri 与 frame 祖先(防点击劫持)。外链均为 <a> 导航,不受影响。
const csp = [
"default-src 'self'",
"script-src 'self' " + [...scriptHashes].join(' '),
"style-src 'self'",
"img-src 'self' data:",
"font-src 'self'",
"connect-src 'self'",
"object-src 'none'",
"base-uri 'self'",
"frame-ancestors 'none'",
"form-action 'self'",
].join('; ');
// Cloudflare Pages全站安全响应头/*+ 缓存策略静态资源长缓存HTML 不缓存)
writeFileSync(join(DIST, '_headers'),
'/*\n' +
' Content-Security-Policy: ' + csp + '\n' +
' X-Content-Type-Options: nosniff\n' +
' X-Frame-Options: DENY\n' +
' Referrer-Policy: no-referrer\n' +
' Cross-Origin-Opener-Policy: same-origin\n' +
' Permissions-Policy: geolocation=(), microphone=(), camera=()\n' +
'/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();

BIN
site/md.mjs Normal file

Binary file not shown.

92
site/template/app.js Normal file
View File

@@ -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, '&amp;').replace(/</g, '&lt;').replace(/>/g, '&gt;');
}
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);
})();

448
site/template/styles.css Normal file
View File

@@ -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; }
}