fix: handle Unicode characters in clipboard copy/paste and add Paste menu option (#7103)

## Summary
Use TextEncoder/TextDecoder for UTF-8 safe base64 encoding/decoding

When copying nodes containing non-Latin1 characters (e.g., Chinese
characters in localized_name field), btoa() throws an error:

InvalidCharacterError: Failed to execute 'btoa' on 'Window': The string
to be encoded contains characters outside of the Latin1 range.

The copy operation saved data to localStorage successfully, but failed
to write to the browser clipboard. On paste, the browser clipboard still
contained old data, causing the wrong node to be pasted.

<!-- Fixes #ISSUE_NUMBER -->
https://github.com/Comfy-Org/ComfyUI_frontend/issues/6993
https://github.com/Comfy-Org/ComfyUI_frontend/issues/5449
https://github.com/comfyanonymous/ComfyUI/issues/8481

## Screenshots (if applicable)
before

https://github.com/user-attachments/assets/8abd9049-91bb-4200-8853-e26753376007

after

https://github.com/user-attachments/assets/7d969f32-bb0f-4c7a-baa2-65d576a4eba2

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-7103-fix-handle-Unicode-characters-in-clipboard-copy-paste-and-add-Paste-menu-option-2bd6d73d365081f39c40e7e7f832b97c)
by [Unito](https://www.unito.io)

---------

Co-authored-by: github-actions <github-actions@github.com>
This commit is contained in:
Terry Jia
2025-12-02 18:31:27 -05:00
committed by GitHub
parent c263111eeb
commit 379af28678
5 changed files with 124 additions and 5 deletions

View File

@@ -23,10 +23,16 @@ export const useCopy = () => {
const canvas = canvasStore.canvas
if (canvas?.selectedItems) {
const serializedData = canvas.copyToClipboard()
// Use TextEncoder to handle Unicode characters properly
const base64Data = btoa(
String.fromCharCode(
...Array.from(new TextEncoder().encode(serializedData))
)
)
// clearData doesn't remove images from clipboard
e.clipboardData?.setData(
'text/html',
clipboardHTMLWrapper.join(btoa(serializedData))
clipboardHTMLWrapper.join(base64Data)
)
e.preventDefault()
e.stopImmediatePropagation()

View File

@@ -14,9 +14,11 @@ function pasteClipboardItems(data: DataTransfer): boolean {
const match = rawData.match(/data-metadata="([A-Za-z0-9+/=]+)"/)?.[1]
if (!match) return false
try {
useCanvasStore()
.getCanvas()
._deserializeItems(JSON.parse(atob(match)), {})
// Decode UTF-8 safe base64
const binaryString = atob(match)
const bytes = Uint8Array.from(binaryString, (c) => c.charCodeAt(0))
const decodedData = new TextDecoder().decode(bytes)
useCanvasStore().getCanvas()._deserializeItems(JSON.parse(decodedData), {})
return true
} catch (err) {
console.error(err)

View File

@@ -8031,7 +8031,13 @@ export class LGraphCanvas
has_submenu: true,
callback: LGraphCanvas.onMenuAdd
},
{ content: 'Add Group', callback: LGraphCanvas.onGroupAdd }
{ content: 'Add Group', callback: LGraphCanvas.onGroupAdd },
{
content: 'Paste',
callback: () => {
this.pasteFromClipboard()
}
}
// { content: "Arrange", callback: that.graph.arrange },
// {content:"Collapse All", callback: LGraphCanvas.onMenuCollapseAll }
]