Safari's security policy blocks programmatic clipboard reads, triggering a native confirmation pop-up whenever the SDK tries to sync the local clipboard with the Cloud Phone. This guide shows you how to implement a custom clipboard dialog that lets users paste text into a Cloud Phone session without triggering that restriction.
Solution overview
Add a custom clipboard button to your local client UI. Clicking the button opens a pop-up input box.
The user pastes the content they want to send to the Cloud Phone into the pop-up input box.
Clicking the Paste Now button in the pop-up synchronizes the content to both the Cloud Phone's clipboard and its active input field.
Configure the SDK parameters
Set the following parameters in connConfig before creating the session. Both are required for the custom clipboard flow to work.
| Parameter | Value | Purpose |
|---|---|---|
readClipboardDataByUser | isSafari() | Disables automatic clipboard reading by the SDK in Safari, giving you control over when clipboard data is sent |
useCustomIme | true | Enables the local Input Method Editor (IME), which is required to insert content into the Cloud Phone's active input field |
// Detect Safari by checking the user agent
function isSafari() {
return /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
}
// Main control configuration
connConfig = {..., readClipboardDataByUser: isSafari(), useCustomIme: true};
var appInfo = {
...,
connConfig: connConfig,
...
};
var sessionParam = {
...,
appInfo: appInfo,
...
};
var wuyingSdk = Wuying.WebSDK;
session = wuyingSdk.createSession('appstream', sessionParam);
// Thumbnail configuration — also requires useCustomIme
this.thumbnail = new window.Wuying.ThumbnailSDK(
{
...,
connectionConfig: {
useCustomIme: true,
},
},
{
onConnected: (data) => {
this.thumbnail.session.getLocalConfig().setClipboardEnabled(true);
},
onDisConnected: (data) => {},
onThumbnailData: (url) => {},
}
);Build the clipboard dialog
Add a clipboard button to your UI. When clicked, it opens a modal dialog where users can paste text and send it to the Cloud Phone.
Create the dialog
function showInput() {
showCustomDialog();
// Close the dialog when clicking outside it
document.querySelector('.modal-overlay').addEventListener('click', function (e) {
if (e.target === this) {
closeModal();
}
});
// Close the dialog on Escape
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
closeModal();
}
});
}
function closeModal() {
console.log('closeModal');
const overlay = document.querySelector('.modal-overlay');
if (overlay) {
overlay.style.display = 'none';
overlay.remove();
}
}
function showCustomDialog() {
// Create the modal overlay
const overlay = document.createElement('div');
overlay.className = 'modal-overlay';
overlay.style.pointerEvents = 'all';
overlay.style.zIndex = '9999';
// Prevent keyboard events from reaching underlying UI elements
const stopPropagation = function(e) {
e.stopPropagation();
};
overlay.addEventListener('contextmenu', stopPropagation, true);
overlay.addEventListener('keydown', stopPropagation, true);
overlay.addEventListener('keyup', stopPropagation, true);
overlay.addEventListener('keypress', stopPropagation, true);
overlay.innerHTML = `
<div class="modal">
<div class="modal-header">
<h2 class="modal-title">Clipboard</h2>
<button class="close-btn" onclick="closeModal()">×</button>
</div>
<div class="input-container">
<textarea
class="input-field"
placeholder="Enter content"
maxlength="500"
></textarea>
</div>
<button class="submit-btn" onclick="submitContent()">
Paste Now
</button>
</div>
`;
document.body.appendChild(overlay);
// Focus the textarea so keyboard events are captured immediately
setTimeout(() => {
const textarea = overlay.querySelector('.input-field');
if (textarea) {
textarea.focus();
}
}, 100);
}Send the text to the Cloud Phone
When the user clicks Paste Now, the following functions validate the input and sync it to the Cloud Phone's clipboard and active input field.
function submitContent() {
const content = document.querySelector('.input-field').value.trim();
if (!content) {
alert('Please enter content');
return;
}
// Sync content to the Cloud Phone
sendMsgToCloudPhoneOperation(content);
closeModal();
}
function sendMsgToCloudPhoneOperation(msg) {
// Main control session
if (session) {
session.setClipboardModule('sendClipboardDataToRemote', msg);
}
// Thumbnail sessions
for (const [key, value] of thumbnailSDKMap) {
console.log('thumbnail sendMsgToCloudPhoneOperation ', key, msg);
value.thumbnail.session.getClipboardModule().sendClipboardDataToRemote(msg);
}
}Keyboard event handling
The modal overlay must intercept all keyboard events with stopPropagation before they reach other elements on the page. Without this, keystrokes typed inside the dialog are captured by the underlying UI instead of the textarea, breaking normal text input.
overlay.addEventListener('contextmenu', stopPropagation, true);
overlay.addEventListener('keydown', stopPropagation, true);
overlay.addEventListener('keyup', stopPropagation, true);
overlay.addEventListener('keypress', stopPropagation, true);