radiant-docs 0.1.56 → 0.1.58
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/index.js +3 -76
- package/package.json +2 -4
- package/template/astro.config.mjs +16 -26
- package/template/package-lock.json +18 -0
- package/template/package.json +1 -1
- package/template/src/components/Footer.astro +13 -4
- package/template/src/components/Header.astro +26 -6
- package/template/src/components/SidebarLink.astro +3 -2
- package/template/src/components/SidebarSubgroup.astro +14 -13
- package/template/src/components/sidebar/SidebarEndpointLink.astro +13 -3
- package/template/src/components/sidebar/SidebarOpenApi.astro +2 -0
- package/template/src/components/sidebar/SidebarOpenApiPageLink.astro +1 -0
- package/template/src/components/user/Accordion.astro +0 -13
- package/template/src/components/user/Callout.astro +0 -29
- package/template/src/components/user/Card.astro +31 -204
- package/template/src/components/user/CardGradient.astro +8 -1
- package/template/src/components/user/Column.astro +0 -17
- package/template/src/components/user/Columns.astro +4 -153
- package/template/src/components/user/Image.astro +17 -30
- package/template/src/components/user/Step.astro +0 -10
- package/template/src/components/user/Tab.astro +0 -12
- package/template/src/components/user/Tabs.astro +2 -9
- package/template/src/content.config.ts +1 -1
- package/template/src/lib/code/code-block.ts +1 -1
- package/template/src/lib/mdx/remark-code-block-component.ts +1 -20
- package/template/src/lib/mdx/remark-resolve-internal-links.ts +150 -204
- package/template/src/lib/routes.ts +150 -29
- package/template/src/lib/utils.ts +127 -12
- package/template/src/lib/validation.ts +5 -2826
- package/template/src/pages/[...slug].astro +16 -0
- package/template/src/lib/code/shiki-theme-config.ts +0 -16
- package/template/src/lib/component-error.ts +0 -202
- 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
|
-
|
|
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:
|
|
151
|
-
propPath: string,
|
|
46
|
+
value: string[] | undefined,
|
|
152
47
|
): string[] | undefined {
|
|
153
48
|
if (value === undefined) return undefined;
|
|
154
|
-
|
|
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:
|
|
52
|
+
function normalizeSeed(value: string | undefined): string | undefined {
|
|
171
53
|
if (value === undefined) return undefined;
|
|
172
|
-
|
|
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:
|
|
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
|
|
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
|
-
|
|
214
|
-
? normalizeHexColor(colorByMode.light, `${propPath}.light`)
|
|
215
|
-
: undefined;
|
|
66
|
+
value.light !== undefined ? normalizeHexColor(value.light) : undefined;
|
|
216
67
|
const dark =
|
|
217
|
-
|
|
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
|
-
|
|
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 =
|
|
101
|
+
const cardButton = button
|
|
269
102
|
? {
|
|
270
|
-
text:
|
|
271
|
-
href: withBasePath(
|
|
272
|
-
|
|
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
|
-
|
|
279
|
-
|
|
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 =
|
|
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
|
-
{
|
|
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
|
|
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:
|
|
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 {
|
|
@@ -7,34 +6,22 @@ interface Props {
|
|
|
7
6
|
alt?: string;
|
|
8
7
|
title?: string;
|
|
9
8
|
width?: number | string;
|
|
9
|
+
align?: "left" | "center" | "right";
|
|
10
10
|
zoom?: boolean;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
const imageProps = Astro.props as Record<string, unknown>;
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
-
const { src, alt, title, width, zoom = true } = imageProps as Props;
|
|
14
|
+
const {
|
|
15
|
+
src,
|
|
16
|
+
alt,
|
|
17
|
+
title,
|
|
18
|
+
width,
|
|
19
|
+
align = "center",
|
|
20
|
+
zoom = true,
|
|
21
|
+
} = imageProps as Props;
|
|
36
22
|
const zoomEnabled = zoom !== false;
|
|
37
23
|
const rawWidth = width;
|
|
24
|
+
const imageAlign = align === "left" || align === "right" ? align : "center";
|
|
38
25
|
|
|
39
26
|
function normalizeImageSource(value: Props["src"]): {
|
|
40
27
|
light: string;
|
|
@@ -51,12 +38,6 @@ function normalizeImageSource(value: Props["src"]): {
|
|
|
51
38
|
}
|
|
52
39
|
|
|
53
40
|
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
41
|
const resolvedLightSrc = resolveStaticAssetUrl(normalizedSource.light);
|
|
61
42
|
const resolvedDarkSrc =
|
|
62
43
|
typeof normalizedSource.dark === "string" && normalizedSource.dark.trim()
|
|
@@ -131,6 +112,12 @@ const hasCustomImageWidth = isConstrainedWidthValue(rawWidth);
|
|
|
131
112
|
const contentWidthStyle = hasCustomImageWidth
|
|
132
113
|
? `width: ${toCssWidthValue(rawWidth) ?? "auto"};`
|
|
133
114
|
: undefined;
|
|
115
|
+
const constrainedWidthAlignmentClass =
|
|
116
|
+
imageAlign === "left"
|
|
117
|
+
? "w-fit max-w-full ml-0 mr-auto"
|
|
118
|
+
: imageAlign === "right"
|
|
119
|
+
? "w-fit max-w-full ml-auto mr-0"
|
|
120
|
+
: "w-fit max-w-full mx-auto";
|
|
134
121
|
|
|
135
122
|
const slotCaptionHtml = Astro.slots.has("default")
|
|
136
123
|
? (await Astro.slots.render("default")).trim()
|
|
@@ -141,7 +128,7 @@ const hasCaption = slotCaptionHtml.length > 0;
|
|
|
141
128
|
<figure
|
|
142
129
|
class:list={[
|
|
143
130
|
"rd-prose-block p-1.5 pb-1 xs:pb-1.5 group border-[0.5px] border-neutral-900/8 dark:border-white/6 bg-(--rd-code-surface) rounded-2xl shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-8px_rgba(0,0,0,0.08)] dark:shadow-[0_-.5px_1px_rgba(255,255,255,0.15),0_5px_12px_-8px_rgba(0,0,0,0.2)]",
|
|
144
|
-
hasCustomImageWidth ?
|
|
131
|
+
hasCustomImageWidth ? constrainedWidthAlignmentClass : "w-full",
|
|
145
132
|
]}
|
|
146
133
|
x-data="{
|
|
147
134
|
open: false,
|
|
@@ -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
|
|
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 "
|
|
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
|