From 85a03b3b7e1b6dd2a7d5c98e51e13b4ccf30bd18 Mon Sep 17 00:00:00 2001 From: T3 Code PR Stack <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 28 Jul 2026 13:54:14 +0200 Subject: [PATCH] feat(web): date headers on classic Settled shelf (V2 parity) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When Hide settled shelves history under classic Recency/None, group settled rows with the same settle-time recency buckets as Sidebar V2 (Last Hour / Earlier Today / …). Reuse groupSettledThreadsByRecencyForSidebarV2 and the shared View preference for “Date headers on settled”. --- apps/web/src/components/Sidebar.logic.ts | 2 +- apps/web/src/components/Sidebar.tsx | 93 ++++++++++++++++++++++-- 2 files changed, 88 insertions(+), 7 deletions(-) diff --git a/apps/web/src/components/Sidebar.logic.ts b/apps/web/src/components/Sidebar.logic.ts index c4f3051292f..08eb6abc37d 100644 --- a/apps/web/src/components/Sidebar.logic.ts +++ b/apps/web/src/components/Sidebar.logic.ts @@ -63,7 +63,7 @@ export const THREAD_JUMP_HINT_SHOW_DELAY_MS = 100; export const SIDEBAR_THREAD_PREWARM_LIMIT = 10; // Settled-tail paging: recent history is the common lookup; the deep tail // stays behind an explicit Show more. Shared by SidebarV2, classic Recent -// (hide-settled shelf), and the board. +// (hide-settled shelf + recency headers), and the board. export const SETTLED_TAIL_INITIAL_COUNT = 10; export const SETTLED_TAIL_PAGE_COUNT = 25; export type SidebarNewThreadEnvMode = "local" | "worktree"; diff --git a/apps/web/src/components/Sidebar.tsx b/apps/web/src/components/Sidebar.tsx index c14acc75def..62f40211b07 100644 --- a/apps/web/src/components/Sidebar.tsx +++ b/apps/web/src/components/Sidebar.tsx @@ -218,6 +218,7 @@ import { orderItemsByPreferredIds, SETTLED_TAIL_INITIAL_COUNT, SETTLED_TAIL_PAGE_COUNT, + groupSettledThreadsByRecencyForSidebarV2, shouldClearThreadSelectionOnMouseDown, sortProjectsForSidebar, sortSettledThreadsForSidebarV2, @@ -235,6 +236,7 @@ import { useClientSettings, useUpdateClientSettings } from "~/hooks/useSettings" import { DEFAULT_HIDE_SETTLED_PROJECTS, DEFAULT_HIDE_SETTLED_RECENT, + DEFAULT_SIDEBAR_V2_SETTLED_RECENCY_HEADERS, DEFAULT_SIDEBAR_V2_SETTLED_SHELF_EXPANDED, DEFAULT_WEB_LIST_MODE, DEFAULT_WEB_THREAD_GROUPING, @@ -249,6 +251,7 @@ import { ListEnvironmentFilterSchema, ListHideSettledSchema, ListProjectFilterSchema, + SIDEBAR_V2_SETTLED_RECENCY_HEADERS_STORAGE_KEY, SIDEBAR_V2_SETTLED_SHELF_EXPANDED_STORAGE_KEY, WEB_LIST_MODE_LABELS, WEB_LIST_MODES, @@ -3674,7 +3677,13 @@ const SidebarRecentThreads = memo(function SidebarRecentThreads(props: { DEFAULT_SIDEBAR_V2_SETTLED_SHELF_EXPANDED, ListHideSettledSchema, ); + const [settledRecencyHeadersEnabled] = useLocalStorage( + SIDEBAR_V2_SETTLED_RECENCY_HEADERS_STORAGE_KEY, + DEFAULT_SIDEBAR_V2_SETTLED_RECENCY_HEADERS, + ListHideSettledSchema, + ); const [settledVisibleCount, setSettledVisibleCount] = useState(SETTLED_TAIL_INITIAL_COUNT); + const nowMinute = useNowMinute(); const { activeEntries, settledEntries } = useMemo(() => { if (!props.hideSettledThreads) { @@ -3773,6 +3782,32 @@ const SidebarRecentThreads = memo(function SidebarRecentThreads(props: { [setSettledShelfExpanded], ); + // Date headers under Settled (Last Hour / Earlier Today / …) — same helper + // and rules as Sidebar V2. Single-bucket pages omit headers; View menu can + // disable headers without changing settle-time sort order. + const settledRecencyLayout = useMemo(() => { + void nowMinute; + const layout = groupSettledThreadsByRecencyForSidebarV2( + renderedSettledEntries.map((entry) => entry.thread), + new Date(), + ); + if (!settledRecencyHeadersEnabled) { + return { groups: layout.groups, showHeaders: false }; + } + return layout; + }, [nowMinute, renderedSettledEntries, settledRecencyHeadersEnabled]); + + const settledEntryByThreadKey = useMemo( + () => + new Map( + renderedSettledEntries.map((entry) => [ + scopedThreadKey(scopeThreadRef(entry.thread.environmentId, entry.thread.id)), + entry, + ]), + ), + [renderedSettledEntries], + ); + // Multi-select range walks rendered rows only (collapsed shelf is out). const orderedRecentThreadKeys = useMemo( () => @@ -3811,6 +3846,38 @@ const SidebarRecentThreads = memo(function SidebarRecentThreads(props: { ); }; + const renderSettledRows = () => { + if (renderedSettledEntries.length === 0) { + return null; + } + if (settledRecencyLayout.showHeaders) { + return ( + <> + {settledRecencyLayout.groups.map((group) => ( +