diff --git a/memento-note/app/api/ai/reformulate/route.ts b/memento-note/app/api/ai/reformulate/route.ts index 0e3143b..eba19c0 100644 --- a/memento-note/app/api/ai/reformulate/route.ts +++ b/memento-note/app/api/ai/reformulate/route.ts @@ -17,7 +17,7 @@ export async function POST(request: NextRequest) { return NextResponse.json({ error: 'Feature disabled' }, { status: 403 }) } - const { text, option, format } = await request.json() + const { text, option, format, language } = await request.json() // Validation if (!text || typeof text !== 'string') { @@ -25,16 +25,19 @@ export async function POST(request: NextRequest) { } // Map option to refactor mode - const modeMap: Record = { + const modeMap: Record = { 'clarify': 'clarify', 'shorten': 'shorten', - 'improve': 'improveStyle' + 'improve': 'improveStyle', + 'fix_grammar': 'fix_grammar', + 'translate': 'translate', + 'explain': 'explain' } const mode = modeMap[option] if (!mode) { return NextResponse.json( - { error: 'Invalid option. Use: clarify, shorten, or improve' }, + { error: 'Invalid option. Use: clarify, shorten, improve, fix_grammar, translate, or explain' }, { status: 400 } ) } @@ -50,7 +53,7 @@ export async function POST(request: NextRequest) { } // Use the ParagraphRefactorService - const result = await paragraphRefactorService.refactor(text, mode, format === 'html' ? 'html' : 'markdown') + const result = await paragraphRefactorService.refactor(text, mode, format === 'html' ? 'html' : 'markdown', language) return NextResponse.json({ originalText: result.original, @@ -66,3 +69,4 @@ export async function POST(request: NextRequest) { ) } } + diff --git a/memento-note/app/globals.css b/memento-note/app/globals.css index 4d395e3..d2911e5 100644 --- a/memento-note/app/globals.css +++ b/memento-note/app/globals.css @@ -1366,6 +1366,9 @@ html.font-system * { color: var(--foreground); transition: background 0.1s ease, transform 0.08s ease; } +[dir="rtl"] .notion-slash-item { + text-align: right; +} .notion-slash-item:hover, .notion-slash-item-selected { background: var(--accent); diff --git a/memento-note/components/ai/ai-settings-panel.tsx b/memento-note/components/ai/ai-settings-panel.tsx index a3d8c25..bc48051 100644 --- a/memento-note/components/ai/ai-settings-panel.tsx +++ b/memento-note/components/ai/ai-settings-panel.tsx @@ -124,8 +124,8 @@ export function AISettingsPanel({ initialSettings }: AISettingsPanelProps) { handleToggle('paragraphRefactor', checked)} /> @@ -167,37 +167,37 @@ export function AISettingsPanel({ initialSettings }: AISettingsPanelProps) { {/* Language Detection Toggle */} handleToggle('languageDetection', checked)} /> {/* Auto Labeling Toggle */} handleToggle('autoLabeling', checked)} /> handleToggle('noteHistory', checked)} /> {settings.noteHistory && (
-

{t('notes.historyMode') || 'Mode d\'historique'}

+

{t('notes.historyMode')}

