radiant-docs 0.1.7 → 0.1.9

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 (78) hide show
  1. package/dist/index.js +28 -5
  2. package/package.json +5 -4
  3. package/template/astro.config.mjs +76 -3
  4. package/template/package-lock.json +924 -737
  5. package/template/package.json +7 -5
  6. package/template/scripts/generate-og-images.mjs +335 -0
  7. package/template/scripts/generate-og-metadata.mjs +173 -0
  8. package/template/scripts/rewrite-static-asset-host.mjs +408 -0
  9. package/template/scripts/stamp-image-versions.mjs +277 -0
  10. package/template/scripts/stamp-og-image-versions.mjs +199 -0
  11. package/template/scripts/stamp-pagefind-runtime-version.mjs +140 -0
  12. package/template/src/assets/fonts/geist-mono/cyrillic.woff2 +0 -0
  13. package/template/src/assets/fonts/geist-mono/latin-ext.woff2 +0 -0
  14. package/template/src/assets/fonts/geist-mono/latin.woff2 +0 -0
  15. package/template/src/assets/fonts/google-sans-flex/canadian-aboriginal.woff2 +0 -0
  16. package/template/src/assets/fonts/google-sans-flex/cherokee.woff2 +0 -0
  17. package/template/src/assets/fonts/google-sans-flex/latin-ext.woff2 +0 -0
  18. package/template/src/assets/fonts/google-sans-flex/latin.woff2 +0 -0
  19. package/template/src/assets/fonts/google-sans-flex/math.woff2 +0 -0
  20. package/template/src/assets/fonts/google-sans-flex/nushu.woff2 +0 -0
  21. package/template/src/assets/fonts/google-sans-flex/symbols.woff2 +0 -0
  22. package/template/src/assets/fonts/google-sans-flex/syriac.woff2 +0 -0
  23. package/template/src/assets/fonts/google-sans-flex/tifinagh.woff2 +0 -0
  24. package/template/src/assets/fonts/google-sans-flex/vietnamese.woff2 +0 -0
  25. package/template/src/components/Footer.astro +94 -0
  26. package/template/src/components/Header.astro +11 -66
  27. package/template/src/components/LogoLink.astro +103 -0
  28. package/template/src/components/MdxPage.astro +126 -11
  29. package/template/src/components/OpenApiPage.astro +1036 -69
  30. package/template/src/components/Search.astro +0 -2
  31. package/template/src/components/SidebarDropdown.astro +34 -14
  32. package/template/src/components/SidebarGroup.astro +3 -6
  33. package/template/src/components/SidebarLink.astro +22 -12
  34. package/template/src/components/SidebarMenu.astro +19 -16
  35. package/template/src/components/SidebarSegmented.astro +99 -0
  36. package/template/src/components/SidebarSubgroup.astro +12 -12
  37. package/template/src/components/ThemeSwitcher.astro +30 -7
  38. package/template/src/components/endpoint/PlaygroundBar.astro +32 -36
  39. package/template/src/components/endpoint/PlaygroundButton.astro +40 -4
  40. package/template/src/components/endpoint/PlaygroundField.astro +1068 -22
  41. package/template/src/components/endpoint/PlaygroundForm.astro +559 -61
  42. package/template/src/components/endpoint/RequestSnippets.astro +342 -193
  43. package/template/src/components/endpoint/ResponseDisplay.astro +161 -147
  44. package/template/src/components/endpoint/ResponseFieldTree.astro +134 -0
  45. package/template/src/components/endpoint/ResponseFields.astro +711 -68
  46. package/template/src/components/endpoint/ResponseSnippets.astro +299 -173
  47. package/template/src/components/sidebar/SidebarEndpointLink.astro +1 -1
  48. package/template/src/components/ui/CodeLanguageIcon.astro +19 -0
  49. package/template/src/components/ui/CodeTabEdge.astro +79 -0
  50. package/template/src/components/ui/Field.astro +103 -20
  51. package/template/src/components/ui/Icon.astro +32 -0
  52. package/template/src/components/ui/ListChevronsToggle.astro +31 -0
  53. package/template/src/components/ui/Tag.astro +1 -1
  54. package/template/src/components/user/{Accordian.astro → Accordion.astro} +6 -6
  55. package/template/src/components/user/Callout.astro +5 -9
  56. package/template/src/components/user/CodeBlock.astro +400 -0
  57. package/template/src/components/user/CodeGroup.astro +225 -0
  58. package/template/src/components/user/ComponentPreview.astro +1 -0
  59. package/template/src/components/user/ComponentPreviewBlock.astro +181 -0
  60. package/template/src/components/user/Image.astro +132 -0
  61. package/template/src/components/user/Steps.astro +1 -3
  62. package/template/src/components/user/Tabs.astro +2 -2
  63. package/template/src/content.config.ts +1 -0
  64. package/template/src/layouts/Layout.astro +109 -8
  65. package/template/src/lib/code/code-block.ts +546 -0
  66. package/template/src/lib/frontmatter-schema.ts +8 -7
  67. package/template/src/lib/mdx/remark-code-block-component.ts +342 -0
  68. package/template/src/lib/mdx/remark-demote-h1.ts +16 -0
  69. package/template/src/lib/pagefind.ts +19 -5
  70. package/template/src/lib/routes.ts +49 -31
  71. package/template/src/lib/utils.ts +20 -0
  72. package/template/src/lib/validation.ts +638 -200
  73. package/template/src/pages/[...slug].astro +18 -5
  74. package/template/src/styles/geist-mono.css +33 -0
  75. package/template/src/styles/global.css +89 -84
  76. package/template/src/styles/google-sans-flex.css +143 -0
  77. package/template/ec.config.mjs +0 -51
  78. /package/template/src/components/user/{AccordianGroup.astro → AccordionGroup.astro} +0 -0
