@farming-labs/docs 0.0.14 → 0.0.16

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 (2) hide show
  1. package/dist/cli/index.mjs +246 -26
  2. package/package.json +1 -1
@@ -214,6 +214,49 @@ const THEME_INFO = {
214
214
  function getThemeInfo(theme) {
215
215
  return THEME_INFO[theme] ?? THEME_INFO.fumadocs;
216
216
  }
217
+ function getThemeExportName(themeName) {
218
+ const base = themeName.replace(/\.ts$/i, "").trim();
219
+ if (!base) return "customTheme";
220
+ return base.replace(/-([a-z])/g, (_, c) => c.toUpperCase()).replace(/^./, (c) => c.toLowerCase());
221
+ }
222
+ function getCustomThemeCssImportPath(globalCssRelPath, themeName) {
223
+ if (globalCssRelPath.startsWith("app/")) return `../themes/${themeName}.css`;
224
+ if (globalCssRelPath.startsWith("src/")) return `../../themes/${themeName}.css`;
225
+ return `../themes/${themeName}.css`;
226
+ }
227
+ /** Content for themes/{name}.ts - createTheme with the given name */
228
+ function customThemeTsTemplate(themeName) {
229
+ return `\
230
+ import { createTheme } from "@farming-labs/docs";
231
+
232
+ export const ${getThemeExportName(themeName)} = createTheme({
233
+ name: "${themeName.replace(/\.ts$/i, "")}",
234
+ ui: {
235
+ colors: {
236
+ primary: "#e11d48",
237
+ background: "#09090b",
238
+ foreground: "#fafafa",
239
+ muted: "#71717a",
240
+ border: "#27272a",
241
+ },
242
+ radius: "0.5rem",
243
+ },
244
+ });
245
+ `;
246
+ }
247
+ function customThemeCssTemplate(themeName) {
248
+ return `\
249
+ /* Custom theme: ${themeName} - edit variables and selectors as needed */
250
+ @import "@farming-labs/theme/presets/black";
251
+
252
+ .dark {
253
+ --color-fd-primary: #e11d48;
254
+ --color-fd-background: #09090b;
255
+ --color-fd-border: #27272a;
256
+ --radius: 0.5rem;
257
+ }
258
+ `;
259
+ }
217
260
  /** Config import for Next.js app/layout.tsx → root docs.config */
218
261
  function nextRootLayoutConfigImport(useAlias) {
219
262
  return useAlias ? "@/docs.config" : "../docs.config";
@@ -250,6 +293,27 @@ function astroPageServerImport(useAlias, depth) {
250
293
  return `${"../".repeat(depth)}lib/docs.server`;
251
294
  }
252
295
  function docsConfigTemplate(cfg) {
296
+ if (cfg.theme === "custom" && cfg.customThemeName) {
297
+ const exportName = getThemeExportName(cfg.customThemeName);
298
+ return `\
299
+ import { defineDocs } from "@farming-labs/docs";
300
+ import { ${exportName} } from "${cfg.useAlias ? "@/themes/" + cfg.customThemeName.replace(/\.ts$/i, "") : "./themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
301
+
302
+ export default defineDocs({
303
+ entry: "${cfg.entry}",
304
+ theme: ${exportName}({
305
+ ui: {
306
+ colors: { primary: "#6366f1" },
307
+ },
308
+ }),
309
+
310
+ metadata: {
311
+ titleTemplate: "%s – ${cfg.projectName}",
312
+ description: "Documentation for ${cfg.projectName}",
313
+ },
314
+ });
315
+ `;
316
+ }
253
317
  const t = getThemeInfo(cfg.theme);
254
318
  return `\
255
319
  import { defineDocs } from "@farming-labs/docs";
@@ -354,16 +418,21 @@ function injectRootProviderIntoLayout(content) {
354
418
  }
355
419
  return out === content ? null : out;
356
420
  }
357
- function globalCssTemplate(theme) {
421
+ function globalCssTemplate(theme, customThemeName, globalCssRelPath) {
422
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `\
423
+ @import "tailwindcss";
424
+ @import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
425
+ `;
358
426
  return `\
359
427
  @import "tailwindcss";
360
428
  @import "@farming-labs/theme/${getThemeInfo(theme).nextCssImport}/css";
361
429
  `;
362
430
  }
363
- function injectCssImport(existingContent, theme) {
364
- const importLine = `@import "@farming-labs/theme/${getThemeInfo(theme).nextCssImport}/css";`;
431
+ function injectCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
432
+ const importLine = theme === "custom" && customThemeName && globalCssRelPath ? `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";` : `@import "@farming-labs/theme/${getThemeInfo(theme).nextCssImport}/css";`;
365
433
  if (existingContent.includes(importLine)) return null;
366
- if (existingContent.includes("@farming-labs/theme/") && existingContent.includes("/css")) return null;
434
+ if (theme !== "custom" && existingContent.includes("@farming-labs/theme/") && existingContent.includes("/css")) return null;
435
+ if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
367
436
  const lines = existingContent.split("\n");
368
437
  const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
369
438
  if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
@@ -602,6 +671,34 @@ Deploy to Vercel, Netlify, or any Node.js hosting platform.
602
671
  `;
603
672
  }
604
673
  function svelteDocsConfigTemplate(cfg) {
674
+ if (cfg.theme === "custom" && cfg.customThemeName) {
675
+ const exportName = getThemeExportName(cfg.customThemeName);
676
+ return `\
677
+ import { defineDocs } from "@farming-labs/docs";
678
+ import { ${exportName} } from "${"../../themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
679
+
680
+ export default defineDocs({
681
+ entry: "${cfg.entry}",
682
+ theme: ${exportName}({
683
+ ui: {
684
+ colors: { primary: "#6366f1" },
685
+ },
686
+ }),
687
+
688
+ nav: {
689
+ title: "${cfg.projectName}",
690
+ url: "/${cfg.entry}",
691
+ },
692
+
693
+ breadcrumb: { enabled: true },
694
+
695
+ metadata: {
696
+ titleTemplate: "%s – ${cfg.projectName}",
697
+ description: "Documentation for ${cfg.projectName}",
698
+ },
699
+ });
700
+ `;
701
+ }
605
702
  const t = getThemeInfo(cfg.theme);
606
703
  return `\
607
704
  import { defineDocs } from "@farming-labs/docs";
@@ -692,17 +789,23 @@ function svelteRootLayoutTemplate(globalCssRelPath) {
692
789
  {@render children()}
693
790
  `;
694
791
  }
695
- function svelteGlobalCssTemplate(theme) {
792
+ function svelteGlobalCssTemplate(theme, customThemeName, globalCssRelPath) {
793
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `\
794
+ @import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
795
+ `;
696
796
  return `\
697
797
  @import "@farming-labs/svelte-theme/${theme}/css";
698
798
  `;
699
799
  }
700
- function svelteCssImportLine(theme) {
800
+ function svelteCssImportLine(theme, customThemeName, globalCssRelPath) {
801
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";`;
701
802
  return `@import "@farming-labs/svelte-theme/${theme}/css";`;
702
803
  }
703
- function injectSvelteCssImport(existingContent, theme) {
704
- const importLine = svelteCssImportLine(theme);
804
+ function injectSvelteCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
805
+ const importLine = svelteCssImportLine(theme, customThemeName, globalCssRelPath);
705
806
  if (existingContent.includes(importLine)) return null;
807
+ if (theme !== "custom" && existingContent.includes("@farming-labs/svelte-theme/") && existingContent.includes("/css")) return null;
808
+ if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
706
809
  const lines = existingContent.split("\n");
707
810
  const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
708
811
  if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
@@ -876,6 +979,35 @@ Deploy to Vercel, Netlify, or any Node.js hosting platform.
876
979
  `;
877
980
  }
878
981
  function astroDocsConfigTemplate(cfg) {
982
+ if (cfg.theme === "custom" && cfg.customThemeName) {
983
+ const exportName = getThemeExportName(cfg.customThemeName);
984
+ return `\
985
+ import { defineDocs } from "@farming-labs/docs";
986
+ import { ${exportName} } from "${"../../themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
987
+
988
+ export default defineDocs({
989
+ entry: "${cfg.entry}",
990
+ contentDir: "${cfg.entry}",
991
+ theme: ${exportName}({
992
+ ui: {
993
+ colors: { primary: "#6366f1" },
994
+ },
995
+ }),
996
+
997
+ nav: {
998
+ title: "${cfg.projectName}",
999
+ url: "/${cfg.entry}",
1000
+ },
1001
+
1002
+ breadcrumb: { enabled: true },
1003
+
1004
+ metadata: {
1005
+ titleTemplate: "%s – ${cfg.projectName}",
1006
+ description: "Documentation for ${cfg.projectName}",
1007
+ },
1008
+ });
1009
+ `;
1010
+ }
879
1011
  const t = getThemeInfo(cfg.theme);
880
1012
  return `\
881
1013
  import { defineDocs } from "@farming-labs/docs";
@@ -1025,17 +1157,23 @@ export const POST: APIRoute = async ({ request }) => {
1025
1157
  };
1026
1158
  `;
1027
1159
  }
1028
- function astroGlobalCssTemplate(theme) {
1160
+ function astroGlobalCssTemplate(theme, customThemeName, globalCssRelPath) {
1161
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `\
1162
+ @import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
1163
+ `;
1029
1164
  return `\
1030
1165
  @import "@farming-labs/astro-theme/${theme}/css";
1031
1166
  `;
1032
1167
  }
1033
- function astroCssImportLine(theme) {
1168
+ function astroCssImportLine(theme, customThemeName, globalCssRelPath) {
1169
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";`;
1034
1170
  return `@import "@farming-labs/astro-theme/${theme}/css";`;
1035
1171
  }
1036
- function injectAstroCssImport(existingContent, theme) {
1037
- const importLine = astroCssImportLine(theme);
1172
+ function injectAstroCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
1173
+ const importLine = astroCssImportLine(theme, customThemeName, globalCssRelPath);
1038
1174
  if (existingContent.includes(importLine)) return null;
1175
+ if (theme !== "custom" && existingContent.includes("@farming-labs/astro-theme/") && existingContent.includes("/css")) return null;
1176
+ if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
1039
1177
  const lines = existingContent.split("\n");
1040
1178
  const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
1041
1179
  if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
@@ -1197,6 +1335,35 @@ Deploy to Vercel, Netlify, or any Node.js hosting platform.
1197
1335
  `;
1198
1336
  }
1199
1337
  function nuxtDocsConfigTemplate(cfg) {
1338
+ if (cfg.theme === "custom" && cfg.customThemeName) {
1339
+ const exportName = getThemeExportName(cfg.customThemeName);
1340
+ return `\
1341
+ import { defineDocs } from "@farming-labs/docs";
1342
+ import { ${exportName} } from "${cfg.useAlias ? "~/themes/" + cfg.customThemeName.replace(/\.ts$/i, "") : "./themes/" + cfg.customThemeName.replace(/\.ts$/i, "")}";
1343
+
1344
+ export default defineDocs({
1345
+ entry: "${cfg.entry}",
1346
+ contentDir: "${cfg.entry}",
1347
+ theme: ${exportName}({
1348
+ ui: {
1349
+ colors: { primary: "#6366f1" },
1350
+ },
1351
+ }),
1352
+
1353
+ nav: {
1354
+ title: "${cfg.projectName}",
1355
+ url: "/${cfg.entry}",
1356
+ },
1357
+
1358
+ breadcrumb: { enabled: true },
1359
+
1360
+ metadata: {
1361
+ titleTemplate: "%s – ${cfg.projectName}",
1362
+ description: "Documentation for ${cfg.projectName}",
1363
+ },
1364
+ });
1365
+ `;
1366
+ }
1200
1367
  const t = getThemeInfo(cfg.theme);
1201
1368
  return `\
1202
1369
  import { defineDocs } from "@farming-labs/docs";
@@ -1360,7 +1527,7 @@ This documentation was generated by \`@farming-labs/docs\`. Edit the markdown fi
1360
1527
  - **Markdown Support** — Write docs with standard Markdown
1361
1528
  - **Syntax Highlighting** — Code blocks with automatic highlighting
1362
1529
  - **Dark Mode** — Built-in theme switching
1363
- - **Search** — Full-text search across all pages (⌘K)
1530
+ - **Search** — Full-text search across all pages (⌘ K)
1364
1531
  - **Responsive** — Works on any screen size
1365
1532
 
1366
1533
  ---
@@ -1496,17 +1663,23 @@ pnpm build
1496
1663
  Deploy to Vercel, Netlify, or any Node.js hosting platform.
1497
1664
  `;
1498
1665
  }
1499
- function nuxtGlobalCssTemplate(theme) {
1666
+ function nuxtGlobalCssTemplate(theme, customThemeName, globalCssRelPath) {
1667
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `\
1668
+ @import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";
1669
+ `;
1500
1670
  return `\
1501
1671
  @import "@farming-labs/nuxt-theme/${theme}/css";
1502
1672
  `;
1503
1673
  }
1504
- function nuxtCssImportLine(theme) {
1674
+ function nuxtCssImportLine(theme, customThemeName, globalCssRelPath) {
1675
+ if (theme === "custom" && customThemeName && globalCssRelPath) return `@import "${getCustomThemeCssImportPath(globalCssRelPath, customThemeName.replace(/\.css$/i, ""))}";`;
1505
1676
  return `@import "@farming-labs/nuxt-theme/${theme}/css";`;
1506
1677
  }
1507
- function injectNuxtCssImport(existingContent, theme) {
1508
- const importLine = nuxtCssImportLine(theme);
1678
+ function injectNuxtCssImport(existingContent, theme, customThemeName, globalCssRelPath) {
1679
+ const importLine = nuxtCssImportLine(theme, customThemeName, globalCssRelPath);
1509
1680
  if (existingContent.includes(importLine)) return null;
1681
+ if (theme !== "custom" && existingContent.includes("@farming-labs/nuxt-theme/") && existingContent.includes("/css")) return null;
1682
+ if (theme === "custom" && existingContent.includes("themes/") && existingContent.includes(".css")) return null;
1510
1683
  const lines = existingContent.split("\n");
1511
1684
  const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
1512
1685
  if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
@@ -1649,6 +1822,11 @@ async function init(options = {}) {
1649
1822
  value: "greentree",
1650
1823
  label: "GreenTree",
1651
1824
  hint: "Emerald green accent, Inter font, Mintlify-inspired"
1825
+ },
1826
+ {
1827
+ value: "custom",
1828
+ label: "Create your own theme",
1829
+ hint: "Scaffold a new theme file + CSS in themes/ (name asked next)"
1652
1830
  }
1653
1831
  ]
1654
1832
  });
@@ -1656,6 +1834,26 @@ async function init(options = {}) {
1656
1834
  p.outro(pc.red("Init cancelled."));
1657
1835
  process.exit(0);
1658
1836
  }
1837
+ let customThemeName;
1838
+ if (theme === "custom") {
1839
+ const nameAnswer = await p.text({
1840
+ message: "Theme name? (we'll create themes/<name>.ts and themes/<name>.css)",
1841
+ placeholder: "my-theme",
1842
+ defaultValue: "my-theme",
1843
+ validate: (value) => {
1844
+ const v = (value ?? "").trim().replace(/\.(ts|css)$/i, "");
1845
+ if (!v) return "Theme name is required";
1846
+ if (v.includes("/") || v.includes("\\")) return "Theme name cannot contain path separators";
1847
+ if (v.includes(" ")) return "Theme name cannot contain spaces";
1848
+ if (!/^[a-z0-9_-]+$/i.test(v)) return "Use only letters, numbers, hyphens, and underscores";
1849
+ }
1850
+ });
1851
+ if (p.isCancel(nameAnswer)) {
1852
+ p.outro(pc.red("Init cancelled."));
1853
+ process.exit(0);
1854
+ }
1855
+ customThemeName = nameAnswer.trim().replace(/\.(ts|css)$/i, "");
1856
+ }
1659
1857
  const aliasHint = framework === "nextjs" ? `Uses ${pc.cyan("@/")} prefix (requires tsconfig paths)` : framework === "sveltekit" ? `Uses ${pc.cyan("$lib/")} prefix (SvelteKit built-in)` : framework === "nuxt" ? `Uses ${pc.cyan("~/")} prefix (Nuxt built-in)` : `Uses ${pc.cyan("@/")} prefix (requires tsconfig paths)`;
1660
1858
  const useAlias = await p.confirm({
1661
1859
  message: `Use path aliases for imports? ${pc.dim(aliasHint)}`,
@@ -1748,10 +1946,12 @@ async function init(options = {}) {
1748
1946
  }
1749
1947
  const pkgJsonContent = readFileSafe(path.join(cwd, "package.json"));
1750
1948
  const pkgJson = pkgJsonContent ? JSON.parse(pkgJsonContent) : { name: "my-project" };
1949
+ const projectName = pkgJson.name || "My Project";
1751
1950
  const cfg = {
1752
1951
  entry: entryPath,
1753
1952
  theme,
1754
- projectName: pkgJson.name || "My Project",
1953
+ customThemeName,
1954
+ projectName,
1755
1955
  framework,
1756
1956
  useAlias,
1757
1957
  astroAdapter
@@ -1864,6 +2064,11 @@ async function init(options = {}) {
1864
2064
  }
1865
2065
  }
1866
2066
  function scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written) {
2067
+ if (cfg.theme === "custom" && cfg.customThemeName) {
2068
+ const baseName = cfg.customThemeName.replace(/\.(ts|css)$/i, "");
2069
+ write(`themes/${baseName}.ts`, customThemeTsTemplate(baseName));
2070
+ write(`themes/${baseName}.css`, customThemeCssTemplate(baseName));
2071
+ }
1867
2072
  write("docs.config.ts", docsConfigTemplate(cfg));
1868
2073
  const existingNextConfig = readFileSafe(path.join(cwd, "next.config.ts")) ?? readFileSafe(path.join(cwd, "next.config.mjs")) ?? readFileSafe(path.join(cwd, "next.config.js"));
1869
2074
  if (existingNextConfig) {
@@ -1887,12 +2092,12 @@ function scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written) {
1887
2092
  const globalCssAbsPath = path.join(cwd, globalCssRelPath);
1888
2093
  const existingGlobalCss = readFileSafe(globalCssAbsPath);
1889
2094
  if (existingGlobalCss) {
1890
- const injected = injectCssImport(existingGlobalCss, cfg.theme);
2095
+ const injected = injectCssImport(existingGlobalCss, cfg.theme, cfg.customThemeName, globalCssRelPath);
1891
2096
  if (injected) {
1892
2097
  writeFileSafe(globalCssAbsPath, injected, true);
1893
2098
  written.push(globalCssRelPath + " (updated)");
1894
2099
  } else skipped.push(globalCssRelPath + " (already configured)");
1895
- } else write(globalCssRelPath, globalCssTemplate(cfg.theme));
2100
+ } else write(globalCssRelPath, globalCssTemplate(cfg.theme, cfg.customThemeName, globalCssRelPath));
1896
2101
  write(`app/${cfg.entry}/layout.tsx`, docsLayoutTemplate(cfg));
1897
2102
  write("postcss.config.mjs", postcssConfigTemplate());
1898
2103
  if (!fileExists(path.join(cwd, "tsconfig.json"))) write("tsconfig.json", tsconfigTemplate(cfg.useAlias));
@@ -1901,6 +2106,11 @@ function scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written) {
1901
2106
  write(`app/${cfg.entry}/quickstart/page.mdx`, quickstartPageTemplate(cfg));
1902
2107
  }
1903
2108
  function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written) {
2109
+ if (cfg.theme === "custom" && cfg.customThemeName) {
2110
+ const baseName = cfg.customThemeName.replace(/\.(ts|css)$/i, "");
2111
+ write(`themes/${baseName}.ts`, customThemeTsTemplate(baseName));
2112
+ write(`themes/${baseName}.css`, customThemeCssTemplate(baseName));
2113
+ }
1904
2114
  write("src/lib/docs.config.ts", svelteDocsConfigTemplate(cfg));
1905
2115
  write("src/lib/docs.server.ts", svelteDocsServerTemplate(cfg));
1906
2116
  write(`src/routes/${cfg.entry}/+layout.svelte`, svelteDocsLayoutTemplate(cfg));
@@ -1920,17 +2130,22 @@ function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written)
1920
2130
  default: "fumadocs"
1921
2131
  }[cfg.theme] || "fumadocs";
1922
2132
  if (existingGlobalCss) {
1923
- const injected = injectSvelteCssImport(existingGlobalCss, cssTheme);
2133
+ const injected = cfg.theme === "custom" && cfg.customThemeName ? injectSvelteCssImport(existingGlobalCss, "custom", cfg.customThemeName, globalCssRelPath) : injectSvelteCssImport(existingGlobalCss, cssTheme);
1924
2134
  if (injected) {
1925
2135
  writeFileSafe(globalCssAbsPath, injected, true);
1926
2136
  written.push(globalCssRelPath + " (updated)");
1927
2137
  } else skipped.push(globalCssRelPath + " (already configured)");
1928
- } else write(globalCssRelPath, svelteGlobalCssTemplate(cssTheme));
2138
+ } else write(globalCssRelPath, cfg.theme === "custom" && cfg.customThemeName ? svelteGlobalCssTemplate("custom", cfg.customThemeName, globalCssRelPath) : svelteGlobalCssTemplate(cssTheme));
1929
2139
  write(`${cfg.entry}/page.md`, svelteWelcomePageTemplate(cfg));
1930
2140
  write(`${cfg.entry}/installation/page.md`, svelteInstallationPageTemplate(cfg));
1931
2141
  write(`${cfg.entry}/quickstart/page.md`, svelteQuickstartPageTemplate(cfg));
1932
2142
  }
1933
2143
  function scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written) {
2144
+ if (cfg.theme === "custom" && cfg.customThemeName) {
2145
+ const baseName = cfg.customThemeName.replace(/\.(ts|css)$/i, "");
2146
+ write(`themes/${baseName}.ts`, customThemeTsTemplate(baseName));
2147
+ write(`themes/${baseName}.css`, customThemeCssTemplate(baseName));
2148
+ }
1934
2149
  write("src/lib/docs.config.ts", astroDocsConfigTemplate(cfg));
1935
2150
  write("src/lib/docs.server.ts", astroDocsServerTemplate(cfg));
1936
2151
  if (!fileExists(path.join(cwd, "astro.config.mjs")) && !fileExists(path.join(cwd, "astro.config.ts"))) write("astro.config.mjs", astroConfigTemplate(cfg.astroAdapter ?? "vercel"));
@@ -1950,17 +2165,22 @@ function scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written) {
1950
2165
  default: "fumadocs"
1951
2166
  }[cfg.theme] || "fumadocs";
