diff --git a/docs/superpowers/specs/2026-07-17-market-favorites-design.md b/docs/superpowers/specs/2026-07-17-market-favorites-design.md new file mode 100644 index 0000000..97bea7b --- /dev/null +++ b/docs/superpowers/specs/2026-07-17-market-favorites-design.md @@ -0,0 +1,158 @@ +# 星图达人收藏夹设计 + +## 背景 + +星图达人市场页已有列表筛选、导出和提交批次能力,但用户无法保存跨筛选条件、跨页面发现的优质达人。本功能新增本地收藏夹:用户可将达人归入多个不同主题的收藏夹,在需要时从收藏夹批量导入秒探。 + +收藏数据只保存在当前浏览器的当前插件配置中,不提供跨设备同步、团队共享或后端持久化。 + +## 目标 + +- 支持创建、改名和删除收藏夹。 +- 支持将同一达人加入多个收藏夹;同一达人在同一收藏夹内只保留一条关联。 +- 在市场列表每一行提供独立收藏入口,收藏时立即选择一个或多个收藏夹。 +- 在页面右侧提供常驻的收藏夹入口和抽屉,用于浏览、整理、筛选和导入已收藏达人。 +- 从抽屉内复用既有批次提交接口导入秒探,不重新采集星图列表。 + +## 非目标 + +- 不改变星图原生筛选、列表、下单或右侧服务栏功能。 +- 不把收藏夹塞入现有导出和筛选工具栏。 +- 不做插件弹窗中的第二套收藏夹管理界面。 +- 不保存完整市场行、视频、指标和画像快照;收藏夹不是历史报表。 +- 不实现跨设备同步、协作共享、后端备份或导入历史。 + +## 方案选择 + +### 方案 A:右侧入口 + 抽屉 + 行内收藏选择器 + +页面右侧提供收藏夹入口,点击后打开抽屉;每行收藏图标只打开贴近图标的收藏夹选择器。 + +优点:收藏、查看和导入各自处于最合适的位置;不挤占现有工具栏;适合频繁浏览和长期整理。 + +缺点:需要处理与星图原生右侧服务栏的空间和层级关系。 + +### 方案 B:行内图标直接打开右侧抽屉 + +每次收藏都先打开抽屉,再在抽屉中选择收藏夹。 + +优点:交互组件较少。 + +缺点:单个达人收藏需要跨越页面宽度,频繁操作成本高。 + +### 方案 C:工具栏按钮 + 模态框 + +在现有插件工具栏中增加收藏相关按钮,使用模态框管理收藏夹。 + +优点:实现边界集中。 + +缺点:工具栏已经承担导出、提交和筛选;收藏夹作为长期资产放在这里会造成信息和操作拥挤。 + +采用方案 A。 + +## 页面与交互 + +### 右侧入口 + +- 在星图原生右侧服务栏左侧固定一个窄入口,使用书签图标和 `收藏夹` 文本。 +- 入口避开原生的帮助、联系和客服按钮;在窄视口保持图标入口,不遮挡页面内容。 +- 入口可显示总收藏数,但不使用红色未读角标,以免被理解为通知。 +- 点击入口打开右侧抽屉;再次点击入口或抽屉关闭按钮可关闭抽屉。 + +### 行内收藏 + +- 在每个有效达人行的原生 `操作`/`下单` 控件之前增加书签图标,不修改或覆盖原生操作。 +- 未收藏时显示描边书签,悬停提示 `加入收藏夹`。 +- 已收藏至至少一个收藏夹时显示粉色实心书签,悬停提示 `已收藏至 N 个收藏夹`。 +- 点击图标在图标附近打开小型选择器,列出所有收藏夹及该达人的当前归属状态;用户可多选或取消选择。 +- 小型选择器提供 `新建收藏夹`,成功创建后立即把当前达人加入新收藏夹。 +- 达人没有可用 `authorId` 时,图标不可用并说明无法收藏该达人。 + +### 收藏夹抽屉 + +- 抽屉从右侧滑入,宽度为 `380-420px`,位于原生右侧服务栏左侧;打开或关闭都不改变星图列表的筛选、滚动和横向滚动位置。 +- 顶部包含 `收藏夹` 标题和 `新建收藏夹` 操作。 +- 默认显示 `全部达人`。同一达人即使在多个收藏夹中,也只显示一条记录。 +- 用户可切换为某个收藏夹,只查看该收藏夹中的达人;支持按达人名称搜索。 +- 每个达人显示最小身份信息、加入时间和操作菜单。操作菜单支持从当前收藏夹移除;在 `全部达人` 中仅展示归属,不提供破坏性的一键全局删除。 +- 抽屉列表支持复选,底部固定操作区显示已选人数。 + +### 导入秒探 + +- `导入已选 N 位达人`:抽屉内至少勾选一位达人后可用。 +- `导入当前收藏夹全部达人`:仅在查看具体收藏夹时可用,点击后必须显示二次确认,并写明本次导入人数。 +- 两个入口都先按 `authorId` 去重,再映射为既有批次 payload 所需的最小 `MarketRecord` 数据。 +- 沿用既有登录校验、批次名称输入、`createBatchPayload()` 和秒探批次提交客户端。 +- 导入成功后保留收藏和收藏夹成员关系;失败时不改变本地数据,并显示既有提交错误或明确的收藏夹错误。 + +## 本地数据 + +使用 `chrome.storage.local`,不使用内容脚本页面的 `window.localStorage`。插件清单已声明 `storage` 权限,插件存储不会与星图网页站点数据混用。 + +首版数据采用一个版本化根对象,便于整体校验和迁移: + +```ts +type FavoritesStateV1 = { + version: 1; + folders: Array<{ + id: string; + name: string; + createdAt: string; + updatedAt: string; + }>; + creators: Array<{ + authorId: string; + authorName: string; + coreUserId?: string; + savedAt: string; + }>; + memberships: Array<{ + authorId: string; + folderId: string; + addedAt: string; + }>; +}; +``` + +- `creator` 以 `authorId` 唯一;多收藏夹通过 `memberships` 表达,避免复制达人数据。 +- `memberships` 以 `(folderId, authorId)` 唯一。 +- 删除收藏夹只删除其成员关联;没有任何关联的达人记录可以一并清理。 +- 导入时若存在 `coreUserId`,继续作为现有 payload 的 `authorUid` 发送。 +- 读取缺失、格式错误或未知版本时视为未初始化状态,不应阻断市场页其他能力;写入失败必须向用户反馈。 + +## 模块边界 + +- 收藏数据仓库:读写、迁移、去重、收藏夹和成员关系规则;不依赖 DOM 或批次接口。 +- 行内收藏装饰:从市场行读取最小达人身份,渲染书签和选择器;不负责抽屉列表。 +- 收藏夹抽屉:呈现文件夹和达人,处理搜索、选择和管理操作;通过数据仓库获取状态。 +- 收藏夹导入适配层:将收藏达人转换为最小 `MarketRecord[]`,调用现有批次提交流程;不直接读写浏览器存储。 +- 市场控制器:组装这些模块并协调抽屉、行内状态与繁忙态。 + +## 状态与错误处理 + +- 收藏或取消收藏后,行内图标、选择器和已打开抽屉必须立刻同步。 +- 新建、改名和删除收藏夹时,名称去除首尾空格;空名称不可保存。删除前确认。 +- 导入执行期间,抽屉的导入和管理操作禁用,结束后恢复。 +- 导入前没有有效达人时,不打开批次名称输入,也不调用提交接口。 +- 对包含多个收藏夹成员的导入,提交前按 `authorId` 去重,避免发送重复作者。 +- 读取或写入插件存储失败时,只提示收藏夹操作失败,不影响星图原生页面或既有导出/提交功能。 + +## 验收标准 + +1. 用户可创建、改名和删除收藏夹;删除收藏夹不删除其他收藏夹中相同达人。 +2. 同一达人可加入多个收藏夹,但不会在同一收藏夹重复出现。 +3. 每个有效达人行均有独立收藏图标,且不会影响星图原生 `下单` 等操作。 +4. 行内图标可以打开多选收藏夹选择器,选择器可即时新建收藏夹并完成收藏。 +5. 右侧入口不与星图原生右侧服务栏重叠;点击后抽屉能显示全部达人和具体收藏夹中的达人。 +6. 抽屉的 `全部达人` 视图对跨收藏夹的同一达人去重;具体收藏夹视图只显示该收藏夹成员。 +7. 用户可导入已选达人;用户也可导入当前收藏夹全部达人,但必须经过人数明确的二次确认。 +8. 导入复用现有批次 payload 和提交接口,携带已有的 `authorId`、`authorName` 和可选 `authorUid`;成功后收藏不丢失。 +9. 浏览器存储不可用、达人缺少 ID、空收藏夹、空选择和提交失败均有清晰反馈。 + +## 验证范围 + +- 收藏数据仓库的创建、改名、删除、成员去重、跨收藏夹关联、序列化错误恢复测试。 +- 行内书签、选择器、新建收藏夹和状态同步的 DOM 测试。 +- 抽屉的默认视图、文件夹切换、搜索、移除、选择和全量导入确认测试。 +- 收藏达人到批次 payload 的映射、跨收藏夹去重、登录失败、提交成功/失败测试。 +- 运行相关 Vitest 测试和 `npm run build`。