blume 1.0.2 → 1.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/dist/cli/index.js +496 -295
  3. package/dist/cli/index.js.map +18 -17
  4. package/dist/types/core/config-input.d.ts +44 -22
  5. package/dist/types/core/config.d.ts +3 -3
  6. package/dist/types/core/data.d.ts +12 -0
  7. package/dist/types/core/i18n-ui.d.ts +136 -136
  8. package/dist/types/core/schema.d.ts +502 -384
  9. package/dist/types/core/types.d.ts +10 -0
  10. package/dist/types/openapi/references.d.ts +12 -7
  11. package/docs/advanced/api-reference.mdx +11 -3
  12. package/docs/advanced/custom-pages.mdx +2 -0
  13. package/docs/configuration/ai.mdx +6 -4
  14. package/docs/configuration/index.mdx +6 -8
  15. package/docs/configuration/seo.mdx +20 -1
  16. package/docs/content/components.mdx +26 -5
  17. package/docs/content/navigation.mdx +10 -0
  18. package/docs/content/syntax.mdx +116 -4
  19. package/package.json +1 -1
  20. package/skills/blume-migrate/SKILL.md +170 -0
  21. package/skills/blume-migrate/assets/oxfmt@0.55.0.patch +20 -0
  22. package/skills/blume-migrate/references/docusaurus.md +95 -0
  23. package/skills/blume-migrate/references/fumadocs.md +95 -0
  24. package/skills/blume-migrate/references/mintlify.md +155 -0
  25. package/skills/blume-migrate/references/monorepo.md +224 -0
  26. package/skills/blume-migrate/references/nextra.md +76 -0
  27. package/skills/blume-migrate/references/starlight.md +116 -0
  28. package/skills/blume-migrate/scripts/mintlify-codemod.mjs +466 -0
  29. package/src/ai/agent-readability.ts +3 -3
  30. package/src/ai/mcp/data.ts +2 -2
  31. package/src/astro/component-slots.ts +3 -2
  32. package/src/astro/generate.ts +97 -30
  33. package/src/astro/templates.ts +140 -53
  34. package/src/blume-modules.d.ts +6 -0
  35. package/src/components/content/Callout.astro +8 -2
  36. package/src/components/content/Prompt.astro +25 -13
  37. package/src/components/layout/Header.astro +19 -10
  38. package/src/components/layout/Logo.astro +13 -1
  39. package/src/components/layout/PageFeedback.astro +1 -1
  40. package/src/components/layout/PageLayout.astro +11 -11
  41. package/src/components/layout/Pagination.astro +6 -6
  42. package/src/components/layout/ReferenceLayout.astro +1 -0
  43. package/src/components/layout/RootLayout.astro +10 -11
  44. package/src/components/layout/Search.astro +1 -1
  45. package/src/components/layout/nav-utils.ts +9 -7
  46. package/src/core/config-input.ts +47 -27
  47. package/src/core/config.ts +3 -3
  48. package/src/core/data.ts +9 -1
  49. package/src/core/navigation.ts +55 -13
  50. package/src/core/schema.ts +32 -15
  51. package/src/core/server-features.ts +1 -1
  52. package/src/core/sources/watch.ts +5 -0
  53. package/src/core/types.ts +10 -0
  54. package/src/deploy/adapter-output.ts +11 -1
  55. package/src/markdown/index.ts +2 -0
  56. package/src/markdown/language-icon.ts +2 -1
  57. package/src/markdown/table-wrap.ts +43 -0
  58. package/src/og/card.ts +39 -12
  59. package/src/og/index.ts +1 -1
  60. package/src/og/logo.ts +21 -0
  61. package/src/openapi/references.ts +19 -16
  62. package/src/registry/eject.ts +11 -5
  63. package/src/theme/entry.ts +50 -5
package/dist/cli/index.js CHANGED
@@ -525,11 +525,11 @@ Next steps:
525
525
  });
526
526
 
527
527
  // src/cli/commands/build.ts
528
- import { existsSync as existsSync15 } from "node:fs";
528
+ import { existsSync as existsSync16 } from "node:fs";
529
529
  import { readdir, stat, writeFile as writeFile7 } from "node:fs/promises";
530
530
  import { build } from "astro";
531
531
  import { defineCommand as defineCommand2 } from "citty";
532
- import { join as join24 } from "pathe";
532
+ import { join as join25 } from "pathe";
533
533
 
534
534
  // src/core/base-path.ts
