blume 1.6.5 → 1.7.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 (179) hide show
  1. package/CHANGELOG.md +39 -0
  2. package/bin/blume.mjs +3 -2
  3. package/dist/cli/chunk-0qhq7b8q.js +111 -0
  4. package/dist/cli/chunk-0qhq7b8q.js.map +11 -0
  5. package/dist/cli/chunk-18tjv4f7.js +96 -0
  6. package/dist/cli/chunk-18tjv4f7.js.map +10 -0
  7. package/dist/cli/chunk-27gtm2ym.js +69 -0
  8. package/dist/cli/chunk-27gtm2ym.js.map +11 -0
  9. package/dist/cli/chunk-2aj8ddew.js +72 -0
  10. package/dist/cli/chunk-2aj8ddew.js.map +10 -0
  11. package/dist/cli/chunk-3r94j3tc.js +221 -0
  12. package/dist/cli/chunk-3r94j3tc.js.map +10 -0
  13. package/dist/cli/chunk-4trphnvy.js +102 -0
  14. package/dist/cli/chunk-4trphnvy.js.map +11 -0
  15. package/dist/cli/chunk-4xyggvgf.js +21 -0
  16. package/dist/cli/chunk-4xyggvgf.js.map +10 -0
  17. package/dist/cli/chunk-5d4q7121.js +4064 -0
  18. package/dist/cli/chunk-5d4q7121.js.map +40 -0
  19. package/dist/cli/chunk-5hs6gb7n.js +32 -0
  20. package/dist/cli/chunk-5hs6gb7n.js.map +10 -0
  21. package/dist/cli/chunk-6kzzpsx8.js +26 -0
  22. package/dist/cli/chunk-6kzzpsx8.js.map +10 -0
  23. package/dist/cli/chunk-8gnpdsn1.js +952 -0
  24. package/dist/cli/chunk-8gnpdsn1.js.map +12 -0
  25. package/dist/cli/chunk-9qs6acpw.js +176 -0
  26. package/dist/cli/chunk-9qs6acpw.js.map +10 -0
  27. package/dist/cli/chunk-agy5rzxy.js +2453 -0
  28. package/dist/cli/chunk-agy5rzxy.js.map +15 -0
  29. package/dist/cli/chunk-bcy492zc.js +16 -0
  30. package/dist/cli/chunk-bcy492zc.js.map +10 -0
  31. package/dist/cli/chunk-btfr9yvw.js +41 -0
  32. package/dist/cli/chunk-btfr9yvw.js.map +10 -0
  33. package/dist/cli/chunk-cbjnx4s8.js +73 -0
  34. package/dist/cli/chunk-cbjnx4s8.js.map +10 -0
  35. package/dist/cli/chunk-cfw6x4rm.js +1967 -0
  36. package/dist/cli/chunk-cfw6x4rm.js.map +34 -0
  37. package/dist/cli/chunk-ckh3a410.js +277 -0
  38. package/dist/cli/chunk-ckh3a410.js.map +11 -0
  39. package/dist/cli/chunk-drke6t0h.js +259 -0
  40. package/dist/cli/chunk-drke6t0h.js.map +11 -0
  41. package/dist/cli/chunk-ev67ycx0.js +15 -0
  42. package/dist/cli/chunk-ev67ycx0.js.map +10 -0
  43. package/dist/cli/chunk-ey89bjj1.js +209 -0
  44. package/dist/cli/chunk-ey89bjj1.js.map +11 -0
  45. package/dist/cli/chunk-j6pxe0dt.js +69 -0
  46. package/dist/cli/chunk-j6pxe0dt.js.map +11 -0
  47. package/dist/cli/chunk-jk1zwka1.js +387 -0
  48. package/dist/cli/chunk-jk1zwka1.js.map +12 -0
  49. package/dist/cli/chunk-jtb45atp.js +467 -0
  50. package/dist/cli/chunk-jtb45atp.js.map +14 -0
  51. package/dist/cli/chunk-jxkxjsc1.js +76 -0
  52. package/dist/cli/chunk-jxkxjsc1.js.map +10 -0
  53. package/dist/cli/chunk-kwx90v78.js +81 -0
  54. package/dist/cli/chunk-kwx90v78.js.map +10 -0
  55. package/dist/cli/chunk-n0nyat6g.js +30 -0
  56. package/dist/cli/chunk-n0nyat6g.js.map +10 -0
  57. package/dist/cli/chunk-pxj10x8y.js +35 -0
  58. package/dist/cli/chunk-pxj10x8y.js.map +10 -0
  59. package/dist/cli/chunk-qq9nm3qd.js +1141 -0
  60. package/dist/cli/chunk-qq9nm3qd.js.map +19 -0
  61. package/dist/cli/chunk-s102bysw.js +5170 -0
  62. package/dist/cli/chunk-s102bysw.js.map +47 -0
  63. package/dist/cli/chunk-s5dsk8bj.js +769 -0
  64. package/dist/cli/chunk-s5dsk8bj.js.map +13 -0
  65. package/dist/cli/chunk-s5e5jt53.js +227 -0
  66. package/dist/cli/chunk-s5e5jt53.js.map +11 -0
  67. package/dist/cli/chunk-sbdqrjbb.js +81 -0
  68. package/dist/cli/chunk-sbdqrjbb.js.map +10 -0
  69. package/dist/cli/chunk-tnskyrej.js +117 -0
  70. package/dist/cli/chunk-tnskyrej.js.map +10 -0
  71. package/dist/cli/chunk-v2ymm99c.js +1016 -0
  72. package/dist/cli/chunk-v2ymm99c.js.map +13 -0
  73. package/dist/cli/chunk-v5mm027v.js +185 -0
  74. package/dist/cli/chunk-v5mm027v.js.map +11 -0
  75. package/dist/cli/chunk-vt8fgygt.js +23 -0
  76. package/dist/cli/chunk-vt8fgygt.js.map +10 -0
  77. package/dist/cli/chunk-vxv4x1n8.js +17 -0
  78. package/dist/cli/chunk-vxv4x1n8.js.map +10 -0
  79. package/dist/cli/chunk-wd27zjcz.js +60 -0
  80. package/dist/cli/chunk-wd27zjcz.js.map +10 -0
  81. package/dist/cli/chunk-x66c5yjn.js +23 -0
  82. package/dist/cli/chunk-x66c5yjn.js.map +10 -0
  83. package/dist/cli/chunk-xv91q4nm.js +5314 -0
  84. package/dist/cli/chunk-xv91q4nm.js.map +58 -0
  85. package/dist/cli/chunk-y3g15rvv.js +679 -0
  86. package/dist/cli/chunk-y3g15rvv.js.map +15 -0
  87. package/dist/cli/chunk-ye9zdkgv.js +136 -0
  88. package/dist/cli/chunk-ye9zdkgv.js.map +10 -0
  89. package/dist/cli/chunk-ynacq3ev.js +1062 -0
  90. package/dist/cli/chunk-ynacq3ev.js.map +25 -0
  91. package/dist/cli/chunk-zr3ygrq3.js +54 -0
  92. package/dist/cli/chunk-zr3ygrq3.js.map +10 -0
  93. package/dist/cli/index.js +55 -27597
  94. package/dist/cli/index.js.map +5 -243
  95. package/dist/types/ai/ask-context.d.ts +26 -0
  96. package/dist/types/components/layout/nav-utils.d.ts +33 -1
  97. package/dist/types/core/code-fences.d.ts +11 -0
  98. package/dist/types/core/package-root.d.ts +1 -1
  99. package/dist/types/core/schema.d.ts +70 -0
  100. package/dist/types/theme/fonts.d.ts +22 -22
  101. package/docs/02-deployment.mdx +22 -1
  102. package/docs/configuration/ask-ai.mdx +1 -1
  103. package/docs/configuration/customization.mdx +2 -9
  104. package/docs/content/navigation.mdx +2 -0
  105. package/docs/content/syntax.mdx +1 -1
  106. package/docs/discoverability/open-graph.mdx +4 -0
  107. package/docs/reference/cli.mdx +1 -1
  108. package/package.json +4 -2
  109. package/src/ai/api/handlers.ts +4 -7
  110. package/src/ai/api/paths.ts +8 -0
  111. package/src/ai/api/spec.ts +2 -1
  112. package/src/ai/ask-context.ts +378 -22
  113. package/src/astro/generate.ts +161 -28
  114. package/src/astro/include-hmr.ts +10 -13
  115. package/src/astro/include-refresh.ts +0 -0
  116. package/src/astro/index.ts +6 -1
  117. package/src/astro/integration.ts +280 -53
  118. package/src/astro/module-types.ts +83 -0
  119. package/src/astro/templates.ts +256 -108
  120. package/src/audit/image-size.ts +10 -8
  121. package/src/cli/command-meta.ts +77 -0
  122. package/src/cli/commands/add.ts +2 -4
  123. package/src/cli/commands/audit.ts +2 -4
  124. package/src/cli/commands/build.ts +70 -346
  125. package/src/cli/commands/check.ts +2 -4
  126. package/src/cli/commands/dev.ts +31 -42
  127. package/src/cli/commands/doctor.ts +2 -4
  128. package/src/cli/commands/eject.ts +3 -41
  129. package/src/cli/commands/eval.ts +2 -5
  130. package/src/cli/commands/init.ts +2 -4
  131. package/src/cli/commands/mcp-stdio.ts +2 -5
  132. package/src/cli/commands/preview.ts +3 -5
  133. package/src/cli/commands/sync.ts +2 -4
  134. package/src/cli/commands/translate.ts +2 -5
  135. package/src/cli/commands/validate.ts +2 -4
  136. package/src/cli/commands/version.ts +2 -4
  137. package/src/cli/eject-scripts.ts +0 -45
  138. package/src/cli/host-args.ts +16 -0
  139. package/src/cli/index.ts +84 -35
  140. package/src/cli/lazy-command.ts +47 -0
  141. package/src/components/Icon.astro +24 -0
  142. package/src/components/content/GithubInfo.astro +4 -1
  143. package/src/components/icon-sprite-middleware.ts +41 -0
  144. package/src/components/icon-sprite.ts +93 -0
  145. package/src/components/layout/IconSprite.astro +11 -0
  146. package/src/components/layout/NavTree.astro +156 -188
  147. package/src/components/layout/NavTreeCache.astro +45 -0
  148. package/src/components/layout/NavTreeScript.astro +256 -0
  149. package/src/components/layout/PageActions.astro +11 -5
  150. package/src/components/layout/PageLayout.astro +21 -3
  151. package/src/components/layout/ReferenceLayout.astro +21 -4
  152. package/src/components/layout/RootLayout.astro +44 -6
  153. package/src/components/layout/nav-cache.ts +49 -0
  154. package/src/components/layout/nav-utils.ts +69 -1
  155. package/src/components/layout/page-locale.ts +29 -0
  156. package/src/core/api-name.ts +18 -0
  157. package/src/core/code-fences.ts +48 -0
  158. package/src/core/content-assets.ts +3 -7
  159. package/src/core/includes.ts +3 -7
  160. package/src/core/package-root.ts +1 -1
  161. package/src/core/schema.ts +19 -0
  162. package/src/core/sources/normalize.ts +2 -37
  163. package/src/core/sources/obsidian.ts +3 -2
  164. package/src/core/svg-dimensions.ts +97 -0
  165. package/src/core/version-cut.ts +2 -2
  166. package/src/deploy/artifacts.ts +370 -0
  167. package/src/deploy/cloudflare-negotiation.ts +97 -32
  168. package/src/deploy/function-bundle.ts +66 -20
  169. package/src/deploy/sitemap.ts +6 -0
  170. package/src/deploy/vercel-negotiation.ts +8 -30
  171. package/src/markdown/language-icon.ts +64 -20
  172. package/src/markdown/mermaid.ts +11 -0
  173. package/src/og/cache.ts +236 -0
  174. package/src/og/card.ts +18 -16
  175. package/src/og/index.ts +8 -1
  176. package/src/openapi/render-mdx.ts +9 -5
  177. package/src/registry/eject.ts +23 -10
  178. package/src/theme/entry.ts +41 -7
  179. package/src/theme/fonts.ts +30 -23
