radiant-docs 0.1.61 → 0.1.62

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.
Files changed (30) hide show
  1. package/package.json +1 -1
  2. package/template/package-lock.json +10 -4
  3. package/template/package.json +11 -2
  4. package/template/scripts/generate-proxy-allowed-origins.mjs +14 -6
  5. package/template/scripts/publish-shiki-platform-assets.mjs +1151 -0
  6. package/template/src/components/Header.astro +6 -1
  7. package/template/src/components/NavigationTabList.astro +65 -0
  8. package/template/src/components/NavigationTabs.astro +109 -0
  9. package/template/src/components/OpenApiPage.astro +17 -1
  10. package/template/src/components/Sidebar.astro +2 -2
  11. package/template/src/components/SidebarDropdown.astro +105 -44
  12. package/template/src/components/SidebarMenu.astro +3 -0
  13. package/template/src/components/SidebarSegmented.astro +87 -52
  14. package/template/src/components/SidebarTabs.astro +86 -0
  15. package/template/src/components/chat/AssistantDocsWidget.tsx +127 -2
  16. package/template/src/components/chat/AssistantEmbedPanel.tsx +269 -283
  17. package/template/src/components/user/Accordion.astro +1 -1
  18. package/template/src/components/user/Callout.astro +2 -2
  19. package/template/src/components/user/CodeBlock.astro +58 -7
  20. package/template/src/components/user/CodeGroup.astro +52 -1
  21. package/template/src/components/user/Column.astro +1 -1
  22. package/template/src/components/user/Step.astro +1 -1
  23. package/template/src/components/user/Tabs.astro +1 -1
  24. package/template/src/generated/shiki-platform-assets.json +24 -0
  25. package/template/src/layouts/Layout.astro +111 -8
  26. package/template/src/lib/assistant-panel-config.ts +59 -0
  27. package/template/src/lib/assistant-shiki-client.ts +506 -0
  28. package/template/src/lib/mdx/remark-resolve-internal-links.ts +334 -17
  29. package/template/src/lib/routes.ts +66 -24
  30. package/template/src/styles/global.css +12 -0
@@ -0,0 +1,86 @@
1
+ ---
2
+ import { getConfig, type NavTabs } from "../lib/validation";
3
+ import SidebarMenu from "./SidebarMenu.astro";
4
+ import { slugify } from "../lib/utils";
5
+ import { stripBasePath } from "../lib/base-path";
6
+ import { getAllRoutes } from "../lib/routes";
7
+ import NavigationTabList from "./NavigationTabList.astro";
8
+
9
+ interface Props {
10
+ tabs: NavTabs;
11
+ parentSlug?: string;
12
+ }
13
+
14
+ const { tabs, parentSlug = "" } = Astro.props;
15
+ const tabItems = tabs.items ?? [];
16
+ const pathname = stripBasePath(Astro.url.pathname);
17
+ const config = await getConfig();
18
+ const routes = (await getAllRoutes()).filter((route) => !route.hidden);
19
+ const normalizedPathParts = pathname
20
+ .replace(/^\/+/, "")
21
+ .replace(/\/+$/, "")
22
+ .split("/")
23
+ .filter(Boolean);
24
+ const parentParts = parentSlug.split("/").filter(Boolean);
25
+
26
+ function getTabSlug(tab: (typeof tabItems)[number]): string {
27
+ return slugify(tab.slug || tab.label);
28
+ }
29
+
30
+ function getTabPrefix(tab: (typeof tabItems)[number]): string {
31
+ const tabSlug = getTabSlug(tab);
32
+ return parentSlug ? `${parentSlug}/${tabSlug}` : tabSlug;
33
+ }
34
+
35
+ let currentTabIndex = tabItems.findIndex(
36
+ (item) => getTabSlug(item) === normalizedPathParts[parentParts.length],
37
+ );
38
+
39
+ if (pathname === "/" && config.home) {
40
+ const homeRoute = routes.find(
41
+ (route) => route.type === "mdx" && route.filePath === config.home,
42
+ );
43
+
44
+ if (homeRoute) {
45
+ const homeRouteParts = homeRoute.slug.split("/").filter(Boolean);
46
+ const homeTabSegment = homeRouteParts[parentParts.length];
47
+ const homeTabIndex = tabItems.findIndex(
48
+ (item) => getTabSlug(item) === homeTabSegment,
49
+ );
50
+ if (homeTabIndex !== -1) {
51
+ currentTabIndex = homeTabIndex;
52
+ }
53
+ }
54
+ }
55
+
56
+ currentTabIndex = currentTabIndex === -1 ? 0 : currentTabIndex;
57
+
58
+ const selectedTabItem = tabItems[currentTabIndex];
59
+ ---
60
+
61
+ {
62
+ selectedTabItem ? (
63
+ <div
64
+ x-data={`{ selectedTabIndex: ${currentTabIndex} }`}
65
+ x-init={`$watch('open', (value) => { if (value) selectedTabIndex = ${currentTabIndex} })`}
66
+ >
67
+ <div class="lg:hidden h-11 border-b border-border-light bg-background">
68
+ <nav
69
+ class="h-full overflow-x-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
70
+ aria-label="Documentation sections"
71
+ >
72
+ <NavigationTabList
73
+ items={tabItems}
74
+ currentIndex={currentTabIndex}
75
+ mode="button"
76
+ />
77
+ </nav>
78
+ </div>
79
+ {tabItems.map((item, index) => (
80
+ <div x-show={`selectedTabIndex === ${index}`} x-cloak>
81
+ <SidebarMenu navigation={item} parentSlug={getTabPrefix(item)} />
82
+ </div>
83
+ ))}
84
+ </div>
85
+ ) : null
86
+ }
@@ -1,11 +1,12 @@
1
1
  import type { JSX } from "preact";
