blume 0.4.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/cli/index.js +1137 -722
  2. package/dist/cli/index.js.map +28 -23
  3. package/dist/types/core/data.d.ts +2 -0
  4. package/dist/types/core/project.d.ts +12 -2
  5. package/dist/types/core/schema.d.ts +154 -15
  6. package/dist/types/core/types.d.ts +7 -0
  7. package/docs/advanced/api-reference.mdx +33 -23
  8. package/docs/advanced/bridge.mdx +74 -0
  9. package/docs/advanced/meta.ts +8 -1
  10. package/docs/advanced/migrate.mdx +119 -0
  11. package/docs/configuration/index.mdx +1 -1
  12. package/docs/content/components.mdx +55 -2
  13. package/docs/content/i18n.mdx +1 -1
  14. package/docs/content/syntax.mdx +2 -2
  15. package/docs/index.mdx +2 -2
  16. package/docs/reference/cli.mdx +29 -1
  17. package/docs/reference/frontmatter.mdx +5 -0
  18. package/package.json +11 -1
  19. package/src/astro/generate.ts +18 -8
  20. package/src/astro/templates.ts +28 -4
  21. package/src/cli/commands/build.ts +107 -63
  22. package/src/cli/commands/check.ts +20 -0
  23. package/src/cli/dev-lock.ts +13 -5
  24. package/src/cli/prepare.ts +3 -0
  25. package/src/components/BlumePage.astro +6 -0
  26. package/src/components/Icon.astro +13 -10
  27. package/src/components/content/ApiField.astro +75 -0
  28. package/src/components/content/ParamField.astro +39 -0
  29. package/src/components/content/RequestField.astro +23 -0
  30. package/src/components/content/ResponseField.astro +23 -0
  31. package/src/components/content/Step.astro +1 -1
  32. package/src/components/layout/Breadcrumbs.astro +7 -2
  33. package/src/components/layout/NavTree.astro +24 -8
  34. package/src/components/layout/RootLayout.astro +56 -34
  35. package/src/components/layout/Search.astro +1 -1
  36. package/src/components/openapi/ApiOverview.astro +84 -0
  37. package/src/components/openapi/MethodBadge.astro +28 -0
  38. package/src/components/openapi/Operation.astro +140 -0
  39. package/src/components/openapi/ParametersTable.astro +97 -0
  40. package/src/components/openapi/RequestBody.astro +58 -0
  41. package/src/components/openapi/RequestPanel.astro +169 -0
  42. package/src/components/openapi/Responses.astro +91 -0
  43. package/src/components/openapi/SchemaProperty.astro +118 -0
  44. package/src/components/openapi/SchemaTable.astro +86 -0
  45. package/src/components/openapi/helpers.ts +238 -0
  46. package/src/components/openapi/panel.ts +59 -0
  47. package/src/components/openapi/snippets.ts +201 -0
  48. package/src/core/builtin-tags.ts +5 -0
  49. package/src/core/data.ts +2 -0
  50. package/src/core/project-graph.ts +5 -1
  51. package/src/core/project.ts +25 -3
  52. package/src/core/schema.ts +47 -6
  53. package/src/core/sources/mintlify.ts +1 -1
  54. package/src/core/sources/resolve.ts +28 -6
  55. package/src/core/types.ts +7 -0
  56. package/src/migrate/mintlify/config.ts +153 -1
  57. package/src/migrate/mintlify/content.ts +8 -2
  58. package/src/migrate/mintlify/index.ts +58 -1
  59. package/src/openapi/model.ts +174 -0
  60. package/src/openapi/parse.ts +48 -0
  61. package/src/openapi/references.ts +164 -0
  62. package/src/openapi/render-mdx.ts +76 -0
  63. package/src/openapi/scalar.ts +15 -103
  64. package/src/openapi/source.ts +140 -0
  65. package/src/registry/eject.ts +15 -2
  66. package/src/theme/chrome-icons.ts +22 -0
  67. package/src/theme/icons.ts +151 -161
