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
Original file line number Diff line number Diff line change
Expand Up @@ -544,10 +544,14 @@ export function GoalResearchHistory({goalId, originalGoalId = goalId, preferHist
</div>
<div className={`research-history-controls${readingSelection ? " is-reading" : ""}`}>
{!emptySourceHistory ? <button type="button" className="research-history-search" aria-label={zh ? "搜索研究记录" : "Search research records"} disabled={!value?.events.length} onClick={() => {
const disclosure = records.current;
const focused = document.activeElement;
setRecordsOpen(true);
window.requestAnimationFrame(() => {
records.current?.querySelector<HTMLInputElement>("input")?.focus({preventScroll: true});
records.current?.scrollIntoView({block: "start"});
// A user can move on to a record before the input's first frame.
if (!disclosure?.isConnected || document.activeElement !== focused) return;
disclosure.querySelector<HTMLInputElement>("input")?.focus({preventScroll: true});
disclosure.scrollIntoView({block: "start"});
});
}}><Search size={15} aria-hidden/></button> : null}
{!readingSelection && !emptySourceHistory ? <div className="research-lens" role="group" aria-label={zh ? "视图" : "View"}>{(["timeline", "graph"] as const).map(item => <button key={item} type="button" aria-pressed={lens === item} onClick={() => chooseLens(item)}>{item === "timeline" ? (zh ? "时间线" : "Timeline") : (zh ? "关系图" : "Graph")}</button>)}</div> : null}
Expand Down
26 changes: 24 additions & 2 deletions examples/personal-workspace-browser/research-record-search.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -305,15 +305,37 @@ Path(sys.argv[2]).write_text(json.dumps({'goal_id':other,'display_name':'Other s
assert.equal(await phases.count(), 3);
assert.equal(await phases.nth(2).getAttribute("aria-pressed"), "true", "Recovery applies the original period bookmark");
await page.goto(url.replace("synthetic-record-search", "synthetic-other-research"));
await history.getByRole("button", {name: "Search research records", exact: true}).click();
const openOtherSearch = history.getByRole("button", {name: "Search research records", exact: true});
await openOtherSearch.evaluate(button => {
// Hold the frame from this one search activation until the reader moves focus.
button.addEventListener("click", () => {
const schedule = window.requestAnimationFrame;
const frames = [];
window.__loopxHeldSearchFrames = frames;
window.requestAnimationFrame = callback => {
frames.push(callback);
return -frames.length;
};
setTimeout(() => {window.requestAnimationFrame = schedule;}, 0);
}, {once: true});
});
await openOtherSearch.click();
await results.getByRole("status", {name: "4 matches in 4 loaded records · 3 entries", exact: true}).waitFor();
assert.equal(await search.inputValue(), "", "Another Goal starts with its own search scope");
assert.doesNotMatch(await results.innerText(), /amber|Earlier negative/);
const grouped = results.locator(".research-record-entry").first();
const groupSummary = grouped.locator(":scope > summary");
assert.match(await groupSummary.innerText(), /Refuted.*Resolved.*2 records/);
assert.equal(await results.locator(".research-record-entry").count(), 3, "Identical titles and times leave independent attempts and absent links separate");
await groupSummary.press("Enter");
await groupSummary.focus();
await page.evaluate(() => {
const frames = window.__loopxHeldSearchFrames ?? [];
delete window.__loopxHeldSearchFrames;
frames.forEach(callback => callback(performance.now()));
});
assert(await groupSummary.evaluate(element => element === document.activeElement),
"Delayed search autofocus leaves the already focused record in control");
await page.keyboard.press("Enter");
const groupHistory = grouped.locator(".research-record-group-history");
await groupHistory.waitFor();
assert.match(await grouped.innerText(), /The alternative missed the target\./);
Expand Down
Loading