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
30 changes: 19 additions & 11 deletions apps/web/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -373,7 +373,7 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
const threadMetaClassName = isConfirmingArchive
? "pointer-events-none opacity-0"
: !isThreadRunning
? "pointer-events-none transition-opacity duration-150 group-hover/menu-sub-item:opacity-0 group-focus-within/menu-sub-item:opacity-0"
? "pointer-events-none transition-opacity duration-150 max-sm:pr-6 group-hover/menu-sub-item:opacity-0 group-focus-within/menu-sub-item:opacity-0"
: "pointer-events-none";
const clearConfirmingArchive = useCallback(() => {
setConfirmingArchiveThreadKey((current) => (current === threadKey ? null : current));
Expand Down Expand Up @@ -605,7 +605,11 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
<TerminalIcon className={`size-3 ${terminalStatus.pulse ? "animate-pulse" : ""}`} />
</span>
)}
<div className="flex min-w-12 justify-end">
<div
className={`flex min-w-12 justify-end ${
isRemoteThread ? "max-sm:min-w-24" : "max-sm:min-w-20"
}`}
>
{isConfirmingArchive ? (
<button
ref={handleConfirmArchiveRef}
Expand All @@ -621,13 +625,13 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
</button>
) : !isThreadRunning ? (
appSettingsConfirmThreadArchive ? (
<div className="pointer-events-none absolute top-1/2 right-1 -translate-y-1/2 opacity-0 transition-opacity duration-150 group-hover/menu-sub-item:pointer-events-auto group-hover/menu-sub-item:opacity-100 group-focus-within/menu-sub-item:pointer-events-auto group-focus-within/menu-sub-item:opacity-100">
<div className="pointer-events-none absolute top-1/2 right-1 -translate-y-1/2 opacity-0 transition-opacity duration-150 max-sm:pointer-events-auto max-sm:opacity-100 group-hover/menu-sub-item:pointer-events-auto group-hover/menu-sub-item:opacity-100 group-focus-within/menu-sub-item:pointer-events-auto group-focus-within/menu-sub-item:opacity-100">
<button
type="button"
data-thread-selection-safe
data-testid={`thread-archive-${thread.id}`}
aria-label={`Archive ${thread.title}`}
className="inline-flex size-5 cursor-pointer items-center justify-center text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring"
className="inline-flex size-5 cursor-pointer items-center justify-center text-muted-foreground/60 transition-colors hover:text-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring"
onPointerDown={stopPropagationOnPointerDown}
onClick={handleStartArchiveConfirmation}
>
Expand All @@ -638,13 +642,13 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
<Tooltip>
<TooltipTrigger
render={
<div className="pointer-events-none absolute top-1/2 right-1 -translate-y-1/2 opacity-0 transition-opacity duration-150 group-hover/menu-sub-item:pointer-events-auto group-hover/menu-sub-item:opacity-100 group-focus-within/menu-sub-item:pointer-events-auto group-focus-within/menu-sub-item:opacity-100">
<div className="pointer-events-none absolute top-1/2 right-1 -translate-y-1/2 opacity-0 transition-opacity duration-150 max-sm:pointer-events-auto max-sm:opacity-100 group-hover/menu-sub-item:pointer-events-auto group-hover/menu-sub-item:opacity-100 group-focus-within/menu-sub-item:pointer-events-auto group-focus-within/menu-sub-item:opacity-100">
<button
type="button"
data-thread-selection-safe
data-testid={`thread-archive-${thread.id}`}
aria-label={`Archive ${thread.title}`}
className="inline-flex size-5 cursor-pointer items-center justify-center text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring"
className="inline-flex size-5 cursor-pointer items-center justify-center text-muted-foreground/60 transition-colors hover:text-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring"
onPointerDown={stopPropagationOnPointerDown}
onClick={handleArchiveImmediateClick}
>
Expand All @@ -665,11 +669,11 @@ const SidebarThreadRow = memo(function SidebarThreadRow(props: SidebarThreadRowP
render={
<span
aria-label={threadEnvironmentLabel ?? "Remote"}
className="inline-flex items-center justify-center"
className="inline-flex h-5 items-center justify-center"
/>
}
>
<CloudIcon className="size-3 text-muted-foreground/40" />
<CloudIcon className="block size-3 text-muted-foreground/60" />
</TooltipTrigger>
<TooltipPopup side="top">{threadEnvironmentLabel}</TooltipPopup>
</Tooltip>
Expand Down Expand Up @@ -2025,7 +2029,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
? "Remote project"
: "Available in multiple environments"
}
className="pointer-events-none absolute top-1 right-1.5 inline-flex size-5 items-center justify-center rounded-md text-muted-foreground/50 transition-opacity duration-150 max-sm:right-7 group-hover/project-header:opacity-0 group-focus-within/project-header:opacity-0 max-sm:group-hover/project-header:opacity-100 max-sm:group-focus-within/project-header:opacity-100"
className="pointer-events-none absolute top-1 right-1.5 inline-flex size-5 items-center justify-center rounded-md text-muted-foreground/60 transition-opacity duration-150 max-sm:right-7 group-hover/project-header:opacity-0 group-focus-within/project-header:opacity-0 max-sm:group-hover/project-header:opacity-100 max-sm:group-focus-within/project-header:opacity-100"
/>
}
>
Expand All @@ -2044,7 +2048,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
type="button"
aria-label={`Create new thread in ${project.displayName}`}
data-testid="new-thread-button"
className="inline-flex size-5 cursor-pointer items-center justify-center rounded-md text-muted-foreground/70 hover:bg-secondary hover:text-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring"
className="inline-flex size-5 cursor-pointer items-center justify-center rounded-md text-muted-foreground/60 hover:bg-secondary hover:text-foreground focus-visible:outline-hidden focus-visible:ring-1 focus-visible:ring-ring"
onClick={handleCreateThreadClick}
>
<SquarePenIcon className="size-3.5" />
Expand Down Expand Up @@ -2420,9 +2424,13 @@ const SidebarChromeHeader = memo(function SidebarChromeHeader({

const SidebarChromeFooter = memo(function SidebarChromeFooter() {
const navigate = useNavigate();
const { isMobile, setOpenMobile } = useSidebar();
const handleSettingsClick = useCallback(() => {
if (isMobile) {
setOpenMobile(false);
}
void navigate({ to: "/settings" });
}, [navigate]);
}, [isMobile, navigate, setOpenMobile]);

return (
<SidebarFooter className="p-2">
Expand Down
30 changes: 26 additions & 4 deletions apps/web/src/components/settings/SettingsSidebarNav.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { ComponentType } from "react";
import { useCallback, type ComponentType } from "react";
import { ArchiveIcon, ArrowLeftIcon, Link2Icon, Settings2Icon } from "lucide-react";
import { useNavigate } from "@tanstack/react-router";
import { useCanGoBack, useNavigate } from "@tanstack/react-router";

import {
SidebarContent,
Expand All @@ -10,6 +10,7 @@ import {
SidebarMenuButton,
SidebarMenuItem,
SidebarSeparator,
useSidebar,
} from "../ui/sidebar";

export type SettingsSectionPath =
Expand All @@ -29,6 +30,27 @@ export const SETTINGS_NAV_ITEMS: ReadonlyArray<{

export function SettingsSidebarNav({ pathname }: { pathname: string }) {
const navigate = useNavigate();
const canGoBack = useCanGoBack();
const { isMobile, setOpenMobile } = useSidebar();
const handleSectionClick = useCallback(
(to: SettingsSectionPath) => {
if (isMobile) {
setOpenMobile(false);
}
void navigate({ to, replace: true });
},
[isMobile, navigate, setOpenMobile],
);
const handleBackClick = useCallback(() => {
if (isMobile) {
setOpenMobile(false);
}
if (canGoBack) {
window.history.back();
return;
}
void navigate({ to: "/" });
}, [canGoBack, isMobile, navigate, setOpenMobile]);

return (
<>
Expand All @@ -48,7 +70,7 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
? "gap-2.5 px-2.5 py-2 text-left text-[13px] font-medium text-foreground"
: "gap-2.5 px-2.5 py-2 text-left text-[13px] text-muted-foreground/70 hover:text-foreground/80"
}
onClick={() => void navigate({ to: item.to, replace: true })}
onClick={() => handleSectionClick(item.to)}
>
<Icon
className={
Expand All @@ -73,7 +95,7 @@ export function SettingsSidebarNav({ pathname }: { pathname: string }) {
<SidebarMenuButton
size="sm"
className="gap-2 px-2 py-2 text-xs text-muted-foreground hover:bg-accent hover:text-foreground"
onClick={() => window.history.back()}
onClick={handleBackClick}
>
<ArrowLeftIcon className="size-4" />
<span>Back</span>
Expand Down
24 changes: 20 additions & 4 deletions apps/web/src/routes/settings.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import { RotateCcwIcon } from "lucide-react";
import { Outlet, createFileRoute, redirect, useLocation } from "@tanstack/react-router";
import { useEffect, useState } from "react";
import {
Outlet,
createFileRoute,
redirect,
useCanGoBack,
useLocation,
useNavigate,
} from "@tanstack/react-router";
import { useCallback, useEffect, useState } from "react";

import { useSettingsRestore } from "../components/settings/SettingsPanels";
import { Button } from "../components/ui/button";
Expand All @@ -25,24 +32,33 @@ function RestoreDefaultsButton({ onRestored }: { onRestored: () => void }) {

function SettingsContentLayout() {
const location = useLocation();
const navigate = useNavigate();
const canGoBack = useCanGoBack();
const [restoreSignal, setRestoreSignal] = useState(0);
const showRestoreDefaults = location.pathname === "/settings/general";
const handleRestored = () => setRestoreSignal((value) => value + 1);
const navigateBackWithinApp = useCallback(() => {
if (canGoBack) {
window.history.back();
return;
}
void navigate({ to: "/" });
}, [canGoBack, navigate]);

useEffect(() => {
const onKeyDown = (event: KeyboardEvent) => {
if (event.defaultPrevented) return;
if (event.key === "Escape") {
event.preventDefault();
window.history.back();
navigateBackWithinApp();
}
};

window.addEventListener("keydown", onKeyDown);
return () => {
window.removeEventListener("keydown", onKeyDown);
};
}, []);
}, [navigateBackWithinApp]);

return (
<SidebarInset className="h-dvh min-h-0 overflow-hidden overscroll-y-none bg-background text-foreground isolate">
Expand Down
Loading