blume 0.3.0 → 0.5.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 (128) hide show
  1. package/dist/cli/index.js +1631 -940
  2. package/dist/cli/index.js.map +62 -50
  3. package/dist/types/core/data.d.ts +2 -0
  4. package/dist/types/core/project.d.ts +12 -2
  5. package/dist/types/core/schema.d.ts +442 -292
  6. package/dist/types/core/types.d.ts +7 -0
  7. package/dist/types/migrate/mintlify/assets.d.ts +8 -0
  8. package/docs/01-quickstart.mdx +5 -16
  9. package/docs/02-deployment.mdx +21 -54
  10. package/docs/advanced/api-reference.mdx +34 -51
  11. package/docs/advanced/blog.mdx +9 -25
  12. package/docs/advanced/bridge.mdx +74 -0
  13. package/docs/advanced/changelog.mdx +10 -33
  14. package/docs/advanced/custom-pages.mdx +21 -78
  15. package/docs/advanced/meta.ts +8 -1
  16. package/docs/advanced/migrate.mdx +119 -0
  17. package/docs/configuration/ai.mdx +42 -103
  18. package/docs/configuration/analytics.mdx +20 -38
  19. package/docs/configuration/customization.mdx +40 -73
  20. package/docs/configuration/export.mdx +9 -34
  21. package/docs/configuration/index.mdx +67 -87
  22. package/docs/configuration/search.mdx +17 -54
  23. package/docs/configuration/seo.mdx +17 -48
  24. package/docs/configuration/theming.mdx +20 -42
  25. package/docs/content/components.mdx +95 -101
  26. package/docs/content/i18n.mdx +21 -72
  27. package/docs/content/index.mdx +18 -48
  28. package/docs/content/islands.mdx +25 -52
  29. package/docs/content/meta.mdx +23 -50
  30. package/docs/content/navigation.mdx +23 -62
  31. package/docs/content/sources.mdx +20 -83
  32. package/docs/content/syntax.mdx +37 -105
  33. package/docs/index.mdx +12 -41
  34. package/docs/reference/cli.mdx +47 -30
  35. package/docs/reference/frontmatter.mdx +7 -5
  36. package/package.json +11 -1
  37. package/src/astro/generate.ts +18 -8
  38. package/src/astro/integration.ts +26 -3
  39. package/src/astro/islands.ts +6 -2
  40. package/src/astro/markdown-negotiation.ts +17 -3
  41. package/src/astro/pages.ts +6 -1
  42. package/src/astro/static-assets.ts +117 -0
  43. package/src/astro/templates.ts +76 -30
  44. package/src/cli/args.ts +23 -0
  45. package/src/cli/commands/build.ts +129 -62
  46. package/src/cli/commands/check.ts +20 -0
  47. package/src/cli/commands/dev.ts +11 -2
  48. package/src/cli/commands/doctor.ts +10 -1
  49. package/src/cli/commands/eject.ts +3 -1
  50. package/src/cli/commands/init.ts +21 -1
  51. package/src/cli/commands/preview.ts +2 -1
  52. package/src/cli/commands/validate.ts +12 -1
  53. package/src/cli/dev-lock.ts +92 -0
  54. package/src/cli/log.ts +11 -0
  55. package/src/cli/prepare.ts +3 -0
  56. package/src/components/BlumePage.astro +8 -0
  57. package/src/components/Icon.astro +13 -10
  58. package/src/components/content/ApiField.astro +75 -0
  59. package/src/components/content/ParamField.astro +39 -0
  60. package/src/components/content/RequestField.astro +23 -0
  61. package/src/components/content/ResponseField.astro +23 -0
  62. package/src/components/content/Step.astro +1 -1
  63. package/src/components/content/YouTube.astro +35 -0
  64. package/src/components/content/youtube.ts +46 -0
  65. package/src/components/islands/ask-ai.tsx +14 -14
  66. package/src/components/layout/Breadcrumbs.astro +7 -2
  67. package/src/components/layout/NavTree.astro +24 -8
  68. package/src/components/layout/RootLayout.astro +56 -34
  69. package/src/components/layout/Search.astro +1 -1
  70. package/src/components/openapi/ApiOverview.astro +84 -0
  71. package/src/components/openapi/MethodBadge.astro +28 -0
  72. package/src/components/openapi/Operation.astro +140 -0
  73. package/src/components/openapi/ParametersTable.astro +97 -0
  74. package/src/components/openapi/RequestBody.astro +58 -0
  75. package/src/components/openapi/RequestPanel.astro +169 -0
  76. package/src/components/openapi/Responses.astro +91 -0
  77. package/src/components/openapi/SchemaProperty.astro +118 -0
  78. package/src/components/openapi/SchemaTable.astro +86 -0
  79. package/src/components/openapi/helpers.ts +238 -0
  80. package/src/components/openapi/panel.ts +59 -0
  81. package/src/components/openapi/snippets.ts +201 -0
  82. package/src/components/props.ts +3 -0
  83. package/src/core/assets.ts +31 -0
  84. package/src/core/bridge.ts +10 -0
  85. package/src/core/builtin-tags.ts +6 -0
  86. package/src/core/data.ts +2 -0
  87. package/src/core/diagnostics.ts +6 -1
  88. package/src/core/gitignore.ts +30 -0
  89. package/src/core/links.ts +60 -19
  90. package/src/core/project-graph.ts +5 -1
  91. package/src/core/project.ts +25 -3
  92. package/src/core/schema.ts +54 -6
  93. package/src/core/sources/mdx-remote.ts +54 -8
  94. package/src/core/sources/mintlify.ts +1 -1
  95. package/src/core/sources/normalize.ts +6 -1
  96. package/src/core/sources/notion.ts +49 -5
  97. package/src/core/sources/resolve.ts +28 -6
  98. package/src/core/sources/sanity.ts +5 -1
  99. package/src/core/types.ts +7 -0
  100. package/src/deploy/rss.ts +1 -8
  101. package/src/deploy/sitemap.ts +20 -1
  102. package/src/deploy/xml.ts +8 -0
  103. package/src/markdown/directives.ts +15 -7
  104. package/src/markdown/package-commands.ts +26 -4
  105. package/src/migrate/fumadocs/content.ts +14 -1
  106. package/src/migrate/fumadocs/groups.ts +7 -0
  107. package/src/migrate/fumadocs/index.ts +5 -2
  108. package/src/migrate/mintlify/assets.ts +46 -0
  109. package/src/migrate/mintlify/config.ts +153 -1
  110. package/src/migrate/mintlify/content.ts +8 -2
  111. package/src/migrate/mintlify/index.ts +111 -46
  112. package/src/migrate/shared.ts +12 -27
  113. package/src/og/card.ts +14 -2
  114. package/src/openapi/model.ts +174 -0
  115. package/src/openapi/parse.ts +48 -0
  116. package/src/openapi/references.ts +164 -0
  117. package/src/openapi/render-mdx.ts +76 -0
  118. package/src/openapi/scalar.ts +15 -103
  119. package/src/openapi/source.ts +140 -0
  120. package/src/registry/eject.ts +28 -5
  121. package/src/registry/registry.ts +6 -0
  122. package/src/registry/rewrite-imports.ts +31 -19
  123. package/src/search/documents.ts +23 -5
  124. package/src/search/sync/algolia.ts +5 -1
  125. package/src/search/sync/typesense.ts +24 -16
  126. package/src/theme/chrome-icons.ts +22 -0
  127. package/src/theme/icons.ts +151 -161
  128. package/src/theme/palette.ts +26 -7
