Vellum/web
mithral 5281a6690d Play HLS in the web player with hls.js
The server could transcode and serve HLS; nothing could play it. Chrome has no
native HLS support, so without this the feature existed only for curl.

hls.js is the only real option for HLS in Chrome, and it is MIT. It is loaded
with a dynamic import, so the ~185 KB gzip is paid only when a transcode
actually happens - a direct play, which is the common case, fetches none of it.

Safari and iOS play HLS natively, so those get the playlist URL directly and
never load the library at all.

Three failure paths all fall back to the direct file URL rather than leaving a
dead player: hls.js not supported, the dynamic import failing, and a fatal
playback error. Transcoding is an optimisation and must never be able to break
playback - a file that would have direct-played has to keep working when the
encoder or the library is unavailable.

The instance is destroyed on source change and on unmount, so seeking between
files does not leave encoders or MediaSource objects behind.

Verified: pnpm check 0/0, the production build succeeds, and hls.js is emitted
as its own lazy chunk rather than folded into the main bundle.

Not verified in a browser - there is no automated browser test in this repo, so
the actual playback of a transcoded stream is unproven end to end. The server
side of that path is verified separately against real hardware.
2026-10-12 12:07:36 -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 Play HLS in the web player with hls.js 2026-10-12 12:07:36 -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 Play HLS in the web player with hls.js 2026-10-12 12:07:36 -04:00
pnpm-lock.yaml Play HLS in the web player with hls.js 2026-10-12 12:07:36 -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.