The encode/decode is implemented in a WebAssembly module built from a
small C wrapper around libavif. Bundled libraries (statically linked,
compiled with the WASI SDK):
* libavif v1.4.1 (container + codec glue)
* libaom v3.14.1 (AV1 encoder + decoder)
* dav1d 1.5.3 (AV1 decoder)
* libyuv (Chromium pin) for color conversion
* parson for JSON message passing across the wasm boundary
HDR handling on the encoder:
* SDR images are written as BT.709 / sRGB / BT.601 (8-bit).
* 10-bit and up are written as BT.2020 primaries with PQ (SMPTE
ST 2084) transfer and BT.2020-NCL matrix coefficients, signalled
via CICP.
* Adobe-style SDR+gainmap inputs (e.g. Lightroom HDR exports) are
baked into a single true-HDR image in BT.2020/PQ at 10-bit, with
the CLLI (Content Light Level Information) box carried through so
HDR-capable clients can tone-map correctly.
Limitations:
* Animated input (animated WebP/GIF) is collapsed to its first frame
when re-encoded as AVIF; animated AVIF output is not yet supported.
Fixes#7837
With renderSegments only a subset of pages is rendered, so the resulting
hugo_stats.json would no longer contain elements from the excluded pages,
causing tools like Tailwind to strip classes that are actually in use.
Fixes#14939
When a module provides a config key whose value is a slice (e.g.
cascade or permalinks), and the site config declares the same key as a
map with only a merge strategy marker (_merge = 'deep'), the types do
not match and Params.merge silently dropped the module's value, leaving
the site with no effective cascade or permalink config from the module.
Fix Params.merge so that when the destination value is an empty Params
(IsZero — only the _merge key is present) and the source value is a
non-Params type, the source value is used provided the user-declared
merge strategy is not 'none'. This honours the explicit _merge
directive regardless of the surrounding shallow-merge context.
Closes#13869
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
If a non-default-name file in the config folder parses to a map with a
single top-level key matching the file's basename, unwrap it. This lets
TOML/YAML express slice-typed roots (cascade, permalinks), which can't
have a headless top-level array, and also lets users copy-paste docs
examples that include the root container (e.g. params.yaml with a
top-level params: block).
Fixes#12899Fixes#14882
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
When CSS imports assets via the file loader (fonts, images), the emitted
URLs were relative to the CSS output directory. That broke when the CSS
was inlined into HTML, since browsers then resolved the URLs against the
page rather than the CSS file.
Set esbuild's PublicPath to the CSS output directory joined with the
site base path so URLs work whether the CSS is published as a file or
inlined.
Fixes#14849
These keys are reserved at the top level of front matter, but are
legitimate user params under cascade.params. Only fire the deprecation
when the key was actually set at the top level of the original front
matter.
Fixes#14848
Whitelist now treats any pattern prefixed with "! " (the same negation
prefix used by hglob/predicate) as a deny rule. Deny matches take
precedence over allow, and a whitelist made up exclusively of deny
rules implicitly allows everything it does not deny.
The default security.http.urls now reads:
urls = ['(?i)^https?://[a-z]', '! (?i)localhost', '! @']
i.e. allow URLs whose host starts with a letter (the common
"https://example.com" shape), deny anything that looks like localhost,
and deny URLs with userinfo to foil "http://user@127.0.0.1/" bypasses.
Public IP literals are collateral blocks; users who need them (or their
own private hosts) override security.http.urls as before, mixing allow
and deny rules with the same "! " prefix, e.g.
[security.http]
urls = ['.*', '! ^https?://evil\.example\.com']
Fixes#14792
Add security.node.permissions config to run Node tools (PostCSS, Babel,
TailwindCSS) under Node's permission model, restricting file system access
to the working directory by default.
The binary resolution is simplified to node_modules/.bin → PATH (npx removed).
For both locations, the actual JS entry point is resolved via symlinks (macOS/Linux)
or by parsing npm wrapper scripts (Windows .cmd), then executed as
"node --permission --allow-fs-read=<path> --allow-fs-write=<path> <script>".
Users can opt out by removing "node" from security.exec.allow.
Closes#7287
Filename identifiers for roles and versions were parsed but never applied
to the SitesMatrix. Now they replace the mount's configuration for that
dimension, matching how language identifiers already worked.
Fixes#14756
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This is some logic that's left behind from when we had only one dimension (language) where the common case would be to have one resource set (e.g. an image) and many content translation.
After this commit:
* For sites matrix defined in the content filename (e.g. data.en.js) or in its mount definition, we may use that as a fallback for e.g. German languages if we don't find a better match.
* For content adapters, this is not relevant: Here you must be explicit about this.
* We never auto-fallback on resources from a role/version to another.
* When a page bundle spans multiple roles (e.g. via roles = "*"), we clone its resources to all roles so each gets role-specific paths.
Fixes#14749Fixes#14752
Strip leading whitespace from Hugo context marker lines before
Goldmark parsing to prevent them from being treated as indented
code blocks.
Fixes#12457
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Goldmark v1.8 reports source positions in render hooks.
Use this to build a source map that translates Goldmark positions back to
the original content source.
Now all render hooks' context object implenent the `BaseContext` interface:
```go
type BaseContext interface {
Position() Position
Page() any
PageInner() any
Ordinal() int
}
```
Closes#14663
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a resource transformation result was served from cache (same
options as a previous build), the output file was not re-written to
disk. This caused incorrect output when toggling transformation
options (e.g. minify) back to a previously seen value in server mode.
Fixes#14629
* I had all kinds of issues upgrading CLDR on the old library, so I decided to start fresh in github.com/bep/golocales
* It shaves off about 3 MB of the binary, and package init cost is zero compared to the old localescompressed (5000x faster).
* We will probably move to the `text/**` packages at this point, but this will have to do for now.
The nested loop had dirs as the outer loop and others as the inner loop
with a single counter, causing n to exceed len(others) when multiple
dirs existed. Swap the loop order so each file in others is checked
against all dirs exactly once.
Fixes#14573
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>