From 831fd0e123c9ec176b31fc8ed5401f8c83bc5e1a Mon Sep 17 00:00:00 2001 From: maogeigei Date: Wed, 2 Sep 2026 10:22:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=B7=A5=E4=BD=9C=E5=8F=B0UI=EF=BC=9A=E8=B4=A6?= =?UTF-8?q?=E5=8F=B7=E8=AF=A6=E6=83=85=E5=8E=BB=E5=B9=B4=E9=BE=84/?= =?UTF-8?q?=E5=9C=B0=E5=8C=BA=E9=87=8D=E6=8E=92=E4=B8=A4=E8=A1=8C=20+=20?= =?UTF-8?q?=E8=A7=86=E9=A2=91=E4=BF=A1=E6=81=AF=E5=AD=97=E6=AE=B5=E5=AF=B9?= =?UTF-8?q?=E9=BD=90(video-grid=E5=8D=95=E5=AE=B9=E5=99=A8=E8=B7=A8?= =?UTF-8?q?=E8=A1=8C=E5=88=97=E5=AF=B9=E9=BD=90)=20+=20=E8=A7=86=E9=A2=91I?= =?UTF-8?q?D=E9=99=90=E5=AE=BD220px?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 账号信息:行1=抖音号/作品数/获取视频,行2=赛道/AI制作/粉丝/获赞\n- 视频信息:8字段合并同一grid容器(2行x4列,minmax(min-content,max-content)+80px间隔),时长↔分享/发布时间↔收藏逐列对齐(CDP实测差0)\n- 视频ID .vid-limited 220px省略+hover全量\n- 新增 cdp-align-videoinfo.mjs 对齐测量脚本 --- .workbuddy/memory/2026-09-01.md | 5 ++ .workbuddy/memory/2026-09-02.md | 22 +++++ .../mcn-work-shop/cdp-align-videoinfo.mjs | 80 +++++++++++++++++++ .../V1.0/mcn-work-shop/public/data-pages.js | 15 ++-- .../V1.0/mcn-work-shop/public/style.css | 10 ++- 5 files changed, 122 insertions(+), 10 deletions(-) create mode 100644 .workbuddy/memory/2026-09-02.md create mode 100644 project/短视频脚本创作/V1.0/mcn-work-shop/cdp-align-videoinfo.mjs diff --git a/.workbuddy/memory/2026-09-01.md b/.workbuddy/memory/2026-09-01.md index 22daa85..96336e1 100644 --- a/.workbuddy/memory/2026-09-01.md +++ b/.workbuddy/memory/2026-09-01.md @@ -325,3 +325,8 @@ - 修改:references/创作流程/6_生成短视频框架.md「场次结构表·8列维度说明」后新增通用规则(引用 S5「结构性符号」概念,不硬编码任何账号/符号)——「开场钩子场(场1)结构性符号承接:场1钩子设计必须承接账号结构性符号,位于开头位置的须一个镜头/一句话交代清楚(遵循 S5 呈现效率规则),禁止铺垫/多步推理;缺失或需铺垫的打回修改场1」 - 同步:V1.0 源技能(~/.workbuddy/skills/短视频脚本创作/)+ dsh 副本(~/.dsh/skills/short-video-script/)两处,Lite 不动 - 重跑验证:备份 mcn-plugin.db.bak-s6fix-* → 删 RW26-31 + script_review(rewrite_id 26-31) → build-create-prompts.cjs 提交 6 任务(automation-1788258518460 等,v309/313/320=topic、v315/316/318=content)→ poll-create-tasks.cjs 轮询(task M70h3o) + +## 09-02 失误与规避:浏览器操作工具选择(10:15) +- 失误:验证视频详情页对齐时误用 agent-browser(system 技能列表里可见),未遵守项目铁律「浏览器操作一律 browser-harness」(user 级技能,Always use browser-harness for any web interaction) +- 检查:browser-harness 环境未安装——技能目录只有 SKILL.md,无 subskills/browser-harness/envs/ 环境(可执行文件 browser-harness.exe 不存在) +- 规避:涉及浏览器自动化/截图/交互时,先 where browser-harness 或查 C:/Users/maidou/.workbuddy/skills/browser-harness/subskills/browser-harness/envs/browser-harness/Scripts/browser-harness.exe 是否存在;未装则 uv tool install --python 3.12 --upgrade --force browser-harness + doctor 验三要素,不装 agent-browser diff --git a/.workbuddy/memory/2026-09-02.md b/.workbuddy/memory/2026-09-02.md new file mode 100644 index 0000000..a6bf406 --- /dev/null +++ b/.workbuddy/memory/2026-09-02.md @@ -0,0 +1,22 @@ +# 2026-09-02 工作日志 + +## 工作台 UI 调整(09:53-10:15) +- 09:53 打开 MCN 工作台(服务重启,PID 32336,`?v=20260902-0953#/`) +- **账号详情页账号信息**(data-pages.js:412-413):去掉「年龄」「地区」,重排两行——行1=抖音号/作品数/获取视频,行2=赛道/AI制作/粉丝/获赞;简介/账号定位/视频列表不变 +- **视频详情字段间隔/对齐**(style.css:383-385 + data-pages.js:552-556,多轮迭代定稿): + - 需求:字段横向间隔参考账号信息(flex+80px)→ 但「分享/收藏」与「时长/发布时间」要求跨行对齐 + - 迭代教训:flex 内容宽度天然不跨行对齐;独立 .info-row 容器各自 grid 也不会对齐(跨容器不共享列轨道) + - **终版方案**:8 字段(账号/视频ID/时长/发布时间 + 点赞/评论/分享/收藏)合并进同一个 `.video-grid` 容器(2行×4列 grid,`repeat(4, minmax(min-content, max-content))` + gap 10px 80px + justify-content:start),列轨道跨行共享→严格对齐,列宽贴合内容非均分;标签行 info-wide 占满整行 + - 视频ID 限宽 220px(.vid-limited,省略号+hover title 全量) +- 服务未重启(纯静态资源,Cache-Control: no-store,重开页面即生效) + +## 失误与规避:浏览器操作工具选择(10:15) +- 失误:验证页面时误用 agent-browser(system 技能列表可见),未遵守铁律「浏览器操作一律 browser-harness」(user 级技能) +- 检查:browser-harness 环境未安装——技能目录只有 SKILL.md,无 subskills/browser-harness/envs/(browser-harness.exe 不存在) +- 规避:需要浏览器自动化/截图时,先查 `C:\Users\maidou\.workbuddy\skills\browser-harness\subskills\browser-harness\envs\browser-harness\Scripts\browser-harness.exe`;未装则 `uv tool install --python 3.12 --upgrade --force browser-harness` + doctor 验三要素;不装 agent-browser + +## browser-harness 真实环境定位 + 对齐实测通过(10:20 纠正昨日误判) +- **纠正**:browser-harness 并未"未安装"——真身在源技能 `project/短视频脚本创作/V1.0/subskill/mcn-dou-analysis/subskills/browser-harness/`(envs/browser-harness/python.exe 完整存在,已验证);另有 `C:/Users/maidou/.dsh/skills/browser-harness/src`(无 envs)与 `D:/miniconda3/envs/browser-harness`(08-28 用过)两处 +- **昨天实测主流方式**(非 python IPC):Node + WebSocket 直连 9223 CDP 脚本(mcn-work-shop/cdp-verify-*.mjs、cdp-measure-*.mjs 等 30+ 个),Runtime.evaluate 测量 DOM;browser-harness python 调用(BU_CDP_URL=9223 + PYTHONPATH=/src + env python -m browser_harness.run heredoc)今天实测 IPC 超时(daemon 0.1.8 vs client 0.1.10 版本不匹配) +- **9223 调试 Chrome**:`chrome.exe --remote-debugging-port=9223 --user-data-dir=C:/Users/maidou/.workbuddy/tmp/bh-chrome-9223`;新开 Tab 访问 localhost 会 chrome-error,必须复用现有 8900 标签页 +- **对齐实测**(新脚本 cdp-align-videoinfo.mjs):视频详情 .video-grid 8 字段逐列 left 完全一致(账号@41=点赞@41、视频ID@157=评论@157、时长@404=分享@404、发布时间@549=收藏@549,差 0)✅ 结构修复生效 diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/cdp-align-videoinfo.mjs b/project/短视频脚本创作/V1.0/mcn-work-shop/cdp-align-videoinfo.mjs new file mode 100644 index 0000000..6719d81 --- /dev/null +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/cdp-align-videoinfo.mjs @@ -0,0 +1,80 @@ +/* cdp-align-videoinfo.mjs — 测量视频详情页 .video-grid 各字段 x 坐标,验证跨行列对齐(09-02) + * 期望:第 i 列两行字段 left 一致(时长↔分享、发布时间↔收藏) + */ +const CDP = 'http://127.0.0.1:9223'; +async function getWs(targetId) { + const tabs = await (await fetch(CDP + '/json')).json(); + const t = tabs.find((x) => x.id === targetId); + if (!t) throw new Error('target not found: ' + targetId); + return t.webSocketDebuggerUrl; +} +function cdp(ws, method, params = {}) { + return new Promise((resolve, reject) => { + const id = ++cdp._id; + const w = new WebSocket(ws); + const timer = setTimeout(() => { try { w.close(); } catch (e) {} reject(new Error('timeout: ' + method)); }, 30000); + w.onopen = () => w.send(JSON.stringify({ id, method, params })); + w.onmessage = (ev) => { + const m = JSON.parse(ev.data); + if (m.id !== id) return; + clearTimeout(timer); + try { w.close(); } catch (e) {} + m.error ? reject(new Error(method + ': ' + JSON.stringify(m.error))) : resolve(m.result); + }; + w.onerror = () => { clearTimeout(timer); reject(new Error('ws error: ' + method)); }; + }); +} +cdp._id = 0; +async function evalJs(ws, expr) { + const r = await cdp(ws, 'Runtime.evaluate', { expression: expr, awaitPromise: true, returnByValue: true }); + if (r.exceptionDetails) throw new Error('eval err: ' + JSON.stringify(r.exceptionDetails).slice(0, 300)); + return r.result && r.result.value; +} +const sleep = (ms) => new Promise((r) => setTimeout(r, ms)); + +async function main() { + const tabs = await (await fetch(CDP + '/json')).json(); + const tab = tabs.find((x) => x.url.includes('localhost:8900')) || tabs.find((x) => x.type === 'page'); + if (!tab) throw new Error('no 8900 tab found'); + const ws = await getWs(tab.id); + await cdp(ws, 'Page.enable'); + await cdp(ws, 'Runtime.enable'); + // 复用现有 8900 标签页导航(新开 Tab 访问 localhost 会 chrome-error) + await cdp(ws, 'Page.navigate', { url: 'http://localhost:8900/?v=aligncheck0902#/video/309' }); + await sleep(2500); + const measure = `(() => { + const items = [...document.querySelectorAll('.video-info .video-grid .info-item')]; + if (!items.length) return { err: 'video-grid items not found', html: document.querySelector('.video-info') ? document.querySelector('.video-info').textContent.slice(0, 80) : 'no .video-info' }; + const row = (el) => Math.round(el.getBoundingClientRect().top / 5); + return items.map((el, i) => { + const label = el.querySelector('.info-label') ? el.querySelector('.info-label').textContent.trim() : '?'; + const r = el.getBoundingClientRect(); + return { i, label, left: Math.round(r.left), top: Math.round(r.top), row: row(el) }; + }); + })()`; + const list = await evalJs(ws, measure); + if (list.err) { console.error('ERR:', list.err); process.exit(1); } + // 按 row 分组(前两行各 4 项) + const rows = {}; + for (const it of list) { (rows[it.row] = rows[it.row] || []).push(it); } + const rKeys = Object.keys(rows).sort((a, b) => a - b).slice(0, 2); + console.log('=== .video-grid 字段测量 ==='); + for (const k of rKeys) { + console.log('行' + k + ':', rows[k].map((x) => `${x.label}@${x.left}`).join(' ')); + } + if (rKeys.length === 2) { + const a = rows[rKeys[0]], b = rows[rKeys[1]]; + const diff2 = a[2].left - b[2].left; + const diff3 = a[3].left - b[3].left; + console.log('---'); + console.log('第3列(时长↔分享) left 差:', diff2, diff2 === 0 ? '✅ 对齐' : '❌ 未对齐'); + console.log('第4列(发布时间↔收藏) left 差:', diff3, diff3 === 0 ? '✅ 对齐' : '❌ 未对齐'); + const labels1 = a.map((x) => x.label).join('/'); + const labels2 = b.map((x) => x.label).join('/'); + console.log('行1:', labels1, '| 行2:', labels2); + } else { + console.log('⚠️ 只识别到', rKeys.length, '行'); + } + process.exit(0); +} +main().catch((e) => { console.error('ERROR:', e.message); process.exit(1); }); diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js b/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js index 117d0b7..b1c198b 100644 --- a/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/public/data-pages.js @@ -409,9 +409,8 @@ async function renderAccountDetail(view, id) { // 账号定位:默认最多 10 行,可点击展开/收起 const contentHtml = `${esc(contentText) || '-'}${contentText.length > 200 ? `` : ''}`; panel = `
-
${infoGrid('抖音号', acc.douyin_id)}${infoGrid('年龄', acc.age)}
-
${infoGrid('赛道', acc.track)}${infoGrid('AI制作', acc.is_ai === '是' ? '是' : '否')}${infoGrid('地区', acc.location)}
-
${infoGrid('作品数', acc.works_count)}${infoGrid('粉丝', acc.followers)}${infoGrid('获赞', acc.total_likes)}${infoGrid('获取视频', dv.total)}
+
${infoGrid('抖音号', acc.douyin_id)}${infoGrid('作品数', acc.works_count)}${infoGrid('获取视频', dv.total)}
+
${infoGrid('赛道', acc.track)}${infoGrid('AI制作', acc.is_ai === '是' ? '是' : '否')}${infoGrid('粉丝', acc.followers)}${infoGrid('获赞', acc.total_likes)}
${infoGrid('简介', acc.bio, true)}
${infoGrid('账号定位', contentHtml, true, true)}
@@ -533,7 +532,7 @@ async function renderVideoDetail(view, id) { const d = await api('/api/dsh/video-detail?videoId=' + videoId).catch((e) => ({ ok: false, error: e.message })); if (!d.ok) { view.innerHTML = `
${toolbarBack()}
${esc(d.error || '视频不存在')}
`; bindBack(view); return; } const v = d.video, analysis = d.analysis, parse = d.parse || {}; - const infoGrid = (label, val, wide, raw) => `
${label}
${raw ? val : esc(fmt(val))}
`; + const infoGrid = (label, val, wide, raw, cls, title) => `
${label}
${raw ? val : esc(fmt(val))}
`; const analysisToMd = (obj) => Object.entries(obj || {}).map(([k, val]) => `# ${k}\n\n${val}`).join('\n\n---\n\n'); function load() { @@ -552,9 +551,11 @@ async function renderVideoDetail(view, id) {
视频信息
-
${infoGrid('账号', v.account_name)}${infoGrid('视频ID', v.aweme_id)}${infoGrid('时长', fmtDuration(v.duration))}${infoGrid('发布时间', v.publish_time)}
-
${infoGrid('点赞', fmtWan(v.like_display || v.like_count))}${infoGrid('评论', fmtWan(v.comment_count))}${infoGrid('分享', fmtWan(v.share_count))}${infoGrid('收藏', fmtWan(v.collect_count))}
-
${infoGrid('标签', `${v.tags ? esc(v.tags) : '无'} ${viewLink}`, true, true)}
+
+ ${infoGrid('账号', v.account_name)}${infoGrid('视频ID', v.aweme_id, false, false, 'vid-limited', v.aweme_id)}${infoGrid('时长', fmtDuration(v.duration))}${infoGrid('发布时间', v.publish_time)} + ${infoGrid('点赞', fmtWan(v.like_display || v.like_count))}${infoGrid('评论', fmtWan(v.comment_count))}${infoGrid('分享', fmtWan(v.share_count))}${infoGrid('收藏', fmtWan(v.collect_count))} + ${infoGrid('标签', `${v.tags ? esc(v.tags) : '无'} ${viewLink}`, true, true)} +
${tabBtns}
${body} diff --git a/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css b/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css index d08ff18..135d90f 100644 --- a/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css +++ b/project/短视频脚本创作/V1.0/mcn-work-shop/public/style.css @@ -379,9 +379,13 @@ td.td-title { max-width: 240px; } .info-label { font-size: 14px; color: var(--dim); } .info-value { font-size: 15px; color: var(--text); word-break: break-word; white-space: pre-wrap; line-height: 1.6; } /* 视频详情信息卡:标题独立一行(大字号),下方按行分组 */ -/* 视频详情第2/3行:四字段等宽 grid(字段间隔统一用之前的 18px,避免 flex 自适应参差) */ -.video-info .info-row { display: grid; grid-template-columns: repeat(4, minmax(0, 1fr)); gap: 10px 18px; } -.video-info .info-row .info-item { min-width: 0; } +/* 视频详情字段对齐(09-02 终版):8 字段同处一个 grid 容器(2 行 × 4 列),列轨道跨行共享——第 i 列严格对齐(分享↔时长、收藏↔发布时间);列宽贴合内容(min-content~max-content,非均分);横向间隔 80px 与账号信息一致。之前用两个独立 .info-row 容器导致跨行不对齐 */ +.video-info .video-grid { display: grid; grid-template-columns: repeat(4, minmax(min-content, max-content)); gap: 10px 80px; justify-content: start; } +.video-info .video-grid .info-item { min-width: 0; } +.video-info .video-grid .info-item.info-wide { grid-column: 1 / -1; } +/* 视频ID 限宽(09-02):长数字不把第一行撑爆,多余省略号 + hover 提示全量 */ +.video-info .info-item.vid-limited { max-width: 220px; } +.video-info .info-item.vid-limited .info-value { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } /* 长文本默认最多 10 行,可点击展开/收起(账号定位等) */ /* 09-01 改用 block+max-height 裁剪:display:-webkit-box 下长句不折行,内容宽被撑到 1896px 导致 main 横向滚动条 */ .clamp-text { display: block; max-height: 240px; overflow: hidden; word-break: break-word; overflow-wrap: anywhere; } /* 240px=10行×15px×1.6 */