@@ -1,6 +1,7 @@
1
1
  ---
2
2
  import { isImageIcon, isInlineSvg } from "../theme/icon-kind.ts";
3
3
  import { resolveIcon } from "../theme/icons.ts";
4
+ import { iconSpriteFor, registerIconSymbol } from "./icon-sprite.ts";
4
5
  import { withBase } from "./islands/base-path.ts";
5
6
 
6
7
  interface Props {
@@ -32,6 +33,17 @@ const rawSvg = iconName && isInlineSvg(iconName) ? iconName.trim() : null;
32
33
  const imageSrc = iconName && !rawSvg && isImageIcon(iconName) ? iconName : null;
33
34
  const resolvedIcon =
34
35
  iconName && !(imageSrc || rawSvg) ? resolveIcon(iconName) : null;
36
+ // A set icon joins the page's sprite when the shell keeps one (see
37
+ // icon-sprite.ts): the paths render once, in the sprite, and this use is a
38
+ // <use> reference. Without a sprite the icon inlines its paths as before.
39
+ const sprite = resolvedIcon ? iconSpriteFor(Astro.locals) : undefined;
40
+ const symbolId =
41
+ sprite && resolvedIcon
42
+ ? registerIconSymbol(sprite, resolvedIcon.name, {
43
+ body: resolvedIcon.body,
44
+ viewBox: resolvedIcon.viewBox,
45
+ })
46
+ : null;
35
47
  const resolvedClass = astroClass ?? className;
36
48
  const customStyle = `display:inline-flex;width:${size}px;height:${size}px;${
37
49
  color ? `color:${color}` : ""
@@ -68,6 +80,18 @@ const style = color ? `color:${color}` : undefined;
68
80
  src={withBase(imageSrc)}
69
81
  width={size}
70
82
  />
83
+ ) : symbolId ? (
84
+ <svg
85
+ aria-hidden={label ? undefined : "true"}
86
+ aria-label={label}
87
+ class={resolvedClass}
88
+ height={size}
89
+ role={label ? "img" : undefined}
90
+ style={style}
91
+ width={size}
92
+ >
93
+ <use href={`#${symbolId}`} />
94
+ </svg>
71
95
  ) : (
72
96
  resolvedIcon && (
73
97
  <svg
@@ -3,6 +3,7 @@
3
3
  // at build time (no client JS). Pass `owner`/`repo`, or omit them to use the
4
4
  // repo from blume.config. A `GITHUB_TOKEN` env var lifts the API rate limit.
5
5
  // If the API is unreachable the card still renders, just without counts.
6
+ import { getSecret } from "astro:env/server";
6
7
  import { apiUrl, PUBLIC_HOST_URL } from "../../core/github.ts";
7
8
  import { resolveIcon } from "../../theme/icons.ts";
8
9
  import data from "blume:data";
@@ -38,7 +39,9 @@ interface Props {
38
39
  const configured = data.config.github;
39
40
  const owner = Astro.props.owner ?? configured?.owner;
40
41
  const repo = Astro.props.repo ?? configured?.repo;
41
- const token = Astro.props.token ?? process.env.GITHUB_TOKEN;
42
+ // Read through Astro's `getSecret` so the adapter's secret store is honored
43
+ // (the card renders at build time, where that is the environment).
44
+ const token = Astro.props.token ?? getSecret("GITHUB_TOKEN");
42
45
  // A `host` prop is reduced to its origin, as the config field is, so a
43
46
  // trailing slash or a path never lands mid-link.
44
47
  const host = Astro.props.host
@@ -0,0 +1,41 @@
1
+ import type { MiddlewareHandler } from "astro";
2
+
3
+ import { iconSpriteFor, renderIconSprite } from "./icon-sprite.ts";
4
+ import type { IconSprite } from "./icon-sprite.ts";
5
+
6
+ /**
7
+ * The placeholder a shell renders where its icon sprite goes (the end of the
8
+ * body); {@link onRequest} replaces it once the page has fully rendered.
9
+ * Rendering the sprite from a component instead wouldn't work: Astro walks a
10
+ * template's own markup before its nested components render, so a sprite
11
+ * component at the end of the body runs before the header's search button or
12
+ * the page content's callouts have registered their icons.
13
+ */
14
+ export const ICON_SPRITE_SLOT = "<!--blume-icon-sprite-->";
15
+
16
+ /** The page HTML with its placeholder replaced by the finished sprite. */
17
+ export const spliceIconSprite = (html: string, sprite?: IconSprite): string =>
18
+ html.includes(ICON_SPRITE_SLOT)
19
+ ? html.replace(ICON_SPRITE_SLOT, sprite ? renderIconSprite(sprite) : "")
20
+ : html;
21
+
22
+ /**
23
+ * Blume's icon-sprite middleware (wired by the integration). Draining the
24
+ * response renders the whole page, after which the request's registry holds
25
+ * every icon it used; the sprite is spliced in where the shell left the slot.
26
+ * Requests whose shell kept no registry — a user layout override, a partial,
27
+ * an endpoint — pass through untouched, streaming and all.
28
+ */
29
+ export const onRequest: MiddlewareHandler = async (context, render) => {
30
+ const response = await render();
31
+ const sprite = iconSpriteFor(context.locals);
32
+ if (!sprite) {
33
+ return response;
34
+ }
35
+ const html = await response.text();
36
+ return new Response(spliceIconSprite(html, sprite), {
37
+ headers: response.headers,
38
+ status: response.status,
39
+ statusText: response.statusText,
40
+ });
41
+ };
@@ -0,0 +1,93 @@
1
+ /**
2
+ * A per-page SVG sprite for the icon sets. A page renders the same handful of
3
+ * icons many times over (every sidebar chevron, every code-block language
4
+ * icon, the page-action buttons); inlining each one repeats its paths on every
5
+ * use, which on a docs page is ~20 kB of the ~60 kB that isn't the sidebar.
6
+ * With the sprite, each use is a `<use href="#…">` reference and the paths
7
+ * appear once, in a hidden `<svg>` the layout renders at the end of the body.
8
+ *
9
+ * The registry lives on the request's `Astro.locals`, so every component in
10
+ * one page render — the layout, the MDX content, cached sidebar subtrees —
11
+ * shares it, and concurrent page renders never see each other's icons. A
12
+ * shell that creates no registry (a user's layout override, a partial) gets
13
+ * inline icons, exactly as before.
14
+ */
15
+
16
+ /** One symbol in the sprite: an icon set's inner markup and its viewBox. */
17
+ export interface IconSymbol {
18
+ body: string;
19
+ viewBox: string;
20
+ }
21
+
22
+ export interface IconSprite {
23
+ /** Symbol id → symbol, in first-use order. */
24
+ symbols: Map<string, IconSymbol>;
25
+ }
26
+
27
+ /** The request locals (`Astro.locals`) slot the registry lives in. */
28
+ export interface IconSpriteLocals {
29
+ blumeIconSprite?: IconSprite;
30
+ }
31
+
32
+ /** The request's sprite registry, when a shell created one. */
33
+ export const iconSpriteFor = (
34
+ locals: IconSpriteLocals
35
+ ): IconSprite | undefined => locals.blumeIconSprite;
36
+
37
+ /**
38
+ * The request's sprite registry, created on first call: a shell calls this
39
+ * up front. Idempotent, so a shell rendered inside another shell's request
40
+ * joins the existing registry instead of discarding its icons.
41
+ */
42
+ export const createIconSprite = (locals: IconSpriteLocals): IconSprite => {
43
+ locals.blumeIconSprite ??= { symbols: new Map() };
44
+ return locals.blumeIconSprite;
45
+ };
46
+
47
+ /**
48
+ * The sprite symbol id for an icon name. Names come from config and content
49
+ * (`lucide:arrow-right`, `simple-icons:github`), so anything outside the id
50
+ * charset is folded to a dash.
51
+ */
52
+ export const iconSymbolId = (name: string): string =>
53
+ `blume-i-${name.toLowerCase().replaceAll(/[^a-z0-9-]+/gu, "-")}`;
54
+
55
+ /** Register a symbol (idempotent) and return its id. */
56
+ export const registerIconSymbol = (
57
+ sprite: IconSprite,
58
+ name: string,
59
+ symbol: IconSymbol
60
+ ): string => {
61
+ const id = iconSymbolId(name);
62
+ if (!sprite.symbols.has(id)) {
63
+ sprite.symbols.set(id, symbol);
64
+ }
65
+ return id;
66
+ };
67
+
68
+ /** The ids of every sprite symbol a rendered HTML fragment references. */
69
+ export const referencedIconSymbols = (html: string): string[] => [
70
+ ...new Set(
71
+ [...html.matchAll(/href="#(?<id>blume-i-[^"]+)"/gu)].map(
72
+ (match) => match.groups?.id ?? ""
73
+ )
74
+ ),
75
+ ];
76
+
77
+ /**
78
+ * The hidden sprite `<svg>` holding every registered symbol, or an empty
79
+ * string when the page used no set icon. Symbol bodies are the icon sets'
80
+ * own markup (already SVG); the viewBox is the only attribute interpolated.
81
+ */
82
+ export const renderIconSprite = (sprite: IconSprite): string => {
83
+ if (sprite.symbols.size === 0) {
84
+ return "";
85
+ }
86
+ const symbols = [...sprite.symbols]
87
+ .map(
88
+ ([id, symbol]) =>
89
+ `<symbol id="${id}" viewBox="${symbol.viewBox}">${symbol.body}</symbol>`
90
+ )
91
+ .join("");
92
+ return `<svg aria-hidden="true" hidden xmlns="http://www.w3.org/2000/svg">${symbols}</svg>`;
93
+ };
@@ -0,0 +1,11 @@
1
+ ---
2
+ // Where the page's icon sprite goes: every set icon the render registered
3
+ // (see icon-sprite.ts), as one hidden <svg> of <symbol>s the icons' <use>
4
+ // references resolve against. This only leaves a placeholder at the end of
5
+ // the body; the icon-sprite middleware replaces it once the page has fully
6
+ // rendered, since a component here would run before nested components
7
+ // (the header's search button, the content's callouts) register their icons.
8
+ import { ICON_SPRITE_SLOT } from "../icon-sprite-middleware.ts";
9
+ ---
10
+
11
+ <Fragment set:html={ICON_SPRITE_SLOT} />