`(复用 renderAccounts 的账号详情链接写法;接口 `v.*` 已含 account_id)
+- **验证**(CDP):20 行账号全部变链接(首个 #/account/1631 白昼小熊),点击 → hash #/account/1631 → 页面标题「白昼小熊」渲染成功
+- 工具栏右对齐(toolbar-right + margin-right:10px,同账号列表页)+ 全部列表页功能栏与表格间距 14→24px(.page-toolbar margin-bottom 全局)
+- 写脚本页说明文字移出工具栏:`.toolbar-note` 小字 12px 右对齐放功能栏下(-14px 上贴工具栏,距表 16px)
+- 改写弹窗三列内容区等高:源脚本列补空 `.rw-sec-foot` 占位 + `.rw-sec-foot` min-height 46px + `verCtrl` 始终渲染 foot(len=1 显示「X 1/1」,len=0 空占位)→ 三列 body 均 1055px 等高
+- **源脚本默认收起**:`let showSrc=false` 控制源脚本列 `rw-sec-hidden`(display:none);标题行加「源脚本/收起源脚本」切换按钮(`data-src-toggle`,`.src-on` 高亮态),点击重渲染弹窗;删除原 `@media(max-width:1100px)` 媒体查询(JS 接管);默认两列 518px 平分,展开后三列 341px
+
+## 查看弹窗 AI分镜列常显 + 生成分镜按钮 + 源脚本按钮图标化(12:25-12:45)
+- 用户需求:AI分镜提示词区域**默认展示**(没有内容也空着);下面功能区域提供**生成按钮**;原脚本按钮变成**图标**
+- **改 data-pages.js openRewriteCompare**:
+ 1. AI分镜列去掉 `${visibleSb.length ? ... : ''}` 条件包裹 → **始终渲染** `.rw-sec`;无分镜时 body 显示 `(暂无分镜提示词)
`
+ 2. foot 按状态分流:有分镜=`verCtrl('s',...)`(分镜 1/N + 上/下一版);无分镜=``
+ 3. `data-gen-sb` 点击 → `confirmRun({title:'生成分镜', desc/prompt:'将基于该 AI 改写脚本展开成 AI 分镜提示词(按分镜技能生成镜头级描述)', label:'分镜指令', btn:genSb})`(复用改写按钮同款任务触发链路)
+ 4. 源脚本按钮文字 → `svgIcon('file-text', 14, 'currentColor')` 图标(title 提示「展开源脚本/收起源脚本」切换;`.src-on` 高亮态保留)
+- **验证**(CDP 9223 #/rewrites 标签页):v189 行(rewrite 21 有 2 分镜)→ 3 列常显、分镜 1/2+上/下一版、源脚本图标按钮(无文字)、展开后三列 body 1055px 等高 / foot 46px / 各 341px 等宽;v190 行(rewrite 25 无分镜)→ 空态 dim「(暂无分镜提示词)」+ foot「生成分镜」按钮(zap 图标)、AI脚本与分镜列 body 均 1031px 等高、点按钮弹出「生成分镜」确认弹窗(title/desc/target 正确)
+- 测试脚本用完即删(cdp-test*.mjs + 截图),工作区无残留
+
+## 已有分镜的 AI 脚本提供「重新生成」按钮(17:56-18:00)
+- 用户需求:已经生成分镜的 AI 脚本,foot 也提供按钮可重新生成分镜
+- **改 data-pages.js**:`verCtrl(p, idx, len, label, genLabel)` 增加可选第5参——有值时 foot 尾部追加 ``(refresh 图标 14px currentColor);分镜列调用 `verCtrl('s', sbIdx, visibleSb.length, '分镜', '重新生成')`(AI脚本列 `verCtrl('v',...)` 不传,不受影响)
+- **事件**:抽出通用 `fireGenSb(btn, regenerate)` → `confirmRun({title: regenerate?'重新生成分镜':'生成分镜', prompt 文案 regenerate 时加「重新」})`;`[data-gen-sb]`(无分镜,zap 图标)与 `[data-sgen]`(有分镜,refresh 图标)两个按钮绑定复用
+- **验证**(CDP 9223):v189 行(2 分镜)foot=「分镜 1/2 ← 上一版 下一版 → ⟳ 重新生成」(无溢出,count margin-right:auto 推右),点按钮弹「重新生成分镜」确认窗;v190 行(无分镜)仍只显「⚡ 生成分镜」不受影响
+
+## 查看弹窗 AI脚本↔AI分镜 场次联动滚动(17:59-18:30)
+- 用户需求:滑动 AI脚本 滑到哪一场,右边 AI分镜提示词对应滚动到那一场
+- **数据结构**:AI脚本场标题=`**场N:…**`(renderMarkdown→``);分镜块=`段N(x-y秒)|场M:…`(一场可拆多段:场2前半/后半),`renderSbBlocks`→`.sb-block/.sb-head`
+- **改 data-pages.js renderModal**(仅 `visibleSb.length>0` 时绑定):`sync()`——①AI脚本列 `strong,h1-h4` 过滤 `/场\s*\d/` 得场标题数组(DOM 顺序=文档顺序)②`limit=aiBody.scrollTop+60`,找最后一个 `rect.top-aiTop+scrollTop <= limit` 的标题=当前场 ③正则提取场号 N ④分镜列 `.sb-block` 中 `find` 第一个 head 匹配 `场N(?=前半|后半|[::]|\s|$)` 的块 ⑤`sbBody.scrollTop = target.rect.top - sbBody.rect.top + sbBody.scrollTop`(不用 offsetTop,防 offsetParent 歧义);`aiBody.addEventListener('scroll', sync, {passive:true})` + 打开时 `sync()` 一次
+- **⚠️ rAF 教训(重要)**:初版用 `requestAnimationFrame(sync)` 节流,CDP 9223 tab `visibilityState=hidden` **rAF 不触发**→联动失效;改为直接同步调用 sync(计算量小:30 strong + 8 块,性能无碍)。**CDP 验证场景禁依赖 rAF**
+- **⚠️ 测试脚本转义教训**:bash heredoc 写 `.mjs` 里 `/场\s*\d/` 单反斜杠 → Node 解析 JS 字符串时 `\s`→`s`(无效转义丢反斜杠)→ 正则变 `场s*\d` 不匹配,误判 2 小时;**JS 字符串里正则必须写 `\\s`**(Edit 工具直写文件的正则字面量不受影响,产品代码一直是对的)
+- **验证**(CDP reload 后):S0 打开弹窗初始对齐场1(sbScrollTop 12);滑到场3→分镜 4392 可见「场3:老屋堂屋·当天傍晚」;场5→8528「场5:老屋灶房·高三深夜」;回场1→12;四条链路全过
+
+## 场次联动回退单向 + 修复段名格式正则 bug(18:30-19:05)
+- **用户明确:不要双向联动,只允许 AI脚本为主导的联动**——删 `syncAi` 反向逻辑与 `sbBody.addEventListener('scroll', syncAi)`,仅保留 `syncSb`(AI脚本列滚动 → 分镜列对齐)
+- **🐛 P0 真 bug(本次核心)**:`syncSb` 里分镜块匹配正则 `场N(?=前半|后半|[::]|\s|$)` 只兼容「场N:标题」冒号格式与「前半/后半」段名;**video 190 实际 head 是「场1前段/场3中段/场3中后段/场4末段」**(前段/后段/中段/中后段/末段 5 种段名)→ 正则完全不匹配 → target 永远 undefined → **联动失效**(之前会话验证的「场3:老屋堂屋」是冒号格式数据,掩盖了此 bug)
+- **修复**:`new RegExp('场' + m[1] + '(?![0-9])')`——负向前瞻防 `场1` 误匹配 `场10`,兼容全部段名格式与冒号格式
+- **验证**(CDP #/rewrites 页 video 190 rewrite 22,18 分镜块):
+ - ① AI脚本主导:滚 400/800(limit 未跨场2标题 t=887)→ 仍对齐场1(正确);滚 1200(limit 1260 > 887)→ 分镜列 12→2473 对齐场2前段 ✅
+ - ② 分镜列独立:分镜列 600→3073,AI脚本列 1200 纹丝不动 ✅(无反向联动)
+ - ③ 跳底:AI列 5621 → 分镜列 17480 对齐场6前段(场6 t=5567 ≤ limit 5681)✅
+- **⚠️ CDP 新教训:`scrollTop` 赋值不触发 scroll 事件监听**(fired 0;Chrome 将 scroll 事件异步/合并派发),`dispatchEvent(new Event('scroll'))` 才触发(fired 1);**验证脚本必须设置 scrollTop 后手动补发 scroll 事件**;真实用户滚轮不受影响(真实滚动正常派发事件)
+- 测试脚本:保留 `cdp-verify-oneway2.mjs`(最终验证版),其余 cdp-probe/debug/regex/event 探针已删
+
+## 视频标题去首尾引号(18:34-18:45)
+- 用户反馈:video 190「他守住的不止是那份快递…」标题带引号,要去掉
+- **根因**:`fmtTitleQ` 只清了尾部引号(`[\s"“”']+$`),开头引号没处理 → 弹窗显示 `《"他守住…》`;列表页完整标题(含 #标签)的引号在 `#` 前,也不在字符串首尾
+- **修复**(app.js + data-pages.js):
+ - app.js 新增 2 个工具:`stripTitleQuotes(s)`(清整串首尾引号:英文/中文双引号/单引号/「『」』,仅首尾中间保留)+ `cleanTitle(t)`(# 前主体去引号、# 标签原样保留——列表页完整标题用)
+ - `fmtTitleQ` 改调 stripTitleQuotes(弹窗);列表页/视频详情/改写按钮 payload/首页最近脚本 8 处显示改调 cleanTitle
+- **验证**(CDP):列表页 `他守住的不止是那份快递,还守住了留守孩子们的那份期待#AI创作浪潮计划…` 无引号;弹窗 `《他守住的不止是那份快递…》⏎#…` 无引号 ✅
+- 工具函数定义在 app.js 顶层(先加载),data-pages.js 直接复用(全局 lexical scope 共享)
+
+## 查看改写弹窗宽度自适应(18:38-18:40)
+- 用户需求:「已改写 → N 查看」弹窗距网页左右各 50px,视口变宽弹窗跟着变宽,**最大 1400px**
+- **改 style.css 347 行**:`.rw-modal` width `min(1100px, 95vw)` → `min(1400px, calc(100% - 100px))`(全局 border-box,100% 相对 mask fixed inset:0 视口宽,不含滚动条)
+- **验证**(CDP 多视口):1200→1085(视口-100,左右 50/50);1400→1285;1600/1920/2200→1400 封顶居中 ✅
+
+## 移植 dsh「热门账号→导入」功能到工作台(19:15-19:35)
+- **用户需求**:「账号列表页就是dsh插件 热门账号,里面还有个导入功能看看能否移植过来」
+- **dsh 原链路调研**(dsh-plugin-douyin-accounts/client.js + dsh-plugin-mcn/index.js):弹窗两种方式(方式一账号名/抖音号/地址 3 输入框、方式二本地文件夹路径)→ `POST /mcn/api/external/stage`(账号模式浏览器采集+AI会话确认,browserTaskRunning 409 互斥;目录模式直接 stage)→ preview(AI 确认前扫描比对不写库)→ import 写库
+- **移植设计**(工作台不直连 dsh /mcn 接口):前端加「导入账号」按钮 + 同款弹窗 → 复用 `executeTask` 提交 prompt 任务到 `/api/run`(左侧 mcn-workshop 会话栏可见,与「保存并分析」同构)→ WorkBuddy AI 执行导入 → 轮询完成自动刷新;确认环节由弹窗「提交导入」承担(dsh 的 AI 会话确认 → 工作台的 executeTask 任务模式)
+- **改动**:app.js 新增 `download` 图标 + `openImportAccounts(btn)`(弹窗两种方式、空校验 toast「请填写抖音账号信息或文件夹路径」、prompt 按目录/账号两种拼法、label「导入指令」);data-pages.js renderAccounts 工具栏加「导入账号」按钮(svgIcon download);style.css 补 `.import-form` 4 类样式;帮助页「账号分析」组加问法「把抖音账号「俊希」导入账号列表」
+- **验证**(CDP 9223 + fetch mock 零副作用):弹窗打开→4 输入框齐全→空提交 toast 校验→填账号提交→弹窗关闭+按钮「执行中…」+ /api/run prompt 正确→mock done 轮询→按钮恢复「导入」✅;帮助页问法 13 条含导入 ✅
+- **⚠️ 两个坑**:①页面有常驻隐藏 `#settingsModal`(index.html 静态弹窗),DOM 选择器查弹窗必须 `.modal-mask:not([hidden])`(否则匹配到设置弹窗误判);②fetch mock 判断顺序必须 `/api/run/status` 在前(`includes('/api/run')` 会先吞掉 status 请求)
+- 验证脚本保留 `cdp-verify-import.mjs`(含 fetch mock 模式,可复用)
+
+## 查看弹窗:展开源脚本按钮移入 AI脚本 标题 + 弹窗最大宽度 1600(18:48-19:00)
+- 用户需求:①「展开原脚本」按钮图标放到 **AI脚本 标题右边,间隔 10px**(原在弹窗顶部 modal-title-row)②弹窗最大宽度 1400→**1600**
+- **改 data-pages.js**:modal-title-row 删 data-src-toggle(保留标题+×);AI脚本列 `.rw-sec-head` 内追加 `