@@ -3,24 +3,18 @@ title: Pages
3
3
  description: How files in your content folder become pages, and how to organize them.
4
4
  ---
5
5
 
6
- Your docs are just a folder of Markdown and MDX files. Blume turns each file into
7
- a page — routing, navigation, and metadata are inferred from the file system, so
8
- there's no manifest to keep in sync.
6
+ Your docs are just a folder of Markdown and MDX files. Blume turns each file into a page — routing, navigation, and metadata are inferred from the file system, so there's no manifest to keep in sync.
9
7
 
10
- Content lives under your **content root** (`docs/` by default; change it with
11
- `content.root` in [`blume.config.ts`](/docs/configuration)).
8
+ Content lives under your **content root** (`docs/` by default; change it with `content.root` in [`blume.config.ts`](/docs/configuration)).
12
9
 
13
10
  ## Markdown and MDX
14
11
 
15
12
  Blume renders two kinds of file:
16
13
 
17
- - **`.md`** — Markdown for plain prose: GFM, frontmatter, smart punctuation, and
18
- super/subscript.
19
- - **`.mdx`** — everything `.md` has, plus [components](/docs/content/components) and
20
- the MDX-only [directives, package installs, and math](/docs/content/syntax).
14
+ - **`.md`** — Markdown for plain prose: GFM, frontmatter, smart punctuation, and super/subscript.
15
+ - **`.mdx`** — everything `.md` has, plus [components](/docs/content/components) and the MDX-only [directives, package installs, and math](/docs/content/syntax).
21
16
 
22
- Reach for `.md` when a page is just prose, and `.mdx` when it needs components or
23
- directives. Switching is as simple as renaming the file.
17
+ Reach for `.md` when a page is just prose, and `.mdx` when it needs components or directives. Switching is as simple as renaming the file.
24
18
 
25
19
  ## Files and routes
26
20
 
@@ -33,26 +27,22 @@ Each file maps to a route by its path under the content root:
33
27
  | `docs/guides/theming.mdx` | `/guides/theming` |
34
28
  | `docs/guides/index.mdx` | `/guides` |
35
29
 
36
- Nested folders become nested routes, and an `index.mdx` inside a folder becomes
37
- that folder's own page.
30
+ Nested folders become nested routes, and an `index.mdx` inside a folder becomes that folder's own page.
38
31
 
39
32
  ## Ordering with numeric prefixes
40
33
 
41
- Prefix a file or folder with a number to control its order in the sidebar. The
42
- prefix is stripped from the URL, so you can reorder pages without breaking links:
34
+ Prefix a file or folder with a number to control its order in the sidebar. The prefix is stripped from the URL, so you can reorder pages without breaking links:
43
35
 
