blume 0.1.5 → 0.3.0

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 (85) hide show
  1. package/dist/cli/index.js +2123 -555
  2. package/dist/cli/index.js.map +39 -25
  3. package/dist/types/core/data.d.ts +16 -0
  4. package/dist/types/core/define-components.d.ts +9 -2
  5. package/dist/types/core/diagnostics.d.ts +5 -0
  6. package/dist/types/core/schema.d.ts +136 -508
  7. package/dist/types/core/types.d.ts +2 -2
  8. package/docs/02-deployment.mdx +21 -2
  9. package/docs/advanced/changelog.mdx +28 -1
  10. package/docs/advanced/custom-pages.mdx +63 -2
  11. package/docs/configuration/ai.mdx +20 -3
  12. package/docs/configuration/customization.mdx +103 -5
  13. package/docs/configuration/index.mdx +25 -11
  14. package/docs/configuration/search.mdx +13 -1
  15. package/docs/configuration/seo.mdx +5 -0
  16. package/docs/configuration/theming.mdx +51 -0
  17. package/docs/content/components.mdx +18 -0
  18. package/docs/content/islands.mdx +73 -0
  19. package/docs/content/navigation.mdx +25 -0
  20. package/docs/content/sources.mdx +43 -0
  21. package/docs/content/syntax.mdx +1 -1
  22. package/docs/index.mdx +3 -12
  23. package/docs/reference/cli.mdx +49 -1
  24. package/docs/reference/frontmatter.mdx +9 -1
  25. package/package.json +3 -1
  26. package/src/ai/ask-context.ts +131 -0
  27. package/src/ai/ask-data.ts +25 -0
  28. package/src/astro/component-slots.ts +165 -0
  29. package/src/astro/generate.ts +162 -26
  30. package/src/astro/integration.ts +59 -0
  31. package/src/astro/pages.ts +5 -12
  32. package/src/astro/templates.ts +102 -45
  33. package/src/blume-modules.d.ts +25 -0
  34. package/src/cli/commands/build.ts +186 -1
  35. package/src/cli/commands/check.ts +62 -0
  36. package/src/cli/commands/dev.ts +21 -1
  37. package/src/cli/commands/doctor.ts +23 -6
  38. package/src/cli/commands/init.ts +163 -15
  39. package/src/cli/commands/validate.ts +16 -2
  40. package/src/cli/env.ts +84 -0
  41. package/src/cli/index.ts +20 -0
  42. package/src/cli/internal-error.ts +63 -0
  43. package/src/cli/log.ts +30 -1
  44. package/src/cli/prepare.ts +22 -3
  45. package/src/cli/required-secrets.ts +44 -0
  46. package/src/components/BlumePage.astro +107 -0
  47. package/src/components/content/CodeBlock.astro +7 -2
  48. package/src/components/index.ts +3 -3
  49. package/src/components/islands/ask-ai.tsx +15 -1
  50. package/src/components/islands/hooks.ts +188 -0
  51. package/src/components/layout/Empty.astro +6 -0
  52. package/src/components/layout/Header.astro +24 -39
  53. package/src/components/layout/Logo.astro +50 -0
  54. package/src/components/layout/NavSelector.astro +75 -0
  55. package/src/components/layout/PageLayout.astro +38 -2
  56. package/src/components/layout/RootLayout.astro +70 -4
  57. package/src/components/layout/hydration-hint.ts +30 -0
  58. package/src/components/layout/overrides.ts +6 -4
  59. package/src/components/props.ts +68 -0
  60. package/src/core/builtin-tags.ts +39 -0
  61. package/src/core/component-diagnostics.ts +44 -0
  62. package/src/core/component-overrides.ts +478 -0
  63. package/src/core/config.ts +8 -0
  64. package/src/core/data.ts +14 -0
  65. package/src/core/define-components.ts +9 -2
  66. package/src/core/diagnostics.ts +90 -1
  67. package/src/core/graph.ts +7 -0
  68. package/src/core/nav-diagnostics.ts +205 -0
  69. package/src/core/project-graph.ts +40 -1
  70. package/src/core/schema.ts +54 -96
  71. package/src/core/sources/github-releases.ts +200 -0
  72. package/src/core/sources/normalize.ts +51 -0
  73. package/src/core/sources/resolve.ts +16 -0
  74. package/src/core/types.ts +2 -2
  75. package/src/deploy/redirects.ts +43 -0
  76. package/src/markdown/index.ts +24 -0
  77. package/src/migrate/mintlify/config.ts +1 -176
  78. package/src/migrate/starlight/config.ts +0 -4
  79. package/src/og/card.ts +163 -38
  80. package/src/registry/eject.ts +39 -9
  81. package/src/registry/registry.ts +166 -0
  82. package/src/runtime/index.ts +61 -0
  83. package/src/vite-env.d.ts +14 -0
  84. package/docs/changelog/v0-1-0.mdx +0 -12
  85. package/docs/changelog/v0-2-0.mdx +0 -16
