From cb01a2cc370d447222fa542b9d346ecc2e01bcf0 Mon Sep 17 00:00:00 2001 From: Scott Rhamy Date: Sun, 4 Jan 2026 16:13:12 -0500 Subject: [PATCH 1/9] Open in LLM feature --- docs/src/lib/components/OpenLLMs.svelte | 97 +++++++++++++++++++ .../docs/components/[name]/+layout.svelte | 3 + docs/vite.config.ts | 4 +- 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 docs/src/lib/components/OpenLLMs.svelte diff --git a/docs/src/lib/components/OpenLLMs.svelte b/docs/src/lib/components/OpenLLMs.svelte new file mode 100644 index 000000000..deb1a0dc8 --- /dev/null +++ b/docs/src/lib/components/OpenLLMs.svelte @@ -0,0 +1,97 @@ + + + + + diff --git a/docs/src/routes/docs/components/[name]/+layout.svelte b/docs/src/routes/docs/components/[name]/+layout.svelte index 6d4b92132..598943356 100644 --- a/docs/src/routes/docs/components/[name]/+layout.svelte +++ b/docs/src/routes/docs/components/[name]/+layout.svelte @@ -2,6 +2,7 @@ import { getSettings } from 'layerchart'; import { Button, Menu, Switch, Toggle, ToggleGroup, ToggleOption, Tooltip } from 'svelte-ux'; import { toTitleCase } from '@layerstack/utils'; + import OpenLLMsbutton from '$lib/components/OpenLLMs.svelte'; import ViewSourceButton from '$lib/components/ViewSourceButton.svelte'; import { examples } from '$lib/context.js'; @@ -138,6 +139,8 @@ /> {/if} + + ` } } }) /*, devtoolsJson()*/ From d11704b0854d1b2346270eb46b46f73341c2c7b8 Mon Sep 17 00:00:00 2001 From: Scott Rhamy Date: Sun, 4 Jan 2026 16:51:51 -0500 Subject: [PATCH 2/9] removed grok --- docs/src/lib/components/OpenLLMs.svelte | 20 -------------------- docs/vite.config.ts | 4 +--- 2 files changed, 1 insertion(+), 23 deletions(-) diff --git a/docs/src/lib/components/OpenLLMs.svelte b/docs/src/lib/components/OpenLLMs.svelte index deb1a0dc8..e6faa322a 100644 --- a/docs/src/lib/components/OpenLLMs.svelte +++ b/docs/src/lib/components/OpenLLMs.svelte @@ -5,10 +5,7 @@ import ChevronDownIcon from '~icons/lucide/chevron-down'; import SimpleIconsOpenai from '~icons/simple-icons/openai'; import SimpleIconsClaude from '~icons/simple-icons/claude'; - import CustomGrokIcon from '~icons/custom-brands/grok'; import ClipboardIcon from '~icons/lucide/clipboard-copy'; - // import CustomDeepseekIcon from '~icons/custom-brands/deepseek'; - // import SimpleIconsGemini from '~icons/simple-icons/googlegemini'; let { buttonLabel = 'LLM Chat' } = $props(); @@ -37,23 +34,6 @@ value: generateLlmUrl('https://claude.ai/new?q='), icon: SimpleIconsClaude }, - /* Gemini does not support native URL query params */ - // { - // label: 'Open in Gemini', - // value: generateLlmUrl('https://google.com'), - // icon: SimpleIconsGemini - // }, - { - label: 'Open in Grok', - value: generateLlmUrl('https://grok.com/?q='), - icon: CustomGrokIcon - }, - /* DeepSeek does not support native URL query params */ - // { - // label: 'Open in DeepSeek', - // value: generateLlmUrl('https://deepseek.com/?q='), - // icon: CustomDeepseekIcon - // }, { label: 'Copy Page URL to Clipboard', value: null, diff --git a/docs/vite.config.ts b/docs/vite.config.ts index c19895e39..fbdfa304b 100644 --- a/docs/vite.config.ts +++ b/docs/vite.config.ts @@ -20,9 +20,7 @@ export default defineConfig({ svelteux: ``, shadcnsvelte: ``, skeleton: ``, - daisyUI: ``, - grok: `Grok` - // deepseek: `` + daisyUI: `` } } }) /*, devtoolsJson()*/ From 9867588093fd3877899c8862811e987c7aba24d0 Mon Sep 17 00:00:00 2001 From: Scott Rhamy Date: Wed, 7 Jan 2026 18:10:36 -0500 Subject: [PATCH 3/9] open-in-llm-component - rewrote copy for LLMs --- docs/src/lib/components/DocsMenu.svelte | 4 +- docs/src/lib/components/OpenLLMs.svelte | 175 +++++++++++++----- .../docs/components/[name]/+layout.svelte | 11 +- docs/src/routes/docs/guides/LLMs/+page.md | 35 ++++ 4 files changed, 166 insertions(+), 59 deletions(-) create mode 100644 docs/src/routes/docs/guides/LLMs/+page.md 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/OpenLLMs.svelte b/docs/src/lib/components/OpenLLMs.svelte index e6faa322a..64a9a330f 100644 --- a/docs/src/lib/components/OpenLLMs.svelte +++ b/docs/src/lib/components/OpenLLMs.svelte @@ -1,77 +1,156 @@ - - + + + + + (openSourceModal = false)} + class="max-h-[98dvh] md:max-h-[90dvh] max-w-[98vw] md:max-w-[90vw] grid grid-rows-[auto_1fr_auto]" +> +
+
+
Source
+
{metadata?.sourceUrl}
- - {#each llms as llm} - { - toggleOff(); - if (llm.onclick) { - llm.onclick(); - } else { - window.open(llm.value, '_blank'); - } - }} - > - - {llm.label} - - {/each} - - - + + {#if metadata?.sourceUrl} + + {/if} +
+ +
+ +
+ +
+ +
+
diff --git a/docs/src/routes/docs/components/[name]/+layout.svelte b/docs/src/routes/docs/components/[name]/+layout.svelte index 598943356..3371fa84a 100644 --- a/docs/src/routes/docs/components/[name]/+layout.svelte +++ b/docs/src/routes/docs/components/[name]/+layout.svelte @@ -130,16 +130,7 @@
{metadata.description}
- {#if 'source' in metadata} - - {/if} - - + +{#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 index c4f5df52b..923b6c114 100644 --- a/docs/src/routes/docs/guides/LLMs/+page.md +++ b/docs/src/routes/docs/guides/LLMs/+page.md @@ -1,33 +1,42 @@ +--- +title: LLMs +--- + -# LLMs Documentation - -The Layerchart documentation pages are designed to be accessible for humans developers using LLMs and large language models (LLMs) for effective content ingestion and training. +The Layerchart documentation pages are designed to be accessible for humans developers using LLMs as well as large language models (LLMs) ingestesting training data. -## For the Humans :icon{name="lucide:user" class="inline-block relative -top-0.5 left-0.5 w-7 h-7"} +## :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 addtional helpful options. -## For the Bots :icon{name="lucide:bot" class="inline-block relative -top-0.5 left-0.5"} +::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 (those deemed irrelevant to LLMs). +Not all pages may support the `/llms.txt` suffix (ie those deemed irrelevant to LLMs). :: -## Accessing LLM-friendly Documentation +## 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. +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. - - **Standard Page**: The LineChart component documentation is available at [layerchart.com/docs/components/LineChart](/docs/components/LineChart). - - **LLM-friendly Version**: Append `/llms.txt` to access it at [layerchart.com/docs/components/Linechart/llms.txt](/docs/components/LineChart/llms.txt). +:::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). +::: -2. 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. 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. -3. For a complete, consolidated view of 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. +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..ba264cc3a --- /dev/null +++ b/docs/src/routes/docs/guides/[name]/llms.txt/+server.ts @@ -0,0 +1,34 @@ +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`); + } + + content = processMarkdownContent(content); + + // Generate title from name + const 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(/[\s\S]*?<\/script>\n*/g, '') + .replace(/\n*\s*export \{ data \};\s*\n*\s*<\/script>/gm, '\n') + .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..e32fd70bc 100644 --- a/docs/src/routes/docs/utils/[name]/+layout.svelte +++ b/docs/src/routes/docs/utils/[name]/+layout.svelte @@ -1,7 +1,7 @@ ') // 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'); +} From 9c8a128ef279fb73d044a43097e479647a1a8537 Mon Sep 17 00:00:00 2001 From: Scott Rhamy Date: Sat, 24 Jan 2026 08:17:35 -0500 Subject: [PATCH 7/9] fix spelling error --- docs/src/routes/docs/guides/LLMs/+page.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/routes/docs/guides/LLMs/+page.md b/docs/src/routes/docs/guides/LLMs/+page.md index 923b6c114..dc591b642 100644 --- a/docs/src/routes/docs/guides/LLMs/+page.md +++ b/docs/src/routes/docs/guides/LLMs/+page.md @@ -7,7 +7,7 @@ title: LLMs import ViewSourceButton from "$lib/components/ViewSourceButton.svelte"; -The Layerchart documentation pages are designed to be accessible for humans developers using LLMs as well as large language models (LLMs) ingestesting training data. +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 From eb517259421e00e6792343dbaefe0c951feded49 Mon Sep 17 00:00:00 2001 From: Scott Rhamy Date: Sat, 24 Jan 2026 08:24:11 -0500 Subject: [PATCH 8/9] another spelling fix --- docs/src/routes/docs/guides/LLMs/+page.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/src/routes/docs/guides/LLMs/+page.md b/docs/src/routes/docs/guides/LLMs/+page.md index dc591b642..1e7663845 100644 --- a/docs/src/routes/docs/guides/LLMs/+page.md +++ b/docs/src/routes/docs/guides/LLMs/+page.md @@ -13,7 +13,7 @@ The Layerchart documentation pages are designed to be accessible for humans deve -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 addtional helpful options. +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. From 3a91975517103acfbd0d4ccecc677ef63fba7af5 Mon Sep 17 00:00:00 2001 From: Scott Rhamy Date: Mon, 26 Jan 2026 08:04:38 -0500 Subject: [PATCH 9/9] Change OpenLLMs.svelte to OpenWithButton.svelte - cleaned up logic on `ddocs/src/routes/docs/guides/[name]/llms.txt/+server.ts` was getting title by url name, now uses frontmatter like others. --- ...{OpenLLMs.svelte => OpenWithButton.svelte} | 0 .../docs/components/[name]/+layout.svelte | 5 ++-- .../components/[name]/[example]/+page.svelte | 4 ++-- .../docs/getting-started/+layout.svelte | 4 ++-- docs/src/routes/docs/guides/+layout.svelte | 6 ++--- docs/src/routes/docs/guides/LLMs/+page.md | 5 ++-- .../docs/guides/[name]/llms.txt/+server.ts | 24 +++++++++++++++---- .../routes/docs/utils/[name]/+layout.svelte | 5 ++-- 8 files changed, 32 insertions(+), 21 deletions(-) rename docs/src/lib/components/{OpenLLMs.svelte => OpenWithButton.svelte} (100%) diff --git a/docs/src/lib/components/OpenLLMs.svelte b/docs/src/lib/components/OpenWithButton.svelte similarity index 100% rename from docs/src/lib/components/OpenLLMs.svelte rename to docs/src/lib/components/OpenWithButton.svelte diff --git a/docs/src/routes/docs/components/[name]/+layout.svelte b/docs/src/routes/docs/components/[name]/+layout.svelte index 456b53b9d..e7ef7c25a 100644 --- a/docs/src/routes/docs/components/[name]/+layout.svelte +++ b/docs/src/routes/docs/components/[name]/+layout.svelte @@ -2,14 +2,13 @@ import { getSettings } from 'layerchart'; import { Button, Menu, Switch, Toggle, ToggleGroup, ToggleOption, Tooltip } from 'svelte-ux'; import { toTitleCase } from '@layerstack/utils'; - import OpenLLMsbutton from '$lib/components/OpenLLMs.svelte'; + import OpenWithButton from '$lib/components/OpenWithButton.svelte'; import { examples } from '$lib/context.js'; import { loadExample } from '$lib/examples.js'; import { page } from '$app/state'; import LucideSettings from '~icons/lucide/settings'; - import LucideCode from '~icons/lucide/code'; import LucideChevronLeft from '~icons/lucide/chevron-left'; import LucideChevronRight from '~icons/lucide/chevron-right'; @@ -129,7 +128,7 @@
{metadata.description}
- + + {#if data.metadata.title !== 'LLMs'}
- +
{/if} diff --git a/docs/src/routes/docs/guides/LLMs/+page.md b/docs/src/routes/docs/guides/LLMs/+page.md index 1e7663845..1fc3e3de5 100644 --- a/docs/src/routes/docs/guides/LLMs/+page.md +++ b/docs/src/routes/docs/guides/LLMs/+page.md @@ -3,15 +3,14 @@ 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. diff --git a/docs/src/routes/docs/guides/[name]/llms.txt/+server.ts b/docs/src/routes/docs/guides/[name]/llms.txt/+server.ts index ba264cc3a..ff4474ebe 100644 --- a/docs/src/routes/docs/guides/[name]/llms.txt/+server.ts +++ b/docs/src/routes/docs/guides/[name]/llms.txt/+server.ts @@ -15,13 +15,27 @@ export const GET: RequestHandler = async ({ params }) => { 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); - // Generate title from name - const title = name - .split('-') - .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(' '); + // 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}`; diff --git a/docs/src/routes/docs/utils/[name]/+layout.svelte b/docs/src/routes/docs/utils/[name]/+layout.svelte index e32fd70bc..9acb7e2a3 100644 --- a/docs/src/routes/docs/utils/[name]/+layout.svelte +++ b/docs/src/routes/docs/utils/[name]/+layout.svelte @@ -1,11 +1,10 @@