blume-migrate

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Migrate 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.ts
.
Your 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
references/<framework>.md
for the exact mappings, and work the loop below. Report everything you drop or approximate.
Throughout this skill (including the
references/
files),
<skill>
means the absolute path of the directory containing this SKILL.md
— resolve it from wherever you read this file (e.g.
node_modules/blume/skills/blume-migrate
or
.claude/skills/blume-migrate
). It is a placeholder to substitute, never a literal path.
Blume 是基于 Astro/Vite 的Markdown优先文档框架。你只需将 Markdown/MDX 文件放入文件夹,即可获得导航、搜索、主题、Open Graph 图片以及无需应用模板代码的组件库——框架本身就是模板。无需克隆启动项目;项目仅需维护内容和一个
blume.config.ts
文件。
你的任务是将源文档仓库转换为符合Blume规范的项目——而非机械地逐行转换。阅读本文档,检测源框架,打开匹配的
references/<framework>.md
文件查看精确映射规则,然后按照以下流程操作。报告所有被舍弃或近似处理的内容。
在整个技能文档(包括
references/
下的文件)中,
<skill>
指包含本SKILL.md文件的目录的绝对路径
——请根据读取本文档的位置解析该路径(例如
node_modules/blume/skills/blume-migrate
.claude/skills/blume-migrate
)。它是一个占位符,需替换为实际路径,而非字面路径。

Migration philosophy

迁移理念

  • Target idiomatic Blume, not a mechanical port. Prefer filesystem-derived navigation over an exhaustive explicit
    navigation.sidebar
    . Prefer
    :::
    directives 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

