blume@2.0.3
Patch Changes
-
090211e: Operation links in the agent-facing Markdown of an API reference overview (
/<route>.md,llms-full.txt, and MCPget_page) now includedeployment.base. With a base of/sub, they used to point at/api/pets/list-pets, which 404s. Inllms.txt, page and feed titles with square brackets ([Beta] Webhooks) are escaped, so their lines stay links. -
ad20661: With
deployment.baseset, root-relative images and file links in the agent-facing Markdown (/<route>.md,llms-full.txt, and MCPget_page) now gain the base the way the rendered page’s do:becomes/sub/logo.pngand[spec](/spec.pdf)becomes/sub/spec.pdf. They used to keep the bare root path, which 404s on a subpath deploy. -
be607f9: A file path longer than 100 bytes inside a published agent skill no longer fails
blume build: the skill’s.tar.gzstores it in the ustarprefixfield up to 255 bytes and in a PAX extended header beyond that, so standardtarreaders unpack it at its full path. Two skill directories that declare the samenamenow publish one index entry, from the directory that sorts first, with a build warning naming both, instead of two entries for one artifact whose digest could not match what was served. -
090211e: A schema with both properties and a
oneOf/anyOfnow shows its shared properties above the variants. Before,{ properties: { amount, currency }, oneOf: [Card, BankAccount] }showed only “One of Card | BankAccount”. An OpenAPI spec whoseoperationId, tag, summary, or description YAML reads as a number (operationId: 404,tags: [2024]) now loads, using the number as text, where it used to fail with “text.normalize is not a function”. The request and response tabs’ Left and Right arrow keys follow the reading direction on right-to-left pages, as<Tabs>does. -
4f7785c: An OpenAPI, AsyncAPI, or GraphQL description that ends inside an unclosed code fence no longer swallows the reference UI rendered after it. The fence used to run to the end of the page, so an operation page lost its parameters, responses, and playground, and an overview page lost its operation lists. Blume now closes the fence where the description ends.
-
af7c4c9: ArkType schemas now work in
frontmatter.extendandcontent.types.<type>.frontmatter. ArkType types are callable functions, and Blume only accepted Standard Schemas that were plain objects, so config validation rejected them. -
08219a2: Closing the assistant panel mid-answer now stops the model call. The generated assistant route passes the request’s abort signal to the model, which used to keep generating, and billing, the rest of the answer after the reader left.
-
6933892: With the assistant panel docked open on desktop, moving to another page keeps the page laid out beside the panel. Each navigation used to replay the page’s slide to make room for the panel.
-
090211e: AsyncAPI operations whose bindings are a
$refto#/components/*Bindingsnow resolve it. Before, the protocol read as “$ref”, so a WebSocket operation got no samples and the composer said “Live sending isn’t possible for $ref”. Channel parameters are URL-encoded only for URL-based protocols (WebSocket, HTTP), so a Kafka or MQTT topic readssensors/sensor 1rather thansensors/sensor%201. The message composer no longer hangs on “Connecting…” when the browser refuses a malformed WebSocket URL: it shows the error and lets you connect again. -
b6992c0:
blume auditno longer counts an inline SVG’s<title>as a page title, so an accessible icon doesn’t raiseBLUME_AUDIT_TITLE_MULTIPLE(an error) or stand in for a missing head<title>. Text-fragment links (#:~:text=…) aren’t reported as broken anchors, and only the id in front of a fragment’s:~:has to exist. An i18n fallback copy of a page is no longer reported as an orphan, which failed--strict. A failing audit now delivers its whole report through a pipe; it used to exit before a long report finished writing, cutting the CI log off partway. -
6933892:
<Badge variant="accent">now uses your theme accent, as documented, instead of a fixed blue. Its label mixes the accent toward the text color, so it stays readable over the accent tint in light and dark mode. -
ad20661: A header link, featured link, or other configured link that already carries
deployment.basefollowed by a fragment or query, like/sub#installunderbase: "/sub", is no longer prefixed a second time. It used to render as/sub/sub#install. -
3311cee: A link that already carries
basePathfollowed by a fragment or query, like[Install](/docs#install)underbasePath: "/docs", is no longer prefixed a second time. It used to render as/docs/docs#install, a 404 the link checker didn’t catch. -
f373654: A page whose body starts with a
---horizontal rule, right below its front matter, no longer loses everything up to the next---line. The renderer read that stretch as a second front matter block and dropped it; both rules and the text between them now render, in.mdand.mdxpages alike. Headings in that stretch are now anchorsblume validateaccepts and title an untitled page, the same as any other heading. -
7258f30:
blume translate,blume validate, andblume doctornow let their whole report reach a piped stderr before exiting with a failure. Before, they exited immediately after writing, so a long list of findings could be cut off in CI logs and| teeoutput — the same fixblume auditandblume evalgot. -
3311cee: A
cloudflare()server build with abasenow writes its_headersrules intodist/client/_headers, where Cloudflare reads them, next to the adapter’s own. They used to land indist/client/<base>/_headers, which Cloudflare never reads and served publicly, so the Markdown and text charset, the discovery files’ media types and CORS header, and the agent skills media types were dropped. The Markdown and JSON 404 answers are wired up under a base too, and abasewritten without a leading slash (cloudflare({ base: "docs" })) no longer stopsAccept: text/markdownnegotiation from running. -
f373654: Rich text from a CMS source (Notion, Contentful, Sanity, Strapi, Payload) renders neighboring runs with the same formatting as one: two bold runs side by side used to show
****between them, and two italic runs**. Neighboring runs under the same link in Notion and Sanity now form one link. A bare URL containing_,~, or*stays a working link instead of gaining backslashes in its address, and struck-through text in a Contentful table cell no longer shows~~~~. -
4f7785c: Rich text from a CMS source (Notion, Contentful, Sanity, Strapi, Payload) renders more faithfully. A
^no longer turns text like “210 and 220” into superscript,$$no longer starts math, and a colon before a word (10:30,pets:read) stays text. Two struck-through runs side by side now render as one strikethrough instead of showing~~~~, and a heading that ends in “ #” keeps its#.A Notion page with no Slug property is now routed by its whole title, with slashes turned into hyphens: “CI/CD Setup” publishes at
ci-cd-setupinstead of atcd-setupin an inventedcigroup. A Slug property still sets a nested path. A Notion video URL that contains a double quote or a backslash no longer breaks the page. -
af7c4c9: An unknown
dateFormat.timeZone(Asia/Tokio) is now a config error that points at the key, instead of aRangeErrorthrown later while rendering a page’s “last updated” date or the changelog. -
af7c4c9: Blume now warns when a content folder is named after the default locale (
docs/en/withdefaultLocale: "en"). The default locale’s pages live at the content root, so such a folder is ordinary content that publishes at/en/…(/en/en/…when the default locale keeps its prefix, and/fr/en/…as fallbacks); before, it did so silently. -
08219a2: The homepage
Linkheaderblume devsends now lists only what the dev server serves: the JSON API’s OpenAPI description and the homepage’s Markdown mirror. It used to also advertise/llms.txt,/agent-readability.json, and the API and AI catalogs, whichblume buildwrites and the dev server answered with 404s. Built and deployed sites send the same header as before. -
b6992c0: Two
blume devservers started at the same moment in one project no longer both get the.blumelock. The lock file now appears with its content already written, a lock that’s still being written is waited on instead of being read as stale, and a stale lock left by a stopped server is cleared by one process at a time, so a starting server can’t delete the lock another one claimed a moment earlier. -
1b83039:
blume devnow shows a body edit to a.mdpage on sites whose content pages render in Astro’sprerenderenvironment, such as sites deployed withcloudflare(). Astro refreshes its content store in thessrenvironment only, so the page kept rendering its old body until something structural changed or the dev server restarted. -
08219a2:
blume devnow refreshes Blume’s generated data in every Vite environment, not only the ones Vite’s legacy module graph covers. On sites whose content pages render in Astro’sprerenderenvironment, such as sites deployed withcloudflare(), a page added while the dev server runs now opens instead of 404ing, sidebar label edits show up,.mdmirrors follow body edits, and an edit to an included partial reaches the pages that include it. Until now they all kept their startup state until the dev server restarted. -
08219a2: The first search in a fresh
blume devsession no longer reloads the page. The configured search adapter’s browser library (@orama/oramafor the default search, or the FlexSearch, Algolia, Orama Cloud, or Typesense client) is now pre-bundled when the dev server starts. It used to be discovered only when the search dialog first opened, which re-optimized dependencies and reloaded the page. -
be9d98e:
blume devreloads on edits under a nested folder that shares a name with an excluded one. Withexclude: ["drafts/**"], which leaves outdrafts/at the content root only, the watcher used to ignore every folder nameddrafts, so edits to pages inguides/drafts/never showed up until a restart. -
be9d98e:
blume ejectnow writes the JSON docs API (/api/docs/…and/openapi.json) into the ejected app, which itsllms.txt, homepageLinkheader, and not-found page already pointed to, so those links no longer 404. The ejectedsrc/generated/includes.jsonnow lists partials and their pages relative to the project instead of as absolute paths from the machine that ran eject, so editing a partial still refreshes the pages that include it in any checkout. -
1b83039:
blume ejectnow writes the not-found page’s Markdown and JSON twins (/404.mdand/404.json) beside its404.astro, so the ejected app answers the same missing-page URLs as the hidden runtime. Like the page itself, they’re left out when the project already owns/404with apages/404.astroor a404.mdcontent page. -
1b83039: A failing
blume evalnow delivers its whole report through a pipe before exiting non-zero. It used to exit as soon as it wrote the summary, so a long--verbosereport was cut off partway in CI logs. -
b6992c0:
blume eval --thresholdrejects an empty value, which it used to read as0, so--threshold "$EVAL_THRESHOLD"with the variable unset switched the gate off. An absolute--filepath is used as given instead of being nested under the project root, for runs and forblume eval init, whose “already exists” check missed it.--timeoutonblume evalandblume translaterefuses values above 2147483 seconds (about 24 days), which used to make every agent time out at once. Durations in their reports round before they split into minutes, so a run prints2m 0srather than1m 60s, and1m 0srather than60.0s. -
af7c4c9: Front matter that opens with a blank line after its
---fence is now read as front matter, the way Astro reads it. Blume used to treat that---as a divider, so the page’sdraft,slug, andhiddenwere ignored while Astro stripped the block, and its YAML leaked into search andllms.txt. A block that holds no key–value pairs now reads as empty front matter, again matching Astro. -
ad20661: Every URL Blume generates for a page now puts
deployment.basein front, even when the page’s route starts with the same segment. Withbase: "/guides", a page atguides/setup.mdis served at/guides/guides/setup, but its canonical URL, sidebar and tab links, breadcrumbs, previous/next links, search results, JSON-LD,llms.txtandllms-full.txtentries, JSON API and MCP URLs, and relative links to it (./setup) all pointed at/guides/setup. They now point where the page is served. A root-relative link you write that already starts with the base is still left as written. The language switcher’s links to locales a page has no translation for get the same fix forbasePath. -
be9d98e: With
lastModified: "git", a project opened through a symlink (such as/tmpon macOS, which links to/private/tmp) now gets its “Last updated” dates. Blume compared the project’s path against the one git reports with the link resolved, so it treated every content folder as outside the repository and dated no page, without a warning. -
af7c4c9: Heading anchors and titles now come from a heading’s rendered text rather than its Markdown source, so they match the ids on the page.
## See [the docs](/x)anchors as#see-the-docs(not#see-the-docsx),## An _important_ noteas#an-important-note, and entities, code spans, inline HTML, images, reference links, footnote references (## Setup[^note]→#setup1, numbered in the page’s footnote order), and smart punctuation (A -- B→#a--b) read the way the renderer reads them.blume validateno longer reports a correct link to such a heading as a broken anchor,blume translatepins the id the page actually has, Obsidian heading links land on the section, and a page without a frontmattertitleis titled “Using Astro with Blume” instead of the raw “Using Astro with Blume”. -
6933892:
usePage()anduseBlume()now return the page being viewed after a client-side navigation. They used to keep answering with the first page’s route, title, and navigation for the rest of the visit, so an island on a page reached by a link, or after switching language, read the wrong page. An island kept across navigations withtransition:persistnow updates too. -
4f7785c: Text in an
.mdxpage that reads as an inline or leaf directive now renders exactly as written. A colon followed by a letter or digit, as in16:9,10:30am,og:image, orpets:read, used to drop everything from the colon to the end of the word (“16:9 frame” rendered as “16 frame”), and a::name[label]{attrs}line vanished. This also covers headings and their anchors, callout titles and bodies, and API reference descriptions.:::callouts work as before, and.mdpages were never affected. -
be9d98e:
mdxRemote()pages lose their file and folder names’ ordering prefixes the way a local content folder’s do:01-getting-started/02-install.mdxnow routes to/sdk/getting-started/installinstead of/sdk/01-getting-started/02-install, while the prefixes still order the sidebar. -
f373654: A
:::block in an.mdxpage whose name isn’t a callout type, like a:::detailscarried over from another docs tool or a typo like:::warnig, no longer disappears along with everything inside it. Its content now renders between its:::lines, which stay on the page as written, andblume dev,blume build, andblume checkwarn withBLUME_UNKNOWN_DIRECTIVE, naming the callout types. A callout nested inside another (a::::notearound a:::tip) now renders instead of vanishing. Site search now indexes text like16:9andog:imageas the page shows it, instead of as “16” and “og”. -
be607f9: The
blume-migrateskill’s Mintlify codemod removes a dropped front matter key’s list items even when they’re written flush at column 0 (keywords:then- one), instead of leaving them behind as front matter that no longer parses. Its Fumadocs and Nextra guides now map locale file suffixes (page.cn.mdx,index.en.mdx) toi18n.parser: "dot"as-is rather than restructuring them into locale folders. -
3e95f16: OG cards render every script by default. A Noto fallback for each script is now part of the default card font stack, not only for configured locales, so a Japanese, Hindi, or Arabic title renders instead of tofu on any site, including sites with no
i18nblock and ejected projects. A card fetches from Google Fonts only when its text has a glyph the built-in font can’t draw, and then only the families and subsets those glyphs need, so English cards fetch nothing and Latin-only sites still build offline. Configured locales move their family to the front, so azhsite draws Han in Chinese forms. -
3cd8149: Add a
oneDollarStats()analytics adapter. It renders the OneDollarStats tracker tag, needs no key, and forwards every option as adata-attribute (hostname,devmode, …).hostnamemust be a bare host name, and"hash-routing": "false"leaves the attribute off, since the tracker turns hash routing on whenever it’s present. Blume’s custom events (page feedback and Ask AI) reachwindow.stonks.eventwith their property values as strings. -
090211e: The API playground’s built-in proxy (
playground: { proxy: true }) now forwards only the headers the Try it panel sets itself: the credentials and header parameters filled in, and the body’sContent-Type. It used to forward every request header except a handful, so credentials the browser or the host attached on their own reached the documented API: HTTP Basic credentials for a password-protected docs site, a Cloudflare Accesscf-access-jwt-assertion, Vercel’sx-vercel-oidc-token, andX-Forwarded-For/X-Real-IP. Platform headers are never forwarded, even when a spec declares a header parameter with the same name. -
1b83039: A
traceoperation’s Try it panel now says that browsers can’t sendTRACErequests when you press Send, instead of blaming CORS or an unreachable API:fetchrefuses the method before anything goes out. Its JavaScript sample, which is built onfetch, is now a note saying so rather than code that could only throw. The cURL and Python samples still send it. -
090211e: The Try it panel and the code samples now send requests the way the spec describes them. Array and object parameters follow their
styleandexplode, with OpenAPI’s defaults when the spec sets none:tags: ["dog", "cat"]becomes?tags=dog&tags=catrather than a URL-encoded JSON array, a path array becomes1,2, and adeepObjectfilter becomesfilter[color]=red, withspaceDelimited,pipeDelimited,label, andmatrixsupported too. Anapplication/x-www-form-urlencodedbody is sent asname=valuepairs (following itsencoding), and amultipart/form-databody as form fields: curl uses--form-string, JavaScript aFormData, and Pythonfiles=. Before, both were sent as JSON. Atext/plainor XML body is sent as written, without being blocked as “Invalid JSON”, and its string example is no longer wrapped in quotes.Examples that
$refan entry under#/components/examplesnow prefill the panel and the response samples instead of being replaced by a generated placeholder. A GraphQL endpoint keeps its trailing slash (https://host/graphql/was sent to…/graphql). The HEAD curl sample uses--headso it no longer waits for a body that never comes, and the Python sample for TRACE usesrequests.request("TRACE", …), sincerequests.tracedoesn’t exist. -
3311cee: With
deployment.baseset, root-relative images and file links in content now gain the base:renders as/sub/logo.pngand[spec](/spec.pdf)(or<Card href="/spec.pdf">) as/sub/spec.pdf, where Astro servespublic/. They used to keep the bare root path and 404 on a subpath deploy. Public files still never gainbasePath, and neither does a redirect whosetonames one: withbasePath: "/docs", a redirect to/files/whitepaper.pdfnow lands on the file instead of/docs/files/whitepaper.pdf. -
be9d98e:
mdxRemote()andgithubReleases()give up on a request after 30 seconds, like the CMS sources, and the diagnostic names the URL that didn’t respond. A server that accepted the connection and never answered used to hangblume buildand everyblume devrescan indefinitely. -
af7c4c9:
%is now dropped from routes the way:already was, sosdks/100%.mdpublishes at/sdks/100and sidebar and pagination links reach it, where a bare%used to break the build’s URL decoding;#and?in a frontmatterslugare dropped the same way. A file or folder with#or?in its name is reported as an error and left out of the site: Astro’s content loader can’t read such a file, so it used to publish a page that only ever said “Page not found”. A frontmatterslugwith a.or..segment (../../etc/escape,guides/./x) is now rejected with an error at the key instead of publishing a page no link could reach. A group folder’s numeric prefix now works on either side of the parentheses:(01-zeta)sorts first instead of last, and01-(gamma)publishes at/gas a “Gamma” group instead of at/(gamma)/g. -
6933892: On a versioned site, the search dialog shows its “All versions” toggle only with a search provider that scopes results by version: Orama, FlexSearch, Algolia, or Typesense. With Pagefind, Orama Cloud, or Mixedbread the toggle used to show but change nothing, since their results always span every version.
-
3311cee: The sitemap and RSS feeds now always put
deployment.basein front of a page’s URL, even when the page’s route starts with the same segment. Withbase: "/guides", a page atguides/setup.mdis listed at/guides/guides/setup, where it’s served, instead of/guides/setup. -
1b83039: An agent skill’s
.tar.gznow always ends with the two zero blocks that mark the end of a tar archive. A skill whose files filled the archive to within a block of its 10 KB record ended with one zero block or none, and GNUtarwarned “A lone zero block” while unpacking it. Archives that already ended correctly keep the same bytes and digest. -
6933892: Every tab panel now has the
tabpanelrole its tab points at, including the panels of a<CodeGroup>, a TypeScript/JavaScript pair, and a nested tab group, which had none. Panels of<Tabs dropdown>no longer claim thetabpanelrole, since that layout shows a select instead of tabs to label them. -
6933892: The “On this page” highlight now skips headings inside tab panels that aren’t selected. Such a heading used to count as already scrolled past, so the table of contents highlighted it from the top of the page.
-
1b83039:
blume translatenow looks formeta.tstitles only in the folders the content source’sincludeglobs reach, the same meta files the build reads. Ameta.tselsewhere under a project-rooted source (beside tooling, say) configures no sidebar group, and it used to be translated anyway. -
b6992c0:
blume translaterewrites an existing translation where it lives. A stale hand-writtenfr/guide.mdfor aguide.mdxsource is updated in place instead of gaining afr/guide.mdxbeside it (which failed the next build with a duplicate route), a locale’s hand-writtenmeta.jsormeta.mjsis rewritten instead of getting ameta.tsnext to it, and a locale folder authored in another casing (pt-br/forpt-BR) now receives the locale’smeta.tstoo, not a secondpt-BR/folder. A source file saved with a byte order mark keeps its frontmatter in the translation; it used to be written with the body alone. Looking formeta.tstitles now skips the content source’sexcludeglobs and moves past a meta file that fails to load, where an application file likesrc/lib/meta.tsin a project-rooted source used to crash the run withBLUME_INTERNAL. -
3311cee: Redirect
frompaths in thevercel.jsona static build writes are now escaped, since Vercel reads eachsourceas a pattern. Afromlike/c++-guideor/what?used to make the whole file invalid, and/faq(old)never matched. -
be9d98e:
blume versionnow also rewrites reference-style link definitions ([setup]: /guides/setup), single-quotedhref='…'andsrc='…'attributes, and links that spell out thebasePath(/docs/guides/setupbecomes/docs/v1.0/guides/setup) inside the snapshot. Those links used to keep pointing at the current docs.