diff --git a/docs/src/lib/components/DocsMenu.svelte b/docs/src/lib/components/DocsMenu.svelte
index 49b3b95cf..b06c8e2d5 100644
--- a/docs/src/lib/components/DocsMenu.svelte
+++ b/docs/src/lib/components/DocsMenu.svelte
@@ -7,6 +7,7 @@
import { sortFunc } from '@layerstack/utils';
import { cls } from '@layerstack/tailwind';
+ import LucideBot from '~icons/lucide/bot';
import LucideCompass from '~icons/lucide/compass';
import LucideGalleryVertical from '~icons/lucide/gallery-vertical';
import LucideGalleryHorizontalEnd from '~icons/lucide/gallery-horizontal-end';
@@ -28,7 +29,8 @@
{ name: 'Simplified charts', path: 'simplified-charts' },
{ name: 'Scales', path: 'scales' },
{ name: 'State', path: 'state' },
- { name: 'Styles', path: 'styles' }
+ { name: 'Styles', path: 'styles' },
+ { name: 'LLMs', path: 'LLMs' }
];
const componentsByCategory = flatGroup(allComponents, (d) => d.category?.toLowerCase())
diff --git a/docs/src/lib/components/OpenWithButton.svelte b/docs/src/lib/components/OpenWithButton.svelte
new file mode 100644
index 000000000..6ec8b09cc
--- /dev/null
+++ b/docs/src/lib/components/OpenWithButton.svelte
@@ -0,0 +1,156 @@
+
+
+
+
+
+
+
+
+
diff --git a/docs/src/lib/markdown/components/Note.svelte b/docs/src/lib/markdown/components/Note.svelte
index c74d80d7d..60bfeb5c1 100644
--- a/docs/src/lib/markdown/components/Note.svelte
+++ b/docs/src/lib/markdown/components/Note.svelte
@@ -25,7 +25,7 @@
p]:my-2',
className
diff --git a/docs/src/lib/markdown/utils.ts b/docs/src/lib/markdown/utils.ts
index 2e012cf84..f1f037f35 100644
--- a/docs/src/lib/markdown/utils.ts
+++ b/docs/src/lib/markdown/utils.ts
@@ -153,3 +153,111 @@ export async function loadExamplesFromMarkdown(
return examples;
}
+
+/**
+ * Process content only outside of code blocks (``` ... ```)
+ * Preserves code block content unchanged while applying transformations elsewhere
+ */
+function processOutsideCodeBlocks(content: string, processor: (text: string) => string): string {
+ // Split by code blocks, preserving the delimiters
+ const parts = content.split(/(```[\s\S]*?```)/g);
+
+ return parts
+ .map((part, index) => {
+ // Odd indices are code blocks (matched by the capture group)
+ if (index % 2 === 1) {
+ return part; // Keep code blocks unchanged
+ }
+ return processor(part); // Process non-code-block content
+ })
+ .join('');
+}
+
+// Process markdown content for LLMs by removing custom syntax and converting to vanilla markdown
+export function processMarkdownContent(content: string): string {
+ // Remove frontmatter (YAML between --- markers at start of file)
+ content = content.replace(/^---\n[\s\S]*?\n---\n*/, '');
+
+ // Remove Svelte script blocks and components ONLY outside of code blocks
+ content = processOutsideCodeBlocks(content, (text) => {
+ // Remove Svelte script blocks
+ text = text.replace(/
+
+
+
+
Component Docs
diff --git a/docs/src/routes/docs/components/[name]/[example]/llms.txt/+server.ts b/docs/src/routes/docs/components/[name]/[example]/llms.txt/+server.ts
new file mode 100644
index 000000000..c47a9dd7c
--- /dev/null
+++ b/docs/src/routes/docs/components/[name]/[example]/llms.txt/+server.ts
@@ -0,0 +1,74 @@
+import { error } from '@sveltejs/kit';
+import { readFileSync } from 'fs';
+import { join } from 'path';
+import type { RequestHandler } from './$types';
+import { allComponents } from 'content-collections';
+import { processMarkdownContent } from '$lib/markdown/utils.js';
+
+const BASE_URL = 'https://layerchart.com';
+
+export const GET: RequestHandler = async ({ params }) => {
+ const { name, example } = params;
+
+ // Find component metadata
+ const component = allComponents.find((c) => c.slug === name);
+
+ // Load example source code
+ let exampleSource = '';
+ try {
+ const examplePath = join(process.cwd(), `src/examples/components/${name}/${example}.svelte`);
+ exampleSource = readFileSync(examplePath, 'utf-8');
+ exampleSource = trimCode(exampleSource);
+ } catch (e) {
+ error(404, `Example "${example}" not found for component "${name}"`);
+ }
+
+ let markdown = generateMarkdown(name, example, component, exampleSource);
+ markdown = processMarkdownContent(markdown);
+
+ return new Response(markdown, {
+ headers: {
+ 'Content-Type': 'text/markdown; charset=utf-8',
+ 'Content-Disposition': `inline; filename="${name}-${example}.md"`
+ }
+ });
+};
+
+/**
+ * Trim code to remove module exports and data export statement
+ */
+function trimCode(code: string): string {
+ return code
+ .replace(/')
+ .trim();
+}
+
+/**
+ * Generate the markdown document
+ */
+function generateMarkdown(
+ componentName: string,
+ exampleName: string,
+ component: (typeof allComponents)[number] | undefined,
+ exampleSource: string
+): string {
+ const sections: string[] = [];
+
+ // Title
+ sections.push(`# ${componentName} - ${exampleName}`);
+
+ // Component description if available
+ if (component?.description) {
+ sections.push(component.description);
+ }
+
+ // Links
+ sections.push(`**Component Documentation:** [${componentName}](${BASE_URL}/docs/components/${componentName})`);
+
+ // Example code
+ sections.push('## Code');
+ sections.push('```svelte\n' + exampleSource + '\n```');
+
+ return sections.join('\n\n');
+}
diff --git a/docs/src/routes/docs/components/[name]/llms.txt/+server.ts b/docs/src/routes/docs/components/[name]/llms.txt/+server.ts
new file mode 100644
index 000000000..2c3fc6dfc
--- /dev/null
+++ b/docs/src/routes/docs/components/[name]/llms.txt/+server.ts
@@ -0,0 +1,155 @@
+import { error } from '@sveltejs/kit';
+import { readFileSync } from 'fs';
+import { join } from 'path';
+import type { RequestHandler } from './$types';
+import type { ComponentAPI } from '$lib/api-types.js';
+import { allComponents } from 'content-collections';
+import { processMarkdownContent } from '$lib/markdown/utils.js';
+
+export const GET: RequestHandler = async ({ params }) => {
+ const { name } = params;
+
+ // Find component metadata from content-collections
+ const component = allComponents.find((c) => c.slug === name);
+ if (!component) {
+ error(404, `Component "${name}" not found`);
+ }
+
+ // Load API JSON
+ let api: ComponentAPI | null = null;
+ try {
+ const apiPath = join(process.cwd(), `src/generated/api/${name}.json`);
+ const apiContent = readFileSync(apiPath, 'utf-8');
+ api = JSON.parse(apiContent);
+ } catch (e) {
+ // API file may not exist for all components
+ }
+
+ // Load first example source code
+ let exampleSource = '';
+ if (component.usageExample) {
+ try {
+ const examplePath = join(
+ process.cwd(),
+ `src/examples/components/${name}/${component.usageExample}.svelte`
+ );
+ exampleSource = readFileSync(examplePath, 'utf-8');
+ exampleSource = trimCode(exampleSource);
+ } catch (e) {
+ // Example file may not exist
+ }
+ }
+
+ // Build markdown content
+ let markdown = generateMarkdown(component, api, exampleSource);
+ markdown = processMarkdownContent(markdown);
+
+ return new Response(markdown, {
+ headers: {
+ 'Content-Type': 'text/markdown; charset=utf-8',
+ 'Content-Disposition': `inline; filename="${name}.md"`
+ }
+ });
+};
+
+/**
+ * Trim code to remove module exports and data export statement
+ */
+function trimCode(code: string): string {
+ return code
+ .replace(/') // remove data export statement
+ .trim();
+}
+
+/**
+ * Generate markdown API table from component properties
+ */
+function generateApiTable(api: ComponentAPI): string {
+ if (!api.properties || api.properties.length === 0) {
+ return '';
+ }
+
+ const rows = api.properties.map((prop) => {
+ const name = prop.required ? `**${prop.name}** (required)` : prop.name;
+ const type = `\`${escapeMarkdown(prop.type)}\``;
+ const defaultVal = prop.default ? `\`${escapeMarkdown(prop.default)}\`` : '-';
+ const description = prop.description ? escapeMarkdown(prop.description) : '-';
+ return `| ${name} | ${type} | ${defaultVal} | ${description} |`;
+ });
+
+ return `| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+${rows.join('\n')}`;
+}
+
+/**
+ * Escape special markdown characters in table cells
+ */
+function escapeMarkdown(text: string): string {
+ return text
+ .replace(/\|/g, '\\|')
+ .replace(/\n/g, ' ')
+ .replace(//g, '>');
+}
+
+/**
+ * Generate the full markdown document
+ */
+function generateMarkdown(
+ component: (typeof allComponents)[number],
+ api: ComponentAPI | null,
+ exampleSource: string
+): string {
+ const sections: string[] = [];
+
+ // Title and description
+ sections.push(`# ${component.name}`);
+ if (component.description) {
+ sections.push(component.description);
+ }
+
+ // Metadata
+ if (component.category) {
+ sections.push(`**Category:** ${component.category}`);
+ }
+ if (component.layers && component.layers.length > 0) {
+ sections.push(`**Supported Layers:** ${component.layers.join(', ')}`);
+ }
+
+ // Documentation link
+ sections.push(`**Full Documentation:** [${component.name}](https://layerchart.com/docs/components/${component.slug})`);
+
+ // Example (before API)
+ if (exampleSource) {
+ sections.push('## Example');
+ sections.push('```svelte\n' + exampleSource + '\n```');
+ }
+
+ // API table
+ if (api) {
+ sections.push('## API');
+ const table = generateApiTable(api);
+ if (table) {
+ sections.push(table);
+ }
+
+ // Extended types
+ if (api.extends && api.extends.length > 0) {
+ const extendsList = api.extends.map((e) => `\`${e.fullType}\``).join(', ');
+ sections.push(`**Extends:** ${extendsList}`);
+ }
+ }
+
+ // Related components
+ if (component.related && component.related.length > 0) {
+ sections.push('## Related');
+ const relatedLinks = component.related
+ .map((r) => `- [${r}](https://layerchart.com/docs/components/${r})`)
+ .join('\n');
+ sections.push(relatedLinks);
+ }
+
+ return sections.join('\n\n');
+}
diff --git a/docs/src/routes/docs/getting-started/+layout.svelte b/docs/src/routes/docs/getting-started/+layout.svelte
new file mode 100644
index 000000000..6d91bb749
--- /dev/null
+++ b/docs/src/routes/docs/getting-started/+layout.svelte
@@ -0,0 +1,18 @@
+
+
+
{data.metadata.title}
+
+
+
+
+
+ {#snippet pending()}
+ loading...
+ {/snippet}
+
+ {@render children()}
+
diff --git a/docs/src/routes/docs/getting-started/+layout.ts b/docs/src/routes/docs/getting-started/+layout.ts
new file mode 100644
index 000000000..c19b31bd5
--- /dev/null
+++ b/docs/src/routes/docs/getting-started/+layout.ts
@@ -0,0 +1,5 @@
+export const load = async () => {
+ return {
+ metadata: { title: 'Getting Started' }
+ };
+};
diff --git a/docs/src/routes/docs/getting-started/+page.md b/docs/src/routes/docs/getting-started/+page.md
index 4439a7784..43ce6fae2 100644
--- a/docs/src/routes/docs/getting-started/+page.md
+++ b/docs/src/routes/docs/getting-started/+page.md
@@ -1,4 +1,6 @@
-# Getting Started
+---
+title: Getting Started
+---
LayerChart can be used standlone, or integrates nicely with other frameworks and design systems.
diff --git a/docs/src/routes/docs/getting-started/llms.txt/+server.ts b/docs/src/routes/docs/getting-started/llms.txt/+server.ts
new file mode 100644
index 000000000..71912f43d
--- /dev/null
+++ b/docs/src/routes/docs/getting-started/llms.txt/+server.ts
@@ -0,0 +1,21 @@
+import { readFileSync } from 'fs';
+import { join } from 'path';
+import type { RequestHandler } from './$types';
+import { processMarkdownContent } from '$lib/markdown/utils.js';
+
+export const GET: RequestHandler = async () => {
+ const mdPath = join(process.cwd(), 'src/routes/docs/getting-started/+page.md');
+ let content = readFileSync(mdPath, 'utf-8');
+
+ content = processMarkdownContent(content);
+
+ const markdown = `# Getting Started\n\n${content}`;
+
+ return new Response(markdown, {
+ headers: {
+ 'Content-Type': 'text/markdown; charset=utf-8',
+ 'Content-Disposition': 'inline; filename="getting-started.md"'
+ }
+ });
+};
+
diff --git a/docs/src/routes/docs/guides/+layout.svelte b/docs/src/routes/docs/guides/+layout.svelte
new file mode 100644
index 000000000..2f15c6a7e
--- /dev/null
+++ b/docs/src/routes/docs/guides/+layout.svelte
@@ -0,0 +1,21 @@
+
+
+
{data.metadata.title}
+
+{#if data.metadata.title !== 'LLMs'}
+
+
+
+{/if}
+
+
+ {#snippet pending()}
+ loading...
+ {/snippet}
+
+ {@render children()}
+
diff --git a/docs/src/routes/docs/guides/+layout.ts b/docs/src/routes/docs/guides/+layout.ts
new file mode 100644
index 000000000..c4b1c5d61
--- /dev/null
+++ b/docs/src/routes/docs/guides/+layout.ts
@@ -0,0 +1,26 @@
+import { toTitleCase } from '@layerstack/utils';
+
+// Dynamically import all guide markdown files to access their frontmatter
+const modules = import.meta.glob('./**/+page.md', { eager: true }) as Record<
+ string,
+ { metadata?: Record
}
+>;
+
+export const load = async ({ url }) => {
+ // Get the last segment of the path (e.g., "features" from "/docs/guides/features")
+ const segments = url.pathname.split('/').filter(Boolean);
+ const name = segments[segments.length - 1];
+
+ // Get metadata from the markdown file's frontmatter
+ const modulePath = `./${name}/+page.md`;
+ const module = modules[modulePath];
+ const frontmatter = module?.metadata ?? {};
+
+ // Use frontmatter title if available, otherwise derive from path
+ const title =
+ (frontmatter.title as string) ?? toTitleCase(name.replaceAll('-', ' '));
+
+ return {
+ metadata: { ...frontmatter, title }
+ };
+};
diff --git a/docs/src/routes/docs/guides/LLMs/+page.md b/docs/src/routes/docs/guides/LLMs/+page.md
new file mode 100644
index 000000000..1fc3e3de5
--- /dev/null
+++ b/docs/src/routes/docs/guides/LLMs/+page.md
@@ -0,0 +1,41 @@
+---
+title: LLMs
+---
+
+
+
+The Layerchart documentation pages are designed to be accessible for humans developers using LLMs as well as large language models (LLMs) ingesting training data.
+
+## :icon{name="lucide:user" class="relative -top-1"} For the Humans
+
+
+
+At the top of each documentation page, and demonstrated above, you'll find a button which copies the content of the page's documentation in Markdown to the clipboard. The convenient dropdown gives you additional helpful options.
+
+::note
+The option for `View Component Source` is only shown for component pages.
+::
+
+## :icon{name="lucide:bot" class="relative -top-1"} For the Bots
+
+LayerChart adopts the [llms.txt](https://llmstxt.org/) proposal standard, which provides a structured, machine-readable format optimized for LLMs. This enables developers, researchers, and AI systems to efficiently parse and utilize our documentation.
+
+::note
+Not all pages may support the `/llms.txt` suffix (ie those deemed irrelevant to LLMs).
+::
+
+## LLM-friendly Documentation 3 Ways
+
+1. To access the LLM-friendly version of supported Layerchart documentation pages, simply append `/llms.txt` to the end of the page's URL. This will return the content in a plain-text, LLM-optimized format. This is the same text which is copied to the clipboard when you click the `View Page Markdown` button.
+
+:::tip
+- **Standard Page**: The LineChart component documentation is available at [layerchart.com/docs/components/LineChart](/docs/components/LineChart).
+
+- **LLM-friendly Version**: is available at [layerchart.com/docs/components/Linechart/llms.txt](/docs/components/LineChart/llms.txt).
+:::
+
+1. To explore all supported pages in LLM-friendly format, visit the root index at [layerchart.com/llms.txt](/llms.txt). This page provides a comprehensive list of available documentation endpoints compatible with the `llms.txt` standard.
+
+1. For a complete, consolidated view of the all the Layerchart documentation in an LLM-friendly format, navigate to [layerchart.com/docs/llms.txt](/docs/llms.txt). This single endpoint aggregates all documentation content into a machine-readable structure, ideal for bulk processing or ingestion into AI systems.
diff --git a/docs/src/routes/docs/guides/[name]/llms.txt/+server.ts b/docs/src/routes/docs/guides/[name]/llms.txt/+server.ts
new file mode 100644
index 000000000..ff4474ebe
--- /dev/null
+++ b/docs/src/routes/docs/guides/[name]/llms.txt/+server.ts
@@ -0,0 +1,48 @@
+import { error } from '@sveltejs/kit';
+import { readFileSync } from 'fs';
+import { join } from 'path';
+import type { RequestHandler } from './$types';
+import { processMarkdownContent } from '$lib/markdown/utils.js';
+
+export const GET: RequestHandler = async ({ params }) => {
+ const { name } = params;
+
+ let content = '';
+ try {
+ const mdPath = join(process.cwd(), `src/routes/docs/guides/${name}/+page.md`);
+ content = readFileSync(mdPath, 'utf-8');
+ } catch (e) {
+ error(404, `Guide "${name}" not found`);
+ }
+
+ // Extract title from frontmatter before processing
+ let title: string | undefined;
+ const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n*/);
+ if (frontmatterMatch) {
+ const frontmatter = frontmatterMatch[1];
+ const titleMatch = frontmatter.match(/^title:\s*(.+)$/m);
+ if (titleMatch) {
+ title = titleMatch[1].trim().replace(/^["']|["']$/g, ''); // Remove quotes if present
+ }
+ }
+
+ // Process content (removes frontmatter)
+ content = processMarkdownContent(content);
+
+ // Use frontmatter title if available, otherwise generate from name
+ if (!title) {
+ title = name
+ .split('-')
+ .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
+ .join(' ');
+ }
+
+ const markdown = `# ${title}\n\n${content}`;
+
+ return new Response(markdown, {
+ headers: {
+ 'Content-Type': 'text/markdown; charset=utf-8',
+ 'Content-Disposition': `inline; filename="${name}.md"`
+ }
+ });
+};
diff --git a/docs/src/routes/docs/guides/features/+page.md b/docs/src/routes/docs/guides/features/+page.md
index 314bed042..b3c74ad62 100644
--- a/docs/src/routes/docs/guides/features/+page.md
+++ b/docs/src/routes/docs/guides/features/+page.md
@@ -1,4 +1,6 @@
-# Features
+---
+title: Features
+---
> WIP
diff --git a/docs/src/routes/docs/guides/layers/+page.md b/docs/src/routes/docs/guides/layers/+page.md
index 7f6cfe5de..dc38fc21d 100644
--- a/docs/src/routes/docs/guides/layers/+page.md
+++ b/docs/src/routes/docs/guides/layers/+page.md
@@ -1,11 +1,13 @@
+---
+title: Layers
+---
+
-# Layers
-
LayerChart provides first-class support for different types of layers including [Svg](/docs/components/Svg), [Html](/docs/components/Html), and [Canvas](/docs/components/Canvas) via [Layer](/docs/components/Layer) and [Primitive](/docs/guides/primitives) components.
Each layer type provides unique and overlapping feature sets. LayerChart supports using layers of different types within the same chart to leverage a type's strengths or workaround a weakness.
diff --git a/docs/src/routes/docs/guides/primitives/+page.md b/docs/src/routes/docs/guides/primitives/+page.md
index 1b332b23f..96cee2a8f 100644
--- a/docs/src/routes/docs/guides/primitives/+page.md
+++ b/docs/src/routes/docs/guides/primitives/+page.md
@@ -1,3 +1,7 @@
+---
+title: Primitives
+---
+
-# Primitives
-
A collection of components which support rendering within different layer types including `Svg`, `Canvas`, or `Html`.
Support for each layer type is dependent on the primitive's feature needs and capabilities of the layer type.
diff --git a/docs/src/routes/docs/guides/scales/+page.md b/docs/src/routes/docs/guides/scales/+page.md
index 4ed81aa27..ebe8db295 100644
--- a/docs/src/routes/docs/guides/scales/+page.md
+++ b/docs/src/routes/docs/guides/scales/+page.md
@@ -1,3 +1,7 @@
+---
+title: Scales
+---
+
-# Scales
-
## What is a scale?
At its essence, a scale is a function that maps data values (`domain`) to pixel or color values (`range`) on a per-dimension basis (`x`, `y`, `color`, etc).
diff --git a/docs/src/routes/docs/guides/simplified-charts/+page.md b/docs/src/routes/docs/guides/simplified-charts/+page.md
index efe17850c..9a2ae6981 100644
--- a/docs/src/routes/docs/guides/simplified-charts/+page.md
+++ b/docs/src/routes/docs/guides/simplified-charts/+page.md
@@ -1,3 +1,7 @@
+---
+title: Simplified Charts
+---
+
-# Simplified charts
-
The LayerChart project was written to offer options for both flexibility/complexity as well as approachablilty/simplicity. This brings us to a decision as you start your first LayerChart.
## Use `` or `Simple Chart`.
diff --git a/docs/src/routes/docs/guides/state/+page.md b/docs/src/routes/docs/guides/state/+page.md
index f48eca287..b24bf03c5 100644
--- a/docs/src/routes/docs/guides/state/+page.md
+++ b/docs/src/routes/docs/guides/state/+page.md
@@ -1,4 +1,6 @@
-# State / Context
+---
+title: State / Context
+---
> TODO
diff --git a/docs/src/routes/docs/guides/styles/+page.md b/docs/src/routes/docs/guides/styles/+page.md
index f84da58aa..8445269fa 100644
--- a/docs/src/routes/docs/guides/styles/+page.md
+++ b/docs/src/routes/docs/guides/styles/+page.md
@@ -1,4 +1,6 @@
-# Styling
+---
+title: Styling
+---
## Colors
diff --git a/docs/src/routes/docs/llms.txt/+server.ts b/docs/src/routes/docs/llms.txt/+server.ts
new file mode 100644
index 000000000..96e27b2bf
--- /dev/null
+++ b/docs/src/routes/docs/llms.txt/+server.ts
@@ -0,0 +1,237 @@
+import { readFileSync } from 'fs';
+import { join } from 'path';
+import type { RequestHandler } from './$types';
+import type { ComponentAPI } from '$lib/api-types.js';
+import { allComponents, allUtils } from 'content-collections';
+
+const BASE_URL = 'https://layerchart.com';
+
+const guides = [
+ { slug: 'getting-started', name: 'Getting Started', description: 'Installation and setup guide for LayerChart' },
+ { slug: 'guides/scales', name: 'Scales', description: 'Understanding scales, domains, and ranges' },
+ { slug: 'guides/layers', name: 'Layers', description: 'Working with SVG, Canvas, and HTML layers' },
+ { slug: 'guides/state', name: 'State', description: 'Managing chart state and reactivity' },
+ { slug: 'guides/styles', name: 'Styles', description: 'Styling and theming your charts' },
+ { slug: 'guides/primitives', name: 'Primitives', description: 'Low-level building blocks for custom charts' },
+ { slug: 'guides/simplified-charts', name: 'Simplified Charts', description: 'Quick chart components for common use cases' },
+ { slug: 'guides/features', name: 'Features', description: 'Overview of LayerChart features and capabilities' }
+];
+
+export const GET: RequestHandler = async () => {
+ const content = generateFullLlmsTxt();
+
+ return new Response(content, {
+ headers: {
+ 'Content-Type': 'text/plain; charset=utf-8'
+ }
+ });
+};
+
+function generateFullLlmsTxt(): string {
+ const sections: string[] = [];
+
+ // Header
+ sections.push(`# LayerChart Full Documentation for LLMs
+
+> LayerChart is a powerful, composable charting library for Svelte built on top of D3.
+
+This file contains the complete LLM-optimized documentation for all components and utilities.`);
+
+ // General section (links only, as these are markdown pages)
+ sections.push(`## General
+
+${guides.map((g) => `- [${g.name}](${BASE_URL}/docs/${g.slug}): ${g.description}`).join('\n')}`);
+
+ // Components section - full content
+ sections.push('---\n\n# Components');
+
+ const sortedComponents = allComponents
+ .filter((c) => c.slug && c.name)
+ .sort((a, b) => a.name.localeCompare(b.name));
+
+ for (const component of sortedComponents) {
+ const componentContent = generateComponentMarkdown(component);
+ sections.push(componentContent);
+ }
+
+ // Utilities section - full content
+ sections.push('---\n\n# Utilities');
+
+ const sortedUtils = allUtils
+ .filter((u) => u.slug && u.name)
+ .sort((a, b) => a.name.localeCompare(b.name));
+
+ for (const util of sortedUtils) {
+ const utilContent = generateUtilMarkdown(util);
+ sections.push(utilContent);
+ }
+
+ return sections.join('\n\n');
+}
+
+/**
+ * Trim code to remove module exports and data export statement
+ */
+function trimCode(code: string): string {
+ return code
+ .replace(/')
+ .trim();
+}
+
+/**
+ * Escape special markdown characters in table cells
+ */
+function escapeMarkdown(text: string): string {
+ return text
+ .replace(/\|/g, '\\|')
+ .replace(/\n/g, ' ')
+ .replace(//g, '>');
+}
+
+/**
+ * Generate markdown API table from component properties
+ */
+function generateApiTable(api: ComponentAPI): string {
+ if (!api.properties || api.properties.length === 0) {
+ return '';
+ }
+
+ const rows = api.properties.map((prop) => {
+ const name = prop.required ? `**${prop.name}** (required)` : prop.name;
+ const type = `\`${escapeMarkdown(prop.type)}\``;
+ const defaultVal = prop.default ? `\`${escapeMarkdown(prop.default)}\`` : '-';
+ const description = prop.description ? escapeMarkdown(prop.description) : '-';
+ return `| ${name} | ${type} | ${defaultVal} | ${description} |`;
+ });
+
+ return `| Property | Type | Default | Description |
+|----------|------|---------|-------------|
+${rows.join('\n')}`;
+}
+
+/**
+ * Generate markdown for a component
+ */
+function generateComponentMarkdown(component: (typeof allComponents)[number]): string {
+ const sections: string[] = [];
+
+ // Title and description
+ sections.push(`## ${component.name}`);
+ if (component.description) {
+ sections.push(component.description);
+ }
+
+ // Metadata
+ if (component.category) {
+ sections.push(`**Category:** ${component.category}`);
+ }
+ if (component.layers && component.layers.length > 0) {
+ sections.push(`**Supported Layers:** ${component.layers.join(', ')}`);
+ }
+
+ // Documentation link
+ sections.push(`**Full Documentation:** [${component.name}](${BASE_URL}/docs/components/${component.slug})`);
+
+ // Load example
+ let exampleSource = '';
+ if (component.usageExample) {
+ try {
+ const examplePath = join(
+ process.cwd(),
+ `src/examples/components/${component.slug}/${component.usageExample}.svelte`
+ );
+ exampleSource = readFileSync(examplePath, 'utf-8');
+ exampleSource = trimCode(exampleSource);
+ } catch (e) {
+ // Example file may not exist
+ }
+ }
+
+ if (exampleSource) {
+ sections.push('### Example');
+ sections.push('```svelte\n' + exampleSource + '\n```');
+ }
+
+ // Load API
+ let api: ComponentAPI | null = null;
+ try {
+ const apiPath = join(process.cwd(), `src/generated/api/${component.slug}.json`);
+ const apiContent = readFileSync(apiPath, 'utf-8');
+ api = JSON.parse(apiContent);
+ } catch (e) {
+ // API file may not exist
+ }
+
+ if (api) {
+ sections.push('### API');
+ const table = generateApiTable(api);
+ if (table) {
+ sections.push(table);
+ }
+
+ if (api.extends && api.extends.length > 0) {
+ const extendsList = api.extends.map((e) => `\`${e.fullType}\``).join(', ');
+ sections.push(`**Extends:** ${extendsList}`);
+ }
+ }
+
+ // Related
+ if (component.related && component.related.length > 0) {
+ sections.push('### Related');
+ const relatedLinks = component.related
+ .map((r) => `- [${r}](${BASE_URL}/docs/components/${r})`)
+ .join('\n');
+ sections.push(relatedLinks);
+ }
+
+ return sections.join('\n\n');
+}
+
+/**
+ * Generate markdown for a utility
+ */
+function generateUtilMarkdown(util: (typeof allUtils)[number]): string {
+ const sections: string[] = [];
+
+ // Title and description
+ sections.push(`## ${util.name}`);
+ if (util.description) {
+ sections.push(util.description);
+ }
+
+ // Documentation link
+ sections.push(`**Full Documentation:** [${util.name}](${BASE_URL}/docs/utils/${util.slug})`);
+
+ // Load example
+ let exampleSource = '';
+ if (util.usageExample) {
+ try {
+ const examplePath = join(
+ process.cwd(),
+ `src/examples/utils/${util.slug}/${util.usageExample}.svelte`
+ );
+ exampleSource = readFileSync(examplePath, 'utf-8');
+ exampleSource = trimCode(exampleSource);
+ } catch (e) {
+ // Example file may not exist
+ }
+ }
+
+ if (exampleSource) {
+ sections.push('### Example');
+ sections.push('```svelte\n' + exampleSource + '\n```');
+ }
+
+ // Related
+ if (util.related && util.related.length > 0) {
+ sections.push('### Related');
+ const relatedLinks = util.related
+ .map((r) => `- [${r}](${BASE_URL}/docs/utils/${r})`)
+ .join('\n');
+ sections.push(relatedLinks);
+ }
+
+ return sections.join('\n\n');
+}
diff --git a/docs/src/routes/docs/utils/[name]/+layout.svelte b/docs/src/routes/docs/utils/[name]/+layout.svelte
index ae00dddfe..9acb7e2a3 100644
--- a/docs/src/routes/docs/utils/[name]/+layout.svelte
+++ b/docs/src/routes/docs/utils/[name]/+layout.svelte
@@ -1,11 +1,10 @@
') // remove data export statement
+ .trim();
+}
+
+/**
+ * Generate the full markdown document
+ */
+function generateMarkdown(
+ util: (typeof allUtils)[number],
+ exampleSource: string
+): string {
+ const sections: string[] = [];
+
+ // Title and description
+ sections.push(`# ${util.name}`);
+ if (util.description) {
+ sections.push(util.description);
+ }
+
+ // Documentation link
+ sections.push(`**Full Documentation:** [${util.name}](https://layerchart.com/docs/utils/${util.slug})`);
+
+ // Example
+ if (exampleSource) {
+ sections.push('## Example');
+ sections.push('```svelte\n' + exampleSource + '\n```');
+ }
+
+ // Related
+ if (util.related && util.related.length > 0) {
+ sections.push('## Related');
+ const relatedLinks = util.related
+ .map((r) => `- [${r}](https://layerchart.com/docs/utils/${r})`)
+ .join('\n');
+ sections.push(relatedLinks);
+ }
+
+ return sections.join('\n\n');
+}
diff --git a/docs/src/routes/llms.txt/+server.ts b/docs/src/routes/llms.txt/+server.ts
new file mode 100644
index 000000000..af7538c52
--- /dev/null
+++ b/docs/src/routes/llms.txt/+server.ts
@@ -0,0 +1,117 @@
+import { readdirSync } from 'fs';
+import { join } from 'path';
+import type { RequestHandler } from './$types';
+import { allComponents, allUtils } from 'content-collections';
+
+const BASE_URL = 'https://layerchart.com';
+
+const guides = [
+ { slug: 'getting-started', name: 'Getting Started', description: 'Installation and setup guide for LayerChart' },
+ { slug: 'guides/scales', name: 'Scales', description: 'Understanding scales, domains, and ranges' },
+ { slug: 'guides/layers', name: 'Layers', description: 'Working with SVG, Canvas, and HTML layers' },
+ { slug: 'guides/state', name: 'State', description: 'Managing chart state and reactivity' },
+ { slug: 'guides/styles', name: 'Styles', description: 'Styling and theming your charts' },
+ { slug: 'guides/primitives', name: 'Primitives', description: 'Low-level building blocks for custom charts' },
+ { slug: 'guides/simplified-charts', name: 'Simplified Charts', description: 'Quick chart components for common use cases' },
+ { slug: 'guides/features', name: 'Features', description: 'Overview of LayerChart features and capabilities' }
+];
+
+export const GET: RequestHandler = async () => {
+ const content = generateLlmsTxt();
+
+ return new Response(content, {
+ headers: {
+ 'Content-Type': 'text/plain; charset=utf-8'
+ }
+ });
+};
+
+function generateLlmsTxt(): string {
+ const sections: string[] = [];
+
+ // Header
+ sections.push(`# LayerChart Documentation for LLMs
+
+> LayerChart is a powerful, composable charting library for Svelte built on top of D3.
+
+This file contains links to LLM-optimized documentation in markdown format.`);
+
+ // General section
+ sections.push(`## General
+
+${guides.map((g) => `- [${g.name}](${BASE_URL}/docs/${g.slug}): ${g.description}`).join('\n')}`);
+
+ // Components section
+ const componentsList = allComponents
+ .filter((c) => c.slug && c.name)
+ .sort((a, b) => a.name.localeCompare(b.name))
+ .map((c) => {
+ const description = c.description || `Documentation for ${c.name} component`;
+ return `- [${c.name}](${BASE_URL}/docs/components/${c.slug}/llms.txt): ${description}`;
+ })
+ .join('\n');
+
+ sections.push(`## Components
+
+${componentsList}`);
+
+ // Utilities section
+ const utilsList = allUtils
+ .filter((u) => u.slug && u.name)
+ .sort((a, b) => a.name.localeCompare(b.name))
+ .map((u) => {
+ const description = u.description || `Documentation for ${u.name} utility`;
+ return `- [${u.name}](${BASE_URL}/docs/utils/${u.slug}/llms.txt): ${description}`;
+ })
+ .join('\n');
+
+ sections.push(`## Utilities
+
+${utilsList}`);
+
+ // Examples section
+ const examplesList = getExamplesList();
+ sections.push(`## Examples
+
+${examplesList}`);
+
+ return sections.join('\n\n');
+}
+
+/**
+ * Get list of all examples from the filesystem
+ */
+function getExamplesList(): string {
+ const examplesDir = join(process.cwd(), 'src/examples/components');
+ const examples: string[] = [];
+
+ try {
+ const componentDirs = readdirSync(examplesDir, { withFileTypes: true })
+ .filter((dirent) => dirent.isDirectory())
+ .map((dirent) => dirent.name)
+ .sort();
+
+ for (const componentName of componentDirs) {
+ const componentDir = join(examplesDir, componentName);
+
+ try {
+ const exampleFiles = readdirSync(componentDir)
+ .filter((file) => file.endsWith('.svelte'))
+ .sort();
+
+ for (const exampleFile of exampleFiles) {
+ const exampleName = exampleFile.replace('.svelte', '');
+ examples.push(
+ `- [${componentName}/${exampleName}](${BASE_URL}/docs/components/${componentName}/${exampleName}/llms.txt): Example code for ${componentName}`
+ );
+ }
+ } catch (e) {
+ // Skip directories that can't be read
+ }
+ }
+ } catch (e) {
+ return 'Could not read examples directory.';
+ }
+
+ return examples.join('\n');
+}