release: v0.14.0

Fourteen commits since v0.13.0. See CHANGELOG.md for the full list.

Two additions: `npm run doctor`, which runs the config doctor from a terminal — including inside the shipped image — and exits with the worst finding, so it can gate a deployment script; and a Playwright spec that loads all fourteen admin routes against a production build.

One change, invisible on screen: the admin panel got a form-field layer (#533).

Five fixes: grid.gap on both axes (#513), the dashboard status badge (#539), the journal editor's unsaved-changes guard (#538), essay paragraph markup (#537), and aria-pressed on the panel's switches and pickers.
This commit is contained in:
Ralksta
2026-08-30 15:11:25 +02:00
committed by GitHub
parent a813433891
commit e99d3f2f1b
59 changed files with 8652 additions and 6676 deletions

View File

@@ -7,6 +7,138 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
Releases up to and including v0.9.2 are documented in the
[GitHub releases](https://github.com/ralksta/immich-folio/releases).
## [0.14.0] — 2026-08-30
### Added
- **`npm run doctor` — the config doctor in the terminal**
([#521](https://github.com/ralksta/immich-folio/issues/521)). The doctor
shipped in v0.12.0 as a panel in `/admin`. That is the wrong place for the
case it is best at: a deployment that will not come up, where there is no
panel to open and no admin password to get past. The same checks now run from
a terminal — `AUTH_SECRET`, whether the YAML parses, whether Immich answers
the three calls Folio depends on, whether every published album still exists
and is shared, whether a password is still plaintext or an unusable bcrypt
hash, and whether `content/` can be written.
It runs in the shipped image too, which is where it matters:
```bash
docker compose exec folio npm run doctor
```
The exit code is the worst thing it found — `0` clean, `1` warnings, `2`
errors, `3` the doctor itself could not run — so it can gate a deployment
script. No secret is ever printed, only whether something is set.
Two findings are reported rather than judged, and say so under their own
**NOTES** heading instead of being counted as passed checks:
`TRUSTED_PROXY_HOPS`, which can only be measured against a real request, and
`content/` writability, which is tested as whoever typed the command — on a
Docker deployment that is not the user the app writes as, so the report names
both and points back at `docker compose exec`. An unwritable path stays a
plain error when the CLI does run as the owner.
No check was reimplemented: the CLI calls the same functions as the panel and
only gathers their inputs differently. It needs no new dependency either —
Node strips the types itself, which works because those checks import nothing.
- **CI loads the admin panel in a real browser.** A Playwright spec walks all
fourteen admin routes against a production build and fails on an error
boundary, a missing page body, or anything thrown during hydration. It exists
because of [#542](https://github.com/ralksta/immich-folio/issues/542): a
temporal-dead-zone error at module evaluation took the whole panel down while
the type check, the build and the unit tests all stayed green — none of them
evaluates a client module in a browser, which is the only place that bug is
visible. The spec was checked against the bug: with it reintroduced, the run
names every broken route and reports the original
`Cannot access 'THEME_INFO' before initialization`.
### Changed
- **The admin panel has a form-field layer**
([#533](https://github.com/ralksta/immich-folio/issues/533)). Nothing changes
on screen; this is about what the panel costs to change. It had grown 171% in
thirty days, and three files were two thirds of it. Every field was
hand-written markup: the toggle card existed as fourteen copies, the five card
pickers as five hand-rolled grids, and the grid editor as two 58-line blocks
differing only in which config key they wrote. A new setting cost twenty lines
of copied markup, and changing how a field looks meant editing it fourteen
times.
`ToggleCard`, `OptionGrid` and `GridOverrideFields` replace those copies; the
subpage and album drawers moved out of `PageBuilder.tsx`, which went from
2,290 lines and 46 characters of indentation to 1,153 and 22; and `admin.css`
— 4,855 lines in one file — became a load order over twenty sheets named
after what they style. The CSS class names are untouched throughout, which is
what kept the appearance out of it.
Two things were deliberately left undone rather than half-done: adopting or
deleting the custom `Listbox` (a product decision about a keyboard model, not
a mechanical one), and a `Field` wrapper that would generate an id and wire
`htmlFor` — no admin label does today, so clicking one does not focus its
input. Both are written down on the issue.
### Fixed
- **`grid.gap` moves both axes, and two presets get their spacing back**
([#513](https://github.com/ralksta/immich-folio/issues/513)). The earlier fix
changed the hardcoded `column-gap` in `editorial`, `minimal` and `monograph`
into a `--grid-gap` default, but left the hardcoded `margin-bottom` sitting a
few lines below it in the same three files. In the masonry layout that margin
_is_ the row spacing — a `column-count` layout has no `row-gap` — so the
setting moved the columns and left the rows pinned. It now derives from
`--grid-gap` like everything else. `monograph` keeps its rows deliberately
looser than its columns, so it derives the ratio instead of the value.
The same change also declared `--grid-gap: 12px` on the base grid, which
meant `classic` and `studio-modern` — which asked for `var(--grid-gap, 20px)`
— never reached their fallback and silently rendered at 12px from v0.12.0 on.
Both declare their 20px outright now, as the other presets do.
Verified in a browser rather than by reading the cascade: for all seven
presets, an explicit gap of 50px produces 50px on both axes (60px rows for
`monograph`), and with no gap set each preset renders its own intended
default.
- **The dashboard status badge's colour and words agree**
([#539](https://github.com/ralksta/immich-folio/issues/539)). The badge decided
its colour and its label through two separately-ordered nested ternaries over
the same four inputs, and they disagreed in two states: with Immich
unreachable _and_ the config doctor warning, the colour matched the warning
first and stayed neutral while the words read "System Degraded"; and a refresh
that followed an earlier error left the badge red under "Checking...". Both
are now decided together, most serious first, so a condition cannot be added
to one and forgotten in the other.
- **The journal editor warns before you close it with unsaved work**
([#538](https://github.com/ralksta/immich-folio/issues/538)). It tracked
unsaved changes and used them to drive its save button, but registered no
`beforeunload` handler — so closing the tab mid-entry discarded the work
silently, while the same action in the page builder or the settings editor
asked first. Those two carried the guard as a copy each; all three now share
one, so a fourth editor cannot quietly be the next to forget it.
- **Editing an essay paragraph no longer strips its italics and links**
([#537](https://github.com/ralksta/immich-folio/issues/537)). The essay block
editor rendered its textarea from a stripped copy of the paragraph — it turned
`<strong>` into `**` and replaced every other tag, `<em>` and `<a>` included,
with nothing — and then wrote that stripped text straight back on the next
keystroke. Touching a paragraph destroyed its markup. The transform was a
partial re-implementation of the serializer, which converts the HTML back to
markdown on save and handles `<em>` correctly, so there was nothing to convert
in the editor at all. It now shows the value raw, as the journal editor
already did for the same data.
- **The admin panel's switches and pickers announce their state.** The toggle
cards and the five card pickers — theme preset, photo frame, hero style,
layout, aspect ratio — showed which option was active by colour alone, with
no `aria-pressed`, so a screen reader read a row of buttons without saying
which one was on. `SettingRow` already announced itself; the cards beside it
did not. Fixed while giving the panel a form-field layer
([#533](https://github.com/ralksta/immich-folio/issues/533)), which is what
made it one change instead of nineteen.
## [0.13.0] — 2026-08-25
### Added
@@ -347,6 +479,11 @@ Releases up to and including v0.9.2 are documented in the
change is that their rows finally match their columns. Sites that set `gap`
are unaffected, and `classic` and `studio-modern` keep the 12px they had.
> **Correction.** Two claims above were wrong: the row spacing did _not_
> follow the setting in those three presets, and `classic` and
> `studio-modern` did not keep what they had — they dropped from 20px to
> 12px. Both were fixed later; see the entry under Unreleased.
- **The Immich description can be switched off**
([#506](https://github.com/ralksta/immich-folio/issues/506)). It was served
regardless of `exifOnHover`, on the grounds that a caption is editorial rather