4.5 小时,从一句话需求到可安装的 Chrome 插件:一次 AI 结对开发的完整复盘
目标读者 :想用 AI 独立完成一个浏览器插件(或类似小产品)的工程师;关心"AI 到底该怎么使唤"的工作方法派。
核心价值 :完整还原一个真实会话------从 27 问需求拷问、开工前性能约束、方案对标循环,到 6 轮主题迭代和 4 个代表性 bug 的根因修复,最后沉淀为一套可直接复用的浏览器插件开发 skill。
阅读时间:约 13 分钟(跳读法:只读六个章节标题 + 文末 8 条清单,3 分钟带走方法论)

这个会话干了什么
2026-08-30,下午 4:33 到晚上 9:06,我在 Cursor 里用一个会话做完了 html2md------一个把网页转成 Markdown 的 Chrome 插件:
- 功能 :扫描当前页 → 本地去噪出区域候选(主内容/导航/内容+导航/全文,页内高亮核对)→ 选定区域的清洗 HTML 发给 DeepSeek(OpenAI 兼容,可覆盖)流式转 GFM Markdown → 可选视觉模型识别图片补图注 → 一键复制 / 下载
.md/ IndexedDB 历史 + 搜索; - 技术栈:WXT + React 19 + TypeScript + Tailwind v4 + shadcn/ui + IndexedDB,Manifest V3,Side Panel 形态;
- 交付物 :47 个单测全绿的
.output/chrome-mv3/、约 215KB 的可分发 zip、一套翡翠绿主题和自定义工具栏图标。
全程 120 次用户输入、810 行会话记录。这篇文章按会话顺序复盘我是怎么想的,但每个章节标题就是一个可直接带走的方法------想跳读的,读完六个标题就够;最后我会把整套打法压缩成一个 skill。
先给一张全景图(时间戳只出现在这里):
16:33 ─ 需求拷问(grill-me,27 问) ~23 min
16:56 ─ 实施方案拆分(9 个 Task) ~4 min
17:00 ─ 开工前性能风险核对(4 类硬约束) ~2 min
17:02 ─ 第一版落地(29 测试通过) ~12 min
17:15 ─ 对标检查 → 补缺口 → 再对标 ~15 min
17:30 ─ UI/UX 打磨马拉松(~2 小时)
├─ 三轮布局重构 + shadcn/ui 接入
├─ 6 轮主题配色迭代
└─ 漂浮按钮 + 交互细节 20 余条
18:49 ─ 性能分析 → 按优先级修复
18:59 ─ Bug 修复若干(手势、跨域、隔离...)
20:25 ─ 功能扩展(指定区域 + 自定义 Prompt)
20:43 ─ 视觉链路 debug(接口返回了但结果没有)
21:06 ─ 打包交付
一、拷问 27 问:共识不是文档,是后面的验收合同
会话的第一条消息不是"帮我写个插件",而是挂了 grill-me skill:
Interview me relentlessly about every aspect of this plan until we reach a shared understanding. Walk down each branch of the design tree... Ask the questions one at a time. For each question, provide your recommended answer.
后面 27 个问题全部由这一段话长出来。我的原始需求如下:
text
/grill-me 我要在当前项目中做一个Chrome 浏览器插件,使技术栈采用主流的开发框架,核心需求如下:
1. 目标:将用户打开的html页面转为markdown文档
2. 默认使用 deepseek API,分别配置文本模型和图片识别模型的配置,支持自定义模型的配置项进行配置覆盖
3. 支持圈选页面结构,提供用户可选择的转换内容,先对DOM结构进行去噪,然后供用户选择可转换的区域,比如转换主内容区域、导航区域、内容+导航区域、全文区域等。
4. 图片识别功能默认不开启,需要保留开启的配置项来启用,默认不启用状态,转换后图片保留原始链接状态,启用并转换后修改为图片链接+图片模型识别够的文字描述
5. 转换后整体内容支持一键复制、分享
6. 保留历史转换记录并支持搜索
针对上述需求,分析是否还存在遗漏需求、流程不清晰的需求。
注意最后一句------我没有让 AI 直接开工,而是先让它审这份需求本身。AI 也确实从原始需求里点出了整棵决策树的根问题("去噪 / 区域发现 / HTML→Markdown 三步分别由本地还是模型完成,没有定义")。
一轮拷问花了 23 分钟、27 个问题,每个都带方案对比表和推荐答案。我基本在"选 默认的推荐选项",但有几处我刻意没有顺从推荐------这是整个阶段最有价值的部分:
决定产品形态的六问
第一问:转换流水线里,本地和大模型如何分工? 推荐 C(混合):本地 Readability 去噪出区域候选,用户选定后只把该区域的清洗 HTML 发给模型转 Markdown。我秒选------整页 HTML 进模型又贵又超上下文,这是整个决策树的根。
第四问:控制面板放哪? 推荐 B(仅 Side Panel)。这决定了后面所有 UI 讨论的舞台:弹窗一点外面就关,和"页内高亮核对区域"直接冲突。
第五问:自定义模型"覆盖"到哪一层? 推荐 B:OpenAI 兼容的 baseURL / apiKey / model 两套(文本/视觉各一套),默认填 DeepSeek。我选 B 的理由是它能使用整个生态------以后接 Ollama、Kimi、GLM 都是改三个字段,不需要写 Provider 适配层。
第九问:开启视觉时,哪些图要识别? 推荐 C(过滤装饰图 + 上限 10)。我的回答是 "C,上限做成配置项" 。这是我第一次修正 AI 的默认值------固定上限是拍脑袋,可配置才把判断权还给用户。类似的还有 "额外增加主题切换"------AI 的 25 问里没有这条,是我在共识收口前主动插入的新分叉。
第十六问:权限范围? 推荐 B:只注入 http(s),chrome://、内置 PDF、file:// 明确禁用并在侧栏说明原因。宁可功能边界画死,也不让用户遇到"转到一半报错"。
第十七问:DOM 什么时间点取样? 推荐 C(打开扫 + 手动重扫 + 转换时再取一次)。我给了一个更严格的答案:"整体功能应该在用户主动点击扫描时才触发,支持手动重扫,转换时再取一次"。打开侧栏绝不自动扫------这条直接决定了"插件空闲时对原页面零打扰"的产品性格。
为什么值得花 23 分钟被拷问
看几条共识里"需求原文没有、但被问题逼出来"的决策:
- API Key 只进
chrome.storage.local,绝不进chrome.storage.sync(Key 属于密钥,不该进 Google 账号); - 视觉模型取图必须走插件拉取 + 压缩 + base64,不能把 URL 丢给模型服务端去抓(登录墙、防盗链、内网必死)------这条后来成为一次大 bug 的伏笔;
- 超出上下文时预检拦截、引导缩小区域,而不是静默截断或分块(分块会切碎表格和代码块);
- 失败降级:视觉部分图失败不推翻整次转换,正文照常入历史;
- 一份长长的**"明确不做"清单**:任意 DOM 点选、广告过滤列表、分块拼接、多 Provider 适配、Web Share、i18n......
这份共识清单就是后面所有"对标检查"的验收合同。没有它,"AI 说做完了"是不可信的。

