Skip to content

Commit 94c8eb6

Browse files
authored
Merge branch 'main' into feat/provider-usage-indicator
2 parents b78fcb1 + 466d8ee commit 94c8eb6

11 files changed

Lines changed: 308 additions & 33 deletions

File tree

.plans/19-remote-endpoints-hosted-static.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -347,4 +347,3 @@ Each implementation PR should run:
347347
- `bun typecheck`
348348
- focused tests for changed backend/web behavior
349349
- backend tests for any server-side endpoint discovery or auth changes using `bun run test`, never `bun test`
350-

apps/desktop/src/clientPersistence.test.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ const clientSettings: ClientSettings = {
6464
},
6565
sidebarProjectSortOrder: "manual",
6666
sidebarThreadSortOrder: "created_at",
67+
sidebarThreadPreviewCount: 6,
6768
timestampFormat: "24-hour",
6869
};
6970

apps/web/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
"test:browser:install": "playwright install --with-deps chromium"
1414
},
1515
"dependencies": {
16-
"@base-ui/react": "^1.2.0",
16+
"@base-ui/react": "^1.4.1",
1717
"@dnd-kit/core": "^6.3.1",
1818
"@dnd-kit/modifiers": "^9.0.0",
1919
"@dnd-kit/sortable": "^10.0.0",

apps/web/src/components/Sidebar.tsx

Lines changed: 94 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,10 @@ import {
5454
} from "@t3tools/client-runtime";
5555
import { Link, useLocation, useNavigate, useParams, useRouter } from "@tanstack/react-router";
5656
import {
57+
MAX_SIDEBAR_THREAD_PREVIEW_COUNT,
58+
MIN_SIDEBAR_THREAD_PREVIEW_COUNT,
5759
type SidebarProjectSortOrder,
60+
type SidebarThreadPreviewCount,
5861
type SidebarThreadSortOrder,
5962
} from "@t3tools/contracts/settings";
6063
import { usePrimaryEnvironmentId } from "../environments/primary";
@@ -128,6 +131,13 @@ import {
128131
MenuSeparator,
129132
MenuTrigger,
130133
} from "./ui/menu";
134+
import {
135+
NumberField,
136+
NumberFieldDecrement,
137+
NumberFieldGroup,
138+
NumberFieldIncrement,
139+
NumberFieldInput,
140+
} from "./ui/number-field";
131141
import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "./ui/select";
132142
import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
133143
import {
@@ -186,7 +196,6 @@ import {
186196
type SidebarProjectSnapshot,
187197
} from "../sidebarProjectGrouping";
188198
import { SidebarProviderUpdatePill } from "./sidebar/SidebarProviderUpdatePill";
189-
const THREAD_PREVIEW_LIMIT = 6;
190199
const SIDEBAR_SORT_LABELS: Record<SidebarProjectSortOrder, string> = {
191200
updated_at: "Last user message",
192201
created_at: "Created at",
@@ -207,6 +216,13 @@ const PROJECT_GROUPING_MODE_LABELS: Record<SidebarProjectGroupingMode, string> =
207216
separate: "Keep separate",
208217
};
209218

219+
function clampSidebarThreadPreviewCount(value: number): SidebarThreadPreviewCount {
220+
return Math.min(
221+
MAX_SIDEBAR_THREAD_PREVIEW_COUNT,
222+
Math.max(MIN_SIDEBAR_THREAD_PREVIEW_COUNT, value),
223+
) as SidebarThreadPreviewCount;
224+
}
225+
210226
function formatProjectMemberActionLabel(
211227
member: SidebarProjectGroupMember,
212228
groupedProjectCount: number,
@@ -936,6 +952,9 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
936952
sidebarProjectGroupingOverrides: settings.sidebarProjectGroupingOverrides,
937953
}));
938954
const { updateSettings } = useUpdateSettings();
955+
const sidebarThreadPreviewCount = useSettings<SidebarThreadPreviewCount>(
956+
(settings) => settings.sidebarThreadPreviewCount,
957+
);
939958
const router = useRouter();
940959
const { isMobile, setOpenMobile } = useSidebar();
941960
const markThreadUnread = useUiStateStore((state) => state.markThreadUnread);
@@ -1159,11 +1178,11 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
11591178
},
11601179
});
11611180
};
1162-
const hasOverflowingThreads = visibleProjectThreads.length > THREAD_PREVIEW_LIMIT;
1181+
const hasOverflowingThreads = visibleProjectThreads.length > sidebarThreadPreviewCount;
11631182
const previewThreads =
11641183
isThreadListExpanded || !hasOverflowingThreads
11651184
? visibleProjectThreads
1166-
: visibleProjectThreads.slice(0, THREAD_PREVIEW_LIMIT);
1185+
: visibleProjectThreads.slice(0, sidebarThreadPreviewCount);
11671186
const visibleThreadKeys = new Set(
11681187
[...previewThreads, ...(pinnedCollapsedThread ? [pinnedCollapsedThread] : [])].map((thread) =>
11691188
scopedThreadKey(scopeThreadRef(thread.environmentId, thread.id)),
@@ -1192,6 +1211,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
11921211
pinnedCollapsedThread,
11931212
projectExpanded,
11941213
projectThreads,
1214+
sidebarThreadPreviewCount,
11951215
threadLastVisitedAts,
11961216
visibleProjectThreads,
11971217
]);
@@ -2253,17 +2273,35 @@ function ProjectSortMenu({
22532273
projectSortOrder,
22542274
threadSortOrder,
22552275
projectGroupingMode,
2276+
threadPreviewCount,
22562277
onProjectSortOrderChange,
22572278
onThreadSortOrderChange,
22582279
onProjectGroupingModeChange,
2280+
onThreadPreviewCountChange,
22592281
}: {
22602282
projectSortOrder: SidebarProjectSortOrder;
22612283
threadSortOrder: SidebarThreadSortOrder;
22622284
projectGroupingMode: SidebarProjectGroupingMode;
2285+
threadPreviewCount: SidebarThreadPreviewCount;
22632286
onProjectSortOrderChange: (sortOrder: SidebarProjectSortOrder) => void;
22642287
onThreadSortOrderChange: (sortOrder: SidebarThreadSortOrder) => void;
22652288
onProjectGroupingModeChange: (mode: SidebarProjectGroupingMode) => void;
2289+
onThreadPreviewCountChange: (count: SidebarThreadPreviewCount) => void;
22662290
}) {
2291+
const handleThreadPreviewCountChange = useCallback(
2292+
(nextValue: number | null) => {
2293+
if (nextValue === null) {
2294+
return;
2295+
}
2296+
2297+
const clampedValue = clampSidebarThreadPreviewCount(nextValue);
2298+
if (clampedValue !== threadPreviewCount) {
2299+
onThreadPreviewCountChange(clampedValue);
2300+
}
2301+
},
2302+
[onThreadPreviewCountChange, threadPreviewCount],
2303+
);
2304+
22672305
return (
22682306
<Menu>
22692307
<Tooltip>
@@ -2274,9 +2312,9 @@ function ProjectSortMenu({
22742312
>
22752313
<ArrowUpDownIcon className="size-3.5" />
22762314
</TooltipTrigger>
2277-
<TooltipPopup side="right">Sort projects</TooltipPopup>
2315+
<TooltipPopup side="right">Sidebar options</TooltipPopup>
22782316
</Tooltip>
2279-
<MenuPopup align="end" side="bottom" className="min-w-44">
2317+
<MenuPopup align="end" side="bottom" className="min-w-52">
22802318
<MenuGroup>
22812319
<div className="px-2 py-1 sm:text-xs font-medium text-muted-foreground">
22822320
Sort projects
@@ -2315,6 +2353,42 @@ function ProjectSortMenu({
23152353
))}
23162354
</MenuRadioGroup>
23172355
</MenuGroup>
2356+
<MenuGroup>
2357+
<div className="px-2 pt-2 pb-1 text-muted-foreground sm:text-xs font-medium">
2358+
Visible threads
2359+
</div>
2360+
<div className="px-2 py-1">
2361+
<NumberField
2362+
aria-label="Visible thread count"
2363+
className="w-28 gap-0"
2364+
max={MAX_SIDEBAR_THREAD_PREVIEW_COUNT}
2365+
min={MIN_SIDEBAR_THREAD_PREVIEW_COUNT}
2366+
onValueChange={handleThreadPreviewCountChange}
2367+
size="sm"
2368+
step={1}
2369+
value={threadPreviewCount}
2370+
>
2371+
<NumberFieldGroup className="h-7 rounded-md sm:h-6.5">
2372+
<NumberFieldDecrement
2373+
aria-label="Decrease visible thread count"
2374+
className="px-2 sm:px-2 [&_svg]:size-3.5"
2375+
/>
2376+
<NumberFieldInput
2377+
aria-label="Visible thread count"
2378+
className="h-7 w-9 grow-0 px-0 text-xs leading-7 sm:h-6.5 sm:leading-6.5"
2379+
inputMode="numeric"
2380+
onKeyDownCapture={(event) => {
2381+
event.stopPropagation();
2382+
}}
2383+
/>
2384+
<NumberFieldIncrement
2385+
aria-label="Increase visible thread count"
2386+
className="px-2 sm:px-2 [&_svg]:size-3.5"
2387+
/>
2388+
</NumberFieldGroup>
2389+
</NumberField>
2390+
</div>
2391+
</MenuGroup>
23182392
<MenuSeparator />
23192393
<MenuGroup>
23202394
<div className="px-2 pt-2 pb-1 font-medium text-muted-foreground sm:text-xs">
@@ -2462,6 +2536,7 @@ interface SidebarProjectsContentProps {
24622536
projectSortOrder: SidebarProjectSortOrder;
24632537
threadSortOrder: SidebarThreadSortOrder;
24642538
projectGroupingMode: SidebarProjectGroupingMode;
2539+
threadPreviewCount: SidebarThreadPreviewCount;
24652540
updateSettings: ReturnType<typeof useUpdateSettings>["updateSettings"];
24662541
openAddProject: () => void;
24672542
isManualProjectSorting: boolean;
@@ -2502,6 +2577,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
25022577
projectSortOrder,
25032578
threadSortOrder,
25042579
projectGroupingMode,
2580+
threadPreviewCount,
25052581
updateSettings,
25062582
openAddProject,
25072583
isManualProjectSorting,
@@ -2548,6 +2624,12 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
25482624
},
25492625
[updateSettings],
25502626
);
2627+
const handleThreadPreviewCountChange = useCallback(
2628+
(count: SidebarThreadPreviewCount) => {
2629+
updateSettings({ sidebarThreadPreviewCount: count });
2630+
},
2631+
[updateSettings],
2632+
);
25512633

25522634
return (
25532635
<SidebarContent className="gap-0">
@@ -2607,9 +2689,11 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
26072689
projectSortOrder={projectSortOrder}
26082690
threadSortOrder={threadSortOrder}
26092691
projectGroupingMode={projectGroupingMode}
2692+
threadPreviewCount={threadPreviewCount}
26102693
onProjectSortOrderChange={handleProjectSortOrderChange}
26112694
onThreadSortOrderChange={handleThreadSortOrderChange}
26122695
onProjectGroupingModeChange={handleProjectGroupingModeChange}
2696+
onThreadPreviewCountChange={handleThreadPreviewCountChange}
26132697
/>
26142698
<Tooltip>
26152699
<TooltipTrigger
@@ -2729,6 +2813,7 @@ export default function Sidebar() {
27292813
sidebarProjectGroupingMode: settings.sidebarProjectGroupingMode,
27302814
sidebarProjectGroupingOverrides: settings.sidebarProjectGroupingOverrides,
27312815
}));
2816+
const sidebarThreadPreviewCount = useSettings((s) => s.sidebarThreadPreviewCount);
27322817
const { updateSettings } = useUpdateSettings();
27332818
const { handleNewThread } = useNewThreadHandler();
27342819
const { archiveThread, deleteThread } = useThreadActions();
@@ -3024,18 +3109,19 @@ export default function Sidebar() {
30243109
return [];
30253110
}
30263111
const isThreadListExpanded = expandedThreadListsByProject.has(project.projectKey);
3027-
const hasOverflowingThreads = projectThreads.length > THREAD_PREVIEW_LIMIT;
3112+
const hasOverflowingThreads = projectThreads.length > sidebarThreadPreviewCount;
30283113
const previewThreads =
30293114
isThreadListExpanded || !hasOverflowingThreads
30303115
? projectThreads
3031-
: projectThreads.slice(0, THREAD_PREVIEW_LIMIT);
3116+
: projectThreads.slice(0, sidebarThreadPreviewCount);
30323117
const renderedThreads = pinnedCollapsedThread ? [pinnedCollapsedThread] : previewThreads;
30333118
return renderedThreads.map((thread) =>
30343119
scopedThreadKey(scopeThreadRef(thread.environmentId, thread.id)),
30353120
);
30363121
}),
30373122
[
30383123
sidebarThreadSortOrder,
3124+
sidebarThreadPreviewCount,
30393125
expandedThreadListsByProject,
30403126
projectExpandedById,
30413127
routeThreadKey,
@@ -3358,6 +3444,7 @@ export default function Sidebar() {
33583444
projectSortOrder={sidebarProjectSortOrder}
33593445
threadSortOrder={sidebarThreadSortOrder}
33603446
projectGroupingMode={sidebarProjectGroupingMode}
3447+
threadPreviewCount={sidebarThreadPreviewCount}
33613448
updateSettings={updateSettings}
33623449
openAddProject={openAddProjectCommandPalette}
33633450
isManualProjectSorting={isManualProjectSorting}

apps/web/src/components/settings/SettingsPanels.tsx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,6 +370,9 @@ export function useSettingsRestore(onRestored?: () => void) {
370370
...(settings.timestampFormat !== DEFAULT_UNIFIED_SETTINGS.timestampFormat
371371
? ["Time format"]
372372
: []),
373+
...(settings.sidebarThreadPreviewCount !== DEFAULT_UNIFIED_SETTINGS.sidebarThreadPreviewCount
374+
? ["Visible threads"]
375+
: []),
373376
...(settings.diffWordWrap !== DEFAULT_UNIFIED_SETTINGS.diffWordWrap
374377
? ["Diff line wrapping"]
375378
: []),
@@ -410,6 +413,7 @@ export function useSettingsRestore(onRestored?: () => void) {
410413
settings.diffIgnoreWhitespace,
411414
settings.diffWordWrap,
412415
settings.enableAssistantStreaming,
416+
settings.sidebarThreadPreviewCount,
413417
settings.timestampFormat,
414418
theme,
415419
],

apps/web/src/components/ui/input.tsx

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,31 @@ function Input({
2727
props.type === "file" &&
2828
"text-muted-foreground file:me-3 file:bg-transparent file:font-medium file:text-foreground file:text-sm",
2929
);
30+
let inputElement: React.ReactElement;
31+
32+
if (nativeInput) {
33+
const { style, onValueChange: _onValueChange, ...nativeInputProps } = props;
34+
const nativeStyle = typeof style === "function" ? undefined : style;
35+
36+
inputElement = (
37+
<input
38+
className={inputClassName}
39+
data-slot="input"
40+
size={typeof size === "number" ? size : undefined}
41+
style={nativeStyle}
42+
{...(nativeInputProps as React.ComponentProps<"input">)}
43+
/>
44+
);
45+
} else {
46+
inputElement = (
47+
<InputPrimitive
48+
className={inputClassName}
49+
data-slot="input"
50+
size={typeof size === "number" ? size : undefined}
51+
{...props}
52+
/>
53+
);
54+
}
3055

3156
return (
3257
<span
@@ -40,21 +65,7 @@ function Input({
4065
data-size={size}
4166
data-slot="input-control"
4267
>
43-
{nativeInput ? (
44-
<input
45-
className={inputClassName}
46-
data-slot="input"
47-
size={typeof size === "number" ? size : undefined}
48-
{...props}
49-
/>
50-
) : (
51-
<InputPrimitive
52-
className={inputClassName}
53-
data-slot="input"
54-
size={typeof size === "number" ? size : undefined}
55-
{...props}
56-
/>
57-
)}
68+
{inputElement}
5869
</span>
5970
);
6071
}

0 commit comments

Comments
 (0)