{ const mode = value as 'manual' | 'auto' setSettings((s) => ({ ...s, noteHistoryMode: mode })) updateAISettings({ noteHistoryMode: mode }).then(() => { - toast.success(t('settings.settingsSaved') || 'Saved') + toast.success(t('settings.settingsSaved')) }) }} className="space-y-2" @@ -206,10 +206,10 @@ export function AISettingsPanel({ initialSettings }: AISettingsPanelProps) {

- {t('notes.historyModeManualDesc') || 'Créer des snapshots avec le bouton commit'} + {t('notes.historyModeManualDesc')}

@@ -217,10 +217,10 @@ export function AISettingsPanel({ initialSettings }: AISettingsPanelProps) {

- {t('notes.historyModeAutoDesc') || 'Snapshots automatiques avec détection intelligente'} + {t('notes.historyModeAutoDesc')}

diff --git a/memento-note/components/contextual-ai-chat.tsx b/memento-note/components/contextual-ai-chat.tsx index dfe377d..14b380d 100644 --- a/memento-note/components/contextual-ai-chat.tsx +++ b/memento-note/components/contextual-ai-chat.tsx @@ -12,7 +12,7 @@ import { Lightbulb, Minimize2, AlignLeft, Wand2, Globe, BookOpen, FileText, RotateCcw, Check, Maximize2, ImageIcon, Link2, Download, ArrowDownToLine, - GitMerge, PlusCircle, Eye, Code, + GitMerge, PlusCircle, Eye, Code, Languages, } from 'lucide-react' import { useLanguage } from '@/lib/i18n' import { MarkdownContent } from '@/components/markdown-content' @@ -64,6 +64,7 @@ const ACTION_IDS = [ { id: 'clarify', icon: Lightbulb, apiPath: '/api/ai/reformulate', body: (content: string) => ({ text: content, option: 'clarify' }), resultKey: 'reformulatedText', i18nKey: 'ai.action.clarify' }, { id: 'shorten', icon: Minimize2, apiPath: '/api/ai/reformulate', body: (content: string) => ({ text: content, option: 'shorten' }), resultKey: 'reformulatedText', i18nKey: 'ai.action.shorten' }, { id: 'improve', icon: AlignLeft, apiPath: '/api/ai/reformulate', body: (content: string) => ({ text: content, option: 'improve' }), resultKey: 'reformulatedText', i18nKey: 'ai.action.improve' }, + { id: 'translate', icon: Languages, apiPath: '/api/ai/reformulate', body: (content: string, _images?: string[], lang?: string) => ({ text: content, option: 'translate', language: lang || 'fr' }), resultKey: 'reformulatedText', i18nKey: 'ai.action.translate' }, { id: 'markdown', icon: Wand2, apiPath: '/api/ai/transform-markdown', body: (content: string) => ({ text: content }), resultKey: 'transformedText', i18nKey: 'ai.action.toMarkdown' }, { id: 'describe-images', icon: ImageIcon, apiPath: '/api/ai/describe-image', body: (_content: string, images?: string[], lang?: string) => ({ imageUrls: images || [], mode: 'description', language: lang || 'fr' }), resultKey: 'descriptions', i18nKey: 'ai.action.describeImages', isImageAction: true }, ] @@ -250,18 +251,18 @@ export function ContextualAIChat({ setResourceScraping(true) try { const result = await scrapePageText(resourceUrl.trim()) - if (!result) { toast.error('Impossible de charger cette URL'); return } + if (!result) { toast.error(t('ai.resource.failedToLoadUrl')); return } setResourceText(result.text) - toast.success(`Page chargée : ${result.title.slice(0, 40)}`) + toast.success(t('ai.resource.pageLoaded', { title: result.title.slice(0, 40) })) } catch { - toast.error('Erreur lors du chargement de la page') + toast.error(t('ai.resource.pageLoadError')) } finally { setResourceScraping(false) } } const handleResourcePreview = async () => { - if (!resourceText.trim()) { toast.error('Collez du texte ou chargez une URL d\'abord'); return } + if (!resourceText.trim()) { toast.error(t('ai.resource.pasteOrUrlFirst')); return } if (resourceMode === 'replace') { setResourcePreview({ text: resourceText, source: 'paste' }) return @@ -279,10 +280,10 @@ export function ContextualAIChat({ }), }) const data = await res.json() - if (!res.ok) throw new Error(data.error || 'Erreur IA') + if (!res.ok) throw new Error(data.error || t('ai.resource.enrichError')) setResourcePreview({ text: data.enrichedContent, source: resourceMode }) } catch (e: any) { - toast.error(e.message || 'Erreur lors de l\'enrichissement') + toast.error(e.message || t('ai.resource.enrichError')) } finally { setResourceEnriching(false) } @@ -294,7 +295,7 @@ export function ContextualAIChat({ setResourcePreview(null) setResourceText('') setResourceUrl('') - toast.success('Contenu appliqué à la note ✓') + toast.success(t('ai.resource.contentApplied')) } /** Called from chat hover-actions: inject a chat message into the note */ @@ -326,7 +327,7 @@ export function ContextualAIChat({ if (!res.ok) throw new Error(data.error || 'Erreur IA') setResourcePreview({ text: data.enrichedContent, source: mode }) } catch (e: any) { - toast.error(e.message || 'Erreur enrichissement') + toast.error(e.message || t('ai.resource.enrichErrorShort')) } finally { setResourceEnriching(false) } @@ -351,7 +352,7 @@ export function ContextualAIChat({

- IA Note + {t('ai.aiNoteTitle')}

{noteTitle ? `"${noteTitle}"` : t('ai.currentNote')} @@ -390,7 +391,7 @@ export function ContextualAIChat({ {([ { id: 'chat', icon: Bot, label: t('ai.chatTab') }, { id: 'actions', icon: Wand2, label: t('ai.noteActions') }, - { id: 'resource', icon: ArrowDownToLine, label: 'Ressource' }, + { id: 'resource', icon: ArrowDownToLine, label: t('ai.resourceTab') }, ] as const).map(tab => (

)} @@ -695,7 +696,7 @@ export function ContextualAIChat({
{t(action.i18nKey)} {noteImages.length > 1 && ( - {noteImages.length} images + {t('ai.imagesCount', { count: noteImages.length })} )}
{loading && {t('ai.processingAction')}} @@ -760,10 +761,10 @@ export function ContextualAIChat({

- {resourcePreview.source === 'chat' ? '💬 Depuis le chat' - : resourcePreview.source === 'replace' ? '↓ Remplacement' - : resourcePreview.source === 'complete' ? '✦ Complété par IA' - : '⟳ Fusionné par IA'} + {resourcePreview.source === 'chat' ? t('ai.resource.fromChat') + : resourcePreview.source === 'replace' ? t('ai.resource.replacement') + : resourcePreview.source === 'complete' ? t('ai.resource.completedByAI') + : t('ai.resource.mergedByAI')}

{/* Format toggle */}
@@ -776,7 +777,7 @@ export function ContextualAIChat({ : 'bg-card text-muted-foreground hover:bg-muted', )} > - Rendu + {t('ai.resource.rendered')}
@@ -832,7 +833,7 @@ export function ContextualAIChat({ {/* URL loader */}