blume 0.5.3 → 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 +2985 -6883
- package/dist/cli/index.js.map +27 -58
- 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 +2 -0
- package/docs/configuration/index.mdx +13 -17
- package/docs/configuration/theming.mdx +2 -10
- package/docs/content/components.mdx +2 -53
- package/docs/content/i18n.mdx +0 -4
- 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/astro/generate.ts +15 -12
- package/src/astro/integration.ts +0 -21
- package/src/astro/templates.ts +9 -16
- package/src/cli/commands/dev.ts +0 -6
- package/src/cli/commands/validate.ts +0 -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 +2 -2
- package/src/components/content/Column.astro +1 -1
- package/src/components/content/Prompt.astro +1 -1
- package/src/components/content/Step.astro +3 -4
- package/src/components/content/Tab.astro +2 -3
- package/src/components/content/TypeTable.astro +5 -5
- package/src/components/content/Update.astro +1 -1
- 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/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 +5 -5
- package/src/components/layout/nav-utils.ts +9 -4
- package/src/components/openapi/ApiOverview.astro +4 -50
- package/src/components/openapi/ApiTagOperations.astro +42 -0
- package/src/core/builtin-tags.ts +1 -3
- package/src/core/config.ts +5 -28
- package/src/core/data.ts +2 -2
- package/src/core/graph.ts +1 -4
- package/src/core/links.ts +5 -19
- package/src/core/meta.ts +1 -1
- package/src/core/navigation.ts +0 -5
- package/src/core/project-graph.ts +0 -5
- package/src/core/schema.ts +13 -90
- package/src/core/sources/resolve.ts +0 -13
- package/src/core/sources/watch.ts +3 -2
- package/src/core/types.ts +1 -8
- package/src/openapi/parse.ts +197 -14
- package/src/openapi/render-mdx.ts +44 -10
- package/src/openapi/source.ts +19 -2
- package/src/theme/entry.ts +45 -17
- package/src/theme/icons.ts +18 -109
- package/src/theme/palette.ts +4 -37
- package/src/theme/twoslash.ts +6 -1
- package/dist/types/core/bridge.d.ts +0 -24
- package/dist/types/core/package-json.d.ts +0 -12
- 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/dist/types/migrate/shared.d.ts +0 -153
- package/docs/advanced/bridge.mdx +0 -76
- package/docs/advanced/migrate.mdx +0 -124
- package/src/astro/static-assets.ts +0 -124
- 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 -190
- package/src/migrate/fumadocs/config.ts +0 -155
- package/src/migrate/fumadocs/content.ts +0 -376
- package/src/migrate/fumadocs/frontmatter.ts +0 -18
- package/src/migrate/fumadocs/groups.ts +0 -237
- package/src/migrate/fumadocs/index.ts +0 -355
- 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 -954
- 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 -459
- package/src/migrate/mintlify/snippets.ts +0 -315
- 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 -389
- package/src/migrate/nextra/meta.ts +0 -266
- package/src/migrate/shared.ts +0 -801
- package/src/migrate/starlight/config.ts +0 -455
- package/src/migrate/starlight/content.ts +0 -75
- 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/openapi/source.ts
CHANGED
|
@@ -69,6 +69,8 @@ interface LoadedSpec {
|
|
|
69
69
|
slug: string;
|
|
70
70
|
spec: ApiSpecData;
|
|
71
71
|
entries: SourceEntry[];
|
|
72
|
+
/** Non-fatal notes from the load (e.g. an offline cache fallback). */
|
|
73
|
+
diagnostics: Diagnostic[];
|
|
72
74
|
}
|
|
73
75
|
|
|
74
76
|
export const openApiSource = (
|
|
@@ -81,7 +83,11 @@ export const openApiSource = (
|
|
|
81
83
|
reference: ReferenceSource
|
|
82
84
|
): Promise<LoadedSpec | Diagnostic> => {
|
|
83
85
|
try {
|
|
84
|
-
const { document } = await parseSpec(
|
|
86
|
+
const { document, warnings } = await parseSpec(
|
|
87
|
+
reference.spec,
|
|
88
|
+
ctx.projectRoot,
|
|
89
|
+
{ cacheDir: ctx.cacheDir, refresh: ctx.refresh }
|
|
90
|
+
);
|
|
85
91
|
const { operations, tags } = extractOperations(document, reference.route);
|
|
86
92
|
const info = document.info ?? { title: reference.label, version: "" };
|
|
87
93
|
const spec: ApiSpecData = {
|
|
@@ -100,6 +106,11 @@ export const openApiSource = (
|
|
|
100
106
|
version: info.version ?? "",
|
|
101
107
|
};
|
|
102
108
|
return {
|
|
109
|
+
diagnostics: warnings.map((message) => ({
|
|
110
|
+
code: "BLUME_OPENAPI_STALE",
|
|
111
|
+
message,
|
|
112
|
+
severity: "warning" as const,
|
|
113
|
+
})),
|
|
103
114
|
entries: specEntries(spec, operations),
|
|
104
115
|
slug: reference.slug,
|
|
105
116
|
spec,
|
|
@@ -108,7 +119,12 @@ export const openApiSource = (
|
|
|
108
119
|
return {
|
|
109
120
|
code: "BLUME_OPENAPI_UNAVAILABLE",
|
|
110
121
|
message: `Could not load OpenAPI spec "${reference.spec}" for ${reference.route} (${(error as Error).message}); its reference pages were skipped.`,
|
|
111
|
-
|
|
122
|
+
// A configured-but-unloadable spec ships a dead nav tab (a 404 route),
|
|
123
|
+
// so fail loudly in build (blocks under --strict) while staying a warning
|
|
124
|
+
// in dev so offline work still runs.
|
|
125
|
+
severity: ctx.mode === "build" ? "error" : "warning",
|
|
126
|
+
suggestion:
|
|
127
|
+
"Check the spec URL/path is reachable from the build environment; behind a proxy, set HTTP(S)_PROXY.",
|
|
112
128
|
};
|
|
113
129
|
}
|
|
114
130
|
};
|
|
@@ -125,6 +141,7 @@ export const openApiSource = (
|
|
|
125
141
|
}
|
|
126
142
|
data[result.slug] = result.spec;
|
|
127
143
|
entries.push(...result.entries);
|
|
144
|
+
diagnostics.push(...result.diagnostics);
|
|
128
145
|
}
|
|
129
146
|
parsed = data;
|
|
130
147
|
return { diagnostics, entries };
|
package/src/theme/entry.ts
CHANGED
|
@@ -32,9 +32,6 @@ ${options.sources.map((source) => `@source "${source}";`).join("\n")}
|
|
|
32
32
|
|
|
33
33
|
:root {
|
|
34
34
|
--blume-background: oklch(1 0 0);
|
|
35
|
-
--blume-background-decoration: none;
|
|
36
|
-
--blume-background-decoration-repeat: no-repeat;
|
|
37
|
-
--blume-background-decoration-size: auto;
|
|
38
35
|
--blume-background-image: none;
|
|
39
36
|
--blume-background-image-repeat: no-repeat;
|
|
40
37
|
--blume-background-image-size: cover;
|
|
@@ -90,9 +87,6 @@ ${options.sources.map((source) => `@source "${source}";`).join("\n")}
|
|
|
90
87
|
|
|
91
88
|
:root[data-theme="dark"] {
|
|
92
89
|
--blume-background: oklch(0.085 0 0);
|
|
93
|
-
--blume-background-decoration: none;
|
|
94
|
-
--blume-background-decoration-repeat: no-repeat;
|
|
95
|
-
--blume-background-decoration-size: auto;
|
|
96
90
|
--blume-background-image: none;
|
|
97
91
|
--blume-background-image-repeat: no-repeat;
|
|
98
92
|
--blume-background-image-size: cover;
|
|
@@ -134,9 +128,22 @@ ${options.sources.map((source) => `@source "${source}";`).join("\n")}
|
|
|
134
128
|
}
|
|
135
129
|
|
|
136
130
|
@layer base {
|
|
131
|
+
/* Nothing refuses to shrink below its intrinsic content width. This global
|
|
132
|
+
min-width reset defuses the classic flex/grid overflow — a long or
|
|
133
|
+
truncating child forcing its container (and the page) past the viewport
|
|
134
|
+
edge — so components don't need per-element min-w-0 overrides. */
|
|
135
|
+
* {
|
|
136
|
+
min-width: 0;
|
|
137
|
+
}
|
|
138
|
+
/* Interactive controls get a pointer cursor unless disabled. */
|
|
139
|
+
button:not(:disabled),
|
|
140
|
+
[role="button"]:not(:disabled) {
|
|
141
|
+
cursor: pointer;
|
|
142
|
+
}
|
|
137
143
|
html {
|
|
138
144
|
scroll-behavior: smooth;
|
|
139
145
|
scroll-padding-top: 4.5rem;
|
|
146
|
+
text-rendering: optimizeLegibility;
|
|
140
147
|
}
|
|
141
148
|
/* Headings use the display font (defaults to the body font when unset). */
|
|
142
149
|
h1,
|
|
@@ -153,16 +160,11 @@ ${options.sources.map((source) => `@source "${source}";`).join("\n")}
|
|
|
153
160
|
border-radius: 2px;
|
|
154
161
|
}
|
|
155
162
|
body {
|
|
156
|
-
background-attachment: fixed
|
|
157
|
-
background-image:
|
|
158
|
-
|
|
159
|
-
background-
|
|
160
|
-
background-
|
|
161
|
-
var(--blume-background-image-repeat),
|
|
162
|
-
var(--blume-background-decoration-repeat);
|
|
163
|
-
background-size:
|
|
164
|
-
var(--blume-background-image-size),
|
|
165
|
-
var(--blume-background-decoration-size);
|
|
163
|
+
background-attachment: fixed;
|
|
164
|
+
background-image: var(--blume-background-image);
|
|
165
|
+
background-position: center top;
|
|
166
|
+
background-repeat: var(--blume-background-image-repeat);
|
|
167
|
+
background-size: var(--blume-background-image-size);
|
|
166
168
|
}
|
|
167
169
|
@media (prefers-reduced-motion: reduce) {
|
|
168
170
|
html {
|
|
@@ -298,7 +300,7 @@ ${options.sources.map((source) => `@source "${source}";`).join("\n")}
|
|
|
298
300
|
line-height: 1.55;
|
|
299
301
|
margin: 1.5rem 0;
|
|
300
302
|
overflow-x: auto;
|
|
301
|
-
padding: 1rem
|
|
303
|
+
padding: 1rem 0;
|
|
302
304
|
position: relative;
|
|
303
305
|
}
|
|
304
306
|
|
|
@@ -395,6 +397,32 @@ blume-diff {
|
|
|
395
397
|
padding: 0;
|
|
396
398
|
}
|
|
397
399
|
|
|
400
|
+
/* Long lines scroll inside the code element, not the pre: the pre stays static
|
|
401
|
+
so its absolute header bar (::before) and copy button don't drift with the
|
|
402
|
+
scroll. The pre's horizontal padding lives here so content still scrolls
|
|
403
|
+
edge-to-edge past it. Two contexts opt out: twoslash blocks (popups must
|
|
404
|
+
escape any scroll container — see theme/twoslash.ts), and the API request
|
|
405
|
+
panel, which owns its code layout and keeps the copy control in the panel
|
|
406
|
+
header. Every other component that hosts a code block — Tabs, CodeGroup,
|
|
407
|
+
Steps, Callout, Card, Accordion — is real prose content and keeps the inset,
|
|
408
|
+
even though its chrome wrapper is not-prose. */
|
|
409
|
+
.prose :where(pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code) {
|
|
410
|
+
display: block;
|
|
411
|
+
overflow-x: auto;
|
|
412
|
+
padding: 0 1.25rem;
|
|
413
|
+
/* The scroller is only as tall as the code, so an overlay scrollbar would
|
|
414
|
+
draw on top of the last line; hide it (wheel/trackpad/keyboard scrolling
|
|
415
|
+
still works). */
|
|
416
|
+
scrollbar-width: none;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
.prose
|
|
420
|
+
:where(
|
|
421
|
+
pre:not(.twoslash, .twoslash pre, blume-panel-tabs *) > code
|
|
422
|
+
)::-webkit-scrollbar {
|
|
423
|
+
display: none;
|
|
424
|
+
}
|
|
425
|
+
|
|
398
426
|
/* Word wrap (markdown.code.wrap): long lines wrap instead of scrolling. The
|
|
399
427
|
attribute is set on <body> from config; default code keeps \`white-space: pre\`. */
|
|
400
428
|
[data-blume-code-wrap] pre,
|
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,61 +20,22 @@ 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 LIBRARY_SETS: Record<string, string> = {
|
|
40
|
-
fa: "fa6-solid",
|
|
41
|
-
"font-awesome": "fa6-solid",
|
|
42
|
-
fontawesome: "fa6-solid",
|
|
43
|
-
lucide: "lucide",
|
|
44
|
-
tabler: "tabler",
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Mintlify Font Awesome `iconType` → Iconify set. The Pro-only styles
|
|
49
|
-
* (`light`/`thin`/`duotone`/`sharp-solid`) aren't in the free data, so they map
|
|
50
|
-
* to `solid` rather than render nothing.
|
|
51
|
-
*/
|
|
52
|
-
const ICON_TYPE_SETS: Record<string, string> = {
|
|
53
|
-
brands: "fa6-brands",
|
|
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). */
|
|
29
|
+
/** Explicit `prefix:name` prefixes. Lucide is the only bundled set. */
|
|
63
30
|
const PREFIX_SETS: Record<string, string> = {
|
|
64
|
-
|
|
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",
|
|
31
|
+
lucide: "lucide",
|
|
76
32
|
};
|
|
77
33
|
|
|
78
34
|
/**
|
|
79
|
-
* Own-property map lookup. Icon names and
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
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.
|
|
83
39
|
*/
|
|
84
40
|
const ownEntry = <T>(map: Record<string, T>, key: string): T | undefined =>
|
|
85
41
|
Object.hasOwn(map, key) ? map[key] : undefined;
|
|
@@ -89,40 +45,16 @@ export interface ResolvedIcon {
|
|
|
89
45
|
body: string;
|
|
90
46
|
/** The resolved icon name. */
|
|
91
47
|
name: string;
|
|
92
|
-
/** The icon's viewBox
|
|
48
|
+
/** The icon's viewBox (Lucide is 24×24). */
|
|
93
49
|
viewBox: string;
|
|
94
50
|
}
|
|
95
51
|
|
|
96
|
-
export interface ResolveIconOptions {
|
|
97
|
-
/** Font Awesome style selector (`solid`, `regular`, `brands`, …). */
|
|
98
|
-
iconType?: string;
|
|
99
|
-
/** Default library for a bare name (`fontawesome` | `lucide` | `tabler`). */
|
|
100
|
-
library?: string;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
52
|
const normalize = (name: string): string =>
|
|
104
53
|
name
|
|
105
54
|
.trim()
|
|
106
55
|
.toLowerCase()
|
|
107
56
|
.replaceAll(/[\s_]+/gu, "-");
|
|
108
57
|
|
|
109
|
-
/** Which set a bare name resolves against, given library/iconType hints. */
|
|
110
|
-
const setFor = (options: ResolveIconOptions): string => {
|
|
111
|
-
if (options.iconType) {
|
|
112
|
-
const set = ownEntry(ICON_TYPE_SETS, normalize(options.iconType));
|
|
113
|
-
if (set) {
|
|
114
|
-
return set;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
if (options.library) {
|
|
118
|
-
const set = ownEntry(LIBRARY_SETS, normalize(options.library));
|
|
119
|
-
if (set) {
|
|
120
|
-
return set;
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
return DEFAULT_SET;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
58
|
const fromSet = (setName: string, iconName: string): ResolvedIcon | null => {
|
|
127
59
|
const set = ownEntry(SETS, setName);
|
|
128
60
|
const data = set && getIconData(set, iconName);
|
|
@@ -133,46 +65,23 @@ const fromSet = (setName: string, iconName: string): ResolvedIcon | null => {
|
|
|
133
65
|
return { body, name: iconName, viewBox: attributes.viewBox };
|
|
134
66
|
};
|
|
135
67
|
|
|
136
|
-
// Font Awesome splits brands into their own set, so a bare `github` under a
|
|
137
|
-
// solid/regular default still resolves.
|
|
138
|
-
const fromFaSet = (setName: string, name: string): ResolvedIcon | null =>
|
|
139
|
-
fromSet(setName, name) ?? fromSet("fa6-brands", name);
|
|
140
|
-
|
|
141
|
-
const resolveInSet = (setName: string, name: string): ResolvedIcon | null =>
|
|
142
|
-
setName.startsWith("fa6-")
|
|
143
|
-
? fromFaSet(setName, name)
|
|
144
|
-
: fromSet(setName, name);
|
|
145
|
-
|
|
146
68
|
/**
|
|
147
|
-
* Resolve an icon name to inline SVG. Honors an explicit `
|
|
148
|
-
*
|
|
149
|
-
* `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.
|
|
150
71
|
*/
|
|
151
|
-
export const resolveIcon = (
|
|
152
|
-
name: string,
|
|
153
|
-
options: ResolveIconOptions = {}
|
|
154
|
-
): ResolvedIcon | null => {
|
|
72
|
+
export const resolveIcon = (name: string): ResolvedIcon | null => {
|
|
155
73
|
const normalized = normalize(name);
|
|
156
74
|
const colon = normalized.indexOf(":");
|
|
157
75
|
if (colon > 0) {
|
|
158
76
|
const setName = ownEntry(PREFIX_SETS, normalized.slice(0, colon));
|
|
159
|
-
|
|
160
|
-
return resolveInSet(setName, normalized.slice(colon + 1));
|
|
161
|
-
}
|
|
77
|
+
return setName ? fromSet(setName, normalized.slice(colon + 1)) : null;
|
|
162
78
|
}
|
|
163
|
-
return
|
|
79
|
+
return fromSet(DEFAULT_SET, normalized);
|
|
164
80
|
};
|
|
165
81
|
|
|
166
|
-
/**
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
* just because the project's default library is Lucide.
|
|
170
|
-
*/
|
|
171
|
-
export const hasIcon = (
|
|
172
|
-
name: string,
|
|
173
|
-
options: ResolveIconOptions = {}
|
|
174
|
-
): boolean => {
|
|
175
|
-
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)) {
|
|
176
85
|
return true;
|
|
177
86
|
}
|
|
178
87
|
const normalized = normalize(name);
|
package/src/theme/palette.ts
CHANGED
|
@@ -52,36 +52,11 @@ const backgroundImageCss = (image: string): string =>
|
|
|
52
52
|
const cssToken = (name: string, value?: string | null): string[] =>
|
|
53
53
|
value ? [` ${name}: ${value};`] : [];
|
|
54
54
|
|
|
55
|
-
const backgroundDecorationCss = (
|
|
56
|
-
decoration: ResolvedConfig["theme"]["backgroundDecoration"]
|
|
57
|
-
): string => {
|
|
58
|
-
if (decoration === "gradient") {
|
|
59
|
-
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);
|
|
60
|
-
--blume-background-decoration-repeat: no-repeat, no-repeat;
|
|
61
|
-
--blume-background-decoration-size: auto, auto;
|
|
62
|
-
`;
|
|
63
|
-
}
|
|
64
|
-
if (decoration === "grid") {
|
|
65
|
-
return ` --blume-background-decoration: linear-gradient(var(--blume-border) 1px, transparent 1px), linear-gradient(90deg, var(--blume-border) 1px, transparent 1px);
|
|
66
|
-
--blume-background-decoration-repeat: repeat, repeat;
|
|
67
|
-
--blume-background-decoration-size: 2rem 2rem, 2rem 2rem;
|
|
68
|
-
`;
|
|
69
|
-
}
|
|
70
|
-
if (decoration === "windows") {
|
|
71
|
-
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);
|
|
72
|
-
--blume-background-decoration-repeat: repeat, repeat;
|
|
73
|
-
--blume-background-decoration-size: 7rem 4.5rem, 7rem 4.5rem;
|
|
74
|
-
`;
|
|
75
|
-
}
|
|
76
|
-
return "";
|
|
77
|
-
};
|
|
78
|
-
|
|
79
55
|
const themeRootCss = (
|
|
80
56
|
theme: ResolvedConfig["theme"],
|
|
81
57
|
options: {
|
|
82
58
|
accent: string;
|
|
83
59
|
action: string | null;
|
|
84
|
-
backgroundDecoration: string;
|
|
85
60
|
radius: string;
|
|
86
61
|
}
|
|
87
62
|
): string =>
|
|
@@ -97,7 +72,6 @@ const themeRootCss = (
|
|
|
97
72
|
"--blume-background-image",
|
|
98
73
|
theme.backgroundImage ? backgroundImageCss(theme.backgroundImage) : null
|
|
99
74
|
),
|
|
100
|
-
options.backgroundDecoration.trimEnd(),
|
|
101
75
|
` --blume-radius: ${options.radius};`,
|
|
102
76
|
]
|
|
103
77
|
.filter(Boolean)
|
|
@@ -108,13 +82,12 @@ const themeDarkCss = (
|
|
|
108
82
|
options: {
|
|
109
83
|
accent: string;
|
|
110
84
|
action: string | null;
|
|
111
|
-
backgroundDecoration: string;
|
|
112
85
|
}
|
|
113
86
|
): string => {
|
|
114
|
-
// Mode-shared tokens (accent, action
|
|
115
|
-
//
|
|
116
|
-
//
|
|
117
|
-
//
|
|
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.
|
|
118
91
|
const tokens = [
|
|
119
92
|
` --blume-accent: ${options.accent};`,
|
|
120
93
|
" --blume-accent-foreground: oklch(1 0 0);",
|
|
@@ -130,7 +103,6 @@ const themeDarkCss = (
|
|
|
130
103
|
? backgroundImageCss(theme.backgroundImageDark)
|
|
131
104
|
: null
|
|
132
105
|
),
|
|
133
|
-
options.backgroundDecoration.trimEnd(),
|
|
134
106
|
].filter(Boolean);
|
|
135
107
|
return `:root[data-theme="dark"] {
|
|
136
108
|
${tokens.join("\n")}
|
|
@@ -159,20 +131,15 @@ export const buildThemeCss = (theme: ResolvedConfig["theme"]): string => {
|
|
|
159
131
|
const accent = presetOrColor(theme.accent);
|
|
160
132
|
const accentDark = theme.accentDark ? presetOrColor(theme.accentDark) : null;
|
|
161
133
|
const action = theme.action ? presetOrColor(theme.action) : null;
|
|
162
|
-
const backgroundDecoration = backgroundDecorationCss(
|
|
163
|
-
theme.backgroundDecoration
|
|
164
|
-
);
|
|
165
134
|
const radius = RADII[theme.radius];
|
|
166
135
|
const root = themeRootCss(theme, {
|
|
167
136
|
accent,
|
|
168
137
|
action,
|
|
169
|
-
backgroundDecoration,
|
|
170
138
|
radius,
|
|
171
139
|
});
|
|
172
140
|
const dark = themeDarkCss(theme, {
|
|
173
141
|
accent: accentDark ?? accent,
|
|
174
142
|
action,
|
|
175
|
-
backgroundDecoration,
|
|
176
143
|
});
|
|
177
144
|
|
|
178
145
|
return `/* Generated by Blume from theme config. */
|
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,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Derive a valid npm package name from a directory name, falling back to
|
|
3
|
-
* `docs` when nothing usable remains.
|
|
4
|
-
*/
|
|
5
|
-
export declare const toPackageName: (raw: string) => string;
|
|
6
|
-
/**
|
|
7
|
-
* A minimal, runnable `package.json` body for a Blume project: the `blume`
|
|
8
|
-
* dependency pinned to the installed version plus `dev`/`build`/`doctor`
|
|
9
|
-
* scripts, so `npm install && npm run dev` works immediately. Shared by
|
|
10
|
-
* `blume init` and the migrators, which scaffold one when a project has none.
|
|
11
|
-
*/
|
|
12
|
-
export declare const blumePackageJson: (name: string) => string;
|
|
@@ -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;
|