Share Code sidebar with conversation programming settings

This commit is contained in:
鲨鱼辣椒 committed 2026-10-14 11:33:59 +08:00
1 parent 5e3afa841a
commit 6bb15aec1f
7 files changed
+72 -27

No files matched your search

+5 -2
View File
@@ -12,6 +12,7 @@ import { getAiModuleForPath } from '@/lib/ai-modules';
import { useSettingsStore } from '@/stores/settings';
import { cn } from '@/lib/utils';
import type { SidebarPeekSource } from './sidebar-peek';
import '@/pages/Chat/classroom-workspace.css';
const SIDEBAR_PEEK_CLOSE_DELAY_MS = 180;
@@ -31,6 +32,7 @@ export function MainLayout() {
const isPaintingModule = activeModule === 'painting';
const isCanvasWorkspace = location.pathname === '/image-canvas';
const isChatWorkspace = location.pathname === '/chat';
const isCodeSidebar = isChatWorkspace || location.pathname === '/project-config' || location.pathname.startsWith('/project-config/');
const isCloudAgents = activeModule === 'cloud_agents';
const handleSidebarPeekChange = useCallback((open: boolean, source: SidebarPeekSource) => {
@@ -80,13 +82,14 @@ export function MainLayout() {
if (location.pathname === '/settings' || location.pathname.startsWith('/settings/')) return <SettingsLayout />;
return (
<div data-testid="main-layout" data-classroom={isChatWorkspace} className="relative flex h-[100dvh] flex-col overflow-hidden bg-transparent font-sans">
<div data-testid="main-layout" data-classroom={isChatWorkspace} data-code-sidebar={isCodeSidebar} className="relative flex h-[100dvh] flex-col overflow-hidden bg-transparent font-sans">
{/* Title bar: drag region on macOS, icon + controls on Windows */}
<TitleBar
integrated
pageTitle={isCloudAgents ? '我的智能体' : undefined}
showModuleReturn={isCloudAgents}
workspaceLayout={isChatWorkspace}
workspaceSidebar={isCodeSidebar}
overlay={isPaintingModule}
showSidebarControls={!isCanvasWorkspace}
sidebarPeekOpen={isCanvasWorkspace || isCloudAgents ? false : sidebarPeekOpen}
@@ -96,7 +99,7 @@ export function MainLayout() {
{/* Below the title bar: sidebar + content */}
<div className="relative flex min-h-0 flex-1 overflow-hidden">
<Sidebar
workspaceLayout={isChatWorkspace}
workspaceLayout={isCodeSidebar}
hideOnCompact={isCanvasWorkspace}
forceExpanded={isCanvasWorkspace}
sidebarPeekOpen={isCanvasWorkspace || isCloudAgents ? false : sidebarPeekOpen}
+2 -3
View File
@@ -141,7 +141,6 @@ export function Sidebar({
const isProgrammingModule = activeModule === 'programming';
const isPaintingModule = activeModule === 'painting';
const isRobotModule = activeModule === 'robot';
const isProjectConfigPage = location.pathname === '/project-config' || location.pathname.startsWith('/project-config/');
const visibleProjects = projects;
const selectedProjectFolderName = getFolderName(newProjectSelectedPath);
const accountName = userProfile?.displayName?.trim() || getAuthUserDisplayName(authUser) || '未登录用户';
@@ -431,7 +430,7 @@ export function Sidebar({
)}
>
{workspaceLayout && !sidebarPinnedCollapsed && <ClassroomSidebarResizer />}
{!isProjectConfigPage && activeModule !== 'cloud_agents' && <div
{activeModule !== 'cloud_agents' && <div
className={cn(
'bg-transparent px-3 text-foreground',
workspaceLayout && 'classroom-sidebar-header',
@@ -880,7 +879,7 @@ export function Sidebar({
onOpenChange={setProfileDialogOpen}
/>
{workspaceLayout && isProgrammingModule && !isProjectConfigPage && (
{workspaceLayout && isProgrammingModule && (
<div className="classroom-community-footer">
<CommunityLink variant="sidebar" collapsed={sidebarCollapsed} />
</div>
+21 -17
View File
@@ -24,6 +24,7 @@ type SidebarPeekChange = (open: boolean, source: SidebarPeekSource) => void;
type TitleBarProps = {
integrated?: boolean;
workspaceLayout?: boolean;
workspaceSidebar?: boolean;
overlay?: boolean;
showSidebarControls?: boolean;
pageTitle?: string;
@@ -32,11 +33,11 @@ type TitleBarProps = {
onSidebarPeekChange?: SidebarPeekChange;
};
export function TitleBar({ integrated = false, workspaceLayout = false, overlay = false, showSidebarControls = true, pageTitle, showModuleReturn = false, sidebarPeekOpen = false, onSidebarPeekChange }: TitleBarProps) {
export function TitleBar({ integrated = false, workspaceLayout = false, workspaceSidebar = workspaceLayout, 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} showModuleReturn={showModuleReturn} nativeTrafficLights sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={onSidebarPeekChange} />;
return <ProductTitleBar integrated={integrated} workspaceLayout={workspaceLayout} workspaceSidebar={workspaceSidebar} overlay={overlay} showSidebarControls={showSidebarControls} pageTitle={pageTitle} showModuleReturn={showModuleReturn} nativeTrafficLights sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={onSidebarPeekChange} />;
}
// Linux keeps its native window frame. Agents still needs an application
@@ -45,12 +46,13 @@ export function TitleBar({ integrated = false, workspaceLayout = false, overlay
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} showModuleReturn={showModuleReturn} sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={onSidebarPeekChange} />;
return <WindowsTitleBar integrated={integrated} workspaceLayout={workspaceLayout} workspaceSidebar={workspaceSidebar} overlay={overlay} showSidebarControls={showSidebarControls} pageTitle={pageTitle} showModuleReturn={showModuleReturn} sidebarPeekOpen={sidebarPeekOpen} onSidebarPeekChange={onSidebarPeekChange} />;
}
function ProductTitleBar({
integrated,
workspaceLayout = false,
workspaceSidebar = workspaceLayout,
overlay = false,
showSidebarControls = true,
pageTitle,
@@ -63,6 +65,7 @@ function ProductTitleBar({
}: {
integrated: boolean;
workspaceLayout?: boolean;
workspaceSidebar?: boolean;
overlay?: boolean;
showSidebarControls?: boolean;
pageTitle?: string;
@@ -93,10 +96,10 @@ function ProductTitleBar({
// The project sidebar preview is rendered below the title bar, so mirror
// its full width in the title bar for ordinary pages. The chat workspace
// keeps project conversations in the sidebar and must preserve its title rail.
const sidebarPreviewTitlebarVisible = sidebarCollapsed && sidebarPeekOpen && !workspaceLayout && !overlay;
const sidebarPreviewTitlebarVisible = sidebarCollapsed && sidebarPeekOpen && !workspaceSidebar && !overlay;
const sidebarTitlebarHidden = sidebarCollapsed && !sidebarPreviewTitlebarVisible;
const agentNavigationWidth = sidebarTitlebarHidden ? (nativeTrafficLights && !nativeFullscreen ? 168 : 88) : 256;
const navigationWidth = workspaceLayout ? classroomTitlebarWidth : showModuleReturn ? agentNavigationWidth : undefined;
const navigationWidth = workspaceSidebar ? classroomTitlebarWidth : showModuleReturn ? agentNavigationWidth : undefined;
const sidebarToggle = (
<button
type="button"
@@ -107,13 +110,13 @@ function ProductTitleBar({
className={cn(
toolbarButtonClass,
'pointer-events-auto absolute top-1 z-[401]',
workspaceLayout || showModuleReturn ? 'right-2' : nativeTrafficLights ? 'left-[88px]' : 'left-2',
workspaceSidebar || showModuleReturn ? 'right-2' : nativeTrafficLights ? 'left-[88px]' : 'left-2',
)}
onClick={toggleSidebar}
onPointerEnter={() => handleSidebarPeekEnter('toggle')}
onPointerLeave={() => handleSidebarPeekLeave('toggle')}
onFocus={!workspaceLayout && !showModuleReturn ? () => handleSidebarPeekEnter('focus') : undefined}
onBlur={!workspaceLayout && !showModuleReturn ? () => handleSidebarPeekLeave('focus') : undefined}
onFocus={!workspaceSidebar && !showModuleReturn ? () => handleSidebarPeekEnter('focus') : undefined}
onBlur={!workspaceSidebar && !showModuleReturn ? () => handleSidebarPeekLeave('focus') : undefined}
>
{sidebarCollapsed ? <PanelLeft className="h-4 w-4" /> : <PanelLeftClose className="h-4 w-4" />}
</button>
@@ -123,18 +126,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 && !showModuleReturn && (nativeTrafficLights ? 'w-[132px]' : 'w-[52px]'),
!workspaceSidebar && !showModuleReturn && (nativeTrafficLights ? 'w-[132px]' : 'w-[52px]'),
)}
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) => {
onPointerEnter={workspaceSidebar || showModuleReturn ? () => handleSidebarPeekEnter('titlebar') : undefined}
onPointerLeave={workspaceSidebar || showModuleReturn ? () => handleSidebarPeekLeave('titlebar') : undefined}
onFocusCapture={workspaceSidebar || showModuleReturn ? () => handleSidebarPeekEnter('focus') : undefined}
onBlurCapture={workspaceSidebar || showModuleReturn ? (event) => {
if (!event.currentTarget.contains(event.relatedTarget as Node | null)) handleSidebarPeekLeave('focus');
} : undefined}
>
{workspaceLayout || showModuleReturn ? <div
data-testid={workspaceLayout ? 'classroom-titlebar-navigation' : 'agent-titlebar-navigation'}
{workspaceSidebar || showModuleReturn ? <div
data-testid={workspaceSidebar ? '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 />
@@ -184,7 +187,7 @@ function ProductTitleBar({
style={navigationWidth !== undefined ? { width: navigationWidth, minWidth: navigationWidth, flexBasis: navigationWidth } : undefined}
className={cn(
'flex h-full shrink-0 items-center gap-1 px-2',
workspaceLayout
workspaceSidebar
? ''
: sidebarTitlebarHidden
? nativeTrafficLights
@@ -258,7 +261,7 @@ function ProductTitleBar({
);
}
function WindowsTitleBar({ integrated, workspaceLayout, overlay, showSidebarControls, pageTitle, showModuleReturn, sidebarPeekOpen, onSidebarPeekChange }: Omit<TitleBarProps, 'integrated'> & { integrated: boolean }) {
function WindowsTitleBar({ integrated, workspaceLayout, workspaceSidebar, overlay, showSidebarControls, pageTitle, showModuleReturn, sidebarPeekOpen, onSidebarPeekChange }: Omit<TitleBarProps, 'integrated'> & { integrated: boolean }) {
const [maximized, setMaximized] = useState(false);
useEffect(() => {
@@ -288,6 +291,7 @@ function WindowsTitleBar({ integrated, workspaceLayout, overlay, showSidebarCont
<ProductTitleBar
integrated={integrated}
workspaceLayout={workspaceLayout}
workspaceSidebar={workspaceSidebar}
overlay={overlay}
showSidebarControls={showSidebarControls}
pageTitle={pageTitle}