AstroJS port of Subfolio — a no-database file browser that turns a folder on disk into a themeable web gallery. The project has been reimagined from its original Kohana 2.x / PHP 5.6 stack to an Astro static site (Cloudflare Pages target, hybrid-ready).
npm install # Node 24 (see .nvmrc)
npm run dev # astro dev → browse the rendered listing/detail pages
npm run build # astro check (types) + astro build
npm run preview # serve the static build locally
npm run deploy # build + publish to Cloudflare Pages (wrangler pages deploy)Content is authored by dropping files into a content directory and naming them by convention (-t-/-m-/-b- embeds, .link/.cut/.pop/.ftr/.slide/.site/.oplx/.rss enhancers, -hidden, -access). A custom Astro content loader walks that directory at build time and interprets the conventions — this is the port of the old PHP engine (Filebrowser.php + Subfolio.php).
The loader reads SUBFOLIO_CONTENT_DIR, defaulting to the bundled content/examples/ fixture so the repo runs standalone. Point it at a live Subfolio install's directory/ to run against real content.
Set it in .env.content (gitignored):
SUBFOLIO_CONTENT_DIR=/path/to/subfolio/directoryThen run through ./dev-content.sh instead of npm directly:
./dev-content.sh # npm run dev
./dev-content.sh build # npm run build
./dev-content.sh preview # npm run previewThe wrapper is needed because Astro only loads dotenv values at render time — not at config time when the loader resolves the content dir — and the gen-* scripts read no dotfile at all. dev-content.sh promotes SUBFOLIO_CONTENT_DIR from .env.content to a real exported shell var so both build phases see the same value. Plain npm run dev still works; it just falls back to content/examples/.
Why
.env.contentand not.env? Astro auto-loads.envat render time only. With the content dir in.env, a plainnpm run buildproduced a split-brain build: pages came from the fixture while the/directory/raw-bytes route walked the live content tree — leaking the content repo's.git/intodist/and permanently failing two smoke tests. Keeping the value in a file Astro never reads means every build is internally consistent: plainnpmcommands = pure fixture,./dev-content.sh= pure live content.
src/loaders/— the content loader (one module per concern; seeschema.tsfor the emitted entry shape). -src/pages/[...path].astro— catch-all that ports the PHP controller, resolving each entry to a folder/file/single/slide view. -src/pages/directory/[...path].ts— raw-bytes endpoint serving file contents under/directory/<path>. -src/components/— porteddefault-theme listing and per-filekind views. -config/filekinds.yml— extension → kind → view mapping (from upstream). -content/examples/— bundled fixture exercising every convention. -docs/— port plan and reference: ROADMAP, the deployment ADR, and the stack-agnostic behavior specs extracted from the original PHP engine.