blume 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/cli/index.js +3349 -7024
  2. package/dist/cli/index.js.map +39 -69
  3. package/dist/types/core/config.d.ts +0 -8
  4. package/dist/types/core/data.d.ts +6 -2
  5. package/dist/types/core/i18n-ui.d.ts +50 -0
  6. package/dist/types/core/schema.d.ts +379 -485
  7. package/dist/types/core/types.d.ts +8 -6
  8. package/docs/advanced/meta.ts +1 -8
  9. package/docs/advanced/skills.mdx +28 -0
  10. package/docs/configuration/ai.mdx +58 -0
  11. package/docs/configuration/index.mdx +13 -17
  12. package/docs/configuration/seo.mdx +59 -1
  13. package/docs/configuration/theming.mdx +15 -18
  14. package/docs/content/components.mdx +2 -53
  15. package/docs/content/i18n.mdx +0 -4
  16. package/docs/content/meta.mdx +3 -17
  17. package/docs/content/navigation.mdx +41 -4
  18. package/docs/content/syntax.mdx +1 -1
  19. package/docs/index.mdx +0 -1
  20. package/docs/reference/cli.mdx +12 -13
  21. package/package.json +6 -6
  22. package/skills/blume/SKILL.md +71 -0
  23. package/skills/blume-update-docs/SKILL.md +52 -0
  24. package/skills/blume-update-docs/references/audit-checklist.md +46 -0
  25. package/src/ai/agent-readability.ts +97 -0
  26. package/src/ai/ask-context.ts +131 -8
  27. package/src/ai/ask-data.ts +4 -1
  28. package/src/astro/generate.ts +19 -12
  29. package/src/astro/integration.ts +0 -21
  30. package/src/astro/templates.ts +33 -21
  31. package/src/cli/commands/build.ts +15 -0
  32. package/src/cli/commands/dev.ts +31 -20
  33. package/src/cli/commands/validate.ts +0 -2
  34. package/src/cli/dev-lock.ts +94 -21
  35. package/src/cli/index.ts +0 -2
  36. package/src/components/BlumePage.astro +0 -6
  37. package/src/components/Icon.astro +1 -12
  38. package/src/components/content/AccordionItem.astro +3 -6
  39. package/src/components/content/Badge.astro +1 -3
  40. package/src/components/content/Callout.astro +3 -9
  41. package/src/components/content/Card.astro +2 -3
  42. package/src/components/content/ColorItem.astro +2 -2
  43. package/src/components/content/Column.astro +1 -1
  44. package/src/components/content/GithubInfo.astro +11 -10
  45. package/src/components/content/Prompt.astro +1 -1
  46. package/src/components/content/Step.astro +3 -4
  47. package/src/components/content/Tab.astro +2 -3
  48. package/src/components/content/TypeTable.astro +13 -8
  49. package/src/components/content/Update.astro +1 -1
  50. package/src/components/islands/AskAI.astro +66 -2
  51. package/src/components/islands/ask-ai.tsx +289 -53
  52. package/src/components/layout/Header.astro +27 -4
  53. package/src/components/layout/Logo.astro +5 -1
  54. package/src/components/layout/NavSelector.astro +1 -1
  55. package/src/components/layout/NavTree.astro +15 -15
  56. package/src/components/layout/PageActions.astro +73 -30
  57. package/src/components/layout/PageLayout.astro +42 -0
  58. package/src/components/layout/ReferenceLayout.astro +1 -0
  59. package/src/components/layout/RootLayout.astro +79 -4
  60. package/src/components/layout/Search.astro +5 -5
  61. package/src/components/layout/nav-utils.ts +9 -4
  62. package/src/components/openapi/ApiOverview.astro +4 -50
  63. package/src/components/openapi/ApiTagOperations.astro +42 -0
  64. package/src/core/builtin-tags.ts +1 -3
  65. package/src/core/config.ts +5 -28
  66. package/src/core/data.ts +6 -2
  67. package/src/core/graph.ts +8 -6
  68. package/src/core/i18n-ui.ts +5 -0
  69. package/src/core/links.ts +5 -19
  70. package/src/core/meta.ts +1 -1
  71. package/src/core/nav-diagnostics.ts +7 -0
  72. package/src/core/navigation.ts +38 -17
  73. package/src/core/project-graph.ts +0 -5
  74. package/src/core/schema.ts +133 -95
  75. package/src/core/sources/filesystem.ts +5 -1
  76. package/src/core/sources/resolve.ts +0 -13
  77. package/src/core/sources/watch.ts +43 -11
  78. package/src/core/types.ts +8 -6
  79. package/src/deploy/robots.ts +37 -4
  80. package/src/openapi/parse.ts +197 -14
  81. package/src/openapi/render-mdx.ts +44 -10
  82. package/src/openapi/scalar.ts +1 -1
  83. package/src/openapi/source.ts +19 -2
  84. package/src/search/documents.ts +9 -2
  85. package/src/theme/entry.ts +45 -17
  86. package/src/theme/icons.ts +18 -109
  87. package/src/theme/palette.ts +25 -51
  88. package/src/theme/twoslash.ts +6 -1
  89. package/dist/types/core/bridge.d.ts +0 -24
  90. package/dist/types/core/package-json.d.ts +0 -12
  91. package/dist/types/migrate/mintlify/assets.d.ts +0 -8
  92. package/dist/types/migrate/mintlify/config.d.ts +0 -16
  93. package/dist/types/migrate/mintlify/i18n.d.ts +0 -7
  94. package/dist/types/migrate/shared.d.ts +0 -153
  95. package/docs/advanced/bridge.mdx +0 -76
  96. package/docs/advanced/migrate.mdx +0 -124
  97. package/src/astro/static-assets.ts +0 -124
  98. package/src/cli/commands/migrate.ts +0 -39
  99. package/src/components/content/ApiField.astro +0 -75
  100. package/src/components/content/ParamField.astro +0 -39
  101. package/src/components/content/RequestField.astro +0 -23
  102. package/src/components/content/ResponseField.astro +0 -23
  103. package/src/components/content/Warning.astro +0 -9
  104. package/src/core/assets.ts +0 -31
  105. package/src/core/bridge.ts +0 -102
  106. package/src/core/sources/mintlify.ts +0 -190
  107. package/src/migrate/fumadocs/config.ts +0 -155
  108. package/src/migrate/fumadocs/content.ts +0 -376
  109. package/src/migrate/fumadocs/frontmatter.ts +0 -18
  110. package/src/migrate/fumadocs/groups.ts +0 -237
  111. package/src/migrate/fumadocs/index.ts +0 -355
  112. package/src/migrate/fumadocs/meta.ts +0 -244
  113. package/src/migrate/migrate.ts +0 -53
  114. package/src/migrate/mintlify/assets.ts +0 -46
  115. package/src/migrate/mintlify/config.ts +0 -954
  116. package/src/migrate/mintlify/content.ts +0 -120
  117. package/src/migrate/mintlify/frontmatter.ts +0 -126
  118. package/src/migrate/mintlify/i18n.ts +0 -51
  119. package/src/migrate/mintlify/icons.ts +0 -128
  120. package/src/migrate/mintlify/index.ts +0 -459
  121. package/src/migrate/mintlify/snippets.ts +0 -315
  122. package/src/migrate/mintlify/transform.ts +0 -82
  123. package/src/migrate/nextra/content.ts +0 -46
  124. package/src/migrate/nextra/frontmatter.ts +0 -40
  125. package/src/migrate/nextra/index.ts +0 -389
  126. package/src/migrate/nextra/meta.ts +0 -266
  127. package/src/migrate/shared.ts +0 -801
  128. package/src/migrate/starlight/config.ts +0 -455
  129. package/src/migrate/starlight/content.ts +0 -75
  130. package/src/migrate/starlight/frontmatter.ts +0 -111
  131. package/src/migrate/starlight/i18n.ts +0 -54
  132. package/src/migrate/starlight/index.ts +0 -131
