@farming-labs/docs 0.0.2-beta.3 → 0.0.2-beta.4
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.
- package/dist/cli/index.mjs +387 -63
- package/dist/index.d.mts +272 -2
- package/dist/index.mjs +3 -1
- package/package.json +1 -1
package/dist/cli/index.mjs
CHANGED
|
@@ -10,10 +10,12 @@ function detectFramework(cwd) {
|
|
|
10
10
|
const pkgPath = path.join(cwd, "package.json");
|
|
11
11
|
if (!fs.existsSync(pkgPath)) return null;
|
|
12
12
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf-8"));
|
|
13
|
-
|
|
13
|
+
const allDeps = {
|
|
14
14
|
...pkg.dependencies,
|
|
15
15
|
...pkg.devDependencies
|
|
16
|
-
}
|
|
16
|
+
};
|
|
17
|
+
if (allDeps["next"]) return "nextjs";
|
|
18
|
+
if (allDeps["@sveltejs/kit"]) return "sveltekit";
|
|
17
19
|
return null;
|
|
18
20
|
}
|
|
19
21
|
function detectPackageManager(cwd) {
|
|
@@ -53,12 +55,13 @@ function readFileSafe(filePath) {
|
|
|
53
55
|
if (!fs.existsSync(filePath)) return null;
|
|
54
56
|
return fs.readFileSync(filePath, "utf-8");
|
|
55
57
|
}
|
|
56
|
-
/** Common locations where global CSS files live in Next.js projects. */
|
|
58
|
+
/** Common locations where global CSS files live in Next.js / SvelteKit projects. */
|
|
57
59
|
const GLOBAL_CSS_CANDIDATES = [
|
|
58
60
|
"app/globals.css",
|
|
59
61
|
"app/global.css",
|
|
60
62
|
"src/app/globals.css",
|
|
61
63
|
"src/app/global.css",
|
|
64
|
+
"src/app.css",
|
|
62
65
|
"styles/globals.css",
|
|
63
66
|
"styles/global.css",
|
|
64
67
|
"src/styles/globals.css",
|
|
@@ -128,7 +131,7 @@ function spawnAndWaitFor(command, args, cwd, waitFor, timeoutMs = 6e4) {
|
|
|
128
131
|
function docsConfigTemplate(cfg) {
|
|
129
132
|
return `\
|
|
130
133
|
import { defineDocs } from "@farming-labs/docs";
|
|
131
|
-
import { fumadocs } from "@farming-labs/
|
|
134
|
+
import { fumadocs } from "@farming-labs/theme";
|
|
132
135
|
|
|
133
136
|
export default defineDocs({
|
|
134
137
|
entry: "${cfg.entry}",
|
|
@@ -176,7 +179,7 @@ function rootLayoutTemplate(globalCssRelPath = "app/globals.css") {
|
|
|
176
179
|
else cssImport = "../" + globalCssRelPath;
|
|
177
180
|
return `\
|
|
178
181
|
import type { Metadata } from "next";
|
|
179
|
-
import { RootProvider } from "@farming-labs/
|
|
182
|
+
import { RootProvider } from "@farming-labs/theme";
|
|
180
183
|
import docsConfig from "@/docs.config";
|
|
181
184
|
import "${cssImport}";
|
|
182
185
|
|
|
@@ -225,7 +228,7 @@ function injectCssImport(existingContent, theme) {
|
|
|
225
228
|
function docsLayoutTemplate() {
|
|
226
229
|
return `\
|
|
227
230
|
import docsConfig from "@/docs.config";
|
|
228
|
-
import { createDocsLayout } from "@farming-labs/
|
|
231
|
+
import { createDocsLayout } from "@farming-labs/theme";
|
|
229
232
|
|
|
230
233
|
export default createDocsLayout(docsConfig);
|
|
231
234
|
`;
|
|
@@ -330,7 +333,7 @@ Your project includes a \`docs.config.ts\` at the root:
|
|
|
330
333
|
|
|
331
334
|
\`\`\`ts
|
|
332
335
|
import { defineDocs } from "@farming-labs/docs";
|
|
333
|
-
import { fumadocs } from "@farming-labs/
|
|
336
|
+
import { fumadocs } from "@farming-labs/theme";
|
|
334
337
|
|
|
335
338
|
export default defineDocs({
|
|
336
339
|
entry: "${cfg.entry}",
|
|
@@ -438,6 +441,268 @@ Build your docs for production:
|
|
|
438
441
|
pnpm build
|
|
439
442
|
\`\`\`
|
|
440
443
|
|
|
444
|
+
Deploy to Vercel, Netlify, or any Node.js hosting platform.
|
|
445
|
+
`;
|
|
446
|
+
}
|
|
447
|
+
function svelteDocsConfigTemplate(cfg) {
|
|
448
|
+
return `\
|
|
449
|
+
import { defineDocs } from "@farming-labs/docs";
|
|
450
|
+
import { fumadocs } from "@farming-labs/svelte-theme";
|
|
451
|
+
|
|
452
|
+
export default defineDocs({
|
|
453
|
+
entry: "${cfg.entry}",
|
|
454
|
+
contentDir: "${cfg.entry}",
|
|
455
|
+
theme: fumadocs({
|
|
456
|
+
ui: {
|
|
457
|
+
colors: { primary: "#6366f1" },
|
|
458
|
+
},
|
|
459
|
+
}),
|
|
460
|
+
|
|
461
|
+
nav: {
|
|
462
|
+
title: "${cfg.projectName}",
|
|
463
|
+
url: "/${cfg.entry}",
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
breadcrumb: { enabled: true },
|
|
467
|
+
|
|
468
|
+
metadata: {
|
|
469
|
+
titleTemplate: "%s – ${cfg.projectName}",
|
|
470
|
+
description: "Documentation for ${cfg.projectName}",
|
|
471
|
+
},
|
|
472
|
+
});
|
|
473
|
+
`;
|
|
474
|
+
}
|
|
475
|
+
function svelteDocsServerTemplate(cfg) {
|
|
476
|
+
return `\
|
|
477
|
+
import { createDocsServer } from "@farming-labs/svelte/server";
|
|
478
|
+
import config from "../../docs.config.js";
|
|
479
|
+
|
|
480
|
+
export const { load, GET, POST } = createDocsServer(config);
|
|
481
|
+
`;
|
|
482
|
+
}
|
|
483
|
+
function svelteDocsLayoutTemplate(cfg) {
|
|
484
|
+
return `\
|
|
485
|
+
<script>
|
|
486
|
+
import { DocsLayout } from "@farming-labs/svelte-theme";
|
|
487
|
+
import config from "../../../docs.config.js";
|
|
488
|
+
|
|
489
|
+
let { data, children } = $props();
|
|
490
|
+
<\/script>
|
|
491
|
+
|
|
492
|
+
<DocsLayout tree={data.tree} {config}>
|
|
493
|
+
{@render children()}
|
|
494
|
+
</DocsLayout>
|
|
495
|
+
`;
|
|
496
|
+
}
|
|
497
|
+
function svelteDocsLayoutServerTemplate() {
|
|
498
|
+
return `\
|
|
499
|
+
export { load } from "$lib/docs.server.js";
|
|
500
|
+
`;
|
|
501
|
+
}
|
|
502
|
+
function svelteDocsPageTemplate(cfg) {
|
|
503
|
+
return `\
|
|
504
|
+
<script>
|
|
505
|
+
import { DocsContent } from "@farming-labs/svelte-theme";
|
|
506
|
+
import config from "../../../../docs.config.js";
|
|
507
|
+
|
|
508
|
+
let { data } = $props();
|
|
509
|
+
<\/script>
|
|
510
|
+
|
|
511
|
+
<DocsContent {data} {config} />
|
|
512
|
+
`;
|
|
513
|
+
}
|
|
514
|
+
function svelteRootLayoutTemplate(globalCssRelPath) {
|
|
515
|
+
let cssImport;
|
|
516
|
+
if (globalCssRelPath.startsWith("src/")) cssImport = "./" + globalCssRelPath.slice(4);
|
|
517
|
+
else cssImport = "../" + globalCssRelPath;
|
|
518
|
+
return `\
|
|
519
|
+
<script>
|
|
520
|
+
import "${cssImport}";
|
|
521
|
+
|
|
522
|
+
let { children } = $props();
|
|
523
|
+
<\/script>
|
|
524
|
+
|
|
525
|
+
{@render children()}
|
|
526
|
+
`;
|
|
527
|
+
}
|
|
528
|
+
function svelteGlobalCssTemplate(theme) {
|
|
529
|
+
return `\
|
|
530
|
+
@import "@farming-labs/svelte-theme/${theme}/css";
|
|
531
|
+
`;
|
|
532
|
+
}
|
|
533
|
+
function svelteCssImportLine(theme) {
|
|
534
|
+
return `@import "@farming-labs/svelte-theme/${theme}/css";`;
|
|
535
|
+
}
|
|
536
|
+
function injectSvelteCssImport(existingContent, theme) {
|
|
537
|
+
const importLine = svelteCssImportLine(theme);
|
|
538
|
+
if (existingContent.includes(importLine)) return null;
|
|
539
|
+
const lines = existingContent.split("\n");
|
|
540
|
+
const lastImportIdx = lines.reduce((acc, l, i) => l.trimStart().startsWith("@import") ? i : acc, -1);
|
|
541
|
+
if (lastImportIdx >= 0) lines.splice(lastImportIdx + 1, 0, importLine);
|
|
542
|
+
else lines.unshift(importLine);
|
|
543
|
+
return lines.join("\n");
|
|
544
|
+
}
|
|
545
|
+
function svelteWelcomePageTemplate(cfg) {
|
|
546
|
+
return `\
|
|
547
|
+
---
|
|
548
|
+
title: "Documentation"
|
|
549
|
+
description: "Welcome to ${cfg.projectName} documentation"
|
|
550
|
+
---
|
|
551
|
+
|
|
552
|
+
# Welcome to ${cfg.projectName}
|
|
553
|
+
|
|
554
|
+
Get started with our documentation. Browse the pages on the left to learn more.
|
|
555
|
+
|
|
556
|
+
## Overview
|
|
557
|
+
|
|
558
|
+
This documentation was generated by \`@farming-labs/docs\`. Edit the markdown files in \`${cfg.entry}/\` to customize.
|
|
559
|
+
|
|
560
|
+
## Features
|
|
561
|
+
|
|
562
|
+
- **Markdown Support** — Write docs with standard Markdown
|
|
563
|
+
- **Syntax Highlighting** — Code blocks with automatic highlighting
|
|
564
|
+
- **Dark Mode** — Built-in theme switching
|
|
565
|
+
- **Search** — Full-text search across all pages
|
|
566
|
+
- **Responsive** — Works on any screen size
|
|
567
|
+
|
|
568
|
+
---
|
|
569
|
+
|
|
570
|
+
## Next Steps
|
|
571
|
+
|
|
572
|
+
Start by reading the [Installation](/${cfg.entry}/installation) guide, then follow the [Quickstart](/${cfg.entry}/quickstart) to build something.
|
|
573
|
+
`;
|
|
574
|
+
}
|
|
575
|
+
function svelteInstallationPageTemplate(cfg) {
|
|
576
|
+
return `\
|
|
577
|
+
---
|
|
578
|
+
title: "Installation"
|
|
579
|
+
description: "How to install and set up ${cfg.projectName}"
|
|
580
|
+
---
|
|
581
|
+
|
|
582
|
+
# Installation
|
|
583
|
+
|
|
584
|
+
Follow these steps to install and configure ${cfg.projectName}.
|
|
585
|
+
|
|
586
|
+
## Prerequisites
|
|
587
|
+
|
|
588
|
+
- Node.js 18+
|
|
589
|
+
- A package manager (pnpm, npm, or yarn)
|
|
590
|
+
|
|
591
|
+
## Install Dependencies
|
|
592
|
+
|
|
593
|
+
\`\`\`bash
|
|
594
|
+
pnpm add @farming-labs/docs @farming-labs/svelte @farming-labs/svelte-theme
|
|
595
|
+
\`\`\`
|
|
596
|
+
|
|
597
|
+
## Configuration
|
|
598
|
+
|
|
599
|
+
Your project includes a \`docs.config.ts\` at the root:
|
|
600
|
+
|
|
601
|
+
\`\`\`ts title="docs.config.ts"
|
|
602
|
+
import { defineDocs } from "@farming-labs/docs";
|
|
603
|
+
import { fumadocs } from "@farming-labs/svelte-theme";
|
|
604
|
+
|
|
605
|
+
export default defineDocs({
|
|
606
|
+
entry: "${cfg.entry}",
|
|
607
|
+
contentDir: "${cfg.entry}",
|
|
608
|
+
theme: fumadocs({
|
|
609
|
+
ui: { colors: { primary: "#6366f1" } },
|
|
610
|
+
}),
|
|
611
|
+
});
|
|
612
|
+
\`\`\`
|
|
613
|
+
|
|
614
|
+
## Project Structure
|
|
615
|
+
|
|
616
|
+
\`\`\`
|
|
617
|
+
${cfg.entry}/ # Markdown content
|
|
618
|
+
page.md # /${cfg.entry}
|
|
619
|
+
installation/
|
|
620
|
+
page.md # /${cfg.entry}/installation
|
|
621
|
+
quickstart/
|
|
622
|
+
page.md # /${cfg.entry}/quickstart
|
|
623
|
+
src/
|
|
624
|
+
lib/
|
|
625
|
+
docs.server.ts # Server-side docs loader
|
|
626
|
+
routes/
|
|
627
|
+
${cfg.entry}/
|
|
628
|
+
+layout.svelte # Docs layout
|
|
629
|
+
+layout.server.js # Layout data loader
|
|
630
|
+
[...slug]/
|
|
631
|
+
+page.svelte # Dynamic doc page
|
|
632
|
+
docs.config.ts # Docs configuration
|
|
633
|
+
\`\`\`
|
|
634
|
+
|
|
635
|
+
## What's Next?
|
|
636
|
+
|
|
637
|
+
Head to the [Quickstart](/${cfg.entry}/quickstart) guide to start writing your first page.
|
|
638
|
+
`;
|
|
639
|
+
}
|
|
640
|
+
function svelteQuickstartPageTemplate(cfg) {
|
|
641
|
+
return `\
|
|
642
|
+
---
|
|
643
|
+
title: "Quickstart"
|
|
644
|
+
description: "Get up and running in minutes"
|
|
645
|
+
---
|
|
646
|
+
|
|
647
|
+
# Quickstart
|
|
648
|
+
|
|
649
|
+
This guide walks you through creating your first documentation page.
|
|
650
|
+
|
|
651
|
+
## Creating a Page
|
|
652
|
+
|
|
653
|
+
Create a new folder under \`${cfg.entry}/\` with a \`page.md\` file:
|
|
654
|
+
|
|
655
|
+
\`\`\`bash
|
|
656
|
+
mkdir -p ${cfg.entry}/my-page
|
|
657
|
+
\`\`\`
|
|
658
|
+
|
|
659
|
+
Then create \`${cfg.entry}/my-page/page.md\`:
|
|
660
|
+
|
|
661
|
+
\`\`\`md
|
|
662
|
+
---
|
|
663
|
+
title: "My Page"
|
|
664
|
+
description: "A custom documentation page"
|
|
665
|
+
---
|
|
666
|
+
|
|
667
|
+
# My Page
|
|
668
|
+
|
|
669
|
+
Write your content here using **Markdown**.
|
|
670
|
+
\`\`\`
|
|
671
|
+
|
|
672
|
+
Your page is now available at \`/${cfg.entry}/my-page\`.
|
|
673
|
+
|
|
674
|
+
## Code Blocks
|
|
675
|
+
|
|
676
|
+
Code blocks are automatically syntax-highlighted:
|
|
677
|
+
|
|
678
|
+
\`\`\`typescript
|
|
679
|
+
function greet(name: string): string {
|
|
680
|
+
return \\\`Hello, \\\${name}!\\\`;
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
console.log(greet("World"));
|
|
684
|
+
\`\`\`
|
|
685
|
+
|
|
686
|
+
## Customizing the Theme
|
|
687
|
+
|
|
688
|
+
Edit \`docs.config.ts\` to change colors, typography, and component defaults:
|
|
689
|
+
|
|
690
|
+
\`\`\`ts title="docs.config.ts"
|
|
691
|
+
theme: fumadocs({
|
|
692
|
+
ui: {
|
|
693
|
+
colors: { primary: "#22c55e" },
|
|
694
|
+
},
|
|
695
|
+
}),
|
|
696
|
+
\`\`\`
|
|
697
|
+
|
|
698
|
+
## Deploying
|
|
699
|
+
|
|
700
|
+
Build your docs for production:
|
|
701
|
+
|
|
702
|
+
\`\`\`bash
|
|
703
|
+
pnpm build
|
|
704
|
+
\`\`\`
|
|
705
|
+
|
|
441
706
|
Deploy to Vercel, Netlify, or any Node.js hosting platform.
|
|
442
707
|
`;
|
|
443
708
|
}
|
|
@@ -447,19 +712,30 @@ Deploy to Vercel, Netlify, or any Node.js hosting platform.
|
|
|
447
712
|
async function init() {
|
|
448
713
|
const cwd = process.cwd();
|
|
449
714
|
p.intro(pc.bgCyan(pc.black(" @farming-labs/docs ")));
|
|
450
|
-
|
|
451
|
-
|
|
715
|
+
const framework = detectFramework(cwd);
|
|
716
|
+
if (!framework) {
|
|
717
|
+
p.log.error("Could not detect a supported framework.\n Make sure you have a " + pc.cyan("package.json") + " with " + pc.cyan("next") + " or " + pc.cyan("@sveltejs/kit") + " installed.\n Supported frameworks: Next.js, SvelteKit");
|
|
452
718
|
p.outro(pc.red("Init cancelled."));
|
|
453
719
|
process.exit(1);
|
|
454
720
|
}
|
|
455
|
-
|
|
721
|
+
const frameworkName = framework === "nextjs" ? "Next.js" : "SvelteKit";
|
|
722
|
+
p.log.success(`Detected framework: ${pc.cyan(frameworkName)}`);
|
|
723
|
+
const themeOptions = framework === "sveltekit" ? [{
|
|
724
|
+
value: "default",
|
|
725
|
+
label: "Default",
|
|
726
|
+
hint: "Clean, modern docs theme with sidebar, search, and dark mode"
|
|
727
|
+
}, {
|
|
728
|
+
value: "pixel-border",
|
|
729
|
+
label: "Pixel Border",
|
|
730
|
+
hint: "Sharp pixel-art inspired theme with monospace text"
|
|
731
|
+
}] : [{
|
|
732
|
+
value: "fumadocs",
|
|
733
|
+
label: "Fumadocs",
|
|
734
|
+
hint: "Clean, modern docs theme with sidebar, search, and dark mode"
|
|
735
|
+
}];
|
|
456
736
|
const theme = await p.select({
|
|
457
737
|
message: "Which theme would you like to use?",
|
|
458
|
-
options:
|
|
459
|
-
value: "fumadocs",
|
|
460
|
-
label: "Fumadocs",
|
|
461
|
-
hint: "Clean, modern docs theme with sidebar, search, and dark mode"
|
|
462
|
-
}]
|
|
738
|
+
options: themeOptions
|
|
463
739
|
});
|
|
464
740
|
if (p.isCancel(theme)) {
|
|
465
741
|
p.outro(pc.red("Init cancelled."));
|
|
@@ -482,6 +758,7 @@ async function init() {
|
|
|
482
758
|
const entryPath = entry;
|
|
483
759
|
const detectedCssFiles = detectGlobalCssFiles(cwd);
|
|
484
760
|
let globalCssRelPath;
|
|
761
|
+
const defaultCssPath = framework === "sveltekit" ? "src/app.css" : "app/globals.css";
|
|
485
762
|
if (detectedCssFiles.length === 1) {
|
|
486
763
|
globalCssRelPath = detectedCssFiles[0];
|
|
487
764
|
p.log.info(`Found global CSS at ${pc.cyan(globalCssRelPath)}`);
|
|
@@ -501,8 +778,8 @@ async function init() {
|
|
|
501
778
|
} else {
|
|
502
779
|
const cssPath = await p.text({
|
|
503
780
|
message: "Where is your global CSS file?",
|
|
504
|
-
placeholder:
|
|
505
|
-
defaultValue:
|
|
781
|
+
placeholder: defaultCssPath,
|
|
782
|
+
defaultValue: defaultCssPath,
|
|
506
783
|
validate: (value) => {
|
|
507
784
|
if (!value) return "CSS file path is required";
|
|
508
785
|
if (!value.endsWith(".css")) return "Path must end with .css";
|
|
@@ -518,7 +795,8 @@ async function init() {
|
|
|
518
795
|
const cfg = {
|
|
519
796
|
entry: entryPath,
|
|
520
797
|
theme,
|
|
521
|
-
projectName: pkgJson.name || "My Project"
|
|
798
|
+
projectName: pkgJson.name || "My Project",
|
|
799
|
+
framework
|
|
522
800
|
};
|
|
523
801
|
const s = p.spinner();
|
|
524
802
|
s.start("Scaffolding docs files");
|
|
@@ -528,32 +806,8 @@ async function init() {
|
|
|
528
806
|
if (writeFileSafe(path.join(cwd, rel), content, overwrite)) written.push(rel);
|
|
529
807
|
else skipped.push(rel);
|
|
530
808
|
}
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
if (existingNextConfig) {
|
|
534
|
-
const configFile = fileExists(path.join(cwd, "next.config.ts")) ? "next.config.ts" : fileExists(path.join(cwd, "next.config.mjs")) ? "next.config.mjs" : "next.config.js";
|
|
535
|
-
const merged = nextConfigMergedTemplate(existingNextConfig);
|
|
536
|
-
if (merged !== existingNextConfig) {
|
|
537
|
-
writeFileSafe(path.join(cwd, configFile), merged, true);
|
|
538
|
-
written.push(configFile + " (updated)");
|
|
539
|
-
} else skipped.push(configFile + " (already configured)");
|
|
540
|
-
} else write("next.config.ts", nextConfigTemplate());
|
|
541
|
-
write("app/layout.tsx", rootLayoutTemplate(globalCssRelPath));
|
|
542
|
-
const globalCssAbsPath = path.join(cwd, globalCssRelPath);
|
|
543
|
-
const existingGlobalCss = readFileSafe(globalCssAbsPath);
|
|
544
|
-
if (existingGlobalCss) {
|
|
545
|
-
const injected = injectCssImport(existingGlobalCss, theme);
|
|
546
|
-
if (injected) {
|
|
547
|
-
writeFileSafe(globalCssAbsPath, injected, true);
|
|
548
|
-
written.push(globalCssRelPath + " (updated)");
|
|
549
|
-
} else skipped.push(globalCssRelPath + " (already configured)");
|
|
550
|
-
} else write(globalCssRelPath, globalCssTemplate(theme));
|
|
551
|
-
write(`app/${entryPath}/layout.tsx`, docsLayoutTemplate());
|
|
552
|
-
write("postcss.config.mjs", postcssConfigTemplate());
|
|
553
|
-
if (!fileExists(path.join(cwd, "tsconfig.json"))) write("tsconfig.json", tsconfigTemplate());
|
|
554
|
-
write(`app/${entryPath}/page.mdx`, welcomePageTemplate(cfg));
|
|
555
|
-
write(`app/${entryPath}/installation/page.mdx`, installationPageTemplate(cfg));
|
|
556
|
-
write(`app/${entryPath}/quickstart/page.mdx`, quickstartPageTemplate(cfg));
|
|
809
|
+
if (framework === "sveltekit") scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written);
|
|
810
|
+
else scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written);
|
|
557
811
|
s.stop("Files scaffolded");
|
|
558
812
|
if (written.length > 0) p.log.success(`Created ${written.length} file${written.length > 1 ? "s" : ""}:\n` + written.map((f) => ` ${pc.green("+")} ${f}`).join("\n"));
|
|
559
813
|
if (skipped.length > 0) p.log.info(`Skipped ${skipped.length} existing file${skipped.length > 1 ? "s" : ""}:\n` + skipped.map((f) => ` ${pc.dim("-")} ${f}`).join("\n"));
|
|
@@ -562,20 +816,23 @@ async function init() {
|
|
|
562
816
|
const s2 = p.spinner();
|
|
563
817
|
s2.start("Installing dependencies");
|
|
564
818
|
try {
|
|
565
|
-
exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
819
|
+
if (framework === "sveltekit") exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/svelte @farming-labs/svelte-theme`, cwd);
|
|
820
|
+
else {
|
|
821
|
+
exec(`${installCommand(pm)} @farming-labs/docs @farming-labs/next @farming-labs/theme`, cwd);
|
|
822
|
+
const devDeps = [
|
|
823
|
+
"@tailwindcss/postcss",
|
|
824
|
+
"postcss",
|
|
825
|
+
"tailwindcss",
|
|
826
|
+
"@types/mdx",
|
|
827
|
+
"@types/node"
|
|
828
|
+
];
|
|
829
|
+
const allDeps = {
|
|
830
|
+
...pkgJson.dependencies,
|
|
831
|
+
...pkgJson.devDependencies
|
|
832
|
+
};
|
|
833
|
+
const missingDevDeps = devDeps.filter((d) => !allDeps[d]);
|
|
834
|
+
if (missingDevDeps.length > 0) exec(`${devInstallCommand(pm)} ${missingDevDeps.join(" ")}`, cwd);
|
|
835
|
+
}
|
|
579
836
|
} catch {
|
|
580
837
|
s2.stop("Failed to install dependencies");
|
|
581
838
|
p.log.error(`Dependency installation failed. Run the install command manually:
|
|
@@ -595,13 +852,23 @@ async function init() {
|
|
|
595
852
|
process.exit(0);
|
|
596
853
|
}
|
|
597
854
|
p.log.step("Starting dev server...");
|
|
598
|
-
|
|
599
|
-
|
|
855
|
+
const devCommand = framework === "sveltekit" ? {
|
|
856
|
+
cmd: "npx",
|
|
857
|
+
args: ["vite", "dev"],
|
|
858
|
+
waitFor: "ready"
|
|
859
|
+
} : {
|
|
860
|
+
cmd: "npx",
|
|
861
|
+
args: [
|
|
600
862
|
"next",
|
|
601
863
|
"dev",
|
|
602
864
|
"--webpack"
|
|
603
|
-
],
|
|
604
|
-
|
|
865
|
+
],
|
|
866
|
+
waitFor: "Ready"
|
|
867
|
+
};
|
|
868
|
+
const defaultPort = framework === "sveltekit" ? "5173" : "3000";
|
|
869
|
+
try {
|
|
870
|
+
const child = await spawnAndWaitFor(devCommand.cmd, devCommand.args, cwd, devCommand.waitFor, 6e4);
|
|
871
|
+
const url = `http://localhost:${defaultPort}/${entryPath}`;
|
|
605
872
|
console.log();
|
|
606
873
|
p.log.success(`Dev server is running! Your docs are live at:\n\n ${pc.cyan(pc.underline(url))}\n\n Press ${pc.dim("Ctrl+C")} to stop the server.`);
|
|
607
874
|
p.outro(pc.green("Happy documenting!"));
|
|
@@ -617,12 +884,66 @@ async function init() {
|
|
|
617
884
|
});
|
|
618
885
|
});
|
|
619
886
|
} catch (err) {
|
|
887
|
+
const manualCmd = framework === "sveltekit" ? "npx vite dev" : "npx next dev --webpack";
|
|
620
888
|
p.log.error(`Could not start dev server. Try running manually:
|
|
621
|
-
${pc.cyan(
|
|
889
|
+
${pc.cyan(manualCmd)}`);
|
|
622
890
|
p.outro(pc.yellow("Setup complete. Start the server manually."));
|
|
623
891
|
process.exit(1);
|
|
624
892
|
}
|
|
625
893
|
}
|
|
894
|
+
function scaffoldNextJs(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
895
|
+
write("docs.config.ts", docsConfigTemplate(cfg));
|
|
896
|
+
const existingNextConfig = readFileSafe(path.join(cwd, "next.config.ts")) ?? readFileSafe(path.join(cwd, "next.config.mjs")) ?? readFileSafe(path.join(cwd, "next.config.js"));
|
|
897
|
+
if (existingNextConfig) {
|
|
898
|
+
const configFile = fileExists(path.join(cwd, "next.config.ts")) ? "next.config.ts" : fileExists(path.join(cwd, "next.config.mjs")) ? "next.config.mjs" : "next.config.js";
|
|
899
|
+
const merged = nextConfigMergedTemplate(existingNextConfig);
|
|
900
|
+
if (merged !== existingNextConfig) {
|
|
901
|
+
writeFileSafe(path.join(cwd, configFile), merged, true);
|
|
902
|
+
written.push(configFile + " (updated)");
|
|
903
|
+
} else skipped.push(configFile + " (already configured)");
|
|
904
|
+
} else write("next.config.ts", nextConfigTemplate());
|
|
905
|
+
write("app/layout.tsx", rootLayoutTemplate(globalCssRelPath));
|
|
906
|
+
const globalCssAbsPath = path.join(cwd, globalCssRelPath);
|
|
907
|
+
const existingGlobalCss = readFileSafe(globalCssAbsPath);
|
|
908
|
+
if (existingGlobalCss) {
|
|
909
|
+
const injected = injectCssImport(existingGlobalCss, cfg.theme);
|
|
910
|
+
if (injected) {
|
|
911
|
+
writeFileSafe(globalCssAbsPath, injected, true);
|
|
912
|
+
written.push(globalCssRelPath + " (updated)");
|
|
913
|
+
} else skipped.push(globalCssRelPath + " (already configured)");
|
|
914
|
+
} else write(globalCssRelPath, globalCssTemplate(cfg.theme));
|
|
915
|
+
write(`app/${cfg.entry}/layout.tsx`, docsLayoutTemplate());
|
|
916
|
+
write("postcss.config.mjs", postcssConfigTemplate());
|
|
917
|
+
if (!fileExists(path.join(cwd, "tsconfig.json"))) write("tsconfig.json", tsconfigTemplate());
|
|
918
|
+
write(`app/${cfg.entry}/page.mdx`, welcomePageTemplate(cfg));
|
|
919
|
+
write(`app/${cfg.entry}/installation/page.mdx`, installationPageTemplate(cfg));
|
|
920
|
+
write(`app/${cfg.entry}/quickstart/page.mdx`, quickstartPageTemplate(cfg));
|
|
921
|
+
}
|
|
922
|
+
function scaffoldSvelteKit(cwd, cfg, globalCssRelPath, write, skipped, written) {
|
|
923
|
+
write("docs.config.ts", svelteDocsConfigTemplate(cfg));
|
|
924
|
+
write("src/lib/docs.server.ts", svelteDocsServerTemplate(cfg));
|
|
925
|
+
write(`src/routes/${cfg.entry}/+layout.svelte`, svelteDocsLayoutTemplate(cfg));
|
|
926
|
+
write(`src/routes/${cfg.entry}/+layout.server.js`, svelteDocsLayoutServerTemplate());
|
|
927
|
+
write(`src/routes/${cfg.entry}/[...slug]/+page.svelte`, svelteDocsPageTemplate(cfg));
|
|
928
|
+
if (!readFileSafe(path.join(cwd, "src/routes/+layout.svelte"))) write("src/routes/+layout.svelte", svelteRootLayoutTemplate(globalCssRelPath));
|
|
929
|
+
const globalCssAbsPath = path.join(cwd, globalCssRelPath);
|
|
930
|
+
const existingGlobalCss = readFileSafe(globalCssAbsPath);
|
|
931
|
+
const cssTheme = {
|
|
932
|
+
default: "fumadocs",
|
|
933
|
+
"pixel-border": "pixel-border",
|
|
934
|
+
fumadocs: "fumadocs"
|
|
935
|
+
}[cfg.theme] || "fumadocs";
|
|
936
|
+
if (existingGlobalCss) {
|
|
937
|
+
const injected = injectSvelteCssImport(existingGlobalCss, cssTheme);
|
|
938
|
+
if (injected) {
|
|
939
|
+
writeFileSafe(globalCssAbsPath, injected, true);
|
|
940
|
+
written.push(globalCssRelPath + " (updated)");
|
|
941
|
+
} else skipped.push(globalCssRelPath + " (already configured)");
|
|
942
|
+
} else write(globalCssRelPath, svelteGlobalCssTemplate(cssTheme));
|
|
943
|
+
write(`${cfg.entry}/page.md`, svelteWelcomePageTemplate(cfg));
|
|
944
|
+
write(`${cfg.entry}/installation/page.md`, svelteInstallationPageTemplate(cfg));
|
|
945
|
+
write(`${cfg.entry}/quickstart/page.md`, svelteQuickstartPageTemplate(cfg));
|
|
946
|
+
}
|
|
626
947
|
|
|
627
948
|
//#endregion
|
|
628
949
|
//#region src/cli/index.ts
|
|
@@ -648,6 +969,9 @@ ${pc.dim("Usage:")}
|
|
|
648
969
|
${pc.dim("Commands:")}
|
|
649
970
|
${pc.cyan("init")} Scaffold docs in your project (default)
|
|
650
971
|
|
|
972
|
+
${pc.dim("Supported frameworks:")}
|
|
973
|
+
Next.js, SvelteKit
|
|
974
|
+
|
|
651
975
|
${pc.dim("Options:")}
|
|
652
976
|
${pc.cyan("-h, --help")} Show this help message
|
|
653
977
|
${pc.cyan("-v, --version")} Show version
|
package/dist/index.d.mts
CHANGED
|
@@ -442,9 +442,246 @@ interface GithubConfig {
|
|
|
442
442
|
*/
|
|
443
443
|
directory?: string;
|
|
444
444
|
}
|
|
445
|
+
/**
|
|
446
|
+
* Configuration for "Ask AI" — a RAG-powered chat that lets users
|
|
447
|
+
* ask questions about the documentation content.
|
|
448
|
+
*
|
|
449
|
+
* The AI handler searches relevant doc pages, builds context, and
|
|
450
|
+
* streams a response from an LLM (OpenAI-compatible API).
|
|
451
|
+
*
|
|
452
|
+
* The API key is **never** stored in the config. It is read from the
|
|
453
|
+
* `OPENAI_API_KEY` environment variable at runtime on the server.
|
|
454
|
+
*
|
|
455
|
+
* @example
|
|
456
|
+
* ```ts
|
|
457
|
+
* ai: {
|
|
458
|
+
* enabled: true,
|
|
459
|
+
* model: "gpt-4o-mini",
|
|
460
|
+
* systemPrompt: "You are a helpful assistant for our developer docs.",
|
|
461
|
+
* }
|
|
462
|
+
* ```
|
|
463
|
+
*/
|
|
464
|
+
interface AIConfig {
|
|
465
|
+
/**
|
|
466
|
+
* Whether to enable "Ask AI" functionality.
|
|
467
|
+
* When enabled, the unified `/api/docs` route handler will accept
|
|
468
|
+
* POST requests for AI chat.
|
|
469
|
+
* @default false
|
|
470
|
+
*/
|
|
471
|
+
enabled?: boolean;
|
|
472
|
+
/**
|
|
473
|
+
* How the AI chat UI is presented.
|
|
474
|
+
*
|
|
475
|
+
* - `"search"` — AI tab integrated into the Cmd+K search dialog (default)
|
|
476
|
+
* - `"floating"` — A floating chat widget (bubble button + slide-out panel)
|
|
477
|
+
*
|
|
478
|
+
* @default "search"
|
|
479
|
+
*
|
|
480
|
+
* @example
|
|
481
|
+
* ```ts
|
|
482
|
+
* // Floating chat bubble in the bottom-right corner
|
|
483
|
+
* ai: {
|
|
484
|
+
* enabled: true,
|
|
485
|
+
* mode: "floating",
|
|
486
|
+
* position: "bottom-right",
|
|
487
|
+
* }
|
|
488
|
+
* ```
|
|
489
|
+
*/
|
|
490
|
+
mode?: "search" | "floating";
|
|
491
|
+
/**
|
|
492
|
+
* Position of the floating chat button on screen.
|
|
493
|
+
* Only used when `mode` is `"floating"`.
|
|
494
|
+
*
|
|
495
|
+
* - `"bottom-right"` — bottom-right corner (default)
|
|
496
|
+
* - `"bottom-left"` — bottom-left corner
|
|
497
|
+
* - `"bottom-center"` — bottom center
|
|
498
|
+
*
|
|
499
|
+
* @default "bottom-right"
|
|
500
|
+
*/
|
|
501
|
+
position?: "bottom-right" | "bottom-left" | "bottom-center";
|
|
502
|
+
/**
|
|
503
|
+
* Visual style of the floating chat when opened.
|
|
504
|
+
* Only used when `mode` is `"floating"`.
|
|
505
|
+
*
|
|
506
|
+
* - `"panel"` — A tall panel that slides up from the button position (default).
|
|
507
|
+
* Stays anchored near the floating button. No backdrop overlay.
|
|
508
|
+
*
|
|
509
|
+
* - `"modal"` — A centered modal dialog with a backdrop overlay,
|
|
510
|
+
* similar to the Cmd+K search dialog. Feels more focused and immersive.
|
|
511
|
+
*
|
|
512
|
+
* - `"popover"` — A compact popover near the button. Smaller than the
|
|
513
|
+
* panel, suitable for quick questions without taking much screen space.
|
|
514
|
+
*
|
|
515
|
+
* - `"full-modal"` — A full-screen immersive overlay (inspired by better-auth).
|
|
516
|
+
* Messages scroll in the center, input is pinned at the bottom.
|
|
517
|
+
* Suggested questions appear as horizontal pills. Best for
|
|
518
|
+
* documentation-heavy sites that want a premium AI experience.
|
|
519
|
+
*
|
|
520
|
+
* @default "panel"
|
|
521
|
+
*
|
|
522
|
+
* @example
|
|
523
|
+
* ```ts
|
|
524
|
+
* ai: {
|
|
525
|
+
* enabled: true,
|
|
526
|
+
* mode: "floating",
|
|
527
|
+
* position: "bottom-right",
|
|
528
|
+
* floatingStyle: "full-modal",
|
|
529
|
+
* }
|
|
530
|
+
* ```
|
|
531
|
+
*/
|
|
532
|
+
floatingStyle?: "panel" | "modal" | "popover" | "full-modal";
|
|
533
|
+
/**
|
|
534
|
+
* Custom trigger component for the floating chat button.
|
|
535
|
+
* Only used when `mode` is `"floating"`.
|
|
536
|
+
*
|
|
537
|
+
* Pass a React element to replace the default sparkles button.
|
|
538
|
+
* The element receives an `onClick` handler automatically.
|
|
539
|
+
*
|
|
540
|
+
* @example
|
|
541
|
+
* ```tsx
|
|
542
|
+
* ai: {
|
|
543
|
+
* enabled: true,
|
|
544
|
+
* mode: "floating",
|
|
545
|
+
* triggerComponent: <button className="my-chat-btn">💬 Help</button>,
|
|
546
|
+
* }
|
|
547
|
+
* ```
|
|
548
|
+
*/
|
|
549
|
+
triggerComponent?: unknown;
|
|
550
|
+
/**
|
|
551
|
+
* The LLM model to use for chat completions.
|
|
552
|
+
* Must be compatible with the OpenAI Chat Completions API.
|
|
553
|
+
* @default "gpt-4o-mini"
|
|
554
|
+
*/
|
|
555
|
+
model?: string;
|
|
556
|
+
/**
|
|
557
|
+
* Custom system prompt prepended to the AI conversation.
|
|
558
|
+
* The documentation context is automatically appended after this prompt.
|
|
559
|
+
*
|
|
560
|
+
* @default "You are a helpful documentation assistant. Answer questions
|
|
561
|
+
* based on the provided documentation context. Be concise and accurate.
|
|
562
|
+
* If the answer is not in the context, say so honestly."
|
|
563
|
+
*/
|
|
564
|
+
systemPrompt?: string;
|
|
565
|
+
/**
|
|
566
|
+
* Base URL for an OpenAI-compatible API endpoint.
|
|
567
|
+
* Use this to point to a self-hosted model, Azure OpenAI, or any
|
|
568
|
+
* compatible provider (e.g. Groq, Together, OpenRouter).
|
|
569
|
+
* @default "https://api.openai.com/v1"
|
|
570
|
+
*/
|
|
571
|
+
baseUrl?: string;
|
|
572
|
+
/**
|
|
573
|
+
* API key for the LLM provider.
|
|
574
|
+
* Pass it via an environment variable to keep it out of source control.
|
|
575
|
+
*
|
|
576
|
+
* @default process.env.OPENAI_API_KEY
|
|
577
|
+
*
|
|
578
|
+
* @example
|
|
579
|
+
* ```ts
|
|
580
|
+
* // Default — reads OPENAI_API_KEY automatically
|
|
581
|
+
* ai: { enabled: true }
|
|
582
|
+
*
|
|
583
|
+
* // Custom provider key
|
|
584
|
+
* ai: {
|
|
585
|
+
* enabled: true,
|
|
586
|
+
* apiKey: process.env.GROQ_API_KEY,
|
|
587
|
+
* }
|
|
588
|
+
* ```
|
|
589
|
+
*/
|
|
590
|
+
apiKey?: string;
|
|
591
|
+
/**
|
|
592
|
+
* Maximum number of search results to include as context for the AI.
|
|
593
|
+
* More results = more context but higher token usage.
|
|
594
|
+
* @default 5
|
|
595
|
+
*/
|
|
596
|
+
maxResults?: number;
|
|
597
|
+
/**
|
|
598
|
+
* Pre-filled suggested questions shown in the AI chat when empty.
|
|
599
|
+
* When a user clicks one, it fills the input and submits automatically.
|
|
600
|
+
*
|
|
601
|
+
* @example
|
|
602
|
+
* ```ts
|
|
603
|
+
* ai: {
|
|
604
|
+
* enabled: true,
|
|
605
|
+
* suggestedQuestions: [
|
|
606
|
+
* "How do I install this?",
|
|
607
|
+
* "What themes are available?",
|
|
608
|
+
* "How do I create a custom component?",
|
|
609
|
+
* ],
|
|
610
|
+
* }
|
|
611
|
+
* ```
|
|
612
|
+
*/
|
|
613
|
+
suggestedQuestions?: string[];
|
|
614
|
+
/**
|
|
615
|
+
* Display name for the AI assistant in the chat UI.
|
|
616
|
+
* Shown as the message label, header title, and passed to the loading component.
|
|
617
|
+
*
|
|
618
|
+
* @default "AI"
|
|
619
|
+
*
|
|
620
|
+
* @example
|
|
621
|
+
* ```ts
|
|
622
|
+
* ai: {
|
|
623
|
+
* enabled: true,
|
|
624
|
+
* aiLabel: "DocsBot",
|
|
625
|
+
* }
|
|
626
|
+
* ```
|
|
627
|
+
*/
|
|
628
|
+
aiLabel?: string;
|
|
629
|
+
/**
|
|
630
|
+
* The npm package name used in import examples.
|
|
631
|
+
* The AI will use this in code snippets instead of generic placeholders.
|
|
632
|
+
*
|
|
633
|
+
* @example
|
|
634
|
+
* ```ts
|
|
635
|
+
* ai: {
|
|
636
|
+
* enabled: true,
|
|
637
|
+
* packageName: "@farming-labs/docs",
|
|
638
|
+
* }
|
|
639
|
+
* ```
|
|
640
|
+
*/
|
|
641
|
+
packageName?: string;
|
|
642
|
+
/**
|
|
643
|
+
* The public URL of the documentation site.
|
|
644
|
+
* The AI will use this for links instead of relative paths.
|
|
645
|
+
*
|
|
646
|
+
* @example
|
|
647
|
+
* ```ts
|
|
648
|
+
* ai: {
|
|
649
|
+
* enabled: true,
|
|
650
|
+
* docsUrl: "https://docs.farming-labs.dev",
|
|
651
|
+
* }
|
|
652
|
+
* ```
|
|
653
|
+
*/
|
|
654
|
+
docsUrl?: string;
|
|
655
|
+
/**
|
|
656
|
+
* Custom loading indicator shown while the AI is generating a response.
|
|
657
|
+
* Replaces the default "AI is thinking..." indicator.
|
|
658
|
+
*
|
|
659
|
+
* Pass a function that receives `{ name }` (the `aiLabel` value) and
|
|
660
|
+
* returns a React element. This way you don't need to duplicate the label.
|
|
661
|
+
*
|
|
662
|
+
* @example
|
|
663
|
+
* ```tsx
|
|
664
|
+
* ai: {
|
|
665
|
+
* enabled: true,
|
|
666
|
+
* aiLabel: "Sage",
|
|
667
|
+
* loadingComponent: ({ name }) => (
|
|
668
|
+
* <div className="flex items-center gap-2 text-sm text-zinc-400">
|
|
669
|
+
* <span className="animate-pulse">🤔</span>
|
|
670
|
+
* <span>{name} is thinking...</span>
|
|
671
|
+
* </div>
|
|
672
|
+
* ),
|
|
673
|
+
* }
|
|
674
|
+
* ```
|
|
675
|
+
*/
|
|
676
|
+
loadingComponent?: (props: {
|
|
677
|
+
name: string;
|
|
678
|
+
}) => unknown;
|
|
679
|
+
}
|
|
445
680
|
interface DocsConfig {
|
|
446
681
|
/** Entry folder for docs (e.g. "docs" → /docs) */
|
|
447
682
|
entry: string;
|
|
683
|
+
/** Path to the content directory. Defaults to `entry` value. */
|
|
684
|
+
contentDir?: string;
|
|
448
685
|
/** Theme configuration - single source of truth for UI */
|
|
449
686
|
theme?: DocsTheme;
|
|
450
687
|
/**
|
|
@@ -579,6 +816,39 @@ interface DocsConfig {
|
|
|
579
816
|
* ```
|
|
580
817
|
*/
|
|
581
818
|
pageActions?: PageActionsConfig;
|
|
819
|
+
/**
|
|
820
|
+
* AI-powered "Ask AI" chat for documentation.
|
|
821
|
+
*
|
|
822
|
+
* When enabled, the unified API route handler (`/api/docs`) accepts
|
|
823
|
+
* POST requests for AI chat. The handler uses RAG (Retrieval-Augmented
|
|
824
|
+
* Generation) — it searches relevant docs, builds context, and streams
|
|
825
|
+
* a response from an LLM.
|
|
826
|
+
*
|
|
827
|
+
* The API key defaults to `process.env.OPENAI_API_KEY`. For other providers,
|
|
828
|
+
* pass the key via `apiKey: process.env.YOUR_KEY`.
|
|
829
|
+
*
|
|
830
|
+
* @example
|
|
831
|
+
* ```ts
|
|
832
|
+
* // Enable with defaults (gpt-4o-mini, OPENAI_API_KEY)
|
|
833
|
+
* ai: { enabled: true }
|
|
834
|
+
*
|
|
835
|
+
* // Custom model + system prompt
|
|
836
|
+
* ai: {
|
|
837
|
+
* enabled: true,
|
|
838
|
+
* model: "gpt-4o",
|
|
839
|
+
* systemPrompt: "You are an expert on our SDK. Be concise.",
|
|
840
|
+
* }
|
|
841
|
+
*
|
|
842
|
+
* // Use a different provider (e.g. Groq)
|
|
843
|
+
* ai: {
|
|
844
|
+
* enabled: true,
|
|
845
|
+
* baseUrl: "https://api.groq.com/openai/v1",
|
|
846
|
+
* apiKey: process.env.GROQ_API_KEY,
|
|
847
|
+
* model: "llama-3.1-70b-versatile",
|
|
848
|
+
* }
|
|
849
|
+
* ```
|
|
850
|
+
*/
|
|
851
|
+
ai?: AIConfig;
|
|
582
852
|
/** SEO metadata - separate from theme */
|
|
583
853
|
metadata?: DocsMetadata;
|
|
584
854
|
/** Open Graph image handling */
|
|
@@ -632,7 +902,7 @@ declare function createTheme(baseTheme: DocsTheme): (overrides?: Partial<DocsThe
|
|
|
632
902
|
* @example
|
|
633
903
|
* ```ts
|
|
634
904
|
* import { extendTheme } from "@farming-labs/docs";
|
|
635
|
-
* import { fumadocs } from "@farming-labs/
|
|
905
|
+
* import { fumadocs } from "@farming-labs/theme/default";
|
|
636
906
|
*
|
|
637
907
|
* // Start with fumadocs defaults, override some values
|
|
638
908
|
* export const myTheme = extendTheme(fumadocs(), {
|
|
@@ -654,4 +924,4 @@ declare function resolveTitle(pageTitle: string, metadata?: DocsMetadata): strin
|
|
|
654
924
|
*/
|
|
655
925
|
declare function resolveOGImage(page: PageFrontmatter, ogConfig?: OGConfig, baseUrl?: string): string | undefined;
|
|
656
926
|
//#endregion
|
|
657
|
-
export { type BreadcrumbConfig, type CopyMarkdownConfig, type DocsConfig, type DocsMetadata, type DocsNav, type DocsTheme, type FontStyle, type GithubConfig, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, type PageActionsConfig, type PageFrontmatter, type SidebarConfig, type ThemeToggleConfig, type TypographyConfig, type UIConfig, createTheme, deepMerge, defineDocs, extendTheme, resolveOGImage, resolveTitle };
|
|
927
|
+
export { type AIConfig, type BreadcrumbConfig, type CopyMarkdownConfig, type DocsConfig, type DocsMetadata, type DocsNav, type DocsTheme, type FontStyle, type GithubConfig, type OGConfig, type OpenDocsConfig, type OpenDocsProvider, type PageActionsConfig, type PageFrontmatter, type SidebarConfig, type ThemeToggleConfig, type TypographyConfig, type UIConfig, createTheme, deepMerge, defineDocs, extendTheme, resolveOGImage, resolveTitle };
|
package/dist/index.mjs
CHANGED
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
function defineDocs(config) {
|
|
6
6
|
return {
|
|
7
7
|
entry: config.entry ?? "docs",
|
|
8
|
+
contentDir: config.contentDir,
|
|
8
9
|
theme: config.theme,
|
|
9
10
|
nav: config.nav,
|
|
10
11
|
github: config.github,
|
|
@@ -14,6 +15,7 @@ function defineDocs(config) {
|
|
|
14
15
|
components: config.components,
|
|
15
16
|
icons: config.icons,
|
|
16
17
|
pageActions: config.pageActions,
|
|
18
|
+
ai: config.ai,
|
|
17
19
|
metadata: config.metadata,
|
|
18
20
|
og: config.og
|
|
19
21
|
};
|
|
@@ -81,7 +83,7 @@ function createTheme(baseTheme) {
|
|
|
81
83
|
* @example
|
|
82
84
|
* ```ts
|
|
83
85
|
* import { extendTheme } from "@farming-labs/docs";
|
|
84
|
-
* import { fumadocs } from "@farming-labs/
|
|
86
|
+
* import { fumadocs } from "@farming-labs/theme/default";
|
|
85
87
|
*
|
|
86
88
|
* // Start with fumadocs defaults, override some values
|
|
87
89
|
* export const myTheme = extendTheme(fumadocs(), {
|