commit 6ecb3ac494cb55e137b0ce6536e0d102dd7b9c93 Author: Ysm-04 Date: Wed May 27 15:19:32 2026 +0800 Initial project documentation and character package diff --git a/docs/Qt_DesktopPet_开发文档.md b/docs/Qt_DesktopPet_开发文档.md new file mode 100644 index 0000000..d8f9b46 --- /dev/null +++ b/docs/Qt_DesktopPet_开发文档.md @@ -0,0 +1,1675 @@ +# Qt 桌宠项目开发文档 + +## 1. 项目目标 + +本项目计划从零创建一个基于 **Qt / C++** 的 Windows 桌面宠物程序。 + +核心目标: + +```text +1. 创建一个透明、无边框、可拖动、可置顶的桌宠窗口 +2. 桌宠形象采用 PNG 图片序列实现动画 +3. 每个角色拥有多个状态,每个状态拥有独立帧动画 +4. 支持用户导入自定义角色资源包 +5. 支持接入大模型 API 进行文字对话 +6. AI 请求、回复、失败等过程需要和桌宠动画状态联动 +7. 提供宽泛的 AI 接口配置入口,支持用户自己的代理、中转、自建接口 +8. 程序需要轻量、低资源占用,保证轻薄本也能流畅运行 +9. 项目开源,推荐使用 MIT License 或其他符合项目实际需求的开源许可 +``` + +当前版本目标不是做完整商业产品,而是先实现一个: + +```text +基于 Qt 的轻量级多状态 PNG 帧动画桌宠 + 可自定义 AI 接口对话助手 +``` + +--- + +## 2. 第一版范围 + +### 2.1 第一版需要实现 + +```text +1. Qt Widgets + CMake 项目 +2. 透明无边框桌宠窗口 +3. 鼠标拖动 +4. 置顶开关 +5. 右键菜单 +6. 系统托盘 +7. 默认角色包加载 +8. PNG 图片序列帧动画 +9. 多状态动画切换 +10. 基础状态机 +11. AI 对话请求 +12. OpenAI Compatible 自定义接口配置 +13. AI 回复气泡 +14. 本地配置保存 +15. 基础日志 +16. 基础错误兜底 +17. 低资源占用策略 +``` + +### 2.2 第一版暂不实现 + +```text +1. 语音输入 +2. 语音朗读 +3. 视频形象 +4. GIF 形象 +5. Live2D +6. 骨骼动画 +7. AI 自动生成角色素材 +8. 角色包市场 +9. 插件系统 +10. 云端同步 +11. 自动更新 +12. 长期记忆系统 +13. 完整安装器 +``` + +第一版要控制范围,不要因为扩展功能过多导致项目失控。 + +--- + +## 3. 平台目标 + +第一版明确优先支持: + +```text +Windows 10 / Windows 11 +``` + +要求: + +```text +1. 以 Windows 桌面体验为主要目标 +2. 透明窗口、置顶、系统托盘、拖动行为优先保证 Windows 正常 +3. 代码结构尽量保持清晰,避免不必要的平台绑定 +4. 第一版不承诺完整适配 macOS / Linux +``` + +后续如果需要跨平台,再单独评估不同系统下的透明窗口、托盘、窗口置顶、多屏和权限行为。 + +--- + +## 4. 开源许可 + +项目计划开源。 + +推荐许可: + +```text +MIT License +``` + +注意事项: + +```text +1. 如果采用 MIT License,需要在仓库中提供 LICENSE 文件 +2. 项目源码可以使用 MIT License +3. 预设角色素材必须自制,或确认拥有可发布、可再分发、可商用或符合项目要求的授权 +4. 第三方库、图标、字体、角色素材必须分别确认许可 +5. 用户导入的素材版权由用户自行负责 +6. 不要把许可不清晰的素材放入仓库 +7. 使用 Qt 时,需要遵守所使用 Qt 版本对应的许可要求 +``` + +如果未来要发布闭源商业版本,需要重新评估 Qt 许可、第三方依赖许可和素材授权。 + +--- + +## 5. 技术选型 + +请从零创建一个新的 Qt 项目。 + +建议技术路线: + +```text +语言:C++17 或更高 +框架:Qt 6 +界面:Qt Widgets +构建:CMake +配置文件:JSON +角色素材:PNG 图片序列 +网络请求:QNetworkAccessManager +动画驱动:QTimer +目标平台:Windows 10 / Windows 11 +``` + +选择 Qt Widgets 的原因: + +```text +1. MVP 开发速度快 +2. 透明无边框窗口实现直接 +3. 桌宠主体 UI 不复杂 +4. 便于控制资源占用 +5. C++ 逻辑组织清晰 +``` + +--- + +## 6. 项目目录建议 + +建议创建如下目录结构: + +```text +DesktopPet/ + ├── CMakeLists.txt + ├── LICENSE + ├── README.md + ├── main.cpp + ├── src/ + │ ├── app/ + │ │ ├── AppController.h + │ │ └── AppController.cpp + │ ├── ui/ + │ │ ├── PetWindow.h + │ │ ├── PetWindow.cpp + │ │ ├── PetView.h + │ │ ├── PetView.cpp + │ │ ├── ChatBubble.h + │ │ ├── ChatBubble.cpp + │ │ ├── SettingsDialog.h + │ │ └── SettingsDialog.cpp + │ ├── character/ + │ │ ├── CharacterPackage.h + │ │ ├── CharacterPackage.cpp + │ │ ├── CharacterPackageLoader.h + │ │ ├── CharacterPackageLoader.cpp + │ │ ├── AnimationClip.h + │ │ ├── AnimationClip.cpp + │ │ ├── FrameAnimator.h + │ │ └── FrameAnimator.cpp + │ ├── state/ + │ │ ├── PetStateMachine.h + │ │ └── PetStateMachine.cpp + │ ├── ai/ + │ │ ├── LLMProvider.h + │ │ ├── LLMProvider.cpp + │ │ ├── OpenAICompatibleProvider.h + │ │ ├── OpenAICompatibleProvider.cpp + │ │ ├── ConversationManager.h + │ │ └── ConversationManager.cpp + │ ├── config/ + │ │ ├── ConfigManager.h + │ │ └── ConfigManager.cpp + │ ├── tray/ + │ │ ├── TrayController.h + │ │ └── TrayController.cpp + │ └── util/ + │ ├── Logger.h + │ └── Logger.cpp + ├── resources/ + │ ├── icons/ + │ └── characters/ + │ └── default_pet/ + │ ├── character.json + │ ├── preview.png + │ ├── idle/ + │ ├── talk/ + │ ├── think/ + │ ├── sleep/ + │ └── error/ + └── docs/ + └── character_package_format.md +``` + +--- + +## 7. 核心模块职责 + +### 7.1 AppController + +负责整体应用启动与模块连接。 + +职责: + +```text +1. 初始化应用配置 +2. 初始化桌宠窗口 +3. 初始化系统托盘 +4. 加载默认角色 +5. 初始化 AI Provider +6. 连接状态机、动画、AI 请求之间的信号 +7. 管理应用退出前的配置保存 +``` + +--- + +### 7.2 PetWindow + +桌宠主窗口。 + +职责: + +```text +1. 创建透明无边框窗口 +2. 承载 PetView 和 ChatBubble +3. 支持鼠标拖动 +4. 支持右键菜单 +5. 支持置顶 +6. 支持缩放 +7. 支持锁定位置 +8. 保存和恢复窗口位置 +9. 避免窗口启动后出现在屏幕外 +``` + +注意: + +```text +1. 不要使用普通主窗口边框 +2. 不要在窗口绘制里做耗时逻辑 +3. 不要在 paintEvent 中加载图片 +4. 鼠标拖动时切换到 drag 状态 +``` + +--- + +### 7.3 PetView + +负责显示当前动画帧。 + +职责: + +```text +1. 接收 FrameAnimator 当前帧 +2. 绘制 PNG 图像 +3. 保持透明背景 +4. 根据缩放比例绘制角色 +5. 避免无意义重绘 +``` + +--- + +### 7.4 ChatBubble + +负责显示桌宠气泡。 + +职责: + +```text +1. 显示用户输入或 AI 回复 +2. 支持自动换行 +3. 支持超长文本裁剪 +4. 支持定时隐藏 +5. 位置根据 character.json 的 bubble.offsetX / offsetY 计算 +``` + +建议限制: + +```text +最大宽度:300px ~ 420px +最大显示行数:6 ~ 8 行 +超出部分省略,后续可通过聊天窗口查看完整内容 +``` + +--- + +### 7.5 CharacterPackage + +表示一个角色资源包。 + +一个角色包包含: + +```text +1. 角色 ID +2. 显示名称 +3. 作者 +4. 版本 +5. 预览图 +6. 默认状态 +7. 基础尺寸 +8. 缩放比例 +9. 锚点 +10. 气泡偏移 +11. 多个动画状态 +``` + +--- + +### 7.6 CharacterPackageLoader + +负责加载角色包。 + +职责: + +```text +1. 读取 character.json +2. 校验 JSON 格式 +3. 校验 schemaVersion +4. 校验状态目录 +5. 校验 PNG 帧文件 +6. 构建 CharacterPackage +7. 支持缺失状态降级 +8. 角色加载失败时回退默认角色 +``` + +不能因为资源包不完整导致程序崩溃。 + +--- + +### 7.7 AnimationClip + +表示一个动画状态。 + +例如: + +```text +idle +talk +think +sleep +happy +drag +error +``` + +每个 AnimationClip 包含: + +```text +1. 状态名 +2. 帧路径列表 +3. FPS +4. 是否循环 +5. 播放结束后的 next 状态 +6. 图片缓存 +7. 是否已加载 +``` + +--- + +### 7.8 FrameAnimator + +帧动画播放器。 + +职责: + +```text +1. 按照当前 AnimationClip 的 FPS 播放帧 +2. 使用 QTimer 驱动动画 +3. 切换状态时重置帧索引 +4. loop 为 true 时循环播放 +5. loop 为 false 时播放完切换 next 状态 +6. 发出 frameChanged 信号给 PetView +7. 窗口隐藏后暂停动画 +``` + +禁止: + +```text +1. 每帧从硬盘读取图片 +2. 使用 while 循环驱动动画 +3. 使用 1ms / 5ms 这种高频定时器 +4. 隐藏到托盘后继续高频播放动画 +``` + +--- + +### 7.9 PetStateMachine + +桌宠状态机。 + +职责: + +```text +1. 统一管理状态切换 +2. 管理状态优先级 +3. 响应用户拖动、点击、AI 请求、AI 回复、错误等事件 +4. 避免状态切换逻辑散落在多个类中 +``` + +第一版状态: + +```text +idle 待机 +talk 说话 +think 思考 +sleep 睡觉 +happy 开心 +drag 被拖动 +error 出错 +``` + +建议优先级: + +```text +drag > error > think > talk > happy > sleep > idle +``` + +基本规则: + +```text +程序启动 → idle +用户拖动桌宠 → drag +用户松开鼠标 → idle +等待 AI 返回 → think +AI 正在输出回复 → talk +AI 回复完成 → idle +请求失败 → error +长时间无操作 → sleep +点击桌宠 → happy +``` + +--- + +### 7.10 TrayController + +负责系统托盘。 + +职责: + +```text +1. 创建托盘图标 +2. 创建托盘菜单 +3. 显示 / 隐藏桌宠 +4. 切换置顶 +5. 打开设置 +6. 退出程序 +``` + +--- + +### 7.11 ConfigManager + +负责配置读写。 + +职责: + +```text +1. 读取应用配置 +2. 保存应用配置 +3. 读取 AI 配置 +4. 保存 AI 配置 +5. 处理配置损坏 +6. 根据便携模式决定配置目录 +``` + +配置文件损坏时,不要直接覆盖,先备份为: + +```text +app_config.broken.json +ai_config.broken.json +``` + +然后再生成默认配置。 + +--- + +### 7.12 Logger + +负责日志。 + +记录: + +```text +1. 程序启动 +2. 角色包加载 +3. 状态切换 +4. 图片加载失败 +5. AI 请求开始 +6. AI 请求结束 +7. HTTP 状态码 +8. JSON 解析失败 +9. 配置保存失败 +10. 资源释放 +``` + +禁止: + +```text +1. 输出完整 API Key +2. 日志文件无限增长 +3. 高频动画每帧写日志 +``` + +建议: + +```text +单日志文件最大 2MB +保留最近 3 个日志文件 +``` + +--- + +## 8. 角色资源包格式 + +本项目第一版不使用视频和 GIF。 + +统一使用: + +```text +PNG 图片序列 + character.json +``` + +核心结构: + +```text +角色 Character + └── 状态 State + └── 帧序列 Frames +``` + +也就是说: + +```text +一个角色不是一组图片 +一个角色是一个资源包 +资源包里有多个状态 +每个状态有自己的一组连续 PNG 图片 +``` + +--- + +### 8.1 角色包目录结构 + +示例: + +```text +characters/ + └── default_pet/ + ├── character.json + ├── preview.png + ├── idle/ + │ ├── 000.png + │ ├── 001.png + │ ├── 002.png + │ └── 003.png + ├── talk/ + │ ├── 000.png + │ ├── 001.png + │ └── 002.png + ├── think/ + │ ├── 000.png + │ ├── 001.png + │ └── 002.png + ├── sleep/ + │ ├── 000.png + │ ├── 001.png + │ └── 002.png + └── error/ + ├── 000.png + ├── 001.png + └── 002.png +``` + +--- + +### 8.2 character.json 示例 + +```json +{ + "schemaVersion": 1, + "id": "default_pet", + "displayName": "默认桌宠", + "author": "DesktopPet", + "version": "1.0.0", + + "preview": "preview.png", + "defaultState": "idle", + + "base": { + "width": 256, + "height": 256, + "scale": 1.0, + "anchorX": 0.5, + "anchorY": 1.0 + }, + + "bubble": { + "offsetX": 0, + "offsetY": -180 + }, + + "states": { + "idle": { + "path": "idle", + "fps": 8, + "loop": true + }, + "talk": { + "path": "talk", + "fps": 12, + "loop": true + }, + "think": { + "path": "think", + "fps": 6, + "loop": true + }, + "sleep": { + "path": "sleep", + "fps": 4, + "loop": true + }, + "error": { + "path": "error", + "fps": 6, + "loop": false, + "next": "idle" + } + } +} +``` + +--- + +### 8.3 角色包版本兼容策略 + +角色包使用 `schemaVersion` 控制格式版本。 + +规则: + +```text +1. 当前程序只保证兼容已支持的 schemaVersion +2. 遇到更高版本角色包时,不要强行加载 +3. 遇到更高版本角色包时,提示用户“角色包版本过高,请升级程序” +4. 后续如果格式升级,应提供迁移函数 +5. 不要静默忽略关键字段变化 +``` + +--- + +### 8.4 角色包校验规则 + +加载角色包时必须校验: + +```text +1. character.json 是否存在 +2. schemaVersion 是否支持 +3. id 是否为空 +4. defaultState 是否存在 +5. states 是否为空 +6. 每个 state 的 path 是否存在 +7. 每个 state 是否至少有一帧 PNG +8. fps 是否大于 0 +9. loop 为 false 时,next 状态是否存在 +10. 图片尺寸是否合理 +``` + +允许角色包只提供部分状态。 + +最小角色包可以只有: + +```text +idle +talk +``` + +缺失状态降级规则: + +```text +缺少 talk → 使用 idle +缺少 think → 使用 idle +缺少 sleep → 使用 idle +缺少 error → 使用 idle +缺少 drag → 使用 idle +缺少 happy → 使用 idle +``` + +--- + +### 8.5 默认角色兜底 + +程序必须内置一个最小默认角色。 + +要求: + +```text +1. 即使用户角色包全部损坏,程序也必须能启动 +2. 默认角色至少包含 idle 状态 +3. 默认角色素材必须自制或授权清晰 +4. 用户当前角色加载失败时,自动回退默认角色 +``` + +--- + +## 9. AI 接入设计 + +AI 接入不要写死某一家平台。 + +目标是支持: + +```text +1. OpenAI Compatible API +2. 用户自定义 Base URL +3. 用户自定义 API Key +4. 用户自定义模型名 +5. 用户自建代理 +6. 用户中转接口 +7. 本地网关 +8. 聚合接口 +``` + +第一版重点支持: + +```text +OpenAI Compatible +``` + +但配置入口要足够宽泛。 + +--- + +### 9.1 AI 配置项 + +普通配置: + +```text +服务类型:OpenAI Compatible +Base URL +API Key +Model +测试连接 +保存配置 +``` + +高级配置: + +```text +请求路径 +是否启用流式输出 +Temperature +Max Tokens +超时时间 +自定义 Header +``` + +第一版可以先实现非流式,流式接口预留。 + +--- + +### 9.2 ai_config.json 示例 + +```json +{ + "providerType": "openai-compatible", + "name": "自定义代理", + "baseUrl": "https://api.example.com/v1", + "apiKey": "sk-xxxx", + "model": "deepseek-chat", + "path": "/chat/completions", + "stream": false, + "timeoutMs": 60000, + "temperature": 0.7, + "maxTokens": 1024, + "headers": {} +} +``` + +实际请求地址: + +```text +POST {baseUrl}{path} +``` + +示例: + +```text +https://api.example.com/v1/chat/completions +``` + +--- + +### 9.3 LLMProvider 抽象 + +设计接口: + +```text +LLMProvider + ├── OpenAICompatibleProvider + └── CustomHttpProvider +``` + +第一版只实现: + +```text +OpenAICompatibleProvider +``` + +但保留 CustomHttpProvider 的接口位置。 + +后续 CustomHttpProvider 可支持: + +```text +1. 自定义 URL +2. 自定义 Method +3. 自定义 Headers +4. 自定义 Body Template +5. 自定义 Response Path +6. 自定义 Stream Delta Path +``` + +--- + +### 9.4 AI 请求与桌宠状态联动 + +正常流程: + +```text +用户输入消息 +↓ +切换 think 状态 +↓ +发送 API 请求 +↓ +收到回复 +↓ +切换 talk 状态 +↓ +气泡显示回复 +↓ +回复显示完成 +↓ +切换 idle 状态 +``` + +失败流程: + +```text +用户输入消息 +↓ +切换 think 状态 +↓ +请求失败 / 超时 / JSON 解析失败 +↓ +切换 error 状态 +↓ +气泡显示错误提示 +↓ +回到 idle +``` + +--- + +### 9.5 AI 并发策略 + +第一版明确: + +```text +1. 同一时间只允许一个 AI 请求 +2. AI 回复期间,用户再次发送消息时提示“正在回复中” +3. 不允许请求队列无限堆积 +4. 后续可支持“取消当前请求并重新发送” +5. 请求失败、取消或完成后,必须正确恢复状态机 +``` + +--- + +### 9.6 AI 错误处理 + +必须处理: + +```text +1. Base URL 为空 +2. API Key 为空 +3. Model 为空 +4. HTTP 401 +5. HTTP 404 +6. HTTP 429 +7. HTTP 500 +8. 请求超时 +9. 返回不是 JSON +10. JSON 格式不符合 OpenAI Compatible 格式 +11. 网络断开 +12. 用户取消请求 +``` + +错误提示分两层: + +```text +用户提示:简洁易懂 +日志信息:保留具体调试信息 +``` + +不要在日志中输出完整 API Key。 + +只允许输出类似: + +```text +sk-****abcd +``` + +--- + +## 10. 对话系统 + +### 10.1 ConversationManager + +职责: + +```text +1. 保存当前会话消息 +2. 构建请求 messages +3. 限制上下文长度 +4. 清空会话 +5. 后续支持人格设定 +``` + +第一版上下文限制: + +```text +最多保留最近 10 轮对话 +超过后丢弃更早内容 +``` + +不要无限增长。 + +--- + +### 10.2 人格设定预留 + +第一版可以先使用简单默认人格。 + +后续可扩展: + +```json +{ + "name": "小星", + "personality": "温柔、简洁、有一点吐槽感", + "systemPrompt": "你是用户桌面上的虚拟助手。" +} +``` + +--- + +## 11. 性能与资源占用要求 + +本项目是常驻桌面的后台应用,必须控制资源占用。 + +基本原则: + +```text +1. 空闲时 CPU 占用应尽量接近 0 +2. 不允许无意义高频刷新 +3. 不允许每帧从硬盘读取图片 +4. 不允许一次性加载所有角色资源 +5. 不允许 AI 请求阻塞 UI 线程 +6. 不允许聊天历史无限增长 +7. 不允许日志无限写入 +8. 不允许窗口隐藏后仍然高频播放动画 +``` + +--- + +### 11.1 帧率要求 + +默认帧率建议: + +```text +idle 6~8 FPS +talk 10~12 FPS +think 6~8 FPS +sleep 2~4 FPS +happy 8~12 FPS +error 6~8 FPS +drag 8~12 FPS +``` + +不需要 30 FPS 或 60 FPS。 + +桌宠不是游戏角色,不应高频刷新。 + +--- + +### 11.2 图片资源限制 + +建议限制: + +```text +单帧推荐尺寸:256x256 或 384x384 +单帧最大建议:512x512 +单状态帧数建议:不超过 60 帧 +单角色包建议:不超过 50MB +``` + +超过建议值时不一定禁止,但需要警告用户: + +```text +该角色包较大,可能增加内存占用或导致低配设备卡顿。 +``` + +--- + +### 11.3 图片加载策略 + +不要启动时加载所有角色。 + +建议: + +```text +1. 启动时只加载当前角色 +2. 当前角色优先加载 idle / talk / think / sleep +3. 其他状态首次使用时延迟加载 +4. 切换角色时释放旧角色资源 +5. 对超大图片进行警告 +6. 必要时生成缩放后的缓存图 +``` + +注意: + +```text +1. 不要重复缓存同一张图片 +2. 不要在 paintEvent 中加载图片 +3. 不要每次状态切换都重新扫描目录 +4. 不要每次绘制都解析 character.json +``` + +--- + +### 11.4 内存目标 + +第一版建议目标: + +```text +空闲状态内存占用:尽量控制在 100MB 以内 +普通角色包运行:尽量控制在 150MB 以内 +AI 对话过程中:内存不应持续增长 +切换角色后:旧角色资源应正确释放 +``` + +重点检查: + +```text +1. QPixmap / QImage 是否重复缓存 +2. 旧角色资源是否释放 +3. 对话历史是否无限保存 +4. 日志文件是否无限增大 +5. QNetworkReply 是否释放 +6. QTimer 是否重复创建但未停止 +``` + +--- + +### 11.5 CPU 占用要求 + +禁止: + +```text +1. while 循环驱动动画 +2. 高频 QTimer +3. 不可见时仍然播放动画 +4. 每帧扫描目录 +5. 每帧解析 JSON +6. 每帧加载图片 +``` + +动画计时: + +```text +frameInterval = 1000 / fps +``` + +例如: + +```text +8 FPS → 125ms +12 FPS → 83ms +4 FPS → 250ms +``` + +--- + +### 11.6 窗口隐藏后的行为 + +当桌宠隐藏到托盘后: + +```text +1. 暂停动画 +2. 停止不必要定时器 +3. 不刷新 PetView +4. 不显示气泡 +5. 不执行无意义后台任务 +``` + +重新显示后再恢复动画。 + +--- + +### 11.7 低功耗模式 + +第一版建议预留设置项: + +```text +性能模式: +1. 标准模式 +2. 低功耗模式 +``` + +低功耗模式下: + +```text +1. idle FPS 降低 +2. sleep 更容易触发 +3. 窗口不可见时暂停动画 +4. 气泡隐藏后减少刷新 +5. 禁止后台无意义定时任务 +``` + +--- + +## 12. 用户数据目录与便携模式 + +正式桌面程序不应默认把用户配置写到程序安装目录。 + +建议: + +```text +1. 开发阶段可以使用项目目录下的 config/ 和 resources/ +2. 正式版本应使用 Qt 标准应用数据目录 +3. 配置文件、用户角色包、日志文件应保存到用户可写目录 +``` + +建议目录职责: + +```text +AppConfigLocation:保存 app_config.json、ai_config.json +AppDataLocation:保存用户导入角色包、会话历史等数据 +CacheLocation:保存缩放缓存、临时缓存 +``` + +--- + +### 12.1 便携模式 + +支持 portable 模式。 + +规则: + +```text +1. 如果程序目录下存在 portable.flag,则启用便携模式 +2. 便携模式下,配置、角色包、日志保存在程序目录 +3. 非便携模式下,使用系统用户数据目录 +4. 便携模式下也要处理写权限失败 +``` + +--- + +## 13. 配置文件设计 + +建议配置分开存储: + +```text +app_config.json +ai_config.json +conversation_history.json +``` + +### 13.1 app_config.json + +```json +{ + "window": { + "x": 100, + "y": 100, + "scale": 1.0, + "alwaysOnTop": true, + "locked": false + }, + "character": { + "current": "default_pet" + }, + "performance": { + "mode": "standard", + "pauseWhenHidden": true, + "enableLazyLoad": true + } +} +``` + +--- + +### 13.2 API Key 存储 + +第一版可以先保存在本地配置中,但必须注意: + +```text +1. 不要上传到任何服务器 +2. 不要写入日志 +3. 不要在错误信息中完整展示 +4. UI 中默认隐藏 +5. README 中说明 API Key 仅保存在本地 +``` + +界面提示: + +```text +API Key 仅保存在本地配置文件中,不会上传到作者服务器。 +``` + +后续可考虑系统凭据管理: + +```text +Windows Credential Manager +macOS Keychain +Linux Secret Service +``` + +第一版目标平台是 Windows,因此后续优先考虑 Windows Credential Manager。 + +--- + +## 14. 窗口与桌面体验 + +桌宠窗口需要支持: + +```text +1. 透明背景 +2. 无边框 +3. 鼠标拖动 +4. 右键菜单 +5. 双击打开聊天 +6. 置顶切换 +7. 锁定位置 +8. 缩放 +9. 隐藏到托盘 +10. 退出程序 +``` + +右键菜单建议: + +```text +开始对话 +切换角色 +切换置顶 +锁定位置 +性能模式 +打开设置 +重新加载角色 +隐藏 +退出 +``` + +--- + +### 14.1 透明区域点击策略 + +第一版采用简单策略: + +```text +1. 按窗口矩形区域处理点击 +2. 暂不做基于 PNG alpha 的透明区域点击穿透 +3. 后续可扩展透明像素命中检测 +``` + +不要第一版就实现复杂点击穿透,避免拖慢 MVP。 + +--- + +## 15. 多屏幕与 DPI + +必须考虑: + +```text +1. 高 DPI 缩放 +2. 多显示器 +3. 副屏负坐标 +4. 主屏幕变化 +5. 分辨率变化 +6. 启动时窗口位置不可见 +``` + +如果保存的位置不在任何屏幕范围内,启动时自动移动到主屏幕可见区域。 + +--- + +## 16. 错误处理与兜底 + +程序不能因为以下情况崩溃: + +```text +1. character.json 不存在 +2. character.json 格式错误 +3. 状态目录不存在 +4. 状态帧为空 +5. PNG 加载失败 +6. 帧尺寸不一致 +7. Base URL 为空 +8. API Key 为空 +9. Model 为空 +10. 网络请求失败 +11. 请求超时 +12. 返回 JSON 不符合预期 +13. 角色包过大 +14. 配置文件损坏 +15. 用户数据目录不可写 +``` + +必须有降级策略: + +```text +角色加载失败 → 加载默认角色 +状态缺失 → 回退 idle +AI 请求失败 → 显示错误气泡并切 error +配置损坏 → 备份损坏文件并生成默认配置 +目录不可写 → 提示用户并尝试使用备用目录 +``` + +--- + +## 17. 隐私、版权与风险提示 + +### 17.1 用户素材版权 + +要求: + +```text +1. 预设素材必须自制或授权清晰 +2. 用户导入素材的版权责任由用户自行承担 +3. 后续如果支持角色包分享,必须加入版权提示 +4. 不要在仓库中放入来源不明的动漫、游戏、影视、真人照片素材 +``` + +--- + +### 17.2 AI 接口隐私 + +要求: + +```text +1. 程序只负责把用户消息发送到用户配置的接口 +2. 用户应自行确认第三方代理、中转服务是否可信 +3. 不要默认承诺第三方代理的隐私安全 +4. README 中需要说明自定义代理可能带来的隐私风险 +5. API Key 和聊天内容不得发送到作者服务器 +``` + +--- + +## 18. 打包部署要求 + +Windows 下不能只拷贝 exe。 + +需要检查: + +```text +1. Qt DLL 是否完整 +2. platforms 插件是否完整 +3. imageformats 插件是否完整 +4. PNG 是否可以正常加载 +5. 托盘图标是否正常 +6. 无 Qt 开发环境的电脑上是否可以运行 +7. 配置目录是否可写 +8. 便携模式是否可用 +``` + +建议后续提供: + +```text +1. Windows x64 Release 构建 +2. 打包脚本 +3. README 部署说明 +4. 示例角色包 +``` + +第一版可以先不做安装器,但需要保证构建产物可以在普通 Windows 环境运行。 + +--- + +## 19. 编码规范 + +使用 C++。 + +大括号使用 Allman 风格: + +```cpp +void Example::run() +{ + if (condition) + { + doSomething(); + } +} +``` + +要求: + +```text +1. 类职责清晰 +2. 不要把所有逻辑写进 PetWindow +3. 不要写超长函数 +4. 不要硬编码路径 +5. 不要硬编码单一 AI 平台 +6. 不要在 UI 线程执行耗时任务 +7. 不要在 paintEvent 里做资源加载 +8. 不要输出完整 API Key +9. 不要无意义高频刷新 +``` + +--- + +## 20. 第一阶段开发顺序 + +### 阶段 1:创建 Qt 项目与基础窗口 + +```text +1. 创建新的 Qt Widgets + CMake 项目 +2. 创建 PetWindow +3. 实现透明无边框窗口 +4. 显示默认 PNG 图片 +5. 支持鼠标拖动 +6. 支持右键退出 +7. 支持置顶开关 +``` + +--- + +### 阶段 2:系统托盘 + +```text +1. 创建 TrayController +2. 显示托盘图标 +3. 添加右键菜单 +4. 支持显示 / 隐藏桌宠 +5. 支持退出程序 +``` + +--- + +### 阶段 3:角色包加载 + +```text +1. 创建 CharacterPackage +2. 创建 CharacterPackageLoader +3. 读取 resources/characters/default_pet/character.json +4. 解析 states +5. 加载 idle 状态帧 +6. 校验资源包 +7. 缺失状态回退 idle +8. 角色包损坏时回退默认角色 +``` + +--- + +### 阶段 4:帧动画播放器 + +```text +1. 创建 AnimationClip +2. 创建 FrameAnimator +3. 使用 QTimer 按 FPS 播放帧 +4. 支持 loop +5. 支持 next +6. 支持切换状态 +7. 窗口隐藏时暂停动画 +``` + +--- + +### 阶段 5:状态机 + +```text +1. 创建 PetStateMachine +2. 支持 idle / talk / think / sleep / drag / error +3. 拖动时切 drag +4. 松开后切 idle +5. 错误时切 error +6. 非循环状态播放完回 idle +``` + +--- + +### 阶段 6:AI 接入 + +```text +1. 创建 LLMProvider +2. 创建 OpenAICompatibleProvider +3. 支持 Base URL / API Key / Model / Path +4. 使用 QNetworkAccessManager 异步请求 +5. 支持超时 +6. 支持取消请求接口预留 +7. 普通非流式回复显示到 ChatBubble +8. 请求中切 think +9. 收到回复后切 talk +10. 结束后切 idle +11. 失败后切 error +12. 同时只允许一个请求 +``` + +--- + +### 阶段 7:配置界面 + +```text +1. 创建 SettingsDialog +2. 配置 Base URL +3. 配置 API Key +4. 配置 Model +5. 配置请求路径 +6. 测试连接 +7. 选择角色 +8. 设置置顶 +9. 设置性能模式 +10. 设置便携模式提示 +``` + +--- + +### 阶段 8:性能与稳定性检查 + +```text +1. 静置测试 CPU 和内存 +2. 检查隐藏到托盘后动画是否暂停 +3. 检查角色切换后资源释放 +4. 检查连续对话后内存是否持续增长 +5. 检查损坏配置文件是否能恢复 +6. 检查损坏角色包是否能回退默认角色 +``` + +--- + +## 21. 验收标准 + +第一版完成后,应满足: + +```text +1. 程序可以启动 +2. 桌宠窗口透明无边框 +3. 桌宠可以拖动 +4. 桌宠可以置顶 +5. 右键菜单可用 +6. 系统托盘可用 +7. 可以加载默认角色包 +8. idle 动画可以正常播放 +9. 可以切换 think / talk / error 状态 +10. 可以填写自定义 AI Base URL / API Key / Model +11. 可以发送一条消息并显示回复 +12. AI 请求过程中桌宠状态会变化 +13. 请求失败不会崩溃 +14. 隐藏到托盘后动画暂停 +15. 空闲状态 CPU 占用较低 +16. 内存不会随时间持续上涨 +17. 配置损坏后可以备份并恢复默认配置 +18. 用户角色损坏后可以回退默认角色 +19. API Key 不会完整输出到日志 +20. Windows 10/11 下基本功能可用 +``` + +--- + +## 22. 性能测试建议 + +至少测试: + +```text +1. 启动后静置 10 分钟,观察 CPU 和内存 +2. idle 动画连续播放 10 分钟 +3. 连续发送 20 轮 AI 对话 +4. 连续切换角色 10 次 +5. 隐藏到托盘后观察 CPU 是否下降 +6. 导入较大角色包,确认不会卡死 +7. 在轻薄本或低压 CPU 设备上测试拖动流畅度 +8. 修改配置文件为错误格式,确认程序不会崩溃 +9. 删除当前角色包,确认可以回退默认角色 +10. 使用错误 API Key,确认错误提示正常 +``` + +--- + +## 23. 必须避免的问题 + +```text +1. 不要支持视频 +2. 不要支持 GIF +3. 不要实现语音功能 +4. 不要实现 Live2D +5. 不要写死 OpenAI 官方接口 +6. 不要把所有逻辑写进一个类 +7. 不要 UI 线程阻塞等待网络请求 +8. 不要每一帧读取硬盘图片 +9. 不要一次性加载所有角色包 +10. 不要隐藏到托盘后继续高频刷新 +11. 不要无限保存聊天历史 +12. 不要无限写日志 +13. 不要输出完整 API Key +14. 不要让错误配置导致程序崩溃 +15. 不要忽略轻薄本性能表现 +16. 不要把来源不明的素材放入仓库 +17. 不要默认承诺第三方代理隐私安全 +18. 不要第一版就实现复杂透明点击穿透 +19. 不要为了跨平台过早引入复杂兼容层 +20. 不要把配置默认写入没有权限的程序安装目录 +``` + +--- + +## 24. README 最低要求 + +README 至少说明: + +```text +1. 项目简介 +2. 当前开发状态 +3. Windows 优先支持 +4. 构建方式 +5. 角色包格式简介 +6. AI 接口配置说明 +7. API Key 本地保存说明 +8. 第三方代理隐私风险提示 +9. 素材版权提示 +10. 开源许可 +``` + +--- + +## 25. 当前版本总结 + +当前版本只做桌宠内核。 + +核心是: + +```text +Qt 透明桌宠窗口 +Windows 优先 +PNG 多状态帧动画角色包 +轻量状态机 +OpenAI Compatible 自定义 AI 接口 +AI 对话与桌宠动画联动 +低资源占用 +MIT License 开源 +``` + +一句话目标: + +```text +先创建一个可运行、可对话、可换角色、资源占用低、后续容易扩展的 Qt 桌宠项目。 +``` diff --git a/docs/implementation_plan.md b/docs/implementation_plan.md new file mode 100644 index 0000000..1eed814 --- /dev/null +++ b/docs/implementation_plan.md @@ -0,0 +1,464 @@ +# QtDesktopPet 实施计划 + +本文档用于把 `Qt_DesktopPet_开发文档.md` 中的总体目标拆成可执行阶段。 + +总原则: + +```text +1. 先让程序跑起来,再逐步完善架构和功能 +2. AI 接入放到桌宠内核稳定之后 +3. UI 优化作为项目收尾,不阻塞早期功能验证 +4. 每个阶段结束后做最小验收,避免问题堆到后期 +5. 修改、删除、移动文件前需要先说明风险并获得确认 +``` + +--- + +## 1. 已确认约定 + +```text +项目名:QtDesktopPet +当前目录:Qt_DesktopPet +主要平台:Windows 10 / Windows 11 +界面框架:Qt Widgets +构建系统:CMake +优先编译链:Qt 6.5.3 + MinGW 11.2.0 + Ninja +默认角色包:shiroko +AI 接入:放到后期阶段 +UI 优化:所有核心功能调试正常后再做 +远程仓库:https://git.emoera.com/Make/Qt_DesktopPet.git +``` + +--- + +## 2. 本机工具检查结果 + +当前已检查到: + +```text +Git:2.51.0.windows.1 +CMake:3.29.3 +Qt:6.5.3 +Ninja:1.12.0 +MinGW g++:11.2.0 +MinGW mingw32-make:4.2.1 +``` + +相关路径: + +```text +Qt MinGW Kit:D:/Qt/6.5.3/mingw_64 +Qt MSVC Kit:D:/Qt/6.5.3/msvc2019_64 +MinGW 工具链:D:/Qt/Tools/mingw1120_64/bin +CMake:D:/Qt/Tools/CMake_64/bin/cmake.exe +Ninja:D:/Qt/Tools/Ninja/ninja.exe +``` + +注意: + +```text +1. 当前 PATH 中的 qmake 默认指向 msvc2019_64 +2. g++ 未直接出现在 PATH 中 +3. 使用 MinGW 构建时,应显式指定 Qt MinGW 路径和编译器路径 +``` + +建议 CMake 配置方式: + +```powershell +cmake -S . -B build/mingw-debug -G Ninja ` + -DCMAKE_BUILD_TYPE=Debug ` + -DCMAKE_PREFIX_PATH=D:/Qt/6.5.3/mingw_64 ` + -DCMAKE_C_COMPILER=D:/Qt/Tools/mingw1120_64/bin/gcc.exe ` + -DCMAKE_CXX_COMPILER=D:/Qt/Tools/mingw1120_64/bin/g++.exe +``` + +--- + +## 3. 角色包约定 + +`shiroko` 目录作为当前默认角色包。 + +已检查到的结构: + +```text +shiroko/ + ├── character.json + ├── preview.png + ├── README.md + ├── idle/ + ├── talk/ + ├── think/ + ├── sleep/ + ├── happy/ + ├── drag/ + └── error/ +``` + +每个状态当前包含 4 帧 PNG。 + +需要注意: + +```text +1. character.json 中 base.width/base.height 为 627x627,超过原开发文档建议的 512x512 +2. 原型阶段可以接受,但后续需要观察内存、缩放缓存和低配设备表现 +3. 如果项目公开发布或推送远程仓库,需要确认 shiroko 素材版权和再分发权限 +4. 版权不明确前,不应把它作为正式开源发布素材承诺 +``` + +--- + +## 4. 阶段 0:仓库与工程准备 + +目标: + +```text +建立可回滚、可构建的工程基础,不写复杂业务逻辑。 +``` + +已确定操作: + +```text +1. 初始化 Git 仓库 +2. 配置 origin 为 https://git.emoera.com/Make/Qt_DesktopPet.git +3. 新增实施计划文档 +``` + +后续待做: + +```text +1. 创建 .gitignore +2. 创建 CMakeLists.txt +3. 创建 main.cpp +4. 创建 src/、resources/ 等基础目录 +5. 决定 shiroko 是否移动到 resources/characters/shiroko +6. 创建最小 README.md +7. 确认 LICENSE 是否采用 MIT +``` + +验收标准: + +```text +1. Git 仓库存在 +2. origin 配置正确 +3. CMake 能完成配置 +4. 空项目或最小窗口项目能编译 +``` + +需要确认: + +```text +1. 是否创建 .gitignore +2. 是否创建 MIT LICENSE +3. 是否将 shiroko 移动到 resources/characters/shiroko +4. 是否立即创建最小 Qt 工程 +``` + +--- + +## 5. 阶段 1:最小可运行桌宠窗口 + +目标: + +```text +先让程序启动并显示一个可交互的透明桌宠窗口。 +``` + +只做: + +```text +1. Qt Widgets + CMake 工程 +2. main.cpp +3. PetWindow +4. 透明无边框窗口 +5. 显示单张占位 PNG 或 shiroko preview.png +6. 鼠标拖动 +7. 右键退出 +8. 置顶开关 +``` + +暂不做: + +```text +1. 完整角色包加载 +2. 多状态动画 +3. 状态机 +4. 托盘 +5. 配置系统 +6. 日志系统 +7. AI 接入 +8. UI 美化 +``` + +验收标准: + +```text +1. 程序能编译 +2. 程序能启动 +3. 窗口透明无边框 +4. 图片能显示 +5. 鼠标能拖动窗口 +6. 右键菜单能退出 +7. 置顶开关生效 +``` + +--- + +## 6. 阶段 2:接入 shiroko 角色包与 idle 动画 + +目标: + +```text +把单图显示升级为角色包驱动的 PNG 帧动画。 +``` + +只做: + +```text +1. CharacterPackage +2. CharacterPackageLoader +3. AnimationClip +4. FrameAnimator +5. 读取 shiroko/character.json +6. 加载 idle 状态帧 +7. 按 fps 播放 idle 动画 +8. 启动失败时回退 preview.png 或内置占位图 +``` + +暂不做: + +```text +1. 多角色切换 +2. 角色导入界面 +3. 完整状态机 +4. AI 联动 +``` + +验收标准: + +```text +1. idle 动画正常播放 +2. 不在 paintEvent 中加载图片 +3. 不每帧读取硬盘 +4. fps 使用 character.json 配置 +5. character.json 缺失或损坏时程序不崩溃 +``` + +--- + +## 7. 阶段 3:状态机与多状态动画 + +目标: + +```text +集中管理桌宠状态,避免状态切换逻辑散落在窗口和动画类中。 +``` + +只做: + +```text +1. PetStateMachine +2. idle / drag / think / talk / happy / sleep / error +3. 拖动时切换 drag +4. 松开后回 idle +5. 右键菜单提供模拟状态切换 +6. loop=false 的状态播放完后切 next +7. 缺失状态回退 idle +``` + +暂不做: + +```text +1. 真实 AI 请求 +2. 长期记忆 +3. 复杂 sleep 触发策略 +4. UI 美化 +``` + +验收标准: + +```text +1. 状态切换由 PetStateMachine 决策 +2. FrameAnimator 只负责播放动画 +3. PetWindow 不堆积复杂状态判断 +4. shiroko 的多个状态动画都能被触发 +``` + +--- + +## 8. 阶段 4:托盘、配置、日志 + +目标: + +```text +补齐常驻桌面应用的基础设施。 +``` + +只做: + +```text +1. TrayController +2. 托盘显示 / 隐藏桌宠 +3. 托盘退出 +4. ConfigManager +5. 保存窗口位置、置顶状态、缩放、性能模式 +6. Logger +7. 基础日志轮转 +8. 配置损坏时备份为 .broken.json +``` + +暂不做: + +```text +1. AI 配置界面 +2. 角色包市场 +3. 自动更新 +4. 完整安装器 +``` + +验收标准: + +```text +1. 隐藏到托盘后动画暂停 +2. 重新显示后动画恢复 +3. 重启后窗口位置恢复 +4. 配置损坏不会导致程序崩溃 +5. 日志不会无限增长 +``` + +--- + +## 9. 阶段 5:稳定性与性能检查 + +目标: + +```text +在接入 AI 前,先确认桌宠内核稳定。 +``` + +检查内容: + +```text +1. 启动后静置 CPU 和内存 +2. idle 动画连续播放 +3. 隐藏到托盘后 CPU 是否下降 +4. 重复显示 / 隐藏 +5. 重复切换状态 +6. 删除或破坏 character.json 后是否兜底 +7. 删除部分状态目录后是否回退 idle +8. 切换窗口置顶和缩放是否稳定 +``` + +验收标准: + +```text +1. 空闲 CPU 占用低 +2. 内存不随时间持续增长 +3. 没有明显资源泄漏 +4. 常见资源损坏不会崩溃 +``` + +--- + +## 10. 阶段 6:AI 接入 + +目标: + +```text +在桌宠内核稳定后,实现 OpenAI Compatible 非流式对话闭环。 +``` + +只做: + +```text +1. LLMProvider +2. OpenAICompatibleProvider +3. ConversationManager +4. Base URL / API Key / Model / Path 配置 +5. QNetworkAccessManager 异步请求 +6. 请求超时 +7. 同时只允许一个请求 +8. think -> talk -> idle +9. 失败 -> error -> idle +10. 气泡显示 AI 回复或错误提示 +``` + +暂不做: + +```text +1. 流式输出 +2. 自定义 Body Template +3. 多 Provider 市场 +4. 语音输入 +5. 语音朗读 +6. 长期记忆 +``` + +验收标准: + +```text +1. 能发送一条消息并显示回复 +2. Base URL 为空时有提示 +3. API Key 为空时有提示 +4. Model 为空时有提示 +5. 错误 API Key 不崩溃 +6. 错误 URL 或超时不崩溃 +7. 日志不输出完整 API Key +8. 对话历史不会无限增长 +``` + +--- + +## 11. 阶段 7:UI 优化与收尾 + +目标: + +```text +核心功能可用后,再统一处理视觉体验和易用性。 +``` + +只做: + +```text +1. 优化右键菜单结构 +2. 优化气泡样式 +3. 优化设置界面布局 +4. 优化错误提示文案 +5. 优化 README 截图和说明 +6. 检查 DPI 和多屏表现 +7. 整理发布包 +``` + +暂不做: + +```text +1. 新增大功能 +2. 插件系统 +3. 角色包市场 +4. 在线素材下载 +``` + +验收标准: + +```text +1. Windows 10 / Windows 11 基本体验正常 +2. 无 Qt 开发环境机器上可运行 +3. README 能指导用户构建和配置 +4. 素材版权、AI 隐私和 API Key 本地保存说明齐全 +``` + +--- + +## 12. 当前未决问题 + +后续开始写代码前,需要逐项确认: + +```text +1. 是否创建 .gitignore +2. 是否创建 MIT LICENSE +3. 是否把 shiroko 移动到 resources/characters/shiroko +4. 是否使用 shiroko/preview.png 作为阶段 1 单图 +5. 是否立即创建最小 Qt Widgets 工程 +6. 是否将 build/、.vs/、CMake 生成目录全部加入 .gitignore +7. shiroko 素材是否允许提交到远程仓库 +``` + diff --git a/shiroko/README.md b/shiroko/README.md new file mode 100644 index 0000000..a8aaeec --- /dev/null +++ b/shiroko/README.md @@ -0,0 +1,11 @@ +# 砂狼白子角色包 + +目录结构遵循之前约定的桌宠角色包格式: +- preview.png +- character.json +- idle/ talk/ think/ sleep/ happy/ drag/ error + +说明: +- 每个状态包含 4 帧 PNG +- 资源用于桌宠演示与原型验证 +- character.json 已配置默认帧率和状态切换 diff --git a/shiroko/character.json b/shiroko/character.json new file mode 100644 index 0000000..0a25b6e --- /dev/null +++ b/shiroko/character.json @@ -0,0 +1,59 @@ +{ + "schemaVersion": 1, + "id": "shiroko", + "displayName": "砂狼白子", + "author": "OpenAI", + "version": "1.0.0", + "preview": "preview.png", + "defaultState": "idle", + "base": { + "width": 627, + "height": 627, + "scale": 1.0, + "anchorX": 0.5, + "anchorY": 1.0 + }, + "bubble": { + "offsetX": 0, + "offsetY": -180 + }, + "states": { + "idle": { + "path": "idle", + "fps": 8, + "loop": true + }, + "talk": { + "path": "talk", + "fps": 12, + "loop": true + }, + "think": { + "path": "think", + "fps": 6, + "loop": true + }, + "sleep": { + "path": "sleep", + "fps": 4, + "loop": true + }, + "happy": { + "path": "happy", + "fps": 10, + "loop": false, + "next": "idle" + }, + "drag": { + "path": "drag", + "fps": 10, + "loop": true + }, + "error": { + "path": "error", + "fps": 6, + "loop": false, + "next": "idle" + } + } +} \ No newline at end of file diff --git a/shiroko/drag/000.png b/shiroko/drag/000.png new file mode 100644 index 0000000..428125d Binary files /dev/null and b/shiroko/drag/000.png differ diff --git a/shiroko/drag/001.png b/shiroko/drag/001.png new file mode 100644 index 0000000..eedbda7 Binary files /dev/null and b/shiroko/drag/001.png differ diff --git a/shiroko/drag/002.png b/shiroko/drag/002.png new file mode 100644 index 0000000..6d20ed2 Binary files /dev/null and b/shiroko/drag/002.png differ diff --git a/shiroko/drag/003.png b/shiroko/drag/003.png new file mode 100644 index 0000000..aaef66b Binary files /dev/null and b/shiroko/drag/003.png differ diff --git a/shiroko/error/000.png b/shiroko/error/000.png new file mode 100644 index 0000000..a879963 Binary files /dev/null and b/shiroko/error/000.png differ diff --git a/shiroko/error/001.png b/shiroko/error/001.png new file mode 100644 index 0000000..e8d9f4e Binary files /dev/null and b/shiroko/error/001.png differ diff --git a/shiroko/error/002.png b/shiroko/error/002.png new file mode 100644 index 0000000..ef4c669 Binary files /dev/null and b/shiroko/error/002.png differ diff --git a/shiroko/error/003.png b/shiroko/error/003.png new file mode 100644 index 0000000..7882d51 Binary files /dev/null and b/shiroko/error/003.png differ diff --git a/shiroko/happy/000.png b/shiroko/happy/000.png new file mode 100644 index 0000000..ddbaf77 Binary files /dev/null and b/shiroko/happy/000.png differ diff --git a/shiroko/happy/001.png b/shiroko/happy/001.png new file mode 100644 index 0000000..5abeb2a Binary files /dev/null and b/shiroko/happy/001.png differ diff --git a/shiroko/happy/002.png b/shiroko/happy/002.png new file mode 100644 index 0000000..48ceb6b Binary files /dev/null and b/shiroko/happy/002.png differ diff --git a/shiroko/happy/003.png b/shiroko/happy/003.png new file mode 100644 index 0000000..d911d44 Binary files /dev/null and b/shiroko/happy/003.png differ diff --git a/shiroko/idle/000.png b/shiroko/idle/000.png new file mode 100644 index 0000000..3fbeaef Binary files /dev/null and b/shiroko/idle/000.png differ diff --git a/shiroko/idle/001.png b/shiroko/idle/001.png new file mode 100644 index 0000000..1b3126e Binary files /dev/null and b/shiroko/idle/001.png differ diff --git a/shiroko/idle/002.png b/shiroko/idle/002.png new file mode 100644 index 0000000..c012823 Binary files /dev/null and b/shiroko/idle/002.png differ diff --git a/shiroko/idle/003.png b/shiroko/idle/003.png new file mode 100644 index 0000000..904df80 Binary files /dev/null and b/shiroko/idle/003.png differ diff --git a/shiroko/preview.png b/shiroko/preview.png new file mode 100644 index 0000000..78607bd Binary files /dev/null and b/shiroko/preview.png differ diff --git a/shiroko/sleep/000.png b/shiroko/sleep/000.png new file mode 100644 index 0000000..7b75a74 Binary files /dev/null and b/shiroko/sleep/000.png differ diff --git a/shiroko/sleep/001.png b/shiroko/sleep/001.png new file mode 100644 index 0000000..2bfe45f Binary files /dev/null and b/shiroko/sleep/001.png differ diff --git a/shiroko/sleep/002.png b/shiroko/sleep/002.png new file mode 100644 index 0000000..678b432 Binary files /dev/null and b/shiroko/sleep/002.png differ diff --git a/shiroko/sleep/003.png b/shiroko/sleep/003.png new file mode 100644 index 0000000..f8381ec Binary files /dev/null and b/shiroko/sleep/003.png differ diff --git a/shiroko/talk/000.png b/shiroko/talk/000.png new file mode 100644 index 0000000..77b9693 Binary files /dev/null and b/shiroko/talk/000.png differ diff --git a/shiroko/talk/001.png b/shiroko/talk/001.png new file mode 100644 index 0000000..78df533 Binary files /dev/null and b/shiroko/talk/001.png differ diff --git a/shiroko/talk/002.png b/shiroko/talk/002.png new file mode 100644 index 0000000..5b0b8a0 Binary files /dev/null and b/shiroko/talk/002.png differ diff --git a/shiroko/talk/003.png b/shiroko/talk/003.png new file mode 100644 index 0000000..dc25784 Binary files /dev/null and b/shiroko/talk/003.png differ diff --git a/shiroko/think/000.png b/shiroko/think/000.png new file mode 100644 index 0000000..f770a68 Binary files /dev/null and b/shiroko/think/000.png differ diff --git a/shiroko/think/001.png b/shiroko/think/001.png new file mode 100644 index 0000000..023035d Binary files /dev/null and b/shiroko/think/001.png differ diff --git a/shiroko/think/002.png b/shiroko/think/002.png new file mode 100644 index 0000000..4451a81 Binary files /dev/null and b/shiroko/think/002.png differ diff --git a/shiroko/think/003.png b/shiroko/think/003.png new file mode 100644 index 0000000..339b39c Binary files /dev/null and b/shiroko/think/003.png differ