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: 3 additions & 0 deletions docs/branch-review-ledger.md
Original file line number Diff line number Diff line change
Expand Up @@ -1253,6 +1253,7 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie
| 2026-07-29 | codex/search-composer-focus-pwa | f9a14e8e38a568d8675c4154597f73340161e4ea | current diff vs origin/main: PWA search composer focus | P1 fix reviewed; passive viewport scroll no longer blurs search | focused Vitest 48/48; typecheck; ledger guard; phone and PR plans inspected |
| 2026-07-29 | codex/search-composer-focus-pwa | 5c6a75c21833dc31d4f8658cec15154f58918a36 | PR #1373 unresolved review comment remediation | P2 test race and keyboard/scrollbar intent gaps fixed | focused Vitest 48/48; typecheck; diff check |
| 2026-07-29 | codex/search-composer-focus-pwa | 3f7cd1e4069b7ef8f8b18519adfb0dba0dc349a8 | PR #1373 CircleCI lint remediation | Deterministic unused locator warning removed | CircleCI format passed; lint root cause captured; focused Vitest 48/48; typecheck |
| 2026-07-29 | PR #1380 / codex/document-reader-condensed-view | f5cf28172d01ff2b05b627bc71bee41fdda7e1f3 | PR #1380 CI babysit: lint + Production UI | FIXED. Root causes: (1) react-hooks/set-state-in-effect in density/priorities/indexed-text sync blocked Static PR + CircleCI; (2) Prettier format drift; (3) Codex P2 inert full-view summary; (4) exclusive-accordion latched condensed indexed-text closed over deep-linked chunks (Production UI). Fixed via useSyncExternalStore + adjust-during-render + forceReveal restore. Codex thread resolved. | lint; format:check; typecheck; verify:cheap; vitest document-*; playwright ui-smoke document condensed (4/4); hosted Static/Build/Unit/Advisory/Production UI/PR required/CircleCI SUCCESS |
| 2026-07-29 | PR #1379 / claude/claude-md-documentation-kfoxrb | be83f5cebbf44495d4ad0fc22d7ba7bfe80bdb32 | PR babysit | ALREADY MERGED; no failing CI, 0 review threads, 0 Bugbot findings; local verify:pr-local green (422 files / 4271 tests); no code changes | hosted PR required SUCCESS; docs:check-links/scripts; prettier; verify:pr-local 422/4271; Bugbot triage 0 findings |
| 2026-07-29 | claude/clinical-design-system-update-e34ca9 | 064e70b79bfd9c22008f2d7bf57b0515870b73ce | PR #1375 conflict fix + Bugbot | FIXED CONFLICTING: merged origin/main; took DocumentViewerRail from main; reapplied tracking-eyebrow in document-rail-panels. Bugbot: 0 findings. MERGEABLE; hosted CI re-running. No unresolved review threads. | local: typecheck PASS; lint PASS; format:changed PASS; design-token+theme+section-nav 40/40; verify:cheap 423 files / 4298 tests PASS. Hosted: PR policy/Change scope/Safety/Semgrep/Gitleaks PASS; Build/Static/Unit/Production UI pending at record time. |
| 2026-07-29 | claude/clinical-design-system-update-e34ca9 | 38bc5682abc4ceca26eca9dfba63872e1cf032be | PR #1375 conflict fix + Bugbot | FIXED Production UI flake: form-detail-page strict-mode double main during hydration; expectSingleSettledOwner on desktop+mobile form detail tests. Prior conflict fix retained. MERGEABLE; CI re-running. | local: form detail e2e 2/2 PASS; prior verify:cheap/typecheck/lint green. Hosted Production UI was fail on 0a3f7a6d; awaiting tip recheck. |
Expand All @@ -1262,5 +1263,7 @@ Records before 2026-07-28 were written by hand and had drifted: 146 lines carrie
| 2026-07-29 | claude/latency-fixes-2026-07-29 | 0e30215e98328513e23342a8dceafc9d1728bf9d | pr-1376-ci-bugbot-repair | fixed-p1s-plus-followups;owner-scoped-epochs;shared-cache-race;stream-signal;empty-scope-timing;threads-resolved | vitest:15-pass;docs:check-links:pass;verify:cheap:earlier-pass |
| 2026-07-29 | codex/remove-source-overlays | a08a81d320c9f8e1bbbe1facc266d8257213b1ad | PR #1378 babysit | FIXED Codex P1s (restore governance notice); overlays/Preview removed; merged main; verify:cheap PASS; Bugbot no open findings | verify:cheap 4273 pass; focused DOM 4/4; eslint/tsc/build PASS; hosted CI re-running after main sync |
| 2026-07-29 | claude/clinical-design-system-update-e34ca9 | 0cdae091ad92f40e0ad7335b3e2d396c44188a4f | PR #1375 conflict fix + Bugbot | FIXED second CONFLICTING after #1378: took main removal of SelectedDocumentEvidencePanel; retained tracking-eyebrow on surviving document-search-results. Prior DocumentViewerRail + form-detail settlement retained. MERGEABLE; CI re-running. | local: document-search-record-fault + design-token tests; merge-tree CLEAN; prior Production UI PASS on 6903f51f; form-detail e2e 2/2. |
| 2026-07-29 | codex/document-reader-condensed-view | 7cefb24e99f9745a61843c7e48c4889f7324ec42 | pr-1380-main-merge-coderabbit-density | merged origin/main; resolved source-panels conflict (kept condensed details + tracking-eyebrow); density in-memory fallback when storage blocked; summary keys + search/plain compact tests; local vitest/lint/typecheck/format/playwright condensed pass; awaiting hosted CI | vitest document suites 20/20; lint; typecheck; format:check; playwright condensed 4/4; merge-tree clean |
| 2026-07-29 | claude/test-coverage-analysis-2vcd8a | 0922d7f56624ef84be8abcb2bbc89205027cf9a6 | PR #1383 babysit | BLOCKER CLEARED: merged origin/main; renumbered coverage follow-ups #098/#099 -> #106/#107 (main claimed #098-#105). Before: CONFLICTING/DIRTY, 4 behind; CI green on prior tip; 0 review threads; 0 Bugbot findings. After: mergeable expected; verify:cheap 424 files/4371 passed; test:coverage exit 0; format:changed + check:rag:fixtures pass. | verify:cheap PASS (424 files, 4371 passed \| 4 skipped); test:coverage PASS (no threshold errors); format:changed PASS; check:rag:fixtures PASS (36 golden); Bugbot: no findings; no provider-backed checks |
| 2026-07-29 | claude/test-coverage-analysis-2vcd8a | 6f476b5f741627cb622af57d1b4665e3989789ca | PR #1383 babysit | CLOSEOUT at tip after ledger bookkeeping commit. Merge conflict cleared; coverage follow-ups live as #106/#107; local gates green; awaiting hosted CI on tip. | same as prior tip 0922d7f5 plus ledger append only; no product code change |
| 2026-07-29 | codex/document-reader-condensed-view | 5678e878d4fe681d33bb58df5b5b3468a138a1c8 | pr-1380-ci-green-resync | hosted CI green on 7150899a (Static/Build/Unit/Advisory/Production UI/PR required/CircleCI); CodeRabbit density fallback + summary keys + search/plain compact tests landed; unresolved review threads none; resynced main after tip went BEHIND by 1 | hosted CI success on 7150899a; merge-tree clean; bugbot no P0/P1 |
44 changes: 11 additions & 33 deletions src/components/DocumentViewer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,8 @@ import {
} from "@/components/document-viewer/section-nav";
import { useDocumentSectionSpy } from "@/components/document-viewer/use-section-spy";
import { useDocumentChromeMetrics } from "@/components/document-viewer/use-document-chrome-metrics";
import { useDocumentViewDensity } from "@/components/document-viewer/use-document-view-density";
import { usePrintableDisclosures } from "@/components/document-viewer/use-printable-disclosures";

