[go: up one dir, main page]

Skip to content
Blume is now publicly available.
Blume
Esc
navigateopen⌘Jpreview

Changelog

blume@1.3.1

August 3, 2026

Release

Patch Changes

  • 4d7dc87: Negotiate Accept: text/markdown on the homepage even when it’s a landing page. A user-authored home page has no Markdown source, so agent requests for a markdown homepage previously fell through to HTML; the homepage’s mirror now falls back to the llms.txt index — the machine-readable map of the site — served at /index.md and wired into the dev server, the Vercel routing config, and the homepage Link header’s rel="alternate" entry.
  • ff31ab5: Remove a polynomial-backtracking regex from the font-name slugifier’s dash trim
  • 02eb6c7: Fix the homepage Link header and Vary: Accept never being sent on Vercel deploys. The injected header routes sat after handle: "filesystem" in the Build Output config — the miss phase, which prerendered static responses never reach — so agent-readiness checkers saw no Link header on GET /. Both header routes now ride in the main phase, ahead of static-file matching.
  • de62812: Stamp an x-markdown-tokens header (estimated token count, ~4 characters per token) on Markdown responses, following the Cloudflare Markdown for Agents convention: the raw-Markdown endpoints send it on dev and server-rendered responses, and the Vercel routing config carries it on the negotiated homepage.
blume@1.3.0

August 3, 2026

Release