535
535
  var normalizeBasePath = (input) => {
@@ -676,10 +676,10 @@ var buildAgentReadability = (project) => {
676
676
  artifacts.llmsFullTxt = abs("/llms-full.txt");
677
677
  artifacts.llmsTxt = abs("/llms.txt");
678
678
  }
679
- if (config.mcp.enabled) {
679
+ if (config.ai.mcp.enabled) {
680
680
  artifacts.mcp = {
681
681
  discovery: abs("/.well-known/mcp.json"),
682
- url: abs(config.mcp.route)
682
+ url: abs(config.ai.mcp.route)
683
683
  };
684
684
  }
685
685
  if (config.ai.ask?.enabled) {
@@ -697,7 +697,7 @@ var buildAgentReadability = (project) => {
697
697
  artifacts,
698
698
  description: config.description,
699
699
  generator: version ? `blume@${version}` : undefined,
700
- name: config.mcp.name ?? config.title,
700
+ name: config.ai.mcp.name ?? config.title,
701
701
  site
702
702
  };
703
703
  const usage = usagePolicy(config.seo.contentSignals);
@@ -1225,7 +1225,7 @@ var serverFeatures = (config) => {
1225
1225
  if (config.ai.ask?.enabled) {
1226
1226
  features.push("Ask AI");
1227
1227
  }
1228
- if (config.mcp.enabled) {
1228
+ if (config.ai.mcp.enabled) {
1229
1229
  features.push("MCP server");
1230
1230
  }
1231
1231
  if (searchProviderMeta(config.search.provider).requiresServer) {
@@ -1269,7 +1269,7 @@ var surfaceAdapterOutput = async (config, context) => {
1269
1269
  }
1270
1270
  await mkdir2(dirname4(to), { recursive: true });
1271
1271
  await rm(to, { force: true, recursive: true });
1272
- await cp(from, to, { recursive: true });
1272
+ await cp(from, to, { recursive: true, verbatimSymlinks: true });
1273
1273
  await rm(from, { force: true, recursive: true });
1274
1274
  return { from, ignore: `${rel.split("/")[0]}/`, moved: true, to };
1275
1275
  };
@@ -3972,7 +3972,7 @@ var refuseIfDevRunning = (root, action, options = {}) => {
3972
3972
  };
3973
3973
 
3974
3974
  // src/astro/generate.ts
3975
- import { existsSync as existsSync12, readFileSync as readFileSync7, realpathSync } from "node:fs";
3975
+ import { existsSync as existsSync13, readFileSync as readFileSync8, realpathSync } from "node:fs";
3976
3976
  import {
3977
3977
  lstat,
3978
3978
  mkdir as mkdir6,
@@ -3984,7 +3984,7 @@ import {
3984
3984
  } from "node:fs/promises";
3985
3985
  import { createRequire as createRequire5 } from "node:module";
3986
3986
  import { pathToFileURL as pathToFileURL3 } from "node:url";
3987
- import { basename as basename2, dirname as dirname8, join as join22, normalize as normalize3, relative as relative8 } from "pathe";
3987
+ import { basename as basename2, dirname as dirname8, join as join23, normalize as normalize3, relative as relative8 } from "pathe";
3988
3988
  import { glob as glob5 } from "tinyglobby";
3989
3989
 
3990
3990
  // src/ai/ask-data.ts
@@ -4117,8 +4117,8 @@ var buildMcpData = async (project) => {
4117
4117
  route: doc.route,
4118
4118
  title: doc.title
4119
4119
  })),
4120
- instructions: config.mcp.instructions,
4121
- name: config.mcp.name ?? config.title,
4120
+ instructions: config.ai.mcp.instructions,
4121
+ name: config.ai.mcp.name ?? config.title,
4122
4122
  navigation: graph.navigation,
4123
4123
  pages,
4124
4124
  routes,
@@ -4899,10 +4899,7 @@ var resolveReferences = (config) => [
4899
4899
  }, config.basePath),
4900
4900
  ...referencesFor("asyncapi", config.asyncapi, "Events", "scalar", NO_DISPLAY, config.basePath)
4901
4901
  ];
4902
- var referenceTabs = (config) => resolveReferences(config).map((ref) => ({
4903
- label: ref.label,
4904
- path: ref.renderer === "blume" ? withBasePath(config.basePath, ref.route) : ref.route
4905
- }));
4902
+ var referenceRoutes = (config) => resolveReferences(config).map((ref) => ref.renderer === "blume" ? withBasePath(config.basePath, ref.route) : ref.route);
4906
4903
  var blumeReferenceOf = (ref, seen, usedSlugs) => {
4907
4904
  if (ref.kind !== "openapi" || ref.renderer !== "blume") {
4908
4905
  return null;
@@ -5480,6 +5477,7 @@ import { glob as glob2 } from "tinyglobby";
5480
5477
  // src/core/sources/watch.ts
5481
5478
  var BLUME_IGNORE_DIRS = [
5482
5479
  ".blume",
5480
+ ".blume-verify",
5483
5481
  ".cache",
5484
5482
  ".git",
5485
5483
  ".next",
@@ -6377,6 +6375,12 @@ var askAiProviders = [
6377
6375
  "inkeep",
6378
6376
  "openai-compatible"
6379
6377
  ];
6378
+ var mcpConfigSchema = z2.strictObject({
6379
+ enabled: z2.boolean().default(false),
6380
+ instructions: z2.string().optional(),
6381
+ name: z2.string().optional(),
6382
+ route: z2.string().default("/mcp").transform(normalizeRoute)
6383
+ });
6380
6384
  var aiConfigSchema = z2.strictObject({
6381
6385
  ask: z2.strictObject({
6382
6386
  apiKeyEnv: z2.string().optional(),
@@ -6406,7 +6410,8 @@ var aiConfigSchema = z2.strictObject({
6406
6410
  ]).default(true).transform((value) => typeof value === "boolean" ? { enabled: value, openapi: true } : value),
6407
6411
  markdownComponents: z2.record(z2.string(), z2.custom((value) => typeof value === "function", {
6408
6412
  message: "Expected a serializer function."
6409
- })).default({})
6413
+ })).default({}),
6414
+ mcp: mcpConfigSchema.default({})
6410
6415
  });
6411
6416
  var featuredLinkSchema = z2.strictObject({
6412
6417
  href: z2.string(),
@@ -6424,7 +6429,7 @@ var navigationConfigSchema = z2.strictObject({
6424
6429
  items: z2.array(sidebarItemSchema).optional()
6425
6430
  })
6426
6431
  ]).default({}).transform((value) => Array.isArray(value) ? { display: "flat", items: value } : value),
6427
- tabs: z2.array(navTabSchema).optional()
6432
+ tabs: z2.array(navTabSchema).default([])
6428
6433
  });
6429
6434
  var exportConfigSchema = z2.union([
6430
6435
  z2.boolean(),
@@ -6433,12 +6438,6 @@ var exportConfigSchema = z2.union([
6433
6438
  pdf: z2.boolean().default(false)
6434
6439
  })
6435
6440
  ]).transform((value) => typeof value === "boolean" ? { epub: value, pdf: value } : value);
6436
- var mcpConfigSchema = z2.strictObject({
6437
- enabled: z2.boolean().default(false),
6438
- instructions: z2.string().optional(),
6439
- name: z2.string().optional(),
6440
- route: z2.string().default("/mcp").transform(normalizeRoute)
6441
- });
6442
6441
  var localeSchema = z2.strictObject({
6443
6442
  code: z2.string().min(1),
6444
6443
  dir: z2.enum(["ltr", "rtl"]).default("ltr"),
@@ -6499,8 +6498,18 @@ var xConfigSchema = z2.strictObject({
6499
6498
  creator: xHandleSchema,
6500
6499
  handle: xHandleSchema
6501
6500
  });
6501
+ var ogColorSchema = z2.string().regex(/^#(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/iu);
6502
+ var ogPaletteSchema = z2.strictObject({
6503
+ accent: ogColorSchema.optional(),
6504
+ background: ogColorSchema.optional(),
6505
+ border: ogColorSchema.optional(),
6506
+ foreground: ogColorSchema.optional(),
6507
+ muted: ogColorSchema.optional()
6508
+ });
6502
6509
  var ogConfigSchema = z2.strictObject({
6503
- enabled: z2.boolean().optional()
6510
+ enabled: z2.boolean().optional(),
6511
+ logo: z2.string().optional(),
6512
+ palette: ogPaletteSchema.optional()
6504
6513
  });
6505
6514
  var rssConfigSchema = z2.strictObject({
6506
6515
  enabled: z2.boolean().default(true),
@@ -6625,7 +6634,6 @@ var blumeConfigSchema = z2.strictObject({
6625
6634
  lastModified: lastModifiedConfigSchema.default(false),
6626
6635
  logo: logoConfigSchema.optional(),
6627
6636
  markdown: markdownConfigSchema.default({}),
6628
- mcp: mcpConfigSchema.default({}),
6629
6637
  navigation: navigationConfigSchema.default({}),
6630
6638
  openapi: openapiConfigSchema.default({}),
6631
6639
  react: reactConfigSchema.default({}),
@@ -7646,14 +7654,29 @@ var resolveTsconfigAliases = (root) => {
7646
7654
  return aliases;
7647
7655
  };
7648
7656
 
7657
+ // src/og/logo.ts
7658
+ import { existsSync as existsSync11, readFileSync as readFileSync5 } from "node:fs";
7659
+ import { join as join18 } from "pathe";
7660
+ var resolveOgLogo = (project, source) => {
7661
+ if (!source?.toLowerCase().endsWith(".svg")) {
7662
+ return;
7663
+ }
7664
+ const relative6 = source.replace(/^\//u, "");
7665
+ const file = [
7666
+ join18(project.context.root, "public", relative6),
7667
+ join18(project.context.root, relative6)
7668
+ ].find((path) => existsSync11(path));
7669
+ return file ? readFileSync5(file, "utf-8") : undefined;
7670
+ };
7671
+
7649
7672
  // src/openapi/scalar.ts
7650
7673
  import { readFile as readFile8 } from "node:fs/promises";
7651
- import { isAbsolute as isAbsolute8, join as join19 } from "pathe";
7674
+ import { isAbsolute as isAbsolute8, join as join20 } from "pathe";
7652
7675
 
7653
7676
  // src/astro/templates.ts
7654
- import { existsSync as existsSync11, readFileSync as readFileSync5 } from "node:fs";
7677
+ import { existsSync as existsSync12, readFileSync as readFileSync6 } from "node:fs";
7655
7678
  import { pathToFileURL as pathToFileURL2 } from "node:url";
7656
- import { dirname as dirname7, isAbsolute as isAbsolute7, join as join18, relative as relative6 } from "pathe";
7679
+ import { dirname as dirname7, isAbsolute as isAbsolute7, join as join19, relative as relative6 } from "pathe";
7657
7680
  var WORKSPACE_MARKERS = [
7658
7681
  ".git",
7659
7682
  "bun.lock",
@@ -7664,17 +7687,17 @@ var WORKSPACE_MARKERS = [
7664
7687
  "yarn.lock"
7665
7688
  ];
7666
7689
  var hasWorkspacesField = (pkgPath) => {
7667
- if (!existsSync11(pkgPath)) {
7690
+ if (!existsSync12(pkgPath)) {
7668
7691
  return false;
7669
7692
  }
7670
7693
  try {
7671
- const pkg = JSON.parse(readFileSync5(pkgPath, "utf-8"));
7694
+ const pkg = JSON.parse(readFileSync6(pkgPath, "utf-8"));
7672
7695
  return pkg.workspaces !== undefined;
7673
7696
  } catch {
7674
7697
  return false;
7675
7698
  }
7676
7699
  };
7677
- var hasWorkspaceMarker = (dir) => hasWorkspacesField(join18(dir, "package.json")) || WORKSPACE_MARKERS.some((marker) => existsSync11(join18(dir, marker)));
7700
+ var hasWorkspaceMarker = (dir) => hasWorkspacesField(join19(dir, "package.json")) || WORKSPACE_MARKERS.some((marker) => existsSync12(join19(dir, marker)));
7678
7701
  var findWorkspaceRoot = (start) => {
7679
7702
  let dir = start;
7680
7703
  for (;; ) {
@@ -7697,6 +7720,23 @@ var ADAPTER_IMPORTS = {
7697
7720
  var ADAPTER_OPTIONS = {
7698
7721
  node: '{ mode: "standalone" }'
7699
7722
  };
7723
+ var WRANGLER_CONFIG_FILES = [
7724
+ "wrangler.jsonc",
7725
+ "wrangler.json",
7726
+ "wrangler.toml"
7727
+ ];
7728
+ var resolveCloudflareAdapterArgs = (context) => {
7729
+ const args = ['prerenderEnvironment: "node"'];
7730
+ const wranglerPath = WRANGLER_CONFIG_FILES.map((file) => join19(context.root, file)).find((file) => existsSync12(file));
7731
+ if (wranglerPath) {
7732
+ let configPath = relative6(context.outDir, wranglerPath);
7733
+ if (!configPath.startsWith(".") && !configPath.startsWith("/")) {
7734
+ configPath = `./${configPath}`;
7735
+ }
7736
+ args.push(`configPath: ${JSON.stringify(configPath)}`);
7737
+ }
7738
+ return `{ ${args.join(", ")} }`;
7739
+ };
7700
7740
  var runtimeDependencies = (options) => {
7701
7741
  const { config, needsReact, needsSvelte, needsVue } = options;
7702
7742
  const deps = ["@astrojs/mdx"];
@@ -7748,6 +7788,7 @@ var reactIntegration = (compilerPath) => compilerPath ? `react({ babel: { plugin
7748
7788
  var astroConfigTemplate = (options) => {
7749
7789
  const { context, config, needsReact, pages, dataPath, themePath } = options;
7750
7790
  const {
7791
+ askPath,
7751
7792
  contentRoutes,
7752
7793
  examplesPath,
7753
7794
  examplesThemePath,
@@ -7762,7 +7803,15 @@ var astroConfigTemplate = (options) => {
7762
7803
  const fsAllow = [...new Set([findWorkspaceRoot(context.root), context.root])];
7763
7804
  const adapterImport = server && deployment.adapter ? `import adapter from "${ADAPTER_IMPORTS[deployment.adapter]}";
7764
7805
  ` : "";
7765
- const adapterArgs = server && deployment.adapter ? ADAPTER_OPTIONS[deployment.adapter] ?? "" : "";
7806
+ const adapterArgs = (() => {
7807
+ if (!server || !deployment.adapter) {
7808
+ return "";
7809
+ }
7810
+ if (deployment.adapter === "cloudflare") {
7811
+ return resolveCloudflareAdapterArgs(context);
7812
+ }
7813
+ return ADAPTER_OPTIONS[deployment.adapter] ?? "";
7814
+ })();
7766
7815
  const adapterOption = server && deployment.adapter ? `
7767
7816
  adapter: adapter(${adapterArgs}),` : "";
7768
7817
  const siteOption = deployment.site ? `
@@ -7849,6 +7898,18 @@ export default defineConfig({
7849
7898
  devToolbar: { enabled: false },
7850
7899
  vite: {
7851
7900
  plugins: [tailwindcss(), prerenderDepsPlugin(), serverAppResolvePlugin()],
7901
+ // Mermaid (lazy-loaded client-side for diagrams) statically imports dayjs as
7902
+ // CJS (\`dayjs/dayjs.min.js\`). In dev, an un-pre-bundled dependency is served
7903
+ // as raw ESM, and that UMD file exposes no \`default\` export, so mermaid
7904
+ // throws on load and diagrams render blank. Forcing mermaid through the dep
7905
+ // optimizer bundles dayjs with correct CJS interop. In a standalone install
7906
+ // Blume's dynamic \`import("mermaid")\` lives inside \`node_modules/blume\`,
7907
+ // which Vite's optimizer scan doesn't crawl, so mermaid is never discovered
7908
+ // on its own — hence the explicit include. mermaid resolves through the
7909
+ // \`blume\` package (it isn't a direct dep of the generated project), so the
7910
+ // nested \`blume > mermaid\` form is required. Production (Rollup) already
7911
+ // handles the interop, so this only affects dev.
7912
+ optimizeDeps: { include: ["blume > mermaid"] },
7852
7913
  // Blume's render-time deps are forced external on both build environments so
7853
7914
  // native bindings resolve at runtime and isolated linkers don't bundle
7854
7915
  // symlinked store copies (which would surface their children as unresolvable
@@ -7867,6 +7928,7 @@ export default defineConfig({
7867
7928
  },
7868
7929
  resolve: {
7869
7930
  alias: {
7931
+ "blume:ask": ${JSON.stringify(askPath)},
7870
7932
  "blume:data": ${JSON.stringify(dataPath)},
7871
7933
  "blume:examples": ${JSON.stringify(examplesPath)},
7872
7934
  "blume:examples-theme": ${JSON.stringify(examplesThemePath)},
@@ -7886,14 +7948,14 @@ export default defineConfig({
7886
7948
  // self-hosted fonts) -- pure noise the loader logs as "No entry type
7887
7949
  // found". Vite appends this to its default ignores.
7888
7950
  watch: {
7889
- ignored: ${JSON.stringify([join18(context.outDir, ".astro", "**")])},
7951
+ ignored: ${JSON.stringify([join19(context.outDir, ".astro", "**")])},
7890
7952
  },
7891
7953
  },
7892
7954
  },
7893
7955
  });
7894
7956
  `;
7895
7957
  };
7896
- var stagedContentDir = (outDir) => join18(outDir, "content");
7958
+ var stagedContentDir = (outDir) => join19(outDir, "content");
7897
7959
  var astroGlobBase = (base) => isAbsolute7(base) ? pathToFileURL2(base).href : base;
7898
7960
  var contentConfigTemplate = (options) => {
7899
7961
  const { context, config } = options;
@@ -8038,6 +8100,22 @@ ${setup}
8038
8100
  ${handler}
8039
8101
  `;
8040
8102
  };
8103
+ var askComponentTemplate = (askEnabled) => askEnabled ? `---
8104
+ // Generated by Blume. Do not edit.
8105
+ import AskAI from "blume/components/islands/AskAI.astro";
8106
+ import data from "blume:data";
8107
+
8108
+ const { strings } = Astro.props;
8109
+ ---
8110
+
8111
+ <AskAI strings={strings ?? data.ui.ask} suggestions={data.config.ask?.suggestions ?? []} />
8112
+ ` : `---
8113
+ // Generated by Blume. Do not edit.
8114
+ // Ask AI is off (\`ai.ask.enabled\`), so the header's Ask trigger renders nothing.
8115
+ // Deliberately imports no React island, keeping the JSX renderer out of projects
8116
+ // that don't need it.
8117
+ ---
8118
+ `;
8041
8119
  var searchEndpointTemplate = () => `// Generated by Blume. Do not edit.
8042
8120
  import documents from "../generated/search.json";
8043
8121
 
@@ -8160,9 +8238,10 @@ export function getStaticPaths() {
8160
8238
  }));
8161
8239
  }
8162
8240
 
8163
- export function GET({ props }) {
8164
- const entry = raw[props.route];
8165
- return new Response(entry ? ${kind === "md" ? "(entry.md ?? entry.mdx)" : "entry.mdx"} : "", {
8241
+ export function GET({ props }: { props: { route: string } }) {
8242
+ const entries = raw as Record<string, { md?: string; mdx?: string }>;
8243
+ const entry = entries[props.route];
8244
+ return new Response(entry ? ${kind === "md" ? '(entry.md ?? entry.mdx ?? "")' : '(entry.mdx ?? "")'} : "", {
8166
8245
  headers: { "Content-Type": "text/markdown; charset=utf-8" },
8167
8246
  });
8168
8247
  }
@@ -8206,15 +8285,16 @@ export function getStaticPaths() {
8206
8285
  }));
8207
8286
  }
8208
8287
 
8209
- export function GET({ props }) {
8210
- return new Response(feeds[props.section] ?? "", {
8288
+ export function GET({ props }: { props: { section: string } }) {
8289
+ const bySection = feeds as Record<string, string>;
8290
+ return new Response(bySection[props.section] ?? "", {
8211
8291
  headers: { "Content-Type": "application/rss+xml; charset=utf-8" },
8212
8292
  });
8213
8293
  }
8214
8294
  `;
8215
8295
  var ogEndpointTemplate = (customRoutes = []) => `// Generated by Blume. Do not edit.
8216
8296
  import { renderOgImage } from "blume/og";
8217
- import data from "../../generated/data.json";
8297
+ import data from "blume:data";
8218
8298
 
8219
8299
  export const prerender = true;
8220
8300
 
@@ -8222,9 +8302,9 @@ export const prerender = true;
8222
8302
  const customRoutes = ${JSON.stringify(customRoutes)};
8223
8303
 
8224
8304
  export function getStaticPaths() {
8225
- const seen = new Set();
8226
- const paths = [];
8227
- const add = (slug, title) => {
8305
+ const seen = new Set<string>();
8306
+ const paths: { params: { slug: string }; props: { title: string } }[] = [];
8307
+ const add = (slug: string, title: string) => {
8228
8308
  if (seen.has(slug)) {
8229
8309
  return;
8230
8310
  }
@@ -8257,17 +8337,18 @@ const siteHost = (() => {
8257
8337
  }
8258
8338
  })();
8259
8339
 
8260
- export async function GET({ props }) {
8340
+ export async function GET({ props }: { props: { title: string } }) {
8261
8341
  const png = await renderOgImage({
8262
- accent: data.config.theme.accent.light,
8342
+ accent: data.config.og.palette?.accent ?? data.config.theme.accent.light,
8263
8343
  brand: data.config.title,
8264
8344
  description: data.config.description,
8265
- logo: data.config.logo?.svg,
8345
+ logo: data.config.og.logo,
8346
+ palette: data.config.og.palette,
8266
8347
  repo: repoSlug,
8267
8348
  site: siteHost,
8268
8349
  title: props.title,
8269
8350
  });
8270
- return new Response(png, {
8351
+ return new Response(new Uint8Array(png), {
8271
8352
  headers: {
8272
8353
  "Cache-Control": "public, max-age=31536000, immutable",
8273
8354
  "Content-Type": "image/png",
@@ -8317,10 +8398,6 @@ const htmlLang = i18n ? i18n.defaultLocale : "en";
8317
8398
  </ReferenceLayout>
8318
8399
  `;
8319
8400
  var catchAllPageTemplate = (options) => {
8320
- const askImport = options.askEnabled ? `import AskAI from "blume/components/islands/AskAI.astro";
8321
- ` : "";
8322
- const askSlot = options.askEnabled ? `
8323
- <AskAI slot="ask" strings={ui.ask} suggestions={data.config.ask?.suggestions ?? []} />` : "";
8324
8401
  const mathImport = options.mathEnabled ? `import Math from "blume/components/content/Math.astro";
8325
8402
  ` : "";
8326
8403
  const mathEntry = options.mathEnabled ? `Math,
@@ -8330,10 +8407,10 @@ var catchAllPageTemplate = (options) => {
8330
8407
  return `---
8331
8408
  // Generated by Blume. Do not edit.
8332
8409
  import { getEntry, render } from "astro:content";
8410
+ import type { CollectionKey } from "astro:content";
8333
8411
  import RootLayout from "blume/components/layout/RootLayout.astro";
8334
8412
  import { withBase } from "blume/components/islands/base-path.ts";
8335
8413
  import { resolveSlot } from "blume/components/layout/overrides.ts";
8336
- ${askImport}
8337
8414
  import Accordion from "blume/components/content/Accordion.astro";
8338
8415
  import AccordionItem from "blume/components/content/AccordionItem.astro";
8339
8416
  import AutoTypeTable from "blume/components/content/AutoTypeTable.astro";
@@ -8374,7 +8451,7 @@ import ApiTagOperations from "blume/components/openapi/ApiTagOperations.astro";
8374
8451
  import Operation from "blume/components/openapi/Operation.astro";
8375
8452
  ${mathImport}import { mdxComponents as userMdx, layoutOverrides } from "../generated/components.ts";
8376
8453
  import { islandComponents } from "../generated/islands.ts";
8377
- import data from "../generated/data.json";
8454
+ import data from "blume:data";
8378
8455
 
8379
8456
  const Color = Object.assign(ColorRoot, { Item: ColorItem, Row: ColorRow });
8380
8457
  const Tree = Object.assign(TreeRoot, { File: TreeFile, Folder: TreeFolder });
@@ -8443,7 +8520,7 @@ export function getStaticPaths() {
8443
8520
  }
8444
8521
 
8445
8522
  const { entryId, collection, route, title, indexable, editUrl, lastModified, locale, alternates, fallback } = Astro.props;
8446
- const entry = await getEntry(collection, entryId);
8523
+ const entry = await getEntry(collection as CollectionKey, entryId);
8447
8524
  if (!entry) {
8448
8525
  return new Response(null, { status: 404 });
8449
8526
  }
@@ -8478,18 +8555,18 @@ const canonical =
8478
8555
  // Locale resolution. With i18n on, pick the active locale's nav + dictionary,
8479
8556
  // build hreflang alternates, and derive the language-switcher targets.
8480
8557
  const i18n = data.config.i18n;
8481
- const localePrefix = (codeArg) =>
8558
+ const localePrefix = (codeArg: string) =>
8482
8559
  i18n && codeArg === i18n.defaultLocale && i18n.hideDefaultLocalePrefix
8483
8560
  ? ""
8484
8561
  : \`/\${codeArg}\`;
8485
- const localizeRoute = (logical, codeArg) => {
8562
+ const localizeRoute = (logical: string, codeArg: string) => {
8486
8563
  const prefix = localePrefix(codeArg);
8487
8564
  if (!prefix) {
8488
8565
  return logical;
8489
8566
  }
8490
8567
  return logical === "/" ? prefix : \`\${prefix}\${logical}\`;
8491
8568
  };
8492
- const stripLocale = (path, codeArg) => {
8569
+ const stripLocale = (path: string, codeArg: string) => {
8493
8570
  const prefix = localePrefix(codeArg);
8494
8571
  return prefix && path.startsWith(prefix) ? path.slice(prefix.length) || "/" : path;
8495
8572
  };
@@ -8506,7 +8583,7 @@ const contentLocale =
8506
8583
  const contentDir = i18n
8507
8584
  ? (i18n.locales.find((l) => l.code === contentLocale)?.dir ?? "ltr")
8508
8585
  : "ltr";
8509
- const absolute = (path) => {
8586
+ const absolute = (path: string) => {
8510
8587
  const p = withBase(path);
8511
8588
  return base + (p === "/" ? "" : p);
8512
8589
  };
@@ -8570,7 +8647,6 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
8570
8647
  canonical={canonical}
8571
8648
  editUrl={editUrl}
8572
8649
  feedback={data.config.feedback}
8573
- askEnabled={${options.askEnabled}}
8574
8650
  exportPdf={${options.exportPdf}}
8575
8651
  exportEpub={${options.exportEpub}}
8576
8652
  feeds={data.feeds}
@@ -8580,7 +8656,7 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
8580
8656
  lastModified={lastModified}
8581
8657
  noindex={seo.noindex}
8582
8658
  structuredDataEnabled={data.config.structuredData}
8583
- >${askSlot}
8659
+ >
8584
8660
  <h1>{title}</h1>
8585
8661
  {frontmatter.description && <p class="text-lg text-muted-foreground">{frontmatter.description}</p>}
8586
8662
  <Content components={components} />
@@ -8588,10 +8664,6 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
8588
8664
  `;
8589
8665
  };
8590
8666
  var changelogIndexTemplate = (options) => {
8591
- const askImport = options.askEnabled ? `import AskAI from "blume/components/islands/AskAI.astro";
8592
- ` : "";
8593
- const askSlot = options.askEnabled ? `
8594
- <AskAI slot="ask" strings={data.ui.ask} suggestions={data.config.ask?.suggestions ?? []} />` : "";
8595
8667
  const clientData = options.needsReact ? `
8596
8668
  clientData={{ config: data.config, navigation: data.navigation, page: { route: "/changelog", title: pageTitle } }}` : "";
8597
8669
  const stagedSpread = options.staged ? `
@@ -8604,14 +8676,15 @@ import Update from "blume/components/content/Update.astro";
8604
8676
  import { withBase } from "blume/components/islands/base-path.ts";
8605
8677
  import { resolveSlot } from "blume/components/layout/overrides.ts";
8606
8678
  import { layoutOverrides } from "../generated/components.ts";
8607
- ${askImport}import data from "../generated/data.json";
8679
+ import data from "blume:data";
8608
8680
 
8609
8681
  export const prerender = true;
8610
8682
 
8611
- const entryDate = (entry) =>
8612
- entry.data.date ?? entry.data.changelog?.date ?? null;
8683
+ const entryDate = (entry: {
8684
+ data: { date?: string | null; changelog?: { date?: string | null } | null };
8685
+ }) => entry.data.date ?? entry.data.changelog?.date ?? null;
8613
8686
 
8614
- const toTime = (value) => {
8687
+ const toTime = (value: string | null | undefined) => {
8615
8688
  if (!value) {
8616
8689
  return 0;
8617
8690
  }
@@ -8619,7 +8692,7 @@ const toTime = (value) => {
8619
8692
  return Number.isNaN(date.getTime()) ? 0 : date.getTime();
8620
8693
  };
8621
8694
 
8622
- const formatDate = (value) => {
8695
+ const formatDate = (value: string | null | undefined) => {
8623
8696
  if (!value) {
8624
8697
  return;
8625
8698
  }
@@ -8632,14 +8705,14 @@ const formatDate = (value) => {
8632
8705
  }).format(date);
8633
8706
  };
8634
8707
 
8635
- const slugify = (text) =>
8708
+ const slugify = (text: string) =>
8636
8709
  text.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "") ||
8637
8710
  "update";
8638
8711
 
8639
8712
  // The major of a version's embedded semver (\`1.2.3\` -> 1, \`pkg@2.0.0\` -> 2), or
8640
8713
  // null when there is no full major.minor.patch to key on. Drives the changelog's
8641
8714
  // group-by-major pagination, so it tolerates the scoped tags monorepos publish.
8642
- const majorVersion = (version) => {
8715
+ const majorVersion = (version: string | null | undefined) => {
8643
8716
  const match = /(\\d+)\\.\\d+\\.\\d+/.exec(String(version ?? ""));
8644
8717
  return match ? Number(match[1]) : null;
8645
8718
  };
@@ -8671,7 +8744,7 @@ const items = await Promise.all(
8671
8744
  return {
8672
8745
  Content: (await render(entry)).Content,
8673
8746
  date: formatDate(entryDate(entry)),
8674
- href: routeByEntry.get(entry.id) ?? null,
8747
+ href: routeByEntry.get(entry.id) ?? undefined,
8675
8748
  id: slugify(label),
8676
8749
  label,
8677
8750
  major: majorVersion(entry.data.changelog?.version),
@@ -8700,7 +8773,9 @@ for (const item of items) {
8700
8773
  // semver and they span more than one major line. Older majors then collapse
8701
8774
  // into groups the reader reveals one at a time; otherwise the timeline is flat.
8702
8775
  const majors = items.every((item) => item.major !== null)
8703
- ? [...new Set(items.map((item) => item.major))].toSorted((a, b) => b - a)
8776
+ ? [...new Set(items.map((item) => item.major))]
8777
+ .filter((major): major is number => major !== null)
8778
+ .toSorted((a, b) => b - a)
8704
8779
  : [];
8705
8780
  const paginate = majors.length > 1;
8706
8781
  const majorGroups = majors.map((major) => ({
@@ -8773,14 +8848,13 @@ const LayoutComponent = resolveSlot(layoutOverrides.Layout, RootLayout);
8773
8848
  ogImage={null}
8774
8849
  x={data.config.x}
8775
8850
  canonical={canonical}
8776
- askEnabled={${options.askEnabled}}
8777
8851
  exportPdf={${options.exportPdf}}
8778
8852
  exportEpub={${options.exportEpub}}
8779
8853
  feeds={data.feeds}
8780
8854
  siteUrl={data.config.site}
8781
8855
  noindex={false}
8782
8856
  structuredDataEnabled={data.config.structuredData}
8783
- >${askSlot}
8857
+ >
8784
8858
  <h1>{changelogTitle}</h1>
8785
8859
  {
8786
8860
  items.length === 0 ? (
@@ -8839,7 +8913,7 @@ var notFoundPageTemplate = () => `---
8839
8913
  // Generated by Blume. Do not edit. Override by adding \`pages/404.astro\`.
8840
8914
  import PageLayout from "blume/components/layout/PageLayout.astro";
8841
8915
  import { withBase } from "blume/components/islands/base-path.ts";
8842
- import data from "../generated/data.json";
8916
+ import data from "blume:data";
8843
8917
 
8844
8918
  export const prerender = true;
8845
8919
 
@@ -8956,7 +9030,10 @@ export const getStaticPaths = () =>
8956
9030
  Object.keys(examples).map((path) => ({ params: { path } }));
8957
9031
 
8958
9032
  const { path } = Astro.params;
8959
- const entry = examples[path];
9033
+ const entry = path ? examples[path] : undefined;
9034
+ if (!entry) {
9035
+ return new Response(null, { status: 404 });
9036
+ }
8960
9037
  const Example = entry.Component;
8961
9038
  ---
8962
9039
 
@@ -9000,11 +9077,24 @@ const Example = entry.Component;
9000
9077
  var envTemplate = () => `/// <reference path="../.astro/types.d.ts" />
9001
9078
  /// <reference types="astro/client" />
9002
9079
 
9080
+ declare module "blume:ask" {
9081
+ const Ask: typeof import("blume/components/islands/AskAI.astro").default;
9082
+ export default Ask;
9083
+ }
9084
+
9003
9085
  declare module "blume:data" {
9004
9086
  const data: import("blume").BlumeData;
9005
9087
  export default data;
9006
9088
  }
9007
9089
 
9090
+ declare module "blume:examples" {
9091
+ type Examples = typeof import("./generated/examples.ts").examples;
9092
+ export const examples: Record<string, Examples[keyof Examples]>;
9093
+ export const examplesBase: string;
9094
+ }
9095
+
9096
+ declare module "blume:examples-theme";
9097
+
9008
9098
  declare module "blume:openapi" {
9009
9099
  const specs: import("blume/openapi/model.ts").OpenApiData;
9010
9100
  export default specs;
@@ -9135,7 +9225,7 @@ var specConfiguration = async (spec, root) => {
9135
9225
  if (URL_SPEC2.test(spec)) {
9136
9226
  return { config: { url: spec } };
9137
9227
  }
9138
- const absolute = isAbsolute8(spec) ? spec : join19(root, spec);
9228
+ const absolute = isAbsolute8(spec) ? spec : join20(root, spec);
9139
9229
  try {
9140
9230
  return { config: { content: await readFile8(absolute, "utf-8") } };
9141
9231
  } catch {
@@ -9201,6 +9291,13 @@ var buildReferenceFiles = async (options) => {
9201
9291
  // src/theme/entry.ts
9202
9292
  var DARK_VARIANT = `/* Dark mode is driven by data-theme on the <html> element. */
9203
9293
  @custom-variant dark (&:where([data-theme="dark"], [data-theme="dark"] *));`;
9294
+ var COLOR_SCHEME_DEFAULTS = `:root {
9295
+ color-scheme: light;
9296
+ }
9297
+
9298
+ :root[data-theme="dark"] {
9299
+ color-scheme: dark;
9300
+ }`;
9204
9301
  var TOKEN_DEFAULTS = `:root {
9205
9302
  --blume-background: oklch(1 0 0);
9206
9303
  --blume-background-image: none;
@@ -9306,6 +9403,8 @@ ${options.sources.map((source) => `@source "${source}";`).join(`
9306
9403
 
9307
9404
  ${DARK_VARIANT}
9308
9405
 
9406
+ ${COLOR_SCHEME_DEFAULTS}
9407
+
9309
9408
  ${TOKEN_DEFAULTS}
9310
9409
 
9311
9410
  ${THEME_MAPPING}
@@ -9551,13 +9650,16 @@ blume-diff {
9551
9650
 
9552
9651
  /* Language icon (simple-icons) sits at the header's left edge; the label shifts
9553
9652
  right to make room. Injected at build time by the language-icon transformer.
9554
- Shown only for top-level prose blocks, which have a header flush code in
9555
- tabs and API panels is nested deeper, so the child combinator skips it. */
9653
+ The icon is gated on data-language (the header bar), not just data-icon: the
9654
+ transformer runs whenever icons are on, but a header-less standalone
9655
+ CodeBlock (no title) never gets data-language, so without this gate the
9656
+ absolutely-positioned icon would overlap the first code line. Fenced code
9657
+ and titled blocks always have a header, so the icon shows there. */
9556
9658
  .blume-lang-icon {
9557
9659
  display: none;
9558
9660
  }
9559
9661
 
9560
- .prose > :where(pre[data-icon]) > .blume-lang-icon {
9662
+ .prose > :where(pre[data-language][data-icon]) > .blume-lang-icon {
9561
9663
  color: var(--blume-muted-foreground);
9562
9664
  display: block;
9563
9665
  height: 0.875rem;
@@ -9567,7 +9669,7 @@ blume-diff {
9567
9669
  width: 0.875rem;
9568
9670
  }
9569
9671
 
9570
- .prose > :where(pre[data-icon])::before {
9672
+ .prose > :where(pre[data-language][data-icon])::before {
9571
9673
  padding-left: 2.5rem;
9572
9674
  }
9573
9675
 
@@ -9618,11 +9720,40 @@ blume-diff {
9618
9720
  font-size: 0.8125rem;
9619
9721
  }
9620
9722
 
9723
+ /* Scroll wide tables in place; the wrapper is added by the blume:table-wrap hast
9724
+ plugin. The border+radius frames the scroll viewport so a clipped wide table
9725
+ reads as scrollable rather than cut off, and carries the table's vertical
9726
+ rhythm (the inner table's own margin is zeroed so it fills the frame). */
9727
+ .blume-table-scroll {
9728
+ overflow-x: auto;
9729
+ -webkit-overflow-scrolling: touch;
9730
+ margin: 1.5rem 0;
9731
+ border: 1px solid var(--blume-border);
9732
+ border-radius: var(--blume-radius);
9733
+ }
9734
+ .blume-table-scroll > table {
9735
+ margin: 0;
9736
+ }
9737
+ /* Restore inner padding on every cell. Typography zeroes the first/last cell's
9738
+ inline padding so a borderless table aligns to the prose margin; inside the
9739
+ framed wrapper that leaves edge text touching the border. The :is() selector
9740
+ outweighs Typography's :where()-scoped rules so the outer columns get it too. */
9741
+ .blume-table-scroll :is(th, td) {
9742
+ padding: 0.5rem 0.75rem;
9743
+ }
9744
+ /* Keep column labels on one line so a two-word header does not wrap into a
9745
+ ragged stack; the table just scrolls a little wider instead. Body cells keep
9746
+ wrapping so a wide table stays a scroll away, not an endless horizontal run. */
9747
+ .blume-table-scroll th {
9748
+ white-space: nowrap;
9749
+ }
9750
+
9621
9751
  /* GFM renders cells as <td><code> directly, which the descendant form alone
9622
9752
  never matches (a cell is not its own descendant). */
9623
9753
  .prose :where(td, th) > code,
9624
9754
  .prose :where(td, th) :not(pre) > code {
9625
- white-space: nowrap;
9755
+ overflow-wrap: break-word;
9756
+ white-space: normal;
9626
9757
  }
9627
9758
 
9628
9759
  blume-tabs pre,
@@ -9862,6 +9993,8 @@ ${options.sources.map((source) => `@source "${source}";`).join(`
9862
9993
 
9863
9994
  ${DARK_VARIANT}
9864
9995
 
9996
+ ${COLOR_SCHEME_DEFAULTS}
9997
+
9865
9998
  ${TOKEN_DEFAULTS}
9866
9999
 
9867
10000
  ${THEME_MAPPING}
@@ -9882,7 +10015,7 @@ ${options.userCss}
9882
10015
  `;
9883
10016
 
9884
10017
  // src/theme/twoslash.ts
9885
- import { readFileSync as readFileSync6 } from "node:fs";
10018
+ import { readFileSync as readFileSync7 } from "node:fs";
9886
10019
  import { createRequire as createRequire4 } from "node:module";
9887
10020
  var require2 = createRequire4(import.meta.url);
9888
10021
  var OVERRIDES = `
@@ -9949,14 +10082,15 @@ var OVERRIDES = `
9949
10082
  `;
9950
10083
  var twoslashCss = () => {
9951
10084
  const file = require2.resolve("@shikijs/twoslash/style-rich.css");
9952
- return `${readFileSync6(file, "utf-8")}
10085
+ return `${readFileSync7(file, "utf-8")}
9953
10086
  ${OVERRIDES}`;
9954
10087
  };
9955
10088
 
9956
10089
  // src/astro/component-slots.ts
9957
10090
  var EMPTY_MODULE = `// Generated by Blume. Do not edit.
9958
- export const mdxComponents = {};
9959
- export const layoutOverrides = {};
10091
+ import type { ComponentOverride } from "blume/core/define-components.ts";
10092
+ export const mdxComponents: Record<string, ComponentOverride> = {};
10093
+ export const layoutOverrides: Record<string, ComponentOverride> = {};
9960
10094
  `;
9961
10095
  var attributeValue = (value) => value.replaceAll(/["\n\r]/gu, " ").trim();
9962
10096
  var CLIENT_LOAD = "client:load";
@@ -10054,12 +10188,12 @@ export const layoutOverrides = { ...(overrides.layout ?? {})${layoutEntries.leng
10054
10188
 
10055
10189
  // src/astro/examples.ts
10056
10190
  import { readFile as readFile10 } from "node:fs/promises";
10057
- import { join as join21, relative as relative7 } from "pathe";
10191
+ import { join as join22, relative as relative7 } from "pathe";
10058
10192
  import { glob as glob4 } from "tinyglobby";
10059
10193
 
10060
10194
  // src/astro/islands.ts
10061
10195
  import { readFile as readFile9 } from "node:fs/promises";
10062
- import { basename, join as join20 } from "pathe";
10196
+ import { basename, join as join21 } from "pathe";
10063
10197
  import { glob as glob3 } from "tinyglobby";
10064
10198
  var DEFAULT_CLIENT = "visible";
10065
10199
  var VALID_MODES = new Set([
@@ -10088,7 +10222,7 @@ var readClientMode = (source, file, warnings) => {
10088
10222
  return mode;
10089
10223
  };
10090
10224
  var discoverIslands = async (root) => {
10091
- const dir = join20(root, "islands");
10225
+ const dir = join21(root, "islands");
10092
10226
  const matches = await glob3(["**/*.{jsx,svelte,tsx,vue}"], {
10093
10227
  absolute: true,
10094
10228
  cwd: dir,
@@ -10151,7 +10285,7 @@ var splitGlobBase = (pattern) => {
10151
10285
  };
10152
10286
  var discoverExamples = async (root, pattern = "examples") => {
10153
10287
  const { base, rest } = GLOB_MAGIC.test(pattern) ? splitGlobBase(pattern) : { base: pattern, rest: DEFAULT_EXAMPLE_GLOB };
10154
- const dir = join21(root, base);
10288
+ const dir = join22(root, base);
10155
10289
  const matches = await glob4([rest], {
10156
10290
  absolute: true,
10157
10291
  cwd: dir,
@@ -10191,10 +10325,10 @@ var discoverExamples = async (root, pattern = "examples") => {
10191
10325
  };
10192
10326
 
10193
10327
  // src/astro/generate.ts
10194
- var BLUME_SRC = join22(packageRoot(), "src");
10328
+ var BLUME_SRC = join23(packageRoot(), "src");
10195
10329
  var canResolveFrom = (fromDir, spec) => {
10196
10330
  try {
10197
- createRequire5(pathToFileURL3(join22(fromDir, "_.js")).href).resolve(spec);
10331
+ createRequire5(pathToFileURL3(join23(fromDir, "_.js")).href).resolve(spec);
10198
10332
  return true;
10199
10333
  } catch {
10200
10334
  return false;
@@ -10205,7 +10339,7 @@ var resolveReactCompiler = (config, needsReact) => {
10205
10339
  return null;
10206
10340
  }
10207
10341
  try {
10208
- return createRequire5(pathToFileURL3(join22(packageRoot(), "_.js")).href).resolve("babel-plugin-react-compiler");
10342
+ return createRequire5(pathToFileURL3(join23(packageRoot(), "_.js")).href).resolve("babel-plugin-react-compiler");
10209
10343
  } catch {
10210
10344
  return null;
10211
10345
  }
@@ -10215,15 +10349,15 @@ var reactCompilerWarnings = (config, needsReact, compilerPath) => needsReact &&
10215
10349
  ] : [];
10216
10350
  var resolvedAstroPath = (fromDir) => {
10217
10351
  try {
10218
- const pkg = createRequire5(pathToFileURL3(join22(fromDir, "_.js")).href).resolve("astro/package.json");
10352
+ const pkg = createRequire5(pathToFileURL3(join23(fromDir, "_.js")).href).resolve("astro/package.json");
10219
10353
  return realpathSync(pkg);
10220
10354
  } catch {
10221
10355
  return null;
10222
10356
  }
10223
10357
  };
10224
10358
  var blumeDepsDir = (pkgDir = packageRoot()) => {
10225
- const candidates = [join22(pkgDir, "node_modules"), dirname8(pkgDir)];
10226
- return candidates.find((dir) => existsSync12(join22(dir, "astro"))) ?? null;
10359
+ const candidates = [join23(pkgDir, "node_modules"), dirname8(pkgDir)];
10360
+ return candidates.find((dir) => existsSync13(join23(dir, "astro"))) ?? null;
10227
10361
  };
10228
10362
  var linkDepsJunction = async (link, depsDir) => {
10229
10363
  let existing;
@@ -10246,7 +10380,7 @@ var readPkgVersion = (pkgJsonPath) => {
10246
10380
  return null;
10247
10381
  }
10248
10382
  try {
10249
- return JSON.parse(readFileSync7(pkgJsonPath, "utf-8")).version ?? null;
10383
+ return JSON.parse(readFileSync8(pkgJsonPath, "utf-8")).version ?? null;
10250
10384
  } catch {
10251
10385
  return null;
10252
10386
  }
@@ -10268,8 +10402,8 @@ var ensureDepsLink = async (outDir, pkgDir = packageRoot()) => {
10268
10402
  if (blumeAstro && outDirAstro === blumeAstro) {
10269
10403
  return null;
10270
10404
  }
10271
- if (existsSync12(join22(depsDir, "@astrojs", "mdx"))) {
10272
- await linkDepsJunction(join22(outDir, "node_modules"), depsDir);
10405
+ if (existsSync13(join23(depsDir, "@astrojs", "mdx"))) {
10406
+ await linkDepsJunction(join23(outDir, "node_modules"), depsDir);
10273
10407
  return null;
10274
10408
  }
10275
10409
  return astroConflictWarning(blumeAstro, outDirAstro);
@@ -10301,14 +10435,14 @@ var deploymentAdapterWarnings = (deployment, root) => {
10301
10435
  }
10302
10436
  return [];
10303
10437
  };
10304
- var examplesCssFile = (root, config) => config.examples.css ? join22(root, config.examples.css) : null;
10438
+ var examplesCssFile = (root, config) => config.examples.css ? join23(root, config.examples.css) : null;
10305
10439
  var writeExamplesPreview = async (options) => {
10306
10440
  const { config, hasExamples, root, srcDir, write } = options;
10307
10441
  if (hasExamples) {
10308
- await write(join22(srcDir, "pages", ...config.basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro"), examplesPageTemplate());
10442
+ await write(join23(srcDir, "pages", ...config.basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro"), examplesPageTemplate());
10309
10443
  }
10310
10444
  const cssFile = examplesCssFile(root, config);
10311
- return cssFile && !existsSync12(cssFile) ? [
10445
+ return cssFile && !existsSync13(cssFile) ? [
10312
10446
  `examples.css points at "${config.examples.css}", which doesn't exist; previews render without it.`
10313
10447
  ] : [];
10314
10448
  };
@@ -10337,7 +10471,7 @@ var detectUsesMath = async (root, staged = []) => {
10337
10471
  ignore: ["**/node_modules/**", "**/.blume/**", "**/dist/**"],
10338
10472
  onlyFiles: true
10339
10473
  });
10340
- const contents = await Promise.all(files.map((file) => readOptional(join22(root, file))));
10474
+ const contents = await Promise.all(files.map((file) => readOptional(join23(root, file))));
10341
10475
  return [...contents, ...staged].some(containsMath);
10342
10476
  };
10343
10477
  var writeIfChanged = async (path, content) => {
@@ -10389,14 +10523,46 @@ var writeStagedContent = async (out, staged) => {
10389
10523
  const contentDir = stagedContentDir(out);
10390
10524
  const written = new Set;
10391
10525
  await Promise.all([...staged].map(async ([entryId, text]) => {
10392
- const path = join22(contentDir, entryId);
10526
+ const path = join23(contentDir, entryId);
10393
10527
  written.add(normalize3(path));
10394
10528
  await writeIfChanged(path, text);
10395
10529
  }));
10396
- if (existsSync12(contentDir)) {
10530
+ if (existsSync13(contentDir)) {
10397
10531
  await pruneOrphans(contentDir, written);
10398
10532
  }
10399
10533
  };
10534
+ var SVG_ROOT = /<svg\b(?<attributes>[^>]*)>/u;
10535
+ var SVG_WIDTH = /\bwidth\s*=\s*["'](?<value>[^"']+)["']/u;
10536
+ var SVG_HEIGHT = /\bheight\s*=\s*["'](?<value>[^"']+)["']/u;
10537
+ var SVG_LENGTH = /^\s*(?<value>[\d.]+)(?:px)?\s*$/u;
10538
+ var SVG_VIEW_BOX = /\bviewBox\s*=\s*["'][\d.-]+[\s,]+[\d.-]+[\s,]+(?<width>[\d.]+)[\s,]+(?<height>[\d.]+)["']/u;
10539
+ var parseSvgLength = (value) => {
10540
+ const length = Number(value?.match(SVG_LENGTH)?.groups?.value);
10541
+ return length > 0 ? length : undefined;
10542
+ };
10543
+ var svgDimensions = (svg) => {
10544
+ const attributes = svg?.match(SVG_ROOT)?.groups?.attributes;
10545
+ const width = parseSvgLength(attributes?.match(SVG_WIDTH)?.groups?.value);
10546
+ const height = parseSvgLength(attributes?.match(SVG_HEIGHT)?.groups?.value);
10547
+ if (width && height) {
10548
+ return { height, width };
10549
+ }
10550
+ const viewBox = attributes?.match(SVG_VIEW_BOX);
10551
+ const viewBoxWidth = Number(viewBox?.groups?.width);
10552
+ const viewBoxHeight = Number(viewBox?.groups?.height);
10553
+ return viewBoxWidth > 0 && viewBoxHeight > 0 ? { height: viewBoxHeight, width: viewBoxWidth } : undefined;
10554
+ };
10555
+ var readLogoSvg = (project, source) => {
10556
+ if (!source?.toLowerCase().endsWith(".svg")) {
10557
+ return;
10558
+ }
10559
+ const rel = source.replace(/^\//u, "");
10560
+ const file = [
10561
+ join23(project.context.root, "public", rel),
10562
+ join23(project.context.root, rel)
10563
+ ].find((path) => existsSync13(path));
10564
+ return file ? readFileSync8(file, "utf-8") : undefined;
10565
+ };
10400
10566
  var resolveLogo = (project) => {
10401
10567
  const { logo } = project.config;
10402
10568
  if (!logo) {
@@ -10409,17 +10575,15 @@ var resolveLogo = (project) => {
10409
10575
  const dark = image?.dark ?? image?.light;
10410
10576
  const alt = image?.alt ?? "";
10411
10577
  const brandHref = href ?? "/";
10412
- if (light && light === dark && light.toLowerCase().endsWith(".svg")) {
10413
- const rel = light.replace(/^\//u, "");
10414
- const file = [
10415
- join22(project.context.root, "public", rel),
10416
- join22(project.context.root, rel)
10417
- ].find((path) => existsSync12(path));
10418
- if (file) {
10419
- return { alt, href: brandHref, svg: readFileSync7(file, "utf-8"), text };
10420
- }
10578
+ const lightSvg = readLogoSvg(project, light);
10579
+ const darkSvg = dark === light ? lightSvg : readLogoSvg(project, dark);
10580
+ if (light && light === dark && lightSvg) {
10581
+ return { alt, href: brandHref, svg: lightSvg, text };
10421
10582
  }
10422
- return { alt, dark, href: brandHref, light, text };
10583
+ const lightDimensions = svgDimensions(lightSvg);
10584
+ const darkDimensions = svgDimensions(darkSvg);
10585
+ const dimensions = lightDimensions || darkDimensions ? { dark: darkDimensions, light: lightDimensions } : undefined;
10586
+ return { alt, dark, dimensions, href: brandHref, light, text };
10423
10587
  };
10424
10588
  var FAVICON_CANDIDATES = [
10425
10589
  "icon.svg",
@@ -10440,9 +10604,9 @@ var faviconType = (name) => {
10440
10604
  const ext = name.split(".").pop()?.toLowerCase();
10441
10605
  return ext ? FAVICON_TYPES[ext] : undefined;
10442
10606
  };
10443
- var inlineDataUri = (file, type) => `data:${type};base64,${readFileSync7(file).toString("base64")}`;
10607
+ var inlineDataUri = (file, type) => `data:${type};base64,${readFileSync8(file).toString("base64")}`;
10444
10608
  var defaultFavicon = () => ({
10445
- href: inlineDataUri(join22(BLUME_SRC, "assets", "icon.png"), "image/png"),
10609
+ href: inlineDataUri(join23(BLUME_SRC, "assets", "icon.png"), "image/png"),
10446
10610
  type: "image/png"
10447
10611
  });
10448
10612
  var APPLE_ICON_CANDIDATES = [
@@ -10454,13 +10618,13 @@ var APPLE_ICON_CANDIDATES = [
10454
10618
  var resolveIconFile = (project, candidates) => {
10455
10619
  const { root } = project.context;
10456
10620
  for (const name of candidates) {
10457
- if (existsSync12(join22(root, "public", name))) {
10621
+ if (existsSync13(join23(root, "public", name))) {
10458
10622
  return { href: `/${name}`, type: faviconType(name) };
10459
10623
  }
10460
10624
  }
10461
10625
  for (const name of candidates) {
10462
- const file = join22(root, name);
10463
- if (existsSync12(file)) {
10626
+ const file = join23(root, name);
10627
+ if (existsSync13(file)) {
10464
10628
  const type = faviconType(name);
10465
10629
  return { href: inlineDataUri(file, type ?? "image/x-icon"), type };
10466
10630
  }
@@ -10489,6 +10653,8 @@ var buildRuntimeData = (project) => {
10489
10653
  const { github } = config;
10490
10654
  const repoUrl = github ? `https://github.com/${github.owner}/${github.repo}` : null;
10491
10655
  const editBase = github ? `${repoUrl}/edit/${github.branch}` : null;
10656
+ const logo = resolveLogo(project);
10657
+ const ogLogo = config.seo.og.logo ? resolveOgLogo(project, config.seo.og.logo) : logo?.svg;
10492
10658
  const editUrlFor = (sourcePath) => {
10493
10659
  if (!(editBase && sourcePath)) {
10494
10660
  return null;
@@ -10498,10 +10664,9 @@ var buildRuntimeData = (project) => {
10498
10664
  return `${editBase}/${editPath}`;
10499
10665
  };
10500
10666
  const { i18n } = config;
10501
- const withReferenceTabs = (nav) => ({
10667
+ const withRepoUrl = (nav) => ({
10502
10668
  ...nav,
10503
- repoUrl: config.navigation.repo && repoUrl ? repoUrl : null,
10504
- tabs: [...nav.tabs, ...referenceTabs(config)]
10669
+ repoUrl: config.navigation.repo && repoUrl ? repoUrl : null
10505
10670
  });
10506
10671
  const uiByLocale = i18n ? Object.fromEntries(i18n.locales.map(({ code }) => [
10507
10672
  code,
@@ -10516,7 +10681,7 @@ var buildRuntimeData = (project) => {
10516
10681
  }) : EN_UI;
10517
10682
  const navigationByLocale = i18n ? Object.fromEntries(i18n.locales.map(({ code }) => [
10518
10683
  code,
10519
- withReferenceTabs(graph.navigationByLocale[code] ?? {
10684
+ withRepoUrl(graph.navigationByLocale[code] ?? {
10520
10685
  featured: [],
10521
10686
  selectors: [],
10522
10687
  sidebar: [],
@@ -10546,9 +10711,16 @@ var buildRuntimeData = (project) => {
10546
10711
  }))
10547
10712
  } : null,
10548
10713
  imageZoom: config.markdown.imageZoom,
10549
- logo: resolveLogo(project),
10550
- mcp: config.mcp.enabled ? { name: config.mcp.name ?? config.title, route: config.mcp.route } : null,
10551
- og: { enabled: config.seo.og.enabled ?? false },
10714
+ logo,
10715
+ mcp: config.ai.mcp.enabled ? {
10716
+ name: config.ai.mcp.name ?? config.title,
10717
+ route: config.ai.mcp.route
10718
+ } : null,
10719
+ og: {
10720
+ enabled: config.seo.og.enabled ?? false,
10721
+ logo: ogLogo,
10722
+ palette: config.seo.og.palette
10723
+ },
10552
10724
  repoUrl,
10553
10725
  search: {
10554
10726
  enabled: config.search.provider !== "none",
@@ -10566,7 +10738,7 @@ var buildRuntimeData = (project) => {
10566
10738
  title: feed.title
10567
10739
  })),
10568
10740
  fontCssVars: configuredCssVars(config.theme.fonts),
10569
- navigation: withReferenceTabs(graph.navigation),
10741
+ navigation: withRepoUrl(graph.navigation),
10570
10742
  navigationByLocale,
10571
10743
  routes: manifest.routes.map((route) => ({
10572
10744
  alternates: route.alternates,
@@ -10591,8 +10763,8 @@ var buildRuntimeData = (project) => {
10591
10763
  };
10592
10764
  var planMcp = (project, srcDir, userPages) => {
10593
10765
  const { config } = project;
10594
- const { route } = config.mcp;
10595
- const dir = join22(srcDir, "blume-mcp");
10766
+ const { route } = config.ai.mcp;
10767
+ const dir = join23(srcDir, "blume-mcp");
10596
10768
  const base = {
10597
10769
  dir,
10598
10770
  discoveryPages: [],
@@ -10601,14 +10773,14 @@ var planMcp = (project, srcDir, userPages) => {
10601
10773
  srcDir,
10602
10774
  warnings: []
10603
10775
  };
10604
- if (!config.mcp.enabled) {
10776
+ if (!config.ai.mcp.enabled) {
10605
10777
  return base;
10606
10778
  }
10607
10779
  if (routeIsTaken(userPages, project.graph.pages, route)) {
10608
10780
  return {
10609
10781
  ...base,
10610
10782
  warnings: [
10611
- `MCP server route "${route}" is already used by a content or custom page; the MCP server was not generated. Set a different "mcp.route" in blume.config.ts.`
10783
+ `MCP server route "${route}" is already used by a content or custom page; the MCP server was not generated. Set a different "ai.mcp.route" in blume.config.ts.`
10612
10784
  ]
10613
10785
  };
10614
10786
  }
@@ -10616,11 +10788,11 @@ var planMcp = (project, srcDir, userPages) => {
10616
10788
  ...base,
10617
10789
  discoveryPages: [
10618
10790
  {
10619
- entrypoint: join22(dir, "discovery.ts"),
10791
+ entrypoint: join23(dir, "discovery.ts"),
10620
10792
  pattern: "/.well-known/mcp.json"
10621
10793
  },
10622
10794
  {
10623
- entrypoint: join22(dir, "server-card.ts"),
10795
+ entrypoint: join23(dir, "server-card.ts"),
10624
10796
  pattern: "/.well-known/mcp/server-card.json"
10625
10797
  }
10626
10798
  ],
@@ -10640,11 +10812,11 @@ var writeMcpFiles = async (project, plan, write) => {
10640
10812
  version: data.version
10641
10813
  };
10642
10814
  await Promise.all([
10643
- write(join22(plan.srcDir, "generated", "mcp-data.json"), `${JSON.stringify(data)}
10815
+ write(join23(plan.srcDir, "generated", "mcp-data.json"), `${JSON.stringify(data)}
10644
10816
  `),
10645
- write(join22(plan.srcDir, "pages", mcpPageFile(plan.route)), mcpEndpointTemplate(plan.route)),
10646
- write(join22(plan.dir, "discovery.ts"), staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput))),
10647
- write(join22(plan.dir, "server-card.ts"), staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)))
10817
+ write(join23(plan.srcDir, "pages", mcpPageFile(plan.route)), mcpEndpointTemplate(plan.route)),
10818
+ write(join23(plan.dir, "discovery.ts"), staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput))),
10819
+ write(join23(plan.dir, "server-card.ts"), staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)))
10648
10820
  ]);
10649
10821
  };
10650
10822
  var writeAskFiles = async (project, srcDir, write) => {
@@ -10654,16 +10826,16 @@ var writeAskFiles = async (project, srcDir, write) => {
10654
10826
  }
10655
10827
  const grounded = ask.provider !== "inkeep";
10656
10828
  if (grounded) {
10657
- await write(join22(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
10829
+ await write(join23(srcDir, "generated", "ask-data.json"), `${JSON.stringify(await buildAskData(project))}
10658
10830
  `);
10659
10831
  }
10660
- await write(join22(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded));
10832
+ await write(join23(srcDir, "pages", "api", "ask.ts"), askEndpointTemplate(resolveAskBackend(ask), grounded));
10661
10833
  };
10662
10834
  var writeNotFoundPage = async (write, srcDir, pages, contentPages) => {
10663
10835
  if (routeIsTaken(pages, contentPages, "/404")) {
10664
10836
  return;
10665
10837
  }
10666
- await write(join22(srcDir, "pages", "404.astro"), notFoundPageTemplate());
10838
+ await write(join23(srcDir, "pages", "404.astro"), notFoundPageTemplate());
10667
10839
  };
10668
10840
  var buildComponentSlots = async (componentsFile) => {
10669
10841
  const analysis = componentsFile ? analyzeComponentOverrides(await readFile11(componentsFile, "utf-8"), componentsFile) : null;
@@ -10676,13 +10848,14 @@ var buildComponentSlots = async (componentsFile) => {
10676
10848
  var generateRuntime = async (project) => {
10677
10849
  const { context, config } = project;
10678
10850
  const out = context.outDir;
10679
- const srcDir = join22(out, "src");
10680
- const dataPath = join22(srcDir, "generated", "data.json");
10681
- const themePath = join22(srcDir, "generated", "app.css");
10682
- const searchClientPath = join22(srcDir, "generated", "search-client.ts");
10683
- const examplesPath = join22(srcDir, "generated", "examples.ts");
10684
- const examplesThemePath = join22(srcDir, "generated", "examples.css");
10685
- const openapiPath = join22(srcDir, "generated", "openapi.json");
10851
+ const srcDir = join23(out, "src");
10852
+ const askPath = join23(srcDir, "generated", "Ask.astro");
10853
+ const dataPath = join23(srcDir, "generated", "data.json");
10854
+ const themePath = join23(srcDir, "generated", "app.css");
10855
+ const searchClientPath = join23(srcDir, "generated", "search-client.ts");
10856
+ const examplesPath = join23(srcDir, "generated", "examples.ts");
10857
+ const examplesThemePath = join23(srcDir, "generated", "examples.css");
10858
+ const openapiPath = join23(srcDir, "generated", "openapi.json");
10686
10859
  const written = new Set;
10687
10860
  const write = (path, content) => {
10688
10861
  written.add(normalize3(path));
@@ -10733,8 +10906,9 @@ var generateRuntime = async (project) => {
10733
10906
  const hasFilesystemSource = project.sources.some((source) => !source.staged);
10734
10907
  const [structural] = await Promise.all([
10735
10908
  Promise.all([
10736
- write(join22(out, "astro.config.mjs"), astroConfigTemplate({
10909
+ write(join23(out, "astro.config.mjs"), astroConfigTemplate({
10737
10910
  aliases: resolveTsconfigAliases(context.root),
10911
+ askPath,
10738
10912
  config,
10739
10913
  contentRoutes: project.manifest.routes.map((route) => route.path),
10740
10914
  context,
@@ -10750,26 +10924,26 @@ var generateRuntime = async (project) => {
10750
10924
  searchClientPath,
10751
10925
  themePath
10752
10926
  })),
10753
- write(join22(out, "package.json"), runtimePackageTemplate(runtimeDependencies({ config, needsReact, needsSvelte, needsVue }))),
10754
- write(join22(out, "tsconfig.json"), runtimeTsconfigTemplate()),
10755
- write(join22(srcDir, "env.d.ts"), envTemplate()),
10756
- write(join22(srcDir, "content.config.ts"), contentConfigTemplate({
10927
+ write(join23(out, "package.json"), runtimePackageTemplate(runtimeDependencies({ config, needsReact, needsSvelte, needsVue }))),
10928
+ write(join23(out, "tsconfig.json"), runtimeTsconfigTemplate()),
10929
+ write(join23(srcDir, "env.d.ts"), envTemplate()),
10930
+ write(join23(srcDir, "content.config.ts"), contentConfigTemplate({
10757
10931
  collection: resolveDocsCollection(config, context),
10758
10932
  config,
10759
10933
  context,
10760
10934
  filesystem: hasFilesystemSource,
10761
10935
  staged: hasStaged
10762
10936
  })),
10763
- write(join22(srcDir, "pages", "[...slug].astro"), catchAllPageTemplate({
10764
- askEnabled,
10937
+ write(join23(srcDir, "pages", "[...slug].astro"), catchAllPageTemplate({
10765
10938
  exportEpub,
10766
10939
  exportPdf,
10767
10940
  mathEnabled: usesMath,
10768
10941
  needsReact
10769
10942
  })),
10770
- write(join22(srcDir, "generated", "components.ts"), slotPlan.module),
10771
- write(join22(srcDir, "generated", "islands.ts"), islandMapTemplate(islandDiscovery.islands)),
10772
- write(join22(srcDir, "generated", "examples.ts"), exampleMapTemplate(exampleDiscovery.examples, config.basePath)),
10943
+ write(askPath, askComponentTemplate(askEnabled)),
10944
+ write(join23(srcDir, "generated", "components.ts"), slotPlan.module),
10945
+ write(join23(srcDir, "generated", "islands.ts"), islandMapTemplate(islandDiscovery.islands)),
10946
+ write(join23(srcDir, "generated", "examples.ts"), exampleMapTemplate(exampleDiscovery.examples, config.basePath)),
10773
10947
  write(examplesThemePath, examplesEntryTemplate({
10774
10948
  configTokens: buildThemeCss(config.theme),
10775
10949
  sources: [`${context.root}/**/*.{astro,jsx,svelte,ts,tsx,vue}`],
@@ -10785,18 +10959,17 @@ var generateRuntime = async (project) => {
10785
10959
  userTheme
10786
10960
  }))
10787
10961
  ]),
10788
- Promise.all(islandDiscovery.islands.map((island) => write(join22(srcDir, "generated", "islands", `${island.name}.astro`), islandWrapperTemplate(island)))),
10789
- Promise.all(slotPlan.wrappers.map((wrapper) => write(join22(srcDir, "generated", "component-slots", `${wrapper.name}.astro`), wrapper.content))),
10790
- Promise.all(exampleDiscovery.examples.map((example) => write(join22(srcDir, "generated", "examples", `${exampleSlug(example.path)}.astro`), exampleWrapperTemplate(example)))),
10962
+ Promise.all(islandDiscovery.islands.map((island) => write(join23(srcDir, "generated", "islands", `${island.name}.astro`), islandWrapperTemplate(island)))),
10963
+ Promise.all(slotPlan.wrappers.map((wrapper) => write(join23(srcDir, "generated", "component-slots", `${wrapper.name}.astro`), wrapper.content))),
10964
+ Promise.all(exampleDiscovery.examples.map((example) => write(join23(srcDir, "generated", "examples", `${exampleSlug(example.path)}.astro`), exampleWrapperTemplate(example)))),
10791
10965
  writeAskFiles(project, srcDir, write),
10792
10966
  writeMcpFiles(project, mcp, write)
10793
10967
  ]);
10794
10968
  if (config.seo.og.enabled) {
10795
- await write(join22(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes));
10969
+ await write(join23(srcDir, "pages", "og", "[...slug].png.ts"), ogEndpointTemplate(ogRoutes));
10796
10970
  }
10797
10971
  if (hasGeneratedChangelog(project, pages)) {
10798
- await write(join22(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
10799
- askEnabled,
10972
+ await write(join23(srcDir, "pages", "changelog.astro"), changelogIndexTemplate({
10800
10973
  exportEpub,
10801
10974
  exportPdf,
10802
10975
  needsReact,
@@ -10816,27 +10989,27 @@ var generateRuntime = async (project) => {
10816
10989
  ]);
10817
10990
  if (servesStaticIndex(config.search.provider)) {
10818
10991
  const documents = await buildSearchDocuments(project);
10819
- await write(join22(srcDir, "generated", "search.json"), `${JSON.stringify(documents)}
10992
+ await write(join23(srcDir, "generated", "search.json"), `${JSON.stringify(documents)}
10820
10993
  `);
10821
- await write(join22(srcDir, "pages", "blume-search.json.ts"), searchEndpointTemplate());
10994
+ await write(join23(srcDir, "pages", "blume-search.json.ts"), searchEndpointTemplate());
10822
10995
  }
10823
10996
  if (config.search.provider === "mixedbread") {
10824
- await write(join22(srcDir, "pages", "api", "search.ts"), mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""));
10997
+ await write(join23(srcDir, "pages", "api", "search.ts"), mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""));
10825
10998
  }
10826
10999
  const rawMarkdown = await buildRawMarkdown(project);
10827
11000
  await Promise.all([
10828
- write(join22(srcDir, "generated", "raw-markdown.json"), `${JSON.stringify(rawMarkdown)}
11001
+ write(join23(srcDir, "generated", "raw-markdown.json"), `${JSON.stringify(rawMarkdown)}
10829
11002
  `),
10830
- write(join22(srcDir, "pages", "[...slug].md.ts"), rawMarkdownEndpointTemplate("md")),
10831
- write(join22(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate("mdx"))
11003
+ write(join23(srcDir, "pages", "[...slug].md.ts"), rawMarkdownEndpointTemplate("md")),
11004
+ write(join23(srcDir, "pages", "[...slug].mdx.ts"), rawMarkdownEndpointTemplate("mdx"))
10832
11005
  ]);
10833
11006
  const feeds = buildRssFeeds(project);
10834
11007
  if (feeds.length > 0) {
10835
11008
  const feedXml = Object.fromEntries(feeds.map((feed) => [feed.type, renderRssFeed(feed)]));
10836
11009
  await Promise.all([
10837
- write(join22(srcDir, "generated", "rss.json"), `${JSON.stringify(feedXml)}
11010
+ write(join23(srcDir, "generated", "rss.json"), `${JSON.stringify(feedXml)}
10838
11011
  `),
10839
- write(join22(srcDir, "pages", "[section]", "rss.xml.ts"), rssEndpointTemplate())
11012
+ write(join23(srcDir, "pages", "[section]", "rss.xml.ts"), rssEndpointTemplate())
10840
11013
  ]);
10841
11014
  }
10842
11015
  const warnings = [
@@ -10851,7 +11024,7 @@ var generateRuntime = async (project) => {
10851
11024
  const navTargetRoutes = new Set([
10852
11025
  ...project.graph.routes.keys(),
10853
11026
  ...pages.map((page) => page.pattern),
10854
- ...referenceTabs(config).map((tab) => tab.path)
11027
+ ...referenceRoutes(config)
10855
11028
  ]);
10856
11029
  if (hasGeneratedChangelog(project, pages)) {
10857
11030
  navTargetRoutes.add("/changelog");
@@ -10875,14 +11048,14 @@ var generateRuntime = async (project) => {
10875
11048
  root: context.root
10876
11049
  });
10877
11050
  warnings.push(...references.warnings);
10878
- await Promise.all(references.files.map((file) => write(join22(srcDir, "pages", file.pagePath), file.content)));
11051
+ await Promise.all(references.files.map((file) => write(join23(srcDir, "pages", file.pagePath), file.content)));
10879
11052
  }
10880
11053
  const openApiSource2 = project.sources.find(isOpenApiSource);
10881
11054
  await Promise.all([
10882
- write(join22(srcDir, "generated", "data.json"), buildRuntimeData(project)),
11055
+ write(join23(srcDir, "generated", "data.json"), buildRuntimeData(project)),
10883
11056
  write(openapiPath, `${JSON.stringify(openApiSource2 ? openApiSource2.openApiData() : {})}
10884
11057
  `),
10885
- write(join22(out, "blume.manifest.json"), `${JSON.stringify(project.manifest, null, 2)}
11058
+ write(join23(out, "blume.manifest.json"), `${JSON.stringify(project.manifest, null, 2)}
10886
11059
  `),
10887
11060
  writeStagedContent(out, staged)
10888
11061
  ]);
@@ -10894,7 +11067,7 @@ var generateRuntime = async (project) => {
10894
11067
  import { relative as relative11 } from "pathe";
10895
11068
 
10896
11069
  // src/core/config.ts
10897
- import { existsSync as existsSync13, readFileSync as readFileSync8 } from "node:fs";
11070
+ import { existsSync as existsSync14, readFileSync as readFileSync9 } from "node:fs";
10898
11071
 
10899
11072
  // src/core/deployment-env.ts
10900
11073
  var toUrl = (value) => {
@@ -10964,7 +11137,7 @@ var loadConfig = async (root, options = {}) => {
10964
11137
  }
10965
11138
  const parsed = blumeConfigSchema.safeParse(raw ?? {});
10966
11139
  if (!parsed.success) {
10967
- const source = configFile && existsSync13(configFile) ? readFileSync8(configFile, "utf-8") : undefined;
11140
+ const source = configFile && existsSync14(configFile) ? readFileSync9(configFile, "utf-8") : undefined;
10968
11141
  const diagnostics = diagnosticsFromZod(parsed.error, {
10969
11142
  code: "BLUME_CONFIG_INVALID",
10970
11143
  file: configFile ?? undefined,
@@ -11259,6 +11432,29 @@ var buildConfigSidebar = (items, byRoute, display, basePath) => {
11259
11432
  }
11260
11433
  return nodes;
11261
11434
  };
11435
+ var resolveTabHref = (sidebar, path) => {
11436
+ let first;
11437
+ const walk = (nodes) => {
11438
+ for (const node of nodes) {
11439
+ const { route } = node;
11440
+ if (route === path) {
11441
+ return true;
11442
+ }
11443
+ if (first === undefined && route !== undefined && route.startsWith(`${path}/`)) {
11444
+ first = route;
11445
+ }
11446
+ if (node.kind === "group" && walk(node.children)) {
11447
+ return true;
11448
+ }
11449
+ }
11450
+ return false;
11451
+ };
11452
+ return walk(sidebar) ? path : first ?? path;
11453
+ };
11454
+ var withTabHrefs = (tabs2, sidebar) => tabs2.map((tab) => {
11455
+ const href = resolveTabHref(sidebar, tab.path);
11456
+ return href === tab.path ? tab : { ...tab, href };
11457
+ });
11262
11458
  var buildNavigation = (pages, options) => {
11263
11459
  const basePath = options.basePath ?? "";
11264
11460
  const display = options.display ?? "flat";
@@ -11294,19 +11490,21 @@ var buildNavigation = (pages, options) => {
11294
11490
  }
11295
11491
  }
11296
11492
  if (options.sidebar) {
11493
+ const sidebar2 = buildConfigSidebar(options.sidebar, byRoute, display, basePath);
11297
11494
  return {
11298
11495
  featured,
11299
11496
  selectors,
11300
- sidebar: buildConfigSidebar(options.sidebar, byRoute, display, basePath),
11301
- tabs: tabs2
11497
+ sidebar: sidebar2,
11498
+ tabs: withTabHrefs(tabs2, sidebar2)
11302
11499
  };
11303
11500
  }
11304
11501
  const rootTabPath = withBasePath(basePath, options.localizedRoot ?? "/");
11502
+ const sidebar = buildFileSystemSidebar(pages, options.folderMeta, sharedFolderMeta, metaPrefix, display, new Set(tabs2.flatMap((tab) => tab.path === rootTabPath ? [] : [tab.path])));
11305
11503
  return {
11306
11504
  featured,
11307
11505
  selectors,
11308
- sidebar: buildFileSystemSidebar(pages, options.folderMeta, sharedFolderMeta, metaPrefix, display, new Set(tabs2.flatMap((tab) => tab.path === rootTabPath ? [] : [tab.path]))),
11309
- tabs: tabs2
11506
+ sidebar,
11507
+ tabs: withTabHrefs(tabs2, sidebar)
11310
11508
  };
11311
11509
  };
11312
11510
 
@@ -11675,8 +11873,8 @@ var scanProject = async (root, options = {}) => {
11675
11873
  };
11676
11874
 
11677
11875
  // src/cli/env.ts
11678
- import { existsSync as existsSync14, readFileSync as readFileSync9 } from "node:fs";
11679
- import { dirname as dirname10, join as join23, resolve as resolve7 } from "pathe";
11876
+ import { existsSync as existsSync15, readFileSync as readFileSync10 } from "node:fs";
11877
+ import { dirname as dirname10, join as join24, resolve as resolve7 } from "pathe";
11680
11878
  var ENV_LINE = /^\s*(?:export\s+)?(?<key>[A-Za-z_][A-Za-z0-9_]*)\s*=\s*(?<value>.*?)\s*$/u;
11681
11879
  var DOUBLE_QUOTED2 = /^"(?<body>[\s\S]*)"$/u;
11682
11880
  var SINGLE_QUOTED = /^'(?<body>[\s\S]*)'$/u;
@@ -11722,8 +11920,8 @@ var applyEnv = (parsed) => {
11722
11920
  };
11723
11921
  var loadFile = (path) => {
11724
11922
  try {
11725
- if (existsSync14(path)) {
11726
- applyEnv(parseEnv(readFileSync9(path, "utf-8")));
11923
+ if (existsSync15(path)) {
11924
+ applyEnv(parseEnv(readFileSync10(path, "utf-8")));
11727
11925
  }
11728
11926
  } catch {}
11729
11927
  };
@@ -11731,10 +11929,10 @@ var loadEnvFiles = (startDir) => {
11731
11929
  let dir = resolve7(startDir);
11732
11930
  let done = false;
11733
11931
  while (!done) {
11734
- loadFile(join23(dir, ".env.local"));
11735
- loadFile(join23(dir, ".env"));
11932
+ loadFile(join24(dir, ".env.local"));
11933
+ loadFile(join24(dir, ".env"));
11736
11934
  const parent = dirname10(dir);
11737
- done = existsSync14(join23(dir, ".git")) || parent === dir;
11935
+ done = existsSync15(join24(dir, ".git")) || parent === dir;
11738
11936
  dir = parent;
11739
11937
  }
11740
11938
  };
@@ -11869,12 +12067,12 @@ var emitRedirectFiles = async (config, distDir) => {
11869
12067
  if (redirects.length === 0 || config.deployment.output !== "static") {
11870
12068
  return;
11871
12069
  }
11872
- await writeFile7(join24(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
12070
+ await writeFile7(join25(distDir, "blume-redirects.json"), buildRedirectManifest(redirects), "utf-8");
11873
12071
  const platformFiles = [
11874
12072
  { content: buildNetlifyRedirects(redirects), name: "_redirects" },
11875
12073
  { content: buildVercelConfig(redirects), name: "vercel.json" }
11876
12074
  ];
11877
- await Promise.all(platformFiles.map((file) => existsSync15(join24(distDir, file.name)) ? Promise.resolve() : writeFile7(join24(distDir, file.name), file.content, "utf-8")));
12075
+ await Promise.all(platformFiles.map((file) => existsSync16(join25(distDir, file.name)) ? Promise.resolve() : writeFile7(join25(distDir, file.name), file.content, "utf-8")));
11878
12076
  logger.success(`Emitted redirect files for ${redirects.length} redirect(s)`);
11879
12077
  };
11880
12078
  var formatBytes = (bytes) => {
@@ -11885,14 +12083,14 @@ var formatBytes = (bytes) => {
11885
12083
  return `${(bytes / 1024).toFixed(digits)} kB`;
11886
12084
  };
11887
12085
  var astroAssets = async (distDir, ext) => {
11888
- const astroDir = join24(distDir, "_astro");
11889
- if (!existsSync15(astroDir)) {
12086
+ const astroDir = join25(distDir, "_astro");
12087
+ if (!existsSync16(astroDir)) {
11890
12088
  return [];
11891
12089
  }
11892
12090
  const entries = await readdir(astroDir);
11893
12091
  const files = entries.filter((name) => name.endsWith(`.${ext}`));
11894
12092
  const sized = await Promise.all(files.map(async (name) => {
11895
- const info = await stat(join24(astroDir, name));
12093
+ const info = await stat(join25(astroDir, name));
11896
12094
  return { name, size: info.size };
11897
12095
  }));
11898
12096
  return sized.toSorted((a, b) => b.size - a.size);
@@ -11945,26 +12143,26 @@ var runClientAssetChecks = async (staticDir, args) => {
11945
12143
  var isolatedOutputDir = (config, context) => {
11946
12144
  const { adapter, output } = config.deployment;
11947
12145
  if (output === "server" && adapter === "vercel") {
11948
- return join24(context.outDir, ".vercel", "output");
12146
+ return join25(context.outDir, ".vercel", "output");
11949
12147
  }
11950
- return context.distDir ?? join24(context.outDir, "dist");
12148
+ return context.distDir ?? join25(context.outDir, "dist");
11951
12149
  };
11952
12150
  var isolatedStaticDir = (config, context) => {
11953
12151
  const { adapter, output } = config.deployment;
11954
12152
  const outputDir = isolatedOutputDir(config, context);
11955
12153
  if (output === "server" && adapter === "vercel") {
11956
- return join24(outputDir, "static");
12154
+ return join25(outputDir, "static");
11957
12155
  }
11958
12156
  if (output === "server" && adapter === "node") {
11959
- return join24(outputDir, "client");
12157
+ return join25(outputDir, "client");
11960
12158
  }
11961
12159
  return outputDir;
11962
12160
  };
11963
12161
  var publishLlmsFiles = async (project, distDir) => {
11964
- const indexPath = join24(distDir, "llms.txt");
11965
- const fullPath = join24(distDir, "llms-full.txt");
11966
- const writeIndex = !existsSync15(indexPath);
11967
- const writeFull = !existsSync15(fullPath);
12162
+ const indexPath = join25(distDir, "llms.txt");
12163
+ const fullPath = join25(distDir, "llms-full.txt");
12164
+ const writeIndex = !existsSync16(indexPath);
12165
+ const writeFull = !existsSync16(fullPath);
11968
12166
  if (!(writeIndex || writeFull)) {
11969
12167
  return;
11970
12168
  }
@@ -11997,18 +12195,18 @@ var publishBuildArtifacts = async (project, distDir, args) => {
11997
12195
  await publishLlmsFiles(project, distDir);
11998
12196
  }
11999
12197
  const sitemap = buildSitemap(project);
12000
- if (sitemap && !existsSync15(join24(distDir, "sitemap.xml"))) {
12001
- await writeFile7(join24(distDir, "sitemap.xml"), sitemap, "utf-8");
12198
+ if (sitemap && !existsSync16(join25(distDir, "sitemap.xml"))) {
12199
+ await writeFile7(join25(distDir, "sitemap.xml"), sitemap, "utf-8");
12002
12200
  logger.success("Generated sitemap.xml");
12003
12201
  }
12004
12202
  const robots = buildRobots(project);
12005
- if (robots && !existsSync15(join24(distDir, "robots.txt"))) {
12006
- await writeFile7(join24(distDir, "robots.txt"), robots, "utf-8");
12203
+ if (robots && !existsSync16(join25(distDir, "robots.txt"))) {
12204
+ await writeFile7(join25(distDir, "robots.txt"), robots, "utf-8");
12007
12205
  logger.success("Generated robots.txt");
12008
12206
  }
12009
12207
  const agentReadability = buildAgentReadability(project);
12010
- if (agentReadability && !existsSync15(join24(distDir, "agent-readability.json"))) {
12011
- await writeFile7(join24(distDir, "agent-readability.json"), `${JSON.stringify(agentReadability, null, 2)}
12208
+ if (agentReadability && !existsSync16(join25(distDir, "agent-readability.json"))) {
12209
+ await writeFile7(join25(distDir, "agent-readability.json"), `${JSON.stringify(agentReadability, null, 2)}
12012
12210
  `, "utf-8");
12013
12211
  logger.success("Generated agent-readability.json");
12014
12212
  }
@@ -12120,11 +12318,11 @@ var buildCommand = defineCommand2({
12120
12318
  });
12121
12319
 
12122
12320
  // src/cli/commands/check.ts
12123
- import { existsSync as existsSync16 } from "node:fs";
12321
+ import { existsSync as existsSync17 } from "node:fs";
12124
12322
  import { check } from "@astrojs/check";
12125
12323
  import { sync } from "astro";
12126
12324
  import { defineCommand as defineCommand3 } from "citty";
12127
- import { join as join25 } from "pathe";
12325
+ import { join as join26 } from "pathe";
12128
12326
  var checkCommand = defineCommand3({
12129
12327
  args: {
12130
12328
  isolated: {
@@ -12160,13 +12358,13 @@ var checkCommand = defineCommand3({
12160
12358
  });
12161
12359
  const { outDir } = project.context;
12162
12360
  await sync({ logLevel: "warn", root: outDir });
12163
- const tsconfig = join25(root, "tsconfig.json");
12361
+ const tsconfig = join26(root, "tsconfig.json");
12164
12362
  logger.start(`Type-checking ${project.graph.pages.length} page(s)`);
12165
12363
  const failed = await check({
12166
12364
  minimumFailingSeverity: "error",
12167
12365
  minimumSeverity: "hint",
12168
12366
  root: outDir,
12169
- tsconfig: existsSync16(tsconfig) ? tsconfig : undefined,
12367
+ tsconfig: existsSync17(tsconfig) ? tsconfig : undefined,
12170
12368
  watch: false
12171
12369
  });
12172
12370
  if (failed) {
@@ -12386,14 +12584,14 @@ var devCommand = defineCommand4({
12386
12584
  });
12387
12585
 
12388
12586
  // src/cli/commands/doctor.ts
12389
- import { readFileSync as readFileSync10 } from "node:fs";
12587
+ import { readFileSync as readFileSync11 } from "node:fs";
12390
12588
  import { defineCommand as defineCommand5 } from "citty";
12391
- import { join as join26 } from "pathe";
12589
+ import { join as join27 } from "pathe";
12392
12590
  var FALLBACK_MIN_NODE = "22.12.0";
12393
12591
  var LEADING_RANGE = /^[^\d]*/u;
12394
12592
  var minSupportedNode = () => {
12395
12593
  try {
12396
- const pkg = JSON.parse(readFileSync10(join26(packageRoot(), "package.json"), "utf-8"));
12594
+ const pkg = JSON.parse(readFileSync11(join27(packageRoot(), "package.json"), "utf-8"));
12397
12595
  const range = pkg.engines?.node ?? "";
12398
12596
  return range.replace(LEADING_RANGE, "") || FALLBACK_MIN_NODE;
12399
12597
  } catch {
@@ -12489,17 +12687,17 @@ import { defineCommand as defineCommand6 } from "citty";
12489
12687
  import { relative as relative14 } from "pathe";
12490
12688
 
12491
12689
  // src/registry/eject.ts
12492
- import { existsSync as existsSync17 } from "node:fs";
12690
+ import { existsSync as existsSync18 } from "node:fs";
12493
12691
  import { cp as cp2, mkdir as mkdir7, readFile as readFile12, rm as rm3, writeFile as writeFile8 } from "node:fs/promises";
12494
- import { join as join27, relative as relative12 } from "pathe";
12692
+ import { join as join28, relative as relative12 } from "pathe";
12495
12693
  var toPosix = (path) => path.split("\\").join("/");
12496
12694
  var LOCAL_BLUME_SOURCE = "../../node_modules/blume/src/**/*.{astro,ts,tsx}";
12497
12695
  var blumeSourceGlob = (root, genDir, resolveBlumeRoot = packageRoot) => {
12498
- if (existsSync17(join27(root, "node_modules", "blume"))) {
12696
+ if (existsSync18(join28(root, "node_modules", "blume"))) {
12499
12697
  return LOCAL_BLUME_SOURCE;
12500
12698
  }
12501
12699
  try {
12502
- const src = join27(resolveBlumeRoot(), "src");
12700
+ const src = join28(resolveBlumeRoot(), "src");
12503
12701
  return `${toPosix(relative12(genDir, src))}/**/*.{astro,ts,tsx}`;
12504
12702
  } catch {
12505
12703
  console.warn('blume: could not locate the installed blume package; src/generated/app.css keeps its default `@source "../../node_modules/blume/..."` glob. If blume is hoisted elsewhere, point that glob at its install location or Blume\'s utility classes will be missing.');
@@ -12519,19 +12717,19 @@ var askFiles = async (project, srcDir, genDir) => {
12519
12717
  const files = [
12520
12718
  {
12521
12719
  content: askEndpointTemplate(resolveAskBackend(ask), grounded),
12522
- path: join27(srcDir, "pages", "api", "ask.ts")
12720
+ path: join28(srcDir, "pages", "api", "ask.ts")
12523
12721
  }
12524
12722
  ];
12525
12723
  if (grounded) {
12526
12724
  files.push({
12527
12725
  content: `${JSON.stringify(await buildAskData(project))}
12528
12726
  `,
12529
- path: join27(genDir, "ask-data.json")
12727
+ path: join28(genDir, "ask-data.json")
12530
12728
  });
12531
12729
  }
12532
12730
  return files;
12533
12731
  };
12534
- var hostsMcp = (project, userPages) => project.config.mcp.enabled && !routeIsTaken(userPages, project.graph.pages, project.config.mcp.route);
12732
+ var hostsMcp = (project, userPages) => project.config.ai.mcp.enabled && !routeIsTaken(userPages, project.graph.pages, project.config.ai.mcp.route);
12535
12733
  var mcpDiscoveryPages = (project, userPages) => hostsMcp(project, userPages) ? [
12536
12734
  {
12537
12735
  entrypoint: "src/blume-mcp/discovery.ts",
@@ -12546,7 +12744,7 @@ var mcpFiles = async (project, userPages, srcDir, genDir) => {
12546
12744
  if (!hostsMcp(project, userPages)) {
12547
12745
  return [];
12548
12746
  }
12549
- const { route } = project.config.mcp;
12747
+ const { route } = project.config.ai.mcp;
12550
12748
  const data = await buildMcpData(project);
12551
12749
  const discoveryInput = {
12552
12750
  base: data.base,
@@ -12559,19 +12757,19 @@ var mcpFiles = async (project, userPages, srcDir, genDir) => {
12559
12757
  {
12560
12758
  content: `${JSON.stringify(data)}
12561
12759
  `,
12562
- path: join27(genDir, "mcp-data.json")
12760
+ path: join28(genDir, "mcp-data.json")
12563
12761
  },
12564
12762
  {
12565
12763
  content: mcpEndpointTemplate(route),
12566
- path: join27(srcDir, "pages", mcpPageFile(route))
12764
+ path: join28(srcDir, "pages", mcpPageFile(route))
12567
12765
  },
12568
12766
  {
12569
12767
  content: staticJsonEndpointTemplate(buildMcpDiscovery(discoveryInput)),
12570
- path: join27(srcDir, "blume-mcp", "discovery.ts")
12768
+ path: join28(srcDir, "blume-mcp", "discovery.ts")
12571
12769
  },
12572
12770
  {
12573
12771
  content: staticJsonEndpointTemplate(buildMcpServerCard(discoveryInput)),
12574
- path: join27(srcDir, "blume-mcp", "server-card.ts")
12772
+ path: join28(srcDir, "blume-mcp", "server-card.ts")
12575
12773
  }
12576
12774
  ];
12577
12775
  };
@@ -12584,22 +12782,22 @@ var changelogFiles = (project, userPages, srcDir, options) => {
12584
12782
  return [
12585
12783
  {
12586
12784
  content: changelogIndexTemplate(options),
12587
- path: join27(srcDir, "pages", "changelog.astro")
12785
+ path: join28(srcDir, "pages", "changelog.astro")
12588
12786
  }
12589
12787
  ];
12590
12788
  };
12591
- var readExamplesCss = (root, css) => css && existsSync17(join27(root, css)) ? readFile12(join27(root, css), "utf-8") : Promise.resolve("");
12789
+ var readExamplesCss = (root, css) => css && existsSync18(join28(root, css)) ? readFile12(join28(root, css), "utf-8") : Promise.resolve("");
12592
12790
  var examplesPreviewFiles = (srcDir, basePath, hasExamples) => hasExamples ? [
12593
12791
  {
12594
12792
  content: examplesPageTemplate(),
12595
- path: join27(srcDir, "pages", ...basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro")
12793
+ path: join28(srcDir, "pages", ...basePath.split("/").filter(Boolean), "blume-examples", "[...path].astro")
12596
12794
  }
12597
12795
  ] : [];
12598
12796
  var eject = async (root) => {
12599
12797
  const project = await scanProject(root, { mode: "build" });
12600
12798
  const { context, config } = project;
12601
- const srcDir = join27(root, "src");
12602
- const genDir = join27(srcDir, "generated");
12799
+ const srcDir = join28(root, "src");
12800
+ const genDir = join28(srcDir, "generated");
12603
12801
  const askEnabled = config.ai.ask?.enabled ?? false;
12604
12802
  const exportPdf = config.export.pdf;
12605
12803
  const exportEpub = config.export.epub;
@@ -12649,6 +12847,7 @@ var eject = async (root) => {
12649
12847
  const files = [
12650
12848
  {
12651
12849
  content: astroConfigTemplate({
12850
+ askPath: "./src/generated/Ask.astro",
12652
12851
  config,
12653
12852
  contentRoutes: project.manifest.routes.map((route) => route.path),
12654
12853
  context: relContext,
@@ -12663,14 +12862,14 @@ var eject = async (root) => {
12663
12862
  searchClientPath: "./src/generated/search-client.ts",
12664
12863
  themePath: "./src/generated/app.css"
12665
12864
  }),
12666
- path: join27(root, "astro.config.mjs")
12865
+ path: join28(root, "astro.config.mjs")
12667
12866
  },
12668
12867
  {
12669
12868
  content: runtimeTsconfigTemplate(),
12670
- path: join27(root, "tsconfig.json"),
12869
+ path: join28(root, "tsconfig.json"),
12671
12870
  skipIfExists: true
12672
12871
  },
12673
- { content: envTemplate(), path: join27(srcDir, "env.d.ts") },
12872
+ { content: envTemplate(), path: join28(srcDir, "env.d.ts") },
12674
12873
  {
12675
12874
  content: contentConfigTemplate({
12676
12875
  config,
@@ -12678,29 +12877,28 @@ var eject = async (root) => {
12678
12877
  staged: hasStaged,
12679
12878
  stagedBase: stagedDir
12680
12879
  }),
12681
- path: join27(srcDir, "content.config.ts")
12880
+ path: join28(srcDir, "content.config.ts")
12682
12881
  },
12683
12882
  {
12684
12883
  content: catchAllPageTemplate({
12685
- askEnabled,
12686
12884
  exportEpub,
12687
12885
  exportPdf,
12688
12886
  mathEnabled: usesMath,
12689
12887
  needsReact
12690
12888
  }),
12691
- path: join27(srcDir, "pages", "[...slug].astro")
12889
+ path: join28(srcDir, "pages", "[...slug].astro")
12692
12890
  },
12693
12891
  {
12694
12892
  content: planComponentSlots(componentsImport, null).module,
12695
- path: join27(genDir, "components.ts")
12893
+ path: join28(genDir, "components.ts")
12696
12894
  },
12697
12895
  {
12698
12896
  content: islandMapTemplate(islands.islands),
12699
- path: join27(genDir, "islands.ts")
12897
+ path: join28(genDir, "islands.ts")
12700
12898
  },
12701
12899
  {
12702
12900
  content: exampleMapTemplate(examples.examples, config.basePath),
12703
- path: join27(genDir, "examples.ts")
12901
+ path: join28(genDir, "examples.ts")
12704
12902
  },
12705
12903
  {
12706
12904
  content: examplesEntryTemplate({
@@ -12708,7 +12906,7 @@ var eject = async (root) => {
12708
12906
  sources: ["../../**/*.{astro,jsx,svelte,ts,tsx,vue}"],
12709
12907
  userCss: userExamplesCss
12710
12908
  }),
12711
- path: join27(genDir, "examples.css")
12909
+ path: join28(genDir, "examples.css")
12712
12910
  },
12713
12911
  {
12714
12912
  content: tailwindEntryTemplate({
@@ -12720,26 +12918,30 @@ var eject = async (root) => {
12720
12918
  twoslashCss: twoslashCss(),
12721
12919
  userTheme
12722
12920
  }),
12723
- path: join27(genDir, "app.css")
12921
+ path: join28(genDir, "app.css")
12922
+ },
12923
+ { content: buildRuntimeData(project), path: join28(genDir, "data.json") },
12924
+ {
12925
+ content: askComponentTemplate(askEnabled),
12926
+ path: join28(genDir, "Ask.astro")
12724
12927
  },
12725
- { content: buildRuntimeData(project), path: join27(genDir, "data.json") },
12726
12928
  {
12727
12929
  content: `${JSON.stringify(ejectOpenApiData(project))}
12728
12930
  `,
12729
- path: join27(genDir, "openapi.json")
12931
+ path: join28(genDir, "openapi.json")
12730
12932
  },
12731
12933
  {
12732
12934
  content: `${JSON.stringify(rawMarkdown)}
12733
12935
  `,
12734
- path: join27(genDir, "raw-markdown.json")
12936
+ path: join28(genDir, "raw-markdown.json")
12735
12937
  },
12736
12938
  {
12737
12939
  content: rawMarkdownEndpointTemplate("md"),
12738
- path: join27(srcDir, "pages", "[...slug].md.ts")
12940
+ path: join28(srcDir, "pages", "[...slug].md.ts")
12739
12941
  },
12740
12942
  {
12741
12943
  content: rawMarkdownEndpointTemplate("mdx"),
12742
- path: join27(srcDir, "pages", "[...slug].mdx.ts")
12944
+ path: join28(srcDir, "pages", "[...slug].mdx.ts")
12743
12945
  }
12744
12946
  ];
12745
12947
  if (askEnabled) {
@@ -12748,11 +12950,10 @@ var eject = async (root) => {
12748
12950
  if (config.seo.og.enabled) {
12749
12951
  files.push({
12750
12952
  content: ogEndpointTemplate(customOgRoutes(pages, config.title)),
12751
- path: join27(srcDir, "pages", "og", "[...slug].png.ts")
12953
+ path: join28(srcDir, "pages", "og", "[...slug].png.ts")
12752
12954
  });
12753
12955
  }
12754
12956
  files.push(...await mcpFiles(project, pages, srcDir, genDir), ...changelogFiles(project, pages, srcDir, {
12755
- askEnabled,
12756
12957
  exportEpub,
12757
12958
  exportPdf,
12758
12959
  needsReact,
@@ -12761,28 +12962,28 @@ var eject = async (root) => {
12761
12962
  if (!routeIsTaken(pages, project.graph.pages, "/404")) {
12762
12963
  files.push({
12763
12964
  content: notFoundPageTemplate(),
12764
- path: join27(srcDir, "pages", "404.astro")
12965
+ path: join28(srcDir, "pages", "404.astro")
12765
12966
  });
12766
12967
  }
12767
12968
  files.push({
12768
12969
  content: searchClientTemplate(config),
12769
- path: join27(genDir, "search-client.ts")
12970
+ path: join28(genDir, "search-client.ts")
12770
12971
  });
12771
12972
  if (servesStaticIndex(config.search.provider)) {
12772
12973
  const documents = await buildSearchDocuments(project);
12773
12974
  files.push({
12774
12975
  content: `${JSON.stringify(documents)}
12775
12976
  `,
12776
- path: join27(genDir, "search.json")
12977
+ path: join28(genDir, "search.json")
12777
12978
  }, {
12778
12979
  content: searchEndpointTemplate(),
12779
- path: join27(srcDir, "pages", "blume-search.json.ts")
12980
+ path: join28(srcDir, "pages", "blume-search.json.ts")
12780
12981
  });
12781
12982
  }
12782
12983
  if (config.search.provider === "mixedbread") {
12783
12984
  files.push({
12784
12985
  content: mixedbreadSearchEndpointTemplate(config.search.mixedbread?.storeId ?? ""),
12785
- path: join27(srcDir, "pages", "api", "search.ts")
12986
+ path: join28(srcDir, "pages", "api", "search.ts")
12786
12987
  });
12787
12988
  }
12788
12989
  const feeds = buildRssFeeds(project);
@@ -12791,10 +12992,10 @@ var eject = async (root) => {
12791
12992
  files.push({
12792
12993
  content: `${JSON.stringify(feedXml)}
12793
12994
  `,
12794
- path: join27(genDir, "rss.json")
12995
+ path: join28(genDir, "rss.json")
12795
12996
  }, {
12796
12997
  content: rssEndpointTemplate(),
12797
- path: join27(srcDir, "pages", "[section]", "rss.xml.ts")
12998
+ path: join28(srcDir, "pages", "[section]", "rss.xml.ts")
12798
12999
  });
12799
13000
  }
12800
13001
  const warnings = [];
@@ -12808,28 +13009,28 @@ var eject = async (root) => {
12808
13009
  for (const file of references.files) {
12809
13010
  files.push({
12810
13011
  content: file.content,
12811
- path: join27(srcDir, "pages", file.pagePath)
13012
+ path: join28(srcDir, "pages", file.pagePath)
12812
13013
  });
12813
13014
  }
12814
13015
  }
12815
13016
  files.push(...islands.islands.map((island) => ({
12816
13017
  content: islandWrapperTemplate(island),
12817
- path: join27(genDir, "islands", `${island.name}.astro`)
13018
+ path: join28(genDir, "islands", `${island.name}.astro`)
12818
13019
  })), ...examples.examples.map((example) => ({
12819
13020
  content: exampleWrapperTemplate(example),
12820
- path: join27(genDir, "examples", `${exampleSlug(example.path)}.astro`)
13021
+ path: join28(genDir, "examples", `${exampleSlug(example.path)}.astro`)
12821
13022
  })), ...examplesPreviewFiles(srcDir, config.basePath, examples.examples.length > 0));
12822
13023
  for (const [entryId, content] of staged) {
12823
- files.push({ content, path: join27(root, stagedDir, entryId) });
13024
+ files.push({ content, path: join28(root, stagedDir, entryId) });
12824
13025
  }
12825
- const written = files.filter((file) => !(file.skipIfExists && existsSync17(file.path)));
13026
+ const written = files.filter((file) => !(file.skipIfExists && existsSync18(file.path)));
12826
13027
  await Promise.all(written.map(async (file) => {
12827
- await mkdir7(join27(file.path, ".."), { recursive: true });
13028
+ await mkdir7(join28(file.path, ".."), { recursive: true });
12828
13029
  await writeFile8(file.path, file.content, "utf-8");
12829
13030
  }));
12830
- const assetsSrc = join27(context.outDir, "public", "blume-assets");
12831
- if (existsSync17(assetsSrc)) {
12832
- await cp2(assetsSrc, join27(root, "public", "blume-assets"), {
13031
+ const assetsSrc = join28(context.outDir, "public", "blume-assets");
13032
+ if (existsSync18(assetsSrc)) {
13033
+ await cp2(assetsSrc, join28(root, "public", "blume-assets"), {
12833
13034
  recursive: true
12834
13035
  });
12835
13036
  }
@@ -12839,7 +13040,7 @@ var eject = async (root) => {
12839
13040
 
12840
13041
  // src/cli/eject-scripts.ts
12841
13042
  import { readFile as readFile13, writeFile as writeFile9 } from "node:fs/promises";
12842
- import { join as join28 } from "pathe";
13043
+ import { join as join29 } from "pathe";
12843
13044
  var droppedArtifactNotices = (config) => {
12844
13045
  const notices = [];
12845
13046
  if (config.search.provider === "pagefind") {
@@ -12866,7 +13067,7 @@ var droppedArtifactNotices = (config) => {
12866
13067
  return notices;
12867
13068
  };
12868
13069
  var updatePackageScripts = async (root) => {
12869
- const pkgPath = join28(root, "package.json");
13070
+ const pkgPath = join29(root, "package.json");
12870
13071
  let pkg;
12871
13072
  try {
12872
13073
  pkg = JSON.parse(await readFile13(pkgPath, "utf-8"));
@@ -12885,9 +13086,9 @@ var updatePackageScripts = async (root) => {
12885
13086
  };
12886
13087
 
12887
13088
  // src/cli/init/scaffold.ts
12888
- import { existsSync as existsSync18 } from "node:fs";
13089
+ import { existsSync as existsSync19 } from "node:fs";
12889
13090
  import { mkdir as mkdir8, writeFile as writeFile10 } from "node:fs/promises";
12890
- import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute9, join as join29, relative as relative13 } from "pathe";
13091
+ import { basename as basename4, dirname as dirname11, isAbsolute as isAbsolute9, join as join30, relative as relative13 } from "pathe";
12891
13092
 
12892
13093
  // src/core/package-json.ts
12893
13094
  var toPackageName = (raw) => raw.toLowerCase().replaceAll(/[^a-z0-9._-]+/gu, "-").replaceAll(/^[-_.]+|[-_.]+$/gu, "") || "docs";
@@ -12946,7 +13147,7 @@ var STARTERS = {
12946
13147
  files: (dir) => [
12947
13148
  {
12948
13149
  content: page("API Reference", "Explore the API.", "# API Reference\n\nYour OpenAPI spec renders at [`/api`](/api). Point `openapi.sources` at your own spec in `blume.config.ts`."),
12949
- path: join29(dir, "index.mdx")
13150
+ path: join30(dir, "index.mdx")
12950
13151
  }
12951
13152
  ]
12952
13153
  },
@@ -12961,7 +13162,7 @@ var STARTERS = {
12961
13162
  files: (dir) => [
12962
13163
  {
12963
13164
  content: page("Introduction", "Welcome to your new Blume docs.", "# Introduction\n\nWrite your docs here, and log releases under `changelog/`."),
12964
- path: join29(dir, "index.mdx")
13165
+ path: join30(dir, "index.mdx")
12965
13166
  },
12966
13167
  {
12967
13168
  content: `---
@@ -12972,7 +13173,7 @@ date: 2026-01-01
12972
13173
 
12973
13174
  The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries beside it.
12974
13175
  `,
12975
- path: join29(dir, "changelog", "v1-0-0.mdx")
13176
+ path: join30(dir, "changelog", "v1-0-0.mdx")
12976
13177
  }
12977
13178
  ]
12978
13179
  },
@@ -12985,7 +13186,7 @@ The first release. Edit \`${dir}/changelog/v1-0-0.mdx\` or add new entries besid
12985
13186
  Welcome to **Blume** — markdown-first docs powered by Astro and Vite.
12986
13187
 
12987
13188
  Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
12988
- path: join29(dir, "index.mdx")
13189
+ path: join30(dir, "index.mdx")
12989
13190
  }
12990
13191
  ]
12991
13192
  },
@@ -12996,11 +13197,11 @@ Edit \`${dir}/index.mdx\` to get started, then run \`blume dev\`.`),
12996
13197
  content: page("Introduction", "Get started with the SDK.", `# Introduction
12997
13198
 
12998
13199
  Install the SDK and make your first call. See [Installation](/installation).`),
12999
- path: join29(dir, "index.mdx")
13200
+ path: join30(dir, "index.mdx")
13000
13201
  },
13001
13202
  {
13002
13203
  content: page("Installation", "Install the SDK.", "# Installation\n\n```package-install\nyour-sdk\n```"),
13003
- path: join29(dir, "installation.mdx")
13204
+ path: join30(dir, "installation.mdx")
13004
13205
  }
13005
13206
  ]
13006
13207
  }
@@ -13015,7 +13216,7 @@ var detectPackageManager = (userAgent) => {
13015
13216
  const name = userAgent?.split("/")[0];
13016
13217
  return name !== undefined && PACKAGE_MANAGERS.includes(name) ? name : "npm";
13017
13218
  };
13018
- var validateContentDir = (root, dir) => isAbsolute9(dir) || relative13(root, join29(root, dir)).startsWith("..") ? "Must be a relative path inside the project." : undefined;
13219
+ var validateContentDir = (root, dir) => isAbsolute9(dir) || relative13(root, join30(root, dir)).startsWith("..") ? "Must be a relative path inside the project." : undefined;
13019
13220
  var titleize = (raw) => {
13020
13221
  const words = raw.replaceAll(/[-_.]+/gu, " ").split(/\s+/u).filter(Boolean);
13021
13222
  return words.length === 0 ? "My Docs" : words.map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
@@ -13098,17 +13299,17 @@ var buildPlan = (root, answers) => {
13098
13299
  const files = [
13099
13300
  {
13100
13301
  content: blumePackageJson(toPackageName(basename4(root)), extraDepsFor(answers.sources)),
13101
- path: join29(root, "package.json")
13302
+ path: join30(root, "package.json")
13102
13303
  },
13103
- { content: buildConfig(answers), path: join29(root, "blume.config.ts") }
13304
+ { content: buildConfig(answers), path: join30(root, "blume.config.ts") }
13104
13305
  ];
13105
13306
  if (answers.sources.length === 0 || answers.sources.includes("filesystem")) {
13106
- files.push(...STARTERS[answers.template].files(answers.contentDir).map((file) => ({ ...file, path: join29(root, file.path) })));
13307
+ files.push(...STARTERS[answers.template].files(answers.contentDir).map((file) => ({ ...file, path: join30(root, file.path) })));
13107
13308
  }
13108
13309
  return files;
13109
13310
  };
13110
13311
  var writeFileSafe = async (file, log) => {
13111
- if (existsSync18(file.path)) {
13312
+ if (existsSync19(file.path)) {
13112
13313
  log.info(`Skipped existing ${file.path}`);
13113
13314
  return false;
13114
13315
  }
@@ -13415,10 +13616,10 @@ var initCommand = defineCommand7({
13415
13616
  });
13416
13617
 
13417
13618
  // src/cli/commands/preview.ts
13418
- import { existsSync as existsSync19 } from "node:fs";
13619
+ import { existsSync as existsSync20 } from "node:fs";
13419
13620
  import { preview } from "astro";
13420
13621
  import { defineCommand as defineCommand8 } from "citty";
13421
- import { join as join30 } from "pathe";
13622
+ import { join as join31 } from "pathe";
13422
13623
  var previewCommand = defineCommand8({
13423
13624
  args: {
13424
13625
  host: { description: "Network host to bind.", type: "string" },
@@ -13432,7 +13633,7 @@ var previewCommand = defineCommand8({
13432
13633
  const root = process.cwd();
13433
13634
  const { config } = await loadConfig(root);
13434
13635
  const context = resolveProjectContext(root, config);
13435
- if (!existsSync19(join30(context.outDir, "astro.config.mjs"))) {
13636
+ if (!existsSync20(join31(context.outDir, "astro.config.mjs"))) {
13436
13637
  logger.error("No build found. Run `blume build` first.");
13437
13638
  process.exit(1);
13438
13639
  }
@@ -13450,7 +13651,7 @@ var previewCommand = defineCommand8({
13450
13651
  // src/cli/commands/sync.ts
13451
13652
  import { rm as rm4 } from "node:fs/promises";
13452
13653
  import { defineCommand as defineCommand9 } from "citty";
13453
- import { join as join31 } from "pathe";
13654
+ import { join as join32 } from "pathe";
13454
13655
  var syncCommand = defineCommand9({
13455
13656
  args: {
13456
13657
  force: {
@@ -13472,7 +13673,7 @@ var syncCommand = defineCommand9({
13472
13673
  if (args.force) {
13473
13674
  const { config } = await loadConfig(root);
13474
13675
  const context = resolveProjectContext(root, config);
13475
- await rm4(join31(context.outDir, "cache"), { force: true, recursive: true });
13676
+ await rm4(join32(context.outDir, "cache"), { force: true, recursive: true });
13476
13677
  logger.info("Cleared source cache.");
13477
13678
  }
13478
13679
  const lock = readDevLock(resolveRuntimeDir(root));
@@ -13490,13 +13691,13 @@ var syncCommand = defineCommand9({
13490
13691
  });
13491
13692
 
13492
13693
  // src/cli/commands/validate.ts
13493
- import { existsSync as existsSync21 } from "node:fs";
13694
+ import { existsSync as existsSync22 } from "node:fs";
13494
13695
  import { defineCommand as defineCommand10 } from "citty";
13495
- import { join as join33 } from "pathe";
13696
+ import { join as join34 } from "pathe";
13496
13697
 
13497
13698
  // src/core/links.ts
13498
- import { existsSync as existsSync20 } from "node:fs";
13499
- import { basename as basename6, join as join32 } from "pathe";
13699
+ import { existsSync as existsSync21 } from "node:fs";
13700
+ import { basename as basename6, join as join33 } from "pathe";
13500
13701
  var HTTP = /^https?:\/\//iu;
13501
13702
  var PROTOCOL_RELATIVE = /^\/\//u;
13502
13703
  var SCHEME = /^[a-z][a-z0-9+.-]*:/iu;
@@ -13515,7 +13716,7 @@ var STATUS_NOT_FOUND = 404;
13515
13716
  var STATUS_GONE = 410;
13516
13717
  var STATUS_METHOD_NOT_ALLOWED = 405;
13517
13718
  var STATUS_NOT_IMPLEMENTED = 501;
13518
- var assetIsPresent = (resolved, ctx) => ctx.publicDir !== null && existsSync20(join32(ctx.publicDir, resolved));
13719
+ var assetIsPresent = (resolved, ctx) => ctx.publicDir !== null && existsSync21(join33(ctx.publicDir, resolved));
13519
13720
  var NUMERIC_PREFIX3 = /^\d+[-_.]/u;
13520
13721
  var isIndexPage = (page2) => /^index\.(?:md|mdx)$/iu.test(basename6(page2.navPath).replace(NUMERIC_PREFIX3, ""));
13521
13722
  var applyRelativePart = (segments, part) => {
@@ -13781,12 +13982,12 @@ var validateCommand = defineCommand10({
13781
13982
  });
13782
13983
  extraRoutes.push(...manifest.routes.flatMap((route) => route.fallback ? [route.path] : []));
13783
13984
  }
13784
- const publicDir = join33(root, "public");
13985
+ const publicDir = join34(root, "public");
13785
13986
  diagnostics.push(...await validateLinks(project.graph, {
13786
13987
  basePath: project.config.basePath,
13787
13988
  checkExternal: Boolean(args.external),
13788
13989
  extraRoutes,
13789
- publicDir: existsSync21(publicDir) ? publicDir : null,
13990
+ publicDir: existsSync22(publicDir) ? publicDir : null,
13790
13991
  redirects: project.config.redirects
13791
13992
  }));
13792
13993
  } catch (error) {
@@ -13847,5 +14048,5 @@ process.on("unhandledRejection", (error) => {
13847
14048
  });
13848
14049
  runMain(main);
13849
14050
 
13850
- //# debugId=5F5E50EAF35B586A64756E2164756E21
14051
+ //# debugId=E383A1CFB1E8864864756E2164756E21
13851
14052
  //# sourceMappingURL=index.js.map