mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-23 17:33:51 +02:00
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 <noreply@paperclip.ing>
This commit is contained in:
1 parent
1c3abf5075
commit
2a8a99e4a5
2 files changed
+59
-13
No files matched your search
@@ -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(
|
||||
<MemoryRouter>
|
||||
<IssueChatThread
|
||||
comments={[]}
|
||||
linkedRuns={[run]}
|
||||
timelineEvents={[]}
|
||||
liveRuns={[]}
|
||||
onAdd={async () => {}}
|
||||
showComposer={false}
|
||||
enableLiveTranscriptPolling={false}
|
||||
transcriptsByRunId={issueChatLongThreadTranscriptsByRunId}
|
||||
hasOutputForRun={() => true}
|
||||
/>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
});
|
||||
|
||||
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({
|
||||
|
||||
@@ -2659,19 +2659,18 @@ function IssueChatAssistantMessage({
|
||||
<span className="text-xs text-muted-foreground/60">
|
||||
{chainOfThoughtLabel?.toLowerCase()}
|
||||
</span>
|
||||
<span className="ml-auto flex items-center gap-1.5">
|
||||
{message.createdAt ? (
|
||||
<span className="text-(length:--text-micro) text-muted-foreground/50">
|
||||
{commentDateLabel(message.createdAt)}
|
||||
</span>
|
||||
) : null}
|
||||
<ChevronDown
|
||||
className={cn(
|
||||
"h-3.5 w-3.5 text-muted-foreground/40 transition-transform",
|
||||
!folded && "rotate-180",
|
||||
)}
|
||||
/>
|
||||
</span>
|
||||
<ChevronDown
|
||||
aria-hidden="true"
|
||||
className={cn(
|
||||
"h-3.5 w-3.5 shrink-0 text-muted-foreground/40 opacity-0 transition-[opacity,transform] group-hover:opacity-100 group-focus-visible:opacity-100",
|
||||
!folded && "rotate-180",
|
||||
)}
|
||||
/>
|
||||
{message.createdAt ? (
|
||||
<span className="ml-auto text-(length:--text-micro) text-muted-foreground/50">
|
||||
{commentDateLabel(message.createdAt)}
|
||||
</span>
|
||||
) : null}
|
||||
</button>
|
||||
) : (
|
||||
<div className="mb-1.5 flex items-center gap-2">
|
||||
|
||||
Reference in new issue
Block a user