Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions .claude/skills/verify/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
name: verify
description: Verify reader features end-to-end by importing a synthetic volume through the real upload modal and driving the reader with Playwright. Use when a change needs runtime observation in the actual app (not unit tests).
---

# Verifying mokuro-reader changes end-to-end

## Launch

```bash
npm run dev -- --port 5199 --strictPort # dedicated port; NEVER bare 5173 (other worktrees own it)
```

Browser: Playwright from `node_modules`, executablePath
`~/.cache/ms-playwright/chromium-1208/chrome-linux64/chrome` (note `chrome-linux64`, not `chrome-linux`).
Headless is fine. Don't drive the user's Chrome (stale service worker, port collisions, hidden-window rAF freeze).

## Synthetic volume fixture

The import needs a `.mokuro` + matching `.cbz` (same basename):

- Pages: ImageMagick solid-color portraits with page numbers:
`magick -size 1400x2000 xc:"#d8f0ff" -bordercolor black -border 8 -resize 1400x2000\! -gravity center -pointsize 400 -annotate 0 "1" 001.png`
- `vol1.cbz`: `zip -j vol1.cbz *.png`
- `vol1.mokuro` (all fields required): `{version, title, title_uuid, volume, volume_uuid, chars, pages: [{version, img_width, img_height, blocks: [], img_path}]}`

## Import through the real UI

1. Navbar icon cluster: `div.flex.gap-5 > button` — nth(2) is the upload icon → opens the Import modal (`<dialog>`).
2. **Gotcha:** `getByRole()` fails to match buttons inside the flowbite modal `<dialog>` — use CSS locators (`page.locator('dialog button', { hasText: ... })`) or `page.evaluate` with `textContent.trim()` matching.
3. `waitForEvent('filechooser')` + click the `choose files` button, `setFiles([vol1.mokuro, vol1.cbz])`, then click the exact-text `Import` button.
4. Wait for `text=GapTest` in the catalog, click series → volume → reader (`#/reader/<title>/<uuid>`).

## Driving the reader

- RTL default: `ArrowLeft` = forward. Auto view mode pairs pages (cover shows alone first).
- Page elements: `#manga-panel [data-page-index]` — measure `getBoundingClientRect()` for gap/scale/pan assertions (natural page width 1400 → scale = rect.width/1400).
- Wheel with modifiers: `page.keyboard.down('Control'/'Shift')` + `page.mouse.wheel(0, ±100)` — Playwright applies held modifiers to the wheel event. CDP does NOT swap deltaY→deltaX under shift the way real input does; handlers reading `deltaY || deltaX` cover both.
- Reader toast: search text nodes for the notification string.
- Reader settings drawer: `button.reader-hud.right-3` (there are 3 `.reader-hud` buttons; `.first()` is the page-counter). Toggle labels (e.g. "Continuous scroll") are clickable once the drawer opens; close by clicking far from the drawer.
- Persisted settings check: `JSON.parse(localStorage.getItem('profiles'))[currentProfile]`.

## Known noise

- `[pageerror] Unexpected token '<'` on first dev-server load — pre-existing dev artifact, unrelated to features.
9 changes: 8 additions & 1 deletion docs/INPUT-CONTRACTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,14 @@ Zoom settles carry a `SettleReason` (`zoom-controller.ts`): only
| Pinch survivor | keeps panning | ignored until fresh press |
| Tap commit | immediate | deferred (300 ms) |
| Swipe-to-flip | yes (mobile setting, edge-gated) | no (panning is the scroll) |
| Wheel | zoom or camera glide | zoom or (native/strip) scroll |
| Wheel | zoom, gap, or camera glide | zoom, gap, or (native/strip) scroll |

Ctrl/meta+shift+wheel is the page-gap adjustment chord on every surface
(paged writes `pagedGap`; scroll readers write `scrollGap` and sync
`pageDividers`), checked before the zoom intent. Binding rule: wheel combos
with a native browser meaning keep that meaning tuned for the reader
(ctrl+wheel zooms, shift+wheel stays a horizontal pan); the gap chord is
deliberately one no browser binds.

## Testing

Expand Down
Loading
Loading