radiant-docs 0.1.60 → 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 (41) 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 +178 -14
  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 +287 -290
  17. package/template/src/components/endpoint/PlaygroundBar.astro +54 -9
  18. package/template/src/components/endpoint/PlaygroundForm.astro +1 -1
  19. package/template/src/components/endpoint/RequestSnippets.astro +6 -1
  20. package/template/src/components/endpoint/ResponseFieldTree.astro +17 -13
  21. package/template/src/components/endpoint/ResponseFields.astro +4 -6
  22. package/template/src/components/endpoint/ResponseSnippets.astro +6 -1
  23. package/template/src/components/sidebar/SidebarEndpointLink.astro +9 -12
  24. package/template/src/components/sidebar/SidebarOpenApi.astro +3 -9
  25. package/template/src/components/ui/Field.astro +18 -15
  26. package/template/src/components/ui/Tag.astro +16 -2
  27. package/template/src/components/user/Accordion.astro +1 -1
  28. package/template/src/components/user/Callout.astro +2 -2
  29. package/template/src/components/user/CodeBlock.astro +58 -7
  30. package/template/src/components/user/CodeGroup.astro +52 -1
  31. package/template/src/components/user/Column.astro +1 -1
  32. package/template/src/components/user/Step.astro +1 -1
  33. package/template/src/components/user/Tabs.astro +1 -1
  34. package/template/src/generated/shiki-platform-assets.json +24 -0
  35. package/template/src/layouts/Layout.astro +111 -8
  36. package/template/src/lib/assistant-panel-config.ts +59 -0
  37. package/template/src/lib/assistant-shiki-client.ts +506 -0
  38. package/template/src/lib/mdx/remark-resolve-internal-links.ts +334 -17
  39. package/template/src/lib/routes.ts +66 -24
  40. package/template/src/lib/utils.ts +11 -0
  41. package/template/src/styles/global.css +12 -0
@@ -190,7 +190,7 @@ class="rd-prose-block">
190
190
  >
191
191
  <div
192
192
  data-rd-tabs-panel-content
193
- class="prose-rules w-full max-w-none! *:max-w-none!"
193
+ class="prose-rules w-full max-w-none!"
194
194
  set:html={content}
195
195
  />
196
196
  </div>
@@ -0,0 +1,24 @@
1
+ {
2
+ "assetVersion": "shiki-3.23.0-c5695a7656dd",
3
+ "counts": {
4
+ "languages": 347,
5
+ "themes": 66
6
+ },
7
+ "manifest": {
8
+ "bytes": 172640,
9
+ "module": "manifest.json",
10
+ "sha256": "d8ce64a7c4247eac90030e4dd92a9ac00f1de67114e0ac6c5e003e810c8e9cfb"
11
+ },
12
+ "packageVersions": {
13
+ "@shikijs/core": "3.23.0",
14
+ "@shikijs/engine-javascript": "3.23.0",
15
+ "@shikijs/langs": "3.23.0",
16
+ "@shikijs/themes": "3.23.0",
17
+ "shiki": "3.23.0"
18
+ },
19
+ "prefix": "_platform/shiki",
20
+ "runtime": {
21
+ "engine": "javascript-regexp",
22
+ "module": "runtime.mjs"
23
+ }
24
+ }
@@ -4,6 +4,7 @@ import Sidebar from "../components/Sidebar.astro";
4
4
  import { getConfig } from "../lib/validation";
5
5
  import Header from "../components/Header.astro";
6
6
  import Footer from "../components/Footer.astro";
7
+ import NavigationTabs from "../components/NavigationTabs.astro";
7
8
  import AssistantDocsWidget from "../components/chat/AssistantDocsWidget.astro";
8
9
  import { ClientRouter } from "astro:transitions";
9
10
  import { prependBasePath, stripBasePath } from "../lib/base-path";
@@ -18,6 +19,8 @@ interface Props {
18
19
  pageDescription?: string;
19
20
  }
20
21
 
