feat(agent): v4.11.1 - branche entrées IA sur le FlowDeck Agent (Ask AI, AI meeting note, bouton flottant)
This commit is contained in:
@@ -509,13 +509,13 @@
|
||||
<span class="gs-label">Get started with</span>
|
||||
<button
|
||||
class="gs-pill"
|
||||
@click="replaceBlock(0, 'paragraph');blocks[0].content='Ask AI to write...';render()"
|
||||
@click="askAI()"
|
||||
>
|
||||
<span class="gs-icon">{{ fd_icon("sparkles",14) }}</span> Ask AI
|
||||
</button>
|
||||
<button
|
||||
class="gs-pill"
|
||||
@click="replaceBlock(0, 'heading_1');blocks[0].content='AI Meeting Note';render()"
|
||||
@click="meetingNote()"
|
||||
>
|
||||
<span class="gs-icon">{{ fd_icon('edit',14) }}</span> AI meeting note
|
||||
</button>
|
||||
|
||||
@@ -289,6 +289,7 @@
|
||||
updatedAt:'{{ page.get("updated_at", "") }}',
|
||||
fmt:{open:false,top:0,left:0,idx:-1},
|
||||
shareOpen:false,moreOpen:false,gsMoreOpen:false,accessMenuOpen:false,exportOpen:false,
|
||||
aiBusy:false,
|
||||
moveOpen:false,moveWorkspaces:[],moveLoading:false,
|
||||
activityOpen:false,pageCreated:'{{ page.get("created_at", "") }}',
|
||||
shareTab:'share',favorited:false,
|
||||
@@ -391,6 +392,36 @@
|
||||
addAfter(idx,type){return this.addAt(idx+1,type);},
|
||||
removeBlock(idx){this.sync();if(this.blocks.length<=1){this.blocks[0]=this.mkB('paragraph','');}else{this.blocks.splice(idx,1);}const fi=Math.min(idx,this.blocks.length-1);this.render();this.dirty=true;this.autoSave();setTimeout(()=>{const el=this.getEl(this.blocks[fi]?.id);if(el){el.focus();ce(el);}},60);},
|
||||
|
||||
// ── v4.11.1: Inline AI — "Ask AI" & "AI meeting note" (Get started) ──
|
||||
// Wire the contextual get-started actions into the FlowDeck Agent
|
||||
// (Notion-style inline generation). The reply is streamed in the agent
|
||||
// panel and inserted into this page via window.__fdInsertAI.
|
||||
askAI(){
|
||||
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.');
|
||||
},
|
||||
meetingNote(){
|
||||
this.aiRun('Crée le squelette d\'une « AI meeting note » FlowDeck : une ligne d\'en-tête avec la date et les participants, puis les sections « Résumé », « Décisions » et « Action items » avec des exemples réalistes. Sépare chaque bloc/paragraphe par une ligne vide.');
|
||||
},
|
||||
aiRun(prompt){
|
||||
const self=this;
|
||||
if(self.aiBusy) return;
|
||||
if(!(window.fdAgent && window.fdAgent.ask)){ self.showToast('Agent IA indisponible — configurez-le dans Paramètres → Agent & IA'); return; }
|
||||
self.aiBusy=true;
|
||||
window.__fdInsertAI = function(content){ window.__fdInsertAI=null; self.aiBusy=false; self.applyAIBlocks(content); };
|
||||
self.showToast('FlowDeck Agent : génération en cours…');
|
||||
window.fdAgent.ask(prompt);
|
||||
},
|
||||
applyAIBlocks(text){
|
||||
const lines=(text||'').split(/\r?\n/).map(function(s){return s.trim();}).filter(Boolean);
|
||||
if(!lines.length) return;
|
||||
this.sync();
|
||||
const startEmpty=this.blocks.length===1 && this.blocks[0].type==='paragraph' && !(this.blocks[0].content||'').trim();
|
||||
const nb=lines.map(l=>this.mkB('paragraph',l));
|
||||
this.blocks = startEmpty ? nb : this.blocks.concat(nb);
|
||||
this.dirty=true; this.autoSave(); this.render();
|
||||
},
|
||||
|
||||
toggleFavorite(){
|
||||
const csrf=document.cookie.match(/csrf_token=([^;]+)/);
|
||||
const m=this.favorited?'DELETE':'POST';
|
||||
|
||||
@@ -135,6 +135,17 @@
|
||||
});
|
||||
this.fetchProviders();
|
||||
this.fetchAgents();
|
||||
|
||||
// Programmatic entry point used by the page editor ("Ask AI",
|
||||
// "AI meeting note") and any global UI: open the panel and run a prompt.
|
||||
window.fdAgent = window.fdAgent || {};
|
||||
window.fdAgent.ask = function(text){
|
||||
var t = (text || '').trim();
|
||||
if(!t) return;
|
||||
self.openAgent();
|
||||
self.input = t;
|
||||
setTimeout(function(){ self.send(); }, 60);
|
||||
};
|
||||
},
|
||||
|
||||
fetchProviders(){
|
||||
@@ -202,6 +213,30 @@
|
||||
self.messages = []; self.errorMsg=''; self.tab='chat';
|
||||
}).catch(function(){});
|
||||
},
|
||||
ensureConversation(){
|
||||
// Make the chat usable immediately: reuse the open conversation or
|
||||
// create one lazily so sending never silently no-ops (empty state).
|
||||
var self = this;
|
||||
if(self.currentConv && self.currentConv.id) return Promise.resolve(self.currentConv);
|
||||
if(self._ensuring) return self._ensuring;
|
||||
var body = {title:'New conversation'};
|
||||
if(self.llmProvider) body.provider = self.llmProvider;
|
||||
if(self.llmModel) body.model = self.llmModel;
|
||||
self._ensuring = fetch('/api/agent/conversations', {
|
||||
method:'POST', headers:{'Content-Type':'application/json'}, body: JSON.stringify(body)
|
||||
}).then(function(r){return r.json()}).then(function(d){
|
||||
var conv = {id:d.id, title:d.title, updated_at:new Date().toISOString()};
|
||||
self.conversations.unshift(conv); self.currentConv = conv;
|
||||
self.messages = []; self.errorMsg=''; self.tab='chat';
|
||||
self._ensuring = null;
|
||||
return conv;
|
||||
}).catch(function(err){
|
||||
self._ensuring = null;
|
||||
self.errorMsg = (err && err.message) ? err.message : 'Impossible de créer la conversation';
|
||||
throw err;
|
||||
});
|
||||
return self._ensuring;
|
||||
},
|
||||
openAgent(){ this.open=true; this.loadConversations(); },
|
||||
close(){ this.open=false; },
|
||||
get commandOpen(){ return /^\s*\//.test(this.input) && !this.streaming; },
|
||||
@@ -292,12 +327,13 @@
|
||||
}
|
||||
return false;
|
||||
},
|
||||
send(){
|
||||
async send(){
|
||||
var self = this;
|
||||
if(!self.currentConv || self.streaming) return;
|
||||
var text = self.input.trim();
|
||||
if(self.streaming) return;
|
||||
var text = (self.input || '').trim();
|
||||
if(!text) return;
|
||||
if(self.commandOpen && self.handleCommand(text)){ self.input=''; return; }
|
||||
try { await self.ensureConversation(); } catch(e){ return; }
|
||||
self.input = '';
|
||||
self.errorMsg = '';
|
||||
self.streaming = true;
|
||||
@@ -340,6 +376,9 @@
|
||||
}
|
||||
else if(ev.type==='final'){
|
||||
self.messages.push({id:'f-'+Date.now(), role:'assistant', content: ev.content});
|
||||
// "Ask AI" / "AI meeting note" (page editor) register this hook
|
||||
// so the generated text is inserted directly into the page.
|
||||
if(window.__fdInsertAI){ try{ var h=window.__fdInsertAI; window.__fdInsertAI=null; h(ev.content||''); }catch(e){} }
|
||||
}
|
||||
else if(ev.type==='error'){ self.errorMsg = ev.message; }
|
||||
}catch(e){}
|
||||
|
||||
Reference in New Issue
Block a user