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 @@ -260,6 +260,7 @@
.research-history-detail .research-finding-exceptions { font-size: var(--text-body); color: var(--pw-muted); margin: 0; }
.research-recorded-finding > h3 { max-width: none; font-size: var(--text-heading); line-height: var(--leading-heading); margin: 8px 0; }
.research-reader-states { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 12px; }
.research-reader-return { display: flex; align-items: center; gap: 4px; }
.research-reader-question { margin-block: 4px 12px; font-size: var(--text-body); }
.research-reader-question > summary { display: flex; align-items: center; gap: 8px; min-height: 44px; cursor: pointer; color: var(--pw-muted); }
.research-reader-question > summary::before { content: "▸"; flex-shrink: 0; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -247,6 +247,7 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re
const selected = value?.nodes.find(node => node.id === selectedId);
const selectedFinding = selected?.findings.find(finding => finding.result_id === selection.finding)
?? (!selection.finding ? selected?.findings.at(-1) : undefined);
const hasSeparateQuestion = Boolean(selected?.findings.length && selectedFinding?.title !== selected.latest.title);
const selectedGroup = selection.day !== null ? grouped.groups.find(group => group.day === selection.day) : undefined;
const selectedMembers = selectedGroup?.nodes ?? (selection.nodes.length > 1 ? value?.nodes.filter(node => selection.nodes.includes(node.id)) : undefined);
const requestedCollection = selection.collection;
Expand Down Expand Up @@ -501,8 +502,9 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re

{selected ? <div className="research-history-detail" ref={detail} role="region" aria-label={selected.latest.title} tabIndex={-1} onKeyDown={event => {if (event.key === "Escape") {event.stopPropagation(); closeReader();}}}>
<div className="research-reader-content">
<div className="research-reader-location"><button type="button" className="research-reader-back" onClick={closeReader}><ArrowLeft size={14} aria-hidden/>{collection ? (zh ? "返回问题列表" : "Back to questions") : copy.all}</button>
<div className="research-reader-states">
<div className="research-reader-location"><div className="research-reader-return"><button type="button" className="research-reader-back" onClick={closeReader}><ArrowLeft size={14} aria-hidden/>{collection ? (zh ? "返回问题列表" : "Back to questions") : copy.all}</button>
{!hasQuestionNavigation && hasSeparateQuestion && !questionExpanded ? <ContextHelp icon={CircleHelp} label={zh ? "完整研究问题" : "Full research question"}><div className="research-question-text"><p>{selected.latest.title}</p>{selected.latest.summary ? <p>{selected.latest.summary}</p> : null}</div></ContextHelp> : null}
</div><div className="research-reader-states">
<div className="research-detail-kind"><GitBranch size={14} aria-hidden/>{kindLabel(selected.latest.node_kind)} · {stateLabel(selected.latest.status)} <ContextHelp label={findingCopy.help}><p>{findingCopy.meaning}</p></ContextHelp></div>
{selected.findings.length > 1 && findingExceptions ? <p className="research-finding-exceptions">{findingExceptions}</p> : null}
</div></div>
Expand All @@ -520,7 +522,11 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re
})}
</div>
</nav> : null}
{!hasQuestionNavigation ? selected.findings.length ? selectedFinding?.title !== selected.latest.title ? <details className="research-reader-question" key={`question:${selected.id}`} open={questionExpanded} onToggle={event => setQuestionExpanded(event.currentTarget.open)}>
{!hasQuestionNavigation ? selected.findings.length ? hasSeparateQuestion && questionExpanded ? <details className="research-reader-question" key={`question:${selected.id}`} open onToggle={event => {
if (event.currentTarget.open) return;
if (event.currentTarget.contains(document.activeElement)) detail.current?.focus({preventScroll: true});
setQuestionExpanded(false);
}}>
<summary><span>{selected.latest.title}</span></summary>
{selected.latest.summary ? <p>{selected.latest.summary}</p> : null}
</details> : null : <h3>{selected.latest.title}</h3> : null}{displayControl}
Expand Down
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
6 changes: 6 additions & 0 deletions docs/development/design.md
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,12 @@ the chart. Show one finding at a time with its declared status and full text;
keep all findings selectable and failed or tentative counts visible. Preserve
the exact finding on reload and Back. Offer the whole recorded history in place
and through the existing return action, without letting it bury the chosen text.
When opening a specific finding, put its parent question in accessible contextual
help rather than another collapsed title row. Opening a route's question still
reveals that complete question directly; these two reading intents are distinct.

打开具体结论时,所属研究问题放进可悬浮、聚焦和点按的上下文帮助,避免再堆一行
折叠标题;从路线打开问题时仍直接显示完整问题,不增加必经操作。

Decision history groups records by their recorded question identity, with the
question title once above its successive decisions. Matching titles alone do
Expand Down
27 changes: 22 additions & 5 deletions examples/personal-workspace-browser/research-route-reading.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,17 @@ export const researchRouteReadingScenario = {
const question = reader.locator(".research-reader-content > .research-reader-question > summary > span");
assert.equal(await question.innerText(), fullTitle, "One route activation reveals the complete original question");
assert(await question.evaluate(element => element.scrollHeight <= element.clientHeight + 1 && element.scrollWidth <= element.clientWidth + 1), "Requested question text is not clipped");
if (!endpoint) {
const summary = reader.locator(".research-reader-question > summary");
await summary.focus();
await page.keyboard.press("Enter");
await page.waitForFunction(() => document.activeElement?.classList.contains("research-history-detail"));
assert.equal(await reader.locator(".research-reader-question").count(), 0, "Closing the question preserves reader focus without a redundant title row");
const context = reader.getByRole("button", {name: language === "zh-CN" ? "完整研究问题" : "Full research question", exact: true});
await context.tap();
assert.equal(await reader.locator(".research-question-text p").first().innerText(), fullTitle, "Touch can recover the complete question after collapse");
await page.keyboard.press("Escape");
}
if (endpoint) {
await reader.locator("select").selectOption("finding-route-0-end");
const body = reader.getByText("完整观测:局部质量下降,延迟改善;完整成本和独立验证仍未知。", {exact: true}).first();
Expand All @@ -145,10 +156,14 @@ export const researchRouteReadingScenario = {
assert(box.y >= reading.top - 1 && box.y + Math.min(box.height, lineHeight * 2) <= reading.bottom + 1, "The selected finding begins with readable content inside the reading viewport");
assert(await body.evaluate(element => parseFloat(getComputedStyle(element).fontSize) >= 16), "Density retains readable finding text");
assert.match(await reader.locator(".research-finding-exceptions").innerText(), /1/);
const context = reader.locator(".research-reader-content > .research-reader-question > summary");
await context.click();
assert(await context.evaluate(element => element.scrollHeight <= element.clientHeight + 1 && element.scrollWidth <= element.clientWidth + 1), "Full parent context remains one optional activation away");
await context.click();
assert.equal(await reader.locator(".research-reader-question").count(), 0, "Finding reading does not repeat the parent question in a collapsed title row");
const context = reader.getByRole("button", {name: language === "zh-CN" ? "完整研究问题" : "Full research question", exact: true});
await context.focus();
const fullQuestion = reader.locator(".research-question-text p").first();
assert.equal(await fullQuestion.innerText(), fullTitle, "Full parent context remains available through keyboard focus");
assert(await fullQuestion.evaluate(element => parseFloat(getComputedStyle(element).fontSize) >= 16 && element.scrollWidth <= element.clientWidth + 1), "Original question remains readable without horizontal scrolling");
await page.keyboard.press("Escape");
assert(await context.evaluate(element => element === document.activeElement), "Dismissing question help keeps focus at its trigger");
}
if (endpoint && language === "zh-CN" && width !== 768) await page.screenshot({path: resolve(outputDir, `research-route-reading-detail-${width}.png`), animations: "disabled"});
await page.keyboard.press("Escape");
Expand Down Expand Up @@ -181,7 +196,9 @@ export const researchRouteReadingScenario = {
await reader.getByRole("button", {name: "Whole graph", exact: true}).click();
await page.goBack();
await reader.waitFor();
assert.match(await reader.locator(".research-reader-question").innerText(), /末端研究/);
await reader.getByRole("button", {name: "Full research question", exact: true}).click();
assert.match(await reader.locator(".research-question-text").innerText(), /末端研究/);
await page.keyboard.press("Escape");
// A grouped reading context survives the document/URL boundary, rather
// than living only in an in-memory opener ref. Navigation is a read.
for (const locale of ["zh-CN", "en"]) for (const width of [1440, 390]) {
Expand Down
Loading