Commit graph

150 commits

Author SHA1 Message Date
Luna
4de452c503 feat(tray): integrate egui context for system tray events to ensure immediate UI updates 2026-07-04 02:54:00 -07:00
Luna
f185e7062f Trigger mirror sync 20260629 2026-06-29 11:19:54 -07:00
Luna
cbade2cfd7 feat: desktop visual refresh + 12-theme pack
- 12 new themes (neon/goth/dev/cozy) + hand-tuned Dark/Light (19 total)
- theme-aware semantic accents replacing hardcoded selection rings
- List/Card/Grid video-list modes with global default + per-view override
- unified placeholder thumbnails + typography/spacing refresh
2026-06-28 00:57:28 -07:00
Luna
5091d0de96 fix(app): Grid mode no longer paints thumbnail twice; drop unused ThemeAccents::LEGACY 2026-06-27 19:30:02 -07:00
Luna
29d449eb9b style(app): bump base spacing + card title size for legibility 2026-06-27 19:21:10 -07:00
Luna
5aaa760ab4 feat(app): Settings picker for default view mode 2026-06-27 19:19:12 -07:00
Luna
4b011f8584 feat(app): view-mode toolbar toggle with per-view override 2026-06-27 19:17:00 -07:00
Luna
ada5b6d802 feat(app): List/Card/Grid video list render modes 2026-06-27 19:14:45 -07:00
Luna
91f3d8500d feat(app): unified theme-tinted placeholder thumbnails 2026-06-27 10:58:29 -07:00
Luna
0336615fa3 feat(app): ViewMode enum + global default + per-view overrides 2026-06-27 10:55:53 -07:00
Luna
1c858a3271 feat(app): use theme-aware accent rings for select/play/bulk 2026-06-27 10:51:03 -07:00
Luna
5fb8cc7036 feat(app): wire ThemeAccents snapshot, recompute on theme change 2026-06-27 10:48:27 -07:00
Luna
bb85d1fac0 feat(config): add [ui] default_view_mode field 2026-06-27 10:45:05 -07:00
Luna
0a58f8ae40 feat(theme): add 12 new themes (neon, goth, dev, cozy) 2026-06-27 10:42:43 -07:00
Luna
01c258e6d2 feat(theme): hand-tune default Dark/Light themes 2026-06-27 10:39:04 -07:00
Luna
ee1a02e20d feat(theme): add ThemeAccents struct + accents_for() lookup 2026-06-27 10:33:58 -07:00
Luna
4effd7b061 chore: gitignore .worktrees/ for isolated workspaces 2026-06-27 10:26:02 -07:00
Luna
98e8738a30 docs(plan): desktop visual refresh implementation plan 2026-06-27 10:20:21 -07:00
Luna
9aaaee0151 docs(book): add public Theming & appearance page 2026-06-27 10:13:06 -07:00
Luna
300820975c docs(design): desktop visual refresh + theme pack spec 2026-06-27 10:11:11 -07:00
Luna
c76ad1c467 docs(android-spike): gitignore local tooling/session files; add Stage-1 prototype plan 2026-06-27 09:46:25 -07:00
Luna
bd0ebfec1f docs(android-spike): synthesis — GO, with Stage-1 prototype recommended
All four questions answered: Q1-3 RISKY (credible paths), Q4 PROVEN. WebView-
BotGuard POT (YTDLnis-proven) collapses the JS-runtime problem; HLahwani/yt-
dlp-android solves Q1+Q2+Q3 together. One open question remains: does WebView-
POT recover anti-bot WITHOUT curl_cffi (broken on Android)? The Stage-1
prototype tests exactly that. Fallback: client-to-server app against existing web
API. Update ROADMAP 3.2 with pointer to findings.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 08:39:05 -07:00
Luna
dcdf35889e docs(android-spike): Q4 Rust-core-via-JNI findings + verdict (PROVEN)
Real build-proof run locally: vtt.rs cross-compiles to aarch64 + x86_64 Android
.so (NDK r26d, exported C-ABI symbol). cargo-ndk + uniffi recommended; ~5 days
to reuse 5-6 pure modules. On-device run-proof blocked by sandbox emulator
reaping, downgraded to symbol-export check per plan.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 02:40:13 -07:00
Luna
11b6679b29 docs(android-spike): Q3 POT/Proof-of-Origin findings + verdict (RISKY)
The pivotal gate does NOT block: POT is required on mobile, but WebView-based
BotGuard token generation (proven in YTDLnis) is the durable path and collapses
the JS-runtime + POT problems into one, killing the Deno/Node sidecar.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 02:33:23 -07:00
Luna
0021dd7516 docs(android-spike): Q2 JS-runtime findings + verdict (RISKY)
External JS runtime now a hard yt-dlp dependency (Python jsinterp deprecated
for YouTube 2025.11.12); QuickJS-ng via NDK or HLahwani/yt-dlp-android is the
path. Also fixes a markdown line starting with '#'.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 02:28:28 -07:00
Luna
d739fd76ee docs(android-spike): Q1 yt-dlp-on-Android findings + verdict (RISKY)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 01:56:20 -07:00
Luna
1fd169954b docs(android-spike): scaffold feasibility findings doc
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 00:28:01 -07:00
Luna
6cfc09b17a docs: implementation plan for Android engine feasibility research spike
Six tasks (Q1–Q4 research + emulator repros, plus scaffold and go/no-go
synthesis) against the approved spec. Research-spike shape: investigate →
reproduce-where-cheap → record evidence → verdict, not TDD.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 00:24:54 -07:00
Luna
541324f7c2 docs: spec for Android engine feasibility research spike (roadmap 3.2)
First sub-project of the Android client: a research spike to decide go/no-go
on a standalone on-device download engine (yt-dlp + JS runtime + POT) before
building any UI. Four research questions, "reproduce where cheap" on the
emulator.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-27 00:21:56 -07:00
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
Luna
c12de9c645 web UI: command-palette search overlay (f / 🔍)
Replace the old search modal with a keyboard-driven command palette: blurred
backdrop, centered input, results grouped by channel with highlighted FTS
snippets (char(2)/char(3) → <mark>), ↑↓ navigation, Enter to open, Esc to
close, recent searches + quick actions (Downloads/Stats/Health/Shortcuts) in
localStorage. Debounced queries to /api/search with a sequence guard so stale
responses don't clobber newer ones. Upload date is looked up client-side from
the loaded library (SearchHit has no date field). Opened by the 🔍 header
button and the `f` hotkey.

