Commit Graph

21 Commits

Author SHA1 Message Date
77d788d7a5 feat(Live2D): 设备端 Firefox kiosk 全屏渲染 Live2D 角色
Cubism Core 闭源且无 aarch64 Linux native 库,无法纯 Rust 原生渲染。
设备有 X11+kwin+Firefox 140+OpenGL,用 Firefox kiosk 模式全屏渲染
Live2D 模型(复用 Web 端 Cubism SDK),是 aarch64 上唯一可行路径。

1. src/plugins/http/live2d_display.html — 设备端专用全屏 Live2D 页面
   - 纯 Canvas 渲染,无 UI 控件
   - 轮询 /api/live2d/talking 驱动嘴部动效
   - 自动从 /api/config 读取 live2d_model 路径

2. src/plugins/http/mod.rs — HttpState 加 live2d_talking AtomicBool
   - set_live2d_talking/getter 方法

3. src/plugins/http/routes.rs
   - 新增 GET /live2d-display.html 路由
   - 新增 GET /api/live2d/talking 路由(设备端轮询)
   - chat_text_route 对话开始时 set_live2d_talking(true)
   - 回复后按字数估算时长延迟 set_live2d_talking(false)

4. src/plugins/video/mod.rs — Live2D 模式管理 Firefox kiosk 进程
   - ConfigReloaded render_type=live2d:停止视频,启动 firefox --kiosk
   - ConfigReloaded render_type=video:杀掉 Firefox,恢复视频播放
   - stop() 清理 Firefox 子进程
2026-07-07 13:24:57 +08:00
af5b975375 fix(Web): 角色列表显示所有角色 + 区分视频/Live2D 模式
问题:切到 live2d 配置后角色列表只显示一个角色;角色卡片用当前配置的
character 信息渲染,而非各配置自己的。

1. routes.rs config_available_route 扩展返回结构
   - configs 从字符串数组改为对象数组,每项含 filename + character 元信息
     (name/character_type/render_type/live2d_model)
   - 读取每个配置文件提取 character 块

2. chat.js loadCharacters 用各配置自己的 character 元信息渲染卡片
   - 显示角色名/类型徽章/渲染模式(video/live2d)
   - Live2D 角色额外显示模型路径
   - 策略说明区分"视频播放+状态机"和"Canvas渲染+嘴部动效"

3. chat.js switchCharacter 改为 await loadConfig() 确保配置加载后再刷新
   loadAvailableConfigs 兼容新旧返回结构
