├── .roo └── mcp.json ├── .cursorignore ├── extension ├── icons │ ├── icon128.png │ ├── icon16.png │ ├── icon48.png │ ├── icon16.js │ ├── icon48.js │ ├── icon128.js │ └── generate_icons.html ├── fontawesome │ └── webfonts │ │ ├── fa-brands-400.ttf │ │ ├── fa-solid-900.ttf │ │ ├── fa-brands-400.woff2 │ │ ├── fa-regular-400.ttf │ │ ├── fa-solid-900.woff2 │ │ ├── fa-regular-400.woff2 │ │ ├── fa-v4compatibility.ttf │ │ └── fa-v4compatibility.woff2 ├── manifest.json ├── panel-inline.js ├── panel-inline.css ├── bridge.js ├── panel.html ├── background.js ├── panel.css ├── content.js └── panel.js ├── backend ├── src │ ├── types │ │ └── qrcode-terminal.d.ts │ ├── index.ts │ ├── whatsapp.service.ts │ └── ai.service.ts ├── tsconfig.json ├── package.json └── install.js ├── .gitignore └── README.md /.roo/mcp.json: -------------------------------------------------------------------------------- 1 | { 2 | "mcpServers": {} 3 | } -------------------------------------------------------------------------------- /.cursorignore: -------------------------------------------------------------------------------- 1 | # Add directories or file patterns to ignore during indexing (e.g. foo/ or *.csv) 2 | -------------------------------------------------------------------------------- /extension/icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romiluz13/whatsapp_ai/HEAD/extension/icons/icon128.png -------------------------------------------------------------------------------- /extension/icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romiluz13/whatsapp_ai/HEAD/extension/icons/icon16.png -------------------------------------------------------------------------------- /extension/icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romiluz13/whatsapp_ai/HEAD/extension/icons/icon48.png -------------------------------------------------------------------------------- /extension/fontawesome/webfonts/fa-brands-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romiluz13/whatsapp_ai/HEAD/extension/fontawesome/webfonts/fa-brands-400.ttf -------------------------------------------------------------------------------- /extension/fontawesome/webfonts/fa-solid-900.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romiluz13/whatsapp_ai/HEAD/extension/fontawesome/webfonts/fa-solid-900.ttf -------------------------------------------------------------------------------- /extension/fontawesome/webfonts/fa-brands-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romiluz13/whatsapp_ai/HEAD/extension/fontawesome/webfonts/fa-brands-400.woff2 -------------------------------------------------------------------------------- /extension/fontawesome/webfonts/fa-regular-400.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romiluz13/whatsapp_ai/HEAD/extension/fontawesome/webfonts/fa-regular-400.ttf -------------------------------------------------------------------------------- /extension/fontawesome/webfonts/fa-solid-900.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romiluz13/whatsapp_ai/HEAD/extension/fontawesome/webfonts/fa-solid-900.woff2 -------------------------------------------------------------------------------- /extension/fontawesome/webfonts/fa-regular-400.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romiluz13/whatsapp_ai/HEAD/extension/fontawesome/webfonts/fa-regular-400.woff2 -------------------------------------------------------------------------------- /extension/fontawesome/webfonts/fa-v4compatibility.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romiluz13/whatsapp_ai/HEAD/extension/fontawesome/webfonts/fa-v4compatibility.ttf -------------------------------------------------------------------------------- /extension/fontawesome/webfonts/fa-v4compatibility.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/romiluz13/whatsapp_ai/HEAD/extension/fontawesome/webfonts/fa-v4compatibility.woff2 -------------------------------------------------------------------------------- /backend/src/types/qrcode-terminal.d.ts: -------------------------------------------------------------------------------- 1 | declare module 'qrcode-terminal' { 2 | const qrcodeTerminal: { 3 | generate: (text: string, options?: { small?: boolean }) => void 4 | }; 5 | export default qrcodeTerminal; 6 | } -------------------------------------------------------------------------------- /backend/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "ES2020", 4 | "module": "commonjs", 5 | "rootDir": "./src", 6 | "outDir": "./dist", 7 | "esModuleInterop": true, 8 | "forceConsistentCasingInFileNames": true, 9 | "strict": true, 10 | "skipLibCheck": true, 11 | "resolveJsonModule": true, 12 | "typeRoots": ["./node_modules/@types", "./src/types"] 13 | }, 14 | "include": ["src/**/*"], 15 | "exclude": ["node_modules", "**/*.spec.ts"] 16 | } -------------------------------------------------------------------------------- /extension/icons/icon16.js: -------------------------------------------------------------------------------- 1 | // This is a helper file to create a base64-encoded icon for the extension 2 | // You can replace these files with actual icons later 3 | 4 | // 16x16 icon - Base64 encoded PNG with WhatsApp green background and white robot icon 5 | const icon16Base64 = "iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAABFUlEQVQ4jcWTvUpDQRCFv7nZdRNCELQRgoWFhU3AzlJ8Ah9AfARrsRCfQbDTF7CwESxs0qSzkA2IYKHgD8k9FtkbN5sbaGPjwMAwO+d8Z2ZnYc7KigJu9G4Ft24Lna6h08YE4tnjL4HbnCju7wRb36N96wEEoZ8oKnf3Hlu+l/V3BUc3DzUBUr+f+Jkxwq0ucH+E7QWTYmLkGRiZSXGmDN6b6HRLNYvfYIU7GWy9ilDRYCNqrxkIm7AeQWUF9qsgBQ6OAb/KYO8CXj9Sqg9S6p0wvNyGYj6V6QzkBFvLcHmbMJrMgH9i8PIerO+AcwmsmCEYw9MrbE1LGZUB+PgC0UlOFbOrsHsGzpWn8L9fJ8Dc9QWYqLYdnrWyYgAAAABJRU5ErkJggg=="; 6 | 7 | // You can use this data URI in your extension 8 | console.log('Icon 16x16:', 'data:image/png;base64,' + icon16Base64); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Node.js 2 | node_modules/ 3 | npm-debug.log* 4 | yarn-debug.log* 5 | yarn-error.log* 6 | package-lock.json # Usually committed, but can be ignored if preferred by project 7 | lerna-debug.log* 8 | 9 | # Environment variables 10 | .env 11 | .env.local 12 | .env.development.local 13 | .env.test.local 14 | .env.production.local 15 | .history 16 | 17 | # IDEs and editors 18 | .idea/ 19 | .vscode/* 20 | !.vscode/settings.json 21 | !.vscode/tasks.json 22 | !.vscode/launch.json 23 | !.vscode/extensions.json 24 | *.sublime-workspace 25 | 26 | # macOS 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Windows 32 | Thumbs.db 33 | ehthumbs.db 34 | Desktop.ini 35 | 36 | # Project Specific 37 | memory-bank/ 38 | backend/.env 39 | backend/.wwebjs_auth/ 40 | 41 | # Build output (if any in the future) 42 | dist/ 43 | build/ 44 | out/ 45 | 46 | # Optional: Chrome extension packaging files (if generated locally) 47 | *.crx 48 | *.pem 49 | -------------------------------------------------------------------------------- /backend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "whatsapp-ai-copilot-backend", 3 | "version": "1.0.0", 4 | "description": "Backend for WhatsApp AI Co-Pilot Chrome Extension", 5 | "main": "dist/index.js", 6 | "scripts": { 7 | "start": "ts-node src/index.ts", 8 | "build": "tsc", 9 | "dev": "nodemon --exec ts-node src/index.ts", 10 | "test": "echo \"Error: no test specified\" && exit 1", 11 | "setup": "node install.js" 12 | }, 13 | "keywords": [ 14 | "whatsapp", 15 | "ai", 16 | "copilot", 17 | "backend" 18 | ], 19 | "author": "", 20 | "license": "ISC", 21 | "dependencies": { 22 | "cors": "^2.8.5", 23 | "dotenv": "^16.3.1", 24 | "express": "^4.18.2", 25 | "openai": "^4.12.1", 26 | "qrcode-terminal": "^0.12.0", 27 | "whatsapp-web.js": "^1.23.0" 28 | }, 29 | "devDependencies": { 30 | "@types/cors": "^2.8.17", 31 | "@types/express": "^4.17.17", 32 | "@types/node": "^20.6.2", 33 | "nodemon": "^3.0.1", 34 | "ts-node": "^10.9.1", 35 | "typescript": "^5.2.2" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /extension/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "manifest_version": 3, 3 | "name": "WhatsApp AI Co-Pilot", 4 | "version": "0.1.0", 5 | "description": "AI assistant for WhatsApp Web, built with your awesome plan!", 6 | "permissions": [ 7 | "storage", 8 | "activeTab", 9 | "alarms", 10 | "scripting" 11 | ], 12 | "host_permissions": [ 13 | "https://web.whatsapp.com/*", 14 | "http://localhost:3000/*" 15 | ], 16 | "content_security_policy": { 17 | "extension_pages": "script-src 'self'; object-src 'self'; style-src 'self' 'unsafe-inline'; connect-src 'self' http://localhost:* http://127.0.0.1:*;" 18 | }, 19 | "background": { 20 | "service_worker": "background.js" 21 | }, 22 | "content_scripts": [ 23 | { 24 | "matches": ["https://web.whatsapp.com/*"], 25 | "js": ["content.js"], 26 | "css": ["panel.css"] 27 | } 28 | ], 29 | "web_accessible_resources": [ 30 | { 31 | "resources": [ 32 | "panel.html", "panel.css", "panel.js", 33 | "fontawesome/css/all.min.css", 34 | "fontawesome/webfonts/*" 35 | ], 36 | "matches": ["https://web.whatsapp.com/*"] 37 | } 38 | ], 39 | "action": { 40 | "default_title": "WhatsApp AI Co-Pilot" 41 | }, 42 | "icons": { 43 | "16": "icons/icon16.png", 44 | "48": "icons/icon48.png", 45 | "128": "icons/icon128.png" 46 | } 47 | } -------------------------------------------------------------------------------- /extension/icons/icon48.js: -------------------------------------------------------------------------------- 1 | // This is a helper file to create a base64-encoded icon for the extension 2 | // You can replace these files with actual icons later 3 | 4 | // 48x48 icon - Base64 encoded PNG with WhatsApp green background and white robot icon 5 | const icon48Base64 = "iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAACXBIWXMAAAsTAAALEwEAmpwYAAAEuklEQVR4nO2Z3W8UVRTGf3dm2i1LXUoLtFS+GwgVCkJCAlFAPhJEo8YYEzXxgyiJiTHGxPgFxhg/okajJsYQEvUP0BgVBYxUQA20ha8WSrfQj9nd7cfM9cVzb8+d2ZnpbXenlvgkJzN37j3nPPfcc8499w6swAIscRhzqVPsOIgdBTJqDAAFoMzKgvp9ZpxuFN1WUK8XJxbp4tbrdKoJCkDEgCQQxY0DFJOxGCuLEchidBwMnGrn0FEDBtQNKWRnFlkl4vY6iiKwDdioaBuQQhrXi1MF+lGIIdtF3JByQcjKwzVk9WPqO9OI4yb6Ox9gK3Bdfe5D8YFLaA9QMgQpVZCBooZ2I7adQHrqmkfxJXdRvMD0IaFUVL9LwB3N9r1onoGvgD3AYaRdHJHddSFkObm5W7MNm5h2CQz8CnwD/AL8BfTWaqAWAmYULtAOQkZCymJ9G4GoyF0yGpH2oiJwT/1fZPbC/TwCCVwV8tOI5iMwiqzGjDqnLbcYsQIMi9kHyniRrAWBEuIsI+qnHeZPwQzSrh/JYBBNnXDw2KnTgcnCUmhGYvkWxeeIQAV4GUkZS4pqUaigJlNIjJbwRyOhZVIz9sUIo5GcwhVFZVzuK0UCQCF2iXExSCuBNO4uDBKO27jGJWQ5dYRVz4bXuFJBAqKI8gVmE7FxucbCyWWugTDUcqCfkEzaI6PmEDuPbOTzCHg9lkHxEjPiINKhDMI9Xm3jcJUNXtLz1YA3hCJI200FtU+xMXTRBJrxwBSiY6sO1ELAVXlIdl2YZgJFZEda0LwnrXbMdoqQauTniUBBB4PZF9YLtYhq1UeXiKYAo2L0vRVtZZ6wCYxrdi+wB9lMa8FYRZL4BvA40Ilknr2+smMIy08h4dCK5gJx5Ll1vS4cU7QWZMXeAU4hGS5sTCBV+rvAVeAykhmDYSFk2ZsRR24htMUQtQ/qNzDZBMAzwMeaXVhjbSPEnsWf/RZEARHqrSFUDVPSa4rWjtQKHcAnDXQyCnRr9muI7YNg2zjMJjCJCNSGqLFQHOjS7LPIjhxGPWl0TXhTaTfCEdmJJmQnwqAmgV2a3YnUDj7UG0bVCNjA35odRXbCQlYxLJpJInWBjn5kxX3IoFbKXAvmkONaDJjfH0EIFBHP25p9GdnmQSRRDhHsMbPVxjRY6Syk+cERQKDWMfENpAx5TdGagBNAV8DYKeBnzT6GbOc4LqGKRsZEQSPxWyPxW+tK2cgbIwUtvq0hGvAm8CEyCz1IHO+ttsDKwEOIp/cg4VPGWyTZfnIVJHmFeSw9huwRDf8A7yBPUxHkRWWIYA9MIfH6EfC+Gm8hp0ELf7EV5H1HfwbeBL5HahIHKZTPAo8ghV4ESVvbgE7kyTWNhFY38BvyCvkxcuQyC7lqpCzL9u621cSZl5D8fwGYwDgIQZ6eo8jDZgLx2INI/NtIcdaMHCsLyAnwOnKGNxchUNQcTKuTnw3cRapivzrm4mEfcvn+I7Jze5Gd6WH+b+W6kRe9S8BHBGRcHTZSJM0gKjKlxuJ5ahpFPUciAWPMa/bHShisFLCCJYL/AWG+GZZj6TFuAAAAAElFTkSuQmCC"; 6 | 7 | // You can use this data URI in your extension 8 | console.log('Icon 48x48:', 'data:image/png;base64,' + icon48Base64); -------------------------------------------------------------------------------- /extension/panel-inline.js: -------------------------------------------------------------------------------- 1 | // Icon fallback script 2 | document.addEventListener('DOMContentLoaded', function() { 3 | // Check if Font Awesome is loaded properly 4 | const isFontAwesomeLoaded = Array.from(document.styleSheets) 5 | .some(sheet => sheet.href && sheet.href.includes('font-awesome')); 6 | 7 | if (!isFontAwesomeLoaded) { 8 | console.log('Font Awesome not loaded, using fallback icons'); 9 | // Add text fallbacks for each icon 10 | document.querySelectorAll('.card-icon').forEach(icon => { 11 | const iconType = icon.querySelector('i')?.className || ''; 12 | let fallbackText = ''; 13 | 14 | if (iconType.includes('reply')) fallbackText = 'SR'; 15 | else if (iconType.includes('chart')) fallbackText = 'AC'; 16 | else if (iconType.includes('bell')) fallbackText = 'RM'; 17 | else if (iconType.includes('clipboard')) fallbackText = 'DD'; 18 | else fallbackText = 'AI'; 19 | 20 | icon.innerHTML = `${fallbackText}`; 21 | }); 22 | 23 | // Replace other icons with text 24 | document.querySelectorAll('.logo-icon i').forEach(icon => { 25 | icon.parentNode.innerHTML = 'AI'; 26 | }); 27 | 28 | document.querySelectorAll('.section-title i').forEach(icon => { 29 | icon.outerHTML = '📝'; 30 | }); 31 | 32 | document.querySelectorAll('.send-button i').forEach(icon => { 33 | icon.outerHTML = '→'; 34 | }); 35 | 36 | document.querySelectorAll('.fold-button i').forEach(icon => { 37 | icon.outerHTML = '←'; 38 | }); 39 | 40 | document.querySelectorAll('.toggle-icon i').forEach(icon => { 41 | icon.outerHTML = '→'; 42 | }); 43 | 44 | document.querySelectorAll('.action-button i').forEach((icon, index) => { 45 | const symbols = ['💡', '⚙️', 'ℹ️']; 46 | icon.outerHTML = symbols[index] || '•'; 47 | }); 48 | } 49 | 50 | // Add click handlers 51 | document.querySelectorAll('.feature-card').forEach(card => { 52 | card.addEventListener('click', function() { 53 | // Add visual active feedback 54 | document.querySelectorAll('.feature-card').forEach(c => 55 | c.classList.remove('active')); 56 | this.classList.add('active'); 57 | }); 58 | }); 59 | }); -------------------------------------------------------------------------------- /backend/install.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const readline = require('readline'); 3 | const path = require('path'); 4 | 5 | const rl = readline.createInterface({ 6 | input: process.stdin, 7 | output: process.stdout 8 | }); 9 | 10 | console.log("\n===== WhatsApp AI Co-Pilot Setup =====\n"); 11 | console.log("This script will help you configure your OpenAI API key.\n"); 12 | 13 | // Check for existing .env file 14 | const envPath = path.join(__dirname, '.env'); 15 | let existingKey = ''; 16 | 17 | if (fs.existsSync(envPath)) { 18 | try { 19 | const envContent = fs.readFileSync(envPath, 'utf8'); 20 | const match = envContent.match(/OPENAI_API_KEY=([^\s]*)/); 21 | 22 | if (match && match[1] && match[1] !== 'your_openai_api_key_here') { 23 | existingKey = match[1]; 24 | console.log(`An existing OpenAI API key was found (${existingKey.slice(0, 3)}...${existingKey.slice(-4)}).`); 25 | } 26 | } catch (err) { 27 | console.error("Error reading .env file:", err.message); 28 | } 29 | } 30 | 31 | const askApiKey = () => { 32 | const defaultPrompt = existingKey ? 33 | `Enter your OpenAI API key (press Enter to keep existing key): ` : 34 | `Enter your OpenAI API key: `; 35 | 36 | rl.question(defaultPrompt, (apiKey) => { 37 | const keyToUse = apiKey.trim() || existingKey; 38 | 39 | if (!keyToUse) { 40 | console.log("Error: No API key provided. The key is required for AI functionality."); 41 | return askApiKey(); 42 | } 43 | 44 | // Ask for model preference 45 | rl.question(`Select AI model (default is gpt-4o-mini):\n1. gpt-4o-mini (faster, cheaper)\n2. gpt-4o (more powerful)\nEnter choice [1/2]: `, (modelChoice) => { 46 | let model = 'gpt-4o-mini'; // Default 47 | 48 | if (modelChoice.trim() === '2') { 49 | model = 'gpt-4o'; 50 | } 51 | 52 | // Create or update .env file 53 | const envContent = `# WhatsApp AI Co-Pilot Environment Variables 54 | 55 | # OpenAI API Key 56 | OPENAI_API_KEY=${keyToUse} 57 | 58 | # Server port 59 | PORT=3000 60 | 61 | # OpenAI Model to use 62 | OPENAI_MODEL=${model} 63 | `; 64 | 65 | try { 66 | fs.writeFileSync(envPath, envContent); 67 | console.log("\n✅ Configuration saved successfully!"); 68 | console.log("\nYou can now start the backend with:"); 69 | console.log(" npm start"); 70 | console.log("\nAnd install the Chrome extension from the extension/ folder."); 71 | 72 | } catch (err) { 73 | console.error("Error writing .env file:", err.message); 74 | } 75 | 76 | rl.close(); 77 | }); 78 | }); 79 | }; 80 | 81 | askApiKey(); -------------------------------------------------------------------------------- /extension/panel-inline.css: -------------------------------------------------------------------------------- 1 | /* Base styling for the panel */ 2 | :root { 3 | --primary-color: #128C7E; 4 | --secondary-color: #34B7F1; 5 | --text-color: #333; 6 | --background-color: #f8f9fa; 7 | --sidebar-width: 320px; 8 | --sidebar-collapsed-width: 10px; 9 | --header-height: 50px; 10 | --animation-duration: 0.3s; 11 | } 12 | 13 | /* Reset styles */ 14 | * { 15 | margin: 0; 16 | padding: 0; 17 | box-sizing: border-box; 18 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif; 19 | } 20 | 21 | /* Panel container */ 22 | #ai-copilot-panel { 23 | width: 100%; 24 | height: 100vh; 25 | display: flex; 26 | flex-direction: column; 27 | background-color: var(--background-color); 28 | color: var(--text-color); 29 | overflow: hidden; 30 | } 31 | 32 | /* Panel header */ 33 | .panel-header { 34 | display: flex; 35 | justify-content: space-between; 36 | align-items: center; 37 | padding: 0 15px; 38 | height: var(--header-height); 39 | background-color: var(--primary-color); 40 | color: white; 41 | box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 42 | } 43 | 44 | .panel-title { 45 | display: flex; 46 | align-items: center; 47 | gap: 10px; 48 | font-weight: bold; 49 | font-size: 18px; 50 | } 51 | 52 | .fold-button { 53 | background: none; 54 | border: none; 55 | color: white; 56 | cursor: pointer; 57 | width: 30px; 58 | height: 30px; 59 | display: flex; 60 | justify-content: center; 61 | align-items: center; 62 | border-radius: 50%; 63 | transition: background-color 0.2s; 64 | } 65 | 66 | .fold-button:hover { 67 | background-color: rgba(255, 255, 255, 0.1); 68 | } 69 | 70 | /* Panel content */ 71 | .panel-content { 72 | flex: 1; 73 | overflow-y: auto; 74 | padding: 15px; 75 | } 76 | 77 | /* Feature grid */ 78 | .feature-grid { 79 | display: grid; 80 | grid-template-columns: repeat(2, 1fr); 81 | gap: 10px; 82 | margin-bottom: 20px; 83 | } 84 | 85 | /* Message styles */ 86 | .message { 87 | margin-bottom: 10px; 88 | padding: 8px 12px; 89 | border-radius: 10px; 90 | max-width: 85%; 91 | animation: fadeIn 0.3s; 92 | } 93 | 94 | .message.user { 95 | background-color: #dcf8c6; 96 | align-self: flex-end; 97 | margin-left: auto; 98 | } 99 | 100 | .message.assistant { 101 | background-color: #f3f3f3; 102 | align-self: flex-start; 103 | margin-right: auto; 104 | } 105 | 106 | .message.system { 107 | background-color: #e2f1fb; 108 | align-self: center; 109 | margin: 10px auto; 110 | font-style: italic; 111 | text-align: center; 112 | } 113 | 114 | .message-content { 115 | word-break: break-word; 116 | } 117 | 118 | /* Toggle button for collapsed sidebar */ 119 | #sidebar-toggle { 120 | position: fixed; 121 | left: 0; 122 | top: 50%; 123 | transform: translateY(-50%); 124 | background-color: var(--primary-color); 125 | color: white; 126 | padding: 10px 5px; 127 | border-radius: 0 5px 5px 0; 128 | cursor: pointer; 129 | box-shadow: 2px 0 5px rgba(0, 0, 0, 0.1); 130 | transition: opacity 0.3s, transform 0.3s; 131 | } 132 | 133 | #sidebar-toggle.hidden { 134 | display: none; 135 | } 136 | 137 | /* Animation classes */ 138 | @keyframes fadeIn { 139 | from { opacity: 0; transform: translateY(10px); } 140 | to { opacity: 1; transform: translateY(0); } 141 | } 142 | 143 | /* Loading indicators */ 144 | .loading-dots { 145 | display: inline-flex; 146 | gap: 3px; 147 | } 148 | 149 | .loading-dots span { 150 | width: 8px; 151 | height: 8px; 152 | background-color: var(--text-color); 153 | border-radius: 50%; 154 | animation: dotPulse 1.5s infinite ease-in-out; 155 | } 156 | 157 | .loading-dots span:nth-child(2) { 158 | animation-delay: 0.2s; 159 | } 160 | 161 | .loading-dots span:nth-child(3) { 162 | animation-delay: 0.4s; 163 | } 164 | 165 | @keyframes dotPulse { 166 | 0%, 100% { transform: scale(0.6); opacity: 0.6; } 167 | 50% { transform: scale(1); opacity: 1; } 168 | } -------------------------------------------------------------------------------- /extension/icons/icon128.js: -------------------------------------------------------------------------------- 1 | // This is a helper file to create a base64-encoded icon for the extension 2 | // You can replace these files with actual icons later 3 | 4 | // 128x128 icon - Base64 encoded PNG with WhatsApp green background and white robot icon 5 | const icon128Base64 = "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"; 6 | 7 | // You can use this data URI in your extension 8 | console.log('Icon 128x128:', 'data:image/png;base64,' + icon128Base64); -------------------------------------------------------------------------------- /extension/bridge.js: -------------------------------------------------------------------------------- 1 | // Bridge for messaging between the panel script and content script 2 | window.postMessageToExtension = function(message) { 3 | try { 4 | window.postMessage({ 5 | source: 'panel-script', 6 | message: message 7 | }, '*'); 8 | } catch (error) { 9 | console.error('Error in postMessageToExtension:', error); 10 | } 11 | }; 12 | 13 | // Override chrome.runtime.sendMessage in the page context 14 | window.chromeRuntimeSendMessage = function(message, callback) { 15 | try { 16 | // Generate a random ID for this message to track the response 17 | const messageId = 'msg_' + Math.random().toString(36).substr(2, 9); 18 | 19 | // Store the callback 20 | window.pendingCallbacks = window.pendingCallbacks || {}; 21 | window.pendingCallbacks[messageId] = callback; 22 | 23 | // Post the message to the content script with the ID 24 | window.postMessage({ 25 | source: 'panel-script', 26 | message: { 27 | ...message, 28 | _messageId: messageId 29 | } 30 | }, '*'); 31 | 32 | // Listen for the response 33 | const responseHandler = function(event) { 34 | try { 35 | if (event.source !== window) return; 36 | if (event.data.source === 'content-script' && 37 | event.data.destination === 'panel' && 38 | event.data.messageId === messageId) { 39 | // Execute the callback with the response 40 | if (typeof window.pendingCallbacks[messageId] === 'function') { 41 | try { 42 | window.pendingCallbacks[messageId](event.data.response); 43 | } catch (callbackError) { 44 | console.error('Error executing callback:', callbackError); 45 | } 46 | delete window.pendingCallbacks[messageId]; 47 | } 48 | // Remove this specific response handler 49 | window.removeEventListener('message', responseHandler); 50 | } 51 | } catch (handlerError) { 52 | console.error('Error in message response handler:', handlerError); 53 | window.removeEventListener('message', responseHandler); 54 | } 55 | }; 56 | 57 | window.addEventListener('message', responseHandler); 58 | 59 | // Set a timeout to clean up if no response is received 60 | setTimeout(() => { 61 | if (window.pendingCallbacks[messageId]) { 62 | delete window.pendingCallbacks[messageId]; 63 | window.removeEventListener('message', responseHandler); 64 | if (typeof callback === 'function') { 65 | try { 66 | callback({ error: 'Timeout waiting for response' }); 67 | } catch (timeoutError) { 68 | console.error('Error executing timeout callback:', timeoutError); 69 | } 70 | } 71 | } 72 | }, 10000); // 10 second timeout 73 | } catch (error) { 74 | console.error('Error in chromeRuntimeSendMessage:', error); 75 | if (typeof callback === 'function') { 76 | try { 77 | callback({ error: 'Error sending message: ' + error.message }); 78 | } catch (callbackError) { 79 | console.error('Error executing error callback:', callbackError); 80 | } 81 | } 82 | } 83 | }; 84 | 85 | // Safely override event handlers to prevent interference with WhatsApp 86 | document.addEventListener('DOMContentLoaded', function() { 87 | // Create a global error handler for our extension to avoid affecting WhatsApp 88 | window.addEventListener('error', function(event) { 89 | // Only handle errors coming from our extension files 90 | if (event.filename && ( 91 | event.filename.includes('panel.js') || 92 | event.filename.includes('bridge.js') || 93 | event.filename.includes('content.js') 94 | )) { 95 | console.error('WhatsApp AI Extension error:', event.error); 96 | event.preventDefault(); 97 | event.stopPropagation(); 98 | return true; // Prevent default error handler 99 | } 100 | }, true); 101 | }); -------------------------------------------------------------------------------- /extension/icons/generate_icons.html: -------------------------------------------------------------------------------- 1 | 2 | 3 |
4 | 5 | 6 |Click on each canvas to download the icon image
44 | 45 |מאמת חיבור...
61 | 65 |