radiant-docs 0.1.7 → 0.1.8
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 +28 -5
- package/package.json +3 -3
- package/template/astro.config.mjs +76 -3
- package/template/package-lock.json +924 -737
- package/template/package.json +7 -5
- package/template/scripts/generate-og-images.mjs +335 -0
- package/template/scripts/generate-og-metadata.mjs +173 -0
- package/template/scripts/rewrite-static-asset-host.mjs +408 -0
- package/template/scripts/stamp-image-versions.mjs +277 -0
- package/template/scripts/stamp-og-image-versions.mjs +199 -0
- package/template/scripts/stamp-pagefind-runtime-version.mjs +140 -0
- package/template/src/assets/fonts/geist-mono/cyrillic.woff2 +0 -0
- package/template/src/assets/fonts/geist-mono/latin-ext.woff2 +0 -0
- package/template/src/assets/fonts/geist-mono/latin.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/canadian-aboriginal.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/cherokee.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/latin-ext.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/latin.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/math.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/nushu.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/symbols.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/syriac.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/tifinagh.woff2 +0 -0
- package/template/src/assets/fonts/google-sans-flex/vietnamese.woff2 +0 -0
- package/template/src/components/Footer.astro +94 -0
- package/template/src/components/Header.astro +11 -66
- package/template/src/components/LogoLink.astro +103 -0
- package/template/src/components/MdxPage.astro +126 -11
- package/template/src/components/OpenApiPage.astro +1036 -69
- package/template/src/components/Search.astro +0 -2
- package/template/src/components/SidebarDropdown.astro +34 -14
- package/template/src/components/SidebarGroup.astro +3 -6
- package/template/src/components/SidebarLink.astro +22 -12
- package/template/src/components/SidebarMenu.astro +19 -16
- package/template/src/components/SidebarSegmented.astro +99 -0
- package/template/src/components/SidebarSubgroup.astro +12 -12
- package/template/src/components/ThemeSwitcher.astro +30 -7
- package/template/src/components/endpoint/PlaygroundBar.astro +32 -36
- package/template/src/components/endpoint/PlaygroundButton.astro +40 -4
- package/template/src/components/endpoint/PlaygroundField.astro +1068 -22
- package/template/src/components/endpoint/PlaygroundForm.astro +559 -61
- package/template/src/components/endpoint/RequestSnippets.astro +342 -193
- package/template/src/components/endpoint/ResponseDisplay.astro +161 -147
- package/template/src/components/endpoint/ResponseFieldTree.astro +134 -0
- package/template/src/components/endpoint/ResponseFields.astro +711 -68
- package/template/src/components/endpoint/ResponseSnippets.astro +299 -173
- package/template/src/components/sidebar/SidebarEndpointLink.astro +1 -1
- package/template/src/components/ui/CodeLanguageIcon.astro +19 -0
- package/template/src/components/ui/CodeTabEdge.astro +79 -0
- package/template/src/components/ui/Field.astro +103 -20
- package/template/src/components/ui/Icon.astro +32 -0
- package/template/src/components/ui/ListChevronsToggle.astro +31 -0
- package/template/src/components/ui/Tag.astro +1 -1
- package/template/src/components/user/{Accordian.astro → Accordion.astro} +6 -6
- package/template/src/components/user/Callout.astro +5 -9
- package/template/src/components/user/CodeBlock.astro +400 -0
- package/template/src/components/user/CodeGroup.astro +225 -0
- package/template/src/components/user/ComponentPreview.astro +1 -0
- package/template/src/components/user/ComponentPreviewBlock.astro +181 -0
- package/template/src/components/user/Image.astro +132 -0
- package/template/src/components/user/Steps.astro +1 -3
- package/template/src/components/user/Tabs.astro +2 -2
- package/template/src/content.config.ts +1 -0
- package/template/src/layouts/Layout.astro +109 -8
- package/template/src/lib/code/code-block.ts +546 -0
- package/template/src/lib/frontmatter-schema.ts +8 -7
- package/template/src/lib/mdx/remark-code-block-component.ts +342 -0
- package/template/src/lib/mdx/remark-demote-h1.ts +16 -0
- package/template/src/lib/pagefind.ts +19 -5
- package/template/src/lib/routes.ts +49 -31
- package/template/src/lib/utils.ts +20 -0
- package/template/src/lib/validation.ts +638 -200
- package/template/src/pages/[...slug].astro +18 -5
- package/template/src/styles/geist-mono.css +33 -0
- package/template/src/styles/global.css +89 -84
- package/template/src/styles/google-sans-flex.css +143 -0
- package/template/ec.config.mjs +0 -51
- /package/template/src/components/user/{AccordianGroup.astro → AccordionGroup.astro} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
import
|
|
3
|
-
import type
|
|
2
|
+
import Icon from "./ui/Icon.astro";
|
|
3
|
+
import { getConfig, type NavMenu } from "../lib/validation";
|
|
4
4
|
import SidebarMenu from "./SidebarMenu.astro";
|
|
5
5
|
import { slugify } from "../lib/utils";
|
|
6
6
|
import { getAllRoutes } from "../lib/routes";
|
|
@@ -13,13 +13,30 @@ interface Props {
|
|
|
13
13
|
const { menu, parentSlug = "" } = Astro.props;
|
|
14
14
|
|
|
15
15
|
const pathname = Astro.url.pathname;
|
|
16
|
+
const config = await getConfig();
|
|
17
|
+
const routes = await getAllRoutes();
|
|
16
18
|
|
|
17
19
|
let currentMenuIndex = menu.items.findIndex(
|
|
18
|
-
(i) => slugify(i.label) === pathname.split("/")[1]
|
|
20
|
+
(i) => slugify(i.label) === pathname.split("/")[1],
|
|
19
21
|
);
|
|
20
|
-
currentMenuIndex = currentMenuIndex === -1 ? 0 : currentMenuIndex;
|
|
21
22
|
|
|
22
|
-
|
|
23
|
+
if (pathname === "/" && config.home) {
|
|
24
|
+
const homeRoute = routes.find(
|
|
25
|
+
(route) => route.type === "mdx" && route.filePath === config.home,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (homeRoute) {
|
|
29
|
+
const homeTopLevelSegment = homeRoute.slug.split("/")[0];
|
|
30
|
+
const homeMenuIndex = menu.items.findIndex(
|
|
31
|
+
(item) => slugify(item.label) === homeTopLevelSegment,
|
|
32
|
+
);
|
|
33
|
+
if (homeMenuIndex !== -1) {
|
|
34
|
+
currentMenuIndex = homeMenuIndex;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
currentMenuIndex = currentMenuIndex === -1 ? 0 : currentMenuIndex;
|
|
23
40
|
|
|
24
41
|
let firstHrefOfMenuItems: string[] = [];
|
|
25
42
|
|
|
@@ -33,7 +50,11 @@ for (const route of routes) {
|
|
|
33
50
|
|
|
34
51
|
if (!seen.has(topLevel)) {
|
|
35
52
|
seen.add(topLevel);
|
|
36
|
-
|
|
53
|
+
const href =
|
|
54
|
+
route.type === "mdx" && config.home && route.filePath === config.home
|
|
55
|
+
? "/"
|
|
56
|
+
: `/${slug}`;
|
|
57
|
+
firstHrefOfMenuItems.push(href);
|
|
37
58
|
}
|
|
38
59
|
}
|
|
39
60
|
|
|
@@ -51,8 +72,7 @@ const currentPrefix = parentSlug
|
|
|
51
72
|
<div
|
|
52
73
|
class:list={[
|
|
53
74
|
"mt-3 mx-2",
|
|
54
|
-
menu.label &&
|
|
55
|
-
"rounded-lg bg-neutral-100/80 px-0.5 pb-0.5 inset-shadow-xs inset-shadow-neutral-700/5 border border-neutral-200/60",
|
|
75
|
+
menu.label && "rounded-lg bg-neutral-100 p-[3px]",
|
|
56
76
|
]}
|
|
57
77
|
>
|
|
58
78
|
{
|
|
@@ -64,7 +84,7 @@ const currentPrefix = parentSlug
|
|
|
64
84
|
}
|
|
65
85
|
<div class="relative">
|
|
66
86
|
<button
|
|
67
|
-
class="flex items-center w-full text-sm bg-white border border-neutral-200 rounded-lg shadow-
|
|
87
|
+
class="flex items-center w-full text-sm text-neutral-700 bg-white border-t border-x border-neutral-200/70 rounded-lg shadow-sm shadow-neutral-200 px-3 py-2 cursor-pointer"
|
|
68
88
|
x-on:click="open = true"
|
|
69
89
|
aria-haspopup="menu"
|
|
70
90
|
aria-expanded
|
|
@@ -72,8 +92,8 @@ const currentPrefix = parentSlug
|
|
|
72
92
|
{
|
|
73
93
|
menu.items[currentMenuIndex].icon && (
|
|
74
94
|
<Icon
|
|
75
|
-
class="mr-2 size-4"
|
|
76
|
-
name={
|
|
95
|
+
class="mr-2 size-4 opacity-80"
|
|
96
|
+
name={menu.items[currentMenuIndex].icon}
|
|
77
97
|
/>
|
|
78
98
|
)
|
|
79
99
|
}
|
|
@@ -109,12 +129,12 @@ const currentPrefix = parentSlug
|
|
|
109
129
|
? "before:bg-neutral-200/50 text-neutral-900"
|
|
110
130
|
: "hover:before:bg-neutral-100/70",
|
|
111
131
|
]}
|
|
112
|
-
href={
|
|
132
|
+
href={firstHrefOfMenuItems[index] ?? "/"}
|
|
113
133
|
>
|
|
114
134
|
{menu.items[index].icon && (
|
|
115
135
|
<Icon
|
|
116
|
-
class="mr-2 size-4"
|
|
117
|
-
name={
|
|
136
|
+
class="mr-2 size-4 opacity-75"
|
|
137
|
+
name={menu.items[index].icon}
|
|
118
138
|
/>
|
|
119
139
|
)}
|
|
120
140
|
{label}
|
|
@@ -4,7 +4,7 @@ import SidebarLink from "./SidebarLink.astro";
|
|
|
4
4
|
import SidebarSubgroup from "./SidebarSubgroup.astro";
|
|
5
5
|
import { slugify } from "../lib/utils";
|
|
6
6
|
import Tag from "./ui/Tag.astro";
|
|
7
|
-
import
|
|
7
|
+
import Icon from "./ui/Icon.astro";
|
|
8
8
|
|
|
9
9
|
interface Props {
|
|
10
10
|
item: NavGroup;
|
|
@@ -19,11 +19,7 @@ const currentPrefix = parentSlug ? `${parentSlug}/${groupSlug}` : groupSlug;
|
|
|
19
19
|
|
|
20
20
|
<li>
|
|
21
21
|
<div class:list={["text-sm font-semibold mb-2 flex items-center gap-2 px-2"]}>
|
|
22
|
-
{
|
|
23
|
-
item.icon && (
|
|
24
|
-
<Icon name={`lucide:${item.icon}`} class="size-4 text-neutral-500" />
|
|
25
|
-
)
|
|
26
|
-
}
|
|
22
|
+
{item.icon && <Icon name={item.icon} class="size-4 text-neutral-500" />}
|
|
27
23
|
{item.group}
|
|
28
24
|
{item.tag && <Tag>{item.tag}</Tag>}
|
|
29
25
|
</div>
|
|
@@ -39,6 +35,7 @@ const currentPrefix = parentSlug ? `${parentSlug}/${groupSlug}` : groupSlug;
|
|
|
39
35
|
path={child.page}
|
|
40
36
|
icon={child.icon}
|
|
41
37
|
tag={child.tag}
|
|
38
|
+
title={child.title}
|
|
42
39
|
groupSlug={currentPrefix}
|
|
43
40
|
/>
|
|
44
41
|
) : (
|
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
---
|
|
2
|
-
import {
|
|
3
|
-
import
|
|
2
|
+
import { buildMdxPageHref, deriveTitleFromEntryId } from "../lib/utils";
|
|
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
7
|
|
|
7
8
|
interface Props {
|
|
8
9
|
path: string;
|
|
9
10
|
groupSlug?: string;
|
|
10
|
-
icon?: string;
|
|
11
|
+
icon?: string | null;
|
|
11
12
|
tag?: string;
|
|
13
|
+
title?: string;
|
|
12
14
|
}
|
|
13
15
|
|
|
14
|
-
const {
|
|
16
|
+
const {
|
|
17
|
+
path: filePath,
|
|
18
|
+
groupSlug = "",
|
|
19
|
+
icon,
|
|
20
|
+
tag,
|
|
21
|
+
title: configTitle,
|
|
22
|
+
} = Astro.props;
|
|
15
23
|
|
|
16
24
|
const docs = await getCollection("docs");
|
|
25
|
+
const config = await getConfig();
|
|
17
26
|
|
|
18
27
|
// Find the entry matching this path
|
|
19
28
|
const entry = docs.find((doc: any) => {
|
|
@@ -25,13 +34,14 @@ if (!entry) {
|
|
|
25
34
|
throw new Error("Entry not found");
|
|
26
35
|
}
|
|
27
36
|
|
|
28
|
-
//
|
|
29
|
-
const
|
|
30
|
-
const text = entry.data.title || deriveTitleFromEntryId(entry.id);
|
|
37
|
+
// Use title from docs.json config if provided, otherwise derive from entry id
|
|
38
|
+
const text = configTitle || deriveTitleFromEntryId(entry.id);
|
|
31
39
|
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
const href = buildMdxPageHref({
|
|
41
|
+
filePath,
|
|
42
|
+
groupSlug,
|
|
43
|
+
homePath: config.home,
|
|
44
|
+
});
|
|
35
45
|
|
|
36
46
|
// Normalize paths for comparison (remove trailing slashes)
|
|
37
47
|
const currentPath = Astro.url.pathname.replace(/\/$/, "");
|
|
@@ -42,14 +52,14 @@ const isActive = currentPath === targetPath;
|
|
|
42
52
|
<a
|
|
43
53
|
href={href}
|
|
44
54
|
class:list={[
|
|
45
|
-
"block px-2 py-[7px] text-sm relative z-0
|
|
55
|
+
"block px-2 py-[7px] text-sm relative z-0 before:-z-10 before:absolute before:inset-x-0 before:inset-y-px before:rounded-md before:duration-150",
|
|
46
56
|
isActive
|
|
47
57
|
? "before:bg-neutral-200/50 dark:before:bg-neutral-800 text-neutral-900 dark:text-neutral-200"
|
|
48
58
|
: "text-neutral-600 dark:text-neutral-400 hover:before:bg-neutral-100/70 dark:hover:before:bg-neutral-800/50 hover:text-neutral-900 dark:hover:text-neutral-300",
|
|
49
59
|
]}
|
|
50
60
|
>
|
|
51
61
|
<div class="flex items-center gap-2">
|
|
52
|
-
{icon && <Icon name={
|
|
62
|
+
{icon && <Icon name={icon} class="size-4 opacity-75" />}
|
|
53
63
|
{text}
|
|
54
64
|
{tag && <Tag>{tag}</Tag>}
|
|
55
65
|
</div>
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import type { NavGroup, NavigationItem, NavPage } from "../lib/validation";
|
|
3
3
|
import SidebarOpenApi from "./sidebar/SidebarOpenApi.astro";
|
|
4
4
|
import SidebarDropdown from "./SidebarDropdown.astro";
|
|
5
|
+
import SidebarSegmented from "./SidebarSegmented.astro";
|
|
5
6
|
import SidebarGroup from "./SidebarGroup.astro";
|
|
6
7
|
import SidebarLink from "./SidebarLink.astro";
|
|
7
8
|
|
|
@@ -15,31 +16,33 @@ let { navigation, parentSlug = "" } = Astro.props;
|
|
|
15
16
|
|
|
16
17
|
{
|
|
17
18
|
navigation.pages ? (
|
|
18
|
-
<ul class="px-2 pt-4 pb-3">
|
|
19
|
-
{navigation.pages.map((item) =>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
<SidebarLink path={item} />
|
|
23
|
-
|
|
19
|
+
<ul class="px-2 pt-4 pb-3 [&>:nth-child(n+2)]:mt-6 [&>:nth-child(n+2)]:pt-[25px] [&>:nth-child(n+2)]:relative [&>:nth-child(n+2)]:before:absolute [&>:nth-child(n+2)]:before:top-0 [&>:nth-child(n+2)]:before:inset-x-0 [&>:nth-child(n+2)]:before:h-px [&>:nth-child(n+2)]:before:bg-linear-[90deg,transparent,var(--color-neutral-200)_20%,var(--color-neutral-200)_80%,transparent] dark:[&>:nth-child(n+2)]:before:bg-linear-[90deg,transparent,var(--color-neutral-700)_20%,var(--color-neutral-700)_80%,transparent]">
|
|
20
|
+
{navigation.pages.map((item) =>
|
|
21
|
+
typeof item === "string" ? (
|
|
22
|
+
<li>
|
|
23
|
+
<SidebarLink path={item} groupSlug={parentSlug} />
|
|
24
|
+
</li>
|
|
25
|
+
) : "group" in item ? (
|
|
26
|
+
<SidebarGroup item={item as NavGroup} parentSlug={parentSlug} />
|
|
27
|
+
) : (
|
|
28
|
+
<li>
|
|
24
29
|
<SidebarLink
|
|
25
30
|
path={(item as NavPage).page}
|
|
26
31
|
icon={(item as NavPage).icon}
|
|
27
32
|
tag={(item as NavPage).tag}
|
|
33
|
+
title={(item as NavPage).title}
|
|
34
|
+
groupSlug={parentSlug}
|
|
28
35
|
/>
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
)
|
|
32
|
-
</ul>
|
|
33
|
-
) : navigation.groups ? (
|
|
34
|
-
<ul class="px-2 pt-4 pb-3 [&>:nth-child(n+2)]:mt-5 [&>:nth-child(n+2)]:pt-[25px] [&>:nth-child(n+2)]:relative [&>:nth-child(n+2)]:before:absolute [&>:nth-child(n+2)]:before:top-0 [&>:nth-child(n+2)]:before:inset-x-0 [&>:nth-child(n+2)]:before:h-px [&>:nth-child(n+2)]:before:bg-linear-[90deg,transparent,var(--color-neutral-200)_20%,var(--color-neutral-200)_80%,transparent] dark:[&>:nth-child(n+2)]:before:bg-linear-[90deg,transparent,var(--color-neutral-700)_20%,var(--color-neutral-700)_80%,transparent]">
|
|
35
|
-
{navigation.groups.map((item) => (
|
|
36
|
-
<SidebarGroup item={item as NavGroup} parentSlug={parentSlug} />
|
|
37
|
-
))}
|
|
36
|
+
</li>
|
|
37
|
+
),
|
|
38
|
+
)}
|
|
38
39
|
</ul>
|
|
39
40
|
) : navigation.menu ? (
|
|
40
41
|
!navigation.menu.type || navigation.menu.type === "dropdown" ? (
|
|
41
42
|
<SidebarDropdown menu={navigation.menu} parentSlug={parentSlug} />
|
|
42
|
-
) : navigation.menu.type === "
|
|
43
|
+
) : navigation.menu.type === "segmented" ? (
|
|
44
|
+
<SidebarSegmented menu={navigation.menu} parentSlug={parentSlug} />
|
|
45
|
+
) : null
|
|
43
46
|
) : navigation.openapi ? (
|
|
44
47
|
<SidebarOpenApi openapi={navigation.openapi} parentSlug={parentSlug} />
|
|
45
48
|
) : null
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Icon from "./ui/Icon.astro";
|
|
3
|
+
import { getConfig, type NavMenu } from "../lib/validation";
|
|
4
|
+
import SidebarMenu from "./SidebarMenu.astro";
|
|
5
|
+
import { slugify } from "../lib/utils";
|
|
6
|
+
import { getAllRoutes } from "../lib/routes";
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
menu: NavMenu;
|
|
10
|
+
parentSlug?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const { menu, parentSlug = "" } = Astro.props;
|
|
14
|
+
|
|
15
|
+
const pathname = Astro.url.pathname;
|
|
16
|
+
const config = await getConfig();
|
|
17
|
+
const routes = await getAllRoutes();
|
|
18
|
+
|
|
19
|
+
let currentMenuIndex = menu.items.findIndex(
|
|
20
|
+
(i) => slugify(i.label) === pathname.split("/")[1],
|
|
21
|
+
);
|
|
22
|
+
|
|
23
|
+
if (pathname === "/" && config.home) {
|
|
24
|
+
const homeRoute = routes.find(
|
|
25
|
+
(route) => route.type === "mdx" && route.filePath === config.home,
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
if (homeRoute) {
|
|
29
|
+
const homeTopLevelSegment = homeRoute.slug.split("/")[0];
|
|
30
|
+
const homeMenuIndex = menu.items.findIndex(
|
|
31
|
+
(item) => slugify(item.label) === homeTopLevelSegment,
|
|
32
|
+
);
|
|
33
|
+
if (homeMenuIndex !== -1) {
|
|
34
|
+
currentMenuIndex = homeMenuIndex;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
currentMenuIndex = currentMenuIndex === -1 ? 0 : currentMenuIndex;
|
|
40
|
+
|
|
41
|
+
let firstHrefOfMenuItems: string[] = [];
|
|
42
|
+
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
|
|
45
|
+
for (const route of routes) {
|
|
46
|
+
const slug = route.slug;
|
|
47
|
+
|
|
48
|
+
const parts = slug.split("/");
|
|
49
|
+
const topLevel = parts[0];
|
|
50
|
+
|
|
51
|
+
if (!seen.has(topLevel)) {
|
|
52
|
+
seen.add(topLevel);
|
|
53
|
+
const href =
|
|
54
|
+
route.type === "mdx" && config.home && route.filePath === config.home
|
|
55
|
+
? "/"
|
|
56
|
+
: `/${slug}`;
|
|
57
|
+
firstHrefOfMenuItems.push(href);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const selectedMenuItem = menu.items[currentMenuIndex];
|
|
62
|
+
const menuItemSlug = slugify(selectedMenuItem.label);
|
|
63
|
+
const currentPrefix = parentSlug
|
|
64
|
+
? `${parentSlug}/${menuItemSlug}`
|
|
65
|
+
: menuItemSlug;
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
<div class="mt-3 mx-2">
|
|
69
|
+
{
|
|
70
|
+
menu.label && (
|
|
71
|
+
<label class="font-semibold text-xs px-2 pb-1 block">{menu.label}</label>
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
<ul class="rounded-lg bg-neutral-100 p-[3px]">
|
|
75
|
+
{
|
|
76
|
+
menu.items.map((item, index) => (
|
|
77
|
+
<li>
|
|
78
|
+
<a
|
|
79
|
+
class="flex items-center px-3 py-1.5 cursor-pointer text-sm rounded-md border-x border-t"
|
|
80
|
+
class:list={[
|
|
81
|
+
index === currentMenuIndex
|
|
82
|
+
? "bg-white border-neutral-200/70 shadow-sm shadow-neutral-200 text-neutral-900"
|
|
83
|
+
: "text-neutral-600 border-transparent",
|
|
84
|
+
]}
|
|
85
|
+
href={firstHrefOfMenuItems[index] ?? "/"}
|
|
86
|
+
>
|
|
87
|
+
{item.icon && <Icon class="mr-2 size-4" name={item.icon} />}
|
|
88
|
+
{item.label}
|
|
89
|
+
</a>
|
|
90
|
+
</li>
|
|
91
|
+
))
|
|
92
|
+
}
|
|
93
|
+
</ul>
|
|
94
|
+
</div>
|
|
95
|
+
|
|
96
|
+
<SidebarMenu
|
|
97
|
+
navigation={menu.items[currentMenuIndex].submenu}
|
|
98
|
+
parentSlug={currentPrefix}
|
|
99
|
+
/>
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
---
|
|
2
|
-
import type
|
|
2
|
+
import { getConfig, type NavGroup } from "../lib/validation";
|
|
3
3
|
import SidebarLink from "./SidebarLink.astro";
|
|
4
|
-
import { slugify } from "../lib/utils";
|
|
4
|
+
import { buildMdxPageHref, slugify } from "../lib/utils";
|
|
5
5
|
import Tag from "./ui/Tag.astro";
|
|
6
|
-
import
|
|
6
|
+
import Icon from "./ui/Icon.astro";
|
|
7
7
|
|
|
8
8
|
interface Props {
|
|
9
9
|
item: NavGroup;
|
|
@@ -14,6 +14,7 @@ const { item, parentSlug } = Astro.props;
|
|
|
14
14
|
|
|
15
15
|
const groupSlug = slugify(item.group);
|
|
16
16
|
const currentPrefix = `${parentSlug}/${groupSlug}`;
|
|
17
|
+
const config = await getConfig();
|
|
17
18
|
|
|
18
19
|
const groupId = `group-${currentPrefix}`;
|
|
19
20
|
const listId = `list-${groupId}`;
|
|
@@ -24,9 +25,11 @@ const containsActivePage = item.pages.some((child) => {
|
|
|
24
25
|
typeof child === "string" ? child : "pages" in child ? null : child.page;
|
|
25
26
|
|
|
26
27
|
if (pagePath) {
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
const href = buildMdxPageHref({
|
|
29
|
+
filePath: pagePath,
|
|
30
|
+
groupSlug: currentPrefix,
|
|
31
|
+
homePath: config.home,
|
|
32
|
+
});
|
|
30
33
|
|
|
31
34
|
// Normalize paths for comparison (remove trailing slashes)
|
|
32
35
|
const normalizedCurrent = Astro.url.pathname.replace(/\/$/, "");
|
|
@@ -52,15 +55,11 @@ const containsActivePage = item.pages.some((child) => {
|
|
|
52
55
|
type="button"
|
|
53
56
|
@click="expanded = !expanded"
|
|
54
57
|
class:list={[
|
|
55
|
-
"flex items-center justify-between gap-2 w-full px-2 py-[7px] text-sm
|
|
58
|
+
"flex items-center justify-between gap-2 w-full px-2 py-[7px] text-sm text-neutral-600 dark:text-neutral-400 hover:text-neutral-900 dark:hover:text-neutral-300 cursor-pointer relative z-0 before:-z-10 before:absolute before:inset-x-0 before:inset-y-px before:rounded-md before:transition-colors hover:before:bg-neutral-100/70 dark:hover:before:bg-neutral-800/50",
|
|
56
59
|
]}
|
|
57
60
|
>
|
|
58
61
|
<div class="flex items-center gap-2">
|
|
59
|
-
{
|
|
60
|
-
item.icon && (
|
|
61
|
-
<Icon name={`lucide:${item.icon}`} class="size-4 opacity-75" />
|
|
62
|
-
)
|
|
63
|
-
}
|
|
62
|
+
{item.icon && <Icon name={item.icon} class="size-4 opacity-75" />}
|
|
64
63
|
{item.group}
|
|
65
64
|
{item.tag && <Tag>{item.tag}</Tag>}
|
|
66
65
|
</div>
|
|
@@ -98,6 +97,7 @@ const containsActivePage = item.pages.some((child) => {
|
|
|
98
97
|
path={child.page}
|
|
99
98
|
icon={child.icon}
|
|
100
99
|
tag={child.tag}
|
|
100
|
+
title={child.title}
|
|
101
101
|
groupSlug={currentPrefix}
|
|
102
102
|
/>
|
|
103
103
|
) : null}
|
|
@@ -4,13 +4,30 @@ import { Icon } from "astro-icon/components";
|
|
|
4
4
|
|
|
5
5
|
<div
|
|
6
6
|
x-data="{
|
|
7
|
+
forcedTheme: (() => {
|
|
8
|
+
const mode = new URLSearchParams(window.location.search).get('mode');
|
|
9
|
+
return mode === 'light' || mode === 'dark' ? mode : null;
|
|
10
|
+
})(),
|
|
7
11
|
theme: localStorage.getItem('theme') || 'system',
|
|
8
12
|
init() {
|
|
13
|
+
if (this.forcedTheme) {
|
|
14
|
+
this.theme = this.forcedTheme;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
this.updateDOM();
|
|
18
|
+
|
|
9
19
|
// 1. Initial positioning
|
|
10
20
|
this.$nextTick(() => this.updateMarker());
|
|
11
21
|
|
|
12
22
|
// Watch for changes to the 'theme' state
|
|
13
23
|
this.$watch('theme', val => {
|
|
24
|
+
if (this.forcedTheme) {
|
|
25
|
+
this.theme = this.forcedTheme;
|
|
26
|
+
this.updateDOM();
|
|
27
|
+
this.$nextTick(() => this.updateMarker());
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
14
31
|
localStorage.setItem('theme', val);
|
|
15
32
|
this.updateDOM();
|
|
16
33
|
this.$nextTick(() => this.updateMarker())
|
|
@@ -27,10 +44,12 @@ import { Icon } from "astro-icon/components";
|
|
|
27
44
|
},
|
|
28
45
|
updateDOM() {
|
|
29
46
|
document.documentElement.classList.add('is-switching-theme')
|
|
30
|
-
const
|
|
31
|
-
|
|
47
|
+
const activeTheme = this.forcedTheme || this.theme;
|
|
48
|
+
const isDark = activeTheme === 'dark' ||
|
|
49
|
+
(activeTheme === 'system' && window.matchMedia('(prefers-color-scheme: dark)').matches);
|
|
32
50
|
|
|
33
51
|
document.documentElement.classList.toggle('dark', isDark);
|
|
52
|
+
document.documentElement.dataset.theme = isDark ? 'dark' : 'light';
|
|
34
53
|
setTimeout(() => {
|
|
35
54
|
document.documentElement.classList.remove('is-switching-theme');
|
|
36
55
|
}, 200);
|
|
@@ -38,7 +57,8 @@ import { Icon } from "astro-icon/components";
|
|
|
38
57
|
markerStyle: { left: null, width: null },
|
|
39
58
|
updateMarker() {
|
|
40
59
|
// Use the theme name as the ref key
|
|
41
|
-
const
|
|
60
|
+
const markerTheme = this.forcedTheme || this.theme;
|
|
61
|
+
const el = this.$refs[markerTheme];
|
|
42
62
|
if (el) {
|
|
43
63
|
this.markerStyle = {
|
|
44
64
|
left: el.offsetLeft + 'px',
|
|
@@ -47,10 +67,10 @@ import { Icon } from "astro-icon/components";
|
|
|
47
67
|
}
|
|
48
68
|
}
|
|
49
69
|
}"
|
|
50
|
-
class="relative flex gap-[3px] p-
|
|
70
|
+
class="relative flex gap-[3px] p-px bg-neutral-100 dark:bg-neutral-800 rounded-full w-fit inset-shadow-sm inset-shadow-neutral-100/80 text-neutral-500"
|
|
51
71
|
>
|
|
52
72
|
<div
|
|
53
|
-
class="anchor-pill absolute top-
|
|
73
|
+
class="anchor-pill absolute top-px bottom-px bg-white dark:bg-neutral-700 rounded-full shadow-xs border-[0.5px] border-neutral-200/80 ease-out z-0 flex items-center justify-center animate-[scaleIn_0.5s_ease-out]"
|
|
54
74
|
style="left: 3px;"
|
|
55
75
|
:style="markerStyle.width ? `left: ${markerStyle.left}; width: ${markerStyle.width}` : ''"
|
|
56
76
|
>
|
|
@@ -59,6 +79,7 @@ import { Icon } from "astro-icon/components";
|
|
|
59
79
|
x-ref="light"
|
|
60
80
|
@click="theme = 'light'"
|
|
61
81
|
:class="theme === 'light' ? 'text-neutral-800' : 'text-neutral-500'"
|
|
82
|
+
:disabled="Boolean(forcedTheme)"
|
|
62
83
|
class="p-[5px] rounded-full text-sm font-medium transition-all cursor-pointer z-10"
|
|
63
84
|
>
|
|
64
85
|
<Icon name="lucide:sun-medium" class="size-[13px]" />
|
|
@@ -68,6 +89,7 @@ import { Icon } from "astro-icon/components";
|
|
|
68
89
|
x-ref="dark"
|
|
69
90
|
@click="theme = 'dark'"
|
|
70
91
|
:class="theme === 'dark' ? 'text-neutral-300' : 'text-neutral-500'"
|
|
92
|
+
:disabled="Boolean(forcedTheme)"
|
|
71
93
|
class="p-[5px] rounded-full text-sm font-medium transition-all cursor-pointer z-10"
|
|
72
94
|
>
|
|
73
95
|
<Icon name="lucide:moon" class="size-[13px]" />
|
|
@@ -77,8 +99,9 @@ import { Icon } from "astro-icon/components";
|
|
|
77
99
|
x-ref="system"
|
|
78
100
|
@click="theme = 'system'"
|
|
79
101
|
:class="theme === 'system' ? 'text-neutral-800 dark:text-neutral-300' : 'text-neutral-500'"
|
|
80
|
-
|
|
102
|
+
:disabled="Boolean(forcedTheme)"
|
|
103
|
+
class="p-[6px] rounded-full text-sm font-medium transition-all cursor-pointer z-10"
|
|
81
104
|
>
|
|
82
|
-
<Icon name="lucide:monitor" class="size-[
|
|
105
|
+
<Icon name="lucide:monitor" class="size-[12px]" />
|
|
83
106
|
</button>
|
|
84
107
|
</div>
|
|
@@ -12,23 +12,20 @@ const { route, serverUrl } = Astro.props;
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
14
|
<div
|
|
15
|
-
class="min-w-0 flex items-center
|
|
15
|
+
class="min-w-0 flex-1 flex items-center p-1 border bg-white rounded-xl shadow-xs overflow-hidden"
|
|
16
16
|
>
|
|
17
|
-
<
|
|
18
|
-
class=
|
|
17
|
+
<span
|
|
18
|
+
class:list={[
|
|
19
|
+
"shrink-0 inline-block px-1.5 ml-1 text-sm font-semibold rounded-md uppercase border",
|
|
20
|
+
methodColors[route.openApiMethod.toLowerCase()] || methodColors.get,
|
|
21
|
+
]}
|
|
19
22
|
>
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
{route.openApiMethod}
|
|
27
|
-
</span>
|
|
28
|
-
<code
|
|
29
|
-
class="group flex-1 mx-2 h-[30px] flex items-center text-[13px] text-neutral-600 min-w-0 relative cursor-pointer"
|
|
30
|
-
@click="copyPath()"
|
|
31
|
-
x-data=`{
|
|
23
|
+
{route.openApiMethod}
|
|
24
|
+
</span>
|
|
25
|
+
<code
|
|
26
|
+
class="group flex-1 mx-2 h-[30px] flex items-center text-[13px] text-neutral-600 min-w-0 relative cursor-pointer"
|
|
27
|
+
@click="copyPath()"
|
|
28
|
+
x-data=`{
|
|
32
29
|
copied: false,
|
|
33
30
|
path: '${serverUrl ? serverUrl + route.openApiPath : route.openApiPath}',
|
|
34
31
|
async copyPath() {
|
|
@@ -43,26 +40,25 @@ const { route, serverUrl } = Astro.props;
|
|
|
43
40
|
}
|
|
44
41
|
}
|
|
45
42
|
}`
|
|
43
|
+
>
|
|
44
|
+
<span class="truncate min-w-0 flex-1">
|
|
45
|
+
{route.openApiPath}
|
|
46
|
+
</span>
|
|
47
|
+
<div
|
|
48
|
+
class="absolute right-0 top-1/2 -translate-y-1/2 flex items-center gap-1 text-[12px] px-1.5 py-px bg-white border border-neutral-200 rounded-md duration-200 opacity-0 scale-75 group-hover:scale-100 group-hover:opacity-100 group-hover:duration-200 group-hover:ease-out group-hover:delay-75"
|
|
46
49
|
>
|
|
47
|
-
<
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
x-bind:class="copied ? 'scale-100 opacity-100 delay-75':'scale-0 opacity-0'"
|
|
62
|
-
/>
|
|
63
|
-
Copy
|
|
64
|
-
</div>
|
|
65
|
-
</code>
|
|
66
|
-
<slot />
|
|
67
|
-
</div>
|
|
50
|
+
<Icon
|
|
51
|
+
name="lucide:copy"
|
|
52
|
+
class="**:stroke-[2.4]"
|
|
53
|
+
x-bind:class="copied ? 'scale-0 opacity-0 delay-0 duration-100':'duration-150 delay-75'"
|
|
54
|
+
/>
|
|
55
|
+
<Icon
|
|
56
|
+
name="lucide:check"
|
|
57
|
+
class="absolute text-green-700 duration-150 **:stroke-3"
|
|
58
|
+
x-bind:class="copied ? 'scale-100 opacity-100 delay-75':'scale-0 opacity-0'"
|
|
59
|
+
/>
|
|
60
|
+
Copy
|
|
61
|
+
</div>
|
|
62
|
+
</code>
|
|
63
|
+
<slot />
|
|
68
64
|
</div>
|