Skip to content
Blume
Esc
↑↓navigate↵open⌘Jpreview
On this page

blume@2.1.0

Minor Changes

  • 99119e7: API references can generate code samples in 18 languages: cURL, Python, JavaScript, Node.js, TypeScript, PHP, Go, Java, Ruby, PowerShell, Swift, C#, .NET, C, C++, Kotlin, Rust, and Dart. List them in codeSamples on openapi() or graphql() in the order you want them, by id or by a common alias such as golang, c#, or ts. Each sample quotes values by its own language’s string rules and updates live with the Try it form. node and typescript now generate their own samples (axios, and typed fetch) instead of repeating the JavaScript one.

    Operations also render the spec’s own x-codeSamples (or x-code-samples) as tabs ahead of the generated samples, for SDK snippets written by hand or by Speakeasy or Stainless. Set codeSamples: false to show only those.

  • f4854ef: Add a bot check for the assistant. ai.assistant.captcha takes an adapter from blume/captcha, turnstile({ siteKey }) or hcaptcha({ siteKey }), run invisibly: the provider’s script loads with a reader’s first question, the panel sends a fresh token with each question, and the generated route verifies it (with TURNSTILE_SECRET_KEY or HCAPTCHA_SECRET_KEY) before the model runs. A failed check answers 403, which the panel shows as a translated “we couldn’t check that you’re human”; a missing secret answers with the assistant’s “not configured” notice, and blume build warns. With an external endpoint, the token is sent as captcha in the request body for that backend to verify. useAssistant takes the captcha settings and a verifyMessage too.

  • 253ad9e: The assistant can now search the docs and read whole pages itself. Beyond the pages retrieved up front, the model gets a search_docs tool and a read_page tool, and can search, read, and search again before it answers, within one question and at most five steps. The tools run on your server against the assistant’s snapshot and keep to the reader’s language and docs version. When nothing matches a question up front, the assistant now searches instead of saying it doesn’t know. They’re on by default, except for openai() with a baseUrl, whose model may not support tool calling. Set ai.assistant.tools to change either.

  • 6653f72: The assistant can now call OpenAI, Anthropic, Gemini, and Grok directly. openai({ model }), anthropic({ model }), gemini({ model }), and grok({ model }) from blume/ai send each question to the provider’s own API with your key, through its AI SDK package (@ai-sdk/openai, @ai-sdk/anthropic, @ai-sdk/google, or @ai-sdk/xai, installed as needed). They read OPENAI_API_KEY, ANTHROPIC_API_KEY, GEMINI_API_KEY, and XAI_API_KEY, turn the docs tools on, and send reasoning as each model’s own control. openai() also takes a baseUrl for any OpenAI-compatible endpoint, which it calls through @ai-sdk/openai-compatible with the docs tools off, as openaiCompatible() did. openaiCompatible() still works and now returns the same adapter.

  • aca5ac9: Add content variables. Define values under variables in blume.config.ts, like { version: "2.1.0", "api-url": "https://api.example.com" }, and {{version}} anywhere in a page reads the value: in prose, headings, links, code, and component props, in .md and .mdx pages and the files they include. Search, the .md mirrors, and llms-full.txt show the value too. An undefined name in prose fails the build at its line (BLUME_UNDEFINED_VARIABLE); inside code it’s left as written. Sites that define no variables are unaffected.

  • 9ac2262: Add cookie consent. consent in blume.config.ts takes an adapter from blume/consent, and with it set every analytics adapter waits for the reader: its tags stay in the page as inert text/plain until the reader allows analytics, then run in order, once per page load, and Vercel Web Analytics sends nothing until then. native() is Blume’s own banner, a card with Accept and Decline side by side and an optional policy link, translated in every built-in language and shown in blume dev too. osano({ customerId, configId }) and ethyca({ privacyCenter, propertyId, notice }) load Osano or Ethyca’s Fides instead, and follow the answers readers give there. The site footer gains a Cookie settings link that reopens the banner or the manager’s preferences (any element with data-blume-consent-open works in a custom footer), and a reader who takes consent back gets a reload so analytics stops. The “Was this page helpful?” rating, which reports through analytics too, only shows for readers who allowed analytics. Your own scripts can wait the same way with type="text/plain" data-blume-consent="analytics", or listen for the blume:consent event.

  • c3e1cd3: Add directory listings. directory in a folder’s meta.ts, or on a group in an explicit navigation.sidebar, lists the group’s pages below the content of its own page (a folder’s index page, or the group’s root): card as a grid of cards with each page’s icon and description, accordion as rows with each subgroup a section that opens to its pages, and none, the default, as nothing. Nested groups inherit the nearest setting, so one directory in the content root’s meta.ts gives every section a listing, and any folder can set its own. The values match Mintlify’s directory.

  • 0036165: Add components for documenting endpoints by hand. <ParamField> is a request parameter, named by where it goes (<ParamField query="limit" type="integer" default={20}>, or path, header, body), and <ResponseField name="id" type="string"> is a field of the response, with optional pre and post labels. Both take type, required, deprecated, and default, hold their description as content, and nest an object’s fields inside an <Expandable>. They render like the OpenAPI reference’s rows, and a page’s Markdown copy lists each field with its type and flags. <RequestExample> and <ResponseExample> hold code blocks as tabs (or a language menu with dropdown); on a wide screen they pin to a column beside the page, request above response, and stay in view while the reader scrolls, the way an OpenAPI operation’s examples do. A page with api frontmatter (api: POST /v1/users) documents one endpoint without a spec: it shows the method and path at the top, and its <ParamField>s build a Try it panel and request samples in cURL, JavaScript, and Python, the same ones an OpenAPI operation gets, with nested fields as the properties of a JSON body. authMethod and playground frontmatter tune a page, and a new top-level api config sets the server a path joins, the default auth, and the playground, whose proxy: true routes sends through the built-in CORS proxy for those origins. The names and props match Mintlify’s, so migrated pages keep them as written.

  • 72f7cf5: Slim the header down to icon buttons, making room in it. Search is now an icon button beside the theme toggle and the assistant, in place of the search field, and each icon button names itself in a tooltip on hover and keyboard focus: “Search (⌘K)” (Ctrl K off Apple devices), “Toggle theme”, and “Open assistant”. The GitHub link leaves the header for the new site footer, which is now the one place the site links its repository and social profiles; navigation.repo works as before and applies there. The header’s tabs now sit at its center on wide screens. The version and language pickers lose their border to match the icon buttons, the header’s right-hand controls sit closer together, and the language switcher drops its globe icon and always shows the language’s name. Its menu now widens to fit, so a long language name no longer runs into the “Not translated” note beside it. A Header override is unaffected. The “Open assistant” tooltip is a new assistant.open UI string, translated in every built-in language pack.

  • f8bd737: Pass props to an <include>: every attribute other than lang and meta becomes a value the included file reads with {{name}}, like <include plan="Pro" feature="SSO">./_snippets/upgrade.mdx</include>. Props reach the partial’s own nested includes and take precedence over a site-wide variable with the same name.

  • 0758eaa: OpenAPI references now document webhooks and callbacks. Each webhook in a 3.1 spec’s webhooks gets its own page, filed under its first tag or a Webhooks group. The page shows its payload schema, the responses your endpoint should send, and an example payload in place of the Try it panel. It lists only the security the webhook itself declares, since the spec’s root security guards calls to the API, not the requests it sends. Webhook pages are in search, llms.txt, and the MCP server, and their Markdown marks them as requests the API sends. An operation’s callbacks, inline or $ref’d from components.callbacks, render in a Callbacks section on its page with their URL expression, method, request body, and responses. Specs with webhooks no longer log a warning that they’re missing from the reference.

  • 2153f6b: Add page layout modes. mode in a page’s frontmatter sets what it shows around its content: wide drops the table of contents and lets the content take its width, center drops the sidebar too and centers a wider column, custom keeps only the header for a landing page written in MDX, and frame is custom with the sidebar. custom and frame pages render no title, description, breadcrumbs, page-end links, or site footer, so they bring their own heading. Every mode stays in search and keeps its Markdown copy. The values match Mintlify’s; its full-page assistant mode has no equivalent.

  • ecbfb0e: Add narration: a “Listen to this page” player that reads each page aloud and highlights the sentence being read. Turn it on with narration: true to use the reader’s browser voices, which need no key and work on any host. Or pass narration: { provider: gateway({ model: "openai/tts-1-hd", voice: "alloy" }) } to generate neural audio at build: one cached clip per sentence, shipped as static files, with browser voices as the fallback in blume dev or when the key is missing.

    The player announces callouts, steps, tabs, and collapsible sections with short spoken cues, and skips code, tables, media, and type tables. It opens a closed section or switches to a hidden tab when narration reaches it, and keeps the sentence in view until the reader scrolls away. It offers speeds from 0.8× to 2×, and stops when the reader opens another page. Set narration: false in a page’s frontmatter to leave it out, or add data-blume-narration="skip" to keep any element out of narration.

  • aea605b: Add pattern redirects. A redirect’s from can now cover many paths, written the way Mintlify and vercel.json write them: :name matches one segment (/blog/:slug), :name* as the last segment matches the rest of the path (/beta/:slug* sends /beta/a/b to /v2/a/b and /beta to /v2), and * ending a segment matches the rest from there (/old/article-*). to reads the captures (/v2/:slug*, /new/article-*). Every host gets them in its own syntax: _redirects splats, vercel.json sources, and the routing config of a Vercel or Netlify server build, while the Node and Cloudflare servers and blume dev match them directly. Exact redirects win over a pattern that covers the same path. A pattern that also matches a page fails the build (BLUME_REDIRECT_MATCHES_PAGE), since hosts disagree on which answers, and links into a pattern count as valid in blume validate and blume audit.

  • 09efac4: Add rate limiting to the server routes a reader can call: the assistant, the API playground proxy, and Mixedbread search. Each reader, by IP address, gets 30 requests per route every 10 minutes by default, and past that the route answers 429 Too Many Requests with a Retry-After header, which the assistant turns into a translated “try again in a few minutes”. rateLimit in blume.config.ts takes an adapter from blume/ratelimit: memory(), the default, counts in the server’s memory (exact on node(), per instance on serverless hosts); upstash() shares the count through Upstash Redis’s REST API (UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN); and cloudflare() counts with Workers rate limiting, declaring the binding in the built Worker’s config. Each takes requests and window (seconds). A request whose address the host can’t tell is let through, and so is one a shared store fails to count. rateLimit: false turns it off.

  • 0dd30af: Add a site footer: one row, as tall as the header, below the content on every page. Your repository link moves there from the header (see the header changes), as the first of the footer’s icons, and footer in blume.config.ts adds the rest: links, a list of { label, href } shown on one side, and socials, profile URLs keyed by platform (x, discord, linkedin, youtube, website, and more) shown as brand icons on the other, both in the order written. A github social replaces the repository link, and navigation.repo still hides it or points it elsewhere. Custom pages built on PageLayout show the footer too, unless they fill the layout’s footer slot. A Footer layout override still replaces it, and blume add footer copies the built-in into your project to edit. A site with no repository, links, or profiles has no footer.

