mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-29 19:57:12 +00:00
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:
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user