前言
最近梳理了 CNB 平台上 Afilmory 项目仓库的 .specstory/history/ 目录——里面完整记录了从 2025 年 5 月 24 日到 7 月 21 日期间,共计 213 次 opencode AI 编程会话的全文转录。
Afilmory 是一个面向摄影师的现代化照片画廊平台(SaaS + 自部署),核心卖点是高性能 WebGL 渲染器、响应式瀑布流布局、增量同步和国际化支持。截至 2026 年,已有多位摄影师在使用,包括 afilmory.innei.in 等公开案例。
这篇文章是对这 213 次会话的宏观总结——看看一个 1.2 万行级别的项目是如何通过 AI 会话逐步演进的。
第一阶段:项目启动与核心基建(May 24-25, ~40 sessions)
主题:从零开始搭建照片展示网站
Session 001:项目初始化
第一场会话的 prompt 极其简单:
“我要做一个网站。用来展示我用相机拍摄的照片。我希望整个页面是黑色背景的。整体是一个瀑布流的形态。现代化美学风格。图片托管在 Flickr 中,build 阶段生成 manifest,manifest 中需要有原图的链接、blurhash string、缩略图的链接。”
AI 助手在已有 React + Vite + TypeScript + Tailwind 项目骨架的基础上,完成了:
- 瀑布流布局实现
- 黑色主题设计系统
- Flickr API 构建脚本(build 阶段拉取照片列表)
build-manifest.ts——核心构建管道,生成包含原图 URL、blurhash、缩略图的 manifest JSON- Sharp 缩略图生成
PhotoMasonryItem — 图片懒加载(Session 7)
单条图片项的详细实现:读取 manifest、计算宽高比、懒加载、加载前显示 blurhash、加载后 fade 过渡到缩略图。
动态 OG 图片生成(Session ~14)
每次 build 自动生成动态 OG image,在 HTML 中引用 metadata og 标签——这是一项后来被广泛使用的功能。
启动屏幕(Session ~18)
在应用完全加载前显示 splash screen,提升首屏感知体验。
第二阶段:图像查看器革命(May 26-27, ~60 sessions)
这是整个项目中 会话密度最高 的阶段。近 60 场会话围绕一个核心问题:如何在浏览器中流畅渲染 40MP 级别的照片。
HEIC/HEIF 地狱(Session ~10-15)
苹果生态的 HEIC 格式给构建管道带来了巨大挑战。Sharp 库对 HEIC 的支持有限,团队尝试了:
- Sharp 原生解析 → 报错
heic-convert库 → 可行但慢- 最终采用
heic-convert+ 并行处理
这个阶段的试错过程非常典型——heic-convert 的 API 文档不完善,团队通过阅读源码、在浏览器端测试、最终才确定了正确的调用方式。
WebGL 图像查看器(Session ~15-40,最关键的部分)
最初的图片查看器使用 react-zoom-pan-pinch(TransformWrapper),但 40MP 图片的缩放和拖拽存在严重的性能问题。团队决定编写自定义 WebGL 图像查看器。
关键里程碑:
| Session | 内容 |
|---|---|
| 替换 TransformWrapper 为 WebGLImageViewer | 核心引擎搭建 |
| Chrome Shader 黑色边框修复 | 跨浏览器兼容 |
| Blurhash 无缝过渡 | 加载体验优化 |
| 双击缩放中心设置 | 交互细节 |
| iPhone Safari 黑屏问题 | iOS 兼容修复 |
| 移除 Swiper,使用 WebGL 实现滑动 | 彻底甩开第三方依赖 |
WebGLImageViewer 的实现包括:
- WebGLImageViewerEngine 核心类:使用 WebGL2 纹理渲染,支持缩放、平移、双击
- Worker 化计算:将图像解码和纹理处理迁移到 Web Worker
- 双缓冲渲染:低分辨率 blurhash → 高分辨率原图的无缝过渡
- 节流/防抖:在连续手势中控制渲染频率
移除 Swiper(Session ~40)
一个激进的决策:移除所有 Swiper 依赖,完全使用 WebGL 实现图片查看器的滑动切换。这消除了 Swiper 与 WebGL 手势之间的冲突,但工作量巨大——需要实现方向锁定、惯性滑动、边缘回弹等机制。
第三阶段:Live Photos 与视频支持(June 22, ~5 sessions)
主题:让画廊支持 Apple Live Photos
Live Photos 的核心是 .mov(视频)+ .heic(图片)配对。需要实现:
- 使用
mov-demuxer提取视频的matrix变换信息(旋转、翻转) - 使用
mp4box完成视频转封装(transmux):将 MOV 容器转为 MP4 格式 - 视频的画面缩放和位置必须与 DOM 图片完全对齐
最终采用 transmux 方案:mov-demuxer 解封装 → mp4box 重封装,期间保存并传递视频的 transform matrix。
第四阶段:图像预处理策略模式重构(June 25, ~3 sessions)
主题:从硬编码到可扩展
之前的 processImageBlob 方法硬编码了 HEIC 格式转换。为了支持更多格式(WebP、AVIF 等),重构为策略模式:
ImageConverterStrategy (interface)
├── HEICStrategy → heic-convert
├── WebPStrategy → sharp (native)
├── AVIFStrategy → sharp (native)
└── (任意扩展)
ImageConverterManager
├── registerStrategy()
├── detectStrategy()
└── execute()
每个策略独立实现转换逻辑,管理器负责格式检测和策略路由。这是项目中为数不多的有意识架构决策的会话之一。
第五阶段:互动功能与数据库(June 26-28, ~10 sessions)
Reaction Button(Session ~150)
实现了一个 FAB 风格的 Reaction 按钮——点击后迸发出多个 emoji 到四周。使用了 Framer Motion 的 spring 动画,与项目现有的磨砂玻璃(backdrop-blur)UI 风格保持一致。
Neon Database 迁移(Session ~155)
将 PostgreSQL 数据库从之前的选择切换到 Neon(Serverless PostgreSQL)。主要改动:
- 连接方式从
pg切换到@neondatabase/serverless的neonHTTP 驱动 - 实现了原子性 upsert 操作处理并发点赞
- 使用 Drizzle ORM 做类型安全的查询
第六阶段:文档站点建设(July 14-21, ~30 sessions)
主题:为 builder 包编写完整的文档站点
这是项目的第二个高强度开发期。团队用约定式路由 + MDX 构建了一个完整的文档站点(apps/docs)。
Builder Package 文档(Session ~180)
builder 包是整个项目的构建核心——它负责图像处理、缩略图生成、manifest 管理。团队编写了多篇 MDX 文档来描述:
- Builder 架构总览
- 存储提供者(S3/Flickr/Local)接口
- 插件系统
- 二次开发指南
约定式路由生成器(Session ~190)
编写了一个 Vite 插件,扫描 contents/ 目录自动生成 routes.ts,使用 index 作为首页。支持嵌套目录结构。
主题系统:从 Apple UIKit 到 @pastel/chromatik(Session ~210)
最初使用 Apple UIKit 语义颜色系统(iOS 风格的 text-primary、fill-secondary 等)。在意识到开源社区更熟悉 Tailwind 生态后,果断迁移到 @pastel-palette/tailwindcss。
这场会话本身就是一个有趣的故事——从"设计一套 light 和 dark 颜色主题"开始,经历了 Apple 风格的全面应用,到最后"不用苹果得了,切换成 @pastel",体现了 AI 辅助开发中设计决策的快速迭代。
数据洞察
会话时间分布
| 时段 | 会话数 | 密度 |
|---|---|---|
| May 24-25(基建期) | ~40 | 高 |
| May 26-27(WebGL 期) | ~60 | 最高 |
| May 28-Jun 21(平稳期) | ~30 | 低 |
| Jun 22(Live Photos) | ~5 | 中 |
| Jun 25-28(重构+互动) | ~15 | 中 |
| Jul 14-21(文档站点) | ~60 | 很高 |
会话主题分布(粗略)
- 前端组件开发 ~35%
- 性能优化与 WebGL ~25%
- 构建管道与图像处理 ~15%
- UI/UX 设计 ~10%
- 后端/数据库 ~8%
- 文档与配置 ~7%
失败模式
从会话记录中可以观察到几种典型的 AI 编程失败模式:
- API 猜测错误:heic-convert 的导入方式、Neon 的连接字符串格式——AI 经常根据记忆猜测 API,但实际 API 已更新
- 类型链断裂:重构一个函数后,关联的类型定义没有同步更新,导致连锁的 TypeScript 错误
- “继续迭代"死循环:AI 在解决一个错误后引入新错误,反复 5-6 轮无法收敛,最终人类介入”@agent Pause"
- 上下文遗忘:长会话中 AI 忘记之前的约束条件,生成与已存在功能冲突的代码
七个经验教训
WebGL 是性能瓶颈的最后解法:能用 CSS/Canvas 解决的问题,不要上 WebGL。一旦上了,就要做好投入大量会话的心理准备。
策略模式值得早期引入:HEIC 处理从硬编码到策略模式的迁移,花了 3 场会话。如果一开始就用策略模式,后面添加 WebP/AVIF 支持只需要 1 场。
AI 适合"快写快改",不适合"一次成型":所有成功的 WebGL 实现都不是 AI 一次写对的,而是在 5-10 轮迭代中逐步修正边界条件。
数据库迁移风险极高:Neon 切换的 5 场会话中,有 3 场在解决连接问题和类型错误。AI 对 Serverless 数据库的认知存在盲区。
文档站点反而占用最多会话:意想不到的是,文档站点开发(~60 场)的会话数量超过了 WebGL 核心(~50 场)。这说明 AI 编程中"文档即代码"的写法虽然高效,但需要大量 UI 迭代。
混合语言 prompt 很有效:很多会话的 prompt 是中英混合的(“在这里写一个 contents 的约定式路由生成器插件”),AI 可以准确理解这种混合指令。
Pause 是最重要的指令:当 AI 进入"改错-引入新错"的死循环时,
@agent Pause比任何技术提示都有效。重启后的第一轮回复通常是高质量的。
结语
213 场 opencode 会话,从 2025 年 5 月到 7 月,完整记录了一个照片画廊从 idea 到生产级项目的全过程。这些转录文件不仅是代码变更的历史,更是人类与 AI 协作编程的原始档案。
最有意思的是:这些会话没有一个是"一次完成"的。每个功能都经历了多轮迭代——人类提出需求、AI 编码、人类测试反馈、AI 修复。这种 human-in-the-loop 的螺旋式开发,可能就是 AI 编程时代最真实的软件开发范式。
如果想了解 Afilmory 项目本身,可以访问 GitHub 仓库 或官方 文档站点。
本文基于 CNB Gallery workspace 中 .specstory/history/ 目录的 213 个 markdown 文件整理而成。