Patch Changes

  • ade0397: Add an Ask button to code blocks when the assistant is on. It opens the assistant with the block attached as a chip above the input; the question goes to the model with the code as a fenced block (up to 6,000 characters), and an empty question asks it to explain the code. The conversation shows the attached code under the question, and the button’s label and the chip’s text are translated in every built-in language. Code groups get one button in their tab strip, which asks about the tab that’s showing.
  • 57db488: Add a support handoff to the assistant. ai.assistant.support takes a mailto: address, a URL, or a page on your site, and once there’s a conversation the panel shows a translated “Contact support” link: an email starts with the conversation as its body, and a link gets the conversation’s id as a thread query parameter. The assistant’s ask, ask_answer, and ask_error analytics events now carry the same thread, and useAssistant returns it.
  • 0654705: Add blume skill, which writes your docs site’s agent skill with a coding agent. blume skill --claude (or --codex) opens the agent on the new blume-write-skill skill. The agent writes a SKILL.md grounded in the docs that teaches an agent to use the product: setup, core concepts, common tasks, and gotchas, each linked to its page. The file goes under agents.skills, named after the site so it replaces the generated skill at /skill.md. Run it again to refresh the skill after big docs changes.
  • c3e1cd3: Add wrap and expandable to code fences. wrap after the language (```ts wrap) wraps one block’s long lines instead of scrolling them, the way markdown.code.wrap does for every block. expandable collapses a long block to its first lines under a fade, with a Show more toggle that opens it in full, no inner scroll, and Show less to close it again; a block under 16 lines renders as usual. Both compose with a title, line numbers, and each other, and the toggle’s labels are translated in every built-in language.
  • a7c8ceb: Fix header controls overlapping on narrow screens. With many controls on (tabs, a version selector, the language switcher, search, and the icon buttons), the version and language pills squeezed on top of each other on phones, and the tab bar ran into them on tablets. Below the desktop breakpoint, selectors, the version selector, and the language switcher now sit at the top of the navigation drawer, full width. The remaining header controls keep their size, and only the site title truncates to make room.
  • 553a58f: Apply OpenAPI Overlays to a spec before it renders. List overlays beside spec in openapi() or scalar(), or on each source, as local paths or http(s) URLs, applied in order. Overlay Specification 1.0 and 1.1 are supported: update, copy, and remove actions, with targets as RFC 9535 JSONPath. Overlays apply to the spec as written, before it’s upgraded to OpenAPI 3.1, and everything built from the spec sees the result. An overlay that fails stops its reference from rendering, the way an unreadable spec does, so it can’t publish what it was meant to hide.
  • d6190ee: OpenAPI overlays can now add a property named constructor (it used to fail with a merge error), and a __proto__ key in an overlay no longer reaches Object.prototype. Parsing a long run of digits in a theme color no longer takes quadratic time.
  • c3e1cd3: Add pagination: false to page frontmatter, which leaves the previous and next links off that page. The page keeps its place in the order, so its neighbors still link to it. The Mintlify migration maps hideFooterPagination: true to it.
  • 7f9fa4a: Add <View> for pages written for several audiences, such as one per programming language. Each distinct title becomes an option in a picker above the page’s content, and only the chosen view’s blocks show. Prose outside any <View> shows in every view, and headings in hidden views drop out of the table of contents. The reader’s pick is remembered across pages and written to the URL as ?view=, a link to a heading opens its view, and the choice applies before the page paints. The agent-facing Markdown includes every view under its name.
  • 0a29cb2: Translate the banner, header links, and footer per locale. The banner’s content and link text, navigation.actions, navigation.cta, and navigation.featured labels, and footer link labels now accept a map of locale code to text, like tab labels do. Each page shows its locale’s entry and falls back to the default locale’s. Internal footer links now move into the reader’s locale whenever that locale serves the page, as header links already did.
  • 4e4b258: Add related pages. related in a page’s frontmatter lists up to ten pages to suggest at its foot, shown as cards under a translated “Related pages” heading: a root-relative path shows that page’s title and description (its translation, on a translated page), a { Title: link } entry names the card itself, and an absolute URL links off the site. Paths are checked like the page’s other links, so blume validate reports one that matches no page. The key matches Mintlify’s.
  • 8f040cd: Add i18n.routeByBrowserLanguage, which sends visitors who land on the default language’s home page to the home page in their browser’s preferred language. Blume tries each language the browser prefers, in order, matching an exact locale code and then the base language, and a visitor whose first match is the default language stays. Picking a language with the switcher stops the routing for that reader, and only visitors arriving from outside the site are routed. The redirect runs in the browser before the page paints, so it works on static hosts. It’s off by default.
  • 169966c: Report search to your analytics. Once a query settles (a second without typing, or the reader picks a result or closes the dialog), the search dialog sends a search event with the query, its number of results, and the path, so queries with no results are easy to find. Picking a result sends search_select with the query, the result’s position, and its url. Both go through every analytics adapter with an event API and as blume:track events.
  • 0f8a46e: Rank pages with search.boost and search.keywords frontmatter. boost multiplies a page’s search relevance: above 1 moves it up, below 1 moves it down. The default Orama search, FlexSearch, the MCP server, and the assistant apply it exactly. Pagefind weighs a boosted page’s text more heavily, Algolia and Typesense sort by it among close matches, and Orama Cloud re-sorts a wider set of results. keywords are extra terms a page is found by, beyond its own text. Blume 1 accepted search.boost without reading it, so a page that still sets it now ranks by it. The Mintlify migration moves keywords, boost, and searchable: false into the search block.
  • aa2babe: Add seo.metatags for tags written into every page’s head: site-verification tokens, theme-color, an app banner, and anything else Blume has no setting for. Open Graph families render with property and everything else with name. A tag Blume writes itself, such as description, robots, og:image, or a twitter: card tag, is refused, and the build names the setting that controls it.
  • 7dee91b: Publish an agent skill for every site. The build writes a SKILL.md named after the site’s title, served at /skill.md and listed in the skills discovery index, llms.txt, and the AI catalog. It’s built from what Blume already knows, with no model call: how to read any page as Markdown, llms.txt, the MCP server, the API references, the changelog, and a map of the docs with each page’s description. It needs deployment.site for its absolute links. A skill of the same name in agents.skills replaces it, and agents.skillMd: false turns it off.
  • 7a58b53: Meet WCAG AA contrast with the accent colors, and check a site’s theme colors in blume audit. Each accent preset now has a darker light-mode shade and a lighter dark-mode shade, so accent text and button labels reach 4.5:1 in both modes. Blue is the default, so this changes sites that set no accent too. Labels on accent and action fills are white while white reaches AA on that color, and dark otherwise. A light accent, and every preset in dark mode, now gets dark labels instead of unreadable white ones. A new accessibility category in blume audit measures a custom theme.accent, theme.action, or theme.background against the same bar in both modes, and says when it can’t read a color.
  • 0c0acd5: Tell the translation agent to keep frontmatter valid YAML by quoting a translated value that contains a colon or starts with a quote or backtick, and name the YAML error and its line and column when blume translate rejects a page whose frontmatter doesn’t parse.
  • f50ef8a: The BLUME_WIKILINK_UNRESOLVED and BLUME_WIKILINK_AMBIGUOUS diagnostics now link to the Obsidian source page instead of the content sources overview.
  • aecd4b9: Add written feedback. feedback: { comments: true } offers a box after the “Was this page helpful?” rating where the reader can say what worked or what’s missing. A comment is sent as a feedback_comment event, with the rating, path, title, and the comment (up to 1,000 characters), through every analytics adapter with an event API and as a blume:track event. The box’s label and button are UI strings, translated in every built-in language. feedback: true and false work as before, and the Feedback layout slot now receives a comments prop.

Last updated on September 30, 2026

Was this page helpful?