From 29b9588a5119fc1ec5443c48b001c09a41c7bee8 Mon Sep 17 00:00:00 2001 From: Bohdan Triapitsyn Date: Wed, 9 Sep 2026 09:22:33 +0300 Subject: [PATCH] refactor(mobile): move project sorting into the drawer header MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The five sort modes sat as a chip row under the search field, which cost a project row of list space for a setting people touch about once a month. Put them behind an icon in the header next to reordering — the two answer the same question about the list — and let it open the same sheet the composer already uses to pick a model or an agent, with a check on the active mode. The icon hides while reordering or searching, where sorting means nothing. The new-session button moves to the outer right edge and stays there: it is the one action people hit without looking, so it must not slide as the icons beside it come and go. Testing: package type-check and lint; drove the drawer in a browser at 390px (chip row gone, sheet lists the five modes and marks the active one, picking one reorders the list and closes the sheet, the icon disappears in reorder mode, and the new-session button keeps the right edge with and without a search). --- packages/ui/src/apps/MobileSessionsSheet.tsx | 79 +++++++++++++------- 1 file changed, 52 insertions(+), 27 deletions(-) diff --git a/packages/ui/src/apps/MobileSessionsSheet.tsx b/packages/ui/src/apps/MobileSessionsSheet.tsx index 7d35b453..f81b2031 100644 --- a/packages/ui/src/apps/MobileSessionsSheet.tsx +++ b/packages/ui/src/apps/MobileSessionsSheet.tsx @@ -36,6 +36,7 @@ import { DirectoryExplorerDialog } from '@/components/session/DirectoryExplorerD import { Icon } from '@/components/icon/Icon'; import { NewWorktreeDialog } from '@/components/session/NewWorktreeDialog'; import { Button } from '@/components/ui/button'; +import { MobileOverlayPanel } from '@/components/ui/MobileOverlayPanel'; import { Input } from '@/components/ui/input'; import { ScrollShadow } from '@/components/ui/ScrollShadow'; import { toast } from '@/components/ui'; @@ -959,6 +960,7 @@ export const MobileSessionsSheet: React.FC = ({ open, } | null>(null); // Bumped to force a re-list of worktrees (e.g. after one is deleted in the editor). const [worktreeRefreshKey, setWorktreeRefreshKey] = React.useState(0); + const [sortPanelOpen, setSortPanelOpen] = React.useState(false); const [directoryDialogOpen, setDirectoryDialogOpen] = React.useState(false); const [newWorktreeDialogOpen, setNewWorktreeDialogOpen] = React.useState(false); const [worktreeDialogProjectId, setWorktreeDialogProjectId] = React.useState(null); @@ -1498,6 +1500,23 @@ export const MobileSessionsSheet: React.FC = ({ open, // the user rearrange a list that is about to be re-sorted anyway. const canEditOrder = !normalizedQuery && projectsMeta.length > 1 && projectSortOrder === 'manual'; + // Sorting lives in the header next to reordering — the two answer the same + // question about the list, and a permanent row of modes above it would cost + // a project row for a setting touched once a month. + const sortToggle = !editingOrder && !normalizedQuery && projectsMeta.length > 1 ? ( + + ) : null; + const editToggle = canEditOrder ? ( ) : null; + // The new-session button keeps the outer right edge whatever else is showing: + // it is the one action people reach for without looking, so it must not slide + // around as the icons beside it come and go. const trailingActions = - newChatButton || addProjectButton || editToggle ? ( + newChatButton || addProjectButton || sortToggle || editToggle ? ( <> - {newChatButton} {addProjectButton} + {sortToggle} {editToggle} + {newChatButton} ) : null; @@ -1580,31 +1603,6 @@ export const MobileSessionsSheet: React.FC = ({ open, ) : null} - {/* Sorting sits with the search field rather than in the header: - it scrolls away with it, so a setting touched once a month - costs no permanent room above the list. */} - {!normalizedQuery && projectsMeta.length > 1 ? ( -
- {PROJECT_SORT_OPTIONS.map(([order, labelKey]) => ( - - ))} -
- ) : null} {projectsMeta.length === 0 && chatSessions.length === 0 ? ( = ({ open, onClose={() => setEditingProjectId(null)} onWorktreesChanged={() => setWorktreeRefreshKey((value) => value + 1)} /> + setSortPanelOpen(false)} + title={t('sessions.sidebar.header.actions.sortProjects')} + > +
+ {PROJECT_SORT_OPTIONS.map(([order, labelKey]) => ( + + ))} +
+
+ {worktreeToDelete ? (