迁移流程

  1. Detect the source framework and read its reference file:
    • docs.json
      /
      mint.json
      Mintlify (
      references/mintlify.md
      ) — the deepest, config-declared nav.
    • docusaurus.config.*
      Docusaurus (
      references/docusaurus.md
      ).
    • meta.json
      +
      fumadocs-*
      deps (content under
      content/docs/
      ) → Fumadocs (
      references/fumadocs.md
      ).
    • _meta.{js,ts,json}
      +
      nextra
      deps → Nextra (
      references/nextra.md
      ).
    • astro.config.*
      calling
      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-
      docs/
      content layout, or a Vercel deploy each need integration steps (
      content.root
      scoping,
      minimumReleaseAge
      , lockfile,
      vercel.json
      , an Astro/Vite patch) — all in
      references/monorepo.md
      . Read it whenever the target isn't a bare single-package docs folder.
  2. 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.
  3. Write
    blume.config.ts
    with
    defineConfig
    from
    blume
    . Map only declared fields (see the reference's mapping table); rely on defaults everywhere else. A minimal result is
    defineConfig({ title: "…" })
    .
  4. Restructure content. Choose
    content.root
    (default
    docs
    ) — detect where
    .md
    /
    .mdx
    actually live, don't assume a
    docs/
    folder.
    Many repos keep content directly under an app dir (
    apps/docs/api/
    ,
    .../getting-started/
    ) with no
    docs/
    subfolder; when so, set
    content.root
    to that dir and scope
    content.include
    to the real content folders rather than leaving a bare
    content.root: "."
    that scans everything (see
    references/monorepo.md
    §1). Order with numeric prefixes (
    01-intro.mdx
    ), group without a URL segment via
    (group)/
    folders, and add a
    meta.ts
    (
    defineMeta
    ) only where filesystem order isn't enough. A source that already declares per-folder navigation in a sidecar file — Fumadocs
    meta.json
    , Nextra
    _meta.*
    is that case: convert each one to a
    meta.ts
    , 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 explicit
    navigation.sidebar
    only 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 into
    redirects
    in step 5.
  5. 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
    .md
    page that needs them to
    .mdx
    ; 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
    redirects
    entry 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 (
    title
    renders the H1; bodies start at
    ##
    ). If the source has a hand-maintained changelog and the repo is open source on GitHub, offer to swap it for the
    github-releases
    source
    (see "Changelogs" below) rather than porting the entries. For Mintlify, run the bundled codemod first —
    node <skill>/scripts/mintlify-codemod.mjs --write <content-dir>
    deterministically remaps icons and drops/renames unsupported frontmatter keys, and reports the rest (unknown icons, OpenAPI-stub flags) for you to finish by hand (see
    references/mintlify.md
    ).
  6. Adopt
    package.json
    .
    Repoint
    dev
    /
    build
    /
    start
    blume dev
    /
    blume build
    /
    blume preview
    , remove the old framework's deps, add
    blume
    . A config-only source (e.g. a bare Mintlify
    docs.json
    ) has no manifest — scaffold one. In a pnpm workspace: if
    pnpm-workspace.yaml
    /
    .npmrc
    sets
    minimumReleaseAge
    , add only
    blume
    to
    minimumReleaseAgeExclude
    (don't disable the guard) so the just-published version installs. Always regenerate the lockfile in the same change: after editing deps run a plain
    pnpm install
    (from the workspace root) and commit
    pnpm-lock.yaml
    alongside
    package.json
    — CI/Vercel use
    --frozen-lockfile
    , 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
    :::
    directives you just wrote unless you ship the bundled
    assets/oxfmt@0.55.0.patch
    and register it under
    patchedDependencies
    — see
    references/monorepo.md
    §6. See
    references/monorepo.md
    §2–3.
  7. Wire up the host repo & deploy (non-trivial repos). For a monorepo on Vercel, emit the root-aware install/build recipe and
    apps/docs/vercel.json
    , and tell the user the two settings you can't commit (Vercel Root Directory, Node 22). If the workspace pins Vite and
    blume build
    crashes inside Astro/Vite, apply the pnpm-patch workaround. All copy-pasteable in
    references/monorepo.md
    §4–5.
  8. Verify. Run
    blume build --strict
    (frontmatter schema, duplicate routes, config — without
    --strict
    a build exits 0 despite content errors
    , silently dropping invalid pages) and
    blume validate --strict
    (internal links, heading anchors, assets — the link checker lives in
    validate
    , not
    build
    ), fix diagnostics, then
    blume dev
    for 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).
  1. 检测源框架并阅读对应的参考文件:
    • docs.json
      /
      mint.json
      Mintlify
      references/mintlify.md
      )——深度最深、由配置声明的导航。
    • docusaurus.config.*
      Docusaurus
      references/docusaurus.md
      )。
    • meta.json
      +
      fumadocs-*
      依赖项(内容位于
      content/docs/
      下)→ Fumadocs
      references/fumadocs.md
      )。
    • _meta.{js,ts,json}
      +
      nextra
      依赖项 → Nextra
      references/nextra.md
      )。
    • astro.config.*
      中调用
      starlight({…})
      Starlight
      references/starlight.md
      )。
    • 其他情况→直接应用本文档的思路;无框架特定参考文件,需手动盘点。
    • 同时注意宿主仓库(独立于源框架):pnpm/Turbo工作区、非
      docs/
      的内容布局或Vercel部署均需集成步骤(
      content.root
      范围设置、
      minimumReleaseAge
      、锁文件、
      vercel.json
      、Astro/Vite补丁)——所有这些均在
      references/monorepo.md
      中。当目标不是单一独立的文档文件夹时,请阅读该文件。
  2. 盘点仓库,再进行任何修改:配置文件、内容树、导航定义、代码片段/部分/包含文件、静态资源、OpenAPI/AsyncAPI规范、重定向、国际化语言环境、自定义组件和图标使用情况。记录哪些是已声明的,哪些是默认值。
  3. 编写
    blume.config.ts
    ,使用来自
    blume
    defineConfig
    。仅映射已声明的字段(参考对应框架的映射表);其他所有地方依赖默认值。最小化的结果可以是
    defineConfig({ title: "…" })
  4. 重构内容。选择
    content.root
    (默认值为
    docs
    )——检测
    .md
    /
    .mdx
    文件实际所在位置,不要假设存在
    docs/
    文件夹
    。许多仓库将内容直接放在应用目录下(如
    apps/docs/api/
    .../getting-started/
    ),没有
    docs/
    子文件夹;这种情况下,将
    content.root
    设置为该目录,并将
    content.include
    限定为实际内容文件夹,而非设置为
    content.root: "."
    扫描所有内容(见
    references/monorepo.md
    第1节)。使用数字前缀(如
    01-intro.mdx
    )排序,通过
    (group)/
    文件夹创建无URL段的分组,仅在文件系统顺序不足时添加
    meta.ts
    defineMeta
    )。如果源框架已通过辅助文件声明了每个文件夹的导航——如Fumadocs的
    meta.json
    、Nextra的
    _meta.*
    ——则属于这种情况:将每个文件转换为
    meta.ts
    ,保留其标题/图标/顺序/折叠状态,而非丢弃并依赖文件名重现意图。文件系统推断是未声明文件夹导航的 fallback 方案,绝不是丢弃已有导航声明的理由
    。仅当源导航确实无法通过文件结构表达时,才使用显式的
    navigation.sidebar
    将内容重构为“每个标签对应一个文件夹”会改变URL——在操作过程中跟踪每个旧路径→新路径;后续在步骤5中将其转换为
    redirects
    条目。
  5. 重写页面。将前置元数据映射到Blume的严格模式;将JSX提示框转换为
    :::
    指令——指令(以及数学公式/mermaid/package-install代码块)仅支持MDX,因此任何需要这些功能的
    .md
    页面需重命名为
    .mdx
    ;重命名组件;内联代码片段/部分(Blume不支持基于导入的包含);修复资源路径;重写内部链接以匹配新路由(包括OpenAPI操作链接——见OpenAPI部分,其slug与大多数源框架不同);为步骤4中移动的每个路由添加
    redirects
    条目
    将所有图标名称转换为Lucide(Blume仅支持Lucide——不支持FontAwesome/Tabler)。移除正文中重复的H1标题(
    title
    会渲染H1;正文从
    ##
    开始)。如果源框架有手动维护的变更日志,且仓库在GitHub上开源,建议将其替换为
    github-releases
    数据源
    (见下文“变更日志”部分),而非移植现有条目。对于Mintlify,先运行捆绑的代码转换工具——
    node <skill>/scripts/mintlify-codemod.mjs --write <content-dir>
    可确定性地重映射图标并丢弃/重命名不支持的前置元数据键,其余内容(未知图标、OpenAPI存根标记)会报告给你,由你手动完成(见
    references/mintlify.md
    )。
  6. 更新
    package.json
    。将
    dev
    /
    build
    /
    start
    脚本指向
    blume dev
    /
    blume build
    /
    blume preview
    ,移除旧框架的依赖项,添加
    blume
    。仅包含配置的源(如仅含Mintlify
    docs.json
    )无清单文件——需生成一个。在pnpm工作区中:如果
    pnpm-workspace.yaml
    /
    .npmrc
    设置了
    minimumReleaseAge
    ,仅将
    blume
    添加到
    minimumReleaseAgeExclude
    (不要禁用该防护),以便安装刚发布的版本。始终在同一变更中重新生成锁文件:编辑依赖项后,运行普通的
    pnpm install
    (从工作区根目录),并将
    pnpm-lock.yaml
    package.json
    一起提交——CI/Vercel使用
    --frozen-lockfile
    ,过时的锁文件会在构建开始前导致失败。如果仓库使用(或用户希望使用)Ultracite 进行格式化:其oxfmt格式化工具会破坏你刚编写的
    :::
    指令,除非你使用捆绑的
    assets/oxfmt@0.55.0.patch
    并在
    patchedDependencies
    中注册——见
    references/monorepo.md
    第6节。详见
    references/monorepo.md
    第2-3节。
  7. 连接宿主仓库与部署(非简单仓库)。对于Vercel上的单体仓库,生成根目录感知的安装/构建脚本和
    apps/docs/vercel.json
    ,并告知用户无法提交的两个设置(Vercel根目录、Node 22)。如果工作区固定了Vite版本且
    blume build
    在Astro/Vite中崩溃,应用pnpm补丁解决方法。所有可复制粘贴的内容均在
    references/monorepo.md
    第4-5节。
  8. 验证。运行
    blume build --strict
    (前置元数据模式、重复路由、配置——不带
    --strict
    时,即使存在内容错误,构建也会返回0,静默丢弃无效页面
    )和
    blume validate --strict
    (内部链接、标题锚点、资源——链接检查器在
    validate
    中,而非
    build
    ),修复诊断问题,然后运行
    blume dev
    进行视觉检查。最后编写迁移总结,涵盖已迁移内容、已丢弃内容和近似处理内容——以及你所做的所有仓库特定修改(pnpm-workspace、vercel.json、配置通配符)及其原因,以及留给用户的手动步骤(Astro补丁、Vercel仪表盘设置)。

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
    title
    ; a group's label is the humanized folder name.
  • Ordering resolves highest-priority-first: an explicit
    navigation.sidebar
    (replaces the whole tree) → a folder's
    meta.ts
    pages
    array → a page's frontmatter
    sidebar.order
    → the filesystem (
    index
    first, then numeric filename prefix like
    01-
    , then alphabetical).
  • meta.ts
    refines one folder
    (
    defineMeta({ title, icon, order, collapsed, pages, display })
    ). The
    pages
    array lists children by slug (numeric prefix and parentheses stripped); children you omit fall back to their own
    sidebar.order
    , then filesystem order (
    index
    still sorts first) — so a partial
    pages
    list is safe, but list every child when the source declared a complete order.
  • Sidebar render mode: a global default with per-folder overrides.
    navigation.sidebar.display
    in
    blume.config.ts
    is
    "flat"
    (default),
    "group"
    (collapsible), or
    "page"
    (drill-in sub-panel) and sets the mode for every group at once. A folder can override its own group:
    display
    in its
    meta.ts
    , or — sugar when the folder has an
    index
    page —
    sidebar.display
    in that index page's frontmatter. Precedence: index frontmatter →
    meta.ts
    → global config →
    flat
    ; 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 explicit
    navigation.sidebar
    , the config item's own
    display
    field is the only per-group control (frontmatter/meta
    display
    is ignored there, with a
    BLUME_SIDEBAR_DISPLAY_IGNORED
    warning).
  • An explicit
    navigation.sidebar
    replaces filesystem generation entirely.
    Use it only for a nav shape files can't express. Its items are a page route string, a group (
    { label, items }
    ), or a link (
    { label, href }
    ).
  • Config-declared nesting has no on-disk counterpart — materialize it or it flattens silently. When a source (Mintlify
    groups
    , Nextra
    _meta
    , 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 (
    meta.ts
    for label/
    collapsed
    ) — which changes their URLs, so add
    redirects
    or declare the group in an explicit
    navigation.sidebar
    , which nests the existing routes without moving a file. Walk config
    pages
    /nav arrays recursively during inventory and record where config nesting depth exceeds on-disk depth; that gap is exactly what gets lost.
  • 文件夹变为分组,文件变为页面。页面的侧边栏标签是其前置元数据中的
    title
    ;分组的标签是人性化的文件夹名称。
  • 排序优先级从高到低:显式的
    navigation.sidebar
    (替换整个树)→ 文件夹的
    meta.ts
    中的
    pages
    数组 → 页面前置元数据中的
    sidebar.order
    → 文件系统顺序(
    index
    文件优先,然后是带数字前缀的文件名如
    01-
    ,最后是字母顺序)。
  • meta.ts
    用于细化单个文件夹
    defineMeta({ title, icon, order, collapsed, pages, display })
    )。
    pages
    数组通过slug列出子项(数字前缀和括号会被移除);未列出的子项会回退到自身的
    sidebar.order
    ,然后是文件系统顺序(
    index
    文件仍优先排序)——因此部分
    pages
    列表是安全的,但当源框架声明了完整顺序时,需列出所有子项。
  • 侧边栏渲染模式:全局默认值,支持按文件夹覆盖
    blume.config.ts
    中的
    navigation.sidebar.display
    可选值为
    "flat"
    (默认)、
    "group"
    (可折叠)或
    "page"
    (钻取子面板),为所有分组设置默认模式。文件夹可覆盖自身分组的模式:在其
    meta.ts
    中设置
    display
    ,或者——当文件夹包含
    index
    文件时的简化写法——在该index页面的前置元数据中设置
    sidebar.display
    。优先级:index页面前置元数据 →
    meta.ts
    → 全局配置 →
    flat
    ;覆盖仅适用于该分组(嵌套子分组会解析自身的优先级链)。因此源框架的按类别折叠/钻取模式需按文件夹迁移——仅当整个侧边栏模式改变时才使用全局模式。在显式的
    navigation.sidebar
    下,配置项自身的
    display
    字段是唯一的分组控制方式(前置元数据/meta中的
    display
    会被忽略,并发出
    BLUME_SIDEBAR_DISPLAY_IGNORED
    警告)。
  • 显式的
    navigation.sidebar
    会完全替换文件系统生成的导航
    。仅在文件无法表达导航结构时使用。其项可以是页面路由字符串、分组(
    { label, items }
    )或链接(
    { label, href }
    )。
  • 配置中声明的嵌套结构无磁盘对应项——需具体化,否则会被静默扁平化。当源框架(Mintlify的
    groups
    、Nextra的
    _meta
    、Docusaurus的侧边栏等)声明嵌套分组时,其页面通常平放在一个文件夹中,分组仅存在于配置中。文件系统派生的导航会看到扁平文件夹,并丢弃内部分组。要保留嵌套结构,你必须要么将这些页面移动到真实的子文件夹中(使用
    meta.ts
    设置标签/
    collapsed
    )——这会改变URL,因此需添加
    redirects
    要么在显式的
    navigation.sidebar
    中声明分组,无需移动文件即可嵌套现有路由。盘点时需递归遍历配置的
    pages
    /导航数组,记录配置嵌套深度超过磁盘深度的地方;这些差距就是会丢失的内容。

