radiant-docs 0.1.7 → 0.1.8

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 +3 -3
  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,199 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+
5
+ const CWD = process.cwd();
6
+ const DIST_DIR = path.join(CWD, "dist");
7
+ const OG_IMAGES_DIR = path.join(DIST_DIR, "_og/images");
8
+ const VERSION_LENGTH = 12;
9
+
10
+ function findHtmlFiles(dir, files = []) {
11
+ if (!fs.existsSync(dir)) return files;
12
+
13
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
14
+ for (const entry of entries) {
15
+ const fullPath = path.join(dir, entry.name);
16
+
17
+ if (entry.isDirectory()) {
18
+ findHtmlFiles(fullPath, files);
19
+ continue;
20
+ }
21
+
22
+ if (entry.isFile() && entry.name.endsWith(".html")) {
23
+ files.push(fullPath);
24
+ }
25
+ }
26
+
27
+ return files;
28
+ }
29
+
30
+ function normalizeRoutePath(routePath) {
31
+ if (!routePath || routePath === "/") return "/";
32
+ if (!routePath.startsWith("/")) routePath = `/${routePath}`;
33
+ return routePath.endsWith("/") ? routePath : `${routePath}/`;
34
+ }
35
+
36
+ function routePathFromHtmlPath(filePath) {
37
+ const relative = path.relative(DIST_DIR, filePath).replace(/\\/g, "/");
38
+
39
+ if (relative === "index.html") return "/";
40
+ if (relative.endsWith("/index.html")) {
41
+ const base = relative.slice(0, -"/index.html".length);
42
+ return normalizeRoutePath(base);
43
+ }
44
+
45
+ if (relative.endsWith(".html")) {
46
+ return normalizeRoutePath(relative.slice(0, -".html".length));
47
+ }
48
+
49
+ return "/";
50
+ }
51
+
52
+ function routePathToOgImageRelativePath(routePath) {
53
+ const normalizedRoutePath = normalizeRoutePath(routePath);
54
+ if (normalizedRoutePath === "/") return "index.png";
55
+ return `${normalizedRoutePath.slice(1, -1)}.png`;
56
+ }
57
+
58
+ function hashFile(filePath) {
59
+ const buffer = fs.readFileSync(filePath);
60
+ return crypto
61
+ .createHash("sha256")
62
+ .update(buffer)
63
+ .digest("hex")
64
+ .slice(0, VERSION_LENGTH);
65
+ }
66
+
67
+ function updateVersionInUrl(urlValue, version) {
68
+ if (typeof urlValue !== "string" || urlValue.trim().length === 0) {
69
+ return urlValue;
70
+ }
71
+
72
+ const decoded = urlValue.trim().replace(/&/g, "&");
73
+ const hasScheme = /^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(decoded);
74
+ const isProtocolRelative = decoded.startsWith("//");
75
+ const isAbsolutePath = decoded.startsWith("/");
76
+
77
+ let parsed;
78
+ try {
79
+ if (hasScheme) {
80
+ parsed = new URL(decoded);
81
+ } else if (isProtocolRelative) {
82
+ parsed = new URL(`https:${decoded}`);
83
+ } else {
84
+ parsed = new URL(decoded, "https://radiant.invalid/");
85
+ }
86
+ } catch {
87
+ return urlValue;
88
+ }
89
+
90
+ parsed.searchParams.set("v", version);
91
+
92
+ let encoded;
93
+ if (hasScheme) {
94
+ encoded = parsed.toString();
95
+ } else if (isProtocolRelative) {
96
+ encoded = `//${parsed.host}${parsed.pathname}${parsed.search}${parsed.hash}`;
97
+ } else if (isAbsolutePath) {
98
+ encoded = `${parsed.pathname}${parsed.search}${parsed.hash}`;
99
+ } else {
100
+ encoded = `${parsed.pathname.replace(/^\//, "")}${parsed.search}${parsed.hash}`;
101
+ }
102
+
103
+ return encoded.replace(/&/g, "&");
104
+ }
105
+
106
+ function replaceMetaImageUrl(html, attrName, attrValue, version) {
107
+ const patterns = [
108
+ new RegExp(
109
+ `(<meta\\s+[^>]*${attrName}\\s*=\\s*["']${attrValue}["'][^>]*\\bcontent\\s*=\\s*["'])([^"']*)(["'][^>]*>)`,
110
+ "gi",
111
+ ),
112
+ new RegExp(
113
+ `(<meta\\s+[^>]*\\bcontent\\s*=\\s*["'])([^"']*)(["'][^>]*${attrName}\\s*=\\s*["']${attrValue}["'][^>]*>)`,
114
+ "gi",
115
+ ),
116
+ ];
117
+
118
+ let found = false;
119
+ let changed = false;
120
+ let nextHtml = html;
121
+
122
+ for (const pattern of patterns) {
123
+ nextHtml = nextHtml.replace(pattern, (_, prefix, currentUrl, suffix) => {
124
+ found = true;
125
+ const updatedUrl = updateVersionInUrl(currentUrl, version);
126
+ if (updatedUrl !== currentUrl) {
127
+ changed = true;
128
+ }
129
+ return `${prefix}${updatedUrl}${suffix}`;
130
+ });
131
+ }
132
+
133
+ return { html: nextHtml, found, changed };
134
+ }
135
+
136
+ function main() {
137
+ if (!fs.existsSync(DIST_DIR)) {
138
+ console.warn("Skipping OG version stamping: dist directory not found.");
139
+ return;
140
+ }
141
+
142
+ if (!fs.existsSync(OG_IMAGES_DIR)) {
143
+ console.warn("Skipping OG version stamping: OG images directory not found.");
144
+ return;
145
+ }
146
+
147
+ const htmlFiles = findHtmlFiles(DIST_DIR).sort();
148
+ if (htmlFiles.length === 0) {
149
+ console.warn("Skipping OG version stamping: no HTML files found in dist.");
150
+ return;
151
+ }
152
+
153
+ let updatedCount = 0;
154
+ let missingOgCount = 0;
155
+ let missingMetaCount = 0;
156
+
157
+ for (const htmlPath of htmlFiles) {
158
+ const routePath = routePathFromHtmlPath(htmlPath);
159
+ const ogImageRelativePath = routePathToOgImageRelativePath(routePath);
160
+ const ogImagePath = path.join(OG_IMAGES_DIR, ...ogImageRelativePath.split("/"));
161
+
162
+ if (!fs.existsSync(ogImagePath)) {
163
+ missingOgCount += 1;
164
+ continue;
165
+ }
166
+
167
+ const version = hashFile(ogImagePath);
168
+ const sourceHtml = fs.readFileSync(htmlPath, "utf8");
169
+
170
+ const ogResult = replaceMetaImageUrl(
171
+ sourceHtml,
172
+ "property",
173
+ "og:image",
174
+ version,
175
+ );
176
+ const twitterResult = replaceMetaImageUrl(
177
+ ogResult.html,
178
+ "name",
179
+ "twitter:image",
180
+ version,
181
+ );
182
+
183
+ if (!ogResult.found && !twitterResult.found) {
184
+ missingMetaCount += 1;
185
+ continue;
186
+ }
187
+
188
+ if (twitterResult.html !== sourceHtml) {
189
+ fs.writeFileSync(htmlPath, twitterResult.html, "utf8");
190
+ updatedCount += 1;
191
+ }
192
+ }
193
+
194
+ console.log(
195
+ `✅ OG version stamping complete. Updated ${updatedCount}/${htmlFiles.length} HTML files (missing OG image: ${missingOgCount}, missing meta tags: ${missingMetaCount}).`,
196
+ );
197
+ }
198
+
199
+ main();
@@ -0,0 +1,140 @@
1
+ import crypto from "node:crypto";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+
5
+ const CWD = process.cwd();
6
+ const DIST_DIR = path.join(CWD, "dist");
7
+ const PAGEFIND_DIR = path.join(DIST_DIR, "pagefind");
8
+ const PAGEFIND_RUNTIME_PATH = path.join(PAGEFIND_DIR, "pagefind.js");
9
+ const VERSION_LENGTH = 12;
10
+
11
+ function findFilesRecursively(dir, files = []) {
12
+ if (!fs.existsSync(dir)) return files;
13
+
14
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
15
+ for (const entry of entries) {
16
+ const fullPath = path.join(dir, entry.name);
17
+
18
+ if (entry.isDirectory()) {
19
+ findFilesRecursively(fullPath, files);
20
+ continue;
21
+ }
22
+
23
+ if (entry.isFile()) {
24
+ files.push(fullPath);
25
+ }
26
+ }
27
+
28
+ return files;
29
+ }
30
+
31
+ function normalizeRuntimeSourceForHash(source) {
32
+ return source
33
+ .replaceAll(
34
+ /pagefind-entry\.json\?(?:ts=\$\{Date\.now\(\)\}|v=[^"'`)]*)/g,
35
+ "pagefind-entry.json?__VERSION__",
36
+ )
37
+ .replaceAll(
38
+ /wasm\.\$\{language\}\.pagefind(?:\?v=[^"'`)]*)?/g,
39
+ "wasm.${language}.pagefind?__VERSION__",
40
+ )
41
+ .replaceAll(
42
+ /wasm\.unknown\.pagefind(?:\?v=[^"'`)]*)?/g,
43
+ "wasm.unknown.pagefind?__VERSION__",
44
+ );
45
+ }
46
+
47
+ function deriveContentVersion(runtimeSource) {
48
+ const hash = crypto.createHash("sha256");
49
+ const normalizedRuntimeSource = normalizeRuntimeSourceForHash(runtimeSource);
50
+
51
+ const pagefindFiles = findFilesRecursively(PAGEFIND_DIR).sort();
52
+ for (const filePath of pagefindFiles) {
53
+ const relativePath = path.relative(PAGEFIND_DIR, filePath).replace(/\\/g, "/");
54
+ hash.update(relativePath);
55
+ hash.update("\0");
56
+
57
+ if (filePath === PAGEFIND_RUNTIME_PATH) {
58
+ hash.update(normalizedRuntimeSource);
59
+ } else {
60
+ hash.update(fs.readFileSync(filePath));
61
+ }
62
+
63
+ hash.update("\0");
64
+ }
65
+
66
+ return hash.digest("hex").slice(0, VERSION_LENGTH);
67
+ }
68
+
69
+ function deriveVersion(runtimeSource) {
70
+ const commitSha = process.env.COMMIT_SHA?.trim().toLowerCase();
71
+ if (commitSha && /^[a-f0-9]{7,40}$/.test(commitSha)) {
72
+ return {
73
+ version: commitSha.slice(0, VERSION_LENGTH),
74
+ source: "commit-sha",
75
+ };
76
+ }
77
+
78
+ return {
79
+ version: deriveContentVersion(runtimeSource),
80
+ source: "content-hash",
81
+ };
82
+ }
83
+
84
+ function stampRuntimeScript(runtimeSource, encodedVersion) {
85
+ let replacements = 0;
86
+
87
+ let next = runtimeSource.replaceAll(
88
+ /pagefind-entry\.json\?(?:ts=\$\{Date\.now\(\)\}|v=[^"'`)]*)/g,
89
+ () => {
90
+ replacements += 1;
91
+ return `pagefind-entry.json?v=${encodedVersion}`;
92
+ },
93
+ );
94
+
95
+ next = next.replaceAll(
96
+ /wasm\.\$\{language\}\.pagefind(?:\?v=[^"'`)]*)?/g,
97
+ () => {
98
+ replacements += 1;
99
+ return `wasm.\${language}.pagefind?v=${encodedVersion}`;
100
+ },
101
+ );
102
+
103
+ next = next.replaceAll(
104
+ /wasm\.unknown\.pagefind(?:\?v=[^"'`)]*)?/g,
105
+ () => {
106
+ replacements += 1;
107
+ return `wasm.unknown.pagefind?v=${encodedVersion}`;
108
+ },
109
+ );
110
+
111
+ return { next, replacements };
112
+ }
113
+
114
+ function main() {
115
+ if (!fs.existsSync(PAGEFIND_RUNTIME_PATH)) {
116
+ console.warn(
117
+ "Skipping Pagefind runtime version stamping: dist/pagefind/pagefind.js not found.",
118
+ );
119
+ return;
120
+ }
121
+
122
+ const runtimeSource = fs.readFileSync(PAGEFIND_RUNTIME_PATH, "utf8");
123
+ const { version, source } = deriveVersion(runtimeSource);
124
+ const encodedVersion = encodeURIComponent(version);
125
+
126
+ const runtimeResult = stampRuntimeScript(runtimeSource, encodedVersion);
127
+ if (runtimeResult.next !== runtimeSource) {
128
+ fs.writeFileSync(PAGEFIND_RUNTIME_PATH, runtimeResult.next, "utf8");
129
+ } else if (runtimeResult.replacements === 0) {
130
+ console.warn(
131
+ "Pagefind runtime version stamping made no runtime changes. Runtime format may have changed.",
132
+ );
133
+ }
134
+
135
+ console.log(
136
+ `✅ Pagefind runtime version stamping complete (version=${version}, source=${source}, runtimeReplacements=${runtimeResult.replacements}).`,
137
+ );
138
+ }
139
+
140
+ main();
@@ -0,0 +1,94 @@
1
+ ---
2
+ import Icon from "./ui/Icon.astro";
3
+ import { getConfig } from "../lib/validation";
4
+ import LogoLink from "./LogoLink.astro";
5
+
6
+ const config = await getConfig();
7
+ const { footer } = config;
8
+
9
+ if (!footer) return null;
10
+
11
+ const socialIcons: Record<string, string> = {
12
+ x: "simple-icons:x",
13
+ website: "lucide:globe",
14
+ facebook: "simple-icons:facebook",
15
+ youtube: "simple-icons:youtube",
16
+ discord: "simple-icons:discord",
17
+ slack: "simple-icons:slack",
18
+ github: "simple-icons:github",
19
+ linkedin: "simple-icons:linkedin",
20
+ instagram: "simple-icons:instagram",
21
+ "hacker-news": "simple-icons:ycombinator",
22
+ medium: "simple-icons:medium",
23
+ telegram: "simple-icons:telegram",
24
+ bluesky: "simple-icons:bluesky",
25
+ threads: "simple-icons:threads",
26
+ reddit: "simple-icons:reddit",
27
+ podcast: "lucide:podcast",
28
+ };
29
+
30
+ ---
31
+
32
+ <footer class="border-t border-border-light pt-16" data-pagefind-ignore>
33
+ <div class="flex flex-col items-center gap-6">
34
+ <!-- Branding -->
35
+ <LogoLink
36
+ class="flex items-center gap-2.5 text-xl font-bold text-neutral-800 dark:text-neutral-100"
37
+ />
38
+ <!-- Socials -->
39
+ {
40
+ footer.socials && (
41
+ <div class="flex items-center justify-center gap-6">
42
+ {Object.entries(footer.socials).map(([platform, url]) => (
43
+ <a
44
+ href={url}
45
+ class="text-neutral-400 hover:text-neutral-600 dark:text-neutral-500 dark:hover:text-neutral-300 transition-all duration-300 transform hover:scale-105"
46
+ target="_blank"
47
+ rel="noopener noreferrer"
48
+ title={platform}
49
+ >
50
+ <Icon
51
+ name={socialIcons[platform] || "lucide:external-link"}
52
+ class="w-[18px] h-[18px]"
53
+ />
54
+ </a>
55
+ ))}
56
+ </div>
57
+ )
58
+ }
59
+ {
60
+ footer.links && (
61
+ <div class="flex flex-wrap justify-center gap-x-8 gap-y-3">
62
+ {footer.links.map((link) => (
63
+ <a
64
+ href={link.href}
65
+ class="text-sm text-neutral-500 hover:text-neutral-900 dark:text-neutral-400 dark:hover:text-neutral-100 transition-colors duration-300"
66
+ >
67
+ {link.text}
68
+ </a>
69
+ ))}
70
+ </div>
71
+ )
72
+ }
73
+ </div>
74
+
75
+ <!-- Links -->
76
+ </footer>
77
+
78
+ <div
79
+ class="mt-20 py-8 border-t border-neutral-100 dark:border-neutral-800/60 flex flex-col sm:flex-row justify-between items-center gap-4"
80
+ >
81
+ <p class="text-[13px] text-neutral-400 dark:text-neutral-500">
82
+ &copy; {new Date().getFullYear()}
83
+ {config.title}. All rights reserved.
84
+ </p>
85
+ <a
86
+ href="https://radiant.io"
87
+ class="group flex items-center gap-1.5 text-[13px] text-neutral-400 dark:text-neutral-500 hover:text-neutral-600 dark:hover:text-neutral-300 transition-colors duration-200"
88
+ >
89
+ Built with <span
90
+ class="group-hover:text-neutral-900 dark:group-hover:text-neutral-100 font-medium duration-200"
91
+ >Radiant Docs</span
92
+ >
93
+ </a>
94
+ </div>
@@ -1,40 +1,22 @@
1
1
  ---
2
- import { Icon } from "astro-icon/components";
2
+ import Icon from "./ui/Icon.astro";
3
3
  import { getConfig } from "../lib/validation";
4
4
  import Search from "./Search.astro";
5
+ import LogoLink from "./LogoLink.astro";
5
6
 
6
7
  const config = await getConfig();
7
-
8
- function getLogoUrl(logoPath: string | undefined): string | null {
9
- if (!logoPath) return null;
10
-
11
- // Normalize path: remove leading slash if present
12
- const normalizedPath = logoPath.startsWith("/")
13
- ? logoPath.slice(1)
14
- : logoPath;
15
-
16
- // Return public URL - Vite plugin ensures file exists in public
17
- return `/${normalizedPath}`;
18
- }
19
-
20
- // Load light and dark logos
21
- const lightLogoUrl = getLogoUrl(config.logo?.light);
22
- const darkLogoUrl = getLogoUrl(config.logo?.dark);
23
-
24
- // Get the href for the logo link (defaults to "/")
25
- const logoHref = config.logo?.href || "/";
26
8
  ---
27
9
 
28
10
  <header
29
11
  class:list={[
30
- "fixed z-30 inset-x-1 top-0 h-16 mt-1 border-x border-t border-border rounded-t-xl overflow-hidden",
12
+ "fixed z-30 inset-x-1 top-0 h-16 mt-1 border-x border-t border-border rounded-t-xl overflow-hidden border-b border-b-border-light",
31
13
  config.navbar?.blur
32
- ? "bg-background/80 backdrop-blur-[18px] backdrop-saturate-200"
14
+ ? "bg-background/80 backdrop-blur-[18px] backdrop-saturate-50 border-b-neutral-100/85 bg-clip-padding"
33
15
  : "bg-background",
34
16
  ]}
35
17
  data-pagefind-ignore
36
18
  >
37
- <div class="flex h-full border-b border-b-border-light">
19
+ <div class="flex h-full">
38
20
  <div class="lg:w-[283px] h-full">
39
21
  <div class="flex gap-3 lg:block h-full lg:border-r border-border-light">
40
22
  <button
@@ -57,44 +39,7 @@ const logoHref = config.logo?.href || "/";
57
39
  </div>
58
40
  </div>
59
41
  </button>
60
- <a
61
- href={logoHref}
62
- class="h-full flex items-center justify-center gap-2 lg:gap-3 text-xl font-bold text-neutral-800 dark:text-neutral-100"
63
- >
64
- {
65
- lightLogoUrl || darkLogoUrl ? (
66
- <>
67
- {/* Light mode: show light logo if available, otherwise show title */}
68
- {lightLogoUrl ? (
69
- <img
70
- src={lightLogoUrl}
71
- class="max-h-[40px] w-auto max-w-[200px] object-contain dark:hidden"
72
- alt="Logo"
73
- />
74
- ) : (
75
- <span class="dark:hidden">{config.title}</span>
76
- )}
77
-
78
- {/* Dark mode: show dark logo if available, otherwise show title */}
79
- {darkLogoUrl ? (
80
- <img
81
- src={darkLogoUrl}
82
- class="max-h-[40px] w-auto max-w-[200px] object-contain hidden dark:block"
83
- alt="Logo"
84
- />
85
- ) : (
86
- <span class="hidden dark:block">{config.title}</span>
87
- )}
88
- </>
89
- ) : (
90
- config.title
91
- )
92
- }
93
- <span
94
- class="text-[10px] text-neutral-500 font-semibold bg-neutral-100 dark:bg-neutral-800 px-2 py-px mt-0.5 rounded-full border border-neutral-200 dark:border-neutral-700/70 shadow-xs"
95
- >Docs</span
96
- >
97
- </a>
42
+ <LogoLink />
98
43
  </div>
99
44
  </div>
100
45
  <div class="flex-1 flex">
@@ -122,7 +67,7 @@ const logoHref = config.logo?.href || "/";
122
67
  {l.icon && (
123
68
  <Icon
124
69
  class="ml-0.5"
125
- name={`lucide:${l.icon}`}
70
+ name={l.icon}
126
71
  width="16"
127
72
  height="16"
128
73
  />
@@ -135,14 +80,14 @@ const logoHref = config.logo?.href || "/";
135
80
  {config.navbar.secondary && (
136
81
  <a
137
82
  class:list={[
138
- "items-center gap-1.5 px-3 py-[5px] text-sm font-[450] bg-white text-neutral-600 hover:text-neutral-700 rounded-md border border-neutral-200 shadow-xs hover:shadow-sm transition-all whitespace-nowrap",
83
+ "items-center gap-1.5 px-3 py-[5px] text-sm bg-white text-neutral-600 hover:text-neutral-700 rounded-md border border-neutral-200 shadow-xs hover:shadow-sm transition-all whitespace-nowrap",
139
84
  config.navbar.primary ? "hidden lg:flex" : "flex",
140
85
  ]}
141
86
  href={config.navbar.secondary.href}
142
87
  >
143
88
  {config.navbar.secondary.icon && (
144
89
  <Icon
145
- name={`lucide:${config.navbar.secondary.icon}`}
90
+ name={config.navbar.secondary.icon}
146
91
  width="16"
147
92
  height="16"
148
93
  />
@@ -153,13 +98,13 @@ const logoHref = config.logo?.href || "/";
153
98
  {config.navbar.primary && (
154
99
  <a
155
100
  class:list={[
156
- "flex items-center gap-1.5 px-5 py-[5px] text-sm font-medium rounded-md bg-neutral-900 text-white/95 hover:text-white duration-200 shadow-[inset_0_1px_0_rgb(255,255,255,0.3),0_0_0_1px_var(--color-neutral-800)] transition-all whitespace-nowrap",
101
+ "flex items-center gap-1.5 px-5 py-[5px] text-sm rounded-md bg-neutral-900 text-white/95 hover:text-white duration-200 shadow-[inset_0_1px_0_rgb(255,255,255,0.3),0_0_0_1px_var(--color-neutral-800)] transition-all whitespace-nowrap",
157
102
  ]}
158
103
  href={config.navbar.primary.href}
159
104
  >
160
105
  {config.navbar.primary.icon && (
161
106
  <Icon
162
- name={`lucide:${config.navbar.primary.icon}`}
107
+ name={config.navbar.primary.icon}
163
108
  width="16"
164
109
  height="16"
165
110
  />