# Knowledge Wiki Design

## 1. 概述

从已有的知识卡片（card）生成一个**静态 Wiki**，通过浏览器直接访问。
依赖 Prism.js（代码高亮）+ Mermaid.js（图表渲染），从 CDN 加载，纯 HTML + JavaScript 驱动。

## 2. 架构

```
┌─────────────────────────────────────────────────────────────────────┐
│  knowledge-card/                                                    │
│                                                                     │
│  ├── summary/*.summary.md    ← 摘要（元数据：layer, domain, 依赖等）  │
│  ├── card/*.md               ← 完整知识卡片（10 章节，300-700 行）    │
│  │                                                                   │
│  ├── .wiki/                   ← 生成的 Wiki（输出目录）               │
│  │   ├── index.html          ← Wiki 入口（含侧边栏 + markdown 渲染）  │
│  │   ├── README.md           ← 项目总览页（generate-wiki.py 生成）    │
│  │   ├── _sidebar.md         ← 侧边栏目录（docsify 兼容，前台未使用）  │
│  │   └── project_config.py   ← 项目配置（wiki-analyzer 生成）         │
│  │                                                                   │
└─────────────────────────────────────────────────────────────────────┘

### 生成脚本位置

`local_knowledge/dev-toolkit/skills/knowledge-lifecycle/scripts/generate-wiki.py`
```

### 核心文件

| 文件 | 生产者 | 作用 |
|------|--------|------|
| `card/*.md` | Cici （知识蒸馏） | 知识卡片原文，10 章节完整内容 |
| `summary/*.summary.md` | card-splitter | 模块元数据（类型、域、依赖关系） |
| `.wiki/index.html` | 手写 | Wiki 入口，含侧边栏渲染 + markdown 渲染 |
| `.wiki/README.md` | generate-wiki.py | 项目总览（技术栈、规模、依赖辐射） |
| `.wiki/_sidebar.md` | generate-wiki.py | 侧边栏目录树，docsify 兼容（前台未使用） |
| `.wiki/project_config.py` | wiki-analyzer | 项目专属配置（CN_NAMES / GROUP_MAP / TECH_STACK / SUMMARY_IDS / DEPENDENCIES） |

## 3. index.html 设计

**单页应用，marked.js + Prism.js + Mermaid.js 从 CDN 加载。**

### 功能

- 左侧侧边栏：模块列表按分组展示
- 搜索框：按模块名/中文名实时过滤
- 点击总览：展示架构全景图（内容内嵌 HTML，零请求）
- 点击模块名：加载对应的 card/{id}.md 并渲染
- Mermaid 图表渲染（flowchart / sequenceDiagram / classDiagram 等）
- Prism 代码高亮（okaidia 深色主题，自动加载各语言组件）

### 关键技术

```javascript
// 首页内容直接内嵌到 HTML 中，避免请求等待
const HOME_MD = `...`;  // 由 generate-wiki.py 注入

// 统一渲染函数 — marked + mermaid 转换 + prism
function renderMd(md, el) {
  const html = marked.parse(md);
  // 将 marked 渲染的 ```mermaid 代码块转为 <div class="mermaid">
  tmp.querySelectorAll('pre code.language-mermaid').forEach(el2 => {
    const div = document.createElement('div');
    div.className = 'mermaid';
    div.textContent = el2.textContent;
    el2.parentElement.replaceWith(div);
  });
  el.innerHTML = ...;
  Prism.highlightAll();
  mermaid.run({ nodes: [el] });
}

// 加载卡片内容（fetch + render）
async function loadContent(path) {
  if (!path) { renderMd(HOME_MD, el); return; }
  const resp = await fetch(`card/${path}.md`);
  renderMd(stripFrontmatter(await resp.text()), el);
}

