fix(agents): open cards from home and align sidebar navigation
This commit is contained in:
1 parent
e5d79bf37a
commit
5e99804bc2
13 files changed
+460
-27
No files matched your search
@@ -79,6 +79,7 @@ export function MainLayout() {
|
||||
<TitleBar
|
||||
integrated
|
||||
pageTitle={isCloudAgents ? '我的智能体' : undefined}
|
||||
showModuleReturn={isCloudAgents}
|
||||
workspaceLayout={isChatWorkspace}
|
||||
overlay={isPaintingModule}
|
||||
showSidebarControls={!isCanvasWorkspace}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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 (
|
||||
|
||||
Reference in new issue
Block a user