blume 0.3.0 → 0.4.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.
- package/dist/cli/index.js +747 -471
- package/dist/cli/index.js.map +45 -38
- package/dist/types/core/schema.d.ts +289 -278
- package/dist/types/migrate/mintlify/assets.d.ts +8 -0
- package/docs/01-quickstart.mdx +5 -16
- package/docs/02-deployment.mdx +21 -54
- package/docs/advanced/api-reference.mdx +10 -37
- package/docs/advanced/blog.mdx +9 -25
- package/docs/advanced/changelog.mdx +10 -33
- package/docs/advanced/custom-pages.mdx +21 -78
- package/docs/configuration/ai.mdx +42 -103
- package/docs/configuration/analytics.mdx +20 -38
- package/docs/configuration/customization.mdx +40 -73
- package/docs/configuration/export.mdx +9 -34
- package/docs/configuration/index.mdx +67 -87
- package/docs/configuration/search.mdx +17 -54
- package/docs/configuration/seo.mdx +17 -48
- package/docs/configuration/theming.mdx +20 -42
- package/docs/content/components.mdx +42 -101
- package/docs/content/i18n.mdx +21 -72
- package/docs/content/index.mdx +18 -48
- package/docs/content/islands.mdx +25 -52
- package/docs/content/meta.mdx +23 -50
- package/docs/content/navigation.mdx +23 -62
- package/docs/content/sources.mdx +20 -83
- package/docs/content/syntax.mdx +37 -105
- package/docs/index.mdx +11 -40
- package/docs/reference/cli.mdx +18 -29
- package/docs/reference/frontmatter.mdx +2 -5
- package/package.json +1 -1
- package/src/astro/integration.ts +26 -3
- package/src/astro/islands.ts +6 -2
- package/src/astro/markdown-negotiation.ts +17 -3
- package/src/astro/pages.ts +6 -1
- package/src/astro/static-assets.ts +117 -0
- package/src/astro/templates.ts +48 -26
- package/src/cli/args.ts +23 -0
- package/src/cli/commands/build.ts +23 -0
- package/src/cli/commands/dev.ts +11 -2
- package/src/cli/commands/doctor.ts +10 -1
- package/src/cli/commands/eject.ts +3 -1
- package/src/cli/commands/init.ts +21 -1
- package/src/cli/commands/preview.ts +2 -1
- package/src/cli/commands/validate.ts +12 -1
- package/src/cli/dev-lock.ts +84 -0
- package/src/cli/log.ts +11 -0
- package/src/components/BlumePage.astro +2 -0
- package/src/components/content/YouTube.astro +35 -0
- package/src/components/content/youtube.ts +46 -0
- package/src/components/islands/ask-ai.tsx +14 -14
- package/src/components/props.ts +3 -0
- package/src/core/assets.ts +31 -0
- package/src/core/bridge.ts +10 -0
- package/src/core/builtin-tags.ts +1 -0
- package/src/core/diagnostics.ts +6 -1
- package/src/core/gitignore.ts +30 -0
- package/src/core/links.ts +60 -19
- package/src/core/schema.ts +7 -0
- package/src/core/sources/mdx-remote.ts +54 -8
- package/src/core/sources/normalize.ts +6 -1
- package/src/core/sources/notion.ts +49 -5
- package/src/core/sources/sanity.ts +5 -1
- package/src/deploy/rss.ts +1 -8
- package/src/deploy/sitemap.ts +20 -1
- package/src/deploy/xml.ts +8 -0
- package/src/markdown/directives.ts +15 -7
- package/src/markdown/package-commands.ts +26 -4
- package/src/migrate/fumadocs/content.ts +14 -1
- package/src/migrate/fumadocs/groups.ts +7 -0
- package/src/migrate/fumadocs/index.ts +5 -2
- package/src/migrate/mintlify/assets.ts +46 -0
- package/src/migrate/mintlify/index.ts +53 -45
- package/src/migrate/shared.ts +12 -27
- package/src/og/card.ts +14 -2
- package/src/registry/eject.ts +13 -3
- package/src/registry/registry.ts +6 -0
- package/src/registry/rewrite-imports.ts +31 -19
- package/src/search/documents.ts +23 -5
- package/src/search/sync/algolia.ts +5 -1
- package/src/search/sync/typesense.ts +24 -16
- package/src/theme/palette.ts +26 -7
|
@@ -3,10 +3,7 @@ title: Custom Pages
|
|
|
3
3
|
description: Mount fully custom .astro routes alongside your docs and read your site's config, navigation, and content from the blume:data module.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Most of a Blume site is Markdown, but sometimes you need a route that _isn't_ a
|
|
7
|
-
doc — a landing page, a pricing page, a hand-built blog or changelog index, or an
|
|
8
|
-
interactive dashboard. Drop an `.astro` file under your **pages** folder and
|
|
9
|
-
Blume mounts it as a real route, right alongside your content.
|
|
6
|
+
Most of a Blume site is Markdown, but sometimes you need a route that _isn't_ a doc — a landing page, a pricing page, a hand-built blog or changelog index, or an interactive dashboard. Drop an `.astro` file under your **pages** folder and Blume mounts it as a real route, right alongside your content.
|
|
10
7
|
|
|
11
8
|
## Add a page
|
|
12
9
|
|
|
@@ -20,19 +17,13 @@ import data from "blume:data";
|
|
|
20
17
|
<h1>Pricing for {data.config.title}</h1>
|
|
21
18
|
```
|
|
22
19
|
|
|
23
|
-
`blume dev` picks it up immediately and `blume build` prerenders it to static
|
|
24
|
-
HTML. The folder name is configurable with [`content.pages`](/docs/configuration#content)
|
|
25
|
-
(default `"pages"`).
|
|
20
|
+
`blume dev` picks it up immediately and `blume build` prerenders it to static HTML. The folder name is configurable with [`content.pages`](/docs/configuration#content) (default `"pages"`).
|
|
26
21
|
|
|
27
|
-
Custom pages keep their original location on disk, so relative imports, component
|
|
28
|
-
imports, and [`getStaticPaths`](https://docs.astro.build/en/reference/routing-reference/#getstaticpaths)
|
|
29
|
-
all work exactly as they do in a plain Astro project — Blume mounts each file
|
|
30
|
-
where it sits rather than copying it.
|
|
22
|
+
Custom pages keep their original location on disk, so relative imports, component imports, and [`getStaticPaths`](https://docs.astro.build/en/reference/routing-reference/#getstaticpaths) all work exactly as they do in a plain Astro project — Blume mounts each file where it sits rather than copying it.
|
|
31
23
|
|
|
32
24
|
## Files and routes
|
|
33
25
|
|
|
34
|
-
Each file's path under the pages folder becomes its route. `index` maps to the
|
|
35
|
-
parent folder, and dynamic `[param]` segments are preserved:
|
|
26
|
+
Each file's path under the pages folder becomes its route. `index` maps to the parent folder, and dynamic `[param]` segments are preserved:
|
|
36
27
|
|
|
37
28
|
| File | Route |
|
|
38
29
|
| ------------------------- | ------------- |
|
|
@@ -41,14 +32,11 @@ parent folder, and dynamic `[param]` segments are preserved:
|
|
|
41
32
|
| `pages/blog/[slug].astro` | `/blog/:slug` |
|
|
42
33
|
| `pages/changelog.astro` | `/changelog` |
|
|
43
34
|
|
|
44
|
-
A custom page **wins** over a generated route at the same path. Adding
|
|
45
|
-
`pages/changelog.astro`, for example, replaces Blume's
|
|
46
|
-
[generated changelog timeline](/docs/advanced/changelog) with your own.
|
|
35
|
+
A custom page **wins** over a generated route at the same path. Adding `pages/changelog.astro`, for example, replaces Blume's [generated changelog timeline](/docs/advanced/changelog) with your own.
|
|
47
36
|
|
|
48
37
|
## Reading site data
|
|
49
38
|
|
|
50
|
-
Import `blume:data` to read the same resolved config, navigation, routes, and
|
|
51
|
-
feeds the rest of the site uses:
|
|
39
|
+
Import `blume:data` to read the same resolved config, navigation, routes, and feeds the rest of the site uses:
|
|
52
40
|
|
|
53
41
|
```astro pages/all-pages.astro lineNumbers
|
|
54
42
|
---
|
|
@@ -69,9 +57,7 @@ import data from "blume:data";
|
|
|
69
57
|
</ul>
|
|
70
58
|
```
|
|
71
59
|
|
|
72
|
-
Inside a Blume project the module is typed automatically. You can also pull the
|
|
73
|
-
shape in explicitly — for typed helpers, props, or your own tsconfig — with
|
|
74
|
-
`import type { BlumeData } from "blume"`:
|
|
60
|
+
Inside a Blume project the module is typed automatically. You can also pull the shape in explicitly — for typed helpers, props, or your own tsconfig — with `import type { BlumeData } from "blume"`:
|
|
75
61
|
|
|
76
62
|
```ts
|
|
77
63
|
import type { BlumeData, BlumeRoute } from "blume";
|
|
@@ -134,9 +120,7 @@ The module exposes:
|
|
|
134
120
|
}}
|
|
135
121
|
/>
|
|
136
122
|
|
|
137
|
-
`routes` carries page metadata but not frontmatter like `type` or `date`. To
|
|
138
|
-
build a list filtered by content type — a blog or changelog index — pair it with
|
|
139
|
-
Astro's `docs` content collection, which holds the frontmatter:
|
|
123
|
+
`routes` carries page metadata but not frontmatter like `type` or `date`. To build a list filtered by content type — a blog or changelog index — pair it with Astro's `docs` content collection, which holds the frontmatter:
|
|
140
124
|
|
|
141
125
|
```astro pages/blog/index.astro lineNumbers
|
|
142
126
|
---
|
|
@@ -169,12 +153,9 @@ const posts = (await getCollection("docs"))
|
|
|
169
153
|
|
|
170
154
|
## Runtime helpers
|
|
171
155
|
|
|
172
|
-
`blume/runtime` bundles the common data patterns so you don't reach into
|
|
173
|
-
`blume:data` internals.
|
|
156
|
+
`blume/runtime` bundles the common data patterns so you don't reach into `blume:data` internals.
|
|
174
157
|
|
|
175
|
-
**`getBlumeCollection(data, query?)`** selects content routes — filtered by
|
|
176
|
-
collection, locale, or path prefix, with drafts and hidden pages excluded and the
|
|
177
|
-
result sorted by path — which is exactly what a custom index needs:
|
|
158
|
+
**`getBlumeCollection(data, query?)`** selects content routes — filtered by collection, locale, or path prefix, with drafts and hidden pages excluded and the result sorted by path — which is exactly what a custom index needs:
|
|
178
159
|
|
|
179
160
|
```astro pages/blog/index.astro lineNumbers
|
|
180
161
|
---
|
|
@@ -191,10 +172,7 @@ const posts = getBlumeCollection(data, { prefix: "/blog" });
|
|
|
191
172
|
</ul>
|
|
192
173
|
```
|
|
193
174
|
|
|
194
|
-
**`<BlumePage>`** renders a content entry's body inside a custom page, with
|
|
195
|
-
Blume's built-in MDX components (callouts, cards, steps…) already wired in — for
|
|
196
|
-
featuring a doc on a landing page or building a bespoke index that shows real
|
|
197
|
-
content:
|
|
175
|
+
**`<BlumePage>`** renders a content entry's body inside a custom page, with Blume's built-in MDX components (callouts, cards, steps…) already wired in — for featuring a doc on a landing page or building a bespoke index that shows real content:
|
|
198
176
|
|
|
199
177
|
```astro pages/index.astro lineNumbers
|
|
200
178
|
---
|
|
@@ -208,18 +186,11 @@ const [intro] = getBlumeCollection(data, { prefix: "/docs" });
|
|
|
208
186
|
{intro && <BlumePage id={intro.entryId} />}
|
|
209
187
|
```
|
|
210
188
|
|
|
211
|
-
Pass `components` to add your own overrides or islands (which live in the
|
|
212
|
-
generated runtime and aren't imported by default), and `collection` to read from
|
|
213
|
-
a collection other than `"docs"`.
|
|
189
|
+
Pass `components` to add your own overrides or islands (which live in the generated runtime and aren't imported by default), and `collection` to read from a collection other than `"docs"`.
|
|
214
190
|
|
|
215
191
|
## Using the site layout
|
|
216
192
|
|
|
217
|
-
`RootLayout` gives a custom page the full docs chrome — header, sidebar, search,
|
|
218
|
-
TOC, and theme — by wrapping it in the same 3-column grid the generated pages
|
|
219
|
-
use. For a landing or marketing page that grid is in the way, so reach for
|
|
220
|
-
**`PageLayout`** instead: it provides the document shell, header, theme, and
|
|
221
|
-
fonts, then a single full-width `<slot />` (no sidebar, no prose, no TOC). An
|
|
222
|
-
optional `footer` slot renders after `<main>`:
|
|
193
|
+
`RootLayout` gives a custom page the full docs chrome — header, sidebar, search, TOC, and theme — by wrapping it in the same 3-column grid the generated pages use. For a landing or marketing page that grid is in the way, so reach for **`PageLayout`** instead: it provides the document shell, header, theme, and fonts, then a single full-width `<slot />` (no sidebar, no prose, no TOC). An optional `footer` slot renders after `<main>`:
|
|
223
194
|
|
|
224
195
|
```astro pages/index.astro lineNumbers
|
|
225
196
|
---
|
|
@@ -251,15 +222,7 @@ const { config } = data;
|
|
|
251
222
|
</PageLayout>
|
|
252
223
|
```
|
|
253
224
|
|
|
254
|
-
Passing `siteUrl` (and `ogEnabled`) derives the page's `canonical` and a
|
|
255
|
-
generated `og:image` automatically: Blume renders an Open Graph card for every
|
|
256
|
-
static custom page — the home included, the most-shared URL — served at
|
|
257
|
-
`/og/<route>.png` (`/og/index.png` for `/`). The home card uses the site title
|
|
258
|
-
with the description as its eyebrow; a deeper page is titled from its last path
|
|
259
|
-
segment. Set `ogImage` or `canonical` explicitly to override either. `ogImage`
|
|
260
|
-
takes a root-relative path — a file in `public/`, resolved against
|
|
261
|
-
[`deployment.site`](/docs/deployment) to the absolute URL crawlers need — or an
|
|
262
|
-
external URL, which passes through untouched:
|
|
225
|
+
Passing `siteUrl` (and `ogEnabled`) derives the page's `canonical` and a generated `og:image` automatically: Blume renders an Open Graph card for every static custom page — the home included, the most-shared URL — served at `/og/<route>.png` (`/og/index.png` for `/`). The home card uses the site title with the description as its eyebrow; a deeper page is titled from its last path segment. Set `ogImage` or `canonical` explicitly to override either. `ogImage` takes a root-relative path — a file in `public/`, resolved against [`deployment.site`](/docs/deployment) to the absolute URL crawlers need — or an external URL, which passes through untouched:
|
|
263
226
|
|
|
264
227
|
```astro pages/index.astro lineNumbers
|
|
265
228
|
<PageLayout
|
|
@@ -272,13 +235,9 @@ external URL, which passes through untouched:
|
|
|
272
235
|
</PageLayout>
|
|
273
236
|
```
|
|
274
237
|
|
|
275
|
-
Only this page changes — every other route keeps its generated card — so it's how
|
|
276
|
-
you give the home page alone a bespoke share image.
|
|
238
|
+
Only this page changes — every other route keeps its generated card — so it's how you give the home page alone a bespoke share image.
|
|
277
239
|
|
|
278
|
-
`page.title` is used verbatim as the document title (no `- siteTitle` suffix),
|
|
279
|
-
since marketing pages usually set their own. To give a custom page the full docs
|
|
280
|
-
chrome instead — sidebar, TOC, and all — wrap it in `RootLayout`, the layout the
|
|
281
|
-
generated pages use. Pull the required props straight from `blume:data`:
|
|
240
|
+
`page.title` is used verbatim as the document title (no `- siteTitle` suffix), since marketing pages usually set their own. To give a custom page the full docs chrome instead — sidebar, TOC, and all — wrap it in `RootLayout`, the layout the generated pages use. Pull the required props straight from `blume:data`:
|
|
282
241
|
|
|
283
242
|
```astro pages/pricing.astro lineNumbers
|
|
284
243
|
---
|
|
@@ -301,24 +260,13 @@ import data from "blume:data";
|
|
|
301
260
|
</RootLayout>
|
|
302
261
|
```
|
|
303
262
|
|
|
304
|
-
:::note
|
|
305
|
-
`RootLayout` is part of the generated runtime, so its props can change between
|
|
306
|
-
releases. When you want a layout that's fully yours,
|
|
307
|
-
[`blume eject`](/docs/configuration/customization#eject) turns `.blume/` into a
|
|
308
|
-
standard Astro project you own outright.
|
|
309
|
-
:::
|
|
263
|
+
:::note `RootLayout` is part of the generated runtime, so its props can change between releases. When you want a layout that's fully yours, [`blume eject`](/docs/configuration/customization#eject) turns `.blume/` into a standard Astro project you own outright. :::
|
|
310
264
|
|
|
311
265
|
## 404 page
|
|
312
266
|
|
|
313
|
-
Blume ships a default **not found** page out of the box: a centered "404" message
|
|
314
|
-
wrapped in the site chrome (header, search, theme), served for any unmatched URL.
|
|
315
|
-
`blume build` writes it to `404.html`, which static hosts serve automatically, and
|
|
316
|
-
`blume dev` shows it for unknown routes.
|
|
267
|
+
Blume ships a default **not found** page out of the box: a centered "404" message wrapped in the site chrome (header, search, theme), served for any unmatched URL. `blume build` writes it to `404.html`, which static hosts serve automatically, and `blume dev` shows it for unknown routes.
|
|
317
268
|
|
|
318
|
-
To replace it with your own, add a `pages/404.astro`. It owns the `/404` route the
|
|
319
|
-
same way `pages/changelog.astro` takes over the changelog — your page wins and the
|
|
320
|
-
default is dropped. Build it like any other custom page, in `PageLayout` or
|
|
321
|
-
`RootLayout`:
|
|
269
|
+
To replace it with your own, add a `pages/404.astro`. It owns the `/404` route the same way `pages/changelog.astro` takes over the changelog — your page wins and the default is dropped. Build it like any other custom page, in `PageLayout` or `RootLayout`:
|
|
322
270
|
|
|
323
271
|
```astro pages/404.astro lineNumbers
|
|
324
272
|
---
|
|
@@ -342,16 +290,11 @@ import data from "blume:data";
|
|
|
342
290
|
</PageLayout>
|
|
343
291
|
```
|
|
344
292
|
|
|
345
|
-
To keep the default design but change its wording — including for other languages
|
|
346
|
-
— override the `notFound` [UI strings](/docs/content/i18n) (`title`, `description`,
|
|
347
|
-
`home`) via `i18n.ui`.
|
|
293
|
+
To keep the default design but change its wording — including for other languages — override the `notFound` [UI strings](/docs/content/i18n) (`title`, `description`, `home`) via `i18n.ui`.
|
|
348
294
|
|
|
349
295
|
## Interactive pages
|
|
350
296
|
|
|
351
|
-
Custom pages are ordinary Astro, so you can drop in React (or any framework)
|
|
352
|
-
[islands](/docs/configuration/customization#react-islands) with a hydration directive.
|
|
353
|
-
React switches on automatically the moment your project contains a `.tsx` or
|
|
354
|
-
`.jsx` file.
|
|
297
|
+
Custom pages are ordinary Astro, so you can drop in React (or any framework) [islands](/docs/configuration/customization#interactive-islands) with a hydration directive. React switches on automatically the moment your project contains a `.tsx` or `.jsx` file.
|
|
355
298
|
|
|
356
299
|
<CardGroup cols={2}>
|
|
357
300
|
<Card
|
|
@@ -3,15 +3,11 @@ title: AI
|
|
|
3
3
|
description: Machine-readable docs with llms.txt and an optional in-page Ask AI assistant.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Blume has several opt-in AI features: machine-readable docs for external tools
|
|
7
|
-
(`llms.txt`), an in-page **Ask AI** assistant, and a hosted **MCP server** for
|
|
8
|
-
coding agents. Everything is off by default, and static docs stay fully static
|
|
9
|
-
until you turn a feature on.
|
|
6
|
+
Blume has several opt-in AI features: machine-readable docs for external tools (`llms.txt`), an in-page **Ask AI** assistant, and a hosted **MCP server** for coding agents. Everything is off by default, and static docs stay fully static until you turn a feature on.
|
|
10
7
|
|
|
11
8
|
## llms.txt
|
|
12
9
|
|
|
13
|
-
Emit machine-readable versions of your docs that coding agents and chat
|
|
14
|
-
assistants can consume:
|
|
10
|
+
Emit machine-readable versions of your docs that coding agents and chat assistants can consume:
|
|
15
11
|
|
|
16
12
|
```ts blume.config.ts lineNumbers
|
|
17
13
|
ai: {
|
|
@@ -21,19 +17,14 @@ ai: {
|
|
|
21
17
|
|
|
22
18
|
With this on, `blume build` writes two files to the root of your site:
|
|
23
19
|
|
|
24
|
-
- **`/llms.txt`** — a compact index: your site title and description, then a
|
|
25
|
-
|
|
26
|
-
- **`/llms-full.txt`** — the entire corpus: each page's full Markdown body, with
|
|
27
|
-
its source URL, in one file.
|
|
20
|
+
- **`/llms.txt`** — a compact index: your site title and description, then a linked list of every page with its summary.
|
|
21
|
+
- **`/llms-full.txt`** — the entire corpus: each page's full Markdown body, with its source URL, in one file.
|
|
28
22
|
|
|
29
|
-
Draft pages are excluded. Set [`deployment.site`](/docs/deployment) so the links and
|
|
30
|
-
source URLs resolve to absolute addresses.
|
|
23
|
+
Draft pages are excluded. Set [`deployment.site`](/docs/deployment) so the links and source URLs resolve to absolute addresses.
|
|
31
24
|
|
|
32
25
|
## Raw Markdown
|
|
33
26
|
|
|
34
|
-
Append `.md` or `.mdx` to any page's URL to fetch its raw Markdown source —
|
|
35
|
-
perfect for LLMs, coding agents, and "copy as Markdown" workflows. It's available
|
|
36
|
-
for every page, in dev and production, with no configuration.
|
|
27
|
+
Append `.md` or `.mdx` to any page's URL to fetch its raw Markdown source — perfect for LLMs, coding agents, and "copy as Markdown" workflows. It's available for every page, in dev and production, with no configuration.
|
|
37
28
|
|
|
38
29
|
| URL | Returns |
|
|
39
30
|
| ----------------- | ----------------------- |
|
|
@@ -41,38 +32,25 @@ for every page, in dev and production, with no configuration.
|
|
|
41
32
|
| `/quickstart.md` | The raw Markdown source |
|
|
42
33
|
| `/quickstart.mdx` | The raw Markdown source |
|
|
43
34
|
|
|
44
|
-
Nested routes work the same way (`/content/syntax.md`), and the home page is
|
|
45
|
-
served at `/index.md`.
|
|
35
|
+
Nested routes work the same way (`/content/syntax.md`), and the home page is served at `/index.md`.
|
|
46
36
|
|
|
47
37
|
## Copy as Markdown
|
|
48
38
|
|
|
49
|
-
Every page carries a **Copy as Markdown** action — in the [page
|
|
50
|
-
actions](/docs/content/navigation#page-actions) beneath the table of contents — that copies the
|
|
51
|
-
page's raw Markdown to the clipboard. It's the same source served at the [`.md`
|
|
52
|
-
URL](#raw-markdown) above, ready to paste into an LLM, an issue, or your notes.
|
|
53
|
-
It's available on every page, in dev and production, with no configuration.
|
|
39
|
+
Every page carries a **Copy as Markdown** action — in the [page actions](/docs/content/navigation#page-actions) beneath the table of contents — that copies the page's raw Markdown to the clipboard. It's the same source served at the [`.md` URL](#raw-markdown) above, ready to paste into an LLM, an issue, or your notes. It's available on every page, in dev and production, with no configuration.
|
|
54
40
|
|
|
55
41
|
## Open in chat
|
|
56
42
|
|
|
57
|
-
The **Open in chat** action opens the current page in an AI assistant — v0,
|
|
58
|
-
ChatGPT, Claude, T3 Chat, Scira, or Cursor — pre-filled with a prompt that points
|
|
59
|
-
it at the page's raw Markdown so it can answer questions about what you're
|
|
60
|
-
reading:
|
|
43
|
+
The **Open in chat** action opens the current page in an AI assistant — v0, ChatGPT, Claude, T3 Chat, Scira, or Cursor — pre-filled with a prompt that points it at the page's raw Markdown so it can answer questions about what you're reading:
|
|
61
44
|
|
|
62
|
-
> Read `https://your-site/this-page.md` so I can ask you questions about this
|
|
63
|
-
> page.
|
|
45
|
+
> Read `https://your-site/this-page.md` so I can ask you questions about this page.
|
|
64
46
|
|
|
65
|
-
Like Copy as Markdown, it needs no setup. The assistant fetches the page over its
|
|
66
|
-
public URL, so it works as soon as the page is deployed.
|
|
47
|
+
Like Copy as Markdown, it needs no setup. The assistant fetches the page over its public URL, so it works as soon as the page is deployed.
|
|
67
48
|
|
|
68
|
-
To embed a ready-to-copy prompt inline in your content — rather than a whole-page
|
|
69
|
-
action — use the [Prompt component](/docs/content/components#prompt), which renders
|
|
70
|
-
a labelled row with a **Copy prompt** button and an optional open-in-Cursor link.
|
|
49
|
+
To embed a ready-to-copy prompt inline in your content — rather than a whole-page action — use the [Prompt component](/docs/content/components#prompt), which renders a labelled row with a **Copy prompt** button and an optional open-in-Cursor link.
|
|
71
50
|
|
|
72
51
|
## Ask AI
|
|
73
52
|
|
|
74
|
-
Add an assistant that answers reader questions in an in-page chat panel, backed
|
|
75
|
-
by a streaming server endpoint and the [AI SDK](https://ai-sdk.dev):
|
|
53
|
+
Add an assistant that answers reader questions in an in-page chat panel, backed by a streaming server endpoint and the [AI SDK](https://ai-sdk.dev):
|
|
76
54
|
|
|
77
55
|
```ts blume.config.ts lineNumbers
|
|
78
56
|
ai: {
|
|
@@ -86,26 +64,15 @@ ai: {
|
|
|
86
64
|
|
|
87
65
|
### Grounding
|
|
88
66
|
|
|
89
|
-
Ask AI is **grounded in your docs**. For each question it retrieves the most
|
|
90
|
-
relevant pages — using the same lexical [Orama](/docs/configuration/search) index
|
|
91
|
-
that powers on-page search — and injects them into the model's system prompt, so
|
|
92
|
-
answers come from your content instead of the model's own knowledge. The
|
|
93
|
-
assistant is told to answer only from the retrieved pages, to say when something
|
|
94
|
-
isn't covered, and to cite the pages it drew from.
|
|
67
|
+
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.
|
|
95
68
|
|
|
96
|
-
The page the reader is currently on is added to the context first and used to
|
|
97
|
-
scope retrieval to that page's language, so answers stay relevant to where they
|
|
98
|
-
are in the docs. Retrieval runs at request time from a snapshot baked into the
|
|
99
|
-
build, so it works regardless of your [search](/docs/configuration/search)
|
|
100
|
-
provider — even when search is set to `none` — and needs no configuration.
|
|
69
|
+
The page the reader is currently on is added to the context first and used to scope retrieval to that page's language, so answers stay relevant to where they are in the docs. Retrieval runs at request time from a snapshot baked into the build, so it works regardless of your [search](/docs/configuration/search) provider — even when search is set to `none` — and needs no configuration.
|
|
101
70
|
|
|
102
|
-
Grounding is on for every backend except **[Inkeep](#backends)**, which runs its
|
|
103
|
-
own retrieval over the content you've indexed in its dashboard.
|
|
71
|
+
Grounding is on for every backend except **[Inkeep](#backends)**, which runs its own retrieval over the content you've indexed in its dashboard.
|
|
104
72
|
|
|
105
73
|
### Server output required
|
|
106
74
|
|
|
107
|
-
Ask AI is a server route (`POST /api/ask`), so it can't run on a static build.
|
|
108
|
-
Switch to server output and pick an adapter:
|
|
75
|
+
Ask AI is a server route (`POST /api/ask`), so it can't run on a static build. Switch to server output and pick an adapter:
|
|
109
76
|
|
|
110
77
|
```ts blume.config.ts lineNumbers
|
|
111
78
|
deployment: {
|
|
@@ -114,28 +81,21 @@ deployment: {
|
|
|
114
81
|
}
|
|
115
82
|
```
|
|
116
83
|
|
|
117
|
-
A static build with Ask AI enabled fails fast with a message telling you to set
|
|
118
|
-
`deployment.output` to `server`. See [Deployment](/docs/deployment) for the adapters.
|
|
84
|
+
A static build with Ask AI enabled fails fast with a message telling you to set `deployment.output` to `server`. See [Deployment](/docs/deployment) for the adapters.
|
|
119
85
|
|
|
120
86
|
### Backends
|
|
121
87
|
|
|
122
|
-
By default Ask AI routes through the **Vercel AI Gateway**: `model` is a
|
|
123
|
-
`provider/model` string, so you switch models by changing it (`openai/gpt-5.5`,
|
|
124
|
-
`anthropic/claude-sonnet-4-5`, and so on) with no provider SDK to install. The
|
|
125
|
-
gateway reads `AI_GATEWAY_API_KEY` from your environment and is wired up
|
|
126
|
-
automatically when you deploy on Vercel.
|
|
88
|
+
By default Ask AI routes through the **Vercel AI Gateway**: `model` is a `provider/model` string, so you switch models by changing it (`openai/gpt-5.5`, `anthropic/claude-sonnet-4-5`, and so on) with no provider SDK to install. The gateway reads `AI_GATEWAY_API_KEY` from your environment and is wired up automatically when you deploy on Vercel.
|
|
127
89
|
|
|
128
|
-
Set `provider` to point Ask AI somewhere else. Each backend reads its API key
|
|
129
|
-
from an environment variable, and the matching provider SDK is added to your
|
|
130
|
-
project's runtime automatically when you build — only the one you use:
|
|
90
|
+
Set `provider` to point Ask AI somewhere else. Each backend reads its API key from an environment variable, and the matching provider SDK is added to your project's runtime automatically when you build — only the one you use:
|
|
131
91
|
|
|
132
|
-
| `provider`
|
|
133
|
-
|
|
|
134
|
-
| `gateway` (default) | a `provider/model` string via the AI Gateway
|
|
135
|
-
| `openrouter`
|
|
136
|
-
| `llmgateway`
|
|
137
|
-
| `inkeep`
|
|
138
|
-
| `openai-compatible` | whatever your endpoint serves
|
|
92
|
+
| `provider` | `model` | API key env var |
|
|
93
|
+
| --- | --- | --- |
|
|
94
|
+
| `gateway` (default) | a `provider/model` string via the AI Gateway | `AI_GATEWAY_API_KEY` |
|
|
95
|
+
| `openrouter` | any [OpenRouter](https://openrouter.ai) model | `OPENROUTER_API_KEY` |
|
|
96
|
+
| `llmgateway` | any [LLMGateway](https://llmgateway.io) model | `LLMGATEWAY_API_KEY` |
|
|
97
|
+
| `inkeep` | an [Inkeep](https://inkeep.com) QA model | `INKEEP_API_KEY` |
|
|
98
|
+
| `openai-compatible` | whatever your endpoint serves | set with `apiKeyEnv` |
|
|
139
99
|
|
|
140
100
|
For example, to use OpenRouter:
|
|
141
101
|
|
|
@@ -149,8 +109,7 @@ ai: {
|
|
|
149
109
|
}
|
|
150
110
|
```
|
|
151
111
|
|
|
152
|
-
Any OpenAI-compatible endpoint works through `openai-compatible` — supply the
|
|
153
|
-
`baseUrl` and the env var holding its key:
|
|
112
|
+
Any OpenAI-compatible endpoint works through `openai-compatible` — supply the `baseUrl` and the env var holding its key:
|
|
154
113
|
|
|
155
114
|
```ts blume.config.ts lineNumbers
|
|
156
115
|
ai: {
|
|
@@ -164,26 +123,19 @@ ai: {
|
|
|
164
123
|
}
|
|
165
124
|
```
|
|
166
125
|
|
|
167
|
-
Set `apiKeyEnv` (and, for the named providers, `baseUrl`) on any backend to point
|
|
168
|
-
at a different env var or proxy.
|
|
126
|
+
Set `apiKeyEnv` (and, for the named providers, `baseUrl`) on any backend to point at a different env var or proxy.
|
|
169
127
|
|
|
170
|
-
:::note
|
|
171
|
-
**Inkeep** answers from the content you've indexed in the Inkeep dashboard — it
|
|
172
|
-
runs its own retrieval — so Blume leaves it ungrounded. Every other backend is
|
|
173
|
-
[grounded](#grounding) in this site's pages.
|
|
174
|
-
:::
|
|
128
|
+
:::note **Inkeep** answers from the content you've indexed in the Inkeep dashboard — it runs its own retrieval — so Blume leaves it ungrounded. Every other backend is [grounded](#grounding) in this site's pages. :::
|
|
175
129
|
|
|
176
|
-
Keys are read with `process.env`, which covers the Node, Vercel, and Netlify
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
130
|
+
Keys are read with `process.env`, which covers the Node, Vercel, and Netlify adapters. On Cloudflare, expose the key through the platform's [runtime binding](https://docs.astro.build/en/guides/integrations-guide/cloudflare/#environment-variables-and-secrets). Enabling Ask AI also turns on React for the in-page island — see [Customization](/docs/configuration/customization#interactive-islands).
|
|
131
|
+
|
|
132
|
+
### Rate limiting
|
|
133
|
+
|
|
134
|
+
The `POST /api/ask` endpoint is **unauthenticated** — it has to be, so the in-page assistant can call it. Blume validates each request (rejecting malformed bodies and capping it to 1–40 messages) to bound how much a single call can spend against your model, but it can't stop someone from calling the endpoint repeatedly. If cost abuse is a concern, put the route behind a rate limiter — your host's (e.g. Vercel's) edge rate limiting, a middleware, or your model provider's per-key spend limits.
|
|
181
135
|
|
|
182
136
|
## MCP server
|
|
183
137
|
|
|
184
|
-
Host a [Model Context Protocol](https://modelcontextprotocol.io) server so coding
|
|
185
|
-
agents (Claude Code, Cursor, VS Code, claude.ai connectors) can search and read
|
|
186
|
-
your docs directly — no scraping:
|
|
138
|
+
Host a [Model Context Protocol](https://modelcontextprotocol.io) server so coding agents (Claude Code, Cursor, VS Code, claude.ai connectors) can search and read your docs directly — no scraping:
|
|
187
139
|
|
|
188
140
|
```ts blume.config.ts lineNumbers
|
|
189
141
|
mcp: {
|
|
@@ -199,20 +151,13 @@ mcp: {
|
|
|
199
151
|
| `name` | title | Server name shown to clients (defaults to title). |
|
|
200
152
|
| `instructions` | — | Optional system hint passed to connecting agents. |
|
|
201
153
|
|
|
202
|
-
The server exposes four read-only tools — `search_docs`, `get_page`,
|
|
203
|
-
`list_pages`, and `get_navigation` — and publishes discovery documents at
|
|
204
|
-
`/.well-known/mcp.json` and `/.well-known/mcp/server-card.json`. Each page's
|
|
205
|
-
**Connect to MCP** menu offers copy-and-go install for Claude Code, Cursor, and
|
|
206
|
-
VS Code (shown once [`deployment.site`](/docs/deployment) is set).
|
|
154
|
+
The server exposes four read-only tools — `search_docs`, `get_page`, `list_pages`, and `get_navigation` — and publishes discovery documents at `/.well-known/mcp.json` and `/.well-known/mcp/server-card.json`. Each page's **Connect to MCP** menu offers copy-and-go install for Claude Code, Cursor, and VS Code (shown once [`deployment.site`](/docs/deployment) is set).
|
|
207
155
|
|
|
208
|
-
`search_docs` runs its own full-text index, so it works regardless of your
|
|
209
|
-
[search](/docs/configuration/search) provider — and even when search is set to
|
|
210
|
-
`none`. The MCP server is a separate feature from on-page search.
|
|
156
|
+
`search_docs` runs its own full-text index, so it works regardless of your [search](/docs/configuration/search) provider — and even when search is set to `none`. The MCP server is a separate feature from on-page search.
|
|
211
157
|
|
|
212
158
|
### Server output required
|
|
213
159
|
|
|
214
|
-
The MCP server is a live endpoint (`/mcp`), so it can't run on a static build.
|
|
215
|
-
Switch to server output and pick an adapter:
|
|
160
|
+
The MCP server is a live endpoint (`/mcp`), so it can't run on a static build. Switch to server output and pick an adapter:
|
|
216
161
|
|
|
217
162
|
```ts blume.config.ts lineNumbers
|
|
218
163
|
deployment: {
|
|
@@ -222,9 +167,7 @@ deployment: {
|
|
|
222
167
|
}
|
|
223
168
|
```
|
|
224
169
|
|
|
225
|
-
A static build with `mcp.enabled` fails fast with a message telling you to set
|
|
226
|
-
`deployment.output` to `server`. See [Deployment](/docs/deployment) for the
|
|
227
|
-
adapters. Once deployed, connect from Claude Code with:
|
|
170
|
+
A static build with `mcp.enabled` fails fast with a message telling you to set `deployment.output` to `server`. See [Deployment](/docs/deployment) for the adapters. Once deployed, connect from Claude Code with:
|
|
228
171
|
|
|
229
172
|
```bash
|
|
230
173
|
claude mcp add --transport http my-docs https://docs.example.com/mcp
|
|
@@ -232,14 +175,10 @@ claude mcp add --transport http my-docs https://docs.example.com/mcp
|
|
|
232
175
|
|
|
233
176
|
## Agent skill
|
|
234
177
|
|
|
235
|
-
Building a Blume site with the help of a coding agent? Install the Blume
|
|
236
|
-
[agent skill](https://docs.claude.com/en/docs/claude-code/skills) so it knows
|
|
237
|
-
how Blume works without you explaining it:
|
|
178
|
+
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:
|
|
238
179
|
|
|
239
180
|
```bash
|
|
240
181
|
npx skills add haydenbleasel/blume
|
|
241
182
|
```
|
|
242
183
|
|
|
243
|
-
The skill teaches the agent what Blume is and how to scaffold, write, and
|
|
244
|
-
configure a site, and points it at the full docs bundled in the installed
|
|
245
|
-
package (`node_modules/blume/docs`).
|
|
184
|
+
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`).
|
|
@@ -3,24 +3,15 @@ title: Analytics
|
|
|
3
3
|
description: First-party web analytics — Vercel Web Analytics, PostHog, or any custom script — wired up from blume.config.ts.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
Blume injects analytics for you from a single `analytics` block in
|
|
7
|
-
`blume.config.ts`. Vercel Web Analytics and PostHog are first-class, and a
|
|
8
|
-
`scripts` escape hatch covers every other provider — Plausible, Fathom, Google
|
|
9
|
-
Analytics, Umami, and the rest.
|
|
6
|
+
Blume injects analytics for you from a single `analytics` block in `blume.config.ts`. Vercel Web Analytics and PostHog are first-class, and a `scripts` escape hatch covers every other provider — Plausible, Fathom, Google Analytics, Umami, and the rest.
|
|
10
7
|
|
|
11
|
-
Analytics loads in **production builds only**. The scripts are emitted by
|
|
12
|
-
`blume build`, never by `blume dev`, so local traffic never reaches your
|
|
13
|
-
dashboards and you don't need a separate "development" project.
|
|
8
|
+
Analytics loads in **production builds only**. The scripts are emitted by `blume build`, never by `blume dev`, so local traffic never reaches your dashboards and you don't need a separate "development" project.
|
|
14
9
|
|
|
15
|
-
:::note
|
|
16
|
-
Analytics is opt-in. With no `analytics` block, Blume injects nothing.
|
|
17
|
-
:::
|
|
10
|
+
:::note Analytics is opt-in. With no `analytics` block, Blume injects nothing. :::
|
|
18
11
|
|
|
19
12
|
## Vercel Web Analytics
|
|
20
13
|
|
|
21
|
-
Set `vercel: true` to add [Vercel Web Analytics](https://vercel.com/docs/analytics).
|
|
22
|
-
Blume injects Vercel's first-party script, which is served from your own domain
|
|
23
|
-
once Web Analytics is enabled for the project in the Vercel dashboard.
|
|
14
|
+
Set `vercel: true` to add [Vercel Web Analytics](https://vercel.com/docs/analytics). Blume injects Vercel's first-party script, which is served from your own domain once Web Analytics is enabled for the project in the Vercel dashboard.
|
|
24
15
|
|
|
25
16
|
```ts blume.config.ts lineNumbers
|
|
26
17
|
analytics: {
|
|
@@ -28,15 +19,11 @@ analytics: {
|
|
|
28
19
|
}
|
|
29
20
|
```
|
|
30
21
|
|
|
31
|
-
No keys are needed — the script reports to the project it's deployed under. This
|
|
32
|
-
only collects data on Vercel deployments, where the `/_vercel/insights` endpoint
|
|
33
|
-
exists.
|
|
22
|
+
No keys are needed — the script reports to the project it's deployed under. This only collects data on Vercel deployments, where the `/_vercel/insights` endpoint exists.
|
|
34
23
|
|
|
35
24
|
## PostHog
|
|
36
25
|
|
|
37
|
-
Provide your **project API key** to add [PostHog](https://posthog.com). The host
|
|
38
|
-
defaults to PostHog Cloud US; set `host` for EU Cloud
|
|
39
|
-
(`https://eu.i.posthog.com`) or a self-hosted instance.
|
|
26
|
+
Provide your **project API key** to add [PostHog](https://posthog.com). The host defaults to PostHog Cloud US; set `host` for EU Cloud (`https://eu.i.posthog.com`) or a self-hosted instance.
|
|
40
27
|
|
|
41
28
|
```ts blume.config.ts lineNumbers
|
|
42
29
|
analytics: {
|
|
@@ -47,14 +34,11 @@ analytics: {
|
|
|
47
34
|
}
|
|
48
35
|
```
|
|
49
36
|
|
|
50
|
-
The project API key is safe to ship to the browser — it's a public,
|
|
51
|
-
write-only key.
|
|
37
|
+
The project API key is safe to ship to the browser — it's a public, write-only key.
|
|
52
38
|
|
|
53
39
|
## Custom scripts
|
|
54
40
|
|
|
55
|
-
Use `scripts` to load any other analytics provider. Each entry renders one
|
|
56
|
-
`<script>` tag and must set **exactly one** of `src` (external) or `content`
|
|
57
|
-
(inline).
|
|
41
|
+
Use `scripts` to load any other analytics provider. Each entry renders one `<script>` tag and must set **exactly one** of `src` (external) or `content` (inline).
|
|
58
42
|
|
|
59
43
|
```ts blume.config.ts lineNumbers
|
|
60
44
|
analytics: {
|
|
@@ -79,20 +63,18 @@ analytics: {
|
|
|
79
63
|
}
|
|
80
64
|
```
|
|
81
65
|
|
|
82
|
-
`attributes` is a map of extra HTML attributes (`data-*`, `id`, …) spread onto
|
|
83
|
-
the tag, and `strategy` adds `async` or `defer` to external scripts.
|
|
66
|
+
`attributes` is a map of extra HTML attributes (`data-*`, `id`, …) spread onto the tag, and `strategy` adds `async` or `defer` to external scripts.
|
|
84
67
|
|
|
85
68
|
## Options
|
|
86
69
|
|
|
87
|
-
| Option
|
|
88
|
-
|
|
|
89
|
-
| `vercel`
|
|
90
|
-
| `posthog.key`
|
|
91
|
-
| `posthog.host`
|
|
92
|
-
| `scripts[].src`
|
|
93
|
-
| `scripts[].content`
|
|
94
|
-
| `scripts[].strategy`
|
|
95
|
-
| `scripts[].attributes` | —
|
|
96
|
-
|
|
97
|
-
All three can be combined — enable Vercel, PostHog, and custom scripts together.
|
|
98
|
-
For deploying your built site, see [Deployment](/docs/deployment).
|
|
70
|
+
| Option | Default | Description |
|
|
71
|
+
| --- | --- | --- |
|
|
72
|
+
| `vercel` | `false` | Add Vercel Web Analytics (Vercel deployments only). |
|
|
73
|
+
| `posthog.key` | — | PostHog project API key. Enables PostHog when set. |
|
|
74
|
+
| `posthog.host` | `https://us.i.posthog.com` | PostHog ingestion host (EU Cloud or self-hosted). |
|
|
75
|
+
| `scripts[].src` | — | External script URL. Mutually exclusive with `content`. |
|
|
76
|
+
| `scripts[].content` | — | Inline script body. Mutually exclusive with `src`. |
|
|
77
|
+
| `scripts[].strategy` | — | `async` or `defer` for an external script. |
|
|
78
|
+
| `scripts[].attributes` | — | Extra HTML attributes spread onto the `<script>` tag. |
|
|
79
|
+
|
|
80
|
+
All three can be combined — enable Vercel, PostHog, and custom scripts together. For deploying your built site, see [Deployment](/docs/deployment).
|