blume 1.6.2 → 1.6.4

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 (79) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/dist/cli/index.js +307 -68
  3. package/dist/cli/index.js.map +28 -23
  4. package/dist/types/ai/component-markdown.d.ts +14 -0
  5. package/docs/01-quickstart.mdx +2 -2
  6. package/docs/02-deployment.mdx +5 -5
  7. package/docs/{07-faq.mdx → 08-faq.mdx} +7 -7
  8. package/docs/advanced/blog.mdx +3 -3
  9. package/docs/advanced/changelog.mdx +2 -2
  10. package/docs/advanced/custom-pages.mdx +4 -4
  11. package/docs/advanced/meta.ts +1 -1
  12. package/docs/configuration/ask-ai.mdx +179 -0
  13. package/docs/configuration/index.mdx +8 -7
  14. package/docs/configuration/meta.ts +1 -2
  15. package/docs/configuration/search.mdx +1 -1
  16. package/docs/configuration/theming.mdx +1 -1
  17. package/docs/content/components.mdx +1 -1
  18. package/docs/content/i18n.mdx +7 -1
  19. package/docs/content/index.mdx +1 -1
  20. package/docs/content/navigation.mdx +2 -2
  21. package/docs/content/syntax.mdx +1 -1
  22. package/docs/discoverability/agent-discovery.mdx +196 -0
  23. package/docs/discoverability/index.mdx +48 -0
  24. package/docs/discoverability/json-api.mdx +58 -0
  25. package/docs/discoverability/llms-txt.mdx +68 -0
  26. package/docs/discoverability/markdown.mdx +76 -0
  27. package/docs/discoverability/mcp.mdx +64 -0
  28. package/docs/discoverability/meta.ts +18 -0
  29. package/docs/discoverability/metadata.mdx +82 -0
  30. package/docs/discoverability/open-graph.mdx +113 -0
  31. package/docs/discoverability/rss.mdx +24 -0
  32. package/docs/discoverability/sitemap-and-robots.mdx +95 -0
  33. package/docs/discoverability/structured-data.mdx +51 -0
  34. package/docs/index.mdx +5 -5
  35. package/docs/reference/eval.mdx +1 -1
  36. package/docs/reference/meta.ts +1 -1
  37. package/docs/reference/translate.mdx +1 -0
  38. package/package.json +18 -18
  39. package/src/ai/component-markdown.ts +17 -2
  40. package/src/ai/llms.ts +3 -10
  41. package/src/ai/markdown.ts +3 -10
  42. package/src/ai/openapi-components.ts +123 -0
  43. package/src/ai/serializers.ts +24 -0
  44. package/src/astro/templates.ts +42 -12
  45. package/src/audit/checks/links.ts +1 -8
  46. package/src/audit/checks/llms.ts +5 -4
  47. package/src/audit/redirects.ts +4 -3
  48. package/src/audit/run.ts +6 -8
  49. package/src/audit/url.ts +33 -0
  50. package/src/cli/commands/validate.ts +1 -0
  51. package/src/components/content/Component.astro +65 -68
  52. package/src/components/content/Tabs.astro +24 -9
  53. package/src/components/content/example-pane.ts +6 -0
  54. package/src/components/layout/LocaleLinks.astro +42 -0
  55. package/src/components/layout/PageLayout.astro +5 -3
  56. package/src/components/layout/ReferenceLayout.astro +5 -0
  57. package/src/components/layout/RootLayout.astro +110 -39
  58. package/src/components/layout/search-locale.ts +13 -0
  59. package/src/components/openapi/ApiOverview.astro +7 -39
  60. package/src/components/openapi/ApiTagOperations.astro +2 -1
  61. package/src/components/openapi/AsyncApiOperation.astro +3 -2
  62. package/src/components/openapi/GraphqlOperation.astro +3 -2
  63. package/src/components/openapi/Operation.astro +3 -2
  64. package/src/core/i18n.ts +13 -2
  65. package/src/core/links.ts +33 -1
  66. package/src/core/locale-links.ts +163 -0
  67. package/src/core/sources/normalize.ts +57 -7
  68. package/src/markdown/package-commands.ts +27 -3
  69. package/src/openapi/graphql.ts +29 -0
  70. package/src/openapi/model.ts +69 -0
  71. package/src/openapi/render-mdx.ts +3 -2
  72. package/src/openapi/signature.ts +18 -0
  73. package/src/search/documents.ts +4 -9
  74. package/src/theme/code-block-padding.ts +0 -8
  75. package/src/theme/entry.ts +33 -27
  76. package/src/translate/anchors.ts +91 -0
  77. package/src/translate/validate.ts +8 -3
  78. package/docs/configuration/ai.mdx +0 -613
  79. package/docs/configuration/seo.mdx +0 -364