Tabs and selectors

标签与选择器

  • navigation.tabs
    (
    { label, path, icon? }
    ) render top-of-header sections and scope the sidebar by route — the folder at a tab's
    path
    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 (Mintlify
    navigation.tabs
    , a top-level product/section switcher) map to these header tabs — keep them as tabs; don't flatten them into a single global
    navigation.sidebar
    .
    Blume picks the active tab by URL prefix (longest tab
    path
    that prefixes the route), so every page in a tab must live under that tab's single
    path
    ; a source tab that mixes arbitrary routes isn't portable as-is — either move its pages under one prefix (route change → add
    redirects
    ) or accept the closest shape, and say which in the report (details in
    references/mintlify.md
    ). The filtering runs both ways: on a route under a tab's
    path
    , 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 whose
    path
    is
    /
    ), 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
    /
    language
    ) partition a whole site (products, versions) via a header dropdown keyed on the current route.
  • navigation.featured
    (
    { label, href, icon? }
    ) 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.
    href
    points anywhere — an external URL opens in a new tab, an internal route (
    /contact
    ) is validated against your pages at build time.
    icon
    is 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) — see
    references/mintlify.md
    .
  • navigation.tabs
    { label, path, icon? }
    )渲染顶部标题栏区域,并按路由限定侧边栏范围——标签
    path
    对应的文件夹成为该部分,因此除标签本身外无需其他配置;将内容结构化为每个标签对应一个文件夹源框架的顶级标签(Mintlify的
    navigation.tabs
    、顶级产品/部分切换器)映射到这些标题栏标签——保留为标签,不要扁平化到单个全局
    navigation.sidebar
    。Blume通过URL前缀选择活动标签(最长的标签
    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? }
    )将链接固定在侧边栏顶部,所有部分之上——如博客、变更日志或支持页面,应始终一键可达。这些是标签范围的例外:与生成的树不同,特色链接在所有路由和断点上都显示。
    href
    可指向任何地方——外部URL会在新标签页打开,内部路由(如
    /contact
    )会在构建时验证是否存在页面。
    icon
    是Lucide名称(或图片路径/URL/内联SVG),与其他地方一致。这是源框架中始终可见的标题栏/实用链接(Mintlify锚点、博客/联系链接)的归宿——见
    references/mintlify.md

