blume 0.8.0 → 1.0.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 (80) hide show
  1. package/CHANGELOG.md +49 -1
  2. package/dist/cli/index.js +373 -28
  3. package/dist/cli/index.js.map +51 -50
  4. package/dist/types/core/config-input.d.ts +11 -0
  5. package/dist/types/core/data.d.ts +5 -0
  6. package/dist/types/core/i18n-ui.d.ts +50 -0
  7. package/dist/types/core/schema.d.ts +77 -1
  8. package/dist/types/seo/x-handle.d.ts +12 -0
  9. package/docs/advanced/api-reference.mdx +1 -1
  10. package/docs/advanced/changelog.mdx +1 -1
  11. package/docs/configuration/export.mdx +1 -1
  12. package/docs/configuration/seo.mdx +33 -3
  13. package/docs/content/components.mdx +1 -1
  14. package/docs/content/syntax.mdx +2 -2
  15. package/docs/index.mdx +1 -1
  16. package/docs/reference/cli.mdx +1 -1
  17. package/docs/reference/frontmatter.mdx +1 -1
  18. package/package.json +9 -1
  19. package/src/ai/mcp/tools.ts +1 -1
  20. package/src/astro/generate.ts +1 -0
  21. package/src/astro/templates.ts +51 -7
  22. package/src/cli/commands/build.ts +25 -8
  23. package/src/cli/internal-error.ts +9 -4
  24. package/src/cli/prepare.ts +3 -2
  25. package/src/components/Icon.astro +2 -1
  26. package/src/components/islands/ask-ai.tsx +30 -5
  27. package/src/components/layout/Breadcrumbs.astro +11 -2
  28. package/src/components/layout/NavTree.astro +2 -2
  29. package/src/components/layout/PageLayout.astro +40 -1
  30. package/src/components/layout/Pagination.astro +10 -4
  31. package/src/components/layout/ReferenceLayout.astro +12 -1
  32. package/src/components/layout/RootLayout.astro +75 -3
  33. package/src/components/layout/Search.astro +23 -5
  34. package/src/core/config-input.ts +11 -0
  35. package/src/core/data.ts +2 -0
  36. package/src/core/frontmatter.ts +7 -0
  37. package/src/core/i18n-ui.ts +9 -0
  38. package/src/core/i18n.ts +16 -8
  39. package/src/core/schema.ts +35 -0
  40. package/src/core/ui-packs/ar.ts +7 -0
  41. package/src/core/ui-packs/bg.ts +7 -0
  42. package/src/core/ui-packs/bn.ts +7 -0
  43. package/src/core/ui-packs/ca.ts +7 -0
  44. package/src/core/ui-packs/cs.ts +7 -0
  45. package/src/core/ui-packs/da.ts +7 -0
  46. package/src/core/ui-packs/de.ts +7 -0
  47. package/src/core/ui-packs/el.ts +7 -0
  48. package/src/core/ui-packs/es.ts +7 -0
  49. package/src/core/ui-packs/fa.ts +7 -0
  50. package/src/core/ui-packs/fi.ts +7 -0
  51. package/src/core/ui-packs/fr.ts +7 -0
  52. package/src/core/ui-packs/he.ts +7 -0
  53. package/src/core/ui-packs/hi.ts +7 -0
  54. package/src/core/ui-packs/hr.ts +7 -0
  55. package/src/core/ui-packs/hu.ts +7 -0
  56. package/src/core/ui-packs/id.ts +7 -0
  57. package/src/core/ui-packs/it.ts +7 -0
  58. package/src/core/ui-packs/ja.ts +7 -0
  59. package/src/core/ui-packs/ko.ts +7 -0
  60. package/src/core/ui-packs/nl.ts +7 -0
  61. package/src/core/ui-packs/no.ts +7 -0
  62. package/src/core/ui-packs/pl.ts +7 -0
  63. package/src/core/ui-packs/pt-br.ts +7 -0
  64. package/src/core/ui-packs/pt.ts +7 -0
  65. package/src/core/ui-packs/ro.ts +7 -0
  66. package/src/core/ui-packs/ru.ts +7 -0
  67. package/src/core/ui-packs/sk.ts +7 -0
  68. package/src/core/ui-packs/sr.ts +7 -0
  69. package/src/core/ui-packs/sv.ts +7 -0
  70. package/src/core/ui-packs/th.ts +7 -0
  71. package/src/core/ui-packs/tr.ts +7 -0
  72. package/src/core/ui-packs/uk.ts +7 -0
  73. package/src/core/ui-packs/vi.ts +7 -0
  74. package/src/core/ui-packs/zh-tw.ts +7 -0
  75. package/src/core/ui-packs/zh.ts +7 -0
  76. package/src/og/card.ts +4 -2
  77. package/src/og/dimensions.ts +12 -0
  78. package/src/openapi/render-mdx.ts +62 -1
  79. package/src/seo/jsonld.ts +7 -1
  80. package/src/seo/x-handle.ts +18 -0
