catacomb/HANDOFF.md
Luna 5dd1284310 docs: refresh HANDOFF with rename + push-status notes
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-26 23:25:57 -07:00

11 KiB
Raw Blame History

Session handoff — Catacomb

Working notes for continuing this work in another session/agent (Copilot, Codex, etc.). Updated 2026-06-19. Delete or update freely; this is not a tracked spec.

TL;DR — where things are

  • Project: Catacomb (crate/binary catacomb) — one Rust binary that is both an egui desktop GUI and an axum web server wrapping yt-dlp. See CLAUDE.md for the authoritative architecture; ROADMAP.md for the plan.
  • Branch: main. Last commit c12de9c (command palette); 5f95bdb and earlier are pushed, c12de9c is committed but NOT yet pushed. Remote: https://codeberg.org/anassaeneroi/catacomb.git.
  • Local checkout dir was renamed ~/code/youtube-backup~/code/catacomb (code dir only; the library/backup dir at /mnt/InannaBeloved/youtube-backup is unchanged). Run the server with the right CWD, e.g. env -C ~/code/catacomb ~/code/catacomb/target/release/catacomb --web 8081.
  • Only HANDOFF.md is uncommitted now.
  • The project was renamed yt-offline → Catacomb (crate, binary, data paths with migration, UI, docs, repo URLs). See "The rename" below.
  • A dev web server is usually run on :8081 against the user's real library. It gets reaped between turns in this sandbox — just relaunch it (see gotchas).

How to build / run / test

cargo build --release                 # ~1.5 min (opt-level=3 + thin LTO) → target/release/catacomb
cargo test --release                  # 128 unit + 11 integration (tests/api.rs); no network
./target/release/catacomb --web 8081  # headless web server (what the user uses)
./target/release/catacomb             # desktop GUI (default)

