blume 0.5.4 → 0.6.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 (41) hide show
  1. package/dist/cli/index.js +380 -157
  2. package/dist/cli/index.js.map +23 -22
  3. package/dist/types/core/data.d.ts +4 -0
  4. package/dist/types/core/i18n-ui.d.ts +50 -0
  5. package/dist/types/core/schema.d.ts +328 -39
  6. package/dist/types/core/types.d.ts +8 -0
  7. package/docs/configuration/ai.mdx +56 -0
  8. package/docs/configuration/seo.mdx +59 -1
  9. package/docs/configuration/theming.mdx +14 -9
  10. package/docs/content/meta.mdx +3 -17
  11. package/docs/content/navigation.mdx +41 -4
  12. package/package.json +3 -1
  13. package/src/ai/agent-readability.ts +97 -0
  14. package/src/ai/ask-context.ts +131 -8
  15. package/src/ai/ask-data.ts +4 -1
  16. package/src/astro/generate.ts +4 -0
  17. package/src/astro/templates.ts +24 -5
  18. package/src/cli/commands/build.ts +15 -0
  19. package/src/cli/commands/dev.ts +31 -14
  20. package/src/cli/dev-lock.ts +94 -21
  21. package/src/components/content/GithubInfo.astro +11 -10
  22. package/src/components/content/TypeTable.astro +8 -3
  23. package/src/components/islands/AskAI.astro +66 -2
  24. package/src/components/islands/ask-ai.tsx +289 -53
  25. package/src/components/layout/Header.astro +1 -1
  26. package/src/components/layout/NavTree.astro +1 -1
  27. package/src/components/layout/PageActions.astro +73 -30
  28. package/src/components/layout/RootLayout.astro +48 -2
  29. package/src/core/data.ts +4 -0
  30. package/src/core/graph.ts +7 -2
  31. package/src/core/i18n-ui.ts +5 -0
  32. package/src/core/nav-diagnostics.ts +7 -0
  33. package/src/core/navigation.ts +38 -12
  34. package/src/core/schema.ts +124 -9
  35. package/src/core/sources/filesystem.ts +5 -1
  36. package/src/core/sources/watch.ts +43 -12
  37. package/src/core/types.ts +9 -0
  38. package/src/deploy/robots.ts +37 -4
  39. package/src/openapi/scalar.ts +1 -1
  40. package/src/search/documents.ts +9 -2
  41. package/src/theme/palette.ts +21 -14
