fix(ui): resolve 9 animated design system bugs including Monaco tab height accumulation

- fix(editor): Monaco compose↔.env tab switching no longer accumulates height
  each switch — root cause was Monaco's position:static internal child creating
  a circular CSS dependency with the CSS grid; broken by calling layout({0,0})
  to force reflow before re-measuring. Added overflow-hidden to Monaco container.
- fix(ui): sliding tab indicator on compose/env tabs via motion.div layoutId
- fix(ui): sliding tab indicator on Notification tabs (Discord/Slack/Webhook)
- fix(ui): switch thumb now animates position (Radix data-attribute CSS translation)
- fix(ui): 'Always Local' tooltip no longer crashes — replaced animate-ui tooltip
  (getStrictContext throws outside provider) with pure Radix primitives + CSS animation
- feat(settings): Auto theme option added (light/dark/auto with matchMedia listener)
- fix(ui): NodeManager dialog buttons no longer stuck together (DialogFooter gap)
- fix(ui): AlertDialog spring animation + Quick Clean button text wraps correctly
- fix(ui): Resources/App Store/Logs buttons toggle off on second click
This commit is contained in:
SaelixCode
2026-03-20 23:51:54 -04:00
parent 0cb5fae947
commit 22e646286e
10 changed files with 203 additions and 127 deletions
+43 -14
View File
@@ -1,4 +1,5 @@
import { useState, useEffect, useRef } from 'react';
import { motion } from 'motion/react';
import {
Dialog,
DialogContent,
@@ -14,7 +15,7 @@ import { Badge } from '@/components/ui/badge';
import { toast } from 'sonner';
import { apiFetch } from '@/lib/api';
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select';
import { Shield, Activity, Bell, Palette, Moon, Sun, Code, Server, Package, RefreshCw, Database, Info } from 'lucide-react';
import { Shield, Activity, Bell, Palette, Moon, Sun, Monitor, Code, Server, Package, RefreshCw, Database, Info } from 'lucide-react';
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '@/components/ui/tooltip';
import { NodeManager } from './NodeManager';
import { useNodes } from '@/context/NodeContext';
@@ -41,11 +42,13 @@ interface PatchableSettings {
type SectionId = 'account' | 'system' | 'notifications' | 'appearance' | 'developer' | 'nodes' | 'appstore';
type Theme = 'light' | 'dark' | 'auto';
interface SettingsModalProps {
isOpen: boolean;
onClose: () => void;
isDarkMode: boolean;
setIsDarkMode: (mode: boolean) => void;
theme: Theme;
setTheme: (theme: Theme) => void;
}
const DEFAULT_SETTINGS: PatchableSettings = {
@@ -61,7 +64,7 @@ const DEFAULT_SETTINGS: PatchableSettings = {
log_retention_days: '30',
};
export function SettingsModal({ isOpen, onClose, isDarkMode, setIsDarkMode }: SettingsModalProps) {
export function SettingsModal({ isOpen, onClose, theme, setTheme }: SettingsModalProps) {
const { activeNode } = useNodes();
const isRemote = activeNode?.type === 'remote';
const [activeSection, setActiveSection] = useState<SectionId>('account');
@@ -73,6 +76,9 @@ export function SettingsModal({ isOpen, onClose, isDarkMode, setIsDarkMode }: Se
}
}, [isRemote]); // eslint-disable-line react-hooks/exhaustive-deps
// Notification tab state (controlled for sliding indicator)
const [notifTab, setNotifTab] = useState<'discord' | 'slack' | 'webhook'>('discord');
// Auth State
const [authData, setAuthData] = useState({ oldPassword: '', newPassword: '', confirmPassword: '' });
@@ -571,11 +577,26 @@ export function SettingsModal({ isOpen, onClose, isDarkMode, setIsDarkMode }: Se
<h3 className="text-lg font-semibold tracking-tight">Notifications & Alerts</h3>
<p className="text-sm text-muted-foreground">Configure external integrations for crash alerts.</p>
</div>
<Tabs defaultValue="discord" className="w-full">
<TabsList className="grid w-full grid-cols-3 mb-4">
<TabsTrigger value="discord">Discord</TabsTrigger>
<TabsTrigger value="slack">Slack</TabsTrigger>
<TabsTrigger value="webhook">Webhook</TabsTrigger>
<Tabs value={notifTab} onValueChange={(v) => setNotifTab(v as 'discord' | 'slack' | 'webhook')} className="w-full">
<TabsList className="w-full mb-4 grid grid-cols-3">
<TabsTrigger value="discord" className="relative data-[state=active]:bg-transparent data-[state=active]:shadow-none">
{notifTab === 'discord' && (
<motion.div layoutId="notif-tab-indicator" className="absolute inset-0 rounded-md bg-background shadow-sm" transition={{ type: 'spring', stiffness: 350, damping: 30 }} />
)}
<span className="relative z-10">Discord</span>
</TabsTrigger>
<TabsTrigger value="slack" className="relative data-[state=active]:bg-transparent data-[state=active]:shadow-none">
{notifTab === 'slack' && (
<motion.div layoutId="notif-tab-indicator" className="absolute inset-0 rounded-md bg-background shadow-sm" transition={{ type: 'spring', stiffness: 350, damping: 30 }} />
)}
<span className="relative z-10">Slack</span>
</TabsTrigger>
<TabsTrigger value="webhook" className="relative data-[state=active]:bg-transparent data-[state=active]:shadow-none">
{notifTab === 'webhook' && (
<motion.div layoutId="notif-tab-indicator" className="absolute inset-0 rounded-md bg-background shadow-sm" transition={{ type: 'spring', stiffness: 350, damping: 30 }} />
)}
<span className="relative z-10">Webhook</span>
</TabsTrigger>
</TabsList>
<TabsContent value="discord">{renderAgentTab('discord', 'Discord')}</TabsContent>
<TabsContent value="slack">{renderAgentTab('slack', 'Slack')}</TabsContent>
@@ -590,23 +611,31 @@ export function SettingsModal({ isOpen, onClose, isDarkMode, setIsDarkMode }: Se
<h3 className="text-lg font-semibold tracking-tight">Appearance</h3>
<p className="text-sm text-muted-foreground">Customize Sencho's visual theme.</p>
</div>
<div className="flex items-center space-x-4 mt-6">
<div className="flex items-center gap-3 mt-6 flex-wrap">
<Button
variant={!isDarkMode ? 'default' : 'outline'}
variant={theme === 'light' ? 'default' : 'outline'}
className="w-32 h-20 flex flex-col gap-2 rounded-xl"
onClick={() => setIsDarkMode(false)}
onClick={() => setTheme('light')}
>
<Sun className="w-6 h-6" />
Light
</Button>
<Button
variant={isDarkMode ? 'default' : 'outline'}
variant={theme === 'dark' ? 'default' : 'outline'}
className="w-32 h-20 flex flex-col gap-2 rounded-xl"
onClick={() => setIsDarkMode(true)}
onClick={() => setTheme('dark')}
>
<Moon className="w-6 h-6" />
Dark
</Button>
<Button
variant={theme === 'auto' ? 'default' : 'outline'}
className="w-32 h-20 flex flex-col gap-2 rounded-xl"
onClick={() => setTheme('auto')}
>
<Monitor className="w-6 h-6" />
Auto
</Button>
</div>
</div>
)}