From 2a8a99e4a5f69aa803b3f10b982f583e75a87042 Mon Sep 17 00:00:00 2001 From: Dotta <34892728+cryppadotta@users.noreply.github.com> Date: Sat, 3 Oct 2026 12:41:22 -0500 Subject: [PATCH] fix(ui): reveal completed thinking caret beside label (#15048) ## Thinking Path > - Paperclip helps people manage AI agents and their work. > - The task thread shows completed agent work and its reasoning. > - The completed work row places the disclosure caret at the far right. > - The caret stays visible even when the reader does not inspect the row. > - The row needs a quiet control that stays close to the work label. > - This pull request places the caret after that label and shows it on hover or keyboard focus. > - The change keeps the timestamp on the right and does not move content on hover. ## Linked Issues or Issue Description **What happened?** The completed agent work row shows a disclosure caret at the far right of the task thread. The caret stays visible when the pointer is elsewhere. **Expected behavior** The caret stays near the work label. It appears on hover or keyboard focus. The timestamp stays on the right. **Steps to reproduce** 1. Open a task with a completed agent run. 2. Look at the completed work row without hovering over it. 3. Hover over the row and then use the keyboard to focus its button. Related: #11772 addresses a different caret and composer layout. ## What Changed - Move the completed work caret next to the work label. - Keep its space reserved. Show it on hover or keyboard focus. - Add a test for caret position, visibility classes, and the open state. ## Verification - `node node_modules/vitest/vitest.mjs run ui/src/components/IssueChatThread.test.tsx` passes all 100 tests. - `node scripts/check-token-gates.mjs` passes all token gates. - `git diff origin/master...HEAD --check` passes. - CI passes on the latest head, including typecheck, build, tests, and verification. The local isolated worktree could not run typecheck because dependency installation failed while applying the existing `codex-acp` patch. ## Risks - Low risk. The same button still opens the work detail. Only the caret position and visibility change. - On a touch device, the caret is not visible without hover. The work row stays a button that users can tap. > This is a focused UI fix. It does not add a planned core feature from `ROADMAP.md`. ## Model Used - OpenAI Codex coding agent. The runner does not expose the exact model ID or context window. The agent used reasoning, shell tools, and code execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used with the details available in this runner - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either linked public issues or described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal ticket ID - [x] I have run focused tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have checked documentation impact; no documentation change is needed for this visual fix - [x] I have considered and documented the risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip --- ui/src/components/IssueChatThread.test.tsx | 47 ++++++++++++++++++++++ ui/src/components/IssueChatThread.tsx | 25 ++++++------ 2 files changed, 59 insertions(+), 13 deletions(-) diff --git a/ui/src/components/IssueChatThread.test.tsx b/ui/src/components/IssueChatThread.test.tsx index 384fa952a3d..72d4dd8968a 100644 --- a/ui/src/components/IssueChatThread.test.tsx +++ b/ui/src/components/IssueChatThread.test.tsx @@ -4813,6 +4813,53 @@ describe("IssueChatThread", () => { }); }); + it("keeps the completed chain-of-thought caret beside its label and reveals it on hover or focus", () => { + const root = createRoot(container); + const run = issueChatLongThreadLinkedRuns[2]; + + act(() => { + root.render( + + {}} + showComposer={false} + enableLiveTranscriptPolling={false} + transcriptsByRunId={issueChatLongThreadTranscriptsByRunId} + hasOutputForRun={() => true} + /> + , + ); + }); + + const header = Array.from(container.querySelectorAll("button")).find((button) => + button.textContent?.includes("worked for 4 minutes"), + ); + expect(header).toBeDefined(); + const label = Array.from(header!.children).find((child) => + child.textContent?.includes("worked for 4 minutes"), + ); + const caret = label?.nextElementSibling; + expect(caret?.tagName.toLowerCase()).toBe("svg"); + expect(header?.classList.contains("group")).toBe(true); + expect(caret?.classList.contains("opacity-0")).toBe(true); + expect(caret?.classList.contains("group-hover:opacity-100")).toBe(true); + expect(caret?.classList.contains("group-focus-visible:opacity-100")).toBe(true); + expect(caret?.nextElementSibling?.classList.contains("ml-auto")).toBe(true); + + act(() => { + header!.click(); + }); + expect(caret?.classList.contains("rotate-180")).toBe(true); + + act(() => { + root.unmount(); + }); + }); + it("folds chain-of-thought when the same message transitions from running to complete", () => { expect( resolveAssistantMessageFoldedState({ diff --git a/ui/src/components/IssueChatThread.tsx b/ui/src/components/IssueChatThread.tsx index f04a25585a9..4a9aef03b1f 100644 --- a/ui/src/components/IssueChatThread.tsx +++ b/ui/src/components/IssueChatThread.tsx @@ -2659,19 +2659,18 @@ function IssueChatAssistantMessage({ {chainOfThoughtLabel?.toLowerCase()} - - {message.createdAt ? ( - - {commentDateLabel(message.createdAt)} - - ) : null} - - +