FE-674: Side-chat polish + DiffPopover + saved-toast move#121
FE-674: Side-chat polish + DiffPopover + saved-toast move#121kostandinang wants to merge 2 commits into
Conversation
PR SummaryMedium Risk Overview Polishes the side-chat composer and staged-changes strip to match updated vocabulary and chrome ("Annotate"→"Note", edit-mode strip + placeholder changes, icon-only Apply/Undo/Discard, per-row kind chips) and persists side-chat edit mode in Relocates the "Change saved" toast out of Reviewed by Cursor Bugbot for commit d6e8500. Bugbot is set up for automated code reviews on this repo. Configure here. |
🤖 Augment PR SummarySummary: This PR delivers Card 4 UI polish for side-chat and pending-review, and introduces a reusable Changes:
Technical Notes: 🤖 Was this summary useful? React with 👍 or 👎 |
dfd595a to
d0aa725
Compare
5c3a70c to
7e4d0d8
Compare
d0aa725 to
e2d1fef
Compare
7e4d0d8 to
98c902c
Compare
e2d1fef to
4e3e4ed
Compare
98c902c to
1caa9f8
Compare
| <PatchListProvider appliers={appliers}> | ||
| <SideChatHost specificationId={specificationState.specification.id}> | ||
| <div className="flex h-full min-h-0 flex-1 flex-col"> | ||
| <PatchListOverlay /> |
There was a problem hiding this comment.
PatchListOverlay lost on non-graph specification routes
High Severity
PatchListOverlay was removed from route.tsx (the layout wrapping all /specification/$id/* child routes) and placed only inside StructuredListView, which is rendered exclusively by the graph route. Interview-phase views (grounding, elicitation, acceptance-review, requirements-review) and the export view still have SideChatHost and can stage/apply patches, but no longer render PatchListOverlay. Since the "Change saved" toast was also removed from the side-chat popover in this same PR, users on non-graph routes receive zero post-apply feedback and lose the Undo affordance entirely.
Additional Locations (1)
Reviewed by Cursor Bugbot for commit 4e3e4ed. Configure here.
4e3e4ed to
cd79c3f
Compare
81a4822 to
d36f61a
Compare
0cfc320 to
6280efb
Compare
d36f61a to
1f26ae7
Compare
…cation
Card 4 (Figma-aligned polish) + DiffPopover primitive + saved-toast surface relocation.
Polish (S1-S4):
- Vocabulary: 'Annotate' → 'Note', 'add to chat context' → 'Add to context',
composer placeholders, 'Apply N change(s)' / 'Undo last change' aria.
- Side-chat composer: + note + Notes(N) move into the input card's left action
row; Edit-mode strip below the input card with kind-accent toggle pill;
top-right header buttons 24×24 → 20×20.
- Staged-patches strip: per-row layout with kind chip + '↗ view diff' chip +
impact chip + discard; footer 28×28 icon-only Undo + Apply; Saving status
inline next to Apply.
- Pending-review section: strip bg (255,219,168,0.18); '[Replace] N pending
reviews' header; per-row 2px neutral-amber left bar; kind chip with
supersedes/confirm icon; raw '#ID · {target excerpt}' title; 'from #ID was
edited [↗ view source diff]' sub-line; icon-only Edit + solid kind-accent
Resolve action row; inline edit form wrapped in kind-accent-tinted card.
- ItemEditTextarea: drop shadow-card + border-rule; use kindAccent-derived
border + 2px focus ring; icon-only Cancel; Save inline kind-accent fill.
DiffPopover primitive (NEW):
- Anchored, viewport-aware popover wrapping <ContentDiff>. Read-only v1;
props { open, onClose, anchor, before, after, title, kindChip?, kindAccent? }.
- Renders into document.body via createPortal so the popover escapes the
side-chat dialog's transformed/clipped ancestors.
- Prefers BELOW the anchor (keeps the chip in view); flips ABOVE only when
there isn't room. Right-aligns with anchor; reposition on scroll/resize.
- ESC + click-outside close. Slice 7's auto-edit Apply/Skip can extend
additively via a future footer? slot.
- Wired into both side-chat-popover staged-patches and pending-review rows.
Saved-toast / Undo relocation:
- Removed inline composer 'Change saved' overlay from side-chat-popover (no
longer covers the input row).
- Moved <PatchListOverlay /> out of the page-level top bar (route.tsx) into
-structured-list-view.tsx, just above <PendingReviewSection /> (under the
kind chips). Dropped sticky top-0 z-30 so the overlay sits in flow rather
than on top of the structured-list nodes.
- Saved-toast banner gains animate-in fade-in slide-in-from-top-1 + a small
zoom-in emerald check badge.
- Toast trigger broadened from 'applied has data' to 'new lastBatchId + not
applying + stagedCount=0' so annotate batches (applied=undefined) also
surface 'Change saved'.
Persistence (Card 4 follow-up):
- side-chat-host.tsx persists Edit-mode toggle in localStorage
(brunch.side-chat.mode); a new pinned item adopts the stored mode rather
than falling back to 'explore'.
Color system:
- pending-review-section: kind-relevant chrome (kind chip, row left bar) keeps
amber; action affordances (Resolve, Save, edit form) use the product's
primary blue (#3484fa). The neutral-amber row left bar remains a v1 stand-in
until target_item_kind enrichment lands on the listing endpoint.
Tests:
- New diff-popover.test.tsx (9 tests).
- side-chat-popover.test.tsx: relabeled assertions; replaced FE-665 inline
<details> expander tests with popover-trigger tests; deleted saved-toast
lifecycle suite (lifecycle now lives in patch-list-overlay).
- side-chat-host.test.tsx: mounted <PatchListOverlay /> in tests that exercise
Undo / saved-toast surfaces; cleared localStorage in afterEach so persisted
Edit-mode doesn't leak between tests; deleted obsolete 'does not leak the
saved confirmation to another pinned item' assertion (toast is now global).
- structured-list-view.test.tsx: mounted <PatchListOverlay /> + stubbed
useSpecificationOpenReconciliationNeeds to fix 2 inherited failures.
- patch-list-overlay.test.tsx: removed Pending-review composition assertion
(PendingReviewSection now renders inside structured-list view).
- pending-review-section.test.tsx: source-diff-popover tests replace inline
ContentDiff tests; +1 Loader2 spinner test.
memory/CARDS.md: Card 4 marked done.
Verify: 1139 passed, 0 failed; oxlint + oxfmt clean; vite build clean.
Disable inline edit-target textarea/Cancel/Save while the row is also resolving, so a click can't fire Save mid-Resolve and overlap mutations. Stop mutating textarea.style.boxShadow imperatively in onFocus/onBlur — React rerenders during typing restored the inline style and dropped the intended 2px focus ring. Both PendingReview and structured-list inline edit textareas now express the focus ring via CSS focus state, using inline CSS variables for the kind/action accent so the ring color stays dynamic. Drop the fallback 'target #id' string when target_current_content is null — the row title already prefixes #id, so the fallback produced '#id · target #id'. Now the row shows just '#id' when no content. Constrain DiffPopover to max-h-[min(70vh,40rem)] with an internal overflow-y-auto body region so the header (with close button) stays reachable on large diffs that exceeded the viewport.
6280efb to
d6e8500
Compare
1f26ae7 to
c5bb7ef
Compare
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
There are 2 total unresolved issues (including 1 from previous review).
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit d6e8500. Configure here.
| // the toast renders conditionally on canUndo so hard applies show "Change | ||
| // saved" without an undo affordance. | ||
| if (hasApply && !state.isApplying && stagedCount === 0) { | ||
| if (!state.isApplying && stagedCount === 0) { |
There was a problem hiding this comment.
Saved toast re-fires when navigating between sibling views
Medium Severity
Moving PatchListOverlay from route.tsx (always mounted) into -structured-list-view.tsx (one child view) causes lastSeenBatchIdRef to reset to null whenever the user navigates to a sibling route like graph.tsx or export.tsx and back. On remount, the effect sees state.lastBatchId (persisted in PatchListProvider above Outlet) as a "new" batch and re-fires the saved toast, even though the user already saw it. The staged-changes bar also loses its expanded state on each navigation.
Additional Locations (2)
Reviewed by Cursor Bugbot for commit d6e8500. Configure here.



What
Figma-aligned polish across the side-chat + Pending review surfaces. New
DiffPopoverprimitive. Saved-toast moved out of the page top bar so it doesn't cover the input.Stacked on #120.
Visual + interaction
+ note+Notes(N)moved into the input card's action row; Edit-mode strip below the input with a kind-accent toggle pill.↗ view diff+ impact chip + discard.[Replace] N pending reviewsheader; per-row 2px amber left bar;from #ID was edited [↗ view source diff]sub-line; icon-only Edit + solid Resolve.New
DiffPopoverprimitiveAnchored, viewport-aware popover wrapping
<ContentDiff>. Portals intobodyso it escapes the side-chat dialog's clipping ancestors. Prefers below the anchor, flips above when there's no room. Internal scroll on large diffs. ESC + click-outside close.Saved-toast
Misc
Test plan
↗ view difffrom a few rows; popover scrolls internally on large diffs