Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions apps/www/next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,10 @@ const config = {
{
source: '/docs/:path*.md',
destination: '/llms.mdx/:path*'
},
{
source: '/tokens/:path*.mdx',
destination: '/tokens.mdx/:path*'
}
];
},
Expand Down
4 changes: 3 additions & 1 deletion apps/www/src/app/(llms)/llms-full.txt/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,5 +10,7 @@ export async function GET() {
const header =
'<SYSTEM>This is the full developer documentation for Apsara Design System.</SYSTEM>';

return new Response(header + '\n\n' + scanned.join('\n\n***\n\n'));
return new Response(header + '\n\n' + scanned.join('\n\n***\n\n'), {
headers: { 'Content-Type': 'text/markdown' }
});
}
8 changes: 5 additions & 3 deletions apps/www/src/app/(llms)/llms.mdx/[[...slug]]/route.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { getLLMText } from '@/lib/ai';
import { docs } from '@/lib/source';
import { notFound } from 'next/navigation';
import { type NextRequest, NextResponse } from 'next/server';
import { getLLMText } from '@/lib/ai';
import { docs } from '@/lib/source';

// cached forever
export const revalidate = false;
Expand All @@ -15,7 +15,9 @@ export async function GET(

if (!page) notFound();

return new NextResponse(await getLLMText(page));
return new NextResponse(await getLLMText(page), {
headers: { 'Content-Type': 'text/markdown' }
});
}

export function generateStaticParams() {
Expand Down
18 changes: 16 additions & 2 deletions apps/www/src/app/(llms)/llms.txt/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,24 @@ export const revalidate = false;
export async function GET() {
const scanned: string[] = [];
scanned.push('# Apsara Design System Documentation for LLMs');
scanned.push('## tokens');
scanned.push(
[
'- [Color Tokens](/tokens/colors.mdx): CSS variables for foreground, background, border, and overlay colors',
'- [Spacing Tokens](/tokens/spacing.mdx): CSS variables for spacing scale',
'- [Typography Tokens](/tokens/typography.mdx): CSS variables for fonts, sizes, line heights, and letter spacing',
'- [Effects Tokens](/tokens/effects.mdx): CSS variables for shadows and blurs',
'- [Radius Tokens](/tokens/radius.mdx): CSS variables for border radius'
].join('\n')
);
const map = new Map<string, string[]>();

for (const page of docs.getPages()) {
const dir = page.slugs[0] ?? 'root';
const list = map.get(dir) ?? [];
list.push(`- [${page.data.title}](${page.url}): ${page.data.description}`);
list.push(
`- [${page.data.title}](${page.url}.mdx): ${page.data.description}`
);
map.set(dir, list);
}

Expand All @@ -20,5 +32,7 @@ export async function GET() {
scanned.push(value.join('\n'));
}

return new Response(scanned.join('\n\n'));
return new Response(scanned.join('\n\n'), {
headers: { 'Content-Type': 'text/markdown' }
});
}
123 changes: 123 additions & 0 deletions apps/www/src/app/(llms)/tokens.mdx/[category]/route.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
import { promises as fs } from 'fs';
import { notFound } from 'next/navigation';
import path from 'path';

const STYLES_DIR = path.resolve(
process.cwd(),
'../../packages/raystack/styles'
);

export const revalidate = false;

function stripVar(value: string) {
return value.replace(/var\((--[\w-]+)\)/g, '$1');
}

function parseCSSFile(content: string) {
const sections: {
selector: string;
comment: string | null;
tokens: { name: string; value: string }[];
}[] = [];
let currentSelector: string | null = null;
let currentComment: string | null = null;
let tokens: { name: string; value: string }[] = [];

for (const line of content.split('\n')) {
const trimmed = line.trim();

const selectorMatch = trimmed.match(/^(:root|\[[\w-]+="[\w-]+"\])\s*\{/);
if (selectorMatch) {
currentSelector = selectorMatch[1];
continue;
}

if (trimmed === '}') {
if (currentSelector && tokens.length) {
sections.push({
selector: currentSelector,
comment: currentComment,
tokens: [...tokens]
});
}
currentSelector = null;
tokens = [];
currentComment = null;
continue;
}

const commentMatch = trimmed.match(/^\/\*\s*(.+?)\s*\*\/$/);
if (commentMatch && currentSelector) {
if (tokens.length) {
sections.push({
selector: currentSelector,
comment: currentComment,
tokens: [...tokens]
});
tokens = [];
}
currentComment = commentMatch[1];
if (currentComment.startsWith('Example usage:')) currentComment = null;
continue;
}

const varMatch = trimmed.match(
/^(--[\w-]+)\s*:\s*(.+?)\s*;(?:\s*\/\*\s*(.+?)\s*\*\/)?$/
);
if (varMatch && currentSelector) {
tokens.push({ name: varMatch[1], value: stripVar(varMatch[2]) });
}
}

return sections;
}

function toMarkdown(
category: string,
sections: ReturnType<typeof parseCSSFile>
) {
const title = category.charAt(0).toUpperCase() + category.slice(1);
let output = `# ${title} Tokens\n`;

for (const section of sections) {
const prefix =
section.selector === ':root' ? title : `${title} (${section.selector})`;
const heading = section.comment ? `${prefix} - ${section.comment}` : prefix;

output += `\n## ${heading}\n`;
for (const t of section.tokens) {
output += `${t.name}: ${t.value}\n`;
}
}

return output;
}

const CATEGORIES = ['colors', 'effects', 'radius', 'spacing', 'typography'];

export async function GET(
_req: Request,
{ params }: { params: Promise<{ category: string }> }
) {
const { category } = await params;

if (!CATEGORIES.includes(category)) notFound();

const filePath = path.join(STYLES_DIR, `${category}.css`);
const exists = await fs.stat(filePath).then(
() => true,
() => false
);
if (!exists) notFound();

const content = await fs.readFile(filePath, 'utf-8');

const sections = parseCSSFile(content);
return new Response(toMarkdown(category, sections), {
headers: { 'Content-Type': 'text/markdown' }
});
}

export function generateStaticParams() {
return CATEGORIES.map(category => ({ category }));
}
Loading