// 搜索过滤
function filterModules(q) {
  // 按 .mod-link 的 data-name/data-cn 匹配，不匹配的 display:none
}
```

### 样式

- 深色主题（`#1a1a2e` 背景）
- 左侧 280px 侧边栏（`#16213e`），右侧自适应内容区
- 侧边栏分组标题：红色（`#e94560`），模块链接：灰蓝色（`#b0b0c8`），悬停/选中：绿色（`#4ecca3`）
- 搜索框：深色背景 `#0f3460`，边框 `#2d2d5e`
- 表格：深色表头（`#16213e`）+ 绿色表头文字 + 交替行背景
- 行内代码：背景 `#2a2a3e`
- 代码块：`pre` 背景 `#0e0e1e`，边框 `1px solid #2a2a3e`，Prism okaidia 主题
- Mermaid 图表：背景 `#0e0e1e`，`theme: 'base'` + 手写 `themeVariables` 统一深色主题
- 引用块：左侧红色（`#e94560`）边框
- 自定义滚动条样式，匹配深色主题

### Mermaid 配色表

| 变量 | 值 | 用途 |
|------|-----|------|
| `background` / `mainBkg` | `#1a1a2e` / `#2d2d5e` | 图表背景 / 节点底色 |
| `primaryColor` | `#2d2d5e` | 主要色 |
| `primaryBorderColor` / `nodeBorder` | `#4ecca3` | 节点边框（绿色） |
| `primaryTextColor` / `nodeTextColor` | `#e0e0e0` | 节点文字 |
| `lineColor` / `arrowheadColor` | `#4ecca3` | 连线颜色（绿色） |
| `secondaryColor` | `#242444` | 次要色 |
| `secondaryBorderColor` | `#e94560` | 次要边框（红色） |
| `tertiaryColor` / `clusterBkg` | `#16213e` | 第三级色 / 分组框背景 |
| `tertiaryBorderColor` / `clusterBorder` | `#0f3460` | 第三级边框 / 分组框边框 |
| `titleColor` | `#e94560` | 标题文字（红色） |
| `noteBkgColor` | `#ffd460` | 便签背景（黄色） |
| `noteBorderColor` | `#ffd460` | 便签边框 |
| `noteTextColor` | `#1a1a2e` | 便签文字 |
| `activationBkgColor` | `#242444` | 时序图激活条背景 |
| `sectionBkgColor` | `#16213e` | 甘特图区域背景 |
| `critBorderColor` / `critBkgColor` | `#e94560` | 甘特图关键路径 |
| `todayLineColor` | `#e94560` | 甘特图今日线 |

- 链接：绿色高亮（`#4ecca3`）
- 引用块：左侧红色（`#e94560`）边框

## 4. generate-wiki.py 设计

### 输入

- `architecture/architecture-overview.md`：架构全景图（首页内容来源）
- `card/*.md`：卡片原文（用于生成卡片链接）
- `.wiki/project_config.py`：项目配置（CN_NAMES / GROUP_MAP / TECH_STACK / SUMMARY_IDS / DEPENDENCIES）

### 输出（全部写入 .wiki/）

- `README.md`：项目总览（嵌入 architecture-overview.md 内容）
- `_sidebar.md`：docsify 格式的侧边栏目录，供 docsify 兼容使用（前台 index.html 使用内嵌 HTML）
- `index.html`：单页 Wiki 应用，侧边栏列表由 Python 直接生成 HTML 片段嵌入，首页内容（架构全景图）内嵌为 JS 变量 HOME_MD，零请求加载

### 分组映射

以下配置由 wiki-analyzer 写入 `project_config.py`，generate-wiki.py 读取后使用：

分组直接用 summary 的 `node_type` 字段映射，无需启发式推断：

| node_type | 分组 |
|-----------|------|
| 项目配置 | 1 项目元信息 |
| 外部依赖库 | 2 外部依赖库 |
| 基础设施 | 3 基础设施 |
| 公共服务 | 4 公共服务 |
| 业务模块 | 5 业务模块 |
| 工程支撑 | 6 工程支撑 |
| 资源文件 | 7 资源文件 |
| 其他 | 其他 |

