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

This commit is contained in:
Antigravity
2026-05-16 13:06:45 +00:00
parent bd495be965
commit ee8e2bda59
3 changed files with 77 additions and 80 deletions

View File

@@ -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 && (

View File

@@ -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
)}
>

View File

@@ -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">