Vellum/web
mithral 08b894c411 Add a subtitle picker to the video player
The API could serve tracks but nothing asked for them. The picker lists what
the item has and attaches one <track> at a time, removing the previous element
first so switching never accumulates them.

Default is Off. Auto-selecting a track would turn subtitles on for people who
did not ask and cannot tell why, and the failure mode is worse than a missing
feature: a track that loads over a film you did not want subtitled.

Subtitles never block playback. A failed track list hides the picker, and a
track that fails to load leaves the video playing - the media element's own
state is never touched by any of this.

Not verified by a test: the web package has no test runner, only svelte-check,
so verification here is the type and a11y check (0 errors, 0 warnings) plus
the API end-to-end below.

Verified end-to-end against a running server: a video with "Show.en.srt" and
"Show.pt-BR.srt" beside it produced two sidecar rows with the right languages,
the list route returned both, and the track route served real WebVTT - header,
Content-Type text/vtt, and the SRT commas converted to dots.
2026-10-12 11:45:14 -04:00
..
dist Documentation, and make a fresh clone actually work 2026-10-12 00:18:11 -04:00
public Apply the brand: charcoal, blue-violet, purple, and the real logo 2026-10-10 17:41:49 -04:00
src Add a subtitle picker to the video player 2026-10-12 11:45:14 -04:00
.gitignore Web UI: Svelte app, embedded into the binary 2026-10-09 16:40:58 -04:00
embed.go Web UI: Svelte app, embedded into the binary 2026-10-09 16:40:58 -04:00
index.html Make the brand the only theme 2026-10-11 13:41:55 -04:00
package.json web: type process.env in vite.config (add @types/node) 2026-10-10 16:33:05 -04:00
pnpm-lock.yaml web: type process.env in vite.config (add @types/node) 2026-10-10 16:33:05 -04:00
pnpm-workspace.yaml Web UI: Svelte app, embedded into the binary 2026-10-09 16:40:58 -04:00
README.md Web UI: Svelte app, embedded into the binary 2026-10-09 16:40:58 -04:00
svelte.config.js Web UI: Svelte app, embedded into the binary 2026-10-09 16:40:58 -04:00
tsconfig.json web: type process.env in vite.config (add @types/node) 2026-10-10 16:33:05 -04:00
vite.config.ts dev: fix the Vite proxy and add a one-command dev runner 2026-10-10 16:32:32 -04:00

Vellum web

Svelte 5 + Vite + TypeScript single-page app. It is built to a static bundle in dist/ and embedded into the Go binary, so all asset paths are relative (base: './' in vite.config.ts).

Commands

pnpm install
pnpm dev      # dev server on :5173, proxying /api to :7788
pnpm build    # writes dist/
pnpm check    # svelte-check

Run the Go server on :7788 (make run from the repo root) and pnpm dev proxies API and health requests to it, so cookies and sessions behave exactly as they will in production.

Layout

src/
  main.ts                 entry point
  App.svelte              login gate -> shell
  app.css                 design tokens and base styles
  lib/
    api.ts                typed client; mirrors the Go handlers
    session.ts            auth + library stores
    nav.ts                view state
    components/
      Login.svelte
      Shell.svelte        nav + view switch
      Libraries.svelte    library list, create, scan, delete
      LibraryView.svelte  item grid with covers
      Reader.svelte       epub.js reader with progress sync
      Users.svelte        admin user management

The reader resumes from a spine index, which is why progress positions are integers: it round-trips through the polymorphic progress table without needing a text column.