feat: Actress Showcase mode + precise match hero card (v0.7.3)#25
feat: Actress Showcase mode + precise match hero card (v0.7.3)#25
Conversation
CHANGELOG.md 從 1270 行縮減至 340 行(-73%): 0.6.x~0.7.x 完整保留,0.1.x~0.5.x 壓縮為 5 段摘要。 完整歷史移至 CHANGELOG_ARCHIVE.md(962 行)。 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- ActressRepository.count_videos_for_actress() — json_each() exact match + json_valid() guard
- GET /api/actresses — list all favorites with video_count + created_at
- POST /api/actresses/{name}/rescrape — force re-fetch + DB upsert + photo refresh
- _actress_to_response() extended with video_count (default 0) + created_at
- 10 unit tests (list happy/empty/exact-match/dirty-data, rescrape happy/timeout/404/new-name)
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…lightbox - Module-level _actresses[] / _filteredActresses[] (non-reactive pattern) - 16 new Alpine state properties (showFavoriteActresses, sort/filter/lightbox state) - _setLightboxIndex() mutual exclusion: clears currentLightboxActress + resets _videoChipsExpanded - 6 core methods: toggleActressMode, loadActresses, applyActressFilterAndSort, sort/order/search - 5 lightbox methods: _setActressLightboxIndex, open/close/prev/next with GSAP generation guards - 6-key sort (video_count/name/created_at/age/height/cup) with null-last consistency - handleKeydown actress lightbox dispatch - saveState/restoreState for 3 actress keys (strict === true for mode) - 41 frontend guard tests (TestShowcaseActressState) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tate - Mode toggle button (bi-film/bi-person) with CD-8 disabled guard - Dual search inputs: x-show video/actress with shared clear button - Video toolbar controls conditional (x-show="!showFavoriteActresses") - Actress toolbar controls: 6-option sort dropdown + order toggle - x-if video/actress grid branches (CD-2: avoid Alpine tracking hidden arrays) - Actress grid: paginatedActresses x-for + actress-card + data-flip-id - Actress card: 3:4 portrait photo, no-photo placeholder, name + count badge - Loading spinner + empty state (actressCount === 0) - CSS .actress-card with design tokens + hover effects - 14 frontend guard tests (TestShowcaseActressTemplate) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Shared lightbox DOM: x-if actress/video content branches (CD-3) - 5-row actress metadata: cover, header (name+heart), core metadata (·-joined), aliases chips +N, info chips +N, URLs (http-only guard), action placeholders - Chips +N: _chipsLimit (desktop 10/mobile 6), _visibleAliases/_aliasesOverflow, _visibleInfoChips/_infoChipsOverflow, _allInfoChips merge - Video tag chips sync +N: _visibleVideoTags/_videoTagsOverflow - Nav arrows dispatch on showFavoriteActresses (not currentLightboxActress) - 9 new JS methods with null guards - CSS: actress-lightbox-meta, lb-chips-more (shared), cover placeholder - Fix lb-header test regression (precise class match, not substring) - 11 frontend guard tests (TestShowcaseActressLightbox) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- [+ 新增] DaisyUI popover: input + confirm + Enter shortcut + loading spinner - addFavoriteActress(): POST /favorite → 409 info/404 error/504 error/200 success toast push to _actresses + re-sort + close dropdown - rescrapeActress(): POST /rescrape → Object.assign in-place + photo cache-bust sync _actresses array, lightbox stays open - removeActress(): confirm → DELETE → splice _actresses + re-sort + close lightbox - encodeURIComponent for Japanese names in URL path - zh_TW.json: showcase.actress 14 keys + mode/search/sort/unit keys - 7 frontend guard tests (TestShowcaseActressCRUD) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… FLIP - animations.js: 4 selector changes (.av-card-preview → .av-card-preview, .actress-card) playEntry, capturePositions, playFlipReorder, playFlipFilter (playSettle unchanged) - toggleActressMode: $nextTick crossfade + conditional playEntry for cache-hit - loadActresses: $nextTick + rAF playEntry in success path - Sort FLIP works via existing _sortWithFlip (no change needed after selector fix) - Lightbox animations already wired in T4 (zero change) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- zh_TW: +2 keys (showcase.search.video, showcase.unit.films) - zh_CN/en/ja: +26 keys each (showcase.actress.*, mode.*, search.*, sort.actress.*, unit.*) - Capabilities: rescrape_actress tool (side_effect + confirmation_required, irreversible warning) - TestShowcaseActressI18n: 26 parametrize keys × 4 locales = 104 sub-tests - test_capabilities: count 22→23 + rescrape_actress in EXPECTED_TOOL_NAMES - Full suite: 2276 passed, 0 failed Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ate)
F1: videoCount > 0 guard → (videoCount > 0 || showFavoriteActresses)
actress mode accessible with 0 indexed videos
F2: init() calls loadActresses() when restoring showFavoriteActresses=true
F3: photo cache-bust '&t=' → '?t=' (no existing query string)
F4: rescrapeActress() stale guard — array always updated, lightbox only if same actress
F4b: removeActress() stale guard — splice+re-sort always, closeActressLightbox only if same
F5: sort comparator 'created_at' → 'added_at' (matches template sort value)
F6: video loading/empty states add !showFavoriteActresses guard
F7: rescrapeActress() stale guard restructured — don't discard successful array update
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Both endpoints called _actress_to_response without querying count_videos_for_actress(), defaulting to 0. Cards now show correct film count immediately after favorite/rescrape. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Three-column footer (name / dynamic sort indicator / age) with hover layer showing height · cup · BWH. Sort indicator follows actressSort state; hover excludes age/birth per CD-7. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Remove Row 6 text buttons, add rescrape + remove as .lb-action-btn on lightbox cover with --danger red tint modifier and touch fallback (@media pointer:coarse). Matches video lightbox cover-actions pattern. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace ghost button with capsule toggle inside .spotlight-search. Active mode gets glass highlight (backdrop-filter + semi-transparent). Non-empty search disables opposite mode button. Input padding adjusted. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add width:100% + box-sizing to .dropdown-item so clickable area fills the panel. Add z-index:210 to .toolbar-dropdown-wrap to prevent stacking issues with adjacent toolbar buttons. Bump min-width 160px. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dedicated .actress-grid (minmax 160px) replaces shared .showcase-grid for denser card packing. _getActiveGrid() helper routes 6 querySelector calls. playModeCrossfade gets actress selector. 480px breakpoint = 3col. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… mode Design adjustment: .actress-grid minmax 160px→220px for visual readability (spec/plan updated locally). Fix _sortWithFlip guard to include actress mode. Fix toggleActressMode crossfade to use self.mode instead of hardcoded 'grid'. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…reels Lightbox cover-actions: rescrape button → searchActressFilms() with bi-camera-reels icon. Grid card: new .actress-card-overlay hover button with same action. Flow: close lightbox → switch to video mode → set search to actress name → animate filter. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Move .actress-card-overlay from center (inset:0) to bottom gradient bar matching lightbox cover-actions pattern. Add :focus-within for keyboard accessibility. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…okens 8a: --spotlight-width/--spotlight-left-slot/--spotlight-right-slot tokens 8b: showcase-toolbar flex→grid (1fr/680px/1fr) — search always page-center 8c: variant class .spotlight-search--mode-toggle replaces bare padding leak Bonus: #ff6b6b→var(--color-error-hover), tailwind recompile, 4 guard tests Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add _checkPreciseActressMatch(term, source) with stale guard and _actresses lazy-load flag. Wire into searchFromMetadata (metadata path) and onSearchChange (manual path). 12 frontend lint guards added. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add ♥/♡ heart in search bar for precise actress match. Clickable ♡ on metadata path triggers POST /api/actresses/favorite with loading state + toast feedback. 4 frontend lint guards + 2 i18n keys added. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add av-card-preview hero-card template with photo/fallback/overlay/ footer-hover. Reset _heroCardImageError on actress change. Add openHeroCardLightbox stub for T4. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Implement openHeroCardLightbox with direct -1 assignment, prev/next sentinel transitions, hasVisiblePrev/Next computed, handleKeydown showFavoriteActresses fix, and removeActress x-show guard. 7 guards. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add playHeroCardAppear in animations.js with reduced-motion fallback. Trigger on precise match with is_favorite. Alpine x-transition:leave for disappear. Lightbox animations reuse T4 wiring. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hardcoded "No Image" with t('common.no_image') in 3 places
across showcase + search templates. Add 4-locale key + 2 guard tests.
Full suite 2316 passed.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
P0: _actress_to_response() now includes is_favorite: True — all actresses in DB are favorites by definition. Fixes empty heart + missing hero card. P0: hero card max-width: 280px — prevents full-width stretch on desktop when outside grid container. P1: searchFromMetadata(term, type) — only 'actress' type triggers _checkPreciseActressMatch; other metadata (maker/series/tag) clears any stale match instead of creating false actress stubs. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
P0: searchActressFilms() now calls _checkPreciseActressMatch so hero card appears when navigating from actress mode. P1: test_list_with_actresses asserts is_favorite: True on every entry to guard the API contract. P2: hero card moved inside .showcase-grid as first child (matching /search pattern) — grid auto-sizes width instead of fixed max-width. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
P1: toggleActressMode() now calls _clearPreciseMatch() when entering actress mode, preventing hero card/heart from persisting with empty search bar after mode round-trip. Guard test added. P2: ja.json showcase.actress.favorited fixed from Chinese "収藏済み" to Japanese "お気に入り済み". Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Bump version 0.7.2→0.7.3. Add Phase 44 CHANGELOG entry. Add list_actresses to capabilities manifest (24 tools). Add 2 guard tests (playHeroCardAppear + searchFromMetadata actress type). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 7a9e146f74
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
P1: evict _actress_cache entry before get_actress_profile in rescrape handler, fulfilling the "bypass cache" contract. P2: re-read actress from DB after save() in both favorite and rescrape handlers so created_at is the real SQLite timestamp, not null. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Codex Review Response (c290a1f)P1 — rescrape cache bypass: Fixed. P2 — created_at null: Fixed. Both |
Summary
Phases
Test plan
🤖 Generated with Claude Code