@tenphi/starlight 0.5.0 → 0.6.1

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 (38) hide show
  1. package/dist/components/Card.astro +5 -3
  2. package/dist/components/GlobalStyles.js +761 -0
  3. package/dist/components/LayoutComponents.js +404 -0
  4. package/dist/components/Logo.astro +39 -0
  5. package/dist/components/MobileNavigationTabs.astro +7 -2
  6. package/dist/components/PackageVersion.astro +22 -0
  7. package/dist/components/Preview.astro +21 -10
  8. package/dist/components/Steps.astro +5 -1
  9. package/dist/components/Tabs.astro +9 -2
  10. package/dist/components/TastyComponents.js +156 -0
  11. package/dist/components/component-styles.test.ts +75 -0
  12. package/dist/components/component-styles.ts +79 -0
  13. package/dist/components/customize-component.js +10 -0
  14. package/dist/components/svg-icon.test.ts +11 -0
  15. package/dist/components/svg-icon.ts +11 -0
  16. package/dist/components/tasty-states.d.ts +2 -0
  17. package/dist/components/tasty-states.js +21 -0
  18. package/dist/components-public.d.ts +1 -0
  19. package/dist/components.d.ts +2 -1
  20. package/dist/components.js +2 -1
  21. package/dist/index.d.ts +2 -1
  22. package/dist/index.d.ts.map +1 -1
  23. package/dist/index.js +297 -163
  24. package/dist/index.js.map +1 -1
  25. package/dist/{navigation-CC0dlAL8.js → navigation-CkQXI2Zb.js} +41 -2
  26. package/dist/navigation-CkQXI2Zb.js.map +1 -0
  27. package/dist/navigation.d.ts +11 -2
  28. package/dist/navigation.d.ts.map +1 -1
  29. package/dist/navigation.js +2 -2
  30. package/dist/overrides/Header.astro +28 -5
  31. package/dist/overrides/MobileMenuFooter.astro +15 -0
  32. package/dist/overrides/ThemeSelect.astro +69 -0
  33. package/dist/routes/DocsPage.astro +43 -145
  34. package/package.json +5 -9
  35. package/dist/components/NavigationTree.astro +0 -98
  36. package/dist/navigation-CC0dlAL8.js.map +0 -1
  37. package/dist/styles.css +0 -1346
  38. package/dist/styles.d.ts +0 -1
package/dist/index.js CHANGED
@@ -1,14 +1,15 @@
1
1
  import { n as starlight, t as createStarlightCollection } from "./content-Cwk8rm6i.js";
2
- import { a as resolveNavigationLayout } from "./navigation-CC0dlAL8.js";
2
+ import { a as resolveNavigationLayout } from "./navigation-CkQXI2Zb.js";
3
3
  import { createRequire } from "node:module";
4
4
  import { existsSync } from "node:fs";
5
5
  import { cp, mkdir, readFile } from "node:fs/promises";
6
6
  import { dirname, extname, join } from "node:path";
7
- import { fileURLToPath } from "node:url";
8
- import { assertValidDocs, createDocsGraph } from "@tenphi/docs";
7
+ import { fileURLToPath, pathToFileURL } from "node:url";
8
+ import { COOKBOOK_COMPONENT_NAMES, assertValidDocs, createDocsGraph } from "@tenphi/docs";
9
9
  import { configure } from "@tenphi/tasty/core";
10
10
  import { tastyIntegration } from "@tenphi/tasty/ssr/astro";
11
11
  import { apcaContrast, glaze, okhslToLinearSrgb, relativeLuminanceFromLinearRgb, variantToOkhsl } from "@tenphi/glaze";
12
+ import "@tenphi/tasty";
12
13
  //#region src/theme/defaults.ts
