@tenphi/starlight 0.10.2 → 0.11.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.
- package/dist/client/code-copy.d.ts +1 -0
- package/dist/client/code-copy.js +57 -0
- package/dist/client/code-copy.js.map +1 -0
- package/dist/components/GlobalStyles.js +265 -83
- package/dist/components/LayoutComponents.js +6 -2
- package/dist/components/LayoutComponents.test.ts +8 -0
- package/dist/components/tasty-states.js +1 -0
- package/dist/icons/check.svg +1 -0
- package/dist/icons/favicon.svg +11 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +329 -6
- package/dist/index.js.map +1 -1
- package/dist/markdown/rendered-content.d.ts +6 -0
- package/dist/markdown/rendered-content.d.ts.map +1 -0
- package/dist/markdown/rendered-content.js +33 -0
- package/dist/markdown/rendered-content.js.map +1 -0
- package/dist/overrides/Header.astro +2 -0
- package/dist/overrides/MarkdownContent.astro +7 -0
- package/dist/routes/DocsPage.astro +9 -0
- package/package.json +4 -3
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@ 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, readdir, unlink, writeFile } from "node:fs/promises";
|
|
6
|
-
import { dirname, extname, join } from "node:path";
|
|
6
|
+
import { dirname, extname, isAbsolute, join, resolve } from "node:path";
|
|
7
7
|
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
8
8
|
import { COOKBOOK_COMPONENT_NAMES, assertValidDocs, createDocsGraph } from "@tenphi/docs";
|
|
9
9
|
import { configure } from "@tenphi/tasty/core";
|
|
@@ -11,6 +11,7 @@ import { tastyIntegration } from "@tenphi/tasty/ssr/astro";
|
|
|
11
11
|
import { renderMermaidSVG } from "beautiful-mermaid";
|
|
12
12
|
import { apcaContrast, glaze, okhslToLinearSrgb, relativeLuminanceFromLinearRgb, variantToOkhsl } from "@tenphi/glaze";
|
|
13
13
|
import "@tenphi/tasty";
|
|
14
|
+
import sharp from "sharp";
|
|
14
15
|
//#region src/markdown/rehype-mermaid.ts
|
|
15
16
|
const sourceStyleDirective = /^\s*(?:classDef|style|linkStyle)\s+.*$/gim;
|
|
16
17
|
const accessibilityDirective = /^\s*acc(?:Title|Descr):\s*.*$/gim;
|
|
@@ -144,6 +145,64 @@ function isScrollContainer(node) {
|
|
|
144
145
|
return node.type === "element" && node.tagName === "div" && Array.isArray(className) && className.includes(containerClass);
|
|
145
146
|
}
|
|
146
147
|
//#endregion
|
|
148
|
+
//#region src/markdown/rehype-page-affordances.ts
|
|
149
|
+
/** Add copy controls to rendered Markdown code blocks. */
|
|
150
|
+
function rehypePageAffordances() {
|
|
151
|
+
return (tree) => addPageAffordances(tree);
|
|
152
|
+
}
|
|
153
|
+
/** Sätteri adapter for Astro's default Markdown processor. */
|
|
154
|
+
const satteriPageAffordances = {
|
|
155
|
+
name: "cookbook:page-affordances",
|
|
156
|
+
element: {
|
|
157
|
+
filter: ["pre"],
|
|
158
|
+
visit(node, context) {
|
|
159
|
+
const replacement = pageAffordance(node);
|
|
160
|
+
if (replacement !== node) context.replaceNode(node, replacement);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
function addPageAffordances(parent) {
|
|
165
|
+
if (!parent.children) return;
|
|
166
|
+
for (const [index, child] of parent.children.entries()) {
|
|
167
|
+
const replacement = pageAffordance(child);
|
|
168
|
+
if (replacement !== child) {
|
|
169
|
+
parent.children[index] = replacement;
|
|
170
|
+
continue;
|
|
171
|
+
}
|
|
172
|
+
addPageAffordances(child);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function pageAffordance(node) {
|
|
176
|
+
if (isOrdinaryCodeBlock(node)) return codeBlockWithCopy(node);
|
|
177
|
+
return node;
|
|
178
|
+
}
|
|
179
|
+
function isOrdinaryCodeBlock(node) {
|
|
180
|
+
if (node.type !== "element" || node.tagName !== "pre") return false;
|
|
181
|
+
return (node.properties?.dataLanguage ?? node.properties?.["data-language"]) !== "mermaid" && node.children?.[0]?.tagName === "code";
|
|
182
|
+
}
|
|
183
|
+
function codeBlockWithCopy(pre) {
|
|
184
|
+
return element("cookbook-code-block", {
|
|
185
|
+
className: ["td-code-block"],
|
|
186
|
+
dataTastyAnatomy: "MarkdownCodeBlock"
|
|
187
|
+
}, [pre, element("button", {
|
|
188
|
+
type: "button",
|
|
189
|
+
dataCopyCode: "",
|
|
190
|
+
ariaLabel: "Copy code",
|
|
191
|
+
title: "Copy code"
|
|
192
|
+
}, [element("span", {
|
|
193
|
+
dataCopyIcon: "",
|
|
194
|
+
ariaHidden: "true"
|
|
195
|
+
}, [])])]);
|
|
196
|
+
}
|
|
197
|
+
function element(tagName, properties, children) {
|
|
198
|
+
return {
|
|
199
|
+
type: "element",
|
|
200
|
+
tagName,
|
|
201
|
+
properties,
|
|
202
|
+
children
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
//#endregion
|
|
147
206
|
//#region src/theme/defaults.ts
|
|
148
207
|
const DEFAULT_THEME_TOKENS = {
|
|
149
208
|
$gap: "0.5rem",
|
|
@@ -868,6 +927,7 @@ const cookbookStates = {
|
|
|
868
927
|
"@mobile": "@media(w < 50rem)",
|
|
869
928
|
"@desktop": "@media(w >= 50rem)",
|
|
870
929
|
"@small": "@media(w <= 40rem)",
|
|
930
|
+
"@compact": "@media(w <= 23rem)",
|
|
871
931
|
"@shell-mobile": "@media(w <= 48rem)",
|
|
872
932
|
"@shell-desktop": "@media(w > 48rem)",
|
|
873
933
|
"@narrow-layout": "@media(w < 72rem)",
|
|
@@ -875,6 +935,212 @@ const cookbookStates = {
|
|
|
875
935
|
"@reduced-motion": "@media(prefers-reduced-motion: reduce)"
|
|
876
936
|
};
|
|
877
937
|
//#endregion
|
|
938
|
+
//#region src/site-icons.ts
|
|
939
|
+
const ICON_DIRECTORY = "_cookbook/icons";
|
|
940
|
+
const DEFAULT_ICON_BACKGROUND = "#315efb";
|
|
941
|
+
const SUPPORTED_SOURCE_FORMATS = /* @__PURE__ */ new Set([
|
|
942
|
+
"avif",
|
|
943
|
+
"gif",
|
|
944
|
+
"heif",
|
|
945
|
+
"jpeg",
|
|
946
|
+
"jpg",
|
|
947
|
+
"png",
|
|
948
|
+
"svg",
|
|
949
|
+
"webp"
|
|
950
|
+
]);
|
|
951
|
+
async function createSiteIcons({ base, root, site = {}, themeColors }) {
|
|
952
|
+
const configured = site.favicon;
|
|
953
|
+
const source = typeof configured === "string" ? configured : configured?.source;
|
|
954
|
+
const sourcePath = source ? resolveSourcePath(source, root) : fileURLToPath(new URL("./icons/favicon.svg", import.meta.url));
|
|
955
|
+
const background = typeof configured === "object" && configured?.background ? configured.background : DEFAULT_ICON_BACKGROUND;
|
|
956
|
+
const input = await readFile(sourcePath).catch((error) => {
|
|
957
|
+
throw new Error(`Unable to read site.favicon source at ${sourcePath}: ${errorMessage$1(error)}`);
|
|
958
|
+
});
|
|
959
|
+
const format = (await sharp(input, { animated: false }).metadata()).format?.toLowerCase();
|
|
960
|
+
if (!format || !SUPPORTED_SOURCE_FORMATS.has(format)) throw new Error("site.favicon must point to an SVG, PNG, JPEG, WebP, AVIF, or GIF image.");
|
|
961
|
+
const definitions = [
|
|
962
|
+
await pngAsset(input, base, "favicon-32x32.png", 32),
|
|
963
|
+
await safePngAsset(input, base, "apple-touch-icon.png", 180, background),
|
|
964
|
+
await pngAsset(input, base, "icon-192x192.png", 192),
|
|
965
|
+
await pngAsset(input, base, "icon-512x512.png", 512),
|
|
966
|
+
await safePngAsset(input, base, "icon-192x192-maskable.png", 192, background),
|
|
967
|
+
await safePngAsset(input, base, "icon-512x512-maskable.png", 512, background)
|
|
968
|
+
];
|
|
969
|
+
const scalable = format === "svg" ? asset(base, "favicon.svg", input, "image/svg+xml") : void 0;
|
|
970
|
+
const faviconPath = `/${ICON_DIRECTORY}/${scalable ? "favicon.svg" : "favicon-32x32.png"}`;
|
|
971
|
+
const iconEntries = [
|
|
972
|
+
{
|
|
973
|
+
src: pathWithBase(base, `/${ICON_DIRECTORY}/icon-192x192.png`),
|
|
974
|
+
sizes: "192x192",
|
|
975
|
+
type: "image/png",
|
|
976
|
+
purpose: "any"
|
|
977
|
+
},
|
|
978
|
+
{
|
|
979
|
+
src: pathWithBase(base, `/${ICON_DIRECTORY}/icon-512x512.png`),
|
|
980
|
+
sizes: "512x512",
|
|
981
|
+
type: "image/png",
|
|
982
|
+
purpose: "any"
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
src: pathWithBase(base, `/${ICON_DIRECTORY}/icon-192x192-maskable.png`),
|
|
986
|
+
sizes: "192x192",
|
|
987
|
+
type: "image/png",
|
|
988
|
+
purpose: "maskable"
|
|
989
|
+
},
|
|
990
|
+
{
|
|
991
|
+
src: pathWithBase(base, `/${ICON_DIRECTORY}/icon-512x512-maskable.png`),
|
|
992
|
+
sizes: "512x512",
|
|
993
|
+
type: "image/png",
|
|
994
|
+
purpose: "maskable"
|
|
995
|
+
},
|
|
996
|
+
...scalable ? [{
|
|
997
|
+
src: scalable.publicPath,
|
|
998
|
+
sizes: "any",
|
|
999
|
+
type: "image/svg+xml",
|
|
1000
|
+
purpose: "any"
|
|
1001
|
+
}] : []
|
|
1002
|
+
];
|
|
1003
|
+
const manifest = asset(base, "site.webmanifest", Buffer.from(`${JSON.stringify({
|
|
1004
|
+
name: site.title ?? "Documentation",
|
|
1005
|
+
short_name: site.title ?? "Documentation",
|
|
1006
|
+
...site.description ? { description: site.description } : {},
|
|
1007
|
+
id: normalizedBase(base),
|
|
1008
|
+
start_url: normalizedBase(base),
|
|
1009
|
+
scope: normalizedBase(base),
|
|
1010
|
+
display: "standalone",
|
|
1011
|
+
background_color: background,
|
|
1012
|
+
theme_color: themeColors.light,
|
|
1013
|
+
icons: iconEntries
|
|
1014
|
+
}, null, 2)}\n`), "application/manifest+json");
|
|
1015
|
+
const favicon32 = definitions[0];
|
|
1016
|
+
const appleTouchIcon = definitions[1];
|
|
1017
|
+
if (!favicon32 || !appleTouchIcon) throw new Error("Cookbook failed to generate the required site icons.");
|
|
1018
|
+
return {
|
|
1019
|
+
assets: [
|
|
1020
|
+
...definitions,
|
|
1021
|
+
...scalable ? [scalable] : [],
|
|
1022
|
+
manifest
|
|
1023
|
+
],
|
|
1024
|
+
faviconPath,
|
|
1025
|
+
head: [
|
|
1026
|
+
{
|
|
1027
|
+
tag: "link",
|
|
1028
|
+
attrs: {
|
|
1029
|
+
rel: "icon",
|
|
1030
|
+
href: favicon32.publicPath,
|
|
1031
|
+
sizes: "32x32",
|
|
1032
|
+
type: favicon32.contentType
|
|
1033
|
+
}
|
|
1034
|
+
},
|
|
1035
|
+
...scalable ? [{
|
|
1036
|
+
tag: "link",
|
|
1037
|
+
attrs: {
|
|
1038
|
+
rel: "icon",
|
|
1039
|
+
href: scalable.publicPath,
|
|
1040
|
+
sizes: "any",
|
|
1041
|
+
type: scalable.contentType
|
|
1042
|
+
}
|
|
1043
|
+
}] : [],
|
|
1044
|
+
{
|
|
1045
|
+
tag: "link",
|
|
1046
|
+
attrs: {
|
|
1047
|
+
rel: "apple-touch-icon",
|
|
1048
|
+
href: appleTouchIcon.publicPath,
|
|
1049
|
+
sizes: "180x180"
|
|
1050
|
+
}
|
|
1051
|
+
},
|
|
1052
|
+
{
|
|
1053
|
+
tag: "link",
|
|
1054
|
+
attrs: {
|
|
1055
|
+
rel: "manifest",
|
|
1056
|
+
href: manifest.publicPath
|
|
1057
|
+
}
|
|
1058
|
+
},
|
|
1059
|
+
{
|
|
1060
|
+
tag: "meta",
|
|
1061
|
+
attrs: {
|
|
1062
|
+
name: "theme-color",
|
|
1063
|
+
content: themeColors.light,
|
|
1064
|
+
media: "(prefers-color-scheme: light)"
|
|
1065
|
+
}
|
|
1066
|
+
},
|
|
1067
|
+
{
|
|
1068
|
+
tag: "meta",
|
|
1069
|
+
attrs: {
|
|
1070
|
+
name: "theme-color",
|
|
1071
|
+
content: themeColors.dark,
|
|
1072
|
+
media: "(prefers-color-scheme: dark)"
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
],
|
|
1076
|
+
sourcePath
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
async function pngAsset(input, base, name, size) {
|
|
1080
|
+
return asset(base, name, await sharp(input, {
|
|
1081
|
+
animated: false,
|
|
1082
|
+
density: 512
|
|
1083
|
+
}).resize(size, size, {
|
|
1084
|
+
fit: "contain",
|
|
1085
|
+
background: {
|
|
1086
|
+
r: 0,
|
|
1087
|
+
g: 0,
|
|
1088
|
+
b: 0,
|
|
1089
|
+
alpha: 0
|
|
1090
|
+
}
|
|
1091
|
+
}).png().toBuffer(), "image/png");
|
|
1092
|
+
}
|
|
1093
|
+
async function safePngAsset(input, base, name, size, background) {
|
|
1094
|
+
const safeSize = Math.round(size * .8);
|
|
1095
|
+
const foreground = await sharp(input, {
|
|
1096
|
+
animated: false,
|
|
1097
|
+
density: 512
|
|
1098
|
+
}).resize(safeSize, safeSize, {
|
|
1099
|
+
fit: "contain",
|
|
1100
|
+
background: {
|
|
1101
|
+
r: 0,
|
|
1102
|
+
g: 0,
|
|
1103
|
+
b: 0,
|
|
1104
|
+
alpha: 0
|
|
1105
|
+
}
|
|
1106
|
+
}).png().toBuffer();
|
|
1107
|
+
return asset(base, name, await sharp({ create: {
|
|
1108
|
+
width: size,
|
|
1109
|
+
height: size,
|
|
1110
|
+
channels: 4,
|
|
1111
|
+
background
|
|
1112
|
+
} }).composite([{
|
|
1113
|
+
input: foreground,
|
|
1114
|
+
gravity: "center"
|
|
1115
|
+
}]).png().toBuffer().catch((error) => {
|
|
1116
|
+
throw new Error(`Unable to use site.favicon.background ${JSON.stringify(background)}: ${errorMessage$1(error)}`);
|
|
1117
|
+
}), "image/png");
|
|
1118
|
+
}
|
|
1119
|
+
function asset(base, name, body, contentType) {
|
|
1120
|
+
const outputPath = `${ICON_DIRECTORY}/${name}`;
|
|
1121
|
+
return {
|
|
1122
|
+
body,
|
|
1123
|
+
contentType,
|
|
1124
|
+
outputPath,
|
|
1125
|
+
publicPath: pathWithBase(base, `/${outputPath}`)
|
|
1126
|
+
};
|
|
1127
|
+
}
|
|
1128
|
+
function resolveSourcePath(source, root) {
|
|
1129
|
+
if (/^[a-z][a-z\d+.-]*:/i.test(source)) throw new Error("site.favicon must reference a local image path.");
|
|
1130
|
+
return isAbsolute(source) ? source : resolve(root, source);
|
|
1131
|
+
}
|
|
1132
|
+
function pathWithBase(base, pathname) {
|
|
1133
|
+
const prefix = normalizedBase(base);
|
|
1134
|
+
return `${prefix === "/" ? "" : prefix.replace(/\/$/, "")}${pathname}`;
|
|
1135
|
+
}
|
|
1136
|
+
function normalizedBase(base) {
|
|
1137
|
+
const value = base.replace(/^\/+|\/+$/g, "");
|
|
1138
|
+
return value ? `/${value}/` : "/";
|
|
1139
|
+
}
|
|
1140
|
+
function errorMessage$1(error) {
|
|
1141
|
+
return error instanceof Error ? error.message : String(error);
|
|
1142
|
+
}
|
|
1143
|
+
//#endregion
|
|
878
1144
|
//#region src/integration.ts
|
|
879
1145
|
const packageRequire = createRequire(import.meta.url);
|
|
880
1146
|
const starlightRoot = dirname(packageRequire.resolve("@astrojs/starlight"));
|
|
@@ -892,12 +1158,16 @@ function cookbook(options = {}) {
|
|
|
892
1158
|
const headerPath = fileURLToPath(new URL("./overrides/Header.astro", import.meta.url));
|
|
893
1159
|
const footerPath = fileURLToPath(new URL("./overrides/Footer.astro", import.meta.url));
|
|
894
1160
|
const emptyFooterPath = fileURLToPath(new URL("./overrides/EmptyFooter.astro", import.meta.url));
|
|
1161
|
+
const sidebarPath = fileURLToPath(new URL("./overrides/Sidebar.astro", import.meta.url));
|
|
1162
|
+
const mobileMenuFooterPath = fileURLToPath(new URL("./overrides/MobileMenuFooter.astro", import.meta.url));
|
|
1163
|
+
const mobileMenuTogglePath = fileURLToPath(new URL("./overrides/MobileMenuToggle.astro", import.meta.url));
|
|
895
1164
|
const components = resolveComponentOverrides({
|
|
896
1165
|
Footer: footerPath,
|
|
897
1166
|
Header: headerPath,
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
1167
|
+
MarkdownContent: fileURLToPath(new URL("./overrides/MarkdownContent.astro", import.meta.url)),
|
|
1168
|
+
Sidebar: sidebarPath,
|
|
1169
|
+
MobileMenuFooter: mobileMenuFooterPath,
|
|
1170
|
+
MobileMenuToggle: mobileMenuTogglePath,
|
|
901
1171
|
ThemeSelect: fileURLToPath(new URL("./overrides/ThemeSelect.astro", import.meta.url))
|
|
902
1172
|
}, options.config?.components?.overrides, emptyFooterPath);
|
|
903
1173
|
const navigation = resolveNavigationLayout(options.config?.navigation);
|
|
@@ -910,6 +1180,8 @@ function cookbook(options = {}) {
|
|
|
910
1180
|
let graphConfig = options.config;
|
|
911
1181
|
let graph;
|
|
912
1182
|
let usingContentCollection = false;
|
|
1183
|
+
let siteIconBase = options.config?.build?.base ?? "/";
|
|
1184
|
+
let siteIcons;
|
|
913
1185
|
async function loadGraph(refresh = false) {
|
|
914
1186
|
if (!graph || refresh) {
|
|
915
1187
|
graph = await createDocsGraph({
|
|
@@ -920,6 +1192,18 @@ function cookbook(options = {}) {
|
|
|
920
1192
|
}
|
|
921
1193
|
return graph;
|
|
922
1194
|
}
|
|
1195
|
+
async function loadSiteIcons() {
|
|
1196
|
+
if (!projectRoot) throw new Error("Cookbook cannot generate site icons without a project root.");
|
|
1197
|
+
return createSiteIcons({
|
|
1198
|
+
base: siteIconBase,
|
|
1199
|
+
root: projectRoot,
|
|
1200
|
+
...options.config?.site ? { site: options.config.site } : {},
|
|
1201
|
+
themeColors: {
|
|
1202
|
+
light: docsTheme.colors.surface.light ?? "#ffffff",
|
|
1203
|
+
dark: docsTheme.colors.surface.dark ?? "#20232a"
|
|
1204
|
+
}
|
|
1205
|
+
});
|
|
1206
|
+
}
|
|
923
1207
|
return {
|
|
924
1208
|
name: "cookbook",
|
|
925
1209
|
hooks: {
|
|
@@ -928,6 +1212,8 @@ function cookbook(options = {}) {
|
|
|
928
1212
|
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.");
|
|
929
1213
|
projectRoot ??= fileURLToPath(context.config.root);
|
|
930
1214
|
const base = options.config?.build?.base ?? context.config.base;
|
|
1215
|
+
siteIconBase = base;
|
|
1216
|
+
siteIcons = await loadSiteIcons();
|
|
931
1217
|
graphConfig = {
|
|
932
1218
|
...options.config,
|
|
933
1219
|
build: {
|
|
@@ -940,7 +1226,12 @@ function cookbook(options = {}) {
|
|
|
940
1226
|
const starlightIntegration = starlight({
|
|
941
1227
|
title: options.config?.site?.title ?? "Documentation",
|
|
942
1228
|
expressiveCode: false,
|
|
943
|
-
|
|
1229
|
+
favicon: siteIcons.faviconPath,
|
|
1230
|
+
head: [...siteIcons.head, ...options.config?.head ?? []],
|
|
1231
|
+
...options.config?.editLink ? { editLink: options.config.editLink } : {},
|
|
1232
|
+
...options.config?.lastUpdated !== void 0 ? { lastUpdated: options.config.lastUpdated } : {},
|
|
1233
|
+
...options.config?.locales ? { locales: options.config.locales } : {},
|
|
1234
|
+
...options.config?.defaultLocale ? { defaultLocale: options.config.defaultLocale } : {},
|
|
944
1235
|
...options.config?.site?.description ? { description: options.config.site.description } : {},
|
|
945
1236
|
...options.config?.search?.enabled === false ? { pagefind: false } : {},
|
|
946
1237
|
...!usingContentCollection ? { disable404Route: true } : {},
|
|
@@ -1072,14 +1363,35 @@ function cookbook(options = {}) {
|
|
|
1072
1363
|
"astro:config:done": async (context) => {
|
|
1073
1364
|
await callInner(inner, "astro:config:done", context);
|
|
1074
1365
|
},
|
|
1075
|
-
"astro:server:setup": async ({ server }) => {
|
|
1366
|
+
"astro:server:setup": async ({ server, logger }) => {
|
|
1076
1367
|
let assets = docsAssetMap(await loadGraph());
|
|
1368
|
+
if (options.config?.site?.favicon && siteIcons) {
|
|
1369
|
+
server.watcher.add(siteIcons.sourcePath);
|
|
1370
|
+
server.watcher.on("change", async (changedPath) => {
|
|
1371
|
+
if (changedPath !== siteIcons?.sourcePath) return;
|
|
1372
|
+
try {
|
|
1373
|
+
siteIcons = await loadSiteIcons();
|
|
1374
|
+
server.ws.send({ type: "full-reload" });
|
|
1375
|
+
} catch (error) {
|
|
1376
|
+
logger.error(errorMessage(error));
|
|
1377
|
+
}
|
|
1378
|
+
});
|
|
1379
|
+
}
|
|
1077
1380
|
server.middlewares.use(async (request, response, next) => {
|
|
1078
1381
|
if (request.method !== "GET" && request.method !== "HEAD") {
|
|
1079
1382
|
next();
|
|
1080
1383
|
return;
|
|
1081
1384
|
}
|
|
1082
1385
|
const pathname = requestPath(request.url);
|
|
1386
|
+
const siteIcon = siteIcons?.assets.find((asset) => asset.publicPath === pathname);
|
|
1387
|
+
if (siteIcon) {
|
|
1388
|
+
response.statusCode = 200;
|
|
1389
|
+
response.setHeader("Content-Type", siteIcon.contentType);
|
|
1390
|
+
response.setHeader("Content-Length", siteIcon.body.byteLength);
|
|
1391
|
+
response.setHeader("Cache-Control", "no-cache");
|
|
1392
|
+
response.end(request.method === "HEAD" ? void 0 : siteIcon.body);
|
|
1393
|
+
return;
|
|
1394
|
+
}
|
|
1083
1395
|
if (!pathname.includes("/_tasty-assets/")) {
|
|
1084
1396
|
next();
|
|
1085
1397
|
return;
|
|
@@ -1111,6 +1423,7 @@ function cookbook(options = {}) {
|
|
|
1111
1423
|
});
|
|
1112
1424
|
},
|
|
1113
1425
|
"astro:build:start": async (context) => {
|
|
1426
|
+
siteIcons = await loadSiteIcons();
|
|
1114
1427
|
await loadGraph();
|
|
1115
1428
|
await callInner(inner, "astro:build:start", context);
|
|
1116
1429
|
},
|
|
@@ -1128,6 +1441,11 @@ function cookbook(options = {}) {
|
|
|
1128
1441
|
if (existsSync(pagefindOutput)) {
|
|
1129
1442
|
for (const name of await readdir(pagefindOutput)) if (extname(name) === ".css") await unlink(join(pagefindOutput, name));
|
|
1130
1443
|
}
|
|
1444
|
+
for (const asset of siteIcons?.assets ?? []) {
|
|
1445
|
+
const target = join(output, asset.outputPath);
|
|
1446
|
+
await mkdir(dirname(target), { recursive: true });
|
|
1447
|
+
await writeFile(target, asset.body);
|
|
1448
|
+
}
|
|
1131
1449
|
if (!graph) return;
|
|
1132
1450
|
for (const asset of graph.assets) {
|
|
1133
1451
|
if (!asset.sourcePath || !asset.publicPath) continue;
|
|
@@ -1145,12 +1463,14 @@ function registerCookbookMarkdownPlugins(processor) {
|
|
|
1145
1463
|
const plugins = Array.isArray(options.rehypePlugins) ? options.rehypePlugins : [];
|
|
1146
1464
|
if (!plugins.includes(rehypeMermaid)) plugins.push(rehypeMermaid);
|
|
1147
1465
|
if (!plugins.includes(rehypeTableScroll)) plugins.push(rehypeTableScroll);
|
|
1466
|
+
if (!plugins.includes(rehypePageAffordances)) plugins.push(rehypePageAffordances);
|
|
1148
1467
|
options.rehypePlugins = plugins;
|
|
1149
1468
|
} else if (processor.name === "satteri") {
|
|
1150
1469
|
const options = processor.options;
|
|
1151
1470
|
const plugins = Array.isArray(options.hastPlugins) ? options.hastPlugins : [];
|
|
1152
1471
|
if (!plugins.includes(satteriMermaid)) plugins.push(satteriMermaid);
|
|
1153
1472
|
if (!plugins.includes(satteriTableScroll)) plugins.push(satteriTableScroll);
|
|
1473
|
+
if (!plugins.includes(satteriPageAffordances)) plugins.push(satteriPageAffordances);
|
|
1154
1474
|
options.hastPlugins = plugins;
|
|
1155
1475
|
}
|
|
1156
1476
|
}
|
|
@@ -1219,6 +1539,9 @@ function assetContentType(pathname) {
|
|
|
1219
1539
|
default: return "application/octet-stream";
|
|
1220
1540
|
}
|
|
1221
1541
|
}
|
|
1542
|
+
function errorMessage(error) {
|
|
1543
|
+
return error instanceof Error ? error.message : String(error);
|
|
1544
|
+
}
|
|
1222
1545
|
function configureTastyTheme(theme, resolved) {
|
|
1223
1546
|
const tokens = tastyTokens(resolved);
|
|
1224
1547
|
const globalStyles = resolveLegacyAnatomyStyles(theme?.styles);
|