diff --git a/.gitignore b/.gitignore index a1b951b..5153306 100644 --- a/.gitignore +++ b/.gitignore @@ -134,4 +134,6 @@ dist .pnp.* dist +.output/ +.vercel/ examples/frontier diff --git a/README.md b/README.md index 0b0490c..938c983 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Chronicle -Config-driven documentation framework built on Next.js, Fumadocs, and Apsara UI. +Config-driven documentation framework built on Vite, Nitro, and Apsara UI. ## Features @@ -75,7 +75,7 @@ bun install 3. Run the dev server ```bash -cd docs && bun ../packages/chronicle/bin/chronicle.js dev +bun run dev:docs ``` Open [http://localhost:3000](http://localhost:3000) to see the docs site. diff --git a/bun.lock b/bun.lock index 10c12c2..f0b8399 100644 --- a/bun.lock +++ b/bun.lock @@ -32,7 +32,7 @@ "lodash": "^4.17.23", "mermaid": "^11.13.0", "minisearch": "^7.2.0", - "nitro": "latest", + "nitro": "3.0.260311-beta", "openapi-types": "^12.1.3", "react": "^19.0.0", "react-dom": "^19.0.0", diff --git a/docs/chronicle.yaml b/docs/chronicle.yaml index c955c5c..045b114 100644 --- a/docs/chronicle.yaml +++ b/docs/chronicle.yaml @@ -1,5 +1,6 @@ title: Chronicle description: Config-driven documentation framework +content: . theme: name: paper diff --git a/docs/cli.mdx b/docs/cli.mdx index 84d87d4..0cd7330 100644 --- a/docs/cli.mdx +++ b/docs/cli.mdx @@ -10,7 +10,7 @@ Chronicle provides a CLI to initialize, develop, build, and serve your documenta ## init -Initialize a new Chronicle project. Must be run before other commands. +Initialize a new Chronicle project. ```bash chronicle init [options] @@ -23,15 +23,13 @@ chronicle init [options] This creates: - `chronicle.yaml` — site configuration - `content/` (or custom name) — content directory with a sample `index.mdx` -- `package.json` — with `@raystack/chronicle` dependency (if not exists) -- `.chronicle/` — scaffolded build directory -- `.gitignore` — with `.chronicle` entry +- `.gitignore` — with `node_modules`, `dist`, `.output` entries If the content directory already exists and has files, the sample `index.mdx` is skipped. ## dev -Start the development server with hot reload. Requires `chronicle init` first. +Start the development server with hot reload. ```bash chronicle dev [options] @@ -39,16 +37,24 @@ chronicle dev [options] | Flag | Description | Default | |------|-------------|---------| +| `--content ` | Content directory | `content` | +| `--config ` | Path to `chronicle.yaml` | `./chronicle.yaml` | | `-p, --port ` | Port number | `3000` | ## build -Build the site for production. Requires `chronicle init` first. +Build the site for production. ```bash -chronicle build +chronicle build [options] ``` +| Flag | Description | Default | +|------|-------------|---------| +| `--content ` | Content directory | `content` | +| `--config ` | Path to `chronicle.yaml` | `./chronicle.yaml` | +| `--preset ` | Deploy preset (`vercel`, `cloudflare`, `node-server`) | — | + ## start Start the production server. Requires a prior `chronicle build`. @@ -59,11 +65,12 @@ chronicle start [options] | Flag | Description | Default | |------|-------------|---------| +| `--content ` | Content directory | `content` | | `-p, --port ` | Port number | `3000` | ## serve -Build and start the production server in one step. Requires `chronicle init` first. +Build and start the production server in one step. ```bash chronicle serve [options] @@ -71,11 +78,17 @@ chronicle serve [options] | Flag | Description | Default | |------|-------------|---------| +| `--content ` | Content directory | `content` | +| `--config ` | Path to `chronicle.yaml` | `./chronicle.yaml` | | `-p, --port ` | Port number | `3000` | +| `--preset ` | Deploy preset (`vercel`, `cloudflare`, `node-server`) | — | -## Config Resolution +## Resolution Order -`chronicle.yaml` is resolved in this order: +CLI flags take precedence over `chronicle.yaml` values, which take precedence over defaults. -1. Current working directory -2. Content directory +| Option | CLI flag | Config field | Default | +|--------|----------|-------------|---------| +| Content directory | `--content` | `content` | `content` | +| Deploy preset | `--preset` | `preset` | — | +| Config path | `--config` | — | `./chronicle.yaml` | diff --git a/docs/components.mdx b/docs/components.mdx index 11d2622..97f9e31 100644 --- a/docs/components.mdx +++ b/docs/components.mdx @@ -158,7 +158,7 @@ Images support both local and external sources: ![External image](https://example.com/image.png) ``` -- **Local images** — Rendered with Next.js `Image` component (optimized, default 800x400) +- **Local images** — Rendered with optimized `Image` component (default 800x400) - **External images** — Rendered with standard `` tag ## Links @@ -171,7 +171,7 @@ Links are automatically handled: [Anchor link](#section) ``` -- **Internal links** — Use Next.js client-side navigation +- **Internal links** — Use client-side navigation - **External links** — Open in a new tab automatically - **Anchor links** — Smooth scroll to the section @@ -203,7 +203,7 @@ Chronicle overrides these HTML elements with styled components: | HTML Element | Chronicle Component | Notes | |-------------|-------------------|-------| | `

