@farming-labs/docs 0.0.2-beta.15 → 0.0.2-beta.17

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.
@@ -17,6 +17,7 @@ function detectFramework(cwd) {
17
17
  if (allDeps["next"]) return "nextjs";
18
18
  if (allDeps["@sveltejs/kit"]) return "sveltekit";
19
19
  if (allDeps["astro"]) return "astro";
20
+ if (allDeps["nuxt"]) return "nuxt";
20
21
  return null;
21
22
  }
22
23
  function detectPackageManager(cwd) {
@@ -66,7 +67,9 @@ const GLOBAL_CSS_CANDIDATES = [
66
67
  "styles/globals.css",
67
68
  "styles/global.css",
68
69
  "src/styles/globals.css",
69
- "src/styles/global.css"
70
+ "src/styles/global.css",
71
+ "assets/css/main.css",
72
+ "assets/main.css"
70
73
  ];
71
74
  /**
72
75
  * Find existing global CSS files in the project.
@@ -135,27 +138,44 @@ const THEME_INFO = {
135
138
  nextImport: "@farming-labs/theme",
136
139
  svelteImport: "@farming-labs/svelte-theme",
137
140
  astroImport: "@farming-labs/astro-theme",
141
+ nuxtImport: "@farming-labs/nuxt-theme",
138
142
  nextCssImport: "default",
139
143
  svelteCssTheme: "fumadocs",
140
- astroCssTheme: "fumadocs"
144
+ astroCssTheme: "fumadocs",
145
+ nuxtCssTheme: "fumadocs"
141
146
  },
142
147
  darksharp: {
143
148
  factory: "darksharp",
144
149
  nextImport: "@farming-labs/theme/darksharp",
145
150
  svelteImport: "@farming-labs/svelte-theme/darksharp",
146
151
  astroImport: "@farming-labs/astro-theme/darksharp",
152
+ nuxtImport: "@farming-labs/nuxt-theme/darksharp",
147
153
  nextCssImport: "darksharp",
148
154
  svelteCssTheme: "darksharp",
149
- astroCssTheme: "darksharp"
155
+ astroCssTheme: "darksharp",
156
+ nuxtCssTheme: "darksharp"
150
157
  },
151
158
  "pixel-border": {
152
159
  factory: "pixelBorder",
153
160
  nextImport: "@farming-labs/theme/pixel-border",
154
161
  svelteImport: "@farming-labs/svelte-theme/pixel-border",
155
162
  astroImport: "@farming-labs/astro-theme/pixel-border",
163
+ nuxtImport: "@farming-labs/nuxt-theme/pixel-border",
156
164
  nextCssImport: "pixel-border",
157
165
  svelteCssTheme: "pixel-border",
158
- astroCssTheme: "pixel-border"
166
+ astroCssTheme: "pixel-border",
167
+ nuxtCssTheme: "pixel-border"
168
+ },
169
+ colorful: {
170
+ factory: "colorful",
171
+ nextImport: "@farming-labs/theme/colorful",
172
+ svelteImport: "@farming-labs/svelte-theme/colorful",
173
+ astroImport: "@farming-labs/astro-theme/colorful",
174
+ nuxtImport: "@farming-labs/nuxt-theme/colorful",
175
+ nextCssImport: "colorful",
176
+ svelteCssTheme: "colorful",
177
+ astroCssTheme: "colorful",
178
+ nuxtCssTheme: "colorful"
159
179
  }
160
180
  };
161
181
  function getThemeInfo(theme) {
@@ -1107,6 +1127,322 @@ pnpm build
1107
1127
  Deploy to Vercel, Netlify, or any Node.js hosting platform.
1108
1128
  `;
1109
1129
  }
1130
+ function nuxtDocsConfigTemplate(cfg) {
1131
+ const t = getThemeInfo(cfg.theme);
1132
+ return `\
1133
+ import { defineDocs } from "@farming-labs/docs";
1134
+ import { ${t.factory} } from "${t.nuxtImport}";
1135
+
1136
+ export default defineDocs({
1137
+ entry: "${cfg.entry}",
1138
+ contentDir: "${cfg.entry}",
1139
+ theme: ${t.factory}({
1140
+ ui: {
1141
+ colors: { primary: "#6366f1" },
1142
+ },
1143
+ }),
1144
+
1145
+ nav: {
1146
+ title: "${cfg.projectName}",
1147
+ url: "/${cfg.entry}",
1148
+ },
1149
+
1150
+ breadcrumb: { enabled: true },
1151
+
1152
+ metadata: {
1153
+ titleTemplate: "%s – ${cfg.projectName}",
1154
+ description: "Documentation for ${cfg.projectName}",
1155
+ },
1156
+ });
1157
+ `;
1158
+ }
1159
+ function nuxtDocsServerTemplate(cfg) {
1160
+ return `\
1161
+ import { createDocsServer } from "@farming-labs/nuxt/server";
1162
+ import config from "../../docs.config";
1163
+
1164
+ const contentFiles = import.meta.glob("/${cfg.entry ?? "docs"}/**/*.{md,mdx}", {
1165
+ query: "?raw",
1166
+ import: "default",
1167
+ eager: true,
1168
+ }) as Record<string, string>;
1169
+
1170
+ export const docsServer = createDocsServer({
1171
+ ...config,
1172
+ _preloadedContent: contentFiles,
1173
+ });
1174
+ `;
1175
+ }
1176
+ function nuxtServerApiDocsGetTemplate() {
1177
+ return `\
1178
+ import { getRequestURL } from "h3";
1179
+ import { docsServer } from "../utils/docs-server";
1180
+
1181
+ export default defineEventHandler((event) => {
1182
+ const url = getRequestURL(event);
1183
+ const request = new Request(url.href, {
1184
+ method: event.method,
1185
+ headers: event.headers,
1186
+ });
1187
+ return docsServer.GET({ request });
1188
+ });
1189
+ `;
1190
+ }
1191
+ function nuxtServerApiDocsPostTemplate() {
1192
+ return `\
1193
+ import { getRequestURL, readRawBody } from "h3";
1194
+ import { docsServer } from "../utils/docs-server";
1195
+
1196
+ export default defineEventHandler(async (event) => {
1197
+ const url = getRequestURL(event);
1198
+ const body = await readRawBody(event);
1199
+ const request = new Request(url.href, {
1200
+ method: "POST",
1201
+ headers: event.headers,
1202
+ body: body ?? undefined,
1203
+ });
1204
+ return docsServer.POST({ request });
1205
+ });
1206
+ `;
1207
+ }
1208
+ function nuxtServerApiDocsLoadTemplate() {
1209
+ return `\
1210
+ import { getQuery } from "h3";
1211
+ import { docsServer } from "../../utils/docs-server";
1212
+
1213
+ export default defineEventHandler(async (event) => {
1214
+ const query = getQuery(event);
1215
+ const pathname = (query.pathname as string) ?? "/docs";
1216
+ return docsServer.load(pathname);
1217
+ });
1218
+ `;
1219
+ }
1220
+ function nuxtDocsPageTemplate(cfg) {
1221
+ return `\
1222
+ <script setup lang="ts">
1223
+ import { DocsLayout, DocsContent } from "@farming-labs/nuxt-theme";
1224
+ import config from "${cfg.useAlias ? "~/docs.config" : "../../docs.config"}";
1225
+
1226
+ const route = useRoute();
1227
+ const pathname = computed(() => route.path);
1228
+
1229
+ const { data, error } = await useAsyncData(\`docs-\${pathname.value}\`, () =>
1230
+ $fetch("/api/docs/load", {
1231
+ query: { pathname: pathname.value },
1232
+ })
1233
+ );
1234
+
1235
+ if (error.value) {
1236
+ throw createError({
1237
+ statusCode: 404,
1238
+ statusMessage: "Page not found",
1239
+ });
1240
+ }
1241
+ <\/script>
1242
+
1243
+ <template>
1244
+ <div v-if="data" class="fd-docs-wrapper">
1245
+ <DocsLayout :tree="data.tree" :config="config">
1246
+ <DocsContent :data="data" :config="config" />
1247
+ </DocsLayout>
1248
+ </div>
1249
+ </template>
1250
+ `;
1251
+ }
1252
+ function nuxtConfigTemplate(cfg) {
1253
+ return `\
1254
+ export default defineNuxtConfig({
1255
+ compatibilityDate: "2024-11-01",
1256
+
1257
+ css: ["@farming-labs/nuxt-theme/${getThemeInfo(cfg.theme).nuxtCssTheme}/css"],
1258
+
1259
+ vite: {
1260
+ optimizeDeps: {
1261
+ include: ["@farming-labs/docs", "@farming-labs/nuxt", "@farming-labs/nuxt-theme"],
1262
+ },
1263
+ },
1264
+
1265
+ nitro: {
1266
+ moduleSideEffects: ["@farming-labs/nuxt/server"],
1267
+ },
1268
+ });
1269
+ `;
1270
+ }
1271
+ function nuxtWelcomePageTemplate(cfg) {
1272
+ return `\
1273
+ ---
1274
+ order: 1
1275
+ title: Documentation
1276
+ description: Welcome to ${cfg.projectName} documentation
1277
+ icon: book
1278
+ ---
1279
+
1280
+ # Welcome to ${cfg.projectName}
1281
+
1282
+ Get started with our documentation. Browse the pages on the left to learn more.
1283
+
1284
+ ## Overview
1285
+
1286
+ This documentation was generated by \`@farming-labs/docs\`. Edit the markdown files in \`${cfg.entry}/\` to customize.
1287
+
1288
+ ## Features
1289
+
1290
+ - **Markdown Support** — Write docs with standard Markdown
1291
+ - **Syntax Highlighting** — Code blocks with automatic highlighting
1292
+ - **Dark Mode** — Built-in theme switching
1293
+ - **Search** — Full-text search across all pages (⌘K)
1294
+ - **Responsive** — Works on any screen size
1295
+
1296
+ ---
1297
+
1298
+ ## Next Steps
1299
+
1300
+ Start by reading the [Installation](/${cfg.entry}/installation) guide, then follow the [Quickstart](/${cfg.entry}/quickstart) to build something.
1301
+ `;
1302
+ }
1303
+ function nuxtInstallationPageTemplate(cfg) {
1304
+ const t = getThemeInfo(cfg.theme);
1305
+ return `\
1306
+ ---
1307
+ order: 3
1308
+ title: Installation
1309
+ description: How to install and set up ${cfg.projectName}
1310
+ icon: terminal
1311
+ ---
1312
+
1313
+ # Installation
1314
+
1315
+ Follow these steps to install and configure ${cfg.projectName}.
1316
+
1317
+ ## Prerequisites
1318
+
1319
+ - Node.js 18+
1320
+ - A package manager (pnpm, npm, or yarn)
1321
+
1322
+ ## Install Dependencies
1323
+
1324
+ \`\`\`bash
1325
+ pnpm add @farming-labs/docs @farming-labs/nuxt @farming-labs/nuxt-theme
1326
+ \`\`\`
1327
+
1328
+ ## Configuration
1329
+
1330
+ Your project includes a \`docs.config.ts\` at the root:
1331
+
1332
+ \`\`\`ts title="docs.config.ts"
1333
+ import { defineDocs } from "@farming-labs/docs";
1334
+ import { ${t.factory} } from "${t.nuxtImport}";
1335
+
1336
+ export default defineDocs({
1337
+ entry: "${cfg.entry}",
1338
+ contentDir: "${cfg.entry}",
1339
+ theme: ${t.factory}({
1340
+ ui: { colors: { primary: "#6366f1" } },
1341
+ }),
1342
+ });
1343
+ \`\`\`
1344
+
1345
+ ## Project Structure
1346
+
1347
+ \`\`\`
1348
+ ${cfg.entry}/ # Markdown content
1349
+ page.md
1350
+ installation/page.md
1351
+ quickstart/page.md
1352
+ server/
1353
+ utils/docs-server.ts # createDocsServer + preloaded content
1354
+ api/docs/
1355
+ load.get.ts # Page data API
1356
+ docs.get.ts # Search API
1357
+ docs.post.ts # AI chat API
1358
+ pages/
1359
+ ${cfg.entry}/[[...slug]].vue # Docs catch-all page
1360
+ docs.config.ts
1361
+ nuxt.config.ts
1362
+ \`\`\`
1363
+
1364
+ ## What's Next?
1365
+
1366
+ Head to the [Quickstart](/${cfg.entry}/quickstart) guide to start writing your first page.
1367
+ `;
1368
+ }
1369
+ function nuxtQuickstartPageTemplate(cfg) {
1370
+ const t = getThemeInfo(cfg.theme);
1371
+ return `\
1372
+ ---
1373
+ order: 2
1374
+ title: Quickstart
1375
+ description: Get up and running in minutes
1376
+ icon: rocket
1377
+ ---
1378
+
1379
+ # Quickstart
1380
+
1381
+ This guide walks you through creating your first documentation page.
1382
+
1383
+ ## Creating a Page
1384
+
1385
+ Create a new folder under \`${cfg.entry}/\` with a \`page.md\` file:
1386
+
1387
+ \`\`\`bash
1388
+ mkdir -p ${cfg.entry}/my-page
1389
+ \`\`\`
1390
+
1391
+ Then create \`${cfg.entry}/my-page/page.md\`:
1392
+
1393
+ \`\`\`md
1394
+ ---
1395
+ title: "My Page"
1396
+ description: "A custom documentation page"
1397
+ ---
1398
+
1399
+ # My Page
1400
+
1401
+ Write your content here using **Markdown**.
1402
+ \`\`\`
1403
+
1404
+ Your page is now available at \`/${cfg.entry}/my-page\`.
1405
+
1406
+ ## Customizing the Theme
1407
+
1408
+ Edit \`docs.config.ts\` to change colors and typography:
1409
+
1410
+ \`\`\`ts
1411
+ theme: ${t.factory}({
1412
+ ui: {
1413
+ colors: { primary: "#22c55e" },
1414
+ },
1415
+ }),
1416
+ \`\`\`
1417
+
1418
+ ## Deploying
1419
+
1420
+ Build your docs for production:
1421
+
1422
+ \`\`\`bash
1423
+ pnpm build
1424
+ \`\`\`
1425
+
1426
+ Deploy to Vercel, Netlify, or any Node.js hosting platform.
1427
+ `;
1428
+ }
1429
+ function nuxtGlobalCssTemplate(theme) {
1430
+ return `\
1431
+ @import "@farming-labs/nuxt-theme/${theme}/css";
1432
+ `;
1433
+ }
1434
+ function nuxtCssImportLine(theme) {
1435
+ return `@import "@farming-labs/nuxt-theme/${theme}/css";`;
1436
+ }
1437
+ function injectNuxtCssImport(existingContent, theme) {
1438
+ const importLine = nuxtCssImportLine(theme);
1439
+ if (existingContent.includes(importLine)) return null;
1440
+ const lines = existingContent.split("\n");
1441
+ const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
1442
+ if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
1443
+ else lines.unshift(importLine);
1444
+ return lines.join("\n");
1445
+ }
1110
1446
 
