diff --git a/CHANGELOG.md b/CHANGELOG.md index efcb39b..b106e80 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,16 @@ # Changelog - FlowDeck +## v7.79.0 (2026-10-22) — Panneau Spark v2 (Lot 1) + +- **Panneau Spark** (`draco-panneau.js/css`, vendangés dans les 4 + apps) : réponses de l'agent en **Markdown rendu** (HTML du + service d'agents 0.10.0, sanitizé côté serveur), rapports de + missions compris ; **largeur réglable** persistée ; **thème + clair/sombre aligné** sur le thème de Flowdeck + (`html[data-theme]`, suivi en direct) ; **discussions façon + Muse** : discussion principale perpétuelle + parallèles + créées/archivées/supprimées depuis le tiroir du panneau. + ## v7.78.0 (2026-10-22) — MCP : périmètre du jeton (enfin) ### Corrigé diff --git a/VERSION b/VERSION index 45d57c6..33654fc 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -7.78.0 +7.79.0 diff --git a/static/css/draco-panneau.css b/static/css/draco-panneau.css index 743bc07..d337013 100644 --- a/static/css/draco-panneau.css +++ b/static/css/draco-panneau.css @@ -1,83 +1,189 @@ -/* Panneau Spark — branchement au service d'agents (Fusion, jalon J1). - Composant d'intégration TikTak : le panneau définitif sera vendangé - depuis dracodev-commons ; celui-ci prouve le chemin panneau → service. */ +/* Panneau Spark — service d'agents (Fusion, J1 → Panneau v2 Lot 1). + Thème clair/sombre piloté par [data-draco-theme] sur le panneau : + le JS le cale sur le thème de l'app hôte (html[data-theme], repli + prefers-color-scheme) et suit les bascules en direct. Toutes les + couleurs passent par les variables ci-dessous — en-tête et fenêtre + partagent donc toujours le même mode. */ .draco-panneau { - position: fixed; top: 0; right: 0; height: 100dvh; width: 388px; max-width: 94vw; - background: #fbfbfa; color: #242624; z-index: 400; + --p-fond: #fbfbfa; --p-surface: #ffffff; --p-bord: #e4e5e0; + --p-texte: #242624; --p-doux: #6c7268; --p-faint: #8a8f84; + --p-chip: #f4f5f1; --p-chip-bord: #d6d8d1; --p-chip-survol: #e9ebe4; + --p-agent: #eef0ea; --p-champ: #ffffff; --p-code: #e8eae3; + --p-appro-fond: #fdf6ec; --p-danger-fond: #f6e3de; --p-danger: #a03a28; + --p-accent: #7c5cd6; --p-lien: #5b3df0; + position: fixed; top: 0; right: 0; height: 100dvh; width: 388px; max-width: 96vw; + background: var(--p-fond); color: var(--p-texte); z-index: 400; box-shadow: -12px 0 36px rgb(0 0 0 / .22); display: flex; flex-direction: column; transform: translateX(105%); transition: transform .28s ease; font: 14px/1.5 system-ui, "Segoe UI", sans-serif; } +.draco-panneau[data-draco-theme="dark"] { + --p-fond: #17181a; --p-surface: #1f2023; --p-bord: #33353a; + --p-texte: #eceeeb; --p-doux: #a6aba0; --p-faint: #83887e; + --p-chip: #26272b; --p-chip-bord: #3a3c41; --p-chip-survol: #2e3036; + --p-agent: #26272b; --p-champ: #1f2023; --p-code: #2c2e33; + --p-appro-fond: #2a2118; --p-danger-fond: #40241f; --p-danger: #e8a08d; + --p-lien: #b39df5; +} .draco-panneau.open { transform: translateX(0); } +.draco-poignee { + position: absolute; left: 0; top: 0; bottom: 0; width: 8px; + cursor: ew-resize; z-index: 5; touch-action: none; +} +.draco-poignee:hover { background: color-mix(in srgb, var(--p-accent) 35%, transparent); } +@media (max-width: 700px) { .draco-poignee { display: none; } } .draco-panneau-hd { display: flex; align-items: center; gap: 9px; - padding: 13px 15px; border-bottom: 1px solid #e4e5e0; background: #fff; + padding: 13px 15px; border-bottom: 1px solid var(--p-bord); background: var(--p-surface); } .draco-panneau-hd .titre { font-weight: 700; font-size: 15px; } -.draco-panneau-hd .soustitre { font-size: 12px; color: #6c7268; } +.draco-panneau-hd .soustitre { font-size: 12px; color: var(--p-doux); } .draco-panneau-hd .fermer { - margin-left: auto; border: 0; background: #eef0ea; border-radius: 8px; - width: 30px; height: 30px; cursor: pointer; font-size: 14px; + margin-left: auto; border: 0; background: var(--p-agent); color: var(--p-texte); + border-radius: 8px; width: 30px; height: 30px; cursor: pointer; font-size: 14px; } .draco-panneau-etat { display: flex; align-items: center; gap: 7px; font-size: 12.5px; } .draco-panneau-etat .point { width: 9px; height: 9px; border-radius: 50%; background: #b9bdb2; flex: none; } .draco-panneau-etat.ok .point { background: #2ea36b; } .draco-panneau-etat.err .point { background: #cf4b32; } -.draco-panneau-outils { display: flex; gap: 8px; padding: 9px 15px; border-bottom: 1px solid #e4e5e0; background: #fff; } -.draco-panneau-outils button { - border: 1px solid #d6d8d1; background: #f4f5f1; border-radius: 999px; - padding: 5px 12px; font: 600 12.5px system-ui; cursor: pointer; +/* Barre des discussions (façon Muse) */ +.draco-disc-bar { + display: flex; align-items: center; gap: 7px; + padding: 8px 15px; border-bottom: 1px solid var(--p-bord); background: var(--p-surface); } -.draco-panneau-outils button:hover { background: #e9ebe4; } +.draco-disc-bar .d-titre { + flex: 1; font-weight: 600; font-size: 13px; min-width: 0; + white-space: nowrap; overflow: hidden; text-overflow: ellipsis; +} +.draco-disc-bar .d-btn { + border: 1px solid var(--p-chip-bord); background: var(--p-chip); color: var(--p-texte); + border-radius: 999px; padding: 4px 10px; font: 600 12px system-ui; cursor: pointer; flex: none; +} +.draco-disc-bar .d-btn:hover { background: var(--p-chip-survol); } +.draco-discussions { + border-bottom: 1px solid var(--p-bord); background: var(--p-surface); + max-height: 46%; overflow-y: auto; padding: 8px 12px; +} +.draco-discussions .d-section { + font-size: 11px; font-weight: 700; text-transform: uppercase; + letter-spacing: .5px; color: var(--p-doux); margin: 8px 2px 4px; +} +.draco-discussions .d-section:first-child { margin-top: 0; } +.draco-discussions .d-item { + border: 1px solid transparent; border-radius: 9px; padding: 6px 8px; cursor: pointer; +} +.draco-discussions .d-item:hover { background: var(--p-chip); } +.draco-discussions .d-item.actif { border-color: var(--p-accent); background: var(--p-chip); } +.draco-discussions .d-meta { font-weight: 600; font-size: 13px; } +.draco-discussions .d-sous { font-size: 12px; color: var(--p-doux); } +.draco-discussions .d-actions { display: flex; gap: 6px; margin-top: 5px; } +.draco-discussions .d-actions button { + border: 1px solid var(--p-chip-bord); background: var(--p-surface); color: var(--p-texte); + border-radius: 7px; padding: 3px 9px; font: 600 11.5px system-ui; cursor: pointer; +} +.draco-discussions .d-actions button.danger { color: var(--p-danger); border-color: var(--p-danger-fond); } +.draco-discussions .d-vide, .draco-discussions .d-chargement { + font-size: 12.5px; color: var(--p-doux); padding: 2px 8px 6px; +} +.draco-panneau-outils { display: flex; gap: 8px; padding: 9px 15px; border-bottom: 1px solid var(--p-bord); background: var(--p-surface); } +.draco-panneau-outils button { + border: 1px solid var(--p-chip-bord); background: var(--p-chip); color: var(--p-texte); + border-radius: 999px; padding: 5px 12px; font: 600 12.5px system-ui; cursor: pointer; +} +.draco-panneau-outils button:hover { background: var(--p-chip-survol); } .draco-appro { - align-self: stretch; background: #fdf6ec; border: 1.6px solid #e8a13c; + align-self: stretch; background: var(--p-appro-fond); border: 1.6px solid #e8a13c; border-radius: 12px; padding: 10px 12px; } .draco-appro .titre { font-weight: 700; font-size: 12px; color: #9a6a1c; text-transform: uppercase; letter-spacing: .4px; } +.draco-panneau[data-draco-theme="dark"] .draco-appro .titre { color: #e8bc7a; } .draco-appro .resume { margin: 5px 0 8px; } .draco-appro .btns { display: flex; gap: 7px; } -.draco-appro .btns button { border: 0; border-radius: 8px; padding: 6px 11px; font: 600 12.5px system-ui; cursor: pointer; background: #eef0ea; } +.draco-appro .btns button { border: 0; border-radius: 8px; padding: 6px 11px; font: 600 12.5px system-ui; cursor: pointer; background: var(--p-agent); color: var(--p-texte); } .draco-appro .btns button.go { background: #2ea36b; color: #fff; } -.draco-auj { align-self: stretch; background: #fff; border: 1px solid #e4e5e0; border-radius: 12px; padding: 10px 12px; } -.draco-auj h5 { font-size: 12px; text-transform: uppercase; letter-spacing: .4px; color: #6c7268; margin: 6px 0 4px; } +.draco-auj { align-self: stretch; background: var(--p-surface); border: 1px solid var(--p-bord); border-radius: 12px; padding: 10px 12px; } +.draco-auj h5 { font-size: 12px; text-transform: uppercase; letter-spacing: .4px; color: var(--p-doux); margin: 6px 0 4px; } .draco-auj h5:first-child { margin-top: 0; } .draco-auj ul { margin: 0; padding-left: 17px; } -.draco-auj .etat-source { color: #6c7268; font-size: 12.5px; } +.draco-auj .etat-source { color: var(--p-doux); font-size: 12.5px; } .draco-panneau-msgs { flex: 1; overflow-y: auto; padding: 14px 15px; display: flex; flex-direction: column; gap: 9px; } .draco-msg { max-width: 88%; padding: 9px 12px; border-radius: 13px; white-space: pre-wrap; word-wrap: break-word; } .draco-msg.user { align-self: flex-end; background: #2f6fed; color: #fff; border-bottom-right-radius: 4px; } -.draco-msg.agent { align-self: flex-start; background: #eef0ea; border-bottom-left-radius: 4px; } -.draco-msg.sys { align-self: center; background: none; color: #6c7268; font-size: 12.5px; text-align: center; max-width: 96%; } -.draco-panneau-ft { border-top: 1px solid #e4e5e0; padding: 11px 12px; background: #fff; } +.draco-msg.agent { align-self: flex-start; background: var(--p-agent); border-bottom-left-radius: 4px; } +.draco-msg.sys { align-self: center; background: none; color: var(--p-doux); font-size: 12.5px; text-align: center; max-width: 96%; } +.draco-panneau-ft { border-top: 1px solid var(--p-bord); padding: 11px 12px; background: var(--p-surface); } .draco-panneau-form { display: flex; gap: 8px; } .draco-panneau-form input { - flex: 1; border: 1px solid #d6d8d1; border-radius: 10px; padding: 9px 11px; font: inherit; + flex: 1; border: 1px solid var(--p-chip-bord); border-radius: 10px; padding: 9px 11px; + font: inherit; background: var(--p-champ); color: var(--p-texte); } .draco-panneau-form button { - border: 0; border-radius: 10px; background: #7c5cd6; color: #fff; + border: 0; border-radius: 10px; background: var(--p-accent); color: #fff; padding: 9px 14px; font: inherit; font-weight: 600; cursor: pointer; } .draco-panneau-form button:disabled { opacity: .5; cursor: default; } -.draco-panneau-note { font-size: 11.5px; color: #8a8f84; margin-top: 8px; text-align: center; } -.draco-missions { align-self: stretch; background: #fff; border: 1px solid #e4e5e0; border-radius: 12px; padding: 10px 12px; } -.draco-missions h5 { font-size: 12px; text-transform: uppercase; letter-spacing: .4px; color: #6c7268; margin: 0 0 6px; } -.draco-missions label { display: block; font-size: 12px; font-weight: 600; color: #6c7268; margin: 8px 0 3px; } +.draco-panneau-note { font-size: 11.5px; color: var(--p-faint); margin-top: 8px; text-align: center; } +.draco-missions { align-self: stretch; background: var(--p-surface); border: 1px solid var(--p-bord); border-radius: 12px; padding: 10px 12px; } +.draco-missions h5 { font-size: 12px; text-transform: uppercase; letter-spacing: .4px; color: var(--p-doux); margin: 0 0 6px; } +.draco-missions label { display: block; font-size: 12px; font-weight: 600; color: var(--p-doux); margin: 8px 0 3px; } .draco-missions select, .draco-missions input, .draco-missions textarea { - width: 100%; box-sizing: border-box; border: 1px solid #d6d8d1; border-radius: 8px; - padding: 7px 9px; font: inherit; font-size: 13px; background: #fff; + width: 100%; box-sizing: border-box; border: 1px solid var(--p-chip-bord); border-radius: 8px; + padding: 7px 9px; font: inherit; font-size: 13px; background: var(--p-champ); color: var(--p-texte); } .draco-missions .btns { display: flex; gap: 7px; margin-top: 10px; } -.draco-missions .btns button { border: 0; border-radius: 8px; padding: 6px 11px; font: 600 12.5px system-ui; cursor: pointer; background: #eef0ea; } -.draco-missions .btns button.go { background: #7c5cd6; color: #fff; } +.draco-missions .btns button { border: 0; border-radius: 8px; padding: 6px 11px; font: 600 12.5px system-ui; cursor: pointer; background: var(--p-agent); color: var(--p-texte); } +.draco-missions .btns button.go { background: var(--p-accent); color: #fff; } .draco-missions .m-liste { margin-top: 12px; display: flex; flex-direction: column; gap: 8px; } -.draco-missions .etat-source { color: #6c7268; font-size: 12.5px; } -.draco-missions .m-item { border: 1px solid #e4e5e0; border-radius: 10px; padding: 8px 10px; } +.draco-missions .etat-source { color: var(--p-doux); font-size: 12.5px; } +.draco-missions .m-item { border: 1px solid var(--p-bord); border-radius: 10px; padding: 8px 10px; } .draco-missions .m-entete { display: flex; justify-content: space-between; gap: 8px; align-items: baseline; } -.draco-missions .m-statut { font-size: 11.5px; font-weight: 700; color: #6c7268; white-space: nowrap; } +.draco-missions .m-statut { font-size: 11.5px; font-weight: 700; color: var(--p-doux); white-space: nowrap; } .draco-missions .m-statut.st-succeeded { color: #2ea36b; } .draco-missions .m-statut.st-failed { color: #cf4b32; } .draco-missions .m-statut.st-running, .draco-missions .m-statut.st-paused_question { color: #9a6a1c; } -.draco-missions .m-meta { font-size: 12px; color: #6c7268; } +.draco-missions .m-meta { font-size: 12px; color: var(--p-doux); } .draco-missions .m-rapport { font-size: 12.5px; margin-top: 5px; white-space: pre-wrap; word-wrap: break-word; } -.draco-missions .m-annuler { margin-top: 6px; border: 0; border-radius: 7px; padding: 4px 9px; font: 600 12px system-ui; cursor: pointer; background: #f6e3de; color: #a03a28; } +.draco-missions .m-annuler { margin-top: 6px; border: 0; border-radius: 7px; padding: 4px 9px; font: 600 12px system-ui; cursor: pointer; background: var(--p-danger-fond); color: var(--p-danger); } +/* ── Rendu Markdown (HTML produit par le service) ──────────── */ +.draco-msg.md, .draco-missions .m-rapport.md { white-space: normal; } +.draco-msg.md > *:first-child, .m-rapport.md > *:first-child { margin-top: 0; } +.draco-msg.md > *:last-child, .m-rapport.md > *:last-child { margin-bottom: 0; } +.draco-msg.md p, .m-rapport.md p { margin: 6px 0; } +.draco-msg.md h1, .draco-msg.md h2, .draco-msg.md h3, +.draco-msg.md h4, .draco-msg.md h5, .draco-msg.md h6 { margin: 10px 0 4px; line-height: 1.3; } +.draco-msg.md h1 { font-size: 16.5px; } .draco-msg.md h2 { font-size: 15.5px; } +.draco-msg.md h3 { font-size: 14.5px; } .draco-msg.md h4, +.draco-msg.md h5, .draco-msg.md h6 { font-size: 13.5px; } +.draco-msg.md ul, .draco-msg.md ol, .m-rapport.md ul, .m-rapport.md ol { margin: 6px 0; padding-left: 20px; } +.draco-msg.md li, .m-rapport.md li { margin: 2px 0; } +.draco-msg.md code, .m-rapport.md code { + background: var(--p-code); border-radius: 5px; padding: 1px 5px; + font: 12.5px ui-monospace, "Cascadia Mono", Consolas, monospace; +} +.draco-msg.md pre, .m-rapport.md pre { + background: var(--p-code); border-radius: 9px; padding: 9px 11px; + overflow-x: auto; margin: 7px 0; +} +.draco-msg.md pre code, .m-rapport.md pre code { background: none; padding: 0; } +.draco-msg.md blockquote, .m-rapport.md blockquote { + border-left: 3px solid var(--p-accent); margin: 7px 0; padding: 2px 0 2px 11px; + color: var(--p-doux); +} +.draco-msg.md blockquote.callout, .m-rapport.md blockquote.callout { + border-radius: 0 8px 8px 0; padding: 6px 11px; color: var(--p-texte); + background: var(--p-chip); +} +.draco-msg.md blockquote.callout-warning { border-left-color: #e8a13c; } +.draco-msg.md blockquote.callout-danger { border-left-color: #cf4b32; } +.draco-msg.md blockquote.callout-tip { border-left-color: #2ea36b; } +.draco-msg.md a, .m-rapport.md a { color: var(--p-lien); } +.draco-msg.md table, .m-rapport.md table { + border-collapse: collapse; margin: 7px 0; font-size: 12.5px; + display: block; overflow-x: auto; max-width: 100%; +} +.draco-msg.md th, .draco-msg.md td, .m-rapport.md th, .m-rapport.md td { + border: 1px solid var(--p-bord); padding: 4px 8px; text-align: left; +} +.draco-msg.md hr, .m-rapport.md hr { border: 0; border-top: 1px solid var(--p-bord); margin: 9px 0; } +.draco-msg.md input[type="checkbox"], .m-rapport.md input[type="checkbox"] { margin-right: 5px; } diff --git a/static/js/draco-panneau.js b/static/js/draco-panneau.js index 1eeeb55..5a0d8ec 100644 --- a/static/js/draco-panneau.js +++ b/static/js/draco-panneau.js @@ -1,43 +1,129 @@ -/* Panneau Spark — branchement au service d'agents (Fusion, J1 → J5). +/* Panneau Spark — branchement au service d'agents (Fusion, J1 → Panneau v2). * * Parle à l'API du service d'agents (contrat de dracodev-commons) : * GET /api/v1/me — identité (cookie draco_session) * POST /api/v1/chat — conversation réelle du noyau (J2) : - * LLM DeepSeek + outils MCP des apps - * GET /api/v1/conversations/… — historique de la conversation - * GET /api/v1/executors — registre (noyau, sous-agents J3, + * LLM DeepSeek + outils MCP des apps ; + * la réponse arrive avec son HTML + * Markdown rendu côté service (v2) + * GET /api/v1/conversations — discussions façon Muse (v2) : une + * principale perpétuelle + des + * parallèles créées/archivées/ + * supprimées par l'utilisateur + * GET /api/v1/conversations/… — historique d'une discussion (les + * messages agent portent leur html) + * GET /api/v1/executors — registre (noyau, sous-agents J3, * externes J4 : état de configuration) - * POST /api/v1/missions — missions longues (J5) : lancement, - * suivi, rapport, annulation + * POST /api/v1/missions — missions longues (J5) + * Panneau v2 (Lot 1) : rendu Markdown, largeur réglable à la souris + * (persistée), thème clair/sombre aligné sur l'app hôte + * (html[data-theme], repli prefers-color-scheme, suivi en direct). * États honnêtes : service injoignable / session non reconnue / LLM non * configuré / exécuteur non configuré sont affichés tels quels. */ (function () { "use strict"; var API = "https://agent.dracodev.net"; var convId = null; - try { convId = sessionStorage.getItem("draco_panneau_conv") || null; } catch (e) { /* stockage indispo */ } + var convKind = null; // 'principale' | 'parallele' + var convTitre = ""; var etatConnu = false; - var historiqueCharge = false; var root = null, msgsEl = null, etatEl = null, inputEl = null, sendBtn = null; + var titreEl = null, tiroirEl = null; function domaine() { try { return localStorage.getItem("draco_domain") || "pro"; } catch (e) { return "pro"; } } + function stockerConv() { + try { + if (convId) sessionStorage.setItem("draco_panneau_conv", convId); + else sessionStorage.removeItem("draco_panneau_conv"); + } catch (e) { /* stockage indisponible */ } + } + function convStockee() { + try { return sessionStorage.getItem("draco_panneau_conv") || null; } + catch (e) { return null; } + } + + /* ── Thème : suivre l'app hôte ─────────────────────────────── */ + function themeHote() { + var t = document.documentElement.getAttribute("data-theme"); + if (t === "dark" || t === "light") return t; + if (document.documentElement.classList.contains("dark") || + document.body.classList.contains("dark")) return "dark"; + try { + if (window.matchMedia("(prefers-color-scheme: dark)").matches) return "dark"; + } catch (e) { /* pas de matchMedia */ } + return "light"; + } + function appliquerTheme() { + if (root) root.setAttribute("data-draco-theme", themeHote()); + } + function surveillerTheme() { + try { + new MutationObserver(appliquerTheme).observe(document.documentElement, + { attributes: true, attributeFilter: ["data-theme", "class"] }); + window.matchMedia("(prefers-color-scheme: dark)") + .addEventListener("change", appliquerTheme); + } catch (e) { /* surveillance facultative */ } + } + + /* ── Largeur réglable ──────────────────────────────────────── */ + function largeurStockee() { + try { + var v = parseInt(localStorage.getItem("draco_panneau_largeur") || "", 10); + return isNaN(v) ? null : v; + } catch (e) { return null; } + } + function appliquerLargeur() { + if (!root) return; + var v = largeurStockee(); + root.style.width = v ? Math.min(v, Math.round(window.innerWidth * 0.96)) + "px" : ""; + } + function brancherPoignee(poignee) { + poignee.addEventListener("pointerdown", function (ev) { + if (window.innerWidth <= 700) return; // mobile : pleine largeur + ev.preventDefault(); + var x0 = ev.clientX, w0 = root.getBoundingClientRect().width; + function bouger(e2) { + var w = Math.max(300, Math.min(760, w0 + (x0 - e2.clientX))); + root.style.width = w + "px"; + } + function lacher(e2) { + document.removeEventListener("pointermove", bouger); + document.removeEventListener("pointerup", lacher); + var w = Math.round(root.getBoundingClientRect().width); + try { localStorage.setItem("draco_panneau_largeur", String(w)); } catch (e) { /* ok */ } + } + document.addEventListener("pointermove", bouger); + document.addEventListener("pointerup", lacher); + }); + poignee.addEventListener("dblclick", function () { + try { localStorage.removeItem("draco_panneau_largeur"); } catch (e) { /* ok */ } + appliquerLargeur(); + }); + } function construire() { root = document.createElement("aside"); root.className = "draco-panneau"; root.setAttribute("aria-label", "Spark, l'agent commun"); root.innerHTML = + '
' + '