Routes and pathing

路由与路径

  • A route is the content path relative to
    content.root
    , with numeric prefixes stripped (
    01-intro.mdx
    /intro
    ) and
    (group)/
    folders adding no segment
    . An
    index
    file maps to its folder's route. Frontmatter
    slug
    overrides the generated route.
  • 路由是相对于
    content.root
    的内容路径,数字前缀会被移除
    01-intro.mdx
    /intro
    ),
    (group)/
    文件夹不会添加路径段
    index
    文件映射到其所在文件夹的路由。前置元数据中的
    slug
    会覆盖生成的路由。

blume.config.ts
shape

blume.config.ts
结构

defineConfig({...})
— every field optional, all with defaults:
  • Site:
    title
    ,
    description
    ,
    logo
    (string SVG, or
    { image: string | { light, dark, alt }, text, href }
    ),
    banner
    (
    { content, link, dismissible, id }
    — no color/type). A logo renders beside
    title
    in the header, so a wordmark logo doubles the brand ("Acme Acme") — set
    text: ""
    to render the mark alone. Prefer the string form over
    { light, dark }
    :
    if you have the logo SVG locally and it's monochrome (solid black or white), rewrite its
    fill
    /
    stroke
    to
    currentColor
    and use
    logo: "/logo.svg"
    — it then inherits the theme's text color and adapts to light/dark automatically, so you don't need separate light/dark files.
  • theme
    :
    accent
    (a color string for both modes, or
    { light, dark }
    per mode),
    action
    (color),
    mode
    (
    light
    /
    dark
    /
    system
    ),
    radius
    ,
    fonts
    (
    { body, display, mono }
    — each a curated Google-font slug, a
    { name, provider?, weights? }
    object for any Google/Fontsource/Bunny/Fontshare family, or
    { name, variants: [{ src, weight?, style? }] }
    for local font files),
    background
    and
    backgroundImage
    (each a string, or
    { light, dark }
    per mode). The old
    accentDark
    /
    backgroundDark
    /
    backgroundImageDark
    fields were merged into these per-mode objects — a bare string still applies to both modes, so only reach for
    { light, dark }
    when the two modes differ. There is no
    theme.strict
    and no
    theme.css
    config field — custom CSS goes in a project-root
    theme.css
    file
    (auto-picked-up), and a source's "strict appearance" flags drop.
  • content
    :
    root
    (default
    "docs"
    , relative to the project dir where
    blume
    runs),
    include
    /
    exclude
    (arrays of globs relative to
    content.root
    ; defaults
    ["**/*.{md,mdx}"]
    /
    ["**/_*", "**/.*"]
    ),
    sources
    (staged sources:
    filesystem
    ,
    github-releases
    ,
    notion
    ,
    sanity
    ,
    mdx-remote
    ,
    custom
    — OpenAPI is not one of these; it's the top-level
    openapi
    field),
    pages
    (custom
    .astro
    dir),
    defaultType
    . When docs sit directly under the project dir (no
    docs/
    subfolder), set
    root
    there and scope
    include
    to the real content folders
    instead of scanning everything —
    references/monorepo.md
    §1.
  • basePath
    (top-level): a site-wide mount point (e.g.
    "/docs"
    ) 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
    routeBasePath
    , a Fumadocs
    baseUrl
    of
    /docs
    ) — distinct from a per-source
    prefix
    (which adds a nav group) and from
    deployment.base
    (host subdirectory).
  • navigation
    :
    tabs
    ,
    selectors
    ,
    featured
    (links pinned above the sidebar on every route),
    sidebar
    (
    { display, items }
    display
    is the global render mode above;
    items
    is an explicit tree),
    repo
    . Avoid an explicit
    navigation.sidebar
    unless 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 for
    sidebar.items
    only for a shape files genuinely can't express (see "Config-declared nesting" above).
  • search
    (Orama default, Pagefind opt-in),
    ai
    (llms.txt, Ask AI, the MCP server),
    openapi
    ,
    redirects
    ,
    seo
    ,
    markdown
    ,
    analytics
    ,
    deployment
    ,
    i18n
    ,
    toc
    ,
    lastModified
    ,
    github
    .
  • Don't set
    deployment.site
    .
    Blume auto-fills it: the dev server's
    localhost
    URL in dev, and the deployment URL (
    VERCEL_PROJECT_PRODUCTION_URL
    /
    VERCEL_URL
    ) on Vercel. Hardcoding it in
    blume.config.ts
    overrides 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 a
    url
    /
    site
    field. (Sitemap still generates in production because the deploy URL is present there.)
  • Favicon is a filename convention, not config. Drop
    icon
    /
    favicon.{svg,png,ico}
    (and
    apple-icon.png
    ) in the project root or
    public/
    — Blume auto-detects it. There is no
    favicon
    config field. A source favicon given as
    { light, dark }
    collapses to one — pick a single file and report the loss.
