[{"data":1,"prerenderedAt":1277},["ShallowReactive",2],{"i-lucide:menu":3,"profil":8,"profil-footer":116,"i-simple-icons:github":140,"i-simple-icons:linkedin":142,"i-lucide:mail":145,"projets-tous":147,"i-lucide:layout-dashboard":1269,"i-lucide:server":1271,"i-lucide:database":1273,"i-lucide:arrow-right":1275},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":7},0,24,false,"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M4 5h16M4 12h16M4 19h16\"\u002F>",{"id":9,"accroche":10,"apropos":11,"competences":16,"cv":77,"disponibilite":78,"email":79,"extension":80,"localisation":81,"meta":82,"nom":83,"parcours":84,"reseaux":100,"stem":113,"titre":114,"__hash__":115},"profil\u002Fprofil.yml","Je conçois et développe des applications web de bout en bout — de la base de données au pixel. Vue \u002F Nuxt côté interface, Python et Node côté serveur, Docker et VPS pour la mise en production.",[12,13,14,15],"Développeur full-stack, je construis des produits complets plutôt que des morceaux de produits : modèle de données, API, interface, déploiement et supervision. C'est cette vue d'ensemble qui m'intéresse — comprendre comment une décision de schéma se répercute jusqu'à l'expérience utilisateur.","Mes projets personnels tournent souvent autour de la donnée géographique et du temps réel : cartographie de feux de forêt à partir de données satellitaires, fusion de sources publiques immobilières, suivi de satellites en orbite. J'aime les sujets où la donnée brute est ingrate et où le travail consiste à la rendre lisible.","Je travaille en Docker de bout en bout, j'héberge sur mes propres serveurs, et je documente ce que je construis — parce qu'un projet qu'on ne peut pas reprendre six mois plus tard n'est pas vraiment terminé.","À COMPLÉTER — ajoute ici un paragraphe plus personnel : ce qui t'a amené au développement, ce que tu cherches aujourd'hui, tes centres d'intérêt hors code.",[17,28,39,50,61,68],{"categorie":18,"icon":19,"items":20},"Front-end","lucide:layout-dashboard",[21,22,23,24,25,26,27],"Vue 3","Nuxt","TypeScript","Pinia","Tailwind CSS","Vuetify","MapLibre GL",{"categorie":29,"icon":30,"items":31},"Back-end","lucide:server",[32,33,34,35,36,37,38],"Python","FastAPI","AdonisJS","Node.js","REST","WebSocket","Server-Sent Events",{"categorie":40,"icon":41,"items":42},"Données","lucide:database",[43,44,45,46,47,48,49],"PostgreSQL","PostGIS","Redis","SQLite","SQLAlchemy","Drizzle","Celery",{"categorie":51,"icon":52,"items":53},"Infrastructure","lucide:container",[54,55,56,57,58,59,60],"Docker","Docker Compose","nginx","Caddy","GitHub Actions","VPS","Raspberry Pi",{"categorie":62,"icon":63,"items":64},"Mobile","lucide:smartphone",[65,66,67],"SwiftUI","SwiftData","iOS",{"categorie":69,"icon":70,"items":71},"Méthode","lucide:git-branch",[72,73,74,75,76],"Git","GitHub Flow","Conventional Commits","Tests","Documentation","","Ouvert aux opportunités","rs.szmygiel@gmail.com","yml","France",{},"Ronan Szmygiel",[85,91,95],{"periode":86,"titre":87,"organisation":88,"type":89,"description":90},"2024 — aujourd'hui","À COMPLÉTER — intitulé du poste","À COMPLÉTER — entreprise","experience","À COMPLÉTER — deux ou trois lignes sur le périmètre, la stack et un résultat concret (une migration menée, une fonctionnalité livrée, un gain mesurable).",{"periode":92,"titre":93,"organisation":88,"type":89,"description":94},"2022 — 2024","À COMPLÉTER — poste précédent","À COMPLÉTER",{"periode":96,"titre":97,"organisation":98,"type":99,"description":94},"2022","À COMPLÉTER — diplôme","À COMPLÉTER — école","formation",[101,105,109],{"nom":102,"url":103,"icon":104},"GitHub","https:\u002F\u002Fgithub.com\u002Fnanro22","simple-icons:github",{"nom":106,"url":107,"icon":108},"LinkedIn","https:\u002F\u002Fwww.linkedin.com\u002Fin\u002F","simple-icons:linkedin",{"nom":110,"url":111,"icon":112},"Email","mailto:rs.szmygiel@gmail.com","lucide:mail","profil","Développeur full-stack","FDM5PBLsVY9oE5BCeIZ1gApbAvUzQFUsJHJ9FVK7has",{"id":9,"accroche":10,"apropos":117,"competences":118,"cv":77,"disponibilite":78,"email":79,"extension":80,"localisation":81,"meta":131,"nom":83,"parcours":132,"reseaux":136,"stem":113,"titre":114,"__hash__":115},[12,13,14,15],[119,121,123,125,127,129],{"categorie":18,"icon":19,"items":120},[21,22,23,24,25,26,27],{"categorie":29,"icon":30,"items":122},[32,33,34,35,36,37,38],{"categorie":40,"icon":41,"items":124},[43,44,45,46,47,48,49],{"categorie":51,"icon":52,"items":126},[54,55,56,57,58,59,60],{"categorie":62,"icon":63,"items":128},[65,66,67],{"categorie":69,"icon":70,"items":130},[72,73,74,75,76],{},[133,134,135],{"periode":86,"titre":87,"organisation":88,"type":89,"description":90},{"periode":92,"titre":93,"organisation":88,"type":89,"description":94},{"periode":96,"titre":97,"organisation":98,"type":99,"description":94},[137,138,139],{"nom":102,"url":103,"icon":104},{"nom":106,"url":107,"icon":108},{"nom":110,"url":111,"icon":112},{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":141},"\u003Cpath fill=\"currentColor\" d=\"M12 .297c-6.63 0-12 5.373-12 12c0 5.303 3.438 9.8 8.205 11.385c.6.113.82-.258.82-.577c0-.285-.01-1.04-.015-2.04c-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729c1.205.084 1.838 1.236 1.838 1.236c1.07 1.835 2.809 1.305 3.495.998c.108-.776.417-1.305.76-1.605c-2.665-.3-5.466-1.332-5.466-5.93c0-1.31.465-2.38 1.235-3.22c-.135-.303-.54-1.523.105-3.176c0 0 1.005-.322 3.3 1.23c.96-.267 1.98-.399 3-.405c1.02.006 2.04.138 3 .405c2.28-1.552 3.285-1.23 3.285-1.23c.645 1.653.24 2.873.12 3.176c.765.84 1.23 1.91 1.23 3.22c0 4.61-2.805 5.625-5.475 5.92c.42.36.81 1.096.81 2.22c0 1.606-.015 2.896-.015 3.286c0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12\"\u002F>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":143,"hidden":144},"\u003Cpath fill=\"currentColor\" d=\"M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037c-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85c3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433a2.06 2.06 0 0 1-2.063-2.065a2.064 2.064 0 1 1 2.063 2.065m1.782 13.019H3.555V9h3.564zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0z\"\u002F>",true,{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":146},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cpath d=\"m22 7l-8.991 5.727a2 2 0 0 1-2.009 0L2 7\"\u002F>\u003Crect width=\"20\" height=\"16\" x=\"2\" y=\"4\" rx=\"2\"\u002F>\u003C\u002Fg>",[148,278,405,480,539,638,698,845,937,990,1124],{"id":149,"title":150,"body":151,"cover":77,"demo":77,"description":260,"extension":261,"featured":6,"highlights":262,"meta":266,"navigation":144,"path":267,"rang":268,"repo":77,"role":269,"seo":270,"status":271,"stem":272,"tags":273,"year":276,"__hash__":277},"projets\u002Fprojets\u002Fadele.md","Adele",{"type":152,"value":153,"toc":254},"minimark",[154,159,163,167,175,241,244,248,251],[155,156,158],"h2",{"id":157},"le-projet","Le projet",[160,161,162],"p",{},"Une liste de naissance en ligne, doublée d'un jeu de pronostics : les proches réservent\nun cadeau et parient sur la date de naissance, le poids et le prénom.",[155,164,166],{"id":165},"le-choix-technique","Le choix technique",[160,168,169,170,174],{},"Le point intéressant de ce projet est son ",[171,172,173],"strong",{},"dimensionnement",". Une liste de naissance,\nc'est quelques dizaines d'utilisateurs sur quelques mois. Y déployer PostgreSQL, un\ncache Redis et une infrastructure séparée aurait été absurde.",[176,177,178,191],"table",{},[179,180,181],"thead",{},[182,183,184,188],"tr",{},[185,186,187],"th",{},"Couche",[185,189,190],{},"Choix",[192,193,194,203,211,218,225,233],"tbody",{},[182,195,196,200],{},[197,198,199],"td",{},"Framework",[197,201,202],{},"Nuxt 3 (Vue 3 + Nitro)",[182,204,205,208],{},[197,206,207],{},"Base",[197,209,210],{},"SQLite via better-sqlite3",[182,212,213,216],{},[197,214,215],{},"ORM",[197,217,48],{},[182,219,220,223],{},[197,221,222],{},"CSS",[197,224,25],{},[182,226,227,230],{},[197,228,229],{},"Runtime",[197,231,232],{},"Node.js 22",[182,234,235,238],{},[197,236,237],{},"Déploiement",[197,239,240],{},"Docker + Docker Compose",[160,242,243],{},"SQLite embarqué signifie : un seul conteneur, une sauvegarde qui tient dans un fichier à\ncopier, et zéro latence réseau sur les requêtes. Nitro sert le rendu serveur et l'API dans\nle même processus.",[155,245,247],{"id":246},"expérience-invité","Expérience invité",[160,249,250],{},"Aucune inscription. Les invités arrivent par un lien, voient ce qui est déjà réservé, et\nréservent en une action. Imposer la création d'un compte à des grands-parents pour\nréserver un cadeau, c'est garantir qu'ils appelleront au téléphone à la place.",[160,252,253],{},"Côté serveur, la réservation est atomique — deux personnes qui cliquent en même temps sur\nle dernier exemplaire d'un cadeau ne peuvent pas le réserver toutes les deux.",{"title":77,"searchDepth":255,"depth":255,"links":256},2,[257,258,259],{"id":157,"depth":255,"text":158},{"id":165,"depth":255,"text":166},{"id":246,"depth":255,"text":247},"Application web de liste de naissance avec système de pronostics, pensée pour être partagée simplement avec les proches.","md",[263,264,265],"Réservation de cadeaux sans création de compte pour les invités","Jeu de pronostics — date, poids, prénom","Base SQLite embarquée, déploiement en un seul conteneur",{},"\u002Fprojets\u002Fadele",11,"Conception et développement full-stack",{"title":150,"description":260},"Terminé","projets\u002Fadele",[274,275,46,25,54],"Nuxt 3","Drizzle ORM",2024,"a2IxeV0BucP6Hy8HG-3LMHecfza9ZoJXA1KTkX0K52I",{"id":279,"title":280,"body":281,"cover":77,"demo":77,"description":389,"extension":261,"featured":6,"highlights":390,"meta":394,"navigation":144,"path":395,"rang":396,"repo":77,"role":397,"seo":398,"status":399,"stem":400,"tags":401,"year":403,"__hash__":404},"projets\u002Fprojets\u002Farona.md","Arona",{"type":152,"value":282,"toc":384},[283,285,288,292,335,338,359,363,381],[155,284,158],{"id":157},[160,286,287],{},"Arona est une application de mixage audio et voix : on dépose des fichiers, on les\nassemble, on récupère un rendu. Ce qui semble simple côté interface implique côté serveur\nune chaîne de traitement complète — validation des formats, transcodage, assemblage,\nstockage des rendus.",[155,289,291],{"id":290},"architecture","Architecture",[176,293,294,302],{},[179,295,296],{},[182,297,298,300],{},[185,299,187],{},[185,301,190],{},[192,303,304,312,320,327],{},[182,305,306,309],{},[197,307,308],{},"Frontend",[197,310,311],{},"Nuxt 3, TypeScript",[182,313,314,317],{},[197,315,316],{},"Backend",[197,318,319],{},"Python 3.12, FastAPI, migrations Alembic",[182,321,322,324],{},[197,323,207],{},[197,325,326],{},"PostgreSQL 16",[182,328,329,332],{},[197,330,331],{},"Stockage",[197,333,334],{},"MinIO (objets audio)",[160,336,337],{},"Les fichiers audio ne transitent jamais par la base. MinIO reçoit les binaires, Postgres\ngarde les métadonnées et les références — cette séparation garde les sauvegardes de base\nlégères et permet de faire grossir le stockage indépendamment.",[160,339,340,341,345,346,345,349,345,352,345,355,358],{},"L'ensemble tourne en conteneurs : ",[342,343,344],"code",{},"db",", ",[342,347,348],{},"minio",[342,350,351],{},"back",[342,353,354],{},"front",[342,356,357],{},"back_storage",", avec\ntrois fichiers compose distincts pour le développement, la préproduction et la production.",[155,360,362],{"id":361},"qualité","Qualité",[160,364,365,366,369,370,373,374,373,377,380],{},"Le projet applique une CI reproductible en local comme sur le serveur : ",[342,367,368],{},"make ci-local","\nenchaîne ",[342,371,372],{},"lint"," → ",[342,375,376],{},"typecheck",[342,378,379],{},"test",", avec Biome et nuxi côté front, Ruff et mypy côté\nback. Les trois étapes doivent passer avant tout merge.",[160,382,383],{},"Pouvoir rejouer exactement la CI en local avant de pousser fait gagner beaucoup plus de\ntemps que l'inverse — attendre le retour d'un runner pour découvrir une erreur de typage.",{"title":77,"searchDepth":255,"depth":255,"links":385},[386,387,388],{"id":157,"depth":255,"text":158},{"id":290,"depth":255,"text":291},{"id":361,"depth":255,"text":362},"Application de mixage audio et voix, avec traitement des fichiers côté serveur et stockage objet dédié.",[391,392,393],"Stockage objet MinIO découplé de la base applicative","Pipeline CI complet — lint, typecheck et tests sur le front comme sur le back","Trois environnements isolés — développement, préproduction, production",{},"\u002Fprojets\u002Farona",4,"Développement full-stack",{"title":280,"description":389},"En production","projets\u002Farona",[274,23,33,32,43,402],"MinIO",2025,"fOc6xEbIiSlNhx5l2rmU5BtJwvfRCTEYr1KfMXMSK3o",{"id":406,"title":407,"body":408,"cover":77,"demo":77,"description":468,"extension":261,"featured":6,"highlights":469,"meta":473,"navigation":144,"path":474,"rang":475,"repo":77,"role":269,"seo":476,"status":271,"stem":477,"tags":478,"year":403,"__hash__":479},"projets\u002Fprojets\u002Fcabinya.md","Cabinya",{"type":152,"value":409,"toc":462},[410,412,415,417,426,429,433,436,439,443],[155,411,158],{"id":157},[160,413,414],{},"Cabinya gère les plannings de travail et les demandes de congés de plusieurs\norganisations sur une même instance. Chaque organisation dispose de ses utilisateurs, de\nses règles, de ses rôles et de sa propre apparence — logo et palette de couleurs.",[155,416,291],{"id":290},[418,419,424],"pre",{"className":420,"code":422,"language":423},[421],"language-text","Navigateur — Vue 3 + Vuetify 3\n        ↓ HTTP\nFastAPI (Uvicorn) — Swagger UI intégrée\n        ↓ SQLAlchemy\nPostgreSQL 16\n","text",[342,425,422],{"__ignoreMap":77},[160,427,428],{},"Le tout est orchestré par docker-compose : un conteneur par couche, une commande pour\ndémarrer l'ensemble.",[155,430,432],{"id":431},"le-point-délicat","Le point délicat",[160,434,435],{},"Le multi-tenant est facile à faire fonctionner et difficile à rendre sûr. Chaque requête\ndoit être filtrée par organisation, et une seule requête qui oublie ce filtre suffit à\nexposer les données d'un client à un autre.",[160,437,438],{},"La réponse a été de porter le filtrage au niveau de la couche d'accès aux données plutôt\nque de le laisser à la charge de chaque endpoint : le contexte d'organisation est résolu\nà l'authentification, puis appliqué systématiquement — plutôt que rappelé à chaque\nrequête, où il finit forcément par être oublié.",[155,440,442],{"id":441},"fonctionnalités","Fonctionnalités",[444,445,446,450,453,456,459],"ul",{},[447,448,449],"li",{},"Plannings de travail par organisation, par équipe et par personne",[447,451,452],{},"Demandes de congés avec circuit de validation",[447,454,455],{},"Rôles et permissions granulaires",[447,457,458],{},"Personnalisation de l'apparence par organisation",[447,460,461],{},"API REST documentée automatiquement (Swagger)",{"title":77,"searchDepth":255,"depth":255,"links":463},[464,465,466,467],{"id":157,"depth":255,"text":158},{"id":290,"depth":255,"text":291},{"id":431,"depth":255,"text":432},{"id":441,"depth":255,"text":442},"Application web multi-tenant de gestion RH — plannings de travail, congés, rôles et apparence personnalisable par organisation.",[470,471,472],"Multi-tenant avec isolation des données par organisation","Gestion fine des rôles et des permissions","Thème et apparence personnalisables par organisation",{},"\u002Fprojets\u002Fcabinya",5,{"title":407,"description":468},"projets\u002Fcabinya",[21,26,33,32,43,54],"ZLbfUsI2VDALib119YtjsjfBv4gTiecW_8EN1rHrrsM",{"id":481,"title":482,"body":483,"cover":77,"demo":77,"description":527,"extension":261,"featured":6,"highlights":528,"meta":532,"navigation":144,"path":533,"rang":534,"repo":77,"role":269,"seo":535,"status":271,"stem":536,"tags":537,"year":276,"__hash__":538},"projets\u002Fprojets\u002Fcatan.md","Catan",{"type":152,"value":484,"toc":522},[485,487,490,492,498,501,504,508,519],[155,486,158],{"id":157},[160,488,489],{},"Une version numérique de Catan, jouable en multijoueur dans le navigateur : plateau\ngénéré aléatoirement, placement des colonies et des routes, production de ressources,\néchanges entre joueurs, voleur et décompte des points de victoire.",[155,491,291],{"id":290},[160,493,494,497],{},[171,495,496],{},"Stack"," : Vue 3 · FastAPI · PostgreSQL · WebSocket",[160,499,500],{},"L'état de la partie vit sur le serveur, jamais dans le client. Chaque action envoyée par\nun joueur est validée côté serveur — est-ce son tour, a-t-il les ressources, l'emplacement\nest-il légal — avant d'être appliquée puis rediffusée à tous les participants.",[160,502,503],{},"C'est la seule architecture défendable pour un jeu multijoueur : un client qui calcule\nlui-même ce qu'il a le droit de faire est un client qu'on peut modifier.",[155,505,507],{"id":506},"ce-que-le-jeu-apprend","Ce que le jeu apprend",[160,509,510,511,514,515,518],{},"Catan est un excellent exercice de modélisation. Le plateau est un pavage hexagonal où\nles colonies se placent sur les ",[171,512,513],{},"sommets"," et les routes sur les ",[171,516,517],{},"arêtes"," — trois\nsystèmes de coordonnées cohabitent donc, et la moitié du travail consiste à naviguer\nproprement entre eux : quelles tuiles touchent ce sommet, quelles arêtes partent d'ici,\nquels sommets sont trop proches pour accueillir une nouvelle colonie.",[160,520,521],{},"Une fois cette géométrie posée correctement, les règles du jeu deviennent presque\ntriviales à implémenter. Posée de travers, chaque règle devient un cas particulier.",{"title":77,"searchDepth":255,"depth":255,"links":523},[524,525,526],{"id":157,"depth":255,"text":158},{"id":290,"depth":255,"text":291},{"id":506,"depth":255,"text":507},"Adaptation web multijoueur du jeu de société Catan, jouable à plusieurs dans un navigateur en temps réel.",[529,530,531],"Partie multijoueur synchronisée en temps réel par WebSocket","Règles du jeu et validation des coups entièrement côté serveur","Démarrage complet en une commande docker compose",{},"\u002Fprojets\u002Fcatan",7,{"title":482,"description":527},"projets\u002Fcatan",[21,33,32,43,37,54],"ZV64zE21AiNpSjVcGS72-ez4qyLAENIqpY3b9PHdrtM",{"id":540,"title":541,"body":542,"cover":77,"demo":77,"description":624,"extension":261,"featured":144,"highlights":625,"meta":629,"navigation":144,"path":630,"rang":631,"repo":77,"role":269,"seo":632,"status":633,"stem":634,"tags":635,"year":636,"__hash__":637},"projets\u002Fprojets\u002Fdatabridge-supermap.md","DataBridge SuperMap",{"type":152,"value":543,"toc":617},[544,548,551,554,558,561,563,586,597,604,607,610,614],[155,545,547],{"id":546},"le-problème","Le problème",[160,549,550],{},"Les données immobilières françaises sont publiques et gratuites. Elles sont aussi\néparpillées : les transactions dans DVF, la géométrie des parcelles dans le cadastre, les\ncaractéristiques du bâti dans la BDNB, la performance énergétique dans les DPE, la\ndémographie à l'INSEE, l'exposition aux risques ailleurs encore.",[160,552,553],{},"Chacune a son format, sa granularité et son identifiant pivot. Croiser « le prix au m²\ndes maisons construites avant 1975 avec un DPE F ou G dans un secteur inondable » demande\ndonc un travail de fusion préalable considérable.",[155,555,557],{"id":556},"lobjectif-produit","L'objectif produit",[160,559,560],{},"Un moteur d'estimation de prix immobilier — la carte est le support d'exploration, mais\nl'objectif est de nourrir un modèle avec des caractéristiques réellement croisées.",[155,562,291],{"id":290},[444,564,565,570,575,580],{},[447,566,567,569],{},[171,568,308],{}," : Nuxt 3 + MapLibre GL — exploration cartographique, filtres, backoffice",[447,571,572,574],{},[171,573,316],{}," : FastAPI + SQLAlchemy, avec Celery pour les traitements longs",[447,576,577,579],{},[171,578,40],{}," : PostgreSQL \u002F PostGIS pour le spatial, Redis comme broker",[447,581,582,585],{},[171,583,584],{},"Infra"," : docker-compose, workers séparés par file d'attente",[160,587,588,589,592,593,596],{},"Les workers sont scindés en deux files : ",[342,590,591],{},"imports"," pour l'ingestion des sources, et\n",[342,594,595],{},"mutations"," pour les rebuilds, l'enrichissement, la génération de tuiles et les\ntraitements ML. Cette séparation évite qu'un import de plusieurs heures bloque un\nrecalcul de tuiles de quelques minutes.",[160,598,599,600,603],{},"Un backoffice ",[342,601,602],{},"\u002Fadmin"," pilote les imports et suit l'état des jobs.",[155,605,69],{"id":606},"méthode",[160,608,609],{},"Le projet s'appuie sur une documentation en dix fichiers — architecture, sources, modèle\nde données, pipelines, API, frontend, backoffice, runbook, roadmap — tenue à jour à\nchaque évolution significative. Le runbook consigne notamment les pièges d'infrastructure\nappris à la dure, ce qui évite de les réapprendre.",[155,611,613],{"id":612},"ce-que-jen-retire","Ce que j'en retire",[160,615,616],{},"Sur ce type de projet, la difficulté n'est jamais l'algorithme : c'est la réconciliation\nd'identifiants entre référentiels qui ne se sont jamais parlé. Une parcelle cadastrale et\nun bâtiment BDNB ne partagent pas de clé — il faut la reconstruire spatialement, et\naccepter un taux d'appariement imparfait.",{"title":77,"searchDepth":255,"depth":255,"links":618},[619,620,621,622,623],{"id":546,"depth":255,"text":547},{"id":556,"depth":255,"text":557},{"id":290,"depth":255,"text":291},{"id":606,"depth":255,"text":69},{"id":612,"depth":255,"text":613},"Carte interactive de France fusionnant les données publiques immobilières — DVF, cadastre, BDNB, DPE, INSEE, risques — au service d'un moteur d'estimation de prix.",[626,627,628],"Sept sources de données publiques fusionnées sur un référentiel commun","Pipelines d'import asynchrones supervisés depuis un backoffice","Tuiles vectorielles générées à la volée pour la France entière",{},"\u002Fprojets\u002Fdatabridge-supermap",3,{"title":541,"description":624},"En développement","projets\u002Fdatabridge-supermap",[274,27,33,49,44,45],2026,"GrJngFMxZYiNhAtdhpJRHDrZUvXD5nNUiq5luecJzys",{"id":639,"title":640,"body":641,"cover":77,"demo":77,"description":685,"extension":261,"featured":6,"highlights":686,"meta":690,"navigation":144,"path":691,"rang":692,"repo":77,"role":269,"seo":693,"status":399,"stem":694,"tags":695,"year":403,"__hash__":697},"projets\u002Fprojets\u002Fglobe-satellites.md","Globe Satellites",{"type":152,"value":642,"toc":680},[643,645,648,651,653,659,666,669,673],[155,644,158],{"id":157},[160,646,647],{},"Plusieurs milliers de satellites actifs tournent au-dessus de nos têtes, et leurs orbites\nsont publiques. Celestrak diffuse les éléments orbitaux au format TLE (Two-Line Element),\nqui suffisent à calculer la position de n'importe quel objet à n'importe quel instant.",[160,649,650],{},"Globe Satellites affiche ce ballet sur un globe 3D, actualisé toutes les cinq secondes.",[155,652,291],{"id":290},[418,654,657],{"className":655,"code":656,"language":423},[421],"Navigateur — Globe 3D (Globe.gl)\n      ↕ WebSocket\nFrontend nginx — fichiers statiques Vue 3, proxy \u002Fws\n      ↓ réseau Docker\nBackend FastAPI — propagation orbitale (Skyfield)\n",[342,658,656],{"__ignoreMap":77},[160,660,661,662,665],{},"Le choix structurant est de ",[171,663,664],{},"calculer côté serveur",". La propagation orbitale SGP4 pour\nplusieurs milliers d'objets, cinq fois par minute, n'est pas un travail pour le\nnavigateur — d'autant que tous les clients connectés partagent exactement le même\nrésultat. Le serveur calcule une fois, diffuse à tout le monde par WebSocket, et le\nclient ne fait que du rendu.",[160,667,668],{},"Les TLE sont rafraîchis périodiquement depuis Celestrak : ils se dégradent en précision\nau fil des jours, une propagation sur des éléments vieux d'une semaine dérive\nsensiblement.",[155,670,672],{"id":671},"contrainte-matérielle","Contrainte matérielle",[160,674,675,676,679],{},"Le projet est pensé pour un ",[171,677,678],{},"Raspberry Pi 4 en ARM64",". Cette contrainte a orienté\nplusieurs décisions : image Docker multi-architecture, calculs vectorisés avec NumPy\nplutôt qu'en boucle Python, et diffusion d'un état partagé unique plutôt que d'un calcul\npar client connecté.",{"title":77,"searchDepth":255,"depth":255,"links":681},[682,683,684],{"id":157,"depth":255,"text":158},{"id":290,"depth":255,"text":291},{"id":671,"depth":255,"text":672},"Globe 3D interactif affichant en temps réel la position des satellites actifs en orbite terrestre, conçu pour tourner sur un Raspberry Pi.",[687,688,689],"Positions recalculées toutes les 5 secondes à partir des données TLE Celestrak","Propagation orbitale avec Skyfield côté serveur, rendu WebGL côté client","Image ARM64 tournant en continu sur un Raspberry Pi 4",{},"\u002Fprojets\u002Fglobe-satellites",6,{"title":640,"description":685},"projets\u002Fglobe-satellites",[21,696,33,32,37,54,60],"Globe.gl","sQHE4eQG0PICz3ZGP-gwCdw7f7lNa2UYxZzey9QvRMg",{"id":699,"title":700,"body":701,"cover":77,"demo":77,"description":833,"extension":261,"featured":144,"highlights":834,"meta":838,"navigation":144,"path":839,"rang":255,"repo":77,"role":269,"seo":840,"status":633,"stem":841,"tags":842,"year":636,"__hash__":844},"projets\u002Fprojets\u002Fpyro-radar.md","Pyro Radar",{"type":152,"value":702,"toc":827},[703,707,710,713,717,785,788,792,798,804,810,816,822,824],[155,704,706],{"id":705},"lidée","L'idée",[160,708,709],{},"Les données de détection d'incendies sont publiques, gratuites et abondantes — la NASA,\nl'ESA, la NOAA et la JAXA les diffusent en continu. Elles sont aussi diffusées dans des\nformats hétérogènes, à des cadences différentes, avec des couvertures géographiques qui\nse recouvrent partiellement.",[160,711,712],{},"Pyro Radar fusionne ces flux en une carte unique, mondiale, rafraîchie en continu.",[155,714,716],{"id":715},"les-sources","Les sources",[176,718,719,732],{},[179,720,721],{},[182,722,723,726,729],{},[185,724,725],{},"Source",[185,727,728],{},"Couverture",[185,730,731],{},"Cadence",[192,733,734,745,756,766,776],{},[182,735,736,739,742],{},[197,737,738],{},"NASA FIRMS (VIIRS \u002F MODIS)",[197,740,741],{},"Monde",[197,743,744],{},"Passages orbitaux",[182,746,747,750,753],{},[197,748,749],{},"GOES-18 \u002F 19",[197,751,752],{},"Amériques",[197,754,755],{},"10 min",[182,757,758,761,764],{},[197,759,760],{},"Meteosat MTG (LSA SAF)",[197,762,763],{},"Europe \u002F Afrique",[197,765,755],{},[182,767,768,771,774],{},[197,769,770],{},"Himawari-9",[197,772,773],{},"Asie \u002F Océanie",[197,775,755],{},[182,777,778,781,783],{},[197,779,780],{},"Sentinel-3 SLSTR (Copernicus)",[197,782,741],{},[197,784,744],{},[160,786,787],{},"Les satellites géostationnaires apportent la fréquence, les orbites polaires apportent la\nrésolution. Chaque source supplémentaire s'active dès que ses identifiants sont présents\ndans la configuration, et est ignorée sinon — le projet tourne donc avec la seule clé\nNASA FIRMS, gratuite et immédiate.",[155,789,791],{"id":790},"le-travail-technique","Le travail technique",[160,793,794,797],{},[171,795,796],{},"Ingestion."," Chaque source a son propre format : CSV pour FIRMS, NetCDF pour GOES,\nHDF5 pour MTG. Un ingesteur par source normalise vers un schéma commun de détections\ngéolocalisées avec puissance radiative.",[160,799,800,803],{},[171,801,802],{},"Zones brûlées."," Les détections ponctuelles sont regroupées puis converties en polygones\nestimés directement en PostGIS. Le calcul se fait par lots pour éviter les pics mémoire —\ncharger toutes les zones d'un coup faisait tomber Postgres.",[160,805,806,809],{},[171,807,808],{},"Vent."," Les données GFS ne sont téléchargées et décodées (GRIB, via ecCodes) qu'autour\ndes feux actifs. Télécharger la grille mondiale aurait représenté plusieurs gigaoctets\npar cycle pour une information utile sur quelques dizaines de kilomètres carrés.",[160,811,812,815],{},[171,813,814],{},"Temps réel."," Les nouvelles détections sont poussées au navigateur par Server-Sent\nEvents. Pas de polling, pas de WebSocket bidirectionnel là où un flux descendant suffit.",[160,817,818,821],{},[171,819,820],{},"Carto."," MapLibre GL avec fond OpenFreeMap et vue satellite hybride Esri World Imagery —\nles deux sans clé d'API, donc sans quota ni facturation surprise.",[155,823,613],{"id":612},[160,825,826],{},"Ajouter une zone de couverture, c'est une ligne de SQL. Cette contrainte de conception,\nposée tôt, a évité que la logique géographique se disperse dans le code applicatif.",{"title":77,"searchDepth":255,"depth":255,"links":828},[829,830,831,832],{"id":705,"depth":255,"text":706},{"id":715,"depth":255,"text":716},{"id":790,"depth":255,"text":791},{"id":612,"depth":255,"text":613},"Suivi des feux de forêt en temps réel à l'échelle mondiale, à partir de six sources satellitaires agrégées et affichées sur une carte vectorielle.",[835,836,837],"Six sources satellitaires agrégées, des orbites polaires aux géostationnaires","Estimation des zones brûlées calculée directement en PostGIS","Diffusion temps réel par Server-Sent Events, sans polling",{},"\u002Fprojets\u002Fpyro-radar",{"title":700,"description":833},"projets\u002Fpyro-radar",[21,27,34,44,43,843],"SSE","91WU6lSGEk6-85WMvGHJtvL4eug7cEs3-A7Rkrjuu90",{"id":846,"title":847,"body":848,"cover":77,"demo":77,"description":922,"extension":261,"featured":6,"highlights":923,"meta":927,"navigation":144,"path":928,"rang":929,"repo":77,"role":930,"seo":931,"status":271,"stem":932,"tags":933,"year":403,"__hash__":936},"projets\u002Fprojets\u002Frootine.md","Rootine",{"type":152,"value":849,"toc":916},[850,856,858,874,876,882,888,894,900,904,907,910,913],[851,852,853],"blockquote",{},[160,854,855],{},"Les habitudes sont les racines de qui tu deviens.",[155,857,158],{"id":157},[160,859,860,861,865,866,869,870,873],{},"Rootine — jeu de mots entre ",[862,863,864],"em",{},"root"," et ",[862,867,868],{},"routine"," — est une application de suivi\nd'habitudes construite autour d'un principe simple : ",[171,871,872],{},"la régularité bat l'intensité",".\nDix minutes de sport chaque jour valent mieux qu'une séance de deux heures par semaine.",[155,875,442],{"id":441},[160,877,878,881],{},[171,879,880],{},"Accueil"," — la checklist des habitudes du jour, avec case à cocher animée (ressort et\nretour haptique), barre de progression globale et message de félicitations quand tout est\nfait. Un bascule Hier \u002F Aujourd'hui permet de valider rétroactivement.",[160,883,884,887],{},[171,885,886],{},"Routines"," — création, modification et suppression des habitudes suivies.",[160,889,890,893],{},[171,891,892],{},"Calendrier"," — heatmap mensuelle, où l'intensité de la couleur reflète le taux de\ncomplétion du jour. La régularité devient visible d'un coup d'œil.",[160,895,896,899],{},[171,897,898],{},"Statistiques"," — séries en cours, taux de complétion, tendances dans le temps.",[155,901,903],{"id":902},"le-parti-pris","Le parti pris",[160,905,906],{},"La plupart des applications d'habitudes punissent l'oubli : séries cassées, badges perdus,\nnotifications culpabilisantes. Ce ressort motive une semaine puis produit l'effet inverse —\non désinstalle plutôt que d'affronter le tableau de bord.",[160,908,909],{},"Rootine assume l'inverse. On peut valider la veille, aucune série ne se casse\ndéfinitivement, et le ton reste encourageant. L'objectif est de tenir des mois, pas de\ngagner un score.",[155,911,496],{"id":912},"stack",[160,914,915],{},"SwiftUI et SwiftData, sans dépendance externe. Toutes les données restent sur l'appareil.",{"title":77,"searchDepth":255,"depth":255,"links":917},[918,919,920,921],{"id":157,"depth":255,"text":158},{"id":441,"depth":255,"text":442},{"id":902,"depth":255,"text":903},{"id":912,"depth":255,"text":496},"Application iOS de suivi d'habitudes, minimaliste et bienveillante — checklist du jour, calendrier heatmap et statistiques de régularité.",[924,925,926],"Validation rétroactive de la veille, sans culpabilisation","Calendrier heatmap mensuel et statistiques de tendance","Animations ressort et retour haptique sur chaque validation",{},"\u002Fprojets\u002Frootine",10,"Conception, design et développement iOS",{"title":847,"description":922},"projets\u002Frootine",[65,66,67,934,935],"Swift","Charts","52SigAFkadFcP5T0tNCpIsu85TXS1LXpC-BQjh3A3fM",{"id":938,"title":939,"body":940,"cover":77,"demo":77,"description":977,"extension":261,"featured":6,"highlights":978,"meta":982,"navigation":144,"path":983,"rang":984,"repo":77,"role":930,"seo":985,"status":271,"stem":986,"tags":987,"year":403,"__hash__":989},"projets\u002Fprojets\u002Fsofa.md","Sofa",{"type":152,"value":941,"toc":971},[942,944,947,949,956,959,963,966,968],[155,943,158],{"id":157},[160,945,946],{},"Sofa répond à un problème quotidien : savoir quoi regarder, et surtout se souvenir de ce\nqu'on voulait regarder. L'application permet de chercher un film ou une série via l'API\nTMDB, de l'ajouter à sa bibliothèque, de marquer ce qui a été vu, de noter les titres et\nde voir où les regarder en streaming.",[155,948,496],{"id":912},[160,950,951,952,955],{},"Application ",[171,953,954],{},"iOS 100 % native",", écrite en SwiftUI avec SwiftData pour la persistance.\nPas de framework multiplateforme, pas de WebView : les animations, les gestes et le\ncomportement du système sont ceux d'une application iOS, parce que c'est précisément ce\nqui distingue une application qu'on garde d'une application qu'on désinstalle.",[160,957,958],{},"SwiftData assure la persistance locale, ce qui rend la bibliothèque consultable hors\nconnexion — seules la recherche et la mise à jour des fiches nécessitent le réseau.",[155,960,962],{"id":961},"design","Design",[160,964,965],{},"Interface sombre, inspirée de l'ambiance d'une salle de cinéma : fonds profonds, affiches\nmises en valeur, typographie contrastée. Le design system est défini une fois — couleurs,\nespacements, typographies — puis appliqué de façon cohérente à tous les écrans.",[155,967,613],{"id":612},[160,969,970],{},"Venant du web, SwiftUI demande de désapprendre certains réflexes. Le système de layout ne\nfonctionne pas comme Flexbox, et la propagation d'état suit ses propres règles. En\nrevanche, l'intégration avec le système — widgets, partage, mode sombre, accessibilité —\nest sans commune mesure avec ce qu'on obtient dans une application web.",{"title":77,"searchDepth":255,"depth":255,"links":972},[973,974,975,976],{"id":157,"depth":255,"text":158},{"id":912,"depth":255,"text":496},{"id":961,"depth":255,"text":962},{"id":612,"depth":255,"text":613},"Application iOS native de watchlist films et séries — recherche via TMDB, bibliothèque personnelle, suivi des visionnages et notes.",[979,980,981],"Persistance locale avec SwiftData, utilisable hors connexion","Intégration TMDB — recherche, fiches détaillées et plateformes de streaming","Design system sombre inspiré des salles de cinéma",{},"\u002Fprojets\u002Fsofa",9,{"title":939,"description":977},"projets\u002Fsofa",[65,66,67,988,934],"TMDB API","1A_1u3V4tj95W_bPpc2tqYHnGFKgZ3gwvblZA37RMWY",{"id":991,"title":992,"body":993,"cover":77,"demo":77,"description":1107,"extension":261,"featured":6,"highlights":1108,"meta":1112,"navigation":144,"path":1113,"rang":1114,"repo":77,"role":1115,"seo":1116,"status":271,"stem":1117,"tags":1118,"year":276,"__hash__":1123},"projets\u002Fprojets\u002Fthanos.md","Thanos",{"type":152,"value":994,"toc":1101},[995,997,1000,1004,1058,1061,1065,1091,1095,1098],[155,996,158],{"id":157},[160,998,999],{},"Thanos est un environnement complet pour prototyper, tester et comparer des stratégies de\ntrading à travers plusieurs exchanges et plusieurs unités de temps. L'objectif n'est pas\nde trader : c'est de disposer d'un banc d'essai honnête pour répondre à la question\n« est-ce que cette idée aurait fonctionné, et à quel prix en risque ».",[155,1001,1003],{"id":1002},"stratégies-implémentées","Stratégies implémentées",[176,1005,1006,1016],{},[179,1007,1008],{},[182,1009,1010,1013],{},[185,1011,1012],{},"Stratégie",[185,1014,1015],{},"Principe",[192,1017,1018,1026,1034,1042,1050],{},[182,1019,1020,1023],{},[197,1021,1022],{},"SMA",[197,1024,1025],{},"Croisement de moyennes mobiles simples",[182,1027,1028,1031],{},[197,1029,1030],{},"EMA",[197,1032,1033],{},"Croisement de moyennes mobiles exponentielles",[182,1035,1036,1039],{},[197,1037,1038],{},"Momentum (RSI)",[197,1040,1041],{},"Entrées sur zones de survente \u002F surachat",[182,1043,1044,1047],{},[197,1045,1046],{},"SMA-EMA combiné",[197,1048,1049],{},"Confirmation croisée entre les deux familles de moyennes",[182,1051,1052,1055],{},[197,1053,1054],{},"TRIX",[197,1056,1057],{},"Oscillateur de triple lissage exponentiel",[160,1059,1060],{},"Toutes partagent la même interface, ce qui permet de les évaluer sur un socle identique —\nmêmes données, mêmes frais, mêmes métriques.",[155,1062,1064],{"id":1063},"modules","Modules",[444,1066,1067,1073,1079,1085],{},[447,1068,1069,1072],{},[171,1070,1071],{},"Data Manager"," — récupération et mise en cache des historiques par exchange et timeframe",[447,1074,1075,1078],{},[171,1076,1077],{},"Custom Indicators"," — indicateurs techniques implémentés à la main plutôt qu'importés",[447,1080,1081,1084],{},[171,1082,1083],{},"Backtest Analysis"," — métriques de performance et de risque",[447,1086,1087,1090],{},[171,1088,1089],{},"Plot Analysis"," — visualisation des entrées, sorties et courbes de capital",[155,1092,1094],{"id":1093},"le-vrai-enseignement","Le vrai enseignement",[160,1096,1097],{},"La difficulté d'un backtest n'est pas de le faire fonctionner : c'est de l'empêcher de\nmentir. Les biais s'y glissent facilement — utiliser une donnée qui n'était pas encore\ndisponible au moment de la décision, ignorer les frais et le slippage, ou optimiser les\nparamètres sur la même période que celle qui sert à valider la stratégie.",[160,1099,1100],{},"Une stratégie qui affiche un résultat spectaculaire en backtest est presque toujours le\nsigne d'une fuite de données quelque part, pas d'une découverte.",{"title":77,"searchDepth":255,"depth":255,"links":1102},[1103,1104,1105,1106],{"id":157,"depth":255,"text":158},{"id":1002,"depth":255,"text":1003},{"id":1063,"depth":255,"text":1064},{"id":1093,"depth":255,"text":1094},"Plateforme de recherche, de backtesting et d'analyse de stratégies de trading sur les marchés de crypto-monnaies.",[1109,1110,1111],"Cinq familles de stratégies comparables sur un même socle de test","Indicateurs techniques personnalisés implémentés à la main","Gestion du cache de données historiques multi-exchanges et multi-timeframes",{},"\u002Fprojets\u002Fthanos",8,"Conception et développement",{"title":992,"description":1107},"projets\u002Fthanos",[32,1119,1120,1121,1122],"Pandas","NumPy","Backtesting","Data analysis","vRXyfr3Hv2JILVAnL1Xlb3-S1ZldYs0rAIX1Ux5WGFU",{"id":1125,"title":1126,"body":1127,"cover":77,"demo":77,"description":1257,"extension":261,"featured":144,"highlights":1258,"meta":1262,"navigation":144,"path":1263,"rang":1264,"repo":77,"role":269,"seo":1265,"status":399,"stem":1266,"tags":1267,"year":636,"__hash__":1268},"projets\u002Fprojets\u002Fvigie.md","Vigie",{"type":152,"value":1128,"toc":1251},[1129,1131,1134,1137,1141,1173,1176,1178,1223,1242,1244],[155,1130,547],{"id":546},[160,1132,1133],{},"J'avais une dizaine de projets en ligne et aucune vision d'ensemble : est-ce que le\ncron de la nuit est passé ? Est-ce que l'API répond encore ? Combien de visiteurs\ncette semaine ? Chaque réponse demandait de se connecter à un serveur différent.",[160,1135,1136],{},"Brancher un service tiers par projet aurait multiplié les comptes, les coûts et les\nbandeaux de cookies. J'ai préféré construire l'outil unique qui me manquait.",[155,1138,1140],{"id":1139},"ce-que-fait-vigie","Ce que fait Vigie",[444,1142,1143,1149,1155,1161,1167],{},[447,1144,1145,1148],{},[171,1146,1147],{},"Analytique de visite"," sans cookie ni identifiant persistant — aucun bandeau de consentement nécessaire",[447,1150,1151,1154],{},[171,1152,1153],{},"Surveillance de disponibilité"," : sondes HTTP régulières avec historique et calcul d'uptime",[447,1156,1157,1160],{},[171,1158,1159],{},"Supervision des tâches planifiées"," : un cron qui ne s'est pas signalé dans sa fenêtre déclenche une alerte",[447,1162,1163,1166],{},[171,1164,1165],{},"Suivi des erreurs"," applicatives remontées par les projets branchés",[447,1168,1169,1172],{},[171,1170,1171],{},"Page de statut publique",", alimentée par les mêmes données que le tableau de bord interne",[160,1174,1175],{},"Chaque projet supervisé possède sa clé API, et ne voit jamais les données des autres.",[155,1177,291],{"id":290},[176,1179,1180,1188],{},[179,1181,1182],{},[182,1183,1184,1186],{},[185,1185,187],{},[185,1187,190],{},[192,1189,1190,1198,1205,1213],{},[182,1191,1192,1195],{},[197,1193,1194],{},"API",[197,1196,1197],{},"AdonisJS v6, TypeScript, Lucid",[182,1199,1200,1202],{},[197,1201,207],{},[197,1203,1204],{},"PostgreSQL 17",[182,1206,1207,1210],{},[197,1208,1209],{},"Interface",[197,1211,1212],{},"Vue 3, Vite, Pinia, TypeScript",[182,1214,1215,1217],{},[197,1216,237],{},[197,1218,1219,1220],{},"Docker Compose sur VPS, Caddy en frontal, GitHub Actions sur ",[342,1221,1222],{},"main",[160,1224,1225,1226,345,1229,345,1232,345,1235,345,1238,1241],{},"Tout tourne en conteneurs — ",[342,1227,1228],{},"postgres",[342,1230,1231],{},"redis",[342,1233,1234],{},"api",[342,1236,1237],{},"worker",[342,1239,1240],{},"web"," — sans aucun\nNode requis sur la machine hôte. Le worker traite les sondes et les agrégations hors\ndu cycle de requête, ce qui garde l'API réactive quel que soit le nombre de projets\nsupervisés.",[155,1243,613],{"id":612},[160,1245,1246,1247,1250],{},"Le point dur n'était pas la collecte mais la ",[171,1248,1249],{},"rétention"," : conserver des années de\npoints de mesure sans faire exploser la base. La réponse a été l'agrégation\nprogressive — les données fines vieillissent en moyennes horaires, puis journalières.",{"title":77,"searchDepth":255,"depth":255,"links":1252},[1253,1254,1255,1256],{"id":546,"depth":255,"text":547},{"id":1139,"depth":255,"text":1140},{"id":290,"depth":255,"text":291},{"id":612,"depth":255,"text":613},"Supervision unifiée de mes projets web — analytique sans cookies, monitoring de disponibilité, suivi des tâches planifiées et page de statut publique.",[1259,1260,1261],"Multi-projets avec isolation stricte des données par clé API","Analytique de visite sans cookie, donc sans bandeau de consentement","Alertes et page de statut publique générée automatiquement",{},"\u002Fprojets\u002Fvigie",1,{"title":1126,"description":1257},"projets\u002Fvigie",[34,21,23,43,45,54],"vt2BqP6n3u1aBtkKwgv_E70_4G8vOWboipMOFd_BlUU",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1270},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"7\" height=\"9\" x=\"3\" y=\"3\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"5\" x=\"14\" y=\"3\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"9\" x=\"14\" y=\"12\" rx=\"1\"\u002F>\u003Crect width=\"7\" height=\"5\" x=\"3\" y=\"16\" rx=\"1\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1272},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Crect width=\"20\" height=\"8\" x=\"2\" y=\"2\" rx=\"2\" ry=\"2\"\u002F>\u003Crect width=\"20\" height=\"8\" x=\"2\" y=\"14\" rx=\"2\" ry=\"2\"\u002F>\u003Cpath d=\"M6 6h.01M6 18h.01\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1274},"\u003Cg fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\">\u003Cellipse cx=\"12\" cy=\"5\" rx=\"9\" ry=\"3\"\u002F>\u003Cpath d=\"M3 5v14a9 3 0 0 0 18 0V5\"\u002F>\u003Cpath d=\"M3 12a9 3 0 0 0 18 0\"\u002F>\u003C\u002Fg>",{"left":4,"top":4,"width":5,"height":5,"rotate":4,"vFlip":6,"hFlip":6,"body":1276},"\u003Cpath fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"2\" d=\"M5 12h14m-7-7l7 7l-7 7\"\u002F>",1787073891281]