From a7402a5500f303dfef6986200dd947becb548664 Mon Sep 17 00:00:00 2001 From: Forgotten Date: Thu, 26 Feb 2026 16:30:12 -0600 Subject: [PATCH] style(ui): restore rounding for buttons, comments, and company/project icons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Per feedback on PAP-186: containers should have hard edges but buttons, comment containers, project icons, and company icons should keep rounding. - Restore --radius-sm (6px) and --radius-md (8px) for buttons/inputs - Keep --radius-lg and --radius-xl at 0 for cards/containers/dialogs - Add rounded-sm to comment container divs in CommentThread - Replace rounded-xl with rounded-[14px] on company icons (CompanyRail, CompanySettings) since --radius-xl is 0 - Fix brand color dot in Sidebar (rounded → rounded-sm) Co-Authored-By: Claude Opus 4.6 --- ui/src/components/CommentThread.tsx | 278 ++++++++++++++++++++++------ ui/src/components/CompanyRail.tsx | 6 +- ui/src/components/Sidebar.tsx | 8 + ui/src/index.css | 4 +- ui/src/pages/CompanySettings.tsx | 214 +++++++++++++++------ 5 files changed, 384 insertions(+), 126 deletions(-) diff --git a/ui/src/components/CommentThread.tsx b/ui/src/components/CommentThread.tsx index 70c64d66..5988bf91 100644 --- a/ui/src/components/CommentThread.tsx +++ b/ui/src/components/CommentThread.tsx @@ -6,6 +6,7 @@ import { Paperclip } from "lucide-react"; import { Identity } from "./Identity"; import { MarkdownBody } from "./MarkdownBody"; import { MarkdownEditor, type MarkdownEditorRef, type MentionOption } from "./MarkdownEditor"; +import { StatusBadge } from "./StatusBadge"; import { formatDateTime } from "../lib/utils"; interface CommentWithRunMeta extends IssueComment { @@ -13,9 +14,28 @@ interface CommentWithRunMeta extends IssueComment { runAgentId?: string | null; } +interface LinkedRunItem { + runId: string; + status: string; + agentId: string; + createdAt: Date | string; + startedAt: Date | string | null; +} + +interface CommentReassignment { + assigneeAgentId: string | null; + assigneeUserId: string | null; +} + +interface ReassignOption { + value: string; + label: string; +} + interface CommentThreadProps { comments: CommentWithRunMeta[]; - onAdd: (body: string, reopen?: boolean) => Promise; + linkedRuns?: LinkedRunItem[]; + onAdd: (body: string, reopen?: boolean, reassignment?: CommentReassignment) => Promise; issueStatus?: string; agentMap?: Map; imageUploadHandler?: (file: File) => Promise; @@ -23,6 +43,8 @@ interface CommentThreadProps { onAttachImage?: (file: File) => Promise; draftKey?: string; liveRunSlot?: React.ReactNode; + enableReassign?: boolean; + reassignOptions?: ReassignOption[]; } const CLOSED_STATUSES = new Set(["done", "cancelled"]); @@ -56,22 +78,70 @@ function clearDraft(draftKey: string) { } } -export function CommentThread({ comments, onAdd, issueStatus, agentMap, imageUploadHandler, onAttachImage, draftKey, liveRunSlot }: CommentThreadProps) { +function parseReassignment(target: string): CommentReassignment | null { + if (!target) return null; + if (target === "__none__") { + return { assigneeAgentId: null, assigneeUserId: null }; + } + if (target.startsWith("agent:")) { + const assigneeAgentId = target.slice("agent:".length); + return assigneeAgentId ? { assigneeAgentId, assigneeUserId: null } : null; + } + if (target.startsWith("user:")) { + const assigneeUserId = target.slice("user:".length); + return assigneeUserId ? { assigneeAgentId: null, assigneeUserId } : null; + } + return null; +} + +type TimelineItem = + | { kind: "comment"; id: string; createdAtMs: number; comment: CommentWithRunMeta } + | { kind: "run"; id: string; createdAtMs: number; run: LinkedRunItem }; + +export function CommentThread({ + comments, + linkedRuns = [], + onAdd, + issueStatus, + agentMap, + imageUploadHandler, + onAttachImage, + draftKey, + liveRunSlot, + enableReassign = false, + reassignOptions = [], +}: CommentThreadProps) { const [body, setBody] = useState(""); const [reopen, setReopen] = useState(true); const [submitting, setSubmitting] = useState(false); const [attaching, setAttaching] = useState(false); + const [reassign, setReassign] = useState(false); + const [reassignTarget, setReassignTarget] = useState(""); const editorRef = useRef(null); const attachInputRef = useRef(null); const draftTimer = useRef | null>(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], - ); + const timeline = useMemo(() => { + const commentItems: TimelineItem[] = comments.map((comment) => ({ + kind: "comment", + id: comment.id, + createdAtMs: new Date(comment.createdAt).getTime(), + comment, + })); + const runItems: TimelineItem[] = linkedRuns.map((run) => ({ + kind: "run", + id: run.runId, + createdAtMs: new Date(run.startedAt ?? run.createdAt).getTime(), + run, + })); + return [...commentItems, ...runItems].sort((a, b) => { + if (a.createdAtMs !== b.createdAtMs) return a.createdAtMs - b.createdAtMs; + if (a.kind === b.kind) return a.id.localeCompare(b.id); + return a.kind === "comment" ? -1 : 1; + }); + }, [comments, linkedRuns]); // Build mention options from agent map (exclude terminated agents) const mentions = useMemo(() => { @@ -103,16 +173,26 @@ export function CommentThread({ comments, onAdd, issueStatus, agentMap, imageUpl }; }, []); + useEffect(() => { + if (enableReassign) return; + setReassign(false); + setReassignTarget(""); + }, [enableReassign]); + async function handleSubmit() { const trimmed = body.trim(); if (!trimmed) return; + const reassignment = reassign ? parseReassignment(reassignTarget) : null; + if (reassign && !reassignment) return; setSubmitting(true); try { - await onAdd(trimmed, isClosed && reopen ? true : undefined); + await onAdd(trimmed, isClosed && reopen ? true : undefined, reassignment ?? undefined); setBody(""); if (draftKey) clearDraft(draftKey); setReopen(false); + setReassign(false); + setReassignTarget(""); } finally { setSubmitting(false); } @@ -130,45 +210,85 @@ export function CommentThread({ comments, onAdd, issueStatus, agentMap, imageUpl } } + const canSubmit = !submitting && !!body.trim() && (!reassign || !!parseReassignment(reassignTarget)); + return (
-

Comments ({comments.length})

+

Comments & Runs ({timeline.length})

- {comments.length === 0 && ( -

No comments yet.

+ {timeline.length === 0 && ( +

No comments or runs yet.

)}
- {sorted.map((comment) => ( -
-
- {comment.authorAgentId ? ( - - - - ) : ( - - )} - - {formatDateTime(comment.createdAt)} - -
- {comment.body} - {comment.runId && comment.runAgentId && ( -
- - run {comment.runId.slice(0, 8)} - + {timeline.map((item) => { + if (item.kind === "run") { + const run = item.run; + return ( +
+
+ + + + + {formatDateTime(run.startedAt ?? run.createdAt)} + +
+
+ Run + + {run.runId.slice(0, 8)} + + +
- )} -
- ))} + ); + } + + const comment = item.comment; + return ( +
+
+ {comment.authorAgentId ? ( + + + + ) : ( + + )} + + {formatDateTime(comment.createdAt)} + +
+ {comment.body} + {comment.runId && ( +
+ {comment.runAgentId ? ( + + run {comment.runId.slice(0, 8)} + + ) : ( + + run {comment.runId.slice(0, 8)} + + )} +
+ )} +
+ ); + })}
{liveRunSlot} @@ -185,26 +305,62 @@ export function CommentThread({ comments, onAdd, issueStatus, agentMap, imageUpl contentClassName="min-h-[60px] text-sm" />
- {onAttachImage && ( - <> - - - + {(onAttachImage || enableReassign) && ( +
+ {onAttachImage && ( + <> + + + + )} + {enableReassign && ( +
+ + +
+ )} +
)} {isClosed && ( )} -
diff --git a/ui/src/components/CompanyRail.tsx b/ui/src/components/CompanyRail.tsx index 4509561c..f5c184eb 100644 --- a/ui/src/components/CompanyRail.tsx +++ b/ui/src/components/CompanyRail.tsx @@ -116,8 +116,8 @@ function SortableCompanyItem({ brandColor={company.brandColor} className={cn( isSelected - ? "rounded-xl" - : "rounded-[22px] group-hover:rounded-xl", + ? "rounded-[14px]" + : "rounded-[22px] group-hover:rounded-[14px]", isDragging && "shadow-lg", )} /> @@ -243,7 +243,7 @@ export function CompanyRail() { diff --git a/ui/src/components/Sidebar.tsx b/ui/src/components/Sidebar.tsx index 7c65d576..68e9b637 100644 --- a/ui/src/components/Sidebar.tsx +++ b/ui/src/components/Sidebar.tsx @@ -8,6 +8,7 @@ import { Search, SquarePen, Network, + Settings, } from "lucide-react"; import { useQuery } from "@tanstack/react-query"; import { SidebarSection } from "./SidebarSection"; @@ -46,6 +47,12 @@ export function Sidebar() {