fix: simplify task center status selection and rendering
This commit is contained in:
@@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
[当前会话统计 Demo](./hierarchy-demo.html) 用于评审任务中心中的紧凑统计区:上方显示当前会话的消息数与 Agent 累计回复时长,下方管理当前项目全部会话的任务及用时。切换会话只更新会话统计,任务范围固定为当前项目,没有全局或所有项目入口。支持状态筛选、定位所属会话,以及暂停、继续和取消任务的模拟操作。新建任务支持填写内容、关联本项目已有或新建会话、立即或定时执行;仅在页面内模拟,刷新后恢复样例。
|
[当前会话统计 Demo](./hierarchy-demo.html) 用于评审任务中心中的紧凑统计区:上方显示当前会话的消息数与 Agent 累计回复时长,下方管理当前项目全部会话的任务及用时。切换会话只更新会话统计,任务范围固定为当前项目,没有全局或所有项目入口。支持状态筛选、定位所属会话,以及暂停、继续和取消任务的模拟操作。新建任务支持填写内容、关联本项目已有或新建会话、立即或定时执行;仅在页面内模拟,刷新后恢复样例。
|
||||||
配套 `hierarchy-demo.css` 与 `hierarchy-demo.js` 仅服务于该静态原型;使用演示数据,未接入产品。
|
配套 `hierarchy-demo.css` 与 `hierarchy-demo.js` 仅服务于该静态原型;使用演示数据,未接入产品。
|
||||||
项目任务标题右侧依次为 `+` 新建按钮与筛选图标,状态筛选默认收起;应用筛选后图标高亮并显示当前筛选条件。
|
应用中的项目任务标题右侧仅保留 `+` 新建按钮;展开列表后直接显示状态选项,默认选中“进行中”,收起后保留所选状态。静态 Demo 的筛选入口仍为旧交互。
|
||||||
时间为固定样例,Demo 从每次回复的起止秒数计算。正式功能按请求有效区间累计,包含轮内工具和审批等待、不计轮间空闲;任务用时按 `schedule_runs` 关联请求汇总,不按父任务生命周期计算。缺失历史有提示,远程缺少可靠证据时不推算用时,数据来源与边界以技术设计为准。
|
时间为固定样例,Demo 从每次回复的起止秒数计算。正式功能按请求有效区间累计,包含轮内工具和审批等待、不计轮间空闲;任务用时按 `schedule_runs` 关联请求汇总,不按父任务生命周期计算。缺失历史有提示,远程缺少可靠证据时不推算用时,数据来源与边界以技术设计为准。
|
||||||
|
|
||||||
先阅读统一领域模型。其他功能文档不得重新定义 Task、Conversation、Job、Run 或 Subagent。
|
先阅读统一领域模型。其他功能文档不得重新定义 Task、Conversation、Job、Run 或 Subagent。
|
||||||
|
|||||||
@@ -77,10 +77,10 @@ FR-TC3:任务累计执行时间按其历次执行请求汇总,周期触发
|
|||||||
## 5. 交互
|
## 5. 交互
|
||||||
|
|
||||||
- 点击条目打开关联 Conversation,并定位到该 Task。
|
- 点击条目打开关联 Conversation,并定位到该 Task。
|
||||||
- FR-TC4:项目任务标题右侧依次放置“+”新建按钮和筛选图标。状态筛选默认收起、默认全部,
|
- FR-TC4:项目任务标题右侧放置“+”新建按钮,不再提供独立筛选图标。
|
||||||
项目任务列表默认收起,标题与数量、新建及筛选入口保持可见;点击标题切换展开状态,点击筛选同时展开列表。
|
项目任务列表默认收起,标题与数量、新建入口保持可见;点击标题切换展开状态。
|
||||||
标题旁同时显示当前项目顶层任务中处于 `running` 状态的数量(如“2 运行中”),不受筛选或收起影响;为零时省略。
|
标题旁同时显示当前项目顶层任务中处于 `running` 状态的数量(如“2 运行中”),不受筛选或收起影响;为零时省略。
|
||||||
展开后提供全部、进行中、暂停、已结束;收起不清除已选条件,非全部时图标高亮并显示筛选条件。
|
展开后直接显示全部、进行中、暂停、已结束状态选项,初始选中进行中;收起不清除已选条件。
|
||||||
- 进行中包含 `idle`、`queued`、`running`、`waiting_approval`、`failed` 和 `interrupted`;
|
- 进行中包含 `idle`、`queued`、`running`、`waiting_approval`、`failed` 和 `interrupted`;
|
||||||
暂停对应 `paused`,已结束包含 `completed` 和 `cancelled`。分类只影响列表筛选,
|
暂停对应 `paused`,已结束包含 `completed` 和 `cancelled`。分类只影响列表筛选,
|
||||||
不改变任务状态语义;任务行保留具体状态、错误信息和原有操作。
|
不改变任务状态语义;任务行保留具体状态、错误信息和原有操作。
|
||||||
@@ -158,7 +158,7 @@ FR-TC3:任务累计执行时间按其历次执行请求汇总,周期触发
|
|||||||
- [ ] FR-TC1:仅显示当前项目任务,切换会话不缩小列表;无项目时显示选择项目提示。
|
- [ ] FR-TC1:仅显示当前项目任务,切换会话不缩小列表;无项目时显示选择项目提示。
|
||||||
- [ ] FR-TC2:消息总数与累计回复时间随当前会话更新;轮内工具和审批计时,轮间空闲不计时,历史缺失及远程证据不足有提示。
|
- [ ] FR-TC2:消息总数与累计回复时间随当前会话更新;轮内工具和审批计时,轮间空闲不计时,历史缺失及远程证据不足有提示。
|
||||||
- [ ] FR-TC3:多次执行汇总到同一任务,不按父 Task 生命周期计算用时。
|
- [ ] FR-TC3:多次执行汇总到同一任务,不按父 Task 生命周期计算用时。
|
||||||
- [ ] FR-TC4:标题右侧先新建、后筛选;筛选默认收起且为全部,收起后保留所选条件。
|
- [x] FR-TC4:标题右侧仅保留新建;展开列表直接显示状态选项,默认进行中,收起后保留所选条件。
|
||||||
- [ ] FR-TC5:创建默认立即执行和当前会话,可选择本项目其他会话、新会话及定时周期,空名称从内容生成。
|
- [ ] FR-TC5:创建默认立即执行和当前会话,可选择本项目其他会话、新会话及定时周期,空名称从内容生成。
|
||||||
- [x] 任务中心保持单实例、默认存在且不可关闭。
|
- [x] 任务中心保持单实例、默认存在且不可关闭。
|
||||||
|
|
||||||
|
|||||||
@@ -363,6 +363,7 @@ describe('RightAssistantSidebar resizing', () => {
|
|||||||
}
|
}
|
||||||
const view = render(sidebarElement(props))
|
const view = render(sidebarElement(props))
|
||||||
fireEvent.click(screen.getByRole('button', { name: '项目任务 2' }))
|
fireEvent.click(screen.getByRole('button', { name: '项目任务 2' }))
|
||||||
|
fireEvent.click(screen.getByRole('button', { name: '全部' }))
|
||||||
const stats = within(screen.getByRole('region', { name: '当前会话统计' }))
|
const stats = within(screen.getByRole('region', { name: '当前会话统计' }))
|
||||||
expect(stats.getByText('01:01:01')).toBeVisible()
|
expect(stats.getByText('01:01:01')).toBeVisible()
|
||||||
expect(stats.getByText('24')).toBeVisible()
|
expect(stats.getByText('24')).toBeVisible()
|
||||||
@@ -385,29 +386,22 @@ describe('RightAssistantSidebar resizing', () => {
|
|||||||
for (const task of tasks) expect(screen.queryByText(task.title)).not.toBeInTheDocument()
|
for (const task of tasks) expect(screen.queryByText(task.title)).not.toBeInTheDocument()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('keeps an applied filter visible when collapsed and clears it without opening the filter panel', () => {
|
it('shows the task status tabs when expanded and defaults to active tasks', () => {
|
||||||
renderSidebar({ tasks: [{
|
renderSidebar({ tasks: [{
|
||||||
id: 'completed', projectId: currentProject.id, title: 'Completed task', instructions: '',
|
id: 'completed', projectId: currentProject.id, title: 'Completed task', instructions: '',
|
||||||
origin: 'schedule', status: 'completed', createdAt: '2026-09-17T00:00:00Z'
|
origin: 'schedule', status: 'completed', createdAt: '2026-09-17T00:00:00Z'
|
||||||
}] })
|
}] })
|
||||||
const toggle = screen.getByRole('button', { name: '筛选任务' })
|
expect(screen.queryByText('Completed task')).not.toBeInTheDocument()
|
||||||
expect(screen.getByText('Completed task')).toBeVisible()
|
expect(screen.queryByRole('button', { name: '筛选任务' })).not.toBeInTheDocument()
|
||||||
fireEvent.click(toggle)
|
const filters = within(screen.getByRole('group', { name: '筛选任务' }))
|
||||||
fireEvent.click(screen.getByRole('button', { name: '进行中' }))
|
expect(filters.getByRole('button', { name: '进行中' })).toHaveAttribute('aria-pressed', 'true')
|
||||||
fireEvent.click(toggle)
|
expect(screen.queryByText('Completed task')).not.toBeInTheDocument()
|
||||||
expect(toggle).toHaveAttribute('aria-expanded', 'false')
|
fireEvent.click(filters.getByRole('button', { name: '已结束' }))
|
||||||
expect(toggle).toHaveAttribute('data-active', 'true')
|
|
||||||
expect(screen.queryByRole('group', { name: '筛选任务' })).not.toBeInTheDocument()
|
|
||||||
expect(screen.getByText('筛选:进行中 · 0 项')).toBeVisible()
|
|
||||||
expect(screen.getByText('当前筛选条件下没有任务。')).toBeVisible()
|
|
||||||
fireEvent.click(screen.getByRole('button', { name: '清除筛选' }))
|
|
||||||
expect(toggle).toHaveAttribute('data-active', 'false')
|
|
||||||
expect(screen.getByText('Completed task')).toBeVisible()
|
expect(screen.getByText('Completed task')).toBeVisible()
|
||||||
})
|
})
|
||||||
|
|
||||||
it('scrolls the focused task filter into view when keyboard navigation reaches either end', () => {
|
it('scrolls the focused task filter into view when keyboard navigation reaches either end', () => {
|
||||||
renderSidebar()
|
renderSidebar()
|
||||||
fireEvent.click(screen.getByRole('button', { name: '筛选任务' }))
|
|
||||||
const filters = within(screen.getByRole('group', { name: '筛选任务' }))
|
const filters = within(screen.getByRole('group', { name: '筛选任务' }))
|
||||||
const first = filters.getByRole('button', { name: '全部' })
|
const first = filters.getByRole('button', { name: '全部' })
|
||||||
const last = filters.getByRole('button', { name: '已结束' })
|
const last = filters.getByRole('button', { name: '已结束' })
|
||||||
@@ -658,7 +652,7 @@ describe('RightAssistantSidebar resizing', () => {
|
|||||||
expect(list).toBeVisible()
|
expect(list).toBeVisible()
|
||||||
fireEvent.click(toggle)
|
fireEvent.click(toggle)
|
||||||
expect(list).not.toBeVisible()
|
expect(list).not.toBeVisible()
|
||||||
fireEvent.click(screen.getByRole('button', { name: '筛选任务' }))
|
fireEvent.click(toggle)
|
||||||
expect(toggle).toHaveAttribute('aria-expanded', 'true')
|
expect(toggle).toHaveAttribute('aria-expanded', 'true')
|
||||||
expect(list).toBeVisible()
|
expect(list).toBeVisible()
|
||||||
})
|
})
|
||||||
@@ -676,7 +670,7 @@ describe('RightAssistantSidebar resizing', () => {
|
|||||||
expect(within(stats).getByText(`项目:${currentProject.name}`)).toBeVisible()
|
expect(within(stats).getByText(`项目:${currentProject.name}`)).toBeVisible()
|
||||||
expect(screen.getByRole('button', { name: '项目任务 4' })).toHaveAttribute('aria-expanded', 'false')
|
expect(screen.getByRole('button', { name: '项目任务 4' })).toHaveAttribute('aria-expanded', 'false')
|
||||||
expect(screen.getByText('2 运行中')).toBeVisible()
|
expect(screen.getByText('2 运行中')).toBeVisible()
|
||||||
fireEvent.click(screen.getByRole('button', { name: '筛选任务' }))
|
fireEvent.click(screen.getByRole('button', { name: '项目任务 4' }))
|
||||||
fireEvent.click(within(screen.getByRole('group', { name: '筛选任务' })).getByRole('button', { name: '已结束' }))
|
fireEvent.click(within(screen.getByRole('group', { name: '筛选任务' })).getByRole('button', { name: '已结束' }))
|
||||||
expect(screen.getByText('2 运行中')).toBeVisible()
|
expect(screen.getByText('2 运行中')).toBeVisible()
|
||||||
view.rerender(sidebarElement({ tab: 'tasks', tasks: [] }))
|
view.rerender(sidebarElement({ tab: 'tasks', tasks: [] }))
|
||||||
@@ -701,10 +695,7 @@ describe('RightAssistantSidebar resizing', () => {
|
|||||||
)
|
)
|
||||||
fireEvent.click(newTaskButton)
|
fireEvent.click(newTaskButton)
|
||||||
expect(onCreateCustomTask).toHaveBeenCalledOnce()
|
expect(onCreateCustomTask).toHaveBeenCalledOnce()
|
||||||
expect(
|
expect(screen.getByRole('group', { name: '筛选任务' })).toBeVisible()
|
||||||
screen.queryByRole('group', { name: '筛选任务' })
|
|
||||||
).not.toBeInTheDocument()
|
|
||||||
expect(screen.getByRole('button', { name: '筛选任务' })).toHaveAttribute('aria-expanded', 'false')
|
|
||||||
expect(screen.queryByLabelText('定时任务标题')).not.toBeInTheDocument()
|
expect(screen.queryByLabelText('定时任务标题')).not.toBeInTheDocument()
|
||||||
expect(screen.queryByText('最近任务')).not.toBeInTheDocument()
|
expect(screen.queryByText('最近任务')).not.toBeInTheDocument()
|
||||||
})
|
})
|
||||||
@@ -736,7 +727,6 @@ describe('RightAssistantSidebar resizing', () => {
|
|||||||
approvals: [approval], onOpenTask, onRespondApproval
|
approvals: [approval], onOpenTask, onRespondApproval
|
||||||
})
|
})
|
||||||
|
|
||||||
fireEvent.click(screen.getByRole('button', { name: '筛选任务' }))
|
|
||||||
const filters = within(screen.getByRole('group', { name: '筛选任务' }))
|
const filters = within(screen.getByRole('group', { name: '筛选任务' }))
|
||||||
expect(filters.getAllByRole('button').map((button) => button.textContent))
|
expect(filters.getAllByRole('button').map((button) => button.textContent))
|
||||||
.toEqual(['全部', '进行中', '暂停', '已结束'])
|
.toEqual(['全部', '进行中', '暂停', '已结束'])
|
||||||
@@ -782,7 +772,6 @@ describe('RightAssistantSidebar resizing', () => {
|
|||||||
}
|
}
|
||||||
const onRespondApproval = vi.fn()
|
const onRespondApproval = vi.fn()
|
||||||
renderSidebar({ tasks: [task], approvals: [approval], onRespondApproval })
|
renderSidebar({ tasks: [task], approvals: [approval], onRespondApproval })
|
||||||
fireEvent.click(screen.getByRole('button', { name: '筛选任务' }))
|
|
||||||
const row = screen.getByText(task.title).closest('article')!
|
const row = screen.getByText(task.title).closest('article')!
|
||||||
expect(within(row).getByLabelText('等待审批: Confirm write')).toBeVisible()
|
expect(within(row).getByLabelText('等待审批: Confirm write')).toBeVisible()
|
||||||
expect(screen.queryByRole('heading', { name: '等待审批' })).not.toBeInTheDocument()
|
expect(screen.queryByRole('heading', { name: '等待审批' })).not.toBeInTheDocument()
|
||||||
|
|||||||
@@ -7,7 +7,6 @@ import {
|
|||||||
Copy,
|
Copy,
|
||||||
ExternalLink,
|
ExternalLink,
|
||||||
FileText,
|
FileText,
|
||||||
Filter,
|
|
||||||
Maximize2,
|
Maximize2,
|
||||||
Minimize2,
|
Minimize2,
|
||||||
Plus,
|
Plus,
|
||||||
@@ -48,7 +47,6 @@ import {
|
|||||||
isBrowserViewportOccluded
|
isBrowserViewportOccluded
|
||||||
} from './browser-viewport-occlusion'
|
} from './browser-viewport-occlusion'
|
||||||
import {
|
import {
|
||||||
findTaskSchedule,
|
|
||||||
TaskScheduleActions
|
TaskScheduleActions
|
||||||
} from './TaskScheduleActions'
|
} from './TaskScheduleActions'
|
||||||
import type {
|
import type {
|
||||||
@@ -883,8 +881,7 @@ export function RightAssistantSidebar({
|
|||||||
useState('')
|
useState('')
|
||||||
const [taskFilter, setTaskFilter] = useState<
|
const [taskFilter, setTaskFilter] = useState<
|
||||||
'all' | 'active' | 'paused' | 'finished'
|
'all' | 'active' | 'paused' | 'finished'
|
||||||
>('all')
|
>('active')
|
||||||
const [taskFiltersExpanded, setTaskFiltersExpanded] = useState(false)
|
|
||||||
const [taskListExpanded, setTaskListExpanded] = useState(false)
|
const [taskListExpanded, setTaskListExpanded] = useState(false)
|
||||||
const [actionErrorState, setActionErrorState] = useState({
|
const [actionErrorState, setActionErrorState] = useState({
|
||||||
scope: { instanceId: activeWorkbarInstanceId, open },
|
scope: { instanceId: activeWorkbarInstanceId, open },
|
||||||
@@ -1027,6 +1024,14 @@ export function RightAssistantSidebar({
|
|||||||
}),
|
}),
|
||||||
[approvalsByTask, taskFilter, topLevelTasks]
|
[approvalsByTask, taskFilter, topLevelTasks]
|
||||||
)
|
)
|
||||||
|
const schedulesByTaskKey = useMemo(() => {
|
||||||
|
const byKey = new Map<string, AssistantSchedule>()
|
||||||
|
for (const schedule of schedules) {
|
||||||
|
byKey.set(schedule.id, schedule)
|
||||||
|
byKey.set(schedule.taskId, schedule)
|
||||||
|
}
|
||||||
|
return byKey
|
||||||
|
}, [schedules])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Context switches preserve the selected instance and its pinned binding.
|
// Context switches preserve the selected instance and its pinned binding.
|
||||||
@@ -1981,21 +1986,8 @@ export function RightAssistantSidebar({
|
|||||||
>
|
>
|
||||||
<Plus aria-hidden="true" size={13} />
|
<Plus aria-hidden="true" size={13} />
|
||||||
</button>
|
</button>
|
||||||
<button
|
|
||||||
className="secondary-button task-center__filter-toggle"
|
|
||||||
aria-label={t('sidebar.tasks.filters.ariaLabel')}
|
|
||||||
title={t('sidebar.tasks.filters.ariaLabel')}
|
|
||||||
aria-expanded={taskListExpanded && taskFiltersExpanded}
|
|
||||||
aria-controls={`task-filters-${instance.id}`}
|
|
||||||
data-active={taskFilter !== 'all'}
|
|
||||||
onClick={() => {
|
|
||||||
setTaskListExpanded(true)
|
|
||||||
setTaskFiltersExpanded((expanded) => !expanded)
|
|
||||||
}}
|
|
||||||
type="button"
|
|
||||||
><Filter aria-hidden="true" size={14} /></button>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="task-center__filters" id={`task-filters-${instance.id}`} hidden={!taskListExpanded || !taskFiltersExpanded}
|
<div className="task-center__filters" id={`task-filters-${instance.id}`} hidden={!taskListExpanded}
|
||||||
onFocusCapture={(event) => event.target.scrollIntoView?.({ block: 'nearest', inline: 'nearest' })}>
|
onFocusCapture={(event) => event.target.scrollIntoView?.({ block: 'nearest', inline: 'nearest' })}>
|
||||||
<SegmentedControl
|
<SegmentedControl
|
||||||
ariaLabel={t('sidebar.tasks.filters.ariaLabel')}
|
ariaLabel={t('sidebar.tasks.filters.ariaLabel')}
|
||||||
@@ -2018,10 +2010,6 @@ export function RightAssistantSidebar({
|
|||||||
value={taskFilter}
|
value={taskFilter}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
{taskListExpanded && taskFilter !== 'all' && <div className="task-center__filter-summary">
|
|
||||||
<span>{t('sidebar.tasks.filters.summary', { filter: t(`sidebar.tasks.filters.${taskFilter}`), count: filteredTasks.length })}</span>
|
|
||||||
<button type="button" onClick={() => setTaskFilter('all')}>{t('sidebar.tasks.filters.clear')}</button>
|
|
||||||
</div>}
|
|
||||||
{unassociatedApprovals.length > 0 && <>
|
{unassociatedApprovals.length > 0 && <>
|
||||||
<h3><ShieldAlert size={15} />{t('sidebar.tasks.approvalsTitle')}</h3>
|
<h3><ShieldAlert size={15} />{t('sidebar.tasks.approvalsTitle')}</h3>
|
||||||
{unassociatedApprovals.map((approval) => (
|
{unassociatedApprovals.map((approval) => (
|
||||||
@@ -2037,7 +2025,9 @@ export function RightAssistantSidebar({
|
|||||||
</p>
|
</p>
|
||||||
) : (
|
) : (
|
||||||
filteredTasks.map((task) => {
|
filteredTasks.map((task) => {
|
||||||
const schedule = findTaskSchedule(task, schedules)
|
const schedule =
|
||||||
|
schedulesByTaskKey.get(task.scheduleId ?? '') ??
|
||||||
|
schedulesByTaskKey.get(task.id)
|
||||||
const conversationTitle = task.conversationId
|
const conversationTitle = task.conversationId
|
||||||
? conversationTitles.get(task.conversationId)
|
? conversationTitles.get(task.conversationId)
|
||||||
: undefined
|
: undefined
|
||||||
|
|||||||
@@ -81,8 +81,13 @@
|
|||||||
.task-center__list { display: flex; flex-direction: column; gap: var(--space-2); }
|
.task-center__list { display: flex; flex-direction: column; gap: var(--space-2); }
|
||||||
.task-center__list[hidden] { display: none; }
|
.task-center__list[hidden] { display: none; }
|
||||||
|
|
||||||
.task-center .task-center__create,
|
/* Keep expanding a long history responsive by skipping off-screen cards. */
|
||||||
.task-center .task-center__filter-toggle {
|
.task-center__list > .task-center__item {
|
||||||
|
content-visibility: auto;
|
||||||
|
contain-intrinsic-size: 0 132px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-center .task-center__create {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -92,13 +97,6 @@
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.task-center .task-center__filter-toggle[data-active='true'],
|
|
||||||
.task-center .task-center__filter-toggle[aria-expanded='true'] {
|
|
||||||
border-color: var(--accent-selected);
|
|
||||||
background: var(--accent-subtle);
|
|
||||||
color: var(--accent);
|
|
||||||
}
|
|
||||||
|
|
||||||
.task-center .task-center__filters { flex: 0 0 auto; }
|
.task-center .task-center__filters { flex: 0 0 auto; }
|
||||||
.task-center .task-center__filters .segmented-control { overflow-x: auto; }
|
.task-center .task-center__filters .segmented-control { overflow-x: auto; }
|
||||||
.task-center .task-center__filters .segmented-control__option {
|
.task-center .task-center__filters .segmented-control__option {
|
||||||
@@ -107,26 +105,6 @@
|
|||||||
}
|
}
|
||||||
.task-center .task-center__filters[hidden] { display: none; }
|
.task-center .task-center__filters[hidden] { display: none; }
|
||||||
|
|
||||||
.task-center__filter-summary {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
gap: var(--space-2);
|
|
||||||
color: var(--text-secondary);
|
|
||||||
font-size: var(--font-caption);
|
|
||||||
}
|
|
||||||
|
|
||||||
.task-center__filter-summary button {
|
|
||||||
border: 0;
|
|
||||||
background: transparent;
|
|
||||||
color: var(--accent);
|
|
||||||
padding: var(--space-2);
|
|
||||||
cursor: pointer;
|
|
||||||
font: inherit;
|
|
||||||
}
|
|
||||||
|
|
||||||
.task-center__filter-summary button:hover { background: var(--accent-subtle); }
|
|
||||||
.task-center button:focus-visible,
|
.task-center button:focus-visible,
|
||||||
.task-center summary:focus-visible {
|
.task-center summary:focus-visible {
|
||||||
outline: 2px solid var(--accent);
|
outline: 2px solid var(--accent);
|
||||||
|
|||||||
Reference in New Issue
Block a user