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.
Files changed (137) hide show
  1. package/dist/cli/index.js +2235 -5942
  2. package/dist/cli/index.js.map +36 -65
  3. package/dist/types/core/config.d.ts +0 -8
  4. package/dist/types/core/data.d.ts +2 -2
  5. package/dist/types/core/schema.d.ts +58 -453
  6. package/dist/types/core/types.d.ts +1 -7
  7. package/docs/advanced/meta.ts +1 -8
  8. package/docs/advanced/skills.mdx +28 -0
  9. package/docs/configuration/ai.mdx +3 -1
  10. package/docs/configuration/index.mdx +13 -17
  11. package/docs/configuration/theming.mdx +3 -11
  12. package/docs/content/components.mdx +2 -53
  13. package/docs/content/i18n.mdx +1 -5
  14. package/docs/content/sources.mdx +1 -1
  15. package/docs/content/syntax.mdx +1 -1
  16. package/docs/index.mdx +0 -1
  17. package/docs/reference/cli.mdx +12 -13
  18. package/package.json +4 -6
  19. package/skills/blume/SKILL.md +71 -0
  20. package/skills/blume-update-docs/SKILL.md +52 -0
  21. package/skills/blume-update-docs/references/audit-checklist.md +46 -0
  22. package/src/ai/mcp/discovery.ts +3 -1
  23. package/src/ai/mcp/server.ts +3 -1
  24. package/src/astro/component-slots.ts +10 -2
  25. package/src/astro/generate.ts +15 -12
  26. package/src/astro/integration.ts +0 -21
  27. package/src/astro/templates.ts +62 -37
  28. package/src/cli/coalesce.ts +43 -0
  29. package/src/cli/commands/dev.ts +30 -22
  30. package/src/cli/commands/validate.ts +0 -2
  31. package/src/cli/dev-lock.ts +4 -2
  32. package/src/cli/index.ts +0 -2
  33. package/src/components/BlumePage.astro +0 -6
  34. package/src/components/Icon.astro +1 -12
  35. package/src/components/content/AccordionItem.astro +3 -6
  36. package/src/components/content/Badge.astro +1 -3
  37. package/src/components/content/Callout.astro +3 -9
  38. package/src/components/content/Card.astro +2 -3
  39. package/src/components/content/ColorItem.astro +8 -5
  40. package/src/components/content/Column.astro +1 -1
  41. package/src/components/content/Prompt.astro +8 -4
  42. package/src/components/content/Step.astro +3 -4
  43. package/src/components/content/Tab.astro +2 -3
  44. package/src/components/content/Tabs.astro +13 -2
  45. package/src/components/content/TypeTable.astro +5 -5
  46. package/src/components/content/Update.astro +1 -1
  47. package/src/components/content/mermaid-element.ts +20 -2
  48. package/src/components/islands/ask-ai.tsx +4 -8
  49. package/src/components/islands/base-path.ts +30 -0
  50. package/src/components/islands/hooks.ts +12 -8
  51. package/src/components/layout/Header.astro +26 -3
  52. package/src/components/layout/Logo.astro +5 -1
  53. package/src/components/layout/NavSelector.astro +1 -1
  54. package/src/components/layout/NavTree.astro +14 -14
  55. package/src/components/layout/PageActions.astro +17 -11
  56. package/src/components/layout/PageLayout.astro +42 -0
  57. package/src/components/layout/ReferenceLayout.astro +1 -0
  58. package/src/components/layout/RootLayout.astro +31 -2
  59. package/src/components/layout/Search.astro +9 -6
  60. package/src/components/layout/nav-utils.ts +9 -4
  61. package/src/components/layout/search/types.ts +16 -5
  62. package/src/components/openapi/ApiOverview.astro +4 -50
  63. package/src/components/openapi/ApiTagOperations.astro +42 -0
  64. package/src/components/openapi/ParametersTable.astro +1 -1
  65. package/src/components/openapi/SchemaProperty.astro +1 -1
  66. package/src/components/openapi/SchemaTable.astro +3 -3
  67. package/src/components/openapi/helpers.ts +17 -8
  68. package/src/components/openapi/snippets.ts +17 -4
  69. package/src/core/builtin-tags.ts +1 -3
  70. package/src/core/config.ts +19 -33
  71. package/src/core/data.ts +2 -2
  72. package/src/core/graph.ts +7 -5
  73. package/src/core/links.ts +5 -19
  74. package/src/core/meta.ts +1 -1
  75. package/src/core/navigation.ts +5 -6
  76. package/src/core/project-graph.ts +0 -5
  77. package/src/core/schema.ts +13 -90
  78. package/src/core/sources/filesystem.ts +19 -1
  79. package/src/core/sources/mdx-remote.ts +20 -4
  80. package/src/core/sources/normalize.ts +28 -6
  81. package/src/core/sources/resolve.ts +0 -13
  82. package/src/core/sources/watch.ts +45 -0
  83. package/src/core/types.ts +1 -8
  84. package/src/markdown/code-title.ts +6 -3
  85. package/src/markdown/package-install.ts +3 -1
  86. package/src/og/card.ts +16 -4
  87. package/src/openapi/parse.ts +197 -14
  88. package/src/openapi/render-mdx.ts +54 -11
  89. package/src/openapi/source.ts +19 -2
  90. package/src/search/sync/orama-cloud.ts +2 -0
  91. package/src/search/sync/typesense.ts +4 -0
  92. package/src/theme/entry.ts +45 -17
  93. package/src/theme/icons.ts +22 -104
  94. package/src/theme/palette.ts +35 -47
  95. package/src/theme/twoslash.ts +6 -1
  96. package/dist/types/core/bridge.d.ts +0 -24
  97. package/dist/types/migrate/mintlify/assets.d.ts +0 -8
  98. package/dist/types/migrate/mintlify/config.d.ts +0 -16
  99. package/dist/types/migrate/mintlify/i18n.d.ts +0 -7
  100. package/docs/advanced/bridge.mdx +0 -76
  101. package/docs/advanced/migrate.mdx +0 -123
  102. package/src/astro/static-assets.ts +0 -117
  103. package/src/cli/commands/migrate.ts +0 -39
  104. package/src/components/content/ApiField.astro +0 -75
  105. package/src/components/content/ParamField.astro +0 -39
  106. package/src/components/content/RequestField.astro +0 -23
  107. package/src/components/content/ResponseField.astro +0 -23
  108. package/src/components/content/Warning.astro +0 -9
  109. package/src/core/assets.ts +0 -31
  110. package/src/core/bridge.ts +0 -102
  111. package/src/core/sources/mintlify.ts +0 -204
  112. package/src/migrate/fumadocs/config.ts +0 -155
  113. package/src/migrate/fumadocs/content.ts +0 -378
  114. package/src/migrate/fumadocs/frontmatter.ts +0 -18
  115. package/src/migrate/fumadocs/groups.ts +0 -237
  116. package/src/migrate/fumadocs/index.ts +0 -340
  117. package/src/migrate/fumadocs/meta.ts +0 -244
  118. package/src/migrate/migrate.ts +0 -53
  119. package/src/migrate/mintlify/assets.ts +0 -46
  120. package/src/migrate/mintlify/config.ts +0 -958
  121. package/src/migrate/mintlify/content.ts +0 -120
  122. package/src/migrate/mintlify/frontmatter.ts +0 -126
  123. package/src/migrate/mintlify/i18n.ts +0 -51
  124. package/src/migrate/mintlify/icons.ts +0 -128
  125. package/src/migrate/mintlify/index.ts +0 -372
  126. package/src/migrate/mintlify/snippets.ts +0 -306
  127. package/src/migrate/mintlify/transform.ts +0 -82
  128. package/src/migrate/nextra/content.ts +0 -46
  129. package/src/migrate/nextra/frontmatter.ts +0 -40
  130. package/src/migrate/nextra/index.ts +0 -374
  131. package/src/migrate/nextra/meta.ts +0 -266
  132. package/src/migrate/shared.ts +0 -728
  133. package/src/migrate/starlight/config.ts +0 -455
  134. package/src/migrate/starlight/content.ts +0 -78
  135. package/src/migrate/starlight/frontmatter.ts +0 -111
  136. package/src/migrate/starlight/i18n.ts +0 -54
  137. package/src/migrate/starlight/index.ts +0 -131
