@nu-appdev/northwestern-starlight-theme 1.3.2 → 1.5.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.
@@ -0,0 +1,97 @@
1
+ import type { AstroIntegration } from "astro";
2
+ import type { AstroMermaidOptions } from "astro-mermaid";
3
+ /**
4
+ * Configuration options for the Northwestern Mermaid integration.
5
+ *
6
+ * Extends `astro-mermaid` options with a `toolbar` toggle that controls
7
+ * the hover toolbar (fullscreen, download, copy) on rendered diagrams.
8
+ *
9
+ * @see {@link northwesternMermaid} for the integration factory
10
+ */
11
+ export interface NorthwesternMermaidOptions extends AstroMermaidOptions {
12
+ /**
13
+ * Show the hover toolbar (fullscreen, download SVG, copy source) on diagrams.
14
+ *
15
+ * @default true
16
+ */
17
+ toolbar?: boolean;
18
+ }
19
+ /**
20
+ * Theme mode for Mermaid color palette generation.
21
+ *
22
+ * Maps to the `data-theme` attribute on `<html>`: `"light"` for the default
23
+ * palette, `"dark"` for the inverted palette with lighter primary colors
24
+ * and darker canvas backgrounds.
25
+ */
26
+ export type NorthwesternMermaidMode = "light" | "dark";
27
+ /**
28
+ * Generate a complete `astro-mermaid` config with Northwestern-branded colors
29
+ * for the given theme mode.
30
+ *
31
+ * Builds a Mermaid `themeVariables` object from the Northwestern brand palette,
32
+ * deriving all node, edge, label, chart, and diagram-specific colors from a
33
+ * small set of brand primaries via `khroma` color manipulation.
34
+ *
35
+ * @param mode - `"light"` or `"dark"`. Controls canvas, text, and primary color values.
36
+ * @returns A complete `AstroMermaidOptions` object ready to pass to `astro-mermaid`.
37
+ *
38
+ * @example
39
+ * ```ts
40
+ * import { createNorthwesternMermaidConfig } from "@nu-appdev/northwestern-starlight-theme/mermaid";
41
+ *
42
+ * const darkConfig = createNorthwesternMermaidConfig("dark");
43
+ * // darkConfig.mermaidConfig.themeVariables contains all colors
44
+ * ```
45
+ */
46
+ export declare function createNorthwesternMermaidConfig(mode: NorthwesternMermaidMode): AstroMermaidOptions;
47
+ /**
48
+ * Pre-built light-mode Mermaid config with Northwestern brand colors.
49
+ *
50
+ * Used as the default when Mermaid is auto-detected. Override individual
51
+ * `themeVariables` by passing a `mermaid` object to {@link northwesternTheme}
52
+ * in `index.ts`, or use {@link createNorthwesternMermaidConfig} for full control.
53
+ */
54
+ export declare const defaultMermaidConfig: AstroMermaidOptions;
55
+ /**
56
+ * Pre-built dark-mode Mermaid config with Northwestern brand colors.
57
+ *
58
+ * Applied at runtime when the user switches to dark mode. The toolbar script
59
+ * re-renders diagrams with this config via `window.__NU_MERMAID_CONFIGS__.dark`.
60
+ */
61
+ export declare const darkMermaidConfig: AstroMermaidOptions;
62
+ /**
63
+ * Create an Astro integration that registers Northwestern-branded Mermaid diagrams.
64
+ *
65
+ * Wraps `astro-mermaid` with Northwestern color palettes for both light and dark
66
+ * modes, and injects the toolbar script (fullscreen viewer, download, copy).
67
+ *
68
+ * **Note:** `defineNorthwesternConfig` handles Mermaid integration ordering.
69
+ * This function is only needed for manual setups.
70
+ *
71
+ * **Must be added before `starlight()` in the `integrations` array.** The
72
+ * `astro-mermaid` remark plugin needs to register before Starlight's rehype
73
+ * processing, and Astro processes integrations in order. Placing it after
74
+ * `starlight()` causes Mermaid code blocks to be treated as plain code.
75
+ *
76
+ * @param options - Merged with Northwestern defaults. Set `toolbar: false` to
77
+ * disable the hover toolbar.
78
+ * @returns An Astro integration to add to `integrations` in your Astro config.
79
+ *
80
+ * @example Manual setup
81
+ * ```ts
82
+ * import { northwesternMermaid } from "@nu-appdev/northwestern-starlight-theme/mermaid";
83
+ * import northwesternTheme from "@nu-appdev/northwestern-starlight-theme";
84
+ *
85
+ * export default defineConfig({
86
+ * integrations: [
87
+ * northwesternMermaid(), // Must come before starlight()
88
+ * starlight({
89
+ * plugins: [northwesternTheme()],
90
+ * title: "My Docs",
91
+ * }),
92
+ * ],
93
+ * });
94
+ * ```
95
+ */
96
+ export declare function northwesternMermaid(options?: NorthwesternMermaidOptions): AstroIntegration;
97
+ //# sourceMappingURL=mermaid.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"mermaid.d.ts","sourceRoot":"","sources":["../mermaid.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,OAAO,CAAC;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,eAAe,CAAC;AAIzD;;;;;;;GAOG;AACH,MAAM,WAAW,0BAA2B,SAAQ,mBAAmB;IACnE;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;CACrB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,uBAAuB,GAAG,OAAO,GAAG,MAAM,CAAC;AAwUvD;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,+BAA+B,CAAC,IAAI,EAAE,uBAAuB,GAAG,mBAAmB,CAmBlG;AAED;;;;;;GAMG;AACH,eAAO,MAAM,oBAAoB,EAAE,mBAA8D,CAAC;AAElG;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,mBAA6D,CAAC;AAE9F;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,0BAA+B,GAAG,gBAAgB,CAqD9F"}
@@ -0,0 +1,141 @@
1
+ import { z } from "zod";
2
+ /**
3
+ * Non-empty string helper used when reading titles from mixed Starlight config shapes.
4
+ *
5
+ * Trims surrounding whitespace and rejects empty strings after trimming.
6
+ */
7
+ export declare const nonEmptyStringSchema: z.ZodString;
8
+ /**
9
+ * Top-level configuration for the Northwestern Starlight theme plugin.
10
+ *
11
+ * With `defineNorthwesternConfig`, pass this object as the `theme` key.
12
+ * With `northwesternTheme()` directly, pass it as the function argument.
13
+ */
14
+ export declare const northwesternThemeConfigSchema: z.ZodObject<{
15
+ /**
16
+ * Homepage hero layout configuration.
17
+ */
18
+ homepage: z.ZodOptional<z.ZodObject<{
19
+ layout: z.ZodOptional<z.ZodEnum<{
20
+ centered: "centered";
21
+ split: "split";
22
+ }>>;
23
+ showTitle: z.ZodOptional<z.ZodBoolean>;
24
+ imageWidth: z.ZodOptional<z.ZodString>;
25
+ }, z.core.$strict>>;
26
+ /**
27
+ * Mermaid diagram support.
28
+ *
29
+ * - `true`: auto-detect Mermaid packages and enable support when available
30
+ * - `false`: disable Mermaid integration entirely
31
+ * - `object`: merge custom Mermaid options with Northwestern defaults
32
+ */
33
+ mermaid: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
34
+ /**
35
+ * Open Graph image generation.
36
+ *
37
+ * Generates a branded 1200x630 image for each docs page when `site`
38
+ * is configured in `astro.config.ts`.
39
+ */
40
+ ogImage: z.ZodOptional<z.ZodBoolean>;
41
+ }, z.core.$strict>;
42
+ /**
43
+ * Configuration options for the standalone Northwestern Mermaid integration.
44
+ *
45
+ * This intentionally validates only the Northwestern-owned wrapper surface and
46
+ * allows additional upstream `astro-mermaid` options to pass through unchanged.
47
+ */
48
+ export declare const northwesternMermaidOptionsSchema: z.ZodObject<{
49
+ /**
50
+ * Show the hover toolbar on rendered diagrams.
51
+ *
52
+ * The toolbar provides fullscreen, download, and copy-source actions.
53
+ */
54
+ toolbar: z.ZodOptional<z.ZodBoolean>;
55
+ }, z.core.$loose>;
56
+ /**
57
+ * Configuration options for `defineNorthwesternConfig()`.
58
+ *
59
+ * This schema validates the Northwestern-owned wrapper surface while allowing
60
+ * the rest of Astro's top-level config to pass through untouched.
61
+ */
62
+ export declare const northwesternConfigOptionsSchema: z.ZodObject<{
63
+ /**
64
+ * Full Starlight configuration object.
65
+ *
66
+ * This is forwarded to `starlight()` after Northwestern defaults and
67
+ * helper-managed integration ordering are applied.
68
+ */
69
+ starlight: z.ZodObject<{}, z.core.$loose>;
70
+ /**
71
+ * Northwestern theme plugin options.
72
+ */
73
+ theme: z.ZodOptional<z.ZodObject<{
74
+ /**
75
+ * Homepage hero layout configuration.
76
+ */
77
+ homepage: z.ZodOptional<z.ZodObject<{
78
+ layout: z.ZodOptional<z.ZodEnum<{
79
+ centered: "centered";
80
+ split: "split";
81
+ }>>;
82
+ showTitle: z.ZodOptional<z.ZodBoolean>;
83
+ imageWidth: z.ZodOptional<z.ZodString>;
84
+ }, z.core.$strict>>;
85
+ /**
86
+ * Mermaid diagram support.
87
+ *
88
+ * - `true`: auto-detect Mermaid packages and enable support when available
89
+ * - `false`: disable Mermaid integration entirely
90
+ * - `object`: merge custom Mermaid options with Northwestern defaults
91
+ */
92
+ mermaid: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodRecord<z.ZodString, z.ZodUnknown>]>>;
93
+ /**
94
+ * Open Graph image generation.
95
+ *
96
+ * Generates a branded 1200x630 image for each docs page when `site`
97
+ * is configured in `astro.config.ts`.
98
+ */
99
+ ogImage: z.ZodOptional<z.ZodBoolean>;
100
+ }, z.core.$strict>>;
101
+ /**
102
+ * Mermaid diagram support.
103
+ *
104
+ * - `true`: add Northwestern Mermaid with defaults
105
+ * - `false`: disable Mermaid
106
+ * - `object`: add Northwestern Mermaid with custom options
107
+ */
108
+ mermaid: z.ZodOptional<z.ZodUnion<readonly [z.ZodBoolean, z.ZodObject<{
109
+ /**
110
+ * Show the hover toolbar on rendered diagrams.
111
+ *
112
+ * The toolbar provides fullscreen, download, and copy-source actions.
113
+ */
114
+ toolbar: z.ZodOptional<z.ZodBoolean>;
115
+ }, z.core.$loose>]>>;
116
+ /**
117
+ * Additional Starlight plugins to register after the Northwestern theme.
118
+ */
119
+ plugins: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
120
+ /**
121
+ * Escape hatch for advanced integration ordering.
122
+ *
123
+ * Use `before` for integrations that must run before Mermaid/Starlight,
124
+ * and `after` for integrations that should be appended after Starlight.
125
+ */
126
+ integrations: z.ZodOptional<z.ZodObject<{
127
+ /**
128
+ * Integrations added before Mermaid and Starlight.
129
+ */
130
+ before: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
131
+ /**
132
+ * Integrations added after Starlight.
133
+ */
134
+ after: z.ZodOptional<z.ZodArray<z.ZodUnknown>>;
135
+ }, z.core.$strict>>;
136
+ }, z.core.$loose>;
137
+ /**
138
+ * Validate a public config object and throw a friendly package-scoped error on failure.
139
+ */
140
+ export declare function validateSchema<T>(schema: z.ZodType<T>, value: unknown, scope: string): T;
141
+ //# sourceMappingURL=config-schema.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-schema.d.ts","sourceRoot":"","sources":["../../src/config-schema.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB;;;;GAIG;AACH,eAAO,MAAM,oBAAoB,aAIoD,CAAC;AA6DtF;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B;IAElC;;OAEG;;;;;;;;;IAKH;;;;;;OAMG;;IASH;;;;;OAKG;;kBAOL,CAAC;AAEP;;;;;GAKG;AACH,eAAO,MAAM,gCAAgC;IAErC;;;;OAIG;;iBAQL,CAAC;AAEP;;;;;GAKG;AACH,eAAO,MAAM,+BAA+B;IAEpC;;;;;OAKG;;IAKH;;OAEG;;QA9EH;;WAEG;;;;;;;;;QAKH;;;;;;WAMG;;QASH;;;;;WAKG;;;IAwDH;;;;;;OAMG;;QA7CH;;;;WAIG;;;IA+CH;;OAEG;;IAKH;;;;;OAKG;;QAGK;;WAEG;;QAKH;;WAEG;;;iBAYb,CAAC;AA2BP;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,GAAG,CAAC,CAQxF"}
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Rehype plugin that wraps `<table>` elements in a scrollable container at build time.
3
+ *
4
+ * Inserts a `<div class="nu-table-scroll">` around each table with `tabindex="0"`,
5
+ * `role="region"`, and `aria-label` for keyboard scrollability. Runs during the
6
+ * Astro build so the HTML arrives with the wrapper already in place.
7
+ */
8
+ import type { Root } from "hast";
9
+ export default function rehypeTableScroll(): (tree: Root) => void;
10
+ //# sourceMappingURL=rehype-table-scroll.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rehype-table-scroll.d.ts","sourceRoot":"","sources":["../../src/rehype-table-scroll.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AACH,OAAO,KAAK,EAAkB,IAAI,EAAE,MAAM,MAAM,CAAC;AAgCjD,MAAM,CAAC,OAAO,UAAU,iBAAiB,KAC7B,MAAM,IAAI,UACrB"}
@@ -0,0 +1,35 @@
1
+ import type { StarlightExpressiveCodeOptions } from "@astrojs/starlight/expressive-code";
2
+ import { pluginLineNumbers } from "@expressive-code/plugin-line-numbers";
3
+
4
+ /**
5
+ * Pre-configured Expressive Code options for the Northwestern Starlight theme.
6
+ *
7
+ * Adds the line-numbers plugin and sets sensible defaults. Pass your own
8
+ * options to merge with the Northwestern defaults.
9
+ *
10
+ * **Note:** `defineNorthwesternConfig` includes these options.
11
+ * This helper is only needed for manual setups using `northwesternTheme()` directly.
12
+ *
13
+ * @example
14
+ * ```ts
15
+ * // ec.config.mjs (manual setup only)
16
+ * import { defineEcConfig } from "@astrojs/starlight/expressive-code";
17
+ * import { northwesternExpressiveCode } from "@nu-appdev/northwestern-starlight-theme/expressive-code";
18
+ *
19
+ * export default defineEcConfig(northwesternExpressiveCode());
20
+ * ```
21
+ */
22
+ export function northwesternExpressiveCode(
23
+ config: StarlightExpressiveCodeOptions = {},
24
+ ): StarlightExpressiveCodeOptions {
25
+ const { defaultProps, plugins, ...rest } = config;
26
+
27
+ return {
28
+ ...rest,
29
+ plugins: [pluginLineNumbers(), ...(plugins ?? [])],
30
+ defaultProps: {
31
+ showLineNumbers: false,
32
+ ...(defaultProps ?? {}),
33
+ },
34
+ };
35
+ }
package/index.ts CHANGED
@@ -1,9 +1,11 @@
1
1
  import { copyFileSync, mkdirSync, readFileSync } from "node:fs";
