radiant-docs 0.1.41 → 0.1.42
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 +42 -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 +76 -22
- 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 +1 -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/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
|
@@ -18,50 +18,52 @@ import remarkGfm from "remark-gfm";
|
|
|
18
18
|
import rehypeSlug from "rehype-slug";
|
|
19
19
|
import rehypeAutolinkHeadings from "rehype-autolink-headings";
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
function createLucideIconNode({ className, path }) {
|
|
22
|
+
return {
|
|
23
|
+
type: "element",
|
|
24
|
+
tagName: "svg",
|
|
25
|
+
properties: {
|
|
26
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
27
|
+
width: "24",
|
|
28
|
+
height: "24",
|
|
29
|
+
viewBox: "0 0 24 24",
|
|
30
|
+
fill: "none",
|
|
31
|
+
stroke: "currentColor",
|
|
32
|
+
strokeWidth: "2",
|
|
33
|
+
strokeLinecap: "round",
|
|
34
|
+
strokeLinejoin: "round",
|
|
35
|
+
className,
|
|
36
|
+
ariaHidden: "true",
|
|
37
|
+
},
|
|
38
|
+
children: [
|
|
39
|
+
{
|
|
40
|
+
type: "element",
|
|
41
|
+
tagName: "path",
|
|
42
|
+
properties: {
|
|
43
|
+
d: path,
|
|
44
|
+
},
|
|
45
|
+
children: [],
|
|
46
|
+
},
|
|
47
|
+
],
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const headingAnchorContent = {
|
|
22
52
|
type: "element",
|
|
23
|
-
tagName: "
|
|
53
|
+
tagName: "span",
|
|
24
54
|
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"],
|
|
55
|
+
className: ["heading-anchor-icons"],
|
|
35
56
|
ariaHidden: "true",
|
|
36
57
|
},
|
|
37
58
|
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
|
-
},
|
|
59
|
+
createLucideIconNode({
|
|
60
|
+
className: ["heading-anchor-icon", "heading-anchor-link-icon"],
|
|
61
|
+
path: "M9 17H7A5 5 0 0 1 7 7h2m6 0h2a5 5 0 1 1 0 10h-2m-7-5h8",
|
|
62
|
+
}),
|
|
63
|
+
createLucideIconNode({
|
|
64
|
+
className: ["heading-anchor-icon", "heading-anchor-check-icon"],
|
|
65
|
+
path: "M20 6L9 17l-5-5",
|
|
66
|
+
}),
|
|
65
67
|
],
|
|
66
68
|
};
|
|
67
69
|
|
|
@@ -347,7 +349,7 @@ export default defineConfig({
|
|
|
347
349
|
rehypeAutolinkHeadings,
|
|
348
350
|
{
|
|
349
351
|
behavior: "append",
|
|
350
|
-
content:
|
|
352
|
+
content: headingAnchorContent,
|
|
351
353
|
properties: {
|
|
352
354
|
className: ["heading-anchor"],
|
|
353
355
|
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";
|
|
@@ -30,6 +33,9 @@ const { entry, route, previousRoute, nextRoute, homePath } = Astro.props;
|
|
|
30
33
|
const components = {
|
|
31
34
|
Accordion,
|
|
32
35
|
AccordionGroup,
|
|
36
|
+
Card,
|
|
37
|
+
Column,
|
|
38
|
+
Columns,
|
|
33
39
|
Callout,
|
|
34
40
|
Tabs,
|
|
35
41
|
Tab,
|
|
@@ -54,26 +60,24 @@ const tocHeadings = headings.filter(({ depth }) => depth === 2 || depth === 3);
|
|
|
54
60
|
---
|
|
55
61
|
|
|
56
62
|
<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>
|
|
63
|
+
<div class="flex w-full min-w-0 justify-between gap-x-10">
|
|
64
|
+
<div class="mx-auto max-w-3xl w-full">
|
|
65
|
+
<header class="mb-6">
|
|
66
|
+
<h1 class="text-4xl font-semibold tracking-tight">{title}</h1>
|
|
67
|
+
</header>
|
|
68
|
+
<article class="prose-rules">
|
|
69
|
+
<Content components={components} />
|
|
70
|
+
</article>
|
|
71
|
+
<PagePagination
|
|
72
|
+
previousRoute={previousRoute}
|
|
73
|
+
nextRoute={nextRoute}
|
|
74
|
+
homePath={homePath}
|
|
75
|
+
/>
|
|
75
76
|
</div>
|
|
76
|
-
|
|
77
|
+
<aside class="hidden xl:block w-48 shrink-0">
|
|
78
|
+
<TableOfContents headings={tocHeadings} />
|
|
79
|
+
</aside>
|
|
80
|
+
</div>
|
|
77
81
|
<script is:inline>
|
|
78
82
|
function fallbackCopy(text) {
|
|
79
83
|
const textarea = document.createElement("textarea");
|
|
@@ -101,7 +105,52 @@ const tocHeadings = headings.filter(({ depth }) => depth === 2 || depth === 3);
|
|
|
101
105
|
return fallbackCopy(text);
|
|
102
106
|
}
|
|
103
107
|
|
|
104
|
-
|
|
108
|
+
var headingCopyStateTimers =
|
|
109
|
+
window.__headingCopyStateTimers instanceof WeakMap
|
|
110
|
+
? window.__headingCopyStateTimers
|
|
111
|
+
: new WeakMap();
|
|
112
|
+
window.__headingCopyStateTimers = headingCopyStateTimers;
|
|
113
|
+
|
|
114
|
+
function showHeadingCopiedState(anchor) {
|
|
115
|
+
if (!(anchor instanceof HTMLElement)) return;
|
|
116
|
+
|
|
117
|
+
const defaultLabel =
|
|
118
|
+
anchor.dataset.defaultAriaLabel ||
|
|
119
|
+
anchor.getAttribute("aria-label") ||
|
|
120
|
+
"Copy section link";
|
|
121
|
+
const defaultTitle =
|
|
122
|
+
anchor.dataset.defaultTitle ||
|
|
123
|
+
anchor.getAttribute("title") ||
|
|
124
|
+
defaultLabel;
|
|
125
|
+
anchor.dataset.defaultAriaLabel = defaultLabel;
|
|
126
|
+
anchor.dataset.defaultTitle = defaultTitle;
|
|
127
|
+
|
|
128
|
+
const existingTimeout = headingCopyStateTimers.get(anchor);
|
|
129
|
+
if (existingTimeout) window.clearTimeout(existingTimeout);
|
|
130
|
+
|
|
131
|
+
anchor.dataset.copyState = "copied";
|
|
132
|
+
anchor.setAttribute("aria-label", "Copied section link");
|
|
133
|
+
anchor.setAttribute("title", "Copied");
|
|
134
|
+
|
|
135
|
+
const timeout = window.setTimeout(() => {
|
|
136
|
+
if (anchor.dataset.copyState === "copied") {
|
|
137
|
+
delete anchor.dataset.copyState;
|
|
138
|
+
anchor.setAttribute(
|
|
139
|
+
"aria-label",
|
|
140
|
+
anchor.dataset.defaultAriaLabel || "Copy section link",
|
|
141
|
+
);
|
|
142
|
+
anchor.setAttribute(
|
|
143
|
+
"title",
|
|
144
|
+
anchor.dataset.defaultTitle || "Copy section link",
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
headingCopyStateTimers.delete(anchor);
|
|
148
|
+
}, 1400);
|
|
149
|
+
|
|
150
|
+
headingCopyStateTimers.set(anchor, timeout);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async function copyHeadingUrlFromHash(hash, stateTarget) {
|
|
105
154
|
const url = `${window.location.origin}${window.location.pathname}${hash}`;
|
|
106
155
|
const copied = await copyToClipboard(url);
|
|
107
156
|
if (!copied) return false;
|
|
@@ -112,6 +161,8 @@ const tocHeadings = headings.filter(({ depth }) => depth === 2 || depth === 3);
|
|
|
112
161
|
window.location.hash = hash;
|
|
113
162
|
}
|
|
114
163
|
|
|
164
|
+
showHeadingCopiedState(stateTarget);
|
|
165
|
+
|
|
115
166
|
return true;
|
|
116
167
|
}
|
|
117
168
|
|
|
@@ -134,7 +185,7 @@ const tocHeadings = headings.filter(({ depth }) => depth === 2 || depth === 3);
|
|
|
134
185
|
return;
|
|
135
186
|
}
|
|
136
187
|
|
|
137
|
-
const copied = await copyHeadingUrlFromHash(hash);
|
|
188
|
+
const copied = await copyHeadingUrlFromHash(hash, anchor);
|
|
138
189
|
if (!copied) {
|
|
139
190
|
if (isPointerClick) anchor.blur();
|
|
140
191
|
return;
|
|
@@ -160,7 +211,10 @@ const tocHeadings = headings.filter(({ depth }) => depth === 2 || depth === 3);
|
|
|
160
211
|
const id = heading.getAttribute("id");
|
|
161
212
|
if (!id) return;
|
|
162
213
|
|
|
163
|
-
await copyHeadingUrlFromHash(
|
|
214
|
+
await copyHeadingUrlFromHash(
|
|
215
|
+
`#${id}`,
|
|
216
|
+
heading.querySelector("a.heading-anchor"),
|
|
217
|
+
);
|
|
164
218
|
});
|
|
165
219
|
});
|
|
166
220
|
}
|
|
@@ -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 />
|