cherry-studio/docs/EXPORT_IMAGES_PLAN.md

7.9 KiB
Raw Permalink Blame History

对话图片导出功能设计方案

一、需求背景

随着多模态AI模型的普及用户在对话中使用图片的频率增加。当前的导出功能只处理文本内容图片被完全忽略需要增加图片导出能力。

二、现状分析

2.1 图片存储机制

当前系统中图片有两种存储方式:

  1. 用户上传的图片

    • 存储位置:本地文件系统
    • 访问方式:通过 FileMetadata.path 字段,使用 file:// 协议
    • 数据结构:ImageMessageBlock.file
  2. AI生成的图片

    • 存储位置内存中的Base64字符串
    • 访问方式:ImageMessageBlock.metadata.generateImageResponse.images 数组
    • 数据格式Base64编码的图片数据

2.2 现有导出功能

当前支持的导出格式:

  • Markdown本地文件/指定路径)
  • Word文档.docx
  • Notion需要API配置
  • 语雀需要API配置
  • Obsidian带弹窗配置
  • Joplin需要API配置
  • 思源笔记需要API配置
  • 笔记工作区
  • 纯文本
  • 图片截图

2.3 导出菜单问题

  1. 设置分散:导出相关设置分布在多个地方
  2. 每次导出可能需要不同配置:如是否包含推理内容、是否包含引用等
  3. 缺乏统一的导出界面除Obsidian外其他格式直接执行导出

三、解决方案

3.1 第一阶段:图片导出功能实现

3.1.1 导出模式设计

提供两种图片导出模式供用户选择:

模式1Base64嵌入模式

![图片描述](data:image/png;base64,iVBORw0KGg...)
  • 优点:单文件、便于分享、保证完整性
  • 缺点:文件体积大、部分编辑器不支持、性能较差

模式2文件夹模式

导出结构:
conversation_2024-01-21/
├── conversation.md
└── images/
    ├── user_upload_1.png
    ├── ai_generated_1.png
    └── ...

Markdown中使用相对路径

![图片描述](./images/user_upload_1.png)
  • 优点:文件体积小、兼容性好、性能优秀
  • 缺点:需要管理多个文件、分享需打包

3.1.2 核心功能实现

  1. 新增图片处理工具函数 (utils/export.ts)

    // 处理消息中的所有图片块
    export async function processImageBlocks(
      message: Message,
      mode: 'base64' | 'folder',
      outputDir?: string
    ): Promise<ImageExportResult[]>
    
    // 将file://协议的图片转换为Base64
    export async function convertFileToBase64(filePath: string): Promise<string>
    
    // 保存图片到指定文件夹
    export async function saveImageToFolder(
      image: string | Buffer,
      outputDir: string,
      fileName: string
    ): Promise<string>
    
    // 在Markdown中插入图片引用
    export function insertImageIntoMarkdown(
      markdown: string,
      images: ImageExportResult[]
    ): string
    
  2. 更新现有导出函数

    • messageToMarkdown(): 增加图片处理参数
    • topicToMarkdown(): 批量处理话题中的图片
    • exportTopicAsMarkdown(): 支持图片导出选项
  3. 图片元数据保留

    • AI生成图片保存prompt信息
    • 用户上传图片:保留原始文件名
    • 添加图片索引和时间戳

3.2 第二阶段:统一导出弹窗(后续实施)

3.2.1 弹窗设计

创建统一的导出配置弹窗 UnifiedExportDialog

interface ExportDialogProps {
  // 导出内容
  content: {
    message?: Message
    messages?: Message[]
    topic?: Topic
    rawContent?: string
  }

  // 导出格式
  format: ExportFormat

  // 通用配置
  options: {
    includeReasoning?: boolean      // 包含推理内容
    excludeCitations?: boolean       // 排除引用
    imageExportMode?: 'base64' | 'folder' | 'none'  // 图片导出模式
    imageQuality?: number           // 图片质量0-100
    maxImageSize?: number           // 最大图片尺寸
  }

