diff --git a/src/renderer/src/components/activity/ActivityThreadOptionsMenu.test.tsx b/src/renderer/src/components/activity/ActivityThreadOptionsMenu.test.tsx
index c490b4adedd..e55a0d24331 100644
--- a/src/renderer/src/components/activity/ActivityThreadOptionsMenu.test.tsx
+++ b/src/renderer/src/components/activity/ActivityThreadOptionsMenu.test.tsx
@@ -5,6 +5,7 @@ import { createRoot, type Root } from 'react-dom/client'
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
import { TooltipProvider } from '@/components/ui/tooltip'
+import { useAppStore } from '@/store'
import { ActivityThreadOptionsMenu } from './ActivityPrototypePage'
import type { ActivityGroupBy } from './activity-thread-types'
@@ -46,6 +47,7 @@ describe('ActivityThreadOptionsMenu', () => {
let root: Root
beforeEach(() => {
+ useAppStore.setState({ agentsVisibleHostIds: null, agentsFilterRepoIds: [] })
container = document.createElement('div')
document.body.appendChild(container)
root = createRoot(container)
@@ -57,6 +59,20 @@ describe('ActivityThreadOptionsMenu', () => {
document.body.replaceChildren()
})
+ it('exposes an active persisted scope visually and in the trigger label', async () => {
+ useAppStore.setState({ agentsVisibleHostIds: ['local'] })
+
+ await act(async () => {
+ root.render()
+ })
+
+ const trigger = container.querySelector(
+ 'button[aria-label="Thread list options, filters active"]'
+ )
+ expect(trigger).not.toBeNull()
+ expect(trigger?.querySelector('[data-scope-filter-dot]')).not.toBeNull()
+ })
+
it('opens without recursively updating composed Radix trigger refs', async () => {
await act(async () => {
root.render()
@@ -191,7 +207,7 @@ describe('ActivityThreadOptionsMenu', () => {
expect(document.body.textContent).toContain(
'Filters the activity list to show only threads with unread updates.'
)
- expect(unreadItem?.querySelector('svg.lucide-check')).toBeNull()
+ expect(document.querySelector('[data-unread-dot]')).toBeNull()
})
it('renders show child agents checkbox when onShowChildAgentsChange is provided', async () => {
diff --git a/src/renderer/src/components/activity/activity-clear-completed.ts b/src/renderer/src/components/activity/activity-clear-completed.ts
index c77425f6148..79b69e96ea9 100644
--- a/src/renderer/src/components/activity/activity-clear-completed.ts
+++ b/src/renderer/src/components/activity/activity-clear-completed.ts
@@ -96,9 +96,7 @@ function evictPersistedStatuses(identities: readonly AgentStatusCacheIdentity[])
}
}
-/**
- * Clear a single activity thread silently (no toast notification).
- */
+/** Clear one completed thread immediately; bulk clearing owns the undo toast. */
export function clearActivityThread(thread: AgentPaneThread): boolean {
const state = useAppStore.getState()
const plan = planClearCompletedActivity([thread], state)
diff --git a/src/renderer/src/components/activity/activity-thread-hover-card.test.tsx b/src/renderer/src/components/activity/activity-thread-hover-card.test.tsx
index e075930d387..ef1f109d50a 100644
--- a/src/renderer/src/components/activity/activity-thread-hover-card.test.tsx
+++ b/src/renderer/src/components/activity/activity-thread-hover-card.test.tsx
@@ -165,6 +165,8 @@ describe('ActivityThreadHoverCard and ActivityThreadRow', () => {
)
expect(clearButton).not.toBeNull()
expect(clearButton?.className).toContain('focus-visible:opacity-100')
+ expect(clearButton?.className).toContain('can-hover:pointer-events-none')
+ expect(clearButton?.className).toContain('can-hover:group-hover:pointer-events-auto')
expect(clearButton?.className).not.toContain('invisible')
act(() => clearButton?.click())
diff --git a/src/renderer/src/components/activity/activity-thread-options-menu.tsx b/src/renderer/src/components/activity/activity-thread-options-menu.tsx
index c1968fe8489..f9273d15678 100644
--- a/src/renderer/src/components/activity/activity-thread-options-menu.tsx
+++ b/src/renderer/src/components/activity/activity-thread-options-menu.tsx
@@ -80,6 +80,12 @@ export function ActivityThreadOptionsMenu({
}): React.JSX.Element {
const skipCloseAutoFocusRef = React.useRef(false)
const scopeFilterActive = useActivityScopeFilterActive()
+ const optionsLabel = scopeFilterActive
+ ? translate(
+ 'auto.components.activity.ActivityPrototypePage.threadListOptionsFiltered',
+ 'Thread list options, filters active'
+ )
+ : translate('auto.components.activity.ActivityPrototypePage.db8a1878b5', 'Thread list options')
return (
@@ -93,10 +99,7 @@ export function ActivityThreadOptionsMenu({
variant="ghost"
size="icon-xs"
className="relative text-muted-foreground"
- aria-label={translate(
- 'auto.components.activity.ActivityPrototypePage.db8a1878b5',
- 'Thread list options'
- )}
+ aria-label={optionsLabel}
>
{scopeFilterActive ? (
diff --git a/src/renderer/src/components/activity/activity-thread-row.tsx b/src/renderer/src/components/activity/activity-thread-row.tsx
index b62e50fefd7..918c27cb42c 100644
--- a/src/renderer/src/components/activity/activity-thread-row.tsx
+++ b/src/renderer/src/components/activity/activity-thread-row.tsx
@@ -29,7 +29,7 @@ function ActivityThreadRowAction({
type="button"
variant="ghost"
size="icon-xs"
- className="size-4 p-0 text-muted-foreground opacity-100 transition-opacity hover:text-foreground can-hover:opacity-0 group-hover:opacity-100 focus-visible:opacity-100"
+ className="size-4 p-0 text-muted-foreground transition-opacity hover:text-foreground can-hover:pointer-events-none can-hover:opacity-0 can-hover:group-hover:pointer-events-auto can-hover:group-hover:opacity-100 focus-visible:opacity-100"
aria-label={label}
onClick={(event) => {
event.stopPropagation()
diff --git a/src/renderer/src/i18n/locales/en.json b/src/renderer/src/i18n/locales/en.json
index 3b68ce5575c..4f790240e23 100644
--- a/src/renderer/src/i18n/locales/en.json
+++ b/src/renderer/src/i18n/locales/en.json
@@ -16161,7 +16161,8 @@
"search": "Search",
"showUnreadOnly": "Show unread only",
"showChildAgents": "Show child agents",
- "activityOptions": "Activity options"
+ "activityOptions": "Activity options",
+ "threadListOptionsFiltered": "Thread list options, filters active"
},
"clearCompleted": {
"clearedOne": "Cleared 1 completed agent",
diff --git a/src/renderer/src/i18n/locales/es.json b/src/renderer/src/i18n/locales/es.json
index 98b07917880..8dc3b872765 100644
--- a/src/renderer/src/i18n/locales/es.json
+++ b/src/renderer/src/i18n/locales/es.json
@@ -14180,6 +14180,7 @@
"770d458144": "Agrupar por",
"795cbf26e2": "Filtrar...",
"4616ea39fd": "Ir al workspace",
+ "threadListOptionsFiltered": "Opciones de lista de hilos, filtros activos",
"markThreadRead": "Marcar hilo como leído",
"59b131fbd9": "Marcar hilo como no leído",
"beb2c19173": "No leído",
@@ -14187,6 +14188,9 @@
"22b22034bc": "Terminal independiente no disponible en Actividad.",
"afdc2139a8": "Terminal de Agent cerrada. Abre una nueva terminal en este workspace para continuar."
},
+ "ActivityThreadRow": {
+ "clearNotification": "Borrar notificación"
+ },
"ActivityTitlebarControls": {
"f915168c8e": "no leído",
"d6a8de3934": "agentes",
diff --git a/src/renderer/src/i18n/locales/fr.json b/src/renderer/src/i18n/locales/fr.json
index 5bf316d36f7..bbc7490d344 100644
--- a/src/renderer/src/i18n/locales/fr.json
+++ b/src/renderer/src/i18n/locales/fr.json
@@ -15462,12 +15462,16 @@
"770d458144": "Grouper l'activité des agents par",
"795cbf26e2": "Filtrer...",
"4616ea39fd": "Aller à l'espace de travail",
+ "threadListOptionsFiltered": "Options de la liste des fils, filtres actifs",
"59b131fbd9": "Marquer le fil comme non lu",
"beb2c19173": "Non lus",
"5651b216c6": "Projet inconnu",
"22b22034bc": "Terminal autonome indisponible dans Activité.",
"afdc2139a8": "Terminal de l'agent fermé. Ouvrez un nouveau terminal dans cet espace de travail pour continuer."
},
+ "ActivityThreadRow": {
+ "clearNotification": "Effacer la notification"
+ },
"ActivityTitlebarControls": {
"f915168c8e": "non lu",
"d6a8de3934": "agents",
diff --git a/src/renderer/src/i18n/locales/ja.json b/src/renderer/src/i18n/locales/ja.json
index a703b7ad614..f71ae4764c6 100644
--- a/src/renderer/src/i18n/locales/ja.json
+++ b/src/renderer/src/i18n/locales/ja.json
@@ -14180,6 +14180,7 @@
"770d458144": "グループ化",
"795cbf26e2": "フィルター…",
"4616ea39fd": "ワークスペースにジャンプ",
+ "threadListOptionsFiltered": "スレッドリストのオプション、フィルター適用中",
"markThreadRead": "スレッドを既読としてマーク",
"59b131fbd9": "スレッドを未読としてマークする",
"beb2c19173": "未読",
@@ -14187,6 +14188,9 @@
"22b22034bc": "スタンドアロンターミナルはアクティビティでは使用できません。",
"afdc2139a8": "Agent ターミナルが閉じられました。続行するには、このワークスペースで新規ターミナルを開いてください。"
},
+ "ActivityThreadRow": {
+ "clearNotification": "通知を消去"
+ },
"ActivityTitlebarControls": {
"f915168c8e": "未読",
"d6a8de3934": "Agent",
diff --git a/src/renderer/src/i18n/locales/ko.json b/src/renderer/src/i18n/locales/ko.json
index d9d822b8fc3..29353604c4b 100644
--- a/src/renderer/src/i18n/locales/ko.json
+++ b/src/renderer/src/i18n/locales/ko.json
@@ -14258,6 +14258,7 @@
"770d458144": "그룹화 기준",
"795cbf26e2": "필터...",
"4616ea39fd": "워크스페이스로 이동",
+ "threadListOptionsFiltered": "스레드 목록 옵션, 필터 활성화됨",
"markThreadRead": "스레드를 읽은 것으로 표시",
"59b131fbd9": "스레드를 읽지 않은 것으로 표시",
"beb2c19173": "읽지 않음",
@@ -14265,6 +14266,9 @@
"22b22034bc": "활동에서는 독립형 terminal을 사용할 수 없습니다.",
"afdc2139a8": "Agent terminal이 닫혔습니다. 계속하려면 이 워크스페이스에서 새 terminal을 여세요."
},
+ "ActivityThreadRow": {
+ "clearNotification": "알림 지우기"
+ },
"ActivityTitlebarControls": {
"f915168c8e": "읽지 않음",
"d6a8de3934": "에이전트",
diff --git a/src/renderer/src/i18n/locales/zh.json b/src/renderer/src/i18n/locales/zh.json
index 46dc592dd2c..ef4c62f5797 100644
--- a/src/renderer/src/i18n/locales/zh.json
+++ b/src/renderer/src/i18n/locales/zh.json
@@ -14236,6 +14236,7 @@
"770d458144": "分组方式",
"795cbf26e2": "筛选...",
"4616ea39fd": "跳转到工作区",
+ "threadListOptionsFiltered": "线程列表选项,筛选器已启用",
"markThreadRead": "将话题标记为已读",
"59b131fbd9": "将话题标记为未读",
"beb2c19173": "未读",
@@ -14243,6 +14244,9 @@
"22b22034bc": "独立终端在活动中不可用。",
"afdc2139a8": "智能体终端关闭。在此工作区中打开一个新终端以继续。"
},
+ "ActivityThreadRow": {
+ "clearNotification": "清除通知"
+ },
"ActivityTitlebarControls": {
"f915168c8e": "未读",
"d6a8de3934": "智能体",