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/astro/templates.ts
CHANGED
|
@@ -4,7 +4,6 @@ import { dirname, isAbsolute, join, relative } from "pathe";
|
|
|
4
4
|
|
|
5
5
|
import { askBackendRuntimeDep } from "../ai/ask.ts";
|
|
6
6
|
import type { AskBackend } from "../ai/ask.ts";
|
|
7
|
-
import { resolveAssetMounts } from "../core/assets.ts";
|
|
8
7
|
import type { ResolvedConfig } from "../core/schema.ts";
|
|
9
8
|
import type { ProjectContext } from "../core/types.ts";
|
|
10
9
|
import { hasScalarReferences } from "../openapi/references.ts";
|
|
@@ -313,12 +312,10 @@ export const astroConfigTemplate = (options: {
|
|
|
313
312
|
if (needsSvelte) {
|
|
314
313
|
integrations.push("svelte()");
|
|
315
314
|
}
|
|
316
|
-
// Always mounted: injects user pages (a no-op when there are none)
|
|
317
|
-
//
|
|
318
|
-
// negotiation over the content routes.
|
|
319
|
-
const assets = resolveAssetMounts(context.root, config.content.assets);
|
|
315
|
+
// Always mounted: injects user pages (a no-op when there are none) and wires
|
|
316
|
+
// up dev-server `Accept: text/markdown` negotiation over the content routes.
|
|
320
317
|
integrations.push(
|
|
321
|
-
`blumeIntegration(${JSON.stringify({
|
|
318
|
+
`blumeIntegration(${JSON.stringify({ base: deployment.base, contentRoutes, pages })})`
|
|
322
319
|
);
|
|
323
320
|
|
|
324
321
|
return `// Generated by Blume. Do not edit; this file is recreated on each run.
|
|
@@ -403,8 +400,8 @@ export const contentConfigTemplate = (options: {
|
|
|
403
400
|
stagedBase?: string;
|
|
404
401
|
/**
|
|
405
402
|
* Whether any filesystem (non-staged) source feeds the `docs` collection.
|
|
406
|
-
* When false (e.g.
|
|
407
|
-
* collection globs nothing — see below.
|
|
403
|
+
* When false (e.g. an all-staged project where every page is materialized by
|
|
404
|
+
* a non-filesystem source), the collection globs nothing — see below.
|
|
408
405
|
*/
|
|
409
406
|
filesystem?: boolean;
|
|
410
407
|
}): string => {
|
|
@@ -423,8 +420,8 @@ export const contentConfigTemplate = (options: {
|
|
|
423
420
|
|
|
424
421
|
// With no filesystem source, no route renders through `docs`, so glob nothing.
|
|
425
422
|
// Beyond skipping wasted work, this is the only thing that keeps Astro's
|
|
426
|
-
// content-layer *watcher* out of `.blume/`:
|
|
427
|
-
// at the project dir (which contains `.blume/.astro/fonts`, rewritten on every
|
|
423
|
+
// content-layer *watcher* out of `.blume/`: an all-staged project roots the
|
|
424
|
+
// collection at the project dir (which contains `.blume/.astro/fonts`, rewritten on every
|
|
428
425
|
// request), and the watcher's match test is `picomatch.isMatch(path, pattern)`
|
|
429
426
|
// — with array-OR semantics, any `!ignored/**` negation *matches* unrelated
|
|
430
427
|
// files, so negative patterns can't exclude a subtree there. An empty pattern
|
|
@@ -980,10 +977,7 @@ import FileTree from "blume/components/content/FileTree.astro";
|
|
|
980
977
|
import Frame from "blume/components/content/Frame.astro";
|
|
981
978
|
import GithubInfo from "blume/components/content/GithubInfo.astro";
|
|
982
979
|
import Panel from "blume/components/content/Panel.astro";
|
|
983
|
-
import ParamField from "blume/components/content/ParamField.astro";
|
|
984
980
|
import Prompt from "blume/components/content/Prompt.astro";
|
|
985
|
-
import RequestField from "blume/components/content/RequestField.astro";
|
|
986
|
-
import ResponseField from "blume/components/content/ResponseField.astro";
|
|
987
981
|
import Step from "blume/components/content/Step.astro";
|
|
988
982
|
import Steps from "blume/components/content/Steps.astro";
|
|
989
983
|
import Tab from "blume/components/content/Tab.astro";
|
|
@@ -998,6 +992,7 @@ import Visibility from "blume/components/content/Visibility.astro";
|
|
|
998
992
|
import YouTube from "blume/components/content/YouTube.astro";
|
|
999
993
|
import Icon from "blume/components/Icon.astro";
|
|
1000
994
|
import ApiOverview from "blume/components/openapi/ApiOverview.astro";
|
|
995
|
+
import ApiTagOperations from "blume/components/openapi/ApiTagOperations.astro";
|
|
1001
996
|
import Operation from "blume/components/openapi/Operation.astro";
|
|
1002
997
|
${mathImport}import { mdxComponents as userMdx, layoutOverrides } from "../generated/components.ts";
|
|
1003
998
|
import { islandComponents } from "../generated/islands.ts";
|
|
@@ -1016,6 +1011,7 @@ const components = {
|
|
|
1016
1011
|
Accordion,
|
|
1017
1012
|
AccordionItem,
|
|
1018
1013
|
ApiOverview,
|
|
1014
|
+
ApiTagOperations,
|
|
1019
1015
|
AutoTypeTable,
|
|
1020
1016
|
Badge,
|
|
1021
1017
|
Callout,
|
|
@@ -1035,10 +1031,7 @@ const components = {
|
|
|
1035
1031
|
Icon,
|
|
1036
1032
|
Operation,
|
|
1037
1033
|
Panel,
|
|
1038
|
-
ParamField,
|
|
1039
1034
|
Prompt,
|
|
1040
|
-
RequestField,
|
|
1041
|
-
ResponseField,
|
|
1042
1035
|
Step,
|
|
1043
1036
|
Steps,
|
|
1044
1037
|
Tab,
|
package/src/cli/commands/dev.ts
CHANGED
|
@@ -56,12 +56,6 @@ export const devCommand = defineCommand({
|
|
|
56
56
|
strict: args.strict,
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
if (project.bridge) {
|
|
60
|
-
logger.info(
|
|
61
|
-
'Detected docs.json — running in Mintlify bridge mode (no migration). Run "blume migrate mintlify" to convert permanently.'
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
|
|
65
59
|
// Claim the shared `.blume` dir so a concurrent build/eject/sync refuses
|
|
66
60
|
// rather than regenerating or deleting it out from under this server. A
|
|
67
61
|
// second dev server would fight over the same generated tree the same
|
|
@@ -3,7 +3,6 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { join } from "pathe";
|
|
5
5
|
|
|
6
|
-
import { resolveAssetMounts } from "../../core/assets.ts";
|
|
7
6
|
import { BlumeError } from "../../core/diagnostics.ts";
|
|
8
7
|
import { validateLinks } from "../../core/links.ts";
|
|
9
8
|
import { scanProject } from "../../core/project-graph.ts";
|
|
@@ -48,7 +47,6 @@ export const validateCommand = defineCommand({
|
|
|
48
47
|
const publicDir = join(root, "public");
|
|
49
48
|
diagnostics.push(
|
|
50
49
|
...(await validateLinks(project.graph, {
|
|
51
|
-
assetMounts: resolveAssetMounts(root, project.config.content.assets),
|
|
52
50
|
checkExternal: Boolean(args.external),
|
|
53
51
|
publicDir: existsSync(publicDir) ? publicDir : null,
|
|
54
52
|
redirects: project.config.redirects,
|
package/src/cli/index.ts
CHANGED
|
@@ -8,7 +8,6 @@ import { devCommand } from "./commands/dev.ts";
|
|
|
8
8
|
import { doctorCommand } from "./commands/doctor.ts";
|
|
9
9
|
import { ejectCommand } from "./commands/eject.ts";
|
|
10
10
|
import { initCommand } from "./commands/init.ts";
|
|
11
|
-
import { migrateCommand } from "./commands/migrate.ts";
|
|
12
11
|
import { previewCommand } from "./commands/preview.ts";
|
|
13
12
|
import { syncCommand } from "./commands/sync.ts";
|
|
14
13
|
import { validateCommand } from "./commands/validate.ts";
|
|
@@ -29,7 +28,6 @@ const main = defineCommand({
|
|
|
29
28
|
doctor: doctorCommand,
|
|
30
29
|
eject: ejectCommand,
|
|
31
30
|
init: initCommand,
|
|
32
|
-
migrate: migrateCommand,
|
|
33
31
|
preview: previewCommand,
|
|
34
32
|
sync: syncCommand,
|
|
35
33
|
validate: validateCommand,
|
|
@@ -38,10 +38,7 @@ import FileTree from "./content/FileTree.astro";
|
|
|
38
38
|
import Frame from "./content/Frame.astro";
|
|
39
39
|
import GithubInfo from "./content/GithubInfo.astro";
|
|
40
40
|
import Panel from "./content/Panel.astro";
|
|
41
|
-
import ParamField from "./content/ParamField.astro";
|
|
42
41
|
import Prompt from "./content/Prompt.astro";
|
|
43
|
-
import RequestField from "./content/RequestField.astro";
|
|
44
|
-
import ResponseField from "./content/ResponseField.astro";
|
|
45
42
|
import Step from "./content/Step.astro";
|
|
46
43
|
import Steps from "./content/Steps.astro";
|
|
47
44
|
import Tab from "./content/Tab.astro";
|
|
@@ -90,10 +87,7 @@ const components = {
|
|
|
90
87
|
GithubInfo,
|
|
91
88
|
Icon,
|
|
92
89
|
Panel,
|
|
93
|
-
ParamField,
|
|
94
90
|
Prompt,
|
|
95
|
-
RequestField,
|
|
96
|
-
ResponseField,
|
|
97
91
|
Step,
|
|
98
92
|
Steps,
|
|
99
93
|
Tab,
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
import data from "blume:data";
|
|
3
2
|
import { resolveIcon } from "../theme/icons.ts";
|
|
4
3
|
|
|
5
4
|
interface Props {
|
|
6
5
|
name?: unknown;
|
|
7
6
|
icon?: unknown;
|
|
8
|
-
iconType?: string;
|
|
9
|
-
/** Icon library for a bare name; defaults to the project's `icons.library`. */
|
|
10
|
-
library?: string;
|
|
11
7
|
size?: number;
|
|
12
8
|
class?: string;
|
|
13
9
|
className?: string;
|
|
@@ -18,8 +14,6 @@ interface Props {
|
|
|
18
14
|
const {
|
|
19
15
|
color,
|
|
20
16
|
icon,
|
|
21
|
-
iconType,
|
|
22
|
-
library,
|
|
23
17
|
name,
|
|
24
18
|
size = 16,
|
|
25
19
|
class: astroClass,
|
|
@@ -41,12 +35,7 @@ const isImageIcon = (value: string): boolean =>
|
|
|
41
35
|
/\.(?:avif|gif|jpe?g|png|svg|webp)$/iu.test(value);
|
|
42
36
|
const imageSrc = iconName && !rawSvg && isImageIcon(iconName) ? iconName : null;
|
|
43
37
|
const resolvedIcon =
|
|
44
|
-
iconName && !(imageSrc || rawSvg)
|
|
45
|
-
? resolveIcon(iconName, {
|
|
46
|
-
iconType,
|
|
47
|
-
library: library ?? data.config.icons.library,
|
|
48
|
-
})
|
|
49
|
-
: null;
|
|
38
|
+
iconName && !(imageSrc || rawSvg) ? resolveIcon(iconName) : null;
|
|
50
39
|
const resolvedClass = astroClass ?? className;
|
|
51
40
|
const customStyle = `display:inline-flex;width:${size}px;height:${size}px;${
|
|
52
41
|
color ? `color:${color}` : ""
|
|
@@ -7,11 +7,9 @@ interface Props {
|
|
|
7
7
|
defaultOpen?: boolean;
|
|
8
8
|
id?: string;
|
|
9
9
|
icon?: string;
|
|
10
|
-
iconType?: string;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
const { defaultOpen = false, description, icon,
|
|
14
|
-
Astro.props;
|
|
12
|
+
const { defaultOpen = false, description, icon, id, title } = Astro.props;
|
|
15
13
|
const slugify = (value: string): string =>
|
|
16
14
|
value
|
|
17
15
|
.toLowerCase()
|
|
@@ -32,18 +30,17 @@ const accordionId = id ?? slugify(title);
|
|
|
32
30
|
<summary
|
|
33
31
|
class="flex cursor-pointer list-none items-center justify-between p-4 font-medium [&::-webkit-details-marker]:hidden"
|
|
34
32
|
>
|
|
35
|
-
<span class="flex
|
|
33
|
+
<span class="flex items-start gap-2">
|
|
36
34
|
{
|
|
37
35
|
icon && (
|
|
38
36
|
<Icon
|
|
39
37
|
class="mt-0.5 shrink-0 text-muted-foreground"
|
|
40
|
-
iconType={iconType}
|
|
41
38
|
name={icon}
|
|
42
39
|
size={16}
|
|
43
40
|
/>
|
|
44
41
|
)
|
|
45
42
|
}
|
|
46
|
-
<span
|
|
43
|
+
<span>
|
|
47
44
|
<span class="block">{title}</span>
|
|
48
45
|
{
|
|
49
46
|
description && (
|
|
@@ -10,7 +10,6 @@ interface Props {
|
|
|
10
10
|
color?: string;
|
|
11
11
|
disabled?: boolean | string;
|
|
12
12
|
icon?: unknown;
|
|
13
|
-
iconType?: string;
|
|
14
13
|
shape?: BadgeShape;
|
|
15
14
|
size?: BadgeSize;
|
|
16
15
|
stroke?: boolean | string;
|
|
@@ -23,7 +22,6 @@ const {
|
|
|
23
22
|
color,
|
|
24
23
|
disabled = false,
|
|
25
24
|
icon,
|
|
26
|
-
iconType,
|
|
27
25
|
shape = "rounded",
|
|
28
26
|
size = "md",
|
|
29
27
|
stroke = false,
|
|
@@ -119,6 +117,6 @@ const customStyle = customColor
|
|
|
119
117
|
style={customStyle}
|
|
120
118
|
title={tooltip}
|
|
121
119
|
>
|
|
122
|
-
{icon && <Icon icon={icon}
|
|
120
|
+
{icon && <Icon icon={icon} size={iconSize[size]} />}
|
|
123
121
|
<slot />
|
|
124
122
|
</span>
|
|
@@ -14,11 +14,10 @@ interface Props {
|
|
|
14
14
|
type?: CalloutType;
|
|
15
15
|
title?: string;
|
|
16
16
|
icon?: unknown;
|
|
17
|
-
iconType?: string;
|
|
18
17
|
color?: string;
|
|
19
18
|
}
|
|
20
19
|
|
|
21
|
-
const { color, icon,
|
|
20
|
+
const { color, icon, type = "info", title } = Astro.props;
|
|
22
21
|
|
|
23
22
|
const iconByType: Record<CalloutType, string> = {
|
|
24
23
|
check: "circle-check",
|
|
@@ -59,14 +58,9 @@ const iconClass: Record<CalloutType, string> = {
|
|
|
59
58
|
style={color ? `border:1px solid ${color};color:${color}` : undefined}
|
|
60
59
|
>
|
|
61
60
|
<span class:list={["mt-0.5 shrink-0", color ? "" : iconClass[type]]}>
|
|
62
|
-
<Icon
|
|
63
|
-
color={color}
|
|
64
|
-
icon={icon ?? iconByType[type]}
|
|
65
|
-
iconType={iconType}
|
|
66
|
-
size={16}
|
|
67
|
-
/>
|
|
61
|
+
<Icon color={color} icon={icon ?? iconByType[type]} size={16} />
|
|
68
62
|
</span>
|
|
69
|
-
<div class="
|
|
63
|
+
<div class="flex-1 [&>:first-child]:mt-0! [&>:last-child]:mb-0!">
|
|
70
64
|
{title && <p class="mb-1 font-semibold text-foreground">{title}</p>}
|
|
71
65
|
<slot />
|
|
72
66
|
</div>
|
|
@@ -5,7 +5,6 @@ interface Props {
|
|
|
5
5
|
title?: string;
|
|
6
6
|
href?: string;
|
|
7
7
|
icon?: string;
|
|
8
|
-
iconType?: string;
|
|
9
8
|
img?: string;
|
|
10
9
|
horizontal?: boolean | string;
|
|
11
10
|
cta?: string;
|
|
@@ -14,7 +13,7 @@ interface Props {
|
|
|
14
13
|
color?: string;
|
|
15
14
|
}
|
|
16
15
|
|
|
17
|
-
const { arrow, color, cta, horizontal, href, icon,
|
|
16
|
+
const { arrow, color, cta, horizontal, href, icon, img, title, type } =
|
|
18
17
|
Astro.props;
|
|
19
18
|
const Tag = href ? "a" : "div";
|
|
20
19
|
const external = href?.startsWith("http");
|
|
@@ -75,7 +74,7 @@ const variantClass = {
|
|
|
75
74
|
{
|
|
76
75
|
iconName && (
|
|
77
76
|
<div class:list={["mb-2.5", color ? "" : type ? iconClass[type] : "text-accent"]}>
|
|
78
|
-
<Icon color={color}
|
|
77
|
+
<Icon color={color} name={iconName} size={20} />
|
|
79
78
|
</div>
|
|
80
79
|
)
|
|
81
80
|
}
|
|
@@ -21,7 +21,7 @@ const displayValue =
|
|
|
21
21
|
|
|
22
22
|
<button
|
|
23
23
|
aria-label={`Copy ${name} color ${displayValue}`}
|
|
24
|
-
class="group flex w-full
|
|
24
|
+
class="group flex w-full cursor-pointer flex-col gap-2.5 text-center focus:outline-none"
|
|
25
25
|
data-blume-color-copy={displayValue}
|
|
26
26
|
style={`--blume-color-light:${lightValue};--blume-color-dark:${darkValue};`}
|
|
27
27
|
type="button"
|
|
@@ -32,7 +32,7 @@ const displayValue =
|
|
|
32
32
|
style="background:var(--blume-color-light)"
|
|
33
33
|
>
|
|
34
34
|
</span>
|
|
35
|
-
<span class="block
|
|
35
|
+
<span class="block leading-tight">
|
|
36
36
|
<span class="block truncate font-semibold text-foreground text-sm">
|
|
37
37
|
{name}
|
|
38
38
|
</span>
|
|
@@ -45,7 +45,7 @@ const secondaryButton =
|
|
|
45
45
|
>
|
|
46
46
|
<div class="flex items-center justify-between gap-3 px-4 py-3">
|
|
47
47
|
<p
|
|
48
|
-
class="m-0!
|
|
48
|
+
class="m-0! text-foreground text-sm [&_a]:font-medium [&_a]:text-foreground [&_a]:underline [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_strong]:font-semibold [&_strong]:text-foreground"
|
|
49
49
|
data-blume-prompt-description
|
|
50
50
|
set:html={descriptionHtml}
|
|
51
51
|
/>
|
|
@@ -4,11 +4,10 @@ import { hasIcon } from "../../theme/icons.ts";
|
|
|
4
4
|
|
|
5
5
|
interface Props {
|
|
6
6
|
icon?: string;
|
|
7
|
-
iconType?: string;
|
|
8
7
|
title?: string;
|
|
9
8
|
}
|
|
10
9
|
|
|
11
|
-
const { icon,
|
|
10
|
+
const { icon, title } = Astro.props;
|
|
12
11
|
const markerClass =
|
|
13
12
|
"absolute top-0 -start-[2.75rem] flex size-6 items-center justify-center rounded-full bg-accent text-center font-semibold text-accent-foreground text-xs";
|
|
14
13
|
const isImageIcon = (value: string): boolean =>
|
|
@@ -18,14 +17,14 @@ const canRenderIcon =
|
|
|
18
17
|
icon !== undefined &&
|
|
19
18
|
(/^\s*<svg[\s\S]*<\/svg>\s*$/u.test(icon) ||
|
|
20
19
|
isImageIcon(icon) ||
|
|
21
|
-
hasIcon(icon
|
|
20
|
+
hasIcon(icon));
|
|
22
21
|
---
|
|
23
22
|
|
|
24
23
|
<div
|
|
25
24
|
class="relative [counter-increment:blume-step]"
|
|
26
25
|
>
|
|
27
26
|
<span class:list={[markerClass, !canRenderIcon && "before:[content:counter(blume-step)]"]}>
|
|
28
|
-
{canRenderIcon && <Icon
|
|
27
|
+
{canRenderIcon && <Icon name={icon} size={14} />}
|
|
29
28
|
</span>
|
|
30
29
|
{title && <p class="mb-1.5 font-semibold">{title}</p>}
|
|
31
30
|
<div class="[&>:first-child]:mt-0! [&>:last-child]:mb-0!">
|
|
@@ -4,13 +4,12 @@ import Icon from "../Icon.astro";
|
|
|
4
4
|
interface Props {
|
|
5
5
|
class?: string;
|
|
6
6
|
icon?: string;
|
|
7
|
-
iconType?: string;
|
|
8
7
|
id?: string;
|
|
9
8
|
style?: string;
|
|
10
9
|
title: string;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
const { class: className, icon,
|
|
12
|
+
const { class: className, icon, id, style, title } = Astro.props;
|
|
14
13
|
const slugify = (value: string): string =>
|
|
15
14
|
value
|
|
16
15
|
.toLowerCase()
|
|
@@ -39,7 +38,7 @@ const tabId = id ?? slugify(title);
|
|
|
39
38
|
{
|
|
40
39
|
icon && (
|
|
41
40
|
<template data-blume-tab-icon>
|
|
42
|
-
<Icon class="shrink-0"
|
|
41
|
+
<Icon class="shrink-0" name={icon} size={14} />
|
|
43
42
|
</template>
|
|
44
43
|
)
|
|
45
44
|
}
|
|
@@ -37,19 +37,19 @@ const columns = "grid grid-cols-[1fr_2fr_auto] items-center gap-4";
|
|
|
37
37
|
{entries.map(([name, info]) => (
|
|
38
38
|
<details class="group/tt border-border border-t [[open]+&]:border-t-transparent open:rounded-blume open:border-transparent open:bg-background open:shadow-sm open:ring-1 open:ring-border">
|
|
39
39
|
<summary class={`${columns} cursor-pointer list-none px-3 py-2.5 [&::-webkit-details-marker]:hidden`}>
|
|
40
|
-
<code class="
|
|
40
|
+
<code class="break-words font-mono text-accent text-xs">
|
|
41
41
|
{name}
|
|
42
42
|
{info.required ? "" : "?"}
|
|
43
43
|
</code>
|
|
44
44
|
{info.typeDescriptionLink ? (
|
|
45
45
|
<a
|
|
46
|
-
class="
|
|
46
|
+
class="break-words font-mono text-foreground text-xs underline-offset-2 hover:underline"
|
|
47
47
|
href={info.typeDescriptionLink}
|
|
48
48
|
>
|
|
49
49
|
{info.type}
|
|
50
50
|
</a>
|
|
51
51
|
) : (
|
|
52
|
-
<code class="
|
|
52
|
+
<code class="break-words font-mono text-foreground text-xs">
|
|
53
53
|
{info.type}
|
|
54
54
|
</code>
|
|
55
55
|
)}
|
|
@@ -81,7 +81,7 @@ const columns = "grid grid-cols-[1fr_2fr_auto] items-center gap-4";
|
|
|
81
81
|
<div class="space-y-1.5">
|
|
82
82
|
<div class={columns}>
|
|
83
83
|
<span class="text-muted-foreground">Type</span>
|
|
84
|
-
<code class="
|
|
84
|
+
<code class="break-words font-mono text-foreground">
|
|
85
85
|
{info.type}
|
|
86
86
|
</code>
|
|
87
87
|
<span aria-hidden="true" class="size-3.5" />
|
|
@@ -89,7 +89,7 @@ const columns = "grid grid-cols-[1fr_2fr_auto] items-center gap-4";
|
|
|
89
89
|
{info.default && (
|
|
90
90
|
<div class={columns}>
|
|
91
91
|
<span class="text-muted-foreground">Default</span>
|
|
92
|
-
<code class="
|
|
92
|
+
<code class="break-words font-mono text-foreground">
|
|
93
93
|
{info.default}
|
|
94
94
|
</code>
|
|
95
95
|
<span aria-hidden="true" class="size-3.5" />
|
|
@@ -60,7 +60,7 @@ const tagList = Array.isArray(tags) ? tags : tags ? [tags] : [];
|
|
|
60
60
|
)
|
|
61
61
|
}
|
|
62
62
|
</header>
|
|
63
|
-
<div class="
|
|
63
|
+
<div class="text-muted-foreground text-sm leading-6 [&_a]:font-medium [&_a]:text-foreground [&_a]:underline [&_code]:rounded [&_code]:bg-muted [&_code]:px-1 [&_h2]:mt-0! [&_h2]:border-t-0! [&_h2]:pt-0! [&_h2]:text-base [&_h2]:font-semibold [&_h2]:text-foreground [&_h3]:font-semibold [&_h3]:text-foreground [&_img]:rounded-blume [&_li]:my-1 [&_p:first-child]:mt-0! [&_p:last-child]:mb-0!">
|
|
64
64
|
<slot />
|
|
65
65
|
</div>
|
|
66
66
|
</article>
|
|
@@ -18,6 +18,7 @@ interface Props {
|
|
|
18
18
|
dark?: string;
|
|
19
19
|
alt: string;
|
|
20
20
|
href: string;
|
|
21
|
+
text?: string;
|
|
21
22
|
} | null;
|
|
22
23
|
navigation: Navigation;
|
|
23
24
|
route: string;
|
|
@@ -57,6 +58,19 @@ const {
|
|
|
57
58
|
const LogoSlot = resolveSlot(layout.Logo, Logo);
|
|
58
59
|
const SearchSlot = resolveSlot(layout.Search, Search);
|
|
59
60
|
|
|
61
|
+
// The mobile nav-toggle opens a drawer of navigation. On docs pages that drawer
|
|
62
|
+
// is the sidebar tree (`hasSidebar`); on chrome-only pages (a landing page via
|
|
63
|
+
// PageLayout) it's a tabs-only drawer the layout renders — so the button is also
|
|
64
|
+
// needed whenever there are tabs to reveal.
|
|
65
|
+
const showNavToggle = hasSidebar || navigation.tabs.length > 0;
|
|
66
|
+
// Where the header's inline tab bar appears. With a sidebar it shares the `md`
|
|
67
|
+
// breakpoint with the docs drawer; without one, the tabs-only drawer is the sole
|
|
68
|
+
// mobile nav below `lg`, so the inline tabs wait until `lg` to avoid duplicating
|
|
69
|
+
// the drawer's links between `md` and `lg`.
|
|
70
|
+
const tabsNavClass = hasSidebar
|
|
71
|
+
? "hidden gap-1 md:flex"
|
|
72
|
+
: "hidden gap-1 lg:flex";
|
|
73
|
+
|
|
60
74
|
const iconButton =
|
|
61
75
|
"inline-flex size-9 cursor-pointer items-center justify-center rounded-full text-muted-foreground transition-colors hover:bg-muted hover:text-foreground";
|
|
62
76
|
|
|
@@ -68,14 +82,23 @@ const iconButton =
|
|
|
68
82
|
// styles fade while the non-animated ones snap, so we disable all transitions
|
|
69
83
|
// for the swap (next-themes' disableTransitionOnChange trick), force a
|
|
70
84
|
// synchronous style flush, then restore them on the next tick.
|
|
71
|
-
|
|
85
|
+
//
|
|
86
|
+
// The mobile drawer sits below the sticky header, but the banner above the
|
|
87
|
+
// header has no fixed height (its text wraps), so the drawer's top offset can't
|
|
88
|
+
// be static: on open we measure the header's on-screen bottom edge into
|
|
89
|
+
// `--blume-drawer-top` (read by the drawer's `top`/`height`), lock page scroll
|
|
90
|
+
// so the offset can't drift while open, and re-measure when a banner dismiss or
|
|
91
|
+
// a resize/rotation moves the header. Crossing into the desktop breakpoint
|
|
92
|
+
// (64rem, Tailwind `lg`) closes the drawer so the scroll lock can't outlive it.
|
|
93
|
+
const clickScript = `(()=>{const dr=()=>{const h=document.querySelector("[data-blume-header]");if(h){document.documentElement.style.setProperty("--blume-drawer-top",h.getBoundingClientRect().bottom+"px");}};document.addEventListener("click",(e)=>{const t=e.target.closest("[data-blume-theme-toggle]");if(t){const d=document.documentElement;const next=d.dataset.theme==="dark"?"light":"dark";const s=document.createElement("style");s.appendChild(document.createTextNode("*,*::before,*::after{transition:none!important}"));document.head.appendChild(s);d.dataset.theme=next;localStorage.setItem("blume-theme",next);window.getComputedStyle(d).opacity;setTimeout(()=>document.head.removeChild(s),1);}const n=e.target.closest("[data-blume-nav-toggle]");if(n){const d=document.documentElement;const open=d.toggleAttribute("data-blume-nav-open");if(open){dr();}d.style.overflow=open?"hidden":"";}const b=e.target.closest("[data-blume-banner-dismiss]");if(b){const bar=b.closest("[data-blume-banner]");const k=bar&&bar.getAttribute("data-banner-key");if(k){localStorage.setItem("blume-banner:"+k,"1");}document.documentElement.setAttribute("data-blume-banner-hidden","");requestAnimationFrame(dr);}});addEventListener("resize",()=>{const d=document.documentElement;if(!d.hasAttribute("data-blume-nav-open")){return;}if(matchMedia("(min-width:64rem)").matches){d.removeAttribute("data-blume-nav-open");d.style.overflow="";}else{dr();}});})();`;
|
|
72
94
|
---
|
|
73
95
|
|
|
74
96
|
<header
|
|
75
97
|
class="sticky top-0 z-40 flex h-16 items-center gap-3 bg-background/90 px-4 backdrop-blur md:px-6"
|
|
98
|
+
data-blume-header
|
|
76
99
|
>
|
|
77
100
|
{
|
|
78
|
-
|
|
101
|
+
showNavToggle && (
|
|
79
102
|
<button
|
|
80
103
|
aria-label="Toggle navigation"
|
|
81
104
|
class={`${iconButton} lg:hidden`}
|
|
@@ -98,7 +121,7 @@ const clickScript = `document.addEventListener("click",(e)=>{const t=e.target.cl
|
|
|
98
121
|
}
|
|
99
122
|
{
|
|
100
123
|
navigation.tabs.length > 0 && (
|
|
101
|
-
<nav aria-label="Sections" class=
|
|
124
|
+
<nav aria-label="Sections" class={tabsNavClass}>
|
|
102
125
|
{navigation.tabs.map((tab) => (
|
|
103
126
|
<a
|
|
104
127
|
aria-current={
|
|
@@ -19,6 +19,10 @@ const logoLight = logo?.light;
|
|
|
19
19
|
const logoDark = logo?.dark ?? logo?.light;
|
|
20
20
|
const logoAlt = logo?.alt ?? "";
|
|
21
21
|
const brandHref = logo?.href ?? "/";
|
|
22
|
+
// Wordmark beside the mark: the configured `logo.text`, or the site title when
|
|
23
|
+
// it's omitted. An explicit `""` hides it (an image-only logo whose mark already
|
|
24
|
+
// carries the wordmark).
|
|
25
|
+
const brandText = logo?.text ?? site.title;
|
|
22
26
|
---
|
|
23
27
|
|
|
24
28
|
<a
|
|
@@ -46,5 +50,5 @@ const brandHref = logo?.href ?? "/";
|
|
|
46
50
|
</>
|
|
47
51
|
))
|
|
48
52
|
}
|
|
49
|
-
{
|
|
53
|
+
{brandText && <span>{brandText}</span>}
|
|
50
54
|
</a>
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
---
|
|
2
|
-
// A top-level navigation selector
|
|
2
|
+
// A top-level navigation selector: a dropdown that switches
|
|
3
3
|
// between partitions of the site — a product, a version, or any grouped set of
|
|
4
4
|
// destinations (`navigation.selectors` in the config). Zero-JS, built on
|
|
5
5
|
// <details>/<summary> like the language switcher.
|