mirror of
https://github.com/YspCoder/clawgo.git
synced 2026-05-05 23:17:31 +08:00
Update UI components from checkboxes to switches and introduce a recursive configuration component.
This commit is contained in:
@@ -15,7 +15,7 @@ import {
|
||||
} from '../components/channel/channelSchema';
|
||||
import WhatsAppQRCodePanel from '../components/channel/WhatsAppQRCodePanel';
|
||||
import WhatsAppStatusPanel from '../components/channel/WhatsAppStatusPanel';
|
||||
import { CheckboxField } from '../components/ui/FormControls';
|
||||
import { SwitchField } from '../components/ui/FormControls';
|
||||
import PageHeader from '../components/layout/PageHeader';
|
||||
import type { ChannelKey } from '../components/channel/channelSchema';
|
||||
import { cloneJSON } from '../utils/object';
|
||||
@@ -204,7 +204,7 @@ const ChannelSettings: React.FC = () => {
|
||||
const stateLabel = wa?.connected ? t('online') : wa?.logged_in ? t('whatsappStateDisconnected') : wa?.qr_available ? t('whatsappStateAwaitingScan') : t('offline');
|
||||
|
||||
return (
|
||||
<div className="space-y-6 px-5 py-5 md:px-7 md:py-6 xl:px-8">
|
||||
<div className="space-y-4 px-5 py-5 md:px-7 md:py-6 xl:px-8">
|
||||
<PageHeader
|
||||
title={t(definition.titleKey)}
|
||||
titleClassName="ui-text-primary text-3xl font-bold"
|
||||
@@ -224,18 +224,23 @@ const ChannelSettings: React.FC = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<div className={`grid gap-6 ${key === 'whatsapp' ? 'xl:grid-cols-[1fr_0.92fr]' : ''}`}>
|
||||
<div className="space-y-4">
|
||||
{definition.sections.map((section) => {
|
||||
<div className={`grid gap-4 ${key === 'whatsapp' ? 'xl:grid-cols-[1fr_0.92fr]' : ''}`}>
|
||||
<div className="brand-card ui-panel rounded-2xl p-5">
|
||||
{definition.sections.map((section, idx) => {
|
||||
const Icon = getChannelSectionIcon(section.id);
|
||||
return (
|
||||
<ChannelSectionCard
|
||||
key={section.id}
|
||||
icon={<Icon className="ui-icon-muted h-[18px] w-[18px]" />}
|
||||
title={t(section.titleKey)}
|
||||
hint={t(section.hintKey)}
|
||||
>
|
||||
<div className={`grid gap-4 ${section.columns === 1 ? 'grid-cols-1' : 'lg:grid-cols-2'}`}>
|
||||
<div key={section.id}>
|
||||
{idx > 0 && <hr className="ui-border-subtle my-4 border-t" />}
|
||||
<div className="ui-section-header mb-3">
|
||||
<div className="ui-subpanel flex h-11 w-11 shrink-0 items-center justify-center">
|
||||
<Icon className="ui-icon-muted h-[18px] w-[18px]" />
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="ui-text-primary text-lg font-semibold">{t(section.titleKey)}</div>
|
||||
<p className="ui-text-muted mt-0.5 text-sm">{t(section.hintKey)}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className={`grid gap-3 ${section.columns === 1 ? 'grid-cols-1' : 'lg:grid-cols-2'}`}>
|
||||
{section.fields.map((field) => (
|
||||
<ChannelFieldRenderer
|
||||
key={field.key}
|
||||
@@ -249,7 +254,7 @@ const ChannelSettings: React.FC = () => {
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
</ChannelSectionCard>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
|
||||
@@ -350,7 +350,7 @@ const Chat: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="flex h-full min-w-0 p-4 md:p-6 xl:p-8">
|
||||
<div className="flex-1 flex flex-col brand-card ui-panel rounded-[30px] overflow-hidden">
|
||||
<div className="flex-1 flex flex-col brand-card ui-panel rounded-2xl overflow-hidden">
|
||||
<div className="ui-surface-muted ui-border-subtle px-4 py-3 border-b flex items-center gap-2 min-w-0 overflow-x-auto">
|
||||
<div className="flex items-center gap-2 min-w-0 shrink-0">
|
||||
<Button onClick={() => setChatTab('main')} variant={chatTab === 'main' ? 'primary' : 'neutral'} size="xs">{t('mainChat')}</Button>
|
||||
|
||||
@@ -100,7 +100,7 @@ const Config: React.FC = () => {
|
||||
t={t}
|
||||
/>
|
||||
|
||||
<div className="flex-1 brand-card ui-border-subtle border rounded-[30px] overflow-hidden flex flex-col shadow-sm min-h-[420px]">
|
||||
<div className="flex-1 brand-card ui-border-subtle border rounded-2xl overflow-hidden flex flex-col shadow-sm min-h-[420px]">
|
||||
{!showRaw ? (
|
||||
<div className="flex-1 flex min-h-0">
|
||||
<ConfigSidebar
|
||||
|
||||
@@ -6,7 +6,7 @@ import { useAppContext } from '../context/AppContext';
|
||||
import { useUI } from '../context/UIContext';
|
||||
import { Button, FixedButton } from '../components/ui/Button';
|
||||
import EmptyState from '../components/data-display/EmptyState';
|
||||
import { CheckboxCardField, FieldBlock, SelectField, TextField, TextareaField } from '../components/ui/FormControls';
|
||||
import { SwitchCardField, FieldBlock, SelectField, TextField, TextareaField } from '../components/ui/FormControls';
|
||||
import { ModalBackdrop, ModalBody, ModalCard, ModalFooter, ModalHeader, ModalShell } from '../components/ui/ModalFrame';
|
||||
import PageHeader from '../components/layout/PageHeader';
|
||||
import { CronJob } from '../types';
|
||||
@@ -192,7 +192,7 @@ const Cron: React.FC = () => {
|
||||
{cron.map((j) => {
|
||||
const schedule = formatSchedule(j, t);
|
||||
return (
|
||||
<div key={j.id} className="brand-card rounded-[30px] border border-zinc-800/80 p-6 flex flex-col group hover:border-zinc-700/50 transition-colors">
|
||||
<div key={j.id} className="brand-card rounded-2xl border border-zinc-800/80 p-6 flex flex-col group hover:border-zinc-700/50 transition-colors">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div>
|
||||
<h3 className="font-semibold text-zinc-100 mb-1">{j.name || j.id}</h3>
|
||||
@@ -344,15 +344,15 @@ const Cron: React.FC = () => {
|
||||
</div>
|
||||
|
||||
<div className="grid grid-cols-1 gap-3 pt-2 md:grid-cols-2">
|
||||
<CheckboxCardField
|
||||
<SwitchCardField
|
||||
checked={cronForm.deliver}
|
||||
help={t('cronDeliverHint', { defaultValue: 'Send the message through the selected channel.' })}
|
||||
help={t('cronDeliverHint')}
|
||||
label={t('deliver')}
|
||||
onChange={(checked) => setCronForm({ ...cronForm, deliver: checked })}
|
||||
/>
|
||||
<CheckboxCardField
|
||||
<SwitchCardField
|
||||
checked={cronForm.enabled}
|
||||
help={t('cronEnabledHint', { defaultValue: 'Enable this cron job immediately after saving.' })}
|
||||
help={t('cronEnabledHint')}
|
||||
label={t('active')}
|
||||
onChange={(checked) => setCronForm({ ...cronForm, enabled: checked })}
|
||||
/>
|
||||
|
||||
@@ -62,7 +62,7 @@ const Dashboard: React.FC = () => {
|
||||
}, [nodeAlerts]);
|
||||
|
||||
return (
|
||||
<div className="p-4 md:p-6 xl:p-8 w-full space-y-6 xl:space-y-8">
|
||||
<div className="p-4 md:p-5 w-full space-y-4">
|
||||
<PageHeader
|
||||
title={t('dashboard')}
|
||||
subtitle={
|
||||
@@ -138,7 +138,7 @@ const Dashboard: React.FC = () => {
|
||||
)}
|
||||
</SectionPanel>
|
||||
|
||||
<div className="grid grid-cols-1 xl:grid-cols-2 gap-6 items-stretch">
|
||||
<div className="grid grid-cols-1 xl:grid-cols-2 gap-4 items-stretch">
|
||||
<SectionPanel title={t('taskAudit')} icon={<Activity className="w-5 h-5 text-zinc-400" />} className="min-h-[340px] h-full">
|
||||
<div className="space-y-3">
|
||||
{recentTasks.length === 0 ? (
|
||||
|
||||
@@ -56,7 +56,7 @@ const LogCodes: React.FC = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="brand-card ui-border-subtle border rounded-[30px] overflow-hidden">
|
||||
<div className="brand-card ui-border-subtle border rounded-2xl overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="ui-soft-panel ui-border-subtle border-b">
|
||||
<tr className="ui-text-secondary">
|
||||
|
||||
@@ -95,18 +95,20 @@ const Logs: React.FC = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="p-4 md:p-6 xl:p-8 w-full space-y-4 h-full flex flex-col">
|
||||
<div className="p-3 md:p-4 w-full space-y-3 h-full flex flex-col min-h-0">
|
||||
<PageHeader
|
||||
title={t('logs')}
|
||||
titleClassName="ui-text-primary"
|
||||
subtitle={
|
||||
<div className={`ui-pill flex items-center gap-1.5 px-2.5 py-0.5 rounded-md text-[10px] font-bold uppercase tracking-wider border ${
|
||||
isStreaming ? 'ui-pill-success' : 'ui-pill-neutral'
|
||||
}`}>
|
||||
<div className={`w-1.5 h-1.5 rounded-full ${isStreaming ? 'ui-dot-live animate-pulse' : 'ui-dot-neutral'}`} />
|
||||
{isStreaming ? t('live') : t('paused')}
|
||||
title={
|
||||
<div className="flex items-center gap-3">
|
||||
<span>{t('logs')}</span>
|
||||
<div className={`ui-pill flex items-center gap-1.5 px-2.5 py-0.5 rounded-md text-[10px] font-bold uppercase tracking-wider border ${
|
||||
isStreaming ? 'ui-pill-success' : 'ui-pill-neutral'
|
||||
}`}>
|
||||
<div className={`w-1.5 h-1.5 rounded-full ${isStreaming ? 'ui-dot-live animate-pulse' : 'ui-dot-neutral'}`} />
|
||||
{isStreaming ? t('live') : t('paused')}
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
titleClassName="ui-text-primary flex items-center gap-3"
|
||||
actions={
|
||||
<ToolbarRow>
|
||||
<Button onClick={() => setShowRaw(!showRaw)} gap="2">
|
||||
@@ -122,7 +124,7 @@ const Logs: React.FC = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="flex-1 brand-card ui-border-subtle border rounded-[30px] overflow-hidden flex flex-col shadow-2xl">
|
||||
<div className="flex-1 brand-card ui-border-subtle border rounded-2xl overflow-hidden flex flex-col shadow-2xl">
|
||||
<div className="ui-soft-panel ui-border-subtle px-4 py-2 border-b flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Terminal className="ui-icon-muted w-4 h-4" />
|
||||
|
||||
@@ -116,7 +116,7 @@ const Memory: React.FC = () => {
|
||||
|
||||
return (
|
||||
<div className="h-full p-4 md:p-5 xl:p-6">
|
||||
<div className="flex h-full flex-col overflow-hidden rounded-[30px] border brand-card ui-border-subtle lg:flex-row">
|
||||
<div className="flex h-full flex-col overflow-hidden rounded-2xl border brand-card ui-border-subtle lg:flex-row">
|
||||
<aside className="ui-border-subtle w-full overflow-y-auto border-b p-2 md:p-3 lg:w-72 lg:border-r lg:border-b-0">
|
||||
<div className="sidebar-section rounded-[24px] p-2 md:p-2.5 space-y-1">
|
||||
<div className="flex items-center justify-between">
|
||||
|
||||
@@ -206,7 +206,7 @@ const Providers: React.FC = () => {
|
||||
}
|
||||
/>
|
||||
|
||||
<div className="brand-card ui-border-subtle border rounded-[30px] p-4 md:p-6 space-y-4">
|
||||
<div className="brand-card ui-border-subtle border rounded-2xl p-4 md:p-6 space-y-4">
|
||||
<ProviderRuntimeToolbar
|
||||
newProxyName={newProxyName}
|
||||
onAddProxy={() => addProxy(newProxyName)}
|
||||
|
||||
@@ -5,7 +5,7 @@ import { useTranslation } from 'react-i18next';
|
||||
import { useAppContext } from '../context/AppContext';
|
||||
import { useUI } from '../context/UIContext';
|
||||
import { Button, FixedButton } from '../components/ui/Button';
|
||||
import { TextField, TextareaField, ToolbarCheckboxField } from '../components/ui/FormControls';
|
||||
import { TextField, TextareaField, ToolbarSwitchField } from '../components/ui/FormControls';
|
||||
import { ModalBackdrop, ModalBody, ModalCard, ModalHeader, ModalShell } from '../components/ui/ModalFrame';
|
||||
import PageHeader from '../components/layout/PageHeader';
|
||||
import ToolbarRow from '../components/layout/ToolbarRow';
|
||||
@@ -231,7 +231,7 @@ const Skills: React.FC = () => {
|
||||
>
|
||||
{installingSkill ? t('loading') : t('install')}
|
||||
</Button>
|
||||
<ToolbarCheckboxField
|
||||
<ToolbarSwitchField
|
||||
checked={ignoreSuspicious}
|
||||
className={installingSkill ? 'pointer-events-none opacity-60' : 'shrink-0'}
|
||||
help={t('skillsIgnoreSuspiciousHint', { defaultValue: 'Use --force to ignore suspicious package warnings.' })}
|
||||
@@ -256,7 +256,7 @@ const Skills: React.FC = () => {
|
||||
|
||||
<div className="grid grid-cols-1 sm:grid-cols-2 xl:grid-cols-3 2xl:grid-cols-4 gap-6">
|
||||
{skills.map(s => (
|
||||
<div key={s.id} className="brand-card rounded-[28px] border border-zinc-800/80 p-6 flex flex-col shadow-sm group hover:border-zinc-700/50 transition-colors">
|
||||
<div key={s.id} className="brand-card rounded-2xl border border-zinc-800/80 p-6 flex flex-col shadow-sm group hover:border-zinc-700/50 transition-colors">
|
||||
<div className="flex items-start justify-between mb-4">
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="w-12 h-12 rounded-2xl bg-zinc-800/50 flex items-center justify-center border border-zinc-700/50">
|
||||
|
||||
@@ -238,7 +238,7 @@ const SubagentProfiles: React.FC = () => {
|
||||
|
||||
<div className="flex-1 min-h-0 grid grid-cols-1 lg:grid-cols-[360px_1fr] gap-4">
|
||||
<ProfileListPanel
|
||||
emptyLabel="No subagent profiles."
|
||||
emptyLabel={t('profileEmptyLabel')}
|
||||
items={items}
|
||||
onSelect={onSelect}
|
||||
selectedId={selectedId}
|
||||
@@ -263,15 +263,15 @@ const SubagentProfiles: React.FC = () => {
|
||||
promptPlaceholder={t('agentPromptContentPlaceholder')}
|
||||
promptPathHint={promptPathHint}
|
||||
promptPathInvalid={!!promptPathErrorKey}
|
||||
roleLabel="Role"
|
||||
roleLabel={t('profileRoleLabel')}
|
||||
saving={saving}
|
||||
statusLabel={t('status')}
|
||||
toolAllowlistHint={<><span className="ui-text-subtle font-mono">skill_exec</span> is inherited automatically and does not need to be listed here.</>}
|
||||
toolAllowlistHint={<><span className="ui-text-subtle font-mono">skill_exec</span> {t('profileToolAllowlistInheritHint')}</>}
|
||||
toolAllowlistLabel={t('toolAllowlist')}
|
||||
maxRetriesLabel={t('maxRetries')}
|
||||
retryBackoffLabel={t('retryBackoffMs')}
|
||||
maxTaskCharsLabel="Max Task Chars"
|
||||
maxResultCharsLabel="Max Result Chars"
|
||||
maxTaskCharsLabel={t('profileMaxTaskCharsLabel')}
|
||||
maxResultCharsLabel={t('profileMaxResultCharsLabel')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -111,15 +111,15 @@ const TaskAudit: React.FC = () => {
|
||||
title={t('taskAudit')}
|
||||
titleClassName="text-xl md:text-2xl font-semibold"
|
||||
actions={(
|
||||
<ToolbarRow className="flex-nowrap">
|
||||
<SelectField dense className="w-[152px] shrink-0" value={sourceFilter} onChange={(e) => setSourceFilter(e.target.value)}>
|
||||
<ToolbarRow className="flex flex-wrap sm:flex-nowrap justify-end items-center gap-2">
|
||||
<SelectField dense className="w-full sm:w-[152px] shrink-0" value={sourceFilter} onChange={(e) => setSourceFilter(e.target.value)}>
|
||||
<option value="all">{t('allSources')}</option>
|
||||
<option value="direct">{t('sourceDirect')}</option>
|
||||
<option value="memory_todo">{t('sourceMemoryTodo')}</option>
|
||||
<option value="task_watchdog">task_watchdog</option>
|
||||
<option value="-">-</option>
|
||||
</SelectField>
|
||||
<SelectField dense className="w-[152px] shrink-0" value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>
|
||||
<SelectField dense className="w-full sm:w-[152px] shrink-0" value={statusFilter} onChange={(e) => setStatusFilter(e.target.value)}>
|
||||
<option value="all">{t('allStatus')}</option>
|
||||
<option value="running">{t('statusRunning')}</option>
|
||||
<option value="waiting">{t('statusWaiting')}</option>
|
||||
|
||||
Reference in New Issue
Block a user