blume 0.5.2 → 0.5.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.
- package/dist/cli/index.js +2235 -5942
- package/dist/cli/index.js.map +36 -65
- package/dist/types/core/config.d.ts +0 -8
- package/dist/types/core/data.d.ts +2 -2
- package/dist/types/core/schema.d.ts +58 -453
- package/dist/types/core/types.d.ts +1 -7
- package/docs/advanced/meta.ts +1 -8
- package/docs/advanced/skills.mdx +28 -0
- package/docs/configuration/ai.mdx +3 -1
- package/docs/configuration/index.mdx +13 -17
- package/docs/configuration/theming.mdx +3 -11
- package/docs/content/components.mdx +2 -53
- package/docs/content/i18n.mdx +1 -5
- package/docs/content/sources.mdx +1 -1
- package/docs/content/syntax.mdx +1 -1
- package/docs/index.mdx +0 -1
- package/docs/reference/cli.mdx +12 -13
- package/package.json +4 -6
- package/skills/blume/SKILL.md +71 -0
- package/skills/blume-update-docs/SKILL.md +52 -0
- package/skills/blume-update-docs/references/audit-checklist.md +46 -0
- package/src/ai/mcp/discovery.ts +3 -1
- package/src/ai/mcp/server.ts +3 -1
- package/src/astro/component-slots.ts +10 -2
- package/src/astro/generate.ts +15 -12
- package/src/astro/integration.ts +0 -21
- package/src/astro/templates.ts +62 -37
- package/src/cli/coalesce.ts +43 -0
- package/src/cli/commands/dev.ts +30 -22
- package/src/cli/commands/validate.ts +0 -2
- package/src/cli/dev-lock.ts +4 -2
- package/src/cli/index.ts +0 -2
- package/src/components/BlumePage.astro +0 -6
- package/src/components/Icon.astro +1 -12
- package/src/components/content/AccordionItem.astro +3 -6
- package/src/components/content/Badge.astro +1 -3
- package/src/components/content/Callout.astro +3 -9
- package/src/components/content/Card.astro +2 -3
- package/src/components/content/ColorItem.astro +8 -5
- package/src/components/content/Column.astro +1 -1
- package/src/components/content/Prompt.astro +8 -4
- package/src/components/content/Step.astro +3 -4
- package/src/components/content/Tab.astro +2 -3
- package/src/components/content/Tabs.astro +13 -2
- package/src/components/content/TypeTable.astro +5 -5
- package/src/components/content/Update.astro +1 -1
- package/src/components/content/mermaid-element.ts +20 -2
- package/src/components/islands/ask-ai.tsx +4 -8
- package/src/components/islands/base-path.ts +30 -0
- package/src/components/islands/hooks.ts +12 -8
- package/src/components/layout/Header.astro +26 -3
- package/src/components/layout/Logo.astro +5 -1
- package/src/components/layout/NavSelector.astro +1 -1
- package/src/components/layout/NavTree.astro +14 -14
- package/src/components/layout/PageActions.astro +17 -11
- package/src/components/layout/PageLayout.astro +42 -0
- package/src/components/layout/ReferenceLayout.astro +1 -0
- package/src/components/layout/RootLayout.astro +31 -2
- package/src/components/layout/Search.astro +9 -6
- package/src/components/layout/nav-utils.ts +9 -4
- package/src/components/layout/search/types.ts +16 -5
- package/src/components/openapi/ApiOverview.astro +4 -50
- package/src/components/openapi/ApiTagOperations.astro +42 -0
- package/src/components/openapi/ParametersTable.astro +1 -1
- package/src/components/openapi/SchemaProperty.astro +1 -1
- package/src/components/openapi/SchemaTable.astro +3 -3
- package/src/components/openapi/helpers.ts +17 -8
- package/src/components/openapi/snippets.ts +17 -4
- package/src/core/builtin-tags.ts +1 -3
- package/src/core/config.ts +19 -33
- package/src/core/data.ts +2 -2
- package/src/core/graph.ts +7 -5
- package/src/core/links.ts +5 -19
- package/src/core/meta.ts +1 -1
- package/src/core/navigation.ts +5 -6
- package/src/core/project-graph.ts +0 -5
- package/src/core/schema.ts +13 -90
- package/src/core/sources/filesystem.ts +19 -1
- package/src/core/sources/mdx-remote.ts +20 -4
- package/src/core/sources/normalize.ts +28 -6
- package/src/core/sources/resolve.ts +0 -13
- package/src/core/sources/watch.ts +45 -0
- package/src/core/types.ts +1 -8
- package/src/markdown/code-title.ts +6 -3
- package/src/markdown/package-install.ts +3 -1
- package/src/og/card.ts +16 -4
- package/src/openapi/parse.ts +197 -14
- package/src/openapi/render-mdx.ts +54 -11
- package/src/openapi/source.ts +19 -2
- package/src/search/sync/orama-cloud.ts +2 -0
- package/src/search/sync/typesense.ts +4 -0
- package/src/theme/entry.ts +45 -17
- package/src/theme/icons.ts +22 -104
- package/src/theme/palette.ts +35 -47
- package/src/theme/twoslash.ts +6 -1
- package/dist/types/core/bridge.d.ts +0 -24
- package/dist/types/migrate/mintlify/assets.d.ts +0 -8
- package/dist/types/migrate/mintlify/config.d.ts +0 -16
- package/dist/types/migrate/mintlify/i18n.d.ts +0 -7
- package/docs/advanced/bridge.mdx +0 -76
- package/docs/advanced/migrate.mdx +0 -123
- package/src/astro/static-assets.ts +0 -117
- package/src/cli/commands/migrate.ts +0 -39
- package/src/components/content/ApiField.astro +0 -75
- package/src/components/content/ParamField.astro +0 -39
- package/src/components/content/RequestField.astro +0 -23
- package/src/components/content/ResponseField.astro +0 -23
- package/src/components/content/Warning.astro +0 -9
- package/src/core/assets.ts +0 -31
- package/src/core/bridge.ts +0 -102
- package/src/core/sources/mintlify.ts +0 -204
- package/src/migrate/fumadocs/config.ts +0 -155
- package/src/migrate/fumadocs/content.ts +0 -378
- package/src/migrate/fumadocs/frontmatter.ts +0 -18
- package/src/migrate/fumadocs/groups.ts +0 -237
- package/src/migrate/fumadocs/index.ts +0 -340
- package/src/migrate/fumadocs/meta.ts +0 -244
- package/src/migrate/migrate.ts +0 -53
- package/src/migrate/mintlify/assets.ts +0 -46
- package/src/migrate/mintlify/config.ts +0 -958
- package/src/migrate/mintlify/content.ts +0 -120
- package/src/migrate/mintlify/frontmatter.ts +0 -126
- package/src/migrate/mintlify/i18n.ts +0 -51
- package/src/migrate/mintlify/icons.ts +0 -128
- package/src/migrate/mintlify/index.ts +0 -372
- package/src/migrate/mintlify/snippets.ts +0 -306
- package/src/migrate/mintlify/transform.ts +0 -82
- package/src/migrate/nextra/content.ts +0 -46
- package/src/migrate/nextra/frontmatter.ts +0 -40
- package/src/migrate/nextra/index.ts +0 -374
- package/src/migrate/nextra/meta.ts +0 -266
- package/src/migrate/shared.ts +0 -728
- package/src/migrate/starlight/config.ts +0 -455
- package/src/migrate/starlight/content.ts +0 -78
- package/src/migrate/starlight/frontmatter.ts +0 -111
- package/src/migrate/starlight/i18n.ts +0 -54
- package/src/migrate/starlight/index.ts +0 -131
package/src/theme/icons.ts
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Icon resolution backed by the open Iconify
|
|
3
|
-
* Lucide, and Tabler — the three libraries Mintlify exposes. Resolution runs at
|
|
2
|
+
* Icon resolution backed by the open Iconify Lucide set. Resolution runs at
|
|
4
3
|
* **build time, server-side**, and returns ready-to-inline SVG, so icons stay
|
|
5
|
-
* zero-JS and fully self-contained (no runtime CDN fetch
|
|
4
|
+
* zero-JS and fully self-contained (no runtime CDN fetch).
|
|
6
5
|
*
|
|
7
6
|
* Because the Iconify set data is large, this module must only be imported from
|
|
8
7
|
* server contexts (`.astro` frontmatter, the CLI). Client scripts use the tiny
|
|
9
8
|
* hand-inlined set in `./chrome-icons.ts` instead.
|
|
10
|
-
*
|
|
11
|
-
* Coverage vs Mintlify: full parity for every Font Awesome *free* name, Lucide,
|
|
12
|
-
* and Tabler. Font Awesome Pro styles (`light`/`thin`/`duotone`/`sharp-solid`)
|
|
13
|
-
* aren't in the open data, so they fall back to `solid`.
|
|
14
9
|
*/
|
|
15
10
|
import { createRequire } from "node:module";
|
|
16
11
|
|
|
@@ -25,97 +20,43 @@ const requireJson = createRequire(import.meta.url);
|
|
|
25
20
|
const loadSet = (pkg: string): IconifyJSON => requireJson(pkg) as IconifyJSON;
|
|
26
21
|
|
|
27
22
|
const SETS: Record<string, IconifyJSON> = {
|
|
28
|
-
"fa6-brands": loadSet("@iconify-json/fa6-brands/icons.json"),
|
|
29
|
-
"fa6-regular": loadSet("@iconify-json/fa6-regular/icons.json"),
|
|
30
|
-
"fa6-solid": loadSet("@iconify-json/fa6-solid/icons.json"),
|
|
31
23
|
lucide: loadSet("@iconify-json/lucide/icons.json"),
|
|
32
|
-
tabler: loadSet("@iconify-json/tabler/icons.json"),
|
|
33
24
|
};
|
|
34
25
|
|
|
35
|
-
/** Blume's
|
|
26
|
+
/** Blume's only icon library. */
|
|
36
27
|
const DEFAULT_SET = "lucide";
|
|
37
28
|
|
|
38
|
-
/** `
|
|
39
|
-
const
|
|
40
|
-
fa: "fa6-solid",
|
|
41
|
-
"font-awesome": "fa6-solid",
|
|
42
|
-
fontawesome: "fa6-solid",
|
|
29
|
+
/** Explicit `prefix:name` prefixes. Lucide is the only bundled set. */
|
|
30
|
+
const PREFIX_SETS: Record<string, string> = {
|
|
43
31
|
lucide: "lucide",
|
|
44
|
-
tabler: "tabler",
|
|
45
32
|
};
|
|
46
33
|
|
|
47
34
|
/**
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
*
|
|
35
|
+
* Own-property map lookup. Icon names come from content and config, so a value
|
|
36
|
+
* like `constructor:x` would otherwise resolve an Object.prototype member (a
|
|
37
|
+
* function) and crash resolution deep in the build with no pointer to the
|
|
38
|
+
* offending page.
|
|
51
39
|
*/
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
duotone: "fa6-solid",
|
|
55
|
-
light: "fa6-solid",
|
|
56
|
-
regular: "fa6-regular",
|
|
57
|
-
"sharp-solid": "fa6-solid",
|
|
58
|
-
solid: "fa6-solid",
|
|
59
|
-
thin: "fa6-solid",
|
|
60
|
-
};
|
|
61
|
-
|
|
62
|
-
/** Explicit `prefix:name` prefixes (Iconify prefixes + common FA aliases). */
|
|
63
|
-
const PREFIX_SETS: Record<string, string> = {
|
|
64
|
-
...LIBRARY_SETS,
|
|
65
|
-
fa: "fa6-solid",
|
|
66
|
-
"fa-brands": "fa6-brands",
|
|
67
|
-
"fa-regular": "fa6-regular",
|
|
68
|
-
"fa-solid": "fa6-solid",
|
|
69
|
-
"fa6-brands": "fa6-brands",
|
|
70
|
-
"fa6-regular": "fa6-regular",
|
|
71
|
-
"fa6-solid": "fa6-solid",
|
|
72
|
-
fab: "fa6-brands",
|
|
73
|
-
far: "fa6-regular",
|
|
74
|
-
fas: "fa6-solid",
|
|
75
|
-
ti: "tabler",
|
|
76
|
-
};
|
|
40
|
+
const ownEntry = <T>(map: Record<string, T>, key: string): T | undefined =>
|
|
41
|
+
Object.hasOwn(map, key) ? map[key] : undefined;
|
|
77
42
|
|
|
78
43
|
export interface ResolvedIcon {
|
|
79
44
|
/** Inner SVG markup (self-styled: carries its own fill/stroke). */
|
|
80
45
|
body: string;
|
|
81
46
|
/** The resolved icon name. */
|
|
82
47
|
name: string;
|
|
83
|
-
/** The icon's viewBox
|
|
48
|
+
/** The icon's viewBox (Lucide is 24×24). */
|
|
84
49
|
viewBox: string;
|
|
85
50
|
}
|
|
86
51
|
|
|
87
|
-
export interface ResolveIconOptions {
|
|
88
|
-
/** Font Awesome style selector (`solid`, `regular`, `brands`, …). */
|
|
89
|
-
iconType?: string;
|
|
90
|
-
/** Default library for a bare name (`fontawesome` | `lucide` | `tabler`). */
|
|
91
|
-
library?: string;
|
|
92
|
-
}
|
|
93
|
-
|
|
94
52
|
const normalize = (name: string): string =>
|
|
95
53
|
name
|
|
96
54
|
.trim()
|
|
97
55
|
.toLowerCase()
|
|
98
56
|
.replaceAll(/[\s_]+/gu, "-");
|
|
99
57
|
|
|
100
|
-
/** Which set a bare name resolves against, given library/iconType hints. */
|
|
101
|
-
const setFor = (options: ResolveIconOptions): string => {
|
|
102
|
-
if (options.iconType) {
|
|
103
|
-
const set = ICON_TYPE_SETS[normalize(options.iconType)];
|
|
104
|
-
if (set) {
|
|
105
|
-
return set;
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
if (options.library) {
|
|
109
|
-
const set = LIBRARY_SETS[normalize(options.library)];
|
|
110
|
-
if (set) {
|
|
111
|
-
return set;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return DEFAULT_SET;
|
|
115
|
-
};
|
|
116
|
-
|
|
117
58
|
const fromSet = (setName: string, iconName: string): ResolvedIcon | null => {
|
|
118
|
-
const set = SETS
|
|
59
|
+
const set = ownEntry(SETS, setName);
|
|
119
60
|
const data = set && getIconData(set, iconName);
|
|
120
61
|
if (!data) {
|
|
121
62
|
return null;
|
|
@@ -124,46 +65,23 @@ const fromSet = (setName: string, iconName: string): ResolvedIcon | null => {
|
|
|
124
65
|
return { body, name: iconName, viewBox: attributes.viewBox };
|
|
125
66
|
};
|
|
126
67
|
|
|
127
|
-
// Font Awesome splits brands into their own set, so a bare `github` under a
|
|
128
|
-
// solid/regular default still resolves.
|
|
129
|
-
const fromFaSet = (setName: string, name: string): ResolvedIcon | null =>
|
|
130
|
-
fromSet(setName, name) ?? fromSet("fa6-brands", name);
|
|
131
|
-
|
|
132
|
-
const resolveInSet = (setName: string, name: string): ResolvedIcon | null =>
|
|
133
|
-
setName.startsWith("fa6-")
|
|
134
|
-
? fromFaSet(setName, name)
|
|
135
|
-
: fromSet(setName, name);
|
|
136
|
-
|
|
137
68
|
/**
|
|
138
|
-
* Resolve an icon name to inline SVG. Honors an explicit `
|
|
139
|
-
*
|
|
140
|
-
* `library`, falling back to Lucide.
|
|
69
|
+
* Resolve an icon name to inline SVG. Honors an explicit `lucide:name` prefix;
|
|
70
|
+
* a bare name resolves against Lucide.
|
|
141
71
|
*/
|
|
142
|
-
export const resolveIcon = (
|
|
143
|
-
name: string,
|
|
144
|
-
options: ResolveIconOptions = {}
|
|
145
|
-
): ResolvedIcon | null => {
|
|
72
|
+
export const resolveIcon = (name: string): ResolvedIcon | null => {
|
|
146
73
|
const normalized = normalize(name);
|
|
147
74
|
const colon = normalized.indexOf(":");
|
|
148
75
|
if (colon > 0) {
|
|
149
|
-
const setName = PREFIX_SETS
|
|
150
|
-
|
|
151
|
-
return resolveInSet(setName, normalized.slice(colon + 1));
|
|
152
|
-
}
|
|
76
|
+
const setName = ownEntry(PREFIX_SETS, normalized.slice(0, colon));
|
|
77
|
+
return setName ? fromSet(setName, normalized.slice(colon + 1)) : null;
|
|
153
78
|
}
|
|
154
|
-
return
|
|
79
|
+
return fromSet(DEFAULT_SET, normalized);
|
|
155
80
|
};
|
|
156
81
|
|
|
157
|
-
/**
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
* just because the project's default library is Lucide.
|
|
161
|
-
*/
|
|
162
|
-
export const hasIcon = (
|
|
163
|
-
name: string,
|
|
164
|
-
options: ResolveIconOptions = {}
|
|
165
|
-
): boolean => {
|
|
166
|
-
if (resolveIcon(name, options)) {
|
|
82
|
+
/** Whether a name resolves to a known Lucide icon. */
|
|
83
|
+
export const hasIcon = (name: string): boolean => {
|
|
84
|
+
if (resolveIcon(name)) {
|
|
167
85
|
return true;
|
|
168
86
|
}
|
|
169
87
|
const normalized = normalize(name);
|
package/src/theme/palette.ts
CHANGED
|
@@ -22,6 +22,17 @@ const CSS_COLOR = /^[\w\s#%.,()/+-]+$/u;
|
|
|
22
22
|
const safeColor = (value: string, fallback: string): string =>
|
|
23
23
|
CSS_COLOR.test(value.trim()) ? value.trim() : fallback;
|
|
24
24
|
|
|
25
|
+
/**
|
|
26
|
+
* Resolve a named preset or fall back to {@link safeColor}. `hasOwn` keeps a
|
|
27
|
+
* value like "constructor" from resolving an Object.prototype member — which
|
|
28
|
+
* would stringify a function into the generated CSS, breaking the rule (the
|
|
29
|
+
* exact breakout safeColor exists to prevent).
|
|
30
|
+
*/
|
|
31
|
+
const presetOrColor = (value: string): string =>
|
|
32
|
+
Object.hasOwn(ACCENTS, value)
|
|
33
|
+
? (ACCENTS[value] as string)
|
|
34
|
+
: safeColor(value, FALLBACK_ACCENT);
|
|
35
|
+
|
|
25
36
|
/** Like {@link safeColor} but drops an unsafe/absent value to `null`. */
|
|
26
37
|
const safeColorOrNull = (value: string | undefined): string | null =>
|
|
27
38
|
value && CSS_COLOR.test(value.trim()) ? value.trim() : null;
|
|
@@ -41,36 +52,11 @@ const backgroundImageCss = (image: string): string =>
|
|
|
41
52
|
const cssToken = (name: string, value?: string | null): string[] =>
|
|
42
53
|
value ? [` ${name}: ${value};`] : [];
|
|
43
54
|
|
|
44
|
-
const backgroundDecorationCss = (
|
|
45
|
-
decoration: ResolvedConfig["theme"]["backgroundDecoration"]
|
|
46
|
-
): string => {
|
|
47
|
-
if (decoration === "gradient") {
|
|
48
|
-
return ` --blume-background-decoration: radial-gradient(circle at top left, color-mix(in oklab, var(--blume-accent) 18%, transparent), transparent 28rem), radial-gradient(circle at top right, color-mix(in oklab, var(--blume-action) 12%, transparent), transparent 24rem);
|
|
49
|
-
--blume-background-decoration-repeat: no-repeat, no-repeat;
|
|
50
|
-
--blume-background-decoration-size: auto, auto;
|
|
51
|
-
`;
|
|
52
|
-
}
|
|
53
|
-
if (decoration === "grid") {
|
|
54
|
-
return ` --blume-background-decoration: linear-gradient(var(--blume-border) 1px, transparent 1px), linear-gradient(90deg, var(--blume-border) 1px, transparent 1px);
|
|
55
|
-
--blume-background-decoration-repeat: repeat, repeat;
|
|
56
|
-
--blume-background-decoration-size: 2rem 2rem, 2rem 2rem;
|
|
57
|
-
`;
|
|
58
|
-
}
|
|
59
|
-
if (decoration === "windows") {
|
|
60
|
-
return ` --blume-background-decoration: linear-gradient(90deg, color-mix(in oklab, var(--blume-border) 70%, transparent) 1px, transparent 1px), linear-gradient(var(--blume-border) 1px, transparent 1px);
|
|
61
|
-
--blume-background-decoration-repeat: repeat, repeat;
|
|
62
|
-
--blume-background-decoration-size: 7rem 4.5rem, 7rem 4.5rem;
|
|
63
|
-
`;
|
|
64
|
-
}
|
|
65
|
-
return "";
|
|
66
|
-
};
|
|
67
|
-
|
|
68
55
|
const themeRootCss = (
|
|
69
56
|
theme: ResolvedConfig["theme"],
|
|
70
57
|
options: {
|
|
71
58
|
accent: string;
|
|
72
59
|
action: string | null;
|
|
73
|
-
backgroundDecoration: string;
|
|
74
60
|
radius: string;
|
|
75
61
|
}
|
|
76
62
|
): string =>
|
|
@@ -86,7 +72,6 @@ const themeRootCss = (
|
|
|
86
72
|
"--blume-background-image",
|
|
87
73
|
theme.backgroundImage ? backgroundImageCss(theme.backgroundImage) : null
|
|
88
74
|
),
|
|
89
|
-
options.backgroundDecoration.trimEnd(),
|
|
90
75
|
` --blume-radius: ${options.radius};`,
|
|
91
76
|
]
|
|
92
77
|
.filter(Boolean)
|
|
@@ -94,10 +79,23 @@ const themeRootCss = (
|
|
|
94
79
|
|
|
95
80
|
const themeDarkCss = (
|
|
96
81
|
theme: ResolvedConfig["theme"],
|
|
97
|
-
|
|
82
|
+
options: {
|
|
83
|
+
accent: string;
|
|
84
|
+
action: string | null;
|
|
85
|
+
}
|
|
98
86
|
): string => {
|
|
87
|
+
// Mode-shared tokens (accent, action) must be re-declared here: the base
|
|
88
|
+
// stylesheet's own `:root[data-theme="dark"]` block outranks the `:root`
|
|
89
|
+
// config tokens on specificity, so without this block dark mode would
|
|
90
|
+
// silently keep its neutral defaults and ignore the config.
|
|
99
91
|
const tokens = [
|
|
100
|
-
|
|
92
|
+
` --blume-accent: ${options.accent};`,
|
|
93
|
+
" --blume-accent-foreground: oklch(1 0 0);",
|
|
94
|
+
...cssToken("--blume-action", options.action),
|
|
95
|
+
...cssToken(
|
|
96
|
+
"--blume-action-foreground",
|
|
97
|
+
options.action ? "oklch(1 0 0)" : null
|
|
98
|
+
),
|
|
101
99
|
...cssToken("--blume-background", safeColorOrNull(theme.backgroundDark)),
|
|
102
100
|
...cssToken(
|
|
103
101
|
"--blume-background-image",
|
|
@@ -105,10 +103,7 @@ const themeDarkCss = (
|
|
|
105
103
|
? backgroundImageCss(theme.backgroundImageDark)
|
|
106
104
|
: null
|
|
107
105
|
),
|
|
108
|
-
];
|
|
109
|
-
if (tokens.length === 0) {
|
|
110
|
-
return "";
|
|
111
|
-
}
|
|
106
|
+
].filter(Boolean);
|
|
112
107
|
return `:root[data-theme="dark"] {
|
|
113
108
|
${tokens.join("\n")}
|
|
114
109
|
}
|
|
@@ -121,7 +116,7 @@ ${tokens.join("\n")}
|
|
|
121
116
|
* arbitrary colors without a config change.
|
|
122
117
|
*/
|
|
123
118
|
export const resolveAccent = (theme: ResolvedConfig["theme"]): string =>
|
|
124
|
-
|
|
119
|
+
presetOrColor(theme.accent);
|
|
125
120
|
|
|
126
121
|
/** Resolve the configured radius preset to a CSS length. */
|
|
127
122
|
export const resolveRadius = (theme: ResolvedConfig["theme"]): string =>
|
|
@@ -133,26 +128,19 @@ export const resolveRadius = (theme: ResolvedConfig["theme"]): string =>
|
|
|
133
128
|
* arbitrary colors without a config change.
|
|
134
129
|
*/
|
|
135
130
|
export const buildThemeCss = (theme: ResolvedConfig["theme"]): string => {
|
|
136
|
-
const accent =
|
|
137
|
-
|
|
138
|
-
const
|
|
139
|
-
? (ACCENTS[theme.accentDark] ??
|
|
140
|
-
safeColor(theme.accentDark, FALLBACK_ACCENT))
|
|
141
|
-
: null;
|
|
142
|
-
const action = theme.action
|
|
143
|
-
? (ACCENTS[theme.action] ?? safeColor(theme.action, FALLBACK_ACCENT))
|
|
144
|
-
: null;
|
|
145
|
-
const backgroundDecoration = backgroundDecorationCss(
|
|
146
|
-
theme.backgroundDecoration
|
|
147
|
-
);
|
|
131
|
+
const accent = presetOrColor(theme.accent);
|
|
132
|
+
const accentDark = theme.accentDark ? presetOrColor(theme.accentDark) : null;
|
|
133
|
+
const action = theme.action ? presetOrColor(theme.action) : null;
|
|
148
134
|
const radius = RADII[theme.radius];
|
|
149
135
|
const root = themeRootCss(theme, {
|
|
150
136
|
accent,
|
|
151
137
|
action,
|
|
152
|
-
backgroundDecoration,
|
|
153
138
|
radius,
|
|
154
139
|
});
|
|
155
|
-
const dark = themeDarkCss(theme,
|
|
140
|
+
const dark = themeDarkCss(theme, {
|
|
141
|
+
accent: accentDark ?? accent,
|
|
142
|
+
action,
|
|
143
|
+
});
|
|
156
144
|
|
|
157
145
|
return `/* Generated by Blume from theme config. */
|
|
158
146
|
:root {
|
package/src/theme/twoslash.ts
CHANGED
|
@@ -33,9 +33,14 @@ const OVERRIDES = `
|
|
|
33
33
|
|
|
34
34
|
/* Popups are absolutely positioned and must escape the pre's scroll container.
|
|
35
35
|
The base prose pre rule (from the typography layer) wins the cascade here
|
|
36
|
-
despite lower specificity, so !important is needed to force visibility.
|
|
36
|
+
despite lower specificity, so !important is needed to force visibility.
|
|
37
|
+
Regular code blocks scroll their inner code element and carry the horizontal
|
|
38
|
+
padding there; twoslash code opts out of that scroller (popups again), so the
|
|
39
|
+
padding is restored on the pre. */
|
|
37
40
|
.prose pre.twoslash {
|
|
38
41
|
overflow: visible !important;
|
|
42
|
+
padding-left: 1.25rem;
|
|
43
|
+
padding-right: 1.25rem;
|
|
39
44
|
}
|
|
40
45
|
|
|
41
46
|
/* The rich renderer renders each popup's type signature as a nested Shiki
|
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import type { BlumeConfig } from "./schema.ts";
|
|
2
|
-
/** A detected docs-tool config that Blume can serve without a migration. */
|
|
3
|
-
export interface BridgeDetection {
|
|
4
|
-
/** Which foreign docs tool was detected. */
|
|
5
|
-
tool: "mintlify";
|
|
6
|
-
/** Absolute path of the detected config file (`docs.json`/`mint.json`). */
|
|
7
|
-
configFile: string;
|
|
8
|
-
/** A Blume config synthesized from the foreign config, ready to validate. */
|
|
9
|
-
raw: BlumeConfig;
|
|
10
|
-
}
|
|
11
|
-
/**
|
|
12
|
-
* Detect a Mintlify project at `root` and synthesize an equivalent Blume config.
|
|
13
|
-
*
|
|
14
|
-
* "Bridge mode" lets a team swap `mintlify dev` for `blume dev` with no file
|
|
15
|
-
* changes: `docs.json` is translated to Blume config (`loadMintlifyConfig`) and
|
|
16
|
-
* its content block is rewired to a single `mintlify` content source, which
|
|
17
|
-
* transforms each MDX page to Blume idiom at scan time. The original
|
|
18
|
-
* `content.root`/`exclude` and `variables` move onto the source (Blume has no
|
|
19
|
-
* runtime variable substitution, so globals are inlined into content there).
|
|
20
|
-
*
|
|
21
|
-
* Returns `null` when no Mintlify config is present. Only called when no
|
|
22
|
-
* `blume.config.*` exists, so an explicit Blume config always takes precedence.
|
|
23
|
-
*/
|
|
24
|
-
export declare const detectMintlifyBridge: (root: string) => Promise<BridgeDetection | null>;
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import type { BlumeConfig } from "../../core/schema.ts";
|
|
2
|
-
/**
|
|
3
|
-
* Top-level path segments referenced as static assets by a Mintlify config
|
|
4
|
-
* (the conventional `/images`, plus logo/favicon/background paths). These are
|
|
5
|
-
* the root-served folders Mintlify exposes at the site root; Blume serves them
|
|
6
|
-
* via `content.assets` (bridge) or relocates them under `public/` (migrator).
|
|
7
|
-
*/
|
|
8
|
-
export declare const assetSegments: (config: BlumeConfig) => string[];
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import type { BlumeConfig } from "../../core/schema.ts";
|
|
2
|
-
type JsonObject = Record<string, unknown>;
|
|
3
|
-
export interface MintlifyRedirectPartition {
|
|
4
|
-
/** Static redirects Blume can honor, translated to Blume's `from`/`to` shape. */
|
|
5
|
-
kept: NonNullable<BlumeConfig["redirects"]>;
|
|
6
|
-
/** Source paths of dynamic redirects dropped because Blume can't model them. */
|
|
7
|
-
dropped: string[];
|
|
8
|
-
}
|
|
9
|
-
/**
|
|
10
|
-
* Split a spec's redirects into the static ones Blume emits and the dynamic
|
|
11
|
-
* (wildcard/param) ones it drops. Keeping a dynamic redirect crashes the Astro
|
|
12
|
-
* build, so the migrator surfaces the dropped sources as a warning instead.
|
|
13
|
-
*/
|
|
14
|
-
export declare const partitionMintlifyRedirects: (spec: JsonObject) => MintlifyRedirectPartition;
|
|
15
|
-
export declare const loadMintlifyConfig: (root: string, file: string) => Promise<BlumeConfig>;
|
|
16
|
-
export {};
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import type { BlumeConfig } from "../../core/schema.ts";
|
|
2
|
-
/**
|
|
3
|
-
* Map a Mintlify `navigation.languages[]` array to a Blume `i18n` config. The
|
|
4
|
-
* entry marked `default: true` becomes `defaultLocale`; translated content
|
|
5
|
-
* already lives in ISO-code directories, which match Blume's `dir` parser.
|
|
6
|
-
*/
|
|
7
|
-
export declare const mintlifyI18n: (spec: Record<string, unknown>) => BlumeConfig["i18n"] | null;
|
package/docs/advanced/bridge.mdx
DELETED
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
title: Bridge
|
|
3
|
-
description: Run blume dev directly on a Mintlify codebase — no config, no migration, no file changes. Blume detects docs.json and serves it in place.
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
Bridge mode lets you point Blume at a **Mintlify** project and run it as-is. There's no config to write and no migration to commit: drop `blume dev` into a directory that has a `docs.json` (or legacy `mint.json`) and Blume detects it, synthesizes an equivalent config in memory, and serves your existing MDX — transformed to Blume idiom on the fly, on disk untouched.
|
|
7
|
-
|
|
8
|
-
```bash
|
|
9
|
-
cd my-mintlify-docs
|
|
10
|
-
npx blume dev
|
|
11
|
-
```
|
|
12
|
-
|
|
13
|
-
```txt
|
|
14
|
-
ℹ Detected docs.json — running in Mintlify bridge mode (no migration).
|
|
15
|
-
Run "blume migrate mintlify" to convert permanently.
|
|
16
|
-
```
|
|
17
|
-
|
|
18
|
-
It's the zero-commitment way to see what your docs look like on Blume — you can keep running `mintlify dev` in one terminal and `blume dev` in another, side by side, and decide later.
|
|
19
|
-
|
|
20
|
-
## When it activates
|
|
21
|
-
|
|
22
|
-
Bridge mode is entirely automatic, gated on two conditions:
|
|
23
|
-
|
|
24
|
-
- **No Blume config.** There's no `blume.config.{ts,js,mjs}` at the project root. An explicit Blume config always wins — the moment one exists, Blume runs a normal project and bridge mode never triggers.
|
|
25
|
-
- **A Mintlify config is present.** A `docs.json` or `mint.json` sits at the root.
|
|
26
|
-
|
|
27
|
-
With both true, Blume treats the Mintlify config as its source of truth for that run.
|
|
28
|
-
|
|
29
|
-
:::note
|
|
30
|
-
Bridge mode applies to `blume build` too, not just `blume dev` — you can produce a static site from an unconverted Mintlify project. Only `blume dev` prints the detection notice.
|
|
31
|
-
:::
|
|
32
|
-
|
|
33
|
-
## What happens under the hood
|
|
34
|
-
|
|
35
|
-
Bridge mode is the **read-only twin** of [`blume migrate mintlify`](/docs/advanced/migrate#mintlify): the same translation, without writing anything back.
|
|
36
|
-
|
|
37
|
-
- **Config is synthesized in memory.** `docs.json` is translated to a Blume config — navigation, theme, and chrome mapped across — exactly as the migrator would, but nothing is written to disk.
|
|
38
|
-
- **Content is served through a Mintlify source.** The config's content block is rewired to a single [`mintlify` content source](/docs/content/sources) rooted at the project. Each MDX page is transformed to Blume markup **at scan time** as it's read, so your files never change.
|
|
39
|
-
- **Variables are inlined at scan time.** `docs.json` `variables` (`{{name}}`) are substituted into content as it's scanned — Blume has no runtime substitution.
|
|
40
|
-
- **Assets are served in place.** Referenced folders like `images/` are served through [`content.assets`](/docs/content/sources) rather than moved to `public/`, so nothing is relocated.
|
|
41
|
-
- **Languages map to i18n.** A multi-language `docs.json` maps to Blume's [`i18n`](/docs/content/i18n), with the language switch handled by Blume's locale routing instead of a nav selector.
|
|
42
|
-
|
|
43
|
-
Because it reads the same `docs.json` and runs the same transforms, what you see in bridge mode is what you'd get from a full migration.
|
|
44
|
-
|
|
45
|
-
## Bridge vs. migrate
|
|
46
|
-
|
|
47
|
-
<CardGroup cols={2}>
|
|
48
|
-
<Card title="Bridge mode" icon="cable">
|
|
49
|
-
**Try Blume with zero changes.** Nothing is written; your Mintlify project
|
|
50
|
-
stays exactly as it is. Ideal for evaluating Blume, running both dev servers
|
|
51
|
-
side by side, or a reversible spike.
|
|
52
|
-
</Card>
|
|
53
|
-
<Card title="Migrate" href="/docs/advanced/migrate" icon="arrow-right">
|
|
54
|
-
**Convert for good.** [`blume migrate mintlify`](/docs/advanced/migrate)
|
|
55
|
-
rewrites your pages, config, and assets in place so Blume becomes the source
|
|
56
|
-
of truth. Do this once you've decided to switch.
|
|
57
|
-
</Card>
|
|
58
|
-
</CardGroup>
|
|
59
|
-
|
|
60
|
-
Everything you can do in bridge mode, you can do permanently by migrating — bridge is the preview, migrate is the commit.
|
|
61
|
-
|
|
62
|
-
## Limitations
|
|
63
|
-
|
|
64
|
-
- **Mintlify only.** Bridge detection is Mintlify-specific. The other frameworks — Fumadocs, Nextra, Starlight — need a [one-shot migration](/docs/advanced/migrate).
|
|
65
|
-
- **Same idiom gaps as the migrator.** Components without a Blume equivalent are transformed on a best-effort basis; when you hit one, [migrate](/docs/advanced/migrate) and address the warnings, or switch that page to a Blume-native equivalent such as the [OpenAPI reference](/docs/advanced/api-reference).
|
|
66
|
-
- **No runtime variables.** As with a migration, `{{variable}}` values are inlined at scan time rather than substituted at runtime.
|
|
67
|
-
|
|
68
|
-
<CardGroup cols={2}>
|
|
69
|
-
<Card title="Migrate" href="/docs/advanced/migrate" icon="arrow-right">
|
|
70
|
-
Convert a Mintlify, Fumadocs, Nextra, or Starlight project permanently.
|
|
71
|
-
</Card>
|
|
72
|
-
<Card title="Content sources" href="/docs/content/sources" icon="folder-tree">
|
|
73
|
-
How the `mintlify` source and `content.assets` fit into Blume's content
|
|
74
|
-
graph.
|
|
75
|
-
</Card>
|
|
76
|
-
</CardGroup>
|
|
@@ -1,123 +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
|
-
- Multi-language projects map to [`i18n.locales`](/docs/content/i18n); the language nav selector is dropped in favor of Blume's locale switching.
|
|
63
|
-
- 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.
|
|
64
|
-
- 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.
|
|
65
|
-
- 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.
|
|
66
|
-
|
|
67
|
-
:::tip
|
|
68
|
-
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.
|
|
69
|
-
:::
|
|
70
|
-
|
|
71
|
-
### Fumadocs
|
|
72
|
-
|
|
73
|
-
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`.
|
|
74
|
-
|
|
75
|
-
### Nextra
|
|
76
|
-
|
|
77
|
-
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.
|
|
78
|
-
|
|
79
|
-
### Starlight
|
|
80
|
-
|
|
81
|
-
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.
|
|
82
|
-
|
|
83
|
-
## After migrating
|
|
84
|
-
|
|
85
|
-
<Steps>
|
|
86
|
-
<Step title="Review the config">
|
|
87
|
-
Open `blume.config.ts` and check the mapped navigation, theme, and site
|
|
88
|
-
settings. The migrator is thorough but conservative — some source options
|
|
89
|
-
have no Blume equivalent and are left out.
|
|
90
|
-
</Step>
|
|
91
|
-
<Step title="Read the warnings">
|
|
92
|
-
Each warning points at something the codemod couldn't fully translate. Work
|
|
93
|
-
through them before shipping.
|
|
94
|
-
</Step>
|
|
95
|
-
<Step title="Run the dev server">
|
|
96
|
-
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.
|
|
97
|
-
|
|
98
|
-
```bash
|
|
99
|
-
blume dev
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
</Step>
|
|
103
|
-
<Step title="Delete leftovers">
|
|
104
|
-
Old framework files (lockfile entries, `astro.config`, `theme.config`,
|
|
105
|
-
framework dependencies) are left untouched so nothing is lost. Remove them
|
|
106
|
-
by hand once you've confirmed the site builds.
|
|
107
|
-
</Step>
|
|
108
|
-
</Steps>
|
|
109
|
-
|
|
110
|
-
<CardGroup cols={2}>
|
|
111
|
-
<Card title="Bridge mode" href="/docs/advanced/bridge" icon="cable">
|
|
112
|
-
Serve a Mintlify project with `blume dev` — no migration, no file changes.
|
|
113
|
-
</Card>
|
|
114
|
-
<Card title="CLI reference" href="/docs/reference/cli" icon="terminal">
|
|
115
|
-
Every command and flag, including `blume migrate`.
|
|
116
|
-
</Card>
|
|
117
|
-
<Card title="Content sources" href="/docs/content/sources" icon="folder-tree">
|
|
118
|
-
How Blume scans content, assets, and remote sources into a site.
|
|
119
|
-
</Card>
|
|
120
|
-
<Card title="Frontmatter" href="/docs/reference/frontmatter" icon="file-text">
|
|
121
|
-
The page schema your migrated frontmatter is mapped onto.
|
|
122
|
-
</Card>
|
|
123
|
-
</CardGroup>
|