import { useMemo, useRef, useState } from "react"; import { Link } from "react-router-dom"; import Markdown from "react-markdown"; import type { IssueComment, Agent } from "@paperclip/shared"; import { Button } from "@/components/ui/button"; import { Identity } from "./Identity"; import { MarkdownEditor, type MarkdownEditorRef, type MentionOption } from "./MarkdownEditor"; import { formatDateTime } from "../lib/utils"; interface CommentWithRunMeta extends IssueComment { runId?: string | null; runAgentId?: string | null; } interface CommentThreadProps { comments: CommentWithRunMeta[]; onAdd: (body: string, reopen?: boolean) => Promise; issueStatus?: string; agentMap?: Map; } const CLOSED_STATUSES = new Set(["done", "cancelled"]); export function CommentThread({ comments, onAdd, issueStatus, agentMap }: CommentThreadProps) { const [body, setBody] = useState(""); const [reopen, setReopen] = useState(true); const [submitting, setSubmitting] = useState(false); const editorRef = useRef(null); const isClosed = issueStatus ? CLOSED_STATUSES.has(issueStatus) : false; // Display oldest-first const sorted = useMemo( () => [...comments].sort((a, b) => new Date(a.createdAt).getTime() - new Date(b.createdAt).getTime()), [comments], ); // Build mention options from agent map const mentions = useMemo(() => { if (!agentMap) return []; return Array.from(agentMap.values()).map((a) => ({ id: a.id, name: a.name, })); }, [agentMap]); async function handleSubmit() { const trimmed = body.trim(); if (!trimmed) return; setSubmitting(true); try { await onAdd(trimmed, isClosed && reopen ? true : undefined); setBody(""); setReopen(false); } finally { setSubmitting(false); } } return (

Comments ({comments.length})

{comments.length === 0 && (

No comments yet.

)}
{sorted.map((comment) => (
{formatDateTime(comment.createdAt)}
{comment.body}
{comment.runId && comment.runAgentId && (
run {comment.runId.slice(0, 8)}
)}
))}
{isClosed && ( )}
); }