1952
2167
  if (existingGlobalCss) {
1953
- const injected = injectAstroCssImport(existingGlobalCss, cssTheme);
2168
+ const injected = cfg.theme === "custom" && cfg.customThemeName ? injectAstroCssImport(existingGlobalCss, "custom", cfg.customThemeName, globalCssRelPath) : injectAstroCssImport(existingGlobalCss, cssTheme);
1954
2169
  if (injected) {
1955
2170
  writeFileSafe(globalCssAbsPath, injected, true);
1956
2171
  written.push(globalCssRelPath + " (updated)");
1957
2172
  } else skipped.push(globalCssRelPath + " (already configured)");
1958
- } else write(globalCssRelPath, astroGlobalCssTemplate(cssTheme));
2173
+ } else write(globalCssRelPath, cfg.theme === "custom" && cfg.customThemeName ? astroGlobalCssTemplate("custom", cfg.customThemeName, globalCssRelPath) : astroGlobalCssTemplate(cssTheme));
1959
2174
  write(`${cfg.entry}/page.md`, astroWelcomePageTemplate(cfg));
1960
2175
  write(`${cfg.entry}/installation/page.md`, astroInstallationPageTemplate(cfg));
1961
2176
  write(`${cfg.entry}/quickstart/page.md`, astroQuickstartPageTemplate(cfg));
