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,266 +0,0 @@
1
- import type { FolderMeta } from "../../core/schema.ts";
2
- import {
3
- parseKey,
4
- readString,
5
- scanObject,
6
- splitKeyValue,
7
- stripJsComments,
8
- } from "../shared.ts";
9
-
10
- /**
11
- * Read Nextra `_meta` files (ordering + per-entry config) and map them onto
12
- * Blume's folder `meta.ts` shape. `_meta.js`/`_meta.ts` are parsed by extracting
13
- * their default-exported object literal as text — Nextra meta is overwhelmingly
14
- * a map of string titles and simple config objects — rather than executing user
15
- * code (matching the other migrators, which never eval). Entries whose value is
16
- * an expression (JSX title, imported component, computed) keep their slug for
17
- * ordering but are otherwise flagged `unparseable`. The low-level JS scanners
18
- * live in `../shared.ts` so the Starlight migrator reuses them.
19
- */
20
-
21
- export interface NextraMetaEntry {
22
- display?: string;
23
- href?: string;
24
- newWindow?: boolean;
25
- slug: string;
26
- title?: string;
27
- type?: string;
28
- /** The value couldn't be read beyond its key (e.g. a JSX/expression title). */
29
- unparseable: boolean;
30
- }
31
-
32
- const DEFAULT_EXPORT = /(?:export\s+default|module\.exports\s*=)\s*/u;
33
-
34
- /** Locate and split the default-exported object literal into raw entries. */
35
- const extractObjectEntries = (source: string): string[] | null => {
36
- const clean = stripJsComments(source);
37
- const marker = DEFAULT_EXPORT.exec(clean);
38
- if (!marker) {
39
- return null;
40
- }
41
- let index = marker.index + marker[0].length;
42
- while (index < clean.length && /\s/u.test(clean[index] ?? "")) {
43
- index += 1;
44
- }
45
- if (clean[index] !== "{") {
46
- return null;
47
- }
48
- return scanObject(clean, index)?.entries ?? null;
49
- };
50
-
51
- const applyField = (
52
- target: NextraMetaEntry,
53
- key: string,
54
- value: string
55
- ): void => {
56
- if (key === "newWindow") {
57
- const flag = value.trim();
58
- if (flag === "true") {
59
- target.newWindow = true;
60
- } else if (flag === "false") {
61
- target.newWindow = false;
62
- }
63
- return;
64
- }
65
- const str = readString(value);
66
- if (str === null) {
67
- return;
68
- }
69
- if (key === "title") {
70
- target.title = str;
71
- } else if (key === "type") {
72
- target.type = str;
73
- } else if (key === "display") {
74
- target.display = str;
75
- } else if (key === "href") {
76
- target.href = str;
77
- }
78
- };
79
-
80
- const entryFromObjectEntries = (
81
- slug: string,
82
- entries: string[]
83
- ): NextraMetaEntry => {
84
- const entry: NextraMetaEntry = { slug, unparseable: false };
85
- for (const raw of entries) {
86
- const kv = splitKeyValue(raw);
87
- if (kv?.key) {
88
- applyField(entry, parseKey(kv.key), kv.value);
89
- }
90
- }
91
- return entry;
92
- };
93
-
94
- const entryFromValue = (slug: string, value: string): NextraMetaEntry => {
95
- const trimmed = value.trim();
96
- const [first] = trimmed;
97
- if (first === '"' || first === "'" || first === "`") {
98
- const str = readString(trimmed);
99
- return str === null
100
- ? { slug, unparseable: true }
101
- : { slug, title: str, unparseable: false };
102
- }
103
- if (first === "{") {
104
- const scan = scanObject(trimmed, 0);
105
- if (scan) {
106
- return entryFromObjectEntries(slug, scan.entries);
107
- }
108
- }
109
- return { slug, unparseable: true };
110
- };
111
-
112
- const entryFromJson = (slug: string, value: unknown): NextraMetaEntry => {
113
- if (typeof value === "string") {
114
- return { slug, title: value, unparseable: false };
115
- }
116
- if (value && typeof value === "object" && !Array.isArray(value)) {
117
- const obj = value as Record<string, unknown>;
118
- const entry: NextraMetaEntry = { slug, unparseable: false };
119
- if (typeof obj.title === "string") {
120
- entry.title = obj.title;
121
- }
122
- if (typeof obj.type === "string") {
123
- entry.type = obj.type;
124
- }
125
- if (typeof obj.display === "string") {
126
- entry.display = obj.display;
127
- }
128
- if (typeof obj.href === "string") {
129
- entry.href = obj.href;
130
- }
131
- if (typeof obj.newWindow === "boolean") {
132
- entry.newWindow = obj.newWindow;
133
- }
134
- return entry;
135
- }
136
- return { slug, unparseable: true };
137
- };
138
-
139
- /**
140
- * Parse a Nextra `_meta` file into ordered entries, or null if no object
141
- * literal could be located (the caller then moves the file as-is and warns).
142
- */
143
- export const parseNextraMeta = (
144
- source: string,
145
- ext: string
146
- ): NextraMetaEntry[] | null => {
147
- if (ext === ".json") {
148
- let parsed: unknown;
149
- try {
150
- parsed = JSON.parse(source);
151
- } catch {
152
- return null;
153
- }
154
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
155
- return null;
156
- }
157
- return Object.entries(parsed as Record<string, unknown>).map(
158
- ([slug, value]) => entryFromJson(slug, value)
159
- );
160
- }
161
-
162
- const rawEntries = extractObjectEntries(source);
163
- if (!rawEntries) {
164
- return null;
165
- }
166
- const result: NextraMetaEntry[] = [];
167
- for (const raw of rawEntries) {
168
- const kv = splitKeyValue(raw);
169
- if (kv?.key) {
170
- result.push(entryFromValue(parseKey(kv.key), kv.value));
171
- }
172
- }
173
- return result;
174
- };
175
-
176
- export interface NextraMetaConversion {
177
- folderMeta: FolderMeta;
178
- /** Child subfolder slug -> title (Nextra declares a folder's title in its parent). */
179
- folderTitles: Record<string, string>;
180
- /** Child page slugs to hide from the sidebar (`display: "hidden"`). */
181
- hiddenPages: string[];
182
- /** `type: "page"` entries -> top-nav tabs (only used for the root meta). */
183
- navPages: { label: string; path: string }[];
184
- /** Child page slug -> sidebar label (a Nextra `_meta` string title). */
185
- pageLabels: Record<string, string>;
186
- warnings: string[];
187
- }
188
-
189
- /**
190
- * Map parsed Nextra entries onto a Blume `FolderMeta` (`pages` ordering) plus
191
- * the cross-file overrides the orchestrator threads into child `meta.ts` titles
192
- * and page frontmatter. `hasPage`/`hasDir` report whether a slug resolves to a
193
- * content file or a subdirectory in this folder.
194
- */
195
- export const toBlumeFolderMeta = (
196
- entries: NextraMetaEntry[],
197
- options: {
198
- hasDir: (slug: string) => boolean;
199
- hasPage: (slug: string) => boolean;
200
- }
201
- ): NextraMetaConversion => {
202
- const pages: string[] = [];
203
- const folderTitles: Record<string, string> = {};
204
- const pageLabels: Record<string, string> = {};
205
- const hiddenPages: string[] = [];
206
- const navPages: { label: string; path: string }[] = [];
207
- const warnings: string[] = [];
208
-
209
- for (const entry of entries) {
210
- const { slug } = entry;
211
- const label = entry.title ?? slug;
212
-
213
- if (entry.type === "separator") {
214
- warnings.push(
215
- `Dropped separator "${label}" — recreate it with a sidebar group if needed.`
216
- );
217
- continue;
218
- }
219
- if (entry.href) {
220
- warnings.push(
221
- `External link "${label}" (${entry.href}) — add it to navbar.links manually.`
222
- );
223
- continue;
224
- }
225
- if (entry.type === "menu") {
226
- warnings.push(
227
- `Dropped navbar menu "${label}" — recreate it via navbar/navigation config.`
228
- );
229
- continue;
230
- }
231
- if (entry.type === "page") {
232
- navPages.push({ label, path: slug === "index" ? "/" : `/${slug}` });
233
- continue;
234
- }
235
-
236
- const isPage = options.hasPage(slug);
237
- const isDir = options.hasDir(slug);
238
- if (!(isPage || isDir)) {
239
- continue;
240
- }
241
- if (entry.display === "hidden") {
242
- if (isPage) {
243
- hiddenPages.push(slug);
244
- }
245
- continue;
246
- }
247
-
248
- pages.push(slug);
249
- if (entry.title) {
250
- if (isDir) {
251
- folderTitles[slug] = entry.title;
252
- } else {
253
- pageLabels[slug] = entry.title;
254
- }
255
- }
256
- }
257
-
258
- return {
259
- folderMeta: pages.length > 0 ? { pages } : {},
260
- folderTitles,
261
- hiddenPages,
262
- navPages,
263
- pageLabels,
264
- warnings,
265
- };
266
- };