radiant-docs 0.1.54 → 0.1.57

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 (34) hide show
  1. package/dist/index.js +3 -75
  2. package/package.json +2 -4
  3. package/template/astro.config.mjs +16 -26
  4. package/template/package-lock.json +18 -0
  5. package/template/package.json +1 -1
  6. package/template/scripts/generate-og-metadata.mjs +8 -4
  7. package/template/src/components/Footer.astro +13 -4
  8. package/template/src/components/Header.astro +26 -6
  9. package/template/src/components/SidebarLink.astro +3 -2
  10. package/template/src/components/SidebarSubgroup.astro +14 -13
  11. package/template/src/components/sidebar/SidebarEndpointLink.astro +13 -3
  12. package/template/src/components/sidebar/SidebarOpenApi.astro +2 -0
  13. package/template/src/components/sidebar/SidebarOpenApiPageLink.astro +1 -0
  14. package/template/src/components/user/Accordion.astro +0 -13
  15. package/template/src/components/user/Callout.astro +0 -29
  16. package/template/src/components/user/Card.astro +31 -204
  17. package/template/src/components/user/CardGradient.astro +8 -1
  18. package/template/src/components/user/Column.astro +0 -17
  19. package/template/src/components/user/Columns.astro +4 -153
  20. package/template/src/components/user/Image.astro +0 -28
  21. package/template/src/components/user/Step.astro +0 -10
  22. package/template/src/components/user/Tab.astro +0 -12
  23. package/template/src/components/user/Tabs.astro +2 -9
  24. package/template/src/content.config.ts +1 -1
  25. package/template/src/lib/code/code-block.ts +1 -1
  26. package/template/src/lib/mdx/remark-code-block-component.ts +1 -20
  27. package/template/src/lib/mdx/remark-resolve-internal-links.ts +150 -204
  28. package/template/src/lib/routes.ts +150 -29
  29. package/template/src/lib/utils.ts +127 -12
  30. package/template/src/lib/validation.ts +5 -2826
  31. package/template/src/pages/[...slug].astro +16 -0
  32. package/template/src/lib/code/shiki-theme-config.ts +0 -16
  33. package/template/src/lib/component-error.ts +0 -202
  34. package/template/src/lib/frontmatter-schema.ts +0 -10
@@ -2,10 +2,6 @@
2
2
  import { withBasePath } from "../../lib/base-path";
3
3
  import { getConfig, type ThemeColorByMode } from "../../lib/validation";
4
4
  import Icon from "../ui/Icon.astro";
