From 2ce46b6196b74a084a27bc139013e05492f8c333 Mon Sep 17 00:00:00 2001 From: Mikerpen22 <45140812+Mikerpen22@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:22:56 -0700 Subject: [PATCH 1/2] feat(web): switch sidebar grouping and project order from the sidebar header A view menu next to the project filter toggles Group by project, the Working section or two-line folder rows (whichever applies to the current view), and the project sort order. Each control writes the same client setting as its Settings row, so both stay in sync. --- .../components/settings/SettingsPanels.tsx | 11 +-- .../sidebar/SidebarThreadHeader.tsx | 96 ++++++++++++++++++- 2 files changed, 99 insertions(+), 8 deletions(-) diff --git a/apps/web/src/components/settings/SettingsPanels.tsx b/apps/web/src/components/settings/SettingsPanels.tsx index 31234fb6be51a..0697601b58a5d 100644 --- a/apps/web/src/components/settings/SettingsPanels.tsx +++ b/apps/web/src/components/settings/SettingsPanels.tsx @@ -84,6 +84,10 @@ import { Button } from "../ui/button"; import { DraftInput } from "../ui/draft-input"; import { Input } from "../ui/input"; import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../ui/select"; +import { + isSidebarProjectSortOrder, + SIDEBAR_PROJECT_SORT_ORDER_LABELS, +} from "../sidebar/SidebarThreadHeader"; import { Switch } from "../ui/switch"; import { ScopedSwitch } from "./ScopedSwitch"; import { stackedThreadToast, toastManager } from "../ui/toast"; @@ -131,13 +135,6 @@ const RESPONSE_STREAMING_MODE_DESCRIPTIONS: Record = { - updated_at: msg("Last user message"), - created_at: msg("Created at"), - manual: msg("Manual"), -}; -const isSidebarProjectSortOrder = Schema.is(SidebarProjectSortOrder); - const TIMESTAMP_FORMAT_LABELS = { locale: msg("System default"), "12-hour": msg("12-hour"), diff --git a/apps/web/src/components/sidebar/SidebarThreadHeader.tsx b/apps/web/src/components/sidebar/SidebarThreadHeader.tsx index e400e03d27586..4a03ad429833c 100644 --- a/apps/web/src/components/sidebar/SidebarThreadHeader.tsx +++ b/apps/web/src/components/sidebar/SidebarThreadHeader.tsx @@ -1,4 +1,7 @@ -import { FolderPlusIcon, SearchIcon, SquarePenIcon, XIcon } from "lucide-react"; +import { SidebarProjectSortOrder } from "@t3tools/contracts/settings"; +import * as Schema from "effect/Schema"; +import { msg, useMessages } from "gt-react"; +import { ArrowDownUpIcon, FolderPlusIcon, SearchIcon, SquarePenIcon, XIcon } from "lucide-react"; import { type ComponentProps, type KeyboardEvent as ReactKeyboardEvent, @@ -7,11 +10,31 @@ import { type RefObject, } from "react"; +import { updateClientSettings, useClientSettings } from "~/hooks/useSettings"; import { cn } from "~/lib/utils"; import { Button } from "../ui/button"; +import { + Menu, + MenuCheckboxItem, + MenuGroup, + MenuGroupLabel, + MenuPopup, + MenuRadioGroup, + MenuRadioItem, + MenuRadioItemIndicator, + MenuSeparator, + MenuTrigger, +} from "../ui/menu"; import { SidebarInput, SidebarMenuButton } from "../ui/sidebar"; import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; +export const SIDEBAR_PROJECT_SORT_ORDER_LABELS: Record = { + updated_at: msg("Last user message"), + created_at: msg("Created at"), + manual: msg("Manual"), +}; +export const isSidebarProjectSortOrder = Schema.is(SidebarProjectSortOrder); + export interface SidebarThreadHeaderProps { hasProjects: boolean; projectScope: ReactNode; @@ -100,6 +123,7 @@ export function SidebarThreadHeader({ {hasProjects ? ( <> {projectScope} + @@ -130,6 +154,76 @@ export function SidebarThreadHeader({ ); } +function SidebarViewMenu() { + const m = useMessages(); + const groupByProject = useClientSettings((settings) => settings.boringSidebarEnabled); + const projectSortOrder = useClientSettings((settings) => settings.sidebarProjectSortOrder); + const workingShelfEnabled = useClientSettings((settings) => settings.sidebarWorkingShelfEnabled); + const multilineRows = useClientSettings((settings) => settings.boringSidebarMultilineRows); + + return ( + + }> + + + {/* Refocusing the trigger after a pointer close shows its focus ring and opens its tooltip. */} + closeType === "keyboard"}> + + void updateClientSettings({ boringSidebarEnabled: checked }) + } + > + Group by project + + {groupByProject ? ( + + void updateClientSettings({ boringSidebarMultilineRows: checked }) + } + > + Two-line rows + + ) : ( + + void updateClientSettings({ sidebarWorkingShelfEnabled: checked }) + } + > + Working section + + )} + + + Sort projects + { + if (isSidebarProjectSortOrder(value)) { + void updateClientSettings({ sidebarProjectSortOrder: value }); + } + }} + > + {SidebarProjectSortOrder.literals.map((sortOrder) => ( + + + {m(SIDEBAR_PROJECT_SORT_ORDER_LABELS[sortOrder])} + + + + ))} + + + + + ); +} + export function SidebarHeaderIconButton({ label, tooltip = label, From 005e97e225358295b859a1f340cc7772ace19214 Mon Sep 17 00:00:00 2001 From: Mikerpen22 <45140812+Mikerpen22@users.noreply.github.com> Date: Sun, 8 Nov 2026 16:25:07 -0700 Subject: [PATCH 2/2] docs: point the sidebar guide at the new view options menu --- docs/user/thread-sidebar.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/user/thread-sidebar.md b/docs/user/thread-sidebar.md index 5dd5a4bf59205..66b5efbab6482 100644 --- a/docs/user/thread-sidebar.md +++ b/docs/user/thread-sidebar.md @@ -140,8 +140,9 @@ there. A thread returns to the top of the active list when it finishes, fails, o answer. The Working section lists the thread you last sent work to first. Pinned threads stay in the pinned section. -This is on by default. Turn it off with **Settings → General → Working section** on web and desktop, -or **Settings → General → Working section** on iOS and Android. Each device keeps its own +This is on by default. Turn it off from the sidebar's **View options** menu or +**Settings → General → Working section** on web and desktop, or **Settings → General → Working +section** on iOS and Android. Each device keeps its own choice. While this is on, the active list is ordered by when each thread last came back to you, so you @@ -149,8 +150,9 @@ cannot drag or move threads within it. Your saved order returns when you turn it ### Group threads by project (beta) -Turn on **Settings → Labs → Boring sidebar (group by project)** on web and desktop to put active -threads in a folder for each project. Pinned projects come first. A project with no active +Turn on **Group by project** in the sidebar's **View options** menu, or **Settings → Labs → Boring +sidebar (group by project)**, on web and desktop to put active threads in a folder for each +project. The same menu sets the project order. Pinned projects come first. A project with no active threads has no folder unless it is pinned or you pick it in the project filter. An archived project's folder comes last and shows only while it has active threads. Click a folder to open or close it. A closed folder shows how many threads it holds, and a dot when one of them