1111
1447
  //#endregion
1112
1448
  //#region src/cli/init.ts
@@ -1115,7 +1451,7 @@ async function init() {
1115
1451
  p.intro(pc.bgCyan(pc.black(" @farming-labs/docs ")));
1116
1452
  let framework = detectFramework(cwd);
1117
1453
  if (framework) {
1118
- const frameworkName = framework === "nextjs" ? "Next.js" : framework === "sveltekit" ? "SvelteKit" : "Astro";
1454
+ const frameworkName = framework === "nextjs" ? "Next.js" : framework === "sveltekit" ? "SvelteKit" : framework === "astro" ? "Astro" : "Nuxt";
1119
1455
  p.log.success(`Detected framework: ${pc.cyan(frameworkName)}`);
1120
1456
  } else {
1121
1457
  p.log.warn("Could not auto-detect a framework from " + pc.cyan("package.json") + ".");
@@ -1136,6 +1472,11 @@ async function init() {
1136
1472
  value: "astro",
1137
1473
  label: "Astro",
1138
1474
  hint: "Content-focused framework with island architecture"
1475
+ },
1476
+ {
1477
+ value: "nuxt",
1478
+ label: "Nuxt",
1479
+ hint: "Vue 3 framework with file-based routing and Nitro server"
1139
1480
  }
1140
1481
  ]
1141
1482
  });
