FE-656: V1.2-E β Floating selection menu and active-card chat context#93
Conversation
PR SummaryMedium Risk Overview Introduces active annotation cards interleaved into the side-chat thread, with dismiss/promotion controls and an 8-item cap for what gets sent as Plumbs Reviewed by Cursor Bugbot for commit 66665bc. Bugbot is set up for automated code reviews on this repo. Configure here. |
FE-656 Side chat
Side chat β graph-launched chat with patch-list stagingTL;DRProblem / Motivation
Acceptance criteria
ConceptPopover-to-panel chat anchored to items in the structured spec view. Two entry modes (per-row The side-chat is the unified user-driven mutation surface. It subsumes three previously-separate horizon items: D128 graph-launched refinement, trigger-popover composer, revisit / edit mode + cascade preview. Phasing
Acceptance-criteria mapping
Design doc & PR
Updates |
This stack of pull requests is managed by Graphite. Learn more about stacking. |
π€ Augment PR SummarySummary: Implements V1.2-E βhighlight-to-noteβ for side-chat by letting users select text in the structured list view and either chat about it (one-shot hint) or instantly save it as an annotation that becomes prompt context. Changes:
Technical Notes: Side-chat state is now session-scoped (active cards + pending span hint reset on item switch/dismiss), and prompt context is capped to avoid request/prompt bloat. π€ Was this summary useful? React with π or π |
54727e5 to
9fbf42a
Compare
Merge activity
|
275f434 to
673feef
Compare
8dbbb48 to
292b62a
Compare
673feef to
daea777
Compare
β¦a, constant - threadItems: messages and cards now both timestamp via Date.now() (parallel messageTimestamps array on ActiveSideChat), so the chronological sort actually interleaves; previously messages used `index` and cards used Date.now() so cards always landed after messages regardless of when they were created. Pending assistant timestamps are preserved across replacePendingText / finalizePending. - dismiss now clears activeCards and pendingSpanHint; openFor clears them when the target item differs from the current pinned (kind, id) pair. Prevents single-pin state from leaking across close/reopen and item switches. - annotate applier returns the server-normalized summary/body (created.summary, created.body) under `applied`, so the rendered card matches what was persisted rather than the patch the client sent. - Extracted MAX_ACTIVE_ANNOTATIONS = 8 used in both the request-payload slice and the threadItems inContext flag, so the cap has a single source of truth. Tests added: dismiss-clears-cards (close then reopen same item) and item-switch-clears-cards (open A β save β open B). annotation-api `applied` test rewritten to assert the server values, not the patch values. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
β¦the composer Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
β¦ canUndo Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Replaces center-pill positioning (left-1/2 + translateX) with anchored left-9 right-9 so the toast bar fills the available area between the + and send buttons. Content stays centered inside via justify-between (check + label on the left, Undo on the right). Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
β¦em heights - Put h-72 directly on the <ul> for guaranteed scroll (the flex-1 + min-h-0 pattern wasn't propagating height through the absolute-positioned wrapper). - Drop px-2 from the <ul> so each item spans the full popover width and the hover background paints edge-to-edge. - Move horizontal padding inside each item (px-3 on summary/div); body section uses pl-7 to stay aligned with summary text after the chevron. - Tighten vertical rhythm: py-2.5 β py-2 between items. - Bump body text size to text-sm with leading-relaxed and pt-1 spacing for comfortable reading when an item is expanded. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
a44c6bb to
33b2082
Compare
Join applied annotation metadata back to its source patch so cards are only promoted for the currently pinned item, and keep openFor state updaters pure. Co-authored-by: Cursor <cursoragent@cursor.com>
See the output of git range-diff at https://github.com/hashintel/brunch/actions/runs/25488347398
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 2 potential issues.
β Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 66665bc. Configure here.
| setActiveSideChat((active) => | ||
| active && active.itemKind === item.kind && active.itemId === item.id ? nextActiveSideChat : active, | ||
| ); | ||
| return; |
There was a problem hiding this comment.
Same-item openFor discards pending state from streaming
Medium Severity
When openFor is called for the already-open item, nextActiveSideChat is built from activeRef.current (which lags behind queued state updates). The functional setActiveSideChat callback receives the up-to-date active state but ignores it, returning nextActiveSideChat instead. During streaming, queued replacePendingText updates are silently discarded. The next streaming delta self-corrects (because buffered accumulates all text), but intermediate message state and messageTimestamps can briefly regress.
Reviewed by Cursor Bugbot for commit 66665bc. Configure here.
| } | ||
| let cancelled = false; | ||
| const batchId = patchListState.lastBatchId; | ||
| setAnnotations(null); |
There was a problem hiding this comment.
Annotations cleared during refetch causes drawer flash
Low Severity
setAnnotations(null) is called eagerly at the start of every annotation refetch (triggered by lastBatchId changes). This immediately clears existingAnnotations to an empty array, causing the notes drawer count to flash to zero and back on every successful annotation save. If the drawer is open, the annotation list visibly disappears and reappears after the fetch resolves.
Reviewed by Cursor Bugbot for commit 66665bc. Configure here.



Summary
Implements V1.2-E from
memory/PLAN.mdActive 1.π¬ Chat/π Annotate) appears when text is highlighted inside a knowledge item's content or rationale in the graph view.π Annotateinstantly creates a span-anchored annotation (DB-persisted withselection_start/end) and lands as an interleaved quote-card in the side-chat thread. Active cards feed the LLM as prompt context (User-pinned snippets:block) on subsequent turns, capped at the 8 most-recent.π¬ Chatopens the side-chat with a one-shotspanHint. A dismissable chip (π Β«phraseΒ») surfaces above the composer until the next message is sent or the chip is cleared. No DB write.+and send), with 5-second auto-dismiss.Test plan
Chat/AnnotateAnnotateβ side-chat opens, quote card lands in thread, notes drawer count increments[Γ]β card disappears from thread, drawer entry remains, next prompt omits itChatβ chip appears above composer; next message includes `spanHint`; chip clears after sendSpec / plan
π€ Generated with Claude Code