// pdf-canvas-viewer is only needed after a source document has loaded and the
// user is viewing a PDF. Keeping it out of the document route's initial client
Expand Down Expand Up @@ -192,39 +194,7 @@ export function DocumentViewer({
},
[activePage, documentId],
);
useEffect(() => {
const previousOpenStates = new Map<HTMLDetailsElement, boolean>();
const expandPrintableDisclosures = () => {
if (previousOpenStates.size) return;
previousOpenStates.clear();
const printable = window.document.querySelectorAll<HTMLDetailsElement>("details.source-print");
window.document
.querySelectorAll<HTMLDetailsElement>('details.source-print, details[name="document-viewer-section"]')
.forEach((disclosure) => {
previousOpenStates.set(disclosure, disclosure.open);
});
printable.forEach((disclosure) => {
disclosure.open = true;
});
};
const restorePrintableDisclosures = () => {
const connected = [...previousOpenStates].filter(([disclosure]) => disclosure.isConnected);
connected.forEach(([disclosure]) => {
disclosure.open = false;
});
connected.forEach(([disclosure, wasOpen]) => {
if (wasOpen) disclosure.open = true;
});
previousOpenStates.clear();
};
window.addEventListener("beforeprint", expandPrintableDisclosures);
window.addEventListener("afterprint", restorePrintableDisclosures);
return () => {
restorePrintableDisclosures();
window.removeEventListener("beforeprint", expandPrintableDisclosures);
window.removeEventListener("afterprint", restorePrintableDisclosures);
};
}, []);
usePrintableDisclosures();
const [document, setDocument] = useState<ClinicalDocument | null>(() => initialDetail?.document ?? null);
const [pages, setPages] = useState<PageRow[]>(() => initialDetail?.pages ?? []);
const [images, setImages] = useState<ImageRow[]>(() => initialDetail?.images ?? []);
Expand Down Expand Up @@ -266,6 +236,7 @@ export function DocumentViewer({
const [localProjectReady, setLocalProjectReady] = useState(true);
const [mobileActionsOpen, setMobileActionsOpen] = useState(false);
const [sectionSheetOpen, setSectionSheetOpen] = useState(false);
const [compactView, setCompactView] = useDocumentViewDensity();
const [composerChromeFocused, setComposerChromeFocused] = useState(false);
const [shellScrollContainer, setShellScrollContainer] = useState<HTMLElement | null>(null);
useEffect(() => {
Expand Down Expand Up @@ -1183,6 +1154,8 @@ export function DocumentViewer({
onSelect={jumpToSection}
documentTitle={headerTitle}
returnFocusRef={sectionTriggerRef}
compact={compactView}
onCompactChange={setCompactView}
/>
{readyDocument ? (
<Sheet
Expand Down Expand Up @@ -1330,6 +1303,7 @@ export function DocumentViewer({
composerScrollHidden ? "0rem" : "calc(9rem + var(--safe-area-bottom) + var(--keyboard-height, 0px))"
}
data-phone-chrome-transition={reserveTransitioning ? "active" : "idle"}
data-document-view={compactView ? "condensed" : "full"}
className={cn(
"mx-auto grid max-w-[1440px] gap-4 px-3 py-4 sm:gap-5 sm:px-4 sm:py-5 sm:pb-40 lg:grid-cols-[minmax(0,1fr)_480px] lg:items-start lg:px-8",
// The visible fixed composer needs endpoint clearance. Once hidden,
Expand Down Expand Up @@ -1400,6 +1374,7 @@ export function DocumentViewer({
onDownload={() => void openSourceDownload()}
downloading={downloadingSource}
canSummarizeDocument={canSummarizeDocument}
compact={compactView}
/>
</div>
) : null}
Expand Down Expand Up @@ -1561,6 +1536,7 @@ export function DocumentViewer({
sectionId="source-text"
selectedChunkId={activeChunkId}
onSearchChange={setSourceSearch}
compact={compactView}
/>
</div>
</div>
Expand All @@ -1570,6 +1546,8 @@ export function DocumentViewer({
documentSections={documentSections}
activeSectionId={activeSectionId}
onSelectSection={jumpToSection}
compact={compactView}
onCompactChange={setCompactView}
indexWarnings={indexWarnings}
effectiveLoadingDocument={effectiveLoadingDocument}
selectedChunk={selectedChunk}
Expand Down
12 changes: 11 additions & 1 deletion src/components/document-viewer/document-clinical-summary.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -346,19 +346,29 @@ export function DocumentClinicalSummary({
document,
pageHref,
onPageChange,
compact = false,
}: {
document: ClinicalDocument;
pageHref: (page: number) => string;
onPageChange: (page: number) => void;
compact?: boolean;
}) {
const model = buildDocumentClinicalSummaryModel(document);
const [summaryExpanded, setSummaryExpanded] = useState(false);
const [prioritiesExpanded, setPrioritiesExpanded] = useState(true);
const [prioritiesExpanded, setPrioritiesExpanded] = useState(!compact);
const [prevCompact, setPrevCompact] = useState(compact);
const [mobilePrioritiesOpen, setMobilePrioritiesOpen] = useState(false);
const mobilePrioritiesButtonRef = useRef<HTMLButtonElement>(null);
const prioritiesId = useId();
const canExpandSummary = model.summary.length > 140;

// React's supported "adjust state during render" pattern for reacting to the
// condensed/full density prop without a cascading setState-in-effect.
if (compact !== prevCompact) {
setPrevCompact(compact);
setPrioritiesExpanded(!compact);
}

function navigateFromSheet(page: number) {
setMobilePrioritiesOpen(false);
onPageChange(page);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@ export function DocumentOverviewLanding({
onDownload,
downloading,
canSummarizeDocument,
compact,
}: {
document: ClinicalDocument;
signedUrl: string | null;
Expand All @@ -62,6 +63,7 @@ export function DocumentOverviewLanding({
onDownload: () => void;
downloading: boolean;
canSummarizeDocument: boolean;
compact: boolean;
}) {
const documentType = compactDocumentType(document);

Expand Down Expand Up @@ -131,7 +133,7 @@ export function DocumentOverviewLanding({
</DocumentActionButton>
</div>
</article>
<DocumentClinicalSummary document={document} pageHref={pageHref} onPageChange={onPageChange} />
<DocumentClinicalSummary document={document} pageHref={pageHref} onPageChange={onPageChange} compact={compact} />
</section>
);
}
6 changes: 6 additions & 0 deletions src/components/document-viewer/document-rail-panels.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export function DocumentViewerRail({
documentSections,
activeSectionId,
onSelectSection,
compact,
onCompactChange,
indexWarnings,
effectiveLoadingDocument,
selectedChunk,
Expand All @@ -61,6 +63,8 @@ export function DocumentViewerRail({
documentSections: DocumentSection[];
activeSectionId: string | null;
onSelectSection: (id: string) => void;
compact: boolean;
onCompactChange: (compact: boolean) => void;
indexWarnings: string[];
effectiveLoadingDocument: boolean;
selectedChunk: ChunkRow | undefined;
Expand Down Expand Up @@ -96,6 +100,8 @@ export function DocumentViewerRail({
sections={documentSections}
activeId={activeSectionId}
onSelect={onSelectSection}
compact={compact}
onCompactChange={onCompactChange}
className="hidden md:col-span-2 lg:col-span-1 lg:block"
/>

Expand Down
6 changes: 3 additions & 3 deletions src/components/document-viewer/section-index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,9 +107,9 @@ export function buildDocumentSectionIndex(input: DocumentSectionIndexInput): Doc
label: "Indexed source text",
icon: FileSearch,
detail: input.loading ? "Indexing" : plural(input.chunkCount, "chunk"),
// IndexedTextPanel is a <section>, not a details disclosure — a chevron
// would advertise an open action that never happens.
collapsible: false,
// Condensed view renders IndexedTextPanel as a disclosure; full view keeps
// it open, while the same navigation row remains valid in both states.
collapsible: true,
pending: input.loading,
},
];
Expand Down
50 changes: 49 additions & 1 deletion src/components/document-viewer/section-nav.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

import { ChevronDown, Loader2 } from "lucide-react";
import { ChevronDown, ListCollapse, Loader2 } from "lucide-react";
import type { RefObject } from "react";

import { cn } from "@/components/ui-primitives";
Expand Down Expand Up @@ -151,16 +151,58 @@ function SectionListHeading({ sections, activeId }: { sections: DocumentSection[
);
}

export function DocumentViewDensityToggle({
compact,
onCompactChange,
className,
}: {
compact: boolean;
onCompactChange: (compact: boolean) => void;
className?: string;
}) {
return (
<button
type="button"
data-testid="document-view-density-toggle"
aria-pressed={compact}
aria-label={compact ? "Show full document content" : "Condense document content"}
title={compact ? "Show the full indexed text" : "Collapse long document sections"}
onClick={() => onCompactChange(!compact)}
className={cn(
"flex min-h-tap w-full items-center gap-2.5 rounded-lg border px-2.5 text-left transition",
focusRing,
compact
? "border-[color:var(--clinical-accent-border)] bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)]"
: "border-[color:var(--border)] bg-[color:var(--surface-subtle)] text-[color:var(--text-muted)] hover:border-[color:var(--border-strong)] hover:text-[color:var(--text)]",
className,
)}
>
<ListCollapse aria-hidden="true" className="h-4 w-4 shrink-0" />
<span className="min-w-0 flex-1">
<span className="block text-xs font-bold">{compact ? "Condensed view" : "Full view"}</span>
<span className="block truncate text-3xs font-semibold opacity-75">
{compact ? "Long sections collapsed" : "All indexed text shown"}
</span>
</span>
<span className="text-3xs font-black uppercase tracking-[0.08em]">{compact ? "On" : "Off"}</span>
</button>
);
}

/** Desktop and tablet placement: a card in the column the viewer already renders. */
export function DocumentSectionIndexCard({
sections,
activeId,
onSelect,
compact,
onCompactChange,
className,
}: {
sections: DocumentSection[];
activeId: string | null;
onSelect: (id: string) => void;
compact: boolean;
onCompactChange: (compact: boolean) => void;
className?: string;
}) {
if (sections.length === 0) return null;
Expand All @@ -175,6 +217,7 @@ export function DocumentSectionIndexCard({
)}
>
<SectionListHeading sections={sections} activeId={activeId} />
<DocumentViewDensityToggle compact={compact} onCompactChange={onCompactChange} className="mb-2" />
<DocumentSectionList sections={sections} activeId={activeId} onSelect={onSelect} />
</nav>
);
Expand All @@ -193,6 +236,8 @@ export function DocumentSectionSheet({
onSelect,
documentTitle,
returnFocusRef,
compact,
onCompactChange,
}: {
open: boolean;
onClose: () => void;
Expand All @@ -201,6 +246,8 @@ export function DocumentSectionSheet({
onSelect: (id: string) => void;
documentTitle: string;
returnFocusRef?: RefObject<HTMLElement | null>;
compact: boolean;
onCompactChange: (compact: boolean) => void;
}) {
const active = sections.find((section) => section.id === activeId) ?? sections[0];
const position = sections.findIndex((section) => section.id === activeId) + 1;
Expand All @@ -219,6 +266,7 @@ export function DocumentSectionSheet({
returnFocusRef={returnFocusRef}
testId="document-section-sheet"
>
<DocumentViewDensityToggle compact={compact} onCompactChange={onCompactChange} className="mb-3" />
<DocumentSectionList
sections={sections}
activeId={activeId}
Expand Down
Loading