blume@1.3.0
Minor Changes
- a089dbc: Upgrade the AI SDK to v7. The generated Ask AI endpoint now passes
instructionsinstead of the deprecatedsystemoption, and the optional provider peer dependencies moved to their AI SDK 7-compatible majors:@openrouter/ai-sdk-provider@^3and@ai-sdk/openai-compatible@^3. If your Ask AI backend uses one of those providers, upgrade the provider package when you update Blume; gateway-backed setups need no changes. - 56979c0: Built-in image optimization for local images. A relative image reference (
) next to your content is now optimized at build time end-to-end — compressed, converted to WebP, and stamped with intrinsicwidth/heightso the layout doesn’t shift while it loads. sharp now ships as a Blume dependency, so the Astro image service works out of the box under every installer (it was previously unresolvable from the generated runtime under isolated linkers, failing the build with “Could not find Sharp”). Agent-facing surfaces (/<route>.md,/<route>.mdx, llms-full.txt, MCP) rewrite relative references to a new/blume-assets/content/…endpoint serving the original files, so image links in raw Markdown resolve instead of 404ing; the same endpoint serves remote-source assets materialized under.blume/public/blume-assets, which builds previously never shipped. A newimageconfig (domains,remotePatterns) authorizes remote hosts for optimization, mapping directly onto Astro’simageconfig. - bd2b8cb: Custom fonts:
theme.fontsroles now accept any Google/Fontsource/Bunny/Fontshare family by name ({ name, provider?, weights?, fallback? }) and local font files ({ name, variants: [{ src, weight?, style? }] }) alongside the curated slugs — all self-hosted and optimized through Astro’s Fonts API.seo.og.fontsgains a matching local form ({ name, src, weight?, style? }), and when a config setstheme.fontsexplicitly, the generated Open Graph cards now render in the theme’s display and body fonts automatically (explicitog.fontsstill wins;og.fonts: []opts out). Sites that never touchedtheme.fontsare unaffected. - 6e293cf: Publish a Web Bot Auth signature directory from
ai.webBotAuth.keys. The configured public JWKs are served at/.well-known/http-message-signatures-directorywith the registered media type on every build surface (static hosts via_headers, Vercel server builds via a Build Output content-type override), advertised inagent-readability.json, and validated to be public-key-only — a JWK containing private material is rejected at config time. - 64bac00: Publish Agent Skills for discovery from
ai.skills. Point the new config field at a directory of skills (each subdirectory holding aSKILL.md) and the build publishes them per the Agent Skills Discovery RFC v0.2.0: single-file skills verbatim at/.well-known/agent-skills/<name>/SKILL.md, skills with supporting resources as deterministic.tar.gzarchives (execute bits preserved), and a discovery index at/.well-known/agent-skills/index.jsonwith the v0.2.0$schemaand per-skill SHA-256 digests. Artifacts get explicit media types on static hosts, the index is advertised inagent-readability.json, and spec-invalid skills are skipped with a build warning. - 4e7a824: Generate an RFC 9727 API catalog at
/.well-known/api-catalog. The linkset is derived from the site’s configured APIs — each OpenAPI/AsyncAPI reference (anchored at its docs route, withservice-docand, for remote specs,service-descrelations) and the hosted MCP server (with its discovery document as the service description). Served asapplication/linkset+jsonon every build surface, advertised via arel="api-catalog"homepage Link header and inagent-readability.json. Sites with no APIs emit no catalog. - f4e6ce7: Serve Markdown to agents through
Accept: text/markdowncontent negotiation on Vercel server builds. The build splices header-conditional rewrite rules into the Vercel routing config, so a content-page request that preferstext/markdowngets the page’s raw-Markdown mirror at the same URL — withVary: Accepton both variants — andagent-readability.jsonnow advertisescontentNegotiationonly on deployments that honor the header. - 7d426b2: Add
seo.og.siteandseo.og.descriptionoverrides for the generated OG card’s footer URL and subtitle, and acceptfalseon them (and onseo.og.logo) to hide that layer of the card entirely - a730bef: Check DNS-AID agent discovery in
blume audit. Whendeployment.siteis set, the network tier (--url) queries_index._agents.<host>for ServiceMode SVCB/HTTPS records over DNS-over-HTTPS and reports the exact record to publish when none exist, plus whether the answers are DNSSEC-authenticated. SetBLUME_DOH_URLto use your own resolver. - cacab69: Register WebMCP tools on every page. Agentic browsers with a model context (
navigator.modelContextordocument.modelContext,provideContextorregisterTool) get the docs’ read-only surface as in-page tools:search_docs(lazy-loads the configured search client on first call),get_page(a page’s raw-Markdown mirror), andlist_pages(the llms.txt index). The script is tiny and no-ops in browsers without the API. On by default; setai.webmcp: falseto opt out. - 8567927: Advertise the agent-facing surface with an RFC 8288
Linkheader on the homepage. The header points agents atagent-readability.jsonandllms.txt(rel="describedby") and the homepage’s raw-Markdown mirror (rel="alternate"; type="text/markdown"), and is emitted on every surface Blume controls: the dev server, the_headersfile on static builds (Netlify/Cloudflare), and the routing config on Vercel server builds. - 8c46244: Align the MCP server card with the SEP-2127 Server Card extension schema.
/.well-known/mcp/server-card.jsonnow declares the published$schema, a reverse-DNSnamederived from the site host,title,websiteUrl, andremotestransport endpoints (absolute, so present once the site URL is known), alongside initialize-shaped compat fields (serverInfo,capabilities,transports) for scanners built against the proposal’s earlier revision. The advertised tool set and existingtransport/urlfields are unchanged.
Patch Changes
-
a92b52f: Align the table of contents, search results, search preview, and Ask AI scrollbars with the sidebar’s thin scrollbar styling
-
1794284: Update katex to ^0.18.1
-
a4f4a62: Update tailwindcss to ^4.3.3
-
4202f80: Rank Japanese and Chinese search results by whole compound terms rather than their parts. Dictionary segmentation cuts a term like 資金決済法 into 資金 / 決済 / 法, and because Orama scores a bag of words, a page mentioning each fragment somewhere could outrank the page the term is about — on one 65-page Japanese site every law-name query returned its index page first. Han, Hiragana and Katakana runs are now indexed as overlapping character bigrams, and queries on those indexes look for documents carrying all of a term’s bigrams before falling back to the any-token default, so sentence-like queries still return their closest pages. Korean and Thai keep their segmented words, and Latin terms are still indexed whole — though on a Japanese or Chinese index a query of several Latin words now also prefers pages carrying all of them, with the same any-token fallback.
-
04d37f1: Give the code-block copy button a visible surface and cap code block height
The copy button previously rendered transparent over the code, making it hard to see against syntax-highlighted lines. It is now an opaque chip with hover states and a check icon that swaps in after copying. Code blocks taller than 24rem now scroll vertically in place (on the inner code scroller, so the header bar and copy button stay put), with thin theme-colored scrollbars matching the sidebar treatment and a brighter thumb in dark mode. The scroller is keyboard-focusable (the tab stop moves from the pre to the element that actually scrolls), print output renders capped blocks in full, and the Component source pane keeps its own measured height. Copy success is now announced to screen readers via a polite live region, using the existing localized “Copied!” string.
-
76ce58f: Let Mermaid diagrams take the full content width instead of shrink-wrapping to the SVG’s 300px fallback size; small diagrams stay centered and diagrams that set
useMaxWidth: falsestill scroll horizontally -
cbec130: Include
deployment.basein the generated OG card’s footer site text, so a subpath deploy (like a GitHub Pages project site) showsuser.github.io/repoinstead of the bare shared host -
87a3e9a: Drop empty table header rows. GFM requires a header row, so a table that doesn’t want one is authored with blank header cells (
| | |) — that used to render as a dead band above the body; the empty<thead>is now removed. A header cell containing any non-text content (an image, an icon) still counts as non-empty. -
5951ee0: Support @sanity/client v7. The optional peer dependency range is now
^6.21.0 || ^7.0.0, andblume initscaffolds new Sanity projects with v7. -
f3334cd: Harden two code-scanning findings: the WebMCP search tool now strips search-hit markup so no
<fragment (such as a dangling<script) can survive mangled highlighting, and the API catalog trims the configured site origin with the lineartrimEndhelper instead of a quadratic trailing-slash regex. -
67bdef6: Define the
<skill>placeholder in the blume-migrate skill so the codemod and oxfmt-patch commands resolve to the skill’s own directory instead of failing on a literal<skill>path -
1500d32: Resolve the bundled docs path from the installed
blumepackage instead of a barenode_modules/blume/docs. Theblumeandblume-migrateskills now tell agents to locate the package from the workspace that depends on it (viarequire.resolve('blume/package.json')), so the docs lookup works in pnpm workspace monorepos where the package is not installed at the repository root. -
ea3c5fd:
blume audit --verbosenow prints each finding’s full message under the affected page, so link checks name the broken target instead of only the page containing it. -
3afcf7a: Move Blume’s own
zoddependency from v3 to v4 (^4.3.6), the major Astro 7, Scalar, and the MCP SDK already use, so an install resolves one Zod major instead of a v3 copy hoisted beside nested v4 copies. That mixed tree is what made partially updatednode_modules(a restored CI cache that a dependency bump left half-reconciled) resolve Scalar’s schemas against a Zod withoutz.function().optional, failing builds withTypeError: z.function(...).optional is not a function. Resolved configs and frontmatter parse exactly as before: shorthand defaults that Zod 4’s.default()would return unparsed — collapsing blocks likethemeorseoto a bare{}instead of their fully-defaulted shape — now use.prefault(), which keeps Zod 3’s parse-the-default semantics.frontmatter.extendschemas still go through the Standard Schema contract, so any Zod version (or Valibot, or ArkType) works there unchanged.