@@ -16,9 +16,15 @@ import {
1616 type ReactNode ,
1717} from "react" ;
1818import { LegendList , type LegendListRef } from "@legendapp/list/react" ;
19+ import { FileDiff } from "@pierre/diffs/react" ;
1920import { deriveTimelineEntries , formatElapsed } from "../../session-logic" ;
2021import { type TurnDiffSummary } from "../../types" ;
2122import { summarizeTurnDiffStats } from "../../lib/turnDiffTree" ;
23+ import {
24+ getRenderablePatch ,
25+ resolveDiffThemeName ,
26+ resolveFileDiffPath ,
27+ } from "../../lib/diffRendering" ;
2228import ChatMarkdown from "../ChatMarkdown" ;
2329import {
2430 BotIcon ,
@@ -67,6 +73,11 @@ import {
6773} from "./userMessageTerminalContexts" ;
6874import { SkillInlineText } from "./SkillInlineText" ;
6975import { formatWorkspaceRelativePath } from "../../filePathDisplay" ;
76+ import {
77+ buildReviewCommentRenderablePatch ,
78+ parseReviewCommentMessageSegments ,
79+ type ReviewCommentContext ,
80+ } from "../../reviewCommentContext" ;
7081
7182// ---------------------------------------------------------------------------
7283// Context — shared state consumed by every row component via Context.
@@ -837,6 +848,25 @@ const UserMessageBody = memo(function UserMessageBody(props: {
837848 terminalContexts : ParsedTerminalContextEntry [ ] ;
838849 skills : ReadonlyArray < Pick < ServerProviderSkill , "name" | "displayName" > > ;
839850} ) {
851+ const reviewCommentSegments = parseReviewCommentMessageSegments ( props . text ) ;
852+ if ( reviewCommentSegments . some ( ( segment ) => segment . kind === "review-comment" ) ) {
853+ return (
854+ < div className = "space-y-3 text-sm leading-relaxed text-foreground" >
855+ { reviewCommentSegments . map ( ( segment ) =>
856+ segment . kind === "text" ? (
857+ segment . text . trim ( ) . length > 0 ? (
858+ < div key = { segment . id } className = "whitespace-pre-wrap wrap-break-word" >
859+ < SkillInlineText text = { segment . text . trim ( ) } skills = { props . skills } />
860+ </ div >
861+ ) : null
862+ ) : (
863+ < UserMessageReviewCommentCard key = { segment . comment . id } comment = { segment . comment } />
864+ ) ,
865+ ) }
866+ </ div >
867+ ) ;
868+ }
869+
840870 if ( props . terminalContexts . length > 0 ) {
841871 const hasEmbeddedInlineLabels = textContainsInlineTerminalContextLabels (
842872 props . text ,
@@ -930,6 +960,49 @@ const UserMessageBody = memo(function UserMessageBody(props: {
930960 ) ;
931961} ) ;
932962
963+ function UserMessageReviewCommentCard ( { comment } : { comment : ReviewCommentContext } ) {
964+ const ctx = use ( TimelineRowCtx ) ;
965+ const renderablePatch = getRenderablePatch (
966+ buildReviewCommentRenderablePatch ( comment ) ,
967+ `review-comment:${ comment . id } ` ,
968+ ) ;
969+
970+ return (
971+ < div className = "space-y-2 rounded-lg border border-border/70 bg-background/70 p-3" >
972+ < div className = "space-y-1" >
973+ < div className = "text-xs font-medium text-foreground" >
974+ { formatWorkspaceRelativePath ( comment . filePath , ctx . workspaceRoot ) }
975+ </ div >
976+ < div className = "text-[11px] text-muted-foreground" >
977+ { comment . sectionTitle } · { comment . rangeLabel }
978+ </ div >
979+ </ div >
980+ { comment . text . length > 0 && (
981+ < div className = "whitespace-pre-wrap wrap-break-word text-sm" >
982+ < SkillInlineText text = { comment . text } skills = { ctx . skills } />
983+ </ div >
984+ ) }
985+ { renderablePatch ?. kind === "files" &&
986+ renderablePatch . files . map ( ( fileDiff ) => (
987+ < FileDiff
988+ key = { resolveFileDiffPath ( fileDiff ) }
989+ fileDiff = { fileDiff }
990+ options = { {
991+ collapsed : false ,
992+ diffStyle : "unified" ,
993+ theme : resolveDiffThemeName ( ctx . resolvedTheme ) ,
994+ } }
995+ />
996+ ) ) }
997+ { renderablePatch ?. kind === "raw" && (
998+ < pre className = "overflow-x-auto rounded-md bg-muted/40 p-2 text-xs" >
999+ { renderablePatch . text }
1000+ </ pre >
1001+ ) }
1002+ </ div >
1003+ ) ;
1004+ }
1005+
9331006// ---------------------------------------------------------------------------
9341007// Structural sharing — reuse old row references when data hasn't changed
9351008// so LegendList (and React) can skip re-rendering unchanged items.
0 commit comments