blume-migrate
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseMigrate to Blume
迁移至Blume
Blume is a markdown-first documentation framework on Astro/Vite. You drop Markdown/MDX into a folder and get navigation, search, theming, Open Graph images, and a component library with no app boilerplate — the framework is the template. There is no starter to clone; the only thing a project owns is its content and a .
blume.config.tsYour job is to convert a source docs repo into an idiomatic Blume project — not a 1:1 transliteration. Read this file, detect the source framework, open the matching for the exact mappings, and work the loop below. Report everything you drop or approximate.
references/<framework>.mdThroughout this skill (including the files), means the absolute path of the directory containing this SKILL.md — resolve it from wherever you read this file (e.g. or ). It is a placeholder to substitute, never a literal path.
references/<skill>node_modules/blume/skills/blume-migrate.claude/skills/blume-migrateBlume 是基于 Astro/Vite 的Markdown优先文档框架。你只需将 Markdown/MDX 文件放入文件夹,即可获得导航、搜索、主题、Open Graph 图片以及无需应用模板代码的组件库——框架本身就是模板。无需克隆启动项目;项目仅需维护内容和一个 文件。
blume.config.ts你的任务是将源文档仓库转换为符合Blume规范的项目——而非机械地逐行转换。阅读本文档,检测源框架,打开匹配的 文件查看精确映射规则,然后按照以下流程操作。报告所有被舍弃或近似处理的内容。
references/<framework>.md在整个技能文档(包括 下的文件)中, 指包含本SKILL.md文件的目录的绝对路径——请根据读取本文档的位置解析该路径(例如 或 )。它是一个占位符,需替换为实际路径,而非字面路径。
references/<skill>node_modules/blume/skills/blume-migrate.claude/skills/blume-migrateMigration philosophy
迁移理念
- Target idiomatic Blume, not a mechanical port. Prefer filesystem-derived navigation over an exhaustive explicit . Prefer
navigation.sidebardirectives over JSX callouts. Prefer Blume defaults over restating them in config.::: - Every field has a default; is a valid config. Map only what the source declares. If the source uses a framework default, don't write it.
{} - Drop chrome that has no Blume equivalent — and say so. Navbar CTAs, footer columns, custom theming, dynamic redirects, and unmappable icons get reported to the user, not silently discarded or faked.
- Convert, don't preserve. Blume's page frontmatter schema is strict — unknown keys are build errors. A source-only frontmatter key must be mapped to a Blume key or removed (and reported), never left to "maybe validate."
- 以符合Blume规范为目标,而非机械移植。优先使用文件系统派生导航,而非详尽的显式 。优先使用
navigation.sidebar指令而非JSX提示框。优先使用Blume默认配置,而非在配置中重复声明。::: - 每个字段都有默认值;是有效的配置。仅映射源框架中已声明的字段。如果源框架使用的是框架默认值,则无需写入配置。
{} - 舍弃Blume无等效功能的装饰元素,并告知用户。导航栏CTA、页脚列、自定义主题、动态重定向和无法映射的图标需向用户报告,而非静默丢弃或伪造。
- 转换而非保留。Blume的页面前置元数据(frontmatter)模式是严格的——未知键会导致构建错误。源框架独有的前置元数据键必须映射到Blume的对应键或移除(并报告),绝不能留待“可能验证”。
Migration workflow
迁移流程
- Detect the source framework and read its reference file:
- /
docs.json→ Mintlify (mint.json) — the deepest, config-declared nav.references/mintlify.md - → Docusaurus (
docusaurus.config.*).references/docusaurus.md - +
meta.jsondeps (content underfumadocs-*) → Fumadocs (content/docs/).references/fumadocs.md - +
_meta.{js,ts,json}deps → Nextra (nextra).references/nextra.md - calling
astro.config.*→ Starlight (starlight({…})).references/starlight.md - Anything else → apply this file's mental model directly; there's no framework-specific reference, so inventory by hand.
- Also note the host repo, independent of source framework: a pnpm/Turbo workspace, a non-content layout, or a Vercel deploy each need integration steps (
docs/scoping,content.root, lockfile,minimumReleaseAge, an Astro/Vite patch) — all invercel.json. Read it whenever the target isn't a bare single-package docs folder.references/monorepo.md
- Inventory the repo before changing anything: the config file(s), the content tree, the nav definition, snippets/partials/includes, static assets, OpenAPI/AsyncAPI specs, redirects, i18n locales, custom components, and icon usage. Note what's declared vs. defaulted.
- Write with
blume.config.tsfromdefineConfig. Map only declared fields (see the reference's mapping table); rely on defaults everywhere else. A minimal result isblume.defineConfig({ title: "…" }) - Restructure content. Choose (default
content.root) — detect wheredocs/.mdactually live, don't assume a.mdxfolder. Many repos keep content directly under an app dir (docs/,apps/docs/api/) with no.../getting-started/subfolder; when so, setdocs/to that dir and scopecontent.rootto the real content folders rather than leaving a barecontent.includethat scans everything (seecontent.root: "."§1). Order with numeric prefixes (references/monorepo.md), group without a URL segment via01-intro.mdxfolders, and add a(group)/(meta.ts) only where filesystem order isn't enough. A source that already declares per-folder navigation in a sidecar file — FumadocsdefineMeta, Nextrameta.json— is that case: convert each one to a_meta.*, carrying over its title/icon/order/collapse, rather than dropping it and hoping filenames reproduce the intent. Filesystem inference is the fallback for folders that declare no per-folder nav, never a reason to discard one that does. Reach for an explicitmeta.tsonly when the source nav genuinely can't be expressed by files. Reshaping into folder-per-tab moves URLs — track every old→new path as you go; you'll turn them intonavigation.sidebarin step 5.redirects - Rewrite pages. Map frontmatter to Blume's strict schema; convert callout JSX to directives — directives (and math/mermaid/package-install fences) are MDX-only, so rename any
:::page that needs them to.md; rename components; inline snippets/partials (Blume has no import-based includes); fix asset paths; rewrite internal links to their new routes (including OpenAPI operation links — see the OpenAPI section, their slugs differ from most sources); add a.mdxentry for every route you moved in step 4; convert every icon name to Lucide (Blume is Lucide-only — no FontAwesome/Tabler). Remove any duplicated H1 in the body (redirectsrenders the H1; bodies start attitle). If the source has a hand-maintained changelog and the repo is open source on GitHub, offer to swap it for the##source (see "Changelogs" below) rather than porting the entries. For Mintlify, run the bundled codemod first —github-releasesdeterministically remaps icons and drops/renames unsupported frontmatter keys, and reports the rest (unknown icons, OpenAPI-stub flags) for you to finish by hand (seenode <skill>/scripts/mintlify-codemod.mjs --write <content-dir>).references/mintlify.md - Adopt . Repoint
package.json/dev/build→start/blume dev/blume build, remove the old framework's deps, addblume preview. A config-only source (e.g. a bare Mintlifyblume) has no manifest — scaffold one. In a pnpm workspace: ifdocs.json/pnpm-workspace.yamlsets.npmrc, add onlyminimumReleaseAgetoblume(don't disable the guard) so the just-published version installs. Always regenerate the lockfile in the same change: after editing deps run a plainminimumReleaseAgeExclude(from the workspace root) and commitpnpm installalongsidepnpm-lock.yaml— CI/Vercel usepackage.json, so a stale lockfile fails the build before it starts. If the repo uses (or the user wants) Ultracite for formatting: its oxfmt formatter mangles the--frozen-lockfiledirectives you just wrote unless you ship the bundled:::and register it underassets/oxfmt@0.55.0.patch— seepatchedDependencies§6. Seereferences/monorepo.md§2–3.references/monorepo.md - Wire up the host repo & deploy (non-trivial repos). For a monorepo on Vercel, emit the root-aware install/build recipe and , and tell the user the two settings you can't commit (Vercel Root Directory, Node 22). If the workspace pins Vite and
apps/docs/vercel.jsoncrashes inside Astro/Vite, apply the pnpm-patch workaround. All copy-pasteable inblume build§4–5.references/monorepo.md - Verify. Run (frontmatter schema, duplicate routes, config — without
blume build --stricta build exits 0 despite content errors, silently dropping invalid pages) and--strict(internal links, heading anchors, assets — the link checker lives inblume validate --strict, notvalidate), fix diagnostics, thenbuildfor a visual pass. End with a written summary of what was migrated, dropped, and approximated — and every repo-specific edit you made (pnpm-workspace, vercel.json, config globs) with the reason, plus any manual step left to the user (the Astro patch, Vercel dashboard settings).blume dev
- 检测源框架并阅读对应的参考文件:
- /
docs.json→ Mintlify(mint.json)——深度最深、由配置声明的导航。references/mintlify.md - → Docusaurus(
docusaurus.config.*)。references/docusaurus.md - +
meta.json依赖项(内容位于fumadocs-*下)→ Fumadocs(content/docs/)。references/fumadocs.md - +
_meta.{js,ts,json}依赖项 → Nextra(nextra)。references/nextra.md - 中调用
astro.config.*→ Starlight(starlight({…}))。references/starlight.md - 其他情况→直接应用本文档的思路;无框架特定参考文件,需手动盘点。
- 同时注意宿主仓库(独立于源框架):pnpm/Turbo工作区、非的内容布局或Vercel部署均需集成步骤(
docs/范围设置、content.root、锁文件、minimumReleaseAge、Astro/Vite补丁)——所有这些均在vercel.json中。当目标不是单一独立的文档文件夹时,请阅读该文件。references/monorepo.md
- 盘点仓库,再进行任何修改:配置文件、内容树、导航定义、代码片段/部分/包含文件、静态资源、OpenAPI/AsyncAPI规范、重定向、国际化语言环境、自定义组件和图标使用情况。记录哪些是已声明的,哪些是默认值。
- 编写 ,使用来自
blume.config.ts的blume。仅映射已声明的字段(参考对应框架的映射表);其他所有地方依赖默认值。最小化的结果可以是defineConfig。defineConfig({ title: "…" }) - 重构内容。选择 (默认值为
content.root)——检测docs/.md文件实际所在位置,不要假设存在.mdx文件夹。许多仓库将内容直接放在应用目录下(如docs/、apps/docs/api/),没有.../getting-started/子文件夹;这种情况下,将docs/设置为该目录,并将content.root限定为实际内容文件夹,而非设置为content.include扫描所有内容(见content.root: "."第1节)。使用数字前缀(如references/monorepo.md)排序,通过01-intro.mdx文件夹创建无URL段的分组,仅在文件系统顺序不足时添加(group)/(meta.ts)。如果源框架已通过辅助文件声明了每个文件夹的导航——如Fumadocs的defineMeta、Nextra的meta.json——则属于这种情况:将每个文件转换为_meta.*,保留其标题/图标/顺序/折叠状态,而非丢弃并依赖文件名重现意图。文件系统推断是未声明文件夹导航的 fallback 方案,绝不是丢弃已有导航声明的理由。仅当源导航确实无法通过文件结构表达时,才使用显式的meta.ts。将内容重构为“每个标签对应一个文件夹”会改变URL——在操作过程中跟踪每个旧路径→新路径;后续在步骤5中将其转换为navigation.sidebar条目。redirects - 重写页面。将前置元数据映射到Blume的严格模式;将JSX提示框转换为 指令——指令(以及数学公式/mermaid/package-install代码块)仅支持MDX,因此任何需要这些功能的
:::页面需重命名为.md;重命名组件;内联代码片段/部分(Blume不支持基于导入的包含);修复资源路径;重写内部链接以匹配新路由(包括OpenAPI操作链接——见OpenAPI部分,其slug与大多数源框架不同);为步骤4中移动的每个路由添加.mdx条目;将所有图标名称转换为Lucide(Blume仅支持Lucide——不支持FontAwesome/Tabler)。移除正文中重复的H1标题(redirects会渲染H1;正文从title开始)。如果源框架有手动维护的变更日志,且仓库在GitHub上开源,建议将其替换为##数据源(见下文“变更日志”部分),而非移植现有条目。对于Mintlify,先运行捆绑的代码转换工具——github-releases可确定性地重映射图标并丢弃/重命名不支持的前置元数据键,其余内容(未知图标、OpenAPI存根标记)会报告给你,由你手动完成(见node <skill>/scripts/mintlify-codemod.mjs --write <content-dir>)。references/mintlify.md - 更新 。将
package.json/dev/build脚本指向start/blume dev/blume build,移除旧框架的依赖项,添加blume preview。仅包含配置的源(如仅含Mintlifyblume)无清单文件——需生成一个。在pnpm工作区中:如果docs.json/pnpm-workspace.yaml设置了.npmrc,仅将minimumReleaseAge添加到blume(不要禁用该防护),以便安装刚发布的版本。始终在同一变更中重新生成锁文件:编辑依赖项后,运行普通的minimumReleaseAgeExclude(从工作区根目录),并将pnpm install与pnpm-lock.yaml一起提交——CI/Vercel使用package.json,过时的锁文件会在构建开始前导致失败。如果仓库使用(或用户希望使用)Ultracite 进行格式化:其oxfmt格式化工具会破坏你刚编写的--frozen-lockfile指令,除非你使用捆绑的:::并在assets/oxfmt@0.55.0.patch中注册——见patchedDependencies第6节。详见references/monorepo.md第2-3节。references/monorepo.md - 连接宿主仓库与部署(非简单仓库)。对于Vercel上的单体仓库,生成根目录感知的安装/构建脚本和 ,并告知用户无法提交的两个设置(Vercel根目录、Node 22)。如果工作区固定了Vite版本且
apps/docs/vercel.json在Astro/Vite中崩溃,应用pnpm补丁解决方法。所有可复制粘贴的内容均在blume build第4-5节。references/monorepo.md - 验证。运行 (前置元数据模式、重复路由、配置——不带
blume build --strict时,即使存在内容错误,构建也会返回0,静默丢弃无效页面)和--strict(内部链接、标题锚点、资源——链接检查器在blume validate --strict中,而非validate),修复诊断问题,然后运行build进行视觉检查。最后编写迁移总结,涵盖已迁移内容、已丢弃内容和近似处理内容——以及你所做的所有仓库特定修改(pnpm-workspace、vercel.json、配置通配符)及其原因,以及留给用户的手动步骤(Astro补丁、Vercel仪表盘设置)。blume dev
The Blume mental model
Blume核心思路
The single biggest shift for most sources — especially Mintlify — is that navigation is derived from the filesystem, not declared in config.
对于大多数源框架(尤其是Mintlify),最大的转变是导航由文件系统派生,而非在配置中声明。
Navigation is the file tree
导航即文件树
- Folders become groups, files become pages. A page's sidebar label is its frontmatter ; a group's label is the humanized folder name.
title - Ordering resolves highest-priority-first: an explicit (replaces the whole tree) → a folder's
navigation.sidebarmeta.tsarray → a page's frontmatterpages→ the filesystem (sidebar.orderfirst, then numeric filename prefix likeindex, then alphabetical).01- - refines one folder (
meta.ts). ThedefineMeta({ title, icon, order, collapsed, pages, display })array lists children by slug (numeric prefix and parentheses stripped); children you omit fall back to their ownpages, then filesystem order (sidebar.orderstill sorts first) — so a partialindexlist is safe, but list every child when the source declared a complete order.pages - Sidebar render mode: a global default with per-folder overrides. in
navigation.sidebar.displayisblume.config.ts(default),"flat"(collapsible), or"group"(drill-in sub-panel) and sets the mode for every group at once. A folder can override its own group:"page"in itsdisplay, or — sugar when the folder has anmeta.tspage —indexin that index page's frontmatter. Precedence: index frontmatter →sidebar.display→ global config →meta.ts; an override applies to that one group only (nested subgroups resolve their own chain). So a source's per-category collapse/drill-in modes migrate per folder — only reach for the global mode when the whole sidebar changes. Under an explicitflat, the config item's ownnavigation.sidebarfield is the only per-group control (frontmatter/metadisplayis ignored there, with adisplaywarning).BLUME_SIDEBAR_DISPLAY_IGNORED - An explicit replaces filesystem generation entirely. Use it only for a nav shape files can't express. Its items are a page route string, a group (
navigation.sidebar), or a link ({ label, items }).{ label, href } - Config-declared nesting has no on-disk counterpart — materialize it or it flattens silently. When a source (Mintlify , Nextra
groups, a Docusaurus sidebar…) declares a nested group, its pages usually sit flat in one folder and the grouping lives only in config. Filesystem-derived nav sees the flat folder and drops the inner group. To keep the nesting you must either move those pages into a real subfolder (_metafor label/meta.ts) — which changes their URLs, so addcollapsed— or declare the group in an explicitredirects, which nests the existing routes without moving a file. Walk confignavigation.sidebar/nav arrays recursively during inventory and record where config nesting depth exceeds on-disk depth; that gap is exactly what gets lost.pages
- 文件夹变为分组,文件变为页面。页面的侧边栏标签是其前置元数据中的 ;分组的标签是人性化的文件夹名称。
title - 排序优先级从高到低:显式的 (替换整个树)→ 文件夹的
navigation.sidebar中的meta.ts数组 → 页面前置元数据中的pages→ 文件系统顺序(sidebar.order文件优先,然后是带数字前缀的文件名如index,最后是字母顺序)。01- - 用于细化单个文件夹(
meta.ts)。defineMeta({ title, icon, order, collapsed, pages, display })数组通过slug列出子项(数字前缀和括号会被移除);未列出的子项会回退到自身的pages,然后是文件系统顺序(sidebar.order文件仍优先排序)——因此部分index列表是安全的,但当源框架声明了完整顺序时,需列出所有子项。pages - 侧边栏渲染模式:全局默认值,支持按文件夹覆盖。中的
blume.config.ts可选值为navigation.sidebar.display(默认)、"flat"(可折叠)或"group"(钻取子面板),为所有分组设置默认模式。文件夹可覆盖自身分组的模式:在其"page"中设置meta.ts,或者——当文件夹包含display文件时的简化写法——在该index页面的前置元数据中设置index。优先级:index页面前置元数据 →sidebar.display→ 全局配置 →meta.ts;覆盖仅适用于该分组(嵌套子分组会解析自身的优先级链)。因此源框架的按类别折叠/钻取模式需按文件夹迁移——仅当整个侧边栏模式改变时才使用全局模式。在显式的flat下,配置项自身的navigation.sidebar字段是唯一的分组控制方式(前置元数据/meta中的display会被忽略,并发出display警告)。BLUME_SIDEBAR_DISPLAY_IGNORED - 显式的 会完全替换文件系统生成的导航。仅在文件无法表达导航结构时使用。其项可以是页面路由字符串、分组(
navigation.sidebar)或链接({ label, items })。{ label, href } - 配置中声明的嵌套结构无磁盘对应项——需具体化,否则会被静默扁平化。当源框架(Mintlify的 、Nextra的
groups、Docusaurus的侧边栏等)声明嵌套分组时,其页面通常平放在一个文件夹中,分组仅存在于配置中。文件系统派生的导航会看到扁平文件夹,并丢弃内部分组。要保留嵌套结构,你必须要么将这些页面移动到真实的子文件夹中(使用_meta设置标签/meta.ts)——这会改变URL,因此需添加collapsed;要么在显式的redirects中声明分组,无需移动文件即可嵌套现有路由。盘点时需递归遍历配置的navigation.sidebar/导航数组,记录配置嵌套深度超过磁盘深度的地方;这些差距就是会丢失的内容。pages
Tabs and selectors
标签与选择器
- (
navigation.tabs) render top-of-header sections and scope the sidebar by route — the folder at a tab's{ label, path, icon? }becomes the section, so this needs no config beyond the tabs themselves; structure content as one folder per tab. A source's top-level tabs (Mintlifypath, a top-level product/section switcher) map to these header tabs — keep them as tabs; don't flatten them into a single globalnavigation.tabs. Blume picks the active tab by URL prefix (longest tabnavigation.sidebarthat prefixes the route), so every page in a tab must live under that tab's singlepath; a source tab that mixes arbitrary routes isn't portable as-is — either move its pages under one prefix (route change → addpath) or accept the closest shape, and say which in the report (details inredirects). The filtering runs both ways: on a route under a tab'sreferences/mintlify.md, the sidebar shows only that tab's folder (a tab also highlights when the current route is under it); on a root or untabbed route (or a tab whosepathispath), the tab folders are hidden and the sidebar shows only the loose pages that belong to no tab (full tree as a fallback, so it's never blank). Consequence for migrations: once you add tabs, the landing sidebar automatically drops the sectioned content — that's intended, not lost pages; don't hand-build excludes for it./ - (
navigation.selectors,{ kind, label, items: [{ label, path, icon?, description?, tag? }] }=kind/dropdown/product/version) partition a whole site (products, versions) via a header dropdown keyed on the current route.language - (
navigation.featured) pins links to the top of the sidebar, above every section — a blog, changelog, or support page that should always be one click away. These are the exception to tab scoping: unlike the generated tree, featured links show on every route and breakpoint.{ label, href, icon? }points anywhere — an external URL opens in a new tab, an internal route (href) is validated against your pages at build time./contactis a Lucide name (or image path/URL/inline SVG), as everywhere else. This is the home for a source's always-visible header/utility links (Mintlify anchors, Blog/Contact links) — seeicon.references/mintlify.md
- (
navigation.tabs)渲染顶部标题栏区域,并按路由限定侧边栏范围——标签{ label, path, icon? }对应的文件夹成为该部分,因此除标签本身外无需其他配置;将内容结构化为每个标签对应一个文件夹。源框架的顶级标签(Mintlify的path、顶级产品/部分切换器)映射到这些标题栏标签——保留为标签,不要扁平化到单个全局navigation.tabs中。Blume通过URL前缀选择活动标签(最长的标签navigation.sidebar作为路由前缀),因此标签下的每个页面必须位于该标签的单个path下;如果源框架的标签混合了任意路由,则无法直接移植——要么将其页面移动到一个前缀下(路由变更→添加path),要么接受最接近的结构,并在报告中说明(详情见redirects)。过滤是双向的:在属于标签references/mintlify.md的路由上,侧边栏仅显示该标签的文件夹;在根路由或未关联标签的路由(或path为path的标签)上,标签文件夹会被隐藏,侧边栏仅显示不属于任何标签的零散页面(作为回退显示完整树,因此不会空白)。迁移的结果:添加标签后,首页侧边栏会自动移除分区内容——这是预期行为,并非页面丢失;无需手动构建排除规则。/ - (
navigation.selectors,{ kind, label, items: [{ label, path, icon?, description?, tag? }] }=kind/dropdown/product/version)通过标题栏下拉菜单按当前路由划分整个站点(产品、版本)。language - (
navigation.featured)将链接固定在侧边栏顶部,所有部分之上——如博客、变更日志或支持页面,应始终一键可达。这些是标签范围的例外:与生成的树不同,特色链接在所有路由和断点上都显示。{ label, href, icon? }可指向任何地方——外部URL会在新标签页打开,内部路由(如href)会在构建时验证是否存在页面。/contact是Lucide名称(或图片路径/URL/内联SVG),与其他地方一致。这是源框架中始终可见的标题栏/实用链接(Mintlify锚点、博客/联系链接)的归宿——见icon。references/mintlify.md
Routes and pathing
路由与路径
- A route is the content path relative to , with numeric prefixes stripped (
content.root→01-intro.mdx) and/introfolders adding no segment. An(group)/file maps to its folder's route. Frontmatterindexoverrides the generated route.slug
- 路由是相对于 的内容路径,数字前缀会被移除(
content.root→01-intro.mdx),/intro文件夹不会添加路径段。(group)/文件映射到其所在文件夹的路由。前置元数据中的index会覆盖生成的路由。slug
blume.config.ts
shape
blume.config.tsblume.config.ts
结构
blume.config.tsdefineConfig({...})- Site: ,
title,description(string SVG, orlogo),{ image: string | { light, dark, alt }, text, href }(banner— no color/type). A logo renders beside{ content, link, dismissible, id }in the header, so a wordmark logo doubles the brand ("Acme Acme") — settitleto render the mark alone. Prefer the string form overtext: "": if you have the logo SVG locally and it's monochrome (solid black or white), rewrite its{ light, dark }/filltostrokeand usecurrentColor— it then inherits the theme's text color and adapts to light/dark automatically, so you don't need separate light/dark files.logo: "/logo.svg" - :
theme(a color string for both modes, oraccentper mode),{ light, dark }(color),action(mode/light/dark),system,radius(fonts— each a curated Google-font slug, a{ body, display, mono }object for any Google/Fontsource/Bunny/Fontshare family, or{ name, provider?, weights? }for local font files),{ name, variants: [{ src, weight?, style? }] }andbackground(each a string, orbackgroundImageper mode). The old{ light, dark }/accentDark/backgroundDarkfields were merged into these per-mode objects — a bare string still applies to both modes, so only reach forbackgroundImageDarkwhen the two modes differ. There is no{ light, dark }and notheme.strictconfig field — custom CSS goes in a project-roottheme.cssfile (auto-picked-up), and a source's "strict appearance" flags drop.theme.css - :
content(defaultroot, relative to the project dir where"docs"runs),blume/include(arrays of globs relative toexclude; defaultscontent.root/["**/*.{md,mdx}"]),["**/_*", "**/.*"](staged sources:sources,filesystem,github-releases,notion,sanity,mdx-remote— OpenAPI is not one of these; it's the top-levelcustomfield),openapi(custompagesdir),.astro. When docs sit directly under the project dir (nodefaultTypesubfolder), setdocs/there and scoperootto the real content folders instead of scanning everything —include§1.references/monorepo.md - (top-level): a site-wide mount point (e.g.
basePath) prepended to every route while staying invisible to the sidebar (no wrapper group). This is the right target for a source that served all docs under a prefix (Docusaurus"/docs", a FumadocsrouteBasePathofbaseUrl) — distinct from a per-source/docs(which adds a nav group) and fromprefix(host subdirectory).deployment.base - :
navigation,tabs,selectors(links pinned above the sidebar on every route),featured(sidebar—{ display, items }is the global render mode above;displayis an explicit tree),items. Avoid an explicitrepounless you have to — lean on the filesystem-derived sidebar. It only works when the file tree roughly matches the intended sidebar layout, so reshape folders to match first; reach fornavigation.sidebaronly for a shape files genuinely can't express (see "Config-declared nesting" above).sidebar.items - (Orama default, Pagefind opt-in),
search(llms.txt, Ask AI, the MCP server),ai,openapi,redirects,seo,markdown,analytics,deployment,i18n,toc,lastModified.github - Don't set . Blume auto-fills it: the dev server's
deployment.siteURL in dev, and the deployment URL (localhost/VERCEL_PROJECT_PRODUCTION_URL) on Vercel. Hardcoding it inVERCEL_URLoverrides that auto-detection and pins the wrong absolute URL (canonical links, sitemap, OG, llms.txt) everywhere but the one host you typed — so leave it unset even when a source config had ablume.config.ts/urlfield. (Sitemap still generates in production because the deploy URL is present there.)site - Favicon is a filename convention, not config. Drop /
icon(andfavicon.{svg,png,ico}) in the project root orapple-icon.png— Blume auto-detects it. There is nopublic/config field. A source favicon given asfaviconcollapses to one — pick a single file and report the loss.{ light, dark }
The schema is exported from ; the full field reference is in the directory of the installed package (see "Full documentation" below for how to locate it).
blume/schemadocs/configuration/blumedefineConfig({...})- 站点:、
title、description(字符串SVG,或logo)、{ image: string | { light, dark, alt }, text, href }(banner——无颜色/类型)。Logo会在标题栏中显示在{ content, link, dismissible, id }旁边,因此文字标志会重复品牌名(如“Acme Acme”)——设置title仅显示标志。优先使用字符串形式而非text: "":如果本地有Logo SVG且为单色(纯黑或纯白),将其{ light, dark }/fill重写为stroke,并使用currentColor——它会继承主题的文字颜色,并自动适配明暗模式,因此无需单独的明暗文件。logo: "/logo.svg" - :
theme(适用于两种模式的颜色字符串,或按模式设置的accent)、{ light, dark }(颜色)、action(mode/light/dark)、system、radius(fonts——每个可以是精选的Google字体slug、适用于任何Google/Fontsource/Bunny/Fontshare字体家族的{ body, display, mono }对象,或适用于本地字体文件的{ name, provider?, weights? })、{ name, variants: [{ src, weight?, style? }] }和background(每个可以是字符串,或按模式设置的backgroundImage)。旧的{ light, dark }/accentDark/backgroundDark字段已合并到这些按模式划分的对象中——纯字符串仍适用于两种模式,因此仅当两种模式不同时才使用backgroundImageDark。没有{ light, dark }和theme.strict配置字段——自定义CSS需放在项目根目录的theme.css文件中(会被自动识别),源框架的“严格外观”标志会被丢弃。theme.css - :
content(默认值为root,相对于运行"docs"的项目目录)、blume/include(相对于exclude的通配符数组;默认值为content.root/["**/*.{md,mdx}"])、["**/_*", "**/.*"](已支持的数据源:sources、filesystem、github-releases、notion、sanity、mdx-remote——OpenAPI不属于这些;它是顶级的custom字段)、openapi(自定义pages目录)、.astro。当文档直接放在项目目录下(无defaultType子文件夹),将docs/设置为该目录,并将root限定为实际内容文件夹,而非扫描所有内容——见include第1节。references/monorepo.md - (顶级):站点范围的挂载点(如
basePath),会添加到所有路由前,但对侧边栏不可见(无包装分组)。这是源框架在前缀下提供所有文档的合适目标(如Docusaurus的"/docs"、Fumadocs的routeBasePath形式的/docs)——与每个数据源的baseUrl(会添加导航分组)和prefix(宿主子目录)不同。deployment.base - :
navigation、tabs、selectors(固定在侧边栏顶部、所有路由可见的链接)、featured(sidebar——{ display, items }是上述全局渲染模式;display是显式树)、items。除非必要,否则避免使用显式的repo——尽量依赖文件系统派生的侧边栏。仅当文件树大致匹配预期的侧边栏布局时才有效,因此先调整文件夹结构以匹配;仅当文件确实无法表达结构时才使用navigation.sidebar(见上文“配置中声明的嵌套结构”)。sidebar.items - (默认Orama,可选Pagefind)、
search(llms.txt、Ask AI、MCP服务器)、ai、openapi、redirects、seo、markdown、analytics、deployment、i18n、toc、lastModified。github - 不要设置 。Blume会自动填充:开发环境中为开发服务器的
deployment.siteURL,Vercel上为部署URL(localhost/VERCEL_PROJECT_PRODUCTION_URL)。在VERCEL_URL中硬编码会覆盖自动检测,并在除你输入的宿主外的所有环境中设置错误的绝对URL(规范链接、站点地图、OG、llms.txt)——因此即使源框架配置中有blume.config.ts/url字段,也请留空。(生产环境仍会生成站点地图,因为部署URL在那里是可用的。)site - Favicon遵循文件名约定,而非配置。将 /
icon(以及favicon.{svg,png,ico})放在项目根目录或apple-icon.png下——Blume会自动检测。没有public/配置字段。源框架中以favicon形式提供的favicon会合并为一个——选择单个文件并报告损失。{ light, dark }
模式从 导出;完整字段参考位于已安装 包的 目录中(见下文“完整文档”了解如何定位)。
blume/schemablumedocs/configuration/Icons are Lucide, period
图标仅支持Lucide
Blume resolves bare kebab-case Lucide names everywhere an icon is accepted — frontmatter , , , / icons, and ///etc. props. There is no FontAwesome or Tabler support and no prop. Names must be kebab-case (, not ) — a PascalCase React-component name (common in Fumadocs/lucide-react sources) does not resolve and renders nothing. When migrating a source that uses another icon set (Mintlify defaults to FontAwesome), map each name to its closest Lucide equivalent; where none exists, drop the icon and report it. Verify a name exists at lucide.dev/icons before writing it.
iconsidebar.iconmeta.tsiconnavigation.tabsselectorsCardStepIconiconTypebook-openBookOpenBlume在所有接受图标的地方解析短横线命名的Lucide名称——前置元数据中的 、、 中的 、/ 图标,以及 ///等组件的props。不支持FontAwesome或Tabler,也没有 prop。名称必须是短横线命名(如 ,而非 )——PascalCase的React组件名称(在Fumadocs/lucide-react源中常见)无法解析,会渲染为空。迁移使用其他图标集的源框架时(Mintlify默认使用FontAwesome),将每个名称映射到最接近的Lucide等效项;如果没有等效项,丢弃图标并报告。写入前请在 lucide.dev/icons 验证名称是否存在。
iconsidebar.iconmeta.tsiconnavigation.tabsselectorsCardStepIconiconTypebook-openBookOpenPage frontmatter (strict — unknown keys are build errors)
页面前置元数据(严格——未知键会导致构建错误)
yaml
---
title: Install # renders as the page H1 — remove any duplicate H1 in the body
description: Install Blume and scaffold your first project.
type: doc # doc (default) | blog | changelog | api
icon: download # a Lucide name
sidebar:
label: Install # overrides title in the sidebar
order: 2
icon: download
badge: New
hidden: false
seo:
title: …
description: …
image: /og/install.png
canonical: https://…
noindex: false
search:
exclude: false
tags: [api]
slug: install # override the generated route
draft: false
lastModified: 2026-06-20 # pin the "last updated" date
---Also valid: / (blog/changelog feeds), (changelog metadata), , , .
dateauthorschangelogdeprecatedhiddennoindexyaml
---
title: 安装 # 渲染为页面H1——移除正文中重复的H1
description: 安装Blume并搭建你的第一个项目。
type: doc # doc(默认)| blog | changelog | api
icon: download # Lucide名称
sidebar:
label: 安装 # 覆盖侧边栏中的title
order: 2
icon: download
badge: 新
hidden: false
seo:
title: …
description: …
image: /og/install.png
canonical: https://…
noindex: false
search:
exclude: false
tags: [api]
slug: install # 覆盖生成的路由
draft: false
lastModified: 2026-06-20 # 固定“最后更新”日期
---同样有效的字段:/(博客/变更日志订阅源)、(变更日志元数据)、、、。
dateauthorschangelogdeprecatedhiddennoindexAuthoring features (no imports needed in .mdx
)
.mdx创作功能(.mdx
中无需导入)
.mdx- The rich features are MDX-only. Directives, , mermaid, and math are wired into the MDX processor; in a plain
package-installfile a.mdstays literal text — and the build stays green. Rename any:::notefile that uses (or should use) these to.mdduring migration. This bites hardest on Docusaurus/Starlight sources, whose.mdxcontent is full of.mdadmonitions. Plain Markdown (headings, tables, fenced code with titles/highlighting) is fine in:::..md - Callouts as directives: ,
:::note,:::tip,:::warning,:::danger,:::info, with an optional title in brackets::::success. Aliases:::warning[Heads up]→warning,caution→danger,error→note,important→warning.warn - No-import MDX components: ,
Callout/Card,CardGroup/Columns,Column/Steps,Step/Tabs,Tab/Accordion,AccordionItem,Expandable,FileTree/Tree/Tree.Folder,Tree.File,CodeGroup,Frame,Panel,Tooltip,Tile,Badge,Icon/TypeTable,AutoTypeTable,Color,YouTube,Visibility,GithubInfo,Component,CodeBlock,Diff,Prompt. (Not shipped — convert away:Math→ the<Warning>directive, and the:::warning/ParamField/ResponseFieldfield family →RequestFieldrows or the OpenAPI reference. See the reference files for targets.)TypeTable - Fenced-code superpowers: → package-manager tabs;
```package-install→ a rendered diagram; code-block titles (```mermaid), line numbers (```ts server.ts), and highlighting (lineNumbers,{1,4-5}).// [!code ++] - Math: block math renders in
$$…$$with no config (there is no.mdxfield). Inlinemarkdown.mathis not supported — a bare$…$stays literal text; convert inline math to display math or drop it (report).$
- 丰富功能仅支持MDX。指令、、mermaid和数学公式已集成到MDX处理器中;在纯
package-install文件中,.md会保持字面文本——且构建仍会成功。迁移期间,将任何使用(或应使用)这些功能的:::note文件重命名为.md。这在Docusaurus/Starlight源中最容易出问题,其.mdx内容中充满了.md警告。纯Markdown(标题、表格、带标题/高亮的代码块)在:::中是没问题的。.md - 提示框作为指令:、
:::note、:::tip、:::warning、:::danger、:::info,可在括号中添加可选标题::::success。别名::::warning[注意]→warning、caution→danger、error→note、important→warning。warn - 无需导入的MDX组件:、
Callout/Card、CardGroup/Columns、Column/Steps、Step/Tabs、Tab/Accordion、AccordionItem、Expandable、FileTree/Tree/Tree.Folder、Tree.File、CodeGroup、Frame、Panel、Tooltip、Tile、Badge、Icon/TypeTable、AutoTypeTable、Color、YouTube、Visibility、GithubInfo、Component、CodeBlock、Diff、Prompt。不支持的组件需转换:Math→<Warning>指令,:::warning/ParamField/ResponseField系列组件 →RequestField行或OpenAPI参考。参考文件中有对应的目标组件。TypeTable - 代码块增强功能:→ 包管理器标签;
```package-install→ 渲染图表;代码块标题(```mermaid)、行号(```ts server.ts)和高亮(lineNumbers、{1,4-5})。// [!code ++] - 数学公式:块级公式 在
$$…$$中无需配置即可渲染(没有.mdx字段)。内联公式markdown.math不支持——单独的$…$会保持字面文本;将内联公式转换为块级公式或丢弃(并报告)。$
OpenAPI
OpenAPI
openapi: { enabled: true, sources: [{ spec, label?, route? }] }navigation.tabsrouteopenapi.sourcesrenderer: "scalar"- Vendor the spec by default. A remote URL makes every build depend on fetching it at build time — a single point of failure in CI, offline, or behind a proxy, and a failed fetch skips the whole reference. Prefer committing the spec into the repo (
spec:) and pointingopenapi/<name>.jsonat the local path; if you keep the URL, say so and consider aspecstep that refreshes the local copy with a fallback.prebuild - Operation routes have their own slug scheme — (e.g. tag
<route>/<slugified-tag>/<slugified-operationId>, idModels→listModels). This rarely matches the source's endpoint links (Mintlify/others kebab-case differently), so rewrite every inbound link to an operation./api-reference/models/listmodelsresolves operation pages like any other route, so it catches the ones you miss.blume validate - Keep hand-written conceptual pages. Sources often pair a written "Introduction/Authentication" page with the endpoint group in the same tab. A normal content page placed under the openapi merges into the reference tab's sidebar — so keep those (auth, errors, rate limits) and delete only the per-endpoint stubs.
route
openapi: { enabled: true, sources: [{ spec, label?, route? }] }routenavigation.tabsopenapi.sourcesrenderer: "scalar"- 默认将规范文件本地化。远程 URL会使每次构建都依赖于构建时获取该文件——这是CI、离线或代理环境中的单点故障,获取失败会跳过整个参考文档。优先将规范文件提交到仓库中(如
spec:),并将openapi/<name>.json指向本地路径;如果保留URL,请告知用户,并考虑添加spec步骤刷新本地副本并提供回退。prebuild - 操作路由有自己的slug规则——(例如标签
<route>/<slugified-tag>/<slugified-operationId>、IDModels→listModels)。这很少与源框架的端点链接匹配(Mintlify/其他框架的短横线命名方式不同),因此重写所有指向操作的内部链接。/api-reference/models/listmodels会像处理其他路由一样解析操作页面,因此会遗漏的链接会被捕获。blume validate - 保留手写的概念性页面。源框架通常会将手写的“介绍/认证”页面与同一标签中的端点分组放在一起。放在openapi 下的普通内容页面会合并到参考文档标签的侧边栏中——因此保留这些页面(认证、错误、速率限制),仅删除每个端点的存根页面。
route
Changelogs
变更日志
If the source ships a hand-maintained changelog (a , a folder of dated entries, Mintlify blocks) and the project is open source on GitHub, offer to replace it with the content source — release notes become the changelog automatically, with no files to maintain. It's an offer, not an automatic rewrite: some teams keep a curated changelog that doesn't map 1:1 to GitHub releases, so confirm the release notes are the source of truth before deleting their pages.
changelog.mdx<Update>github-releasesAdd it under alongside the filesystem source:
content.sourcests
content: {
sources: [
{ include: ["docs/**/*.mdx"], root: ".", type: "filesystem" },
{
owner: "haydenbleasel",
repo: "ultracite",
prefix: "changelog",
type: "github-releases",
},
],
},- Each release materializes as a page under
type: changelog(/<prefix>/→prefix: "changelog"); omit/changelog/…to mount at the root.prefix - Optional fields: (cap materialized releases, newest-first, default 100),
limit(include prereleases),prereleases(include drafts — needs a token with repo write access),drafts(dev polling seconds; omit to freeze for the session).pollInterval - A private repo reads a token from ; it is never inlined in config. A public repo needs no token.
GITHUB_TOKEN - Delete the old changelog pages once the source is wired (and add from their old routes to the new
redirectsslugs). Pin a header/sidebar link with/<prefix>/…if the source had one.navigation.featured
如果源框架有手动维护的变更日志(如 、日期条目文件夹、Mintlify 块),且项目在GitHub上开源,建议将其替换为 数据源——发布说明会自动成为变更日志,无需维护文件。这是一个建议,而非自动重写:有些团队会维护精选的变更日志,与GitHub发布说明并非1:1对应,因此在删除现有页面前,请确认发布说明是事实来源。
changelog.mdx<Update>github-releases在 中添加该数据源,与文件系统数据源一起:
content.sourcests
content: {
sources: [
{ include: ["docs/**/*.mdx"], root: ".", type: "filesystem" },
{
owner: "haydenbleasel",
repo: "ultracite",
prefix: "changelog",
type: "github-releases",
},
],
},- 每个发布版本会生成为 页面,位于
type: changelog下(/<prefix>/→prefix: "changelog");省略/changelog/…会挂载到根目录。prefix - 可选字段:(限制生成的发布版本数量,按最新优先,默认100)、
limit(包含预发布版本)、prereleases(包含草稿——需要有仓库写入权限的令牌)、drafts(开发环境轮询秒数;省略则会话期间冻结)。pollInterval - 私有仓库会从 读取令牌;令牌绝不会内联到配置中。公共仓库无需令牌。
GITHUB_TOKEN - 删除旧的变更日志页面,一旦数据源连接完成(并添加从旧路由到新 slug的
/<prefix>/…)。如果源框架有对应的链接,使用redirects固定标题栏/侧边栏链接。navigation.featured
Redirects are static
重定向是静态的
A array in maps old URLs when you restructure routes — Blume serves these itself, so any reorganization that moves a page (folder-per-tab, materialized nested groups, renamed slugs, index promotion) is fixed by adding an entry there; no host config needed. Restructuring is the main source of these: every page you moved in step 4 (folder-per-tab, renamed slugs, index promotion) needs an entry, or old URLs 404. defaults to 301 (permanent — browsers cache it indefinitely); that's correct for genuine moves, but never use 301/308 for redirects you might reverse. Dynamic/wildcard patterns () can't be modeled as static path-to-path; move those to host-level config (, ) and report them.
redirects: [{ from, to, status? }]blume.config.tsstatus:slug*_redirectsvercel.jsonblume.config.tsredirects: [{ from, to, status? }]status:slug*_redirectsvercel.jsonVerification & reporting
验证与报告
- Run — it validates the frontmatter schema, duplicate routes, and config, and
blume build --strictmakes diagnostics fail the build (without it,--strictexits 0 despite content errors and silently drops invalid pages). Then runblume build— links, heading anchors, and assets live here, not inblume validate --strict(addbuildto also check outbound HTTP links). OpenAPI operation pages are real routes to--external, so dead links to them are caught too. Iterate until both are clean.validate - Run and review the site visually — nav structure, tabs, theme, rendered components.
blume dev - Write a migration summary covering: what was migrated (config, N pages, nav, OpenAPI), what was dropped (navbar CTAs, footers, custom theming, dynamic redirects, unmappable icons, unsupported components), and suggested follow-ups (for full control,
blume ejectto vendor a component for customization).blume add
- 运行 ——它会验证前置元数据模式、重复路由和配置,
blume build --strict会使诊断结果导致构建失败(不带该参数时,--strict即使存在内容错误也会返回0,并静默丢弃无效页面)。然后运行blume build——链接、标题锚点和资源的检查在这里,而非blume validate --strict(添加build还会检查出站HTTP链接)。OpenAPI操作页面是--external会检查的真实路由,因此指向它们的无效链接也会被捕获。迭代直到两者都无错误。validate - 运行 并视觉检查站点——导航结构、标签、主题、渲染的组件。
blume dev - 编写迁移总结,涵盖:已迁移内容(配置、N个页面、导航、OpenAPI)、已丢弃内容(导航栏CTA、页脚、自定义主题、动态重定向、无法映射的图标、不支持的组件),以及建议的后续操作(获取完全控制权、
blume eject引入组件进行自定义)。blume add
Full documentation
完整文档
The mapping details live in : one file per source framework (, , , , ), plus for host-repo integration (content-layout detection, pnpm , frozen-lockfile regeneration, the Vercel monorepo recipe, and the Astro/Vite patch). The Mintlify icon + frontmatter pass is automated by (zero-dependency, deterministic, idempotent; to apply). The authoritative Blume docs are bundled in the installed package's directory — not necessarily at the repository root: in a workspace monorepo (pnpm especially) the package lives in the depending workspace's (e.g. ); run from the depending package prints the exact location. (In a repo checkout of Blume itself, the docs source is .) The most relevant pages:
references/mintlify.mddocusaurus.mdfumadocs.mdnextra.mdstarlight.mdmonorepo.mdminimumReleaseAgescripts/mintlify-codemod.mjs--writedocs/node_modulesapps/docs/node_modules/blume/docsnode -e "console.log(require.resolve('blume/package.json'))"apps/docs/content/docs- — every
configuration/index.mdxfield.blume.config.ts - — the sidebar/tabs/selectors model.
content/navigation.mdx - —
content/meta.mdxand display modes.meta.ts - — directives, code features, math.
content/syntax.mdx - — the component library and APIs.
content/components.mdx - — the strict page schema.
reference/frontmatter.mdx
映射细节位于 下:每个源框架对应一个文件(、、、、),还有 用于宿主仓库集成(内容布局检测、pnpm 、冻结锁文件重新生成、Vercel单体仓库脚本、Astro/Vite补丁)。Mintlify图标+前置元数据处理可通过 自动化完成(零依赖、确定性、幂等;使用 应用)。权威的Blume文档捆绑在已安装包的 目录中——不一定在仓库根目录:在工作区单体仓库中(尤其是pnpm),包位于依赖工作区的 中(如 );从依赖包目录运行 会打印确切位置。(在Blume的仓库检出中,文档源位于 。)最相关的页面:
references/mintlify.mddocusaurus.mdfumadocs.mdnextra.mdstarlight.mdmonorepo.mdminimumReleaseAgescripts/mintlify-codemod.mjs--writedocs/node_modulesapps/docs/node_modules/blume/docsnode -e "console.log(require.resolve('blume/package.json'))"apps/docs/content/docs- ——
configuration/index.mdx的每个字段。blume.config.ts - ——侧边栏/标签/选择器模型。
content/navigation.mdx - ——
content/meta.mdx和显示模式。meta.ts - ——指令、代码功能、数学公式。
content/syntax.mdx - ——组件库和API。
content/components.mdx - ——严格的页面模式。",
reference/frontmatter.mdx