@@ -1,364 +0,0 @@
1
- ---
2
- title: SEO
3
- description: Metadata, Open Graph images, RSS feeds, and JSON-LD — Blume's discoverability layer, grouped under one seo config.
4
- sidebar:
5
- label: SEO
6
- ---
7
-
8
- Blume handles the discoverability layer for you: page metadata, social share images, feeds, and structured data. The configurable features live under the `seo` key in `blume.config.ts`; metadata is driven by your content.
9
-
10
- ```ts blume.config.ts lineNumbers
11
- seo: {
12
- og: { enabled: true },
13
- rss: { enabled: true, types: ["blog", "changelog"] },
14
- sitemap: true,
15
- robots: true,
16
- structuredData: true,
17
- x: { handle: "@acme" },
18
- }
19
- ```
20
-
21
- Most of this is sharper with an absolute site URL — set [`deployment.site`](/docs/deployment) so feeds, OG images, canonicals, the sitemap, and JSON-LD can emit full URLs.
22
-
23
- ## Metadata
24
-
25
- Every page renders the standard `<head>` tags from your config and frontmatter:
26
-
27
- - `<title>` — the page title plus your site `title`.
28
- - `<meta name="description">` and `og:description` — the page `description`, falling back to the site `description`.
29
- - `og:title` and `og:site_name` — the page title and your site `title`.
30
- - `<link rel="canonical">` and `og:url` — the page's absolute URL (when `deployment.site` is set).
31
- - `og:type` — `article` on blog posts and changelog entries, `website` elsewhere. Article pages also emit `article:published_time` and `article:modified_time` from the page's `date` and last-modified timestamp.
32
- - `og:image` — the [OG image](#open-graph-images) for the page. A generated card also declares its `og:image:width`, `og:image:height`, `og:image:type`, and `og:image:alt`, so a crawler can lay the card out without fetching it first; an `seo.image` you supply yourself declares none of these, since its size and format are unknown.
33
- - `twitter:card`, `twitter:title`, `twitter:description`, `twitter:image` — the X card. Pages with an image get the wide `summary_large_image` variant; pages without one still get the compact `summary` card rather than rendering as a bare link.
34
-
35
- ### X attribution
36
-
37
- X reads everything else on the card from the `og:*` tags, so the only values it can't infer are the accounts to credit. Set them under `seo.x` and Blume emits `twitter:site` (your site's account) and `twitter:creator` (the author's). The `@` is optional — `acme` and `@acme` both work.
38
-
39
- ```ts blume.config.ts lineNumbers
40
- seo: {
41
- x: { handle: "@acme", creator: "@jane" },
42
- }
43
- ```
44
-
45
- A page can claim its own author, which is what you want for a guest post:
46
-
47
- ```yaml lineNumbers
48
- ---
49
- title: How we shipped it
50
- seo:
51
- x:
52
- creator: "@guestauthor"
53
- ---
54
- ```
55
-
56
- Override any of the other tags per page with `seo` frontmatter:
57
-
58
- ```yaml lineNumbers
59
- ---
60
- title: Pricing
61
- description: Plans and pricing for every team size.
62
- seo:
63
- title: Pricing — Acme
64
- canonical: https://acme.com/pricing
65
- noindex: false
66
- ---
67
- ```
68
-
69
- <TypeTable
70
- type={{
71
- "seo.title": {
72
- type: "string",
73
- description: "Override the <title> and og:title for this page.",
74
- },
75
- "seo.description": {
76
- type: "string",
77
- description: "Override the meta + og:description.",
78
- },
79
- "seo.image": {
80
- type: "string",
81
- description: "Custom social image (see Open Graph).",
82
- },
83
- "seo.canonical": {
84
- type: "string",
85
- description: "Override the canonical URL.",
86
- },
87
- "seo.noindex": {
88
- type: "boolean",
89
- description: "Emit robots noindex and skip structured data.",
90
- },
91
- "seo.x.creator": {
92
- type: "string",
93
- description:
94
- "Credit this page to an X account (twitter:creator), overriding seo.x.creator from your config.",
95
- },
96
- }}
97
- />
98
-
99
- ## Open Graph images
100
-
101
- Blume can render a 1200×630 social card for every page at build time — no headless browser, thanks to [Takumi](https://takumi.kane.tw), so builds stay fast. On by default once [`deployment.site`](/docs/deployment) is set or auto-detected (the `og:image` URL has to be absolute to be useful to crawlers), and off otherwise. Set `enabled` to override that either way:
102
-
103
- ```ts blume.config.ts lineNumbers
104
- seo: {
105
- og: { enabled: true }, // or false to opt out even with a site set
106
- }
107
- ```
108
-
109
- ### Brand the generated card
110
-
111
- Set a local SVG and color palette to match the generated card to your brand. The logo can live in `public/` or at the project root. Omit any palette value to keep its default.
112
-
113
- ```ts blume.config.ts lineNumbers
114
- seo: {
115
- og: {
116
- logo: "/logo/og.svg",
117
- palette: {
118
- accent: "#ff5410",
119
- background: "#1d1d1d",
120
- foreground: "#fff6f2",
121
- muted: "#a6a19f",
122
- border: "#323232",
123
- },
124
- },
125
- }
126
- ```
127
-
128
- By default, each card is derived from your content and theme — the **page title** as the headline, the **page description** as the subtitle (the same text as its `og:description`, so `seo.description` wins over `description`), your **site title** as the eyebrow, and your theme **accent** for the mark. Images are served at `/og/<slug>.png`, mirroring each route, and are prerendered as static files even in server mode:
129
-
130
- | Page route | Image URL |
131
- | ------------------- | -------------------------- |
132
- | `/` | `/og/index.png` |
133
- | `/quickstart` | `/og/quickstart.png` |
134
- | `/configuration/ai` | `/og/configuration/ai.png` |
135
-
136
- Override the generated card for any page with `seo.image` — a file in `public/` or an external URL. It takes precedence over the generated card and works even when `og` is off, so you can mix custom images with generated ones:
137
-
138
- ```yaml lineNumbers
139
- ---
140
- title: Pricing
141
- seo:
142
- image: /og/pricing-custom.png
143
- ---
144
- ```
145
-
146
- :::note
147
- Every palette color accepts any CSS color — hex, `oklch(…)`, `rgb(…)`, and so on. The accent also accepts a named preset (`blue`, `teal`, …), matching [`theme.accent`](/docs/configuration/theming#accent). A color the renderer can't parse fails the build rather than silently shipping a default-colored card.
148
- :::
149
-
150
- Emoji in a page title or site title render as [Twemoji](https://github.com/jdecked/twemoji) glyphs, fetched from a CDN while the card renders — so a build whose titles contain emoji needs network access. Each glyph is fetched once per build, however many pages use it.
151
-
152
- ### Show, hide, or override card layers
153
-
154
- Beyond the headline, the card carries three optional layers: the **brand mark** in the top-left (your logo, or an accent tile with the site title's initial), the **subtitle** under the headline (the page `description`, or your site `description` for pages without one), and a **footer** with your repo slug (from `github`) and the site's URL — the deployment site's host plus [`deployment.base`](/docs/deployment#subpath-deploys), so a GitHub Pages project site reads `user.github.io/repo`. Override any of them with a string of your own, or hide one with `false`:
155
-
156
- ```ts blume.config.ts lineNumbers
157
- seo: {
158
- og: {
159
- site: "docs.acme.com", // footer URL text, or false to hide it
160
- description: false, // hide the subtitle on every card; a string replaces the site fallback
161
- logo: false, // no brand mark at all — not even the initial tile
162
- },
163
- }
164
- ```
165
-
166
- ### Card fonts
167
-
168
- By default the card renders in Takumi's built-in font, which covers only Latin glyphs — a title in another script (Japanese, Chinese, Korean, Arabic, …) would render as tofu, empty boxes.
169
-
170
- **Set [`theme.fonts`](/docs/configuration/theming#fonts) and the card follows it.** When your config picks its own fonts, the generated cards automatically render the headline in your display font and the description and footer in your body font, so shared links match the site — including non-Latin coverage, with nothing to configure here. (Families from non-Google providers are skipped — the card renderer can only fetch from Google Fonts — but local font files work.)
171
-
172
- To use different fonts on cards than on the site, or to add script coverage without touching the theme, set `og.fonts` explicitly — it always wins over the theme-derived fonts:
173
-
174
- ```ts blume.config.ts lineNumbers
175
- seo: {
176
- og: {
177
- fonts: [
178
- "Noto Sans JP",
179
- { name: "Inter", weight: [400, 700] },
180
- { name: "Berkeley Mono", src: "./fonts/BerkeleyMono-Regular.woff2" },
181
- ],
182
- },
183
- }
184
- ```
185
-
186
- Each entry is a Google Fonts family name, an object pinning its `weight` (a number, a list, or a variable range like `"100..900"`) and `style` (`"normal"`, `"italic"`, or both), or a local font file — `src` resolves from the project root, with optional `weight` and `style` when the file's own metadata shouldn't decide.
187
-
188
- Google families are fetched at build — so a build that uses them needs network access — and the renderer only pulls the glyph subsets each title actually uses. Fallback is per-glyph, so adding a family only affects glyphs the other fonts can't draw.
189
-
190
- An explicit `og.fonts: []` opts out entirely: cards keep the built-in font even when `theme.fonts` is set.
191
-
192
- ### Custom page titles
193
-
194
- A custom [`.astro` page](/docs/advanced/custom-pages) has no frontmatter to read, so its generated card is titled by humanizing the last URL segment of its route — `/getting-started` becomes "Getting Started", but `/cli` becomes "Cli". Name those cards explicitly with `og.titles`, keyed by route (`"/"` addresses the home, whose card otherwise carries the site title):
195
-
196
- ```ts blume.config.ts lineNumbers
197
- seo: {
198
- og: {
199
- titles: {
200
- "/cli": "CLI",
201
- },
202
- },
203
- }
204
- ```
205
-
206
- Entries only apply to custom pages — a content page's card always takes its headline from the page title, so retitle those in frontmatter instead.
207
-
208
- `seo.image` is frontmatter, so it only covers Markdown and MDX content. To give a custom [`.astro` page](/docs/advanced/custom-pages) its own social image — a marketing home or landing page, and the way to give the home page alone a bespoke share image — pass the `ogImage` prop to `PageLayout`.
209
-
210
- ## RSS feeds
211
-
212
- Blume builds an RSS feed for each content type in `rss.types` — `blog` and `changelog` by default — that has pages, served at `/<type>/rss.xml`. See [Feeds](/docs/content#feeds) for authoring blog and changelog entries with dates.
213
-
214
- ```ts blume.config.ts lineNumbers
215
- seo: {
216
- rss: {
217
- enabled: true,
218
- types: ["blog", "changelog"],
219
- limit: 50,
220
- },
221
- }
222
- ```
223
-
224
- | Option | Default | Description |
225
- | --------- | ----------------------- | ------------------------------------- |
226
- | `enabled` | `true` | Generate feeds. |
227
- | `types` | `["blog", "changelog"]` | Content types that each get a feed. |
228
- | `limit` | `50` | Maximum items per feed, newest first. |
229
-
230
- Blume injects `<link rel="alternate">` tags so browsers and feed readers discover the feeds automatically.
231
-
232
- ## Structured data
233
-
234
- Blume emits [schema.org](https://schema.org) JSON-LD in every page's `<head>` so search engines understand your content. On by default:
235
-
236
- ```ts blume.config.ts lineNumbers
237
- seo: {
238
- structuredData: true,
239
- }
240
- ```
241
-
242
- Each page includes:
243
-
244
- - a **WebSite** node for site identity,
245
- - the page as an **article** — `BlogPosting` for blog posts, `TechArticle` for changelog and docs — with its description and publish date,
246
- - a **BreadcrumbList** built from the navigation trail.
247
-
248
- URLs are absolute when `deployment.site` is set. Pages marked `seo.noindex` are skipped.
249
-
250
- ### Site identity
251
-
252
- The WebSite node says a site exists; it doesn't say _what_ it is or _who_ runs it — which is what AI agents read JSON-LD for before they recommend or cite you. Two optional blocks fill that in, both needing `deployment.site` (the nodes carry absolute identifiers):
253
-
254
- ```ts blume.config.ts lineNumbers
255
- seo: {
256
- organization: {
257
- name: "Acme", // defaults to the site title
258
- email: "hello@acme.com",
259
- telephone: "+1 555 0100",
260
- address: { addressLocality: "Sydney", addressCountry: "AU" },
261
- logo: "/logo.svg",
262
- sameAs: ["https://github.com/acme", "https://x.com/acme"],
263
- },
264
- software: {
265
- license: "MIT",
266
- operatingSystem: "Node.js 22+",
267
- price: 0, // emitted as an Offer; 0 marks it free
268
- sameAs: ["https://www.npmjs.com/package/acme"],
269
- },
270
- }
271
- ```
272
-
273
- `organization` adds an **Organization** node to every page — the WebSite and article nodes cite it as `publisher` — with the email and telephone as a `ContactPoint` (`contactType` defaults to `"customer support"`) and the address as a `PostalAddress`, the two fields business-verification checks look for. `name` and `url` default to the site's; a root-relative `logo` is absolutized like any page URL.
274
-
275
- `software` adds a **SoftwareApplication** node to the homepage: the product's name and description (defaulting to the site's), `applicationCategory` (default `"DeveloperApplication"`), operating system, license, an `Offer` when `price` is set, and the registry or repository URLs in `sameAs`. Pass `software: true` to take every default. The organization, when configured, is cited as its `publisher`.
276
-
277
- ## Sitemap
278
-
279
- Blume writes a `sitemap.xml` of every indexable page at build time. It needs an absolute [`deployment.site`](/docs/deployment) and lists every page except drafts, hidden, and `noindex` pages. On a [versioned](/docs/content/versioning) site, archived pages whose canonical points at their live equivalent are left out too — the live page is the one to index. On by default:
280
-
281
- ```ts blume.config.ts lineNumbers
282
- seo: {
283
- sitemap: true,
284
- }
285
- ```
286
-
287
- Ship your own `public/sitemap.xml` to take over — Blume never overwrites a file you place in `public/`.
288
-
289
- ## Robots
290
-
291
- 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:
292
-
293
- ```ts blume.config.ts lineNumbers
294
- seo: {
295
- robots: true,
296
- }
297
- ```
298
-
299
- ```txt robots.txt
300
- User-agent: *
301
- Content-Signal: search=yes, ai-input=yes, ai-train=yes
302
- Allow: /
303
-
304
- Sitemap: https://docs.example.com/sitemap.xml
305
- ```
306
-
307
- ### Content signals
308
-
309
- 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:
310
-
311
- - `search` — traditional and AI search indexing
312
- - `aiInput` — grounding / RAG at answer time
313
- - `aiTrain` — model training
314
-
315
- Restrict any signal by setting it to `false`; the ones you leave out stay `yes`:
316
-
317
- ```ts blume.config.ts lineNumbers
318
- seo: {
319
- contentSignals: {
320
- aiTrain: false, // opt out of training, keep search + grounding
321
- },
322
- }
323
- ```
324
-
325
- ```txt robots.txt
326
- User-agent: *
327
- Content-Signal: search=yes, ai-input=yes, ai-train=no
328
- Allow: /
329
- ```
330
-
331
- Set `contentSignals: false` to drop the declaration entirely:
332
-
333
- ```ts blume.config.ts lineNumbers
334
- seo: {
335
- contentSignals: false,
336
- }
337
- ```
338
-
339
- <TypeTable
340
- type={{
341
- "seo.contentSignals": {
342
- type: "boolean | object",
343
- description:
344
- "Content-Signal declaration. true or omitted emits all signals as yes; false drops the line; an object sets signals individually.",
345
- },
346
- "contentSignals.search": {
347
- type: "boolean",
348
- description: "Allow use for search indexing (search). Default true.",
349
- },
350
- "contentSignals.aiInput": {
351
- type: "boolean",
352
- description:
353
- "Allow use for AI grounding / RAG at answer time (ai-input). Default true.",
354
- },
355
- "contentSignals.aiTrain": {
356
- type: "boolean",
357
- description: "Allow use for AI model training (ai-train). Default true.",
358
- },
359
- }}
360
- />
361
-
362
- 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 honor them.
363
-
364
- Ship your own `public/robots.txt` to take over.