5
- import {
6
- validateNoUnknownProps,
7
- validateProps,
8
- } from "../../lib/component-error";
9
5
  import {
10
6
  getDocsBaseColorShade,
11
7
  getThemeForegroundColor,
@@ -35,194 +31,41 @@ interface Props {
35
31
  }
36
32
 
37
33
  const cardProps = Astro.props as Record<string, unknown>;
34
+ const { title, href, icon, cover, button } = cardProps as Props;
38
35
 
39
- validateNoUnknownProps(
40
- "Card",
41
- cardProps,
42
- ["title", "href", "icon", "cover", "button"],
43
- Astro.url.pathname,
44
- );
45
-
46
- validateProps(
47
- "Card",
48
- cardProps,
49
- {
50
- title: { required: true, type: "string" },
51
- href: { type: "string" },
52
- icon: { type: "string" },
53
- cover: { type: "object" },
54
- button: { type: "object" },
55
- },
56
- Astro.url.pathname,
57
- );
58
-
59
- const rawCover = cardProps.cover;
60
- const rawButton = cardProps.button;
61
- if (rawCover !== undefined && (rawCover === null || Array.isArray(rawCover))) {
62
- throw new Error(
63
- `[USER_ERROR]: <Card>: Invalid prop "cover": expected object (in ${Astro.url.pathname})`,
64
- );
65
- }
66
- if (
67
- rawButton !== undefined &&
68
- (rawButton === null || Array.isArray(rawButton))
69
- ) {
70
- throw new Error(
71
- `[USER_ERROR]: <Card>: Invalid prop "button": expected object (in ${Astro.url.pathname})`,
72
- );
73
- }
74
-
75
- const coverProps =
76
- rawCover && typeof rawCover === "object"
77
- ? (rawCover as Record<string, unknown>)
78
- : undefined;
79
- const buttonProps =
80
- rawButton && typeof rawButton === "object"
81
- ? (rawButton as Record<string, unknown>)
82
- : undefined;
83
-
84
- if (coverProps) {
85
- validateNoUnknownProps(
86
- "Card.cover",
87
- coverProps,
88
- ["icon", "text", "glass", "colors", "patternSeed", "colorSeed"],
89
- Astro.url.pathname,
90
- );
91
- validateProps(
92
- "Card.cover",
93
- coverProps,
94
- {
95
- icon: { type: "string" },
96
- text: { type: "string" },
97
- glass: { type: "boolean" },
98
- colors: { type: "array" },
99
- patternSeed: { type: "string" },
100
- colorSeed: { type: "string" },
101
- },
102
- Astro.url.pathname,
103
- );
104
- }
105
-
106
- if (buttonProps) {
107
- validateNoUnknownProps(
108
- "Card.button",
109
- buttonProps,
110
- ["text", "href", "color"],
111
- Astro.url.pathname,
112
- );
113
- validateProps(
114
- "Card.button",
115
- buttonProps,
116
- {
117
- text: { required: true, type: "string" },
118
- href: { required: true, type: "string" },
119
- color: { type: ["string", "object"] },
120
- },
121
- Astro.url.pathname,
122
- );
123
- }
124
-
125
- function normalizeHexColor(value: unknown, propPath: string): string {
126
- if (typeof value !== "string") {
127
- throw new Error(
128
- `[USER_ERROR]: <Card>: Invalid prop "${propPath}": expected string (in ${Astro.url.pathname})`,
129
- );
130
- }
131
-
36
+ function normalizeHexColor(value: string): string {
132
37
  const trimmedValue = value.trim();
133
38
  const normalizedValue = trimmedValue.startsWith("#")
134
39
  ? trimmedValue
135
40
  : `#${trimmedValue}`;
136
- if (
137
- !/^#(?:[A-Fa-f0-9]{3}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8})$/.test(
138
- normalizedValue,
139
- )
140
- ) {
141
- throw new Error(
142
- `[USER_ERROR]: <Card>: Invalid prop "${propPath}": expected a hex color like "#3b82f6" (in ${Astro.url.pathname})`,
143
- );
144
- }
145
41
 
146
42
  return normalizedValue.toLowerCase();
147
43
  }
148
44
 
149
45
  function normalizeColorArray(
150
- value: unknown,
151
- propPath: string,
46
+ value: string[] | undefined,
152
47
  ): string[] | undefined {
153
48
  if (value === undefined) return undefined;
154
- if (!Array.isArray(value)) {
155
- throw new Error(
156
- `[USER_ERROR]: <Card>: Invalid prop "${propPath}": expected array (in ${Astro.url.pathname})`,
157
- );
158
- }
159
- if (value.length < 1 || value.length > 4) {
160
- throw new Error(
161
- `[USER_ERROR]: <Card>: Invalid prop "${propPath}": expected 1 to 4 colors (in ${Astro.url.pathname})`,
162
- );
163
- }
164
-
165
- return value.map((color, index) =>
166
- normalizeHexColor(color, `${propPath}.${index}`),
167
- );
49
+ return value.map((color) => normalizeHexColor(color));
168
50
  }
169
51
 
170
- function normalizeSeed(value: unknown, propPath: string): string | undefined {
52
+ function normalizeSeed(value: string | undefined): string | undefined {
171
53
  if (value === undefined) return undefined;
172
- if (typeof value !== "string") {
173
- throw new Error(
174
- `[USER_ERROR]: <Card>: Invalid prop "${propPath}": expected string (in ${Astro.url.pathname})`,
175
- );
176
- }
177
-
178
- const trimmedValue = value.trim();
179
- if (trimmedValue.length === 0) {
180
- throw new Error(
181
- `[USER_ERROR]: <Card>: Invalid prop "${propPath}": expected a non-empty string (in ${Astro.url.pathname})`,
182
- );
183
- }
184
-
185
- return trimmedValue;
54
+ return value.trim();
186
55
  }
187
56
 
188
57
  function normalizeThemeColorConfig(
189
- value: unknown,
190
- propPath: string,
58
+ value: string | ThemeColorByMode | undefined,
191
59
  ): string | ThemeColorByMode | undefined {
192
60
  if (value === undefined) return undefined;
193
61
  if (typeof value === "string") {
194
- return normalizeHexColor(value, propPath);
195
- }
196
-
197
- if (typeof value !== "object" || value === null || Array.isArray(value)) {
198
- throw new Error(
199
- `[USER_ERROR]: <Card>: Invalid prop "${propPath}": expected a hex color string or an object with light/dark values (in ${Astro.url.pathname})`,
200
- );
201
- }
202
-
203
- const colorByMode = value as Record<string, unknown>;
204
- for (const key of Object.keys(colorByMode)) {
205
- if (key !== "light" && key !== "dark") {
206
- throw new Error(
207
- `[USER_ERROR]: <Card>: Invalid prop "${propPath}.${key}": only "light" and "dark" are supported (in ${Astro.url.pathname})`,
208
- );
209
- }
62
+ return normalizeHexColor(value);
210
63
  }
211
64
 
212
65
  const light =
213
- colorByMode.light !== undefined
214
- ? normalizeHexColor(colorByMode.light, `${propPath}.light`)
215
- : undefined;
66
+ value.light !== undefined ? normalizeHexColor(value.light) : undefined;
216
67
  const dark =
217
- colorByMode.dark !== undefined
218
- ? normalizeHexColor(colorByMode.dark, `${propPath}.dark`)
219
- : undefined;
220
-
221
- if (light === undefined && dark === undefined) {
222
- throw new Error(
223
- `[USER_ERROR]: <Card>: Invalid prop "${propPath}": expected at least one of "light" or "dark" (in ${Astro.url.pathname})`,
224
- );
225
- }
68
+ value.dark !== undefined ? normalizeHexColor(value.dark) : undefined;
226
69
 
227
70
  return {
228
71
  ...(light !== undefined ? { light } : {}),
@@ -230,23 +73,6 @@ function normalizeThemeColorConfig(
230
73
  };
231
74
  }
232
75
 
233
- function normalizeRequiredString(value: unknown, propPath: string): string {
234
- if (typeof value !== "string") {
235
- throw new Error(
236
- `[USER_ERROR]: <Card>: Invalid prop "${propPath}": expected string (in ${Astro.url.pathname})`,
237
- );
238
- }
239
-
240
- const trimmedValue = value.trim();
241
- if (trimmedValue.length === 0) {
242
- throw new Error(
243
- `[USER_ERROR]: <Card>: Invalid prop "${propPath}": expected a non-empty string (in ${Astro.url.pathname})`,
244
- );
245
- }
246
-
247
- return trimmedValue;
248
- }
249
-
250
76
  function getConfiguredColorForMode(
251
77
  value: string | ThemeColorByMode | undefined,
252
78
  mode: "light" | "dark",
@@ -255,7 +81,14 @@ function getConfiguredColorForMode(
255
81
  return value?.[mode];
256
82
  }
257
83
 
258
- const { title, href, icon, cover } = cardProps as Props;
84
+ function getExternalLinkAttrs(
85
+ value: string | undefined,
86
+ ): { target: "_blank"; rel: "noopener noreferrer" } | Record<string, never> {
87
+ return value?.startsWith("http://") || value?.startsWith("https://")
88
+ ? { target: "_blank", rel: "noopener noreferrer" }
89
+ : {};
90
+ }
91
+
259
92
  const resolvedTitle = title.trim();
260
93
  const resolvedHref =
261
94
  typeof href === "string" && href.trim().length > 0
@@ -265,27 +98,16 @@ const contentIcon = icon?.trim();
265
98
  const coverIcon = cover?.icon?.trim();
266
99
  const coverText = cover?.text?.trim();
267
100
  const hasCover = Boolean(coverIcon || coverText);
268
- const cardButton = buttonProps
101
+ const cardButton = button
269
102
  ? {
270
- text: normalizeRequiredString(buttonProps.text, "button.text"),
271
- href: withBasePath(
272
- normalizeRequiredString(buttonProps.href, "button.href"),
273
- ),
274
- color: normalizeThemeColorConfig(buttonProps.color, "button.color"),
103
+ text: button.text.trim(),
104
+ href: withBasePath(button.href.trim()),
105
+ color: normalizeThemeColorConfig(button.color),
275
106
  }
276
107
  : undefined;
277
- const explicitCoverColors = normalizeColorArray(
278
- coverProps?.colors,
279
- "cover.colors",
280
- );
281
- const explicitPatternSeed = normalizeSeed(
282
- coverProps?.patternSeed,
283
- "cover.patternSeed",
284
- );
285
- const explicitColorSeed = normalizeSeed(
286
- coverProps?.colorSeed,
287
- "cover.colorSeed",
288
- );
108
+ const explicitCoverColors = normalizeColorArray(cover?.colors);
109
+ const explicitPatternSeed = normalizeSeed(cover?.patternSeed);
110
+ const explicitColorSeed = normalizeSeed(cover?.colorSeed);
289
111
  const config =
290
112
  cardButton || (hasCover && (!explicitCoverColors || !explicitColorSeed))
291
113
  ? await getConfig()
@@ -360,7 +182,10 @@ const descriptionHtml = Astro.slots.has("default")
360
182
  : "";
361
183
  const rootIsLink = Boolean(resolvedHref && !cardButton);
362
184
  const Element = rootIsLink ? "a" : "article";
363
- const rootAttrs = rootIsLink ? { href: resolvedHref } : {};
185
+ const rootAttrs =
186
+ rootIsLink && resolvedHref
187
+ ? { href: resolvedHref, ...getExternalLinkAttrs(resolvedHref) }
188
+ : {};
364
189
  const CardGradient = hasCover
365
190
  ? (await import("./CardGradient.astro")).default
366
191
  : undefined;
@@ -421,6 +246,7 @@ const CardGradient = hasCover
421
246
  resolvedHref && cardButton ? (
422
247
  <a
423
248
  href={resolvedHref}
249
+ {...getExternalLinkAttrs(resolvedHref)}
424
250
  class="group/rd-card-title inline-flex min-w-0 items-center rounded-sm text-inherit no-underline outline-none focus-visible:ring-ring/50 focus-visible:ring-[3px]"
425
251
  >
426
252
  <span class="min-w-0 leading-6">{resolvedTitle}</span>
@@ -456,6 +282,7 @@ const CardGradient = hasCover
456
282
  cardButton && (
457
283
  <a
458
284
  href={cardButton.href}
285
+ {...getExternalLinkAttrs(cardButton.href)}
459
286
  class="rd-card-button relative z-10 inline-flex h-8 shrink-0 items-center justify-center self-center rounded-xl [corner-shape:superellipse(1.2)] px-3 text-[13px] no-underline transition-opacity duration-200 hover:opacity-95 focus-visible:outline-none focus-visible:ring-ring/50 focus-visible:ring-[3px]"
460
287
  style={cardButtonStyle}
461
288
  >
@@ -313,7 +313,14 @@ const textSizeClass = icon ? "text-lg sm:text-xl" : "text-xl sm:text-2xl";
313
313
  glass && isIconOnly && "rd-card-gradient-content-glass-icon",
314
314
  ]}
315
315
  >
316
- {icon && <Icon name={icon} class="size-9 text-white/70" />}
316
+ {
317
+ icon && (
318
+ <Icon
319
+ name={icon}
320
+ class:list={["text-white/70", text ? "size-7" : "size-9"]}
321
+ />
322
+ )
323
+ }
317
324
  {
318
325
  text && (
319
326
  <span
@@ -1,21 +1,4 @@
1
1
  ---
2
- const columnProps = Astro.props as Record<string, unknown>;
3
-
4
- function getSourceFile(pathname: string): string {
5
- const pagePath = pathname
6
- .replace(/^\/documentation\//, "")
7
- .replace(/\/$/, "");
8
- return `${pagePath}.mdx`;
9
- }
10
-
11
- const unknownProps = Object.keys(columnProps);
12
- if (unknownProps.length > 0) {
13
- const propLabel = unknownProps.length === 1 ? "prop" : "props";
14
- const unknownLabel = unknownProps.map((name) => `"${name}"`).join(", ");
15
- throw new Error(
16
- `[USER_ERROR]: <Column>: Unsupported ${propLabel}: ${unknownLabel}. <Column> does not accept props; render another <Columns> block for a new row. (in ${getSourceFile(Astro.url.pathname)})`,
17
- );
18
- }
19
2
  ---
20
3
 
21
4
  <div data-rd-column class="min-w-0">
@@ -1,168 +1,19 @@
1
1
  ---
2
- import {
3
- validateNoUnknownProps,
4
- validateProps,
5
- } from "../../lib/component-error";
6
-
7
2
  interface Props {
8
- columns?: 2 | 3;
3
+ columns?: 2 | 3 | "2" | "3";
9
4
  }
10
5
 
11
- const columnsProps = Astro.props as Record<string, unknown>;
12
-
13
- validateNoUnknownProps(
14
- "Columns",
15
- columnsProps,
16
- ["columns"],
17
- Astro.url.pathname,
18
- );
19
-
20
- validateProps(
21
- "Columns",
22
- columnsProps,
23
- {
24
- columns: { type: ["number", "string"] },
25
- },
26
- Astro.url.pathname,
27
- );
6
+ const columnsProps = Astro.props as Props;
28
7
 
29
- function normalizeColumns(value: unknown): 2 | 3 {
8
+ function normalizeColumns(value: Props["columns"]): 2 | 3 {
30
9
  if (value === undefined) return 2;
31
-
32
- const normalizedValue =
33
- typeof value === "number"
34
- ? value
35
- : typeof value === "string"
36
- ? Number(value.trim())
37
- : Number.NaN;
38
-
39
- if (normalizedValue !== 2 && normalizedValue !== 3) {
40
- throw new Error(
41
- `[USER_ERROR]: <Columns>: Invalid prop "columns": expected 2 or 3 (in ${Astro.url.pathname})`,
42
- );
43
- }
44
-
45
- return normalizedValue;
46
- }
47
-
48
- function getSourceFile(pathname: string): string {
49
- const pagePath = pathname
50
- .replace(/^\/documentation\//, "")
51
- .replace(/\/$/, "");
52
- return `${pagePath}.mdx`;
53
- }
54
-
55
- function createColumnsError(message: string): Error {
56
- return new Error(
57
- `[USER_ERROR]: <Columns>: ${message} (in ${getSourceFile(Astro.url.pathname)})`,
58
- );
59
- }
60
-
61
- const VOID_ELEMENTS = new Set([
62
- "area",
63
- "base",
64
- "br",
65
- "col",
66
- "embed",
67
- "hr",
68
- "img",
69
- "input",
70
- "link",
71
- "meta",
72
- "param",
73
- "source",
74
- "track",
75
- "wbr",
76
- ]);
77
-
78
- function getTopLevelElements(html: string): Array<{
79
- tagName: string;
80
- attributes: string;
81
- }> {
82
- const elements: Array<{ tagName: string; attributes: string }> = [];
83
- let depth = 0;
84
- let index = 0;
85
-
86
- while (index < html.length) {
87
- const nextTagStart = html.indexOf("<", index);
88
- if (nextTagStart === -1) break;
89
-
90
- if (html.startsWith("<!--", nextTagStart)) {
91
- const commentEnd = html.indexOf("-->", nextTagStart + 4);
92
- index = commentEnd === -1 ? html.length : commentEnd + 3;
93
- continue;
94
- }
95
-
96
- const tagEnd = html.indexOf(">", nextTagStart + 1);
97
- if (tagEnd === -1) break;
98
-
99
- const rawTag = html.slice(nextTagStart + 1, tagEnd).trim();
100
- index = tagEnd + 1;
101
-
102
- if (!rawTag || rawTag.startsWith("!") || rawTag.startsWith("?")) {
103
- continue;
104
- }
105
-
106
- if (rawTag.startsWith("/")) {
107
- depth = Math.max(0, depth - 1);
108
- continue;
109
- }
110
-
111
- const tagMatch = rawTag.match(/^([A-Za-z][\w:-]*)([\s\S]*)$/);
112
- if (!tagMatch) continue;
113
-
114
- const tagName = tagMatch[1].toLowerCase();
115
- const attributes = tagMatch[2] ?? "";
116
- const isSelfClosing = /\/\s*$/.test(rawTag);
117
-
118
- if (depth === 0) {
119
- elements.push({ tagName, attributes });
120
- }
121
-
122
- if (!isSelfClosing && !VOID_ELEMENTS.has(tagName)) {
123
- depth += 1;
124
- }
125
- }
126
-
127
- return elements;
128
- }
129
-
130
- function isColumnElement(element: { tagName: string; attributes: string }) {
131
- return (
132
- element.tagName === "div" &&
133
- /(?:^|\s)data-rd-column(?:\s|=|$)/.test(element.attributes)
134
- );
135
- }
136
-
137
- function validateColumnChildren(html: string, expectedColumns: 2 | 3): void {
138
- const topLevelElements = getTopLevelElements(html);
139
- const columnElements = topLevelElements.filter(isColumnElement);
140
-
141
- if (topLevelElements.length === 0) {
142
- throw createColumnsError(
143
- `Must contain exactly ${expectedColumns} <Column> children.`,
144
- );
145
- }
146
-
147
- if (columnElements.length !== topLevelElements.length) {
148
- throw createColumnsError(
149
- "Only direct <Column> children are allowed. Wrap each column's content in <Column>.",
150
- );
151
- }
152
-
153
- if (columnElements.length !== expectedColumns) {
154
- throw createColumnsError(
155
- `Expected exactly ${expectedColumns} <Column> children for columns={${expectedColumns}}, but found ${columnElements.length}. Render another <Columns> block for a new row.`,
156
- );
157
- }
10
+ return Number(value) === 3 ? 3 : 2;
158
11
  }
159
12
 
160
13
  const columns = normalizeColumns(columnsProps.columns);
161
14
  const html = Astro.slots.has("default")
162
15
  ? await Astro.slots.render("default")
163
16
  : "";
164
-
165
- validateColumnChildren(html, columns);
166
17
  ---
167
18
 
168
19
  <div class="rd-prose-block rd-columns-container w-full max-w-none!">
@@ -1,5 +1,4 @@
1
1
  ---
2
- import { validateNoUnknownProps, validateProps } from "../../lib/component-error";
3
2
  import { resolveStaticAssetUrl } from "../../lib/static-asset-url";
4
3
 
5
4
  interface Props {
@@ -11,27 +10,6 @@ interface Props {
11
10
  }
12
11
 
13
12
  const imageProps = Astro.props as Record<string, unknown>;
14
-
15
- validateNoUnknownProps(
16
- "Image",
17
- imageProps,
18
- ["src", "alt", "title", "width", "zoom"],
19
- Astro.url.pathname,
20
- );
21
-
22
- validateProps(
23
- "Image",
24
- imageProps,
25
- {
26
- src: { required: true, type: ["string", "object"] },
27
- alt: { type: "string" },
28
- title: { type: "string" },
29
- width: { type: ["number", "string"] },
30
- zoom: { type: "boolean" },
31
- },
32
- Astro.url.pathname,
33
- );
34
-
35
13
  const { src, alt, title, width, zoom = true } = imageProps as Props;
36
14
  const zoomEnabled = zoom !== false;
37
15
  const rawWidth = width;
@@ -51,12 +29,6 @@ function normalizeImageSource(value: Props["src"]): {
51
29
  }
52
30
 
53
31
  const normalizedSource = normalizeImageSource(src);
54
- if (!normalizedSource.light.trim()) {
55
- throw new Error(
56
- `[USER_ERROR]: <Image>: Invalid prop "src": object form requires a non-empty "light" string (in ${Astro.url.pathname})`,
57
- );
58
- }
59
-
60
32
  const resolvedLightSrc = resolveStaticAssetUrl(normalizedSource.light);
61
33
  const resolvedDarkSrc =
62
34
  typeof normalizedSource.dark === "string" && normalizedSource.dark.trim()
@@ -1,21 +1,11 @@
1
1
  ---
2
2
  import { Icon } from "astro-icon/components";
3
- import { validateProps } from "../../lib/component-error";
4
3
 
5
4
  interface Props {
6
5
  title: string;
7
6
  }
8
7
 
9
8
  const { title } = Astro.props;
10
-
11
- validateProps(
12
- "Step",
13
- { title },
14
- {
15
- title: { required: true, type: "string" },
16
- },
17
- Astro.url.pathname,
18
- );
19
9
  ---
20
10
 
21
11
  <div
@@ -1,21 +1,9 @@
1
1
  ---
2
- import { validateProps, validateRequired } from "../../lib/component-error";
3
-
4
2
  interface Props {
5
3
  label: string;
6
4
  icon?: string;
7
5
  }
8
6
  const { label, icon } = Astro.props;
9
-
10
- validateProps(
11
- "Tab",
12
- { label, icon },
13
- {
14
- label: { required: true, type: "string" },
15
- icon: { type: "string" },
16
- },
17
- Astro.url.pathname
18
- );
19
7
  ---
20
8
 
21
9
  <section data-label={label} data-icon={icon || ""}>
@@ -16,13 +16,6 @@ while ((match = tabRegex.exec(html)) !== null) {
16
16
  icons.push(iconMatch?.[1] ?? "");
17
17
  tabContents.push(match[4]);
18
18
  }
19
-
20
- if (labels.length === 0) {
21
- const pagePath = Astro.url.pathname.replace(/^\/documentation\//, "").replace(/\/$/, "");
22
- throw new Error(
23
- `[USER_ERROR]: <Tabs>: Must contain at least two <Tab> children (in ${pagePath}.mdx)`
24
- );
25
- }
26
19
  ---
27
20
 
28
21
  <div x-data="{
@@ -166,7 +159,7 @@ class="rd-prose-block">
166
159
  type="button"
167
160
  x-ref={`tab-${index}`}
168
161
  @click={`selectTab(${index})`}
169
- class="relative flex h-[32px] w-full min-w-0 max-w-full cursor-pointer items-center gap-2 px-3 text-sm font-medium transition-colors duration-200"
162
+ class="relative flex h-[32px] w-full min-w-0 max-w-full cursor-pointer items-center justify-center gap-2 px-3 text-sm font-medium transition-colors duration-200"
170
163
  style={index === 0 ? "" : ""}
171
164
  class:list={[index === 0 ? "text-foreground" : "text-muted-foreground"]}
172
165
  :class={`{
@@ -175,7 +168,7 @@ class="rd-prose-block">
175
168
  }`}
176
169
  >
177
170
  {icons[index] && <Icon name={icons[index]} class="size-4 shrink-0" />}
178
- <span class="min-w-0 flex-1 truncate" title={label}>{label}</span>
171
+ <span class="min-w-0 truncate" title={label}>{label}</span>
179
172
  </button>
180
173
  </li>
181
174
  )) }
@@ -1,6 +1,6 @@
1
1
  import { defineCollection } from "astro:content";
2
2
  import { glob } from "astro/loaders";
3
- import { docsSchema } from "./lib/frontmatter-schema";
3
+ import { docsSchema } from "radiant-docs-validator/frontmatter-schema";
4
4
 
5
5
  const docs = defineCollection({
6
6
  // Load Markdown and MDX files from src/content/docs
@@ -12,7 +12,7 @@ import {
12
12
  DEFAULT_SHIKI_DARK_THEME,
13
13
  DEFAULT_SHIKI_LIGHT_THEME,
14
14
  type CodeSyntaxThemeByMode,
15
- } from "./shiki-theme-config";
15
+ } from "radiant-docs-validator/shiki-theme-config";
16
16
 
17
17
  export const DEFAULT_CODE_BLOCK_LANGUAGE = "plaintext";
18
18