poly-weaver 0.11.3 → 0.11.4
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/dist/cli.js +3 -0
- package/dist/cli.js.map +1 -1
- package/dist/flow-editor/tui.d.ts +3 -0
- package/dist/flow-editor/tui.d.ts.map +1 -1
- package/dist/flow-editor/tui.js +87 -9
- package/dist/flow-editor/tui.js.map +1 -1
- package/dist/markdown.d.ts.map +1 -1
- package/dist/markdown.js +386 -30
- package/dist/markdown.js.map +1 -1
- package/dist/offscreen/artifact-pane.d.ts +20 -0
- package/dist/offscreen/artifact-pane.d.ts.map +1 -0
- package/dist/offscreen/artifact-pane.js +72 -0
- package/dist/offscreen/artifact-pane.js.map +1 -0
- package/dist/offscreen/controller.d.ts +16 -3
- package/dist/offscreen/controller.d.ts.map +1 -1
- package/dist/offscreen/controller.js +84 -75
- package/dist/offscreen/controller.js.map +1 -1
- package/dist/offscreen/inquiry-pane.d.ts +14 -0
- package/dist/offscreen/inquiry-pane.d.ts.map +1 -0
- package/dist/offscreen/inquiry-pane.js +101 -0
- package/dist/offscreen/inquiry-pane.js.map +1 -0
- package/dist/offscreen/panes.d.ts +14 -0
- package/dist/offscreen/panes.d.ts.map +1 -0
- package/dist/offscreen/panes.js +2 -0
- package/dist/offscreen/panes.js.map +1 -0
- package/dist/offscreen/split-inquiry.d.ts +28 -0
- package/dist/offscreen/split-inquiry.d.ts.map +1 -0
- package/dist/offscreen/split-inquiry.js +88 -0
- package/dist/offscreen/split-inquiry.js.map +1 -0
- package/dist/orchestrator.d.ts +3 -0
- package/dist/orchestrator.d.ts.map +1 -1
- package/dist/orchestrator.js +18 -2
- package/dist/orchestrator.js.map +1 -1
- package/dist/paste-image/attachment-manager.d.ts +20 -0
- package/dist/paste-image/attachment-manager.d.ts.map +1 -0
- package/dist/paste-image/attachment-manager.js +88 -0
- package/dist/paste-image/attachment-manager.js.map +1 -0
- package/dist/paste-image/bracketed-paste.d.ts +13 -0
- package/dist/paste-image/bracketed-paste.d.ts.map +1 -0
- package/dist/paste-image/bracketed-paste.js +82 -0
- package/dist/paste-image/bracketed-paste.js.map +1 -0
- package/dist/paste-image/clipboard-ffi.d.ts +9 -0
- package/dist/paste-image/clipboard-ffi.d.ts.map +1 -0
- package/dist/paste-image/clipboard-ffi.js +149 -0
- package/dist/paste-image/clipboard-ffi.js.map +1 -0
- package/dist/paste-image/clipboard.d.ts +38 -0
- package/dist/paste-image/clipboard.d.ts.map +1 -0
- package/dist/paste-image/clipboard.js +454 -0
- package/dist/paste-image/clipboard.js.map +1 -0
- package/dist/paste-image/image-format.d.ts +7 -0
- package/dist/paste-image/image-format.d.ts.map +1 -0
- package/dist/paste-image/image-format.js +61 -0
- package/dist/paste-image/image-format.js.map +1 -0
- package/dist/paste-image/intents.d.ts +11 -0
- package/dist/paste-image/intents.d.ts.map +1 -0
- package/dist/paste-image/intents.js +66 -0
- package/dist/paste-image/intents.js.map +1 -0
- package/dist/paste-image/persistence.d.ts +2 -0
- package/dist/paste-image/persistence.d.ts.map +1 -0
- package/dist/paste-image/persistence.js +8 -0
- package/dist/paste-image/persistence.js.map +1 -0
- package/dist/paste-image/staging.d.ts +8 -0
- package/dist/paste-image/staging.d.ts.map +1 -0
- package/dist/paste-image/staging.js +23 -0
- package/dist/paste-image/staging.js.map +1 -0
- package/dist/paste-image/token.d.ts +14 -0
- package/dist/paste-image/token.d.ts.map +1 -0
- package/dist/paste-image/token.js +26 -0
- package/dist/paste-image/token.js.map +1 -0
- package/dist/providers/option-catalog.d.ts.map +1 -1
- package/dist/providers/option-catalog.js +0 -1
- package/dist/providers/option-catalog.js.map +1 -1
- package/dist/startup-tui.d.ts +8 -2
- package/dist/startup-tui.d.ts.map +1 -1
- package/dist/startup-tui.js +76 -5
- package/dist/startup-tui.js.map +1 -1
- package/dist/terminal/input-router.d.ts +9 -0
- package/dist/terminal/input-router.d.ts.map +1 -1
- package/dist/terminal/input-router.js +14 -0
- package/dist/terminal/input-router.js.map +1 -1
- package/dist/text-editing/emacs-input.d.ts +6 -0
- package/dist/text-editing/emacs-input.d.ts.map +1 -1
- package/dist/text-editing/emacs-input.js +243 -17
- package/dist/text-editing/emacs-input.js.map +1 -1
- package/dist/user/curate-handler.d.ts.map +1 -1
- package/dist/user/curate-handler.js +4 -1
- package/dist/user/curate-handler.js.map +1 -1
- package/dist/user/curate-prompt.d.ts +2 -0
- package/dist/user/curate-prompt.d.ts.map +1 -1
- package/dist/user/curate-prompt.js.map +1 -1
- package/dist/user/handler.d.ts.map +1 -1
- package/dist/user/handler.js +2 -0
- package/dist/user/handler.js.map +1 -1
- package/dist/user/host-curate-prompt.d.ts +3 -0
- package/dist/user/host-curate-prompt.d.ts.map +1 -1
- package/dist/user/host-curate-prompt.js +153 -21
- package/dist/user/host-curate-prompt.js.map +1 -1
- package/dist/user/host-prompt.d.ts.map +1 -1
- package/dist/user/host-prompt.js +80 -5
- package/dist/user/host-prompt.js.map +1 -1
- package/dist/user/prompt.d.ts +2 -0
- package/dist/user/prompt.d.ts.map +1 -1
- package/dist/user/shortcut-hints.d.ts +1 -0
- package/dist/user/shortcut-hints.d.ts.map +1 -1
- package/dist/user/shortcut-hints.js +1 -1
- package/dist/user/shortcut-hints.js.map +1 -1
- package/dist/user/split-surface.d.ts +5 -1
- package/dist/user/split-surface.d.ts.map +1 -1
- package/dist/user/split-surface.js +15 -8
- package/dist/user/split-surface.js.map +1 -1
- package/package.json +1 -2
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { loadKoffi } from "../terminal/koffi-loader.js";
|
|
2
|
+
const CF_BITMAP = 2;
|
|
3
|
+
const CF_DIB = 8;
|
|
4
|
+
const CF_UNICODETEXT = 13;
|
|
5
|
+
const CF_HDROP = 15;
|
|
6
|
+
const CF_DIBV5 = 17;
|
|
7
|
+
const DRAG_QUERY_FILE_COUNT = 0xffffffff;
|
|
8
|
+
export function readClipboardViaFfi() {
|
|
9
|
+
const koffi = loadKoffi();
|
|
10
|
+
if (!koffi)
|
|
11
|
+
return null;
|
|
12
|
+
try {
|
|
13
|
+
const user32 = koffi.load("user32.dll");
|
|
14
|
+
const kernel32 = koffi.load("kernel32.dll");
|
|
15
|
+
const shell32 = koffi.load("shell32.dll");
|
|
16
|
+
const openClipboard = user32.func("OpenClipboard", "int", ["void *"]);
|
|
17
|
+
const closeClipboard = user32.func("CloseClipboard", "int", []);
|
|
18
|
+
const enumClipboardFormats = user32.func("EnumClipboardFormats", "uint32", ["uint32"]);
|
|
19
|
+
const getClipboardData = user32.func("GetClipboardData", "void *", ["uint32"]);
|
|
20
|
+
const registerClipboardFormatW = user32.func("RegisterClipboardFormatW", "uint32", ["str16"]);
|
|
21
|
+
const globalLock = kernel32.func("GlobalLock", "void *", ["void *"]);
|
|
22
|
+
const globalUnlock = kernel32.func("GlobalUnlock", "int", ["void *"]);
|
|
23
|
+
const globalSize = kernel32.func("GlobalSize", "size_t", ["void *"]);
|
|
24
|
+
const dragQueryFileW = shell32.func("DragQueryFileW", "uint32", ["void *", "uint32", "void *", "uint32"]);
|
|
25
|
+
if (!openClipboard(null))
|
|
26
|
+
return null;
|
|
27
|
+
try {
|
|
28
|
+
const formats = enumerateClipboardFormats(enumClipboardFormats);
|
|
29
|
+
const pngFormat = Number(registerClipboardFormatW("PNG"));
|
|
30
|
+
const fileContentsFormat = Number(registerClipboardFormatW("FileContents"));
|
|
31
|
+
const fileGroupDescriptorFormat = Number(registerClipboardFormatW("FileGroupDescriptorW"));
|
|
32
|
+
const pngBytes = formats.has(pngFormat)
|
|
33
|
+
? readGlobalBytes(koffi, getClipboardData, globalLock, globalUnlock, globalSize, pngFormat)
|
|
34
|
+
: null;
|
|
35
|
+
const hdropPaths = formats.has(CF_HDROP)
|
|
36
|
+
? readHdropPaths(koffi, getClipboardData, dragQueryFileW)
|
|
37
|
+
: null;
|
|
38
|
+
return {
|
|
39
|
+
pngBytes,
|
|
40
|
+
hasDib: formats.has(CF_DIB) || formats.has(CF_DIBV5) || formats.has(CF_BITMAP),
|
|
41
|
+
hdropPaths,
|
|
42
|
+
hasFileContents: formats.has(fileGroupDescriptorFormat) && formats.has(fileContentsFormat),
|
|
43
|
+
text: formats.has(CF_UNICODETEXT)
|
|
44
|
+
? readGlobalText(koffi, getClipboardData, globalLock, globalUnlock, globalSize, CF_UNICODETEXT)
|
|
45
|
+
: null,
|
|
46
|
+
};
|
|
47
|
+
}
|
|
48
|
+
finally {
|
|
49
|
+
closeClipboard();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function enumerateClipboardFormats(enumClipboardFormats) {
|
|
57
|
+
const formats = new Set();
|
|
58
|
+
let previous = 0;
|
|
59
|
+
for (;;) {
|
|
60
|
+
const next = Number(enumClipboardFormats(previous));
|
|
61
|
+
if (!next)
|
|
62
|
+
break;
|
|
63
|
+
formats.add(next);
|
|
64
|
+
previous = next;
|
|
65
|
+
}
|
|
66
|
+
return formats;
|
|
67
|
+
}
|
|
68
|
+
function readGlobalBytes(koffi, getClipboardData, globalLock, globalUnlock, globalSize, format) {
|
|
69
|
+
const handle = getClipboardData(format);
|
|
70
|
+
if (!handle)
|
|
71
|
+
return null;
|
|
72
|
+
const size = Number(globalSize(handle));
|
|
73
|
+
if (!Number.isFinite(size) || size <= 0)
|
|
74
|
+
return null;
|
|
75
|
+
const pointer = globalLock(handle);
|
|
76
|
+
if (!pointer)
|
|
77
|
+
return null;
|
|
78
|
+
try {
|
|
79
|
+
return Buffer.from(toByteArray(koffi.decode(pointer, "uint8", size)));
|
|
80
|
+
}
|
|
81
|
+
finally {
|
|
82
|
+
globalUnlock(handle);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
function readGlobalText(koffi, getClipboardData, globalLock, globalUnlock, globalSize, format) {
|
|
86
|
+
const handle = getClipboardData(format);
|
|
87
|
+
if (!handle)
|
|
88
|
+
return null;
|
|
89
|
+
const size = Number(globalSize(handle));
|
|
90
|
+
if (!Number.isFinite(size) || size <= 0)
|
|
91
|
+
return "";
|
|
92
|
+
const pointer = globalLock(handle);
|
|
93
|
+
if (!pointer)
|
|
94
|
+
return null;
|
|
95
|
+
try {
|
|
96
|
+
return trimAtNul(decodeWideString(koffi.decode(pointer, "char16", Math.floor(size / 2))));
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
globalUnlock(handle);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
function readHdropPaths(koffi, getClipboardData, dragQueryFileW) {
|
|
103
|
+
const handle = getClipboardData(CF_HDROP);
|
|
104
|
+
if (!handle)
|
|
105
|
+
return null;
|
|
106
|
+
const count = Number(dragQueryFileW(handle, DRAG_QUERY_FILE_COUNT, null, 0));
|
|
107
|
+
if (!Number.isFinite(count) || count <= 0)
|
|
108
|
+
return [];
|
|
109
|
+
const paths = [];
|
|
110
|
+
for (let index = 0; index < count; index++) {
|
|
111
|
+
const length = Number(dragQueryFileW(handle, index, null, 0));
|
|
112
|
+
if (!Number.isFinite(length) || length <= 0)
|
|
113
|
+
continue;
|
|
114
|
+
const buffer = koffi.alloc("char16", length + 1);
|
|
115
|
+
const copied = Number(dragQueryFileW(handle, index, buffer, length + 1));
|
|
116
|
+
const decodedLength = Number.isFinite(copied) && copied > 0 ? copied : length;
|
|
117
|
+
const path = trimAtNul(decodeWideString(koffi.decode(buffer, "char16", decodedLength + 1)));
|
|
118
|
+
if (path)
|
|
119
|
+
paths.push(path);
|
|
120
|
+
}
|
|
121
|
+
return paths;
|
|
122
|
+
}
|
|
123
|
+
function toByteArray(value) {
|
|
124
|
+
if (value instanceof Uint8Array)
|
|
125
|
+
return value;
|
|
126
|
+
if (Array.isArray(value))
|
|
127
|
+
return Uint8Array.from(value);
|
|
128
|
+
if (value instanceof ArrayBuffer)
|
|
129
|
+
return new Uint8Array(value);
|
|
130
|
+
return Buffer.from([]);
|
|
131
|
+
}
|
|
132
|
+
function decodeWideString(value) {
|
|
133
|
+
if (typeof value === "string")
|
|
134
|
+
return value;
|
|
135
|
+
if (value instanceof Uint16Array)
|
|
136
|
+
return String.fromCharCode(...value);
|
|
137
|
+
if (Array.isArray(value))
|
|
138
|
+
return String.fromCharCode(...value.map(Number));
|
|
139
|
+
if (value instanceof Uint8Array)
|
|
140
|
+
return Buffer.from(value).toString("utf16le");
|
|
141
|
+
if (Buffer.isBuffer(value))
|
|
142
|
+
return value.toString("utf16le");
|
|
143
|
+
return "";
|
|
144
|
+
}
|
|
145
|
+
function trimAtNul(value) {
|
|
146
|
+
const nul = value.indexOf("\0");
|
|
147
|
+
return nul >= 0 ? value.slice(0, nul) : value;
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=clipboard-ffi.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard-ffi.js","sourceRoot":"","sources":["../../src/paste-image/clipboard-ffi.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { type StagedImageFile } from "./staging.js";
|
|
2
|
+
export type ClipboardImageResult = {
|
|
3
|
+
kind: "image";
|
|
4
|
+
file: StagedImageFile;
|
|
5
|
+
} | {
|
|
6
|
+
kind: "none";
|
|
7
|
+
} | {
|
|
8
|
+
kind: "error";
|
|
9
|
+
message?: string;
|
|
10
|
+
};
|
|
11
|
+
export type ClipboardVirtualFileResult = {
|
|
12
|
+
kind: "none";
|
|
13
|
+
} | {
|
|
14
|
+
kind: "multiple";
|
|
15
|
+
} | {
|
|
16
|
+
kind: "empty";
|
|
17
|
+
fileName: string;
|
|
18
|
+
} | {
|
|
19
|
+
kind: "present";
|
|
20
|
+
fileName: string;
|
|
21
|
+
path: string;
|
|
22
|
+
};
|
|
23
|
+
export interface ClipboardCommandRunner {
|
|
24
|
+
execFile(file: string, args: readonly string[], opts?: {
|
|
25
|
+
encoding?: BufferEncoding | "buffer";
|
|
26
|
+
windowsHide?: boolean;
|
|
27
|
+
}): Promise<{
|
|
28
|
+
stdout: Buffer | string;
|
|
29
|
+
stderr: Buffer | string;
|
|
30
|
+
}>;
|
|
31
|
+
}
|
|
32
|
+
export declare function registerSlowClipboardSpawn(listener: () => void): () => void;
|
|
33
|
+
export declare function readClipboardImage(runner?: ClipboardCommandRunner): Promise<ClipboardImageResult>;
|
|
34
|
+
export declare function readClipboardText(runner?: ClipboardCommandRunner): Promise<string>;
|
|
35
|
+
export declare function readClipboardFileList(runner?: ClipboardCommandRunner): Promise<string[]>;
|
|
36
|
+
export declare function readClipboardVirtualFile(runner?: ClipboardCommandRunner): Promise<ClipboardVirtualFileResult>;
|
|
37
|
+
export declare function __resetClipboardSnapshotForTests(): void;
|
|
38
|
+
//# sourceMappingURL=clipboard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../../src/paste-image/clipboard.ts"],"names":[],"mappings":"AAQA,OAAO,EAAoB,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAOtE,MAAM,MAAM,oBAAoB,GAC5B;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,eAAe,CAAA;CAAE,GACxC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,OAAO,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAExC,MAAM,MAAM,0BAA0B,GAClC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,UAAU,CAAA;CAAE,GACpB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GACnC;IAAE,IAAI,EAAE,SAAS,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAExD,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CACN,IAAI,EAAE,MAAM,EACZ,IAAI,EAAE,SAAS,MAAM,EAAE,EACvB,IAAI,CAAC,EAAE;QAAE,QAAQ,CAAC,EAAE,cAAc,GAAG,QAAQ,CAAC;QAAC,WAAW,CAAC,EAAE,OAAO,CAAA;KAAE,GACrE,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC,CAAC;CAClE;AAcD,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,MAAM,IAAI,GAAG,MAAM,IAAI,CAU3E;AAwBD,wBAAsB,kBAAkB,CACtC,MAAM,GAAE,sBAAsC,GAC7C,OAAO,CAAC,oBAAoB,CAAC,CAe/B;AAED,wBAAsB,iBAAiB,CACrC,MAAM,GAAE,sBAAsC,GAC7C,OAAO,CAAC,MAAM,CAAC,CA0BjB;AAED,wBAAsB,qBAAqB,CACzC,MAAM,GAAE,sBAAsC,GAC7C,OAAO,CAAC,MAAM,EAAE,CAAC,CAMnB;AAED,wBAAsB,wBAAwB,CAC5C,MAAM,GAAE,sBAAsC,GAC7C,OAAO,CAAC,0BAA0B,CAAC,CAMrC;AAmRD,wBAAgB,gCAAgC,IAAI,IAAI,CAEvD"}
|
|
@@ -0,0 +1,454 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { existsSync } from "node:fs";
|
|
3
|
+
import { stat, unlink } from "node:fs/promises";
|
|
4
|
+
import { promisify } from "node:util";
|
|
5
|
+
import { platform } from "node:os";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
import { readClipboardViaFfi } from "./clipboard-ffi.js";
|
|
8
|
+
import { detectImageFormatFromFile } from "./image-format.js";
|
|
9
|
+
import { writeImageToTemp } from "./staging.js";
|
|
10
|
+
const execFileAsync = promisify(execFile);
|
|
11
|
+
let slowClipboardSpawnRegistration;
|
|
12
|
+
const defaultRunner = {
|
|
13
|
+
async execFile(file, args, opts) {
|
|
14
|
+
notifySlowClipboardSpawn();
|
|
15
|
+
const result = await execFileAsync(file, [...args], {
|
|
16
|
+
encoding: opts?.encoding ?? "buffer",
|
|
17
|
+
windowsHide: opts?.windowsHide ?? true,
|
|
18
|
+
maxBuffer: 32 * 1024 * 1024,
|
|
19
|
+
});
|
|
20
|
+
return result;
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
export function registerSlowClipboardSpawn(listener) {
|
|
24
|
+
// Per-registration token so an older release can never clear a newer
|
|
25
|
+
// registration (last-registration-wins, even for the same listener fn).
|
|
26
|
+
const registration = { listener };
|
|
27
|
+
slowClipboardSpawnRegistration = registration;
|
|
28
|
+
return () => {
|
|
29
|
+
if (slowClipboardSpawnRegistration === registration) {
|
|
30
|
+
slowClipboardSpawnRegistration = undefined;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function notifySlowClipboardSpawn() {
|
|
35
|
+
const registration = slowClipboardSpawnRegistration;
|
|
36
|
+
slowClipboardSpawnRegistration = undefined; // fire at most once per registration
|
|
37
|
+
try {
|
|
38
|
+
registration?.listener();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Progress notifications must not change clipboard read behaviour.
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
let ffiSnapshot;
|
|
45
|
+
let consolidatedRead;
|
|
46
|
+
export async function readClipboardImage(runner = defaultRunner) {
|
|
47
|
+
if (runner === defaultRunner)
|
|
48
|
+
resetClipboardSnapshotForPaste();
|
|
49
|
+
try {
|
|
50
|
+
if (usesWindowsClipboardBridge()) {
|
|
51
|
+
return await readWindowsClipboardImageResult(runner);
|
|
52
|
+
}
|
|
53
|
+
const file = await readPlatformClipboardImage(runner);
|
|
54
|
+
return await validateClipboardImageFile(file);
|
|
55
|
+
}
|
|
56
|
+
catch (err) {
|
|
57
|
+
return isMissingClipboardCommandError(err)
|
|
58
|
+
? { kind: "none" }
|
|
59
|
+
: { kind: "error", message: err instanceof Error ? err.message : String(err) };
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
export async function readClipboardText(runner = defaultRunner) {
|
|
63
|
+
try {
|
|
64
|
+
if (usesWindowsClipboardBridge()) {
|
|
65
|
+
return await readWindowsClipboardText(runner);
|
|
66
|
+
}
|
|
67
|
+
if (platform() === "darwin") {
|
|
68
|
+
const { stdout } = await runner.execFile("pbpaste", [], { encoding: "utf8" });
|
|
69
|
+
return String(stdout ?? "");
|
|
70
|
+
}
|
|
71
|
+
if (existsSync("/usr/bin/wl-paste")) {
|
|
72
|
+
const { stdout } = await runner.execFile("wl-paste", ["--no-newline"], { encoding: "utf8" });
|
|
73
|
+
return String(stdout ?? "");
|
|
74
|
+
}
|
|
75
|
+
const { stdout } = await runner.execFile("xclip", ["-selection", "clipboard", "-o"], { encoding: "utf8" });
|
|
76
|
+
return String(stdout ?? "");
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
return "";
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
export async function readClipboardFileList(runner = defaultRunner) {
|
|
83
|
+
try {
|
|
84
|
+
return await readPlatformClipboardFileList(runner);
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return [];
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
export async function readClipboardVirtualFile(runner = defaultRunner) {
|
|
91
|
+
try {
|
|
92
|
+
return await readPlatformClipboardVirtualFile(runner);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return { kind: "none" };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async function readPlatformClipboardImage(runner) {
|
|
99
|
+
if (platform() === "win32")
|
|
100
|
+
return readWindowsClipboardImage(runner);
|
|
101
|
+
if (platform() === "darwin")
|
|
102
|
+
return readMacClipboardImage(runner);
|
|
103
|
+
return readLinuxClipboardImage(runner);
|
|
104
|
+
}
|
|
105
|
+
async function readPlatformClipboardFileList(runner) {
|
|
106
|
+
if (platform() === "win32")
|
|
107
|
+
return readWindowsClipboardFileList(runner);
|
|
108
|
+
if (platform() === "darwin")
|
|
109
|
+
return readMacClipboardFileList(runner);
|
|
110
|
+
return readLinuxClipboardFileList(runner);
|
|
111
|
+
}
|
|
112
|
+
async function readPlatformClipboardVirtualFile(runner) {
|
|
113
|
+
if (platform() === "win32")
|
|
114
|
+
return readWindowsClipboardVirtualFile(runner);
|
|
115
|
+
if (platform() === "darwin")
|
|
116
|
+
return { kind: "none" };
|
|
117
|
+
return readLinuxClipboardVirtualFile(runner);
|
|
118
|
+
}
|
|
119
|
+
async function readWindowsClipboardImage(runner) {
|
|
120
|
+
const result = await readWindowsClipboardImageResult(runner);
|
|
121
|
+
return result.kind === "image" ? result.file : null;
|
|
122
|
+
}
|
|
123
|
+
async function readWindowsClipboardImageResult(runner) {
|
|
124
|
+
const snapshot = getFfiSnapshot(runner);
|
|
125
|
+
if (snapshot) {
|
|
126
|
+
if (snapshot.pngBytes) {
|
|
127
|
+
const file = await writeImageToTemp(snapshot.pngBytes, "png");
|
|
128
|
+
const result = await validateClipboardImageFile(file);
|
|
129
|
+
if (result.kind === "image")
|
|
130
|
+
return result;
|
|
131
|
+
await unlink(file.path).catch(() => { });
|
|
132
|
+
if (snapshot.hasDib) {
|
|
133
|
+
return resolveDibImage(snapshot, runner);
|
|
134
|
+
}
|
|
135
|
+
return { kind: "error" };
|
|
136
|
+
}
|
|
137
|
+
if (snapshot.hasDib) {
|
|
138
|
+
return resolveDibImage(snapshot, runner);
|
|
139
|
+
}
|
|
140
|
+
return { kind: "none" };
|
|
141
|
+
}
|
|
142
|
+
return validateConsolidatedImage(await getConsolidatedClipboard(runner));
|
|
143
|
+
}
|
|
144
|
+
async function resolveDibImage(snapshot, runner) {
|
|
145
|
+
const preferred = await preferSingleHdropImage(snapshot);
|
|
146
|
+
if (preferred)
|
|
147
|
+
return preferred;
|
|
148
|
+
return validateConsolidatedImage(await getConsolidatedClipboard(runner), {
|
|
149
|
+
detectedImageSource: true,
|
|
150
|
+
});
|
|
151
|
+
}
|
|
152
|
+
async function preferSingleHdropImage(snapshot) {
|
|
153
|
+
// Copied image files often publish both a DIB and the original file path.
|
|
154
|
+
const paths = snapshot.hdropPaths;
|
|
155
|
+
if (!paths || paths.length !== 1)
|
|
156
|
+
return null;
|
|
157
|
+
const path = paths[0];
|
|
158
|
+
const format = await detectImageFormatFromFile(path).catch(() => null);
|
|
159
|
+
if (!format)
|
|
160
|
+
return null;
|
|
161
|
+
return { kind: "image", file: { path, ext: format } };
|
|
162
|
+
}
|
|
163
|
+
async function validateConsolidatedImage(result, opts = {}) {
|
|
164
|
+
if (result.kind !== "image")
|
|
165
|
+
return opts.detectedImageSource ? { kind: "error" } : { kind: "none" };
|
|
166
|
+
return validateClipboardImageFile(result.file);
|
|
167
|
+
}
|
|
168
|
+
async function validateClipboardImageFile(file) {
|
|
169
|
+
if (!file)
|
|
170
|
+
return { kind: "none" };
|
|
171
|
+
const info = await stat(file.path).catch(() => null);
|
|
172
|
+
if (!info || info.size === 0)
|
|
173
|
+
return { kind: "error" };
|
|
174
|
+
const format = await detectImageFormatFromFile(file.path).catch(() => null);
|
|
175
|
+
if (format !== "png")
|
|
176
|
+
return { kind: "error" };
|
|
177
|
+
return { kind: "image", file };
|
|
178
|
+
}
|
|
179
|
+
async function readWindowsClipboardText(runner) {
|
|
180
|
+
const snapshot = getFfiSnapshot(runner);
|
|
181
|
+
if (snapshot) {
|
|
182
|
+
if (snapshot.hasDib || snapshot.hasFileContents)
|
|
183
|
+
return "";
|
|
184
|
+
return snapshot.text ?? "";
|
|
185
|
+
}
|
|
186
|
+
const result = await getConsolidatedClipboard(runner);
|
|
187
|
+
return result.kind === "text" ? result.text : "";
|
|
188
|
+
}
|
|
189
|
+
async function readWindowsClipboardFileList(runner) {
|
|
190
|
+
const snapshot = getFfiSnapshot(runner);
|
|
191
|
+
if (snapshot) {
|
|
192
|
+
if (snapshot.hasDib)
|
|
193
|
+
return [];
|
|
194
|
+
if (!snapshot.hasFileContents)
|
|
195
|
+
return snapshot.hdropPaths ?? [];
|
|
196
|
+
const result = await getConsolidatedClipboard(runner);
|
|
197
|
+
return result.kind === "filelist" ? result.paths : [];
|
|
198
|
+
}
|
|
199
|
+
const result = await getConsolidatedClipboard(runner);
|
|
200
|
+
return result.kind === "filelist" ? result.paths : [];
|
|
201
|
+
}
|
|
202
|
+
async function readWindowsClipboardVirtualFile(runner) {
|
|
203
|
+
const snapshot = getFfiSnapshot(runner);
|
|
204
|
+
if (snapshot && !snapshot.hasFileContents)
|
|
205
|
+
return { kind: "none" };
|
|
206
|
+
const result = await getConsolidatedClipboard(runner);
|
|
207
|
+
if (result.kind === "virtual") {
|
|
208
|
+
return { kind: "present", fileName: result.fileName, path: result.path };
|
|
209
|
+
}
|
|
210
|
+
if (result.kind === "virtual-empty") {
|
|
211
|
+
return { kind: "empty", fileName: result.fileName };
|
|
212
|
+
}
|
|
213
|
+
if (result.kind === "multiple")
|
|
214
|
+
return { kind: "multiple" };
|
|
215
|
+
if (snapshot?.hasFileContents)
|
|
216
|
+
return { kind: "multiple" };
|
|
217
|
+
return { kind: "none" };
|
|
218
|
+
}
|
|
219
|
+
async function runConsolidatedClipboard(runner) {
|
|
220
|
+
const temp = await writeImageToTemp(Buffer.alloc(0), "png");
|
|
221
|
+
const outputPath = escapePowerShellSingleQuoted(temp.path);
|
|
222
|
+
const script = [
|
|
223
|
+
"Add-Type -AssemblyName System.Windows.Forms,System.Drawing;",
|
|
224
|
+
"$img=[System.Windows.Forms.Clipboard]::GetImage();",
|
|
225
|
+
"if ($null -ne $img) {",
|
|
226
|
+
`$img.Save('${outputPath}',[System.Drawing.Imaging.ImageFormat]::Png);`,
|
|
227
|
+
"[Console]::Out.WriteLine('kind=image');",
|
|
228
|
+
"exit 0",
|
|
229
|
+
"}",
|
|
230
|
+
"$d=[System.Windows.Forms.Clipboard]::GetDataObject();",
|
|
231
|
+
"if ($null -ne $d -and $d.GetDataPresent('FileGroupDescriptorW') -and $d.GetDataPresent('FileContents')) {",
|
|
232
|
+
"$fgd=$d.GetData('FileGroupDescriptorW');",
|
|
233
|
+
"if ($null -ne $fgd) {",
|
|
234
|
+
"$fgdBytes=$null;",
|
|
235
|
+
"if ($fgd -is [byte[]]) { $fgdBytes=$fgd } else { $ms=New-Object System.IO.MemoryStream; $fgd.CopyTo($ms); $fgdBytes=$ms.ToArray() }",
|
|
236
|
+
"if ($fgdBytes.Length -ge 4) {",
|
|
237
|
+
"$count=[BitConverter]::ToUInt32($fgdBytes,0);",
|
|
238
|
+
"if ($count -ge 2) { exit 3 }",
|
|
239
|
+
"if ($count -eq 1) {",
|
|
240
|
+
"$nameStart=76;",
|
|
241
|
+
"$nameLen=[Math]::Min(520,[Math]::Max(0,$fgdBytes.Length-$nameStart));",
|
|
242
|
+
"$fileName='';",
|
|
243
|
+
"if ($nameLen -gt 0) { $fileName=[System.Text.Encoding]::Unicode.GetString($fgdBytes,$nameStart,$nameLen); $nul=$fileName.IndexOf([char]0); if ($nul -ge 0) { $fileName=$fileName.Substring(0,$nul) } }",
|
|
244
|
+
"$wrote=$false;",
|
|
245
|
+
"try {",
|
|
246
|
+
"$contents=$d.GetData('FileContents');",
|
|
247
|
+
"if ($null -ne $contents) {",
|
|
248
|
+
`$fs=[System.IO.File]::Open('${outputPath}',[System.IO.FileMode]::Create,[System.IO.FileAccess]::Write,[System.IO.FileShare]::None);`,
|
|
249
|
+
"try { if ($contents -is [byte[]]) { if ($contents.Length -gt 0) { $fs.Write($contents,0,$contents.Length); $wrote=$true } } else { $before=$fs.Position; $contents.CopyTo($fs); if ($fs.Position -gt $before) { $wrote=$true } } } finally { $fs.Close() }",
|
|
250
|
+
"}",
|
|
251
|
+
"} catch {}",
|
|
252
|
+
"if ($wrote) {",
|
|
253
|
+
"[Console]::Out.WriteLine('kind=virtual');",
|
|
254
|
+
"[Console]::Out.WriteLine($fileName);",
|
|
255
|
+
"exit 0",
|
|
256
|
+
"}",
|
|
257
|
+
"$files=[System.Windows.Forms.Clipboard]::GetFileDropList();",
|
|
258
|
+
"if ($null -ne $files -and $files.Count -gt 0) {",
|
|
259
|
+
"[Console]::Out.WriteLine('kind=filelist');",
|
|
260
|
+
"foreach ($file in $files) { [Console]::Out.WriteLine($file) }",
|
|
261
|
+
"exit 0",
|
|
262
|
+
"}",
|
|
263
|
+
"[Console]::Out.WriteLine('kind=virtual-empty');",
|
|
264
|
+
"[Console]::Out.WriteLine($fileName);",
|
|
265
|
+
"exit 0",
|
|
266
|
+
"}",
|
|
267
|
+
"}",
|
|
268
|
+
"}",
|
|
269
|
+
"}",
|
|
270
|
+
"$files=[System.Windows.Forms.Clipboard]::GetFileDropList();",
|
|
271
|
+
"if ($null -ne $files -and $files.Count -gt 0) {",
|
|
272
|
+
"[Console]::Out.WriteLine('kind=filelist');",
|
|
273
|
+
"foreach ($file in $files) { [Console]::Out.WriteLine($file) }",
|
|
274
|
+
"exit 0",
|
|
275
|
+
"}",
|
|
276
|
+
"$text=Get-Clipboard -Raw;",
|
|
277
|
+
"if ($null -ne $text) {",
|
|
278
|
+
"[Console]::Out.WriteLine('kind=text');",
|
|
279
|
+
"[Console]::Out.Write($text);",
|
|
280
|
+
"exit 0",
|
|
281
|
+
"}",
|
|
282
|
+
"exit 2",
|
|
283
|
+
].join(" ");
|
|
284
|
+
try {
|
|
285
|
+
const { stdout } = await runner.execFile("powershell.exe", ["-NoProfile", "-Command", script], { encoding: "utf8", windowsHide: true });
|
|
286
|
+
return parseConsolidatedClipboardOutput(String(stdout ?? ""), temp);
|
|
287
|
+
}
|
|
288
|
+
catch (err) {
|
|
289
|
+
await unlink(temp.path).catch(() => { });
|
|
290
|
+
if (isCommandExitStatus(err, 2)) {
|
|
291
|
+
return { kind: "none" };
|
|
292
|
+
}
|
|
293
|
+
if (isCommandExitStatus(err, 3))
|
|
294
|
+
return { kind: "multiple" };
|
|
295
|
+
throw err;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
function parseConsolidatedClipboardOutput(stdout, temp) {
|
|
299
|
+
const lineEnd = stdout.search(/\r?\n/);
|
|
300
|
+
const firstLine = lineEnd >= 0 ? stdout.slice(0, lineEnd).replace(/\r$/, "") : stdout.trim();
|
|
301
|
+
const rest = lineEnd >= 0 ? stdout.slice(lineEnd + (stdout[lineEnd] === "\r" ? 2 : 1)) : "";
|
|
302
|
+
if (firstLine === "kind=image")
|
|
303
|
+
return { kind: "image", file: temp };
|
|
304
|
+
if (firstLine === "kind=virtual") {
|
|
305
|
+
const fileName = rest.split(/\r?\n/)[0] ?? "";
|
|
306
|
+
return { kind: "virtual", fileName, path: temp.path };
|
|
307
|
+
}
|
|
308
|
+
void unlink(temp.path).catch(() => { });
|
|
309
|
+
if (firstLine === "kind=virtual-empty") {
|
|
310
|
+
const fileName = rest.split(/\r?\n/)[0] ?? "";
|
|
311
|
+
return { kind: "virtual-empty", fileName };
|
|
312
|
+
}
|
|
313
|
+
if (firstLine === "kind=filelist") {
|
|
314
|
+
return { kind: "filelist", paths: parseClipboardFileList(rest) };
|
|
315
|
+
}
|
|
316
|
+
if (firstLine === "kind=text")
|
|
317
|
+
return { kind: "text", text: rest };
|
|
318
|
+
return { kind: "none" };
|
|
319
|
+
}
|
|
320
|
+
function getFfiSnapshot(runner) {
|
|
321
|
+
if (runner !== defaultRunner || platform() !== "win32")
|
|
322
|
+
return null;
|
|
323
|
+
if (ffiSnapshot === undefined)
|
|
324
|
+
ffiSnapshot = readClipboardViaFfi();
|
|
325
|
+
return ffiSnapshot;
|
|
326
|
+
}
|
|
327
|
+
function getConsolidatedClipboard(runner) {
|
|
328
|
+
if (runner !== defaultRunner)
|
|
329
|
+
return runConsolidatedClipboard(runner);
|
|
330
|
+
consolidatedRead ??= runConsolidatedClipboard(runner);
|
|
331
|
+
return consolidatedRead;
|
|
332
|
+
}
|
|
333
|
+
function resetClipboardSnapshotForPaste() {
|
|
334
|
+
ffiSnapshot = undefined;
|
|
335
|
+
consolidatedRead = undefined;
|
|
336
|
+
}
|
|
337
|
+
export function __resetClipboardSnapshotForTests() {
|
|
338
|
+
resetClipboardSnapshotForPaste();
|
|
339
|
+
}
|
|
340
|
+
function usesWindowsClipboardBridge() {
|
|
341
|
+
return platform() === "win32" || (platform() === "linux" && Boolean(process.env.WSL_DISTRO_NAME));
|
|
342
|
+
}
|
|
343
|
+
async function readMacClipboardImage(runner) {
|
|
344
|
+
const temp = await writeImageToTemp(Buffer.alloc(0), "png");
|
|
345
|
+
try {
|
|
346
|
+
await runner.execFile("pngpaste", [temp.path], { encoding: "utf8" });
|
|
347
|
+
}
|
|
348
|
+
catch (err) {
|
|
349
|
+
if (isCommandExitStatus(err, 1)) {
|
|
350
|
+
await unlink(temp.path).catch(() => { });
|
|
351
|
+
return null;
|
|
352
|
+
}
|
|
353
|
+
throw err;
|
|
354
|
+
}
|
|
355
|
+
return temp;
|
|
356
|
+
}
|
|
357
|
+
async function readMacClipboardFileList(runner) {
|
|
358
|
+
const script = [
|
|
359
|
+
"set output to \"\"",
|
|
360
|
+
"try",
|
|
361
|
+
"set clipItems to the clipboard as \u00abclass furl\u00bb",
|
|
362
|
+
"on error",
|
|
363
|
+
"return \"\"",
|
|
364
|
+
"end try",
|
|
365
|
+
"if class of clipItems is list then",
|
|
366
|
+
"repeat with clipItem in clipItems",
|
|
367
|
+
"set output to output & POSIX path of clipItem & linefeed",
|
|
368
|
+
"end repeat",
|
|
369
|
+
"else",
|
|
370
|
+
"set output to POSIX path of clipItems & linefeed",
|
|
371
|
+
"end if",
|
|
372
|
+
"return output",
|
|
373
|
+
];
|
|
374
|
+
const { stdout } = await runner.execFile("osascript", script.flatMap((line) => ["-e", line]), {
|
|
375
|
+
encoding: "utf8",
|
|
376
|
+
});
|
|
377
|
+
return parseClipboardFileList(String(stdout ?? ""));
|
|
378
|
+
}
|
|
379
|
+
async function readLinuxClipboardImage(runner) {
|
|
380
|
+
if (process.env.WSL_DISTRO_NAME) {
|
|
381
|
+
return readWindowsClipboardImage(runner);
|
|
382
|
+
}
|
|
383
|
+
if (existsSync("/usr/bin/wl-paste")) {
|
|
384
|
+
let stdout;
|
|
385
|
+
try {
|
|
386
|
+
({ stdout } = await runner.execFile("wl-paste", ["--type", "image/png", "--no-newline"], { encoding: "buffer" }));
|
|
387
|
+
}
|
|
388
|
+
catch (err) {
|
|
389
|
+
if (isCommandExitStatus(err, 1))
|
|
390
|
+
return null;
|
|
391
|
+
throw err;
|
|
392
|
+
}
|
|
393
|
+
return writeImageToTemp(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout), "png");
|
|
394
|
+
}
|
|
395
|
+
let stdout;
|
|
396
|
+
try {
|
|
397
|
+
({ stdout } = await runner.execFile("xclip", ["-selection", "clipboard", "-t", "image/png", "-o"], { encoding: "buffer" }));
|
|
398
|
+
}
|
|
399
|
+
catch (err) {
|
|
400
|
+
if (isCommandExitStatus(err, 1))
|
|
401
|
+
return null;
|
|
402
|
+
throw err;
|
|
403
|
+
}
|
|
404
|
+
return writeImageToTemp(Buffer.isBuffer(stdout) ? stdout : Buffer.from(stdout), "png");
|
|
405
|
+
}
|
|
406
|
+
async function readLinuxClipboardFileList(runner) {
|
|
407
|
+
if (process.env.WSL_DISTRO_NAME) {
|
|
408
|
+
return readWindowsClipboardFileList(runner);
|
|
409
|
+
}
|
|
410
|
+
if (existsSync("/usr/bin/wl-paste")) {
|
|
411
|
+
const { stdout } = await runner.execFile("wl-paste", ["--type", "text/uri-list"], { encoding: "utf8" });
|
|
412
|
+
return parseClipboardFileList(String(stdout ?? ""), { skipComments: true });
|
|
413
|
+
}
|
|
414
|
+
const { stdout } = await runner.execFile("xclip", ["-selection", "clipboard", "-t", "text/uri-list", "-o"], { encoding: "utf8" });
|
|
415
|
+
return parseClipboardFileList(String(stdout ?? ""), { skipComments: true });
|
|
416
|
+
}
|
|
417
|
+
async function readLinuxClipboardVirtualFile(runner) {
|
|
418
|
+
if (process.env.WSL_DISTRO_NAME) {
|
|
419
|
+
return readWindowsClipboardVirtualFile(runner);
|
|
420
|
+
}
|
|
421
|
+
return { kind: "none" };
|
|
422
|
+
}
|
|
423
|
+
function isMissingClipboardCommandError(err) {
|
|
424
|
+
const code = err.code;
|
|
425
|
+
return code === "ENOENT";
|
|
426
|
+
}
|
|
427
|
+
function isCommandExitStatus(err, expected) {
|
|
428
|
+
const status = err.status;
|
|
429
|
+
if (status === expected)
|
|
430
|
+
return true;
|
|
431
|
+
const code = err.code;
|
|
432
|
+
return code === expected;
|
|
433
|
+
}
|
|
434
|
+
function escapePowerShellSingleQuoted(value) {
|
|
435
|
+
return value.replace(/'/g, "''");
|
|
436
|
+
}
|
|
437
|
+
function parseClipboardFileList(value, opts = {}) {
|
|
438
|
+
return value
|
|
439
|
+
.split(/\r?\n/)
|
|
440
|
+
.map((line) => line.trim())
|
|
441
|
+
.filter((line) => line.length > 0)
|
|
442
|
+
.filter((line) => !opts.skipComments || !line.startsWith("#"))
|
|
443
|
+
.map((line) => clipboardFileListLineToPath(line))
|
|
444
|
+
.filter((path) => path !== null);
|
|
445
|
+
}
|
|
446
|
+
function clipboardFileListLineToPath(line) {
|
|
447
|
+
if (/^file:/i.test(line))
|
|
448
|
+
return fileURLToPath(line);
|
|
449
|
+
if (/^[a-z][a-z0-9+.-]*:/i.test(line) && !/^[a-z]:[\\/]/i.test(line)) {
|
|
450
|
+
return null;
|
|
451
|
+
}
|
|
452
|
+
return line;
|
|
453
|
+
}
|
|
454
|
+
//# sourceMappingURL=clipboard.js.map
|