@@ -0,0 +1,181 @@
1
+ ---
2
+ import CodeBlock from "./CodeBlock.astro";
3
+
4
+ interface Props {
5
+ language?: string;
6
+ raw?: string;
7
+ filename?: string;
8
+ showFilename?: boolean | string;
9
+ showLineNumbers?: boolean | string;
10
+ hideLanguageIcon?: boolean | string;
11
+ highlightedLines?: string;
12
+ collapsedLines?: string;
13
+ previewVisibleLines?: number | string;
14
+ }
15
+
16
+ const {
17
+ language = "jsx",
18
+ raw = "",
19
+ filename = "",
20
+ showFilename = false,
21
+ showLineNumbers = false,
22
+ hideLanguageIcon = false,
23
+ highlightedLines = "",
24
+ collapsedLines = "",
25
+ previewVisibleLines = 5,
26
+ } = Astro.props as Props;
27
+
28
+ function parsePositiveInteger(
29
+ value: number | string | undefined,
30
+ fallback: number,
31
+ ): number {
32
+ if (typeof value === "number" && Number.isFinite(value) && value > 0) {
33
+ return Math.floor(value);
34
+ }
35
+ if (typeof value === "string") {
36
+ const parsed = Number.parseInt(value, 10);
37
+ if (Number.isFinite(parsed) && parsed > 0) {
38
+ return parsed;
39
+ }
40
+ }
41
+ return fallback;
42
+ }
43
+
44
+ const visibleLines = parsePositiveInteger(previewVisibleLines, 5);
45
+ const totalLineCount = Math.max(1, raw.split("\n").length);
46
+ ---
47
+
48
+ <div
49
+ class="rd-component-preview my-6 flex w-full max-w-full min-w-0 flex-col"
50
+ data-rd-component-preview-root="true"
51
+ >
52
+ <div
53
+ class="w-full max-w-full min-w-0 overflow-x-auto rounded-t-xl border border-b-0 border-neutral-200 bg-white p-4 xs:p-6 sm:p-12 shadow-xs [&>:first-child]:mt-0! [&>:last-child]:mb-0!"
54
+ >
55
+ <slot />
56
+ </div>
57
+ <div
58
+ class="rd-component-preview__code not-prose relative"
59
+ data-rd-preview-expanded="false"
60
+ style={{ "--rd-preview-visible-lines": String(visibleLines) }}
61
+ >
62
+ <CodeBlock
63
+ language={language}
64
+ raw={raw}
65
+ filename={filename}
66
+ showFilename={showFilename}
67
+ showLineNumbers={showLineNumbers}
68
+ hideLanguageIcon={hideLanguageIcon}
69
+ highlightedLines={highlightedLines}
70
+ collapsedLines={collapsedLines}
71
+ />
72
+ <div
73
+ class="rd-component-preview__overlay pointer-events-none absolute inset-x-px inset-y-px hidden items-end justify-center rounded-b-xl bg-linear-to-t from-neutral-50/90 to-neutral-50/30 pb-4"
74
+ data-rd-preview-overlay
75
+ aria-hidden="true"
76
+ >
77
+ <button
78
+ type="button"
79
+ class="pointer-events-auto inline-flex h-8 items-center justify-center rounded-md border border-neutral-200 bg-white px-3 text-sm font-medium text-neutral-700 shadow-md shadow-neutral-200 transition-colors duration-150 hover:bg-neutral-50 cursor-pointer"
80
+ data-rd-preview-expand
81
+ >
82
+ View code
83
+ </button>
84
+ </div>
85
+ </div>
86
+ </div>
87
+
88
+ <style>
89
+ .rd-component-preview :global(.group\/prose-code) {
90
+ margin-top: 0 !important;
91
+ margin-bottom: 0 !important;
92
+ }
93
+
94
+ .rd-component-preview :global(.group\/prose-code > div) {
95
+ background-color: var(--color-neutral-50) !important;
96
+ border-top-left-radius: 0 !important;
97
+ border-top-right-radius: 0 !important;
98
+ }
99
+
100
+ .rd-component-preview :global(.group\/prose-code pre),
101
+ .rd-component-preview :global(.group\/prose-code code) {
102
+ background-color: var(--color-neutral-50) !important;
103
+ }
104
+
105
+ .rd-component-preview__code[data-rd-preview-expanded="false"]
106
+ .rd-component-preview__overlay {
107
+ display: flex;
108
+ }
109
+
110
+ .rd-component-preview__code :global([data-rd-code-scroll-area]) {
111
+ overflow-y: hidden;
112
+ transition: max-height 280ms cubic-bezier(0.22, 1, 0.36, 1);
113
+ }
114
+
115
+ .rd-component-preview__code[data-rd-preview-expanded="false"]
116
+ :global([data-rd-code-scroll-area]) {
117
+ max-height: calc(var(--rd-preview-visible-lines, 5) * 1.5rem + 1.25rem);
118
+ }
119
+
120
+ .rd-component-preview__code[data-rd-preview-expanded="true"]
121
+ :global([data-rd-code-scroll-area]) {
122
+ max-height: var(--rd-preview-expanded-height, 200rem);
123
+ }
124
+ </style>
125
+
126
+ <script is:inline define:vars={{ visibleLines, totalLineCount }}>
127
+ (() => {
128
+ const script = document.currentScript;
129
+ if (!(script instanceof HTMLScriptElement)) return;
130
+
131
+ let root = script.previousElementSibling;
132
+ while (
133
+ root &&
134
+ (!(root instanceof HTMLElement) ||
135
+ !root.hasAttribute("data-rd-component-preview-root"))
136
+ ) {
137
+ root = root.previousElementSibling;
138
+ }
139
+ if (!(root instanceof HTMLElement)) return;
140
+
141
+ const codeWrapper = root.querySelector(".rd-component-preview__code");
142
+ if (!(codeWrapper instanceof HTMLElement)) return;
143
+
144
+ const overlay = codeWrapper.querySelector("[data-rd-preview-overlay]");
145
+ const expandButton = codeWrapper.querySelector("[data-rd-preview-expand]");
146
+ const scrollArea = codeWrapper.querySelector("[data-rd-code-scroll-area]");
147
+ if (
148
+ !(overlay instanceof HTMLElement) ||
149
+ !(expandButton instanceof HTMLButtonElement) ||
150
+ !(scrollArea instanceof HTMLElement)
151
+ ) {
152
+ return;
153
+ }
154
+
155
+ const syncExpandedHeight = () => {
156
+ codeWrapper.style.setProperty(
157
+ "--rd-preview-expanded-height",
158
+ `${scrollArea.scrollHeight}px`,
159
+ );
160
+ };
161
+
162
+ syncExpandedHeight();
163
+
164
+ if (totalLineCount <= visibleLines) {
165
+ codeWrapper.dataset.rdPreviewExpanded = "true";
166
+ window.addEventListener("resize", syncExpandedHeight, { passive: true });
167
+ return;
168
+ }
169
+
170
+ const setExpanded = () => {
171
+ syncExpandedHeight();
172
+ codeWrapper.dataset.rdPreviewExpanded = "true";
173
+ };
174
+
175
+ expandButton.addEventListener("click", () => {
176
+ setExpanded();
177
+ });
178
+
179
+ window.addEventListener("resize", syncExpandedHeight, { passive: true });
180
+ })();
181
+ </script>
@@ -0,0 +1,132 @@
1
+ ---
2
+ import type { HTMLAttributes } from "astro/types";
3
+ import { validateProps } from "../../lib/component-error";
4
+
5
+ interface Props extends HTMLAttributes<"img"> {
6
+ src: string;
7
+ }
8
+
9
+ const { title, ...attrs } = Astro.props;
10
+
11
+ validateProps(
12
+ "Image",
13
+ Astro.props as Record<string, any>,
14
+ {
15
+ src: { required: true, type: "string" },
16
+ alt: { type: "string" },
17
+ title: { type: "string" },
18
+ },
19
+ Astro.url.pathname,
20
+ );
21
+ ---
22
+
23
+ <figure
24
+ class="p-1.5 pb-2 my-8 group border border-neutral-200/80 dark:border-neutral-800 shadow-xs bg-neutral-50 dark:bg-neutral-900 rounded-2xl"
25
+ x-data="{
26
+ open: false,
27
+ showZoomed: false,
28
+ style: 'visibility: hidden;',
29
+ fullShadow: '0 25px 50px -12px rgba(0, 0, 0, 0.25)',
30
+ noShadow: '0 0 0 rgba(0, 0, 0, 0)',
31
+ async zoom() {
32
+ // 1. Lock scroll and measure
33
+ document.body.style.overflow = 'hidden';
34
+ const rect = this.$refs.img.getBoundingClientRect();
35
+
36
+ // 2. Prepare the zoomed image (hidden but in DOM)
37
+ this.style = 'opacity: 0; transition: none;';
38
+ this.open = true;
39
+ this.showZoomed = false;
40
+
41
+ await this.$nextTick();
42
+ const zoomed = this.$refs.zoomedImg;
43
+ const zRect = zoomed.getBoundingClientRect();
44
+
45
+ // 3. Calculate positioning
46
+ const scale = rect.width / zRect.width;
47
+ const tx = (rect.left + rect.width/2) - (zRect.left + zRect.width/2);
48
+ const ty = (rect.top + rect.height/2) - (zRect.top + zRect.height/2);
49
+
50
+ // 4. Snap to initial position (still invisible)
51
+ this.style = `transform: translate(${tx}px, ${ty}px) scale(${scale}); box-shadow: ${this.noShadow}; opacity: 0; transition: none;`;
52
+
53
+ // 5. Triple-frame buffer to ensure paint completion
54
+ requestAnimationFrame(() => {
55
+ // Reveal zoomed image exactly over the thumbnail
56
+ this.style = `transform: translate(${tx}px, ${ty}px) scale(${scale}); box-shadow: ${this.noShadow}; opacity: 1; transition: none;`;
57
+
58
+ requestAnimationFrame(() => {
59
+ // Now start the animation and hide the thumbnail simultaneously
60
+ this.showZoomed = true;
61
+ this.style = `transform: translate(0,0) scale(1); box-shadow: ${this.fullShadow}; opacity: 1; transition: transform 450ms cubic-bezier(0.4, 0, 0.2, 1), box-shadow 450ms cubic-bezier(0.4, 0, 0.2, 1);`;
62
+ });
63
+ });
64
+ },
65
+ close() {
66
+ document.body.style.overflow = 'auto';
67
+ const rect = this.$refs.img.getBoundingClientRect();
68
+ const zRect = this.$refs.zoomedImg.getBoundingClientRect();
69
+
70
+ const scale = rect.width / zRect.width;
71
+ const tx = (rect.left + rect.width/2) - (zRect.left + zRect.width/2);
72
+ const ty = (rect.top + rect.height/2) - (zRect.top + zRect.height/2);
73
+
74
+ this.style = `transform: translate(${tx}px, ${ty}px) scale(${scale}); box-shadow: ${this.noShadow}; opacity: 1; transition: transform 400ms cubic-bezier(0.4, 0, 0.2, 1), box-shadow 400ms cubic-bezier(0.4, 0, 0.2, 1);`;
75
+ this.showZoomed = false;
76
+
77
+ setTimeout(() => {
78
+ this.open = false;
79
+ this.style = 'opacity: 0;';
80
+ }, 400);
81
+ }
82
+ }"
83
+ >
84
+ <div
85
+ class="overflow-hidden rounded-xl border border-neutral-200/50 dark:border-neutral-800/50 bg-neutral-100 dark:bg-black/20"
86
+ >
87
+ <img
88
+ {...attrs}
89
+ x-ref="img"
90
+ title={title}
91
+ class="w-full h-auto my-0! block cursor-zoom-in transition-opacity"
92
+ :class="showZoomed ? 'opacity-0 duration-0' : 'opacity-100 duration-300'"
93
+ @click="zoom()"
94
+ />
95
+ </div>
96
+ {
97
+ title && (
98
+ <figcaption class="mt-2 text-center text-sm text-neutral-500 dark:text-neutral-400 font-medium whitespace-pre-line leading-relaxed px-2">
99
+ {title}
100
+ </figcaption>
101
+ )
102
+ }
103
+
104
+ <template x-teleport="body">
105
+ <div
106
+ x-show="open"
107
+ class="fixed bottom-0 top-1 inset-x-1 rounded-t-2xl z-20 flex items-center justify-center pt-20 pb-4 px-4 md:pt-28 md:pb-12 md:px-12 overflow-hidden"
108
+ @keydown.escape.window="close()"
109
+ style="display: none;"
110
+ >
111
+ <div
112
+ x-show="showZoomed"
113
+ x-transition:enter="transition ease-out duration-300"
114
+ x-transition:enter-start="opacity-0"
115
+ x-transition:enter-end="opacity-100"
116
+ x-transition:leave="transition ease-in duration-400"
117
+ x-transition:leave-start="opacity-100"
118
+ x-transition:leave-end="opacity-0"
119
+ class="absolute inset-0 bg-white/90 dark:bg-black/90 backdrop-blur-xl cursor-zoom-out"
120
+ @click="close()"
121
+ >
122
+ </div>
123
+
124
+ <img
125
+ {...attrs}
126
+ x-ref="zoomedImg"
127
+ class="relative z-10 max-w-full max-h-full object-contain rounded-2xl shadow-none will-change-transform pointer-events-none"
128
+ :style="style"
129
+ />
130
+ </div>
131
+ </template>
132
+ </figure>
@@ -1,9 +1,7 @@
1
1
  ---
