From a1d6271b655c0b24c1e29414b36dd3d38df5f7a6 Mon Sep 17 00:00:00 2001 From: Jules Neny Date: Mon, 11 May 2026 18:48:15 +0200 Subject: [PATCH] feat(v12-o): Carte O logos plateforme via Brandfetch CDN (visible zoom>1.5x) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Champ optionnel domain dans YAML carte-o-source : propage vers JSON et permet d'afficher un logo plateforme en bas-droite de chaque node (cercle blanc 18px + image clippee circulaire 14px) quand le zoom depasse 1.5x. V1.2-O par defaut : substack.com sur les 15 thematiques essais. Centre + projet TMIP gardent leur fill brut (encre / ocre). Toggle visibilite via callback zoom (opacity 0/1 sur .logo-overlay). A flagger : CDN Brandfetch retourne 403 en curl server-side avec le client ID fourni. A revalider en browser (origin trans-former.fr) — le CDN peut exiger un Origin header autorise. Si bloque, fallback prevu en V1.3 (proxy local ou logos packages dans /public/logos/). Files: - public/data/carte-o-source.yaml : +15 champs domain - scripts/build-carte-o.js : propagation domain -> JSON - src/components/vue/CarteO.vue : CarteNode.domain + logoUrl helper + logo-overlay (circle + image clip-path) + toggle visibilite zoom - public/data/carte-o.json : regenere (15/17 nodes ont domain) --- public/data/carte-o-source.yaml | 18 +++++++++++++ public/data/carte-o.json | 47 ++++++++++++++++++++++----------- scripts/build-carte-o.js | 7 +++-- src/components/vue/CarteO.vue | 38 ++++++++++++++++++++++++++ 4 files changed, 92 insertions(+), 18 deletions(-) diff --git a/public/data/carte-o-source.yaml b/public/data/carte-o-source.yaml index 3a8dafd..40ad47f 100644 --- a/public/data/carte-o-source.yaml +++ b/public/data/carte-o-source.yaml @@ -4,6 +4,9 @@ # statut: gestation (draft/en cours) | edite (publie) # nature: essai (texte politique) | projet (projet archi) # niveau: 0 (centre) | 1 (concepts force) | 2 (thematiques/projets) +# domain (optionnel) : domaine plateforme source pour logo Brandfetch CDN +# - affiche logo en bas-droite du node si zoom > 1.5x +# - V1.2 par defaut : substack.com pour tous les essais AEP version: "1.1" @@ -23,76 +26,91 @@ thematiques: niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "pratiques-collectives" label: "Pratiques collectives" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "art-narration" label: "Art & narration" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "pouvoir-domination" label: "Rapport au pouvoir" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "medias-critique" label: "Medias & pensee critique" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "justice-securite" label: "Justice & securite" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "sante-globale" label: "Sante globale" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "agriculture" label: "Agriculture" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "post-croissance" label: "Post-croissance" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "anthropocene" label: "Anthropocene & effondrement" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "education" label: "Education a la transformation" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "urbanisme" label: "Urbanisme" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "geopolitique" label: "Geopolitique & decolonisation" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "ia-technologie" label: "IA & technologie" niveau: 2 nature: essai statut: gestation + domain: "substack.com" - id: "spiritualite" label: "Spiritualite" niveau: 2 nature: essai statut: gestation + domain: "substack.com" projets: - id: "tmip" diff --git a/public/data/carte-o.json b/public/data/carte-o.json index f3f6661..6c0f168 100644 --- a/public/data/carte-o.json +++ b/public/data/carte-o.json @@ -1,6 +1,6 @@ { "version": "1.1", - "generatedAt": "2026-05-11T16:41:21.600Z", + "generatedAt": "2026-05-11T16:47:45.459Z", "nodes": [ { "id": "contrat-social-medecine-corps-social", @@ -20,7 +20,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "pratiques-collectives", @@ -30,7 +31,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "art-narration", @@ -40,7 +42,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "pouvoir-domination", @@ -50,7 +53,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "medias-critique", @@ -60,7 +64,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "justice-securite", @@ -70,7 +75,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "sante-globale", @@ -80,7 +86,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "agriculture", @@ -90,7 +97,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "post-croissance", @@ -100,7 +108,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "anthropocene", @@ -110,7 +119,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "education", @@ -120,7 +130,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "urbanisme", @@ -130,7 +141,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "geopolitique", @@ -140,7 +152,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "ia-technologie", @@ -150,7 +163,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "spiritualite", @@ -160,7 +174,8 @@ "statut": "gestation", "resume": null, "radius": 12, - "family": "concept" + "family": "concept", + "domain": "substack.com" }, { "id": "tmip", diff --git a/scripts/build-carte-o.js b/scripts/build-carte-o.js index 2348dfc..ae6180f 100644 --- a/scripts/build-carte-o.js +++ b/scripts/build-carte-o.js @@ -43,7 +43,7 @@ async function main() { } function addNode(obj) { - nodes.push({ + const node = { id: obj.id, label: obj.label, niveau: obj.niveau, @@ -52,7 +52,10 @@ async function main() { resume: obj.resume || null, radius: getRadius(obj.niveau, obj.nature), family: getFamily(obj.nature), - }) + } + // V1.2-O : propage le champ optionnel domain (logo plateforme via Brandfetch CDN) + if (obj.domain) node.domain = obj.domain + nodes.push(node) } const centreId = data.centre.id diff --git a/src/components/vue/CarteO.vue b/src/components/vue/CarteO.vue index 61cfa3e..c15c457 100644 --- a/src/components/vue/CarteO.vue +++ b/src/components/vue/CarteO.vue @@ -19,8 +19,15 @@ interface CarteNode { slug?: string theme?: string path?: string + domain?: string // V1.2-O : domaine plateforme source (logo Brandfetch CDN) } +// V1.2-O : logos plateforme via Brandfetch CDN (visible zoom > 1.5x seulement) +const BRANDFETCH_CLIENT_ID = '4ae58bd85c8140eab0cee72f40656120' +const LOGO_ZOOM_THRESHOLD = 1.5 +const logoUrl = (domain: string) => + `https://cdn.brandfetch.io/${domain}/w/64/h/64?c=${BRANDFETCH_CLIENT_ID}` + interface CarteEdge { source: string | CarteNode target: string | CarteNode @@ -187,6 +194,11 @@ function initSvg() { .scaleExtent([0.3, 4]) .on('zoom', (event) => { gZoom!.attr('transform', event.transform.toString()) + // V1.2-O : toggle visibilite logos plateforme selon le niveau de zoom + // (evite la surcharge visuelle au niveau d'ensemble, montre detail au drill-down) + const scale = event.transform.k + gZoom!.selectAll('.logo-overlay') + .style('opacity', scale > LOGO_ZOOM_THRESHOLD ? 1 : 0) }) svgRoot.call(zoomBehavior as any) @@ -290,6 +302,32 @@ function render() { } }) + // V1.2-O : Logo plateforme (visible zoom > 1.5x seulement) + // Cercle blanc 14px en bas-droite du node (offset +60% x/+60% y du radius) + // Image clip-path circle pour avatar style. Hidden par defaut (opacity 0). + const nodeGroupsWithLogo = nodeGroups.filter(d => !!d.domain) + + const logoOverlay = nodeGroupsWithLogo.append('g') + .attr('class', 'logo-overlay') + .style('opacity', 0) + .attr('pointer-events', 'none') + + logoOverlay.append('circle') + .attr('cx', d => getRadius(d) * 0.6) + .attr('cy', d => getRadius(d) * 0.6) + .attr('r', 9) + .attr('fill', '#FFFFFF') + .attr('stroke', '#0F172A') + .attr('stroke-width', 0.5) + + logoOverlay.append('image') + .attr('href', d => logoUrl(d.domain!)) + .attr('x', d => getRadius(d) * 0.6 - 7) + .attr('y', d => getRadius(d) * 0.6 - 7) + .attr('width', 14) + .attr('height', 14) + .attr('clip-path', d => `circle(7px at ${getRadius(d) * 0.6}px ${getRadius(d) * 0.6}px)`) + // Tooltip nodeGroups.append('title') .text(d => `${d.label}\n[${d.family}]\n${truncate(d.resume || d.intention || '', 200)}`)