From 8f6690f8547173ee64f365fe6fe9848c80feb292 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Wed, 1 Apr 2026 16:29:16 +0000 Subject: [PATCH] Fix supervised approval command visibility Co-authored-by: Julius Marminge --- apps/web/src/components/ChatView.browser.tsx | 72 +++++++++++++++++++ apps/web/src/components/ChatView.tsx | 3 +- .../chat/ComposerPendingApprovalPanel.tsx | 19 +++++ 3 files changed, 92 insertions(+), 2 deletions(-) diff --git a/apps/web/src/components/ChatView.browser.tsx b/apps/web/src/components/ChatView.browser.tsx index 0e5f573d54..4e2b36c9db 100644 --- a/apps/web/src/components/ChatView.browser.tsx +++ b/apps/web/src/components/ChatView.browser.tsx @@ -573,6 +573,51 @@ function createSnapshotWithPendingUserInput(): OrchestrationReadModel { }; } +function createSnapshotWithPendingApproval(): OrchestrationReadModel { + const snapshot = createSnapshotForTargetUser({ + targetMessageId: "msg-user-pending-approval-target" as MessageId, + targetText: "approval thread", + }); + const approvalDetail = [ + '"C:\\WINDOWS\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" -Command', + '"ssh pipe-[REDACTED] \\"vdpkg -l localepurge 2>/dev/null || true; printf \\\\\\"\\\\n--- localepurge --\\\\n\\\\\\"; sed -n 1,80p /etc/locale.nopurge\\""', + ].join(" "); + + return { + ...snapshot, + threads: snapshot.threads.map((thread) => + thread.id === THREAD_ID + ? Object.assign({}, thread, { + runtimeMode: "approval-required", + activities: [ + { + id: EventId.makeUnsafe("activity-approval-requested"), + tone: "approval", + kind: "approval.requested", + summary: "Command approval requested", + payload: { + requestId: "req-browser-approval", + requestKind: "command", + detail: approvalDetail, + }, + turnId: null, + sequence: 1, + createdAt: isoAt(1_000), + }, + ], + session: { + ...thread.session, + runtimeMode: "approval-required", + status: "ready", + updatedAt: isoAt(1_000), + }, + updatedAt: isoAt(1_000), + }) + : thread, + ), + }; +} + function createSnapshotWithPlanFollowUpPrompt(): OrchestrationReadModel { const snapshot = createSnapshotForTargetUser({ targetMessageId: "msg-user-plan-follow-up-target" as MessageId, @@ -2602,6 +2647,33 @@ describe("ChatView timeline estimator parity (full app)", () => { } }); + it("renders pending approval commands in a readable wrapped block", async () => { + const mounted = await mountChatView({ + viewport: COMPACT_FOOTER_VIEWPORT, + snapshot: createSnapshotWithPendingApproval(), + }); + + try { + await waitForButtonByText("Approve once"); + const detail = await waitForElement( + () => document.querySelector('[data-testid="pending-approval-detail"]'), + "Unable to find pending approval detail block.", + ); + const detailStyle = getComputedStyle(detail); + + expect(detail.textContent).toContain("powershell.exe"); + expect(detail.textContent).toContain("localepurge"); + expect(detail.textContent).not.toContain("..."); + expect(detailStyle.whiteSpace).toBe("pre-wrap"); + expect(detailStyle.userSelect).toBe("text"); + expect(detailStyle.color).not.toBe("rgba(0, 0, 0, 0)"); + expect(detail.getBoundingClientRect().height).toBeGreaterThan(32); + expect(document.body.textContent).toContain("Resolve this approval request to continue"); + } finally { + await mounted.cleanup(); + } + }); + it("keeps plan follow-up footer actions fused and aligned after a real resize", async () => { const mounted = await mountChatView({ viewport: WIDE_FOOTER_VIEWPORT, diff --git a/apps/web/src/components/ChatView.tsx b/apps/web/src/components/ChatView.tsx index 76133712d4..8eeca6e91f 100644 --- a/apps/web/src/components/ChatView.tsx +++ b/apps/web/src/components/ChatView.tsx @@ -4016,8 +4016,7 @@ export default function ChatView({ threadId }: ChatViewProps) { onPaste={onComposerPaste} placeholder={ isComposerApprovalState - ? (activePendingApproval?.detail ?? - "Resolve this approval request to continue") + ? "Resolve this approval request to continue" : activePendingProgress ? "Type your own answer, or leave this blank to use the selected option" : showPlanFollowUpPrompt && activeProposedPlan diff --git a/apps/web/src/components/chat/ComposerPendingApprovalPanel.tsx b/apps/web/src/components/chat/ComposerPendingApprovalPanel.tsx index 569fd108a4..d7ebe15cc6 100644 --- a/apps/web/src/components/chat/ComposerPendingApprovalPanel.tsx +++ b/apps/web/src/components/chat/ComposerPendingApprovalPanel.tsx @@ -16,6 +16,12 @@ export const ComposerPendingApprovalPanel = memo(function ComposerPendingApprova : approval.requestKind === "file-read" ? "File-read approval requested" : "File-change approval requested"; + const detailLabel = + approval.requestKind === "command" + ? "Command" + : approval.requestKind === "file-read" + ? "Requested path" + : "Requested changes"; return (
@@ -26,6 +32,19 @@ export const ComposerPendingApprovalPanel = memo(function ComposerPendingApprova 1/{pendingCount} ) : null}
+ {approval.detail ? ( +
+

+ {detailLabel} +

+
+            {approval.detail}
+          
+
+ ) : null} ); });