22
+ type TabsPresentation = "topbar" | "sidebar";
23
+
21
24
  function normalizeRoutePath(routePath: string): string {
22
25
  if (!routePath || routePath === "/") return "/";
23
26
  if (!routePath.startsWith("/")) routePath = `/${routePath}`;
@@ -66,6 +69,12 @@ const orgTier =
66
69
  Number.isFinite(parsedOrgTier) && parsedOrgTier > 0 ? parsedOrgTier : 1;
67
70
  const isDev = import.meta.env.DEV;
68
71
  const askAiEnabled = isDev || orgTier >= 3;
72
+ const navigationTabs = config.navigation.tabs as
73
+ | (typeof config.navigation.tabs & { presentation?: TabsPresentation })
74
+ | undefined;
75
+ const hasNavigationTabs =
76
+ Array.isArray(navigationTabs?.items) && navigationTabs.items.length > 0;
77
+ const tabsPresentation = navigationTabs?.presentation ?? "topbar";
69
78
  ---
70
79
 
71
80
  <!doctype html>
@@ -73,7 +82,7 @@ const askAiEnabled = isDev || orgTier >= 3;
73
82
  <head>
74
83
  <style is:inline is:global set:html={neutralPaletteCss}></style>
75
84
  <style is:inline is:global set:html={fontCss}></style>
76
- <ClientRouter />
85
+ <ClientRouter fallback="swap" />
77
86
  <script is:inline>
78
87
  const applyTheme = () => {
79
88
  const modeParam = new URLSearchParams(window.location.search).get(
@@ -192,6 +201,71 @@ const askAiEnabled = isDev || orgTier >= 3;
192
201
  }
193
202
  })();
194
203
  </script>