2
2
  interface Props {}
3
-
4
- const stepsId = `steps-${Math.random().toString(36).substring(2, 9)}`;
5
3
  ---
6
4
 
7
- <div class="my-6 space-y-1.5" id={stepsId}>
5
+ <div class="my-6 space-y-1.5">
8
6
  <slot />
9
7
  </div>
@@ -1,5 +1,5 @@
1
1
  ---
2
- import { Icon } from 'astro-icon/components';
2
+ import Icon from '../ui/Icon.astro';
3
3
 
4
4
  const html = await Astro.slots.render("default");
5
5
 
@@ -92,7 +92,7 @@ class="my-5">
92
92
  'text-neutral-500 hover:text-neutral-600': activeTab !== ${index}
93
93
  }`}
94
94
  >
95
- {icons[index] && <Icon name={`lucide:${icons[index]}`} class="size-4 shrink-0" />}
95
+ {icons[index] && <Icon name={icons[index]} class="size-4 shrink-0" />}
96
96
  {label}
97
97
  </button>
98
98
  </li>
@@ -6,6 +6,7 @@ const docs = defineCollection({
6
6
  // Load Markdown and MDX files from src/content/docs
7
7
  // This pattern excludes non-content files like docs.json and images
8
8
  loader: glob({ pattern: "**/*.{md,mdx}", base: "./src/content/docs" }),
9
+ schema: docsSchema,
9
10
  });
10
11
 
11
12
  export const collections = { docs };
@@ -1,10 +1,56 @@
1
1
  ---
2
2
  import "../styles/global.css";
3
+ import "../styles/google-sans-flex.css";
4
+ import "../styles/geist-mono.css";
5
+ import googleSansLatinWoff2 from "../assets/fonts/google-sans-flex/latin.woff2?url";
6
+ import googleSansLatinExtWoff2 from "../assets/fonts/google-sans-flex/latin-ext.woff2?url";
7
+ import geistMonoLatinWoff2 from "../assets/fonts/geist-mono/latin.woff2?url";
8
+ import geistMonoLatinExtWoff2 from "../assets/fonts/geist-mono/latin-ext.woff2?url";
3
9
  import Sidebar from "../components/Sidebar.astro";
4
10
  import { getConfig } from "../lib/validation";
5
11
  import Header from "../components/Header.astro";
12
+ import Footer from "../components/Footer.astro";
6
13
 
14
+ interface Props {
15
+ pageTitle?: string;
16
+ pageDescription?: string;
17
+ }
18
+
19
+ function normalizeRoutePath(routePath: string): string {
20
+ if (!routePath || routePath === "/") return "/";
21
+ if (!routePath.startsWith("/")) routePath = `/${routePath}`;
22
+ return routePath.endsWith("/") ? routePath : `${routePath}/`;
23
+ }
24
+
25
+ function routePathToOgImagePath(routePath: string): string {
26
+ const normalizedRoutePath = normalizeRoutePath(routePath);
27
+ if (normalizedRoutePath === "/") {
28
+ return "/_og/images/index.png";
29
+ }
30
+
31
+ return `/_og/images/${normalizedRoutePath.slice(1, -1)}.png`;
32
+ }
33
+
34
+ const { pageTitle, pageDescription } = Astro.props as Props;
7
35
  const config = await getConfig();
36
+ const resolvedPageTitle = pageTitle?.trim();
37
+ const resolvedPageDescription =
38
+ typeof pageDescription === "string" && pageDescription.trim().length > 0
39
+ ? pageDescription.trim()
40
+ : undefined;
41
+ const fallbackDescription = resolvedPageTitle
42
+ ? `Learn about ${resolvedPageTitle} in the ${config.title} documentation.`
43
+ : `${config.title} documentation.`;
44
+ const resolvedMetaDescription = resolvedPageDescription ?? fallbackDescription;
45
+ const documentTitle = resolvedPageTitle
46
+ ? `${resolvedPageTitle} | ${config.title}`
47
+ : `${config.title} Docs`;
48
+ const canonicalUrl = new URL(Astro.url.pathname, Astro.site ?? Astro.url).toString();
49
+ const ogImageUrl = new URL(
50
+ routePathToOgImagePath(Astro.url.pathname),
51
+ Astro.site ?? Astro.url,
52
+ );
53
+ const ogImageHref = ogImageUrl.toString();
8
54
  ---
9
55
 
10
56
  <!doctype html>
@@ -12,30 +58,82 @@ const config = await getConfig();
12
58
  <head>
13
59
  <script is:inline>
14
60
  const applyTheme = () => {
61
+ const modeParam = new URLSearchParams(window.location.search).get(
62
+ "mode",
63
+ );
64
+ const forcedTheme =
65
+ modeParam === "light" || modeParam === "dark" ? modeParam : null;
15
66
  const localStorageTheme = localStorage.getItem("theme");
16
67
  const systemTheme = window.matchMedia("(prefers-color-scheme: dark)")
17
68
  .matches
18
69
  ? "dark"
19
70
  : "light";
20
- const resolvedTheme =
21
- localStorageTheme === "system" || !localStorageTheme
71
+ const resolvedTheme = forcedTheme
72
+ ? forcedTheme
73
+ : localStorageTheme === "system" || !localStorageTheme
22
74
  ? systemTheme
23
75
  : localStorageTheme;
24
76
 
25
77
  document.documentElement.classList.toggle(
26
78
  "dark",
27
- resolvedTheme === "dark"
79
+ resolvedTheme === "dark",
28
80
  );
81
+ document.documentElement.dataset.theme = resolvedTheme;
29
82
  };
30
83
 
31
84
  // Run on initial load
32
85
  applyTheme();
33
86
  </script>
87
+ <link
88
+ rel="preload"
89
+ href={googleSansLatinWoff2}
90
+ as="font"
91
+ type="font/woff2"
92
+ crossorigin
93
+ />
94
+ <link
95
+ rel="preload"
96
+ href={googleSansLatinExtWoff2}
97
+ as="font"
98
+ type="font/woff2"
99
+ crossorigin
100
+ />
101
+ <link
102
+ rel="preload"
103
+ href={geistMonoLatinWoff2}
104
+ as="font"
105
+ type="font/woff2"
106
+ crossorigin
107
+ />
108
+ <link
109
+ rel="preload"
110
+ href={geistMonoLatinExtWoff2}
111
+ as="font"
112
+ type="font/woff2"
113
+ crossorigin
114
+ />
34
115
  <meta charset="UTF-8" />
35
116
  <meta name="viewport" content="width=device-width" />
36
117
  <link rel="icon" type="image/svg+xml" href="/favicon.svg" />
37
118
  <meta name="generator" content={Astro.generator} />
38
- <title>{config.title} Docs</title>
119
+ <link rel="canonical" href={canonicalUrl} />
120
+ <meta property="og:url" content={canonicalUrl} />
121
+ <meta property="og:type" content="website" />
122
+ <meta property="og:site_name" content={config.title} />
123
+ <title>{documentTitle}</title>
124
+ <meta property="og:title" content={documentTitle} />
125
+ <meta property="og:image" content={ogImageHref} />
126
+ <meta property="og:image:type" content="image/png" />
127
+ <meta property="og:image:width" content="1200" />
128
+ <meta property="og:image:height" content="630" />
129
+ <meta property="og:image:alt" content={documentTitle} />
130
+ <meta name="twitter:title" content={documentTitle} />
131
+ <meta name="twitter:card" content="summary_large_image" />
132
+ <meta name="twitter:image" content={ogImageHref} />
133
+ <meta name="twitter:image:alt" content={documentTitle} />
134
+ <meta name="description" content={resolvedMetaDescription} />
135
+ <meta property="og:description" content={resolvedMetaDescription} />
136
+ <meta name="twitter:description" content={resolvedMetaDescription} />
39
137
  </head>
40
138
  <body
41
139
  class="bg-background text-neutral-900 dark:text-white"
@@ -50,11 +148,11 @@ const config = await getConfig();
50
148
  </div>
51
149
  <div class="fixed top-0 inset-x-0 h-1 bg-background-dark z-50"></div>
52
150
  <div
53
- class="fixed top-[63px] -z-10 w-[5px] right-0 bottom-0 bg-background-dark border-l border-l-border"
151
+ class="fixed top-[63px] z-30 w-[5px] right-0 bottom-0 bg-background-dark border-l border-l-border"
54
152
  >
55
153
  </div>
56
154
  <div
57
- class="fixed top-[63px] -z-10 w-[5px] left-0 bottom-0 bg-background-dark border-r border-r-border"
155
+ class="fixed top-[63px] z-30 w-[5px] left-0 bottom-0 bg-background-dark border-r border-r-border"
58
156
  >
59
157
  </div>
60
158
  <div
@@ -83,10 +181,13 @@ const config = await getConfig();
83
181
  </div>
84
182
 
85
183
  <!-- Main Content -->
86
- <div class="px-5 sm:px-7 lg:pl-[calc(288px+24px)] pt-16 lg:pr-7">
87
- <main class="max-w-2xl xl:max-w-5xl mx-auto pt-16 pb-16">
184
+ <div class="px-5 sm:px-7 lg:pl-[calc(288px+36px)] pt-17 lg:pr-9">
185
+ <main
186
+ class="max-w-2xl xl:max-w-5xl mx-auto pt-16 pb-16 min-h-[calc(100vh-64px)]"
187
+ >
88
188
  <slot />
89
189
  </main>
190
+ <Footer />
90
191
  </div>
91
192
  </body>
92
193
  </html>