@@ -1162,6 +1503,11 @@ async function init() {
1162
1503
  value: "pixel-border",
1163
1504
  label: "Pixel Border",
1164
1505
  hint: "Rounded borders, pixel-perfect spacing, refined sidebar"
1506
+ },
1507
+ {
1508
+ value: "colorful",
1509
+ label: "Colorful",
1510
+ hint: "Fumadocs-style neutral theme with description support"
1165
1511
  }
1166
1512
  ]
1167
1513
  });
@@ -1169,7 +1515,7 @@ async function init() {
1169
1515
  p.outro(pc.red("Init cancelled."));
1170
1516
  process.exit(0);
1171
1517
  }
1172
- const aliasHint = framework === "nextjs" ? `Uses ${pc.cyan("@/")} prefix (requires tsconfig paths)` : framework === "sveltekit" ? `Uses ${pc.cyan("$lib/")} prefix (SvelteKit built-in)` : `Uses ${pc.cyan("@/")} prefix (requires tsconfig paths)`;
1518
+ 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)`;
1173
1519
  const useAlias = await p.confirm({
1174
1520
  message: `Use path aliases for imports? ${pc.dim(aliasHint)}`,
1175
1521
  initialValue: false
@@ -1226,7 +1572,7 @@ async function init() {
1226
1572
  const entryPath = entry;
1227
1573
  const detectedCssFiles = detectGlobalCssFiles(cwd);
1228
1574
  let globalCssRelPath;
1229
- const defaultCssPath = framework === "sveltekit" ? "src/app.css" : framework === "astro" ? "src/styles/global.css" : "app/globals.css";
1575
+ const defaultCssPath = framework === "sveltekit" ? "src/app.css" : framework === "astro" ? "src/styles/global.css" : framework === "nuxt" ? "assets/css/main.css" : "app/globals.css";
1230
1576
  if (detectedCssFiles.length === 1) {
1231
1577
  globalCssRelPath = detectedCssFiles[0];
1232
1578
  p.log.info(`Found global CSS at ${pc.cyan(globalCssRelPath)}`);
@@ -1279,6 +1625,7 @@ async function init() {
1279
1625
  }
1280
1626
  if (framework === "sveltekit") scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written);
1281
1627
  else if (framework === "astro") scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written);
1628
+ else if (framework === "nuxt") scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written);
1282
1629
  else scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written);
1283
1630
  s.stop("Files scaffolded");
1284
1631
  if (written.length > 0) p.log.success(`Created ${written.length} file${written.length > 1 ? "s" : ""}:\n` + written.map((f) => ` ${pc.green("+")} ${f}`).join("\n"));
@@ -1292,7 +1639,8 @@ async function init() {
1292
1639
  else if (framework === "astro") {
1293
1640
  const adapterPkg = getAstroAdapterPkg(cfg.astroAdapter ?? "vercel");
1294
1641
  exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/astro @farming-labs/astro-theme ${adapterPkg}`, cwd);
