perf(app): keep desktop sidebar ready to reopen

Retain the hidden sidebar tree so common toggles preserve local UI state while existing activity gates pause expensive hidden work.
This commit is contained in:
Mohamed Boudra
2026-07-15 12:05:08 +02:00
parent 3673846433
commit 5ddd5f3726
4 changed files with 79 additions and 26 deletions
@@ -169,6 +169,47 @@ test.describe("Mobile sidebar panelState transition", () => {
test.describe("Half-screen desktop layout", () => {
test.use({ viewport: { width: 751, height: 982 } });
test("keeps the sidebar scroll position across close and reopen", async ({ page }) => {
const workspace = await seedWorkspace({ repoPrefix: "sidebar-retained-scroll-" });
try {
let lastWorkspaceId = workspace.workspaceId;
for (let index = 0; index < 24; index += 1) {
const created = await workspace.client.createWorkspace({
source: {
kind: "directory",
path: workspace.repoPath,
projectId: workspace.projectId,
},
title: `Retained sidebar ${index + 1}`,
});
if (!created.workspace) {
throw new Error(created.error ?? "Failed to fill the retained sidebar");
}
lastWorkspaceId = created.workspace.id;
}
await gotoAppShell(page);
await waitForSidebarWorkspace(page, lastWorkspaceId);
const sidebarScroll = page.getByTestId("sidebar-project-workspace-list-scroll");
const scrollTop = await sidebarScroll.evaluate((element) => {
element.scrollTop = 160;
return element.scrollTop;
});
expect(scrollTop).toBe(160);
await page.getByTestId("menu-button").click();
await expect(page.getByTestId("sidebar-global-new-workspace")).not.toBeVisible();
await page.getByTestId("menu-button").click();
await expect(page.getByTestId("sidebar-global-new-workspace")).toBeVisible();
await expect(sidebarScroll).toHaveJSProperty("scrollTop", scrollTop);
} finally {
await workspace.cleanup();
}
});
test("keeps the pinned sidebar at half of a 14-inch Mac display", async ({ page }) => {
await gotoAppShell(page);
await expect(page.getByTestId("sidebar-global-new-workspace")).toBeVisible();
+13 -9
View File
@@ -482,10 +482,10 @@ function AppContainer({ children, chromeEnabled: chromeEnabledOverride }: AppCon
requestedExplorerWidth: explorerWidth,
viewportWidth,
});
const desktopSidebarRendered =
const desktopSidebarMounted = chromeEnabled && !isFocusModeEnabled;
const desktopSidebarVisible =
!isCompactLayout &&
chromeEnabled &&
!isFocusModeEnabled &&
desktopSidebarMounted &&
isDesktopAgentListOpen &&
canDesktopAppSidebarShare({
contentMinimumWidth: appContentMinimumWidth,
@@ -494,13 +494,14 @@ function AppContainer({ children, chromeEnabled: chromeEnabledOverride }: AppCon
});
const hasTopLeftWindowControls = useHasWindowChromeObstruction("top-left");
const appChromeLayout = resolveDesktopAppChromeLayout({
desktopSidebarRendered,
desktopSidebarRendered: desktopSidebarVisible,
hasTopLeftWindowControls,
sidebarControlsEnabled: chromeEnabled && !isFocusModeEnabled,
});
const sidebarChrome = (
<SidebarChrome
showSidebar={isCompactLayout ? chromeEnabled : desktopSidebarRendered}
mounted={isCompactLayout ? chromeEnabled : desktopSidebarMounted}
visible={isCompactLayout ? chromeEnabled : desktopSidebarVisible}
keyboardShortcutsEnabled={keyboardShortcutsEnabled}
/>
);
@@ -567,19 +568,22 @@ function AppContainer({ children, chromeEnabled: chromeEnabledOverride }: AppCon
}
function SidebarChrome({
showSidebar,
mounted,
visible,
keyboardShortcutsEnabled,
}: {
showSidebar: boolean;
mounted: boolean;
visible: boolean;
keyboardShortcutsEnabled: boolean;
}) {
const isCompactLayout = useIsCompactFormFactor();
const isOpen = usePanelStore((state) =>
selectIsAgentListOpen(state, { isCompact: isCompactLayout }),
);
const active = visible && isOpen;
return (
<SidebarModelProvider active={showSidebar && isOpen}>
{showSidebar ? <LeftSidebar /> : null}
<SidebarModelProvider active={active}>
{mounted ? <LeftSidebar active={active} /> : null}
<WorkspaceShortcutTargetsSubscriber enabled={keyboardShortcutsEnabled} />
</SidebarModelProvider>
);
+22 -17
View File
@@ -51,7 +51,7 @@ import { useKeyboardShortcutsStore } from "@/stores/keyboard-shortcuts-store";
import { useHosts } from "@/runtime/host-runtime";
import { useActiveWorkspaceSelection } from "@/stores/navigation-active-workspace-store";
import { useWorkspace } from "@/stores/session-store-hooks";
import { selectIsAgentListOpen, usePanelStore } from "@/stores/panel-store";
import { usePanelStore } from "@/stores/panel-store";
import { useOwnsWindowChromeCorner, WindowChromeSafeArea } from "@/utils/desktop-window";
import { useCloseAgentListGesture } from "@/mobile-panels/gestures";
import { MobilePanelOverlay } from "@/mobile-panels/presentation";
@@ -117,19 +117,16 @@ interface MobileSidebarProps extends SidebarSharedProps {
interface DesktopSidebarProps extends SidebarSharedProps {
insetsTop: number;
isOpen: boolean;
active: boolean;
handleViewMore: () => void;
handleViewSchedules: () => void;
}
export const LeftSidebar = memo(function LeftSidebar() {
export const LeftSidebar = memo(function LeftSidebar({ active }: { active: boolean }) {
const { theme } = useUnistyles();
const { t } = useTranslation();
const insets = useSafeAreaInsets();
const isCompactLayout = useIsCompactFormFactor();
const isOpen = usePanelStore((state) =>
selectIsAgentListOpen(state, { isCompact: isCompactLayout }),
);
const showMobileAgent = usePanelStore((state) => state.showMobileAgent);
const {
@@ -256,7 +253,7 @@ export const LeftSidebar = memo(function LeftSidebar() {
if (isCompactLayout) {
return (
<RetainedPanelActivity active={isOpen}>
<RetainedPanelActivity active={active}>
<MobileSidebar
{...sharedProps}
insetsTop={insets.top}
@@ -275,11 +272,11 @@ export const LeftSidebar = memo(function LeftSidebar() {
}
return (
<RetainedPanelActivity active={isOpen}>
<RetainedPanelActivity active={active}>
<DesktopSidebar
{...sharedProps}
insetsTop={insets.top}
isOpen={isOpen}
active={active}
handleOpenProject={handleOpenProjectDesktop}
handleHome={handleHomeDesktop}
handleSettings={handleSettingsDesktop}
@@ -702,7 +699,7 @@ function DesktopSidebar({
handleAddHost,
handleOpenHostSettings,
insetsTop,
isOpen,
active,
handleViewMore,
handleViewSchedules,
}: DesktopSidebarProps) {
@@ -753,8 +750,12 @@ function DesktopSidebar({
}));
const desktopSidebarStyle = useMemo(
() => [staticStyles.desktopSidebar, resizeAnimatedStyle],
[resizeAnimatedStyle],
() => [
staticStyles.desktopSidebar,
!active && staticStyles.desktopSidebarHidden,
resizeAnimatedStyle,
],
[active, resizeAnimatedStyle],
);
const desktopSidebarBorderStyle = useMemo(
() => [styles.desktopSidebarBorder, { flex: 1, paddingTop: insetsTop }],
@@ -769,12 +770,13 @@ function DesktopSidebar({
[],
);
if (!isOpen) {
return null;
}
return (
<Animated.View style={desktopSidebarStyle}>
<Animated.View
accessibilityElementsHidden={!active}
importantForAccessibility={active ? "auto" : "no-hide-descendants"}
pointerEvents={active ? "auto" : "none"}
style={desktopSidebarStyle}
>
<View style={desktopSidebarBorderStyle}>
<View style={styles.sidebarDragArea}>
{ownsTopLeft ? (
@@ -917,6 +919,9 @@ const staticStyles = RNStyleSheet.create({
desktopSidebar: {
position: "relative" as const,
},
desktopSidebarHidden: {
display: "none",
},
});
const styles = StyleSheet.create((theme) => ({