UI Stabilization: Global color theme updates (#75B2D6), AI Assistant styling refactor, and navigation fixes
This commit is contained in:
@@ -107,16 +107,15 @@ export function NoteInlineEditor({
|
||||
const { data: session } = useSession()
|
||||
const [aiAssistantEnabled, setAiAssistantEnabled] = useState(true)
|
||||
const [autoLabelingEnabled, setAutoLabelingEnabled] = useState(true)
|
||||
const [autoSaveEnabled, setAutoSaveEnabled] = useState(true)
|
||||
|
||||
useEffect(() => {
|
||||
if (session?.user?.id) {
|
||||
const userId = session.user.id
|
||||
import('@/app/actions/ai-settings').then(({ getAISettings }) => {
|
||||
getAISettings(userId).then(settings => {
|
||||
setAiAssistantEnabled(settings.paragraphRefactor !== false)
|
||||
setAutoLabelingEnabled(settings.autoLabeling !== false)
|
||||
}).catch(err => console.error("Failed to fetch AI settings", err))
|
||||
})
|
||||
getAISettings(session.user.id).then((settings) => {
|
||||
setAiAssistantEnabled(settings.paragraphRefactor !== false)
|
||||
setAutoLabelingEnabled(settings.autoLabeling !== false)
|
||||
setAutoSaveEnabled(settings.autoSave !== false)
|
||||
}).catch(err => console.error("Failed to fetch AI settings", err))
|
||||
}
|
||||
}, [session?.user?.id])
|
||||
const { labels: globalLabels, addLabel } = useNotebooks()
|
||||
@@ -207,6 +206,10 @@ export function NoteInlineEditor({
|
||||
|
||||
// ── Auto-save (1.5 s debounce, skipContentTimestamp) ─────────────────────
|
||||
const scheduleSave = useCallback(() => {
|
||||
if (!autoSaveEnabled) {
|
||||
setIsDirty(true)
|
||||
return
|
||||
}
|
||||
setIsDirty(true)
|
||||
clearTimeout(saveTimerRef.current)
|
||||
saveTimerRef.current = setTimeout(async () => {
|
||||
@@ -567,10 +570,11 @@ export function NoteInlineEditor({
|
||||
)}
|
||||
|
||||
{previousContent !== null && (
|
||||
<Button variant="ghost" size="icon" className="h-8 w-8 text-amber-500 hover:text-amber-600"
|
||||
<Button variant="ghost" size="sm" className="h-8 gap-1.5 px-2 text-amber-600 hover:text-amber-700 hover:bg-amber-50 dark:hover:bg-amber-950/30 font-medium"
|
||||
title={t('ai.undoAI') }
|
||||
onClick={() => { changeContent(previousContent); setPreviousContent(null); scheduleSave(); toast.info(t('ai.undoApplied') ) }}>
|
||||
<RotateCcw className="h-3.5 w-3.5" />
|
||||
<span className="text-[11px]">{t('general.undo') || 'Annuler'}</span>
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
@@ -601,7 +605,31 @@ export function NoteInlineEditor({
|
||||
{isSaving ? (
|
||||
<><Loader2 className="h-3 w-3 animate-spin" /> {t('notes.saving')}</>
|
||||
) : isDirty ? (
|
||||
<><span className="h-1.5 w-1.5 rounded-full bg-amber-400" /> {t('notes.dirtyStatus')}</>
|
||||
!autoSaveEnabled ? (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-6 px-2 text-[11px] text-amber-600 hover:text-amber-700 hover:bg-amber-50 dark:hover:bg-amber-950/30"
|
||||
onClick={() => {
|
||||
setIsSaving(true)
|
||||
saveInline(note.id, { title, content, checkItems, type: noteType, isMarkdown: showMarkdownPreview && noteType === 'markdown' })
|
||||
.then(() => {
|
||||
setIsSaving(false)
|
||||
setIsDirty(false)
|
||||
toast.success(t('notes.savedStatus'))
|
||||
})
|
||||
.catch(() => {
|
||||
setIsSaving(false)
|
||||
toast.error(t('general.error'))
|
||||
})
|
||||
}}
|
||||
>
|
||||
<span className="h-1.5 w-1.5 rounded-full bg-amber-400 mr-1.5" />
|
||||
{t('notes.saveNow') || 'Enregistrer'}
|
||||
</Button>
|
||||
) : (
|
||||
<><span className="h-1.5 w-1.5 rounded-full bg-amber-400" /> {t('notes.dirtyStatus')}</>
|
||||
)
|
||||
) : (
|
||||
<><Check className="h-3 w-3 text-emerald-500" /> {t('notes.savedStatus')}</>
|
||||
)}
|
||||
@@ -707,13 +735,22 @@ export function NoteInlineEditor({
|
||||
<div className="flex flex-1 flex-col overflow-y-auto px-6 py-5">
|
||||
{/* Title */}
|
||||
<div className="group relative flex items-start gap-2 shrink-0 mb-1">
|
||||
<input
|
||||
type="text"
|
||||
<textarea
|
||||
dir="auto"
|
||||
className="flex-1 bg-transparent text-xl font-semibold tracking-tight text-foreground outline-none placeholder:text-muted-foreground/40"
|
||||
rows={1}
|
||||
className="flex-1 bg-transparent text-xl font-semibold tracking-tight text-foreground outline-none placeholder:text-muted-foreground/40 resize-none overflow-hidden min-h-[1.5em]"
|
||||
placeholder={t('notes.titlePlaceholder') || 'Titre…'}
|
||||
value={title}
|
||||
onChange={(e) => { changeTitle(e.target.value); scheduleSave() }}
|
||||
onChange={(e) => {
|
||||
changeTitle(e.target.value);
|
||||
scheduleSave();
|
||||
e.target.style.height = 'auto';
|
||||
e.target.style.height = e.target.scrollHeight + 'px';
|
||||
}}
|
||||
onFocus={(e) => {
|
||||
e.target.style.height = 'auto';
|
||||
e.target.style.height = e.target.scrollHeight + 'px';
|
||||
}}
|
||||
/>
|
||||
{!title && content.trim().split(/\s+/).filter(Boolean).length >= 5 && (
|
||||
<button type="button"
|
||||
@@ -920,9 +957,20 @@ export function NoteInlineEditor({
|
||||
noteImages={allImages}
|
||||
noteId={note.id}
|
||||
onApplyToNote={(newContent) => {
|
||||
setPreviousContent(content)
|
||||
const current = content
|
||||
setPreviousContent(current)
|
||||
changeContent(newContent)
|
||||
scheduleSave()
|
||||
toast.success(t('ai.appliedToNote') || 'Applied to note', {
|
||||
action: {
|
||||
label: t('general.undo') || 'Undo',
|
||||
onClick: () => {
|
||||
changeContent(current)
|
||||
setPreviousContent(null)
|
||||
scheduleSave()
|
||||
}
|
||||
}
|
||||
})
|
||||
}}
|
||||
onUndoLastAction={previousContent !== null ? () => {
|
||||
changeContent(previousContent)
|
||||
|
||||
Reference in New Issue
Block a user