blume 1.0.2 → 1.0.3
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/CHANGELOG.md +8 -0
- package/dist/cli/index.js +59 -40
- package/dist/cli/index.js.map +11 -11
- package/dist/types/core/config-input.d.ts +17 -14
- package/dist/types/core/config.d.ts +3 -3
- package/dist/types/core/schema.d.ts +82 -34
- package/docs/advanced/custom-pages.mdx +2 -0
- package/docs/configuration/ai.mdx +6 -4
- package/docs/configuration/index.mdx +6 -8
- package/docs/content/components.mdx +25 -3
- package/package.json +1 -1
- package/src/ai/agent-readability.ts +3 -3
- package/src/ai/mcp/data.ts +2 -2
- package/src/astro/generate.ts +15 -7
- package/src/astro/templates.ts +48 -20
- package/src/blume-modules.d.ts +6 -0
- package/src/components/layout/Header.astro +15 -2
- package/src/components/layout/PageLayout.astro +7 -3
- package/src/components/layout/ReferenceLayout.astro +1 -0
- package/src/components/layout/RootLayout.astro +6 -3
- package/src/core/config-input.ts +18 -19
- package/src/core/config.ts +3 -3
- package/src/core/schema.ts +15 -14
- package/src/core/server-features.ts +1 -1
- package/src/deploy/adapter-output.ts +11 -1
- package/src/registry/eject.ts +11 -5
|
@@ -446,7 +446,21 @@ export interface LlmsTxtConfig {
|
|
|
446
446
|
*/
|
|
447
447
|
openapi?: boolean;
|
|
448
448
|
}
|
|
449
|
-
/**
|
|
449
|
+
/** Expose the docs as an MCP server for connecting agents. */
|
|
450
|
+
export interface McpConfig {
|
|
451
|
+
/** Turn the MCP server on. Defaults to `false`. */
|
|
452
|
+
enabled?: boolean;
|
|
453
|
+
/** Optional system hint passed to connecting agents. */
|
|
454
|
+
instructions?: string;
|
|
455
|
+
/** Server name shown to clients; defaults to the site title. */
|
|
456
|
+
name?: string;
|
|
457
|
+
/** Route the server mounts at. Defaults to `/mcp`. */
|
|
458
|
+
route?: string;
|
|
459
|
+
}
|
|
460
|
+
/**
|
|
461
|
+
* AI-facing features: the Ask AI assistant, an `llms.txt` manifest, and the
|
|
462
|
+
* hosted MCP server.
|
|
463
|
+
*/
|
|
450
464
|
export interface AiConfig {
|
|
451
465
|
/** The Ask AI chat assistant. */
|
|
452
466
|
ask?: AskConfig;
|
|
@@ -474,6 +488,8 @@ export interface AiConfig {
|
|
|
474
488
|
* ```
|
|
475
489
|
*/
|
|
476
490
|
markdownComponents?: Record<string, ComponentMarkdown>;
|
|
491
|
+
/** Expose the docs as an MCP server for agents. */
|
|
492
|
+
mcp?: McpConfig;
|
|
477
493
|
}
|
|
478
494
|
/** An arbitrary analytics `<script>`; set exactly one of `src` or `content`. */
|
|
479
495
|
export interface AnalyticsScript {
|
|
@@ -500,17 +516,6 @@ export interface AnalyticsConfig {
|
|
|
500
516
|
/** Enable Vercel Web Analytics. */
|
|
501
517
|
vercel?: boolean;
|
|
502
518
|
}
|
|
503
|
-
/** Expose the docs as an MCP server for connecting agents. */
|
|
504
|
-
export interface McpConfig {
|
|
505
|
-
/** Turn the MCP server on. Defaults to `false`. */
|
|
506
|
-
enabled?: boolean;
|
|
507
|
-
/** Optional system hint passed to connecting agents. */
|
|
508
|
-
instructions?: string;
|
|
509
|
-
/** Server name shown to clients; defaults to the site title. */
|
|
510
|
-
name?: string;
|
|
511
|
-
/** Route the server mounts at. Defaults to `/mcp`. */
|
|
512
|
-
route?: string;
|
|
513
|
-
}
|
|
514
519
|
/** A configured locale plus display metadata for the switcher. */
|
|
515
520
|
export interface LocaleConfigInput {
|
|
516
521
|
/** Locale code, e.g. `en`, `fr`, `pt-BR`. */
|
|
@@ -829,8 +834,6 @@ export interface BlumeConfig {
|
|
|
829
834
|
logo?: LogoConfig;
|
|
830
835
|
/** Markdown / MDX rendering behavior. */
|
|
831
836
|
markdown?: MarkdownConfig;
|
|
832
|
-
/** Expose the docs as an MCP server for agents. */
|
|
833
|
-
mcp?: McpConfig;
|
|
834
837
|
/** Header, sidebar, tabs, and switchers. */
|
|
835
838
|
navigation?: NavigationConfig;
|
|
836
839
|
/** Native OpenAPI reference. */
|
|
@@ -66,9 +66,9 @@ import type { Diagnostic } from "./types.ts";
|
|
|
66
66
|
* `algolia`, `typesense`, `orama-cloud`, `mixedbread`, or `none`) plus its
|
|
67
67
|
* credential block.
|
|
68
68
|
* - `ai` — `ask` (the Ask AI chat endpoint and its provider/model), `llmsTxt`
|
|
69
|
-
* (emit `llms.txt`),
|
|
70
|
-
*
|
|
71
|
-
* -
|
|
69
|
+
* (emit `llms.txt`), `mcp` (expose the docs as an MCP server for connecting
|
|
70
|
+
* agents), and `markdownComponents` (Markdown serializers for custom
|
|
71
|
+
* components in agent-facing output).
|
|
72
72
|
*
|
|
73
73
|
* **SEO, feeds & analytics**
|
|
74
74
|
* - `seo` — `og` images, `sitemap`, `robots`, `rss` feeds, `structuredData`
|
|
@@ -819,12 +819,41 @@ declare const aiConfigSchema: z.ZodObject<{
|
|
|
819
819
|
openapi?: boolean | undefined;
|
|
820
820
|
} | undefined>;
|
|
821
821
|
markdownComponents: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodType<ComponentMarkdown, z.ZodTypeDef, ComponentMarkdown>>>;
|
|
822
|
+
/** Expose the docs as an MCP server for connecting agents. */
|
|
823
|
+
mcp: z.ZodDefault<z.ZodObject<{
|
|
824
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
825
|
+
/** Optional system hint passed to connecting agents. */
|
|
826
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
827
|
+
/** Server name shown to clients; defaults to the site title. */
|
|
828
|
+
name: z.ZodOptional<z.ZodString>;
|
|
829
|
+
/**
|
|
830
|
+
* Normalized like `openapi.route`: a slash-less value would otherwise be
|
|
831
|
+
* string-concatenated onto the site origin (`https://acme.comdocs-mcp`).
|
|
832
|
+
*/
|
|
833
|
+
route: z.ZodEffects<z.ZodDefault<z.ZodString>, string, string | undefined>;
|
|
834
|
+
}, "strict", z.ZodTypeAny, {
|
|
835
|
+
enabled: boolean;
|
|
836
|
+
route: string;
|
|
837
|
+
name?: string | undefined;
|
|
838
|
+
instructions?: string | undefined;
|
|
839
|
+
}, {
|
|
840
|
+
name?: string | undefined;
|
|
841
|
+
enabled?: boolean | undefined;
|
|
842
|
+
instructions?: string | undefined;
|
|
843
|
+
route?: string | undefined;
|
|
844
|
+
}>>;
|
|
822
845
|
}, "strict", z.ZodTypeAny, {
|
|
823
846
|
llmsTxt: {
|
|
824
847
|
enabled: boolean;
|
|
825
848
|
openapi: boolean;
|
|
826
849
|
};
|
|
827
850
|
markdownComponents: Record<string, ComponentMarkdown>;
|
|
851
|
+
mcp: {
|
|
852
|
+
enabled: boolean;
|
|
853
|
+
route: string;
|
|
854
|
+
name?: string | undefined;
|
|
855
|
+
instructions?: string | undefined;
|
|
856
|
+
};
|
|
828
857
|
ask?: {
|
|
829
858
|
enabled: boolean;
|
|
830
859
|
model: string;
|
|
@@ -853,6 +882,12 @@ declare const aiConfigSchema: z.ZodObject<{
|
|
|
853
882
|
openapi?: boolean | undefined;
|
|
854
883
|
} | undefined;
|
|
855
884
|
markdownComponents?: Record<string, ComponentMarkdown> | undefined;
|
|
885
|
+
mcp?: {
|
|
886
|
+
name?: string | undefined;
|
|
887
|
+
enabled?: boolean | undefined;
|
|
888
|
+
instructions?: string | undefined;
|
|
889
|
+
route?: string | undefined;
|
|
890
|
+
} | undefined;
|
|
856
891
|
}>;
|
|
857
892
|
export type AskAiProvider = (typeof askAiProviders)[number];
|
|
858
893
|
export type AskAiConfig = NonNullable<z.infer<typeof aiConfigSchema>["ask"]>;
|
|
@@ -1075,12 +1110,41 @@ export declare const blumeConfigSchema: z.ZodObject<{
|
|
|
1075
1110
|
openapi?: boolean | undefined;
|
|
1076
1111
|
} | undefined>;
|
|
1077
1112
|
markdownComponents: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodType<ComponentMarkdown, z.ZodTypeDef, ComponentMarkdown>>>;
|
|
1113
|
+
/** Expose the docs as an MCP server for connecting agents. */
|
|
1114
|
+
mcp: z.ZodDefault<z.ZodObject<{
|
|
1115
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1116
|
+
/** Optional system hint passed to connecting agents. */
|
|
1117
|
+
instructions: z.ZodOptional<z.ZodString>;
|
|
1118
|
+
/** Server name shown to clients; defaults to the site title. */
|
|
1119
|
+
name: z.ZodOptional<z.ZodString>;
|
|
1120
|
+
/**
|
|
1121
|
+
* Normalized like `openapi.route`: a slash-less value would otherwise be
|
|
1122
|
+
* string-concatenated onto the site origin (`https://acme.comdocs-mcp`).
|
|
1123
|
+
*/
|
|
1124
|
+
route: z.ZodEffects<z.ZodDefault<z.ZodString>, string, string | undefined>;
|
|
1125
|
+
}, "strict", z.ZodTypeAny, {
|
|
1126
|
+
enabled: boolean;
|
|
1127
|
+
route: string;
|
|
1128
|
+
name?: string | undefined;
|
|
1129
|
+
instructions?: string | undefined;
|
|
1130
|
+
}, {
|
|
1131
|
+
name?: string | undefined;
|
|
1132
|
+
enabled?: boolean | undefined;
|
|
1133
|
+
instructions?: string | undefined;
|
|
1134
|
+
route?: string | undefined;
|
|
1135
|
+
}>>;
|
|
1078
1136
|
}, "strict", z.ZodTypeAny, {
|
|
1079
1137
|
llmsTxt: {
|
|
1080
1138
|
enabled: boolean;
|
|
1081
1139
|
openapi: boolean;
|
|
1082
1140
|
};
|
|
1083
1141
|
markdownComponents: Record<string, ComponentMarkdown>;
|
|
1142
|
+
mcp: {
|
|
1143
|
+
enabled: boolean;
|
|
1144
|
+
route: string;
|
|
1145
|
+
name?: string | undefined;
|
|
1146
|
+
instructions?: string | undefined;
|
|
1147
|
+
};
|
|
1084
1148
|
ask?: {
|
|
1085
1149
|
enabled: boolean;
|
|
1086
1150
|
model: string;
|
|
@@ -1109,6 +1173,12 @@ export declare const blumeConfigSchema: z.ZodObject<{
|
|
|
1109
1173
|
openapi?: boolean | undefined;
|
|
1110
1174
|
} | undefined;
|
|
1111
1175
|
markdownComponents?: Record<string, ComponentMarkdown> | undefined;
|
|
1176
|
+
mcp?: {
|
|
1177
|
+
name?: string | undefined;
|
|
1178
|
+
enabled?: boolean | undefined;
|
|
1179
|
+
instructions?: string | undefined;
|
|
1180
|
+
route?: string | undefined;
|
|
1181
|
+
} | undefined;
|
|
1112
1182
|
}>>;
|
|
1113
1183
|
analytics: z.ZodOptional<z.ZodObject<{
|
|
1114
1184
|
posthog: z.ZodOptional<z.ZodObject<{
|
|
@@ -1895,28 +1965,6 @@ export declare const blumeConfigSchema: z.ZodObject<{
|
|
|
1895
1965
|
headingAnchors?: boolean | undefined;
|
|
1896
1966
|
imageZoom?: boolean | undefined;
|
|
1897
1967
|
}>>;
|
|
1898
|
-
mcp: z.ZodDefault<z.ZodObject<{
|
|
1899
|
-
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1900
|
-
/** Optional system hint passed to connecting agents. */
|
|
1901
|
-
instructions: z.ZodOptional<z.ZodString>;
|
|
1902
|
-
/** Server name shown to clients; defaults to the site title. */
|
|
1903
|
-
name: z.ZodOptional<z.ZodString>;
|
|
1904
|
-
/**
|
|
1905
|
-
* Normalized like `openapi.route`: a slash-less value would otherwise be
|
|
1906
|
-
* string-concatenated onto the site origin (`https://acme.comdocs-mcp`).
|
|
1907
|
-
*/
|
|
1908
|
-
route: z.ZodEffects<z.ZodDefault<z.ZodString>, string, string | undefined>;
|
|
1909
|
-
}, "strict", z.ZodTypeAny, {
|
|
1910
|
-
enabled: boolean;
|
|
1911
|
-
route: string;
|
|
1912
|
-
name?: string | undefined;
|
|
1913
|
-
instructions?: string | undefined;
|
|
1914
|
-
}, {
|
|
1915
|
-
name?: string | undefined;
|
|
1916
|
-
enabled?: boolean | undefined;
|
|
1917
|
-
route?: string | undefined;
|
|
1918
|
-
instructions?: string | undefined;
|
|
1919
|
-
}>>;
|
|
1920
1968
|
navigation: z.ZodDefault<z.ZodObject<{
|
|
1921
1969
|
/** Pinned links shown above the generated sidebar sections. */
|
|
1922
1970
|
featured: z.ZodDefault<z.ZodArray<z.ZodObject<{
|
|
@@ -2692,6 +2740,12 @@ export declare const blumeConfigSchema: z.ZodObject<{
|
|
|
2692
2740
|
openapi: boolean;
|
|
2693
2741
|
};
|
|
2694
2742
|
markdownComponents: Record<string, ComponentMarkdown>;
|
|
2743
|
+
mcp: {
|
|
2744
|
+
enabled: boolean;
|
|
2745
|
+
route: string;
|
|
2746
|
+
name?: string | undefined;
|
|
2747
|
+
instructions?: string | undefined;
|
|
2748
|
+
};
|
|
2695
2749
|
ask?: {
|
|
2696
2750
|
enabled: boolean;
|
|
2697
2751
|
model: string;
|
|
@@ -2835,12 +2889,6 @@ export declare const blumeConfigSchema: z.ZodObject<{
|
|
|
2835
2889
|
headingAnchors: boolean;
|
|
2836
2890
|
imageZoom: boolean;
|
|
2837
2891
|
};
|
|
2838
|
-
mcp: {
|
|
2839
|
-
enabled: boolean;
|
|
2840
|
-
route: string;
|
|
2841
|
-
name?: string | undefined;
|
|
2842
|
-
instructions?: string | undefined;
|
|
2843
|
-
};
|
|
2844
2892
|
navigation: {
|
|
2845
2893
|
sidebar: {
|
|
2846
2894
|
display: "flat" | "page" | "group";
|
|
@@ -3025,6 +3073,12 @@ export declare const blumeConfigSchema: z.ZodObject<{
|
|
|
3025
3073
|
openapi?: boolean | undefined;
|
|
3026
3074
|
} | undefined;
|
|
3027
3075
|
markdownComponents?: Record<string, ComponentMarkdown> | undefined;
|
|
3076
|
+
mcp?: {
|
|
3077
|
+
name?: string | undefined;
|
|
3078
|
+
enabled?: boolean | undefined;
|
|
3079
|
+
instructions?: string | undefined;
|
|
3080
|
+
route?: string | undefined;
|
|
3081
|
+
} | undefined;
|
|
3028
3082
|
} | undefined;
|
|
3029
3083
|
content?: {
|
|
3030
3084
|
root?: string | undefined;
|
|
@@ -3206,12 +3260,6 @@ export declare const blumeConfigSchema: z.ZodObject<{
|
|
|
3206
3260
|
headingAnchors?: boolean | undefined;
|
|
3207
3261
|
imageZoom?: boolean | undefined;
|
|
3208
3262
|
} | undefined;
|
|
3209
|
-
mcp?: {
|
|
3210
|
-
name?: string | undefined;
|
|
3211
|
-
enabled?: boolean | undefined;
|
|
3212
|
-
route?: string | undefined;
|
|
3213
|
-
instructions?: string | undefined;
|
|
3214
|
-
} | undefined;
|
|
3215
3263
|
navigation?: {
|
|
3216
3264
|
sidebar?: SidebarItemConfig[] | {
|
|
3217
3265
|
display?: "flat" | "page" | "group" | undefined;
|
|
@@ -222,6 +222,8 @@ const { config } = data;
|
|
|
222
222
|
</PageLayout>
|
|
223
223
|
```
|
|
224
224
|
|
|
225
|
+
The header a custom page gets is the same one the docs pages get, so the chrome that lives in it comes along: search, the theme toggle, the language switcher, and — when [Ask AI](/docs/configuration/ai) is configured — the Ask AI trigger. None of it needs wiring up per page. Pass `askEnabled={false}` to leave the Ask trigger off one page while keeping it everywhere else.
|
|
226
|
+
|
|
225
227
|
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:
|
|
226
228
|
|
|
227
229
|
```astro pages/index.astro lineNumbers
|
|
@@ -198,9 +198,11 @@ The `POST /api/ask` endpoint is **unauthenticated** — it has to be, so the in-
|
|
|
198
198
|
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:
|
|
199
199
|
|
|
200
200
|
```ts blume.config.ts lineNumbers
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
201
|
+
ai: {
|
|
202
|
+
mcp: {
|
|
203
|
+
enabled: true,
|
|
204
|
+
route: "/mcp", // where the server is mounted
|
|
205
|
+
},
|
|
204
206
|
}
|
|
205
207
|
```
|
|
206
208
|
|
|
@@ -227,7 +229,7 @@ deployment: {
|
|
|
227
229
|
}
|
|
228
230
|
```
|
|
229
231
|
|
|
230
|
-
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:
|
|
232
|
+
A static build with `ai.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:
|
|
231
233
|
|
|
232
234
|
```bash
|
|
233
235
|
claude mcp add --transport http my-docs https://docs.example.com/mcp
|
|
@@ -64,12 +64,11 @@ export default defineConfig({
|
|
|
64
64
|
// AI — see the AI guide
|
|
65
65
|
ai: {
|
|
66
66
|
llmsTxt: true,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
route: "/mcp",
|
|
67
|
+
// MCP server (needs server output)
|
|
68
|
+
mcp: {
|
|
69
|
+
enabled: false,
|
|
70
|
+
route: "/mcp",
|
|
71
|
+
},
|
|
73
72
|
},
|
|
74
73
|
|
|
75
74
|
// SEO — OG images, feeds, sitemap, structured data; see the SEO guide
|
|
@@ -287,8 +286,7 @@ Each of these has its own guide. The config field is the entry point:
|
|
|
287
286
|
| `navigation` | Explicit sidebar and header tabs | [Navigation](/docs/content/navigation) |
|
|
288
287
|
| `search` | Provider (Orama, Pagefind, Algolia, and more) and indexing | [Search](/docs/configuration/search) |
|
|
289
288
|
| `markdown` | Markdown rendering options — code blocks, heading anchors, image zoom | [Syntax](/docs/content/syntax) |
|
|
290
|
-
| `ai` | `llms.txt`, Ask AI, and the MCP server | [AI](/docs/configuration/ai) |
|
|
291
|
-
| `mcp` | Hosted MCP server for coding agents | [AI](/docs/configuration/ai#mcp-server) |
|
|
289
|
+
| `ai` | `llms.txt`, Ask AI, and the hosted MCP server for coding agents | [AI](/docs/configuration/ai) |
|
|
292
290
|
| `analytics` | Vercel, PostHog, and custom scripts | [Analytics](/docs/configuration/analytics) |
|
|
293
291
|
| `seo` | Metadata, OG images, feeds, structured data | [SEO](/docs/configuration/seo) |
|
|
294
292
|
| `deployment` | Output mode, adapter, and site URL | [Deployment](/docs/deployment) |
|
|
@@ -532,14 +532,14 @@ export interface ButtonProps {
|
|
|
532
532
|
|
|
533
533
|
A card linking to a GitHub repository with its live star and fork counts. Counts are fetched at build time — no client JavaScript — and the card still renders if the API is unreachable. Pass `owner` and `repo`, or omit them to use the repository from your `blume.config`. Set a `GITHUB_TOKEN` environment variable to lift the API rate limit.
|
|
534
534
|
|
|
535
|
-
<GithubInfo owner="
|
|
535
|
+
<GithubInfo owner="haydenbleasel" repo="blume" />
|
|
536
536
|
|
|
537
537
|
```astro lineNumbers
|
|
538
538
|
<!-- Uses the repo from blume.config -->
|
|
539
539
|
<GithubInfo />
|
|
540
540
|
|
|
541
541
|
<!-- Or point it at any repository -->
|
|
542
|
-
<GithubInfo owner="
|
|
542
|
+
<GithubInfo owner="haydenbleasel" repo="blume" />
|
|
543
543
|
```
|
|
544
544
|
|
|
545
545
|
## Component
|
|
@@ -670,6 +670,28 @@ Diff two files in your project, relative to its root:
|
|
|
670
670
|
|
|
671
671
|
<Diff before="diffs/button-before.ts" after="diffs/button-after.ts" />
|
|
672
672
|
|
|
673
|
-
|
|
673
|
+
```astro
|
|
674
|
+
<Diff before="diffs/button-before.ts" after="diffs/button-after.ts" />
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
Or render a unified patch — either from a file with `src`, or inline with `patch`:
|
|
674
678
|
|
|
675
679
|
<Diff src="diffs/greet.patch" />
|
|
680
|
+
|
|
681
|
+
```astro
|
|
682
|
+
<Diff src="diffs/greet.patch" />
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
```astro
|
|
686
|
+
<Diff
|
|
687
|
+
patch={`--- a/greet.ts
|
|
688
|
+
+++ b/greet.ts
|
|
689
|
+
@@ -1,3 +1,4 @@
|
|
690
|
+
-export function greet(name) {
|
|
691
|
+
- return "Hi, " + name;
|
|
692
|
+
+export function greet(name: string): string {
|
|
693
|
+
+ const greeting = "Hi, " + name + "!";
|
|
694
|
+
+ return greeting;
|
|
695
|
+
}`}
|
|
696
|
+
/>
|
|
697
|
+
```
|
package/package.json
CHANGED
|
@@ -62,10 +62,10 @@ export const buildAgentReadability = (
|
|
|
62
62
|
artifacts.llmsFullTxt = abs("/llms-full.txt");
|
|
63
63
|
artifacts.llmsTxt = abs("/llms.txt");
|
|
64
64
|
}
|
|
65
|
-
if (config.mcp.enabled) {
|
|
65
|
+
if (config.ai.mcp.enabled) {
|
|
66
66
|
artifacts.mcp = {
|
|
67
67
|
discovery: abs("/.well-known/mcp.json"),
|
|
68
|
-
url: abs(config.mcp.route),
|
|
68
|
+
url: abs(config.ai.mcp.route),
|
|
69
69
|
};
|
|
70
70
|
}
|
|
71
71
|
if (config.ai.ask?.enabled) {
|
|
@@ -87,7 +87,7 @@ export const buildAgentReadability = (
|
|
|
87
87
|
artifacts,
|
|
88
88
|
description: config.description,
|
|
89
89
|
generator: version ? `blume@${version}` : undefined,
|
|
90
|
-
name: config.mcp.name ?? config.title,
|
|
90
|
+
name: config.ai.mcp.name ?? config.title,
|
|
91
91
|
site,
|
|
92
92
|
};
|
|
93
93
|
|
package/src/ai/mcp/data.ts
CHANGED
|
@@ -88,8 +88,8 @@ export const buildMcpData = async (project: BlumeProject): Promise<McpData> => {
|
|
|
88
88
|
route: doc.route,
|
|
89
89
|
title: doc.title,
|
|
90
90
|
})),
|
|
91
|
-
instructions: config.mcp.instructions,
|
|
92
|
-
name: config.mcp.name ?? config.title,
|
|
91
|
+
instructions: config.ai.mcp.instructions,
|
|
92
|
+
name: config.ai.mcp.name ?? config.title,
|
|
93
93
|
navigation: graph.navigation,
|
|
94
94
|
pages,
|
|
95
95
|
routes,
|
package/src/astro/generate.ts
CHANGED
|
@@ -61,6 +61,7 @@ import {
|
|
|
61
61
|
routeIsTaken,
|
|
62
62
|
} from "./pages.ts";
|
|
63
63
|
import {
|
|
64
|
+
askComponentTemplate,
|
|
64
65
|
askEndpointTemplate,
|
|
65
66
|
astroConfigTemplate,
|
|
66
67
|
catchAllPageTemplate,
|
|
@@ -865,8 +866,11 @@ export const buildRuntimeData = (project: BlumeProject): string => {
|
|
|
865
866
|
: null,
|
|
866
867
|
imageZoom: config.markdown.imageZoom,
|
|
867
868
|
logo: resolveLogo(project),
|
|
868
|
-
mcp: config.mcp.enabled
|
|
869
|
-
? {
|
|
869
|
+
mcp: config.ai.mcp.enabled
|
|
870
|
+
? {
|
|
871
|
+
name: config.ai.mcp.name ?? config.title,
|
|
872
|
+
route: config.ai.mcp.route,
|
|
873
|
+
}
|
|
870
874
|
: null,
|
|
871
875
|
// `og.enabled` is resolved to a definite boolean in `loadConfig`; coerce
|
|
872
876
|
// the optional schema type so the serialized shape stays `boolean`.
|
|
@@ -939,7 +943,7 @@ const planMcp = (
|
|
|
939
943
|
userPages: { pattern: string }[]
|
|
940
944
|
): McpPlan => {
|
|
941
945
|
const { config } = project;
|
|
942
|
-
const { route } = config.mcp;
|
|
946
|
+
const { route } = config.ai.mcp;
|
|
943
947
|
const dir = join(srcDir, "blume-mcp");
|
|
944
948
|
const base: McpPlan = {
|
|
945
949
|
dir,
|
|
@@ -949,14 +953,14 @@ const planMcp = (
|
|
|
949
953
|
srcDir,
|
|
950
954
|
warnings: [],
|
|
951
955
|
};
|
|
952
|
-
if (!config.mcp.enabled) {
|
|
956
|
+
if (!config.ai.mcp.enabled) {
|
|
953
957
|
return base;
|
|
954
958
|
}
|
|
955
959
|
if (routeIsTaken(userPages, project.graph.pages, route)) {
|
|
956
960
|
return {
|
|
957
961
|
...base,
|
|
958
962
|
warnings: [
|
|
959
|
-
`MCP server route "${route}" is already used by a content or custom page; the MCP server was not generated. Set a different "mcp.route" in blume.config.ts.`,
|
|
963
|
+
`MCP server route "${route}" is already used by a content or custom page; the MCP server was not generated. Set a different "ai.mcp.route" in blume.config.ts.`,
|
|
960
964
|
],
|
|
961
965
|
};
|
|
962
966
|
}
|
|
@@ -1105,6 +1109,7 @@ export const generateRuntime = async (
|
|
|
1105
1109
|
const { context, config } = project;
|
|
1106
1110
|
const out = context.outDir;
|
|
1107
1111
|
const srcDir = join(out, "src");
|
|
1112
|
+
const askPath = join(srcDir, "generated", "Ask.astro");
|
|
1108
1113
|
const dataPath = join(srcDir, "generated", "data.json");
|
|
1109
1114
|
const themePath = join(srcDir, "generated", "app.css");
|
|
1110
1115
|
const searchClientPath = join(srcDir, "generated", "search-client.ts");
|
|
@@ -1205,6 +1210,7 @@ export const generateRuntime = async (
|
|
|
1205
1210
|
join(out, "astro.config.mjs"),
|
|
1206
1211
|
astroConfigTemplate({
|
|
1207
1212
|
aliases: resolveTsconfigAliases(context.root),
|
|
1213
|
+
askPath,
|
|
1208
1214
|
config,
|
|
1209
1215
|
contentRoutes: project.manifest.routes.map((route) => route.path),
|
|
1210
1216
|
context,
|
|
@@ -1242,13 +1248,16 @@ export const generateRuntime = async (
|
|
|
1242
1248
|
write(
|
|
1243
1249
|
join(srcDir, "pages", "[...slug].astro"),
|
|
1244
1250
|
catchAllPageTemplate({
|
|
1245
|
-
askEnabled,
|
|
1246
1251
|
exportEpub,
|
|
1247
1252
|
exportPdf,
|
|
1248
1253
|
mathEnabled: usesMath,
|
|
1249
1254
|
needsReact,
|
|
1250
1255
|
})
|
|
1251
1256
|
),
|
|
1257
|
+
// The header's Ask trigger, behind the `blume:ask` alias. Always written
|
|
1258
|
+
// (even when Ask is off, as a component that renders nothing) so the alias
|
|
1259
|
+
// resolves — the same contract as `blume:search-client`.
|
|
1260
|
+
write(askPath, askComponentTemplate(askEnabled)),
|
|
1252
1261
|
write(join(srcDir, "generated", "components.ts"), slotPlan.module),
|
|
1253
1262
|
write(
|
|
1254
1263
|
join(srcDir, "generated", "islands.ts"),
|
|
@@ -1336,7 +1345,6 @@ export const generateRuntime = async (
|
|
|
1336
1345
|
await write(
|
|
1337
1346
|
join(srcDir, "pages", "changelog.astro"),
|
|
1338
1347
|
changelogIndexTemplate({
|
|
1339
|
-
askEnabled,
|
|
1340
1348
|
exportEpub,
|
|
1341
1349
|
exportPdf,
|
|
1342
1350
|
needsReact,
|
package/src/astro/templates.ts
CHANGED
|
@@ -222,6 +222,8 @@ export const astroConfigTemplate = (options: {
|
|
|
222
222
|
needsSvelte?: boolean;
|
|
223
223
|
pages: BlumePageRoute[];
|
|
224
224
|
contentRoutes: string[];
|
|
225
|
+
/** The generated Ask trigger (`blume:ask`); renders nothing when Ask is off. */
|
|
226
|
+
askPath: string;
|
|
225
227
|
dataPath: string;
|
|
226
228
|
examplesPath: string;
|
|
227
229
|
/** The example-preview Tailwind entry (`blume:examples-theme`). */
|
|
@@ -240,6 +242,7 @@ export const astroConfigTemplate = (options: {
|
|
|
240
242
|
}): string => {
|
|
241
243
|
const { context, config, needsReact, pages, dataPath, themePath } = options;
|
|
242
244
|
const {
|
|
245
|
+
askPath,
|
|
243
246
|
contentRoutes,
|
|
244
247
|
examplesPath,
|
|
245
248
|
examplesThemePath,
|
|
@@ -425,6 +428,7 @@ export default defineConfig({
|
|
|
425
428
|
},
|
|
426
429
|
resolve: {
|
|
427
430
|
alias: {
|
|
431
|
+
"blume:ask": ${JSON.stringify(askPath)},
|
|
428
432
|
"blume:data": ${JSON.stringify(dataPath)},
|
|
429
433
|
"blume:examples": ${JSON.stringify(examplesPath)},
|
|
430
434
|
"blume:examples-theme": ${JSON.stringify(examplesThemePath)},
|
|
@@ -694,6 +698,42 @@ ${handler}
|
|
|
694
698
|
`;
|
|
695
699
|
};
|
|
696
700
|
|
|
701
|
+
/**
|
|
702
|
+
* Generate `.blume/src/generated/Ask.astro` — the component behind the
|
|
703
|
+
* `blume:ask` alias that the shared header renders in place of a per-page slot.
|
|
704
|
+
*
|
|
705
|
+
* The header can't import the Ask AI island directly: it's a React component, so
|
|
706
|
+
* the import alone would drag the JSX renderer into the module graph of every
|
|
707
|
+
* project — including the ones that never enable Ask AI and therefore have no
|
|
708
|
+
* React integration wired into their generated Astro config (see `needsReact`).
|
|
709
|
+
* Routing the import through a generated component keeps that dependency behind
|
|
710
|
+
* the config switch: enabled projects get the island, disabled ones get a
|
|
711
|
+
* component that renders nothing and imports no React.
|
|
712
|
+
*
|
|
713
|
+
* `strings` comes from the header (the active locale's dictionary); the empty-
|
|
714
|
+
* state suggestions are read straight from the data snapshot, which is why no
|
|
715
|
+
* page has to pass them.
|
|
716
|
+
*/
|
|
717
|
+
export const askComponentTemplate = (askEnabled: boolean): string =>
|
|
718
|
+
askEnabled
|
|
719
|
+
? `---
|
|
720
|
+
// Generated by Blume. Do not edit.
|
|
721
|
+
import AskAI from "blume/components/islands/AskAI.astro";
|
|
722
|
+
import data from "blume:data";
|
|
723
|
+
|
|
724
|
+
const { strings } = Astro.props;
|
|
725
|
+
---
|
|
726
|
+
|
|
727
|
+
<AskAI strings={strings ?? data.ui.ask} suggestions={data.config.ask?.suggestions ?? []} />
|
|
728
|
+
`
|
|
729
|
+
: `---
|
|
730
|
+
// Generated by Blume. Do not edit.
|
|
731
|
+
// Ask AI is off (\`ai.ask.enabled\`), so the header's Ask trigger renders nothing.
|
|
732
|
+
// Deliberately imports no React island, keeping the JSX renderer out of projects
|
|
733
|
+
// that don't need it.
|
|
734
|
+
---
|
|
735
|
+
`;
|
|
736
|
+
|
|
697
737
|
/** Generate the static search index endpoint (`/blume-search.json`). */
|
|
698
738
|
export const searchEndpointTemplate = (): string =>
|
|
699
739
|
`// Generated by Blume. Do not edit.
|
|
@@ -1069,19 +1109,12 @@ const htmlLang = i18n ? i18n.defaultLocale : "en";
|
|
|
1069
1109
|
`;
|
|
1070
1110
|
|
|
1071
1111
|
export const catchAllPageTemplate = (options: {
|
|
1072
|
-
askEnabled: boolean;
|
|
1073
1112
|
exportEpub: boolean;
|
|
1074
1113
|
exportPdf: boolean;
|
|
1075
1114
|
mathEnabled: boolean;
|
|
1076
1115
|
/** Serialize the island-hooks snapshot; only needed when React is enabled. */
|
|
1077
1116
|
needsReact: boolean;
|
|
1078
1117
|
}): string => {
|
|
1079
|
-
const askImport = options.askEnabled
|
|
1080
|
-
? 'import AskAI from "blume/components/islands/AskAI.astro";\n'
|
|
1081
|
-
: "";
|
|
1082
|
-
const askSlot = options.askEnabled
|
|
1083
|
-
? '\n <AskAI slot="ask" strings={ui.ask} suggestions={data.config.ask?.suggestions ?? []} />'
|
|
1084
|
-
: "";
|
|
1085
1118
|
const mathImport = options.mathEnabled
|
|
1086
1119
|
? 'import Math from "blume/components/content/Math.astro";\n'
|
|
1087
1120
|
: "";
|
|
@@ -1097,7 +1130,6 @@ import { getEntry, render } from "astro:content";
|
|
|
1097
1130
|
import RootLayout from "blume/components/layout/RootLayout.astro";
|
|
1098
1131
|
import { withBase } from "blume/components/islands/base-path.ts";
|
|
1099
1132
|
import { resolveSlot } from "blume/components/layout/overrides.ts";
|
|
1100
|
-
${askImport}
|
|
1101
1133
|
import Accordion from "blume/components/content/Accordion.astro";
|
|
1102
1134
|
import AccordionItem from "blume/components/content/AccordionItem.astro";
|
|
1103
1135
|
import AutoTypeTable from "blume/components/content/AutoTypeTable.astro";
|
|
@@ -1334,7 +1366,6 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
1334
1366
|
canonical={canonical}
|
|
1335
1367
|
editUrl={editUrl}
|
|
1336
1368
|
feedback={data.config.feedback}
|
|
1337
|
-
askEnabled={${options.askEnabled}}
|
|
1338
1369
|
exportPdf={${options.exportPdf}}
|
|
1339
1370
|
exportEpub={${options.exportEpub}}
|
|
1340
1371
|
feeds={data.feeds}
|
|
@@ -1344,7 +1375,7 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
1344
1375
|
lastModified={lastModified}
|
|
1345
1376
|
noindex={seo.noindex}
|
|
1346
1377
|
structuredDataEnabled={data.config.structuredData}
|
|
1347
|
-
|
|
1378
|
+
>
|
|
1348
1379
|
<h1>{title}</h1>
|
|
1349
1380
|
{frontmatter.description && <p class="text-lg text-muted-foreground">{frontmatter.description}</p>}
|
|
1350
1381
|
<Content components={components} />
|
|
@@ -1359,7 +1390,6 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
1359
1390
|
* by {@link generateAstroProject} when changelog entries exist.
|
|
1360
1391
|
*/
|
|
1361
1392
|
export const changelogIndexTemplate = (options: {
|
|
1362
|
-
askEnabled: boolean;
|
|
1363
1393
|
exportEpub: boolean;
|
|
1364
1394
|
exportPdf: boolean;
|
|
1365
1395
|
/** Serialize the island-hooks snapshot; only needed when React is enabled. */
|
|
@@ -1367,12 +1397,6 @@ export const changelogIndexTemplate = (options: {
|
|
|
1367
1397
|
/** Whether a `staged` collection exists (non-filesystem changelog sources). */
|
|
1368
1398
|
staged: boolean;
|
|
1369
1399
|
}): string => {
|
|
1370
|
-
const askImport = options.askEnabled
|
|
1371
|
-
? 'import AskAI from "blume/components/islands/AskAI.astro";\n'
|
|
1372
|
-
: "";
|
|
1373
|
-
const askSlot = options.askEnabled
|
|
1374
|
-
? '\n <AskAI slot="ask" strings={data.ui.ask} suggestions={data.config.ask?.suggestions ?? []} />'
|
|
1375
|
-
: "";
|
|
1376
1400
|
const clientData = options.needsReact
|
|
1377
1401
|
? '\n clientData={{ config: data.config, navigation: data.navigation, page: { route: "/changelog", title: pageTitle } }}'
|
|
1378
1402
|
: "";
|
|
@@ -1390,7 +1414,7 @@ import Update from "blume/components/content/Update.astro";
|
|
|
1390
1414
|
import { withBase } from "blume/components/islands/base-path.ts";
|
|
1391
1415
|
import { resolveSlot } from "blume/components/layout/overrides.ts";
|
|
1392
1416
|
import { layoutOverrides } from "../generated/components.ts";
|
|
1393
|
-
|
|
1417
|
+
import data from "../generated/data.json";
|
|
1394
1418
|
|
|
1395
1419
|
export const prerender = true;
|
|
1396
1420
|
|
|
@@ -1559,14 +1583,13 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
|
|
|
1559
1583
|
ogImage={null}
|
|
1560
1584
|
x={data.config.x}
|
|
1561
1585
|
canonical={canonical}
|
|
1562
|
-
askEnabled={${options.askEnabled}}
|
|
1563
1586
|
exportPdf={${options.exportPdf}}
|
|
1564
1587
|
exportEpub={${options.exportEpub}}
|
|
1565
1588
|
feeds={data.feeds}
|
|
1566
1589
|
siteUrl={data.config.site}
|
|
1567
1590
|
noindex={false}
|
|
1568
1591
|
structuredDataEnabled={data.config.structuredData}
|
|
1569
|
-
|
|
1592
|
+
>
|
|
1570
1593
|
<h1>{changelogTitle}</h1>
|
|
1571
1594
|
{
|
|
1572
1595
|
items.length === 0 ? (
|
|
@@ -1891,6 +1914,11 @@ export const envTemplate =
|
|
|
1891
1914
|
(): string => `/// <reference path="../.astro/types.d.ts" />
|
|
1892
1915
|
/// <reference types="astro/client" />
|
|
1893
1916
|
|
|
1917
|
+
declare module "blume:ask" {
|
|
1918
|
+
const Ask: typeof import("blume/components/islands/AskAI.astro").default;
|
|
1919
|
+
export default Ask;
|
|
1920
|
+
}
|
|
1921
|
+
|
|
1894
1922
|
declare module "blume:data" {
|
|
1895
1923
|
const data: import("blume").BlumeData;
|
|
1896
1924
|
export default data;
|