diff --git a/src/components/RecordPreview.tsx b/src/components/RecordPreview.tsx index 0677a12..1c86cef 100644 --- a/src/components/RecordPreview.tsx +++ b/src/components/RecordPreview.tsx @@ -364,11 +364,64 @@ function FieldPrimitive({ value }: { value: unknown }) { if (value === null) { return null; } - if (typeof value === 'string') { - const display = value.length > 280 ? `${value.substring(0, 280)}…` : value; - return <>{display}; + const full = typeof value === 'string' ? value : String(value); + const display = + typeof value === 'string' && value.length > 280 + ? `${value.substring(0, 280)}…` + : full; + return ; +} + +/** + * Tap-anywhere-to-copy wrapper for a primitive field value. Renders the + * text inline with a faint copy icon trailing it; clicking copies the + * full (untruncated) value to the clipboard and flashes a check mark. + */ +function CopyableValue({ display, copy }: { display: string; copy: string }) { + const [copied, setCopied] = useState(false); + async function onClick() { + await writeToClipboard(copy); + setCopied(true); + window.setTimeout(() => setCopied(false), 1400); + } + function onKeyDown(e: React.KeyboardEvent) { + if (e.key === 'Enter' || e.key === ' ') { + e.preventDefault(); + onClick(); + } } - return <>{String(value)}; + return ( +
+ {display} + + {copied ? : } + +
+ ); } function isExpandable(v: unknown): boolean {