fix(agents): open cards from home and align sidebar navigation

This commit is contained in:
鲨鱼辣椒 committed 2026-10-12 20:06:22 +08:00
1 parent e5d79bf37a
commit 5e99804bc2
13 files changed
+460 -27

No files matched your search

+1
View File
@@ -79,6 +79,7 @@ export function MainLayout() {
<TitleBar
integrated
pageTitle={isCloudAgents ? '我的智能体' : undefined}
showModuleReturn={isCloudAgents}
workspaceLayout={isChatWorkspace}
overlay={isPaintingModule}
showSidebarControls={!isCanvasWorkspace}
+3 -1
View File
@@ -12,6 +12,7 @@ export function ModuleSwitcher({ sidebarCollapsed, compact = false, toolbar = fa
?? `${module.subtitle.replace(/^AI\s*/u, '')} ${module.title.replace(/^Makelore\s+/u, '')}`;
const activeModuleLabel = getModuleLabel(activeModule);
const compactTrigger = compact && !sidebarCollapsed;
const agentReturn = activeModuleId === 'cloud_agents' && toolbar && iconOnly;
return (
<div data-testid="sidebar-module-switcher" className={cn('relative', toolbar && 'min-w-0 flex-1')}>
@@ -30,12 +31,13 @@ export function ModuleSwitcher({ sidebarCollapsed, compact = false, toolbar = fa
: 'flex min-h-11 w-full justify-between gap-2 rounded-2xl p-3 text-sm',
sidebarCollapsed ? 'justify-center px-0' : '',
toolbar && iconOnly && 'w-8 justify-center px-0',
agentReturn && 'rounded-full bg-background/70 text-brand',
)}
>
<ArrowLeft
data-testid="sidebar-module-return-icon"
aria-hidden="true"
className="h-4 w-4 shrink-0 -translate-y-px text-muted-foreground transition-[color,transform] duration-150 group-hover:-translate-x-0.5 group-hover:text-brand"
className={cn('h-4 w-4 shrink-0 -translate-y-px transition-[color,transform] duration-150 group-hover:-translate-x-0.5 group-hover:text-brand', agentReturn ? 'text-brand' : 'text-muted-foreground')}
/>
{!sidebarCollapsed && !iconOnly ? <span className={cn('min-w-0 whitespace-nowrap leading-4', toolbar && 'truncate')}>{activeModuleLabel}</span> : null}
</button>
+2 -2
View File
@@ -404,7 +404,7 @@ export function Sidebar({
)}
>
{workspaceLayout && !sidebarPinnedCollapsed && <ClassroomSidebarResizer />}
{!isProjectConfigPage && <div
{!isProjectConfigPage && activeModule !== 'cloud_agents' && <div
className={cn(
'bg-transparent px-3 text-foreground',
workspaceLayout && 'classroom-sidebar-header',
@@ -502,7 +502,7 @@ export function Sidebar({
</>
) : (
activeModule === 'cloud_agents' ? <div data-testid="sidebar-cloud-agents-navigation" className="px-2 py-3">
<button type="button" aria-label="我的智能体" onClick={() => navigate('/cloud-agents')}
<button type="button" aria-label="我的智能体" onClick={() => 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')}>
<Bot className="h-4 w-4 shrink-0 text-brand" />
+26 -19
View File
@@ -3,7 +3,7 @@
* macOS: light application toolbar with native traffic lights overlaid by the
* full-size hidden title bar.
* Windows: drag region with custom minimize/maximize/close controls.
* Linux: use native window chrome (no custom title bar).
* Linux: retain native window chrome; Agents adds an application navigation row.
*/
import { useState, useEffect } from 'react';
import { createPortal } from 'react-dom';
@@ -27,23 +27,25 @@ type TitleBarProps = {
overlay?: boolean;
showSidebarControls?: boolean;
pageTitle?: string;
showModuleReturn?: boolean;
sidebarPeekOpen?: boolean;
onSidebarPeekChange?: SidebarPeekChange;
};
export function TitleBar({ integrated = false, workspaceLayout = false, overlay = false, showSidebarControls = true, pageTitle, sidebarPeekOpen = false, onSidebarPeekChange }: TitleBarProps) {
export function TitleBar({ integrated = false, workspaceLayout = false, overlay = false, showSidebarControls = true, pageTitle, showModuleReturn = false, sidebarPeekOpen = false, onSidebarPeekChange }: TitleBarProps) {
const platform = window.electron?.platform;
if (platform === 'darwin') {
return <ProductTitleBar integrated={integrated} workspaceLayout={workspaceLayout} overlay={overlay} showSidebarControls={showSidebarControls} pageTitle={pageTitle} nativeTrafficLights sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={onSidebarPeekChange} />;
return <ProductTitleBar integrated={integrated} workspaceLayout={workspaceLayout} overlay={overlay} showSidebarControls={showSidebarControls} pageTitle={pageTitle} showModuleReturn={showModuleReturn} nativeTrafficLights sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={onSidebarPeekChange} />;
}
// Linux keeps the native frame/title bar for better IME compatibility.
// Linux keeps its native window frame. Agents still needs an application
// navigation row that remains reachable when the sidebar is collapsed.
if (platform !== 'win32') {
return null;
return showModuleReturn ? <ProductTitleBar integrated={integrated} overlay={overlay} showSidebarControls={showSidebarControls} pageTitle={pageTitle} showModuleReturn sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={onSidebarPeekChange} /> : null;
}
return <WindowsTitleBar integrated={integrated} workspaceLayout={workspaceLayout} overlay={overlay} showSidebarControls={showSidebarControls} pageTitle={pageTitle} sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={onSidebarPeekChange} />;
return <WindowsTitleBar integrated={integrated} workspaceLayout={workspaceLayout} overlay={overlay} showSidebarControls={showSidebarControls} pageTitle={pageTitle} showModuleReturn={showModuleReturn} sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={onSidebarPeekChange} />;
}
function ProductTitleBar({
@@ -52,6 +54,7 @@ function ProductTitleBar({
overlay = false,
showSidebarControls = true,
pageTitle,
showModuleReturn = false,
children,
windowControlsWidth = 0,
nativeTrafficLights = false,
@@ -63,6 +66,7 @@ function ProductTitleBar({
overlay?: boolean;
showSidebarControls?: boolean;
pageTitle?: string;
showModuleReturn?: boolean;
children?: React.ReactNode;
windowControlsWidth?: number;
nativeTrafficLights?: boolean;
@@ -91,6 +95,8 @@ function ProductTitleBar({
// keeps project conversations in the sidebar and must preserve its title rail.
const sidebarPreviewTitlebarVisible = sidebarCollapsed && sidebarPeekOpen && !workspaceLayout && !overlay;
const sidebarTitlebarHidden = sidebarCollapsed && !sidebarPreviewTitlebarVisible;
const agentNavigationWidth = sidebarTitlebarHidden ? (nativeTrafficLights && !nativeFullscreen ? 168 : 88) : 256;
const navigationWidth = workspaceLayout ? classroomTitlebarWidth : showModuleReturn ? agentNavigationWidth : undefined;
const sidebarToggle = (
<button
type="button"
@@ -101,13 +107,13 @@ function ProductTitleBar({
className={cn(
toolbarButtonClass,
'pointer-events-auto absolute top-1 z-[401]',
workspaceLayout ? 'right-2' : nativeTrafficLights ? 'left-[88px]' : 'left-2',
workspaceLayout || showModuleReturn ? 'right-2' : nativeTrafficLights ? 'left-[88px]' : 'left-2',
)}
onClick={toggleSidebar}
onPointerEnter={() => handleSidebarPeekEnter('toggle')}
onPointerLeave={() => handleSidebarPeekLeave('toggle')}
onFocus={!workspaceLayout ? () => handleSidebarPeekEnter('focus') : undefined}
onBlur={!workspaceLayout ? () => handleSidebarPeekLeave('focus') : undefined}
onFocus={!workspaceLayout && !showModuleReturn ? () => handleSidebarPeekEnter('focus') : undefined}
onBlur={!workspaceLayout && !showModuleReturn ? () => handleSidebarPeekLeave('focus') : undefined}
>
{sidebarCollapsed ? <PanelLeft className="h-4 w-4" /> : <PanelLeftClose className="h-4 w-4" />}
</button>
@@ -117,18 +123,18 @@ function ProductTitleBar({
data-testid="titlebar-sidebar-toggle-layer"
className={cn(
'no-drag pointer-events-auto fixed left-0 top-0 z-[400] h-10',
!workspaceLayout && (nativeTrafficLights ? 'w-[132px]' : 'w-[52px]'),
!workspaceLayout && !showModuleReturn && (nativeTrafficLights ? 'w-[132px]' : 'w-[52px]'),
)}
style={workspaceLayout ? { width: classroomTitlebarWidth } : undefined}
onPointerEnter={workspaceLayout ? () => handleSidebarPeekEnter('titlebar') : undefined}
onPointerLeave={workspaceLayout ? () => handleSidebarPeekLeave('titlebar') : undefined}
onFocusCapture={workspaceLayout ? () => handleSidebarPeekEnter('focus') : undefined}
onBlurCapture={workspaceLayout ? (event) => {
style={navigationWidth !== undefined ? { width: navigationWidth } : undefined}
onPointerEnter={workspaceLayout || showModuleReturn ? () => handleSidebarPeekEnter('titlebar') : undefined}
onPointerLeave={workspaceLayout || showModuleReturn ? () => handleSidebarPeekLeave('titlebar') : undefined}
onFocusCapture={workspaceLayout || showModuleReturn ? () => handleSidebarPeekEnter('focus') : undefined}
onBlurCapture={workspaceLayout || showModuleReturn ? (event) => {
if (!event.currentTarget.contains(event.relatedTarget as Node | null)) handleSidebarPeekLeave('focus');
} : undefined}
>
{workspaceLayout ? <div
data-testid="classroom-titlebar-navigation"
{workspaceLayout || showModuleReturn ? <div
data-testid={workspaceLayout ? 'classroom-titlebar-navigation' : 'agent-titlebar-navigation'}
className={cn('flex h-full items-center gap-2 pr-12', nativeTrafficLights && !nativeFullscreen ? 'pl-[88px]' : 'pl-3')}
>
<ModuleSwitcher sidebarCollapsed={false} toolbar iconOnly />
@@ -175,7 +181,7 @@ function ProductTitleBar({
<>
<div
data-testid="titlebar-sidebar-surface"
style={workspaceLayout ? { width: classroomTitlebarWidth, minWidth: classroomTitlebarWidth, flexBasis: classroomTitlebarWidth } : undefined}
style={navigationWidth !== undefined ? { width: navigationWidth, minWidth: navigationWidth, flexBasis: navigationWidth } : undefined}
className={cn(
'flex h-full shrink-0 items-center gap-1 px-2',
workspaceLayout
@@ -251,7 +257,7 @@ function ProductTitleBar({
);
}
function WindowsTitleBar({ integrated, workspaceLayout, overlay, showSidebarControls, pageTitle, sidebarPeekOpen, onSidebarPeekChange }: Omit<TitleBarProps, 'integrated'> & { integrated: boolean }) {
function WindowsTitleBar({ integrated, workspaceLayout, overlay, showSidebarControls, pageTitle, showModuleReturn, sidebarPeekOpen, onSidebarPeekChange }: Omit<TitleBarProps, 'integrated'> & { integrated: boolean }) {
const [maximized, setMaximized] = useState(false);
useEffect(() => {
@@ -284,6 +290,7 @@ function WindowsTitleBar({ integrated, workspaceLayout, overlay, showSidebarCont
overlay={overlay}
showSidebarControls={showSidebarControls}
pageTitle={pageTitle}
showModuleReturn={showModuleReturn}
windowControlsWidth={WINDOWS_TITLEBAR_CONTROLS_WIDTH}
sidebarPeekOpen={sidebarPeekOpen}
onSidebarPeekChange={onSidebarPeekChange}
+1
View File
@@ -83,6 +83,7 @@ function AgentWorkspace() {
cloudAgentsApi.recovery().then(async state => {
if (!live) return;
setPending(state.pending);
// Explicit list entries keep the recent workspace pointer without reopening it.
if (!state.recent || location.pathname.includes('/shared/') || new URLSearchParams(location.search).has('view')) return;
const pointer = state.recent;
// Creator drafts may be unpublished; shared entry revalidates current access separately.
+1 -1
View File
@@ -44,7 +44,7 @@ export function ModuleSelection({ authRequired = true }: { authRequired?: boolea
navigate('/login');
return;
}
navigate(route);
navigate(route === '/cloud-agents' ? `${route}?view=mine` : route);
};
return (