Skip to content
Closed
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
1 change: 1 addition & 0 deletions apps/desktop/src/settings/DesktopClientSettings.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const clientSettings: ClientSettings = {
sidebarThreadSortOrder: "created_at",
boringSidebarEnabled: false,
boringSidebarMultilineRows: false,
sidebarThreadRowLines: "two",
threadDetailsUsageEnabled: false,
threadDetailsItems: {},
sidebarWorkingShelfEnabled: false,
Expand Down
28 changes: 28 additions & 0 deletions apps/web/src/components/Sidebar.logic.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
resolveSidebarDropVerb,
groupSidebarThreadsByProject,
resolveSidebarFolderDot,
resolveSidebarRowLines,
sidebarFolderExpansionKeys,
} from "./Sidebar.logic";
import { scopeThreadRef, scopedThreadKey } from "@t3tools/client-runtime/environment";
Expand Down Expand Up @@ -1641,6 +1642,33 @@ describe("resolveSidebarFolderDot", () => {
});
});

describe("resolveSidebarRowLines", () => {
it.each([
// Automatic keeps cards outside folders and the retired switch inside them.
{ setting: "automatic", section: "active", nested: false, multiline: true, lines: 3 },
{ setting: "automatic", section: "pinned", nested: false, multiline: false, lines: 3 },
{ setting: "automatic", section: "active", nested: true, multiline: false, lines: 1 },
{ setting: "automatic", section: "active", nested: true, multiline: true, lines: 2 },
// An explicit pick overrides the retired switch, in and out of folders.
{ setting: "one", section: "working", nested: false, multiline: true, lines: 1 },
{ setting: "two", section: "pinned", nested: false, multiline: false, lines: 2 },
{ setting: "one", section: "active", nested: true, multiline: true, lines: 1 },
{ setting: "three", section: "active", nested: true, multiline: false, lines: 3 },
// Snoozed and Settled stay on one line whatever the setting.
{ setting: "three", section: "snoozed", nested: false, multiline: false, lines: 1 },
{ setting: "automatic", section: "settled", nested: false, multiline: true, lines: 1 },
] as const)("$setting $section nested=$nested multiline=$multiline → $lines", (row) => {
expect(
resolveSidebarRowLines({
setting: row.setting,
section: row.section,
nested: row.nested,
folderRowsMultiline: row.multiline,
}),
).toBe(row.lines);
});
});

