diff --git a/ui/src/components/OperationPanel.jsx b/ui/src/components/OperationPanel.jsx index cbb6adf..a38c837 100644 --- a/ui/src/components/OperationPanel.jsx +++ b/ui/src/components/OperationPanel.jsx @@ -1219,18 +1219,69 @@ function ApplyModeChooser({ op, applyMode, setApplyMode, count }) { return } -function RequestPreview({ payload }) { +// The request as it will be sent. A 40rem-tall box in the corner of a docked +// panel could show about six lines of it, which is enough to see that there is +// JSON and not enough to read it, so it opens as a window of its own. +// +// It is the payload itself -- `submitOp` posts exactly what `buildPayload` +// returns, with no last-minute additions -- so what is shown here is what goes +// over the wire, minus the two things the browser never sees: the session that +// names `pf_user`, and the territory predicate the server ANDs on. +function RequestPreview({ payload, op, versionId }) { const [open, setOpen] = useState(false) + const [copied, setCopied] = useState(false) + + useEffect(() => { + if (!open) return + const onKey = (e) => { if (e.key === 'Escape') setOpen(false) } + window.addEventListener('keydown', onKey) + return () => window.removeEventListener('keydown', onKey) + }, [open]) + if (!payload) return null + const text = JSON.stringify(payload, null, 2) + const line = `POST /api/versions/${versionId}/${op}` + + // navigator.clipboard is undefined on a plain-http origin, which this app is + // served over, so the fallback is not decoration. + const copy = async () => { + try { + if (navigator.clipboard?.writeText) await navigator.clipboard.writeText(text) + else { + const ta = document.createElement('textarea') + ta.value = text + ta.style.position = 'fixed'; ta.style.opacity = '0' + document.body.appendChild(ta); ta.select() + document.execCommand('copy') + document.body.removeChild(ta) + } + setCopied(true); setTimeout(() => setCopied(false), 1500) + } catch { /* leave the text on screen to select by hand */ } + } + return (
- setOpen(o => !o)}> - {open ? '▾' : '▸'} request - + setOpen(true)}>▸ request + {open && ( -
-          {JSON.stringify(payload, null, 2)}
-        
+
setOpen(false)}> +
e.stopPropagation()}> +
+ Request + {line} +
+ {copied ? 'copied' : 'copy'} + +
+
+
+              {text}
+            
+
+
)}
) @@ -1408,7 +1459,7 @@ export default function OperationPanel({ {opBusy && writing rows…} - + )