```python
GROUP_ORDER = [
    "1 项目元信息", "2 外部依赖库", "3 基础设施",
    "4 公共服务", "5 业务模块", "6 工程支撑",
    "7 资源文件", "其他",
]

GROUP_MAP = {
    "{summary.name}": "分组名",
    ...
}
```

### Card 文件匹配

generate-wiki.py 从 `project_config.py` 读取 `GROUP_MAP` 和 `SUMMARY_IDS`：

1. `SUMMARY_IDS` 提供 `{summary.name} → {summary.id}` 映射关系
2. 遍历 `GROUP_MAP` 的 key（即 `summary.name`），通过 `SUMMARY_IDS[name]` 获取 `id`
3. 拼接 `card/{id}.md` 作为卡片链接
4. 不存在的 card 文件在 fetch 时跳过展示

### MODULES 排序

`modules_data` 注入 index.html 前按 `GROUP_ORDER` 排序，确保侧边栏连续显示（同组模块不交错）。

### 运行

```bash
python3 local_knowledge/dev-toolkit/skills/knowledge-lifecycle/scripts/generate-wiki.py
```

## 5. 服务启动

```bash
cd knowledge-card && python3 server.py
```

浏览器打开 `http://localhost:8081/`，自动重定向到 `/.wiki/`。

### 自定义 Python 服务器（knowledge-card/server.py）

```python
import http.server, os, subprocess

PORT = 8081
DIR = os.path.dirname(os.path.abspath(__file__))

def kill_port(port):
    try:
        pid = subprocess.check_output(["lsof", "-ti", f":{port}"], stderr=subprocess.DEVNULL).decode().strip()
        if pid:
            subprocess.run(["kill", "-9", *pid.split()], check=False)
            print(f"Killed existing process on port {port}")
    except subprocess.CalledProcessError:
        pass

class Handler(http.server.SimpleHTTPRequestHandler):
    def __init__(self, *args, **kwargs):
        super().__init__(*args, directory=DIR, **kwargs)

    def guess_type(self, path):
        if path.endswith(".md"): return "text/html; charset=utf-8"
        if path.endswith(".css"): return "text/css; charset=utf-8"
        if path.endswith(".js"): return "application/javascript"
        if path.endswith(".html"): return "text/html; charset=utf-8"
        return super().guess_type(path)

    def end_headers(self):
        self.send_header("Cache-Control", "no-cache, no-store, must-revalidate")
        self.send_header("Access-Control-Allow-Origin", "*")
        super().end_headers()

if __name__ == "__main__":
    kill_port(PORT)
    http.server.HTTPServer(("0.0.0.0", PORT), Handler).serve_forever()
```

## 6. wiki-analyzer Agent

wiki-analyzer 是 `knowledge-lifecycle` Skill 流程四（初始化 Wiki）的第一步。它扫描项目已有的 summary + card，推断项目结构，生成 `project_config.py` 到 `.wiki/`。

### 分析逻辑

wiki-analyzer 分三步进行：

**第一步：收集数据**
- 遍历 `summary/` 读取 `id`、`name`、`node_type`、`domain`、`code_locations`
- 遍历 `card/` 读取 `title`

**第二步：中文名提取**

按以下优先级推断每个模块的中文名：

```
1. 从 card 的 title 字段提取：
   "Find Watch — 查找手表"      → 取 "查找手表"（取中文部分）
   "Attention (专注力)"         → 取 "专注力"（取括号内中文）
   "定时开关机（AutoOnOff）"     → 取 "定时开关机"（取括号前中文）
   "Sport"                     → 无中文，进入第 2 步
2. 兜底：直接使用 summary 的 name 字段
```

**第三步：分组推断**

直接用 summary 的 `node_type` 字段映射分组：

