refine whatsapp bridge defaults and webui polish

This commit is contained in:
lpf
2026-03-10 12:07:56 +08:00
parent 8a6a2755de
commit c7b159d2ed
29 changed files with 1389 additions and 432 deletions

View File

@@ -1,5 +1,5 @@
import React from 'react';
import { Terminal, Globe, Github, Menu, Moon, RefreshCw, SunMedium } from 'lucide-react';
import { Github, Moon, RefreshCw, SunMedium, Terminal } from 'lucide-react';
import { useTranslation } from 'react-i18next';
import { useAppContext } from '../context/AppContext';
import { useUI } from '../context/UIContext';
@@ -12,7 +12,7 @@ function normalizeVersion(value: string) {
const Header: React.FC = () => {
const { t, i18n } = useTranslation();
const { isGatewayOnline, setSidebarOpen, sidebarCollapsed, gatewayVersion, webuiVersion } = useAppContext();
const { isGatewayOnline, sidebarCollapsed, gatewayVersion, webuiVersion } = useAppContext();
const { theme, toggleTheme, notify } = useUI();
const [checkingVersion, setCheckingVersion] = React.useState(false);
@@ -56,28 +56,23 @@ const Header: React.FC = () => {
};
return (
<header className="app-header h-14 md:h-16 border-b border-zinc-800 flex items-center justify-between px-3 md:px-6 shrink-0 z-10">
<header className="app-header ui-border-subtle h-14 md:h-16 border-b flex items-center justify-between px-3 md:px-6 shrink-0 z-10">
<div className="flex items-center gap-2 md:gap-3 min-w-0">
<button className="md:hidden p-2 rounded-lg hover:bg-zinc-800 text-zinc-300" onClick={() => setSidebarOpen(true)}>
<Menu className="w-5 h-5" />
</button>
<div className="hidden md:flex items-center gap-3 rounded-xl px-2 py-1.5 min-w-0">
<div className="brand-badge w-9 h-9 rounded-xl flex items-center justify-center shadow-lg shrink-0">
<Terminal className="w-5 h-5 text-zinc-950" />
</div>
{!sidebarCollapsed && (
<span className="font-semibold text-lg md:text-xl tracking-tight truncate">{t('appName')}</span>
)}
<div className="brand-badge hidden md:flex h-9 w-9 items-center justify-center rounded-xl shadow-lg shrink-0">
<Terminal className="h-5 w-5 text-white" />
</div>
<div className="brand-badge md:hidden w-8 h-8 rounded-xl flex items-center justify-center shadow-lg shrink-0">
<Terminal className="w-4 h-4 text-zinc-950" />
<div className="brand-badge flex h-8 w-8 items-center justify-center rounded-xl shadow-lg shrink-0 md:hidden">
<Terminal className="h-4 w-4 text-white" />
</div>
{!sidebarCollapsed && (
<span className="hidden md:inline font-semibold text-lg md:text-xl tracking-tight truncate">{t('appName')}</span>
)}
<span className="md:hidden font-semibold text-lg tracking-tight truncate">{t('appName')}</span>
</div>
<div className="flex items-center gap-2 md:gap-6">
<div className="flex items-center gap-1.5 md:gap-2.5 bg-zinc-900 border border-zinc-800 px-2 md:px-3 py-1 rounded-lg max-w-[140px] md:max-w-none overflow-hidden">
<span className="hidden md:inline text-sm font-medium text-zinc-400">{t('gatewayStatus')}:</span>
<div className="ui-toolbar-chip flex items-center gap-1.5 md:gap-2.5 px-2 md:px-3 py-1 rounded-lg max-w-[140px] md:max-w-none overflow-hidden">
<span className="ui-text-muted hidden md:inline text-sm font-medium">{t('gatewayStatus')}:</span>
{isGatewayOnline ? (
<div className="status-pill-online flex items-center gap-1.5 px-2.5 py-0.5 rounded-md text-xs font-semibold border">
<div className="status-dot-online w-1.5 h-1.5 rounded-full" />
@@ -91,13 +86,13 @@ const Header: React.FC = () => {
)}
</div>
<div className="hidden md:block h-5 w-px bg-zinc-800" />
<div className="ui-border-subtle hidden md:block h-5 w-px bg-transparent border-l" />
<a
href={REPO_URL}
target="_blank"
rel="noreferrer"
className="inline-flex h-9 w-9 items-center justify-center text-sm font-medium text-zinc-400 hover:text-zinc-200 transition-colors bg-zinc-900 hover:bg-zinc-800 border border-zinc-800 rounded-lg"
className="ui-button ui-button-neutral ui-button-icon text-sm font-medium"
title={t('githubRepo')}
>
<Github className="w-4 h-4" />
@@ -106,7 +101,7 @@ const Header: React.FC = () => {
<button
onClick={checkVersion}
disabled={checkingVersion}
className="inline-flex h-9 w-9 items-center justify-center text-sm font-medium text-zinc-400 hover:text-zinc-200 transition-colors bg-zinc-900 hover:bg-zinc-800 border border-zinc-800 rounded-lg disabled:opacity-60"
className="ui-button ui-button-neutral ui-button-icon text-sm font-medium disabled:opacity-60"
title={t('checkVersion')}
>
<RefreshCw className={`w-4 h-4 ${checkingVersion ? 'animate-spin' : ''}`} />
@@ -114,18 +109,19 @@ const Header: React.FC = () => {
<button
onClick={toggleTheme}
className="inline-flex h-9 w-9 items-center justify-center text-sm font-medium text-zinc-400 hover:text-zinc-200 transition-colors bg-zinc-900 hover:bg-zinc-800 border border-zinc-800 rounded-lg"
className="ui-button ui-button-neutral ui-button-icon text-sm font-medium"
title={theme === 'dark' ? t('themeLight') : t('themeDark')}
>
{theme === 'dark' ? <SunMedium className="w-4 h-4" /> : <Moon className="w-4 h-4" />}
</button>
<button
<button
onClick={toggleLang}
className="flex items-center gap-2 text-sm font-medium text-zinc-400 hover:text-zinc-200 transition-colors bg-zinc-900 hover:bg-zinc-800 border border-zinc-800 px-3 py-1.5 rounded-lg"
className="ui-button ui-button-neutral ui-button-square text-sm font-semibold"
title={i18n.language === 'en' ? t('languageZh') : t('languageEn')}
aria-label={i18n.language === 'en' ? t('languageZh') : t('languageEn')}
>
<Globe className="w-4 h-4" />
{i18n.language === 'en' ? t('languageZh') : t('languageEn')}
{i18n.language === 'en' ? '中' : 'EN'}
</button>
</div>
</header>

View File

@@ -1,4 +1,5 @@
import React, { useMemo, useState } from 'react';
import { Plus } from 'lucide-react';
import { useTranslation } from 'react-i18next';
interface RecursiveConfigProps {
@@ -55,11 +56,11 @@ const PrimitiveArrayEditor: React.FC<{
return (
<div className="space-y-2">
<div className="flex flex-wrap gap-2">
{value.length === 0 && <span className="text-xs text-zinc-500 italic">{t('empty')}</span>}
{value.length === 0 && <span className="ui-text-muted text-xs italic">{t('empty')}</span>}
{value.map((item, idx) => (
<span key={`${item}-${idx}`} className="inline-flex items-center gap-1 px-2 py-1 rounded-xl ui-soft-panel text-xs font-mono text-zinc-700 dark:text-zinc-200">
<span key={`${item}-${idx}`} className="ui-text-secondary inline-flex items-center gap-1 px-2 py-1 rounded-xl ui-soft-panel text-xs font-mono">
{String(item)}
<button onClick={() => removeAt(idx)} className="ui-text-danger-hover text-zinc-400">×</button>
<button onClick={() => removeAt(idx)} className="ui-text-danger-hover ui-text-subtle">×</button>
</span>
))}
</div>
@@ -83,9 +84,11 @@ const PrimitiveArrayEditor: React.FC<{
addValue(draft);
setDraft('');
}}
className="ui-button ui-button-neutral px-3 py-2 text-xs rounded-xl"
className="ui-button ui-button-neutral ui-button-icon rounded-xl"
title={t('add')}
aria-label={t('add')}
>
{t('add')}
<Plus className="w-4 h-4" />
</button>
<select
@@ -126,8 +129,8 @@ const RecursiveConfig: React.FC<RecursiveConfigProps> = ({ data, labels, path =
return (
<div key={currentPath} className="space-y-2 col-span-full">
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-zinc-300 block capitalize">{label}</span>
<span className="text-[10px] text-zinc-600 font-mono">{currentPath}</span>
<span className="ui-text-secondary text-sm font-medium block capitalize">{label}</span>
<span className="ui-text-subtle text-[10px] font-mono">{currentPath}</span>
</div>
<div className="ui-soft-panel p-3">
{allPrimitive ? (
@@ -158,11 +161,11 @@ const RecursiveConfig: React.FC<RecursiveConfigProps> = ({ data, labels, path =
if (typeof value === 'object' && value !== null && !Array.isArray(value)) {
return (
<div key={currentPath} className="space-y-6 col-span-full">
<h3 className="text-sm font-semibold text-zinc-400 uppercase tracking-wider flex items-center gap-2">
<span className="w-1.5 h-4 bg-indigo-500 rounded-full" />
<h3 className="ui-text-subtle text-sm font-semibold uppercase tracking-wider flex items-center gap-2">
<span className="ui-icon-info w-1.5 h-4 rounded-full" />
{label}
</h3>
<div className="pl-6 border-l border-zinc-800/50 dark:border-zinc-700/50">
<div className="ui-border-subtle pl-6 border-l">
<RecursiveConfig data={value} labels={labels} path={currentPath} onChange={onChange} hotPaths={hotPaths} onlyHot={onlyHot} />
</div>
</div>
@@ -172,8 +175,8 @@ const RecursiveConfig: React.FC<RecursiveConfigProps> = ({ data, labels, path =
return (
<div key={currentPath} className="space-y-2">
<div className="flex items-center justify-between">
<span className="text-sm font-medium text-zinc-300 block capitalize">{label}</span>
<span className="text-[10px] text-zinc-600 font-mono">{currentPath}</span>
<span className="ui-text-secondary text-sm font-medium block capitalize">{label}</span>
<span className="ui-text-subtle text-[10px] font-mono">{currentPath}</span>
</div>
{typeof value === 'boolean' ? (
<label className="ui-toggle-card flex items-center gap-3 p-3 cursor-pointer transition-colors group">
@@ -181,9 +184,9 @@ const RecursiveConfig: React.FC<RecursiveConfigProps> = ({ data, labels, path =
type="checkbox"
checked={value}
onChange={(e) => onChange(currentPath, e.target.checked)}
className="w-4 h-4 rounded border-zinc-700 text-indigo-500 focus:ring-indigo-500 focus:ring-offset-zinc-950 bg-zinc-900"
className="w-4 h-4 rounded border-zinc-700 text-indigo-500 focus:ring-indigo-500"
/>
<span className="text-sm text-zinc-400 group-hover:text-zinc-300 transition-colors">
<span className="ui-text-subtle group-hover:ui-text-secondary text-sm transition-colors">
{value ? (labels['enabled_true'] || t('enabled_true')) : (labels['enabled_false'] || t('enabled_false'))}
</span>
</label>