44
36
  ```txt
45
37
  01-introduction.mdx -> /introduction
46
38
  02-installation.mdx -> /installation
47
39
  ```
48
40
 
49
- Ordering has several layers — see [Navigation](/docs/content/navigation) for the full
50
- precedence rules.
41
+ Ordering has several layers — see [Navigation](/docs/content/navigation) for the full precedence rules.
51
42
 
52
43
  ## Group folders
53
44
 
54
- Wrap a folder name in parentheses to group its pages in the sidebar **without**
55
- adding a URL segment:
45
+ Wrap a folder name in parentheses to group its pages in the sidebar **without** adding a URL segment:
56
46
 
57
47
  ```txt
58
48
  docs/(internal)/security.mdx -> /security
@@ -62,8 +52,7 @@ The pages share an “Internal” sidebar group but keep flat, parenthesis-free
62
52
 
63
53
  ## Drafts
64
54
 
65
- Mark a page as a draft to keep it out of production builds while still previewing
66
- it in `blume dev`:
55
+ Mark a page as a draft to keep it out of production builds while still previewing it in `blume dev`:
67
56
 
68
57
  ```yaml lineNumbers
69
58
  ---
@@ -76,9 +65,7 @@ draft: true
76
65
 
77
66
  ## Content types
78
67
 
