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
9 changes: 8 additions & 1 deletion apps/web/src/components/Sidebar.logic.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1048,14 +1048,21 @@ export function pullRequestMatchesSearchQuery(
return normalizedQuery.split(" ").every((token) => haystack.includes(token));
}

export const ADD_PROJECT_SCOPE_ITEM_VALUE = "add-project";

export function filterSidebarProjectScopeItems<TItem extends { readonly value: string }>(input: {
items: readonly TItem[];
query: string;
matches: (item: TItem, query: string) => boolean;
}): readonly TItem[] {
const query = input.query.trim();
if (query.length === 0) return input.items;
return input.items.filter((item) => item.value !== "all" && input.matches(item, query));
return input.items.filter(
(item) =>
item.value !== "all" &&
item.value !== ADD_PROJECT_SCOPE_ITEM_VALUE &&
input.matches(item, query),
);
}

interface SidebarProjectScopeMenuState {
Expand Down
28 changes: 21 additions & 7 deletions apps/web/src/components/Sidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ import {
CheckIcon,
ChevronDownIcon,
FolderIcon,
ListFilterIcon,
FolderPlusIcon,
PlusIcon,
SettingsIcon,
} from "lucide-react";
Expand Down Expand Up @@ -185,6 +185,7 @@ import {
buildBulkTitleRegenerationContextMenuItem,
buildBulkUnpinContextMenuItem,
deleteSelectedThreadEntries,
ADD_PROJECT_SCOPE_ITEM_VALUE,
filterSidebarProjectScopeItems,
firstValidTimestampMs,
isSidebarNestedLinkClick,
Expand Down Expand Up @@ -823,6 +824,7 @@ function useSidebarLookups() {
projectGroups.map((project) => [project.projectKey, project] as const),
);
const projectScopeItems = [
{ value: ADD_PROJECT_SCOPE_ITEM_VALUE, label: "Add project" },
{ value: "all", label: "All projects" },
...pickableProjectGroups(projectGroups).map((project) => ({
value: project.projectKey,
Expand Down Expand Up @@ -1172,7 +1174,7 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
projectScopeKeys.length > 1
? null
: (projectScopeItems.find((item) => item.value === (projectScopeKeys[0] ?? "all")) ??
projectScopeItems[0]!);
projectScopeItems.find((item) => item.value === "all")!);
const filteredProjectScopeItems = filterSidebarProjectScopeItems({
items: projectScopeItems,
query: projectScopeMenuState.query,
Expand Down Expand Up @@ -3010,7 +3012,10 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
return;
}
if (!item) return;
if (item.value === "all") {
if (item.value === ADD_PROJECT_SCOPE_ITEM_VALUE) {
dispatchProjectScopeMenu({ type: "open-changed", open: false });
openAddProjectCommandPalette();
} else if (item.value === "all") {
setProjectScopeKeys([]);
} else if (
eventDetails.event instanceof MouseEvent &&
Expand All @@ -3029,8 +3034,8 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
<SidebarHeaderIconButton
label={
scopedProjectGroups.length > 0
? `Filter threads by project: ${scopedProjectGroups.map((project) => project.displayName).join(", ")}`
: "Filter threads by project"
? `Projects: ${scopedProjectGroups.map((project) => project.displayName).join(", ")}`
: "Projects"
}
/>
}
Expand All @@ -3055,7 +3060,7 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
<ProjectFavicon project={scopedProjectGroups[0]} className="size-4" />
</span>
) : (
<ListFilterIcon className="size-4" />
<FolderIcon className="size-4" />
)}
</ComboboxTrigger>
{/* Refocusing the trigger after a pointer close shows its focus ring and opens its tooltip. */}
Expand Down Expand Up @@ -3115,6 +3120,16 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
<ComboboxEmpty>No matching projects.</ComboboxEmpty>
<ComboboxList aria-multiselectable>
{(item: (typeof projectScopeItems)[number]) => {
if (item.value === ADD_PROJECT_SCOPE_ITEM_VALUE) {
return (
<ComboboxItem key={item.value} hideIndicator value={item}>
<FolderPlusIcon className="size-4 shrink-0" />
<span className="min-w-0 truncate text-sm">{item.label}</span>
{/* Keeps "Add project" as tall as the project rows. */}
<span className="ml-auto size-7 shrink-0 sm:size-6" />
</ComboboxItem>
);
}
const project = projectGroupByScopeKey.get(item.value) ?? null;
const isSelected = project
? projectScopeKeys.includes(item.value)
Expand Down Expand Up @@ -3188,7 +3203,6 @@ export default function Sidebar({ routeReveal }: { routeReveal: SidebarRouteReve
</ComboboxPopup>
</Combobox>
}
onNewProject={openAddProjectCommandPalette}
onNewThread={handleNewThreadClick}
newThreadDisabled={projects.length === 0}
newThreadShortcutLabel={newThreadShortcutLabel}
Expand Down
15 changes: 3 additions & 12 deletions apps/web/src/components/sidebar/SidebarThreadHeader.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { FolderPlusIcon, SearchIcon, SquarePenIcon, XIcon } from "lucide-react";
import { SearchIcon, SquarePenIcon, XIcon } from "lucide-react";
import {
type ComponentProps,
type KeyboardEvent as ReactKeyboardEvent,
Expand All @@ -15,7 +15,6 @@ import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip";
export interface SidebarThreadHeaderProps {
hasProjects: boolean;
projectScope: ReactNode;
onNewProject: () => void;
onNewThread: (event: ReactMouseEvent) => void;
newThreadDisabled: boolean;
newThreadShortcutLabel: string | null | undefined;
Expand All @@ -34,7 +33,6 @@ export interface SidebarThreadHeaderProps {
export function SidebarThreadHeader({
hasProjects,
projectScope,
onNewProject,
onNewThread,
newThreadDisabled,
newThreadShortcutLabel,
Expand Down Expand Up @@ -97,14 +95,7 @@ export function SidebarThreadHeader({
) : null}
</div>
<div className="flex shrink-0 items-center">
{hasProjects ? (
<>
{projectScope}
<SidebarHeaderIconButton label="Add project" onClick={onNewProject}>
<FolderPlusIcon />
</SidebarHeaderIconButton>
</>
) : null}
{hasProjects ? projectScope : null}
<SidebarHeaderIconButton
label="New thread"
tooltip={
Expand All @@ -123,7 +114,7 @@ export function SidebarThreadHeader({
disabled={newThreadDisabled}
onClick={onNewThread}
>
<SquarePenIcon />
<SquarePenIcon className="size-[15px]" />
</SidebarHeaderIconButton>
</div>
</div>
Expand Down
Loading