@@ -3,8 +3,10 @@ import { cp, mkdir, readFile, rm, writeFile } from "node:fs/promises";
3
3
 
4
4
  import { join, relative } from "pathe";
5
5
 
6
+ import { buildAskData } from "../ai/ask-data.ts";
6
7
  import { resolveAskBackend } from "../ai/ask.ts";
7
8
  import { buildRawMarkdown } from "../ai/markdown.ts";
9
+ import { planComponentSlots } from "../astro/component-slots.ts";
8
10
  import { discoverExamples } from "../astro/examples.ts";
9
11
  import {
10
12
  buildRuntimeData,
@@ -32,9 +34,9 @@ import {
32
34
  runtimeTsconfigTemplate,
33
35
  searchClientTemplate,
34
36
  searchEndpointTemplate,
35
- userComponentsTemplate,
36
37
  } from "../astro/templates.ts";
37
38
  import { scanProject } from "../core/project-graph.ts";
39
+ import type { BlumeProject } from "../core/project-graph.ts";
38
40
  import type { ProjectContext } from "../core/types.ts";
39
41
  import { buildRssFeeds, renderRssFeed } from "../deploy/rss.ts";
40
42
  import { buildReferenceFiles, hasReferences } from "../openapi/scalar.ts";
@@ -46,6 +48,35 @@ import { twoslashCss } from "../theme/twoslash.ts";
46
48
 
47
49
  const POSIX = (path: string): string => path.split("\\").join("/");
48
50
 
51
+ /**
52
+ * The Ask AI endpoint plus, unless the backend runs its own retrieval (Inkeep),
53
+ * its grounding snapshot. Empty when Ask AI is disabled.
54
+ */
55
+ const askFiles = async (
56
+ project: BlumeProject,
57
+ srcDir: string,
58
+ genDir: string
59
+ ): Promise<{ content: string; path: string }[]> => {
60
+ const { ask } = project.config.ai;
61
+ if (!ask?.enabled) {
62
+ return [];
63
+ }
64
+ const grounded = ask.provider !== "inkeep";
65
+ const files = [
66
+ {
67
+ content: askEndpointTemplate(resolveAskBackend(ask), grounded),
68
+ path: join(srcDir, "pages", "api", "ask.ts"),
69
+ },
70
+ ];
71
+ if (grounded) {
72
+ files.push({
73
+ content: `${JSON.stringify(await buildAskData(project))}\n`,
74
+ path: join(genDir, "ask-data.json"),
75
+ });
76
+ }
77
+ return files;
78
+ };
79
+
49
80
  /**
50
81
  * Promote the generated runtime into the project as an owned Astro app. After
51
82
  * eject the project has a normal `astro.config.mjs` and `src/`, the `blume` CLI
@@ -146,11 +177,15 @@ export const eject = async (root: string): Promise<string[]> => {
146
177
  exportEpub,
147
178
  exportPdf,
148
179
  mathEnabled: config.markdown.math,
180
+ needsReact,
149
181
  }),
150
182
  path: join(srcDir, "pages", "[...slug].astro"),
151
183
  },
152
184
  {
153
- content: userComponentsTemplate(componentsImport),
185
+ // Eject keeps the portable re-export form (relative import to the user's
186
+ // components file); hydration/island wrappers would need machine-specific
187
+ // absolute paths, so the ejected app owns and wires those itself.
188
+ content: planComponentSlots(componentsImport, null).module,
154
189
  path: join(genDir, "components.ts"),
155
190
  },
156
191
  // Island/example maps the catch-all imports; written even when empty so the
@@ -192,17 +227,12 @@ export const eject = async (root: string): Promise<string[]> => {
192
227
  ];
193
228
 
194
229
  if (askEnabled) {
195
- files.push({
196
- content: askEndpointTemplate(resolveAskBackend(config.ai.ask)),
197
- path: join(srcDir, "pages", "api", "ask.ts"),
198
- });
230
+ files.push(...(await askFiles(project, srcDir, genDir)));
199
231
  }
200
232
 
201
233
  if (config.seo.og.enabled) {
202
234
  files.push({
203
- content: ogEndpointTemplate(
204
- customOgRoutes(pages, config.title, config.description)
205
- ),
235
+ content: ogEndpointTemplate(customOgRoutes(pages, config.title)),
206
236
  path: join(srcDir, "pages", "og", "[...slug].png.ts"),
207
237
  });
208
238
  }
@@ -62,6 +62,165 @@ const layoutComponent = (config: {
62
62
  };
63
63
  };
64
64
 
65
+ /**
66
+ * A built-in MDX content component offered as editable source. Like
67
+ * {@link layoutComponent}, but registered under the `mdx` map (the tag you write
68
+ * in `.mdx`) rather than a `layout` slot.
69
+ */
70
+ const contentComponent = (config: {
71
+ name: string;
72
+ description: string;
73
+ /** Source basename under `src/components/content`. */
74
+ file: string;
75
+ /** MDX tag / component name, also the import name in the post-install hint. */
76
+ tag: string;
77
+ }): RegistryItem => {
78
+ const target = `components/blume/${config.file}`;
79
+ return {
80
+ description: config.description,
81
+ files: [
82
+ {
83
+ rewrite: true,
84
+ source: `components/content/${config.file}`,
85
+ target,
86
+ },
87
+ ],
88
+ name: config.name,
89
+ postInstall: [
90
+ "Register it in components.ts:",
91
+ ' import { defineComponents } from "blume";',
92
+ ` import ${config.tag} from "./${target}";`,
93
+ "",
94
+ ` export default defineComponents({ mdx: { ${config.tag} } });`,
95
+ "",
96
+ "It imports the rest from `blume/*`, so it matches the built-in until you edit it.",
97
+ ],
98
+ };
99
+ };
100
+
101
+ /** Every user-facing content component, by `blume add` name → source basename. */
102
+ const CONTENT_COMPONENTS: {
103
+ name: string;
104
+ description: string;
105
+ file: string;
106
+ tag: string;
107
+ }[] = [
108
+ {
109
+ description: "Aside for notes, tips, and warnings.",
110
+ file: "Callout.astro",
111
+ name: "callout",
112
+ tag: "Callout",
113
+ },
114
+ {
115
+ description: "A linkable card with icon, title, and body.",
116
+ file: "Card.astro",
117
+ name: "card",
118
+ tag: "Card",
119
+ },
120
+ {
121
+ description: "A responsive grid of cards.",
122
+ file: "CardGroup.astro",
123
+ name: "card-group",
124
+ tag: "CardGroup",
125
+ },
126
+ {
127
+ description: "Tabbed code blocks for multiple languages.",
128
+ file: "CodeGroup.astro",
129
+ name: "code-group",
130
+ tag: "CodeGroup",
131
+ },
132
+ {
133
+ description: "A small status/label badge.",
134
+ file: "Badge.astro",
135
+ name: "badge",
136
+ tag: "Badge",
137
+ },
138
+ {
139
+ description: "A numbered list of steps.",
140
+ file: "Steps.astro",
141
+ name: "steps",
142
+ tag: "Steps",
143
+ },
144
+ {
145
+ description: "A single step within Steps.",
146
+ file: "Step.astro",
147
+ name: "step",
148
+ tag: "Step",
149
+ },
150
+ {
151
+ description: "A tabbed content panel.",
152
+ file: "Tabs.astro",
153
+ name: "tabs",
154
+ tag: "Tabs",
155
+ },
156
+ {
157
+ description: "A single tab within Tabs.",
158
+ file: "Tab.astro",
159
+ name: "tab",
160
+ tag: "Tab",
161
+ },
162
+ {
163
+ description: "A collapsible accordion group.",
164
+ file: "Accordion.astro",
165
+ name: "accordion",
166
+ tag: "Accordion",
167
+ },
168
+ {
169
+ description: "A single item within an Accordion.",
170
+ file: "AccordionItem.astro",
171
+ name: "accordion-item",
172
+ tag: "AccordionItem",
173
+ },
174
+ {
175
+ description: "A multi-column layout.",
176
+ file: "Columns.astro",
177
+ name: "columns",
178
+ tag: "Columns",
179
+ },
180
+ {
181
+ description: "A single column within Columns.",
182
+ file: "Column.astro",
183
+ name: "column",
184
+ tag: "Column",
185
+ },
186
+ {
187
+ description: "A bordered frame around an image or embed.",
188
+ file: "Frame.astro",
189
+ name: "frame",
190
+ tag: "Frame",
191
+ },
192
+ {
193
+ description: "An inline expand/collapse disclosure.",
194
+ file: "Expandable.astro",
195
+ name: "expandable",
196
+ tag: "Expandable",
197
+ },
198
+ {
199
+ description: "A titled content panel.",
200
+ file: "Panel.astro",
201
+ name: "panel",
202
+ tag: "Panel",
203
+ },
204
+ {
205
+ description: "A hover tooltip.",
206
+ file: "Tooltip.astro",
207
+ name: "tooltip",
208
+ tag: "Tooltip",
209
+ },
210
+ {
211
+ description: "A compact linkable tile.",
212
+ file: "Tile.astro",
213
+ name: "tile",
214
+ tag: "Tile",
215
+ },
216
+ {
217
+ description: "A styled prompt / terminal block.",
218
+ file: "Prompt.astro",
219
+ name: "prompt",
220
+ tag: "Prompt",
221
+ },
222
+ ];
223
+
65
224
  /** The built-in, Blume-owned source registry. */
66
225
  export const registry: RegistryItem[] = [
67
226
  layoutComponent({
@@ -94,6 +253,13 @@ export const registry: RegistryItem[] = [
94
253
  name: "pagination",
95
254
  slot: "Pagination",
96
255
  }),
256
+ layoutComponent({
257
+ description: 'The "Was this page helpful?" feedback rating.',
258
+ file: "PageFeedback.astro",
259
+ name: "feedback",
260
+ slot: "Feedback",
261
+ }),
262
+ ...CONTENT_COMPONENTS.map(contentComponent),
97
263
  ];
98
264
 
99
265
  export const findItem = (name: string): RegistryItem | undefined =>
@@ -5,6 +5,14 @@
5
5
  * (config, navigation, page collections) without reaching into generated
6
6
  * runtime internals. The surface grows with the customization milestone.
7
7
  */
8
+ import type { BlumeData, BlumeRoute } from "../core/data.ts";
9
+
10
+ export type {
11
+ BlumeData,
12
+ BlumeDataConfig,
13
+ BlumeFeed,
14
+ BlumeRoute,
15
+ } from "../core/data.ts";
8
16
  export type {
9
17
  Heading,
10
18
  NavNode,
@@ -12,3 +20,56 @@ export type {
12
20
  NavTab,
13
21
  PageRecord,
14
22
  } from "../core/types.ts";
23
+
24
+ /** Query for {@link getBlumeCollection}. */
25
+ export interface BlumeCollectionQuery {
26
+ /** Astro collection to read from. Defaults to `"docs"`. */
27
+ collection?: string;
28
+ /** Include drafts, hidden pages, and translation fallbacks. Default `false`. */
29
+ includeHidden?: boolean;
30
+ /** Restrict to a locale code (matches `route.locale`). */
31
+ locale?: string;
32
+ /** Restrict to routes whose path starts with this prefix, e.g. `"/blog"`. */
33
+ prefix?: string;
34
+ }
35
+
36
+ /**
37
+ * Select content routes from the `blume:data` snapshot — for building custom
38
+ * index pages, listings, or feeds without touching generated internals. Pass the
39
+ * imported `data` and an optional query; results are sorted by path.
40
+ *
41
+ * ```astro
42
+ * ---
43
+ * import data from "blume:data";
44
+ * import { getBlumeCollection } from "blume/runtime";
45
+ * const posts = getBlumeCollection(data, { prefix: "/blog" });
46
+ * ---
47
+ * <ul>{posts.map((p) => <li><a href={p.path}>{p.title}</a></li>)}</ul>
48
+ * ```
49
+ */
50
+ export const getBlumeCollection = (
51
+ data: BlumeData,
52
+ query: BlumeCollectionQuery = {}
53
+ ): BlumeRoute[] => {
54
+ const collection = query.collection ?? "docs";
55
+ return data.routes
56
+ .filter((route) => {
57
+ if (route.collection !== collection) {
58
+ return false;
59
+ }
60
+ if (query.locale && route.locale !== query.locale) {
61
+ return false;
62
+ }
63
+ if (query.prefix && !route.path.startsWith(query.prefix)) {
64
+ return false;
65
+ }
66
+ if (
67
+ !query.includeHidden &&
68
+ (route.draft || route.hidden || route.fallback)
69
+ ) {
70
+ return false;
71
+ }
72
+ return true;
73
+ })
74
+ .toSorted((a, b) => a.path.localeCompare(b.path));
75
+ };
@@ -0,0 +1,14 @@
1
+ // Ambient types for the Vite `import.meta.env` fields Blume's client islands read
2
+ // (`.astro` files aren't typechecked, so only real `.ts`/`.tsx` sources need this).
3
+ interface ImportMetaEnv {
4
+ /** Deployment base path, always with a trailing slash (e.g. `/` or `/docs/`). */
5
+ readonly BASE_URL: string;
6
+ readonly DEV: boolean;
7
+ readonly MODE: string;
8
+ readonly PROD: boolean;
9
+ readonly SSR: boolean;
10
+ }
11
+
12
+ interface ImportMeta {
13
+ readonly env: ImportMetaEnv;
14
+ }
@@ -1,12 +0,0 @@
1
- ---
2
- title: v0.1.0
3
- type: changelog
4
- date: 2026-06-01
5
- changelog:
6
- version: 0.1.0
7
- category: Release
8
- ---
9
-
10
- The first public release of Blume — a markdown-first docs framework on Astro and
11
- Vite. Drop in Markdown or MDX, run `blume dev`, and ship a production-grade docs
12
- site with search, theming, and AI-ready output.
@@ -1,16 +0,0 @@
1
- ---
2
- title: v0.2.0
3
- type: changelog
4
- date: 2026-06-24
5
- changelog:
6
- version: 0.2.0
7
- category: Features
8
- ---
9
-
10
- A big batch of built-in components landed: **columns**, **frames**, **trees**,
11
- **tooltips**, **code groups**, **panels**, **tiles**, and **fields**. Code groups
12
- now render as proper language tabs, and the changelog gets this timeline.
13
-
14
- - New `Accordion` / `AccordionItem`, `Expandable`, `Tooltip`, `Frame`, `Color`
15
- - `CodeGroup` tabs with flush code blocks
16
- - Redesigned `Prompt` with a copy-to-clipboard button