diff --git a/apps/web/index.html b/apps/web/index.html index a4a4648..f528865 100644 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -2,13 +2,23 @@ - - + + + + + + + + + + + + Rubis Sur l'Ongle diff --git a/apps/web/package.json b/apps/web/package.json index f3a0467..f3650da 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -14,7 +14,8 @@ "typecheck": "tsc -b --noEmit", "test": "vitest run", "test:watch": "vitest", - "msw:init": "msw init public --save" + "msw:init": "msw init public --save", + "icons": "node scripts/generate-icons.mjs" }, "dependencies": { "@fontsource-variable/bricolage-grotesque": "^5.2.5", @@ -45,6 +46,7 @@ }, "devDependencies": { "@eslint/js": "^10.0.1", + "@resvg/resvg-js": "^2.6.2", "@tailwindcss/vite": "^4.1.0", "@tanstack/router-cli": "^1.114.3", "@tanstack/router-plugin": "^1.114.3", diff --git a/apps/web/public/apple-touch-icon.png b/apps/web/public/apple-touch-icon.png new file mode 100644 index 0000000..361d968 Binary files /dev/null and b/apps/web/public/apple-touch-icon.png differ diff --git a/apps/web/public/favicon.svg b/apps/web/public/favicon.svg index 162a2cd..88d7213 100644 --- a/apps/web/public/favicon.svg +++ b/apps/web/public/favicon.svg @@ -1,3 +1,22 @@ -RealFaviconGeneratorhttps://realfavicongenerator.net \ No newline at end of file + + + + + + + + + + + + + + + + diff --git a/apps/web/public/icon-192.png b/apps/web/public/icon-192.png new file mode 100644 index 0000000..487ef8b Binary files /dev/null and b/apps/web/public/icon-192.png differ diff --git a/apps/web/public/icon-512.png b/apps/web/public/icon-512.png new file mode 100644 index 0000000..653ac09 Binary files /dev/null and b/apps/web/public/icon-512.png differ diff --git a/apps/web/public/icon.svg b/apps/web/public/icon.svg new file mode 100644 index 0000000..88d7213 --- /dev/null +++ b/apps/web/public/icon.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/apps/web/public/site.webmanifest b/apps/web/public/site.webmanifest new file mode 100644 index 0000000..d59cbcf --- /dev/null +++ b/apps/web/public/site.webmanifest @@ -0,0 +1,32 @@ +{ + "name": "Rubis.", + "short_name": "Rubis", + "description": "Vos factures relancées toutes seules pendant que vous travaillez.", + "start_url": "/", + "scope": "/", + "display": "standalone", + "orientation": "portrait", + "lang": "fr-FR", + "theme_color": "#9F1239", + "background_color": "#FAF7F2", + "icons": [ + { + "src": "/icon.svg", + "sizes": "any", + "type": "image/svg+xml", + "purpose": "any" + }, + { + "src": "/icon-192.png", + "sizes": "192x192", + "type": "image/png", + "purpose": "any maskable" + }, + { + "src": "/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any maskable" + } + ] +} diff --git a/apps/web/scripts/generate-icons.mjs b/apps/web/scripts/generate-icons.mjs new file mode 100644 index 0000000..f99cf56 --- /dev/null +++ b/apps/web/scripts/generate-icons.mjs @@ -0,0 +1,36 @@ +/** + * Génère les PNGs d'icône PWA depuis `public/icon.svg`. + * + * Sort : + * - public/icon-192.png (Android mostly, manifest) + * - public/icon-512.png (Android, splashscreen) + * - public/apple-touch-icon.png (iOS, 180×180 — recommandation Apple) + * + * À relancer si on touche au design de la gem : + * pnpm --filter @rubis/web run icons + */ + +import { readFile, writeFile } from 'node:fs/promises' +import { fileURLToPath } from 'node:url' +import { dirname, join } from 'node:path' +import { Resvg } from '@resvg/resvg-js' + +const __dirname = dirname(fileURLToPath(import.meta.url)) +const ROOT = join(__dirname, '..') +const SRC = join(ROOT, 'public/icon.svg') + +async function render(size, out) { + const svg = await readFile(SRC, 'utf-8') + const resvg = new Resvg(svg, { + fitTo: { mode: 'width', value: size }, + background: '#FAF7F2', + }) + const buf = resvg.render().asPng() + const dest = join(ROOT, 'public', out) + await writeFile(dest, buf) + console.log(`✓ ${out} (${size}×${size}, ${buf.byteLength.toLocaleString()} B)`) +} + +await render(192, 'icon-192.png') +await render(512, 'icon-512.png') +await render(180, 'apple-touch-icon.png') diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 10bf62f..46b4607 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -247,6 +247,9 @@ importers: '@eslint/js': specifier: ^10.0.1 version: 10.0.1(eslint@10.3.0(jiti@2.7.0)) + '@resvg/resvg-js': + specifier: ^2.6.2 + version: 2.6.2 '@tailwindcss/vite': specifier: ^4.1.0 version: 4.2.4(vite@8.0.10(@types/node@24.12.2)(esbuild@0.27.7)(jiti@2.7.0)(tsx@4.21.0)(yaml@2.8.4)) @@ -2001,6 +2004,82 @@ packages: react-redux: optional: true + '@resvg/resvg-js-android-arm-eabi@2.6.2': + resolution: {integrity: sha512-FrJibrAk6v29eabIPgcTUMPXiEz8ssrAk7TXxsiZzww9UTQ1Z5KAbFJs+Z0Ez+VZTYgnE5IQJqBcoSiMebtPHA==} + engines: {node: '>= 10'} + cpu: [arm] + os: [android] + + '@resvg/resvg-js-android-arm64@2.6.2': + resolution: {integrity: sha512-VcOKezEhm2VqzXpcIJoITuvUS/fcjIw5NA/w3tjzWyzmvoCdd+QXIqy3FBGulWdClvp4g+IfUemigrkLThSjAQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [android] + + '@resvg/resvg-js-darwin-arm64@2.6.2': + resolution: {integrity: sha512-nmok2LnAd6nLUKI16aEB9ydMC6Lidiiq2m1nEBDR1LaaP7FGs4AJ90qDraxX+CWlVuRlvNjyYJTNv8qFjtL9+A==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [darwin] + + '@resvg/resvg-js-darwin-x64@2.6.2': + resolution: {integrity: sha512-GInyZLjgWDfsVT6+SHxQVRwNzV0AuA1uqGsOAW+0th56J7Nh6bHHKXHBWzUrihxMetcFDmQMAX1tZ1fZDYSRsw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [darwin] + + '@resvg/resvg-js-linux-arm-gnueabihf@2.6.2': + resolution: {integrity: sha512-YIV3u/R9zJbpqTTNwTZM5/ocWetDKGsro0SWp70eGEM9eV2MerWyBRZnQIgzU3YBnSBQ1RcxRZvY/UxwESfZIw==} + engines: {node: '>= 10'} + cpu: [arm] + os: [linux] + + '@resvg/resvg-js-linux-arm64-gnu@2.6.2': + resolution: {integrity: sha512-zc2BlJSim7YR4FZDQ8OUoJg5holYzdiYMeobb9pJuGDidGL9KZUv7SbiD4E8oZogtYY42UZEap7dqkkYuA91pg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@resvg/resvg-js-linux-arm64-musl@2.6.2': + resolution: {integrity: sha512-3h3dLPWNgSsD4lQBJPb4f+kvdOSJHa5PjTYVsWHxLUzH4IFTJUAnmuWpw4KqyQ3NA5QCyhw4TWgxk3jRkQxEKg==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [linux] + + '@resvg/resvg-js-linux-x64-gnu@2.6.2': + resolution: {integrity: sha512-IVUe+ckIerA7xMZ50duAZzwf1U7khQe2E0QpUxu5MBJNao5RqC0zwV/Zm965vw6D3gGFUl7j4m+oJjubBVoftw==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@resvg/resvg-js-linux-x64-musl@2.6.2': + resolution: {integrity: sha512-UOf83vqTzoYQO9SZ0fPl2ZIFtNIz/Rr/y+7X8XRX1ZnBYsQ/tTb+cj9TE+KHOdmlTFBxhYzVkP2lRByCzqi4jQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [linux] + + '@resvg/resvg-js-win32-arm64-msvc@2.6.2': + resolution: {integrity: sha512-7C/RSgCa+7vqZ7qAbItfiaAWhyRSoD4l4BQAbVDqRRsRgY+S+hgS3in0Rxr7IorKUpGE69X48q6/nOAuTJQxeQ==} + engines: {node: '>= 10'} + cpu: [arm64] + os: [win32] + + '@resvg/resvg-js-win32-ia32-msvc@2.6.2': + resolution: {integrity: sha512-har4aPAlvjnLcil40AC77YDIk6loMawuJwFINEM7n0pZviwMkMvjb2W5ZirsNOZY4aDbo5tLx0wNMREp5Brk+w==} + engines: {node: '>= 10'} + cpu: [ia32] + os: [win32] + + '@resvg/resvg-js-win32-x64-msvc@2.6.2': + resolution: {integrity: sha512-ZXtYhtUr5SSaBrUDq7DiyjOFJqBVL/dOBN7N/qmi/pO0IgiWW/f/ue3nbvu9joWE5aAKDoIzy/CxsY0suwGosQ==} + engines: {node: '>= 10'} + cpu: [x64] + os: [win32] + + '@resvg/resvg-js@2.6.2': + resolution: {integrity: sha512-xBaJish5OeGmniDj9cW5PRa/PtmuVU3ziqrbr5xJj901ZDN4TosrVaNZpEiLZAxdfnhAe7uQ7QFWfjPe9d9K2Q==} + engines: {node: '>= 10'} + '@rolldown/binding-android-arm64@1.0.0-rc.17': resolution: {integrity: sha512-s70pVGhw4zqGeFnXWvAzJDlvxhlRollagdCCKRgOsgUOH3N1l0LIxf83AtGzmb5SiVM4Hjl5HyarMRfdfj3DaQ==} engines: {node: ^20.19.0 || >=22.12.0} @@ -8051,6 +8130,57 @@ snapshots: react: 19.2.5 react-redux: 9.2.0(@types/react@19.2.14)(react@19.2.5)(redux@5.0.1) + '@resvg/resvg-js-android-arm-eabi@2.6.2': + optional: true + + '@resvg/resvg-js-android-arm64@2.6.2': + optional: true + + '@resvg/resvg-js-darwin-arm64@2.6.2': + optional: true + + '@resvg/resvg-js-darwin-x64@2.6.2': + optional: true + + '@resvg/resvg-js-linux-arm-gnueabihf@2.6.2': + optional: true + + '@resvg/resvg-js-linux-arm64-gnu@2.6.2': + optional: true + + '@resvg/resvg-js-linux-arm64-musl@2.6.2': + optional: true + + '@resvg/resvg-js-linux-x64-gnu@2.6.2': + optional: true + + '@resvg/resvg-js-linux-x64-musl@2.6.2': + optional: true + + '@resvg/resvg-js-win32-arm64-msvc@2.6.2': + optional: true + + '@resvg/resvg-js-win32-ia32-msvc@2.6.2': + optional: true + + '@resvg/resvg-js-win32-x64-msvc@2.6.2': + optional: true + + '@resvg/resvg-js@2.6.2': + optionalDependencies: + '@resvg/resvg-js-android-arm-eabi': 2.6.2 + '@resvg/resvg-js-android-arm64': 2.6.2 + '@resvg/resvg-js-darwin-arm64': 2.6.2 + '@resvg/resvg-js-darwin-x64': 2.6.2 + '@resvg/resvg-js-linux-arm-gnueabihf': 2.6.2 + '@resvg/resvg-js-linux-arm64-gnu': 2.6.2 + '@resvg/resvg-js-linux-arm64-musl': 2.6.2 + '@resvg/resvg-js-linux-x64-gnu': 2.6.2 + '@resvg/resvg-js-linux-x64-musl': 2.6.2 + '@resvg/resvg-js-win32-arm64-msvc': 2.6.2 + '@resvg/resvg-js-win32-ia32-msvc': 2.6.2 + '@resvg/resvg-js-win32-x64-msvc': 2.6.2 + '@rolldown/binding-android-arm64@1.0.0-rc.17': optional: true