Files
mangaBookmark/userscript/CLAUDE.md
sulthan 4229c179b0 rebrand: MangaBM → BookmarkManager, add novel library support (#15)
Two intertwined changes — the rebrand and the novel library were developed on
the same branch because the novel UI plumbing is part of the new "Bookmark
Manager" wordmark in the web shell.

## What it does

- **Rebrand**: MangaBM → BookmarkManager across the Go module, compose stack,
  env vars, Traefik hostnames, container/image names, userscript storage
  prefixes (`mangabm:cache` → `bmgr:manga:cache`, `mangabm:queue` → `bmgr:manga:queue`),
  and docs.
- **Novel library**: same backend, two libraries. New `kind` column splits
  bookmarks into `manga` / `novel`; PUT validates it. Two userscripts:
  - `manga-bookmark.user.js` — unchanged behaviour, just stamps its own `kind`.
  - `novel-bookmark.user.js` — separate Violentmonkey install with adapters
    for **novelfull.com** (polled via headless browser — Cloudflare JS
    challenge) and **lightnovelworld.net** (polled via plain TLS).
- **Web UI**: library switch on the app shell. Login art, libswitch, and
  novel-site colours from the Cinder design snapshot.

## Plumbing

- `addedColumns` ALTER for `kind` runs on first start after upgrade; every
  pre-existing row is backfilled to `'manga'`. No manual SQL, no down-time.
- `ALLOWED_ORIGINS` gains the two novel sites.
- New `NOVEL_USERSCRIPT_PATH` env (default `/userscript/novel-bookmark.user.js`),
  bindmounted alongside the manga script.
- Traefik router names `mangabm*` → `bmapi*` / `bmweb*`.

## Test status

- `go test ./...` — green
- `node --test userscript/test/logic.test.js` — 34 pass
- `node --test userscript/test/novel-logic.test.js` — 11 pass
- `node --check` on both userscripts — clean

## Notes for the redeploy

.env keys were renamed (`MANGA_API_HOST` → `BOOKMARK_API_HOST`,
`MANGA_WEB_HOST` → `BOOKMARK_WEB_HOST`). Update DNS / Traefik labels on the
prod override before pulling, otherwise the public hostnames go dark.
See the redeploy instructions I'll post next to this PR.

Reviewed-on: #15
Co-authored-by: Sulthan Zaki <sultankiki05@gmail.com>
Co-committed-by: Sulthan Zaki <sultankiki05@gmail.com>
2026-08-06 03:58:23 +07:00

4.7 KiB
Raw Permalink Blame History

Guidance for Claude Code working under userscript/. See root CLAUDE.md for the project-wide architecture diagram, hard constraints, and design system.

Userscript structure (single IIFE, manga-bookmark.user.js)

  1. Site adapters — one per host, detect(location, document) return page type + IDs. Identify type/IDs from URL regex (most stable); pull title/cover from og:title/og:image meta tags, not CSS classes.
  2. API client — apiGet/apiPut/apiDelete with bearer header; localStorage key bmgr:manga:cache for instant render + offline fallback.
  3. Progress logic — auto-upsert last_chapter only when chapterNum >= stored last_chapter_num (re-reading old chapters must not regress progress; unparseable -> set current). Manual panel override forces any value.
  4. Retry queue — every write go through pushBookmark/pushDelete, so failed mutation park in localStorage (bmgr:manga:queue) and replayed on next navigation, reconnect, or refresh(). Entries are markers ({key, op, sendStatus, attempts}), never payloads — body read from cache at send time, so one entry per key give ordering and coalescing for free. sendStatus is sticky: while archive pending, later writes to that key keep carrying bucket, which stop successful in-between write from silently un-archiving series. refresh() drains before it fetches and overlays anything still pending, so list never flaps. 400 drops entry, 401 abort pass and keep queue, and transient failures retry to cap of 10. Latest-chapter writes deliberately stay out of queue. See docs/superpowers/specs/2026-07-27-offline-retry-queue-design.md.
  5. UI — rendered inside Shadow DOM root to isolate from site CSS (critical on mobile). Three tabs (All / Favourites / Archived) and row of link chips to web UI and both manga sites; WEB_BASE sits in CONFIG block next to API_BASE. FAB is 7 × 44 edge tab whose hit area widened to 28 × 72 by invisible #hit child; #fab must keep touch-action: none and must not regain overflow: hidden. Since touch-action resolved at gesture start, strip can't be both browser-scrolled and script-dragged, so makeDraggable splits by intent: swipe from #hit scrolls via window.scrollBy, hold of ARM_MS arms reposition drag, visible sliver drags with no hold. See docs/superpowers/specs/2026-07-28-edge-tab-hitbox-design.md.
  6. SPA navigation — Asura is Astro, client-routed on comic/chapter pages: patch history.pushState/replaceState + listen popstate, re-run detect() on URL change so auto-update fire without reload. Demonic uses classic reloads (initial document-idle run suffice).

Live URL shapes (verified 2026-07-26, may drift — re-check against live pages before trust)

  • asurascans.com: series /comics/<slug> (slug carries trailing site-wide build-hash suffix, e.g. -059befe1, that rotates on every redeploy), chapter /comics/<slug>/chapter/<n>. seriesId must strip hash (/-[0-9a-f]{8}$/, stripBuildHash in userscript, asuraBuildHash in backend); URLs keep full slug — stale-hash URLs 302 to current ones. Astro-rendered; chapter links present in raw server HTML.
  • demonicscans.org: series /manga/<slug> (slug may URL-encode punctuation, e.g. %2527 for '), chapter /title/<slug>/chapter/<n>/<page> (older chaptered.php?manga=<id>&chapter=<n> form still exists as redirect, what series-page chapter-list anchors link through). Encodings (incl. triple-encoded punctuation like %25252D) identical on /manga/ and /title/ pages, so decode-once seriesIds match — verified 2026-07-28.
  • novelfull.com (novel script): series /<slug>.html, chapter /<slug>/chapter-<n>[-<title-slug>].html. No og:* tags at all — title from h3.title (series) or a.truyen-title (chapter), cover from meta[name="image"]. Behind a Cloudflare JS challenge no TLS fingerprint clears, so the backend polls it through the headless browser.
  • lightnovelworld.net (novel script): series /novel/<slug>/, chapter /<slug>-chapter-<n>/ — flat, at the site root. h1.entry-title is the clean title on a series page and <Title> Chapter <n> on a chapter page. Chapter pages carry no og:image. Its series page lists every chapter with an absolute href, so the backend polls it with the plain TLS client.

Second script: novel-bookmark.user.js

A copy of the manga script with two adapters, LIBRARY = "novel" and STORE_PREFIX = "bmgr:novel:". No migration loop (this script has no previous installation to carry keys over from). Installed alongside the manga script; both write to the same backend with the same LIBRARY column discriminating them.