The schema is exported from
blume/schema
; the full field reference is in the
docs/configuration/
directory of the installed
blume
package (see "Full documentation" below for how to locate it).
defineConfig({...})
——所有字段都是可选的,均有默认值:
  • 站点
    title
    description
    logo
    (字符串SVG,或
    { image: string | { light, dark, alt }, text, href }
    )、
    banner
    { content, link, dismissible, id }
    ——无颜色/类型)。Logo会在标题栏中显示在
    title
    旁边,因此文字标志会重复品牌名(如“Acme Acme”)——设置
    text: ""
    仅显示标志。优先使用字符串形式而非
    { light, dark }
    :如果本地有Logo SVG且为单色(纯黑或纯白),将其
    fill
    /
    stroke
    重写为
    currentColor
    ,并使用
    logo: "/logo.svg"
    ——它会继承主题的文字颜色,并自动适配明暗模式,因此无需单独的明暗文件。
  • theme
    accent
    (适用于两种模式的颜色字符串,或按模式设置的
    { light, dark }
    )、
    action
    (颜色)、
    mode
    light
    /
    dark
    /
    system
    )、
    radius
    fonts
    { body, display, mono }
    ——每个可以是精选的Google字体slug、适用于任何Google/Fontsource/Bunny/Fontshare字体家族的
    { name, provider?, weights? }
    对象,或适用于本地字体文件的
    { name, variants: [{ src, weight?, style? }] }
    )、
    background
    backgroundImage
    (每个可以是字符串,或按模式设置的
    { light, dark }
    )。旧的
    accentDark
    /
    backgroundDark
    /
    backgroundImageDark
    字段已合并到这些按模式划分的对象中——纯字符串仍适用于两种模式,因此仅当两种模式不同时才使用
    { light, dark }
    没有
    theme.strict
    theme.css
    配置字段——自定义CSS需放在项目根目录的
    theme.css
    文件
    中(会被自动识别),源框架的“严格外观”标志会被丢弃。
  • content
    root
    (默认值为
    "docs"
    ,相对于运行
    blume
    的项目目录)、
    include
    /
    exclude
    (相对于
    content.root
    的通配符数组;默认值为
    ["**/*.{md,mdx}"]
    /
    ["**/_*", "**/.*"]
    )、
    sources
    (已支持的数据源:
    filesystem
    github-releases
    notion
    sanity
    mdx-remote
    custom
    ——OpenAPI不属于这些;它是顶级的
    openapi
    字段)、
    pages
    (自定义
    .astro
    目录)、
    defaultType
    。当文档直接放在项目目录下(无
    docs/
    子文件夹),将
    root
    设置为该目录,并
    include
    限定为实际内容文件夹
    ,而非扫描所有内容——见
    references/monorepo.md
    第1节。
  • basePath
    (顶级):站点范围的挂载点(如
    "/docs"
    ),会添加到所有路由前,但对侧边栏不可见(无包装分组)。这是源框架在前缀下提供所有文档的合适目标(如Docusaurus的
    routeBasePath
    、Fumadocs的
    /docs
    形式的
    baseUrl
    )——与每个数据源的
    prefix
    (会添加导航分组)和
    deployment.base
    (宿主子目录)不同。
  • navigation
    tabs
    selectors
    featured
    (固定在侧边栏顶部、所有路由可见的链接)、
    sidebar
    { display, items }
    ——
    display
    是上述全局渲染模式;
    items
    是显式树)、
    repo
    除非必要,否则避免使用显式的
    navigation.sidebar
    ——尽量依赖文件系统派生的侧边栏。仅当文件树大致匹配预期的侧边栏布局时才有效,因此先调整文件夹结构以匹配;仅当文件确实无法表达结构时才使用
    sidebar.items
    (见上文“配置中声明的嵌套结构”)。
  • search
    (默认Orama,可选Pagefind)、
    ai
    (llms.txt、Ask AI、MCP服务器)、
    openapi
    redirects
    seo
    markdown
    analytics
    deployment
    i18n
    toc
    lastModified
    github
  • 不要设置
    deployment.site
    。Blume会自动填充:开发环境中为开发服务器的
    localhost
    URL,Vercel上为部署URL(
    VERCEL_PROJECT_PRODUCTION_URL
    /
    VERCEL_URL
    )。在
    blume.config.ts
    中硬编码会覆盖自动检测,并在除你输入的宿主外的所有环境中设置错误的绝对URL(规范链接、站点地图、OG、llms.txt)——因此即使源框架配置中有
    url
    /
    site
    字段,也请留空。(生产环境仍会生成站点地图,因为部署URL在那里是可用的。)
  • Favicon遵循文件名约定,而非配置。将
    icon
    /
    favicon.{svg,png,ico}
    (以及
    apple-icon.png
    )放在项目根目录或
    public/
    下——Blume会自动检测。没有
    favicon
    配置字段。源框架中以
    { light, dark }
    形式提供的favicon会合并为一个——选择单个文件并报告损失。
