fix(web): summarize session titles, sort by activity, and paginate history (BUG-553)
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -40,6 +40,7 @@ import {
|
||||
import { ThemePreferenceMenu } from "@/components/theme-preference-menu";
|
||||
import { UserAvatar } from "@/components/user-avatar";
|
||||
import type { BeamAvatar } from "@/lib/beam-avatar";
|
||||
import { groupSessionsByRecency } from "@/lib/session-groups";
|
||||
|
||||
export type SidebarAccount = {
|
||||
name: string;
|
||||
@@ -106,12 +107,25 @@ export function AppSidebar({
|
||||
const { isMobile, setOpen, setOpenMobile, state, viewport } = useSidebar();
|
||||
const firstSessionRef = useRef<HTMLButtonElement>(null);
|
||||
const historyHeadingRef = useRef<HTMLElement>(null);
|
||||
const loadMoreRef = useRef<HTMLDivElement>(null);
|
||||
const isCollapsedDesktop = state === "collapsed" && !isMobile;
|
||||
const showExpandedContent = !isCollapsedDesktop;
|
||||
const menuPlacement = `${viewport}:${state}`;
|
||||
const previousMenuPlacement = useRef(menuPlacement);
|
||||
const favoriteSessions = sessions.filter((session) => session.pinned);
|
||||
const historySessions = sessions.filter((session) => !session.pinned);
|
||||
const historyGroups = groupSessionsByRecency(historySessions);
|
||||
|
||||
useEffect(() => {
|
||||
if (!sessionControls.hasMore || !sessionControls.onLoadMore) return;
|
||||
const node = loadMoreRef.current;
|
||||
if (!node) return;
|
||||
const observer = new IntersectionObserver((entries) => {
|
||||
if (entries.some((entry) => entry.isIntersecting)) sessionControls.onLoadMore?.();
|
||||
});
|
||||
observer.observe(node);
|
||||
return () => observer.disconnect();
|
||||
}, [historySessions.length, sessionControls.hasMore, sessionControls.onLoadMore]);
|
||||
|
||||
useEffect(() => {
|
||||
if (previousMenuPlacement.current !== menuPlacement && accountMenuOpen) {
|
||||
@@ -259,7 +273,16 @@ export function AppSidebar({
|
||||
<SidebarGroupContent className="sidebar-nested">
|
||||
{historySessions.length === 0 ? <p className="sidebar-empty">暂无对话,点上方「新建对话」开始</p> : (
|
||||
<SidebarMenu className="session-list">
|
||||
{historySessions.map((session, index) => renderSession(session, favoriteSessions.length + index))}
|
||||
{historyGroups.map((group) => (
|
||||
<div key={group.key}>
|
||||
<p className="sidebar-group-label">{group.label}</p>
|
||||
{group.sessions.map((session) => renderSession(
|
||||
session,
|
||||
favoriteSessions.length + historySessions.findIndex((item) => item.id === session.id),
|
||||
))}
|
||||
</div>
|
||||
))}
|
||||
{sessionControls.hasMore ? <div ref={loadMoreRef} aria-hidden className="session-list-sentinel" /> : null}
|
||||
</SidebarMenu>
|
||||
)}
|
||||
</SidebarGroupContent>
|
||||
|
||||
@@ -19,13 +19,17 @@ import { sessionMutationMenuVisible } from "@/lib/chat-session-persistence";
|
||||
export type SidebarSession = {
|
||||
readonly id: string;
|
||||
readonly title: string;
|
||||
readonly subtitle?: string | null;
|
||||
readonly pinned: boolean;
|
||||
readonly archived: boolean;
|
||||
readonly updatedAt: number;
|
||||
};
|
||||
|
||||
export type SidebarSessionControls = {
|
||||
readonly archivedCount: number;
|
||||
readonly showingArchived: boolean;
|
||||
readonly hasMore?: boolean;
|
||||
readonly onLoadMore?: () => void;
|
||||
readonly menuSessionId: string | null;
|
||||
readonly disabled: boolean;
|
||||
readonly onToggleArchivedView: () => void;
|
||||
@@ -96,6 +100,7 @@ export const SidebarSessionRow = forwardRef<HTMLButtonElement, SidebarSessionRow
|
||||
<span className="truncate">{session.title}</span>
|
||||
{opening ? <span className="session-opening-note">{RECTIFICATION_SIDEBAR_OPENING_NOTE}</span> : null}
|
||||
</span>
|
||||
{session.subtitle ? <small className="session-subtitle">{session.subtitle}</small> : null}
|
||||
</SidebarMenuButton>
|
||||
<Menu.Trigger
|
||||
className="session-menu-trigger"
|
||||
|
||||
Reference in New Issue
Block a user