radiant-docs 0.1.41 → 0.1.43
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/astro.config.mjs +44 -40
- package/template/package-lock.json +7 -0
- package/template/package.json +1 -0
- package/template/src/components/Header.astro +150 -16
- package/template/src/components/MdxPage.astro +82 -23
- package/template/src/components/PagePagination.astro +44 -8
- package/template/src/components/Sidebar.astro +10 -1
- package/template/src/components/TableOfContents.astro +159 -53
- package/template/src/components/chat/AssistantDocsWidget.tsx +221 -8
- package/template/src/components/chat/AssistantEmbedPanel.tsx +1090 -104
- package/template/src/components/user/Accordion.astro +2 -2
- package/template/src/components/user/AccordionGroup.astro +1 -1
- package/template/src/components/user/Callout.astro +2 -2
- package/template/src/components/user/Card.astro +488 -0
- package/template/src/components/user/CardGradient.astro +964 -0
- package/template/src/components/user/CodeBlock.astro +1 -1
- package/template/src/components/user/CodeGroup.astro +1 -1
- package/template/src/components/user/Column.astro +25 -0
- package/template/src/components/user/Columns.astro +200 -0
- package/template/src/components/user/ComponentPreviewBlock.astro +11 -1
- package/template/src/components/user/Image.astro +1 -1
- package/template/src/components/user/Step.astro +1 -1
- package/template/src/components/user/Steps.astro +1 -1
- package/template/src/components/user/Tab.astro +1 -3
- package/template/src/components/user/Tabs.astro +2 -2
- package/template/src/layouts/Layout.astro +2 -4
- package/template/src/lib/assistant-chrome-defaults.ts +12 -0
- package/template/src/lib/assistant-embed-script.ts +209 -18
- package/template/src/lib/mdx/rehype-prefix-preview-heading-ids.ts +52 -0
- package/template/src/lib/mdx/remark-code-block-component.ts +14 -8
- package/template/src/lib/validation.ts +325 -75
- package/template/src/styles/global.css +81 -4
- package/template/src/components/chat/AskAiWidget.tsx +0 -2011
package/package.json
CHANGED
|
@@ -14,54 +14,57 @@ import remarkDemoteH1 from "./src/lib/mdx/remark-demote-h1";
|
|
|
14
14
|
import remarkResolveInternalLinks from "./src/lib/mdx/remark-resolve-internal-links";
|
|
15
15
|
import remarkStandaloneImageComponent from "./src/lib/mdx/remark-standalone-image-component";
|
|
16
16
|
import rehypeExternalLinks from "./src/lib/mdx/rehype-external-links";
|
|
17
|
+
import rehypePrefixPreviewHeadingIds from "./src/lib/mdx/rehype-prefix-preview-heading-ids";
|
|
17
18
|
import remarkGfm from "remark-gfm";
|
|
18
19
|
import rehypeSlug from "rehype-slug";
|
|
19
20
|
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
|
20
21
|
|
|
21
|
-
|
|
22
|
+
function createLucideIconNode({ className, path }) {
|
|
23
|
+
return {
|
|
24
|
+
type: "element",
|
|
25
|
+
tagName: "svg",
|
|
26
|
+
properties: {
|
|
27
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
28
|
+
width: "24",
|
|
29
|
+
height: "24",
|
|
30
|
+
viewBox: "0 0 24 24",
|
|
31
|
+
fill: "none",
|
|
32
|
+
stroke: "currentColor",
|
|
33
|
+
strokeWidth: "2",
|
|
34
|
+
strokeLinecap: "round",
|
|
35
|
+
strokeLinejoin: "round",
|
|
36
|
+
className,
|
|
37
|
+
ariaHidden: "true",
|
|
38
|
+
},
|
|
39
|
+
children: [
|
|
40
|
+
{
|
|
41
|
+
type: "element",
|
|
42
|
+
tagName: "path",
|
|
43
|
+
properties: {
|
|
44
|
+
d: path,
|
|
45
|
+
},
|
|
46
|
+
children: [],
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const headingAnchorContent = {
|
|
22
53
|
type: "element",
|
|
23
|
-
tagName: "
|
|
54
|
+
tagName: "span",
|
|
24
55
|
properties: {
|
|
25
|
-
|
|
26
|
-
width: "24",
|
|
27
|
-
height: "24",
|
|
28
|
-
viewBox: "0 0 24 24",
|
|
29
|
-
fill: "none",
|
|
30
|
-
stroke: "currentColor",
|
|
31
|
-
strokeWidth: "2",
|
|
32
|
-
strokeLinecap: "round",
|
|
33
|
-
strokeLinejoin: "round",
|
|
34
|
-
className: ["heading-anchor-icon"],
|
|
56
|
+
className: ["heading-anchor-icons"],
|
|
35
57
|
ariaHidden: "true",
|
|
36
58
|
},
|
|
37
59
|
children: [
|
|
38
|
-
{
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
},
|
|
46
|
-
{
|
|
47
|
-
type: "element",
|
|
48
|
-
tagName: "path",
|
|
49
|
-
properties: {
|
|
50
|
-
d: "M15 7h2a5 5 0 1 1 0 10h-2",
|
|
51
|
-
},
|
|
52
|
-
children: [],
|
|
53
|
-
},
|
|
54
|
-
{
|
|
55
|
-
type: "element",
|
|
56
|
-
tagName: "line",
|
|
57
|
-
properties: {
|
|
58
|
-
x1: "8",
|
|
59
|
-
x2: "16",
|
|
60
|
-
y1: "12",
|
|
61
|
-
y2: "12",
|
|
62
|
-
},
|
|
63
|
-
children: [],
|
|
64
|
-
},
|
|
60
|
+
createLucideIconNode({
|
|
61
|
+
className: ["heading-anchor-icon", "heading-anchor-link-icon"],
|
|
62
|
+
path: "M9 17H7A5 5 0 0 1 7 7h2m6 0h2a5 5 0 1 1 0 10h-2m-7-5h8",
|
|
63
|
+
}),
|
|
64
|
+
createLucideIconNode({
|
|
65
|
+
className: ["heading-anchor-icon", "heading-anchor-check-icon"],
|
|
66
|
+
path: "M20 6L9 17l-5-5",
|
|
67
|
+
}),
|
|
65
68
|
],
|
|
66
69
|
};
|
|
67
70
|
|
|
@@ -342,12 +345,13 @@ export default defineConfig({
|
|
|
342
345
|
],
|
|
343
346
|
rehypePlugins: [
|
|
344
347
|
rehypeSlug,
|
|
348
|
+
rehypePrefixPreviewHeadingIds,
|
|
345
349
|
rehypeExternalLinks,
|
|
346
350
|
[
|
|
347
351
|
rehypeAutolinkHeadings,
|
|
348
352
|
{
|
|
349
353
|
behavior: "append",
|
|
350
|
-
content:
|
|
354
|
+
content: headingAnchorContent,
|
|
351
355
|
properties: {
|
|
352
356
|
className: ["heading-anchor"],
|
|
353
357
|
ariaLabel: "Copy section link",
|
|
@@ -21,6 +21,7 @@
|
|
|
21
21
|
"@iconify-json/simple-icons": "^1.2.69",
|
|
22
22
|
"@iconify/react": "^6.0.2",
|
|
23
23
|
"@jongwooo/prism-theme-github": "^1.15.1",
|
|
24
|
+
"@paper-design/shaders": "^0.0.76",
|
|
24
25
|
"@preact/preset-vite": "^2.10.3",
|
|
25
26
|
"@readme/oas-to-snippet": "^29.3.0",
|
|
26
27
|
"@resvg/resvg-js": "^2.6.2",
|
|
@@ -2826,6 +2827,12 @@
|
|
|
2826
2827
|
"win32"
|
|
2827
2828
|
]
|
|
2828
2829
|
},
|
|
2830
|
+
"node_modules/@paper-design/shaders": {
|
|
2831
|
+
"version": "0.0.76",
|
|
2832
|
+
"resolved": "https://registry.npmjs.org/@paper-design/shaders/-/shaders-0.0.76.tgz",
|
|
2833
|
+
"integrity": "sha512-AcNDY4J66YQHUfQYFInkCP7M9VOje0od7wLpOR7LtCmc532opJy6ll+h1W9zBovz8tt9U7OADUmJ/qKEXyOX/A==",
|
|
2834
|
+
"license": "SEE LICENSE IN https://github.com/paper-design/shaders/blob/main/LICENSE"
|
|
2835
|
+
},
|
|
2829
2836
|
"node_modules/@preact/preset-vite": {
|
|
2830
2837
|
"version": "2.10.3",
|
|
2831
2838
|
"resolved": "https://registry.npmjs.org/@preact/preset-vite/-/preset-vite-2.10.3.tgz",
|
package/template/package.json
CHANGED
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
"@iconify-json/simple-icons": "^1.2.69",
|
|
26
26
|
"@iconify/react": "^6.0.2",
|
|
27
27
|
"@jongwooo/prism-theme-github": "^1.15.1",
|
|
28
|
+
"@paper-design/shaders": "^0.0.76",
|
|
28
29
|
"@preact/preset-vite": "^2.10.3",
|
|
29
30
|
"@readme/oas-to-snippet": "^29.3.0",
|
|
30
31
|
"@resvg/resvg-js": "^2.6.2",
|
|
@@ -1,10 +1,14 @@
|
|
|
1
1
|
---
|
|
2
2
|
import Icon from "./ui/Icon.astro";
|
|
3
|
-
import { getConfig } from "../lib/validation";
|
|
3
|
+
import { getConfig, type ThemeColorByMode } from "../lib/validation";
|
|
4
4
|
import Search from "./Search.astro";
|
|
5
5
|
import LogoLink from "./LogoLink.astro";
|
|
6
|
-
import sparkleIcon from "../assets/icons/sparkle.svg?url";
|
|
7
6
|
import { withBasePath } from "../lib/base-path";
|
|
7
|
+
import { getAssistantPanelRuntimeConfig } from "../lib/assistant-panel-config";
|
|
8
|
+
import {
|
|
9
|
+
getDocsBaseColorShade,
|
|
10
|
+
getThemeForegroundColor,
|
|
11
|
+
} from "../lib/theme-css";
|
|
8
12
|
|
|
9
13
|
interface Props {
|
|
10
14
|
showAskAiTrigger?: boolean;
|
|
@@ -12,6 +16,98 @@ interface Props {
|
|
|
12
16
|
|
|
13
17
|
const { showAskAiTrigger = false } = Astro.props as Props;
|
|
14
18
|
const config = await getConfig();
|
|
19
|
+
const assistantNavbarButton = config.assistant?.navbarButton;
|
|
20
|
+
const showAssistantNavbarButton =
|
|
21
|
+
showAskAiTrigger && assistantNavbarButton?.enabled !== false;
|
|
22
|
+
const assistantNavbarButtonText = assistantNavbarButton?.text ?? "Ask";
|
|
23
|
+
const assistantRuntimeConfig = showAssistantNavbarButton
|
|
24
|
+
? getAssistantPanelRuntimeConfig(config)
|
|
25
|
+
: null;
|
|
26
|
+
const assistantHeaderIconSrc =
|
|
27
|
+
assistantRuntimeConfig?.launcherIconImageSrc ?? "";
|
|
28
|
+
|
|
29
|
+
function getConfiguredColorForMode(
|
|
30
|
+
value: string | ThemeColorByMode | undefined,
|
|
31
|
+
mode: "light" | "dark",
|
|
32
|
+
): string | undefined {
|
|
33
|
+
if (typeof value === "string") return value;
|
|
34
|
+
return value?.[mode];
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function getDefaultButtonColor(mode: "light" | "dark"): string {
|
|
38
|
+
return getDocsBaseColorShade(
|
|
39
|
+
config.theme,
|
|
40
|
+
mode,
|
|
41
|
+
mode === "light" ? "900" : "100",
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function getButtonForegroundColor(
|
|
46
|
+
mode: "light" | "dark",
|
|
47
|
+
color: string,
|
|
48
|
+
): string {
|
|
49
|
+
return getThemeForegroundColor(
|
|
50
|
+
color,
|
|
51
|
+
getDocsBaseColorShade(config.theme, mode, "900"),
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
function getAssistantNavbarButtonColor(mode: "light" | "dark"): string {
|
|
56
|
+
return (
|
|
57
|
+
getConfiguredColorForMode(config.assistant?.navbarButton?.color, mode) ??
|
|
58
|
+
getConfiguredColorForMode(config.assistant?.button?.color, mode) ??
|
|
59
|
+
getDefaultButtonColor(mode)
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function getNavbarPrimaryThemeColor(mode: "light" | "dark"): string {
|
|
64
|
+
return (
|
|
65
|
+
getConfiguredColorForMode(config.navbar?.primary?.color, mode) ??
|
|
66
|
+
getDefaultButtonColor(mode)
|
|
67
|
+
);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const assistantHeaderThemeColors = showAssistantNavbarButton
|
|
71
|
+
? {
|
|
72
|
+
light: getAssistantNavbarButtonColor("light"),
|
|
73
|
+
dark: getAssistantNavbarButtonColor("dark"),
|
|
74
|
+
}
|
|
75
|
+
: null;
|
|
76
|
+
const assistantHeaderButtonStyle = assistantHeaderThemeColors
|
|
77
|
+
? [
|
|
78
|
+
`--assistant-header-theme-light: ${assistantHeaderThemeColors.light}`,
|
|
79
|
+
`--assistant-header-theme-dark: ${assistantHeaderThemeColors.dark}`,
|
|
80
|
+
`--assistant-header-foreground-light: ${
|
|
81
|
+
config.assistant?.icon?.color ??
|
|
82
|
+
getButtonForegroundColor("light", assistantHeaderThemeColors.light)
|
|
83
|
+
}`,
|
|
84
|
+
`--assistant-header-foreground-dark: ${
|
|
85
|
+
config.assistant?.icon?.color ??
|
|
86
|
+
getButtonForegroundColor("dark", assistantHeaderThemeColors.dark)
|
|
87
|
+
}`,
|
|
88
|
+
].join("; ")
|
|
89
|
+
: "";
|
|
90
|
+
|
|
91
|
+
const navbarPrimaryThemeColors = config.navbar?.primary
|
|
92
|
+
? {
|
|
93
|
+
light: getNavbarPrimaryThemeColor("light"),
|
|
94
|
+
dark: getNavbarPrimaryThemeColor("dark"),
|
|
95
|
+
}
|
|
96
|
+
: null;
|
|
97
|
+
const navbarPrimaryButtonStyle = navbarPrimaryThemeColors
|
|
98
|
+
? [
|
|
99
|
+
`--navbar-primary-theme-light: ${navbarPrimaryThemeColors.light}`,
|
|
100
|
+
`--navbar-primary-theme-dark: ${navbarPrimaryThemeColors.dark}`,
|
|
101
|
+
`--navbar-primary-foreground-light: ${getButtonForegroundColor(
|
|
102
|
+
"light",
|
|
103
|
+
navbarPrimaryThemeColors.light,
|
|
104
|
+
)}`,
|
|
105
|
+
`--navbar-primary-foreground-dark: ${getButtonForegroundColor(
|
|
106
|
+
"dark",
|
|
107
|
+
navbarPrimaryThemeColors.dark,
|
|
108
|
+
)}`,
|
|
109
|
+
].join("; ")
|
|
110
|
+
: "";
|
|
15
111
|
---
|
|
16
112
|
|
|
17
113
|
<header
|
|
@@ -56,20 +152,23 @@ const config = await getConfig();
|
|
|
56
152
|
<div class="flex items-center gap-2 mx-0 md:mx-auto lg:mx-0">
|
|
57
153
|
<Search />
|
|
58
154
|
{
|
|
59
|
-
|
|
155
|
+
showAssistantNavbarButton ? (
|
|
60
156
|
<button
|
|
61
157
|
type="button"
|
|
62
|
-
|
|
63
|
-
|
|
158
|
+
aria-label="Open assistant"
|
|
159
|
+
class="assistant-header-trigger hidden md:inline-flex items-center gap-2 h-8 rounded-lg [corner-shape:superellipse(1.2)] px-3 text-[13px] font-[350] dark:font-[450] transition-opacity duration-200 hover:opacity-95 cursor-pointer"
|
|
160
|
+
style={assistantHeaderButtonStyle}
|
|
161
|
+
onclick="window.__assistantToggleRequested = true; window.dispatchEvent(new CustomEvent('ask-ai:toggle'));"
|
|
64
162
|
>
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
163
|
+
{assistantHeaderIconSrc ? (
|
|
164
|
+
<img
|
|
165
|
+
src={assistantHeaderIconSrc}
|
|
166
|
+
alt=""
|
|
167
|
+
aria-hidden="true"
|
|
168
|
+
class="size-4 -ml-px object-contain"
|
|
169
|
+
/>
|
|
170
|
+
) : null}
|
|
171
|
+
{assistantNavbarButtonText}
|
|
73
172
|
</button>
|
|
74
173
|
) : null
|
|
75
174
|
}
|
|
@@ -90,7 +189,7 @@ const config = await getConfig();
|
|
|
90
189
|
<a
|
|
91
190
|
class:list={[
|
|
92
191
|
"items-center gap-1 text-[13px] font-[450] dark:font-normal text-neutral-600/85 hover:text-neutral-600 dark:text-neutral-200/90 dark:hover:text-neutral-200 duration-200 px-1.5 py-[5px] whitespace-nowrap",
|
|
93
|
-
|
|
192
|
+
showAssistantNavbarButton && a.length === 3 && i === 2
|
|
94
193
|
? "hidden 2xl:flex"
|
|
95
194
|
: "flex",
|
|
96
195
|
]}
|
|
@@ -112,7 +211,7 @@ const config = await getConfig();
|
|
|
112
211
|
{config.navbar.secondary && (
|
|
113
212
|
<a
|
|
114
213
|
class:list={[
|
|
115
|
-
"h-[33px] items-center gap-1.5 px-[11px] text-[13px] bg-
|
|
214
|
+
"h-[33px] items-center gap-1.5 px-[11px] text-[13px] bg-linear-to-br from-neutral-100/70 to-neutral-100/90 dark:bg-none dark:bg-neutral-800/80 text-neutral-900/85 hover:text-neutral-600 dark:text-neutral-200/95 dark:hover:text-neutral-200 rounded-lg [corner-shape:superellipse(1.2)] border-[0.5px] border-neutral-900/10 dark:border-white/5 transition-all whitespace-nowrap",
|
|
116
215
|
config.navbar.primary ? "hidden lg:flex" : "flex",
|
|
117
216
|
]}
|
|
118
217
|
href={withBasePath(config.navbar.secondary.href)}
|
|
@@ -131,8 +230,9 @@ const config = await getConfig();
|
|
|
131
230
|
{config.navbar.primary && (
|
|
132
231
|
<a
|
|
133
232
|
class:list={[
|
|
134
|
-
"font-[350] h-8 flex items-center gap-2 px-3 text-[13px] rounded-lg [corner-shape:superellipse(1.2)]
|
|
233
|
+
"navbar-primary-trigger font-[350] dark:font-[450] h-8 flex items-center gap-2 px-3 text-[13px] rounded-lg [corner-shape:superellipse(1.2)] duration-200 transition-all whitespace-nowrap hover:opacity-95",
|
|
135
234
|
]}
|
|
235
|
+
style={navbarPrimaryButtonStyle}
|
|
136
236
|
href={withBasePath(config.navbar.primary.href)}
|
|
137
237
|
>
|
|
138
238
|
{config.navbar.primary.icon && (
|
|
@@ -153,3 +253,37 @@ const config = await getConfig();
|
|
|
153
253
|
</div>
|
|
154
254
|
</div>
|
|
155
255
|
</header>
|
|
256
|
+
|
|
257
|
+
<style>
|
|
258
|
+
.assistant-header-trigger {
|
|
259
|
+
--assistant-header-theme: var(--assistant-header-theme-light);
|
|
260
|
+
--assistant-header-foreground: var(--assistant-header-foreground-light);
|
|
261
|
+
background: linear-gradient(
|
|
262
|
+
to bottom,
|
|
263
|
+
color-mix(in oklab, var(--assistant-header-theme) 88%, white),
|
|
264
|
+
color-mix(in oklab, var(--assistant-header-theme) 90%, black)
|
|
265
|
+
);
|
|
266
|
+
color: var(--assistant-header-foreground);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
:global(.dark) .assistant-header-trigger {
|
|
270
|
+
--assistant-header-theme: var(--assistant-header-theme-dark);
|
|
271
|
+
--assistant-header-foreground: var(--assistant-header-foreground-dark);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.navbar-primary-trigger {
|
|
275
|
+
--navbar-primary-theme: var(--navbar-primary-theme-light);
|
|
276
|
+
--navbar-primary-foreground: var(--navbar-primary-foreground-light);
|
|
277
|
+
background: linear-gradient(
|
|
278
|
+
to bottom,
|
|
279
|
+
color-mix(in oklab, var(--navbar-primary-theme) 88%, white),
|
|
280
|
+
color-mix(in oklab, var(--navbar-primary-theme) 90%, black)
|
|
281
|
+
);
|
|
282
|
+
color: var(--navbar-primary-foreground);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
:global(.dark) .navbar-primary-trigger {
|
|
286
|
+
--navbar-primary-theme: var(--navbar-primary-theme-dark);
|
|
287
|
+
--navbar-primary-foreground: var(--navbar-primary-foreground-dark);
|
|
288
|
+
}
|
|
289
|
+
</style>
|
|
@@ -8,6 +8,9 @@ import Steps from "./user/Steps.astro";
|
|
|
8
8
|
import Step from "./user/Step.astro";
|
|
9
9
|
import Accordion from "./user/Accordion.astro";
|
|
10
10
|
import AccordionGroup from "./user/AccordionGroup.astro";
|
|
11
|
+
import Card from "./user/Card.astro";
|
|
12
|
+
import Column from "./user/Column.astro";
|
|
13
|
+
import Columns from "./user/Columns.astro";
|
|
11
14
|
import TableOfContents from "./TableOfContents.astro";
|
|
12
15
|
import Image from "./user/Image.astro";
|
|
13
16
|
import CodeBlock from "./user/CodeBlock.astro";
|
|
@@ -16,6 +19,7 @@ import ComponentPreview from "./user/ComponentPreview.astro";
|
|
|
16
19
|
import ComponentPreviewBlock from "./user/ComponentPreviewBlock.astro";
|
|
17
20
|
import type { MdxRoute, Route } from "../lib/routes";
|
|
18
21
|
import PagePagination from "./PagePagination.astro";
|
|
22
|
+
import { PREVIEW_HEADING_ID_PREFIX } from "../lib/mdx/rehype-prefix-preview-heading-ids";
|
|
19
23
|
|
|
20
24
|
interface Props {
|
|
21
25
|
entry: any;
|
|
@@ -30,6 +34,9 @@ const { entry, route, previousRoute, nextRoute, homePath } = Astro.props;
|
|
|
30
34
|
const components = {
|
|
31
35
|
Accordion,
|
|
32
36
|
AccordionGroup,
|
|
37
|
+
Card,
|
|
38
|
+
Column,
|
|
39
|
+
Columns,
|
|
33
40
|
Callout,
|
|
34
41
|
Tabs,
|
|
35
42
|
Tab,
|
|
@@ -50,30 +57,32 @@ const description =
|
|
|
50
57
|
? entry.data.description.trim()
|
|
51
58
|
: undefined;
|
|
52
59
|
|
|
53
|
-
const tocHeadings = headings.filter(
|
|
60
|
+
const tocHeadings = headings.filter(
|
|
61
|
+
({ depth, slug }) =>
|
|
62
|
+
(depth === 2 || depth === 3) &&
|
|
63
|
+
!slug.startsWith(PREVIEW_HEADING_ID_PREFIX),
|
|
64
|
+
);
|
|
54
65
|
---
|
|
55
66
|
|
|
56
67
|
<Layout pageTitle={title} pageDescription={description}>
|
|
57
|
-
<
|
|
58
|
-
<
|
|
59
|
-
<
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
<
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
/>
|
|
71
|
-
</div>
|
|
72
|
-
<aside class="hidden xl:block w-56 shrink-0">
|
|
73
|
-
<TableOfContents headings={tocHeadings} />
|
|
74
|
-
</aside>
|
|
68
|
+
<div class="flex w-full min-w-0 justify-between gap-x-10">
|
|
69
|
+
<div class="mx-auto max-w-3xl w-full">
|
|
70
|
+
<header class="mb-6">
|
|
71
|
+
<h1 class="text-4xl font-semibold tracking-tight">{title}</h1>
|
|
72
|
+
</header>
|
|
73
|
+
<article class="prose-rules">
|
|
74
|
+
<Content components={components} />
|
|
75
|
+
</article>
|
|
76
|
+
<PagePagination
|
|
77
|
+
previousRoute={previousRoute}
|
|
78
|
+
nextRoute={nextRoute}
|
|
79
|
+
homePath={homePath}
|
|
80
|
+
/>
|
|
75
81
|
</div>
|
|
76
|
-
|
|
82
|
+
<aside class="hidden xl:block w-48 shrink-0">
|
|
83
|
+
<TableOfContents headings={tocHeadings} />
|
|
84
|
+
</aside>
|
|
85
|
+
</div>
|
|
77
86
|
<script is:inline>
|
|
78
87
|
function fallbackCopy(text) {
|
|
79
88
|
const textarea = document.createElement("textarea");
|
|
@@ -101,7 +110,52 @@ const tocHeadings = headings.filter(({ depth }) => depth === 2 || depth === 3);
|
|
|
101
110
|
return fallbackCopy(text);
|
|
102
111
|
}
|
|
103
112
|
|
|
104
|
-
|
|
113
|
+
var headingCopyStateTimers =
|
|
114
|
+
window.__headingCopyStateTimers instanceof WeakMap
|
|
115
|
+
? window.__headingCopyStateTimers
|
|
116
|
+
: new WeakMap();
|
|
117
|
+
window.__headingCopyStateTimers = headingCopyStateTimers;
|
|
118
|
+
|
|
119
|
+
function showHeadingCopiedState(anchor) {
|
|
120
|
+
if (!(anchor instanceof HTMLElement)) return;
|
|
121
|
+
|
|
122
|
+
const defaultLabel =
|
|
123
|
+
anchor.dataset.defaultAriaLabel ||
|
|
124
|
+
anchor.getAttribute("aria-label") ||
|
|
125
|
+
"Copy section link";
|
|
126
|
+
const defaultTitle =
|
|
127
|
+
anchor.dataset.defaultTitle ||
|
|
128
|
+
anchor.getAttribute("title") ||
|
|
129
|
+
defaultLabel;
|
|
130
|
+
anchor.dataset.defaultAriaLabel = defaultLabel;
|
|
131
|
+
anchor.dataset.defaultTitle = defaultTitle;
|
|
132
|
+
|
|
133
|
+
const existingTimeout = headingCopyStateTimers.get(anchor);
|
|
134
|
+
if (existingTimeout) window.clearTimeout(existingTimeout);
|
|
135
|
+
|
|
136
|
+
anchor.dataset.copyState = "copied";
|
|
137
|
+
anchor.setAttribute("aria-label", "Copied section link");
|
|
138
|
+
anchor.setAttribute("title", "Copied");
|
|
139
|
+
|
|
140
|
+
const timeout = window.setTimeout(() => {
|
|
141
|
+
if (anchor.dataset.copyState === "copied") {
|
|
142
|
+
delete anchor.dataset.copyState;
|
|
143
|
+
anchor.setAttribute(
|
|
144
|
+
"aria-label",
|
|
145
|
+
anchor.dataset.defaultAriaLabel || "Copy section link",
|
|
146
|
+
);
|
|
147
|
+
anchor.setAttribute(
|
|
148
|
+
"title",
|
|
149
|
+
anchor.dataset.defaultTitle || "Copy section link",
|
|
150
|
+
);
|
|
151
|
+
}
|
|
152
|
+
headingCopyStateTimers.delete(anchor);
|
|
153
|
+
}, 1400);
|
|
154
|
+
|
|
155
|
+
headingCopyStateTimers.set(anchor, timeout);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function copyHeadingUrlFromHash(hash, stateTarget) {
|
|
105
159
|
const url = `${window.location.origin}${window.location.pathname}${hash}`;
|
|
106
160
|
const copied = await copyToClipboard(url);
|
|
107
161
|
if (!copied) return false;
|
|
@@ -112,6 +166,8 @@ const tocHeadings = headings.filter(({ depth }) => depth === 2 || depth === 3);
|
|
|
112
166
|
window.location.hash = hash;
|
|
113
167
|
}
|
|
114
168
|
|
|
169
|
+
showHeadingCopiedState(stateTarget);
|
|
170
|
+
|
|
115
171
|
return true;
|
|
116
172
|
}
|
|
117
173
|
|
|
@@ -134,7 +190,7 @@ const tocHeadings = headings.filter(({ depth }) => depth === 2 || depth === 3);
|
|
|
134
190
|
return;
|
|
135
191
|
}
|
|
136
192
|
|
|
137
|
-
const copied = await copyHeadingUrlFromHash(hash);
|
|
193
|
+
const copied = await copyHeadingUrlFromHash(hash, anchor);
|
|
138
194
|
if (!copied) {
|
|
139
195
|
if (isPointerClick) anchor.blur();
|
|
140
196
|
return;
|
|
@@ -160,7 +216,10 @@ const tocHeadings = headings.filter(({ depth }) => depth === 2 || depth === 3);
|
|
|
160
216
|
const id = heading.getAttribute("id");
|
|
161
217
|
if (!id) return;
|
|
162
218
|
|
|
163
|
-
await copyHeadingUrlFromHash(
|
|
219
|
+
await copyHeadingUrlFromHash(
|
|
220
|
+
`#${id}`,
|
|
221
|
+
heading.querySelector("a.heading-anchor"),
|
|
222
|
+
);
|
|
164
223
|
});
|
|
165
224
|
});
|
|
166
225
|
}
|
|
@@ -23,17 +23,35 @@ function buildHref(route: Route): string {
|
|
|
23
23
|
|
|
24
24
|
{
|
|
25
25
|
(previousRoute || nextRoute) && (
|
|
26
|
-
<nav class="w-full
|
|
27
|
-
<div class="
|
|
26
|
+
<nav class="w-full pt-16" aria-label="Page pagination">
|
|
27
|
+
<div class="flex gap-4 xs:grid-cols-2">
|
|
28
28
|
{previousRoute && (
|
|
29
29
|
<a
|
|
30
30
|
href={buildHref(previousRoute)}
|
|
31
|
-
class="group flex flex-col
|
|
31
|
+
class="group flex-1 flex min-w-0 flex-col py-4 px-5 relative z-10 before:-z-10 before:absolute before:inset-0 hover:before:-inset-1 before:rounded-xl before:bg-neutral-50 hover:before:bg-neutral-100/80 dark:before:bg-(--rd-code-surface)/70 dark:hover:before:bg-(--rd-code-surface) before:duration-200 before:ease-in-out"
|
|
32
32
|
>
|
|
33
|
-
<span class="
|
|
33
|
+
<span class="inline-flex items-center text-sm tracking-wide text-neutral-500/90 dark:text-neutral-300/70">
|
|
34
|
+
<svg
|
|
35
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
36
|
+
width="24"
|
|
37
|
+
height="24"
|
|
38
|
+
viewBox="0 0 24 24"
|
|
39
|
+
fill="none"
|
|
40
|
+
stroke="currentColor"
|
|
41
|
+
stroke-width="2.2"
|
|
42
|
+
stroke-linecap="round"
|
|
43
|
+
stroke-linejoin="round"
|
|
44
|
+
class="size-3.5 group-hover:-translate-x-[5px] duration-200 ease-in-out"
|
|
45
|
+
>
|
|
46
|
+
<path d="m12 19-7-7 7-7" />
|
|
47
|
+
<path
|
|
48
|
+
d="M19 12H5"
|
|
49
|
+
class="scale-x-0 group-hover:scale-x-100 origin-[30%] duration-200 ease-in-out"
|
|
50
|
+
/>
|
|
51
|
+
</svg>
|
|
34
52
|
Previous
|
|
35
53
|
</span>
|
|
36
|
-
<div class="font-medium text-neutral-900 dark:text-neutral-100">
|
|
54
|
+
<div class="min-w-0 max-w-full wrap-break-word font-medium text-neutral-900 dark:text-neutral-100">
|
|
37
55
|
{previousRoute.title}
|
|
38
56
|
</div>
|
|
39
57
|
</a>
|
|
@@ -43,14 +61,32 @@ function buildHref(route: Route): string {
|
|
|
43
61
|
<a
|
|
44
62
|
href={buildHref(nextRoute)}
|
|
45
63
|
class:list={[
|
|
46
|
-
"group flex flex-col
|
|
64
|
+
"group flex-1 flex min-w-0 flex-col py-4 px-5 relative z-10 before:-z-10 before:absolute before:inset-0 hover:before:-inset-1 before:rounded-xl before:bg-neutral-50 hover:before:bg-neutral-100/80 dark:before:bg-(--rd-code-surface)/70 dark:hover:before:bg-(--rd-code-surface) before:duration-200 before:ease-in-out",
|
|
47
65
|
!previousRoute && "sm:col-start-2",
|
|
48
66
|
]}
|
|
49
67
|
>
|
|
50
|
-
<span class="
|
|
68
|
+
<span class="ml-auto inline-flex items-center text-sm tracking-wide text-neutral-500/90 dark:text-neutral-300/70">
|
|
51
69
|
Next
|
|
70
|
+
<svg
|
|
71
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
72
|
+
width="24"
|
|
73
|
+
height="24"
|
|
74
|
+
viewBox="0 0 24 24"
|
|
75
|
+
fill="none"
|
|
76
|
+
stroke="currentColor"
|
|
77
|
+
stroke-width="2.2"
|
|
78
|
+
stroke-linecap="round"
|
|
79
|
+
stroke-linejoin="round"
|
|
80
|
+
class="size-3.5 group-hover:translate-x-[5px] duration-200 ease-in-out"
|
|
81
|
+
>
|
|
82
|
+
<path
|
|
83
|
+
d="M5 12h14"
|
|
84
|
+
class="scale-x-0 group-hover:scale-x-100 origin-[70%] duration-200 ease-in-out"
|
|
85
|
+
/>
|
|
86
|
+
<path d="m12 5 7 7-7 7" />
|
|
87
|
+
</svg>
|
|
52
88
|
</span>
|
|
53
|
-
<div class="text-right font-medium text-neutral-900 dark:text-neutral-100">
|
|
89
|
+
<div class="min-w-0 max-w-full wrap-break-word text-right font-medium text-neutral-900 dark:text-neutral-100">
|
|
54
90
|
{nextRoute.title}
|
|
55
91
|
</div>
|
|
56
92
|
</a>
|
|
@@ -3,6 +3,12 @@ import { getConfig, type DocsConfig } from "../lib/validation";
|
|
|
3
3
|
import ThemeSwitcher from "./ThemeSwitcher.astro";
|
|
4
4
|
import SidebarMenu from "./SidebarMenu.astro";
|
|
5
5
|
|
|
6
|
+
interface Props {
|
|
7
|
+
askAiEnabled?: boolean;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
const { askAiEnabled = false } = Astro.props as Props;
|
|
11
|
+
|
|
6
12
|
const config: DocsConfig = await getConfig();
|
|
7
13
|
---
|
|
8
14
|
|
|
@@ -13,7 +19,10 @@ const config: DocsConfig = await getConfig();
|
|
|
13
19
|
<SidebarMenu navigation={config.navigation} />
|
|
14
20
|
</nav>
|
|
15
21
|
<div
|
|
16
|
-
class=
|
|
22
|
+
class:list={[
|
|
23
|
+
"mt-auto bg-background z-10 p-3 border-t border-t-border-light flex gap-1.5 items-center",
|
|
24
|
+
askAiEnabled ? "justify-start" : "justify-end",
|
|
25
|
+
]}
|
|
17
26
|
>
|
|
18
27
|
<span class="text-neutral-400 text-xs font-light">Theme</span>
|
|
19
28
|
<ThemeSwitcher />
|