@specglass/core 0.0.12 → 0.0.13

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.
@@ -21,8 +21,12 @@ export const DEFAULT_CONFIG = {
21
21
  footer: undefined,
22
22
  favicon: undefined,
23
23
  socialImage: undefined,
24
- headerLinks: undefined,
24
+ headerLinks: [
25
+ { label: "Documentation", href: "/" },
26
+ { label: "API Reference", href: "/api-reference" },
27
+ ],
25
28
  colors: undefined,
29
+ socialLinks: undefined,
26
30
  },
27
31
  build: {
28
32
  outDir: "dist",
@@ -180,6 +180,26 @@ export declare const configSchema: z.ZodObject<{
180
180
  dark?: string | undefined;
181
181
  } | undefined;
182
182
  }>>;
183
+ /** Social/external links shown in the footer */
184
+ socialLinks: z.ZodOptional<z.ZodArray<z.ZodObject<{
185
+ /** Display label for the link */
186
+ label: z.ZodString;
187
+ /** URL the link navigates to */
188
+ href: z.ZodString;
189
+ /** Optional icon identifier (e.g., 'github', 'twitter', 'discord') */
190
+ icon: z.ZodOptional<z.ZodString>;
191
+ }, "strip", z.ZodTypeAny, {
192
+ label: string;
193
+ href: string;
194
+ icon?: string | undefined;
195
+ }, {
196
+ label: string;
197
+ href: string;
198
+ icon?: string | undefined;
199
+ }>, "many">>;
200
+ /** Base URL for "Edit on GitHub" links. File path will be appended.
201
+ * Example: "https://github.com/org/repo/edit/main/docs" */
202
+ editUrl: z.ZodOptional<z.ZodString>;
183
203
  }, "strip", z.ZodTypeAny, {
184
204
  logo?: string | undefined;
185
205
  primaryColor?: string | undefined;
@@ -213,6 +233,12 @@ export declare const configSchema: z.ZodObject<{
213
233
  dark?: string | undefined;
214
234
  } | undefined;
215
235
  } | undefined;
236
+ socialLinks?: {
237
+ label: string;
238
+ href: string;
239
+ icon?: string | undefined;
240
+ }[] | undefined;
241
+ editUrl?: string | undefined;
216
242
  }, {
217
243
  logo?: string | undefined;
218
244
  primaryColor?: string | undefined;
@@ -246,6 +272,12 @@ export declare const configSchema: z.ZodObject<{
246
272
  dark?: string | undefined;
247
273
  } | undefined;
248
274
  } | undefined;
275
+ socialLinks?: {
276
+ label: string;
277
+ href: string;
278
+ icon?: string | undefined;
279
+ }[] | undefined;
280
+ editUrl?: string | undefined;
249
281
  }>>;
250
282
  /** Build output configuration */
251
283
  build: z.ZodOptional<z.ZodObject<{
@@ -305,6 +337,12 @@ export declare const configSchema: z.ZodObject<{
305
337
  dark?: string | undefined;
306
338
  } | undefined;
307
339
  } | undefined;
340
+ socialLinks?: {
341
+ label: string;
342
+ href: string;
343
+ icon?: string | undefined;
344
+ }[] | undefined;
345
+ editUrl?: string | undefined;
308
346
  } | undefined;
309
347
  build?: {
310
348
  outDir?: string | undefined;
@@ -358,6 +396,12 @@ export declare const configSchema: z.ZodObject<{
358
396
  dark?: string | undefined;
359
397
  } | undefined;
360
398
  } | undefined;
399
+ socialLinks?: {
400
+ label: string;
401
+ href: string;
402
+ icon?: string | undefined;
403
+ }[] | undefined;
404
+ editUrl?: string | undefined;
361
405
  } | undefined;
362
406
  build?: {
363
407
  outDir?: string | undefined;
@@ -20,6 +20,15 @@ const headerLinkSchema = z.object({
20
20
  /** URL the link navigates to */
21
21
  href: z.string(),
22
22
  });
23
+ /** Schema for a social/external link shown in the footer */
24
+ const socialLinkSchema = z.object({
25
+ /** Display label for the link */
26
+ label: z.string(),
27
+ /** URL the link navigates to */
28
+ href: z.string(),
29
+ /** Optional icon identifier (e.g., 'github', 'twitter', 'discord') */
30
+ icon: z.string().optional(),
31
+ });
23
32
  /**
24
33
  * A color value that supports both light and dark modes.
25
34
  *
@@ -70,6 +79,11 @@ const themeSchema = z.object({
70
79
  headerLinks: z.array(headerLinkSchema).optional(),
71
80
  /** Semantic color tokens for CSS custom property theming */
72
81
  colors: colorsSchema.optional(),
82
+ /** Social/external links shown in the footer */
83
+ socialLinks: z.array(socialLinkSchema).optional(),
84
+ /** Base URL for "Edit on GitHub" links. File path will be appended.
85
+ * Example: "https://github.com/org/repo/edit/main/docs" */
86
+ editUrl: z.string().url().optional(),
73
87
  });