@@ -162,23 +162,25 @@ export interface NavSelectorItem {
162
162
  icon?: string;
163
163
  tag?: string;
164
164
  }
165
- /** Top-level Mintlify-style partition selectors. */
165
+ /** Top-level partition selectors (products, versions, languages). */
166
166
  export interface NavSelector {
167
167
  label: string;
168
168
  kind: "dropdown" | "language" | "product" | "version";
169
169
  items: NavSelectorItem[];
170
170
  }
171
- /** Chrome overrides used when the current route belongs to a nav partition. */
172
- export interface NavChromeVariant {
173
- path: string;
174
- banner?: ResolvedConfig["banner"];
171
+ /** A pinned link rendered above the sidebar sections (external or internal). */
172
+ export interface FeaturedLink {
173
+ label: string;
174
+ href: string;
175
+ icon?: string;
175
176
  }
176
177
  /** The complete navigation model derived from the content graph. */
177
178
  export interface Navigation {
178
179
  tabs: NavTab[];
179
180
  selectors: NavSelector[];
180
- chromeVariants: NavChromeVariant[];
181
181
  sidebar: NavNode[];
182
+ /** Pinned links shown above the sidebar sections, unscoped by tab. */
183
+ featured: FeaturedLink[];
182
184
  /** Repo URL for the header link, or null when hidden (`navigation.repo`). */
183
185
  repoUrl?: string | null;
184
186
  }
@@ -2,13 +2,6 @@ import { defineMeta } from "blume";
2
2
 
3
3
  export default defineMeta({
4
4
  order: 5,
5
- pages: [
6
- "migrate",
7
- "bridge",
8
- "custom-pages",
9
- "changelog",
10
- "blog",
11
- "api-reference",
12
- ],
5
+ pages: ["skills", "custom-pages", "changelog", "blog", "api-reference"],
13
6
  title: "Advanced",
14
7
  });
@@ -0,0 +1,28 @@
1
+ ---
2
+ title: Skills
3
+ description: The agent skills Blume ships — playbooks that teach a coding agent to build and maintain a Blume docs site.
4
+ ---
5
+
6
+ Blume ships [agent skills](https://docs.claude.com/en/docs/claude-code/skills) — playbooks that teach a coding agent (Claude Code, Codex, Cursor) how to do a Blume-shaped job without you explaining it. They live on GitHub in the repo's `skills/` folder and are bundled in the package at `node_modules/blume/skills/` once Blume is installed, so any agent can be pointed at a `SKILL.md` directly.
7
+
8
+ ## Blume
9
+
10
+ The core skill. It teaches the agent what Blume is and how to scaffold, write, and configure a site — filesystem-derived navigation, the config schema, content components — and points it at the full docs bundled in the installed package (`node_modules/blume/docs`). Install it in any project where an agent helps you build your docs:
11
+
12
+ ```bash
13
+ npx skills add haydenbleasel/blume
14
+ ```
15
+
16
+ ## Self-updating docs
17
+
18
+ `blume-update-docs` keeps your docs in sync with the product they document. On each run — typically from a schedule you configure in your agent runner — it audits recently merged PRs, changelogs, config schemas, and CLI help against the docs content, updates only pages that are factually stale (feature-flagged work is ignored), verifies with `blume build`, and opens or updates a `blume/*` pull request. If nothing drifted, it reports a clean no-op instead of opening a noisy PR.
19
+
20
+ ```bash
21
+ npx skills use haydenbleasel/blume@blume-update-docs
22
+ ```
23
+
24
+ Blume doesn't host the automation — wire the skill into a scheduled task in Claude Code, a Codex or Cursor automation, or plain cron, with permission to read repo history and open PRs. A typical weekly prompt:
25
+
26
+ ```text
27
+ Use the blume-update-docs skill. Review the PRs merged within the last 7 days and compare them to the docs content. Ignore work behind feature flags. If docs need updates, make them, verify the docs build, and open a blume/* PR. If not, report what you checked and do not open a PR.
28
+ ```
@@ -62,6 +62,25 @@ ai: {
62
62
  }
63
63
  ```
64
64
 
65
+ ### Suggested questions
66
+
67
+ Seed the empty state with a few starter prompts. Each renders as a clickable suggestion — click one to send it — with an optional [Lucide icon](/docs/content/components#icon) beside the label:
68
+
69
+ ```ts blume.config.ts lineNumbers
70
+ ai: {
71
+ ask: {
72
+ enabled: true,
73
+ suggestions: [
74
+ { label: "What is Blume?", icon: "rocket" },
75
+ { label: "How do I write a docs page?", icon: "file-text" },
76
+ { label: "How do I configure the theme?", icon: "settings" },
77
+ ],
78
+ },
79
+ }
80
+ ```
81
+
82
+ `label` is the question that gets asked; `icon` is optional. Leave `suggestions` unset (or empty) and the panel opens to a plain input.
83
+
65
84
  ### Grounding
66
85
 
67
86
  Ask AI is **grounded in your docs**. For each question it retrieves the most relevant pages — using the same lexical [Orama](/docs/configuration/search) index that powers on-page search — and injects them into the model's system prompt, so answers come from your content instead of the model's own knowledge. The assistant is told to answer only from the retrieved pages, to say when something isn't covered, and to cite the pages it drew from.
@@ -175,6 +194,43 @@ A static build with `mcp.enabled` fails fast with a message telling you to set `
175
194
  claude mcp add --transport http my-docs https://docs.example.com/mcp
176
195
  ```
177
196
 
197
+ ## Agent readability
198
+
199
+ Blume writes an **`/agent-readability.json`** manifest at your site root that indexes the agent-facing surface described on this page — so an agent can discover it in a single fetch instead of guessing at conventions or scraping HTML. Unlike the features above, it's on by default:
200
+
201
+ ```ts blume.config.ts lineNumbers
202
+ seo: {
203
+ agentReadability: true,
204
+ }
205
+ ```
206
+
207
+ The manifest lists only what you've enabled — the [raw Markdown](#raw-markdown) mirror pattern, [`llms.txt`](#llmstxt) and `llms-full.txt`, the [MCP server](#mcp-server) and its discovery document, the [Ask AI](#ask-ai) endpoint, the [sitemap](/docs/configuration/seo#sitemap), and [RSS feeds](/docs/configuration/seo#rss-feeds) — alongside your site name, description, source repository, and the [content-signal](/docs/configuration/seo#content-signals) usage policy. URLs are absolute when [`deployment.site`](/docs/deployment) is set and root-relative otherwise:
208
+
209
+ ```json agent-readability.json
210
+ {
211
+ "artifacts": {
212
+ "markdown": {
213
+ "contentNegotiation": "text/markdown",
214
+ "pattern": "https://docs.example.com/{route}.md"
215
+ },
216
+ "llmsFullTxt": "https://docs.example.com/llms-full.txt",
217
+ "llmsTxt": "https://docs.example.com/llms.txt",
218
+ "mcp": {
219
+ "discovery": "https://docs.example.com/.well-known/mcp.json",
220
+ "url": "https://docs.example.com/mcp"
221
+ }
222
+ },
223
+ "description": "Docs for the Acme API.",
224
+ "generator": "blume@1.0.0",
225
+ "name": "Acme Docs",
226
+ "site": "https://docs.example.com",
227
+ "contentUsage": { "search": true, "ai-input": true, "ai-train": true },
228
+ "repository": "https://github.com/acme/docs"
229
+ }
230
+ ```
231
+
232
+ Set `seo.agentReadability` to `false` to skip it, or ship your own `public/agent-readability.json` to take over — Blume never overwrites a file you place in `public/`.
233
+
178
234
  ## Agent skill
179
235
 
180
236
  Building a Blume site with the help of a coding agent? Install the Blume [agent skill](https://docs.claude.com/en/docs/claude-code/skills) so it knows how Blume works without you explaining it:
@@ -184,3 +240,5 @@ npx skills add haydenbleasel/blume
184
240
  ```
185
241
 
186
242
  The skill teaches the agent what Blume is and how to scaffold, write, and configure a site, and points it at the full docs bundled in the installed package (`node_modules/blume/docs`).
243
+
244
+ It's one of the [agent skills Blume ships](/docs/advanced/skills), alongside skills for migrating an existing docs site to Blume and keeping docs in sync with your product from a scheduled agent run.
@@ -91,7 +91,7 @@ export default defineConfig({
91
91
  | --- | --- | --- |
92
92
  | `title` | `"Documentation"` | Site name — shown in the header, page titles, OG cards. |
93
93
  | `description` | — | Default meta description, used for SEO and OG. |
94
- | `logo` | — | Brand logo shown beside the title in the header. |
94
+ | `logo` | — | Brand mark and/or wordmark shown in the header. |
95
95
  | `banner` | — | Site-wide announcement bar above the header. |
96
96
 
97
97
  ### Logo
@@ -102,18 +102,23 @@ Point `logo` at an SVG and Blume inlines it, so a `currentColor` logo follows th
102
102
  logo: "/logo.svg",
103
103
  ```
104
104
 
105
- The SVG can live at your project root or in `public/`. For raster images or separate light and dark artwork — use the object form (these must live in `public/`):
105
+ The SVG can live at your project root or in `public/`. The brand is a mark (`image`) plus a wordmark (`text`); the object form lets you set them independently:
106
106
 
107
107
  ```ts blume.config.ts lineNumbers
108
108
  logo: {
109
- light: "/logo-light.png",
110
- dark: "/logo-dark.png",
111
- alt: "Acme",
112
- href: "/",
109
+ image: "/logo.svg", // string, or { light, dark, alt } for themed raster art
110
+ text: "Acme", // wordmark beside the mark
111
+ href: "/", // overrides the brand link (defaults to "/")
113
112
  },
114
113
  ```
115
114
 
116
- `href` overrides the logo's link (it defaults to `/`).
115
+ `image` takes the same value as the shorthand — a single path, or `{ light, dark, alt }` for separate light/dark artwork (raster images must live in `public/`).
116
+
117
+ `text` controls the wordmark independently of the mark:
118
+
119
+ - **Omit `text`** and the brand uses your site `title` (the default).
120
+ - **Set `text: ""`** to show the mark alone — handy when the logo image already includes the wordmark.
121
+ - **Set `text` with no `image`** for a text-only logo.
117
122
 
118
123
  ### Favicon
119
124
 
@@ -177,17 +182,8 @@ content: {
177
182
  | `exclude` | `["**/_*", "**/.*"]` | Globs to ignore (underscore- and dot-files). |
178
183
  | `pages` | `"pages"` | Folder for custom `.astro` pages. |
179
184
  | `defaultType` | `"doc"` | Page `type` used when frontmatter omits it. |
180
- | `assets` | `[]` | Extra top-level directories served at the site root, alongside `public/`. |
181
-
182
- Assets normally live in `public/` — a file at `public/logo.png` is served at `/logo.png`. Use `assets` to serve additional root-level folders in place instead of moving them under `public/`. Each entry is a directory relative to your project root, served at the matching URL and copied into `dist/` on build:
183
-
184
- ```ts blume.config.ts
185
- content: {
186
- assets: ["images"], // /images/create.png → images/create.png
187
- }
188
- ```
189
185
 
190
- This is mainly useful after [migrating](/docs/advanced/migrate) from a tool that serves assets from the project root (like Mintlify), so references such as `![](/images/create.png)` keep resolving without relocating every file. [Bridge mode](/docs/advanced/bridge) uses the same mechanism to serve a Mintlify project's assets in place, untouched.
186
+ Static assets live in `public/` a file at `public/logo.png` is served at `/logo.png`, so a reference like `![](/images/create.png)` resolves against `public/images/create.png`.
191
187
 
192
188
  ## Last modified
193
189
 
@@ -154,7 +154,7 @@ Ship your own `public/sitemap.xml` to take over — Blume never overwrites a fil
154
154
 
155
155
  ## Robots
156
156
 
157
- Blume writes a `robots.txt` that allows all crawlers and adds a `Sitemap:` line pointing to the sitemap when one is available. On by default:
157
+ Blume writes a `robots.txt` that allows all crawlers, declares your [content signals](#content-signals), and adds a `Sitemap:` line pointing to the sitemap when one is available. On by default:
158
158
 
159
159
  ```ts blume.config.ts lineNumbers
160
160
  seo: {
@@ -164,9 +164,67 @@ seo: {
164
164
 
165
165
  ```txt robots.txt
166
166
  User-agent: *
167
+ Content-Signal: search=yes, ai-input=yes, ai-train=yes
167
168
  Allow: /
168
169
 
169
170
  Sitemap: https://docs.example.com/sitemap.xml
170
171
  ```
171
172
 
173
+ ### Content signals
174
+
175
+ The `Content-Signal` line — the emerging content-usage convention — declares how AI crawlers may reuse your docs. Blume emits it **on by default with every signal set to `yes`**, matching its stance that docs are open to humans and agents alike:
176
+
177
+ - `search` — traditional and AI search indexing
178
+ - `aiInput` — grounding / RAG at answer time
179
+ - `aiTrain` — model training
180
+
181
+ Restrict any signal by setting it to `false`; the ones you leave out stay `yes`:
182
+
183
+ ```ts blume.config.ts lineNumbers
184
+ seo: {
185
+ contentSignals: {
186
+ aiTrain: false, // opt out of training, keep search + grounding
187
+ },
188
+ }
189
+ ```
190
+
191
+ ```txt robots.txt
192
+ User-agent: *
193
+ Content-Signal: search=yes, ai-input=yes, ai-train=no
194
+ Allow: /
195
+ ```
196
+
197
+ Set `contentSignals: false` to drop the declaration entirely:
198
+
199
+ ```ts blume.config.ts lineNumbers
200
+ seo: {
201
+ contentSignals: false,
202
+ }
203
+ ```
204
+
205
+ <TypeTable
206
+ type={{
207
+ "seo.contentSignals": {
208
+ type: "boolean | object",
209
+ description:
210
+ "Content-Signal declaration. true or omitted emits all signals as yes; false drops the line; an object sets signals individually.",
211
+ },
212
+ "contentSignals.search": {
213
+ type: "boolean",
214
+ description: "Allow use for search indexing (search). Default true.",
215
+ },
216
+ "contentSignals.aiInput": {
217
+ type: "boolean",
218
+ description:
219
+ "Allow use for AI grounding / RAG at answer time (ai-input). Default true.",
220
+ },
221
+ "contentSignals.aiTrain": {
222
+ type: "boolean",
223
+ description: "Allow use for AI model training (ai-train). Default true.",
224
+ },
225
+ }}
226
+ />
227
+
228
+ Content signals express a preference, not access control: they tell well-behaved crawlers how you'd like your content used, and it's on the crawler to honour them.
229
+
172
230
  Ship your own `public/robots.txt` to take over.
@@ -34,6 +34,8 @@ theme: {
34
34
 
35
35
  Named presets: `blue` (default), `green`, `orange`, `pink`, `purple`, `red`, and `teal`.
36
36
 
37
+ A string applies to both color modes; pass an object for [a different accent per mode](#dark-mode-colors).
38
+
37
39
  ### Radius
38
40
 
39
41
  `radius` sets the corner rounding shared by cards, code blocks, callouts, and inputs — `none`, `sm`, `md` (default), or `lg`.
@@ -89,18 +91,19 @@ Need a font that isn't listed, or want to drop back to the system stack? Overrid
89
91
 
90
92
  ### Dark-mode colors
91
93
 
92
- By default light and dark share one `accent`. Set `accentDark` for a different accent in dark mode, and `background` / `backgroundDark` to override the page background per mode:
94
+ `accent` and `background` follow one rule: a string applies to both color modes, and a `{ light, dark }` object sets each mode individually:
93
95
 
94
96
  ```ts blume.config.ts lineNumbers
95
97
  theme: {
96
- accent: "blue",
97
- accentDark: "teal", // a different accent in dark mode
98
- background: "#ffffff", // light-mode page background
99
- backgroundDark: "#0a0a0a", // dark-mode page background
98
+ accent: { light: "blue", dark: "teal" },
99
+ background: {
100
+ light: "#ffffff",
101
+ dark: "#0a0a0a",
102
+ },
100
103
  }
101
104
  ```
102
105
 
103
- Each takes a named preset or any CSS color, exactly like `accent`.
106
+ Each color takes a named preset or any CSS color. For `background` (and `backgroundImage`) either key can be omitted to override a single mode — `background: { dark: "#0a0a0a" }` keeps the default light background.
104
107
 
105
108
  ### Action color
106
109
 
@@ -112,22 +115,16 @@ theme: {
112
115
  }
113
116
  ```
114
117
 
115
- ### Background decoration
118
+ ### Background image
116
119
 
117
- Add a subtle, theme-aware pattern behind your content with `backgroundDecoration` — `gradient`, `grid`, or `windows`:
118
-
119
- ```ts blume.config.ts
120
- theme: {
121
- backgroundDecoration: "grid",
122
- }
123
- ```
124
-
125
- For a full background image, set `backgroundImage` (and `backgroundImageDark` for a dark variant) to a URL or a path under `public/`:
120
+ Set a background image behind your content with `backgroundImage` — a URL or a path under `public/`. Like the colors, a string applies to both modes and a `{ light, dark }` object sets each mode's image:
126
121
 
127
122
  ```ts blume.config.ts lineNumbers
128
123
  theme: {
129
- backgroundImage: "/bg-light.svg",
130
- backgroundImageDark: "/bg-dark.svg",
124
+ backgroundImage: {
125
+ light: "/bg-light.svg",
126
+ dark: "/bg-dark.svg",
127
+ },
131
128
  }
132
129
  ```
133
130
 
@@ -125,7 +125,7 @@ A negative or breaking state, such as a deprecation.
125
125
 
126
126
  ## Icon
127
127
 
128
- Render an icon by name — the same `icon` props power cards, steps, tiles, tabs, and sidebar entries. Names come from three bundled libraries: [Lucide](https://lucide.dev) (the default), [Font Awesome](https://fontawesome.com/icons) (its free set), and [Tabler](https://tabler.io/icons). Names are lowercase and kebab-cased (`rocket`, `gauge-high`, `book-open`).
128
+ Render an icon by name — the same `icon` prop powers cards, steps, tabs, and sidebar entries. Names come from [Lucide](https://lucide.dev/icons), lowercase and kebab-cased (`rocket`, `gauge`, `book-open`).
129
129
 
130
130
  <Icon icon="rocket" size={20} />
131
131
 
@@ -133,27 +133,10 @@ Render an icon by name — the same `icon` props power cards, steps, tiles, tabs
133
133
  <Icon icon="rocket" size={20} />
134
134
  ```
135
135
 
136
- A bare name resolves against your project's [`icons.library`](#default-library) (Lucide unless you change it). Two ways to reach another library for a single icon:
137
-
138
- - **`iconType`** selects a Font Awesome style — `solid` (default), `regular`, or `brands`: `<Icon icon="github" iconType="brands" />`. Font Awesome Pro styles (`light`, `thin`, `duotone`, `sharp-solid`) aren't in the bundled free data and fall back to solid.
139
- - **A `library:name` prefix** overrides the default per icon: `fa6-solid:gauge-high`, `fa6-brands:github`, `lucide:rocket`, `tabler:heart`.
140
-
141
- `size` sets the pixel size (default `16`) and `color` tints it (any CSS color; defaults to `currentColor`). Pass a raw `<svg>` string, an image URL, or a local image path in place of a name to render your own art, and add a `label` to expose it to assistive tech — without one, the icon is decorative.
136
+ Blume is Lucide-only — a bare name resolves against Lucide, and you can prefix a name with `lucide:` (`lucide:rocket`) for symmetry with other icon inputs. `size` sets the pixel size (default `16`) and `color` tints it (any CSS color; defaults to `currentColor`). Pass a raw `<svg>` string, an image URL, or a local image path in place of a name to render your own art, and add a `label` to expose it to assistive tech — without one, the icon is decorative.
142
137
 
143
138
  Icons resolve at build time and inline as zero-JS SVG — nothing is fetched at runtime.
144
139
 
145
- ### Default library
146
-
147
- Set which library bare icon names resolve against:
148
-
149
- ```ts title="blume.config.ts"
150
- export default defineConfig({
151
- icons: { library: "fontawesome" }, // "lucide" (default) | "fontawesome" | "tabler"
152
- });
153
- ```
154
-
155
- A `library:name` prefix always wins over this, so you can mix libraries on any default. Migrating from Mintlify sets this to `fontawesome` for you (Mintlify's default), so your existing icon names keep working.
156
-
157
140
  ## File tree
158
141
 
159
142
  Illustrate a project or folder layout. Wrap a normal Markdown list and Blume styles it as a tree — handy for explaining structure in setup and config guides.
@@ -457,40 +440,6 @@ Show or hide content by audience. `for="web"` renders only on the site; `for="ag
457
440
  <Visibility for="agents">Shown only in the generated Markdown.</Visibility>
458
441
  ```
459
442
 
460
- ## API fields
461
-
462
- Document a single request/response field — a CLI flag, an SDK argument, an endpoint parameter — with its name, type, and description. `ParamField`, `ResponseField`, and `RequestField` are Mintlify-compatible, so a migrated site's field markup renders unchanged. Each takes a `type`, plus `required`, `deprecated`, and `default`; the description is the element's body and may hold rich MDX, including a nested [Expandable](#expandable) for sub-properties.
463
-
464
- `ParamField` names the field through the attribute that marks its location — `path`, `query`, `header`, or `body` (or a plain `name`); the location shows as a small label.
465
-
466
- <ParamField path="userId" type="string" required>
467
- The unique identifier of the user.
468
- </ParamField>
469
-
470
- <ParamField query="limit" type="integer" default="20">
471
- Maximum number of results to return per page.
472
- </ParamField>
473
-
474
- <ResponseField name="createdAt" type="string">
475
- ISO 8601 timestamp for when the record was created.
476
- </ResponseField>
477
-
478
- ```mdx
479
- <ParamField path="userId" type="string" required>
480
- The unique identifier of the user.
481
- </ParamField>
482
-
483
- <ParamField query="limit" type="integer" default="20">
484
- Maximum number of results to return per page.
485
- </ParamField>
486
-
487
- <ResponseField name="createdAt" type="string">
488
- ISO 8601 timestamp for when the record was created.
489
- </ResponseField>
490
- ```
491
-
492
- For a full spec-driven API reference — one page per operation with generated schemas — use the [OpenAPI reference](/docs/advanced/api-reference) instead.
493
-
494
443
  ## Type tables
495
444
 
496
445
  Tables for documenting an object's properties — its props, types, and defaults. Write the rows by hand with `TypeTable`, or generate them straight from a TypeScript interface or type alias with `AutoTypeTable`.
@@ -22,10 +22,6 @@ i18n: {
22
22
 
23
23
  Each locale has a `code` (used in URLs), a `label` (shown in the language switcher), and an optional `dir` for right-to-left scripts (`"ltr"` by default).
24
24
 
25
- :::tip
26
- Migrating from Mintlify? [`blume migrate mintlify`](/docs/advanced/migrate) reads `navigation.languages[]` and writes the `i18n` block for you — the `default: true` language becomes `defaultLocale`, and translated folders already match Blume's layout.
27
- :::
28
-
29
25
  ## Organize translated content
30
26
 
31
27
  The default locale lives at your content root. Every other locale is a top-level folder named by its `code`, mirroring the default structure:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Folder meta
3
- description: Configure a sidebar group — its title, icon, order, display mode, and page order — with a meta.ts file.
3
+ description: Configure a sidebar group — its title, icon, order, and page order — with a meta.ts file.
4
4
  ---
5
5
 
6
6
  Every folder in your content tree becomes a sidebar group. Drop a `meta.ts` beside its pages to control how that group looks and how its children are ordered. It's entirely optional: without one, the group's label is the humanized folder name and its pages sort by [index, numeric prefix, then alphabetically](/docs/content/navigation#ordering).
@@ -16,7 +16,6 @@ export default defineMeta({
16
16
  title: "Guides",
17
17
  icon: "book-open",
18
18
  order: 2,
19
- display: "flat",
20
19
  collapsed: false,
21
20
  pages: ["configuration", "theming", "deployment"],
22
21
  });
@@ -31,25 +30,12 @@ Every field is optional — set only what you want to override.
31
30
  | `title` | `string` | The group's label. Defaults to the humanized folder name. |
32
31
  | `icon` | `string` | Icon shown next to the label. |
33
32
  | `order` | `number` | Position among sibling groups and pages. Lower numbers sort first. |
34
- | `display` | `"flat" \| "group" \| "page"` | How the group renders in the sidebar. Defaults to `flat`. See [below](#display-modes). |
35
- | `collapsed` | `boolean` | For `display: "group"`, whether the group starts collapsed. |
33
+ | `collapsed` | `boolean` | Under the [`group` display mode](/docs/content/navigation#display-modes), whether the group starts collapsed. |
36
34
  | `pages` | `string[]` | Explicit order for the group's children, by slug. |
37
35
 
38
36
  The `pages` array lists children by slug — the folder or file name with its numeric prefix and any parentheses stripped (so `01-quickstart.mdx` is `"quickstart"`). Children you leave out still appear, after the listed ones.
39
37
 
40
- ## Display modes
41
-
42
- `display` controls how a group and its items appear in the sidebar:
43
-
44
- - **`flat`** (default) — a non-collapsible header with its pages listed beneath.
45
- - **`group`** — a collapsible `<details>` disclosure. Pair it with `collapsed` to set the initial state; a group containing the current page always starts open.
46
- - **`page`** — a single row that, when clicked, slides the sidebar into a sub-panel showing only that group's items, with a back arrow at the top. The panel is route-aware, so landing directly on a page inside the group opens straight to it.
47
-
48
- :::tip
49
- `page` mode keeps deep sections tidy — reach for it when a group has many children and you'd rather drill into it than scroll past it.
50
- :::
51
-
52
- `display` works the same on a group in an [explicit sidebar](/docs/content/navigation#explicit-sidebar) config.
38
+ How groups render — flat headers, collapsible disclosures, or drill-in panels — is a sidebar-wide setting, not per folder: see [display modes](/docs/content/navigation#display-modes).
53
39
 
54
40
  ## Computed meta
55
41
 
@@ -31,7 +31,7 @@ See [Frontmatter](/docs/reference/frontmatter) for the full page schema.
31
31
 
32
32
  ## Folder groups
33
33
 
34
- Each folder becomes a sidebar group. Drop a [`meta.ts`](/docs/content/meta) beside its pages to set the group's title, icon, order, [display mode](/docs/content/meta#display-modes), and the order of its children:
34
+ Each folder becomes a sidebar group. Drop a [`meta.ts`](/docs/content/meta) beside its pages to set the group's title, icon, order, and the order of its children:
35
35
 
36
36
  ```ts meta.ts
37
37
  import { defineMeta } from "blume";
@@ -43,10 +43,32 @@ export default defineMeta({
43
43
  });
44
44
  ```
45
45
 
46
- See [Folder meta](/docs/content/meta) for every field, the three display modes, and computing meta at scan time.
46
+ See [Folder meta](/docs/content/meta) for every field and computing meta at scan time.
47
47
 
48
48
  To group pages _without_ adding a URL segment, use a parenthesized folder name — see [Pages](/docs/content#group-folders).
49
49
 
50
+ ## Display modes
51
+
52
+ `navigation.sidebar.display` sets how every sidebar group renders:
53
+
54
+ ```ts blume.config.ts lineNumbers
55
+ navigation: {
56
+ sidebar: {
57
+ display: "flat", // "flat" | "group" | "page"
58
+ },
59
+ }
60
+ ```
61
+
62
+ - **`flat`** (default) — a non-collapsible header with its pages listed beneath. Pages that aren't in any group always list first, above the group sections, so they can't be mistaken for a group's children.
63
+ - **`group`** — a collapsible `<details>` disclosure per group. Pair it with `collapsed` in [folder meta](/docs/content/meta) to set the initial state; a group containing the current page always starts open.
64
+ - **`page`** — each group is a single row that, when clicked, slides the sidebar into a sub-panel showing only that group's items, with a back arrow at the top. The panel is route-aware, so landing directly on a page inside the group opens straight to it.
65
+
66
+ :::tip
67
+ `page` mode keeps deep sections tidy — reach for it when groups have many children and you'd rather drill into them than scroll past them.
68
+ :::
69
+
70
+ A group in an [explicit sidebar](#explicit-sidebar) can override the global mode with its own `display`.
71
+
50
72
  ## Ordering
51
73
 
52
74
  When the sidebar is generated, order is resolved highest priority first:
@@ -112,9 +134,24 @@ navigation: {
112
134
 
113
135
  Each item takes a `label`, a `path`, and optional `icon`, `description`, and `tag`. `kind` (`dropdown`, `product`, `version`, or `language`) is a hint for how the selector is used; all render the same dropdown.
114
136
 
137
+ ## Featured links
138
+
139
+ Pin links to the top of the sidebar, above every section — a blog, a changelog, a contact or support page that should always be one click away. Unlike the generated tree, featured links are **not scoped by tab**: they show on every route, on every breakpoint.
140
+
141
+ ```ts blume.config.ts lineNumbers
142
+ navigation: {
143
+ featured: [
144
+ { label: "Blog", href: "https://example.com/blog", icon: "newspaper" },
145
+ { label: "Contact", href: "/contact", icon: "headphones" },
146
+ ],
147
+ }
148
+ ```
149
+
150
+ Each link takes a `label`, an `href`, and an optional `icon` (a [built-in icon](/docs/configuration/customization) name, image path/URL, or inline SVG — the same as anywhere else). An `href` may point anywhere: an external URL opens in a new tab, while an internal route (`/contact`) is validated against your pages at build time, warning you if nothing matches.
151
+
115
152
  ## Explicit sidebar
116
153
 
117
- For full control, define `navigation.sidebar` in config. When it's set, Blume uses it verbatim and skips file-system generation:
154
+ For full control, list explicit items in `navigation.sidebar` a bare array is shorthand for `sidebar.items`, and the object form combines them with a global [`display`](#display-modes). When items are set, Blume uses them verbatim and skips file-system generation:
118
155
 
119
156
  ```ts blume.config.ts lineNumbers
120
157
  navigation: {
@@ -130,7 +167,7 @@ navigation: {
130
167
  }
131
168
  ```
132
169
 
133
- Each item is a page route (a string), a group (`label` + `items`), or a link (`label` + `href`). Groups can nest, take a [`display` mode](/docs/content/meta#display-modes), and start `collapsed`.
170
+ Each item is a page route (a string), a group (`label` + `items`), or a link (`label` + `href`). Groups can nest, override the global [`display` mode](#display-modes), and start `collapsed`.
134
171
 
135
172
  ## Repository link
136
173
 
@@ -95,7 +95,7 @@ Tabulate structured data — config options, comparison matrices, parameter list
95
95
 
96
96
  ## Links and images
97
97
 
98
- Link to other pages or external sites. Images accept any path under `public/` (or another directory listed in [`content.assets`](/docs/configuration#content)) or a remote URL.
98
+ Link to other pages or external sites. Images accept any path under `public/` (served at the site root) or a remote URL.
99
99
 
100
100
  Read the [quickstart](/docs/quickstart) to get started.
101
101
 
package/docs/index.mdx CHANGED
@@ -49,7 +49,6 @@ Your [`blume.config.ts`](/docs/configuration) and every [`meta.ts`](/docs/conten
49
49
  - **Navigation** — inferred from files, refined with `meta.ts` or config.
50
50
  - **SEO** — metadata, Open Graph images, RSS feeds, and JSON-LD, [built in](/docs/configuration/seo).
51
51
  - **Customization** — component overrides, React islands, custom pages, theme tokens, and a source-component registry via `blume add`.
52
- - **Migration** — [`blume migrate mintlify | starlight | nextra | fumadocs`](/docs/advanced/migrate), or run `blume dev` on a Mintlify project as-is with [bridge mode](/docs/advanced/bridge).
53
52
  - **Eject** — `blume eject` produces a standalone Astro project that still uses the `blume` package.
54
53
 
55
54
  ## How it works
@@ -9,19 +9,18 @@ blume <command> [options]
9
9
 
10
10
  ## Commands
11
11
 
12
- | Command | Description |
13
- | --- | --- |
14
- | `blume init` | Scaffold a minimal project. |
15
- | `blume dev` | Start the dev server with hot reload. |
16
- | `blume build` | Build the static (or server) site. |
17
- | `blume preview` | Preview the last build. |
18
- | `blume add <item>` | Install a source component from the registry. |
19
- | [`blume migrate <tool>`](/docs/advanced/migrate) | Migrate from Mintlify, Starlight, Nextra, or Fumadocs. |
20
- | `blume sync` | Re-fetch remote content sources and regenerate. |
21
- | `blume eject` | Promote the runtime into a standalone Astro app. |
22
- | `blume check` | Type-check the site with `astro check`. |
23
- | `blume doctor` | Diagnose config and content problems. |
24
- | `blume validate` | Validate links across your content. |
12
+ | Command | Description |
13
+ | ------------------ | ------------------------------------------------ |
14
+ | `blume init` | Scaffold a minimal project. |
15
+ | `blume dev` | Start the dev server with hot reload. |
16
+ | `blume build` | Build the static (or server) site. |
17
+ | `blume preview` | Preview the last build. |
18
+ | `blume add <item>` | Install a source component from the registry. |
19
+ | `blume sync` | Re-fetch remote content sources and regenerate. |
20
+ | `blume eject` | Promote the runtime into a standalone Astro app. |
21
+ | `blume check` | Type-check the site with `astro check`. |
22
+ | `blume doctor` | Diagnose config and content problems. |
23
+ | `blume validate` | Validate links across your content. |
25
24
 
26
25
  ## Common flags
27
26