Q188: search shows only the answer to the current words, and says while it is searching

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
ZacharyZhang-NYandClaude Opus 5.5 committed 2026-10-09 08:17:34 -04:00
1 parent 6bedba850c
commit 1b7106d511
3 files changed
+37 -11

No files matched your search

+2
View File
@@ -696,6 +696,8 @@ export const zh: Record<string, string> = {
"Working language": "工作语言",
"That did not work": "操作未成功",
"Working…": "处理中…",
"Searching…": "正在搜索…",
"Search failed: {reason}": "搜索失败:{reason}",
"Not up to date.": "数据不是最新的。",
"Showing data from {time}.": "当前显示 {time} 的数据。",
"Updating failed: {reason} (attempt {count}).": "更新失败:{reason}(第 {count} 次尝试)。",
+24 -11
View File
@@ -37,16 +37,22 @@ export function SearchDialog({
}) {
const [query, setQuery] = useState("");
const [filter, setFilter] = useState<SearchFilter>("all");
const [results, setResults] = useState<SearchResultRecord[]>([]);
// An answer belongs to the company, filter and words it was asked for; only the current question's answer is shown.
const [answer, setAnswer] = useState<{ key: string; results: SearchResultRecord[]; error: string | null } | null>(null);
const orgId = snapshot.activeOrgId;
const employee = useMemo(() => snapshot.employees.find((item) => item.orgId === orgId), [snapshot.employees, orgId]);
const canSearch = Boolean(orgId && employee && query.trim().length >= 2);
const visibleResults = canSearch ? results : [];
const trimmed = query.trim();
const canSearch = Boolean(orgId && employee && trimmed.length >= 2);
const key = `${orgId}|${filter}|${trimmed}`;
const current = canSearch && answer?.key === key ? answer : null;
const visibleResults = current?.results ?? [];
const searching = canSearch && !current;
useEffect(() => {
if (!open) return;
const trimmed = query.trim();
if (!orgId || !employee || trimmed.length < 2) return;
if (!open || !orgId || !employee || trimmed.length < 2) return;
// A reply to a question that has since changed is dropped, so a slow old answer never replaces a newer one.
let live = true;
const asked = `${orgId}|${filter}|${trimmed}`;
const timeout = window.setTimeout(() => {
void window.agentCompany.search({
orgId,
@@ -55,12 +61,17 @@ export function SearchDialog({
entityTypes: filter === "all" ? undefined : [filter],
limit: 12,
}).then((result) => {
if (!result.ok) throw new Error(result.error.message);
setResults(result.data);
if (!live) return;
setAnswer(result.ok ? { key: asked, results: result.data, error: null } : { key: asked, results: [], error: result.error.message });
}, (reason: unknown) => {
if (live) setAnswer({ key: asked, results: [], error: reason instanceof Error ? reason.message : String(reason) });
});
}, 120);
return () => window.clearTimeout(timeout);
}, [employee, filter, open, orgId, query]);
return () => {
live = false;
window.clearTimeout(timeout);
};
}, [employee, filter, open, orgId, trimmed]);
useEffect(() => {
if (!open) return;
@@ -115,7 +126,9 @@ export function SearchDialog({
</select>
<Button size="small" onClick={onClose}>{t("Close")}</Button>
</div>
<div style={{ maxHeight: 420, overflow: "auto", padding: 8 }}>
<div style={{ maxHeight: 420, overflow: "auto", padding: 8 }} aria-busy={searching}>
{searching && <div role="status" style={{ padding: 8, fontSize: 13, color: "var(--text-3)" }}>{t("Searching…")}</div>}
{current?.error && <div className="form-error" role="alert">{t("Search failed: {reason}", { reason: current.error })}</div>}
{visibleResults.map((result) => (
<button
key={`${result.entityType}:${result.entityId}`}
+11
View File
@@ -37,6 +37,17 @@ test("a search hit opens the object itself, not just its page", async () => {
await page.getByRole("dialog", { name: "Search" }).getByRole("button", { name: title }).first().click();
};
// While asking, search says so; quick typing shows only the latest words' answer.
await page.locator(".search-kbd").click();
const searchDialog = page.getByRole("dialog", { name: "Search" });
await searchDialog.getByRole("textbox").fill("zebra");
await expect(searchDialog.getByRole("status")).toHaveText("Searching…");
await searchDialog.getByRole("textbox").fill("Walrus");
await expect(searchDialog.getByRole("button", { name: /Walrus pricing memo/ })).toBeVisible();
await expect(searchDialog.getByRole("button", { name: /zebra crossing note/ })).toHaveCount(0);
await expect(searchDialog.getByRole("status")).toHaveCount(0);
await page.keyboard.press("Escape");
// A message opens its conversation with its thread shown and the message marked.
await search("zebra", /zebra crossing note/);
await expect(page.getByRole("complementary", { name: "Thread" })).toBeVisible();