@@ -623,6 +623,17 @@ export interface SeoConfig {
623
623
  sitemap?: boolean;
624
624
  /** Emit schema.org JSON-LD in each page's `<head>`. Defaults to `true`. */
625
625
  structuredData?: boolean;
626
+ /**
627
+ * X (Twitter) attribution for share cards. Handles may omit the `@`. The rest
628
+ * of the X card is read from the `og:*` tags, so these accounts are the only
629
+ * values X cannot infer.
630
+ */
631
+ x?: {
632
+ /** Author account (`twitter:creator`); a page can override it via `seo.x.creator` frontmatter. */
633
+ creator?: string;
634
+ /** The site's own account (`twitter:site`), e.g. `@blume`. */
635
+ handle?: string;
636
+ };
626
637
  }
627
638
  /** Source repository, powering "Edit this page" links and the header repo link. */
628
639
  export interface GithubConfig {
@@ -122,6 +122,11 @@ export interface BlumeDataConfig {
122
122
  title: string;
123
123
  /** Table-of-contents settings: whether to show it and the heading range. */
124
124
  toc: ResolvedConfig["toc"];
125
+ /** X (Twitter) attribution: the site's account, and a default creator. */
126
+ x: {
127
+ creator?: string;
128
+ handle?: string;
129
+ };
125
130
  }
126
131
  /**
127
132
  * The compact snapshot the layout serializes into the page for React island
@@ -111,10 +111,16 @@ declare const uiStringsObject: z.ZodObject<{
111
111
  dismiss?: string | undefined;
112
112
  }>>;
113
113
  changelog: z.ZodDefault<z.ZodObject<{
114
+ description: z.ZodDefault<z.ZodString>;
114
115
  showReleases: z.ZodDefault<z.ZodString>;
116
+ title: z.ZodDefault<z.ZodString>;
115
117
  }, "strip", z.ZodTypeAny, {
118
+ title: string;
119
+ description: string;
116
120
  showReleases: string;
117
121
  }, {
122
+ title?: string | undefined;
123
+ description?: string | undefined;
118
124
  showReleases?: string | undefined;
119
125
  }>>;
120
126
  content: z.ZodDefault<z.ZodObject<{
@@ -152,6 +158,7 @@ declare const uiStringsObject: z.ZodObject<{
152
158
  }>>;
153
159
  nav: z.ZodDefault<z.ZodObject<{
154
160
  back: z.ZodDefault<z.ZodString>;
161
+ breadcrumb: z.ZodDefault<z.ZodString>;
155
162
  closeNavigation: z.ZodDefault<z.ZodString>;
156
163
  deprecated: z.ZodDefault<z.ZodString>;
157
164
  featured: z.ZodDefault<z.ZodString>;
@@ -165,6 +172,7 @@ declare const uiStringsObject: z.ZodObject<{
165
172
  deprecated: string;
166
173
  featured: string;
167
174
  navigation: string;
175
+ breadcrumb: string;
168
176
  back: string;
169
177
  closeNavigation: string;
170
178
  githubRepository: string;
@@ -176,6 +184,7 @@ declare const uiStringsObject: z.ZodObject<{
176
184
  deprecated?: string | undefined;
177
185
  featured?: string | undefined;
178
186
  navigation?: string | undefined;
187
+ breadcrumb?: string | undefined;
179
188
  back?: string | undefined;
180
189
  closeNavigation?: string | undefined;
181
190
  githubRepository?: string | undefined;
@@ -200,20 +209,24 @@ declare const uiStringsObject: z.ZodObject<{
200
209
  page: z.ZodDefault<z.ZodObject<{
201
210
  lastUpdated: z.ZodDefault<z.ZodString>;
202
211
  next: z.ZodDefault<z.ZodString>;
212
+ pagination: z.ZodDefault<z.ZodString>;
203
213
  previous: z.ZodDefault<z.ZodString>;
204
214
  skipToContent: z.ZodDefault<z.ZodString>;
205
215
  }, "strip", z.ZodTypeAny, {
206
216
  lastUpdated: string;
207
217
  next: string;
218
+ pagination: string;
208
219
  previous: string;
209
220
  skipToContent: string;
210
221
  }, {
211
222
  lastUpdated?: string | undefined;
212
223
  next?: string | undefined;
224
+ pagination?: string | undefined;
213
225
  previous?: string | undefined;
214
226
  skipToContent?: string | undefined;
215
227
  }>>;
216
228
  search: z.ZodDefault<z.ZodObject<{
229
+ all: z.ZodDefault<z.ZodString>;
217
230
  allLanguages: z.ZodDefault<z.ZodString>;
218
231
  askAi: z.ZodDefault<z.ZodString>;
219
232
  askAiHint: z.ZodDefault<z.ZodString>;
@@ -232,6 +245,7 @@ declare const uiStringsObject: z.ZodObject<{
232
245
  error: string;
233
246
  label: string;
234
247
  placeholder: string;
248
+ all: string;
235
249
  button: string;
236
250
  devOnly: string;
237
251
  noResults: string;
@@ -247,6 +261,7 @@ declare const uiStringsObject: z.ZodObject<{
247
261
  error?: string | undefined;
248
262
  label?: string | undefined;
249
263
  placeholder?: string | undefined;
264
+ all?: string | undefined;
250
265
  button?: string | undefined;
251
266
  devOnly?: string | undefined;
252
267
  noResults?: string | undefined;
@@ -268,12 +283,15 @@ declare const uiStringsObject: z.ZodObject<{
268
283
  }>>;
269
284
  }, "strip", z.ZodTypeAny, {
270
285
  changelog: {
286
+ title: string;
287
+ description: string;
271
288
  showReleases: string;
272
289
  };
273
290
  search: {
274
291
  error: string;
275
292
  label: string;
276
293
  placeholder: string;
294
+ all: string;
277
295
  button: string;
278
296
  devOnly: string;
279
297
  noResults: string;
@@ -289,6 +307,7 @@ declare const uiStringsObject: z.ZodObject<{
289
307
  page: {
290
308
  lastUpdated: string;
291
309
  next: string;
310
+ pagination: string;
292
311
  previous: string;
293
312
  skipToContent: string;
294
313
  };
@@ -348,6 +367,7 @@ declare const uiStringsObject: z.ZodObject<{
348
367
  deprecated: string;
349
368
  featured: string;
350
369
  navigation: string;
370
+ breadcrumb: string;
351
371
  back: string;
352
372
  closeNavigation: string;
353
373
  githubRepository: string;
@@ -363,12 +383,15 @@ declare const uiStringsObject: z.ZodObject<{
363
383
  };
364
384
  }, {
365
385
  changelog?: {
386
+ title?: string | undefined;
387
+ description?: string | undefined;
366
388
  showReleases?: string | undefined;
367
389
  } | undefined;
368
390
  search?: {
369
391
  error?: string | undefined;
370
392
  label?: string | undefined;
371
393
  placeholder?: string | undefined;
394
+ all?: string | undefined;
372
395
  button?: string | undefined;
373
396
  devOnly?: string | undefined;
374
397
  noResults?: string | undefined;
@@ -384,6 +407,7 @@ declare const uiStringsObject: z.ZodObject<{
384
407
  page?: {
385
408
  lastUpdated?: string | undefined;
386
409
  next?: string | undefined;
410
+ pagination?: string | undefined;
387
411
  previous?: string | undefined;
388
412
  skipToContent?: string | undefined;
389
413
  } | undefined;
@@ -443,6 +467,7 @@ declare const uiStringsObject: z.ZodObject<{
443
467
  deprecated?: string | undefined;
444
468
  featured?: string | undefined;
445
469
  navigation?: string | undefined;
470
+ breadcrumb?: string | undefined;
446
471
  back?: string | undefined;
447
472
  closeNavigation?: string | undefined;
448
473
  githubRepository?: string | undefined;
@@ -561,10 +586,16 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
561
586
  dismiss?: string | undefined;
562
587
  }>>;
563
588
  changelog: z.ZodDefault<z.ZodObject<{
589
+ description: z.ZodDefault<z.ZodString>;
564
590
  showReleases: z.ZodDefault<z.ZodString>;
591
+ title: z.ZodDefault<z.ZodString>;
565
592
  }, "strip", z.ZodTypeAny, {
593
+ title: string;
594
+ description: string;
566
595
  showReleases: string;
567
596
  }, {
597
+ title?: string | undefined;
598
+ description?: string | undefined;
568
599
  showReleases?: string | undefined;
569
600
  }>>;
570
601
  content: z.ZodDefault<z.ZodObject<{
@@ -602,6 +633,7 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
602
633
  }>>;
603
634
  nav: z.ZodDefault<z.ZodObject<{
604
635
  back: z.ZodDefault<z.ZodString>;
636
+ breadcrumb: z.ZodDefault<z.ZodString>;
605
637
  closeNavigation: z.ZodDefault<z.ZodString>;
606
638
  deprecated: z.ZodDefault<z.ZodString>;
607
639
  featured: z.ZodDefault<z.ZodString>;
@@ -615,6 +647,7 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
615
647
  deprecated: string;
616
648
  featured: string;
617
649
  navigation: string;
650
+ breadcrumb: string;
618
651
  back: string;
619
652
  closeNavigation: string;
620
653
  githubRepository: string;
@@ -626,6 +659,7 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
626
659
  deprecated?: string | undefined;
627
660
  featured?: string | undefined;
628
661
  navigation?: string | undefined;
662
+ breadcrumb?: string | undefined;
629
663
  back?: string | undefined;
630
664
  closeNavigation?: string | undefined;
631
665
  githubRepository?: string | undefined;
@@ -650,20 +684,24 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
650
684
  page: z.ZodDefault<z.ZodObject<{
651
685
  lastUpdated: z.ZodDefault<z.ZodString>;
652
686
  next: z.ZodDefault<z.ZodString>;
687
+ pagination: z.ZodDefault<z.ZodString>;
653
688
  previous: z.ZodDefault<z.ZodString>;
654
689
  skipToContent: z.ZodDefault<z.ZodString>;
655
690
  }, "strip", z.ZodTypeAny, {
656
691
  lastUpdated: string;
657
692
  next: string;
693
+ pagination: string;
658
694
  previous: string;
659
695
  skipToContent: string;
660
696
  }, {
661
697
  lastUpdated?: string | undefined;
662
698
  next?: string | undefined;
699
+ pagination?: string | undefined;
663
700
  previous?: string | undefined;
664
701
  skipToContent?: string | undefined;
665
702
  }>>;
666
703
  search: z.ZodDefault<z.ZodObject<{
704
+ all: z.ZodDefault<z.ZodString>;
667
705
  allLanguages: z.ZodDefault<z.ZodString>;
668
706
  askAi: z.ZodDefault<z.ZodString>;
669
707
  askAiHint: z.ZodDefault<z.ZodString>;
@@ -682,6 +720,7 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
682
720
  error: string;
683
721
  label: string;
684
722
  placeholder: string;
723
+ all: string;
685
724
  button: string;
686
725
  devOnly: string;
687
726
  noResults: string;
@@ -697,6 +736,7 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
697
736
  error?: string | undefined;
698
737
  label?: string | undefined;
699
738
  placeholder?: string | undefined;
739
+ all?: string | undefined;
700
740
  button?: string | undefined;
701
741
  devOnly?: string | undefined;
702
742
  noResults?: string | undefined;
@@ -718,12 +758,15 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
718
758
  }>>;
719
759
  }, "strip", z.ZodTypeAny, {
720
760
  changelog: {
761
+ title: string;
762
+ description: string;
721
763
  showReleases: string;
722
764
  };
723
765
  search: {
724
766
  error: string;
725
767
  label: string;
726
768
  placeholder: string;
769
+ all: string;
727
770
  button: string;
728
771
  devOnly: string;
729
772
  noResults: string;
@@ -739,6 +782,7 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
739
782
  page: {
740
783
  lastUpdated: string;
741
784
  next: string;
785
+ pagination: string;
742
786
  previous: string;
743
787
  skipToContent: string;
744
788
  };
@@ -798,6 +842,7 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
798
842
  deprecated: string;
799
843
  featured: string;
800
844
  navigation: string;
845
+ breadcrumb: string;
801
846
  back: string;
802
847
  closeNavigation: string;
803
848
  githubRepository: string;
@@ -813,12 +858,15 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
813
858
  };
814
859
  }, {
815
860
  changelog?: {
861
+ title?: string | undefined;
862
+ description?: string | undefined;
816
863
  showReleases?: string | undefined;
817
864
  } | undefined;
818
865
  search?: {
819
866
  error?: string | undefined;
820
867
  label?: string | undefined;
821
868
  placeholder?: string | undefined;
869
+ all?: string | undefined;
822
870
  button?: string | undefined;
823
871
  devOnly?: string | undefined;
824
872
  noResults?: string | undefined;
@@ -834,6 +882,7 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
834
882
  page?: {
835
883
  lastUpdated?: string | undefined;
836
884
  next?: string | undefined;
885
+ pagination?: string | undefined;
837
886
  previous?: string | undefined;
838
887
  skipToContent?: string | undefined;
839
888
  } | undefined;
@@ -893,6 +942,7 @@ export declare const uiStringsSchema: z.ZodDefault<z.ZodObject<{
893
942
  deprecated?: string | undefined;
894
943
  featured?: string | undefined;
895
944
  navigation?: string | undefined;
945
+ breadcrumb?: string | undefined;
896
946
  back?: string | undefined;
897
947
  closeNavigation?: string | undefined;
898
948
  githubRepository?: string | undefined;
@@ -79,18 +79,32 @@ declare const pageMetaBaseSchema: z.ZodObject<{
79
79
  image: z.ZodOptional<z.ZodString>;
80
80
  noindex: z.ZodDefault<z.ZodBoolean>;
81
81
  title: z.ZodOptional<z.ZodString>;
82
+ /** Per-page X attribution — a guest post credits its own author. */
83
+ x: z.ZodOptional<z.ZodObject<{
84
+ creator: z.ZodOptional<z.ZodEffects<z.ZodString, string | undefined, string>>;
85
+ }, "strict", z.ZodTypeAny, {
86
+ creator?: string | undefined;
87
+ }, {
88
+ creator?: string | undefined;
89
+ }>>;
82
90
  }, "strict", z.ZodTypeAny, {
83
91
  noindex: boolean;
84
92
  title?: string | undefined;
85
93
  image?: string | undefined;
86
94
  description?: string | undefined;
87
95
  canonical?: string | undefined;
96
+ x?: {
97
+ creator?: string | undefined;
98
+ } | undefined;
88
99
  }, {
89
100
  title?: string | undefined;
90
101
  image?: string | undefined;
91
102
  description?: string | undefined;
92
103
  noindex?: boolean | undefined;
93
104
  canonical?: string | undefined;
105
+ x?: {
106
+ creator?: string | undefined;
107
+ } | undefined;
94
108
  }>>;