CRITICAL gotchas

  1. The web SPA is one big embedded filesrc/web_ui/index.html, baked in at compile time via include_str!. Editing it requires a cargo build to take effect. A JS syntax error there will NOT be caught by cargo build (the HTML is just a string), so after every edit:
    awk '/<script>/{f=1;next}/<\/script>/{f=0}f' src/web_ui/index.html > /tmp/spa.js && node --check /tmp/spa.js
    
    src/web_ui/login.html is a second, separate embedded page (the login screen).
  2. DB location: the server opens the DB at channels_root.join("catacomb.db") (web.rs ~L3127 and app.rs ~L395), i.e. <backup.directory>/catacomb.db. For this user that's /mnt/InannaBeloved/youtube-backup/catacomb.db (~64 MB, real data — do NOT clobber). config.toml is read from the process CWD. A stray 0-byte catacomb.db may sit in the repo dir; it's unused — ignore it.
  3. Offline-first: this is a self-hosted, possibly-no-internet archiver. Never load fonts/CSS/JS from a CDN. UI fonts are embedded as base64 woff2 (SIL OFL) directly in the <style> block. Keep it that way.
  4. All 10 themes are driven by 7 CSS variables (--bg --panel --card --accent --text --muted --border) set per .theme-* class. Any new UI must use those vars (and the design-layer tokens below) so every theme inherits it.
  5. Background server reaping: in the sandbox, run_in_background servers get killed between turns and shell &/nohup is unreliable. Just relaunch ./target/release/catacomb --web 8081; pkill -f "web 8081" first.
  6. Verifying UI visually without a browser driver (no puppeteer/playwright): extract the relevant CSS/JS from index.html into a /tmp/*.html harness with a mock payload and screenshot with headless chromium:
    chromium --headless=new --no-sandbox --disable-gpu --hide-scrollbars \
      --window-size=1100,1300 --virtual-time-budget=3000 \
      --screenshot=/tmp/out.png "file:///tmp/harness.html"
    
    --virtual-time-budget can capture entry animations mid-flight; add a "settle" override (disable transitions, force final state) for a clean final shot.

The rename (yt-offline → Catacomb)

Done and pushed across 404362b (code), 3f61184 (docs), 5f95bdb (repo URLs):

  • Crate/binary catacomb (Cargo.toml, deb/rpm assets, PKGBUILD, package.sh, catacomb.desktop, launch.json, release CI, tests/api.rsCARGO_BIN_EXE_catacomb).
  • Data paths: DB yt-offline.dbcatacomb.db; venv ~/.local/share/yt-offline~/.local/share/catacomb. migrate_legacy_paths() in main.rs adopts the old names on first run (renames DB + WAL/SHM sidecars + venv dir; best-effort, no-op once migrated). Keep this function's OLD-name args as yt-offline — a blunt rename sed will wrongly make both sides catacomb and break migration (happened once; watch for it).
  • Display: window/tray/title/feed/login/SPA wordmarks → "Catacomb". Env override YT_OFFLINE_RENDERERCATACOMB_RENDERER.
  • Verified: builds, 128+11 tests pass, scratch-dir migration preserved bytes, and the live cutover migrated the real 64 MB DB + venv (server served the library).
  • Repo URLs point at …/anassaeneroi/catacomb (Cargo.toml, PKGBUILD, package.sh).

Settings flow (the easy thing to get wrong)

Adding a setting touches five places (grep an existing one like dedup_enabled or sponsorblock_mode end-to-end first):

  1. config.rs — field + Default + default_with_dir().
  2. download_options.rsOption<…> per-channel override (None = use global).
  3. downloader.rs — resolver merging global+override + a pub field on Downloader.
  4. Both UIs: app.rs (egui Settings + channel-options dialog) AND web_ui/index.html (Settings modal + channel dialog) AND web.rs SettingsPayload (GET reads config, POST writes config + pushes to live Downloader).
  5. Seed the Downloader field at construction AND on settings-save, in BOTH app.rs and web.rs.

What this session shipped (committed, newest first)

  • 5f95bdb repo URLs → renamed Codeberg repo.
  • 3f61184 / 404362b Rename → Catacomb (see above).
  • 6d2261b Login page reskinsrc/web_ui/login.html: serif wordmark + recording dot, aurora + grain, accent focus ring; system-serif (offline-safe).
  • 2f95e7f Unified cinematic video player. Replaced native <video controls> with ONE custom player for both direct + transcode. Custom scrubber (played/buffered/chapter-ticks/hover tooltip, drag-to-seek via pointer events, commits on release), speed popover (0.52×), persistent volume/speed/captions (localStorage plPrefs), CC toggle, PiP, fullscreen, auto-hiding controls + center flash, expanded keyboard (space/k j/l ←→ ↑↓ m c p f < > 09). All seeking routes through playerSeek/effTime (transcode reload-at-offset intact). CSS prefix .pl-*; JS around playVideo/updateVctrl/plBindScrubber.
  • 816da05 Persist login sessions in SQLitesessions(token, issued_at) table; map switched Instantu64; insert/delete/clear + rehydrate at startup. Fixes "restart logs everyone out".
  • dd48e89 api() 401 → reload to login instead of a cryptic "error" toast.
  • 615c088 Maintenance modal → "diagnostics console" (CSS .mx-*): pulsing health verdict + status tiles + instrument panels. Presentation only; handler ids/classes untouched.
  • 6f61821 Stats modal → "observatory" dashboard (CSS .sx-*): count-up metrics, self-drawing SVG area chart, growing histogram, ranked leaderboard.
  • c8cb700 "Cinematheque" reskin: embedded Instrument Serif + Hanken Grotesk (base64 OFL), accent aurora + film-grain, cinematic card hover, recording dot.
  • 983864f macOS osxcross packaging; 207013e Windows shippable .zip + CI; 8b25787 library sorting (download-date + grouped options).

Design-layer conventions (web UI)

  • CSS is appended near the end of the <style> block, AFTER the functional rules, so equal-specificity overrides win by source order. Prefixes: .sx-* = stats observatory, .mx-* = maintenance console, .pl-* = player.
  • Reveal animations gate behind a class added post-layout (e.g. .sx-go) so they play once per open, not on re-render. All honor prefers-reduced-motion (global rule at the end of <style>).
  • Fonts: --font-display (Instrument Serif), --font-body (Hanken Grotesk). Extra tokens: --radius, --radius-sm, --ring, --glow, --shadow, --hair.

Command-palette search overlay — SHIPPED (c12de9c)

openSearch() is now a .cp-* command palette (CSS .cp-*; JS openSearch, runCommandSearch, renderCommandPalette, cpKeydown, updateSelected, scrollIntoSelected, cpSelect, cpOpenRecent, cpClearRecents, closeCommandPalette, ftsSnippet). Blurred backdrop, centered input, results grouped by channel with highlighted FTS snippets (char(2)/char(3)<mark> via ftsSnippet), ↑↓ nav, Enter to open (cpSelectselectVideo), Esc to close, recents + quick-actions in localStorage['cp-recents']. Debounced queries to /api/search?limit=60&q=… with a cpSeq guard against stale responses. Upload date is looked up client-side via findVideo (SearchHit has no date). Triggers: 🔍 header button + f hotkey. Verified by build + headless harness screenshot. Built into the running :8081 binary.

Suggested next steps (pick up here)

  1. Pushc12de9c (command palette) is committed but not yet pushed.
  2. Any rough edges from real daily use (the player + palette especially — both verified via harness screenshots, not live click-through).
  3. A fresh web-UI surface to reskin/upgrade, or a roadmap item below.

Roadmap "surpass" items still open (see ROADMAP.md §3):

  • 3.1 macOS binary — osxcross scaffolding done (scripts/package.sh mac); needs the toolchain + SDK installed, then verify. Windows already ships.
  • 3.2 Android client (big), 3.8 plugin/scripting hooks (architectural).
  • Phase 4 blue-sky: AI transcript summarisation (FTS transcript index already exists), TV-mode layout, multi-user accounts.
  • Federation follow-up (3.5): in-UI "add remote" editor (peers are config-only).

Watch-outs / open questions

  • The video player's seek/speed/captions are verified by code-path review + static chrome screenshot, NOT live playback (no headless video). Worth a real click-through in the app.
  • After the rename + session-persistence work, the user's existing browser cookie predates both, so they must log in once more; logins after that survive restarts. A download password may or may not be set (toggled during testing) — that's user-driven, not a bug.
  • The user's local config.toml source_url still points at the old …/yt-offline repo (gitignored, user-specific) — change it in Settings if the UI "source" link should match the renamed repo.
  • Don't commit cookies.txt, config.toml, or catacomb.db (all gitignored; contain creds / the Argon2 password hash).