` | `MdxParagraph` | Auto-converts to `

` when containing block elements | -| `` | `Image` | Next.js optimized for local, standard for external | +| `` | `Image` | Optimized for local, standard for external | | `` | `Link` | Smart routing with external link detection | | `` | `MdxCode` | Inline code styling | | `
` | `MdxPre` | Code blocks with optional title header |
diff --git a/docs/configuration.mdx b/docs/configuration.mdx
index 789d011..33432af 100644
--- a/docs/configuration.mdx
+++ b/docs/configuration.mdx
@@ -6,13 +6,16 @@ order: 3
 
 # Configuration
 
-All site configuration lives in a single `chronicle.yaml` file in your content directory.
+All site configuration lives in a single `chronicle.yaml` file in your project root. The config is validated using Zod — invalid fields will produce clear error messages at startup.
 
 ## Full Example
 
 ```yaml
 title: My Project Docs
 description: Documentation for My Project
+url: https://docs.example.com
+content: docs
+preset: vercel
 
 logo:
   light: ./logo-light.png
@@ -51,6 +54,14 @@ api:
       type: apiKey
       header: Authorization
       placeholder: "Bearer token"
+
+llms:
+  enabled: true
+
+analytics:
+  enabled: true
+  googleAnalytics:
+    measurementId: G-XXXXXXXXXX
 ```
 
 ## Reference
@@ -63,6 +74,30 @@ api:
 title: My Documentation
 ```
 
+### url
+
+Optional site URL. Used for SEO metadata, sitemap, and canonical URLs.
+
+```yaml
+url: https://docs.example.com
+```
+
+### content
+
+Optional content directory path. Can be overridden by the `--content` CLI flag.
+
+```yaml
+content: docs
+```
+
+### preset
+
+Optional deploy preset. Can be overridden by the `--preset` CLI flag.
+
+```yaml
+preset: vercel # vercel, cloudflare, or node-server
+```
+
 ### description
 
 Optional meta description for SEO.
@@ -203,6 +238,35 @@ Each entry in the `api` array creates a section of API documentation.
 
 API pages include a "Try it out" panel that uses the configured server URL and auth settings.
 
+### llms
+
+Configuration for LLM-friendly content generation. When enabled, Chronicle generates `/llms.txt` and `/llms-full.txt` endpoints.
+
+```yaml
+llms:
+  enabled: true
+```
+
+| Field | Type | Description | Default |
+|-------|------|-------------|---------|
+| `enabled` | `boolean` | Enable/disable LLM content endpoints | `false` |
+
+### analytics
+
+Analytics integration for tracking page views.
+
+```yaml
+analytics:
+  enabled: true
+  googleAnalytics:
+    measurementId: G-XXXXXXXXXX
+```
+
+| Field | Type | Description | Default |
+|-------|------|-------------|---------|
+| `enabled` | `boolean` | Enable/disable analytics | `false` |
+| `googleAnalytics.measurementId` | `string` | Google Analytics measurement ID | — |
+
 ## Defaults
 
 If `chronicle.yaml` is missing or fields are omitted, these defaults apply:
diff --git a/docs/frontmatter.mdx b/docs/frontmatter.mdx
index ad4abb6..7ea8440 100644
--- a/docs/frontmatter.mdx
+++ b/docs/frontmatter.mdx
@@ -78,6 +78,14 @@ icon: rectangle-stack
 | `method-delete` | HTTP DELETE badge |
 | `method-patch` | HTTP PATCH badge |
 
+### lastModified
+
+Optional date string indicating when the page was last updated.
+
+```yaml
+lastModified: "2026-03-30"
+```
+
 ## Navigation Ordering
 
 Sidebar navigation is determined by:
diff --git a/docs/index.mdx b/docs/index.mdx
index 5baaa03..4240051 100644
--- a/docs/index.mdx
+++ b/docs/index.mdx
@@ -6,7 +6,7 @@ order: 1
 
 # Getting Started
 
