diff --git a/.project-docs/30-worklog/tasks/20261012-agent-sidebar-manual-67c1a8e4.md b/.project-docs/30-worklog/tasks/20261012-agent-sidebar-manual-67c1a8e4.md new file mode 100644 index 00000000..c68d5ce1 --- /dev/null +++ b/.project-docs/30-worklog/tasks/20261012-agent-sidebar-manual-67c1a8e4.md @@ -0,0 +1,81 @@ +# Task: Keep Agent sidebar collapsed until explicit expansion + +## Identity + +- Task ID: 20261012-agent-sidebar-manual-67c1a8e4 +- Mode: Feature +- Branch: codex/20261012-agent-sidebar-manual-67c1a8e4-agent-sidebar-manual +- Worktree: /Users/chillishark/Makelore 麦洛/.codex-worktrees/makelore/20261012-agent-sidebar-manual-67c1a8e4 +- Base commit: a340c0fd94ff5711f00a08312e80db4b148ff101 +- Owner: codex +- Status: Ready for Integration + +## Scope + +- Remove the advanced-settings resource policy helper sentence. Keep the Agent sidebar collapsed on pointer/focus arrival; explicit toggle remains the only expansion action. Refresh the “我的智能体” and “渠道” SVG icons in the existing visual language, preserving labels and routes. + +## Intent And Constraints + +- User confirmed the requested icons are the sidebar “我的智能体” and “渠道”, not the top return/collapse controls. Product-language communication; no temporary app update. Preserve return/collapse alignment, cards-first entry, model/resource choices and permission enforcement. Restrict changed hover policy to cloud_agents; Code and other modules keep existing peek. No real cloud saves/publish/model requests during checks; only isolated fixtures. + +## Project Context Loaded + +Task context: +- Task ID: 20261012-agent-sidebar-manual-67c1a8e4; Feature; owner codex. +- Branch: codex/20261012-agent-sidebar-manual-67c1a8e4-agent-sidebar-manual. +- Worktree: /Users/chillishark/Makelore 麦洛/.codex-worktrees/makelore/20261012-agent-sidebar-manual-67c1a8e4. +- Base: a340c0fd94ff5711f00a08312e80db4b148ff101, completed conversation polish including cards-first navigation. +- Bundled check/start/status passed and ownership/reservation exactly match. 37 peer records read-only: 36 concrete scopes, one old child-language record undefined. No blocking semantic conflict. Relevant overlaps: entry-nav same-row return/toggle and cards-first behavior, config-simplify model/resource controls and existing permission semantics, Code hover/resize behavior outside Agents. + +Read: +- AGENTS.md; 05-agent-entry read-before-planning, memory-index and planning-gate; own task record. +- 00-brief positioning/success; 20-architecture community-collaboration; task-relevant current-state integrated snapshot, decision-index/system-overview and full personal-cloud-agents ADR. +- Task-relevant domain/evidence/reflection/commitments indexes; scopes/constraints/promotion candidates of every peer. +- Current MainLayout, Sidebar, TitleBar, ConfigurationFields and focused navigation/peek checks. Design skill search; adapt its friendly/icon consistency guidance to the existing cream/pastel/orange product language. + +Relevant understanding: +- Project serves students/creators with creation and AI, excludes courses in the client. Main/WS/Yuxi own authorization, resources, accounting and run state; removing text does not change policy. +- Integrated snapshot predates current simplified Agents UI. Human-approved base is authoritative for this feature; no canonical docs are edited in Feature mode. +- Existing collapse preview listens across toggle, titlebar, focus and sidebar zones. Agents must have no transient preview, including a preview left over from another route. Explicit toggle and keyboard activation remain accessible. +- Navigation labels/routes, current-page indication, return control/native window safe spaces and non-Agent preview remain intact. Personal cloud resource selections/enforcement unchanged. +- Evidence commitments distinguish fixture/compiled source from real account, paid provider or installed app acceptance. User expressly forbids trial update; no live app operation is needed. +- Likely files: MainLayout hover policy, Sidebar scoped SVG presentation, ConfigurationFields one sentence, README and focused behavior checks. +- Unknowns resolved: user clarified both sidebar icon positions; no prescribed replacement artwork, use consistent existing SVG set. Old undefined peer has no concrete dependency. + +Gate result: +- Passed. + +## Plan + +1. Scope sidebar-preview suppression to Agents and clear stale transient state across route changes; preserve explicit mouse/keyboard toggle and other modules. +2. Delete only the resource helper sentence and refresh the two sidebar icons without changing navigation or policy. +3. Verify hover/toggle/route transitions, resource UI and relevant Electron fixture, typecheck/lint/build; update task-scoped evidence and save changes. Do not update the temporary app. + +## Outcome + +- Removed only the advanced-settings resource-policy helper sentence. Model requirements, resource selection and underlying access enforcement are unchanged. +- Agents, including channels and conversation/configuration routes, now disable transient sidebar preview. Hovering the toggle, titlebar or left edge and focusing the toggle keep a collapsed sidebar hidden. Clicking the toggle or activating it with the keyboard still changes the persisted collapsed preference. +- Entering/leaving the Agents module resets preview sources and cancels pending close timers. A preview from Code cannot appear in Agents or revive after returning; Code preview and the fixed expanded Canvas sidebar remain available. +- Replaced the two confirmed sidebar icons with a pale-purple conversation robot for “我的智能体” and a pale-blue connection-node icon for “渠道”. Labels, current-page indication and destinations are preserved. Retained the separate Bot icon used by the robot module. +- README and focused checks reflect the final behavior. No temporary application, runtime pointer, manifest, user profile or cloud data was updated. + +## Verification + +- `typecheck`: passed after preserving the robot module's shared Bot import. The initial check caught that missing import; it was corrected before the final build and UI check. +- Scoped ESLint for MainLayout, Sidebar, ConfigurationFields, the sidebar-preview unit check and the Electron check: passed. `git diff --check`: passed. +- `build:vite`: passed on the final source. Build log: `/tmp/makelore-agent-sidebar-manual-build.log`. +- `cloud-agents-page.test.tsx`: 31 checks passed. MainLayout sidebar-preview and module-gate checks: 18 passed. Covers both Agent routes, all four preview sources, explicit toggle, module transitions, pending timers, unchanged Code preview and Canvas expansion. +- Production Electron isolated fixture (`tests/e2e/cloud-agents.spec.ts`): 1 scenario passed. Actual shell checks confirm hovering the toggle/titlebar/left edge and focusing do not open the sidebar; Enter and click do. The removed helper is absent; existing create/configure/resource/save/start-chat checks still pass. +- Visually reviewed `cloud-agent-sidebar-icons.png` and `cloud-agent-advanced-model.png` from `.tmp/agent-sidebar-manual-electron/cloud-agents-personal-clou-c4180--published-chat-in-Electron/`. Both sidebar icons are visible with distinct pastel badges and unchanged readable labels. +- Verification uses an isolated test profile and fixture-owned Host API responses. This is source/fixture evidence, not acceptance in the user's logged-in temporary app or a real cloud/model call. User expressly requested no temporary app update. +- Read-only peer review found no actionable issues: route boundary covers Agent subpages, sources/timers are cleared, explicit toggle remains available, other modules and shared robot icon are preserved. +- Bundled `check_doc_drift.py --task-id 20261012-agent-sidebar-manual-67c1a8e4`: passed; changes remain inside Feature ownership boundaries. + +## Follow-ups + +- Preserve this task branch/worktree for integration. Include its recorded base and preceding Agent page changes when integrating; do not replace another active checkout. +- Temporary app update remains deferred until the user requests it. No cloud service change is needed for this task. + +## Promotion Candidates + +- Target: integrated current state and Agent navigation/UI guidance. Proposal: Agents alone use explicit sidebar expansion; other modules retain their existing preview behavior. Record the removed resource-policy helper as a presentation change, with policy enforcement unchanged. Evidence: user's explicit request, confirmed icon locations, 18 MainLayout checks and production Electron scenario. Future impact: subsequent shared-sidebar changes should preserve this per-module interaction. Semantic conflicts: older generic hover-preview descriptions need the Agents exception during serialized integration. Human confirmation: the behavior is already authorized by this conversation; canonical edits still require Integration mode. diff --git a/README.md b/README.md index a10576b2..967c88e0 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Makelore 是一个面向软件、视觉创作、智能机器人与个人云智 - `Makelore Code|AI 编程`:管理本地项目、项目智能体、对话、文件上下文、代码变更和运行时。 会话工作台采用浅蓝项目栏、彩色通用手柄图标与卡片选中态;中间对话/作品使用圆角标签,作品预览的后退、前进、刷新与网址输入统一放在标签右侧;只在作品页显示,窄窗口自动换行。右侧智能体咨询使用奶油色标题区。沿用真实项目和会话入口、侧栏伸缩与账户菜单,样式只作用于 Code 会话工作台。可编辑的空对话在输入区上方的可用区域中央显示童趣欢迎语;「去作品广场逛逛」入口紧贴输入框上方,两者一起位于底部。老师介绍以固定宽度浮在右上角,不占用中间布局;欢迎语和底部输入区始终相对项目栏右侧的整个内容区域居中;小窗口的欢迎内容独立滚动;发送首条消息后收起欢迎区。侧栏保留「作品广场」入口,只有点击后才在系统浏览器打开公共广场,不请求、摘取或内嵌展示广场作品。打开广场保留当前项目、对话、草稿和附件,不自动发送消息;老师求助按钮保留临时应用的最新文案「帮我整体看🧠」。 -- `Makelore Agents|AI 智能体`:从模块首页进入时先打开“我的智能体”卡片列表,保留此前对话与操作恢复记录;返回首页箭头与侧栏折叠按钮同排,折叠后仍可返回或展开,macOS 原生窗口按钮留出安全空间。首页围绕自己的智能体和创建入口,显示童趣问候“Hi,今天想创造还是想聊聊?”,不再显示更多、收到的分享、活动或归档筛选。卡片显示名称、可为空的一句话介绍及状态,支持聊天、调整、继续制作和确认删除;删除沿用云端归档机制,移出列表并停用新调用,历史和费用保留。创建先填写名称并选择平台像素头像或上传头像,下一步进入独立配置。顶部名称有编辑图标,头像可点击修改;未设置头像时使用同一张默认像素头像,从创建到配置、改名、保存和重新打开保持一致。基础页为“介绍与提示词”,一句话介绍可留空,完整提示词保留原始文本;模型作为高级设置第一项必选,未选择时不能开始使用,草稿可分步保存,不自动选模型。介绍与提示词采用浅蓝介绍卡和浅紫提示词卡;高级设置按模型、工具箱(Tool)、做事手册(Skill)、MCP 服务、子智能体和知识库纵向呈现,不再设内部分类切换;不展示 Tool/Skill 解释、云端能力管理、费用上限或运行限制卡片,保存其他配置时保留既有运行限制,费用规则不变。顶层页签切换保留搜索、资源选择、知识表单及未保存输入。配置页移除聊天、自动任务和使用与接入导航,独立聊天仍从卡片进入,微信账号仍在侧栏渠道管理。草稿明确保存(Ctrl/⌘+S);保存只更新草稿,“应用最新修改”使新配置生效,失败重试保持原操作身份;点击“开始使用”完成必要的私有启用后直接打开聊天,不再展示二次确认页;未保存修改、缺少模型、冲突或尚未确认的请求会留在配置页处理,打开聊天不自动发送消息。独立对话沿用奶油底色和浅蓝、浅紫、薄荷色:空对话展示当前智能体头像与名称,示例只填入草稿;用户消息和智能体回复区分呈现,长 Markdown、表格和代码在消息区内滚动,底部输入框、附件、停止和历史入口保留;短窗口中,配置完全生效且没有待处理问题时收起重复的已保存/已应用提示和禁用按钮,修改或待确认时恢复;“调整智能体”与客户端/微信来源同排,窄窗口会换行,未发送内容的离开保护不变。头像上传、Main-owned Host API、访问隔离、费用与不可变版本规则保持;介绍留空需要配套 Yuxi DraftCreate/DraftSave 的空字符串契约。 +- `Makelore Agents|AI 智能体`:从模块首页进入时先打开“我的智能体”卡片列表,保留此前对话与操作恢复记录;返回首页箭头与侧栏折叠按钮同排,折叠后仍可返回;智能体模块的侧栏只通过按钮点击或键盘激活展开,鼠标经过左侧、收纳按钮或顶栏不会展开,切换模块不沿用旧悬停预览;其他模块仍保留原预览方式。“我的智能体”使用浅紫对话机器人图标,“渠道”使用浅蓝连接节点图标。macOS 原生窗口按钮留出安全空间。首页围绕自己的智能体和创建入口,显示童趣问候“Hi,今天想创造还是想聊聊?”,不再显示更多、收到的分享、活动或归档筛选。卡片显示名称、可为空的一句话介绍及状态,支持聊天、调整、继续制作和确认删除;删除沿用云端归档机制,移出列表并停用新调用,历史和费用保留。创建先填写名称并选择平台像素头像或上传头像,下一步进入独立配置。顶部名称有编辑图标,头像可点击修改;未设置头像时使用同一张默认像素头像,从创建到配置、改名、保存和重新打开保持一致。基础页为“介绍与提示词”,一句话介绍可留空,完整提示词保留原始文本;模型作为高级设置第一项必选,未选择时不能开始使用,草稿可分步保存,不自动选模型。介绍与提示词采用浅蓝介绍卡和浅紫提示词卡;高级设置按模型、工具箱(Tool)、做事手册(Skill)、MCP 服务、子智能体和知识库纵向呈现,不再设内部分类切换;不展示 Tool/Skill 解释、资源开放规则提示、云端能力管理、费用上限或运行限制卡片,保存其他配置时保留既有运行限制,费用规则不变。顶层页签切换保留搜索、资源选择、知识表单及未保存输入。配置页移除聊天、自动任务和使用与接入导航,独立聊天仍从卡片进入,微信账号仍在侧栏渠道管理。草稿明确保存(Ctrl/⌘+S);保存只更新草稿,“应用最新修改”使新配置生效,失败重试保持原操作身份;点击“开始使用”完成必要的私有启用后直接打开聊天,不再展示二次确认页;未保存修改、缺少模型、冲突或尚未确认的请求会留在配置页处理,打开聊天不自动发送消息。独立对话沿用奶油底色和浅蓝、浅紫、薄荷色:空对话展示当前智能体头像与名称,示例只填入草稿;用户消息和智能体回复区分呈现,长 Markdown、表格和代码在消息区内滚动,底部输入框、附件、停止和历史入口保留;短窗口中,配置完全生效且没有待处理问题时收起重复的已保存/已应用提示和禁用按钮,修改或待确认时恢复;“调整智能体”与客户端/微信来源同排,窄窗口会换行,未发送内容的离开保护不变。头像上传、Main-owned Host API、访问隔离、费用与不可变版本规则保持;介绍留空需要配套 Yuxi DraftCreate/DraftSave 的空字符串契约。 云知识库的 embedding 模型只由平台管理员在 Yuxi 后台维护:在「智能体管理 → 模型供应商」启用供应商,按供应商要求填写有效的服务地址和 API Key,添加并启用 embedding 模型并填写实际向量维度;embedding 向量化费用由平台承担,不计入用户用量、词元点数或智能体费用上限;one-api 不参与 embedding 配置,Works Square 只负责其他应计费模型调用的创建者词元点数账本。客户端区分目录加载中、加载失败与无可用模型,空状态提供管理员配置说明;「刷新知识库与模型」保留未提交名称,未确认的创建请求沿用原操作身份。 diff --git a/src/components/layout/MainLayout.tsx b/src/components/layout/MainLayout.tsx index c9631c1c..94712efb 100644 --- a/src/components/layout/MainLayout.tsx +++ b/src/components/layout/MainLayout.tsx @@ -31,7 +31,7 @@ export function MainLayout() { const isCloudAgents = activeModule === 'cloud_agents'; const handleSidebarPeekChange = useCallback((open: boolean, source: SidebarPeekSource) => { - if (!sidebarCollapsed) return; + if (!sidebarCollapsed || isCloudAgents) return; const sources = sidebarPeekSourcesRef.current; if (sources[source] === open) return; @@ -55,19 +55,20 @@ export function MainLayout() { setSidebarPeekOpen(false); sidebarPeekCloseTimerRef.current = null; }, SIDEBAR_PEEK_CLOSE_DELAY_MS); - }, [sidebarCollapsed]); + }, [sidebarCollapsed, isCloudAgents]); useEffect(() => { sidebarPeekSourcesRef.current = { toggle: false, focus: false, sidebar: false, titlebar: false }; // This transient state mirrors the external persisted sidebar preference; - // reset it when that preference changes so a stale preview cannot reopen. + // reset it on preference changes and both edges of the Agents boundary so + // a previous module's preview or timer cannot reopen the manual-only sidebar. // eslint-disable-next-line react-hooks/set-state-in-effect setSidebarPeekOpen(false); if (sidebarPeekCloseTimerRef.current) { clearTimeout(sidebarPeekCloseTimerRef.current); sidebarPeekCloseTimerRef.current = null; } - }, [sidebarCollapsed]); + }, [sidebarCollapsed, isCloudAgents]); useEffect(() => () => { if (sidebarPeekCloseTimerRef.current) clearTimeout(sidebarPeekCloseTimerRef.current); @@ -83,8 +84,8 @@ export function MainLayout() { workspaceLayout={isChatWorkspace} overlay={isPaintingModule} showSidebarControls={!isCanvasWorkspace} - sidebarPeekOpen={isCanvasWorkspace ? false : sidebarPeekOpen} - onSidebarPeekChange={isCanvasWorkspace ? undefined : handleSidebarPeekChange} + sidebarPeekOpen={isCanvasWorkspace || isCloudAgents ? false : sidebarPeekOpen} + onSidebarPeekChange={isCanvasWorkspace || isCloudAgents ? undefined : handleSidebarPeekChange} /> {/* Below the title bar: sidebar + content */} @@ -93,8 +94,8 @@ export function MainLayout() { workspaceLayout={isChatWorkspace} hideOnCompact={isCanvasWorkspace} forceExpanded={isCanvasWorkspace} - sidebarPeekOpen={isCanvasWorkspace ? false : sidebarPeekOpen} - onSidebarPeekChange={isCanvasWorkspace ? undefined : handleSidebarPeekChange} + sidebarPeekOpen={isCanvasWorkspace || isCloudAgents ? false : sidebarPeekOpen} + onSidebarPeekChange={isCanvasWorkspace || isCloudAgents ? undefined : handleSidebarPeekChange} />
navigate('/cloud-agents?view=mine')} aria-current={location.pathname === '/cloud-agents' ? 'page' : undefined} className={cn('flex min-h-10 w-full items-center gap-2 rounded-lg px-2 py-2 text-left text-sm font-semibold hover:bg-surface-tertiary', location.pathname === '/cloud-agents' && 'bg-brand-soft', sidebarCollapsed && 'justify-center px-0')}> - + {!sidebarCollapsed && 我的智能体} :
diff --git a/src/pages/CloudAgents/ConfigurationFields.tsx b/src/pages/CloudAgents/ConfigurationFields.tsx index e26bca41..61b4ead7 100644 --- a/src/pages/CloudAgents/ConfigurationFields.tsx +++ b/src/pages/CloudAgents/ConfigurationFields.tsx @@ -76,6 +76,5 @@ export function ConfigurationFields({ slug, value, disabled, onChange, section = onCreated={kb => { change('knowledges', [...new Set([...value.knowledges, kb.kb_id])]); setRefresh(v => v + 1); }} />
} ; })} -

