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>
This commit is contained in:
浅念
2026-09-21 21:22:36 +08:00
parent 2cbd167cc0
commit 9c3915b387
13 changed files with 1333 additions and 258 deletions
+4 -3
View File
@@ -251,9 +251,10 @@ test('reduced motion suppresses bubbles while counts still calibrate', async ({
});
await expect(page.locator('.activity[data-connection="open"]')).toBeVisible();
// 顶栏开关减少动画(localStorage 偏好持久)。
await page.locator('.motion-toggle').click();
await expect(page.locator('.motion-toggle')).toHaveAttribute('aria-pressed', 'true');
// 顶栏「⋯」菜单内开关减少动画(UI-02 V01;localStorage 偏好持久)。
await page.locator('.topbar .icon-btn').click();
await page.locator('.prefs-item').click();
await expect(page.locator('.prefs-item')).toHaveAttribute('aria-checked', 'true');
// 他者点赞:无气泡(动画停用),计数照常被 burst 校准。
await apiLike(token, photoIds[0]!, 'e2e-motion-other', 1);
+3 -2
View File
@@ -4,6 +4,7 @@ import { expect, test } from '@playwright/test';
// 真实观众旅程(活动页/瀑布流/大图深链)由 Task 6-8/11 补充。
test('dev server serves viewer root with top bar and locale switcher', async ({ page }) => {
await page.goto('/');
await expect(page.locator('.brand')).toHaveText(/现场相册|Live Gallery/);
await expect(page.locator('button.locale-switcher')).toBeVisible();
// UI-02 V01:品牌为英文 PhotoLive(双语一致),语言切换为紧凑控件。
await expect(page.locator('.brand-name')).toHaveText('PhotoLive');
await expect(page.locator('.locale-trigger')).toBeVisible();
});