1962
2177
  }
1963
2178
  function scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written) {
2179
+ if (cfg.theme === "custom" && cfg.customThemeName) {
2180
+ const baseName = cfg.customThemeName.replace(/\.(ts|css)$/i, "");
2181
+ write(`themes/${baseName}.ts`, customThemeTsTemplate(baseName));
2182
+ write(`themes/${baseName}.css`, customThemeCssTemplate(baseName));
2183
+ }
1964
2184
  write("docs.config.ts", nuxtDocsConfigTemplate(cfg));
1965
2185
  write("server/utils/docs-server.ts", nuxtDocsServerTemplate(cfg));
1966
2186
  write("server/api/docs.get.ts", nuxtServerApiDocsGetTemplate());
@@ -1982,12 +2202,12 @@ function scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written) {
1982
2202
  const globalCssAbsPath = path.join(cwd, globalCssRelPath);
1983
2203
  const existingGlobalCss = readFileSafe(globalCssAbsPath);
1984
2204
  if (existingGlobalCss) {
1985
- const injected = injectNuxtCssImport(existingGlobalCss, cssTheme);
2205
+ const injected = cfg.theme === "custom" && cfg.customThemeName ? injectNuxtCssImport(existingGlobalCss, "custom", cfg.customThemeName, globalCssRelPath) : injectNuxtCssImport(existingGlobalCss, cssTheme);
1986
2206
  if (injected) {
1987
2207
  writeFileSafe(globalCssAbsPath, injected, true);
1988
2208
  written.push(globalCssRelPath + " (updated)");
1989
2209
  } else skipped.push(globalCssRelPath + " (already configured)");
1990
- } else write(globalCssRelPath, nuxtGlobalCssTemplate(cssTheme));
2210
+ } else write(globalCssRelPath, cfg.theme === "custom" && cfg.customThemeName ? nuxtGlobalCssTemplate("custom", cfg.customThemeName, globalCssRelPath) : nuxtGlobalCssTemplate(cssTheme));
1991
2211
  write(`${cfg.entry}/page.md`, nuxtWelcomePageTemplate(cfg));
1992
2212
  write(`${cfg.entry}/installation/page.md`, nuxtInstallationPageTemplate(cfg));
1993
2213
  write(`${cfg.entry}/quickstart/page.md`, nuxtQuickstartPageTemplate(cfg));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/docs",
3
- "version": "0.0.14",
3
+ "version": "0.0.16",
4
4
  "description": "Modern, flexible MDX-based docs framework — core types, config, and CLI",
5
5
  "keywords": [
6
6
  "docs",