Spaces:
Running
Running
| <html lang="fr"> | |
| <head> | |
| <meta charset="utf-8" /> | |
| <meta name="viewport" content="width=device-width, initial-scale=1" /> | |
| <title>Affordances computationnelles — atlas UI</title> | |
| <style> | |
| :root { | |
| --bg: #f7f4ef; | |
| --surface: #ffffff; | |
| --surface-2: #f0ece5; | |
| --ink: #1f1f1f; | |
| --muted: #62605b; | |
| --line: #d8d0c4; | |
| --accent: #315c72; | |
| --accent-2: #8b4b35; | |
| --ok: #2f6c4f; | |
| --warn: #8a5a00; | |
| --shadow: 0 16px 45px rgba(31, 31, 31, 0.08); | |
| --radius: 22px; | |
| --mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", monospace; | |
| --sans: Inter, ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; | |
| --serif: Georgia, "Times New Roman", serif; | |
| } | |
| * { box-sizing: border-box; } | |
| html { scroll-behavior: smooth; } | |
| body { | |
| margin: 0; | |
| color: var(--ink); | |
| background: | |
| radial-gradient(circle at top left, rgba(49, 92, 114, 0.14), transparent 32rem), | |
| radial-gradient(circle at 90% 10%, rgba(139, 75, 53, 0.12), transparent 24rem), | |
| var(--bg); | |
| font-family: var(--sans); | |
| line-height: 1.5; | |
| } | |
| button, input, select, textarea { font: inherit; } | |
| a { color: inherit; } | |
| .wrap { | |
| width: min(1180px, calc(100vw - 32px)); | |
| margin: 0 auto; | |
| } | |
| header { | |
| padding: 48px 0 28px; | |
| } | |
| .eyebrow { | |
| display: inline-flex; | |
| gap: 8px; | |
| align-items: center; | |
| padding: 6px 10px; | |
| border: 1px solid var(--line); | |
| border-radius: 999px; | |
| background: rgba(255, 255, 255, 0.72); | |
| color: var(--muted); | |
| font-size: 0.86rem; | |
| } | |
| .dot { | |
| width: 8px; | |
| height: 8px; | |
| border-radius: 999px; | |
| background: var(--ok); | |
| } | |
| h1 { | |
| max-width: 900px; | |
| margin: 18px 0 12px; | |
| font-family: var(--serif); | |
| font-size: clamp(2.25rem, 6vw, 5rem); | |
| line-height: 0.96; | |
| letter-spacing: -0.055em; | |
| } | |
| .lead { | |
| max-width: 760px; | |
| margin: 0; | |
| color: var(--muted); | |
| font-size: clamp(1.02rem, 1.8vw, 1.22rem); | |
| } | |
| .toolbar { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 10px; | |
| margin-top: 24px; | |
| } | |
| .pill, .chip { | |
| display: inline-flex; | |
| gap: 8px; | |
| align-items: center; | |
| min-height: 32px; | |
| padding: 7px 11px; | |
| border: 1px solid var(--line); | |
| border-radius: 999px; | |
| background: rgba(255, 255, 255, 0.72); | |
| color: var(--muted); | |
| font-size: 0.88rem; | |
| white-space: nowrap; | |
| } | |
| main { padding: 8px 0 54px; } | |
| .figure-board { | |
| display: grid; | |
| grid-template-columns: 1.15fr 0.85fr; | |
| gap: 18px; | |
| margin: 20px 0 24px; | |
| } | |
| .panel, .card { | |
| background: rgba(255, 255, 255, 0.82); | |
| border: 1px solid rgba(216, 208, 196, 0.92); | |
| border-radius: var(--radius); | |
| box-shadow: var(--shadow); | |
| backdrop-filter: blur(12px); | |
| } | |
| .panel { padding: 18px; } | |
| .panel h2, .card h3 { | |
| margin: 0; | |
| letter-spacing: -0.02em; | |
| } | |
| .panel h2 { | |
| font-size: 1.15rem; | |
| } | |
| .panel p, .card p { | |
| margin: 8px 0 0; | |
| color: var(--muted); | |
| } | |
| .layers { | |
| display: grid; | |
| gap: 12px; | |
| margin-top: 16px; | |
| } | |
| .layer { | |
| display: grid; | |
| grid-template-columns: 150px 1fr; | |
| gap: 14px; | |
| align-items: start; | |
| padding: 14px; | |
| border: 1px solid var(--line); | |
| border-radius: 18px; | |
| background: var(--surface); | |
| } | |
| .layer strong { font-size: 0.95rem; } | |
| .layer span { color: var(--muted); } | |
| .timeline { | |
| display: grid; | |
| gap: 10px; | |
| margin-top: 16px; | |
| } | |
| .time-item { | |
| display: grid; | |
| grid-template-columns: 64px 1fr; | |
| gap: 12px; | |
| padding: 11px 0; | |
| border-bottom: 1px solid var(--line); | |
| } | |
| .time-item:last-child { border-bottom: 0; } | |
| .time-item time { color: var(--accent); font-weight: 700; } | |
| .time-item b { display: block; } | |
| .time-item span { color: var(--muted); font-size: 0.92rem; } | |
| .grid { | |
| display: grid; | |
| grid-template-columns: repeat(12, 1fr); | |
| gap: 18px; | |
| margin-top: 18px; | |
| } | |
| .card { | |
| grid-column: span 6; | |
| overflow: hidden; | |
| } | |
| .card.wide { grid-column: span 12; } | |
| .card.small { grid-column: span 4; } | |
| .card-head { | |
| display: flex; | |
| justify-content: space-between; | |
| gap: 14px; | |
| padding: 18px 18px 0; | |
| } | |
| .tag { | |
| align-self: start; | |
| padding: 5px 9px; | |
| border-radius: 999px; | |
| background: var(--surface-2); | |
| color: var(--muted); | |
| font-size: 0.78rem; | |
| white-space: nowrap; | |
| } | |
| .ui { | |
| margin: 16px 18px 18px; | |
| padding: 14px; | |
| border: 1px solid var(--line); | |
| border-radius: 18px; | |
| background: linear-gradient(180deg, rgba(255,255,255,0.72), rgba(240,236,229,0.6)); | |
| } | |
| label { | |
| display: block; | |
| margin: 0 0 6px; | |
| color: var(--muted); | |
| font-size: 0.84rem; | |
| font-weight: 650; | |
| } | |
| textarea, input[type="text"], select { | |
| width: 100%; | |
| min-height: 42px; | |
| border: 1px solid var(--line); | |
| border-radius: 14px; | |
| padding: 10px 12px; | |
| background: #fff; | |
| color: var(--ink); | |
| outline: none; | |
| } | |
| textarea { min-height: 92px; resize: vertical; } | |
| input[type="range"] { width: 100%; } | |
| .row { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 12px; | |
| margin-top: 12px; | |
| } | |
| .actions { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 10px; | |
| margin-top: 12px; | |
| } | |
| .btn { | |
| border: 0; | |
| border-radius: 999px; | |
| padding: 10px 14px; | |
| background: var(--ink); | |
| color: white; | |
| cursor: pointer; | |
| transition: transform 160ms ease, opacity 160ms ease; | |
| } | |
| .btn:hover { transform: translateY(-1px); } | |
| .btn:disabled { cursor: wait; opacity: 0.62; transform: none; } | |
| .btn.secondary { | |
| border: 1px solid var(--line); | |
| background: #fff; | |
| color: var(--ink); | |
| } | |
| .btn.ghost { | |
| background: transparent; | |
| color: var(--accent); | |
| padding-left: 0; | |
| padding-right: 0; | |
| } | |
| .output { | |
| min-height: 58px; | |
| margin-top: 12px; | |
| padding: 12px; | |
| border: 1px dashed rgba(49, 92, 114, 0.38); | |
| border-radius: 14px; | |
| background: rgba(49, 92, 114, 0.06); | |
| color: #27343a; | |
| } | |
| .output[aria-busy="true"]::after { | |
| content: ""; | |
| display: inline-block; | |
| width: 10px; | |
| height: 10px; | |
| margin-left: 8px; | |
| border-radius: 50%; | |
| border: 2px solid currentColor; | |
| border-top-color: transparent; | |
| animation: spin 900ms linear infinite; | |
| } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| .meter { | |
| height: 8px; | |
| border-radius: 999px; | |
| background: var(--surface-2); | |
| overflow: hidden; | |
| margin-top: 8px; | |
| } | |
| .meter > i { | |
| display: block; | |
| height: 100%; | |
| width: var(--value, 48%); | |
| background: var(--accent); | |
| } | |
| .gallery { | |
| display: grid; | |
| grid-template-columns: repeat(4, 1fr); | |
| gap: 10px; | |
| margin-top: 12px; | |
| } | |
| .thumb { | |
| aspect-ratio: 1; | |
| border: 1px solid var(--line); | |
| border-radius: 14px; | |
| background: | |
| radial-gradient(circle at var(--x, 50%) var(--y, 50%), rgba(49,92,114,0.66), transparent 32%), | |
| linear-gradient(var(--angle, 135deg), rgba(139,75,53,0.28), rgba(255,255,255,0.85)); | |
| position: relative; | |
| overflow: hidden; | |
| } | |
| .thumb::after { | |
| content: attr(data-seed); | |
| position: absolute; | |
| right: 8px; | |
| bottom: 6px; | |
| color: rgba(31,31,31,0.54); | |
| font: 700 0.72rem var(--mono); | |
| } | |
| pre { | |
| margin: 0; | |
| padding: 12px; | |
| overflow: auto; | |
| border-radius: 14px; | |
| background: #1f1f1f; | |
| color: #f5f1e9; | |
| font-family: var(--mono); | |
| font-size: 0.82rem; | |
| } | |
| code { font-family: var(--mono); } | |
| .checks { | |
| display: grid; | |
| gap: 8px; | |
| margin-top: 10px; | |
| } | |
| .check { | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| color: var(--muted); | |
| } | |
| .latent { | |
| position: relative; | |
| height: 250px; | |
| border: 1px solid var(--line); | |
| border-radius: 18px; | |
| overflow: hidden; | |
| background: | |
| linear-gradient(90deg, rgba(49,92,114,0.12), transparent), | |
| linear-gradient(0deg, rgba(139,75,53,0.16), transparent), | |
| repeating-linear-gradient(0deg, rgba(31,31,31,0.04), rgba(31,31,31,0.04) 1px, transparent 1px, transparent 24px), | |
| repeating-linear-gradient(90deg, rgba(31,31,31,0.04), rgba(31,31,31,0.04) 1px, transparent 1px, transparent 24px), | |
| #fff; | |
| } | |
| .latent-dot { | |
| position: absolute; | |
| left: var(--lx, 50%); | |
| top: var(--ly, 50%); | |
| width: 20px; | |
| height: 20px; | |
| border-radius: 50%; | |
| background: var(--accent-2); | |
| box-shadow: 0 0 0 10px rgba(139, 75, 53, 0.14); | |
| transform: translate(-50%, -50%); | |
| } | |
| .latent-label { | |
| position: absolute; | |
| left: 14px; | |
| bottom: 12px; | |
| max-width: calc(100% - 28px); | |
| padding: 8px 10px; | |
| border-radius: 12px; | |
| background: rgba(255,255,255,0.82); | |
| color: var(--muted); | |
| font-size: 0.86rem; | |
| } | |
| .flow { | |
| display: grid; | |
| grid-template-columns: repeat(5, minmax(0, 1fr)); | |
| gap: 10px; | |
| margin-top: 12px; | |
| } | |
| .step { | |
| min-height: 78px; | |
| padding: 10px; | |
| border: 1px solid var(--line); | |
| border-radius: 14px; | |
| background: #fff; | |
| } | |
| .step b { display: block; font-size: 0.88rem; } | |
| .step span { color: var(--muted); font-size: 0.82rem; } | |
| footer { | |
| padding: 20px 0 46px; | |
| color: var(--muted); | |
| font-size: 0.92rem; | |
| } | |
| @media (max-width: 880px) { | |
| .figure-board { grid-template-columns: 1fr; } | |
| .card, .card.small { grid-column: span 12; } | |
| .row, .layer { grid-template-columns: 1fr; } | |
| .flow { grid-template-columns: 1fr; } | |
| } | |
| @media print { | |
| body { background: white; } | |
| header, footer { padding: 10px 0; } | |
| .btn, .toolbar { display: none; } | |
| .panel, .card { box-shadow: none; break-inside: avoid; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <header class="wrap"> | |
| <div class="eyebrow"><span class="dot" aria-hidden="true"></span> Hugging Face Space · static HTML · Transformers.js</div> | |
| <h1>Atlas d’interfaces web génératives</h1> | |
| <p class="lead">Cette page propose une figure exploratoire : elle regroupe des composants visibles de l’interface et les relie à des opérations plus profondes — modèle, paramètres, API, bibliothèque et environnement de travail.</p> | |
| <nav class="toolbar" aria-label="Repères"> | |
| <a class="pill" href="#prompt">Prompt</a> | |
| <a class="pill" href="#parametres">Paramètres</a> | |
| <a class="pill" href="#variations">Variations</a> | |
| <a class="pill" href="#assistant">Assistant</a> | |
| <a class="pill" href="#api">API</a> | |
| <a class="pill" href="#local">Modèle local</a> | |
| <a class="pill" href="#latent">Espace latent</a> | |
| <a class="pill" href="#workflow">Workflow</a> | |
| </nav> | |
| </header> | |
| <main class="wrap"> | |
| <section class="figure-board" aria-label="Synthèse des couches"> | |
| <div class="panel"> | |
| <h2>Couches d’une affordance computationnelle</h2> | |
| <p>Une action visible dans le navigateur donne accès à une chaîne de traitement qui reste partiellement masquée.</p> | |
| <div class="layers"> | |
| <div class="layer"> | |
| <strong>Surface visible</strong> | |
| <span>Champ de prompt, bouton de génération, curseur, sortie, historique, galerie.</span> | |
| </div> | |
| <div class="layer"> | |
| <strong>Procédure</strong> | |
| <span>Prétraitement, inférence, paramètres, filtrage, post-traitement, export.</span> | |
| </div> | |
| <div class="layer"> | |
| <strong>Infrastructure</strong> | |
| <span>Modèle, bibliothèque IA, pipeline, navigateur, GPU/CPU, API, espace de déploiement.</span> | |
| </div> | |
| </div> | |
| </div> | |
| <aside class="panel"> | |
| <h2>Repères d’évolution</h2> | |
| <p>Quelques composants du web génératif se stabilisent progressivement dans les interfaces.</p> | |
| <div class="timeline"> | |
| <div class="time-item"><time>2020</time><span><b>API de modèles</b> Intégrer des traitements génératifs dans une application web.</span></div> | |
| <div class="time-item"><time>2021</time><span><b>Spaces / démos</b> Publier et tester un modèle depuis un navigateur.</span></div> | |
| <div class="time-item"><time>2022</time><span><b>Diffusion</b> Généraliser prompt, paramètres et galerie de sorties visuelles.</span></div> | |
| <div class="time-item"><time>2023</time><span><b>Assistants</b> Configurer des instructions, connaissances et actions.</span></div> | |
| <div class="time-item"><time>2024+</time><span><b>WebGPU / local</b> Exécuter certains modèles directement dans le navigateur.</span></div> | |
| </div> | |
| </aside> | |
| </section> | |
| <section class="grid" aria-label="Options d’interfaces UI"> | |
| <article class="card" id="prompt"> | |
| <div class="card-head"> | |
| <div> | |
| <h3>1. Prompt minimal</h3> | |
| <p>Le champ de consigne devient le composant d’entrée dominant.</p> | |
| </div> | |
| <span class="tag">usage</span> | |
| </div> | |
| <div class="ui"> | |
| <label for="promptText">Consigne</label> | |
| <textarea id="promptText">Décrire en trois phrases une affordance computationnelle.</textarea> | |
| <div class="actions"> | |
| <button class="btn" data-action="mock-generate">Générer</button> | |
| <button class="btn secondary" data-action="clear-output" data-target="promptOut">Effacer</button> | |
| </div> | |
| <div id="promptOut" class="output" aria-live="polite">Résultat simulé : le système attend une consigne, puis produit une forme médiatique.</div> | |
| </div> | |
| </article> | |
| <article class="card" id="parametres"> | |
| <div class="card-head"> | |
| <div> | |
| <h3>2. Paramétrage</h3> | |
| <p>La génération devient réglable par modèle, température et longueur.</p> | |
| </div> | |
| <span class="tag">configuration</span> | |
| </div> | |
| <div class="ui"> | |
| <div class="row"> | |
| <div> | |
| <label for="modelSelect">Modèle</label> | |
| <select id="modelSelect"> | |
| <option>modèle léger local</option> | |
| <option>modèle généraliste API</option> | |
| <option>modèle spécialisé image</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label for="formatSelect">Sortie</label> | |
| <select id="formatSelect"> | |
| <option>texte court</option> | |
| <option>liste structurée</option> | |
| <option>JSON</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="row"> | |
| <div> | |
| <label for="temperature">Température : <span id="temperatureValue">0.7</span></label> | |
| <input id="temperature" type="range" min="0" max="1" step="0.1" value="0.7" /> | |
| </div> | |
| <div> | |
| <label for="tokens">Longueur : <span id="tokensValue">128</span> tokens</label> | |
| <input id="tokens" type="range" min="32" max="512" step="32" value="128" /> | |
| </div> | |
| </div> | |
| <div class="output" id="paramOut">Paramètres actifs : modèle léger local · texte court · température 0.7 · 128 tokens.</div> | |
| </div> | |
| </article> | |
| <article class="card" id="variations"> | |
| <div class="card-head"> | |
| <div> | |
| <h3>3. Variations visuelles</h3> | |
| <p>Une galerie rend perceptibles les essais, séries et variations.</p> | |
| </div> | |
| <span class="tag">image</span> | |
| </div> | |
| <div class="ui"> | |
| <label for="imagePrompt">Prompt image</label> | |
| <input id="imagePrompt" type="text" value="interface web générative, formes latentes, diagramme" /> | |
| <div class="row"> | |
| <div> | |
| <label for="guidance">Guidance : <span id="guidanceValue">7</span></label> | |
| <input id="guidance" type="range" min="1" max="12" step="1" value="7" /> | |
| </div> | |
| <div> | |
| <label for="steps">Étapes : <span id="stepsValue">30</span></label> | |
| <input id="steps" type="range" min="10" max="50" step="5" value="30" /> | |
| </div> | |
| </div> | |
| <div class="actions"><button class="btn" data-action="make-gallery">Créer 4 variations</button></div> | |
| <div class="gallery" id="gallery" aria-label="Galerie de variations"> | |
| <div class="thumb" data-seed="A1"></div> | |
| <div class="thumb" data-seed="B2"></div> | |
| <div class="thumb" data-seed="C3"></div> | |
| <div class="thumb" data-seed="D4"></div> | |
| </div> | |
| </div> | |
| </article> | |
| <article class="card" id="assistant"> | |
| <div class="card-head"> | |
| <div> | |
| <h3>4. Assistant configurable</h3> | |
| <p>Les instructions et les ressources transforment un modèle en outil situé.</p> | |
| </div> | |
| <span class="tag">assistant</span> | |
| </div> | |
| <div class="ui"> | |
| <label for="systemPrompt">Instructions</label> | |
| <textarea id="systemPrompt">Tu aides à analyser des interfaces web génératives avec un vocabulaire de SIC, design et HCI.</textarea> | |
| <div class="checks" aria-label="Outils activés"> | |
| <label class="check"><input type="checkbox" checked /> Corpus de textes</label> | |
| <label class="check"><input type="checkbox" checked /> Génération de tableaux</label> | |
| <label class="check"><input type="checkbox" /> Appel à une API externe</label> | |
| </div> | |
| <div class="output">Assistant : instructions + connaissances + outils. L’affordance devient une configuration réutilisable.</div> | |
| </div> | |
| </article> | |
| <article class="card" id="api"> | |
| <div class="card-head"> | |
| <div> | |
| <h3>5. API et documentation</h3> | |
| <p>L’interface lisible par l’humain se double d’une interface programmable.</p> | |
| </div> | |
| <span class="tag">développement</span> | |
| </div> | |
| <div class="ui"> | |
| <pre id="apiCode"><code>const output = await pipe("Classer ces notes de lecture", { | |
| model: "modèle-compatible-web", | |
| temperature: 0.4, | |
| max_new_tokens: 128 | |
| });</code></pre> | |
| <div class="actions"> | |
| <button class="btn secondary" data-action="copy-code">Copier le fragment</button> | |
| </div> | |
| <div id="copyOut" class="output">Une API transforme une affordance en composant réutilisable par un autre outil.</div> | |
| </div> | |
| </article> | |
| <article class="card" id="local"> | |
| <div class="card-head"> | |
| <div> | |
| <h3>6. Modèle local dans le navigateur</h3> | |
| <p>Cette carte utilise Transformers.js pour charger un petit pipeline côté client.</p> | |
| </div> | |
| <span class="tag">transformers.js</span> | |
| </div> | |
| <div class="ui"> | |
| <label for="sentimentText">Texte à analyser</label> | |
| <textarea id="sentimentText">I find this browser-based interface useful and surprisingly clear.</textarea> | |
| <div class="actions"> | |
| <button class="btn" id="runSentiment">Charger le modèle et analyser</button> | |
| <button class="btn secondary" id="resetSentiment">Réinitialiser</button> | |
| </div> | |
| <div id="sentimentOut" class="output" aria-live="polite">Le modèle sera téléchargé au premier usage. Le calcul s’exécute ensuite dans le navigateur.</div> | |
| </div> | |
| </article> | |
| <article class="card" id="latent"> | |
| <div class="card-head"> | |
| <div> | |
| <h3>7. Espace latent</h3> | |
| <p>Les curseurs figurent l’exploration d’un espace de possibilités.</p> | |
| </div> | |
| <span class="tag">imaginaire</span> | |
| </div> | |
| <div class="ui"> | |
| <div class="latent" id="latentSpace"> | |
| <div class="latent-dot" id="latentDot"></div> | |
| <div class="latent-label" id="latentLabel">Position : proximité sémantique moyenne · style neutre.</div> | |
| </div> | |
| <div class="row"> | |
| <div> | |
| <label for="latentX">Axe X : <span id="latentXValue">50</span></label> | |
| <input id="latentX" type="range" min="0" max="100" value="50" /> | |
| </div> | |
| <div> | |
| <label for="latentY">Axe Y : <span id="latentYValue">50</span></label> | |
| <input id="latentY" type="range" min="0" max="100" value="50" /> | |
| </div> | |
| </div> | |
| </div> | |
| </article> | |
| <article class="card" id="workflow"> | |
| <div class="card-head"> | |
| <div> | |
| <h3>8. Chaîne de traitement</h3> | |
| <p>Le web génératif articule plusieurs opérations en un workflow.</p> | |
| </div> | |
| <span class="tag">workflow</span> | |
| </div> | |
| <div class="ui"> | |
| <div class="flow"> | |
| <div class="step"><b>1. Entrée</b><span>texte, image, corpus</span></div> | |
| <div class="step"><b>2. Modèle</b><span>inférence ou API</span></div> | |
| <div class="step"><b>3. Paramètres</b><span>style, seuil, format</span></div> | |
| <div class="step"><b>4. Sortie</b><span>résultat ou variantes</span></div> | |
| <div class="step"><b>5. Réemploi</b><span>export, prototype</span></div> | |
| </div> | |
| <div class="output">L’affordance computationnelle réside dans l’enchaînement praticable : saisir, calculer, ajuster, réutiliser.</div> | |
| </div> | |
| </article> | |
| </section> | |
| </main> | |
| <footer class="wrap"> | |
| <p>Prototype statique pour un Hugging Face Space. Les cartes 1–5 et 7–8 sont des maquettes interactives ; la carte 6 charge un modèle compatible Transformers.js dans le navigateur.</p> | |
| </footer> | |
| <script> | |
| const $ = (selector, scope = document) => scope.querySelector(selector); | |
| const $$ = (selector, scope = document) => Array.from(scope.querySelectorAll(selector)); | |
| function setOutput(id, text, busy = false) { | |
| const el = document.getElementById(id); | |
| if (!el) return; | |
| el.textContent = text; | |
| el.setAttribute("aria-busy", String(busy)); | |
| } | |
| function pseudoSentence(prompt) { | |
| const words = prompt.trim().split(/\s+/).filter(Boolean); | |
| const subject = words.slice(0, 4).join(" ") || "La consigne"; | |
| return `${subject} devient ici une entrée opératoire. L’interface transforme la demande en chaîne de traitement, puis en résultat médiatique réutilisable.`; | |
| } | |
| document.addEventListener("click", async (event) => { | |
| const button = event.target.closest("[data-action]"); | |
| if (!button) return; | |
| const action = button.dataset.action; | |
| if (action === "mock-generate") { | |
| setOutput("promptOut", "Calcul simulé…", true); | |
| window.setTimeout(() => { | |
| setOutput("promptOut", pseudoSentence($("#promptText").value)); | |
| }, 420); | |
| } | |
| if (action === "clear-output") { | |
| setOutput(button.dataset.target, ""); | |
| } | |
| if (action === "make-gallery") { | |
| $$("#gallery .thumb").forEach((thumb, index) => { | |
| const seed = Math.random().toString(36).slice(2, 4).toUpperCase() + (index + 1); | |
| thumb.dataset.seed = seed; | |
| thumb.style.setProperty("--x", `${12 + Math.random() * 76}%`); | |
| thumb.style.setProperty("--y", `${12 + Math.random() * 76}%`); | |
| thumb.style.setProperty("--angle", `${Math.floor(Math.random() * 180)}deg`); | |
| }); | |
| } | |
| if (action === "copy-code") { | |
| const text = $("#apiCode").innerText; | |
| try { | |
| await navigator.clipboard.writeText(text); | |
| setOutput("copyOut", "Fragment copié dans le presse-papiers."); | |
| } catch (error) { | |
| setOutput("copyOut", "Copie impossible dans ce navigateur. Sélectionner le code manuellement."); | |
| } | |
| } | |
| }); | |
| function updateParams() { | |
| const temperature = $("#temperature").value; | |
| const tokens = $("#tokens").value; | |
| $("#temperatureValue").textContent = temperature; | |
| $("#tokensValue").textContent = tokens; | |
| setOutput( | |
| "paramOut", | |
| `Paramètres actifs : ${$("#modelSelect").value} · ${$("#formatSelect").value} · température ${temperature} · ${tokens} tokens.` | |
| ); | |
| } | |
| ["#temperature", "#tokens", "#modelSelect", "#formatSelect"].forEach((selector) => { | |
| $(selector).addEventListener("input", updateParams); | |
| $(selector).addEventListener("change", updateParams); | |
| }); | |
| function updateImageParams() { | |
| $("#guidanceValue").textContent = $("#guidance").value; | |
| $("#stepsValue").textContent = $("#steps").value; | |
| } | |
| ["#guidance", "#steps"].forEach((selector) => { | |
| $(selector).addEventListener("input", updateImageParams); | |
| }); | |
| function updateLatent() { | |
| const x = Number($("#latentX").value); | |
| const y = Number($("#latentY").value); | |
| $("#latentXValue").textContent = x; | |
| $("#latentYValue").textContent = y; | |
| $("#latentDot").style.setProperty("--lx", `${x}%`); | |
| $("#latentDot").style.setProperty("--ly", `${y}%`); | |
| const style = x < 33 ? "documentaire" : x > 66 ? "spéculatif" : "analytique"; | |
| const density = y < 33 ? "faible densité" : y > 66 ? "haute densité" : "densité moyenne"; | |
| $("#latentLabel").textContent = `Position : style ${style} · ${density} · variation potentielle.`; | |
| } | |
| ["#latentX", "#latentY"].forEach((selector) => { | |
| $(selector).addEventListener("input", updateLatent); | |
| }); | |
| let classifier = null; | |
| async function getClassifier() { | |
| if (classifier) return classifier; | |
| const { pipeline } = await import("https://cdn.jsdelivr.net/npm/@huggingface/transformers@3.8.1"); | |
| classifier = await pipeline( | |
| "sentiment-analysis", | |
| "Xenova/distilbert-base-uncased-finetuned-sst-2-english" | |
| ); | |
| return classifier; | |
| } | |
| $("#runSentiment").addEventListener("click", async () => { | |
| const button = $("#runSentiment"); | |
| const text = $("#sentimentText").value.trim(); | |
| if (!text) { | |
| setOutput("sentimentOut", "Ajouter un texte à analyser."); | |
| return; | |
| } | |
| button.disabled = true; | |
| setOutput("sentimentOut", "Chargement du modèle et analyse en cours…", true); | |
| try { | |
| const pipe = await getClassifier(); | |
| const result = await pipe(text); | |
| const first = Array.isArray(result) ? result[0] : result; | |
| const percent = Math.round((first.score || 0) * 100); | |
| setOutput("sentimentOut", `Résultat : ${first.label} · confiance ${percent} %. Le traitement a été exécuté côté navigateur.`); | |
| } catch (error) { | |
| console.error(error); | |
| setOutput("sentimentOut", "Erreur de chargement ou d’exécution. Vérifier la connexion, le navigateur et la disponibilité du modèle."); | |
| } finally { | |
| button.disabled = false; | |
| } | |
| }); | |
| $("#resetSentiment").addEventListener("click", () => { | |
| $("#sentimentText").value = "I find this browser-based interface useful and surprisingly clear."; | |
| setOutput("sentimentOut", "Le modèle sera téléchargé au premier usage. Le calcul s’exécute ensuite dans le navigateur."); | |
| }); | |
| </script> | |
| </body> | |
| </html> | |