From 875bd924a9807940387c7b92dca1156a37980737 Mon Sep 17 00:00:00 2001 From: Michael Chihlas Date: Fri, 24 Apr 2026 23:45:52 -0400 Subject: [PATCH] fix(pilot): auto-scroll Resolve preview into view when opened MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ResolutionNotePreview popover renders inside TaskLane's overflow-y-auto region at the bottom of the lane. On a 720px viewport with the default question/check list expanded, the popover lands below the visible scroll position — the engineer clicks "Preview Resolve note", sees the button label flip to "Showing", but no preview appears on screen. Add a useEffect that calls scrollIntoView({block: 'nearest'}) on the popover's outer div whenever `open` flips to true. block: 'nearest' scrolls just enough to make it visible without yanking the lane to the top. Discovered during Phase 9 QA. Reproduced at 1280x720; fix verified visually in the same QA run (screenshots in .gstack/qa-reports/phase9-*/). Co-Authored-By: Claude Opus 4.7 --- .../src/components/pilot/ResolutionNotePreview.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/frontend/src/components/pilot/ResolutionNotePreview.tsx b/frontend/src/components/pilot/ResolutionNotePreview.tsx index 600d3143..3a6ec8c4 100644 --- a/frontend/src/components/pilot/ResolutionNotePreview.tsx +++ b/frontend/src/components/pilot/ResolutionNotePreview.tsx @@ -9,7 +9,7 @@ * Kind switches the labels, button colors, and confirm-CTA text — the * underlying mechanics (preview fetch + edit + post) are identical. */ -import { useState, useEffect } from 'react' +import { useRef, useState, useEffect } from 'react' import { Loader2, RefreshCw, X, FileText, Pencil, Check, ArrowUpRight } from 'lucide-react' import { MarkdownContent } from '@/components/ui/MarkdownContent' import { cn } from '@/lib/utils' @@ -43,6 +43,7 @@ export function ResolutionNotePreview({ const [refreshing, setRefreshing] = useState(false) const [editing, setEditing] = useState(false) const [draft, setDraft] = useState('') + const popoverRef = useRef(null) // Keep the draft textarea in sync whenever fresh markdown arrives and we // aren't in the middle of editing. Once the engineer edits, their changes @@ -53,6 +54,15 @@ export function ResolutionNotePreview({ } }, [preview?.markdown, editing]) + // The popover renders at the bottom of TaskLane's scrollable region, which + // can leave it below the fold on smaller viewports. Scroll it into view + // whenever it opens so the engineer sees their preview immediately. + useEffect(() => { + if (open && popoverRef.current) { + popoverRef.current.scrollIntoView({ behavior: 'smooth', block: 'nearest' }) + } + }, [open]) + if (!open) return null const label = kind === 'resolve' ? 'Resolution note' : 'Escalation handoff package' @@ -73,7 +83,7 @@ export function ResolutionNotePreview({ } return ( -
+