blume 1.0.4 → 1.1.1

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 (117) hide show
  1. package/CHANGELOG.md +80 -0
  2. package/dist/cli/index.js +13404 -10228
  3. package/dist/cli/index.js.map +94 -63
  4. package/dist/types/ai/component-markdown.d.ts +12 -1
  5. package/dist/types/core/config-input.d.ts +73 -4
  6. package/dist/types/core/data.d.ts +9 -0
  7. package/dist/types/core/deployment-env.d.ts +6 -0
  8. package/dist/types/core/diagnostics.d.ts +23 -0
  9. package/dist/types/core/i18n-ui.d.ts +8 -8
  10. package/dist/types/core/schema.d.ts +144 -22
  11. package/dist/types/core/sources/types.d.ts +3 -1
  12. package/dist/types/core/standard-schema.d.ts +41 -0
  13. package/dist/types/core/types.d.ts +20 -0
  14. package/dist/types/og/card.d.ts +63 -0
  15. package/dist/types/og/dimensions.d.ts +12 -0
  16. package/docs/01-quickstart.mdx +1 -1
  17. package/docs/02-deployment.mdx +9 -1
  18. package/docs/advanced/api-reference.mdx +11 -0
  19. package/docs/advanced/changelog.mdx +2 -2
  20. package/docs/advanced/skills.mdx +1 -1
  21. package/docs/configuration/ai.mdx +3 -3
  22. package/docs/configuration/customization.mdx +1 -1
  23. package/docs/configuration/export.mdx +1 -1
  24. package/docs/configuration/index.mdx +21 -1
  25. package/docs/configuration/search.mdx +28 -1
  26. package/docs/configuration/seo.mdx +37 -2
  27. package/docs/configuration/theming.mdx +1 -1
  28. package/docs/content/components.mdx +14 -0
  29. package/docs/content/index.mdx +1 -1
  30. package/docs/content/meta.mdx +1 -1
  31. package/docs/content/navigation.mdx +5 -1
  32. package/docs/content/sources.mdx +1 -1
  33. package/docs/reference/cli.mdx +80 -2
  34. package/docs/reference/frontmatter.mdx +31 -1
  35. package/package.json +4 -3
  36. package/skills/blume-migrate/SKILL.md +1 -1
  37. package/skills/blume-migrate/references/mintlify.md +3 -2
  38. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +16 -4
  39. package/src/ai/component-markdown.ts +39 -11
  40. package/src/ai/llms.ts +19 -2
  41. package/src/ai/markdown.ts +5 -1
  42. package/src/astro/adapter-root.ts +70 -0
  43. package/src/astro/generate.ts +124 -50
  44. package/src/astro/index.ts +1 -0
  45. package/src/astro/pages.ts +39 -8
  46. package/src/astro/templates.ts +93 -28
  47. package/src/audit/agent.ts +114 -0
  48. package/src/audit/catalog.ts +826 -0
  49. package/src/audit/checks/assets.ts +177 -0
  50. package/src/audit/checks/content.ts +231 -0
  51. package/src/audit/checks/duplicates.ts +131 -0
  52. package/src/audit/checks/i18n.ts +246 -0
  53. package/src/audit/checks/indexability.ts +213 -0
  54. package/src/audit/checks/links.ts +223 -0
  55. package/src/audit/checks/llms.ts +138 -0
  56. package/src/audit/checks/network.ts +272 -0
  57. package/src/audit/checks/og-image.ts +113 -0
  58. package/src/audit/checks/redirects.ts +87 -0
  59. package/src/audit/checks/robots.ts +114 -0
  60. package/src/audit/checks/sitemap.ts +229 -0
  61. package/src/audit/checks/social.ts +238 -0
  62. package/src/audit/crawl.ts +259 -0
  63. package/src/audit/graph.ts +74 -0
  64. package/src/audit/html.ts +54 -0
  65. package/src/audit/image-size.ts +63 -0
  66. package/src/audit/locate.ts +33 -0
  67. package/src/audit/redirects.ts +74 -0
  68. package/src/audit/report.ts +278 -0
  69. package/src/audit/run.ts +198 -0
  70. package/src/audit/snapshot.ts +189 -0
  71. package/src/audit/types.ts +214 -0
  72. package/src/audit/url.ts +103 -0
  73. package/src/cli/commands/audit.ts +205 -0
  74. package/src/cli/commands/build.ts +64 -13
  75. package/src/cli/index.ts +2 -0
  76. package/src/cli/prepare.ts +10 -2
  77. package/src/components/content/Tabs.astro +98 -15
  78. package/src/components/layout/Breadcrumbs.astro +1 -1
  79. package/src/components/layout/Header.astro +1 -0
  80. package/src/components/layout/PageFeedback.astro +1 -1
  81. package/src/components/layout/PageLayout.astro +5 -1
  82. package/src/components/layout/Pagination.astro +1 -1
  83. package/src/components/layout/RootLayout.astro +5 -3
  84. package/src/components/layout/Search.astro +35 -6
  85. package/src/components/layout/TableOfContents.astro +1 -1
  86. package/src/components/openapi/Authorization.astro +80 -0
  87. package/src/components/openapi/Operation.astro +19 -1
  88. package/src/components/openapi/ParametersTable.astro +1 -1
  89. package/src/components/openapi/security.ts +201 -0
  90. package/src/components/openapi/snippets.ts +42 -13
  91. package/src/core/config-input.ts +78 -4
  92. package/src/core/data.ts +9 -1
  93. package/src/core/deployment-env.ts +9 -0
  94. package/src/core/diagnostics.ts +61 -12
  95. package/src/core/graph.ts +23 -4
  96. package/src/core/links.ts +2 -91
  97. package/src/core/nav-diagnostics.ts +48 -4
  98. package/src/core/navigation.ts +169 -14
  99. package/src/core/probe.ts +136 -0
  100. package/src/core/project-graph.ts +54 -20
  101. package/src/core/schema.ts +93 -3
  102. package/src/core/sources/github-releases.ts +65 -2
  103. package/src/core/sources/normalize.ts +198 -25
  104. package/src/core/sources/types.ts +3 -1
  105. package/src/core/standard-schema.ts +54 -0
  106. package/src/core/types.ts +20 -0
  107. package/src/deploy/adapter-output.ts +27 -15
  108. package/src/deploy/headers.ts +66 -0
  109. package/src/deploy/redirects.ts +49 -9
  110. package/src/markdown/index.ts +1 -0
  111. package/src/markdown/twoslash.ts +60 -0
  112. package/src/og/card.ts +98 -33
  113. package/src/og/index.ts +1 -1
  114. package/src/registry/eject.ts +3 -1
  115. package/src/search/popular.ts +33 -0
  116. package/src/theme/entry.ts +6 -1
  117. /package/docs/{03-faq.mdx → 07-faq.mdx} +0 -0