13
14
  const DEFAULT_THEME_TOKENS = {
14
15
  $gap: "0.5rem",
@@ -27,40 +28,48 @@ const DEFAULT_TYPOGRAPHY_PRESETS = {
27
28
  body: {
28
29
  fontFamily: BODY_FONT,
29
30
  fontSize: "1rem",
30
- lineHeight: 1.75,
31
- letterSpacing: "0",
32
- fontWeight: 400,
33
- boldFontWeight: 650
31
+ lineHeight: 1.65,
32
+ letterSpacing: "-0.006em",
33
+ fontWeight: 420,
34
+ boldFontWeight: 640
34
35
  },
35
36
  heading: {
36
37
  fontFamily: BODY_FONT,
37
38
  fontSize: "1rem",
38
- lineHeight: 1.2,
39
- letterSpacing: "-0.018em",
40
- fontWeight: 650,
41
- boldFontWeight: 750
39
+ lineHeight: 1.15,
40
+ letterSpacing: "-0.025em",
41
+ fontWeight: 640,
42
+ boldFontWeight: 720
43
+ },
44
+ h1: heading("clamp(2.5rem, 5vw, 3.25rem)", 1.05, "-0.045em"),
45
+ h2: heading("clamp(1.75rem, 3vw, 2.125rem)", 1.1, "-0.035em"),
46
+ h3: heading("1.5rem", 1.15, "-0.025em"),
47
+ h4: heading("1.25rem", 1.2, "-0.018em"),
48
+ h5: heading("1.125rem", 1.25, "-0.012em"),
49
+ h6: heading("1rem", 1.3, "-0.006em"),
50
+ navigation: {
51
+ fontFamily: "var(--body-font-family)",
52
+ fontSize: "0.9375rem",
53
+ lineHeight: 1.4,
54
+ letterSpacing: "-0.006em",
55
+ fontWeight: 540,
56
+ boldFontWeight: 650
42
57
  },
43
- h1: heading("3rem", 1.08, "-0.035em"),
44
- h2: heading("2rem", 1.15, "-0.025em"),
45
- h3: heading("1.5rem", 1.2, "-0.015em"),
46
- h4: heading("1.25rem", 1.25, "-0.01em"),
47
- h5: heading("1.125rem", 1.3, "0"),
48
- h6: heading("1rem", 1.35, "0"),
49
58
  small: {
50
59
  fontFamily: "var(--body-font-family)",
51
60
  fontSize: "0.875rem",
52
- lineHeight: 1.5,
53
- letterSpacing: "0",
54
- fontWeight: 400,
55
- boldFontWeight: 700
61
+ lineHeight: 1.45,
62
+ letterSpacing: "-0.002em",
63
+ fontWeight: 420,
64
+ boldFontWeight: 650
56
65
  },
57
66
  code: {
58
67
  fontFamily: "'JetBrains Mono Variable', 'JetBrains Mono', ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace",
59
68
  fontSize: "0.875rem",
60
- lineHeight: 1.6,
69
+ lineHeight: 1.65,
61
70
  letterSpacing: "0",
62
71
  fontWeight: 400,
63
- boldFontWeight: 700
72
+ boldFontWeight: 650
64
73
  }
65
74
  };
66
75
  function resolveThemeTokens(tokens = {}) {
@@ -108,16 +117,18 @@ function resolveDocsTheme(theme = {}) {
108
117
  const surface2 = glaze.color({
109
118
  from: surfaceFrom,
110
119
  base: surface,
111
- tone: ["-2", "-4"],
120
+ tone: "-2",
112
121
  mode: "auto",
113
- darkSaturation: .35
122
+ saturationFactor: .9,
123
+ darkSaturation: .325
114
124
  }, glazeOptions);
115
125
  const surface3 = glaze.color({
116
126
  from: surfaceFrom,
117
- base: surface,
118
- tone: ["-4", "-8"],
127
+ base: surface2,
128
+ tone: "-2",
119
129
  mode: "auto",
120
- darkSaturation: .35
130
+ saturationFactor: .8,
131
+ darkSaturation: .3
121
132
  }, glazeOptions);
122
133
  const text = glaze.color({
123
134
  from: theme.palette?.text ?? "#20232a",
@@ -160,6 +171,33 @@ function resolveDocsTheme(theme = {}) {
160
171
  });
161
172
  const resolvedSurface = surface.resolve();
162
173
  const resolvedAccent = accentText.resolve();
174
+ const shadowTheme = glaze({
175
+ hue: variantToOkhsl(resolvedSurface.light).h,
176
+ saturation: variantToOkhsl(resolvedSurface.light).s * 100,
177
+ darkHue: variantToOkhsl(resolvedSurface.dark).h,
178
+ darkSaturation: variantToOkhsl(resolvedSurface.dark).s * 100
179
+ }, void 0, glazeOptions);
180
+ shadowTheme.colors({
181
+ surface: {
182
+ from: surfaceFrom,
183
+ mode: "auto",
184
+ darkSaturation: .35
185
+ },
186
+ text: {
187
+ from: theme.palette?.text ?? "#20232a",
188
+ base: "surface",
189
+ role: "text",
190
+ contrast: { apca: [75, 90] },
191
+ mode: "auto"
192
+ },
193
+ shadow: {
194
+ type: "shadow",
195
+ bg: "surface",
196
+ fg: "text",
197
+ intensity: [12, 20],
198
+ tuning: { alphaMax: .28 }
199
+ }
200
+ });
163
201
  const scores = {
164
202
  light: score(resolvedAccent.light, resolvedSurface.light),
165
203
  dark: score(resolvedAccent.dark, resolvedSurface.dark),
@@ -177,20 +215,21 @@ function resolveDocsTheme(theme = {}) {
177
215
  });
178
216
  }
179
217
  const outputOptions = { modes: { highContrast: true } };
180
- const colors = {
181
- surface: surface.json(outputOptions),
182
- surface2: surface2.json(outputOptions),
183
- surface3: surface3.json(outputOptions),
184
- text: text.json(outputOptions),
185
- textSoft: textSoft.json(outputOptions),
186
- accentText: accentText.json(outputOptions),
187
- accentSurface: accentSurface.json(outputOptions),
188
- accentSurfaceText: accentSurfaceText.json(outputOptions),
189
- focus: focus.json(outputOptions)
190
- };
218
+ const shadow = shadowTheme.json(outputOptions).shadow;
219
+ if (!shadow) throw new Error("The Glaze shadow token failed to resolve.");
191
220
  return {
192
- css: themeCss(colors, theme),
193
- colors,
221
+ colors: {
222
+ surface: surface.json(outputOptions),
223
+ surface2: surface2.json(outputOptions),
224
+ surface3: surface3.json(outputOptions),
225
+ text: text.json(outputOptions),
226
+ textSoft: textSoft.json(outputOptions),
227
+ accentText: accentText.json(outputOptions),
228
+ accentSurface: accentSurface.json(outputOptions),
229
+ accentSurfaceText: accentSurfaceText.json(outputOptions),
230
+ focus: focus.json(outputOptions),
231
+ shadow
232
+ },
194
233
  tokens: resolveThemeTokens(theme.tokens),
195
234
  presets: resolveTypographyPresets(theme.presets),
196
235
  contrast: scores,
@@ -208,75 +247,110 @@ function luminance(variant) {
208
247
  const { h, s, l } = variantToOkhsl(variant);
209
248
  return relativeLuminanceFromLinearRgb(okhslToLinearSrgb(h, s, l, variant.pastel));
210
249
  }
211
- function themeCss(colors, theme) {
212
- const tokens = resolveThemeTokens(theme.tokens);
213
- const presets = resolveTypographyPresets(theme.presets);
214
- const designDeclarations = [...Object.entries(tokens).map(([name, value]) => `${tokenProperty(name)}:${String(value)}`), ...Object.entries(presets).flatMap(([name, preset]) => Object.entries(preset).map(([property, value]) => `--${kebab(name)}-${kebab(property)}:${String(value)}`))].join(";");
215
- const declarations = (mode) => [
216
- `--td-surface:${colors.surface[mode]}`,
217
- `--td-surface-2:${colors.surface2[mode]}`,
218
- `--td-surface-3:${colors.surface3[mode]}`,
219
- `--td-text:${colors.text[mode]}`,
220
- `--td-text-soft:${colors.textSoft[mode]}`,
221
- `--td-accent-text:${colors.accentText[mode]}`,
222
- `--td-accent-surface:${colors.accentSurface[mode]}`,
223
- `--td-accent-surface-text:${colors.accentSurfaceText[mode]}`,
224
- `--td-focus:${colors.focus[mode]}`,
225
- "--td-surface-2-hover:color-mix(in oklab,var(--td-text) 3%,var(--td-surface-2))",
226
- "--td-surface-2-pressed:color-mix(in oklab,var(--td-text) 9%,var(--td-surface-2))",
227
- "--td-surface-3-hover:color-mix(in oklab,var(--td-text) 3%,var(--td-surface-3))",
228
- "--td-surface-3-pressed:color-mix(in oklab,var(--td-text) 9%,var(--td-surface-3))",
229
- "--td-border:color-mix(in oklab,var(--td-text) 18%,var(--td-surface))",
230
- "--td-border-strong:color-mix(in oklab,var(--td-text) 34%,var(--td-surface))",
231
- "--td-shadow:color-mix(in oklab,var(--td-text) 16%,transparent)",
232
- "--td-overlay:color-mix(in oklab,var(--td-text) 58%,transparent)"
233
- ].join(";");
234
- return [
235
- `:root{${designDeclarations};${declarations("dark")}}`,
236
- `:root[data-theme=light]{${declarations("light")}}`,
237
- `@media(prefers-color-scheme:light){:root:not([data-theme]){${declarations("light")}}}`,
238
- `@media(prefers-contrast:more){:root{${declarations("darkContrast")}}:root[data-theme=light]{${declarations("lightContrast")}}@media(prefers-color-scheme:light){:root:not([data-theme]){${declarations("lightContrast")}}}}`,
239
- `:root[data-contrast=more]{${declarations("darkContrast")}}`,
240
- `:root[data-theme=light][data-contrast=more]{${declarations("lightContrast")}}`
241
- ].join("\n");
250
+ //#endregion
251
+ //#region src/theme/tasty-config.ts
252
+ const TASTY_UNITS = {
253
+ x: "var(--gap)",
254
+ r: "var(--radius)",
255
+ cr: "var(--card-radius)",
256
+ bw: "var(--border-width)"
257
+ };
258
+ const LIGHT = "theme=light | (@media(prefers-color-scheme: light) & :not([data-theme]))";
259
+ const HIGH_CONTRAST = "contrast=more | (@media(prefers-contrast: more) & :not([data-contrast]))";
260
+ function tastyTokens(theme) {
261
+ const tokens = Object.fromEntries(Object.entries(theme.tokens).filter(([name]) => name.startsWith("$")));
262
+ Object.assign(tokens, {
263
+ "#surface": colorStates(theme.colors.surface),
264
+ "#surface-2": colorStates(theme.colors.surface2),
265
+ "#surface-3": colorStates(theme.colors.surface3),
266
+ "#surface-2-hover": mix("#text", 3, "#surface-2"),
267
+ "#surface-2-pressed": mix("#text", 9, "#surface-2"),
268
+ "#surface-3-hover": mix("#text", 3, "#surface-3"),
269
+ "#surface-3-pressed": mix("#text", 9, "#surface-3"),
270
+ "#text": colorStates(theme.colors.text),
271
+ "#text-soft": colorStates(theme.colors.textSoft),
272
+ "#border": mix("#text", 18, "#surface"),
273
+ "#border-strong": mix("#text", 34, "#surface"),
274
+ "#accent-text": colorStates(theme.colors.accentText),
275
+ "#accent-surface": colorStates(theme.colors.accentSurface),
276
+ "#accent-surface-text": colorStates(theme.colors.accentSurfaceText),
277
+ "#focus": colorStates(theme.colors.focus),
278
+ "#shadow": colorStates(theme.colors.shadow),
279
+ "#overlay": mix("#text", 58, "transparent")
280
+ });
281
+ return tokens;
242
282
  }
243
- function tokenProperty(name) {
244
- if (name.startsWith("--")) return name;
245
- return `--${name.replace(/^\$/, "")}`;
283
+ function colorStates(colors) {
284
+ return {
285
+ "": colors.dark,
286
+ [LIGHT]: colors.light,
287
+ [HIGH_CONTRAST]: colors.darkContrast,
288
+ [`(${LIGHT}) & (${HIGH_CONTRAST})`]: colors.lightContrast
289
+ };
290
+ }
291
+ function mix(foreground, percentage, background) {
292
+ return `color-mix(in oklab, ${foreground} ${percentage}%, ${background})`;
293
+ }
294
+ //#endregion
295
+ //#region src/components/component-styles.ts
296
+ const sharedConfiguration = globalThis;
297
+ const cookbookComponentNames = new Set(COOKBOOK_COMPONENT_NAMES);
298
+ function configureComponentStyles(styles) {
299
+ sharedConfiguration.__tenphiCookbookComponentStyles = styles ?? {};
300
+ }
301
+ /** Preserve custom anatomy names from the pre-component style API. */
302
+ function resolveLegacyAnatomyStyles(styles) {
303
+ if (!styles) return void 0;
304
+ const entries = Object.entries(styles).filter((entry) => !cookbookComponentNames.has(entry[0]) && entry[1] !== void 0).map(([name, value]) => [`[data-tasty-anatomy="${name}"]`, isModeConfig(value) ? value.styles : value]);
305
+ return entries.length ? Object.fromEntries(entries) : void 0;
246
306
  }
247
- function kebab(value) {
248
- return value.replace(/([a-z0-9])([A-Z])/g, "$1-$2").replace(/[^a-zA-Z0-9_-]/g, "-").toLowerCase();
307
+ function isModeConfig(value) {
308
+ return "mode" in value && (value.mode === "extend" || value.mode === "replace") && "styles" in value;
249
309
  }
250
310
  //#endregion
311
+ //#region src/components/tasty-states.js
312
+ const cookbookStates = {
313
+ "@mobile": "@media(w < 50rem)",
314
+ "@desktop": "@media(w >= 50rem)",
315
+ "@small": "@media(w <= 40rem)",
316
+ "@shell-mobile": "@media(w <= 48rem)",
317
+ "@shell-desktop": "@media(w > 48rem)",
318
+ "@narrow-layout": "@media(w < 72rem)",
319
+ "@medium-layout": "@media(w >= 50rem) & @media(w < 72rem)",
320
+ "@reduced-motion": "@media(prefers-reduced-motion: reduce)"
321
+ };
322
+ //#endregion
251
323
  //#region src/integration.ts
252
324
  const packageRequire = createRequire(import.meta.url);
253
325
  const starlightRoot = dirname(packageRequire.resolve("@astrojs/starlight"));
254
326
  const tastyStaticMiddleware = packageRequire.resolve("@tenphi/tasty/ssr/astro-middleware-static");
327
+ const tastyExtractStaticMiddleware = packageRequire.resolve("@tenphi/tasty/ssr/astro-middleware-extract-static");
328
+ const astroReactServer = packageRequire.resolve("@astrojs/react/server.js");
329
+ const astroReactClient = packageRequire.resolve("@astrojs/react/client.js");
330
+ const astroReactIntegration = packageRequire.resolve("@astrojs/react");
331
+ const importNative = new Function("specifier", "return import(specifier)");
255
332
  function cookbook(options = {}) {
256
333
  const docsTheme = resolveDocsTheme(options.config?.theme);
257
334
  if (docsTheme.diagnostics.some((diagnostic) => diagnostic.severity === "error")) throw new Error(docsTheme.diagnostics.map((diagnostic) => diagnostic.message).join("\n"));
258
335
  configureTastyTheme(options.config?.theme, docsTheme);
259
- const cssPath = fileURLToPath(new URL("./styles.css", import.meta.url));
260
- const searchClientPath = fileURLToPath(new URL("./client/search.js", import.meta.url));
261
- const appearanceClientPath = fileURLToPath(new URL("./client/appearance.js", import.meta.url));
336
+ configureComponentStyles(options.config?.theme?.styles);
262
337
  const components = {
263
338
  Header: fileURLToPath(new URL("./overrides/Header.astro", import.meta.url)),
264
339
  Sidebar: fileURLToPath(new URL("./overrides/Sidebar.astro", import.meta.url)),
340
+ MobileMenuFooter: fileURLToPath(new URL("./overrides/MobileMenuFooter.astro", import.meta.url)),
341
+ ThemeSelect: fileURLToPath(new URL("./overrides/ThemeSelect.astro", import.meta.url)),
265
342
  ...options.config?.components?.overrides
266
343
  };
267
344
  const navigation = resolveNavigationLayout(options.config?.navigation);
268
- const inner = [tastyIntegration({ islands: false }), starlight({
269
- title: options.config?.site?.title ?? "Documentation",
270
- ...options.config?.site?.description ? { description: options.config.site.description } : {},
271
- customCss: ["virtual:cookbook/theme.css", cssPath],
272
- ...options.config?.search?.enabled === false ? { pagefind: false } : {},
273
- components,
274
- sidebar: starlightSidebar(navigation)
275
- })];
345
+ const tasty = tastyIntegration({
346
+ islands: false,
347
+ css: { mode: "extract" }
348
+ });
349
+ let inner = [tasty];
276
350
  let projectRoot = options.root;
277
351
  let graphConfig = options.config;
278
352
  let graph;
279
- let usingStarlight = false;
353
+ let usingContentCollection = false;
280
354
  async function loadGraph(refresh = false) {
281
355
  if (!graph || refresh) {
282
356
  graph = await createDocsGraph({
@@ -291,6 +365,7 @@ function cookbook(options = {}) {
291
365
  name: "cookbook",
292
366
  hooks: {
293
367
  "astro:config:setup": async (context) => {
368
+ const react = (await importNative(pathToFileURL(astroReactIntegration).href)).default();
294
369
  if (context.config.integrations.some((integration) => integration.name === "@astrojs/starlight")) throw new Error("Cookbook already includes Starlight. Remove the direct @astrojs/starlight integration before continuing.");
295
370
  projectRoot ??= fileURLToPath(context.config.root);
296
371
  const base = options.config?.build?.base ?? context.config.base;
@@ -301,58 +376,127 @@ function cookbook(options = {}) {
301
376
  base
302
377
  }
303
378
  };
304
- usingStarlight = hasContentConfig(context.config.srcDir);
379
+ usingContentCollection = hasContentConfig(context.config.srcDir);
380
+ const starlightIntegration = starlight({
381
+ title: options.config?.site?.title ?? "Documentation",
382
+ ...options.config?.site?.description ? { description: options.config.site.description } : {},
383
+ ...options.config?.search?.enabled === false ? { pagefind: false } : {},
384
+ ...!usingContentCollection ? { disable404Route: true } : {},
385
+ components,
386
+ sidebar: usingContentCollection ? starlightSidebar(navigation) : []
387
+ });
388
+ inner = [
389
+ react,
390
+ tasty,
391
+ starlightIntegration
392
+ ];
393
+ let markdownRuntime = {
394
+ image: context.config.image,
395
+ markdown: context.config.markdown,
396
+ srcDir: context.config.srcDir
397
+ };
398
+ let markdownRenderer;
305
399
  context.updateConfig({
306
400
  base,
307
401
  output: "static",
308
402
  vite: {
309
- ssr: { external: ["@tenphi/docs"] },
310
- plugins: [virtualDocsPlugin(docsTheme.css, () => ({
311
- entries: graph?.entries ?? [],
312
- routes: graph?.routes ?? [],
313
- site: graph?.config.site ?? options.config?.site ?? {},
314
- base: graph?.config.build.base ?? base,
315
- search: graph?.config.search.enabled ?? options.config?.search?.enabled ?? true
316
- }), navigation)],
317
- resolve: { alias: [{
318
- find: "@astrojs/starlight",
319
- replacement: starlightRoot
320
- }, {
321
- find: "@tenphi/tasty/ssr/astro-middleware-static",
322
- replacement: tastyStaticMiddleware
323
- }] }
403
+ ssr: { external: [
404
+ "@tenphi/docs",
405
+ "react",
406
+ "react-dom",
407
+ "react-dom/server"
408
+ ] },
409
+ plugins: [virtualDocsPlugin(async () => {
410
+ const loaded = await loadGraph();
411
+ return {
412
+ entries: usingContentCollection ? loaded.entries : await Promise.all(loaded.entries.map(async (entry) => {
413
+ const { image, markdown, srcDir } = markdownRuntime;
414
+ markdownRenderer ??= markdown.processor.createRenderer({
415
+ image,
416
+ syntaxHighlight: markdown.syntaxHighlight,
417
+ shikiConfig: markdown.shikiConfig,
418
+ gfm: markdown.gfm,
419
+ smartypants: markdown.smartypants
420
+ });
421
+ const rendered = await (await markdownRenderer).render(entry.transformedBody, {
422
+ frontmatter: entry.frontmatter,
423
+ fileURL: starlightContentUrl(entry.route, srcDir)
424
+ });
425
+ return {
426
+ ...entry,
427
+ rendered: {
428
+ html: rendered.code,
429
+ headings: rendered.metadata.headings
430
+ }
431
+ };
432
+ })),
433
+ routes: loaded.routes,
434
+ site: documentedSite(loaded),
435
+ base: loaded.config.build.base,
436
+ search: loaded.config.search.enabled
437
+ };
438
+ }, navigation)],
439
+ resolve: { alias: [
440
+ {
441
+ find: "@astrojs/starlight",
442
+ replacement: starlightRoot
443
+ },
444
+ {
445
+ find: "@tenphi/tasty/ssr/astro-middleware-static",
446
+ replacement: tastyStaticMiddleware
447
+ },
448
+ {
449
+ find: "@tenphi/tasty/ssr/astro-middleware-extract-static",
450
+ replacement: tastyExtractStaticMiddleware
451
+ },
452
+ {
453
+ find: "@astrojs/react/server.js",
454
+ replacement: astroReactServer
455
+ },
456
+ {
457
+ find: "@astrojs/react/client.js",
458
+ replacement: astroReactClient
459
+ }
460
+ ] }
324
461
  }
325
462
  });
326
- await callInner(inner.slice(0, 1), "astro:config:setup", context);
327
- if (!usingStarlight) {
463
+ await callInner(inner.slice(0, 2), "astro:config:setup", context);
464
+ if (!usingContentCollection) {
328
465
  graph = await createDocsGraph({
329
466
  root: projectRoot,
330
467
  config: graphConfig
331
468
  });
332
469
  assertValidDocs(graph);
333
- if (graph.config.search.enabled) context.injectScript("page", `import ${JSON.stringify(searchClientPath)};`);
334
- context.injectScript("page", `import ${JSON.stringify(appearanceClientPath)};`);
335
470
  context.injectRoute({
336
471
  pattern: "[...route]",
337
472
  entrypoint: new URL("./routes/DocsPage.astro", import.meta.url),
338
473
  prerender: true
339
474
  });
340
- return;
341
475
  }
342
- const starlightIntegration = inner[1];
343
- if (starlightIntegration) {
476
+ const starlightWithPlugins = inner[2];
477
+ if (starlightWithPlugins) {
344
478
  const selfIndex = context.config.integrations.findIndex((integration) => integration.name === "cookbook");
345
- context.config.integrations.splice(selfIndex + 1, 0, starlightIntegration);
479
+ context.config.integrations.splice(selfIndex + 1, 0, starlightWithPlugins);
346
480
  try {
347
- await callInner([starlightIntegration], "astro:config:setup", context);
481
+ await callInner([starlightWithPlugins], "astro:config:setup", usingContentCollection ? context : withoutStarlightDocsRoute(context));
348
482
  } finally {
349
- const placeholderIndex = context.config.integrations.indexOf(starlightIntegration);
483
+ const placeholderIndex = context.config.integrations.indexOf(starlightWithPlugins);
350
484
  if (placeholderIndex >= 0) context.config.integrations.splice(placeholderIndex, 1);
351
485
  }
352
486
  }
487
+ context.config.integrations.push({
488
+ name: "cookbook-markdown-renderer",
489
+ hooks: { "astro:config:setup": ({ config }) => {
490
+ markdownRuntime = {
491
+ image: config.image,
492
+ markdown: config.markdown,
493
+ srcDir: config.srcDir
494
+ };
495
+ } }
496
+ });
353
497
  },
354
498
  "astro:config:done": async (context) => {
355
- await callInner(usingStarlight ? inner : inner.slice(0, 1), "astro:config:done", context);
499
+ await callInner(inner, "astro:config:done", context);
356
500
  },
357
501
  "astro:server:setup": async ({ server }) => {
358
502
  let assets = docsAssetMap(await loadGraph());
@@ -394,10 +538,10 @@ function cookbook(options = {}) {
394
538
  },
395
539
  "astro:build:start": async (context) => {
396
540
  await loadGraph();
397
- await callInner(usingStarlight ? inner : inner.slice(0, 1), "astro:build:start", context);
541
+ await callInner(inner, "astro:build:start", context);
398
542
  },
399
543
  "astro:build:done": async (context) => {
400
- await callInner(usingStarlight ? inner : inner.slice(0, 1), "astro:build:done", context);
544
+ await callInner(inner, "astro:build:done", context);
401
545
  if (!graph) return;
402
546
  const output = fileURLToPath(context.dir);
403
547
  for (const asset of graph.assets) {
@@ -406,23 +550,30 @@ function cookbook(options = {}) {
406
550
  await mkdir(dirname(target), { recursive: true });
407
551
  await cp(asset.sourcePath, target);
408
552
  }
409
- if (!usingStarlight && graph.config.search.enabled) {
410
- const { close, createIndex } = await import("pagefind");
411
- const created = await createIndex({ rootSelector: "[data-pagefind-body]" });
412
- if (!created.index || created.errors.length > 0) throw new Error(`Pagefind failed to start: ${created.errors.join("; ")}`);
413
- const indexed = await created.index.addDirectory({ path: output });
414
- if (indexed.errors.length > 0) throw new Error(`Pagefind failed to index the site: ${indexed.errors.join("; ")}`);
415
- const written = await created.index.writeFiles({ outputPath: join(output, "pagefind") });
416
- await close();
417
- if (written.errors.length > 0) throw new Error(`Pagefind failed to write the index: ${written.errors.join("; ")}`);
418
- }
419
553
  }
420
554
  }
421
555
  };
422
556
  }
557
+ function starlightContentUrl(route, srcDir) {
558
+ const slug = route === "/" ? "index" : route.replace(/^\/+|\/+$/g, "");
559
+ return new URL(`content/docs/${slug}.md`, srcDir);
560
+ }
423
561
  function docsAssetMap(graph) {
424
562
  return new Map(graph.assets.flatMap((asset) => asset.publicPath && asset.sourcePath ? [[asset.publicPath, asset]] : []));
425
563
  }
564
+ function documentedSite(graph) {
565
+ if (graph.config.site.version) return graph.config.site;
566
+ const packages = new Set(graph.entries.flatMap((entry) => entry.package?.resolved ? [entry.package.resolved] : []));
567
+ if (packages.size !== 1) return graph.config.site;
568
+ const resolved = packages.values().next().value;
569
+ if (!resolved) return graph.config.site;
570
+ const separator = resolved.lastIndexOf("@");
571
+ if (separator <= 0 || separator === resolved.length - 1) return graph.config.site;
572
+ return {
573
+ ...graph.config.site,
574
+ version: resolved.slice(separator + 1)
575
+ };
576
+ }
426
577
  function requestPath(url) {
427
578
  try {
428
579
  return decodeURIComponent(new URL(url ?? "/", "http://localhost").pathname);
@@ -443,41 +594,19 @@ function assetContentType(pathname) {
443
594
  }
444
595
  }
445
596
  function configureTastyTheme(theme, resolved) {
446
- const tokens = Object.fromEntries(Object.entries(resolved.tokens).filter(([name]) => name.startsWith("$")));
447
- Object.assign(tokens, {
448
- "#surface": "var(--td-surface)",
449
- "#surface-2": "var(--td-surface-2)",
450
- "#surface-3": "var(--td-surface-3)",
451
- "#text": "var(--td-text)",
452
- "#text-soft": "var(--td-text-soft)",
453
- "#border": "var(--td-border)",
454
- "#border-strong": "var(--td-border-strong)",
455
- "#accent-text": "var(--td-accent-text)",
456
- "#accent-surface": "var(--td-accent-surface)",
457
- "#accent-surface-text": "var(--td-accent-surface-text)",
458
- "#focus": "var(--td-focus)"
459
- });
460
- const globalStyles = anatomyStyles(theme?.styles);
597
+ const tokens = tastyTokens(resolved);
598
+ const globalStyles = resolveLegacyAnatomyStyles(theme?.styles);
461
599
  configure({
462
- ...theme?.states ? { states: theme.states } : {},
463
- units: {
464
- x: "var(--gap)",
465
- r: "var(--radius)",
466
- cr: "var(--card-radius)",
467
- bw: "var(--border-width)"
600
+ states: {
601
+ ...cookbookStates,
602
+ ...theme?.states
468
603
  },
604
+ units: TASTY_UNITS,
469
605
  tokens,
470
606
  presets: resolved.presets,
471
607
  ...globalStyles ? { globalStyles } : {}
472
608
  });
473
609
  }
474
- function anatomyStyles(styles) {
475
- if (!styles) return void 0;
476
- return Object.fromEntries(Object.entries(styles).filter((entry) => isRecord(entry[1])).map(([name, value]) => [`[data-tasty-anatomy="${name}"]`, value]));
477
- }
478
- function isRecord(value) {
479
- return typeof value === "object" && value !== null && !Array.isArray(value);
480
- }
481
610
  function starlightSidebar(layout) {
482
611
  const fallback = layout.items?.length ? layout.items.map(starlightSidebarItem) : [{ autogenerate: { directory: "" } }];
483
612
  if (!layout.sectioned) return fallback;
@@ -516,20 +645,25 @@ async function callInner(integrations, hook, context) {
516
645
  if (typeof handler === "function") await handler(context);
517
646
  }
518
647
  }
519
- function virtualDocsPlugin(css, getContent, layout) {
520
- const themeId = "\0virtual:cookbook/theme.css";
648
+ function withoutStarlightDocsRoute(context) {
649
+ return new Proxy(context, { get(target, property, receiver) {
650
+ if (property !== "injectRoute") return Reflect.get(target, property, receiver);
651
+ return (route) => {
652
+ if (route.pattern !== "[...slug]") context.injectRoute(route);
653
+ };
654
+ } });
655
+ }
656
+ function virtualDocsPlugin(getContent, layout) {
521
657
  const configId = "\0virtual:cookbook/config";
522
658
  const layoutId = "\0virtual:cookbook/layout";
523
659
  return {
524
- name: "cookbook-theme",
660
+ name: "cookbook-data",
525
661
  resolveId(id) {
526
- if (id === "virtual:cookbook/theme.css") return themeId;
527
662
  if (id === "virtual:cookbook/config") return configId;
528
663
  if (id === "virtual:cookbook/layout") return layoutId;
529
664
  },
530
- load(id) {
531
- if (id === themeId) return css;
532
- if (id === configId) return `export const content = ${JSON.stringify(getContent())};`;
665
+ async load(id) {
666
+ if (id === configId) return `export const content = ${JSON.stringify(await getContent())};`;
533
667
  if (id === layoutId) return `export const layout = ${JSON.stringify(layout)};`;
534
668
  }
535
669
  };