2
2
  import type { IncomingMessage, ServerResponse } from "node:http";
3
+ import { createRequire } from "node:module";
3
4
  import { dirname, join } from "node:path";
4
5
  import { fileURLToPath } from "node:url";
5
6
  import type { StarlightPlugin } from "@astrojs/starlight/types";
6
- import rehypeTableScroll from "./src/rehype-table-scroll.ts";
7
+ import { nonEmptyStringSchema, northwesternThemeConfigSchema, validateSchema } from "./src/config-schema";
8
+ import rehypeTableScroll from "./src/rehype-table-scroll";
7
9
 
8
10
  /**
9
11
  * Configuration for the homepage hero section.
@@ -53,21 +55,16 @@ export interface NorthwesternHomepageConfig {
53
55
  /**
54
56
  * Top-level configuration for the Northwestern Starlight theme plugin.
55
57
  *
56
- * Pass to {@link northwesternTheme} when registering the plugin in your
57
- * Starlight config. All properties are optional and have sensible defaults.
58
+ * With `defineNorthwesternConfig`, pass these as the `theme` key.
59
+ * With `northwesternTheme()` directly, pass them as the function argument.
60
+ * All properties are optional.
58
61
  *
59
62
  * @example
60
63
  * ```ts
61
- * // astro.config.ts
62
- * import northwesternTheme from "@nu-appdev/northwestern-starlight-theme";
63
- *
64
- * export default defineConfig({
65
- * integrations: [
66
- * starlight({
67
- * plugins: [northwesternTheme({ homepage: { layout: "split" } })],
68
- * }),
69
- * ],
70
- * });
64
+ * defineNorthwesternConfig({
65
+ * starlight: { title: "My Docs" },
66
+ * theme: { homepage: { layout: "split" } },
67
+ * })
71
68
  * ```
72
69
  *
73
70
  * @see {@link NorthwesternHomepageConfig} for homepage hero options
@@ -86,12 +83,35 @@ export interface NorthwesternThemeConfig {
86
83
  * - `true` (default) — auto-detect: enables Mermaid if `astro-mermaid` and `mermaid`
87
84
  * are installed, skips silently if they are not
88
85
  * - `false` — disables Mermaid entirely
89
- * - `object` — enables Mermaid with custom {@link https://mermaid.js.org/config/schema-docs/config.html | MermaidConfig}
90
- * merged with Northwestern defaults
86
+ * - `object` — enables Mermaid with custom Mermaid config merged with Northwestern defaults
91
87
  *
92
88
  * @default true
93
89
  */
