diff --git a/package-lock.json b/package-lock.json index 35fdafc..5bf3771 100644 --- a/package-lock.json +++ b/package-lock.json @@ -76,6 +76,7 @@ "globals": "^15.15.0", "lovable-tagger": "^1.1.10", "postcss": "^8.5.6", + "sharp": "^0.34.4", "tailwindcss": "^3.4.17", "typescript": "^5.8.3", "typescript-eslint": "^8.38.0", @@ -153,6 +154,17 @@ "node": ">=6.9.0" } }, + "node_modules/@emnapi/runtime": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@emnapi/runtime/-/runtime-1.5.0.tgz", + "integrity": "sha512-97/BJ3iXHww3djw6hYIfErCZFee7qCtrneuLa20UXFCOTCfBM2cvQHjWJ2EG0s0MtdNwInarqCTz35i4wWXHsQ==", + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "tslib": "^2.4.0" + } + }, "node_modules/@esbuild/aix-ppc64": { "version": "0.21.5", "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", @@ -845,6 +857,456 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@img/colour": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@img/colour/-/colour-1.0.0.tgz", + "integrity": "sha512-A5P/LfWGFSl6nsckYtjw9da+19jB8hkJ6ACTGcDfEJ0aE+l2n2El7dsVM7UVHZQ9s2lmYMWlrS21YLy2IR1LUw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@img/sharp-darwin-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-arm64/-/sharp-darwin-arm64-0.34.4.tgz", + "integrity": "sha512-sitdlPzDVyvmINUdJle3TNHl+AG9QcwiAMsXmccqsCOMZNIdW2/7S26w0LyU8euiLVzFBL3dXPwVCq/ODnf2vA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-arm64": "1.2.3" + } + }, + "node_modules/@img/sharp-darwin-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-darwin-x64/-/sharp-darwin-x64-0.34.4.tgz", + "integrity": "sha512-rZheupWIoa3+SOdF/IcUe1ah4ZDpKBGWcsPX6MT0lYniH9micvIU7HQkYTfrx5Xi8u+YqwLtxC/3vl8TQN6rMg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-darwin-x64": "1.2.3" + } + }, + "node_modules/@img/sharp-libvips-darwin-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-arm64/-/sharp-libvips-darwin-arm64-1.2.3.tgz", + "integrity": "sha512-QzWAKo7kpHxbuHqUC28DZ9pIKpSi2ts2OJnoIGI26+HMgq92ZZ4vk8iJd4XsxN+tYfNJxzH6W62X5eTcsBymHw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-darwin-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-darwin-x64/-/sharp-libvips-darwin-x64-1.2.3.tgz", + "integrity": "sha512-Ju+g2xn1E2AKO6YBhxjj+ACcsPQRHT0bhpglxcEf+3uyPY+/gL8veniKoo96335ZaPo03bdDXMv0t+BBFAbmRA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "darwin" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm/-/sharp-libvips-linux-arm-1.2.3.tgz", + "integrity": "sha512-x1uE93lyP6wEwGvgAIV0gP6zmaL/a0tGzJs/BIDDG0zeBhMnuUPm7ptxGhUbcGs4okDJrk4nxgrmxpib9g6HpA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-arm64/-/sharp-libvips-linux-arm64-1.2.3.tgz", + "integrity": "sha512-I4RxkXU90cpufazhGPyVujYwfIm9Nk1QDEmiIsaPwdnm013F7RIceaCc87kAH+oUB1ezqEvC6ga4m7MSlqsJvQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-ppc64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-ppc64/-/sharp-libvips-linux-ppc64-1.2.3.tgz", + "integrity": "sha512-Y2T7IsQvJLMCBM+pmPbM3bKT/yYJvVtLJGfCs4Sp95SjvnFIjynbjzsa7dY1fRJX45FTSfDksbTp6AGWudiyCg==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-s390x": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-s390x/-/sharp-libvips-linux-s390x-1.2.3.tgz", + "integrity": "sha512-RgWrs/gVU7f+K7P+KeHFaBAJlNkD1nIZuVXdQv6S+fNA6syCcoboNjsV2Pou7zNlVdNQoQUpQTk8SWDHUA3y/w==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linux-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linux-x64/-/sharp-libvips-linux-x64-1.2.3.tgz", + "integrity": "sha512-3JU7LmR85K6bBiRzSUc/Ff9JBVIFVvq6bomKE0e63UXGeRw2HPVEjoJke1Yx+iU4rL7/7kUjES4dZ/81Qjhyxg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-arm64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-arm64/-/sharp-libvips-linuxmusl-arm64-1.2.3.tgz", + "integrity": "sha512-F9q83RZ8yaCwENw1GieztSfj5msz7GGykG/BA+MOUefvER69K/ubgFHNeSyUu64amHIYKGDs4sRCMzXVj8sEyw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-libvips-linuxmusl-x64": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/@img/sharp-libvips-linuxmusl-x64/-/sharp-libvips-linuxmusl-x64-1.2.3.tgz", + "integrity": "sha512-U5PUY5jbc45ANM6tSJpsgqmBF/VsL6LnxJmIf11kB7J5DctHgqm0SkuXzVWtIY90GnJxKnC/JT251TDnk1fu/g==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "LGPL-3.0-or-later", + "optional": true, + "os": [ + "linux" + ], + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-linux-arm": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm/-/sharp-linux-arm-0.34.4.tgz", + "integrity": "sha512-Xyam4mlqM0KkTHYVSuc6wXRmM7LGN0P12li03jAnZ3EJWZqj83+hi8Y9UxZUbxsgsK1qOEwg7O0Bc0LjqQVtxA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm": "1.2.3" + } + }, + "node_modules/@img/sharp-linux-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-arm64/-/sharp-linux-arm64-0.34.4.tgz", + "integrity": "sha512-YXU1F/mN/Wu786tl72CyJjP/Ngl8mGHN1hST4BGl+hiW5jhCnV2uRVTNOcaYPs73NeT/H8Upm3y9582JVuZHrQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-arm64": "1.2.3" + } + }, + "node_modules/@img/sharp-linux-ppc64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-ppc64/-/sharp-linux-ppc64-0.34.4.tgz", + "integrity": "sha512-F4PDtF4Cy8L8hXA2p3TO6s4aDt93v+LKmpcYFLAVdkkD3hSxZzee0rh6/+94FpAynsuMpLX5h+LRsSG3rIciUQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-ppc64": "1.2.3" + } + }, + "node_modules/@img/sharp-linux-s390x": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-s390x/-/sharp-linux-s390x-0.34.4.tgz", + "integrity": "sha512-qVrZKE9Bsnzy+myf7lFKvng6bQzhNUAYcVORq2P7bDlvmF6u2sCmK2KyEQEBdYk+u3T01pVsPrkj943T1aJAsw==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-s390x": "1.2.3" + } + }, + "node_modules/@img/sharp-linux-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linux-x64/-/sharp-linux-x64-0.34.4.tgz", + "integrity": "sha512-ZfGtcp2xS51iG79c6Vhw9CWqQC8l2Ot8dygxoDoIQPTat/Ov3qAa8qpxSrtAEAJW+UjTXc4yxCjNfxm4h6Xm2A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linux-x64": "1.2.3" + } + }, + "node_modules/@img/sharp-linuxmusl-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-arm64/-/sharp-linuxmusl-arm64-0.34.4.tgz", + "integrity": "sha512-8hDVvW9eu4yHWnjaOOR8kHVrew1iIX+MUgwxSuH2XyYeNRtLUe4VNioSqbNkB7ZYQJj9rUTT4PyRscyk2PXFKA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3" + } + }, + "node_modules/@img/sharp-linuxmusl-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-linuxmusl-x64/-/sharp-linuxmusl-x64-0.34.4.tgz", + "integrity": "sha512-lU0aA5L8QTlfKjpDCEFOZsTYGn3AEiO6db8W5aQDxj0nQkVrZWmN3ZP9sYKWJdtq3PWPhUNlqehWyXpYDcI9Sg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-libvips-linuxmusl-x64": "1.2.3" + } + }, + "node_modules/@img/sharp-wasm32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-wasm32/-/sharp-wasm32-0.34.4.tgz", + "integrity": "sha512-33QL6ZO/qpRyG7woB/HUALz28WnTMI2W1jgX3Nu2bypqLIKx/QKMILLJzJjI+SIbvXdG9fUnmrxR7vbi1sTBeA==", + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later AND MIT", + "optional": true, + "dependencies": { + "@emnapi/runtime": "^1.5.0" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-arm64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-arm64/-/sharp-win32-arm64-0.34.4.tgz", + "integrity": "sha512-2Q250do/5WXTwxW3zjsEuMSv5sUU4Tq9VThWKlU2EYLm4MB7ZeMwF+SFJutldYODXF6jzc6YEOC+VfX0SZQPqA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-ia32": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-ia32/-/sharp-win32-ia32-0.34.4.tgz", + "integrity": "sha512-3ZeLue5V82dT92CNL6rsal6I2weKw1cYu+rGKm8fOCCtJTR2gYeUfY3FqUnIJsMUPIH68oS5jmZ0NiJ508YpEw==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, + "node_modules/@img/sharp-win32-x64": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/@img/sharp-win32-x64/-/sharp-win32-x64-0.34.4.tgz", + "integrity": "sha512-xIyj4wpYs8J18sVN3mSQjwrw7fKUqRw+Z5rnHNCy5fYTxigBz81u5mOMPmFumwjcn8+ld1ppptMBCLic1nz6ig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "Apache-2.0 AND LGPL-3.0-or-later", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -4282,6 +4744,16 @@ "dev": true, "license": "MIT" }, + "node_modules/detect-libc": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.1.tgz", + "integrity": "sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, "node_modules/detect-node-es": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/detect-node-es/-/detect-node-es-1.1.0.tgz", @@ -6567,6 +7039,49 @@ "node": ">=10" } }, + "node_modules/sharp": { + "version": "0.34.4", + "resolved": "https://registry.npmjs.org/sharp/-/sharp-0.34.4.tgz", + "integrity": "sha512-FUH39xp3SBPnxWvd5iib1X8XY7J0K0X7d93sie9CJg2PO8/7gmg89Nve6OjItK53/MlAushNNxteBYfM6DEuoA==", + "dev": true, + "hasInstallScript": true, + "license": "Apache-2.0", + "dependencies": { + "@img/colour": "^1.0.0", + "detect-libc": "^2.1.0", + "semver": "^7.7.2" + }, + "engines": { + "node": "^18.17.0 || ^20.3.0 || >=21.0.0" + }, + "funding": { + "url": "https://opencollective.com/libvips" + }, + "optionalDependencies": { + "@img/sharp-darwin-arm64": "0.34.4", + "@img/sharp-darwin-x64": "0.34.4", + "@img/sharp-libvips-darwin-arm64": "1.2.3", + "@img/sharp-libvips-darwin-x64": "1.2.3", + "@img/sharp-libvips-linux-arm": "1.2.3", + "@img/sharp-libvips-linux-arm64": "1.2.3", + "@img/sharp-libvips-linux-ppc64": "1.2.3", + "@img/sharp-libvips-linux-s390x": "1.2.3", + "@img/sharp-libvips-linux-x64": "1.2.3", + "@img/sharp-libvips-linuxmusl-arm64": "1.2.3", + "@img/sharp-libvips-linuxmusl-x64": "1.2.3", + "@img/sharp-linux-arm": "0.34.4", + "@img/sharp-linux-arm64": "0.34.4", + "@img/sharp-linux-ppc64": "0.34.4", + "@img/sharp-linux-s390x": "0.34.4", + "@img/sharp-linux-x64": "0.34.4", + "@img/sharp-linuxmusl-arm64": "0.34.4", + "@img/sharp-linuxmusl-x64": "0.34.4", + "@img/sharp-wasm32": "0.34.4", + "@img/sharp-win32-arm64": "0.34.4", + "@img/sharp-win32-ia32": "0.34.4", + "@img/sharp-win32-x64": "0.34.4" + } + }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", diff --git a/package.json b/package.json index 4229319..30519f3 100644 --- a/package.json +++ b/package.json @@ -8,7 +8,8 @@ "build": "vite build", "build:dev": "vite build --mode development", "lint": "eslint .", - "preview": "vite preview" + "preview": "vite preview", + "optimize-images": "node scripts/optimize-images.js" }, "dependencies": { "@hookform/resolvers": "^3.10.0", @@ -79,6 +80,7 @@ "globals": "^15.15.0", "lovable-tagger": "^1.1.10", "postcss": "^8.5.6", + "sharp": "^0.34.4", "tailwindcss": "^3.4.17", "typescript": "^5.8.3", "typescript-eslint": "^8.38.0", diff --git a/public/images/projects/cloud_1.webp b/public/images/projects/cloud_1.webp new file mode 100644 index 0000000..77643bf Binary files /dev/null and b/public/images/projects/cloud_1.webp differ diff --git a/public/images/projects/cub3d.webp b/public/images/projects/cub3d.webp new file mode 100644 index 0000000..8c1d0b8 Binary files /dev/null and b/public/images/projects/cub3d.webp differ diff --git a/public/images/projects/homemade_nas.webp b/public/images/projects/homemade_nas.webp new file mode 100644 index 0000000..19824a3 Binary files /dev/null and b/public/images/projects/homemade_nas.webp differ diff --git a/public/images/projects/minishell.webp b/public/images/projects/minishell.webp new file mode 100644 index 0000000..145393e Binary files /dev/null and b/public/images/projects/minishell.webp differ diff --git a/public/images/projects/pong.webp b/public/images/projects/pong.webp new file mode 100644 index 0000000..979a775 Binary files /dev/null and b/public/images/projects/pong.webp differ diff --git a/public/images/sites/avopieces/mookup/3-devices-black (1).webp b/public/images/sites/avopieces/mookup/3-devices-black (1).webp new file mode 100644 index 0000000..ce79337 Binary files /dev/null and b/public/images/sites/avopieces/mookup/3-devices-black (1).webp differ diff --git a/public/images/sites/avopieces/mookup/3-devices-white (1).webp b/public/images/sites/avopieces/mookup/3-devices-white (1).webp new file mode 100644 index 0000000..03b221b Binary files /dev/null and b/public/images/sites/avopieces/mookup/3-devices-white (1).webp differ diff --git a/public/images/sites/avopieces/mookup/desktop (1).webp b/public/images/sites/avopieces/mookup/desktop (1).webp new file mode 100644 index 0000000..f257a1d Binary files /dev/null and b/public/images/sites/avopieces/mookup/desktop (1).webp differ diff --git a/public/images/sites/avopieces/mookup/laptop (1).webp b/public/images/sites/avopieces/mookup/laptop (1).webp new file mode 100644 index 0000000..1a78b07 Binary files /dev/null and b/public/images/sites/avopieces/mookup/laptop (1).webp differ diff --git a/public/images/sites/avopieces/mookup/mobile-black (1).webp b/public/images/sites/avopieces/mookup/mobile-black (1).webp new file mode 100644 index 0000000..867d2ae Binary files /dev/null and b/public/images/sites/avopieces/mookup/mobile-black (1).webp differ diff --git a/public/images/sites/avopieces/mookup/mobile-white (1).webp b/public/images/sites/avopieces/mookup/mobile-white (1).webp new file mode 100644 index 0000000..dec013f Binary files /dev/null and b/public/images/sites/avopieces/mookup/mobile-white (1).webp differ diff --git a/public/images/sites/avopieces/mookup/tablet-black (1).webp b/public/images/sites/avopieces/mookup/tablet-black (1).webp new file mode 100644 index 0000000..800c0c4 Binary files /dev/null and b/public/images/sites/avopieces/mookup/tablet-black (1).webp differ diff --git a/public/images/sites/avopieces/mookup/tablet-white (1).webp b/public/images/sites/avopieces/mookup/tablet-white (1).webp new file mode 100644 index 0000000..5ef302a Binary files /dev/null and b/public/images/sites/avopieces/mookup/tablet-white (1).webp differ diff --git a/public/images/sites/avopieces/pc.webp b/public/images/sites/avopieces/pc.webp new file mode 100644 index 0000000..cbae2da Binary files /dev/null and b/public/images/sites/avopieces/pc.webp differ diff --git a/public/images/sites/avopieces/tablette.webp b/public/images/sites/avopieces/tablette.webp new file mode 100644 index 0000000..2bd5da3 Binary files /dev/null and b/public/images/sites/avopieces/tablette.webp differ diff --git a/public/images/sites/avopieces/tel.webp b/public/images/sites/avopieces/tel.webp new file mode 100644 index 0000000..4215160 Binary files /dev/null and b/public/images/sites/avopieces/tel.webp differ diff --git a/public/images/sites/etsidemain/mookup/3-devices-black.webp b/public/images/sites/etsidemain/mookup/3-devices-black.webp new file mode 100644 index 0000000..df7ac60 Binary files /dev/null and b/public/images/sites/etsidemain/mookup/3-devices-black.webp differ diff --git a/public/images/sites/etsidemain/mookup/3-devices-white.webp b/public/images/sites/etsidemain/mookup/3-devices-white.webp new file mode 100644 index 0000000..c07535f Binary files /dev/null and b/public/images/sites/etsidemain/mookup/3-devices-white.webp differ diff --git a/public/images/sites/etsidemain/mookup/desktop.webp b/public/images/sites/etsidemain/mookup/desktop.webp new file mode 100644 index 0000000..30321c6 Binary files /dev/null and b/public/images/sites/etsidemain/mookup/desktop.webp differ diff --git a/public/images/sites/etsidemain/mookup/laptop.webp b/public/images/sites/etsidemain/mookup/laptop.webp new file mode 100644 index 0000000..a9d7173 Binary files /dev/null and b/public/images/sites/etsidemain/mookup/laptop.webp differ diff --git a/public/images/sites/etsidemain/mookup/mobile-black.webp b/public/images/sites/etsidemain/mookup/mobile-black.webp new file mode 100644 index 0000000..7e8f23c Binary files /dev/null and b/public/images/sites/etsidemain/mookup/mobile-black.webp differ diff --git a/public/images/sites/etsidemain/mookup/mobile-white.webp b/public/images/sites/etsidemain/mookup/mobile-white.webp new file mode 100644 index 0000000..5673dc6 Binary files /dev/null and b/public/images/sites/etsidemain/mookup/mobile-white.webp differ diff --git a/public/images/sites/etsidemain/mookup/tablet-black.webp b/public/images/sites/etsidemain/mookup/tablet-black.webp new file mode 100644 index 0000000..1fdfea8 Binary files /dev/null and b/public/images/sites/etsidemain/mookup/tablet-black.webp differ diff --git a/public/images/sites/etsidemain/mookup/tablet-white.webp b/public/images/sites/etsidemain/mookup/tablet-white.webp new file mode 100644 index 0000000..b04ba77 Binary files /dev/null and b/public/images/sites/etsidemain/mookup/tablet-white.webp differ diff --git a/public/images/sites/etsidemain/pc.webp b/public/images/sites/etsidemain/pc.webp new file mode 100644 index 0000000..2c9d557 Binary files /dev/null and b/public/images/sites/etsidemain/pc.webp differ diff --git a/public/images/sites/etsidemain/tablette.webp b/public/images/sites/etsidemain/tablette.webp new file mode 100644 index 0000000..4d4c7e0 Binary files /dev/null and b/public/images/sites/etsidemain/tablette.webp differ diff --git a/public/images/sites/etsidemain/tel.webp b/public/images/sites/etsidemain/tel.webp new file mode 100644 index 0000000..8d02015 Binary files /dev/null and b/public/images/sites/etsidemain/tel.webp differ diff --git a/scripts/cleanup-old-images.js b/scripts/cleanup-old-images.js new file mode 100644 index 0000000..3ad1066 --- /dev/null +++ b/scripts/cleanup-old-images.js @@ -0,0 +1,50 @@ +import { unlink } from 'fs/promises'; +import { readdir, stat } from 'fs/promises'; +import { join, extname } from 'path'; +import { fileURLToPath } from 'url'; +import { dirname } from 'path'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const publicDir = join(__dirname, '..', 'public', 'images'); + +async function getAllImageFiles(dir) { + const files = []; + const items = await readdir(dir); + + for (const item of items) { + const fullPath = join(dir, item); + const itemStat = await stat(fullPath); + + if (itemStat.isDirectory()) { + files.push(...(await getAllImageFiles(fullPath))); + } else if (['.png', '.jpg', '.jpeg'].includes(extname(item).toLowerCase())) { + files.push(fullPath); + } + } + + return files; +} + +async function main() { + console.log('🗑️ Suppression des anciennes images PNG/JPG...\n'); + + const imageFiles = await getAllImageFiles(publicDir); + console.log(`📊 ${imageFiles.length} images à supprimer\n`); + + let deletedCount = 0; + for (const file of imageFiles) { + try { + await unlink(file); + console.log(`✅ Supprimé: ${file}`); + deletedCount++; + } catch (error) { + console.error(`❌ Erreur avec ${file}:`, error.message); + } + } + + console.log(`\n✨ ${deletedCount} image(s) supprimée(s)!`); +} + +main().catch(console.error); diff --git a/scripts/optimize-images.js b/scripts/optimize-images.js new file mode 100644 index 0000000..289f0a5 --- /dev/null +++ b/scripts/optimize-images.js @@ -0,0 +1,88 @@ +import sharp from 'sharp'; +import { readdir, stat } from 'fs/promises'; +import { join, extname, dirname, basename } from 'path'; +import { fileURLToPath } from 'url'; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); + +const publicDir = join(__dirname, '..', 'public', 'images'); + +async function getAllImageFiles(dir) { + const files = []; + const items = await readdir(dir); + + for (const item of items) { + const fullPath = join(dir, item); + const itemStat = await stat(fullPath); + + if (itemStat.isDirectory()) { + files.push(...(await getAllImageFiles(fullPath))); + } else if (['.png', '.jpg', '.jpeg'].includes(extname(item).toLowerCase())) { + files.push(fullPath); + } + } + + return files; +} + +async function optimizeImage(filePath) { + const ext = extname(filePath).toLowerCase(); + const dir = dirname(filePath); + const name = basename(filePath, ext); + const webpPath = join(dir, `${name}.webp`); + + try { + const stats = await stat(filePath); + const originalSize = stats.size; + + // Convertir en WebP avec optimisation + await sharp(filePath) + .webp({ + quality: 85, + effort: 6 + }) + .toFile(webpPath); + + const newStats = await stat(webpPath); + const newSize = newStats.size; + const reduction = ((originalSize - newSize) / originalSize * 100).toFixed(2); + + console.log(`✅ ${basename(filePath)}`); + console.log(` Original: ${(originalSize / 1024).toFixed(2)} KB`); + console.log(` WebP: ${(newSize / 1024).toFixed(2)} KB`); + console.log(` Réduction: ${reduction}%\n`); + + return { originalSize, newSize }; + } catch (error) { + console.error(`❌ Erreur avec ${filePath}:`, error.message); + return { originalSize: 0, newSize: 0 }; + } +} + +async function main() { + console.log('🖼️ Optimisation des images...\n'); + + const imageFiles = await getAllImageFiles(publicDir); + console.log(`📊 ${imageFiles.length} images trouvées\n`); + + let totalOriginal = 0; + let totalNew = 0; + + for (const file of imageFiles) { + const { originalSize, newSize } = await optimizeImage(file); + totalOriginal += originalSize; + totalNew += newSize; + } + + const totalReduction = ((totalOriginal - totalNew) / totalOriginal * 100).toFixed(2); + + console.log('=' .repeat(50)); + console.log(`📊 RÉSUMÉ TOTAL:`); + console.log(` Taille originale: ${(totalOriginal / 1024 / 1024).toFixed(2)} MB`); + console.log(` Taille optimisée: ${(totalNew / 1024 / 1024).toFixed(2)} MB`); + console.log(` Économie totale: ${totalReduction}%`); + console.log(` ${(totalOriginal - totalNew) / 1024 / 1024} MB économisés! 🎉`); +} + +main().catch(console.error); diff --git a/scripts/update-image-references.js b/scripts/update-image-references.js new file mode 100644 index 0000000..0d7790e --- /dev/null +++ b/scripts/update-image-references.js @@ -0,0 +1,44 @@ +import { readFile, writeFile } from 'fs/promises'; +import { join } from 'path'; + +const filesToUpdate = [ + 'src/components/sections/ProjectsSection.tsx', + 'src/data/projects.ts' +]; + +async function updateFile(filePath) { + try { + let content = await readFile(filePath, 'utf-8'); + const originalContent = content; + + // Remplacer .png, .jpg, .jpeg par .webp + content = content.replace(/\.(png|jpg|jpeg)"/g, '.webp"'); + content = content.replace(/\.(png|jpg|jpeg)'/g, ".webp'"); + + if (content !== originalContent) { + await writeFile(filePath, content, 'utf-8'); + console.log(`✅ ${filePath} mis à jour`); + return true; + } else { + console.log(`⏭️ ${filePath} - aucun changement nécessaire`); + return false; + } + } catch (error) { + console.error(`❌ Erreur avec ${filePath}:`, error.message); + return false; + } +} + +async function main() { + console.log('🔄 Mise à jour des références d\'images...\n'); + + let updatedCount = 0; + for (const file of filesToUpdate) { + const updated = await updateFile(file); + if (updated) updatedCount++; + } + + console.log(`\n✨ ${updatedCount} fichier(s) mis à jour!`); +} + +main().catch(console.error); diff --git a/src/components/sections/ProjectsSection.tsx b/src/components/sections/ProjectsSection.tsx index 8ca466f..cd3c923 100644 --- a/src/components/sections/ProjectsSection.tsx +++ b/src/components/sections/ProjectsSection.tsx @@ -11,37 +11,37 @@ export const ProjectsSection = () => { { key: "avopieces", icon: , - image: "/images/sites/avopieces/mookup/3-devices-white (1).png", + image: "/images/sites/avopieces/mookup/3-devices-white (1).webp", }, { key: "nas", icon: , - image: "/images/projects/homemade_nas.png", + image: "/images/projects/homemade_nas.webp", }, { key: "transcendence", icon: , - image: "/images/projects/pong.png", + image: "/images/projects/pong.webp", }, { key: "cloud", icon: , - image: "/images/projects/cloud_1.png", + image: "/images/projects/cloud_1.webp", }, { key: "minishell", icon: , - image: "/images/projects/minishell.png", + image: "/images/projects/minishell.webp", }, { key: "etsidemain", icon: , - image: "/images/sites/etsidemain/mookup/3-devices-white.png", + image: "/images/sites/etsidemain/mookup/3-devices-white.webp", }, { key: "cube3d", icon: , - image: "/images/projects/cub3d.png", + image: "/images/projects/cub3d.webp", }, ]; diff --git a/src/data/projects.ts b/src/data/projects.ts index c01046a..2ea2d19 100644 --- a/src/data/projects.ts +++ b/src/data/projects.ts @@ -59,8 +59,8 @@ export const projectsData: Record = { fr: "Architecture microservices avec Docker Compose, reverse proxy Traefik automatisant les certificats Let's Encrypt, système de backup automatique avec rsync, et monitoring avec Prometheus.", en: "Microservices architecture with Docker Compose, Traefik reverse proxy automating Let's Encrypt certificates, automatic backup system with rsync, and monitoring with Prometheus.", }, - mainImage: "/images/projects/homemade_nas.png", - images: ["/images/projects/homemade_nas.png"], + mainImage: "/images/projects/homemade_nas.webp", + images: ["/images/projects/homemade_nas.webp"], technologies: [ { name: "OpenMediaVault", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/debian/debian-original.svg" }, { name: "Docker", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/docker/docker-original.svg" }, @@ -113,8 +113,8 @@ export const projectsData: Record = { fr: "Architecture MVC avec NestJS, WebSocket rooms pour les matchs, système de queuing Redis pour le matchmaking, JWT pour l'authentification, et Canvas HTML5 pour le rendu du jeu.", en: "MVC architecture with NestJS, WebSocket rooms for matches, Redis queuing system for matchmaking, JWT for authentication, and HTML5 Canvas for game rendering.", }, - mainImage: "/images/projects/pong.png", - images: ["/images/projects/pong.png"], + mainImage: "/images/projects/pong.webp", + images: ["/images/projects/pong.webp"], technologies: [ { name: "React", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/react/react-original.svg" }, { name: "TypeScript", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/typescript/typescript-original.svg" }, @@ -167,8 +167,8 @@ export const projectsData: Record = { fr: "Docker Compose pour l'orchestration, Ansible playbooks pour l'automatisation, volumes Docker pour la persistance, et réseau bridge personnalisé pour l'isolation.", en: "Docker Compose for orchestration, Ansible playbooks for automation, Docker volumes for persistence, and custom bridge network for isolation.", }, - mainImage: "/images/projects/cloud_1.png", - images: ["/images/projects/cloud_1.png"], + mainImage: "/images/projects/cloud_1.webp", + images: ["/images/projects/cloud_1.webp"], technologies: [ { name: "Docker", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/docker/docker-original.svg" }, { name: "Ansible", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/ansible/ansible-original.svg" }, @@ -220,8 +220,8 @@ export const projectsData: Record = { fr: "Tokenizer/Lexer pour le parsing, AST pour représenter les commandes, gestion des descripteurs de fichiers pour les redirections, et table de hash pour les variables d'environnement.", en: "Tokenizer/Lexer for parsing, AST to represent commands, file descriptor management for redirections, and hash table for environment variables.", }, - mainImage: "/images/projects/minishell.png", - images: ["/images/projects/minishell.png"], + mainImage: "/images/projects/minishell.webp", + images: ["/images/projects/minishell.webp"], technologies: [ { name: "C", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/c/c-original.svg" }, { name: "Linux", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/linux/linux-original.svg" }, @@ -272,8 +272,8 @@ export const projectsData: Record = { fr: "Algorithme DDA pour le raycasting, lookup tables pour les calculs trigonométriques, buffer d'image pour le double buffering, et grille 2D pour la détection de collisions.", en: "DDA algorithm for raycasting, lookup tables for trigonometric calculations, image buffer for double buffering, and 2D grid for collision detection.", }, - mainImage: "/images/projects/cub3d.png", - images: ["/images/projects/cub3d.png"], + mainImage: "/images/projects/cub3d.webp", + images: ["/images/projects/cub3d.webp"], technologies: [ { name: "C", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/c/c-original.svg" }, { name: "MiniLibX", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/c/c-original.svg" }, @@ -324,16 +324,16 @@ export const projectsData: Record = { fr: "HTML5/CSS3 sémantique, animations avec transitions CSS et Intersection Observer, balises meta optimisées, structure de données JSON-LD, et optimisation des assets.", en: "Semantic HTML5/CSS3, animations with CSS transitions and Intersection Observer, optimized meta tags, JSON-LD data structure, and asset optimization.", }, - mainImage: "/images/sites/etsidemain/mookup/3-devices-white.png", + mainImage: "/images/sites/etsidemain/mookup/3-devices-white.webp", images: [ - "/images/sites/etsidemain/mookup/3-devices-white.png", - "/images/sites/etsidemain/mookup/desktop.png", - "/images/sites/etsidemain/mookup/laptop.png", - "/images/sites/etsidemain/mookup/tablet-white.png", - "/images/sites/etsidemain/mookup/mobile-white.png", - "/images/sites/etsidemain/pc.png", - "/images/sites/etsidemain/tablette.png", - "/images/sites/etsidemain/tel.png" + "/images/sites/etsidemain/mookup/3-devices-white.webp", + "/images/sites/etsidemain/mookup/desktop.webp", + "/images/sites/etsidemain/mookup/laptop.webp", + "/images/sites/etsidemain/mookup/tablet-white.webp", + "/images/sites/etsidemain/mookup/mobile-white.webp", + "/images/sites/etsidemain/pc.webp", + "/images/sites/etsidemain/tablette.webp", + "/images/sites/etsidemain/tel.webp" ], technologies: [ { name: "HTML5", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/html5/html5-original.svg" }, @@ -386,16 +386,16 @@ export const projectsData: Record = { fr: "Architecture MERN stack, RAG (Retrieval Augmented Generation) pour le chatbot, chiffrement des données, système de roles et permissions, API RESTful, et design system cohérent.", en: "MERN stack architecture, RAG (Retrieval Augmented Generation) for chatbot, data encryption, roles and permissions system, RESTful API, and consistent design system.", }, - mainImage: "/images/sites/avopieces/mookup/3-devices-white (1).png", + mainImage: "/images/sites/avopieces/mookup/3-devices-white (1).webp", images: [ - "/images/sites/avopieces/mookup/3-devices-white (1).png", - "/images/sites/avopieces/mookup/desktop (1).png", - "/images/sites/avopieces/mookup/laptop (1).png", - "/images/sites/avopieces/mookup/tablet-white (1).png", - "/images/sites/avopieces/mookup/mobile-white (1).png", - "/images/sites/avopieces/pc.png", - "/images/sites/avopieces/tablette.png", - "/images/sites/avopieces/tel.png" + "/images/sites/avopieces/mookup/3-devices-white (1).webp", + "/images/sites/avopieces/mookup/desktop (1).webp", + "/images/sites/avopieces/mookup/laptop (1).webp", + "/images/sites/avopieces/mookup/tablet-white (1).webp", + "/images/sites/avopieces/mookup/mobile-white (1).webp", + "/images/sites/avopieces/pc.webp", + "/images/sites/avopieces/tablette.webp", + "/images/sites/avopieces/tel.webp" ], technologies: [ { name: "React", iconUrl: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/react/react-original.svg" },