Skip to content
Open
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
37 changes: 20 additions & 17 deletions apps/web/src/components/LegacySidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,7 @@ import { useCopyToClipboard } from "~/hooks/useCopyToClipboard";
import { useIsMobile } from "~/hooks/useMediaQuery";
import { CommandDialogTrigger } from "./ui/command";
import { useClientSettings, useUpdateClientSettings } from "~/hooks/useSettings";
import { usePanelAnimationSettings } from "~/panelAnimations";
import { primaryServerKeybindingsAtom } from "../state/server";
import {
derivePhysicalProjectKey,
Expand Down Expand Up @@ -3341,23 +3342,25 @@ export default function LegacySidebar() {
dragInProgressRef.current = false;
}, []);

const animatedProjectListsRef = useRef(new WeakSet<HTMLElement>());
const attachProjectListAutoAnimateRef = useCallback((node: HTMLElement | null) => {
if (!node || animatedProjectListsRef.current.has(node)) {
return;
}
autoAnimate(node, SIDEBAR_LIST_ANIMATION_OPTIONS);
animatedProjectListsRef.current.add(node);
}, []);

const animatedThreadListsRef = useRef(new WeakSet<HTMLElement>());
const attachThreadListAutoAnimateRef = useCallback((node: HTMLElement | null) => {
if (!node || animatedThreadListsRef.current.has(node)) {
return;
}
autoAnimate(node, SIDEBAR_LIST_ANIMATION_OPTIONS);
animatedThreadListsRef.current.add(node);
}, []);
// Both lists follow the Motion setting, as the default sidebar does: at the
// default 0 ms they change immediately and auto-animate is never attached,
// so its per-row position polling (#4693) never runs. A detached list is
// destroyed rather than left polling.
const { active: listMotionActive, durationMs: listMotionDurationMs } =
usePanelAnimationSettings();
const attachListAutoAnimateRef = useCallback(
(node: HTMLElement | null) => {
if (!node || !listMotionActive) return;
const controller = autoAnimate(node, {
...SIDEBAR_LIST_ANIMATION_OPTIONS,
duration: Math.min(SIDEBAR_LIST_ANIMATION_OPTIONS.duration, listMotionDurationMs),
});
return () => controller.destroy?.();
},
[listMotionActive, listMotionDurationMs],
);
const attachProjectListAutoAnimateRef = attachListAutoAnimateRef;
const attachThreadListAutoAnimateRef = attachListAutoAnimateRef;

const visibleThreads = useMemo(
() => sidebarThreads.filter((thread) => thread.archivedAt === null),
Expand Down
24 changes: 20 additions & 4 deletions apps/web/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ import { useHandleNewThread } from "../hooks/useHandleNewThread";
import { openCommandPalette } from "../commandPaletteBus";
import { startNewThreadFromContext } from "../lib/chatThreadActions";
import { useClientSettings } from "../hooks/useSettings";
import { usePanelAnimationSettings } from "../panelAnimations";
import { useCopyToClipboard } from "../hooks/useCopyToClipboard";
import { useLocalStorage } from "../hooks/useLocalStorage";
import { useNowMinute } from "../hooks/useNowMinute";
Expand Down Expand Up @@ -3458,10 +3459,25 @@ export default function Sidebar() {
updateThreadJumpHintsVisibility(shouldShowJumpHintsNow);
}, [shouldShowJumpHintsNow, updateThreadJumpHintsVisibility]);

const attachListAutoAnimateRef = useCallback((node: HTMLUListElement | null) => {
if (!node) return;
autoAnimate(node, { duration: 150, easing: "ease-out" });
}, []);
// The thread list follows the same Motion setting as the panels: at the
// default 0 ms a row appears, moves, or leaves immediately. auto-animate
// also polls every row's position for as long as it is attached, which alone
// kept an idle app on a quarter of a core (#4693), so a list that never
// animates is never attached, and a detached list is destroyed rather than
// left polling.
const { active: listMotionActive, durationMs: listMotionDurationMs } =
usePanelAnimationSettings();
const attachListAutoAnimateRef = useCallback(
(node: HTMLUListElement | null) => {
if (!node || !listMotionActive) return;
const controller = autoAnimate(node, {
duration: Math.min(150, listMotionDurationMs),
easing: "ease-out",
});
return () => controller.destroy?.();
},
[listMotionActive, listMotionDurationMs],
);

// New thread defaults to the project you're in (active thread's project,
// falling back to the top project) — same resolution the command palette
Expand Down
4 changes: 4 additions & 0 deletions docs/user/thread-sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,10 @@ The main sidebar, right panel, and terminal drawer open and close immediately by
The duration can be set up to 400 ms. Clicking the preview replays all three panel transitions; at
0 ms, it snaps between the same open and closed states.

Thread rows in the sidebar follow the same setting. At 0 ms, or when your system asks for reduced
motion, a thread appears, moves between sections, or leaves the list immediately; otherwise it
slides into place.

## Environment icons

When you are connected to more than one environment, every thread that lives somewhere other than
Expand Down
Loading