Repository navigation
feat(clipboard): expose clipboard text to JavaScript - #121
Merged
Merged
Conversation
The runtime already owned the OS clipboard for copy and paste, but an app had
no way to call it. Add `writeToClipboard(text)` and `readFromClipboard()` to
`GpuixRenderer`, and `copyText(text)` to `@gpuix/react`.
Both calls are synchronous. Every desktop GPUI platform reads and writes the
platform clipboard directly (`Platform::read_from_clipboard_async` is only
overridden by the browser), so a promise would be a fake of an async read that
does not exist. On Windows and Linux the call runs on the UI thread and only
the JavaScript thread waits, the same path `cycleDebugFrameOverlay()` uses.
Text only. An empty string is a real entry, so `copyText('')` clears the
clipboard instead of no-opping, and reads back as `''`. `readFromClipboard()`
returns null when the clipboard holds no text, and a file list never becomes
paths.
Reading keeps an empty string, which `ClipboardItem::text()` cannot express.
`input.rs`'s paste helper moves to `crate::clipboard` so paste and the binding
share one definition.
The test renderer wraps `VisualTestPlatform`, whose clipboard is in memory, so
the tests never touch the system clipboard.
Cyberistic
marked this pull request as ready for review
October 25, 2026 04:20
`docs/gpuix-capabilities.md` repeated the README section and added a host capability table that nothing keeps in sync with the renderers. README is the API contract, so the clipboard semantics stay there. Also corrects the claim that no UI thread is involved: on Windows and Linux the call runs on the UI thread while JavaScript waits. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Apps can now read and write the OS clipboard. The runtime already had logic for
copy/paste inside native elements.
GpuixRenderer.writeToClipboard(text: string): voidGpuixRenderer.readFromClipboard(): string | nullcopyText(text: string): voidexported from@gpuix/reacthere is some AI slop:
Why sync
The issue asked for an async read if macOS reads asynchronously. It does not in
this submodule:
crates/gpui/src/platform.rs:326—Platform::read_from_clipboard_asyncdefaults to
Task::ready(Ok(self.read_from_clipboard())); the only overridein the tree is
gpui_web.crates/gpui_macos/src/platform.rs:1347—MacPlatform::read_from_clipboardreads
NSPasteboardunder a mutex, with no main-thread hop and noTask.custom_elements/input.rspaste already reads this way.So a
Promisewould fake an async read that does not exist, and blocking on aTaskfrom the app thread would be worse. The napi call is synchronous:GPUI_APP(with_app).UiCommandand wait on async_channelforthe UI thread's answer, the same pattern
cycleDebugFrameOverlay()uses. TheUI thread never blocks; only the JS thread waits, with the existing 2 s
timeout.
protocol, so a read returns only text this app wrote.
copyTextthrows.Semantics
ClipboardItem::new_string. Metadata/multi-entry is not exposed.''.readFromClipboard()returnsnullfor an empty clipboard, image-only, orfile-only content. A file list is never flattened into paths.
ClipboardItem::text()returnsNoneforempty text, so
crate::clipboard::textjoins string entries itself.input.rs's paste helper moved tocrate::clipboard::paste_text, so pasteand the binding share one definition and one test module.