radiant-docs 0.1.49 → 0.1.50
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/package.json +1 -1
- package/template/src/components/SidebarGroup.astro +1 -1
- package/template/src/components/SidebarLink.astro +3 -3
- package/template/src/components/SidebarSubgroup.astro +1 -1
- package/template/src/components/sidebar/SidebarEndpointLink.astro +3 -2
- package/template/src/components/ui/Tag.astro +64 -2
- package/template/src/components/user/CodeBlock.astro +1 -1
- package/template/src/components/user/ComponentPreviewBlock.astro +1 -1
- package/template/src/components/user/Image.astro +51 -34
- package/template/src/lib/validation.ts +120 -8
package/package.json
CHANGED
|
@@ -22,7 +22,7 @@ const currentPrefix = parentSlug ? `${parentSlug}/${groupSlug}` : groupSlug;
|
|
|
22
22
|
<div class:list={["text-sm font-semibold mb-2 flex items-center gap-2 px-2"]}>
|
|
23
23
|
{item.icon && <Icon name={item.icon} class="size-4 text-neutral-500" />}
|
|
24
24
|
{item.group}
|
|
25
|
-
{item.tag && <Tag
|
|
25
|
+
{item.tag && <Tag tag={item.tag} />}
|
|
26
26
|
</div>
|
|
27
27
|
|
|
28
28
|
<ul>
|
|
@@ -3,13 +3,13 @@ import { buildMdxPageHref, deriveTitleFromEntryId } from "../lib/utils";
|
|
|
3
3
|
import Icon from "./ui/Icon.astro";
|
|
4
4
|
import Tag from "./ui/Tag.astro";
|
|
5
5
|
import { getCollection } from "astro:content";
|
|
6
|
-
import { getConfig } from "../lib/validation";
|
|
6
|
+
import { getConfig, type NavTag } from "../lib/validation";
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
9
9
|
path: string;
|
|
10
10
|
groupSlug?: string;
|
|
11
11
|
icon?: string | null;
|
|
12
|
-
tag?:
|
|
12
|
+
tag?: NavTag;
|
|
13
13
|
title?: string;
|
|
14
14
|
}
|
|
15
15
|
|
|
@@ -61,6 +61,6 @@ const isActive = currentPath === targetPath;
|
|
|
61
61
|
<div class="flex items-center gap-2">
|
|
62
62
|
{icon && <Icon name={icon} class="size-4 opacity-75" />}
|
|
63
63
|
{text}
|
|
64
|
-
{tag && <Tag
|
|
64
|
+
{tag && <Tag tag={tag} />}
|
|
65
65
|
</div>
|
|
66
66
|
</a>
|
|
@@ -82,7 +82,7 @@ const containsActivePage = item.pages.some((child) => {
|
|
|
82
82
|
<div class="flex items-center gap-2">
|
|
83
83
|
{item.icon && <Icon name={item.icon} class="size-4 opacity-75" />}
|
|
84
84
|
{item.group}
|
|
85
|
-
{item.tag && <Tag
|
|
85
|
+
{item.tag && <Tag tag={item.tag} />}
|
|
86
86
|
</div>
|
|
87
87
|
<svg
|
|
88
88
|
xmlns="http://www.w3.org/2000/svg"
|
|
@@ -2,13 +2,14 @@
|
|
|
2
2
|
import Tag from "../ui/Tag.astro";
|
|
3
3
|
import { buildOpenApiEndpointHref } from "../../lib/utils";
|
|
4
4
|
import { methodColors } from "../../lib/utils";
|
|
5
|
+
import type { NavTag } from "../../lib/validation";
|
|
5
6
|
|
|
6
7
|
interface Props {
|
|
7
8
|
method: string;
|
|
8
9
|
path: string;
|
|
9
10
|
summary?: string;
|
|
10
11
|
title?: string;
|
|
11
|
-
tag?:
|
|
12
|
+
tag?: NavTag;
|
|
12
13
|
parentSlug?: string;
|
|
13
14
|
}
|
|
14
15
|
|
|
@@ -49,6 +50,6 @@ const isActive = currentPath === targetPath;
|
|
|
49
50
|
</span>
|
|
50
51
|
<div class="flex items-center gap-2 min-w-0">
|
|
51
52
|
<span>{text}</span>
|
|
52
|
-
{tag && <Tag
|
|
53
|
+
{tag && <Tag tag={tag} />}
|
|
53
54
|
</div>
|
|
54
55
|
</a>
|
|
@@ -1,5 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
import {
|
|
3
|
+
getConfig,
|
|
4
|
+
type NavTag,
|
|
5
|
+
type ThemeColorByMode,
|
|
6
|
+
} from "../../lib/validation";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
tag?: NavTag;
|
|
10
|
+
color?: string | ThemeColorByMode;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { tag, color } = Astro.props;
|
|
14
|
+
const config = await getConfig();
|
|
15
|
+
|
|
16
|
+
function getTagText(value: NavTag | undefined): string | undefined {
|
|
17
|
+
if (typeof value === "string") return value;
|
|
18
|
+
return value?.text;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function getTagColor(
|
|
22
|
+
value: NavTag | undefined,
|
|
23
|
+
): string | ThemeColorByMode | undefined {
|
|
24
|
+
if (typeof value === "object" && value !== null) {
|
|
25
|
+
return value.color;
|
|
26
|
+
}
|
|
27
|
+
return undefined;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function resolveColorByMode(
|
|
31
|
+
value: string | ThemeColorByMode | undefined,
|
|
32
|
+
): { light: string; dark: string } | undefined {
|
|
33
|
+
if (typeof value === "string") {
|
|
34
|
+
return { light: value, dark: value };
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const light = value?.light ?? value?.dark;
|
|
38
|
+
const dark = value?.dark ?? value?.light;
|
|
39
|
+
if (!light || !dark) return undefined;
|
|
40
|
+
|
|
41
|
+
return { light, dark };
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const text = getTagText(tag);
|
|
45
|
+
const configuredColor =
|
|
46
|
+
color ?? getTagColor(tag) ?? config.theme?.tag?.color ?? undefined;
|
|
47
|
+
const resolvedColor = resolveColorByMode(configuredColor);
|
|
48
|
+
const colorStyle = resolvedColor
|
|
49
|
+
? `--rd-tag-color-light:${resolvedColor.light};--rd-tag-color-dark:${resolvedColor.dark};`
|
|
50
|
+
: undefined;
|
|
51
|
+
---
|
|
52
|
+
|
|
1
53
|
<span
|
|
2
|
-
class="text-[9px]
|
|
54
|
+
class="rd-tag text-[9px] border px-[5px] py-[2px] rounded-full tracking-wide leading-none font-medium shrink-0"
|
|
55
|
+
style={colorStyle}
|
|
3
56
|
>
|
|
4
|
-
<slot />
|
|
57
|
+
{text ? text : <slot />}
|
|
5
58
|
</span>
|
|
59
|
+
|
|
60
|
+
<style>
|
|
61
|
+
.rd-tag {
|
|
62
|
+
--rd-tag-color: var(--rd-tag-color-light, var(--color-theme));
|
|
63
|
+
background-color: color-mix(in oklab, var(--rd-tag-color) 8%, transparent);
|
|
64
|
+
border-color: color-mix(in oklab, var(--rd-tag-color) 0%, transparent);
|
|
65
|
+
color: color-mix(in oklab, var(--rd-tag-color) 95%, transparent);
|
|
66
|
+
}
|
|
67
|
+
</style>
|
|
@@ -98,7 +98,7 @@ const isInitiallyExpanded = shouldShowAllCode || totalLineCount <= visibleLines;
|
|
|
98
98
|
>
|
|
99
99
|
<button
|
|
100
100
|
type="button"
|
|
101
|
-
class="pointer-events-auto inline-flex h-8 items-center justify-center rounded-xl [corner-shape:superellipse(1.2)] border-[0.5px] border-neutral-900/10 dark:border-white/8 bg-linear-to-br from-white via-white/10 to-neutral-900/5 dark:from-white/7 dark:via-white/6 dark:to-white/2 px-3 text-sm font-medium text-neutral-600 hover:text-neutral-900 dark:text-neutral-300/90 hover:dark:text-neutral-200 transition-colors duration-200 hover:bg-neutral-50/80 cursor-pointer dark:hover:bg-neutral-700/30 shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-4px_rgba(0,0,0,0.08)] dark:shadow-[0_-0.5px_0px_rgba(255,255,255,0.15),0_5px_12px_-4px_rgba(0,0,0,0.
|
|
101
|
+
class="pointer-events-auto inline-flex h-8 items-center justify-center rounded-xl [corner-shape:superellipse(1.2)] border-[0.5px] border-neutral-900/10 dark:border-white/8 bg-linear-to-br from-white via-white/10 to-neutral-900/5 dark:from-white/7 dark:via-white/6 dark:to-white/2 px-3 text-sm font-medium text-neutral-600 hover:text-neutral-900 dark:text-neutral-300/90 hover:dark:text-neutral-200 transition-colors duration-200 hover:bg-neutral-50/80 cursor-pointer dark:hover:bg-neutral-700/30 shadow-[0_.5px_1px_rgba(0,0,0,0.15),0_5px_12px_-4px_rgba(0,0,0,0.08)] dark:shadow-[0_-0.5px_0px_rgba(255,255,255,0.15),0_5px_12px_-4px_rgba(0,0,0,0.18)]"
|
|
102
102
|
data-rd-preview-expand
|
|
103
103
|
>
|
|
104
104
|
View code
|
|
@@ -114,7 +114,23 @@ function isConstrainedWidthValue(value: unknown): boolean {
|
|
|
114
114
|
return true;
|
|
115
115
|
}
|
|
116
116
|
|
|
117
|
+
function toCssWidthValue(value: unknown): string | undefined {
|
|
118
|
+
if (typeof value === "number") {
|
|
119
|
+
if (!Number.isFinite(value) || value <= 0) return undefined;
|
|
120
|
+
return `${value}px`;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (typeof value !== "string") return undefined;
|
|
124
|
+
const normalized = value.trim();
|
|
125
|
+
if (!normalized) return undefined;
|
|
126
|
+
|
|
127
|
+
return /^-?\d*\.?\d+$/.test(normalized) ? `${normalized}px` : normalized;
|
|
128
|
+
}
|
|
129
|
+
|
|
117
130
|
const hasCustomImageWidth = isConstrainedWidthValue(rawWidth);
|
|
131
|
+
const contentWidthStyle = hasCustomImageWidth
|
|
132
|
+
? `width: ${toCssWidthValue(rawWidth) ?? "auto"};`
|
|
133
|
+
: undefined;
|
|
118
134
|
|
|
119
135
|
const slotCaptionHtml = Astro.slots.has("default")
|
|
120
136
|
? (await Astro.slots.render("default")).trim()
|
|
@@ -240,45 +256,46 @@ const hasCaption = slotCaptionHtml.length > 0;
|
|
|
240
256
|
}"
|
|
241
257
|
>
|
|
242
258
|
<div
|
|
243
|
-
class="
|
|
259
|
+
class:list={["max-w-full", !hasCustomImageWidth && "w-full"]}
|
|
260
|
+
style={contentWidthStyle}
|
|
244
261
|
>
|
|
245
|
-
<
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
262
|
+
<div class="overflow-hidden rounded-[11px]">
|
|
263
|
+
<img
|
|
264
|
+
{...lightImageAttrs}
|
|
265
|
+
x-ref="lightImg"
|
|
266
|
+
title={title}
|
|
267
|
+
class:list={[
|
|
268
|
+
"h-auto my-0! block w-full transition-opacity",
|
|
269
|
+
hasDarkSource && "dark:hidden",
|
|
270
|
+
zoomEnabled && "cursor-zoom-in",
|
|
271
|
+
]}
|
|
272
|
+
:class="showZoomed ? 'opacity-0 duration-0' : 'opacity-100 duration-300'"
|
|
273
|
+
@click={zoomEnabled ? "zoom()" : undefined}
|
|
274
|
+
/>
|
|
275
|
+
{
|
|
276
|
+
darkImageAttrs && (
|
|
277
|
+
<img
|
|
278
|
+
{...darkImageAttrs}
|
|
279
|
+
x-ref="darkImg"
|
|
280
|
+
title={title}
|
|
281
|
+
class:list={[
|
|
282
|
+
"h-auto my-0! hidden w-full transition-opacity dark:block",
|
|
283
|
+
zoomEnabled && "cursor-zoom-in",
|
|
284
|
+
]}
|
|
285
|
+
:class="showZoomed ? 'opacity-0 duration-0' : 'opacity-100 duration-300'"
|
|
286
|
+
@click={zoomEnabled ? "zoom()" : undefined}
|
|
287
|
+
/>
|
|
288
|
+
)
|
|
289
|
+
}
|
|
290
|
+
</div>
|
|
258
291
|
{
|
|
259
|
-
|
|
260
|
-
<
|
|
261
|
-
{
|
|
262
|
-
|
|
263
|
-
title={title}
|
|
264
|
-
class:list={[
|
|
265
|
-
"h-auto my-0! hidden transition-opacity dark:block",
|
|
266
|
-
zoomEnabled && "cursor-zoom-in",
|
|
267
|
-
hasCustomImageWidth ? "max-w-full" : "w-full",
|
|
268
|
-
]}
|
|
269
|
-
:class="showZoomed ? 'opacity-0 duration-0' : 'opacity-100 duration-300'"
|
|
270
|
-
@click={zoomEnabled ? "zoom()" : undefined}
|
|
271
|
-
/>
|
|
292
|
+
hasCaption && (
|
|
293
|
+
<figcaption class="prose-rules mt-1! xs:mt-1.5! max-w-none! *:max-w-none! text-center text-xs! xs:text-sm! text-neutral-500 dark:text-neutral-400 leading-relaxed px-2">
|
|
294
|
+
<Fragment set:html={slotCaptionHtml} />
|
|
295
|
+
</figcaption>
|
|
272
296
|
)
|
|
273
297
|
}
|
|
274
298
|
</div>
|
|
275
|
-
{
|
|
276
|
-
hasCaption && (
|
|
277
|
-
<figcaption class="prose-rules mt-1! xs:mt-1.5! max-w-none! *:max-w-none! text-center text-xs! xs:text-sm! text-neutral-500 dark:text-neutral-400 leading-relaxed px-2">
|
|
278
|
-
<Fragment set:html={slotCaptionHtml} />
|
|
279
|
-
</figcaption>
|
|
280
|
-
)
|
|
281
|
-
}
|
|
282
299
|
|
|
283
300
|
{
|
|
284
301
|
zoomEnabled && (
|
|
@@ -131,7 +131,7 @@ const INTERNAL_ONLY_COMPONENTS = new Set(["ComponentPreview"]);
|
|
|
131
131
|
export type NavPage = {
|
|
132
132
|
page: string;
|
|
133
133
|
icon?: string | null;
|
|
134
|
-
tag?:
|
|
134
|
+
tag?: NavTag;
|
|
135
135
|
title?: string;
|
|
136
136
|
};
|
|
137
137
|
export type NavOpenApiPageRef = {
|
|
@@ -141,14 +141,14 @@ export type NavOpenApiPageRef = {
|
|
|
141
141
|
export type NavOpenApiPage = {
|
|
142
142
|
openapi: NavOpenApiPageRef;
|
|
143
143
|
title?: string;
|
|
144
|
-
tag?:
|
|
144
|
+
tag?: NavTag;
|
|
145
145
|
};
|
|
146
146
|
export type NavGroup = {
|
|
147
147
|
group: string;
|
|
148
148
|
pages: (string | NavPage | NavGroup | NavOpenApiPage)[];
|
|
149
149
|
icon?: string | null;
|
|
150
150
|
expanded?: boolean; // need to add this logic
|
|
151
|
-
tag?:
|
|
151
|
+
tag?: NavTag;
|
|
152
152
|
};
|
|
153
153
|
export type NavOpenApi = {
|
|
154
154
|
source: string;
|
|
@@ -199,6 +199,16 @@ export type Logo = {
|
|
|
199
199
|
href?: string;
|
|
200
200
|
pill?: string | false;
|
|
201
201
|
};
|
|
202
|
+
export type ThemeColorByMode = {
|
|
203
|
+
light?: string;
|
|
204
|
+
dark?: string;
|
|
205
|
+
};
|
|
206
|
+
export type NavTag =
|
|
207
|
+
| string
|
|
208
|
+
| {
|
|
209
|
+
text: string;
|
|
210
|
+
color?: string | ThemeColorByMode;
|
|
211
|
+
};
|
|
202
212
|
export const BASE_COLOR_OPTIONS = [
|
|
203
213
|
"slate",
|
|
204
214
|
"gray",
|
|
@@ -217,10 +227,6 @@ export type BaseColorByMode = {
|
|
|
217
227
|
};
|
|
218
228
|
export const DEFAULT_THEME_COLOR_LIGHT = "#171717";
|
|
219
229
|
export const DEFAULT_THEME_COLOR_DARK = "#f5f5f5";
|
|
220
|
-
export type ThemeColorByMode = {
|
|
221
|
-
light?: string;
|
|
222
|
-
dark?: string;
|
|
223
|
-
};
|
|
224
230
|
export type CardCoverTheme = {
|
|
225
231
|
colors?: string[];
|
|
226
232
|
colorSeed?: string;
|
|
@@ -241,11 +247,15 @@ export type CodeSyntaxThemeConfig =
|
|
|
241
247
|
export type CodeTheme = {
|
|
242
248
|
syntaxTheme?: CodeSyntaxThemeConfig;
|
|
243
249
|
};
|
|
250
|
+
export type TagTheme = {
|
|
251
|
+
color?: string | ThemeColorByMode;
|
|
252
|
+
};
|
|
244
253
|
export type DocsTheme = {
|
|
245
254
|
baseColor?: BaseColorOption | BaseColorByMode;
|
|
246
255
|
themeColor?: string | ThemeColorByMode;
|
|
247
256
|
card?: CardTheme;
|
|
248
257
|
code?: CodeTheme;
|
|
258
|
+
tag?: TagTheme;
|
|
249
259
|
};
|
|
250
260
|
export type AssistantIcon = {
|
|
251
261
|
src?: string;
|
|
@@ -428,6 +438,71 @@ function normalizeThemeColorConfig(
|
|
|
428
438
|
};
|
|
429
439
|
}
|
|
430
440
|
|
|
441
|
+
function normalizeNavTagConfig(
|
|
442
|
+
value: unknown,
|
|
443
|
+
currentPath: Path,
|
|
444
|
+
label: string,
|
|
445
|
+
): NavTag | undefined {
|
|
446
|
+
if (value === undefined || value === null) return undefined;
|
|
447
|
+
|
|
448
|
+
if (typeof value === "string") {
|
|
449
|
+
const trimmedText = value.trim();
|
|
450
|
+
if (trimmedText.length === 0) {
|
|
451
|
+
throwConfigError(`${label} cannot be empty.`, currentPath);
|
|
452
|
+
}
|
|
453
|
+
return trimmedText;
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
checkType(value, "object", currentPath, label);
|
|
457
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) {
|
|
458
|
+
throwConfigError(
|
|
459
|
+
`${label} must be a string or an object with text and optional color.`,
|
|
460
|
+
currentPath,
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
const tagConfig = value as Record<string, unknown>;
|
|
465
|
+
const allowedKeys = new Set(["text", "color"]);
|
|
466
|
+
for (const key of Object.keys(tagConfig)) {
|
|
467
|
+
if (!allowedKeys.has(key)) {
|
|
468
|
+
throwConfigError(`${label} object only supports 'text' and 'color'.`, [
|
|
469
|
+
...currentPath,
|
|
470
|
+
key,
|
|
471
|
+
]);
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
checkType(tagConfig.text, "string", [...currentPath, "text"], `${label} text`);
|
|
476
|
+
if (typeof tagConfig.text !== "string") {
|
|
477
|
+
throwConfigError(`${label} text must be a string.`, [
|
|
478
|
+
...currentPath,
|
|
479
|
+
"text",
|
|
480
|
+
]);
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
const trimmedText = tagConfig.text.trim();
|
|
484
|
+
if (trimmedText.length === 0) {
|
|
485
|
+
throwConfigError(`${label} text cannot be empty.`, [
|
|
486
|
+
...currentPath,
|
|
487
|
+
"text",
|
|
488
|
+
]);
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
const color =
|
|
492
|
+
tagConfig.color !== undefined
|
|
493
|
+
? normalizeThemeColorConfig(
|
|
494
|
+
tagConfig.color,
|
|
495
|
+
[...currentPath, "color"],
|
|
496
|
+
`${label} color`,
|
|
497
|
+
)
|
|
498
|
+
: undefined;
|
|
499
|
+
|
|
500
|
+
return {
|
|
501
|
+
text: trimmedText,
|
|
502
|
+
...(color !== undefined ? { color } : {}),
|
|
503
|
+
};
|
|
504
|
+
}
|
|
505
|
+
|
|
431
506
|
function normalizeHexColorArray(
|
|
432
507
|
value: unknown,
|
|
433
508
|
currentPath: Path,
|
|
@@ -909,6 +984,7 @@ async function validateNavigationNode(
|
|
|
909
984
|
checkType(item.expanded, "boolean", [...path, "expanded"], "Expanded");
|
|
910
985
|
|
|
911
986
|
validateIcon(item.icon, [...path, "icon"]);
|
|
987
|
+
item.tag = normalizeNavTagConfig(item.tag, [...path, "tag"], "Group tag");
|
|
912
988
|
|
|
913
989
|
// Check if pages array exists and validate children
|
|
914
990
|
if (!item.pages)
|
|
@@ -947,6 +1023,7 @@ async function validateNavigationNode(
|
|
|
947
1023
|
|
|
948
1024
|
// Validate optional title
|
|
949
1025
|
checkType(item.title, "string", [...path, "title"], "Page title");
|
|
1026
|
+
item.tag = normalizeNavTagConfig(item.tag, [...path, "tag"], "Page tag");
|
|
950
1027
|
|
|
951
1028
|
// Check D.2/D.3: Page cannot have group properties
|
|
952
1029
|
if ("expanded" in item)
|
|
@@ -971,7 +1048,11 @@ async function validateNavigationNode(
|
|
|
971
1048
|
|
|
972
1049
|
await validateNavOpenApiPage(item.openapi, [...path, "openapi"]);
|
|
973
1050
|
checkType(item.title, "string", [...path, "title"], "Open API page title");
|
|
974
|
-
|
|
1051
|
+
item.tag = normalizeNavTagConfig(
|
|
1052
|
+
item.tag,
|
|
1053
|
+
[...path, "tag"],
|
|
1054
|
+
"Open API page tag",
|
|
1055
|
+
);
|
|
975
1056
|
|
|
976
1057
|
if ("expanded" in item)
|
|
977
1058
|
throwConfigError("Open API page items cannot have 'expanded'.", [
|
|
@@ -1855,6 +1936,37 @@ function validateTheme(theme: DocsConfig["theme"]): void {
|
|
|
1855
1936
|
}
|
|
1856
1937
|
}
|
|
1857
1938
|
|
|
1939
|
+
if (theme.tag !== undefined) {
|
|
1940
|
+
checkType(theme.tag, "object", ["theme", "tag"], "Theme tag");
|
|
1941
|
+
if (
|
|
1942
|
+
typeof theme.tag !== "object" ||
|
|
1943
|
+
theme.tag === null ||
|
|
1944
|
+
Array.isArray(theme.tag)
|
|
1945
|
+
) {
|
|
1946
|
+
throwConfigError("Theme tag must be an object.", ["theme", "tag"]);
|
|
1947
|
+
}
|
|
1948
|
+
|
|
1949
|
+
const tagTheme = theme.tag as TagTheme & Record<string, unknown>;
|
|
1950
|
+
const allowedTagKeys = new Set(["color"]);
|
|
1951
|
+
for (const key of Object.keys(tagTheme)) {
|
|
1952
|
+
if (!allowedTagKeys.has(key)) {
|
|
1953
|
+
throwConfigError("Theme tag configuration only supports 'color'.", [
|
|
1954
|
+
"theme",
|
|
1955
|
+
"tag",
|
|
1956
|
+
key,
|
|
1957
|
+
]);
|
|
1958
|
+
}
|
|
1959
|
+
}
|
|
1960
|
+
|
|
1961
|
+
if (tagTheme.color !== undefined) {
|
|
1962
|
+
tagTheme.color = normalizeThemeColorConfig(
|
|
1963
|
+
tagTheme.color,
|
|
1964
|
+
["theme", "tag", "color"],
|
|
1965
|
+
"Theme tag color",
|
|
1966
|
+
);
|
|
1967
|
+
}
|
|
1968
|
+
}
|
|
1969
|
+
|
|
1858
1970
|
if (theme.card !== undefined) {
|
|
1859
1971
|
checkType(theme.card, "object", ["theme", "card"], "Theme card");
|
|
1860
1972
|
if (
|