feat(cockpit): approval preview — command/diff above Approve/Reject (W1)
Remote one-tap approval was blind (you'd tap Approve without seeing Claude wants to run `rm -rf` or rewrite a config). The pending tool's actual command / diff now renders above the approval bar, on every attached device, riding the same broadcast + late-joiner rails as the existing `gate` field. - src/http/approval-preview.ts (new, pure, never-throws): deriveApprovalPreview — Bash → command; Edit/Write/MultiEdit/NotebookEdit → a synthetic DiffFile; else null. Every line sanitized via sanitizeField (strips control/ANSI); caps 40 lines / 200 chars/line / 4KB (security limits, UTF-8-safe byte clamp). - src/types.ts: additive optional `preview?: ApprovalPreview` on the status ServerMessage + handleHookEvent (older clients ignore it). - src/server.ts /hook/permission: derive preview from tool_input, store on the PendingApproval entry, re-send to late joiners exactly like `gate`. - manager.ts threads it; public/tabs.ts renderApprovalPreview (command → <pre> textContent; diff → reused innerHTML-free renderDiffFile). Unknown tools / plan gates fall back to today's name-only bar. Attacker-influenced tool input → rendered via textContent/diff-renderer only, never innerHTML. Verified independently: typecheck + build:web clean, 1692 pass.
This commit is contained in:
@@ -1086,6 +1086,7 @@ body {
|
||||
inset: auto 0 calc(var(--keybar-h) + var(--safe-b)) 0;
|
||||
z-index: 1050;
|
||||
display: flex;
|
||||
flex-wrap: wrap; /* W1: a preview row wraps below the label + buttons */
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
padding: 10px 14px;
|
||||
@@ -1097,6 +1098,31 @@ body {
|
||||
.approval-label {
|
||||
flex: 1 1 auto;
|
||||
}
|
||||
/* W1: approval preview (command/diff) — its own full-width row, scrollable. */
|
||||
.approval-preview {
|
||||
flex: 1 1 100%;
|
||||
order: 3; /* below the label + buttons regardless of insertion order */
|
||||
max-height: 30vh;
|
||||
overflow: auto;
|
||||
overflow-x: auto;
|
||||
border: 1px solid rgba(245, 177, 76, 0.35);
|
||||
border-radius: 8px;
|
||||
background: rgba(0, 0, 0, 0.35);
|
||||
padding: 8px 10px;
|
||||
}
|
||||
.approval-cmd {
|
||||
margin: 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
font-family: Menlo, Consolas, monospace;
|
||||
font-size: 13px;
|
||||
color: #f4f5f7;
|
||||
}
|
||||
.approval-truncated {
|
||||
margin-top: 6px;
|
||||
font-size: 12px;
|
||||
opacity: 0.7;
|
||||
}
|
||||
#approvalbar button {
|
||||
flex: none;
|
||||
border: none;
|
||||
|
||||
Reference in New Issue
Block a user