2026-07-07 12:26:45 +08:00
39fa2a135a feat(Live2D): 集成 Cubism Web SDK 实现二次元角色渲染
1. routes.rs 新增 GET /live2d/* 静态文件路由
   - 从 source_dir/live2d/ 托管模型文件(.moc3/.model3.json/.png/.physics3.json)
   - 路径穿越防护(禁止 .. 段,canonicalize 校验在 base 内)

2. chat.html 引入 Live2D 渲染依赖
   - PixiJS v6.5.10 + Cubism Core + pixi-live2d-display(CDN)
   - 支持 Cubism 2.1 和 4 全版本模型

3. chat.js 实现 Live2D 模型加载与渲染
   - applyCharStrategy live2d 分支:初始化 PIXI.Application + canvas
   - loadLive2DModel:从 /live2d/<model_path> 加载模型,自动缩放居中
   - 说话动效:对话期间 ParamMouthOpenY 正弦波开合(模拟说话)
   - 呼吸动效:ParamBreath 持续起伏
   - destroyLive2D:切角色时清理 PIXI 资源

4. configs/live2d_anime.json 新建二次元角色"小雅"
   - character_type: live2d, render_type: live2d
   - live2d_model: haru/index.model3.json(Cubism 官方免费样例)
   - persona: 温柔二次元少女人设
   - playlist 复用 dog 的视频作为占位(Live2D 模式下 Web 端显示 canvas)
2026-07-07 11:56:29 +08:00
1ab77df85d refactor(Web): 统一入口 — 合并旧控制台与 AI 对话页为单一 chat.html
- / 和 /index.html 和 /chat 都服务统一页面(原 / 服务旧 WEB_UI_HTML)
- 删除 routes.rs 内嵌的 WEB_UI_HTML 常量(424 行),HTML 拆为 chat.html + chat.js
- 整合 8 个 tab:对话 / 角色 / 模型 / 播放控制 / 视频管理 / 文件管理 / 网络设置 / 系统配置
- 保留全部旧功能:WS 实时状态、播放控制、触发器、视频上传/删除、文件管理器、WiFi 扫描/连接/热点、BLE、配置切换/编辑/显示设置、APK 下载弹窗
- 保留全部新功能:文字对话、语音录音(客户端 WAV 转码)、角色切换、模型管理、Live2D 占位
2026-07-07 11:29:53 +08:00
5df99ced7b feat(M2.1): Web 端语音录放闭环
1. 新增 GET /api/chat/audio/file?path=<tmp_dir内路径> 路由
   - 路径白名单:canonicalize 后必须位于 config.ai.tmp_dir 内
   - 返回 audio/wav 二进制流(浏览器可直接 <audio> 播放)
   - 60s 后自动清理临时文件
   - 修复:原 /download/:filename 只能读 downloads_dir,TTS 输出在 tmp_dir 无法访问

2. POST /api/chat/audio 支持 X-Audio-Format 头
   - 浏览器 MediaRecorder 产 webm/ogg/m4a,whisper-cli 靠扩展名判断格式
   - 服务端按头指示决定文件扩展名(webm/ogg/m4a/mp3/wav)

3. chat.html 录音 UI + 客户端 WAV 转码
   - 按住🎙按钮录音(MediaRecorder),松开发送
   - 客户端用 Web Audio API 解码 + 重采样到 16k 单声道 PCM16
   - 手写 WAV header,输出标准 16k mono wav(whisper-cli 直接可处理)
   - 非安全上下文(HTTP)下禁用录音并显示 Chrome flag 启用指引
   - 回复音频改用 /api/chat/audio/file 路径播放
   - 显示 ASR 转写文本(🎤 你说: xxx)
2026-07-06 14:18:17 +08:00
0ac4aa31c8 feat(M2.1): Web 对话控制端 (/chat 页面 - 文字对话+角色切换+模型管理) 2026-07-06 10:24:34 +08:00
1bb044a3d7 fix: 修复 M2.1 骨架编译错误 (import/match/AppConfig 字段) 2026-07-04 15:09:11 +08:00
a0c4ca2307 feat(M2.1): AI 语音对话插件骨架 + 模型管理 + HTTP API + config schema 扩展
这是 M2.1(语音数字生命 V1)的后端骨架实现,不含 Web UI / Flutter / 设备部署。

新增文件:
- src/plugins/ai/mod.rs: AiPlugin 薄层(持有 ChatPipeline Arc,处理消息系统联动)
- src/plugins/ai/backend.rs: AiBackend trait + LocalCliBackend (whisper-cli/llama-cli/piper 子进程)
  + CloudBackend 占位(V1 返回"暂未支持")
- src/plugins/ai/chat.rs: ChatPipeline 对话管线执行器(HTTP 层 spawn_blocking 直接调用)
- src/plugins/ai/model_manager.rs: ModelManager 模型资产管理
  (清单/下载/切换/删除/配额/档位守门,参照 plugin_repo + version_manager 模式)

修改文件:
- src/core/message.rs: 新增 ChatRequest/ChatResponse/AiModelEvent 消息类型
- src/core/config.rs: AppConfig 新增 character 块(角色元信息+人设)+ ai 块(后端配置)
  均为 #[serde(default)] 向后兼容旧配置
- src/plugins/mod.rs: 注册 ai 模块
- src/plugins/http/mod.rs: HttpState 新增 ai_pipeline + ai_models 字段及注册方法;
  HttpPlugin 新增 set_ai_pipeline/set_ai_models 方法
- src/plugins/http/routes.rs: 新增 6 个 AI 相关路由
  - POST /api/chat/text (文字对话,Web 端主路径)
  - POST /api/chat/audio (语音对话,App 主路径)
  - GET /api/models (模型清单+水位+配额)
  - POST /api/models/download (下载模型,后台线程执行)
  - POST /api/models/switch (切换激活模型)
  - POST /api/models/delete (删除模型,保护当前激活)
- src/main.rs: 注册 AiPlugin,连接 pipeline 到 HttpPlugin

技术决策:
- 对话管线用 spawn_blocking 而非消息系统,满足 HTTP 同步响应需求
- ChatPipeline 用 Arc<Mutex> 共享,HTTP 和 AiPlugin 共用同一实例
- 互斥用 try_lock,忙时返回 409 而非阻塞
- Spike 结论固化: LLM t=2 锁大核、ctx=1024 限死、Qwen2.5-0.5B 默认档

待完成 (后续提交):
- Web 控制端 UI (文字对话 + 角色切换 + 模型管理界面)
- Flutter App (角色页/语音页/模型管理页)
- 设备端部署 llama.cpp/whisper.cpp/piper 二进制 + 模型下载
- 画面联动 (talk/idle 状态切换)
- 测试

注: Windows 开发环境缺 dbus,无法本地 cargo check;待目标机验证。
2026-07-04 15:01:00 +08:00
c8dece351c fix(security): sanitize filename in Content-Disposition header to prevent injection 2026-03-31 23:39:20 +08:00
566cfd0cbc fix(security): fix percent_decode to handle multi-byte UTF-8 properly
The previous implementation decoded percent-encoded bytes one at a time
and cast each to char, which produces invalid characters for multi-byte
UTF-8 sequences (e.g. Chinese filenames). Fixed to accumulate raw bytes
first and convert to String at the end with proper UTF-8 validation.
2026-03-31 23:29:00 +08:00
d48a1cf88b fix: 使用 CARGO_PKG_VERSION 替换硬编码版本号
- routes.rs: AppInfoResponse.version 使用 env!(CARGO_PKG_VERSION)
- device/mod.rs: DevicePlugin PluginInfo.version 使用 env!(CARGO_PKG_VERSION)

消除版本不一致问题:之前 Cargo.toml 版本为 0.2.0,但代码中硬编码为 0.1.0。
2026-03-31 23:22:32 +08:00
showen
d30c111c71 feat: M1.1 完成 + M1.2 启动 — 全量更新
M1.1 收尾:
- 24项 P0/P1/P2 bug 修复 (Rust 107 tests + Flutter 15 tests)
- Flutter App v0.3: cupertino_icons 修复, 单元测试, 调试面板, APK 52.6MB
- 示例插件完善: manifest.json + 请求/响应示范 + 7个测试
- API 文档重写 (以 routes.rs 为唯一权威)
- MILESTONES.md 更新至 100%

M1.2 启动:
- P0: 插件管理 API 闭环 (handle_manager_message Custom 分支 + broadcast_plugin_states)
- ServiceManager 集成测试 8/8 (tests/m1_2_service_manager.rs)
- M1.2 测试计划 (docs/M1.2_TEST_PLAN.md, 18个E2E场景)
- 动态插件系统: auto_rollback + version_manager GC + 路径穿越防护

总计: Rust 115/115 测试, Flutter 15/15 测试, 零 warning

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 18:12:42 +08:00
showen
8ed9cb2d9d feat: Flutter APK 编译成功 + Gradle 配置修复 + APK 下载部署 + 待优化清单
- 通过 qemu-user-static 实现 ARM64 主机编译 Android APK (51MB)
- 修复 Gradle: Aliyun 镜像 + PREFER_SETTINGS + JVM 内存 1536M
- 部署 APK 到 configs/downloads/, Web 下载接口已验证 (HTTP 200)
- 新增 Flutter TODO.md: 10项待优化 (P0/P1/P2 分级)
- 新增 pm_soul.md, 更新 routes.rs APK 下载路由

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 06:43:55 +08:00
showen
bff9ec535d feat: Flutter 客户端 App + Web UI APK 下载入口
- 新增 Flutter 跨平台客户端项目 (clients/flutter/)
  - 29 个 Dart 文件: 服务层/状态管理/5个页面/BLE配网
  - BLE 蓝牙配网: 扫描设备、写入WiFi凭据、配网状态监听
  - HTTP API 客户端: 覆盖全部端点 (播放/场景/WiFi/视频/配置/文件/插件)
  - WebSocket 实时通信: 事件流 + 自动重连
  - 暗色主题 Material 3 UI, 中文界面
  - Android 配置: minSdkVersion 21, BLE/网络权限
  - PRD 产品需求文档 + 开发任务看板
- Web UI 添加 APK 下载入口 (routes.rs)
  - 下载弹窗 + 二维码 + /download/{filename} 静态文件路由
- BLE 插件增加自动重连循环 (ble/mod.rs)
- BLE 默认设备名修正为 'Showen' (config.rs)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-14 02:09:52 +08:00
showen
7135f28545 feat: 实现动态插件系统 (6阶段完成)
- 阶段1: 消息类型序列化 (Serialize/Deserialize, &'static str → String)
- 阶段2: FFI 边界类型 + Plugin SDK (plugin_abi, showen-plugin-sdk crate)
- 阶段3: PluginLoader + DynamicPlugin (libloading 动态加载 .so)
- 阶段4: 版本管理 + 错误策略 (VersionManager, PluginState, 自动回退)
- 阶段5: 远程仓库客户端 (HTTP 下载 + tar.gz 安装)
- 阶段6: 示例插件 + HTTP 管理 API + 全目录 README 文档

54/54 测试通过,0 warnings。

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-13 03:38:08 +08:00
showen
6ca5992b33 实现 /api/playlist 快照语义,返回 playlist 和 current_index
- 新增 PlaylistSnapshot 结构体,包含 playlist 和 current_index
- 修改 playlist_route 从 HttpState 获取 player_status.current_index
- 与旧版 hologram_player_rust 行为一致
- cargo check 零 warning
2026-03-12 13:00:26 +08:00
showen
5af7fc18a5 feat: core tests, bug fixes, API docs rewrite, HTTP compat routes
- Fix state_machine reset_state_progress: reset sequence index before
  validation to prevent out-of-bounds error on state transitions
- Fix video transformer test: use ±1 tolerance for OpenCV interpolation
- Add core integration tests (service_manager, dependencies, messages)
- Add HTTP compat routes (/index.html, POST /api/wifi/scan, hotspot aliases)
- Rewrite clients/docs/API.md to match actual implementation
- Fix BLE unused imports warning
- CEO task planning for next round (ConfigReload, playlist snapshot)

cargo check: 0 warnings, cargo test: 22/22 passed

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 12:40:17 +08:00
showen
8ed9c93c8e fix BLE wifi status delivery and websocket compile issues 2026-03-12 08:07:21 +08:00
showen
62c02b541c team: 组建产品和需求团队
新增产品和需求团队:
- 张婉琳 - 产品总监(前字节抖音产品总监)
- 李明哲 - 需求分析师(前腾讯微信需求分析师)
- 王思远 - 架构师(前阿里淘宝资深架构师)

更新管理架构:
- 产品线:产品总监 → 需求分析师 → PRD/需求文档
- 技术线:PM + 架构师 → 开发团队 → QA 团队
- 工作流程:产品规划 → 需求分析 → 架构设计 → 需求评审 → 开发实现 → 质量保证

团队职责:
- 产品总监:制定产品战略和路线图
- 需求分析师:细化需求,编写需求规格说明
- 架构师:设计技术方案,编写技术设计文档
2026-03-12 06:38:37 +08:00
showen
6940f03187 feat: 第二轮核心插件完成 + QA 团队组建
第二轮任务完成:
- Message Clone + ServiceManager Broadcast (张明远)
- VideoProcessor 完整迁移 1349行 (李思琪)
- BlePlugin 双连接修复 590行 (王浩然)
- HttpPlugin + Web UI 914行 (赵雨薇)

总计新增/修改 1303行代码,cargo check 通过

QA 团队组建:
- 新增 QA 负责人林晓峰(前腾讯测试专家)
- 新增测试工程师周雅婷(前字节测试工程师)
- 更新工作流程:开发 → PM 初审 → QA 测试 → CEO 终审
- 开发和 QA 并行工作,提高效率
2026-03-12 06:30:08 +08:00
showen
d443f28f6e docs: 战略规划和管理架构优化
- 新增 STRATEGY.md: 三年战略规划、技术路线、团队策略
- 新增 MILESTONES.md: 详细里程碑和时间表(M1.1-M1.4)
- 新增 CODE_REVIEW.md: 代码审核标准和流程
- 组建管理班子: 新增 PM 刘建国,优化管理架构
- 丰富团队成员背景: 补充所有成员的教育经历、工作经验、技能树
- 解锁多线程思考能力: 团队成员可使用 kilo 命令并行探索
- 更新工作流程: CEO → PM → 开发团队,两级审核制度
- 修正 kilo 调用方式: 不使用 -f 参数,在消息中指示读取文件
2026-03-12 06:14:52 +08:00