describe("shouldCreateNewThreadInCurrentProject", () => {
it("opens the picker when several projects are open, or only archived ones exist", () => {
expect(shouldCreateNewThreadInCurrentProject(false, 2, 3)).toBe(false);
Expand Down
24 changes: 23 additions & 1 deletion apps/web/src/components/Sidebar.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,11 @@ import {
type ScopedThreadRef,
type ThreadId,
} from "@t3tools/contracts";
import type { SidebarProjectSortOrder, SidebarThreadSortOrder } from "@t3tools/contracts/settings";
import type {
SidebarProjectSortOrder,
SidebarThreadRowLines,
SidebarThreadSortOrder,
} from "@t3tools/contracts/settings";
import type { AsyncResult } from "effect/reactivity";
import {
effectiveSnoozed,
Expand Down Expand Up @@ -194,6 +198,24 @@ export function resolveSidebarThreadSection(input: {
return "active";
}

export type SidebarRowLines = 1 | 2 | 3;

const SIDEBAR_ROW_LINES = { one: 1, two: 2, three: 3 } as const;

// Snoozed and Settled rows stay on one line. Automatic keeps the looks rows had
// before the setting: cards outside folders, the retired switch inside them.
export function resolveSidebarRowLines(input: {
readonly setting: SidebarThreadRowLines;
readonly section: SidebarSection;
readonly nested: boolean;
readonly folderRowsMultiline: boolean;
}): SidebarRowLines {
if (input.section === "snoozed" || input.section === "settled") return 1;
if (input.setting !== "automatic") return SIDEBAR_ROW_LINES[input.setting];
if (!input.nested) return 3;
return input.folderRowsMultiline ? 2 : 1;
}

const SIDEBAR_MARKER_PREFIX = "sidebar-marker-";

export type SidebarListMarker =
Expand Down
21 changes: 11 additions & 10 deletions apps/web/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,7 @@ import {
resolveSidebarSweepKeys,
resolveSidebarDropTarget,
resolveSidebarDropVerb,
resolveSidebarRowLines,
resolveSidebarThreadSection,
resolveSidebarReorderableThreadKeys,
searchSidebarThreads,
Expand Down Expand Up @@ -992,6 +993,7 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
});
const boringSidebarEnabled = useClientSettings((settings) => settings.boringSidebarEnabled);
const folderRowsMultiline = useClientSettings((settings) => settings.boringSidebarMultilineRows);
const rowLinesSetting = useClientSettings((settings) => settings.sidebarThreadRowLines);
const newThreadContext = useHandleNewThread();
// Row callbacks capture only the handler; the rest of the context changes with the route.
const { handleNewThread } = newThreadContext;
Expand Down Expand Up @@ -3323,15 +3325,13 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
const threadKey = scopedThreadKey(
scopeThreadRef(thread.environmentId, thread.id),
);
const isCard =
section === "active" || section === "pinned" || section === "working";
const rowVariant = folderRow
? folderRowsMultiline
? "folder-multiline"
: "folder"
: isCard
? "card"
: "slim";
const nested = folderRow !== undefined;
const rowLines = resolveSidebarRowLines({
setting: rowLinesSetting,
section,
nested,
folderRowsMultiline,
});
return (
<SortableThreadRow
key={threadKey}
Expand All @@ -3343,7 +3343,8 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
optimisticDrop !== null
}
thread={thread}
variant={rowVariant}
lines={rowLines}
nested={nested}
navigationParent={
folderRow ??
(section === "working" ||
Expand Down
55 changes: 37 additions & 18 deletions apps/web/src/components/settings/LabsSettingsPanel.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { useAuth } from "@clerk/react";
import { Link } from "@tanstack/react-router";
import { managedCapabilityDenial } from "@t3tools/contracts";
import { SidebarThreadRowLines } from "@t3tools/contracts/settings";
import * as Schema from "effect/Schema";

import { hasCloudPublicConfig } from "~/cloud/publicConfig";
import { useT3ConnectAuthPrompt } from "../clerk/useT3ConnectAuthPrompt";
import { Button } from "../ui/button";
import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "../ui/select";
import { Switch } from "../ui/switch";
import { ScopedSwitch } from "./ScopedSwitch";
import { SettingsPageContainer, SettingsRow, SettingsSection } from "./settingsLayout";
Expand All @@ -19,6 +22,7 @@ export function LabsSettingsPanel() {
<SettingsPageContainer>
<SettingsSection id="beta" title="Beta">
<BoringSidebarRow />
<ThreadRowLinesRow />
{/* Clerk is only mounted when this build has T3 Connect. */}
{hasCloudPublicConfig() ? <GuestLinksRowWithSignIn /> : <GuestLinksRow />}
<LinkedEnvironmentsRow />
Expand All @@ -44,32 +48,47 @@ function BoringSidebarRow() {
aria-label="Boring sidebar (group by project)"
/>
}
>
{settings.boringSidebarEnabled ? (
<div className="mt-2 mb-2 border-l-2 border-border/60 ps-3">
<BoringSidebarMultilineRow />
</div>
) : null}
</SettingsRow>
/>
);
}

function BoringSidebarMultilineRow() {
const THREAD_ROW_LINES_LABELS: Record<SidebarThreadRowLines, string> = {
automatic: "Automatic",
one: "1 line",
two: "2 lines",
three: "3 lines",
};

const isSidebarThreadRowLines = Schema.is(SidebarThreadRowLines);

function ThreadRowLinesRow() {
const settings = useScopedSettings();
const updateSettings = useUpdateScopedSettings();
return (
<SettingsRow
{...searchableSetting("boring-sidebar-multiline")}
className="px-0 py-2 sm:px-0"
description="Show folder threads on two lines: the title, then the branch, pull request, and agent."
{...searchableSetting("sidebar-thread-row-lines")}
description="Lines per sidebar thread: the title, then its branch, pull request, and agent, then its project. Automatic uses three, and one inside project folders. Snoozed and Settled always use one."
control={
<Switch
checked={settings.boringSidebarMultilineRows}
onCheckedChange={(checked) =>
updateSettings({ boringSidebarMultilineRows: Boolean(checked) })
}
aria-label="Two-line folder rows"
/>
<div className="w-full sm:w-40">
<Select
value={settings.sidebarThreadRowLines}
onValueChange={(value) => {
if (!isSidebarThreadRowLines(value)) return;
// Any pick retires the old two-line switch, so Automatic can return to one line.
updateSettings({ sidebarThreadRowLines: value, boringSidebarMultilineRows: false });
}}
>
<SelectTrigger size="sm" className="w-full min-w-0" aria-label="Thread rows">
<SelectValue>{THREAD_ROW_LINES_LABELS[settings.sidebarThreadRowLines]}</SelectValue>
</SelectTrigger>
<SelectPopup align="end" alignItemWithTrigger={false}>
<SelectItem value="automatic">Automatic</SelectItem>
<SelectItem value="one">1 line</SelectItem>
<SelectItem value="two">2 lines</SelectItem>
<SelectItem value="three">3 lines</SelectItem>
</SelectPopup>
</Select>
</div>
}
/>
);
Expand Down
10 changes: 5 additions & 5 deletions apps/web/src/components/settings/settingsSearch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -468,12 +468,12 @@ export const SETTINGS_SEARCH_ITEMS = [
searchTerms: ["beta folders project tree classic sidebar"],
},
{
id: "boring-sidebar-multiline",
title: "Two-line folder rows",
id: "sidebar-thread-row-lines",
title: "Thread rows",
to: "/settings/labs",
// Shown inside the boring sidebar row only while it is on, so search lands on the parent.
targetId: "boring-sidebar",
searchTerms: ["beta boring sidebar folder rows multiline branch pull request details"],
searchTerms: [
"beta sidebar thread rows lines compact dense card one two three multiline branch pull request",
],
},
{
id: "tool-call-visibility",
Expand Down
Loading
Loading