fix: align assistant workbar controls

Existing titlebar controls could be pushed into a second row, while task controls left uneven or redundant surfaces. The window titlebar is restored, the shared workbar toggle stays directly below the close control, empty conversation task rows disappear, and Task index owns creation plus full-width filters.

Release note: 修复助手工作栏顶部错位与任务入口重复问题;空会话不再显示任务栏,任务索引统一提供新建入口和全宽状态筛选。
This commit is contained in:
mesalogo
2026-08-21 08:43:17 +08:00
parent b29f2d7b0d
commit 97419f3ef7
13 changed files with 405 additions and 348 deletions
+37 -10
View File
@@ -1121,17 +1121,29 @@ describe('App', () => {
expect(
within(assistantSidebar).getByText('Project: Default project')
).toBeInTheDocument()
fireEvent.click(
within(assistantSidebar).getByRole('button', {
name: 'New custom task'
})
const taskIndexHeading = within(assistantSidebar).getByRole(
'heading',
{ name: 'Task index' }
)
const newTaskButton = within(assistantSidebar).getByRole(
'button',
{ name: 'New task' }
)
expect(taskIndexHeading.parentElement).toContainElement(
newTaskButton
)
fireEvent.click(newTaskButton)
const taskDialog = screen.getByRole('dialog', {
name: 'New custom task'
})
expect(
within(taskDialog).getByText('Default project')
).toBeInTheDocument()
expect(
within(taskDialog).getByRole('button', {
name: 'Current conversation'
})
).toHaveAttribute('aria-pressed', 'true')
fireEvent.click(
within(taskDialog).getByRole('button', {
name: 'Close new custom task'
@@ -8015,9 +8027,9 @@ describe('App', () => {
expect(sidebar).not.toHaveClass('assistant-sidebar--open')
const assistantTrigger =
screen.getByLabelText('切换助手工作栏')
expect(
container.querySelector('.topbar')
).toContainElement(assistantTrigger)
expect(assistantTrigger.parentElement).toBe(
container.querySelector('.app-shell')
)
expect(assistantTrigger).toHaveClass('assistant-sidebar-toggle')
expect(assistantTrigger).not.toHaveClass('icon-button--active')
expect(assistantTrigger).toHaveAttribute('aria-expanded', 'false')
@@ -8032,8 +8044,14 @@ describe('App', () => {
assistantTrigger.querySelector('.lucide-panel-right-close')
).not.toBeInTheDocument()
expect(
container.querySelector('.window-titlebar')
container.querySelector('.topbar')
).toContainElement(screen.getByLabelText('关闭窗口'))
expect(container.querySelector('.topbar')).toContainElement(
container.querySelector('.conversation-title')
)
expect(
container.querySelector('.topbar')
).not.toContainElement(assistantTrigger)
expect(
screen.queryByLabelText('关闭助手工作栏')
).not.toBeInTheDocument()
@@ -8059,9 +8077,18 @@ describe('App', () => {
expect(
screen.getByRole('tab', { name: '任务中心' })
).toHaveAttribute('aria-selected', 'true')
const taskIndexHeading = screen.getByRole('heading', {
name: '任务索引'
})
const newTaskButton = screen.getByRole('button', {
name: '新建任务'
})
expect(taskIndexHeading.parentElement).toContainElement(
newTaskButton
)
expect(
screen.getByRole('heading', { name: '任务索引' })
).toBeInTheDocument()
screen.queryByRole('region', { name: '当前会话的任务' })
).not.toBeInTheDocument()
expect(screen.queryByText('最近任务')).not.toBeInTheDocument()
fireEvent.click(screen.getByRole('tab', { name: '上下文' }))
expect(
+126 -137
View File
@@ -7390,7 +7390,110 @@ function App(): React.JSX.Element {
return (
<div className="app-frame">
<header className="window-titlebar">
<header className="topbar">
<button
className="icon-button sidebar-toggle"
type="button"
aria-label={t('sidebar.toggle')}
onClick={() => setSidebarOpen((open) => !open)}
ref={sidebarToggleRef}
>
<PanelLeft aria-hidden="true" size={18} />
</button>
{view === 'chat' && (
<>
<div
className="conversation-title"
title={activeConversation?.title}
>
<span className="conversation-title__text">
{activeConversation
? getConversationDisplayTitle(
activeConversation,
t('conversation.defaultTitle')
)
:
(activeProject?.kind === 'channel'
? t('conversation.remoteTitle')
: t('conversation.defaultTitle'))}
</span>
{activeConversation?.branch && (
<ConversationBranchBadge
sourceTitle={
conversationTitles.get(
activeConversation.branch.sourceConversationId
) ?? activeConversation.branch.sourceTitle
}
/>
)}
{activeConversation?.remote && (
<b className="conversation-source-badge">
{
projectChannelLabels[
activeConversation.remote.channel
]
}
</b>
)}
</div>
<ScopeBadge
scope={
activeProject
? {
kind: 'project',
projectName:
activeProjectDisplayName ?? activeProject.name
}
: {
kind: 'unavailable',
explanation: t('notices.projectNotLoaded')
}
}
/>
</>
)}
<div className="topbar__actions">
<span
className={
runtime?.available
? 'runtime-status runtime-status--online'
: 'runtime-status'
}
title={runtime?.detail}
>
<span className="runtime-status__dot" />
<span className="runtime-status__label">
{runtime?.label ?? t('runtime.detecting')}
</span>
{runtime?.capability === 'image-generation' && (
<span className="runtime-capability-badge">
{t('runtime.imageGeneration')}
</span>
)}
</span>
<button
aria-label={
resolvedAppearanceTheme === 'dark'
? t('topbar.switchLight')
: t('topbar.switchDark')
}
aria-pressed={resolvedAppearanceTheme === 'dark'}
className="icon-button theme-toggle-button"
onClick={toggleAppearanceTheme}
title={
resolvedAppearanceTheme === 'dark'
? t('topbar.switchLight')
: t('topbar.switchDark')
}
type="button"
>
{resolvedAppearanceTheme === 'dark' ? (
<Sun aria-hidden="true" size={18} />
) : (
<Moon aria-hidden="true" size={18} />
)}
</button>
</div>
<WindowControls onError={handleWindowControlError} />
</header>
<div className="app-shell">
@@ -8045,143 +8148,32 @@ function App(): React.JSX.Element {
/>
)}
{view === 'chat' && (
<button
aria-controls="assistant-sidebar"
aria-expanded={assistantSidebarOpen}
aria-label={t('topbar.toggleAssistantSidebar')}
className="icon-button assistant-sidebar-toggle"
onClick={() =>
setAssistantSidebarOpen((current) => !current)
}
ref={assistantSidebarToggleRef}
title={t('topbar.toggleAssistantSidebar')}
type="button"
>
{assistantSidebarOpen ? (
<PanelRightClose aria-hidden="true" size={18} />
) : (
<PanelRightOpen aria-hidden="true" size={18} />
)}
</button>
)}
<main
aria-hidden={backgroundIsolated ? 'true' : undefined}
className="workspace"
inert={backgroundIsolated}
>
<header
className={
view === 'chat'
? 'topbar topbar--with-assistant-toggle'
: 'topbar'
}
>
<button
className="icon-button sidebar-toggle"
type="button"
aria-label={t('sidebar.toggle')}
onClick={() => setSidebarOpen((open) => !open)}
ref={sidebarToggleRef}
>
<PanelLeft aria-hidden="true" size={18} />
</button>
{view === 'chat' && (
<>
<div
className="conversation-title"
title={activeConversation?.title}
>
<span className="conversation-title__text">
{activeConversation
? getConversationDisplayTitle(
activeConversation,
t('conversation.defaultTitle')
)
:
(activeProject?.kind === 'channel'
? t('conversation.remoteTitle')
: t('conversation.defaultTitle'))}
</span>
{activeConversation?.branch && (
<ConversationBranchBadge
sourceTitle={
conversationTitles.get(
activeConversation.branch.sourceConversationId
) ?? activeConversation.branch.sourceTitle
}
/>
)}
{activeConversation?.remote && (
<b className="conversation-source-badge">
{
projectChannelLabels[
activeConversation.remote.channel
]
}
</b>
)}
</div>
<ScopeBadge
scope={
activeProject
? {
kind: 'project',
projectName:
activeProjectDisplayName ?? activeProject.name
}
: {
kind: 'unavailable',
explanation: t('notices.projectNotLoaded')
}
}
/>
</>
)}
<div className="topbar__actions">
<span
className={
runtime?.available
? 'runtime-status runtime-status--online'
: 'runtime-status'
}
title={runtime?.detail}
>
<span className="runtime-status__dot" />
<span className="runtime-status__label">
{runtime?.label ?? t('runtime.detecting')}
</span>
{runtime?.capability === 'image-generation' && (
<span className="runtime-capability-badge">
{t('runtime.imageGeneration')}
</span>
)}
</span>
<button
aria-label={
resolvedAppearanceTheme === 'dark'
? t('topbar.switchLight')
: t('topbar.switchDark')
}
aria-pressed={resolvedAppearanceTheme === 'dark'}
className="icon-button theme-toggle-button"
onClick={toggleAppearanceTheme}
title={
resolvedAppearanceTheme === 'dark'
? t('topbar.switchLight')
: t('topbar.switchDark')
}
type="button"
>
{resolvedAppearanceTheme === 'dark' ? (
<Sun aria-hidden="true" size={18} />
) : (
<Moon aria-hidden="true" size={18} />
)}
</button>
</div>
{view === 'chat' && (
<button
aria-controls="assistant-sidebar"
aria-expanded={assistantSidebarOpen}
aria-label={t('topbar.toggleAssistantSidebar')}
className="icon-button assistant-sidebar-toggle"
onClick={() =>
setAssistantSidebarOpen((current) => !current)
}
ref={assistantSidebarToggleRef}
title={t('topbar.toggleAssistantSidebar')}
type="button"
>
{assistantSidebarOpen ? (
<PanelRightClose aria-hidden="true" size={18} />
) : (
<PanelRightOpen aria-hidden="true" size={18} />
)}
</button>
)}
</header>
{(view === 'chat' || cachedWorkspaceViewKeys.has('chat')) && (
<KeepAliveRoute
active={view === 'chat'}
@@ -8225,9 +8217,6 @@ function App(): React.JSX.Element {
!conversation.remote ? (
<ConversationTaskStrip
locale={locale}
onCreate={() =>
openCustomTaskDialog('current')
}
onRemoveSchedule={removeAssistantSchedule}
onRunSchedule={runAssistantSchedule}
onSelectTask={setSelectedAssistantTaskId}
@@ -9805,7 +9794,7 @@ function App(): React.JSX.Element {
conversationTitles={conversationTitles}
enabledLibraries={enabledSidebarLibraries}
memories={assistantMemories}
onCreateCustomTask={() => openCustomTaskDialog('new')}
onCreateCustomTask={() => openCustomTaskDialog('current')}
schedules={assistantSchedules}
selectedTaskId={selectedAssistantTaskId}
tasks={productAssistantTasks}
@@ -24,7 +24,6 @@ describe('ConversationTaskStrip', () => {
render(
<ConversationTaskStrip
locale="zh-CN"
onCreate={vi.fn()}
onRemoveSchedule={onRemoveSchedule}
onRunSchedule={onRunSchedule}
onSelectTask={vi.fn()}
@@ -62,6 +61,9 @@ describe('ConversationTaskStrip', () => {
expect(screen.getByText('每日状态', { selector: 'strong' }))
.toBeInTheDocument()
expect(
screen.queryByRole('button', { name: '新建任务' })
).not.toBeInTheDocument()
expect(screen.getByText('Execute')).toBeInTheDocument()
fireEvent.click(screen.getByRole('button', { name: '立即运行' }))
await waitFor(() =>
@@ -87,4 +89,22 @@ describe('ConversationTaskStrip', () => {
expect(onRemoveSchedule).toHaveBeenCalledWith(scheduleId)
)
})
it('hides the conversation task row when there are no tasks', () => {
render(
<ConversationTaskStrip
locale="zh-CN"
onRemoveSchedule={vi.fn(async () => undefined)}
onRunSchedule={vi.fn(async () => undefined)}
onSelectTask={vi.fn()}
onSetScheduleEnabled={vi.fn(async () => undefined)}
schedules={[]}
tasks={[]}
/>
)
expect(
screen.queryByRole('region', { name: '当前会话的任务' })
).not.toBeInTheDocument()
})
})
+98 -113
View File
@@ -2,8 +2,7 @@ import {
ChevronDown,
ChevronUp,
CircleAlert,
ClockFading,
Plus
ClockFading
} from 'lucide-react'
import { useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -18,7 +17,6 @@ import {
type ConversationTaskStripProps = {
locale: string
onCreate: () => void
onRemoveSchedule: (scheduleId: string) => Promise<void>
onRunSchedule: (scheduleId: string) => Promise<void>
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 (
<section
aria-label={t('taskStrip.ariaLabel')}
@@ -91,120 +92,104 @@ export function ConversationTaskStrip({
<ChevronDown aria-hidden="true" size={14} />
)}
</button>
<button
className="secondary-button conversation-task-strip__create"
onClick={onCreate}
type="button"
>
<Plus aria-hidden="true" size={13} />
{t('taskStrip.create')}
</button>
</header>
{expanded && (
<div className="conversation-task-strip__content">
{tasks.length === 0 ? (
<p className="conversation-task-strip__empty">
{t('taskStrip.empty')}
</p>
) : (
<>
<div
aria-label={t('taskStrip.taskList')}
className="conversation-task-strip__list"
<div
aria-label={t('taskStrip.taskList')}
className="conversation-task-strip__list"
>
{tasks.map((task) => (
<button
aria-pressed={selectedTask?.id === task.id}
className={
selectedTask?.id === task.id
? 'conversation-task-strip__task conversation-task-strip__task--active'
: 'conversation-task-strip__task'
}
key={task.id}
onClick={() => onSelectTask(task.id)}
type="button"
>
{tasks.map((task) => (
<button
aria-pressed={selectedTask?.id === task.id}
className={
selectedTask?.id === task.id
? 'conversation-task-strip__task conversation-task-strip__task--active'
: 'conversation-task-strip__task'
}
key={task.id}
onClick={() => onSelectTask(task.id)}
type="button"
>
<span
aria-hidden="true"
className={`task-status-dot task-status-dot--${task.status}`}
/>
<span>{task.title}</span>
</button>
))}
</div>
{selectedTask && (
<article className="conversation-task-details">
<header>
<span>
<strong>{selectedTask.title}</strong>
<small>
{t(`task.status.${selectedTask.status}`)}
</small>
</span>
{selectedTask.status === 'failed' && (
<CircleAlert aria-hidden="true" size={15} />
)}
</header>
<dl>
<div>
<dt>{t('task.fields.mode')}</dt>
<dd>
{selectedSchedule
? t(`task.mode.${selectedSchedule.workMode}`)
: selectedTask.workMode
? t(`task.mode.${selectedTask.workMode}`)
: t('task.mode.unavailable')}
</dd>
</div>
<div>
<dt>{t('task.fields.schedule')}</dt>
<dd>
{selectedSchedule
? t(
`sidebar.tasks.schedule.recurrence.${selectedSchedule.recurrence}`
)
: t('task.schedule.none')}
</dd>
</div>
<div>
<dt>{t('task.fields.nextRun')}</dt>
<dd>
{selectedSchedule
? new Date(
selectedSchedule.nextRunAt
<span
aria-hidden="true"
className={`task-status-dot task-status-dot--${task.status}`}
/>
<span>{task.title}</span>
</button>
))}
</div>
{selectedTask && (
<article className="conversation-task-details">
<header>
<span>
<strong>{selectedTask.title}</strong>
<small>
{t(`task.status.${selectedTask.status}`)}
</small>
</span>
{selectedTask.status === 'failed' && (
<CircleAlert aria-hidden="true" size={15} />
)}
</header>
<dl>
<div>
<dt>{t('task.fields.mode')}</dt>
<dd>
{selectedSchedule
? t(`task.mode.${selectedSchedule.workMode}`)
: selectedTask.workMode
? t(`task.mode.${selectedTask.workMode}`)
: t('task.mode.unavailable')}
</dd>
</div>
<div>
<dt>{t('task.fields.schedule')}</dt>
<dd>
{selectedSchedule
? t(
`sidebar.tasks.schedule.recurrence.${selectedSchedule.recurrence}`
)
: t('task.schedule.none')}
</dd>
</div>
<div>
<dt>{t('task.fields.nextRun')}</dt>
<dd>
{selectedSchedule
? new Date(
selectedSchedule.nextRunAt
).toLocaleString(locale)
: t('task.notAvailable')}
</dd>
</div>
<div>
<dt>{t('task.fields.outcome')}</dt>
<dd>
{selectedTask.error ??
(selectedTask.completedAt
? t('task.completedAt', {
time: new Date(
selectedTask.completedAt
).toLocaleString(locale)
: t('task.notAvailable')}
</dd>
</div>
<div>
<dt>{t('task.fields.outcome')}</dt>
<dd>
{selectedTask.error ??
(selectedTask.completedAt
? t('task.completedAt', {
time: new Date(
selectedTask.completedAt
).toLocaleString(locale)
})
: t('task.noOutcome'))}
</dd>
</div>
</dl>
{selectedSchedule && (
<div className="conversation-task-details__actions">
<TaskScheduleActions
onError={setActionError}
onRemoveSchedule={onRemoveSchedule}
onRunSchedule={onRunSchedule}
onSetScheduleEnabled={onSetScheduleEnabled}
schedule={selectedSchedule}
taskTitle={selectedTask.title}
/>
</div>
)}
</article>
})
: t('task.noOutcome'))}
</dd>
</div>
</dl>
{selectedSchedule && (
<div className="conversation-task-details__actions">
<TaskScheduleActions
onError={setActionError}
onRemoveSchedule={onRemoveSchedule}
onRunSchedule={onRunSchedule}
onSetScheduleEnabled={onSetScheduleEnabled}
schedule={selectedSchedule}
taskTitle={selectedTask.title}
/>
</div>
)}
</>
</article>
)}
{actionError && (
<p className="conversation-task-strip__error" role="alert">
@@ -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()
+17 -17
View File
@@ -640,19 +640,9 @@ export function RightAssistantSidebar({
) : null}
{tab === 'tasks' && (
<section className="assistant-sidebar__section">
<div className="task-center__heading">
<p className="assistant-sidebar__section-description">
{t('sidebar.tasks.description')}
</p>
<button
className="primary-button"
onClick={onCreateCustomTask}
type="button"
>
<Plus aria-hidden="true" size={13} />
{t('sidebar.tasks.createCustom')}
</button>
</div>
<p className="assistant-sidebar__section-description">
{t('sidebar.tasks.description')}
</p>
<h3>
<ShieldAlert size={15} />
{t('sidebar.tasks.approvalsTitle')}
@@ -694,10 +684,20 @@ export function RightAssistantSidebar({
))
)}
<h3>
<ClockFading size={15} />
{t('sidebar.tasks.taskIndexTitle')}
</h3>
<div className="task-center__index-heading">
<h3>
<ClockFading size={15} />
{t('sidebar.tasks.taskIndexTitle')}
</h3>
<button
className="secondary-button task-center__create"
onClick={onCreateCustomTask}
type="button"
>
<Plus aria-hidden="true" size={13} />
{t('taskStrip.create')}
</button>
</div>
<div className="task-center__filters">
<SegmentedControl
ariaLabel={t('sidebar.tasks.filters.ariaLabel')}
@@ -192,6 +192,39 @@ describe('WorkspacePrimitives', () => {
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', () => {
@@ -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: {
@@ -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: {
+41 -48
View File
@@ -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,