79
- Every page has a **type**, set with the `type` frontmatter field (default
80
- `doc`). Types let Blume treat groups of pages differently — most importantly,
81
- `blog` and `changelog` pages are collected into [feeds](#feeds).
68
+ Every page has a **type**, set with the `type` frontmatter field (default `doc`). Types let Blume treat groups of pages differently — most importantly, `blog` and `changelog` pages are collected into [feeds](#feeds).
82
69
 
83
70
  ```yaml lineNumbers
84
71
  ---
@@ -91,25 +78,18 @@ changelog:
91
78
  ---
92
79
  ```
93
80
 
94
- The type is independent of where the file lives, but by convention blog posts
95
- go under `blog/` and changelog entries under `changelog/`. Both get an RSS feed
96
- automatically, and changelog entries are also collected into a generated
97
- [`/changelog` timeline](/docs/advanced/changelog). See [Blog](/docs/advanced/blog) and
98
- [Changelog](/docs/advanced/changelog) for authoring each.
81
+ The type is independent of where the file lives, but by convention blog posts go under `blog/` and changelog entries under `changelog/`. Both get an RSS feed automatically, and changelog entries are also collected into a generated [`/changelog` timeline](/docs/advanced/changelog). See [Blog](/docs/advanced/blog) and [Changelog](/docs/advanced/changelog) for authoring each.
99
82
 
100
83
  ## Feeds
101
84
 
102
- Blume generates an RSS feed automatically for each content type listed in
103
- [`rss.types`](/docs/configuration/seo#rss-feeds) — `blog` and `changelog` by default —
104
- as long as it has at least one page. Feeds are served at `/<type>/rss.xml`:
85
+ Blume generates an RSS feed automatically for each content type listed in [`rss.types`](/docs/configuration/seo#rss-feeds) — `blog` and `changelog` by default — as long as it has at least one page. Feeds are served at `/<type>/rss.xml`:
105
86
 
106
87
  | Type | Feed |
107
88
  | ----------- | -------------------- |
108
89
  | `blog` | `/blog/rss.xml` |
109
90
  | `changelog` | `/changelog/rss.xml` |
110
91
 
111
- Give each entry a `date` so items sort newest-first and carry a `pubDate`. An
112
- unquoted YAML date is fine — Blume normalizes it:
92
+ Give each entry a `date` so items sort newest-first and carry a `pubDate`. An unquoted YAML date is fine — Blume normalizes it:
113
93
 
114
94
  ```yaml lineNumbers
115
95
  ---
@@ -120,25 +100,15 @@ description: Why we built a markdown-first docs framework.
120
100
  ---
121
101
  ```
122
102
 
123
- Feeds need an absolute site URL, so set [`deployment.site`](/docs/deployment). Blume
124
- adds `<link rel="alternate">` tags to every page so browsers and feed readers
125
- discover them automatically. See [Blog](/docs/advanced/blog) and
126
- [Changelog](/docs/advanced/changelog) for authoring each content type.
103
+ Feeds need an absolute site URL, so set [`deployment.site`](/docs/deployment). Blume adds `<link rel="alternate">` tags to every page so browsers and feed readers discover them automatically. See [Blog](/docs/advanced/blog) and [Changelog](/docs/advanced/changelog) for authoring each content type.
127
104
 
128
105
  ## On this page
129
106
 
130
- Every page gets an automatic table of contents, built from its headings. On wide
131
- screens it sits in a sticky sidebar beside your content; on narrower screens it
132
- collapses into an **On this page** panel above the page. As you scroll, the entry
133
- for the section you're reading is highlighted, so you always know where you are in
134
- a long page.
107
+ Every page gets an automatic table of contents, built from its headings. On wide screens it sits in a sticky sidebar beside your content; on narrower screens it collapses into an **On this page** panel above the page. As you scroll, the entry for the section you're reading is highlighted, so you always know where you are in a long page.
135
108
 
136
- Blume slugifies each heading into an anchor, so every entry links straight to its
137
- section — and you can deep-link to any heading by appending its slug to the URL
138
- (`.../my-page#getting-started`).
109
+ Blume slugifies each heading into an anchor, so every entry links straight to its section — and you can deep-link to any heading by appending its slug to the URL (`.../my-page#getting-started`).
139
110
 
140
- The contents list your `##` and `###` headings (H2 and H3). A page with no
141
- headings at that level simply has no table of contents.
111
+ The contents list your `##` and `###` headings (H2 and H3). A page with no headings at that level simply has no table of contents.
142
112
 
143
113
  ## Where to next
144
114
 
@@ -3,15 +3,11 @@ title: Islands
3
3
  description: Drop an interactive component into islands/ and use it in any MDX page — hydrated automatically, no per-page import.
4
4
  ---
5
5
 
6
- Blume renders your docs as static HTML with **zero JavaScript** by default. When
7
- you need something interactive — a live demo, a chart, a playground — you add an
8
- **island**: a framework component that ships JS only for itself, only on the
9
- pages that use it.
6
+ Blume renders your docs as static HTML with **zero JavaScript** by default. When you need something interactive — a live demo, a chart, a playground — you add an **island**: a framework component that ships JS only for itself, only on the pages that use it.
10
7
 
11
8
  ## The `islands/` convention
12
9
 
13
- Drop a component into an `islands/` folder at your project root. Its filename
14
- becomes a component you can use in **any** `.mdx` page, with no import:
10
+ Drop a component into an `islands/` folder at your project root. Its filename becomes a component you can use in **any** `.mdx` page, with no import:
15
11
 
16
12
  ```tsx islands/Counter.tsx lineNumbers
17
13
  import { useState } from "react";
@@ -26,22 +22,13 @@ export default function Counter() {
26
22
  Here's a live counter: <Counter />
27
23
  ```
28
24
 
29
- The filename is the component name, so it **must be PascalCase**
30
- (`Counter.tsx` → `<Counter />`). Lowercase filenames and two islands that
31
- resolve to the same name are skipped with a build warning.
25
+ The filename is the component name, so it **must be a PascalCase identifier** — letters, digits, and underscores only (`Counter.tsx` → `<Counter />`). Lowercase filenames, names with dashes/dots/spaces (like `Time-Picker.tsx`), and two islands that resolve to the same name are skipped with a build warning.
32
26
 
33
- :::note
34
- Islands are for **interactive** UI. For a static component you reuse across
35
- pages (a styled callout, a pricing table), use an [MDX
36
- override](/docs/configuration/customization) instead — it ships no JavaScript.
37
- :::
27
+ :::note Islands are for **interactive** UI. For a static component you reuse across pages (a styled callout, a pricing table), use an [MDX override](/docs/configuration/customization) instead — it ships no JavaScript. :::
38
28
 
39
29
  ## Registering islands in `components.ts`
40
30
 
41
- If you'd rather keep islands next to the rest of your components — or give them a
42
- different name than the file — register them with `defineComponents`. The
43
- `islands` group is exactly like the `islands/` folder: each entry is available in
44
- every MDX page and hydrates (defaulting to `client: "visible"`).
31
+ If you'd rather keep islands next to the rest of your components — or give them a different name than the file — register them with `defineComponents`. The `islands` group is exactly like the `islands/` folder: each entry is available in every MDX page and hydrates (defaulting to `client: "visible"`).
45
32
 
46
33
  ```ts components.ts
47
34
  import { defineComponents } from "blume";
@@ -54,8 +41,7 @@ export default defineComponents({
54
41
  });
55
42
  ```
56
43
 
57
- Reference the component by import or by a path string, and set a hydration mode
58
- per island with the descriptor form:
44
+ Reference the component by import or by a path string, and set a hydration mode per island with the descriptor form:
59
45
 
60
46
  ```ts components.ts
61
47
  export default defineComponents({
@@ -67,9 +53,7 @@ export default defineComponents({
67
53
 
68
54
  ## Hydration
69
55
 
70
- By default an island uses `client:visible`: it hydrates when the reader scrolls
71
- it into view, so a page full of islands still loads instantly. Opt into a
72
- different strategy with an `export const client` in the island file:
56
+ By default an island uses `client:visible`: it hydrates when the reader scrolls it into view, so a page full of islands still loads instantly. Opt into a different strategy with an `export const client` in the island file:
73
57
 
74
58
  ```tsx islands/Chart.tsx lineNumbers
75
59
  // Skip server rendering entirely — for components that touch the DOM/window.
@@ -80,20 +64,18 @@ export default function Chart() {
80
64
  }
81
65
  ```
82
66
 
83
- | `client` value | Hydrates | Use it for |
84
- | ----------------------- | ---------------------------------- | --------------------------------------------------------- |
85
- | `"visible"` _(default)_ | When scrolled into view | Most islands |
86
- | `"load"` | Immediately on page load | Above-the-fold, must-be-instant UI |
87
- | `"idle"` | When the main thread is idle | Non-urgent interactivity |
88
- | `"only"` | Client only, never server-rendered | Libraries that need `window`/`document` (charts, editors) |
67
+ | `client` value | Hydrates | Use it for |
68
+ | --- | --- | --- |
69
+ | `"visible"` _(default)_ | When scrolled into view | Most islands |
70
+ | `"load"` | Immediately on page load | Above-the-fold, must-be-instant UI |
71
+ | `"idle"` | When the main thread is idle | Non-urgent interactivity |
72
+ | `"only"` | Client only, never server-rendered | Libraries that need `window`/`document` (charts, editors) |
89
73
 
90
74
  ## Frameworks
91
75
 
92
- **React works out of the box** — Blume turns it on automatically the moment your
93
- project contains a `.tsx`/`.jsx` island.
76
+ **React works out of the box** — Blume turns it on automatically the moment your project contains a `.tsx`/`.jsx` island.
94
77
 
95
- **Vue and Svelte** are supported too; install the matching Astro integration and
96
- Blume wires up the renderer when it sees a `.vue` or `.svelte` island:
78
+ **Vue and Svelte** are supported too; install the matching Astro integration and Blume wires up the renderer when it sees a `.vue` or `.svelte` island:
97
79
 
98
80
  ```bash
99
81
  # Vue
@@ -114,19 +96,13 @@ const on = ref(false);
114
96
  </template>
115
97
  ```
116
98
 
117
- Props you pass in MDX (`<Counter start={5} />`) are forwarded to the component,
118
- and children (`<Counter>label</Counter>`) arrive as the default slot.
99
+ Props you pass in MDX (`<Counter start={5} />`) are forwarded to the component, and children (`<Counter>label</Counter>`) arrive as the default slot.
119
100
 
120
- :::tip
121
- Islands hydrate on the client, so anything you pass as a prop must be
122
- serializable — strings, numbers, plain objects, not functions.
123
- :::
101
+ :::tip Islands hydrate on the client, so anything you pass as a prop must be serializable — strings, numbers, plain objects, not functions. :::
124
102
 
125
103
  ## Hooks
126
104
 
127
- Islands hydrate on their own, so there's no React context to thread project data
128
- through. Instead, `blume/hooks` reads a small snapshot the layout serializes into
129
- the page — no props to drill:
105
+ Islands hydrate on their own, so there's no React context to thread project data through. Instead, `blume/hooks` reads a small snapshot the layout serializes into the page — no props to drill:
130
106
 
131
107
  ```tsx islands/PageInfo.tsx lineNumbers
132
108
  import { useBlume, usePage } from "blume/hooks";
@@ -145,19 +121,16 @@ export default function PageInfo() {
145
121
  }
146
122
  ```
147
123
 
148
- | Hook | Returns |
149
- | ------------- | --------------------------------------------------------------------- |
150
- | `useBlume()` | `{ config, navigation }` for the site, or `null` before mount |
151
- | `usePage()` | `{ route, title }` for the current page, or `null` before mount |
124
+ | Hook | Returns |
125
+ | --- | --- |
126
+ | `useBlume()` | `{ config, navigation }` for the site, or `null` before mount |
127
+ | `usePage()` | `{ route, title }` for the current page, or `null` before mount |
152
128
  | `useSearch()` | `{ search, results, loading }` — query the configured search provider |
153
- | `useAskAI()` | `{ ask, messages, loading, reset }` — stream from the Ask AI endpoint |
129
+ | `useAskAI()` | `{ ask, messages, loading, reset }` — stream from the Ask AI endpoint |
154
130
 
155
- `useBlume()` and `usePage()` return `null` until the island mounts (so server and
156
- client render the same first frame) — guard for it. The snapshot is emitted only
157
- on pages that ship React, so a fully static site pays nothing.
131
+ `useBlume()` and `usePage()` return `null` until the island mounts (so server and client render the same first frame) — guard for it. The snapshot is emitted only on pages that ship React, so a fully static site pays nothing.
158
132
 
159
- On [custom pages](/docs/advanced/custom-pages) built with `PageLayout`, pass
160
- `clientData` so islands there can read it:
133
+ On [custom pages](/docs/advanced/custom-pages) built with `PageLayout`, pass `clientData` so islands there can read it:
161
134
 
162
135
  ```astro
163
136
  <PageLayout
@@ -3,16 +3,11 @@ title: Folder meta
3
3
  description: Configure a sidebar group — its title, icon, order, display mode, and page order — with a meta.ts file.
4
4
  ---
5
5
 
6
- Every folder in your content tree becomes a sidebar group. Drop a `meta.ts`
7
- beside its pages to control how that group looks and how its children are
8
- ordered. It's entirely optional: without one, the group's label is the humanized
9
- folder name and its pages sort by [index, numeric prefix, then
10
- alphabetically](/docs/content/navigation#ordering).
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).
11
7
 
12
8
  ## Defining meta
13
9
 
14
- Export a `defineMeta` object for a fully typed config. Place the file at the root
15
- of the folder it configures — `guides/meta.ts` configures the **Guides** group:
10
+ Export a `defineMeta` object for a fully typed config. Place the file at the root of the folder it configures — `guides/meta.ts` configures the **Guides** group:
16
11
 
17
12
  ```ts meta.ts lineNumbers
18
13
  import { defineMeta } from "blume";
@@ -31,45 +26,32 @@ Every field is optional — set only what you want to override.
31
26
 
32
27
  ## Fields
33
28
 
34
- | Field | Type | Description |
35
- | ----------- | ----------------------------- | -------------------------------------------------------------------------------------- |
36
- | `title` | `string` | The group's label. Defaults to the humanized folder name. |
37
- | `icon` | `string` | Icon shown next to the label. |
38
- | `order` | `number` | Position among sibling groups and pages. Lower numbers sort first. |
39
- | `display` | `"flat" \| "group" \| "page"` | How the group renders in the sidebar. Defaults to `flat`. See [below](#display-modes). |
40
- | `collapsed` | `boolean` | For `display: "group"`, whether the group starts collapsed. |
41
- | `pages` | `string[]` | Explicit order for the group's children, by slug. |
29
+ | Field | Type | Description |
30
+ | --- | --- | --- |
31
+ | `title` | `string` | The group's label. Defaults to the humanized folder name. |
32
+ | `icon` | `string` | Icon shown next to the label. |
33
+ | `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. |
36
+ | `pages` | `string[]` | Explicit order for the group's children, by slug. |
42
37
 
43
- The `pages` array lists children by slug — the folder or file name with its
44
- numeric prefix and any parentheses stripped (so `01-quickstart.mdx` is
45
- `"quickstart"`). Children you leave out still appear, after the listed ones.
38
+ 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.
46
39
 
47
40
  ## Display modes
48
41
 
49
42
  `display` controls how a group and its items appear in the sidebar:
50
43
 
51
44
  - **`flat`** (default) — a non-collapsible header with its pages listed beneath.
52
- - **`group`** — a collapsible `<details>` disclosure. Pair it with `collapsed`
53
- to set the initial state; a group containing the current page always starts
54
- open.
55
- - **`page`** — a single row that, when clicked, slides the sidebar into a
56
- sub-panel showing only that group's items, with a back arrow at the top. The
57
- panel is route-aware, so landing directly on a page inside the group opens
58
- straight to it.
59
-
60
- :::tip
61
- `page` mode keeps deep sections tidy — reach for it when a group has many
62
- children and you'd rather drill into it than scroll past it.
63
- :::
64
-
65
- `display` works the same on a group in an [explicit
66
- sidebar](/docs/content/navigation#explicit-sidebar) config.
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 `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. :::
49
+
50
+ `display` works the same on a group in an [explicit sidebar](/docs/content/navigation#explicit-sidebar) config.
67
51
 
68
52
  ## Computed meta
69
53
 
70
- Because `meta.ts` is a real module, you can compute the meta — pass a function
71
- (sync or `async`) instead of an object to build it at scan time. Handy for
72
- ordering pages from an external source:
54
+ Because `meta.ts` is a real module, you can compute the meta — pass a function (sync or `async`) instead of an object to build it at scan time. Handy for ordering pages from an external source:
73
55
 
74
56
  ```ts meta.ts
75
57
  import { defineMeta } from "blume";
@@ -82,30 +64,21 @@ export default defineMeta(async () => ({
82
64
 
83
65
  ## Ordering within a group
84
66
 
85
- The `pages` array sets the order of a group's children. Anything it omits falls
86
- back to each page's frontmatter `sidebar.order`, then the file system (an `index`
87
- page first, then numeric prefixes, then alphabetical). For the full sidebar
88
- precedence — including an explicit config sidebar — see [Navigation ›
89
- Ordering](/docs/content/navigation#ordering).
67
+ The `pages` array sets the order of a group's children. Anything it omits falls back to each page's frontmatter `sidebar.order`, then the file system (an `index` page first, then numeric prefixes, then alphabetical). For the full sidebar precedence — including an explicit config sidebar — see [Navigation › Ordering](/docs/content/navigation#ordering).
90
68
 
91
- To group pages _without_ adding a URL segment, you don't need a `meta.ts` at all:
92
- use a parenthesized folder name — see [Pages › Group
93
- folders](/docs/content#group-folders).
69
+ To group pages _without_ adding a URL segment, you don't need a `meta.ts` at all: use a parenthesized folder name — see [Pages › Group folders](/docs/content#group-folders).
94
70
 
95
71
  ## Internationalization
96
72
 
97
- Under [i18n](/docs/content/i18n), folder meta resolves per locale: put a
98
- `meta.ts` under `fr/guides/` to order the French group independently.
73
+ Under [i18n](/docs/content/i18n), folder meta resolves per locale: put a `meta.ts` under `fr/guides/` to order the French group independently.
99
74
 
100
- For folder meta that's identical in every language, add a `$` marker so one file
101
- serves all locales without duplication:
75
+ For folder meta that's identical in every language, add a `$` marker so one file serves all locales without duplication:
102
76
 
103
77
  ```txt
104
78
  docs/guides/meta.$.ts (folder meta applied to every locale)
105
79
  ```
106
80
 
107
- A locale-specific `meta.ts` still overrides the shared `meta.$.ts` for that
108
- language.
81
+ A locale-specific `meta.ts` still overrides the shared `meta.$.ts` for that language.
109
82
 
110
83
  ## Where to next
111
84
 
@@ -3,27 +3,21 @@ title: Navigation
3
3
  description: Build the sidebar from your files, then refine it with frontmatter, folder meta, or config.
4
4
  ---
5
5
 
6
- Blume builds your sidebar from the file system, then lets you refine it as much —
7
- or as little — as you want: page by page, folder by folder, or with one explicit
8
- config. Breadcrumbs, previous/next links, and the on-page outline all follow from
9
- the same model, with nothing to wire up.
6
+ Blume builds your sidebar from the file system, then lets you refine it as much — or as little — as you want: page by page, folder by folder, or with one explicit config. Breadcrumbs, previous/next links, and the on-page outline all follow from the same model, with nothing to wire up.
10
7
 
11
8
  ## The generated sidebar
12
9
 
13
10
  By default the sidebar mirrors your content tree:
14
11
 
15
12
  - folders become **groups**, files become **pages**
16
- - a page's label is its frontmatter `title`; a group's label is the humanized
17
- folder name
18
- - items sort by [numeric prefix](/docs/content), then alphabetically, and a folder's
19
- `index` page comes first
13
+ - a page's label is its frontmatter `title`; a group's label is the humanized folder name
14
+ - items sort by [numeric prefix](/docs/content), then alphabetically, and a folder's `index` page comes first
20
15
 
21
16
  That's enough for many sites — everything below is opt-in.
22
17
 
23
18
  ## Page label, icon, and badge
24
19
 
25
- Tune how a single page appears in the sidebar from its frontmatter, under
26
- `sidebar`:
20
+ Tune how a single page appears in the sidebar from its frontmatter, under `sidebar`:
27
21
 
28
22
  ```yaml lineNumbers
29
23
  sidebar:
@@ -37,9 +31,7 @@ See [Frontmatter](/docs/reference/frontmatter) for the full page schema.
37
31
 
38
32
  ## Folder groups
39
33
 
40
- Each folder becomes a sidebar group. Drop a [`meta.ts`](/docs/content/meta)
41
- beside its pages to set the group's title, icon, order, [display
42
- 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, [display mode](/docs/content/meta#display-modes), and the order of its children:
43
35
 
44
36
  ```ts meta.ts
45
37
  import { defineMeta } from "blume";
@@ -51,11 +43,9 @@ export default defineMeta({
51
43
  });
52
44
  ```
53
45
 
54
- See [Folder meta](/docs/content/meta) for every field, the three display modes,
55
- and computing meta at scan time.
46
+ See [Folder meta](/docs/content/meta) for every field, the three display modes, and computing meta at scan time.
56
47
 
57
- To group pages _without_ adding a URL segment, use a parenthesized folder name —
58
- see [Pages](/docs/content#group-folders).
48
+ To group pages _without_ adding a URL segment, use a parenthesized folder name — see [Pages](/docs/content#group-folders).
59
49
 
60
50
  ## Ordering
61
51
 
@@ -76,8 +66,7 @@ When the sidebar is generated, order is resolved highest priority first:
76
66
 
77
67
  ## Hidden pages
78
68
 
79
- Hide a page from the sidebar — and from previous/next pagination — while keeping
80
- it built and reachable by its URL:
69
+ Hide a page from the sidebar — and from previous/next pagination — while keeping it built and reachable by its URL:
81
70
 
82
71
  ```yaml
83
72
  sidebar:
@@ -86,9 +75,7 @@ sidebar:
86
75
 
87
76
  ## Tabs
88
77
 
89
- Render top-level sections as tabs in the header, useful for splitting a large
90
- site into distinct areas — say adapters, an API, and AI guides. A tab is
91
- highlighted when the current route falls under its `path`:
78
+ Render top-level sections as tabs in the header, useful for splitting a large site into distinct areas — say adapters, an API, and AI guides. A tab is highlighted when the current route falls under its `path`:
92
79
 
93
80
  ```ts blume.config.ts lineNumbers
94
81
  navigation: {
@@ -100,24 +87,13 @@ navigation: {
100
87
  }
101
88
  ```
102
89
 
103
- Tabs also **scope the sidebar**: when the current route falls under a tab's
104
- `path`, the sidebar shows only that section's pages — so `/adapters/*` lists the
105
- adapters and nothing else. The folder at a tab's `path` becomes the section, so
106
- this needs no extra config beyond the tabs themselves; structure your content
107
- into a folder per tab and point each tab at it.
90
+ Tabs also **scope the sidebar**: when the current route falls under a tab's `path`, the sidebar shows only that section's pages — so `/adapters/*` lists the adapters and nothing else. The folder at a tab's `path` becomes the section, so this needs no extra config beyond the tabs themselves; structure your content into a folder per tab and point each tab at it.
108
91
 
109
- On a route under no tab (or a tab whose `path` is `/`), the sidebar shows the
110
- pages that _don't_ belong to a tab — each tab's folder is hidden from it, since
111
- that section already has its own tab in the header. So a root landing page lists
112
- your loose top-level pages while the sectioned content stays behind its tab,
113
- mirroring Fumadocs' root folders. If a route has no pages of its own to show
114
- this way, the full tree is shown instead, so the sidebar is never left blank.
92
+ On a route under no tab (or a tab whose `path` is `/`), the sidebar shows the pages that _don't_ belong to a tab — each tab's folder is hidden from it, since that section already has its own tab in the header. So a root landing page lists your loose top-level pages while the sectioned content stays behind its tab, mirroring Fumadocs' root folders. If a route has no pages of its own to show this way, the full tree is shown instead, so the sidebar is never left blank.
115
93
 
116
94
  ## Selectors
117
95
 
118
- For switching between whole partitions of a site — a product, a version, or any
119
- grouped set of destinations — add a `selector`. Each renders as a dropdown in the
120
- header, showing the option whose `path` matches the current route:
96
+ For switching between whole partitions of a site — a product, a version, or any grouped set of destinations — add a `selector`. Each renders as a dropdown in the header, showing the option whose `path` matches the current route:
121
97
 
122
98
  ```ts blume.config.ts lineNumbers
123
99
  navigation: {
@@ -134,14 +110,11 @@ navigation: {
134
110
  }
135
111
  ```
136
112
 
137
- Each item takes a `label`, a `path`, and optional `icon`, `description`, and
138
- `tag`. `kind` (`dropdown`, `product`, `version`, or `language`) is a hint for how
139
- the selector is used; all render the same dropdown.
113
+ 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.
140
114
 
141
115
  ## Explicit sidebar
142
116
 
143
- For full control, define `navigation.sidebar` in config. When it's set, Blume
144
- uses it verbatim and skips file-system generation:
117
+ For full control, define `navigation.sidebar` in config. When it's set, Blume uses it verbatim and skips file-system generation:
145
118
 
146
119
  ```ts blume.config.ts lineNumbers
147
120
  navigation: {
@@ -157,15 +130,11 @@ navigation: {
157
130
  }
158
131
  ```
159
132
 
160
- Each item is a page route (a string), a group (`label` + `items`), or a link
161
- (`label` + `href`). Groups can nest, take a [`display`
162
- mode](/docs/content/meta#display-modes), and start `collapsed`.
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`.
163
134
 
164
135
  ## Repository link
165
136
 
166
- When you set [`github`](/docs/configuration) in your config, Blume shows a GitHub
167
- icon in the header — beside the theme toggle — that links to your repository. It's
168
- on by default; hide it with `navigation.repo`:
137
+ When you set [`github`](/docs/configuration) in your config, Blume shows a GitHub icon in the header — beside the theme toggle — that links to your repository. It's on by default; hide it with `navigation.repo`:
169
138
 
170
139
  ```ts blume.config.ts lineNumbers
171
140
  navigation: {
@@ -173,35 +142,27 @@ navigation: {
173
142
  }
174
143
  ```
175
144
 
176
- The link only appears when `github` is configured, so projects without a repo are
177
- unaffected either way.
145
+ The link only appears when `github` is configured, so projects without a repo are unaffected either way.
178
146
 
179
147
  ## Breadcrumbs and pagination
180
148
 
181
149
  These come for free from the sidebar tree — no configuration:
182
150
 
183
151
  - **Breadcrumbs** show the current page's parent group above the title.
184
- - **Previous and next** links at the foot of each page follow sidebar order,
185
- skipping hidden pages.
152
+ - **Previous and next** links at the foot of each page follow sidebar order, skipping hidden pages.
186
153
 
187
154
  ## On this page
188
155
 
189
- A right-rail outline is generated automatically from each page's `##` and `###`
190
- headings, so long pages stay scannable. On narrower screens, where the right rail
191
- is hidden, it collapses into an “On this page” dropdown above the content.
156
+ A right-rail outline is generated automatically from each page's `##` and `###` headings, so long pages stay scannable. On narrower screens, where the right rail is hidden, it collapses into an “On this page” dropdown above the content.
192
157
 
193
158
  ## Page actions
194
159
 
195
160
  Below the table of contents, every page shows a set of quick actions:
196
161
 
197
- - **Edit this page on GitHub** — links straight to the source file. Appears once
198
- you set [`github`](/docs/configuration) in your config.
162
+ - **Edit this page on GitHub** — links straight to the source file. Appears once you set [`github`](/docs/configuration) in your config.
199
163
  - **Scroll to top** — smoothly returns to the top of long pages.
200
- - **Give feedback** — opens a prefilled GitHub issue with an optional reaction and
201
- note (also requires `github`).
164
+ - **Give feedback** — opens a prefilled GitHub issue with an optional reaction and note (also requires `github`).
202
165
 
203
- Three more hand the page to AI tools — **Copy as Markdown**, **Open in chat**, and
204
- **Ask AI about this page** — covered in [AI](/docs/configuration/ai#copy-as-markdown).
166
+ Three more hand the page to AI tools — **Copy as Markdown**, **Open in chat**, and **Ask AI about this page** — covered in [AI](/docs/configuration/ai#copy-as-markdown).
205
167
 
206
- With [`export`](/docs/configuration/export) on, an **Export** action also lets
207
- readers download the page as a PDF or EPUB.
168
+ With [`export`](/docs/configuration/export) on, an **Export** action also lets readers download the page as a PDF or EPUB.