Give each Coach answer one action row and keep its chips in its column

Under an answer: copy, read aloud, try again and the time as text on the
left; tokens and model on the right, on one line down to 390 px. The
thumbs leave the web (the feedback route stays for the native client),
and so does the "What I'm looking at" disclosure: the method line and
the tables the answer read move into the steps list, and an older
message without steps lists the areas it drew on, never a raw key.

Remember becomes a bookmark icon in the question's row and confirms in
place with a toast. Follow-up chips render inside the latest answer's
column at its text size. The answer column takes the full width below
`sm` and a fixed 80 % above, so nothing moves sideways as a reply
streams. A round button fades in when the thread is scrolled away from
the end and brings it back.
This commit is contained in:
Marc-André Bombeck committed 2026-10-04 20:02:48 +02:00
1 parent 1cb50ed21d
commit a91e1bf0a7
26 files changed
+1469 -1143

No files matched your search

+9 -8
View File
@@ -1047,23 +1047,24 @@ test.describe("axe-core authenticated route and state matrix", () => {
);
});
await test.step(`${theme} /coach open evidence disclosure and source chips`, async () => {
await test.step(`${theme} /coach open the areas an older answer looked at`, async () => {
await page.goto(`/coach?c=${A11Y_CONVERSATION_ID}`, {
waitUntil: "domcontentloaded",
});
const evidence = page.locator('[data-slot="coach-evidence"]').first();
await expect(evidence).toBeVisible({ timeout: 15_000 });
await evidence.locator("summary").click();
await expect(evidence).toHaveAttribute("open", "");
const steps = page.locator('[data-slot="coach-turn-steps"]').first();
await expect(steps).toBeVisible({ timeout: 15_000 });
const toggle = steps.locator('[data-slot="coach-turn-steps-toggle"]');
await toggle.click();
await expect(toggle).toHaveAttribute("aria-expanded", "true");
await expect(
evidence.locator('[data-slot="coach-source-chips"]'),
steps.locator('[data-slot="coach-turn-areas"]'),
).toBeVisible();
blocking.push(
...(await scanPaintedState(
page,
theme,
"/coach open evidence disclosure and source chips",
evidence,
"/coach open the areas an older answer looked at",
steps,
)),
);
});
+4 -3
View File
@@ -550,10 +550,11 @@ test.describe("Coach dialog", () => {
await expect(figure).toBeVisible();
await expect(table).toBeHidden();
// The method line sits in the evidence disclosure.
await firstBubble.locator('[data-slot="coach-evidence-summary"]').click();
// The method line closes the open steps list.
await expect(
firstBubble.locator('[data-slot="coach-method-line"]'),
steps.locator(
'[data-slot="coach-turn-steps-panel"] [data-slot="coach-method-line"]',
),
).toContainText(TURN_ONE_METHOD.text);
// 3. A follow-up chip sends which chip it was.
+10 -4
View File
@@ -289,7 +289,7 @@ test.describe("Coach result tables and charts", () => {
});
for (const theme of ["light", "dark"] as const) {
test(`theme=${theme}: chart first, table on toggle, data used in the evidence`, async ({
test(`theme=${theme}: chart first, table on toggle, data used in the steps list`, async ({
page,
}) => {
await useTheme(page, theme);
@@ -342,12 +342,18 @@ test.describe("Coach result tables and charts", () => {
await table.locator('[data-slot="coach-result-view-chart"]').click();
await expect(figure).toBeVisible();
// The table the answer only used waits in the evidence disclosure.
// The table the answer only used waits in the open steps list.
await expect(
bubble.locator('[data-slot="coach-result-table"][data-ref="r2"]'),
).toBeHidden();
await bubble.locator('[data-slot="coach-evidence-summary"]').click();
const dataUsed = bubble.locator('[data-slot="coach-data-used"]');
const stepsToggle = bubble.locator(
'[data-slot="coach-turn-steps-toggle"]',
);
await expect(stepsToggle).toHaveAttribute("aria-expanded", "false");
await stepsToggle.click();
const dataUsed = bubble.locator(
'[data-slot="coach-turn-steps-panel"] [data-slot="coach-data-used"]',
);
await expect(dataUsed).toContainText("Data used (1)");
const workouts = dataUsed.locator(
'[data-slot="coach-result-table"][data-ref="r2"]',