-Chronicle is a config-driven documentation framework built on Next.js 15, Fumadocs, and Apsara UI components. Write MDX content, configure with a single YAML file, and get a fully themed documentation site.
+Chronicle is a config-driven documentation framework built on Vite, Nitro, and Apsara UI components. Write MDX content, configure with a single YAML file, and get a fully themed documentation site.
 
 ## Installation
 
@@ -25,8 +25,7 @@ chronicle init
 This creates:
 - `chronicle.yaml` — your site configuration
 - `content/` — content directory with a sample `index.mdx`
-- `package.json` — with `@raystack/chronicle` dependency
-- `.chronicle/` — scaffolded build directory (gitignored)
+- `.gitignore` — with `node_modules`, `dist`, `.output` entries
 
 To use an existing directory as content (e.g. `docs/`):
 
@@ -55,7 +54,7 @@ my-docs/
 │   └── guides/
 │       ├── installation.mdx
 │       └── configuration.mdx
-└── .chronicle/          # generated, gitignored
+└── .output/             # build output, gitignored
 ```
 
 ### 4. Build for production
@@ -82,7 +81,7 @@ my-docs/
 │   ├── index.mdx       # Home page
 │   └── guides/
 │       └── setup.mdx   # Nested page at /guides/setup
-└── .chronicle/          # Generated by init, gitignored
+└── .output/             # Build output, gitignored
 ```
 
 All configuration is done through `chronicle.yaml`. No additional config files needed.
diff --git a/docs/themes.mdx b/docs/themes.mdx
index 74ab076..251293b 100644
--- a/docs/themes.mdx
+++ b/docs/themes.mdx
@@ -57,3 +57,4 @@ theme:
 - Minimal, distraction-free design
 - Reading progress tracking
 - Optimized typography for long content
+- Light mode only (dark mode toggle is disabled)
diff --git a/package.json b/package.json
index 61028e4..c251626 100644
--- a/package.json
+++ b/package.json
@@ -1,8 +1,16 @@
 {
   "name": "chronicle",
   "private": true,
-  "workspaces": ["packages/*", "examples/*"],
+  "workspaces": [
+    "packages/*",
+    "examples/*"
+  ],
   "engines": {
     "node": ">=22"
+  },
+  "scripts": {
+    "build:cli": "bun run --filter @raystack/chronicle build:cli",
+    "dev:docs": "./packages/chronicle/bin/chronicle.js dev --config docs/chronicle.yaml",
+    "build:docs": "./packages/chronicle/bin/chronicle.js build --config docs/chronicle.yaml"
   }
 }
diff --git a/packages/chronicle/package.json b/packages/chronicle/package.json
index cb01e78..9cb8ad8 100644
--- a/packages/chronicle/package.json
+++ b/packages/chronicle/package.json
@@ -51,7 +51,7 @@
     "lodash": "^4.17.23",
     "mermaid": "^11.13.0",
     "minisearch": "^7.2.0",
-    "nitro": "latest",
+    "nitro": "3.0.260311-beta",
     "openapi-types": "^12.1.3",
     "react": "^19.0.0",
     "react-dom": "^19.0.0",
diff --git a/packages/chronicle/src/cli/utils/config.ts b/packages/chronicle/src/cli/utils/config.ts
index 737ca13..90d7728 100644
--- a/packages/chronicle/src/cli/utils/config.ts
+++ b/packages/chronicle/src/cli/utils/config.ts
@@ -45,9 +45,9 @@ function validateConfig(raw: string, configPath: string): ChronicleConfig {
   return result.data;
 }
 
-export function resolveContentDir(config: ChronicleConfig, contentFlag?: string): string {
+export function resolveContentDir(config: ChronicleConfig, configPath: string, contentFlag?: string): string {
   if (contentFlag) return path.resolve(contentFlag);
-  if (config.content) return path.resolve(config.content);
+  if (config.content) return path.resolve(path.dirname(configPath), config.content);
   return path.resolve('content');
 }
 
@@ -64,7 +64,7 @@ export async function loadCLIConfig(
 
   const raw = await readConfig(resolvedConfigPath);
   const config = validateConfig(raw, resolvedConfigPath);
-  const contentDir = resolveContentDir(config, options?.content);
+  const contentDir = resolveContentDir(config, resolvedConfigPath, options?.content);
   const preset = resolvePreset(config, options?.preset);
 
   return { config, configPath: resolvedConfigPath, contentDir, preset };
diff --git a/vercel.json b/vercel.json
new file mode 100644
index 0000000..47e057a
--- /dev/null
+++ b/vercel.json
@@ -0,0 +1,5 @@
+{
+  "installCommand": "bun install",
+  "buildCommand": "bun run build:cli && bun run build:docs -- --preset vercel",
+  "outputDirectory": ".vercel/output"
+}