94
90
  mermaid?: boolean | Record<string, unknown>;
91
+
92
+ /**
93
+ * Open Graph image generation.
94
+ *
95
+ * Generates branded OG images (1200x630 PNG) for every docs page with the
96
+ * page title and description on a Northwestern purple background.
97
+ *
98
+ * Requires `site` to be set in `astro.config.ts` for absolute image URLs.
99
+ *
100
+ * @default true
101
+ */
102
+ ogImage?: boolean;
103
+ }
104
+
105
+ function getSiteTitle(title: unknown): string {
106
+ const parsedTitle = nonEmptyStringSchema.safeParse(title);
107
+ if (parsedTitle.success) return parsedTitle.data;
108
+ if (title && typeof title === "object") {
109
+ for (const value of Object.values(title as Record<string, unknown>)) {
110
+ const parsedValue = nonEmptyStringSchema.safeParse(value);
111
+ if (parsedValue.success) return parsedValue.data;
112
+ }
113
+ }
114
+ return "";
95
115
  }
96
116
 
97
117
  /**
@@ -116,10 +136,14 @@ const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
116
136
  * component overrides (Hero, ThemeToggle, EditLink), and optional Mermaid diagram
117
137
  * support with branded color schemes.
118
138
  *
139
+ * **Recommended:** Use {@link https://github.com/NUAppDev/northwestern-starlight-theme | defineNorthwesternConfig}
140
+ * from `@nu-appdev/northwestern-starlight-theme/config` instead. It handles
141
+ * integration ordering, Mermaid, and Expressive Code (line numbers, GitHub themes).
142
+ *
119
143
  * @param config - Theme configuration. All properties optional.
120
144
  * @returns A Starlight plugin to pass to `plugins` in your Starlight config.
121
145
  *
122
- * @example Basic usage (all defaults)
146
+ * @example Manual setup
123
147
  * ```ts
124
148
  * starlight({ plugins: [northwesternTheme()] })
125
149
  * ```
@@ -137,19 +161,35 @@ const RESOLVED_VIRTUAL_MODULE_ID = `\0${VIRTUAL_MODULE_ID}`;
137
161
  * ```
138
162
  */