74
88
  /** Schema for OpenAPI configuration */
75
89
  const openApiSchema = z.object({
@@ -37,6 +37,45 @@ function isAlreadyWrapped(parent) {
37
37
  return true;
38
38
  return false;
39
39
  }
40
+ function parseMeta(meta, lang) {
41
+ const result = {
42
+ showLineNumbers: false,
43
+ isDiff: lang === "diff",
44
+ highlightLines: [],
45
+ };
46
+ if (!meta)
47
+ return result;
48
+ // Extract filename="..." or title="..."
49
+ const filenameMatch = meta.match(/(?:filename|title)=["']([^"']+)["']/);
50
+ if (filenameMatch) {
51
+ result.filename = filenameMatch[1];
52
+ }
53
+ // Check for showLineNumbers flag
54
+ if (/\bshowLineNumbers\b/.test(meta)) {
55
+ result.showLineNumbers = true;
56
+ }
57
+ // Check for diff flag
58
+ if (/\bdiff\b/.test(meta)) {
59
+ result.isDiff = true;
60
+ }
61
+ // Parse highlight ranges: {1,3-5,8}
62
+ const highlightMatch = meta.match(/\{([\d,\s-]+)\}/);
63
+ if (highlightMatch) {
64
+ const ranges = highlightMatch[1].split(",").map((s) => s.trim());
65
+ for (const range of ranges) {
66
+ if (range.includes("-")) {
67
+ const [start, end] = range.split("-").map(Number);
68
+ for (let i = start; i <= end; i++) {
69
+ result.highlightLines.push(i);
70
+ }
71
+ }
72
+ else {
73
+ result.highlightLines.push(Number(range));
74
+ }
75
+ }
76
+ }
77
+ return result;
78
+ }
40
79
  export function rehypeCodeBlocks() {
41
80
  return (tree) => {
42
81
  // Process in reverse to avoid index shifts when splicing
@@ -55,22 +94,64 @@ export function rehypeCodeBlocks() {
55
94
  return;
56
95
  // Extract raw text for clipboard
57
96
  const rawCode = extractText(codeEl).trimEnd();
58
- // Build wrapper: <div class="code-block code-block--fenced">
97
+ // Get language from class="language-xxx"
98
+ const codeClasses = Array.isArray(codeEl.properties?.className)
99
+ ? codeEl.properties.className
100
+ : [];
101
+ const langClass = codeClasses.find((c) => typeof c === "string" && c.startsWith("language-"));
102
+ const lang = langClass ? langClass.replace("language-", "") : "text";
103
+ // Parse meta string from Astro/Shiki
104
+ const meta = (node.properties?.["data-meta"] ??
105
+ codeEl.properties?.["data-meta"] ??
106
+ node.data?.meta);
107
+ const parsed = parseMeta(meta, lang);
108
+ // Build CSS classes for the wrapper
109
+ const wrapperClasses = ["code-block", "code-block--fenced"];
110
+ if (parsed.showLineNumbers)
111
+ wrapperClasses.push("code-block--line-numbers");
112
+ if (parsed.isDiff)
113
+ wrapperClasses.push("code-block--diff");
114
+ if (parsed.highlightLines.length > 0)
115
+ wrapperClasses.push("code-block--has-highlights");
116
+ // Build children: optional header + body
117
+ const children = [];
118
+ // Add filename header if present
119
+ if (parsed.filename) {
120
+ children.push({
121
+ type: "element",
122
+ tagName: "div",
123
+ properties: { className: ["code-block-header"] },
124
+ children: [
125
+ {
126
+ type: "element",
127
+ tagName: "span",
128
+ properties: { className: ["code-block-title"] },
129
+ children: [{ type: "text", value: parsed.filename }],
130
+ },
131
+ ],
132
+ });
133
+ }
134
+ // Body
135
+ children.push({
136
+ type: "element",
137
+ tagName: "div",
138
+ properties: { className: ["code-block-body"] },
139
+ children: [node],
140
+ });
141
+ // Build wrapper div
59
142
  const wrapper = {
60
143
  type: "element",
61
144
  tagName: "div",
62
145
  properties: {
63
- className: ["code-block", "code-block--fenced"],
146
+ className: wrapperClasses,
64
147
  "data-code": rawCode,
148
+ ...(parsed.filename ? { "data-filename": parsed.filename } : {}),
149
+ ...(parsed.showLineNumbers ? { "data-line-numbers": "" } : {}),
150
+ ...(parsed.highlightLines.length > 0
151
+ ? { "data-highlight": parsed.highlightLines.join(",") }
152
+ : {}),
65
153
  },
66
- children: [
67
- {
68
- type: "element",
69
- tagName: "div",
70
- properties: { className: ["code-block-body"] },
71
- children: [node],
72
- },
73
- ],
154
+ children,
74
155
  };
75
156
  replacements.push({ parent: parent, index, wrapper });
76
157
  });
@@ -97,6 +97,7 @@ async function buildItems(dirPath, slugPrefix) {
97
97
  type: "external-link",
98
98
  href: entry.href,
99
99
  ...(entry.icon !== undefined && { icon: entry.icon }),
100
+ ...(entry.badge !== undefined && { badge: entry.badge }),
100
101
  });
101
102
  continue;
102
103
  }
@@ -112,6 +113,7 @@ async function buildItems(dirPath, slugPrefix) {
112
113
  children,
113
114
  ...(entry.collapsed !== undefined && { collapsed: entry.collapsed }),
114
115
  ...(entry.icon !== undefined && { icon: entry.icon }),
116
+ ...(entry.badge !== undefined && { badge: entry.badge }),
115
117
  });
