generated from nhcarrigan/template
feat: add copy buttons to user and assistant messages (#78)
Closes #74 Reviewed-on: #78 Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com> Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
This commit was merged in pull request #78.
This commit is contained in:
@@ -155,6 +155,30 @@
|
||||
terminalElement.removeEventListener("copy", handleCopy);
|
||||
}
|
||||
});
|
||||
|
||||
// Copy message content to clipboard
|
||||
async function copyMessage(content: string) {
|
||||
try {
|
||||
await navigator.clipboard.writeText(content);
|
||||
// Optionally capture to clipboard history
|
||||
await clipboardStore.captureClipboard(content, null, "Message from chat");
|
||||
|
||||
// Visual feedback could be added here if needed
|
||||
} catch (error) {
|
||||
console.error("Failed to copy message:", error);
|
||||
}
|
||||
}
|
||||
|
||||
// State for showing "Copied!" feedback
|
||||
let copiedMessageId: string | null = null;
|
||||
|
||||
async function handleCopyMessage(messageId: string, content: string) {
|
||||
await copyMessage(content);
|
||||
copiedMessageId = messageId;
|
||||
setTimeout(() => {
|
||||
copiedMessageId = null;
|
||||
}, 2000);
|
||||
}
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -185,7 +209,7 @@
|
||||
</div>
|
||||
{:else}
|
||||
{#each lines as line (line.id)}
|
||||
<div class="terminal-line mb-2 {getLineClass(line.type)}">
|
||||
<div class="terminal-line mb-2 {getLineClass(line.type)} relative group">
|
||||
<span class="terminal-timestamp text-xs mr-2">{formatTime(line.timestamp)}</span>
|
||||
{#if getLinePrefix(line.type)}
|
||||
<span class="terminal-prefix mr-2">{getLinePrefix(line.type)}</span>
|
||||
@@ -194,10 +218,32 @@
|
||||
<span class="terminal-tool-name mr-2">[{line.toolName}]</span>
|
||||
{/if}
|
||||
{#if line.type === "assistant" || line.type === "user"}
|
||||
<Markdown
|
||||
content={maskPaths(line.content, hidePaths)}
|
||||
searchQuery={currentSearchQuery}
|
||||
/>
|
||||
<div class="message-content-wrapper">
|
||||
<Markdown
|
||||
content={maskPaths(line.content, hidePaths)}
|
||||
searchQuery={currentSearchQuery}
|
||||
/>
|
||||
<button
|
||||
class="copy-message-btn opacity-0 group-hover:opacity-100 transition-opacity"
|
||||
onclick={() => handleCopyMessage(line.id, line.content)}
|
||||
title="Copy message"
|
||||
>
|
||||
<svg
|
||||
width="14"
|
||||
height="14"
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
stroke-linecap="round"
|
||||
stroke-linejoin="round"
|
||||
>
|
||||
<rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
|
||||
<path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
|
||||
</svg>
|
||||
<span class="copy-text">{copiedMessageId === line.id ? "Copied!" : "Copy"}</span>
|
||||
</button>
|
||||
</div>
|
||||
{:else}
|
||||
<HighlightedText
|
||||
content={maskPaths(line.content, hidePaths)}
|
||||
@@ -267,4 +313,45 @@
|
||||
border-radius: 2px;
|
||||
padding: 0 2px;
|
||||
}
|
||||
|
||||
/* Message content wrapper for positioning */
|
||||
.message-content-wrapper {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* Copy button styling */
|
||||
.copy-message-btn {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4em;
|
||||
background: var(--bg-secondary);
|
||||
border: 1px solid var(--border-color);
|
||||
color: var(--text-secondary);
|
||||
padding: 0.25em 0.5em;
|
||||
cursor: pointer;
|
||||
border-radius: 4px;
|
||||
font-size: 0.85em;
|
||||
font-family: inherit;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.copy-message-btn:hover {
|
||||
background: var(--bg-hover);
|
||||
color: var(--text-primary);
|
||||
border-color: var(--border-hover, var(--border-color));
|
||||
}
|
||||
|
||||
.copy-message-btn svg {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* Ensure relative positioning for parent */
|
||||
.terminal-line {
|
||||
position: relative;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user