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
11 changes: 4 additions & 7 deletions apps/web/src/components/settings/SettingsPanels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -137,13 +141,6 @@ const RESPONSE_STREAMING_MODE_DESCRIPTIONS: Record<ResponseStreamingMode, string
paragraph: "Each paragraph or code block appears as soon as it is complete.",
};

const SIDEBAR_PROJECT_SORT_ORDER_LABELS: Record<SidebarProjectSortOrder, string> = {
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"),
Expand Down
96 changes: 95 additions & 1 deletion apps/web/src/components/sidebar/SidebarThreadHeader.tsx
Original file line number Diff line number Diff line change
@@ -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,
Expand All @@ -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<SidebarProjectSortOrder, string> = {
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;
Expand Down Expand Up @@ -100,6 +123,7 @@ export function SidebarThreadHeader({
{hasProjects ? (
<>
{projectScope}
<SidebarViewMenu />
<SidebarHeaderIconButton label="Add project" onClick={onNewProject}>
<FolderPlusIcon />
</SidebarHeaderIconButton>
Expand Down Expand Up @@ -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 (
<Menu>
<MenuTrigger render={<SidebarHeaderIconButton label="View options" />}>
Comment thread
Mikerpen22 marked this conversation as resolved.
Resolved
<ArrowDownUpIcon className="size-4" />
</MenuTrigger>
{/* Refocusing the trigger after a pointer close shows its focus ring and opens its tooltip. */}
<MenuPopup align="end" finalFocus={(closeType) => closeType === "keyboard"}>
<MenuCheckboxItem
variant="switch"
checked={groupByProject}
onCheckedChange={(checked) =>
void updateClientSettings({ boringSidebarEnabled: checked })
}
>
Group by project
</MenuCheckboxItem>
{groupByProject ? (
<MenuCheckboxItem
variant="switch"
checked={multilineRows}
onCheckedChange={(checked) =>
void updateClientSettings({ boringSidebarMultilineRows: checked })
}
>
Two-line rows
</MenuCheckboxItem>
) : (
<MenuCheckboxItem
variant="switch"
checked={workingShelfEnabled}
onCheckedChange={(checked) =>
void updateClientSettings({ sidebarWorkingShelfEnabled: checked })
}
>
Working section
</MenuCheckboxItem>
)}
<MenuSeparator />
<MenuGroup>
<MenuGroupLabel>Sort projects</MenuGroupLabel>
<MenuRadioGroup
value={projectSortOrder}
onValueChange={(value) => {
if (isSidebarProjectSortOrder(value)) {
void updateClientSettings({ sidebarProjectSortOrder: value });
}
}}
>
{SidebarProjectSortOrder.literals.map((sortOrder) => (
<MenuRadioItem key={sortOrder} value={sortOrder}>
<span className="flex items-center justify-between gap-2">
{m(SIDEBAR_PROJECT_SORT_ORDER_LABELS[sortOrder])}
<MenuRadioItemIndicator />
</span>
</MenuRadioItem>
))}
</MenuRadioGroup>
</MenuGroup>
</MenuPopup>
</Menu>
);
}

export function SidebarHeaderIconButton({
label,
tooltip = label,
Expand Down
10 changes: 6 additions & 4 deletions docs/user/thread-sidebar.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,17 +141,19 @@ 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
cannot drag or move threads within it. Your saved order returns when you turn it off.

### 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
Expand Down
Loading