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
74 changes: 74 additions & 0 deletions apps/web/src/shell/workbench-list.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ import { describe, expect, test } from "bun:test";

import type { Workbench } from "@corbits/chat-ui";

import type { SidebarRow } from "./sidebar-rows";
import {
filterSidebarRows,
orderWorkbenchRows,
renamePayload,
rowMenuLabels,
Expand All @@ -20,6 +22,10 @@ function workbench(overrides: Partial<Workbench> = {}): Workbench {
} as Workbench;
}

function row(overrides: Partial<Workbench> = {}): SidebarRow {
return { kind: "workbench", workbench: workbench(overrides) };
}

describe("rowMenuLabels", () => {
test("offers Pin for an unpinned row and Unpin for a pinned one", () => {
expect(rowMenuLabels({ pinned: false })).toEqual(["Rename", "Pin"]);
Expand Down Expand Up @@ -112,3 +118,71 @@ describe("orderWorkbenchRows", () => {
]);
});
});

describe("filterSidebarRows", () => {
// CL-6662: the row shows title + preview; search must match either, or a
// query visible in the preview (e.g. "Solvora") falsely returns no matches.
test("matches the displayed title", () => {
const rows = [
row({ id: "ch_a", title: "Launch plan" }),
row({ id: "ch_b", title: "Research brief" }),
];
expect(
filterSidebarRows(rows, "launch").map((r) => r.workbench.id),
).toEqual(["ch_a"]);
});

test("matches preview text even when the title does not (CL-6662)", () => {
const rows = [
row({
id: "ch_solvora",
title: "Myra",
preview: "Drafted the Solvora outreach email",
}),
row({
id: "ch_other",
title: "Scout",
preview: "Weekly digest ready",
}),
];
expect(
filterSidebarRows(rows, "Solvora").map((r) => r.workbench.id),
).toEqual(["ch_solvora"]);
});

test("is case-insensitive across title and preview", () => {
const rows = [
row({ id: "ch_1", title: "Myra", preview: "Talked about Acme pricing" }),
];
expect(filterSidebarRows(rows, "ACME").map((r) => r.workbench.id)).toEqual([
"ch_1",
]);
expect(filterSidebarRows(rows, "myra").map((r) => r.workbench.id)).toEqual([
"ch_1",
]);
});

test("an empty or whitespace-only query returns every row", () => {
const rows = [row({ id: "ch_a" }), row({ id: "ch_b", title: "Other" })];
expect(filterSidebarRows(rows, "").map((r) => r.workbench.id)).toEqual([
"ch_a",
"ch_b",
]);
expect(filterSidebarRows(rows, " ").map((r) => r.workbench.id)).toEqual([
"ch_a",
"ch_b",
]);
});

test("returns no rows when neither title nor preview matches", () => {
const rows = [row({ id: "ch_1", title: "Myra", preview: "Hello there" })];
expect(filterSidebarRows(rows, "zzz")).toEqual([]);
});

test("a missing preview still matches on title alone", () => {
const rows = [row({ id: "ch_1", title: "Launch plan" })];
expect(
filterSidebarRows(rows, "launch").map((r) => r.workbench.id),
).toEqual(["ch_1"]);
});
});
32 changes: 23 additions & 9 deletions apps/web/src/shell/workbench-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,28 @@ export function orderWorkbenchRows(
];
}

/**
* Sidebar search over what the row actually shows: the displayed title and
* the preview snippet. Title-only matching left people staring at a visible
* preview word (CL-6662) and a "No matches" empty state. Case-insensitive
* substring; empty/whitespace query keeps every row.
*/
export function filterSidebarRows(
rows: readonly SidebarRow[],
query: string,
): readonly SidebarRow[] {
const needle = query.trim().toLowerCase();
if (needle === "") return rows;
return rows.filter((row) => {
const title =
displayWorkbenchTitle(row.workbench.title, row.workbench.id) ||
CHAT_STRINGS.unnamedWorkbench;
if (title.toLowerCase().includes(needle)) return true;
const preview = row.workbench.preview ?? "";
return preview.toLowerCase().includes(needle);
});
}

/**
* One workbench row — avatar, name (the agent's for an agent conversation,
* the row's own title for a multi-party one), optional shared/live, optional
Expand Down Expand Up @@ -412,15 +434,7 @@ export function WorkbenchList({
);
}

const rowName = (row: SidebarRow): string =>
displayWorkbenchTitle(row.workbench.title, row.workbench.id) ||
CHAT_STRINGS.unnamedWorkbench;

const q = query.trim().toLowerCase();
const filtered =
q === ""
? all
: all.filter((row) => rowName(row).toLowerCase().includes(q));
const filtered = filterSidebarRows(all, query);

const tenantId = selectedTenantId ?? "";

Expand Down
Loading