fix(agent): v4.13.1 - actions contenu document (resume/traduire/ameliorer/meeting) sans outils, apercu + appliquer/rejeter, mock ignore contexte
This commit is contained in:
@@ -629,8 +629,23 @@
|
||||
// (Notion-style inline generation). The reply is streamed in the agent
|
||||
// panel and inserted into this page via window.__fdInsertAI.
|
||||
askAI(){
|
||||
const self=this;
|
||||
const title=(this.pageTitle||'Sans titre').trim();
|
||||
this.aiRun('Rédige le contenu de la page FlowDeck intitulée « '+title+' ». Fournis un texte structuré et directement utilisable : paragraphes et, si utile, des listes à puces. Sépare chaque paragraphe par une ligne vide. Ne précède pas le texte de commentaires.');
|
||||
if(!(window.fdAgent && window.fdAgent.generate)){ self.showToast('Agent IA indisponible — configurez-le dans Paramètres → Agent & IA'); return; }
|
||||
if(self.aiBusy) return;
|
||||
self.aiBusy=true;
|
||||
self.showToast('FlowDeck Agent : génération en cours…');
|
||||
let ctx='';
|
||||
try{ if(window.fdGetPageContext) ctx=String(window.fdGetPageContext()||''); }catch(e){}
|
||||
const prompt='Rédige le contenu de la page FlowDeck intitulée « '+title+' ». Fournis un texte structuré et directement utilisable : paragraphes et, si utile, des listes à puces. Sépare chaque paragraphe par une ligne vide. Ne précède pas le texte de commentaires.';
|
||||
window.fdAgent.generate(prompt, ctx).then(function(text){
|
||||
self.aiBusy=false;
|
||||
if(text && text.trim()){ self.applyAIBlocks(text); }
|
||||
else { self.showToast('Aucun retour généré par l\u2019agent'); }
|
||||
}).catch(function(err){
|
||||
self.aiBusy=false;
|
||||
self.showToast((err && err.message) ? err.message : 'Erreur de génération');
|
||||
});
|
||||
},
|
||||
meetingNote(){
|
||||
// v4.12.0: crée une vraie « boîte de réunion » (bloc 'meeting') au lieu
|
||||
|
||||
@@ -55,6 +55,13 @@
|
||||
.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}
|
||||
|
||||
@@ -175,11 +182,20 @@
|
||||
</div>
|
||||
|
||||
<template x-for="m in messages" :key="m.id">
|
||||
<div class="fd-agent-msg" :class="'fd-agent-msg-'+(m.role||'user')">
|
||||
<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>
|
||||
<div class="fd-agent-msg-content" x-text="m.content"></div>
|
||||
<div class="fd-agent-msg-content" x-text="m.generating ? 'Génération…' : 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>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@@ -376,10 +392,10 @@
|
||||
var c = this.activeContext || {};
|
||||
var out = [];
|
||||
if(c.kind === 'page'){
|
||||
out.push({icon:'📄', label:'Résumer ce document', prompt:'Résume ce document de façon structurée et concise.'});
|
||||
out.push({icon:'🌐', label:'Traduire cette page', prompt:'Traduis le contenu de ce document en anglais.', new:false});
|
||||
out.push({icon:'✍️', label:'Proposer des améliorations', prompt:'Analyse ce document et propose des améliorations concrètes de structure et de contenu.'});
|
||||
out.push({icon:'📝', label:'Créer une note de réunion', prompt:'Crée un squelette d\u2019AI meeting note structuré à partir de ce document.'});
|
||||
out.push({icon:'📄', label:'Résumer ce document', prompt:'Résume ce document de façon structurée et concise.', docRun:true, mode:'insert'});
|
||||
out.push({icon:'🌐', label:'Traduire cette page', prompt:'Traduis le contenu de ce document en anglais, en conservant sa structure (titres, listes, paragraphes).', docRun:true, mode:'replace'});
|
||||
out.push({icon:'✍️', label:'Proposer des améliorations', prompt:'Analyse ce document et propose une version améliorée (structure, clarté, complétude) sans en perdre le sens.', docRun:true, mode:'insert'});
|
||||
out.push({icon:'📝', label:'Créer une note de réunion', prompt:'Rédige une « AI Meeting Note » structurée à partir de ce document : en-tête avec date, sections Résumé, Décisions, Action items et Prochaines étapes.', docRun:true, mode:'insert'});
|
||||
} else if(c.kind === 'db'){
|
||||
out.push({icon:'🗄️', label:'Analyser cette base', prompt:'Analyse cette base de données et résume sa structure, ses propriétés et ses données.'});
|
||||
out.push({icon:'📊', label:'Créer une vue', prompt:'Propose une vue adaptée (board, tableau, calendrier…) pour cette base et explique pourquoi.'});
|
||||
@@ -399,6 +415,7 @@
|
||||
},
|
||||
runQuick(q){
|
||||
if(!q || !q.prompt) return;
|
||||
if(q.docRun){ this.runDocAction(q); return; }
|
||||
var self = this;
|
||||
this.ensureConversation().then(function(){
|
||||
self.input = q.prompt;
|
||||
@@ -406,6 +423,63 @@
|
||||
}).catch(function(){});
|
||||
},
|
||||
|
||||
// ── Document actions (no tools) → proposition à approuver / rejeter ──
|
||||
runDocAction(q){
|
||||
var self = this;
|
||||
this.ensureConversation().then(function(){
|
||||
self.errorMsg = '';
|
||||
self.messages.push({id:'udoc-'+Date.now(), role:'user', content: q.label});
|
||||
self.refreshContext();
|
||||
var ctxText = (self.activeContext && self.activeContext.kind === 'page' && self.activeContext.text) ? self.activeContext.text : '';
|
||||
var thinkId = 'g-'+Date.now()+Math.random();
|
||||
self.messages.push({id: thinkId, role:'assistant', generating: true});
|
||||
self.scrollBottom();
|
||||
|
||||
var payload = {prompt: q.prompt};
|
||||
if(ctxText) payload.context = ctxText;
|
||||
if(self.llmProvider) payload.provider = self.llmProvider;
|
||||
if(self.llmModel) payload.model = self.llmModel;
|
||||
|
||||
fetch('/api/agent/generate', {
|
||||
method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(payload)
|
||||
}).then(function(r){ return r.json(); }).then(function(d){
|
||||
self.messages = self.messages.filter(function(m){ return m.id !== thinkId; });
|
||||
if(d && d.ok && d.text){
|
||||
self.messages.push({
|
||||
id:'prop-'+Date.now()+Math.random(), role:'assistant',
|
||||
content: d.text, proposal: true, mode: q.mode || 'insert',
|
||||
offline: !!d.offline, model: d.model || '', applied: false, rejected: false
|
||||
});
|
||||
} else {
|
||||
self.messages.push({id:'err-'+Date.now()+Math.random(), role:'assistant',
|
||||
content: '⚠️ ' + ((d && d.error) || 'Échec de la génération'), model:''});
|
||||
}
|
||||
self.scrollBottom();
|
||||
}).catch(function(err){
|
||||
self.messages = self.messages.filter(function(m){ return m.id !== thinkId; });
|
||||
self.messages.push({id:'err-'+Date.now()+Math.random(), role:'assistant',
|
||||
content: '⚠️ ' + ((err && err.message) || 'Erreur réseau'), model:''});
|
||||
self.scrollBottom();
|
||||
});
|
||||
}).catch(function(){});
|
||||
},
|
||||
approveProposal(m){
|
||||
if(!m || m.applied || m.offline) return;
|
||||
var ok = window.fdApplyDocument ? window.fdApplyDocument(m.content, m.mode) : false;
|
||||
m.applied = true;
|
||||
m.rejected = false;
|
||||
if(!ok){
|
||||
m.content = '⚠️ Aucun document éditable n\u2019est ouvert pour appliquer cette proposition.';
|
||||
}
|
||||
this.$nextTick(function(){ this.scrollBottom(); });
|
||||
},
|
||||
rejectProposal(m){
|
||||
if(!m || m.applied) return;
|
||||
m.applied = true;
|
||||
m.rejected = true;
|
||||
this.$nextTick(function(){ this.scrollBottom(); });
|
||||
},
|
||||
|
||||
// ── Providers / models ──
|
||||
fetchProviders(){
|
||||
var self = this;
|
||||
@@ -832,6 +906,36 @@
|
||||
})();
|
||||
window.FlowDeckCtx = FlowDeckCtx;
|
||||
|
||||
// Apply an AI-produced document change to the open page editor.
|
||||
// mode 'replace' → remplace tout le contenu de la page ;
|
||||
// mode 'insert' → insère la proposition en fin de page (comme Ask AI).
|
||||
window.fdApplyDocument = function(text, mode){
|
||||
var E = window.E;
|
||||
if(!E || !E.blocks || typeof E.mkB !== 'function') return false;
|
||||
text = (text || '').trim();
|
||||
if(!text) return false;
|
||||
try{
|
||||
E.sync();
|
||||
if(mode === 'replace'){
|
||||
var nb = (typeof E.md2b === 'function') ? E.md2b(text) : null;
|
||||
if(!nb || !nb.length){ E.blocks = [E.mkB('paragraph', text)]; }
|
||||
else { E.blocks = nb; }
|
||||
} else {
|
||||
if(typeof E.applyAIBlocks === 'function'){ E.applyAIBlocks(text); }
|
||||
else {
|
||||
var lines = text.split(/\r?\n/).map(function(s){ return s.trim(); }).filter(Boolean);
|
||||
var startEmpty = E.blocks.length === 1 && E.blocks[0].type === 'paragraph' && !(E.blocks[0].content || '').trim();
|
||||
var nb2 = lines.map(function(l){ return E.mkB('paragraph', l); });
|
||||
E.blocks = startEmpty ? nb2 : E.blocks.concat(nb2);
|
||||
}
|
||||
}
|
||||
E.dirty = true;
|
||||
if(E.autoSave) E.autoSave();
|
||||
if(E.render) E.render();
|
||||
return true;
|
||||
}catch(e){ return false; }
|
||||
};
|
||||
|
||||
// Backward-compatible detection hooks (banner + Ask AI)
|
||||
window.fdPageContextProvider = function(){
|
||||
var c = FlowDeckCtx.current();
|
||||
|
||||
Reference in New Issue
Block a user