204
+ <script is:inline>
205
+ (() => {
206
+ const root = document.documentElement;
207
+ let viewportFrameId = null;
208
+
209
+ const syncVisualViewport = () => {
210
+ viewportFrameId = null;
211
+
212
+ const viewport = window.visualViewport;
213
+ const width =
214
+ viewport && Number.isFinite(viewport.width)
215
+ ? viewport.width
216
+ : window.innerWidth;
217
+ const height =
218
+ viewport && Number.isFinite(viewport.height)
219
+ ? viewport.height
220
+ : window.innerHeight;
221
+ const offsetLeft =
222
+ viewport && Number.isFinite(viewport.offsetLeft)
223
+ ? viewport.offsetLeft
224
+ : 0;
225
+ const offsetTop =
226
+ viewport && Number.isFinite(viewport.offsetTop)
227
+ ? viewport.offsetTop
228
+ : 0;
229
+
230
+ root.style.setProperty(
231
+ "--rd-visual-viewport-left",
232
+ `${Math.max(0, offsetLeft)}px`,
233
+ );
234
+ root.style.setProperty(
235
+ "--rd-visual-viewport-top",
236
+ `${Math.max(0, offsetTop)}px`,
237
+ );
238
+ root.style.setProperty(
239
+ "--rd-visual-viewport-width",
240
+ `${Math.max(1, width)}px`,
241
+ );
242
+ root.style.setProperty(
243
+ "--rd-visual-viewport-height",
244
+ `${Math.max(1, height)}px`,
245
+ );
246
+ };
247
+
248
+ const scheduleVisualViewportSync = () => {
249
+ if (viewportFrameId !== null) return;
250
+ viewportFrameId = window.requestAnimationFrame(syncVisualViewport);
251
+ };
252
+
253
+ scheduleVisualViewportSync();
254
+ window.visualViewport?.addEventListener(
255
+ "resize",
256
+ scheduleVisualViewportSync,
257
+ );
258
+ window.visualViewport?.addEventListener(
259
+ "scroll",
260
+ scheduleVisualViewportSync,
261
+ );
262
+ window.addEventListener("resize", scheduleVisualViewportSync);
263
+ window.addEventListener(
264
+ "orientationchange",
265
+ scheduleVisualViewportSync,
266
+ );
267
+ })();
268
+ </script>
195
269
  {
196
270
  fontPreloads.map((font) => (
197
271
  <link
@@ -233,12 +307,10 @@ const askAiEnabled = isDev || orgTier >= 3;
233
307
  <body
234
308
  class="bg-background text-neutral-900 dark:text-white"
235
309
  x-data="{ open: false }"
236
- x-bind:class="open ? 'overflow-hidden touch-none' : ''"
310
+ x-bind:class="open ? 'overflow-hidden' : ''"
237
311
  >
238
312
  <!-- Edges -->
239
- <div
240
- class="fixed top-1 inset-x-1 h-16 -z-10 bg-background-dark"
241
- >
313
+ <div class="fixed top-1 inset-x-1 h-16 -z-10 bg-background-dark">
242
314
  <div class="bg-background w-full h-full rounded-t-2xl"></div>
243
315
  </div>
244
316
  <div
@@ -252,10 +324,21 @@ const askAiEnabled = isDev || orgTier >= 3;
252
324
 
253
325
  <!-- Header -->
254
326
  <Header showAskAiTrigger={askAiEnabled} />
327
+ {
328
+ hasNavigationTabs ? (
329
+ <NavigationTabs
330
+ tabs={navigationTabs!}
331
+ presentation={tabsPresentation}
332
+ />
333
+ ) : null
334
+ }
255
335
 
256
336
  <!-- Desktop Sidebar -->
257
337
  <div
258
- class="bg-background w-[283px] ml-[5px] fixed inset-y-0 mt-17 hidden lg:block border-r border-r-border-light"
338
+ class:list={[
339
+ "bg-background w-[283px] ml-[5px] fixed inset-y-0 hidden lg:block border-r border-r-border-light",
340
+ hasNavigationTabs ? "mt-[112px]" : "mt-17",
341
+ ]}
259
342
  data-pagefind-ignore
260
343
  >
261
344
  <Sidebar />
@@ -265,7 +348,7 @@ const askAiEnabled = isDev || orgTier >= 3;
265
348
  <div
266
349
  x-show="open"
267
350
  x-cloak
268
- class="bg-background mx-[5px] min-h-[calc(100vh-68px)] mt-17 fixed inset-0 lg:hidden z-50 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
351
+ class="rd-mobile-menu bg-background fixed lg:hidden z-50 overflow-y-auto overscroll-contain [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
269
352
  x-transition.opacity
270
353
  >
271
354
  <Sidebar askAiEnabled={askAiEnabled} />
@@ -275,11 +358,31 @@ const askAiEnabled = isDev || orgTier >= 3;
275
358
  <div
276
359
  class="mx-1 mt-1 px-4 sm:px-6 lg:pl-[calc(288px+32px)] pt-16 lg:pr-8 bg-background"
277
360
  >
278
- <main class="mx-auto pt-16 pb-16 min-h-[calc(100vh-64px)]">
361
+ <main class="mx-auto pt-16 pb-20 min-h-[calc(100vh-64px)]">
279
362
  <slot />
280
363
  </main>
281
364
  <Footer askAiEnabled={askAiEnabled} />
282
365
  </div>
283
366
  {askAiEnabled ? <AssistantDocsWidget /> : null}
367
+ <style is:global>
368
+ :root {
369
+ --rd-visual-viewport-left: 0px;
370
+ --rd-visual-viewport-top: 0px;
371
+ --rd-visual-viewport-width: 100vw;
372
+ --rd-visual-viewport-height: 100dvh;
373
+ }
374
+
375
+ .rd-mobile-menu {
376
+ --rd-mobile-menu-top-offset: 68px;
377
+ left: calc(var(--rd-visual-viewport-left) + 5px);
378
+ top: calc(
379
+ var(--rd-visual-viewport-top) + var(--rd-mobile-menu-top-offset)
380
+ );
381
+ width: calc(var(--rd-visual-viewport-width) - 10px);
382
+ height: calc(
383
+ var(--rd-visual-viewport-height) - var(--rd-mobile-menu-top-offset)
384
+ );
385
+ }
386
+ </style>
284
387
  </body>
285
388
  </html>
@@ -4,7 +4,13 @@ import {
4
4
  } from "./assistant-chrome";
5
5
  import { withBasePath } from "./base-path";
6
6
  import { getAssistantLauncherIconConfig } from "./assistant-embed-script";
7
+ import type { AssistantShikiRuntimeConfig } from "./assistant-shiki-client";
7
8
  import type { DocsConfig } from "./validation";
9
+ import shikiPlatformAssets from "../generated/shiki-platform-assets.json";
10
+ import {
11
+ DEFAULT_SHIKI_DARK_THEME,
12
+ DEFAULT_SHIKI_LIGHT_THEME,
13
+ } from "radiant-docs-validator/shiki-theme-config";
8
14
 
9
15
  export type AssistantPanelRuntimeConfig = {
10
16
  apiPath: string;
@@ -26,8 +32,60 @@ export type AssistantPanelRuntimeConfig = {
26
32
  emptyStateQuestions?: string[];
27
33
  devProxyToken?: string;
28
34
  chrome: AssistantChromeConfig;
35
+ shiki?: AssistantShikiRuntimeConfig;
29
36
  };
30
37
 
38
+ function normalizeStaticAssetHost(value: unknown): string {
39
+ const rawValue = typeof value === "string" ? value.trim() : "";
40
+ if (!rawValue) return "";
41
+
42
+ const normalizedHost = rawValue.replace(/\/+$/, "");
43
+ return /^https?:\/\//i.test(normalizedHost)
44
+ ? normalizedHost
45
+ : `https://${normalizedHost}`;
46
+ }
47
+
48
+ function joinUrl(baseUrl: string, path: string): string {
49
+ return `${baseUrl.replace(/\/+$/, "")}/${path.replace(/^\/+/, "")}`;
50
+ }
51
+
52
+ function getConfiguredCodeSyntaxThemes(
53
+ config: DocsConfig,
54
+ ): AssistantShikiRuntimeConfig["syntaxThemes"] {
55
+ const configuredSyntaxTheme = config.theme?.code?.syntaxTheme;
56
+
57
+ if (typeof configuredSyntaxTheme === "string") {
58
+ return {
59
+ light: configuredSyntaxTheme,
60
+ dark: configuredSyntaxTheme,
61
+ };
62
+ }
63
+
64
+ return {
65
+ light: configuredSyntaxTheme?.light ?? DEFAULT_SHIKI_LIGHT_THEME,
66
+ dark: configuredSyntaxTheme?.dark ?? DEFAULT_SHIKI_DARK_THEME,
67
+ };
68
+ }
69
+
70
+ function getAssistantShikiRuntimeConfig(
71
+ config: DocsConfig,
72
+ ): AssistantShikiRuntimeConfig | undefined {
73
+ const staticAssetHost = normalizeStaticAssetHost(
74
+ import.meta.env.STATIC_ASSET_HOST ?? process.env.STATIC_ASSET_HOST,
75
+ );
76
+ if (!staticAssetHost) {
77
+ return undefined;
78
+ }
79
+
80
+ return {
81
+ assetBaseUrl: joinUrl(
82
+ staticAssetHost,
83
+ `${shikiPlatformAssets.prefix}/${shikiPlatformAssets.assetVersion}`,
84
+ ),
85
+ syntaxThemes: getConfiguredCodeSyntaxThemes(config),
86
+ };
87
+ }
88
+
31
89
  export function getAssistantPanelRuntimeConfig(
32
90
  config: DocsConfig,
33
91
  ): AssistantPanelRuntimeConfig {
@@ -76,5 +134,6 @@ export function getAssistantPanelRuntimeConfig(
76
134
  emptyStateQuestions: assistantConfig?.questions,
77
135
  devProxyToken: isDev ? assistantDevProxySecret : undefined,
78
136
  chrome: getAssistantChromeConfig(config),
137
+ shiki: getAssistantShikiRuntimeConfig(config),
79
138
  };
80
139
  }