Skip to content
Merged
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: 0 additions & 1 deletion .plans/19-remote-endpoints-hosted-static.md
Original file line number Diff line number Diff line change
Expand Up @@ -347,4 +347,3 @@ Each implementation PR should run:
- `bun typecheck`
- focused tests for changed backend/web behavior
- backend tests for any server-side endpoint discovery or auth changes using `bun run test`, never `bun test`

1 change: 1 addition & 0 deletions apps/desktop/src/clientPersistence.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ const clientSettings: ClientSettings = {
},
sidebarProjectSortOrder: "manual",
sidebarThreadSortOrder: "created_at",
sidebarThreadPreviewCount: 6,
timestampFormat: "24-hour",
};

Expand Down
2 changes: 1 addition & 1 deletion apps/web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"test:browser:install": "playwright install --with-deps chromium"
},
"dependencies": {
"@base-ui/react": "^1.2.0",
"@base-ui/react": "^1.4.1",
"@dnd-kit/core": "^6.3.1",
"@dnd-kit/modifiers": "^9.0.0",
"@dnd-kit/sortable": "^10.0.0",
Expand Down
101 changes: 94 additions & 7 deletions apps/web/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,10 @@ import {
} from "@t3tools/client-runtime";
import { Link, useLocation, useNavigate, useParams, useRouter } from "@tanstack/react-router";
import {
MAX_SIDEBAR_THREAD_PREVIEW_COUNT,
MIN_SIDEBAR_THREAD_PREVIEW_COUNT,
type SidebarProjectSortOrder,
type SidebarThreadPreviewCount,
type SidebarThreadSortOrder,
} from "@t3tools/contracts/settings";
import { usePrimaryEnvironmentId } from "../environments/primary";
Expand Down Expand Up @@ -128,6 +131,13 @@ import {
MenuSeparator,
MenuTrigger,
} from "./ui/menu";
import {
NumberField,
NumberFieldDecrement,
NumberFieldGroup,
NumberFieldIncrement,
NumberFieldInput,
} from "./ui/number-field";
import { Select, SelectItem, SelectPopup, SelectTrigger, SelectValue } from "./ui/select";
import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
import {
Expand Down Expand Up @@ -186,7 +196,6 @@ import {
type SidebarProjectSnapshot,
} from "../sidebarProjectGrouping";
import { SidebarProviderUpdatePill } from "./sidebar/SidebarProviderUpdatePill";
const THREAD_PREVIEW_LIMIT = 6;
const SIDEBAR_SORT_LABELS: Record<SidebarProjectSortOrder, string> = {
updated_at: "Last user message",
created_at: "Created at",
Expand All @@ -207,6 +216,13 @@ const PROJECT_GROUPING_MODE_LABELS: Record<SidebarProjectGroupingMode, string> =
separate: "Keep separate",
};

function clampSidebarThreadPreviewCount(value: number): SidebarThreadPreviewCount {
return Math.min(
MAX_SIDEBAR_THREAD_PREVIEW_COUNT,
Math.max(MIN_SIDEBAR_THREAD_PREVIEW_COUNT, value),
) as SidebarThreadPreviewCount;
}

function formatProjectMemberActionLabel(
member: SidebarProjectGroupMember,
groupedProjectCount: number,
Expand Down Expand Up @@ -936,6 +952,9 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
sidebarProjectGroupingOverrides: settings.sidebarProjectGroupingOverrides,
}));
const { updateSettings } = useUpdateSettings();
const sidebarThreadPreviewCount = useSettings<SidebarThreadPreviewCount>(
(settings) => settings.sidebarThreadPreviewCount,
);
const router = useRouter();
const { isMobile, setOpenMobile } = useSidebar();
const markThreadUnread = useUiStateStore((state) => state.markThreadUnread);
Expand Down Expand Up @@ -1159,11 +1178,11 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
},
});
};
const hasOverflowingThreads = visibleProjectThreads.length > THREAD_PREVIEW_LIMIT;
const hasOverflowingThreads = visibleProjectThreads.length > sidebarThreadPreviewCount;
const previewThreads =
isThreadListExpanded || !hasOverflowingThreads
? visibleProjectThreads
: visibleProjectThreads.slice(0, THREAD_PREVIEW_LIMIT);
: visibleProjectThreads.slice(0, sidebarThreadPreviewCount);
const visibleThreadKeys = new Set(
[...previewThreads, ...(pinnedCollapsedThread ? [pinnedCollapsedThread] : [])].map((thread) =>
scopedThreadKey(scopeThreadRef(thread.environmentId, thread.id)),
Expand Down Expand Up @@ -1192,6 +1211,7 @@ const SidebarProjectItem = memo(function SidebarProjectItem(props: SidebarProjec
pinnedCollapsedThread,
projectExpanded,
projectThreads,
sidebarThreadPreviewCount,
threadLastVisitedAts,
visibleProjectThreads,
]);
Expand Down Expand Up @@ -2253,17 +2273,35 @@ function ProjectSortMenu({
projectSortOrder,
threadSortOrder,
projectGroupingMode,
threadPreviewCount,
onProjectSortOrderChange,
onThreadSortOrderChange,
onProjectGroupingModeChange,
onThreadPreviewCountChange,
}: {
projectSortOrder: SidebarProjectSortOrder;
threadSortOrder: SidebarThreadSortOrder;
projectGroupingMode: SidebarProjectGroupingMode;
threadPreviewCount: SidebarThreadPreviewCount;
onProjectSortOrderChange: (sortOrder: SidebarProjectSortOrder) => void;
onThreadSortOrderChange: (sortOrder: SidebarThreadSortOrder) => void;
onProjectGroupingModeChange: (mode: SidebarProjectGroupingMode) => void;
onThreadPreviewCountChange: (count: SidebarThreadPreviewCount) => void;
}) {
const handleThreadPreviewCountChange = useCallback(
(nextValue: number | null) => {
if (nextValue === null) {
return;
}

const clampedValue = clampSidebarThreadPreviewCount(nextValue);
if (clampedValue !== threadPreviewCount) {
onThreadPreviewCountChange(clampedValue);
}
},
[onThreadPreviewCountChange, threadPreviewCount],
);

return (
<Menu>
<Tooltip>
Expand All @@ -2274,9 +2312,9 @@ function ProjectSortMenu({
>
<ArrowUpDownIcon className="size-3.5" />
</TooltipTrigger>
<TooltipPopup side="right">Sort projects</TooltipPopup>
<TooltipPopup side="right">Sidebar options</TooltipPopup>
</Tooltip>
<MenuPopup align="end" side="bottom" className="min-w-44">
<MenuPopup align="end" side="bottom" className="min-w-52">
<MenuGroup>
<div className="px-2 py-1 sm:text-xs font-medium text-muted-foreground">
Sort projects
Expand Down Expand Up @@ -2315,6 +2353,42 @@ function ProjectSortMenu({
))}
</MenuRadioGroup>
</MenuGroup>
<MenuGroup>
<div className="px-2 pt-2 pb-1 text-muted-foreground sm:text-xs font-medium">
Visible threads
</div>
<div className="px-2 py-1">
<NumberField
aria-label="Visible thread count"
className="w-28 gap-0"
max={MAX_SIDEBAR_THREAD_PREVIEW_COUNT}
min={MIN_SIDEBAR_THREAD_PREVIEW_COUNT}
onValueChange={handleThreadPreviewCountChange}
size="sm"
step={1}
value={threadPreviewCount}
>
<NumberFieldGroup className="h-7 rounded-md sm:h-6.5">
<NumberFieldDecrement
aria-label="Decrease visible thread count"
className="px-2 sm:px-2 [&_svg]:size-3.5"
/>
<NumberFieldInput
aria-label="Visible thread count"
className="h-7 w-9 grow-0 px-0 text-xs leading-7 sm:h-6.5 sm:leading-6.5"
inputMode="numeric"
onKeyDownCapture={(event) => {
event.stopPropagation();
}}
/>
<NumberFieldIncrement
aria-label="Increase visible thread count"
className="px-2 sm:px-2 [&_svg]:size-3.5"
/>
</NumberFieldGroup>
</NumberField>
</div>
</MenuGroup>
<MenuSeparator />
<MenuGroup>
<div className="px-2 pt-2 pb-1 font-medium text-muted-foreground sm:text-xs">
Expand Down Expand Up @@ -2462,6 +2536,7 @@ interface SidebarProjectsContentProps {
projectSortOrder: SidebarProjectSortOrder;
threadSortOrder: SidebarThreadSortOrder;
projectGroupingMode: SidebarProjectGroupingMode;
threadPreviewCount: SidebarThreadPreviewCount;
updateSettings: ReturnType<typeof useUpdateSettings>["updateSettings"];
openAddProject: () => void;
isManualProjectSorting: boolean;
Expand Down Expand Up @@ -2502,6 +2577,7 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
projectSortOrder,
threadSortOrder,
projectGroupingMode,
threadPreviewCount,
updateSettings,
openAddProject,
isManualProjectSorting,
Expand Down Expand Up @@ -2548,6 +2624,12 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
},
[updateSettings],
);
const handleThreadPreviewCountChange = useCallback(
(count: SidebarThreadPreviewCount) => {
updateSettings({ sidebarThreadPreviewCount: count });
},
[updateSettings],
);

return (
<SidebarContent className="gap-0">
Expand Down Expand Up @@ -2607,9 +2689,11 @@ const SidebarProjectsContent = memo(function SidebarProjectsContent(
projectSortOrder={projectSortOrder}
threadSortOrder={threadSortOrder}
projectGroupingMode={projectGroupingMode}
threadPreviewCount={threadPreviewCount}
onProjectSortOrderChange={handleProjectSortOrderChange}
onThreadSortOrderChange={handleThreadSortOrderChange}
onProjectGroupingModeChange={handleProjectGroupingModeChange}
onThreadPreviewCountChange={handleThreadPreviewCountChange}
/>
<Tooltip>
<TooltipTrigger
Expand Down Expand Up @@ -2729,6 +2813,7 @@ export default function Sidebar() {
sidebarProjectGroupingMode: settings.sidebarProjectGroupingMode,
sidebarProjectGroupingOverrides: settings.sidebarProjectGroupingOverrides,
}));
const sidebarThreadPreviewCount = useSettings((s) => s.sidebarThreadPreviewCount);
const { updateSettings } = useUpdateSettings();
const { handleNewThread } = useNewThreadHandler();
const { archiveThread, deleteThread } = useThreadActions();
Expand Down Expand Up @@ -3024,18 +3109,19 @@ export default function Sidebar() {
return [];
}
const isThreadListExpanded = expandedThreadListsByProject.has(project.projectKey);
const hasOverflowingThreads = projectThreads.length > THREAD_PREVIEW_LIMIT;
const hasOverflowingThreads = projectThreads.length > sidebarThreadPreviewCount;
const previewThreads =
isThreadListExpanded || !hasOverflowingThreads
? projectThreads
: projectThreads.slice(0, THREAD_PREVIEW_LIMIT);
: projectThreads.slice(0, sidebarThreadPreviewCount);
const renderedThreads = pinnedCollapsedThread ? [pinnedCollapsedThread] : previewThreads;
return renderedThreads.map((thread) =>
scopedThreadKey(scopeThreadRef(thread.environmentId, thread.id)),
);
}),
[
sidebarThreadSortOrder,
sidebarThreadPreviewCount,
expandedThreadListsByProject,
projectExpandedById,
routeThreadKey,
Expand Down Expand Up @@ -3358,6 +3444,7 @@ export default function Sidebar() {
projectSortOrder={sidebarProjectSortOrder}
threadSortOrder={sidebarThreadSortOrder}
projectGroupingMode={sidebarProjectGroupingMode}
threadPreviewCount={sidebarThreadPreviewCount}
updateSettings={updateSettings}
openAddProject={openAddProjectCommandPalette}
isManualProjectSorting={isManualProjectSorting}
Expand Down
4 changes: 4 additions & 0 deletions apps/web/src/components/settings/SettingsPanels.tsx
Comment thread
macroscopeapp[bot] marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -364,6 +364,9 @@ export function useSettingsRestore(onRestored?: () => void) {
...(settings.timestampFormat !== DEFAULT_UNIFIED_SETTINGS.timestampFormat
? ["Time format"]
: []),
...(settings.sidebarThreadPreviewCount !== DEFAULT_UNIFIED_SETTINGS.sidebarThreadPreviewCount
? ["Visible threads"]
: []),
...(settings.diffWordWrap !== DEFAULT_UNIFIED_SETTINGS.diffWordWrap
? ["Diff line wrapping"]
: []),
Expand Down Expand Up @@ -400,6 +403,7 @@ export function useSettingsRestore(onRestored?: () => void) {
settings.diffIgnoreWhitespace,
settings.diffWordWrap,
settings.enableAssistantStreaming,
settings.sidebarThreadPreviewCount,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Restore defaults omits new thread preview count setting

Medium Severity

The changedSettingLabels array correctly detects when sidebarThreadPreviewCount differs from the default and displays "Visible threads" in the restore confirmation dialog. However, the restoreDefaults callback's updateSettings call does not include sidebarThreadPreviewCount, so confirming "Restore defaults" will not actually reset the visible threads setting despite listing it in the confirmation prompt.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit f1eba1d. Configure here.

settings.timestampFormat,
theme,
],
Expand Down
41 changes: 26 additions & 15 deletions apps/web/src/components/ui/input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,31 @@ function Input({
props.type === "file" &&
"text-muted-foreground file:me-3 file:bg-transparent file:font-medium file:text-foreground file:text-sm",
);
let inputElement: React.ReactElement;

if (nativeInput) {
const { style, onValueChange: _onValueChange, ...nativeInputProps } = props;
const nativeStyle = typeof style === "function" ? undefined : style;

inputElement = (
<input
className={inputClassName}
data-slot="input"
size={typeof size === "number" ? size : undefined}
style={nativeStyle}
{...(nativeInputProps as React.ComponentProps<"input">)}
/>
);
} else {
inputElement = (
<InputPrimitive
className={inputClassName}
data-slot="input"
size={typeof size === "number" ? size : undefined}
{...props}
/>
);
}

return (
<span
Expand All @@ -40,21 +65,7 @@ function Input({
data-size={size}
data-slot="input-control"
>
{nativeInput ? (
<input
className={inputClassName}
data-slot="input"
size={typeof size === "number" ? size : undefined}
{...props}
/>
) : (
<InputPrimitive
className={inputClassName}
data-slot="input"
size={typeof size === "number" ? size : undefined}
{...props}
/>
)}
{inputElement}
</span>
);
}
Expand Down
Loading
Loading