95
109
  sidebar: z.ZodDefault<z.ZodObject<{
96
110
  badge: z.ZodOptional<z.ZodString>;
@@ -130,6 +144,9 @@ declare const pageMetaBaseSchema: z.ZodObject<{
130
144
  image?: string | undefined;
131
145
  description?: string | undefined;
132
146
  canonical?: string | undefined;
147
+ x?: {
148
+ creator?: string | undefined;
149
+ } | undefined;
133
150
  };
134
151
  sidebar: {
135
152
  hidden: boolean;
@@ -199,6 +216,9 @@ declare const pageMetaBaseSchema: z.ZodObject<{
199
216
  description?: string | undefined;
200
217
  noindex?: boolean | undefined;
201
218
  canonical?: string | undefined;
219
+ x?: {
220
+ creator?: string | undefined;
221
+ } | undefined;
202
222
  } | undefined;
203
223
  sidebar?: {
204
224
  hidden?: boolean | undefined;
@@ -284,18 +304,32 @@ export declare const pageMetaSchema: z.ZodObject<{
284
304
  image: z.ZodOptional<z.ZodString>;
285
305
  noindex: z.ZodDefault<z.ZodBoolean>;
286
306
  title: z.ZodOptional<z.ZodString>;
307
+ /** Per-page X attribution — a guest post credits its own author. */
308
+ x: z.ZodOptional<z.ZodObject<{
309
+ creator: z.ZodOptional<z.ZodEffects<z.ZodString, string | undefined, string>>;
310
+ }, "strict", z.ZodTypeAny, {
311
+ creator?: string | undefined;
312
+ }, {
313
+ creator?: string | undefined;
314
+ }>>;
287
315
  }, "strict", z.ZodTypeAny, {
288
316
  noindex: boolean;
289
317
  title?: string | undefined;
290
318
  image?: string | undefined;
291
319
  description?: string | undefined;
292
320
  canonical?: string | undefined;
321
+ x?: {
322
+ creator?: string | undefined;
323
+ } | undefined;
293
324
  }, {
294
325
  title?: string | undefined;
295
326
  image?: string | undefined;
296
327
  description?: string | undefined;
297
328
  noindex?: boolean | undefined;
298
329
  canonical?: string | undefined;
330
+ x?: {
331
+ creator?: string | undefined;
332
+ } | undefined;
299
333
  }>>;
300
334
  sidebar: z.ZodDefault<z.ZodObject<{
301
335
  badge: z.ZodOptional<z.ZodString>;
@@ -335,6 +369,9 @@ export declare const pageMetaSchema: z.ZodObject<{
335
369
  image?: string | undefined;
336
370
  description?: string | undefined;
337
371
  canonical?: string | undefined;
372
+ x?: {
373
+ creator?: string | undefined;
374
+ } | undefined;
338
375
  };
339
376
  sidebar: {
340
377
  hidden: boolean;
@@ -404,6 +441,9 @@ export declare const pageMetaSchema: z.ZodObject<{
404
441
  description?: string | undefined;
405
442
  noindex?: boolean | undefined;
406
443
  canonical?: string | undefined;
444
+ x?: {
445
+ creator?: string | undefined;
446
+ } | undefined;
407
447
  } | undefined;
408
448
  sidebar?: {
409
449
  hidden?: boolean | undefined;
@@ -2384,7 +2424,24 @@ export declare const blumeConfigSchema: z.ZodObject<{
2384
2424
  sitemap: z.ZodDefault<z.ZodBoolean>;
2385
2425
  /** Emit schema.org JSON-LD in each page's <head>. */
2386
2426
  structuredData: z.ZodDefault<z.ZodBoolean>;
2427
+ /** X (Twitter) account attribution for share cards. */
2428
+ x: z.ZodDefault<z.ZodObject<{
2429
+ /** The author's account, overridable per page via `seo.x.creator`. */
2430
+ creator: z.ZodOptional<z.ZodEffects<z.ZodString, string | undefined, string>>;
2431
+ /** The site's own account, e.g. `@blume`. */
2432
+ handle: z.ZodOptional<z.ZodEffects<z.ZodString, string | undefined, string>>;
2433
+ }, "strict", z.ZodTypeAny, {
2434
+ creator?: string | undefined;
2435
+ handle?: string | undefined;
2436
+ }, {
2437
+ creator?: string | undefined;
2438
+ handle?: string | undefined;
2439
+ }>>;
2387
2440
  }, "strict", z.ZodTypeAny, {
2441
+ x: {
2442
+ creator?: string | undefined;
2443
+ handle?: string | undefined;
2444
+ };
2388
2445
  agentReadability: boolean;
2389
2446
  contentSignals: {
2390
2447
  search: boolean;
@@ -2403,6 +2460,10 @@ export declare const blumeConfigSchema: z.ZodObject<{
2403
2460
  sitemap: boolean;
2404
2461
  structuredData: boolean;
2405
2462
  }, {
2463
+ x?: {
2464
+ creator?: string | undefined;
2465
+ handle?: string | undefined;
2466
+ } | undefined;
2406
2467
  agentReadability?: boolean | undefined;
2407
2468
  contentSignals?: boolean | {
2408
2469
  search?: boolean | undefined;
@@ -2533,7 +2594,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
2533
2594
  radius?: "md" | "none" | "sm" | "lg" | undefined;
2534
2595
  }>>;
2535
2596
  title: z.ZodDefault<z.ZodString>;
2536
- toc: z.ZodEffects<z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodObject<{
2597
+ toc: z.ZodEffects<z.ZodEffects<z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodObject<{
2537
2598
  maxHeadingLevel: z.ZodOptional<z.ZodNumber>;
2538
2599
  minHeadingLevel: z.ZodOptional<z.ZodNumber>;
2539
2600
  }, "strict", z.ZodTypeAny, {
@@ -2549,6 +2610,13 @@ export declare const blumeConfigSchema: z.ZodObject<{
2549
2610
  }, boolean | {
2550
2611
  maxHeadingLevel?: number | undefined;
2551
2612
  minHeadingLevel?: number | undefined;
2613
+ } | undefined>, {
2614
+ enabled: boolean;
2615
+ maxLevel: number;
2616
+ minLevel: number;
2617
+ }, boolean | {
2618
+ maxHeadingLevel?: number | undefined;
2619
+ minHeadingLevel?: number | undefined;
2552
2620
  } | undefined>;
2553
2621
  }, "strict", z.ZodTypeAny, {
2554
2622
  title: string;
@@ -2582,6 +2650,10 @@ export declare const blumeConfigSchema: z.ZodObject<{
2582
2650
  } | undefined;
2583
2651
  };
2584
2652
  seo: {
2653
+ x: {
2654
+ creator?: string | undefined;
2655
+ handle?: string | undefined;
2656
+ };
2585
2657
  agentReadability: boolean;
2586
2658
  contentSignals: {
2587
2659
  search: boolean;
@@ -2900,6 +2972,10 @@ export declare const blumeConfigSchema: z.ZodObject<{
2900
2972
  } | undefined;
2901
2973
  } | undefined;
2902
2974
  seo?: {
2975
+ x?: {
2976
+ creator?: string | undefined;
2977
+ handle?: string | undefined;
2978
+ } | undefined;
2903
2979
  agentReadability?: boolean | undefined;
2904
2980
  contentSignals?: boolean | {
2905
2981
  search?: boolean | undefined;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Normalize an X account to the leading `@` that `twitter:site`/`twitter:creator`
3
+ * require, so `acme`, `@acme`, and ` @acme ` all land on `@acme`. Empty or
4
+ * blank input yields undefined, which renders no tag at all.
5
+ *
6
+ * The layouts call this on values that never passed through the config schema:
7
+ * Astro's collections carry no schema here, so a page's `seo.x.creator` reaches
8
+ * them as raw frontmatter, and the schema's own transform never runs on it.
9
+ * (Blume's page pipeline does reject a non-string `creator` before the page is
10
+ * built, so `unknown` is defense in depth rather than the expected path.)
11
+ */
12
+ export declare const normalizeXHandle: (value: unknown) => string | undefined;
@@ -3,7 +3,7 @@ title: OpenAPI / AsyncAPI
3
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 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.
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. These docs ship one: the [Example API](/api) reference is generated from the Petstore spec by the config below.
7
7
 
8
8
  ```ts blume.config.ts lineNumbers
9
9
  openapi: {
@@ -71,7 +71,7 @@ When your versions follow [semver](https://semver.org) and span more than one ma
71
71
 
72
72
  ## From GitHub Releases
73
73
 
74
- Rather than authoring entries by hand, point the built-in [`github-releases` source](/docs/content/sources#github-releases) at a repo and every release becomes a `type: changelog` entry — the same timeline and feed, fed straight from the releases you already publish:
74
+ Rather than authoring entries by hand, point the built-in [`github-releases` source](/docs/content/sources#github-releases) at a repo and every release becomes a `type: changelog` entry — the same timeline and feed, fed straight from the releases you already publish. Blume's own [changelog](/changelog) is built this way:
75
75
 
76
76
  ```ts blume.config.ts
77
77
  content: {
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Export
3
- description: Let readers download any page as a PDF or EPUB.
3
+ description: Let readers download any page as a PDF or EPUB — client-side, so static builds stay static.
4
4
  ---
5
5
 
6
6
  Blume can add an **Export** action to the [page actions](/docs/content/navigation#page-actions) beneath the table of contents, letting readers save the page they're on as a **PDF** or an **EPUB**. It's off by default and entirely client-side — no server, and [static](/docs/deployment) builds stay static.
@@ -14,6 +14,7 @@ seo: {
14
14
  sitemap: true,
15
15
  robots: true,
16
16
  structuredData: true,
17
+ x: { handle: "@acme" },
17
18
  }
18
19
  ```
19
20
 
@@ -25,10 +26,34 @@ Every page renders the standard `<head>` tags from your config and frontmatter:
25
26
 
26
27
  - `<title>` — the page title plus your site `title`.
27
28
  - `<meta name="description">` and `og:description` — the page `description`, falling back to the site `description`.
28
- - `og:title` — the page title.
29
- - `<link rel="canonical">` — the page's absolute URL (when `deployment.site` is set).
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.
30
34
 
31
- Override any of these per page with `seo` frontmatter:
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:
32
57
 
33
58
  ```yaml lineNumbers
34
59
  ---
@@ -63,6 +88,11 @@ seo:
63
88
  type: "boolean",
64
89
  description: "Emit robots noindex and skip structured data.",
65
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
+ },
66
96
  }}
67
97
  />
68
98
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Components
3
- description: The built-in JSX components — cards, columns, steps, tabs, accordions, badges, code groups, frames, trees, tooltips, type tables, live component previews, diffs, and a GitHub card usable in any MDX page.
3
+ description: Cards, steps, tabs, accordions, badges, code groups, frames, trees, type tables, live previews, and diffs the built-in components, usable in any MDX page.
4
4
  ---
5
5
 
6
6
  Blume ships an accessible, themeable component set available in any `.mdx` page with **no imports**. Each one is shown below with a live preview and its source. Components are vanilla and React-free; React only switches on if you add your own island.
@@ -261,7 +261,7 @@ Hide the language icons or wrap long lines instead of scrolling with `markdown:
261
261
 
262
262
  ## Package install
263
263
 
264
- A `package-install` block turns a single install command into a tabbed snippet for npm, pnpm, yarn, and bun — so readers copy the one that matches their setup.
264
+ A `package-install` block turns a single install command into a tabbed snippet for npm, pnpm, yarn, and bun — so readers copy the one that matches their setup. Like diagrams and math, this is an MDX-only feature — in a `.md` file the block renders as a plain code fence.
265
265
 
266
266
  ```package-install
267
267
  npm i blume
@@ -297,7 +297,7 @@ Diagrams render on the client, so this is an MDX-only feature, and the Mermaid l
297
297
 
298
298
  ## Callouts
299
299
 
300
- Callouts pull a reader's attention to context, advice, or risk. Write them as `:::type` directives; add a title in brackets, like `:::warning[Heads up]`.
300
+ Callouts pull a reader's attention to context, advice, or risk. Write them as `:::type` directives; add a title in brackets, like `:::warning[Heads up]`. Directives are an MDX-only feature — in a `.md` file a `:::note` line stays literal text.
301
301
 
302
302
  ### Note
303
303
 
package/docs/index.mdx CHANGED
@@ -21,7 +21,7 @@ Drop Markdown or MDX into a folder, run `blume dev`, and get a production-grade
21
21
 
22
22
  Docs should be fast, AI-ready, and zero-config — down to not needing a starter template at all. Some docs tools hand you an entire codebase to maintain before you've written a word. Others build the template around your content, but lock you in to their managed service.
23
23
 
24
- Blume takes the best of both worlds. The framework is the template, so the only thing you ever touch is your content. When you want to customize, you can start by replacing the built-in components, modifying the single configuration file, or even ejecting if you want the Astro site directly.
24
+ Blume takes the best of both worlds. The framework is the template, so the only thing you ever touch is your content. When you want to customize, you can start by replacing the built-in components, modifying the single configuration file, or even ejecting if you want the Astro site directly. The [FAQ](/docs/faq) walks through how that compares to Mintlify, Fumadocs, and the rest.
25
25
 
26
26
  ## What makes Blume different
27
27
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: CLI
3
- description: The Blume command-line interface.
3
+ description: Every Blume command and flag — init, dev, build, preview, add, sync, and eject.
4
4
  ---
5
5
 
6
6
  ```bash
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Frontmatter
3
- description: The page metadata schema.
3
+ description: Every frontmatter field a page accepts — title, description, sidebar, SEO, search, and more.
4
4
  ---
5
5
 
6
6
  Every page accepts the following frontmatter. All fields are optional.