Skip to content

Conversation

@ammar-agent
Copy link
Collaborator

@ammar-agent ammar-agent commented Dec 8, 2025

Summary

Adds a reviews system that allows users to create review notes from diff viewers, manage them via a collapsible banner, and send them to Claude with messages. Reviews are orthogonal to message type (works with both normal messages and auto-compaction).

Architecture

Data Flow:

  1. User selects code in diff viewer → clicks "+" → enters comment → ReviewNoteData created
  2. New reviews start with status: "attached" (auto-shows in ChatInput)
  3. User can detach (→ pending), check as done, or delete reviews
  4. On send: reviews formatted into message text + stored in muxMetadata for rich UI display

Key Types (src/common/types/):

  • ReviewNoteData: Structured data with filePath, lineRange, selectedCode, userNote
  • Review: Contains id, data, status ("pending" | "attached" | "checked"), timestamps
  • UserMessageContent: Shared type for normal send and continue-after-compaction (ensures reviews work with auto-compaction)
  • prepareUserMessageForSend(): Shared utility to format reviews → message text + metadata

Components:

  • useReviews hook: localStorage persistence per workspace with cross-component sync
  • ReviewsBanner: Collapsible banner above chat input showing pending/checked reviews
  • ReviewBlockFromData: Renders review with diff preview and editable comments
  • ChatInput: Shows attached reviews, handles send with reviews

Review State Machine

Created → Attached (auto)
Attached → Send message → Checked
Attached → Click X → Pending
Pending → "Send to chat" → Attached
Pending/Checked → Delete → Removed
Checked → Uncheck → Pending

Key Implementation Details

  • Orthogonal to auto-compaction: Reviews included in continueMessage so they survive compaction flow
  • No duplicate display: Reviews hidden from ChatInput during send operation
  • Wide viewport alignment: Banner content uses max-w-4xl mx-auto to align with chat
  • Error boundary: Corrupted localStorage data shows recovery UI instead of crashing

Stories

  • App/Reviews/ReviewsBanner - Multiple reviews in different states
  • App/Reviews/AllReviewsChecked - All completed state
  • App/Reviews/ManyReviews - Scroll behavior with many items

Generated with mux

@ammar-agent ammar-agent force-pushed the stateful-pending-reviews branch 5 times, most recently from ae38229 to 3f281ea Compare December 8, 2025 17:49
- Add PendingReview types (pending/checked status, timestamps)
- Add usePendingReviews hook for localStorage persistence per workspace
- Add PendingReviewsBanner component above chat input
  - Thin collapsible stripe showing pending count
  - Expand to see review list with check/send/remove actions
  - Toggle between pending and checked views
  - Clear all checked reviews option
  - Uses shadcn Button component with semantic Tailwind classes
- Review notes from diff viewer now queue to pending instead of direct chat
- Enable review notes on inline file_edit diffs in chat messages
  - DRY: reuses existing onReviewNote prop through message rendering chain
  - AIView -> MessageRenderer -> ToolMessage -> FileEditToolCall
- Reviews persist across sessions via localStorage
- Add Storybook stories for pending reviews feature
- Add ReviewNoteData interface for structured review data
- Add formatReviewNoteForChat() to format data when sending to chat
- Update PendingReview.data to hold ReviewNoteData instead of content string
- Update all onReviewNote callbacks throughout the callback chain
- Simplify getReviewSummary() to use review.data.userNote directly
- Remove string parsing regex from banner component
- Add BannerErrorBoundary class component to catch rendering errors
- Show 'Reviews data corrupted' message with clear button on error
- Add clearAll() method to usePendingReviews hook
- Wire up onClearAll prop through AIView

Handles old localStorage format gracefully by catching render errors
rather than requiring migration logic.
Major improvements to PendingReviewsBanner:
- Self-contained ConnectedPendingReviewsBanner - only needs workspaceId + chatInputAPI
- Full review display with expandable diff viewer and editable comments
- Pending reviews first, then completed reviews with 'show more' load
- Relative timestamps (ages) for each review
- updateReviewNote() method in usePendingReviews hook

Styled review rendering in chat:
- New shared ReviewBlock component in shared/ReviewBlock.tsx
- Reviews render as styled cards in UserMessage (not raw text)
- Live preview of reviews in ChatInput before sending
- Custom 'review' element handler in MarkdownComponents

Also removes ~10 props from AIView → PendingReviewsBanner wiring
- Track attached reviews separately from text input in ChatInput
- Add attachReview/detachReview/getAttachedReviews to ChatInputAPI
- Show attached reviews as styled blocks with X to remove above input
- Auto-mark reviews as completed when sent to chat
- Use text-primary for review comments (more readable)
- Pass filePath to DiffRenderer for syntax highlighting
- Rename ConnectedPendingReviewsBanner to PendingReviewsBanner

_Generated with mux_
@ammar-agent ammar-agent force-pushed the stateful-pending-reviews branch from cbe03f7 to 25327da Compare December 8, 2025 18:54
- Use draft construct for error recovery (no duplication)
- Hide attached reviews from banner to prevent double-send
- Move 'Send to chat' action left, away from delete
- Make 'Clear' button smaller (just text link)
- Grey chat icon when no pending reviews
- Reduce padding/margins in ReviewBlock for compact display
- Notify parent when attached reviews change

_Generated with mux_
@ammar-agent ammar-agent force-pushed the stateful-pending-reviews branch from 25327da to b25ea62 Compare December 8, 2025 18:57
…nly sends, auto-attach

