blume 0.5.3 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/cli/index.js +3349 -7024
  2. package/dist/cli/index.js.map +39 -69
  3. package/dist/types/core/config.d.ts +0 -8
  4. package/dist/types/core/data.d.ts +6 -2
  5. package/dist/types/core/i18n-ui.d.ts +50 -0
  6. package/dist/types/core/schema.d.ts +379 -485
  7. package/dist/types/core/types.d.ts +8 -6
  8. package/docs/advanced/meta.ts +1 -8
  9. package/docs/advanced/skills.mdx +28 -0
  10. package/docs/configuration/ai.mdx +58 -0
  11. package/docs/configuration/index.mdx +13 -17
  12. package/docs/configuration/seo.mdx +59 -1
  13. package/docs/configuration/theming.mdx +15 -18
  14. package/docs/content/components.mdx +2 -53
  15. package/docs/content/i18n.mdx +0 -4
  16. package/docs/content/meta.mdx +3 -17
  17. package/docs/content/navigation.mdx +41 -4
  18. package/docs/content/syntax.mdx +1 -1
  19. package/docs/index.mdx +0 -1
  20. package/docs/reference/cli.mdx +12 -13
  21. package/package.json +6 -6
  22. package/skills/blume/SKILL.md +71 -0
  23. package/skills/blume-update-docs/SKILL.md +52 -0
  24. package/skills/blume-update-docs/references/audit-checklist.md +46 -0
  25. package/src/ai/agent-readability.ts +97 -0
  26. package/src/ai/ask-context.ts +131 -8
  27. package/src/ai/ask-data.ts +4 -1
  28. package/src/astro/generate.ts +19 -12
  29. package/src/astro/integration.ts +0 -21
  30. package/src/astro/templates.ts +33 -21
  31. package/src/cli/commands/build.ts +15 -0
  32. package/src/cli/commands/dev.ts +31 -20
  33. package/src/cli/commands/validate.ts +0 -2
  34. package/src/cli/dev-lock.ts +94 -21
  35. package/src/cli/index.ts +0 -2
  36. package/src/components/BlumePage.astro +0 -6
  37. package/src/components/Icon.astro +1 -12
  38. package/src/components/content/AccordionItem.astro +3 -6
  39. package/src/components/content/Badge.astro +1 -3
  40. package/src/components/content/Callout.astro +3 -9
  41. package/src/components/content/Card.astro +2 -3
  42. package/src/components/content/ColorItem.astro +2 -2
  43. package/src/components/content/Column.astro +1 -1
  44. package/src/components/content/GithubInfo.astro +11 -10
  45. package/src/components/content/Prompt.astro +1 -1
  46. package/src/components/content/Step.astro +3 -4
  47. package/src/components/content/Tab.astro +2 -3
  48. package/src/components/content/TypeTable.astro +13 -8
  49. package/src/components/content/Update.astro +1 -1
  50. package/src/components/islands/AskAI.astro +66 -2
  51. package/src/components/islands/ask-ai.tsx +289 -53
  52. package/src/components/layout/Header.astro +27 -4
  53. package/src/components/layout/Logo.astro +5 -1
  54. package/src/components/layout/NavSelector.astro +1 -1
  55. package/src/components/layout/NavTree.astro +15 -15
  56. package/src/components/layout/PageActions.astro +73 -30
  57. package/src/components/layout/PageLayout.astro +42 -0
  58. package/src/components/layout/ReferenceLayout.astro +1 -0
  59. package/src/components/layout/RootLayout.astro +79 -4
  60. package/src/components/layout/Search.astro +5 -5
  61. package/src/components/layout/nav-utils.ts +9 -4
  62. package/src/components/openapi/ApiOverview.astro +4 -50
  63. package/src/components/openapi/ApiTagOperations.astro +42 -0
  64. package/src/core/builtin-tags.ts +1 -3
  65. package/src/core/config.ts +5 -28
  66. package/src/core/data.ts +6 -2
  67. package/src/core/graph.ts +8 -6
  68. package/src/core/i18n-ui.ts +5 -0
  69. package/src/core/links.ts +5 -19
  70. package/src/core/meta.ts +1 -1
  71. package/src/core/nav-diagnostics.ts +7 -0
  72. package/src/core/navigation.ts +38 -17
  73. package/src/core/project-graph.ts +0 -5
  74. package/src/core/schema.ts +133 -95
  75. package/src/core/sources/filesystem.ts +5 -1
  76. package/src/core/sources/resolve.ts +0 -13
  77. package/src/core/sources/watch.ts +43 -11
  78. package/src/core/types.ts +8 -6
  79. package/src/deploy/robots.ts +37 -4
  80. package/src/openapi/parse.ts +197 -14
  81. package/src/openapi/render-mdx.ts +44 -10
  82. package/src/openapi/scalar.ts +1 -1
  83. package/src/openapi/source.ts +19 -2
  84. package/src/search/documents.ts +9 -2
  85. package/src/theme/entry.ts +45 -17
  86. package/src/theme/icons.ts +18 -109
  87. package/src/theme/palette.ts +25 -51
  88. package/src/theme/twoslash.ts +6 -1
  89. package/dist/types/core/bridge.d.ts +0 -24
  90. package/dist/types/core/package-json.d.ts +0 -12
  91. package/dist/types/migrate/mintlify/assets.d.ts +0 -8
  92. package/dist/types/migrate/mintlify/config.d.ts +0 -16
  93. package/dist/types/migrate/mintlify/i18n.d.ts +0 -7
  94. package/dist/types/migrate/shared.d.ts +0 -153
  95. package/docs/advanced/bridge.mdx +0 -76
  96. package/docs/advanced/migrate.mdx +0 -124
  97. package/src/astro/static-assets.ts +0 -124
  98. package/src/cli/commands/migrate.ts +0 -39
  99. package/src/components/content/ApiField.astro +0 -75
  100. package/src/components/content/ParamField.astro +0 -39
  101. package/src/components/content/RequestField.astro +0 -23
  102. package/src/components/content/ResponseField.astro +0 -23
  103. package/src/components/content/Warning.astro +0 -9
  104. package/src/core/assets.ts +0 -31
  105. package/src/core/bridge.ts +0 -102
  106. package/src/core/sources/mintlify.ts +0 -190
  107. package/src/migrate/fumadocs/config.ts +0 -155
  108. package/src/migrate/fumadocs/content.ts +0 -376
  109. package/src/migrate/fumadocs/frontmatter.ts +0 -18
  110. package/src/migrate/fumadocs/groups.ts +0 -237
  111. package/src/migrate/fumadocs/index.ts +0 -355
  112. package/src/migrate/fumadocs/meta.ts +0 -244
  113. package/src/migrate/migrate.ts +0 -53
  114. package/src/migrate/mintlify/assets.ts +0 -46
  115. package/src/migrate/mintlify/config.ts +0 -954
  116. package/src/migrate/mintlify/content.ts +0 -120
  117. package/src/migrate/mintlify/frontmatter.ts +0 -126
  118. package/src/migrate/mintlify/i18n.ts +0 -51
  119. package/src/migrate/mintlify/icons.ts +0 -128
  120. package/src/migrate/mintlify/index.ts +0 -459
  121. package/src/migrate/mintlify/snippets.ts +0 -315
  122. package/src/migrate/mintlify/transform.ts +0 -82
  123. package/src/migrate/nextra/content.ts +0 -46
  124. package/src/migrate/nextra/frontmatter.ts +0 -40
  125. package/src/migrate/nextra/index.ts +0 -389
  126. package/src/migrate/nextra/meta.ts +0 -266
  127. package/src/migrate/shared.ts +0 -801
  128. package/src/migrate/starlight/config.ts +0 -455
  129. package/src/migrate/starlight/content.ts +0 -75
  130. package/src/migrate/starlight/frontmatter.ts +0 -111
  131. package/src/migrate/starlight/i18n.ts +0 -54
  132. package/src/migrate/starlight/index.ts +0 -131