1295
- } else {
1642
+ } else if (framework === "nuxt") exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/nuxt @farming-labs/nuxt-theme`, cwd);
1643
+ else {
1296
1644
  exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/next @farming-labs/theme`, cwd);
1297
1645
  const devDeps = [
1298
1646
  "@tailwindcss/postcss",
@@ -1335,6 +1683,10 @@ async function init() {
1335
1683
  cmd: "npx",
1336
1684
  args: ["astro", "dev"],
1337
1685
  waitFor: "ready"
1686
+ } : framework === "nuxt" ? {
1687
+ cmd: "npx",
1688
+ args: ["nuxt", "dev"],
1689
+ waitFor: "Local"
1338
1690
  } : {
1339
1691
  cmd: "npx",
1340
1692
  args: [
@@ -1344,7 +1696,7 @@ async function init() {
1344
1696
  ],
1345
1697
  waitFor: "Ready"
1346
1698
  };
1347
- const defaultPort = framework === "sveltekit" ? "5173" : framework === "astro" ? "4321" : "3000";
1699
+ const defaultPort = framework === "sveltekit" ? "5173" : framework === "astro" ? "4321" : framework === "nuxt" ? "3000" : "3000";
1348
1700
  try {
1349
1701
  const child = await spawnAndWaitFor(devCommand.cmd, devCommand.args, cwd, devCommand.waitFor, 6e4);
1350
1702
  const url = `http://localhost:${defaultPort}/${entryPath}`;
@@ -1363,7 +1715,7 @@ async function init() {
1363
1715
  });
1364
1716
  });
