Summary
In the web viewer, the clipboard can currently only be transferred with the Send Clipboard and Receive Clipboard buttons in the input popover. Keyboard shortcuts are forwarded as plain keys, so Ctrl+C copies to the remote clipboard and Ctrl+V pastes from it, but nothing ever reaches the viewer's own clipboard. Users coming from RDP expect copy and paste to just work.
I've implemented this and attached it as a patch (git am-ready), based on main at ece9542 (v0.28.2.0). I don't have a GitHub fork, so I'm sending it this way — feel free to take it as-is, change it, or use it only as a reference.
Behavior
Paste (Ctrl+V): the key-down is no longer prevented, so the browser raises a paste event carrying the local clipboard text. That text is sent to the remote device — but only if it changed since the last sync — and only then is the held key-down forwarded. Sending it unconditionally would overwrite text the user had just copied on the remote device with the mouse (context menu), and the paste would insert something else than expected. A "Clipboard sent" snackbar confirms the transfer after it succeeds.
Taking the text from the paste event rather than navigator.clipboard.readText() matters: readText() needs the clipboard-read permission and fails with NotAllowedError on every site where the user hasn't granted it, while the paste event needs no permission and never prompts.
Copy / cut (Ctrl+C, Ctrl+X): after the key is forwarded, the remote clipboard is requested following a short delay (300 ms), so the remote application has time to update it. The existing clipboard handler writes it locally and shows "Received clipboard text".
Both directions:
- record the same device access activities as the buttons (
send_clipboard_text / receive_clipboard_text),
- respect view-only mode and the
CanReadClipboard / CanWriteClipboard permissions,
- ignore shortcuts combined with Alt or Meta (AltGr on many European layouts is Ctrl+Alt),
- show errors in a snackbar and never break key handling.
Settings
- New user preference
is-clipboard-shortcut-sync-enabled, enabled by default, with a checkbox on the Settings page.
- A per-session switch in the input popover under "Clipboard", initialized from the preference — the same pattern as the keyboard input mode.
- Preferences are stored as name/value pairs, so no migration is needed.
ControlR.Web.Server_internal.json is regenerated by the build.
Key event ordering
Sending the clipboard before the Ctrl+V key-down is asynchronous. Key-down events were awaited but key-up events were not, so a key-up could reach the remote device before its key-down. Key events now go through a small promise queue in RemoteDisplay.razor.js. The Ctrl+V key-down is held until its paste event, or until its key-up if no paste event arrives.
Tests
- The logic lives in a new
ClipboardShortcutSync service with 20 new tests (27 cases) in ControlR.Web.Client.Tests: copy shortcut detection, permissions, the per-session switch, unchanged/blank clipboard text, retry after a failed send, the copy delay (with FakeTimeProvider), cancellation, error handling and activity recording. Paste shortcut detection and the key queue live in RemoteDisplay.razor.js, which has no test harness, so they were verified manually.
UserPreferencesManagerTests updated for the new preference.
ControlR.Web.Client.Tests: 156 passed. ControlR.Web.Server.Tests: 903 passed. ControlR.Web.Server builds with 0 warnings.
Manual testing: done on our own build of 0.27.6.0 with this change backported, in a Chromium-based browser, against a real remote device and without an RDP session to it (RDP syncs the clipboard on its own and would mask the result):
- Ctrl+C and Ctrl+X on the remote device put the text on the local clipboard.
- Ctrl+V pastes the local clipboard on the remote device, on a site where the clipboard-read permission was never granted.
- Text copied on the remote device with the mouse is still what Ctrl+V pastes there when the local clipboard hasn't changed.
- Typing, AltGr combinations and pasting into input fields on the ControlR page itself are unaffected.
- The per-session switch and the Settings default both work.
Known limitations
- The 300 ms copy delay is a heuristic. If a remote application updates its clipboard later, the previous text is received and the Receive Clipboard button still works as before. The viewer logs whether the received text changed, which helps tune this.
- Text only, same as the existing buttons.
- Web viewer only; the Avalonia viewer is untouched.
- Ctrl-based shortcuts only; Cmd on a macOS viewer isn't handled.
Applying the patch
Unzip the attachment and run:
Contributor License Agreement
I have read and agree to the Contribution License Agreement in .github/pull_request_template.md, and it applies to this submission.
The change was written with the help of Claude Code (Anthropic), noted in the commit trailer.
clipboard-shortcut-sync.zip
Summary
In the web viewer, the clipboard can currently only be transferred with the Send Clipboard and Receive Clipboard buttons in the input popover. Keyboard shortcuts are forwarded as plain keys, so Ctrl+C copies to the remote clipboard and Ctrl+V pastes from it, but nothing ever reaches the viewer's own clipboard. Users coming from RDP expect copy and paste to just work.
I've implemented this and attached it as a patch (
git am-ready), based onmainatece9542(v0.28.2.0). I don't have a GitHub fork, so I'm sending it this way — feel free to take it as-is, change it, or use it only as a reference.Behavior
Paste (Ctrl+V): the key-down is no longer prevented, so the browser raises a
pasteevent carrying the local clipboard text. That text is sent to the remote device — but only if it changed since the last sync — and only then is the held key-down forwarded. Sending it unconditionally would overwrite text the user had just copied on the remote device with the mouse (context menu), and the paste would insert something else than expected. A "Clipboard sent" snackbar confirms the transfer after it succeeds.Taking the text from the
pasteevent rather thannavigator.clipboard.readText()matters:readText()needs the clipboard-read permission and fails withNotAllowedErroron every site where the user hasn't granted it, while thepasteevent needs no permission and never prompts.Copy / cut (Ctrl+C, Ctrl+X): after the key is forwarded, the remote clipboard is requested following a short delay (300 ms), so the remote application has time to update it. The existing clipboard handler writes it locally and shows "Received clipboard text".
Both directions:
send_clipboard_text/receive_clipboard_text),CanReadClipboard/CanWriteClipboardpermissions,Settings
is-clipboard-shortcut-sync-enabled, enabled by default, with a checkbox on the Settings page.ControlR.Web.Server_internal.jsonis regenerated by the build.Key event ordering
Sending the clipboard before the Ctrl+V key-down is asynchronous. Key-down events were awaited but key-up events were not, so a key-up could reach the remote device before its key-down. Key events now go through a small promise queue in
RemoteDisplay.razor.js. The Ctrl+V key-down is held until itspasteevent, or until its key-up if nopasteevent arrives.Tests
ClipboardShortcutSyncservice with 20 new tests (27 cases) inControlR.Web.Client.Tests: copy shortcut detection, permissions, the per-session switch, unchanged/blank clipboard text, retry after a failed send, the copy delay (withFakeTimeProvider), cancellation, error handling and activity recording. Paste shortcut detection and the key queue live inRemoteDisplay.razor.js, which has no test harness, so they were verified manually.UserPreferencesManagerTestsupdated for the new preference.ControlR.Web.Client.Tests: 156 passed.ControlR.Web.Server.Tests: 903 passed.ControlR.Web.Serverbuilds with 0 warnings.Manual testing: done on our own build of 0.27.6.0 with this change backported, in a Chromium-based browser, against a real remote device and without an RDP session to it (RDP syncs the clipboard on its own and would mask the result):
Known limitations
Applying the patch
Unzip the attachment and run:
Contributor License Agreement
I have read and agree to the Contribution License Agreement in
.github/pull_request_template.md, and it applies to this submission.The change was written with the help of Claude Code (Anthropic), noted in the commit trailer.
clipboard-shortcut-sync.zip