radiant-docs 0.1.34 → 0.1.38

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 (56) hide show
  1. package/package.json +1 -1
  2. package/template/astro.config.mjs +27 -0
  3. package/template/package-lock.json +1027 -513
  4. package/template/package.json +3 -2
  5. package/template/scripts/generate-proxy-allowed-origins.mjs +217 -0
  6. package/template/scripts/generate-robots-txt.mjs +19 -0
  7. package/template/scripts/stamp-image-versions.mjs +63 -11
  8. package/template/src/components/Footer.astro +1 -1
  9. package/template/src/components/Header.astro +9 -9
  10. package/template/src/components/LogoLink.astro +2 -1
  11. package/template/src/components/OpenApiPage.astro +18 -18
  12. package/template/src/components/Search.astro +18 -18
  13. package/template/src/components/Sidebar.astro +4 -2
  14. package/template/src/components/SidebarDropdown.astro +82 -79
  15. package/template/src/components/SidebarGroup.astro +3 -0
  16. package/template/src/components/SidebarMenu.astro +14 -1
  17. package/template/src/components/SidebarSegmented.astro +5 -5
  18. package/template/src/components/SidebarSubgroup.astro +35 -12
  19. package/template/src/components/TableOfContents.astro +24 -15
  20. package/template/src/components/ThemeSwitcher.astro +15 -8
  21. package/template/src/components/chat/AskAiWidget.tsx +10 -5
  22. package/template/src/components/endpoint/PlaygroundBar.astro +3 -3
  23. package/template/src/components/endpoint/PlaygroundButton.astro +3 -3
  24. package/template/src/components/endpoint/PlaygroundField.astro +53 -53
  25. package/template/src/components/endpoint/PlaygroundForm.astro +51 -37
  26. package/template/src/components/endpoint/RequestSnippets.astro +54 -21
  27. package/template/src/components/endpoint/ResponseDisplay.astro +24 -24
  28. package/template/src/components/endpoint/ResponseFieldTree.astro +12 -12
  29. package/template/src/components/endpoint/ResponseFields.astro +19 -19
  30. package/template/src/components/endpoint/ResponseSnippets.astro +66 -29
  31. package/template/src/components/sidebar/SidebarEndpointLink.astro +18 -15
  32. package/template/src/components/sidebar/SidebarOpenApiPageLink.astro +56 -0
  33. package/template/src/components/ui/CodeTabEdge.astro +6 -4
  34. package/template/src/components/ui/Field.astro +7 -7
  35. package/template/src/components/ui/Icon.astro +2 -1
  36. package/template/src/components/ui/demo/Demo.astro +1 -1
  37. package/template/src/components/user/Accordion.astro +3 -3
  38. package/template/src/components/user/Callout.astro +8 -8
  39. package/template/src/components/user/CodeBlock.astro +57 -22
  40. package/template/src/components/user/CodeGroup.astro +14 -10
  41. package/template/src/components/user/ComponentPreviewBlock.astro +38 -12
  42. package/template/src/components/user/Image.astro +6 -2
  43. package/template/src/components/user/Step.astro +4 -4
  44. package/template/src/components/user/Tab.astro +1 -1
  45. package/template/src/components/user/Tabs.astro +15 -20
  46. package/template/src/layouts/Layout.astro +9 -4
  47. package/template/src/lib/code/code-block.ts +150 -15
  48. package/template/src/lib/mdx/remark-resolve-internal-links.ts +639 -0
  49. package/template/src/lib/pagefind.ts +2 -1
  50. package/template/src/lib/routes.ts +134 -58
  51. package/template/src/lib/static-asset-url.ts +62 -0
  52. package/template/src/lib/utils.ts +48 -0
  53. package/template/src/lib/validation.ts +115 -27
  54. package/template/src/pages/404.astro +44 -0
  55. package/template/src/styles/global.css +28 -19
  56. package/template/scripts/rewrite-static-asset-host.mjs +0 -408
@@ -137,7 +137,9 @@ const CODE_BLOCK_LANGUAGE_ICON_FILE_BY_VALUE: Record<string, string> = {
137
137
  yaml: "file_type_yaml_official.svg",
138
138
  };
139
139
 
140
- const SHIKI_THEME = "github-light";
140
+ const SHIKI_LIGHT_THEME = "github-light";
141
+ const SHIKI_DARK_THEME = "github-dark";
142
+ const SHIKI_THEMES = [SHIKI_LIGHT_THEME, SHIKI_DARK_THEME] as const;
141
143
  const BUNDLED_LANGUAGE_SET = new Set(Object.keys(bundledLanguages));
