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
214 changes: 203 additions & 11 deletions docs/answer-page-redesign-handover.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
| 2026-08-24 | PR #2346 | 586ee8182c7bee69385f47dc215ee0ddcfd5d9da | answer page redesign handover | Reviewed the handover and the perfected mockup against the live answer surface. Two core decisions (one-colour mark, one source per drawer) confirmed sound and kept. Seven findings; four are defects: overlapping mark tap targets can open the wrong source, box-shadow ring plus background wash both drop in forced-colors, the streaming frame draws a shape the stream contract excludes by name, and the verification line contradicts the placement answer-result-surface records (#207/#227/#228). Three gaps: only 1 of 5 AnswerState kinds drawn (source_only was ~2/3 of the cited sample), supportLevel needs four treatments not two, no citation-feedback control. Corrections landed in handover section 12 plus new section 2b; corrected design built at /mockups/answer-chat-perfected-v2 in PR #2356. No production surface changed. | lint, typecheck, test (831 files / 10011 passed), verify:pr-local, build, check:bundle-budget (mockups 522.0 KiB vs 487.6 KiB baseline, within tolerance), Chromium browser check at 390px and 1440px, Chromium forcedColors active |
1 change: 1 addition & 0 deletions docs/site-map.md
Original file line number Diff line number Diff line change
Expand Up @@ -1149,6 +1149,7 @@ This file is generated by `npm run docs:update` (or `npm run sitemap:update` dir
- `/mockups/accessible-table-browser-fixture` - Route discovered from app directory Source: `src/app/mockups/accessible-table-browser-fixture/page.tsx`.
- `/mockups/also-matches-accents` - Route discovered from app directory Source: `src/app/mockups/also-matches-accents/page.tsx`.
- `/mockups/answer-chat-perfected` - Route discovered from app directory Source: `src/app/mockups/answer-chat-perfected/page.tsx`.
- `/mockups/answer-chat-perfected-v2` - Route discovered from app directory Source: `src/app/mockups/answer-chat-perfected-v2/page.tsx`.
- `/mockups/answer-chat-redesign` - Route discovered from app directory Source: `src/app/mockups/answer-chat-redesign/page.tsx`.
- `/mockups/answer-evidence-popups` - Route discovered from app directory Source: `src/app/mockups/answer-evidence-popups/page.tsx`.
- `/mockups/answer-home-proposal` - Route discovered from app directory Source: `src/app/mockups/answer-home-proposal/page.tsx`.
Expand Down
5 changes: 5 additions & 0 deletions src/app/mockups/answer-chat-perfected-v2/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { AnswerChatPerfectedV2MockupsPage } from "@/components/answer-chat-perfected-v2-mockups";

export default function AnswerChatPerfectedV2MockupRoute() {
return <AnswerChatPerfectedV2MockupsPage />;
}
3 changes: 2 additions & 1 deletion src/app/mockups/mockups-layout-client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ export function MockupsLayoutClient({ children }: { children: ReactNode }) {
// sits directly between them, so shared chrome would read as a second real
// header and a second real search bar over the study.
const isAnswerChatRedesignMockup = pathname === "/mockups/answer-chat-redesign";
const isAnswerChatPerfectedMockup = pathname === "/mockups/answer-chat-perfected";
const isAnswerChatPerfectedMockup =
pathname === "/mockups/answer-chat-perfected" || pathname === "/mockups/answer-chat-perfected-v2";
// Draws its own sticky chrome + device frames for /privacy; shared shell would
// read as a second real header over the study.
const isPrivacyPageDirectionsMockup = pathname === "/mockups/privacy-page-directions";
Expand Down
48 changes: 24 additions & 24 deletions src/components/answer-chat-perfected-mockups.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,20 +42,20 @@ import { cn } from "@/components/ui-primitives";
* Nothing here is wired to real retrieval. All copy is synthetic.
*/

const focusRing =
export const focusRing =
"focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-[color:var(--focus)]";

/** Frame and mark geometry lives in JS: this repo's mockup CSS pipeline only
* re-emits utilities that already exist in non-mockup source, so a novel
* arbitrary value written inside a mockup file never reaches the stylesheet. */
const PHONE_WIDTH = 390;
const PHONE_HEIGHT = 844;
const DESKTOP_HEIGHT = 660;
const PROSE_MEASURE = { maxWidth: "68ch" } as const;
export const PHONE_WIDTH = 390;
export const PHONE_HEIGHT = 844;
export const DESKTOP_HEIGHT = 660;
export const PROSE_MEASURE = { maxWidth: "68ch" } as const;

type SourceStatus = "current" | "review-due";
export type SourceStatus = "current" | "review-due";

type MockSource = {
export type MockSource = {
id: string;
index: number;
short: string;
Expand All @@ -68,7 +68,7 @@ type MockSource = {
attachment?: { kind: "table" | "figure"; label: string };
};

const SOURCES: MockSource[] = [
export const SOURCES: MockSource[] = [
{
id: "s1",
index: 1,
Expand Down Expand Up @@ -108,18 +108,18 @@ const SOURCES: MockSource[] = [
},
];

const sourceById = (id: string) => SOURCES.find((source) => source.id === id) ?? SOURCES[0];
export const sourceById = (id: string) => SOURCES.find((source) => source.id === id) ?? SOURCES[0];

type AnswerBlock = {
export type AnswerBlock = {
id: string;
text: string;
sourceIds: string[];
safety?: boolean;
};

const QUESTION = "What physical health monitoring does a patient on clozapine need in the first year?";
export const QUESTION = "What physical health monitoring does a patient on clozapine need in the first year?";

const ANSWER_BLOCKS: AnswerBlock[] = [
export const ANSWER_BLOCKS: AnswerBlock[] = [
{
id: "b1",
text: "Haematological monitoring is the part that cannot slip: FBC and ANC at baseline, weekly for the first 18 weeks, fortnightly to week 52, then monthly while treatment continues.",
Expand Down Expand Up @@ -189,7 +189,7 @@ export const MARK_VARIANTS: Array<{
* carry a real tap target without disturbing the line, and show which
* source is currently open.
*/
function RefMark({
export function RefMark({
source,
variant,
active,
Expand Down Expand Up @@ -305,7 +305,7 @@ function RefMark({
* cluster are bound into one non-breaking run so a mark can never be
* stranded at the start of the next line.
*/
function Claim({
export function Claim({
block,
variant,
activeSourceId,
Expand Down Expand Up @@ -358,7 +358,7 @@ function Claim({

/* ══════════════════════ the source rail ══════════════════════ */

function statusLabel(status: SourceStatus) {
export function statusLabel(status: SourceStatus) {
return status === "current" ? "Current" : "Review due";
}

Expand All @@ -371,7 +371,7 @@ function statusLabel(status: SourceStatus) {
* separate object beside the card rather than part of it, and the badge was
* already carrying the same signal.
*/
function SourceRail({ activeId, onOpen }: { activeId: string | null; onOpen: (id: string) => void }) {
export function SourceRail({ activeId, onOpen }: { activeId: string | null; onOpen: (id: string) => void }) {
return (
<div
className="flex gap-1.5 overflow-x-auto pb-1"
Expand Down Expand Up @@ -734,7 +734,7 @@ function DrawerPanel({

/* ══════════════════════ the message ══════════════════════ */

function AssistantMark() {
export function AssistantMark() {
return (
<span
aria-hidden="true"
Expand Down Expand Up @@ -852,7 +852,7 @@ function AnswerMessage({

/* ══════════════════════ screen chrome ══════════════════════ */

function TopBar() {
export function TopBar() {
return (
<div className="flex shrink-0 items-center gap-2 border-b border-[color:var(--border)] bg-[color:var(--surface-chrome)] px-2.5 py-2">
<button
Expand Down Expand Up @@ -880,7 +880,7 @@ function TopBar() {
);
}

function Composer({ suggestions = false }: { suggestions?: boolean }) {
export function Composer({ suggestions = false }: { suggestions?: boolean }) {
return (
<div className="shrink-0 border-t border-[color:var(--border)] bg-[color:var(--surface-chrome)] px-3 pb-3 pt-2">
{suggestions ? (
Expand Down Expand Up @@ -942,7 +942,7 @@ function ThreadDivider() {
);
}

function UserTurn() {
export function UserTurn() {
return (
<div className="flex justify-end">
<p
Expand Down Expand Up @@ -999,7 +999,7 @@ function AnswerScreen({

/* ══════════════════════ page scaffold ══════════════════════ */

function PhoneFrame({ caption, children }: { caption: string; children: React.ReactNode }) {
export function PhoneFrame({ caption, children }: { caption: string; children: React.ReactNode }) {
return (
<figure className="min-w-0">
<figcaption className="mb-2 text-2xs font-semibold uppercase tracking-eyebrow text-[color:var(--text-muted)]">
Expand All @@ -1021,7 +1021,7 @@ function PhoneFrame({ caption, children }: { caption: string; children: React.Re
);
}

function DesktopFrame({ caption, children }: { caption: string; children: React.ReactNode }) {
export function DesktopFrame({ caption, children }: { caption: string; children: React.ReactNode }) {
return (
<figure className="min-w-0">
<figcaption className="mb-2 text-2xs font-semibold uppercase tracking-eyebrow text-[color:var(--text-muted)]">
Expand Down Expand Up @@ -1055,7 +1055,7 @@ function DesktopFrame({ caption, children }: { caption: string; children: React.
);
}

function Panel({
export function Panel({
id,
step,
title,
Expand Down Expand Up @@ -1143,7 +1143,7 @@ function MarkSpecimen() {
);
}

function DetailCard({ title, body, children }: { title: string; body: string; children: React.ReactNode }) {
export function DetailCard({ title, body, children }: { title: string; body: string; children: React.ReactNode }) {
return (
<article className="rounded-xl border border-[color:var(--border)] bg-[color:var(--surface)] p-3">
<h3 className="text-sm font-semibold text-[color:var(--text-heading)]">{title}</h3>
Expand Down
Loading
Loading