feat(ui): 实现深色/浅色主题切换功能
- 添加 CSS 变量定义浅色和深色主题色板 - 扩展 Tailwind 配置支持语义化颜色 (surface-*, fg-*, line-*, code) - 创建 useTheme hook 管理主题状态和持久化 - 创建 ThemeToggle 组件支持三种模式 (light/dark/system) - 迁移所有组件从硬编码 gray-* 到语义化颜色 - 支持系统主题偏好检测 (prefers-color-scheme) - 添加主题初始化脚本防止闪烁 (FOUC)
This commit is contained in:
@@ -145,11 +145,11 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
toast.success(
|
||||
<div>
|
||||
<div className="font-medium">Command succeeded</div>
|
||||
<div className="text-xs text-gray-400 mt-1">
|
||||
<div className="text-xs text-fg-muted mt-1">
|
||||
Exit code: {result.data.exitCode} ({result.data.duration}ms)
|
||||
</div>
|
||||
{result.data.stdout && (
|
||||
<pre className="text-xs bg-gray-900 p-2 rounded mt-2 max-h-32 overflow-auto">
|
||||
<pre className="text-xs bg-surface-base p-2 rounded mt-2 max-h-32 overflow-auto">
|
||||
{result.data.stdout.slice(0, 500)}
|
||||
</pre>
|
||||
)}
|
||||
@@ -159,11 +159,11 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
toast.error(
|
||||
<div>
|
||||
<div className="font-medium">Command failed</div>
|
||||
<div className="text-xs text-gray-400 mt-1">
|
||||
<div className="text-xs text-fg-muted mt-1">
|
||||
Exit code: {result.data.exitCode} ({result.data.duration}ms)
|
||||
</div>
|
||||
{result.data.stderr && (
|
||||
<pre className="text-xs bg-gray-900 p-2 rounded mt-2 max-h-32 overflow-auto text-red-400">
|
||||
<pre className="text-xs bg-surface-base p-2 rounded mt-2 max-h-32 overflow-auto text-red-400">
|
||||
{result.data.stderr.slice(0, 500)}
|
||||
</pre>
|
||||
)}
|
||||
@@ -274,7 +274,7 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
const LoadingSkeleton = () => (
|
||||
<div className="space-y-3 p-4">
|
||||
{[1, 2, 3, 4].map((i) => (
|
||||
<div key={i} className="bg-gray-900/50 rounded-lg p-3">
|
||||
<div key={i} className="bg-surface-base/50 rounded-lg p-3">
|
||||
<div className="flex items-center gap-3">
|
||||
<Skeleton className="h-4 w-4" />
|
||||
<Skeleton className="h-4 w-32" />
|
||||
@@ -292,7 +292,7 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
|
||||
if (patterns.length === 0) {
|
||||
return (
|
||||
<div className="text-xs text-gray-500 py-2 px-3">
|
||||
<div className="text-xs text-fg-subtle py-2 px-3">
|
||||
No hooks configured
|
||||
</div>
|
||||
);
|
||||
@@ -305,7 +305,7 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
const cmdId = `${type}-${pattern}`;
|
||||
|
||||
return (
|
||||
<div key={pattern} className="bg-gray-800/50 rounded p-2">
|
||||
<div key={pattern} className="bg-surface-subtle/50 rounded p-2">
|
||||
<div className="flex items-center justify-between">
|
||||
<code className="text-xs font-mono text-blue-400">{pattern}</code>
|
||||
<div className="flex items-center gap-1">
|
||||
@@ -332,7 +332,7 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
<div className="mt-1 space-y-1">
|
||||
{commands.map((cmd, idx) => (
|
||||
<div key={idx} className="flex items-center justify-between text-xs">
|
||||
<code className="font-mono text-gray-400 truncate flex-1">
|
||||
<code className="font-mono text-fg-muted truncate flex-1">
|
||||
{cmd.command.join(' ')}
|
||||
</code>
|
||||
<Button
|
||||
@@ -365,7 +365,7 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
|
||||
if (commands.length === 0) {
|
||||
return (
|
||||
<div className="text-xs text-gray-500 py-2 px-3">
|
||||
<div className="text-xs text-fg-subtle py-2 px-3">
|
||||
No hooks configured
|
||||
</div>
|
||||
);
|
||||
@@ -376,8 +376,8 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
{commands.map((cmd, idx) => {
|
||||
const cmdId = `session-${idx}`;
|
||||
return (
|
||||
<div key={idx} className="bg-gray-800/50 rounded p-2 flex items-center justify-between">
|
||||
<code className="text-xs font-mono text-gray-400 truncate flex-1">
|
||||
<div key={idx} className="bg-surface-subtle/50 rounded p-2 flex items-center justify-between">
|
||||
<code className="text-xs font-mono text-fg-muted truncate flex-1">
|
||||
{cmd.command.join(' ')}
|
||||
</code>
|
||||
<div className="flex items-center gap-1">
|
||||
@@ -444,7 +444,7 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
transition={smoothTransition}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
className={cn(
|
||||
'bg-gray-800 max-h-[90vh] overflow-hidden flex flex-col',
|
||||
'bg-surface-subtle max-h-[90vh] overflow-hidden flex flex-col',
|
||||
responsive
|
||||
? 'w-full md:w-full md:max-w-2xl md:mx-4 rounded-t-2xl md:rounded-lg'
|
||||
: 'rounded-lg w-full max-w-2xl mx-4'
|
||||
@@ -453,19 +453,19 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
{/* Header */}
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center justify-between border-b border-gray-700',
|
||||
'flex items-center justify-between border-b border-line',
|
||||
responsive ? 'px-4 md:px-6 py-4' : 'px-6 py-4'
|
||||
)}
|
||||
>
|
||||
{responsive && (
|
||||
<div className="absolute top-2 left-1/2 -translate-x-1/2 w-10 h-1 bg-gray-600 rounded-full md:hidden" />
|
||||
<div className="absolute top-2 left-1/2 -translate-x-1/2 w-10 h-1 bg-surface-emphasis rounded-full md:hidden" />
|
||||
)}
|
||||
<div className={cn(responsive && 'mt-2 md:mt-0')}>
|
||||
<h2 className="text-lg font-semibold flex items-center gap-2">
|
||||
<Zap size={20} className="text-yellow-400" />
|
||||
Hooks Configuration
|
||||
</h2>
|
||||
<p className="text-xs text-gray-500">
|
||||
<p className="text-xs text-fg-subtle">
|
||||
{totalHooks} hooks configured
|
||||
</p>
|
||||
</div>
|
||||
@@ -512,17 +512,17 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
<motion.div
|
||||
key={type}
|
||||
layout
|
||||
className="bg-gray-900/50 rounded-lg overflow-hidden"
|
||||
className="bg-surface-base/50 rounded-lg overflow-hidden"
|
||||
>
|
||||
{/* Type Header */}
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-3 p-3',
|
||||
'hover:bg-gray-900/80 transition-colors cursor-pointer'
|
||||
'hover:bg-surface-base/80 transition-colors cursor-pointer'
|
||||
)}
|
||||
onClick={() => toggleExpanded(type)}
|
||||
>
|
||||
<button className="text-gray-500 hover:text-gray-300">
|
||||
<button className="text-fg-subtle hover:text-fg-secondary">
|
||||
{isExpanded ? <ChevronDown size={16} /> : <ChevronRight size={16} />}
|
||||
</button>
|
||||
|
||||
@@ -530,14 +530,14 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-medium text-gray-200">{label}</span>
|
||||
<span className="font-medium text-fg-secondary">{label}</span>
|
||||
{hookCount > 0 && (
|
||||
<span className="text-xs bg-gray-700 px-1.5 py-0.5 rounded">
|
||||
<span className="text-xs bg-surface-muted px-1.5 py-0.5 rounded">
|
||||
{hookCount}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="text-xs text-gray-500">{description}</div>
|
||||
<div className="text-xs text-fg-subtle">{description}</div>
|
||||
</div>
|
||||
|
||||
<Button
|
||||
@@ -568,7 +568,7 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
transition={{ duration: 0.2 }}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<div className="px-4 pb-3 pt-1 border-t border-gray-700/50">
|
||||
<div className="px-4 pb-3 pt-1 border-t border-line/50">
|
||||
{isFileHook ? renderFileHooks(type) : renderSessionHooks()}
|
||||
</div>
|
||||
</motion.div>
|
||||
@@ -584,7 +584,7 @@ export function HooksPanel({ onClose, responsive = false }: HooksPanelProps) {
|
||||
{/* Footer */}
|
||||
<div
|
||||
className={cn(
|
||||
'border-t border-gray-700 text-xs text-gray-500 px-4 py-3 flex items-start gap-2',
|
||||
'border-t border-line text-xs text-fg-subtle px-4 py-3 flex items-start gap-2',
|
||||
responsive && 'safe-area-pb'
|
||||
)}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user