feat(ui): 实现深色/浅色主题切换功能
- 添加 CSS 变量定义浅色和深色主题色板 - 扩展 Tailwind 配置支持语义化颜色 (surface-*, fg-*, line-*, code) - 创建 useTheme hook 管理主题状态和持久化 - 创建 ThemeToggle 组件支持三种模式 (light/dark/system) - 迁移所有组件从硬编码 gray-* 到语义化颜色 - 支持系统主题偏好检测 (prefers-color-scheme) - 添加主题初始化脚本防止闪烁 (FOUC)
This commit is contained in:
@@ -44,13 +44,13 @@ type CommandItem = CommandListResponse['commands'][number];
|
||||
function getSourceIcon(source: string) {
|
||||
switch (source) {
|
||||
case 'builtin':
|
||||
return <Cog size={14} className="text-gray-400" />;
|
||||
return <Cog size={14} className="text-fg-muted" />;
|
||||
case 'user':
|
||||
return <User size={14} className="text-blue-400" />;
|
||||
case 'project':
|
||||
return <FolderOpen size={14} className="text-green-400" />;
|
||||
default:
|
||||
return <Terminal size={14} className="text-gray-400" />;
|
||||
return <Terminal size={14} className="text-fg-muted" />;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,13 +58,13 @@ function getSourceIcon(source: string) {
|
||||
function getSourceBadgeClass(source: string) {
|
||||
switch (source) {
|
||||
case 'builtin':
|
||||
return 'bg-gray-700 text-gray-300';
|
||||
return 'bg-surface-muted text-fg-secondary';
|
||||
case 'user':
|
||||
return 'bg-blue-500/20 text-blue-400';
|
||||
case 'project':
|
||||
return 'bg-green-500/20 text-green-400';
|
||||
default:
|
||||
return 'bg-gray-700 text-gray-300';
|
||||
return 'bg-surface-muted text-fg-secondary';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -186,7 +186,7 @@ export function CommandPanel({ onClose, responsive = false }: CommandPanelProps)
|
||||
const LoadingSkeleton = () => (
|
||||
<div className="space-y-3 p-4">
|
||||
{[1, 2, 3, 4, 5].map((i) => (
|
||||
<div key={i} className="flex items-center gap-3 p-3 bg-gray-900/50 rounded-lg">
|
||||
<div key={i} className="flex items-center gap-3 p-3 bg-surface-base/50 rounded-lg">
|
||||
<Skeleton className="h-5 w-5 rounded" />
|
||||
<div className="flex-1 space-y-2">
|
||||
<Skeleton className="h-4 w-32" />
|
||||
@@ -223,7 +223,7 @@ export function CommandPanel({ onClose, responsive = false }: CommandPanelProps)
|
||||
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'
|
||||
@@ -232,17 +232,17 @@ export function CommandPanel({ onClose, responsive = false }: CommandPanelProps)
|
||||
{/* 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-muted rounded-full md:hidden" />
|
||||
)}
|
||||
<div className={cn(responsive && 'mt-2 md:mt-0')}>
|
||||
<h2 className="text-lg font-semibold">Commands</h2>
|
||||
{stats && (
|
||||
<p className="text-xs text-gray-500">
|
||||
<p className="text-xs text-fg-subtle">
|
||||
{stats.total} commands ({stats.bySource.builtin || 0} builtin,{' '}
|
||||
{stats.bySource.user || 0} user, {stats.bySource.project || 0} project)
|
||||
</p>
|
||||
@@ -261,14 +261,14 @@ export function CommandPanel({ onClose, responsive = false }: CommandPanelProps)
|
||||
{/* Toolbar */}
|
||||
<div
|
||||
className={cn(
|
||||
'flex items-center gap-3 border-b border-gray-700',
|
||||
'flex items-center gap-3 border-b border-line',
|
||||
responsive ? 'px-4 md:px-6 py-3' : 'px-6 py-3'
|
||||
)}
|
||||
>
|
||||
<div className="flex-1 relative">
|
||||
<Search
|
||||
size={16}
|
||||
className="absolute left-3 top-1/2 -translate-y-1/2 text-gray-500"
|
||||
className="absolute left-3 top-1/2 -translate-y-1/2 text-fg-subtle"
|
||||
/>
|
||||
<Input
|
||||
value={searchQuery}
|
||||
@@ -297,7 +297,7 @@ export function CommandPanel({ onClose, responsive = false }: CommandPanelProps)
|
||||
{loading ? (
|
||||
<LoadingSkeleton />
|
||||
) : filteredCommands.length === 0 ? (
|
||||
<div className="flex flex-col items-center justify-center py-12 text-gray-500">
|
||||
<div className="flex flex-col items-center justify-center py-12 text-fg-subtle">
|
||||
<Terminal size={48} className="mb-4 opacity-50" />
|
||||
<p>
|
||||
{searchQuery
|
||||
@@ -325,8 +325,8 @@ export function CommandPanel({ onClose, responsive = false }: CommandPanelProps)
|
||||
animate={{ opacity: 1, y: 0 }}
|
||||
exit={{ opacity: 0, y: -10 }}
|
||||
className={cn(
|
||||
'flex items-center gap-3 p-3 bg-gray-900/50 rounded-lg',
|
||||
'hover:bg-gray-900/80 transition-colors group'
|
||||
'flex items-center gap-3 p-3 bg-surface-base/50 rounded-lg',
|
||||
'hover:bg-surface-base/80 transition-colors group'
|
||||
)}
|
||||
>
|
||||
{/* Icon */}
|
||||
@@ -335,7 +335,7 @@ export function CommandPanel({ onClose, responsive = false }: CommandPanelProps)
|
||||
{/* Info */}
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="font-mono text-sm text-gray-200">/{command.name}</span>
|
||||
<span className="font-mono text-sm text-fg-secondary">/{command.name}</span>
|
||||
<span
|
||||
className={cn(
|
||||
'px-1.5 py-0.5 text-xs rounded',
|
||||
@@ -346,7 +346,7 @@ export function CommandPanel({ onClose, responsive = false }: CommandPanelProps)
|
||||
</span>
|
||||
</div>
|
||||
{command.description && (
|
||||
<p className="text-xs text-gray-500 truncate mt-0.5">
|
||||
<p className="text-xs text-fg-subtle truncate mt-0.5">
|
||||
{command.description}
|
||||
</p>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user