  // 格式特定配置
  formatOptions?: {
    // Markdown特定
    markdownPath?: string

    // Notion特定
    notionDatabase?: string
    notionPageName?: string

    // Obsidian特定
    obsidianVault?: string
    obsidianFolder?: string
    processingMethod?: string

    // 其他格式配置...
  }
}

3.2.2 交互流程

  1. 用户点击导出按钮
  2. 弹出统一导出弹窗
  3. 用户选择导出格式
  4. 根据格式显示相应配置选项
  5. 用户调整配置
  6. 点击确认执行导出

3.2.3 优势

  1. 配置集中管理:所有导出配置在一个界面完成
  2. 动态配置:每次导出可以调整不同设置
  3. 用户体验统一:所有格式使用相同的交互模式
  4. 易于扩展:新增导出格式只需添加配置项

四、实施计划

Phase 1: 基础图片导出(本次实施)

  • 创建设计文档
  • 实现图片处理工具函数
  • 更新Markdown导出支持图片
  • 添加图片导出模式设置
  • 测试不同场景

Phase 2: 扩展格式支持

  • Word文档图片嵌入
  • Obsidian图片处理
  • Joplin图片上传
  • 思源笔记图片支持

Phase 3: 统一导出弹窗

  • 设计弹窗UI组件
  • 实现配置管理逻辑
  • 迁移现有导出功能
  • 添加配置持久化

Phase 4: 高级功能

  • 图片压缩优化
  • 批量导出进度显示
  • 导出历史记录
  • 导出模板系统

五、技术细节

5.1 图片格式转换

// Base64转换示例
async function imageToBase64(imagePath: string): Promise<string> {
  if (imagePath.startsWith('file://')) {
    const actualPath = imagePath.slice(7)
    const buffer = await fs.readFile(actualPath)
    const mimeType = getMimeType(actualPath)
    return `data:${mimeType};base64,${buffer.toString('base64')}`
  }
  return imagePath // 已经是Base64
}

5.2 文件夹结构生成

async function createExportFolder(topicName: string): Promise<string> {
  const timestamp = dayjs().format('YYYY-MM-DD-HH-mm-ss')
  const folderName = `${sanitizeFileName(topicName)}_${timestamp}`
  const exportPath = path.join(getExportDir(), folderName)

  await fs.mkdir(path.join(exportPath, 'images'), { recursive: true })
  return exportPath
}

5.3 Markdown图片引用更新

function updateMarkdownImages(
  markdown: string,
  imageMap: Map<string, string>
): string {
  let updatedMarkdown = markdown

  for (const [originalPath, newPath] of imageMap) {
    // 替换图片引用
    const regex = new RegExp(`!\\[([^\\]]*)\\]\\(${escapeRegex(originalPath)}\\)`, 'g')
    updatedMarkdown = updatedMarkdown.replace(
      regex,
      `![$1](${newPath})`
    )
  }

  return updatedMarkdown
}

六、注意事项

  1. 性能考虑

    • 大量图片时使用异步处理
    • 提供进度反馈
    • 实现取消操作
  2. 兼容性

    • 检测目标应用对图片格式的支持
    • 提供降级方案
  3. 安全性

    • 验证文件路径合法性
    • 限制图片大小
    • 清理临时文件
  4. 用户体验

    • 清晰的配置说明
    • 合理的默认值
    • 错误提示友好

七、后续优化

  1. Notion图片支持(需要调研)

    • 研究Notion API的图片上传能力
    • 评估 @notionhq/client 库的图片处理功能
    • 可能需要先上传到图床再引用
  2. 智能压缩

    • 根据图片内容自动选择压缩算法
    • 保持图片质量的同时减小体积
  3. 批量导出

    • 支持多个话题同时导出
    • 生成导出报告
  4. 云存储集成

    • 支持直接上传到云存储
    • 生成分享链接

八、参考资料


文档创建日期2025-01-21 最后更新2025-01-21