Thumbnail QA

自动检测并修复 Next.js 站点中图片的裁剪问题,优化 object-position 值。

已扫描
适合谁
前端开发人员、网站内容运营者
不适合谁
无 Next.js 项目经验者、不熟悉 Git 和本地开发环境者
国内可用性
需网络配置。可能需要网络配置或第三方服务可访问。
安装难度
新手友好(★☆☆)。基于终端操作、依赖、API Key 和本地环境要求的初步判断。

安装与下载

openclaw skills install @kaicianflone/thumbnail-qa

Skill 说明

命令、参数、文件名以原文为准

缩略图质量检查技能

概述

此技能会遍历 Next.js 站点的每一页,检测容器中裁剪不当的图片,基于焦点分析计算最优的 object-position 值,并应用细粒度的 CSS 修复——每个修复都附带前后截图和独立的原子提交。


配置

# 查找 browse 可执行文件
B=$(command -v browse 2>/dev/null || echo "$HOME/.claude/skills/gstack/browse/bin/browse")

# 验证 browse 是否可用
if [ ! -x "$B" ]; then
  echo "错误:在 $B 处未找到 browse 可执行文件"
  echo "请安装 gstack 或确保 browse 在 PATH 中。"
  exit 1
fi

检查工作区清洁性

运行 git status --porcelain。如果输出非空,向用户询问:

“您的工作区存在未提交的更改。在运行缩略图质量检查前,您希望:

  1. 现在提交更改
  2. 保存更改(git stash)
  3. 中止并手动处理

请选择一项?”

仅当工作区干净(或用户明确选择第 3 项并理解风险)后才继续。

检查开发服务器状态

# 检查开发服务器是否运行
curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 | grep -q "200\|301\|302" && echo "运行中" || echo "未运行"

若未运行,启动它:

npm run dev &
DEV_PID=$!
# 等待服务器就绪(最长 30 秒)
for i in $(seq 1 30); do
  curl -s -o /dev/null -w "%{http_code}" http://localhost:3000 | grep -q "200\|301\|302" && break
  sleep 1
done

配置视口和输出目录

# 设置视口
$B viewport 1280x800

# 创建输出目录
mkdir -p .gstack/thumbnail-qa/screenshots

# 确保 .gstack/ 被加入 .gitignore
if ! grep -q "^\.gstack/" .gitignore 2>/dev/null; then
  echo ".gstack/" >> .gitignore
  git add .gitignore
  git commit -m "chore: 将 .gstack/ 加入 .gitignore"
fi

第一阶段:构建图像注册表

步骤 1.1 — 搜索带有 fill 模式的 Next.js Image 组件

grep -rn "<Image" --include="*.tsx" --include="*.ts" --include="*.jsx" --include="*.js" . \
  | grep -v "node_modules" \
  | grep "fill"

对每个匹配到的文件,使用 Read 工具完整读取整个文件(而非仅 25 行窗口)。必须理解组件全部内容,才能正确解析条件类名和动态 src 值。

步骤 1.2 — 提取每张图片的元数据

对每个带有 fill 属性的 <Image,记录以下信息:

