From cf6bff2709dc1addce9a52968958a6b80394022a Mon Sep 17 00:00:00 2001 From: lpf Date: Mon, 9 Mar 2026 15:32:18 +0800 Subject: [PATCH] fix(webui): unify button styling and chat contrast --- webui/src/components/GlobalDialog.tsx | 4 +- webui/src/index.css | 142 ++++++++++++++++++++++++++ webui/src/pages/Chat.tsx | 38 ++++--- webui/src/pages/Config.tsx | 20 ++-- webui/src/pages/Cron.tsx | 16 +-- webui/src/pages/Logs.tsx | 8 +- webui/src/pages/MCP.tsx | 12 +-- webui/src/pages/Memory.tsx | 6 +- webui/src/pages/NodeArtifacts.tsx | 10 +- webui/src/pages/Nodes.tsx | 10 +- webui/src/pages/Skills.tsx | 10 +- webui/src/pages/SubagentProfiles.tsx | 16 +-- webui/src/pages/TaskAudit.tsx | 2 +- 13 files changed, 223 insertions(+), 71 deletions(-) diff --git a/webui/src/components/GlobalDialog.tsx b/webui/src/components/GlobalDialog.tsx index c567852..dd734ce 100644 --- a/webui/src/components/GlobalDialog.tsx +++ b/webui/src/components/GlobalDialog.tsx @@ -61,9 +61,9 @@ export const GlobalDialog: React.FC<{
{(kind === 'confirm' || kind === 'prompt') && ( - + )} -
diff --git a/webui/src/index.css b/webui/src/index.css index a11135e..15c8d1f 100644 --- a/webui/src/index.css +++ b/webui/src/index.css @@ -56,6 +56,26 @@ html { --button-start: #fb923c; --button-end: #f97316; --button-shadow: rgb(249 115 22 / 0.16); + --button-neutral-bg: rgb(226 232 240 / 0.9); + --button-neutral-bg-hover: rgb(203 213 225 / 0.96); + --button-neutral-border: rgb(203 213 225 / 0.98); + --button-neutral-text: rgb(51 65 85 / 0.96); + --button-primary-bg: rgb(255 237 213 / 0.96); + --button-primary-bg-hover: rgb(255 224 189 / 0.98); + --button-primary-border: rgb(251 146 60 / 0.34); + --button-primary-text: rgb(154 52 18 / 0.98); + --button-success-bg: rgb(220 252 231 / 0.96); + --button-success-bg-hover: rgb(187 247 208 / 0.98); + --button-success-border: rgb(52 211 153 / 0.34); + --button-success-text: rgb(6 95 70 / 0.98); + --button-warning-bg: rgb(254 243 199 / 0.98); + --button-warning-bg-hover: rgb(253 230 138 / 0.98); + --button-warning-border: rgb(245 158 11 / 0.34); + --button-warning-text: rgb(146 64 14 / 0.98); + --button-danger-bg: rgb(255 228 230 / 0.98); + --button-danger-bg-hover: rgb(254 205 211 / 0.98); + --button-danger-border: rgb(244 63 94 / 0.28); + --button-danger-text: rgb(159 18 57 / 0.98); --chip-bg: rgb(226 232 240 / 0.82); --chip-bg-hover: rgb(203 213 225 / 0.92); --chip-border: rgb(203 213 225 / 0.96); @@ -125,6 +145,26 @@ html.theme-dark { --button-start: #ee9852; --button-end: #d96b25; --button-shadow: rgb(217 107 37 / 0.18); + --button-neutral-bg: rgb(36 49 69 / 0.9); + --button-neutral-bg-hover: rgb(48 63 87 / 0.96); + --button-neutral-border: rgb(93 109 135 / 0.82); + --button-neutral-text: rgb(226 232 240 / 0.96); + --button-primary-bg: rgb(232 132 58 / 0.16); + --button-primary-bg-hover: rgb(232 132 58 / 0.24); + --button-primary-border: rgb(232 132 58 / 0.3); + --button-primary-text: rgb(255 228 205 / 0.96); + --button-success-bg: rgb(16 185 129 / 0.14); + --button-success-bg-hover: rgb(16 185 129 / 0.22); + --button-success-border: rgb(52 211 153 / 0.24); + --button-success-text: rgb(209 250 229 / 0.96); + --button-warning-bg: rgb(245 158 11 / 0.14); + --button-warning-bg-hover: rgb(245 158 11 / 0.22); + --button-warning-border: rgb(251 191 36 / 0.24); + --button-warning-text: rgb(254 243 199 / 0.96); + --button-danger-bg: rgb(244 63 94 / 0.14); + --button-danger-bg-hover: rgb(244 63 94 / 0.22); + --button-danger-border: rgb(251 113 133 / 0.24); + --button-danger-text: rgb(255 228 230 / 0.96); --chip-bg: rgb(36 49 69 / 0.9); --chip-bg-hover: rgb(48 63 87 / 0.96); --chip-border: rgb(93 109 135 / 0.82); @@ -299,6 +339,108 @@ html.theme-dark .brand-button { box-shadow: 0 10px 24px var(--button-shadow); } +.ui-button { + border-radius: var(--radius-button); + border: 1px solid transparent; + transition: background-color 160ms ease, border-color 160ms ease, color 160ms ease, box-shadow 160ms ease; +} + +.ui-button:disabled { + opacity: 0.5; + cursor: not-allowed; +} + +.ui-button-neutral { + background: var(--button-neutral-bg); + border-color: var(--button-neutral-border); + color: var(--button-neutral-text); +} + +.ui-button-neutral:hover:not(:disabled) { + background: var(--button-neutral-bg-hover); +} + +.ui-button-primary { + background: var(--button-primary-bg); + border-color: var(--button-primary-border); + color: var(--button-primary-text); +} + +.ui-button-primary:hover:not(:disabled) { + background: var(--button-primary-bg-hover); +} + +.ui-button-success { + background: var(--button-success-bg); + border-color: var(--button-success-border); + color: var(--button-success-text); +} + +.ui-button-success:hover:not(:disabled) { + background: var(--button-success-bg-hover); +} + +.ui-button-warning { + background: var(--button-warning-bg); + border-color: var(--button-warning-border); + color: var(--button-warning-text); +} + +.ui-button-warning:hover:not(:disabled) { + background: var(--button-warning-bg-hover); +} + +.ui-button-danger { + background: var(--button-danger-bg); + border-color: var(--button-danger-border); + color: var(--button-danger-text); +} + +.ui-button-danger:hover:not(:disabled) { + background: var(--button-danger-bg-hover); +} + +.chat-bubble-user { + background: linear-gradient(135deg, var(--button-start) 0%, var(--button-end) 100%); + color: white; +} + +.chat-bubble-tool { + background: rgb(255 247 237 / 0.96); + border: 1px solid rgb(253 186 116 / 0.7); + color: rgb(124 45 18 / 0.98); +} + +.chat-bubble-agent { + background: rgb(226 232 240 / 0.9); + border: 1px solid rgb(203 213 225 / 0.95); + color: rgb(51 65 85 / 0.98); +} + +.chat-bubble-system { + background: rgb(226 232 240 / 0.72); + border: 1px solid rgb(203 213 225 / 0.9); + color: rgb(51 65 85 / 0.98); +} + +html.theme-dark .chat-bubble-tool { + background: rgb(123 58 24 / 0.24); + border-color: rgb(232 132 58 / 0.28); + color: rgb(255 237 213 / 0.96); +} + +html.theme-dark .chat-bubble-agent { + background: rgb(22 32 50 / 0.82); + border-color: rgb(71 85 105 / 0.76); + color: rgb(226 232 240 / 0.96); +} + +html.theme-dark .chat-bubble-system { + background: rgb(36 49 69 / 0.74); + border-color: rgb(71 85 105 / 0.74); + color: rgb(226 232 240 / 0.96); +} + .control-chip { border-radius: var(--radius-chip); background: var(--chip-bg); diff --git a/webui/src/pages/Chat.tsx b/webui/src/pages/Chat.tsx index 433cfce..0cf00b0 100644 --- a/webui/src/pages/Chat.tsx +++ b/webui/src/pages/Chat.tsx @@ -558,13 +558,13 @@ const Chat: React.FC = () => {
@@ -574,14 +574,14 @@ const Chat: React.FC = () => { )}
- + {chatTab === 'subagents' && (
@@ -589,7 +589,7 @@ const Chat: React.FC = () => { @@ -631,7 +631,7 @@ const Chat: React.FC = () => { @@ -689,14 +689,24 @@ const Chat: React.FC = () => { const isExec = m.role === 'tool' || m.role === 'exec'; const isSystem = m.role === 'system'; const bubbleClass = isUser - ? 'bg-indigo-600 text-white rounded-br-sm' + ? 'chat-bubble-user rounded-br-sm' : isExec - ? 'bg-amber-500/10 text-amber-100 rounded-bl-sm border border-amber-500/30' + ? 'chat-bubble-tool rounded-bl-sm' : isSystem - ? 'bg-zinc-700/40 text-zinc-100 rounded-bl-sm border border-zinc-600/40' + ? 'chat-bubble-system rounded-bl-sm' : m.isReadonlyGroup - ? 'bg-zinc-900/85 text-zinc-200 rounded-bl-sm border border-zinc-700/60' - : 'bg-zinc-800/80 text-zinc-200 rounded-bl-sm border border-zinc-700/50'; + ? 'chat-bubble-system rounded-bl-sm' + : 'chat-bubble-agent rounded-bl-sm'; + const metaClass = isUser + ? 'text-white/75' + : isExec + ? 'text-amber-800/75 dark:text-amber-100/75' + : 'text-zinc-500 dark:text-zinc-400'; + const subLabelClass = isUser + ? 'text-white/70' + : isExec + ? 'text-amber-700/80 dark:text-amber-100/70' + : 'text-zinc-500 dark:text-zinc-400'; return ( {
{m.avatarText || (isUser ? 'U' : 'A')}
-
{m.actorName || m.label || (isUser ? t('user') : isExec ? t('exec') : isSystem ? t('system') : t('agent'))}
- {m.metaLine &&
{m.metaLine}
} +
{m.actorName || m.label || (isUser ? t('user') : isExec ? t('exec') : isSystem ? t('system') : t('agent'))}
+ {m.metaLine &&
{m.metaLine}
}
{m.label && m.actorName && m.label !== m.actorName && ( -
{m.label}
+
{m.label}
)}

{m.text}

diff --git a/webui/src/pages/Config.tsx b/webui/src/pages/Config.tsx index b543199..e89c104 100644 --- a/webui/src/pages/Config.tsx +++ b/webui/src/pages/Config.tsx @@ -282,18 +282,18 @@ const Config: React.FC = () => {

{t('configuration')}

- - + +
- - - + setSearch(e.target.value)} placeholder={t('configSearchPlaceholder')} className="px-3 py-2 bg-zinc-950/70 border border-zinc-800 rounded-xl text-sm" />
-
@@ -345,7 +345,7 @@ const Config: React.FC = () => {
{t('configProxies')}
setNewProxyName(e.target.value)} placeholder={t('configNewProviderName')} className="px-2 py-1 rounded-lg bg-zinc-900/70 border border-zinc-700 text-xs" /> - +
@@ -355,7 +355,7 @@ const Config: React.FC = () => { updateProxyField(name, 'api_base', e.target.value)} placeholder={t('configLabels.api_base')} className="md:col-span-2 px-2 py-1 rounded-lg bg-zinc-950/70 border border-zinc-800" /> updateProxyField(name, 'api_key', e.target.value)} placeholder={t('configLabels.api_key')} className="md:col-span-2 px-2 py-1 rounded-lg bg-zinc-950/70 border border-zinc-800" /> updateProxyField(name, 'models', e.target.value.split(',').map(s=>s.trim()).filter(Boolean))} placeholder={`${t('configLabels.models')}${t('configCommaSeparatedHint')}`} className="md:col-span-1 px-2 py-1 rounded-lg bg-zinc-950/70 border border-zinc-800" /> - +
))} {Object.keys(((cfg as any)?.providers?.proxies || {}) as Record).length === 0 && ( @@ -403,7 +403,7 @@ const Config: React.FC = () => {
{t('configNodeP2PIceServers')}
- +
{Array.isArray((cfg as any)?.gateway?.nodes?.p2p?.ice_servers) && (cfg as any).gateway.nodes.p2p.ice_servers.length > 0 ? ( ((cfg as any).gateway.nodes.p2p.ice_servers as Array).map((server, index) => ( @@ -426,7 +426,7 @@ const Config: React.FC = () => { placeholder={t('configNodeP2PIceCredential')} className="md:col-span-2 px-2 py-1 rounded-lg bg-zinc-950/70 border border-zinc-800" /> - +
)) ) : ( diff --git a/webui/src/pages/Cron.tsx b/webui/src/pages/Cron.tsx index c4d3059..10a4701 100644 --- a/webui/src/pages/Cron.tsx +++ b/webui/src/pages/Cron.tsx @@ -172,10 +172,10 @@ const Cron: React.FC = () => {

{t('cronJobs')}

- -
@@ -215,20 +215,20 @@ const Cron: React.FC = () => {
@@ -368,13 +368,13 @@ const Cron: React.FC = () => {
diff --git a/webui/src/pages/Logs.tsx b/webui/src/pages/Logs.tsx index 97430d3..cd8f9c6 100644 --- a/webui/src/pages/Logs.tsx +++ b/webui/src/pages/Logs.tsx @@ -176,19 +176,19 @@ const Logs: React.FC = () => {
-
diff --git a/webui/src/pages/MCP.tsx b/webui/src/pages/MCP.tsx index ebccc99..05f9b1c 100644 --- a/webui/src/pages/MCP.tsx +++ b/webui/src/pages/MCP.tsx @@ -275,7 +275,7 @@ const MCP: React.FC = () => {
@@ -285,7 +285,7 @@ const MCP: React.FC = () => { @@ -296,7 +296,7 @@ const MCP: React.FC = () => {
{t('configMCPServers')}
setNewMCPServerName(e.target.value)} placeholder={t('configNewMCPServerName')} className="px-2 py-1 rounded-lg bg-zinc-900/70 border border-zinc-700 text-xs" /> - +
@@ -356,9 +356,9 @@ const MCP: React.FC = () => { )} updateMCPServerField(name, 'description', e.target.value)} placeholder={t('configLabels.description')} className="md:col-span-2 px-2 py-1 rounded-lg bg-zinc-950/70 border border-zinc-800" /> {isStdio && ( - + )} - + {(() => { const check = mcpServerChecks.find((item) => item.name === name); if (!check || check.status === 'ok' || check.status === 'disabled' || check.status === 'not_applicable') return null; @@ -371,7 +371,7 @@ const MCP: React.FC = () => { )}
{check.installable && ( - )} diff --git a/webui/src/pages/Memory.tsx b/webui/src/pages/Memory.tsx index 071a7b3..369a981 100644 --- a/webui/src/pages/Memory.tsx +++ b/webui/src/pages/Memory.tsx @@ -115,19 +115,19 @@ const Memory: React.FC = () => {

{active || t('noFileSelected')}

- +