二、开工前先问性能:事前约束是几行字,事后优化要面对整个架构
共识齐了,AI 给出 9 个 Task 的实施方案后,我没有说"开始",而是说:
在实施之前先确认下当前实施方案是否存在性能问题,比如拖垮原页面,原页面加载缓慢、卡顿或存在内容溢出
这是内容脚本类插件最容易翻车的地方------你的代码跑在别人的页面里。AI 给出的四类风险我原样收进了计划约束:
- 每个 http(s) 页都注入脚本 → 约束:
document_idle只注册消息监听,重模块(Readability)点扫描时才动态import();未扫描不改 DOM、不注入全局 CSS; - 在活文档上改结构 → 约束:去噪只在 clone 上做,活页面只允许多一个高亮宿主;
- 高亮层撑破布局 ("内容溢出"最常见来源)→ 约束:Shadow DOM +
position:fixed+ 视口坐标 +pointer-events:none,全文模式只描视口边框,禁止整页蒙层; - 图片 canvas 压缩抢主线程 → 约束:content 只 fetch blob,压缩放侧栏做;视觉默认关,最多 10 张串行。
开工前把性能写成"不许违反"的清单,成本几乎为零;做完再优化,要动的就是已经成型的架构。 这是整个会话里成本收益最不对称的一件事。

