Skip to content

Commit 0500afd

Browse files
authored
Merge pull request #179 from sourcery-ai/ben/sou-1442-feat-copyinsert-buttons-on-markdown-chat-blocks
feat: add a copy button to each code block in a chat assistant message
2 parents ddd7b89 + 8201e91 commit 0500afd

File tree

3 files changed

+102
-34
lines changed

3 files changed

+102
-34
lines changed

media/chat.css

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -92,7 +92,7 @@
9292
}
9393

9494
.sidebar__chat-assistant--chat-bubble-text {
95-
margin: 0;
95+
margin: var(--spacing-sm);
9696
color: var(--vscode-settings-textInputForeground);
9797
}
9898

@@ -105,7 +105,7 @@
105105
background: transparent;
106106
}
107107

108-
.sidebar__chat-assistant--chat-bubble-text *:not(:last-child) {
108+
.sidebar__chat-assistant--chat-bubble-text > *:not(:last-child) {
109109
margin-bottom: var(--spacing-sm);
110110
}
111111

@@ -127,25 +127,54 @@
127127
/*padding-left: 1.5em;*/
128128
}
129129

130-
.sidebar__chat-assistant--chat-bubble-text *:not(:last-child) {
131-
margin-bottom: var(--spacing-sm);
132-
}
133-
134130
.sidebar__chat-assistant--chat-bubble-text code {
135131
font-family: var(--vscode-editor-font-family);
136132
font-size: var(--vscode-sidebar-font-size);
137133
}
138134

139135
.sidebar__chat-assistant--chat-bubble-text pre {
140136
margin: 0;
141-
overflow-x: auto;
142137
width: 100%;
143138
background-color: var(--vscode-editor-background);
144139
border: 1px solid var(--vscode-panel-border);
145140
border-radius: var(--spacing-xs);
141+
position: relative;
146142
}
147143

