From 0cf33695d31081e37ce220d8ecc81f1815a5448a Mon Sep 17 00:00:00 2001 From: Forgotten Date: Fri, 20 Feb 2026 13:35:15 -0600 Subject: [PATCH] feat: use markdown editor with @-mentions for issue comments - Replaced plain textarea in CommentThread with MarkdownEditor for rich markdown editing (no toolbar, compact styling) - Added @-mention autocomplete to MarkdownEditor: - Detects @ trigger while typing with cursor-positioned dropdown - Arrow key navigation, Enter/Tab to select, Escape to dismiss - Filters mentionable names as user types after @ - Added onSubmit prop to MarkdownEditor for Cmd/Ctrl+Enter support - Agents from the company are passed as mentionable options Co-Authored-By: Claude Opus 4.6 --- ui/src/components/CommentThread.tsx | 40 +++--- ui/src/components/MarkdownEditor.tsx | 207 ++++++++++++++++++++++++++- 2 files changed, 227 insertions(+), 20 deletions(-) diff --git a/ui/src/components/CommentThread.tsx b/ui/src/components/CommentThread.tsx index 992db42f..f0adb0ca 100644 --- a/ui/src/components/CommentThread.tsx +++ b/ui/src/components/CommentThread.tsx @@ -1,10 +1,10 @@ -import { useMemo, useState } from "react"; +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 { Textarea } from "@/components/ui/textarea"; import { Identity } from "./Identity"; +import { MarkdownEditor, type MarkdownEditorRef, type MentionOption } from "./MarkdownEditor"; import { formatDate } from "../lib/utils"; interface CommentWithRunMeta extends IssueComment { @@ -25,6 +25,7 @@ export function CommentThread({ comments, onAdd, issueStatus, agentMap }: Commen 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; @@ -34,8 +35,16 @@ export function CommentThread({ comments, onAdd, issueStatus, agentMap }: Commen [comments], ); - async function handleSubmit(e?: React.FormEvent) { - e?.preventDefault(); + // 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; @@ -90,18 +99,15 @@ export function CommentThread({ comments, onAdd, issueStatus, agentMap }: Commen ))} -
-