@@ -1,124 +0,0 @@
1
- ---
2
- title: Migrate
3
- description: Convert a Mintlify, Fumadocs, Nextra, or Starlight project to Blume in one command — config, content, navigation, and assets translated to idiomatic Blume.
4
- ---
5
-
6
- Already have a docs site somewhere else? `blume migrate` is a one-shot codemod that converts a project from another docs framework into Blume — translating the config, rewriting each page to idiomatic Blume MDX, converting navigation files, and relocating assets, all in place. When it finishes you have a `blume.config.ts` and a tree that `blume dev` can serve.
7
-
8
- ```bash
9
- npx blume migrate <tool>
10
- ```
11
-
12
- The `<tool>` is the framework you're coming from:
13
-
14
- | Tool | Detects | Command |
15
- | --- | --- | --- |
16
- | [Mintlify](#mintlify) | `docs.json` / `mint.json` | `blume migrate mintlify` |
17
- | [Fumadocs](#fumadocs) | `content/docs` + `meta.json` | `blume migrate fumadocs` |
18
- | [Nextra](#nextra) | `content/` or `pages/` + `_meta` | `blume migrate nextra` |
19
- | [Starlight](#starlight) | `src/content/docs` + `astro.config.*` | `blume migrate starlight` |
20
-
21
- :::warning
22
- A migration **rewrites files in place** — pages, config, navigation, and assets. Commit (or stash) your work first so you can review the diff and roll back cleanly.
23
- :::
24
-
25
- ## What a migration does
26
-
27
- Every migrator runs the same shape of work, tuned to the source framework:
28
-
29
- <Steps>
30
- <Step title="Translate the config">
31
- The framework's config — `docs.json`, a `starlight({...})` block, and so on — becomes a `blume.config.ts` at your project root, with navigation, theme, and site chrome mapped across.
32
- </Step>
33
- <Step title="Rewrite every page">
34
- Each `.md`/`.mdx` file is rewritten to idiomatic Blume markup: framework callouts become [`:::` directives](/docs/content/syntax), components are renamed or converted, and frontmatter is mapped to Blume's [page schema](/docs/reference/frontmatter). Keys Blume doesn't recognize are dropped and reported.
35
- </Step>
36
- <Step title="Convert navigation">
37
- Navigation files (`_meta.{js,ts,json}`, `meta.json`) become typed [`meta.ts`](/docs/content/meta) files, preserving order and titles.
38
- </Step>
39
- <Step title="Relocate assets and scripts">
40
- Referenced asset folders are kept in place and served via [`content.assets`](/docs/content/sources); loose top-level files move under `public/`. Your `dev`/`build`/`start` npm scripts are repointed at the matching `blume` commands.
41
- </Step>
42
- </Steps>
43
-
44
- When it's done the CLI prints how many files it touched, a list of **warnings** — anything that needs a human eye, like a component with no Blume equivalent or a dropped frontmatter key — and a reminder to review the generated config:
45
-
46
- ```txt
47
- ✔ Migrated 42 content file(s).
48
- ⚠ Components without a Blume equivalent need manual review: <Frame>.
49
- ⚠ Dropped unsupported page frontmatter keys: mode, "og:image".
50
- ▶ Review blume.config.ts and run `blume dev`.
51
- ```
52
-
53
- ## Tools
54
-
55
- ### Mintlify
56
-
57
- Reads `docs.json` (or legacy `mint.json`) and rewrites every page **in place** — content stays at the project root (`content.root` is `"."`).
58
-
59
- - Snippets under `/snippets` are inlined as includes; component snippets (e.g. `.jsx`) are kept and their imports rewritten to relative paths.
60
- - A Mintlify `openapi` spec — declared top-level or on a nav group (a path, URL, or `{ source, directory }`) — maps to Blume's [native OpenAPI reference](/docs/advanced/api-reference) (`openapi.sources`), which renders one real page per operation. A group's `directory` becomes the reference's route; endpoint refs like `GET /users` are dropped since Blume generates them from the spec.
61
- - `docs.json` `variables` are inlined into content — Blume has no runtime `{{variable}}` substitution.
62
- - The source `docs.json`/`mint.json` is removed once `blume.config.ts` is safely written. Leaving it around would keep the project a [Bridge mode](/docs/advanced/bridge) candidate — a later run without a Blume config would silently serve the un-migrated Mintlify site.
63
- - Multi-language projects map to [`i18n.locales`](/docs/content/i18n); the language nav selector is dropped in favor of Blume's locale switching.
64
- - Icons resolve against the real bundled libraries — Font Awesome (free), Lucide, and Tabler. The migrator sets [`icons.library: fontawesome`](/docs/content/components#default-library) (Mintlify's default), so Font Awesome names (`shield-halved`, `gauge-high`, `layer-group`, …) and `iconType` styles render unchanged. Pro-only FA styles (`light`/`thin`/`duotone`/`sharp-solid`) fall back to solid.
65
- - Fonts map to [`theme.fonts`](/docs/configuration/theming) when the family is one of Blume's curated Google Fonts (`fonts.family`, or a `heading`/`body` split); a family outside that set is warned about, not guessed. Header links (`navbar.links`/`navbar.primary`) and footer socials (`footer.socials`) have no `blume.config` equivalent yet, so they're reported as warnings rather than dropped silently — re-add them with [`navigation.tabs`](/docs/content/navigation) or a Header/Footer [layout override](/docs/advanced/custom-pages). The contextual page menu and last-updated timestamp are already Blume defaults.
66
- - Field components — [`<ParamField>`, `<ResponseField>`, `<RequestField>`](/docs/content/components#api-fields) — render natively via Blume's compat components, so hand-written CLI/SDK/endpoint field docs carry over unchanged. Any remaining component with no Blume equivalent (e.g. `<Update>`) is flagged for manual review.
67
-
68
- :::tip
69
- Want to preview Blume against a Mintlify codebase **without** rewriting anything? Run `blume dev` with no config and Blume serves `docs.json` as-is in [Bridge mode](/docs/advanced/bridge). Migrate when you're ready to commit.
70
- :::
71
-
72
- ### Fumadocs
73
-
74
- Reads `content/docs` + `meta.json`. Moves pages into `docs/`, preserving the `/docs` route prefix, and rewrites Fumadocs MDX to Blume markup: callouts, `<Cards>`/`<Accordions>`/`<Files>`, `<Tabs items>`, and `<include>` directives are all converted. Each `meta.json` becomes a typed `meta.ts`.
75
-
76
- ### Nextra
77
-
78
- Reads `content/` or `pages/` plus `_meta` files. Moves pages into `docs/`, rewrites `<Callout>`s to [directives](/docs/content/syntax), and converts every `_meta.{js,ts,json}` into a typed `meta.ts` — navigation order and titles preserved.
79
-
80
- ### Starlight
81
-
82
- Reads `src/content/docs` + your `astro.config.*`. Translates the `starlight({...})` options into `blume.config.ts` and rewrites each page **in place** (content stays under `src/content/docs`): asides become directives, components are renamed, and frontmatter is mapped across.
83
-
84
- ## After migrating
85
-
86
- <Steps>
87
- <Step title="Review the config">
88
- Open `blume.config.ts` and check the mapped navigation, theme, and site
89
- settings. The migrator is thorough but conservative — some source options
90
- have no Blume equivalent and are left out.
91
- </Step>
92
- <Step title="Read the warnings">
93
- Each warning points at something the codemod couldn't fully translate. Work
94
- through them before shipping.
95
- </Step>
96
- <Step title="Run the dev server">
97
- Serve the migrated site. Blume [validates your content](/docs/reference/cli#validating-links) as it serves it, so broken links, missing anchors, and schema errors surface immediately.
98
-
99
- ```bash
100
- blume dev
101
- ```
102
-
103
- </Step>
104
- <Step title="Delete leftovers">
105
- Old framework files (lockfile entries, `astro.config`, `theme.config`,
106
- framework dependencies) are left untouched so nothing is lost. Remove them
107
- by hand once you've confirmed the site builds.
108
- </Step>
109
- </Steps>
110
-
111
- <CardGroup cols={2}>
112
- <Card title="Bridge mode" href="/docs/advanced/bridge" icon="cable">
113
- Serve a Mintlify project with `blume dev` — no migration, no file changes.
114
- </Card>
115
- <Card title="CLI reference" href="/docs/reference/cli" icon="terminal">
116
- Every command and flag, including `blume migrate`.
117
- </Card>
118
- <Card title="Content sources" href="/docs/content/sources" icon="folder-tree">
119
- How Blume scans content, assets, and remote sources into a site.
120
- </Card>
121
- <Card title="Frontmatter" href="/docs/reference/frontmatter" icon="file-text">
122
- The page schema your migrated frontmatter is mapped onto.
123
- </Card>
124
- </CardGroup>
@@ -1,124 +0,0 @@
1
- import { createReadStream, existsSync, statSync } from "node:fs";
2
- import type { Stats } from "node:fs";
3
- import { cp } from "node:fs/promises";
4
- import type { IncomingMessage, ServerResponse } from "node:http";
5
-
6
- import { extname, join, relative, resolve, sep } from "pathe";
7
-
8
- import type { AssetMount } from "../core/assets.ts";
9
-
10
- /** Content types for the asset extensions a docs project commonly serves. */
11
- const MIME_TYPES: Record<string, string> = {
12
- ".avif": "image/avif",
13
- ".css": "text/css; charset=utf-8",
14
- ".gif": "image/gif",
15
- ".ico": "image/x-icon",
16
- ".jpeg": "image/jpeg",
17
- ".jpg": "image/jpeg",
18
- ".js": "text/javascript; charset=utf-8",
19
- ".json": "application/json; charset=utf-8",
20
- ".mp4": "video/mp4",
21
- ".otf": "font/otf",
22
- ".pdf": "application/pdf",
23
- ".png": "image/png",
24
- ".svg": "image/svg+xml",
25
- ".ttf": "font/ttf",
26
- ".txt": "text/plain; charset=utf-8",
27
- ".webm": "video/webm",
28
- ".webp": "image/webp",
29
- ".woff": "font/woff",
30
- ".woff2": "font/woff2",
31
- };
32
-
33
- const mimeType = (file: string): string =>
34
- MIME_TYPES[extname(file).toLowerCase()] ?? "application/octet-stream";
35
-
36
- /** Whether `child` is `parent` itself or a path nested under it. */
37
- const isContained = (parent: string, child: string): boolean => {
38
- const rel = relative(parent, child);
39
- return rel === "" || (!rel.startsWith("..") && !rel.startsWith(sep));
40
- };
41
-
42
- /** Resolve a request URL to an on-disk file within one of the mounts, if any. */
43
- const resolveRequest = (url: string, mounts: AssetMount[]): string | null => {
44
- let pathname: string;
45
- try {
46
- pathname = decodeURIComponent(
47
- (url.split("?")[0] ?? "").split("#")[0] ?? ""
48
- );
49
- } catch {
50
- // Malformed percent-encoding (`/images/%zz`) throws URIError; treat it as
51
- // a plain miss (404) rather than a middleware exception.
52
- return null;
53
- }
54
- for (const mount of mounts) {
55
- if (pathname !== mount.url && !pathname.startsWith(`${mount.url}/`)) {
56
- continue;
57
- }
58
- // `.` + rel keeps the join relative so an absolute-looking suffix can't
59
- // escape the mount; the containment check rejects `..` traversal outright.
60
- const file = resolve(mount.dir, `.${pathname.slice(mount.url.length)}`);
61
- if (isContained(mount.dir, file)) {
62
- return file;
63
- }
64
- }
65
- return null;
66
- };
67
-
68
- /** `statSync` that returns null instead of throwing on a missing file. */
69
- const statFile = (file: string): Stats | null => {
70
- try {
71
- return statSync(file);
72
- } catch {
73
- return null;
74
- }
75
- };
76
-
77
- /**
78
- * A dev-server middleware that serves `content.assets` mounts (top-level dirs
79
- * kept in place, e.g. a migrated `images/`) at their site URL. Astro only serves
80
- * `publicDir` in dev, so without this those references would 404. Non-matching
81
- * requests, and any path that isn't a real file, fall through to Astro. Stat is
82
- * synchronous — this is dev-only middleware and mirrors how sirv serves statics.
83
- */
84
- export const serveAssetMounts =
85
- (mounts: AssetMount[]) =>
86
- (req: IncomingMessage, res: ServerResponse, next: () => void): void => {
87
- if ((req.method !== "GET" && req.method !== "HEAD") || !req.url) {
88
- next();
89
- return;
90
- }
91
- const file = resolveRequest(req.url, mounts);
92
- const stats = file ? statFile(file) : null;
93
- if (!(file && stats?.isFile())) {
94
- next();
95
- return;
96
- }
97
- res.setHeader("Content-Type", mimeType(file));
98
- res.setHeader("Content-Length", String(stats.size));
99
- if (req.method === "HEAD") {
100
- res.end();
101
- return;
102
- }
103
- createReadStream(file).pipe(res);
104
- };
105
-
106
- /**
107
- * Copy every asset mount into the build output, mirroring what Astro does with
108
- * `publicDir`. Runs on `astro:build:done` so in-place asset dirs ship in the
109
- * final `dist/`. A missing source dir is skipped (it may be referenced but
110
- * absent); `cp` handles both directories and single files.
111
- */
112
- export const copyAssetMounts = async (
113
- mounts: AssetMount[],
114
- outDir: string
115
- ): Promise<void> => {
116
- for (const mount of mounts) {
117
- if (!existsSync(mount.dir)) {
118
- continue;
119
- }
120
- const dest = join(outDir, mount.url.replace(/^\/+/u, ""));
121
- // oxlint-disable-next-line no-await-in-loop -- sequential fs copies
122
- await cp(mount.dir, dest, { recursive: true });
123
- }
124
- };
@@ -1,39 +0,0 @@
1
- import { defineCommand } from "citty";
2
-
3
- import { migrators } from "../../migrate/migrate.ts";
4
- import { logger } from "../log.ts";
5
-
6
- const makeMigrateCommand = (source: keyof typeof migrators) =>
7
- defineCommand({
8
- meta: {
9
- description: `Migrate a ${source} project to Blume.`,
10
- name: source,
11
- },
12
- async run() {
13
- const root = process.cwd();
14
- logger.start(`Migrating ${source} project`);
15
- const result = await migrators[source]?.(root);
16
- if (!result) {
17
- logger.error(`No migrator for ${source}.`);
18
- process.exit(1);
19
- }
20
- logger.success(`Migrated ${result.moved} content file(s).`);
21
- for (const warning of result.warnings) {
22
- logger.warn(warning);
23
- }
24
- logger.box("Review blume.config.ts and run `blume dev`.");
25
- },
26
- });
27
-
28
- export const migrateCommand = defineCommand({
29
- meta: {
30
- description: "Migrate from another docs tool to Blume.",
31
- name: "migrate",
32
- },
33
- subCommands: {
34
- fumadocs: makeMigrateCommand("fumadocs"),
35
- mintlify: makeMigrateCommand("mintlify"),
36
- nextra: makeMigrateCommand("nextra"),
37
- starlight: makeMigrateCommand("starlight"),
38
- },
39
- });
@@ -1,75 +0,0 @@
1
- ---
2
- // Shared field row behind the Mintlify-compat <ParamField>/<ResponseField>/
3
- // <RequestField> components: a labeled name + type + badges, with the field's
4
- // description rendered from the slot (which may hold rich MDX, including nested
5
- // <Expandable> or further fields). Styled to match the native OpenAPI reference
6
- // rows (src/components/openapi/), so a migrated site reads consistently.
7
- interface Props {
8
- default?: unknown;
9
- deprecated?: unknown;
10
- location?: string;
11
- name?: string;
12
- required?: unknown;
13
- type?: string;
14
- }
15
-
16
- const {
17
- default: defaultValue,
18
- deprecated,
19
- location,
20
- name,
21
- required,
22
- type,
23
- } = Astro.props;
24
-
25
- // Booleans arrive as MDX shorthand (`required`) or strings (`required="true"`).
26
- const isTrue = (value: unknown): boolean => value === true || value === "true";
27
- const hasDefault =
28
- defaultValue !== undefined && defaultValue !== null && defaultValue !== "";
29
- const hasDescription = Astro.slots.has("default");
30
- ---
31
-
32
- <div class="not-prose my-2 rounded-blume border border-border px-4 py-3">
33
- <div class="flex flex-wrap items-baseline gap-x-2 gap-y-1">
34
- {name && <code class="font-mono text-foreground text-sm">{name}</code>}
35
- {
36
- location && (
37
- <span class="rounded bg-muted px-1.5 py-0.5 font-medium text-[0.625rem] text-muted-foreground uppercase tracking-wide">
38
- {location}
39
- </span>
40
- )
41
- }
42
- {type && <span class="text-muted-foreground text-xs">{type}</span>}
43
- {
44
- isTrue(required) && (
45
- <span class="font-medium text-[0.625rem] text-red-600 uppercase tracking-wide dark:text-red-400">
46
- required
47
- </span>
48
- )
49
- }
50
- {
51
- isTrue(deprecated) && (
52
- <span class="font-medium text-[0.625rem] text-muted-foreground uppercase tracking-wide line-through">
53
- deprecated
54
- </span>
55
- )
56
- }
57
- </div>
58
- {
59
- hasDefault && (
60
- <div class="mt-1 text-muted-foreground text-xs">
61
- Default:{" "}
62
- <code class="rounded bg-muted px-1 py-0.5 text-foreground">
63
- {String(defaultValue)}
64
- </code>
65
- </div>
66
- )
67
- }
68
- {
69
- hasDescription && (
70
- <div class="mt-1.5 text-muted-foreground text-sm [&>:first-child]:mt-0 [&>:last-child]:mb-0">
71
- <slot />
72
- </div>
73
- )
74
- }
75
- </div>
@@ -1,39 +0,0 @@
1
- ---
2
- // Mintlify-compat <ParamField>. Mintlify encodes the parameter's location in the
3
- // attribute name (`<ParamField path="id" />`, `query`, `header`, `body`); the
4
- // attribute's value is the field name. Falls back to a plain `name` prop.
5
- import ApiField from "./ApiField.astro";
6
-
7
- const {
8
- body,
9
- default: defaultValue,
10
- deprecated,
11
- header,
12
- name,
13
- path,
14
- query,
15
- required,
16
- type,
17
- } = Astro.props;
18
-
19
- const locations = [
20
- { key: "path", value: path },
21
- { key: "query", value: query },
22
- { key: "header", value: header },
23
- { key: "body", value: body },
24
- ] as const;
25
- const located = locations.find((entry) => typeof entry.value === "string");
26
- const fieldName = located ? located.value : name;
27
- const location = located?.key;
28
- ---
29
-
30
- <ApiField
31
- default={defaultValue}
32
- deprecated={deprecated}
33
- location={location}
34
- name={fieldName}
35
- required={required}
36
- type={type}
37
- >
38
- <slot />
39
- </ApiField>
@@ -1,23 +0,0 @@
1
- ---
2
- // Mintlify-compat <RequestField>: same shape as <ResponseField> — a named field
3
- // (`name`, `type`, `required`, `default`, `deprecated`) with a slotted body.
4
- import ApiField from "./ApiField.astro";
5
-
6
- const {
7
- default: defaultValue,
8
- deprecated,
9
- name,
10
- required,
11
- type,
12
- } = Astro.props;
13
- ---
14
-
15
- <ApiField
16
- default={defaultValue}
17
- deprecated={deprecated}
18
- name={name}
19
- required={required}
20
- type={type}
21
- >
22
- <slot />
23
- </ApiField>
@@ -1,23 +0,0 @@
1
- ---
2
- // Mintlify-compat <ResponseField>: a named field (`name`, `type`, `required`,
3
- // `default`, `deprecated`) with its description in the slot.
4
- import ApiField from "./ApiField.astro";
5
-
6
- const {
7
- default: defaultValue,
8
- deprecated,
9
- name,
10
- required,
11
- type,
12
- } = Astro.props;
13
- ---
14
-
15
- <ApiField
16
- default={defaultValue}
17
- deprecated={deprecated}
18
- name={name}
19
- required={required}
20
- type={type}
21
- >
22
- <slot />
23
- </ApiField>
@@ -1,9 +0,0 @@
1
- ---
2
- import Callout from "./Callout.astro";
3
-
4
- const { title } = Astro.props;
5
- ---
6
-
7
- <Callout title={title} type="warning">
8
- <slot />
9
- </Callout>
@@ -1,31 +0,0 @@
1
- import { join } from "pathe";
2
-
3
- /** A static directory served at a URL prefix, in addition to `public/`. */
4
- export interface AssetMount {
5
- /** Absolute filesystem path to the source directory (or file). */
6
- dir: string;
7
- /** URL path prefix the source is served at, e.g. `/images`. */
8
- url: string;
9
- }
10
-
11
- /**
12
- * Resolve `content.assets` entries (top-level dirs served at the site root,
13
- * alongside `public/`) to `{ dir, url }` mounts. Shared by the generated Astro
14
- * runtime (dev middleware + build copy) and by link validation, so all three
15
- * agree on where a `/images/foo.png` reference resolves on disk.
16
- *
17
- * Each entry is normalized to a leading-slash URL and joined to the project
18
- * root; leading `./` or `/` and any `..` segments are stripped so a mount can't
19
- * escape the root or collide with the site's own routing prefix.
20
- */
21
- export const resolveAssetMounts = (
22
- root: string,
23
- assets: string[]
24
- ): AssetMount[] =>
25
- assets.map((entry) => {
26
- const rel = entry
27
- .replace(/^[./]+/u, "")
28
- .replaceAll(/\.\.\/?/gu, "")
29
- .replace(/\/+$/u, "");
30
- return { dir: join(root, rel), url: `/${rel}` };
31
- });
@@ -1,102 +0,0 @@
1
- import { existsSync } from "node:fs";
2
- import { readFile } from "node:fs/promises";
3
-
4
- import { join } from "pathe";
5
-
6
- import { assetSegments } from "../migrate/mintlify/assets.ts";
7
- import { loadMintlifyConfig } from "../migrate/mintlify/config.ts";
8
- import { mintlifyI18n } from "../migrate/mintlify/i18n.ts";
9
- import type { BlumeConfig } from "./schema.ts";
10
-
11
- /** A detected docs-tool config that Blume can serve without a migration. */
12
- export interface BridgeDetection {
13
- /** Which foreign docs tool was detected. */
14
- tool: "mintlify";
15
- /** Absolute path of the detected config file (`docs.json`/`mint.json`). */
16
- configFile: string;
17
- /** A Blume config synthesized from the foreign config, ready to validate. */
18
- raw: BlumeConfig;
19
- }
20
-
21
- const MINTLIFY_CONFIG_FILES = ["docs.json", "mint.json"];
22
-
23
- /**
24
- * Detect a Mintlify project at `root` and synthesize an equivalent Blume config.
25
- *
26
- * "Bridge mode" lets a team swap `mintlify dev` for `blume dev` with no file
27
- * changes: `docs.json` is translated to Blume config (`loadMintlifyConfig`) and
28
- * its content block is rewired to a single `mintlify` content source, which
29
- * transforms each MDX page to Blume idiom at scan time. The original
30
- * `content.root`/`exclude` and `variables` move onto the source (Blume has no
31
- * runtime variable substitution, so globals are inlined into content there).
32
- *
33
- * Returns `null` when no Mintlify config is present. Only called when no
34
- * `blume.config.*` exists, so an explicit Blume config always takes precedence.
35
- */
36
- export const detectMintlifyBridge = async (
37
- root: string
38
- ): Promise<BridgeDetection | null> => {
39
- const configFile = MINTLIFY_CONFIG_FILES.map((name) => join(root, name)).find(
40
- (candidate) => existsSync(candidate)
41
- );
42
- if (!configFile) {
43
- return null;
44
- }
45
-
46
- const config = await loadMintlifyConfig(root, configFile);
47
-
48
- // i18n is derived from the raw spec (language dirs), mirroring the migrator.
49
- const spec = JSON.parse(await readFile(configFile, "utf-8")) as Record<
50
- string,
51
- unknown
52
- >;
53
- const i18n = mintlifyI18n(spec);
54
- if (i18n) {
55
- config.i18n = i18n;
56
- // Language switching is handled by Blume i18n, not a nav selector.
57
- if (config.navigation?.selectors) {
58
- config.navigation.selectors = config.navigation.selectors.filter(
59
- (selector) => selector.kind !== "language"
60
- );
61
- }
62
- }
63
-
64
- const variables = (config.variables as Record<string, string>) ?? {};
65
- const root_ = config.content?.root ?? ".";
66
- const exclude = config.content?.exclude ?? [];
67
-
68
- // Mintlify serves assets from the project root; the bridge never moves files,
69
- // so referenced root-level asset folders (e.g. `images/`) are served in place
70
- // via `content.assets` instead. This is the read-only twin of the migrator's
71
- // relocation — same referenced segments, just no `public/` move.
72
- const assets = assetSegments(config).filter(
73
- (segment) => segment !== "public" && existsSync(join(root, segment))
74
- );
75
-
76
- return {
77
- configFile,
78
- raw: {
79
- ...config,
80
- content: {
81
- assets,
82
- // Mirror the excludes onto `content.exclude` too: the generated Astro
83
- // `docs` collection globs `content.root` (here the project root) and
84
- // must skip node_modules/snippets just like the source does.
85
- exclude,
86
- root: root_,
87
- sources: [
88
- {
89
- configFile,
90
- exclude,
91
- root: root_,
92
- type: "mintlify",
93
- variables,
94
- },
95
- ],
96
- },
97
- // Globals are inlined into content by the source; drop the top-level copy.
98
- variables: undefined,
99
- },
100
- tool: "mintlify",
101
- };
102
- };