# Kigmap RAG Widget

<p align="center">
  <strong>🎭 Kigurumi 知识问答助手 — 可嵌入任何网站的智能对话 Widget</strong>
</p>

<p align="center">
  基于 Kigmap RAG 知识库，支持智能客服浮窗和 Live2D 形象两种模式。<br/>
  一行代码接入，内置 HMAC 签名认证、Markdown 渲染、反馈学习、打字机效果。
</p>

---

## ✨ 功能特性

| 特性 | 说明 |
|------|------|
| 🗨️ 智能客服模式 | 右下角浮窗按钮 + 聊天面板，类似 Intercom/Crisp |
| 🎭 Live2D 模式 | Live2D 角色 + 对话气泡 + 表情互动 |
| 📝 Markdown 渲染 | 支持加粗、列表、代码块、表格等完整格式 |
| 👍👎 反馈学习 | 用户反馈自动转化为学习记忆，持续优化回答 |
| ⌨️ 打字机效果 | 回答逐字显示，模拟真实对话体验 |
| 📎 引用来源 | 展示回答引用的知识库文档 |
| 🔐 HMAC 签名 | Web Crypto API 实现，安全调用开放平台 API |
| 🎨 主题定制 | 可配置主题色、位置、问候语、快捷问题 |
| 📱 响应式 | 自适应移动端和桌面端 |

## 📦 两种使用方式

### 方式一：SDK 嵌入（推荐）

将 Widget 嵌入任何网站，一行 `<script>` 即可：

```html
<script src="kigmap-rag-widget.js"></script>
<script>
  KigmapRAG.init({
    publicKey: "pk_xxx",
    secretKey: "sk_xxx",
    baseUrl: "https://rag.kigermap.cn",
    mode: "chat",  // "chat" 或 "live2d"
  });
</script>
```

### 方式二：独立页面（带 Live2D）

全屏对话页面，左侧 Live2D 看板娘 + 右侧聊天面板：

```bash
npm install
npm run dev
# 访问 http://localhost:5174
```

## ⚙️ 配置项

| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| `publicKey` | `string` | — | API 凭证 Public Key（**必填**） |
| `secretKey` | `string` | — | API 凭证 Secret Key（**必填**） |
| `baseUrl` | `string` | `""` | RAG API 地址 |
| `mode` | `string` | `"chat"` | `"chat"` 智能客服 \| `"live2d"` Live2D 形象 |
| `primaryColor` | `string` | `"#6366f1"` | 主题色 |
| `position` | `string` | `"right"` | 按钮位置 `"left"` \| `"right"` |
| `greeting` | `string` | — | 初始问候语 |
| `quickQuestions` | `string[]` | — | 快捷问题列表 |
| `modelUrl` | `string` | — | Live2D 模型 JSON 地址（仅 live2d 模式） |

## 🏗️ 构建

```bash
# 构建 SDK（单文件，可嵌入任何网站）
npm run build:sdk
# 输出: dist-sdk/kigmap-rag-widget.js

# 构建独立页面
npm run build
# 输出: dist/
```

### SDK 产物

| 模式 | 体积 | gzip | 说明 |
|------|------|------|------|
| chat | ~200 KB | ~64 KB | 纯聊天浮窗，无 Live2D 依赖 |
| live2d | ~775 KB | ~224 KB | 含 PIXI.js + pixi-live2d-display |

> Live2D 模式需要预先加载 Cubism 2 运行时 `live2d.min.js`。

## 🧪 测试

```bash
npm run build:sdk
npx serve .
# 访问 http://localhost:3000/demo.html
```

## 📁 项目结构

```
kigmap-rag-widget/
├── src/
│   ├── App.tsx                     # 独立页面主应用
│   ├── main.tsx                    # 独立页面入口
│   ├── sdk.tsx                     # SDK 入口（KigmapRAG.init/destroy）
│   ├── styles.css                  # 独立页面样式
│   ├── lib/
│   │   ├── sign.ts                 # HMAC-SHA256 签名（独立页面用）
│   │   └── markdown.ts             # Markdown → HTML 渲染 + XSS 过滤
│   ├── components/
│   │   ├── Live2DModel.tsx          # Live2D 渲染（独立页面）
│   │   ├── ChatPanel.tsx            # 聊天面板（独立页面）
│   │   └── Effects.tsx              # 粒子/光晕特效（独立页面）
│   └── widget/
│       ├── WidgetApp.tsx            # SDK 智能客服模式
│       ├── WidgetChat.tsx           # SDK 聊天面板（含反馈）
│       ├── Live2DApp.tsx            # SDK Live2D 模式
│       └── widget.css               # SDK 自包含样式
├── public/
│   ├── lib/live2d.min.js            # Cubism 2 运行时
│   └── models/HK416-1-normal/       # Live2D 模型文件
├── demo.html                        # SDK 演示页面
├── vite.config.ts                   # 独立页面构建配置
├── vite.sdk.config.ts               # SDK 构建配置
└── package.json
```

## 🔒 安全说明

- **HMAC 签名**：所有 API 请求使用 HMAC-SHA256 签名，Secret Key 不在网络中传输
- **XSS 防护**：Markdown 渲染后过滤 `<script>`、`<iframe>`、`<svg>`、`javascript:` 协议、内联事件处理器
- **HTML 沙箱**：Widget 渲染在宿主页面 DOM 中，CSS 使用 `krw-` 前缀避免样式冲突

> ⚠️ **注意**：SDK 模式下 `secretKey` 会暴露在客户端 JavaScript 中。这是 HMAC 客户端签名的固有限制。建议：
> - 为 Widget 创建独立的 API 凭证，scope 限制为 `query` 
> - 设置合理的速率限制（requests_per_minute）
> - 定期轮换 Secret Key
> - 不要在 Widget 凭证上授予管理权限

## 🛠️ 技术栈

- **React 18** + **TypeScript**
- **Tailwind CSS**（独立页面）/ 自包含 CSS（SDK）
- **marked** — Markdown 解析
- **pixi.js 6** + **pixi-live2d-display** — Live2D 渲染
- **oh-my-live2d** — Live2D 工具库（独立页面）
- **Vite** — 构建工具
- **Web Crypto API** — HMAC-SHA256 签名

## 📄 License

本项目采用 [GNU General Public License v3.0](./LICENSE) 许可证。

```
Copyright (c) 2026 KigerMap Team

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
```

详见 [LICENSE](./LICENSE) 文件。
