Skip to content

Commit 2964388

Browse files
cacosub7qiaozhuoyueellipsis-dev[bot]dcbartlett
authored
feat: add copy code button to mermaid diagrams(RooCodeInc#2129) (RooCodeInc#2758)
* feat: add copy code button to mermaid diagrams(RooCodeInc#2129) - Added copy button to MermaidBlock component - Improved loading message text - Ensure image buffer type safety with explicit Uint8Array conversion * Apply suggestions from code review Enhance accessibility by adding an aria-label description to the "Copy Code" button . Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> * Update webview-ui/src/components/common/MermaidBlock.tsx Add try/catch or handle promise rejection to provide feedback on copy failures. Co-authored-by: Dennis Bartlett <[email protected]> * lint code after use CR suggestions * fix: add async/await for clipboard operation handling - Fixes RooCodeInc#2129 --------- Co-authored-by: qiaozhuoyue <[email protected]> Co-authored-by: ellipsis-dev[bot] <65095814+ellipsis-dev[bot]@users.noreply.github.com> Co-authored-by: Dennis Bartlett <[email protected]>
1 parent 6fcd435 commit 2964388

File tree

3 files changed

+64
-3
lines changed

3 files changed

+64
-3
lines changed

.changeset/gorgeous-rice-judge.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"claude-dev": minor
3+
---
4+
5+
Added copy button to MermaidBlock component

src/integrations/misc/open-file.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ export async function openImage(dataUri: string) {
1313
const imageBuffer = Buffer.from(base64Data, "base64")
1414
const tempFilePath = path.join(os.tmpdir(), `temp_image_${Date.now()}.${format}`)
1515
try {
16-
await vscode.workspace.fs.writeFile(vscode.Uri.file(tempFilePath), imageBuffer)
16+
await vscode.workspace.fs.writeFile(vscode.Uri.file(tempFilePath), new Uint8Array(imageBuffer))
1717
await vscode.commands.executeCommand("vscode.open", vscode.Uri.file(tempFilePath))
1818
} catch (error) {
1919
vscode.window.showErrorMessage(`Error opening image: ${error}`)

webview-ui/src/components/common/MermaidBlock.tsx

Lines changed: 58 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import mermaid from "mermaid"
33
import { useDebounceEffect } from "@/utils/useDebounceEffect"
44
import styled from "styled-components"
55
import { vscode } from "@/utils/vscode"
6+
import { VSCodeButton } from "@vscode/webview-ui-toolkit/react"
67

78
const MERMAID_THEME = {
89
background: "#1e1e1e", // VS Code dark theme background
@@ -139,11 +140,22 @@ export default function MermaidBlock({ code }: MermaidBlockProps) {
139140
}
140141
}
141142

143+
const handleCopyCode = async () => {
144+
try {
145+
await navigator.clipboard.writeText(code)
146+
} catch (err) {
147+
console.error("Copy failed", err)
148+
}
149+
}
150+
142151
return (
143152
<MermaidBlockContainer>
144153
{isLoading && <LoadingMessage>Generating mermaid diagram...</LoadingMessage>}
145-
146-
{/* The container for the final <svg> or raw code. */}
154+
<ButtonContainer>
155+
<StyledVSCodeButton onClick={handleCopyCode} title="Copy Code" aria-label="Copy Code">
156+
<span className="codicon codicon-copy"></span>
157+
</StyledVSCodeButton>
158+
</ButtonContainer>
147159
<SvgContainer onClick={handleClick} ref={containerRef} $isLoading={isLoading} />
148160
</MermaidBlockContainer>
149161
)
@@ -209,6 +221,19 @@ const MermaidBlockContainer = styled.div`
209221
margin: 8px 0;
210222
`
211223

224+
const ButtonContainer = styled.div`
225+
position: absolute;
226+
top: 8px;
227+
right: 8px;
228+
z-index: 1;
229+
opacity: 0.6;
230+
transition: opacity 0.2s ease;
231+
232+
&:hover {
233+
opacity: 1;
234+
}
235+
`
236+
212237
const LoadingMessage = styled.div`
213238
padding: 8px 0;
214239
color: var(--vscode-descriptionForeground);
@@ -228,3 +253,34 @@ const SvgContainer = styled.div<SvgContainerProps>`
228253
display: flex;
229254
justify-content: center;
230255
`
256+
257+
const StyledVSCodeButton = styled(VSCodeButton)`
258+
padding: 4px;
259+
height: 24px;
260+
width: 24px;
261+
min-width: unset;
262+
background-color: var(--vscode-button-secondaryBackground);
263+
color: var(--vscode-button-secondaryForeground);
264+
border: 1px solid var(--vscode-button-border);
265+
border-radius: 3px;
266+
display: flex;
267+
align-items: center;
268+
justify-content: center;
269+
transition: all 0.2s ease;
270+
271+
.codicon {
272+
font-size: 14px;
273+
}
274+
275+
&:hover {
276+
background-color: var(--vscode-button-secondaryHoverBackground);
277+
border-color: var(--vscode-button-border);
278+
transform: translateY(-1px);
279+
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
280+
}
281+
282+
&:active {
283+
transform: translateY(0);
284+
box-shadow: none;
285+
}
286+
`

0 commit comments

Comments
 (0)