然后我说:"开始实施"。
三、落地只用 12 分钟,但"做完了"要过三道对标
第一版只用了 12 分钟
AI 搭好 WXT 脚手架、写完全部核心模块:29 个单测通过,wxt build 成功,产物 .output/chrome-mv3/。中途 wxt init 脚手架卡住,它直接改成按官方 React 模板手搭------没有在我等的 12 分钟里跟我抱怨一句过程,只在结果里交代了降级方式。
"对标下实施方案,是否已经全部落地"
我发起了本次会话我最推荐的一个动作:让 AI 拿着共识和计划逐条核对代码,只报事实。它交出来的诚实度令人舒适:
| 计划要求 | 现状 |
|---|---|
| Readability 按消息拆包 | 产物把 Readability 打进了 content.js(约 46KB),每个 http(s) 页都会解析 |
| 转换前提示"将识别 n 张" | 提示出现在文本流结束后 |
| 401/429 提供"重试" | 文案有,没有重试按钮 |
| Readability 抽取单测 | 没有 |
| Task 9 真机验收 | 没做,环境里点不了 Chrome |
一句"修正下"之后:抽取改到侧栏用 DOMParser 做,content.js 从 46KB 降到 12KB;识图张数提前显示;补了重试和扫描取消;补了单测(31 个)。二次对标,只剩"真机点验"一项明确挂账------因为 AI 确实无法替你点浏览器,它反复提醒"请加载扩展后走一遍这四条"。
第一次"打包下":zip + crx 都产出,并如实告知新版 Chrome 会拦商店外 crx、zip 不能直接拖------这类平台事实 AI 主动说清楚了,省了我一次搜索。

