Files
xsh-assistant-next/.github/copilot-instructions.md

8.9 KiB
Raw Permalink Blame History

XSH Assistant 编码指南

项目概览

xsh-assistant 是一个基于 Nuxt 3 的 AI 驱动内容生成平台,专注于数字内容创作(微课视频、虚拟讲师、绿幕合成等)。核心特性:

  • 前端框架: Nuxt 3 + Vue 3 + TypeScript + Tailwind CSS + Radix Vue UI
  • 状态管理: Pinia带持久化
  • 媒体处理: FFmpeg WASM客户端视频处理、WebAV视频剪辑
  • API 集成: 统一的 useFetchWrapped 包装器,所有请求都通过 API_BASE 代理(https://service1.fenshenzhike.com/
  • 部署模式: SPASSR=false

核心架构模式

1. Composables 设计Pinia + 自定义 Composables

状态管理采用分层设计

Pinia Stores持久化状态
├── useLoginState: 用户认证、token、个人资料
├── useHistory: AIGC 会话、聊天历史
└── useTourState: 新手引导状态

业务 Composables无状态或短生命周期
├── useFetchWrapped: API 请求包装(自动添加 token、user_id
├── useLLM: LLM API 调用Spark 模型集成)
├── useFFmpeg: FFmpeg WASM 单例管理
├── useVideoBackgroundCompositing: 视频合成(数字人+背景)
├── useVideoSubtitleEmbedding: 字幕嵌入
└── useDownload: 文件下载管理

关键模式

  • Pinia stores 必须是单一实例,通过 storeToRefs() 获取响应式引用
  • API 请求必须通过 useFetchWrapped 来自动处理认证头token/user_id
  • FFmpeg 采用单例模式useFFmpeg() 返回全局加载的实例),避免重复初始化

2. API 请求模式

所有 API 请求使用统一的 useFetchWrapped 包装器:

// 基础签名
useFetchWrapped<RequestType, ResponseType>(action: string, payload?: RequestType, options?: FetchOptions)

// 请求格式示例(来自 useHistory
useFetchWrapped<AuthedRequest, BaseResponse<resp.xxx>>(
  'App.User_User.CheckSession',  // action 作为查询参数 ?s=
  { token: loginState.token, user_id: loginState.user.id, ...payload },
  { method: 'POST' }  // 默认 POST
)

约定

  • 每个请求必须包含 tokenuser_id(来自 useLoginState
  • 响应结构统一: BaseResponse<T> 包含 ret: number 状态码和 data: T 数据
  • API_BASE 在 nuxt.config.ts 中定义,所有请求都相对于此 URL

3. 媒体处理架构

FFmpeg 初始化流程

  • 单例加载: 首次调用 useFFmpeg() 时初始化,后续复用缓存的实例
  • WASM 资源加载: 从 CDNcdn.jsdelivr.net)加载 FFmpeg core、wasm、worker
  • 错误恢复: 调用 cleanupFFmpeg() 清理资源并重置单例

视频合成流程(核心用例)

输入: 透明通道视频 (WebM) + 背景图 (PNG/File)
  ↓
1. 获取背景图尺寸
2. 计算等比缩放到 720P
3. 加载文件到 FFmpeg vFS
4. 执行 FFmpeg 滤镜链:
   - 背景: scale → ${outputWidth}x${outputHeight}
   - 视频: scale → ${outputWidth}x${outputHeight} (保留 alpha)
   - overlay: 视频叠加到背景format=auto
