fix(ui): standardize AI sidebar height and scrolling behavior in all editor modes
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 5s
All checks were successful
Deploy to Production / Build and Deploy (push) Successful in 5s
This commit is contained in:
@@ -555,7 +555,7 @@ export function ContextualAIChat({
|
||||
/>
|
||||
)}
|
||||
<aside className={cn(
|
||||
'border-l border-border bg-[#FDFCFB] dark:bg-[#0D0D0D] flex flex-col z-10 transition-all duration-300 shadow-2xl',
|
||||
'border-l border-border bg-[#FDFCFB] dark:bg-[#0D0D0D] flex flex-col z-10 transition-all duration-300 shadow-2xl overflow-hidden',
|
||||
expanded
|
||||
? 'fixed right-0 top-0 h-screen w-[640px] z-[200]'
|
||||
: 'self-stretch h-full w-[400px]',
|
||||
@@ -591,7 +591,7 @@ export function ContextualAIChat({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex border-b border-border shrink-0 px-2">
|
||||
<div className="flex border-b border-border shrink-0 px-2 flex-shrink-0 h-12 items-stretch">
|
||||
{[
|
||||
{ id: 'actions' as const, label: t('ai.assistantTabActions'), icon: <Sparkles size={16} /> },
|
||||
{ id: 'chat' as const, label: t('ai.chatTab'), icon: <MessageSquare size={16} /> },
|
||||
@@ -601,7 +601,7 @@ export function ContextualAIChat({
|
||||
key={tab.id}
|
||||
onClick={() => setActiveTab(tab.id as any)}
|
||||
className={cn(
|
||||
"flex-1 py-3 text-[10px] font-bold uppercase tracking-[0.2em] transition-all relative",
|
||||
"flex-1 text-[10px] font-bold uppercase tracking-[0.2em] transition-all relative whitespace-nowrap focus:outline-none",
|
||||
activeTab === tab.id ? 'text-brand-accent' : 'text-concrete hover:text-ink/60'
|
||||
)}
|
||||
>
|
||||
@@ -656,15 +656,16 @@ export function ContextualAIChat({
|
||||
</div>
|
||||
)}
|
||||
|
||||
<AnimatePresence mode="wait">
|
||||
<div className="flex-1 flex flex-col min-h-0 relative">
|
||||
<AnimatePresence mode="wait">
|
||||
{activeTab === 'chat' && (
|
||||
<motion.div
|
||||
key="chat"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
className="flex flex-col flex-1 overflow-hidden"
|
||||
>
|
||||
<motion.div
|
||||
key="chat"
|
||||
initial={{ opacity: 0, y: 10 }}
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
className="flex flex-col flex-1 h-full min-h-0 overflow-hidden"
|
||||
>
|
||||
<div className="flex-1 overflow-y-auto p-6 custom-scrollbar space-y-6 min-h-0">
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
@@ -777,17 +778,55 @@ export function ContextualAIChat({
|
||||
)}
|
||||
<div ref={messagesEndRef} />
|
||||
</div>
|
||||
|
||||
<div className="p-6 bg-white/40 dark:bg-black/20 border-t border-border backdrop-blur-xl shrink-0">
|
||||
<div className="relative group/chat">
|
||||
<textarea
|
||||
rows={4}
|
||||
placeholder={t('ai.chatPlaceholder')}
|
||||
className="w-full bg-white/80 dark:bg-white/5 border border-border rounded-[24px] p-5 pr-14 text-sm outline-none focus:border-brand-accent focus:ring-4 ring-brand-accent/5 transition-all resize-none leading-relaxed font-light shadow-inner text-ink"
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onKeyDown={e => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend() }
|
||||
}}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<div className="absolute right-4 bottom-4 flex flex-col gap-2">
|
||||
{isLoading ? (
|
||||
<button onClick={() => stop()} className="p-2.5 bg-rose-500 text-white rounded-xl transition-all hover:scale-110 active:scale-95 shadow-lg shadow-rose-500/20">
|
||||
<Square size={16} />
|
||||
</button>
|
||||
) : (
|
||||
<button onClick={handleSend} disabled={!input.trim()} className="p-2.5 bg-brand-accent text-white rounded-xl transition-all hover:scale-110 active:scale-95 shadow-lg shadow-brand-accent/20 disabled:opacity-40 disabled:pointer-events-none">
|
||||
<Send size={16} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="absolute left-6 bottom-4 flex gap-3 text-concrete/40">
|
||||
<button
|
||||
onClick={() => webSearchAvailable && setWebSearch(!webSearch)}
|
||||
className={cn("hover:text-brand-accent transition-colors", webSearch && "text-brand-accent")}
|
||||
>
|
||||
<Globe size={14} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-center mt-4">
|
||||
<p className="text-[9px] text-concrete/40 uppercase tracking-[0.3em] font-bold">{t('ai.newLineHint') || "Maj+Entrée = nouvelle ligne"}</p>
|
||||
</div>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{activeTab === 'actions' && (
|
||||
<motion.div
|
||||
key="actions"
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -20 }}
|
||||
className="flex flex-col flex-1 overflow-y-auto p-6 space-y-10 custom-scrollbar"
|
||||
>
|
||||
<motion.div
|
||||
key="actions"
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -20 }}
|
||||
className="flex flex-col flex-1 h-full min-h-0 overflow-y-auto p-6 space-y-10 custom-scrollbar"
|
||||
>
|
||||
{notebookId && (
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center gap-2">
|
||||
@@ -1089,21 +1128,18 @@ export function ContextualAIChat({
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center gap-4 py-8 opacity-20">
|
||||
<PenTool size={20} />
|
||||
<span className="text-[9px] font-bold uppercase tracking-[0.3em] whitespace-nowrap italic text-center">{t('nav.workspace')}</span>
|
||||
</div>
|
||||
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{activeTab === 'resource' && (
|
||||
<motion.div
|
||||
key="resource"
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -20 }}
|
||||
className="flex flex-col flex-1 min-h-0 overflow-y-auto p-6 space-y-8 custom-scrollbar"
|
||||
>
|
||||
<motion.div
|
||||
key="resource"
|
||||
initial={{ opacity: 0, x: 20 }}
|
||||
animate={{ opacity: 1, x: 0 }}
|
||||
exit={{ opacity: 0, x: -20 }}
|
||||
className="flex flex-col flex-1 h-full min-h-0 overflow-y-auto p-6 space-y-8 custom-scrollbar"
|
||||
>
|
||||
<div className="space-y-6">
|
||||
<div className="space-y-3">
|
||||
<label className="text-[10px] uppercase tracking-[0.2em] font-bold text-concrete">{t('ai.resource.urlLabel')}</label>
|
||||
@@ -1138,56 +1174,17 @@ export function ContextualAIChat({
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
</AnimatePresence>
|
||||
</div>
|
||||
|
||||
{/* Textarea — only on chat tab */}
|
||||
<AnimatePresence>
|
||||
{activeTab === 'chat' && (
|
||||
<motion.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
exit={{ opacity: 0 }}
|
||||
className="p-6 bg-white/40 dark:bg-black/20 border-t border-border backdrop-blur-xl shrink-0"
|
||||
>
|
||||
<div className="relative group/chat">
|
||||
<textarea
|
||||
rows={4}
|
||||
placeholder={t('ai.chatPlaceholder')}
|
||||
className="w-full bg-white/80 dark:bg-white/5 border border-border rounded-[24px] p-5 pr-14 text-sm outline-none focus:border-brand-accent focus:ring-4 ring-brand-accent/5 transition-all resize-none leading-relaxed font-light shadow-inner text-ink"
|
||||
value={input}
|
||||
onChange={(e) => setInput(e.target.value)}
|
||||
onKeyDown={e => {
|
||||
if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); handleSend() }
|
||||
}}
|
||||
disabled={isLoading}
|
||||
/>
|
||||
<div className="absolute right-4 bottom-4 flex flex-col gap-2">
|
||||
{isLoading ? (
|
||||
<button onClick={() => stop()} className="p-2.5 bg-rose-500 text-white rounded-xl transition-all hover:scale-110 active:scale-95 shadow-lg shadow-rose-500/20">
|
||||
<Square size={16} />
|
||||
</button>
|
||||
) : (
|
||||
<button onClick={handleSend} disabled={!input.trim()} className="p-2.5 bg-brand-accent text-white rounded-xl transition-all hover:scale-110 active:scale-95 shadow-lg shadow-brand-accent/20 disabled:opacity-40 disabled:pointer-events-none">
|
||||
<Send size={16} />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
<div className="absolute left-6 bottom-4 flex gap-3 text-concrete/40">
|
||||
<button
|
||||
onClick={() => webSearchAvailable && setWebSearch(!webSearch)}
|
||||
className={cn("hover:text-brand-accent transition-colors", webSearch && "text-brand-accent")}
|
||||
>
|
||||
<Globe size={14} />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-center mt-4">
|
||||
<p className="text-[9px] text-concrete/40 uppercase tracking-[0.3em] font-bold">Shift+Enter for new line</p>
|
||||
</div>
|
||||
</motion.div>
|
||||
)}
|
||||
</AnimatePresence>
|
||||
{/* Shared footer for all tabs to ensure consistent bottom spacing */}
|
||||
<div className="flex flex-col items-center gap-4 py-8 opacity-20 mt-auto shrink-0 border-t border-border/10">
|
||||
<PenTool size={20} />
|
||||
<span className="text-[9px] font-bold uppercase tracking-[0.3em] whitespace-nowrap italic text-center">
|
||||
{t('nav.workspace') || 'Momento Workspace'}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{autoLabelOpen && notebookId && (
|
||||
|
||||
@@ -43,7 +43,7 @@ export function NoteEditorDialog({ onClose }: NoteEditorDialogProps) {
|
||||
<Dialog open={true} onOpenChange={onClose}>
|
||||
<DialogContent
|
||||
className={cn(
|
||||
'!max-w-[min(95vw,1600px)] max-h-[90vh] overflow-hidden p-0 flex flex-row items-stretch rounded-lg',
|
||||
'!max-w-[min(95vw,1600px)] h-[90vh] overflow-hidden p-0 flex flex-row items-stretch rounded-lg',
|
||||
state.colorClasses
|
||||
)}
|
||||
>
|
||||
|
||||
@@ -45,7 +45,7 @@ export function NoteEditorFullPage({ onClose }: NoteEditorFullPageProps) {
|
||||
return (
|
||||
<>
|
||||
{/* ── outer container ── */}
|
||||
<div className="h-full flex items-stretch overflow-hidden transition-all duration-500">
|
||||
<div className="h-screen flex items-stretch overflow-hidden transition-all duration-500">
|
||||
|
||||
{/* ── main scrollable column ── */}
|
||||
<div className="flex-1 flex flex-col overflow-y-auto bg-white dark:bg-background">
|
||||
|
||||
Reference in New Issue
Block a user