@v587d/capital-generation 2.13.1 → 2.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -7,6 +7,7 @@ const { createElement: h, useEffect, useRef, useState } = require('react')
7
7
  const {
8
8
  IconAlarmClockOutlineRegular,
9
9
  IconChecklistOutlineRegular,
10
+ IconChevronDownOutlineRegular,
10
11
  IconChevronLeftOutlineRegular,
11
12
  IconChevronRightOutlineRegular,
12
13
  IconClockOutlineRegular,
@@ -23,6 +24,7 @@ const {
23
24
  IconPlayOutlineRegular,
24
25
  IconPlusOutlineRegular,
25
26
  IconRefreshOutlineRegular,
27
+ IconRightUpOutlineRegular,
26
28
  IconSearchOutlineRegular,
27
29
  IconSparkleRegular,
28
30
  IconTrashOutlineRegular,
@@ -39,6 +41,7 @@ const PANEL_ID = 'capital-automation'
39
41
  const STYLE_TAG = 'capital-automation-css'
40
42
  const ACTIVE_RUN_STATUSES = ['queued', 'starting', 'running']
41
43
  const FAILURE_RUN_STATUSES = ['failed', 'timed_out', 'blocked_requires_user', 'unknown_after_restart']
44
+ const SKIPPED_RUN_STATUSES = ['skipped_overlap', 'skipped_calendar_unavailable', 'missed']
42
45
  const DEFAULT_LIMITS = { runTimeoutMinutes: 30, idleTimeoutMinutes: 5, maxSteps: 300, maxToolCalls: 1200 }
43
46
  const WEEKDAYS = [
44
47
  { value: 1, label: '一' },
@@ -51,6 +54,7 @@ const WEEKDAYS = [
51
54
  ]
52
55
  const TIMEZONES = ['Asia/Shanghai', 'Asia/Hong_Kong', 'Asia/Singapore', 'Asia/Tokyo', 'UTC', 'Europe/London', 'America/New_York', 'America/Los_Angeles']
53
56
  const HISTORY_DAYS = 30
57
+ const DETAIL_RECENT_RUN_LIMIT = 3
54
58
 
55
59
  const zh = {
56
60
  panel: 'CapGen 投研自动化',
@@ -104,7 +108,7 @@ const zh = {
104
108
  categoryField: '任务分类',
105
109
  categoryFixed: '内置模板的分类固定;复制为我的任务后可以调整。',
106
110
  modeField: '输出侧重',
107
- modeHint: '只影响输出结构,不改变 Agent preset;所有运行固定使用 Capital 自动模式。',
111
+ modeHint: '只影响输出结构;所有运行固定使用自动模式。',
108
112
  mode: { news: '资讯', market: '行情', review: '复盘', research: '研究' },
109
113
  cadence: { daily: '每天', weekly: '每周', monthly: '每月' },
110
114
  stateLabel: '状态',
@@ -137,7 +141,11 @@ const zh = {
137
141
  dirty: '有未保存的修改',
138
142
  noDirty: '配置已同步',
139
143
  nextRun: '下次运行',
144
+ nextRunDormant: '添加后开始调度',
145
+ nextRunPaused: '启用后恢复调度',
146
+ notScheduled: '当前不会自动运行',
140
147
  lastResult: '上次结果',
148
+ lastRunEmpty: '运行后可在这里查看结果',
141
149
  never: '暂无',
142
150
  calendarUnavailable: '日历不可用',
143
151
  unread: '未读',
@@ -182,16 +190,13 @@ const zh = {
182
190
  notifyFailure: '失败时通知',
183
191
  notifySuccess: '成功时通知',
184
192
  notifyHint: '通知只记录在当前 profile;不会发送邮件或外部消息。',
185
- presetLabel: 'Agent preset',
186
- presetValue: 'capital-generation(自动模式)',
187
- policy: '执行策略',
188
- policyText: '固定 preset:capital-generation(Capital 自动模式)。工作区来自已注册列表;未选择时使用专属自动化工作区。无人值守运行禁止提问、写文件与 shell 提权。',
193
+ presetLabel: '运行模式',
194
+ presetValue: '自动模式',
189
195
  budgetHint: '工具调用与步骤预算覆盖整棵运行子树:子 Agent 与 PTC 派发都计入。',
190
196
  environment: '执行环境',
191
197
  policyField: '无人值守策略',
192
198
  cwdKind: '工作区来源',
193
199
  techInfo: '技术信息',
194
- version: '版本',
195
200
  profileKey: 'profileKey',
196
201
  runHistory: '运行历史',
197
202
  last30Days: '最近 30 天',
@@ -258,9 +263,9 @@ const zh = {
258
263
  runBlocked: '任务正在运行,结束或取消后才能再次运行。',
259
264
  configWhileRunning: '任务正在运行;修改会在下一次运行时生效。',
260
265
  configBuiltinHint: '这是内置模板;修改只覆盖当前 profile,休眠后会恢复默认。',
261
- detailRecentRuns: '最近运行',
266
+ detailRecentRuns: '最近 {n} 次运行',
262
267
  detailNoRuns: '还没有运行记录。',
263
- detailAllRuns: '查看运行页',
268
+ detailAllRuns: '完整记录',
264
269
  detailTaskNotifications: '任务通知',
265
270
  taskRunsLimit: '这里显示当前任务最近 20 次运行;全部运行见「运行」页。',
266
271
  runErrors: '{n} 条错误',
@@ -467,8 +472,6 @@ function installStyles() {
467
472
  .capital-automation-herotitle { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
468
473
  .capital-automation-herotitle h2 { margin: 0; font-size: 22px; line-height: 30px; font-weight: 600; letter-spacing: .1px; overflow-wrap: anywhere; }
469
474
  .capital-automation-herodesc { max-width: 760px; margin: 5px 0 0; color: var(--ca-muted); font-size: 12.5px; line-height: 19px; }
470
- .capital-automation-herometa { display: flex; flex-wrap: wrap; align-items: center; gap: 5px 14px; margin-top: 10px; color: var(--ca-muted); font-size: 11.5px; line-height: 18px; }
471
- .capital-automation-herometa > span { display: inline-flex; align-items: center; gap: 5px; }
472
475
  .capital-automation-heroactions { display: flex; flex-wrap: wrap; align-items: center; gap: 7px; flex: none; padding-top: 2px; }
473
476
  .capital-automation-detailtabs { display: flex; align-items: center; gap: 26px; overflow-x: auto; overflow-y: hidden; border-bottom: .5px solid var(--ca-line); }
474
477
  .capital-automation-detailtab { position: relative; flex: none; height: 42px; padding: 0 1px; border: 0; background: transparent; color: var(--ca-muted); font: inherit; font-size: 12.5px; font-weight: 500; cursor: pointer; }
@@ -487,11 +490,13 @@ function installStyles() {
487
490
  .capital-automation-statvalue[data-tone="ok"] { color: var(--ca-success-text); }
488
491
  .capital-automation-statvalue[data-tone="error"] { color: var(--ca-error-text); }
489
492
  .capital-automation-statvalue[data-tone="running"] { color: var(--ca-business-text); }
493
+ .capital-automation-statvalue[data-tone="warn"] { color: var(--ca-warn-text); }
490
494
  .capital-automation-statsub { color: var(--ca-caption); font-size: 11px; line-height: 16px; }
491
495
  .capital-automation-panel { padding: 16px 18px; }
492
496
  .capital-automation-panelhead { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: 8px 12px; margin-bottom: 12px; }
493
497
  .capital-automation-panelhead h3 { margin: 0; font-size: 13.5px; line-height: 20px; font-weight: 600; }
494
498
  .capital-automation-panelhead .capital-automation-btn { margin-left: auto; }
499
+ .capital-automation-panelhead .capital-automation-btn[data-variant="ghost"] { padding-right: 0; }
495
500
  .capital-automation-kv { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px 18px; }
496
501
  .capital-automation-kvitem { min-width: 0; }
497
502
  .capital-automation-kvlabel { display: block; color: var(--ca-caption); font-size: 11px; line-height: 16px; }
@@ -505,25 +510,35 @@ function installStyles() {
505
510
  .capital-automation-error { margin: 0; color: var(--ca-error-text); font-size: 12px; line-height: 18px; }
506
511
  .capital-automation-toast { position: fixed; bottom: 26px; left: 50%; z-index: 1300; max-width: min(520px, calc(100vw - 40px)); transform: translateX(-50%); padding: 8px 15px; border: .5px solid var(--ca-line-soft); border-radius: 999px; background: var(--dsw-alias-label-primary); color: var(--dsw-alias-bg-base); box-shadow: 0 8px 26px rgba(0,0,0,.22); font-size: 12px; line-height: 18px; }
507
512
  .capital-automation-form { display: flex; flex-direction: column; gap: 16px; }
513
+ .capital-automation-formnote { display: flex; align-items: flex-start; gap: 7px; padding: 9px 11px; border: .5px solid var(--ca-line); border-radius: 10px; background: var(--ca-field); color: var(--ca-muted); font-size: 11.5px; line-height: 18px; }
514
+ .capital-automation-formnote svg { flex: none; margin-top: 2px; }
508
515
  .capital-automation-formgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 12px; }
509
516
  .capital-automation-field { display: flex; flex-direction: column; gap: 5px; min-width: 0; }
510
517
  .capital-automation-field > span { color: var(--ca-muted); font-size: 11.5px; line-height: 17px; }
511
- .capital-automation-field input, .capital-automation-field select, .capital-automation-field textarea { box-sizing: border-box; width: 100%; padding: 0 10px; border: .5px solid var(--ca-line-soft); border-radius: 10px; background: var(--ca-field); color: var(--dsw-alias-label-primary); font: inherit; font-size: 12.5px; line-height: 32px; }
512
- .capital-automation-field input, .capital-automation-field select { height: 34px; }
518
+ .capital-automation-field input, .capital-automation-field textarea { box-sizing: border-box; width: 100%; padding: 0 10px; border: .5px solid var(--ca-line-soft); border-radius: 10px; background: var(--ca-field); color: var(--dsw-alias-label-primary); font: inherit; font-size: 12.5px; line-height: 32px; }
519
+ .capital-automation-field input { height: 34px; }
513
520
  .capital-automation-field textarea { min-height: 110px; padding: 9px 10px; line-height: 19px; resize: vertical; }
514
- .capital-automation-field input:hover, .capital-automation-field select:hover, .capital-automation-field textarea:hover { border-color: var(--ca-line); }
515
- .capital-automation-field input:focus-visible, .capital-automation-field select:focus-visible, .capital-automation-field textarea:focus-visible { border-color: var(--ca-business); outline: 2px solid color-mix(in srgb, var(--ca-business) 22%, transparent); outline-offset: 0; }
516
- .capital-automation-field input:disabled, .capital-automation-field select:disabled, .capital-automation-field textarea:disabled { color: var(--ca-muted); opacity: .8; }
521
+ .capital-automation-field input:hover, .capital-automation-field textarea:hover { border-color: var(--ca-line); }
522
+ .capital-automation-field input:focus-visible, .capital-automation-field textarea:focus-visible { border-color: var(--ca-business); outline: 2px solid color-mix(in srgb, var(--ca-business) 22%, transparent); outline-offset: 0; }
523
+ .capital-automation-field input:disabled, .capital-automation-field textarea:disabled { color: var(--ca-muted); opacity: .8; }
524
+ .capital-automation-select { box-sizing: border-box; display: inline-flex; align-items: center; justify-content: space-between; gap: 10px; min-width: 0; height: 34px; padding: 0 10px 0 12px; border: 0; border-radius: 10px; background: var(--ca-field); color: var(--dsw-alias-label-primary); font: inherit; font-size: 12.5px; line-height: 20px; text-align: left; cursor: pointer; transition: background .15s, opacity .15s; }
525
+ .capital-automation-select:hover:not(:disabled) { background: var(--ca-hover); }
526
+ .capital-automation-select[data-block="true"] { width: 100%; }
527
+ .capital-automation-select[data-size="sm"] { height: 28px; padding: 0 8px 0 9px; border-radius: 8px; font-size: 11.5px; }
528
+ .capital-automation-select:focus-visible { outline: 2px solid var(--dsw-focus-ring-color, var(--ca-business)); outline-offset: 0; }
529
+ .capital-automation-select:disabled { opacity: .5; cursor: not-allowed; }
530
+ .capital-automation-selectvalue { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
531
+ .capital-automation-select > svg { flex: none; color: var(--ca-muted); }
517
532
  .capital-automation-formactions { display: flex; flex-wrap: wrap; align-items: center; justify-content: flex-end; gap: 8px; padding-top: 14px; border-top: .5px solid var(--ca-line); }
518
533
  .capital-automation-formactions[data-sticky="true"] { position: sticky; bottom: -1px; z-index: 2; margin-top: 2px; padding: 12px 0 4px; background: var(--dsw-alias-bg-base); }
519
534
  .capital-automation-formstatus { margin-right: auto; color: var(--ca-caption); font-size: 11.5px; line-height: 18px; }
520
535
  .capital-automation-triggerbox { display: flex; flex-direction: column; gap: 12px; padding: 13px 14px; border: .5px solid var(--ca-line); border-radius: 14px; background: var(--ca-field); }
521
536
  .capital-automation-triggerrow { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; }
522
537
  .capital-automation-triggerclock { display: inline-grid; place-items: center; width: 32px; height: 32px; border-radius: 10px; background: linear-gradient(135deg, #5b9dff, #2563eb); color: #fff; flex: none; }
523
- .capital-automation-triggerrow label { display: inline-flex; align-items: center; gap: 6px; color: var(--ca-muted); font-size: 12px; }
524
- .capital-automation-triggerrow input, .capital-automation-triggerrow select { box-sizing: border-box; height: 32px; padding: 0 9px; border: .5px solid var(--ca-line-soft); border-radius: 9px; background: var(--ca-card); color: var(--dsw-alias-label-primary); font: inherit; font-size: 12px; }
538
+ .capital-automation-triggerrow label, .capital-automation-triggerfield { display: inline-flex; align-items: center; gap: 6px; color: var(--ca-muted); font-size: 12px; }
539
+ .capital-automation-triggerrow input { box-sizing: border-box; height: 32px; padding: 0 9px; border: .5px solid var(--ca-line-soft); border-radius: 9px; background: var(--ca-card); color: var(--dsw-alias-label-primary); font: inherit; font-size: 12px; }
525
540
  .capital-automation-triggerrow input[type="time"] { width: 112px; }
526
- .capital-automation-triggerrow select { max-width: 190px; }
541
+ .capital-automation-triggerrow .capital-automation-select { max-width: 190px; height: 32px; background: var(--ca-card); }
527
542
  .capital-automation-weekdays { display: flex; flex-wrap: wrap; gap: 4px; }
528
543
  .capital-automation-weekday { width: 30px; height: 30px; padding: 0; border: .5px solid var(--ca-line-soft); border-radius: 999px; background: transparent; color: var(--ca-muted); font: inherit; font-size: 11.5px; cursor: pointer; }
529
544
  .capital-automation-weekday:hover:not(:disabled) { border-color: var(--ca-line); color: var(--dsw-alias-label-primary); }
@@ -594,14 +609,14 @@ function installStyles() {
594
609
  .capital-automation-promptbox textarea { box-sizing: border-box; width: 100%; min-height: 120px; max-height: 320px; padding: 13px 14px; border: 0; outline: 0; background: transparent; color: var(--dsw-alias-label-primary); font: inherit; font-size: 13px; line-height: 21px; resize: vertical; }
595
610
  .capital-automation-promptbox textarea::placeholder { color: var(--ca-caption); }
596
611
  .capital-automation-prompttoolbar { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 8px 10px; border-top: .5px solid var(--ca-line); }
597
- .capital-automation-prompttoolbar label { display: inline-flex; align-items: center; gap: 5px; color: var(--ca-muted); font-size: 11.5px; }
598
- .capital-automation-prompttoolbar select { max-width: 168px; height: 28px; padding: 0 8px; border: .5px solid var(--ca-line-soft); border-radius: 8px; background: var(--ca-card); color: var(--dsw-alias-label-primary); font: inherit; font-size: 11.5px; }
612
+ .capital-automation-prompttoolbar label, .capital-automation-toolbarfield { display: inline-flex; align-items: center; gap: 5px; color: var(--ca-muted); font-size: 11.5px; }
613
+ .capital-automation-prompttoolbar .capital-automation-select { max-width: 168px; background: var(--ca-card); }
599
614
  .capital-automation-prompttoolbar .capital-automation-presetchip { margin-left: auto; }
600
615
  .capital-automation-presetchip { display: inline-flex; align-items: center; gap: 5px; height: 26px; padding: 0 9px; border-radius: 999px; background: color-mix(in srgb, var(--ca-business) 12%, var(--ca-card)); color: var(--ca-business-text); font-size: 11px; font-weight: 500; white-space: nowrap; }
601
616
  .capital-automation-checklist { display: flex; flex-direction: column; gap: 2px; }
602
- .capital-automation-technical { margin-top: 8px; color: var(--ca-caption); font-size: 11px; line-height: 17px; }
603
- .capital-automation-technical summary { cursor: pointer; }
604
- .capital-automation-technical dl { display: grid; grid-template-columns: auto 1fr; gap: 3px 10px; margin: 7px 0 0; }
617
+ .capital-automation-technical { margin-top: 14px; padding-top: 12px; border-top: .5px solid var(--ca-line); color: var(--ca-caption); font-size: 11px; line-height: 17px; }
618
+ .capital-automation-technicaltitle { margin: 0 0 7px; color: var(--ca-muted); font-size: 11.5px; line-height: 17px; font-weight: 500; }
619
+ .capital-automation-technical dl { display: grid; grid-template-columns: auto 1fr; gap: 3px 10px; margin: 0; }
605
620
  .capital-automation-technical dt { color: var(--ca-caption); }
606
621
  .capital-automation-technical dd { min-width: 0; margin: 0; overflow-wrap: anywhere; color: var(--ca-muted); }
607
622
  .capital-automation-page :focus-visible { outline: 2px solid var(--dsw-focus-ring-color, var(--ca-business)); outline-offset: 2px; }
@@ -846,6 +861,31 @@ function scheduleRangeSummary(trigger) {
846
861
  return '每天'
847
862
  }
848
863
 
864
+ // Dormant/paused tasks still carry a computed nextRunLocal, but that instant will not fire until
865
+ // the task is activated; don't present it as a pending schedule.
866
+ function nextRunView(task) {
867
+ if (task?.state === 'dormant') return { value: zh.nextRunDormant, sub: zh.notScheduled }
868
+ if (task?.state === 'paused') return { value: zh.nextRunPaused, sub: zh.notScheduled }
869
+ const value = task?.nextRunLocal ?? (task?.nextRunUnavailable ? zh.calendarUnavailable : zh.never)
870
+ const sub = task?.nextRunAt ? formatRelative(task.nextRunAt) : (task?.nextRunUnavailable ? zh.nextRunUnavailable : zh.never)
871
+ return { value, sub }
872
+ }
873
+
874
+ function lastRunView(run, timeZone) {
875
+ if (run === null || run === undefined) return { value: zh.neverRun, sub: zh.lastRunEmpty }
876
+ const active = isActiveRun(run)
877
+ const parts = [formatShortTime(run.queuedAt, timeZone)]
878
+ if (!active && typeof run.durationMs === 'number') parts.push(formatDuration(run.durationMs))
879
+ const errors = Array.isArray(run.errors) ? run.errors.length : 0
880
+ if (errors > 0) parts.push(zh.runErrors.replace('{n}', String(errors)))
881
+ let tone
882
+ if (run.status === 'succeeded') tone = 'ok'
883
+ else if (isFailureStatus(run.status)) tone = 'error'
884
+ else if (active) tone = 'running'
885
+ else if (SKIPPED_RUN_STATUSES.includes(run.status)) tone = 'warn'
886
+ return { value: statusLabel(run.status), sub: parts.filter(Boolean).join(' · '), tone }
887
+ }
888
+
849
889
  function workspaceOptions(meta, current) {
850
890
  const options = [{ id: '', title: zh.workspaceDefault }]
851
891
  const list = Array.isArray(meta?.workspaces) ? meta.workspaces : []
@@ -1039,6 +1079,21 @@ function RunRow(props) {
1039
1079
  const errorCount = Array.isArray(run.errors) ? run.errors.length : 0
1040
1080
  const [errorsOpen, setErrorsOpen] = useState(false)
1041
1081
  const errorPanelId = `capital-automation-run-errors-${run.id}`
1082
+ const errorBadge = errorCount > 0
1083
+ ? h('button', {
1084
+ type: 'button',
1085
+ className: 'capital-automation-runerrorbadge',
1086
+ 'data-tone': isFailureStatus(run.status) ? 'error' : 'warn',
1087
+ 'aria-expanded': errorsOpen ? 'true' : 'false',
1088
+ 'aria-controls': errorPanelId,
1089
+ 'aria-label': `${zh.runErrors.replace('{n}', String(errorCount))},${errorsOpen ? zh.hideErrorDetails : zh.showErrorDetails}`,
1090
+ title: errorsOpen ? zh.hideErrorDetails : zh.showErrorDetails,
1091
+ onClick: () => setErrorsOpen((current) => !current),
1092
+ },
1093
+ h(IconWarningOutlineRegular, { size: 12 }),
1094
+ zh.runErrors.replace('{n}', String(errorCount)),
1095
+ h(IconChevronRightOutlineRegular, { size: 12 }))
1096
+ : null
1042
1097
  return h('div', { className: 'capital-automation-run' },
1043
1098
  h('div', { className: 'capital-automation-runhead' },
1044
1099
  h('div', { className: 'capital-automation-runmain' },
@@ -1050,27 +1105,15 @@ function RunRow(props) {
1050
1105
  h('div', { className: 'capital-automation-runmetaline' },
1051
1106
  h('span', { className: 'capital-automation-runmeta' }, `${formatShortTime(run.queuedAt, task?.trigger?.timezone)} · ${run.trigger === 'manual' ? zh.triggerManual : zh.triggerScheduled}`),
1052
1107
  typeof run.durationMs === 'number' ? h('span', { className: 'capital-automation-runmeta' }, `${zh.duration} ${formatDuration(run.durationMs)}`) : null,
1053
- h('span', { className: 'capital-automation-runmeta' }, `${zh.steps} ${run.steps} · ${zh.tools} ${run.toolCalls}`))),
1108
+ h('span', { className: 'capital-automation-runmeta' }, `${zh.steps} ${run.steps} · ${zh.tools} ${run.toolCalls}`),
1109
+ errorBadge)),
1054
1110
  h('div', { className: 'capital-automation-runactions' },
1055
- errorCount > 0
1056
- ? h('button', {
1057
- type: 'button',
1058
- className: 'capital-automation-runerrorbadge',
1059
- 'data-tone': isFailureStatus(run.status) ? 'error' : 'warn',
1060
- 'aria-expanded': errorsOpen ? 'true' : 'false',
1061
- 'aria-controls': errorPanelId,
1062
- 'aria-label': `${zh.runErrors.replace('{n}', String(errorCount))},${errorsOpen ? zh.hideErrorDetails : zh.showErrorDetails}`,
1063
- title: errorsOpen ? zh.hideErrorDetails : zh.showErrorDetails,
1064
- onClick: () => setErrorsOpen((current) => !current),
1065
- },
1066
- h(IconWarningOutlineRegular, { size: 12 }),
1067
- zh.runErrors.replace('{n}', String(errorCount)),
1068
- h(IconChevronRightOutlineRegular, { size: 12 }))
1069
- : null,
1070
1111
  active && typeof onCancel === 'function'
1071
1112
  ? h('button', { type: 'button', className: 'capital-automation-btn', 'data-size': 'sm', 'data-variant': 'danger', disabled: busy, onClick: () => onCancel(run) }, zh.cancelRun)
1072
1113
  : run.sessionId && onOpenSession
1073
- ? h('button', { type: 'button', className: 'capital-automation-btn', 'data-size': 'sm', onClick: () => onOpenSession(run.sessionId) }, zh.openSession)
1114
+ ? h('button', { type: 'button', className: 'capital-automation-btn', 'data-size': 'sm', 'data-variant': 'primary', onClick: () => onOpenSession(run.sessionId) },
1115
+ h(IconRightUpOutlineRegular, { size: 13 }),
1116
+ zh.openSession)
1074
1117
  : null)),
1075
1118
  errorsOpen && errorCount > 0
1076
1119
  ? h('div', { id: errorPanelId, className: 'capital-automation-runerrors', role: 'region', 'aria-label': zh.errorDetailsLabel },
@@ -1266,7 +1309,7 @@ function TaskRow(props) {
1266
1309
  const { task, onOpen, menuOpen, onMenuToggle, onMenuClose, onEdit, onAction, onRequestConfirm, canRun, busy, mutationsDisabled } = props
1267
1310
  const run = task.lastRun
1268
1311
  const running = isActiveRun(run)
1269
- const nextLabel = task.nextRunLocal ?? (task.nextRunUnavailable ? zh.calendarUnavailable : zh.never)
1312
+ const next = nextRunView(task)
1270
1313
  return h('div', { className: 'capital-automation-trow' },
1271
1314
  h('button', { type: 'button', className: 'capital-automation-trowmain', onClick: () => onOpen(task.id) },
1272
1315
  h('span', { className: 'capital-automation-cell capital-automation-identity' },
@@ -1281,8 +1324,8 @@ function TaskRow(props) {
1281
1324
  h('span', { className: 'capital-automation-schedule' }, scheduleSummary(task.trigger)),
1282
1325
  h('span', { className: 'capital-automation-schedulesub' }, task.trigger?.timezone ?? '')),
1283
1326
  h('span', { className: 'capital-automation-cell', 'data-cell': 'next' },
1284
- h('span', { className: 'capital-automation-nextmain' }, nextLabel),
1285
- h('span', { className: 'capital-automation-nextsub' }, task.nextRunAt ? formatRelative(task.nextRunAt) : (task.nextRunUnavailable ? zh.nextRunUnavailable : zh.never))),
1327
+ h('span', { className: 'capital-automation-nextmain' }, next.value),
1328
+ h('span', { className: 'capital-automation-nextsub' }, next.sub)),
1286
1329
  h('span', { className: 'capital-automation-cell' },
1287
1330
  h(StateBadge, { state: task.state, running }))),
1288
1331
  h('div', { className: 'capital-automation-trowmenu' },
@@ -1347,7 +1390,9 @@ function TemplateCard(props) {
1347
1390
  h('span', { className: 'capital-automation-templatemeta' },
1348
1391
  h(IconClockOutlineRegular, { size: 12 }),
1349
1392
  `${zh.templateMeta} · ${scheduleSummary(task.trigger)}`),
1350
- h('button', { type: 'button', className: 'capital-automation-btn', 'data-size': 'sm', 'data-variant': 'primary', onClick: () => onEnable(task), disabled: busy || disabled }, busy ? zh.templateAdding : zh.templateAdd)))
1393
+ h('button', { type: 'button', className: 'capital-automation-btn', 'data-size': 'sm', 'data-variant': 'primary', onClick: () => onEnable(task), disabled: busy || disabled },
1394
+ h(IconPlusOutlineRegular, { size: 12 }),
1395
+ busy ? zh.templateAdding : zh.templateAdd)))
1351
1396
  }
1352
1397
 
1353
1398
  function EmptyState(props) {
@@ -1617,14 +1662,19 @@ function TriggerFields(props) {
1617
1662
  return h('div', { className: 'capital-automation-triggerbox' },
1618
1663
  h('div', { className: 'capital-automation-triggerrow' },
1619
1664
  h('span', { className: 'capital-automation-triggerclock', 'aria-hidden': 'true' }, h(IconAlarmClockOutlineRegular, { size: 16 })),
1620
- h('label', null,
1665
+ h('div', { className: 'capital-automation-triggerfield' },
1621
1666
  h('span', null, '重复'),
1622
- h('select', { value: trigger.cadence, disabled, onChange: (event) => setCadence(event.target.value) },
1623
- Object.entries(zh.cadence).map(([value, label]) => h('option', { key: value, value }, label)))),
1624
- h('label', null,
1667
+ h(Select, {
1668
+ value: trigger.cadence,
1669
+ options: Object.entries(zh.cadence).map(([value, label]) => ({ value, label })),
1670
+ onChange: setCadence,
1671
+ disabled,
1672
+ label: '重复',
1673
+ })),
1674
+ h('label', { className: 'capital-automation-triggerfield' },
1625
1675
  h('span', null, zh.timeField),
1626
1676
  h('input', { type: 'time', value: trigger.time, disabled, onChange: (event) => update({ time: event.target.value }) })),
1627
- h('label', null,
1677
+ h('label', { className: 'capital-automation-triggerfield' },
1628
1678
  h('span', null, zh.timezoneField),
1629
1679
  h('input', {
1630
1680
  type: 'text',
@@ -1647,13 +1697,18 @@ function TriggerFields(props) {
1647
1697
  }, day.label)))
1648
1698
  : null,
1649
1699
  trigger.cadence === 'monthly'
1650
- ? h('label', null,
1700
+ ? h('div', { className: 'capital-automation-triggerfield' },
1651
1701
  h('span', null, zh.dayField),
1652
- h('select', {
1653
- value: trigger.dayOfMonth ?? 1,
1702
+ h(Select, {
1703
+ value: String(trigger.dayOfMonth ?? 1),
1704
+ options: Array.from({ length: 28 }, (_, index) => {
1705
+ const day = index + 1
1706
+ return { value: String(day), label: zh.dayOfMonth.replace('{n}', String(day)) }
1707
+ }),
1708
+ onChange: (day) => update({ dayOfMonth: Number(day) }),
1654
1709
  disabled,
1655
- onChange: (event) => update({ dayOfMonth: Number(event.target.value) }),
1656
- }, Array.from({ length: 28 }, (_, index) => index + 1).map((day) => h('option', { key: day, value: day }, zh.dayOfMonth.replace('{n}', String(day))))))
1710
+ label: zh.dayField,
1711
+ }))
1657
1712
  : null,
1658
1713
  trigger.cadence === 'daily' ? h('span', { className: 'capital-automation-hint' }, trigger.tradingDaysOnly === true ? zh.triggerHint : zh.triggerHintNatural) : null,
1659
1714
  h('label', { className: 'capital-automation-switch', style: { marginLeft: 'auto' } },
@@ -1685,6 +1740,67 @@ function Field(props) {
1685
1740
  hint ? h('span', { className: 'capital-automation-hint' }, hint) : null)
1686
1741
  }
1687
1742
 
1743
+ /**
1744
+ * DSH General-settings style selector: a filled pill plus the shared Menu surface.
1745
+ * Native <select> is intentionally avoided so light/dark themes follow host tokens.
1746
+ */
1747
+ function Select(props) {
1748
+ const { value, options, onChange, disabled, label, block, size, align = 'start' } = props
1749
+ const [open, setOpen] = useState(false)
1750
+ const triggerRef = useRef(null)
1751
+ const items = options.map((option) => ({
1752
+ id: String(option.value),
1753
+ label: option.label,
1754
+ disabled: option.disabled === true,
1755
+ }))
1756
+ const currentValue = String(value ?? '')
1757
+ const current = items.find((item) => item.id === currentValue) ?? items[0]
1758
+ useEffect(() => {
1759
+ if (disabled === true && open) setOpen(false)
1760
+ }, [disabled, open])
1761
+ const closeMenu = () => setOpen(false)
1762
+ const selectValue = (id) => {
1763
+ setOpen(false)
1764
+ triggerRef.current?.focus?.({ preventScroll: true })
1765
+ if (id !== currentValue) onChange(id)
1766
+ }
1767
+ const trigger = h('button', {
1768
+ ref: triggerRef,
1769
+ type: 'button',
1770
+ className: 'capital-automation-select',
1771
+ 'data-block': block === true ? 'true' : undefined,
1772
+ 'data-size': size,
1773
+ disabled,
1774
+ 'aria-label': current?.label ? `${label}:${current.label}` : label,
1775
+ 'aria-haspopup': 'menu',
1776
+ 'aria-expanded': open ? 'true' : 'false',
1777
+ onClick: () => setOpen((currentOpen) => !currentOpen),
1778
+ },
1779
+ h('span', {
1780
+ className: 'capital-automation-selectvalue',
1781
+ title: current?.label,
1782
+ }, current?.label ?? ''),
1783
+ h(IconChevronDownOutlineRegular, { size: size === 'sm' ? 12 : 14 }))
1784
+ return h(Menu, {
1785
+ open,
1786
+ anchor: trigger,
1787
+ items,
1788
+ selectedId: currentValue,
1789
+ onSelect: selectValue,
1790
+ onClose: closeMenu,
1791
+ align,
1792
+ portal: true,
1793
+ })
1794
+ }
1795
+
1796
+ function SelectField(props) {
1797
+ const { label, hint, ariaLabel, block = true, ...rest } = props
1798
+ return h('div', { className: 'capital-automation-field' },
1799
+ h('span', null, label),
1800
+ h(Select, { ...rest, label: ariaLabel ?? label, block }),
1801
+ hint ? h('span', { className: 'capital-automation-hint' }, hint) : null)
1802
+ }
1803
+
1688
1804
  function NumberField(props) {
1689
1805
  const { label, value, min, max, disabled, onChange } = props
1690
1806
  return h(Field, { label },
@@ -1755,53 +1871,46 @@ function RunsPage(props) {
1755
1871
  }
1756
1872
 
1757
1873
  function OverviewTab(props) {
1758
- const { detail, meta, onGoRuns } = props
1874
+ const { detail, meta, onGoRuns, onCancelRun, onOpenSession } = props
1759
1875
  const latest = detail.runs[0]
1760
- const next = detail.task.nextRunLocal ?? (detail.task.nextRunUnavailable ? zh.calendarUnavailable : zh.never)
1876
+ const next = nextRunView(detail.task)
1877
+ const last = lastRunView(latest, detail.task.trigger.timezone)
1761
1878
  const anyRunning = detail.runs.some((run) => isActiveRun(run))
1762
1879
  const stateText = anyRunning ? zh.status.running : stateLabel(detail.task.state)
1880
+ const originLabel = detail.task.origin === 'builtin' ? zh.origin.builtin : zh.origin.custom
1881
+ const scheduleText = `${scheduleRangeSummary(detail.task.trigger)}${detail.task.trigger.tradingDaysOnly === true ? ` · ${zh.tradingDaysField}` : ''}`
1882
+ const scheduleHint = detail.task.trigger.tradingDaysOnly === true ? zh.triggerHint : zh.triggerHintNatural
1763
1883
  return h('div', { className: 'capital-automation-overview' },
1764
1884
  h('div', { className: 'capital-automation-statgrid' },
1765
1885
  h('div', { className: 'capital-automation-stat' },
1766
1886
  h('span', { className: 'capital-automation-statlabel' }, zh.stateLabel),
1767
1887
  h('span', { className: 'capital-automation-statvalue', 'data-tone': anyRunning ? 'running' : undefined }, stateText),
1768
- h('span', { className: 'capital-automation-statsub' }, detail.task.origin === 'builtin' ? zh.origin.builtin : zh.origin.custom)),
1888
+ h('span', { className: 'capital-automation-statsub', title: detail.task.origin === 'builtin' ? zh.configBuiltinHint : undefined }, originLabel)),
1769
1889
  h('div', { className: 'capital-automation-stat' },
1770
1890
  h('span', { className: 'capital-automation-statlabel' }, zh.nextRun),
1771
- h('span', { className: 'capital-automation-statvalue' }, next),
1772
- h('span', { className: 'capital-automation-statsub' }, detail.task.nextRunAt ? formatRelative(detail.task.nextRunAt) : (detail.task.nextRunUnavailable ? zh.nextRunUnavailable : zh.never))),
1891
+ h('span', { className: 'capital-automation-statvalue' }, next.value),
1892
+ h('span', { className: 'capital-automation-statsub' }, next.sub)),
1773
1893
  h('div', { className: 'capital-automation-stat' },
1774
1894
  h('span', { className: 'capital-automation-statlabel' }, zh.lastResult),
1775
- h('span', { className: 'capital-automation-statvalue', 'data-tone': latest?.status === 'succeeded' ? 'ok' : latest && isFailureStatus(latest.status) ? 'error' : undefined }, latest ? statusLabel(latest.status) : zh.neverRun),
1776
- h('span', { className: 'capital-automation-statsub' }, latest ? `${formatShortTime(latest.queuedAt, detail.task.trigger.timezone)}${typeof latest.durationMs === 'number' ? ` · ${formatDuration(latest.durationMs)}` : ''}` : zh.never)),
1777
- h('div', { className: 'capital-automation-stat' },
1895
+ h('span', { className: 'capital-automation-statvalue', 'data-tone': last.tone }, last.value),
1896
+ h('span', { className: 'capital-automation-statsub' }, last.sub)),
1897
+ h('div', { className: 'capital-automation-stat', title: scheduleHint },
1778
1898
  h('span', { className: 'capital-automation-statlabel' }, zh.triggerSummary),
1779
- h('span', { className: 'capital-automation-statvalue' }, scheduleRangeSummary(detail.task.trigger)),
1899
+ h('span', { className: 'capital-automation-statvalue' }, scheduleText),
1780
1900
  h('span', { className: 'capital-automation-statsub' }, `${detail.task.trigger.time} · ${detail.task.trigger.timezone}`))),
1781
1901
  h('section', { className: 'capital-automation-card capital-automation-panel' },
1782
1902
  h('div', { className: 'capital-automation-panelhead' },
1783
- h('h3', null, zh.triggerSummary),
1784
- h('span', { className: 'capital-automation-hint' }, detail.task.trigger.tradingDaysOnly === true ? zh.triggerHint : zh.triggerHintNatural)),
1785
- h('div', { className: 'capital-automation-kv' },
1786
- h('div', { className: 'capital-automation-kvitem' },
1787
- h('span', { className: 'capital-automation-kvlabel' }, zh.triggerField),
1788
- h('span', { className: 'capital-automation-kvvalue' }, scheduleSummary(detail.task.trigger))),
1789
- h('div', { className: 'capital-automation-kvitem' },
1790
- h('span', { className: 'capital-automation-kvlabel' }, zh.timezoneField),
1791
- h('span', { className: 'capital-automation-kvvalue' }, detail.task.trigger.timezone)),
1792
- h('div', { className: 'capital-automation-kvitem' },
1793
- h('span', { className: 'capital-automation-kvlabel' }, zh.nextRun),
1794
- h('span', { className: 'capital-automation-kvvalue' }, `${next}${detail.task.nextRunAt ? ` · ${formatRelative(detail.task.nextRunAt)}` : ''}`)),
1795
- h('div', { className: 'capital-automation-kvitem' },
1796
- h('span', { className: 'capital-automation-kvlabel' }, zh.stateLabel),
1797
- h('span', { className: 'capital-automation-kvvalue' }, `${stateText}${detail.task.trigger.tradingDaysOnly ? ` · ${zh.tradingDaysField}` : ''}`)))),
1798
- h('section', { className: 'capital-automation-card capital-automation-panel' },
1799
- h('div', { className: 'capital-automation-panelhead' },
1800
- h('h3', null, zh.detailRecentRuns),
1903
+ h('h3', null, zh.detailRecentRuns.replace('{n}', String(DETAIL_RECENT_RUN_LIMIT))),
1801
1904
  h('button', { type: 'button', className: 'capital-automation-btn', 'data-size': 'sm', 'data-variant': 'ghost', onClick: onGoRuns },
1802
1905
  zh.detailAllRuns,
1803
1906
  h(IconChevronRightOutlineRegular, { size: 13 }))),
1804
- h(RunList, { runs: detail.runs.slice(0, 3), tasks: [detail.task], compact: true })),
1907
+ h(RunList, {
1908
+ runs: detail.runs.slice(0, DETAIL_RECENT_RUN_LIMIT),
1909
+ tasks: [detail.task],
1910
+ onCancel: onCancelRun,
1911
+ onOpenSession,
1912
+ compact: true,
1913
+ })),
1805
1914
  h('section', { className: 'capital-automation-card capital-automation-panel' },
1806
1915
  h('div', { className: 'capital-automation-panelhead' },
1807
1916
  h('h3', null, zh.environment)),
@@ -1818,26 +1927,21 @@ function OverviewTab(props) {
1818
1927
  h('div', { className: 'capital-automation-kvitem' },
1819
1928
  h('span', { className: 'capital-automation-kvlabel' }, zh.modeField),
1820
1929
  h('span', { className: 'capital-automation-kvvalue' }, modeLabel(detail.task.mode)))),
1821
- h('div', { className: 'capital-automation-policy', style: { marginTop: 14 } },
1822
- h('p', null, zh.policyText),
1823
- h('p', null, zh.budgetHint)),
1824
- h('details', { className: 'capital-automation-technical' },
1825
- h('summary', null, zh.techInfo),
1930
+ h('section', { className: 'capital-automation-technical' },
1931
+ h('h4', { className: 'capital-automation-technicaltitle' }, zh.techInfo),
1826
1932
  h('dl', null,
1827
1933
  h('dt', null, zh.profileKey),
1828
- h('dd', null, meta?.profile?.key ?? '—'),
1934
+ h('dd', { title: 'profileKey 是 profile 名称的稳定哈希;与 session 标题里的 run id 无关。' }, meta?.profile?.key ?? '—'),
1829
1935
  h('dt', null, zh.presetLabel),
1830
1936
  h('dd', null, zh.presetValue),
1831
1937
  h('dt', null, zh.policyField),
1832
1938
  h('dd', null, meta?.execution?.policy ?? 'automation-v1'),
1833
1939
  h('dt', null, zh.cwdKind),
1834
- h('dd', null, meta?.execution?.cwdKind ?? 'profile'),
1835
- h('dt', null, 'profile'),
1940
+ h('dd', { title: '当前固定为 profile(专属自动化工作区);没有 storage 等其他来源。' }, meta?.execution?.cwdKind ?? 'profile'),
1941
+ h('dt', null, 'profile 名称'),
1836
1942
  h('dd', null, meta?.profile?.name ?? '—'),
1837
1943
  h('dt', null, zh.calendarField),
1838
- h('dd', null, calendarLabel(meta?.calendar)),
1839
- h('dt', null, zh.version),
1840
- h('dd', null, meta?.version ? `v${meta.version}` : '—')))))
1944
+ h('dd', null, calendarLabel(meta?.calendar))))))
1841
1945
  }
1842
1946
 
1843
1947
  function ConfigTab(props) {
@@ -1855,18 +1959,21 @@ function ConfigTab(props) {
1855
1959
  h('summary', null, zh.advanced),
1856
1960
  h('div', { className: 'capital-automation-advancedbody' },
1857
1961
  h('div', { className: 'capital-automation-formgrid' },
1858
- h(Field, { label: zh.workspaceField },
1859
- h('select', {
1860
- value: task.workspaceId ?? '',
1861
- disabled: busy || disabled,
1862
- onChange: (event) => set({ workspaceId: event.target.value }),
1863
- }, workspaceOptions(meta, task.workspaceId).map((option) => h('option', { key: option.id, value: option.id }, option.title)))),
1864
- h(Field, { label: zh.modeField, hint: zh.modeHint },
1865
- h('select', {
1866
- value: task.mode,
1867
- disabled: busy || disabled,
1868
- onChange: (event) => set({ mode: event.target.value }),
1869
- }, Object.entries(zh.mode).map(([value, label]) => h('option', { key: value, value }, label))))),
1962
+ h(SelectField, {
1963
+ label: zh.workspaceField,
1964
+ value: task.workspaceId ?? '',
1965
+ options: workspaceOptions(meta, task.workspaceId).map((option) => ({ value: option.id, label: option.title })),
1966
+ disabled: busy || disabled,
1967
+ onChange: (workspaceId) => set({ workspaceId }),
1968
+ }),
1969
+ h(SelectField, {
1970
+ label: zh.modeField,
1971
+ hint: zh.modeHint,
1972
+ value: task.mode,
1973
+ options: Object.entries(zh.mode).map(([value, label]) => ({ value, label })),
1974
+ disabled: busy || disabled,
1975
+ onChange: (mode) => set({ mode }),
1976
+ })),
1870
1977
  h('div', { className: 'capital-automation-formgrid' },
1871
1978
  h(NumberField, { label: zh.runTimeout, value: task.limits.runTimeoutMinutes, min: timeoutRange[0], max: timeoutRange[1], disabled: busy || disabled, onChange: (value) => set((current) => ({ limits: { ...current.limits, runTimeoutMinutes: value } })) }),
1872
1979
  h(NumberField, { label: zh.idleTimeout, value: task.limits.idleTimeoutMinutes, min: idleRange[0], max: idleRange[1], disabled: busy || disabled, onChange: (value) => set((current) => ({ limits: { ...current.limits, idleTimeoutMinutes: value } })) }),
@@ -1882,16 +1989,23 @@ function ConfigTab(props) {
1882
1989
  h(SwitchRow, { label: zh.notifySuccess, checked: task.notifications.onSuccess, disabled: busy || disabled, onChange: (value) => set((current) => ({ notifications: { ...current.notifications, onSuccess: value } })) })),
1883
1990
  h('p', { className: 'capital-automation-hint' }, zh.notifyHint))
1884
1991
  return h('div', { className: 'capital-automation-form' },
1992
+ task.origin === 'builtin'
1993
+ ? h('div', { className: 'capital-automation-formnote' },
1994
+ h(IconInfoOutlineRegular, { size: 13 }),
1995
+ h('span', null, zh.configBuiltinHint))
1996
+ : null,
1885
1997
  h(Field, { label: zh.titleField },
1886
1998
  h('input', { value: task.title, disabled: busy || disabled, onChange: (event) => set({ title: event.target.value }) })),
1887
1999
  h(Field, { label: zh.descriptionField },
1888
2000
  h('textarea', { value: task.description ?? '', disabled: busy || disabled, rows: 3, onChange: (event) => set({ description: event.target.value }) })),
1889
- h(Field, { label: zh.categoryField, hint: task.origin === 'builtin' ? zh.categoryFixed : undefined },
1890
- h('select', {
1891
- value: task.category,
1892
- disabled: busy || disabled || task.origin === 'builtin',
1893
- onChange: (event) => set({ category: event.target.value }),
1894
- }, Object.entries(zh.category).map(([value, label]) => h('option', { key: value, value }, label)))),
2001
+ h(SelectField, {
2002
+ label: zh.categoryField,
2003
+ hint: task.origin === 'builtin' ? zh.categoryFixed : undefined,
2004
+ value: task.category,
2005
+ options: Object.entries(zh.category).map(([value, label]) => ({ value, label })),
2006
+ disabled: busy || disabled || task.origin === 'builtin',
2007
+ onChange: (category) => set({ category }),
2008
+ }),
1895
2009
  h(Field, { label: zh.instructionField },
1896
2010
  h('textarea', {
1897
2011
  value: task.instruction,
@@ -1957,7 +2071,7 @@ function DetailBar(props) {
1957
2071
  }
1958
2072
 
1959
2073
  function TaskDetailPage(props) {
1960
- const { detail, meta, api, onChanged, openSession, tab, onTab, onRequestConfirm, onRequestCancelRun, onOpenAllRuns, schedulerActive, storageReady } = props
2074
+ const { detail, meta, api, onChanged, openSession, tab, onTab, onRequestConfirm, onRequestCancelRun, schedulerActive, storageReady } = props
1961
2075
  const [draft, setDraft] = useState(null)
1962
2076
  const [busy, setBusy] = useState('')
1963
2077
  const [notice, setNotice] = useState('')
@@ -2063,7 +2177,7 @@ function TaskDetailPage(props) {
2063
2177
  disabled: busy || !canRun,
2064
2178
  title: schedulerActive === false ? zh.schedulerBlocked : running ? zh.runBlocked : undefined,
2065
2179
  onClick: () => { void runNow() },
2066
- }, zh.runNow)
2180
+ }, h(IconPlayOutlineRegular, { size: 14 }), zh.runNow)
2067
2181
 
2068
2182
  const resetDraft = () => {
2069
2183
  setDraft({
@@ -2084,13 +2198,7 @@ function TaskDetailPage(props) {
2084
2198
  h('h2', null, detail.task.title),
2085
2199
  h(StateBadge, { state: detail.task.state, running }),
2086
2200
  unread > 0 ? h('span', { className: 'capital-automation-count', title: zh.unread }, String(unread)) : null),
2087
- h('p', { className: 'capital-automation-herodesc' }, detail.task.description || zh.subtitle),
2088
- h('div', { className: 'capital-automation-herometa' },
2089
- h('span', null, h(IconClockOutlineRegular, { size: 13 }), scheduleSummary(detail.task.trigger)),
2090
- h('span', null, `${zh.nextRun} ${detail.task.nextRunLocal ?? (detail.task.nextRunUnavailable ? zh.calendarUnavailable : zh.never)}`),
2091
- h('span', null, `${zh.lastResult} ${detail.runs[0] ? statusLabel(detail.runs[0].status) : zh.never}`),
2092
- h('span', null, categoryLabel(detail.task.category)),
2093
- detail.task.origin === 'builtin' ? h('span', { className: 'capital-automation-tag' }, zh.origin.builtin) : h('span', { className: 'capital-automation-tag', 'data-tone': 'custom' }, zh.origin.custom))),
2201
+ h('p', { className: 'capital-automation-herodesc' }, detail.task.description || zh.subtitle)),
2094
2202
  h('div', { className: 'capital-automation-heroactions' },
2095
2203
  runButton,
2096
2204
  running
@@ -2101,7 +2209,7 @@ function TaskDetailPage(props) {
2101
2209
  'data-variant': detail.task.state === 'active' ? undefined : 'primary',
2102
2210
  disabled: busy || !storageReadyNow,
2103
2211
  onClick: () => { void toggleState() },
2104
- }, activationLabel(detail.task)),
2212
+ }, h(activationIcon(detail.task), { size: 14 }), activationLabel(detail.task)),
2105
2213
  h(TaskActionsMenu, {
2106
2214
  task: detail.task,
2107
2215
  open: menuOpen,
@@ -2135,9 +2243,14 @@ function TaskDetailPage(props) {
2135
2243
  error.length > 0 ? h('p', { className: 'capital-automation-error', role: 'alert' }, error) : null)
2136
2244
  : null,
2137
2245
  running ? h('p', { className: 'capital-automation-hint', style: { paddingTop: 10 } }, zh.configWhileRunning) : null,
2138
- detail.task.origin === 'builtin' ? h('p', { className: 'capital-automation-hint', style: { paddingTop: 8 } }, zh.configBuiltinHint) : null,
2139
2246
  h('div', { className: 'capital-automation-detailbody' },
2140
- tab === 'overview' ? h(OverviewTab, { detail, meta, onGoRuns: onOpenAllRuns }) : null,
2247
+ tab === 'overview' ? h(OverviewTab, {
2248
+ detail,
2249
+ meta,
2250
+ onGoRuns: () => onTab('runs'),
2251
+ onCancelRun: onRequestCancelRun,
2252
+ onOpenSession: openSession,
2253
+ }) : null,
2141
2254
  tab === 'config' ? h(ConfigTab, {
2142
2255
  draft,
2143
2256
  setDraft,
@@ -2244,25 +2357,37 @@ function NewTaskModal(props) {
2244
2357
  onChange: (event) => set({ instruction: event.target.value }),
2245
2358
  }),
2246
2359
  h('div', { className: 'capital-automation-prompttoolbar' },
2247
- h('label', null,
2360
+ h('div', { className: 'capital-automation-toolbarfield' },
2248
2361
  h(IconFolderOpenOutlineRegular, { size: 13 }),
2249
2362
  h('span', null, zh.workspaceField),
2250
- h('select', {
2363
+ h(Select, {
2251
2364
  value: draft.workspaceId ?? '',
2252
- onChange: (event) => set({ workspaceId: event.target.value }),
2253
- }, workspaceOptions(meta, draft.workspaceId).map((option) => h('option', { key: option.id, value: option.id }, option.title)))),
2254
- h('label', null,
2365
+ options: workspaceOptions(meta, draft.workspaceId).map((option) => ({ value: option.id, label: option.title })),
2366
+ onChange: (workspaceId) => set({ workspaceId }),
2367
+ label: zh.workspaceField,
2368
+ size: 'sm',
2369
+ align: 'end',
2370
+ })),
2371
+ h('div', { className: 'capital-automation-toolbarfield' },
2255
2372
  h('span', null, zh.categoryField),
2256
- h('select', {
2373
+ h(Select, {
2257
2374
  value: draft.category,
2258
- onChange: (event) => set({ category: event.target.value }),
2259
- }, Object.entries(zh.category).map(([value, label]) => h('option', { key: value, value }, label)))),
2260
- h('label', null,
2375
+ options: Object.entries(zh.category).map(([value, label]) => ({ value, label })),
2376
+ onChange: (category) => set({ category }),
2377
+ label: zh.categoryField,
2378
+ size: 'sm',
2379
+ align: 'end',
2380
+ })),
2381
+ h('div', { className: 'capital-automation-toolbarfield' },
2261
2382
  h('span', null, zh.modeField),
2262
- h('select', {
2383
+ h(Select, {
2263
2384
  value: draft.mode,
2264
- onChange: (event) => set({ mode: event.target.value }),
2265
- }, Object.entries(zh.mode).map(([value, label]) => h('option', { key: value, value }, label)))),
2385
+ options: Object.entries(zh.mode).map(([value, label]) => ({ value, label })),
2386
+ onChange: (mode) => set({ mode }),
2387
+ label: zh.modeField,
2388
+ size: 'sm',
2389
+ align: 'end',
2390
+ })),
2266
2391
  h('span', { className: 'capital-automation-presetchip', title: zh.modeHint },
2267
2392
  h(IconSparkleRegular, { size: 12 }),
2268
2393
  zh.presetValue)))),
@@ -2631,7 +2756,6 @@ function AutomationPage(props) {
2631
2756
  onTab: setDetailTab,
2632
2757
  onRequestConfirm: requestConfirm,
2633
2758
  onRequestCancelRun: (run) => requestConfirm('cancel-run', selectedTask ?? { id: run.taskId, title: run.taskTitle }, run),
2634
- onOpenAllRuns: () => switchTab('runs'),
2635
2759
  schedulerActive,
2636
2760
  storageReady,
2637
2761
  onChanged: async (taskId) => {