四、UI 马拉松:先立规矩(设计令牌),再谈审美(每轮一个变量)
占了全程近一半时间的,是 40 多条几乎全是 UI/UX 的消息。外人看是"来回改皮肤",实际有清晰的三层递进------顺序不能反:先一致性,再状态归属,最后才是好看。
第一层:先要"一致性",再要"好看"
开场白是:"当前主题设计和交互方式太落后了,修正下保证主题一致性、交互一致性,提升可用性"。第一轮它抽了一套最小 UI 原子组件(Button 三档 / Section / TextField / Segmented / Status),把三个页签统一成卡片工作台。
第二轮我直接给了参照系:"对标下主流 AI 应用的主题和交互形式"------左侧图标导航、底部固定操作栏、就地展开的历史卡片。
第三轮我把参照系变成硬规矩:"UI 组件接入 shadcn/ui 组件库,保证 UI 的一致性、一致的交互操作、主题切换的一致性。主题设置采用主流 AI 应用的主题设置"。
shadcn/ui 的接入理由值得单独说:它不是 npm 依赖库,而是复制源码 + Radix 原语 + Tailwind CSS 变量 ,与 WXT + Tailwind v4 完全兼容,且 WXT 内置 @/* 别名,标准目录零配置。接入后所有页面禁止手写颜色值,一切走 bg-background / text-muted-foreground / border-border 语义令牌------这一步是后面六轮换色只动 CSS 变量的前提。主题设置本身也换成了 ChatGPT/Claude 式 Select 下拉(跟随系统 / 浅色 / 深色,带 Sun/Moon/Monitor 图标)。
第二层:布局与状态归属
一条消息同时管了视觉和状态两件事:"tab 切换应该放到顶部横向切换,另外不同浏览器页签的切换不应该相互影响,各自展示各自的进度状态"。
第二条的根因是旧实现 useEffect([tabId]) → resetScan:切页签就清空一切。修复方案是把转换页状态按 tabId 分桶(Record<number, TabState>,连 markdownRef、abortController 都进 Map<number, TabRefs>),切页签只搬运高亮、不动数据,页签关闭随 tabs.onRemoved 清理。
第三层:审美迭代,六轮收敛
接下来是肉眼可见的"骂街式"迭代,但我每一轮都只给一个变量:
| 轮 | 我的一句话输入 | 改动(全部发生在令牌层) |
|---|---|---|
| 0 | (初始 shadcn 默认) | zinc 灰,全黑白 |
| 1 | "不好看也不协调" | 蓝降饱和、背景加冷调、边框柔化 |
| 2 | "活泼灵动一点,不要黑白按钮或边框" | 活力靛蓝 + 全彩色按钮变体 + primary-soft 新令牌 |
| 3 | "主题色不要使用蓝色" | 换紫色 |
| 4 | "紫色要不好看" | AI 给候选,我拍板翡翠绿 oklch(0.60 0.16 160) |
| 5 | "完善阴影、渐变增加交互感" | 按钮渐变 + 双层阴影 + hover 浮起 |
六轮下来主色从 zinc 灰收敛到翡翠绿,每一轮都只改 CSS 变量、不碰组件代码------这就是第一层规矩立得住之后,第三层才能跑这么快的证明。选绿色还有个附带收益:和页内高亮、漂浮按钮、Markdown 预览的强调色天然统一。

零散但体现判断力的需求还有:基础字号设置(12--18 下拉,设置里的示例和转换结果共用同一个 --html2md-md-font 变量,所见即所得);16px 工具栏图标上字母发糊,加粗 M、收紧边距重导出。
漂浮按钮:一段"越收越紧"的交互打磨
页内入口的 8 条递进需求,节奏是一放一收一磨:
- 放:先让 AI 做全------右侧吸附、上下拖动(按 hostname 持久化位置)、点击唤起侧栏;贴参考图定外形(白色半胶囊 + 翡翠绿圆钮 + 自绘扫描括角图标);
- 收:再一刀刀砍------"改的不动"纠正误删拖动;"移除键盘 Enter/Space 唤醒"把可访问性让给交互纯粹(我的决定,AI 没劝);
- 磨:剩下全是单变量微调------缩小尺寸、hover 贴边无缝、默认轻微模糊 hover 变清晰。"侧栏开闭同步图标状态"这条还引出一个隐藏 bug(见下一部分)。
贴参考图 + 一次一个变量 + 用"改的不动"这种话纠偏,是 UI 结对里效率最高的沟通方式。别指望一轮 prompt 出设计稿,但每轮 prompt 必须只动一个维度。
五、Bug 复盘:报现象、修根因,让每一处失败都可见
Bug 1:「主内容」永远等于「全文」------一个数学上必输的比较
我反馈:"扫描完成后的转换区域不太对,大多数都是主内容和全文是一样的,是否应该让大模型去识别下 DOM 结构更好?"
AI 先反驳了我(这是我很喜欢的一次),然后找到了真正的根因:旧实现 findLargestTextElement 从 body 开始向下比较文本长度------任何后代元素的文本都不可能比 body 多 ,所以只要页面没有 <article>/<main> 语义标签,主内容必然退化成 body。这是个数学上必输的比较,跟"识别得不够聪明"无关。
结论:LLM 识别 DOM 不该进扫描路径(扫描强依赖 API Key、每次多烧几秒和 token),改用 Readability 同款文本密度评分 :得分 = 正文长度 × (1 − 链接密度) × 段落加成 × 语义加成,外加两条从测试失败里长出来的规则------语义标签优先短路 (article/main 分数够就直接用,密度分只当兜底),同分带取小(父子元素得分差 ≤2% 时选更小的,解决"整页包在一个大 div 里");再做区域去重:主内容覆盖全文字数 ≥90% 时不再显示重复的"全文"选项。
我对症状的归因(模型不够聪明)是错的,AI 对着代码重新归因(比较函数必输)是对的。报 bug 描述现象就好,别急着指定药方------但前提是你允许 AI 反驳你。
Bug 2:点入口图标打不开侧栏------用户手势会"过期"
"当前点击入口图标无法打开侧栏"。根因一句话:Chrome 要求 sidePanel.open() 必须在用户手势的同步调用栈里执行 ,而 background 里写成 await setOptions(...) 之后才 open()------手势已经随 await 失效。修复就是把 open() 提到任何 await 之前。MV3 老坑,但没踩过一遍真的记不住。
Bug 3:「将识别 6 张」实际只跑了 1 张------一条静默失败的三重奏
我盯着视觉链路 debug 了二十多分钟,分两幕:
第一幕 :模型接口明明返回了详细描述(贴了完整 JSON),转换结果里却没有 > 图:。根因:insertCaptions 按"抽取时的绝对 URL"精确匹配 Markdown 里的图片语法,而文本模型经常改写 URL、转成相对路径、或干脆把小图标丢了------对不上就静默 continue。修复分四级降级:URL 路径归一化匹配 → 也认 <img> 标签 → 再对不上按文档顺序插 → 一张都找不到就追加到文末,不再静默丢弃。
第二幕 :提示识别 6 张,实际只跑了 1 张还不在主内容区。顺链路一查是三个独立缺陷叠加:提示按扫描时活 DOM 计数,但真正拉图在 content script 里 fetch,跨域 CDN 被 CORS 挡掉后直接 continue,界面上完全不可见 ;进度又按"拉成功张数"显示,于是只剩同源小图标(那个绿色对勾);选图按 DOM 顺序,页头图标排在正文大图前面。修复:从送给模型的最终 HTML 里选图并按尺寸优先;改由扩展页权限拉图绕过 CORS(页面 canvas 兜底);拉取失败写进提示("5 张拉取失败")而不是吞掉。
跨模块管线里,每一处
catch { }吞掉的都是未来 debug 的燃料。"静默跳过"在用户视角等同于功能坏了。这条教训我在会话里一共用了三次"为什么接口返回了结果里没有"才榨干。

Bug 4:关侧栏后按钮不复位------复杂方案被一句"不用这么麻烦"救回
"侧边栏关闭后,入口图标没有恢复模糊"。根因:侧栏页面正在卸载时 pagehide 里发的消息经常送不出去。AI 第一次修:改走后台中转------还是失败;第二次修:侧栏与 background 建长连接,断开即视为关闭------方案开始发重。这时我说了整个会话里最有"剃刀"味的一句话:
"不用这么麻烦了,只有 hover 图标入口改为清晰状态,否则都是默认的模糊状态即可。"
需求降级后,长连接、FAB_STATE 消息、开关状态全部删除,十行 CSS 解决。当你发现 AI 在为一个小需求搭越来越复杂的机械时,第一反应应该是砍需求,不是加约束。
其他值得一提的修复(一句话系列)
- 「已转换过的链接显示尚未扫描」→ 页签切换不再卸载转换页 + 按 URL 从历史回填;「结果(来自历史)」标记保持到下一次真正开始转换;
- 图片弹窗"放大缩小没效果"→
zoom与max-height互相抵消,改按"适配尺寸 × 缩放倍数"显式设宽高; - 滚动区文字贴边框 → 规律:上下留白要么放在滚动内容里、要么固定在外框上,两种都行,唯独不能指望
overflow-auto容器自己的 padding(内层 padding 会被滚走); - 流式预览自动跟随 → 外层先平滑滚到结果区、内层瞬时贴底跟字、用户向上滚动即打断;全局
scroll-behavior: smooth对流式跟随是负优化(每 80ms 一段没跑完的动画); - 循环依赖导致 IDE 报"找不到模块" → 共享类型抽
convert-types.ts打断; - 转换成功后按钮仍可点是否合理 → AI 判断合理(换区域/改设置要能重跑),缺的只是文案,改为已有结果时显示「重新转换」------先分析再动手,不擅自改。
又一次性能回马枪
全部功能稳定后,我再问了一遍"分析当前实现是否存在性能问题"。空闲路径被确认是克制的(document_idle、动态 import、只多一个 Shadow DOM 按钮);真正开销集中在主动路径,按优先级列出四条并当场修了三条:高亮滚动每帧重跑全页打分 → 选区时缓存根节点,滚动只重算矩形 ;扫描遍历所有 div → 只给语义元素和段落祖先打分、上限 120 候选;SSE 每个 token 重跑 react-markdown → 约 80ms 节流合并;主内容提取整页克隆 → 只克隆主区域。修完视觉链路后又例行问了一次,给拉图加了 8s 单张超时 + 4 路并发、页面 canvas 已能读到的图不再重复下载。性能审计在这个会话里不是一个任务,是一个随时可复用的提问句式。
六、给稳定系统加需求:先问价值,再审计状态机
临近收工,我提了一个新功能:"扫描当前页旁边增加指定区域扫描 + 自定义 Prompt",但第一句是------
先分析当前功能是否有价值
AI 的分析:自动扫描搞不定的文档站局部模块、评论区,点选是刚需;且能复用整条转换管线;但注意这会推翻共识里"不做任意 DOM 点选"的承诺。我确认后才出计划、才实施。紧接着我发了一句"分析两套流程是否冲突,状态切换是否正常"------它画了张 mermaid 状态机图,指出取消/重选路径上侧栏状态和页面节点会失同步,一句"修正下"后按"成对回滚"补齐:进入任何新流程先拍快照藏旧 UI,取消则整体回到进入前。
新价值确认 → 实施 → 状态机审计 → 修补,这是给已有系统加功能的完整安全链。放在会话末尾做,而不是第一版就贪。
方法论:4.5 小时压缩成 8 个可执行动作
前文每章各论证了一部分,这里汇总成可照抄的清单(括号内为出处章节):
- 动手前挂 grill-me,让 AI 把决策树每个分叉问完、每条都带推荐答案,逼出一份含"明确不做"的共识------那是后面一切验收的合同(§一);
- 开工前先问性能(尤其代码会注入别人地盘的场景),把风险转成硬约束写进计划(§二);
- 实施后必对标:"对标下实施方案,是否已经全部落地"------只要事实清单,然后"修正下",循环到诚实的绿灯(§三);
- UI 先立规矩再谈审美:一致性(原子组件/shadcn 设计令牌)→ 布局与状态归属 → 每轮一个变量的审美迭代(§四);
- 报 bug 只报现象,允许 AI 反驳你的归因;管线里禁止静默失败,失败必须可见(§五 Bug 1/3);
- AI 开始为小需求搭复杂机械时,先考虑砍需求------"不用这么麻烦了"(§五 Bug 4);
- "打包下"当心跳:每个里程碑产出可装进浏览器的 zip,真机验证永远是人的事(贯穿全程);
- 加新价值先问"是否有价值",落地后审计状态机(§六)。
附:提炼出的 Skill ------ browser-extension-dev
以下 skill 可直接存入 ~/.claude/skills/browser-extension-dev/SKILL.md(Cursor 同理)。它不是 html2md 的使用说明书,而是从这次会话蒸馏出的通用浏览器插件开发流程与坑位清单。
markdown
---
name: browser-extension-dev
description: 用 AI 从零开发 Chrome/Edge 浏览器扩展(Manifest V3)的端到端流程。适用于:需求评审一个插件方案、搭建 WXT/CRX 工程、设计 content script 与页面交互、修 side panel/权限/跨域类 bug、出包前验收。触发词:浏览器插件、Chrome 扩展、browser extension、MV3、content script、side panel。
---
# 浏览器插件开发(AI 结对版)
核心信条:插件的代码跑在**别人的页面**里,插件的需求长在**决策树**上。
一切流程围绕两件事:开工前钉死决策,开工后不许静默失败。
## Phase 0 --- 需求拷问(不写完代码,先问完问题)
动手前逐分支追问直到共识收口,每个问题给方案表 + 推荐答案。扩展专属分叉,缺一不可:
- **流水线分工**:哪些步骤本地做(免费、离线、即时),哪些步骤大模型做(贵、慢、聪明)?
默认推荐混合:本地去噪/抽取,模型只处理用户圈定的一小块。
- **UI 表面**:toolbar popup(一点就关,装不下工作流)/ options 页 / **Side Panel**
(与页面并排、常驻,适合工作台型)/ 页内注入浮层。选 Side Panel 时注意它是**窗口级视图**,
按 tab 隔离要自己管 `setOptions({ tabId })`。
- **权限边界**:`<all_urls>`(审核面大)vs 仅 `http(s)` + 明确禁用页(chrome://、内置 PDF、
file://------侧栏要**预先禁用并说明原因**,不能转到一半报错)。密钥只进 `storage.local`,绝不 `sync`。
- **模型接入粒度**:只换模型名 / OpenAI 兼容 baseURL+key+model 整套覆盖(推荐,使用生态)/ 多 Provider。
- **存储分层**:设置 → `chrome.storage.local`;大对象(历史正文)→ IndexedDB + 条数/时间双上限;不存原始 HTML。
- **取样时机**:打开面板即扫 / 自动跟随 DOM / **仅用户主动扫描 + 转换时按当时 DOM 重取**(推荐:
空闲零打扰,SPA 用户手动重扫)。
- **失败语义**:逐类定义(无 Key 仍可预览 / 401 引导去设置 / 429 给重试不自动连打 /
可选子任务失败不推翻主结果),禁止一个 toast 走天下。
- **产出一份"明确不做"清单**并写进共识。
## Phase 1 --- 开工前性能与行为约束
把以下四类风险写成硬约束再写代码:
1. **注入成本**:content script `document_idle` 挂载且只注册消息监听;重库(Readability 等)
动态 `import()` 或干脆放侧栏跑;校验产物体积(本例:主包 46KB → 12KB)。
2. **不改活 DOM**:一切清洗/解析在 `clone` 上做;空闲时对原页面的改动必须为零。
3. **页内覆盖层不撑布局**:Shadow DOM + `position:fixed` + 视口坐标 + `pointer-events:none`;
高亮"全文"只描视口边框,禁止整页蒙层;滚动对齐用 rAF。
4. **重计算不占页面主线程**:canvas 压缩等放扩展页;对模型/图片的调用设上限并默认关闭。
## Phase 2 --- 实施与对标循环
- 按计划拆 Task 实施后,**必问**:"对标实施方案,是否已全部落地?只报事实。"
- 期望产出「已对齐 / 未对齐」两列清单(含诚实的"没做真机验收"),按缺口"修正下",再对标。
- 类型报错、构建产物、测试数一并汇报;每个里程碑执行打包:`wxt build` + zip。
- 打包常识:新版 Chrome 拦截商店外 `.crx` 拖拽,zip + "加载已解压"才是可靠分发;pem 不外发。
## Phase 3 --- UI 与主题
- **先一致性后审美**:第一轮抽原子组件(Button/Field/Card/Status),第二轮接 shadcn/ui
(复制源码 + Radix + Tailwind CSS 变量),全部颜色走语义令牌(`bg-background` 等),
禁止散落的手写色值------这是后续换肤只动 CSS 变量的前提。
- 主题三档(跟随系统/浅/深)用 `.dark` class + `storage.onChanged` 即时换肤;
主题设置形态对齐主流 AI 应用(Select 下拉 + 图标)。
- 审美迭代**每轮只动一个变量**(色相 / 饱和度 / 阴影 / 圆角 / 尺寸),可贴参考图;
色值一律 oklch,主色变则派生令牌(primary-soft、accent、border、ring、高亮色、
Markdown 预览强调色)同步换 hue。
- 页内元素(高亮框、漂浮按钮)用固定强调色,**不跟侧栏主题变**。
- 状态归属早想清楚:多浏览器 tab 的工作台,状态必须按 `tabId` 分桶(含 ref/AbortController),
切 tab 只迁移覆盖层,不清数据;页签关闭随 `tabs.onRemoved` 清理。
- 交互底线:可点元素手型指针;瞬切视图加淡入/高度过渡并尊重 `prefers-reduced-motion`;
流式输出区自动贴底且可被用户滚动打断;滚动容器 padding 放内容层或固定外框,不放 overflow 层自身。
## Phase 4 --- 坑位清单(bug 类问题先查这里)
- `sidePanel.open()` 必须在用户手势的**同步**调用栈里------前面任何 `await` 都会吃掉手势。
- 侧栏 `pagehide` 时发的消息大概率丢失;跨"侧栏↔页面"的状态同步优先经 background 中转。
- content script 里 `fetch` 图片会被 CORS 挡:**用扩展页权限拉图**,页面 canvas 兜底;
视觉模型传图用插件拉取+压缩+base64 data URL,别把裸 URL 丢给模型服务端。
- 把模型产物贴回文档(caption/引用)时,精确字符串匹配必炸(模型会改写 URL/丢图):
归一化匹配 → 备选格式 → 按序插入 → 追加兜底,**对不上也要可见**。
- "文本最长元素"启发式数学上必输(后代不可能比 body 长):主内容识别用
文本密度评分(长度 ×(1−链接密度)×段落加成),语义标签优先短路,同分带(±2%)取更小元素,
区域间做 ≥90% 覆盖去重。
- 每处 `catch {}` 吞掉的失败都要在 UI 计数可见("n 张拉取失败"),否则无法区分"没做"和"没做成"。
- 弹窗内缩放图片:CSS `zoom` 会与 `max-height` 互相抵消,按"适配尺寸 × 倍数"显式设宽高。
## Phase 5 --- 交付与回马枪
- 功能稳定后再问一遍:"分析当前实现是否存在性能问题" → 按「收益最大改动最小」排序修复
(高频重算先缓存、全量遍历设上限、流式渲染节流合并、克隆只取所需子树、
网络设单张超时 + 小并发)。
- 真机验收清单(AI 做不了,人来):安装前后原页面无差异;扫描后无双滚动条、链接可点、
高亮不挡点击;chrome:// 等禁用页表现正确。
- 加新功能先问"这个功能是否有价值",落地后问"新旧两套流程状态切换是否冲突"
(画状态机,检查取消/重入路径是否成对回滚)。
## 通用纪律
- AI 为小需求搭复杂机械时:先考虑砍需求("不用这么麻烦,改成......即可"),再加约束。
- 允许 AI 反驳你对 bug 的归因;你负责描述现象和拍板,别急着开药方。
- 每个共识决策都可能成为未来 bug 报告的裁判------把"明确不做"也当作交付物。
尾声
回看整个会话,AI 负责了 95% 的代码和 100% 的体力活,但真正让质量爬坡的是那十几句非代码输入:"先分析下是否有遗漏需求"、"实施之前先确认性能问题"、"对标下是否全部落地"、"先分析当前功能是否有价值"、"不用这么麻烦了"。
浏览器插件是个特别好的 AI 结对标的------它有清晰的边界(MV3 清单、权限、注入成本)、可验证的交付(能不能拖进浏览器跑起来)、以及大量只有踩过才知道的人肉坑(手势过期、CORS、pagehide 丢消息)。把这些坑沉进 skill,下一个插件会话就可以直接从第二十七问开始,而不是从第一个坑开始。
会话最后一条消息是第二天早上 9:58:「当前对话的完整 session 文件路径是什么」------你现在读到的这篇文章,就是那个文件的答案。