Files
flowdeck/app/templates/agent_panel.html
T
bruno 1d1cdbd618
FlowDeck CI / test (push) Failing after 3h13m58s
FlowDeck CI / lint (push) Successful in 2m12s
FlowDeck CI / docker (push) Skipped
fix: side peek des bases repasse en vanilla JS + largeur 1100px standard (v7.49.0)
- Panneau peek: les bindings Alpine (x-data absent du conteneur) rendaient
  loovverture et le redimensionnement inoperants -> cblage direct sur le document.
- Helper unique window.fdWirePeekResize (app.js): pointer capture, 300px-90vw,
  clic=fermer, largeur persiste fd_peek_width partagee entre les 4 peeks.
- database-table-container margin:0 (tableau colle a gauche, marge Library).
- .lib-container remonte dans app.css (trash etait pleine largeur), .db-index 1100px.
- ObsiGate verifie sans code: creation .xlsx OK (openpyxl, #186).
2026-10-05 22:47:36 -04:00

451 lines
34 KiB
HTML
Raw 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.
{# ════════════ FlowDeck Agent panel v4.13.0 — Notion-AI-style sidebar ════════════ #}
<style>
#fd-agent-fab{position:fixed;right:22px;bottom:22px;width:54px;height:54px;border-radius:50%;background:var(--text-primary,#37352F);color:var(--bg-primary,#fff);border:none;cursor:pointer;box-shadow:0 8px 26px rgba(0,0,0,.35);display:flex;align-items:center;justify-content:center;z-index:1700;transition:transform .15s,box-shadow .15s}
#fd-agent-fab:hover{transform:scale(1.06);box-shadow:0 12px 34px rgba(0,0,0,.45)}
#fd-agent-panel{position:fixed;top:0;right:0;bottom:0;width:var(--fd-agent-width,470px);max-width:100vw;background:var(--bg-primary,#191919);border-left:1px solid var(--border,rgba(255,255,255,.1));z-index:1850;display:flex;flex-direction:column;box-shadow:-16px 0 48px rgba(0,0,0,.55);transform:translateX(102%);transition:transform .24s cubic-bezier(.16,1,.3,1)}
#fd-agent-panel.open{transform:translateX(0)}
#fd-agent-panel ::-webkit-scrollbar{width:8px;height:8px}
#fd-agent-panel ::-webkit-scrollbar-thumb{background:rgba(128,128,128,.35);border-radius:4px}
#fd-agent-panel ::-webkit-scrollbar-track{background:transparent}
/* Resize handle (left edge) */
.fd-ap-resize{position:absolute;top:0;bottom:0;left:0;width:6px;cursor:col-resize;z-index:5}
.fd-ap-resize::before{content:"";position:absolute;top:0;bottom:0;left:2px;width:2px;background:transparent;border-radius:2px;transition:background .12s ease}
.fd-ap-resize:hover::before,body.fd-ap-resizing .fd-ap-resize::before{background:var(--accent,#2383E2)}
body.fd-ap-resizing{cursor:col-resize!important;user-select:none}
body.fd-ap-resizing *{cursor:col-resize!important}
/* Header */
.fd-ap-head{display:flex;align-items:center;gap:12px;padding:16px 18px 12px}
.fd-ap-logo{width:36px;height:36px;border-radius:50%;background:var(--text-primary,#37352F);color:var(--bg-primary,#fff);display:flex;align-items:center;justify-content:center;flex-shrink:0;box-shadow:0 2px 8px rgba(0,0,0,.25)}
.fd-ap-head-main{flex:1;min-width:0}
.fd-ap-title{font-size:15px;font-weight:600;color:var(--text,#f0f0f0);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fd-ap-sub{font-size:11.5px;color:var(--text-dim,#9aa);margin-top:2px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fd-ap-headbtn{background:none;border:none;color:var(--text-dim,#999);width:30px;height:30px;border-radius:8px;cursor:pointer;font-size:18px;display:flex;align-items:center;justify-content:center;flex-shrink:0}
.fd-ap-headbtn:hover{background:var(--bg-hover,#2b2b2b);color:var(--text,#fff)}
/* Context bar */
.fd-ap-ctx{display:flex;align-items:center;gap:8px;margin:0 16px 8px;padding:7px 11px;border-radius:9px;background:rgba(35,131,226,.09);border:1px solid rgba(35,131,226,.16);font-size:12px;color:var(--text-secondary,#ddd);min-height:18px}
.fd-ap-ctx .fd-ctx-ico{font-size:13px}
.fd-ap-ctx .fd-ctx-lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
/* No-config banner */
.fd-ap-noconfig{padding:8px 14px;font-size:12px;color:#e6b4b4;background:rgba(227,98,98,.1);border:1px solid rgba(227,98,98,.18);margin:0 16px 10px;border-radius:9px;display:flex;gap:6px;flex-wrap:wrap;align-items:center}
.fd-ap-noconfig a{color:#ff9d9d;font-weight:600;text-decoration:none}
.fd-ap-noconfig .fd-k-sub{width:100%;color:var(--text-dim,#aaa)}
/* Tabs */
.fd-ap-tabs{display:flex;gap:2px;padding:0 16px 8px;border-bottom:1px solid var(--border,rgba(255,255,255,.08))}
.fd-ap-tab{flex:1;text-align:center;padding:7px;background:none;border:none;border-radius:8px;color:var(--text-dim,#999);font-size:12.5px;cursor:pointer}
.fd-ap-tab.active{background:var(--bg-tertiary,#242424);color:var(--text,#fff);font-weight:600}
/* Body */
.fd-agent-body{flex:1;overflow-y:auto;padding:12px 16px}
.fd-ap-hist-item{padding:8px 11px;border-radius:9px;cursor:pointer;color:var(--text,#e0e0e0);font-size:13px;border:1px solid transparent;margin-bottom:2px}
.fd-ap-hist-item:hover,.fd-ap-hist-item.active{background:var(--bg-hover,#262626);border-color:var(--border,rgba(255,255,255,.08))}
/* History hover / long-press preview */
.fd-ap-preview{position:fixed;width:290px;max-width:calc(100vw - 24px);background:var(--bg-modal,#1c1c1c);border:1px solid var(--border,rgba(255,255,255,.16));border-radius:12px;box-shadow:0 16px 44px rgba(0,0,0,.6);z-index:1990;padding:11px 13px;font-size:12px;color:var(--text,#e6e6e6);display:none;pointer-events:auto}
.fd-ap-preview.show{display:block}
.fd-ap-preview-title{font-weight:600;font-size:12.5px;margin-bottom:2px;color:var(--text,#fff);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fd-ap-preview-meta{font-size:10px;color:var(--text-dim,#999);margin-bottom:8px}
.fd-ap-preview-empty{font-size:11px;color:var(--text-dim,#aaa);padding:4px 0}
.fd-ap-preview-label{font-size:9.5px;text-transform:uppercase;letter-spacing:.5px;color:var(--text-dim,#999);margin:7px 0 3px}
.fd-ap-preview-txt{white-space:pre-wrap;word-break:break-word;line-height:1.55;max-height:72px;overflow:hidden;color:var(--text-secondary,#d0d0d0)}
/* Empty / welcome state */
.fd-ap-empty{padding:6px 2px 4px}
.fd-ap-empty-title{font-size:14px;font-weight:600;color:var(--text,#eee);margin-bottom:2px}
.fd-ap-empty-desc{font-size:12px;color:var(--text-dim,#999);margin-bottom:12px}
.fd-ap-qa{display:flex;flex-direction:column;gap:6px}
.fd-ap-qa-item{display:flex;align-items:center;gap:10px;text-align:left;background:var(--bg-tertiary,#202020);border:1px solid var(--border,rgba(255,255,255,.07));border-radius:10px;padding:9px 12px;color:var(--text,#e6e6e6);font-size:13px;cursor:pointer;transition:background .12s,border-color .12s}
.fd-ap-qa-item:hover{background:var(--bg-hover,#2a2a2a);border-color:rgba(35,131,226,.4)}
.fd-ap-qa-item .fd-qa-ico{font-size:16px;flex-shrink:0}
.fd-ap-qa-item .fd-qa-new{margin-left:auto;font-size:9px;font-weight:700;color:#fff;background:var(--accent);border-radius:99px;padding:2px 8px;letter-spacing:.3px}
/* Messages */
.fd-agent-msg{display:flex;gap:9px;margin-bottom:14px}
.fd-agent-msg-avatar{width:28px;height:28px;border-radius:50%;background:var(--bg-tertiary,#2b2b2b);display:flex;align-items:center;justify-content:center;font-size:15px;flex-shrink:0}
.fd-agent-msg-assistant .fd-agent-msg-avatar{background:rgba(35,131,226,.18)}
.fd-agent-msg-body{flex:1;min-width:0}
.fd-agent-msg-meta{font-size:10px;color:var(--text-dim,#888);margin-bottom:2px;text-transform:uppercase;letter-spacing:.5px}
.fd-agent-msg-content{font-size:13.5px;color:var(--text,#ececec);white-space:pre-wrap;word-break:break-word;line-height:1.55}
.fd-agent-msg-content a{color:var(--accent)}
.fd-agent-msg-proposal .fd-agent-msg-content{background:rgba(35,131,226,.07);border:1px solid rgba(35,131,226,.22);border-radius:10px;padding:10px 12px}
.fd-proposal-bar{display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:8px}
.fd-proposal-btn{background:var(--bg-tertiary,#242424);border:1px solid var(--border,rgba(255,255,255,.12));color:var(--text,#e6e6e6);border-radius:8px;padding:6px 12px;font-size:12.5px;cursor:pointer}
.fd-proposal-btn:hover{background:var(--bg-hover,#2e2e2e)}
.fd-proposal-btn.primary{background:var(--accent,#2383E2);border-color:transparent;color:#fff;font-weight:600}
.fd-proposal-btn:disabled{opacity:.5;cursor:not-allowed}
.fd-proposal-note{font-size:11px;color:var(--text-dim,#999)}
.fd-agent-thinking{font-size:12.5px;color:var(--text-dim,#999);padding:10px 12px;border-radius:9px;background:rgba(255,255,255,.04);margin:4px 0 8px;font-style:italic}
.fd-ap-err{color:#ef8a8a;font-size:12px;padding:4px 2px}
.fd-ap-notice{color:var(--text-secondary,#ccc);font-size:12px;padding:7px 10px;margin:2px 0 8px;border-radius:9px;background:rgba(128,128,128,.12);border:1px solid var(--border,rgba(255,255,255,.12))}
/* Steps accordion (Notion-style reasoning + actions audit trail) */
.fd-steps{margin:0 0 8px;border:1px solid var(--border,rgba(255,255,255,.11));border-radius:10px;background:rgba(255,255,255,.03);overflow:hidden}
.fd-steps-head{display:flex;align-items:center;gap:7px;width:100%;padding:7px 11px;background:transparent;border:none;color:var(--text-secondary,#ccc);font-size:12px;cursor:pointer;text-align:left}
.fd-steps-head:hover{background:rgba(255,255,255,.05)}
.fd-steps-chev{display:inline-block;font-size:9px;color:var(--text-dim,#999);transition:transform .15s;width:12px;flex-shrink:0;text-align:center}
.fd-steps-chev.down{transform:rotate(90deg)}
.fd-steps-count{font-weight:600;color:var(--text,#eee)}
.fd-steps-elem{font-weight:400;color:var(--text-dim,#999);margin-left:6px}
.fd-steps-body{border-top:1px solid var(--border,rgba(255,255,255,.08));padding:6px 8px 8px;max-height:340px;overflow-y:auto}
.fd-step-thought{margin:3px 0}
.fd-step-thought-head{display:flex;align-items:center;gap:6px;width:100%;border:none;background:transparent;color:var(--text-dim,#b9b9b9);font-size:12px;text-align:left;cursor:pointer;padding:4px 5px;border-radius:7px}
.fd-step-thought-head:hover{background:rgba(128,128,128,.1);color:var(--text,#fff)}
.fd-step-thought-title{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fd-step-thought-title .fd-t-ico{opacity:.85}
.fd-step-thought-body{margin:2px 0 4px 27px;padding:7px 10px;font-size:12px;color:var(--text-secondary,#cfcfcf);background:rgba(128,128,128,.1);border-left:2px solid rgba(35,131,226,.45);border-radius:0 8px 8px 0;white-space:pre-wrap;word-break:break-word;line-height:1.55}
.fd-step-action{display:flex;align-items:flex-start;gap:7px;padding:4px 7px;font-size:12px;color:var(--text-secondary,#c9c9c9)}
.fd-step-action .fd-step-ico{flex-shrink:0;width:14px;text-align:center;font-size:11px;margin-top:1px}
.fd-step-action.success{color:var(--text,#d9d9d9)}
.fd-step-action.success .fd-step-ico{color:#58c47a}
.fd-step-action.error{color:#e78c8c}
.fd-step-action.error .fd-step-ico{color:#ef6a6a}
.fd-step-action-detail{font-size:11px;color:#e0a3a3;display:block;margin-top:1px}
/* Composer */
.fd-ap-composer{padding:10px 14px 14px;border-top:1px solid var(--border,rgba(255,255,255,.09));position:relative}
.fd-ap-box{background:var(--bg-tertiary,#1f1f1f);border:1px solid var(--border,rgba(255,255,255,.13));border-radius:16px;padding:10px 12px 8px;transition:border-color .12s,box-shadow .12s}
.fd-ap-box:focus-within{border-color:rgba(35,131,226,.65);box-shadow:0 0 0 3px rgba(35,131,226,.12)}
.fd-ap-input{width:100%;background:transparent;border:none;outline:none;color:var(--text,#fff);font-size:14px;resize:none;font-family:inherit;line-height:1.6;max-height:220px;box-sizing:border-box;overflow-y:auto;white-space:pre-wrap;word-break:break-word;min-height:26px;position:relative}
.fd-ap-input.empty::before{content:attr(data-placeholder);color:var(--text-dim,#8a8a8a);position:absolute;left:0;top:0;pointer-events:none;white-space:pre-wrap;max-width:100%}
.ment-token{display:inline-flex;align-items:center;gap:4px;vertical-align:middle;background:rgba(35,131,226,.14);border:1px solid rgba(35,131,226,.35);color:var(--text,#eaf2ff);border-radius:6px;padding:0 3px 0 4px;font-size:13px;line-height:1.5;margin:0 1px;max-width:200px;overflow:hidden;white-space:nowrap}
.ment-token .tk-lbl{overflow:hidden;text-overflow:ellipsis}
.ment-token.skill{background:rgba(168,85,247,.16);border-color:rgba(168,85,247,.5);color:#f3e8ff}
.ment-token .tk-ico{flex-shrink:0;opacity:.9}
.fd-ap-toolbar{display:flex;align-items:center;gap:8px;margin-top:6px;flex-wrap:wrap}
.fd-ap-tip{font-size:11px;color:var(--text-dim,#8f8f8f);flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fd-ap-tools{display:flex;align-items:center;gap:6px;margin-left:auto}
.fd-ap-model{display:flex;align-items:center;gap:6px;background:rgba(255,255,255,.05);border:1px solid var(--border);border-radius:99px;padding:4px 10px;font-size:11.5px;color:var(--text-secondary,#ddd);cursor:pointer;white-space:nowrap;max-width:220px;overflow:hidden;text-overflow:ellipsis}
.fd-ap-model:hover{background:var(--bg-hover,#2c2c2c)}
.fd-ap-model.off{opacity:.75}
.fd-ap-send{width:34px;height:34px;border-radius:50%;border:none;background:var(--accent,#2383E2);color:#fff;cursor:pointer;display:flex;align-items:center;justify-content:center;font-size:15px;transition:background .12s,transform .12s;flex-shrink:0}
.fd-ap-send:hover{background:#2c8ceb}
.fd-ap-send:disabled{opacity:.4;cursor:not-allowed;transform:none}
.fd-ap-send:not(:disabled):active{transform:scale(.94)}
/* Model picker popover */
.fd-ap-pop{position:absolute;right:14px;bottom:calc(100% + 6px);width:270px;background:var(--bg-modal,#1c1c1c);border:1px solid var(--border,rgba(255,255,255,.12));border-radius:12px;box-shadow:0 12px 40px rgba(0,0,0,.5);padding:12px;z-index:60}
.fd-ap-pop label{display:block;font-size:10.5px;text-transform:uppercase;letter-spacing:.5px;color:var(--text-dim,#999);margin:8px 0 4px}
.fd-ap-pop select{width:100%;background:var(--bg-tertiary,#232323);border:1px solid var(--border);border-radius:8px;color:var(--text,#fff);font-size:12.5px;padding:7px 8px;outline:none}
.fd-ap-pop a{display:inline-block;margin-top:10px;font-size:12px;color:var(--accent);text-decoration:none}
/* Slash command menu */
.fd-agent-cmd-menu{position:absolute;bottom:calc(100% + 4px);left:0;right:0;background:var(--bg-modal,#1e1e1e);border:1px solid var(--border,rgba(255,255,255,.12));border-radius:11px;max-height:190px;overflow-y:auto;box-shadow:0 -8px 26px rgba(0,0,0,.4);z-index:70;display:none}
.fd-agent-cmd-menu.show{display:block}
.fd-agent-cmd-item{padding:8px 12px;font-size:12px;color:var(--text,#eee);cursor:pointer;border-radius:7px;margin:3px;display:flex;gap:8px;align-items:baseline}
.fd-agent-cmd-item:hover{background:var(--bg-hover,#2b2b2b)}
.fd-agent-cmd-item.focus{background:var(--bg-hover,#2b2b2b);box-shadow:inset 2px 0 0 var(--accent,#2383E2);color:var(--text,#fff)}
.fd-agent-cmd-item b{color:var(--accent,#2383E2);font-family:monospace;white-space:nowrap}
.fd-agent-cmd-item b.k-cmd{color:var(--accent,#2383E2)}
.fd-agent-cmd-item b.k-skill{color:#c084fc}
.fd-agent-cmd-item span{color:var(--text-dim,#999);font-size:11px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
.fd-agent-cmd-sep{font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--text-dim,#888);padding:8px 14px 2px}
/* Action bar under each assistant post */
.fd-agent-msg-actions{display:flex;gap:5px;flex-wrap:wrap;margin-top:7px;align-items:center}
.fd-act-btn{display:inline-flex;align-items:center;justify-content:center;gap:0;background:transparent;border:1px solid var(--border,rgba(255,255,255,.09));color:var(--text-dim,#9aa);border-radius:7px;width:30px;height:26px;font-size:13px;cursor:pointer;transition:background .12s,color .12s,border-color .12s;line-height:1}
.fd-act-btn:hover{background:var(--bg-hover,#262626);color:var(--text,#fff);border-color:rgba(255,255,255,.25)}
.fd-act-btn.on{background:rgba(35,131,226,.18);border-color:rgba(35,131,226,.55);color:#fff}
.fd-act-btn:disabled,.fd-act-btn.disabled{opacity:.4;cursor:not-allowed}
/* Chips (contexte épinglé @ / + / skills) */
.fd-ap-chips{display:flex;flex-direction:column;gap:5px;padding:8px 4px 4px;max-height:92px;overflow-y:auto}
.fd-chip-row{display:flex;align-items:center;gap:6px;flex-wrap:wrap}
.fd-chip{display:inline-flex;align-items:center;gap:6px;background:rgba(35,131,226,.10);border:1px solid rgba(35,131,226,.30);color:var(--text,#e8e8e8);border-radius:99px;padding:3px 5px 3px 9px;font-size:11.5px;max-width:220px}
.fd-chip.skill{background:rgba(168,85,247,.12);border-color:rgba(168,85,247,.38)}
.fd-chip.chip-open{cursor:pointer}
.fd-chip.chip-open:hover{background:rgba(35,131,226,.20);border-color:rgba(35,131,226,.55)}
.fd-chip-ico{flex-shrink:0;font-size:12px}
.fd-chip-lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fd-chip-spacer{flex-basis:100%;height:0}
.fd-chip-x{background:none;border:none;color:var(--text-dim,#aaa);cursor:pointer;font-size:13px;line-height:1;padding:0 4px;border-radius:50%;flex-shrink:0}
.fd-chip-x:hover{color:#fff;background:rgba(255,255,255,.14)}
/* Add button (+ contexte) */
.fd-ap-add{width:30px;height:30px;border-radius:50%;background:var(--bg-tertiary,#262626);border:1px solid var(--border,rgba(255,255,255,.14));color:var(--text,#ddd);cursor:pointer;font-size:17px;display:flex;align-items:center;justify-content:center;flex-shrink:0;line-height:1}
.fd-ap-add:hover{background:var(--bg-hover,#303030);color:#fff}
/* Mention menu (@ / +) */
.fd-ap-mention-menu{position:absolute;bottom:calc(100% + 4px);left:0;right:0;background:var(--bg-modal,#1e1e1e);border:1px solid var(--border,rgba(255,255,255,.12));border-radius:11px;max-height:220px;overflow-y:auto;box-shadow:0 -8px 26px rgba(0,0,0,.4);z-index:70;display:none}
.fd-ap-mention-menu.show{display:block}
.fd-mention-item{display:flex;align-items:center;gap:9px;padding:7px 12px;font-size:12.5px;color:var(--text,#eee);cursor:pointer;border-radius:7px;margin:2px 4px}
.fd-mention-item:hover,.fd-mention-item.focus{background:var(--bg-hover,#2b2b2b)}
.fd-mention-ico{width:24px;text-align:center;flex-shrink:0;font-size:14px}
.fd-mention-main{flex:1;min-width:0;display:flex;flex-direction:column}
.fd-mention-lbl{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fd-mention-sub{font-size:10.5px;color:var(--text-dim,#999);overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
.fd-mention-sep{font-size:10px;text-transform:uppercase;letter-spacing:.6px;color:var(--text-dim,#888);padding:8px 14px 2px}
.fd-mention-empty,.fd-mention-load{padding:11px 12px;font-size:12px;color:var(--text-dim,#aaa);text-align:center}
/* Toast */
.fd-ap-toast{position:absolute;left:14px;right:14px;bottom:calc(100% + 10px);background:var(--bg-modal,#1c1c1c);border:1px solid rgba(35,131,226,.5);color:var(--text,#fff);font-size:12px;border-radius:9px;padding:8px 12px;box-shadow:0 10px 30px rgba(0,0,0,.45);z-index:90;display:flex;gap:8px;align-items:center;justify-content:space-between}
.fd-ap-toast.err{border-color:rgba(227,98,98,.55)}
.fd-ap-toast button{background:none;border:none;color:var(--text-dim);cursor:pointer;font-size:13px;padding:0}
[x-cloak]{display:none!important}
.fd-agent-msg-markdown{line-height:1.6}
.fd-agent-msg-markdown p{margin:0 0 8px}
.fd-agent-msg-markdown h1,.fd-agent-msg-markdown h2,.fd-agent-msg-markdown h3,.fd-agent-msg-markdown h4{margin:12px 0 6px;font-weight:700;line-height:1.25}
.fd-agent-msg-markdown h1{font-size:1.4em}
.fd-agent-msg-markdown h2{font-size:1.25em}
.fd-agent-msg-markdown h3{font-size:1.1em}
.fd-agent-msg-markdown h4{font-size:1em}
.fd-agent-msg-markdown ul,.fd-agent-msg-markdown ol{margin:0 0 8px;padding-left:20px}
.fd-agent-msg-markdown li{margin:2px 0}
.fd-agent-msg-markdown code{background:rgba(127,127,127,.18);border-radius:4px;padding:1px 5px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:.9em}
.fd-agent-msg-markdown pre{background:var(--bg-code,var(--bg-tertiary,#1f1f1f));border:1px solid var(--border,rgba(255,255,255,.12));border-radius:8px;padding:12px;overflow-x:auto;margin:8px 0;color:var(--text,#ececec)}
.fd-agent-msg-markdown pre code{background:none;padding:0;font-size:12px;line-height:1.5}
.fd-md-table-wrap{overflow-x:auto;margin:8px 0}
.fd-md-table{border-collapse:collapse;width:100%;font-size:13px}
.fd-md-table th,.fd-md-table td{border:1px solid var(--border,#333);padding:6px 10px;text-align:left}
.fd-md-table th{background:rgba(127,127,127,.14);font-weight:600}
.fd-md-table tr:nth-child(even) td{background:rgba(127,127,127,.06)}
.fd-md-quote{border-left:3px solid var(--accent,#2383E2);margin:8px 0;padding:2px 12px;color:var(--text-dim)}
.fd-md-quote p{margin:4px 0}
.fd-md-hr{border:none;border-top:1px solid var(--border,#333);margin:12px 0}
.fd-agent-msg-markdown a{color:var(--accent,#2383E2);text-decoration:none}
.fd-agent-msg-markdown a:hover{text-decoration:underline}
</style>
{# ── Global Agent API (available before Alpine for the FAB / shortcut) ── #}
<script data-cfasync="false" nonce="{{ csp_nonce() }}" src="/static/js/agent_panel_1.js?v={{ asset_version }}"></script>
{# ── Floating button ── #}
<button id="fd-agent-fab" title="FlowDeck Agent (Ctrl+J)" onclick="window.fdAgent&&window.fdAgent.toggle()"><svg viewBox="0 0 24 24" width="26" height="26" fill="currentColor" aria-hidden="true"><path d="M12 3l1.9 5.8a2 2 0 0 0 1.3 1.3L21 12l-5.8 1.9a2 2 0 0 0-1.3 1.3L12 21l-1.9-5.8a2 2 0 0 0-1.3-1.3L3 12l5.8-1.9a2 2 0 0 0 1.3-1.3L12 3Z"/></svg></button>
{# ── Panel ── #}
<div id="fd-agent-panel" class="fd-ap" :class="{open: open}" x-data="agentPanel()" @keydown.escape.window="close()" role="dialog" aria-label="Agent FlowDeck">
<div class="fd-ap-resize" x-ref="resizeHandle" role="separator" aria-orientation="vertical" aria-label="Redimensionner le panneau" tabindex="0" title="Glisser pour redimensionner — double-clic pour réinitialiser"></div>
<div class="fd-ap-head">
<span class="fd-ap-logo"><svg viewBox="0 0 24 24" width="19" height="19" fill="currentColor" aria-hidden="true"><path d="M12 3l1.9 5.8a2 2 0 0 0 1.3 1.3L21 12l-5.8 1.9a2 2 0 0 0-1.3 1.3L12 21l-1.9-5.8a2 2 0 0 0-1.3-1.3L3 12l5.8-1.9a2 2 0 0 0 1.3-1.3L12 3Z"/></svg></span>
<div class="fd-ap-head-main">
<div class="fd-ap-title" x-text="currentConv && currentConv.title ? currentConv.title : 'How can I help you today?'"></div>
<div class="fd-ap-sub" x-text="providerModelLabel"></div>
</div>
<button class="fd-ap-headbtn" title="Nouvelle conversation" @click="newConversation()">+</button>
<button class="fd-ap-headbtn" title="Fermer (Ctrl+J)" @click="close()">✕</button>
</div>
<div class="fd-ap-noconfig" x-show="!hasActiveProvider">
<span>⚠️ Aucun fournisseur activé et fonctionnel.</span>
<a href="/settings" target="_blank" @click.stop>Configurer → Agent &amp; IA</a>
<template x-if="unverifiedCount">
<span class="fd-k-sub">🔑 <span x-text="unverifiedCount"></span> clé(s) à tester : Paramètres puis « Tester ».</span>
</template>
</div>
<div class="fd-ap-tabs">
<button class="fd-ap-tab" :class="{active: tab==='chat'}" @click="tab='chat'">Conversation</button>
<button class="fd-ap-tab" :class="{active: tab==='history'}" @click="showHistory()">Historique</button>
</div>
<div class="fd-agent-body">
<template x-if="tab==='history'">
<div>
<template x-for="c in conversations" :key="c.id">
<div class="fd-ap-hist-item" :class="{active: currentConv && currentConv.id===c.id}"
@click="historyClick(c.id)"
@mouseenter="previewConversation(c, $event)" @mouseleave="schedulePreviewHide()"
@touchstart="startLongPress(c, $event)" @touchend="onTouchEnd()"
@touchmove="onTouchCancel()" @touchcancel="onTouchCancel()">
<div x-text="c.title || 'New conversation'"></div>
<div style="font-size:11px;color:var(--text-dim)" x-text="c.updated_at"></div>
</div>
</template>
<div x-show="!conversations.length" style="font-size:13px;color:var(--text-dim);padding:10px">Aucune conversation. Démarrez-en une !</div>
</div>
</template>
<template x-if="tab==='chat'">
<div>
<!-- Welcome / quick actions -->
<div class="fd-ap-empty" x-show="!messages.length && !streaming" x-cloak>
<div class="fd-ap-empty-desc" x-text="quickIntro"></div>
<div class="fd-ap-qa">
<template x-for="q in quickActions" :key="q.label">
<button class="fd-ap-qa-item" type="button" @click="runQuick(q)">
<span class="fd-qa-ico" x-text="q.icon"></span>
<span x-text="q.label"></span>
<span class="fd-qa-new" x-show="q.new" x-cloak>New</span>
</button>
</template>
</div>
</div>
<template x-for="m in messages" :key="m.id">
<div class="fd-agent-msg" :class="'fd-agent-msg-'+(m.role||'user') + (m.proposal ? ' fd-agent-msg-proposal' : '')">
<div class="fd-agent-msg-avatar" x-text="m.role==='assistant'?'🤖':'👤'"></div>
<div class="fd-agent-msg-body">
<div class="fd-agent-msg-meta" x-text="(m.role==='assistant'?'Agent':'Vous')+(m.model?' · '+m.model:'')"></div>
<!-- Steps accordion (reasoning thoughts + tool actions) -->
<div class="fd-steps" x-show="m.steps && m.steps.length" x-cloak>
<button class="fd-steps-head" type="button" @click="toggleSteps(m)">
<span class="fd-steps-chev" :class="{down: m.stepsOpen}">▶</span>
<span class="fd-steps-count" x-text="!m.steps ? '' : (m.steps.length === 1 ? '1 étape' : m.steps.length + ' étapes')"></span>
<span class="fd-steps-elem" x-text="m.generating ? 'en cours…' : (m.stepsOpen ? 'réduire' : 'voir le déroulé')"></span>
</button>
<div class="fd-steps-body" x-show="m.stepsOpen" x-cloak>
<template x-for="(s, si) in m.steps" :key="si">
<div>
<template x-if="s.type==='thought'">
<div class="fd-step-thought">
<button class="fd-step-thought-head" type="button" @click="toggleStep(s)">
<span class="fd-steps-chev" :class="{down: s.open}">▶</span>
<span class="fd-step-thought-title"><span class="fd-t-ico">💭</span> <span x-text="s.title || 'Réflexion'"></span></span>
</button>
<div class="fd-step-thought-body" x-show="s.open" x-text="s.content"></div>
</div>
</template>
<template x-if="s.type==='action'">
<div class="fd-step-action" :class="s.status || 'success'" :title="(s.detail && s.status==='error') ? s.detail : s.tool || ''">
<span class="fd-step-ico" x-text="(s.status==='success') ? '✓' : '✗'"></span>
<div>
<div x-text="s.label"></div>
<div class="fd-step-action-detail" x-show="s.status==='error' && s.detail" x-text="s.detail" x-cloak></div>
</div>
</div>
</template>
</div>
</template>
</div>
</div>
<div class="fd-agent-msg-content" x-show="m.generating" x-cloak>Génération…</div>
<div class="fd-agent-msg-content fd-agent-msg-markdown" x-show="!m.generating && m.role==='assistant' && m.content" x-init="bindMarkdown($el, m)"></div>
<div class="fd-agent-msg-content" x-show="!m.generating && m.role!=='assistant' && m.content" x-text="m.content"></div>
<template x-if="m.proposal">
<div class="fd-proposal-bar">
<button class="fd-proposal-btn primary" type="button" @click="approveProposal(m)" :disabled="m.applied || m.offline">
<span x-text="m.applied ? (m.rejected ? 'Rejetée' : '✓ Appliquée') : '✓ Appliquer au document'"></span>
</button>
<button class="fd-proposal-btn" type="button" @click="rejectProposal(m)" :disabled="m.applied">✕ Rejeter</button>
<span class="fd-proposal-note" x-show="m.offline" x-cloak>Hors-ligne — configurez une clé API pour appliquer la modification.</span>
</div>
</template>
<template x-if="m.role==='assistant' && !m.generating && m.content && !m.act">
<div class="fd-agent-msg-actions">
<button class="fd-act-btn" type="button" @click="copyResponse(m)" title="Copier la réponse">⧉</button>
<button class="fd-act-btn" type="button" @click="insertIntoPage(m)" :disabled="!canInsert" title="Insérer dans la page ouverte">➕</button>
<button class="fd-act-btn" :class="{on: m.fb==='up'}" type="button" @click="feedback(m,'up')" :disabled="!!m.fb && m.fb!=='up'" title="Feedback positif">👍</button>
<button class="fd-act-btn" :class="{on: m.fb==='down'}" type="button" @click="feedback(m,'down')" :disabled="!!m.fb && m.fb!=='down'" title="Feedback négatif">👎</button>
</div>
</template>
</div>
</div>
</template>
<div class="fd-agent-thinking" x-show="streaming && !(_livePh && _livePh.steps && _livePh.steps.length)">L'agent réfléchit et agit… <span x-text="liveReasoning"></span></div>
<div class="fd-ap-notice" x-show="notice" x-cloak x-text="notice"></div>
<div class="fd-ap-err" x-show="errorMsg" x-text="errorMsg"></div>
</div>
</template>
</div>
<div class="fd-ap-composer">
<div style="position:relative;">
<div class="fd-ap-toast" x-show="toastMsg" x-cloak :class="{err: toastErr}">
<span x-text="toastMsg"></span>
<button type="button" @click="toastMsg=''">✕</button>
</div>
<div class="fd-agent-cmd-menu" x-ref="cmdMenu" :class="{show: commandOpen && commandSuggestions.length && !slashDismiss}">
<template x-for="c in commandSuggestions" :key="c.key">
<div>
<div class="fd-agent-cmd-sep" x-show="c._sep" x-text="c._sep"></div>
<div class="fd-agent-cmd-item" x-show="!c._sep" :class="{focus: c._i===cmdFocus}" @click="useSuggestion(c)" @mouseenter="cmdFocus=c._i">
<b :class="c._admin ? 'k-cmd' : 'k-skill'" x-text="(c._admin ? c.cmd : c.cmd)"></b>
<span x-text="c.desc"></span>
</div>
</div>
</template>
</div>
<div class="fd-ap-mention-menu" :class="{show: mentionOpen}" x-ref="mentionMenu">
<div class="fd-mention-load" x-show="mentionLoad" x-cloak>Recherche…</div>
<template x-if="!mentionLoad && !hasMentionItems">
<div class="fd-mention-empty">Aucun élément. Tapez un nom de document, page ou base.</div>
</template>
<template x-if="!mentionLoad">
<template x-for="it in mentionItems" :key="it.token || it.key">
<div>
<div class="fd-mention-sep" x-show="it._sep" x-text="it._sep"></div>
<div class="fd-mention-item" x-show="!it._sep" :class="{focus: it._i===mentionFocus}"
@click="pickMention(it)" @mouseenter="setMentionFocus(it._i)">
<span class="fd-mention-ico" x-text="it.icon"></span>
<div class="fd-mention-main">
<div class="fd-mention-lbl" x-text="it.label"></div>
<div class="fd-mention-sub" x-text="it.sub"></div>
</div>
</div>
</div>
</template>
</template>
</div>
</div>
<div class="fd-ap-chips" x-show="contextChips.length" x-cloak>
<div class="fd-chip-row" x-show="mentionChips.length">
<template x-for="chip in mentionChips" :key="chip.key">
<span class="fd-chip chip-open" :title="chipAction(chip)" @click="openChip(chip)">
<span class="fd-chip-ico" x-text="chip.icon"></span>
<span class="fd-chip-lbl" x-text="chip.label"></span>
<button class="fd-chip-x" type="button" @click.stop="removeChip(chip.token)" title="Retirer du contexte">✕</button>
</span>
</template>
</div>
<div class="fd-chip-row" x-show="skillChips.length">
<template x-for="chip in skillChips" :key="chip.key">
<span class="fd-chip skill">
<span class="fd-chip-ico" x-text="chip.icon"></span>
<span class="fd-chip-lbl" x-text="chip.label"></span>
<button class="fd-chip-x" type="button" @click="removeChip(chip.token)" title="Retirer le skill">✕</button>
</span>
</template>
</div>
</div>
<div class="fd-ap-box">
<div class="fd-ap-input empty" x-ref="cmdInput" contenteditable="true" spellcheck="false" role="textbox" aria-multiline="true"
data-placeholder="Demandez à l'agent… Tapez @ pour référencer une page/base, / pour un skill. Entrée = envoyer, Maj+Entrée = ligne"
@input="onInput()" @keydown="onComposerKeydown($event)" @paste="onPaste($event)"></div>
<div class="fd-ap-toolbar">
<button class="fd-ap-add" type="button" title="Ajouter un élément au contexte (@ / +)" @click="toggleAddPicker()">+</button>
<span class="fd-ap-tip" x-text="composerHint"></span>
<div class="fd-ap-tools">
<button class="fd-ap-model" :class="{off: !hasActiveProvider}" type="button" @click="modelOpen=!modelOpen" title="Choisir fournisseur et modèle">
<span x-text="autoLabel"></span> <span style="opacity:.6">▾</span>
</button>
<button class="fd-ap-send" @click="send()" :disabled="streaming || !canSend" title="Envoyer">▲</button>
</div>
</div>
</div>
<div class="fd-ap-pop" x-show="modelOpen" @click.outside="modelOpen=false" x-cloak>
<label>Fournisseur</label>
<select x-model="llmProvider" @change="onProviderChange()">
<template x-for="p in providers" :key="p.id">
<option :value="p.id" x-text="p.id==='offline' ? 'Hors-ligne (mock)' : p.name"></option>
</template>
</select>
<label>Modèle</label>
<select x-model="llmModel" @change="patchConversation()" x-show="llmProvider!=='offline'">
<template x-for="m in providerModels" :key="m">
<option :value="m" x-text="m"></option>
</template>
</select>
<a href="/settings" target="_blank" @click="modelOpen=false">⚙️ Configurer (clés API) →</a>
</div>
</div>
{# Historique : aperçu (hover / long-press) #}
<div class="fd-ap-preview" x-ref="convPreview" :class="{show: previewVisible}"
@mouseenter="cancelPreviewHide()" @mouseleave="schedulePreviewHide()" x-cloak>
<div class="fd-ap-preview-title" x-text="previewTitle"></div>
<div class="fd-ap-preview-meta" x-text="previewMeta"></div>
<div class="fd-ap-preview-empty" x-show="!previewStart && !previewEnd" x-text="previewEmptyHint"></div>
<div class="fd-ap-preview-label" x-show="previewStart" x-cloak>Début</div>
<div class="fd-ap-preview-txt" x-show="previewStart" x-text="previewStart"></div>
<div class="fd-ap-preview-label" x-show="previewEnd" x-cloak>Réponse</div>
<div class="fd-ap-preview-txt" x-show="previewEnd" x-text="previewEnd"></div>
</div>
</div>
<script data-cfasync="false" nonce="{{ csp_nonce() }}" src="/static/js/agent_panel_2.js?v={{ asset_version }}"></script>