142
144
  const LANGUAGE_RUNTIME_DEPENDENCIES: Record<string, string[]> = {
143
145
  // MDX tokenization relies on TSX grammar injections for JSX-style tags.
@@ -228,7 +230,7 @@ function pickIconFromFileName(fileName: string): string | null {
228
230
  const candidates = uniqueValues([trimmedFileName, trimmedFileName.toLowerCase()]);
229
231
  for (const candidate of candidates) {
230
232
  const iconFileName = getIconForFile(candidate);
231
- if (iconFileName !== DEFAULT_FILE) return iconFileName;
233
+ if (iconFileName && iconFileName !== DEFAULT_FILE) return iconFileName;
232
234
  }
233
235
 
234
236
  return null;
@@ -372,7 +374,7 @@ function namespaceSvgIds(svg: string, namespace: string): string {
372
374
  async function getHighlighter() {
373
375
  if (!highlighterPromise) {
374
376
  highlighterPromise = getSingletonHighlighter({
375
- themes: [SHIKI_THEME],
377
+ themes: [...SHIKI_THEMES],
376
378
  langs: [DEFAULT_CODE_BLOCK_LANGUAGE],
377
379
  });
378
380
  }
@@ -414,7 +416,7 @@ async function ensureLanguageLoaded(
414
416
  }
415
417
 
416
418
  const loadPromise = highlighter
417
- .loadLanguage(language)
419
+ .loadLanguage(language as keyof typeof bundledLanguages)
418
420
  .then(() => {
419
421
  loadedLanguageSet.add(language);
420
422
  })
@@ -493,7 +495,7 @@ export async function getCodeLineTokens({
493
495
  language: string;
494
496
  }): Promise<{
495
497
  normalizedLanguage: string;
496
- lines: ThemedToken[][];
498
+ lines: CodeLineToken[][];
497
499
  }> {
498
500
  const highlighter = await getHighlighter();
499
501
  const normalizedLanguage = normalizeCodeLanguageValue(language);
@@ -523,24 +525,157 @@ export async function getCodeLineTokens({
523
525
  }
524
526
 
525
527
  try {
526
- const tokenResult = highlighter.codeToTokens(code, {
527
- lang: targetLanguage,
528
- theme: SHIKI_THEME,
529
- });
528
+ const themedTokenLines = getThemedTokenLines(highlighter, code, targetLanguage);
530
529
 
531
530
  return {
532
531
  normalizedLanguage: targetLanguage,
533
- lines: tokenResult.tokens,
532
+ lines: mergeTokenLines(themedTokenLines.light, themedTokenLines.dark),
534
533
  };
535
534
  } catch {
536
- const tokenResult = highlighter.codeToTokens(code, {
537
- lang: DEFAULT_CODE_BLOCK_LANGUAGE,
538
- theme: SHIKI_THEME,
539
- });
535
+ const themedTokenLines = getThemedTokenLines(
536
+ highlighter,
537
+ code,
538
+ DEFAULT_CODE_BLOCK_LANGUAGE,
539
+ );
540
540
 
541
541
  return {
542
542
  normalizedLanguage: DEFAULT_CODE_BLOCK_LANGUAGE,
543
- lines: tokenResult.tokens,
543
+ lines: mergeTokenLines(themedTokenLines.light, themedTokenLines.dark),
544
544
  };
545
545
  }
546
546
  }
547
+
548
+ export type CodeLineToken = {
549
+ content: string;
550
+ color?: string;
551
+ darkColor?: string;
552
+ bgColor?: string;
553
+ darkBgColor?: string;
554
+ fontStyle?: number;
555
+ htmlStyle?: Record<string, string>;
556
+ darkHtmlStyle?: Record<string, string>;
557
+ };
558
+
559
+ function getThemedTokenLines(
560
+ highlighter: Awaited<ReturnType<typeof getSingletonHighlighter>>,
561
+ code: string,
562
+ lang: string,
563
+ ): {
564
+ light: ThemedToken[][];
565
+ dark: ThemedToken[][];
566
+ } {
567
+ const shikiLanguage = lang as keyof typeof bundledLanguages;
568
+ const lightTokenResult = highlighter.codeToTokens(code, {
569
+ lang: shikiLanguage,
570
+ theme: SHIKI_LIGHT_THEME,
571
+ });
572
+ const darkTokenResult = highlighter.codeToTokens(code, {
573
+ lang: shikiLanguage,
574
+ theme: SHIKI_DARK_THEME,
575
+ });
576
+
577
+ return {
578
+ light: lightTokenResult.tokens,
579
+ dark: darkTokenResult.tokens,
580
+ };
581
+ }
582
+
583
+ function buildLightOnlyTokenLine(lightLine: ThemedToken[]): CodeLineToken[] {
584
+ return lightLine.map((token) => ({
585
+ content: token.content,
586
+ color: token.color,
587
+ bgColor: token.bgColor,
588
+ fontStyle: token.fontStyle,
589
+ htmlStyle: token.htmlStyle,
590
+ }));
591
+ }
592
+
593
+ function mergeTokenLineByContent(
594
+ lightLine: ThemedToken[],
595
+ darkLine: ThemedToken[],
596
+ ): CodeLineToken[] | null {
597
+ const lightContent = lightLine.map((token) => token.content).join("");
598
+ const darkContent = darkLine.map((token) => token.content).join("");
599
+
600
+ // When themes tokenize the same text with different boundaries (common for markdown),
601
+ // align on content segments so we can still apply dark token colors reliably.
602
+ if (lightContent !== darkContent) return null;
603
+ if (lightContent.length === 0) return [];
604
+
605
+ const mergedLine: CodeLineToken[] = [];
606
+ let lightTokenIndex = 0;
607
+ let darkTokenIndex = 0;
608
+ let lightTokenOffset = 0;
609
+ let darkTokenOffset = 0;
610
+
611
+ while (
612
+ lightTokenIndex < lightLine.length &&
613
+ darkTokenIndex < darkLine.length
614
+ ) {
615
+ const lightToken = lightLine[lightTokenIndex];
616
+ const darkToken = darkLine[darkTokenIndex];
617
+
618
+ if (lightTokenOffset >= lightToken.content.length) {
619
+ lightTokenIndex += 1;
620
+ lightTokenOffset = 0;
621
+ continue;
622
+ }
623
+
624
+ if (darkTokenOffset >= darkToken.content.length) {
625
+ darkTokenIndex += 1;
626
+ darkTokenOffset = 0;
627
+ continue;
628
+ }
629
+
630
+ const lightRemainingLength = lightToken.content.length - lightTokenOffset;
631
+ const darkRemainingLength = darkToken.content.length - darkTokenOffset;
632
+ const segmentLength = Math.min(lightRemainingLength, darkRemainingLength);
633
+ if (segmentLength <= 0) break;
634
+
635
+ const lightSegment = lightToken.content.slice(
636
+ lightTokenOffset,
637
+ lightTokenOffset + segmentLength,
638
+ );
639
+ const darkSegment = darkToken.content.slice(
640
+ darkTokenOffset,
641
+ darkTokenOffset + segmentLength,
642
+ );
643
+
644
+ if (lightSegment !== darkSegment) return null;
645
+
646
+ mergedLine.push({
647
+ content: lightSegment,
648
+ color: lightToken.color,
649
+ darkColor: darkToken.color,
650
+ bgColor: lightToken.bgColor,
651
+ darkBgColor: darkToken.bgColor,
652
+ fontStyle: lightToken.fontStyle,
653
+ htmlStyle: lightToken.htmlStyle,
654
+ darkHtmlStyle: darkToken.htmlStyle,
655
+ });
656
+
657
+ lightTokenOffset += segmentLength;
658
+ darkTokenOffset += segmentLength;
659
+ }
660
+
661
+ const mergedContent = mergedLine.map((token) => token.content).join("");
662
+ return mergedContent === lightContent ? mergedLine : null;
663
+ }
664
+
665
+ function mergeTokenLines(
666
+ lightTokenLines: ThemedToken[][],
667
+ darkTokenLines: ThemedToken[][],
668
+ ): CodeLineToken[][] {
669
+ const lineCount = Math.max(lightTokenLines.length, darkTokenLines.length);
670
+ const mergedLines: CodeLineToken[][] = [];
671
+
672
+ for (let lineIndex = 0; lineIndex < lineCount; lineIndex += 1) {
673
+ const lightLine = lightTokenLines[lineIndex] ?? [];
674
+ const darkLine = darkTokenLines[lineIndex] ?? [];
675
+
676
+ const mergedLine = mergeTokenLineByContent(lightLine, darkLine);
677
+ mergedLines.push(mergedLine ?? buildLightOnlyTokenLine(lightLine));
678
+ }
679
+
680
+ return mergedLines;
681
+ }