模式从
blume/schema
导出;完整字段参考位于已安装
blume
包的
docs/configuration/
目录中(见下文“完整文档”了解如何定位)。

Icons are Lucide, period

图标仅支持Lucide

Blume resolves bare kebab-case Lucide names everywhere an icon is accepted — frontmatter
icon
,
sidebar.icon
,
meta.ts
icon
,
navigation.tabs
/
selectors
icons, and
Card
/
Step
/
Icon
/etc. props. There is no FontAwesome or Tabler support and no
iconType
prop. Names must be kebab-case (
book-open
, not
BookOpen
) — 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.
Blume在所有接受图标的地方解析短横线命名的Lucide名称——前置元数据中的
icon
sidebar.icon
meta.ts
中的
icon
navigation.tabs
/
selectors
图标,以及
Card
/
Step
/
Icon
/等组件的props。支持FontAwesome或Tabler,也没有
iconType
prop。名称必须是短横线命名(如
book-open
,而非
BookOpen
)——PascalCase的React组件名称(在Fumadocs/lucide-react源中常见)无法解析,会渲染为空。迁移使用其他图标集的源框架时(Mintlify默认使用FontAwesome),将每个名称映射到最接近的Lucide等效项;如果没有等效项,丢弃图标并报告。写入前请在 lucide.dev/icons 验证名称是否存在。

Page 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:
date
/
authors
(blog/changelog feeds),
changelog
(changelog metadata),
deprecated
,
hidden
,
noindex
.
yaml
---
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 # 固定“最后更新”日期
---
同样有效的字段:
date
/
authors
(博客/变更日志订阅源)、
changelog
(变更日志元数据)、
deprecated
hidden
noindex

Authoring features (no imports needed in
.mdx
)

创作功能(
.mdx
中无需导入)

  • The rich features are MDX-only. Directives,
    package-install
    , mermaid, and math are wired into the MDX processor; in a plain
    .md
    file a
    :::note
    stays literal text — and the build stays green. Rename any
    .md
    file that uses (or should use) these to
    .mdx
    during migration.
    This bites hardest on Docusaurus/Starlight sources, whose
    .md
    content is full of
    :::
    admonitions. Plain Markdown (headings, tables, fenced code with titles/highlighting) is fine in
    .md
    .
  • Callouts as directives:
    :::note
    ,
    :::tip
    ,
    :::warning
    ,
    :::danger
    ,
    :::info
    ,
    :::success
    , with an optional title in brackets:
    :::warning[Heads up]
    . Aliases
    caution
    →warning,
    error
    →danger,
    important
    →note,
    warn
    →warning.
  • 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
    ,
    Math
    . (Not shipped — convert away:
    <Warning>
    → the
    :::warning
    directive, and the
    ParamField
    /
    ResponseField
    /
    RequestField
    field family →
    TypeTable
    rows or the OpenAPI reference. See the reference files for targets.)
  • Fenced-code superpowers:
    ```package-install
    → package-manager tabs;
    ```mermaid
    → a rendered diagram; code-block titles (
    ```ts server.ts
    ), line numbers (
    lineNumbers
    ), and highlighting (
    {1,4-5}
    ,
    // [!code ++]
    ).
  • Math: block math
    $$…$$
    renders in
    .mdx
    with no config (there is no
    markdown.math
    field). Inline
    $…$
    is not supported — a bare
    $
    stays literal text; convert inline math to display math or drop it (report).
  • 丰富功能仅支持MDX。指令、
    package-install
    、mermaid和数学公式已集成到MDX处理器中;在纯
    .md
    文件中,
    :::note
    会保持字面文本——且构建仍会成功。迁移期间,将任何使用(或应使用)这些功能的
    .md
    文件重命名为
    .mdx
    。这在Docusaurus/Starlight源中最容易出问题,其
    .md
    内容中充满了
    :::
    警告。纯Markdown(标题、表格、带标题/高亮的代码块)在
    .md
    中是没问题的。
  • 提示框作为指令
    :::note
    :::tip
    :::warning
    :::danger
    :::info
    :::success
    ,可在括号中添加可选标题:
    :::warning[注意]
    。别名:
    caution
    →warning、
    error
    →danger、
    important
    →note、
    warn
    →warning。
  • 无需导入的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
    系列组件 →
    TypeTable
    行或OpenAPI参考。参考文件中有对应的目标组件。
  • 代码块增强功能
    ```package-install
    → 包管理器标签;
    ```mermaid
    → 渲染图表;代码块标题(
    ```ts server.ts
    )、行号(
    lineNumbers
    )和高亮(
    {1,4-5}
    // [!code ++]
    )。
  • 数学公式:块级公式
    $$…$$
    .mdx
    中无需配置即可渲染(没有
    markdown.math
    字段)。内联公式
    $…$
    不支持——单独的
    $
    会保持字面文本;将内联公式转换为块级公式或丢弃(并报告)。

