diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.css b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.css index 6906f80b95..b047b31058 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.css +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.css @@ -276,8 +276,11 @@ .research-reading-order { display: flex; align-items: center; gap: 12px; border-block-end: 1px solid var(--pw-line); padding-block: 0 8px; margin-block-end: 8px; } .research-reading-order button { min-height: 44px; padding: 6px 0; background: none; border: 0; color: var(--pw-text); font: inherit; font-size: var(--text-body); cursor: pointer; } .research-reading-order > span { color: var(--pw-muted); } +.research-reading-overview { display: grid; grid-template-columns: repeat(auto-fit, minmax(min(100%, 420px), 1fr)); align-items: start; gap: 24px 40px; margin-block: 16px 32px; } +.research-reading-overview.is-expanded { grid-template-columns: minmax(0, 1fr); } .research-reading-layer { min-width: 0; scroll-margin-block-start: 16px; } -.research-reading-layer .research-synthesis:not(.research-synthesis-detail) { margin-block: 24px 32px; } +.research-reading-overview .research-synthesis:not(.research-synthesis-detail), .research-reading-overview .research-progress { margin-block: 0; } +.research-reading-overview .research-progress { padding-block-start: 0; } .research-reading-layer:focus, .research-history-heading:focus { outline: none; } @media (max-width: 640px) { diff --git a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx index f127f12cdc..ad9f31545b 100644 --- a/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx +++ b/apps/presentation/dashboard/src/features/personal-workspace/goal-research-history.tsx @@ -413,14 +413,16 @@ export function GoalResearchHistory({goalId, active, zh, refreshRevision = 0, re return
{progressiveOverview ? <> -
-
- +
+
{analysis.location.open ? : null}
+
+
+ {!analysis.location.open ? : null} : null}

{copy.title}

{copy.scope} · {copy.time}{zh ? "。" : ". "}{copy.coverage}{zh ? "。" : "."}

{copy.interpretation}{zh ? "。" : "."}

diff --git a/docs/development/assets/runtime-source-analysis/reading-flow/after-desktop.png b/docs/development/assets/runtime-source-analysis/reading-flow/after-desktop.png new file mode 100644 index 0000000000..f9f2d72cae Binary files /dev/null and b/docs/development/assets/runtime-source-analysis/reading-flow/after-desktop.png differ diff --git a/docs/development/assets/runtime-source-analysis/reading-flow/after-phone.png b/docs/development/assets/runtime-source-analysis/reading-flow/after-phone.png new file mode 100644 index 0000000000..15a55f6c52 Binary files /dev/null and b/docs/development/assets/runtime-source-analysis/reading-flow/after-phone.png differ diff --git a/docs/development/assets/runtime-source-analysis/reading-flow/before-desktop.png b/docs/development/assets/runtime-source-analysis/reading-flow/before-desktop.png new file mode 100644 index 0000000000..f2631df93c Binary files /dev/null and b/docs/development/assets/runtime-source-analysis/reading-flow/before-desktop.png differ diff --git a/docs/development/assets/runtime-source-analysis/reading-flow/before-phone.png b/docs/development/assets/runtime-source-analysis/reading-flow/before-phone.png new file mode 100644 index 0000000000..ef7a3a3d91 Binary files /dev/null and b/docs/development/assets/runtime-source-analysis/reading-flow/before-phone.png differ diff --git a/docs/development/assets/runtime-source-analysis/reading-flow/english-phone.png b/docs/development/assets/runtime-source-analysis/reading-flow/english-phone.png new file mode 100644 index 0000000000..723d64ca8d Binary files /dev/null and b/docs/development/assets/runtime-source-analysis/reading-flow/english-phone.png differ diff --git a/docs/development/assets/runtime-source-analysis/reading-flow/expanded-desktop.png b/docs/development/assets/runtime-source-analysis/reading-flow/expanded-desktop.png new file mode 100644 index 0000000000..7cfab689fb Binary files /dev/null and b/docs/development/assets/runtime-source-analysis/reading-flow/expanded-desktop.png differ diff --git a/docs/development/assets/runtime-source-analysis/reading-flow/expanded-phone.png b/docs/development/assets/runtime-source-analysis/reading-flow/expanded-phone.png new file mode 100644 index 0000000000..21a12ebb30 Binary files /dev/null and b/docs/development/assets/runtime-source-analysis/reading-flow/expanded-phone.png differ diff --git a/docs/development/assets/runtime-source-analysis/reading-flow/long-default-desktop.png b/docs/development/assets/runtime-source-analysis/reading-flow/long-default-desktop.png new file mode 100644 index 0000000000..11aa41e948 Binary files /dev/null and b/docs/development/assets/runtime-source-analysis/reading-flow/long-default-desktop.png differ diff --git a/examples/personal-workspace-browser/research-history.mjs b/examples/personal-workspace-browser/research-history.mjs index 8f4ea680ec..71816d3d1a 100644 --- a/examples/personal-workspace-browser/research-history.mjs +++ b/examples/personal-workspace-browser/research-history.mjs @@ -425,15 +425,21 @@ export const researchHistoryScenario = { assert.match(await processJudgments.locator('[data-metric="goal_alignment"]').getAttribute('aria-label'), /不适用/); await processJudgments.locator(':scope > summary').click(); assert.equal(await synthesisPage.locator('.research-effect-metrics:visible').count(), 0); + await synthesisPage.evaluate(() => {for (const e of document.querySelectorAll('*')) if (e.scrollTop) e.scrollTop = 0;}); + assert(await synthesisPage.evaluate(() => { + const summary = document.querySelector('.research-synthesis-summary').getBoundingClientRect(); + const viewport = document.querySelector('.personal-channel-scroll').getBoundingClientRect(); + return summary.top >= viewport.top && summary.bottom <= viewport.bottom; + }), 'The first viewport answers what the study found, including its unknowns'); await synthesisPage.screenshot({path: resolve(outputDir, 'research-synthesis-ordinary-desktop.png'), animations: 'disabled'}); assert(await synthesisPage.evaluate(() => { const results = document.querySelector('.research-progress'); const report = document.querySelector('.research-synthesis'); const history = document.querySelector('.research-history-heading'); return Boolean(results && report && history - && (results.compareDocumentPosition(report) & Node.DOCUMENT_POSITION_FOLLOWING) - && (report.compareDocumentPosition(history) & Node.DOCUMENT_POSITION_FOLLOWING)); - }), 'The complete overview reads results, report, then research history'); + && (report.compareDocumentPosition(results) & Node.DOCUMENT_POSITION_FOLLOWING) + && (results.compareDocumentPosition(history) & Node.DOCUMENT_POSITION_FOLLOWING)); + }), 'The report answers the research question before readers inspect measurements and history'); const reading = synthesisPage.getByRole('navigation', {name: '研究阅读', exact: true}); await reading.getByRole('button', {name: '汇报', exact: true}).click(); assert(await synthesisPage.evaluate(() => document.activeElement?.contains(document.querySelector('.research-synthesis')))); @@ -466,6 +472,11 @@ export const researchHistoryScenario = { await synthesis.locator('.research-synthesis-finding > summary').first().click(); await synthesisPage.evaluate(() => {for (const e of document.querySelectorAll('*')) if (e.scrollTop) e.scrollTop = 0;}); assert(await synthesisPage.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1)); + assert(await synthesisPage.evaluate(() => { + const summary = document.querySelector('.research-synthesis-summary').getBoundingClientRect(); + const viewport = document.querySelector('.personal-channel-scroll').getBoundingClientRect(); + return summary.top >= viewport.top && summary.bottom <= viewport.bottom; + }), 'A narrow first viewport retains the substantive report instead of only metric labels'); await synthesisPage.screenshot({path: resolve(outputDir, 'research-synthesis-ordinary-mobile.png'), animations: 'disabled'}); await reading.getByRole('button', {name: '汇报', exact: true}).click(); await synthesisPage.mouse.move(1, 1);