一键收藏
AI-SideChat 的核心功能之一是一键收藏 AI 对话内容。本文档详细介绍收藏功能的工作原理和使用方法。
功能概述
在支持的 AI 平台上浏览对话时,每个用户提问旁边会自动显示一个收藏按钮:
- 🤍 空心 - 未收藏状态,点击可收藏
- ❤️ 红心 - 已收藏状态,点击可取消收藏
收藏按钮位置
Gemini 平台
收藏按钮会显示在 user-query 元素的右上角。
ChatGPT 平台
收藏按钮会显示在 [data-message-author-role='user'] 元素的右上角。
收藏的内容
点击收藏按钮后,系统会自动提取并保存以下信息:
基本信息
| 字段 | 说明 | 示例 |
|---|---|---|
id | 唯一标识符(时间戳) | 1706014823456 |
timestamp | 收藏时间 | 1706014823456 |
site | 平台名称 | Gemini / ChatGPT |
siteColor | 平台主题色 | #8850e5 / #10a37f |
对话信息
| 字段 | 说明 |
|---|---|
convTitle | 对话标题 |
url | 对话完整 URL |
问题信息
| 字段 | 说明 |
|---|---|
question | 用户提问的纯文本 |
questionHash | 问题内容的哈希值(用于去重和定位) |
bubbleIndex | 问题在对话中的索引位置 |
回答信息
| 字段 | 说明 |
|---|---|
answer | AI 回答的纯文本(用于搜索) |
answerHtml | AI 回答的 HTML(用于预览显示) |
contentHash | 回答内容的哈希值 |
标签信息
| 字段 | 说明 |
|---|---|
tags | 标签数组 |
智能去重机制
哈希校验
AI-Clip 使用自定义哈希算法为每个问题生成唯一标识:
javascript
function simpleHash(text) {
let hash = 0
for (let i = 0; i < text.length; i++) {
const char = text.charCodeAt(i)
hash = ((hash << 5) - hash) + char
hash = hash & hash // 转换为 32 位整数
}
return hash.toString()
}重复检测
在收藏前,系统会:
- 计算当前问题的哈希值
- 检查是否已存在相同哈希值的收藏
- 如果存在,按钮显示为红心 ❤️
- 如果不存在,按钮显示为空心 🤍
好处
- 避免重复收藏:相同的问题只会被收藏一次
- 快速判断:通过哈希值快速查找,无需遍历全部内容
- 精准定位:哈希值用于跨页跳转时的内容匹配
索引定位
什么是 bubbleIndex?
bubbleIndex 记录了问题在对话中的位置索引(从 0 开始):
- 第 1 个用户提问:
bubbleIndex = 0 - 第 2 个用户提问:
bubbleIndex = 1 - 第 3 个用户提问:
bubbleIndex = 2
作用
- 快速定位:跳转时优先使用索引快速定位
- 容错机制:当内容变化时,索引可作为备用定位方式
- 性能优化:避免全局搜索,直接访问目标位置
数据存储
存储位置
所有收藏数据存储在 chrome.storage.local 中,键名为 aiClipData。
数据结构
javascript
{
"aiClipData": [
{
"id": 1706014823456,
"questionHash": "123456789",
"bubbleIndex": 2,
"contentHash": "987654321",
"site": "Gemini",
"siteColor": "#8850e5",
"convTitle": "Python 教程",
"question": "如何使用 Python 读取文件?",
"answer": "在 Python 中读取文件...",
"answerHtml": "<p>在 Python 中读取文件...</p>",
"url": "https://gemini.google.com/app/xxx",
"tags": ["Python", "文件操作"],
"timestamp": 1706014823456
},
// 更多收藏...
]
}存储限制
- Chrome 本地存储上限通常为 5MB
- 平均每条收藏约 2-5KB(取决于回答长度)
- 估计可存储 1000-2500 条收藏
收藏状态同步
实时更新
AI-Clip 使用 chrome.storage.onChanged 监听器实现实时同步:
- 在任何页面收藏/取消收藏
- 所有打开的标签页自动更新按钮状态
- 悬浮坞列表实时刷新
跨标签页同步
- 在标签页 A 收藏了某个问题
- 标签页 B 中相同问题的按钮会立即变成红心 ❤️
- 无需刷新页面
收藏操作
收藏步骤
- 点击空心按钮 🤍
- 系统提取内容
- 提取问题文本
- 提取回答内容(纯文本和 HTML)
- 获取对话标题和 URL
- 计算哈希值和索引
- 保存到存储
- 添加到
aiClipData数组 - 存储到
chrome.storage.local
- 添加到
- 更新 UI
- 按钮变为红心 ❤️
- 所有标签页同步更新
取消收藏步骤
- 点击红心按钮 ❤️
- 系统删除数据
- 根据问题哈希值查找收藏
- 从
aiClipData数组中移除
- 更新存储
- 保存新的数组到
chrome.storage.local
- 保存新的数组到
- 更新 UI
- 按钮变为空心 🤍
- 从悬浮坞列表中移除
自动检测机制
DOM 监听
AI-Clip 使用 MutationObserver 监听页面变化:
javascript
const observer = new MutationObserver(() => {
// 检测新的用户提问气泡
// 自动添加收藏按钮
})
observer.observe(document.body, {
childList: true,
subtree: true
})智能识别
- 动态内容:即使对话实时生成,也能及时添加按钮
- SPA 路由:支持单页应用的路由切换
- 延迟加载:处理懒加载的对话内容
最佳实践
✅ 建议做法
- 及时收藏:看到有价值的内容立即收藏
- 添加标签:为收藏添加有意义的标签
- 定期整理:清理不再需要的收藏
❌ 避免做法
- 过度收藏:不要收藏所有对话,保持精选
- 忽略标签:没有标签的收藏难以管理
- 忘记清理:定期删除过时的收藏
技术细节
内容提取算法
问题提取:
javascript
// Gemini
const question = bubble.querySelector('user-query')?.innerText
// ChatGPT
const question = bubble.querySelector('[data-message-author-role="user"]')?.innerText回答提取:
javascript
// Gemini
const answerHtml = bubble.nextElementSibling?.querySelector('.model-response-text')?.innerHTML
// ChatGPT
const answerHtml = bubble.nextElementSibling?.querySelector('.markdown')?.innerHTML性能优化
- 事件委托:使用事件委托减少监听器数量
- 防抖处理:搜索和筛选操作使用防抖
- 懒加载:悬浮坞列表支持虚拟滚动(未来版本)
故障排除
按钮不显示
可能原因:
- 页面尚未完全加载
- DOM 结构发生变化
- 扩展未正确加载
解决方法:
- 刷新页面
- 检查浏览器控制台错误
- 重新加载扩展
收藏失败
可能原因:
- 存储空间已满
- 内容提取失败
解决方法:
- 清理旧的收藏
- 检查控制台错误信息
状态不同步
可能原因:
- 存储监听器未正常工作
解决方法:
- 刷新所有标签页
- 重启浏览器