@@ -87,6 +87,8 @@ export interface BlumeDataConfig {
87
87
  favicon: BlumeFavicon;
88
88
  feedback: boolean;
89
89
  i18n: BlumeDataI18n | null;
90
+ /** Default icon library for bare `icon` names. */
91
+ icons: ResolvedConfig["icons"];
90
92
  /** `markdown.imageZoom`: click-to-zoom content images. */
91
93
  imageZoom: boolean;
92
94
  logo: BlumeLogo | null;
@@ -2,8 +2,18 @@ import type { ResolvedConfig } from "./schema.ts";
2
2
  import type { ProjectContext } from "./types.ts";
3
3
  /** Locate the Blume config file for a project root, if any. */
4
4
  export declare const findConfigFile: (root: string) => string | null;
5
+ /**
6
+ * Resolve the generated runtime directory for a project. Defaults to
7
+ * `<root>/.blume`; an override (e.g. `.blume-verify` for an isolated build that
8
+ * runs alongside a live `blume dev`) may be relative to the root or absolute.
9
+ */
10
+ export declare const resolveRuntimeDir: (root: string, runtimeDir?: string) => string;
5
11
  /**
6
12
  * Resolve every path Blume needs from a project root and its resolved config.
7
- * Paths are absolute and normalized.
13
+ * Paths are absolute and normalized. `options.runtimeDir` relocates the whole
14
+ * generated runtime (and its build output) so a verify build/check can run
15
+ * without touching a live dev server's `.blume/` or the real `dist/`.
8
16
  */
9
- export declare const resolveProjectContext: (root: string, config: ResolvedConfig) => ProjectContext;
17
+ export declare const resolveProjectContext: (root: string, config: ResolvedConfig, options?: {
18
+ runtimeDir?: string;
19
+ }) => ProjectContext;
@@ -4,6 +4,38 @@ declare const hydrationMode: z.ZodEnum<["load", "idle", "visible", "media", "onl
4
4
  export type HydrationMode = z.infer<typeof hydrationMode>;
5
5
  /** Frontmatter accepted on any content page. */
6
6
  declare const pageMetaBaseSchema: z.ZodObject<{
7
+ /** Post author(s) for blog/changelog content; preserved, not yet rendered. */
8
+ authors: z.ZodOptional<z.ZodUnion<[z.ZodUnion<[z.ZodString, z.ZodObject<{
9
+ avatar: z.ZodOptional<z.ZodString>;
10
+ image: z.ZodOptional<z.ZodString>;
11
+ name: z.ZodString;
12
+ url: z.ZodOptional<z.ZodString>;
13
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
14
+ avatar: z.ZodOptional<z.ZodString>;
15
+ image: z.ZodOptional<z.ZodString>;
16
+ name: z.ZodString;
17
+ url: z.ZodOptional<z.ZodString>;
18
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
19
+ avatar: z.ZodOptional<z.ZodString>;
20
+ image: z.ZodOptional<z.ZodString>;
21
+ name: z.ZodString;
22
+ url: z.ZodOptional<z.ZodString>;
23
+ }, z.ZodTypeAny, "passthrough">>]>, z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodObject<{
24
+ avatar: z.ZodOptional<z.ZodString>;
25
+ image: z.ZodOptional<z.ZodString>;
26
+ name: z.ZodString;
27
+ url: z.ZodOptional<z.ZodString>;
28
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
29
+ avatar: z.ZodOptional<z.ZodString>;
30
+ image: z.ZodOptional<z.ZodString>;
31
+ name: z.ZodString;
32
+ url: z.ZodOptional<z.ZodString>;
33
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
34
+ avatar: z.ZodOptional<z.ZodString>;
35
+ image: z.ZodOptional<z.ZodString>;
36
+ name: z.ZodString;
37
+ url: z.ZodOptional<z.ZodString>;
38
+ }, z.ZodTypeAny, "passthrough">>]>, "many">]>>;
7
39
  changelog: z.ZodOptional<z.ZodObject<{
8
40
  category: z.ZodOptional<z.ZodString>;
9
41
  date: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodDate]>, string, string | Date>>;
@@ -58,14 +90,14 @@ declare const pageMetaBaseSchema: z.ZodObject<{
58
90
  noindex: boolean;
59
91
  title?: string | undefined;
60
92
  description?: string | undefined;
61
- canonical?: string | undefined;
62
93
  image?: string | undefined;
94
+ canonical?: string | undefined;
63
95
  }, {
64
96
  title?: string | undefined;
65
97
  description?: string | undefined;
98
+ image?: string | undefined;
66
99
  noindex?: boolean | undefined;
67
100
  canonical?: string | undefined;
68
- image?: string | undefined;
69
101
  }>>;
70
102
  sidebar: z.ZodDefault<z.ZodObject<{
71
103
  badge: z.ZodOptional<z.ZodString>;
@@ -107,8 +139,8 @@ declare const pageMetaBaseSchema: z.ZodObject<{
107
139
  noindex: boolean;
108
140
  title?: string | undefined;
109
141
  description?: string | undefined;
110
- canonical?: string | undefined;
111
142
  image?: string | undefined;
143
+ canonical?: string | undefined;
112
144
  };
113
145
  sidebar: {
114
146
  hidden: boolean;
@@ -120,6 +152,17 @@ declare const pageMetaBaseSchema: z.ZodObject<{
120
152
  date?: string | undefined;
121
153
  title?: string | undefined;
122
154
  description?: string | undefined;
155
+ authors?: string | z.objectOutputType<{
156
+ avatar: z.ZodOptional<z.ZodString>;
157
+ image: z.ZodOptional<z.ZodString>;
158
+ name: z.ZodString;
159
+ url: z.ZodOptional<z.ZodString>;
160
+ }, z.ZodTypeAny, "passthrough"> | (string | z.objectOutputType<{
161
+ avatar: z.ZodOptional<z.ZodString>;
162
+ image: z.ZodOptional<z.ZodString>;
163
+ name: z.ZodString;
164
+ url: z.ZodOptional<z.ZodString>;
165
+ }, z.ZodTypeAny, "passthrough">)[] | undefined;
123
166
  changelog?: {
124
167
  date?: string | undefined;
125
168
  category?: string | undefined;
@@ -147,6 +190,17 @@ declare const pageMetaBaseSchema: z.ZodObject<{
147
190
  tags?: string[] | undefined;
148
191
  } | undefined;
149
192
  description?: string | undefined;
193
+ authors?: string | z.objectInputType<{
194
+ avatar: z.ZodOptional<z.ZodString>;
195
+ image: z.ZodOptional<z.ZodString>;
196
+ name: z.ZodString;
197
+ url: z.ZodOptional<z.ZodString>;
198
+ }, z.ZodTypeAny, "passthrough"> | (string | z.objectInputType<{
199
+ avatar: z.ZodOptional<z.ZodString>;
200
+ image: z.ZodOptional<z.ZodString>;
201
+ name: z.ZodString;
202
+ url: z.ZodOptional<z.ZodString>;
203
+ }, z.ZodTypeAny, "passthrough">)[] | undefined;
150
204
  changelog?: {
151
205
  date?: string | Date | undefined;
152
206
  category?: string | undefined;
@@ -169,9 +223,9 @@ declare const pageMetaBaseSchema: z.ZodObject<{
169
223
  seo?: {
170
224
  title?: string | undefined;
171
225
  description?: string | undefined;
226
+ image?: string | undefined;
172
227
  noindex?: boolean | undefined;
173
228
  canonical?: string | undefined;
174
- image?: string | undefined;
175
229
  } | undefined;
176
230
  sidebar?: {
177
231
  label?: string | undefined;
@@ -185,6 +239,38 @@ declare const pageMetaBaseSchema: z.ZodObject<{
185
239
  tag?: string | undefined;
186
240
  }>;
187
241
  export declare const pageMetaSchema: z.ZodObject<{
242
+ /** Post author(s) for blog/changelog content; preserved, not yet rendered. */
243
+ authors: z.ZodOptional<z.ZodUnion<[z.ZodUnion<[z.ZodString, z.ZodObject<{
244
+ avatar: z.ZodOptional<z.ZodString>;
245
+ image: z.ZodOptional<z.ZodString>;
246
+ name: z.ZodString;
247
+ url: z.ZodOptional<z.ZodString>;
248
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
249
+ avatar: z.ZodOptional<z.ZodString>;
250
+ image: z.ZodOptional<z.ZodString>;
251
+ name: z.ZodString;
252
+ url: z.ZodOptional<z.ZodString>;
253
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
254
+ avatar: z.ZodOptional<z.ZodString>;
255
+ image: z.ZodOptional<z.ZodString>;
256
+ name: z.ZodString;
257
+ url: z.ZodOptional<z.ZodString>;
258
+ }, z.ZodTypeAny, "passthrough">>]>, z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodObject<{
259
+ avatar: z.ZodOptional<z.ZodString>;
260
+ image: z.ZodOptional<z.ZodString>;
261
+ name: z.ZodString;
262
+ url: z.ZodOptional<z.ZodString>;
263
+ }, "passthrough", z.ZodTypeAny, z.objectOutputType<{
264
+ avatar: z.ZodOptional<z.ZodString>;
265
+ image: z.ZodOptional<z.ZodString>;
266
+ name: z.ZodString;
267
+ url: z.ZodOptional<z.ZodString>;
268
+ }, z.ZodTypeAny, "passthrough">, z.objectInputType<{
269
+ avatar: z.ZodOptional<z.ZodString>;
270
+ image: z.ZodOptional<z.ZodString>;
271
+ name: z.ZodString;
272
+ url: z.ZodOptional<z.ZodString>;
273
+ }, z.ZodTypeAny, "passthrough">>]>, "many">]>>;
188
274
  changelog: z.ZodOptional<z.ZodObject<{
189
275
  category: z.ZodOptional<z.ZodString>;
190
276
  date: z.ZodOptional<z.ZodEffects<z.ZodUnion<[z.ZodString, z.ZodDate]>, string, string | Date>>;
@@ -239,14 +325,14 @@ export declare const pageMetaSchema: z.ZodObject<{
239
325
  noindex: boolean;
240
326
  title?: string | undefined;
241
327
  description?: string | undefined;
242
- canonical?: string | undefined;
243
328
  image?: string | undefined;
329
+ canonical?: string | undefined;
244
330
  }, {
245
331
  title?: string | undefined;
246
332
  description?: string | undefined;
333
+ image?: string | undefined;
247
334
  noindex?: boolean | undefined;
248
335
  canonical?: string | undefined;
249
- image?: string | undefined;
250
336
  }>>;
251
337
  sidebar: z.ZodDefault<z.ZodObject<{
252
338
  badge: z.ZodOptional<z.ZodString>;
@@ -288,8 +374,8 @@ export declare const pageMetaSchema: z.ZodObject<{
288
374
  noindex: boolean;
289
375
  title?: string | undefined;
290
376
  description?: string | undefined;
291
- canonical?: string | undefined;
292
377
  image?: string | undefined;
378
+ canonical?: string | undefined;
293
379
  };
294
380
  sidebar: {
295
381
  hidden: boolean;
@@ -301,6 +387,17 @@ export declare const pageMetaSchema: z.ZodObject<{
301
387
  date?: string | undefined;
302
388
  title?: string | undefined;
303
389
  description?: string | undefined;
390
+ authors?: string | z.objectOutputType<{
391
+ avatar: z.ZodOptional<z.ZodString>;
392
+ image: z.ZodOptional<z.ZodString>;
393
+ name: z.ZodString;
394
+ url: z.ZodOptional<z.ZodString>;
395
+ }, z.ZodTypeAny, "passthrough"> | (string | z.objectOutputType<{
396
+ avatar: z.ZodOptional<z.ZodString>;
397
+ image: z.ZodOptional<z.ZodString>;
398
+ name: z.ZodString;
399
+ url: z.ZodOptional<z.ZodString>;
400
+ }, z.ZodTypeAny, "passthrough">)[] | undefined;
304
401
  changelog?: {
305
402
  date?: string | undefined;
306
403
  category?: string | undefined;
@@ -328,6 +425,17 @@ export declare const pageMetaSchema: z.ZodObject<{
328
425
  tags?: string[] | undefined;
329
426
  } | undefined;
330
427
  description?: string | undefined;
428
+ authors?: string | z.objectInputType<{
429
+ avatar: z.ZodOptional<z.ZodString>;
430
+ image: z.ZodOptional<z.ZodString>;
431
+ name: z.ZodString;
432
+ url: z.ZodOptional<z.ZodString>;
433
+ }, z.ZodTypeAny, "passthrough"> | (string | z.objectInputType<{
434
+ avatar: z.ZodOptional<z.ZodString>;
435
+ image: z.ZodOptional<z.ZodString>;
436
+ name: z.ZodString;
437
+ url: z.ZodOptional<z.ZodString>;
438
+ }, z.ZodTypeAny, "passthrough">)[] | undefined;
331
439
  changelog?: {
332
440
  date?: string | Date | undefined;
333
441
  category?: string | undefined;
@@ -350,9 +458,9 @@ export declare const pageMetaSchema: z.ZodObject<{
350
458
  seo?: {
351
459
  title?: string | undefined;
352
460
  description?: string | undefined;
461
+ image?: string | undefined;
353
462
  noindex?: boolean | undefined;
354
463
  canonical?: string | undefined;
355
- image?: string | undefined;
356
464
  } | undefined;
357
465
  sidebar?: {
358
466
  label?: string | undefined;
@@ -827,8 +935,8 @@ declare const i18nConfigSchema: z.ZodEffects<z.ZodObject<{
827
935
  ui?: Record<string, Record<string, Record<string, string>>> | undefined;
828
936
  }>;
829
937
  /**
830
- * A single spec rendered by the API reference (Scalar). `spec` is a local path
831
- * or an `http(s)` URL; Scalar auto-detects OpenAPI vs AsyncAPI documents.
938
+ * A single spec rendered by the API reference. `spec` is a local path or an
939
+ * `http(s)` URL (OpenAPI for the Blume renderer; OpenAPI or AsyncAPI for Scalar).
832
940
  */
833
941
  declare const openapiSourceSchema: z.ZodObject<{
834
942
  /** Nav/section label for this source. */
@@ -1643,6 +1751,13 @@ export declare const blumeConfigSchema: z.ZodObject<{
1643
1751
  parser?: "dir" | "dot" | undefined;
1644
1752
  ui?: Record<string, Record<string, Record<string, string>>> | undefined;
1645
1753
  }>>;
1754
+ icons: z.ZodDefault<z.ZodObject<{
1755
+ library: z.ZodDefault<z.ZodEnum<["lucide", "fontawesome", "tabler"]>>;
1756
+ }, "strict", z.ZodTypeAny, {
1757
+ library: "lucide" | "fontawesome" | "tabler";
1758
+ }, {
1759
+ library?: "lucide" | "fontawesome" | "tabler" | undefined;
1760
+ }>>;
1646
1761
  lastModified: z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodObject<{
1647
1762
  type: z.ZodDefault<z.ZodEnum<["git", "frontmatter"]>>;
1648
1763
  }, "strict", z.ZodTypeAny, {
@@ -1773,13 +1888,13 @@ export declare const blumeConfigSchema: z.ZodObject<{
1773
1888
  }, "strict", z.ZodTypeAny, {
1774
1889
  enabled: boolean;
1775
1890
  route: string;
1776
- instructions?: string | undefined;
1777
1891
  name?: string | undefined;
1892
+ instructions?: string | undefined;
1778
1893
  }, {
1894
+ name?: string | undefined;
1779
1895
  enabled?: boolean | undefined;
1780
1896
  route?: string | undefined;
1781
1897
  instructions?: string | undefined;
1782
- name?: string | undefined;
1783
1898
  }>>;
1784
1899
  navigation: z.ZodDefault<z.ZodObject<{
1785
1900
  chromeVariants: z.ZodDefault<z.ZodArray<z.ZodObject<{
@@ -2078,7 +2193,13 @@ export declare const blumeConfigSchema: z.ZodObject<{
2078
2193
  }[] | undefined;
2079
2194
  }>>;
2080
2195
  openapi: z.ZodDefault<z.ZodObject<{
2196
+ /** Code-sample languages shown per operation (Blume renderer). */
2197
+ codeSamples: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
2081
2198
  enabled: z.ZodDefault<z.ZodBoolean>;
2199
+ /** Start nested schema rows expanded rather than collapsed (Blume renderer). */
2200
+ expandSchemas: z.ZodDefault<z.ZodBoolean>;
2201
+ /** Who renders the reference: Blume's own UI, or the embedded Scalar SPA. */
2202
+ renderer: z.ZodDefault<z.ZodEnum<["blume", "scalar"]>>;
2082
2203
  /** Where the reference mounts. */
2083
2204
  route: z.ZodDefault<z.ZodString>;
2084
2205
  /** One or more specs; each renders on its own route by default. */
@@ -2100,7 +2221,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
2100
2221
  }>, "many">>;
2101
2222
  /** Shorthand for a single source: `sources: [{ spec }]`. */
2102
2223
  spec: z.ZodOptional<z.ZodString>;
2103
- /** Scalar theme name; defaults to a Blume-derived accent override. */
2224
+ /** Scalar theme name (Scalar renderer only). */
2104
2225
  theme: z.ZodOptional<z.ZodString>;
2105
2226
  }, "strict", z.ZodTypeAny, {
2106
2227
  enabled: boolean;
@@ -2110,6 +2231,9 @@ export declare const blumeConfigSchema: z.ZodObject<{
2110
2231
  label?: string | undefined;
2111
2232
  route?: string | undefined;
2112
2233
  }[];
2234
+ codeSamples: string[];
2235
+ expandSchemas: boolean;
2236
+ renderer: "blume" | "scalar";
2113
2237
  spec?: string | undefined;
2114
2238
  theme?: string | undefined;
2115
2239
  }, {
@@ -2122,6 +2246,9 @@ export declare const blumeConfigSchema: z.ZodObject<{
2122
2246
  route?: string | undefined;
2123
2247
  }[] | undefined;
2124
2248
  theme?: string | undefined;
2249
+ codeSamples?: string[] | undefined;
2250
+ expandSchemas?: boolean | undefined;
2251
+ renderer?: "blume" | "scalar" | undefined;
2125
2252
  }>>;
2126
2253
  redirects: z.ZodDefault<z.ZodArray<z.ZodObject<{
2127
2254
  from: z.ZodString;
@@ -2629,6 +2756,9 @@ export declare const blumeConfigSchema: z.ZodObject<{
2629
2756
  epub: boolean;
2630
2757
  pdf: boolean;
2631
2758
  };
2759
+ icons: {
2760
+ library: "lucide" | "fontawesome" | "tabler";
2761
+ };
2632
2762
  markdown: {
2633
2763
  code: {
2634
2764
  icons: boolean;
@@ -2648,8 +2778,8 @@ export declare const blumeConfigSchema: z.ZodObject<{
2648
2778
  mcp: {
2649
2779
  enabled: boolean;
2650
2780
  route: string;
2651
- instructions?: string | undefined;
2652
2781
  name?: string | undefined;
2782
+ instructions?: string | undefined;
2653
2783
  };
2654
2784
  navigation: {
2655
2785
  repo: boolean;
@@ -2707,6 +2837,9 @@ export declare const blumeConfigSchema: z.ZodObject<{
2707
2837
  label?: string | undefined;
2708
2838
  route?: string | undefined;
2709
2839
  }[];
2840
+ codeSamples: string[];
2841
+ expandSchemas: boolean;
2842
+ renderer: "blume" | "scalar";
2710
2843
  spec?: string | undefined;
2711
2844
  theme?: string | undefined;
2712
2845
  };
@@ -3000,6 +3133,9 @@ export declare const blumeConfigSchema: z.ZodObject<{
3000
3133
  parser?: "dir" | "dot" | undefined;
3001
3134
  ui?: Record<string, Record<string, Record<string, string>>> | undefined;
3002
3135
  } | undefined;
3136
+ icons?: {
3137
+ library?: "lucide" | "fontawesome" | "tabler" | undefined;
3138
+ } | undefined;
3003
3139
  logo?: string | {
3004
3140
  dark?: string | undefined;
3005
3141
  light?: string | undefined;
@@ -3023,10 +3159,10 @@ export declare const blumeConfigSchema: z.ZodObject<{
3023
3159
  math?: boolean | undefined;
3024
3160
  } | undefined;
3025
3161
  mcp?: {
3162
+ name?: string | undefined;
3026
3163
  enabled?: boolean | undefined;
3027
3164
  route?: string | undefined;
3028
3165
  instructions?: string | undefined;
3029
- name?: string | undefined;
3030
3166
  } | undefined;
3031
3167
  navigation?: {
3032
3168
  sidebar?: SidebarItemConfig[] | undefined;
@@ -3086,6 +3222,9 @@ export declare const blumeConfigSchema: z.ZodObject<{
3086
3222
  route?: string | undefined;
3087
3223
  }[] | undefined;
3088
3224
  theme?: string | undefined;
3225
+ codeSamples?: string[] | undefined;
3226
+ expandSchemas?: boolean | undefined;
3227
+ renderer?: "blume" | "scalar" | undefined;
3089
3228
  } | undefined;
3090
3229
  redirects?: {
3091
3230
  from: string;
@@ -44,6 +44,13 @@ export interface ProjectContext {
44
44
  pagesRoot: string | null;
45
45
  /** Absolute path to the generated runtime (`<root>/.blume`). */
46
46
  outDir: string;
47
+ /**
48
+ * Absolute path to the Astro build output. `<root>/dist` normally; for a
49
+ * relocated runtime (isolated verify build) it lives under the runtime dir so
50
+ * it never empties the real `dist/`. Optional so hand-built test contexts and
51
+ * older callers still typecheck; `resolveProjectContext` always sets it.
52
+ */
53
+ distDir?: string;
47
54
  /** Absolute path to the user `theme.css`, if present. */
48
55
  themeFile: string | null;
49
56
  /** Absolute path to the user `components.ts`/`.tsx`, if present. */
@@ -1,9 +1,9 @@
1
1
  ---
2
2
  title: OpenAPI / AsyncAPI
3
- description: Drop in an OpenAPI or AsyncAPI spec and get an interactive reference, rendered by Scalar.
3
+ description: Drop in an OpenAPI spec and get a native API reference — one real page per operation, in your sidebar and search.
4
4
  ---
5
5
 
6
- Point Blume at an OpenAPI or AsyncAPI spec and it generates an interactive API reference schemas, authentication, examples, and a request playground on its own route. The reference is rendered by [Scalar](https://scalar.com), not by Blume: API reference isn't core to Blume, so the whole surface is delegated to a tool that has already solved its large edge-case space.
6
+ Point Blume at an OpenAPI spec and it generates a native API reference: one **real page per operation**, grouped by tag in a tab-scoped sidebar, with schema tables, request/response examples, and generated code samples. Because each operation is a genuine Blume page, it gets its own URL, shows up in **site search** and `llms.txt`, and gets an Open Graph image the same as any hand-written doc.
7
7
 
8
8
  ```ts blume.config.ts lineNumbers
9
9
  openapi: {
@@ -12,13 +12,13 @@ openapi: {
12
12
  }
13
13
  ```
14
14
 
15
- That mounts the reference at `/reference` and adds a header link to it. The `spec` is either an `http(s)` URL or a path to a local file in your project.
15
+ That mounts the reference at `/reference` (an overview page) with each operation at `/reference/<tag>/<operation>`, and adds a header tab. The `spec` is either an `http(s)` URL or a path to a local file in your project. Blume parses it with [Scalar's OpenAPI parser](https://github.com/scalar/scalar) — Swagger 2.0 and OpenAPI 3.0 specs are upgraded to 3.1 automatically.
16
16
 
17
- :::note The reference mounts inside Blume's shell your navbar stays on top but the body is a **self-contained Scalar embed** with its own sidebar, search, and theme. By design it doesn't weave into Blume's content sidebar, site search, or `llms.txt`. :::
17
+ :::note Operations are indexed for search by their **summary and tag**. The rendered schema tables and code samples aren't full-text indexed; search matches an operation's title and section, then links to its own page. :::
18
18
 
19
19
  ## A local spec
20
20
 
21
- A relative path is resolved from your project root and inlined at build time, so nothing is copied into your source tree and the page works offline:
21
+ A relative path is resolved from your project root and read at build time. Both JSON and YAML work:
22
22
 
23
23
  ```ts blume.config.ts lineNumbers
24
24
  openapi: {
@@ -27,23 +27,34 @@ openapi: {
27
27
  }
28
28
  ```
29
29
 
30
- Both JSON and YAML specs work; Scalar auto-detects the format.
31
-
32
30
  ## Route
33
31
 
34
- `route` controls where the reference mounts (and the header link's target):
32
+ `route` controls where the reference mounts — the overview page and the prefix for every operation route (and the header tab's target):
33
+
34
+ ```ts blume.config.ts lineNumbers
35
+ openapi: {
36
+ enabled: true,
37
+ route: "/api", // overview at /api, operations at /api/<tag>/<operation>
38
+ spec: "./openapi.yaml",
39
+ }
40
+ ```
41
+
42
+ ## Code samples and schemas
43
+
44
+ `codeSamples` picks which languages render per operation (built in: `curl`, `js`, `python`); `expandSchemas` starts nested schema rows expanded rather than collapsed:
35
45
 
36
46
  ```ts blume.config.ts lineNumbers
37
47
  openapi: {
38
48
  enabled: true,
39
- route: "/api",
40
49
  spec: "./openapi.yaml",
50
+ codeSamples: ["curl", "js"],
51
+ expandSchemas: true,
41
52
  }
42
53
  ```
43
54
 
44
55
  ## Multiple specs
45
56
 
46
- Use `sources` to publish more than one spec. Each source gets its own route and header link. Give each a `label` (used for the link and to derive its route), or set an explicit `route`:
57
+ Use `sources` to publish more than one spec. Each source gets its own overview route, operation pages, and header tab. Give each a `label` (used for the tab and to derive its route), or set an explicit `route`:
47
58
 
48
59
  ```ts blume.config.ts lineNumbers
49
60
  openapi: {
@@ -57,31 +68,30 @@ openapi: {
57
68
 
58
69
  `spec` is shorthand for a single-entry `sources`, so you only reach for `sources` when you have more than one.
59
70
 
60
- ## AsyncAPI
71
+ ## The Scalar renderer
61
72
 
62
- Event-driven APIs use a sibling `asyncapi` block with the **same shape**Scalar renders AsyncAPI through the same component. Only the default route differs (`/events`):
73
+ The native renderer is the default. If you'd rather embed [Scalar](https://scalar.com)'s self-contained API reference its own sidebar, search, theme, and "Try it" playground on a single route set `renderer: "scalar"`:
63
74
 
64
75
  ```ts blume.config.ts lineNumbers
65
- asyncapi: {
76
+ openapi: {
66
77
  enabled: true,
67
- spec: "./asyncapi.yaml",
78
+ renderer: "scalar",
79
+ spec: "./openapi.yaml",
80
+ theme: "purple", // a Scalar theme name (Scalar renderer only)
68
81
  }
69
82
  ```
70
83
 
71
- :::warning Scalar's AsyncAPI support is still a work in progress — it renders channels, operations, messages, and a Models section, but there's **no interactive playground** for events yet. It improves upstream over time. :::
84
+ A Scalar-rendered reference is a self-contained embed on its own route — it doesn't weave into Blume's sidebar, search, or `llms.txt`. Its "Try it" playground calls your **target API directly from the browser** (Blume doesn't proxy), so the API must allow cross-origin requests from the docs site (`Access-Control-Allow-Origin`). `theme` and the playground apply to the Scalar renderer only.
72
85
 
73
- ## Theming
86
+ ## AsyncAPI
74
87
 
75
- By default Blume layers your [accent and radius](/docs/configuration/theming) onto Scalar's theme so the reference doesn't feel jarring. It's best-effort, not pixel-identical Scalar carries its own design system. To pick a specific [Scalar theme](https://github.com/scalar/scalar) instead, set `theme`:
88
+ Event-driven APIs use a sibling `asyncapi` block with the same shape. AsyncAPI is rendered by Scalar (the native renderer is OpenAPI-only for now); only the default route differs (`/events`):
76
89
 
77
90
  ```ts blume.config.ts lineNumbers
78
- openapi: {
91
+ asyncapi: {
79
92
  enabled: true,
80
- spec: "./openapi.yaml",
81
- theme: "purple",
93
+ spec: "./asyncapi.yaml",
82
94
  }
83
95
  ```
84
96
 
85
- ## The playground and CORS
86
-
87
- Scalar's "Try it" playground calls your **target API directly from the browser** — Blume doesn't proxy the request. For it to succeed, the API must allow cross-origin requests from the docs site (send the appropriate `Access-Control-Allow-Origin` headers). This is a property of the API being documented, not of Blume.
97
+ :::warning Scalar's AsyncAPI support is still a work in progress — it renders channels, operations, messages, and a Models section, but there's **no interactive playground** for events yet. It improves upstream over time. :::
@@ -0,0 +1,74 @@
1
+ ---
2
+ title: Bridge
3
+ description: Run blume dev directly on a Mintlify codebase — no config, no migration, no file changes. Blume detects docs.json and serves it in place.
4
+ ---
5
+
6
+ Bridge mode lets you point Blume at a **Mintlify** project and run it as-is. There's no config to write and no migration to commit: drop `blume dev` into a directory that has a `docs.json` (or legacy `mint.json`) and Blume detects it, synthesizes an equivalent config in memory, and serves your existing MDX — transformed to Blume idiom on the fly, on disk untouched.
7
+
8
+ ```bash
9
+ cd my-mintlify-docs
10
+ npx blume dev
11
+ ```
12
+
13
+ ```txt
14
+ ℹ Detected docs.json — running in Mintlify bridge mode (no migration).
15
+ Run "blume migrate mintlify" to convert permanently.
16
+ ```
17
+
18
+ It's the zero-commitment way to see what your docs look like on Blume — you can keep running `mintlify dev` in one terminal and `blume dev` in another, side by side, and decide later.
19
+
20
+ ## When it activates
21
+
22
+ Bridge mode is entirely automatic, gated on two conditions:
23
+
24
+ - **No Blume config.** There's no `blume.config.{ts,js,mjs}` at the project root. An explicit Blume config always wins — the moment one exists, Blume runs a normal project and bridge mode never triggers.
25
+ - **A Mintlify config is present.** A `docs.json` or `mint.json` sits at the root.
26
+
27
+ With both true, Blume treats the Mintlify config as its source of truth for that run.
28
+
29
+ :::note Bridge mode applies to `blume build` too, not just `blume dev` — you can produce a static site from an unconverted Mintlify project. Only `blume dev` prints the detection notice. :::
30
+
31
+ ## What happens under the hood
32
+
33
+ Bridge mode is the **read-only twin** of [`blume migrate mintlify`](/docs/advanced/migrate#mintlify): the same translation, without writing anything back.
34
+
35
+ - **Config is synthesized in memory.** `docs.json` is translated to a Blume config — navigation, theme, and chrome mapped across — exactly as the migrator would, but nothing is written to disk.
36
+ - **Content is served through a Mintlify source.** The config's content block is rewired to a single [`mintlify` content source](/docs/content/sources) rooted at the project. Each MDX page is transformed to Blume markup **at scan time** as it's read, so your files never change.
37
+ - **Variables are inlined at scan time.** `docs.json` `variables` (`{{name}}`) are substituted into content as it's scanned — Blume has no runtime substitution.
38
+ - **Assets are served in place.** Referenced folders like `images/` are served through [`content.assets`](/docs/content/sources) rather than moved to `public/`, so nothing is relocated.
39
+ - **Languages map to i18n.** A multi-language `docs.json` maps to Blume's [`i18n`](/docs/content/i18n), with the language switch handled by Blume's locale routing instead of a nav selector.
40
+
41
+ Because it reads the same `docs.json` and runs the same transforms, what you see in bridge mode is what you'd get from a full migration.
42
+
43
+ ## Bridge vs. migrate
44
+
45
+ <CardGroup cols={2}>
46
+ <Card title="Bridge mode" icon="cable">
47
+ **Try Blume with zero changes.** Nothing is written; your Mintlify project
48
+ stays exactly as it is. Ideal for evaluating Blume, running both dev servers
49
+ side by side, or a reversible spike.
50
+ </Card>
51
+ <Card title="Migrate" href="/docs/advanced/migrate" icon="arrow-right">
52
+ **Convert for good.** [`blume migrate mintlify`](/docs/advanced/migrate)
53
+ rewrites your pages, config, and assets in place so Blume becomes the source
54
+ of truth. Do this once you've decided to switch.
55
+ </Card>
56
+ </CardGroup>
57
+
58
+ Everything you can do in bridge mode, you can do permanently by migrating — bridge is the preview, migrate is the commit.
59
+
60
+ ## Limitations
61
+
62
+ - **Mintlify only.** Bridge detection is Mintlify-specific. The other frameworks — Fumadocs, Nextra, Starlight — need a [one-shot migration](/docs/advanced/migrate).
63
+ - **Same idiom gaps as the migrator.** Components without a Blume equivalent are transformed on a best-effort basis; when you hit one, [migrate](/docs/advanced/migrate) and address the warnings, or switch that page to a Blume-native equivalent such as the [OpenAPI reference](/docs/advanced/api-reference).
64
+ - **No runtime variables.** As with a migration, `{{variable}}` values are inlined at scan time rather than substituted at runtime.
65
+
66
+ <CardGroup cols={2}>
67
+ <Card title="Migrate" href="/docs/advanced/migrate" icon="arrow-right">
68
+ Convert a Mintlify, Fumadocs, Nextra, or Starlight project permanently.
69
+ </Card>
70
+ <Card title="Content sources" href="/docs/content/sources" icon="folder-tree">
71
+ How the `mintlify` source and `content.assets` fit into Blume's content
72
+ graph.
73
+ </Card>
74
+ </CardGroup>
@@ -2,6 +2,13 @@ import { defineMeta } from "blume";
2
2
 
3
3
  export default defineMeta({
4
4
  order: 5,
5
- pages: ["custom-pages", "changelog", "blog", "api-reference"],
5
+ pages: [
6
+ "migrate",
7
+ "bridge",
8
+ "custom-pages",
9
+ "changelog",
10
+ "blog",
11
+ "api-reference",
12
+ ],
6
13
  title: "Advanced",
7
14
  });