2
2
  import { navigate } from "astro:transitions/client";
3
- import { useEffect, useRef, useState } from "preact/hooks";
3
+ import { useEffect, useLayoutEffect, useRef, useState } from "preact/hooks";
4
4
  import AssistantEmbedPanel, {
5
5
  ASSISTANT_PANEL_STORAGE_KEY,
6
6
  AssistantPanelIcon,
7
7
  type AssistantPanelSize,
8
8
  } from "./AssistantEmbedPanel";
9
+ import type { AssistantShikiRuntimeConfig } from "../../lib/assistant-shiki-client";
9
10
  import {
10
11
  DEFAULT_ASSISTANT_CHROME_CONFIG,
11
12
  type AssistantChromeConfig,
@@ -31,6 +32,7 @@ type AssistantDocsWidgetProps = {
31
32
  emptyStateQuestions?: string[];
32
33
  devProxyToken?: string;
33
34
  chrome?: AssistantChromeConfig;
35
+ shiki?: AssistantShikiRuntimeConfig;
34
36
  };
35
37
 
36
38
  type AssistantOpenRequestWindow = Window & {
@@ -39,6 +41,20 @@ type AssistantOpenRequestWindow = Window & {
39
41
  __assistantDocsLauncherEntered?: boolean;
40
42
  };
41
43
 
44
+ type PageScrollLockState = {
45
+ scrollX: number;
46
+ scrollY: number;
47
+ htmlOverflow: string;
48
+ htmlOverscrollBehavior: string;
49
+ bodyOverflow: string;
50
+ bodyOverscrollBehavior: string;
51
+ bodyPosition: string;
52
+ bodyTop: string;
53
+ bodyLeft: string;
54
+ bodyRight: string;
55
+ bodyWidth: string;
56
+ };
57
+
42
58
  function normalizePanelSize(value: unknown): AssistantPanelSize {
43
59
  return value === "expanded" ? "expanded" : "default";
44
60
  }
@@ -75,6 +91,7 @@ export default function AssistantDocsWidget({
75
91
  emptyStateQuestions,
76
92
  devProxyToken,
77
93
  chrome,
94
+ shiki,
78
95
  }: AssistantDocsWidgetProps) {
79
96
  const [isOpen, setIsOpen] = useState(false);
80
97
  const [panelSize, setPanelSize] = useState<AssistantPanelSize>(() =>
@@ -99,6 +116,7 @@ export default function AssistantDocsWidget({
99
116
  const navigationTransitionCleanupRef = useRef<(() => void) | null>(null);
100
117
  const navigationTransitionTimeoutRef = useRef<number | null>(null);
101
118
  const panelSizeTransitionTimeoutRef = useRef<number | null>(null);
119
+ const pageScrollLockRef = useRef<PageScrollLockState | null>(null);
102
120
  const chromeConfig = chrome ?? DEFAULT_ASSISTANT_CHROME_CONFIG;
103
121
  const numericZIndex =
104
122
  Number.parseInt(chromeConfig.zIndex, 10) ||
@@ -159,6 +177,13 @@ export default function AssistantDocsWidget({
159
177
  };
160
178
 
161
179
  const closePanel = () => {
180
+ if (typeof document !== "undefined") {
181
+ const activeElement = document.activeElement;
182
+ if (activeElement instanceof HTMLElement) {
183
+ activeElement.blur();
184
+ }
185
+ }
186
+
162
187
  setIsOpen(false);
163
188
  };
164
189
 
@@ -267,6 +292,96 @@ export default function AssistantDocsWidget({
267
292
  };
268
293
  }, []);
269
294
 
295
+ useLayoutEffect(() => {
296
+ if (!isOpen || typeof window === "undefined") {
297
+ return;
298
+ }
299
+
300
+ const unlockPageScroll = () => {
301
+ const lockState = pageScrollLockRef.current;
302
+ if (!lockState) {
303
+ return;
304
+ }
305
+
306
+ document.documentElement.style.overflow = lockState.htmlOverflow;
307
+ document.documentElement.style.overscrollBehavior =
308
+ lockState.htmlOverscrollBehavior;
309
+ document.body.style.overflow = lockState.bodyOverflow;
310
+ document.body.style.overscrollBehavior =
311
+ lockState.bodyOverscrollBehavior;
312
+ document.body.style.position = lockState.bodyPosition;
313
+ document.body.style.top = lockState.bodyTop;
314
+ document.body.style.left = lockState.bodyLeft;
315
+ document.body.style.right = lockState.bodyRight;
316
+ document.body.style.width = lockState.bodyWidth;
317
+ pageScrollLockRef.current = null;
318
+
319
+ window.scrollTo(lockState.scrollX, lockState.scrollY);
320
+ };
321
+
322
+ const lockPageScroll = () => {
323
+ if (pageScrollLockRef.current) {
324
+ return;
325
+ }
326
+
327
+ const html = document.documentElement;
328
+ const body = document.body;
329
+ const scrollX = window.scrollX;
330
+ const scrollY = window.scrollY;
331
+ pageScrollLockRef.current = {
332
+ scrollX,
333
+ scrollY,
334
+ htmlOverflow: html.style.overflow,
335
+ htmlOverscrollBehavior: html.style.overscrollBehavior,
336
+ bodyOverflow: body.style.overflow,
337
+ bodyOverscrollBehavior: body.style.overscrollBehavior,
338
+ bodyPosition: body.style.position,
339
+ bodyTop: body.style.top,
340
+ bodyLeft: body.style.left,
341
+ bodyRight: body.style.right,
342
+ bodyWidth: body.style.width,
343
+ };
344
+
345
+ html.style.overflow = "hidden";
346
+ html.style.overscrollBehavior = "none";
347
+ body.style.overflow = "hidden";
348
+ body.style.overscrollBehavior = "none";
349
+ body.style.position = "fixed";
350
+ body.style.top = `-${scrollY}px`;
351
+ body.style.left = "0";
352
+ body.style.right = "0";
353
+ body.style.width = "100%";
354
+ };
355
+
356
+ const fullscreenPanelQuery = window.matchMedia(
357
+ `(max-width: ${chromeConfig.mobileBreakpoint}), (max-height: ${chromeConfig.panelFullscreenHeightBreakpoint})`,
358
+ );
359
+ const syncPageScrollLock = () => {
360
+ if (fullscreenPanelQuery.matches) {
361
+ lockPageScroll();
362
+ return;
363
+ }
364
+
365
+ unlockPageScroll();
366
+ };
367
+
368
+ syncPageScrollLock();
369
+ fullscreenPanelQuery.addEventListener("change", syncPageScrollLock);
370
+ window.addEventListener("resize", syncPageScrollLock);
371
+ window.addEventListener("orientationchange", syncPageScrollLock);
372
+
373
+ return () => {
374
+ fullscreenPanelQuery.removeEventListener("change", syncPageScrollLock);
375
+ window.removeEventListener("resize", syncPageScrollLock);
376
+ window.removeEventListener("orientationchange", syncPageScrollLock);
377
+ unlockPageScroll();
378
+ };
379
+ }, [
380
+ chromeConfig.mobileBreakpoint,
381
+ chromeConfig.panelFullscreenHeightBreakpoint,
382
+ isOpen,
383
+ ]);
384
+
270
385
  useEffect(() => {
271
386
  return () => {
272
387
  navigationTransitionCleanupRef.current?.();
@@ -320,10 +435,15 @@ export default function AssistantDocsWidget({
320
435
  emptyStateHeading={emptyStateHeading}
321
436
  emptyStateQuestions={emptyStateQuestions}
322
437
  devProxyToken={devProxyToken}
438
+ shiki={shiki}
323
439
  panelSurface="inline"
324
440
  linkTarget="current"
325
441
  allowApiPathQueryOverride={false}
326
442
  openSignal={openSignal}
443
+ mobileBreakpoint={chromeConfig.mobileBreakpoint}
444
+ panelFullscreenHeightBreakpoint={
445
+ chromeConfig.panelFullscreenHeightBreakpoint
446
+ }
327
447
  onRequestOpen={openPanel}
328
448
  onRequestClose={closePanel}
329
449
  onRequestPanelSizeToggle={handlePanelSizeChange}
@@ -571,12 +691,17 @@ export default function AssistantDocsWidget({
571
691
 
572
692
  @media (max-width: ${chromeConfig.mobileBreakpoint}), (max-height: ${chromeConfig.panelFullscreenHeightBreakpoint}) {
573
693
  .assistant-docs-panel-shell {
574
- inset: 0 !important;
694
+ top: 0 !important;
695
+ right: auto !important;
696
+ bottom: auto !important;
697
+ left: 0 !important;
575
698
  z-index: var(--assistant-docs-mobile-panel-z-index) !important;
576
699
  width: 100vw !important;
577
700
  height: 100dvh !important;
578
701
  border: 0 !important;
579
702
  border-radius: 0 !important;
703
+ transition: none !important;
704
+ transition-behavior: normal !important;
580
705
  }
581
706
 
582
707
  .assistant-docs-panel-shell > div {