diff --git a/src/App.tsx b/src/App.tsx index 7b8fab8..8f92382 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -244,7 +244,7 @@ function App() { }, [handleExportSessions, handleImportSessions, handleOpenSettings]) return ( -
+
{connection ? `已连接: ${connection.config.username}@${connection.config.host}` : '未连接 - 请先建立 SSH 连接'} @@ -205,7 +205,7 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P setShowAddModal(true)} disabled={!connection} - className="flex items-center gap-2 px-4 py-2 bg-accent-blue hover:bg-blue-600 disabled:bg-dark-700 disabled:text-gray-500 rounded-lg text-sm font-medium transition-colors" + className="flex items-center gap-2 px-4 py-2 bg-surface-baseccent-blue hover:bg-surface-baselue-600 disabled:bg-dark-700 disabled:text-txt-muted rounded-lg text-sm font-medium transition-colors" > 添加规则 @@ -215,7 +215,7 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P {/* Rules List */} {rules.length === 0 ? ( - + 暂无端口转发规则 点击右上角添加规则 @@ -235,7 +235,7 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P {/* Type Icon */} @@ -247,9 +247,9 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P {/* Rule Info */} - {rule.name} + {rule.name} @@ -262,7 +262,7 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P )} - + {rule.type === 'dynamic' ? ( SOCKS5 代理: 127.0.0.1:{rule.localPort} ) : rule.type === 'local' ? ( @@ -320,7 +320,7 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P {showAddModal && ( - 添加端口转发规则 + 添加端口转发规则 {/* Type Selection */} @@ -334,8 +334,8 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P onClick={() => setNewRule({ ...newRule, type: t.type as 'local' | 'remote' | 'dynamic' })} className={`p-3 rounded-lg border text-left transition-colors ${ newRule.type === t.type - ? 'bg-accent-blue/20 border-accent-blue text-white' - : 'bg-dark-700 border-dark-600 text-gray-400 hover:bg-dark-600' + ? 'bg-surface-baseccent-blue/20 border-accent-blue text-content-primary' + : 'bg-dark-700 border-dark-600 text-txt-muted hover:bg-dark-600' }`} > {t.label} @@ -347,19 +347,19 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P {/* Form Fields */} - 规则名称 + 规则名称 setNewRule({ ...newRule, name: e.target.value })} placeholder="例如: MySQL 转发" - className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> - + {newRule.type === 'remote' ? '远程端口' : '本地端口'} setNewRule({ ...newRule, localPort: parseInt(e.target.value) })} placeholder="例如: 3306" - className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> {newRule.type !== 'dynamic' && ( - 远程端口 + 远程端口 setNewRule({ ...newRule, remotePort: parseInt(e.target.value) })} placeholder="例如: 3306" - className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> )} @@ -387,13 +387,13 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P {newRule.type === 'local' && ( - 远程主机 + 远程主机 setNewRule({ ...newRule, remoteHost: e.target.value })} placeholder="例如: localhost 或 192.168.1.100" - className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> )} @@ -409,7 +409,7 @@ export default function PortForwardView({ tabId, isActive: _isActive = true }: P 添加 diff --git a/src/components/QuickConnectDialog.tsx b/src/components/QuickConnectDialog.tsx index 444682e..8fa9ebb 100644 --- a/src/components/QuickConnectDialog.tsx +++ b/src/components/QuickConnectDialog.tsx @@ -60,22 +60,22 @@ export default function QuickConnectDialog({ isOpen, onClose }: QuickConnectDial } return ( - + {/* Header */} - + - 快速连接 - 临时连接到服务器 + 快速连接 + 临时连接到服务器 @@ -86,7 +86,7 @@ export default function QuickConnectDialog({ isOpen, onClose }: QuickConnectDial {/* Host and Port */} - + 主机地址 @@ -97,11 +97,11 @@ export default function QuickConnectDialog({ isOpen, onClose }: QuickConnectDial value={formData.host} onChange={(e) => setFormData({ ...formData, host: e.target.value })} placeholder="192.168.1.1" - className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 placeholder-gray-600 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> - + 端口 setFormData({ ...formData, port: parseInt(e.target.value) })} placeholder="22" - className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary focus:outline-none focus:border-accent-blue" /> {/* Username */} - + 用户名 @@ -126,13 +126,13 @@ export default function QuickConnectDialog({ isOpen, onClose }: QuickConnectDial value={formData.username} onChange={(e) => setFormData({ ...formData, username: e.target.value })} placeholder="root" - className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 placeholder-gray-600 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> {/* Password */} - + 密码 @@ -141,7 +141,7 @@ export default function QuickConnectDialog({ isOpen, onClose }: QuickConnectDial value={formData.password} onChange={(e) => setFormData({ ...formData, password: e.target.value })} placeholder="••••••••" - className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 placeholder-gray-600 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> @@ -155,7 +155,7 @@ export default function QuickConnectDialog({ isOpen, onClose }: QuickConnectDial {/* Hint */} - + 快速连接不会保存会话信息。如需保存,请使用"新建会话"。 @@ -165,14 +165,14 @@ export default function QuickConnectDialog({ isOpen, onClose }: QuickConnectDial 取消 {isConnecting ? ( '连接中...' diff --git a/src/components/SessionDialog.tsx b/src/components/SessionDialog.tsx index 2783313..d4e8fa7 100644 --- a/src/components/SessionDialog.tsx +++ b/src/components/SessionDialog.tsx @@ -142,22 +142,22 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD } return ( - + {/* Header */} - + - {isEditing ? '编辑会话' : '新建会话'} - {isEditing ? '修改 SSH 连接配置' : '配置 SSH 连接信息'} + {isEditing ? '编辑会话' : '新建会话'} + {isEditing ? '修改 SSH 连接配置' : '配置 SSH 连接信息'} @@ -168,7 +168,7 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD {/* Basic info */} - + 会话名称 setFormData({ ...formData, name: e.target.value })} placeholder="例如: 生产服务器" - className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 placeholder-gray-600 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> - + 主机地址 - + setFormData({ ...formData, host: e.target.value })} placeholder="192.168.1.1" - className="w-full pl-9 pr-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 placeholder-gray-600 focus:outline-none focus:border-accent-blue" + className="w-full pl-9 pr-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> - + 端口 setFormData({ ...formData, port: parseInt(e.target.value) })} placeholder="22" - className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary focus:outline-none focus:border-accent-blue" /> - + 用户名 - + setFormData({ ...formData, username: e.target.value })} placeholder="root" - className="w-full pl-9 pr-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 placeholder-gray-600 focus:outline-none focus:border-accent-blue" + className="w-full pl-9 pr-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> @@ -232,7 +232,7 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD {/* Authentication */} - + 认证方式 @@ -248,8 +248,8 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD onClick={() => setFormData({ ...formData, authType: type.value as AuthType })} className={`flex-1 flex items-center justify-center gap-2 px-3 py-2 rounded-lg border text-sm transition-colors ${ formData.authType === type.value - ? 'border-accent-blue bg-accent-blue/20 text-accent-blue' - : 'border-dark-700 hover:border-dark-600 text-gray-400' + ? 'border-accent-blue bg-surface-baseccent-blue/20 text-accent-blue' + : 'border-dark-700 hover:border-dark-600 text-txt-muted' }`} > @@ -261,17 +261,17 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD {formData.authType === 'password' && ( - + 密码 - + setFormData({ ...formData, password: e.target.value })} placeholder="••••••••" - className="w-full pl-9 pr-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 placeholder-gray-600 focus:outline-none focus:border-accent-blue" + className="w-full pl-9 pr-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> @@ -280,7 +280,7 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD {formData.authType === 'key' && ( - + 私钥路径 @@ -289,11 +289,11 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD value={formData.privateKey} onChange={(e) => setFormData({ ...formData, privateKey: e.target.value })} placeholder="~/.ssh/id_rsa" - className="flex-1 px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 placeholder-gray-600 focus:outline-none focus:border-accent-blue" + className="flex-1 px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> { const result = await window.electronAPI?.showOpenDialog({ title: '选择私钥文件', @@ -315,7 +315,7 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD - + 密钥密码 (可选) setFormData({ ...formData, passphrase: e.target.value })} placeholder="••••••••" - className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 placeholder-gray-600 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> @@ -335,11 +335,11 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD setShowProxySettings(!showProxySettings)} - className="flex items-center gap-2 text-sm font-medium text-gray-400 hover:text-gray-200 transition-colors" + className="flex items-center gap-2 text-sm font-medium text-txt-muted hover:text-content-primary transition-colors" > 代理设置 - + {formData.proxy.type !== 'none' && formData.proxy.host ? `(已配置 ${formData.proxy.type.toUpperCase()})` : '(未配置)'} {type.label} @@ -386,7 +386,7 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD {/* Proxy Host & Port */} - 代理地址 + 代理地址 - 端口 + 端口 @@ -416,7 +416,7 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD {/* Proxy Auth */} - 代理用户名 (可选) + 代理用户名 (可选) - 代理密码 (可选) + 代理密码 (可选) @@ -451,7 +451,7 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD {/* Group and tags */} - + 分组 @@ -460,11 +460,11 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD value={formData.group} onChange={(e) => setFormData({ ...formData, group: e.target.value })} placeholder="default" - className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 placeholder-gray-600 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> - + 标签 @@ -473,7 +473,7 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD value={formData.tags} onChange={(e) => setFormData({ ...formData, tags: e.target.value })} placeholder="生产, 阿里云" - className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-gray-200 placeholder-gray-600 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-900 border border-dark-700 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> @@ -483,14 +483,14 @@ export default function SessionDialog({ isOpen, onClose, editSession }: SessionD 取消 {isSubmitting ? (isEditing ? '保存中...' : '创建中...') : (isEditing ? '保存' : '创建会话')} diff --git a/src/components/SettingsView.tsx b/src/components/SettingsView.tsx index 3db75d0..57312b0 100644 --- a/src/components/SettingsView.tsx +++ b/src/components/SettingsView.tsx @@ -141,7 +141,7 @@ export default function SettingsView() { if (!isLoaded) { return ( - 加载中... + 加载中... ) } @@ -197,8 +197,8 @@ export default function SettingsView() { onClick={() => setSetting('theme', theme.value as 'dark' | 'light' | 'system')} className={`p-3 rounded-lg border text-sm transition-colors ${ settings.theme === theme.value - ? 'border-accent-blue bg-accent-blue/20 text-accent-blue' - : 'border-dark-700/30 hover:border-dark-600 text-gray-300' + ? 'border-accent-blue bg-surface-baseccent-blue/20 text-accent-blue' + : 'border-dark-700/30 hover:border-dark-600 text-content-secondary' }`} > {theme.label} @@ -212,7 +212,7 @@ export default function SettingsView() { setSetting('language', e.target.value as 'zh-CN' | 'en-US')} - className="w-full mt-3 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-gray-300 focus:outline-none focus:border-accent-blue" + className="w-full mt-3 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-content-secondary focus:outline-none focus:border-accent-blue" > 简体中文 English @@ -229,11 +229,11 @@ export default function SettingsView() { 字体 - 字体族 + 字体族 setSetting('fontFamily', e.target.value)} - className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-gray-300 focus:outline-none focus:border-accent-blue" + className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-content-secondary focus:outline-none focus:border-accent-blue" > JetBrains Mono Fira Code @@ -242,11 +242,11 @@ export default function SettingsView() { - 字体大小 + 字体大小 setSetting('fontSize', parseInt(e.target.value))} - className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-gray-300 focus:outline-none focus:border-accent-blue" + className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-content-secondary focus:outline-none focus:border-accent-blue" > {[12, 13, 14, 15, 16, 18, 20].map((size) => ( {size}px @@ -265,8 +265,8 @@ export default function SettingsView() { onClick={() => setSetting('terminalTheme', key as 'dracula' | 'monokai' | 'solarized' | 'one-dark')} className={`p-3 rounded-lg border text-sm transition-colors ${ settings.terminalTheme === key - ? 'border-accent-blue bg-accent-blue/20 text-accent-blue' - : 'border-dark-700/30 hover:border-dark-600 text-gray-300' + ? 'border-accent-blue bg-surface-baseccent-blue/20 text-accent-blue' + : 'border-dark-700/30 hover:border-dark-600 text-content-secondary' }`} > keybindingState.resetAll()} - className="flex items-center gap-1.5 px-3 py-1.5 text-xs text-gray-400 hover:text-accent-blue rounded-lg hover:bg-dark-700/50 transition-colors" + className="flex items-center gap-1.5 px-3 py-1.5 text-xs text-txt-muted hover:text-accent-blue rounded-lg hover:bg-dark-700/50 transition-colors" > 重置全部为默认 @@ -311,7 +311,7 @@ export default function SettingsView() { const defs = getKeybindingsByCategory(category) return ( - {category} + {category} {defs.map((def) => { const currentKeys = allKeys[def.id] @@ -323,14 +323,14 @@ export default function SettingsView() { key={def.id} className={`flex items-center justify-between px-3 py-2 rounded-lg transition-colors ${ isRecordingThis - ? 'bg-accent-blue/10 border border-accent-blue/30' + ? 'bg-surface-baseccent-blue/10 border border-accent-blue/30' : 'hover:bg-dark-800/60' }`} > - {def.label} + {def.label} {isCustom && ( - + 自定义 )} @@ -339,12 +339,12 @@ export default function SettingsView() { {isRecordingThis ? ( - + {recordedKeys || '按下快捷键...'} stopRecording()} - className="text-xs text-gray-500 hover:text-gray-300" + className="text-xs text-txt-muted hover:text-content-secondary" > 取消 @@ -368,8 +368,8 @@ export default function SettingsView() { {currentKeys} @@ -378,7 +378,7 @@ export default function SettingsView() { {isCustom && ( keybindingState.resetKeybinding(def.id)} - className="text-gray-600 hover:text-gray-400 transition-colors" + className="text-txt-faint hover:text-txt-muted transition-colors" title="重置为默认" > @@ -398,7 +398,7 @@ export default function SettingsView() { {/* 录入确认弹层 */} {recordingId && recordedKeys && ( - + 设置为 {recordedKeys} 确认 取消 )} - + 点击快捷键可重新绑定 · 按 Esc 取消录入 @@ -445,7 +445,7 @@ export default function SettingsView() { type="number" value={settings.defaultPort} onChange={(e) => setSetting('defaultPort', parseInt(e.target.value) || 22)} - className="w-32 mt-2 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-gray-300 focus:outline-none focus:border-accent-blue" + className="w-32 mt-2 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-content-secondary focus:outline-none focus:border-accent-blue" /> @@ -455,7 +455,7 @@ export default function SettingsView() { type="number" value={settings.timeout} onChange={(e) => setSetting('timeout', parseInt(e.target.value) || 30)} - className="w-32 mt-2 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-gray-300 focus:outline-none focus:border-accent-blue" + className="w-32 mt-2 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-content-secondary focus:outline-none focus:border-accent-blue" /> @@ -465,60 +465,60 @@ export default function SettingsView() { Pro 连接管理 - 配置连接池行为,优化资源使用和性能 + 配置连接池行为,优化资源使用和性能 - 最大连接数 + 最大连接数 setSetting('maxConnections', parseInt(e.target.value) || 10)} - className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-gray-300 focus:outline-none focus:border-accent-blue" + className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-content-secondary focus:outline-none focus:border-accent-blue" /> - 最大并发 SSH 连接数 + 最大并发 SSH 连接数 - 最大后台连接 + 最大后台连接 setSetting('maxBackgroundConnections', parseInt(e.target.value) || 5)} - className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-gray-300 focus:outline-none focus:border-accent-blue" + className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-content-secondary focus:outline-none focus:border-accent-blue" /> - 非活跃标签保持连接数 + 非活跃标签保持连接数 - 空闲超时 (分钟) + 空闲超时 (分钟) setSetting('idleTimeoutMs', (parseInt(e.target.value) || 5) * 60000)} - className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-gray-300 focus:outline-none focus:border-accent-blue" + className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-content-secondary focus:outline-none focus:border-accent-blue" /> - 无操作后进入挂起 + 无操作后进入挂起 - 挂起超时 (分钟) + 挂起超时 (分钟) setSetting('suspendTimeoutMs', (parseInt(e.target.value) || 30) * 60000)} - className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-gray-300 focus:outline-none focus:border-accent-blue" + className="w-full mt-1 px-3 py-2 bg-dark-900 border border-dark-700/30 rounded-lg text-content-secondary focus:outline-none focus:border-accent-blue" /> - 挂起后自动断开 + 挂起后自动断开 @@ -571,11 +571,11 @@ export default function SettingsView() { - + - OpsXShell - 版本 {appVersion} - + OpsXShell + 版本 {appVersion} + 跨平台 SSH/SFTP 终端工具 · 简洁高效 · 安全可靠 · 操作便捷人性化 @@ -595,8 +595,8 @@ export default function SettingsView() { ['Lucide React', '0.294+'], ].map(([name, ver]) => ( - {name} - {ver} + {name} + {ver} ))} @@ -604,10 +604,10 @@ export default function SettingsView() { 开源许可 - + OpsXShell 基于 MIT 许可证开源。本项目使用了以下开源组件: - + ssh2 — MIT License xterm.js — MIT License zmodem-ts — MIT License @@ -631,18 +631,18 @@ export default function SettingsView() { {/* 搜索框 */} - + setSearchQuery(e.target.value)} - className="w-full pl-8 pr-8 py-1.5 text-sm bg-dark-900 border border-dark-700/30 rounded-lg text-gray-300 placeholder-gray-600 focus:outline-none focus:border-accent-blue/60" + className="w-full pl-8 pr-8 py-1.5 text-sm bg-dark-900 border border-dark-700/30 rounded-lg text-content-secondary placeholder-ph focus:outline-none focus:border-accent-blue/60" /> {searchQuery && ( setSearchQuery('')} - className="absolute right-2 top-1/2 -translate-y-1/2 text-gray-500 hover:text-gray-300" + className="absolute right-2 top-1/2 -translate-y-1/2 text-txt-muted hover:text-content-secondary" > @@ -661,14 +661,14 @@ export default function SettingsView() { onClick={() => setActiveTab(tab.id)} className={`w-full flex items-center gap-3 px-3 py-2.5 rounded-lg text-left transition-all ${ active - ? 'bg-accent-blue text-white shadow-sm' - : 'text-gray-400 hover:bg-dark-700/50 hover:text-gray-200' + ? 'bg-surface-baseccent-blue text-content-primary shadow-sm' + : 'text-txt-muted hover:bg-dark-700/50 hover:text-content-primary' }`} > - + {tab.label} - + {tab.description} @@ -682,8 +682,8 @@ export default function SettingsView() { {/* Header bar */} - {currentMeta.label} - {currentMeta.description} + {currentMeta.label} + {currentMeta.description} {/* 内容 */} @@ -727,7 +727,7 @@ function SettingSectionTitle({ className?: string }) { return ( - + {children} ) @@ -748,13 +748,13 @@ function SettingToggle({ return ( - {label} - {description} + {label} + {description} onChange(!checked)} className={`relative flex-shrink-0 w-11 h-6 rounded-full transition-colors ${ - checked ? 'bg-accent-blue' : 'bg-dark-600' + checked ? 'bg-surface-baseccent-blue' : 'bg-dark-600' }`} > - - + + OpsXShell @@ -195,7 +195,7 @@ export default function Sidebar({ onNewSession, onQuickConnect, onEditSession }: 快速连接 @@ -235,7 +235,7 @@ export default function Sidebar({ onNewSession, onQuickConnect, onEditSession }: addTab({ title: '设置', type: 'settings' })} - className="w-full flex items-center gap-2 px-3 py-2 text-sm text-gray-400 hover:bg-dark-700 rounded transition-colors" + className="w-full flex items-center gap-2 px-3 py-2 text-sm text-txt-muted hover:bg-dark-700 rounded transition-colors" > 设置 @@ -263,7 +263,7 @@ export default function Sidebar({ onNewSession, onQuickConnect, onEditSession }: w-full px-4 py-2 text-left text-sm transition-colors ${item.danger ? 'text-red-400 hover:bg-red-500/20' - : 'text-gray-300 hover:bg-dark-700' + : 'text-content-secondary hover:bg-dark-700' } `} > @@ -279,10 +279,10 @@ export default function Sidebar({ onNewSession, onQuickConnect, onEditSession }: onMouseDown={handleResizeStart} className={` absolute top-0 right-0 bottom-0 w-1 cursor-col-resize - hover:bg-accent-blue/50 active:bg-accent-blue/70 + hover:bg-surface-baseccent-blue/50 active:bg-surface-baseccent-blue/70 hover:w-1.5 hover:shadow-[0_0_6px_rgba(59,130,246,0.4)] transition-all duration-150 - ${isResizing ? 'bg-accent-blue/70 w-1.5 shadow-[0_0_6px_rgba(59,130,246,0.5)]' : ''} + ${isResizing ? 'bg-surface-baseccent-blue/70 w-1.5 shadow-[0_0_6px_rgba(59,130,246,0.5)]' : ''} `} /> diff --git a/src/components/StatusBar.tsx b/src/components/StatusBar.tsx index 2ba83ea..6f5c80d 100644 --- a/src/components/StatusBar.tsx +++ b/src/components/StatusBar.tsx @@ -54,7 +54,7 @@ const STATUS_CONFIG: Record = { disconnected: { icon: , label: '未连接', - colorClass: 'text-gray-400', + colorClass: 'text-txt-muted', animateClass: '', }, } @@ -163,7 +163,7 @@ export default function StatusBar() { : null return ( - + {/* Left: Connection status area — clickable to toggle ConnectionPanel */} {statusCfg.label} {hostLabel && ( <> - · - {hostLabel} + · + {hostLabel} > )} {totalConnections > 0 && ( - + {totalConnections} )} - + {/* Center: Transfer stats */} {transferLabel && ( - + {transferLabel} @@ -204,11 +204,11 @@ export default function StatusBar() { {/* Right: Clock + version */} - + {formatTime(currentTime)} - OpsXShell v{version} + OpsXShell v{version} ) diff --git a/src/components/TabBar.tsx b/src/components/TabBar.tsx index 9596826..80e208b 100644 --- a/src/components/TabBar.tsx +++ b/src/components/TabBar.tsx @@ -137,7 +137,7 @@ function TabBar() { {/* Add Tab Button */} addTab({ title: '新标签', type: 'welcome' })} - className="flex items-center justify-center w-10 h-10 text-gray-500 hover:text-gray-300 hover:bg-dark-800 transition-colors" + className="flex items-center justify-center w-10 h-10 text-txt-muted hover:text-content-secondary hover:bg-dark-800 transition-colors" > diff --git a/src/components/WelcomeView.tsx b/src/components/WelcomeView.tsx index 23d509d..b454387 100644 --- a/src/components/WelcomeView.tsx +++ b/src/components/WelcomeView.tsx @@ -27,7 +27,7 @@ export default function WelcomeView({ onNewSession, onQuickConnect }: WelcomeVie - + @@ -36,12 +36,12 @@ export default function WelcomeView({ onNewSession, onQuickConnect }: WelcomeVie onClick={onQuickConnect} className="flex items-center gap-4 p-6 bg-dark-800 hover:bg-dark-700 rounded-xl border border-dark-700/30 hover:border-accent-blue transition-all group" > - + - 快速连接 - 输入地址快速建立连接 + 快速连接 + 输入地址快速建立连接 @@ -49,12 +49,12 @@ export default function WelcomeView({ onNewSession, onQuickConnect }: WelcomeVie onClick={onNewSession} className="flex items-center gap-4 p-6 bg-dark-800 hover:bg-dark-700 rounded-xl border border-dark-700/30 hover:border-accent-green transition-all group" > - - + + - 新建会话 - 创建并保存新的会话 + 新建会话 + 创建并保存新的会话 @@ -62,12 +62,12 @@ export default function WelcomeView({ onNewSession, onQuickConnect }: WelcomeVie onClick={() => addTab({ title: 'SFTP', type: 'sftp' })} className="flex items-center gap-4 p-6 bg-dark-800 hover:bg-dark-700 rounded-xl border border-dark-700/30 hover:border-accent-purple transition-all group" > - + - SFTP 传输 - 可视化文件管理 + SFTP 传输 + 可视化文件管理 @@ -79,15 +79,15 @@ export default function WelcomeView({ onNewSession, onQuickConnect }: WelcomeVie - 端口转发 - 本地/远程/SOCKS5 代理 + 端口转发 + 本地/远程/SOCKS5 代理 {/* Recent connections */} - 最近连接 + 最近连接 {recentSessions.length === 0 ? ( - {session.name} - + {session.name} + {session.username}@{session.host}{session.port !== 22 ? `:${session.port}` : ''} - -- + -- ))} @@ -119,7 +119,7 @@ export default function WelcomeView({ onNewSession, onQuickConnect }: WelcomeVie {/* Shortcuts hint */} - + 快捷键: Ctrl+N 新建会话 Ctrl+K 快速连接 diff --git a/src/components/common/Button.tsx b/src/components/common/Button.tsx index 17aba34..d4913b6 100644 --- a/src/components/common/Button.tsx +++ b/src/components/common/Button.tsx @@ -20,10 +20,10 @@ interface ButtonProps extends ButtonHTMLAttributes { } const variantStyles = { - primary: 'bg-accent-blue hover:bg-blue-600 text-white', - secondary: 'bg-dark-700 hover:bg-dark-600 text-gray-200', - danger: 'bg-red-600 hover:bg-red-500 text-white', - ghost: 'hover:bg-dark-700 text-gray-400 hover:text-gray-200', + primary: 'bg-surface-baseccent-blue hover:bg-surface-baselue-600 text-content-primary', + secondary: 'bg-dark-700 hover:bg-dark-600 text-content-primary', + danger: 'bg-red-600 hover:bg-red-500 text-content-primary', + ghost: 'hover:bg-dark-700 text-txt-muted hover:text-content-primary', } const sizeStyles = { diff --git a/src/components/common/CommandPalette.tsx b/src/components/common/CommandPalette.tsx index 874ee44..e3d7af4 100644 --- a/src/components/common/CommandPalette.tsx +++ b/src/components/common/CommandPalette.tsx @@ -45,9 +45,9 @@ interface CommandPaletteProps { // ─── 类型 badge 颜色映射 ────────────────────────────────────────────── const TYPE_BADGE: Record = { - command: { label: '命令', className: 'bg-accent-blue/20 text-accent-blue' }, - session: { label: '会话', className: 'bg-accent-green/20 text-accent-green' }, - snippet: { label: '片段', className: 'bg-accent-purple/20 text-accent-purple' }, + command: { label: '命令', className: 'bg-surface-baseccent-blue/20 text-accent-blue' }, + session: { label: '会话', className: 'bg-surface-baseccent-green/20 text-accent-green' }, + snippet: { label: '片段', className: 'bg-surface-baseccent-purple/20 text-accent-purple' }, } // ─── 组件 ────────────────────────────────────────────────────────────── @@ -305,7 +305,7 @@ export function CommandPalette({ onKeyDown={handleKeyDown} > {/* Backdrop */} - + {/* Palette container */} {/* Search input */} - + setQuery(e.target.value)} placeholder="搜索命令、会话或片段..." - className="flex-1 bg-transparent text-gray-200 placeholder-gray-500 outline-none text-sm" + className="flex-1 bg-transparent text-content-primary placeholder-ph outline-none text-sm" autoComplete="off" spellCheck={false} /> - + ESC @@ -335,10 +335,10 @@ export function CommandPalette({ {/* Results list */} {filteredItems.length === 0 ? ( - + 没有找到匹配的结果 - 试试其他关键词 + 试试其他关键词 ) : ( groupedItems.map((group) => { @@ -348,14 +348,14 @@ export function CommandPalette({ return ( {/* Group header */} - + {TYPE_BADGE[group.type].label} - {group.items.length} + {group.items.length} {/* Group items */} @@ -369,8 +369,8 @@ export function CommandPalette({ className={` flex items-center gap-3 px-4 py-2 cursor-pointer transition-colors duration-75 ${isSelected - ? 'bg-accent-blue/15 text-gray-100' - : 'text-gray-300 hover:bg-dark-700' + ? 'bg-surface-baseccent-blue/15 text-content-primary' + : 'text-content-secondary hover:bg-dark-700' } `} onClick={() => { @@ -382,7 +382,7 @@ export function CommandPalette({ {/* Icon */} {item.icon} @@ -392,7 +392,7 @@ export function CommandPalette({ {item.label} {item.description && ( - + {item.description} )} @@ -400,7 +400,7 @@ export function CommandPalette({ {/* Shortcut */} {item.shortcut && ( - + {item.shortcut} )} @@ -421,7 +421,7 @@ export function CommandPalette({ {/* Footer hint */} - + ↑↓ diff --git a/src/components/common/ConfirmDialog.tsx b/src/components/common/ConfirmDialog.tsx index 7396063..2d9de57 100644 --- a/src/components/common/ConfirmDialog.tsx +++ b/src/components/common/ConfirmDialog.tsx @@ -94,7 +94,7 @@ export function useConfirmDialog() { {state.danger && ( )} - {state.message} + {state.message} ) diff --git a/src/components/common/DefaultFallback.tsx b/src/components/common/DefaultFallback.tsx index ca0850c..d1155e9 100644 --- a/src/components/common/DefaultFallback.tsx +++ b/src/components/common/DefaultFallback.tsx @@ -18,13 +18,13 @@ export default function DefaultFallback({ error, onReset }: DefaultFallbackProps - 出错了 - 应用遇到意外问题 + 出错了 + 应用遇到意外问题 - {appError.message} + {appError.message} {appError.recoverable && ( 此错误可以恢复,请尝试刷新页面 )} @@ -33,19 +33,19 @@ export default function DefaultFallback({ error, onReset }: DefaultFallbackProps 刷新页面 window.location.reload()} - className="px-4 py-2 bg-dark-700 hover:bg-dark-600 text-gray-300 rounded-lg transition-colors text-sm" + className="px-4 py-2 bg-dark-700 hover:bg-dark-600 text-content-secondary rounded-lg transition-colors text-sm" > 重启应用 - + 如果问题持续存在,请检查日志或联系支持 diff --git a/src/components/common/EmptyState.tsx b/src/components/common/EmptyState.tsx index e59b23e..158dccb 100644 --- a/src/components/common/EmptyState.tsx +++ b/src/components/common/EmptyState.tsx @@ -38,10 +38,10 @@ export default function EmptyState({ {/* 标题 */} - {title} + {title} {/* 描述 */} - {description} + {description} {/* 操作按钮 */} {action && ( diff --git a/src/components/common/Loading.tsx b/src/components/common/Loading.tsx index 3d88548..dfa853a 100644 --- a/src/components/common/Loading.tsx +++ b/src/components/common/Loading.tsx @@ -50,7 +50,7 @@ export default function Loading({ {text && ( - {text} + {text} )} ) diff --git a/src/components/common/Modal.tsx b/src/components/common/Modal.tsx index 3cc56b1..ae1e96e 100644 --- a/src/components/common/Modal.tsx +++ b/src/components/common/Modal.tsx @@ -74,7 +74,7 @@ export default function Modal({ {/* 遮罩 */} @@ -82,12 +82,12 @@ export default function Modal({ {/* 头部 */} - {title} + {title} - + diff --git a/src/components/common/PromptDialog.tsx b/src/components/common/PromptDialog.tsx index 26be7e8..0d0c49b 100644 --- a/src/components/common/PromptDialog.tsx +++ b/src/components/common/PromptDialog.tsx @@ -104,7 +104,7 @@ export function usePromptDialog() { cancelText={state.cancelText} > - {state.message} + {state.message} setState((prev) => ({ ...prev, value: e.target.value }))} onKeyDown={handleKeyDown} placeholder={state.placeholder} - className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:border-accent-blue" + className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> diff --git a/src/components/common/Skeleton.tsx b/src/components/common/Skeleton.tsx index a3e0cda..0f984d9 100644 --- a/src/components/common/Skeleton.tsx +++ b/src/components/common/Skeleton.tsx @@ -257,7 +257,7 @@ export function TabContentSkeleton({ className }: TabContentSkeletonProps) { return ( @@ -287,7 +287,7 @@ export function TabContentSkeleton({ className }: TabContentSkeletonProps) { $ diff --git a/src/components/common/Toast.tsx b/src/components/common/Toast.tsx index 32d59b8..4d75a76 100644 --- a/src/components/common/Toast.tsx +++ b/src/components/common/Toast.tsx @@ -32,13 +32,13 @@ const colorMap: Record - + ) diff --git a/src/components/common/ToastContainer.tsx b/src/components/common/ToastContainer.tsx index fba3849..340b213 100644 --- a/src/components/common/ToastContainer.tsx +++ b/src/components/common/ToastContainer.tsx @@ -18,7 +18,7 @@ const iconMap: Record = { } const bgMap: Record = { - info: 'bg-blue-900/80 border-blue-700', + info: 'bg-surface-baselue-900/80 border-blue-700', success: 'bg-green-900/80 border-green-700', error: 'bg-red-900/80 border-red-700', warning: 'bg-yellow-900/80 border-yellow-700', @@ -32,7 +32,7 @@ export function ToastContainer({ toasts, onRemove }: ToastContainerProps) { {toasts.map((toast) => ( {iconMap[toast.type]} {toast.message} @@ -40,7 +40,7 @@ export function ToastContainer({ toasts, onRemove }: ToastContainerProps) { onClick={() => onRemove(toast.id)} className="p-0.5 hover:bg-white/10 rounded" > - + ))} diff --git a/src/components/sftp/Breadcrumb.tsx b/src/components/sftp/Breadcrumb.tsx index 8471f18..35b272f 100644 --- a/src/components/sftp/Breadcrumb.tsx +++ b/src/components/sftp/Breadcrumb.tsx @@ -20,11 +20,11 @@ export function Breadcrumb({ path, onNavigate, onHome }: BreadcrumbProps) { className="p-1.5 hover:bg-dark-700 rounded transition-colors flex-shrink-0" title="主目录" > - + onNavigate('/')} - className="px-1.5 py-0.5 hover:bg-dark-700 rounded text-gray-400 hover:text-dark-200 transition-colors" + className="px-1.5 py-0.5 hover:bg-dark-700 rounded text-txt-muted hover:text-dark-200 transition-colors" > / @@ -33,7 +33,7 @@ export function Breadcrumb({ path, onNavigate, onHome }: BreadcrumbProps) { onNavigate('/' + parts.slice(0, index + 1).join('/'))} - className="px-1.5 py-0.5 hover:bg-dark-700 rounded text-gray-200 hover:text-white transition-colors truncate max-w-[120px]" + className="px-1.5 py-0.5 hover:bg-dark-700 rounded text-content-primary hover:text-content-primary transition-colors truncate max-w-[120px]" > {part} diff --git a/src/components/sftp/ChmodDialog.tsx b/src/components/sftp/ChmodDialog.tsx index 92e3d1e..af136bf 100644 --- a/src/components/sftp/ChmodDialog.tsx +++ b/src/components/sftp/ChmodDialog.tsx @@ -108,7 +108,7 @@ export default function ChmodDialog({ onChange={() => togglePerm(scope, perm)} className="w-4 h-4 rounded border-dark-500 bg-dark-700 text-accent-blue focus:ring-accent-blue focus:ring-offset-0" /> - {label} + {label} ) @@ -120,7 +120,7 @@ export default function ChmodDialog({ label: string }) => ( - {label} + {label} @@ -141,14 +141,14 @@ export default function ChmodDialog({ - {fileName} - {filePath} + {fileName} + {filePath} {/* Current octal mode display */} - 当前权限: + 当前权限: {octalString} @@ -156,9 +156,9 @@ export default function ChmodDialog({ - 读取 (r) - 写入 (w) - 执行 (x) + 读取 (r) + 写入 (w) + 执行 (x) diff --git a/src/components/sftp/ConnectionPanel.tsx b/src/components/sftp/ConnectionPanel.tsx index 4f37a33..b23de8f 100644 --- a/src/components/sftp/ConnectionPanel.tsx +++ b/src/components/sftp/ConnectionPanel.tsx @@ -42,7 +42,7 @@ export function ConnectionPanel({ // Connecting State if (isConnecting) { return ( - + 连接中... @@ -71,10 +71,10 @@ export function ConnectionPanel({ return ( <> - 未连接 + 未连接 选择服务器 diff --git a/src/components/sftp/ConnectionSelector.tsx b/src/components/sftp/ConnectionSelector.tsx index 2de403a..ceabbcf 100644 --- a/src/components/sftp/ConnectionSelector.tsx +++ b/src/components/sftp/ConnectionSelector.tsx @@ -16,14 +16,14 @@ export function ConnectionSelector({ sessions, onSelect, onClose }: ConnectionSe - 选择服务器 + 选择服务器 - + {sessions.length === 0 ? ( - + 暂无保存的会话 请先在侧边栏创建会话 @@ -38,12 +38,12 @@ export function ConnectionSelector({ sessions, onSelect, onClose }: ConnectionSe > - {session.name} - + {session.name} + {session.username}@{session.host}:{session.port} - + ))} diff --git a/src/components/sftp/ContextMenu.tsx b/src/components/sftp/ContextMenu.tsx index 6349d75..7901a22 100644 --- a/src/components/sftp/ContextMenu.tsx +++ b/src/components/sftp/ContextMenu.tsx @@ -56,7 +56,7 @@ export function ContextMenu({ x, y, items, onClose }: ContextMenuProps) { onClose() }} className={`w-full flex items-center gap-3 px-4 py-2 text-sm transition-colors ${ - item.danger ? 'text-red-400 hover:bg-red-500/20' : 'text-gray-200 hover:bg-dark-700 hover:text-white' + item.danger ? 'text-red-400 hover:bg-red-500/20' : 'text-content-primary hover:bg-dark-700 hover:text-content-primary' }`} > {item.icon} diff --git a/src/components/sftp/FileListPanel.tsx b/src/components/sftp/FileListPanel.tsx index 23f27d2..e502605 100644 --- a/src/components/sftp/FileListPanel.tsx +++ b/src/components/sftp/FileListPanel.tsx @@ -156,7 +156,7 @@ export function FileListPanel({ {/* Column Headers */} - + @@ -185,7 +185,7 @@ export function FileListPanel({ {/* Drag overlay - pointer-events-none so it doesn't interfere with drop events */} {isDragOver && ( - + 拖拽文件到此处上传 @@ -195,7 +195,7 @@ export function FileListPanel({ {/* Status Bar */} - + {loading ? '加载中...' : selected.size > 0 ? `已选择 ${selected.size} 项` : `共 ${files.length} 项`} diff --git a/src/components/sftp/FilePropertiesDialog.tsx b/src/components/sftp/FilePropertiesDialog.tsx index d1b3a82..8c2c484 100644 --- a/src/components/sftp/FilePropertiesDialog.tsx +++ b/src/components/sftp/FilePropertiesDialog.tsx @@ -128,13 +128,13 @@ export default function FilePropertiesDialog({ - {fileName} - {typeLabel} + {fileName} + {typeLabel} {loading && ( - + 加载详细信息... @@ -169,10 +169,10 @@ function PropertyRow({ }) { return ( - {icon} - {label} + {icon} + {label} {value} diff --git a/src/components/sftp/FileRow.tsx b/src/components/sftp/FileRow.tsx index 5b93cb3..5f10648 100644 --- a/src/components/sftp/FileRow.tsx +++ b/src/components/sftp/FileRow.tsx @@ -24,7 +24,7 @@ export function FileRow({ file, isSelected, onSelect, onOpen, onContextMenu, onD draggable={file.type === 'file'} onDragStart={onDragStart} className={`h-10 flex items-center px-4 cursor-pointer border-b border-dark-700/20 transition-colors group ${ - isSelected ? 'bg-accent-blue/20 hover:bg-accent-blue/25' : 'hover:bg-dark-700/50' + isSelected ? 'bg-surface-baseccent-blue/20 hover:bg-surface-baseccent-blue/25' : 'hover:bg-dark-700/50' }`} > @@ -33,16 +33,16 @@ export function FileRow({ file, isSelected, onSelect, onOpen, onContextMenu, onD ) : file.type === 'symlink' ? ( ) : ( - + )} - {file.name} + {file.name} {file.type === 'symlink' && →} - {formatDate(file.modified)} - + {formatDate(file.modified)} + {file.type === 'directory' ? '--' : formatSize(file.size)} - + {file.type === 'directory' ? '文件夹' : file.type === 'symlink' diff --git a/src/components/sftp/SortHeader.tsx b/src/components/sftp/SortHeader.tsx index b46ef10..a9622ba 100644 --- a/src/components/sftp/SortHeader.tsx +++ b/src/components/sftp/SortHeader.tsx @@ -20,12 +20,12 @@ export function SortHeader({ onSort, sortField, sortOrder }: SortHeaderProps) { ] return ( - + {fields.map(({ key, label, width, align }) => ( onSort(key)} - className={`${width} flex items-center justify-${align} gap-1 hover:text-gray-200 transition-colors`} + className={`${width} flex items-center justify-${align} gap-1 hover:text-content-primary transition-colors`} > {label} {sortField === key && (sortOrder === 'asc' ? : )} diff --git a/src/components/sftp/Toolbar.tsx b/src/components/sftp/Toolbar.tsx index 8028c60..891fc5c 100644 --- a/src/components/sftp/Toolbar.tsx +++ b/src/components/sftp/Toolbar.tsx @@ -21,7 +21,7 @@ export function Toolbar({ isConnected, onUpload, onDownload, onRefresh, onNewFol 上传 @@ -29,7 +29,7 @@ export function Toolbar({ isConnected, onUpload, onDownload, onRefresh, onNewFol 下载 @@ -37,7 +37,7 @@ export function Toolbar({ isConnected, onUpload, onDownload, onRefresh, onNewFol 刷新 @@ -45,7 +45,7 @@ export function Toolbar({ isConnected, onUpload, onDownload, onRefresh, onNewFol 新建文件夹 diff --git a/src/components/sftp/TransferItem.tsx b/src/components/sftp/TransferItem.tsx index c9a3e82..f899069 100644 --- a/src/components/sftp/TransferItem.tsx +++ b/src/components/sftp/TransferItem.tsx @@ -21,19 +21,19 @@ export function TransferItem({ task, onCancel, onRemove }: TransferItemProps) { {task.type === 'download' ? : } - {task.fileName} + {task.fileName} {isActive && ( <> - + - {task.progress}% - {task.speed !== undefined && {formatSpeed(task.speed)}} + {task.progress}% + {task.speed !== undefined && {formatSpeed(task.speed)}} > )} {task.status === 'pending' && ( - + 等待中 @@ -51,7 +51,7 @@ export function TransferItem({ task, onCancel, onRemove }: TransferItemProps) { )} {task.status === 'canceled' && ( - + 已取消 @@ -62,7 +62,7 @@ export function TransferItem({ task, onCancel, onRemove }: TransferItemProps) { {task.status === 'transferring' && ( @@ -71,7 +71,7 @@ export function TransferItem({ task, onCancel, onRemove }: TransferItemProps) { {(task.status === 'completed' || task.status === 'failed' || task.status === 'canceled') && ( diff --git a/src/components/sftp/TransferPanel.tsx b/src/components/sftp/TransferPanel.tsx index f8aa845..7619312 100644 --- a/src/components/sftp/TransferPanel.tsx +++ b/src/components/sftp/TransferPanel.tsx @@ -30,7 +30,7 @@ export function TransferPanel({ tasks, isOpen, onToggle, onCancel, onRemove }: T 传输队列 {activeCount > 0 && ( - + {activeCount} 进行中 )} @@ -41,7 +41,7 @@ export function TransferPanel({ tasks, isOpen, onToggle, onCancel, onRemove }: T )} - {tasks.length} 个任务 + {tasks.length} 个任务 {isOpen ? : } @@ -61,7 +61,7 @@ export function TransferPanel({ tasks, isOpen, onToggle, onCancel, onRemove }: T )} {isOpen && tasks.length === 0 && ( - + 暂无传输任务 )} diff --git a/src/components/sidebar/SearchBox.tsx b/src/components/sidebar/SearchBox.tsx index 2fae7ab..a76a589 100644 --- a/src/components/sidebar/SearchBox.tsx +++ b/src/components/sidebar/SearchBox.tsx @@ -13,7 +13,7 @@ interface SearchBoxProps { export function SearchBox({ value, onChange, placeholder = '搜索...' }: SearchBoxProps) { return ( - + onToggleGroup(group)} - className="w-full flex items-center gap-2 px-4 py-2 text-sm text-gray-400 hover:bg-dark-700 transition-colors" + className="w-full flex items-center gap-2 px-4 py-2 text-sm text-txt-muted hover:bg-dark-700 transition-colors" > {group === 'default' ? '默认分组' : group} - {groupSessions.length} + {groupSessions.length} {/* Sessions - CSS Grid collapse animation */} @@ -100,8 +100,8 @@ export function SessionTree({ > - {session.name} - {session.host}:{session.port} + {session.name} + {session.host}:{session.port} ))} diff --git a/src/components/snippets/SnippetPanel.tsx b/src/components/snippets/SnippetPanel.tsx index d97921f..03593f7 100644 --- a/src/components/snippets/SnippetPanel.tsx +++ b/src/components/snippets/SnippetPanel.tsx @@ -54,37 +54,37 @@ function SnippetFormModal({ isOpen, initial, title, confirmText, onConfirm, onCl {/* 名称 */} - 名称 + 名称 setForm((f) => ({ ...f, name: e.target.value }))} placeholder="例如:重启 Nginx" - className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:border-accent-blue text-sm" + className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue text-sm" /> {/* 命令内容 */} - 命令 + 命令 setForm((f) => ({ ...f, content: e.target.value }))} placeholder="sudo systemctl restart nginx" rows={4} - className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:border-accent-blue text-sm font-mono resize-none" + className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue text-sm font-mono resize-none" /> {/* 标签 */} - 标签 (逗号分隔) + 标签 (逗号分隔) setForm((f) => ({ ...f, tags: e.target.value }))} placeholder="linux, nginx, ops" - className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-white placeholder-gray-500 focus:outline-none focus:border-accent-blue text-sm" + className="w-full px-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue text-sm" /> @@ -203,26 +203,26 @@ export default function SnippetPanel() { {/* 头部 */} - 命令片段 + 命令片段 {/* 搜索框 */} - + setQuery(e.target.value)} placeholder="搜索片段..." - className="w-full pl-9 pr-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-sm text-white placeholder-gray-500 focus:outline-none focus:border-accent-blue" + className="w-full pl-9 pr-3 py-2 bg-dark-700 border border-dark-600 rounded-lg text-sm text-content-primary placeholder-ph focus:outline-none focus:border-accent-blue" /> {query && ( setQuery('')} className="absolute right-2 top-1/2 -translate-y-1/2 p-0.5 hover:bg-dark-600 rounded" > - + )} @@ -307,7 +307,7 @@ function SnippetItem({ snippet, copied, canInsert, onInsert, onCopy, onEdit, onD {/* 名称行 */} - + {snippet.name} @@ -316,7 +316,7 @@ function SnippetItem({ snippet, copied, canInsert, onInsert, onCopy, onEdit, onD onClick={() => onInsert(snippet.content)} disabled={!canInsert} title={canInsert ? '插入到终端' : '无活跃终端'} - className="p-1 rounded hover:bg-accent-blue/20 text-accent-blue disabled:text-gray-600 disabled:hover:bg-transparent transition-colors" + className="p-1 rounded hover:bg-surface-baseccent-blue/20 text-accent-blue disabled:text-txt-faint disabled:hover:bg-transparent transition-colors" > @@ -324,7 +324,7 @@ function SnippetItem({ snippet, copied, canInsert, onInsert, onCopy, onEdit, onD onCopy(snippet)} title="复制" - className="p-1 rounded hover:bg-dark-600 text-gray-400 transition-colors" + className="p-1 rounded hover:bg-dark-600 text-txt-muted transition-colors" > {copied ? ( ✓ @@ -336,7 +336,7 @@ function SnippetItem({ snippet, copied, canInsert, onInsert, onCopy, onEdit, onD onEdit(snippet)} title="编辑" - className="p-1 rounded hover:bg-dark-600 text-gray-400 transition-colors" + className="p-1 rounded hover:bg-dark-600 text-txt-muted transition-colors" > @@ -348,13 +348,13 @@ function SnippetItem({ snippet, copied, canInsert, onInsert, onCopy, onEdit, onD await onDelete(snippet.id) setConfirmDelete(false) }} - className="px-1.5 py-0.5 text-xs bg-red-600 hover:bg-red-500 text-white rounded transition-colors" + className="px-1.5 py-0.5 text-xs bg-red-600 hover:bg-red-500 text-content-primary rounded transition-colors" > 确认 setConfirmDelete(false)} - className="px-1.5 py-0.5 text-xs text-gray-400 hover:text-gray-200 transition-colors" + className="px-1.5 py-0.5 text-xs text-txt-muted hover:text-content-primary transition-colors" > 取消 @@ -363,7 +363,7 @@ function SnippetItem({ snippet, copied, canInsert, onInsert, onCopy, onEdit, onD setConfirmDelete(true)} title="删除" - className="p-1 rounded hover:bg-red-500/20 text-gray-400 hover:text-red-400 transition-colors" + className="p-1 rounded hover:bg-red-500/20 text-txt-muted hover:text-red-400 transition-colors" > @@ -373,7 +373,7 @@ function SnippetItem({ snippet, copied, canInsert, onInsert, onCopy, onEdit, onD {/* 命令预览 */} {preview} @@ -385,7 +385,7 @@ function SnippetItem({ snippet, copied, canInsert, onInsert, onCopy, onEdit, onD {snippet.tags.map((tag) => ( {tag} diff --git a/src/components/tabbar/TabContextMenu.tsx b/src/components/tabbar/TabContextMenu.tsx index 5d0cc9b..a46940a 100644 --- a/src/components/tabbar/TabContextMenu.tsx +++ b/src/components/tabbar/TabContextMenu.tsx @@ -77,7 +77,7 @@ export function TabContextMenu({ w-full px-4 py-2 text-left text-sm transition-colors ${item.danger ? 'text-red-400 hover:bg-red-500/20' - : 'text-gray-300 hover:bg-dark-700' + : 'text-content-secondary hover:bg-dark-700' } `} > diff --git a/src/components/tabbar/TabItem.tsx b/src/components/tabbar/TabItem.tsx index 8c96679..950a5f7 100644 --- a/src/components/tabbar/TabItem.tsx +++ b/src/components/tabbar/TabItem.tsx @@ -63,19 +63,19 @@ export function TabItem({ border-r border-dark-700/20 cursor-pointer select-none transition-all duration-150 relative ${isActive - ? 'bg-dark-900 text-gray-200' - : 'bg-dark-800 text-gray-500 hover:bg-dark-700 hover:text-gray-400' + ? 'bg-dark-900 text-content-primary' + : 'bg-dark-800 text-txt-muted hover:bg-dark-700 hover:text-txt-muted' } ${isDragging ? 'opacity-50' : 'opacity-100'} `} > {/* Left drop indicator */} {showLeftIndicator && ( - + )} {/* Right drop indicator */} {showRightIndicator && ( - + )} {/* Status indicator */} @@ -110,7 +110,7 @@ export function TabItem({ {/* Active tab bottom indicator — always rendered for width transition */} - + - 会话已恢复 - + 会话已恢复 + 上次关闭时的终端标签已恢复 点击下方按钮重新建立 SSH 连接 重新连接 @@ -48,18 +48,18 @@ function TerminalOverlay({ 连接已挂起 - + 长时间未操作,连接已进入挂起状态 恢复连接 重新连接 @@ -72,10 +72,10 @@ function TerminalOverlay({ 连接失败 - {statusMessage} + {statusMessage} 重新连接 diff --git a/src/components/terminal/TerminalSearchBar.tsx b/src/components/terminal/TerminalSearchBar.tsx index 57f2aa2..c21a7a5 100644 --- a/src/components/terminal/TerminalSearchBar.tsx +++ b/src/components/terminal/TerminalSearchBar.tsx @@ -24,7 +24,7 @@ function TerminalSearchBar({ }: TerminalSearchBarProps) { return ( - + {searchResultCount !== undefined && ( - + {searchResultIndex ?? 0}/{searchResultCount} )} diff --git a/src/hooks/useTheme.ts b/src/hooks/useTheme.ts index e6bc477..13b031d 100644 --- a/src/hooks/useTheme.ts +++ b/src/hooks/useTheme.ts @@ -6,9 +6,12 @@ import { darkColors, lightColors, type AppThemeMode, type ColorMap } from '../th * Resolves the *effective* theme ('dark' | 'light') from the user preference, * including the 'system' → prefers-color-scheme mapping. */ -function resolveEffectiveTheme(_preference: AppThemeMode): 'dark' | 'light' { - // 当前版本固定使用 dark 主题,后续版本开放主题切换 - return 'dark' +function resolveEffectiveTheme(preference: AppThemeMode): 'dark' | 'light' { + if (preference === 'system') { + if (typeof window === 'undefined') return 'dark' + return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' + } + return preference } /** @@ -38,8 +41,10 @@ export function useTheme() { useEffect(() => { const colors = effective === 'dark' ? darkColors : lightColors - // Set data attribute for potential direct CSS selectors + // Set data attribute for CSS selectors document.documentElement.setAttribute('data-theme', effective) + + // Toggle Tailwind dark class if (effective === 'dark') { document.documentElement.classList.add('dark') } else { @@ -49,9 +54,9 @@ export function useTheme() { // Apply CSS custom properties applyCSSVars(colors) - // Update body bg & colour (mirrors index.css body rules but via vars) - document.body.style.backgroundColor = '#0f172a' - document.body.style.color = '#e2e8f0' + // Update body bg & colour from tokens (not hardcoded) + document.body.style.backgroundColor = colors['surface-base'] || '#0f172a' + document.body.style.color = colors['text-primary'] || '#e2e8f0' }, [effective]) // Listen for system colour-scheme changes when preference is 'system' @@ -64,9 +69,16 @@ export function useTheme() { const newEffective = e.matches ? 'dark' : 'light' const colors = e.matches ? darkColors : lightColors document.documentElement.setAttribute('data-theme', newEffective) + + if (newEffective === 'dark') { + document.documentElement.classList.add('dark') + } else { + document.documentElement.classList.remove('dark') + } + applyCSSVars(colors) - document.body.style.backgroundColor = '#0f172a' - document.body.style.color = '#e2e8f0' + document.body.style.backgroundColor = colors['surface-base'] || '#0f172a' + document.body.style.color = colors['text-primary'] || '#e2e8f0' } mql.addEventListener('change', handler) diff --git a/src/index.css b/src/index.css index 5ab1923..04b481c 100644 --- a/src/index.css +++ b/src/index.css @@ -3,55 +3,78 @@ @tailwind utilities; /* =================================================================== - * OpsXShell v1.3.0 — Theme CSS Variables + * OpsXShell v1.4.0 — Professional Theme System * - * The default (no data-theme attribute) keeps dark mode as before so - * that the very first paint looks correct even before React hydrates. - * useTheme.ts will immediately set data-theme + override vars on mount. + * Default (no data-theme) is dark mode for correct first paint. + * useTheme.ts immediately sets data-theme + overrides vars on mount. + * + * Design: Warp-inspired warm dark + Linear luminance stacking * =================================================================== */ :root { - /* ---- Dark (default, matches original hard-coded values) ---- */ + /* ---- Dark (default, matches warm slate-navy palette) ---- */ --color-dark-900: #0f172a; --color-dark-800: #1e293b; --color-dark-700: #334155; --color-dark-600: #475569; - /* Semantic tokens (dark default) */ - --color-surface-base: #0f172a; - --color-surface-raised: #1e293b; + /* Semantic surface tokens */ + --color-surface-base: #0f172a; + --color-surface-raised: #1e293b; --color-surface-overlay: #334155; - --color-surface-inset: #475569; + --color-surface-inset: #475569; + --color-surface-hover: #374151; + --color-surface-active: #4b5563; - --color-text-primary: #e2e8f0; + /* Text */ + --color-text-primary: #f1f5f9; --color-text-secondary: #cbd5e1; --color-text-muted: #94a3b8; --color-text-faint: #64748b; --color-text-dim: #475569; - --color-content-primary: #e2e8f0; + /* Content */ + --color-content-primary: #f1f5f9; --color-content-secondary: #cbd5e1; - --color-content-tertiary: #94a3b8; - --color-content-inverse: #0f172a; + --color-content-tertiary: #94a3b8; + --color-content-inverse: #0f172a; - --color-border: #334155; - --color-border-strong: #475569; + /* Border */ + --color-border: #1e293b; + --color-border-subtle: #1e293b; + --color-border-default: #334155; + --color-border-strong: #475569; + --color-border-focus: #3b82f6; - --color-accent-blue: #3b82f6; - --color-accent-green: #10b981; - --color-accent-purple: #8b5cf6; - --color-accent-orange: #f97316; - --color-accent-red: #ef4444; + /* Accent */ + --color-accent-blue: #3b82f6; + --color-accent-blue-hover: #60a5fa; + --color-accent-green: #10b981; + --color-accent-purple: #8b5cf6; + --color-accent-orange: #f97316; + --color-accent-red: #ef4444; - --color-feedback-success: #10b981; - --color-feedback-warning: #f59e0b; - --color-feedback-error: #ef4444; - --color-feedback-info: #3b82f6; + /* Feedback */ + --color-feedback-success: #10b981; + --color-feedback-success-bg: #064e3b; + --color-feedback-warning: #f59e0b; + --color-feedback-warning-bg: #78350f; + --color-feedback-error: #ef4444; + --color-feedback-error-bg: #7f1d1d; + --color-feedback-info: #3b82f6; + --color-feedback-info-bg: #1e3a5f; - --color-scrollbar-track: #1e293b; - --color-scrollbar-thumb: #475569; - --color-scrollbar-thumb-h: #64748b; + /* Placeholder */ + --color-placeholder: #64748b; + --color-placeholder-disabled: #475569; + /* UI chrome */ + --color-scrollbar-track: #1e293b; + --color-scrollbar-thumb: #475569; + --color-scrollbar-thumb-h: #64748b; + --color-shadow-color: rgba(0, 0, 0, 0.4); + + /* Terminal */ --color-terminal-bg: #0f172a; } @@ -67,8 +90,8 @@ html, body { sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; - background-color: #0f172a !important; - color: #e2e8f0; + background-color: var(--color-surface-base) !important; + color: var(--color-text-primary); overflow: hidden; } @@ -217,7 +240,7 @@ button { 100% { background-position: 200% 0; } } .animate-shimmer { - background: linear-gradient(90deg, var(--color-dark-800) 25%, var(--color-dark-700) 50%, var(--color-dark-800) 75%); + background: linear-gradient(90deg, var(--color-surface-raised) 25%, var(--color-surface-overlay) 50%, var(--color-surface-raised) 75%); background-size: 200% 100%; animation: shimmer 1.5s ease-in-out infinite; } diff --git a/src/themes/app-theme.ts b/src/themes/app-theme.ts index 6513463..1d7fbe2 100644 --- a/src/themes/app-theme.ts +++ b/src/themes/app-theme.ts @@ -1,13 +1,14 @@ /** - * OpsXShell v1.3.0 — App Theme System + * OpsXShell v1.4.0 — Professional Theme System * * CSS-variable-driven theme definitions for Dark / Light / System modes. - * Terminal themes (terminal.ts) remain independent — this only covers the - * Chrome / UI layer. + * Inspired by Warp (warm dark terminal) + Linear (precise luminance stacking). * - * Design references: - * - Dark: existing slate-blue palette - * - Light: VS Code Light+ inspired — white canvas, soft grey borders, blue accent + * Design principles: + * - Dark: warm near-black base, soft parchment text, restrained accent + * - Light: clean canvas, ink-dark text, vibrant accent + * - All colors use semantic tokens so components never reference raw values + * - Surface elevation via luminance stepping, not shadows */ // --------------------------------------------------------------------------- @@ -20,109 +21,170 @@ export type AppThemeMode = 'dark' | 'light' | 'system' export type ColorMap = Record // --------------------------------------------------------------------------- -// Dark palette (unchanged from original hard-coded values) +// Dark palette +// +// Inspired by: +// - Warp: warm near-black base (#0f172a slate-blue), cream-white text +// - Linear: luminance-stacking surface hierarchy, semi-transparent borders +// +// Surface ladder: base → raised → overlay → inset (deep → shallow) +// Text ladder: primary → secondary → muted → faint → dim (hot → cold) // --------------------------------------------------------------------------- - export const darkColors: ColorMap = { - // Background surfaces (deepest → shallowest) + // ── Background surfaces (deepest → shallowest) ────────────────────── + // Level 0: Page canvas — deep slate-navy (Warp-inspired warmth) 'dark-900': '#0f172a', + // Level 1: Sidebar / panels — one step up 'dark-800': '#1e293b', + // Level 2: Cards / elevated surfaces — visible but restrained 'dark-700': '#334155', + // Level 3: Hover / active states — lightest dark surface 'dark-600': '#475569', - // Semantic surface tokens - 'surface-base': '#0f172a', - 'surface-raised': '#1e293b', + // ── Semantic surface tokens ────────────────────────────────────────── + 'surface-base': '#0f172a', + 'surface-raised': '#1e293b', 'surface-overlay': '#334155', - 'surface-inset': '#475569', + 'surface-inset': '#475569', + // Hover elevation + 'surface-hover': '#374151', // gray-700 — subtle lift on hover + // Active/pressed + 'surface-active': '#4b5563', // gray-600 — pressed state - // Text - 'text-primary': '#e2e8f0', // gray-200 - 'text-secondary': '#cbd5e1', // gray-300 - 'text-muted': '#94a3b8', // gray-400 - 'text-faint': '#64748b', // gray-500 - 'text-dim': '#475569', // gray-600 + // ── Text ───────────────────────────────────────────────────────────── + 'text-primary': '#f1f5f9', // warm parchment (slate-50) — not pure white + 'text-secondary': '#cbd5e1', // slate-300 — comfortable reading + 'text-muted': '#94a3b8', // slate-400 — secondary info + 'text-faint': '#64748b', // slate-500 — placeholder / dim + 'text-dim': '#475569', // slate-600 — disabled / decorative - // Content tokens - 'content-primary': '#e2e8f0', + // ── Content tokens ─────────────────────────────────────────────────── + 'content-primary': '#f1f5f9', 'content-secondary': '#cbd5e1', - 'content-tertiary': '#94a3b8', - 'content-inverse': '#0f172a', + 'content-tertiary': '#94a3b8', + 'content-inverse': '#0f172a', - // Border tokens - 'border': '#334155', - 'border-strong': '#475569', + // ── Border tokens ──────────────────────────────────────────────────── + // Linear-inspired: semi-transparent feel via opaque colors + 'border': '#1e293b', // whisper-thin separator + 'border-subtle': '#1e293b', // minimal visibility + 'border-default': '#334155', // standard component border + 'border-strong': '#475569', // emphasis / focus + 'border-focus': '#3b82f6', // input focus ring (blue accent) - // Accent - 'accent-blue': '#3b82f6', - 'accent-green': '#10b981', - 'accent-purple': '#8b5cf6', - 'accent-orange': '#f97316', - 'accent-red': '#ef4444', + // ── Accent ─────────────────────────────────────────────────────────── + 'accent-blue': '#3b82f6', + 'accent-blue-hover': '#60a5fa', // lighter blue for hover + 'accent-green': '#10b981', + 'accent-purple': '#8b5cf6', + 'accent-orange': '#f97316', + 'accent-red': '#ef4444', - // Feedback tokens - 'feedback-success': '#10b981', - 'feedback-warning': '#f59e0b', - 'feedback-error': '#ef4444', - 'feedback-info': '#3b82f6', + // ── Feedback tokens ────────────────────────────────────────────────── + 'feedback-success': '#10b981', + 'feedback-success-bg': '#064e3b', // emerald-900 tint + 'feedback-warning': '#f59e0b', + 'feedback-warning-bg': '#78350f', // amber-900 tint + 'feedback-error': '#ef4444', + 'feedback-error-bg': '#7f1d1d', // red-900 tint + 'feedback-info': '#3b82f6', + 'feedback-info-bg': '#1e3a5f', // blue-900 tint - // UI chrome + // ── Placeholder ────────────────────────────────────────────────────── + 'placeholder': '#64748b', // slate-500 + 'placeholder-disabled': '#475569', // slate-600 + + // ── UI chrome ──────────────────────────────────────────────────────── 'scrollbar-track': '#1e293b', 'scrollbar-thumb': '#475569', 'scrollbar-thumb-h': '#64748b', + 'shadow-color': 'rgba(0, 0, 0, 0.4)', + + // ── Terminal ───────────────────────────────────────────────────────── + 'terminal-bg': '#0f172a', } // --------------------------------------------------------------------------- -// Light palette (VS Code Light+ inspired) +// Light palette +// +// Inspired by: +// - Notion: warm white canvas, soft borders, clean hierarchy +// - VS Code Light+: readable code editor aesthetic +// - Linear light: subtle cool gray surfaces +// +// Key: warmer than typical "sterile white" — invites long sessions // --------------------------------------------------------------------------- - export const lightColors: ColorMap = { - // Background surfaces + // ── Background surfaces (lightest → darkest) ──────────────────────── + // Level 0: Page canvas — warm white 'dark-900': '#ffffff', - 'dark-800': '#f3f4f6', // gray-100 - 'dark-700': '#e5e7eb', // gray-200 - 'dark-600': '#d1d5db', // gray-300 + // Level 1: Sidebar / panels — barely-warm gray + 'dark-800': '#f8fafc', // slate-50 + // Level 2: Cards / elevated — soft boundary + 'dark-700': '#f1f5f9', // slate-100 + // Level 3: Hover / active — noticeable surface + 'dark-600': '#e2e8f0', // slate-200 - // Semantic surface tokens - 'surface-base': '#ffffff', - 'surface-raised': '#f3f4f6', - 'surface-overlay': '#e5e7eb', - 'surface-inset': '#d1d5db', + // ── Semantic surface tokens ────────────────────────────────────────── + 'surface-base': '#ffffff', + 'surface-raised': '#f8fafc', + 'surface-overlay': '#f1f5f9', + 'surface-inset': '#e2e8f0', + // Hover elevation + 'surface-hover': '#e2e8f0', // slate-200 + // Active/pressed + 'surface-active': '#cbd5e1', // slate-300 - // Text - 'text-primary': '#1f2937', // gray-800 - 'text-secondary': '#374151', // gray-700 - 'text-muted': '#6b7280', // gray-500 - 'text-faint': '#9ca3af', // gray-400 - 'text-dim': '#d1d5db', // gray-300 + // ── Text ───────────────────────────────────────────────────────────── + 'text-primary': '#0f172a', // slate-900 — deep ink (NOT pure black) + 'text-secondary': '#334155', // slate-700 — comfortable body text + 'text-muted': '#64748b', // slate-500 — secondary info + 'text-faint': '#94a3b8', // slate-400 — placeholder / dim + 'text-dim': '#cbd5e1', // slate-300 — disabled / decorative - // Content tokens - 'content-primary': '#1f2937', - 'content-secondary': '#374151', - 'content-tertiary': '#6b7280', - 'content-inverse': '#ffffff', + // ── Content tokens ─────────────────────────────────────────────────── + 'content-primary': '#0f172a', + 'content-secondary': '#334155', + 'content-tertiary': '#64748b', + 'content-inverse': '#ffffff', - // Border tokens - 'border': '#e5e7eb', - 'border-strong': '#d1d5db', + // ── Border tokens ──────────────────────────────────────────────────── + 'border': '#f1f5f9', // slate-100 — whisper thin + 'border-subtle': '#f1f5f9', // minimal + 'border-default': '#e2e8f0', // slate-200 — standard + 'border-strong': '#cbd5e1', // slate-300 — emphasis + 'border-focus': '#3b82f6', // same blue accent - // Accent (kept the same hue family, slightly adjusted for light bg) - 'accent-blue': '#2563eb', - 'accent-green': '#059669', - 'accent-purple': '#7c3aed', - 'accent-orange': '#ea580c', - 'accent-red': '#dc2626', + // ── Accent ─────────────────────────────────────────────────────────── + 'accent-blue': '#2563eb', // blue-600 — deeper for white bg + 'accent-blue-hover': '#1d4ed8', // blue-700 — hover + 'accent-green': '#059669', // emerald-600 + 'accent-purple': '#7c3aed', // violet-600 + 'accent-orange': '#ea580c', // orange-600 + 'accent-red': '#dc2626', // red-600 - // Feedback tokens - 'feedback-success': '#059669', - 'feedback-warning': '#d97706', - 'feedback-error': '#dc2626', - 'feedback-info': '#2563eb', + // ── Feedback tokens ────────────────────────────────────────────────── + 'feedback-success': '#059669', + 'feedback-success-bg': '#d1fae5', // emerald-100 + 'feedback-warning': '#d97706', + 'feedback-warning-bg': '#fef3c7', // amber-100 + 'feedback-error': '#dc2626', + 'feedback-error-bg': '#fee2e2', // red-100 + 'feedback-info': '#2563eb', + 'feedback-info-bg': '#dbeafe', // blue-100 - // UI chrome - 'scrollbar-track': '#f3f4f6', - 'scrollbar-thumb': '#d1d5db', - 'scrollbar-thumb-h': '#9ca3af', + // ── Placeholder ────────────────────────────────────────────────────── + 'placeholder': '#94a3b8', // slate-400 + 'placeholder-disabled': '#cbd5e1', // slate-300 + + // ── UI chrome ──────────────────────────────────────────────────────── + 'scrollbar-track': '#f8fafc', + 'scrollbar-thumb': '#cbd5e1', + 'scrollbar-thumb-h': '#94a3b8', + 'shadow-color': 'rgba(0, 0, 0, 0.08)', + + // ── Terminal ───────────────────────────────────────────────────────── + 'terminal-bg': '#ffffff', } // --------------------------------------------------------------------------- diff --git a/tailwind.config.js b/tailwind.config.js index b0002cf..9f09e33 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -8,36 +8,68 @@ export default { theme: { extend: { colors: { - // Semantic color tokens — driven by CSS custom properties set per theme + // ── Semantic surface tokens (theme-aware via CSS vars) ────────── surface: { - base: 'var(--color-surface-base)', - raised: 'var(--color-surface-raised)', + base: 'var(--color-surface-base)', + raised: 'var(--color-surface-raised)', overlay: 'var(--color-surface-overlay)', - inset: 'var(--color-surface-inset)', + inset: 'var(--color-surface-inset)', + hover: 'var(--color-surface-hover)', + active: 'var(--color-surface-active)', }, - // Legacy dark-* aliases for backward compatibility + // ── Legacy dark-* aliases (backward compat → surface tokens) ─── dark: { 900: 'var(--color-surface-base)', 800: 'var(--color-surface-raised)', 700: 'var(--color-surface-overlay)', 600: 'var(--color-surface-inset)', }, + // ── Accent colors ────────────────────────────────────────────── accent: { - blue: 'var(--color-accent-blue)', - green: 'var(--color-accent-green)', - purple: 'var(--color-accent-purple)', - orange: 'var(--color-accent-orange)', - red: 'var(--color-accent-red)', + blue: 'var(--color-accent-blue)', + 'blue-hover':'var(--color-accent-blue-hover)', + green: 'var(--color-accent-green)', + purple: 'var(--color-accent-purple)', + orange: 'var(--color-accent-orange)', + red: 'var(--color-accent-red)', }, + // ── Content / text tokens ────────────────────────────────────── content: { primary: 'var(--color-content-primary)', secondary: 'var(--color-content-secondary)', tertiary: 'var(--color-content-tertiary)', inverse: 'var(--color-content-inverse)', }, + // ── Text tokens (direct mapping) ─────────────────────────────── + txt: { + primary: 'var(--color-text-primary)', + secondary: 'var(--color-text-secondary)', + muted: 'var(--color-text-muted)', + faint: 'var(--color-text-faint)', + dim: 'var(--color-text-dim)', + }, + // ── Border tokens ────────────────────────────────────────────── border: { - DEFAULT: 'var(--color-border)', + DEFAULT: 'var(--color-border-default)', + subtle: 'var(--color-border-subtle)', strong: 'var(--color-border-strong)', + focus: 'var(--color-border-focus)', + }, + // ── Placeholder tokens ───────────────────────────────────────── + ph: { + DEFAULT: 'var(--color-placeholder)', + disabled: 'var(--color-placeholder-disabled)', + }, + // ── Feedback / status tokens ─────────────────────────────────── + feedback: { + 'success': 'var(--color-feedback-success)', + 'success-bg': 'var(--color-feedback-success-bg)', + 'warning': 'var(--color-feedback-warning)', + 'warning-bg': 'var(--color-feedback-warning-bg)', + 'error': 'var(--color-feedback-error)', + 'error-bg': 'var(--color-feedback-error-bg)', + 'info': 'var(--color-feedback-info)', + 'info-bg': 'var(--color-feedback-info-bg)', }, }, fontFamily: {
暂无端口转发规则
点击右上角添加规则
临时连接到服务器
快速连接不会保存会话信息。如需保存,请使用"新建会话"。
{isEditing ? '修改 SSH 连接配置' : '配置 SSH 连接信息'}
点击快捷键可重新绑定 · 按 Esc 取消录入
配置连接池行为,优化资源使用和性能
版本 {appVersion}
跨平台 SSH/SFTP 终端工具 · 简洁高效 · 安全可靠 · 操作便捷人性化
OpsXShell 基于 MIT 许可证开源。本项目使用了以下开源组件:
{currentMeta.description}
没有找到匹配的结果
试试其他关键词
{state.message}
应用遇到意外问题
{appError.message}
此错误可以恢复,请尝试刷新页面
如果问题持续存在,请检查日志或联系支持
{description}
{fileName}
{filePath}
暂无保存的会话
请先在侧边栏创建会话
{typeLabel}
{preview} @@ -385,7 +385,7 @@ function SnippetItem({ snippet, copied, canInsert, onInsert, onCopy, onEdit, onD {snippet.tags.map((tag) => ( {tag} diff --git a/src/components/tabbar/TabContextMenu.tsx b/src/components/tabbar/TabContextMenu.tsx index 5d0cc9b..a46940a 100644 --- a/src/components/tabbar/TabContextMenu.tsx +++ b/src/components/tabbar/TabContextMenu.tsx @@ -77,7 +77,7 @@ export function TabContextMenu({ w-full px-4 py-2 text-left text-sm transition-colors ${item.danger ? 'text-red-400 hover:bg-red-500/20' - : 'text-gray-300 hover:bg-dark-700' + : 'text-content-secondary hover:bg-dark-700' } `} > diff --git a/src/components/tabbar/TabItem.tsx b/src/components/tabbar/TabItem.tsx index 8c96679..950a5f7 100644 --- a/src/components/tabbar/TabItem.tsx +++ b/src/components/tabbar/TabItem.tsx @@ -63,19 +63,19 @@ export function TabItem({ border-r border-dark-700/20 cursor-pointer select-none transition-all duration-150 relative ${isActive - ? 'bg-dark-900 text-gray-200' - : 'bg-dark-800 text-gray-500 hover:bg-dark-700 hover:text-gray-400' + ? 'bg-dark-900 text-content-primary' + : 'bg-dark-800 text-txt-muted hover:bg-dark-700 hover:text-txt-muted' } ${isDragging ? 'opacity-50' : 'opacity-100'} `} > {/* Left drop indicator */} {showLeftIndicator && ( -