浅念
2a489aa16a
docs: UI-02 acceptance record and visual verification
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 23:10:58 +08:00
浅念
160e7e5f1a
feat(viewer): UI-02 heart particles, interaction notices, share and download
...
Task 5 (V05 §5.4/§6): decorative heart particles on the lightbox stage
(local like clicks + others' WS bursts for the current photo), compact
cross-photo interaction notices above the action bar (click to locate),
navigator.share-first share orchestration with clipboard fallback for
album and photo deep links (lang param), and download failures attributed
to the photo that triggered them. Self-echo dedup stays in the realtime
router (own batches never produce particles/notices); particles cap at
12 with visibility/unmount cleanup and are suppressed under reduced
motion; notices cap at 3 with ~4s auto-dismiss and throttled SR summary.
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 22:20:19 +08:00
浅念
89f45896b7
feat(viewer): UI-02 dark immersive photo viewer with thumbnail strip
...
V04 (方案 §5):PhotoLightbox 模板重构为深色沉浸布局——顶栏(关闭/位置
计数/⋯ 减少动画菜单)+ 舞台(contain 大图 + 边缘 ‹› + 粒子/互动占位层)+
缩略图条 + 底部操作栏;业务逻辑(序列导航/缩放手势/签名刷新/下载/点赞)
原样保留。
- 新增 viewer/position.ts:§5.2 位置计数真实回退(solo→照片预览;无
total 未到末页→已加载 N 张;全部加载→i/n;不伪造总数)
- 新增 ThumbnailStrip:当前 ±3 窗口(≤7 项)、主色 2px 描边、thumb 变体、
滚入可见(遵守减少动画)、点击选择不新增历史
- 新增 PhotoActionBar:Like(coral 心形+计数+连续点赞提示)/Share/
Download 均分大触控;下载关闭整项移除;原图二级菜单(▾ + 长按)
- 模态可用性:body 滚动锁、焦点进入/困住/归还触发元素、Esc 先收菜单、
菜单内左右键不抢占、dvh + 安全区、chrome 区手势/滚轮隔离
- 分享 V04 最小实现(复制当前深链,support/clipboard 从 AlbumHeader
提取共用);navigator.share 编排属 Task 5
- zh-CN/en 新词条;vitest 新增 23 例(位置回退/缩略图/操作栏/焦点/滚锁)
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 21:58:09 +08:00
浅念
3d89fcdb99
feat(viewer): UI-02 responsive waterfall with like badges
...
Task 3 (V03 瀑布流重排, 方案 §4.3):
- responsiveColumns 断点:<600px 双列 gap4 / 600-959px 三列 gap6 / ≥960px 四列
gap8;PhotoFeed 经既有 ResizeObserver 容器测量整体重排
- 照片卡片去卡片化:图片边到边 + 4px 圆角,无白色厚包裹/无常驻文件名;
右下角点赞角标(半透明暗底渐变、白心 16px、12px tabular-nums 计数),
@click.stop 独立交互不冒泡开大图;计数经 Intl 分组
- 新照片提示改白色 pill(viewer.newPhotos,接口仅 hasNew 不伪造数量);
refreshFromHint 不再强制回顶(用户浏览位置保持)
- 首屏骨架按 skeletonSources 竖/横/方比例走真实瀑布流放置(非均匀网格);
宽高缺失/NaN/Infinity 回退 4:3 安全比例(无 NaN/无限高度)
- 保留懒加载 IO、游标分页、photoId 稳定键、翻页失败底部重试与已到底文案
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 21:34:56 +08:00
浅念
9c3915b387
feat(viewer): UI-02 visual foundation, top bar and album info
...
V01+V02 (Task 2 of the UI-02 viewer redesign plan):
- viewer-tokens.css: UI-02 §3.1 design tokens scoped to .viewer-shell
(never :root), so embed routes consume none of them
- App.vue: normal routes get a sticky white top bar (PhotoLive brand +
compact locale switcher + lightweight "..." menu holding the
reduce-motion toggle); global 16px content padding removed so banner
and photos run edge-to-edge; embed shell branch untouched
- LocaleSwitcher: compact "EN/中文 + caret" trigger with a proper menu
(current language checked), 44px touch target, Escape/outside close
- AlbumHeader (new): banner (2.4:1 mobile, 320px desktop cap,
object-fit contain, collapses on missing/failed), 2-line expandable
title + share button (clipboard copy with selectable-text fallback),
3-line expandable pre-line description, meta row (Intl-formatted
view count with em-dash fallback for unknown, album-timezone dates
with full range on title, venue), Latest/Popular 44px tabs and
category pills from the API with horizontal scroll + overflow fade
- ActivityPage: delegates the info area to AlbumHeader; sequence
reset/sort-lock/category-by-id semantics unchanged; desktop content
capped at 1120px
- i18n: viewer.* strings (brand/share/views/readMore/latest/popular/
allCategories/noPhotos/newPhotos/reduceMotion/settings) in zh-CN+en
- tests: viewer-shell (token source + normal/embed isolation) and
album-header (8 cases) specs; activity/i18n specs updated for the
menu switcher and the em-dash unknown view count; e2e selectors
refreshed
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 21:22:36 +08:00
浅念
ddf26a177f
feat(embed,viewer): UI-01 complete carousel, loader config and accessibility
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-21 18:55:15 +08:00
浅念
7385785504
feat(i18n): T15 full-stack language audit with coverage assertions
...
- viewer/admin 错误码→文案映射按契约 ErrorCodes 枚举全集登记(机器守护
spec 遍历枚举断言无缺漏 + 映射键在 zh/en 词条均存在 + locale 键集
递归相等);mobile 镜像补齐 DOWNLOAD_DISABLED(P4a 增补漏登)
- 漏译修复:viewer.photoRemoved en 侧缺失(审计测试捕获);
DOWNLOAD_DISABLED 观众侧专属文案 + useDownload 专属提示接线;
admin apiMsg 增 RATE_LIMITED/ALBUM_NOT_PUBLIC/DOWNLOAD_DISABLED/
SYNC_AUTH_FAILED 四码词条
- 运行时抽查:admin 主旅程 en 版 e2e(登录→相册管理全英文 + 偏好
持久);embed iframe lang=en 渲染与深链跟随 e2e(zh 侧既有用例覆盖)
- contracts 新增 ./errors 浏览器安全子路径(errors.ts 纯 TS);
admin 增 @photo/contracts devDep(仅测试消费)
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 23:44:27 +08:00
浅念
8f2c21d25a
feat(viewer): embed carousel iframe with visibility pause
...
P4b Task 4:宿主 loader 以 iframe 载入的轮播页(/embed/:slotToken,裸布局)。
- src/embed/feed.ts:纯函数集——A22 旧响应裁决(dispatch seq + configVersion
双闸:过期/回看丢弃、等值差分合并、升值全量重置)、照片队列差分(新照片
追加队尾、缺席淘汰、thumb 签名刷新)、interval/高度夹取、轮询退避;
- src/embed/useEmbedFeed.ts:取数编排(If-None-Match → 304 零处理)、轮询
(查询 interval 覆盖 → EMBED_POLL_INTERVAL_MS,连续 5 失败 ×5 退避,成功
归零;404/410 静默 collapse 隐藏自身)、A23 不可见暂停(document 隐藏 +
IntersectionObserver 滚出视口 + 本地收起;恢复立即校准)、轮播 intervalMs
切换;__embedPollMs e2e 注入点;
- src/embed/postMessage.ts:上行 resize/collapse(referrer origin 收敛,缺失
退 '*'——载荷无敏感数据,宿主侧 loader source+origin 双校验)、下行 expand
(event.source === window.parent 白名单);
- EmbedPage.vue:交叉淡入淡出(仅 thumb 小图省流量)、点击 window.open 单图
深链(noopener)、计数圆点、「照片即将上线」空态卡、×收起;lang 查询
(auto→相册 defaultLocale)驱动嵌入词条子集;
- api client:fetchPublicEmbedSlot(304/ETag 分支,不走 apiRequest);
- e2e:宿主夹具页 public/__test__/embed-host.html(仅测试资产)+ embed.spec 7
用例(轮换/新标签深链/收起记忆/窄屏 sm/轮询增量/换绑重置/空态/幽灵槽);
helpers 增展示位 admin API 种子 + flushOutbox;
- vitest 18 用例(裁决矩阵/队列差分/夹取/退避/304/可见性状态机)。
配套修正:api CORS exposedHeaders ETag(跨源浏览器可读,304 语义成立);
contracts 新增浏览器安全子路径 ./constants(根出口 re-export internal-sync
的 node:crypto 会打进浏览器包);eslint ignore embed-loader 产物拷贝
(Task 3 起 viewer lint 红,先修)。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 23:02:07 +08:00
浅念
ddb4c8175f
feat(embed): versioned loader with postMessage whitelist
...
packages/embed-loader:手写零依赖加载器(esbuild bundle+minify→dist/v1/loader.js,
IIFE target es2018 4.7KB)。自发现 EMBED_ORIGIN(currentScript 兜底扫描)、
data-slot 必填缺失静默 no-op、__photoliveEmbedV1 标记去重+同槽去重、
窄屏 <480px 强制 sm+默认收起、postMessage 双向白名单(source+origin 严格
校验,resize 夹取 60-640)、sessionStorage 收起记忆、零全局零 console
全静默。sync 脚本携 sha256 校验拷贝至 viewer publicDir(两份产物入库);
src/params.js 为 loader/单测共用参数源(11 vitest 用例)。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 22:31:40 +08:00
浅念
2bb6f291d3
feat(ov,viewer): two-tier download with wechat guidance
...
P4a Task 4(T12 收口):ov 域公开照片下载端点 + viewer 下载 UI。
- interactions 增 downloads:GET /public/albums/{token}/photos/{photoId}
/download?variant=browse|original。两级开关(A18):browse 需
downloadEnabled;original 需叠加 originalDownloadEnabled(级联,browse 关
则两者均 403 DOWNLOAD_DISABLED)。水印相册 browse 现签 watermark pattern
key(browse-key 解析抽取 shared/ov-browse-key 与 public-ov 复用);原图
现签 originalObjectKey(SIGNED_URL_TTL)。来源 IP 固定窗限速
(DOWNLOAD_WINDOW_REQUESTS/DOWNLOAD_WINDOW_SECONDS,env 可调);零浏览
计数写入(排除规则)。STORAGE_PORT_OV 经新 StorageOvModule 与 public-ov
共享同一实例。
- 契约微调登记:PublicAlbum 增 readOnly 必返 downloadEnabled/
originalDownloadEnabled(cn+ov 同形返回;public e2e 精确键断言同步)。
- viewer:lightbox 下载入口随开关渲染(无/单按钮/分体菜单两选项)、过期
凭证静默重取一次、微信内置浏览器(MicroMessenger UA)出保存图片指引
浮层(复制深链 + 反馈)替代直接下载;i18n zh/en 词条。
- tests:api e2e +7(开关矩阵 + URL/字节级断言、水印键一致、隐藏 404、
限速 429、零计数行);viewer vitest +9(UA/矩阵/菜单/重取/失败/指引/
复制);viewer Playwright +3(入口矩阵、微信 UA 浮层、真实端点 200 +
签名 URL 模式)。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-20 18:32:34 +08:00
浅念
cdead49838
feat(viewer): likes, bubbles, visit reporting and hot sorting
...
Task 10 viewer 互动化:
- 点赞:useLikes 批次状态机(500ms 防抖 / 5 次满额即冲 / 失败同 batchId
退避重试 3 次后回滚丢弃),乐观计数 + 服务端 total 权威校准
(countVersion 单调),大图与格子双入口;
- WS 实时:RealtimeClient 纯状态机(半频心跳、指数退避重连、三败降级、
重连成功 resync 第 1 页重取),消息路由器做 likes.burst 自身过滤
(originBatchIds 求交)、隐藏页暂停与减少动画下仅校准不出气泡;
photo.hidden/deleted 即时移出序列并回退邻居大图;降级后 8s 轮询接管;
- 浏览上报:visitorId 跨标签 BroadcastChannel first-writer-wins 协调,
可见时上报、requestId 持久幂等、网络失败指数重试;
- 排序:最新/热门 chips(热门走 ov 冷快照),大图打开期间禁用切换;
顶栏「减少动画」偏好开关;
- 契约包新增 ./realtime 子路径出口(浏览器面不可引桶出口——其连带
internal-sync 的 node:crypto 会被 Vite 外置化致整页崩溃);
- e2e 注入钩子 __viewerWsUrl/__viewerWsRetryBaseMs/__viewerRealtimePollMs/
__viewerBubbleMs 与 .activity[data-connection] 连接态锚。
vitest:viewer 105(新增 36:likes 批次 8、visitor 协调 5、上报门控 4、
路由 8、WS 状态机 7、排序 4);Playwright:viewer 21(新增 likes.spec 7、
journey 补互动腿断言)。
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 19:52:27 +08:00
浅念
0fcf18cc5c
feat(ov): public read api on replicas with hot sorting
...
P2 Task 6(ov 公开读 API + viewer 切换):
- ov 装配 PublicOvModule(仅 app-ov.module):/public/albums/{token}、
/photos、/photos/{id} 三端点同契约读 ov 副本——translations/categories
自 JSON 列解析(防御性过滤),banner 经 ov 侧 STORAGE_PORT_OV 预签名
(同本地 MinIO;只读属约定,真实 OSS 只读凭证 P5),viewCount 取
album_stats 权威累计(无行默认 0;计数端点 Task 8 接线)
- 排序:latest=发布时间 keyset(shared/cursor.ts 同 cn);hot=likeTotal
desc + photoId desc 决胜,顺序按 `${albumId}|${category}` 短期快照
(内存 TTL 默认 30s,HOT_SORT_TTL_MS 惰性读取)——计数逐请求现读、
顺序冻结窗口内一致,偏移游标(base64url 整数)在快照内严格稳定;
非法 sort 400 VALIDATION_FAILED(cn 不感知该参数)
- 契约:viewCount 放宽为 integer|null(cn null、ov 权威值);
listPublicPhotos 增 sort 枚举(latest|hot,缺省 latest);
PhotoPublishedPayload 增 categoryId——补上 Tasks 3/4 管线缺口
(payloads/publish.service/sync-inbox 投影 + 关联测试随改)
- e2e(services/api/test/public-ov.e2e-spec.ts,7 用例):双入口同进程
起 cn+ov,cn API 种子→真实发送管线投递;断言相册/列表/单图与 cn
深等(仅签名 URL 剔除)、banner 真字节、参数矩阵 400/404、hot 排序
TTL 内快照(计数新/顺序旧)+TTL 过期翻转+偏移翻页、隐藏/下线/紧急
停用逐态撤下
- viewer 切 ov:API 基址 VITE_API_BASE(缺省 http://127.0.0.1:8081 ,
空串=同源网关;Vite 只读 apps/viewer/.env*,根 .env 对前端无效),
移除 /v1 dev 代理(CORS_ORIGINS 增 127.0.0.1:5175 直连两形);
playwright webServer 增 dev:ov;e2e 夹具 flushSync(albumId)=cn
bootstrap 快照 + deliverPendingOnce(sender 新导出)经真实管线投影
种子,deleteSeededPhoto 改软删让墓碑可同步;14 个 Playwright 全绿
(journey 管理/staffB 腿仍走 cn 8080)
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 17:05:56 +08:00
浅念
40f8f37ca4
test(e2e): p1b cross-frontend playwright journey
...
- viewer playwright.config: third webServer boots admin dev (5176,
reuseExisting) so one config serves both frontends
- journey.spec.ts: admin UI (login, zh+en album, real-jpeg banner via
file chooser, category, staffB invite by account search, logout) ->
staffB API uploads x3 (init/presign-PUT/complete) -> worker
PublishService.processPhoto x3 -> public list verify -> viewer UI
(banner bytes, zh->en switch, waterfall 3, chip filter 1, lightbox
zoom/close, deep link) -> admin hide -> viewer 2 + hidden deep-link
404 notice -> empty album state
- cleanup: journey album domain rows + staffB account via Prisma
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 12:54:18 +08:00
浅念
90fe64f637
feat(viewer): photo viewer with zoom, navigation and deep link
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 11:04:05 +08:00
浅念
d0b79dd0c9
feat(viewer): waterfall feed with lazy load, filter and new-photo hint
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 10:23:27 +08:00
浅念
adc45b1563
feat(viewer): album page with locale fallback and timezone display
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 09:52:22 +08:00
浅念
04b5641901
chore(viewer): move dev ports to 5175/5176 (user decision; avoids cms project conflict on 5173)
...
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 09:18:27 +08:00
浅念
f9cb72ed06
feat(viewer): vite vue3 scaffold with routing and i18n
...
- apps/viewer (@photo/viewer): Vite 5 + Vue 3.4 + TS 最小骨架(无 Pinia/UI 库)
- router: /a/:token 活动页 + /a/:token/p/:photoId 单图深链(复用活动页组件)
- i18n: vue-i18n 9,zh-CN/en 词条,localStorage(viewer.locale) 持久 +
navigator.language 检测兜底 zh-CN;顶栏语言切换组件;文案全量走 i18n
- api client: 类型化 fetch 封装(契约生成类型直连),错误信封 → ApiError,
错误码 → i18n key 映射表(src/api/errors.ts)
- scripts: dev/build/preview/test(vitest 10 例)/test:e2e(Playwright 冒烟,
webServer 自拉 dev)/lint/vue-tsc 全部接入 turbo
- root: +@playwright/test、+eslint-plugin-vue(flat config 收敛到 apps/**/*.vue)、
pnpm.onlyBuiltDependencies 放行 esbuild
Co-Authored-By: Claude Code <noreply@anthropic.com >
2026-09-18 09:12:45 +08:00