From bfdd5fa0543a69a01ebdf164d1b7a07f35dd0122 Mon Sep 17 00:00:00 2001 From: Jinjing <6427696+AmethystLiang@users.noreply.github.com> Date: Fri, 4 Sep 2026 13:40:15 -0700 Subject: [PATCH] refactor(activity): indicate active scope filters Display visual indicator and updated aria-label in the options menu when scope filters are applied. Improves visibility of the current filter state and enhances pointer event handling for action buttons. --- .../ActivityThreadOptionsMenu.test.tsx | 18 +++++++++++++++++- .../activity/activity-clear-completed.ts | 4 +--- .../activity-thread-hover-card.test.tsx | 2 ++ .../activity/activity-thread-options-menu.tsx | 11 +++++++---- .../activity/activity-thread-row.tsx | 2 +- src/renderer/src/i18n/locales/en.json | 3 ++- src/renderer/src/i18n/locales/es.json | 4 ++++ src/renderer/src/i18n/locales/fr.json | 4 ++++ src/renderer/src/i18n/locales/ja.json | 4 ++++ src/renderer/src/i18n/locales/ko.json | 4 ++++ src/renderer/src/i18n/locales/zh.json | 4 ++++ 11 files changed, 50 insertions(+), 10 deletions(-) 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": "智能体",