Skip to content

Commit fadbb3c

Browse files
committed
Optimize scheduled task interface
1 parent 247c022 commit fadbb3c

2 files changed

Lines changed: 192 additions & 134 deletions

File tree

packages/desktop/src/agent/AutomationForm.tsx

Lines changed: 80 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { useState, useEffect } from 'react';
22
import { X } from 'lucide-react';
33
import { useGlobalStore, type Automation } from '../stores/global.store';
4-
import { createAutomation, updateAutomation, listAutomations } from '../lib/core-api';
4+
import { createAutomation, updateAutomation } from '../lib/core-api';
55

66
interface AutomationFormProps {
77
automationId: string | null;
@@ -32,7 +32,12 @@ const WEEKDAYS = [
3232
{ value: '0', label: '周日' },
3333
];
3434

35-
export function AutomationForm({ automationId, defaultProjectCwd, onClose, onSaved }: AutomationFormProps) {
35+
export function AutomationForm({
36+
automationId,
37+
defaultProjectCwd,
38+
onClose,
39+
onSaved,
40+
}: AutomationFormProps) {
3641
const automations = useGlobalStore((s) => s.agent.automations);
3742
const existing = automationId ? automations.find((a: Automation) => a.id === automationId) : null;
3843

@@ -46,7 +51,9 @@ export function AutomationForm({ automationId, defaultProjectCwd, onClose, onSav
4651
const [intervalUnit, setIntervalUnit] = useState<'hours' | 'minutes'>('hours');
4752
const [customCron, setCustomCron] = useState(existing?.cron ?? '');
4853
const [timezone, setTimezone] = useState(existing?.timezone ?? 'Asia/Shanghai');
49-
const [sandbox, setSandbox] = useState<'readonly' | 'workspace-write'>(existing?.sandbox ?? 'workspace-write');
54+
const [sandbox, setSandbox] = useState<'readonly' | 'workspace-write'>(
55+
existing?.sandbox ?? 'workspace-write'
56+
);
5057
const [projectCwd, setProjectCwd] = useState(existing?.projectCwd ?? defaultProjectCwd);
5158
const [runOnce, setRunOnce] = useState(existing?.runOnce ?? false);
5259
const [saving, setSaving] = useState(false);
@@ -161,49 +168,62 @@ export function AutomationForm({ automationId, defaultProjectCwd, onClose, onSav
161168
}
162169
}
163170

171+
const inputClass =
172+
'w-full px-3 py-2 bg-[var(--bg-base)] border border-[var(--border-card)] rounded-lg text-[13px] text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent-primary)] transition-colors';
173+
const selectClass =
174+
'w-full px-3 py-2 bg-[var(--bg-base)] border border-[var(--border-card)] rounded-lg text-[13px] text-[var(--text-primary)] focus:outline-none focus:border-[var(--accent-primary)] transition-colors';
175+
const labelClass = 'block text-[12px] text-[var(--text-muted)] mb-1.5';
176+
164177
return (
165-
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
166-
<div className="bg-[#252525] rounded-lg border border-[#333] w-full max-w-md mx-4 max-h-[90vh] overflow-y-auto">
167-
<div className="flex items-center justify-between px-4 py-3 border-b border-[#333]">
168-
<h3 className="text-sm font-medium">{automationId ? '编辑自动化任务' : '新建自动化任务'}</h3>
169-
<button onClick={onClose} className="p-1 hover:bg-[#333] rounded transition-colors">
178+
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50">
179+
<div className="bg-[var(--bg-panel)] rounded-xl border border-[var(--border-default)] w-full max-w-md mx-4 max-h-[85vh] overflow-y-auto shadow-2xl">
180+
<div className="flex items-center justify-between px-5 py-4 border-b border-[var(--border-default)]">
181+
<h3 className="text-[14px] font-medium text-[var(--text-title)]">
182+
{automationId ? '编辑自动化任务' : '新建自动化任务'}
183+
</h3>
184+
<button
185+
onClick={onClose}
186+
className="p-1.5 rounded-lg hover:bg-[var(--bg-hover)] text-[var(--text-placeholder)] hover:text-[var(--text-primary)] transition-colors"
187+
>
170188
<X size={16} />
171189
</button>
172190
</div>
173191

174-
<form onSubmit={handleSubmit} className="p-4 space-y-4">
192+
<form onSubmit={handleSubmit} className="p-5 space-y-4">
175193
{error && (
176-
<div className="text-xs text-red-400 bg-red-900/20 px-3 py-2 rounded">{error}</div>
194+
<div className="text-[12px] text-red-500 bg-red-500/10 px-3 py-2 rounded-lg">
195+
{error}
196+
</div>
177197
)}
178198

179199
<div>
180-
<label className="block text-xs text-gray-400 mb-1">名称</label>
200+
<label className={labelClass}>名称</label>
181201
<input
182202
type="text"
183203
value={name}
184204
onChange={(e) => setName(e.target.value)}
185-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
205+
className={inputClass}
186206
placeholder="每日报告"
187207
/>
188208
</div>
189209

190210
<div>
191-
<label className="block text-xs text-gray-400 mb-1">任务描述 (发送给 Agent)</label>
211+
<label className={labelClass}>任务描述 (发送给 Agent)</label>
192212
<textarea
193213
value={description}
194214
onChange={(e) => setDescription(e.target.value)}
195215
rows={3}
196-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500 resize-none"
216+
className={`${inputClass} resize-none`}
197217
placeholder="请检查项目状态并生成日报"
198218
/>
199219
</div>
200220

201221
<div>
202-
<label className="block text-xs text-gray-400 mb-1">触发频率</label>
222+
<label className={labelClass}>触发频率</label>
203223
<select
204224
value={frequency}
205225
onChange={(e) => setFrequency(e.target.value as FrequencyType)}
206-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
226+
className={selectClass}
207227
>
208228
<option value="daily">每天</option>
209229
<option value="weekly">每周</option>
@@ -214,27 +234,27 @@ export function AutomationForm({ automationId, defaultProjectCwd, onClose, onSav
214234
</div>
215235

216236
{frequency === 'daily' && (
217-
<div className="flex gap-2">
237+
<div className="flex gap-3">
218238
<div className="flex-1">
219-
<label className="block text-xs text-gray-400 mb-1">小时</label>
239+
<label className={labelClass}>小时</label>
220240
<input
221241
type="number"
222242
min="0"
223243
max="23"
224244
value={hour}
225245
onChange={(e) => setHour(e.target.value)}
226-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
246+
className={inputClass}
227247
/>
228248
</div>
229249
<div className="flex-1">
230-
<label className="block text-xs text-gray-400 mb-1">分钟</label>
250+
<label className={labelClass}>分钟</label>
231251
<input
232252
type="number"
233253
min="0"
234254
max="59"
235255
value={minute}
236256
onChange={(e) => setMinute(e.target.value)}
237-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
257+
className={inputClass}
238258
/>
239259
</div>
240260
</div>
@@ -243,62 +263,64 @@ export function AutomationForm({ automationId, defaultProjectCwd, onClose, onSav
243263
{frequency === 'weekly' && (
244264
<>
245265
<div>
246-
<label className="block text-xs text-gray-400 mb-1">星期</label>
266+
<label className={labelClass}>星期</label>
247267
<select
248268
value={weekday}
249269
onChange={(e) => setWeekday(e.target.value)}
250-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
270+
className={selectClass}
251271
>
252272
{WEEKDAYS.map((d) => (
253-
<option key={d.value} value={d.value}>{d.label}</option>
273+
<option key={d.value} value={d.value}>
274+
{d.label}
275+
</option>
254276
))}
255277
</select>
256278
</div>
257-
<div className="flex gap-2">
279+
<div className="flex gap-3">
258280
<div className="flex-1">
259-
<label className="block text-xs text-gray-400 mb-1">小时</label>
281+
<label className={labelClass}>小时</label>
260282
<input
261283
type="number"
262284
min="0"
263285
max="23"
264286
value={hour}
265287
onChange={(e) => setHour(e.target.value)}
266-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
288+
className={inputClass}
267289
/>
268290
</div>
269291
<div className="flex-1">
270-
<label className="block text-xs text-gray-400 mb-1">分钟</label>
292+
<label className={labelClass}>分钟</label>
271293
<input
272294
type="number"
273295
min="0"
274296
max="59"
275297
value={minute}
276298
onChange={(e) => setMinute(e.target.value)}
277-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
299+
className={inputClass}
278300
/>
279301
</div>
280302
</div>
281303
</>
282304
)}
283305

284306
{frequency === 'interval' && (
285-
<div className="flex gap-2">
307+
<div className="flex gap-3">
286308
<div className="flex-1">
287-
<label className="block text-xs text-gray-400 mb-1">间隔</label>
309+
<label className={labelClass}>间隔</label>
288310
<input
289311
type="number"
290312
min="1"
291313
value={intervalValue}
292314
onChange={(e) => setIntervalValue(e.target.value)}
293-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
315+
className={inputClass}
294316
/>
295317
</div>
296318
<div className="flex-1">
297-
<label className="block text-xs text-gray-400 mb-1">单位</label>
319+
<label className={labelClass}>单位</label>
298320
<select
299321
value={intervalUnit}
300322
onChange={(e) => setIntervalUnit(e.target.value as 'hours' | 'minutes')}
301-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
323+
className={selectClass}
302324
>
303325
<option value="hours">小时</option>
304326
<option value="minutes">分钟</option>
@@ -308,65 +330,69 @@ export function AutomationForm({ automationId, defaultProjectCwd, onClose, onSav
308330
)}
309331

310332
{frequency === 'once' && (
311-
<div className="flex gap-2">
333+
<div className="flex gap-3">
312334
<div className="flex-1">
313-
<label className="block text-xs text-gray-400 mb-1">小时</label>
335+
<label className={labelClass}>小时</label>
314336
<input
315337
type="number"
316338
min="0"
317339
max="23"
318340
value={hour}
319341
onChange={(e) => setHour(e.target.value)}
320-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
342+
className={inputClass}
321343
/>
322344
</div>
323345
<div className="flex-1">
324-
<label className="block text-xs text-gray-400 mb-1">分钟</label>
346+
<label className={labelClass}>分钟</label>
325347
<input
326348
type="number"
327349
min="0"
328350
max="59"
329351
value={minute}
330352
onChange={(e) => setMinute(e.target.value)}
331-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
353+
className={inputClass}
332354
/>
333355
</div>
334356
</div>
335357
)}
336358

337359
{frequency === 'custom' && (
338360
<div>
339-
<label className="block text-xs text-gray-400 mb-1">Cron 表达式</label>
361+
<label className={labelClass}>Cron 表达式</label>
340362
<input
341363
type="text"
342364
value={customCron}
343365
onChange={(e) => setCustomCron(e.target.value)}
344-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500 font-mono"
366+
className={`${inputClass} font-mono`}
345367
placeholder="0 9 * * *"
346368
/>
347-
<p className="text-xs text-gray-500 mt-1">格式: 分 时 日 月 周</p>
369+
<p className="text-[11px] text-[var(--text-disabled)] mt-1.5">
370+
格式: 分 时 日 月 周
371+
</p>
348372
</div>
349373
)}
350374

351375
<div>
352-
<label className="block text-xs text-gray-400 mb-1">时区</label>
376+
<label className={labelClass}>时区</label>
353377
<select
354378
value={timezone}
355379
onChange={(e) => setTimezone(e.target.value)}
356-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
380+
className={selectClass}
357381
>
358382
{TIMEZONES.map((tz) => (
359-
<option key={tz} value={tz}>{tz}</option>
383+
<option key={tz} value={tz}>
384+
{tz}
385+
</option>
360386
))}
361387
</select>
362388
</div>
363389

364390
<div>
365-
<label className="block text-xs text-gray-400 mb-1">沙箱模式</label>
391+
<label className={labelClass}>沙箱模式</label>
366392
<select
367393
value={sandbox}
368394
onChange={(e) => setSandbox(e.target.value as 'readonly' | 'workspace-write')}
369-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500"
395+
className={selectClass}
370396
>
371397
<option value="workspace-write">workspace-write (可读写)</option>
372398
<option value="readonly">readonly (只读)</option>
@@ -375,12 +401,12 @@ export function AutomationForm({ automationId, defaultProjectCwd, onClose, onSav
375401

376402
{!automationId && (
377403
<div>
378-
<label className="block text-xs text-gray-400 mb-1">项目路径</label>
404+
<label className={labelClass}>项目路径</label>
379405
<input
380406
type="text"
381407
value={projectCwd}
382408
onChange={(e) => setProjectCwd(e.target.value)}
383-
className="w-full px-3 py-2 bg-[#1a1a1a] border border-[#333] rounded text-sm focus:outline-none focus:border-blue-500 font-mono"
409+
className={`${inputClass} font-mono`}
384410
placeholder="/home/user/project"
385411
/>
386412
</div>
@@ -392,25 +418,25 @@ export function AutomationForm({ automationId, defaultProjectCwd, onClose, onSav
392418
id="runOnce"
393419
checked={runOnce}
394420
onChange={(e) => setRunOnce(e.target.checked)}
395-
className="rounded border-[#333]"
421+
className="rounded border-[var(--border-card)]"
396422
/>
397-
<label htmlFor="runOnce" className="text-xs text-gray-400">
423+
<label htmlFor="runOnce" className="text-[12px] text-[var(--text-muted)]">
398424
执行一次后自动删除
399425
</label>
400426
</div>
401427

402-
<div className="flex justify-end gap-2 pt-2">
428+
<div className="flex justify-end gap-2 pt-3 border-t border-[var(--border-default)]">
403429
<button
404430
type="button"
405431
onClick={onClose}
406-
className="px-4 py-2 text-sm bg-[#333] hover:bg-[#444] rounded transition-colors"
432+
className="px-4 py-2 text-[13px] bg-[var(--bg-card)] text-[var(--text-secondary)] hover:bg-[var(--bg-hover)] rounded-lg transition-colors border border-[var(--border-card)]"
407433
>
408434
取消
409435
</button>
410436
<button
411437
type="submit"
412438
disabled={saving}
413-
className="px-4 py-2 text-sm bg-blue-600 hover:bg-blue-700 rounded transition-colors disabled:opacity-50"
439+
className="px-4 py-2 text-[13px] bg-[var(--accent-primary)] text-white rounded-lg hover:opacity-90 transition-opacity disabled:opacity-50"
414440
>
415441
{saving ? '保存中...' : '保存'}
416442
</button>

0 commit comments

Comments
 (0)