diff --git a/README.md b/README.md index af82559cb7..69c36bb1d1 100644 --- a/README.md +++ b/README.md @@ -173,6 +173,23 @@ npm start # Starts local development server --- +## AI-Friendly Documentation + +Lime Elements generates Context7-compatible markdown documentation to help AI coding assistants and LLMs better understand and use our component library. This documentation is automatically published with each release and includes: + +- 📄 **Component API docs** - Detailed markdown documentation for all components +- 📚 **Design guidelines** - Best practices and usage patterns +- 🎯 **Usage rules** - Context7 rules for proper component usage +- 🔗 **Navigation index** - Searchable overview of all components and guides + +The documentation is available at: +- **Latest version:** [lundalogik.github.io/lime-elements/versions/latest/markdown-docs/](https://lundalogik.github.io/lime-elements/versions/latest/markdown-docs/) +- **Configuration:** [context7.json](https://github.com/Lundalogik/lime-elements/blob/main/context7.json) + +This makes Lime Elements easier to work with when using AI pair programming tools like GitHub Copilot, Cursor, or Claude Code. + +--- + ## 📄 License Lime Elements is open source software licensed under the [Apache 2.0 License](https://opensource.org/licenses/Apache-2.0). diff --git a/context7.json b/context7.json new file mode 100644 index 0000000000..bf01786f11 --- /dev/null +++ b/context7.json @@ -0,0 +1,29 @@ +{ + "$schema": "https://context7.com/schema/context7.json", + "projectTitle": "Lime Elements", + "description": "Modern web component library for enterprise applications built with Stencil. Provides accessible, customizable UI components with comprehensive design system.", + "branch": "gh-pages", + "folders": ["versions/latest/markdown-docs"], + "excludeFolders": [ + "versions/latest/build", + "versions/latest/assets", + "versions/latest/style", + "versions/latest/markdown-docs/**/examples" + ], + "rules": [ + "Lime Elements components are web components built with Stencil", + "Use kebab-case for component tags (e.g., , )", + "All components are prefixed with 'limel-'", + "Install via npm: npm install @limetech/lime-elements", + "TypeScript is fully supported with built-in type definitions", + "Components use shadow DOM and work with any framework (React, Vue, Angular, vanilla JS)", + "CSS custom properties are used for theming and customization", + "Icons require setup - components accept icon names that correspond to SVG filenames", + "Most components emit custom events - listen with addEventListener or framework event bindings", + "Components inherit font styles from parent - set font-family on body or parent element", + "Use the --lime-primary-color CSS variable to set the accent color for your application", + "Import components via script tag or module bundler as needed", + "For dark mode support, load color-palette-extended.css from the package", + "All prop names use camelCase in JavaScript/TypeScript but kebab-case in HTML attributes" + ] +} diff --git a/generate-context7-docs.cjs b/generate-context7-docs.cjs new file mode 100644 index 0000000000..9a99ab2a9a --- /dev/null +++ b/generate-context7-docs.cjs @@ -0,0 +1,281 @@ +/* eslint-env node */ +const shell = require('shelljs'); +const fs = require('node:fs'); +const path = require('node:path'); +const argv = require('yargs').argv; + +const OUTPUT_DIR = 'www/markdown-docs'; +const GUIDES_DIR = `${OUTPUT_DIR}/guides`; + +if (argv.h !== undefined) { + shell.echo(` +usage: node generate-context7-docs.cjs + `); + shell.exit(0); +} + +shell.echo('=== Generating Context7-compatible documentation ===\n'); + +try { + shell.echo('Building component markdown documentation...'); + if ( + shell.exec( + 'cross-env-shell NODE_ENV=prod SASS_PATH=node_modules "npx stencil build --config stencil.config.context7.ts"' + ).code !== 0 + ) { + shell.echo('ERROR: Stencil build failed!'); + shell.exit(1); + } + shell.echo('✓ Component READMEs generated\n'); + + shell.echo('Creating guides directory...'); + shell.mkdir('-p', GUIDES_DIR); + shell.echo('✓ Guides directory created\n'); + + shell.echo('Copying root-level documentation...'); + const rootDocs = ['README.md', 'CONTRIBUTING.md']; + for (const doc of rootDocs) { + if (fs.existsSync(doc)) { + shell.cp(doc, OUTPUT_DIR); + shell.echo(` ✓ Copied ${doc}`); + } else { + shell.echo(` ⚠ Warning: ${doc} not found, skipping`); + } + } + shell.echo(); + + shell.echo('Copying guide documentation...'); + const guides = ['src/contributing.md', 'src/events.md', 'src/theming.md']; + for (const guide of guides) { + if (fs.existsSync(guide)) { + const basename = path.basename(guide); + shell.cp(guide, path.join(GUIDES_DIR, basename)); + shell.echo(` ✓ Copied ${guide} → guides/${basename}`); + } else { + shell.echo(` ⚠ Warning: ${guide} not found, skipping`); + } + } + shell.echo(); + + shell.echo('Copying design guideline markdown files...'); + const guidelineSources = shell + .find('src/design-guidelines') + .filter((file) => { + return ( + file.endsWith('.md') && + !file.includes('/examples/') && + file !== 'src/design-guidelines' + ); + }); + + for (const guideline of guidelineSources) { + // Preserve directory structure: src/design-guidelines/foo/bar.md → + // www/markdown-docs/design-guidelines/foo/bar.md + const relativePath = guideline.replace('src/', ''); + const targetPath = path.join(OUTPUT_DIR, relativePath); + const targetDir = path.dirname(targetPath); + + shell.mkdir('-p', targetDir); + shell.cp(guideline, targetPath); + shell.echo(` ✓ Copied ${guideline} → ${relativePath}`); + } + shell.echo(); + + shell.echo('Generating INDEX.md...'); + const indexContent = generateIndexFile(); + fs.writeFileSync(path.join(OUTPUT_DIR, 'INDEX.md'), indexContent); + shell.echo('✓ INDEX.md created\n'); + + shell.echo('Generating META.json...'); + const metadata = generateMetadata(); + fs.writeFileSync( + path.join(OUTPUT_DIR, 'META.json'), + JSON.stringify(metadata, null, 2) + ); + shell.echo('✓ META.json created\n'); + + shell.echo('=== Documentation generation complete! ==='); + shell.echo(`Output directory: ${OUTPUT_DIR}`); + shell.echo(`Total components: ${metadata.componentCount}`); + shell.echo(`Total files: ${metadata.fileCount}`); +} catch (error) { + shell.echo('ERROR: Documentation generation failed'); + shell.echo(error.message); + shell.exit(1); +} + +/** + * Generate the INDEX.md file with navigation and overview + * @returns The content of the INDEX.md file + */ +function generateIndexFile() { + const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); + const version = packageJson.version; + + // Get list of components + const componentsDir = path.join(OUTPUT_DIR, 'components'); + const components = fs + .readdirSync(componentsDir) + .filter((item) => { + const stat = fs.statSync(path.join(componentsDir, item)); + + return stat.isDirectory(); + }) + .sort(); + + // Get list of design guidelines + const guidelinesDir = path.join(OUTPUT_DIR, 'design-guidelines'); + const guidelines = fs.existsSync(guidelinesDir) + ? fs + .readdirSync(guidelinesDir) + .filter((item) => { + const stat = fs.statSync(path.join(guidelinesDir, item)); + + return stat.isDirectory(); + }) + .sort() + : []; + + let content = `# Lime Elements Documentation + +**Version ${version}** + +A comprehensive design system and component library built with Stencil. + +## Quick Start + +\`\`\`bash +npm install @limetech/lime-elements +\`\`\` + +\`\`\`html + + + +\`\`\` + +## Components (${components.length}) + +`; + + // Add components in columns + const columns = 3; + for (let i = 0; i < components.length; i += columns) { + const row = components.slice(i, i + columns); + content += + '| ' + + row + .map( + (c) => + `[${formatComponentName(c)}](components/${c}/readme.md)` + ) + .join(' | ') + + ' |\n'; + if (i === 0) { + content += '| ' + '--- | '.repeat(row.length) + '\n'; + } + } + + content += ` + +## Design Guidelines + +`; + + for (const guideline of guidelines) { + // Try to find the main markdown file in the guideline directory + const guidelineDir = path.join(guidelinesDir, guideline); + const mdFiles = fs + .readdirSync(guidelineDir) + .filter((f) => f.endsWith('.md') && f !== 'readme.md'); + + if (mdFiles.length > 0) { + content += `- [${formatComponentName(guideline)}](design-guidelines/${guideline}/${mdFiles[0]})\n`; + } else { + content += `- [${formatComponentName(guideline)}](design-guidelines/${guideline}/)\n`; + } + } + + content += ` + +## Guides + +- [Contributing](guides/contributing.md) - How to contribute to Lime Elements +- [Events](guides/events.md) - Working with component events +- [Theming](guides/theming.md) - Customizing component styles + +## Resources + +- [Full README](README.md) +- [Contributing Guidelines](CONTRIBUTING.md) +- [NPM Package](https://www.npmjs.com/package/@limetech/lime-elements) +- [GitHub Repository](https://github.com/Lundalogik/lime-elements) +- [Official Documentation](https://lundalogik.github.io/lime-elements/) + +## About + +Lime Elements is an enterprise-ready component library that provides: +- 🚀 Battle-tested components used in production +- 🎨 Comprehensive design system +- ⚡ Web standards-based (works with any framework) +- 👾 Full TypeScript support +- ♿ Accessibility built-in +- ⚙️ Extensive customization options + +Built with ❤️ by [Lime Technologies](https://www.lime-technologies.com/) +`; + + return content; +} + +/** + * Format component name for display (kebab-case to Title Case) + * @param name - The kebab-case component name + * @returns The formatted component name in Title Case + */ +function formatComponentName(name) { + return name + .split('-') + .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) + .join(' '); +} + +/** + * Generate metadata about the documentation + * @returns Metadata object containing version, timestamp, and counts + */ +function generateMetadata() { + const packageJson = JSON.parse(fs.readFileSync('package.json', 'utf8')); + + // Count components + const componentsDir = path.join(OUTPUT_DIR, 'components'); + const components = fs.readdirSync(componentsDir).filter((item) => { + const stat = fs.statSync(path.join(componentsDir, item)); + + return stat.isDirectory(); + }); + + // Count all markdown files + let fileCount = 0; + const countFiles = (dir) => { + const items = fs.readdirSync(dir); + for (const item of items) { + const fullPath = path.join(dir, item); + const stat = fs.statSync(fullPath); + if (stat.isDirectory()) { + countFiles(fullPath); + } else if (item.endsWith('.md')) { + fileCount++; + } + } + }; + countFiles(OUTPUT_DIR); + + return { + version: packageJson.version, + generated: new Date().toISOString(), + componentCount: components.length, + fileCount: fileCount, + generator: 'generate-context7-docs.cjs', + }; +} diff --git a/package.json b/package.json index 92c060d7f7..9d27f412ae 100644 --- a/package.json +++ b/package.json @@ -23,6 +23,7 @@ "watch": "cross-env-shell SASS_PATH=node_modules \"stencil build --dev --watch --docs --serve\"", "watch:prod": "shx rm -rf www/ && cross-env-shell SASS_PATH=node_modules \"stencil build --watch\"", "docs:build": "cross-env-shell NODE_ENV=prod SASS_PATH=node_modules \"stencil build --docs --config stencil.config.docs.ts\"", + "docs:context7": "node generate-context7-docs.cjs", "docs:publish": "node publish-docs.cjs", "lint": "eslint --max-warnings=0 && prettier -c --ignore-path .gitignore **/*.scss", "lint:fix": "eslint --max-warnings=0 --fix && prettier --write --ignore-path .gitignore **/*.scss", diff --git a/publish-docs.cjs b/publish-docs.cjs index ae8a417874..d9d6fd84ac 100644 --- a/publish-docs.cjs +++ b/publish-docs.cjs @@ -190,6 +190,12 @@ function build() { shell.exit(1); } + if (shell.exec('npm run docs:context7').code !== 0) { + shell.echo('docs:context7 generation failed!'); + teardown(); + shell.exit(1); + } + shell.exec('ls -la www'); } @@ -231,6 +237,16 @@ function copyBuildOutput() { shell.exit(1); } + shell.echo('Copying markdown documentation...'); + if ( + shell.cp('-R', 'www/markdown-docs', `docsDist/versions/${version}/`) + .code !== 0 + ) { + shell.echo('copying markdown docs failed!'); + teardown(); + shell.exit(1); + } + shell.echo( `Copying docs-index.html and docs-index.css to 'docsDist/versions/${version}/'` ); diff --git a/stencil.config.context7.ts b/stencil.config.context7.ts new file mode 100644 index 0000000000..6c4290d6bf --- /dev/null +++ b/stencil.config.context7.ts @@ -0,0 +1,25 @@ +import { Config } from '@stencil/core'; +import { sass } from '@stencil/sass'; +import { nodeResolve } from '@rollup/plugin-node-resolve'; + +/** + * Stencil configuration for generating Context7-compatible markdown documentation. + * This config generates component README files that will be published to gh-pages + * and indexed by Context7 for LLM consumption. + */ +export const config: Config = { + namespace: 'lime-elements', + outputTargets: [ + { + type: 'docs-readme', + dir: 'www/markdown-docs', + strict: true, + }, + ], + plugins: [sass()], + rollupPlugins: { + before: [nodeResolve()], + }, + tsconfig: './tsconfig.docs.json', + globalStyle: 'src/global/core-styles.scss', +};