Skip to content

Commit 706ba9c

Browse files
committed
Correctly set llms.txt base url based on deployed origin (use next.layerchart.com instead of layerchart.com)
1 parent b05a264 commit 706ba9c

6 files changed

Lines changed: 48 additions & 74 deletions

File tree

docs/src/lib/llms/utils.ts

Lines changed: 38 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -32,22 +32,22 @@ const catalogFiles = import.meta.glob<string>('/src/examples/catalog/*.json', {
3232
import: 'default'
3333
});
3434

35-
const BASE_URL = 'https://layerchart.com';
36-
3735
/** Generate URL for a docs page */
38-
function docsUrl(type: 'components' | 'utils' | 'guides', slug: string): string {
36+
function docsUrl(baseUrl: string, type: 'components' | 'utils' | 'guides', slug: string): string {
3937
if (type === 'guides') {
40-
return `${BASE_URL}/docs/${slug}`;
38+
return `${baseUrl}/docs/${slug}`;
4139
}
42-
return `${BASE_URL}/docs/${type}/${slug}`;
40+
return `${baseUrl}/docs/${type}/${slug}`;
4341
}
4442

4543
/** Generate URL for an llms.txt endpoint */
46-
function llmsUrl(type: 'components' | 'utils' | 'guides', slug: string): string {
47-
return `${docsUrl(type, slug)}/llms.txt`;
44+
function llmsUrl(baseUrl: string, type: 'components' | 'utils' | 'guides', slug: string): string {
45+
return `${docsUrl(baseUrl, type, slug)}/llms.txt`;
4846
}
4947

5048
interface GenerateMarkdownOptions {
49+
/** Base URL for the site (e.g. 'https://layerchart.com') */
50+
baseUrl: string;
5151
/** Heading level for the title. 1 = "#", 2 = "##", etc. Default: 1 */
5252
headingLevel?: number;
5353
/** Whether to inline all example code blocks. Default: false */
@@ -61,7 +61,8 @@ interface GenerateMarkdownOptions {
6161
function inlineExampleDirectives(
6262
content: string,
6363
slug: string,
64-
type: 'components' | 'utils'
64+
type: 'components' | 'utils',
65+
baseUrl: string
6566
): string {
6667
// Remove HTML comments first to avoid inlining commented-out examples
6768
content = content.replace(/<!--[\s\S]*?-->/g, '');
@@ -75,7 +76,7 @@ function inlineExampleDirectives(
7576
if (raw) {
7677
return '```svelte\n' + trimCode(raw) + '\n```';
7778
}
78-
return `See example: [${component}/${name}](${docsUrl('components', component)}/${name})`;
79+
return `See example: [${component}/${name}](${docsUrl(baseUrl, 'components', component)}/${name})`;
7980
}
8081
);
8182

@@ -94,7 +95,7 @@ function inlineExampleDirectives(
9495
/**
9596
* Process markdown content for LLMs by removing custom syntax and converting to vanilla markdown
9697
*/
97-
function processMarkdownContent(content: string): string {
98+
function processMarkdownContent(content: string, baseUrl: string): string {
9899
// Remove frontmatter (YAML between --- markers at start of file)
99100
content = content.replace(/^---\n[\s\S]*?\n---\n*/, '');
100101

@@ -187,7 +188,7 @@ function processMarkdownContent(content: string): string {
187188
content = content.replace(
188189
/:example\{component="([^"]+)"\s+name="([^"]+)"[^}]*\}/g,
189190
(_match, comp: string, name: string) =>
190-
`See example: [${comp}/${name}](${docsUrl('components', comp)}/${name})`
191+
`See example: [${comp}/${name}](${docsUrl(baseUrl, 'components', comp)}/${name})`
191192
);
192193

193194
// Convert remaining :example directives (same-component, not inlined) to plain text
@@ -242,9 +243,9 @@ ${rows.join('\n')}`;
242243
*/
243244
export function generateComponentMarkdown(
244245
component: (typeof allComponents)[number],
245-
options: GenerateMarkdownOptions = {}
246+
options: GenerateMarkdownOptions
246247
): string {
247-
const { headingLevel = 1, inlineExamples: shouldInlineExamples = false } = options;
248+
const { baseUrl, headingLevel = 1, inlineExamples: shouldInlineExamples = false } = options;
248249
const h = (level: number) => '#'.repeat(level);
249250

250251
const sections: string[] = [];
@@ -267,9 +268,9 @@ export function generateComponentMarkdown(
267268
if (component.content) {
268269
let rawContent = component.content;
269270
if (shouldInlineExamples) {
270-
rawContent = inlineExampleDirectives(rawContent, component.slug, 'components');
271+
rawContent = inlineExampleDirectives(rawContent, component.slug, 'components', baseUrl);
271272
}
272-
const processed = processMarkdownContent(rawContent);
273+
const processed = processMarkdownContent(rawContent, baseUrl);
273274
if (processed) {
274275
sections.push(processed);
275276
}
@@ -303,46 +304,17 @@ export function generateComponentMarkdown(
303304
if (examples && examples.length > 0) {
304305
sections.push(`${h(headingLevel + 1)} Examples`);
305306
const exampleLinks = examples
306-
.map((ex) => `- [${ex.name}](${docsUrl('components', component.slug)}/${ex.name})`)
307+
.map((ex) => `- [${ex.name}](${docsUrl(baseUrl, 'components', component.slug)}/${ex.name})`)
307308
.join('\n');
308309
sections.push(exampleLinks);
309310
}
310-
311-
// TODO: should we include usage examples?
312-
// Additional usage in other component examples (deduplicated)
313-
// const usage = catalog.usage as Array<{ example: string; component: string; path: string }>;
314-
// if (usage && usage.length > 0) {
315-
// const exampleNames = new Set(examples?.map((ex) => ex.name) ?? []);
316-
// const seen = new Set<string>();
317-
// const uniqueUsage = usage.filter((item) => {
318-
// // Exclude if already shown in main examples
319-
// if (item.component === catalog.component && exampleNames.has(item.example)) {
320-
// return false;
321-
// }
322-
// const key = `${item.component}::${item.example}`;
323-
// if (seen.has(key)) return false;
324-
// seen.add(key);
325-
// return true;
326-
// });
327-
328-
// if (uniqueUsage.length > 0) {
329-
// sections.push(`${h(headingLevel + 1)} Additional Usage`);
330-
// const usageLinks = uniqueUsage
331-
// .map(
332-
// (u) =>
333-
// `- [${u.component}/${u.example}](${BASE_URL}/docs/components/${u.component}/${u.example})`
334-
// )
335-
// .join('\n');
336-
// sections.push(usageLinks);
337-
// }
338-
// }
339311
}
340312

341313
// Related
342314
if (component.related && component.related.length > 0) {
343315
sections.push(`${h(headingLevel + 1)} Related`);
344316
const relatedLinks = component.related
345-
.map((r) => `- [${r}](${docsUrl('components', r)})`)
317+
.map((r) => `- [${r}](${docsUrl(baseUrl, 'components', r)})`)
346318
.join('\n');
347319
sections.push(relatedLinks);
348320
}
@@ -355,9 +327,9 @@ export function generateComponentMarkdown(
355327
*/
356328
export function generateUtilMarkdown(
357329
util: (typeof allUtils)[number],
358-
options: GenerateMarkdownOptions = {}
330+
options: GenerateMarkdownOptions
359331
): string {
360-
const { headingLevel = 1, inlineExamples: shouldInlineExamples = false } = options;
332+
const { baseUrl, headingLevel = 1, inlineExamples: shouldInlineExamples = false } = options;
361333
const h = (level: number) => '#'.repeat(level);
362334

363335
const sections: string[] = [];
@@ -372,9 +344,9 @@ export function generateUtilMarkdown(
372344
if (util.content) {
373345
let rawContent = util.content;
374346
if (shouldInlineExamples) {
375-
rawContent = inlineExampleDirectives(rawContent, util.slug, 'utils');
347+
rawContent = inlineExampleDirectives(rawContent, util.slug, 'utils', baseUrl);
376348
}
377-
const processed = processMarkdownContent(rawContent);
349+
const processed = processMarkdownContent(rawContent, baseUrl);
378350
if (processed) {
379351
sections.push(processed);
380352
}
@@ -383,7 +355,7 @@ export function generateUtilMarkdown(
383355
// Related
384356
if (util.related && util.related.length > 0) {
385357
sections.push(`${h(headingLevel + 1)} Related`);
386-
const relatedLinks = util.related.map((r) => `- [${r}](${docsUrl('utils', r)})`).join('\n');
358+
const relatedLinks = util.related.map((r) => `- [${r}](${docsUrl(baseUrl, 'utils', r)})`).join('\n');
387359
sections.push(relatedLinks);
388360
}
389361

@@ -450,7 +422,7 @@ export function generateGuideMarkdown(options: {
450422
.join(' ');
451423
}
452424

453-
const content = processMarkdownContent(raw);
425+
const content = processMarkdownContent(raw, '');
454426
return `# ${title}\n\n${content}`;
455427
}
456428

@@ -486,6 +458,7 @@ function getAllExamplePaths(): string[] {
486458
}
487459

488460
interface CollectionListOptions {
461+
baseUrl: string;
489462
title: string;
490463
items: Array<{ slug: string; name: string; description?: string }>;
491464
type: 'components' | 'utils' | 'guides';
@@ -495,7 +468,7 @@ interface CollectionListOptions {
495468
* Generate a markdown section listing collection items as links to their llms.txt endpoints.
496469
*/
497470
function generateCollectionListSection(options: CollectionListOptions): string {
498-
const { title, items, type } = options;
471+
const { baseUrl, title, items, type } = options;
499472

500473
const fallbackLabel =
501474
type === 'components' ? 'component' : type === 'utils' ? 'utility' : 'guide';
@@ -505,7 +478,7 @@ function generateCollectionListSection(options: CollectionListOptions): string {
505478
.sort((a, b) => a.name.localeCompare(b.name))
506479
.map((item) => {
507480
const description = item.description || `Documentation for ${item.name} ${fallbackLabel}`;
508-
return `- [${item.name}](${llmsUrl(type, item.slug)}): ${description}`;
481+
return `- [${item.name}](${llmsUrl(baseUrl, type, item.slug)}): ${description}`;
509482
});
510483

511484
return `## ${title}\n\n${lines.join('\n')}`;
@@ -514,7 +487,7 @@ function generateCollectionListSection(options: CollectionListOptions): string {
514487
/**
515488
* Generate markdown for a single component example
516489
*/
517-
export function generateExampleMarkdown(componentSlug: string, exampleName: string): string | null {
490+
export function generateExampleMarkdown(baseUrl: string, componentSlug: string, exampleName: string): string | null {
518491
const raw = getExampleSource('components', componentSlug, exampleName);
519492
if (!raw) return null;
520493

@@ -547,7 +520,7 @@ export function generateExampleMarkdown(componentSlug: string, exampleName: stri
547520
if (usedComponents.length > 0) {
548521
sections.push('## Components');
549522
const links = usedComponents
550-
.map((comp) => `- [${comp.name}](${llmsUrl('components', comp.slug)})`)
523+
.map((comp) => `- [${comp.name}](${llmsUrl(baseUrl, 'components', comp.slug)})`)
551524
.join('\n');
552525
sections.push(links);
553526
}
@@ -558,7 +531,7 @@ export function generateExampleMarkdown(componentSlug: string, exampleName: stri
558531
/**
559532
* Generate the llms.txt index with links to all documentation
560533
*/
561-
export function generateLlmsTxt(): string {
534+
export function generateLlmsTxt(baseUrl: string): string {
562535
const sections: string[] = [];
563536

564537
// Header
@@ -570,12 +543,13 @@ This file contains links to LLM-optimized documentation in markdown format.`);
570543

571544
// Guides
572545
sections.push(
573-
generateCollectionListSection({ title: 'Guides', items: getSortedGuides(), type: 'guides' })
546+
generateCollectionListSection({ baseUrl, title: 'Guides', items: getSortedGuides(), type: 'guides' })
574547
);
575548

576549
// Components
577550
sections.push(
578551
generateCollectionListSection({
552+
baseUrl,
579553
title: 'Components',
580554
items: allComponents,
581555
type: 'components'
@@ -584,7 +558,7 @@ This file contains links to LLM-optimized documentation in markdown format.`);
584558

585559
// Utilities
586560
sections.push(
587-
generateCollectionListSection({ title: 'Utilities', items: allUtils, type: 'utils' })
561+
generateCollectionListSection({ baseUrl, title: 'Utilities', items: allUtils, type: 'utils' })
588562
);
589563

590564
// Examples
@@ -595,7 +569,7 @@ This file contains links to LLM-optimized documentation in markdown format.`);
595569
if (match) {
596570
const [, componentName, exampleName] = match;
597571
examples.push(
598-
`- [${componentName}/${exampleName}](${llmsUrl('components', `${componentName}/${exampleName}`)}): Example code for ${componentName}`
572+
`- [${componentName}/${exampleName}](${llmsUrl(baseUrl, 'components', `${componentName}/${exampleName}`)}): Example code for ${componentName}`
599573
);
600574
}
601575
}
@@ -609,7 +583,7 @@ This file contains links to LLM-optimized documentation in markdown format.`);
609583
/**
610584
* Generate the full llms.txt with all components and utilities
611585
*/
612-
export function generateFullLlmsTxt(): string {
586+
export function generateFullLlmsTxt(baseUrl: string): string {
613587
const sections: string[] = [];
614588

615589
// Header
@@ -620,7 +594,7 @@ export function generateFullLlmsTxt(): string {
620594
This file contains the complete LLM-optimized documentation for all components and utilities.`);
621595

622596
sections.push(
623-
generateCollectionListSection({ title: 'Guides', items: getSortedGuides(), type: 'guides' })
597+
generateCollectionListSection({ baseUrl, title: 'Guides', items: getSortedGuides(), type: 'guides' })
624598
);
625599

626600
// Components section - full content
@@ -632,7 +606,7 @@ This file contains the complete LLM-optimized documentation for all components a
632606
.sort((a, b) => a.name.localeCompare(b.name));
633607

634608
for (const component of sortedComponents) {
635-
sections.push(generateComponentMarkdown(component, { headingLevel: 2 }));
609+
sections.push(generateComponentMarkdown(component, { baseUrl, headingLevel: 2 }));
636610
}
637611

638612
// Utilities section - full content
@@ -644,7 +618,7 @@ This file contains the complete LLM-optimized documentation for all components a
644618
.sort((a, b) => a.name.localeCompare(b.name));
645619

646620
for (const util of sortedUtils) {
647-
sections.push(generateUtilMarkdown(util, { headingLevel: 2 }));
621+
sections.push(generateUtilMarkdown(util, { baseUrl, headingLevel: 2 }));
648622
}
649623

650624
return sections.join('\n\n');

docs/src/routes/docs/components/[name]/[example]/llms.txt/+server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@ import { error } from '@sveltejs/kit';
22
import type { RequestHandler } from './$types';
33
import { generateExampleMarkdown, markdownResponse } from '$lib/llms/utils.js';
44

5-
export const GET: RequestHandler = async ({ params }) => {
5+
export const GET: RequestHandler = async ({ params, url }) => {
66
const { name, example } = params;
77

8-
const markdown = generateExampleMarkdown(name, example);
8+
const markdown = generateExampleMarkdown(url.origin, name, example);
99
if (!markdown) {
1010
error(404, `Example "${example}" not found for component "${name}"`);
1111
}

docs/src/routes/docs/components/[name]/llms.txt/+server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,15 @@ import type { RequestHandler } from './$types';
33
import { allComponents } from 'content-collections';
44
import { generateComponentMarkdown, markdownResponse } from '$lib/llms/utils.js';
55

6-
export const GET: RequestHandler = async ({ params }) => {
6+
export const GET: RequestHandler = async ({ params, url }) => {
77
const { name } = params;
88

99
const component = allComponents.find((c) => c.slug === name);
1010
if (!component) {
1111
error(404, `Component "${name}" not found`);
1212
}
1313

14-
const markdown = generateComponentMarkdown(component, { inlineExamples: true });
14+
const markdown = generateComponentMarkdown(component, { baseUrl: url.origin, inlineExamples: true });
1515

1616
return markdownResponse(markdown, `${name}.md`);
1717
};
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { RequestHandler } from './$types';
22
import { generateFullLlmsTxt, markdownResponse } from '$lib/llms/utils.js';
33

4-
export const GET: RequestHandler = async () => {
5-
return markdownResponse(generateFullLlmsTxt(), 'llms-full.md');
4+
export const GET: RequestHandler = async ({ url }) => {
5+
return markdownResponse(generateFullLlmsTxt(url.origin), 'llms-full.md');
66
};

docs/src/routes/docs/utils/[name]/llms.txt/+server.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ import type { RequestHandler } from './$types';
33
import { allUtils } from 'content-collections';
44
import { generateUtilMarkdown, markdownResponse } from '$lib/llms/utils.js';
55

6-
export const GET: RequestHandler = async ({ params }) => {
6+
export const GET: RequestHandler = async ({ params, url }) => {
77
const { name } = params;
88

99
const util = allUtils.find((u) => u.slug === name);
1010
if (!util) {
1111
error(404, `Utility "${name}" not found`);
1212
}
1313

14-
const markdown = generateUtilMarkdown(util, { inlineExamples: true });
14+
const markdown = generateUtilMarkdown(util, { baseUrl: url.origin, inlineExamples: true });
1515
return markdownResponse(markdown, `${name}.md`);
1616
};
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { RequestHandler } from './$types';
22
import { generateLlmsTxt, markdownResponse } from '$lib/llms/utils.js';
33

4-
export const GET: RequestHandler = async () => {
5-
return markdownResponse(generateLlmsTxt(), 'llms.md');
4+
export const GET: RequestHandler = async ({ url }) => {
5+
return markdownResponse(generateLlmsTxt(url.origin), 'llms.md');
66
};

0 commit comments

Comments
 (0)