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
3 changes: 2 additions & 1 deletion docs/branch-review-ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,5 @@ Use this ledger to prevent repeated branch and PR reviews when the reviewed HEAD
| 2026-07-11 | PR #489 / claude/document-viewer-redesign-55b68b | 9130c8b15a22dbbc965464a247ae930c04f2da62 | open-PR review, unresolved comments, and CI | P2 fixed: document deep links now expand the mobile indexed-text details and scroll the branch-specific visible mobile or desktop chunk instead of the first duplicated DOM match. Added focused desktop/mobile assertions. No additional high-confidence defect was found in the three-file diff. | Focused Prettier; TypeScript; `git diff --check`. Browser proof delegated to hosted CI because Turbopack rejects the isolated worktree's external node_modules junction. |
| 2026-07-11 | PR #488 / claude/code-review-42a2c3 | 7a8ea145013444f7cc29631499f48a8b0454937a | open-PR review, unresolved comments, and CI | Confirmed the remaining public error-code finding was already fixed at the reviewed head. Added the two focused advisory UI assertion stabilizations required by the hosted failure logs; no additional high-confidence defect was found in the changed scope. | `tests/http-error-response.test.ts` (3/3); Prettier check on affected files; `git diff --check`; hosted required CI passed before the test-only fix. Browser rerun deferred to hosted CI because Turbopack rejects the isolated worktree's external node_modules junction. |
| 2026-07-11 | PR #473 / claude/mobile-search-bar-popup-bx163m | 7cef01852a9713ec51184578868212df5805adbf | open-PR review, unresolved comments, and CI | P1 merge-conflict markers removed from the shared search header while retaining the all-viewport hero portal and inline fallback. P2 fixed: phone-hidden command results can no longer open, report expanded state, receive keyboard navigation, or execute an invisible selection. The launcher and global-shell conflict findings were already resolved at the reviewed head. | No conflict markers; TypeScript; focused Prettier; app-mode/search/universal-search Vitest (37/37); `git diff --check`. Browser proof delegated to hosted CI because Turbopack rejects the isolated worktree's external node_modules junction. |
| 2026-07-11 | PR #461 / claude/differentials-search-ux-polish-f2ff06 | 5e313872a | open-PR review, unresolved comments, and CI | Preserved diagnosis selections through comparison routing, selected the workflow that actually contains those diagnoses, and removed comparison controls from presentation rows. Restored required anonymous-search coverage and hardened the answer/search route mocks against invalid payloads and stale-response races. The prior best-answer and zero-source findings were already fixed. | TypeScript; focused differentials/app-mode Vitest (38/38); focused Prettier; `git diff --check`; hosted browser verification pending on the final head. |
| 2026-07-11 | PR #461 / claude/differentials-search-ux-polish-f2ff06 | 8bf455325b0915898417dd66aa61d419080c5528 | open-PR review, unresolved comments, and CI | Preserved diagnosis selections through workflow-aware comparison routing, constrained cross-workflow IDs to supported candidates, and removed comparison controls from presentation rows. Restored all four required core UI smoke markers and hardened answer/search mocks against invalid payloads and stale-response races. | Focused differential Vitest (22/22); TypeScript; full required CI, advisory Chromium, CodeRabbit, Semgrep, Gitleaks, and GitGuardian passed on the final head. |
| 2026-07-11 | PR #485 / claude/home-answer-page-layout-rtx10n | 162085d5177f01e503b498a04efe4597478163ef | open-PR review and CI | No unresolved review threads or additional high-confidence defects were found in the compact home-layout diff. Integrated the all-viewport hero composer and retained the branch's deliberate 44px icon and 24px mobile heading scale. | TypeScript; focused app-mode/search Vitest (28/28); full Vitest (1,594 passed, 1 skipped); runtime/action/sitemap/type-scale/lint checks passed; hosted browser verification pending on the final integrated head. |
4 changes: 2 additions & 2 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -1311,7 +1311,7 @@ summary::-webkit-details-marker {

@media (min-width: 640px) {
.mode-home-composer-slot {
width: min(100%, clamp(28rem, 74vw, 54rem));
width: min(100%, clamp(28rem, 50vw, 48rem));
max-width: calc(100vw - 3rem - var(--safe-area-left) - var(--safe-area-right));
}

Expand Down Expand Up @@ -1528,7 +1528,7 @@ summary::-webkit-details-marker {

@media (min-width: 1024px) {
.mode-home-composer-slot {
width: min(100%, clamp(36rem, 56vw, 56rem));
width: min(100%, clamp(28rem, 50vw, 48rem));
}

.universal-top-search-edge {
Expand Down
10 changes: 8 additions & 2 deletions src/components/ClinicalDashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3298,7 +3298,10 @@ export function ClinicalDashboard({
<SearchCommandProvider value={searchCommandContextValue}>
<div
className={cn(
"mx-auto max-w-7xl space-y-4 overflow-x-hidden px-3 py-4 sm:space-y-5 sm:px-4 sm:py-5 lg:px-8",
// overflow-x-CLIP, not -hidden: hidden makes this wrapper a scroll
// container (overflow-y computes to auto), which clips the composer's
// command dropdown mid-panel and shows a phantom inner scrollbar.
"mx-auto max-w-7xl space-y-4 overflow-x-clip px-3 py-4 sm:space-y-5 sm:px-4 sm:py-5 lg:px-8",
compactMobileModeHome && "max-sm:px-0",
// Centred mode homes carry little content, so drop the large
// mobile bottom padding (the fixed composer already has its own
Expand Down Expand Up @@ -3329,7 +3332,10 @@ export function ClinicalDashboard({
className={cn(
compactMobileModeHome
? cn(
"max-sm:flex max-sm:min-h-0 max-sm:flex-1 max-sm:flex-col",
// Every breakpoint keeps a viewport-height floor so
// justify/place-items-center has free space to centre the
// home block instead of hugging the header.
"max-sm:flex max-sm:min-h-[calc(100dvh-12.5rem)] max-sm:flex-col sm:min-h-[calc(100dvh-11rem)]",
centeredModeHome && "max-sm:justify-center",
)
: "min-h-[calc(100dvh-12.5rem)] sm:min-h-[calc(100dvh-11rem)]",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,9 @@ function CommandDropdown({
return (
<div
className={cn(
"universal-command-dropdown absolute left-0 right-0 z-30 overflow-hidden rounded-2xl border border-[color:var(--border-strong)] bg-[color:var(--surface)] shadow-[0_8px_20px_rgb(16_24_40_/_9%),0_24px_56px_rgb(16_24_40_/_14%)]",
// text-left: the hero composer slot sits inside the centred mode-home
// template, so without it the section headings inherit text-center.
"universal-command-dropdown absolute left-0 right-0 z-30 overflow-hidden rounded-2xl border border-[color:var(--border-strong)] bg-[color:var(--surface)] text-left shadow-[0_8px_20px_rgb(16_24_40_/_9%),0_24px_56px_rgb(16_24_40_/_14%)]",
opensUpward ? "bottom-[calc(100%+0.5rem)] top-auto" : "top-[calc(100%+0.5rem)]",
// Phones never get the typeahead popup — it crowds the small screen —
// so both placements stay hidden below their smallest useful width.
Expand Down Expand Up @@ -182,7 +184,7 @@ function CommandDropdown({
id={listboxId}
role="listbox"
aria-label={`${mode.label} search suggestions`}
className={cn("overflow-y-auto p-2", opensUpward ? "max-h-[min(38dvh,20rem)]" : "max-h-[26rem]")}
className={cn("overflow-y-auto p-2", opensUpward ? "max-h-[min(38dvh,20rem)]" : "max-h-[min(42dvh,24rem)]")}
>
{sections.map((section) =>
section.items.length ? (
Expand Down
32 changes: 16 additions & 16 deletions src/components/mode-home-template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -75,30 +75,30 @@ export function ModeHomeHero({

return (
<section
className={cn("grid justify-items-center px-4 sm:gap-4 sm:px-0", compact ? "gap-2" : "gap-3")}
className={cn("grid justify-items-center px-4 sm:gap-3 sm:px-0", compact ? "gap-2" : "gap-3")}
aria-labelledby={`${testId ?? "mode-home"}-title`}
>
<span
className={cn(
"mode-home-icon grid place-items-center rounded-2xl border border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)] shadow-[var(--shadow-inset)] sm:h-16 sm:w-16 lg:h-[4.75rem] lg:w-[4.75rem] lg:rounded-[1.35rem]",
compact ? "h-12 w-12" : "h-14 w-14",
"mode-home-icon grid place-items-center rounded-2xl border border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)] shadow-[var(--shadow-inset)] sm:h-14 sm:w-14",
compact ? "h-11 w-11" : "h-12 w-12",
)}
>
<Icon className={cn("sm:h-8 sm:w-8 lg:h-10 lg:w-10", compact ? "h-6 w-6" : "h-7 w-7")} aria-hidden="true" />
<Icon className={cn("sm:h-7 sm:w-7", compact ? "h-5 w-5" : "h-6 w-6")} aria-hidden="true" />
</span>
<div className={cn("grid", compact ? "gap-1.5 sm:gap-2" : "gap-2")}>
<div className={cn("grid", compact ? "gap-1.5" : "gap-2")}>
<Heading
id={`${testId ?? "mode-home"}-title`}
className={cn(
"text-balance font-extrabold leading-[1.05] tracking-normal text-[color:var(--text-heading)] sm:text-4xl lg:text-5xl",
compact ? "text-3xl-minus" : "text-3xl",
"text-balance font-extrabold leading-[1.1] tracking-normal text-[color:var(--text-heading)] sm:text-3xl lg:text-4xl",
compact ? "text-2xl" : "text-3xl-minus",
)}
>
{title}
</Heading>
<p
className={cn(
"mx-auto max-w-2xl text-pretty text-sm font-medium text-[color:var(--text-muted)] sm:text-base sm:leading-6 lg:text-lg-minus",
"mx-auto max-w-2xl text-pretty text-sm font-medium text-[color:var(--text-muted)] sm:text-base sm:leading-6",
compact ? "leading-5" : "leading-6",
)}
>
Expand Down Expand Up @@ -219,7 +219,7 @@ export function ModeHomeTemplate({
<div
data-testid={testId}
className={cn(
"mode-home-template mx-auto box-border flex w-full max-w-none flex-col items-center justify-center gap-3.5 px-0 text-center sm:max-w-[64rem] sm:gap-6 lg:gap-7",
"mode-home-template mx-auto box-border flex w-full max-w-none flex-col items-center justify-center gap-3.5 px-0 text-center sm:max-w-[58rem] sm:gap-5",
className,
)}
>
Expand All @@ -235,20 +235,20 @@ export function ModeHomeTemplate({
{actions?.length ? (
<section
aria-label={actionsLabel}
className="grid w-full max-w-none overflow-hidden rounded-none border-y border-[color:var(--border)] bg-[color:var(--surface)] shadow-[var(--shadow-card)] sm:max-w-none sm:grid-cols-[repeat(auto-fit,minmax(16rem,1fr))] sm:gap-5 sm:overflow-visible sm:rounded-xl sm:border sm:shadow-[var(--shadow-card)] lg:rounded-none lg:border-0 lg:bg-transparent lg:shadow-none"
className="grid w-full max-w-none overflow-hidden rounded-none border-y border-[color:var(--border)] bg-[color:var(--surface)] shadow-[var(--shadow-card)] sm:max-w-none sm:grid-cols-[repeat(auto-fit,minmax(15rem,1fr))] sm:gap-4 sm:overflow-visible sm:rounded-xl sm:border sm:shadow-[var(--shadow-card)] lg:rounded-none lg:border-0 lg:bg-transparent lg:shadow-none"
>
{actions.map((action, index) => {
const ActionIcon = action.icon;
const content = (
<>
<span className="grid h-10 w-10 place-items-center rounded-lg border border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)] shadow-[var(--shadow-inset)] sm:h-14 sm:w-14 sm:rounded-xl">
<ActionIcon className="h-5 w-5 sm:h-7 sm:w-7" aria-hidden="true" />
<span className="grid h-10 w-10 place-items-center rounded-lg border border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)] shadow-[var(--shadow-inset)] sm:h-11 sm:w-11 sm:rounded-xl">
<ActionIcon className="h-5 w-5 sm:h-6 sm:w-6" aria-hidden="true" />
</span>
<span className="min-w-0">
<span className="block text-balance text-base font-bold leading-5 text-[color:var(--text-heading)] [overflow-wrap:anywhere] sm:text-lg-minus">
<span className="block text-balance text-base font-bold leading-5 text-[color:var(--text-heading)] [overflow-wrap:anywhere]">
{action.title}
</span>
<span className="mt-1 block text-xs font-medium leading-5 text-[color:var(--text-muted)] sm:text-sm sm:leading-6">
<span className="mt-1 block text-xs font-medium leading-5 text-[color:var(--text-muted)] sm:text-sm sm:leading-5">
{action.description}
</span>
</span>
Expand All @@ -259,7 +259,7 @@ export function ModeHomeTemplate({
</>
);
const actionClassName = cn(
"mode-home-action group grid min-h-[4.8rem] w-full grid-cols-[2.5rem_minmax(0,1fr)_1.25rem] items-center gap-3 bg-[color:var(--surface)] px-4 py-3 text-left transition hover:bg-[color:var(--surface-subtle)] focus-visible:relative focus-visible:z-10 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[color:var(--focus)] disabled:cursor-wait disabled:opacity-60 sm:min-h-[8rem] sm:grid-cols-[3.5rem_minmax(0,1fr)_1.5rem] sm:gap-4 sm:rounded-lg sm:border sm:border-[color:var(--border)] sm:px-5 sm:py-5 sm:shadow-[var(--shadow-card)] lg:min-h-[8.4rem] lg:px-6",
"mode-home-action group grid min-h-[4.8rem] w-full grid-cols-[2.5rem_minmax(0,1fr)_1.25rem] items-center gap-3 bg-[color:var(--surface)] px-4 py-3 text-left transition hover:bg-[color:var(--surface-subtle)] focus-visible:relative focus-visible:z-10 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-[-2px] focus-visible:outline-[color:var(--focus)] disabled:cursor-wait disabled:opacity-60 sm:min-h-[6.25rem] sm:grid-cols-[2.75rem_minmax(0,1fr)_1.25rem] sm:gap-3.5 sm:rounded-lg sm:border sm:border-[color:var(--border)] sm:px-4 sm:py-4 sm:shadow-[var(--shadow-card)] lg:px-5",
index > 0 && "border-t border-[color:var(--border)] sm:border-t-[color:var(--border)]",
);

Expand Down Expand Up @@ -290,7 +290,7 @@ export function ModeHomeTemplate({
{pills?.length ? (
<section
aria-label={pillsTitle ?? "Quick links"}
className="grid w-full max-w-none self-stretch gap-2.5 border-t border-[color:var(--border)]/70 px-4 pt-5 sm:px-0 sm:pt-6"
className="grid w-full max-w-none self-stretch gap-2.5 border-t border-[color:var(--border)]/70 px-4 pt-5 sm:px-0 sm:pt-5"
>
{pillsTitle || pillsAction ? (
<div className="flex min-h-8 w-full items-center justify-between gap-3">
Expand Down
12 changes: 12 additions & 0 deletions tests/clinical-dashboard-merge-artifacts.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ const globalSearchShellSource = readFileSync(
resolve(process.cwd(), "src/components/clinical-dashboard/global-mockup-search-shell.tsx"),
"utf8",
);
const clinicalDashboardSource = readFileSync(resolve(process.cwd(), "src/components/ClinicalDashboard.tsx"), "utf8");
const globalStylesSource = readFileSync(resolve(process.cwd(), "src/app/globals.css"), "utf8");

type FoundDeclaration = { node: ts.FunctionDeclaration; ast: ts.SourceFile };

Expand Down Expand Up @@ -60,6 +62,16 @@ function descendantIdentifiers(node: ts.Node) {
}

describe("ClinicalDashboard merge-artifact guards", () => {
it("keeps the mode-home composer continuous across the desktop breakpoint", () => {
expect(globalStylesSource.match(/clamp\(28rem, 50vw, 48rem\)/g)).toHaveLength(2);
expect(globalStylesSource).not.toContain("clamp(34rem, 50vw, 48rem)");
});

it("keeps a mobile height floor for centered mode homes", () => {
expect(clinicalDashboardSource).toContain("max-sm:min-h-[calc(100dvh-12.5rem)]");
expect(clinicalDashboardSource).not.toContain("max-sm:min-h-0 max-sm:flex-1");
});

it("reserves phone space for the fixed mode-home composer", () => {
expect(globalSearchShellSource).toContain("const mobileComposerReserve = !shouldShowSearchComposer");
expect(globalSearchShellSource).not.toContain("const mobileComposerReserve = !reservesFloatingComposer");
Expand Down
8 changes: 4 additions & 4 deletions tests/ui-tools.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -577,10 +577,10 @@ test.describe("Clinical KB tools launcher", () => {
};
if (!baseline) {
baseline = metrics;
// Compact hero mobile scale: 3rem icon, 1.625rem heading, 0.875rem subtitle.
expect(metrics.iconWidth).toBe(48);
expect(metrics.iconHeight).toBe(48);
expect(metrics.headingFontSize).toBe(26);
// Compact hero mobile scale: 2.75rem icon, 1.5rem heading, 0.875rem subtitle.
expect(metrics.iconWidth).toBe(44);
expect(metrics.iconHeight).toBe(44);
expect(metrics.headingFontSize).toBeCloseTo(24, 1);
expect(metrics.subtitleFontSize).toBeCloseTo(14, 1);
} else {
expect(metrics, `${home.path} hero metrics`).toEqual(baseline);
Expand Down