@@ -114,10 +114,15 @@ const buildCrumbIndex = (sidebar: NavNode[]): Map<string, Crumbs> => {
114
114
  * Pass `includeWhenDisabled` to index pages on their content merits even when
115
115
  * the search provider is `none` — used by the MCP server, which is a separate
116
116
  * feature from on-page search.
117
+ *
118
+ * `content` selects the extraction: `"plain"` (default) strips Markdown to bare
119
+ * searchable text; `"markdown"` keeps the body's Markdown — code blocks, lists,
120
+ * headings — for Ask AI grounding, where fenced examples are often the answer
121
+ * and stripping them makes the model unable to cite content the docs do contain.
117
122
  */
118
123
  export const buildSearchDocuments = async (
119
124
  project: BlumeProject,
120
- options?: { includeWhenDisabled?: boolean }
125
+ options?: { includeWhenDisabled?: boolean; content?: "markdown" | "plain" }
121
126
  ): Promise<SearchDocument[]> => {
122
127
  const pageById = new Map(project.graph.pages.map((page) => [page.id, page]));
123
128
 
@@ -148,7 +153,9 @@ export const buildSearchDocuments = async (
148
153
  indexable.map(async (route) => {
149
154
  const page = pageById.get(route.id);
150
155
  const raw = page ? await readEntryText(project, page) : "";
151
- const body = raw ? toPlainText(matter(raw).content) : "";
156
+ const source = raw ? matter(raw).content : "";
157
+ const body =
158
+ options?.content === "markdown" ? source.trim() : toPlainText(source);
152
159
  const tags = page?.meta?.search?.tags;
153
160
  const crumb = crumbs.get(route.path);
154
161
  return {
@@ -67,10 +67,12 @@ const themeRootCss = (
67
67
  "--blume-action-foreground",
68
68
  options.action ? "oklch(1 0 0)" : null
69
69
  ),
70
- ...cssToken("--blume-background", safeColorOrNull(theme.background)),
70
+ ...cssToken("--blume-background", safeColorOrNull(theme.background?.light)),
71
71
  ...cssToken(
72
72
  "--blume-background-image",
73
- theme.backgroundImage ? backgroundImageCss(theme.backgroundImage) : null
73
+ theme.backgroundImage?.light
74
+ ? backgroundImageCss(theme.backgroundImage.light)
75
+ : null
74
76
  ),
75
77
  ` --blume-radius: ${options.radius};`,
76
78
  ]
@@ -96,11 +98,11 @@ const themeDarkCss = (
96
98
  "--blume-action-foreground",
97
99
  options.action ? "oklch(1 0 0)" : null
98
100
  ),
99
- ...cssToken("--blume-background", safeColorOrNull(theme.backgroundDark)),
101
+ ...cssToken("--blume-background", safeColorOrNull(theme.background?.dark)),
100
102
  ...cssToken(
101
103
  "--blume-background-image",
102
- theme.backgroundImageDark
103
- ? backgroundImageCss(theme.backgroundImageDark)
104
+ theme.backgroundImage?.dark
105
+ ? backgroundImageCss(theme.backgroundImage.dark)
104
106
  : null
105
107
  ),
106
108
  ].filter(Boolean);
@@ -111,12 +113,18 @@ ${tokens.join("\n")}
111
113
  };
112
114
 
113
115
  /**
114
- * Resolve the configured accent to a CSS color. A named accent resolves to its
115
- * preset; any other value is treated as a raw CSS color so users can pass
116
- * arbitrary colors without a config change.
116
+ * Resolve the configured accent to per-mode CSS colors. A named accent
117
+ * resolves to its preset; any other value is treated as a raw CSS color so
118
+ * users can pass arbitrary colors without a config change. A string accent
119
+ * has already been normalized by the config schema to the same color for
120
+ * both modes.
117
121
  */
118
- export const resolveAccent = (theme: ResolvedConfig["theme"]): string =>
119
- presetOrColor(theme.accent);
122
+ export const resolveAccent = (
123
+ theme: ResolvedConfig["theme"]
124
+ ): { dark: string; light: string } => ({
125
+ dark: presetOrColor(theme.accent.dark),
126
+ light: presetOrColor(theme.accent.light),
127
+ });
120
128
 
121
129
  /** Resolve the configured radius preset to a CSS length. */
122
130
  export const resolveRadius = (theme: ResolvedConfig["theme"]): string =>
@@ -128,17 +136,16 @@ export const resolveRadius = (theme: ResolvedConfig["theme"]): string =>
128
136
  * arbitrary colors without a config change.
129
137
  */
130
138
  export const buildThemeCss = (theme: ResolvedConfig["theme"]): string => {
131
- const accent = presetOrColor(theme.accent);
132
- const accentDark = theme.accentDark ? presetOrColor(theme.accentDark) : null;
139
+ const accent = resolveAccent(theme);
133
140
  const action = theme.action ? presetOrColor(theme.action) : null;
134
141
  const radius = RADII[theme.radius];
135
142
  const root = themeRootCss(theme, {
136
- accent,
143
+ accent: accent.light,
137
144
  action,
138
145
  radius,
139
146
  });
140
147
  const dark = themeDarkCss(theme, {
141
- accent: accentDark ?? accent,
148
+ accent: accent.dark,
142
149
  action,
143
150
  });
144
151