diff --git a/README.md b/README.md index 2c56d20..5b77832 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,22 @@ -# AI SEO Copilot - Chrome Extension +# Optia - Chrome Extension A Chrome extension that analyzes web pages for SEO optimization and provides AI-powered recommendations to improve search rankings. **Website:** https://ai-seo-copilot.webflow.io/ +> ⚠️ **Rebrand in progress (AI SEO Copilot → Optia).** The codebase, UI, docs, and package names have been renamed to **Optia**. The items below live on **external systems** and must be renamed/migrated **manually by the project owner** — they were intentionally left unchanged so links don't break before migration: +> +> - **GitHub repository & remote URL** — currently `die-Manufaktur/AISEOC-Chrome-Extension`. Rename the repo, update the git remote, then update the links in `docs/` and `app/src/components/Footer.tsx`. +> - **Marketing website** — `https://ai-seo-copilot.webflow.io/` (Webflow project). +> - **Chrome Web Store listing** — extension name, description, screenshots, and the public store URL. +> - **Documentation site** — `ai-seo-copilot.gitbook.io` (referenced in `app/src/lib/docs-links.ts` and `app/src/components/Footer.tsx`). +> - **Feature-request portal** — `aiseocopilot.featurebase.app` (referenced in `app/src/components/Footer.tsx`). +> - **Bug-report repo link** — `github.com/PMDevSolutions/seo-copilot` (referenced in `app/src/components/Footer.tsx`). +> - **Figma design file** — `…/AI-SEO-Copilot-design` (referenced in `app/QA-PROMPT.md`). +> - **Donation / sponsor links** — none found in the repo; update if any exist outside it. +> +> After migrating each external property, update the corresponding URL in `Footer.tsx`, `docs-links.ts`, and the docs to the new Optia domain. + ## Features - **Real-time SEO Analysis** - Instant scoring for titles, meta descriptions, headings, images, and more @@ -29,7 +42,7 @@ pnpm build ## Project Structure ``` -ai-seo-copilot-extension/ +optia-extension/ ├── app/ # Chrome extension source code │ ├── src/ # React + TypeScript source │ ├── dist/ # Built extension (load this in Chrome) diff --git a/app/CHANGELOG.md b/app/CHANGELOG.md index 115cffc..8455569 100644 --- a/app/CHANGELOG.md +++ b/app/CHANGELOG.md @@ -1,6 +1,6 @@ # Changelog -All notable changes to AI SEO Copilot will be documented in this file. +All notable changes to Optia will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). diff --git a/app/PRIVACY_POLICY.md b/app/PRIVACY_POLICY.md index d98222b..5590474 100644 --- a/app/PRIVACY_POLICY.md +++ b/app/PRIVACY_POLICY.md @@ -1,8 +1,8 @@ -# Privacy Policy — AI SEO Copilot +# Privacy Policy — Optia **Last updated:** April 1, 2026 -AI SEO Copilot is a Chrome browser extension that analyzes web pages for SEO issues and provides recommendations. This privacy policy explains what data the extension accesses, how it is used, and how it is stored. +Optia is a Chrome browser extension that analyzes web pages for SEO issues and provides recommendations. This privacy policy explains what data the extension accesses, how it is used, and how it is stored. ## Data the Extension Accesses @@ -53,7 +53,7 @@ No data is stored on external servers. No cookies are set. No analytics or track ## Data Sharing -AI SEO Copilot does **not**: +Optia does **not**: - Collect or transmit personal information - Use analytics, telemetry, or tracking of any kind diff --git a/app/QA-PROMPT.md b/app/QA-PROMPT.md index ec2a8d6..4953c2f 100644 --- a/app/QA-PROMPT.md +++ b/app/QA-PROMPT.md @@ -1,4 +1,4 @@ -# AI SEO Copilot — Visual QA, Functional Testing & Code Review +# Optia — Visual QA, Functional Testing & Code Review ## Your Role @@ -73,7 +73,7 @@ The app uses a strict dark-mode design system. Match these tokens against both F #### Screen 1: Setup Page (initial state) - Navigate to the dev app URL. This is the default view. - **Expected elements:** - - Blue circle icon with Settings gear + "AI SEO Copilot" heading + - Blue circle icon with Settings gear + "Optia" heading - "Dev mode" subtitle - "Page URL to Analyze" input (dev mode only) - "OpenAI API Key (optional)" password input @@ -329,7 +329,7 @@ Read the following files and review for correctness, consistency, and bugs: Produce your report in this structure: ```markdown -# AI SEO Copilot — QA Report +# Optia — QA Report ## 1. Visual Comparison diff --git a/app/README.md b/app/README.md index eefc9b2..8409174 100644 --- a/app/README.md +++ b/app/README.md @@ -1,4 +1,4 @@ -# AI SEO Copilot - Chrome Extension +# Optia - Chrome Extension A Chrome extension that analyzes web pages for SEO issues and provides AI-powered recommendations to improve search rankings. @@ -46,8 +46,8 @@ You should see something like `9.x.x` Clone or download this repository from GitHub: ```bash -git clone https://github.com/YOUR-USERNAME/ai-seo-copilot-extension.git -cd ai-seo-copilot-extension +git clone https://github.com/YOUR-USERNAME/optia-extension.git +cd optia-extension ``` Or download the ZIP file from GitHub and extract it. @@ -85,7 +85,7 @@ This creates a `dist` folder with the built extension. 4. Navigate to the `app/dist` folder inside this project and select it -5. The AI SEO Copilot extension icon should appear in your browser toolbar +5. The Optia extension icon should appear in your browser toolbar --- @@ -93,7 +93,7 @@ This creates a `dist` folder with the built extension. 1. Navigate to any website you want to analyze -2. Click the AI SEO Copilot icon in your toolbar (the little robot) +2. Click the Optia icon in your toolbar 3. Enter your target keyword and click **Analyze** diff --git a/app/dev.html b/app/dev.html index ef29677..1f3a8c6 100644 --- a/app/dev.html +++ b/app/dev.html @@ -3,11 +3,11 @@ - - AI SEO Copilot — Dev Preview + + Optia — Dev Preview diff --git a/app/eslint.config.js b/app/eslint.config.js index bf11856..84fca11 100644 --- a/app/eslint.config.js +++ b/app/eslint.config.js @@ -127,6 +127,19 @@ export default [ "no-unused-vars": "off", }, }, + { + // Build scripts (icon generation, etc.) - Node.js environment + files: ["scripts/**/*.{js,mjs}"], + languageOptions: { + parserOptions: { + ecmaVersion: "latest", + sourceType: "module", + }, + globals: { + ...globals.node, + }, + }, + }, { ignores: [ "node_modules/**", diff --git a/app/icons/icon-128.png b/app/icons/icon-128.png index 19207cc..8afcb2e 100644 Binary files a/app/icons/icon-128.png and b/app/icons/icon-128.png differ diff --git a/app/icons/icon-16.png b/app/icons/icon-16.png index 1c20e15..646961b 100644 Binary files a/app/icons/icon-16.png and b/app/icons/icon-16.png differ diff --git a/app/icons/icon-32.png b/app/icons/icon-32.png index b29a519..7dc8833 100644 Binary files a/app/icons/icon-32.png and b/app/icons/icon-32.png differ diff --git a/app/icons/icon-48.png b/app/icons/icon-48.png index 9273bb1..5b1bb65 100644 Binary files a/app/icons/icon-48.png and b/app/icons/icon-48.png differ diff --git a/app/icons/icon.svg b/app/icons/icon.svg new file mode 100644 index 0000000..f587849 --- /dev/null +++ b/app/icons/icon.svg @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + diff --git a/app/manifest.json b/app/manifest.json index 350808e..081f1be 100644 --- a/app/manifest.json +++ b/app/manifest.json @@ -1,7 +1,7 @@ { "manifest_version": 3, - "name": "AI SEO Copilot", - "short_name": "SEO Copilot", + "name": "Optia", + "short_name": "Optia", "description": "Analyze page SEO and get AI-powered recommendations", "version": "0.1.0", "minimum_chrome_version": "116", @@ -31,7 +31,7 @@ "open_in_tab": false }, "action": { - "default_title": "Open AI SEO Copilot", + "default_title": "Open Optia", "default_icon": { "16": "icons/icon-16.png", "32": "icons/icon-32.png", diff --git a/app/package.json b/app/package.json index 39c08f6..51dac05 100644 --- a/app/package.json +++ b/app/package.json @@ -1,5 +1,5 @@ { - "name": "ai-seo-copilot", + "name": "optia", "version": "0.1.0", "private": true, "type": "module", @@ -8,6 +8,7 @@ "dev:ext": "vite", "build": "tsc && vite build", "preview": "vite preview", + "icons": "node scripts/generate-icons.mjs", "test": "vitest run", "test:watch": "vitest", "lint": "eslint .", @@ -49,6 +50,7 @@ "globals": "^17.4.0", "jsdom": "^26.1.0", "postcss": "^8.5.4", + "sharp": "^0.35.2", "standard-version": "^9.5.0", "tailwindcss": "^3.4.17", "typescript": "^5.8.3", diff --git a/app/pnpm-lock.yaml b/app/pnpm-lock.yaml index 46a5678..82ed86c 100644 --- a/app/pnpm-lock.yaml +++ b/app/pnpm-lock.yaml @@ -93,6 +93,9 @@ importers: postcss: specifier: ^8.5.4 version: 8.5.8 + sharp: + specifier: ^0.35.2 + version: 0.35.2 standard-version: specifier: ^9.5.0 version: 9.5.0 @@ -247,6 +250,9 @@ packages: resolution: {integrity: sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==} engines: {node: '>=18'} + '@emnapi/runtime@1.11.1': + resolution: {integrity: sha512-vgj7R3y3Wgx24IQaGPA/R6YFXLHVMOZ0uVEyIQPaWs+rd1AzfEMXlAC22FYwO1XkKR6NPsq7mUandH8oIRdZFw==} + '@esbuild/aix-ppc64@0.25.12': resolution: {integrity: sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==} engines: {node: '>=18'} @@ -461,6 +467,152 @@ packages: resolution: {integrity: sha512-H9XAx3hc0BQHY6l+IFSWHDySypcXsvsuLhgYLUGywmJ5pswRVQJUHpOsobnLYp2ZUaUlKiKDrgWWhosOwAEM8Q==} engines: {node: '>=6.9.0'} + '@img/colour@1.1.0': + resolution: {integrity: sha512-Td76q7j57o/tLVdgS746cYARfSyxk8iEfRxewL9h4OMzYhbW4TAcppl0mT4eyqXddh6L/jwoM75mo7ixa/pCeQ==} + engines: {node: '>=18'} + + '@img/sharp-darwin-arm64@0.35.2': + resolution: {integrity: sha512-eEieHsMksAW4IiO5NzauESRl2D2qz3J/kwUxUrSfV06A93eEaRfMpHXyUb1mAqrR7i8U9A0GRqE9pjn6u1Jjpg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [darwin] + + '@img/sharp-darwin-x64@0.35.2': + resolution: {integrity: sha512-BaktuGPCeHJMARpodR8jK4uKiZrPAy9WrfQW0sdI37clracq8Bp01AYS3SZgi5FS/y5twa9t4+LIuuxQjqRrWw==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [darwin] + + '@img/sharp-freebsd-wasm32@0.35.2': + resolution: {integrity: sha512-YoAxdnd8hPUkvLHd3bWY+YA8nw3xM/RyRopYucNsWHVSan8NLVM3X2volsfoRDcXdUJPg6tXahSd7HXPK7lRnw==} + engines: {node: '>=20.9.0'} + os: [freebsd] + + '@img/sharp-libvips-darwin-arm64@1.3.1': + resolution: {integrity: sha512-4V/M3roRMTYjiwZY9IOVQOE8OyeCxFAkYmyZDrZl51uOKjibm3oeEJ4WAmLxutAfzFbC9jqUiPs2gbnGflH+7g==} + cpu: [arm64] + os: [darwin] + + '@img/sharp-libvips-darwin-x64@1.3.1': + resolution: {integrity: sha512-c0/DxItpJv2+dGhgycJBBgotdqruGYDvA79drdh0MD1dFpy7JzJ/PlXwi1H4rFf0eTy8tgbI91aHDnZIceY3jQ==} + cpu: [x64] + os: [darwin] + + '@img/sharp-libvips-linux-arm64@1.3.1': + resolution: {integrity: sha512-JznefmcK9j1JKPz8AkQDh89kjojubyfOasWBPKfzMIhPwsgDy9evpE/naJTXXXmghS1iFwR8u/kTwh/I2/+GCw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linux-arm@1.3.1': + resolution: {integrity: sha512-aGGy9aWzXgHBG7HNyQPWorZthlp7+x6fDRoPAQbGO3ThcttuTyKIx3NuSHb6zb4gBNq6/yNn9f1cy9nFKS/Vmg==} + cpu: [arm] + os: [linux] + + '@img/sharp-libvips-linux-ppc64@1.3.1': + resolution: {integrity: sha512-1EkwGNCZk6iWNCMWqrvdJ+r1j0PT1zIz60CNPhYnJlK/zyeWqlsPZIe+ocBVqPF8k/Ssee/NCk+tE9Ryrko6ng==} + cpu: [ppc64] + os: [linux] + + '@img/sharp-libvips-linux-riscv64@1.3.1': + resolution: {integrity: sha512-Ilays+w2bXdnxzxtQdmXR62u8o8GYa3eL4+Gr+1KiE4xperMZUslRaVPJwwPkzlHEjGfXAfRVAa/7CYCtSqsBw==} + cpu: [riscv64] + os: [linux] + + '@img/sharp-libvips-linux-s390x@1.3.1': + resolution: {integrity: sha512-VfBwVHQTbRoj4XlpA/KLZ7ltgMpz+4WSejFzQ+GnoImjo1PtEJ59QB2qR1xQEeRPYIkNrPIm2L4cICMvz4C2ew==} + cpu: [s390x] + os: [linux] + + '@img/sharp-libvips-linux-x64@1.3.1': + resolution: {integrity: sha512-+c8ukgwU62DS54nCAjw7keOfHUkmr0B5QHEdcOqRnodF/MNXJbVI8Eopoj4B/0H8Asr65I+A4Amrn7a85/md6A==} + cpu: [x64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': + resolution: {integrity: sha512-qlKb/pwbkAi1WMsJrYHk7CuDrd12s27U2QnRhFYUoJNrRCmkosMTttuRFat/DDB3IlDm5qE1TJgZ4JDnHX8Ldw==} + cpu: [arm64] + os: [linux] + + '@img/sharp-libvips-linuxmusl-x64@1.3.1': + resolution: {integrity: sha512-yO21HwoUVLN8Qa+/SBjQLMYwBWAVJjeGPNe+hc0OUeMeifEtJqu5a1c4HayE1nNpDih9y3/KkoltfkDodmKAlg==} + cpu: [x64] + os: [linux] + + '@img/sharp-linux-arm64@0.35.2': + resolution: {integrity: sha512-af12Pnd0ZGu2HfP8NayB0kk6eC/lrfbQE6HlR4jD+34wdJ1Vw9TF6TMn6ZvffT+WgqVsl0hRbmNvz2u/23VmwA==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linux-arm@0.35.2': + resolution: {integrity: sha512-SE4kzF2mepn6z+6E7L6lsV8FzuLL6IPQdyX8ZiwROAG/G8td+hP/m7FsFPwidtrF19gvajuC9l6TxAVcsA4S7A==} + engines: {node: '>=20.9.0'} + cpu: [arm] + os: [linux] + + '@img/sharp-linux-ppc64@0.35.2': + resolution: {integrity: sha512-hYSBm7zcNtDCozCxQHYZJiu63b/bXsgRZuOxCIBZsStMM9Vap47iFHdbX4kCvQsblPB/k+clhELpdQJHQLSHvg==} + engines: {node: '>=20.9.0'} + cpu: [ppc64] + os: [linux] + + '@img/sharp-linux-riscv64@0.35.2': + resolution: {integrity: sha512-qQt0Kc13+Hoan/Awq/qMSQw3L+RI1NCRPgD5cUJ/1WSSmIoysLOc72jlRM3E0OHN9Yr313jgeQ2T+zW+F03QFA==} + engines: {node: '>=20.9.0'} + cpu: [riscv64] + os: [linux] + + '@img/sharp-linux-s390x@0.35.2': + resolution: {integrity: sha512-E4fLLfRPzDLlEeDaTzI98OFLcv++WL5ChLLMwPoVd0CIoZQqupBSNbOisPL5am9XsbQ9T84+iiMpUvbFtkunbA==} + engines: {node: '>=20.9.0'} + cpu: [s390x] + os: [linux] + + '@img/sharp-linux-x64@0.35.2': + resolution: {integrity: sha512-gi0zFJJRLswfCZmHtJdikXPOc5u7qamSOS3NHedLqLd4W8Q0NqjdBr6TTRIgsfFjqfTsHFgdfvJ9LwqSgcHiAA==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-linuxmusl-arm64@0.35.2': + resolution: {integrity: sha512-siWbOW1u6HFnFLrp0waKyW7VEf7jYvcDWdrXEFa8AkdAQgEvuu5Fz8/Y70w9EeqAdwDtfU012BhEHHaDqvQNzg==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [linux] + + '@img/sharp-linuxmusl-x64@0.35.2': + resolution: {integrity: sha512-YBqMMcjDi4QGYiSn4vNOYBhmlC4z5AXqkOUUqI2e0AFA4urNv4ESgOgwNl3K+4etQhha0twXlzeF20bbULm9Yg==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [linux] + + '@img/sharp-wasm32@0.35.2': + resolution: {integrity: sha512-Mrv4JQNYVQ94xH+jzZ9r+gowleN8mv2FTgKT+PI6bx5C0G8TdNYndu161pg2i7uoBwxy2ImPMHrJOM2LZef7Bw==} + engines: {node: '>=20.9.0'} + + '@img/sharp-webcontainers-wasm32@0.35.2': + resolution: {integrity: sha512-QNV27pxs9wpApEiCfvHM1RDoP1w1+2KrUWWDPEhEwg+latvOrfuhWrHWZKwdSFwU6jh3myjw/yOCRsUIuOft3g==} + engines: {node: '>=20.9.0'} + cpu: [wasm32] + + '@img/sharp-win32-arm64@0.35.2': + resolution: {integrity: sha512-BiVRYc/t6/Vl3e1hBx0hugG4oN9Pydf4fgMSpxTQJmwGUg/YoXTWHiFeRymHfCZzifxu4F4rpk/I67D0LQ20wQ==} + engines: {node: '>=20.9.0'} + cpu: [arm64] + os: [win32] + + '@img/sharp-win32-ia32@0.35.2': + resolution: {integrity: sha512-YYEhx9PImCC7T0tI8JDMi4DB9LwLCXCU5OWNYEXAxh5Q1ShKkyC6byxzoBJ3gEFDnH2lQckWuDe70G7mB2XJog==} + engines: {node: ^20.9.0} + cpu: [ia32] + os: [win32] + + '@img/sharp-win32-x64@0.35.2': + resolution: {integrity: sha512-imoOyBcoM/iiUr4J6VPpCNjPnjvP/Gks95898yB8YqoGGYmHYbOyCuNv9FMhFgtaiHFGbHW8bxKqRV6VjtXThQ==} + engines: {node: '>=20.9.0'} + cpu: [x64] + os: [win32] + '@isaacs/cliui@8.0.2': resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==} engines: {node: '>=12'} @@ -1289,6 +1441,10 @@ packages: resolution: {integrity: sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==} engines: {node: '>=8'} + detect-libc@2.1.2: + resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} + engines: {node: '>=8'} + detect-newline@3.1.0: resolution: {integrity: sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==} engines: {node: '>=8'} @@ -2599,6 +2755,11 @@ packages: engines: {node: '>=10'} hasBin: true + semver@7.8.5: + resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} + engines: {node: '>=10'} + hasBin: true + set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} @@ -2611,6 +2772,10 @@ packages: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} + sharp@0.35.2: + resolution: {integrity: sha512-FVtFjtBCMiJS6yb5CX7Sop45WFMpeGw6oRKuJnXYgf/f1ms/D7LE/ZUSNxnW7rZ/dbslQWYkoqFHGPaDBtaK4w==} + engines: {node: '>=20.9.0'} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -3323,6 +3488,11 @@ snapshots: '@csstools/css-tokenizer@3.0.4': {} + '@emnapi/runtime@1.11.1': + dependencies: + tslib: 2.8.1 + optional: true + '@esbuild/aix-ppc64@0.25.12': optional: true @@ -3460,6 +3630,112 @@ snapshots: '@hutson/parse-repository-url@3.0.2': {} + '@img/colour@1.1.0': {} + + '@img/sharp-darwin-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-darwin-arm64': 1.3.1 + optional: true + + '@img/sharp-darwin-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-darwin-x64': 1.3.1 + optional: true + + '@img/sharp-freebsd-wasm32@0.35.2': + dependencies: + '@img/sharp-wasm32': 0.35.2 + optional: true + + '@img/sharp-libvips-darwin-arm64@1.3.1': + optional: true + + '@img/sharp-libvips-darwin-x64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-arm64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-arm@1.3.1': + optional: true + + '@img/sharp-libvips-linux-ppc64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-riscv64@1.3.1': + optional: true + + '@img/sharp-libvips-linux-s390x@1.3.1': + optional: true + + '@img/sharp-libvips-linux-x64@1.3.1': + optional: true + + '@img/sharp-libvips-linuxmusl-arm64@1.3.1': + optional: true + + '@img/sharp-libvips-linuxmusl-x64@1.3.1': + optional: true + + '@img/sharp-linux-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm64': 1.3.1 + optional: true + + '@img/sharp-linux-arm@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-arm': 1.3.1 + optional: true + + '@img/sharp-linux-ppc64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-ppc64': 1.3.1 + optional: true + + '@img/sharp-linux-riscv64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-riscv64': 1.3.1 + optional: true + + '@img/sharp-linux-s390x@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-s390x': 1.3.1 + optional: true + + '@img/sharp-linux-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linux-x64': 1.3.1 + optional: true + + '@img/sharp-linuxmusl-arm64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 + optional: true + + '@img/sharp-linuxmusl-x64@0.35.2': + optionalDependencies: + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 + optional: true + + '@img/sharp-wasm32@0.35.2': + dependencies: + '@emnapi/runtime': 1.11.1 + optional: true + + '@img/sharp-webcontainers-wasm32@0.35.2': + dependencies: + '@img/sharp-wasm32': 0.35.2 + optional: true + + '@img/sharp-win32-arm64@0.35.2': + optional: true + + '@img/sharp-win32-ia32@0.35.2': + optional: true + + '@img/sharp-win32-x64@0.35.2': + optional: true + '@isaacs/cliui@8.0.2': dependencies: string-width: 5.1.2 @@ -4385,6 +4661,8 @@ snapshots: detect-indent@6.1.0: {} + detect-libc@2.1.2: {} + detect-newline@3.1.0: {} didyoumean@1.2.2: {} @@ -5858,6 +6136,8 @@ snapshots: semver@7.7.4: {} + semver@7.8.5: {} + set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 @@ -5880,6 +6160,38 @@ snapshots: es-errors: 1.3.0 es-object-atoms: 1.1.1 + sharp@0.35.2: + dependencies: + '@img/colour': 1.1.0 + detect-libc: 2.1.2 + semver: 7.8.5 + optionalDependencies: + '@img/sharp-darwin-arm64': 0.35.2 + '@img/sharp-darwin-x64': 0.35.2 + '@img/sharp-freebsd-wasm32': 0.35.2 + '@img/sharp-libvips-darwin-arm64': 1.3.1 + '@img/sharp-libvips-darwin-x64': 1.3.1 + '@img/sharp-libvips-linux-arm': 1.3.1 + '@img/sharp-libvips-linux-arm64': 1.3.1 + '@img/sharp-libvips-linux-ppc64': 1.3.1 + '@img/sharp-libvips-linux-riscv64': 1.3.1 + '@img/sharp-libvips-linux-s390x': 1.3.1 + '@img/sharp-libvips-linux-x64': 1.3.1 + '@img/sharp-libvips-linuxmusl-arm64': 1.3.1 + '@img/sharp-libvips-linuxmusl-x64': 1.3.1 + '@img/sharp-linux-arm': 0.35.2 + '@img/sharp-linux-arm64': 0.35.2 + '@img/sharp-linux-ppc64': 0.35.2 + '@img/sharp-linux-riscv64': 0.35.2 + '@img/sharp-linux-s390x': 0.35.2 + '@img/sharp-linux-x64': 0.35.2 + '@img/sharp-linuxmusl-arm64': 0.35.2 + '@img/sharp-linuxmusl-x64': 0.35.2 + '@img/sharp-webcontainers-wasm32': 0.35.2 + '@img/sharp-win32-arm64': 0.35.2 + '@img/sharp-win32-ia32': 0.35.2 + '@img/sharp-win32-x64': 0.35.2 + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 diff --git a/app/pnpm-workspace.yaml b/app/pnpm-workspace.yaml index c5739b7..f1823ff 100644 --- a/app/pnpm-workspace.yaml +++ b/app/pnpm-workspace.yaml @@ -1,2 +1,6 @@ -ignoredBuiltDependencies: - - esbuild +# pnpm build-script approval. pnpm v11 replaced onlyBuiltDependencies with +# `allowBuilds`; this allows esbuild's (and sharp's) postinstall so CI's +# `pnpm install --frozen-lockfile` passes instead of ERR_PNPM_IGNORED_BUILDS. +allowBuilds: + esbuild: true + sharp: true diff --git a/app/scripts/generate-icons.mjs b/app/scripts/generate-icons.mjs new file mode 100644 index 0000000..bb6b1ed --- /dev/null +++ b/app/scripts/generate-icons.mjs @@ -0,0 +1,28 @@ +/** + * Generate the Optia extension icons (PNG) from the SVG master. + * + * Usage: pnpm icons + * Source: icons/icon.svg -> icons/icon-{16,32,48,128}.png + * + * Requires `sharp` (devDependency). Re-run whenever icon.svg changes. + */ +import sharp from "sharp"; +import { readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +const here = dirname(fileURLToPath(import.meta.url)); +const iconsDir = join(here, "..", "icons"); +const svg = readFileSync(join(iconsDir, "icon.svg")); +const sizes = [16, 32, 48, 128]; + +for (const size of sizes) { + const out = join(iconsDir, `icon-${size}.png`); + await sharp(svg, { density: 512 }) + .resize(size, size, { fit: "contain", background: { r: 0, g: 0, b: 0, alpha: 0 } }) + .png() + .toFile(out); + console.log(`✓ icon-${size}.png`); +} + +console.log("Done — Optia icons regenerated."); diff --git a/app/src/background/service-worker.ts b/app/src/background/service-worker.ts index 6e23e30..c183b56 100644 --- a/app/src/background/service-worker.ts +++ b/app/src/background/service-worker.ts @@ -1,6 +1,6 @@ import { extractPageDataInline } from "@/lib/extract-page-data-inline"; -console.log("[AI SEO Copilot] Service worker initializing..."); +console.log("[Optia] Service worker initializing..."); // Disable automatic panel opening - we'll handle it manually for per-tab scoping chrome.sidePanel.setPanelBehavior({ openPanelOnActionClick: false }); @@ -32,7 +32,7 @@ chrome.storage.session.get(PANEL_TABS_KEY).then((result) => { const saved = result[PANEL_TABS_KEY] as number[] | undefined; if (saved?.length) { saved.forEach((id) => panelTabs.add(id)); - console.log("[AI SEO Copilot] Restored panel tabs:", saved); + console.log("[Optia] Restored panel tabs:", saved); } }).catch(() => { // Ignore errors @@ -56,26 +56,26 @@ async function removePanelTab(tabId: number): Promise { await persistPanelTabs(); } -console.log("[AI SEO Copilot] Service worker initialized successfully"); +console.log("[Optia] Service worker initialized successfully"); // When the user switches tabs, hide the panel unless it was opened on that tab. // Only start scoping after the panel has been opened at least once — otherwise // the first click after install/reload would be blocked (chicken-and-egg bug). chrome.tabs.onActivated.addListener(({ tabId }) => { - console.log("[AI SEO Copilot] Tab activated:", tabId, "panelTabs:", Array.from(panelTabs)); + console.log("[Optia] Tab activated:", tabId, "panelTabs:", Array.from(panelTabs)); if (panelTabs.size === 0) { - console.log("[AI SEO Copilot] panelTabs empty, skipping"); + console.log("[Optia] panelTabs empty, skipping"); return; } if (panelTabs.has(tabId)) { - console.log("[AI SEO Copilot] Enabling panel for tab:", tabId); + console.log("[Optia] Enabling panel for tab:", tabId); chrome.sidePanel.setOptions({ tabId, path: "src/sidepanel/index.html", enabled: true, }); } else { - console.log("[AI SEO Copilot] Disabling panel for tab:", tabId); + console.log("[Optia] Disabling panel for tab:", tabId); chrome.sidePanel.setOptions({ tabId, enabled: false }); } }); @@ -89,14 +89,14 @@ chrome.tabs.onRemoved.addListener((tabId) => { }); }); -console.log("[AI SEO Copilot] Registering message listener..."); +console.log("[Optia] Registering message listener..."); chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { - console.log("[AI SEO Copilot] Received message:", message.type); + console.log("[Optia] Received message:", message.type); // Ping handler for verifying service worker is responsive if (message.type === "PING") { - console.log("[AI SEO Copilot] Responding to PING"); + console.log("[Optia] Responding to PING"); sendResponse({ pong: true, timestamp: Date.now() }); return true; } @@ -147,31 +147,31 @@ chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => { // (Side panel already tried content script and it failed, so skip that step here) if (message.type === "EXTRACT_PAGE_DATA") { const tabId = message.tabId as number; - console.log("[AI SEO Copilot] EXTRACT_PAGE_DATA for tab:", tabId); + console.log("[Optia] EXTRACT_PAGE_DATA for tab:", tabId); (async () => { try { // Inject the analyzer directly via executeScript - console.log("[AI SEO Copilot] Executing analyzer script..."); + console.log("[Optia] Executing analyzer script..."); const results = await chrome.scripting.executeScript({ target: { tabId }, func: extractPageDataInline, }); - console.log("[AI SEO Copilot] executeScript completed, results:", results?.length ?? 0); + console.log("[Optia] executeScript completed, results:", results?.length ?? 0); const data = results?.[0]?.result; if (data) { - console.log("[AI SEO Copilot] Sending extracted data back"); + console.log("[Optia] Sending extracted data back"); sendResponse({ data }); } else { - console.log("[AI SEO Copilot] No data from executeScript"); + console.log("[Optia] No data from executeScript"); sendResponse({ error: "Failed to extract page data" }); } } catch (error) { - console.error("[AI SEO Copilot] EXTRACT_PAGE_DATA error:", error); + console.error("[Optia] EXTRACT_PAGE_DATA error:", error); sendResponse({ error: String(error) }); } })().catch((err) => { // Catch any unhandled promise rejections from the async IIFE - console.error("[AI SEO Copilot] Unhandled error in EXTRACT_PAGE_DATA:", err); + console.error("[Optia] Unhandled error in EXTRACT_PAGE_DATA:", err); try { sendResponse({ error: String(err) }); } catch { diff --git a/app/src/components/AltTextEditor.tsx b/app/src/components/AltTextEditor.tsx index c64c9d9..4a5983b 100644 --- a/app/src/components/AltTextEditor.tsx +++ b/app/src/components/AltTextEditor.tsx @@ -35,18 +35,18 @@ export function AltTextEditor({ }; return ( -
+
{alt
setAlt(e.target.value)} placeholder="Enter alt text..." - className="w-full rounded-input bg-bg-500 px-3 py-2 text-body-12 text-text-primary placeholder:text-bg-300 outline-none focus:ring-1 focus:ring-accent-blue" + className="w-full rounded-input bg-surface-2 px-3 py-2 text-body-12 text-ink placeholder:text-faint outline-none focus:ring-1 focus:ring-brand" />
diff --git a/app/src/components/Footer.tsx b/app/src/components/Footer.tsx index 537a3ce..5613a76 100644 --- a/app/src/components/Footer.tsx +++ b/app/src/components/Footer.tsx @@ -9,7 +9,7 @@ const links = [ export function Footer() { return ( -