未选择的资源不会开放给此智能体。通过微信或 API 使用时也遵循本配置。

; } diff --git a/tests/e2e/cloud-agents.spec.ts b/tests/e2e/cloud-agents.spec.ts index a9773576..763d74b5 100644 --- a/tests/e2e/cloud-agents.spec.ts +++ b/tests/e2e/cloud-agents.spec.ts @@ -126,11 +126,28 @@ test('personal cloud Agent identity, configuration and separate published chat i expect(collapseBounds).not.toBeNull(); expect(Math.abs(returnBounds!.y - collapseBounds!.y)).toBeLessThanOrEqual(1); expect(returnBounds!.x + returnBounds!.width).toBeLessThan(collapseBounds!.x); + await page.screenshot({ path: testInfo.outputPath('cloud-agent-sidebar-icons.png') }); await navigation.getByRole('button', { name: '折叠侧栏', exact: true }).click(); - await page.mouse.move(700, 400); + const sidebar = page.getByTestId('sidebar'); + const sidebarToggle = page.getByTestId('titlebar-sidebar-toggle'); + await sidebarToggle.hover(); + await expect(sidebar).toHaveAttribute('aria-hidden', 'true'); + await expect(sidebarToggle).toHaveAttribute('aria-expanded', 'false'); + await page.getByTestId('titlebar-sidebar-toggle-layer').hover(); + await expect(sidebar).toHaveAttribute('aria-hidden', 'true'); + await page.mouse.move(3, 120); + await expect(sidebar).toHaveAttribute('aria-hidden', 'true'); + await sidebarToggle.focus(); + await expect(sidebar).toHaveAttribute('aria-hidden', 'true'); await expect(navigation.getByRole('button', { name: '返回首页', exact: true })).toBeVisible(); - await navigation.getByRole('button', { name: /展开侧栏|固定侧栏/ }).click(); + await expect(navigation.getByRole('button', { name: '展开侧栏', exact: true })).toBeVisible(); + await sidebarToggle.press('Enter'); await expect(page.getByTestId('sidebar-cloud-agents-navigation')).toBeVisible(); + await expect(sidebarToggle).toHaveAttribute('aria-expanded', 'true'); + await sidebarToggle.click(); + await expect(sidebar).toHaveAttribute('aria-hidden', 'true'); + await sidebarToggle.click(); + await expect(sidebar).toHaveAttribute('aria-hidden', 'false'); const fixtureState = () => app.evaluate(() => Reflect.get(globalThis, '__cloudAgentUiFixture').snapshot()); const messageOperations = async () => (await fixtureState()).operations.filter((operation: string) => operation === 'preview' || operation === 'submit'); await page.getByRole('button', { name: '创建我的第一个智能体', exact: true }).click(); @@ -192,6 +209,7 @@ test('personal cloud Agent identity, configuration and separate published chat i await expect(page.getByText('选它做事时能用的工具。', { exact: true })).toHaveCount(0); await expect(page.getByText('给它一本手册,照着方法做。', { exact: true })).toHaveCount(0); await expect(page.getByText('管理我的云端能力', { exact: true })).toHaveCount(0); + await expect(page.getByText('未选择的资源不会开放给此智能体。通过微信或 API 使用时也遵循本配置。', { exact: true })).toHaveCount(0); await expect(page.getByRole('group', { name: /^知识库/ })).toBeVisible(); await expect(page.getByRole('heading', { name: '费用上限', exact: true })).toHaveCount(0); await expect(page.getByRole('heading', { name: '运行限制', exact: true })).toHaveCount(0); diff --git a/tests/unit/main-layout-sidebar-peek.test.tsx b/tests/unit/main-layout-sidebar-peek.test.tsx index eff2f39a..3881dc06 100644 --- a/tests/unit/main-layout-sidebar-peek.test.tsx +++ b/tests/unit/main-layout-sidebar-peek.test.tsx @@ -1,5 +1,5 @@ import { act, fireEvent, render, screen } from '@testing-library/react'; -import { MemoryRouter, Route, Routes } from 'react-router-dom'; +import { MemoryRouter, Route, Routes, useLocation, useNavigate } from 'react-router-dom'; import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; import { MainLayout } from '@/components/layout/MainLayout'; import { codingWorkspaceStore } from '@/stores/coding-workspace'; @@ -8,6 +8,7 @@ import { useSettingsStore } from '@/stores/settings'; type PeekProps = { sidebarPeekOpen?: boolean; onSidebarPeekChange?: (open: boolean, source: 'toggle' | 'focus' | 'sidebar' | 'titlebar') => void; + forceExpanded?: boolean; }; vi.mock('@/components/layout/TitleBar', () => ({ @@ -17,8 +18,12 @@ vi.mock('@/components/layout/TitleBar', () => ({ type="button" data-testid="peek-trigger" data-peek-open={sidebarPeekOpen ? 'true' : 'false'} + aria-label={useSettingsStore.getState().sidebarCollapsed ? '展开侧栏' : '折叠侧栏'} + onClick={() => useSettingsStore.getState().setSidebarCollapsed(!useSettingsStore.getState().sidebarCollapsed)} onPointerEnter={() => onSidebarPeekChange?.(true, 'toggle')} onPointerLeave={() => onSidebarPeekChange?.(false, 'toggle')} + onFocus={() => onSidebarPeekChange?.(true, 'focus')} + onBlur={() => onSidebarPeekChange?.(false, 'focus')} />
({ })); vi.mock('@/components/layout/Sidebar', () => ({ - Sidebar: ({ sidebarPeekOpen, onSidebarPeekChange }: PeekProps) => ( + Sidebar: ({ sidebarPeekOpen, onSidebarPeekChange, forceExpanded }: PeekProps) => (