Verified: node --check passes, release builds clean, and a headless harness
screenshot confirms grouped results, highlighted matches, and selection.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-22 15:48:37 -07:00
Luna
5f95bdb479 Point repo URLs at the renamed Codeberg repo (catacomb)
The Codeberg repo was renamed yt-offline → catacomb. Update repository/homepage
(Cargo.toml), url + git source (PKGBUILD), and the Windows-zip README Source
line (package.sh) to the new path. The PKGBUILD `$pkgname::` checkout-dir prefix
is no longer needed now that the repo basename matches pkgname.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 19:34:48 -07:00
Luna
3f611849af docs: rename yt-offline → Catacomb across README, ROADMAP, guides, packaging
Prose + commands updated to the new name: brand reads "Catacomb", while
binary/path/package references are the lowercase `catacomb` (commands, deb/rpm
names, ~/.local/share/catacomb, catacomb.db, catacomb.desktop). Codeberg repo
URLs left pointing at the existing repo. Also folds in the pending doc edits
from earlier in the session.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 03:34:18 -07:00
Luna
404362bfb0 Rename yt-offline → Catacomb (crate, binary, data paths, UI)
Full product rename with a one-time data migration so existing installs keep
their library + bundled toolchain:

- Crate/binary: `yt-offline` → `catacomb` (Cargo.toml, deb/rpm assets, PKGBUILD,
  package.sh, the desktop file → catacomb.desktop, launch.json, release CI,
  tests/api.rs CARGO_BIN_EXE_catacomb). Codeberg repo URLs left as-is (real
  addresses); PKGBUILD pins the checkout dir via `$pkgname::`.
