@tooee/clipboard 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,5 @@
1
+ # @tooee/clipboard
2
+
3
+ Cross-platform clipboard operations for Tooee.
4
+
5
+ Part of the [Tooee](https://github.com/gingerhendrix/tooee) monorepo. See the main repo for documentation.
@@ -0,0 +1,8 @@
1
+ export interface ClipboardContent {
2
+ data: string;
3
+ mime: string;
4
+ }
5
+ export declare function readClipboard(): Promise<ClipboardContent | undefined>;
6
+ export declare function readClipboardText(): Promise<string | undefined>;
7
+ export declare function copyToClipboard(text: string): Promise<void>;
8
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../src/clipboard.ts"],"names":[],"mappings":"AAIA,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,MAAM,CAAA;CACb;AAED,wBAAsB,aAAa,IAAI,OAAO,CAAC,gBAAgB,GAAG,SAAS,CAAC,CA+D3E;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CA6BrE;AAyED,wBAAsB,eAAe,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAEjE"}
@@ -0,0 +1,158 @@
1
+ import { $ } from "bun";
2
+ import { platform, release, tmpdir } from "os";
3
+ import path from "path";
4
+ export async function readClipboard() {
5
+ const os = platform();
6
+ if (os === "darwin") {
7
+ const tmpfile = path.join(tmpdir(), "tooee-clipboard.png");
8
+ try {
9
+ 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'`
10
+ .nothrow()
11
+ .quiet();
12
+ const file = Bun.file(tmpfile);
13
+ const buffer = await file.arrayBuffer();
14
+ if (buffer.byteLength > 0) {
15
+ return {
16
+ data: Buffer.from(buffer).toString("base64"),
17
+ mime: "image/png",
18
+ };
19
+ }
20
+ }
21
+ catch {
22
+ // Image read failed, try text below
23
+ }
24
+ finally {
25
+ await $ `rm -f "${tmpfile}"`.nothrow().quiet();
26
+ }
27
+ }
28
+ if (os === "win32" || release().includes("WSL")) {
29
+ 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()) }";
30
+ const base64 = await $ `powershell.exe -command "${script}"`.nothrow().text();
31
+ if (base64) {
32
+ const imageBuffer = Buffer.from(base64.trim(), "base64");
33
+ if (imageBuffer.length > 0) {
34
+ return { data: imageBuffer.toString("base64"), mime: "image/png" };
35
+ }
36
+ }
37
+ }
38
+ if (os === "linux") {
39
+ if (process.env["WAYLAND_DISPLAY"] && Bun.which("wl-paste")) {
40
+ const wayland = await $ `wl-paste -t image/png`.nothrow().arrayBuffer();
41
+ if (wayland && wayland.byteLength > 0) {
42
+ return {
43
+ data: Buffer.from(wayland).toString("base64"),
44
+ mime: "image/png",
45
+ };
46
+ }
47
+ }
48
+ if (Bun.which("xclip")) {
49
+ const x11 = await $ `xclip -selection clipboard -t image/png -o`.nothrow().arrayBuffer();
50
+ if (x11 && x11.byteLength > 0) {
51
+ return {
52
+ data: Buffer.from(x11).toString("base64"),
53
+ mime: "image/png",
54
+ };
55
+ }
56
+ }
57
+ }
58
+ const text = await readClipboardText();
59
+ if (text) {
60
+ return { data: text, mime: "text/plain" };
61
+ }
62
+ return undefined;
63
+ }
64
+ export async function readClipboardText() {
65
+ const os = platform();
66
+ if (os === "darwin") {
67
+ const result = await $ `pbpaste`.nothrow().quiet().text();
68
+ return result || undefined;
69
+ }
70
+ if (os === "linux") {
71
+ if (process.env["WAYLAND_DISPLAY"] && Bun.which("wl-paste")) {
72
+ const result = await $ `wl-paste`.nothrow().quiet().text();
73
+ return result || undefined;
74
+ }
75
+ if (Bun.which("xclip")) {
76
+ const result = await $ `xclip -selection clipboard -o`.nothrow().quiet().text();
77
+ return result || undefined;
78
+ }
79
+ if (Bun.which("xsel")) {
80
+ const result = await $ `xsel --clipboard --output`.nothrow().quiet().text();
81
+ return result || undefined;
82
+ }
83
+ }
84
+ if (os === "win32") {
85
+ const result = await $ `powershell -command "Get-Clipboard"`.nothrow().quiet().text();
86
+ return result || undefined;
87
+ }
88
+ return undefined;
89
+ }
90
+ let copyMethod = null;
91
+ function getCopyMethod() {
92
+ if (copyMethod)
93
+ return copyMethod;
94
+ const os = platform();
95
+ if (os === "darwin" && Bun.which("osascript")) {
96
+ copyMethod = async (text) => {
97
+ const escaped = text.replace(/\\/g, "\\\\").replace(/"/g, '\\"');
98
+ await $ `osascript -e 'set the clipboard to "${escaped}"'`.nothrow().quiet();
99
+ };
100
+ return copyMethod;
101
+ }
102
+ if (os === "linux") {
103
+ if (process.env["WAYLAND_DISPLAY"] && Bun.which("wl-copy")) {
104
+ copyMethod = async (text) => {
105
+ const proc = Bun.spawn(["wl-copy"], {
106
+ stdin: "pipe",
107
+ stdout: "ignore",
108
+ stderr: "ignore",
109
+ });
110
+ proc.stdin.write(text);
111
+ proc.stdin.end();
112
+ await proc.exited.catch(() => undefined);
113
+ };
114
+ return copyMethod;
115
+ }
116
+ if (Bun.which("xclip")) {
117
+ copyMethod = async (text) => {
118
+ const proc = Bun.spawn(["xclip", "-selection", "clipboard"], {
119
+ stdin: "pipe",
120
+ stdout: "ignore",
121
+ stderr: "ignore",
122
+ });
123
+ proc.stdin.write(text);
124
+ proc.stdin.end();
125
+ await proc.exited.catch(() => undefined);
126
+ };
127
+ return copyMethod;
128
+ }
129
+ if (Bun.which("xsel")) {
130
+ copyMethod = async (text) => {
131
+ const proc = Bun.spawn(["xsel", "--clipboard", "--input"], {
132
+ stdin: "pipe",
133
+ stdout: "ignore",
134
+ stderr: "ignore",
135
+ });
136
+ proc.stdin.write(text);
137
+ proc.stdin.end();
138
+ await proc.exited.catch(() => undefined);
139
+ };
140
+ return copyMethod;
141
+ }
142
+ }
143
+ if (os === "win32") {
144
+ copyMethod = async (text) => {
145
+ const escaped = text.replace(/"/g, '""');
146
+ await $ `powershell -command "Set-Clipboard -Value \"${escaped}\""`.nothrow().quiet();
147
+ };
148
+ return copyMethod;
149
+ }
150
+ copyMethod = async () => {
151
+ // Silent no-op — no clipboard support available
152
+ };
153
+ return copyMethod;
154
+ }
155
+ export async function copyToClipboard(text) {
156
+ await getCopyMethod()(text);
157
+ }
158
+ //# sourceMappingURL=clipboard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.js","sourceRoot":"","sources":["../src/clipboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ export { copyToClipboard, readClipboard, readClipboardText } from "./clipboard.js";
2
+ export type { ClipboardContent } from "./clipboard.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA;AAClF,YAAY,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAA"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { copyToClipboard, readClipboard, readClipboardText } from "./clipboard.js";
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAA"}
package/package.json ADDED
@@ -0,0 +1,32 @@
1
+ {
2
+ "name": "@tooee/clipboard",
3
+ "version": "0.1.0",
4
+ "description": "Cross-platform clipboard operations for Tooee",
5
+ "license": "MIT",
6
+ "author": "Gareth Andrew",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "https://github.com/gingerhendrix/tooee.git",
10
+ "directory": "packages/clipboard"
11
+ },
12
+ "homepage": "https://github.com/gingerhendrix/tooee",
13
+ "bugs": "https://github.com/gingerhendrix/tooee/issues",
14
+ "keywords": ["tui", "terminal", "cli", "opentui", "clipboard"],
15
+ "type": "module",
16
+ "exports": {
17
+ ".": {
18
+ "import": {
19
+ "@tooee/source": "./src/index.ts",
20
+ "default": "./dist/index.js"
21
+ }
22
+ }
23
+ },
24
+ "files": ["dist", "src"],
25
+ "scripts": {
26
+ "typecheck": "tsc --noEmit"
27
+ },
28
+ "devDependencies": {
29
+ "@types/bun": "^1.3.5",
30
+ "typescript": "^5.8.3"
31
+ }
32
+ }
@@ -0,0 +1,179 @@
1
+ import { $ } from "bun"
2
+ import { platform, release, tmpdir } from "os"
3
+ import path from "path"
4
+
5
+ export interface ClipboardContent {
6
+ data: string
7
+ mime: string
8
+ }
9
+
10
+ export async function readClipboard(): Promise<ClipboardContent | undefined> {
11
+ const os = platform()
12
+
13
+ if (os === "darwin") {
14
+ const tmpfile = path.join(tmpdir(), "tooee-clipboard.png")
15
+ try {
16
+ 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'`
17
+ .nothrow()
18
+ .quiet()
19
+ const file = Bun.file(tmpfile)
20
+ const buffer = await file.arrayBuffer()
21
+ if (buffer.byteLength > 0) {
22
+ return {
23
+ data: Buffer.from(buffer).toString("base64"),
24
+ mime: "image/png",
25
+ }
26
+ }
27
+ } catch {
28
+ // Image read failed, try text below
29
+ } finally {
30
+ await $`rm -f "${tmpfile}"`.nothrow().quiet()
31
+ }
32
+ }
33
+
34
+ if (os === "win32" || release().includes("WSL")) {
35
+ const script =
36
+ "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()) }"
37
+ const base64 = await $`powershell.exe -command "${script}"`.nothrow().text()
38
+ if (base64) {
39
+ const imageBuffer = Buffer.from(base64.trim(), "base64")
40
+ if (imageBuffer.length > 0) {
41
+ return { data: imageBuffer.toString("base64"), mime: "image/png" }
42
+ }
43
+ }
44
+ }
45
+
46
+ if (os === "linux") {
47
+ if (process.env["WAYLAND_DISPLAY"] && Bun.which("wl-paste")) {
48
+ const wayland = await $`wl-paste -t image/png`.nothrow().arrayBuffer()
49
+ if (wayland && wayland.byteLength > 0) {
50
+ return {
51
+ data: Buffer.from(wayland).toString("base64"),
52
+ mime: "image/png",
53
+ }
54
+ }
55
+ }
56
+ if (Bun.which("xclip")) {
57
+ const x11 = await $`xclip -selection clipboard -t image/png -o`.nothrow().arrayBuffer()
58
+ if (x11 && x11.byteLength > 0) {
59
+ return {
60
+ data: Buffer.from(x11).toString("base64"),
61
+ mime: "image/png",
62
+ }
63
+ }
64
+ }
65
+ }
66
+
67
+ const text = await readClipboardText()
68
+ if (text) {
69
+ return { data: text, mime: "text/plain" }
70
+ }
71
+
72
+ return undefined
73
+ }
74
+
75
+ export async function readClipboardText(): Promise<string | undefined> {
76
+ const os = platform()
77
+
78
+ if (os === "darwin") {
79
+ const result = await $`pbpaste`.nothrow().quiet().text()
80
+ return result || undefined
81
+ }
82
+
83
+ if (os === "linux") {
84
+ if (process.env["WAYLAND_DISPLAY"] && Bun.which("wl-paste")) {
85
+ const result = await $`wl-paste`.nothrow().quiet().text()
86
+ return result || undefined
87
+ }
88
+ if (Bun.which("xclip")) {
89
+ const result = await $`xclip -selection clipboard -o`.nothrow().quiet().text()
90
+ return result || undefined
91
+ }
92
+ if (Bun.which("xsel")) {
93
+ const result = await $`xsel --clipboard --output`.nothrow().quiet().text()
94
+ return result || undefined
95
+ }
96
+ }
97
+
98
+ if (os === "win32") {
99
+ const result = await $`powershell -command "Get-Clipboard"`.nothrow().quiet().text()
100
+ return result || undefined
101
+ }
102
+
103
+ return undefined
104
+ }
105
+
106
+ let copyMethod: ((text: string) => Promise<void>) | null = null
107
+
108
+ function getCopyMethod(): (text: string) => Promise<void> {
109
+ if (copyMethod) return copyMethod
110
+
111
+ const os = platform()
112
+
113
+ if (os === "darwin" && Bun.which("osascript")) {
114
+ copyMethod = async (text: string) => {
115
+ const escaped = text.replace(/\\/g, "\\\\").replace(/"/g, '\\"')
116
+ await $`osascript -e 'set the clipboard to "${escaped}"'`.nothrow().quiet()
117
+ }
118
+ return copyMethod
119
+ }
120
+
121
+ if (os === "linux") {
122
+ if (process.env["WAYLAND_DISPLAY"] && Bun.which("wl-copy")) {
123
+ copyMethod = async (text: string) => {
124
+ const proc = Bun.spawn(["wl-copy"], {
125
+ stdin: "pipe",
126
+ stdout: "ignore",
127
+ stderr: "ignore",
128
+ })
129
+ proc.stdin.write(text)
130
+ proc.stdin.end()
131
+ await proc.exited.catch(() => undefined)
132
+ }
133
+ return copyMethod
134
+ }
135
+ if (Bun.which("xclip")) {
136
+ copyMethod = async (text: string) => {
137
+ const proc = Bun.spawn(["xclip", "-selection", "clipboard"], {
138
+ stdin: "pipe",
139
+ stdout: "ignore",
140
+ stderr: "ignore",
141
+ })
142
+ proc.stdin.write(text)
143
+ proc.stdin.end()
144
+ await proc.exited.catch(() => undefined)
145
+ }
146
+ return copyMethod
147
+ }
148
+ if (Bun.which("xsel")) {
149
+ copyMethod = async (text: string) => {
150
+ const proc = Bun.spawn(["xsel", "--clipboard", "--input"], {
151
+ stdin: "pipe",
152
+ stdout: "ignore",
153
+ stderr: "ignore",
154
+ })
155
+ proc.stdin.write(text)
156
+ proc.stdin.end()
157
+ await proc.exited.catch(() => undefined)
158
+ }
159
+ return copyMethod
160
+ }
161
+ }
162
+
163
+ if (os === "win32") {
164
+ copyMethod = async (text: string) => {
165
+ const escaped = text.replace(/"/g, '""')
166
+ await $`powershell -command "Set-Clipboard -Value \"${escaped}\""`.nothrow().quiet()
167
+ }
168
+ return copyMethod
169
+ }
170
+
171
+ copyMethod = async () => {
172
+ // Silent no-op — no clipboard support available
173
+ }
174
+ return copyMethod
175
+ }
176
+
177
+ export async function copyToClipboard(text: string): Promise<void> {
178
+ await getCopyMethod()(text)
179
+ }
package/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { copyToClipboard, readClipboard, readClipboardText } from "./clipboard.js"
2
+ export type { ClipboardContent } from "./clipboard.js"