Minor Changes

  • a089dbc: Upgrade the AI SDK to v7. The generated Ask AI endpoint now passes instructions instead of the deprecated system option, and the optional provider peer dependencies moved to their AI SDK 7-compatible majors: @openrouter/ai-sdk-provider@^3 and @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 (![alt](./diagram.png)) next to your content is now optimized at build time end-to-end — compressed, converted to WebP, and stamped with intrinsic width/height so 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 new image config (domains, remotePatterns) authorizes remote hosts for optimization, mapping directly onto Astro’s image config.
  • bd2b8cb: Custom fonts: theme.fonts roles 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.fonts gains a matching local form ({ name, src, weight?, style? }), and when a config sets theme.fonts explicitly, the generated Open Graph cards now render in the theme’s display and body fonts automatically (explicit og.fonts still wins; og.fonts: [] opts out). Sites that never touched theme.fonts are 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-directory with the registered media type on every build surface (static hosts via _headers, Vercel server builds via a Build Output content-type override), advertised in agent-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 a SKILL.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.gz archives (execute bits preserved), and a discovery index at /.well-known/agent-skills/index.json with the v0.2.0 $schema and per-skill SHA-256 digests. Artifacts get explicit media types on static hosts, the index is advertised in agent-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, with service-doc and, for remote specs, service-desc relations) and the hosted MCP server (with its discovery document as the service description). Served as application/linkset+json on every build surface, advertised via a rel="api-catalog" homepage Link header and in agent-readability.json. Sites with no APIs emit no catalog.
  • f4e6ce7: Serve Markdown to agents through Accept: text/markdown content negotiation on Vercel server builds. The build splices header-conditional rewrite rules into the Vercel routing config, so a content-page request that prefers text/markdown gets the page’s raw-Markdown mirror at the same URL — with Vary: Accept on both variants — and agent-readability.json now advertises contentNegotiation only on deployments that honor the header.
  • 7d426b2: Add seo.og.site and seo.og.description overrides for the generated OG card’s footer URL and subtitle, and accept false on them (and on seo.og.logo) to hide that layer of the card entirely
  • a730bef: Check DNS-AID agent discovery in blume audit. When deployment.site is 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. Set BLUME_DOH_URL to use your own resolver.
  • cacab69: Register WebMCP tools on every page. Agentic browsers with a model context (navigator.modelContext or document.modelContext, provideContext or registerTool) 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), and list_pages (the llms.txt index). The script is tiny and no-ops in browsers without the API. On by default; set ai.webmcp: false to opt out.
  • 8567927: Advertise the agent-facing surface with an RFC 8288 Link header on the homepage. The header points agents at agent-readability.json and llms.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 _headers file 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.json now declares the published $schema, a reverse-DNS name derived from the site host, title, websiteUrl, and remotes transport 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 existing transport/url fields 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: false still scroll horizontally

  • cbec130: Include deployment.base in the generated OG card’s footer site text, so a subpath deploy (like a GitHub Pages project site) shows user.github.io/repo instead 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, and blume init scaffolds 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 linear trimEnd helper 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 blume package instead of a bare node_modules/blume/docs. The blume and blume-migrate skills now tell agents to locate the package from the workspace that depends on it (via require.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 --verbose now 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 zod dependency 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 updated node_modules (a restored CI cache that a dependency bump left half-reconciled) resolve Scalar’s schemas against a Zod without z.function().optional, failing builds with TypeError: 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 like theme or seo to a bare {} instead of their fully-defaulted shape — now use .prefault(), which keeps Zod 3’s parse-the-default semantics. frontmatter.extend schemas still go through the Standard Schema contract, so any Zod version (or Valibot, or ArkType) works there unchanged.

blume@1.2.1

July 30, 2026

Release

Patch Changes

  • a015b0a: Match CJK and Thai content in the default Orama search provider. With i18n.defaultLocale set to a language written without spaces (Japanese, Chinese, Korean, Thai), the search index now uses a word-segmenting tokenizer built on Intl.Segmenter — previously every query in those scripts silently returned zero results because the default tokenizer collapsed the text to no tokens. The fix covers the search dialog, the MCP server’s search_docs tool, and Ask AI grounding, and keeps Latin terms matching case-insensitively on mixed-language sites.
  • 454e67f: Match every hoverable sidebar navigation row — the back rows, routed panel header, and flat group header — to the shared 0.65rem navigation radius.
  • 17d520b: Style the desktop sidebar scrollbar with a thin, theme-colored thumb and a transparent track. The mobile drawer and the page scrollbar keep their platform defaults. Requires Tailwind CSS 4.3 or later, which is now the declared minimum.
blume@1.2.0

July 27, 2026

Release

Minor Changes

  • 217975c: Add a top-level integrations array to blume.config.ts for registering Astro integrations. Entries are schema-validated as an array (each element is left for Astro to validate) and appended after Blume’s built-in integrations in declaration order, with no sorting or deduplication. The generated Astro config loads them through a portable bridge back to blume.config.ts rather than serializing the instances, so function-bearing hooks survive across build, blume dev, config regeneration, and eject. Install and version each integration in the site itself — Blume neither adds it to the runtime’s dependencies nor manages its Astro compatibility.
  • 592af35: Add blume eval, a test suite for your docs. An AI agent — Claude Code by default, Codex with --agent codex, spawned from your own installation with no API keys held by Blume — answers the questions in evals.yaml using ONLY the documentation, served over a private MCP stdio bridge to an agent locked out of its file, shell, and web tools; a judge pass then grades each answer against the expected facts you listed. Any question the docs can’t answer fails CI (relax with --threshold), each failure is anchored to the source page that should answer it, --json emits the validate/audit-compatible machine report, --fix hands the failing report to the agent to edit the docs interactively, and blume eval init drafts a starter evals file from your existing docs. No build or deployment is needed — the docs snapshot is computed from the content tree.
  • 2063196: Let a header tab declare its link target with href. A tab’s path scopes its sidebar section and doubles as the link, so a section whose path isn’t a page of its own falls back to the section’s first page rather than linking to a 404. That fallback only sees the content tree, so a tab pointing at a route generated outside it — the automatic /changelog index, or a custom page under pages/ — lands on the section’s first entry instead of the page the reader expected. Setting href keeps the tab on the declared route; the field is optional and tabs that omit it resolve exactly as before. Declared hrefs are localized and rebased like any other route, so they work under i18n and a deployment.base.
  • 55e176a: Support external Ask AI endpoints on static sites and per-source OpenAPI search, llms.txt, and crawler indexing controls.

Patch Changes

  • 18e1d8d: Stop Cloudflare server builds from declaring unused SESSION KV and IMAGES bindings in the generated wrangler config. Without a configured session driver, @astrojs/cloudflare force-enables KV-backed sessions — making wrangler deploy demand a real KV namespace nothing reads — and defaults images to the runtime Cloudflare Images binding. Blume never reads Astro.session and every HTML route prerenders, so the generated Astro config now sets an inert in-memory session driver and imageService: "compile", which pre-optimizes images at build time with sharp.
  • 38475cb: blume init now adds node_modules/ to the generated .gitignore alongside Blume’s runtime and build output directories.
  • 0c8f78d: Make the entire sidebar navigation back row clickable. When a drilled-in section has no index page, the chevron and title now form a single full-width back button; when it does have one, the title link and back button each fill their side of the row so there are no dead zones, and both get row hover states.
  • 812b09e: Fix nested <Tree> folder chevrons, nested <Accordion> chevrons, and a nested object schema’s “Show properties” toggle reflecting an ancestor’s open state instead of their own. All three rotated or flipped on Tailwind’s group-open: variant, which matches any open ancestor .group — the same leak as the nested sidebar chevron — so a collapsed disclosure inside an expanded one showed an open indicator. Each indicator is now scoped to its own details.
  • 812b09e: Fix a nested sidebar group’s chevron pointing down while the group is collapsed. The chevron rotated on Tailwind’s group-open: variant, which matches any descendant of an open .group — and since every collapsible group in the tree is a .group, expanding a parent rotated the chevrons of its collapsed children too, so the arrow disagreed with the items it was hiding. The rotation is now scoped to the group’s own details, leaving each chevron to reflect only its own open state.
  • d5d6b7a: Fix the EPUB page action failing in dev with epub is not a function. epub-gen-memory’s browser bundle is a browserified UMD, and its dynamic import lives inside node_modules/blume, which Vite’s optimizer scan doesn’t crawl — so in dev it was served as raw ESM, where the UMD finds no exports/define, exposes no default, and strands its callable on window.epubGen. It now joins mermaid in optimizeDeps.include, naming the /bundle subpath that is actually imported, since optimizing the package root leaves that entry unoptimized. Production builds already bundled it correctly and are unchanged.
  • 55e176a: Pagefind now honors search exclusions: pages with search.exclude frontmatter (and hidden pages, unless search.indexing.includeHiddenPages opts them in) no longer appear in local search results.
blume@1.1.4

July 23, 2026

Release

Patch Changes

  • e4506a0: Keep the Ask AI panel open when Escape dismisses the search dialog stacked on top of it. The panel’s window-level Escape listener fired alongside the dialog’s native cancel, closing both surfaces when the user only meant to close search.
  • e4506a0: Stop reporting every blume audit --claude/--codex launch failure as “not found on PATH”. Only a missing executable (ENOENT) gets the install hint now; any other spawn failure (EACCES, EMFILE, …) surfaces as itself instead of being masked by an irrelevant install suggestion.
  • e4506a0: Strip deployment.base before comparing canonical and sitemap URLs in blume audit. Canonicals and <loc>s are emitted as site + base + route while page URLs come from the base-less file tree, so on a subpath deployment every page false-fired CANONICAL_BAD_TARGET, NON_CANONICAL_IN_SITEMAP, and INDEXABLE_PAGE_NOT_IN_SITEMAP — and the duplicate-content checks silently skipped every page (each one looked like it canonicalized elsewhere).
  • e4506a0: Percent-decode pathnames before comparing them against the built file tree in blume audit. Sitemap <loc>s are encodeURI’d and URL#pathname re-encodes non-ASCII, while page URLs and file-index keys are raw on-disk names — so a non-ASCII route (e.g. a Japanese slug) false-fired SITEMAP_BAD_URL, and a percent-encoded href false-fired LINK_TO_BROKEN.
  • e4506a0: Probe live URLs under deployment.base in blume audit --url. Page URLs come from the base-less build tree, but the deployed site serves everything (pages, robots.txt, sitemap.xml) under the base — so auditing a healthy subpath deployment produced a wall of HTTP_4XX findings from probing the wrong URLs.
  • e4506a0: Strip query strings and fragments from redirect destinations before blume audit checks them against the build. A working redirect to /guide#setup or /search?q=x was reported REDIRECT_BROKEN because the suffixed path is not a file-tree member.
  • e4506a0: Apply basePath to configured redirects before blume audit resolves them. Redirects are authored as if mounted at root and gain the base at build time, but the audit compared them raw against built page URLs that carry the base — so every redirect on a basePath site was reported REDIRECT_BROKEN, while LINK_TO_REDIRECT and REDIRECT_SOURCE_IS_PAGE could never fire.
  • e4506a0: Keep / separators in Sanity and Notion slugs. Slugging deleted slashes along with other punctuation, so a guides/setup slug was mashed into guidessetup — and two documents whose slugs differ only by a slash silently overwrote each other. Segments are now slugged individually.
  • e4506a0: Stop promoting a title="…" embedded in another code-fence meta attribute’s quoted value (caption='set title="X" here' file.ts) to the block title. Other quoted attributes are blanked before the explicit-title scan, so the bare-token title (file.ts) wins as intended.
  • 4294b00: Carry the resolved dateFormat config into the runtime data and its BlumeDataConfig type. A configured dateFormat was silently dropped from the serialized site data, so the date stamps always rendered the default long style, and blume check failed with ts(2339) on data.config.dateFormat in the generated catch-all page.
  • e4506a0: Fall through to the next platform env var when one is set but empty. VERCEL_PROJECT_PRODUCTION_URL="" dead-ended the chain before VERCEL_URL (same for Netlify’s URL/DEPLOY_PRIME_URL/DEPLOY_URL), leaving deployment.site unset so canonicals, OG images, and the sitemap silently switched off for that deploy.
  • e4506a0: Leave the .blume/node_modules junction alone when it already points at the right target. It was deleted and re-created on every dev regeneration in the split-install layout, opening a window in which the dev server’s module resolution raced a missing node_modules and intermittently failed with “Cannot find package”.
  • e4506a0: Watch blume.config.ts, theme.css, and components.ts via their parent directory in blume dev. Watching the file path tracks the inode, so a rename-replace save (vim and most “atomic save” editors) orphaned the watcher after the first save — every later edit was silently ignored until the server restarted.
  • e4506a0: Localize internal navigation.featured hrefs per locale, like header tabs. A pinned /changelog link rendered on /fr/… pages always targeted the default-locale route, kicking the reader out of their language.
  • e4506a0: Stop opening a phantom code fence on a line-leading inline backtick span. A paragraph line like ```inline``` is not a fence opener (CommonMark forbids backticks in a backtick fence’s info string), but the heading/link scanner treated it as one and silently dropped every heading and link after it from the TOC, search index, and anchor validation.
  • e4506a0: Stop misreading a body-leading thematic break as front matter in heading extraction. A stripped body opening with --- followed by a blank line lost every heading up to the next --- line — missing TOC and search entries, and false BLUME_BROKEN_ANCHOR findings from blume validate.
  • e4506a0: Strip the locale directory from a shared .$ file’s nav path with the dir parser. fr/changelog.$.mdx kept its fr/ segment, silently routing the default locale’s record inside the French URL namespace, the French copy to /fr/fr/changelog, and conjuring a spurious “Fr” sidebar group.
  • e4506a0: Skip images that are themselves links when wiring click-to-zoom. A linked image ([![alt](/shot.png)](https://example.com)) navigates on click, so the zoom binding only flashed an overlay in the instant before navigation while the cursor-zoom-in affordance promised a zoom that never happened.
  • e4506a0: Extract markdown link targets with balanced parentheses and image-wrapped labels intact. [wiki](https://en.wikipedia.org/wiki/Foo_(bar)) was truncated at the inner ) and reported as a broken link, and the outer target of [![alt](/img.png)](/target) was never validated at all (the nested image’s own target still is).
  • e4506a0: Make the MCP tools’ contract hold together: search_docs hits now include the route the tool description promises (alongside url), and get_page accepts a full URL or a base-prefixed path — an agent following “pass a route from search_docs” no longer gets “No page found” for a page that exists on a site with deployment.site or deployment.base configured.
  • ff0b2b0: Fix .md pages serving stale content in blume dev. The generated dev config kept Vite’s watcher out of Astro’s cache dir, which suppressed the data-store.json change events Astro relies on to invalidate content in a running dev server — so edited Markdown bodies (rendered into the data store at load time) kept serving the old HTML even after a hard reload, while .mdx pages (rendered through their own module) updated fine. The watcher ignore is now scoped to migrated (content.root: ".") projects, the only layout whose glob loader would otherwise churn on Astro’s own cache writes.
  • e4506a0: Pair backtick code in OpenAPI descriptions the way CommonMark does — a run only closes on an equal-length run. A lone inline backtick followed by a code fence used to “close” on the fence’s first backtick, leaving {/< in the surrounding prose unescaped (an MDX compile error that fails the operation page’s build) and entity-escaping the fence body.
  • e4506a0: Stop escaping > in OpenAPI descriptions rendered to MDX. It isn’t MDX-special on its own, and escaping it turned a common > **Note:** … blockquote into a literal “> Note:” paragraph.
  • e4506a0: Give every distinct OpenAPI tag a unique slug. Slugging strips all non-ASCII, so two non-Latin tags (ペット, 注文) both collapsed to operations — merging their routes and sidebar groups and dropping the second tag’s overview section. Colliding slugs now gain -2, -3, … in first-seen order.
  • e4506a0: Leave shifted and alted keyboard chords to the browser. Ctrl+Shift+I (DevTools) toggled the Ask AI panel and Ctrl+Shift+K (Firefox web console) opened the search dialog, because the ⌘I/⌘K matchers ignored the Shift and Alt modifiers.
  • e4506a0: Resolve explicit sidebar refs written with a trailing slash. A hand-written "guides/" normalized to /guides/, missed the slashless /guides route, and the item was silently dropped from the sidebar with no diagnostic.
  • 6554485: Keep the docs sidebar scrolled to the current page across navigations. Each page load previously reset the sidebar’s own scroll container to the top, so on long sidebars the viewport visibly jumped away from the link you just clicked. A pre-paint inline script now centers the active link when it would otherwise be out of view — this also fixes deep links landing with the current page’s link below the fold. Short sidebars, and pages whose active link is already visible, are untouched.
  • e4506a0: Only unwrap a single rendered paragraph in <Prompt>, <Frame>, and <Tooltip>. The greedy unwrap matched across multiple paragraphs, injecting unbalanced </p>/<p> tags via set:html — a multi-paragraph description, caption, or tooltip label broke the surrounding layout when the parser re-parented the stray tags.
  • e4506a0: Dedupe repeated <Update> ids on a page, mirroring the accordion id dedupe. Two entries labeled “Bug fixes” produced duplicate DOM ids, so the second entry’s self-anchor permalink jumped to the first one; later duplicates now gain -2, -3, … and their header anchors follow.
  • e4506a0: Make useAskAI().reset() revoke the in-flight stream, matching the built-in island. Resetting mid-answer used to let the next chunk re-append an orphaned assistant bubble onto the emptied conversation, and a fetch error after reset resurrected the entire pre-reset history. The request is now aborted and stale writes are discarded.
blume@1.1.3

July 22, 2026

Release

Patch Changes

  • 42522fc: Downlevel <Component> to its example’s source in agent-facing Markdown. The /<route>.md mirror, llms-full.txt, and the MCP get_page tool now render <Component path="…" /> as a fenced code block of the example’s source (the same code the on-page “Code” tab shows) instead of leaving the raw JSX tag, so agents reading a page get the component’s code rather than an opaque element. An unknown path (or a missing path) is left verbatim, and a same-name ai.markdownComponents serializer still overrides the built-in.
  • a71f70e: Add a dateFormat config option for the “last updated” stamp and the changelog timeline. Both surfaces previously hardcoded dateStyle: "long"; they now share a configurable pass-through to Intl.DateTimeFormat options, defaulting to { dateStyle: "long" } so existing sites are unchanged. Set a preset (dateFormat: { dateStyle: "medium" }) or a numeric house style (dateFormat: { year: "numeric", month: "2-digit", day: "2-digit" }); dates still render in the site’s locale and in UTC unless a timeZone is given.
  • 68fc939: Harden the agent-facing code-fence helper against a polynomial-time regex (ReDoS). Trailing newlines are now stripped with an unambiguous pattern, so example source with many interior blank lines can’t force quadratic backtracking.
  • ee77cfd: Stop long OpenAPI routes from overflowing the native API reference layout. An operation’s heading now wraps a long METHOD /path title instead of clipping it off the content column, and the overview list rows stack the summary over the route (each getting the full row width) and wrap a long route inside the card — dropping the duplicate path that overlapped the label when a spec sets no summary.
  • 93a94a2: Fix two responsive/mobile layout issues. Twoslash code blocks now wrap their lines on narrow screens instead of pushing the page sideways (hover popups still escape as before), and a long site title in the header now truncates on one line instead of wrapping into the fixed-height bar.
  • a27c543: Add a scalar passthrough object to the openapi and asyncapi config blocks (Scalar renderer). Any Scalar configuration set there is forwarded verbatim to the embedded <ScalarComponent>localization (to translate Scalar’s own UI), agent, hideTestRequestButton, orderSchemaPropertiesBy, and the rest. Options in the scalar object win over Blume’s derived spec/theme config, making it a full escape hatch to Scalar’s API; the dedicated theme field remains the ergonomic shorthand.
  • fa07dc4: Create the .blume/node_modules dependency junction when an isolated linker (Bun’s isolated mode, pnpm) dedupes the workspace’s own astro dependency to Blume’s copy. The walk from .blume/ found the “correct” astro through the workspace’s direct-dep symlink — in a directory holding none of Blume’s integrations — so the junction was skipped and the build died on Cannot find module '@astrojs/mdx'.
blume@1.1.2

July 20, 2026

Release

Patch Changes

  • 6cf2995: Allow custom Shiki theme objects in markdown.codeBlocks.theme.light and markdown.codeBlocks.theme.dark. Custom themes now flow through fenced code, inline highlighted code, <CodeBlock>, and <Diff> alongside bundled Shiki theme names.
  • cb30a40: Recognize the localized/based root tab when scoping the sidebar. With i18n enabled and header tabs configured, a non-default locale’s tabs arrive localized (/ becomes /en), but render-time scoping compared the active tab against a bare / — so on any /en/... route the root tab was misread as a section tab, and a root-level (group) folder (whose path is exactly the locale prefix) collapsed the sidebar to that one group. The same bare comparison blanked the sidebar entirely under a basePath with a root tab. The navigation now carries its root in the tabs’ own path space (/, /en, /docs) and the sidebar scoping compares against it, so non-default locales show the full tree minus tab-owned sections, matching the default locale.
  • aa3f588: Size <Component> preview panes to the rendered example instead of the source line count. The generated frame page now observes the example with a ResizeObserver and reports its height to the docs page, which applies it to both the Preview and Code tabs — so short sources that render tall UIs no longer clip, and long sources that render small components no longer float in dead space. The line-count estimate remains the SSR/no-JS initial height (288px floor, 400px ceiling; the measured height is unceilinged up to the viewport), with a height transition so the settle on lazy load doesn’t snap. Examples that resize after load keep the pane in sync, and viewport-tracking examples (h-screen) can’t feed the measurement back into unbounded growth.
  • 103733d: Re-point a cache-restored .blume/node_modules junction that resolves a superseded Blume install. A restored build cache (e.g. Vercel’s) could resurrect the junction pointing into the previous release’s store directory; because releases rarely bump Astro, every astro-based health check passed straight through the stale link, and the freshly generated Astro config then imported the old package — crashing on any export added since (blumeTwoslashTransformer is not a function). The junction is now dropped and relinked whenever the directory behind it holds a blume other than the one running.
blume@1.1.1

July 19, 2026

Release

Patch Changes

  • b5ed87a: Add seo.og.titles to name the generated Open Graph card of a custom .astro page, keyed by route. A custom page has no frontmatter to read, so its card was titled by humanizing the last URL segment — turning /cli into “Cli” with no way to say “CLI”. An entry here wins over the humanized segment; "/" addresses the home, whose card otherwise carries the site title.
  • 52cfa77: Give each GitHub-sourced changelog release page a unique meta description derived from its release notes, instead of every release falling back to the site-wide description. The summary is the notes reduced to plain text — section headings (“### Patch Changes”) and changesets’ commit-hash bullet prefixes dropped, code fences and link syntax stripped — then cut at a word boundary to fit the 110–160 character search-snippet range blume audit checks for. It’s carried as seo.description, so it feeds the meta/OG/Twitter description tags without adding a visible lede paragraph to the page.
  • 20ae16d: Fix blume check failing on generated files under a strict tsconfig: island and example wrappers now mirror the wrapped component’s props onto Astro.props so required props type-check through the spread, and the OG endpoint’s customRoutes array is explicitly typed so an empty list is no longer an implicit any[].
  • 9a6345f: Decouple Twoslash from the project’s hoisted TypeScript so sites can use TypeScript 7 (tsgo). The generated Astro config now wires in blumeTwoslashTransformer from blume/markdown, which compiles Twoslash fences with Blume’s own pinned classic TypeScript (passed explicitly as tsModule plus tsLibDirectory for the default lib files) instead of resolving whatever typescript the surrounding project installed — under TS7 that package’s main export is a version stub with no compiler API and no lib.*.d.ts files, so any twoslash fence crashed the build.
  • 32ced54: Evaluate {frontmatter.*} prop expressions when downleveling components for agent-facing output. Serializers — built-in and ai.markdownComponents — now receive the same values the rendered page shows instead of empty props, and the page’s front-matter is exposed on the serializer context.
  • ff37999: Stop blume audit flagging the changelog RSS feed link in llms.txt as a stale entry. The stale-entry check compared each llms.txt target against built pages only, but the generator itself links non-page assets — the changelog RSS feed — so a target the static file index serves now counts as valid, the same way redirect targets may land on a served asset.
  • f322ac1: Warn when an index page’s own frontmatter title diverges from its folder’s explicit meta.title. The two are resolved independently, so a translator can update one and forget the other — the sidebar looks right while the page’s own <title>/heading stays stale. Reported as BLUME_NAV_INDEX_TITLE_MISMATCH from blume doctor/blume check. A mismatch is reported once — untranslated pages filled in from the fallback locale are exempt, since their fix is translating the page, not editing the fallback locale’s frontmatter.
  • eb25103: Link Blume’s nested integrations into the generated runtime when npm’s split install hoists astro away from them. An overrides astro pin plus an incremental npm install — the exact steps the Astro-conflict warning recommends — hoists astro to the project root while @astrojs/mdx and Blume’s other deps stay nested under node_modules/blume/node_modules, so fresh checkouts and blume build --isolated failed with Cannot find module '@astrojs/mdx'. The dependency link now probes for the integrations instead of astro alone and links the nested set, letting astro keep resolving from the hoisted copy.
  • 80dc1c1: Fail blume build (exit 1) when any page fails frontmatter validation, instead of silently dropping the invalid pages and reporting a green build. Pass --no-strict to restore the old lenient behavior — the build then warns how many pages were dropped instead of printing an unqualified success.
  • 7c75bb8: Warn when two sidebar siblings (pages or folders) resolve to the same explicit or numeric order, instead of silently falling back to an alphabetical tiebreak. Reported as BLUME_DUPLICATE_SIDEBAR_ORDER from blume doctor/blume check and any other diagnostics consumer.
blume@1.1.0

July 19, 2026

Release

Minor Changes

  • 9fec53f: Add blume audit, an offline site audit that replaces a hosted SEO crawler.

    blume audit reads the built dist/ HTML, joins each page back to the .mdx it came from, and reports SEO and site-health issues that name both the URL that is wrong and the front matter line that fixes it:

    ⚠ Meta description too long or too short   5 pages
        /docs/configuration/export    content/docs/configuration/export.mdx:3
        fix: Rewrite `description` in the frontmatter to fit the length range.

    It runs 87 checks across content, duplicates, indexability, links, redirects, social tags, localization, assets, sitemap, robots.txt, structured data, and AI discovery. Findings are rolled up by check rather than dumped per page, and any tier that didn’t run says so. The check set deliberately skips things that can’t happen to an Astro-built site (missing hashed bundles, rel=nofollow) in favor of checks a crawler can’t do — broken #fragment anchors, draft: true pages that shipped, llms.txt held to the sitemap’s standard, and Open Graph images verified as bytes.

    Flags:

    • --url <origin> also probes a live deployment for what dist/ can’t show (bad rewrites, missing compression, X-Robots-Tag deindexing).
    • --external probes outbound links.
    • --claude / --codex write the JSON report and open the agent interactively to fix each finding at its source.
    • --fail-on <severity> (default error) as the CI gate, plus --only/--skip, --json, --verbose, and --list-checks.

    blume validate is unchanged — it remains the fast source-level link check that needs no build.

Patch Changes

  • 3aee378: Strip trailing slashes from the deploy adapter root with a linear scan instead of a /\/+$/ regex. The old pattern could backtrack polynomially on a root path containing long runs of / (CodeQL js/polynomial-redos); the new trim is O(n) and yields the same single-trailing-slash directory URL.

  • f779fd5: Make blume audit --claude/--codex work on Windows: npm installs the agent CLIs as .cmd shims that Node only runs through a shell, and cmd.exe can’t carry the multi-line prompt as an argument — the handoff now writes the prompt to a file and launches the shim with a one-line pointer, and a missing executable still gets the install hint instead of a raw ENOENT

  • d99726a: Account for deployment.base throughout the audit: link, sitemap, hreflang, llms.txt, asset, and og:image checks now strip the deployment base from emitted URLs before comparing them to the built file tree, instead of reporting every internal link and sitemap entry as broken on subpath deploys

  • d99726a: Make the audit’s double-slash check able to fire on the case it was written for: an href like //docs/x from a trailing-slash base is now flagged (once per target) instead of being silently skipped as a protocol-relative external link

  • d99726a: Replace a raw NUL byte in the audit’s duplicate-content grouping key with the \u0000 escape, so the shipped source is valid text (git diffed it as binary and grep skipped it)

  • d99726a: Exempt the home page from the audit’s orphan-page check when it lives under a basePath/docs was reported as an orphan even though / never is

  • d99726a: Stop reporting a redirect to a served static file (/old-whitepaper/files/whitepaper.pdf) as broken — the audit now resolves redirects against files as well as pages

  • d99726a: Keep a gap between the URL and source-file columns in the audit report when a URL reaches the column width, instead of fusing them into one string

  • d99726a: Fix two robots.txt matching gaps in the audit: Disallow: /docs*$ now matches everything under /docs (the trailing wildcard absorbs the anchor), and trailing-slash rules like Disallow: /page/ are matched against the sitemap <loc> as served instead of a slash-stripped copy

  • d99726a: Only report ROBOTS_META_UNEXPECTED when the robots meta actually blocks indexing — a page declaring index, follow is no longer claimed to “not be indexed”

  • 8d7e779: Format the changelog timeline’s dates in the configured locale. The timeline hardcoded en, so an i18n site showed two languages at once: a page’s “last updated” stamp honored the locale while /changelog stayed English. /changelog is an unlocalized route whose chrome already renders in the default locale, so its dates now follow that same locale. English sites are unaffected.

  • d518958: Add inline and param props to the Tabs component. inline renders borderless — a tab strip on a full-width rule with the content flowing beneath as prose — instead of the bordered box. param syncs the active tab to a URL query param instead of the hash; because each group owns its own param, several Tabs can share a page and every selection is deep-linkable (a link ending in ?install=windows opens on that tab). Existing boxed, hash-synced Tabs and CodeGroup are unchanged.

  • d9dfdba: Stop shipping underscore-prefixed .astro files in pages/ as routes. Blume injects user pages itself and globbed every .astro file, so private partials — shared layouts and home-page sections like pages/_home/Hero.astro or pages/_FeatureBrowser.astro — were each built into their own HTML page. Page discovery now honors Astro’s convention: any file or folder whose name starts with _ stays importable but is never routed.

  • 437ce03: Fix blume dev under pnpm’s default isolated linker. The generated runtime now checks Astro through the same physical node_modules ancestor lookup used by its ESM config, instead of mistaking pnpm’s CommonJS-only NODE_PATH exposure for a resolvable astro/config import.

  • 9aad3e6: Add frontmatter.extend: opt-in custom frontmatter keys, each validated by a user-supplied schema. Page frontmatter stays strictly validated by default; a project can now declare extra keys (e.g. owner, reviewedAt) in blume.config.ts, mapped to schemas consumed through the Standard Schema interface — so Zod (any version the project installs), Valibot, and ArkType all work. Declared keys are validated on every page (mark them .optional() to relax), validated values are preserved on each page record’s custom field, and every other key keeps the strict typo-catching behavior.

  • 368b258: Emit a _headers file for static builds so hosts serve the raw AI-ready endpoints with an explicit charset=utf-8. Blume’s /<route>.md, /<route>.mdx, and .txt outputs (llms.txt, llms-full.txt) are valid UTF-8, but common static hosts serve them as text/markdown / text/plain with no charset — so browsers fall back to Windows-1252 and non-ASCII docs (Japanese, accented Latin, …) render as mojibake when the raw URL is opened directly. The new _headers pins the same charset=utf-8 Content-Type the dev/server runtime already sends; Netlify and Cloudflare (Pages/Workers static assets) honor it, and hosts that ignore _headers (Vercel, S3) are unaffected. The globs carry any deployment.base/basePath stack, and a _headers you ship in public/ is left untouched — exactly like _redirects.

  • d99726a: Prefix the _headers .txt charset rule with only deployment.basellms.txt/llms-full.txt are served at the deploy root, so a basePath deployment shipped a /docs/*.txt rule that matched nothing and left the mojibake fix inert

  • a4453e4: Upgrade Takumi to v2 via takumi-js. Emoji in titles now render as Twemoji glyphs, fetched once per glyph per build. The OG card palette accepts any CSS color, matching theme.accent — a color the renderer can’t parse now fails the build instead of silently falling back. renderOgImage (exported from blume/og) now returns a Uint8Array rather than a Buffer.

  • 548bdd7: Link the RSS feeds from llms.txt. The generated index mirrored the docs navigation but never referenced the per-content-type feeds (e.g. /blog/rss.xml), so an agent reading llms.txt had no pointer to fresh blog posts or changelog entries. The index now closes with an ## RSS Feeds section listing each configured feed that has pages, under the same condition the feeds themselves exist — RSS enabled and an absolute deployment.site — and carrying any deployment.base subpath. This mirrors the artifacts.feeds list already emitted in agent-readability.json.

  • d99726a: Fix a data-loss bug in the blume-migrate Mintlify codemod: renaming a key into a parent block that appears earlier in the frontmatter (e.g. canonical into an existing seo:) deleted the wrong line and left the source key behind; icon remaps also now preserve trailing comments, and the skill references no longer document the pre-1.0.3 top-level mcp config

  • 8769dd1: Add seo.og.fonts to load Google Font families into the Open Graph card renderer. Takumi’s built-in font covers only Latin, so a non-Latin page or site title (CJK, and so on) rendered as tofu with no way to fix it. List the families by name — bare strings, or { name, weight, style } for weight/style — and Blume fetches them from Google Fonts at build via Takumi’s googleFonts helper, registering only the glyph subsets each title uses. Latin text renders unchanged.

  • d99726a: Add seo.og.fonts to the OgConfig authoring type — the schema accepted it but TypeScript rejected it in blume.config.ts, making the documented CJK/tofu fix untypeable — and correct the palette doc comment to say any CSS color works, not just hex

  • de6403c: Fix the OG image build failing with “Cannot find native binding” on Vercel (Linux). The googleFonts OG-font loader is imported from takumi-js/helpers, but only the bare takumi-js was externalized for the static-prerender Vite environment — which matches by exact specifier, so the subpath (and the native @takumi-rs/core backend it pulls in) got bundled into the prerender chunk, relocating the .node binding lookup. Externalize takumi-js/helpers and the @takumi-rs/* packages so the native backend always resolves from node_modules at runtime.

  • bb9737e: Render authentication requirements in the native OpenAPI reference. Operations that declare security requirements — their own security, or the document’s root default — now show an Authorization section above their parameters: the credential’s carrier (Authorization header, API-key header/query/cookie), a human label per scheme type (Bearer token, Basic auth, API key, OAuth2, OpenID Connect, Mutual TLS), the scheme’s description, and OAuth scopes. Multiple requirement alternatives render as “or” groups (schemes within one requirement are required together), an empty {} requirement marks auth as optional, and security: [] on an operation keeps it public with no section. The generated code samples now send a matching placeholder credential (e.g. -H "Authorization: Bearer YOUR_TOKEN"), with a spec-declared explicit header parameter still taking precedence, and a query-borne API key appended to the sample URL. Previously the renderer ignored security entirely, so authenticated endpoints were indistinguishable from public ones.

  • d99726a: Align the accessible heading level of the Authorization and Parameters sections with Request body and Responses on API reference operation pages

  • d99726a: Stop duplicating a query API key in request samples when the spec also declares the credential as an explicit query parameter — the parameter’s own example now wins, matching the header behavior

  • e6be2f6: Match unordered-list bullets to ordered-list numbers in prose. --tw-prose-bullets now defaults to --blume-muted-foreground (like --tw-prose-counters) instead of --blume-border, which rendered bullets much lighter than the numbers beside them.

  • d99726a: Normalize deployment.base before composing it into redirect targets — a trailing-slash (/base/) or bare (base) value produced /base//new or relative destinations in Astro redirects and the platform redirect files

  • 93ea41b: Fix redirects escaping the site under deployment.base. A redirect’s to was only ever rewritten with basePath, never with the deployment base, so with base: "/docs" a to: "/new" emitted a redirect to /new — outside the base, 404ing on a subpath deploy (GitHub Pages project sites, most commonly) with no build-time error. Astro applies base when it builds the match pattern for from, but resolves a destination either by regenerating it from a matching route’s segments (which carry no base) or by passing it through verbatim — neither prepends base, so Blume now applies it to to itself. The two bases also compose correctly: deployment.base and basePath set together stack as {base}/{basePath}, which the old front-prepend could not produce in that order.

    The static host redirect files (_redirects, vercel.json, blume-redirects.json) had the mirror-image bug on the other side: they are matched against the real served URL, but from was written without the deployment base, so it never matched. Both sides now carry the full stack. Redirects are authored root-relative in every case, and a base already written into to by hand is preserved rather than doubled.

  • d99726a: Warn when a search.popular link uses an image or inline-SVG icon — the client search island can only render built-in icon names, and the silent fallback to the file glyph was exactly what the validator was meant to catch

  • 1c18379: Add search.popular to curate the Cmd+K empty-state link list. When set, each { href, label, icon? } entry replaces the default first-six sidebar pages — useful on multi-tab sites where sidebar order surfaces the wrong section. Each href is authored root-relative and picks up basePath automatically (external URLs pass through); icon takes a built-in icon name and defaults to a file glyph. Omit or leave empty to keep the sidebar fallback.

  • ea4c560: Stop headings inside a <Prompt> block from leaking into the page’s table of contents. Prompt.astro renders its children into a permanently hidden node (used only to build the copy-to-clipboard and Cursor-deeplink text), but extractHeadings had no way to know that — any ## inside a <Prompt> was extracted as a real page heading and appeared in the “On this page” sidebar, linking to content that never renders visibly. Heading extraction now tracks <Prompt>/</Prompt> nesting depth the same way fenced code blocks already are, and skips headings while inside one. Tag detection is anchored to line starts — block-level JSX in MDX starts its own line — so a prose or heading mention of <Prompt> never opens a hidden region, and a tag whose attributes span several lines only counts once its closing > shows it isn’t self-closing.

  • c6ca54a: Add a themeable content-column width. A new --blume-content-width token (default 42rem) is exposed as a max-w-content utility through Tailwind’s --container-content theme key, and the article, breadcrumb, mobile table of contents, page feedback, pagination, and last-updated line now use it instead of hardcoding 42rem. Override --blume-content-width to re-measure the whole column at once; the default is unchanged.

  • 31ee259: Bundle the Vercel serverless function with its chunks, virtual middleware, and dependencies. With deployment.output: "server" and adapter: "vercel", the render function (.vercel/output/functions/_render.func) shipped as entry.mjs alone, so any server-rendered request — the Docs MCP endpoint, Ask AI — 500’d at runtime with ERR_MODULE_NOT_FOUND. The adapter resolves both its Build Output tree and its @vercel/nft dependency trace against Astro’s root, which Blume points at the hidden .blume runtime; the trace’s base then excluded the server bundle (which lives under outDir, outside .blume) and collapsed to a single file. The adapter is now shown the real project root, so the trace covers the function’s chunks and node_modules and the output lands at the project root natively. Projects inside a workspace were unaffected — nft’s base search climbed past .blume to the workspace root — so this only ever broke standalone projects.

blume@1.0.4

July 15, 2026

Release

Patch Changes

  • cf8fa22: Fix the spacing inside directive callouts (:::note, :::success, …). The global prose paragraph rule leaks a 1rem margin onto the callout’s paragraphs even though the callout is not-prose, and with a title the body paragraph isn’t the first child — so that margin stacked under the title’s own gap and left a too-large space between the title and the body. The Callout now overrides that margin locally for a uniform, compact gap between the title, paragraphs, and lists. Callouts without a title and normal prose spacing are unchanged.

  • bb5944d: Gate the language icon on data-language so it no longer overlaps the first code line on header-less standalone <CodeBlock>s. The icon transformer runs whenever icons are on, but a standalone block without a title never gets data-language (the header bar that reserves the icon’s space), so the absolutely-positioned icon sat on top of the first line. Fenced code and titled blocks are unaffected.

  • d59be0a: API references (OpenAPI, AsyncAPI) no longer add a header tab automatically. Point a navigation.tabs entry at the reference route to surface it — this also lets you control the tab label and scopes the operations sidebar for the native renderer.

  • f02b94e: Introduce the blume-migrate agent skill. It teaches an AI agent to migrate an existing docs site — Mintlify, Docusaurus, Fumadocs, Nextra, Starlight, or any docs framework — into an idiomatic Blume project: translating the source config to blume.config.ts, restructuring content into filesystem-derived navigation (with redirects for every moved route), rewriting callouts to ::: directives, converting icons to Lucide, inlining snippets, and pointing generated API references at openapi.sources instead of porting endpoint stubs. Ships with per-framework mapping references, a deterministic Mintlify codemod for the icon/frontmatter pass, and monorepo/Vercel integration recipes. The skill is bundled in the package under skills/.

  • a421a1e: Make the generated runtime type-check cleanly under blume check --strict --isolated (and any project whose tsconfig includes the generated files). Previously a valid site could fail with dozens of errors in the generated .blume-verify files:

    • The raw-Markdown, RSS, and OG endpoints imported data.json directly, so TypeScript widened the JSON (navigation kind to string, empty arrays to never[], theme mode to string) and rejected it against Blume’s own types. They now import the typed blume:data virtual module.
    • Endpoint handlers (GET, getStaticPaths callbacks) and the changelog/content-page helper functions had implicit-any parameters; they now carry explicit types.
    • The OG endpoint’s PNG Buffer is wrapped in a Uint8Array so it satisfies the Response body type, and the empty component-overrides module and the blume:examples/blume:examples-theme virtual modules now declare types.

    Also stops blume check --isolated from reloading a running blume dev server: .blume-verify is now in Blume’s ignored-directory set, so generating the isolated runtime no longer trips the content watcher.

  • 6a97cb2: Reserve space for local per-mode SVG logos before they load.

  • 5793ccf: Render Mermaid diagrams in blume dev. Mermaid statically imports dayjs as CommonJS (dayjs/dayjs.min.js), and in dev Vite served that dependency un-pre-bundled, so it exposed no default export and mermaid threw does not provide an export named 'default' — leaving diagrams blank (the production build already handled the interop). Mermaid now goes through Vite’s dependency optimizer, which bundles dayjs with correct CommonJS interop. Because Blume’s import("mermaid") lives inside node_modules/blume — a path Vite’s optimizer scan doesn’t crawl in a standalone install — the diagram library was never discovered on its own, so it’s included explicitly via the nested blume > mermaid form (mermaid isn’t a direct dependency of the generated project).

  • 5c0b0f0: Add custom logo and palette settings for generated Open Graph cards.

  • 1bdc849: Truncate long page titles in the previous/next pagination links so they no longer overflow their pill container, and allow inline code inside table cells to wrap instead of forcing the whole table to overflow horizontally.

  • f2ffcb5: Prerender Cloudflare adapter builds in Node so build-time node: imports resolve. Astro 6 changed the @astrojs/cloudflare default prerender runtime from Node to workerd, which broke Blume prerender on Cloudflare (No such module "node:path" and node:fs usage in Blume’s build-time content tooling). The generated astro.config.mjs now passes prerenderEnvironment: "node" to the Cloudflare adapter. On-demand pages still run in workerd at request time.

  • d11a90c: Stop the Prompt component’s copy button from shifting the layout. On copy, the button’s label swapped “Copy prompt” → “Copied”, and because the button was sized to its text, it shrank — giving the description beside it more room and reflowing it (a two-line description would collapse to one line, then jump back). Both labels now share a single grid cell, so the button is always sized to the wider “Copy prompt” and never resizes when the state changes.

  • fcdc3b3: Match native browser controls, including scrollbars, to the active color theme.

  • 3795fbb: Keep root navigation tabs active on descendant routes unless a more specific tab matches.

  • e5aa042: Use the rounded-blume radius token instead of a hardcoded rounded-full on the PageFeedback and Pagination buttons so they respect the configured theme.radius.

  • 32e29f0: Keep the text caret inside the search input on mobile. The search dialog was vertically centered, so when the on-screen keyboard opened and the viewport shrank, the dialog re-centered and the input moved up while the native caret stayed put — stranding the cursor below the input, over the results. The dialog is now top-anchored on small screens (like DocSearch/cmdk) so the input sits above where the keyboard appears and doesn’t move, and its height is capped to the dynamic viewport so it fits above the keyboard. Desktop keeps its centered layout.

  • 7806565: Resolve a section tab’s link to its first page when the section has no index page, so the tab no longer 404s. A tab’s path still scopes its sidebar section and matches the active tab, but the clickable target now falls back to the first page in the section (sidebar order) when nothing lives at the path itself — e.g. /examples with only /examples/hello-world links to that page instead of a missing /examples.

  • fe21c54: Wrap markdown tables in a horizontal-scroll container so wide tables no longer overflow the prose column. The wrapper is framed with a rounded border and cell padding, and header labels stay on one line, so a clipped wide table reads as scrollable rather than cut off.

blume@1.0.3

July 13, 2026

Release

Patch Changes

  • e2f902c: Render the Ask AI trigger from the shared header instead of wiring it up per page. Custom pages built on PageLayout (a landing page, most of all) never passed the header’s ask slot, so the Ask AI button — and the search modal’s hand-off to it — silently went missing on them while the generated docs, changelog, and reference pages had it. The header now owns the trigger and reads whether Ask AI is on from the config, so every page gets it; pass askEnabled={false} to opt a page out.
  • 66b721b: Move the MCP server config under ai in blume.config.ts, alongside the other agent-facing features. Rename mcp: { … } to ai: { mcp: { … } } — the shape of the block is unchanged.
  • c8ae77e: Fix ERR_MODULE_NOT_FOUND in a deployed server function. Surfacing an adapter’s deploy bundle out of .blume resolved every traced dependency’s symlink against the source dir, so the links pointed into a directory the same step then deleted — the function died on its first external import (Cannot find package '@orama/orama' with Ask AI or the MCP server enabled). The bundle is now copied verbatim, leaving those links relative and internal to it.
blume@1.0.2

July 13, 2026

Release

Patch Changes

  • d62dbd0: Harden the pre-paint inline scripts and the route-normalizing regexes against the issues CodeQL flagged.

    The theme and banner scripts in <head> used to be built by interpolating config values into JavaScript source with JSON.stringify. JSON escaping isn’t a code-context escape — </script> and U+2028/U+2029 pass straight through it — so a crafted banner.id or theme value could break out of the script. Both scripts are now constants and take their values from data-* attributes on their own <script> tag, which Astro HTML-escapes. ReferenceLayout had drifted to its own inline copies of both scripts; it now shares the same module as RootLayout and PageLayout.

    Route and slug normalization used /^\/+|\/+$/-style patterns to trim leading and trailing separators. Those take quadratic time on a long run of the trimmed character, and they run on values that come from outside Blume (configured routes, OpenAPI spec URLs, the site origin), so a pathological config could hang the build. They’re replaced by linear trimming helpers in core/trim.ts; behavior is unchanged.

blume@1.0.1

July 13, 2026

Release

Patch Changes

  • 6afb56a: Declare the blume bin as bin/blume.mjs instead of ./bin/blume.mjs. The leading ./ is redundant and some package managers normalize it away when linking the binary; dropping it keeps the published manifest consistent with what installers actually write.
blume@1.0.0

July 13, 2026

Release

Major Changes

  • 7372fe8: Blume v1.

Patch Changes

  • e922ff5: Stop “Clear conversation” from resurrecting an orphaned answer bubble while a reply is still streaming. Clearing mid-answer emptied the panel, but the in-flight stream kept re-appending its assistant message to the cleared conversation — a growing answer with no question above it. Clearing now aborts the in-flight request and revokes the stream’s right to write into the conversation, and asking a new question right after a clear works as before.

  • e922ff5: Match locale folders and dot suffixes case-insensitively, the way BCP 47 codes are defined. A configured pt-BR with the conventional lowercase pt-br/ folder (or an intro.pt-br.mdx suffix under the dot parser) previously fell through as default-locale content at a literal /pt-br/… route — and the unconfigured-locale warning, which already compared case-insensitively, stayed silent about it. Those files now route as the configured locale, with the configured casing in routes and labels.

  • e922ff5: The generated changelog index’s heading, page title, and meta description now come from the translatable changelog.title and changelog.description UI strings (translated in every built-in pack), joining the reveal button the template already localized. Previously the page rendered a hardcoded English “Changelog” heading, an English “ changelog“ title suffix, and an English description even on non-English default locales.

  • 1e5446f: Lengthen the default changelog description. “Product updates and release notes.” is 34 characters — under the 50-character floor search engines want from a meta description, and the generated /changelog page uses it for both its meta tag and its on-page subtitle. It now reads “Product updates, new features, and fixes from every release.” Sites that set ui.changelog.description are unaffected.

  • e922ff5: The syntax docs now label callouts and package-install blocks as MDX-only, matching the existing notes on diagrams and math. Previously a reader following the page could write :::note or a package-install fence in a .md file and get literal text or a plain code fence with no hint why.

  • e922ff5: Apply the js-yaml 4-safe YAML engine to matter.read as well. The front-matter wrapper previously exposed gray-matter’s own read helper unwrapped, so reading a file through it would parse with the removed safeLoad default and crash with “Function yaml.safeLoad is removed in js-yaml 4” — the exact failure the wrapper exists to prevent for matter() and matter.stringify().

  • 68520af: Exclude Vite’s pre-bundled dep cache (node_modules/.vite/) from @vitejs/plugin-react. Astro’s react() replaces the plugin’s default node_modules exclude, so Babel (carrying the React Compiler) was re-parsing every 500KB+ optimized dep chunk in dev — the source of the “[BABEL] Note: The code generator has deoptimised the styling” messages. Blume’s own components stay covered by the compiler.

  • e922ff5: Serve image-path icons from under deployment.base. <Icon> emitted an image icon’s path (/brand/mark.png) as-is, so on a site deployed under a base path the request went to the domain root and 404’d while <Card img>, the logo, and every other image emitter were correctly rebased. Image icons now get the same withBase treatment; external URLs, data URIs, and relative paths are untouched.

  • e922ff5: @astrojs/vue and @astrojs/svelte are now declared as optional peer dependencies, matching @astrojs/netlify and @astrojs/cloudflare. Projects using Vue or Svelte islands must install the matching integration themselves, and package managers now surface and satisfy that requirement instead of the build relying on an undeclared package.

  • e922ff5: blume build --isolated now reports the build’s actual output directory on success. A server output build with the Vercel adapter lands its deploy bundle at .blume-verify/.vercel/output (it is never surfaced to the project root), but the message previously pointed at .blume-verify/dist, which that build never populates.

  • e922ff5: The breadcrumb and pagination <nav> landmark labels are now translatable via the new nav.breadcrumb and page.pagination UI strings, with translations in every built-in language pack. Previously both were hardcoded English (“Breadcrumb”, “Pagination”) while the sibling chrome landmarks were dictionary-driven, so screen readers announced English landmark names on localized sites.

  • e922ff5: Fix ai.markdownComponents crashing config validation in projects that resolve Zod 4. The schema used the single-argument z.record(...) form, which Zod 4 rejects at schema-construction time, so any config parse failed before your settings were even read; it now uses the dual-compatible two-argument form.

  • e922ff5: The MCP get_page tool’s description — shipped user-facing in tools/list and the server card — no longer claims to return a page’s original Markdown source. The tool serves the agent variant (components downleveled to plain Markdown, <Visibility> resolved for agents), and the description now says so.

  • e922ff5: The sidebar’s panel-stack back arrow and drill-in chevrons now mirror under RTL locales (rtl:-scale-x-100), matching the pagination arrows and the panel slide animation, which already flipped direction. Previously the arrows pointed against the reading direction on RTL sites.

  • d9590fc: Emit a complete Open Graph card in the page head, so crawlers and social validators stop flagging the metadata as incomplete:

    • og:url — the page’s canonical URL (rendered only when deployment.site is set, or a page overrides seo.canonical).
    • og:typearticle on blog posts and changelog entries, website everywhere else. Article pages also emit article:published_time and article:modified_time.
    • og:site_name — the site title.
    • og:image:width, og:image:height, og:image:type, and og:image:alt on Blume’s generated OG card, so a crawler can lay it out without fetching the image. An seo.image you supply yourself declares none of these, since its dimensions and format are unknown.
  • 1e5446f: Give every generated OpenAPI page its own meta description. Operation and overview pages set no description, so all of them fell back to the site-wide default — a spec with twenty operations shipped twenty pages carrying one identical description, which search engines treat as duplicate content. Each operation page now derives a description from the spec’s own prose (its description, or its summary) followed by the endpoint it documents, and the overview page uses the spec description. These land in seo.description, so they feed the meta tag without also printing as a visible subtitle above the body prose.

  • 80eb252: Emit X (Twitter) card tags, and add seo.x for account attribution:

    seo: {
      x: { handle: "@acme", creator: "@jane" },
    }

    handle becomes twitter:site and creator becomes twitter:creator — the one piece of X card metadata with no Open Graph equivalent to fall back to. A page can credit its own author with seo.x.creator frontmatter, which is what a guest post wants. The @ is optional in both places.

    Every page also now emits twitter:card, twitter:title, twitter:description, and twitter:image:alt on the generated card. twitter:card previously rendered only when a page had an image; a page without one now gets the compact summary card instead of sharing as a bare link.

  • e922ff5: The Scalar API reference shell now sets <html lang> and dir from the default locale, mirroring the changelog index’s locale wiring, and renders the same localized skip-to-content link as the other layouts. Previously it hardcoded lang="en" with no dir — so RTL default locales rendered LTR chrome — and offered keyboard users no way to skip past the navbar.

  • e922ff5: The search dialog’s section-filter “All” pill is now translatable via the new search.all UI string, with translations in every built-in language pack. Previously it rendered hardcoded English inside an otherwise fully localized dialog.

  • e922ff5: ⌘K / Ctrl+K now toggles the search dialog: pressing it while the dialog is open closes it, matching how ⌘I toggles the Ask AI panel. Previously the shortcut unconditionally re-opened, calling showModal() on an already-open dialog — a silent no-op on evergreen browsers but an InvalidStateError on older engines. The / shortcut stays open-only, and open() itself now guards against an already-open dialog.

  • e922ff5: Reject a toc config whose minHeadingLevel exceeds its maxHeadingLevel. An inverted range (including an explicit min above the default max of 3) previously validated fine and silently rendered an empty table of contents on every page; it now fails config validation with a clear message.

  • e922ff5: Internal-error stack traces now relativize .blume/ runtime frames on Windows too. The remap previously matched only POSIX absolute paths, so drive-letter frames like C:\...\.blume\... printed the full machine path instead of the project-relative .blume\... form tagged (generated).