OpenAPI

OpenAPI

openapi: { enabled: true, sources: [{ spec, label?, route? }] }
generates one real page per operation — with routing, sidebar, search, and OG images for free. The reference does not get a header tab automatically — add a
navigation.tabs
entry pointing at the reference's
route
(reference routes are valid tab targets) or the API reference is unreachable from the header. Never hand-migrate generated API-reference pages (per-endpoint stub pages in the source): delete them and point
openapi.sources
at the spec. (
renderer: "scalar"
keeps the Scalar embed instead; AsyncAPI uses the same embed.)
  • Vendor the spec by default. A remote
    spec:
    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 (
    openapi/<name>.json
    ) and pointing
    spec
    at the local path; if you keep the URL, say so and consider a
    prebuild
    step that refreshes the local copy with a fallback.
  • Operation routes have their own slug scheme
    <route>/<slugified-tag>/<slugified-operationId>
    (e.g. tag
    Models
    , id
    listModels
    /api-reference/models/listmodels
    ). This rarely matches the source's endpoint links (Mintlify/others kebab-case differently), so rewrite every inbound link to an operation.
    blume validate
    resolves operation pages like any other route, so it catches the ones you miss.
  • 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
    route
    merges into the reference tab's sidebar — so keep those (auth, errors, rate limits) and delete only the per-endpoint stubs.
openapi: { enabled: true, sources: [{ spec, label?, route? }] }
会为每个操作生成真实页面——自动提供路由、侧边栏、搜索和Open Graph图片。参考文档不会自动获得标题栏标签——需添加指向参考文档
route
navigation.tabs
条目(参考文档路由是有效的标签目标),否则API参考无法从标题栏访问。永远不要手动迁移生成的API参考页面(源框架中的每个端点存根页面):删除它们,并将
openapi.sources
指向规范文件。(
renderer: "scalar"
会保留Scalar嵌入;AsyncAPI使用相同的嵌入。)
  • 默认将规范文件本地化。远程
    spec:
    URL会使每次构建都依赖于构建时获取该文件——这是CI、离线或代理环境中的单点故障,获取失败会跳过整个参考文档。优先将规范文件提交到仓库中(如
    openapi/<name>.json
    ),并将
    spec
    指向本地路径;如果保留URL,请告知用户,并考虑添加
    prebuild
    步骤刷新本地副本并提供回退。
  • 操作路由有自己的slug规则——
    <route>/<slugified-tag>/<slugified-operationId>
    (例如标签
    Models
    、ID
    listModels
    /api-reference/models/listmodels
    )。这很少与源框架的端点链接匹配(Mintlify/其他框架的短横线命名方式不同),因此重写所有指向操作的内部链接
    blume validate
    会像处理其他路由一样解析操作页面,因此会遗漏的链接会被捕获。
  • 保留手写的概念性页面。源框架通常会将手写的“介绍/认证”页面与同一标签中的端点分组放在一起。放在openapi
    route
    下的普通内容页面会合并到参考文档标签的侧边栏中——因此保留这些页面(认证、错误、速率限制),仅删除每个端点的存根页面。

Changelogs

变更日志

If the source ships a hand-maintained changelog (a
changelog.mdx
, a folder of dated entries, Mintlify
<Update>
blocks) and the project is open source on GitHub, offer to replace it with the
github-releases
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.
Add it under
content.sources
alongside the filesystem source:
ts
content: {
  sources: [
    { include: ["docs/**/*.mdx"], root: ".", type: "filesystem" },
    {
      owner: "haydenbleasel",
      repo: "ultracite",
      prefix: "changelog",
      type: "github-releases",
    },
  ],
},
  • Each release materializes as a
    type: changelog
    page under
    /<prefix>/
    (
    prefix: "changelog"
    /changelog/…
    ); omit
    prefix
    to mount at the root.
  • Optional fields:
    limit
    (cap materialized releases, newest-first, default 100),
    prereleases
    (include prereleases),
    drafts
    (include drafts — needs a token with repo write access),
    pollInterval
    (dev polling seconds; omit to freeze for the session).
  • A private repo reads a token from
    GITHUB_TOKEN
    ; it is never inlined in config. A public repo needs no token.
  • Delete the old changelog pages once the source is wired (and add
    redirects
    from their old routes to the new
    /<prefix>/…
    slugs). Pin a header/sidebar link with
    navigation.featured
    if the source had one.
如果源框架有手动维护的变更日志(如
changelog.mdx
、日期条目文件夹、Mintlify
<Update>
块),且项目在GitHub上开源,建议将其替换为
github-releases
数据源——发布说明会自动成为变更日志,无需维护文件。这是一个建议,而非自动重写:有些团队会维护精选的变更日志,与GitHub发布说明并非1:1对应,因此在删除现有页面前,请确认发布说明是事实来源。
content.sources
中添加该数据源,与文件系统数据源一起:
ts
content: {
  sources: [
    { include: ["docs/**/*.mdx"], root: ".", type: "filesystem" },
    {
      owner: "haydenbleasel",
      repo: "ultracite",
      prefix: "changelog",
      type: "github-releases",
    },
  ],
},
  • 每个发布版本会生成为
    type: changelog
    页面,位于
    /<prefix>/
    下(
    prefix: "changelog"
    /changelog/…
    );省略
    prefix
    会挂载到根目录。
  • 可选字段:
    limit
    (限制生成的发布版本数量,按最新优先,默认100)、
    prereleases
    (包含预发布版本)、
    drafts
    (包含草稿——需要有仓库写入权限的令牌)、
    pollInterval
    (开发环境轮询秒数;省略则会话期间冻结)。
  • 私有仓库会从
    GITHUB_TOKEN
    读取令牌;令牌绝不会内联到配置中。公共仓库无需令牌。
  • 删除旧的变更日志页面,一旦数据源连接完成(并添加从旧路由到新
    /<prefix>/…
    slug的
    redirects
    )。如果源框架有对应的链接,使用
    navigation.featured
    固定标题栏/侧边栏链接。

