mirror of
https://github.com/catlog22/Claude-Code-Workflow.git
synced 2026-02-13 02:41:50 +08:00
feat(discovery): enhance discovery index reading and issue exporting
- Improved the reading of the discovery index by adding a fallback mechanism to scan directories for discovery folders if the index.json is invalid or missing. - Added sorting of discoveries by creation time in descending order. - Enhanced the `appendToIssuesJsonl` function to include deduplication logic for issues based on ID and source finding ID. - Updated the discovery route handler to reflect the number of issues added and skipped during export. - Introduced UI elements for selecting and deselecting findings in the dashboard. - Added CSS styles for exported findings and action buttons. - Implemented search functionality for filtering findings based on title, file, and description. - Added internationalization support for new UI elements. - Created scripts for automated API extraction from various project types, including FastAPI and TypeScript. - Documented the API extraction process and library bundling instructions.
This commit is contained in:
@@ -38,9 +38,9 @@ Generate comprehensive, interactive software manuals in TiddlyWiki-style single-
|
||||
1. **主 Agent 编排,子 Agent 执行**: 所有繁重计算委托给 `universal-executor` 子 Agent
|
||||
2. **Brief Returns**: Agents return path + summary, not full content (avoid context overflow)
|
||||
3. **System Agents**: 使用 `cli-explore-agent` (探索) 和 `universal-executor` (执行)
|
||||
4. **Chrome MCP Integration**: Batch screenshot capture with Base64 embedding
|
||||
4. **成熟库内嵌**: marked.js (MD 解析) + highlight.js (语法高亮),无 CDN 依赖
|
||||
5. **Single-File HTML**: TiddlyWiki-style interactive document with embedded resources
|
||||
6. **User-Friendly Writing**: Clear, step-by-step guides with difficulty levels
|
||||
6. **动态标签**: 根据实际章节自动生成导航标签
|
||||
|
||||
## Execution Flow
|
||||
|
||||
@@ -54,6 +54,10 @@ Generate comprehensive, interactive software manuals in TiddlyWiki-style single-
|
||||
│ → 并行探索: architecture, ui-routes, api-endpoints, config │
|
||||
│ → Output: exploration-*.json │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Phase 2.5: API Extraction (extract_apis.py) │
|
||||
│ → 自动提取: FastAPI/TypeDoc/pdoc │
|
||||
│ → Output: api-docs/{backend,frontend,modules}/*.md │
|
||||
├─────────────────────────────────────────────────────────────────┤
|
||||
│ Phase 3: Parallel Analysis (universal-executor × 6) │
|
||||
│ → 6 个子 Agent 并行: overview, ui-guide, api-docs, config, │
|
||||
│ troubleshooting, code-examples │
|
||||
@@ -161,17 +165,20 @@ Bash(`mkdir "${dir}\\sections" && mkdir "${dir}\\screenshots" && mkdir "${dir}\\
|
||||
|
||||
| Document | Purpose |
|
||||
|----------|---------|
|
||||
| [phases/01-requirements-discovery.md](phases/01-requirements-discovery.md) | User config collection |
|
||||
| [phases/02-project-exploration.md](phases/02-project-exploration.md) | Project type detection |
|
||||
| [phases/03-parallel-analysis.md](phases/03-parallel-analysis.md) | 6 Agent orchestration |
|
||||
| [phases/03.5-consolidation.md](phases/03.5-consolidation.md) | Cross-section synthesis |
|
||||
| [phases/04-screenshot-capture.md](phases/04-screenshot-capture.md) | Chrome MCP integration |
|
||||
| [phases/05-html-assembly.md](phases/05-html-assembly.md) | HTML generation |
|
||||
| [phases/06-iterative-refinement.md](phases/06-iterative-refinement.md) | Quality iteration |
|
||||
| [specs/quality-standards.md](specs/quality-standards.md) | Quality gates |
|
||||
| [specs/writing-style.md](specs/writing-style.md) | User-friendly writing |
|
||||
| [specs/html-template.md](specs/html-template.md) | HTML template spec |
|
||||
| [templates/tiddlywiki-shell.html](templates/tiddlywiki-shell.html) | HTML template |
|
||||
| [scripts/typedoc-runner.md](scripts/typedoc-runner.md) | TypeDoc execution |
|
||||
| [scripts/swagger-runner.md](scripts/swagger-runner.md) | Swagger/OpenAPI |
|
||||
| [scripts/screenshot-helper.md](scripts/screenshot-helper.md) | Chrome MCP guide |
|
||||
| [phases/01-requirements-discovery.md](phases/01-requirements-discovery.md) | 用户配置收集 |
|
||||
| [phases/02-project-exploration.md](phases/02-project-exploration.md) | 项目类型检测 |
|
||||
| [phases/02.5-api-extraction.md](phases/02.5-api-extraction.md) | API 自动提取 |
|
||||
| [phases/03-parallel-analysis.md](phases/03-parallel-analysis.md) | 6 Agent 并行分析 |
|
||||
| [phases/03.5-consolidation.md](phases/03.5-consolidation.md) | 整合与质量检查 |
|
||||
| [phases/04-screenshot-capture.md](phases/04-screenshot-capture.md) | Chrome MCP 截图 |
|
||||
| [phases/05-html-assembly.md](phases/05-html-assembly.md) | HTML 组装 |
|
||||
| [phases/06-iterative-refinement.md](phases/06-iterative-refinement.md) | 迭代优化 |
|
||||
| [specs/quality-standards.md](specs/quality-standards.md) | 质量标准 |
|
||||
| [specs/writing-style.md](specs/writing-style.md) | 写作风格 |
|
||||
| [templates/tiddlywiki-shell.html](templates/tiddlywiki-shell.html) | HTML 模板 |
|
||||
| [templates/css/wiki-base.css](templates/css/wiki-base.css) | 基础样式 |
|
||||
| [templates/css/wiki-dark.css](templates/css/wiki-dark.css) | 暗色主题 |
|
||||
| [scripts/bundle-libraries.md](scripts/bundle-libraries.md) | 库文件打包 |
|
||||
| [scripts/api-extractor.md](scripts/api-extractor.md) | API 提取说明 |
|
||||
| [scripts/extract_apis.py](scripts/extract_apis.py) | API 提取脚本 |
|
||||
| [scripts/screenshot-helper.md](scripts/screenshot-helper.md) | 截图辅助 |
|
||||
|
||||
161
.claude/skills/software-manual/phases/02.5-api-extraction.md
Normal file
161
.claude/skills/software-manual/phases/02.5-api-extraction.md
Normal file
@@ -0,0 +1,161 @@
|
||||
# Phase 2.5: API Extraction
|
||||
|
||||
在项目探索后、并行分析前,自动提取 API 文档。
|
||||
|
||||
## 核心原则
|
||||
|
||||
**使用成熟工具提取,确保输出格式与 wiki 模板兼容。**
|
||||
|
||||
## 执行流程
|
||||
|
||||
```javascript
|
||||
const config = JSON.parse(Read(`${workDir}/manual-config.json`));
|
||||
|
||||
// 检查项目路径配置
|
||||
const apiSources = config.api_sources || detectApiSources(config.project_path);
|
||||
|
||||
// 执行 API 提取
|
||||
Bash({
|
||||
command: `python .claude/skills/software-manual/scripts/extract_apis.py -o "${workDir}" -p ${apiSources.join(' ')}`
|
||||
});
|
||||
|
||||
// 验证输出
|
||||
const apiDocsDir = `${workDir}/api-docs`;
|
||||
const extractedFiles = Glob(`${apiDocsDir}/**/*.{json,md}`);
|
||||
console.log(`Extracted ${extractedFiles.length} API documentation files`);
|
||||
```
|
||||
|
||||
## 支持的项目类型
|
||||
|
||||
| 类型 | 检测方式 | 提取工具 | 输出格式 |
|
||||
|------|----------|----------|----------|
|
||||
| FastAPI | `app/main.py` + FastAPI import | OpenAPI JSON | `openapi.json` + `API_SUMMARY.md` |
|
||||
| Next.js | `package.json` + next | TypeDoc | `*.md` (Markdown) |
|
||||
| Python Module | `__init__.py` + setup.py/pyproject.toml | pdoc | `*.md` (Markdown) |
|
||||
| Express | `package.json` + express | swagger-jsdoc | `openapi.json` |
|
||||
| NestJS | `package.json` + @nestjs | @nestjs/swagger | `openapi.json` |
|
||||
|
||||
## 输出格式规范
|
||||
|
||||
### Markdown 兼容性要求
|
||||
|
||||
确保输出 Markdown 与 wiki CSS 样式兼容:
|
||||
|
||||
```markdown
|
||||
# API Reference → <h1> (wiki-base.css)
|
||||
|
||||
## Endpoints → <h2>
|
||||
|
||||
| Method | Path | Summary | → <table> 蓝色表头
|
||||
|--------|------|---------|
|
||||
| `GET` | `/api/...` | ... | → <code> 红色高亮
|
||||
|
||||
### GET /api/users → <h3>
|
||||
|
||||
\`\`\`json → <pre><code> 深色背景
|
||||
{
|
||||
"id": 1,
|
||||
"name": "example"
|
||||
}
|
||||
\`\`\`
|
||||
|
||||
- Parameter: `id` (required) → <ul><li> + <code>
|
||||
```
|
||||
|
||||
### 格式验证检查
|
||||
|
||||
```javascript
|
||||
function validateApiDocsFormat(apiDocsDir) {
|
||||
const issues = [];
|
||||
const mdFiles = Glob(`${apiDocsDir}/**/*.md`);
|
||||
|
||||
for (const file of mdFiles) {
|
||||
const content = Read(file);
|
||||
|
||||
// 检查表格格式
|
||||
if (content.includes('|') && !content.match(/\|.*\|.*\|/)) {
|
||||
issues.push(`${file}: 表格格式不完整`);
|
||||
}
|
||||
|
||||
// 检查代码块语言标注
|
||||
const codeBlocks = content.match(/```(\w*)\n/g) || [];
|
||||
const unlabeled = codeBlocks.filter(b => b === '```\n');
|
||||
if (unlabeled.length > 0) {
|
||||
issues.push(`${file}: ${unlabeled.length} 个代码块缺少语言标注`);
|
||||
}
|
||||
|
||||
// 检查标题层级
|
||||
if (!content.match(/^# /m)) {
|
||||
issues.push(`${file}: 缺少一级标题`);
|
||||
}
|
||||
}
|
||||
|
||||
return issues;
|
||||
}
|
||||
```
|
||||
|
||||
## 项目配置示例
|
||||
|
||||
在 `manual-config.json` 中配置 API 源:
|
||||
|
||||
```json
|
||||
{
|
||||
"software": {
|
||||
"name": "Hydro Generator Workbench",
|
||||
"type": "web"
|
||||
},
|
||||
"api_sources": {
|
||||
"backend": {
|
||||
"path": "D:/dongdiankaifa9/backend",
|
||||
"type": "fastapi",
|
||||
"entry": "app.main:app"
|
||||
},
|
||||
"frontend": {
|
||||
"path": "D:/dongdiankaifa9/frontend",
|
||||
"type": "typescript",
|
||||
"entries": ["lib", "hooks", "components"]
|
||||
},
|
||||
"hydro_generator_module": {
|
||||
"path": "D:/dongdiankaifa9/hydro_generator_module",
|
||||
"type": "python"
|
||||
},
|
||||
"multiphysics_network": {
|
||||
"path": "D:/dongdiankaifa9/multiphysics_network",
|
||||
"type": "python"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 输出结构
|
||||
|
||||
```
|
||||
{workDir}/api-docs/
|
||||
├── backend/
|
||||
│ ├── openapi.json # OpenAPI 3.0 规范
|
||||
│ └── API_SUMMARY.md # Markdown 摘要(wiki 兼容)
|
||||
├── frontend/
|
||||
│ ├── modules.md # TypeDoc 模块文档
|
||||
│ ├── classes/ # 类文档
|
||||
│ └── functions/ # 函数文档
|
||||
├── hydro_generator/
|
||||
│ ├── assembler.md # pdoc 模块文档
|
||||
│ ├── blueprint.md
|
||||
│ └── builders/
|
||||
└── multiphysics/
|
||||
├── analysis_domain.md
|
||||
├── builders.md
|
||||
└── compilers.md
|
||||
```
|
||||
|
||||
## 质量门禁
|
||||
|
||||
- [ ] 所有配置的 API 源已提取
|
||||
- [ ] Markdown 格式与 wiki CSS 兼容
|
||||
- [ ] 表格正确渲染(蓝色表头)
|
||||
- [ ] 代码块有语言标注
|
||||
- [ ] 无空文件或错误文件
|
||||
|
||||
## 下一阶段
|
||||
|
||||
→ [Phase 3: Parallel Analysis](03-parallel-analysis.md)
|
||||
@@ -11,51 +11,66 @@ const AGENT_CONFIGS = {
|
||||
output: 'section-overview.md',
|
||||
task: '撰写产品概览、核心功能、快速入门指南',
|
||||
focus: '产品定位、目标用户、5步快速入门、系统要求',
|
||||
input: ['exploration-architecture.json', 'README.md', 'package.json']
|
||||
input: ['exploration-architecture.json', 'README.md', 'package.json'],
|
||||
tag: 'getting-started'
|
||||
},
|
||||
'ui-guide': {
|
||||
role: 'UX Expert',
|
||||
output: 'section-ui-guide.md',
|
||||
task: '撰写界面操作指南,标注所有需要截图的 UI 元素',
|
||||
focus: '界面布局、导航流程、功能操作、快捷键',
|
||||
input: ['exploration-ui-routes.json', 'pages/**', 'views/**'],
|
||||
'interface-guide': {
|
||||
role: 'Product Designer',
|
||||
output: 'section-interface.md',
|
||||
task: '撰写界面或交互指南(Web 截图、CLI 命令交互、桌面应用操作)',
|
||||
focus: '视觉布局、交互流程、命令行参数、输入/输出示例',
|
||||
input: ['exploration-ui-routes.json', 'src/**', 'pages/**', 'views/**', 'components/**', 'src/commands/**'],
|
||||
tag: 'interface',
|
||||
screenshot_rules: `
|
||||
每个关键 UI 交互点必须插入截图标记:
|
||||
<!-- SCREENSHOT: id="ss-{功能}-{状态}" url="{路由}" selector="{CSS选择器}" wait_for="{等待元素}" description="{描述}" -->
|
||||
根据项目类型标注交互点:
|
||||
|
||||
示例:
|
||||
- 页面全貌: <!-- SCREENSHOT: id="ss-dashboard-overview" url="/dashboard" description="仪表盘主界面" -->
|
||||
- 特定组件: <!-- SCREENSHOT: id="ss-login-form" url="/login" selector=".login-form" description="登录表单" -->
|
||||
- 交互状态: <!-- SCREENSHOT: id="ss-modal-open" url="/settings" selector=".modal" wait_for=".modal.show" description="设置弹窗" -->
|
||||
[Web] <!-- SCREENSHOT: id="ss-{功能}" url="{路由}" selector="{CSS选择器}" description="{描述}" -->
|
||||
[CLI] 使用代码块展示命令交互:
|
||||
\`\`\`bash
|
||||
$ command --flag value
|
||||
Expected output here
|
||||
\`\`\`
|
||||
[Desktop] <!-- SCREENSHOT: id="ss-{功能}" description="{描述}" -->
|
||||
`
|
||||
},
|
||||
'api-docs': {
|
||||
role: 'API Architect',
|
||||
output: 'section-api-reference.md',
|
||||
task: '撰写 REST API 和前端 API 参考文档',
|
||||
focus: 'API 概览、端点分类、请求/响应示例、错误码',
|
||||
input: ['exploration-api-endpoints.json', 'controllers/**', 'routes/**']
|
||||
'api-reference': {
|
||||
role: 'Technical Architect',
|
||||
output: 'section-reference.md',
|
||||
task: '撰写接口参考文档(REST API / 函数库 / CLI 命令)',
|
||||
focus: '函数签名、端点定义、参数说明、返回值、错误代码',
|
||||
pre_extract: 'python .claude/skills/software-manual/scripts/extract_apis.py -o ${workDir}',
|
||||
input: [
|
||||
'${workDir}/api-docs/backend/openapi.json', // FastAPI OpenAPI
|
||||
'${workDir}/api-docs/backend/API_SUMMARY.md', // Backend summary
|
||||
'${workDir}/api-docs/frontend/**/*.md', // TypeDoc output
|
||||
'${workDir}/api-docs/hydro_generator/**/*.md', // Python module
|
||||
'${workDir}/api-docs/multiphysics/**/*.md' // Python module
|
||||
],
|
||||
tag: 'api'
|
||||
},
|
||||
config: {
|
||||
role: 'DevOps Engineer',
|
||||
output: 'section-configuration.md',
|
||||
task: '撰写配置指南,涵盖环境变量、配置文件、部署设置',
|
||||
focus: '环境变量表格、配置文件格式、部署选项、安全设置',
|
||||
input: ['exploration-config.json', '.env.example', 'config/**']
|
||||
input: ['exploration-config.json', '.env.example', 'config/**', '*.config.*'],
|
||||
tag: 'config'
|
||||
},
|
||||
troubleshooting: {
|
||||
role: 'Support Engineer',
|
||||
output: 'section-troubleshooting.md',
|
||||
task: '撰写故障排查指南,涵盖常见问题、错误码、FAQ',
|
||||
focus: '常见问题与解决方案、错误码参考、FAQ、获取帮助',
|
||||
input: ['all exploration files', 'error handling code']
|
||||
input: ['docs/troubleshooting.md', 'src/**/errors.*', 'src/**/exceptions.*', 'TROUBLESHOOTING.md'],
|
||||
tag: 'troubleshooting'
|
||||
},
|
||||
'code-examples': {
|
||||
role: 'Developer Advocate',
|
||||
output: 'section-examples.md',
|
||||
task: '撰写多难度级别代码示例(入门40%/进阶40%/高级20%)',
|
||||
focus: '完整可运行代码、分步解释、预期输出、最佳实践',
|
||||
input: ['all exploration files', 'examples/**', 'tests/**']
|
||||
input: ['examples/**', 'tests/**', 'demo/**', 'samples/**'],
|
||||
tag: 'examples'
|
||||
}
|
||||
};
|
||||
```
|
||||
@@ -65,7 +80,16 @@ const AGENT_CONFIGS = {
|
||||
```javascript
|
||||
const config = JSON.parse(Read(`${workDir}/manual-config.json`));
|
||||
|
||||
// 并行启动 6 个 universal-executor
|
||||
// 1. 预提取 API 文档(如有 pre_extract 配置)
|
||||
for (const [name, cfg] of Object.entries(AGENT_CONFIGS)) {
|
||||
if (cfg.pre_extract) {
|
||||
const cmd = cfg.pre_extract.replace(/\$\{workDir\}/g, workDir);
|
||||
console.log(`[Pre-extract] ${name}: ${cmd}`);
|
||||
Bash({ command: cmd });
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 并行启动 6 个 universal-executor
|
||||
const tasks = Object.entries(AGENT_CONFIGS).map(([name, cfg]) =>
|
||||
Task({
|
||||
subagent_type: 'universal-executor',
|
||||
@@ -83,35 +107,51 @@ const results = await Promise.all(tasks);
|
||||
function buildAgentPrompt(name, cfg, config, workDir) {
|
||||
const screenshotSection = cfg.screenshot_rules
|
||||
? `\n[SCREENSHOT RULES]\n${cfg.screenshot_rules}`
|
||||
: '\n[SCREENSHOT]\n截图标记: <!-- SCREENSHOT: id="ss-xxx" url="/path" description="xxx" -->';
|
||||
: '';
|
||||
|
||||
return `
|
||||
[ROLE] ${cfg.role}
|
||||
|
||||
[PROJECT CONTEXT]
|
||||
项目类型: ${config.software.type} (web/cli/sdk/desktop)
|
||||
语言: ${config.software.language || 'auto-detect'}
|
||||
名称: ${config.software.name}
|
||||
|
||||
[TASK]
|
||||
${cfg.task}
|
||||
输出: ${workDir}/sections/${cfg.output}
|
||||
|
||||
[INPUT]
|
||||
- Read: ${workDir}/manual-config.json
|
||||
- Read: ${cfg.input.map(f => `${workDir}/exploration/${f}`).join(', ')}
|
||||
- 配置: ${workDir}/manual-config.json
|
||||
- 探索结果: ${workDir}/exploration/
|
||||
- 扫描路径: ${cfg.input.join(', ')}
|
||||
|
||||
[STYLE]
|
||||
- 用户友好语言,避免技术术语
|
||||
- 步骤编号清晰
|
||||
- 代码块标注语言
|
||||
[CONTENT REQUIREMENTS]
|
||||
- 标题层级: # ## ### (最多3级)
|
||||
- 代码块: \`\`\`language ... \`\`\` (必须标注语言)
|
||||
- 表格: | col1 | col2 | 格式
|
||||
- 列表: 有序 1. 2. 3. / 无序 - - -
|
||||
- 内联代码: \`code\`
|
||||
- 链接: [text](url)
|
||||
${screenshotSection}
|
||||
|
||||
[FOCUS]
|
||||
${cfg.focus}
|
||||
|
||||
[OUTPUT FORMAT]
|
||||
Markdown 文件,包含:
|
||||
- 清晰的章节结构
|
||||
- 具体的代码示例
|
||||
- 参数/配置表格
|
||||
- 常见用例说明
|
||||
|
||||
[RETURN JSON]
|
||||
{
|
||||
"status": "completed",
|
||||
"output_file": "sections/${cfg.output}",
|
||||
"summary": "<50字>",
|
||||
"screenshots_needed": [{ "id": "ss-xxx", "url": "/path", "selector": ".class", "description": "..." }],
|
||||
"cross_references": []
|
||||
"tag": "${cfg.tag}",
|
||||
"screenshots_needed": []
|
||||
}
|
||||
`;
|
||||
}
|
||||
|
||||
@@ -29,29 +29,40 @@ function buildAssemblyPrompt(config, workDir) {
|
||||
[ROLE] HTML Assembler
|
||||
|
||||
[TASK]
|
||||
生成 TiddlyWiki 风格的交互式 HTML 手册
|
||||
生成 TiddlyWiki 风格的交互式 HTML 手册(使用成熟库,无外部 CDN 依赖)
|
||||
|
||||
[INPUT]
|
||||
- 模板: .claude/skills/software-manual/templates/tiddlywiki-shell.html
|
||||
- CSS: .claude/skills/software-manual/templates/css/wiki-base.css, wiki-dark.css
|
||||
- 配置: ${workDir}/manual-config.json
|
||||
- 章节: ${workDir}/sections/section-*.md
|
||||
- Agent 结果: ${workDir}/agent-results.json (含 tag 信息)
|
||||
- 截图: ${workDir}/screenshots/
|
||||
|
||||
[LIBRARIES TO EMBED]
|
||||
1. marked.js (v14+) - Markdown 转 HTML
|
||||
- 从 https://unpkg.com/marked/marked.min.js 获取内容内嵌
|
||||
2. highlight.js (v11+) - 代码语法高亮
|
||||
- 核心 + 常用语言包 (js, ts, python, bash, json, yaml, html, css)
|
||||
- 使用 github-dark 主题
|
||||
|
||||
[STEPS]
|
||||
1. 读取 HTML 模板和 CSS
|
||||
2. 逐个读取 section-*.md,转换为 HTML tiddlers
|
||||
3. 处理 <!-- SCREENSHOT: id="..." --> 标记,嵌入 Base64 图片
|
||||
4. 生成目录、搜索索引
|
||||
5. 组装最终 HTML,写入 ${workDir}/${config.software.name}-使用手册.html
|
||||
6. 生成构建报告 ${workDir}/build-report.json
|
||||
2. 内嵌 marked.js 和 highlight.js 代码
|
||||
3. 读取 agent-results.json 提取各章节 tag
|
||||
4. 动态生成 {{TAG_BUTTONS_HTML}} (基于实际使用的 tags)
|
||||
5. 逐个读取 section-*.md,使用 marked 转换为 HTML
|
||||
6. 为代码块添加 data-language 属性和语法高亮
|
||||
7. 处理 <!-- SCREENSHOT: id="..." --> 标记,嵌入 Base64 图片
|
||||
8. 生成目录、搜索索引
|
||||
9. 组装最终 HTML,写入 ${workDir}/${config.software.name}-使用手册.html
|
||||
|
||||
[HTML FEATURES]
|
||||
- 搜索: 全文检索 + 高亮
|
||||
- 折叠: 章节可展开/收起
|
||||
- 标签: 分类过滤
|
||||
- 主题: 亮/暗模式切换
|
||||
- 离线: 所有资源内嵌
|
||||
[CONTENT FORMATTING]
|
||||
- 代码块: 深色背景 + 语言标签 + 语法高亮
|
||||
- 表格: 蓝色表头 + 边框 + 悬停效果
|
||||
- 内联代码: 红色高亮
|
||||
- 列表: 有序/无序样式增强
|
||||
- 左侧导航: 固定侧边栏 + TOC
|
||||
|
||||
[RETURN JSON]
|
||||
{
|
||||
@@ -59,6 +70,7 @@ function buildAssemblyPrompt(config, workDir) {
|
||||
"output_file": "${config.software.name}-使用手册.html",
|
||||
"file_size": "<size>",
|
||||
"sections_count": <n>,
|
||||
"tags_generated": [],
|
||||
"screenshots_embedded": <n>
|
||||
}
|
||||
`;
|
||||
|
||||
245
.claude/skills/software-manual/scripts/api-extractor.md
Normal file
245
.claude/skills/software-manual/scripts/api-extractor.md
Normal file
@@ -0,0 +1,245 @@
|
||||
# API 文档提取脚本
|
||||
|
||||
根据项目类型自动提取 API 文档,支持 FastAPI、Next.js、Python 模块。
|
||||
|
||||
## 支持的技术栈
|
||||
|
||||
| 类型 | 技术栈 | 工具 | 输出格式 |
|
||||
|------|--------|------|----------|
|
||||
| Backend | FastAPI | openapi-to-md | Markdown |
|
||||
| Frontend | Next.js/TypeScript | TypeDoc | Markdown |
|
||||
| Python Module | Python | pdoc | Markdown/HTML |
|
||||
|
||||
## 使用方法
|
||||
|
||||
### 1. FastAPI Backend (OpenAPI)
|
||||
|
||||
```bash
|
||||
# 提取 OpenAPI JSON
|
||||
cd D:/dongdiankaifa9/backend
|
||||
python -c "
|
||||
from app.main import app
|
||||
import json
|
||||
print(json.dumps(app.openapi(), indent=2))
|
||||
" > api-docs/openapi.json
|
||||
|
||||
# 转换为 Markdown (使用 widdershins)
|
||||
npx widdershins api-docs/openapi.json -o api-docs/API_REFERENCE.md --language_tabs 'python:Python' 'javascript:JavaScript' 'bash:cURL'
|
||||
```
|
||||
|
||||
**备选方案 (无需启动服务)**:
|
||||
```python
|
||||
# scripts/extract_fastapi_openapi.py
|
||||
import sys
|
||||
sys.path.insert(0, 'D:/dongdiankaifa9/backend')
|
||||
|
||||
from app.main import app
|
||||
import json
|
||||
|
||||
openapi_schema = app.openapi()
|
||||
with open('api-docs/openapi.json', 'w', encoding='utf-8') as f:
|
||||
json.dump(openapi_schema, f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"Extracted {len(openapi_schema.get('paths', {}))} endpoints")
|
||||
```
|
||||
|
||||
### 2. Next.js Frontend (TypeDoc)
|
||||
|
||||
```bash
|
||||
cd D:/dongdiankaifa9/frontend
|
||||
|
||||
# 安装 TypeDoc
|
||||
npm install --save-dev typedoc typedoc-plugin-markdown
|
||||
|
||||
# 生成文档
|
||||
npx typedoc --plugin typedoc-plugin-markdown \
|
||||
--out api-docs \
|
||||
--entryPoints "./lib" "./hooks" "./components" \
|
||||
--entryPointStrategy expand \
|
||||
--exclude "**/node_modules/**" \
|
||||
--exclude "**/*.test.*" \
|
||||
--readme none
|
||||
```
|
||||
|
||||
**typedoc.json 配置**:
|
||||
```json
|
||||
{
|
||||
"$schema": "https://typedoc.org/schema.json",
|
||||
"entryPoints": ["./lib", "./hooks", "./components"],
|
||||
"entryPointStrategy": "expand",
|
||||
"out": "api-docs",
|
||||
"plugin": ["typedoc-plugin-markdown"],
|
||||
"exclude": ["**/node_modules/**", "**/*.test.*", "**/*.spec.*"],
|
||||
"excludePrivate": true,
|
||||
"excludeInternal": true,
|
||||
"readme": "none",
|
||||
"name": "Frontend API Reference"
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Python Module (pdoc)
|
||||
|
||||
```bash
|
||||
# 安装 pdoc
|
||||
pip install pdoc
|
||||
|
||||
# hydro_generator_module
|
||||
cd D:/dongdiankaifa9
|
||||
pdoc hydro_generator_module \
|
||||
--output-dir api-docs/hydro_generator \
|
||||
--format markdown \
|
||||
--no-show-source
|
||||
|
||||
# multiphysics_network
|
||||
pdoc multiphysics_network \
|
||||
--output-dir api-docs/multiphysics \
|
||||
--format markdown \
|
||||
--no-show-source
|
||||
```
|
||||
|
||||
**备选: Sphinx (更强大)**:
|
||||
```bash
|
||||
# 安装 Sphinx
|
||||
pip install sphinx sphinx-markdown-builder
|
||||
|
||||
# 生成 API 文档
|
||||
sphinx-apidoc -o docs/source hydro_generator_module
|
||||
cd docs && make markdown
|
||||
```
|
||||
|
||||
## 集成脚本
|
||||
|
||||
```python
|
||||
#!/usr/bin/env python3
|
||||
# scripts/extract_all_apis.py
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
PROJECTS = {
|
||||
'backend': {
|
||||
'path': 'D:/dongdiankaifa9/backend',
|
||||
'type': 'fastapi',
|
||||
'output': 'api-docs/backend'
|
||||
},
|
||||
'frontend': {
|
||||
'path': 'D:/dongdiankaifa9/frontend',
|
||||
'type': 'typescript',
|
||||
'output': 'api-docs/frontend'
|
||||
},
|
||||
'hydro_generator_module': {
|
||||
'path': 'D:/dongdiankaifa9/hydro_generator_module',
|
||||
'type': 'python',
|
||||
'output': 'api-docs/hydro_generator'
|
||||
},
|
||||
'multiphysics_network': {
|
||||
'path': 'D:/dongdiankaifa9/multiphysics_network',
|
||||
'type': 'python',
|
||||
'output': 'api-docs/multiphysics'
|
||||
}
|
||||
}
|
||||
|
||||
def extract_fastapi(config):
|
||||
"""提取 FastAPI OpenAPI 文档"""
|
||||
path = Path(config['path'])
|
||||
sys.path.insert(0, str(path))
|
||||
|
||||
try:
|
||||
from app.main import app
|
||||
import json
|
||||
|
||||
output_dir = Path(config['output'])
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 导出 OpenAPI JSON
|
||||
with open(output_dir / 'openapi.json', 'w', encoding='utf-8') as f:
|
||||
json.dump(app.openapi(), f, indent=2, ensure_ascii=False)
|
||||
|
||||
print(f"✓ FastAPI: {len(app.openapi().get('paths', {}))} endpoints")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"✗ FastAPI error: {e}")
|
||||
return False
|
||||
|
||||
def extract_typescript(config):
|
||||
"""提取 TypeScript 文档"""
|
||||
try:
|
||||
subprocess.run([
|
||||
'npx', 'typedoc',
|
||||
'--plugin', 'typedoc-plugin-markdown',
|
||||
'--out', config['output'],
|
||||
'--entryPoints', './lib', './hooks',
|
||||
'--entryPointStrategy', 'expand'
|
||||
], cwd=config['path'], check=True)
|
||||
print(f"✓ TypeDoc: {config['path']}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"✗ TypeDoc error: {e}")
|
||||
return False
|
||||
|
||||
def extract_python(config):
|
||||
"""提取 Python 模块文档"""
|
||||
try:
|
||||
module_name = Path(config['path']).name
|
||||
subprocess.run([
|
||||
'pdoc', module_name,
|
||||
'--output-dir', config['output'],
|
||||
'--format', 'markdown'
|
||||
], cwd=Path(config['path']).parent, check=True)
|
||||
print(f"✓ pdoc: {module_name}")
|
||||
return True
|
||||
except Exception as e:
|
||||
print(f"✗ pdoc error: {e}")
|
||||
return False
|
||||
|
||||
EXTRACTORS = {
|
||||
'fastapi': extract_fastapi,
|
||||
'typescript': extract_typescript,
|
||||
'python': extract_python
|
||||
}
|
||||
|
||||
if __name__ == '__main__':
|
||||
for name, config in PROJECTS.items():
|
||||
print(f"\n[{name}]")
|
||||
extractor = EXTRACTORS.get(config['type'])
|
||||
if extractor:
|
||||
extractor(config)
|
||||
```
|
||||
|
||||
## Phase 3 集成
|
||||
|
||||
在 `api-reference` Agent 提示词中添加:
|
||||
|
||||
```
|
||||
[PRE-EXTRACTION]
|
||||
运行 API 提取脚本获取结构化文档:
|
||||
- python scripts/extract_all_apis.py
|
||||
|
||||
[INPUT FILES]
|
||||
- api-docs/backend/openapi.json (FastAPI endpoints)
|
||||
- api-docs/frontend/*.md (TypeDoc output)
|
||||
- api-docs/hydro_generator/*.md (pdoc output)
|
||||
- api-docs/multiphysics/*.md (pdoc output)
|
||||
```
|
||||
|
||||
## 输出结构
|
||||
|
||||
```
|
||||
api-docs/
|
||||
├── backend/
|
||||
│ ├── openapi.json # Raw OpenAPI spec
|
||||
│ └── API_REFERENCE.md # Converted Markdown
|
||||
├── frontend/
|
||||
│ ├── modules.md
|
||||
│ ├── functions.md
|
||||
│ └── classes/
|
||||
├── hydro_generator/
|
||||
│ ├── assembler.md
|
||||
│ ├── blueprint.md
|
||||
│ └── builders/
|
||||
└── multiphysics/
|
||||
├── analysis_domain.md
|
||||
├── builders.md
|
||||
└── compilers.md
|
||||
```
|
||||
85
.claude/skills/software-manual/scripts/bundle-libraries.md
Normal file
85
.claude/skills/software-manual/scripts/bundle-libraries.md
Normal file
@@ -0,0 +1,85 @@
|
||||
# 库文件打包说明
|
||||
|
||||
## 依赖库
|
||||
|
||||
HTML 组装阶段需要内嵌以下成熟库(无 CDN 依赖):
|
||||
|
||||
### 1. marked.js - Markdown 解析
|
||||
|
||||
```bash
|
||||
# 获取最新版本
|
||||
curl -o templates/libs/marked.min.js https://unpkg.com/marked/marked.min.js
|
||||
```
|
||||
|
||||
### 2. highlight.js - 代码语法高亮
|
||||
|
||||
```bash
|
||||
# 获取核心 + 常用语言包
|
||||
curl -o templates/libs/highlight.min.js https://unpkg.com/@highlightjs/cdn-assets/highlight.min.js
|
||||
|
||||
# 获取 github-dark 主题
|
||||
curl -o templates/libs/github-dark.min.css https://unpkg.com/@highlightjs/cdn-assets/styles/github-dark.min.css
|
||||
```
|
||||
|
||||
## 内嵌方式
|
||||
|
||||
Phase 5 Agent 应:
|
||||
|
||||
1. 读取 `templates/libs/*.js` 和 `*.css`
|
||||
2. 将内容嵌入 HTML 的 `<script>` 和 `<style>` 标签
|
||||
3. 在 `DOMContentLoaded` 后初始化:
|
||||
|
||||
```javascript
|
||||
// 初始化 marked
|
||||
marked.setOptions({
|
||||
highlight: function(code, lang) {
|
||||
if (lang && hljs.getLanguage(lang)) {
|
||||
return hljs.highlight(code, { language: lang }).value;
|
||||
}
|
||||
return hljs.highlightAuto(code).value;
|
||||
},
|
||||
breaks: true,
|
||||
gfm: true
|
||||
});
|
||||
|
||||
// 应用高亮
|
||||
document.querySelectorAll('pre code').forEach(block => {
|
||||
hljs.highlightElement(block);
|
||||
});
|
||||
```
|
||||
|
||||
## 备选方案
|
||||
|
||||
如果无法获取外部库,使用内置的简化 Markdown 转换:
|
||||
|
||||
```javascript
|
||||
function simpleMarkdown(md) {
|
||||
return md
|
||||
.replace(/^### (.+)$/gm, '<h3>$1</h3>')
|
||||
.replace(/^## (.+)$/gm, '<h2>$1</h2>')
|
||||
.replace(/^# (.+)$/gm, '<h1>$1</h1>')
|
||||
.replace(/```(\w+)?\n([\s\S]*?)```/g, (m, lang, code) =>
|
||||
`<pre data-language="${lang || ''}"><code class="language-${lang || ''}">${escapeHtml(code)}</code></pre>`)
|
||||
.replace(/`([^`]+)`/g, '<code>$1</code>')
|
||||
.replace(/\*\*(.+?)\*\*/g, '<strong>$1</strong>')
|
||||
.replace(/\*(.+?)\*/g, '<em>$1</em>')
|
||||
.replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>')
|
||||
.replace(/^\|(.+)\|$/gm, processTableRow)
|
||||
.replace(/^- (.+)$/gm, '<li>$1</li>')
|
||||
.replace(/^\d+\. (.+)$/gm, '<li>$1</li>');
|
||||
}
|
||||
```
|
||||
|
||||
## 文件结构
|
||||
|
||||
```
|
||||
templates/
|
||||
├── libs/
|
||||
│ ├── marked.min.js # Markdown parser
|
||||
│ ├── highlight.min.js # Syntax highlighting
|
||||
│ └── github-dark.min.css # Code theme
|
||||
├── tiddlywiki-shell.html
|
||||
└── css/
|
||||
├── wiki-base.css
|
||||
└── wiki-dark.css
|
||||
```
|
||||
270
.claude/skills/software-manual/scripts/extract_apis.py
Normal file
270
.claude/skills/software-manual/scripts/extract_apis.py
Normal file
@@ -0,0 +1,270 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
API 文档提取脚本
|
||||
支持 FastAPI、TypeScript、Python 模块
|
||||
"""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import Dict, Any, Optional
|
||||
|
||||
# 项目配置
|
||||
PROJECTS = {
|
||||
'backend': {
|
||||
'path': Path('D:/dongdiankaifa9/backend'),
|
||||
'type': 'fastapi',
|
||||
'entry': 'app.main:app',
|
||||
'output': 'api-docs/backend'
|
||||
},
|
||||
'frontend': {
|
||||
'path': Path('D:/dongdiankaifa9/frontend'),
|
||||
'type': 'typescript',
|
||||
'entries': ['lib', 'hooks', 'components'],
|
||||
'output': 'api-docs/frontend'
|
||||
},
|
||||
'hydro_generator_module': {
|
||||
'path': Path('D:/dongdiankaifa9/hydro_generator_module'),
|
||||
'type': 'python',
|
||||
'output': 'api-docs/hydro_generator'
|
||||
},
|
||||
'multiphysics_network': {
|
||||
'path': Path('D:/dongdiankaifa9/multiphysics_network'),
|
||||
'type': 'python',
|
||||
'output': 'api-docs/multiphysics'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
def extract_fastapi(name: str, config: Dict[str, Any], output_base: Path) -> bool:
|
||||
"""提取 FastAPI OpenAPI 文档"""
|
||||
path = config['path']
|
||||
output_dir = output_base / config['output']
|
||||
output_dir.mkdir(parents=True, exist_ok=True)
|
||||
|
||||
# 添加路径到 sys.path
|
||||
if str(path) not in sys.path:
|
||||
sys.path.insert(0, str(path))
|
||||
|
||||
try:
|
||||
# 动态导入 app
|
||||
from app.main import app
|
||||
|
||||
# 获取 OpenAPI schema
|
||||
openapi_schema = app.openapi()
|
||||
|
||||
# 保存 JSON
|
||||
json_path = output_dir / 'openapi.json'
|
||||
with open(json_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(openapi_schema, f, indent=2, ensure_ascii=False)
|
||||
|
||||
# 生成 Markdown 摘要
|
||||
md_path = output_dir / 'API_SUMMARY.md'
|
||||
generate_api_markdown(openapi_schema, md_path)
|
||||
|
||||
endpoints = len(openapi_schema.get('paths', {}))
|
||||
print(f" ✓ Extracted {endpoints} endpoints → {output_dir}")
|
||||
return True
|
||||
|
||||
except ImportError as e:
|
||||
print(f" ✗ Import error: {e}")
|
||||
return False
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def generate_api_markdown(schema: Dict, output_path: Path):
|
||||
"""从 OpenAPI schema 生成 Markdown"""
|
||||
lines = [
|
||||
f"# {schema.get('info', {}).get('title', 'API Reference')}",
|
||||
"",
|
||||
f"Version: {schema.get('info', {}).get('version', '1.0.0')}",
|
||||
"",
|
||||
"## Endpoints",
|
||||
"",
|
||||
"| Method | Path | Summary |",
|
||||
"|--------|------|---------|"
|
||||
]
|
||||
|
||||
for path, methods in schema.get('paths', {}).items():
|
||||
for method, details in methods.items():
|
||||
if method in ('get', 'post', 'put', 'delete', 'patch'):
|
||||
summary = details.get('summary', details.get('operationId', '-'))
|
||||
lines.append(f"| `{method.upper()}` | `{path}` | {summary} |")
|
||||
|
||||
lines.extend([
|
||||
"",
|
||||
"## Schemas",
|
||||
""
|
||||
])
|
||||
|
||||
for name, schema_def in schema.get('components', {}).get('schemas', {}).items():
|
||||
lines.append(f"### {name}")
|
||||
lines.append("")
|
||||
if 'properties' in schema_def:
|
||||
lines.append("| Property | Type | Required |")
|
||||
lines.append("|----------|------|----------|")
|
||||
required = schema_def.get('required', [])
|
||||
for prop, prop_def in schema_def['properties'].items():
|
||||
prop_type = prop_def.get('type', prop_def.get('$ref', 'any'))
|
||||
is_required = '✓' if prop in required else ''
|
||||
lines.append(f"| `{prop}` | {prop_type} | {is_required} |")
|
||||
lines.append("")
|
||||
|
||||
with open(output_path, 'w', encoding='utf-8') as f:
|
||||
f.write('\n'.join(lines))
|
||||
|
||||
|
||||
def extract_typescript(name: str, config: Dict[str, Any], output_base: Path) -> bool:
|
||||
"""提取 TypeScript 文档 (TypeDoc)"""
|
||||
path = config['path']
|
||||
output_dir = output_base / config['output']
|
||||
|
||||
# 检查 TypeDoc 是否已安装
|
||||
try:
|
||||
result = subprocess.run(
|
||||
['npx', 'typedoc', '--version'],
|
||||
cwd=path,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
if result.returncode != 0:
|
||||
print(f" ⚠ TypeDoc not installed, installing...")
|
||||
subprocess.run(
|
||||
['npm', 'install', '--save-dev', 'typedoc', 'typedoc-plugin-markdown'],
|
||||
cwd=path,
|
||||
check=True
|
||||
)
|
||||
except FileNotFoundError:
|
||||
print(f" ✗ npm/npx not found")
|
||||
return False
|
||||
|
||||
# 运行 TypeDoc
|
||||
try:
|
||||
entries = config.get('entries', ['lib'])
|
||||
cmd = [
|
||||
'npx', 'typedoc',
|
||||
'--plugin', 'typedoc-plugin-markdown',
|
||||
'--out', str(output_dir),
|
||||
'--entryPointStrategy', 'expand',
|
||||
'--exclude', '**/node_modules/**',
|
||||
'--exclude', '**/*.test.*',
|
||||
'--readme', 'none'
|
||||
]
|
||||
for entry in entries:
|
||||
entry_path = path / entry
|
||||
if entry_path.exists():
|
||||
cmd.extend(['--entryPoints', str(entry_path)])
|
||||
|
||||
result = subprocess.run(cmd, cwd=path, capture_output=True, text=True)
|
||||
|
||||
if result.returncode == 0:
|
||||
print(f" ✓ TypeDoc generated → {output_dir}")
|
||||
return True
|
||||
else:
|
||||
print(f" ✗ TypeDoc error: {result.stderr[:200]}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
def extract_python_module(name: str, config: Dict[str, Any], output_base: Path) -> bool:
|
||||
"""提取 Python 模块文档 (pdoc)"""
|
||||
path = config['path']
|
||||
output_dir = output_base / config['output']
|
||||
module_name = path.name
|
||||
|
||||
# 检查 pdoc
|
||||
try:
|
||||
subprocess.run(['pdoc', '--version'], capture_output=True, check=True)
|
||||
except (FileNotFoundError, subprocess.CalledProcessError):
|
||||
print(f" ⚠ pdoc not installed, installing...")
|
||||
subprocess.run([sys.executable, '-m', 'pip', 'install', 'pdoc'], check=True)
|
||||
|
||||
# 运行 pdoc
|
||||
try:
|
||||
result = subprocess.run(
|
||||
[
|
||||
'pdoc', module_name,
|
||||
'--output-dir', str(output_dir),
|
||||
'--format', 'markdown'
|
||||
],
|
||||
cwd=path.parent,
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
# 统计生成的文件
|
||||
md_files = list(output_dir.glob('**/*.md'))
|
||||
print(f" ✓ pdoc generated {len(md_files)} files → {output_dir}")
|
||||
return True
|
||||
else:
|
||||
print(f" ✗ pdoc error: {result.stderr[:200]}")
|
||||
return False
|
||||
|
||||
except Exception as e:
|
||||
print(f" ✗ Error: {e}")
|
||||
return False
|
||||
|
||||
|
||||
EXTRACTORS = {
|
||||
'fastapi': extract_fastapi,
|
||||
'typescript': extract_typescript,
|
||||
'python': extract_python_module
|
||||
}
|
||||
|
||||
|
||||
def main(output_base: Optional[str] = None, projects: Optional[list] = None):
|
||||
"""主入口"""
|
||||
base = Path(output_base) if output_base else Path.cwd()
|
||||
|
||||
print("=" * 50)
|
||||
print("API Documentation Extraction")
|
||||
print("=" * 50)
|
||||
|
||||
results = {}
|
||||
|
||||
for name, config in PROJECTS.items():
|
||||
if projects and name not in projects:
|
||||
continue
|
||||
|
||||
print(f"\n[{name}] ({config['type']})")
|
||||
|
||||
if not config['path'].exists():
|
||||
print(f" ✗ Path not found: {config['path']}")
|
||||
results[name] = False
|
||||
continue
|
||||
|
||||
extractor = EXTRACTORS.get(config['type'])
|
||||
if extractor:
|
||||
results[name] = extractor(name, config, base)
|
||||
else:
|
||||
print(f" ✗ Unknown type: {config['type']}")
|
||||
results[name] = False
|
||||
|
||||
# 汇总
|
||||
print("\n" + "=" * 50)
|
||||
print("Summary")
|
||||
print("=" * 50)
|
||||
success = sum(1 for v in results.values() if v)
|
||||
print(f"Success: {success}/{len(results)}")
|
||||
|
||||
return all(results.values())
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
import argparse
|
||||
|
||||
parser = argparse.ArgumentParser(description='Extract API documentation')
|
||||
parser.add_argument('--output', '-o', default='.', help='Output base directory')
|
||||
parser.add_argument('--projects', '-p', nargs='+', help='Specific projects to extract')
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
success = main(args.output, args.projects)
|
||||
sys.exit(0 if success else 1)
|
||||
@@ -449,14 +449,65 @@ body {
|
||||
margin-bottom: var(--spacing-md);
|
||||
}
|
||||
|
||||
/* Lists - Enhanced Styling */
|
||||
.tiddler-content ul,
|
||||
.tiddler-content ol {
|
||||
margin-bottom: var(--spacing-md);
|
||||
padding-left: var(--spacing-lg);
|
||||
margin: var(--spacing-md) 0;
|
||||
padding-left: var(--spacing-xl);
|
||||
}
|
||||
|
||||
.tiddler-content li {
|
||||
margin-bottom: var(--spacing-xs);
|
||||
.tiddler-content ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.tiddler-content ul > li {
|
||||
position: relative;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
padding-left: 8px;
|
||||
}
|
||||
|
||||
.tiddler-content ul > li::before {
|
||||
content: "•";
|
||||
position: absolute;
|
||||
left: -16px;
|
||||
color: var(--accent-color);
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.tiddler-content ol {
|
||||
list-style: none;
|
||||
counter-reset: item;
|
||||
}
|
||||
|
||||
.tiddler-content ol > li {
|
||||
position: relative;
|
||||
margin-bottom: var(--spacing-sm);
|
||||
padding-left: 8px;
|
||||
counter-increment: item;
|
||||
}
|
||||
|
||||
.tiddler-content ol > li::before {
|
||||
content: counter(item) ".";
|
||||
position: absolute;
|
||||
left: -24px;
|
||||
color: var(--accent-color);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Nested lists */
|
||||
.tiddler-content ul ul,
|
||||
.tiddler-content ol ol,
|
||||
.tiddler-content ul ol,
|
||||
.tiddler-content ol ul {
|
||||
margin: var(--spacing-xs) 0;
|
||||
}
|
||||
|
||||
.tiddler-content ul ul > li::before {
|
||||
content: "◦";
|
||||
}
|
||||
|
||||
.tiddler-content ul ul ul > li::before {
|
||||
content: "▪";
|
||||
}
|
||||
|
||||
.tiddler-content a {
|
||||
@@ -468,70 +519,112 @@ body {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Code */
|
||||
/* Inline Code - Red Highlight */
|
||||
.tiddler-content code {
|
||||
font-family: var(--font-family-mono);
|
||||
font-size: 0.9em;
|
||||
font-size: 0.875em;
|
||||
padding: 2px 6px;
|
||||
background-color: var(--bg-tertiary);
|
||||
background-color: #fff5f5;
|
||||
color: #c92a2a;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #ffc9c9;
|
||||
}
|
||||
|
||||
/* Code Blocks - Dark Background */
|
||||
.tiddler-content pre {
|
||||
position: relative;
|
||||
margin-bottom: var(--spacing-md);
|
||||
padding: var(--spacing-md);
|
||||
background-color: #1e1e1e;
|
||||
margin: var(--spacing-md) 0;
|
||||
padding: 0;
|
||||
background-color: #1e2128;
|
||||
border-radius: 8px;
|
||||
overflow-x: auto;
|
||||
overflow: hidden;
|
||||
border: 1px solid #3d4450;
|
||||
}
|
||||
|
||||
.tiddler-content pre::before {
|
||||
content: attr(data-language);
|
||||
display: block;
|
||||
padding: 8px 16px;
|
||||
background-color: #2d333b;
|
||||
color: #8b949e;
|
||||
font-size: 0.75rem;
|
||||
font-family: var(--font-family);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
border-bottom: 1px solid #3d4450;
|
||||
}
|
||||
|
||||
.tiddler-content pre code {
|
||||
padding: 0;
|
||||
display: block;
|
||||
padding: var(--spacing-md);
|
||||
background: none;
|
||||
color: #d4d4d4;
|
||||
color: #e6edf3;
|
||||
font-size: var(--font-size-sm);
|
||||
line-height: 1.6;
|
||||
overflow-x: auto;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.copy-code-btn {
|
||||
position: absolute;
|
||||
top: var(--spacing-sm);
|
||||
right: var(--spacing-sm);
|
||||
padding: var(--spacing-xs) var(--spacing-sm);
|
||||
font-size: 0.75rem;
|
||||
background-color: var(--bg-tertiary);
|
||||
border: none;
|
||||
top: 6px;
|
||||
right: 12px;
|
||||
padding: 4px 10px;
|
||||
font-size: 0.7rem;
|
||||
background-color: #3d4450;
|
||||
color: #8b949e;
|
||||
border: 1px solid #4d5566;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
opacity: 0;
|
||||
transition: opacity var(--transition-fast);
|
||||
transition: all var(--transition-fast);
|
||||
}
|
||||
|
||||
.copy-code-btn:hover {
|
||||
background-color: #4d5566;
|
||||
color: #e6edf3;
|
||||
}
|
||||
|
||||
.tiddler-content pre:hover .copy-code-btn {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
/* Tables */
|
||||
/* Tables - Blue Header Style */
|
||||
.tiddler-content table {
|
||||
width: 100%;
|
||||
margin-bottom: var(--spacing-md);
|
||||
margin: var(--spacing-md) 0;
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.tiddler-content th,
|
||||
.tiddler-content td {
|
||||
padding: var(--spacing-sm) var(--spacing-md);
|
||||
border: 1px solid var(--border-color);
|
||||
text-align: left;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tiddler-content th {
|
||||
background-color: var(--bg-secondary);
|
||||
padding: 12px 16px;
|
||||
background: linear-gradient(135deg, #1971c2, #228be6);
|
||||
color: white;
|
||||
font-weight: 600;
|
||||
text-align: left;
|
||||
border: none;
|
||||
border-bottom: 2px solid #1864ab;
|
||||
}
|
||||
|
||||
.tiddler-content tr:nth-child(even) {
|
||||
background-color: var(--bg-secondary);
|
||||
.tiddler-content td {
|
||||
padding: 10px 16px;
|
||||
border: 1px solid #e9ecef;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.tiddler-content tbody tr:nth-child(odd) {
|
||||
background-color: #f8f9fa;
|
||||
}
|
||||
|
||||
.tiddler-content tbody tr:nth-child(even) {
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
.tiddler-content tbody tr:hover {
|
||||
background-color: #e7f5ff;
|
||||
}
|
||||
|
||||
/* Screenshots */
|
||||
|
||||
@@ -26,15 +26,10 @@
|
||||
<div id="searchResults" class="search-results" aria-live="polite"></div>
|
||||
</div>
|
||||
|
||||
<!-- Tag Navigation -->
|
||||
<!-- Tag Navigation (Dynamic) -->
|
||||
<nav class="wiki-tags" aria-label="Filter by category">
|
||||
<button class="tag active" data-tag="all">All</button>
|
||||
<button class="tag" data-tag="getting-started">Getting Started</button>
|
||||
<button class="tag" data-tag="ui-guide">UI Guide</button>
|
||||
<button class="tag" data-tag="api">API</button>
|
||||
<button class="tag" data-tag="config">Configuration</button>
|
||||
<button class="tag" data-tag="troubleshooting">Troubleshooting</button>
|
||||
<button class="tag" data-tag="examples">Examples</button>
|
||||
<button class="tag active" data-tag="all">全部</button>
|
||||
{{TAG_BUTTONS_HTML}}
|
||||
</nav>
|
||||
|
||||
<!-- Table of Contents -->
|
||||
|
||||
Reference in New Issue
Block a user