- Data paths: DB `yt-offline.db` → `catacomb.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 + the venv dir; best-effort,
  no-op once migrated). Crash log + restore-temp + IPC socket names follow suit.
- Display: window/app title, tray, web page titles, feed titles, login + SPA
  wordmarks → "Catacomb". Env override `YT_OFFLINE_RENDERER` → `CATACOMB_RENDERER`.

Verified: builds clean as target/release/catacomb, 128 unit + 11 integration
tests pass, and a scratch-dir run confirms the DB (+wal) and venv are migrated
with bytes preserved.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 03:11:07 -07:00
Luna
6d2261b032 web UI: reskin the login page to match the cinematheque aesthetic
The login page was the last surface still on the old flat style. Give it the
same identity as the rest of the UI: serif wordmark with the glowing crimson
"recording" dot, an italic "your private archive" tagline, accent aurora +
film-grain atmosphere on a gradient card, a themed focus ring and accent
button, and a soft entrance. Login JS is unchanged; honours reduced-motion.
Standalone page, so the display font is a system serif stack (no CDN, no
duplicating the SPA's embedded woff2).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 02:38:47 -07:00
Luna
2f95e7f09f web UI: unified cinematic video player (speed, scrubber, captions, PiP)
Replace the browser's native <video controls> (used for direct /files/
playback) with one custom player for both direct and transcode streams, so
the chrome matches the reskin and gains features it never had. All seeking
still routes through playerSeek/effTime, so the transcode reload-at-offset
path is unchanged.

- Custom scrubber: accent played fill + buffered fill + chapter tick markers
  + a hover time tooltip; click/drag to seek (pointer events, works in both
  modes — commits the seek on release so transcode only reloads once).
- Playback speed popover (0.5–2×) and persistent volume + speed + captions
  across videos (localStorage).
- Captions (CC) toggle for overlaid <track> subtitles (off by default; the
  searchable 📄 transcript pane is unchanged), PiP, fullscreen.
- Auto-hiding controls + cursor after idle while playing; a center play/pause
  flash; gradient scrim.
- Expanded keyboard: space/k, j/l ±10, ←/→ ±5, ↑/↓ volume, m, c, p, f,
  < > speed, 0–9 seek-to-percent — now active for direct videos too (they
  previously relied on native controls).

Verified: node --check passes, release builds clean, live 8081 serves it, and
a headless screenshot of the control chrome (extracted CSS) renders correctly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 19:59:02 -07:00
Luna
816da05962 web: persist login sessions in SQLite so a restart doesn't log everyone out
Sessions were an in-memory HashMap seeded empty at startup, so every restart
or upgrade invalidated every browser's cookie (the cause of the recent
"settings save → error" report: a restart out from under a logged-in tab).

Mirror sessions to a new `sessions(token, issued_at)` table:
- store issued-at as a UNIX timestamp (the map switches from Instant→u64 so it
  can round-trip the DB; is_authed prunes by wall-clock age vs SESSION_TTL),
- insert on login, delete on logout, clear on password change,
- rehydrate the map at startup via load_sessions(), which also prunes rows
  past the TTL. DB writes are best-effort — a failure just means that token
  won't survive a restart, not that login breaks.

Verified end-to-end: set a password, log in, restart the server against the
same DB, and the original cookie still authenticates (200) while a cookieless
request is still 401. 128 unit + 11 integration tests green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 19:18:23 -07:00
Luna
dd48e899fa web UI: on 401, reload to the login page instead of a cryptic error toast
Sessions are in-memory, so a server restart (or a session TTL expiry) leaves
a still-open SPA tab holding a stale cookie: cached views keep rendering, but
every mutating call 401s and surfaced only as an "authentication required"
toast (e.g. saving Settings) — a confusing dead end.

Make api() treat 401 as "session gone": flash a notice and location.reload(),
which the server answers with the login page. No reload loop (the login page
isn't the SPA), and authed sessions are unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 18:09:49 -07:00
Luna
615c088b43 web UI: maintenance modal → "diagnostics console"
Reskin the Library-health modal into a systems-diagnostics readout, sibling
to the stats observatory and sharing its sx-* section language. Presentation
only — every id/class/handler the logic depends on (dup-chk, sim-chk, at-chk,
dedup-area, autotag-area, the async polling) is untouched.

- A pulsing health verdict ("Healthy" / "N items to review") whose count and
  status colour update live as the async subsystems resolve.
- Four status tiles (Duplicate IDs, Missing assets, Similar content, Unfiled
  groups) with health-coloured dots + top rules; sim/at start pending and are
  filled by their render functions.
- Sections become instrument panels: duplicate/similar groups as cards with
  KEEP/REMOVE pills, missing-asset rows, a gradient dedup progress bar, and
  auto-tag groups with confidence dots. Modal widened to 920px.
- Status colour flows through --mx-c off a data-status attribute, scoped to
  .mx-body so it can't leak onto other nodes; pulse honours reduced-motion.

Verified: JS passes node --check, release builds clean, live 8081 serves it,
and a headless screenshot of a harness running the extracted CSS+JS against a
mock report confirms the verdict math, tiles, pills and panels all render.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-17 00:39:20 -07:00
Luna
6f61821d1e web UI: stats modal → maximalist "observatory" data dashboard
Rebuild the Library statistics modal from plain tiles + flat bars + tables
into a real data dashboard, driven by the same /api/stats payload and the
theme variables (so all 10 themes inherit it).

- Metric cards with giant serif numbers that count up from zero on open
  (size/runtime spin up through their units), an accent top-rule, and a
  glowing hero tile.
- A self-drawing SVG area chart for downloads-per-week: Catmull-Rom-smoothed
  line that animates in via stroke-dashoffset, a gradient area fill,
  gridlines, a date axis, and hover dots with tooltips.
- A growing-column histogram for videos-by-upload-year with value labels.
- A ranked channel leaderboard with animated meter fills, gold/silver/bronze
  ranks, and a Size/Count segmented toggle that re-animates on switch.
- Reveal animations gate behind .sx-go (added post-layout) so they play once
  per open, not on re-render; honours prefers-reduced-motion; modal widened
  to 1040px.

Verified: JS passes node --check, release builds clean, the live 8081 server
serves it, and a headless screenshot of a harness running the *extracted*
dashboard CSS+JS against a mock payload confirms the chart math, count-up,
histogram and leaderboard all render correctly.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 19:29:24 -07:00
Luna
c8cb700f7e web UI: "cinematheque" reskin — embedded editorial fonts + atmosphere + motion
A presentational redesign of the web SPA, appended as a design layer so it
overrides by source order while every colour still flows through the existing
7 per-theme CSS variables — so all 10 themes inherit it and no element
id/class the JS relies on changes.

- Typography: embed Instrument Serif (display) + Hanken Grotesk (body) as
  base64 woff2 (SIL OFL) — offline-safe, no CDN, no privacy leak. Serif
  wordmark/headers/empty-state; clean grotesque for UI.
- Identity: a glowing crimson "recording" dot before the wordmark; a soft
  accent aurora + fine SVG film-grain fixed behind content (negative z, so
  it never sits under text).
- Depth & motion: translucent blurred masthead with a hairline accent
  underglow; cinematic card hover (lift + accent ring + thumbnail zoom +
  shadow); one-time staggered shell reveal; themed focus rings; thin themed
  scrollbars; restrained button hover (no full crimson flood). Honours
  prefers-reduced-motion.

Verified: JS still passes node --check, release builds clean, and a headless
chromium screenshot of the live 8081 server confirms it renders (fonts +
atmosphere + masthead) without breaking the existing layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 16:34:57 -07:00
Luna
983864f421 macOS: osxcross packaging path (scripts/package.sh mac → .app .zip)
Add a `mac` target that cross-compiles an Apple binary via osxcross
(MAC_ARCH=arm64 default, or x86_64), assembles an unsigned yt-offline.app
(Info.plist + Mach-O + best-effort .icns via png2icns), and zips it. The
crypto stack is ring, which builds against the osxcross SDK cleanly.

- Sets the per-target linker + cc-rs CC/CXX/AR env from the osxcross
  wrappers (oa64-clang / o64-clang); discovers the versioned ar triple.
- Gated on the toolchain being present: `all` folds it in only when an
  Apple rust target + an osxcross wrapper + zip exist, and a bare `mac`
  run skips cleanly (exit 0) otherwise — verified.
- Local-only, not in CI: the macOS SDK can't be hosted in a public image.
- Refresh the stale Windows + macOS sections of docs/PACKAGING.md (they
  still described the pre-cfg-gating blocked state) and the CI section;
  note a possible future MacPorts port. ROADMAP 3.1 updated.

Untested end-to-end (no osxcross/SDK on this box); the scaffolding + skip
path are verified and `all` on a stock box is unaffected.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 03:09:30 -07:00
Luna
207013e957 Windows: shippable cross-compiled .zip + console fix + release CI
The tree already links a working x86_64-pc-windows-gnu exe; turn that into
a shipped artifact:

- scripts/package.sh gains a `win` target (and `all` picks it up when the
  mingw toolchain is present) that cross-compiles and zips the exe +
  LICENSE + a README listing the yt-dlp/ffmpeg/mpv PATH deps.
- Forgejo release workflow installs mingw-w64 + zip + the rust target, so
  a tag push produces yt-offline-<ver>-x86_64-windows.zip from the same
  Linux container — no Windows runner needed.
- main.rs: attach_windows_console() (cfg(windows), windows-sys
  AttachConsole) reattaches a release build to the launching terminal so
  --web/CLI output is visible, while a double-click stays windowless
  (release sets windows_subsystem = "windows").
- ROADMAP 3.1 updated: Windows ships; macOS deferred (needs a Mac runner).

Native Linux build + 128 unit / 11 integration tests still green.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-16 02:31:38 -07:00
Luna
8b2578783a Library sorting: download-date sort + grouped sort options (both UIs)
Add "download date" sorting (file mtime, distinct from upload date) plus a
broader set of options. Web select is now grouped via <optgroup>; desktop
gains SortMode::DownloadDesc/DownloadAsc/ChannelAsc with matching arms and
toolbar entries.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 20:15:27 -07:00
Luna
015d03751c Library scan: leave one core free at startup
The startup channel scan used available_parallelism() (every core), which
could briefly peg the whole box on a cold/large library. Cap it at cores-1
so the machine stays responsive; the scan is disk-I/O-bound and runs as a
background task, so one fewer thread costs little. Mirrors the headroom the
fingerprint pass and thumbnail pool already leave.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 18:58:56 -07:00
Luna
5ffdb179f7 Download modal: diff-aware repaint + Retry-all
- Patch progress/speed in place on each tick instead of rebuilding the whole
  modal body, so open per-job logs no longer collapse and the bar doesn't
  flicker while downloading. Full rebuild only on a structural change
  (job added/removed, state or retryability changed, queue length changed).
- "↻ Retry all failed" header button that re-issues every retryable failed
  job (re-snapshots between retries since indices shift).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 17:20:26 -07:00
Luna
9ed62935f5 Download manager: cancel/retry/queue controls + dedup off-switch
Download management UI (web + desktop):
- Cancel a running job (SIGKILL, marked "cancelled" so it isn't auto-retried
  and shows a distinct state rather than a misleading error class)
- Cancel a still-queued job before it starts
- Manually retry a failed/cancelled job (fresh auto-retry budget)
- Live speed · ETA · % on running jobs, parsed from the yt-dlp progress line
- Expandable full per-job log fetched on demand
New endpoints: POST /api/jobs/:idx/{cancel,retry}, GET /api/jobs/:idx/log,
DELETE /api/queued/:idx.

Perceptual-dedup off-switch:
- backup.dedup_enabled (default true) hard-disables the "similar content"
  scan in both UIs; the web scan endpoint returns {disabled:true} and the
  desktop scan no-ops with a note. Wired through all five settings touchpoints.

Docs: README seeking note + ROADMAP recently-shipped updates for the prior
seekable-playback / federation / auto-tagging work.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 16:35:05 -07:00
Luna
c3ff9121f6 web player: seekable transcode playback + custom controls
Transcoded videos stream through a live ffmpeg pipe with no byte ranges, so
the browser couldn't seek/rewind them at all. Fix it end to end:

- get_transcode accepts ?start=<secs> and passes -ss before -i (fast keyframe
  seek), rebasing timestamps to 0 from there.
- The player gets a custom control bar for transcode sources: play/pause, a
  scrubber driven by the known duration, current/total time, ±10s, mute +
  volume, and fullscreen, plus space / arrows / j / l / f keyboard shortcuts.
  Seeking re-requests the stream at the new offset; effective time is
  offset + element.currentTime. Every seek path (scrubber, chapters,
  transcript click, resume, save-position, chapter/transcript highlight) now
  routes through one offset-aware playerSeek()/effTime().
- Direct /files/ videos keep native <video controls> (already seekable).

Verified server-side: start=0 vs start=120 yield different streams.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-13 06:47:40 -07:00
Luna
033572d3bd Federation: read-only browsing of a peer instance's library (3.5)
Configure peers as [[remote]] entries (name, url, optional password) and
browse their libraries from this instance, read-only.

- remote.rs: RemoteClient (blocking reqwest + rustls + cookie jar). Fetches
  the peer's /api/library, logging in first if the peer has a password and
  retrying on a 401. Media is not proxied: the library JSON's media URLs
  (/files/, /music-files/, /api/transcode/, /api/sub-vtt/) are rewritten to
  absolute peer URLs with the peer's read-only feed token appended, so video
  streams straight from the peer to the browser/mpv while only the small JSON
  passes through us.
- web.rs: auth_middleware now also accepts the feed token for GET
  /api/transcode/ and /api/sub-vtt/ (read-only media, same class as /files/),
  so a transcoded peer streams remotely. New GET /api/remotes and
  /api/remotes/:id/library (the RemoteClient runs under spawn_blocking).
- web UI: a 🌐 Remotes sidebar switcher; remote mode is read-only (hides the
  download bar + per-card mutating actions, keeps Play).
- desktop: a 🌐 Remotes screen that fetches a peer's library off-thread and
  plays via mpv on the tokenized URL.
- config.rs: [[remote]] section list; reqwest dep added (rustls/blocking/
  cookies, no system OpenSSL).

Verified end-to-end against a second local instance, including a transcoded
stream returning HTTP 200 via the token. ROADMAP 3.5 + CLAUDE.md updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 17:51:44 -07:00
Luna
d797f2a698 Cross-compile groundwork: make the tree build for Windows (3.1)
`cargo check --target x86_64-pc-windows-gnu` is now green (only the
upstream egui f32:From<f64> warnings). The Linux-only desktop deps are
target-gated so the rest of the stack — which already cross-compiles —
can build off Linux:

- Cargo.toml: ksni and rfd's xdg-portal backend move to
  [target.'cfg(target_os = "linux")'.dependencies]; non-Linux gets rfd
  with its native Win32/AppKit backend (default features).
- tray.rs: cfg-split. The ksni/SNI implementation stays Linux-only; other
  OSes get a no-op `start() -> None`, i.e. windowed-only (identical to the
  Linux no-SNI-host path). TrayEvent/TrayHandle stay cross-platform.
- plex.rs: add a Windows `symlink_file` arm to make_symlink (also fixes an
  unused-variable warning on non-unix).

The rest was already portable (disk_space/statvfs, mpv IPC UnixStream,
chmod/PermissionsExt guards all cfg(unix)). macOS is Unix so those apply
there unchanged. Still not a *shipped* binary: needs a real per-OS tray
backend and a linking CI matrix. ROADMAP 3.1 + CLAUDE.md updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 04:22:18 -07:00