README ultra-détaillé — captures d'écran, visite guidée, badges enrichis
10 captures Playwright prises en prod (docs/screenshots/, desktop @1600px + mobile @780px, régénérables via scripts/screenshots.mjs) : accueil, fiche+portrait, Vrai-Prix Maps, Ka, stats, page municipalité, méthodologie, mobile. Nouveau README : sommaire, visite guidée en galerie, sections Ka (12 outils), SEO programmatique (3,76 M d'URLs, sitemaps), structure du projet, endpoints complets (nearby/ka/auth), variables d'environnement, badges municipalités/URLs/Tailwind/SQLite/iOS. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
12 changed files +190 −22
modified
README.md
+138 −22
@@ -1,38 +1,85 @@ | ||
| 1 | +<div align="center"> | |
| 2 | + | |
| 1 | 3 | # Vrai-Prix |
| 2 | 4 | |
| 3 | −> **La vraie valeur de votre propriété, sans boîte noire.** | |
| 4 | −> Moteur d'évaluation immobilière transparent pour le Québec — 3,7 millions de propriétés, 745 119 ventes réelles, chaque calcul montré en entier. | |
| 5 | +**La vraie valeur de votre propriété, sans boîte noire.** | |
| 6 | + | |
| 7 | +Moteur d'évaluation immobilière transparent pour le Québec — 3,7 millions de propriétés, | |
| 8 | +745 119 ventes réelles, chaque calcul montré en entier. | |
| 5 | 9 | |
| 6 | 10 |  |
| 7 | 11 |  |
| 12 | + | |
| 8 | 13 |  |
| 9 | 14 |  |
| 10 | 15 |  |
| 16 | + | |
| 11 | 17 |  |
| 12 | 18 |  |
| 13 | 19 |  |
| 20 | + | |
| 21 | + | |
| 22 | + | |
| 14 | 23 | |
| 15 | 24 | **En production : [www.vrai-prix.com](https://www.vrai-prix.com)** |
| 16 | 25 | |
| 26 | +<img src="docs/screenshots/accueil.png" alt="Accueil Vrai-Prix — La vraie valeur, sans boîte noire" width="920"> | |
| 27 | + | |
| 28 | +</div> | |
| 29 | + | |
| 17 | 30 | --- |
| 18 | 31 | |
| 32 | +## Sommaire | |
| 33 | + | |
| 34 | +- [Ce que fait Vrai-Prix](#ce-que-fait-vrai-prix) | |
| 35 | +- [Visite guidée](#visite-guidée) | |
| 36 | +- [Les chiffres qui comptent](#les-chiffres-qui-comptent) | |
| 37 | +- [Architecture](#architecture) | |
| 38 | +- [Le moteur d'estimation](#le-moteur-destimation-srclibenginets) | |
| 39 | +- [Ka — l'agent IA](#ka--lagent-ia) | |
| 40 | +- [SEO programmatique](#seo-programmatique--376-m-durls-indexables) | |
| 41 | +- [Structure du projet](#structure-du-projet) | |
| 42 | +- [Endpoints](#endpoints) | |
| 43 | +- [Démarrage](#démarrage) | |
| 44 | +- [Design](#design) | |
| 45 | +- [Auteur](#auteur) | |
| 46 | + | |
| 19 | 47 | ## Ce que fait Vrai-Prix |
| 20 | 48 | |
| 21 | 49 | Vrai-Prix estime la valeur marchande de **n'importe quelle propriété du Québec** (unifamiliale, plex, condo, chalet, terrain) et montre *pourquoi* — chaque comparable, chaque ajustement en dollars, la pondération du modèle, la fourchette et l'indice de confiance. Le différenciateur est la **transparence totale** : pas de score magique, pas de boîte noire. |
| 22 | 50 | |
| 23 | 51 | | Fonctionnalité | Description | |
| 24 | 52 | |---|---| |
| 25 | −| 🔎 Recherche d'adresse | Plein-texte (FTS5) parmi 3 747 008 unités d'évaluation | | |
| 53 | +| 🔎 Recherche d'adresse | Plein-texte (FTS5) parmi 3 747 008 unités d'évaluation, tolérante aux fautes | | |
| 26 | 54 | | 💰 Estimation hybride | 65 % modèle hédonique + 35 % comparables ajustés | |
| 27 | 55 | | 📊 Fourchette + confiance | P10–P90 par régression quantile, indice A–D obligatoire | |
| 28 | 56 | | 🏠 Portrait de la propriété | Façade SVG dessinée par les données du registre, terrain à l'échelle, composition de la valeur | |
| 29 | −| 🗺️ Plan des comparables | Carte « plan d'arpenteur » maison (SVG pur, zéro tuile) — azimut et distance réels | | |
| 30 | −| 📈 Historique 2021→2026 | Six millésimes d'estimations par propriété | | |
| 57 | +| 🗺️ Vrai-Prix Maps | Recherche par carte 3D (Mapbox GL via le framework partagé **Ka Maps**) — grappes de valeurs estimées pilotées par le viewport | | |
| 58 | +| 📐 Plan des comparables | Carte « plan d'arpenteur » maison (SVG pur, zéro tuile) — azimut et distance réels | | |
| 59 | +| 📈 Historique 2021→2026 | Six millésimes d'estimations par propriété, badges YoY | | |
| 60 | +| 🤖 Ka · agent IA | Agent conversationnel (Claude) branché sur 12 outils serveur : évaluation, comparables, stats, dossier d'expert | | |
| 31 | 61 | | 🏢 Parc immobilier | Évaluation multi-adresses agrégée (jusqu'à 40 propriétés) + rapport consolidé | |
| 32 | 62 | | 📄 Rapports PDF | Standard (3 p.), professionnel bancaire (6 p.), parc (n+1 p.), statistique provincial — 100 % vectoriels | |
| 33 | 63 | | 📉 Statistiques | Valeur totale de la province (**2,01 billions $**), palmarès des 200 municipalités | |
| 64 | +| 🏙️ Pages municipalités | 1 097 pages programmatiques (médiane, croissance, médianes par type) + ~5 900 pages municipalité × type | | |
| 65 | +| 🔑 KA ID | Connexion unique du Groupe KA (SSO signé via le hub groupe-ka.com, session sans base d'utilisateurs) | | |
| 66 | +| 🌐 Bilingue | FR (défaut) / EN, bascule instantanée côté client | | |
| 67 | +| 📱 App iOS | Compagnon natif SwiftUI (onglet carte MapKit) — dépôt séparé | | |
| 34 | 68 | | ⚖️ Conformité | Loi 25, sélecteur de témoins, méthodologie publique intégrale | |
| 35 | 69 | |
| 70 | +## Visite guidée | |
| 71 | + | |
| 72 | +| | | | |
| 73 | +|---|---| | |
| 74 | +| **Fiche d'estimation** — valeur, fourchette honnête, écart vs rôle, indice de confiance | **Portrait de la propriété** — bâtiment, terrain à l'échelle et valeurs dessinés par les données | | |
| 75 | +| <img src="docs/screenshots/fiche-estimation.png" alt="Fiche d'estimation" width="460"> | <img src="docs/screenshots/fiche-portrait.png" alt="Portrait de la propriété" width="460"> | | |
| 76 | +| **Vrai-Prix Maps** — votre quartier, estimé (grappes de valeurs 2026) | **Ka · agent IA** — évaluation en conversant, calcul montré | | |
| 77 | +| <img src="docs/screenshots/carte.png" alt="Recherche par carte" width="460"> | <img src="docs/screenshots/ka-agent.png" alt="Agent IA Ka" width="460"> | | |
| 78 | +| **Statistiques provinciales** — 2,01 billions $, palmarès des municipalités | **Pages municipalités** — 1 097 pages programmatiques indexables | | |
| 79 | +| <img src="docs/screenshots/stats.png" alt="Statistiques provinciales" width="460"> | <img src="docs/screenshots/municipalite.png" alt="Page municipalité Gatineau" width="460"> | | |
| 80 | +| **Méthodologie publique** — 7 sections, normes IAAO, rien de caché | **Mobile** — même expérience, menu plein écran pur CSS | | |
| 81 | +| <img src="docs/screenshots/methodologie.png" alt="Méthodologie" width="460"> | <img src="docs/screenshots/accueil-mobile.png" alt="Accueil mobile" width="230"> <img src="docs/screenshots/fiche-mobile.png" alt="Fiche mobile" width="230"> | | |
| 82 | + | |
| 36 | 83 | ## Les chiffres qui comptent |
| 37 | 84 | |
| 38 | 85 | Validation sur **102 943 ventes jamais vues** du modèle (holdout aléatoire) et test temporel strict (entraîné sans 2026, testé sur 68 364 ventes de 2026), selon la **norme IAAO sur études de ratios** : |
@@ -65,7 +112,9 @@ Validation sur **102 943 ventes jamais vues** du modèle (holdout aléatoire) et | ||
| 65 | 112 | ┌─ Application Next.js 16 ─────────────────────────▼───────────────┐ |
| 66 | 113 | │ SQLite (units + FTS5 + transactions + indice de marché) │ |
| 67 | 114 | │ Moteur comparables (ajust. marché/superficie/âge, médiane │ |
| 68 | −│ pondérée) · RadarMap SVG · MetricViz · rapports pdfkit │ | |
| 115 | +│ pondérée) · Ka Maps (Mapbox GL) · agent Ka (Claude + 12 outils) │ | |
| 116 | +│ RadarMap SVG · MetricViz · rapports pdfkit · SSO KA ID │ | |
| 117 | +│ SEO programmatique (1 097 municipalités · sitemaps 3,76 M URLs) │ | |
| 69 | 118 | └──────────────────────────────────────────────────────────────────┘ |
| 70 | 119 | ``` |
| 71 | 120 | |
@@ -73,13 +122,11 @@ Validation sur **102 943 ventes jamais vues** du modèle (holdout aléatoire) et | ||
| 73 | 122 | |
| 74 | 123 | - **Frontend / API** : Next.js 16 (App Router), TypeScript strict, Tailwind CSS 4 |
| 75 | 124 | - **Base** : SQLite (better-sqlite3) — 3,75 M unités, FTS5, index spatiaux *(non incluse dans ce dépôt)* |
| 125 | +- **Cartes** : [`@groupe-ka/ka-maps`](https://github.com/spboucher-ai) (framework carto partagé du Groupe KA, moteur Mapbox GL, style Standard 3D) | |
| 126 | +- **Agent IA** : SDK Anthropic (Claude Haiku 4.5, streaming SSE, boucle agentique manuelle, prompt caching) | |
| 76 | 127 | - **Rapports** : pdfkit, 100 % vectoriels, polices embarquées (Space Grotesk / Inter / JetBrains Mono) |
| 77 | 128 | - **Modèle** (pipeline externe) : LightGBM, scikit-learn, pandas, pyogrio/GDAL |
| 78 | −- **Tests** : Vitest (moteur d'estimation) + Playwright (mobile) | |
| 79 | − | |
| 80 | −### Design | |
| 81 | − | |
| 82 | −Identité « éditorial sharp » : papier grainé `#f5f3ee`, encre `#141814`, **accent rouge vif `#ff5148`**, bordeaux `#9e2a25` — bordures encre 1,5 px, ombres décalées, display uppercase, micro-étiquettes mono. | |
| 129 | +- **Tests** : Vitest (moteur d'estimation) + Playwright (mobile, captures d'écran) | |
| 83 | 130 | |
| 84 | 131 | ## Le moteur d'estimation (`src/lib/engine.ts`) |
| 85 | 132 | |
@@ -89,19 +136,56 @@ Identité « éditorial sharp » : papier grainé `#f5f3ee`, encre `#141814`, ** | ||
| 89 | 136 | 4. **Fusion** : 65 % modèle hédonique pré-calculé + 35 % médiane pondérée des comparables ; |
| 90 | 137 | 5. **Confiance** : nombre de comparables, dispersion, largeur de fourchette → A/B/C/D. |
| 91 | 138 | |
| 92 | −## Démarrage | |
| 139 | +Chaque fiche montre le calcul en entier : les comparables retenus, chaque ajustement, les poids, la réconciliation. Le rapport PDF « professionnel » reprend la grille d'ajustement au format des évaluateurs (avec les encadrés « ceci n'est pas un rapport OEAQ »). | |
| 93 | 140 | |
| 94 | −```bash | |
| 95 | −npm install | |
| 96 | −# placer la base de données dans data/vraiprix.db (non versionnée) | |
| 97 | −npm run dev # développement | |
| 98 | −npm run test # tests du moteur (Vitest) | |
| 99 | −npm run build && npm run start | |
| 100 | −``` | |
| 141 | +## Ka — l'agent IA | |
| 101 | 142 | |
| 102 | −Variables d'environnement : `VRAIPRIX_DB` (chemin de la base SQLite), `PORT`. | |
| 143 | +Page [`/ka`](https://www.vrai-prix.com/ka) : un agent conversationnel branché en direct sur le registre et le moteur. Boucle agentique manuelle (12 tours max) avec **12 outils serveur** : `chercher_propriete` (FTS avec relaxation progressive), `evaluer_propriete`, `dossier_complet` (évaluation + marché + tendance + comparables + synthèse en un appel), `comparables_detailles`, `indice_marche`, `stats_municipalite`, `stats_provinciales`, `evaluer_parc`, `comparer_proprietes`, `estimation_manuelle`, `chercher_proprietes_secteur`, `liens_rapports` (réfs persistantes anti-hallucination pour les PDF). Streaming SSE, tableaux HTML dans le fil, clarification quand l'adresse est ambiguë. | |
| 103 | 144 | |
| 104 | −> **Note données** : la base SQLite (1,5 Go) et les pipelines d'entraînement ne sont pas versionnés. Données sources : [rôles d'évaluation foncière du Québec](https://www.donneesquebec.ca/recherche/dataset/roles-d-evaluation-fonciere-du-quebec) (MAMH, licence ouverte). Les renseignements des propriétaires sont caviardés à la source. | |
| 145 | +## SEO programmatique — 3,76 M d'URLs indexables | |
| 146 | + | |
| 147 | +Tout le contenu est servi **en HTML complet dès la première requête** (SSR), avec un maillage interne crawlable : | |
| 148 | + | |
| 149 | +- **`/municipalites`** → index A-Z des 1 097 municipalités ; | |
| 150 | +- **`/municipalite/[slug]`** (1 097 pages) → médiane 2026, valeur totale, croissance 2021→2026, médianes par type, 24 fiches liées, municipalités voisines ; | |
| 151 | +- **`/municipalite/[slug]/[type]`** (~5 900 pages) → `maison`, `plex`, `chalet`, `condo`, `terrain`, `maison-mobile`, `autre` ; | |
| 152 | +- **`/estimation/[id]`** (3,75 M fiches) → title et meta description **uniques par fiche** (adresse + estimation), canonical, JSON-LD `SingleFamilyResidence`/`Residence`/`Place` + `BreadcrumbList` ; | |
| 153 | +- **Sitemaps** : `/sitemap-index.xml` → `/sitemap.xml` (7 043 pages) + `/estimation/sitemap/0-83.xml` (45 000 fiches chacun, générés à la volée depuis SQLite par plages de rowid) ; | |
| 154 | +- JSON-LD `Organization` + `WebSite` global, `robots.txt` propre, `noindex` sur les pages privées/paramétriques. | |
| 155 | + | |
| 156 | +Les agrégats par municipalité (`src/lib/municipalites.ts`) sont calculés en SQL et mis en cache mémoire par processus — première visite ~1 s (Montréal), ensuite ~30 ms. | |
| 157 | + | |
| 158 | +## Structure du projet | |
| 159 | + | |
| 160 | +``` | |
| 161 | +src/ | |
| 162 | +├─ app/ | |
| 163 | +│ ├─ page.tsx / HomeClient.tsx # accueil (recherche + estimation manuelle) | |
| 164 | +│ ├─ estimation/[id]/ # fiche (SSR + generateMetadata + JSON-LD) | |
| 165 | +│ ├─ estimation/manuelle/ # estimation sans adresse (noindex) | |
| 166 | +│ ├─ municipalites/ # index A-Z (programmatique) | |
| 167 | +│ ├─ municipalite/[slug]/[type]/ # 1 097 + ~5 900 pages programmatiques | |
| 168 | +│ ├─ carte/ # Vrai-Prix Maps (Ka Maps / Mapbox GL) | |
| 169 | +│ ├─ ka/ # agent IA (streaming SSE) | |
| 170 | +│ ├─ parc/ # parc immobilier multi-adresses | |
| 171 | +│ ├─ stats/ # statistiques provinciales | |
| 172 | +│ ├─ compte/ # carte de membre Groupe KA (SSO) | |
| 173 | +│ ├─ methodologie|conditions|confidentialite/ | |
| 174 | +│ ├─ sitemap.ts · estimation/sitemap.ts · sitemap-index.xml/ · robots.ts | |
| 175 | +│ └─ api/ # endpoints (voir tableau) | |
| 176 | +├─ components/ # SearchBox, ResultView, MetricViz, RadarMap, | |
| 177 | +│ # KaCarte, KaChat, StatsView, LangContext… | |
| 178 | +├─ lib/ | |
| 179 | +│ ├─ engine.ts # moteur comparables (testé Vitest) | |
| 180 | +│ ├─ estimator.ts # service DB ↔ moteur | |
| 181 | +│ ├─ db.ts # SQLite (better-sqlite3, WAL) | |
| 182 | +│ ├─ municipalites.ts # agrégats programmatiques (cache mémoire) | |
| 183 | +│ ├─ seo.ts # slugs, libellés, constantes SEO | |
| 184 | +│ ├─ ka/ # agent IA (outils serveur, prompt) | |
| 185 | +│ ├─ ka-auth.ts · hub-profile.ts # SSO KA ID (JWT HS256, node:crypto pur) | |
| 186 | +│ └─ report*.ts # 4 générateurs PDF (pdfkit) | |
| 187 | +└─ data/stats.json # agrégats provinciaux précalculés | |
| 188 | +``` | |
| 105 | 189 | |
| 106 | 190 | ## Endpoints |
| 107 | 191 | |
@@ -110,14 +194,46 @@ Variables d'environnement : `VRAIPRIX_DB` (chemin de la base SQLite), `PORT`. | ||
| 110 | 194 | | `GET /api/search?q=` | Autocomplétion d'adresses (FTS5) | |
| 111 | 195 | | `GET /api/estimate?id=` | Estimation complète d'une unité | |
| 112 | 196 | | `POST /api/estimate` | Estimation manuelle (municipalité + caractéristiques) | |
| 197 | +| `GET /api/nearby?lat&lng&halfLat&halfLng` | Unités dans une boîte englobante (carte) | | |
| 198 | +| `POST /api/ka` | Agent IA (streaming SSE, boucle d'outils) | | |
| 113 | 199 | | `POST /api/portfolio` | Agrégation d'un parc (jusqu'à 40 ids) | |
| 114 | 200 | | `GET /api/report?id=` | Rapport PDF standard (3 pages) | |
| 115 | 201 | | `GET /api/report/pro?id=` | Rapport détaillé format professionnel (6 pages) | |
| 116 | 202 | | `GET /api/report/portfolio?ids=` | Rapport de parc consolidé | |
| 117 | 203 | | `GET /api/report/stats` | Rapport statistique provincial | |
| 204 | +| `GET /api/auth/ka/login` · `/callback` | SSO KA ID (hub groupe-ka.com) | | |
| 205 | +| `GET /api/auth/me` · `POST /api/auth/logout` | Session (cookie signé, sans table users) | | |
| 206 | + | |
| 207 | +## Démarrage | |
| 208 | + | |
| 209 | +```bash | |
| 210 | +npm install | |
| 211 | +# placer la base de données dans data/vraiprix.db (non versionnée) | |
| 212 | +npm run dev # développement | |
| 213 | +npm run test # tests du moteur (Vitest) | |
| 214 | +npm run build && npm run start | |
| 215 | +``` | |
| 216 | + | |
| 217 | +### Variables d'environnement | |
| 218 | + | |
| 219 | +| Variable | Rôle | | |
| 220 | +|---|---| | |
| 221 | +| `VRAIPRIX_DB` | Chemin de la base SQLite (défaut : `data/vraiprix.db`) | | |
| 222 | +| `NEXT_PUBLIC_SITE_URL` | URL canonique (défaut : `https://www.vrai-prix.com`) | | |
| 223 | +| `ANTHROPIC_API_KEY` | Agent IA Ka (page `/ka`) | | |
| 224 | +| `NEXT_PUBLIC_MAPBOX_TOKEN` | Vrai-Prix Maps (page `/carte`) | | |
| 225 | +| `KA_SSO_SECRET` · `KA_AUTH_SECRET` · `KA_HUB_URL` · `KA_BASE_URL` | SSO KA ID | | |
| 226 | + | |
| 227 | +> **Note données** : la base SQLite (1,5 Go) et les pipelines d'entraînement ne sont pas versionnés. Données sources : [rôles d'évaluation foncière du Québec](https://www.donneesquebec.ca/recherche/dataset/roles-d-evaluation-fonciere-du-quebec) (MAMH, licence ouverte). Les renseignements des propriétaires sont caviardés à la source. | |
| 228 | + | |
| 229 | +> **Captures d'écran** : `node scripts/screenshots.mjs` les régénère depuis la prod (Playwright). | |
| 230 | + | |
| 231 | +## Design | |
| 232 | + | |
| 233 | +Identité « éditorial sharp » : papier grainé `#f5f3ee`, encre `#141814`, **accent rouge vif `#ff5148`**, bordeaux `#9e2a25` — bordures encre 1,5 px, ombres décalées, display uppercase (Space Grotesk), micro-étiquettes mono (JetBrains Mono), ticker marquee, visualisations SVG data-driven (façade du bâtiment, terrain à l'échelle, jauge de confiance, sparklines) — **zéro image bitmap dans l'app**. | |
| 118 | 234 | |
| 119 | 235 | ## Auteur |
| 120 | 236 | |
| 121 | −**Simon-Pierre Boucher** — [contact@spboucher.ai](mailto:contact@spboucher.ai) | |
| 237 | +**Simon-Pierre Boucher** — [contact@spboucher.ai](mailto:contact@spboucher.ai) · Groupe KA | |
| 122 | 238 | |
| 123 | 239 | *Estimations statistiques à titre indicatif — ne remplacent pas une évaluation par un évaluateur agréé (OEAQ). Voir [conditions](https://www.vrai-prix.com/conditions) et [méthodologie complète](https://www.vrai-prix.com/methodologie).* |
added
docs/screenshots/accueil-mobile.png
+0 −0
Binary file not shown.
added
docs/screenshots/accueil.png
+0 −0
Binary file not shown.
added
docs/screenshots/carte.png
+0 −0
Binary file not shown.
added
docs/screenshots/fiche-estimation.png
+0 −0
Binary file not shown.
added
docs/screenshots/fiche-mobile.png
+0 −0
Binary file not shown.
added
docs/screenshots/fiche-portrait.png
+0 −0
Binary file not shown.
added
docs/screenshots/ka-agent.png
+0 −0
Binary file not shown.
added
docs/screenshots/methodologie.png
+0 −0
Binary file not shown.
added
docs/screenshots/municipalite.png
+0 −0
Binary file not shown.
added
docs/screenshots/stats.png
+0 −0
Binary file not shown.
added
scripts/screenshots.mjs
+52 −0
@@ -0,0 +1,52 @@ | ||
| 1 | +// Captures d'écran du site en prod pour le README (jetable). | |
| 2 | +import { chromium } from "playwright"; | |
| 3 | +import { mkdirSync } from "fs"; | |
| 4 | + | |
| 5 | +const OUT = "docs/screenshots"; | |
| 6 | +mkdirSync(OUT, { recursive: true }); | |
| 7 | +const BASE = "https://www.vrai-prix.com"; | |
| 8 | +const FICHE = "/estimation/66023993631880550000000"; | |
| 9 | + | |
| 10 | +const SHOTS = [ | |
| 11 | + { path: "/", name: "accueil", wait: 2500 }, | |
| 12 | + { path: FICHE, name: "fiche-estimation", wait: 3500 }, | |
| 13 | + { path: FICHE, name: "fiche-portrait", wait: 3500, scrollTo: 1800 }, | |
| 14 | + { path: "/carte", name: "carte", wait: 9000 }, | |
| 15 | + { path: "/stats", name: "stats", wait: 3200 }, | |
| 16 | + { path: "/ka", name: "ka-agent", wait: 2500 }, | |
| 17 | + { path: "/municipalite/gatineau", name: "municipalite", wait: 2000 }, | |
| 18 | + { path: "/methodologie", name: "methodologie", wait: 2000 }, | |
| 19 | + { path: "/", name: "accueil-mobile", wait: 2500, mobile: true }, | |
| 20 | + { path: FICHE, name: "fiche-mobile", wait: 3500, mobile: true }, | |
| 21 | +]; | |
| 22 | + | |
| 23 | +const browser = await chromium.launch(); | |
| 24 | +for (const s of SHOTS) { | |
| 25 | + const ctx = await browser.newContext({ | |
| 26 | + viewport: s.mobile ? { width: 390, height: 844 } : { width: 1440, height: 900 }, | |
| 27 | + deviceScaleFactor: 2, | |
| 28 | + locale: "fr-CA", | |
| 29 | + isMobile: !!s.mobile, | |
| 30 | + }); | |
| 31 | + // masque le bandeau de témoins (consentement déjà donné) | |
| 32 | + await ctx.addInitScript(() => { | |
| 33 | + try { | |
| 34 | + localStorage.setItem( | |
| 35 | + "vp-cookie-consent", | |
| 36 | + JSON.stringify({ necessary: true, analytics: false, ts: 1755000000000 }) | |
| 37 | + ); | |
| 38 | + localStorage.setItem("vp-lang", "fr"); | |
| 39 | + } catch {} | |
| 40 | + }); | |
| 41 | + const page = await ctx.newPage(); | |
| 42 | + await page.goto(BASE + s.path, { waitUntil: "networkidle", timeout: 60000 }).catch(() => {}); | |
| 43 | + await page.waitForTimeout(s.wait); | |
| 44 | + if (s.scrollTo) { | |
| 45 | + await page.evaluate((y) => window.scrollTo({ top: y, behavior: "instant" }), s.scrollTo); | |
| 46 | + await page.waitForTimeout(1200); | |
| 47 | + } | |
| 48 | + await page.screenshot({ path: `${OUT}/${s.name}.png` }); | |
| 49 | + console.log("✓", s.name); | |
| 50 | + await ctx.close(); | |
| 51 | +} | |
| 52 | +await browser.close(); | |
| 53 | ||