- Move X remove button inside ReviewBlock header (within yellow borders)
- Add onRemove prop to ReviewBlock component for flexible usage
- Allow submitting messages with only attached reviews (no text required)
- Auto-attach reviews to chat input when created in Review panel
- Remove unused X import from ChatInput
- Simplify extractTextContent in MarkdownComponents (cleaner recursive impl)
- Add className prop to DiffRenderer for container styling override
- Remove rounding from code block in ReviewBlock (flush with borders)
- Increase max height of code preview from max-h-28 to max-h-64
- Show 10 lines at start and 10 at end (was 1 each) before eliding
- Add space-y-2 wrapper in ContentWithReviews for better spacing in sent messages
- Trim text segments to remove excess whitespace
- Increase ChatInput review preview max-height from max-h-36 to max-h-[50vh]
- Increase gap between reviews from space-y-1 to space-y-2
- Add inline editing to ReviewBlock with onEditComment prop
- Wire up onUpdateReviewNote callback from ChatInput to AIView
- Add pencil edit button that appears on hover over comment
- Add ReviewNoteDataForDisplay type to MuxFrontendMetadata for 'normal' messages
- Store structured review data when sending messages (avoids re-parsing from text)
- Add reviews field to DisplayedMessage user type
- Extract reviews from muxMetadata in StreamingMessageAggregator
- Add ReviewBlockFromData component that takes structured data directly
- Update UserMessage to prefer metadata-based display over text parsing
- Keep fallback parsing for legacy messages before metadata support
- Fix file path text readability (change from text-secondary to text-primary)

This eliminates the need for janky regex parsing when displaying user messages
that contain reviews. The text format is still sent to Claude for context,
but the UI now reads from structured metadata.
- Extract ReviewBlockCore as shared internal component
- ReviewBlock (parsing) and ReviewBlockFromData (structured) both use core
- Add SAVE_EDIT keybind to keybinds.ts (Ctrl/Cmd+Enter)
- Use matchesKeybind() for save/cancel in ReviewBlock and PendingReviewsBanner
- Pass maxHeight='none' to DiffRenderer in ReviewBlockCore to fix code preview
  (outer container's max-h-64 handles height constraints)

This eliminates ~130 lines of duplicated code between ReviewBlock variants
and ensures consistent keybind behavior across the app.
…orage

- Add 'attached' to PendingReviewStatus type (pending | attached | checked)
- New reviews start as 'attached' (auto-shows in chat input)
- Remove attachedReviewIds storage key (state lives on review itself)
- Simplify ChatInput: receives attachedReviews prop from parent
- Simplify PendingReviewsBanner: only needs workspaceId
- Remove legacy review parsing from UserMessage (metadata only)
- Remove ContentWithReviews/hasReviewBlocks/splitContentWithReviews
- Fix horizontal scroll: add min-w-0 to ReviewBlock, min-w-fit to DiffRenderer
- ChatInputAPI no longer has attachReview/detachReview/getAttachedReviews
- Move reviews to MuxFrontendMetadataBase (available on all types)
- Remove legacy parsing code from ReviewBlock.tsx (keep only ReviewBlockFromData)
- Remove review component from MarkdownComponents.tsx
- Fix review order: oldest first (newest at end of list)
- Simplify StreamingMessageAggregator reviews extraction
- Rename getPendingReviewsKey -> getReviewsKey (contains all review states)
- Rename PendingReviewsState -> ReviewsState
- Use updatePersistedState in storyHelpers instead of direct localStorage
Display first line of comment with ellipsis truncation in the
collapsed view of pending reviews banner.
- PendingReview -> Review
- PendingReviewStatus -> ReviewStatus
- usePendingReviews -> useReviews (file and hook)
- PendingReviewsBanner -> ReviewsBanner (file and component)
- UsePendingReviewsReturn -> UseReviewsReturn
- setPendingReviews -> setReviews, createPendingReview -> createReview
- Update all story names to remove 'Pending' prefix
@ammario ammario marked this pull request as ready for review December 8, 2025 20:33
Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

continueMessage: {
text: messageText,
imageParts,
model: sendMessageOptions.model,

P1 Badge Preserve review notes through auto-compaction

When auto-compaction triggers, the queued continueMessage only includes the trimmed text and image parts from the current draft and omits any attached review notes, so the follow-up message sent after compaction drops the user’s selected code/comments. Users hitting the compaction threshold with pending reviews will have their review content silently omitted from the actual message sent to the model. Include the attached reviews in the continueMessage payload so they survive the compaction flow.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

- Add UserMessageContent type shared between normal send and continue-after-compaction
- Add prepareUserMessageForSend() utility to format reviews into message text + metadata
- Update ContinueMessage to extend UserMessageContent (includes reviews field)
- Backend now processes reviews in continueMessage using shared utility
- ChatInput auto-compaction path now includes attached reviews in continueMessage
- Remove duplicate formatReviewNoteForChat from review.ts

This ensures reviews work correctly regardless of whether auto-compaction triggers.
- Remove duplicate ReviewNoteDataForDisplay interface (identical to ReviewNoteData)
- Export ReviewNoteDataForDisplay as type alias to ReviewNoteData for backwards compat
- Move formatReviewForModel to review.ts (single source of truth)
- Import and use shared function in message.ts
@ammario ammario merged commit 831d6b8 into main Dec 8, 2025
17 of 19 checks passed
@ammario ammario deleted the stateful-pending-reviews branch December 8, 2025 21:09
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants