feat(ux): epic UX design improvements across agents, chat, notes, and i18n
Comprehensive UI/UX updates including agent card redesign, chat container improvements, note editor enhancements, memory echo notifications, and updated translations for all 15 locales. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -52,10 +52,10 @@ interface AgentCardProps {
|
||||
// --- Config ---
|
||||
|
||||
const typeConfig: Record<string, { icon: typeof Globe; color: string; bgColor: string }> = {
|
||||
scraper: { icon: Globe, color: 'text-blue-600', bgColor: 'bg-blue-50 border-blue-200' },
|
||||
researcher: { icon: Search, color: 'text-purple-600', bgColor: 'bg-purple-50 border-purple-200' },
|
||||
monitor: { icon: Eye, color: 'text-amber-600', bgColor: 'bg-amber-50 border-amber-200' },
|
||||
custom: { icon: Settings, color: 'text-green-600', bgColor: 'bg-green-50 border-green-200' },
|
||||
scraper: { icon: Globe, color: 'text-blue-600 dark:text-blue-400', bgColor: 'bg-blue-50 dark:bg-blue-950 border-blue-200 dark:border-blue-800' },
|
||||
researcher: { icon: Search, color: 'text-purple-600 dark:text-purple-400', bgColor: 'bg-purple-50 dark:bg-purple-950 border-purple-200 dark:border-purple-800' },
|
||||
monitor: { icon: Eye, color: 'text-amber-600 dark:text-amber-400', bgColor: 'bg-amber-50 dark:bg-amber-950 border-amber-200 dark:border-amber-800' },
|
||||
custom: { icon: Settings, color: 'text-green-600 dark:text-green-400', bgColor: 'bg-green-50 dark:bg-green-950 border-green-200 dark:border-green-800' },
|
||||
}
|
||||
|
||||
const frequencyKeys: Record<string, string> = {
|
||||
@@ -142,10 +142,10 @@ export function AgentCard({ agent, onEdit, onRefresh, onToggle }: AgentCardProps
|
||||
|
||||
return (
|
||||
<div className={`
|
||||
bg-white rounded-xl border-2 transition-all overflow-hidden
|
||||
${agent.isEnabled ? 'border-slate-200 hover:border-primary/30 hover:shadow-md' : 'border-slate-100 opacity-60'}
|
||||
bg-card rounded-xl border-2 transition-all overflow-hidden
|
||||
${agent.isEnabled ? 'border-border hover:border-primary/30 hover:shadow-md' : 'border-border/50 opacity-60'}
|
||||
`}>
|
||||
<div className={`h-1 ${agent.isEnabled ? 'bg-primary' : 'bg-slate-200'}`} />
|
||||
<div className={`h-1 ${agent.isEnabled ? 'bg-primary' : 'bg-muted'}`} />
|
||||
|
||||
<div className="p-4">
|
||||
<div className="flex items-start justify-between mb-3">
|
||||
@@ -155,9 +155,9 @@ export function AgentCard({ agent, onEdit, onRefresh, onToggle }: AgentCardProps
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<div className="flex items-center gap-2">
|
||||
<h3 className="font-semibold text-slate-800 truncate">{agent.name}</h3>
|
||||
<h3 className="font-semibold text-card-foreground truncate">{agent.name}</h3>
|
||||
{mounted && isNew && (
|
||||
<span className="flex-shrink-0 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wider bg-emerald-100 text-emerald-700 rounded">
|
||||
<span className="flex-shrink-0 px-1.5 py-0.5 text-[10px] font-bold uppercase tracking-wider bg-emerald-100 dark:bg-emerald-900 text-emerald-700 dark:text-emerald-300 rounded">
|
||||
{t('agents.newBadge')}
|
||||
</span>
|
||||
)}
|
||||
@@ -171,16 +171,16 @@ export function AgentCard({ agent, onEdit, onRefresh, onToggle }: AgentCardProps
|
||||
{agent.isEnabled ? (
|
||||
<ToggleRight className="w-6 h-6 text-primary" />
|
||||
) : (
|
||||
<ToggleLeft className="w-6 h-6 text-slate-300" />
|
||||
<ToggleLeft className="w-6 h-6 text-muted-foreground/40" />
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{agent.description && (
|
||||
<p className="text-xs text-slate-500 mb-3 line-clamp-2">{agent.description}</p>
|
||||
<p className="text-xs text-muted-foreground mb-3 line-clamp-2">{agent.description}</p>
|
||||
)}
|
||||
|
||||
<div className="flex items-center gap-3 text-xs text-slate-400 mb-3">
|
||||
<div className="flex items-center gap-3 text-xs text-muted-foreground mb-3">
|
||||
<span className="flex items-center gap-1">
|
||||
<Clock className="w-3 h-3" />
|
||||
{t(frequencyKeys[agent.frequency] || 'agents.frequencies.manual')}
|
||||
@@ -199,9 +199,9 @@ export function AgentCard({ agent, onEdit, onRefresh, onToggle }: AgentCardProps
|
||||
{lastAction && (
|
||||
<div className={`
|
||||
flex items-center gap-1.5 text-xs px-2 py-1 rounded-md mb-3
|
||||
${lastAction.status === 'success' ? 'bg-green-50 text-green-600' : ''}
|
||||
${lastAction.status === 'failure' ? 'bg-red-50 text-red-600' : ''}
|
||||
${lastAction.status === 'running' ? 'bg-blue-50 text-blue-600' : ''}
|
||||
${lastAction.status === 'success' ? 'bg-green-50 dark:bg-green-950 text-green-600 dark:text-green-400' : ''}
|
||||
${lastAction.status === 'failure' ? 'bg-red-50 dark:bg-red-950 text-red-600 dark:text-red-400' : ''}
|
||||
${lastAction.status === 'running' ? 'bg-blue-50 dark:bg-blue-950 text-blue-600 dark:text-blue-400' : ''}
|
||||
`}>
|
||||
{lastAction.status === 'success' && <CheckCircle2 className="w-3 h-3" />}
|
||||
{lastAction.status === 'failure' && <XCircle className="w-3 h-3" />}
|
||||
@@ -224,7 +224,7 @@ export function AgentCard({ agent, onEdit, onRefresh, onToggle }: AgentCardProps
|
||||
<button
|
||||
onClick={handleRun}
|
||||
disabled={isRunning || !agent.isEnabled}
|
||||
className="p-1.5 text-green-600 bg-green-50 rounded-lg hover:bg-green-100 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
className="p-1.5 text-green-600 dark:text-green-400 bg-green-50 dark:bg-green-950 rounded-lg hover:bg-green-100 dark:hover:bg-green-900 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||
title={t('agents.actions.run')}
|
||||
>
|
||||
{isRunning ? <Loader2 className="w-4 h-4 animate-spin" /> : <Play className="w-4 h-4" />}
|
||||
@@ -232,7 +232,7 @@ export function AgentCard({ agent, onEdit, onRefresh, onToggle }: AgentCardProps
|
||||
<button
|
||||
onClick={handleDelete}
|
||||
disabled={isDeleting}
|
||||
className="p-1.5 text-red-500 bg-red-50 rounded-lg hover:bg-red-100 transition-colors disabled:opacity-50"
|
||||
className="p-1.5 text-red-500 dark:text-red-400 bg-red-50 dark:bg-red-950 rounded-lg hover:bg-red-100 dark:hover:bg-red-900 transition-colors disabled:opacity-50"
|
||||
title={t('agents.actions.delete')}
|
||||
>
|
||||
{isDeleting ? <Loader2 className="w-4 h-4 animate-spin" /> : <Trash2 className="w-4 h-4" />}
|
||||
|
||||
@@ -21,7 +21,7 @@ function FieldHelp({ tooltip }: { tooltip: string }) {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<button type="button" className="inline-flex items-center ml-1 text-slate-300 hover:text-slate-500 transition-colors">
|
||||
<button type="button" className="inline-flex items-center ml-1 text-muted-foreground/40 hover:text-muted-foreground transition-colors">
|
||||
<HelpCircle className="w-3.5 h-3.5" />
|
||||
</button>
|
||||
</TooltipTrigger>
|
||||
@@ -61,6 +61,16 @@ const TOOL_PRESETS: Record<string, string[]> = {
|
||||
custom: ['memory_search'],
|
||||
}
|
||||
|
||||
// --- Shared class strings ---
|
||||
const labelCls = 'block text-sm font-medium text-foreground mb-1.5'
|
||||
const labelCls2 = 'block text-sm font-medium text-foreground mb-2'
|
||||
const inputCls = 'w-full px-3 py-2 text-sm border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary bg-card text-foreground'
|
||||
const selectCls = 'w-full px-3 py-2 text-sm border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary bg-card text-foreground'
|
||||
const toggleOffBorder = 'border-border hover:border-primary/30'
|
||||
const toggleOffIcon = 'text-muted-foreground'
|
||||
const toggleOffLabel = 'text-sm font-medium text-foreground'
|
||||
const toggleOffHint = 'text-xs text-muted-foreground'
|
||||
|
||||
// --- Component ---
|
||||
|
||||
export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps) {
|
||||
@@ -187,26 +197,26 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-2xl shadow-xl max-w-lg w-full max-h-[90vh] overflow-y-auto">
|
||||
<div className="bg-card rounded-2xl shadow-xl max-w-lg w-full max-h-[90vh] overflow-y-auto">
|
||||
{/* Header — editable agent name */}
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-slate-100">
|
||||
<div className="flex items-center justify-between px-6 py-4 border-b border-border">
|
||||
<input
|
||||
type="text"
|
||||
value={name}
|
||||
onChange={e => setName(e.target.value)}
|
||||
className="text-lg font-semibold text-slate-800 bg-transparent border-none outline-none focus:ring-0 p-0 flex-1 placeholder:text-slate-300"
|
||||
className="text-lg font-semibold text-card-foreground bg-transparent border-none outline-none focus:ring-0 p-0 flex-1 placeholder:text-muted-foreground/40"
|
||||
placeholder={t('agents.form.namePlaceholder')}
|
||||
required
|
||||
/>
|
||||
<button onClick={onCancel} className="p-1 rounded-md hover:bg-slate-100 ml-3">
|
||||
<X className="w-5 h-5 text-slate-400" />
|
||||
<button onClick={onCancel} className="p-1 rounded-md hover:bg-accent ml-3">
|
||||
<X className="w-5 h-5 text-muted-foreground" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<form onSubmit={handleSubmit} className="p-6 space-y-5">
|
||||
{/* Agent Type */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('agents.form.agentType')}<FieldHelp tooltip={t('agents.help.tooltips.agentType')} /></label>
|
||||
<label className={labelCls2}>{t('agents.form.agentType')}<FieldHelp tooltip={t('agents.help.tooltips.agentType')} /></label>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{agentTypes.map(at => (
|
||||
<button
|
||||
@@ -217,11 +227,11 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
text-left px-3 py-2.5 rounded-lg border-2 transition-all text-sm
|
||||
${type === at.value
|
||||
? 'border-primary bg-primary/5 text-primary font-medium'
|
||||
: 'border-slate-200 text-slate-600 hover:border-slate-300'}
|
||||
: `${toggleOffBorder} text-muted-foreground`}
|
||||
`}
|
||||
>
|
||||
<div className="font-medium">{t(at.labelKey)}</div>
|
||||
<div className="text-xs text-slate-400 mt-0.5">{t(at.descKey)}</div>
|
||||
<div className="text-xs text-muted-foreground mt-0.5">{t(at.descKey)}</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
@@ -230,12 +240,12 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
{/* Research Topic (researcher only) — replaces Description for this type */}
|
||||
{type === 'researcher' && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1.5">{t('agents.form.researchTopic')}<FieldHelp tooltip={t('agents.help.tooltips.researchTopic')} /></label>
|
||||
<label className={labelCls}>{t('agents.form.researchTopic')}<FieldHelp tooltip={t('agents.help.tooltips.researchTopic')} /></label>
|
||||
<input
|
||||
type="text"
|
||||
value={description}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary"
|
||||
className={inputCls}
|
||||
placeholder={t('agents.form.researchTopicPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
@@ -244,12 +254,12 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
{/* Description (for non-researcher types) */}
|
||||
{type !== 'researcher' && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1.5">{t('agents.form.description')}<FieldHelp tooltip={t('agents.help.tooltips.description')} /></label>
|
||||
<label className={labelCls}>{t('agents.form.description')}<FieldHelp tooltip={t('agents.help.tooltips.description')} /></label>
|
||||
<input
|
||||
type="text"
|
||||
value={description}
|
||||
onChange={e => setDescription(e.target.value)}
|
||||
className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary"
|
||||
className={inputCls}
|
||||
placeholder={t('agents.form.descriptionPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
@@ -258,7 +268,7 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
{/* URLs (scraper and custom only — researcher uses search, not URLs) */}
|
||||
{(type === 'scraper' || type === 'custom') && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1.5">
|
||||
<label className={labelCls}>
|
||||
{t('agents.form.urlsLabel')}<FieldHelp tooltip={t('agents.help.tooltips.urls')} />
|
||||
</label>
|
||||
<div className="space-y-2">
|
||||
@@ -268,14 +278,14 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
type="url"
|
||||
value={url}
|
||||
onChange={e => updateUrl(i, e.target.value)}
|
||||
className="flex-1 px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary"
|
||||
className={inputCls}
|
||||
placeholder="https://example.com"
|
||||
/>
|
||||
{urls.length > 1 && (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => removeUrl(i)}
|
||||
className="p-2 text-red-400 hover:text-red-600 hover:bg-red-50 rounded-lg transition-colors"
|
||||
className="p-2 text-red-400 hover:text-red-600 hover:bg-red-50 dark:hover:bg-red-950 rounded-lg transition-colors"
|
||||
>
|
||||
<Trash2 className="w-4 h-4" />
|
||||
</button>
|
||||
@@ -297,11 +307,11 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
{/* Source Notebook (monitor only) */}
|
||||
{showSourceNotebook && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1.5">{t('agents.form.sourceNotebook')}<FieldHelp tooltip={t('agents.help.tooltips.sourceNotebook')} /></label>
|
||||
<label className={labelCls}>{t('agents.form.sourceNotebook')}<FieldHelp tooltip={t('agents.help.tooltips.sourceNotebook')} /></label>
|
||||
<select
|
||||
value={sourceNotebookId}
|
||||
onChange={e => setSourceNotebookId(e.target.value)}
|
||||
className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary bg-white"
|
||||
className={selectCls}
|
||||
>
|
||||
<option value="">{t('agents.form.selectNotebook')}</option>
|
||||
{notebooks.map(nb => (
|
||||
@@ -315,11 +325,11 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
|
||||
{/* Target Notebook */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1.5">{t('agents.form.targetNotebook')}<FieldHelp tooltip={t('agents.help.tooltips.targetNotebook')} /></label>
|
||||
<label className={labelCls}>{t('agents.form.targetNotebook')}<FieldHelp tooltip={t('agents.help.tooltips.targetNotebook')} /></label>
|
||||
<select
|
||||
value={targetNotebookId}
|
||||
onChange={e => setTargetNotebookId(e.target.value)}
|
||||
className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary bg-white"
|
||||
className={selectCls}
|
||||
>
|
||||
<option value="">{t('agents.form.inbox')}</option>
|
||||
{notebooks.map(nb => (
|
||||
@@ -332,11 +342,11 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
|
||||
{/* Frequency */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1.5">{t('agents.form.frequency')}<FieldHelp tooltip={t('agents.help.tooltips.frequency')} /></label>
|
||||
<label className={labelCls}>{t('agents.form.frequency')}<FieldHelp tooltip={t('agents.help.tooltips.frequency')} /></label>
|
||||
<select
|
||||
value={frequency}
|
||||
onChange={e => setFrequency(e.target.value)}
|
||||
className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary bg-white"
|
||||
className={selectCls}
|
||||
>
|
||||
<option value="manual">{t('agents.frequencies.manual')}</option>
|
||||
<option value="hourly">{t('agents.frequencies.hourly')}</option>
|
||||
@@ -352,16 +362,16 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
className={`flex items-center gap-3 p-3 rounded-lg border-2 cursor-pointer transition-all ${
|
||||
notifyEmail
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-slate-200 hover:border-slate-300'
|
||||
: toggleOffBorder
|
||||
}`}
|
||||
>
|
||||
<Mail className={`w-4 h-4 flex-shrink-0 ${notifyEmail ? 'text-primary' : 'text-slate-400'}`} />
|
||||
<Mail className={`w-4 h-4 flex-shrink-0 ${notifyEmail ? 'text-primary' : toggleOffIcon}`} />
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-sm font-medium text-slate-700">{t('agents.form.notifyEmail')}</div>
|
||||
<div className="text-xs text-slate-400">{t('agents.form.notifyEmailHint')}</div>
|
||||
<div className={toggleOffLabel}>{t('agents.form.notifyEmail')}</div>
|
||||
<div className={toggleOffHint}>{t('agents.form.notifyEmailHint')}</div>
|
||||
</div>
|
||||
<div className={`w-9 h-5 rounded-full transition-colors flex-shrink-0 ${notifyEmail ? 'bg-primary' : 'bg-slate-200'}`}>
|
||||
<div className={`w-4 h-4 bg-white rounded-full shadow-sm transition-transform mt-0.5 ${notifyEmail ? 'translate-x-4.5 ml-0.5' : 'ml-0.5'}`} />
|
||||
<div className={`w-9 h-5 rounded-full transition-colors flex-shrink-0 ${notifyEmail ? 'bg-primary' : 'bg-muted'}`}>
|
||||
<div className={`w-4 h-4 bg-card rounded-full shadow-sm transition-transform mt-0.5 ${notifyEmail ? 'translate-x-4.5 ml-0.5' : 'ml-0.5'}`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -371,16 +381,16 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
className={`flex items-center gap-3 p-3 rounded-lg border-2 cursor-pointer transition-all ${
|
||||
includeImages
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-slate-200 hover:border-slate-300'
|
||||
: toggleOffBorder
|
||||
}`}
|
||||
>
|
||||
<ImageIcon className={`w-4 h-4 flex-shrink-0 ${includeImages ? 'text-primary' : 'text-slate-400'}`} />
|
||||
<ImageIcon className={`w-4 h-4 flex-shrink-0 ${includeImages ? 'text-primary' : toggleOffIcon}`} />
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="text-sm font-medium text-slate-700">{t('agents.form.includeImages')}</div>
|
||||
<div className="text-xs text-slate-400">{t('agents.form.includeImagesHint')}</div>
|
||||
<div className={toggleOffLabel}>{t('agents.form.includeImages')}</div>
|
||||
<div className={toggleOffHint}>{t('agents.form.includeImagesHint')}</div>
|
||||
</div>
|
||||
<div className={`w-9 h-5 rounded-full transition-colors flex-shrink-0 ${includeImages ? 'bg-primary' : 'bg-slate-200'}`}>
|
||||
<div className={`w-4 h-4 bg-white rounded-full shadow-sm transition-transform mt-0.5 ${includeImages ? 'translate-x-4.5 ml-0.5' : 'ml-0.5'}`} />
|
||||
<div className={`w-9 h-5 rounded-full transition-colors flex-shrink-0 ${includeImages ? 'bg-primary' : 'bg-muted'}`}>
|
||||
<div className={`w-4 h-4 bg-card rounded-full shadow-sm transition-transform mt-0.5 ${includeImages ? 'translate-x-4.5 ml-0.5' : 'ml-0.5'}`} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -388,7 +398,7 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setShowAdvanced(!showAdvanced)}
|
||||
className="flex items-center gap-2 text-sm text-slate-500 hover:text-slate-700 font-medium w-full pt-2 border-t border-slate-100"
|
||||
className="flex items-center gap-2 text-sm text-muted-foreground hover:text-foreground font-medium w-full pt-2 border-t border-border"
|
||||
>
|
||||
{showAdvanced ? <ChevronUp className="w-4 h-4" /> : <ChevronDown className="w-4 h-4" />}
|
||||
{t('agents.form.advancedMode')}
|
||||
@@ -398,23 +408,23 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
{showAdvanced && (
|
||||
<>
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1">
|
||||
<label className="block text-sm font-medium text-foreground mb-1">
|
||||
{t('agents.form.instructions')}
|
||||
<FieldHelp tooltip={t('agents.help.tooltips.instructions')} />
|
||||
<span className="text-xs text-slate-400 font-normal ml-1">({t('agents.form.instructionsHint')})</span>
|
||||
<span className="text-xs text-muted-foreground font-normal ml-1">({t('agents.form.instructionsHint')})</span>
|
||||
</label>
|
||||
<textarea
|
||||
value={role}
|
||||
onChange={e => setRole(e.target.value)}
|
||||
rows={3}
|
||||
className="w-full px-3 py-2 text-sm border border-slate-200 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary resize-y min-h-[80px]"
|
||||
className="w-full px-3 py-2 text-sm border border-input rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/20 focus:border-primary resize-y min-h-[80px] bg-card text-foreground"
|
||||
placeholder={t('agents.form.instructionsPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{/* Advanced: Tools */}
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-2">{t('agents.tools.title')}<FieldHelp tooltip={t('agents.help.tooltips.tools')} /></label>
|
||||
<label className="block text-sm font-medium text-foreground mb-2">{t('agents.tools.title')}<FieldHelp tooltip={t('agents.help.tooltips.tools')} /></label>
|
||||
<div className="grid grid-cols-2 gap-2">
|
||||
{availableTools.map(at => {
|
||||
const Icon = at.icon
|
||||
@@ -432,20 +442,20 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
flex items-center gap-2 px-3 py-2 rounded-lg border text-sm transition-all text-left
|
||||
${isSelected
|
||||
? 'border-primary bg-primary/5 text-primary font-medium'
|
||||
: 'border-slate-200 text-slate-600 hover:border-slate-300'}
|
||||
: `${toggleOffBorder} text-muted-foreground`}
|
||||
`}
|
||||
>
|
||||
<Icon className="w-4 h-4 flex-shrink-0" />
|
||||
<span>{t(at.labelKey)}</span>
|
||||
{at.external && !isSelected && (
|
||||
<span className="ml-auto text-[10px] text-amber-500 bg-amber-50 px-1.5 py-0.5 rounded-full">{t('agents.tools.configNeeded')}</span>
|
||||
<span className="ml-auto text-[10px] text-amber-600 dark:text-amber-400 bg-amber-50 dark:bg-amber-950 px-1.5 py-0.5 rounded-full">{t('agents.tools.configNeeded')}</span>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
{selectedTools.length > 0 && (
|
||||
<p className="text-xs text-slate-400 mt-1.5">
|
||||
<p className="text-xs text-muted-foreground mt-1.5">
|
||||
{t('agents.tools.selected', { count: selectedTools.length })}
|
||||
</p>
|
||||
)}
|
||||
@@ -454,9 +464,9 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
{/* Advanced: Max Steps */}
|
||||
{selectedTools.length > 0 && (
|
||||
<div>
|
||||
<label className="block text-sm font-medium text-slate-700 mb-1.5">
|
||||
<label className="block text-sm font-medium text-foreground mb-1.5">
|
||||
{t('agents.tools.maxSteps')}<FieldHelp tooltip={t('agents.help.tooltips.maxSteps')} />
|
||||
<span className="text-slate-400 font-normal ml-1">({maxSteps})</span>
|
||||
<span className="text-muted-foreground font-normal ml-1">({maxSteps})</span>
|
||||
</label>
|
||||
<input
|
||||
type="range"
|
||||
@@ -466,7 +476,7 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
onChange={e => setMaxSteps(Number(e.target.value))}
|
||||
className="w-full accent-primary"
|
||||
/>
|
||||
<div className="flex justify-between text-xs text-slate-400">
|
||||
<div className="flex justify-between text-xs text-muted-foreground">
|
||||
<span>3</span>
|
||||
<span>25</span>
|
||||
</div>
|
||||
@@ -480,14 +490,14 @@ export function AgentForm({ agent, notebooks, onSave, onCancel }: AgentFormProps
|
||||
<button
|
||||
type="button"
|
||||
onClick={onCancel}
|
||||
className="px-4 py-2 text-sm font-medium text-slate-600 bg-slate-100 rounded-lg hover:bg-slate-200 transition-colors"
|
||||
className="px-4 py-2 text-sm font-medium text-muted-foreground bg-muted rounded-lg hover:bg-accent transition-colors"
|
||||
>
|
||||
{t('agents.form.cancel')}
|
||||
</button>
|
||||
<button
|
||||
type="submit"
|
||||
disabled={isSaving}
|
||||
className="px-4 py-2 text-sm font-medium text-white bg-primary rounded-lg hover:bg-primary/90 transition-colors disabled:opacity-50"
|
||||
className="px-4 py-2 text-sm font-medium text-primary-foreground bg-primary rounded-lg hover:bg-primary/90 transition-colors disabled:opacity-50"
|
||||
>
|
||||
{isSaving ? t('agents.form.saving') : agent ? t('agents.form.save') : t('agents.form.create')}
|
||||
</button>
|
||||
|
||||
@@ -66,15 +66,15 @@ export function AgentRunLog({ agentId, agentName, onClose }: AgentRunLogProps) {
|
||||
|
||||
return (
|
||||
<div className="fixed inset-0 bg-black/30 flex items-center justify-center z-50 p-4">
|
||||
<div className="bg-white rounded-2xl shadow-xl max-w-md w-full max-h-[70vh] overflow-hidden flex flex-col">
|
||||
<div className="bg-card rounded-2xl shadow-xl max-w-md w-full max-h-[70vh] overflow-hidden flex flex-col">
|
||||
{/* Header */}
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-slate-100">
|
||||
<div className="flex items-center justify-between px-5 py-4 border-b border-border">
|
||||
<div>
|
||||
<h3 className="font-semibold text-slate-800">{t('agents.runLog.title')}</h3>
|
||||
<p className="text-xs text-slate-400">{agentName}</p>
|
||||
<h3 className="font-semibold text-card-foreground">{t('agents.runLog.title')}</h3>
|
||||
<p className="text-xs text-muted-foreground">{agentName}</p>
|
||||
</div>
|
||||
<button onClick={onClose} className="p-1 rounded-md hover:bg-slate-100">
|
||||
<X className="w-5 h-5 text-slate-400" />
|
||||
<button onClick={onClose} className="p-1 rounded-md hover:bg-accent">
|
||||
<X className="w-5 h-5 text-muted-foreground" />
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -82,12 +82,12 @@ export function AgentRunLog({ agentId, agentName, onClose }: AgentRunLogProps) {
|
||||
<div className="flex-1 overflow-y-auto p-4 space-y-2">
|
||||
{loading && (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<Loader2 className="w-5 h-5 animate-spin text-slate-400" />
|
||||
<Loader2 className="w-5 h-5 animate-spin text-muted-foreground" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{!loading && actions.length === 0 && (
|
||||
<p className="text-center text-sm text-slate-400 py-8">
|
||||
<p className="text-center text-sm text-muted-foreground py-8">
|
||||
{t('agents.runLog.noHistory')}
|
||||
</p>
|
||||
)}
|
||||
@@ -103,10 +103,10 @@ export function AgentRunLog({ agentId, agentName, onClose }: AgentRunLogProps) {
|
||||
key={action.id}
|
||||
className={`
|
||||
p-3 rounded-lg border
|
||||
${action.status === 'success' ? 'bg-green-50/50 border-green-100' : ''}
|
||||
${action.status === 'failure' ? 'bg-red-50/50 border-red-100' : ''}
|
||||
${action.status === 'running' ? 'bg-blue-50/50 border-blue-100' : ''}
|
||||
${action.status === 'pending' ? 'bg-slate-50 border-slate-100' : ''}
|
||||
${action.status === 'success' ? 'bg-green-50/50 dark:bg-green-950/50 border-green-100 dark:border-green-900' : ''}
|
||||
${action.status === 'failure' ? 'bg-red-50/50 dark:bg-red-950/50 border-red-100 dark:border-red-900' : ''}
|
||||
${action.status === 'running' ? 'bg-blue-50/50 dark:bg-blue-950/50 border-blue-100 dark:border-blue-900' : ''}
|
||||
${action.status === 'pending' ? 'bg-muted border-border' : ''}
|
||||
`}
|
||||
>
|
||||
<div className="flex items-start gap-3">
|
||||
@@ -114,19 +114,19 @@ export function AgentRunLog({ agentId, agentName, onClose }: AgentRunLogProps) {
|
||||
{action.status === 'success' && <CheckCircle2 className="w-4 h-4 text-green-500" />}
|
||||
{action.status === 'failure' && <XCircle className="w-4 h-4 text-red-500" />}
|
||||
{action.status === 'running' && <Loader2 className="w-4 h-4 text-blue-500 animate-spin" />}
|
||||
{action.status === 'pending' && <Clock className="w-4 h-4 text-slate-400" />}
|
||||
{action.status === 'pending' && <Clock className="w-4 h-4 text-muted-foreground" />}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="text-sm font-medium text-slate-700">
|
||||
<span className="text-sm font-medium text-foreground">
|
||||
{t(statusKeys[action.status] || action.status)}
|
||||
</span>
|
||||
<span className="text-xs text-slate-400">
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{formatDistanceToNow(new Date(action.createdAt), { addSuffix: true, locale: dateLocale })}
|
||||
</span>
|
||||
</div>
|
||||
{action.log && (
|
||||
<p className="text-xs text-slate-500 mt-1 line-clamp-2">{action.log}</p>
|
||||
<p className="text-xs text-muted-foreground mt-1 line-clamp-2">{action.log}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -142,13 +142,13 @@ export function AgentRunLog({ agentId, agentName, onClose }: AgentRunLogProps) {
|
||||
<div className="mt-2 space-y-2 pl-2">
|
||||
{toolSteps.map((step, i) => (
|
||||
<div key={i} className="text-xs border-l-2 border-primary/30 pl-2 py-1">
|
||||
<span className="font-medium text-slate-600">{t('agents.runLog.step', { num: step.step })}</span>
|
||||
<span className="font-medium text-muted-foreground">{t('agents.runLog.step', { num: step.step })}</span>
|
||||
{step.toolCalls && step.toolCalls.length > 0 && (
|
||||
<div className="mt-1 space-y-1">
|
||||
{step.toolCalls.map((tc, j) => (
|
||||
<div key={j} className="bg-slate-100 rounded px-2 py-1">
|
||||
<div key={j} className="bg-muted rounded px-2 py-1">
|
||||
<span className="font-mono text-primary">{tc.toolName}</span>
|
||||
<span className="text-slate-400 ml-1">
|
||||
<span className="text-muted-foreground ml-1">
|
||||
{JSON.stringify(tc.args).substring(0, 80)}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user