feat(ui): panneau repliable, carnets plus lisibles et accueil plus clair
Harmonise la liste des notes et l’éditeur (titres, dates, retour), rend la saisie rapide et les raccourcis de l’accueil utilisables sur téléphone, et conserve le panneau latéral repliable.
This commit is contained in:
@@ -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<HTMLTextAreaElement>(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 (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-4 min-w-0">
|
||||
{state.quotaExceededFeature === 'auto_title' && (
|
||||
<InlinePaywall
|
||||
feature="auto_title"
|
||||
@@ -38,38 +66,27 @@ export function NoteTitleBlock() {
|
||||
/>
|
||||
)}
|
||||
{/* Title — auto-resizing textarea, adaptive size */}
|
||||
<div className="group relative">
|
||||
<div className="group relative min-w-0">
|
||||
<textarea
|
||||
ref={titleRef}
|
||||
dir={titleDir}
|
||||
lang={titleLang}
|
||||
rows={1}
|
||||
placeholder={t('notes.titlePlaceholder') || 'Untitled…'}
|
||||
value={state.title}
|
||||
onChange={(e) => { actions.setTitle(e.target.value) }}
|
||||
onInput={(e) => {
|
||||
const el = e.currentTarget
|
||||
el.style.height = 'auto'
|
||||
el.style.height = el.scrollHeight + 'px'
|
||||
}}
|
||||
onInput={(e) => autosizeTitle(e.currentTarget)}
|
||||
disabled={readOnly}
|
||||
className={cn(
|
||||
'w-full font-bold border-0 outline-none px-0 bg-transparent text-foreground',
|
||||
'w-full min-w-0 font-bold border-0 outline-none px-0 bg-transparent text-foreground',
|
||||
titleIsRtl
|
||||
? 'font-[family-name:var(--font-sans)] text-right'
|
||||
: 'font-memento-serif text-left',
|
||||
'leading-[1.15] tracking-tight',
|
||||
'leading-snug tracking-tight break-words [overflow-wrap:anywhere] whitespace-pre-wrap',
|
||||
'placeholder:text-foreground/20 resize-none overflow-hidden',
|
||||
titleSizeClass,
|
||||
!readOnly && (titleIsRtl ? 'ps-12' : 'pe-12')
|
||||
)}
|
||||
style={{ height: 'auto' }}
|
||||
ref={(el) => {
|
||||
// Force correct initial height on mount
|
||||
if (el) {
|
||||
el.style.height = 'auto'
|
||||
el.style.height = el.scrollHeight + 'px'
|
||||
}
|
||||
}}
|
||||
/>
|
||||
{/* AI title generation — visible on hover */}
|
||||
{!readOnly && (
|
||||
|
||||
Reference in New Issue
Block a user