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 @@ -33,6 +33,15 @@ try {
api.transformStatus = fixture => {
transform(fixture);
fixture.run_history.goals.find(goal => goal.id === showcaseGoalId).display_name = longTitle;
if (state.quiet) {
const queue = fixture.attention_queue.items.find(item => item.goal_id === showcaseGoalId);
Object.assign(queue.agent_todos, { advancement_done_count: 0, done_count: 0, deferred_count: 0, open_count: 0, total_count: 0, items: [], deferred_items: [] });
Object.assign(queue.project_asset.agent_todos, { advancement_done_count: 0, done: 0, deferred_count: 0, open: 0, total: 0, items: [], recent_completed_advancement_items: [] });
Object.assign(queue.user_todos, { open_count: 0, total_count: 0, items: [] });
for (const agent of fixture.agent_management_projection.agents) {
if (agent.current_todo?.goal_id === showcaseGoalId) agent.current_todo = null;
}
}
};
await page.route('**/api/chat/delivery-review?*', route => route.fulfill({ json: {
ok: true, goal_id: showcaseGoalId, observed_at: '2026-09-01T10:00:00Z', acceptance: null, graph: null,
Expand All @@ -44,6 +53,46 @@ try {
const { page } = session;
try {
await openShowcaseGoal(page);
const inlineRail = page.getByTestId('goal-workspace-inline-rail');
const inventory = inlineRail.locator('.goal-workspace-inventory');
const decision = page.locator('.goal-overview [data-attention-id="showcase-gate-window"]:visible');
assert.equal(await decision.count(), 1, 'The owner decision is shown once in the composed Overview');
assert.equal(await inlineRail.locator('.goal-all-work').count(), 0, 'The full inventory starts collapsed instead of repeating current work');
assert.ok(await decision.locator('.goal-attention-text').evaluate(element => parseFloat(getComputedStyle(element).fontSize) >= 16), 'Owner decisions use readable lead-size text');
assert.ok(await decision.locator('svg').first().evaluate(element => {
const swatch = document.createElement('span');
swatch.style.color = 'var(--pw-caution)';
element.parentElement.append(swatch);
const matches = getComputedStyle(element).color === getComputedStyle(swatch).color;
swatch.remove();
return matches;
}), 'The owner-decision hand retains the theme gold');
await inventory.locator('summary').focus();
await page.keyboard.press('Enter');
await inlineRail.locator('.goal-work-filters').waitFor({ state: 'visible' });
assert.equal(await page.locator('.goal-overview [data-attention-id="showcase-gate-window"]:visible').count(), 1, 'Expanding All work does not duplicate the owner decision');
assert.equal(await inlineRail.getByRole('heading', { name: '等待决定', exact: true }).count(), 0, 'Empty work sections do not leave duplicate-looking headings');
assert.ok(await inlineRail.locator('.goal-work-row strong').first().evaluate(element => parseFloat(getComputedStyle(element).fontSize) >= 14), 'Inventory tasks use body-size text rather than captions');
await inlineRail.getByRole('button', { name: '活动', exact: true }).click();
await inlineRail.locator('.goal-material-activity').waitFor({ state: 'visible' });
await inlineRail.getByRole('button', { name: '全部工作', exact: true }).click();
let historyReads = 0;
page.on('request', request => { if (new URL(request.url()).pathname === '/api/chat/completed-todos') historyReads++; });
const historyResponse = page.waitForResponse(response => new URL(response.url()).pathname === '/api/chat/completed-todos');
await inlineRail.locator('[data-testid="completed-task-lane"] > header button').click();
await historyResponse;
const overview = page.locator('.goal-overview');
await overview.evaluate(element => element.style.maxWidth = '800px');
await page.waitForFunction(() => !document.querySelector('.goal-workspace-rail .goal-all-work'));
assert.equal(await inventory.isVisible(), false, 'Container reflow hides the inline inventory');
const readsBeforeHiddenRefresh = historyReads;
state.done++;
await page.getByRole('button', { name: '刷新状态', exact: true }).click();
await page.waitForFunction(() => document.querySelectorAll('[data-testid="goal-vitals"] .goal-vital')[1]?.querySelector('.goal-vital-value')?.textContent === '24/29');
assert.equal(historyReads, readsBeforeHiddenRefresh, 'Refreshing a hidden inventory does not read completed history');
await overview.evaluate(element => element.style.removeProperty('max-width'));
await inlineRail.locator('.goal-work-filters').waitFor({ state: 'visible' });
await inventory.locator('summary').click();
await page.getByRole("region", {name:"当前工作",exact:true}).getByRole("button",{name:"关系图",exact:true}).click();
const heading = page.locator('.personal-channel-title h1');
assert.equal(await heading.innerText(), longTitle, 'The original Goal title remains complete');
Expand Down Expand Up @@ -76,10 +125,25 @@ try {
assert.ok(await target.evaluate(element => element === document.activeElement), 'Closing details returns focus to the task');
await page.keyboard.press('Enter'); await page.keyboard.press('Escape');
assert.equal(await map.locator('.work-map-node[aria-pressed="true"]').count(), 0, 'Escape closes the map selection');
await inventory.locator('summary').click();
await inlineRail.locator('.goal-all-work').waitFor({ state: 'visible' });
await page.setViewportSize({ width: 1024, height: 768 });
await page.waitForFunction(() => !document.querySelector('.goal-workspace-rail .goal-all-work'));
assert.equal(await inventory.isVisible(), false, 'Narrow view falls back to the All work drawer instead of crowding the brief');
assert.equal(await page.locator('.goal-overview [data-attention-id="showcase-gate-window"]:visible').count(), 1, 'The owner decision remains visible once on the narrow page');
const openAllWork = page.getByRole('button', { name: '打开全部工作' });
await openAllWork.click();
const railDialog = page.locator('.goal-work-rail-dialog');
assert.ok(await railDialog.locator('.goal-all-work.is-dialog').isVisible(), 'The narrow drawer keeps the same All work inventory reachable');
await railDialog.locator('[data-attention-id="showcase-gate-window"]').waitFor({ state: 'visible' });
await page.keyboard.press('Escape');
assert.equal(await railDialog.isVisible(), false, 'Escape closes the inventory drawer');
assert.ok(await openAllWork.evaluate(element => element === document.activeElement), 'Closing the inventory restores focus to the original control');
const measured = await heading.evaluate(element => ({ height: element.clientHeight, line: parseFloat(getComputedStyle(element).lineHeight), overflow: element.scrollWidth - element.clientWidth }));
assert.ok(measured.height > measured.line && measured.overflow <= 1, 'A long title wraps without clipping or page overflow');
await page.setViewportSize({ width: 390, height: 844 });
assert.equal(await page.locator('.goal-overview [data-attention-id="showcase-gate-window"]:visible').count(), 1, 'Phone reflow retains one owner decision');
assert.ok(await page.locator('.goal-overview-attention .goal-attention-text').first().evaluate(element => parseFloat(getComputedStyle(element).fontSize) >= 16), 'Phone reflow does not shrink the requested reading text');
const headerHeight = await page.locator('.personal-channel-header').evaluate(element => element.getBoundingClientRect().height);
assert.ok(headerHeight <= 220, 'A long Goal title cannot consume the phone viewport');
assert.equal(await heading.evaluate(element => getComputedStyle(element).overflow), 'hidden', 'Phone title clipping must survive the packaged CSS cascade, not just constrain the box height');
Expand All @@ -99,7 +163,16 @@ try {
await map.getByRole('button', { name: '关闭事项详情' }).click();
assert.ok(await mobile.evaluate(element => element === document.activeElement));
assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 1), 'The phone has no page-wide horizontal overflow');
state.quiet = true;
await page.setViewportSize({ width: 1440, height: 1000 });
await page.getByRole('button', { name: '刷新状态', exact: true }).click();
await page.waitForFunction(() => document.querySelectorAll('[data-testid="goal-vitals"] .goal-vital')[1]?.querySelector('.goal-vital-value')?.textContent === '0/0');
await inlineRail.waitFor({ state: 'detached' });
assert.ok(await page.locator('.goal-workspace-composition').evaluate(element => {
const frame = element.getBoundingClientRect(), document = element.querySelector('.goal-workspace-document').getBoundingClientRect();
return Math.abs(frame.width - document.width) <= 1;
}), 'A known-quiet Goal gives its reading column the full width instead of reserving a blank rail');
assert.deepEqual(session.errors, [], 'Packaged composition emits no browser errors');
console.log('Goal workspace craft: packaged long-title, readable geometry, stationary desktop selection, close/Escape/focus, 390px and reduced-motion checks passed');
console.log('Goal workspace craft: single gold attention rail, readable inventory, hidden-history isolation, quiet layout, long-title, stationary selection, close/Escape/focus, 390px and reduced-motion checks passed');
} finally { await session.close(); }
} finally { await browser?.close(); server.kill('SIGTERM'); }
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,7 @@ try {
await dialogFor(page).getByRole('combobox').fill('map legacy');
await page.keyboard.press('Enter');
await dialogFor(page).waitFor({state: 'hidden'});
await page.waitForURL(/todo=showcase-map-columns/);
await page.locator('[data-context-drawer]').waitFor({state: 'visible'});
assert.match(page.url(), /todo=showcase-map-columns/);
// The palette on top of an inspector must not close that inspector on Escape.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,13 @@
.goal-overview button:focus-visible { outline: 2px solid var(--pw-blue); outline-offset: 3px; }
.goal-overview button svg { flex: none; }

.goal-overview-summary { display: grid; gap: 32px; align-items: start; }
.goal-overview-summary.has-attention { grid-template-columns: minmax(0, 1fr) minmax(260px, 320px); }
.goal-overview-summary > section { display: grid; align-content: start; gap: 12px; min-width: 0; }
.goal-overview-summary strong, .goal-overview-summary p, .goal-overview-summary button span { overflow-wrap: anywhere; }
.goal-overview-summary ul { list-style: none; margin: 0; padding: 0; }
.goal-overview-direction, .goal-overview-attention { display: grid; align-content: start; gap: 12px; min-width: 0; }
.goal-overview-direction strong, .goal-overview-direction p, .goal-overview-attention button span { overflow-wrap: anywhere; }
.goal-overview-attention ul { list-style: none; margin: 0; padding: 0; }

.goal-overview-heading, .goal-overview-attention header { display: flex; align-items: center; gap: 8px; min-height: 32px; }
.goal-overview-heading h2, .goal-overview-attention h2 { color: var(--pw-muted); font-size: var(--text-caption); font-weight: 500; line-height: var(--leading-caption); }
.goal-overview-heading h2 { color: var(--pw-muted); font-size: var(--text-caption); font-weight: 500; line-height: var(--leading-caption); }
.goal-overview-attention h2 { color: var(--pw-text); font-size: var(--text-body); font-weight: 500; line-height: var(--leading-body); }
.goal-overview-heading > span { color: var(--pw-faint); font-size: var(--text-caption); line-height: var(--leading-caption); }
.goal-overview-heading > span::before { content: "·"; padding-right: 8px; }
.goal-overview .goal-overview-heading button { min-height: 32px; padding: 4px 8px; border-color: transparent; background: transparent; color: var(--pw-muted); font-size: var(--text-caption); line-height: var(--leading-caption); }
Expand Down Expand Up @@ -62,7 +61,7 @@
.goal-overview-criteria .is-accepted > span { color: var(--pw-muted); }
.goal-overview-criteria .is-failed svg, .goal-overview-criteria .is-failed small { color: var(--pw-red); }

.goal-overview-summary .goal-overview-run { display: inline-flex; justify-self: start; gap: 8px; margin: 0; padding: 0; min-height: 28px; border: 0; background: transparent; }
.goal-overview-direction .goal-overview-run { display: inline-flex; justify-self: start; gap: 8px; margin: 0; padding: 0; min-height: 28px; border: 0; background: transparent; }
.goal-overview-run small { color: var(--pw-muted); font-size: var(--text-caption); line-height: var(--leading-caption); }
.goal-overview-run svg { grid-column: 2; grid-row: 1; color: var(--pw-faint); transition: transform var(--motion-fast) var(--ease-out), color var(--motion-fast) var(--ease-out); }
.goal-overview-run:hover svg { color: var(--pw-text); transform: translateX(2px); }
Expand All @@ -75,11 +74,11 @@
.goal-overview-attention header small { color: var(--pw-faint); }
.goal-overview-attention li { transition: opacity var(--motion-base) var(--ease-out), transform var(--motion-base) var(--ease-out); }
.goal-overview-attention li + li { border-top: 1px solid var(--pw-line); }
.goal-overview .goal-overview-attention li button { display: grid; grid-template-columns: 16px minmax(0, 1fr) 14px; align-items: start; gap: 10px; width: 100%; min-height: 48px; padding: 10px 8px; border: 0; border-radius: 6px; background: transparent; }
.goal-overview .goal-overview-attention li button { display: grid; grid-template-columns: 17px minmax(0, 1fr) 14px; align-items: start; gap: 10px; width: 100%; min-height: 48px; padding: 12px 0; border: 0; border-radius: 6px; background: transparent; }
.goal-overview .goal-overview-attention li button:hover { background: var(--pw-hover); }
.goal-overview-attention li svg { margin-top: 2px; }
.goal-overview-attention li svg:first-child { color: var(--pw-caution); }
.goal-attention-text { display: grid; gap: 2px; min-width: 0; font-size: var(--text-body); line-height: var(--leading-body); }
.goal-attention-text { display: grid; gap: 4px; min-width: 0; font-size: var(--text-lead); line-height: var(--leading-lead); }
.goal-attention-text time { color: var(--pw-faint); font-size: var(--text-caption); font-variant-numeric: tabular-nums; line-height: var(--leading-caption); }
.goal-overview-attention li svg:last-child { color: var(--pw-faint); transition: transform var(--motion-fast) var(--ease-out), color var(--motion-fast) var(--ease-out); }
.goal-overview-attention li button:hover svg:last-child { color: var(--pw-text); transform: translateX(2px); }
Expand Down Expand Up @@ -115,9 +114,11 @@
.goal-overview-criteria li, .goal-overview-attention li, .goal-overview-run svg, .goal-overview-attention li svg:last-child { transition: none; }
}

@media (max-width: 900px) {
.goal-overview-summary.has-attention { grid-template-columns: 1fr; }
.goal-overview-attention { padding: 16px 0 0; border-top: 1px solid var(--pw-line); border-left: 0; }
@media (max-width: 1200px) {
.goal-workspace-rail .goal-overview-attention { padding: 16px 0 0; border-top: 1px solid var(--pw-line); border-left: 0; }
}
@container (max-width: 920px) {
.goal-workspace-rail .goal-overview-attention { padding: 16px 0 0; border-top: 1px solid var(--pw-line); border-left: 0; }
}

@media (max-width: 640px) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,8 +79,9 @@ export function GoalOverview({ active, goal, items, userTodos, readOnly, onOpenD
{onMarkSeen ? <button onClick={onMarkSeen} type="button">{t("since.markSeen")}</button> : null}
</p>
) : null}
<div className={`goal-overview-summary${showAttention ? " has-attention" : ""}`}>
<section className="goal-overview-direction" aria-label={copy.direction}>
<GoalWorkspaceWork onOpenAgentConversation={onOpenAgentConversation} goal={goal} digest={digest} items={items} userTodos={userTodos} selectedTodoId={selectedTodoId} onSelect={onSelect}
readOnly={readOnly} active={active} railOpen={railOpen} onCloseRail={onCloseRail} historyRefreshRevision={historyRefreshRevision} proposals={proposals} onPreview={onPreview} board={board}
brief={<section className="goal-overview-direction" aria-label={copy.direction}>
{digest.objective ? <p className="goal-overview-objective" data-testid="goal-declared-objective">{digest.objective}</p> : <ContextHelp icon={CircleHelp} label={locale === "zh-CN" ? "目标声明未读到" : "Declared objective unavailable"}><p>{copy.objectiveUnavailable}</p></ContextHelp>}
{currentWork && currentRow ? <details className="goal-overview-current">
<summary><small>{copy.current} · {goalWorkClassLabel(currentRow.displayClass, locale === "zh-CN")}</small><span>{currentWork.text}</span><ChevronDown aria-hidden size={15}/></summary>
Expand All @@ -92,24 +93,21 @@ export function GoalOverview({ active, goal, items, userTodos, readOnly, onOpenD
</div>
<WorkerReadback goal={goal} zh={locale === "zh-CN"} onViewTasks={() => onView("tasks")}/>
<GoalAcceptanceContractSection overview goalId={goal.goalId} contract={observation?.goal_acceptance_contract} copy={acceptanceCopy} current={review.usable} localSource={!readOnly} canManage={!readOnly && active && review.usable}/>
</section>
{showAttention ? <section className="goal-overview-attention" aria-label={copy.attention} id={goalOverviewAttentionId} tabIndex={-1}>
</section>}
attention={showAttention ? <section className="goal-overview-attention" aria-label={copy.attention} id={goalOverviewAttentionId} tabIndex={-1}>
<header>
<h2>{copy.attention}</h2>
<span className={blocking ? "goal-overview-count is-blocking" : "goal-overview-count"}>{blocking ?? "—"}</span>
{optional ? <small>{copy.optional.replace("{count}", String(optional))}</small> : null}
</header>
<ul>{attention.slice(0, 3).map(item => <li key={item.todoId}><button type="button" onClick={() => onSelect({ kind: "attention", item })}>
<Hand aria-hidden size={15} />
<ul>{attention.slice(0, 3).map(item => <li key={item.todoId}><button type="button" data-attention-id={item.todoId} onClick={() => onSelect({ kind: "attention", item })}>
<Hand aria-hidden size={17} />
<span className="goal-attention-text"><span>{item.text}</span>
{item.updatedAt ? <time dateTime={item.updatedAt}>{relativeTime(item.updatedAt, locale)}</time> : null}</span>
<ArrowRight aria-hidden size={14} />
</button></li>)}</ul>
{moreAttention ? <button className="goal-overview-more" type="button" onClick={() => onView("tasks")}>{copy.tasks}{totalAttention !== null ? ` (${totalAttention})` : ""}<ArrowRight aria-hidden size={14} /></button> : null}
</section> : null}
</div>
<GoalWorkspaceWork onOpenAgentConversation={onOpenAgentConversation} goal={goal} digest={digest} items={items} userTodos={userTodos} selectedTodoId={selectedTodoId} onSelect={onSelect}
readOnly={readOnly} active={active} railOpen={railOpen} onCloseRail={onCloseRail} historyRefreshRevision={historyRefreshRevision} proposals={proposals} onPreview={onPreview} board={board}
map={readOnly ? <ContextHelp label={locale === "zh-CN" ? "来源读取范围" : "Source reading scope"} icon={CircleHelp}><p className="goal-work-scope">{copy.remote}</p></ContextHelp> : <DeliveryWorkMap goal={goal} userTodos={userTodos} review={review} onSelect={onSelect} selectedTodoId={selectedTodoId}/>}/>
{readOnly ? <GoalAcceptanceObservationCard goal={goal} overview />
: <OverviewDeliveryEvidence key={`delivery:${goal.goalId}`} review={review} zh={locale === "zh-CN"}>
Expand Down
Loading
Loading