This commit is contained in:
lpf
2026-03-09 20:21:22 +08:00
parent 0b9192132f
commit 938e762c6b
22 changed files with 558 additions and 169 deletions

View File

@@ -32,7 +32,7 @@ export const GlobalDialog: React.FC<{
return (
<AnimatePresence>
{open && (
<motion.div className="fixed inset-0 z-[130] bg-black/60 backdrop-blur-sm flex items-center justify-center p-4"
<motion.div className="ui-overlay-strong fixed inset-0 z-[130] backdrop-blur-sm flex items-center justify-center p-4"
initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>
<motion.div className="brand-card w-full max-w-md border border-zinc-700 shadow-2xl"
initial={{ scale: 0.95, y: 8 }} animate={{ scale: 1, y: 0 }} exit={{ scale: 0.95, y: 8 }}>

View File

@@ -63,14 +63,14 @@ const Header: React.FC = () => {
</button>
<div className="hidden md:flex items-center gap-3 rounded-xl px-2 py-1.5 min-w-0">
<div className="brand-badge w-9 h-9 rounded-xl flex items-center justify-center shadow-lg shrink-0">
<Terminal className="w-5 h-5 text-white" />
<Terminal className="w-5 h-5 text-zinc-950" />
</div>
{!sidebarCollapsed && (
<span className="font-semibold text-lg md:text-xl tracking-tight truncate">{t('appName')}</span>
)}
</div>
<div className="brand-badge md:hidden w-8 h-8 rounded-xl flex items-center justify-center shadow-lg shrink-0">
<Terminal className="w-4 h-4 text-white" />
<Terminal className="w-4 h-4 text-zinc-950" />
</div>
<span className="md:hidden font-semibold text-lg tracking-tight truncate">{t('appName')}</span>
</div>
@@ -79,13 +79,13 @@ const Header: React.FC = () => {
<div className="flex items-center gap-1.5 md:gap-2.5 bg-zinc-900 border border-zinc-800 px-2 md:px-3 py-1 rounded-lg max-w-[140px] md:max-w-none overflow-hidden">
<span className="hidden md:inline text-sm font-medium text-zinc-400">{t('gatewayStatus')}:</span>
{isGatewayOnline ? (
<div className="flex items-center gap-1.5 bg-emerald-500/10 text-emerald-400 px-2.5 py-0.5 rounded-md text-xs font-semibold border border-emerald-500/20">
<div className="w-1.5 h-1.5 rounded-full bg-emerald-500 shadow-[0_0_8px_rgba(16,185,129,0.8)]" />
<div className="status-pill-online flex items-center gap-1.5 px-2.5 py-0.5 rounded-md text-xs font-semibold border">
<div className="status-dot-online w-1.5 h-1.5 rounded-full" />
{t('online')}
</div>
) : (
<div className="flex items-center gap-1.5 bg-red-500/10 text-red-400 px-2.5 py-0.5 rounded-md text-xs font-semibold border border-red-500/20">
<div className="w-1.5 h-1.5 rounded-full bg-red-500 shadow-[0_0_8px_rgba(239,68,68,0.8)]" />
<div className="status-pill-offline flex items-center gap-1.5 px-2.5 py-0.5 rounded-md text-xs font-semibold border">
<div className="status-dot-offline w-1.5 h-1.5 rounded-full" />
{t('offline')}
</div>
)}

View File

@@ -15,7 +15,7 @@ const Layout: React.FC = () => {
<div className="flex flex-1 min-h-0">
<Sidebar />
{sidebarOpen && (
<button className="fixed inset-0 top-16 bg-black/40 z-30 md:hidden" onClick={() => setSidebarOpen(false)} aria-label="close sidebar" />
<button className="ui-overlay-soft fixed inset-0 top-16 z-30 md:hidden" onClick={() => setSidebarOpen(false)} aria-label="close sidebar" />
)}
<main className="flex-1 flex flex-col min-w-0 relative app-main-surface">
<AnimatePresence mode="wait">

View File

@@ -59,7 +59,7 @@ const PrimitiveArrayEditor: React.FC<{
{value.map((item, idx) => (
<span key={`${item}-${idx}`} className="inline-flex items-center gap-1 px-2 py-1 rounded-xl ui-soft-panel text-xs font-mono text-zinc-700 dark:text-zinc-200">
{String(item)}
<button onClick={() => removeAt(idx)} className="text-zinc-400 hover:text-red-400">×</button>
<button onClick={() => removeAt(idx)} className="ui-text-danger-hover text-zinc-400">×</button>
</span>
))}
</div>

View File

@@ -11,12 +11,12 @@ const Sidebar: React.FC = () => {
const location = useLocation();
const [expandedSections, setExpandedSections] = React.useState<Record<string, boolean>>({
main: true,
agents: true,
ops: true,
config: true,
knowledge: true,
insights: true,
channels: true,
agents: false,
ops: false,
config: false,
knowledge: false,
insights: false,
channels: false,
});
const sections = [
@@ -158,7 +158,7 @@ const Sidebar: React.FC = () => {
</div>
) : (
<div className="hidden md:flex justify-center p-3 border-t border-zinc-800 bg-zinc-900/20">
<div className="w-2.5 h-2.5 rounded-full bg-indigo-500 shadow-[0_0_14px_rgba(240,90,40,0.38)]" title={t('gatewayToken')} />
<div className="gateway-token-indicator w-2.5 h-2.5 rounded-full" title={t('gatewayToken')} />
</div>
)}
<div className={`hidden md:flex border-t border-zinc-800 bg-zinc-900/20 ${sidebarCollapsed ? 'justify-center p-3' : 'justify-end p-3'}`}>

View File

@@ -32,9 +32,22 @@ export const SpaceParticles: React.FC = () => {
resize();
let observer: MutationObserver | null = null;
let themeStyles = typeof document !== 'undefined' ? getComputedStyle(document.documentElement) : null;
const readThemeColor = (name: string, fallback: string) => {
const value = themeStyles?.getPropertyValue(name).trim();
return value || fallback;
};
const readThemeNumber = (name: string, fallback: number) => {
const value = Number.parseFloat(themeStyles?.getPropertyValue(name).trim() || '');
return Number.isFinite(value) ? value : fallback;
};
if (typeof document !== 'undefined') {
observer = new MutationObserver(() => {
isDark = document.documentElement.classList.contains('theme-dark');
themeStyles = getComputedStyle(document.documentElement);
});
observer.observe(document.documentElement, { attributes: true, attributeFilter: ['class'] });
}
@@ -60,18 +73,18 @@ export const SpaceParticles: React.FC = () => {
canvas.width / 2, canvas.height / 2, 0,
canvas.width / 2, canvas.height / 2, Math.max(canvas.width, canvas.height)
);
if (isDark) {
gradient.addColorStop(0, 'rgba(255, 133, 82, 0.16)');
gradient.addColorStop(0.55, 'rgba(112, 41, 22, 0.20)');
gradient.addColorStop(1, 'rgba(28, 12, 8, 0.88)');
} else {
gradient.addColorStop(0, 'rgba(255, 243, 233, 0.30)');
gradient.addColorStop(0.55, 'rgba(255, 179, 107, 0.16)');
gradient.addColorStop(1, 'rgba(255, 226, 209, 0.55)');
}
gradient.addColorStop(0, readThemeColor('--particle-glow-start', 'rgba(255, 243, 233, 0.30)'));
gradient.addColorStop(0.55, readThemeColor('--particle-glow-mid', 'rgba(255, 179, 107, 0.16)'));
gradient.addColorStop(1, readThemeColor('--particle-glow-end', 'rgba(255, 226, 209, 0.55)'));
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, canvas.width, canvas.height);
const particleRGB = readThemeColor('--particle-dot-rgb', isDark ? '255, 179, 107' : '217, 72, 28');
const particleOpacityFloor = readThemeNumber('--particle-dot-opacity-floor', isDark ? 0.1 : 0.08);
const particleOpacityScale = readThemeNumber('--particle-dot-opacity-scale', isDark ? 1 : 0.65);
const lineRGB = readThemeColor('--particle-line-rgb', isDark ? '240, 90, 40' : '164, 58, 24');
const lineOpacityScale = readThemeNumber('--particle-line-opacity-scale', isDark ? 1 : 0.85);
particles.forEach((p) => {
p.x += p.speedX;
p.y += p.speedY;
@@ -83,9 +96,7 @@ export const SpaceParticles: React.FC = () => {
ctx.beginPath();
ctx.arc(p.x, p.y, p.size, 0, Math.PI * 2);
ctx.fillStyle = isDark
? `rgba(255, 179, 107, ${p.opacity})`
: `rgba(217, 72, 28, ${Math.max(0.08, p.opacity * 0.65)})`;
ctx.fillStyle = `rgba(${particleRGB}, ${Math.max(particleOpacityFloor, p.opacity * particleOpacityScale)})`;
ctx.fill();
});
@@ -100,9 +111,7 @@ export const SpaceParticles: React.FC = () => {
if (dist < 120) {
ctx.beginPath();
const lineOpacity = 0.16 * (1 - dist / 120);
ctx.strokeStyle = isDark
? `rgba(240, 90, 40, ${lineOpacity})`
: `rgba(164, 58, 24, ${lineOpacity * 0.85})`;
ctx.strokeStyle = `rgba(${lineRGB}, ${lineOpacity * lineOpacityScale})`;
ctx.moveTo(particles[i].x, particles[i].y);
ctx.lineTo(particles[j].x, particles[j].y);
ctx.stroke();

View File

@@ -8,7 +8,7 @@ interface StatCardProps {
const StatCard: React.FC<StatCardProps> = ({ title, value, icon }) => (
<div className="brand-card h-full min-h-[124px] border border-zinc-800 p-6 flex items-center gap-4">
<div className="w-12 h-12 rounded-2xl bg-zinc-800/50 flex items-center justify-center border border-zinc-700/50 shadow-[inset_0_1px_0_rgba(255,255,255,0.18)] relative z-[1]">
<div className="card-icon-shell w-12 h-12 rounded-2xl bg-zinc-800/50 flex items-center justify-center border border-zinc-700/50 relative z-[1]">
{icon}
</div>
<div className="relative z-[1]">