diff --git a/ui/src/components/Message.tsx b/ui/src/components/Message.tsx index 65196db94fe0436f11f3daa1ddc02b4915d01856..da5ce603dd24c4e899d6a82b078f001114b8bbe5 100644 --- a/ui/src/components/Message.tsx +++ b/ui/src/components/Message.tsx @@ -31,85 +31,200 @@ interface MessageProps { onCommentTextChange?: (text: string) => void; } -function Message({ message, onOpenDiffViewer, onCommentTextChange }: MessageProps) { - // Hide system messages from the UI - if (message.type === "system") { +// Copy icon for the commit hash copy button +const CopyIcon = () => ( + + + + +); + +const CheckIcon = () => ( + + + +); + +// GitInfoMessage renders a compact git state notification +function GitInfoMessage({ + message, + onOpenDiffViewer, +}: { + message: MessageType; + onOpenDiffViewer?: (commit: string) => void; +}) { + const [copied, setCopied] = useState(false); + + // Parse user_data which contains structured git state info + let commitHash: string | null = null; + let subject: string | null = null; + let branch: string | null = null; + let worktree: string | null = null; + + if (message.user_data) { + try { + const userData = + typeof message.user_data === "string" ? JSON.parse(message.user_data) : message.user_data; + if (userData.commit) { + commitHash = userData.commit; + } + if (userData.subject) { + subject = userData.subject; + } + if (userData.branch) { + branch = userData.branch; + } + if (userData.worktree) { + worktree = userData.worktree; + } + } catch (err) { + console.error("Failed to parse gitinfo user_data:", err); + } + } + + if (!commitHash) { return null; } - // Render gitinfo messages as compact status updates - if (message.type === "gitinfo") { - // Parse user_data which contains structured git state info - let commitHash: string | null = null; - let subject: string | null = null; - let branch: string | null = null; - - if (message.user_data) { - try { - const userData = - typeof message.user_data === "string" ? JSON.parse(message.user_data) : message.user_data; - if (userData.commit) { - commitHash = userData.commit; - } - if (userData.subject) { - subject = userData.subject; - } - if (userData.branch) { - branch = userData.branch; - } - } catch (err) { - console.error("Failed to parse gitinfo user_data:", err); - } + const canShowDiff = commitHash && onOpenDiffViewer; + + const handleDiffClick = () => { + if (commitHash && onOpenDiffViewer) { + onOpenDiffViewer(commitHash); } + }; - if (!commitHash) { - return null; + const handleCopyHash = (e: React.MouseEvent) => { + e.preventDefault(); + if (commitHash) { + navigator.clipboard.writeText(commitHash).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 1500); + }); } + }; - const canShowDiff = commitHash && onOpenDiffViewer; + // Truncate subject if too long + const truncatedSubject = subject && subject.length > 40 ? subject.slice(0, 37) + "..." : subject; - const handleDiffClick = () => { - if (commitHash && onOpenDiffViewer) { - onOpenDiffViewer(commitHash); - } - }; + return ( +
+ + {worktree && ( + + {worktree} + + )} + {branch && ( + + {branch} + + )} + {branch ? " now at " : "now at "} + + {commitHash} + + + {truncatedSubject && ( + + "{truncatedSubject}" + + )} + {canShowDiff && ( + <> + {" "} + { + e.preventDefault(); + handleDiffClick(); + }} + style={{ + color: "var(--link-color, #0066cc)", + textDecoration: "underline", + }} + > + diff + + + )} + +
+ ); +} - return ( -
- - {branch} now at {commitHash} - {subject && ` "${subject}"`} - {canShowDiff && ( - <> - {" "} - { - e.preventDefault(); - handleDiffClick(); - }} - style={{ - color: "var(--link-color, #0066cc)", - textDecoration: "underline", - }} - > - diff - - - )} - -
- ); +function Message({ message, onOpenDiffViewer, onCommentTextChange }: MessageProps) { + // Hide system messages from the UI + if (message.type === "system") { + return null; + } + + // Render gitinfo messages as compact status updates + if (message.type === "gitinfo") { + return ; } // Action bar state (show on hover or tap)