@@ -1,6 +1,7 @@
1
1
  import { z } from "zod";
2
2
  import type { ComponentMarkdown } from "../ai/component-markdown.ts";
3
3
  import type { ContentSource } from "./sources/types.ts";
4
+ import type { StandardSchema } from "./standard-schema.ts";
4
5
  declare const hydrationMode: z.ZodEnum<["load", "idle", "visible", "media", "only"]>;
5
6
  export type HydrationMode = z.infer<typeof hydrationMode>;
6
7
  /** Frontmatter accepted on any content page. */
@@ -1759,6 +1760,14 @@ export declare const blumeConfigSchema: z.ZodObject<{
1759
1760
  pdf?: boolean | undefined;
1760
1761
  }>>;
1761
1762
  feedback: z.ZodDefault<z.ZodBoolean>;
1763
+ /** Opt-in custom frontmatter keys, validated by user-supplied schemas. */
1764
+ frontmatter: z.ZodDefault<z.ZodObject<{
1765
+ extend: z.ZodEffects<z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodType<StandardSchema<unknown, unknown>, z.ZodTypeDef, StandardSchema<unknown, unknown>>>>, Record<string, StandardSchema<unknown, unknown>>, Record<string, StandardSchema<unknown, unknown>> | undefined>;
1766
+ }, "strict", z.ZodTypeAny, {
1767
+ extend: Record<string, StandardSchema<unknown, unknown>>;
1768
+ }, {
1769
+ extend?: Record<string, StandardSchema<unknown, unknown>> | undefined;
1770
+ }>>;
1762
1771
  github: z.ZodOptional<z.ZodObject<{
1763
1772
  branch: z.ZodDefault<z.ZodString>;
1764
1773
  /** Path from the repo root to the project root (for monorepos). */
@@ -1848,9 +1857,9 @@ export declare const blumeConfigSchema: z.ZodObject<{
1848
1857
  lastModified: z.ZodDefault<z.ZodUnion<[z.ZodBoolean, z.ZodObject<{
1849
1858
  type: z.ZodDefault<z.ZodEnum<["git", "frontmatter"]>>;
1850
1859
  }, "strict", z.ZodTypeAny, {
1851
- type: "git" | "frontmatter";
1860
+ type: "frontmatter" | "git";
1852
1861
  }, {
1853
- type?: "git" | "frontmatter" | undefined;
1862
+ type?: "frontmatter" | "git" | undefined;
1854
1863
  }>]>>;
1855
1864
  logo: z.ZodOptional<z.ZodUnion<[z.ZodString, z.ZodObject<{
1856
1865
  href: z.ZodOptional<z.ZodString>;
@@ -2288,6 +2297,20 @@ export declare const blumeConfigSchema: z.ZodObject<{
2288
2297
  endpoint: string;
2289
2298
  indexId?: string | undefined;
2290
2299
  }>>;
2300
+ /** Curated links for the Cmd+K empty state; defaults to the first sidebar pages. */
2301
+ popular: z.ZodDefault<z.ZodArray<z.ZodObject<{
2302
+ href: z.ZodString;
2303
+ icon: z.ZodOptional<z.ZodString>;
2304
+ label: z.ZodString;
2305
+ }, "strict", z.ZodTypeAny, {
2306
+ label: string;
2307
+ href: string;
2308
+ icon?: string | undefined;
2309
+ }, {
2310
+ label: string;
2311
+ href: string;
2312
+ icon?: string | undefined;
2313
+ }>, "many">>;
2291
2314
  provider: z.ZodDefault<z.ZodEnum<["orama", "pagefind", "flexsearch", "algolia", "orama-cloud", "typesense", "mixedbread", "none"]>>;
2292
2315
  typesense: z.ZodOptional<z.ZodObject<{
2293
2316
  collection: z.ZodString;
@@ -2313,6 +2336,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
2313
2336
  indexing: {
2314
2337
  includeHiddenPages: boolean;
2315
2338
  };
2339
+ popular: {
2340
+ label: string;
2341
+ href: string;
2342
+ icon?: string | undefined;
2343
+ }[];
2316
2344
  algolia?: {
2317
2345
  appId: string;
2318
2346
  indexName: string;
@@ -2351,6 +2379,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
2351
2379
  endpoint: string;
2352
2380
  indexId?: string | undefined;
2353
2381
  } | undefined;
2382
+ popular?: {
2383
+ label: string;
2384
+ href: string;
2385
+ icon?: string | undefined;
2386
+ }[] | undefined;
2354
2387
  typesense?: {
2355
2388
  host: string;
2356
2389
  searchApiKey: string;
@@ -2363,6 +2396,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
2363
2396
  indexing: {
2364
2397
  includeHiddenPages: boolean;
2365
2398
  };
2399
+ popular: {
2400
+ label: string;
2401
+ href: string;
2402
+ icon?: string | undefined;
2403
+ }[];
2366
2404
  algolia?: {
2367
2405
  appId: string;
2368
2406
  indexName: string;
@@ -2401,6 +2439,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
2401
2439
  endpoint: string;
2402
2440
  indexId?: string | undefined;
2403
2441
  } | undefined;
2442
+ popular?: {
2443
+ label: string;
2444
+ href: string;
2445
+ icon?: string | undefined;
2446
+ }[] | undefined;
2404
2447
  typesense?: {
2405
2448
  host: string;
2406
2449
  searchApiKey: string;
@@ -2446,6 +2489,24 @@ export declare const blumeConfigSchema: z.ZodObject<{
2446
2489
  * `loadConfig`. An explicit value here always wins.
2447
2490
  */
2448
2491
  enabled: z.ZodOptional<z.ZodBoolean>;
2492
+ /**
2493
+ * Google Font families for the generated card, extending Takumi's Latin-only
2494
+ * default so non-Latin titles (CJK, and so on) render instead of tofu.
2495
+ * Fetched from Google Fonts at build.
2496
+ */
2497
+ fonts: z.ZodOptional<z.ZodArray<z.ZodUnion<[z.ZodString, z.ZodObject<{
2498
+ name: z.ZodString;
2499
+ style: z.ZodOptional<z.ZodUnion<[z.ZodEnum<["normal", "italic"]>, z.ZodArray<z.ZodEnum<["normal", "italic"]>, "many">]>>;
2500
+ weight: z.ZodOptional<z.ZodUnion<[z.ZodNumber, z.ZodArray<z.ZodNumber, "many">, z.ZodString]>>;
2501
+ }, "strict", z.ZodTypeAny, {
2502
+ name: string;
2503
+ style?: "normal" | "italic" | ("normal" | "italic")[] | undefined;
2504
+ weight?: string | number | number[] | undefined;
2505
+ }, {
2506
+ name: string;
2507
+ style?: "normal" | "italic" | ("normal" | "italic")[] | undefined;
2508
+ weight?: string | number | number[] | undefined;
2509
+ }>]>, "many">>;
2449
2510
  /** Local SVG used in the generated card instead of the site logo. */
2450
2511
  logo: z.ZodOptional<z.ZodString>;
2451
2512
  /** Optional generated-card colors. */
@@ -2468,9 +2529,21 @@ export declare const blumeConfigSchema: z.ZodObject<{
2468
2529
  foreground?: string | undefined;
2469
2530
  muted?: string | undefined;
2470
2531
  }>>;
2532
+ /**
2533
+ * Card headlines for custom `.astro` pages, keyed by route (`"/"`, `"/cli"`).
2534
+ * A custom page has no frontmatter to read, so its card is otherwise titled
2535
+ * by humanizing its last URL segment (`/cli` → "Cli"); an entry here wins.
2536
+ * Content pages always take their card headline from the page title.
2537
+ */
2538
+ titles: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
2471
2539
  }, "strict", z.ZodTypeAny, {
2472
2540
  enabled?: boolean | undefined;
2473
2541
  logo?: string | undefined;
2542
+ fonts?: (string | {
2543
+ name: string;
2544
+ style?: "normal" | "italic" | ("normal" | "italic")[] | undefined;
2545
+ weight?: string | number | number[] | undefined;
2546
+ })[] | undefined;
2474
2547
  palette?: {
2475
2548
  accent?: string | undefined;
2476
2549
  background?: string | undefined;
@@ -2478,9 +2551,15 @@ export declare const blumeConfigSchema: z.ZodObject<{
2478
2551
  foreground?: string | undefined;
2479
2552
  muted?: string | undefined;
2480
2553
  } | undefined;
2554
+ titles?: Record<string, string> | undefined;
2481
2555
  }, {
2482
2556
  enabled?: boolean | undefined;
2483
2557
  logo?: string | undefined;
2558
+ fonts?: (string | {
2559
+ name: string;
2560
+ style?: "normal" | "italic" | ("normal" | "italic")[] | undefined;
2561
+ weight?: string | number | number[] | undefined;
2562
+ })[] | undefined;
2484
2563
  palette?: {
2485
2564
  accent?: string | undefined;
2486
2565
  background?: string | undefined;
@@ -2488,6 +2567,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
2488
2567
  foreground?: string | undefined;
2489
2568
  muted?: string | undefined;
2490
2569
  } | undefined;
2570
+ titles?: Record<string, string> | undefined;
2491
2571
  }>>;
2492
2572
  /** Generate robots.txt (with a Sitemap reference when available). */
2493
2573
  robots: z.ZodDefault<z.ZodBoolean>;
@@ -2533,6 +2613,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
2533
2613
  og: {
2534
2614
  enabled?: boolean | undefined;
2535
2615
  logo?: string | undefined;
2616
+ fonts?: (string | {
2617
+ name: string;
2618
+ style?: "normal" | "italic" | ("normal" | "italic")[] | undefined;
2619
+ weight?: string | number | number[] | undefined;
2620
+ })[] | undefined;
2536
2621
  palette?: {
2537
2622
  accent?: string | undefined;
2538
2623
  background?: string | undefined;
@@ -2540,6 +2625,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
2540
2625
  foreground?: string | undefined;
2541
2626
  muted?: string | undefined;
2542
2627
  } | undefined;
2628
+ titles?: Record<string, string> | undefined;
2543
2629
  };
2544
2630
  robots: boolean;
2545
2631
  rss: {
@@ -2563,6 +2649,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
2563
2649
  og?: {
2564
2650
  enabled?: boolean | undefined;
2565
2651
  logo?: string | undefined;
2652
+ fonts?: (string | {
2653
+ name: string;
2654
+ style?: "normal" | "italic" | ("normal" | "italic")[] | undefined;
2655
+ weight?: string | number | number[] | undefined;
2656
+ })[] | undefined;
2566
2657
  palette?: {
2567
2658
  accent?: string | undefined;
2568
2659
  background?: string | undefined;
@@ -2570,6 +2661,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
2570
2661
  foreground?: string | undefined;
2571
2662
  muted?: string | undefined;
2572
2663
  } | undefined;
2664
+ titles?: Record<string, string> | undefined;
2573
2665
  } | undefined;
2574
2666
  robots?: boolean | undefined;
2575
2667
  rss?: {
@@ -2651,15 +2743,15 @@ export declare const blumeConfigSchema: z.ZodObject<{
2651
2743
  mode: z.ZodDefault<z.ZodEnum<["system", "light", "dark"]>>;
2652
2744
  radius: z.ZodDefault<z.ZodEnum<["none", "sm", "md", "lg"]>>;
2653
2745
  }, "strict", z.ZodTypeAny, {
2654
- accent: {
2655
- dark: string;
2656
- light: string;
2657
- };
2658
2746
  fonts: {
2659
2747
  mono: string;
2660
2748
  display: string;
2661
2749
  body: string;
2662
2750
  };
2751
+ accent: {
2752
+ dark: string;
2753
+ light: string;
2754
+ };
2663
2755
  layout: "sidebar";
2664
2756
  mode: "dark" | "light" | "system";
2665
2757
  radius: "none" | "sm" | "md" | "lg";
@@ -2673,6 +2765,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
2673
2765
  light?: string | undefined;
2674
2766
  } | undefined;
2675
2767
  }, {
2768
+ fonts?: {
2769
+ mono?: string | undefined;
2770
+ display?: string | undefined;
2771
+ body?: string | undefined;
2772
+ } | undefined;
2676
2773
  accent?: string | {
2677
2774
  dark: string;
2678
2775
  light: string;
@@ -2686,11 +2783,6 @@ export declare const blumeConfigSchema: z.ZodObject<{
2686
2783
  dark?: string | undefined;
2687
2784
  light?: string | undefined;
2688
2785
  } | undefined;
2689
- fonts?: {
2690
- mono?: string | undefined;
2691
- display?: string | undefined;
2692
- body?: string | undefined;
2693
- } | undefined;
2694
2786
  layout?: "sidebar" | undefined;
2695
2787
  mode?: "dark" | "light" | "system" | undefined;
2696
2788
  radius?: "none" | "sm" | "md" | "lg" | undefined;
@@ -2722,6 +2814,9 @@ export declare const blumeConfigSchema: z.ZodObject<{
2722
2814
  } | undefined>;
2723
2815
  }, "strict", z.ZodTypeAny, {
2724
2816
  title: string;
2817
+ frontmatter: {
2818
+ extend: Record<string, StandardSchema<unknown, unknown>>;
2819
+ };
2725
2820
  openapi: {
2726
2821
  enabled: boolean;
2727
2822
  route: string;
@@ -2839,15 +2934,15 @@ export declare const blumeConfigSchema: z.ZodObject<{
2839
2934
  })[] | undefined;
2840
2935
  };
2841
2936
  theme: {
2842
- accent: {
2843
- dark: string;
2844
- light: string;
2845
- };
2846
2937
  fonts: {
2847
2938
  mono: string;
2848
2939
  display: string;
2849
2940
  body: string;
2850
2941
  };
2942
+ accent: {
2943
+ dark: string;
2944
+ light: string;
2945
+ };
2851
2946
  layout: "sidebar";
2852
2947
  mode: "dark" | "light" | "system";
2853
2948
  radius: "none" | "sm" | "md" | "lg";
@@ -2863,7 +2958,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
2863
2958
  };
2864
2959
  basePath: string;
2865
2960
  lastModified: boolean | {
2866
- type: "git" | "frontmatter";
2961
+ type: "frontmatter" | "git";
2867
2962
  };
2868
2963
  deployment: {
2869
2964
  adapter: "vercel" | "node" | "netlify" | "cloudflare" | null;
@@ -2942,6 +3037,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
2942
3037
  indexing: {
2943
3038
  includeHiddenPages: boolean;
2944
3039
  };
3040
+ popular: {
3041
+ label: string;
3042
+ href: string;
3043
+ icon?: string | undefined;
3044
+ }[];
2945
3045
  algolia?: {
2946
3046
  appId: string;
2947
3047
  indexName: string;
@@ -2973,6 +3073,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
2973
3073
  og: {
2974
3074
  enabled?: boolean | undefined;
2975
3075
  logo?: string | undefined;
3076
+ fonts?: (string | {
3077
+ name: string;
3078
+ style?: "normal" | "italic" | ("normal" | "italic")[] | undefined;
3079
+ weight?: string | number | number[] | undefined;
3080
+ })[] | undefined;
2976
3081
  palette?: {
2977
3082
  accent?: string | undefined;
2978
3083
  background?: string | undefined;
@@ -2980,6 +3085,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
2980
3085
  foreground?: string | undefined;
2981
3086
  muted?: string | undefined;
2982
3087
  } | undefined;
3088
+ titles?: Record<string, string> | undefined;
2983
3089
  };
2984
3090
  robots: boolean;
2985
3091
  rss: {
@@ -3051,6 +3157,9 @@ export declare const blumeConfigSchema: z.ZodObject<{
3051
3157
  } | undefined;
3052
3158
  }, {
3053
3159
  title?: string | undefined;
3160
+ frontmatter?: {
3161
+ extend?: Record<string, StandardSchema<unknown, unknown>> | undefined;
3162
+ } | undefined;
3054
3163
  openapi?: {
3055
3164
  enabled?: boolean | undefined;
3056
3165
  route?: string | undefined;
@@ -3181,6 +3290,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
3181
3290
  vercel?: boolean | undefined;
3182
3291
  } | undefined;
3183
3292
  theme?: {
3293
+ fonts?: {
3294
+ mono?: string | undefined;
3295
+ display?: string | undefined;
3296
+ body?: string | undefined;
3297
+ } | undefined;
3184
3298
  accent?: string | {
3185
3299
  dark: string;
3186
3300
  light: string;
@@ -3194,11 +3308,6 @@ export declare const blumeConfigSchema: z.ZodObject<{
3194
3308
  dark?: string | undefined;
3195
3309
  light?: string | undefined;
3196
3310
  } | undefined;
3197
- fonts?: {
3198
- mono?: string | undefined;
3199
- display?: string | undefined;
3200
- body?: string | undefined;
3201
- } | undefined;
3202
3311
  layout?: "sidebar" | undefined;
3203
3312
  mode?: "dark" | "light" | "system" | undefined;
3204
3313
  radius?: "none" | "sm" | "md" | "lg" | undefined;
@@ -3221,7 +3330,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
3221
3330
  } | undefined;
3222
3331
  description?: string | undefined;
3223
3332
  lastModified?: boolean | {
3224
- type?: "git" | "frontmatter" | undefined;
3333
+ type?: "frontmatter" | "git" | undefined;
3225
3334
  } | undefined;
3226
3335
  deployment?: {
3227
3336
  adapter?: "vercel" | "node" | "netlify" | "cloudflare" | null | undefined;
@@ -3334,6 +3443,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
3334
3443
  endpoint: string;
3335
3444
  indexId?: string | undefined;
3336
3445
  } | undefined;
3446
+ popular?: {
3447
+ label: string;
3448
+ href: string;
3449
+ icon?: string | undefined;
3450
+ }[] | undefined;
3337
3451
  typesense?: {
3338
3452
  host: string;
3339
3453
  searchApiKey: string;
@@ -3352,6 +3466,11 @@ export declare const blumeConfigSchema: z.ZodObject<{
3352
3466
  og?: {
3353
3467
  enabled?: boolean | undefined;
3354
3468
  logo?: string | undefined;
3469
+ fonts?: (string | {
3470
+ name: string;
3471
+ style?: "normal" | "italic" | ("normal" | "italic")[] | undefined;
3472
+ weight?: string | number | number[] | undefined;
3473
+ })[] | undefined;
3355
3474
  palette?: {
3356
3475
  accent?: string | undefined;
3357
3476
  background?: string | undefined;
@@ -3359,6 +3478,7 @@ export declare const blumeConfigSchema: z.ZodObject<{
3359
3478
  foreground?: string | undefined;
3360
3479
  muted?: string | undefined;
3361
3480
  } | undefined;
3481
+ titles?: Record<string, string> | undefined;
3362
3482
  } | undefined;
3363
3483
  robots?: boolean | undefined;
3364
3484
  rss?: {
@@ -3380,6 +3500,8 @@ export declare const blumeConfigSchema: z.ZodObject<{
3380
3500
  }>;
3381
3501
  /** Resolved config: every field present after defaults are applied. */
3382
3502
  export type ResolvedConfig = z.infer<typeof blumeConfigSchema>;
3503
+ /** Resolved `frontmatter.extend`: custom key → user-supplied schema. */
3504
+ export type FrontmatterExtend = Record<string, StandardSchema>;
3383
3505
  /** Resolved i18n block (present only when the project opts into i18n). */
3384
3506
  export type ResolvedI18nConfig = z.infer<typeof i18nConfigSchema>;
3385
3507
  /** A configured locale with display metadata. */
@@ -1,4 +1,4 @@
1
- import type { ResolvedI18nConfig } from "../schema.ts";
1
+ import type { FrontmatterExtend, ResolvedI18nConfig } from "../schema.ts";
2
2
  import type { Diagnostic } from "../types.ts";
3
3
  /**
4
4
  * A single content item, normalized by a source adapter. Adapters lower their
@@ -111,5 +111,7 @@ export interface NormalizeContext {
111
111
  /** Site-wide route mount point (`""` or `/seg`), prepended to every route. */
112
112
  basePath?: string;
113
113
  defaultType: string;
114
+ /** Opt-in custom frontmatter keys (`frontmatter.extend`), schema per key. */
115
+ frontmatterExtend?: FrontmatterExtend;
114
116
  i18n?: ResolvedI18nConfig;
115
117
  }
@@ -0,0 +1,41 @@
1
+ /**
2
+ * The Standard Schema interface (https://standardschema.dev), the minimal
3
+ * `~standard` surface shared by Zod 3.24+, Zod 4, Valibot, ArkType, and
4
+ * friends. Blume accepts user-supplied validation schemas (e.g.
5
+ * `frontmatter.extend`) through this interface instead of Zod's own types:
6
+ * `blume.config.ts` imports zod from the *consumer's* node_modules, which may
7
+ * be a different major version than the zod Blume bundles, and calling Zod
8
+ * methods (`.extend()`, `.safeParse()`) across instances is unsupported. The
9
+ * `~standard.validate` contract is version- and library-agnostic.
10
+ */
11
+ /** One validation failure, with an optional path into the checked value. */
12
+ export interface StandardSchemaIssue {
13
+ readonly message: string;
14
+ readonly path?: readonly (PropertyKey | {
15
+ readonly key: PropertyKey;
16
+ })[] | undefined;
17
+ }
18
+ /** A passing validation: the (possibly transformed) output value. */
19
+ export interface StandardSchemaSuccess<Output> {
20
+ readonly value: Output;
21
+ readonly issues?: undefined;
22
+ }
23
+ /** A failing validation: one or more issues. */
24
+ export interface StandardSchemaFailure {
25
+ readonly issues: readonly StandardSchemaIssue[];
26
+ }
27
+ export type StandardSchemaResult<Output> = StandardSchemaSuccess<Output> | StandardSchemaFailure;
28
+ /** A validation schema exposing the Standard Schema `~standard` contract. */
29
+ export interface StandardSchema<Input = unknown, Output = Input> {
30
+ readonly "~standard": {
31
+ readonly version: 1;
32
+ readonly vendor: string;
33
+ readonly validate: (value: unknown) => StandardSchemaResult<Output> | Promise<StandardSchemaResult<Output>>;
34
+ readonly types?: {
35
+ readonly input: Input;
36
+ readonly output: Output;
37
+ } | undefined;
38
+ };
39
+ }
40
+ /** Whether a config-supplied value implements the `~standard` contract. */
41
+ export declare const isStandardSchema: (value: unknown) => value is StandardSchema;
@@ -12,6 +12,13 @@ export interface Diagnostic {
12
12
  file?: string;
13
13
  line?: number;
14
14
  column?: number;
15
+ /**
16
+ * The built URL this diagnostic is about, for findings that are a property of
17
+ * the output rather than of a source file (`blume audit`). Set alongside
18
+ * `file`/`line` where the page maps back to authored content, so a finding can
19
+ * name both the URL that's wrong and the frontmatter line that fixes it.
20
+ */
21
+ url?: string;
15
22
  schemaPath?: string;
16
23
  suggestion?: string;
17
24
  docsUrl?: string;
@@ -96,6 +103,13 @@ export interface PageRecord {
96
103
  * `/guides/x`). Pages with the same key are translations of each other.
97
104
  */
98
105
  translationKey: string;
106
+ /**
107
+ * True for entries filled in from the fallback locale to pad a locale's
108
+ * navigation for pages it hasn't translated yet. The record's content —
109
+ * title included — belongs to the fallback locale, so per-locale content
110
+ * checks skip these.
111
+ */
112
+ fallback?: boolean;
99
113
  /**
100
114
  * Content-relative path with the leading locale directory stripped, used for
101
115
  * sidebar grouping so the locale dir is not surfaced as a nav group. Equals
@@ -110,6 +124,12 @@ export interface PageRecord {
110
124
  description?: string;
111
125
  contentType: string;
112
126
  meta: PageMeta;
127
+ /**
128
+ * Custom frontmatter values declared via `frontmatter.extend`, validated by
129
+ * the user-supplied schemas (schema output, so transforms apply). Present
130
+ * only when the project opts in and the page carries at least one value.
131
+ */
132
+ custom?: Record<string, unknown>;
113
133
  headings: Heading[];
114
134
  /** Whether the file is `.md`/`.mdx`. */
115
135
  format: "md" | "mdx";
@@ -0,0 +1,63 @@
1
+ import type { RenderOptions } from "takumi-js";
2
+ /**
3
+ * A Google Font family to load into the OG card renderer. A bare string is the
4
+ * family name (weight 400, normal style); the object form pins weight and style.
5
+ * Handed straight to Takumi's `googleFonts` helper, which fetches the family
6
+ * from Google Fonts at build and returns per-glyph coverage subsets.
7
+ */
8
+ export type OgFont = string | {
9
+ /** Google Fonts family name, e.g. `"Noto Sans JP"`. */
10
+ name: string;
11
+ /** `400`, `[400, 700]`, or a variable range like `"100..900"`. */
12
+ weight?: number | number[] | string;
13
+ /** `"normal"`, `"italic"`, or both. */
14
+ style?: "normal" | "italic" | ("normal" | "italic")[];
15
+ };
16
+ export interface OgCardPalette {
17
+ accent?: string;
18
+ background?: string;
19
+ border?: string;
20
+ foreground?: string;
21
+ muted?: string;
22
+ }
23
+ export interface OgCardOptions {
24
+ /** Large headline — the page title. */
25
+ title: string;
26
+ /** Accent color (named preset or any CSS color) for the fallback brand mark. */
27
+ accent?: string;
28
+ /** Brand/site name shown in the top-left lockup. */
29
+ brand?: string;
30
+ /** Muted subtitle under the headline (usually the site description). */
31
+ description?: string;
32
+ /**
33
+ * Inlined SVG markup of the configured logo, painted into
34
+ * the brand lockup. Falls back to an accent mark when absent.
35
+ */
36
+ logo?: string;
37
+ /** Optional colors for the generated card. */
38
+ palette?: OgCardPalette;
39
+ /** Footer-left repository slug, e.g. `owner/repo`. */
40
+ repo?: string;
41
+ /** Footer-right site host, e.g. `docs.acme.com`. */
42
+ site?: string;
43
+ /**
44
+ * Pre-fetched image entries, or a group controlling how remote images (and
45
+ * emoji glyphs) are fetched. Blume merges in a shared glyph cache; see
46
+ * {@link resolveImages}.
47
+ */
48
+ images?: RenderOptions["images"];
49
+ /**
50
+ * Google Font families for non-Latin titles. Takumi's built-in font covers
51
+ * only Latin, so a CJK (etc.) title renders as tofu without a family that
52
+ * covers its script — see {@link loadFonts}.
53
+ */
54
+ fonts?: OgFont[];
55
+ }
56
+ /**
57
+ * Truncate to `max` code points with an ellipsis. Slices by code points, not
58
+ * UTF-16 units, so cutting mid-emoji doesn't leave a lone surrogate (a broken
59
+ * glyph) before the ellipsis.
60
+ */
61
+ export declare const truncate: (value: string, max: number) => string;
62
+ /** Render a 1200x630 Open Graph card to a PNG buffer. */
63
+ export declare const renderOgImage: (options: OgCardOptions) => Promise<Uint8Array>;
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Dimensions of a generated OG card, shared by the renderer (`card.ts`) and the
3
+ * layouts that declare them as `og:image:width`/`og:image:height` so a crawler
4
+ * can lay out the card without fetching the PNG first.
5
+ *
6
+ * This lives apart from `card.ts` because that module imports the Takumi native
7
+ * binding at load; a layout importing it would drag the renderer into every
8
+ * page render (and into the prerender/SSR bundles that externalize it).
9
+ */
10
+ export declare const OG_IMAGE_WIDTH = 1200;
11
+ export declare const OG_IMAGE_HEIGHT = 630;
12
+ export declare const OG_IMAGE_TYPE = "image/png";
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Quickstart
3
- description: Install Blume, scaffold a project, and ship your first page in minutes.
3
+ description: Install Blume, scaffold a new project, and ship your first documentation page in minutes — then grow it into a full site at your own pace.
4
4
  sidebar:
5
5
  label: Quickstart
6
6
  order: 1
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Deployment
3
- description: Deploy static docs to any host, or switch to server rendering with an adapter.
3
+ description: Deploy static docs to any host with zero configuration, or switch to server-side rendering with an adapter when you need dynamic behavior.
4
4
  sidebar:
5
5
  label: Deployment
6
6
  order: 2
@@ -119,6 +119,14 @@ redirects: [{ from: "/old", to: "/new", status: 301 }];
119
119
  `from` is matched as an exact path — wildcards and pattern matching (e.g. `/blog/:slug` or `/old/*`) aren't supported. If you need pattern-based rules, handle them in an infrastructure file like `vercel.json` (which supports wildcard `source` patterns) or your host's redirect config instead. A `vercel.json` you ship in `public/` is preserved as-is.
120
120
  :::
121
121
 
122
+ :::note
123
+ Write both `from` and `to` as if mounted at root — under [`deployment.base`](#subpath-deploys) and [`basePath`](#mount-the-docs-under-a-path) alike, Blume rewrites both sides for you, so a redirect lands inside the base. A base you've already written into `to` by hand is preserved rather than doubled.
124
+ :::
125
+
126
+ ## Content types
127
+
128
+ A static build also emits a `_headers` file that pins `charset=utf-8` onto the raw AI-ready endpoints — `/<route>.md`, `/<route>.mdx`, and the `.txt` files (`llms.txt`, `llms-full.txt`). Those responses are valid UTF-8, but many static hosts serve them as `text/markdown` / `text/plain` with **no** charset, and browsers then fall back to Windows-1252 — so non-ASCII docs (Japanese, accented Latin, …) render as mojibake when the raw URL is opened directly. HTML pages are unaffected because they carry `<meta charset>`. Netlify and Cloudflare (Pages/Workers static assets) read `_headers`; hosts that don't (Vercel, S3) ignore the file harmlessly. A `_headers` you ship in `public/` is left untouched.
129
+
122
130
  ## Environment variables
123
131
 
124
132
  When a feature needs a runtime secret, Blume warns at `blume dev`/`build` if it's missing — so the problem surfaces early instead of at the first request:
@@ -78,6 +78,17 @@ openapi: {
78
78
 
79
79
  `spec` is shorthand for a single-entry `sources`, so you only reach for `sources` when you have more than one.
80
80
 
81
+ ## Authorization
82
+
83
+ Operations that declare [security requirements](https://spec.openapis.org/oas/v3.1.0#security-requirement-object) render an **Authorization** section above their parameters, and the generated code samples send a placeholder credential (`Authorization: Bearer YOUR_TOKEN`, an API-key header, or a query key — whatever the scheme calls for). There's nothing to configure: Blume reads `security` from the spec, so the reference always matches what the API actually enforces.
84
+
85
+ The OpenAPI semantics carry over as written:
86
+
87
+ - An operation's own `security` overrides the document's root default; `security: []` marks it **public** and renders no Authorization section.
88
+ - Multiple requirement entries are alternatives — rendered as "or" groups; every scheme inside one entry is required together. The first alternative feeds the code samples.
89
+ - An empty `{}` entry means auth is **optional** for that operation, and the section says so.
90
+ - OAuth2 scopes are listed per scheme; scheme `description`s from `components.securitySchemes` render inline.
91
+
81
92
  ## The Scalar renderer
82
93
 
83
94
  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"`:
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Changelog
3
- description: Author release notes as content, and Blume builds a timeline page and an RSS feed automatically.
3
+ description: Author release notes as ordinary content files or source them from GitHub Releases, and Blume builds a timeline page and an RSS feed automatically.
4
4
  ---
5
5
 
6
6
  Blume ships a changelog out of the box. Write each release as a normal content file, mark it `type: changelog`, and Blume collects every entry into a generated timeline page and an RSS feed — no layout to build, no list to maintain. Or skip the files entirely and [source your changelog from GitHub Releases](#from-github-releases).
@@ -87,7 +87,7 @@ content: {
87
87
  }
88
88
  ```
89
89
 
90
- The release name becomes the title, its tag becomes `changelog.version`, and its published date sorts the timeline. A private repo authenticates with the `GITHUB_TOKEN` environment variable. See [Content sources](/docs/content/sources#github-releases) for every option.
90
+ The release name becomes the title, its tag becomes `changelog.version`, and its published date sorts the timeline. Each release page also gets a unique meta description summarized from its notes — markdown stripped, section headings and changeset commit-hash prefixes dropped, trimmed to the search-snippet length [`blume audit`](/docs/reference/cli#auditing-the-built-site) checks for — instead of falling back to the site description. A private repo authenticates with the `GITHUB_TOKEN` environment variable. See [Content sources](/docs/content/sources#github-releases) for every option.
91
91
 
92
92
  ## The RSS feed
93
93
 
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  title: Skills
3
- description: The agent skills Blume ships — playbooks that teach a coding agent to build and maintain a Blume docs site.
3
+ description: The agent skills Blume ships — playbooks that teach a coding agent like Claude Code, Codex, or Cursor to build and maintain a Blume docs site.
4
4
  ---
5
5
 
6
6
  Blume ships [agent skills](https://docs.claude.com/en/docs/claude-code/skills) — playbooks that teach a coding agent (Claude Code, Codex, Cursor) how to do a Blume-shaped job without you explaining it. They live on GitHub in the repo's `skills/` folder and are bundled in the package at `node_modules/blume/skills/` once Blume is installed, so any agent can be pointed at a `SKILL.md` directly.