字段说明
id顺序编号(1, 2, 3, ...)
file_pathTSX 文件的绝对路径
line_number<Image 所在行号
page_routeURL 路径(例如 /, /about, /connect
srcsrc 属性的值(字符串或表达式)
className完整的 className 字符串或表达式
current_position提取的 object-position 类(如 object-topobject-[50%_25%]none
container_classes包裹 div 的类(特别是 overflow-hiddenaspect-*h-*w-*
conditional_key若 className 为条件类,则记录其依赖字段或表达式
notes任何动态或条件复杂性说明

步骤 1.3 — 过滤非候选图像

跳过满足以下任一条件的图像:

  • fill 属性
  • src 包含 "logo"(不区分大小写)
  • 容器 div 包含 rounded-full 类(用于头像/图标样式)
  • 图像使用 object-contain(有意留黑边)

步骤 1.4 — 处理动态值

动态 src(如 src={photo.src}src={item.image}):

  • 将数据源中的每个唯一值展开为独立注册条目
  • 读取数据文件以枚举所有实际值

条件类名(如 className={category.id === 'worship' ? 'object-top' : 'object-center'}):

  • conditional_key 记录为条件驱动字段(如 category.id
  • 每个分支作为单独子条目记录
  • 在第三阶段应用修复时,使用与该字段相同的键创建位置映射

步骤 1.5 — 输出注册表

打印所有候选图像的编号列表:

图像注册表(共 N 个候选)
================================
[1] src: /images/team/alice.jpg
    route: /about
    file: components/TeamSection.tsx:42
    当前位置: object-top
    容器: relative overflow-hidden h-64 w-full

[2] src: (动态) photo.src — 来自 data/photos.ts 的 6 个条目
    route: /gallery
    file: components/Gallery.tsx:18
    当前位置: object-center(静态)
    conditional_key: 无
    notes: 需分别展开 6 个 photo 条目
...

预期总数:全站约 15 个条目。


第二阶段:视觉分析

步骤 2.1 — 按页面路由分组

将注册表中所有条目按 page_route 分组,以减少浏览器导航次数(每页只访问一次)。

步骤 2.2 — 导航并截屏

对每页执行:

# 导航至页面
$B goto http://localhost:3000/PAGE_ROUTE

# 等待图片加载
sleep 1

# 截取父容器(overflow-hidden 容器)截图,而非 img 标签
$B screenshot ".gstack/thumbnail-qa/screenshots/IMAGE_ID-current.png" \
  --selector "div.relative.overflow-hidden:has(img[src*='FILENAME'])"

若选择器失败(动态 src、复杂 DOM),回退使用:

$B screenshot ".gstack/thumbnail-qa/screenshots/IMAGE_ID-current.png" \
  --selector "CLOSEST_IDENTIFIABLE_PARENT"

在分析备注中记录所用选择器。

步骤 2.3 — 分析原始图像

使用 Read 工具查看 public/ 目录中的原始图像:

Read: public/images/PATH_TO_IMAGE.jpg

这可提供未裁剪图像的完整视觉参考。

步骤 2.4 — 确定焦点区域

分析完整图像并进行分类:

图片类型焦点优先级规则
人物 / 人像面部完全可见,从额头到下巴。眼睛位于容器上三分之一区域。
群体 / 团队尽可能展示更多人脸。优先水平居中。避免裁切任何人。
建筑 / 建筑物展示完整结构。若可能,屋顶线和入口均应可见。
宗教 / 活动 / 事件保持主要动作或发言者在画面内。避免裁切手部或手势。
风景 / 场景主体居中;地平线位置根据天空与地面的视觉兴趣决定。

将焦点坐标记录为百分比形式:X_PERCENT% Y_PERCENT%(例如 50% 25%)。

步骤 2.5 — 评估当前裁剪

将截图中的裁剪效果与焦点规则进行对比,判断:

  • OK:当前裁剪保留了焦点且构图合理。人物照片中面部(从额头到下巴)完全在画面内。现有位置已达到可接受的效果。
  • 需要修复:焦点被裁切、部分遮挡或位置不佳。

请勿将图像标记为“需要修复”,如果当前位置已满足焦点规则。object-top 已正确显示面部,请保留原值。不要用可能更差的计算值替换已生效的值。目标是精心筛选的结果,而非统一的语法格式。

步骤 2.6 — 计算最优 object-position

**object-position 的工作原理:** object-position: X Y 表示将图像的 X% 位置对齐容器的 X% 位置,Y% 位置对齐容器的 Y% 位置。

  • object-top (= 50% 0%) 将图像顶部对齐容器顶部。适用于高比例人像,面部位于上半部分的情况。
  • object-center (= 50% 50%) 居中图像。当主体位于画面中央时适用。
  • object-bottom (= 50% 100%) 将图像底部对齐容器底部。

关键洞察: 对于高比例人像在短宽容器中的情况,容器默认会从图像中间裁剪出一条水平带。若要显示图像顶部附近的内容,应使用 object-top 或极低的 Y 值(0%-10%)。例如 object-[50%_20%] 并不表示“显示顶部 20%”——它会使视图向下偏移,这与希望显示人物顶部内容的目标相反。

经验法则:

  • 高比例人像中面部位于前 30% 区域 → 使用 object-topobject-[50%_5%]
  • 面部位于图像中心 → object-center 即可
  • 面部位于下半部分 → 使用较高的 Y 值(60%-80%)
  • 当标准值(object-topobject-center)可用时优先使用——仅在确实需要微调时才使用任意值

四舍五入至最接近的 5% 以获得更整洁的数值。

每张图片记录分析结果:

[1] alice.jpg — NEEDS_FIX
    图片类型:人像(高比例图像,面部靠近顶部)
    焦点位置:50% 20%(面部,眼睛靠近图像上部)
    当前设置:object-center(面部被裁切——容器显示图像中部)
    推荐设置:object-top
    原因:面部位于高比例人像的上半部分——object-top 将图像顶部对齐容器顶部,以确保面部可见

[2] group-photo.jpg — OK
    图片类型:群体
    焦点位置:50% 40%(人脸集中在中心区域)
    当前设置:object-top
    推荐设置:保持当前
    原因:object-top 已正确显示所有面部——不应替换已有效的工作值

第三阶段:应用修复

按顺序处理每个标记为“需要修复”的图像。

步骤 3.1 — 获取“修复前”截图

$B goto http://localhost:3000/PAGE_ROUTE
sleep 1
$B screenshot ".gstack/thumbnail-qa/screenshots/IMAGE_ID-before.png" \
  --selector "div.relative.overflow-hidden:has(img[src*='FILENAME'])"

步骤 3.2 — 应用 CSS 修复

根据注册表条目选择正确的编辑模式:

模式 A — 静态 className 字符串

查找现有的 object-* 类名(或插入位置),替换或添加:

// 修复前
className="relative overflow-hidden h-64 object-center"
// 修复后
className="relative overflow-hidden h-64 object-[50%_25%]"

使用编辑工具操作。不得修改 className 中的其他部分。

模式 B — 条件三元表达式(基于字段)

将三元表达式替换为基于相同字段的定位映射:

// 修复前(基于 category.id)
className={`... ${category.id === 'worship' ? 'object-top' : 'object-center'}`}

// 修复后(定位映射,相同键:category.id)
const positionMap: Record<string, string> = {
  worship: 'object-[50%_20%]',
  music: 'object-[50%_35%]',
  community: 'object-[50%_45%]',
}
// 在 JSX 中:
className={`... ${positionMap[category.id] ?? 'object-center'}`}

若条件基于 photo.caption 或类似字符串内容而非 ID,建议在数据对象中新增 position 字段,并从中读取值。

**模式 C — 已存在的 object-[X_Y] 自定义值**

仅替换自定义值部分:

// 修复前
object-[50%_50%]
// 修复后
object-[50%_25%]

步骤 3.3 — 等待热重载

sleep 2

步骤 3.4 — 获取“修复后”截图

$B screenshot ".gstack/thumbnail-qa/screenshots/IMAGE_ID-after.png" \
  --selector "div.relative.overflow-hidden:has(img[src*='FILENAME'])"

步骤 3.5 — 验证修复是否符合焦点规则

将修复前和修复后的截图并列显示。然后重新应用步骤 2.4 的焦点规则,针对修复后的截图进行检查:

  • 人物照片:面部是否完全可见(从额头到下巴)?眼睛是否在画面内?
  • 群体照片:相比之前,是否显示了更多人脸,而非更少?
  • 建筑类照片:结构是否比之前更完整

如果修复后的截图未能通过修复前已通过的焦点规则,则说明修复使情况变得更糟。 这是最常见的失败情形——计算出的位置看似合理,但实际裁剪效果反而更差。

技能:缩略图质量检查(Thumbnail QA)

版本:1.0.0

分块:3/3

步骤 3.5 — 修复与判断

  1. 使用编辑功能撤销文件更改(恢复原始的 className
  2. 将该条目标记为 MANUAL_REVIEW,并添加备注:computed position {X} degraded framing vs original {Y}
  3. 不要提交

若修复后的截图存在歧义(改进不明显,无法判断是否更优):应标记为 MANUAL_REVIEW 而非直接提交。优先保留原始位置。

仅当修复后的截图明显优于原图时,方可提交。


步骤 3.6 — 原子提交

确认修复有效后:

git add PATH/TO/CHANGED_FILE.tsx
git commit -m "style: reposition FILENAME thumbnail — REASON"

# 示例:
# git commit -m "style: reposition alice.jpg thumbnail — pull frame up to keep face centered"

每张图片一个提交。例外情况:若多个图片共享同一文件中的同一条件逻辑块(例如一个位置映射覆盖组件中 6 张图片),可将它们一起提交,并在提交信息中列出所有受影响的图片。


阶段 4:总结报告

步骤 4.1 — 收集结果

汇总每张图片的处理结果:

  • OK:无需修复
  • REPOSITIONED:已应用修复并提交
  • SKIPPED:已过滤(如 logo、icon、object-contain 类型)
  • MANUAL_REVIEW:尝试修复但结果不佳或存在歧义

步骤 4.2 — 输出结构化报告

THUMBNAIL QA REPORT
===================
Date:      YYYY-MM-DD
Viewport:  1280x800
Pages checked: N

RESULTS SUMMARY
---------------
Total candidates checked:  N
  OK (no fix needed):      N
  Repositioned:            N
  Skipped (filtered):      N
  Manual review needed:    N

REPOSITIONED IMAGES
-------------------
| # | Image | Page | Before Class | After Class | Reason |
|---|-------|------|--------------|-------------|--------|
| 1 | alice.jpg | /about | object-top | object-[50%_15%] | Face centered with forehead visible |
...

OK IMAGES (no change)
---------------------
| # | Image | Page | Position | Notes |
...

SKIPPED IMAGES
--------------
| # | Image | Reason |
...

MANUAL REVIEW NEEDED
--------------------
| # | Image | Page | Attempted Fix | Issue |
...

COMMITS
-------
  abc1234  style: reposition alice.jpg thumbnail — ...
  def5678  style: reposition team-photo.jpg thumbnail — ...

SCREENSHOTS
-----------
  .gstack/thumbnail-qa/screenshots/

步骤 4.3 — 保存报告到磁盘

REPORT_DATE=$(date +%Y-%m-%d)
REPORT_PATH=".gstack/thumbnail-qa/report-${REPORT_DATE}.md"
# 将上述结构化报告写入 $REPORT_PATH

步骤 4.4 — 最终提示

"缩略图质量检查已完成。共对 N 张图片进行重新定位,覆盖 N 个页面。报告已保存至 .gstack/thumbnail-qa/report-YYYY-MM-DD.md。下次上传图片后,请再次运行 /thumbnail-qa。"

K
@kaicianflone

已收录 2 个 Skill

相关推荐