@@ -926,7 +936,7 @@ export function NoteEditorToolbar({ mode, onClose, onToggleAttachments, attachme
aria-label={voiceState === 'listening' ? (t('richTextEditor.stopVoice') || 'Stop voice') : (t('richTextEditor.startVoice') || 'Start voice')}
onClick={toggleVoice}
className={cn(
- 'p-1.5 rounded-full border transition-all',
+ 'hidden md:flex h-11 w-11 shrink-0 items-center justify-center rounded-full border transition-all md:h-auto md:w-auto md:p-1.5',
voiceState === 'listening'
? 'border-red-400 bg-red-50 dark:bg-red-950/30 text-red-500 animate-pulse'
: 'border-black/20 dark:border-white/20 text-foreground hover:bg-black/5 dark:hover:bg-white/5',
@@ -941,7 +951,7 @@ export function NoteEditorToolbar({ mode, onClose, onToggleAttachments, attachme
title={t('notes.attachments') || 'Attachments'}
aria-label={t('notes.attachments') || 'Attachments'}
onClick={onToggleAttachments}
- className="relative 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"
+ className="relative hidden md:flex h-11 w-11 shrink-0 items-center justify-center rounded-full border border-black/20 dark:border-white/20 text-foreground hover:bg-black/5 dark:hover:bg-white/5 transition-all md:h-auto md:w-auto md:p-1.5"
>
{(attachmentsCount ?? 0) > 0 && (
@@ -958,7 +968,7 @@ export function NoteEditorToolbar({ mode, onClose, onToggleAttachments, attachme
aria-label={state.showMarkdownPreview ? t('notes.markdownEditingTitle') : t('notes.markdownPreviewTitle')}
onClick={() => actions.setShowMarkdownPreview(!state.showMarkdownPreview)}
className={cn(
- 'p-1.5 rounded-full border transition-all duration-300',
+ 'hidden md:flex h-11 w-11 shrink-0 items-center justify-center rounded-full border transition-all duration-300 md:h-auto md:w-auto md:p-1.5',
state.showMarkdownPreview
? 'bg-foreground text-background border-foreground'
: 'border-black/20 dark:border-white/20 text-foreground hover:bg-black/5 dark:hover:bg-white/5'
@@ -975,7 +985,7 @@ export function NoteEditorToolbar({ mode, onClose, onToggleAttachments, attachme
onClick={handleConvertToRichtext}
disabled={isConverting}
className={cn(
- 'p-1.5 rounded-full border transition-all duration-300',
+ 'hidden md:flex h-11 w-11 shrink-0 items-center justify-center rounded-full border transition-all duration-300 md:h-auto md:w-auto md:p-1.5',
'border-black/20 dark:border-white/20 text-foreground hover:bg-black/5 dark:hover:bg-white/5',
isConverting && 'opacity-50 cursor-not-allowed'
)}
@@ -984,53 +994,123 @@ export function NoteEditorToolbar({ mode, onClose, onToggleAttachments, attachme
)}
- {/* 9. Overflow menu */}
- {!readOnly && (
-
-
-
-
-
-
-
- {t('richTextEditor.exportMarkdown')}
+ {/* 9. Overflow menu — always on mobile (secondary actions), desktop when editable */}
+
+
+
+
+
+ {!readOnly && (
+ setShareOpen(true)}>
+
+ {t('notes.shareNoteTitle')}
-
-
- {t('richTextEditor.exportPdf') || 'Exporter en PDF'}
-
-
-
- {t('richTextEditor.importMarkdown')}
-
-
-
-
-
- {t('settings.autoSave') || 'Auto-enregistrement'}
-
-
- {state.autoSaveEnabled ? (t('common.on') || 'Actif') : (t('common.off') || 'Désactivé')}
-
-
-
- setConfirmDeleteOpen(true)}
- className="text-red-600 dark:text-red-400 focus:text-red-600"
- >
-
- {t('notes.deleteNoteConfirmItem')}
-
-
-
- )}
+ )}
+ { actions.setInfoOpen(!state.infoOpen); actions.setAiOpen(false) }}
+ >
+
+ {t('notes.documentInfoAria')}
+
+ {
+ const title = note.title || ''
+ const summary = state.content?.replace(/<[^>]*>/g, '').slice(0, 200) || ''
+ const seed = title ? `${title}. ${summary}` : summary
+ if (!seed.trim()) return
+ window.open(`/brainstorm?seed=${encodeURIComponent(seed.slice(0, 300))}&sourceNoteId=${note.id}`, '_self')
+ }}
+ >
+
+ {t('notes.brainstormThisIdea')}
+
+ {!readOnly && (
+ <>
+ setFlashcardsOpen(true)}>
+
+ {t('flashcards.toolbarGenerate')}
+
+ void handleGenerateExercises()}>
+
+ {t('richTextEditor.generateExercises') || 'Générer des exercices'}
+
+ {voiceSupported && (
+
+ {voiceState === 'listening' ? : }
+ {voiceState === 'listening'
+ ? (t('editor.voiceStop') || 'Arrêter la dictée')
+ : (t('editor.voiceStart') || 'Dicter du texte')}
+
+ )}
+ {onToggleAttachments && (
+
+
+ {t('notes.attachments') || 'Attachments'}
+
+ )}
+ {state.isMarkdown && (
+ <>
+ actions.setShowMarkdownPreview(!state.showMarkdownPreview)}>
+
+ {state.showMarkdownPreview ? t('notes.markdownEditingTitle') : t('notes.markdownPreviewTitle')}
+
+ void handleConvertToRichtext()}>
+
+ {t('ai.convertToRichtext') || 'Convert to Rich Text'}
+
+ >
+ )}
+
+
+
+ {t('richTextEditor.exportMarkdown')}
+
+
+
+ {t('richTextEditor.exportPdf') || 'Exporter en PDF'}
+
+
+
+ {t('richTextEditor.importMarkdown')}
+
+
+
+
+
+ {t('settings.autoSave') || 'Auto-enregistrement'}
+
+
+ {state.autoSaveEnabled ? (t('common.on') || 'Actif') : (t('common.off') || 'Désactivé')}
+
+
+
+ setConfirmDeleteOpen(true)}
+ className="text-red-600 dark:text-red-400 focus:text-red-600"
+ >
+
+ {t('notes.deleteNoteConfirmItem')}
+
+ >
+ )}
+
+
{ actions.setInfoOpen(!state.infoOpen); actions.setAiOpen(false) }}
className={cn(
- 'p-1.5 rounded-full border transition-all duration-300',
+ 'hidden md:flex h-11 w-11 shrink-0 items-center justify-center rounded-full border transition-all duration-300 md:h-auto md:w-auto md:p-1.5',
state.infoOpen
? 'bg-foreground text-background border-foreground'
: 'border-black/20 dark:border-white/20 text-foreground hover:bg-black/5 dark:hover:bg-white/5'
diff --git a/memento-note/components/note-editor/note-title-block.tsx b/memento-note/components/note-editor/note-title-block.tsx
index 7f5bf925..56ffe375 100644
--- a/memento-note/components/note-editor/note-title-block.tsx
+++ b/memento-note/components/note-editor/note-title-block.tsx
@@ -1,5 +1,6 @@
'use client'
+import { useLayoutEffect, useRef } from 'react'
import { useNoteEditorContext } from './note-editor-context'
import { TitleSuggestions } from '@/components/title-suggestions'
import { Loader2, Sparkles } from 'lucide-react'
@@ -10,27 +11,54 @@ import { toast } from 'sonner'
import { resolveTitleDirection, resolveTitleLang } from '@/lib/clip/rtl-content'
import { InlinePaywall } from '@/components/settings/inline-paywall'
+function autosizeTitle(el: HTMLTextAreaElement | null) {
+ if (!el) return
+ el.style.height = 'auto'
+ el.style.height = `${el.scrollHeight}px`
+}
+
export function NoteTitleBlock() {
const { note, state, actions, readOnly, fullPage } = useNoteEditorContext()
const { t } = useLanguage()
const { requestAiConsent } = useAiConsent()
+ const titleRef = useRef(null)
const titleDir = resolveTitleDirection(state.title || '', note.sourceUrl)
const titleLang = resolveTitleLang(state.title || '', note.sourceUrl)
const titleIsRtl = titleDir === 'rtl'
+ useLayoutEffect(() => {
+ autosizeTitle(titleRef.current)
+ }, [state.title, fullPage])
+
+ useLayoutEffect(() => {
+ const el = titleRef.current
+ if (!el) return
+ const onResize = () => autosizeTitle(el)
+ window.addEventListener('resize', onResize)
+ const parent = el.parentElement
+ const ro = typeof ResizeObserver !== 'undefined' && parent
+ ? new ResizeObserver(onResize)
+ : null
+ if (ro && parent) ro.observe(parent)
+ return () => {
+ window.removeEventListener('resize', onResize)
+ ro?.disconnect()
+ }
+ }, [fullPage])
+
if (fullPage) {
// Adaptive font size: short = big editorial, long = smaller but still premium
const titleLen = (state.title || '').length
const titleSizeClass =
- titleLen === 0 ? 'text-5xl md:text-6xl' :
- titleLen < 40 ? 'text-5xl md:text-6xl' :
- titleLen < 70 ? 'text-4xl md:text-5xl' :
- titleLen < 100 ? 'text-3xl md:text-4xl' :
- 'text-2xl md:text-3xl'
+ titleLen === 0 ? 'text-3xl sm:text-5xl md:text-6xl' :
+ titleLen < 40 ? 'text-3xl sm:text-5xl md:text-6xl' :
+ titleLen < 70 ? 'text-2xl sm:text-4xl md:text-5xl' :
+ titleLen < 100 ? 'text-xl sm:text-3xl md:text-4xl' :
+ 'text-xl sm:text-2xl md:text-3xl'
return (
-
+
{state.quotaExceededFeature === 'auto_title' && (
)}
{/* Title — auto-resizing textarea, adaptive size */}
-