116
118
  continue;
117
119
  }
@@ -124,6 +126,7 @@ async function buildItems(dirPath, slugPrefix) {
124
126
  slug: slugPrefix ? `${slugPrefix}/${fileSlug}` : fileSlug,
125
127
  type: "page",
126
128
  ...(entry.icon !== undefined && { icon: entry.icon }),
129
+ ...(entry.badge !== undefined && { badge: entry.badge }),
127
130
  });
128
131
  }
129
132
  // If no file or directory matches, skip silently (meta references a non-existent item)
@@ -17,6 +17,7 @@ function normalizeEntry(key, value) {
17
17
  ...(value.icon !== undefined && { icon: value.icon }),
18
18
  ...(value.href !== undefined && { href: value.href }),
19
19
  ...(value.hidden !== undefined && { hidden: value.hidden }),
20
+ ...(value.badge !== undefined && { badge: value.badge }),
20
21
  };
21
22
  }
22
23
  /**
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  /**
3
3
  * Zod schema for the object form of a `_meta.json` entry value.
4
4
  *
5
- * Supports: `title` (required), `collapsed`, `icon`, `href`, `hidden`.
5
+ * Supports: `title` (required), `collapsed`, `icon`, `href`, `hidden`, `badge`.
6
6
  */
7
7
  export declare const metaJsonEntryObjectSchema: z.ZodObject<{
8
8
  title: z.ZodString;
@@ -10,18 +10,21 @@ export declare const metaJsonEntryObjectSchema: z.ZodObject<{
10
10
  icon: z.ZodOptional<z.ZodString>;
11
11
  href: z.ZodOptional<z.ZodString>;
12
12
  hidden: z.ZodOptional<z.ZodBoolean>;
13
+ badge: z.ZodOptional<z.ZodString>;
13
14
  }, "strip", z.ZodTypeAny, {
14
15
  title: string;
15
16
  href?: string | undefined;
16
- collapsed?: boolean | undefined;
17
17
  icon?: string | undefined;
18
+ collapsed?: boolean | undefined;
18
19
  hidden?: boolean | undefined;
20
+ badge?: string | undefined;
19
21
  }, {
20
22
  title: string;
21
23
  href?: string | undefined;
22
- collapsed?: boolean | undefined;
23
24
  icon?: string | undefined;
25
+ collapsed?: boolean | undefined;
24
26
  hidden?: boolean | undefined;
27
+ badge?: string | undefined;
25
28
  }>;
26
29
  /**
27
30
  * Zod schema for a single `_meta.json` entry value.
@@ -35,18 +38,21 @@ export declare const metaJsonEntryValueSchema: z.ZodUnion<[z.ZodString, z.ZodObj
35
38
  icon: z.ZodOptional<z.ZodString>;
36
39
  href: z.ZodOptional<z.ZodString>;
37
40
  hidden: z.ZodOptional<z.ZodBoolean>;
41
+ badge: z.ZodOptional<z.ZodString>;
38
42
  }, "strip", z.ZodTypeAny, {
39
43
  title: string;
40
44
  href?: string | undefined;
41
- collapsed?: boolean | undefined;
42
45
  icon?: string | undefined;
46
+ collapsed?: boolean | undefined;
43
47
  hidden?: boolean | undefined;
48
+ badge?: string | undefined;
44
49
  }, {
45
50
  title: string;
46
51
  href?: string | undefined;
47
- collapsed?: boolean | undefined;
48
52
  icon?: string | undefined;
53
+ collapsed?: boolean | undefined;
49
54
  hidden?: boolean | undefined;
55
+ badge?: string | undefined;
50
56
  }>]>;
51
57
  /**
52
58
  * Zod schema for a complete `_meta.json` file.
@@ -60,17 +66,20 @@ export declare const metaJsonSchema: z.ZodRecord<z.ZodString, z.ZodUnion<[z.ZodS
60
66
  icon: z.ZodOptional<z.ZodString>;
61
67
  href: z.ZodOptional<z.ZodString>;
62
68
  hidden: z.ZodOptional<z.ZodBoolean>;
69
+ badge: z.ZodOptional<z.ZodString>;
63
70
  }, "strip", z.ZodTypeAny, {
64
71
  title: string;
65
72
  href?: string | undefined;
66
- collapsed?: boolean | undefined;
67
73
  icon?: string | undefined;
74
+ collapsed?: boolean | undefined;
68
75
  hidden?: boolean | undefined;
76
+ badge?: string | undefined;
69
77
  }, {
70
78
  title: string;
71
79
  href?: string | undefined;
72
- collapsed?: boolean | undefined;
73
80
  icon?: string | undefined;
81
+ collapsed?: boolean | undefined;
74
82
  hidden?: boolean | undefined;
83
+ badge?: string | undefined;
75
84
  }>]>>;
76
85
  export type MetaJsonSchemaInput = z.input<typeof metaJsonSchema>;
@@ -2,7 +2,7 @@ import { z } from "zod";
2
2
  /**
3
3
  * Zod schema for the object form of a `_meta.json` entry value.
4
4
  *
5
- * Supports: `title` (required), `collapsed`, `icon`, `href`, `hidden`.
5
+ * Supports: `title` (required), `collapsed`, `icon`, `href`, `hidden`, `badge`.
6
6
  */
7
7
  export const metaJsonEntryObjectSchema = z.object({
8
8
  title: z.string(),
@@ -10,6 +10,7 @@ export const metaJsonEntryObjectSchema = z.object({
10
10
  icon: z.string().optional(),
11
11
  href: z.string().optional(),
12
12
  hidden: z.boolean().optional(),
13
+ badge: z.string().optional(),
13
14
  });
14
15
  /**
15
16
  * Zod schema for a single `_meta.json` entry value.
@@ -22,6 +22,8 @@ export interface NavItem {
22
22
  href?: string;
23
23
  /** If true, item is excluded from the rendered tree */
24
24
  hidden?: boolean;
25
+ /** Optional badge label ("New", "Beta", "Deprecated", or custom string) */
26
+ badge?: string;
25
27
  }
26
28
  /**
27
29
  * The complete sidebar navigation tree.
@@ -41,6 +43,7 @@ export type MetaJsonEntryValue = string | {
41
43
  icon?: string;
42
44
  href?: string;
43
45
  hidden?: boolean;
46
+ badge?: string;
44
47
  };
45
48
  /**
46
49
  * A single parsed entry from `_meta.json`, preserving key order.
@@ -58,4 +61,6 @@ export interface MetaJsonEntry {
58
61
  href?: string;
59
62
  /** Whether to hide from navigation */
60
63
  hidden?: boolean;
64
+ /** Optional badge label */
65
+ badge?: string;
61
66
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@specglass/core",
3
- "version": "0.0.12",
3
+ "version": "0.0.13",
4
4
  "description": "Astro integration, config system, content collections, and OpenAPI spec parsing for Specglass",
5
5
  "type": "module",
6
6
  "license": "MIT",