fix: exercices dans menu GraduationCap + équations KaTeX + refresh liste
Some checks failed
CI / Deploy production (on server) (push) Has been cancelled
CI / Lint, Unit Tests & Build (push) Has been cancelled

- Menu déroulant GraduationCap : Flashcards + Exercices réunis
- Fix: language non défini dans toolbar (useLanguage destructuring)
- Fix: équations 658071 → KaTeX dans exercices (preprocessMathInHtml partagé)
- lib/text/math-preprocess.ts : utilitaire partagé wizard + exercices
- Toast avec bouton 'Voir' pour rafraîchir après création exercices
- emitNoteChange pour rafraîchir la liste
- i18n FR/EN
This commit is contained in:
Antigravity
2026-06-14 20:13:25 +00:00
parent 08d190eb03
commit eff906d187
8 changed files with 98 additions and 97 deletions

View File

@@ -42,7 +42,7 @@ interface NoteEditorToolbarProps {
export function NoteEditorToolbar({ mode, onClose, onToggleAttachments, attachmentsCount }: NoteEditorToolbarProps) {
const { state, actions, note, readOnly, fullPage, notebooks, fileInputRef, richTextEditorRef } = useNoteEditorContext()
const { t } = useLanguage()
const { t, language } = useLanguage()
const [isConverting, setIsConverting] = useState(false)
const [shareOpen, setShareOpen] = useState(false)
const [flashcardsOpen, setFlashcardsOpen] = useState(false)
@@ -236,6 +236,7 @@ export function NoteEditorToolbar({ mode, onClose, onToggleAttachments, attachme
}
const [generatingExercises, setGeneratingExercises] = useState(false)
const [showEduMenu, setShowEduMenu] = useState(false)
const handleGenerateExercises = async () => {
if (generatingExercises) return
setGeneratingExercises(true)
@@ -249,7 +250,16 @@ export function NoteEditorToolbar({ mode, onClose, onToggleAttachments, attachme
if (!res.ok) {
toast.error(data.errorKey === 'ai.featureLocked' ? (t('ai.featureLocked') || 'Plan requis') : (data.error || 'Erreur'))
} else {
toast.success(t('richTextEditor.exercisesGenerated') || `${data.exercises?.length || 0} exercices créés !`)
toast.success(`${data.exercises?.length || 0} ${t('richTextEditor.exercisesGenerated') || 'exercices créés dans ce carnet !'}`, {
action: {
label: t('richTextEditor.seeExercises') || 'Voir',
onClick: () => window.location.reload(),
},
})
// Emit events so the note list refreshes
for (const ex of data.exercises || []) {
emitNoteChange({ type: 'created', noteId: ex.id, notebookId: note.notebookId })
}
}
} catch (e: any) {
toast.error(e.message || 'Erreur')
@@ -389,14 +399,48 @@ export function NoteEditorToolbar({ mode, onClose, onToggleAttachments, attachme
</button>
{!readOnly && (
<button
title={t('flashcards.toolbarGenerate')}
aria-label={t('flashcards.toolbarGenerate')}
onClick={() => setFlashcardsOpen(true)}
className="p-1.5 rounded-full border border-black/20 dark:border-white/20 text-foreground hover:bg-black/5 dark:hover:bg-white/5 transition-all"
>
<GraduationCap size={16} />
</button>
<div className="relative">
<button
title={t('flashcards.toolbarGenerate')}
aria-label={t('flashcards.toolbarGenerate')}
onClick={() => setShowEduMenu(v => !v)}
className="p-1.5 rounded-full border border-black/20 dark:border-white/20 text-foreground hover:bg-black/5 dark:hover:bg-white/5 transition-all"
>
<GraduationCap size={16} />
</button>
{showEduMenu && (
<>
<div className="fixed inset-0 z-40" onClick={() => setShowEduMenu(false)} />
<div className="absolute top-full right-0 mt-1 z-50 w-56 rounded-xl border border-border bg-card shadow-xl overflow-hidden">
<button
onClick={() => { setShowEduMenu(false); setFlashcardsOpen(true) }}
className="w-full flex items-center gap-3 px-4 py-3 hover:bg-muted transition-colors text-left"
>
<div className="p-1.5 rounded-lg bg-purple-50 dark:bg-purple-950/30 text-purple-600 dark:text-purple-400">
<GraduationCap size={16} />
</div>
<div>
<div className="text-sm font-medium">{t('flashcards.toolbarGenerate')}</div>
<div className="text-[10px] text-muted-foreground">{t('flashcards.toolbarGenerateHint') || 'Révision espacée SM-2'}</div>
</div>
</button>
<button
onClick={() => { setShowEduMenu(false); handleGenerateExercises() }}
disabled={generatingExercises}
className="w-full flex items-center gap-3 px-4 py-3 hover:bg-muted transition-colors text-left border-t border-border/30"
>
<div className="p-1.5 rounded-lg bg-brand-accent/10 text-brand-accent">
{generatingExercises ? <Loader2Icon size={16} className="animate-spin" /> : <PenTool size={16} />}
</div>
<div>
<div className="text-sm font-medium">{t('richTextEditor.generateExercises') || 'Générer des exercices'}</div>
<div className="text-[10px] text-muted-foreground">{t('richTextEditor.generateExercisesHint') || '5 exercices + corrigés'}</div>
</div>
</button>
</div>
</>
)}
</div>
)}
{!readOnly && voiceSupported && (