129 lines
4.5 KiB
TypeScript
129 lines
4.5 KiB
TypeScript
import { $ } from "bun"
|
|
import { platform, release } from "os"
|
|
import clipboardy from "clipboardy"
|
|
import { lazy } from "../../../../util/lazy.js"
|
|
import { tmpdir } from "os"
|
|
import path from "path"
|
|
|
|
export namespace Clipboard {
|
|
export interface Content {
|
|
data: string
|
|
mime: string
|
|
}
|
|
|
|
export async function read(): Promise<Content | undefined> {
|
|
const os = platform()
|
|
|
|
if (os === "darwin") {
|
|
const tmpfile = path.join(tmpdir(), "opencode-clipboard.png")
|
|
try {
|
|
await $`osascript -e 'set imageData to the clipboard as "PNGf"' -e 'set fileRef to open for access POSIX file "${tmpfile}" with write permission' -e 'set eof fileRef to 0' -e 'write imageData to fileRef' -e 'close access fileRef'`
|
|
.nothrow()
|
|
.quiet()
|
|
const file = Bun.file(tmpfile)
|
|
const buffer = await file.arrayBuffer()
|
|
return { data: Buffer.from(buffer).toString("base64"), mime: "image/png" }
|
|
} catch {
|
|
} finally {
|
|
await $`rm -f "${tmpfile}"`.nothrow().quiet()
|
|
}
|
|
}
|
|
|
|
if (os === "win32" || release().includes("WSL")) {
|
|
const script =
|
|
"Add-Type -AssemblyName System.Windows.Forms; $img = [System.Windows.Forms.Clipboard]::GetImage(); if ($img) { $ms = New-Object System.IO.MemoryStream; $img.Save($ms, [System.Drawing.Imaging.ImageFormat]::Png); [System.Convert]::ToBase64String($ms.ToArray()) }"
|
|
const base64 = await $`powershell.exe -NonInteractive -NoProfile -command "${script}"`.nothrow().text()
|
|
if (base64) {
|
|
const imageBuffer = Buffer.from(base64.trim(), "base64")
|
|
if (imageBuffer.length > 0) {
|
|
return { data: imageBuffer.toString("base64"), mime: "image/png" }
|
|
}
|
|
}
|
|
}
|
|
|
|
if (os === "linux") {
|
|
const wayland = await $`wl-paste -t image/png`.nothrow().arrayBuffer()
|
|
if (wayland && wayland.byteLength > 0) {
|
|
return { data: Buffer.from(wayland).toString("base64"), mime: "image/png" }
|
|
}
|
|
const x11 = await $`xclip -selection clipboard -t image/png -o`.nothrow().arrayBuffer()
|
|
if (x11 && x11.byteLength > 0) {
|
|
return { data: Buffer.from(x11).toString("base64"), mime: "image/png" }
|
|
}
|
|
}
|
|
|
|
const text = await clipboardy.read().catch(() => {})
|
|
if (text) {
|
|
return { data: text, mime: "text/plain" }
|
|
}
|
|
}
|
|
|
|
const getCopyMethod = lazy(() => {
|
|
const os = platform()
|
|
|
|
if (os === "darwin" && Bun.which("osascript")) {
|
|
console.log("clipboard: using osascript")
|
|
return async (text: string) => {
|
|
const escaped = text.replace(/\\/g, "\\\\").replace(/"/g, '\\"')
|
|
await $`osascript -e 'set the clipboard to "${escaped}"'`.nothrow().quiet()
|
|
}
|
|
}
|
|
|
|
if (os === "linux") {
|
|
if (process.env["WAYLAND_DISPLAY"] && Bun.which("wl-copy")) {
|
|
console.log("clipboard: using wl-copy")
|
|
return async (text: string) => {
|
|
const proc = Bun.spawn(["wl-copy"], { stdin: "pipe", stdout: "ignore", stderr: "ignore" })
|
|
proc.stdin.write(text)
|
|
proc.stdin.end()
|
|
await proc.exited.catch(() => {})
|
|
}
|
|
}
|
|
if (Bun.which("xclip")) {
|
|
console.log("clipboard: using xclip")
|
|
return async (text: string) => {
|
|
const proc = Bun.spawn(["xclip", "-selection", "clipboard"], {
|
|
stdin: "pipe",
|
|
stdout: "ignore",
|
|
stderr: "ignore",
|
|
})
|
|
proc.stdin.write(text)
|
|
proc.stdin.end()
|
|
await proc.exited.catch(() => {})
|
|
}
|
|
}
|
|
if (Bun.which("xsel")) {
|
|
console.log("clipboard: using xsel")
|
|
return async (text: string) => {
|
|
const proc = Bun.spawn(["xsel", "--clipboard", "--input"], {
|
|
stdin: "pipe",
|
|
stdout: "ignore",
|
|
stderr: "ignore",
|
|
})
|
|
proc.stdin.write(text)
|
|
proc.stdin.end()
|
|
await proc.exited.catch(() => {})
|
|
}
|
|
}
|
|
}
|
|
|
|
if (os === "win32") {
|
|
console.log("clipboard: using powershell")
|
|
return async (text: string) => {
|
|
// need to escape backticks because powershell uses them as escape code
|
|
const escaped = text.replace(/"/g, '""').replace(/`/g, '``')
|
|
await $`powershell -NonInteractive -NoProfile -Command "Set-Clipboard -Value \"${escaped}\""`.nothrow().quiet()
|
|
}
|
|
}
|
|
|
|
console.log("clipboard: no native support")
|
|
return async (text: string) => {
|
|
await clipboardy.write(text).catch(() => {})
|
|
}
|
|
})
|
|
|
|
export async function copy(text: string): Promise<void> {
|
|
await getCopyMethod()(text)
|
|
}
|
|
}
|