onSelectTask: (taskId: string) => void
@@ -33,7 +31,6 @@ type ConversationTaskStripProps = {
export function ConversationTaskStrip({
locale,
- onCreate,
onRemoveSchedule,
onRunSchedule,
onSelectTask,
@@ -41,7 +38,7 @@ export function ConversationTaskStrip({
schedules,
selectedTaskId,
tasks
-}: ConversationTaskStripProps): React.JSX.Element {
+}: ConversationTaskStripProps): React.JSX.Element | null {
const { t } = useTranslation('workspace')
const [manuallyExpanded, setManuallyExpanded] = useState(false)
const [collapsedTaskId, setCollapsedTaskId] = useState('')
@@ -58,6 +55,10 @@ export function ConversationTaskStrip({
manuallyExpanded ||
(Boolean(selectedTaskId) && collapsedTaskId !== selectedTaskId)
+ if (tasks.length === 0) {
+ return null
+ }
+
return (
)}
-
{expanded && (
- {tasks.length === 0 ? (
-
- {t('taskStrip.empty')}
-
- ) : (
- <>
-
+ {tasks.map((task) => (
+
- {selectedTask && (
-
-
-
- {selectedTask.title}
-
- {t(`task.status.${selectedTask.status}`)}
-
-
- {selectedTask.status === 'failed' && (
-
- )}
-
-
-
-
- {t('task.fields.mode')}
- -
- {selectedSchedule
- ? t(`task.mode.${selectedSchedule.workMode}`)
- : selectedTask.workMode
- ? t(`task.mode.${selectedTask.workMode}`)
- : t('task.mode.unavailable')}
-
-
-
-
- {t('task.fields.schedule')}
- -
- {selectedSchedule
- ? t(
- `sidebar.tasks.schedule.recurrence.${selectedSchedule.recurrence}`
- )
- : t('task.schedule.none')}
-
-
-
-
- {t('task.fields.nextRun')}
- -
- {selectedSchedule
- ? new Date(
- selectedSchedule.nextRunAt
+
+ {task.title}
+
+ ))}
+
+ {selectedTask && (
+
+
+
+ {selectedTask.title}
+
+ {t(`task.status.${selectedTask.status}`)}
+
+
+ {selectedTask.status === 'failed' && (
+
+ )}
+
+
+
+
- {t('task.fields.mode')}
+ -
+ {selectedSchedule
+ ? t(`task.mode.${selectedSchedule.workMode}`)
+ : selectedTask.workMode
+ ? t(`task.mode.${selectedTask.workMode}`)
+ : t('task.mode.unavailable')}
+
+
+
+
- {t('task.fields.schedule')}
+ -
+ {selectedSchedule
+ ? t(
+ `sidebar.tasks.schedule.recurrence.${selectedSchedule.recurrence}`
+ )
+ : t('task.schedule.none')}
+
+
+
+
- {t('task.fields.nextRun')}
+ -
+ {selectedSchedule
+ ? new Date(
+ selectedSchedule.nextRunAt
+ ).toLocaleString(locale)
+ : t('task.notAvailable')}
+
+
+
+
- {t('task.fields.outcome')}
+ -
+ {selectedTask.error ??
+ (selectedTask.completedAt
+ ? t('task.completedAt', {
+ time: new Date(
+ selectedTask.completedAt
).toLocaleString(locale)
- : t('task.notAvailable')}
-
-
-
-
- {t('task.fields.outcome')}
- -
- {selectedTask.error ??
- (selectedTask.completedAt
- ? t('task.completedAt', {
- time: new Date(
- selectedTask.completedAt
- ).toLocaleString(locale)
- })
- : t('task.noOutcome'))}
-
-
-
- {selectedSchedule && (
-
-
-
- )}
-
+ })
+ : t('task.noOutcome'))}
+
+
+
+ {selectedSchedule && (
+
+
+
)}
- >
+
)}
{actionError && (
diff --git a/src/renderer/src/RightAssistantSidebar.resize.test.tsx b/src/renderer/src/RightAssistantSidebar.resize.test.tsx
index 5ba5d352..215cf1eb 100644
--- a/src/renderer/src/RightAssistantSidebar.resize.test.tsx
+++ b/src/renderer/src/RightAssistantSidebar.resize.test.tsx
@@ -253,11 +253,17 @@ describe('RightAssistantSidebar resizing', () => {
renderSidebar({ tab: 'tasks' })
expect(screen.getByText('等待审批')).toBeInTheDocument()
+ const taskIndexHeading = screen.getByRole('heading', {
+ name: '任务索引'
+ })
+ const newTaskButton = screen.getByRole('button', {
+ name: '新建任务'
+ })
+ expect(taskIndexHeading.parentElement).toContainElement(
+ newTaskButton
+ )
expect(
- screen.getByRole('heading', { name: '任务索引' })
- ).toBeInTheDocument()
- expect(
- screen.getByRole('button', { name: '新建定制任务' })
+ screen.getByRole('group', { name: '筛选任务' })
).toBeInTheDocument()
expect(screen.queryByLabelText('定时任务标题')).not.toBeInTheDocument()
expect(screen.queryByText('最近任务')).not.toBeInTheDocument()
diff --git a/src/renderer/src/RightAssistantSidebar.tsx b/src/renderer/src/RightAssistantSidebar.tsx
index 08d25720..0d820dae 100644
--- a/src/renderer/src/RightAssistantSidebar.tsx
+++ b/src/renderer/src/RightAssistantSidebar.tsx
@@ -640,19 +640,9 @@ export function RightAssistantSidebar({
) : null}
{tab === 'tasks' && (
-
-
- {t('sidebar.tasks.description')}
-
-
-
- {t('sidebar.tasks.createCustom')}
-
-
+
+ {t('sidebar.tasks.description')}
+
{t('sidebar.tasks.approvalsTitle')}
@@ -694,10 +684,20 @@ export function RightAssistantSidebar({
))
)}
-
-
- {t('sidebar.tasks.taskIndexTitle')}
-
+
+
+
+ {t('sidebar.tasks.taskIndexTitle')}
+
+
+
+ {t('taskStrip.create')}
+
+
{
expect(stylesheet).toMatch(
/\.composer-wrap\s*\{[^}]*width:\s*100%;[^}]*max-width:\s*100%;[^}]*min-width:\s*0;[^}]*padding:\s*var\(--space-3\)\s*max\(var\(--page-gutter\),\s*calc\(\(100% - var\(--content-reading\)\) \/ 2\)\)\s*var\(--space-2\);/u
)
+ expect(stylesheet).toMatch(
+ /\.assistant-sidebar__tabs\s*\{[^}]*grid-template-columns:\s*repeat\(5,\s*minmax\(max-content,\s*1fr\)\);/u
+ )
+ expect(stylesheet).toMatch(
+ /\.assistant-sidebar__tab\s*\{[^}]*white-space:\s*nowrap;/u
+ )
+ expect(stylesheet).toMatch(
+ /\.app-frame\s*\{[^}]*display:\s*grid;[^}]*grid-template-rows:\s*58px minmax\(0,\s*1fr\);/u
+ )
+ expect(stylesheet).toMatch(
+ /\.workspace\s*\{[^}]*grid-template-rows:\s*minmax\(0,\s*1fr\);/u
+ )
+ expect(stylesheet).toMatch(
+ /\.assistant-sidebar-toggle\s*\{[^}]*position:\s*absolute;[^}]*top:\s*6px;[^}]*right:\s*6px;/u
+ )
+ expect(stylesheet).toMatch(
+ /\.conversation-task-strip\s*\{[^}]*min-height:\s*46px;[^}]*padding:\s*0\s+max\(/u
+ )
+ expect(stylesheet).toMatch(
+ /\.conversation-task-strip__header\s*\{[^}]*min-height:\s*45px;/u
+ )
+ expect(stylesheet).toMatch(
+ /@media \(max-width: 720px\)\s*\{[\s\S]*?\.conversation-task-strip\s*\{[^}]*padding-right:\s*46px;[^}]*padding-left:\s*var\(--space-3\);/u
+ )
+ expect(stylesheet).not.toMatch(
+ /@media \(max-width: 720px\)\s*\{[\s\S]*?\.conversation-task-strip__header\s*\{[^}]*flex-direction:\s*column;/u
+ )
+ expect(stylesheet).toMatch(
+ /\.task-center__filters \.segmented-control\s*\{[^}]*width:\s*100%;[^}]*overflow:\s*hidden;/u
+ )
+ expect(stylesheet).toMatch(
+ /\.task-center__filters \.segmented-control__option\s*\{[^}]*min-width:\s*0;[^}]*flex:\s*1;/u
+ )
})
it('floats Runtime context compaction without shifting the composer', () => {
diff --git a/src/renderer/src/i18n/locales/en-US/workspace.ts b/src/renderer/src/i18n/locales/en-US/workspace.ts
index faae53ee..1eee42ca 100644
--- a/src/renderer/src/i18n/locales/en-US/workspace.ts
+++ b/src/renderer/src/i18n/locales/en-US/workspace.ts
@@ -99,7 +99,6 @@ export const workspace = {
tasks: {
description:
'Review pending approvals and create or manage automations.',
- createCustom: 'New custom task',
approvalsTitle: 'Awaiting approval',
noApprovals: 'There are no operations awaiting approval.',
deny: 'Deny',
@@ -248,7 +247,6 @@ export const workspace = {
title: 'Conversation tasks',
count: '{{count}}',
create: 'New task',
- empty: 'This conversation has no tasks yet.',
taskList: 'Task list'
},
files: {
diff --git a/src/renderer/src/i18n/locales/zh-CN/workspace.ts b/src/renderer/src/i18n/locales/zh-CN/workspace.ts
index 82157b6d..ad1a6bfe 100644
--- a/src/renderer/src/i18n/locales/zh-CN/workspace.ts
+++ b/src/renderer/src/i18n/locales/zh-CN/workspace.ts
@@ -94,7 +94,6 @@ export const workspace = {
},
tasks: {
description: '处理当前待审批操作,并创建和管理自动化任务。',
- createCustom: '新建定制任务',
approvalsTitle: '等待审批',
noApprovals: '当前没有等待审批的操作。',
deny: '拒绝',
@@ -236,7 +235,6 @@ export const workspace = {
title: '会话任务',
count: '{{count}} 个',
create: '新建任务',
- empty: '当前会话还没有任务。',
taskList: '任务列表'
},
files: {
diff --git a/src/renderer/src/styles.css b/src/renderer/src/styles.css
index fbde7976..ad272160 100644
--- a/src/renderer/src/styles.css
+++ b/src/renderer/src/styles.css
@@ -196,15 +196,21 @@
.conversation-task-strip {
flex: 0 0 auto;
- padding:
- var(--space-2)
+ min-height: 46px;
+ padding: 0
max(var(--page-gutter), calc((100% - var(--content-reading)) / 2));
+ padding-right: max(
+ 46px,
+ var(--page-gutter),
+ calc((100% - var(--content-reading)) / 2)
+ );
border-bottom: 1px solid var(--border-subtle);
background: var(--surface-raised);
}
.conversation-task-strip__header {
display: flex;
+ min-height: 45px;
align-items: center;
justify-content: space-between;
gap: var(--space-2);
@@ -239,12 +245,6 @@
font-size: var(--font-caption);
}
-.conversation-task-strip__create {
- min-height: 30px;
- padding: 0 var(--space-2);
- font-size: var(--font-caption);
-}
-
.conversation-task-strip__content {
display: grid;
padding: var(--space-2) 0 var(--space-1);
@@ -381,7 +381,6 @@
opacity: 0.6;
}
-.conversation-task-strip__empty,
.conversation-task-strip__error {
margin: 0;
color: var(--text-muted);
@@ -392,19 +391,40 @@
color: var(--danger);
}
-.task-center__heading {
- display: grid;
- align-items: start;
+.task-center__index-heading {
+ display: flex;
+ min-width: 0;
+ align-items: center;
+ margin: 8px 0 2px;
gap: var(--space-2);
}
-.task-center__heading .primary-button {
- justify-self: start;
+.assistant-sidebar__section .task-center__index-heading h3 {
+ min-width: 0;
+ margin: 0;
+}
+
+.task-center__create {
+ min-height: 30px;
+ flex: 0 0 auto;
+ padding: 0 var(--space-2);
}
.task-center__filters {
min-width: 0;
- overflow-x: auto;
+ overflow: hidden;
+}
+
+.task-center__filters .segmented-control {
+ width: 100%;
+ overflow: hidden;
+}
+
+.task-center__filters .segmented-control__option {
+ min-width: 0;
+ flex: 1;
+ padding-right: var(--space-1);
+ padding-left: var(--space-1);
}
.task-center__item {
@@ -791,19 +811,9 @@
}
.conversation-task-strip {
- padding-right: var(--space-3);
+ padding-right: 46px;
padding-left: var(--space-3);
}
-
- .conversation-task-strip__header {
- align-items: stretch;
- flex-direction: column;
- }
-
- .conversation-task-strip__create {
- width: 100%;
- justify-content: center;
- }
}
.knowledge-scope__retrieval-mode {
@@ -2266,18 +2276,7 @@ textarea:focus-visible {
min-height: 0;
background: var(--surface-canvas);
color: var(--text-primary);
- grid-template-rows: 30px minmax(0, 1fr);
-}
-
-.window-titlebar {
- display: flex;
- min-width: 0;
- align-items: stretch;
- justify-content: flex-end;
- border-bottom: 1px solid var(--border-subtle);
- background: var(--surface-subtle);
- -webkit-app-region: drag;
- user-select: none;
+ grid-template-rows: 58px minmax(0, 1fr);
}
.app-shell {
@@ -2977,7 +2976,7 @@ textarea:focus-visible {
flex: 1;
overflow: hidden;
background: var(--surface-raised);
- grid-template-rows: 46px minmax(0, 1fr);
+ grid-template-rows: minmax(0, 1fr);
}
.workspace-route-cache {
@@ -3003,10 +3002,6 @@ textarea:focus-visible {
user-select: none;
}
-.topbar--with-assistant-toggle {
- padding-right: 46px;
-}
-
.topbar :is(button, input, select),
.topbar__actions,
.window-controls {
@@ -3018,7 +3013,7 @@ textarea:focus-visible {
height: 100%;
flex: 0 0 auto;
align-self: stretch;
- -webkit-app-region: no-drag;
+ margin-left: 8px;
}
.window-control {
@@ -3160,7 +3155,7 @@ button > svg {
flex: 0 0 46px;
align-items: center;
gap: 3px;
- grid-template-columns: repeat(5, minmax(0, 1fr));
+ grid-template-columns: repeat(5, minmax(max-content, 1fr));
}
.assistant-sidebar-toggle {
@@ -3180,6 +3175,7 @@ button > svg {
color: var(--text-muted);
cursor: pointer;
font-size: 10px;
+ white-space: nowrap;
}
.assistant-sidebar__tab:hover,
@@ -12579,7 +12575,6 @@ details.settings-section > :not(summary) + :not(summary) {
:root[data-theme='dark'] :where(
.sidebar,
- .window-titlebar,
.topbar,
.assistant-sidebar,
.project-create-card,
@@ -12602,7 +12597,6 @@ details.settings-section > :not(summary) + :not(summary) {
:root[data-theme='dark'] :where(
.sidebar,
- .window-titlebar,
.topbar
) {
background: var(--surface-subtle);
@@ -12660,7 +12654,6 @@ details.settings-section > :not(summary) + :not(summary) {
:root[data-theme='dark'] :where(
.primary-nav,
.sidebar-footer,
- .window-titlebar,
.topbar,
.assistant-sidebar,
.assistant-sidebar__tabs,