diff --git a/UI-DESIGN.md b/UI-DESIGN.md index eae0a442..05a6fef8 100644 --- a/UI-DESIGN.md +++ b/UI-DESIGN.md @@ -333,9 +333,13 @@ 应用顶栏用于当前会话标题、范围、窗口级状态、侧栏开关和低频全局操作,不承担主要导航。 顶栏必须保持紧凑,不能与页面内容争夺注意力。 -- 无边框窗口使用一条紧凑标题栏,保留侧栏开关、当前会话标题、范围、Runtime 状态、主题 - 开关和窗口控制,不得为了新增工作栏入口把这一整组控件下移。普通图标按钮使用 +- 应用壳层在水平方向分为全高主侧栏和右侧内容框架。主侧栏从窗口顶部贯穿到底部,品牌区、 + 项目切换器、导航和账户区保持同一连续表面;不能用全宽标题栏把整条侧栏向下推移。 +- 无边框窗口的紧凑标题栏只覆盖右侧内容框架,保留侧栏开关、当前会话标题、范围、Runtime + 状态、主题开关和窗口控制,不得为了新增工作栏入口把这一整组控件下移。普通图标按钮使用 `34px × 34px` 点击区域。 +- 发布说明 Modal 与其他应用级 Modal 一样使用整窗遮罩;遮罩必须覆盖主侧栏、右侧内容和 + 顶栏,不能保留一条未被遮罩的窗口控制色块。关闭操作由 Modal 自身提供。 - 当前会话存在 Task 时,Task 上下文栏位于标题栏下方并承载任务摘要与详情;没有 Task 时 整行隐藏。折叠状态保持为 `46px` 单行表面,与右侧工作栏标签栏等高,左右分隔线必须水平 对齐。该栏不重复放置新建入口。 diff --git a/docs/prd/assistant-experience/assistant-workbar-and-execution-spaces-prd.md b/docs/prd/assistant-experience/assistant-workbar-and-execution-spaces-prd.md index eaa7326c..1b83a617 100644 --- a/docs/prd/assistant-experience/assistant-workbar-and-execution-spaces-prd.md +++ b/docs/prd/assistant-experience/assistant-workbar-and-execution-spaces-prd.md @@ -221,6 +221,8 @@ Runtime - 分栏计算排除最左侧主导航,主工作区与工作栏使用相同的 `300px` 最小宽度。拖动记录两侧 在可用分栏宽度中的比例;任一侧达到最小值后停止,窗口缩放后按原比例重新计算并再次 应用同一最小值约束。 +- 最左侧主导航保持全高连续表面;窗口标题栏只覆盖右侧内容框架,不能横跨主导航并把品牌、 + 项目切换器和导航整体向下推移。 - 对话内容与输入区始终保留对称的左右安全边距,工作栏边界不得覆盖或裁切输入框、发送 按钮及主工作区右侧边距。 - 唯一展开与收起开关固定在窗口关闭按钮正下方。工作栏内部不重复关闭入口。开关图标中的 diff --git a/src/renderer/src/App.test.tsx b/src/renderer/src/App.test.tsx index ed75c8a7..5722258c 100644 --- a/src/renderer/src/App.test.tsx +++ b/src/renderer/src/App.test.tsx @@ -8028,8 +8028,17 @@ describe('App', () => { const assistantTrigger = screen.getByLabelText('切换助手工作栏') expect(assistantTrigger.parentElement).toBe( + container.querySelector('.app-content') + ) + expect(container.querySelector('.sidebar')?.parentElement).toBe( container.querySelector('.app-shell') ) + expect(container.querySelector('.app-frame')?.parentElement).toBe( + container.querySelector('.app-shell') + ) + expect(container.querySelector('.topbar')?.parentElement).toBe( + container.querySelector('.app-frame') + ) expect(assistantTrigger).toHaveClass('assistant-sidebar-toggle') expect(assistantTrigger).not.toHaveClass('icon-button--active') expect(assistantTrigger).toHaveAttribute('aria-expanded', 'false') @@ -8582,7 +8591,7 @@ describe('App', () => { name: '助手工作栏' }) expect(workbar).toHaveClass('assistant-sidebar--open') - expect(workbar.parentElement).toHaveClass('app-shell') + expect(workbar.parentElement).toHaveClass('app-content') expect( workbar.parentElement?.querySelector('main.workspace') ).toBeInTheDocument() diff --git a/src/renderer/src/App.tsx b/src/renderer/src/App.tsx index 16bd77aa..ab790e0d 100644 --- a/src/renderer/src/App.tsx +++ b/src/renderer/src/App.tsx @@ -7389,114 +7389,7 @@ function App(): React.JSX.Element { const backgroundIsolated = mainSidebarOpen return ( -
-
- - {view === 'chat' && ( - <> -
- - {activeConversation - ? getConversationDisplayTitle( - activeConversation, - t('conversation.defaultTitle') - ) - : - (activeProject?.kind === 'channel' - ? t('conversation.remoteTitle') - : t('conversation.defaultTitle'))} - - {activeConversation?.branch && ( - - )} - {activeConversation?.remote && ( - - { - projectChannelLabels[ - activeConversation.remote.channel - ] - } - - )} -
- - - )} -
- - - - {runtime?.label ?? t('runtime.detecting')} - - {runtime?.capability === 'image-generation' && ( - - {t('runtime.imageGeneration')} - - )} - - -
- -
-
+
) diff --git a/src/renderer/src/WorkspacePrimitives.test.tsx b/src/renderer/src/WorkspacePrimitives.test.tsx index bb1659fe..f8577c8a 100644 --- a/src/renderer/src/WorkspacePrimitives.test.tsx +++ b/src/renderer/src/WorkspacePrimitives.test.tsx @@ -199,7 +199,16 @@ describe('WorkspacePrimitives', () => { /\.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 + /\.app-frame\s*\{[^}]*display:\s*grid;[^}]*flex:\s*1;[^}]*grid-template-rows:\s*58px minmax\(0,\s*1fr\);/u + ) + expect(stylesheet).toMatch( + /\.app-shell\s*\{[^}]*display:\s*flex;[^}]*width:\s*100%;[^}]*height:\s*100%;/u + ) + expect(stylesheet).toMatch( + /\.release-notes-backdrop\s*\{[^}]*position:\s*fixed;[^}]*background:\s*var\(--overlay-backdrop\);[^}]*inset:\s*0;/u + ) + expect(stylesheet).toMatch( + /\.app-content\s*\{[^}]*position:\s*relative;[^}]*display:\s*flex;[^}]*width:\s*100%;[^}]*height:\s*100%;/u ) expect(stylesheet).toMatch( /\.workspace\s*\{[^}]*grid-template-rows:\s*minmax\(0,\s*1fr\);/u diff --git a/src/renderer/src/styles.css b/src/renderer/src/styles.css index ad272160..1f0fdf48 100644 --- a/src/renderer/src/styles.css +++ b/src/renderer/src/styles.css @@ -2270,8 +2270,8 @@ textarea:focus-visible { .app-frame { position: relative; display: grid; - width: 100%; height: 100%; + flex: 1; min-width: 0; min-height: 0; background: var(--surface-canvas); @@ -2290,6 +2290,17 @@ textarea:focus-visible { color: var(--text-primary); } +.app-content { + position: relative; + display: flex; + width: 100%; + height: 100%; + min-width: 0; + min-height: 0; + background: var(--surface-canvas); + color: var(--text-primary); +} + .sidebar { display: flex; flex: 0 0 278px; @@ -5411,7 +5422,7 @@ button > svg { display: grid; padding: var(--space-4); background: var(--overlay-backdrop); - inset: 38px 0 0; + inset: 0; place-items: center; } @@ -12558,7 +12569,7 @@ details.settings-section > :not(summary) + :not(summary) { --shadow-dialog: 0 18px 48px rgb(0 4 10 / 48%); } -:root[data-theme='dark'] :where(.app-frame, .app-shell) { +:root[data-theme='dark'] :where(.app-frame, .app-shell, .app-content) { background: var(--surface-canvas); color: var(--text-primary); }