diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..1b7ceb5 --- /dev/null +++ b/.env.example @@ -0,0 +1,18 @@ +# Development Environment Variables +# Copy this file to .env.local and update values as needed + +# Next.js Configuration +NEXT_TELEMETRY_DISABLED=1 + +# GitHub Configuration (for development) +# GITHUB_TOKEN=your_github_token_here + +# API URLs (for future integrations) +# API_BASE_URL=http://localhost:3000/api + +# Feature Flags (for future use) +# ENABLE_ANALYTICS=false +# ENABLE_ERROR_REPORTING=false + +# Database Configuration (for future use) +# DATABASE_URL=postgresql://username:password@localhost:5432/codestorm_hub \ No newline at end of file diff --git a/.github/workflows/copilot-validation.yml b/.github/workflows/copilot-validation.yml new file mode 100644 index 0000000..57efd3a --- /dev/null +++ b/.github/workflows/copilot-validation.yml @@ -0,0 +1,158 @@ +name: Copilot Configuration Validation + +on: + push: + branches: [ "main", "develop" ] + paths: + - '.github/copilot-instructions.md' + - '.copilot/**' + - '.github/copilot-mcp.json' + pull_request: + branches: [ "main" ] + paths: + - '.github/copilot-instructions.md' + - '.copilot/**' + - '.github/copilot-mcp.json' + +jobs: + validate-copilot-config: + runs-on: ubuntu-latest + name: Validate Copilot Configuration + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: 'npm' + + - name: Install dependencies + run: npm ci + + - name: Validate configuration files exist + run: | + echo "Checking for required Copilot configuration files..." + + # Check for repository instructions + if [ ! -f ".github/copilot-instructions.md" ]; then + echo "❌ Missing .github/copilot-instructions.md" + exit 1 + else + echo "✅ Found .github/copilot-instructions.md" + fi + + # Check for custom instructions + if [ ! -f ".copilot/instructions.md" ]; then + echo "❌ Missing .copilot/instructions.md" + exit 1 + else + echo "✅ Found .copilot/instructions.md" + fi + + # Check for development environment config + if [ ! -f ".copilot/dev-environment.yml" ]; then + echo "❌ Missing .copilot/dev-environment.yml" + exit 1 + else + echo "✅ Found .copilot/dev-environment.yml" + fi + + # Check for MCP configuration + if [ ! -f ".github/copilot-mcp.json" ]; then + echo "❌ Missing .github/copilot-mcp.json" + exit 1 + else + echo "✅ Found .github/copilot-mcp.json" + fi + + - name: Validate JSON configuration files + run: | + echo "Validating JSON configuration files..." + + # Validate MCP configuration JSON + if ! jq . .github/copilot-mcp.json > /dev/null; then + echo "❌ Invalid JSON in .github/copilot-mcp.json" + exit 1 + else + echo "✅ Valid JSON in .github/copilot-mcp.json" + fi + + - name: Validate YAML configuration files + run: | + echo "Validating YAML configuration files..." + + # Install yq for YAML validation (Python version via pip) + sudo apt-get update + sudo apt-get install -y python3-pip + pip3 install --user yq + export PATH="$HOME/.local/bin:$PATH" + + # Validate development environment YAML + if ! yq eval . .copilot/dev-environment.yml > /dev/null; then + echo "❌ Invalid YAML in .copilot/dev-environment.yml" + exit 1 + else + echo "✅ Valid YAML in .copilot/dev-environment.yml" + fi + + - name: Validate project still builds + run: | + echo "Ensuring project builds successfully with current configuration..." + npm run build + + - name: Validate linting passes + run: | + echo "Ensuring linting passes with current configuration..." + npm run lint + + - name: Check configuration completeness + run: | + echo "Checking configuration completeness..." + + # Check if copilot-instructions.md contains required sections + if ! grep -q "## Project Overview" .github/copilot-instructions.md; then + echo "❌ Missing Project Overview section in copilot-instructions.md" + exit 1 + fi + + if ! grep -q "Technology Stack" .github/copilot-instructions.md; then + echo "❌ Missing Technology Stack section in copilot-instructions.md" + exit 1 + fi + + if ! grep -q "Development Guidelines" .github/copilot-instructions.md; then + echo "❌ Missing Development Guidelines section in copilot-instructions.md" + exit 1 + fi + + # Check if custom instructions contain key principles + if ! grep -q "Design Engineering Principles" .copilot/instructions.md; then + echo "❌ Missing Design Engineering Principles in .copilot/instructions.md" + exit 1 + fi + + if ! grep -q "Technology Preferences" .copilot/instructions.md; then + echo "❌ Missing Technology Preferences in .copilot/instructions.md" + exit 1 + fi + + # Check if MCP config has required structure + if ! jq -e '.mcp.servers' .github/copilot-mcp.json > /dev/null; then + echo "❌ Missing MCP servers configuration" + exit 1 + fi + + if ! jq -e '.context' .github/copilot-mcp.json > /dev/null; then + echo "❌ Missing context configuration in MCP" + exit 1 + fi + + echo "✅ All configuration completeness checks passed" + + - name: Report validation success + run: | + echo "🎉 All Copilot configuration validation checks passed!" + echo "The repository is properly configured for GitHub Copilot coding agent." \ No newline at end of file diff --git a/README.md b/README.md index 2c0776f..0815047 100644 --- a/README.md +++ b/README.md @@ -48,11 +48,34 @@ This project implements a comprehensive design system based on: ## Development -- `npm run dev` - Start development server -- `npm run build` - Build for production -- `npm run build:github-pages` - Build for GitHub Pages deployment +### Environment Setup + +1. Copy the environment example file: +```bash +cp .env.example .env.local +``` + +2. Install dependencies: +```bash +npm install +``` + +### Available Scripts + +- `npm run dev` - Start development server with Turbopack +- `npm run build` - Build for production (GitHub Pages optimized) - `npm run start` - Start production server -- `npm run lint` - Run ESLint +- `npm run lint` - Run ESLint with auto-fix +- `npm run type-check` - Run TypeScript type checking +- `npm run dev:clean` - Clean build cache and start dev server + +### Code Quality + +This project enforces strict code quality standards: +- TypeScript strict mode +- ESLint with Next.js recommended rules +- Accessibility compliance (WCAG 2.1 AA) +- Performance optimization (Core Web Vitals) ## Deployment diff --git a/next.config.ts b/next.config.ts index ef203e3..21fe843 100644 --- a/next.config.ts +++ b/next.config.ts @@ -16,8 +16,38 @@ const nextConfig: NextConfig = { hostname: "github.com", pathname: "/CodeStorm-Hub/**", }, + { + protocol: "https", + hostname: "images.unsplash.com", + pathname: "/**", + }, + { + protocol: "https", + hostname: "avatars.githubusercontent.com", + pathname: "/**", + }, + { + protocol: "https", + hostname: "ui-avatars.com", + pathname: "/**", + }, ], }, + + // Performance optimizations + poweredByHeader: false, + + // TypeScript configuration + typescript: { + // Type checking is handled by the CI pipeline + ignoreBuildErrors: false, + }, + + // ESLint configuration + eslint: { + // Linting is handled by the CI pipeline + ignoreDuringBuilds: false, + }, }; export default nextConfig; diff --git a/package-lock.json b/package-lock.json index 142f301..91ebeb1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2031,9 +2031,9 @@ "license": "MIT" }, "node_modules/@types/node": { - "version": "20.19.17", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.17.tgz", - "integrity": "sha512-gfehUI8N1z92kygssiuWvLiwcbOB3IRktR6hTDgJlXMYh5OvkPSRmgfoBUmfZt+vhwJtX7v1Yw4KvvAf7c5QKQ==", + "version": "20.19.18", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.18.tgz", + "integrity": "sha512-KeYVbfnbsBCyKG8e3gmUqAfyZNcoj/qpEbHRkQkfZdKOBrU7QQ+BsTdfqLSWX9/m1ytYreMhpKvp+EZi3UFYAg==", "dev": true, "license": "MIT", "dependencies": { @@ -2041,9 +2041,9 @@ } }, "node_modules/@types/react": { - "version": "19.1.14", - "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.14.tgz", - "integrity": "sha512-ukd93VGzaNPMAUPy0gRDSC57UuQbnH9Kussp7HBjM06YFi9uZTFhOvMSO2OKqXm1rSgzOE+pVx1k1PYHGwlc8Q==", + "version": "19.1.15", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.1.15.tgz", + "integrity": "sha512-+kLxJpaJzXybyDyFXYADyP1cznTO8HSuBpenGlnKOAkH4hyNINiywvXS/tGJhsrGGP/gM185RA3xpjY0Yg4erA==", "devOptional": true, "license": "MIT", "dependencies": { @@ -2061,17 +2061,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.44.1.tgz", - "integrity": "sha512-molgphGqOBT7t4YKCSkbasmu1tb1MgrZ2szGzHbclF7PNmOkSTQVHy+2jXOSnxvR3+Xe1yySHFZoqMpz3TfQsw==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.45.0.tgz", + "integrity": "sha512-HC3y9CVuevvWCl/oyZuI47dOeDF9ztdMEfMH8/DW/Mhwa9cCLnK1oD7JoTVGW/u7kFzNZUKUoyJEqkaJh5y3Wg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/type-utils": "8.44.1", - "@typescript-eslint/utils": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/type-utils": "8.45.0", + "@typescript-eslint/utils": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", "graphemer": "^1.4.0", "ignore": "^7.0.0", "natural-compare": "^1.4.0", @@ -2085,7 +2085,7 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^8.44.1", + "@typescript-eslint/parser": "^8.45.0", "eslint": "^8.57.0 || ^9.0.0", "typescript": ">=4.8.4 <6.0.0" } @@ -2101,16 +2101,16 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.44.1.tgz", - "integrity": "sha512-EHrrEsyhOhxYt8MTg4zTF+DJMuNBzWwgvvOYNj/zm1vnaD/IC5zCXFehZv94Piqa2cRFfXrTFxIvO95L7Qc/cw==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.45.0.tgz", + "integrity": "sha512-TGf22kon8KW+DeKaUmOibKWktRY8b2NSAZNdtWh798COm1NWx8+xJ6iFBtk3IvLdv6+LGLJLRlyhrhEDZWargQ==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4" }, "engines": { @@ -2126,14 +2126,14 @@ } }, "node_modules/@typescript-eslint/project-service": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.44.1.tgz", - "integrity": "sha512-ycSa60eGg8GWAkVsKV4E6Nz33h+HjTXbsDT4FILyL8Obk5/mx4tbvCNsLf9zret3ipSumAOG89UcCs/KRaKYrA==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.45.0.tgz", + "integrity": "sha512-3pcVHwMG/iA8afdGLMuTibGR7pDsn9RjDev6CCB+naRsSYs2pns5QbinF4Xqw6YC/Sj3lMrm/Im0eMfaa61WUg==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.44.1", - "@typescript-eslint/types": "^8.44.1", + "@typescript-eslint/tsconfig-utils": "^8.45.0", + "@typescript-eslint/types": "^8.45.0", "debug": "^4.3.4" }, "engines": { @@ -2148,14 +2148,14 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.44.1.tgz", - "integrity": "sha512-NdhWHgmynpSvyhchGLXh+w12OMT308Gm25JoRIyTZqEbApiBiQHD/8xgb6LqCWCFcxFtWwaVdFsLPQI3jvhywg==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.45.0.tgz", + "integrity": "sha512-clmm8XSNj/1dGvJeO6VGH7EUSeA0FMs+5au/u3lrA3KfG8iJ4u8ym9/j2tTEoacAffdW1TVUzXO30W1JTJS7dA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1" + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2166,9 +2166,9 @@ } }, "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.44.1.tgz", - "integrity": "sha512-B5OyACouEjuIvof3o86lRMvyDsFwZm+4fBOqFHccIctYgBjqR3qT39FBYGN87khcgf0ExpdCBeGKpKRhSFTjKQ==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.45.0.tgz", + "integrity": "sha512-aFdr+c37sc+jqNMGhH+ajxPXwjv9UtFZk79k8pLoJ6p4y0snmYpPA52GuWHgt2ZF4gRRW6odsEj41uZLojDt5w==", "dev": true, "license": "MIT", "engines": { @@ -2183,15 +2183,15 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.44.1.tgz", - "integrity": "sha512-KdEerZqHWXsRNKjF9NYswNISnFzXfXNDfPxoTh7tqohU/PRIbwTmsjGK6V9/RTYWau7NZvfo52lgVk+sJh0K3g==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.45.0.tgz", + "integrity": "sha512-bpjepLlHceKgyMEPglAeULX1vixJDgaKocp0RVJ5u4wLJIMNuKtUXIczpJCPcn2waII0yuvks/5m5/h3ZQKs0A==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1", - "@typescript-eslint/utils": "8.44.1", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0", + "@typescript-eslint/utils": "8.45.0", "debug": "^4.3.4", "ts-api-utils": "^2.1.0" }, @@ -2208,9 +2208,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.44.1.tgz", - "integrity": "sha512-Lk7uj7y9uQUOEguiDIDLYLJOrYHQa7oBiURYVFqIpGxclAFQ78f6VUOM8lI2XEuNOKNB7XuvM2+2cMXAoq4ALQ==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.45.0.tgz", + "integrity": "sha512-WugXLuOIq67BMgQInIxxnsSyRLFxdkJEJu8r4ngLR56q/4Q5LrbfkFRH27vMTjxEK8Pyz7QfzuZe/G15qQnVRA==", "dev": true, "license": "MIT", "engines": { @@ -2222,16 +2222,16 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.44.1.tgz", - "integrity": "sha512-qnQJ+mVa7szevdEyvfItbO5Vo+GfZ4/GZWWDRRLjrxYPkhM+6zYB2vRYwCsoJLzqFCdZT4mEqyJoyzkunsZ96A==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.45.0.tgz", + "integrity": "sha512-GfE1NfVbLam6XQ0LcERKwdTTPlLvHvXXhOeUGC1OXi4eQBoyy1iVsW+uzJ/J9jtCz6/7GCQ9MtrQ0fml/jWCnA==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/project-service": "8.44.1", - "@typescript-eslint/tsconfig-utils": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/visitor-keys": "8.44.1", + "@typescript-eslint/project-service": "8.45.0", + "@typescript-eslint/tsconfig-utils": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/visitor-keys": "8.45.0", "debug": "^4.3.4", "fast-glob": "^3.3.2", "is-glob": "^4.0.3", @@ -2307,16 +2307,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.44.1.tgz", - "integrity": "sha512-DpX5Fp6edTlocMCwA+mHY8Mra+pPjRZ0TfHkXI8QFelIKcbADQz1LUPNtzOFUriBB2UYqw4Pi9+xV4w9ZczHFg==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.45.0.tgz", + "integrity": "sha512-bxi1ht+tLYg4+XV2knz/F7RVhU0k6VrSMc9sb8DQ6fyCTrGQLHfo7lDtN0QJjZjKkLA2ThrKuCdHEvLReqtIGg==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.44.1", - "@typescript-eslint/types": "8.44.1", - "@typescript-eslint/typescript-estree": "8.44.1" + "@typescript-eslint/scope-manager": "8.45.0", + "@typescript-eslint/types": "8.45.0", + "@typescript-eslint/typescript-estree": "8.45.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2331,13 +2331,13 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.44.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.44.1.tgz", - "integrity": "sha512-576+u0QD+Jp3tZzvfRfxon0EA2lzcDt3lhUbsC6Lgzy9x2VR4E+JUiNyGHi5T8vk0TV+fpJ5GLG1JsJuWCaKhw==", + "version": "8.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.45.0.tgz", + "integrity": "sha512-qsaFBA3e09MIDAGFUrTk+dzqtfv1XPVz8t8d1f0ybTzrCY7BKiMC5cjrl1O/P7UmHsNyW90EYSkU/ZWpmXelag==", "dev": true, "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.44.1", + "@typescript-eslint/types": "8.45.0", "eslint-visitor-keys": "^4.2.1" }, "engines": { @@ -2879,6 +2879,16 @@ "node": ">= 0.4" } }, + "node_modules/async-generator-function": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/async-generator-function/-/async-generator-function-1.0.0.tgz", + "integrity": "sha512-+NAXNqgCrB95ya4Sr66i1CL2hqLVckAk7xwRYWdcm39/ELQ6YNn1aw5r0bdQtqNZgQpEWzc5yc/igXc7aL5SLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/available-typed-arrays": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz", @@ -4114,18 +4124,31 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/generator-function": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/generator-function/-/generator-function-2.0.0.tgz", + "integrity": "sha512-xPypGGincdfyl/AiSGa7GjXLkvld9V7GjZlowup9SHIJnQnHLFiLODCd/DqKOp0PBagbHJ68r1KJI9Mut7m4sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.1.tgz", + "integrity": "sha512-fk1ZVEeOX9hVZ6QzoBNEC55+Ucqg4sTVwrVuigZhuRPESVFpMyXnd3sbXvPOwp7Y9riVyANiqhEuRF0G1aVSeQ==", "dev": true, "license": "MIT", "dependencies": { + "async-function": "^1.0.0", + "async-generator-function": "^1.0.0", "call-bind-apply-helpers": "^1.0.2", "es-define-property": "^1.0.1", "es-errors": "^1.3.0", "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", + "generator-function": "^2.0.0", "get-proto": "^1.0.1", "gopd": "^1.2.0", "has-symbols": "^1.1.0", @@ -4581,13 +4604,14 @@ } }, "node_modules/is-generator-function": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.0.tgz", - "integrity": "sha512-nPUB5km40q9e8UfN/Zc24eLlzdSf9OfKByBw9CIdw4H1giPMeA0OIJvbchsCu4npfI2QcMVBsGEBHKZ7wLTWmQ==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.1.1.tgz", + "integrity": "sha512-Gn8BWUdrTzf9XUJAvqIYP7QnSC3mKs8QjQdGdJ7HmBemzZo14wj/OVmmAwgxDX/7WhFEjboybL4VhXGIQYPlOA==", "dev": true, "license": "MIT", "dependencies": { "call-bound": "^1.0.3", + "generator-function": "^2.0.0", "get-proto": "^1.0.0", "has-tostringtag": "^1.0.2", "safe-regex-test": "^1.1.0" diff --git a/package.json b/package.json index 86789ce..5d6b802 100644 --- a/package.json +++ b/package.json @@ -3,10 +3,12 @@ "version": "0.1.0", "private": true, "scripts": { - "dev": "next dev --turbopack", - "build": "next build --turbopack", + "dev": "NEXT_TELEMETRY_DISABLED=1 next dev --turbopack", + "build": "NEXT_TELEMETRY_DISABLED=1 next build --turbopack", "start": "next start", - "lint": "eslint" + "lint": "eslint . --ext .ts,.tsx,.js,.jsx --fix", + "type-check": "tsc --noEmit", + "dev:clean": "rm -rf .next && npm run dev" }, "dependencies": { "@radix-ui/colors": "^3.0.0", diff --git a/src/components/ui/avatar.tsx b/src/components/ui/avatar.tsx new file mode 100644 index 0000000..c7b9b48 --- /dev/null +++ b/src/components/ui/avatar.tsx @@ -0,0 +1,56 @@ +import * as React from "react" +import Image from "next/image" +import { cn } from "@/lib/utils" +import { getImageWithFallback } from "@/lib/image-utils" + +export interface AvatarProps extends React.HTMLAttributes { + src: string + alt: string + size?: number + fallback?: string +} + +const Avatar = React.forwardRef( + ({ className, src, alt, size = 40, fallback, ...props }, ref) => { + const [imageSrc, setImageSrc] = React.useState(src) + const { src: optimizedSrc, fallbackSrc } = getImageWithFallback(src, alt) + + const handleError = React.useCallback(() => { + if (fallback) { + setImageSrc(fallback) + } else if (fallbackSrc && imageSrc !== fallbackSrc) { + setImageSrc(fallbackSrc) + } + }, [fallback, fallbackSrc, imageSrc]) + + React.useEffect(() => { + setImageSrc(optimizedSrc) + }, [optimizedSrc]) + + return ( +
+ {alt} +
+ ) + } +) + +Avatar.displayName = "Avatar" + +export { Avatar } \ No newline at end of file diff --git a/src/lib/image-utils.ts b/src/lib/image-utils.ts new file mode 100644 index 0000000..7168754 --- /dev/null +++ b/src/lib/image-utils.ts @@ -0,0 +1,73 @@ +/** + * Image utilities for handling external images and fallbacks + */ + +export interface ImageFallbackProps { + src: string + alt: string + fallbackSrc?: string +} + +/** + * Generate a fallback avatar using GitHub's avatar API or a placeholder + */ +export function generateFallbackAvatar(name: string): string { + // Create a simple avatar using the first letter of the name + const initials = name + .split(' ') + .map(word => word[0]) + .join('') + .toUpperCase() + .slice(0, 2) + + // Return a GitHub-style avatar URL or placeholder + return `https://ui-avatars.com/api/?name=${encodeURIComponent(initials)}&background=171717&color=fff&size=200` +} + +/** + * Validate image URL and provide fallbacks + */ +export function getImageWithFallback(src: string, alt: string): ImageFallbackProps { + // If it's already a fallback avatar, return as is + if (src.includes('ui-avatars.com') || src.includes('github.com')) { + return { src, alt } + } + + // For Unsplash images, provide a fallback + const fallbackSrc = generateFallbackAvatar(alt.replace(' profile picture', '').replace(' avatar', '')) + + return { + src, + alt, + fallbackSrc + } +} + +/** + * Handle image load errors by updating src to fallback + */ +export function handleImageError(event: React.SyntheticEvent, fallbackSrc?: string) { + const img = event.target as HTMLImageElement + if (fallbackSrc && img.src !== fallbackSrc) { + img.src = fallbackSrc + } +} + +/** + * Optimize external image URLs for better loading + */ +export function optimizeImageUrl(src: string, width: number, height: number): string { + // For Unsplash images, add optimization parameters + if (src.includes('unsplash.com')) { + const url = new URL(src) + url.searchParams.set('w', width.toString()) + url.searchParams.set('h', height.toString()) + url.searchParams.set('fit', 'crop') + url.searchParams.set('crop', 'face') + url.searchParams.set('auto', 'format') + url.searchParams.set('q', '80') + return url.toString() + } + + return src +} \ No newline at end of file diff --git a/tailwind.config.ts b/tailwind.config.ts new file mode 100644 index 0000000..6248d0c --- /dev/null +++ b/tailwind.config.ts @@ -0,0 +1,68 @@ +import type { Config } from "tailwindcss"; + +const config: Config = { + content: [ + "./src/pages/**/*.{js,ts,jsx,tsx,mdx}", + "./src/components/**/*.{js,ts,jsx,tsx,mdx}", + "./src/app/**/*.{js,ts,jsx,tsx,mdx}", + ], + theme: { + extend: { + colors: { + background: "var(--background)", + foreground: "var(--foreground)", + card: { + DEFAULT: "var(--card)", + foreground: "var(--card-foreground)", + }, + popover: { + DEFAULT: "var(--popover)", + foreground: "var(--popover-foreground)", + }, + primary: { + DEFAULT: "var(--primary)", + foreground: "var(--primary-foreground)", + }, + secondary: { + DEFAULT: "var(--secondary)", + foreground: "var(--secondary-foreground)", + }, + muted: { + DEFAULT: "var(--muted)", + foreground: "var(--muted-foreground)", + }, + accent: { + DEFAULT: "var(--accent)", + foreground: "var(--accent-foreground)", + }, + destructive: { + DEFAULT: "var(--destructive)", + foreground: "var(--destructive-foreground)", + }, + success: { + DEFAULT: "var(--success)", + foreground: "var(--success-foreground)", + }, + warning: { + DEFAULT: "var(--warning)", + foreground: "var(--warning-foreground)", + }, + border: "var(--border)", + input: "var(--input)", + ring: "var(--ring)", + }, + borderRadius: { + lg: "var(--radius)", + md: "calc(var(--radius) - 2px)", + sm: "calc(var(--radius) - 4px)", + }, + fontFamily: { + sans: ["var(--font-geist-sans)", "ui-sans-serif", "system-ui"], + mono: ["var(--font-geist-mono)", "ui-monospace", "Monaco"], + }, + }, + }, + plugins: [], +}; + +export default config; \ No newline at end of file