Redirects are static

重定向是静态的

A
redirects: [{ from, to, status? }]
array in
blume.config.ts
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.
status
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 (
:slug*
) can't be modeled as static path-to-path; move those to host-level config (
_redirects
,
vercel.json
) and report them.
blume.config.ts
中的
redirects: [{ from, to, status? }]
数组会在重构路由时映射旧URL——Blume会自行处理这些重定向,因此任何移动页面的重组(每个标签对应一个文件夹、具体化嵌套分组、重命名slug、提升index页面)都可通过添加条目修复;无需宿主配置。重组是这些重定向的主要来源:步骤4中移动的每个页面(每个标签对应一个文件夹、重命名slug、提升index页面)都需要一个条目,否则旧URL会返回404。
status
默认值为 301(永久重定向——浏览器会无限期缓存);这适用于真正的移动,但绝不要对可能撤销的重定向使用301/308。动态/通配符模式(如
:slug*
)无法建模为静态路径到路径的映射;将这些移动到宿主级配置(如
_redirects
vercel.json
)并报告。

Verification & reporting

验证与报告

  1. Run
    blume build --strict
    — it validates the frontmatter schema, duplicate routes, and config, and
    --strict
    makes diagnostics fail the build (without it,
    blume build
    exits 0 despite content errors and silently drops invalid pages). Then run
    blume validate --strict
    — links, heading anchors, and assets live here, not in
    build
    (add
    --external
    to also check outbound HTTP links). OpenAPI operation pages are real routes to
    validate
    , so dead links to them are caught too. Iterate until both are clean.
  2. Run
    blume dev
    and review the site visually — nav structure, tabs, theme, rendered components.
  3. 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 (
    blume eject
    for full control,
    blume add
    to vendor a component for customization).
  1. 运行
    blume build --strict
    ——它会验证前置元数据模式、重复路由和配置,
    --strict
    会使诊断结果导致构建失败(不带该参数时,
    blume build
    即使存在内容错误也会返回0,并静默丢弃无效页面)。然后运行
    blume validate --strict
    ——链接、标题锚点和资源的检查在这里,而非
    build
    (添加
    --external
    还会检查出站HTTP链接)。OpenAPI操作页面是
    validate
    会检查的真实路由,因此指向它们的无效链接也会被捕获。迭代直到两者都无错误。
  2. 运行
    blume dev
    并视觉检查站点——导航结构、标签、主题、渲染的组件。
  3. 编写迁移总结,涵盖:已迁移内容(配置、N个页面、导航、OpenAPI)、已丢弃内容(导航栏CTA、页脚、自定义主题、动态重定向、无法映射的图标、不支持的组件),以及建议的后续操作(
    blume eject
    获取完全控制权、
    blume add
    引入组件进行自定义)。

Full documentation

完整文档

The mapping details live in
references/
: one file per source framework (
mintlify.md
,
docusaurus.md
,
fumadocs.md
,
nextra.md
,
starlight.md
), plus
monorepo.md
for host-repo integration (content-layout detection, pnpm
minimumReleaseAge
, frozen-lockfile regeneration, the Vercel monorepo recipe, and the Astro/Vite patch). The Mintlify icon + frontmatter pass is automated by
scripts/mintlify-codemod.mjs
(zero-dependency, deterministic, idempotent;
--write
to apply). The authoritative Blume docs are bundled in the installed package's
docs/
directory
— not necessarily at the repository root: in a workspace monorepo (pnpm especially) the package lives in the depending workspace's
node_modules
(e.g.
apps/docs/node_modules/blume/docs
);
node -e "console.log(require.resolve('blume/package.json'))"
run from the depending package prints the exact location. (In a repo checkout of Blume itself, the docs source is
apps/docs/content/docs
.) The most relevant pages:
  • configuration/index.mdx
    — every
    blume.config.ts
    field.
  • content/navigation.mdx
    — the sidebar/tabs/selectors model.
  • content/meta.mdx
    meta.ts
    and display modes.
  • content/syntax.mdx
    — directives, code features, math.
  • content/components.mdx
    — the component library and APIs.
  • reference/frontmatter.mdx
    — the strict page schema.
映射细节位于
references/
下:每个源框架对应一个文件(
mintlify.md
docusaurus.md
fumadocs.md
nextra.md
starlight.md
),还有
monorepo.md
用于宿主仓库集成(内容布局检测、pnpm
minimumReleaseAge
、冻结锁文件重新生成、Vercel单体仓库脚本、Astro/Vite补丁)。Mintlify图标+前置元数据处理可通过
scripts/mintlify-codemod.mjs
自动化完成(零依赖、确定性、幂等;使用
--write
应用)。权威的Blume文档捆绑在已安装包的
docs/
目录
中——不一定在仓库根目录:在工作区单体仓库中(尤其是pnpm),包位于依赖工作区的
node_modules
中(如
apps/docs/node_modules/blume/docs
);从依赖包目录运行
node -e "console.log(require.resolve('blume/package.json'))"
会打印确切位置。(在Blume的仓库检出中,文档源位于
apps/docs/content/docs
。)最相关的页面:
  • configuration/index.mdx
    ——
    blume.config.ts
    的每个字段。
  • content/navigation.mdx
    ——侧边栏/标签/选择器模型。
  • content/meta.mdx
    ——
    meta.ts
    和显示模式。
  • content/syntax.mdx
    ——指令、代码功能、数学公式。
  • content/components.mdx
    ——组件库和API。
  • reference/frontmatter.mdx
    ——严格的页面模式。",