[
  {
    "path": ".gitattributes",
    "content": "*.md linguist-language=CSS\n*.png linguist-language=CSS\n"
  },
  {
    "path": "MCP/.gitignore",
    "content": "# Dependencies\nnode_modules/\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# Environment variables\n.env\n.env.local\n.env.development.local\n.env.test.local\n.env.production.local\n\n# Database (keep for npm package)\n# data/\n*.db\n*.sqlite\n\n# Logs\nlogs\n*.log\n\n# Runtime data\npids\n*.pid\n*.seed\n*.pid.lock\n\n# Coverage directory used by tools like istanbul\ncoverage/\n*.lcov\n\n# Dependency directories\nnode_modules/\njspm_packages/\n\n# Optional npm cache directory\n.npm\n\n# Optional eslint cache\n.eslintcache\n\n# Microbundle cache\n.rpt2_cache/\n.rts2_cache_cjs/\n.rts2_cache_es/\n.rts2_cache_umd/\n\n# Optional REPL history\n.node_repl_history\n\n# Output of 'npm pack'\n*.tgz\n\n# Yarn Integrity file\n.yarn-integrity\n\n# dotenv environment variables file\n.env\n\n# parcel-bundler cache (https://parceljs.org/)\n.cache\n.parcel-cache\n\n# next.js build output\n.next\n\n# nuxt.js build output\n.nuxt\n\n# vuepress build output\n.vuepress/dist\n\n# Serverless directories\n.serverless/\n\n# FuseBox cache\n.fusebox/\n\n# DynamoDB Local files\n.dynamodb/\n\n# TernJS port file\n.tern-port\n\n# OS generated files\n.DS_Store\n.DS_Store?\n._*\n.Spotlight-V100\n.Trashes\nehthumbs.db\nThumbs.db \n\n# Ignore Cursor IDE local config\n.cursor/ .env\n"
  },
  {
    "path": "MCP/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2024 iCSS MCP Server\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE. "
  },
  {
    "path": "MCP/README.en.md",
    "content": "# iCSS MCP Server\n\n> 🎨 A comprehensive Model Context Protocol (MCP) server that integrates both [iCSS repository](https://github.com/chokcoco/iCSS) techniques and [CSS-Inspiration](https://github.com/chokcoco/CSS-Inspiration) demos, providing complete CSS solutions with runnable code examples.\n\n[![npm version](https://badge.fury.io/js/icss-mcp-server.svg)](https://www.npmjs.com/package/icss-mcp-server)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## 🚀 Quick Start\n\n### Option 1: NPM Installation (Recommended)\n\n```bash\n# Install globally\nnpm install -g icss-mcp-server\n\n# Auto-configure for Cursor\nicss-mcp-install\n\n# Start the server (if needed manually)\nicss-mcp\n```\n\n### Option 2: NPX (No Installation)\n\n```bash\n# Run directly\nnpx icss-mcp-server\n\n# Auto-configure for Cursor\nnpx icss-mcp-server install\n```\n\n### Option 3: Local Development\n\n```bash\n# Clone and setup\ngit clone https://github.com/chokcoco/iCSS.git\ncd iCSS/MCP\nnpm install\nnpm run setup\nnpm start\n```\n\n## 📋 Features\n\n- 🔍 **Dual Search**: Search both iCSS articles and CSS-Inspiration demos\n- 📖 **Article Details**: Access full content of specific iCSS articles\n- 🎯 **Demo Code**: Get complete runnable HTML/CSS code from CSS-Inspiration\n- 🏷️ **Smart Categories**: Browse by technology type and difficulty level\n- 🎲 **Random Discovery**: Get random techniques or demo examples\n- 🔧 **Code Snippets**: Extract and manage CSS/HTML code blocks\n- 🚀 **Easy Integration**: Auto-configures with Cursor IDE\n- 📊 **Performance Analysis**: Browser compatibility and performance insights\n\n## 🛠️ Installation & Configuration\n\n### Automatic Setup\n\nThe easiest way is to use the automatic installer:\n\n```bash\nnpm install -g icss-mcp-server\nicss-mcp-install\n```\n\nThis will:\n1. Install the package globally\n2. Auto-configure Cursor IDE settings\n3. Create necessary database files\n4. Verify the installation\n\n### Manual Configuration\n\nIf automatic setup doesn't work, you can manually configure Cursor:\n\n1. Create or edit `~/.config/cursor/mcp_settings.json`:\n\n```json\n{\n  \"mcpServers\": {\n    \"icss\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/global/node_modules/icss-mcp-server/server.js\"],\n      \"env\": {}\n    }\n  }\n}\n```\n\n2. Restart Cursor IDE completely\n\n## 💡 Usage in Cursor\n\nOnce installed and configured, you can ask Cursor about CSS techniques:\n\n### Example Queries\n\n#### iCSS Articles\n- *\"Show me CSS techniques for flex layout\"*\n- *\"Find articles about CSS animations from iCSS\"*\n- *\"How to create gradient borders with CSS?\"*\n- *\"Get details for issue #80\"*\n\n#### CSS-Inspiration Demos\n- *\"Search for 3D effect demonstrations\"*\n- *\"Find animation demos with complete code\"*\n- *\"Show border effect examples\"*\n- *\"Get complete code for demo ID #25\"*\n\n#### General Features\n- *\"Get a random CSS tip\"*\n- *\"What CSS technique categories are available?\"*\n- *\"Show all CSS-Inspiration categories\"*\n- *\"Random animation demo\"*\n\n### MCP Functions Available\n\n| Function | Description | Parameters |\n|----------|-------------|------------|\n| `search_css_techniques` | Search iCSS technique articles | `query`, `limit` (optional) |\n| `search_css_demos` | Search CSS-Inspiration demos | `query`, `category` (optional), `difficulty` (optional), `limit` (optional) |\n| `get_css_article` | Get full iCSS article content | `issue_number` |\n| `get_css_demo` | Get complete demo with code | `demo_id` |\n| `list_css_categories` | List all available categories | `source` (optional): icss/inspiration/all |\n| `get_random_css_tip` | Get random technique or demo | `source` (optional): icss/inspiration/both |\n\n## 🔧 CLI Commands\n\nAfter installation, these commands are available:\n\n```bash\n# Start MCP server\nicss-mcp\n\n# Install/configure for Cursor\nicss-mcp-install\n\n# Run setup (create database, fetch data)\nnpm run setup\n\n# Fetch iCSS article data\nnpm run build\n\n# Fetch CSS-Inspiration demo data\nnpm run build:inspiration\n\n# Fetch all data\nnpm run build:all\n\n# Test server functionality\nnpm test\n```\n\n## 📊 Data Source\n\n### iCSS Technique Library\n- **270+ high-quality CSS articles** covering animations, layouts, effects, and performance\n- Comprehensive technique explanations with detailed examples\n- Source: [iCSS repository](https://github.com/chokcoco/iCSS)\n\n### CSS-Inspiration Demo Library\n- **14 categories** of complete CSS demonstrations\n- Runnable HTML/CSS code with live examples\n- **Difficulty levels**: Beginner, Intermediate, Advanced\n- **Browser compatibility** information included\n- Source: [CSS-Inspiration repository](https://github.com/chokcoco/CSS-Inspiration)\n\n### Technical Features\n- **Fuzzy search** with intelligent matching\n- **Smart categorization** by technology and difficulty\n- **Automatic code extraction** and snippet management\n- **Performance analysis** and browser compatibility insights\n- **Regular updates** from both repositories\n\n## 🔍 Troubleshooting\n\n### Common Issues\n\n**1. MCP Server not found in Cursor**\n- Ensure Cursor is completely restarted after installation\n- Check the config file: `~/.config/cursor/mcp_settings.json`\n- Verify the server path in the configuration\n\n**2. Permission errors**\n- On macOS/Linux: `chmod +x node_modules/icss-mcp-server/bin/*`\n- Run with sudo if needed: `sudo npm install -g icss-mcp-server`\n\n**3. Database issues**\n- Run setup again: `npm run setup`\n- Check if SQLite3 is properly installed\n- Clear and rebuild: `rm -rf data/icss.db && npm run build`\n\n### Debug Mode\n\nEnable debug logging:\n\n```bash\n# Set debug environment\nexport DEBUG=icss-mcp:*\n\n# Run with debug info\nicss-mcp\n```\n\n### Manual Testing\n\nTest the server directly:\n\n```bash\n# Test server functionality\nnpm test\n\n# Test specific functions\nnode -e \"\nimport('./server.js').then(async () => {\n  // Server will start and show debug info\n});\n\"\n```\n\n## 🤝 Contributing\n\nContributions are welcome! Please feel free to submit a Pull Request.\n\n### Development Setup\n\n```bash\ngit clone https://github.com/chokcoco/iCSS.git\ncd iCSS/MCP\nnpm install\nnpm run setup\nnpm run dev\n```\n\n### Publishing\n\n```bash\nnpm run prepublishOnly\nnpm publish\n```\n\n## 📄 License\n\nMIT License - see the [LICENSE](LICENSE) file for details.\n\n## 🙏 Acknowledgments\n\n- [iCSS Repository](https://github.com/chokcoco/iCSS) - Original CSS techniques collection\n- [chokcoco](https://github.com/chokcoco) - Creator of iCSS\n- [Model Context Protocol](https://github.com/modelcontextprotocol) - MCP specification\n- [Cursor IDE](https://cursor.sh/) - AI-powered code editor\n\n## 📞 Support\n\n- 🐛 [Report Issues](https://github.com/chokcoco/iCSS/issues)\n- 💬 [Discussions](https://github.com/chokcoco/iCSS/discussions)\n- 📚 [iCSS Documentation](https://github.com/chokcoco/iCSS)\n\n---\n\nMade with ❤️ for the CSS community "
  },
  {
    "path": "MCP/README.md",
    "content": "# iCSS MCP Server 中文使用指南\n\n> iCSS MCP Server 是一个基于 Model Context Protocol (MCP) 的服务端，整合了 iCSS 技巧库和 CSS-Inspiration 演示案例，提供 CSS 技巧搜索、分类、文章详情、完整代码演示等能力，支持 Cursor IDE 智能调用。\n\n[![npm version](https://badge.fury.io/js/icss-mcp-server.svg?cacheBust=1)](https://www.npmjs.com/package/icss-mcp-server)\n[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)\n\n## 🚀 快速开始\n\n### 1. 全局安装（推荐）\n```bash\nnpm install -g icss-mcp-server\nicss-mcp-install\n# 启动 MCP 服务（如需手动）\nicss-mcp\n```\n\n### 2. NPX 免安装\n```bash\nnpx icss-mcp-server\nnpx icss-mcp-server install\n```\n\n### 3. 本地开发\n```bash\ngit clone https://github.com/chokcoco/iCSS.git\ncd iCSS/MCP\nnpm install\nnpm run setup\nnpm start\n```\n\n## 📋 主要功能\n- 🔍 **双库搜索**：同时搜索 iCSS 文章和 CSS-Inspiration 演示\n- 📖 **文章详情**：获取指定 iCSS 文章的完整内容\n- 🎯 **演示代码**：获取 CSS-Inspiration 的完整可运行代码\n- 🏷️ **智能分类**：按技术类别、难度级别浏览内容\n- 🎲 **随机发现**：随机获取技巧或演示案例\n- 🔧 **代码片段**：提取和管理 CSS/HTML 代码块\n- 🚀 **一键集成**：自动配置 Cursor IDE\n- 📊 **性能分析**：提供浏览器兼容性和性能建议\n\n## 🛠️ 安装与配置\n\n### 自动配置\n```bash\nnpm install -g icss-mcp-server\nicss-mcp-install\n```\n\n### 手动配置（如自动失败）\n编辑 `~/.config/cursor/mcp_settings.json`，添加：\n```json\n{\n  \"mcpServers\": {\n    \"icss\": {\n      \"command\": \"node\",\n      \"args\": [\"/path/to/global/node_modules/icss-mcp-server/server.js\"],\n      \"env\": {}\n    }\n  }\n}\n```\n重启 Cursor IDE。\n\n## 💡 Cursor 智能调用示例\n- “查找 flex 布局的 CSS 技巧”\n- “iCSS 有哪些动画相关的技巧？”\n- “如何实现渐变边框？”\n- “来一个随机 CSS 技巧”\n- “有哪些 CSS 技巧分类？”\n- “获取 issue #1 的详细内容”\n\n## 🧩 MCP 可用工具\n| 工具名 | 说明 | 参数 |\n|--------|------|------|\n| search_css_techniques | 搜索 iCSS 技巧文章 | query, limit(可选) |\n| search_css_demos | 搜索 CSS-Inspiration 演示 | query, category(可选), difficulty(可选), limit(可选) |\n| get_css_article | 获取 iCSS 文章详情 | issue_number |\n| get_css_demo | 获取演示完整代码 | demo_id |\n| list_css_categories | 获取所有分类 | source(可选): icss/inspiration/all |\n| get_random_css_tip | 随机技巧或演示 | source(可选): icss/inspiration/both |\n\n## 🔧 常用命令\n```bash\nicss-mcp                # 启动 MCP 服务\nicss-mcp-install        # 自动配置 Cursor\nnpm run setup           # 初始化数据库\nnpm run build           # 拉取 iCSS 文章数据\nnpm run build:inspiration # 拉取 CSS-Inspiration 演示数据\nnpm run build:all       # 拉取所有数据\nnpm test                # 测试服务\n```\n\n## 📊 数据来源\n\n### iCSS 技巧库\n- 超过 270 篇高质量 CSS 技巧文章\n- 涵盖动画、布局、特效、性能优化等主题\n- 原始仓库：[iCSS](https://github.com/chokcoco/iCSS)\n\n### CSS-Inspiration 演示库\n- 包含 14 个分类的完整 CSS 演示\n- 提供可运行的 HTML/CSS 代码\n- 按难度级别分类（初级/中级/高级）\n- 包含浏览器兼容性信息\n- 原始仓库：[CSS-Inspiration](https://github.com/chokcoco/CSS-Inspiration)\n\n### 技术特性\n- 支持模糊搜索、智能分类\n- 自动提取代码片段\n- 性能分析和优化建议\n- 定期同步最新内容\n\n## ❓ 常见问题\n1. **Cursor 未识别 MCP Server**\n   - 检查配置文件路径和 server.js 路径\n   - 完全重启 Cursor\n2. **数据库报错**\n   - 运行 `npm run setup` 重新初始化\n3. **权限问题**\n   - macOS/Linux 下 `chmod +x node_modules/icss-mcp-server/bin/*`\n\n## 📝 贡献与支持\n- 欢迎 PR 和 Issue\n- [iCSS 讨论区](https://github.com/chokcoco/iCSS/discussions)\n- [原文档/英文版](./README.en.md)\n\n---\n\nMade with ❤️ for the CSS community "
  },
  {
    "path": "MCP/bin/icss-mcp.js",
    "content": "#!/usr/bin/env node\n\nimport { fileURLToPath } from 'url';\nimport { dirname, join } from 'path';\nimport { spawn } from 'child_process';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\nconst serverPath = join(__dirname, '..', 'server.js');\n\nconsole.log('🚀 Starting iCSS MCP Server v1.1.1...');\nconsole.log('📚 Integrating iCSS techniques + CSS-Inspiration demos');\n\n// 启动 MCP 服务器\nconst server = spawn('node', [serverPath], {\n  stdio: 'inherit',\n  env: process.env\n});\n\nserver.on('error', (error) => {\n  console.error('❌ Failed to start iCSS MCP Server:', error);\n  console.error('💡 Try running: npm install -g icss-mcp-server');\n  process.exit(1);\n});\n\nserver.on('close', (code) => {\n  if (code !== 0) {\n    console.error(`❌ Server exited with code ${code}`);\n    console.error('💡 For help, visit: https://github.com/chokcoco/iCSS/tree/main/MCP');\n  }\n  process.exit(code);\n});\n\n// 处理进程信号\nprocess.on('SIGINT', () => {\n  console.log('\\n⏹️  Shutting down iCSS MCP Server...');\n  server.kill('SIGINT');\n});\n\nprocess.on('SIGTERM', () => {\n  console.log('\\n⏹️  Shutting down iCSS MCP Server...');\n  server.kill('SIGTERM');\n});\n\n// 显示帮助信息\nif (process.argv.includes('--help') || process.argv.includes('-h')) {\n  console.log(`\niCSS MCP Server v1.1.1 - CSS Techniques & Demos\n\nUsage:\n  icss-mcp              Start the MCP server\n  icss-mcp-install      Install for Cursor IDE\n\nFeatures:\n  • 270+ CSS technique articles from iCSS\n  • 160+ complete CSS demos from CSS-Inspiration  \n  • Smart search and categorization\n  • Complete runnable code examples\n\nFor more information:\n  https://github.com/chokcoco/iCSS/tree/main/MCP\n`);\n  process.exit(0);\n} "
  },
  {
    "path": "MCP/bin/install.js",
    "content": "#!/usr/bin/env node\n\nimport fs from 'fs';\nimport path from 'path';\nimport os from 'os';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nconsole.log('🚀 Installing iCSS MCP Server v1.1.1 for Cursor...\\n');\n\n// 获取 Cursor 配置目录\nfunction getCursorConfigPath() {\n  const platform = process.platform;\n  const homeDir = os.homedir();\n  \n  switch (platform) {\n    case 'darwin': // macOS\n      return path.join(homeDir, '.config', 'cursor');\n    case 'win32': // Windows\n      return path.join(homeDir, 'AppData', 'Roaming', 'Cursor', 'User');\n    case 'linux': // Linux\n      return path.join(homeDir, '.config', 'cursor');\n    default:\n      throw new Error(`Unsupported platform: ${platform}`);\n  }\n}\n\n// 获取包的绝对路径\nfunction getPackagePath() {\n  // 从 node_modules/@icss/mcp-server/bin/install.js 回到包根目录\n  return path.resolve(__dirname, '..');\n}\n\n// 创建或更新 MCP 配置\nfunction updateMcpConfig() {\n  try {\n    const configDir = getCursorConfigPath();\n    const configFile = path.join(configDir, 'mcp_settings.json');\n    \n    // 确保配置目录存在\n    if (!fs.existsSync(configDir)) {\n      fs.mkdirSync(configDir, { recursive: true });\n      console.log(`✅ Created Cursor config directory: ${configDir}`);\n    }\n    \n    // 读取现有配置或创建新配置\n    let config = { mcpServers: {} };\n    if (fs.existsSync(configFile)) {\n      try {\n        const existingConfig = fs.readFileSync(configFile, 'utf8');\n        config = JSON.parse(existingConfig);\n        console.log('📝 Found existing MCP configuration');\n      } catch (err) {\n        console.warn('⚠️  Existing config file is invalid, creating new one');\n      }\n    }\n    \n    // 确保 mcpServers 对象存在\n    if (!config.mcpServers) {\n      config.mcpServers = {};\n    }\n    \n    // 添加或更新 iCSS MCP Server 配置\n    const packagePath = getPackagePath();\n    const serverPath = path.join(packagePath, 'server.js');\n    \n    config.mcpServers.icss = {\n      command: \"node\",\n      args: [serverPath],\n      env: {}\n    };\n    \n    // 写入配置文件\n    fs.writeFileSync(configFile, JSON.stringify(config, null, 2));\n    console.log(`✅ Updated MCP configuration: ${configFile}`);\n    \n    return configFile;\n    \n  } catch (error) {\n    console.error('❌ Failed to update MCP configuration:', error.message);\n    throw error;\n  }\n}\n\n// 验证安装\nfunction verifyInstallation() {\n  try {\n    const packagePath = getPackagePath();\n    const serverPath = path.join(packagePath, 'server.js');\n    const dbPath = path.join(packagePath, 'data', 'icss.db');\n    \n    console.log('\\n🔍 Verifying installation...');\n    \n    if (!fs.existsSync(serverPath)) {\n      throw new Error(`Server file not found: ${serverPath}`);\n    }\n    console.log('✅ Server file exists');\n    \n    if (!fs.existsSync(dbPath)) {\n      console.log('⚠️  Database not found, will be created on first run');\n    } else {\n      console.log('✅ Database file exists');\n    }\n    \n    // 检查新增的脚本文件\n    const inspirationScript = path.join(packagePath, 'scripts', 'fetch-inspiration.js');\n    if (fs.existsSync(inspirationScript)) {\n      console.log('✅ CSS-Inspiration integration script exists');\n    }\n    \n    const testInspiration = path.join(packagePath, 'test-inspiration.js');\n    if (fs.existsSync(testInspiration)) {\n      console.log('✅ CSS-Inspiration test script exists');\n    }\n    \n    console.log('✅ Installation verified');\n    \n  } catch (error) {\n    console.error('❌ Installation verification failed:', error.message);\n    throw error;\n  }\n}\n\n// 显示使用说明\nfunction showUsageInstructions(configFile) {\n  console.log('\\n🎉 Installation completed successfully!\\n');\n  \n  console.log('🆕 What\\'s new in v1.1.1:');\n  console.log('   - 🎨 Integrated CSS-Inspiration with 160+ complete demos');\n  console.log('   - 🔍 Search both iCSS articles and CSS-Inspiration demos');\n  console.log('   - 💾 Complete runnable code for all demos');\n  console.log('   - 📊 Smart categorization by difficulty and type\\n');\n  \n  console.log('📋 Next steps:');\n  console.log('1. Restart Cursor IDE completely');\n  console.log('2. The iCSS MCP Server should be available automatically');\n  console.log('3. Try asking Cursor about CSS techniques from both libraries\\n');\n  \n  console.log('🛠️  Configuration details:');\n  console.log(`   Config file: ${configFile}`);\n  console.log(`   Server path: ${path.join(getPackagePath(), 'server.js')}`);\n  \n  console.log('\\n💡 Enhanced usage examples:');\n  console.log('   - \"Show me CSS techniques for flex layout\"');\n  console.log('   - \"Find 3D animation demos from CSS-Inspiration\"');\n  console.log('   - \"Get complete code for demo ID 25\"');\n  console.log('   - \"Search for border effects with runnable code\"');\n  console.log('   - \"Get a random CSS tip from both libraries\"');\n  \n  console.log('\\n🔧 Manual testing:');\n  console.log('   Run: npx icss-mcp-server');\n  console.log('   Or:  icss-mcp');\n  \n  console.log('\\n📚 More info: https://github.com/chokcoco/iCSS');\n  console.log('📚 CSS-Inspiration: https://github.com/chokcoco/CSS-Inspiration');\n}\n\n// 主安装流程\nasync function main() {\n  try {\n    // 更新配置\n    const configFile = updateMcpConfig();\n    \n    // 验证安装\n    verifyInstallation();\n    \n    // 显示说明\n    showUsageInstructions(configFile);\n    \n  } catch (error) {\n    console.error('\\n❌ Installation failed:', error.message);\n    console.error('\\n🔧 Manual setup instructions:');\n    console.error('1. Create ~/.config/cursor/mcp_settings.json');\n    console.error('2. Add the following configuration:');\n    console.error(JSON.stringify({\n      mcpServers: {\n        icss: {\n          command: \"node\",\n          args: [path.join(getPackagePath(), 'server.js')],\n          env: {}\n        }\n      }\n    }, null, 2));\n    \n    process.exit(1);\n  }\n}\n\n// 如果直接运行此脚本\nif (import.meta.url === `file://${process.argv[1]}`) {\n  main();\n} "
  },
  {
    "path": "MCP/debug-interactive.js",
    "content": "#!/usr/bin/env node\n\nimport { spawn } from 'child_process';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport readline from 'readline';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// 创建readline接口\nconst rl = readline.createInterface({\n  input: process.stdin,\n  output: process.stdout\n});\n\nconsole.log('🔄 Starting MCP Server...');\n\n// 启动服务器进程\nconst serverProcess = spawn('node', ['debug-local.js'], {\n  cwd: __dirname,\n  stdio: ['pipe', 'pipe', 'inherit']\n});\n\nlet serverReady = false;\n\n// 监听服务器输出\nserverProcess.stdout.on('data', (data) => {\n  const output = data.toString();\n  \n  // 检查服务器是否准备好\n  if (output.includes('Search index loaded with') && !serverReady) {\n    serverReady = true;\n    console.log('\\n🚀 Server is ready! You can now send commands.');\n    showPrompt();\n  }\n});\n\n// 显示命令提示\nfunction showPrompt() {\n  console.log('\\n📝 Available commands:');\n  console.log('1. List all tools');\n  console.log('2. Search CSS techniques');\n  console.log('3. Get random CSS tip');\n  console.log('4. Custom command');\n  console.log('5. Exit');\n  \n  rl.question('\\n👉 Enter command number: ', (answer) => {\n    switch(answer) {\n      case '1':\n        sendCommand({\n          jsonrpc: \"2.0\",\n          id: 1,\n          method: \"tools/list\",\n          params: {}\n        });\n        break;\n      case '2':\n        rl.question('Enter search query: ', (query) => {\n          sendCommand({\n            jsonrpc: \"2.0\",\n            id: 2,\n            method: \"tools/call\",\n            params: {\n              name: \"search_css_techniques\",\n              arguments: {\n                query: query,\n                limit: 3\n              }\n            }\n          });\n        });\n        break;\n      case '3':\n        sendCommand({\n          jsonrpc: \"2.0\",\n          id: 3,\n          method: \"tools/call\",\n          params: {\n            name: \"get_random_css_tip\",\n            arguments: {}\n          }\n        });\n        break;\n      case '4':\n        rl.question('Enter custom JSON-RPC command: ', (cmd) => {\n          try {\n            const command = JSON.parse(cmd);\n            sendCommand(command);\n          } catch (e) {\n            console.error('❌ Invalid JSON:', e.message);\n            showPrompt();\n          }\n        });\n        break;\n      case '5':\n        console.log('👋 Shutting down server...');\n        serverProcess.kill();\n        rl.close();\n        process.exit(0);\n        break;\n      default:\n        console.log('❌ Invalid command number');\n        showPrompt();\n    }\n  });\n}\n\n// 发送命令到服务器\nfunction sendCommand(command) {\n  console.log('\\n📤 Sending command:', JSON.stringify(command, null, 2));\n  serverProcess.stdin.write(JSON.stringify(command) + '\\n');\n  setTimeout(showPrompt, 1000);\n}\n\n// 处理退出\nprocess.on('SIGINT', () => {\n  console.log('\\n👋 Shutting down server...');\n  serverProcess.kill();\n  rl.close();\n  process.exit(0);\n});\n\nserverProcess.on('error', (error) => {\n  console.error('❌ Server error:', error);\n  rl.close();\n  process.exit(1);\n}); "
  },
  {
    "path": "MCP/debug-local.js",
    "content": "#!/usr/bin/env node\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n  CallToolRequestSchema,\n  ErrorCode,\n  ListToolsRequestSchema,\n  McpError,\n} from '@modelcontextprotocol/sdk/types.js';\nimport Database from 'sqlite3';\nimport Fuse from 'fuse.js';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// 调试模式\nconst DEBUG = true;\nconst debugLog = (...args) => {\n  if (DEBUG) {\n    console.error(`[DEBUG ${new Date().toISOString()}]`, ...args);\n  }\n};\n\nclass IcssDebugServer {\n  constructor() {\n    debugLog('🚀 Initializing iCSS Debug Server');\n    \n    this.server = new Server(\n      {\n        name: 'icss-debug-server',\n        version: '1.0.0',\n      },\n      {\n        capabilities: {\n          tools: {},\n        },\n      }\n    );\n\n    this.transport = null;\n    this.db = null;\n    this.searchEngine = null;\n    this.isReady = false;\n    this.dbInitFailed = false;\n    \n    this.setupDatabase();\n    this.setupHandlers();\n  }\n\n  setupDatabase() {\n    debugLog('📂 Setting up database connection...');\n    const dbPath = path.join(__dirname, 'data', 'icss.db');\n    debugLog(`📍 Database path: ${dbPath}`);\n    \n    this.db = new Database.Database(dbPath, (err) => {\n      if (err) {\n        console.error('❌ Error opening database:', err);\n        this.dbInitFailed = true;\n      } else {\n        debugLog('✅ Connected to SQLite database');\n        this.loadSearchIndex();\n      }\n    });\n  }\n\n  loadSearchIndex() {\n    debugLog('🔍 Loading search index...');\n    \n    const query = `\n      SELECT \n        i.*,\n        GROUP_CONCAT(DISTINCT al.label) as tags,\n        GROUP_CONCAT(DISTINCT al.category) as categories,\n        GROUP_CONCAT(DISTINCT al.weight) as weights,\n        GROUP_CONCAT(DISTINCT lc.description) as tag_descriptions\n      FROM issues i\n      LEFT JOIN article_labels al ON i.number = al.issue_number\n      LEFT JOIN label_categories lc ON al.label = lc.label\n      GROUP BY i.number\n    `;\n\n    this.db.all(query, [], (err, rows) => {\n      if (err) {\n        console.error('❌ Error loading search index:', err);\n        this.dbInitFailed = true;\n        return;\n      }\n\n      debugLog(`📊 Loaded ${rows.length} articles for search index`);\n\n      // 处理数据\n      const processedRows = rows.map(row => {\n        const tags = row.tags ? row.tags.split(',') : [];\n        const categories = row.categories ? row.categories.split(',') : [];\n        const weights = row.weights ? row.weights.split(',').map(Number) : [];\n        const descriptions = row.tag_descriptions ? row.tag_descriptions.split(',') : [];\n        \n        // 解析 JSON 格式的标签\n        let labels = [];\n        try {\n          labels = row.labels ? JSON.parse(row.labels) : [];\n        } catch (e) {\n          debugLog(`⚠️ Failed to parse labels for issue #${row.number}`);\n        }\n        \n        return {\n          ...row,\n          tags,\n          categories,\n          weights,\n          descriptions,\n          labels,\n          // 合并所有可搜索的文本\n          searchContent: [\n            row.title,\n            row.body,\n            ...tags,\n            ...categories,\n            ...descriptions,\n            ...labels\n          ].filter(Boolean).join(' ')\n        };\n      });\n\n      const fuseOptions = {\n        keys: [\n          { name: 'title', weight: 0.4 },\n          { name: 'searchContent', weight: 0.3 },\n          { name: 'tags', weight: 0.2 },\n          { name: 'categories', weight: 0.1 }\n        ],\n        threshold: 0.3,\n        includeScore: true,\n        includeMatches: true\n      };\n\n      this.searchEngine = new Fuse(processedRows, fuseOptions);\n      this.isReady = true;\n      debugLog(`🎉 Search index loaded with ${rows.length} articles - Server ready!`);\n    });\n  }\n\n  async waitForReady(timeout = 10000) {\n    debugLog(`⏳ Waiting for server to be ready (timeout: ${timeout}ms)`);\n    const startTime = Date.now();\n    \n    while (!this.isReady && !this.dbInitFailed) {\n      if (Date.now() - startTime > timeout) {\n        throw new McpError(\n          ErrorCode.InternalError,\n          'Server initialization timeout'\n        );\n      }\n      await new Promise(resolve => setTimeout(resolve, 100));\n    }\n    \n    if (this.dbInitFailed) {\n      throw new McpError(\n        ErrorCode.InternalError,\n        'Server initialization failed'\n      );\n    }\n  }\n\n  setupHandlers() {\n    debugLog('🔗 Setting up request handlers...');\n    \n    this.server.setRequestHandler(ListToolsRequestSchema, async () => {\n      try {\n        debugLog('📋 Received ListTools request');\n        await this.waitForReady();\n        \n        const tools = [\n          {\n            name: 'search_css_techniques',\n            description: 'Search for CSS techniques with filtering and sorting options',\n            inputSchema: {\n              type: 'object',\n              properties: {\n                query: {\n                  type: 'string',\n                  description: 'Search query'\n                },\n                categories: {\n                  type: 'array',\n                  items: { type: 'string' },\n                  description: 'Filter by categories'\n                },\n                tags: {\n                  type: 'array',\n                  items: { type: 'string' },\n                  description: 'Filter by tags'\n                },\n                min_weight: {\n                  type: 'number',\n                  description: 'Minimum tag weight'\n                },\n                limit: {\n                  type: 'number',\n                  default: 10\n                }\n              },\n              required: ['query']\n            }\n          }\n        ];\n        \n        return { tools };\n      } catch (error) {\n        debugLog('❌ Error in ListTools:', error);\n        throw error;\n      }\n    });\n\n    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n      const { name, arguments: args } = request.params;\n      debugLog(`🔧 Received CallTool request: ${name}`, args);\n\n      try {\n        await this.waitForReady();\n\n        switch (name) {\n          case 'search_css_techniques':\n            return await this.searchCssTechniques(args);\n          default:\n            throw new McpError(\n              ErrorCode.MethodNotFound,\n              `Unknown tool: ${name}`\n            );\n        }\n      } catch (error) {\n        debugLog(`❌ Error in ${name}:`, error);\n        throw error;\n      }\n    });\n  }\n\n  async searchCssTechniques({ query, categories = [], tags = [], min_weight, limit = 10 }) {\n    debugLog(`🔍 Searching CSS techniques: \"${query}\"`);\n    \n    if (!this.searchEngine) {\n      throw new McpError(\n        ErrorCode.InternalError,\n        'Search engine not initialized'\n      );\n    }\n    \n    let results = this.searchEngine.search(query);\n    debugLog(`📊 Found ${results.length} initial results`);\n\n    // 过滤结果\n    if (categories.length > 0) {\n      results = results.filter(result => \n        result.item.categories.some(cat => \n          categories.some(c => cat.toLowerCase().includes(c.toLowerCase()))\n        )\n      );\n      debugLog(`📊 After category filter: ${results.length} results`);\n    }\n\n    if (tags.length > 0) {\n      results = results.filter(result => \n        tags.every(tag => \n          result.item.tags.some(t => t.toLowerCase().includes(tag.toLowerCase()))\n        )\n      );\n      debugLog(`📊 After tag filter: ${results.length} results`);\n    }\n\n    if (min_weight) {\n      results = results.filter(result => \n        result.item.weights.some(w => w >= min_weight)\n      );\n      debugLog(`📊 After weight filter: ${results.length} results`);\n    }\n\n    // 格式化结果\n    const formattedResults = results.slice(0, limit).map(result => {\n      const item = result.item;\n      return {\n        title: item.title,\n        number: item.number,\n        url: item.html_url,\n        relevance: Math.round((1 - result.score) * 100),\n        tags: item.tags.map((tag, i) => ({\n          name: tag,\n          category: item.categories[i] || 'other',\n          weight: item.weights[i] || 1.0,\n          description: item.descriptions[i] || ''\n        })),\n        preview: this.extractPreview(item.body),\n        updated_at: item.updated_at\n      };\n    });\n\n    debugLog(`✅ Returning ${formattedResults.length} formatted results`);\n\n    return {\n      content: [\n        {\n          type: 'text',\n          text: `Found ${formattedResults.length} CSS techniques for \"${query}\":\\n\\n` +\n                formattedResults.map((result, index) => \n                  `${index + 1}. **${result.title}** (Issue #${result.number})\\n` +\n                  `   📅 Updated: ${new Date(result.updated_at).toLocaleDateString()}\\n` +\n                  `   💯 Relevance: ${result.relevance}%\\n` +\n                  `   🏷️ Tags:\\n${result.tags.map(tag => \n                    `      - ${tag.name} (${tag.category}, weight: ${tag.weight.toFixed(2)})` +\n                    (tag.description ? `\\n        ${tag.description}` : '')\n                  ).join('\\n')}\\n` +\n                  `   📝 Preview: ${result.preview}\\n` +\n                  `   🔗 Link: ${result.url}\\n`\n                ).join('\\n')\n        }\n      ]\n    };\n  }\n\n  extractPreview(body, maxLength = 200) {\n    if (!body) return 'No preview available';\n    \n    const cleanText = body\n      .replace(/```[\\s\\S]*?```/g, '[code block]')\n      .replace(/`([^`]+)`/g, '$1')\n      .replace(/[#*_~]/g, '')\n      .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n      .replace(/\\s+/g, ' ')\n      .trim();\n\n    return cleanText.length > maxLength \n      ? cleanText.substring(0, maxLength - 3) + '...'\n      : cleanText;\n  }\n\n  async run() {\n    debugLog('🚀 Starting debug server...');\n    this.transport = new StdioServerTransport();\n    \n    try {\n      await this.server.connect(this.transport);\n      debugLog('✅ Server connected to transport');\n      \n      // 保持进程运行\n      process.stdin.resume();\n      \n      // 处理进程退出\n      process.on('SIGINT', () => this.cleanup());\n      process.on('SIGTERM', () => this.cleanup());\n    } catch (error) {\n      debugLog('💥 Server error:', error);\n      console.error('❌ Fatal error:', error.message);\n      process.exit(1);\n    }\n  }\n\n  cleanup() {\n    debugLog('🧹 Cleaning up...');\n    if (this.db) {\n      this.db.close();\n    }\n    if (this.transport) {\n      this.transport.close();\n    }\n    process.exit(0);\n  }\n}\n\n// 启动服务器\nconst server = new IcssDebugServer();\nserver.run(); "
  },
  {
    "path": "MCP/debug-query.js",
    "content": "#!/usr/bin/env node\n\nimport { spawn } from 'child_process';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\n// 测试查询列表\nconst testQueries = [\n  {\n    name: 'List Tools',\n    query: {\n      jsonrpc: \"2.0\",\n      id: 1,\n      method: \"tools/list\",\n      params: {}\n    }\n  },\n  {\n    name: 'Search Border Techniques',\n    query: {\n      jsonrpc: \"2.0\",\n      id: 2,\n      method: \"tools/call\",\n      params: {\n        name: \"search_css_techniques\",\n        arguments: {\n          query: \"border\",\n          categories: [\"visual_effects\"],\n          limit: 3\n        }\n      }\n    }\n  },\n  {\n    name: 'Search Animation Techniques',\n    query: {\n      jsonrpc: \"2.0\",\n      id: 3,\n      method: \"tools/call\",\n      params: {\n        name: \"search_css_techniques\",\n        arguments: {\n          query: \"animation\",\n          categories: [\"animation\"],\n          limit: 3\n        }\n      }\n    }\n  },\n  {\n    name: 'Search High Weight Techniques',\n    query: {\n      jsonrpc: \"2.0\",\n      id: 4,\n      method: \"tools/call\",\n      params: {\n        name: \"search_css_techniques\",\n        arguments: {\n          query: \"\",\n          min_weight: 3,\n          limit: 3\n        }\n      }\n    }\n  }\n];\n\n// 创建一个Promise来处理服务器响应\nfunction queryServer(command) {\n  return new Promise((resolve, reject) => {\n    console.log('\\n🔄 Starting debug server process...');\n    \n    const serverProcess = spawn('node', ['debug-local.js'], {\n      cwd: __dirname,\n      stdio: ['pipe', 'pipe', 'inherit']\n    });\n\n    let responseData = '';\n    let serverReady = false;\n\n    // 监听服务器输出\n    serverProcess.stdout.on('data', (data) => {\n      const output = data.toString();\n      console.log('📝 Server output:', output);\n      \n      // 检查服务器是否已经准备好\n      if (output.includes('Search index loaded with') && !serverReady) {\n        serverReady = true;\n        console.log('\\n🚀 Server is ready, sending query...');\n        \n        // 发送命令\n        console.log('\\n📤 Sending query:', JSON.stringify(command, null, 2));\n        serverProcess.stdin.write(JSON.stringify(command) + '\\n');\n      }\n\n      // 尝试解析响应\n      try {\n        if (output.includes('\"result\"') || output.includes('\"error\"')) {\n          const jsonStart = output.indexOf('{');\n          const jsonEnd = output.lastIndexOf('}') + 1;\n          if (jsonStart !== -1 && jsonEnd !== -1) {\n            const jsonStr = output.substring(jsonStart, jsonEnd);\n            const response = JSON.parse(jsonStr);\n            \n            console.log('\\n✅ Received response:', JSON.stringify(response, null, 2));\n            \n            // 关闭服务器\n            serverProcess.kill();\n            \n            if (response.error) {\n              reject(new Error(response.error.message));\n            } else {\n              resolve(response);\n            }\n          }\n        }\n      } catch (e) {\n        console.log('⚠️ Parse error:', e.message);\n      }\n    });\n\n    // 错误处理\n    serverProcess.on('error', (error) => {\n      console.error('❌ Server process error:', error);\n      reject(error);\n    });\n\n    // 设置超时\n    setTimeout(() => {\n      if (!serverReady) {\n        console.log('\\n⚠️ Server never became ready');\n      }\n      console.log('\\n⚠️ Query timeout, shutting down server...');\n      serverProcess.kill();\n      reject(new Error('Query timeout'));\n    }, 15000);\n  });\n}\n\n// 运行所有测试查询\nasync function runTests() {\n  console.log('🧪 Starting test queries...\\n');\n  \n  for (const test of testQueries) {\n    console.log(`\\n📋 Running test: ${test.name}`);\n    try {\n      const response = await queryServer(test.query);\n      console.log('\\n✅ Response:', JSON.stringify(response, null, 2));\n    } catch (error) {\n      console.error('\\n❌ Error:', error.message);\n    }\n    // 等待一下，避免服务器进程冲突\n    await new Promise(resolve => setTimeout(resolve, 1000));\n  }\n  \n  console.log('\\n🎉 All tests completed!');\n}\n\n// 执行测试\nrunTests().catch(error => {\n  console.error('❌ Test execution error:', error);\n  process.exit(1);\n}); "
  },
  {
    "path": "MCP/debug.js",
    "content": "#!/usr/bin/env node\n\nimport Database from 'sqlite3';\nimport Fuse from 'fuse.js';\n\nconsole.log('🔍 MCP Server Debug Tool\\n');\n\nclass DebugTester {\n  constructor() {\n    this.db = null;\n    this.searchEngine = null;\n    this.isReady = false;\n  }\n\n  async init() {\n    return new Promise((resolve, reject) => {\n      console.log('📂 Connecting to database...');\n      \n      this.db = new Database.Database('./data/icss.db', (err) => {\n        if (err) {\n          console.error('❌ Database connection failed:', err.message);\n          reject(err);\n          return;\n        }\n        \n        console.log('✅ Database connected');\n        this.loadSearchIndex().then(resolve).catch(reject);\n      });\n    });\n  }\n\n  async loadSearchIndex() {\n    return new Promise((resolve, reject) => {\n      console.log('🔄 Loading search index...');\n      \n      this.db.all('SELECT * FROM issues', (err, rows) => {\n        if (err) {\n          console.error('❌ Failed to load search index:', err.message);\n          reject(err);\n          return;\n        }\n\n        console.log(`📊 Loaded ${rows.length} articles`);\n\n        const fuseOptions = {\n          keys: [\n            { name: 'title', weight: 0.4 },\n            { name: 'search_content', weight: 0.4 },\n            { name: 'labels', weight: 0.2 }\n          ],\n          threshold: 0.3,\n          includeScore: true,\n          includeMatches: true\n        };\n\n        this.searchEngine = new Fuse(rows, fuseOptions);\n        this.isReady = true;\n        console.log('✅ Search index ready');\n        resolve();\n      });\n    });\n  }\n\n  async testSearchFunction(query = \"透明 边框\") {\n    console.log(`\\n🔍 Testing search for: \"${query}\"`);\n    \n    if (!this.isReady) {\n      console.error('❌ Search engine not ready');\n      return;\n    }\n\n    const startTime = Date.now();\n    \ntry {\n      const results = this.searchEngine.search(query).slice(0, 5);\n      const endTime = Date.now();\n      \n      console.log(`⏱️ Search completed in ${endTime - startTime}ms`);\n      console.log(`📋 Found ${results.length} results`);\n      \n      results.forEach((result, index) => {\n        const item = result.item;\n        console.log(`\\n${index + 1}. ${item.title}`);\n        console.log(`   Issue #${item.number}`);\n        console.log(`   Score: ${Math.round((1 - result.score) * 100)}%`);\n        console.log(`   URL: ${item.html_url}`);\n        \n        if (result.matches) {\n          console.log(`   Matches: ${result.matches.length}`);\n          result.matches.forEach(match => {\n            console.log(`     - ${match.key}: ${match.value?.substring(0, 50)}...`);\n          });\n    }\n  });\n      \n      return results;\n      \n    } catch (error) {\n      console.error('❌ Search failed:', error.message);\n      console.error(error.stack);\n}\n  }\n\n  async testSpecificArticle(issueNumber = 1) {\n    console.log(`\\n📄 Testing get article #${issueNumber}`);\n    \n    return new Promise((resolve, reject) => {\n      this.db.get(\n        'SELECT * FROM issues WHERE number = ?',\n        [issueNumber],\n        (err, row) => {\n  if (err) {\n            console.error('❌ Database query failed:', err.message);\n            reject(err);\n            return;\n          }\n\n          if (!row) {\n            console.error(`❌ Article #${issueNumber} not found`);\n            reject(new Error('Article not found'));\n    return;\n  }\n  \n          console.log('✅ Article found:');\n          console.log(`   Title: ${row.title}`);\n          console.log(`   Created: ${row.created_at}`);\n          console.log(`   Updated: ${row.updated_at}`);\n          console.log(`   Body length: ${row.body?.length || 0} chars`);\n          \n          resolve(row);\n        }\n      );\n    });\n  }\n\n  async testCategories() {\n    console.log('\\n🏷️ Testing categories listing');\n    \n    return new Promise((resolve, reject) => {\n      this.db.all(\n        'SELECT labels, COUNT(*) as count FROM issues WHERE labels IS NOT NULL GROUP BY labels LIMIT 10',\n        (err, rows) => {\n    if (err) {\n            console.error('❌ Categories query failed:', err.message);\n            reject(err);\n            return;\n          }\n\n          console.log(`✅ Found ${rows.length} label groups`);\n          \n          const categoryMap = new Map();\n          \n          rows.forEach(row => {\n            if (row.labels) {\n              try {\n                const labels = JSON.parse(row.labels);\n                labels.forEach(label => {\n                  categoryMap.set(label, (categoryMap.get(label) || 0) + row.count);\n                });\n              } catch (parseErr) {\n                console.warn(`⚠️ Failed to parse labels: ${row.labels}`);\n              }\n            }\n          });\n\n          const topCategories = Array.from(categoryMap.entries())\n            .sort((a, b) => b[1] - a[1])\n            .slice(0, 10);\n\n          console.log('📊 Top categories:');\n          topCategories.forEach(([label, count]) => {\n            console.log(`   • ${label}: ${count} articles`);\n          });\n          \n          resolve(topCategories);\n        }\n      );\n    });\n  }\n\n  close() {\n    if (this.db) {\n      this.db.close();\n      console.log('📚 Database connection closed');\n    }\n  }\n    }\n    \n// 运行调试测试\nasync function runDebugTests() {\n  const tester = new DebugTester();\n  \n  try {\n    await tester.init();\n    \n    // 测试搜索功能 - 用你想要的查询\n    await tester.testSearchFunction(\"透明 边框\");\n    await tester.testSearchFunction(\"border radius\");\n    await tester.testSearchFunction(\"镂空\");\n    \n    // 测试获取文章\n    await tester.testSpecificArticle(1);\n    \n    // 测试分类\n    await tester.testCategories();\n    \n    console.log('\\n🎉 All debug tests completed successfully!');\n    \n  } catch (error) {\n    console.error('\\n❌ Debug test failed:', error.message);\n    console.error(error.stack);\n  } finally {\n    tester.close();\n  }\n}\n\n// 如果直接运行此脚本\nif (import.meta.url === `file://${process.argv[1]}`) {\n  runDebugTests();\n} "
  },
  {
    "path": "MCP/diagnose-mcp.js",
    "content": "#!/usr/bin/env node\n\nimport fs from 'fs';\nimport path from 'path';\nimport { spawn } from 'child_process';\n\nconsole.log('🔧 Comprehensive MCP Diagnosis Tool\\n');\n\n// 检查所有可能的 Cursor MCP 配置位置\nfunction checkCursorConfigs() {\n  console.log('1️⃣ Checking Cursor MCP Configuration Locations:\\n');\n  \n  const possiblePaths = [\n    '~/.config/cursor/mcp_settings.json',\n    '~/Library/Application Support/Cursor/mcp_settings.json',\n    '~/.cursor/mcp_settings.json',\n    process.env.HOME + '/.config/cursor/mcp_settings.json',\n    process.env.HOME + '/Library/Application Support/Cursor/mcp_settings.json'\n  ];\n\n  let foundConfigs = 0;\n  \n  possiblePaths.forEach(configPath => {\n    const expandedPath = configPath.startsWith('~') ? \n      configPath.replace('~', process.env.HOME) : configPath;\n    \n    if (fs.existsSync(expandedPath)) {\n      foundConfigs++;\n      console.log(`   ✅ Found: ${configPath}`);\n      try {\n        const content = fs.readFileSync(expandedPath, 'utf8');\n        const config = JSON.parse(content);\n        console.log(`      Servers: ${Object.keys(config.mcpServers || {}).join(', ')}`);\n      } catch (err) {\n        console.log(`      ❌ Invalid JSON: ${err.message}`);\n      }\n    } else {\n      console.log(`   ❌ Not found: ${configPath}`);\n    }\n  });\n  \n  if (foundConfigs === 0) {\n    console.log('\\n   ⚠️  No MCP configuration files found!');\n  }\n  \n  return foundConfigs > 0;\n}\n\n// 检查服务器可执行性\nfunction checkServerExecutability() {\n  console.log('\\n2️⃣ Checking Server Executability:\\n');\n  \n  const serverPath = path.resolve('./server.js');\n  console.log(`   Server path: ${serverPath}`);\n  \n  if (!fs.existsSync(serverPath)) {\n    console.log('   ❌ server.js not found');\n    return false;\n  }\n  \n  console.log('   ✅ server.js exists');\n  \n  // 检查权限\n  try {\n    fs.accessSync(serverPath, fs.constants.R_OK);\n    console.log('   ✅ server.js is readable');\n  } catch (err) {\n    console.log('   ❌ server.js is not readable');\n    return false;\n  }\n  \n  return true;\n}\n\n// 手动测试 MCP 协议\nfunction testMCPProtocol() {\n  console.log('\\n3️⃣ Testing MCP Protocol Manually:\\n');\n  \n  return new Promise((resolve) => {\n    const serverProcess = spawn('node', ['./server.js'], {\n      stdio: ['pipe', 'pipe', 'pipe']\n    });\n\n    let stdout = '';\n    let stderr = '';\n    \n    serverProcess.stdout.on('data', (data) => {\n      stdout += data.toString();\n    });\n    \n    serverProcess.stderr.on('data', (data) => {\n      stderr += data.toString();\n    });\n    \n    // 发送 MCP 初始化请求\n    const initRequest = JSON.stringify({\n      jsonrpc: \"2.0\",\n      id: 1,\n      method: \"initialize\",\n      params: {\n        protocolVersion: \"2024-11-05\",\n        capabilities: {},\n        clientInfo: {\n          name: \"test-client\",\n          version: \"1.0.0\"\n        }\n      }\n    }) + '\\n';\n    \n    serverProcess.stdin.write(initRequest);\n    \n    // 等待响应\n    setTimeout(() => {\n      serverProcess.kill();\n      \n      console.log('   📤 Sent initialization request');\n      \n      if (stdout.trim()) {\n        console.log('   ✅ Got stdout response:');\n        console.log(`      ${stdout.trim()}`);\n      } else {\n        console.log('   ❌ No stdout response');\n      }\n      \n      if (stderr.trim()) {\n        console.log('   ⚠️  Got stderr:');\n        console.log(`      ${stderr.trim()}`);\n      }\n      \n      resolve(stdout.length > 0);\n    }, 3000);\n  });\n}\n\n// 生成正确的配置\nfunction generateCorrectConfig() {\n  console.log('\\n4️⃣ Generating Correct Configuration:\\n');\n  \n  const serverPath = path.resolve('./server.js');\n  const configs = [\n    {\n      path: process.env.HOME + '/.config/cursor/mcp_settings.json',\n      content: {\n        mcpServers: {\n          icss: {\n            command: \"node\",\n            args: [serverPath],\n            env: {}\n          }\n        }\n      }\n    },\n    {\n      path: process.env.HOME + '/Library/Application Support/Cursor/mcp_settings.json',\n      content: {\n        mcpServers: {\n          icss: {\n            command: \"node\",\n            args: [serverPath],\n            env: {}\n          }\n        }\n      }\n    }\n  ];\n  \n  configs.forEach((config, index) => {\n    console.log(`   📝 Config ${index + 1}: ${config.path}`);\n    \n    // 确保目录存在\n    const dir = path.dirname(config.path);\n    if (!fs.existsSync(dir)) {\n      fs.mkdirSync(dir, { recursive: true });\n      console.log(`      ✅ Created directory: ${dir}`);\n    }\n    \n    // 写入配置\n    try {\n      fs.writeFileSync(config.path, JSON.stringify(config.content, null, 2));\n      console.log(`      ✅ Written configuration`);\n    } catch (err) {\n      console.log(`      ❌ Failed to write: ${err.message}`);\n    }\n  });\n  \n  console.log('\\n   💡 Try both locations and restart Cursor completely');\n}\n\n// 提供故障排除建议\nfunction provideTroubleshootingTips() {\n  console.log('\\n5️⃣ Troubleshooting Tips:\\n');\n  \n  const tips = [\n    '1. 完全退出 Cursor (Cmd+Q)，然后重新启动',\n    '2. 检查 Cursor 版本是否支持 MCP (需要较新版本)',\n    '3. 在 Cursor 中按 Cmd+Option+I 打开开发者工具，检查 Console 错误',\n    '4. 尝试在 Cursor 设置中查看 MCP 相关配置',\n    '5. 确保没有其他 MCP 服务器冲突',\n    '6. 如果使用 VS Code，确保安装了正确的 Cursor 而不是 VS Code'\n  ];\n  \n  tips.forEach(tip => console.log(`   ${tip}`));\n  \n  console.log('\\n   🔧 Manual Test Command:');\n  console.log(`   cd ${process.cwd()}`);\n  console.log('   echo \\'{\"jsonrpc\":\"2.0\",\"id\":1,\"method\":\"tools/list\",\"params\":{}}\\' | node server.js');\n}\n\n// 主诊断流程\nasync function runDiagnosis() {\n  console.log('Starting comprehensive MCP diagnosis...\\n');\n  \n  const hasConfig = checkCursorConfigs();\n  const serverWorks = checkServerExecutability();\n  \n  if (serverWorks) {\n    const protocolWorks = await testMCPProtocol();\n    console.log(`\\n   MCP Protocol Test: ${protocolWorks ? '✅ Working' : '❌ Failed'}`);\n  }\n  \n  generateCorrectConfig();\n  provideTroubleshootingTips();\n  \n  console.log('\\n🎯 Summary:');\n  console.log(`   Config Found: ${hasConfig ? '✅' : '❌'}`);\n  console.log(`   Server Works: ${serverWorks ? '✅' : '❌'}`);\n  console.log('\\n📚 Next: Restart Cursor completely and try again');\n}\n\n// 运行诊断\nrunDiagnosis().catch(console.error); "
  },
  {
    "path": "MCP/package.json",
    "content": "{\n  \"name\": \"icss-mcp-server\",\n  \"version\": \"1.1.1\",\n  \"description\": \"Comprehensive MCP Server integrating iCSS techniques and CSS-Inspiration demos - provides complete CSS solutions with runnable code\",\n  \"main\": \"server.js\",\n  \"bin\": {\n    \"icss-mcp\": \"./bin/icss-mcp.js\",\n    \"icss-mcp-install\": \"./bin/install.js\"\n  },\n  \"type\": \"module\",\n  \"files\": [\n    \"server.js\",\n    \"setup.js\",\n    \"bin/\",\n    \"scripts/\",\n    \"data/\",\n    \"README.md\",\n    \"README.en.md\",\n    \"LICENSE\"\n  ],\n  \"scripts\": {\n    \"setup\": \"node setup.js\",\n    \"start\": \"node server.js\",\n    \"dev\": \"nodemon server.js\",\n    \"build\": \"node scripts/fetch-issues.js\",\n    \"build:inspiration\": \"node scripts/fetch-inspiration.js\",\n    \"build:all\": \"npm run build && npm run build:inspiration\",\n    \"test\": \"node test-server.js\",\n    \"test:inspiration\": \"node test-inspiration.js\",\n    \"test:all\": \"npm run test && npm run test:inspiration\",\n    \"install-mcp\": \"node bin/install.js\",\n    \"postinstall\": \"node setup.js\",\n    \"prepublishOnly\": \"npm run build:all && npm test\"\n  },\n  \"keywords\": [\n    \"mcp\",\n    \"css\",\n    \"frontend\",\n    \"cursor\",\n    \"icss\",\n    \"css-inspiration\",\n    \"model-context-protocol\",\n    \"ai-tools\",\n    \"css-techniques\",\n    \"css-demos\",\n    \"web-development\",\n    \"css-examples\",\n    \"chokcoco\"\n  ],\n  \"author\": {\n    \"name\": \"iCSS MCP Server\",\n    \"email\": \"icss-mcp@example.com\",\n    \"url\": \"https://github.com/chokcoco/iCSS\"\n  },\n  \"license\": \"MIT\",\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"https://github.com/chokcoco/iCSS.git\",\n    \"directory\": \"MCP\"\n  },\n  \"homepage\": \"https://github.com/chokcoco/iCSS/tree/main/MCP\",\n  \"bugs\": {\n    \"url\": \"https://github.com/chokcoco/iCSS/issues\"\n  },\n  \"dependencies\": {\n    \"@modelcontextprotocol/sdk\": \"^0.4.0\",\n    \"axios\": \"^1.10.0\",\n    \"cheerio\": \"^1.0.0-rc.12\",\n    \"dotenv\": \"^16.6.1\",\n    \"fuse.js\": \"^7.0.0\",\n    \"marked\": \"^9.1.6\",\n    \"sqlite3\": \"^5.1.7\"\n  },\n  \"devDependencies\": {\n    \"nodemon\": \"^3.0.1\"\n  },\n  \"peerDependencies\": {\n    \"node\": \">=18.0.0\"\n  },\n  \"engines\": {\n    \"node\": \">=18.0.0\",\n    \"npm\": \">=8.0.0\"\n  },\n  \"publishConfig\": {\n    \"access\": \"public\",\n    \"registry\": \"https://registry.npmjs.org/\"\n  }\n}\n"
  },
  {
    "path": "MCP/scripts/cursor-config.json",
    "content": "{\n  \"mcpServers\": {\n    \"icss\": {\n      \"command\": \"node\",\n      \"args\": [\"/Users/qi.qiao/Documents/Git/icss/MCP/server.js\"],\n      \"cwd\": \"/Users/qi.qiao/Documents/Git/icss/MCP\",\n      \"env\": {\n        \"DEBUG\": \"true\"\n      },\n      \"description\": \"iCSS repository CSS techniques and solutions\"\n    }\n  },\n  \"_alternatives\": {\n    \"comment\": \"Alternative configurations based on your setup:\",\n    \"option1_absolute_path\": {\n      \"args\": [\"/Users/qi.qiao/Documents/Git/icss/MCP/server.js\"]\n    },\n    \"option2_if_cursor_in_root\": {\n      \"args\": [\"./MCP/server.js\"]\n    },\n    \"option3_with_cwd\": {\n      \"args\": [\"server.js\"],\n      \"cwd\": \"./MCP\"\n    }\n  },\n  \"_instructions\": {\n    \"setup\": \"Copy the 'mcpServers' section to your Cursor settings.json\",\n    \"location_mac\": \"~/Library/Application Support/Cursor/User/settings.json\",\n    \"location_windows\": \"%APPDATA%/Cursor/User/settings.json\",\n    \"location_linux\": \"~/.config/Cursor/User/settings.json\"\n  }\n} "
  },
  {
    "path": "MCP/scripts/fetch-inspiration.js",
    "content": "import axios from 'axios';\nimport Database from 'sqlite3';\nimport fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport dotenv from 'dotenv';\n\ndotenv.config();\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nclass InspirationFetcher {\n  constructor() {\n    this.baseURL = 'https://api.github.com/repos/chokcoco/CSS-Inspiration';\n    this.rawURL = 'https://raw.githubusercontent.com/chokcoco/CSS-Inspiration/master';\n    this.dbPath = path.join(__dirname, '../data/icss.db');\n    this.dataDir = path.join(__dirname, '../data');\n    \n    // GitHub Token\n    this.githubToken = process.env.GITHUB_TOKEN;\n    if (!this.githubToken) {\n      console.warn('⚠️ No GITHUB_TOKEN found in environment variables. API rate limits will be restricted.');\n    }\n    \n    // 确保数据目录存在\n    if (!fs.existsSync(this.dataDir)) {\n      fs.mkdirSync(this.dataDir, { recursive: true });\n    }\n    \n    // 初始化数据库连接\n    this.db = new Database.Database(this.dbPath, (err) => {\n      if (err) {\n        console.error('❌ Error opening database:', err);\n        process.exit(1);\n      }\n    });\n\n    // CSS-Inspiration 分类映射\n    this.categoryMapping = {\n      '3d': '3D 效果',\n      'animation': '动画效果',\n      'background': '背景效果',\n      'blendmode': '混合模式',\n      'border': '边框效果',\n      'clippath': '裁剪路径',\n      'cssdoodle': 'CSS-doodle',\n      'filter': '滤镜效果',\n      'layout': '布局技术',\n      'others': '综合技巧',\n      'pesudo': '伪类/伪元素',\n      'shadow': '阴影效果',\n      'svg': 'SVG 技术',\n      'text': '文字效果'\n    };\n\n    this.initializeDatabase();\n  }\n\n  initializeDatabase() {\n    console.log('🔧 Initializing CSS-Inspiration tables...');\n    \n    // CSS-Inspiration 项目表\n    const createInspirationTableSQL = `\n      CREATE TABLE IF NOT EXISTS css_inspiration (\n        id INTEGER PRIMARY KEY,\n        category TEXT NOT NULL,\n        filename TEXT NOT NULL,\n        title TEXT NOT NULL,\n        description TEXT,\n        html_content TEXT,\n        css_content TEXT,\n        demo_url TEXT,\n        source_url TEXT,\n        tags TEXT,\n        difficulty_level TEXT,\n        browser_support TEXT,\n        created_at TEXT DEFAULT CURRENT_TIMESTAMP,\n        updated_at TEXT DEFAULT CURRENT_TIMESTAMP,\n        search_content TEXT,\n        UNIQUE(category, filename)\n      )\n    `;\n\n    // 代码片段表（用于存储提取的代码块）\n    const createCodeSnippetsTableSQL = `\n      CREATE TABLE IF NOT EXISTS code_snippets (\n        id INTEGER PRIMARY KEY,\n        inspiration_id INTEGER,\n        snippet_type TEXT, -- 'html', 'css', 'javascript'\n        code_content TEXT,\n        line_start INTEGER,\n        line_end INTEGER,\n        description TEXT,\n        FOREIGN KEY(inspiration_id) REFERENCES css_inspiration(id)\n      )\n    `;\n\n    // Demo 样式表（用于存储完整的可运行 demo）\n    const createDemoStylesTableSQL = `\n      CREATE TABLE IF NOT EXISTS demo_styles (\n        id INTEGER PRIMARY KEY,\n        inspiration_id INTEGER,\n        complete_html TEXT,\n        complete_css TEXT,\n        preview_image TEXT,\n        is_interactive BOOLEAN DEFAULT 0,\n        performance_notes TEXT,\n        FOREIGN KEY(inspiration_id) REFERENCES css_inspiration(id)\n      )\n    `;\n\n    this.db.serialize(() => {\n      this.db.run(createInspirationTableSQL)\n          .run(createCodeSnippetsTableSQL)\n          .run(createDemoStylesTableSQL);\n      \n      console.log('✅ CSS-Inspiration database tables initialized');\n    });\n  }\n\n  async fetchDirectoryContents(category) {\n    console.log(`📂 Fetching contents for category: ${category}`);\n    \n    try {\n      const response = await axios.get(`${this.baseURL}/contents/${category}`, {\n        headers: {\n          'Accept': 'application/vnd.github.v3+json',\n          'User-Agent': 'iCSS-MCP-Server',\n          ...(this.githubToken && { 'Authorization': `token ${this.githubToken}` })\n        }\n      });\n\n      const files = response.data.filter(item => \n        item.type === 'file' && item.name.endsWith('.md')\n      );\n\n      console.log(`   📝 Found ${files.length} markdown files in ${category}`);\n      return files;\n    } catch (error) {\n      console.error(`❌ Error fetching ${category} contents:`, error.message);\n      return [];\n    }\n  }\n\n  async fetchFileContent(filePath) {\n    try {\n      const response = await axios.get(`${this.rawURL}/${filePath}`, {\n        headers: {\n          'User-Agent': 'iCSS-MCP-Server',\n          ...(this.githubToken && { 'Authorization': `token ${this.githubToken}` })\n        }\n      });\n\n      return response.data;\n    } catch (error) {\n      console.error(`❌ Error fetching file ${filePath}:`, error.message);\n      return null;\n    }\n  }\n\n  parseMarkdownContent(content, category, filename) {\n    const lines = content.split('\\n');\n    let title = '';\n    let description = '';\n    let htmlContent = '';\n    let cssContent = '';\n    let currentSection = '';\n    let codeBlock = [];\n    let isInCodeBlock = false;\n    let codeBlockType = '';\n\n    for (let i = 0; i < lines.length; i++) {\n      const line = lines[i];\n\n      // 提取标题\n      if (line.startsWith('## ') && !title) {\n        title = line.replace('## ', '').trim();\n        continue;\n      }\n\n      // 检测代码块开始\n      if (line.startsWith('```')) {\n        if (!isInCodeBlock) {\n          // 开始代码块\n          isInCodeBlock = true;\n          codeBlockType = line.replace('```', '').trim() || 'text';\n          codeBlock = [];\n        } else {\n          // 结束代码块\n          isInCodeBlock = false;\n          const code = codeBlock.join('\\n');\n          \n          if (codeBlockType === 'html' || codeBlockType.includes('html')) {\n            htmlContent += code + '\\n\\n';\n          } else if (codeBlockType === 'css' || codeBlockType.includes('css')) {\n            cssContent += code + '\\n\\n';\n          }\n          \n          codeBlock = [];\n          codeBlockType = '';\n        }\n        continue;\n      }\n\n      // 收集代码块内容\n      if (isInCodeBlock) {\n        codeBlock.push(line);\n        continue;\n      }\n\n      // 收集描述信息\n      if (!isInCodeBlock && line.trim() && !line.startsWith('#')) {\n        if (!description && title) {\n          description += line + ' ';\n        }\n      }\n    }\n\n    // 如果没有找到标题，使用文件名\n    if (!title) {\n      title = filename.replace('.md', '').replace(/-/g, ' ');\n    }\n\n    // 生成标签\n    const tags = this.generateTags(title, description, cssContent, category);\n\n    // 评估难度级别\n    const difficultyLevel = this.assessDifficulty(cssContent, htmlContent);\n\n    // 评估浏览器支持\n    const browserSupport = this.assessBrowserSupport(cssContent);\n\n    return {\n      title: title.trim(),\n      description: description.trim().substring(0, 500),\n      htmlContent: htmlContent.trim(),\n      cssContent: cssContent.trim(),\n      tags: JSON.stringify(tags),\n      difficultyLevel,\n      browserSupport: JSON.stringify(browserSupport)\n    };\n  }\n\n  generateTags(title, description, cssContent, category) {\n    const tags = [this.categoryMapping[category] || category];\n    \n    // 从 CSS 内容中提取关键属性\n    const cssProperties = [\n      'transform', 'animation', 'transition', 'background', 'border',\n      'box-shadow', 'clip-path', 'mask', 'filter', 'grid', 'flex',\n      'position', 'pseudo', ':hover', ':before', ':after'\n    ];\n\n    cssProperties.forEach(prop => {\n      if (cssContent.toLowerCase().includes(prop)) {\n        tags.push(prop);\n      }\n    });\n\n    // 从标题和描述中提取关键词\n    const keywords = [\n      '动画', '效果', '布局', '渐变', '阴影', '3D', '响应式', \n      'Loading', '按钮', '卡片', '导航', '菜单'\n    ];\n\n    const text = (title + ' ' + description).toLowerCase();\n    keywords.forEach(keyword => {\n      if (text.includes(keyword.toLowerCase())) {\n        tags.push(keyword);\n      }\n    });\n\n    return [...new Set(tags)]; // 去重\n  }\n\n  assessDifficulty(cssContent, htmlContent) {\n    let complexity = 0;\n    \n    // 基于 CSS 复杂度评估\n    const advancedProperties = [\n      '@keyframes', 'transform3d', 'perspective', 'clip-path',\n      'mask', 'filter', 'mix-blend-mode', 'calc(', 'var(',\n      'custom-property', '@property'\n    ];\n\n    advancedProperties.forEach(prop => {\n      if (cssContent.includes(prop)) {\n        complexity += 2;\n      }\n    });\n\n    // 基于嵌套和选择器复杂度\n    const selectorComplexity = (cssContent.match(/::?[\\w-]+/g) || []).length;\n    complexity += Math.floor(selectorComplexity / 5);\n\n    // 基于动画复杂度\n    const animationCount = (cssContent.match(/@keyframes/g) || []).length;\n    complexity += animationCount * 3;\n\n    if (complexity < 5) return '初级';\n    if (complexity < 10) return '中级';\n    return '高级';\n  }\n\n  assessBrowserSupport(cssContent) {\n    const support = {\n      chrome: '完全支持',\n      firefox: '完全支持',\n      safari: '完全支持',\n      edge: '完全支持',\n      ie: '不支持'\n    };\n\n    // 检查新特性\n    const modernFeatures = {\n      'clip-path': { safari: '部分支持', ie: '不支持' },\n      'mask': { safari: '需要前缀', ie: '不支持' },\n      '@property': { firefox: '部分支持', safari: '部分支持', ie: '不支持' },\n      'backdrop-filter': { firefox: '部分支持', ie: '不支持' },\n      'grid': { ie: '部分支持' },\n      'custom-property': { ie: '不支持' }\n    };\n\n    Object.entries(modernFeatures).forEach(([feature, limitations]) => {\n      if (cssContent.includes(feature)) {\n        Object.assign(support, limitations);\n      }\n    });\n\n    return support;\n  }\n\n  async saveToDatabase(category, filename, parsedContent) {\n    const insertSQL = `\n      INSERT OR REPLACE INTO css_inspiration \n      (category, filename, title, description, html_content, css_content, \n       demo_url, source_url, tags, difficulty_level, browser_support, \n       search_content, updated_at)\n      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, CURRENT_TIMESTAMP)\n    `;\n\n    const sourceUrl = `https://github.com/chokcoco/CSS-Inspiration/blob/master/${category}/${filename}`;\n    const demoUrl = `https://chokcoco.github.io/CSS-Inspiration/${category}/${filename.replace('.md', '.html')}`;\n    \n    const searchContent = [\n      parsedContent.title,\n      parsedContent.description,\n      category,\n      this.categoryMapping[category] || category,\n      ...JSON.parse(parsedContent.tags)\n    ].join(' ');\n\n    return new Promise((resolve, reject) => {\n      this.db.run(\n        insertSQL,\n        [\n          category,\n          filename,\n          parsedContent.title,\n          parsedContent.description,\n          parsedContent.htmlContent,\n          parsedContent.cssContent,\n          demoUrl,\n          sourceUrl,\n          parsedContent.tags,\n          parsedContent.difficultyLevel,\n          parsedContent.browserSupport,\n          searchContent\n        ],\n        function(err) {\n          if (err) {\n            reject(err);\n          } else {\n            resolve(this.lastID);\n          }\n        }\n      );\n    });\n  }\n\n  async extractAndSaveCodeSnippets(inspirationId, htmlContent, cssContent) {\n    if (!htmlContent && !cssContent) return;\n\n    const snippets = [];\n    \n    if (htmlContent) {\n      snippets.push({\n        inspiration_id: inspirationId,\n        snippet_type: 'html',\n        code_content: htmlContent,\n        description: 'HTML 结构代码'\n      });\n    }\n\n    if (cssContent) {\n      // 分割 CSS 代码块\n      const cssBlocks = cssContent.split(/\\/\\*[\\s\\S]*?\\*\\/|\\/\\/.*$/gm)\n        .filter(block => block.trim());\n      \n      cssBlocks.forEach((block, index) => {\n        if (block.trim()) {\n          snippets.push({\n            inspiration_id: inspirationId,\n            snippet_type: 'css',\n            code_content: block.trim(),\n            description: `CSS 样式代码块 ${index + 1}`\n          });\n        }\n      });\n    }\n\n    const insertSnippetSQL = `\n      INSERT INTO code_snippets \n      (inspiration_id, snippet_type, code_content, description)\n      VALUES (?, ?, ?, ?)\n    `;\n\n    for (const snippet of snippets) {\n      await new Promise((resolve, reject) => {\n        this.db.run(\n          insertSnippetSQL,\n          [snippet.inspiration_id, snippet.snippet_type, snippet.code_content, snippet.description],\n          (err) => {\n            if (err) reject(err);\n            else resolve();\n          }\n        );\n      });\n    }\n  }\n\n  async generateCompleteDemo(inspirationId, title, htmlContent, cssContent) {\n    if (!cssContent) return;\n\n    const completeHTML = `<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>${title}</title>\n    <style>\n        * {\n            margin: 0;\n            padding: 0;\n            box-sizing: border-box;\n        }\n        \n        body {\n            font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;\n            min-height: 100vh;\n            display: flex;\n            align-items: center;\n            justify-content: center;\n            background: #f5f5f5;\n        }\n        \n        ${cssContent}\n    </style>\n</head>\n<body>\n    ${htmlContent || '<div class=\"demo-container\">CSS Demo</div>'}\n</body>\n</html>`;\n\n    const insertDemoSQL = `\n      INSERT OR REPLACE INTO demo_styles \n      (inspiration_id, complete_html, complete_css, is_interactive)\n      VALUES (?, ?, ?, ?)\n    `;\n\n    const isInteractive = cssContent.includes(':hover') || \n                          cssContent.includes('animation') || \n                          cssContent.includes('transition');\n\n    await new Promise((resolve, reject) => {\n      this.db.run(\n        insertDemoSQL,\n        [inspirationId, completeHTML, cssContent, isInteractive ? 1 : 0],\n        (err) => {\n          if (err) reject(err);\n          else resolve();\n        }\n      );\n    });\n  }\n\n  async fetchAllInspirations() {\n    console.log('🚀 Starting to fetch CSS-Inspiration repository contents...');\n    \n    try {\n      // 获取所有分类目录\n      const categories = Object.keys(this.categoryMapping);\n      let totalProcessed = 0;\n\n      for (const category of categories) {\n        console.log(`\\n📁 Processing category: ${category} (${this.categoryMapping[category]})`);\n        \n        const files = await this.fetchDirectoryContents(category);\n        \n        for (const file of files) {\n          try {\n            console.log(`   📄 Processing: ${file.name}`);\n            \n            const filePath = `${category}/${file.name}`;\n            const content = await this.fetchFileContent(filePath);\n            \n            if (content) {\n              const parsedContent = this.parseMarkdownContent(content, category, file.name);\n              \n              if (parsedContent.title) {\n                const inspirationId = await this.saveToDatabase(category, file.name, parsedContent);\n                \n                // 保存代码片段\n                await this.extractAndSaveCodeSnippets(\n                  inspirationId, \n                  parsedContent.htmlContent, \n                  parsedContent.cssContent\n                );\n                \n                // 生成完整的演示页面\n                await this.generateCompleteDemo(\n                  inspirationId,\n                  parsedContent.title,\n                  parsedContent.htmlContent,\n                  parsedContent.cssContent\n                );\n                \n                totalProcessed++;\n                console.log(`     ✅ Saved: ${parsedContent.title}`);\n              }\n            }\n            \n            // 添加延迟以避免速率限制\n            await new Promise(resolve => setTimeout(resolve, 500));\n            \n          } catch (error) {\n            console.error(`     ❌ Error processing ${file.name}:`, error.message);\n            continue;\n          }\n        }\n      }\n\n      console.log(`\\n🎉 CSS-Inspiration import completed! Processed ${totalProcessed} demos.`);\n      this.generateStats();\n      \n    } catch (error) {\n      console.error('❌ Error fetching CSS-Inspiration:', error.message);\n    }\n  }\n\n  generateStats() {\n    console.log('\\n📊 CSS-Inspiration Statistics:');\n    \n    this.db.all(`\n      SELECT \n        category, \n        COUNT(*) as count,\n        difficulty_level\n      FROM css_inspiration \n      GROUP BY category, difficulty_level\n      ORDER BY category, difficulty_level\n    `, (err, rows) => {\n      if (err) {\n        console.error('Error generating stats:', err);\n        return;\n      }\n\n      const stats = {};\n      rows.forEach(row => {\n        if (!stats[row.category]) {\n          stats[row.category] = {};\n        }\n        stats[row.category][row.difficulty_level] = row.count;\n      });\n\n      Object.entries(stats).forEach(([category, difficulties]) => {\n        const total = Object.values(difficulties).reduce((sum, count) => sum + count, 0);\n        console.log(`   ${this.categoryMapping[category] || category}: ${total} 个案例`);\n        Object.entries(difficulties).forEach(([level, count]) => {\n          console.log(`     - ${level}: ${count} 个`);\n        });\n      });\n\n      // 总体统计\n      this.db.get('SELECT COUNT(*) as total FROM css_inspiration', (err, row) => {\n        if (!err && row) {\n          console.log(`\\n   📈 总计: ${row.total} 个 CSS 演示案例`);\n        }\n        \n        console.log('\\n🎉 CSS-Inspiration 数据导入完成!');\n        process.exit(0);\n      });\n    });\n  }\n\n  close() {\n    this.db.close();\n  }\n}\n\n// 运行脚本\nconst fetcher = new InspirationFetcher();\nfetcher.fetchAllInspirations().catch(console.error);\n\n// 优雅关闭\nprocess.on('SIGINT', () => {\n  console.log('\\n⏹️ Shutting down...');\n  fetcher.close();\n  process.exit(0);\n}); "
  },
  {
    "path": "MCP/scripts/fetch-issues.js",
    "content": "import axios from 'axios';\nimport Database from 'sqlite3';\nimport fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\nimport dotenv from 'dotenv';\n\ndotenv.config();\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nclass IssuesFetcher {\n  constructor() {\n    this.baseURL = 'https://api.github.com/repos/chokcoco/iCSS/issues';\n    this.dbPath = path.join(__dirname, '../data/icss.db');\n    this.dataDir = path.join(__dirname, '../data');\n    \n    // 检查 GitHub token\n    this.githubToken = process.env.GITHUB_TOKEN;\n    if (!this.githubToken) {\n      console.warn('⚠️ No GITHUB_TOKEN found in environment variables. API rate limits will be restricted.');\n    }\n    \n    // 确保数据目录存在\n    if (!fs.existsSync(this.dataDir)) {\n      fs.mkdirSync(this.dataDir, { recursive: true });\n    }\n    \n    // 初始化数据库连接\n    this.db = new Database.Database(this.dbPath, (err) => {\n      if (err) {\n        console.error('❌ Error opening database:', err);\n        process.exit(1);\n      }\n    });\n    \n    this.labelCategories = {\n      // 核心技术类标签\n      core_tech: [\n        'CSS Layout',      // CSS 布局\n        'CSS Function',    // CSS 函数\n        'CSS-Houdini',    // CSS Houdini API\n        'CSS Variable',    // CSS 变量\n        'CSS 新特性',      // CSS 新特性\n        'Modern CSS',      // 现代 CSS\n        'CSS-doodle'      // CSS-doodle\n      ],\n\n      // 视觉效果类标签\n      visual_effects: [\n        'Background',      // 背景效果\n        'Border',         // 边框效果\n        'Shadow',         // 阴影效果\n        'Shape',          // 形状效果\n        '混合模式',        // 混合模式\n        '滤镜',           // 滤镜效果\n        'clip-path',      // 裁剪路径\n        'Mask',           // 遮罩效果\n        '3D 效果',        // 3D 效果\n        '图片效果',        // 图片处理\n        '文字效果',        // 文字效果\n        '边框效果'         // 边框特效\n      ],\n\n      // 动画类标签\n      animation: [\n        '动效',           // 动态效果\n        '动画',           // 动画效果\n        '特殊交互'        // 特殊交互效果\n      ],\n\n      // 技术实现类标签\n      implementation: [\n        'SVG',           // SVG 技术\n        '伪类',          // CSS 伪类\n        '性能',          // 性能优化\n        '技巧',          // 技术技巧\n        '奇技淫巧',       // 特殊技巧\n        '浏览器特性'      // 浏览器特性\n      ],\n\n      // 用户体验类标签\n      ux: [\n        '可访问性',       // 可访问性\n        '用户体验',       // UX\n        '设计'           // 设计相关\n      ],\n\n      // 内容类型标签\n      content_type: [\n        '翻译',          // 翻译文章\n        '面试',          // 面试相关\n        'Bug'           // 问题修复\n      ]\n    };\n\n    // 标签权重配置\n    this.labelWeights = {\n      // 核心技术标签权重较高\n      'CSS Layout': 1.5,\n      'CSS Function': 1.5,\n      'CSS-Houdini': 1.6,\n      'CSS Variable': 1.4,\n      'CSS 新特性': 1.6,\n      'Modern CSS': 1.5,\n      \n      // 视觉效果标签权重\n      'Background': 1.2,\n      'Border': 1.2,\n      'Shadow': 1.2,\n      'Shape': 1.3,\n      '混合模式': 1.4,\n      '滤镜': 1.3,\n      'clip-path': 1.4,\n      'Mask': 1.4,\n      \n      // 动画相关标签权重\n      '动效': 1.3,\n      '动画': 1.3,\n      '特殊交互': 1.4,\n      '3D 效果': 1.5,\n      \n      // 实现技巧标签权重\n      'SVG': 1.4,\n      '伪类': 1.2,\n      '性能': 1.5,\n      '技巧': 1.1,\n      '奇技淫巧': 1.3,\n      \n      // 用户体验标签权重\n      '可访问性': 1.4,\n      '用户体验': 1.3,\n      '设计': 1.2\n    };\n\n    // 标签关系映射\n    this.labelRelations = {\n      // 相关技术映射\n      'CSS Layout': ['CSS Function', 'CSS Variable'],\n      'CSS-Houdini': ['CSS 新特性', 'Modern CSS'],\n      'Background': ['混合模式', '滤镜', 'clip-path', 'Mask'],\n      'Border': ['clip-path', 'Shape', '边框效果'],\n      '动画': ['动效', '特殊交互', '3D 效果'],\n      'SVG': ['Shape', 'Mask', 'clip-path'],\n      \n      // 应用场景映射\n      '性能': ['Modern CSS', 'CSS-Houdini'],\n      '可访问性': ['用户体验', '设计'],\n      '特殊交互': ['用户体验', '动效']\n    };\n    \n    this.initializeDatabase();\n  }\n\n  initializeDatabase() {\n    // 原有的 issues 表\n    const createIssuesTableSQL = `\n      CREATE TABLE IF NOT EXISTS issues (\n        id INTEGER PRIMARY KEY,\n        number INTEGER UNIQUE,\n        title TEXT NOT NULL,\n        body TEXT,\n        html_url TEXT,\n        labels TEXT,\n        created_at TEXT,\n        updated_at TEXT,\n        search_content TEXT\n      )\n    `;\n\n    // 标签分类表\n    const createLabelCategoriesTableSQL = `\n      CREATE TABLE IF NOT EXISTS label_categories (\n        id INTEGER PRIMARY KEY,\n        label TEXT UNIQUE,\n        category TEXT,\n        description TEXT,\n        usage_count INTEGER DEFAULT 0,\n        last_used TEXT\n      )\n    `;\n\n    // 标签关系表（记录标签之间的关联）\n    const createLabelRelationsTableSQL = `\n      CREATE TABLE IF NOT EXISTS label_relations (\n        id INTEGER PRIMARY KEY,\n        label1 TEXT,\n        label2 TEXT,\n        cooccurrence_count INTEGER DEFAULT 0,\n        correlation_score REAL,\n        last_updated TEXT,\n        UNIQUE(label1, label2)\n      )\n    `;\n\n    // 文章标签映射表（用于快速查询）\n    const createArticleLabelsTableSQL = `\n      CREATE TABLE IF NOT EXISTS article_labels (\n        id INTEGER PRIMARY KEY,\n        issue_number INTEGER,\n        label TEXT,\n        category TEXT,\n        weight REAL DEFAULT 1.0,\n        FOREIGN KEY(issue_number) REFERENCES issues(number)\n      )\n    `;\n\n    // 标签层级关系表\n    const createLabelHierarchyTableSQL = `\n      CREATE TABLE IF NOT EXISTS label_hierarchy (\n        id INTEGER PRIMARY KEY,\n        parent_label TEXT,\n        child_label TEXT,\n        relation_type TEXT,\n        UNIQUE(parent_label, child_label)\n      )\n    `;\n\n    this.db.serialize(() => {\n      this.db.run(createIssuesTableSQL)\n          .run(createLabelCategoriesTableSQL)\n          .run(createLabelRelationsTableSQL)\n          .run(createArticleLabelsTableSQL)\n          .run(createLabelHierarchyTableSQL);\n      \n      // 初始化标签分类\n      this.initializeLabelCategories();\n    });\n  }\n\n  async initializeLabelCategories() {\n    // 预定义的标签分类和描述\n    const labelDescriptions = {\n      // 核心技术类\n      'CSS Layout': 'CSS 布局技术和方法',\n      'CSS Function': 'CSS 函数使用和技巧',\n      'CSS-Houdini': 'CSS Houdini API 相关技术',\n      'CSS Variable': 'CSS 变量使用和应用',\n      'CSS 新特性': '最新的 CSS 特性和用法',\n      'Modern CSS': '现代 CSS 技术和方法',\n      'CSS-doodle': 'CSS-doodle 绘图技术',\n      \n      // 视觉效果类\n      'Background': '背景相关的效果和技巧',\n      'Border': '边框相关的效果和技巧',\n      'Shadow': '阴影效果实现方法',\n      'Shape': '图形形状相关技术',\n      '混合模式': '混合模式效果实现',\n      '滤镜': '滤镜效果实现和应用',\n      'clip-path': '裁剪路径技术和应用',\n      'Mask': '遮罩效果实现方法',\n      '3D 效果': '3D 效果实现技术',\n      '图片效果': '图片处理和效果实现',\n      '文字效果': '文字特效和排版技巧',\n      '边框效果': '特殊边框效果实现',\n      \n      // 动画类\n      '动效': '动态效果实现方法',\n      '动画': '动画效果实现技术',\n      '特殊交互': '特殊交互效果实现',\n      \n      // 技术实现类\n      'SVG': 'SVG 图形和动画技术',\n      '伪类': 'CSS 伪类使用技巧',\n      '性能': 'CSS 性能优化方法',\n      '技巧': 'CSS 使用技巧和方法',\n      '奇技淫巧': '特殊效果实现技巧',\n      '浏览器特性': '浏览器特性和兼容性',\n      \n      // 用户体验类\n      '可访问性': '可访问性优化方法',\n      '用户体验': '用户体验优化技巧',\n      '设计': 'CSS 设计相关技术'\n    };\n\n    // 插入预定义的标签分类\n    const insertSQL = `\n      INSERT OR REPLACE INTO label_categories (label, category, description)\n      VALUES (?, ?, ?)\n    `;\n\n    for (const [category, labels] of Object.entries(this.labelCategories)) {\n      for (const label of labels) {\n        await new Promise((resolve, reject) => {\n          this.db.run(\n            insertSQL,\n            [label, category, labelDescriptions[label] || `${category}类标签`],\n            (err) => {\n              if (err) reject(err);\n              else resolve();\n            }\n          );\n        });\n      }\n    }\n  }\n\n  async analyzeLabelRelations(issues) {\n    console.log('📊 Analyzing label relations...');\n    \n    const labelPairs = new Map();\n    const labelCounts = new Map();\n    \n    // 收集标签共现信息\n    for (const issue of issues) {\n      try {\n        const labels = Array.isArray(issue.labels)\n          ? issue.labels.map(label => typeof label === 'string' ? label : label.name)\n          : JSON.parse(issue.labels || '[]');\n        \n        // 更新单个标签计数\n        labels.forEach(label => {\n          labelCounts.set(label, (labelCounts.get(label) || 0) + 1);\n        });\n        \n        // 更新标签对的共现计数\n        for (let i = 0; i < labels.length; i++) {\n          for (let j = i + 1; j < labels.length; j++) {\n            const pair = [labels[i], labels[j]].sort().join('->');\n            labelPairs.set(pair, (labelPairs.get(pair) || 0) + 1);\n          }\n        }\n      } catch (error) {\n        console.error(`Error processing labels for issue #${issue.number}:`, error);\n        continue;\n      }\n    }\n\n    // 计算相关性分数并更新数据库\n    const updateRelationSQL = `\n      INSERT OR REPLACE INTO label_relations \n      (label1, label2, cooccurrence_count, correlation_score, last_updated)\n      VALUES (?, ?, ?, ?, datetime('now'))\n    `;\n\n    for (const [pair, count] of labelPairs.entries()) {\n      try {\n        const [label1, label2] = pair.split('->');\n        const correlation = count / Math.sqrt(labelCounts.get(label1) * labelCounts.get(label2));\n        \n        await new Promise((resolve, reject) => {\n          this.db.run(\n            updateRelationSQL,\n            [label1, label2, count, correlation],\n            (err) => {\n              if (err) reject(err);\n              else resolve();\n            }\n          );\n        });\n      } catch (error) {\n        console.error(`Error saving relation for pair ${pair}:`, error);\n        continue;\n      }\n    }\n    \n    console.log(`✅ Analyzed ${labelPairs.size} label relationships`);\n  }\n\n  async analyzeArticleLabels(issue) {\n    try {\n      const labels = Array.isArray(issue.labels) \n        ? issue.labels.map(label => typeof label === 'string' ? label : label.name)\n        : JSON.parse(issue.labels || '[]');\n      \n      const content = issue.body || '';\n      \n      // 获取标签分类信息\n      const labelInfo = await Promise.all(\n        labels.map(label => \n          new Promise((resolve, reject) => {\n            this.db.get(\n              'SELECT category FROM label_categories WHERE label = ?',\n              [label],\n              (err, row) => {\n                if (err) reject(err);\n                else resolve({\n                  label,\n                  category: row ? row.category : 'uncategorized'\n                });\n              }\n            );\n          })\n        )\n      );\n\n      // 计算标签权重\n      const weights = this.calculateLabelWeights(labels, content);\n      \n      // 保存到数据库\n      const insertSQL = `\n        INSERT OR REPLACE INTO article_labels \n        (issue_number, label, category, weight)\n        VALUES (?, ?, ?, ?)\n      `;\n\n      for (const {label, category} of labelInfo) {\n        await new Promise((resolve, reject) => {\n          this.db.run(\n            insertSQL,\n            [issue.number, label, category, weights[label] || 1.0],\n            (err) => {\n              if (err) reject(err);\n              else resolve();\n            }\n          );\n        });\n      }\n    } catch (error) {\n      console.error(`Error analyzing labels for issue #${issue.number}:`, error);\n      // 继续处理下一个文章，而不是中断整个过程\n      return;\n    }\n  }\n\n  calculateLabelWeights(labels, content) {\n    const weights = {};\n    \n    for (const label of labels) {\n      // 基础权重\n      let weight = this.labelWeights[label] || 1.0;\n      \n      // 基于内容相关度调整权重\n      const labelRegex = new RegExp(label, 'gi');\n      const matches = content.match(labelRegex) || [];\n      weight += matches.length * 0.1;\n      \n      // 基于标签位置调整权重\n      if (content.toLowerCase().includes(label.toLowerCase())) {\n        const firstIndex = content.toLowerCase().indexOf(label.toLowerCase());\n        if (firstIndex < content.length * 0.2) {\n          weight += 0.3; // 在文章开头出现的标签更重要\n        }\n      }\n      \n      // 基于代码示例调整权重\n      const codeBlocks = content.match(/```[^`]+```/g) || [];\n      for (const block of codeBlocks) {\n        if (block.toLowerCase().includes(label.toLowerCase())) {\n          weight += 0.2; // 在代码示例中出现的标签更重要\n        }\n      }\n      \n      // 基于标签关系调整权重\n      if (this.labelRelations[label]) {\n        const relatedLabels = this.labelRelations[label];\n        const presentRelatedLabels = relatedLabels.filter(rel => \n          labels.includes(rel)\n        );\n        weight += presentRelatedLabels.length * 0.1; // 相关标签共现提升权重\n      }\n      \n      weights[label] = weight;\n    }\n    \n    return weights;\n  }\n\n  async buildLabelHierarchy() {\n    console.log('🌳 Building label hierarchy...');\n    \n    // 基于预定义的分类构建层级关系\n    for (const [category, labels] of Object.entries(this.labelCategories)) {\n      // 将分类作为父节点\n      for (const label of labels) {\n        await new Promise((resolve, reject) => {\n          this.db.run(\n            `INSERT OR REPLACE INTO label_hierarchy (parent_label, child_label, relation_type)\n             VALUES (?, ?, ?)`,\n            [category, label, 'category'],\n            (err) => {\n              if (err) reject(err);\n              else resolve();\n            }\n          );\n        });\n      }\n    }\n    \n    // 基于标签共现关系构建关联\n    const relatedLabelsSQL = `\n      SELECT label1, label2, correlation_score\n      FROM label_relations\n      WHERE correlation_score > 0.5\n      ORDER BY correlation_score DESC\n    `;\n    \n    this.db.all(relatedLabelsSQL, [], async (err, rows) => {\n      if (err) {\n        console.error('Error building label hierarchy:', err);\n        return;\n      }\n      \n      for (const row of rows) {\n        await new Promise((resolve, reject) => {\n          this.db.run(\n            `INSERT OR REPLACE INTO label_hierarchy (parent_label, child_label, relation_type)\n             VALUES (?, ?, ?)`,\n            [row.label1, row.label2, 'related'],\n            (err) => {\n              if (err) reject(err);\n              else resolve();\n            }\n          );\n        });\n      }\n    });\n  }\n\n  async saveAnalysisToDatabase(issueNumber, analysis) {\n    const insertSQL = `\n      INSERT OR REPLACE INTO article_analysis \n      (issue_number, css_properties, techniques, complexity_level, \n       browser_support, use_cases, code_snippets, demo_links, related_articles)\n      VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)\n    `;\n\n    return new Promise((resolve, reject) => {\n      this.db.run(\n        insertSQL,\n        [\n          issueNumber,\n          analysis.css_properties,\n          analysis.techniques,\n          analysis.complexity_level,\n          analysis.browser_support,\n          analysis.use_cases,\n          analysis.code_snippets,\n          analysis.demo_links,\n          analysis.related_articles\n        ],\n        function(err) {\n          if (err) reject(err);\n          else resolve();\n        }\n      );\n    });\n  }\n\n  async saveIssuesToDatabase(issues) {\n    console.log('💾 Saving and analyzing issues...');\n    \n    const insertSQL = `\n      INSERT OR REPLACE INTO issues \n      (number, title, body, html_url, labels, created_at, updated_at, search_content)\n      VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n    `;\n\n    let savedCount = 0;\n    \n    for (const issue of issues) {\n      if (issue.pull_request) continue;\n\n      try {\n        const labels = Array.isArray(issue.labels)\n          ? JSON.stringify(issue.labels.map(label => typeof label === 'string' ? label : label.name))\n          : issue.labels;\n          \n        const searchContent = this.createSearchContent(issue);\n\n        // 保存基本信息\n        await new Promise((resolve, reject) => {\n          this.db.run(\n            insertSQL,\n            [\n              issue.number,\n              issue.title,\n              issue.body || '',\n              issue.html_url,\n              labels,\n              issue.created_at,\n              issue.updated_at,\n              searchContent\n            ],\n            async function(err) {\n              if (err) {\n                console.error(`Error saving issue #${issue.number}:`, err);\n                reject(err);\n              } else {\n                try {\n                  // 分析并保存标签信息\n                  await this.analyzeArticleLabels(issue);\n                  savedCount++;\n                  resolve();\n                } catch (analysisErr) {\n                  console.error(`Error analyzing issue #${issue.number}:`, analysisErr);\n                  reject(analysisErr);\n                }\n              }\n            }.bind(this)\n          );\n        });\n\n        if (savedCount % 10 === 0) {\n          console.log(`   📝 Processed ${savedCount} articles...`);\n        }\n      } catch (error) {\n        console.error(`Error processing issue #${issue.number}:`, error);\n        continue;\n      }\n    }\n\n    // 分析标签关系\n    await this.analyzeLabelRelations(issues);\n    \n    // 构建标签层级\n    await this.buildLabelHierarchy();\n    \n    console.log(`✅ Successfully saved and analyzed ${savedCount} articles`);\n    await this.generateLabelStatistics();\n  }\n\n  async updateRelatedArticles() {\n    console.log('🔄 Updating article relations...');\n    \n    // 获取所有文章的分析数据\n    const articles = await new Promise((resolve, reject) => {\n      this.db.all(`\n        SELECT \n          i.number,\n          i.title,\n          a.css_properties,\n          a.techniques,\n          a.complexity_level\n        FROM issues i\n        JOIN article_analysis a ON i.number = a.issue_number\n      `, (err, rows) => {\n        if (err) reject(err);\n        else resolve(rows);\n      });\n    });\n\n    // 计算文章间的相关性\n    for (const article of articles) {\n      const related = articles\n        .filter(other => other.number !== article.number)\n        .map(other => {\n          const score = this.calculateRelationScore(article, other);\n          return { number: other.number, score };\n        })\n        .sort((a, b) => b.score - a.score)\n        .slice(0, 5)  // 取前5个最相关的文章\n        .map(r => r.number);\n\n      // 更新相关文章\n      await this.saveAnalysisToDatabase(article.number, {\n        ...article,\n        related_articles: JSON.stringify(related)\n      });\n    }\n  }\n\n  calculateRelationScore(article1, article2) {\n    let score = 0;\n    \n    // 比较 CSS 属性相似度\n    const props1 = new Set(JSON.parse(article1.css_properties));\n    const props2 = new Set(JSON.parse(article2.css_properties));\n    const commonProps = new Set([...props1].filter(x => props2.has(x)));\n    score += commonProps.size * 2;\n    \n    // 比较技术相似度\n    const techs1 = new Set(JSON.parse(article1.techniques));\n    const techs2 = new Set(JSON.parse(article2.techniques));\n    const commonTechs = new Set([...techs1].filter(x => techs2.has(x)));\n    score += commonTechs.size * 3;\n    \n    // 考虑难度级别\n    if (article1.complexity_level === article2.complexity_level) {\n      score += 1;\n    }\n    \n    return score;\n  }\n\n  generateEnhancedStats() {\n    console.log('\\n📊 Enhanced Database Statistics:');\n    \n    // 技术分布统计\n    this.db.all(`\n      SELECT \n        techniques,\n        COUNT(*) as count\n      FROM article_analysis\n      GROUP BY techniques\n      ORDER BY count DESC\n      LIMIT 10\n    `, (err, techRows) => {\n      if (err) {\n        console.error('Error getting technique stats:', err);\n        return;\n      }\n\n      console.log('\\n🛠️ Top Techniques Distribution:');\n      techRows.forEach((row, index) => {\n        const techs = JSON.parse(row.techniques);\n        console.log(`   ${index + 1}. ${techs.join(', ')} (${row.count} articles)`);\n      });\n\n      // 难度级别分布\n      this.db.all(`\n        SELECT \n          complexity_level,\n          COUNT(*) as count\n        FROM article_analysis\n        GROUP BY complexity_level\n        ORDER BY count DESC\n      `, (err, complexityRows) => {\n        if (err) {\n          console.error('Error getting complexity stats:', err);\n          return;\n        }\n\n        console.log('\\n📈 Complexity Level Distribution:');\n        complexityRows.forEach(row => {\n          console.log(`   ${row.complexity_level}: ${row.count} articles`);\n        });\n\n        console.log('\\n🎉 Enhanced analysis completed!');\n        process.exit(0);\n      });\n    });\n  }\n\n  async fetchAllIssues() {\n    console.log('🚀 Starting to fetch iCSS repository issues...');\n    \n    let page = 1;\n    let allIssues = [];\n    const perPage = 100; // GitHub API maximum\n\n    try {\n      while (true) {\n        console.log(`📄 Fetching page ${page}...`);\n        \n        const response = await axios.get(this.baseURL, {\n          params: {\n            state: 'all', // 获取所有状态的issues\n            per_page: perPage,\n            page: page,\n            sort: 'updated',\n            direction: 'desc'\n          },\n          headers: {\n            'Accept': 'application/vnd.github.v3+json',\n            'User-Agent': 'iCSS-MCP-Server',\n            ...(this.githubToken && { 'Authorization': `token ${this.githubToken}` })\n          }\n        });\n\n        const issues = response.data;\n        \n        if (issues.length === 0) {\n          console.log('✅ No more issues found');\n          break;\n        }\n\n        console.log(`   📝 Found ${issues.length} issues on page ${page}`);\n        allIssues = allIssues.concat(issues);\n        \n        // GitHub API rate limiting\n        if (issues.length < perPage) {\n          break;\n        }\n        \n        page++;\n        \n        // 添加延迟以避免速率限制\n        await new Promise(resolve => setTimeout(resolve, 1000));\n      }\n\n      console.log(`🎉 Total issues fetched: ${allIssues.length}`);\n      await this.saveIssuesToDatabase(allIssues);\n      \n    } catch (error) {\n      if (error.response?.status === 403) {\n        console.error('❌ GitHub API rate limit exceeded. Please set GITHUB_TOKEN environment variable.');\n        console.error('Visit https://github.com/settings/tokens to generate a token.');\n        process.exit(1);\n      }\n      console.error('❌ Error fetching issues:', error.message);\n      if (error.response) {\n        console.error('Response status:', error.response.status);\n        console.error('Response data:', error.response.data);\n      }\n    }\n  }\n\n  createSearchContent(issue) {\n    // 更好地处理标签\n    const labels = Array.isArray(issue.labels) \n      ? issue.labels\n          .map(label => typeof label === 'string' ? label : label.name)\n          .join(' ')\n      : '';\n    \n    const body = issue.body || '';\n    \n    // 移除markdown语法，提取纯文本用于搜索\n    const cleanBody = body\n      .replace(/```[\\s\\S]*?```/g, ' ') // 移除代码块\n      .replace(/`([^`]+)`/g, '$1') // 移除行内代码\n      .replace(/[#*_~]/g, '') // 移除markdown格式\n      .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1') // 移除链接，保留文本\n      .replace(/\\s+/g, ' ') // 标准化空白字符\n      .trim();\n\n    // 构建更丰富的搜索内容\n    const searchContent = [\n      issue.title,\n      labels,\n      cleanBody,\n      // 添加标签的不同组合形式，以提高匹配率\n      ...labels.split(' ').filter(Boolean),\n      // 为某些常见的组合词添加变体\n      labels.includes('奇技淫巧') ? '技巧 奇技' : '',\n      labels.includes('动画') ? '动效 特效' : '',\n      labels.includes('特殊交互') ? '交互 动效' : ''\n    ].filter(Boolean).join(' ');\n\n    return searchContent.substring(0, 1000); // 限制长度\n  }\n\n  generateStats() {\n    this.db.all('SELECT COUNT(*) as total FROM issues', (err, rows) => {\n      if (err) {\n        console.error('Error generating stats:', err);\n        return;\n      }\n\n      console.log('\\n📊 Database Statistics:');\n      console.log(`   Total articles: ${rows[0].total}`);\n\n      // 标签统计\n      this.db.all(`\n        SELECT labels, COUNT(*) as count \n        FROM issues \n        WHERE labels IS NOT NULL AND labels != '[]'\n        GROUP BY labels \n        ORDER BY count DESC \n        LIMIT 10\n      `, (err, labelRows) => {\n        if (err) {\n          console.error('Error getting label stats:', err);\n          return;\n        }\n\n        console.log('\\n🏷️ Top 10 Label Combinations:');\n        labelRows.forEach((row, index) => {\n          const labels = JSON.parse(row.labels);\n          console.log(`   ${index + 1}. ${labels.join(', ')} (${row.count} articles)`);\n        });\n\n        console.log('\\n🎉 Database update completed!');\n        process.exit(0);\n      });\n    });\n  }\n\n  close() {\n    this.db.close();\n  }\n\n  analyzeCodePatterns(content) {\n    const patterns = [];\n    const codeBlocks = content.match(/```[^`]+```/g) || [];\n    \n    codeBlocks.forEach((block, index) => {\n      const cleanCode = block.replace(/```(css|html|javascript)?\\n/g, '').replace(/```$/g, '');\n      \n      // 分析动画模式\n      if (cleanCode.includes('@keyframes') || cleanCode.includes('animation')) {\n        patterns.push({\n          pattern_name: `animation_pattern_${index}`,\n          pattern_type: 'animation',\n          code_snippet: cleanCode,\n          explanation: this.analyzeAnimationPattern(cleanCode),\n          use_cases: JSON.stringify(['transitions', 'hover effects', 'loading indicators']),\n          performance_notes: this.analyzeAnimationPerformance(cleanCode),\n          browser_support: this.getAnimationBrowserSupport(cleanCode)\n        });\n      }\n      \n      // 分析布局模式\n      if (cleanCode.includes('grid') || cleanCode.includes('flex')) {\n        patterns.push({\n          pattern_name: `layout_pattern_${index}`,\n          pattern_type: 'layout',\n          code_snippet: cleanCode,\n          explanation: this.analyzeLayoutPattern(cleanCode),\n          use_cases: JSON.stringify(['responsive layouts', 'card layouts', 'centering']),\n          performance_notes: this.analyzeLayoutPerformance(cleanCode),\n          browser_support: this.getLayoutBrowserSupport(cleanCode)\n        });\n      }\n    });\n    \n    return patterns;\n  }\n\n  analyzeAnimationPattern(code) {\n    const analysis = [];\n    \n    if (code.includes('transform')) {\n      analysis.push('使用 transform 实现动画，性能优良');\n    }\n    if (code.includes('opacity')) {\n      analysis.push('使用 opacity 实现淡入淡出效果');\n    }\n    if (code.includes('will-change')) {\n      analysis.push('使用 will-change 优化动画性能');\n    }\n    \n    return analysis.join('. ');\n  }\n\n  analyzeAnimationPerformance(code) {\n    const notes = [];\n    \n    // 检查是否使用 GPU 加速\n    if (code.includes('transform3d') || code.includes('translateZ')) {\n      notes.push('使用 3D 变换触发 GPU 加速');\n    }\n    \n    // 检查是否有性能优化\n    if (code.includes('will-change')) {\n      notes.push('使用 will-change 提示浏览器优化');\n    }\n    \n    // 检查动画属性\n    if (code.includes('left') || code.includes('top')) {\n      notes.push('警告：使用定位属性动画可能触发重排');\n    }\n    \n    return notes.join('. ');\n  }\n\n  getAnimationBrowserSupport(code) {\n    const support = {\n      chrome: '支持',\n      firefox: '支持',\n      safari: '支持',\n      edge: '支持',\n      ie: '部分支持'\n    };\n    \n    // 检查新特性支持\n    if (code.includes('@property') || code.includes('backdrop-filter')) {\n      support.safari = '部分支持';\n      support.ie = '不支持';\n    }\n    \n    // 检查动画特性\n    if (code.includes('animation') || code.includes('@keyframes')) {\n      support.ie = 'IE10+ 需要前缀';\n    }\n    \n    return JSON.stringify(support);\n  }\n\n  analyzeLayoutPattern(code) {\n    const analysis = [];\n    \n    if (code.includes('grid')) {\n      analysis.push('使用 Grid 布局实现复杂的二维布局');\n    }\n    if (code.includes('flex')) {\n      analysis.push('使用 Flexbox 实现灵活的一维布局');\n    }\n    if (code.includes('calc')) {\n      analysis.push('使用 calc() 进行动态计算');\n    }\n    \n    return analysis.join('. ');\n  }\n\n  analyzeLayoutPerformance(code) {\n    const notes = [];\n    \n    // 检查 Flexbox 性能\n    if (code.includes('flex')) {\n      notes.push('Flexbox 在大量元素时可能影响性能');\n    }\n    \n    // 检查 Grid 性能\n    if (code.includes('grid')) {\n      notes.push('Grid 布局在复杂嵌套时可能影响性能');\n    }\n    \n    // 检查动态布局\n    if (code.includes('calc')) {\n      notes.push('calc() 在频繁计算时可能影响性能');\n    }\n    \n    return notes.join('. ');\n  }\n\n  getLayoutBrowserSupport(code) {\n    const support = {\n      chrome: '完全支持',\n      firefox: '完全支持',\n      safari: '完全支持',\n      edge: '完全支持',\n      ie: '部分支持'\n    };\n    \n    // Grid 支持\n    if (code.includes('grid')) {\n      support.ie = '不支持 Grid';\n    }\n    \n    // Flexbox 支持\n    if (code.includes('flex')) {\n      support.ie = 'IE11 部分支持 Flexbox';\n    }\n    \n    return JSON.stringify(support);\n  }\n\n  analyzePerformance(content, cssProperties) {\n    const properties = JSON.parse(cssProperties);\n    \n    return {\n      gpu_accelerated: this.checkGPUAcceleration(properties),\n      paint_complexity: this.analyzePaintComplexity(properties),\n      layout_triggers: this.checkLayoutTriggers(properties),\n      memory_impact: this.analyzeMemoryImpact(properties),\n      optimization_tips: JSON.stringify(this.generateOptimizationTips(properties))\n    };\n  }\n\n  checkGPUAcceleration(properties) {\n    return properties.some(p => \n      p.includes('transform3d') || \n      p.includes('translateZ') || \n      p.includes('will-change')\n    );\n  }\n\n  analyzePaintComplexity(properties) {\n    let complexity = 'low';\n    \n    if (properties.some(p => p.includes('box-shadow') || p.includes('text-shadow'))) {\n      complexity = 'medium';\n    }\n    if (properties.some(p => p.includes('filter') || p.includes('backdrop-filter'))) {\n      complexity = 'high';\n    }\n    \n    return complexity;\n  }\n\n  checkLayoutTriggers(properties) {\n    const layoutProperties = ['width', 'height', 'padding', 'margin', 'position', 'top', 'left', 'right', 'bottom'];\n    return properties.some(p => layoutProperties.includes(p));\n  }\n\n  analyzeMemoryImpact(properties) {\n    let impact = 'low';\n    \n    if (properties.some(p => p.includes('background-image'))) {\n      impact = 'medium';\n    }\n    if (properties.some(p => p.includes('filter') || p.includes('backdrop-filter'))) {\n      impact = 'high';\n    }\n    \n    return impact;\n  }\n\n  generateOptimizationTips(properties) {\n    const tips = [];\n    \n    if (this.checkLayoutTriggers(properties)) {\n      tips.push('考虑使用 transform 替代改变位置和尺寸的属性');\n    }\n    if (properties.some(p => p.includes('box-shadow'))) {\n      tips.push('大面积阴影考虑使用 filter: drop-shadow() 优化性能');\n    }\n    if (properties.some(p => p.includes('@keyframes'))) {\n      tips.push('长动画考虑使用 requestAnimationFrame 实现');\n    }\n    \n    return tips;\n  }\n\n  analyzeCSSPropertyCategories(properties) {\n    return properties.map(prop => ({\n      property_name: prop,\n      category: this.getCSSPropertyCategory(prop),\n      sub_category: this.getCSSPropertySubCategory(prop),\n      description: this.getPropertyDescription(prop),\n      performance_impact: this.getPropertyPerformanceImpact(prop),\n      best_practices: JSON.stringify(this.getPropertyBestPractices(prop)),\n      common_pitfalls: JSON.stringify(this.getPropertyCommonPitfalls(prop)),\n      browser_notes: this.getPropertyBrowserNotes(prop)\n    }));\n  }\n\n  getCSSPropertyCategory(prop) {\n    if (prop.match(/margin|padding|width|height|position|top|left|right|bottom|float|clear|display/)) {\n      return 'layout';\n    }\n    if (prop.match(/color|background|border|box-shadow|opacity|filter/)) {\n      return 'visual';\n    }\n    if (prop.match(/animation|transition|transform/)) {\n      return 'animation';\n    }\n    if (prop.match(/font|text|line-height|letter-spacing/)) {\n      return 'typography';\n    }\n    return 'other';\n  }\n\n  getCSSPropertySubCategory(prop) {\n    if (prop.includes('flex')) return 'flexbox';\n    if (prop.includes('grid')) return 'grid';\n    if (prop.includes('animation')) return 'keyframe-animation';\n    if (prop.includes('transition')) return 'transition';\n    if (prop.includes('transform')) return 'transform';\n    return 'basic';\n  }\n\n  getPropertyDescription(prop) {\n    // 这里可以添加更多属性的描述\n    const descriptions = {\n      'flex': 'Flexbox 布局的核心属性，用于设置弹性布局',\n      'grid': 'Grid 布局的核心属性，用于设置网格布局',\n      'animation': '用于设置动画效果的属性',\n      'transform': '用于元素的 2D 或 3D 转换',\n      // ... 更多属性描述\n    };\n    return descriptions[prop] || `CSS ${prop} 属性`;\n  }\n\n  getPropertyPerformanceImpact(prop) {\n    const highImpact = ['box-shadow', 'text-shadow', 'filter', 'backdrop-filter'];\n    const mediumImpact = ['border-radius', 'opacity', 'transform'];\n    \n    if (highImpact.some(p => prop.includes(p))) return 'high';\n    if (mediumImpact.some(p => prop.includes(p))) return 'medium';\n    return 'low';\n  }\n\n  getPropertyBestPractices(prop) {\n    const practices = [];\n    \n    if (prop.includes('animation')) {\n      practices.push('使用 transform 和 opacity 实现动画');\n      practices.push('添加 will-change 提示');\n    }\n    if (prop.includes('transform')) {\n      practices.push('使用 3D transform 触发 GPU 加速');\n      practices.push('合理使用 will-change');\n    }\n    \n    return practices;\n  }\n\n  getPropertyCommonPitfalls(prop) {\n    const pitfalls = [];\n    \n    if (prop.includes('animation')) {\n      pitfalls.push('过度使用可能导致性能问题');\n      pitfalls.push('没有提供回退方案');\n    }\n    if (prop.includes('flex')) {\n      pitfalls.push('嵌套使用可能导致性能问题');\n      pitfalls.push('未考虑浏览器前缀');\n    }\n    \n    return pitfalls;\n  }\n\n  getPropertyBrowserNotes(prop) {\n    const notes = [];\n    \n    if (prop.includes('@property')) {\n      notes.push('Chrome 85+, Safari 15.4+');\n    }\n    if (prop.includes('backdrop-filter')) {\n      notes.push('需要注意浏览器兼容性');\n    }\n    \n    return notes.join('. ');\n  }\n\n  async saveCodePatterns(issueNumber, patterns) {\n    const insertSQL = `\n      INSERT OR REPLACE INTO code_patterns \n      (issue_number, pattern_name, pattern_type, code_snippet, explanation, use_cases, performance_notes, browser_support)\n      VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n    `;\n\n    for (const pattern of patterns) {\n      await new Promise((resolve, reject) => {\n        this.db.run(\n          insertSQL,\n          [\n            issueNumber,\n            pattern.pattern_name,\n            pattern.pattern_type,\n            pattern.code_snippet,\n            pattern.explanation,\n            pattern.use_cases,\n            pattern.performance_notes,\n            pattern.browser_support\n          ],\n          function(err) {\n            if (err) reject(err);\n            else resolve();\n          }\n        );\n      });\n    }\n  }\n\n  async savePropertyCategories(categories) {\n    const insertSQL = `\n      INSERT OR REPLACE INTO property_categories \n      (property_name, category, sub_category, description, performance_impact, best_practices, common_pitfalls, browser_notes)\n      VALUES (?, ?, ?, ?, ?, ?, ?, ?)\n    `;\n\n    for (const category of categories) {\n      await new Promise((resolve, reject) => {\n        this.db.run(\n          insertSQL,\n          [\n            category.property_name,\n            category.category,\n            category.sub_category,\n            category.description,\n            category.performance_impact,\n            category.best_practices,\n            category.common_pitfalls,\n            category.browser_notes\n          ],\n          function(err) {\n            if (err) reject(err);\n            else resolve();\n          }\n        );\n      });\n    }\n  }\n\n  async savePerformanceAnalysis(issueNumber, analysis) {\n    const insertSQL = `\n      INSERT OR REPLACE INTO performance_analysis \n      (issue_number, gpu_accelerated, paint_complexity, layout_triggers, memory_impact, optimization_tips)\n      VALUES (?, ?, ?, ?, ?, ?)\n    `;\n\n    await new Promise((resolve, reject) => {\n      this.db.run(\n        insertSQL,\n        [\n          issueNumber,\n          analysis.gpu_accelerated,\n          analysis.paint_complexity,\n          analysis.layout_triggers,\n          analysis.memory_impact,\n          analysis.optimization_tips\n        ],\n        function(err) {\n          if (err) reject(err);\n          else resolve();\n        }\n      );\n    });\n  }\n\n  async generateLabelStatistics() {\n    console.log('\\n📊 Label Statistics:');\n    \n    // 标签使用统计\n    this.db.all(`\n      SELECT \n        l.label,\n        l.category,\n        COUNT(al.issue_number) as usage_count,\n        AVG(al.weight) as avg_weight\n      FROM label_categories l\n      LEFT JOIN article_labels al ON l.label = al.label\n      GROUP BY l.label, l.category\n      ORDER BY usage_count DESC\n    `, [], (err, rows) => {\n      if (err) {\n        console.error('Error generating label stats:', err);\n        return;\n      }\n\n      console.log('\\n🏷️ Label Usage by Category:');\n      const byCategory = {};\n      rows.forEach(row => {\n        if (!byCategory[row.category]) {\n          byCategory[row.category] = [];\n        }\n        byCategory[row.category].push({\n          label: row.label,\n          count: row.usage_count,\n          weight: row.avg_weight\n        });\n      });\n\n      Object.entries(byCategory).forEach(([category, labels]) => {\n        console.log(`\\n${category}:`);\n        labels.forEach(({label, count, weight}) => {\n          console.log(`   ${label}: ${count} articles (avg weight: ${weight?.toFixed(2) || 0})`);\n        });\n      });\n\n      // 标签关系统计\n      this.db.all(`\n        SELECT \n          label1,\n          label2,\n          cooccurrence_count,\n          correlation_score\n        FROM label_relations\n        WHERE correlation_score > 0.3\n        ORDER BY correlation_score DESC\n        LIMIT 10\n      `, [], (err, relations) => {\n        if (err) {\n          console.error('Error getting label relations:', err);\n          return;\n        }\n\n        console.log('\\n🔗 Strong Label Correlations:');\n        relations.forEach(rel => {\n          console.log(`   ${rel.label1} <-> ${rel.label2}: ${rel.correlation_score.toFixed(2)} (${rel.cooccurrence_count} co-occurrences)`);\n        });\n\n        console.log('\\n🎉 Label analysis completed!');\n        process.exit(0);\n      });\n    });\n  }\n}\n\n// 运行脚本\nconst fetcher = new IssuesFetcher();\nfetcher.fetchAllIssues().catch(console.error);\n\n// 优雅关闭\nprocess.on('SIGINT', () => {\n  console.log('\\n⏹️ Shutting down...');\n  fetcher.close();\n  process.exit(0);\n}); "
  },
  {
    "path": "MCP/scripts/publish-check.js",
    "content": "#!/usr/bin/env node\n\nimport fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\nconst rootDir = path.resolve(__dirname, '..');\n\nconsole.log('🔍 Pre-publish check for icss-mcp-server v1.1.1\\n');\n\nlet allPassed = true;\n\nfunction checkFailed(message) {\n  console.log(`❌ ${message}`);\n  allPassed = false;\n}\n\nfunction checkPassed(message) {\n  console.log(`✅ ${message}`);\n}\n\n// 1. 检查必要文件\nconsole.log('📁 Checking required files...');\nconst requiredFiles = [\n  'package.json',\n  'server.js',\n  'setup.js',\n  'bin/icss-mcp.js',\n  'bin/install.js',\n  'scripts/fetch-issues.js',\n  'scripts/fetch-inspiration.js',\n  'test-server.js',\n  'test-inspiration.js',\n  'README.md',\n  'README.en.md',\n  'LICENSE'\n];\n\nrequiredFiles.forEach(file => {\n  const filePath = path.join(rootDir, file);\n  if (fs.existsSync(filePath)) {\n    checkPassed(`Required file exists: ${file}`);\n  } else {\n    checkFailed(`Missing required file: ${file}`);\n  }\n});\n\n// 2. 检查 package.json\nconsole.log('\\n📦 Checking package.json...');\ntry {\n  const packageJson = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8'));\n  \n  if (packageJson.name === 'icss-mcp-server') {\n    checkPassed('Package name is correct');\n  } else {\n    checkFailed(`Package name should be icss-mcp-server, got: ${packageJson.name}`);\n  }\n  \n  if (packageJson.version === '1.1.1') {\n    checkPassed(`Version is updated to v1.1.1`);\n  } else {\n    checkFailed(`Version should be 1.1.1, got: ${packageJson.version}`);\n  }\n  \n  if (packageJson.bin && packageJson.bin['icss-mcp'] && packageJson.bin['icss-mcp-install']) {\n    checkPassed('CLI commands are defined');\n  } else {\n    checkFailed('CLI commands are missing in package.json');\n  }\n  \n  if (packageJson.files && packageJson.files.length > 0) {\n    checkPassed('Files array is defined');\n  } else {\n    checkFailed('Files array is missing or empty');\n  }\n\n  // 检查新的脚本命令\n  const requiredScripts = ['build:inspiration', 'build:all', 'test:inspiration', 'test:all'];\n  requiredScripts.forEach(script => {\n    if (packageJson.scripts && packageJson.scripts[script]) {\n      checkPassed(`Script '${script}' is defined`);\n    } else {\n      checkFailed(`Missing script: ${script}`);\n    }\n  });\n  \n} catch (error) {\n  checkFailed(`Failed to parse package.json: ${error.message}`);\n}\n\n// 3. 检查 CLI 脚本权限\nconsole.log('\\n🔧 Checking CLI scripts...');\nconst cliScripts = ['bin/icss-mcp.js', 'bin/install.js'];\ncliScripts.forEach(script => {\n  const scriptPath = path.join(rootDir, script);\n  try {\n    const stats = fs.statSync(scriptPath);\n    if (stats.mode & parseInt('111', 8)) {\n      checkPassed(`${script} is executable`);\n    } else {\n      checkFailed(`${script} is not executable`);\n    }\n    \n    const content = fs.readFileSync(scriptPath, 'utf8');\n    if (content.startsWith('#!/usr/bin/env node')) {\n      checkPassed(`${script} has correct shebang`);\n    } else {\n      checkFailed(`${script} is missing shebang`);\n    }\n\n    // 检查版本信息\n    if (content.includes('v1.1.1')) {\n      checkPassed(`${script} includes version v1.1.1`);\n    } else {\n      checkFailed(`${script} missing version v1.1.1`);\n    }\n  } catch (error) {\n    checkFailed(`Failed to check ${script}: ${error.message}`);\n  }\n});\n\n// 4. 检查依赖\nconsole.log('\\n📚 Checking dependencies...');\ntry {\n  const packageJson = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8'));\n  const requiredDeps = [\n    '@modelcontextprotocol/sdk',\n    'sqlite3',\n    'fuse.js',\n    'axios',\n    'marked',\n    'dotenv',\n    'cheerio'\n  ];\n  \n  requiredDeps.forEach(dep => {\n    if (packageJson.dependencies && packageJson.dependencies[dep]) {\n      checkPassed(`Dependency ${dep} is included`);\n    } else {\n      checkFailed(`Missing dependency: ${dep}`);\n    }\n  });\n} catch (error) {\n  checkFailed(`Failed to check dependencies: ${error.message}`);\n}\n\n// 5. 检查数据库\nconsole.log('\\n🗄️ Checking database...');\nconst dbPath = path.join(rootDir, 'data', 'icss.db');\nif (fs.existsSync(dbPath)) {\n  try {\n    const stats = fs.statSync(dbPath);\n    if (stats.size > 1024) { // At least 1KB\n      checkPassed(`Database file exists and has content (${Math.round(stats.size / 1024)}KB)`);\n    } else {\n      checkFailed('Database file is too small (might be empty)');\n    }\n  } catch (error) {\n    checkFailed(`Failed to check database: ${error.message}`);\n  }\n} else {\n  console.log('⚠️  Database file not found (will be created on first run)');\n}\n\n// 6. 检查新增功能文件\nconsole.log('\\n🎨 Checking CSS-Inspiration integration...');\nconst inspirationFiles = [\n  'scripts/fetch-inspiration.js',\n  'test-inspiration.js'\n];\n\ninspirationFiles.forEach(file => {\n  const filePath = path.join(rootDir, file);\n  if (fs.existsSync(filePath)) {\n    checkPassed(`CSS-Inspiration file exists: ${file}`);\n    \n    // 检查文件内容\n    const content = fs.readFileSync(filePath, 'utf8');\n    if (content.includes('css_inspiration') || content.includes('CSS-Inspiration')) {\n      checkPassed(`${file} contains CSS-Inspiration logic`);\n    } else {\n      checkFailed(`${file} missing CSS-Inspiration references`);\n    }\n  } else {\n    checkFailed(`Missing CSS-Inspiration file: ${file}`);\n  }\n});\n\n// 7. 检查 README\nconsole.log('\\n📖 Checking README files...');\nconst readmeFiles = ['README.md', 'README.en.md'];\nreadmeFiles.forEach(readmeFile => {\n  try {\n    const readme = fs.readFileSync(path.join(rootDir, readmeFile), 'utf8');\n    \n    if (readme.includes('# iCSS MCP Server')) {\n      checkPassed(`${readmeFile} has correct title`);\n    } else {\n      checkFailed(`${readmeFile} title is incorrect`);\n    }\n    \n    if (readme.includes('CSS-Inspiration')) {\n      checkPassed(`${readmeFile} mentions CSS-Inspiration integration`);\n    } else {\n      checkFailed(`${readmeFile} missing CSS-Inspiration references`);\n    }\n    \n    if (readme.includes('npm install -g icss-mcp-server')) {\n      checkPassed(`${readmeFile} includes installation instructions`);\n    } else {\n      checkFailed(`${readmeFile} missing installation instructions`);\n    }\n    \n    if (readme.length > 1000) {\n      checkPassed(`${readmeFile} has sufficient content`);\n    } else {\n      checkFailed(`${readmeFile} is too short`);\n    }\n    \n  } catch (error) {\n    checkFailed(`Failed to check ${readmeFile}: ${error.message}`);\n  }\n});\n\n// 8. 检查 LICENSE\nconsole.log('\\n⚖️  Checking LICENSE...');\ntry {\n  const license = fs.readFileSync(path.join(rootDir, 'LICENSE'), 'utf8');\n  \n  if (license.includes('MIT License')) {\n    checkPassed('LICENSE is MIT');\n  } else {\n    checkFailed('LICENSE is not MIT or incorrectly formatted');\n  }\n} catch (error) {\n  checkFailed(`Failed to check LICENSE: ${error.message}`);\n}\n\n// 9. 检查服务器版本\nconsole.log('\\n🖥️  Checking server version...');\ntry {\n  const serverContent = fs.readFileSync(path.join(rootDir, 'server.js'), 'utf8');\n  if (serverContent.includes('1.1.1')) {\n    checkPassed('Server.js includes version 1.1.1');\n  } else {\n    checkFailed('Server.js missing version 1.1.1');\n  }\n\n  // 检查新的 MCP 功能\n  const newFunctions = ['search_css_demos', 'get_css_demo'];\n  newFunctions.forEach(func => {\n    if (serverContent.includes(func)) {\n      checkPassed(`Server includes new function: ${func}`);\n    } else {\n      checkFailed(`Server missing new function: ${func}`);\n    }\n  });\n} catch (error) {\n  checkFailed(`Failed to check server.js: ${error.message}`);\n}\n\n// 总结\nconsole.log('\\n' + '='.repeat(50));\nif (allPassed) {\n  console.log('🎉 All checks passed! Ready to publish v1.1.1.');\n  console.log('\\n📋 New features in v1.1.1:');\n  console.log('   • Integrated CSS-Inspiration with 160+ demos');\n  console.log('   • Added search_css_demos and get_css_demo functions');\n  console.log('   • Enhanced CLI with better error handling');\n  console.log('   • Updated documentation and examples');\n  console.log('\\n📋 To publish:');\n  console.log('   npm publish');\n  console.log('\\n📋 After publishing:');\n  console.log('   npm install -g icss-mcp-server');\n  console.log('   icss-mcp-install');\n  process.exit(0);\n} else {\n  console.log('❌ Some checks failed. Please fix the issues before publishing.');\n  process.exit(1);\n} "
  },
  {
    "path": "MCP/server.js",
    "content": "#!/usr/bin/env node\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n  CallToolRequestSchema,\n  ErrorCode,\n  ListToolsRequestSchema,\n  McpError,\n} from '@modelcontextprotocol/sdk/types.js';\nimport Database from 'sqlite3';\nimport Fuse from 'fuse.js';\nimport { marked } from 'marked';\nimport dotenv from 'dotenv';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\ndotenv.config();\n\n// 调试模式检测\nconst isDebugMode = process.env.NODE_ENV === 'development' || process.env.DEBUG;\nconst debugLog = (...args) => {\n  if (isDebugMode) {\n    console.error(`[DEBUG ${new Date().toISOString()}]`, ...args);\n  }\n};\n\nclass IcssServer {\n  constructor() {\n    debugLog('🚀 Initializing iCSS MCP Server in debug mode');\n    \n    this.server = new Server(\n      {\n        name: 'icss-mcp-server',\n        version: '1.1.1',\n      },\n      {\n        capabilities: {\n          tools: {},\n        },\n      }\n    );\n\n    this.db = null;\n    this.searchEngine = null;\n    this.isReady = false;\n    this.dbInitFailed = false;\n    this.setupDatabase();\n    this.setupHandlers();\n  }\n\n  setupDatabase() {\n    debugLog('📂 Setting up database connection...');\n    // 更健壮的数据库路径\n    const __filename = fileURLToPath(import.meta.url);\n    const __dirname = path.dirname(__filename);\n    const dbPath = path.join(__dirname, 'data', 'icss.db');\n    debugLog(`📍 Database path: ${dbPath}`);\n    this.db = new Database.Database(dbPath, (err) => {\n      if (err) {\n        console.error('❌ Error opening database:', err);\n        this.dbInitFailed = true;\n      } else {\n        debugLog('✅ Connected to SQLite database');\n        this.initializeDatabase();\n      }\n    });\n  }\n\n  initializeDatabase() {\n    debugLog('🔧 Initializing database tables...');\n    \n    const createTableSQL = `\n      CREATE TABLE IF NOT EXISTS issues (\n        id INTEGER PRIMARY KEY,\n        number INTEGER UNIQUE,\n        title TEXT NOT NULL,\n        body TEXT,\n        html_url TEXT,\n        labels TEXT,\n        created_at TEXT,\n        updated_at TEXT,\n        search_content TEXT\n      )\n    `;\n\n    this.db.run(createTableSQL, (err) => {\n      if (err) {\n        console.error('❌ Error creating table:', err);\n        this.dbInitFailed = true;\n      } else {\n        debugLog('✅ Database table initialized');\n        this.loadSearchIndex();\n      }\n    });\n  }\n\n  loadSearchIndex() {\n    debugLog('🔍 Loading search index...');\n    \n    this.db.all('SELECT * FROM issues', (err, rows) => {\n      if (err) {\n        console.error('❌ Error loading search index:', err);\n        this.dbInitFailed = true;\n        return;\n      }\n\n      debugLog(`📊 Loaded ${rows.length} articles for search index`);\n\n      const fuseOptions = {\n        keys: [\n          { name: 'title', weight: 0.4 },\n          { name: 'search_content', weight: 0.4 },\n          { name: 'labels', weight: 0.2 }\n        ],\n        threshold: 0.6,  // 提高阈值以允许更多匹配\n        includeScore: true,\n        includeMatches: true,\n        useExtendedSearch: true,  // 启用扩展搜索\n        ignoreLocation: true,     // 忽略位置影响\n        findAllMatches: true,     // 查找所有匹配项\n        minMatchCharLength: 1     // 最小匹配字符长度，有助于中文匹配\n      };\n\n      this.searchEngine = new Fuse(rows, fuseOptions);\n      this.isReady = true;\n      debugLog(`🎉 Search index loaded with ${rows.length} articles - Server ready!`);\n      console.error(`✅ iCSS MCP Server ready! (Debug mode: ${isDebugMode})`);\n    });\n  }\n\n  async waitForReady(timeout = 10000) {\n    debugLog(`⏳ Waiting for server to be ready (timeout: ${timeout}ms)`);\n    const startTime = Date.now();\n    \n    while (!this.isReady && !this.dbInitFailed) {\n      if (Date.now() - startTime > timeout) {\n        debugLog('⚠️ Server initialization timeout');\n        throw new McpError(\n          ErrorCode.InternalError,\n          'Server initialization timeout'\n        );\n      }\n      await new Promise(resolve => setTimeout(resolve, 100));\n    }\n    \n    if (this.dbInitFailed) {\n      debugLog('❌ Server initialization failed');\n      throw new McpError(\n        ErrorCode.InternalError,\n        'Server initialization failed'\n      );\n    }\n    \n    debugLog('✅ Server is ready');\n  }\n\n  setupHandlers() {\n    debugLog('🔗 Setting up request handlers...');\n    \n    this.server.setRequestHandler(ListToolsRequestSchema, async () => {\n      try {\n        debugLog('📋 Received ListTools request');\n        await this.waitForReady();\n        \n        const tools = [\n          {\n            name: 'search_css_techniques',\n            description: 'Search for CSS techniques and solutions from iCSS repository issues',\n            inputSchema: {\n              type: 'object',\n              properties: {\n                query: {\n                  type: 'string',\n                  description: 'Search query for CSS techniques (e.g., \"flex layout\", \"animation\", \"grid\")'\n                },\n                limit: {\n                  type: 'number',\n                  description: 'Maximum number of results to return (default: 5)',\n                  default: 5\n                }\n              },\n              required: ['query']\n            }\n          },\n          {\n            name: 'search_css_demos',\n            description: 'Search for CSS demo examples from CSS-Inspiration repository with complete code',\n            inputSchema: {\n              type: 'object',\n              properties: {\n                query: {\n                  type: 'string',\n                  description: 'Search query for CSS demos (e.g., \"animation\", \"3d effect\", \"layout\")'\n                },\n                category: {\n                  type: 'string',\n                  description: 'Filter by category (e.g., \"animation\", \"3d\", \"layout\", \"background\")'\n                },\n                difficulty: {\n                  type: 'string',\n                  description: 'Filter by difficulty level (初级, 中级, 高级)'\n                },\n                limit: {\n                  type: 'number',\n                  description: 'Maximum number of results to return (default: 5)',\n                  default: 5\n                }\n              },\n              required: ['query']\n            }\n          },\n          {\n            name: 'get_css_demo',\n            description: 'Get complete demo with HTML and CSS code for a specific CSS-Inspiration example',\n            inputSchema: {\n              type: 'object',\n              properties: {\n                demo_id: {\n                  type: 'number',\n                  description: 'CSS-Inspiration demo ID'\n                }\n              },\n              required: ['demo_id']\n            }\n          },\n          {\n            name: 'get_css_article',\n            description: 'Get detailed content of a specific CSS article by issue number',\n            inputSchema: {\n              type: 'object',\n              properties: {\n                issue_number: {\n                  type: 'number',\n                  description: 'GitHub issue number of the article'\n                }\n              },\n              required: ['issue_number']\n            }\n          },\n          {\n            name: 'list_css_categories',\n            description: 'List available CSS technique categories from both iCSS and CSS-Inspiration',\n            inputSchema: {\n              type: 'object',\n              properties: {\n                source: {\n                  type: 'string',\n                  description: 'Filter by source: \"icss\", \"inspiration\", or \"all\" (default: all)'\n                }\n              }\n            }\n          },\n          {\n            name: 'get_random_css_tip',\n            description: 'Get a random CSS technique or tip from the collection',\n            inputSchema: {\n              type: 'object',\n              properties: {\n                source: {\n                  type: 'string',\n                  description: 'Source preference: \"icss\", \"inspiration\", or \"both\" (default: both)'\n                }\n              }\n            }\n          }\n        ];\n        \n        debugLog(`📋 Returning ${tools.length} tools`);\n        return { tools };\n      } catch (error) {\n        debugLog('❌ Error in ListTools:', error);\n        throw error;\n      }\n    });\n\n    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {\n      const { name, arguments: args } = request.params;\n      debugLog(`🔧 Received CallTool request: ${name}`, args);\n\n      try {\n        await this.waitForReady();\n\n        let result;\n        switch (name) {\n          case 'search_css_techniques':\n            debugLog(`🔍 Executing search: \"${args.query}\" (limit: ${args.limit || 5})`);\n            result = await this.searchCssTechniques(args.query, args.limit || 5);\n            return result;\n          \n          case 'search_css_demos':\n            debugLog(`🎨 Searching CSS demos: \"${args.query}\" (category: ${args.category || 'all'}, difficulty: ${args.difficulty || 'all'})`);\n            result = await this.searchCssDemos(args.query, args.category, args.difficulty, args.limit || 5);\n            return result;\n          \n          case 'get_css_demo':\n            debugLog(`🎯 Fetching CSS demo #${args.demo_id}`);\n            result = await this.getCssDemo(args.demo_id);\n            return result;\n          \n          case 'get_css_article':\n            debugLog(`📖 Fetching article #${args.issue_number}`);\n            result = await this.getCssArticle(args.issue_number);\n            return result;\n          \n          case 'list_css_categories':\n            debugLog(`🏷️ Listing CSS categories (source: ${args.source || 'all'})`);\n            result = await this.listCssCategories(args.source);\n            return result;\n          \n          case 'get_random_css_tip':\n            debugLog(`🎲 Getting random CSS tip (source: ${args.source || 'both'})`);\n            result = await this.getRandomCssTip(args.source);\n            return result;\n          \n          default:\n            throw new McpError(\n              ErrorCode.MethodNotFound,\n              `Unknown tool: ${name}`\n            );\n        }\n      } catch (error) {\n        debugLog(`❌ Error in ${name}:`, error);\n        throw error;\n      }\n    });\n  }\n\n  async searchCssTechniques(query, limit = 5) {\n    debugLog(`[DEBUG] searchCssTechniques called with query: \"${query}\", limit: ${limit}`);\n    debugLog(`[DEBUG] Search engine ready: ${this.isReady}`);\n    \n    return new Promise((resolve, reject) => {\n      const startTime = Date.now();\n      debugLog(`[DEBUG] Starting search at ${new Date().toISOString()}`);\n      \n      try {\n        // 处理中文搜索词\n        const searchTerms = query.split(/\\s+/).filter(Boolean);\n        let results = [];\n        \n        // 对每个搜索词分别进行搜索\n        searchTerms.forEach(term => {\n          const termResults = this.searchEngine.search(term);\n          results = results.concat(termResults);\n        });\n        \n        // 去重并按相关度排序\n        results = Array.from(new Set(results.map(r => r.item.number)))\n          .map(number => results.find(r => r.item.number === number))\n          .sort((a, b) => a.score - b.score)\n          .slice(0, limit);\n\n        const endTime = Date.now();\n        debugLog(`[DEBUG] Search completed in ${endTime - startTime}ms, found ${results.length} results`);\n      \n        const formattedResults = results.map(result => {\n          const item = result.item;\n          const matches = result.matches?.map(match => ({\n            key: match.key,\n            value: match.value.substring(0, 100) + '...'\n          })) || [];\n\n          return {\n            title: item.title,\n            issue_number: item.number,\n            url: item.html_url,\n            labels: item.labels ? JSON.parse(item.labels) : [],\n            score: Math.round((1 - result.score) * 100),\n            preview: this.extractPreview(item.body),\n            matches: matches\n          };\n        });\n\n        debugLog(`[DEBUG] Formatted ${formattedResults.length} results, returning response`);\n\n        resolve({\n          content: [\n            {\n              type: 'text',\n              text: `Found ${formattedResults.length} CSS techniques for \"${query}\":\\n\\n` +\n                    formattedResults.map((result, index) => \n                      `${index + 1}. **${result.title}** (Issue #${result.issue_number})\\n` +\n                      `   💯 Relevance: ${result.score}%\\n` +\n                      `   🏷️ Tags: ${result.labels.join(', ')}\\n` +\n                      `   📝 Preview: ${result.preview}\\n` +\n                      `   🔗 Link: ${result.url}\\n`\n                    ).join('\\n')\n            }\n          ]\n        });\n      } catch (error) {\n        debugLog(`[DEBUG] Search failed with error:`, error);\n        reject(error);\n      }\n    });\n  }\n\n  async getCssArticle(issueNumber) {\n    debugLog(`📖 Getting article #${issueNumber}`);\n    \n    return new Promise((resolve, reject) => {\n      this.db.get(\n        'SELECT * FROM issues WHERE number = ?',\n        [issueNumber],\n        (err, row) => {\n          if (err) {\n            debugLog(`❌ Database error for article #${issueNumber}:`, err);\n            reject(err);\n            return;\n          }\n\n          if (!row) {\n            debugLog(`❌ Article #${issueNumber} not found`);\n            reject(new Error(`Article with issue number ${issueNumber} not found`));\n            return;\n          }\n\n          debugLog(`✅ Found article #${issueNumber}: \"${row.title}\"`);\n\n          const htmlContent = marked(row.body || '');\n          const labels = row.labels ? JSON.parse(row.labels) : [];\n\n          resolve({\n            content: [\n              {\n                type: 'text',\n                text: `# ${row.title}\\n\\n` +\n                      `**Issue #${row.number}** | **Updated:** ${new Date(row.updated_at).toLocaleDateString()}\\n\\n` +\n                      `**Tags:** ${labels.join(', ')}\\n\\n` +\n                      `**GitHub Link:** ${row.html_url}\\n\\n` +\n                      `---\\n\\n${row.body}`\n              }\n            ]\n          });\n        }\n      );\n    });\n  }\n\n  async searchCssDemos(query, category, difficulty, limit = 5) {\n    debugLog(`🎨 Searching CSS demos with query: \"${query}\"`);\n    \n    return new Promise((resolve, reject) => {\n      let sql = `\n        SELECT * FROM css_inspiration \n        WHERE search_content LIKE ?\n      `;\n      let params = [`%${query}%`];\n\n      if (category) {\n        sql += ` AND category = ?`;\n        params.push(category);\n      }\n\n      if (difficulty) {\n        sql += ` AND difficulty_level = ?`;\n        params.push(difficulty);\n      }\n\n      sql += ` ORDER BY title LIMIT ?`;\n      params.push(limit);\n\n      this.db.all(sql, params, (err, rows) => {\n        if (err) {\n          debugLog('❌ Error searching CSS demos:', err);\n          reject(err);\n          return;\n        }\n\n        debugLog(`✅ Found ${rows.length} CSS demos`);\n\n        const formattedResults = rows.map(row => {\n          const tags = JSON.parse(row.tags || '[]');\n          const browserSupport = JSON.parse(row.browser_support || '{}');\n          \n          return {\n            id: row.id,\n            title: row.title,\n            category: row.category,\n            difficulty: row.difficulty_level,\n            description: row.description,\n            tags: tags,\n            demo_url: row.demo_url,\n            source_url: row.source_url,\n            browser_support: browserSupport\n          };\n        });\n\n        resolve({\n          content: [\n            {\n              type: 'text',\n              text: `Found ${formattedResults.length} CSS demos for \"${query}\":\\n\\n` +\n                    formattedResults.map((demo, index) => \n                      `${index + 1}. **${demo.title}** (ID: ${demo.id})\\n` +\n                      `   🏷️ Category: ${demo.category} | Difficulty: ${demo.difficulty}\\n` +\n                      `   📝 ${demo.description}\\n` +\n                      `   🏆 Tags: ${demo.tags.join(', ')}\\n` +\n                      `   🔗 Demo: ${demo.demo_url}\\n` +\n                      `   📁 Source: ${demo.source_url}\\n`\n                    ).join('\\n')\n            }\n          ]\n        });\n      });\n    });\n  }\n\n  async getCssDemo(demoId) {\n    debugLog(`🎯 Getting CSS demo #${demoId}`);\n    \n    return new Promise((resolve, reject) => {\n      // 获取基本信息\n      this.db.get(\n        'SELECT * FROM css_inspiration WHERE id = ?',\n        [demoId],\n        (err, row) => {\n          if (err) {\n            debugLog(`❌ Database error for demo #${demoId}:`, err);\n            reject(err);\n            return;\n          }\n\n          if (!row) {\n            debugLog(`❌ Demo #${demoId} not found`);\n            reject(new Error(`CSS demo with ID ${demoId} not found`));\n            return;\n          }\n\n          // 获取完整的演示代码\n          this.db.get(\n            'SELECT * FROM demo_styles WHERE inspiration_id = ?',\n            [demoId],\n            (err, demoRow) => {\n              if (err) {\n                debugLog(`❌ Error getting demo styles for #${demoId}:`, err);\n                reject(err);\n                return;\n              }\n\n              debugLog(`✅ Found demo #${demoId}: \"${row.title}\"`);\n\n              const tags = JSON.parse(row.tags || '[]');\n              const browserSupport = JSON.parse(row.browser_support || '{}');\n\n              resolve({\n                content: [\n                  {\n                    type: 'text',\n                    text: `# ${row.title}\\n\\n` +\n                          `**Category:** ${row.category} | **Difficulty:** ${row.difficulty_level}\\n\\n` +\n                          `**Description:** ${row.description}\\n\\n` +\n                          `**Tags:** ${tags.join(', ')}\\n\\n` +\n                          `**Browser Support:**\\n` +\n                          Object.entries(browserSupport).map(([browser, support]) => \n                            `- ${browser}: ${support}`\n                          ).join('\\n') + '\\n\\n' +\n                          `## HTML Code\\n\\n\\`\\`\\`html\\n${row.html_content || '<div class=\"demo\">Demo</div>'}\\n\\`\\`\\`\\n\\n` +\n                          `## CSS Code\\n\\n\\`\\`\\`css\\n${row.css_content}\\n\\`\\`\\`\\n\\n` +\n                          (demoRow ? `## Complete Demo\\n\\n\\`\\`\\`html\\n${demoRow.complete_html}\\n\\`\\`\\`\\n\\n` : '') +\n                          `**Demo URL:** ${row.demo_url}\\n` +\n                          `**Source:** ${row.source_url}`\n                  }\n                ]\n              });\n            }\n          );\n        }\n      );\n    });\n  }\n\n  async listCssCategories(source = 'all') {\n    debugLog(`🏷️ Listing CSS categories (source: ${source})`);\n    \n    return new Promise((resolve, reject) => {\n      let promises = [];\n\n      // iCSS categories\n      if (source === 'all' || source === 'icss') {\n        promises.push(\n          new Promise((resolve, reject) => {\n            this.db.all(\n              'SELECT labels, COUNT(*) as count FROM issues WHERE labels IS NOT NULL GROUP BY labels',\n              (err, rows) => {\n                if (err) {\n                  reject(err);\n                  return;\n                }\n\n                const categoryMap = new Map();\n                rows.forEach(row => {\n                  if (row.labels) {\n                    const labels = JSON.parse(row.labels);\n                    labels.forEach(label => {\n                      categoryMap.set(label, (categoryMap.get(label) || 0) + row.count);\n                    });\n                  }\n                });\n\n                const categories = Array.from(categoryMap.entries())\n                  .sort((a, b) => b[1] - a[1])\n                  .map(([label, count]) => ({ \n                    label, \n                    count, \n                    source: 'iCSS',\n                    type: 'article'\n                  }));\n\n                resolve(categories);\n              }\n            );\n          })\n        );\n      }\n\n      // CSS-Inspiration categories\n      if (source === 'all' || source === 'inspiration') {\n        promises.push(\n          new Promise((resolve, reject) => {\n            this.db.all(\n              'SELECT category, COUNT(*) as count FROM css_inspiration GROUP BY category ORDER BY count DESC',\n              (err, rows) => {\n                if (err) {\n                  reject(err);\n                  return;\n                }\n\n                const categories = rows.map(row => ({\n                  label: row.category,\n                  count: row.count,\n                  source: 'CSS-Inspiration',\n                  type: 'demo'\n                }));\n\n                resolve(categories);\n              }\n            );\n          })\n        );\n      }\n\n      Promise.all(promises)\n        .then(results => {\n          const allCategories = results.flat();\n          debugLog(`✅ Found ${allCategories.length} categories`);\n\n          let output = `Available CSS technique categories:\\n\\n`;\n\n          if (source === 'all') {\n            // 按来源分组显示\n            const iCSSCategories = allCategories.filter(cat => cat.source === 'iCSS');\n            const inspirationCategories = allCategories.filter(cat => cat.source === 'CSS-Inspiration');\n\n            if (iCSSCategories.length > 0) {\n              output += `## iCSS Articles (${iCSSCategories.length} categories)\\n`;\n              output += iCSSCategories.map(cat => \n                `• **${cat.label}** (${cat.count} articles)`\n              ).join('\\n') + '\\n\\n';\n            }\n\n            if (inspirationCategories.length > 0) {\n              output += `## CSS-Inspiration Demos (${inspirationCategories.length} categories)\\n`;\n              output += inspirationCategories.map(cat => \n                `• **${cat.label}** (${cat.count} demos)`\n              ).join('\\n');\n            }\n          } else {\n            output += allCategories.map(cat => \n              `• **${cat.label}** (${cat.count} ${cat.type}s) - ${cat.source}`\n            ).join('\\n');\n          }\n\n          resolve({\n            content: [\n              {\n                type: 'text',\n                text: output\n              }\n            ]\n          });\n        })\n        .catch(reject);\n    });\n  }\n\n  async getRandomCssTip(source = 'both') {\n    debugLog(`🎲 Getting random CSS tip (source: ${source})`);\n    \n    return new Promise((resolve, reject) => {\n      let queries = [];\n\n      if (source === 'both' || source === 'icss') {\n        queries.push({\n          sql: 'SELECT *, \"icss\" as source_type FROM issues ORDER BY RANDOM() LIMIT 1',\n          params: []\n        });\n      }\n\n      if (source === 'both' || source === 'inspiration') {\n        queries.push({\n          sql: 'SELECT *, \"inspiration\" as source_type FROM css_inspiration ORDER BY RANDOM() LIMIT 1',\n          params: []\n        });\n      }\n\n      // 随机选择一个查询\n      const randomQuery = queries[Math.floor(Math.random() * queries.length)];\n\n      this.db.get(randomQuery.sql, randomQuery.params, (err, row) => {\n        if (err) {\n          debugLog('❌ Error getting random tip:', err);\n          reject(err);\n          return;\n        }\n\n        if (!row) {\n          reject(new Error('No content available'));\n          return;\n        }\n\n        if (row.source_type === 'icss') {\n          debugLog(`✅ Random iCSS tip: \"${row.title}\" (Issue #${row.number})`);\n\n          const labels = row.labels ? JSON.parse(row.labels) : [];\n          const preview = this.extractPreview(row.body);\n\n          resolve({\n            content: [\n              {\n                type: 'text',\n                text: `🎲 **Random CSS Tip** (from iCSS): ${row.title}\\n\\n` +\n                      `**Issue #${row.number}** | **Tags:** ${labels.join(', ')}\\n\\n` +\n                      `${preview}\\n\\n` +\n                      `[Read full article](${row.html_url})`\n              }\n            ]\n          });\n        } else {\n          debugLog(`✅ Random CSS-Inspiration demo: \"${row.title}\" (ID #${row.id})`);\n\n          const tags = row.tags ? JSON.parse(row.tags) : [];\n          \n          resolve({\n            content: [\n              {\n                type: 'text',\n                text: `🎲 **Random CSS Demo** (from CSS-Inspiration): ${row.title}\\n\\n` +\n                      `**Demo ID #${row.id}** | **Category:** ${row.category} | **Difficulty:** ${row.difficulty_level}\\n\\n` +\n                      `**Tags:** ${tags.join(', ')}\\n\\n` +\n                      `${row.description}\\n\\n` +\n                      `**Demo:** ${row.demo_url}\\n` +\n                      `**Source:** ${row.source_url}\\n\\n` +\n                      `💡 Use \\`get_css_demo\\` with ID ${row.id} to see the complete code!`\n              }\n            ]\n          });\n        }\n      });\n    });\n  }\n\n  extractPreview(body, maxLength = 200) {\n    if (!body) return 'No preview available';\n    \n    // Remove markdown formatting for preview\n    const cleanText = body\n      .replace(/```[\\s\\S]*?```/g, '[code block]')\n      .replace(/`([^`]+)`/g, '$1')\n      .replace(/[#*_~]/g, '')\n      .replace(/\\[([^\\]]+)\\]\\([^)]+\\)/g, '$1')\n      .trim();\n\n    return cleanText.length > maxLength \n      ? cleanText.substring(0, maxLength) + '...'\n      : cleanText;\n  }\n\n  async run() {\n    debugLog('🚀 Starting MCP Server transport...');\n    const transport = new StdioServerTransport();\n    debugLog('🎯 iCSS MCP Server running on stdio (Debug Mode)');\n    console.error('🎯 iCSS MCP Server running on stdio (Debug Mode)');\n    \n    try {\n      await this.server.connect(transport);\n    } catch (error) {\n      debugLog('💥 Server error:', error);\n      console.error('❌ Fatal error:', error.message);\n      process.exit(1);\n    }\n  }\n}\n\n// 启动服务器\nconst server = new IcssServer();\nserver.run(); "
  },
  {
    "path": "MCP/setup.js",
    "content": "#!/usr/bin/env node\n\nimport fs from 'fs';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nclass ProjectSetup {\n  constructor() {\n    this.projectRoot = __dirname;\n    this.dataDir = path.join(this.projectRoot, 'data');\n  }\n\n  async setup() {\n    console.log('🚀 Setting up iCSS MCP Server...\\n');\n\n    // 1. 创建必要的目录\n    this.createDirectories();\n\n    // 2. 创建环境变量文件\n    this.createEnvFile();\n\n    // 3. 显示下一步指令\n    this.showNextSteps();\n  }\n\n  createDirectories() {\n    console.log('📁 Creating necessary directories...');\n    \n    const directories = [\n      this.dataDir\n    ];\n\n    directories.forEach(dir => {\n      if (!fs.existsSync(dir)) {\n        fs.mkdirSync(dir, { recursive: true });\n        console.log(`   ✅ Created: ${path.relative(this.projectRoot, dir)}`);\n      } else {\n        console.log(`   ℹ️  Already exists: ${path.relative(this.projectRoot, dir)}`);\n      }\n    });\n  }\n\n  createEnvFile() {\n    console.log('\\n🔧 Creating environment configuration...');\n    \n    const envPath = path.join(this.projectRoot, '.env');\n    const envExampleContent = `\n        # GitHub API Configuration (Optional - for higher rate limits)\n        # Get your token from: https://github.com/settings/tokens\n        GITHUB_TOKEN=your_github_token_here\n\n        # Server Configuration\n        NODE_ENV=production\n        PORT=3000\n\n        # Database Configuration\n        DB_PATH=./data/icss.db\n    `;\n\n    if (!fs.existsSync(envPath)) {\n      fs.writeFileSync(envPath, envExampleContent);\n      console.log('   ✅ Created .env file');\n    } else {\n      console.log('   ℹ️  .env file already exists');\n    }\n  }\n\n  showNextSteps() {\n    console.log('\\n🎉 Setup completed! Next steps:\\n');\n    \n    console.log('1️⃣  Install dependencies:');\n    console.log('   npm install\\n');\n    \n    console.log('2️⃣  Fetch iCSS repository data:');\n    console.log('   npm run fetch-issues\\n');\n    \n    console.log('3️⃣  Test the server:');\n    console.log('   npm start\\n');\n    \n    console.log('4️⃣  Configure Cursor IDE:');\n    console.log('   Add this to your MCP settings (~/.config/cursor/mcp_settings.json):');\n    console.log('   {');\n    console.log('     \"mcpServers\": {');\n    console.log('       \"icss\": {');\n    console.log(`         \"command\": \"node\",`);\n    console.log(`         \"args\": [\"${path.resolve(this.projectRoot, 'server.js')}\"],`);\n    console.log('         \"env\": {}');\n    console.log('       }');\n    console.log('     }');\n    console.log('   }\\n');\n    \n    console.log('5️⃣  Restart Cursor IDE to load the MCP server\\n');\n    \n    console.log('📚 For more information, see README.md');\n  }\n}\n\nconst setup = new ProjectSetup();\nsetup.setup().catch(console.error); "
  },
  {
    "path": "MCP/test-inspiration.js",
    "content": "#!/usr/bin/env node\n\nimport Database from 'sqlite3';\nimport path from 'path';\nimport { fileURLToPath } from 'url';\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = path.dirname(__filename);\n\nclass InspirationTester {\n  constructor() {\n    this.dbPath = path.join(__dirname, 'data', 'icss.db');\n    this.db = new Database.Database(this.dbPath, (err) => {\n      if (err) {\n        console.error('❌ Error opening database:', err);\n        process.exit(1);\n      }\n    });\n  }\n\n  async testDatabaseIntegrity() {\n    console.log('🔍 Testing database integrity...\\n');\n    \n    // 测试表是否存在\n    const tables = [\n      'issues',\n      'css_inspiration', \n      'code_snippets',\n      'demo_styles'\n    ];\n\n    for (const table of tables) {\n      await this.checkTable(table);\n    }\n  }\n\n  checkTable(tableName) {\n    return new Promise((resolve, reject) => {\n      this.db.get(\n        `SELECT COUNT(*) as count FROM ${tableName}`,\n        (err, row) => {\n          if (err) {\n            console.error(`❌ Table ${tableName} error:`, err.message);\n            reject(err);\n          } else {\n            console.log(`✅ Table ${tableName}: ${row.count} records`);\n            resolve(row.count);\n          }\n        }\n      );\n    });\n  }\n\n  async testSearchFunctionality() {\n    console.log('\\n🔍 Testing search functionality...\\n');\n    \n    // 测试 CSS-Inspiration 搜索\n    await this.testQuery(\n      'CSS-Inspiration Search',\n      `SELECT * FROM css_inspiration WHERE search_content LIKE '%动画%' LIMIT 3`\n    );\n\n    // 测试分类统计\n    await this.testQuery(\n      'Category Statistics',\n      `SELECT category, COUNT(*) as count FROM css_inspiration GROUP BY category ORDER BY count DESC LIMIT 5`\n    );\n\n    // 测试难度分布\n    await this.testQuery(\n      'Difficulty Distribution',\n      `SELECT difficulty_level, COUNT(*) as count FROM css_inspiration GROUP BY difficulty_level`\n    );\n  }\n\n  testQuery(testName, sql) {\n    return new Promise((resolve, reject) => {\n      this.db.all(sql, (err, rows) => {\n        if (err) {\n          console.error(`❌ ${testName} failed:`, err.message);\n          reject(err);\n        } else {\n          console.log(`✅ ${testName}: ${rows.length} results`);\n          if (rows.length > 0) {\n            console.log('   Sample results:');\n            rows.slice(0, 2).forEach((row, index) => {\n              console.log(`   ${index + 1}.`, Object.keys(row).slice(0, 3).map(key => \n                `${key}: ${String(row[key]).substring(0, 30)}`\n              ).join(', '));\n            });\n          }\n          console.log('');\n          resolve(rows);\n        }\n      });\n    });\n  }\n\n  async testCodeSnippets() {\n    console.log('🔍 Testing code snippets...\\n');\n    \n    await this.testQuery(\n      'Code Snippets by Type',\n      `SELECT snippet_type, COUNT(*) as count FROM code_snippets GROUP BY snippet_type`\n    );\n\n    await this.testQuery(\n      'Sample CSS Snippets',\n      `SELECT * FROM code_snippets WHERE snippet_type = 'css' LIMIT 2`\n    );\n  }\n\n  async testDemoStyles() {\n    console.log('🔍 Testing demo styles...\\n');\n    \n    await this.testQuery(\n      'Interactive Demos',\n      `SELECT COUNT(*) as count FROM demo_styles WHERE is_interactive = 1`\n    );\n\n    await this.testQuery(\n      'Sample Demo',\n      `SELECT \n         ci.title, \n         ci.category, \n         ds.is_interactive \n       FROM css_inspiration ci \n       JOIN demo_styles ds ON ci.id = ds.inspiration_id \n       LIMIT 3`\n    );\n  }\n\n  async generateReport() {\n    console.log('📊 Generating comprehensive report...\\n');\n    \n    const stats = {};\n\n    // iCSS 统计\n    stats.icss = await new Promise((resolve) => {\n      this.db.get('SELECT COUNT(*) as count FROM issues', (err, row) => {\n        resolve(err ? 0 : row.count);\n      });\n    });\n\n    // CSS-Inspiration 统计\n    stats.inspiration = await new Promise((resolve) => {\n      this.db.get('SELECT COUNT(*) as count FROM css_inspiration', (err, row) => {\n        resolve(err ? 0 : row.count);\n      });\n    });\n\n    // 代码片段统计\n    stats.snippets = await new Promise((resolve) => {\n      this.db.get('SELECT COUNT(*) as count FROM code_snippets', (err, row) => {\n        resolve(err ? 0 : row.count);\n      });\n    });\n\n    // 演示样式统计\n    stats.demos = await new Promise((resolve) => {\n      this.db.get('SELECT COUNT(*) as count FROM demo_styles', (err, row) => {\n        resolve(err ? 0 : row.count);\n      });\n    });\n\n    console.log('📈 Database Statistics Summary:');\n    console.log(`   iCSS Articles: ${stats.icss}`);\n    console.log(`   CSS-Inspiration Demos: ${stats.inspiration}`);\n    console.log(`   Code Snippets: ${stats.snippets}`);\n    console.log(`   Demo Styles: ${stats.demos}`);\n    console.log(`   Total Records: ${stats.icss + stats.inspiration + stats.snippets + stats.demos}`);\n  }\n\n  async runAllTests() {\n    try {\n      await this.testDatabaseIntegrity();\n      await this.testSearchFunctionality();\n      await this.testCodeSnippets();\n      await this.testDemoStyles();\n      await this.generateReport();\n      \n      console.log('\\n🎉 All tests completed successfully!');\n      console.log('\\n💡 You can now use the enhanced iCSS MCP Server with both iCSS articles and CSS-Inspiration demos!');\n      \n    } catch (error) {\n      console.error('\\n❌ Test failed:', error.message);\n    } finally {\n      this.db.close();\n    }\n  }\n\n  close() {\n    this.db.close();\n  }\n}\n\n// 运行测试\nconsole.log('🚀 Starting iCSS MCP Server Integration Tests...\\n');\nconst tester = new InspirationTester();\ntester.runAllTests().catch(console.error);\n\n// 优雅关闭\nprocess.on('SIGINT', () => {\n  console.log('\\n⏹️ Shutting down...');\n  tester.close();\n  process.exit(0);\n}); "
  },
  {
    "path": "MCP/test-server.js",
    "content": "#!/usr/bin/env node\n\nimport Database from 'sqlite3';\nimport Fuse from 'fuse.js';\n\nconsole.log('🧪 Testing iCSS MCP Server functionality...\\n');\n\n// 测试数据库连接\nfunction testDatabase() {\n  return new Promise((resolve, reject) => {\n    console.log('1️⃣ Testing database connection...');\n    \n    const db = new Database.Database('./data/icss.db', (err) => {\n      if (err) {\n        console.error('❌ Database connection failed:', err.message);\n        reject(err);\n        return;\n      }\n      \n      console.log('✅ Database connected successfully');\n      \n      // 测试数据查询\n      db.all('SELECT COUNT(*) as total FROM issues', (err, rows) => {\n        if (err) {\n          console.error('❌ Database query failed:', err.message);\n          reject(err);\n          return;\n        }\n        \n        console.log(`✅ Found ${rows[0].total} articles in database`);\n        \n        // 测试搜索功能\n        testSearch(db, resolve, reject);\n      });\n    });\n  });\n}\n\n// 测试搜索功能\nfunction testSearch(db, resolve, reject) {\n  console.log('\\n2️⃣ Testing search functionality...');\n  \n  db.all('SELECT * FROM issues LIMIT 10', (err, rows) => {\n    if (err) {\n      console.error('❌ Search test failed:', err.message);\n      reject(err);\n      return;\n    }\n    \n    if (rows.length === 0) {\n      console.error('❌ No data found for search test');\n      reject(new Error('No data found'));\n      return;\n    }\n    \n    console.log('✅ Sample data retrieved successfully');\n    console.log(`✅ First article: \"${rows[0].title}\"`);\n    \n    // 测试Fuse.js搜索\n    const fuseOptions = {\n      keys: [\n        { name: 'title', weight: 0.4 },\n        { name: 'search_content', weight: 0.4 },\n        { name: 'labels', weight: 0.2 }\n      ],\n      threshold: 0.3,\n      includeScore: true\n    };\n    \n    const fuse = new Fuse(rows, fuseOptions);\n    const results = fuse.search('flex');\n    \n    console.log(`✅ Search test completed: found ${results.length} results for \"flex\"`);\n    \n    db.close();\n    resolve();\n  });\n}\n\n// 测试MCP SDK导入\nfunction testMCPSDK() {\n  console.log('\\n3️⃣ Testing MCP SDK...');\n  \n  try {\n    import('@modelcontextprotocol/sdk/server/index.js').then(() => {\n      console.log('✅ MCP SDK imported successfully');\n      console.log('\\n🎉 All tests passed! The server should work correctly.\\n');\n      \n      console.log('📋 Next steps:');\n      console.log('1. Make sure Cursor MCP configuration is correct');\n      console.log('2. Check Cursor logs for any connection issues');\n      console.log('3. Restart Cursor after configuration changes');\n      \n      console.log('\\n🔧 Cursor Configuration:');\n      console.log('File: ~/.config/cursor/mcp_settings.json');\n      console.log('Content:');\n      console.log(JSON.stringify({\n        mcpServers: {\n          icss: {\n            command: \"node\",\n            args: [process.cwd() + \"/server.js\"],\n            env: {}\n          }\n        }\n      }, null, 2));\n    }).catch(err => {\n      console.error('❌ MCP SDK import failed:', err.message);\n      console.log('\\n💡 Try reinstalling dependencies: npm install');\n    });\n  } catch (err) {\n    console.error('❌ MCP SDK test failed:', err.message);\n  }\n}\n\n// 运行测试\ntestDatabase()\n  .then(() => {\n    testMCPSDK();\n  })\n  .catch(err => {\n    console.error('❌ Test failed:', err.message);\n    process.exit(1);\n  }); "
  },
  {
    "path": "readme.md",
    "content": "## ![logo](https://github.com/chokcoco/iCSS/blob/master/logo.jpg?raw=true)\r\n\r\nCSS 奇技淫巧，在这里，都有。\r\n\r\n本 Repo 围绕 **CSS/Web动画** 展开，谈一些有趣的话题，内容天马行空，想到什么说什么，不仅是为了拓宽解决问题的思路，更涉及一些容易忽视或是十分有趣的 CSS 细节。\r\n\r\n所有内容都在 [Issues](https://github.com/chokcoco/iCSS/issues) 中，同步更新到我的[个人博客](http://www.cnblogs.com/coco1s/)，觉得不错的可以点个 `star` 收藏支持。\r\n\r\n### 按分类进行阅读\r\n\r\n [![Background](https://img.shields.io/badge/Background-white)](https://github.com/chokcoco/iCSS/labels/Background) [![Border](https://img.shields.io/badge/Border-yellow)](https://github.com/chokcoco/iCSS/labels/Border) [![clip-path](https://img.shields.io/badge/clip%20path-pink)](https://github.com/chokcoco/iCSS/labels/clip-path) [![Mask](https://img.shields.io/badge/Mask-indigo)](https://github.com/chokcoco/iCSS/labels/Mask) [![Shadow](https://img.shields.io/badge/Shadow-lightsalmon)](https://github.com/chokcoco/iCSS/labels/Shadow) [![Shape](https://img.shields.io/badge/Shape-lightseagrenn)](https://github.com/chokcoco/iCSS/labels/Shape) [![混合模式](https://img.shields.io/badge/混合模式-royalblue)](https://github.com/chokcoco/iCSS/labels/混合模式) [![滤镜](https://img.shields.io/badge/滤镜-silver)](https://github.com/chokcoco/iCSS/labels/滤镜) [![伪类](https://img.shields.io/badge/伪类-linen)](https://github.com/chokcoco/iCSS/labels/伪类)\r\n\r\n[![CSS Layout](https://img.shields.io/badge/CSS%20Layout-palevioletred)](https://github.com/chokcoco/iCSS/labels/Layout) [![CSS Function](https://img.shields.io/badge/CSS%20Function-red)](https://github.com/chokcoco/iCSS/labels/CSS%20Function) [![CSS-Houdini](https://img.shields.io/badge/CSS%20Houdini-gray)](https://github.com/chokcoco/iCSS/labels/CSS%20Houdini) [![CSS Variable](https://img.shields.io/badge/CSS%20Variable-cyan)](https://github.com/chokcoco/iCSS/labels/CSS%20Variable) [![CSS 新特性](https://img.shields.io/badge/CSS%20新特性-gold)](https://github.com/chokcoco/iCSS/labels/CSS%20新特性) [![CSS-doodle](https://img.shields.io/badge/CSS%20doodle-deeppink)](https://github.com/chokcoco/iCSS/labels/CSS-doodle) [![Modern CSS](https://img.shields.io/badge/Modern%20CSS-khaki)](https://github.com/chokcoco/iCSS/labels/Modern%20CSS) \r\n\r\n[![动效](https://img.shields.io/badge/动效-navy)](https://github.com/chokcoco/iCSS/labels/动效) [![动画](https://img.shields.io/badge/动画-oldlace)](https://github.com/chokcoco/iCSS/labels/动画) [![可访问性](https://img.shields.io/badge/可访问性-orchid)](https://github.com/chokcoco/iCSS/labels/可访问性（Accessibility）) [![3D 效果](https://img.shields.io/badge/3D%20效果-blue)](https://github.com/chokcoco/iCSS/labels/3D) [![图片效果](https://img.shields.io/badge/图片效果-orange)](https://github.com/chokcoco/iCSS/labels/图片效果) [![文字效果](https://img.shields.io/badge/文字效果-plum)](https://github.com/chokcoco/iCSS/labels/文字效果) [![边框效果](https://img.shields.io/badge/边框效果-pink)](https://github.com/chokcoco/iCSS/labels/边框效果)\r\n\r\n[![SVG](https://img.shields.io/badge/SVG-lime)](https://github.com/chokcoco/iCSS/labels/SVG) [![奇技淫巧](https://img.shields.io/badge/奇技淫巧-palegreen)](https://github.com/chokcoco/iCSS/labels/奇技淫巧)  [![性能](https://img.shields.io/badge/性能-peru)](https://github.com/chokcoco/iCSS/labels/性能) [![技巧](https://img.shields.io/badge/技巧-purple)](https://github.com/chokcoco/iCSS/labels/技巧)  [![浏览器特性](https://img.shields.io/badge/浏览器特性-rosybrown)](https://github.com/chokcoco/iCSS/labels/浏览器特性)  [![特殊交互](https://img.shields.io/badge/特殊交互-sienna)](https://github.com/chokcoco/iCSS/labels/特殊交互) [![用户体验](https://img.shields.io/badge/用户体验-skyblue)](https://github.com/chokcoco/iCSS/labels/用户体验) [![翻译](https://img.shields.io/badge/翻译-tan)](https://github.com/chokcoco/iCSS/labels/翻译) [![设计](https://img.shields.io/badge/设计-yellowgreen)](https://github.com/chokcoco/iCSS/labels/设计) [![面试](https://img.shields.io/badge/面试-tomato)](https://github.com/chokcoco/iCSS/labels/面试) [![Bug](https://img.shields.io/badge/Bug-green)](https://github.com/chokcoco/iCSS/labels/Bug)\r\n\r\n## iCSS 前端趣闻\r\n\r\n所有内容首发更新到我的**公众号**：\r\n\r\n<img width=160 src=\"https://raw.githubusercontent.com/chokcoco/chokcoco/main/qrcode_big.png\" />\r\n\r\n## LIST\r\n\r\n#### 220、[浏览器原生「磁吸」效果！Anchor Positioning 锚点定位神器解析](https://github.com/chokcoco/iCSS/issues/275)\r\n\r\n![](https://github.com/user-attachments/assets/cb620530-8740-4be7-bdb7-d2485a69a457)\r\n\r\n#### 219、[巧用 CSS 实现高频出现的复杂怪状按钮 - 内凹平滑圆角](https://github.com/chokcoco/iCSS/issues/272)\r\n\r\n![](https://github.com/user-attachments/assets/fcc18faa-e7b0-443c-9430-ae1f04cd4f55)\r\n\r\n#### 218、[巧用 CSS 实现高频出现的复杂怪状按钮 - 镂空的内凹圆角边框](https://github.com/chokcoco/iCSS/issues/271)\r\n\r\n![](https://github.com/user-attachments/assets/b168c68b-069b-4ddb-b131-b618aeacb440)\r\n\r\n#### 217、[【动画进阶】单标签实现多行文本随滚动颜色变换](https://github.com/chokcoco/iCSS/issues/269)\r\n\r\n![](https://github.com/user-attachments/assets/784fbce5-0e77-4365-acb2-3e008a7c4e96)\r\n\r\n#### 216、[【现代 CSS】更强大的 :nth-child 选择器](https://github.com/chokcoco/iCSS/issues/267)\r\n\r\n#### 215、[全尺寸的带圆角的渐变边框](https://github.com/chokcoco/iCSS/issues/266)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/aa45cff6-d6b0-4c80-9289-04eac2110075)\r\n\r\n#### 214、[巧妙使用多种方式实现单侧阴影](https://github.com/chokcoco/iCSS/issues/265)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/fe6e93e4-578d-491e-83d7-883c142bbd2d)\r\n\r\n#### 213、[渐变边框文字效果？CSS 轻松拿捏！](https://github.com/chokcoco/iCSS/issues/264)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/fc584688-1848-4f91-ba16-c40db24c5598)\r\n\r\n#### 212、[【动画进阶】神奇的卡片 Hover 效果与 Blur 的特性探究](https://github.com/chokcoco/iCSS/issues/263)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/92d47216-b270-4f27-babb-fb37d6e7e905)\r\n\r\n#### 211、[【动画进阶】类 ChatGpt 多行文本打字效果](https://github.com/chokcoco/iCSS/issues/262)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/2a2f26ae-a4bc-4139-a108-3067f31c1c52)\r\n\r\n#### 210、[【现代 CSS】标准滚动条控制规范 scrollbar-color 和 scrollbar-width](https://github.com/chokcoco/iCSS/issues/259)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/442f06e2-b525-42a0-b92a-74d3543ba5b9)\r\n\r\n#### 209、[【动画进阶】极具创意的鼠标交互动画](https://github.com/chokcoco/iCSS/issues/258)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/438e0d44-1f4b-44c0-96cc-4724eb632135)\r\n\r\n#### 208、[【动画进阶】巧用 CSS/SVG 实现复杂线条光效动画](https://github.com/chokcoco/iCSS/issues/257)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/b3d46456-7c07-4e17-9b23-97c428aca10c)\r\n\r\n#### 207、[【布局进阶】巧用 :has & drop-shadow 实现复杂布局效果](https://github.com/chokcoco/iCSS/issues/256)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/480a0e78-9a19-4587-a46e-0d3fcf84b0d4)\r\n\r\n#### 206、[现代 CSS 解决方案：accent-color 强调色](https://github.com/chokcoco/iCSS/issues/255)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/605cd0e3-f9a3-4f47-b416-1a1cc2683a06)\r\n\r\n#### 205、[【动画进阶】神奇的 3D 卡片反光闪烁动效](https://github.com/chokcoco/iCSS/issues/254)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/d4a79fd3-c5a0-4757-9c1f-d801bedf9a66)\r\n\r\n#### 204、[现代 CSS 解决方案：文字颜色自动适配背景色！](https://github.com/chokcoco/iCSS/issues/253)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/72b9f222-8a73-4d49-966f-db61bd59eed8)\r\n\r\n#### 203、[带圆角的虚线边框？CSS 不在话下](https://github.com/chokcoco/iCSS/issues/250)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/94750b76-b565-42cb-b261-bc3ed0447f09)\r\n\r\n#### 202、[【布局技巧】Flex 布局下居中溢出滚动截断问题](https://github.com/chokcoco/iCSS/issues/249)\r\n\r\n![288605244-2228ef21-e75c-4d5c-ae31-c472bf20e947](https://github.com/chokcoco/iCSS/assets/8554143/eabca028-5225-4b58-8e57-1e3a2dc32710)\r\n\r\n#### 201、[CSS 也能实现 if 判断？实现动态高度下的不同样式展现](https://github.com/chokcoco/iCSS/issues/248)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/9ef933f9-6f2f-4333-a11a-a979d349e53f)\r\n\r\n#### 200、[【动画进阶】单标签下多色块随机文字随机颜色动画](https://github.com/chokcoco/iCSS/issues/247)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/e4136dc7-159d-4662-bf39-97c99ae6ea48)\r\n\r\n#### 199、[CSS 还原拉斯维加斯球数字动画](https://github.com/chokcoco/iCSS/issues/246)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/274851330-29a8ffd1-e412-49a5-99f4-297eccf907ed.gif)\r\n\r\n#### 198、[【动画进阶】神奇的背景，生化危机4日食 Loading 动画还原 ](https://github.com/chokcoco/iCSS/issues/245)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/273544952-d8b83606-4925-4721-a1d4-81c9fed7382f.gif)\r\n\r\n#### 197、[【动画进阶】当路径动画遇到滚动驱动！](https://github.com/chokcoco/iCSS/issues/244)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/266767709-654010fc-84bf-4684-95c7-046ef9c5a60a.gif)\r\n\r\n#### 196、[现代 CSS 解决方案：原生嵌套（Nesting）](https://github.com/chokcoco/iCSS/issues/243)\r\n\r\n#### 195、[现代 CSS 解决方案：数学函数 Round](https://github.com/chokcoco/iCSS/issues/242)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/264971324-c10e3935-2a30-4aed-81dd-0102ad6dc422.gif)\r\n\r\n#### 194、[震惊！CSS 也能实现碰撞检测？](https://github.com/chokcoco/iCSS/issues/241)\r\n\r\n![](https://p1-juejin.byteimg.com/tos-cn-i-k3u1fbpfcp/f46ba2d04d7c4a9d8ec5ad28a2d997e2~tplv-k3u1fbpfcp-watermark.image?)\r\n\r\n#### 193、[抢先体验！超强的 Anchor Positioning 锚点定位](https://github.com/chokcoco/iCSS/issues/239)\r\n\r\n![](https://img2023.cnblogs.com/blog/608782/202308/608782-20230814230910889-551932555.gif)\r\n\r\n#### 192、[神奇的 3D 磨砂玻璃透视效果](https://github.com/chokcoco/iCSS/issues/238)\r\n\r\n![258775149-4247cd7b-7b36-43d7-9231-f1490e0c442b](https://github.com/chokcoco/iCSS/assets/8554143/fe1faa52-00b5-4514-991d-7259662251a8)\r\n\r\n#### 191、[【动画进阶】有意思的 Emoji 3D 表情切换效果](https://github.com/chokcoco/iCSS/issues/237)\r\n\r\n![](https://github.com/chokcoco/iCSS/assets/8554143/2f10097a-8049-40f7-941d-0332c82baefc)\r\n\r\n#### 190、[【动画进阶】有意思的网格下落渐次加载效果](https://github.com/chokcoco/iCSS/issues/235)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/252986059-7a3d5897-614b-4dde-95f1-eb666309fab9.gif)\r\n\r\n#### 189、[解放生产力！transform 支持单独赋值改变](https://github.com/chokcoco/iCSS/issues/236)\r\n\r\n#### 188、[单标签下的日间/黑夜模式切换按钮效果](https://github.com/chokcoco/iCSS/issues/234)\r\n\r\n![](https://github.com/chokcoco/cococss/assets/8554143/045bbde5-55da-45da-b854-39dc95b85a8b)\r\n\r\n#### 187、[现代 CSS 解决方案：CSS 原生支持的三角函数](https://github.com/chokcoco/iCSS/issues/233)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/240595711-ebfe3bb2-ac54-4c17-8604-5117b8562d6e.png)\r\n\r\n#### 186、[有意思的气泡 Loading 效果](https://github.com/chokcoco/iCSS/issues/231)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/231435406-d26ea832-9dc7-4cdf-80e6-4b392a08d859.gif)\r\n\r\n#### 185、[有趣的六芒星能力图动画](https://github.com/chokcoco/iCSS/issues/228)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/226392554-8f59f5ee-5cd2-4fc4-bdb8-71dab454cd07.gif)\r\n\r\n#### 184、[CSS 高阶小技巧 - 角向渐变的妙用！](https://github.com/chokcoco/iCSS/issues/227)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/225660319-9dc4c4f8-a624-4219-820d-44918fb75fe6.png)\r\n\r\n#### 183、[巧用 CSS 变量，实现动画函数复用，制作高级感拉满的网格动画](https://github.com/chokcoco/iCSS/issues/226)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/222392984-ee7caa2a-d8d9-43c2-ae4c-4bd2e8d50db1.gif)\r\n\r\n#### 182、[CSS 高阶技巧 -- 不定宽文本溢出跑马灯效果完美解决方案](https://github.com/chokcoco/iCSS/issues/225)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/219847245-baf51088-c7e5-450d-b277-06b8f2989443.gif)\r\n\r\n#### 181、[由小见大！不规则造型按钮解决方案](https://github.com/chokcoco/iCSS/issues/224)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/218682392-bbf4bace-e73d-4afa-9727-74ee3a79ac3a.png)\r\n\r\n#### 180、[动态视口单位之 dvh、svh、lvh](https://github.com/chokcoco/iCSS/issues/223)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/215314253-dbf5f970-d86c-4e17-aad7-ede5a43b7593.png)\r\n\r\n#### 179、[开局一张图，构建神奇的 CSS 效果](https://github.com/chokcoco/iCSS/issues/218)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/208087664-1cc7448f-4c4f-45d4-974e-b597ff5799f0.gif)\r\n\r\n#### 178、[巧用视觉障眼法，还原 3D 文字特效](https://github.com/chokcoco/iCSS/issues/219)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/208053417-07b4bfa5-eac3-4a34-af57-62ab8fc8f4ed.gif)\r\n\r\n#### 177、[不规则图形背景排版高阶技巧 -- 酷炫的六边形网格背景图](https://github.com/chokcoco/iCSS/issues/215)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/206407140-0a7fc0da-c8bf-4f26-b15e-0cb07c29b963.gif)\r\n\r\n#### 176、[现代 CSS 高阶技巧，不规则边框解决方案](https://github.com/chokcoco/iCSS/issues/221)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/204084009-611b9415-dd6f-467e-96a4-c2c5373fab08.gif)\r\n\r\n#### 175、[现代 CSS 高阶技巧，完美的波浪进度条效果！](https://github.com/chokcoco/iCSS/issues/220)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/204078431-ee7cf2d3-3340-4713-a26e-51a6adbea76c.gif)\r\n\r\n#### 174、[现代 CSS 高阶技巧，像 Canvas 一样自由绘图构建样式！](https://github.com/chokcoco/iCSS/issues/217)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/203250544-89dcff90-8c3c-4bda-9e70-33a2e7d828a5.png)\r\n\r\n#### 173、[现代 CSS 之高阶图片渐隐消失术](https://github.com/chokcoco/iCSS/issues/214)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/202458010-a4b9c017-7112-40fa-affc-82dc3c3fce69.gif)\r\n\r\n#### 172、[除了 filter 还有什么置灰网站的方式？](https://github.com/chokcoco/iCSS/issues/212)\r\n![](https://user-images.githubusercontent.com/8554143/204998014-154d7877-0592-4601-99fb-3cd644e25cd3.gif)\r\n\r\n#### 171、[快速构建 3D Visualization of DOM](https://github.com/chokcoco/iCSS/issues/210)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/197375744-c35038f6-f498-492e-a065-02376471a78b.gif)\r\n\r\n#### 170、[CSS at-rules(@) 规则扫盲](https://github.com/chokcoco/iCSS/issues/204)\r\n\r\n#### 169、[CSS 渐变锯齿消失术](https://github.com/chokcoco/iCSS/issues/209)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/198038342-79e90db9-3347-43d7-a5e5-c8bfb4fb63af.gif)\r\n\r\n#### 168、[超强的苹果官网滚动文字特效实现](https://github.com/chokcoco/iCSS/issues/208)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/195977878-9833a3bf-4537-4ccf-9111-7d2be26c7b33.gif)\r\n\r\n#### 167、[超强的纯 CSS 鼠标点击拖拽效果](https://github.com/chokcoco/iCSS/issues/207)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/190998870-cca39d69-be96-4ae6-a245-52ee62cbd429.gif)\r\n\r\n#### 166、[两道超有意思的 CSS 面试题，试试你的基础](https://github.com/chokcoco/iCSS/issues/206)\r\n\r\n#### 165、[单标签实现复杂的棋盘布局](https://github.com/chokcoco/iCSS/issues/203)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/188442119-a443a5d4-e268-4e5b-8341-62ca614cda8d.png)\r\n\r\n#### 164、[新时代布局新特性 -- 容器查询](https://github.com/chokcoco/iCSS/issues/201)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/186677287-a1076390-f74c-4a2b-a8be-a385d4326057.gif)\r\n\r\n#### 163、[有意思的水平横向溢出滚动](https://github.com/chokcoco/iCSS/issues/200)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/185925720-d81fb5b5-5f7c-44a3-a23b-cae7cccc2d6d.gif)\r\n\r\n#### 162、[高阶 CSS 技巧在复杂动效中的应用](https://github.com/chokcoco/iCSS/issues/202)\r\n\r\n<img src=\"https://user-images.githubusercontent.com/8554143/187671481-5e075209-44d3-44a6-b540-4f457999ed74.gif\" width=\"600px\"/>\r\n\r\n#### 161、[有意思的方向裁切 overflow: clip](https://github.com/chokcoco/iCSS/issues/199)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/184640231-ace474c7-2699-4b1e-b99f-b38cba26a2df.gif)\r\n\r\n#### 160、[巧用 transition 实现短视频 APP 点赞动画](https://github.com/chokcoco/iCSS/issues/198)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/183909772-acf0837c-1145-4078-8702-6b7f994328d7.gif)\r\n\r\n#### 159、[妙啊！纯 CSS 实现拼图游戏](https://github.com/chokcoco/iCSS/issues/197)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/182617117-4512bb74-d7eb-4a39-8e85-0a02cd52b9f5.gif)\r\n\r\n#### 158、[多行文本下的文字渐隐消失术](https://github.com/chokcoco/iCSS/issues/196)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/182107902-b8e414f1-4d30-442e-b1d2-29234b2b5323.gif)\r\n\r\n#### 157、[使用 CSS 构建强大且酷炫的粒子动画](https://github.com/chokcoco/iCSS/issues/195)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/179990358-5d13aa58-486c-4461-9e16-f04295110d35.gif)\r\n\r\n#### 156、[妙用 CSS 构建花式透视背景效果](https://github.com/chokcoco/iCSS/issues/194)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/179509709-e60ac03f-5db6-41e7-ace7-2ac07f031777.gif)\r\n\r\n#### 155、[圆角大杀器，使用滤镜构建圆角及波浪效果！](https://github.com/chokcoco/iCSS/issues/192)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/178275342-c4dffa35-a886-41be-aa1c-9c0f19468a9e.gif)\r\n\r\n#### 154、[超酷炫的转场动画？CSS 轻松拿下！](https://github.com/chokcoco/iCSS/issues/191)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/177775093-f25e0264-5e74-4516-9068-9a537344e865.gif)\r\n\r\n#### 153、[利用噪声构建美妙的 CSS 图形](https://github.com/chokcoco/iCSS/issues/190)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/177010428-12045a95-3030-4d4d-a3d0-203554af69c3.gif)\r\n\r\n#### 152、[高阶切图技巧！基于单张图片的任意颜色转换](https://github.com/chokcoco/iCSS/issues/189)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/175951163-e8175533-b6e7-4a26-b28d-b1a3bb195fb5.png)\r\n\r\n#### 151、[使用纯 CSS 实现超酷炫的粘性气泡效果](https://github.com/chokcoco/iCSS/issues/188)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/175025476-d27fca48-62af-462f-b030-af6c818e6055.gif)\r\n\r\n#### 150、[超 Nice 的表格响应式布局小技巧](https://github.com/chokcoco/iCSS/issues/187)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/174429173-b2b8352c-1b9a-40d0-a69e-15ceb7b1636f.gif)\r\n\r\n#### 149、[有意思的鼠标指针交互探究](https://github.com/chokcoco/iCSS/issues/186)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/173178051-22618d6a-cdb9-4d8f-ac65-f895ae4e0c0b.gif)\r\n\r\n#### 148、[使用 content-visibility 优化渲染性能](https://github.com/chokcoco/iCSS/issues/185)\r\n\r\n#### 147、[文字轮播与图片轮播？CSS 不在话下](https://github.com/chokcoco/iCSS/issues/184)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/169821330-21625c8c-d9b4-4ef3-9b72-f55e1e45fbbe.gif)\r\n\r\n#### 146、[动画小技巧，通过 hover 让动画只运行一次且停留在最后一帧](https://github.com/chokcoco/iCSS/issues/182)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/167853146-3f842313-0cac-4f26-968f-1746fbda214f.gif)\r\n\r\n#### 145、[浅谈逻辑选择器 is、where、not、has](https://github.com/chokcoco/iCSS/issues/181)\r\n\r\n#### 144、[现代 CSS 解决方案：CSS 数学函数](https://github.com/chokcoco/iCSS/issues/177)\r\n\r\n#### 143、[离谱的 CSS！从表盘刻度到艺术剪纸](https://github.com/chokcoco/iCSS/issues/180)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/164239137-ee2d9e90-66ac-42b3-8e66-1ce81a0b1a38.gif)\r\n\r\n#### 142、[让交互更加生动！有意思的鼠标跟随 3D 旋转动效](https://github.com/chokcoco/iCSS/issues/179)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/163177359-9b1941cd-93ec-478a-bba7-32ed34ca5945.gif)\r\n\r\n#### 141、[Amazing！巧用 CSS 视差实现酷炫交互动效](https://github.com/chokcoco/iCSS/issues/178)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/162746366-7d7bade9-a78d-4b18-b9af-aa79ec9f9e7e.gif)\r\n\r\n#### 140、[现代 CSS 解决方案：Modern CSS Reset](https://github.com/chokcoco/iCSS/issues/176)\r\n\r\n#### 139、[巧用 background-clip 实现超强的文字动效 ](https://github.com/chokcoco/iCSS/issues/175)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/160836908-aa36f16a-4895-41b0-91fd-5782b370579d.gif)\r\n\r\n#### 138、[一道有意思的 CSS 面试题，FizzBu​​zz ~](https://github.com/chokcoco/iCSS/issues/174)\r\n\r\n#### 137、[2022 年最受瞩目的新特性 CSS @layer 到底是个啥？](https://github.com/chokcoco/iCSS/issues/171)\r\n\r\n#### 136、[CSS 阴影进阶，实现更加的立体的阴影效果！](https://github.com/chokcoco/iCSS/issues/170)\r\n\r\n![](https://camo.githubusercontent.com/447042c5666f850c5a3f9a19426e64c7d72cca75d521265386aefe4e52fd81d0/68747470733a2f2f70392d6a75656a696e2e62797465696d672e636f6d2f746f732d636e2d692d6b3375316662706663702f39623138383132346638616434396630393461616135373838326135393439307e74706c762d6b3375316662706663702d77617465726d61726b2e696d6167653f)\r\n\r\n#### 135、[利用混合模式，让文字智能适配背景颜色](https://github.com/chokcoco/iCSS/issues/169)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/156753214-a6b1b6b7-b3fe-47b1-ab3e-601a2e98962e.gif)\r\n\r\n#### 134、[系统性学习 CSS 指南及全 DEMO 练习](https://github.com/chokcoco/iCSS/issues/168)\r\n\r\n#### 133、[巧用 CSS 构建渐变彩色二维码](https://github.com/chokcoco/iCSS/issues/167)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/155879216-ad6fc4e2-979a-4e52-8308-7f99ad8311de.png)\r\n\r\n#### 132、[来了来了，它终于来了，动画杀手锏 @scroll-timeline](https://github.com/chokcoco/iCSS/issues/166)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/155667488-a139c576-6abb-4001-bbf9-a6900cf09c75.gif)\r\n\r\n#### 131、[突破限制，CSS font-variation 可变字体的魅力](https://github.com/chokcoco/iCSS/issues/164)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/154795501-7ae58f37-df35-43ae-803c-16af9bad9d31.gif)\r\n\r\n#### 130、[小技巧 | 渐变消失遮罩的多种实现方式](https://github.com/chokcoco/iCSS/issues/163)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/154455188-ca43054a-c34d-4eb0-89ca-ee2a3881e796.png)\r\n\r\n#### 129、[巧用 CSS 实现炫彩三角边框动画](https://github.com/chokcoco/iCSS/issues/162)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/153760987-305539d2-1896-4717-9551-f054fd9ba6f1.gif)\r\n\r\n#### 128、[扫盲贴：2021 CSS 最冷门特性都是些啥？](https://github.com/chokcoco/iCSS/issues/161)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/153198755-aac1ca13-80c3-4459-9746-3a418a939dd6.png)\r\n\r\n#### 127、[疑难杂症：运用 transform 导致文本模糊的现象探究](https://github.com/chokcoco/iCSS/issues/160)\r\n\r\n#### 126、[LPL Ban/Pick 选人阶段的遮罩效果是如何实现的？](https://github.com/chokcoco/iCSS/issues/159)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/150136348-ea717988-9e1c-4503-b040-719dde193521.gif)\r\n\r\n#### 125、[巧用 CSS 实现动态线条 Loading 动画](https://github.com/chokcoco/iCSS/issues/158)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/149146680-b1d7f89b-2e6a-4787-85bd-c7d871564816.gif)\r\n\r\n#### 124、[深入浅出 CSS 动画](https://github.com/chokcoco/iCSS/issues/141)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/148633639-66ceea90-94ea-4e1e-9bfe-38d4917a2a51.gif)\r\n\r\n#### 123、[妙用滤镜构建高级感拉满的磨砂玻璃渐变背景](https://github.com/chokcoco/iCSS/issues/157)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/148382115-b8092fb6-1e17-46e7-a61d-6fa3cc838e7c.png)\r\n\r\n#### 122、[深入探讨 filter 与 backdrop-filter 的异同](https://github.com/chokcoco/iCSS/issues/147)\r\n\r\n#### 121、[Amazing！！CSS 也能实现烟雾效果？](https://github.com/chokcoco/iCSS/issues/156)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/147098212-8f74ce74-2069-4d28-98ad-6cea0990bdfe.gif)\r\n\r\n#### 120、[Amazing！！CSS 也能实现极光？](https://github.com/chokcoco/iCSS/issues/155)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/146188628-c878bc0c-e48e-48fc-99fb-1fc593580df8.gif)\r\n\r\n#### 119、[神奇的滤镜！巧妙实现内凹的平滑圆角](https://github.com/chokcoco/iCSS/issues/154)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/144423491-3df2c65a-0440-44ba-ad91-9c995a340c90.png)\r\n\r\n#### 118、[利用 clip-path 实现动态区域裁剪](https://github.com/chokcoco/iCSS/issues/153)\r\n\r\n#### 117、[使用 CSS 轻松实现一些高频出现的奇形怪状按钮](https://github.com/chokcoco/iCSS/issues/152)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/142734383-ec0e4dfd-38b0-4eeb-a1ee-b4e17c9488b6.png)\r\n\r\n#### 116、[巧用渐变实现高级感拉满的背景光动画](https://github.com/chokcoco/iCSS/issues/150)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/141609598-e0a1e420-2967-4ce4-8086-bfef1233f5f6.gif)\r\n\r\n#### 115、[巧用滤镜实现高级感拉满的文字快闪切换效果](https://github.com/chokcoco/iCSS/issues/149)\r\n\r\n<img width=400 src=\"https://user-images.githubusercontent.com/8554143/140746534-93b02f03-7624-4d1a-b832-9269f7c4f848.gif\" />\r\n\r\n#### 114、[3D 穿梭效果？使用 CSS 轻松搞定](https://github.com/chokcoco/iCSS/issues/148)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/141036836-b82282d9-a869-4d33-b031-567a38aed965.gif)\r\n\r\n#### 113、[仅仅使用 HTML/CSS 实现进度条的 N 种方式](https://github.com/chokcoco/iCSS/issues/146)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/130446725-8dc02d0e-8fc7-43b1-995c-a26f25392c0c.gif)\r\n\r\n#### 112、[CSS 奇技淫巧 | 巧妙实现文字二次加粗再加边框](https://github.com/chokcoco/iCSS/issues/145)\r\n\r\n#### 111、[利用 CSS Overview 面板重构优化你的网站](https://github.com/chokcoco/iCSS/issues/144)\r\n\r\n#### 110、[小技巧 | 一行代码实现头像与国旗的融合](https://github.com/chokcoco/iCSS/issues/143)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/135429916-18314111-bd24-406e-bc2c-9441040d0eec.png)\r\n\r\n#### 109、[CSS 奇技淫巧 | 妙用 drop-shadow 实现线条光影效果](https://github.com/chokcoco/iCSS/issues/142)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/135230872-8968c011-c078-43b7-9ae2-58e2a259824f.gif)\r\n\r\n#### 108、[CSS 奇技淫巧 | 妙用混合模式实现文字镂空波浪效果](https://github.com/chokcoco/iCSS/issues/140)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/134371611-a1b539b7-f0c5-4755-9312-b09ada72163f.gif)\r\n\r\n#### 107、[妙用 background 实现花式文字效果](https://github.com/chokcoco/iCSS/issues/138)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/132971163-84763256-220a-403c-bdfe-9b308996b950.gif)\r\n\r\n#### 106、[实现一个会动的鸿蒙 LOGO](https://github.com/chokcoco/iCSS/issues/137)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/132118280-7119c524-4b5c-4b9d-b851-aec507dd2ab4.gif)\r\n\r\n#### 105、[巧用模糊实现文字的 3D 效果](https://github.com/chokcoco/iCSS/issues/135)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/131250682-eba2d0eb-897b-4536-b728-7b5828828761.gif)\r\n\r\n#### 104、[奇思妙想 CSS 3D 动画 | 仅使用 CSS 能制作出多惊艳的动画？](https://github.com/chokcoco/iCSS/issues/132)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/130325052-c98c7552-c4eb-496d-84bc-6dd1c6f887e0.gif)\r\n\r\n#### 103、[CSS 奇思妙想 | 使用 resize 实现强大的图片拖拽切换预览功能](https://github.com/chokcoco/iCSS/issues/133)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/129591093-4d5255a1-f0b5-4983-8c7a-8ec919df6e66.gif)\r\n\r\n#### 102、[CSS 即将支持嵌套，SASS/LESS 等预处理器已无用武之地？](https://github.com/chokcoco/iCSS/issues/130)\r\n\r\n#### 101、[【Web动画】科技感十足的暗黑字符雨动画](https://github.com/chokcoco/iCSS/issues/129)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/127498604-2dc65906-b730-4e8f-b5f5-e10160007024.gif)\r\n\r\n#### 100、[CSS 世界中的方位与顺序](https://github.com/chokcoco/iCSS/issues/127)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/126026009-5d8c6c3f-d8dc-48c6-9943-9158ab0edf80.png)\r\n\r\n#### 99、[巧妙的实现带圆角的三角形](https://github.com/chokcoco/iCSS/issues/126)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/124783460-ab1f7580-df77-11eb-920d-3b9707130c90.png)\r\n\r\n#### 98、[CSS 奇思妙想 | 全兼容的毛玻璃效果](https://github.com/chokcoco/iCSS/issues/124)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/123645631-14f19e00-d859-11eb-9aaa-3b0032da89e0.gif)\r\n\r\n#### 97、[试试酷炫的 3D 视角](https://github.com/chokcoco/iCSS/issues/122)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/122393058-0f699d80-cfa7-11eb-98f8-53529b826e8b.gif)\r\n\r\n#### 96、[Web 动画原则及技巧浅析](https://github.com/chokcoco/iCSS/issues/121)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/121014822-fdc90e80-c7cc-11eb-93c1-e9940d6f8a4e.gif)\r\n\r\n#### 95、[CSS ::marker 让文字序号更有意思](https://github.com/chokcoco/iCSS/issues/119)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/118149756-70500400-b444-11eb-9dd8-4a0c113ea2f9.gif)\r\n\r\n#### 94、[Single Div 绘图技巧](https://github.com/chokcoco/iCSS/issues/120)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/120091257-091c9a00-c13c-11eb-93e1-d15301eae96e.png)\r\n\r\n#### 93、[新时代创意布局不完全指南](https://github.com/chokcoco/iCSS/issues/70)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/111740149-34267a00-88bf-11eb-8275-efc28a9b958b.png)\r\n\r\n#### 92、[有意思的 ::maker 伪元素](https://github.com/chokcoco/iCSS/issues/119)\r\n\r\n#### 91、[使用 CSS prefers-* 规范，提升网站的可访问性与健壮性](https://github.com/chokcoco/iCSS/issues/118)\r\n\r\n#### 90、[小技巧！CSS 提取图片主题色功能探索](https://github.com/chokcoco/iCSS/issues/114)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/116404188-fb37d880-a860-11eb-8ca4-c1ad469b8b34.png)\r\n\r\n#### 89、[一种巧妙的使用 CSS 制作波浪效果的思路](https://github.com/chokcoco/iCSS/issues/112)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/118101873-a96b8280-b40a-11eb-95be-6c57b3ccaad4.gif)\r\n\r\n#### 88、[探秘神奇的曲线动画 `motion-path`](https://github.com/chokcoco/iCSS/issues/113)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/116109914-d10edb00-a6e7-11eb-80e7-b261458fd4e8.gif)\r\n\r\n#### 87、[新时代布局中一些有意思的特性](https://github.com/chokcoco/iCSS/issues/111)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/115135561-019da780-a04c-11eb-8ca3-77411aa77d75.png)\r\n\r\n#### 86、[CSS 还能这样玩？奇思妙想渐变的艺术](https://github.com/chokcoco/iCSS/issues/110)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/115102289-88834f00-9f7c-11eb-9555-045376314b59.gif)\r\n\r\n#### 85、[CSS @property，让不可能变可能](https://github.com/chokcoco/iCSS/issues/109) \r\n\r\n![](https://user-images.githubusercontent.com/8554143/114727332-b5631680-9d70-11eb-9515-3e509688ef07.gif)\r\n\r\n#### 84、[CSS 文字装饰 text-decoration & text-emphasis](https://github.com/chokcoco/iCSS/issues/103)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/113866131-846a6b00-97df-11eb-90ae-32d29671dfaa.png)\r\n\r\n#### 83、[老生常谈之 CSS 实现三角形](https://github.com/chokcoco/iCSS/issues/108)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/113475759-143faa80-94aa-11eb-8a20-bcd1d50691cb.png)\r\n\r\n#### 82、[巧用 SVG 滤镜制作有意思动效](https://github.com/chokcoco/iCSS/issues/107)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/112725701-8c5f1b00-8f54-11eb-96e7-ad5a5b83b9f3.gif)\r\n\r\n#### 81、[有意思！不规则边框的生成方案](https://github.com/chokcoco/iCSS/issues/106) \r\n\r\n![](https://user-images.githubusercontent.com/8554143/112712156-922f0f00-8f08-11eb-9f39-068799539d83.png)\r\n\r\n#### 80、[小技巧！CSS 整块文本溢出省略特性探究](https://github.com/chokcoco/iCSS/issues/102)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/110193533-5fe14300-7e6f-11eb-817b-076dae1b174f.png)\r\n\r\n#### 79、[奇思妙想 CSS 文字动画](https://github.com/chokcoco/iCSS/issues/101)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/109828417-948aaa00-7c77-11eb-876f-d2cd863b74ac.gif)\r\n\r\n#### 78、[巧用 `-webkit-box-reflect` 倒影实现各类动效](https://github.com/chokcoco/iCSS/issues/100)\r\n![](https://user-images.githubusercontent.com/8554143/108523825-530a0e80-7309-11eb-84e4-dad148e2c731.gif)\r\n\r\n#### 77、[使用 mask 实现视频弹幕人物遮罩过滤](https://github.com/chokcoco/iCSS/issues/98)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/107122321-0ddde980-68d2-11eb-8457-b3ad6c760c98.gif)\r\n\r\n#### 76、[你可能不知道的 transition 技巧与细节](https://github.com/chokcoco/iCSS/issues/96)\r\n![](https://user-images.githubusercontent.com/8554143/106390351-05456900-6423-11eb-9800-6ffd1d3eeef6.gif)\r\n\r\n#### 75、[CSS奇思妙想 -- 使用 CSS 创造艺术图案](https://github.com/chokcoco/iCSS/issues/94)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/105871313-23caef00-6034-11eb-8ad9-4133502103f7.png)\r\n\r\n#### 74、[生僻标签 fieldset 与 legend 的妙用](https://github.com/chokcoco/iCSS/issues/93)\r\n\r\n<img width=\"200\" src=\"https://user-images.githubusercontent.com/8554143/105197984-2c28b300-5b78-11eb-96e2-26213af4cd90.png\">\r\n\r\n#### 73、[CSS 奇思妙想边框动画](https://github.com/chokcoco/iCSS/issues/92)\r\n\r\n<img width=\"600\" src=\"https://user-images.githubusercontent.com/8554143/104848230-f9d04900-591e-11eb-8ecf-7915010f7bcd.gif\">\r\n\r\n#### 72、[CSS 技巧一则：动态高度过渡动画](https://github.com/chokcoco/iCSS/issues/91)\r\n\r\n#### 71、[如何不使用 `overflow: hidden` 实现 `overflow: hidden`？](https://github.com/chokcoco/iCSS/issues/90)\r\n\r\n#### 70、[水平垂直居中深入挖掘](https://github.com/chokcoco/iCSS/issues/89)\r\n\r\n#### 69、[一行 CSS 代码的魅力](https://github.com/chokcoco/iCSS/issues/87)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/103553627-ad314a80-4ee8-11eb-8093-7599116319ad.gif)\r\n\r\n#### 68、[使用纯 CSS 实现滚动阴影效果](https://github.com/chokcoco/iCSS/issues/86)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/103438190-671b8300-4c6b-11eb-957f-f7557dc83619.gif)\r\n\r\n#### 67、[探究 position-sticky 失效问题](https://github.com/chokcoco/iCSS/issues/85)\r\n\r\n#### 66、[CSS 艺术 -- 使用 background 创造各种美妙的背景](https://github.com/chokcoco/iCSS/issues/84)\r\n\r\n![bg9](https://user-images.githubusercontent.com/8554143/100546640-6030d780-329d-11eb-83f8-457090c76bae.gif)\r\n\r\n#### 65、[使用 tabindex 配合 focus-within 巧妙实现父选择器](https://github.com/chokcoco/iCSS/issues/83)\r\n\r\n#### 64、[CSS 技巧一则 -- 不定宽溢出文本适配滚动](https://github.com/chokcoco/iCSS/issues/81)\r\n\r\n![textscroll](https://user-images.githubusercontent.com/8554143/88412686-871d5f80-ce0c-11ea-9649-becdae112f30.gif)\r\n\r\n#### 63、[奇妙的 CSS MASK](https://github.com/chokcoco/iCSS/issues/80)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/86525662-ab94b480-bebc-11ea-9824-b4f6e0536da7.gif)\r\n\r\n#### 62、[使用 display: contents 增强页面语义](https://github.com/chokcoco/iCSS/issues/79)\r\n\r\n#### 61、[CSS 故障艺术](https://github.com/chokcoco/iCSS/issues/78)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/73518406-2acb1b80-4439-11ea-84c3-c83e51601bac.gif)\r\n\r\n#### 60、[巧妙实现带圆角的渐变边框](https://github.com/chokcoco/iCSS/issues/77) \r\n\r\n![](https://user-images.githubusercontent.com/8554143/73247942-f4995c00-41ec-11ea-97d3-17250b0f0378.gif)\r\n\r\n#### 59、[深入理解 CSS（Cascading Style Sheets）中的层叠（Cascading）](https://github.com/chokcoco/iCSS/issues/76)\r\n\r\n#### 58、[巧用 CSS 实现酷炫的充电动画](https://github.com/chokcoco/iCSS/issues/75)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/71305432-e9ddf100-240e-11ea-81a9-b64bc3db8c21.gif)\r\n\r\n#### 57、[使用 sroll-snap-type 优化滚动](https://github.com/chokcoco/iCSS/issues/74)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/70142474-471a3880-16d4-11ea-8d0b-b6623049d376.gif)\r\n\r\n#### 56、[在 CSS 中使用三角函数绘制曲线图形及展示动画](https://github.com/chokcoco/iCSS/issues/72)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/69351842-e5f76b80-0cb6-11ea-8c3c-b6eee35dff01.gif)\r\n\r\n#### 55、[CSS 阴影动画优化技巧一则](https://github.com/chokcoco/iCSS/issues/71)\r\n\r\n#### 54、[Web 字体 font-family 再探秘](https://github.com/chokcoco/iCSS/issues/69)\r\n\r\n#### 53、[你所不知道的 CSS 负值技巧与细节](https://github.com/chokcoco/iCSS/issues/68)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/62633633-0a794300-b967-11e9-8cca-c5f0e1087915.gif)\r\n\r\n#### 52、[A Guide to CSS Rules](https://github.com/chokcoco/iCSS/issues/67)\r\n\r\n#### 51、[CSS 属性选择器的深入挖掘](https://github.com/chokcoco/iCSS/issues/65)\r\n\r\n#### 50、[探秘 flex 上下文中神奇的自动 margin](https://github.com/chokcoco/iCSS/issues/64)\r\n\r\n#### 49、[巧妙使用 CSS 控制动画行进](https://github.com/chokcoco/iCSS/issues/63)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/55224859-422dec80-5209-11e9-9297-eba3bb834246.gif)\r\n\r\n#### 48、[CSS 火焰，不在话下](https://github.com/chokcoco/iCSS/issues/62)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/54913205-23b6b100-4eea-11e9-9b01-910b30840fb6.gif)\r\n\r\n#### 47、[不可思议的纯 CSS 实现鼠标跟随](https://github.com/chokcoco/iCSS/issues/46)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/53795509-1b172e80-3f6d-11e9-9836-4fc545e9094a.gif)\r\n\r\n#### 46、[有趣的 `box-decoration-break`](https://github.com/chokcoco/iCSS/issues/45)\r\n\r\n#### 45、[不可思议的纯 CSS 进度条效果](https://github.com/chokcoco/iCSS/issues/43)\r\n\r\n#### 44、[探究 CSS 混合模式\\滤镜导致 CSS 3D 失效问题](https://github.com/chokcoco/iCSS/issues/41)\r\n\r\n#### 43、[你所不知道的 CSS 阴影技巧与细节](https://github.com/chokcoco/iCSS/issues/39)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/48009747-415fc580-e157-11e8-8612-25dd32825551.png)\r\n\r\n#### 42、[滚动视差? CSS不在话下](https://github.com/chokcoco/iCSS/issues/37)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/43909161-3ff2bd1c-9c2c-11e8-932e-c21d0d031233.gif)\r\n\r\n#### 41、[神奇的选择器 `:focus-within`](https://github.com/chokcoco/iCSS/issues/36)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/43517148-48c40d10-95ba-11e8-9c42-61567daae18d.gif)\r\n\r\n#### 40、[Pure CSS Button Effect](//codepen.io/Chokcoco/pen/MGPwLg)\r\n\r\n#### 39、[妙用 scale 与 transfrom-origin，精准控制动画方向](https://github.com/chokcoco/iCSS/issues/34)\r\n\r\n#### 38、[不可思议的纯 CSS 导航栏下划线跟随效果](https://github.com/chokcoco/iCSS/issues/33)\r\n\r\n如何使用纯 CSS 制作下述下划线跟随效果？\r\n\r\n![underline](https://user-images.githubusercontent.com/8554143/37917279-8f6fd236-3150-11e8-8b8d-fca96d1d6001.gif)\r\n\r\n#### 37、[两行 CSS 代码实现图片任意颜色赋色技术](https://github.com/chokcoco/iCSS/issues/32)\r\n\r\n#### 36、[`text-fill-color` 与 `color` 的异同](https://github.com/chokcoco/iCSS/issues/17)\r\n\r\n#### 35、[你所不知道的 CSS 滤镜技巧与细节](https://github.com/chokcoco/iCSS/issues/30)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/30364781-3997cd10-9898-11e7-8c25-c54c6b6db705.gif)\r\n\r\n#### 34、[你所不知道的 CSS 动画技巧与细节](https://github.com/chokcoco/iCSS/issues/27)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/29759170-ca7c7768-8beb-11e7-8f8b-981c4e69f36c.gif)\r\n\r\n#### 33、[fixed 定位失效 || 不受控制的 `position:fixed`](https://github.com/chokcoco/iCSS/issues/24)\r\n\r\n#### 32、[CSS 新特性`contain`，控制页面的重绘与重排](https://github.com/chokcoco/iCSS/issues/23)\r\n\r\n#### 31、[纯 CSS 实现波浪效果!](https://github.com/chokcoco/iCSS/issues/22)\r\n\r\n![CSSWaVe](https://user-images.githubusercontent.com/8554143/28265378-f9bf3f82-6b21-11e7-83eb-e240b40b550a.gif)\r\n\r\n#### 30、[奇妙的 CSS shapes（CSS图形）](https://github.com/chokcoco/iCSS/issues/18)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/27017159-f7fb0fe4-4f58-11e7-8397-a467cc9662ab.gif)\r\n\r\n#### 29、[不可思议的混合模式 `background-blend-mode`](https://github.com/chokcoco/iCSS/issues/31)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/34141560-07988702-e4bd-11e7-96bc-e3ae8e931230.gif)\r\n\r\n#### 28、[不可思议的混合模式 `mix-blend-mode` ](https://github.com/chokcoco/iCSS/issues/16)\r\n\r\n![](https://cloud.githubusercontent.com/assets/8554143/25800098/7dccd4fe-341a-11e7-832c-f321e77ddac7.gif)\r\n\r\n#### 27、[神奇的 `conic-gradient` 角向渐变](https://github.com/chokcoco/iCSS/issues/19)\r\n\r\n![](https://user-images.githubusercontent.com/8554143/27471835-c0749fe6-582b-11e7-91eb-9be5ed6610f3.gif)\r\n\r\n#### 26、[奇妙的`-webkit-background-clip: text`](https://github.com/chokcoco/iCSS/issues/14)\r\n\r\n#### 25、[vh、vw、vmin、vmax 知多少](https://github.com/chokcoco/iCSS/issues/15)\r\n\r\n#### 24、[纯 CSS 实现瀑布流布局](https://github.com/chokcoco/iCSS/issues/40)\r\n\r\n#### 23、[谈谈 CSS 关键字 initial、inherit 和 unset](https://github.com/chokcoco/iCSS/issues/13)\r\n\r\n#### 22、[纯 CSS 方式实现 CSS 动画的暂停与播放](https://github.com/chokcoco/iCSS/issues/12)\r\n\r\n#### 21、[提高 CSS 动画性能的正确姿势 | 盒子端 CSS 动画性能提升研究](https://github.com/chokcoco/iCSS/issues/11)\r\n\r\n#### 20、[巧妙地制作背景色渐变动画！](https://github.com/chokcoco/iCSS/issues/10)\r\n\r\n如何实现下述的背景色渐变动画？\r\n\r\n![lineargradient](https://cloud.githubusercontent.com/assets/8554143/24186346/d984600a-0f12-11e7-8220-dc9a6c04b7ef.gif)\r\n\r\n#### 19、[深入探讨 CSS 特性检测 @supports 与 Modernizr](https://github.com/chokcoco/iCSS/issues/9)\r\n\r\n#### 18、[使用 `position:sticky` 实现粘性布局](https://github.com/chokcoco/iCSS/issues/8)\r\n\r\n![](https://cloud.githubusercontent.com/assets/8554143/22967003/97af8828-f39f-11e6-82db-55405160eea3.gif)\r\n\r\n#### 17、[再探究字体的渲染规则及 fallback 机制](https://github.com/chokcoco/iCSS/issues/7)\r\n\r\n#### 16、[你该知道的字体 `font-family`](https://github.com/chokcoco/iCSS/issues/6)\r\n\r\n#### 15、[`reset.css` 知多少 ](https://github.com/chokcoco/iCSS/issues/5)\r\n\r\n#### 14、[CSS命名方式是否有必要规范](https://github.com/chokcoco/iCSS/issues/59)\r\n\r\n#### 13、[引人瞩目的 CSS 自定义属性（CSS Variable）](https://github.com/chokcoco/iCSS/issues/58)\r\n\r\n#### 12、[结构性伪类选择器](https://github.com/chokcoco/iCSS/issues/57)\r\n\r\n#### 11、[IFC、BFC、GFC 与 FFC 知多少](https://github.com/chokcoco/iCSS/issues/56)\r\n\r\n#### 10、[巧妙的实现 CSS 斜线](https://github.com/chokcoco/iCSS/issues/2)\r\n\r\n使用单个标签，如何实现下图所示的斜线效果:\r\n\r\n![CSS slash](http://images2015.cnblogs.com/blog/608782/201611/608782-20161103132531986-482520887.png)\r\n\r\n#### 9、[巧妙的多列等高布局](https://github.com/chokcoco/iCSS/issues/55)\r\n\r\n规定下面的布局，实现多列等高布局，要求两列背景色等高。\r\n\r\n``` HTML\r\n<div class=\"container\">\r\n    <div class=\"left\">多列等高布局左</div> \r\n    <div class=\"right\">多列等高布局右</div>\r\n</div>\r\n```\r\n\r\n#### 8、[纯CSS的导航栏Tab切换方案](https://github.com/chokcoco/iCSS/issues/54)\r\n\r\n不用 `Javascript`，使用纯 `CSS` 方案，实现类似下图的导航栏 Tab 切换：\r\n\r\n![纯CSS的导航栏切换方案](https://user-images.githubusercontent.com/8554143/87443012-8aa03200-c627-11ea-9325-b1c39331263b.gif)\r\n\r\n#### 7、[全兼容的最后一条边界线问题](https://github.com/chokcoco/iCSS/issues/53)\r\n\r\n看看下图，常见于一些导航栏中，要求每行中最后一列的右边框消失，如何在所有浏览器中最便捷最优雅的实现？\r\n\r\n#### 6、[全兼容的多列均匀布局问题](https://github.com/chokcoco/iCSS/issues/52)\r\n\r\n如何实现下列这种多列均匀布局：\r\n\r\n![image](https://user-images.githubusercontent.com/8554143/87442550-03eb5500-c627-11ea-80f5-ada17a79a6d0.png)\r\n\r\n#### 5、[纯 CSS 实现单行居中显示文字，多行居左显示，最多两行超过用省略号结尾](https://github.com/chokcoco/iCSS/issues/50)\r\n\r\n![image](https://user-images.githubusercontent.com/8554143/97247656-0f136b00-183b-11eb-8d8f-fb27af99a04b.png)\r\n\r\n#### 4、[从倒影说起，谈谈 CSS 继承 inherit](https://github.com/chokcoco/iCSS/issues/49)\r\n\r\n#### 3、[层叠顺序（stacking level）与堆栈上下文（stacking context）知多少？](https://github.com/chokcoco/iCSS/issues/48)\r\n\r\n#### 2、[类似下面这样的条纹边框，只使用一个标签，可以有多少种实现方式 -- 从条纹边框的实现谈盒子模型：](https://github.com/chokcoco/iCSS/issues/1)\r\n\r\n![image](https://user-images.githubusercontent.com/8554143/97247472-b6dc6900-183a-11eb-8331-ed952e4c2a1c.png)\r\n\r\n#### 1、[下面这个左边竖条图形，只使用一个标签，可以有多少种实现方式：](https://github.com/chokcoco/iCSS/issues/51)\r\n\r\n![image](https://user-images.githubusercontent.com/8554143/87442343-c686c780-c626-11ea-871a-d95f3176f6a4.png)\r\n\r\n## Stargazers over time\r\n\r\n[![Stargazers over time](https://starchart.cc/chokcoco/iCSS.svg)](https://starchart.cc/chokcoco/iCSS)\r\n\r\n\r\n"
  },
  {
    "path": "website/README.md",
    "content": "# iCSS 网站\n\n基于 GitHub 上的 iCSS 仓库构建的现代化网站，展示 CSS 奇技淫巧。\n\n## 功能特性\n\n### 🎨 主题切换\n- **亮色主题**: 适合日间使用\n- **暗色主题**: 适合夜间使用，护眼\n- **跟随系统**: 自动跟随系统主题设置\n- **持久化**: 主题选择会保存到本地存储\n\n### 🌍 多语言支持\n- **中文**: 简体中文界面\n- **English**: 英文界面\n- **持久化**: 语言选择会保存到本地存储\n\n### 📱 响应式设计\n- 支持桌面端、平板和移动端\n- 自适应布局，提供最佳用户体验\n\n### 🎯 核心功能\n- **文章列表**: 展示所有 CSS 技巧文章\n- **分类筛选**: 按分类筛选文章\n- **搜索功能**: 支持文章标题搜索\n- **文章详情**: 完整的文章内容展示\n- **代码高亮**: 支持语法高亮的代码块\n- **CodePen 集成**: 直接嵌入 CodePen 演示\n- **上一篇/下一篇**: 文章导航功能\n\n## 技术栈\n\n- **框架**: Next.js 14 (App Router)\n- **语言**: TypeScript\n- **样式**: Tailwind CSS\n- **状态管理**: React Context\n- **动画**: Framer Motion\n- **图标**: Lucide React\n- **代码高亮**: react-syntax-highlighter\n\n## 快速开始\n\n### 安装依赖\n```bash\npnpm install\n```\n\n### 启动开发服务器\n```bash\npnpm dev\n```\n\n### 构建生产版本\n```bash\npnpm build\n```\n\n### 启动生产服务器\n```bash\npnpm start\n```\n\n## 项目结构\n\n```\nicss-website/\n├── app/\n│   ├── api/                 # API 路由\n│   ├── article/            # 文章详情页\n│   ├── components/         # 可复用组件\n│   ├── contexts/           # React Context\n│   ├── lib/                # 工具函数\n│   ├── globals.css         # 全局样式\n│   ├── layout.tsx          # 根布局\n│   └── page.tsx            # 首页\n├── public/                 # 静态资源\n└── package.json\n```\n\n## 主题系统\n\n### 主题配置\n主题系统基于 CSS 变量和 Tailwind CSS 的暗色模式实现：\n\n- **CSS 变量**: 定义主题色彩\n- **Tailwind 配置**: 支持 `dark:` 前缀的类名\n- **Context API**: 管理主题状态\n- **localStorage**: 持久化主题选择\n\n### 自定义主题\n可以通过修改 `app/globals.css` 中的 CSS 变量来自定义主题色彩：\n\n```css\n:root {\n  --primary: 221.2 83.2% 53.3%;\n  --background: 0 0% 100%;\n  /* 更多变量... */\n}\n\n.dark {\n  --primary: 217.2 91.2% 59.8%;\n  --background: 222.2 84% 4.9%;\n  /* 更多变量... */\n}\n```\n\n## 多语言系统\n\n### 翻译文件\n翻译内容定义在 `app/lib/translations.ts` 中：\n\n```typescript\nexport const translations: Record<Language, Translations> = {\n  zh: {\n    loading: '加载中...',\n    error: '错误',\n    // 更多翻译...\n  },\n  en: {\n    loading: 'Loading...',\n    error: 'Error',\n    // 更多翻译...\n  }\n};\n```\n\n### 使用翻译\n在组件中使用 `useApp` hook 获取翻译函数：\n\n```typescript\nimport { useApp } from '../contexts/AppContext';\n\nfunction MyComponent() {\n  const { t } = useApp();\n  \n  return <div>{t('loading')}</div>;\n}\n```\n\n## API 接口\n\n### 文章列表\n```\nGET /api/articles?page=1&per_page=12&category=CSS&search=关键词\n```\n\n### 文章详情\n```\nGET /api/articles/[id]\n```\n\n### 分类列表\n```\nGET /api/categories\n```\n\n## 测试页面\n\n访问以下页面测试功能：\n\n- **首页**: `http://localhost:3000`\n- **主题语言测试**: `http://localhost:3000/test-theme-lang`\n- **API 测试**: `http://localhost:3000/test-api`\n- **Demo 测试**: `http://localhost:3000/test-demo`\n\n## 部署\n\n### Vercel 部署\n1. 将代码推送到 GitHub\n2. 在 Vercel 中导入项目\n3. 配置环境变量（如需要）\n4. 自动部署\n\n### 其他平台\n项目支持部署到任何支持 Next.js 的平台：\n- Netlify\n- Railway\n- DigitalOcean App Platform\n- 自托管服务器\n\n## 贡献\n\n欢迎提交 Issue 和 Pull Request！\n\n## 许可证\n\nMIT License "
  },
  {
    "path": "website/THEME_LANG_FEATURES.md",
    "content": "# 主题和语言切换功能说明\n\n## 🎨 主题切换功能\n\n### 功能特性\n- **亮色主题**: 适合日间使用，白色背景\n- **暗色主题**: 适合夜间使用，深色背景，护眼\n- **跟随系统**: 自动跟随系统主题设置\n- **持久化**: 主题选择会保存到 localStorage\n\n### 实现方式\n1. **CSS 变量系统**: 使用 CSS 自定义属性定义主题色彩\n2. **Tailwind 暗色模式**: 支持 `dark:` 前缀的类名\n3. **Context API**: React Context 管理主题状态\n4. **localStorage**: 持久化用户选择\n\n### 文件结构\n```\napp/\n├── lib/\n│   └── theme.ts              # 主题管理工具\n├── contexts/\n│   └── AppContext.tsx        # 应用上下文\n├── components/\n│   └── ThemeToggle.tsx       # 主题切换组件\n└── globals.css               # 全局样式和主题变量\n```\n\n### 使用方法\n```typescript\nimport { useApp } from '../contexts/AppContext';\n\nfunction MyComponent() {\n  const { theme, setTheme } = useApp();\n  \n  return (\n    <div className=\"bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100\">\n      <button onClick={() => setTheme('dark')}>\n        切换到暗色主题\n      </button>\n    </div>\n  );\n}\n```\n\n## 🌍 多语言支持\n\n### 功能特性\n- **中文**: 简体中文界面\n- **English**: 英文界面\n- **持久化**: 语言选择会保存到 localStorage\n- **实时切换**: 无需刷新页面即可切换语言\n\n### 实现方式\n1. **翻译文件**: 集中管理所有翻译内容\n2. **Context API**: React Context 管理语言状态\n3. **翻译函数**: 提供 `t()` 函数获取翻译\n4. **localStorage**: 持久化用户选择\n\n### 文件结构\n```\napp/\n├── lib/\n│   ├── language.ts           # 语言管理工具\n│   └── translations.ts       # 翻译文件\n├── contexts/\n│   └── AppContext.tsx        # 应用上下文\n└── components/\n    └── LanguageToggle.tsx    # 语言切换组件\n```\n\n### 使用方法\n```typescript\nimport { useApp } from '../contexts/AppContext';\n\nfunction MyComponent() {\n  const { t, language, setLanguage } = useApp();\n  \n  return (\n    <div>\n      <h1>{t('title')}</h1>\n      <p>{t('description')}</p>\n      <button onClick={() => setLanguage('en')}>\n        切换到英文\n      </button>\n    </div>\n  );\n}\n```\n\n## 🎯 核心组件\n\n### ThemeToggle 组件\n- 下拉菜单式主题选择\n- 图标和文字显示\n- 点击外部自动关闭\n- 支持键盘导航\n\n### LanguageToggle 组件\n- 下拉菜单式语言选择\n- 国旗图标显示\n- 点击外部自动关闭\n- 支持键盘导航\n\n### AppContext 上下文\n- 统一管理主题和语言状态\n- 提供翻译函数\n- 处理服务端渲染兼容性\n- 防止闪烁问题\n\n## 🎨 样式系统\n\n### CSS 变量\n```css\n:root {\n  --background: 0 0% 100%;\n  --foreground: 222.2 84% 4.9%;\n  --primary: 221.2 83.2% 53.3%;\n  /* 更多变量... */\n}\n\n.dark {\n  --background: 222.2 84% 4.9%;\n  --foreground: 210 40% 98%;\n  --primary: 217.2 91.2% 59.8%;\n  /* 更多变量... */\n}\n```\n\n### Tailwind 配置\n```javascript\nmodule.exports = {\n  darkMode: 'class',\n  theme: {\n    extend: {\n      colors: {\n        background: 'hsl(var(--background))',\n        foreground: 'hsl(var(--foreground))',\n        // 更多颜色...\n      }\n    }\n  }\n}\n```\n\n## 📱 响应式设计\n\n### 移动端适配\n- 主题和语言切换按钮在小屏幕上自动调整布局\n- 下拉菜单在移动端有合适的定位\n- 触摸友好的交互设计\n\n### 无障碍支持\n- 支持键盘导航\n- 适当的 ARIA 标签\n- 高对比度支持\n\n## 🔧 技术细节\n\n### 服务端渲染兼容性\n- 使用 `suppressHydrationWarning` 防止水合警告\n- 客户端初始化时处理主题和语言\n- 防止闪烁的加载状态\n\n### 性能优化\n- 使用 `useCallback` 优化函数引用\n- 使用 `useMemo` 缓存计算结果\n- 避免不必要的重新渲染\n\n### 错误处理\n- 优雅降级到默认主题和语言\n- 处理 localStorage 不可用的情况\n- 网络错误时的备用方案\n\n## 🧪 测试\n\n### 测试页面\n访问 `http://localhost:3000/test-theme-lang` 可以测试：\n- 主题切换功能\n- 语言切换功能\n- 翻译内容显示\n- 样式适配效果\n\n### 测试内容\n- 主题切换是否正常工作\n- 语言切换是否正常工作\n- 持久化是否生效\n- 响应式布局是否正常\n- 无障碍功能是否正常\n\n## 🚀 部署注意事项\n\n### 环境变量\n确保生产环境支持：\n- `NEXT_PUBLIC_` 前缀的环境变量\n- 静态资源路径配置\n\n### 构建优化\n- 确保 TypeScript 编译通过\n- 检查 ESLint 规则\n- 优化包大小\n\n### 浏览器兼容性\n- 支持现代浏览器的 CSS 变量\n- 支持 localStorage API\n- 支持 CSS Grid 和 Flexbox\n\n## 📝 扩展指南\n\n### 添加新主题\n1. 在 `app/lib/theme.ts` 中添加新主题配置\n2. 在 `app/globals.css` 中添加对应的 CSS 变量\n3. 在 `app/components/ThemeToggle.tsx` 中添加新主题选项\n\n### 添加新语言\n1. 在 `app/lib/language.ts` 中添加新语言配置\n2. 在 `app/lib/translations.ts` 中添加新语言翻译\n3. 在 `app/components/LanguageToggle.tsx` 中添加新语言选项\n\n### 添加新翻译\n1. 在 `app/lib/translations.ts` 的 `Translations` 接口中添加新键\n2. 在中文和英文翻译对象中添加对应翻译\n3. 在组件中使用 `t('newKey')` 获取翻译\n\n## 🎉 总结\n\n主题和语言切换功能已经成功集成到网站中，提供了：\n\n✅ **完整的主题系统**: 支持亮色、暗色、跟随系统三种模式\n✅ **多语言支持**: 支持中文和英文切换\n✅ **持久化存储**: 用户选择会保存到本地\n✅ **响应式设计**: 完美适配各种设备\n✅ **无障碍支持**: 支持键盘导航和屏幕阅读器\n✅ **性能优化**: 流畅的切换体验\n✅ **易于扩展**: 可以轻松添加新主题和语言\n\n用户现在可以：\n- 在网站右上角切换主题和语言\n- 享受个性化的浏览体验\n- 在不同设备间保持设置同步\n- 获得更好的可访问性支持 "
  },
  {
    "path": "website/app/api/articles/[id]/route.ts",
    "content": "import { NextRequest, NextResponse } from 'next/server';\nimport { GitHubAPI } from '@/app/lib/github';\n\nexport async function GET(\n  req: NextRequest,\n  { params }: { params: { id: string } }\n) {\n  try {\n    const { id } = params;\n    const issueNumber = parseInt(id);\n    \n    if (isNaN(issueNumber)) {\n      return NextResponse.json(\n        { error: '无效的文章 ID' }, \n        { status: 400 }\n      );\n    }\n    \n    const issueData = await GitHubAPI.getIssue(issueNumber);\n    \n    return NextResponse.json(issueData);\n  } catch (error) {\n    console.error('Error fetching article:', error);\n    return NextResponse.json(\n      { error: '获取文章详情失败' }, \n      { status: 500 }\n    );\n  }\n} "
  },
  {
    "path": "website/app/api/articles/route.ts",
    "content": "import { NextRequest, NextResponse } from 'next/server';\nimport { GitHubAPI } from '@/app/lib/github';\n\nexport async function GET(request: NextRequest) {\n  try {\n    const { searchParams } = new URL(request.url);\n    const page = parseInt(searchParams.get('page') || '1');\n    const perPage = parseInt(searchParams.get('per_page') || '30');\n    const category = searchParams.get('category');\n    const search = searchParams.get('search');\n    \n    // 获取所有文章\n    const allArticles = await GitHubAPI.getAllArticles();\n    \n    // 确保 allArticles 是数组\n    if (!Array.isArray(allArticles)) {\n      console.error('GitHub API returned non-array data:', allArticles);\n      return NextResponse.json({\n        articles: [],\n        pagination: {\n          page,\n          perPage,\n          total: 0,\n          totalPages: 0,\n          hasNext: false,\n          hasPrev: false\n        },\n        error: '数据格式错误'\n      });\n    }\n    \n    // 如果文章列表为空，可能是API限流或网络问题\n    if (allArticles.length === 0) {\n      console.warn('No articles returned from GitHub API, possible rate limiting');\n      return NextResponse.json({\n        articles: [],\n        pagination: {\n          page,\n          perPage,\n          total: 0,\n          totalPages: 0,\n          hasNext: false,\n          hasPrev: false\n        },\n        error: '暂时无法获取文章数据，请稍后再试'\n      });\n    }\n    \n    // 应用分类过滤\n    let filteredArticles = allArticles;\n    if (category && category !== '全部') {\n      filteredArticles = allArticles.filter(article => \n        article.category === category\n      );\n    }\n    \n    // 应用搜索过滤\n    if (search) {\n      const searchLower = search.toLowerCase();\n      filteredArticles = filteredArticles.filter(article =>\n        article.title.toLowerCase().includes(searchLower) ||\n        (article.category && article.category.toLowerCase().includes(searchLower))\n      );\n    }\n    \n    // 应用分页\n    const startIndex = (page - 1) * perPage;\n    const endIndex = startIndex + perPage;\n    const paginatedArticles = filteredArticles.slice(startIndex, endIndex);\n    \n    // 计算分页信息\n    const total = filteredArticles.length;\n    const totalPages = Math.ceil(total / perPage);\n    \n    return NextResponse.json({\n      articles: paginatedArticles,\n      pagination: {\n        page,\n        perPage,\n        total,\n        totalPages,\n        hasNext: page < totalPages,\n        hasPrev: page > 1\n      }\n    });\n  } catch (error) {\n    console.error('Error fetching articles:', error);\n    return NextResponse.json({\n      articles: [],\n      pagination: {\n        page: 1,\n        perPage: 30,\n        total: 0,\n        totalPages: 0,\n        hasNext: false,\n        hasPrev: false\n      },\n      error: '获取文章数据失败，请稍后再试'\n    }, { status: 200 });\n  }\n} "
  },
  {
    "path": "website/app/api/categories/route.ts",
    "content": "import { NextResponse } from 'next/server';\nimport { GitHubAPI } from '@/app/lib/github';\n\nexport async function GET() {\n  try {\n    // 获取所有文章\n    const allArticles = await GitHubAPI.getAllArticles();\n    \n    // 确保 allArticles 是数组\n    if (!Array.isArray(allArticles)) {\n      console.error('GitHub API returned non-array data:', allArticles);\n      return NextResponse.json(['全部'], { status: 200 });\n    }\n    \n    // 提取所有分类\n    const categories = new Set<string>();\n    allArticles.forEach(article => {\n      if (article.category) {\n        categories.add(article.category);\n      }\n    });\n    \n    // 转换为数组并排序\n    const categoryList = Array.from(categories).sort();\n    \n    // 添加\"全部\"选项\n    categoryList.unshift('全部');\n    \n    return NextResponse.json(categoryList);\n  } catch (error) {\n    console.error('Error fetching categories:', error);\n    // 返回默认分类列表，避免前端报错\n    return NextResponse.json(['全部'], { status: 200 });\n  }\n} "
  },
  {
    "path": "website/app/article/[id]/page.tsx",
    "content": "'use client';\n\nimport { useState, useEffect } from 'react';\nimport { useParams, useRouter } from 'next/navigation';\nimport { motion } from 'framer-motion';\nimport { ArrowLeft, ExternalLink, Calendar, User, Eye, ArrowRight } from 'lucide-react';\nimport Image from 'next/image';\nimport CodeBlock from '../../components/CodeBlock';\nimport { useApp } from '../../contexts/AppContext';\n\ninterface Article {\n  id: number;\n  title: string;\n  url: string;\n  image?: string;\n  category?: string;\n}\n\ninterface IssueData {\n  body: string;\n  created_at: string;\n  user: {\n    login: string;\n    avatar_url: string;\n  };\n  comments: number;\n  reactions: {\n    total_count: number;\n  };\n}\n\n// 解析 Markdown 内容\nconst parseMarkdown = (text: string): React.ReactNode[] => {\n  if (!text) return [];\n  \n  const lines = text.split('\\n');\n  const elements: React.ReactNode[] = [];\n  let currentCodeBlock = '';\n  let inCodeBlock = false;\n  let codeLanguage = '';\n  let inList = false;\n  let listItems: React.ReactNode[] = [];\n  \n  for (let i = 0; i < lines.length; i++) {\n    const line = lines[i];\n    \n    // 检测代码块开始\n    if (line.startsWith('```')) {\n      // 结束当前列表\n      if (inList && listItems.length > 0) {\n        elements.push(\n          <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n            {listItems}\n          </ul>\n        );\n        listItems = [];\n        inList = false;\n      }\n      \n      if (!inCodeBlock) {\n        // 开始代码块\n        inCodeBlock = true;\n        codeLanguage = line.slice(3).trim() || 'text';\n        currentCodeBlock = '';\n      } else {\n        // 结束代码块\n        inCodeBlock = false;\n        if (currentCodeBlock.trim()) {\n          // 检测是否是 CodePen 格式\n          const isCodePen = codeLanguage === 'codepen' || \n                           currentCodeBlock.includes('<!-- CodePen Demo -->') ||\n                           currentCodeBlock.includes('<!-- HTML -->') ||\n                           currentCodeBlock.includes('<!-- CSS -->') ||\n                           currentCodeBlock.includes('<!-- JavaScript -->');\n          \n          elements.push(\n            <CodeBlock key={`code-${i}`} language={isCodePen ? 'codepen' : codeLanguage}>\n              {currentCodeBlock.trim()}\n            </CodeBlock>\n          );\n        }\n        currentCodeBlock = '';\n        continue;\n      }\n    } else if (inCodeBlock) {\n      // 在代码块内\n      currentCodeBlock += line + '\\n';\n    } else {\n      // 普通文本处理\n      if (line.trim() === '') {\n        // 结束当前列表\n        if (inList && listItems.length > 0) {\n          elements.push(\n            <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n              {listItems}\n            </ul>\n          );\n          listItems = [];\n          inList = false;\n        }\n        elements.push(<br key={`br-${i}`} />);\n      } else if (line.startsWith('### ')) {\n        // 结束当前列表\n        if (inList && listItems.length > 0) {\n          elements.push(\n            <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n              {listItems}\n            </ul>\n          );\n          listItems = [];\n          inList = false;\n        }\n        elements.push(\n          <h3 key={`h3-${i}`} className=\"text-lg font-bold text-gray-900 dark:text-gray-100 mt-6 mb-4\">\n            {parseInlineMarkdown(line.slice(4))}\n          </h3>\n        );\n      } else if (line.startsWith('## ')) {\n        // 结束当前列表\n        if (inList && listItems.length > 0) {\n          elements.push(\n            <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n              {listItems}\n            </ul>\n          );\n          listItems = [];\n          inList = false;\n        }\n        elements.push(\n          <h2 key={`h2-${i}`} className=\"text-xl font-bold text-gray-900 dark:text-gray-100 mt-6 mb-4\">\n            {parseInlineMarkdown(line.slice(3))}\n          </h2>\n        );\n      } else if (line.startsWith('# ')) {\n        // 结束当前列表\n        if (inList && listItems.length > 0) {\n          elements.push(\n            <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n              {listItems}\n            </ul>\n          );\n          listItems = [];\n          inList = false;\n        }\n        elements.push(\n          <h1 key={`h1-${i}`} className=\"text-2xl font-bold text-gray-900 dark:text-gray-100 mt-6 mb-4\">\n            {parseInlineMarkdown(line.slice(2))}\n          </h1>\n        );\n      } else if (line.startsWith('* ') || line.startsWith('- ')) {\n        inList = true;\n        listItems.push(\n          <li key={`li-${i}`} className=\"mb-1\">\n            {parseInlineMarkdown(line.slice(2))}\n          </li>\n        );\n      } else {\n        // 结束当前列表\n        if (inList && listItems.length > 0) {\n          elements.push(\n            <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n              {listItems}\n            </ul>\n          );\n          listItems = [];\n          inList = false;\n        }\n        \n        // 检查是否是独立的 CodePen 链接\n        const codepenLinkMatch = line.match(/^\\[([^\\]]+)\\]\\((https:\\/\\/codepen\\.io\\/[^\\/]+\\/pen\\/[^\\/\\?]+(?:\\?[^\\/]*)?)\\)$/);\n        if (codepenLinkMatch) {\n          console.log('Found standard CodePen link:', codepenLinkMatch[2]);\n          const [, linkText, url] = codepenLinkMatch;\n          const codepenMatch = url.match(/codepen\\.io\\/([^\\/]+)\\/pen\\/([^\\/\\?]+)/);\n          if (codepenMatch) {\n            const [, username, penId] = codepenMatch;\n            console.log('Extracted CodePen:', username, penId);\n            elements.push(\n              <div key={`codepen-block-${i}`} className=\"my-6\">\n                <iframe\n                  src={`https://codepen.io/${username}/embed/${penId}?default-tab=result`}\n                  className=\"w-full h-96 border-0 rounded-lg\"\n                  title={`CodePen: ${linkText}`}\n                  loading=\"lazy\"\n                  allow=\"accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking\"\n                  sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox\"\n                />\n                <div className=\"mt-2 text-center\">\n                  <a \n                    href={url} \n                    target=\"_blank\" \n                    rel=\"noopener noreferrer\" \n                    className=\"text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 underline\"\n                  >\n                    在 CodePen 中查看完整代码 →\n                  </a>\n                </div>\n              </div>\n            );\n            continue; // 跳过后续的 p 标签处理\n          }\n        }\n        \n        // 检查是否是独立的 CodePen 链接（@ 开头格式）\n        const codepenAtLinkMatch = line.match(/^@(https:\\/\\/codepen\\.io\\/[^\\/]+\\/pen\\/[^\\/\\?]+(?:\\?[^\\/]*)?)/);\n        if (codepenAtLinkMatch) {\n          console.log('Found @ CodePen link:', codepenAtLinkMatch[1]);\n          const [, url] = codepenAtLinkMatch;\n          const codepenMatch = url.match(/codepen\\.io\\/([^\\/]+)\\/pen\\/([^\\/\\?]+)/);\n          if (codepenMatch) {\n            const [, username, penId] = codepenMatch;\n            console.log('Extracted CodePen:', username, penId);\n            elements.push(\n              <div key={`codepen-at-block-${i}`} className=\"my-6\">\n                <iframe\n                  src={`https://codepen.io/${username}/embed/${penId}?default-tab=result`}\n                  className=\"w-full h-96 border-0 rounded-lg\"\n                  title={`CodePen Demo`}\n                  loading=\"lazy\"\n                  allow=\"accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking\"\n                  sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox\"\n                />\n                <div className=\"mt-2 text-center\">\n                  <a \n                    href={url} \n                    target=\"_blank\" \n                    rel=\"noopener noreferrer\" \n                    className=\"text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 underline\"\n                  >\n                    在 CodePen 中查看完整代码 →\n                  </a>\n                </div>\n              </div>\n            );\n            continue; // 跳过后续的 p 标签处理\n          }\n        }\n        \n        // 检查是否包含 CodePen 链接（行内链接）\n        const inlineCodepenMatch = line.match(/\\[([^\\]]+)\\]\\((https:\\/\\/codepen\\.io\\/[^\\/]+\\/pen\\/[^\\/\\?]+(?:\\?[^\\/]*)?)\\)/);\n        if (inlineCodepenMatch) {\n          console.log('Found inline CodePen link:', inlineCodepenMatch[2]);\n          const [, linkText, url] = inlineCodepenMatch;\n          const codepenMatch = url.match(/codepen\\.io\\/([^\\/]+)\\/pen\\/([^\\/\\?]+)/);\n          if (codepenMatch) {\n            const [, username, penId] = codepenMatch;\n            console.log('Extracted inline CodePen:', username, penId);\n            elements.push(\n              <div key={`codepen-inline-${i}`} className=\"my-6\">\n                <iframe\n                  src={`https://codepen.io/${username}/embed/${penId}?default-tab=result`}\n                  className=\"w-full h-96 border-0 rounded-lg\"\n                  title={`CodePen: ${linkText}`}\n                  loading=\"lazy\"\n                  allow=\"accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking\"\n                  sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox\"\n                />\n                <div className=\"mt-2 text-center\">\n                  <a \n                    href={url} \n                    target=\"_blank\" \n                    rel=\"noopener noreferrer\" \n                    className=\"text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 underline\"\n                  >\n                    在 CodePen 中查看完整代码 →\n                  </a>\n                </div>\n              </div>\n            );\n            continue; // 跳过后续的 p 标签处理\n          }\n        }\n        \n        // 检查是否包含 CodePen 链接（@ 开头格式，行内）\n        const inlineCodepenAtMatch = line.match(/@(https:\\/\\/codepen\\.io\\/[^\\/]+\\/pen\\/[^\\/\\?]+(?:\\?[^\\/]*)?)/);\n        if (inlineCodepenAtMatch) {\n          const [, url] = inlineCodepenAtMatch;\n          const codepenMatch = url.match(/codepen\\.io\\/([^\\/]+)\\/pen\\/([^\\/\\?]+)/);\n          if (codepenMatch) {\n            const [, username, penId] = codepenMatch;\n            elements.push(\n              <div key={`codepen-at-inline-${i}`} className=\"my-6\">\n                <iframe\n                  src={`https://codepen.io/${username}/embed/${penId}?default-tab=result`}\n                  className=\"w-full h-96 border-0 rounded-lg\"\n                  title={`CodePen Demo`}\n                  loading=\"lazy\"\n                  allow=\"accelerometer; camera; encrypted-media; geolocation; gyroscope; microphone; midi; payment; usb; xr-spatial-tracking\"\n                  sandbox=\"allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox\"\n                />\n                <div className=\"mt-2 text-center\">\n                  <a \n                    href={url} \n                    target=\"_blank\" \n                    rel=\"noopener noreferrer\" \n                    className=\"text-sm text-gray-600 dark:text-gray-400 hover:text-gray-800 dark:hover:text-gray-200 underline\"\n                  >\n                    在 CodePen 中查看完整代码 →\n                  </a>\n                </div>\n              </div>\n            );\n            continue; // 跳过后续的 p 标签处理\n          }\n        }\n        \n        elements.push(\n          <p key={`p-${i}`} className=\"mb-3 leading-relaxed text-gray-700 dark:text-gray-300\">\n            {parseInlineMarkdown(line)}\n          </p>\n        );\n      }\n    }\n  }\n  \n  // 处理未闭合的列表\n  if (inList && listItems.length > 0) {\n    elements.push(\n      <ul key=\"ul-final\" className=\"mb-4 pl-6 list-disc\">\n        {listItems}\n      </ul>\n    );\n  }\n  \n  // 处理未闭合的代码块\n  if (inCodeBlock && currentCodeBlock.trim()) {\n    // 检测是否是 CodePen 格式\n    const isCodePen = codeLanguage === 'codepen' || \n                     currentCodeBlock.includes('<!-- CodePen Demo -->') ||\n                     currentCodeBlock.includes('<!-- HTML -->') ||\n                     currentCodeBlock.includes('<!-- CSS -->') ||\n                     currentCodeBlock.includes('<!-- JavaScript -->');\n    \n    elements.push(\n      <CodeBlock key=\"code-final\" language={isCodePen ? 'codepen' : codeLanguage}>\n        {currentCodeBlock.trim()}\n      </CodeBlock>\n    );\n  }\n  \n  return elements;\n};\n\n// 解析行内 Markdown\nconst parseInlineMarkdown = (text: string): React.ReactNode => {\n  const parts: React.ReactNode[] = [];\n  let currentText = '';\n  let i = 0;\n  \n  while (i < text.length) {\n    // 处理 HTML 图片标签 <img ...>\n    if (text.slice(i, i + 4) === '<img') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i += 4;\n      let imgTag = '<img';\n      let inQuotes = false;\n      let quoteChar = '';\n      \n      while (i < text.length) {\n        const char = text[i];\n        imgTag += char;\n        \n        if ((char === '\"' || char === \"'\") && !inQuotes) {\n          inQuotes = true;\n          quoteChar = char;\n        } else if (char === quoteChar && inQuotes) {\n          inQuotes = false;\n        } else if (char === '>' && !inQuotes) {\n          i++;\n          break;\n        }\n        i++;\n      }\n      \n      // 解析 img 标签属性\n      const srcMatch = imgTag.match(/src\\s*=\\s*[\"']([^\"']+)[\"']/);\n      const altMatch = imgTag.match(/alt\\s*=\\s*[\"']([^\"']+)[\"']/);\n      const widthMatch = imgTag.match(/width\\s*=\\s*[\"']?(\\d+)[\"']?/);\n      \n      if (srcMatch) {\n        const src = srcMatch[1];\n        const alt = altMatch ? altMatch[1] : '';\n        const width = widthMatch ? widthMatch[1] : undefined;\n        \n        parts.push(\n          <img \n            key={`html-img-${i}`} \n            src={src} \n            alt={alt} \n            width={width}\n            className=\"max-w-full h-auto rounded-lg my-4\" \n          />\n        );\n      } else {\n        parts.push(imgTag);\n      }\n    }\n    // 处理 Markdown 图片 ![alt](url)\n    else if (text.slice(i, i + 2) === '![') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i += 2;\n      let altText = '';\n      while (i < text.length && text[i] !== ']') {\n        altText += text[i];\n        i++;\n      }\n      if (i < text.length && text[i] === ']' && text[i + 1] === '(') {\n        i += 2;\n        let url = '';\n        while (i < text.length && text[i] !== ')') {\n          url += text[i];\n          i++;\n        }\n        if (i < text.length) {\n          parts.push(\n            <img \n              key={`img-${i}`} \n              src={url} \n              alt={altText} \n              className=\"max-w-full h-auto rounded-lg my-4\" \n            />\n          );\n          i++;\n        } else {\n          parts.push(`![${altText}](${url}`);\n        }\n      } else {\n        parts.push(`![${altText}`);\n      }\n    }\n    // 处理 @ 开头的 CodePen 链接\n    else if (text[i] === '@' && text.slice(i + 1, i + 8) === 'https://') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i++; // 跳过 @\n      let url = '';\n      while (i < text.length && text[i] !== ' ' && text[i] !== '\\n' && text[i] !== '\\t') {\n        url += text[i];\n        i++;\n      }\n      \n      // 检查是否是 CodePen 链接\n      const codepenMatch = url.match(/codepen\\.io\\/([^\\/]+)\\/pen\\/([^\\/\\?]+)/);\n      if (codepenMatch) {\n        // CodePen 链接应该在 parseMarkdown 中处理，这里只返回普通链接\n        parts.push(\n          <a \n            key={`at-link-${i}`} \n            href={url} \n            target=\"_blank\" \n            rel=\"noopener noreferrer\" \n            className=\"text-primary-600 hover:text-primary-700 underline\"\n          >\n            {url}\n          </a>\n        );\n      } else {\n        parts.push(\n          <a \n            key={`at-link-${i}`} \n            href={url} \n            target=\"_blank\" \n            rel=\"noopener noreferrer\" \n            className=\"text-primary-600 hover:text-primary-700 underline\"\n          >\n            {url}\n          </a>\n        );\n      }\n    }\n    // 处理链接 [text](url)\n    else if (text[i] === '[') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i++;\n      let linkText = '';\n      while (i < text.length && text[i] !== ']') {\n        linkText += text[i];\n        i++;\n      }\n      if (i < text.length && text[i] === ']' && text[i + 1] === '(') {\n        i += 2;\n        let url = '';\n        while (i < text.length && text[i] !== ')') {\n          url += text[i];\n          i++;\n        }\n        if (i < text.length) {\n          // 检查是否是 CodePen 链接\n          const codepenMatch = url.match(/codepen\\.io\\/([^\\/]+)\\/pen\\/([^\\/\\?]+)/);\n          if (codepenMatch) {\n            // CodePen 链接应该在 parseMarkdown 中处理，这里只返回普通链接\n            parts.push(\n              <a \n                key={`link-${i}`} \n                href={url} \n                target=\"_blank\" \n                rel=\"noopener noreferrer\" \n                className=\"text-primary-600 hover:text-primary-700 underline\"\n              >\n                {linkText}\n              </a>\n            );\n          } else {\n            parts.push(\n              <a \n                key={`link-${i}`} \n                href={url} \n                target=\"_blank\" \n                rel=\"noopener noreferrer\" \n                className=\"text-primary-600 hover:text-primary-700 underline\"\n              >\n                {linkText}\n              </a>\n            );\n          }\n          i++;\n        } else {\n          parts.push(`[${linkText}](${url}`);\n        }\n      } else {\n        parts.push(`[${linkText}`);\n      }\n    }\n    // 处理粗体 **text**\n    else if (text.slice(i, i + 2) === '**') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i += 2;\n      let boldText = '';\n      while (i < text.length && text.slice(i, i + 2) !== '**') {\n        boldText += text[i];\n        i++;\n      }\n      if (i < text.length) {\n        parts.push(<strong key={`bold-${i}`} className=\"font-bold\">{boldText}</strong>);\n        i += 2;\n      } else {\n        parts.push(`**${boldText}`);\n      }\n    }\n    // 处理斜体 *text*\n    else if (text[i] === '*') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i++;\n      let italicText = '';\n      while (i < text.length && text[i] !== '*') {\n        italicText += text[i];\n        i++;\n      }\n      if (i < text.length) {\n        parts.push(<em key={`italic-${i}`} className=\"italic\">{italicText}</em>);\n        i++;\n      } else {\n        parts.push(`*${italicText}`);\n      }\n    }\n    // 处理行内代码 `code`\n    else if (text[i] === '`') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i++;\n      let codeText = '';\n      while (i < text.length && text[i] !== '`') {\n        codeText += text[i];\n        i++;\n      }\n      if (i < text.length) {\n        parts.push(\n          <code key={`code-${i}`} className=\"bg-gray-100 dark:bg-gray-800 px-1 py-0.5 rounded text-sm font-mono\">\n            {codeText}\n          </code>\n        );\n        i++;\n      } else {\n        parts.push(`\\`${codeText}`);\n      }\n    } else {\n      currentText += text[i];\n      i++;\n    }\n  }\n  \n  if (currentText) {\n    parts.push(currentText);\n  }\n  \n  return parts.length === 1 ? parts[0] : parts;\n};\n\nexport default function ArticleDetailPage() {\n  const router = useRouter();\n  const params = useParams();\n  const { t } = useApp();\n  const [article, setArticle] = useState<Article | null>(null);\n  const [issueData, setIssueData] = useState<IssueData | null>(null);\n  const [nextArticle, setNextArticle] = useState<Article | null>(null);\n  const [prevArticle, setPrevArticle] = useState<Article | null>(null);\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState<string | null>(null);\n\n  useEffect(() => {\n    const loadArticleData = async () => {\n      try {\n        setLoading(true);\n        const id = params.id as string;\n        \n        // 直接从单个文章 API 获取基本信息\n        const articleRes = await fetch(`/api/articles/${id}`);\n        const articleData = await articleRes.json();\n        \n        if (!articleData || !articleData.body) {\n          setError(t('articleNotFound'));\n          return;\n        }\n        \n        // 构造文章基本信息\n        const currentArticle: Article = {\n          id: parseInt(id),\n          title: articleData.title || `文章 #${id}`,\n          url: `https://github.com/chokcoco/iCSS/issues/${id}`,\n          image: articleData.image,\n          category: articleData.category\n        };\n        \n        setArticle(currentArticle);\n        setIssueData(articleData);\n        \n        // 获取所有文章用于导航\n        const articlesRes = await fetch('/api/articles?per_page=100');\n        const articlesData = await articlesRes.json();\n        \n        if (articlesData.articles && Array.isArray(articlesData.articles)) {\n          const currentIndex = articlesData.articles.findIndex((a: Article) => a.id === parseInt(id));\n          \n          // 获取下一篇文章\n          if (currentIndex !== -1 && currentIndex < articlesData.articles.length - 1) {\n            setNextArticle(articlesData.articles[currentIndex + 1]);\n          }\n          \n          // 获取上一篇文章\n          if (currentIndex !== -1 && currentIndex > 0) {\n            setPrevArticle(articlesData.articles[currentIndex - 1]);\n          }\n        }\n        \n      } catch (err) {\n        console.error('加载文章详情失败:', err);\n        setError(t('loadFailed'));\n      } finally {\n        setLoading(false);\n      }\n    };\n\n    if (params.id) {\n      loadArticleData();\n    }\n  }, [params.id, t]);\n\n  const goBack = () => {\n    router.back();\n  };\n\n  const goToNextArticle = () => {\n    if (nextArticle) {\n      router.push(`/article/${nextArticle.id}`);\n    }\n  };\n\n  const goToPrevArticle = () => {\n    if (prevArticle) {\n      router.push(`/article/${prevArticle.id}`);\n    }\n  };\n\n  const formatDate = (dateString: string) => {\n    return new Date(dateString).toLocaleDateString('zh-CN', {\n      year: 'numeric',\n      month: 'long',\n      day: 'numeric'\n    });\n  };\n\n  if (loading) {\n    return (\n      <div className=\"min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center\">\n        <div className=\"animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600\"></div>\n      </div>\n    );\n  }\n\n  if (error || !article) {\n    return (\n      <div className=\"min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center\">\n        <div className=\"text-center\">\n          <h1 className=\"text-2xl font-bold text-gray-900 dark:text-gray-100 mb-4\">{t('articleNotFound')}</h1>\n          <button\n            onClick={goBack}\n            className=\"btn-primary\"\n          >\n            {t('returnHome')}\n          </button>\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"min-h-screen bg-gray-50 dark:bg-gray-900\">\n      {/* 头部导航 */}\n      <header className=\"bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700\">\n        <div className=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8\">\n          <div className=\"flex items-center justify-between h-16\">\n            <button\n              onClick={goBack}\n              className=\"flex items-center space-x-2 text-gray-600 dark:text-gray-300 hover:text-gray-900 dark:hover:text-gray-100 transition-colors\"\n            >\n              <ArrowLeft className=\"w-5 h-5\" />\n              <span>{t('back')}</span>\n            </button>\n            <div className=\"flex items-center space-x-4\">\n              <a\n                href={article.url}\n                target=\"_blank\"\n                rel=\"noopener noreferrer\"\n                className=\"flex items-center space-x-2 text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-200 transition-colors\"\n              >\n                <ExternalLink className=\"w-5 h-5\" />\n                <span>{t('viewFullContent')}</span>\n              </a>\n            </div>\n          </div>\n        </div>\n      </header>\n\n      <div className=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8\">\n        <motion.div\n          initial={{ opacity: 0, y: 20 }}\n          animate={{ opacity: 1, y: 0 }}\n          transition={{ duration: 0.5 }}\n          className=\"bg-white dark:bg-gray-800 rounded-lg shadow-sm border border-gray-200 dark:border-gray-700 overflow-hidden\"\n        >\n          {/* 文章头部 */}\n          <div className=\"p-6 border-b border-gray-200 dark:border-gray-700\">\n            <div className=\"flex items-start justify-between mb-4\">\n              <div className=\"flex-1\">\n                <div className=\"flex items-center space-x-2 mb-2\">\n                  <span className=\"text-sm text-gray-500 dark:text-gray-400\">#{article.id}</span>\n                  {article.category && (\n                    <span className=\"text-xs px-2 py-1 rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200\">\n                      {article.category}\n                    </span>\n                  )}\n                </div>\n                <h1 className=\"text-3xl font-bold text-gray-900 dark:text-gray-100 mb-4\">\n                  {article.title}\n                </h1>\n                \n                {/* 文章元信息 */}\n                {issueData && (\n                  <div className=\"flex items-center space-x-6 text-sm text-gray-500 dark:text-gray-400\">\n                    <div className=\"flex items-center space-x-1\">\n                      <User className=\"w-4 h-4\" />\n                      <span>{issueData.user?.login}</span>\n                    </div>\n                    <div className=\"flex items-center space-x-1\">\n                      <Calendar className=\"w-4 h-4\" />\n                      <span>{formatDate(issueData.created_at)}</span>\n                    </div>\n                    <div className=\"flex items-center space-x-1\">\n                      <Eye className=\"w-4 h-4\" />\n                      <span>{issueData.reactions?.total_count || 0} 赞</span>\n                    </div>\n                  </div>\n                )}\n              </div>\n            </div>\n          </div>\n\n          {/* 文章配图 */}\n          {article.image && (\n            <div className=\"relative h-64 md:h-96 overflow-hidden\">\n              <Image\n                src={article.image}\n                alt={article.title}\n                fill\n                className=\"object-cover\"\n              />\n            </div>\n          )}\n\n          {/* 文章内容 */}\n          <div className=\"p-6\">\n            {issueData?.body ? (\n              <div className=\"prose prose-lg max-w-none dark:prose-invert\">\n                <div className=\"markdown-content\">\n                  {parseMarkdown(issueData.body)}\n                </div>\n              </div>\n            ) : (\n              <div className=\"text-center py-12\">\n                <p className=\"text-gray-500 dark:text-gray-400\">{t('loading')}</p>\n              </div>\n            )}\n          </div>\n\n          {/* 底部操作 */}\n          <div className=\"p-6 border-t border-gray-200 dark:border-gray-700 bg-gray-50 dark:bg-gray-900\">\n            <div className=\"flex items-center justify-between\">\n              <div className=\"flex items-center space-x-4\">\n                <button\n                  onClick={goBack}\n                  className=\"btn-secondary\"\n                >\n                  {t('returnList')}\n                </button>\n                {prevArticle && (\n                  <button\n                    onClick={goToPrevArticle}\n                    className=\"btn-secondary flex items-center space-x-2\"\n                  >\n                    <ArrowLeft className=\"w-4 h-4\" />\n                    <span>{t('prevArticle')}</span>\n                  </button>\n                )}\n              </div>\n              {nextArticle ? (\n                <button\n                  onClick={goToNextArticle}\n                  className=\"btn-primary flex items-center space-x-2\"\n                >\n                  <span>{t('nextArticle')}</span>\n                  <ArrowRight className=\"w-4 h-4\" />\n                </button>\n              ) : (\n                <div className=\"text-sm text-gray-500 dark:text-gray-400\">\n                  {t('lastArticle')}\n                </div>\n              )}\n            </div>\n          </div>\n        </motion.div>\n      </div>\n    </div>\n  );\n} "
  },
  {
    "path": "website/app/components/CodeBlock.tsx",
    "content": "'use client';\n\nimport { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';\nimport { tomorrow } from 'react-syntax-highlighter/dist/esm/styles/prism';\nimport { useState } from 'react';\n\ninterface CodeBlockProps {\n  children: string;\n  language?: string;\n}\n\n// CodePen Demo 组件\nfunction CodePenDemo({ html, css, js }: { html: string; css: string; js: string }) {\n  const [isExpanded, setIsExpanded] = useState(false);\n  \n  const fullHtml = `\n<!DOCTYPE html>\n<html>\n<head>\n  <meta charset=\"UTF-8\">\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n  <style>${css}</style>\n</head>\n<body>\n  ${html}\n  <script>${js}</script>\n</body>\n</html>\n  `.trim();\n\n  return (\n    <div className=\"my-6 border border-gray-200 rounded-lg overflow-hidden\">\n      <div className=\"bg-gray-50 px-4 py-2 border-b border-gray-200 flex justify-between items-center\">\n        <span className=\"text-sm font-medium text-gray-700\">Live Demo</span>\n        <button\n          onClick={() => setIsExpanded(!isExpanded)}\n          className=\"text-sm text-blue-600 hover:text-blue-800\"\n        >\n          {isExpanded ? '收起' : '展开'}\n        </button>\n      </div>\n      <div className={`transition-all duration-300 ${isExpanded ? 'h-96' : 'h-64'}`}>\n        <iframe\n          srcDoc={fullHtml}\n          className=\"w-full h-full border-0\"\n          title=\"CodePen Demo\"\n          sandbox=\"allow-scripts allow-same-origin\"\n        />\n      </div>\n    </div>\n  );\n}\n\nexport default function CodeBlock({ children, language = 'text' }: CodeBlockProps) {\n  // 检测是否是 CodePen 格式的代码块\n  const isCodePen = language === 'codepen' || children.includes('<!-- CodePen Demo -->');\n  \n  if (isCodePen) {\n    // 解析 CodePen 格式的代码\n    const lines = children.split('\\n');\n    let html = '';\n    let css = '';\n    let js = '';\n    let currentSection = '';\n    \n    for (const line of lines) {\n      if (line.includes('<!-- HTML -->')) {\n        currentSection = 'html';\n      } else if (line.includes('<!-- CSS -->')) {\n        currentSection = 'css';\n      } else if (line.includes('<!-- JavaScript -->')) {\n        currentSection = 'js';\n      } else if (line.includes('<!-- CodePen Demo -->')) {\n        // 跳过注释行\n        continue;\n      } else {\n        switch (currentSection) {\n          case 'html':\n            html += line + '\\n';\n            break;\n          case 'css':\n            css += line + '\\n';\n            break;\n          case 'js':\n            js += line + '\\n';\n            break;\n        }\n      }\n    }\n    \n    return (\n      <div className=\"my-6\">\n        <CodePenDemo html={html.trim()} css={css.trim()} js={js.trim()} />\n        <div className=\"mt-4\">\n          <SyntaxHighlighter\n            language=\"html\"\n            style={tomorrow}\n            customStyle={{\n              margin: 0,\n              padding: '1rem',\n              borderRadius: '0.5rem',\n              fontSize: '0.875rem',\n              lineHeight: '1.4',\n              backgroundColor: '#2d3748',\n            }}\n            showLineNumbers={true}\n            wrapLines={true}\n            lineProps={{\n              style: {\n                padding: '0.125rem 0',\n                minHeight: '1.4em',\n              },\n            }}\n          >\n            {children}\n          </SyntaxHighlighter>\n        </div>\n      </div>\n    );\n  }\n\n  // 语言映射，确保正确的语法高亮\n  const getLanguage = (lang: string) => {\n    const languageMap: { [key: string]: string } = {\n      'html': 'markup',\n      'htm': 'markup',\n      'xml': 'markup',\n      'css': 'css',\n      'scss': 'scss',\n      'sass': 'sass',\n      'less': 'less',\n      'js': 'javascript',\n      'javascript': 'javascript',\n      'ts': 'typescript',\n      'typescript': 'typescript',\n      'jsx': 'jsx',\n      'tsx': 'tsx',\n      'json': 'json',\n      'bash': 'bash',\n      'shell': 'bash',\n      'sh': 'bash',\n      'python': 'python',\n      'py': 'python',\n      'java': 'java',\n      'c': 'c',\n      'cpp': 'cpp',\n      'c++': 'cpp',\n      'c#': 'csharp',\n      'csharp': 'csharp',\n      'php': 'php',\n      'ruby': 'ruby',\n      'rb': 'ruby',\n      'go': 'go',\n      'rust': 'rust',\n      'rs': 'rust',\n      'sql': 'sql',\n      'yaml': 'yaml',\n      'yml': 'yaml',\n      'toml': 'toml',\n      'ini': 'ini',\n      'conf': 'ini',\n      'diff': 'diff',\n      'git': 'git',\n    };\n    \n    return languageMap[lang.toLowerCase()] || lang.toLowerCase();\n  };\n\n  return (\n    <div className=\"my-4\">\n      <SyntaxHighlighter\n        language={getLanguage(language)}\n        style={tomorrow}\n        customStyle={{\n          margin: 0,\n          padding: '1rem',\n          borderRadius: '0.5rem',\n          fontSize: '0.875rem',\n          lineHeight: '1.4',\n          backgroundColor: '#2d3748',\n        }}\n        showLineNumbers={true}\n        wrapLines={true}\n        lineProps={{\n          style: {\n            padding: '0.125rem 0',\n            minHeight: '1.4em',\n          },\n        }}\n      >\n        {children}\n      </SyntaxHighlighter>\n    </div>\n  );\n} "
  },
  {
    "path": "website/app/components/LanguageToggle.tsx",
    "content": "'use client';\n\nimport { useState, useRef, useEffect } from 'react';\nimport { useApp } from '../contexts/AppContext';\nimport { languages, LanguageConfig } from '../lib/language';\nimport { ChevronDown, Globe } from 'lucide-react';\n\nexport default function LanguageToggle() {\n  const { language, setLanguage } = useApp();\n  const [isOpen, setIsOpen] = useState(false);\n  const dropdownRef = useRef<HTMLDivElement>(null);\n\n  const currentLanguage = languages.find(l => l.value === language) || languages[0];\n\n  useEffect(() => {\n    function handleClickOutside(event: MouseEvent) {\n      if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n        setIsOpen(false);\n      }\n    }\n\n    document.addEventListener('mousedown', handleClickOutside);\n    return () => {\n      document.removeEventListener('mousedown', handleClickOutside);\n    };\n  }, []);\n\n  return (\n    <div className=\"relative\" ref={dropdownRef}>\n      <button\n        onClick={() => setIsOpen(!isOpen)}\n        className=\"flex items-center space-x-2 px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition-colors\"\n      >\n        <Globe className=\"w-4 h-4\" />\n        <span>{currentLanguage.flag}</span>\n        <span>{currentLanguage.name}</span>\n        <ChevronDown className={`w-4 h-4 transition-transform ${isOpen ? 'rotate-180' : ''}`} />\n      </button>\n\n      {isOpen && (\n        <div className=\"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg z-50\">\n          <div className=\"py-1\">\n            {languages.map((languageOption: LanguageConfig) => (\n              <button\n                key={languageOption.value}\n                onClick={() => {\n                  setLanguage(languageOption.value);\n                  setIsOpen(false);\n                }}\n                className={`w-full flex items-center space-x-3 px-4 py-2 text-sm text-left hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors ${\n                  language === languageOption.value\n                    ? 'bg-primary-50 dark:bg-primary-900/20 text-primary-700 dark:text-primary-300'\n                    : 'text-gray-700 dark:text-gray-200'\n                }`}\n              >\n                <span className=\"text-lg\">{languageOption.flag}</span>\n                <span>{languageOption.name}</span>\n              </button>\n            ))}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n} "
  },
  {
    "path": "website/app/components/ThemeToggle.tsx",
    "content": "'use client';\n\nimport { useState, useRef, useEffect } from 'react';\nimport { useApp } from '../contexts/AppContext';\nimport { themes, ThemeConfig } from '../lib/theme';\nimport { Translations } from '../lib/translations';\nimport { ChevronDown, Sun, Moon, Monitor } from 'lucide-react';\n\nexport default function ThemeToggle() {\n  const { theme, setTheme, t } = useApp();\n  const [isOpen, setIsOpen] = useState(false);\n  const dropdownRef = useRef<HTMLDivElement>(null);\n\n  const currentTheme = themes.find(t => t.value === theme) || themes[2];\n\n  const getThemeIcon = (themeValue: string) => {\n    switch (themeValue) {\n      case 'light':\n        return <Sun className=\"w-4 h-4\" />;\n      case 'dark':\n        return <Moon className=\"w-4 h-4\" />;\n      case 'system':\n        return <Monitor className=\"w-4 h-4\" />;\n      default:\n        return <Monitor className=\"w-4 h-4\" />;\n    }\n  };\n\n  useEffect(() => {\n    function handleClickOutside(event: MouseEvent) {\n      if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {\n        setIsOpen(false);\n      }\n    }\n\n    document.addEventListener('mousedown', handleClickOutside);\n    return () => {\n      document.removeEventListener('mousedown', handleClickOutside);\n    };\n  }, []);\n\n  return (\n    <div className=\"relative\" ref={dropdownRef}>\n      <button\n        onClick={() => setIsOpen(!isOpen)}\n        className=\"flex items-center space-x-2 px-3 py-2 text-sm font-medium text-gray-700 dark:text-gray-200 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition-colors\"\n      >\n        {getThemeIcon(currentTheme.value)}\n        <span>{t(currentTheme.value as keyof Translations)}</span>\n        <ChevronDown className={`w-4 h-4 transition-transform ${isOpen ? 'rotate-180' : ''}`} />\n      </button>\n\n      {isOpen && (\n        <div className=\"absolute right-0 mt-2 w-48 bg-white dark:bg-gray-800 border border-gray-300 dark:border-gray-600 rounded-lg shadow-lg z-50\">\n          <div className=\"py-1\">\n            {themes.map((themeOption: ThemeConfig) => (\n              <button\n                key={themeOption.value}\n                onClick={() => {\n                  setTheme(themeOption.value);\n                  setIsOpen(false);\n                }}\n                className={`w-full flex items-center space-x-3 px-4 py-2 text-sm text-left hover:bg-gray-100 dark:hover:bg-gray-700 transition-colors ${\n                  theme === themeOption.value\n                    ? 'bg-primary-50 dark:bg-primary-900/20 text-primary-700 dark:text-primary-300'\n                    : 'text-gray-700 dark:text-gray-200'\n                }`}\n              >\n                <span className=\"text-lg\">{themeOption.icon}</span>\n                <span>{t(themeOption.value as keyof Translations)}</span>\n              </button>\n            ))}\n          </div>\n        </div>\n      )}\n    </div>\n  );\n} "
  },
  {
    "path": "website/app/contexts/AppContext.tsx",
    "content": "'use client';\n\nimport React, { createContext, useContext, useEffect, useState } from 'react';\nimport { Theme, getStoredTheme, applyTheme, initializeTheme } from '../lib/theme';\nimport { Language, getStoredLanguage, setStoredLanguage } from '../lib/language';\nimport { getTranslation, Translations } from '../lib/translations';\n\ninterface AppContextType {\n  theme: Theme;\n  setTheme: (theme: Theme) => void;\n  language: Language;\n  setLanguage: (language: Language) => void;\n  t: (key: keyof Translations) => string;\n}\n\nconst AppContext = createContext<AppContextType | undefined>(undefined);\n\nexport function AppProvider({ children }: { children: React.ReactNode }) {\n  const [theme, setThemeState] = useState<Theme>('system');\n  const [language, setLanguageState] = useState<Language>('zh');\n\n  useEffect(() => {\n    try {\n      // 初始化主题和语言\n      initializeTheme();\n      setThemeState(getStoredTheme());\n      setLanguageState(getStoredLanguage());\n    } catch (error) {\n      console.warn('Failed to initialize theme/language:', error);\n    }\n  }, []);\n\n  const setTheme = (newTheme: Theme) => {\n    try {\n      setThemeState(newTheme);\n      applyTheme(newTheme);\n    } catch (error) {\n      console.warn('Failed to set theme:', error);\n    }\n  };\n\n  const setLanguage = (newLanguage: Language) => {\n    try {\n      setLanguageState(newLanguage);\n      setStoredLanguage(newLanguage);\n    } catch (error) {\n      console.warn('Failed to set language:', error);\n    }\n  };\n\n  const t = (key: keyof Translations): string => {\n    return getTranslation(language, key);\n  };\n\n  const contextValue: AppContextType = {\n    theme,\n    setTheme,\n    language,\n    setLanguage,\n    t\n  };\n\n  return (\n    <AppContext.Provider value={contextValue}>\n      {children}\n    </AppContext.Provider>\n  );\n}\n\nexport function useApp() {\n  const context = useContext(AppContext);\n  if (context === undefined) {\n    throw new Error('useApp must be used within an AppProvider');\n  }\n  return context;\n} "
  },
  {
    "path": "website/app/globals.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n\n@layer base {\n  :root {\n    --background: 0 0% 100%;\n    --foreground: 222.2 84% 4.9%;\n    --card: 0 0% 100%;\n    --card-foreground: 222.2 84% 4.9%;\n    --popover: 0 0% 100%;\n    --popover-foreground: 222.2 84% 4.9%;\n    --primary: 221.2 83.2% 53.3%;\n    --primary-foreground: 210 40% 98%;\n    --secondary: 210 40% 96%;\n    --secondary-foreground: 222.2 84% 4.9%;\n    --muted: 210 40% 96%;\n    --muted-foreground: 215.4 16.3% 46.9%;\n    --accent: 210 40% 96%;\n    --accent-foreground: 222.2 84% 4.9%;\n    --destructive: 0 84.2% 60.2%;\n    --destructive-foreground: 210 40% 98%;\n    --border: 214.3 31.8% 91.4%;\n    --input: 214.3 31.8% 91.4%;\n    --ring: 221.2 83.2% 53.3%;\n    --radius: 0.5rem;\n  }\n\n  .dark {\n    --background: 222.2 84% 4.9%;\n    --foreground: 210 40% 98%;\n    --card: 222.2 84% 4.9%;\n    --card-foreground: 210 40% 98%;\n    --popover: 222.2 84% 4.9%;\n    --popover-foreground: 210 40% 98%;\n    --primary: 217.2 91.2% 59.8%;\n    --primary-foreground: 222.2 84% 4.9%;\n    --secondary: 217.2 32.6% 17.5%;\n    --secondary-foreground: 210 40% 98%;\n    --muted: 217.2 32.6% 17.5%;\n    --muted-foreground: 215 20.2% 65.1%;\n    --accent: 217.2 32.6% 17.5%;\n    --accent-foreground: 210 40% 98%;\n    --destructive: 0 62.8% 30.6%;\n    --destructive-foreground: 210 40% 98%;\n    --border: 217.2 32.6% 17.5%;\n    --input: 217.2 32.6% 17.5%;\n    --ring: 224.3 76.3% 94.1%;\n  }\n\n  html {\n    scroll-behavior: smooth;\n  }\n  \n  body {\n    @apply bg-background text-foreground;\n  }\n}\n\n@layer components {\n  .card {\n    @apply bg-white dark:bg-gray-800 border border-gray-200 dark:border-gray-700 rounded-lg shadow-sm;\n  }\n  \n  .btn-primary {\n    @apply inline-flex items-center px-4 py-2 bg-primary-600 text-white text-sm font-medium rounded-lg hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 transition-colors;\n  }\n  \n  .btn-secondary {\n    @apply inline-flex items-center px-4 py-2 bg-gray-100 dark:bg-gray-700 text-gray-700 dark:text-gray-200 text-sm font-medium rounded-lg hover:bg-gray-200 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500 transition-colors;\n  }\n  \n  .input {\n    @apply block w-full px-3 py-2 border border-gray-300 dark:border-gray-600 rounded-lg shadow-sm placeholder-gray-400 dark:placeholder-gray-500 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:border-primary-500 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100;\n  }\n}\n\n/* Markdown 内容样式 */\n.markdown-content {\n  @apply text-gray-800 leading-relaxed;\n}\n\n.markdown-content h1,\n.markdown-content h2,\n.markdown-content h3,\n.markdown-content h4,\n.markdown-content h5,\n.markdown-content h6 {\n  @apply font-bold text-gray-900 mt-6 mb-4;\n}\n\n.markdown-content h1 {\n  @apply text-2xl;\n}\n\n.markdown-content h2 {\n  @apply text-xl;\n}\n\n.markdown-content h3 {\n  @apply text-lg;\n}\n\n.markdown-content p {\n  @apply mb-3 leading-relaxed;\n}\n\n.markdown-content ul,\n.markdown-content ol {\n  @apply mb-4 pl-6;\n}\n\n.markdown-content li {\n  @apply mb-1;\n}\n\n.markdown-content ul {\n  @apply list-disc;\n}\n\n.markdown-content ol {\n  @apply list-decimal;\n}\n\n.markdown-content blockquote {\n  @apply border-l-4 border-gray-300 pl-4 italic text-gray-600 mb-4;\n}\n\n.markdown-content pre {\n  @apply bg-gray-100 p-4 rounded-lg overflow-x-auto mb-4;\n}\n\n.markdown-content code {\n  @apply bg-gray-100 px-1 py-0.5 rounded text-sm font-mono;\n}\n\n.markdown-content pre code {\n  @apply bg-transparent p-0;\n}\n\n.markdown-content a {\n  @apply text-primary-600 hover:text-primary-700 underline;\n}\n\n.markdown-content img {\n  @apply max-w-full h-auto rounded-lg my-4;\n}\n\n.markdown-content table {\n  @apply w-full border-collapse border border-gray-300 mb-4;\n}\n\n.markdown-content th,\n.markdown-content td {\n  @apply border border-gray-300 px-3 py-2 text-left;\n}\n\n.markdown-content th {\n  @apply bg-gray-100 font-semibold;\n}\n\n/* 代码高亮样式优化 */\n.markdown-content pre {\n  @apply text-sm leading-tight;\n}\n\n.markdown-content pre code {\n  @apply text-sm leading-tight;\n}\n\n/* 自定义滚动条 */\n::-webkit-scrollbar {\n  width: 8px;\n}\n\n::-webkit-scrollbar-track {\n  @apply bg-gray-100 dark:bg-gray-800;\n}\n\n::-webkit-scrollbar-thumb {\n  @apply bg-gray-300 dark:bg-gray-600 rounded-full;\n}\n\n::-webkit-scrollbar-thumb:hover {\n  @apply bg-gray-400 dark:bg-gray-500;\n}\n\n/* 代码高亮暗色主题 */\n.dark .markdown-content pre {\n  @apply bg-gray-900;\n}\n\n.dark .markdown-content code {\n  @apply bg-gray-800 text-gray-100;\n}\n\n/* 过渡动画 */\n.transition-colors {\n  transition-property: color, background-color, border-color, text-decoration-color, fill, stroke;\n  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);\n  transition-duration: 200ms;\n} "
  },
  {
    "path": "website/app/layout.tsx",
    "content": "import type { Metadata } from 'next'\nimport { Inter } from 'next/font/google'\nimport './globals.css'\nimport { AppProvider } from './contexts/AppContext'\n\nconst inter = Inter({ subsets: ['latin'] })\n\nexport const metadata: Metadata = {\n  title: 'iCSS - CSS 奇技淫巧',\n  description: 'CSS 奇技淫巧，在这里，都有。本 Repo 围绕 CSS/Web动画 展开，谈一些有趣的话题，内容天马行空，想到什么说什么。',\n  keywords: 'CSS, 动画, 前端, 技巧, 奇技淫巧',\n}\n\nexport default function RootLayout({\n  children,\n}: {\n  children: React.ReactNode\n}) {\n  return (\n    <html lang=\"zh-CN\" suppressHydrationWarning>\n      <head>\n        <meta \n          httpEquiv=\"Permissions-Policy\" \n          content=\"camera=(), microphone=(), geolocation=(), payment=(), usb=(), magnetometer=(), gyroscope=(), accelerometer=(), serial=(), xr-spatial-tracking=(), clipboard-read=(), clipboard-write=(), web-share=(), display-capture=(), bluetooth=(), midi=(), encrypted-media=()\"\n        />\n      </head>\n      <body className={`${inter.className} transition-colors duration-200`}>\n        <AppProvider>\n          {children}\n        </AppProvider>\n      </body>\n    </html>\n  )\n} "
  },
  {
    "path": "website/app/lib/cache.ts",
    "content": "interface CacheItem<T> {\n  data: T;\n  timestamp: number;\n  ttl: number; // Time to live in milliseconds\n}\n\nclass Cache {\n  private static instance: Cache;\n  private cache = new Map<string, CacheItem<unknown>>();\n\n  private constructor() {}\n\n  static getInstance(): Cache {\n    if (!Cache.instance) {\n      Cache.instance = new Cache();\n    }\n    return Cache.instance;\n  }\n\n  set<T>(key: string, data: T, ttl: number = 5 * 60 * 1000): void {\n    this.cache.set(key, {\n      data,\n      timestamp: Date.now(),\n      ttl\n    });\n  }\n\n  get<T>(key: string): T | null {\n    const item = this.cache.get(key);\n    if (!item) {\n      return null;\n    }\n\n    const now = Date.now();\n    if (now - item.timestamp > item.ttl) {\n      this.cache.delete(key);\n      return null;\n    }\n\n    return item.data as T;\n  }\n\n  delete(key: string): void {\n    this.cache.delete(key);\n  }\n\n  clear(): void {\n    this.cache.clear();\n  }\n\n  // 清理过期的缓存项\n  cleanup(): void {\n    const now = Date.now();\n    Array.from(this.cache.entries()).forEach(([key, item]) => {\n      if (now - item.timestamp > item.ttl) {\n        this.cache.delete(key);\n      }\n    });\n  }\n}\n\nexport const cache = Cache.getInstance();\n\n// 定期清理过期缓存\nif (typeof window === 'undefined') {\n  setInterval(() => {\n    cache.cleanup();\n  }, 60 * 1000); // 每分钟清理一次\n} "
  },
  {
    "path": "website/app/lib/github.ts",
    "content": "import { cache } from './cache';\n\ninterface GitHubIssue {\n  id: number;\n  number: number;\n  title: string;\n  body: string;\n  created_at: string;\n  updated_at: string;\n  user: {\n    login: string;\n    avatar_url: string;\n  };\n  comments: number;\n  reactions: {\n    total_count: number;\n  };\n  labels: Array<{\n    name: string;\n    color: string;\n  }>;\n}\n\ninterface Article {\n  id: number;\n  title: string;\n  url: string;\n  image?: string;\n  category?: string;\n  created_at: string;\n  author: string;\n  comments: number;\n  reactions: number;\n}\n\nexport class GitHubAPI {\n  private static readonly REPO_OWNER = 'chokcoco';\n  private static readonly REPO_NAME = 'iCSS';\n  private static readonly BASE_URL = 'https://api.github.com';\n  \n  // 获取 GitHub Token\n  private static getGitHubToken(): string | undefined {\n    return process.env.GITHUB_TOKEN;\n  }\n  \n  // 构建请求头\n  private static getHeaders(): HeadersInit {\n    const headers: HeadersInit = {\n      'Accept': 'application/vnd.github.v3+json',\n      'User-Agent': 'iCSS-Website'\n    };\n    \n    const token = this.getGitHubToken();\n    if (token) {\n      headers['Authorization'] = `token ${token}`;\n    }\n    \n    return headers;\n  }\n  \n  // 从 issue 内容中提取图片\n  private static extractImageFromBody(body: string): string | undefined {\n    if (!body) return undefined;\n    \n    // 匹配 Markdown 图片 ![alt](url)\n    const markdownImageMatch = body.match(/!\\[.*?\\]\\((https?:\\/\\/[^)]+\\.(?:png|jpg|jpeg|gif|webp))\\)/);\n    if (markdownImageMatch) {\n      return markdownImageMatch[1];\n    }\n    \n    // 匹配 HTML 图片标签 <img src=\"...\">\n    const htmlImageMatch = body.match(/<img[^>]+src\\s*=\\s*[\"'](https?:\\/\\/[^\"']+\\.(?:png|jpg|jpeg|gif|webp))[\"'][^>]*>/);\n    if (htmlImageMatch) {\n      return htmlImageMatch[1];\n    }\n    \n    return undefined;\n  }\n  \n  // 从 issue 内容中提取分类\n  private static extractCategoryFromBody(body: string): string | undefined {\n    if (!body) return undefined;\n    \n    // 匹配标题中的分类标记，如 【动画】、【布局】等\n    const categoryMatch = body.match(/【([^】]+)】/);\n    if (categoryMatch) {\n      return categoryMatch[1];\n    }\n    \n    // 匹配标签中的分类\n    const tagMatch = body.match(/标签[：:]\\s*([^\\n\\r]+)/);\n    if (tagMatch) {\n      return tagMatch[1].trim();\n    }\n    \n    return undefined;\n  }\n  \n  // 转换 GitHub issue 为文章格式\n  private static transformIssueToArticle(issue: GitHubIssue): Article {\n    const image = this.extractImageFromBody(issue.body);\n    const category = this.extractCategoryFromBody(issue.body);\n    \n    return {\n      id: issue.number,\n      title: issue.title,\n      url: `https://github.com/${this.REPO_OWNER}/${this.REPO_NAME}/issues/${issue.number}`,\n      image,\n      category,\n      created_at: issue.created_at,\n      author: issue.user.login,\n      comments: issue.comments,\n      reactions: issue.reactions.total_count\n    };\n  }\n  \n  // 获取所有 issues\n  static async getIssues(page: number = 1, perPage: number = 30): Promise<Article[]> {\n    const cacheKey = `issues_${page}_${perPage}`;\n    const cached = cache.get<Article[]>(cacheKey);\n    if (cached) {\n      return cached;\n    }\n    \n    try {\n      const url = `${this.BASE_URL}/repos/${this.REPO_OWNER}/${this.REPO_NAME}/issues?state=open&sort=created&direction=desc&page=${page}&per_page=${perPage}`;\n      \n      const response = await this.fetchWithRetry(url, {\n        headers: this.getHeaders()\n      });\n      \n      if (!response.ok) {\n        console.error(`GitHub API error: ${response.status} - ${response.statusText}`);\n        // 如果是限流错误，返回空数组而不是抛出错误\n        if (response.status === 403) {\n          console.error('GitHub API rate limit exceeded');\n          return [];\n        }\n        return [];\n      }\n      \n      const issues: GitHubIssue[] = await response.json();\n      \n      // 调试：打印每个 issue 的作者和标题\n      console.log('GitHub issues user:', issues.map(i => ({user: i.user.login, title: i.title})));\n      \n      if (!Array.isArray(issues)) {\n        console.error('GitHub API returned non-array issues:', issues);\n        return [];\n      }\n      \n      console.log('issues', issues);\n\n      // 过滤掉非文章类型的 issue（如 bug 报告等）\n      const articleIssues = issues.filter(issue => {\n        // 只排除明显的非文章内容\n        const excludeKeywords = ['bug', '问题', '建议', '求助', 'question', 'help', 'error'];\n        const hasExcludeKeyword = excludeKeywords.some(keyword => \n          issue.title.toLowerCase().includes(keyword.toLowerCase())\n        );\n        \n        // 排除用户不是 chokcoco 的 issue（通常是用户提问）\n        const isAuthor = issue.user.login === 'chokcoco';\n        \n        return !hasExcludeKeyword && isAuthor;\n      });\n      \n      console.log('Filtered issues count:', articleIssues.length);\n      console.log('Filtered issues:', articleIssues.map(i => ({user: i.user.login, title: i.title})));\n      \n      const articles = articleIssues.map(issue => this.transformIssueToArticle(issue));\n      \n      console.log('Transformed articles count:', articles.length);\n      console.log('Transformed articles:', articles.map(a => ({id: a.id, title: a.title})));\n      \n      // 缓存结果（30分钟）\n      cache.set(cacheKey, articles, 30 * 60 * 1000);\n      \n      return articles;\n    } catch (error) {\n      console.error('Error fetching issues from GitHub:', error);\n      return [];\n    }\n  }\n  \n  // 获取单个 issue 详情\n  static async getIssue(issueNumber: number): Promise<{\n    title: string;\n    body: string;\n    created_at: string;\n    user: { login: string; avatar_url: string };\n    comments: number;\n    reactions: { total_count: number };\n    image?: string;\n    category?: string;\n  }> {\n    const cacheKey = `issue_${issueNumber}`;\n    const cached = cache.get<{\n      title: string;\n      body: string;\n      created_at: string;\n      user: { login: string; avatar_url: string };\n      comments: number;\n      reactions: { total_count: number };\n      image?: string;\n      category?: string;\n    }>(cacheKey);\n    \n    if (cached) {\n      console.log('Using cached issue data for:', issueNumber);\n      return cached;\n    }\n    \n    try {\n      const url = `${this.BASE_URL}/repos/${this.REPO_OWNER}/${this.REPO_NAME}/issues/${issueNumber}`;\n      console.log('Fetching issue from:', url);\n      \n      const response = await this.fetchWithRetry(url, {\n        headers: this.getHeaders()\n      });\n      \n      if (!response.ok) {\n        console.error(`GitHub API error: ${response.status} - ${response.statusText}`);\n        if (response.status === 403) {\n          console.error('GitHub API rate limit exceeded');\n          throw new Error('GitHub API rate limit exceeded');\n        }\n        throw new Error(`GitHub API error: ${response.status}`);\n      }\n      \n      const issue: GitHubIssue = await response.json();\n      console.log('Fetched issue data:', { \n        number: issue.number, \n        title: issue.title, \n        bodyLength: issue.body?.length || 0 \n      });\n      \n      const image = this.extractImageFromBody(issue.body);\n      const category = this.extractCategoryFromBody(issue.body);\n      \n      const result = {\n        title: issue.title,\n        body: issue.body || '',\n        created_at: issue.created_at,\n        user: issue.user,\n        comments: issue.comments,\n        reactions: issue.reactions,\n        image,\n        category\n      };\n      \n      // 缓存结果（60分钟）\n      cache.set(cacheKey, result, 60 * 60 * 1000);\n      \n      return result;\n    } catch (error) {\n      console.error('Error fetching issue from GitHub:', error);\n      throw error;\n    }\n  }\n  \n  // 获取所有文章（分页获取）\n  static async getAllArticles(): Promise<Article[]> {\n    const cacheKey = 'all_articles';\n    const cached = cache.get<Article[]>(cacheKey);\n    if (cached) {\n      return cached;\n    }\n    \n    const allArticles: Article[] = [];\n    let page = 1;\n    const perPage = 100; // GitHub API 最大支持 100\n    \n    try {\n      while (true) {\n        const articles = await this.getIssues(page, perPage);\n        \n        if (!Array.isArray(articles) || articles.length === 0) {\n          break; // 没有更多数据\n        }\n        \n        allArticles.push(...articles);\n        \n        if (articles.length < perPage) {\n          break; // 最后一页\n        }\n        \n        page++;\n        \n        // 限制最大页数，避免无限循环\n        if (page > 20) {\n          break;\n        }\n      }\n      \n      // 缓存结果（120分钟）\n      cache.set(cacheKey, allArticles, 120 * 60 * 1000);\n      \n      return allArticles;\n    } catch (error) {\n      console.error('Error fetching all articles:', error);\n      // 返回空数组而不是抛出错误\n      return [];\n    }\n  }\n  \n  // 带重试的 fetch 函数\n  private static async fetchWithRetry(url: string, options: RequestInit, retries: number = 3): Promise<Response> {\n    for (let i = 0; i < retries; i++) {\n      try {\n        const response = await fetch(url, options);\n        if (response.ok || response.status === 403) {\n          return response;\n        }\n        // 如果不是 403 错误，等待后重试\n        if (i < retries - 1) {\n          await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1))); // 递增延迟\n        }\n      } catch (error) {\n        console.error(`Fetch attempt ${i + 1} failed:`, error);\n        if (i === retries - 1) {\n          throw error;\n        }\n        await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));\n      }\n    }\n    throw new Error(`Failed after ${retries} retries`);\n  }\n} "
  },
  {
    "path": "website/app/lib/language.ts",
    "content": "export type Language = 'zh' | 'en';\n\nexport interface LanguageConfig {\n  name: string;\n  value: Language;\n  flag: string;\n}\n\nexport const languages: LanguageConfig[] = [\n  {\n    name: '中文',\n    value: 'zh',\n    flag: '🇨🇳'\n  },\n  {\n    name: 'English',\n    value: 'en',\n    flag: '🇺🇸'\n  }\n];\n\nexport function getStoredLanguage(): Language {\n  if (typeof window === 'undefined') return 'zh';\n  return (localStorage.getItem('language') as Language) || 'zh';\n}\n\nexport function setStoredLanguage(language: Language) {\n  if (typeof window === 'undefined') return;\n  localStorage.setItem('language', language);\n} "
  },
  {
    "path": "website/app/lib/theme.ts",
    "content": "export type Theme = 'light' | 'dark' | 'system';\n\nexport interface ThemeConfig {\n  name: string;\n  value: Theme;\n  icon: string;\n}\n\nexport const themes: ThemeConfig[] = [\n  {\n    name: '亮色',\n    value: 'light',\n    icon: '☀️'\n  },\n  {\n    name: '暗色',\n    value: 'dark',\n    icon: '🌙'\n  },\n  {\n    name: '跟随系统',\n    value: 'system',\n    icon: '💻'\n  }\n];\n\nexport function getSystemTheme(): 'light' | 'dark' {\n  if (typeof window === 'undefined') return 'light';\n  return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';\n}\n\nexport function applyTheme(theme: Theme) {\n  if (typeof window === 'undefined') return;\n  \n  const root = document.documentElement;\n  const systemTheme = getSystemTheme();\n  \n  // 移除所有主题类\n  root.classList.remove('light', 'dark');\n  \n  // 应用主题\n  if (theme === 'system') {\n    root.classList.add(systemTheme);\n  } else {\n    root.classList.add(theme);\n  }\n  \n  // 保存到 localStorage\n  localStorage.setItem('theme', theme);\n}\n\nexport function getStoredTheme(): Theme {\n  if (typeof window === 'undefined') return 'system';\n  return (localStorage.getItem('theme') as Theme) || 'system';\n}\n\nexport function initializeTheme() {\n  const theme = getStoredTheme();\n  applyTheme(theme);\n  \n  // 监听系统主题变化\n  if (typeof window !== 'undefined') {\n    const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');\n    mediaQuery.addEventListener('change', () => {\n      if (getStoredTheme() === 'system') {\n        applyTheme('system');\n      }\n    });\n  }\n} "
  },
  {
    "path": "website/app/lib/translations.ts",
    "content": "import { Language } from './language';\n\nexport interface Translations {\n  // 通用\n  loading: string;\n  error: string;\n  back: string;\n  next: string;\n  prev: string;\n  search: string;\n  category: string;\n  all: string;\n  \n  // 首页\n  title: string;\n  description: string;\n  keywords: string;\n  viewOnGitHub: string;\n  lastArticle: string;\n  noMoreArticles: string;\n  \n  // 文章详情页\n  articleNotFound: string;\n  loadFailed: string;\n  returnHome: string;\n  viewFullContent: string;\n  nextArticle: string;\n  prevArticle: string;\n  returnList: string;\n  viewInCodePen: string;\n  \n  // 主题\n  light: string;\n  dark: string;\n  system: string;\n  theme: string;\n  \n  // 语言\n  language: string;\n  chinese: string;\n  english: string;\n}\n\nexport const translations: Record<Language, Translations> = {\n  zh: {\n    // 通用\n    loading: '加载中...',\n    error: '错误',\n    back: '返回',\n    next: '下一个',\n    prev: '上一个',\n    search: '搜索',\n    category: '分类',\n    all: '全部',\n    \n    // 首页\n    title: 'iCSS - CSS 奇技淫巧',\n    description: 'CSS 奇技淫巧，在这里，都有。本 Repo 围绕 CSS/Web动画 展开，谈一些有趣的话题，内容天马行空，想到什么说什么。',\n    keywords: 'CSS, 动画, 前端, 技巧, 奇技淫巧',\n    viewOnGitHub: '在 GitHub 中查看',\n    lastArticle: '已经是最后一篇文章了',\n    noMoreArticles: '没有更多文章了',\n    \n    // 文章详情页\n    articleNotFound: '文章不存在',\n    loadFailed: '加载失败',\n    returnHome: '返回首页',\n    viewFullContent: '在 GitHub 中查看完整内容',\n    nextArticle: '下一篇文章',\n    prevArticle: '上一篇文章',\n    returnList: '返回列表',\n    viewInCodePen: '在 CodePen 中查看完整代码',\n    \n    // 主题\n    light: '亮色',\n    dark: '暗色',\n    system: '跟随系统',\n    theme: '主题',\n    \n    // 语言\n    language: '语言',\n    chinese: '中文',\n    english: 'English'\n  },\n  en: {\n    // 通用\n    loading: 'Loading...',\n    error: 'Error',\n    back: 'Back',\n    next: 'Next',\n    prev: 'Previous',\n    search: 'Search',\n    category: 'Category',\n    all: 'All',\n    \n    // 首页\n    title: 'iCSS - CSS Tricks',\n    description: 'CSS tricks and techniques. This repo focuses on CSS/Web animations, discussing interesting topics with creative content.',\n    keywords: 'CSS, Animation, Frontend, Tricks, Techniques',\n    viewOnGitHub: 'View on GitHub',\n    lastArticle: 'This is the last article',\n    noMoreArticles: 'No more articles',\n    \n    // 文章详情页\n    articleNotFound: 'Article not found',\n    loadFailed: 'Load failed',\n    returnHome: 'Return to Home',\n    viewFullContent: 'View full content on GitHub',\n    nextArticle: 'Next Article',\n    prevArticle: 'Previous Article',\n    returnList: 'Return to List',\n    viewInCodePen: 'View full code on CodePen',\n    \n    // 主题\n    light: 'Light',\n    dark: 'Dark',\n    system: 'System',\n    theme: 'Theme',\n    \n    // 语言\n    language: 'Language',\n    chinese: '中文',\n    english: 'English'\n  }\n};\n\nexport function getTranslation(language: Language, key: keyof Translations): string {\n  return translations[language][key];\n} "
  },
  {
    "path": "website/app/page.tsx",
    "content": "'use client';\n\nimport { useState, useEffect } from 'react';\nimport { motion } from 'framer-motion';\nimport { Search, Filter, ExternalLink, Calendar, User, Eye } from 'lucide-react';\nimport Link from 'next/link';\nimport Image from 'next/image';\nimport { useApp } from './contexts/AppContext';\nimport ThemeToggle from './components/ThemeToggle';\nimport LanguageToggle from './components/LanguageToggle';\n\ninterface Article {\n  id: number;\n  title: string;\n  url: string;\n  image?: string;\n  category?: string;\n  created_at: string;\n  author: string;\n  comments: number;\n  reactions: number;\n}\n\nexport default function HomePage() {\n  const { t } = useApp();\n  const [articles, setArticles] = useState<Article[]>([]);\n  const [categories, setCategories] = useState<string[]>([]);\n  const [selectedCategory, setSelectedCategory] = useState<string>('全部');\n  const [searchTerm, setSearchTerm] = useState('');\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState<string | null>(null);\n  const [currentPage, setCurrentPage] = useState(1);\n  const [hasMore, setHasMore] = useState(true);\n\n  // 获取文章数据\n  const fetchArticles = async (page: number = 1, append: boolean = false) => {\n    try {\n      const params = new URLSearchParams({\n        page: page.toString(),\n        per_page: '12'\n      });\n      \n      if (selectedCategory !== '全部') {\n        params.append('category', selectedCategory);\n      }\n      \n      if (searchTerm) {\n        params.append('search', searchTerm);\n      }\n\n      const response = await fetch(`/api/articles?${params}`);\n      const data = await response.json();\n      console.log('Articles API response:', data);\n\n      // 检查是否有错误信息\n      if (data.error) {\n        console.error('API returned error:', data.error);\n        setError(data.error);\n        setLoading(false);\n        return;\n      }\n\n      // 确保数据格式正确\n      const articles = Array.isArray(data.articles) ? data.articles : [];\n      const pagination = data.pagination || { hasNext: false };\n      console.log('Processed articles:', articles.length, 'pagination:', pagination);\n\n      if (append) {\n        setArticles(prev => [...prev, ...articles]);\n      } else {\n        setArticles(articles);\n      }\n      \n      setHasMore(pagination.hasNext);\n      setLoading(false);\n    } catch (err) {\n      console.error('Failed to fetch articles:', err);\n      setError('网络连接失败，请检查网络后重试');\n      setLoading(false);\n    }\n  };\n\n  // 获取分类数据\n  const fetchCategories = async () => {\n    try {\n      const response = await fetch('/api/categories');\n      const data = await response.json();\n      console.log('Categories API response:', data);\n      // API 直接返回数组，不是 { categories: [...] } 格式\n      const categoriesData = Array.isArray(data) ? data : [];\n      console.log('Processed categories:', categoriesData);\n      setCategories(categoriesData);\n    } catch (err) {\n      console.error('Failed to fetch categories:', err);\n      setCategories(['全部']); // 设置默认值\n    }\n  };\n\n  useEffect(() => {\n    fetchArticles(1, false);\n    fetchCategories();\n  }, []);\n\n  useEffect(() => {\n    setCurrentPage(1);\n    fetchArticles(1, false);\n  }, [selectedCategory, searchTerm]);\n\n  const loadMore = () => {\n    const nextPage = currentPage + 1;\n    setCurrentPage(nextPage);\n    fetchArticles(nextPage, true);\n  };\n\n  const formatDate = (dateString: string) => {\n    return new Date(dateString).toLocaleDateString('zh-CN', {\n      year: 'numeric',\n      month: 'long',\n      day: 'numeric'\n    });\n  };\n\n  if (loading && articles.length === 0) {\n    return (\n      <div className=\"min-h-screen bg-gray-50 dark:bg-gray-900\">\n        <div className=\"flex items-center justify-center min-h-screen\">\n          <div className=\"animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600\"></div>\n        </div>\n      </div>\n    );\n  }\n\n  if (error) {\n    return (\n      <div className=\"min-h-screen bg-gray-50 dark:bg-gray-900 flex items-center justify-center\">\n        <div className=\"text-center\">\n          <h1 className=\"text-2xl font-bold text-gray-900 dark:text-gray-100 mb-4\">{t('error')}</h1>\n          <p className=\"text-gray-600 dark:text-gray-400 mb-4\">{error}</p>\n          <button \n            onClick={() => fetchArticles(1, false)}\n            className=\"btn-primary\"\n          >\n            重试\n          </button>\n        </div>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"min-h-screen bg-gray-50 dark:bg-gray-900\">\n      {/* 头部 */}\n      <header className=\"bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700\">\n        <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n          <div className=\"flex items-center justify-between h-16\">\n            <div className=\"flex items-center space-x-4\">\n              <h1 className=\"text-xl font-bold text-gray-900 dark:text-gray-100\">\n                iCSS\n              </h1>\n              <span className=\"text-sm text-gray-500 dark:text-gray-400\">\n                CSS 奇技淫巧\n              </span>\n            </div>\n            \n            {/* 主题和语言切换 */}\n            <div className=\"flex items-center space-x-3\">\n              <ThemeToggle />\n              <LanguageToggle />\n            </div>\n          </div>\n        </div>\n      </header>\n\n      <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8\">\n        {/* 搜索和筛选 */}\n        <div className=\"mb-8 space-y-4\">\n          <div className=\"flex flex-col sm:flex-row gap-4\">\n            <div className=\"flex-1 relative\">\n              <Search className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5\" />\n              <input\n                type=\"text\"\n                placeholder={t('search')}\n                value={searchTerm}\n                onChange={(e) => setSearchTerm(e.target.value)}\n                className=\"input pl-10\"\n              />\n            </div>\n            \n            <div className=\"relative\">\n              <Filter className=\"absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400 w-5 h-5\" />\n              <select\n                value={selectedCategory}\n                onChange={(e) => setSelectedCategory(e.target.value)}\n                className=\"input pl-10 appearance-none cursor-pointer\"\n              >\n                <option value=\"全部\">{t('all')}</option>\n                {categories.map((category) => (\n                  <option key={category} value={category}>\n                    {category}\n                  </option>\n                ))}\n              </select>\n            </div>\n          </div>\n        </div>\n\n        {/* 文章列表 */}\n        <div className=\"grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6\">\n          {articles.map((article, index) => (\n            <motion.div\n              key={article.id}\n              initial={{ opacity: 0, y: 20 }}\n              animate={{ opacity: 1, y: 0 }}\n              transition={{ duration: 0.5, delay: index * 0.1 }}\n              className=\"card hover:shadow-lg transition-shadow duration-200\"\n            >\n              <Link href={`/article/${article.id}`}>\n                <div className=\"p-6\">\n                  {/* 文章配图 */}\n                  {article.image && (\n                    <div className=\"relative h-48 mb-4 rounded-lg overflow-hidden\">\n                      <Image\n                        src={article.image}\n                        alt={article.title}\n                        fill\n                        className=\"object-cover\"\n                      />\n                    </div>\n                  )}\n                  \n                  {/* 文章信息 */}\n                  <div className=\"space-y-3\">\n                    <div className=\"flex items-center space-x-2\">\n                      <span className=\"text-sm text-gray-500 dark:text-gray-400\">#{article.id}</span>\n                      {article.category && (\n                        <span className=\"text-xs px-2 py-1 rounded-full bg-blue-100 dark:bg-blue-900 text-blue-800 dark:text-blue-200\">\n                          {article.category}\n                        </span>\n                      )}\n                    </div>\n                    \n                    <h2 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100 line-clamp-2 hover:text-primary-600 dark:hover:text-primary-400 transition-colors\">\n                      {article.title}\n                    </h2>\n                    \n                    {/* 文章元信息 */}\n                    <div className=\"flex items-center space-x-4 text-sm text-gray-500 dark:text-gray-400\">\n                      <div className=\"flex items-center space-x-1\">\n                        <User className=\"w-4 h-4\" />\n                        <span>{article.author}</span>\n                      </div>\n                      <div className=\"flex items-center space-x-1\">\n                        <Calendar className=\"w-4 h-4\" />\n                        <span>{formatDate(article.created_at)}</span>\n                      </div>\n                      <div className=\"flex items-center space-x-1\">\n                        <Eye className=\"w-4 h-4\" />\n                        <span>{article.reactions}</span>\n                      </div>\n                    </div>\n                  </div>\n                </div>\n              </Link>\n              \n              {/* 外部链接 */}\n              <div className=\"px-6 pb-4\">\n                <a\n                  href={article.url}\n                  target=\"_blank\"\n                  rel=\"noopener noreferrer\"\n                  className=\"inline-flex items-center space-x-1 text-sm text-gray-500 dark:text-gray-400 hover:text-primary-600 dark:hover:text-primary-400 transition-colors\"\n                >\n                  <ExternalLink className=\"w-4 h-4\" />\n                  <span>{t('viewOnGitHub')}</span>\n                </a>\n              </div>\n            </motion.div>\n          ))}\n        </div>\n\n        {/* 加载更多 */}\n        {hasMore && (\n          <div className=\"mt-8 text-center\">\n            <button\n              onClick={loadMore}\n              className=\"btn-primary\"\n              disabled={loading}\n            >\n              {loading ? t('loading') : '加载更多'}\n            </button>\n          </div>\n        )}\n\n        {/* 没有更多文章 */}\n        {!hasMore && articles.length > 0 && (\n          <div className=\"mt-8 text-center\">\n            <p className=\"text-gray-500 dark:text-gray-400\">{t('noMoreArticles')}</p>\n          </div>\n        )}\n      </div>\n    </div>\n  );\n} \n "
  },
  {
    "path": "website/app/test-api/page.tsx",
    "content": "'use client';\n\nimport { useState, useEffect } from 'react';\n\ninterface Article {\n  id: number;\n  title: string;\n  url: string;\n  image?: string;\n  category?: string;\n  created_at: string;\n  author: string;\n  comments: number;\n  reactions: number;\n}\n\ninterface ArticlesResponse {\n  articles: Article[];\n  pagination: {\n    page: number;\n    perPage: number;\n    total: number;\n    totalPages: number;\n    hasNext: boolean;\n    hasPrev: boolean;\n  };\n}\n\nexport default function TestAPIPage() {\n  const [articles, setArticles] = useState<ArticlesResponse | null>(null);\n  const [categories, setCategories] = useState<string[] | null>(null);\n  const [loading, setLoading] = useState(true);\n  const [error, setError] = useState<string | null>(null);\n\n  useEffect(() => {\n    const testAPIs = async () => {\n      try {\n        setLoading(true);\n        setError(null);\n\n        // 测试文章 API\n        const articlesRes = await fetch('/api/articles');\n        const articlesData = await articlesRes.json();\n        setArticles(articlesData);\n\n        // 测试分类 API\n        const categoriesRes = await fetch('/api/categories');\n        const categoriesData = await categoriesRes.json();\n        setCategories(categoriesData);\n\n      } catch (err) {\n        setError(err instanceof Error ? err.message : '未知错误');\n      } finally {\n        setLoading(false);\n      }\n    };\n\n    testAPIs();\n  }, []);\n\n  if (loading) {\n    return (\n      <div className=\"min-h-screen flex items-center justify-center\">\n        <div className=\"animate-spin rounded-full h-32 w-32 border-b-2 border-primary-600\"></div>\n      </div>\n    );\n  }\n\n  return (\n    <div className=\"min-h-screen bg-gray-50 py-8\">\n      <div className=\"max-w-4xl mx-auto px-4\">\n        <h1 className=\"text-3xl font-bold text-gray-900 mb-8\">API 测试页面</h1>\n\n        {error && (\n          <div className=\"bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded mb-6\">\n            <strong>错误：</strong> {error}\n          </div>\n        )}\n\n        <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-8\">\n          {/* 文章 API 测试 */}\n          <div className=\"bg-white rounded-lg shadow-lg p-6\">\n            <h2 className=\"text-xl font-semibold text-gray-800 mb-4\">文章 API 测试</h2>\n            {articles ? (\n              <div>\n                <div className=\"mb-4\">\n                  <strong>状态：</strong> \n                  <span className=\"text-green-600 ml-2\">✅ 成功</span>\n                </div>\n                <div className=\"mb-4\">\n                  <strong>文章数量：</strong> {articles.articles?.length || 0}\n                </div>\n                <div className=\"mb-4\">\n                  <strong>分页信息：</strong>\n                  <pre className=\"bg-gray-100 p-2 rounded text-sm mt-2 overflow-auto\">\n                    {JSON.stringify(articles.pagination, null, 2)}\n                  </pre>\n                </div>\n                {articles.articles && articles.articles.length > 0 && (\n                  <div>\n                    <strong>第一篇文章：</strong>\n                    <div className=\"bg-gray-100 p-2 rounded text-sm mt-2\">\n                      <div><strong>ID：</strong> {articles.articles[0].id}</div>\n                      <div><strong>标题：</strong> {articles.articles[0].title}</div>\n                      <div><strong>分类：</strong> {articles.articles[0].category || '无'}</div>\n                      <div><strong>作者：</strong> {articles.articles[0].author}</div>\n                    </div>\n                  </div>\n                )}\n              </div>\n            ) : (\n              <div className=\"text-red-600\">❌ 获取失败</div>\n            )}\n          </div>\n\n          {/* 分类 API 测试 */}\n          <div className=\"bg-white rounded-lg shadow-lg p-6\">\n            <h2 className=\"text-xl font-semibold text-gray-800 mb-4\">分类 API 测试</h2>\n            {categories ? (\n              <div>\n                <div className=\"mb-4\">\n                  <strong>状态：</strong> \n                  <span className=\"text-green-600 ml-2\">✅ 成功</span>\n                </div>\n                <div className=\"mb-4\">\n                  <strong>分类数量：</strong> {categories.length}\n                </div>\n                <div className=\"mb-4\">\n                  <strong>分类列表：</strong>\n                  <div className=\"flex flex-wrap gap-2 mt-2\">\n                    {categories.map((category: string, index: number) => (\n                      <span\n                        key={index}\n                        className=\"px-2 py-1 bg-blue-100 text-blue-800 rounded text-sm\"\n                      >\n                        {category}\n                      </span>\n                    ))}\n                  </div>\n                </div>\n              </div>\n            ) : (\n              <div className=\"text-red-600\">❌ 获取失败</div>\n            )}\n          </div>\n        </div>\n\n        {/* 原始数据 */}\n        <div className=\"mt-8 bg-white rounded-lg shadow-lg p-6\">\n          <h2 className=\"text-xl font-semibold text-gray-800 mb-4\">原始数据</h2>\n          <div className=\"grid grid-cols-1 lg:grid-cols-2 gap-4\">\n            <div>\n              <h3 className=\"font-medium mb-2\">文章 API 响应：</h3>\n              <pre className=\"bg-gray-100 p-4 rounded text-xs overflow-auto max-h-96\">\n                {JSON.stringify(articles, null, 2)}\n              </pre>\n            </div>\n            <div>\n              <h3 className=\"font-medium mb-2\">分类 API 响应：</h3>\n              <pre className=\"bg-gray-100 p-4 rounded text-xs overflow-auto max-h-96\">\n                {JSON.stringify(categories, null, 2)}\n              </pre>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n} "
  },
  {
    "path": "website/app/test-demo/page.tsx",
    "content": "import CodeBlock from '../components/CodeBlock';\n\nexport default function TestDemoPage() {\n  const demoCode = `<!-- CodePen Demo -->\n<!-- HTML -->\n<div class=\"container\">\n  <div class=\"card\">\n    <div class=\"card-header\">\n      <h3>CSS 动画演示</h3>\n    </div>\n    <div class=\"card-body\">\n      <div class=\"animated-box\">\n        <span>悬停我</span>\n      </div>\n    </div>\n  </div>\n</div>\n\n<!-- CSS -->\n.container {\n  display: flex;\n  justify-content: center;\n  align-items: center;\n  min-height: 100vh;\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;\n}\n\n.card {\n  background: white;\n  border-radius: 12px;\n  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);\n  overflow: hidden;\n  width: 300px;\n}\n\n.card-header {\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n  color: white;\n  padding: 20px;\n  text-align: center;\n}\n\n.card-header h3 {\n  margin: 0;\n  font-size: 18px;\n  font-weight: 600;\n}\n\n.card-body {\n  padding: 30px;\n  text-align: center;\n}\n\n.animated-box {\n  display: inline-block;\n  padding: 15px 30px;\n  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n  color: white;\n  border-radius: 8px;\n  cursor: pointer;\n  transition: all 0.3s ease;\n  position: relative;\n  overflow: hidden;\n}\n\n.animated-box::before {\n  content: '';\n  position: absolute;\n  top: 0;\n  left: -100%;\n  width: 100%;\n  height: 100%;\n  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent);\n  transition: left 0.5s ease;\n}\n\n.animated-box:hover {\n  transform: translateY(-3px) scale(1.05);\n  box-shadow: 0 15px 35px rgba(102, 126, 234, 0.4);\n}\n\n.animated-box:hover::before {\n  left: 100%;\n}\n\n.animated-box span {\n  position: relative;\n  z-index: 1;\n  font-weight: 600;\n}\n\n<!-- JavaScript -->\n// 添加点击效果\ndocument.addEventListener('DOMContentLoaded', function() {\n  const animatedBox = document.querySelector('.animated-box');\n  \n  animatedBox.addEventListener('click', function() {\n    this.style.transform = 'translateY(-3px) scale(1.05) rotate(5deg)';\n    setTimeout(() => {\n      this.style.transform = 'translateY(-3px) scale(1.05)';\n    }, 200);\n  });\n});\n`;\n\n  const htmlCode = `<!DOCTYPE html>\n<html lang=\"zh-CN\">\n<head>\n    <meta charset=\"UTF-8\">\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\n    <title>HTML 示例</title>\n    <style>\n        body {\n            font-family: Arial, sans-serif;\n            margin: 0;\n            padding: 20px;\n            background: #f5f5f5;\n        }\n        .container {\n            max-width: 800px;\n            margin: 0 auto;\n            background: white;\n            padding: 20px;\n            border-radius: 8px;\n            box-shadow: 0 2px 10px rgba(0,0,0,0.1);\n        }\n        .highlight {\n            background: #ffeb3b;\n            padding: 2px 4px;\n            border-radius: 3px;\n        }\n        .button {\n            background: #2196f3;\n            color: white;\n            border: none;\n            padding: 10px 20px;\n            border-radius: 5px;\n            cursor: pointer;\n            margin: 5px;\n        }\n        .button:hover {\n            background: #1976d2;\n        }\n    </style>\n</head>\n<body>\n    <div class=\"container\">\n        <h1>HTML 代码高亮演示</h1>\n        <p>这是一个 <span class=\"highlight\">HTML</span> 页面的示例，展示了各种 HTML 元素的使用。</p>\n        \n        <h2>表单元素</h2>\n        <form>\n            <label for=\"name\">姓名：</label>\n            <input type=\"text\" id=\"name\" name=\"name\" placeholder=\"请输入姓名\">\n            <br><br>\n            \n            <label for=\"email\">邮箱：</label>\n            <input type=\"email\" id=\"email\" name=\"email\" placeholder=\"请输入邮箱\">\n            <br><br>\n            \n            <label for=\"message\">留言：</label>\n            <textarea id=\"message\" name=\"message\" rows=\"4\" placeholder=\"请输入留言\"></textarea>\n            <br><br>\n            \n            <button type=\"submit\" class=\"button\">提交</button>\n            <button type=\"reset\" class=\"button\">重置</button>\n        </form>\n        \n        <h2>列表</h2>\n        <ul>\n            <li>无序列表项 1</li>\n            <li>无序列表项 2</li>\n            <li>无序列表项 3</li>\n        </ul>\n        \n        <ol>\n            <li>有序列表项 1</li>\n            <li>有序列表项 2</li>\n            <li>有序列表项 3</li>\n        </ol>\n    </div>\n</body>\n</html>`;\n\n  const cssCode = `/* CSS 代码高亮演示 */\n\n/* 基础样式重置 */\n* {\n    margin: 0;\n    padding: 0;\n    box-sizing: border-box;\n}\n\nbody {\n    font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;\n    line-height: 1.6;\n    color: #333;\n    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n    min-height: 100vh;\n}\n\n/* 容器样式 */\n.container {\n    max-width: 1200px;\n    margin: 0 auto;\n    padding: 20px;\n}\n\n/* 卡片组件 */\n.card {\n    background: white;\n    border-radius: 12px;\n    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);\n    overflow: hidden;\n    margin-bottom: 20px;\n    transition: transform 0.3s ease, box-shadow 0.3s ease;\n}\n\n.card:hover {\n    transform: translateY(-5px);\n    box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15);\n}\n\n.card-header {\n    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n    color: white;\n    padding: 20px;\n    font-size: 18px;\n    font-weight: 600;\n}\n\n.card-body {\n    padding: 20px;\n}\n\n/* 按钮样式 */\n.btn {\n    display: inline-block;\n    padding: 12px 24px;\n    border: none;\n    border-radius: 6px;\n    cursor: pointer;\n    font-size: 14px;\n    font-weight: 500;\n    text-decoration: none;\n    transition: all 0.3s ease;\n    position: relative;\n    overflow: hidden;\n}\n\n.btn-primary {\n    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n    color: white;\n}\n\n.btn-primary:hover {\n    transform: translateY(-2px);\n    box-shadow: 0 8px 20px rgba(102, 126, 234, 0.3);\n}\n\n.btn-secondary {\n    background: #6c757d;\n    color: white;\n}\n\n.btn-secondary:hover {\n    background: #5a6268;\n    transform: translateY(-2px);\n}\n\n/* 动画效果 */\n@keyframes fadeIn {\n    from {\n        opacity: 0;\n        transform: translateY(20px);\n    }\n    to {\n        opacity: 1;\n        transform: translateY(0);\n    }\n}\n\n.fade-in {\n    animation: fadeIn 0.6s ease-out;\n}\n\n/* 响应式设计 */\n@media (max-width: 768px) {\n    .container {\n        padding: 10px;\n    }\n    \n    .card {\n        margin-bottom: 15px;\n    }\n    \n    .btn {\n        padding: 10px 20px;\n        font-size: 13px;\n    }\n}\n\n/* 特殊效果 */\n.glow {\n    box-shadow: 0 0 20px rgba(102, 126, 234, 0.5);\n}\n\n.text-gradient {\n    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);\n    -webkit-background-clip: text;\n    -webkit-text-fill-color: transparent;\n    background-clip: text;\n}`;\n\n  const jsCode = `// JavaScript 代码高亮演示\n\n// 类定义\nclass AnimationManager {\n    constructor() {\n        this.animations = new Map();\n        this.isRunning = false;\n    }\n    \n    // 添加动画\n    addAnimation(name, animation) {\n        this.animations.set(name, animation);\n        console.log(\\`动画 \"\\${name}\" 已添加\\`);\n    }\n    \n    // 播放动画\n    playAnimation(name) {\n        const animation = this.animations.get(name);\n        if (animation) {\n            animation.play();\n            console.log(\\`播放动画: \\${name}\\`);\n        } else {\n            console.warn(\\`动画 \"\\${name}\" 不存在\\`);\n        }\n    }\n    \n    // 停止所有动画\n    stopAll() {\n        this.animations.forEach(animation => {\n            if (animation.stop) {\n                animation.stop();\n            }\n        });\n        console.log('所有动画已停止');\n    }\n}\n\n// 工具函数\nconst utils = {\n    // 防抖函数\n    debounce(func, wait) {\n        let timeout;\n        return function executedFunction(...args) {\n            const later = () => {\n                clearTimeout(timeout);\n                func(...args);\n            };\n            clearTimeout(timeout);\n            timeout = setTimeout(later, wait);\n        };\n    },\n    \n    // 节流函数\n    throttle(func, limit) {\n        let inThrottle;\n        return function() {\n            const args = arguments;\n            const context = this;\n            if (!inThrottle) {\n                func.apply(context, args);\n                inThrottle = true;\n                setTimeout(() => inThrottle = false, limit);\n            }\n        };\n    },\n    \n    // 深拷贝\n    deepClone(obj) {\n        if (obj === null || typeof obj !== 'object') {\n            return obj;\n        }\n        \n        if (obj instanceof Date) {\n            return new Date(obj.getTime());\n        }\n        \n        if (obj instanceof Array) {\n            return obj.map(item => this.deepClone(item));\n        }\n        \n        if (typeof obj === 'object') {\n            const clonedObj = {};\n            for (const key in obj) {\n                if (obj.hasOwnProperty(key)) {\n                    clonedObj[key] = this.deepClone(obj[key]);\n                }\n            }\n            return clonedObj;\n        }\n    }\n};\n\n// 事件处理\ndocument.addEventListener('DOMContentLoaded', function() {\n    console.log('页面加载完成');\n    \n    // 初始化动画管理器\n    const animationManager = new AnimationManager();\n    \n    // 添加一些示例动画\n    const fadeInAnimation = {\n        play: () => {\n            document.querySelectorAll('.fade-in').forEach(el => {\n                el.style.opacity = '0';\n                el.style.transform = 'translateY(20px)';\n                \n                setTimeout(() => {\n                    el.style.transition = 'all 0.6s ease-out';\n                    el.style.opacity = '1';\n                    el.style.transform = 'translateY(0)';\n                }, 100);\n            });\n        },\n        stop: () => {\n            document.querySelectorAll('.fade-in').forEach(el => {\n                el.style.transition = 'none';\n                el.style.opacity = '1';\n                el.style.transform = 'translateY(0)';\n            });\n        }\n    };\n    \n    animationManager.addAnimation('fadeIn', fadeInAnimation);\n    \n    // 绑定事件\n    const buttons = document.querySelectorAll('.btn');\n    buttons.forEach(button => {\n        button.addEventListener('click', function(e) {\n            e.preventDefault();\n            console.log(\\`按钮被点击: \\${this.textContent}\\`);\n            \n            // 添加点击效果\n            this.style.transform = 'scale(0.95)';\n            setTimeout(() => {\n                this.style.transform = '';\n            }, 150);\n        });\n    });\n    \n    // 窗口大小变化处理\n    const handleResize = utils.debounce(() => {\n        console.log(\\`窗口大小变化: \\${window.innerWidth}x\\${window.innerHeight}\\`);\n    }, 250);\n    \n    window.addEventListener('resize', handleResize);\n});\n\n// 导出模块（如果使用模块系统）\nif (typeof module !== 'undefined' && module.exports) {\n    module.exports = { AnimationManager, utils };\n}`;\n\n  return (\n    <div className=\"min-h-screen bg-gray-50 py-8\">\n      <div className=\"max-w-4xl mx-auto px-4\">\n        <h1 className=\"text-3xl font-bold text-gray-900 mb-8\">代码高亮和 CodePen Demo 测试</h1>\n        \n        <div className=\"space-y-8\">\n          {/* CodePen Demo */}\n          <section>\n            <h2 className=\"text-2xl font-semibold text-gray-800 mb-4\">CodePen Demo 示例</h2>\n            <CodeBlock language=\"codepen\">\n              {demoCode}\n            </CodeBlock>\n          </section>\n          \n          {/* HTML 代码 */}\n          <section>\n            <h2 className=\"text-2xl font-semibold text-gray-800 mb-4\">HTML 代码高亮</h2>\n            <CodeBlock language=\"html\">\n              {htmlCode}\n            </CodeBlock>\n          </section>\n          \n          {/* CSS 代码 */}\n          <section>\n            <h2 className=\"text-2xl font-semibold text-gray-800 mb-4\">CSS 代码高亮</h2>\n            <CodeBlock language=\"css\">\n              {cssCode}\n            </CodeBlock>\n          </section>\n          \n          {/* JavaScript 代码 */}\n          <section>\n            <h2 className=\"text-2xl font-semibold text-gray-800 mb-4\">JavaScript 代码高亮</h2>\n            <CodeBlock language=\"javascript\">\n              {jsCode}\n            </CodeBlock>\n          </section>\n        </div>\n      </div>\n    </div>\n  );\n} "
  },
  {
    "path": "website/app/test-fixes/page.tsx",
    "content": "import CodeBlock from '../components/CodeBlock';\n\n// 解析行内 Markdown\nconst parseInlineMarkdown = (text: string): React.ReactNode => {\n  const parts: React.ReactNode[] = [];\n  let currentText = '';\n  let i = 0;\n  \n  while (i < text.length) {\n    // 处理 HTML 图片标签 <img ...>\n    if (text.slice(i, i + 4) === '<img') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i += 4;\n      let imgTag = '<img';\n      let inQuotes = false;\n      let quoteChar = '';\n      \n      while (i < text.length) {\n        const char = text[i];\n        imgTag += char;\n        \n        if ((char === '\"' || char === \"'\") && !inQuotes) {\n          inQuotes = true;\n          quoteChar = char;\n        } else if (char === quoteChar && inQuotes) {\n          inQuotes = false;\n        } else if (char === '>' && !inQuotes) {\n          i++;\n          break;\n        }\n        i++;\n      }\n      \n      // 解析 img 标签属性\n      const srcMatch = imgTag.match(/src\\s*=\\s*[\"']([^\"']+)[\"']/);\n      const altMatch = imgTag.match(/alt\\s*=\\s*[\"']([^\"']+)[\"']/);\n      const widthMatch = imgTag.match(/width\\s*=\\s*[\"']?(\\d+)[\"']?/);\n      \n      if (srcMatch) {\n        const src = srcMatch[1];\n        const alt = altMatch ? altMatch[1] : '';\n        const width = widthMatch ? widthMatch[1] : undefined;\n        \n        parts.push(\n          <img \n            key={`html-img-${i}`} \n            src={src} \n            alt={alt} \n            width={width}\n            className=\"max-w-full h-auto rounded-lg my-4\" \n          />\n        );\n      } else {\n        parts.push(imgTag);\n      }\n    }\n    // 处理 Markdown 图片 ![alt](url)\n    else if (text.slice(i, i + 2) === '![') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i += 2;\n      let altText = '';\n      while (i < text.length && text[i] !== ']') {\n        altText += text[i];\n        i++;\n      }\n      if (i < text.length && text[i] === ']' && text[i + 1] === '(') {\n        i += 2;\n        let url = '';\n        while (i < text.length && text[i] !== ')') {\n          url += text[i];\n          i++;\n        }\n        if (i < text.length) {\n          parts.push(\n            <img \n              key={`img-${i}`} \n              src={url} \n              alt={altText} \n              className=\"max-w-full h-auto rounded-lg my-4\" \n            />\n          );\n          i++;\n        } else {\n          parts.push(`![${altText}](${url}`);\n        }\n      } else {\n        parts.push(`![${altText}`);\n      }\n    }\n    // 处理链接 [text](url)\n    else if (text[i] === '[') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i++;\n      let linkText = '';\n      while (i < text.length && text[i] !== ']') {\n        linkText += text[i];\n        i++;\n      }\n      if (i < text.length && text[i] === ']' && text[i + 1] === '(') {\n        i += 2;\n        let url = '';\n        while (i < text.length && text[i] !== ')') {\n          url += text[i];\n          i++;\n        }\n        if (i < text.length) {\n          // 检查是否是 CodePen 链接\n          const codepenMatch = url.match(/codepen\\.io\\/([^\\/]+)\\/pen\\/([^\\/\\?]+)/);\n          if (codepenMatch) {\n            const [, username, penId] = codepenMatch;\n            parts.push(\n              <div key={`codepen-${i}`} className=\"my-6\">\n                <iframe\n                  src={`https://codepen.io/${username}/embed/${penId}?default-tab=result`}\n                  className=\"w-full h-96 border-0 rounded-lg\"\n                  title={`CodePen: ${linkText}`}\n                  loading=\"lazy\"\n                />\n                <div className=\"mt-2 text-center\">\n                  <a \n                    href={url} \n                    target=\"_blank\" \n                    rel=\"noopener noreferrer\" \n                    className=\"text-sm text-gray-600 hover:text-gray-800 underline\"\n                  >\n                    在 CodePen 中查看完整代码 →\n                  </a>\n                </div>\n              </div>\n            );\n          } else {\n            parts.push(\n              <a \n                key={`link-${i}`} \n                href={url} \n                target=\"_blank\" \n                rel=\"noopener noreferrer\" \n                className=\"text-primary-600 hover:text-primary-700 underline\"\n              >\n                {linkText}\n              </a>\n            );\n          }\n          i++;\n        } else {\n          parts.push(`[${linkText}](${url}`);\n        }\n      } else {\n        parts.push(`[${linkText}`);\n      }\n    }\n    // 处理粗体 **text**\n    else if (text.slice(i, i + 2) === '**') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i += 2;\n      let boldText = '';\n      while (i < text.length && text.slice(i, i + 2) !== '**') {\n        boldText += text[i];\n        i++;\n      }\n      if (i < text.length) {\n        parts.push(<strong key={`bold-${i}`} className=\"font-bold\">{boldText}</strong>);\n        i += 2;\n      } else {\n        parts.push(`**${boldText}`);\n      }\n    }\n    // 处理斜体 *text*\n    else if (text[i] === '*') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i++;\n      let italicText = '';\n      while (i < text.length && text[i] !== '*') {\n        italicText += text[i];\n        i++;\n      }\n      if (i < text.length) {\n        parts.push(<em key={`italic-${i}`} className=\"italic\">{italicText}</em>);\n        i++;\n      } else {\n        parts.push(`*${italicText}`);\n      }\n    }\n    // 处理行内代码 `code`\n    else if (text[i] === '`') {\n      if (currentText) {\n        parts.push(currentText);\n        currentText = '';\n      }\n      i++;\n      let codeText = '';\n      while (i < text.length && text[i] !== '`') {\n        codeText += text[i];\n        i++;\n      }\n      if (i < text.length) {\n        parts.push(\n          <code key={`code-${i}`} className=\"bg-gray-100 px-1 py-0.5 rounded text-sm font-mono\">\n            {codeText}\n          </code>\n        );\n        i++;\n      } else {\n        parts.push(`\\`${codeText}`);\n      }\n    } else {\n      currentText += text[i];\n      i++;\n    }\n  }\n  \n  if (currentText) {\n    parts.push(currentText);\n  }\n  \n  return parts.length === 1 ? parts[0] : parts;\n};\n\n// 改进的 Markdown 解析函数\nconst parseMarkdown = (text: string): React.ReactNode[] => {\n  if (!text) return [];\n  \n  const lines = text.split('\\n');\n  const elements: React.ReactNode[] = [];\n  let currentCodeBlock = '';\n  let inCodeBlock = false;\n  let codeLanguage = '';\n  let inList = false;\n  let listItems: React.ReactNode[] = [];\n  \n  for (let i = 0; i < lines.length; i++) {\n    const line = lines[i];\n    \n    // 检测代码块开始\n    if (line.startsWith('```')) {\n      // 结束当前列表\n      if (inList && listItems.length > 0) {\n        elements.push(\n          <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n            {listItems}\n          </ul>\n        );\n        listItems = [];\n        inList = false;\n      }\n      \n      if (!inCodeBlock) {\n        // 开始代码块\n        inCodeBlock = true;\n        codeLanguage = line.slice(3).trim() || 'text';\n        currentCodeBlock = '';\n      } else {\n        // 结束代码块\n        inCodeBlock = false;\n        if (currentCodeBlock.trim()) {\n          // 检测是否是 CodePen 格式\n          const isCodePen = codeLanguage === 'codepen' || \n                           currentCodeBlock.includes('<!-- CodePen Demo -->') ||\n                           currentCodeBlock.includes('<!-- HTML -->') ||\n                           currentCodeBlock.includes('<!-- CSS -->') ||\n                           currentCodeBlock.includes('<!-- JavaScript -->');\n          \n          elements.push(\n            <CodeBlock key={`code-${i}`} language={isCodePen ? 'codepen' : codeLanguage}>\n              {currentCodeBlock.trim()}\n            </CodeBlock>\n          );\n        }\n        currentCodeBlock = '';\n        continue;\n      }\n    } else if (inCodeBlock) {\n      // 在代码块内\n      currentCodeBlock += line + '\\n';\n    } else {\n      // 普通文本处理\n      if (line.trim() === '') {\n        // 结束当前列表\n        if (inList && listItems.length > 0) {\n          elements.push(\n            <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n              {listItems}\n            </ul>\n          );\n          listItems = [];\n          inList = false;\n        }\n        elements.push(<br key={`br-${i}`} />);\n      } else if (line.startsWith('### ')) {\n        // 结束当前列表\n        if (inList && listItems.length > 0) {\n          elements.push(\n            <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n              {listItems}\n            </ul>\n          );\n          listItems = [];\n          inList = false;\n        }\n        elements.push(\n          <h3 key={`h3-${i}`} className=\"text-lg font-bold text-gray-900 mt-6 mb-4\">\n            {parseInlineMarkdown(line.slice(4))}\n          </h3>\n        );\n      } else if (line.startsWith('## ')) {\n        // 结束当前列表\n        if (inList && listItems.length > 0) {\n          elements.push(\n            <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n              {listItems}\n            </ul>\n          );\n          listItems = [];\n          inList = false;\n        }\n        elements.push(\n          <h2 key={`h2-${i}`} className=\"text-xl font-bold text-gray-900 mt-6 mb-4\">\n            {parseInlineMarkdown(line.slice(3))}\n          </h2>\n        );\n      } else if (line.startsWith('# ')) {\n        // 结束当前列表\n        if (inList && listItems.length > 0) {\n          elements.push(\n            <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n              {listItems}\n            </ul>\n          );\n          listItems = [];\n          inList = false;\n        }\n        elements.push(\n          <h1 key={`h1-${i}`} className=\"text-2xl font-bold text-gray-900 mt-6 mb-4\">\n            {parseInlineMarkdown(line.slice(2))}\n          </h1>\n        );\n      } else if (line.startsWith('* ') || line.startsWith('- ')) {\n        inList = true;\n        listItems.push(\n          <li key={`li-${i}`} className=\"mb-1\">\n            {parseInlineMarkdown(line.slice(2))}\n          </li>\n        );\n      } else {\n        // 结束当前列表\n        if (inList && listItems.length > 0) {\n          elements.push(\n            <ul key={`ul-${i}`} className=\"mb-4 pl-6 list-disc\">\n              {listItems}\n            </ul>\n          );\n          listItems = [];\n          inList = false;\n        }\n        \n        // 检查是否是独立的 CodePen 链接\n        const codepenLinkMatch = line.match(/^\\[([^\\]]+)\\]\\((https:\\/\\/codepen\\.io\\/[^\\/]+\\/pen\\/[^\\/\\?]+)\\)$/);\n        if (codepenLinkMatch) {\n          const [, linkText, url] = codepenLinkMatch;\n          const codepenMatch = url.match(/codepen\\.io\\/([^\\/]+)\\/pen\\/([^\\/\\?]+)/);\n          if (codepenMatch) {\n            const [, username, penId] = codepenMatch;\n            elements.push(\n              <div key={`codepen-block-${i}`} className=\"my-6\">\n                <iframe\n                  src={`https://codepen.io/${username}/embed/${penId}?default-tab=result`}\n                  className=\"w-full h-96 border-0 rounded-lg\"\n                  title={`CodePen: ${linkText}`}\n                  loading=\"lazy\"\n                />\n                <div className=\"mt-2 text-center\">\n                  <a \n                    href={url} \n                    target=\"_blank\" \n                    rel=\"noopener noreferrer\" \n                    className=\"text-sm text-gray-600 hover:text-gray-800 underline\"\n                  >\n                    在 CodePen 中查看完整代码 →\n                  </a>\n                </div>\n              </div>\n            );\n          } else {\n            elements.push(\n              <p key={`p-${i}`} className=\"mb-3 leading-relaxed\">\n                {parseInlineMarkdown(line)}\n              </p>\n            );\n          }\n        } else {\n          elements.push(\n            <p key={`p-${i}`} className=\"mb-3 leading-relaxed\">\n              {parseInlineMarkdown(line)}\n            </p>\n          );\n        }\n      }\n    }\n  }\n  \n  // 处理未闭合的列表\n  if (inList && listItems.length > 0) {\n    elements.push(\n      <ul key=\"ul-final\" className=\"mb-4 pl-6 list-disc\">\n        {listItems}\n      </ul>\n    );\n  }\n  \n  // 处理未闭合的代码块\n  if (inCodeBlock && currentCodeBlock.trim()) {\n    // 检测是否是 CodePen 格式\n    const isCodePen = codeLanguage === 'codepen' || \n                     currentCodeBlock.includes('<!-- CodePen Demo -->') ||\n                     currentCodeBlock.includes('<!-- HTML -->') ||\n                     currentCodeBlock.includes('<!-- CSS -->') ||\n                     currentCodeBlock.includes('<!-- JavaScript -->');\n    \n    elements.push(\n      <CodeBlock key=\"code-final\" language={isCodePen ? 'codepen' : codeLanguage}>\n        {currentCodeBlock.trim()}\n      </CodeBlock>\n    );\n  }\n  \n  return elements;\n};\n\nexport default function TestFixesPage() {\n  const testContent = `# 测试修复功能\n\n## HTML 图片标签测试\n\n这是一个 HTML 图片标签：\n<img width=160 src=\"https://raw.githubusercontent.com/chokcoco/chokcoco/main/gzh_style.png\">\n\n这是另一个带 alt 属性的图片：\n<img src=\"https://github.com/chokcoco/iCSS/assets/8554143/aa45cff6-d6b0-4c80-9289-04eac2110075\" alt=\"测试图片\" width=\"300\">\n\n## Markdown 图片测试\n\n这是 Markdown 格式的图片：\n![测试图片](https://github.com/chokcoco/iCSS/assets/8554143/aa45cff6-d6b0-4c80-9289-04eac2110075)\n\n## CodePen 链接测试\n\n这是一个 CodePen 链接：\n[CSS 动画演示](https://codepen.io/chokcoco/pen/abPmOyx)\n\n这是另一个 CodePen 链接：\n[渐变边框效果](https://codepen.io/chokcoco/pen/abPmOyx)\n\n## 普通链接测试\n\n这是一个普通链接：\n[GitHub 仓库](https://github.com/chokcoco/iCSS)\n\n## 混合内容测试\n\n这里有一些**粗体文本**和*斜体文本*，以及一些 \\`行内代码\\`。\n\n还有一张图片：<img src=\"https://github.com/chokcoco/iCSS/assets/8554143/aa45cff6-d6b0-4c80-9289-04eac2110075\" width=\"200\">\n\n以及一个 CodePen 链接：[查看演示](https://codepen.io/chokcoco/pen/abPmOyx)\n\n## 列表测试\n\n- 列表项 1\n- 列表项 2 包含 [链接](https://github.com)\n- 列表项 3 包含图片 <img src=\"https://github.com/chokcoco/iCSS/assets/8554143/aa45cff6-d6b0-4c80-9289-04eac2110075\" width=\"100\">\n\n## 代码块测试\n\n\\`\\`\\`html\n<div class=\"container\">\n  <img src=\"test.jpg\" alt=\"测试\">\n  <a href=\"https://codepen.io\">CodePen</a>\n</div>\n\\`\\`\\`\n\n\\`\\`\\`css\n.container {\n  background: url('image.jpg');\n  border: 1px solid #ccc;\n}\n\\`\\`\\`\n`;\n\n  return (\n    <div className=\"min-h-screen bg-gray-50 py-8\">\n      <div className=\"max-w-4xl mx-auto px-4\">\n        <h1 className=\"text-3xl font-bold text-gray-900 mb-8\">修复功能测试</h1>\n        \n        <div className=\"bg-white rounded-lg shadow-lg p-8\">\n          <div className=\"prose max-w-none\">\n            {parseMarkdown(testContent)}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n} "
  },
  {
    "path": "website/app/test-theme-lang/page.tsx",
    "content": "'use client';\n\nimport { useApp } from '../contexts/AppContext';\nimport ThemeToggle from '../components/ThemeToggle';\nimport LanguageToggle from '../components/LanguageToggle';\n\nexport default function TestThemeLangPage() {\n  const { t, theme, language } = useApp();\n\n  return (\n    <div className=\"min-h-screen bg-gray-50 dark:bg-gray-900\">\n      {/* 头部 */}\n      <header className=\"bg-white dark:bg-gray-800 shadow-sm border-b border-gray-200 dark:border-gray-700\">\n        <div className=\"max-w-7xl mx-auto px-4 sm:px-6 lg:px-8\">\n          <div className=\"flex items-center justify-between h-16\">\n            <div className=\"flex items-center space-x-4\">\n              <h1 className=\"text-xl font-bold text-gray-900 dark:text-gray-100\">\n                主题和语言测试\n              </h1>\n            </div>\n            \n            {/* 主题和语言切换 */}\n            <div className=\"flex items-center space-x-3\">\n              <ThemeToggle />\n              <LanguageToggle />\n            </div>\n          </div>\n        </div>\n      </header>\n\n      <div className=\"max-w-4xl mx-auto px-4 sm:px-6 lg:px-8 py-8\">\n        <div className=\"space-y-8\">\n          {/* 当前状态 */}\n          <div className=\"card p-6\">\n            <h2 className=\"text-xl font-bold text-gray-900 dark:text-gray-100 mb-4\">\n              当前状态\n            </h2>\n            <div className=\"grid grid-cols-1 md:grid-cols-2 gap-4\">\n              <div className=\"p-4 bg-gray-100 dark:bg-gray-700 rounded-lg\">\n                <h3 className=\"font-semibold text-gray-900 dark:text-gray-100 mb-2\">\n                  当前主题\n                </h3>\n                <p className=\"text-gray-600 dark:text-gray-300\">\n                  {theme === 'light' && '☀️ 亮色主题'}\n                  {theme === 'dark' && '🌙 暗色主题'}\n                  {theme === 'system' && '💻 跟随系统'}\n                </p>\n              </div>\n              <div className=\"p-4 bg-gray-100 dark:bg-gray-700 rounded-lg\">\n                <h3 className=\"font-semibold text-gray-900 dark:text-gray-100 mb-2\">\n                  当前语言\n                </h3>\n                <p className=\"text-gray-600 dark:text-gray-300\">\n                  {language === 'zh' && '🇨🇳 中文'}\n                  {language === 'en' && '🇺🇸 English'}\n                </p>\n              </div>\n            </div>\n          </div>\n\n          {/* 翻译测试 */}\n          <div className=\"card p-6\">\n            <h2 className=\"text-xl font-bold text-gray-900 dark:text-gray-100 mb-4\">\n              翻译测试\n            </h2>\n            <div className=\"space-y-4\">\n              <div className=\"p-4 bg-blue-50 dark:bg-blue-900/20 rounded-lg\">\n                <h3 className=\"font-semibold text-blue-900 dark:text-blue-100 mb-2\">\n                  通用翻译\n                </h3>\n                <div className=\"grid grid-cols-2 md:grid-cols-4 gap-2 text-sm\">\n                  <div>加载中: {t('loading')}</div>\n                  <div>错误: {t('error')}</div>\n                  <div>返回: {t('back')}</div>\n                  <div>搜索: {t('search')}</div>\n                </div>\n              </div>\n              \n              <div className=\"p-4 bg-green-50 dark:bg-green-900/20 rounded-lg\">\n                <h3 className=\"font-semibold text-green-900 dark:text-green-100 mb-2\">\n                  首页翻译\n                </h3>\n                <div className=\"grid grid-cols-1 md:grid-cols-2 gap-2 text-sm\">\n                  <div>标题: {t('title')}</div>\n                  <div>描述: {t('description')}</div>\n                  <div>在 GitHub 中查看: {t('viewOnGitHub')}</div>\n                  <div>没有更多文章: {t('noMoreArticles')}</div>\n                </div>\n              </div>\n              \n              <div className=\"p-4 bg-purple-50 dark:bg-purple-900/20 rounded-lg\">\n                <h3 className=\"font-semibold text-purple-900 dark:text-purple-100 mb-2\">\n                  文章详情页翻译\n                </h3>\n                <div className=\"grid grid-cols-1 md:grid-cols-2 gap-2 text-sm\">\n                  <div>文章不存在: {t('articleNotFound')}</div>\n                  <div>加载失败: {t('loadFailed')}</div>\n                  <div>返回首页: {t('returnHome')}</div>\n                  <div>下一篇文章: {t('nextArticle')}</div>\n                </div>\n              </div>\n              \n              <div className=\"p-4 bg-orange-50 dark:bg-orange-900/20 rounded-lg\">\n                <h3 className=\"font-semibold text-orange-900 dark:text-orange-100 mb-2\">\n                  主题和语言翻译\n                </h3>\n                <div className=\"grid grid-cols-2 md:grid-cols-4 gap-2 text-sm\">\n                  <div>主题: {t('theme')}</div>\n                  <div>亮色: {t('light')}</div>\n                  <div>暗色: {t('dark')}</div>\n                  <div>跟随系统: {t('system')}</div>\n                  <div>语言: {t('language')}</div>\n                  <div>中文: {t('chinese')}</div>\n                  <div>English: {t('english')}</div>\n                </div>\n              </div>\n            </div>\n          </div>\n\n          {/* 样式测试 */}\n          <div className=\"card p-6\">\n            <h2 className=\"text-xl font-bold text-gray-900 dark:text-gray-100 mb-4\">\n              样式测试\n            </h2>\n            <div className=\"space-y-4\">\n              <div>\n                <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2\">\n                  标题样式\n                </h3>\n                <h1 className=\"text-3xl font-bold text-gray-900 dark:text-gray-100 mb-2\">\n                  H1 标题\n                </h1>\n                <h2 className=\"text-2xl font-bold text-gray-900 dark:text-gray-100 mb-2\">\n                  H2 标题\n                </h2>\n                <h3 className=\"text-xl font-bold text-gray-900 dark:text-gray-100 mb-2\">\n                  H3 标题\n                </h3>\n              </div>\n              \n              <div>\n                <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2\">\n                  文本样式\n                </h3>\n                <p className=\"text-gray-700 dark:text-gray-300 mb-2\">\n                  这是普通文本，支持暗色主题。\n                </p>\n                <p className=\"text-gray-500 dark:text-gray-400 mb-2\">\n                  这是次要文本，支持暗色主题。\n                </p>\n                <p className=\"text-primary-600 dark:text-primary-400 mb-2\">\n                  这是链接文本，支持暗色主题。\n                </p>\n              </div>\n              \n              <div>\n                <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2\">\n                  按钮样式\n                </h3>\n                <div className=\"flex flex-wrap gap-4\">\n                  <button className=\"btn-primary\">\n                    主要按钮\n                  </button>\n                  <button className=\"btn-secondary\">\n                    次要按钮\n                  </button>\n                </div>\n              </div>\n              \n              <div>\n                <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2\">\n                  输入框样式\n                </h3>\n                <input\n                  type=\"text\"\n                  placeholder=\"输入框占位符\"\n                  className=\"input w-full max-w-md\"\n                />\n              </div>\n              \n              <div>\n                <h3 className=\"text-lg font-semibold text-gray-900 dark:text-gray-100 mb-2\">\n                  代码样式\n                </h3>\n                <code className=\"bg-gray-100 dark:bg-gray-800 px-2 py-1 rounded text-sm font-mono\">\n                  console.log(&apos;Hello World&apos;);\n                </code>\n              </div>\n            </div>\n          </div>\n\n          {/* 颜色测试 */}\n          <div className=\"card p-6\">\n            <h2 className=\"text-xl font-bold text-gray-900 dark:text-gray-100 mb-4\">\n              颜色测试\n            </h2>\n            <div className=\"grid grid-cols-2 md:grid-cols-4 gap-4\">\n              <div className=\"p-4 bg-primary-50 dark:bg-primary-900/20 rounded-lg\">\n                <h4 className=\"font-semibold text-primary-900 dark:text-primary-100 mb-2\">\n                  Primary\n                </h4>\n                <p className=\"text-primary-700 dark:text-primary-300 text-sm\">\n                  主要颜色\n                </p>\n              </div>\n              <div className=\"p-4 bg-secondary-50 dark:bg-secondary-900/20 rounded-lg\">\n                <h4 className=\"font-semibold text-secondary-900 dark:text-secondary-100 mb-2\">\n                  Secondary\n                </h4>\n                <p className=\"text-secondary-700 dark:text-secondary-300 text-sm\">\n                  次要颜色\n                </p>\n              </div>\n              <div className=\"p-4 bg-muted-50 dark:bg-muted-900/20 rounded-lg\">\n                <h4 className=\"font-semibold text-muted-900 dark:text-muted-100 mb-2\">\n                  Muted\n                </h4>\n                <p className=\"text-muted-700 dark:text-muted-300 text-sm\">\n                  静音颜色\n                </p>\n              </div>\n              <div className=\"p-4 bg-accent-50 dark:bg-accent-900/20 rounded-lg\">\n                <h4 className=\"font-semibold text-accent-900 dark:text-accent-100 mb-2\">\n                  Accent\n                </h4>\n                <p className=\"text-accent-700 dark:text-accent-300 text-sm\">\n                  强调颜色\n                </p>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n} "
  },
  {
    "path": "website/next-env.d.ts",
    "content": "/// <reference types=\"next\" />\n/// <reference types=\"next/image-types/global\" />\n\n// NOTE: This file should not be edited\n// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information.\n"
  },
  {
    "path": "website/next.config.js",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  images: {\n    remotePatterns: [\n      {\n        protocol: 'https',\n        hostname: 'user-images.githubusercontent.com',\n      },\n      {\n        protocol: 'https',\n        hostname: 'github.com',\n      },\n      {\n        protocol: 'https',\n        hostname: 'raw.githubusercontent.com',\n      },\n      {\n        protocol: 'https',\n        hostname: 'img.shields.io',\n      },\n      {\n        protocol: 'https',\n        hostname: '*.juejin.byteimg.com',\n      },\n      {\n        protocol: 'https',\n        hostname: '*.cnblogs.com',\n      },\n      {\n        protocol: 'https',\n        hostname: 'camo.githubusercontent.com',\n      },\n      {\n        protocol: 'https',\n        hostname: 'cloud.githubusercontent.com',\n      },\n      {\n        protocol: 'https',\n        hostname: 'p1-juejin.byteimg.com',\n      },\n      {\n        protocol: 'https',\n        hostname: 'p9-juejin.byteimg.com',\n      },\n      {\n        protocol: 'https',\n        hostname: 'img2023.cnblogs.com',\n      },\n      {\n        protocol: 'http',\n        hostname: 'images2015.cnblogs.com',\n      },\n      {\n        protocol: 'https',\n        hostname: 'images2015.cnblogs.com',\n      },\n    ],\n  },\n}\n\nmodule.exports = nextConfig "
  },
  {
    "path": "website/package.json",
    "content": "{\n  \"name\": \"icss-website\",\n  \"version\": \"1.0.0\",\n  \"description\": \"iCSS 文章展示网站\",\n  \"private\": true,\n  \"engines\": {\n    \"node\": \">=18.0.0\"\n  },\n  \"scripts\": {\n    \"dev\": \"next dev\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\"\n  },\n  \"dependencies\": {\n    \"@tailwindcss/typography\": \"^0.5.16\",\n    \"@types/node\": \"^20.0.0\",\n    \"@types/react\": \"^18.2.0\",\n    \"@types/react-dom\": \"^18.2.0\",\n    \"@types/react-syntax-highlighter\": \"^15.5.0\",\n    \"autoprefixer\": \"^10.4.0\",\n    \"framer-motion\": \"^10.16.0\",\n    \"lucide-react\": \"^0.292.0\",\n    \"next\": \"^14.0.0\",\n    \"postcss\": \"^8.4.0\",\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-syntax-highlighter\": \"^15.5.0\",\n    \"tailwindcss\": \"^3.3.0\",\n    \"typescript\": \"^5.0.0\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"^8.0.0\",\n    \"eslint-config-next\": \"^14.0.0\"\n  }\n}"
  },
  {
    "path": "website/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n} "
  },
  {
    "path": "website/public/index.md",
    "content": "## 测试"
  },
  {
    "path": "website/tailwind.config.js",
    "content": "/** @type {import('tailwindcss').Config} */\nmodule.exports = {\n  content: [\n    './pages/**/*.{js,ts,jsx,tsx,mdx}',\n    './components/**/*.{js,ts,jsx,tsx,mdx}',\n    './app/**/*.{js,ts,jsx,tsx,mdx}',\n  ],\n  darkMode: 'class',\n  theme: {\n    extend: {\n      colors: {\n        background: 'hsl(var(--background))',\n        foreground: 'hsl(var(--foreground))',\n        card: {\n          DEFAULT: 'hsl(var(--card))',\n          foreground: 'hsl(var(--card-foreground))',\n        },\n        popover: {\n          DEFAULT: 'hsl(var(--popover))',\n          foreground: 'hsl(var(--popover-foreground))',\n        },\n        primary: {\n          DEFAULT: 'hsl(var(--primary))',\n          foreground: 'hsl(var(--primary-foreground))',\n          50: '#eff6ff',\n          100: '#dbeafe',\n          200: '#bfdbfe',\n          300: '#93c5fd',\n          400: '#60a5fa',\n          500: '#3b82f6',\n          600: '#2563eb',\n          700: '#1d4ed8',\n          800: '#1e40af',\n          900: '#1e3a8a',\n        },\n        secondary: {\n          DEFAULT: 'hsl(var(--secondary))',\n          foreground: 'hsl(var(--secondary-foreground))',\n        },\n        muted: {\n          DEFAULT: 'hsl(var(--muted))',\n          foreground: 'hsl(var(--muted-foreground))',\n        },\n        accent: {\n          DEFAULT: 'hsl(var(--accent))',\n          foreground: 'hsl(var(--accent-foreground))',\n        },\n        destructive: {\n          DEFAULT: 'hsl(var(--destructive))',\n          foreground: 'hsl(var(--destructive-foreground))',\n        },\n        border: 'hsl(var(--border))',\n        input: 'hsl(var(--input))',\n        ring: 'hsl(var(--ring))',\n      },\n      borderRadius: {\n        lg: 'var(--radius)',\n        md: 'calc(var(--radius) - 2px)',\n        sm: 'calc(var(--radius) - 4px)',\n      },\n      typography: {\n        DEFAULT: {\n          css: {\n            color: 'hsl(var(--foreground))',\n            a: {\n              color: 'hsl(var(--primary))',\n              '&:hover': {\n                color: 'hsl(var(--primary) / 0.8)',\n              },\n            },\n            h1: {\n              color: 'hsl(var(--foreground))',\n            },\n            h2: {\n              color: 'hsl(var(--foreground))',\n            },\n            h3: {\n              color: 'hsl(var(--foreground))',\n            },\n            h4: {\n              color: 'hsl(var(--foreground))',\n            },\n            strong: {\n              color: 'hsl(var(--foreground))',\n            },\n            code: {\n              color: 'hsl(var(--foreground))',\n              backgroundColor: 'hsl(var(--muted))',\n            },\n            pre: {\n              backgroundColor: 'hsl(var(--muted))',\n            },\n            blockquote: {\n              borderLeftColor: 'hsl(var(--border))',\n            },\n            hr: {\n              borderColor: 'hsl(var(--border))',\n            },\n            thead: {\n              borderBottomColor: 'hsl(var(--border))',\n            },\n            tbody: {\n              tr: {\n                borderBottomColor: 'hsl(var(--border))',\n              },\n            },\n          },\n        },\n      },\n      animation: {\n        'fade-in': 'fadeIn 0.5s ease-in-out',\n        'slide-up': 'slideUp 0.3s ease-out',\n      },\n      keyframes: {\n        fadeIn: {\n          '0%': { opacity: '0' },\n          '100%': { opacity: '1' },\n        },\n        slideUp: {\n          '0%': { transform: 'translateY(10px)', opacity: '0' },\n          '100%': { transform: 'translateY(0)', opacity: '1' },\n        },\n      },\n    },\n  },\n  plugins: [\n    require('@tailwindcss/typography'),\n  ],\n} "
  },
  {
    "path": "website/test-features.md",
    "content": "# 功能测试指南\n\n## 🎯 测试目标\n验证主题切换和多语言切换功能是否正常工作。\n\n## 🧪 测试步骤\n\n### 1. 基础功能测试\n访问以下页面，确保都能正常加载：\n- ✅ 首页: `http://localhost:3000`\n- ✅ 测试页面: `http://localhost:3000/test-theme-lang`\n\n### 2. 主题切换测试\n1. 打开浏览器开发者工具\n2. 访问 `http://localhost:3000/test-theme-lang`\n3. 点击右上角的主题切换按钮\n4. 测试三种主题模式：\n   - ☀️ 亮色主题\n   - 🌙 暗色主题\n   - 💻 跟随系统\n5. 验证：\n   - 主题切换是否生效\n   - 页面样式是否正确更新\n   - 刷新页面后主题是否保持\n\n### 3. 语言切换测试\n1. 在测试页面点击右上角的语言切换按钮\n2. 测试两种语言：\n   - 🇨🇳 中文\n   - 🇺🇸 English\n3. 验证：\n   - 语言切换是否生效\n   - 所有文本是否正确翻译\n   - 刷新页面后语言是否保持\n\n### 4. 持久化测试\n1. 设置一个主题和语言\n2. 刷新页面\n3. 验证设置是否保持\n4. 关闭浏览器重新打开\n5. 验证设置是否仍然保持\n\n### 5. 响应式测试\n1. 调整浏览器窗口大小\n2. 验证主题和语言切换按钮在不同屏幕尺寸下的显示\n3. 在移动设备上测试触摸交互\n\n### 6. 无障碍测试\n1. 使用键盘导航（Tab 键）\n2. 验证是否可以通过键盘切换主题和语言\n3. 检查屏幕阅读器兼容性\n\n## 🎨 主题效果验证\n\n### 亮色主题\n- 背景色：白色\n- 文字色：深灰色\n- 卡片背景：白色\n- 边框：浅灰色\n\n### 暗色主题\n- 背景色：深灰色\n- 文字色：浅灰色\n- 卡片背景：深灰色\n- 边框：深灰色\n\n### 跟随系统\n- 根据系统主题自动切换\n- 系统主题变化时自动响应\n\n## 🌍 语言效果验证\n\n### 中文界面\n- 所有按钮和文本显示中文\n- 日期格式：中文格式\n- 数字格式：中文格式\n\n### 英文界面\n- 所有按钮和文本显示英文\n- 日期格式：英文格式\n- 数字格式：英文格式\n\n## 🔧 技术验证\n\n### 控制台检查\n1. 打开浏览器开发者工具\n2. 查看 Console 标签页\n3. 确保没有 JavaScript 错误\n4. 确保没有 React 错误\n\n### 网络检查\n1. 查看 Network 标签页\n2. 确保所有资源加载成功\n3. 检查 API 请求是否正常\n\n### 性能检查\n1. 使用 Lighthouse 进行性能测试\n2. 确保主题和语言切换响应迅速\n3. 检查页面加载时间\n\n## 🐛 常见问题排查\n\n### 问题：主题切换不生效\n**解决方案：**\n1. 检查浏览器是否支持 CSS 变量\n2. 检查 localStorage 是否可用\n3. 检查控制台是否有错误\n\n### 问题：语言切换不生效\n**解决方案：**\n1. 检查翻译文件是否正确加载\n2. 检查 useApp hook 是否正常工作\n3. 检查组件是否正确使用 t() 函数\n\n### 问题：设置不持久化\n**解决方案：**\n1. 检查 localStorage 是否被禁用\n2. 检查浏览器隐私设置\n3. 检查是否有其他脚本清除 localStorage\n\n## ✅ 测试完成标准\n\n当以下所有项目都通过时，功能测试完成：\n\n- [ ] 首页正常加载\n- [ ] 测试页面正常加载\n- [ ] 主题切换功能正常\n- [ ] 语言切换功能正常\n- [ ] 设置持久化正常\n- [ ] 响应式设计正常\n- [ ] 无障碍功能正常\n- [ ] 无 JavaScript 错误\n- [ ] 性能表现良好\n\n## 🎉 测试完成\n\n如果所有测试都通过，恭喜！主题和语言切换功能已经成功实现并正常工作。 "
  },
  {
    "path": "website/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"es5\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"es6\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"baseUrl\": \".\",\n    \"paths\": {\n      \"@/*\": [\"./*\"]\n    }\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n} "
  },
  {
    "path": "website/vercel.json",
    "content": "{\n    \"buildCommand\": \"pnpm build\",\n    \"devCommand\": \"pnpm dev\",\n    \"installCommand\": \"pnpm install\",\n    \"regions\": [\n        \"hkg1\"\n    ],\n    \"framework\": \"nextjs\",\n    \"outputDirectory\": \".next\"\n}"
  }
]