diff --git a/packages/client/ui-activity/src/client/ActivityListAction.module.css b/packages/client/ui-activity/src/client/ActivityListAction.module.css
index 404c77e5b9..90ecd4d70e 100644
--- a/packages/client/ui-activity/src/client/ActivityListAction.module.css
+++ b/packages/client/ui-activity/src/client/ActivityListAction.module.css
@@ -119,15 +119,26 @@
color: var(--dsw-alias-label-secondary);
}
+/* The armed press widens into a labeled danger pill so the confirm step is
+ legible without hover; the state-error token is the one StateDot uses. */
.stopArmed,
.stopArmed:hover,
.stopArmed:focus-visible {
- background: var(--dsw-alias-fill-l1);
- color: var(--dsw-alias-label-error, var(--dsw-alias-label-secondary));
+ gap: 4px;
+ width: auto;
+ padding: 0 8px;
+ background: color-mix(in srgb, var(--dsw-alias-state-error-primary) 12%, transparent);
+ color: var(--dsw-alias-state-error-primary);
+}
+
+.stopLabel {
+ font-size: 11px;
+ line-height: 18px;
+ white-space: nowrap;
}
.stopFailed {
- color: var(--dsw-alias-label-error, var(--dsw-alias-label-secondary));
+ color: var(--dsw-alias-state-error-primary);
}
.row {
@@ -268,5 +279,5 @@
}
.noticeError {
- color: var(--dsw-alias-label-error, var(--dsw-alias-label-secondary));
+ color: var(--dsw-alias-state-error-primary);
}
diff --git a/packages/client/ui-activity/src/client/ActivityListAction.tsx b/packages/client/ui-activity/src/client/ActivityListAction.tsx
index 44e1887c52..b728d2656e 100644
--- a/packages/client/ui-activity/src/client/ActivityListAction.tsx
+++ b/packages/client/ui-activity/src/client/ActivityListAction.tsx
@@ -329,6 +329,9 @@ function TaskItem({ row, view, expanded, now, onToggle, kill, t }: {
onClick={kill.onPress}
>
+ {/* The armed press must be legible without hover: the button
+ widens into a labeled confirm pill instead of a tint only. */}
+ {kill.state === 'armed' ? {t('kill.confirmAction')} : null}
)
: null}
diff --git a/packages/client/ui-activity/src/client/locales.ts b/packages/client/ui-activity/src/client/locales.ts
index c798b775a4..2a673f27c6 100644
--- a/packages/client/ui-activity/src/client/locales.ts
+++ b/packages/client/ui-activity/src/client/locales.ts
@@ -16,6 +16,7 @@ export const zh = {
'row.collapseAria': '收起 {label} 的实时输出',
'kill.stop': '停止任务 {label}',
'kill.confirm': '再次点击确认停止',
+ 'kill.confirmAction': '确认停止',
'kill.failed': '停止失败',
'status.running': '运行中',
'status.stopping': '正在停止',
@@ -56,6 +57,7 @@ export const en: Record = {
'row.collapseAria': 'Hide live output of {label}',
'kill.stop': 'Stop task {label}',
'kill.confirm': 'Click again to confirm',
+ 'kill.confirmAction': 'Confirm stop',
'kill.failed': 'Stop failed',
'status.running': 'running',
'status.stopping': 'stopping',
diff --git a/packages/client/ui-activity/tests/activity-list-action.client.spec.tsx b/packages/client/ui-activity/tests/activity-list-action.client.spec.tsx
index ae4c8745fe..7a5572a43d 100644
--- a/packages/client/ui-activity/tests/activity-list-action.client.spec.tsx
+++ b/packages/client/ui-activity/tests/activity-list-action.client.spec.tsx
@@ -405,6 +405,8 @@ describe('ActivityListAction human kill', () => {
expect(killJob).not.toHaveBeenCalled()
expect(stop.getAttribute('data-kill-state')).toBe('armed')
expect(screen.getByTitle(zh['kill.confirm'])).toBe(stop)
+ // The armed step is legible without hover: the button carries the label.
+ expect(stop.textContent).toBe(zh['kill.confirmAction'])
await act(async () => { fireEvent.click(stop) })
expect(killJob).toHaveBeenCalledWith(SESSION, 'bash-1')
// An admitted kill stays pending: the unary response and the jobs frames