139
163
  export default function northwesternTheme(config: NorthwesternThemeConfig = {}): StarlightPlugin {
140
- const { homepage = {}, mermaid = true } = config;
164
+ const {
165
+ homepage = {},
166
+ mermaid = true,
167
+ ogImage = true,
168
+ } = validateSchema(northwesternThemeConfigSchema, config, "theme config");
141
169
  const themeConfig = {
142
170
  homepage: {
143
171
  layout: homepage.layout ?? "centered",
144
172
  showTitle: homepage.showTitle ?? true,
145
173
  imageWidth: homepage.imageWidth ?? "500px",
146
174
  },
175
+ ogImage: {
176
+ enabled: false, // set to true in config:setup when ogImage is enabled
177
+ siteTitle: "",
178
+ logoPath: "",
179
+ resvgWasmPath: "",
180
+ },
147
181
  };
148
182
 
149
183
  return {
150
184
  name: "northwestern-starlight-theme",
151
185
  hooks: {
152
- async "config:setup"({ config: starlightConfig, updateConfig, addIntegration, logger }) {
186
+ async "config:setup"({
187
+ config: starlightConfig,
188
+ updateConfig,
189
+ addIntegration,
190
+ addRouteMiddleware,
191
+ logger,
192
+ }) {
153
193
  // Apply default favicon if consumer hasn't set one
154
194
  const consumerSetFavicon =
155
195
  starlightConfig.favicon &&
@@ -244,6 +284,53 @@ export default function northwesternTheme(config: NorthwesternThemeConfig = {}):
244
284
  }
245
285
  }
246
286
 
287
+ // OG image generation (bundled via satori + @resvg/resvg-wasm)
288
+ if (ogImage) {
289
+ const siteTitle = getSiteTitle(starlightConfig.title);
290
+
291
+ if (!siteTitle) {
292
+ logger.warn(
293
+ "Open Graph image generation was disabled because Starlight `title` is empty.\n" +
294
+ " Set `title` in starlight({...}) so OG images and structured data have a site name.",
295
+ );
296
+ } else {
297
+ themeConfig.ogImage.logoPath = faviconPath;
298
+ themeConfig.ogImage.siteTitle = siteTitle;
299
+
300
+ addRouteMiddleware({
301
+ entrypoint: "@nu-appdev/northwestern-starlight-theme/src/og/route-middleware",
302
+ order: "post",
303
+ });
304
+
305
+ addIntegration({
306
+ name: "northwestern-theme-og-image",
307
+ hooks: {
308
+ "astro:config:setup": ({ config: astroConfig, injectRoute }) => {
309
+ if (!astroConfig.site) {
310
+ logger.warn(
311
+ "Open Graph image generation was disabled because `site` is not set.\n" +
312
+ " Set `site` in astro.config.ts to enable absolute OG image URLs.",
313
+ );
314
+ return;
315
+ }
316
+
317
+ themeConfig.ogImage.enabled = true;
318
+ const require = createRequire(import.meta.url);
319
+ themeConfig.ogImage.resvgWasmPath = require.resolve(
320
+ "@resvg/resvg-wasm/index_bg.wasm",
321
+ );
322
+
323
+ injectRoute({
324
+ pattern: "/og/[...slug]",
325
+ entrypoint: "@nu-appdev/northwestern-starlight-theme/src/og/endpoint.ts",
326
+ });
327
+ logger.info("OG image generation enabled");
328
+ },
329
+ },
330
+ });
331
+ }
332
+ }
333
+
247
334
  updateConfig({
248
335
  ...(consumerSetFavicon ? {} : { favicon: "/favicon.png" }),
249
336
  components: {
package/mermaid.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { AstroIntegration } from "astro";
2
- import mermaid, { type AstroMermaidOptions } from "astro-mermaid";
2
+ import type { AstroMermaidOptions } from "astro-mermaid";
3
3
  import { darken, isDark, lighten, mix, transparentize } from "khroma";
4
+ import { northwesternMermaidOptionsSchema, validateSchema } from "./src/config-schema";
4
5
 
5
6
  /**
6
7
  * Configuration options for the Northwestern Mermaid integration.
@@ -417,6 +418,9 @@ export const darkMermaidConfig: AstroMermaidOptions = createNorthwesternMermaidC
417
418
  * Wraps `astro-mermaid` with Northwestern color palettes for both light and dark
418
419
  * modes, and injects the toolbar script (fullscreen viewer, download, copy).
419
420
  *
421
+ * **Note:** `defineNorthwesternConfig` handles Mermaid integration ordering.
422
+ * This function is only needed for manual setups.
423
+ *
420
424
  * **Must be added before `starlight()` in the `integrations` array.** The
421
425
  * `astro-mermaid` remark plugin needs to register before Starlight's rehype
422
426
  * processing, and Astro processes integrations in order. Placing it after
@@ -426,7 +430,7 @@ export const darkMermaidConfig: AstroMermaidOptions = createNorthwesternMermaidC
426
430
  * disable the hover toolbar.
427
431
  * @returns An Astro integration to add to `integrations` in your Astro config.
428
432
  *
429
- * @example
433
+ * @example Manual setup
430
434
  * ```ts
431
435
  * import { northwesternMermaid } from "@nu-appdev/northwestern-starlight-theme/mermaid";
432
436
  * import northwesternTheme from "@nu-appdev/northwestern-starlight-theme";
@@ -443,7 +447,11 @@ export const darkMermaidConfig: AstroMermaidOptions = createNorthwesternMermaidC
443
447
  * ```
444
448
  */
445
449
  export function northwesternMermaid(options: NorthwesternMermaidOptions = {}): AstroIntegration {
446
- const { toolbar = true, ...overrides } = options;
450
+ const { toolbar = true, ...overrides } = validateSchema(
451
+ northwesternMermaidOptionsSchema,
452
+ options,
453
+ "Mermaid config",
454
+ );
447
455
  const lightConfig = createNorthwesternMermaidConfig("light");
448
456
  const darkConfig = createNorthwesternMermaidConfig("dark");
449
457
 
@@ -463,19 +471,21 @@ export function northwesternMermaid(options: NorthwesternMermaidOptions = {}): A
463
471
 
464
472
  const mergedLightMermaidConfig = mergeWithOverrides(lightConfig.mermaidConfig as Record<string, unknown>);
465
473
  const mergedDarkMermaidConfig = mergeWithOverrides(darkConfig.mermaidConfig as Record<string, unknown>);
466
-
467
- const mermaidIntegration = mermaid({
468
- ...lightConfig,
469
- ...overrides,
470
- enableLog: false,
471
- mermaidConfig: mergedLightMermaidConfig,
472
- });
474
+ const mermaidModulePromise = import("astro-mermaid");
473
475
 
474
476
  return {
475
477
  name: "northwestern-mermaid",
476
478
  hooks: {
477
- "astro:config:setup"(params) {
478
- mermaidIntegration.hooks["astro:config:setup"]?.(params);
479
+ async "astro:config:setup"(params) {
480
+ const { default: mermaid } = await mermaidModulePromise;
481
+ const mermaidIntegration = mermaid({
482
+ ...lightConfig,
483
+ ...overrides,
484
+ enableLog: false,
485
+ mermaidConfig: mergedLightMermaidConfig,
486
+ });
487
+
488
+ await mermaidIntegration.hooks["astro:config:setup"]?.(params);
479
489
 
480
490
  params.injectScript(
481
491
  "page",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nu-appdev/northwestern-starlight-theme",
3
- "version": "1.3.2",
3
+ "version": "1.5.0",
4
4
  "description": "A Northwestern-branded theme for Astro Starlight",
5
5
  "license": "MIT",
6
6
  "author": "Danny Foster <danny@northwestern.edu>",
@@ -28,14 +28,19 @@
28
28
  },
29
29
  "exports": {
30
30
  ".": {
31
- "types": "./index.ts",
31
+ "types": "./dist/index.d.ts",
32
32
  "import": "./index.ts"
33
33
  },
34
34
  "./expressive-code": {
35
- "import": "./expressive-code.mjs"
35
+ "types": "./dist/expressive-code.d.ts",
36
+ "import": "./expressive-code.ts"
37
+ },
38
+ "./config": {
39
+ "types": "./dist/config.d.ts",
40
+ "import": "./config.ts"
36
41
  },
37
42
  "./mermaid": {
38
- "types": "./mermaid.ts",
43
+ "types": "./dist/mermaid.d.ts",
39
44
  "import": "./mermaid.ts"
40
45
  },
41
46
  "./components": {
@@ -44,19 +49,25 @@
44
49
  },
45
50
  "./src/styles/components/*": "./src/styles/components/*",
46
51
  "./src/components/*": "./src/components/*",
52
+ "./src/og/*": "./src/og/*",
47
53
  "./src/scripts/*": "./src/scripts/*",
48
54
  "./src/styles/*": "./src/styles/*"
49
55
  },
50
56
  "files": [
51
57
  "CHANGELOG.md",
52
- "expressive-code.mjs",
58
+ "dist/",
59
+ "config.ts",
60
+ "expressive-code.ts",
53
61
  "index.ts",
54
62
  "mermaid.ts",
55
63
  "src/"
56
64
  ],
57
65
  "dependencies": {
58
66
  "@expressive-code/plugin-line-numbers": "^0.41.7",
59
- "khroma": "^2.1.0"
67
+ "@resvg/resvg-wasm": "^2.6.2",
68
+ "khroma": "^2.1.0",
69
+ "satori": "^0.26.0",
70
+ "zod": "^4.3.6"
60
71
  },
61
72
  "peerDependencies": {
62
73
  "@astrojs/starlight": ">=0.32.0",
@@ -73,6 +84,10 @@
73
84
  }
74
85
  },
75
86
  "devDependencies": {
76
- "@types/hast": "^3.0.4"
87
+ "@types/hast": "^3.0.4",
88
+ "typescript": "^6.0.2"
89
+ },
90
+ "scripts": {
91
+ "build": "tsc -p tsconfig.build.json --noCheck"
77
92
  }
78
93
  }