Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions apps/web/src/components/AppSidebarLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@ import { getLocalStorageItem, removeLocalStorageItem } from "../hooks/useLocalSt
import { resolveShortcutCommand, shortcutLabelForCommand } from "../keybindings";
import { cn, isMacPlatform } from "../lib/utils";
import { primaryServerKeybindingsAtom } from "../state/server";
import { useEnvironmentIdentificationMode, useLegacySidebarEnabled } from "../hooks/useSettings";
import {
useCompactSidebarEnabled,
useEnvironmentIdentificationMode,
useLegacySidebarEnabled,
} from "../hooks/useSettings";
import {
PanelAnimationSuppressionProvider,
usePanelAnimationSettings,
Expand Down Expand Up @@ -144,6 +148,7 @@ function ProjectProjectionRetention() {
export function AppSidebarLayout({ children }: { children: ReactNode }) {
const navigate = useNavigate();
const legacySidebarEnabled = useLegacySidebarEnabled();
const compactSidebarEnabled = useCompactSidebarEnabled();
const { active: panelAnimationsActive, durationMs: panelAnimationDurationMs } =
usePanelAnimationSettings();
// Settings routes show the settings nav in place of whichever thread
Expand Down Expand Up @@ -229,7 +234,7 @@ export function AppSidebarLayout({ children }: { children: ReactNode }) {
<ProjectProjectionRetention />
<Sidebar
side="left"
collapsible="offcanvas"
collapsible={compactSidebarEnabled ? "icon" : "offcanvas"}
data-app-sidebar=""
className="border-r border-sidebar-border bg-sidebar text-sidebar-foreground"
resizable={{
Expand Down
77 changes: 52 additions & 25 deletions apps/web/src/components/LegacySidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,11 @@ import { SidebarChromeFooter, SidebarChromeHeader } from "./sidebar/SidebarChrom
import { useCopyToClipboard } from "~/hooks/useCopyToClipboard";
import { useIsMobile } from "~/hooks/useMediaQuery";
import { CommandDialogTrigger } from "./ui/command";
import { useClientSettings, useUpdateClientSettings } from "~/hooks/useSettings";
import {
useClientSettings,
useCompactSidebarEnabled,
useUpdateClientSettings,
} from "~/hooks/useSettings";
import { primaryServerKeybindingsAtom } from "../state/server";
import {
derivePhysicalProjectKey,
Expand Down Expand Up @@ -1194,7 +1198,9 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
const router = useRouter();
const queuePendingFileDrop = useSidebarPendingFileDropStore((s) => s.queuePendingFileDrop);
const clearPendingFileDrop = useSidebarPendingFileDropStore((s) => s.clearPendingFileDrop);
const { isMobile, setOpenMobile } = useSidebar();
const { isMobile, setOpenMobile, state, setOpen } = useSidebar();
const compactSidebarEnabled = useCompactSidebarEnabled();
const isCompact = compactSidebarEnabled && !isMobile && state === "collapsed";
const markThreadUnread = useUiStateStore((state) => state.markThreadUnread);
const setProjectExpanded = useUiStateStore((state) => state.setProjectExpanded);
const toggleThreadSelection = useThreadSelectionStore((state) => state.toggleThread);
Expand Down Expand Up @@ -1445,10 +1451,13 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
if (useThreadSelectionStore.getState().hasSelection()) {
clearSelection();
}
setProjectExpanded(projectPreferenceKeys, !projectExpanded);
setProjectExpanded(projectPreferenceKeys, isCompact || !projectExpanded);
if (isCompact) setOpen(true);
},
[
clearSelection,
isCompact,
setOpen,
dragInProgressRef,
projectExpanded,
projectPreferenceKeys,
Expand All @@ -1465,9 +1474,17 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
if (dragInProgressRef.current) {
return;
}
setProjectExpanded(projectPreferenceKeys, !projectExpanded);
setProjectExpanded(projectPreferenceKeys, isCompact || !projectExpanded);
if (isCompact) setOpen(true);
},
[dragInProgressRef, projectExpanded, projectPreferenceKeys, setProjectExpanded],
[
dragInProgressRef,
isCompact,
projectExpanded,
projectPreferenceKeys,
setOpen,
setProjectExpanded,
],
);

const handleProjectButtonPointerDownCapture = useCallback(
Expand Down Expand Up @@ -2359,8 +2376,10 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
<>
<div className="group/project-header relative">
<SidebarMenuButton
tooltip={project.displayName}
aria-label={project.displayName}
ref={isManualProjectSorting ? dragHandleProps?.setActivatorNodeRef : undefined}
className={`pr-8 group-hover/project-header:bg-sidebar-row-hover group-hover/project-header:text-sidebar-foreground max-sm:pr-14 ${
className={`pr-8 group-hover/project-header:bg-sidebar-row-hover group-hover/project-header:text-sidebar-foreground max-sm:pr-14 group-data-[collapsible=icon]:justify-center ${
isManualProjectSorting ? "cursor-grab active:cursor-grabbing" : ""
}`}
{...(isManualProjectSorting && dragHandleProps ? dragHandleProps.attributes : {})}
Expand All @@ -2370,7 +2389,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
onKeyDown={handleProjectButtonKeyDown}
onContextMenu={handleProjectButtonContextMenu}
>
{!projectExpanded && projectStatus ? (
{isCompact ? null : !projectExpanded && projectStatus ? (
<Tooltip>
<TooltipTrigger
render={
Expand Down Expand Up @@ -2401,7 +2420,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
<span className="flex shrink-0">
<ProjectFavicon project={project} />
</span>
<span className="flex min-w-0 flex-1 items-center gap-2">
<span className="flex min-w-0 flex-1 items-center gap-2 group-data-[collapsible=icon]:hidden">
<span className="truncate text-sm font-medium text-sidebar-foreground/90">
{project.displayName}
</span>
Expand All @@ -2415,7 +2434,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
{/* Environment badge – visible by default, crossfades with the
"new thread" button on hover using the same pointer-events +
opacity pattern as the thread row archive/timestamp swap. */}
{project.environmentPresence === "remote-only" && (
{!isCompact && project.environmentPresence === "remote-only" && (
<Tooltip>
<TooltipTrigger
render={
Expand All @@ -2441,7 +2460,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
<Tooltip>
<TooltipTrigger
render={
<div className="pointer-events-none absolute top-[calc(50%+1px)] right-0.5 -translate-y-1/2 opacity-0 transition-opacity duration-150 max-sm:pointer-events-auto max-sm:opacity-100 group-hover/project-header:pointer-events-auto group-hover/project-header:opacity-100 group-focus-within/project-header:pointer-events-auto group-focus-within/project-header:opacity-100">
<div className="pointer-events-none absolute top-[calc(50%+1px)] right-0.5 -translate-y-1/2 opacity-0 transition-opacity duration-150 max-sm:pointer-events-auto max-sm:opacity-100 group-hover/project-header:pointer-events-auto group-hover/project-header:opacity-100 group-focus-within/project-header:pointer-events-auto group-focus-within/project-header:opacity-100 group-data-[collapsible=icon]:hidden">
<button
type="button"
aria-label={`Create new thread in ${project.displayName}`}
Expand Down Expand Up @@ -2674,7 +2693,7 @@ function LocalSecondaryStatus() {
}

return (
<SidebarGroup className="px-2 pt-2 pb-0">
<SidebarGroup className="px-2 pt-2 pb-0 group-data-[collapsible=icon]:hidden">
{connecting.length > 0 ? (
<Alert
variant="default"
Expand Down Expand Up @@ -2978,14 +2997,16 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
render={
<SidebarMenuButton
className="focus-visible:ring-0"
tooltip="Search"
aria-label="Search"
data-testid="command-palette-trigger"
/>
}
>
<SearchIcon />
<span className="flex-1 truncate">Search</span>
<span className="flex-1 truncate group-data-[collapsible=icon]:hidden">Search</span>
{commandPaletteShortcutLabel ? (
<Kbd className="h-4 min-w-0 rounded-sm px-1.5 text-[10px]">
<Kbd className="h-4 min-w-0 rounded-sm px-1.5 text-[10px] group-data-[collapsible=icon]:hidden">
{commandPaletteShortcutLabel}
</Kbd>
) : null}
Expand All @@ -2996,7 +3017,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
}
>
{showArm64IntelBuildWarning && arm64IntelBuildWarningDescription ? (
<SidebarGroup className="px-2 pt-2 pb-0">
<SidebarGroup className="px-2 pt-2 pb-0 group-data-[collapsible=icon]:hidden">
<Alert variant="warning" className="rounded-2xl border-warning/40 bg-warning/8">
<TriangleAlertIcon />
<AlertTitle>Intel build on Apple Silicon</AlertTitle>
Expand All @@ -3020,17 +3041,21 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
) : null}
<LocalSecondaryStatus />
<SidebarGroup className="px-2 py-2">
<div className="mb-1 flex items-center justify-between pl-2 pr-1.5">
<span className="text-xs font-medium text-sidebar-muted-foreground/80">Projects</span>
<div className="mb-1 flex items-center justify-between pl-2 pr-1.5 group-data-[collapsible=icon]:justify-center group-data-[collapsible=icon]:px-0">
<span className="text-xs font-medium text-sidebar-muted-foreground/80 group-data-[collapsible=icon]:hidden">
Projects
</span>
<div className="flex items-center gap-1">
<ProjectSortMenu
projectSortOrder={projectSortOrder}
threadSortOrder={threadSortOrder}
threadPreviewCount={threadPreviewCount}
onProjectSortOrderChange={handleProjectSortOrderChange}
onThreadSortOrderChange={handleThreadSortOrderChange}
onThreadPreviewCountChange={handleThreadPreviewCountChange}
/>
<div className="group-data-[collapsible=icon]:hidden">
<ProjectSortMenu
projectSortOrder={projectSortOrder}
threadSortOrder={threadSortOrder}
threadPreviewCount={threadPreviewCount}
onProjectSortOrderChange={handleProjectSortOrderChange}
onThreadSortOrderChange={handleThreadSortOrderChange}
onThreadPreviewCountChange={handleThreadPreviewCountChange}
/>
</div>
<Tooltip>
<TooltipTrigger
render={
Expand Down Expand Up @@ -3127,7 +3152,9 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
)}

{projectsLength === 0 && (
<div className="px-2 pt-4 text-center text-secondary-label text-xs">No projects yet</div>
<div className="px-2 pt-4 text-center text-secondary-label text-xs group-data-[collapsible=icon]:hidden">
No projects yet
</div>
)}
</SidebarGroup>
</SidebarContent>
Expand Down
14 changes: 9 additions & 5 deletions apps/web/src/components/Sidebar.drag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ export function createSidebarSortingStrategy(input: {
snoozedThreadCount?: number;
cardHeight?: number;
slimHeight?: number;
compact?: boolean;
/** Space each pinned boundary opens for its label while dragging. The
* markers stay zero height at rest, so nothing is reserved until pickup. */
boundaryLabelHeight?: number;
Expand Down Expand Up @@ -140,11 +141,14 @@ export function createSidebarSortingStrategy(input: {
else slimHeight ??= rects[index]?.height;
if (item.key !== active.key) groups[item.section].push(item);
}
// Cards are 4.875rem + 0.25rem padding; slim rows/placeholders are h-9.
const scale =
slimHeight !== undefined ? slimHeight / 36 : (headerScale ?? (cardHeight ?? 82) / 82);
cardHeight ??= 82 * scale;
slimHeight ??= 36 * scale;
// Compact icons use h-7; expanded cards include their vertical padding.
const scale = input.compact
? (cardHeight ?? slimHeight ?? 28) / 28
: slimHeight !== undefined
? slimHeight / 36
: (headerScale ?? (cardHeight ?? 82) / 82);
cardHeight ??= (input.compact ? 28 : 82) * scale;
slimHeight ??= (input.compact ? 28 : 36) * scale;
const labelHeight = (input.boundaryLabelHeight ?? 0) * scale;
const group = groups[target.section];
const order =
Expand Down
Loading
Loading