diff --git a/crates/gpui_web/Cargo.toml b/crates/gpui_web/Cargo.toml index 5980fa5e855..6ceafc0f3bc 100644 --- a/crates/gpui_web/Cargo.toml +++ b/crates/gpui_web/Cargo.toml @@ -35,6 +35,8 @@ raw-window-handle = "0.6" wasm_thread = { version = "0.3", features = ["es_modules"], optional = true } web-sys = { version = "0.3", features = [ "console", + "Clipboard", + "ClipboardEvent", "CompositionEvent", "CssStyleDeclaration", "DataTransfer", diff --git a/crates/gpui_web/src/events.rs b/crates/gpui_web/src/events.rs index 46be646cb56..5956331ea81 100644 --- a/crates/gpui_web/src/events.rs +++ b/crates/gpui_web/src/events.rs @@ -64,6 +64,7 @@ impl WebWindowInner { self.register_dragleave(), self.register_key_down(), self.register_key_up(), + self.register_paste(), self.register_composition_start(), self.register_composition_update(), self.register_composition_end(), @@ -427,6 +428,32 @@ impl WebWindowInner { }) } + /// Paste is delivered through the DOM `paste` event rather than + /// `Platform::read_from_clipboard`: the browser's asynchronous clipboard + /// read API cannot fit that synchronous signature, while `ClipboardEvent` + /// exposes `clipboardData` synchronously inside the event. It fires for + /// any browser-initiated paste (keyboard, menu bar, context menu). + fn register_paste(self: &Rc) -> Closure { + let this = Rc::clone(self); + self.listen_input("paste", move |event: JsValue| { + let event: web_sys::ClipboardEvent = event.unchecked_into(); + let Some(clipboard_data) = event.clipboard_data() else { + return; + }; + let Ok(text) = clipboard_data.get_data("text/plain") else { + return; + }; + if text.is_empty() { + return; + } + + event.prevent_default(); + this.with_input_handler(|handler| { + handler.replace_text_in_range(None, &text); + }); + }) + } + fn register_composition_start(self: &Rc) -> Closure { let this = Rc::clone(self); self.listen_input("compositionstart", move |_event: JsValue| { diff --git a/crates/gpui_web/src/platform.rs b/crates/gpui_web/src/platform.rs index c39723ea5b2..f70520d629d 100644 --- a/crates/gpui_web/src/platform.rs +++ b/crates/gpui_web/src/platform.rs @@ -342,7 +342,15 @@ impl Platform for WebPlatform { None } - fn write_to_clipboard(&self, _item: ClipboardItem) {} + fn write_to_clipboard(&self, item: ClipboardItem) { + if let Some(text) = item.text() + && let Some(window) = web_sys::window() + { + // Fire-and-forget; called synchronously inside the user's input + // event, which satisfies the browser's user-activation requirement. + drop(window.navigator().clipboard().write_text(&text)); + } + } fn write_credentials(&self, _url: &str, _username: &str, _password: &[u8]) -> Task> { Task::ready(Err(anyhow::anyhow!(