5. 使用 VP9 编码(支持 alpha+ Opus 音频编码
6. 返回 Blob → 可直接上传或本地预览

4. UI 组件架构

内置组件库(components/uni/

自定义包装组件,提供统一 API

  • UniButton: 按钮 + loading 状态
  • UniInput/UniTextArea: 表单输入
  • UniSelect: 下拉选择
  • UniMessage: 全局消息通知(通过 provide/inject
  • UniCopyable: 可复制文本

消息通知用法

const toast = useToast() // Radix Vue 的 Toast顶部通知
// 或从 provide 注入
const messageApi = inject('uni-message')
messageApi.success('操作成功')
messageApi.error('操作失败', 5000)

Radix Vue + Nuxt UI 集成

  • 使用 Radix Vue for 基础组件button, dialog, select
  • Nuxt UI 用于高级组件 + 主题管理
  • 颜色方案: primary='indigo', gray='neutral',详见 app.config.ts

5. 路由与页面结构

目录映射

pages/
├── generation.vue (导航枢纽)
└── aigc/
    ├── chat/index.vue (聊天页,支持多 LLM 模型)
    ├── draw/index.vue (绘图生成)
    └── generation/
        ├── course.vue (微课生成)
        ├── green-screen.vue (绿幕视频)
        ├── avatar-models.vue (数字讲师)
        ├── materials.vue (片头片尾)
        ├── ppt-templates.vue (PPT 库)
        └── admin/ (管理功能)

导航约定

  • /generation → 功能导航页面
  • /aigc/chat → 聊天/文本生成
  • /generation/course → 视频生成工作流
  • 所有生成功能都需要登录ModalAuthentication 处理)

开发工作流

启动项目

ni                    # 安装依赖
nr dev                # 启动 http://localhost:3000
nr generate           # 生产构建 (生成静态文件)

常见任务

添加新的 API 端点

  1. 定义 Request 和 Response 类型(参考 typings/llm.ts
  2. 在 composable 中使用 useFetchWrapped 调用
  3. 自动包含 token/user_id来自 useLoginState

添加新的视频处理功能

  1. 使用 useFFmpeg() 获取实例(自动初始化)
  2. 写入文件到 vFS: ffmpeg.writeFile()
  3. 执行命令:ffmpeg.exec([...filterArgs])
  4. 清理临时文件:ffmpeg.deleteFile()
  5. 使用 progress callback 通报处理进度

添加新的 UI 组件

  1. 创建在 components/ 下(自动注册)
  2. 优先使用 Radix Vue + Nuxt UI已集成
  3. 使用 Tailwind CSS utility classes + @apply 指令
  4. 通过 app.config.ts 自定义 UI 主题

项目特定的约定

类型定义位置

  • LLM 相关: typings/llm.tsChatMessage, ChatSession, ModelTag, LLMModal
  • 全局类型: typings/types.d.tsBaseResponse, AuthedRequest, UserSchema
  • 组件接口: 组件目录下的 index.d.ts(例 components/aigc/drawing/index.d.ts

命名规范

  • Composables: use 前缀(useLoginState, useLLM
  • Stores: use + 功能名(useHistory, useTourState
  • 组件: PascalCaseChatItem.vue, ModalAuthentication.vue
  • 工具函数: camelCase放在 composables/ 或各功能目录

响应式数据模式

  • Pinia store 返回值: 必须通过 storeToRefs() 才能保持响应式
  • 模板中的 ref: 直接访问Vue 自动展开)
  • 跨组件数据: 优先使用 Pinia store带持久化

进度反馈与错误处理

  • 长时间操作 (视频处理): 通过 callback 函数报告 progress0-100
  • 错误处理: 返回 Promise reject上层 catch 处理;可选通过 toast/message 提示
  • FFmpeg 错误: 捕获 exitCode 非零,记录详细的 FFmpeg 输出

依赖与性能优化

关键依赖

  • @ffmpeg/ffmpeg@0.12.15: WASM 视频处理(从 CDN 加载)
  • @webav/av-cliper: 客户端视频剪辑库
  • markdown-it + highlight.js: 内容渲染(支持代码高亮)
  • date-fns/dayjs: 时间处理dayjs-nuxt 提供全局实例)
  • idb-keyval: IndexedDB 简化操作(缓存大文件)

Vite 优化设置

// nuxt.config.ts 中排除以下包进行优化,避免 bundling WASM
optimizeDeps.exclude: ['@ffmpeg/ffmpeg', 'idb-keyval', '@webav/av-cliper', 'gsap', 'markdown-it']

构建排除项

Worker 格式设置为 ES Module避免 Vite 默认处理:

vite.worker.format = 'es'

测试与调试

  • 开发服务器日志: 浏览器控制台查看 FFmpeg、API、业务日志
  • FFmpeg 调试: [FFmpeg] 前缀的日志输出包含加载进度、命令执行信息
  • 状态调试: Pinia DevTools启用 devtools: true
  • 样式调试: Tailwind 配置在 tailwind.config.ts,按需自定义

常见陷阱与解决方案

问题 原因 解决方案
API 请求 401 缺少 token 或已过期 检查 useLoginState().token,通过 ModalAuthentication 重新登录
FFmpeg 加载超时 CDN 资源加载慢 检查网络,可切换到本地 /public/assets/ffmpeg
视频输出无声音 滤镜链未映射音频 确保 FFmpeg 命令包含 -map '1:a?' 映射音频轨道
组件未注册 文件位置错误 确保在 components/ 目录下,子目录自动扁平化注册
Pinia 状态未持久化 未配置 persist 选项 在 store 返回语句后添加 persist 配置(参考 useLoginState

资源链接