@@ -1,244 +0,0 @@
1
- import type { FolderMeta } from "../../core/schema.ts";
2
-
3
- /**
4
- * Translate a Fumadocs folder `meta.json` into a Blume `FolderMeta` (`meta.ts`).
5
- * Unlike Nextra, Fumadocs declares a folder's own title/icon/ordering in *that
6
- * folder's* `meta.json`, so the mapping is self-contained. Fumadocs-only
7
- * concepts with no Blume equivalent are dropped and reported:
8
- *
9
- * - `defaultOpen` -> `collapsed` (inverted) + `display: "group"` (collapsible).
10
- * - `root: true` -> `display: "page"` (the closest analogue to a page-tree root).
11
- * - `description` -> dropped (folders carry no description in Blume).
12
- * - `pages` entries: plain slugs are kept as ordering; `"..."` (the rest marker,
13
- * already Blume's default) is dropped silently; `"...folder"` (the *extract*
14
- * operator, which inlines a folder's children) keeps the folder's place in the
15
- * ordering as a plain `"folder"` key and warns that it renders as a normal
16
- * group rather than flattened; `"---Section---"` separators and `"[Text](url)"`
17
- * links have no folder-meta home and are dropped with a warning.
18
- *
19
- * When a `pages` array carries `---Section---` separators, the migrator instead
20
- * takes the *structural* path ({@link parseFumadocsPages} + the group-folder
21
- * reshape in `index.ts`), which rebuilds each section as a Blume group folder
22
- * rather than flattening it through `filterPages`.
23
- */
24
-
25
- interface FumadocsMeta {
26
- defaultOpen?: unknown;
27
- description?: unknown;
28
- icon?: unknown;
29
- pages?: unknown;
30
- root?: unknown;
31
- title?: unknown;
32
- }
33
-
34
- const SEPARATOR = /^---(?<label>.*)---$/u;
35
- const LINK = /^(?:\[[^\]]*\])?\[(?<text>[^\]]*)\]\((?<href>[^)]*)\)$/u;
36
- const REST = "...";
37
- /** Fumadocs *extract* operator: `...folder` inlines that folder's children. */
38
- const EXTRACT = /^\.\.\.(?<folder>.+)$/u;
39
-
40
- interface PagesResult {
41
- pages: string[];
42
- warnings: string[];
43
- }
44
-
45
- const filterPages = (raw: unknown): PagesResult => {
46
- if (!Array.isArray(raw)) {
47
- return { pages: [], warnings: [] };
48
- }
49
-
50
- const pages: string[] = [];
51
- const warnings: string[] = [];
52
- for (const entry of raw) {
53
- if (typeof entry !== "string") {
54
- continue;
55
- }
56
- const value = entry.trim();
57
- if (value === REST || value === "") {
58
- continue;
59
- }
60
- const extract = EXTRACT.exec(value);
61
- if (extract) {
62
- // Blume has no "flatten a folder's children inline" sidebar primitive, but
63
- // the folder is a sibling here, so keep its position in the ordering and
64
- // let it render as a normal group.
65
- const folder = extract.groups?.folder?.trim() ?? "";
66
- if (folder) {
67
- pages.push(folder);
68
- warnings.push(
69
- `Sidebar extract "...${folder}" became a normal "${folder}" group (its pages are not flattened into the parent).`
70
- );
71
- }
72
- continue;
73
- }
74
- const separator = SEPARATOR.exec(value);
75
- if (separator) {
76
- const label = separator.groups?.label?.trim();
77
- warnings.push(
78
- `Dropped sidebar separator "${label || value}" — recreate it with a sidebar group if needed.`
79
- );
80
- continue;
81
- }
82
- const link = LINK.exec(value);
83
- if (link) {
84
- warnings.push(
85
- `Dropped sidebar link "${link.groups?.text}" (${link.groups?.href}) — add it to navbar.links manually.`
86
- );
87
- continue;
88
- }
89
- pages.push(value);
90
- }
91
-
92
- return { pages, warnings };
93
- };
94
-
95
- export interface FumadocsMetaConversion {
96
- meta: FolderMeta;
97
- warnings: string[];
98
- }
99
-
100
- /** Render a Blume `meta.ts` module source for a `FolderMeta`. */
101
- export const renderMetaModule = (meta: FolderMeta): string =>
102
- `import { defineMeta } from "blume";\n\nexport default defineMeta(${JSON.stringify(meta, null, 2)});\n`;
103
-
104
- /**
105
- * Translate a folder's own title/icon/display fields — everything in a Fumadocs
106
- * `meta.json` *except* the `pages` ordering. Split out from
107
- * {@link translateFumadocsMeta} so the structural path (which reshapes `pages`
108
- * into group folders) can reuse the self fields without the flat `pages` array.
109
- */
110
- export const translateFumadocsSelfMeta = (
111
- value: unknown
112
- ): FumadocsMetaConversion => {
113
- if (!value || typeof value !== "object" || Array.isArray(value)) {
114
- return { meta: {}, warnings: [] };
115
- }
116
-
117
- const source = value as FumadocsMeta;
118
- const meta: FolderMeta = {};
119
- const warnings: string[] = [];
120
-
121
- if (typeof source.title === "string") {
122
- meta.title = source.title;
123
- }
124
- if (typeof source.icon === "string") {
125
- meta.icon = source.icon;
126
- }
127
- if (source.root === true) {
128
- meta.display = "page";
129
- } else if (typeof source.defaultOpen === "boolean") {
130
- meta.display = "group";
131
- meta.collapsed = !source.defaultOpen;
132
- }
133
- if (typeof source.description === "string") {
134
- warnings.push(
135
- "Dropped folder `description` — Blume folders have no description field."
136
- );
137
- }
138
-
139
- return { meta, warnings };
140
- };
141
-
142
- /** Map a parsed Fumadocs `meta.json` object onto a Blume `FolderMeta`. */
143
- export const translateFumadocsMeta = (
144
- value: unknown
145
- ): FumadocsMetaConversion => {
146
- const { meta, warnings } = translateFumadocsSelfMeta(value);
147
- if (value && typeof value === "object" && !Array.isArray(value)) {
148
- const { pages, warnings: pageWarnings } = filterPages(
149
- (value as FumadocsMeta).pages
150
- );
151
- if (pages.length > 0) {
152
- meta.pages = pages;
153
- }
154
- warnings.push(...pageWarnings);
155
- }
156
- return { meta, warnings };
157
- };
158
-
159
- // ---------------------------------------------------------------------------
160
- // Structural pages (separators -> sections)
161
- // ---------------------------------------------------------------------------
162
-
163
- /**
164
- * One entry in a Fumadocs `pages` array, after classification: a `ref` (a plain
165
- * page or folder name), an `extract` (`...folder`, whose children Fumadocs
166
- * inlines), or a `link` (`[Text](url)`).
167
- */
168
- export type FumadocsPageItem =
169
- | { kind: "extract"; name: string }
170
- | { kind: "link"; href: string; text: string }
171
- | { kind: "ref"; name: string };
172
-
173
- /** A run of items under one `---Label---` separator. */
174
- export interface FumadocsSection {
175
- items: FumadocsPageItem[];
176
- label: string;
177
- }
178
-
179
- export interface FumadocsPagesStructure {
180
- /** Whether any `---Section---` separator was present. */
181
- hasSections: boolean;
182
- /** Items before the first separator; they stay at the folder's top level. */
183
- lead: FumadocsPageItem[];
184
- sections: FumadocsSection[];
185
- }
186
-
187
- /**
188
- * Parse a Fumadocs `pages` array into its structural shape: the lead items
189
- * (before any separator) and the sections each `---Label---` introduces. Unlike
190
- * {@link filterPages}, separators are preserved (as section boundaries) so the
191
- * migrator can rebuild them as Blume group folders. The rest marker (`"..."`) is
192
- * dropped — Blume appends unlisted pages by default.
193
- */
194
- export const parseFumadocsPages = (raw: unknown): FumadocsPagesStructure => {
195
- const lead: FumadocsPageItem[] = [];
196
- const sections: FumadocsSection[] = [];
197
- if (!Array.isArray(raw)) {
198
- return { hasSections: false, lead, sections };
199
- }
200
-
201
- let current: FumadocsSection | null = null;
202
- const push = (item: FumadocsPageItem): void => {
203
- (current ? current.items : lead).push(item);
204
- };
205
-
206
- for (const entry of raw) {
207
- if (typeof entry !== "string") {
208
- continue;
209
- }
210
- const value = entry.trim();
211
- if (value === REST || value === "") {
212
- continue;
213
- }
214
- const separator = SEPARATOR.exec(value);
215
- if (separator) {
216
- current = {
217
- items: [],
218
- label: separator.groups?.label?.trim() || "Section",
219
- };
220
- sections.push(current);
221
- continue;
222
- }
223
- const extract = EXTRACT.exec(value);
224
- if (extract) {
225
- const name = extract.groups?.folder?.trim();
226
- if (name) {
227
- push({ kind: "extract", name });
228
- }
229
- continue;
230
- }
231
- const link = LINK.exec(value);
232
- if (link) {
233
- push({
234
- href: link.groups?.href ?? "",
235
- kind: "link",
236
- text: link.groups?.text ?? "",
237
- });
238
- continue;
239
- }
240
- push({ kind: "ref", name: value });
241
- }
242
-
243
- return { hasSections: sections.length > 0, lead, sections };
244
- };
@@ -1,53 +0,0 @@
1
- import { migrateFumadocsProject } from "./fumadocs/index.ts";
2
- import { migrateMintlifyProject } from "./mintlify/index.ts";
3
- import { migrateNextraProject } from "./nextra/index.ts";
4
- import { migrateStarlightProject } from "./starlight/index.ts";
5
-
6
- export interface MigrationResult {
7
- moved: number;
8
- warnings: string[];
9
- }
10
-
11
- /**
12
- * Migrate a Mintlify project (`docs.json`/`mint.json` + MDX). Translates the
13
- * config, rewrites pages to idiomatic Blume MDX in place, and relocates assets.
14
- * Unlike the other migrators, content stays at the project root.
15
- */
16
- export const migrateMintlify = (root: string): Promise<MigrationResult> =>
17
- migrateMintlifyProject(root);
18
-
19
- /**
20
- * Migrate a Nextra project (`content/` or `pages/` + `_meta` files). Moves pages
21
- * into `docs/`, rewrites `<Callout>`s to directives, and converts every `_meta`
22
- * into a typed `meta.ts`, preserving navigation order and titles.
23
- */
24
- export const migrateNextra = (root: string): Promise<MigrationResult> =>
25
- migrateNextraProject(root);
26
-
27
- /**
28
- * Migrate a Fumadocs project (`content/docs` + `meta.json`). Moves pages into
29
- * `docs/`, rewrites Fumadocs MDX (callouts, `<Cards>`/`<Accordions>`/`<Files>`,
30
- * `<Tabs items>`, `<include>`) to idiomatic Blume markup, converts every
31
- * `meta.json` into a typed `meta.ts`, and preserves the `/docs` route prefix.
32
- */
33
- export const migrateFumadocs = (root: string): Promise<MigrationResult> =>
34
- migrateFumadocsProject(root);
35
-
36
- /**
37
- * Migrate a Starlight project (`src/content/docs` + `astro.config.*`). Translates
38
- * the `starlight({...})` options into `blume.config.ts` and rewrites each page to
39
- * idiomatic Blume MDX in place (asides → directives, component renames,
40
- * frontmatter mapping). Content stays under `src/content/docs`.
41
- */
42
- export const migrateStarlight = (root: string): Promise<MigrationResult> =>
43
- migrateStarlightProject(root);
44
-
45
- export const migrators: Record<
46
- string,
47
- (root: string) => Promise<MigrationResult>
48
- > = {
49
- fumadocs: migrateFumadocs,
50
- mintlify: migrateMintlify,
51
- nextra: migrateNextra,
52
- starlight: migrateStarlight,
53
- };
@@ -1,46 +0,0 @@
1
- import type { BlumeConfig } from "../../core/schema.ts";
2
-
3
- /** Asset paths referenced by the resolved config (logo, favicon, backgrounds). */
4
- const assetRefs = (config: BlumeConfig): unknown[] => {
5
- const refs: unknown[] = ["/images"];
6
- const logo = config.logo as
7
- | string
8
- | { dark?: string; light?: string }
9
- | undefined;
10
- if (typeof logo === "string") {
11
- refs.push(logo);
12
- } else if (logo) {
13
- refs.push(logo.light, logo.dark);
14
- }
15
- const favicon = config.favicon as
16
- | string
17
- | { dark?: string; light?: string }
18
- | undefined;
19
- if (typeof favicon === "string") {
20
- refs.push(favicon);
21
- } else if (favicon) {
22
- refs.push(favicon.light, favicon.dark);
23
- }
24
- refs.push(config.theme?.backgroundImage, config.theme?.backgroundImageDark);
25
- return refs;
26
- };
27
-
28
- /**
29
- * Top-level path segments referenced as static assets by a Mintlify config
30
- * (the conventional `/images`, plus logo/favicon/background paths). These are
31
- * the root-served folders Mintlify exposes at the site root; Blume serves them
32
- * via `content.assets` (bridge) or relocates them under `public/` (migrator).
33
- */
34
- export const assetSegments = (config: BlumeConfig): string[] => {
35
- const segments = new Set<string>();
36
- for (const ref of assetRefs(config)) {
37
- if (typeof ref !== "string" || !ref.startsWith("/")) {
38
- continue;
39
- }
40
- const [segment] = ref.replace(/^\/+/u, "").split("/");
41
- if (segment) {
42
- segments.add(segment);
43
- }
44
- }
45
- return [...segments];
46
- };