148-
.sidebar__chat-assistant--chat-bubble-text pre code {
144+
button.sidebar__chat-assistant--chat-bubble-text--code-copy-button {
145+
visibility: hidden;
146+
background: var(--vscode-button-secondaryBackground);
147+
border: 1px solid var(--vscode-panel-border);
148+
border-radius: var(--spacing-xxs);
149+
position: absolute;
150+
display: block;
151+
top: var(--spacing-sm);
152+
right: var(--spacing-sm);
153+
color: var(--vscode-settings-textInputForeground);
154+
padding: var(--spacing-md);
155+
fill: var(--vscode-button-secondaryForeground);
156+
margin: 0;
157+
width: var(--spacing-lg);
158+
height: var(--spacing-lg);
159+
outline: none;
160+
}
161+
162+
.sidebar__chat-assistant--code-block-action-button-icon {
163+
height: var(--spacing-lg);
164+
width: var(--spacing-lg);
165+
position: absolute;
166+
left: var(--spacing-xxs);
167+
top: var(--spacing-xxs);
168+
}
169+
170+
button.sidebar__chat-assistant--chat-bubble-text--code-copy-button:hover {
171+
background-color: var(--vscode-button-secondaryHoverBackground);
172+
}
173+
174+
.sidebar__chat-assistant--chat-bubble-text
175+
pre:hover
176+
button.sidebar__chat-assistant--chat-bubble-text--code-copy-button {
177+
visibility: visible;
149178
}
150179

151180
.sidebar__chat-assistant--code-block {

src/chat.ts

Lines changed: 26 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -252,32 +252,32 @@ export class ChatProvider implements vscode.WebviewViewProvider {
252252
253253
</head>
254254
<body class="sidebar__chat-assistant-body">
255-
<section id="message-container" class="sidebar__section-container active" data-section="chat-assistant">
256-
<ul class="sidebar__chat-assistant--dialogue-container">
257-
258-
<li id="anchor"></li>
259-
</ul>
260-
</section>
261-
<footer class="sidebar__chat-assistant--footer">
262-
<section class="sidebar__chat-assistant--textarea-container">
263-
<button id="cancel-button" class="sidebar__chat-assistant--cancel-button" disabled>
264-
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" class="sidebar__chat-assistant--regenerate-button-icon">
265-
<path d="m464 256a208 208 0 1 0 -416 0 208 208 0 1 0 416 0zm-464 0a256 256 0 1 1 512 0 256 256 0 1 1 -512 0zm224-72v144c0 13.3-10.7 24-24 24s-24-10.7-24-24v-144c0-13.3 10.7-24 24-24s24 10.7 24 24zm112 0v144c0 13.3-10.7 24-24 24s-24-10.7-24-24v-144c0-13.3 10.7-24 24-24s24 10.7 24 24z"></path>
266-
</svg>
267-
<span>Cancel</span>
268-
</button>
269-
<textarea class="sidebar__chat-assistant--textarea" placeholder="Type your message here!"
270-
id="user-prompt"></textarea>
271-
<button class="sidebar__chat-assistant--textarea-send-button sidebar__textarea-send-button--disabled"
272-
id="send-button">
273-
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"
274-
class="sidebar__chat-assistant--textarea-send-icon">
275-
<path
276-
d="m498.1 5.6c10.1 7 15.4 19.1 13.5 31.2l-64 416c-1.5 9.7-7.4 18.2-16 23s-18.9 5.4-28 1.6l-119.6-49.7-68.5 74.1c-8.9 9.7-22.9 12.9-35.2 8.1s-20.3-16.7-20.3-29.9v-83.6c0-4 1.5-7.8 4.2-10.7l167.6-182.9c5.8-6.3 5.6-16-.4-22s-15.7-6.4-22-.7l-203.4 180.7-88.3-44.2c-10.6-5.3-17.4-15.9-17.7-27.7s5.9-22.8 16.1-28.7l448-256c10.7-6.1 23.9-5.5 34 1.4z" />
277-
</svg>
278-
</button>
279-
</section>
280-
</footer>
255+
<section id="message-container" class="sidebar__section-container active" data-section="chat-assistant">
256+
<ul class="sidebar__chat-assistant--dialogue-container">
257+
258+
<li id="anchor"></li>
259+
</ul>
260+
</section>
261+
<footer class="sidebar__chat-assistant--footer">
262+
<section class="sidebar__chat-assistant--textarea-container">
263+
<button id="cancel-button" class="sidebar__chat-assistant--cancel-button" disabled>
264+
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg" class="sidebar__chat-assistant--regenerate-button-icon">
265+
<path d="m464 256a208 208 0 1 0 -416 0 208 208 0 1 0 416 0zm-464 0a256 256 0 1 1 512 0 256 256 0 1 1 -512 0zm224-72v144c0 13.3-10.7 24-24 24s-24-10.7-24-24v-144c0-13.3 10.7-24 24-24s24 10.7 24 24zm112 0v144c0 13.3-10.7 24-24 24s-24-10.7-24-24v-144c0-13.3 10.7-24 24-24s24 10.7 24 24z"></path>
266+
</svg>
267+
<span>Cancel</span>
268+
</button>
269+
<textarea class="sidebar__chat-assistant--textarea" placeholder="Type your message here!"
270+
id="user-prompt"></textarea>
271+
<button class="sidebar__chat-assistant--textarea-send-button sidebar__textarea-send-button--disabled"
272+
id="send-button">
273+
<svg viewBox="0 0 512 512" xmlns="http://www.w3.org/2000/svg"
274+
class="sidebar__chat-assistant--textarea-send-icon">
275+
<path
276+
d="m498.1 5.6c10.1 7 15.4 19.1 13.5 31.2l-64 416c-1.5 9.7-7.4 18.2-16 23s-18.9 5.4-28 1.6l-119.6-49.7-68.5 74.1c-8.9 9.7-22.9 12.9-35.2 8.1s-20.3-16.7-20.3-29.9v-83.6c0-4 1.5-7.8 4.2-10.7l167.6-182.9c5.8-6.3 5.6-16-.4-22s-15.7-6.4-22-.7l-203.4 180.7-88.3-44.2c-10.6-5.3-17.4-15.9-17.7-27.7s5.9-22.8 16.1-28.7l448-256c10.7-6.1 23.9-5.5 34 1.4z" />
277+
</svg>
278+
</button>
279+
</section>
280+
</footer>
281281
</body>
282282
<script nonce="${nonce}" src="${scriptUri}"></script>
283283
</html>`;

src/webview/chat.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,42 @@ const LINE_HEIGHT = 36;
142142
}
143143
}
144144

145+
const setupCopyButton = (block) => {
146+
if (navigator.clipboard) {
147+
let text = block.querySelector("code").innerText;
148+
let button = document.createElement("button");
149+
button.innerHTML = `
150+
<svg
151+
viewBox="0 0 512 512"
152+
xmlns="http://www.w3.org/2000/svg"
153+
class="sidebar__chat-assistant--code-block-action-button-icon"
154+
>
155+
<path d="m272 0h124.1c12.7 0 24.9 5.1 33.9 14.1l67.9 67.9c9 9 14.1 21.2 14.1 33.9v220.1c0 26.5-21.5 48-48 48h-192c-26.5 0-48-21.5-48-48v-288c0-26.5 21.5-48 48-48zm-224 128h144v64h-128v256h192v-32h64v48c0 26.5-21.5 48-48 48h-224c-26.5 0-48-21.5-48-48v-288c0-26.5 21.5-48 48-48z" />
156+
</svg>
157+
`;
158+
button.title = "Copy to Clipboard";
159+
button.classList.add(
160+
"sidebar__chat-assistant--chat-bubble-text--code-copy-button"
161+
);
162+
block.appendChild(button);
163+
button.onclick = async () => {
164+
await navigator.clipboard.writeText(text);
165+
button.innerHTML = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" class="sidebar__chat-assistant--code-block-action-button-icon"><!--! Font Awesome Free 6.4.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. --><path d="M438.6 105.4c12.5 12.5 12.5 32.8 0 45.3l-256 256c-12.5 12.5-32.8 12.5-45.3 0l-128-128c-12.5-12.5-12.5-32.8 0-45.3s32.8-12.5 45.3 0L160 338.7 393.4 105.4c12.5-12.5 32.8-12.5 45.3 0z"/></svg>`;
166+
};
167+
button.onblur = async () => {
168+
button.innerHTML = `
169+
<svg
170+
viewBox="0 0 512 512"
171+
xmlns="http://www.w3.org/2000/svg"
172+
class="sidebar__chat-assistant--code-block-action-button-icon"
173+
>
174+
<path d="m272 0h124.1c12.7 0 24.9 5.1 33.9 14.1l67.9 67.9c9 9 14.1 21.2 14.1 33.9v220.1c0 26.5-21.5 48-48 48h-192c-26.5 0-48-21.5-48-48v-288c0-26.5 21.5-48 48-48zm-224 128h144v64h-128v256h192v-32h64v48c0 26.5-21.5 48-48 48h-224c-26.5 0-48-21.5-48-48v-288c0-26.5 21.5-48 48-48z" />
175+
</svg>
176+
`;
177+
};
178+
}
179+
};
180+
145181
// Function to add an assistant message or add to the existing one
146182
function addAssistantMessageToUI(message) {
147183
cancelButton.disabled = false;
@@ -152,6 +188,9 @@ const LINE_HEIGHT = 36;
152188

153189
const replaceCurrentAssistantMessage = () => {
154190
currentAssistantMessage.innerHTML = message.textContent;
191+
192+
let blocks = currentAssistantMessage.querySelectorAll("pre");
193+
blocks.forEach(setupCopyButton);
155194
};
156195

157196
if (currentAssistantMessage != null && message.outcome !== "error") {

0 commit comments

Comments
 (0)