1365
1717
  } catch (err) {
1366
- const manualCmd = framework === "sveltekit" ? "npx vite dev" : framework === "astro" ? "npx astro dev" : "npx next dev --webpack";
1718
+ const manualCmd = framework === "sveltekit" ? "npx vite dev" : framework === "astro" ? "npx astro dev" : framework === "nuxt" ? "npx nuxt dev" : "npx next dev --webpack";
1367
1719
  p.log.error(`Could not start dev server. Try running manually:
1368
1720
  ${pc.cyan(manualCmd)}`);
1369
1721
  p.outro(pc.yellow("Setup complete. Start the server manually."));
@@ -1411,6 +1763,7 @@ function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written)
1411
1763
  fumadocs: "fumadocs",
1412
1764
  darksharp: "darksharp",
1413
1765
  "pixel-border": "pixel-border",
1766
+ colorful: "colorful",
1414
1767
  default: "fumadocs"
1415
1768
  }[cfg.theme] || "fumadocs";
1416
1769
  if (existingGlobalCss) {
@@ -1437,6 +1790,7 @@ function scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written) {
1437
1790
  fumadocs: "fumadocs",
1438
1791
  darksharp: "darksharp",
1439
1792
  "pixel-border": "pixel-border",
1793
+ colorful: "colorful",
1440
1794
  default: "fumadocs"
1441
1795
  }[cfg.theme] || "fumadocs";
