diff --git a/memento-note/components/contextual-ai-chat.tsx b/memento-note/components/contextual-ai-chat.tsx
index bc6d5b0..bf3a704 100644
--- a/memento-note/components/contextual-ai-chat.tsx
+++ b/memento-note/components/contextual-ai-chat.tsx
@@ -462,11 +462,14 @@ export function ContextualAIChat({
?
:
{content}
}
- {/* Hover-actions — visible only on assistant messages */}
- {isAssistant && onApplyToNote && (
+ {/* Inject buttons — always visible on last assistant msg */}
+ {isAssistant && onApplyToNote && (() => {
+ const lastAssistantId = messages.filter(m => m.role === 'assistant').at(-1)?.id
+ const alwaysShow = msg.id === lastAssistantId
+ return (
handleInjectFromChat(content, 'replace')}
@@ -490,7 +493,8 @@ export function ContextualAIChat({
{t('ai.injectMerge')}
- )}
+ )
+ })()}
)
@@ -560,6 +564,54 @@ export function ContextualAIChat({
+ {/* ══ Inject Preview Panel — fixed, always visible, NO scroll needed ══ */}
+ {resourceEnriching && (
+
+
+ Traitement IA en cours...
+
+ )}
+ {resourcePreview && !resourceEnriching && (
+
+
+
+ {resourcePreview.source === 'complete' ? 'Complété par IA'
+ : resourcePreview.source === 'merge' ? 'Fusionné par IA'
+ : 'Aperçu'}
+
+ setResourcePreview(null)} className="text-muted-foreground hover:text-foreground">
+
+
+
+
+
+
+
+ setResourcePreview(null)}
+ className="flex-1 text-[11px] py-1.5 rounded-lg border border-border/60 text-muted-foreground hover:bg-muted transition-colors"
+ >
+ Annuler
+
+ {
+ if (onApplyToNote && resourcePreview) {
+ onApplyToNote(resourcePreview.text)
+ setResourcePreview(null)
+ setResourceText('')
+ toast.success('Appliqué à la note')
+ }
+ }}
+ disabled={!onApplyToNote}
+ className="flex-1 text-[11px] py-1.5 rounded-lg bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors disabled:opacity-50 flex items-center justify-center gap-1"
+ >
+
+ Appliquer à la note
+
+
+
+ )}
+
{/* Scope & Tone Control Area */}
{/* Scope bar */}
diff --git a/memento-note/scripts/fix-chat-buttons.js b/memento-note/scripts/fix-chat-buttons.js
new file mode 100644
index 0000000..dacd77d
--- /dev/null
+++ b/memento-note/scripts/fix-chat-buttons.js
@@ -0,0 +1,54 @@
+const fs = require('fs');
+const path = require('path');
+const filePath = path.join(__dirname, '..', 'components', 'contextual-ai-chat.tsx');
+let src = fs.readFileSync(filePath, 'utf8');
+
+// ─── FIX 1: make inject buttons always visible on last assistant msg ──────────
+// Replace the hover-gated condition with always-visible for last msg
+const OLD_BUTTONS_CONDITION = ` {/* Hover-actions \u2014 visible only on assistant messages */}\r\n {isAssistant && onApplyToNote && (\r\n
`;
+
+const NEW_BUTTONS_CONDITION = ` {/* Inject buttons \u2014 always visible on last assistant msg */}\r\n {isAssistant && onApplyToNote && (() => {\r\n const lastAssistantId = messages.filter(m => m.role === 'assistant').at(-1)?.id\r\n const alwaysShow = msg.id === lastAssistantId\r\n return (\r\n
`;
+
+// Also need to close the IIFE
+const OLD_BUTTONS_CLOSE = `
\r\n )}\r\n
`;
+const NEW_BUTTONS_CLOSE = `
\r\n )\r\n })()}\r\n `;
+
+if (src.includes(OLD_BUTTONS_CONDITION)) {
+ src = src.replace(OLD_BUTTONS_CONDITION, NEW_BUTTONS_CONDITION);
+ console.log('Fix 1a: buttons condition updated');
+} else {
+ console.error('Fix 1a: OLD_BUTTONS_CONDITION not found!');
+ process.exit(1);
+}
+
+if (src.includes(OLD_BUTTONS_CLOSE)) {
+ src = src.replace(OLD_BUTTONS_CLOSE, NEW_BUTTONS_CLOSE);
+ console.log('Fix 1b: buttons IIFE close updated');
+} else {
+ console.error('Fix 1b: OLD_BUTTONS_CLOSE not found!');
+ process.exit(1);
+}
+
+// ─── FIX 2: Move preview panel OUTSIDE the scrollable div ────────────────────
+// The scrollable div ends with: messagesEndRef then
+// We need to:
+// A) Remove the preview block from inside the scrollable area (already was removed in previous sessions since it was added after messagesEndRef)
+// B) Add preview as shrink-0 panel between the scroll div and controls
+
+// Find the closing of the scrollable area + start of controls
+const OLD_SCROLL_END = `
\r\n \r\n\r\n {/* Scope & Tone Control Area */}`;
+const NEW_SCROLL_END = `
\r\n \r\n\r\n {/* \u2550\u2550 Inject Preview Panel \u2014 fixed, always visible, NO scroll needed \u2550\u2550 */}\r\n {resourceEnriching && (\r\n \r\n \r\n Traitement IA en cours... \r\n
\r\n )}\r\n {resourcePreview && !resourceEnriching && (\r\n \r\n
\r\n \r\n {resourcePreview.source === 'complete' ? 'Compl\u00e9t\u00e9 par IA'\r\n : resourcePreview.source === 'merge' ? 'Fusionn\u00e9 par IA'\r\n : 'Aper\u00e7u'}\r\n \r\n setResourcePreview(null)} className="text-muted-foreground hover:text-foreground">\r\n \r\n \r\n
\r\n
\r\n \r\n
\r\n
\r\n setResourcePreview(null)}\r\n className="flex-1 text-[11px] py-1.5 rounded-lg border border-border/60 text-muted-foreground hover:bg-muted transition-colors"\r\n >\r\n Annuler\r\n \r\n {\r\n if (onApplyToNote && resourcePreview) {\r\n onApplyToNote(resourcePreview.text)\r\n setResourcePreview(null)\r\n setResourceText('')\r\n toast.success('Appliqu\u00e9 \u00e0 la note')\r\n }\r\n }}\r\n disabled={!onApplyToNote}\r\n className="flex-1 text-[11px] py-1.5 rounded-lg bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors disabled:opacity-50 flex items-center justify-center gap-1"\r\n >\r\n \r\n Appliquer \u00e0 la note\r\n \r\n
\r\n
\r\n )}\r\n\r\n {/* Scope & Tone Control Area */}`;
+
+if (src.includes(OLD_SCROLL_END)) {
+ src = src.replace(OLD_SCROLL_END, NEW_SCROLL_END);
+ console.log('Fix 2: preview panel moved outside scroll');
+} else {
+ console.error('Fix 2: OLD_SCROLL_END not found!');
+ // Show what we're looking for context
+ const idx = src.indexOf('messagesEndRef');
+ console.error('Context around messagesEndRef:', JSON.stringify(src.slice(idx-20, idx+200)));
+ process.exit(1);
+}
+
+fs.writeFileSync(filePath, src);
+console.log('Done!');