Add issue No. 02 "Obsession" + reusable image pipeline
Content - 100 new plates under content/posts/02/ — one recurring red-haired model across 10 editorial registers (Haute Couture, Film Noir, Boudoir, Avant-Garde, Minimalism, Baroque, Street Style, Surrealism, Monochrome, Nocturne), generated with flux-1.1-pro, face-swapped to a consistent identity (FaceFusion), and upscaled (Upscayl + Remacri, 3328x4992). - content/issues/02/_index.md; issue 01 -> status archived; hugo.toml params (issueIds newest-first, issueNumber/Name/Season/Blurb). Structure - Plate bundles grouped by issue: content/posts/<issue>/<slug>/, with build.render:never stubs so /posts/<issue>/ isn't emitted. nginx 301s the legacy flat /posts/<slug>/ URLs to /posts/01/<slug>/. The /posts/ archive uses .RegularPagesRecursive. Image pipeline (scripts/, data/ gitignored) - generate-images.py — Replicate flux-1.1-pro, 960x1440, idempotent. - faceswap-images.py — FaceFusion batch-run, one consistent face. - upscale-images.py — Upscayl + remacri-4x, long edge clamped to 4992. - build-issue.py — prompts JSON -> content/posts/<issue>/*/index.md. Build - CSS now compiled by Hugo's css.TailwindCSS (partials/css.html + templates.Defer); dropped the standalone Tailwind CLI step, concurrently, and the gitignored static/css/main.css. Requires Hugo >= 0.161; Dockerfile collapsed to a single hugomods/hugo:debian-node build stage. pnpm-workspace.yaml: preferSymlinkedExecutables + allowBuilds. Front end - Pagination 8 per page. - Lightbox: brand mark and category link out; robust SPA back/forward (fetch before startViewTransition, single-flight guard, swallow the transition abort rejection, popstate re-renders / reopens the viewer). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DZPmxGywFnAhmYJB1eh9fm
This commit is contained in:
@@ -11,8 +11,8 @@ Editorial, couture, beauty and backstage plates.
|
||||
|
||||
| Layer | Tool |
|
||||
|---|---|
|
||||
| Static site generator | Hugo Extended |
|
||||
| CSS | Tailwind CSS v4 |
|
||||
| Static site generator | Hugo Extended (≥ 0.161) |
|
||||
| CSS | Tailwind CSS v4, compiled by Hugo's `css.TailwindCSS` |
|
||||
| JavaScript | Vanilla (no framework) |
|
||||
| Package manager | pnpm |
|
||||
| Server | nginx (Docker) |
|
||||
@@ -20,32 +20,66 @@ Editorial, couture, beauty and backstage plates.
|
||||
## Development
|
||||
|
||||
```bash
|
||||
pnpm install
|
||||
pnpm dev # Tailwind watch + Hugo dev server (http://localhost:1313)
|
||||
pnpm install # installs @tailwindcss/cli (Hugo shells out to it)
|
||||
pnpm dev # hugo server -D → http://localhost:1313
|
||||
```
|
||||
|
||||
Hugo builds the CSS itself via `css.TailwindCSS` (see `layouts/partials/css.html`),
|
||||
watches `assets/`, and hot-reloads. There is no separate CSS build step or watcher.
|
||||
Requires `node` on `PATH` — Hugo execs `node_modules/.bin/tailwindcss`.
|
||||
|
||||
## Build
|
||||
|
||||
```bash
|
||||
pnpm build # compile CSS → hugo --minify → public/
|
||||
pnpm build # hugo --minify → public/
|
||||
```
|
||||
|
||||
The compiled CSS at `static/css/main.css` is a build artefact and gitignored. Always run `pnpm build:css` (or `pnpm build`) before committing changes that affect Tailwind output.
|
||||
CSS is emitted as a fingerprinted, integrity-hashed resource under `public/css/`;
|
||||
`hugo_stats.json` (the class list Tailwind scans) is generated on every build and
|
||||
gitignored.
|
||||
|
||||
`pnpm-workspace.yaml` sets `preferSymlinkedExecutables: true` so pnpm links
|
||||
`node_modules/.bin/tailwindcss` as a symlink — Hugo rejects pnpm's default shell
|
||||
shim (`binary "tailwindcss" is not a Node.js script`).
|
||||
|
||||
## Content
|
||||
|
||||
Posts live in `content/posts/<slug>/` as Hugo page bundles. Each bundle contains:
|
||||
Posts live in `content/posts/<issue>/<slug>/` as Hugo page bundles (e.g.
|
||||
`content/posts/01/…`, `content/posts/02/…`). Each bundle contains:
|
||||
- `index.md` — frontmatter with `plate`, `title`, `description`, `categories`, `tags`, `issues`, optional `featured: true`
|
||||
- `<slug>.png` — the plate image (used for thumbnails, lightbox, og:image)
|
||||
|
||||
Issues are defined in `content/issues/<id>/_index.md`.
|
||||
`content/posts/<issue>/_index.md` carries `build.render: never` so only `/posts/<issue>/<slug>/`
|
||||
pages are emitted — the per-issue landing page is the taxonomy term at `/issues/<issue>/`.
|
||||
Issue membership is the `issues` taxonomy; each issue also has a branch bundle at
|
||||
`content/issues/<id>/_index.md` (`title`, `description`, `issueNumber`, `season`, `status`).
|
||||
|
||||
### Importing posts
|
||||
### Adding a new issue
|
||||
|
||||
Content is sourced from `~/projects/ginger/posts.csv` and matching images in `~/projects/ginger/images/final/selected/`:
|
||||
1. Write `data/prompts/issue-NN.json` — an array of `{slug, title, category, tags, description, prompt, featured?}`.
|
||||
2. Generate images: `python3 scripts/generate-images.py --prompts data/prompts/issue-NN.json --issue NN`
|
||||
(Replicate `flux-1.1-pro` by default; token from `$REPLICATE_API_TOKEN` or `~/.env`).
|
||||
3. Face-swap one consistent model into every plate:
|
||||
`python3 scripts/faceswap-images.py --issue NN --source ~/Bilder/palina.webp`
|
||||
(FaceFusion at `~/Projekte/facefusion`; add `--enhance` for a sharper face pass).
|
||||
4. Upscale: `python3 scripts/upscale-images.py --issue NN`
|
||||
(Upscayl + the `remacri-4x` model, then clamped to a 4992 px long edge; `--scale 2` or
|
||||
`--max-edge 2880` keeps the committed PNGs lean).
|
||||
5. Build the bundles: `python3 scripts/build-issue.py --issue NN --prompts data/prompts/issue-NN.json`
|
||||
6. Add `content/issues/NN/_index.md`, add `content/posts/NN/_index.md` (`build.render: never`),
|
||||
flip the previous issue's `status` away from `current`, and update `hugo.toml` `[params]`
|
||||
(`issueIds`, `issueNumber`, `issueName`, `issueSeason`, `issueBlurb`).
|
||||
|
||||
Steps 2–4 all write `content/posts/NN/<slug>/<slug>.png` in place and are each idempotent
|
||||
(re-run to finish a failed batch; `--force` to redo). Pass `--out DIR` to stage instead.
|
||||
|
||||
### Importing issue 01
|
||||
|
||||
Issue 01 is sourced from `~/projects/ginger/posts.csv` and matching images in
|
||||
`~/projects/ginger/images/final/selected/`:
|
||||
|
||||
```bash
|
||||
python3 scripts/import-posts.py
|
||||
python3 scripts/import-posts.py # writes content/posts/01/
|
||||
```
|
||||
|
||||
This creates or updates page bundles, assigns plate numbers (sorted alphabetically by filename for stability), normalises descriptions, and copies images.
|
||||
@@ -58,7 +92,15 @@ Key tokens: `--color-paper` `--color-ink` `--color-roux` `--font-display` `--fon
|
||||
|
||||
## Deployment
|
||||
|
||||
The repo includes `nginx.conf` and a `Dockerfile` for containerised deployment. Build the image and serve `public/` via nginx. Analytics (Umami) are injected only when `HUGO_ENVIRONMENT=production`.
|
||||
`Dockerfile` is a two-stage build: `hugomods/hugo:debian-node-*` runs
|
||||
`pnpm install` then `hugo --minify --environment production` (Hugo compiles
|
||||
Tailwind in-process — no pre-build CSS step), and `nginx:alpine` serves the
|
||||
result with `nginx.conf`. Analytics (Umami) are injected only in the production
|
||||
environment.
|
||||
|
||||
`nginx.conf` also 301-redirects the legacy flat issue-01 plate URLs
|
||||
(`/posts/<slug>/` → `/posts/01/<slug>/`); widen its negative-lookahead when
|
||||
adding issue 03+.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
Reference in New Issue
Block a user