1442
1796
  if (existingGlobalCss) {
@@ -1450,6 +1804,34 @@ function scaffoldAstro(cwd, cfg, globalCssRelPath, write, skipped, written) {
1450
1804
  write(`${cfg.entry}/installation/page.md`, astroInstallationPageTemplate(cfg));
1451
1805
  write(`${cfg.entry}/quickstart/page.md`, astroQuickstartPageTemplate(cfg));
1452
1806
  }
1807
+ function scaffoldNuxt(cwd, cfg, globalCssRelPath, write, skipped, written) {
1808
+ write("docs.config.ts", nuxtDocsConfigTemplate(cfg));
1809
+ write("server/utils/docs-server.ts", nuxtDocsServerTemplate(cfg));
1810
+ write("server/api/docs.get.ts", nuxtServerApiDocsGetTemplate());
1811
+ write("server/api/docs.post.ts", nuxtServerApiDocsPostTemplate());
1812
+ write("server/api/docs/load.get.ts", nuxtServerApiDocsLoadTemplate());
1813
+ write(`pages/${cfg.entry}/[[...slug]].vue`, nuxtDocsPageTemplate(cfg));
1814
+ path.join(cwd, "nuxt.config.ts");
1815
+ if (!fileExists(path.join(cwd, "nuxt.config.ts")) && !fileExists(path.join(cwd, "nuxt.config.js"))) write("nuxt.config.ts", nuxtConfigTemplate(cfg));
1816
+ const cssTheme = {
1817
+ fumadocs: "fumadocs",
1818
+ darksharp: "darksharp",
1819
+ "pixel-border": "pixel-border",
1820
+ default: "fumadocs"
1821
+ }[cfg.theme] || "fumadocs";
1822
+ const globalCssAbsPath = path.join(cwd, globalCssRelPath);
1823
+ const existingGlobalCss = readFileSafe(globalCssAbsPath);
1824
+ if (existingGlobalCss) {
1825
+ const injected = injectNuxtCssImport(existingGlobalCss, cssTheme);
1826
+ if (injected) {
1827
+ writeFileSafe(globalCssAbsPath, injected, true);
1828
+ written.push(globalCssRelPath + " (updated)");
1829
+ } else skipped.push(globalCssRelPath + " (already configured)");
1830
+ } else write(globalCssRelPath, nuxtGlobalCssTemplate(cssTheme));
1831
+ write(`${cfg.entry}/page.md`, nuxtWelcomePageTemplate(cfg));
1832
+ write(`${cfg.entry}/installation/page.md`, nuxtInstallationPageTemplate(cfg));
1833
+ write(`${cfg.entry}/quickstart/page.md`, nuxtQuickstartPageTemplate(cfg));
1834
+ }
1453
1835
 
1454
1836
  //#endregion
1455
1837
  //#region src/cli/index.ts
@@ -1476,7 +1858,7 @@ ${pc.dim("Commands:")}
1476
1858
  ${pc.cyan("init")} Scaffold docs in your project (default)
1477
1859
 
1478
1860
  ${pc.dim("Supported frameworks:")}
1479
- Next.js, SvelteKit, Astro
1861
+ Next.js, SvelteKit, Astro, Nuxt
1480
1862
 
1481
1863
  ${pc.dim("Options:")}
1482
1864
  ${pc.cyan("-h, --help")} Show this help message
package/dist/index.d.mts CHANGED
@@ -137,6 +137,13 @@ interface UIConfig {
137
137
  toc?: {
138
138
  enabled?: boolean;
139
139
  depth?: number;
140
+ /**
141
+ * Visual style of the TOC indicator.
142
+ * - `"default"` — highlight active link text color only
143
+ * - `"directional"` — animated thumb bar that tracks active headings (fumadocs style)
144
+ * @default "default"
145
+ */
146
+ style?: "default" | "directional";
140
147
  };
141
148
  header?: {
142
149
  height?: number;
@@ -533,19 +540,44 @@ interface AIConfig {
533
540
  */
534
541
  floatingStyle?: "panel" | "modal" | "popover" | "full-modal";
535
542
  /**
536
- * Custom trigger component for the floating chat button.
543
+ * Custom trigger component for the floating chat button (Next.js only).
537
544
  * Only used when `mode` is `"floating"`.
538
545
  *
539
- * Pass a React element to replace the default sparkles button.
540
- * The element receives an `onClick` handler automatically.
546
+ * The click handler is attached automatically by the wrapper.
547
+ *
548
+ * - **Next.js**: Pass a JSX element via this config option.
549
+ * - **SvelteKit**: Use the `aiTrigger` snippet on `<DocsLayout>`.
550
+ * - **Nuxt / Vue**: Use the `ai-trigger` slot on `<DocsLayout>`.
551
+ * - **Astro**: Use `<MyTrigger slot="ai-trigger" />` on `<DocsLayout>`.
541
552
  *
542
553
  * @example
543
554
  * ```tsx
544
- * ai: {
545
- * enabled: true,
546
- * mode: "floating",
547
- * triggerComponent: <button className="my-chat-btn">💬 Help</button>,
548
- * }
555
+ * // Next.js — pass JSX directly in config
556
+ * triggerComponent: <button className="my-chat-btn">Ask AI</button>,
557
+ * ```
558
+ *
559
+ * ```svelte
560
+ * <!-- SvelteKit — use snippet in layout -->
561
+ * <DocsLayout {tree} {config}>
562
+ * {#snippet aiTrigger()}<AskAITrigger />{/snippet}
563
+ * {@render children()}
564
+ * </DocsLayout>
565
+ * ```
566
+ *
567
+ * ```vue
568
+ * <!-- Nuxt / Vue — use named slot in layout -->
569
+ * <DocsLayout :tree="tree" :config="config">
570
+ * <template #ai-trigger><AskAITrigger /></template>
571
+ * <DocsContent />
572
+ * </DocsLayout>
573
+ * ```
574
+ *
575
+ * ```astro
576
+ * <!-- Astro — use named slot in layout -->
577
+ * <DocsLayout tree={tree} config={config}>
578
+ * <AskAITrigger slot="ai-trigger" />
579
+ * <DocsContent />
580
+ * </DocsLayout>
549
581
  * ```
550
582
  */
551
583
  triggerComponent?: unknown;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@farming-labs/docs",
3
- "version": "0.0.2-beta.15",
3
+ "version": "0.0.2-beta.17",
4
4
  "description": "Modern, flexible MDX-based docs framework — core types, config, and CLI",
5
5
  "type": "module",
6
6
  "main": "./dist/index.mjs",