From 5d3245c85f4882ca30d30582bb44e92ad96352c2 Mon Sep 17 00:00:00 2001 From: ildyria Date: Sat, 17 Oct 2026 13:39:09 +0200 Subject: [PATCH 1/2] fix(photo): scroll PDF pages instead of changing photo The mouse wheel no longer navigates photos while a PDF is displayed. The dock is hidden for PDFs and its move and delete actions are shown in the photo header instead. Spec impact: Feature 097 PDF Photo View spec, plan, tasks and open questions Q-097-01 to Q-097-03 resolved, roadmap row 097. Claude-Session: https://claude.ai/code/session_016n9EqCvZpHhBgMtkcFiD3o --- .../097-pdf-photo-view/open-questions.md | 44 +++++++ .../features/097-pdf-photo-view/plan.md | 48 ++++++++ .../features/097-pdf-photo-view/spec.md | 109 ++++++++++++++++++ .../features/097-pdf-photo-view/tasks.md | 17 +++ docs/specs/4-architecture/roadmap.md | 1 + .../components/gallery/photoModule/Dock.vue | 8 +- .../gallery/photoModule/PhotoPanel.vue | 18 ++- .../js/v8/components/headers/PhotoHeader.vue | 29 ++++- 8 files changed, 263 insertions(+), 11 deletions(-) create mode 100644 docs/specs/4-architecture/features/097-pdf-photo-view/open-questions.md create mode 100644 docs/specs/4-architecture/features/097-pdf-photo-view/plan.md create mode 100644 docs/specs/4-architecture/features/097-pdf-photo-view/spec.md create mode 100644 docs/specs/4-architecture/features/097-pdf-photo-view/tasks.md diff --git a/docs/specs/4-architecture/features/097-pdf-photo-view/open-questions.md b/docs/specs/4-architecture/features/097-pdf-photo-view/open-questions.md new file mode 100644 index 00000000000..c748ff5bfe3 --- /dev/null +++ b/docs/specs/4-architecture/features/097-pdf-photo-view/open-questions.md @@ -0,0 +1,44 @@ +# Open Questions – Feature 097 + +Open questions for [Feature 097](spec.md). Log every high- and medium-impact question here (table row + Question Details entry) before asking the user; see [open-questions-format.md](../../spec-guidelines/open-questions-format.md). Once answered, fold the outcome into [spec.md](spec.md) (and an ADR when architecturally significant), then mark the entry resolved. + +## Active Questions + +| Question ID | Feature | Priority | Summary | Status | Opened | Updated | +|-------------|---------|----------|---------|--------|--------|---------| +| ~~Q-097-01~~ | 097 | Medium | Wheel over a PDF: A) never navigate photos while a PDF is shown, B) navigate only when the wheel is outside the PDF frame, C) navigate only once the PDF hits its first/last page | Resolved (A, 2026-10-17, FR-097-01) | 2026-10-17 | 2026-10-17 | +| ~~Q-097-02~~ | 097 | Medium | Which dock actions move to the header for PDFs: A) every dock action that applies to a PDF (highlight, set album header, move, delete), B) move and delete only, C) move and delete in the header, the rest in an overflow menu | Resolved (B, 2026-10-17, FR-097-03) | 2026-10-17 | 2026-10-17 | +| ~~Q-097-03~~ | 097 | Low | Frontend scope: A) v8 only, B) v8 and v7 | Resolved (A, 2026-10-17, Non-Goals) | 2026-10-17 | 2026-10-17 | + +## Question Details + +### ~~Q-097-01~~ · Wheel behaviour while a PDF is displayed + +**Question:** `PhotoPanel.vue` listens to `wheel` on `window` and turns any wheel event into next/previous when "scroll to navigate" is on, so scrolling a PDF changes photo instead of turning pages. What should the wheel do while a PDF is shown? + +**Answer:** A (2026-10-17), folded into FR-097-01. + +- **A (recommended) – Never navigate while a PDF is shown.** Same rule as the 360° sphere (FR-082-10). The wheel only scrolls the PDF; arrows, keyboard and swipe still navigate. Simple, predictable, one guard in `scrollTo`. +- **B – Navigate only when the wheel is outside the PDF frame** (header strip, margins). Keeps wheel navigation, but the strip is thin, and browsers differ in which wheel events leave the embedded viewer, so it is hard to predict and to test. +- **C – Navigate once the PDF reaches its first/last page.** Natural "scroll through", but the embedded browser viewer exposes no page/scroll position to the page, so it cannot be implemented with ``. + +### ~~Q-097-02~~ · Header actions replacing the dock for PDFs + +**Question:** For PDFs, the dock is hidden and its actions move to the header. Which actions? + +Dock actions today: highlight, set album header, watermark, scan faces, rotate CCW/CW, move, delete. Watermark, scan faces and rotate do not apply to a PDF. + +**Answer:** B (2026-10-17), folded into FR-097-03 and Non-Goals. + +- **A (recommended) – Every action that applies to a PDF:** highlight, set album header (not in the timeline), move (when `can_move_content`), delete. No capability lost; same rights checks as the dock. +- **B – Move and delete only.** Smallest header, but PDF owners can no longer highlight a PDF or set it as album header from the photo view. +- **C – Move and delete as buttons, highlight and set album header in a `…` overflow menu.** Compact, but adds a new menu pattern to the photo header. + +### ~~Q-097-03~~ · v7 frontend + +**Question:** The same wheel listener and dock exist in `resources/js/v7/components/gallery/photoModule/`. Should v7 change too? + +**Answer:** A (2026-10-17), folded into Overview and Non-Goals. + +- **A (recommended) – v8 only.** v8 is the current frontend; v7 stays as is. +- **B – v8 and v7.** Same behaviour in both, but twice the change and the testing. diff --git a/docs/specs/4-architecture/features/097-pdf-photo-view/plan.md b/docs/specs/4-architecture/features/097-pdf-photo-view/plan.md new file mode 100644 index 00000000000..a1292a3af48 --- /dev/null +++ b/docs/specs/4-architecture/features/097-pdf-photo-view/plan.md @@ -0,0 +1,48 @@ +# Feature Plan 097 – PDF Photo View + +| Field | Value | +|-------|-------| +| Status | Complete | +| Last updated | 2026-10-17 | +| Linked spec | [spec.md](spec.md) | +| Linked tasks | [tasks.md](tasks.md) | + +## Vision & Success Criteria +Wheel scrolls a displayed PDF instead of changing photo; the dock no longer covers the PDF and its move/delete actions live in the photo header (S-097-01 … 05). + +## Scope Alignment +In: v8 `PhotoPanel.vue`, `PhotoHeader.vue`, `Dock.vue`. Out: v7, backend, highlight / set album header for PDFs (Q-097-02 B). + +## Dependencies & Interfaces +`photoStore.imageViewMode` (`ImageViewMode.Pdf`), `albumStore.rights.can_move_content`, the `toggleMove` / `toggleDelete` events already handled by the photo view parent of `PhotoPanel`. + +## Assumptions & Risks +- Keyboard shortcuts for move/delete are unaffected (they do not go through the dock). +- Risk: header crowding on phones; two icon-only buttons, same style as the existing ones. + +## Implementation Drift Gate +Re-read FR-097-01 … 03 against the diff before commit. + +## Increment Map +1. I1 – Wheel guard for PDFs in `PhotoPanel.scrollTo` (FR-097-01). +2. I2 – Hide the dock for PDFs, drop the now-unused `isNarrowMenu` prop (FR-097-02). +3. I3 – Move/delete buttons in `PhotoHeader` for PDFs, wired through `PhotoPanel` (FR-097-03). + +## Scenario Tracking +| Scenario | Increment | +|----------|-----------| +| S-097-01, S-097-02 | I1 | +| S-097-03, S-097-03b, S-097-04 | I2, I3 | +| S-097-05 | I1–I3 (regression) | + +## Analysis Gate +2026-10-17: spec, plan and tasks agree; Q-097-01 … 03 resolved; no ADR needed (local UI change). + +## Exit Criteria +`vp check --fix`, `vp run typecheck` green; tasks checked; roadmap updated. + +## Follow-ups / Backlog +None. + +## Intent Log +- 2026-10-17: Owner reported the wheel changing photo over PDFs and asked to move dock actions to the header. Questions Q-097-01 … 03 asked in chat, answered "A B A". Owner asked to commit and push once done. diff --git a/docs/specs/4-architecture/features/097-pdf-photo-view/spec.md b/docs/specs/4-architecture/features/097-pdf-photo-view/spec.md new file mode 100644 index 00000000000..ddd6edc51e6 --- /dev/null +++ b/docs/specs/4-architecture/features/097-pdf-photo-view/spec.md @@ -0,0 +1,109 @@ +# Feature 097 – PDF Photo View + +| Field | Value | +|-------|-------| +| Status | Ready | +| Last updated | 2026-10-17 | +| Owners | ildyria | +| Linked plan | [plan.md](plan.md) | +| Linked tasks | [tasks.md](tasks.md) | +| Roadmap entry | #097 | + +> Guardrail: This specification is the single normative source of truth for the feature. Track high- and medium-impact questions in the feature's [open-questions.md](open-questions.md), encode resolved answers directly in the Requirements/NFR/Behaviour/UI/Telemetry sections below (no per-feature `## Clarifications` sections), and use ADRs under `docs/specs/6-decisions/` for architecturally significant clarifications (referencing their IDs from the relevant spec sections). + +## Overview +When a PDF is shown in the photo view (`ImageViewMode.Pdf`), the mouse wheel changes photo instead of scrolling the PDF's pages: `PhotoPanel.vue` listens to `wheel` on `window` and maps every event to next/previous when "scroll to navigate" is enabled. The dock also sits on top of the PDF and hides part of its first page. This feature makes the wheel scroll the PDF and moves the dock actions into the photo header for PDFs. Frontend only: v8 (Q-097-03 A); v7 is unchanged. No backend or API change. + +## Goals +- Scrolling over a displayed PDF scrolls its pages and never changes photo (Q-097-01 A). +- The dock is not shown for PDFs; move and delete are in the photo header instead (Q-097-02 B). + +## Non-Goals +- Replacing the browser's embedded PDF viewer (pdf.js or similar). +- Changing wheel, dock or header behaviour for images, videos, RAW placeholders or 360° photos. +- The v7 frontend (Q-097-03 A). +- Highlight and set-album-header from the PDF photo view: both dock actions are dropped for PDFs (Q-097-02 B). The header's existing highlight button for users without album edit rights is unchanged. + +## Functional Requirements + +| ID | Requirement | Success path | Validation path | Failure path | Telemetry & traces | Source | +|----|-------------|--------------|-----------------|--------------|--------------------|--------| +| FR-097-01 | The wheel never navigates photos while a PDF is displayed, like the 360° sphere (FR-082-10) (Q-097-01 A). | Wheel scrolls the PDF; arrows, keyboard and swipe still navigate. | Applies only when `photoStore.imageViewMode === ImageViewMode.Pdf`. | n/a | none | Owner report 2026-10-17 | +| FR-097-02 | The dock is hidden while a PDF is displayed. | No dock over the PDF. | Applies only to `ImageViewMode.Pdf`. | n/a | none | Owner directive 2026-10-17 | +| FR-097-03 | The photo header shows move (when `albumStore.rights.can_move_content`) and delete for a PDF, with the dock's rights checks (Q-097-02 B). | Buttons emit `toggleMove` / `toggleDelete`, the same events as the dock buttons. | Shown only when the dock would have been shown (`canEditCurrentPhoto`, not editing, not in a slideshow). | n/a | none | Owner directive 2026-10-17 | + +## Non-Functional Requirements + +| ID | Requirement | Driver | Measurement | Dependencies | Source | +|----|-------------|--------|-------------|--------------|--------| +| NFR-097-01 | No regression for non-PDF media. | Scope guard | Manual check: image, video, 360° photo keep their wheel and dock behaviour. | `PhotoPanel.vue`, `PhotoHeader.vue` | AGENTS.md | +| NFR-097-02 | Frontend gates pass. | Quality | `vp check --fix`, `vp run typecheck` | Vite+ (Feature 096) | AGENTS.md | + +## UI / Interaction Mock-ups + +Before (PDF): +``` ++----------------------------------------------------------------+ +| [<] [app] [dl] [edit] [info] ... | +| +----------------------------+ | +| | [flag][img][folder][trash] | <- dock | +| +----------------------------------------------------------+ | +| | PDF page 1 (hidden under the dock) | | +| | | | +| | wheel -> next / previous photo (bug) | | +| +----------------------------------------------------------+ | ++----------------------------------------------------------------+ +``` + +After (PDF): +``` ++----------------------------------------------------------------+ +| [<] [app] [dl] [edit] [info] [folder] [trash] | +| +----------------------------------------------------------+ | +| | PDF page 1 | | +| | | | +| | wheel -> scrolls the PDF pages | | +| +----------------------------------------------------------+ | ++----------------------------------------------------------------+ +``` + +## Branch & Scenario Matrix + +| Scenario ID | Description / Expected outcome | +|-------------|--------------------------------| +| S-097-01 | PDF shown, scroll-to-navigate on, wheel over the PDF: pages scroll, photo stays. | +| S-097-02 | PDF shown, arrow key / next button: navigates as before. | +| S-097-03 | PDF shown, user can edit and move: no dock; header shows move and delete. | +| S-097-03b | PDF shown, user can edit but not move: header shows delete only. | +| S-097-04 | PDF shown, user cannot edit: no dock, no extra header actions. | +| S-097-05 | Image shown: wheel navigation and dock unchanged. | + +## Test Strategy +- **Frontend (v8):** no frontend unit test harness covers `PhotoPanel`/`PhotoHeader`; verified with `vp run typecheck`, `vp check --fix` and a manual run of S-097-01 … 05 on a scratch instance (run-lychee skill). +- **Backend:** none. + +## Interface & Contract Catalogue + +### UI States +| ID | State | Trigger / Expected outcome | +|----|-------|---------------------------| +| UI-097-01 | PDF view, editor | Dock hidden, move and delete in header. | +| UI-097-02 | PDF view, viewer | Dock hidden, header unchanged. | + +## Telemetry & Observability +None. + +## Documentation Deliverables +Roadmap row 097. + +## Fixtures & Sample Data +None. + +## Spec DSL +``` +ui_states: + - id: UI-097-01 + description: PDF view, editor — dock hidden, move and delete in header + - id: UI-097-02 + description: PDF view, viewer — dock hidden, header unchanged +``` diff --git a/docs/specs/4-architecture/features/097-pdf-photo-view/tasks.md b/docs/specs/4-architecture/features/097-pdf-photo-view/tasks.md new file mode 100644 index 00000000000..6b4cdaa43f3 --- /dev/null +++ b/docs/specs/4-architecture/features/097-pdf-photo-view/tasks.md @@ -0,0 +1,17 @@ +# Feature 097 Tasks – PDF Photo View + +_Status: Complete_ +_Last updated: 2026-10-17_ + +## Checklist +- [x] T-097-01 – Skip wheel navigation while a PDF is shown (FR-097-01, S-097-01, S-097-02, S-097-05). + _Verification commands:_ `vp run typecheck`, `vp check --fix` + +- [x] T-097-02 – Hide the dock for PDFs, remove `Dock.isNarrowMenu` (FR-097-02, S-097-03, S-097-04). + _Verification commands:_ `vp run typecheck`, `vp check --fix` + +- [x] T-097-03 – Move and delete buttons in `PhotoHeader` for PDFs (FR-097-03, S-097-03, S-097-03b, S-097-04). + _Verification commands:_ `vp run typecheck`, `vp check --fix` + +## Notes / TODOs +No frontend unit harness for these components. Verified 2026-10-17 with `vp check` and `vp run typecheck` on the three changed files. Manual browser check of S-097-01 … 05 left to the owner. Run `vp check --fix` on the changed files only: on the whole tree it rewrites unrelated files and breaks `EmbedCodeDialog.vue` (`<\/script>` unescaped). diff --git a/docs/specs/4-architecture/roadmap.md b/docs/specs/4-architecture/roadmap.md index a893cd5c96b..62e777f67b2 100644 --- a/docs/specs/4-architecture/roadmap.md +++ b/docs/specs/4-architecture/roadmap.md @@ -6,6 +6,7 @@ High-level planning document for Lychee features and architectural initiatives. | Feature ID | Name | Status | Priority | Assignee | Started | Updated | Progress | |------------|------|--------|----------|----------|---------|---------|----------| +| 097 | PDF Photo View | Testing | Medium | ildyria | 2026-10-17 | 2026-10-17 | 3/3 tasks. Wheel no longer changes photo over a PDF (Q-097-01 A); dock hidden for PDFs, move/delete in the photo header (Q-097-02 B); v8 only (Q-097-03 A). See `features/097-pdf-photo-view/`. | | 096 | Vite+ Migration | Testing | Medium | ildyria | 2026-10-16 | 2026-10-16 | 14/14 tasks. Vite+ 1.3.0 (`vp`: Vite 8.3.4, Oxlint, Oxfmt, `vp staged`) replaces ESLint, Prettier and the `npm run` scripts; `vue-tsc` stays (`vp run typecheck`); npm stays the bootstrap (Dockerfiles, Makefile, post-merge unchanged); CI `js_check.yml` on `setup-vp`, dist job on Node 26. Q-096-01 … 06 resolved 2026-10-16 (all A); ADR-096-01. Implemented 2026-10-16, drift gate passed (assertion script 23/23: build parity on chunk names and asset count, format parity, lint 0 errors, `vp migrate` idempotent, hook rehearsal; Docker node stage on alpine; `vp dev` smoke). PR #4891 open; Q-096-07 B (2026-10-16): the Dependency Review action allow-lists the 16 Vite advisories the dependency graph attaches to the `vite` alias (14/14 tasks). Awaiting CI (Node 24/26, dist, multi-platform Docker, Dependency Review). See `features/096-vite-plus-migration/`. | | 095 | Scrubber Label Formats | Testing | Medium | ildyria | 2026-10-16 | 2026-10-16 | 6/6 tasks. Configurable formats for the date scrubber rail labels (year, month, day, hour), replacing the Feature 094 constants; label inventory in the spec. Q-095-01 … 05 resolved 2026-10-16 (day format free, others SE; labels clipped; sub-album lens uses the album day format; hour header default `g:i` → `H:00`). Q-095-06 A (orders 23–26), Q-095-07 B (album day default `j M` → `j M Y`); implemented, drift gate passed; awaiting commit. Q-095-08/09: hour headers same-day `H:00` or multi-day `j M Y H:00` (new setting), boundaries merge only `unknown`; 9/9 tasks. | | 094 | Scrubber Adaptive Ticks | Testing | Medium | ildyria | 2026-10-16 | 2026-10-16 | 9/9 tasks. v8 date scrubber rail labels adapt to the granularity and the data span (years → `YYYY`, months of one year → short month, days of one month → `DD/MM`, one day → hour), on `/timeline` and album rails; hourly `/timeline` lens and pill show date and hour; frontend only. Q-094-01 … 06 resolved 2026-10-16 (albums keep day entries, label unit = span of the rail entries); Q-094-07 resolved (240 px lens for dated hour labels); Q-094-08 B (no rail on single-header albums); implemented, drift gate passed; awaiting commit. | diff --git a/resources/js/v8/components/gallery/photoModule/Dock.vue b/resources/js/v8/components/gallery/photoModule/Dock.vue index 2ad9777f5fa..86721d5d1e3 100644 --- a/resources/js/v8/components/gallery/photoModule/Dock.vue +++ b/resources/js/v8/components/gallery/photoModule/Dock.vue @@ -10,9 +10,7 @@ 'lg:opacity-20': !isTouchDevice() && !isFullTransparency && !isHovered, 'opacity-75': isTouchDevice() && !isFullTransparency, 'opacity-0': isFullTransparency, - 'z-20 mt-14 sm:mt-0': true, - 'sm:h-1/4': !props.isNarrowMenu, - 'h-14': props.isNarrowMenu, + 'z-20 mt-14 sm:mt-0 sm:h-1/4': true, hidden: is_slideshow_active, 'hidden sm:block': are_details_open, }" @@ -77,10 +75,6 @@ const { rights } = useGlobalRights(); const togglableStore = useTogglablesStateStore(); const { is_slideshow_active, are_details_open } = storeToRefs(togglableStore); -const props = defineProps<{ - isNarrowMenu: boolean; -}>(); - // The dock zone ignores pointer events so clicks reach the face overlay below; only the buttons are interactive. const dockZone = ref(null); const { isInside: isHovered } = usePointerZone(dockZone); diff --git a/resources/js/v8/components/gallery/photoModule/PhotoPanel.vue b/resources/js/v8/components/gallery/photoModule/PhotoPanel.vue index fb2664ccedd..c20360b8b5a 100644 --- a/resources/js/v8/components/gallery/photoModule/PhotoPanel.vue +++ b/resources/js/v8/components/gallery/photoModule/PhotoPanel.vue @@ -1,7 +1,13 @@