fix: preserve UTF-8 clipboard text in native macOS launches
Verify Tessera GUI / verify (push) Failing after 7m14s

This commit is contained in:
2026-10-20 17:59:39 +08:00
parent d641f62116
commit 1d4b1d3c86
3 changed files with 65 additions and 23 deletions
+12 -23
View File
@@ -105,31 +105,20 @@ impl TesseraApp {
if command && let Key::Character(value) = &event.logical_key {
match value.to_lowercase().as_str() {
"a" => self.developer_tools.selection = true,
"v" => {
#[cfg(target_os = "macos")]
if let Ok(output) = std::process::Command::new("pbpaste").output()
&& output.status.success()
{
self.developer_tools
.insert(&String::from_utf8_lossy(&output.stdout));
}
}
"v" => match crate::clipboard::read() {
Ok(value) => self.developer_tools.insert(&value),
Err(error) => self.developer_tools.input_error = format!("Clipboard: {error}"),
},
"c" | "x" => {
#[cfg(target_os = "macos")]
{
use std::io::Write;
let selected = self.developer_tools.selected_text();
if !selected.is_empty()
&& let Ok(mut child) = std::process::Command::new("pbcopy")
.stdin(std::process::Stdio::piped())
.spawn()
{
if let Some(mut stdin) = child.stdin.take() {
let _ = stdin.write_all(selected.as_bytes());
let selected = self.developer_tools.selected_text();
if !selected.is_empty() {
match crate::clipboard::write(&selected) {
Ok(()) if value.eq_ignore_ascii_case("x") => {
self.developer_tools.insert("")
}
let _ = child.wait();
if value.eq_ignore_ascii_case("x") {
self.developer_tools.insert("");
Ok(()) => {}
Err(error) => {
self.developer_tools.input_error = format!("Clipboard: {error}")
}
}
}
+52
View File
@@ -0,0 +1,52 @@
//! Clipboard text is always UTF-8, including Finder/LaunchServices launches without LANG.
use anyhow::{Result, bail};
#[cfg(target_os = "macos")]
fn command(program: &str) -> std::process::Command {
let mut cmd = std::process::Command::new(program);
cmd.env("LC_ALL", "en_US.UTF-8")
.env("LC_CTYPE", "en_US.UTF-8")
.env("LANG", "en_US.UTF-8");
cmd
}
pub fn read() -> Result<String> {
#[cfg(target_os = "macos")]
{
let output = command("/usr/bin/pbpaste")
.arg("-Prefer")
.arg("txt")
.output()?;
if !output.status.success() {
bail!("system clipboard read failed");
}
// Never silently replace invalid bytes in the user's source.
Ok(String::from_utf8(output.stdout)?)
}
#[cfg(not(target_os = "macos"))]
{
bail!("system clipboard adapter is not available on this platform")
}
}
pub fn write(value: &str) -> Result<()> {
#[cfg(target_os = "macos")]
{
use std::io::Write;
let mut child = command("/usr/bin/pbcopy")
.stdin(std::process::Stdio::piped())
.spawn()?;
let mut stdin = child
.stdin
.take()
.ok_or_else(|| anyhow::anyhow!("clipboard stdin is unavailable"))?;
stdin.write_all(value.as_bytes())?;
drop(stdin);
if !child.wait()?.success() {
bail!("system clipboard write failed");
}
Ok(())
}
#[cfg(not(target_os = "macos"))]
{
let _ = value;
bail!("system clipboard adapter is not available on this platform")
}
}
+1
View File
@@ -1,6 +1,7 @@
mod activity;
mod api;
mod app;
mod clipboard;
mod developer_tools;
mod display_list;
mod document;