radiant-docs 0.1.69 → 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 +11 -3
- package/template/src/components/MobileNavbarActions.astro +410 -0
- package/template/src/components/PagePagination.astro +1 -1
- package/template/src/components/Search.astro +24 -3
- package/template/src/components/Sidebar.astro +9 -1
- package/template/src/components/SidebarTabs.astro +3 -1
- package/template/src/components/chat/AssistantDocsWidget.tsx +1 -1
- package/template/src/components/chat/AssistantEmbedPanel.tsx +36 -48
- package/template/src/components/endpoint/PlaygroundForm.astro +1 -1
- package/template/src/layouts/Layout.astro +21 -2
- 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>
|
|
@@ -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,18 +1,26 @@
|
|
|
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"
|
|
@@ -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"
|
|
@@ -6,7 +6,7 @@ import AssistantEmbedPanel, {
|
|
|
6
6
|
AssistantPanelIcon,
|
|
7
7
|
type AssistantPanelSize,
|
|
8
8
|
} from "./AssistantEmbedPanel";
|
|
9
|
-
import type { AssistantShikiRuntimeConfig } from "
|
|
9
|
+
import type { AssistantShikiRuntimeConfig } from "@radiant-docs/assistant-shiki";
|
|
10
10
|
import {
|
|
11
11
|
DEFAULT_ASSISTANT_CHROME_CONFIG,
|
|
12
12
|
type AssistantChromeConfig,
|
|
@@ -6,13 +6,14 @@ import remarkParse from "remark-parse";
|
|
|
6
6
|
import remarkGfm from "remark-gfm";
|
|
7
7
|
import remarkRehype from "remark-rehype";
|
|
8
8
|
import rehypeStringify from "rehype-stringify";
|
|
9
|
-
import { getDocsBasePath, withBasePath } from "../../lib/base-path";
|
|
10
9
|
import {
|
|
11
10
|
highlightAssistantCodeToHtml,
|
|
12
11
|
normalizeAssistantCodeLanguage,
|
|
12
|
+
resolveAssistantLanguageLabel,
|
|
13
13
|
warmAssistantShikiRuntime,
|
|
14
14
|
type AssistantShikiRuntimeConfig,
|
|
15
|
-
} from "
|
|
15
|
+
} from "@radiant-docs/assistant-shiki";
|
|
16
|
+
import { getDocsBasePath, withBasePath } from "../../lib/base-path";
|
|
16
17
|
|
|
17
18
|
type AssistantLinkTarget = "current" | "blank";
|
|
18
19
|
export type AssistantPanelSize = "default" | "expanded";
|
|
@@ -129,30 +130,6 @@ const DEFAULT_ASSISTANT_MOBILE_BREAKPOINT = "640px";
|
|
|
129
130
|
const DEFAULT_ASSISTANT_PANEL_FULLSCREEN_HEIGHT_BREAKPOINT = "560px";
|
|
130
131
|
const markdownHtmlCache = new Map<string, string>();
|
|
131
132
|
const markdownHtmlPromiseCache = new Map<string, Promise<string>>();
|
|
132
|
-
const ASSISTANT_LANGUAGE_LABEL: Record<string, string> = {
|
|
133
|
-
bash: "Bash",
|
|
134
|
-
css: "CSS",
|
|
135
|
-
diff: "Diff",
|
|
136
|
-
go: "Go",
|
|
137
|
-
html: "HTML",
|
|
138
|
-
java: "Java",
|
|
139
|
-
javascript: "JavaScript",
|
|
140
|
-
jsx: "JSX",
|
|
141
|
-
json: "JSON",
|
|
142
|
-
markdown: "Markdown",
|
|
143
|
-
mdx: "MDX",
|
|
144
|
-
php: "PHP",
|
|
145
|
-
plaintext: "Text",
|
|
146
|
-
python: "Python",
|
|
147
|
-
ruby: "Ruby",
|
|
148
|
-
rust: "Rust",
|
|
149
|
-
shell: "Shell",
|
|
150
|
-
sql: "SQL",
|
|
151
|
-
tsx: "TSX",
|
|
152
|
-
typescript: "TypeScript",
|
|
153
|
-
yaml: "YAML",
|
|
154
|
-
};
|
|
155
|
-
|
|
156
133
|
const EXTERNAL_PROTOCOL_REGEX = /^[a-zA-Z][a-zA-Z\d+\-.]*:/;
|
|
157
134
|
|
|
158
135
|
function buildMarkdownCacheKey(
|
|
@@ -633,20 +610,6 @@ function escapeHtml(value: string): string {
|
|
|
633
610
|
.replaceAll("'", "'");
|
|
634
611
|
}
|
|
635
612
|
|
|
636
|
-
function resolveAssistantLanguageLabel(language: string): string {
|
|
637
|
-
const normalized = language.trim().toLowerCase();
|
|
638
|
-
if (!normalized) {
|
|
639
|
-
return "";
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
return (
|
|
643
|
-
ASSISTANT_LANGUAGE_LABEL[normalized] ??
|
|
644
|
-
normalized
|
|
645
|
-
.replace(/[-_]+/g, " ")
|
|
646
|
-
.replace(/\b\w/g, (char) => char.toUpperCase())
|
|
647
|
-
);
|
|
648
|
-
}
|
|
649
|
-
|
|
650
613
|
function ensureCodeBlockCopyButton(preElement: HTMLPreElement): void {
|
|
651
614
|
const existingButton = Array.from(preElement.children).find(
|
|
652
615
|
(child) =>
|
|
@@ -1025,20 +988,33 @@ function AssistantSourcesBlock({
|
|
|
1025
988
|
}
|
|
1026
989
|
|
|
1027
990
|
return (
|
|
1028
|
-
<div
|
|
1029
|
-
className="
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
<
|
|
1033
|
-
<ul>
|
|
991
|
+
<div className="ask-ai-sources mt-4 max-w-full min-w-0" onClick={onClick}>
|
|
992
|
+
<div className="text-xs font-semibold text-neutral-700 dark:text-neutral-200">
|
|
993
|
+
Sources
|
|
994
|
+
</div>
|
|
995
|
+
<ul className="mt-2 flex flex-wrap gap-2">
|
|
1034
996
|
{visibleSources.map((source) => (
|
|
1035
|
-
<li
|
|
997
|
+
<li
|
|
998
|
+
key={`${source.href}\0${source.label}`}
|
|
999
|
+
className="min-w-0 max-w-full"
|
|
1000
|
+
>
|
|
1036
1001
|
<a
|
|
1037
1002
|
href={withBasePath(source.href)}
|
|
1038
1003
|
target={linkTarget === "blank" ? "_blank" : undefined}
|
|
1039
1004
|
rel={linkTarget === "blank" ? "noopener noreferrer" : undefined}
|
|
1005
|
+
className="inline-flex max-w-full items-center gap-1.5 rounded-md border border-neutral-200 bg-white px-2 py-1 text-xs font-medium text-neutral-700 transition-colors hover:bg-neutral-50 hover:text-neutral-950 dark:border-neutral-800 dark:bg-neutral-900 dark:text-neutral-200 dark:hover:bg-neutral-800 dark:hover:text-white"
|
|
1040
1006
|
>
|
|
1041
|
-
|
|
1007
|
+
<Icon
|
|
1008
|
+
icon="lucide:file-text"
|
|
1009
|
+
className="size-3.5 shrink-0"
|
|
1010
|
+
aria-hidden="true"
|
|
1011
|
+
/>
|
|
1012
|
+
<span className="truncate">{source.label}</span>
|
|
1013
|
+
<Icon
|
|
1014
|
+
icon="lucide:external-link"
|
|
1015
|
+
className="size-3 shrink-0 text-neutral-500 dark:text-neutral-400"
|
|
1016
|
+
aria-hidden="true"
|
|
1017
|
+
/>
|
|
1042
1018
|
</a>
|
|
1043
1019
|
</li>
|
|
1044
1020
|
))}
|
|
@@ -2846,6 +2822,18 @@ export default function AssistantEmbedPanel({
|
|
|
2846
2822
|
-ms-overflow-style: none;
|
|
2847
2823
|
}
|
|
2848
2824
|
|
|
2825
|
+
.ask-ai-markdown [data-rd-code-line] {
|
|
2826
|
+
display: flex;
|
|
2827
|
+
width: max-content;
|
|
2828
|
+
min-width: 100%;
|
|
2829
|
+
}
|
|
2830
|
+
|
|
2831
|
+
.ask-ai-markdown [data-rd-code-line-content] {
|
|
2832
|
+
flex: 1;
|
|
2833
|
+
padding-inline: 0.75rem;
|
|
2834
|
+
white-space: pre;
|
|
2835
|
+
}
|
|
2836
|
+
|
|
2849
2837
|
.dark .ask-ai-markdown pre > code {
|
|
2850
2838
|
scrollbar-width: none;
|
|
2851
2839
|
}
|
|
@@ -1074,7 +1074,7 @@ const headerValuePrefixes = Object.fromEntries(
|
|
|
1074
1074
|
import {
|
|
1075
1075
|
highlightAssistantCodeToHtml,
|
|
1076
1076
|
type AssistantShikiRuntimeConfig,
|
|
1077
|
-
} from "
|
|
1077
|
+
} from "@radiant-docs/assistant-shiki";
|
|
1078
1078
|
|
|
1079
1079
|
type PlaygroundShikiConfig = AssistantShikiRuntimeConfig | null | undefined;
|
|
1080
1080
|
|