radiant-docs 0.1.68 → 0.1.70
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/package-lock.json +5 -0
- package/template/package.json +1 -0
- package/template/src/components/Header.astro +346 -71
- package/template/src/components/MdxPage.astro +59 -6
- package/template/src/components/MobileNavbarActions.astro +410 -0
- package/template/src/components/OpenApiPage.astro +1 -1
- package/template/src/components/PageAiActions.astro +49 -43
- package/template/src/components/PagePagination.astro +1 -1
- package/template/src/components/Search.astro +24 -3
- package/template/src/components/Sidebar.astro +36 -1
- package/template/src/components/SidebarTabs.astro +3 -1
- package/template/src/components/TableOfContents.astro +14 -1
- package/template/src/components/chat/AssistantDocsWidget.tsx +1 -1
- package/template/src/components/chat/AssistantEmbedPanel.tsx +160 -50
- package/template/src/components/endpoint/PlaygroundForm.astro +1 -1
- package/template/src/components/user/CodeBlock.astro +102 -72
- package/template/src/components/user/ComponentPreviewBlock.astro +73 -32
- package/template/src/layouts/Layout.astro +45 -3
- package/template/src/lib/assistant-embed-script.ts +33 -5
- package/template/src/lib/assistant-panel-config.ts +1 -1
- package/template/src/lib/client-shiki-config.ts +7 -12
- package/template/src/generated/shiki-platform-assets.json +0 -24
- package/template/src/lib/assistant-shiki-client.ts +0 -522
|
@@ -0,0 +1,410 @@
|
|
|
1
|
+
---
|
|
2
|
+
import Icon from "./ui/Icon.astro";
|
|
3
|
+
import { getConfig, type ThemeColorByMode } from "../lib/validation";
|
|
4
|
+
import { resolveConfiguredHref } from "../lib/routes";
|
|
5
|
+
import {
|
|
6
|
+
getDocsBaseColorShade,
|
|
7
|
+
getThemeForegroundColor,
|
|
8
|
+
} from "../lib/theme-css";
|
|
9
|
+
|
|
10
|
+
const config = await getConfig();
|
|
11
|
+
|
|
12
|
+
function getConfiguredColorForMode(
|
|
13
|
+
value: string | ThemeColorByMode | undefined,
|
|
14
|
+
mode: "light" | "dark",
|
|
15
|
+
): string | undefined {
|
|
16
|
+
if (typeof value === "string") return value;
|
|
17
|
+
return value?.[mode];
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function getDefaultButtonColor(mode: "light" | "dark"): string {
|
|
21
|
+
return getDocsBaseColorShade(
|
|
22
|
+
config.theme,
|
|
23
|
+
mode,
|
|
24
|
+
mode === "light" ? "900" : "100",
|
|
25
|
+
);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getButtonForegroundColor(
|
|
29
|
+
mode: "light" | "dark",
|
|
30
|
+
color: string,
|
|
31
|
+
): string {
|
|
32
|
+
return getThemeForegroundColor(
|
|
33
|
+
color,
|
|
34
|
+
getDocsBaseColorShade(config.theme, mode, "900"),
|
|
35
|
+
);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function getNavbarPrimaryThemeColor(mode: "light" | "dark"): string {
|
|
39
|
+
return (
|
|
40
|
+
getConfiguredColorForMode(config.navbar?.primary?.color, mode) ??
|
|
41
|
+
getDefaultButtonColor(mode)
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const navbarPrimaryThemeColors = config.navbar?.primary
|
|
46
|
+
? {
|
|
47
|
+
light: getNavbarPrimaryThemeColor("light"),
|
|
48
|
+
dark: getNavbarPrimaryThemeColor("dark"),
|
|
49
|
+
}
|
|
50
|
+
: null;
|
|
51
|
+
const navbarPrimaryButtonStyle = navbarPrimaryThemeColors
|
|
52
|
+
? [
|
|
53
|
+
`--mobile-navbar-primary-theme-light: ${navbarPrimaryThemeColors.light}`,
|
|
54
|
+
`--mobile-navbar-primary-theme-dark: ${navbarPrimaryThemeColors.dark}`,
|
|
55
|
+
`--mobile-navbar-primary-foreground-light: ${getButtonForegroundColor(
|
|
56
|
+
"light",
|
|
57
|
+
navbarPrimaryThemeColors.light,
|
|
58
|
+
)}`,
|
|
59
|
+
`--mobile-navbar-primary-foreground-dark: ${getButtonForegroundColor(
|
|
60
|
+
"dark",
|
|
61
|
+
navbarPrimaryThemeColors.dark,
|
|
62
|
+
)}`,
|
|
63
|
+
].join("; ")
|
|
64
|
+
: "";
|
|
65
|
+
|
|
66
|
+
const navbarLinks = config.navbar?.links
|
|
67
|
+
? await Promise.all(
|
|
68
|
+
config.navbar.links.map(async (link) => ({
|
|
69
|
+
...link,
|
|
70
|
+
href: await resolveConfiguredHref(link.href, config),
|
|
71
|
+
})),
|
|
72
|
+
)
|
|
73
|
+
: [];
|
|
74
|
+
const navbarSecondaryHref = config.navbar?.secondary
|
|
75
|
+
? await resolveConfiguredHref(config.navbar.secondary.href, config)
|
|
76
|
+
: "";
|
|
77
|
+
const navbarPrimaryHref = config.navbar?.primary
|
|
78
|
+
? await resolveConfiguredHref(config.navbar.primary.href, config)
|
|
79
|
+
: "";
|
|
80
|
+
const mobileButtonCount =
|
|
81
|
+
Number(Boolean(config.navbar?.primary)) +
|
|
82
|
+
Number(Boolean(config.navbar?.secondary));
|
|
83
|
+
const hasMobileNavbarActions = mobileButtonCount > 0 || navbarLinks.length > 0;
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
{
|
|
87
|
+
hasMobileNavbarActions && (
|
|
88
|
+
<div
|
|
89
|
+
class="relative shrink-0 border-b border-border-light px-4 py-3"
|
|
90
|
+
data-rd-mobile-navbar-actions
|
|
91
|
+
>
|
|
92
|
+
<nav aria-label="Site shortcuts">
|
|
93
|
+
<div
|
|
94
|
+
class="flex w-full min-w-0 items-center justify-end gap-3"
|
|
95
|
+
data-rd-mobile-navbar-row
|
|
96
|
+
>
|
|
97
|
+
{navbarLinks.length > 0 && (
|
|
98
|
+
<div
|
|
99
|
+
class:list={[
|
|
100
|
+
"flex shrink-0 items-center gap-1",
|
|
101
|
+
mobileButtonCount > 0 && "pr-2",
|
|
102
|
+
]}
|
|
103
|
+
data-rd-mobile-navbar-links
|
|
104
|
+
>
|
|
105
|
+
{navbarLinks.map((link) => (
|
|
106
|
+
<a
|
|
107
|
+
class="flex items-center gap-1 px-1.5 py-[5px] text-[13px] font-medium text-neutral-600/85 whitespace-nowrap transition-colors hover:text-neutral-600 dark:font-normal dark:text-neutral-200/90 dark:hover:text-neutral-200"
|
|
108
|
+
href={link.href}
|
|
109
|
+
x-on:click="open = false"
|
|
110
|
+
>
|
|
111
|
+
{link.icon && (
|
|
112
|
+
<Icon
|
|
113
|
+
class="ml-[3px]"
|
|
114
|
+
name={link.icon}
|
|
115
|
+
width="14"
|
|
116
|
+
height="14"
|
|
117
|
+
/>
|
|
118
|
+
)}
|
|
119
|
+
{link.text}
|
|
120
|
+
</a>
|
|
121
|
+
))}
|
|
122
|
+
</div>
|
|
123
|
+
)}
|
|
124
|
+
{navbarLinks.length > 0 && (
|
|
125
|
+
<div class="shrink-0" data-rd-mobile-navbar-overflow hidden>
|
|
126
|
+
<button
|
|
127
|
+
type="button"
|
|
128
|
+
class="flex size-8 cursor-pointer items-center justify-center rounded-lg text-neutral-600 transition-colors hover:bg-neutral-100/70 hover:text-neutral-900 focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-neutral-500 dark:text-neutral-300 dark:hover:bg-neutral-800/70 dark:hover:text-white dark:focus-visible:outline-neutral-400"
|
|
129
|
+
aria-label="Show more navbar links"
|
|
130
|
+
aria-haspopup="true"
|
|
131
|
+
aria-expanded="false"
|
|
132
|
+
aria-controls="mobile-navbar-overflow-menu"
|
|
133
|
+
data-rd-mobile-navbar-overflow-trigger
|
|
134
|
+
>
|
|
135
|
+
<Icon
|
|
136
|
+
name="lucide:ellipsis-vertical"
|
|
137
|
+
class="size-4"
|
|
138
|
+
aria-hidden="true"
|
|
139
|
+
/>
|
|
140
|
+
</button>
|
|
141
|
+
</div>
|
|
142
|
+
)}
|
|
143
|
+
{mobileButtonCount > 0 && (
|
|
144
|
+
<div
|
|
145
|
+
class="flex shrink-0 items-center gap-3"
|
|
146
|
+
data-rd-mobile-navbar-buttons
|
|
147
|
+
>
|
|
148
|
+
{config.navbar?.secondary && (
|
|
149
|
+
<a
|
|
150
|
+
class="flex h-[33px] shrink-0 items-center gap-1.5 rounded-lg [corner-shape:superellipse(1.2)] border-[0.5px] border-neutral-900/10 bg-linear-to-br from-neutral-100/70 to-neutral-100/90 px-[11px] text-[13px] font-medium text-neutral-900/85 whitespace-nowrap transition-all hover:text-neutral-600 dark:border-white/5 dark:bg-none dark:bg-neutral-800/80 dark:text-neutral-200/95 dark:hover:text-neutral-200"
|
|
151
|
+
href={navbarSecondaryHref}
|
|
152
|
+
x-on:click="open = false"
|
|
153
|
+
>
|
|
154
|
+
{config.navbar.secondary.icon && (
|
|
155
|
+
<Icon
|
|
156
|
+
name={config.navbar.secondary.icon}
|
|
157
|
+
width="14"
|
|
158
|
+
height="14"
|
|
159
|
+
class="-ml-px"
|
|
160
|
+
/>
|
|
161
|
+
)}
|
|
162
|
+
{config.navbar.secondary.text}
|
|
163
|
+
</a>
|
|
164
|
+
)}
|
|
165
|
+
{config.navbar?.primary && (
|
|
166
|
+
<a
|
|
167
|
+
class="mobile-navbar-primary-trigger flex h-8 shrink-0 items-center gap-2 rounded-lg [corner-shape:superellipse(1.2)] px-3 text-[13px] font-normal whitespace-nowrap transition-all duration-200 hover:opacity-95 dark:font-medium"
|
|
168
|
+
style={navbarPrimaryButtonStyle}
|
|
169
|
+
href={navbarPrimaryHref}
|
|
170
|
+
x-on:click="open = false"
|
|
171
|
+
>
|
|
172
|
+
{config.navbar.primary.icon && (
|
|
173
|
+
<Icon
|
|
174
|
+
name={config.navbar.primary.icon}
|
|
175
|
+
width="14"
|
|
176
|
+
height="14"
|
|
177
|
+
class="-ml-px"
|
|
178
|
+
/>
|
|
179
|
+
)}
|
|
180
|
+
{config.navbar.primary.text}
|
|
181
|
+
</a>
|
|
182
|
+
)}
|
|
183
|
+
</div>
|
|
184
|
+
)}
|
|
185
|
+
</div>
|
|
186
|
+
</nav>
|
|
187
|
+
{navbarLinks.length > 0 && (
|
|
188
|
+
<nav
|
|
189
|
+
id="mobile-navbar-overflow-menu"
|
|
190
|
+
aria-label="More navbar links"
|
|
191
|
+
class="absolute right-4 top-full z-50 mt-2 w-56 max-w-[calc(100vw-2rem)] overflow-y-auto rounded-xl border-[0.5px] border-neutral-200 bg-white p-1 shadow-2xl shadow-neutral-900/12 sm:right-6 dark:border-neutral-800 dark:bg-neutral-800 dark:shadow-black/40"
|
|
192
|
+
data-rd-mobile-navbar-overflow-menu
|
|
193
|
+
hidden
|
|
194
|
+
>
|
|
195
|
+
{navbarLinks.map((link) => (
|
|
196
|
+
<a
|
|
197
|
+
class="flex min-h-9 items-center gap-2 rounded-lg px-2.5 py-2 text-[13px] font-medium text-neutral-700 transition-colors hover:bg-neutral-100/70 hover:text-neutral-950 focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-neutral-500 dark:font-normal dark:text-neutral-200 dark:hover:bg-neutral-700/70 dark:hover:text-white dark:focus-visible:outline-neutral-400"
|
|
198
|
+
href={link.href}
|
|
199
|
+
x-on:click="open = false"
|
|
200
|
+
data-rd-mobile-navbar-overflow-link
|
|
201
|
+
>
|
|
202
|
+
{link.icon && (
|
|
203
|
+
<Icon
|
|
204
|
+
name={link.icon}
|
|
205
|
+
width="14"
|
|
206
|
+
height="14"
|
|
207
|
+
class="shrink-0"
|
|
208
|
+
/>
|
|
209
|
+
)}
|
|
210
|
+
<span class="min-w-0 break-words">{link.text}</span>
|
|
211
|
+
</a>
|
|
212
|
+
))}
|
|
213
|
+
</nav>
|
|
214
|
+
)}
|
|
215
|
+
</div>
|
|
216
|
+
)
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
<style>
|
|
220
|
+
.mobile-navbar-primary-trigger {
|
|
221
|
+
--mobile-navbar-primary-theme: var(--mobile-navbar-primary-theme-light);
|
|
222
|
+
--mobile-navbar-primary-foreground: var(
|
|
223
|
+
--mobile-navbar-primary-foreground-light
|
|
224
|
+
);
|
|
225
|
+
background: linear-gradient(
|
|
226
|
+
to bottom,
|
|
227
|
+
color-mix(in oklab, var(--mobile-navbar-primary-theme) 88%, white),
|
|
228
|
+
color-mix(in oklab, var(--mobile-navbar-primary-theme) 90%, black)
|
|
229
|
+
);
|
|
230
|
+
color: var(--mobile-navbar-primary-foreground);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
:global(.dark) .mobile-navbar-primary-trigger {
|
|
234
|
+
--mobile-navbar-primary-theme: var(--mobile-navbar-primary-theme-dark);
|
|
235
|
+
--mobile-navbar-primary-foreground: var(
|
|
236
|
+
--mobile-navbar-primary-foreground-dark
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
</style>
|
|
240
|
+
|
|
241
|
+
<script is:inline>
|
|
242
|
+
(() => {
|
|
243
|
+
const cleanupKey = "__radiantDocsMobileNavbarFitCleanup";
|
|
244
|
+
const previousCleanup = window[cleanupKey];
|
|
245
|
+
|
|
246
|
+
if (typeof previousCleanup === "function") previousCleanup();
|
|
247
|
+
|
|
248
|
+
const root = document.querySelector("[data-rd-mobile-navbar-actions]");
|
|
249
|
+
const row = root?.querySelector("[data-rd-mobile-navbar-row]");
|
|
250
|
+
const links = root?.querySelector("[data-rd-mobile-navbar-links]");
|
|
251
|
+
const buttons = root?.querySelector("[data-rd-mobile-navbar-buttons]");
|
|
252
|
+
const overflow = root?.querySelector("[data-rd-mobile-navbar-overflow]");
|
|
253
|
+
const overflowTrigger = root?.querySelector(
|
|
254
|
+
"[data-rd-mobile-navbar-overflow-trigger]",
|
|
255
|
+
);
|
|
256
|
+
const overflowMenu = root?.querySelector(
|
|
257
|
+
"[data-rd-mobile-navbar-overflow-menu]",
|
|
258
|
+
);
|
|
259
|
+
|
|
260
|
+
if (
|
|
261
|
+
!(root instanceof HTMLElement) ||
|
|
262
|
+
!(row instanceof HTMLElement) ||
|
|
263
|
+
!(links instanceof HTMLElement) ||
|
|
264
|
+
!(overflow instanceof HTMLElement) ||
|
|
265
|
+
!(overflowTrigger instanceof HTMLButtonElement) ||
|
|
266
|
+
!(overflowMenu instanceof HTMLElement)
|
|
267
|
+
) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
const mobileQuery = window.matchMedia("(max-width: 63.999rem)");
|
|
272
|
+
let linksWidth = 0;
|
|
273
|
+
let frameId = 0;
|
|
274
|
+
|
|
275
|
+
const setOverflowMenuOpen = (nextOpen, focusFirstLink = false) => {
|
|
276
|
+
const canOpen =
|
|
277
|
+
nextOpen && mobileQuery.matches && !overflow.hasAttribute("hidden");
|
|
278
|
+
|
|
279
|
+
overflowMenu.hidden = !canOpen;
|
|
280
|
+
overflowTrigger.setAttribute("aria-expanded", String(canOpen));
|
|
281
|
+
|
|
282
|
+
if (!canOpen || !focusFirstLink) return;
|
|
283
|
+
|
|
284
|
+
const firstLink = overflowMenu.querySelector(
|
|
285
|
+
"[data-rd-mobile-navbar-overflow-link]",
|
|
286
|
+
);
|
|
287
|
+
if (firstLink instanceof HTMLElement) firstLink.focus();
|
|
288
|
+
};
|
|
289
|
+
|
|
290
|
+
const measureLinksWidth = () => {
|
|
291
|
+
const clone = links.cloneNode(true);
|
|
292
|
+
if (!(clone instanceof HTMLElement)) return linksWidth;
|
|
293
|
+
|
|
294
|
+
clone.removeAttribute("hidden");
|
|
295
|
+
clone.removeAttribute("data-rd-mobile-navbar-links");
|
|
296
|
+
clone.setAttribute("aria-hidden", "true");
|
|
297
|
+
clone.style.position = "fixed";
|
|
298
|
+
clone.style.inset = "0 auto auto -10000px";
|
|
299
|
+
clone.style.display = "flex";
|
|
300
|
+
clone.style.width = "max-content";
|
|
301
|
+
clone.style.visibility = "hidden";
|
|
302
|
+
clone.style.pointerEvents = "none";
|
|
303
|
+
document.body.append(clone);
|
|
304
|
+
|
|
305
|
+
const width = clone.getBoundingClientRect().width;
|
|
306
|
+
clone.remove();
|
|
307
|
+
return width;
|
|
308
|
+
};
|
|
309
|
+
|
|
310
|
+
const updateLinksVisibility = () => {
|
|
311
|
+
frameId = 0;
|
|
312
|
+
|
|
313
|
+
if (!mobileQuery.matches || row.clientWidth <= 0) {
|
|
314
|
+
if (!mobileQuery.matches) {
|
|
315
|
+
links.removeAttribute("hidden");
|
|
316
|
+
overflow.setAttribute("hidden", "");
|
|
317
|
+
setOverflowMenuOpen(false);
|
|
318
|
+
}
|
|
319
|
+
return;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
const rowGap = Number.parseFloat(getComputedStyle(row).columnGap) || 0;
|
|
323
|
+
const buttonsWidth =
|
|
324
|
+
buttons instanceof HTMLElement
|
|
325
|
+
? buttons.getBoundingClientRect().width
|
|
326
|
+
: 0;
|
|
327
|
+
const requiredWidth =
|
|
328
|
+
linksWidth +
|
|
329
|
+
buttonsWidth +
|
|
330
|
+
(buttons instanceof HTMLElement ? rowGap : 0);
|
|
331
|
+
const linksDoNotFit = requiredWidth > row.clientWidth;
|
|
332
|
+
|
|
333
|
+
links.toggleAttribute("hidden", linksDoNotFit);
|
|
334
|
+
overflow.toggleAttribute("hidden", !linksDoNotFit);
|
|
335
|
+
if (!linksDoNotFit) setOverflowMenuOpen(false);
|
|
336
|
+
};
|
|
337
|
+
|
|
338
|
+
const scheduleUpdate = () => {
|
|
339
|
+
if (frameId !== 0) return;
|
|
340
|
+
frameId = requestAnimationFrame(updateLinksVisibility);
|
|
341
|
+
};
|
|
342
|
+
|
|
343
|
+
const onOverflowTriggerClick = () => {
|
|
344
|
+
setOverflowMenuOpen(overflowMenu.hidden);
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
const onOverflowTriggerKeydown = (event) => {
|
|
348
|
+
if (event.key !== "ArrowDown") return;
|
|
349
|
+
event.preventDefault();
|
|
350
|
+
setOverflowMenuOpen(true, true);
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
const onDocumentPointerDown = (event) => {
|
|
354
|
+
if (overflowMenu.hidden || !(event.target instanceof Node)) return;
|
|
355
|
+
if (
|
|
356
|
+
overflowTrigger.contains(event.target) ||
|
|
357
|
+
overflowMenu.contains(event.target)
|
|
358
|
+
) {
|
|
359
|
+
return;
|
|
360
|
+
}
|
|
361
|
+
setOverflowMenuOpen(false);
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
const onDocumentKeydown = (event) => {
|
|
365
|
+
if (event.key !== "Escape" || overflowMenu.hidden) return;
|
|
366
|
+
setOverflowMenuOpen(false);
|
|
367
|
+
overflowTrigger.focus();
|
|
368
|
+
};
|
|
369
|
+
|
|
370
|
+
const onOverflowMenuClick = (event) => {
|
|
371
|
+
if (
|
|
372
|
+
event.target instanceof Element &&
|
|
373
|
+
event.target.closest("[data-rd-mobile-navbar-overflow-link]")
|
|
374
|
+
) {
|
|
375
|
+
setOverflowMenuOpen(false);
|
|
376
|
+
}
|
|
377
|
+
};
|
|
378
|
+
|
|
379
|
+
linksWidth = measureLinksWidth();
|
|
380
|
+
updateLinksVisibility();
|
|
381
|
+
|
|
382
|
+
const resizeObserver = new ResizeObserver(scheduleUpdate);
|
|
383
|
+
resizeObserver.observe(row);
|
|
384
|
+
if (buttons instanceof HTMLElement) resizeObserver.observe(buttons);
|
|
385
|
+
mobileQuery.addEventListener("change", scheduleUpdate);
|
|
386
|
+
overflowTrigger.addEventListener("click", onOverflowTriggerClick);
|
|
387
|
+
overflowTrigger.addEventListener("keydown", onOverflowTriggerKeydown);
|
|
388
|
+
overflowMenu.addEventListener("click", onOverflowMenuClick);
|
|
389
|
+
document.addEventListener("pointerdown", onDocumentPointerDown);
|
|
390
|
+
document.addEventListener("keydown", onDocumentKeydown);
|
|
391
|
+
|
|
392
|
+
document.fonts?.ready.then(() => {
|
|
393
|
+
if (!root.isConnected) return;
|
|
394
|
+
linksWidth = measureLinksWidth();
|
|
395
|
+
scheduleUpdate();
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
window[cleanupKey] = () => {
|
|
399
|
+
if (frameId !== 0) cancelAnimationFrame(frameId);
|
|
400
|
+
resizeObserver.disconnect();
|
|
401
|
+
mobileQuery.removeEventListener("change", scheduleUpdate);
|
|
402
|
+
overflowTrigger.removeEventListener("click", onOverflowTriggerClick);
|
|
403
|
+
overflowTrigger.removeEventListener("keydown", onOverflowTriggerKeydown);
|
|
404
|
+
overflowMenu.removeEventListener("click", onOverflowMenuClick);
|
|
405
|
+
document.removeEventListener("pointerdown", onDocumentPointerDown);
|
|
406
|
+
document.removeEventListener("keydown", onDocumentKeydown);
|
|
407
|
+
overflowMenu.hidden = true;
|
|
408
|
+
};
|
|
409
|
+
})();
|
|
410
|
+
</script>
|
|
@@ -59,7 +59,7 @@ const snippetStickyClass = hasTopbarNavigationTabs
|
|
|
59
59
|
: "top-[92px] max-h-[calc(100vh-92px)]";
|
|
60
60
|
---
|
|
61
61
|
|
|
62
|
-
<Layout pageTitle={title}>
|
|
62
|
+
<Layout pageTitle={title} hasMarkdownAlternate>
|
|
63
63
|
<article class="mx-auto w-full max-w-7xl">
|
|
64
64
|
<header
|
|
65
65
|
class="mb-8 flex flex-col gap-4 sm:flex-row sm:items-end sm:justify-between"
|
|
@@ -431,24 +431,40 @@ const menuItems: MenuItem[] = [
|
|
|
431
431
|
}
|
|
432
432
|
</style>
|
|
433
433
|
|
|
434
|
-
<script
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
434
|
+
<script>
|
|
435
|
+
const pageActionsSelector = "[data-rd-page-actions]";
|
|
436
|
+
const pageActionsControlsKey = "__rdPageActionsControls";
|
|
437
|
+
|
|
438
|
+
function fallbackCopy(text) {
|
|
439
|
+
const textarea = document.createElement("textarea");
|
|
440
|
+
textarea.value = text;
|
|
441
|
+
textarea.setAttribute("readonly", "");
|
|
442
|
+
textarea.style.position = "fixed";
|
|
443
|
+
textarea.style.opacity = "0";
|
|
444
|
+
document.body.appendChild(textarea);
|
|
445
|
+
textarea.select();
|
|
446
|
+
const copied = document.execCommand("copy");
|
|
447
|
+
document.body.removeChild(textarea);
|
|
448
|
+
return copied;
|
|
449
|
+
}
|
|
445
450
|
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
+
async function copyToClipboard(text) {
|
|
452
|
+
try {
|
|
453
|
+
if (navigator.clipboard?.writeText) {
|
|
454
|
+
await navigator.clipboard.writeText(text);
|
|
455
|
+
return true;
|
|
456
|
+
}
|
|
457
|
+
} catch {
|
|
458
|
+
// Fallback below when clipboard API is unavailable or blocked.
|
|
451
459
|
}
|
|
460
|
+
|
|
461
|
+
return fallbackCopy(text);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
function initPageActions(root) {
|
|
465
|
+
if (!(root instanceof HTMLElement)) return;
|
|
466
|
+
if (root.dataset.pageActionsBound === "true") return;
|
|
467
|
+
|
|
452
468
|
root.dataset.pageActionsBound = "true";
|
|
453
469
|
|
|
454
470
|
const trigger = root.querySelector("[data-rd-page-actions-trigger]");
|
|
@@ -463,32 +479,6 @@ const menuItems: MenuItem[] = [
|
|
|
463
479
|
let mcpMetadataPromise;
|
|
464
480
|
const copyFeedbackTimeouts = new WeakMap();
|
|
465
481
|
|
|
466
|
-
function fallbackCopy(text) {
|
|
467
|
-
const textarea = document.createElement("textarea");
|
|
468
|
-
textarea.value = text;
|
|
469
|
-
textarea.setAttribute("readonly", "");
|
|
470
|
-
textarea.style.position = "fixed";
|
|
471
|
-
textarea.style.opacity = "0";
|
|
472
|
-
document.body.appendChild(textarea);
|
|
473
|
-
textarea.select();
|
|
474
|
-
const copied = document.execCommand("copy");
|
|
475
|
-
document.body.removeChild(textarea);
|
|
476
|
-
return copied;
|
|
477
|
-
}
|
|
478
|
-
|
|
479
|
-
async function copyToClipboard(text) {
|
|
480
|
-
try {
|
|
481
|
-
if (navigator.clipboard?.writeText) {
|
|
482
|
-
await navigator.clipboard.writeText(text);
|
|
483
|
-
return true;
|
|
484
|
-
}
|
|
485
|
-
} catch {
|
|
486
|
-
// Fallback below when clipboard API is unavailable or blocked.
|
|
487
|
-
}
|
|
488
|
-
|
|
489
|
-
return fallbackCopy(text);
|
|
490
|
-
}
|
|
491
|
-
|
|
492
482
|
function setExpanded(nextExpanded) {
|
|
493
483
|
if (!(trigger instanceof HTMLElement)) return;
|
|
494
484
|
trigger.setAttribute("aria-expanded", String(nextExpanded));
|
|
@@ -711,5 +701,21 @@ const menuItems: MenuItem[] = [
|
|
|
711
701
|
closeMenu();
|
|
712
702
|
});
|
|
713
703
|
});
|
|
714
|
-
}
|
|
704
|
+
}
|
|
705
|
+
|
|
706
|
+
function initAllPageActions() {
|
|
707
|
+
document.querySelectorAll(pageActionsSelector).forEach((root) => {
|
|
708
|
+
initPageActions(root);
|
|
709
|
+
});
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
window[pageActionsControlsKey] = window[pageActionsControlsKey] || {};
|
|
713
|
+
window[pageActionsControlsKey].init = initAllPageActions;
|
|
714
|
+
|
|
715
|
+
if (!window[pageActionsControlsKey].installed) {
|
|
716
|
+
window[pageActionsControlsKey].installed = true;
|
|
717
|
+
document.addEventListener("astro:page-load", initAllPageActions);
|
|
718
|
+
}
|
|
719
|
+
|
|
720
|
+
initAllPageActions();
|
|
715
721
|
</script>
|
|
@@ -23,7 +23,7 @@ function buildHref(route: Route): string {
|
|
|
23
23
|
|
|
24
24
|
{
|
|
25
25
|
(previousRoute || nextRoute) && (
|
|
26
|
-
<nav class="w-full
|
|
26
|
+
<nav class="w-full" aria-label="Page pagination">
|
|
27
27
|
<div class="flex gap-4 flex-col sm:flex-row">
|
|
28
28
|
{previousRoute && (
|
|
29
29
|
<a
|
|
@@ -12,9 +12,15 @@ import { Icon } from "astro-icon/components";
|
|
|
12
12
|
<!-- Search Trigger Button -->
|
|
13
13
|
<button
|
|
14
14
|
x-on:click="open()"
|
|
15
|
-
|
|
15
|
+
x-bind:data-search-open="isOpen ? 'true' : 'false'"
|
|
16
|
+
aria-label="Search documentation"
|
|
17
|
+
class="mobile-search-trigger flex size-10 cursor-pointer items-center justify-center rounded-lg text-xs text-neutral-900 transition-colors hover:bg-neutral-100/70 dark:text-white dark:hover:bg-neutral-800/70 md:h-[33px] md:w-auto md:min-w-60 md:justify-start md:gap-2 md:border md:border-border md:bg-white/90 md:px-3 md:text-neutral-500/80 md:shadow-xs md:transition md:hover:bg-white/90 md:hover:text-neutral-500 md:hover:shadow-sm dark:md:bg-neutral-800 dark:md:text-neutral-400/70 dark:md:hover:bg-neutral-800 dark:md:hover:text-neutral-400 lg:min-w-64"
|
|
16
18
|
>
|
|
17
|
-
<Icon
|
|
19
|
+
<Icon
|
|
20
|
+
name="lucide:search"
|
|
21
|
+
is:inline
|
|
22
|
+
class="mobile-search-icon size-4"
|
|
23
|
+
/>
|
|
18
24
|
<span class="hidden md:inline">Search documentation</span>
|
|
19
25
|
<kbd
|
|
20
26
|
class="font-sans tracking-wider hidden md:inline-flex ml-auto items-center gap-0.5 px-1.5 py-px text-[10px] text-neutral-500 dark:text-neutral-400 font-medium bg-white dark:bg-neutral-700 border border-border/80 rounded"
|
|
@@ -53,6 +59,7 @@ import { Icon } from "astro-icon/components";
|
|
|
53
59
|
>
|
|
54
60
|
<Icon
|
|
55
61
|
name="lucide:search"
|
|
62
|
+
is:inline
|
|
56
63
|
class="size-5 sm:size-[18px] text-neutral-400 dark:text-neutral-500 shrink-0"
|
|
57
64
|
/>
|
|
58
65
|
<input
|
|
@@ -94,7 +101,8 @@ import { Icon } from "astro-icon/components";
|
|
|
94
101
|
class="size-8 mx-auto mb-3 text-neutral-300 dark:text-neutral-600"
|
|
95
102
|
/>
|
|
96
103
|
<p class="text-sm">
|
|
97
|
-
No results found for <strong
|
|
104
|
+
No results found for <strong
|
|
105
|
+
class="text-neutral-600 dark:text-neutral-300"
|
|
98
106
|
>"<span x-text="query"></span>"</strong
|
|
99
107
|
>.
|
|
100
108
|
</p>
|
|
@@ -172,6 +180,19 @@ import { Icon } from "astro-icon/components";
|
|
|
172
180
|
</template>
|
|
173
181
|
</div>
|
|
174
182
|
|
|
183
|
+
<style>
|
|
184
|
+
@media (max-width: 47.999rem) {
|
|
185
|
+
.mobile-search-trigger[data-search-open="true"] {
|
|
186
|
+
background-color: transparent !important;
|
|
187
|
+
transition: none;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
.mobile-search-icon :global(g) {
|
|
191
|
+
stroke-width: 2.75;
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
</style>
|
|
195
|
+
|
|
175
196
|
<script>
|
|
176
197
|
import {
|
|
177
198
|
search as pagefindSearch,
|
|
@@ -1,24 +1,59 @@
|
|
|
1
1
|
---
|
|
2
2
|
import { getConfig, type DocsConfig } from "../lib/validation";
|
|
3
|
+
import MobileNavbarActions from "./MobileNavbarActions.astro";
|
|
3
4
|
import ThemeSwitcher from "./ThemeSwitcher.astro";
|
|
4
5
|
import SidebarMenu from "./SidebarMenu.astro";
|
|
5
6
|
|
|
6
7
|
interface Props {
|
|
7
8
|
askAiEnabled?: boolean;
|
|
9
|
+
showMobileNavbarActions?: boolean;
|
|
8
10
|
}
|
|
9
11
|
|
|
10
|
-
const { askAiEnabled = false
|
|
12
|
+
const { askAiEnabled = false, showMobileNavbarActions = false } =
|
|
13
|
+
Astro.props as Props;
|
|
11
14
|
|
|
12
15
|
const config: DocsConfig = await getConfig();
|
|
13
16
|
---
|
|
14
17
|
|
|
15
18
|
<aside class="flex flex-col h-full">
|
|
19
|
+
{
|
|
20
|
+
showMobileNavbarActions ? (
|
|
21
|
+
<MobileNavbarActions />
|
|
22
|
+
) : null
|
|
23
|
+
}
|
|
16
24
|
<nav
|
|
17
25
|
data-rd-sidebar-scroll-container
|
|
18
26
|
class="min-h-0 flex-1 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
|
19
27
|
>
|
|
20
28
|
<SidebarMenu navigation={config.navigation} />
|
|
21
29
|
</nav>
|
|
30
|
+
<script is:inline>
|
|
31
|
+
(() => {
|
|
32
|
+
const script = document.currentScript;
|
|
33
|
+
const sidebar = script?.previousElementSibling;
|
|
34
|
+
|
|
35
|
+
if (
|
|
36
|
+
!(sidebar instanceof HTMLElement) ||
|
|
37
|
+
!sidebar.hasAttribute("data-rd-sidebar-scroll-container")
|
|
38
|
+
) {
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
if (document.readyState !== "loading") return;
|
|
43
|
+
|
|
44
|
+
try {
|
|
45
|
+
const value = Number(
|
|
46
|
+
sessionStorage.getItem("radiant-docs:sidebar-scroll-top"),
|
|
47
|
+
);
|
|
48
|
+
if (!Number.isFinite(value) || value <= 0) return;
|
|
49
|
+
|
|
50
|
+
sidebar.scrollTop = Math.max(0, Math.round(value));
|
|
51
|
+
sidebar.dataset.rdSidebarScrollRestored = "true";
|
|
52
|
+
} catch {
|
|
53
|
+
// Storage can be unavailable in private browsing or locked-down embeds.
|
|
54
|
+
}
|
|
55
|
+
})();
|
|
56
|
+
</script>
|
|
22
57
|
<div
|
|
23
58
|
class:list={[
|
|
24
59
|
"bg-background z-10 px-3 pt-3 pb-[calc(env(safe-area-inset-bottom)+0.75rem)] border-t border-t-border-light flex gap-1.5 items-center",
|
|
@@ -64,7 +64,9 @@ const selectedTabItem = tabItems[currentTabIndex];
|
|
|
64
64
|
x-data={`{ selectedTabIndex: ${currentTabIndex} }`}
|
|
65
65
|
x-init={`$watch('open', (value) => { if (value) selectedTabIndex = ${currentTabIndex} })`}
|
|
66
66
|
>
|
|
67
|
-
<div
|
|
67
|
+
<div
|
|
68
|
+
class="sticky top-0 z-20 h-11 border-b border-border-light bg-background lg:hidden"
|
|
69
|
+
>
|
|
68
70
|
<nav
|
|
69
71
|
class="h-full overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
|
70
72
|
aria-label="Documentation sections"
|