DOCS/claude design/mediatheque-ajout-ressource.html
2026-09-04 16:48:04 +02:00

417 lines
26 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!-- Maquette : dialogue d'ajout d'une ressource (Médiathèque).
Artifact de diffusion e029b74d-40d7-43f7-9f63-f89d3a47c7c2, publie le 2026-09-03.
Source de verite du design : ce fichier. L'artifact ne sert qu'a la diffusion.
Contexte : DOCS/v1-mediatheque-plan.md — hors perimetre du lot V1, a arbitrer. -->
<title>Ajouter une ressource — maquette</title>
<style>
:root {
--ground: #EDF0F4;
--surface: #FFFFFF;
--surface-2: #F3F5F8;
--surface-3: #E7EBF1;
--ink: #14202B;
--ink-2: #4A5A6B;
--ink-3: #7C8B9A;
--line: #D6DDE5;
--line-soft: #E6EBF1;
--brand: #264863;
--brand-2: #3C6C90;
--brand-soft: #C2C9D6;
--on-brand: #FFFFFF;
--warning: #9A6608;
--warning-bg: #FDF6E3;
--serious: #A8322A;
--serious-bg: #FBEDEC;
--good: #1F6B4D;
--shadow: 0 1px 2px rgba(20,32,43,.05), 0 4px 14px rgba(20,32,43,.05);
--shadow-lg: 0 2px 6px rgba(20,32,43,.08), 0 18px 42px rgba(20,32,43,.14);
--sans: ui-sans-serif, -apple-system, "Segoe UI", system-ui, "Helvetica Neue", Arial, sans-serif;
--mono: ui-monospace, "Cascadia Mono", "SF Mono", Consolas, "Liberation Mono", monospace;
}
@media (prefers-color-scheme: dark) {
:root:not([data-theme="light"]) {
--ground:#0E141B; --surface:#18222C; --surface-2:#1E2A35; --surface-3:#26333F;
--ink:#E6ECF2; --ink-2:#A3B2C0; --ink-3:#74879A;
--line:#2C3A48; --line-soft:#243140;
--brand:#7FA8C9; --brand-2:#A3C4DD; --brand-soft:#35506B; --on-brand:#0E141B;
--warning:#D6A343; --warning-bg:#2A2417; --serious:#E08078; --serious-bg:#2B1B1A; --good:#6FBF97;
--shadow: 0 1px 2px rgba(0,0,0,.3), 0 4px 14px rgba(0,0,0,.22);
--shadow-lg: 0 2px 6px rgba(0,0,0,.4), 0 18px 42px rgba(0,0,0,.34);
}
}
:root[data-theme="dark"] {
--ground:#0E141B; --surface:#18222C; --surface-2:#1E2A35; --surface-3:#26333F;
--ink:#E6ECF2; --ink-2:#A3B2C0; --ink-3:#74879A;
--line:#2C3A48; --line-soft:#243140;
--brand:#7FA8C9; --brand-2:#A3C4DD; --brand-soft:#35506B; --on-brand:#0E141B;
--warning:#D6A343; --warning-bg:#2A2417; --serious:#E08078; --serious-bg:#2B1B1A; --good:#6FBF97;
--shadow: 0 1px 2px rgba(0,0,0,.3), 0 4px 14px rgba(0,0,0,.22);
--shadow-lg: 0 2px 6px rgba(0,0,0,.4), 0 18px 42px rgba(0,0,0,.34);
}
* { box-sizing: border-box; }
body { margin:0; background:var(--ground); color:var(--ink); font-family:var(--sans); font-size:15px; line-height:1.5; -webkit-font-smoothing:antialiased; }
.page { max-width:1240px; margin:0 auto; padding:34px 22px 72px; }
h1 { margin:0 0 6px; font-size:25px; font-weight:700; letter-spacing:-.02em; text-wrap:balance; }
.lede { margin:0 0 24px; font-size:14.5px; color:var(--ink-2); max-width:72ch; }
h2 { margin:38px 0 4px; font-size:16px; font-weight:700; letter-spacing:-.01em; }
h2 + .sub { margin:0 0 16px; font-size:13px; color:var(--ink-3); max-width:74ch; }
.note { display:flex; gap:12px; align-items:flex-start; background:var(--surface); border:1px solid var(--line); border-left:3px solid var(--brand); border-radius:6px; padding:13px 16px; margin-bottom:26px; }
.note strong { display:block; font-size:13.5px; margin-bottom:2px; }
.note span { font-size:13px; color:var(--ink-2); }
code { font-family:var(--mono); font-size:.88em; background:var(--surface-3); border-radius:3px; padding:1px 5px; color:var(--ink-2); }
/* ── deux colonnes de maquette ─────────────────────────────────────── */
.compare { display:grid; grid-template-columns:1fr 1fr; gap:26px; align-items:start; }
@media (max-width:1080px){ .compare { grid-template-columns:1fr; } }
.stage { background:var(--surface-2); border:1px solid var(--line); border-radius:10px; padding:22px 20px 26px; overflow-x:auto; }
.stage > header { display:flex; align-items:center; gap:9px; margin-bottom:16px; }
.stage > header b { font-size:13.5px; }
.pill { font-family:var(--mono); font-size:10px; letter-spacing:.09em; text-transform:uppercase; border-radius:3px; padding:3px 8px; }
.pill.now { background:var(--serious-bg); color:var(--serious); border:1px solid var(--serious); }
.pill.next { background:var(--brand); color:var(--on-brand); }
/* ── coquille de dialogue ──────────────────────────────────────────── */
.dlg { width:512px; background:var(--surface); box-shadow:var(--shadow-lg); overflow:hidden; }
.dlg.old { border-radius:20px; padding:20px 24px 24px; }
.dlg.new { border-radius:10px; border:1px solid var(--line); }
.dlg-title-old { text-align:center; font-size:22px; font-weight:400; margin:0 0 10px; }
.tabbar { display:flex; border-bottom:1px solid var(--line); }
.tabbar div { flex:1; text-align:center; padding:11px 0; font-size:14px; color:var(--ink); }
.tabbar div.on { color:var(--brand); font-weight:600; box-shadow:inset 0 -2px 0 var(--brand); }
.oldbody { height:238px; display:grid; place-items:center; position:relative; }
.bigbtn { background:var(--brand); color:var(--on-brand); border-radius:15px; padding:15px 25px; font-size:14px; }
.oldfoot { display:flex; justify-content:flex-end; gap:8px; margin-top:16px; }
.rb { display:inline-flex; align-items:center; gap:7px; border-radius:999px; padding:9px 17px; font-size:15px; }
.rb.second { background:var(--brand-soft); color:var(--ink); }
.rb.prim { background:var(--brand); color:var(--on-brand); }
/* la carte muette d'aujourd'hui */
.mute { width:100%; margin:0 12px; background:var(--surface-2); border-radius:8px; box-shadow:var(--shadow); padding:12px 14px; }
.mute b { display:block; font-size:14px; }
.mute span { font-size:12.5px; color:var(--ink-3); }
/* ── dialogue proposé ──────────────────────────────────────────────── */
.nhead { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:17px 18px 14px; }
.nhead h3 { margin:0; font-size:15px; font-weight:700; }
.xbtn { width:28px; height:28px; border-radius:5px; border:1px solid transparent; background:none; color:var(--ink-3); display:grid; place-items:center; cursor:pointer; }
.xbtn:hover { background:var(--surface-2); border-color:var(--line); color:var(--ink); }
.xbtn:focus-visible { outline:2px solid var(--brand); outline-offset:2px; }
.nbody { padding:0 18px 4px; }
.drop { border:1.5px dashed var(--line); border-radius:8px; background:var(--surface-2); padding:26px 18px; text-align:center; }
.drop.hot { border-color:var(--brand); background:color-mix(in srgb, var(--brand) 7%, var(--surface-2)); }
.drop .ico { color:var(--ink-3); margin-bottom:8px; }
.drop b { display:block; font-size:13.5px; font-weight:600; }
.drop .browse { color:var(--brand); text-decoration:underline; text-underline-offset:2px; }
.drop small { display:block; margin-top:5px; font-size:12px; color:var(--ink-3); }
.orurl { display:flex; align-items:center; gap:10px; margin-top:12px; }
.orurl .bar { flex:1; height:1px; background:var(--line-soft); }
.linkbtn { display:inline-flex; align-items:center; gap:6px; background:none; border:none; font-family:inherit; font-size:12.5px; color:var(--ink-2); cursor:pointer; padding:2px 4px; border-radius:4px; }
.linkbtn:hover { color:var(--brand); }
.linkbtn:focus-visible { outline:2px solid var(--brand); outline-offset:2px; }
.urlrow { display:none; gap:8px; margin-top:11px; }
.urlrow.open { display:flex; }
.inp { flex:1; background:var(--surface-2); border:1px solid var(--line); border-radius:5px; padding:8px 11px; font-family:inherit; font-size:13px; color:var(--ink); }
.inp:focus-visible { outline:2px solid var(--brand); outline-offset:-1px; }
.flist { margin:14px 0 0; padding:0; list-style:none; border-top:1px solid var(--line-soft); }
.frow { display:grid; grid-template-columns:26px 1fr auto 28px; gap:10px; align-items:center; padding:9px 0; border-bottom:1px solid var(--line-soft); }
.frow .ft { color:var(--ink-3); display:grid; place-items:center; }
.frow .fn { min-width:0; font-size:13px; }
.frow .fn b { display:block; font-weight:500; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.frow .fn span { font-size:11.5px; color:var(--ink-3); }
.chip { font-family:var(--mono); font-size:10px; letter-spacing:.06em; text-transform:uppercase; color:var(--ink-2); background:var(--surface-3); border-radius:3px; padding:3px 7px; }
.frow .sz { font-size:12px; color:var(--ink-3); font-variant-numeric:tabular-nums; }
.del { width:28px; height:28px; border:1px solid var(--line); background:var(--surface-2); border-radius:5px; color:var(--ink-3); display:grid; place-items:center; cursor:pointer; }
.del:hover { border-color:var(--serious); color:var(--serious); }
.del:focus-visible { outline:2px solid var(--brand); outline-offset:2px; }
.totals { display:flex; justify-content:space-between; align-items:baseline; padding:11px 0 3px; font-size:12.5px; color:var(--ink-3); }
.totals b { color:var(--ink-2); font-weight:600; font-variant-numeric:tabular-nums; }
.nfoot { display:flex; align-items:center; justify-content:flex-end; gap:10px; padding:14px 18px; margin-top:10px; background:var(--surface-2); border-top:1px solid var(--line-soft); }
.btn { display:inline-flex; align-items:center; gap:7px; border-radius:999px; padding:8px 16px; font-family:inherit; font-size:13px; font-weight:600; cursor:pointer; border:1px solid transparent; }
.btn.ghost { background:none; color:var(--ink-2); }
.btn.ghost:hover { color:var(--ink); }
.btn.primary { background:var(--brand); color:var(--on-brand); border-color:var(--brand); }
.btn.primary:hover { background:var(--brand-2); border-color:var(--brand-2); }
.btn:focus-visible { outline:2px solid var(--brand); outline-offset:2px; }
.btn[disabled] { opacity:.45; cursor:not-allowed; }
/* ── pastilles de renvoi vers le tableau ───────────────────────────── */
.cx { display:inline-grid; place-items:center; width:17px; height:17px; border-radius:50%; background:var(--serious); color:#fff; font-family:var(--mono); font-size:10px; font-weight:700; vertical-align:middle; }
.anno { display:flex; align-items:flex-start; gap:7px; margin-top:9px; font-size:12px; color:var(--serious); }
.anno .cx { flex:0 0 auto; margin-top:1px; }
/* ── tableau des défauts ───────────────────────────────────────────── */
.tblwrap { overflow-x:auto; border:1px solid var(--line); border-radius:8px; background:var(--surface); }
table { width:100%; border-collapse:collapse; min-width:720px; }
th, td { text-align:left; padding:11px 14px; border-bottom:1px solid var(--line-soft); font-size:13px; vertical-align:top; }
th { font-size:11px; font-family:var(--mono); letter-spacing:.08em; text-transform:uppercase; color:var(--ink-3); background:var(--surface-2); border-bottom:1px solid var(--line); }
tr:last-child td { border-bottom:none; }
td.n { width:34px; }
td .where { display:block; margin-top:3px; font-family:var(--mono); font-size:11px; color:var(--ink-3); }
.bugflag { display:inline-block; font-family:var(--mono); font-size:10px; letter-spacing:.06em; text-transform:uppercase; color:var(--serious); background:var(--serious-bg); border:1px solid var(--serious); border-radius:3px; padding:1px 6px; margin-right:6px; }
.cols { display:grid; grid-template-columns:1fr 1fr; gap:18px; margin-top:16px; }
@media (max-width:820px){ .cols { grid-template-columns:1fr; } }
.card { background:var(--surface); border:1px solid var(--line); border-radius:8px; padding:16px 18px 18px; }
.card h3 { margin:0 0 10px; font-size:13.5px; font-weight:700; }
.card ul { margin:0; padding-left:18px; }
.card li { font-size:13px; color:var(--ink-2); margin-bottom:6px; }
.card li:last-child { margin-bottom:0; }
.warnbox { display:flex; gap:11px; align-items:flex-start; background:var(--warning-bg); border:1px solid var(--warning); border-radius:6px; padding:13px 16px; margin-top:26px; }
.warnbox strong { display:block; font-size:13.5px; margin-bottom:2px; color:var(--warning); }
.warnbox span { font-size:13px; color:var(--ink-2); }
@media (prefers-reduced-motion: reduce) { * { transition:none !important; animation:none !important; } }
</style>
<div class="page">
<h1>Ajouter une ressource</h1>
<p class="lede">Le dialogue d'ajout est le dernier écran de la Médiathèque à ne pas avoir été repris. Il ne dénote pas seulement : deux de ses défauts sont des bugs de dimensionnement, et un troisième fait qu'on ne voit pas ce qu'on vient de choisir.</p>
<div class="note">
<div>
<strong>La thèse : les deux onglets sont le problème.</strong>
<span>« Local » / « En ligne » est une distinction <em>technique</em> — où vivent les octets — imposée avant que l'utilisateur ait fait quoi que ce soit. Un seul corps suffit : une zone de dépôt, et dessous une ligne discrète « ou coller une URL ».</span>
</div>
</div>
<div class="compare">
<!-- ─────────────────────────── AUJOURD'HUI ─────────────────────────── -->
<section class="stage">
<header><b>Aujourd'hui</b><span class="pill now">tel quel dans le code</span></header>
<div class="dlg old">
<p class="dlg-title-old">Nouvelle ressource</p>
<div style="border:1px solid var(--line-soft); border-radius:4px; overflow:hidden;">
<div class="tabbar">
<div class="on">Local</div>
<div>En ligne</div>
</div>
<div class="oldbody">
<div class="mute">
<b>Fichiers</b>
<span>Aucun aperçu possible</span>
</div>
</div>
</div>
<div class="oldfoot">
<span class="rb second">↺ Annuler</span>
<span class="rb prim">✓ Créer</span>
</div>
</div>
<p class="anno"><span class="cx">1</span> Rayon 20 — l'échelle de l'app va de 5 à 10.</p>
<p class="anno"><span class="cx">2</span> Le corps se dimensionne sur l'écran, pas sur le dialogue : 960 × 540 demandés dans 512 de large.</p>
<p class="anno"><span class="cx">4</span> Cinq fichiers choisis, et voilà tout ce qu'on en voit.</p>
<p class="anno"><span class="cx">5</span> « Local » / « En ligne » écrits en dur, en français.</p>
<p class="anno"><span class="cx">6</span> Deux boutons de même poids ; le primaire accepte le clic à vide.</p>
</section>
<!-- ─────────────────────────── PROPOSÉ ─────────────────────────────── -->
<section class="stage">
<header><b>Proposé</b><span class="pill next">maquette</span></header>
<div class="dlg new">
<div class="nhead">
<h3>Ajouter une ressource</h3>
<button class="xbtn" type="button" aria-label="Fermer">
<svg width="15" height="15" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9"/></svg>
</button>
</div>
<div class="nbody">
<div class="drop" id="drop">
<div class="ico">
<svg width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<path d="M12 15V4M12 4L8 8M12 4l4 4"/><path d="M4 15v3a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2v-3"/>
</svg>
</div>
<b>Glissez vos fichiers ici ou <span class="browse">parcourez</span></b>
<small>JPG, PNG, GIF, MP3, MP4, WEBM, PDF, JSON</small>
</div>
<div class="orurl">
<span class="bar"></span>
<button class="linkbtn" type="button" id="urltoggle" aria-expanded="false" aria-controls="urlrow">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" aria-hidden="true"><path d="M6.5 9.5a2.8 2.8 0 0 0 4 0l2-2a2.8 2.8 0 0 0-4-4l-.8.8"/><path d="M9.5 6.5a2.8 2.8 0 0 0-4 0l-2 2a2.8 2.8 0 0 0 4 4l.8-.8"/></svg>
ou coller une URL
</button>
<span class="bar"></span>
</div>
<div class="urlrow" id="urlrow">
<input class="inp" type="url" placeholder="https://… image, vidéo YouTube ou fichier JSON" aria-label="URL de la ressource">
<button class="btn primary" type="button">Ajouter</button>
</div>
<ul class="flist">
<li class="frow">
<span class="ft"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" aria-hidden="true"><rect x="3" y="4" width="18" height="16" rx="2"/><circle cx="8.5" cy="9.5" r="1.5"/><path d="M21 16l-5-5-6 6-2-2-5 5" stroke-linecap="round" stroke-linejoin="round"/></svg></span>
<span class="fn"><b>salle-des-fossiles.jpg</b><span>2560 × 1707</span></span>
<span class="chip">Image</span>
<button class="del" type="button" aria-label="Retirer salle-des-fossiles.jpg">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9"/></svg>
</button>
</li>
<li class="frow">
<span class="ft"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M9 18V5l10-2v13"/><circle cx="6" cy="18" r="3"/><circle cx="16" cy="16" r="3"/></svg></span>
<span class="fn"><b>commentaire-salle-2-nl.mp3</b><span>3 min 12 s</span></span>
<span class="chip">Audio</span>
<button class="del" type="button" aria-label="Retirer commentaire-salle-2-nl.mp3">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9"/></svg>
</button>
</li>
<li class="frow">
<span class="ft"><svg width="17" height="17" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"><path d="M14 3H7a2 2 0 0 0-2 2v14a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2V8z"/><path d="M14 3v5h5"/></svg></span>
<span class="fn"><b>brochure-2026.pdf</b><span>12 pages</span></span>
<span class="chip">PDF</span>
<button class="del" type="button" aria-label="Retirer brochure-2026.pdf">
<svg width="13" height="13" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" aria-hidden="true"><path d="M3.5 3.5l9 9M12.5 3.5l-9 9"/></svg>
</button>
</li>
</ul>
<div class="totals">
<span>3 fichier(s) · <b>4,8 Mo</b></span>
</div>
</div>
<div class="nfoot">
<button class="btn ghost" type="button">Annuler</button>
<button class="btn primary" type="button">Ajouter 3 fichiers</button>
</div>
</div>
<p class="anno" style="color:var(--ink-3)"><span class="cx" style="background:var(--brand)"></span> Le gain principal n'est pas l'habillage : c'est la liste. On voit ce qu'on a choisi, et on peut en retirer un.</p>
</section>
</div>
<h2>Les défauts, relevés dans le code</h2>
<p class="sub">Numérotés comme sur la maquette de gauche. Les trois marqués <span class="bugflag">bug</span> ne relèvent pas du goût.</p>
<div class="tblwrap">
<table>
<thead>
<tr><th class="n">#</th><th>Ce qui cloche</th><th>Effet</th></tr>
</thead>
<tbody>
<tr>
<td class="n"><span class="cx">1</span></td>
<td>Rayon 20 sur le dialogue<span class="where">new_resource_popup.dart:22</span></td>
<td>Hors de l'échelle de l'app (5 / 8 / 10). Exactement le défaut qu'on vient de retirer de la modale de détail.</td>
</tr>
<tr>
<td class="n"><span class="cx">2</span></td>
<td><span class="bugflag">bug</span>Le corps se dimensionne sur <code>MediaQuery</code><span class="where">upload_content_container.dart:33-35</span></td>
<td><code>width * 0.5</code> × <code>height * 0.5</code> de l'<em>écran</em> : sur 1920 × 1080, 960 × 540 demandés dans un dialogue de 512 de large utile. Le contenu déborde et se fait rogner.</td>
</tr>
<tr>
<td class="n"><span class="cx">3</span></td>
<td><span class="bugflag">bug</span>Le champ URL aussi<span class="where">upload_online_resources_container.dart:107 — <code>// TODO GET SIZE</code></span></td>
<td>Même cause : <code>width * 0.35</code> de l'écran. Le TODO est resté.</td>
</tr>
<tr>
<td class="n"><span class="cx">4</span></td>
<td><span class="bugflag">bug</span>On ne voit pas ce qu'on a choisi<span class="where">upload_content_container.dart:57-58</span></td>
<td>Sur le web, après sélection : une carte titrée « Fichiers », sous-titrée « Aucun aperçu possible ». Cinq fichiers ou un seul, même affichage. Aucun moyen d'en retirer un — il faut tout recommencer.</td>
</tr>
<tr>
<td class="n"><span class="cx">5</span></td>
<td>Onglets écrits en dur<span class="where">resource_tab.dart:30-31</span></td>
<td>« Local » et « En ligne » restent en français en anglais et en néerlandais — le trou qu'on vient de combler ailleurs dans la Médiathèque.</td>
</tr>
<tr>
<td class="n"><span class="cx">6</span></td>
<td>Deux boutons de même poids, et une erreur évitable<span class="where">new_resource_popup.dart:60-92</span></td>
<td>Le primaire accepte le clic à vide, puis affiche une notification orange « aucun fichier chargé ». Désactiver le bouton supprime l'erreur au lieu de l'expliquer.</td>
</tr>
<tr>
<td class="n"></td>
<td>Type forcé à <code>Image</code> pour tout fichier local<span class="where">resource_tab.dart:93 et :97</span></td>
<td>Code mort et trompeur : <code>create()</code> re-déduit le type depuis l'extension. À lire, on croit que le MP3 sera enregistré comme image.</td>
</tr>
<tr>
<td class="n"></td>
<td><span class="bugflag">bug</span><code>filesToSendWeb!.length</code> sur une variable nulle<span class="where">new_resource_popup.dart:82</span></td>
<td>Branche non-web uniquement : cliquer « Créer » sans rien choisir plante. Invisible en production tant que l'app reste web.</td>
</tr>
</tbody>
</table>
</div>
<h2>Ce que ça touche</h2>
<div class="cols">
<div class="card">
<h3>Le geste change</h3>
<ul>
<li>Glisser-déposer réel, plus seulement un bouton bleu.</li>
<li>Une ligne par fichier : type, nom, poids, et une croix pour en retirer un.</li>
<li>Le poids total des fichiers choisis. <em>Avant</em> compression : la calculer pour l'afficher referait tout le travail de l'upload une seconde fois, et l'estimation haute est du bon côté pour un quota.</li>
<li>« Ajouter 3 fichiers » plutôt que « Créer » : le bouton dit ce qui va se passer.</li>
<li>Le type se déduit de l'extension et s'affiche en pastille, ce que le code fait déjà sans le montrer.</li>
</ul>
</div>
<div class="card">
<h3>Les fichiers</h3>
<ul>
<li><code>new_resource_popup.dart</code> — réécrit</li>
<li><code>upload_content_container.dart</code> — réécrit (zone de dépôt + liste)</li>
<li><code>resource_tab.dart</code><strong>supprimé</strong>, il ne portait que la barre d'onglets</li>
<li><code>upload_online_resources_container.dart</code><strong>supprimé</strong>, remplacé par le champ URL dépliant</li>
<li>Une poignée de clés l10n, dans la même série <code>media*</code></li>
</ul>
</div>
</div>
<div class="note" style="margin-top:26px; border-left-color:var(--good)">
<div>
<strong>Implémenté le 2026-09-03.</strong>
<span>Glisser-déposer réel (écoute des événements du document — Flutter web ne reçoit pas les fichiers du bureau), liste retirable, champ URL dépliant, bouton désactivé à vide. <code>resource_tab.dart</code>, <code>upload_content_container.dart</code> et <code>upload_online_resources_container.dart</code> sont supprimés ; le nouveau sélecteur vit dans <code>Components/resource_picker.dart</code>. Le dialogue de confirmation partagé (12 appelants) a été repris dans la foulée : « Oui »/« Non » en dur remplacés, et le bouton de confirmation passe en rouge quand l'action détruit.</span>
</div>
</div>
<div class="warnbox">
<div>
<strong>C'était hors périmètre du lot V1 Médiathèque.</strong>
<span><code>v1-mediatheque-plan.md</code> ne mentionne ce dialogue nulle part : le lot s'arrête à la grille, aux facettes et au panneau de détail. Ce chantier-ci est adjacent, pas inclus — à faire maintenant dans la foulée, ou à poser comme carte de suivi. C'est un arbitrage, pas une évidence.</span>
</div>
</div>
</div>
<script>
(function () {
var toggle = document.getElementById('urltoggle');
var row = document.getElementById('urlrow');
toggle.addEventListener('click', function () {
var open = row.classList.toggle('open');
toggle.setAttribute('aria-expanded', open ? 'true' : 'false');
if (open) row.querySelector('.inp').focus();
});
// La zone de depot reagit au survol comme elle reagira au glisser.
var drop = document.getElementById('drop');
drop.addEventListener('mouseenter', function () { drop.classList.add('hot'); });
drop.addEventListener('mouseleave', function () { drop.classList.remove('hot'); });
})();
</script>