| node_type | 分组 |
|-----------|------|
| 项目配置 | 1 项目元信息 |
| 外部依赖库 | 2 外部依赖库 |
| 基础设施 | 3 基础设施 |
| 公共服务 | 4 公共服务 |
| 业务模块 | 5 业务模块 |
| 工程支撑 | 6 工程支撑 |
| 资源文件 | 7 资源文件 |
| 其他 / 空值 | 其他 |

### project_config.py 输出格式（生成到 .wiki/）

```python
# Auto-generated by wiki-analyzer
PROJECT_NAME = "XTC Flutter"
PROJECT_DESC = "智能穿戴设备伴侣 App 的 Flutter 层..."

CN_NAMES = {
    "sport": "运动",
    "health": "健康首页",
    ...
}

GROUP_ORDER = [
    "1 项目元信息", "2 外部依赖库", "3 基础设施",
    "4 公共服务", "5 业务模块", "6 工程支撑",
    "7 资源文件", "其他",
]

GROUP_MAP = {
    "XTCWatch 项目配置总览": "1 项目元信息",
    "全网开关": "3 基础设施",
    "网络服务": "4 公共服务",
    "外部依赖库汇总": "2 外部依赖库",
    "微聊核心": "5 业务模块",
    ...
}

TECH_STACK = {
    "平台": "Android",
    "架构": "RCP 组件化（Router + Component + ILifeCycle）",
    "网络": "Retrofit + OkHttp",
    "数据库": "ORMLite + SQLCipher",
    "图片": "Fresco + Glide",
    ...
}
```

## 7. 迁移到其他项目

### 需要修改的内容

| 组件 | 修改点 |
|------|--------|
| `.wiki/index.html` | `<title>` 和 `<h2>` 改为新项目名 |
| `.wiki/project_config.py` | **由 wiki-analyzer 自动生成** |
| `server.py` | `PORT` 和 `DIR` 按需调整 |

### 不需要修改的内容

| 组件 | 原因 |
|------|------|
| `generate-wiki.py` | 从 `project_config.py` 读取配置，不硬编码 |
| `.wiki/index.html` 的 JavaScript 渲染逻辑 | 通用的 markdown 渲染 + fetch 加载 |

### 换项目流程

```bash
# 1. 已跑完 card-splitter + card-generator，确保有 summary/ 和 card/
# 2. 用户说 "初始化 Wiki"
#    → wiki-analyzer 生成 .wiki/project_config.py
#    → generate-wiki.py 读取配置 → 生成全部文件到 .wiki/
#    → 启动 HTTP 服务
```

## 8. 文件依赖关系

```
generate-wiki.py
  ├── 读取 .wiki/project_config.py → 获取 CN_NAMES、GROUP_MAP、SUMMARY_IDS 等
  ├── 读取 architecture/architecture-overview.md → 嵌入首页内容
  ├── 写入 .wiki/README.md         → 嵌入架构全景图内容
  ├── 写入 .wiki/_sidebar.md       → 从 GROUP_ORDER + GROUP_MAP 生成（docsify 兼容）
  └── 生成 .wiki/index.html        → 内嵌侧边栏 HTML + 首页 HOME_MD 变量

.wiki/index.html（generate-wiki.py 生成）
  ├── 首页内容由 HOME_MD 内嵌（零请求加载）
  ├── 运行时 fetch card/{id}.md    → 加载并渲染卡片原文
  ├── Prism.highlightAll()          → 代码高亮
  └── mermaid.run()                 → 图表渲染
```

## 9. 边界情况处理

| 情况 | 处理方式 |
|------|---------|
| summary 没有 `node_type` | 默认 `business` |
| 模块不在 `GROUP_MAP` 中 | 归入 `DEFAULT_GROUP` |
| 模块没有 `CN_NAMES` 映射 | 显示英文原始名 |
| 模块无依赖 | 显示 `—` |
| card/*.md 不存在 | generate-wiki.py 跳过该模块，不加入侧边栏 |
| fetch 失败 | 显示 "❌ 加载失败" 错误信息 |
| 搜索无结果 | 侧边栏清空，不显示任何条目 |
