Skip to content

Commit 392488b

Browse files
committed
Fix WebSocket utilities and Add OSC 52 clipboard support
- Fix WebSocket utilities timing issue: * Replace async import() with synchronous require() for Node.js 'ws' module * Prevents race condition where newWebSocket was called before import resolved * Fixes fallback to browser WebSocket in Node.js environment - Add handleOsc52Command() for set/query/clear clipboard operations - Register OSC 52 handler in TermWrap constructor - Support base64 encoding/decoding for clipboard data - Add error handling for clipboard access failures
1 parent 0d339af commit 392488b

File tree

3 files changed

+60
-7
lines changed

3 files changed

+60
-7
lines changed

frontend/app/view/term/termwrap.ts

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,55 @@ function handleOsc7Command(data: string, blockId: string, loaded: boolean): bool
134134
return true;
135135
}
136136

137+
function handleOsc52Command(data: string, terminal: Terminal, loaded: boolean): boolean {
138+
if (!loaded) {
139+
return false;
140+
}
141+
if (!data || data.length === 0) {
142+
console.log("Invalid OSC 52 command received (empty)");
143+
return false;
144+
}
145+
146+
// OSC 52 format: "c;{base64_data}" or "c;?" (query) or "c" (clear)
147+
const parts = data.split(";", 2);
148+
if (parts.length < 1 || parts[0] !== "c") {
149+
console.log("Invalid OSC 52 command received (unsupported clipboard)", data);
150+
return false;
151+
}
152+
153+
if (parts.length === 1 || parts[1] === "") {
154+
// Clear clipboard - not implemented
155+
return true;
156+
}
157+
158+
if (parts[1] === "?") {
159+
// Query clipboard - read from clipboard and send back
160+
navigator.clipboard.readText().then((text) => {
161+
const base64Data = btoa(unescape(encodeURIComponent(text)));
162+
// Send OSC 52 response back to terminal
163+
terminal.write(`\x1b]52;c;${base64Data}\x07`);
164+
}).catch((e) => {
165+
console.log("Failed to read clipboard:", e);
166+
});
167+
return true;
168+
}
169+
170+
// Set clipboard data
171+
try {
172+
const decodedData = decodeURIComponent(escape(atob(parts[1])));
173+
navigator.clipboard.writeText(decodedData).then(() => {
174+
// Successfully wrote to clipboard
175+
}).catch((e) => {
176+
console.log("Failed to write to clipboard:", e);
177+
});
178+
} catch (e) {
179+
console.log("Failed to decode OSC 52 data:", e);
180+
return false;
181+
}
182+
183+
return true;
184+
}
185+
137186
export class TermWrap {
138187
blockId: string;
139188
ptyOffset: number;
@@ -211,6 +260,9 @@ export class TermWrap {
211260
this.terminal.parser.registerOscHandler(7, (data: string) => {
212261
return handleOsc7Command(data, this.blockId, this.loaded);
213262
});
263+
this.terminal.parser.registerOscHandler(52, (data: string) => {
264+
return handleOsc52Command(data, this.terminal, this.loaded);
265+
});
214266
this.terminal.attachCustomKeyEventHandler(waveOptions.keydownHandler);
215267
this.connectElem = connectElem;
216268
this.mainFileSubject = null;

frontend/types/gotypes.d.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -559,7 +559,6 @@ declare global {
559559
"editor:minimapenabled"?: boolean;
560560
"editor:stickyscrollenabled"?: boolean;
561561
"editor:wordwrap"?: boolean;
562-
"editor:fontsize"?: number;
563562
"graph:*"?: boolean;
564563
"graph:numpoints"?: number;
565564
"graph:metrics"?: string[];

frontend/util/wsutil.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,14 @@ import type { WebSocket as NodeWebSocketType } from "ws";
66
let NodeWebSocket: typeof NodeWebSocketType = null;
77

88
if (typeof window === "undefined") {
9-
// Necessary to avoid issues with Rollup: https://github.com/websockets/ws/issues/2057
10-
import("ws")
11-
.then((ws) => (NodeWebSocket = ws.default))
12-
.catch((e) => {
13-
console.log("Error importing 'ws':", e);
14-
});
9+
// Synchronous require in Node.js (Electron main process)
10+
// The async import was causing timing issues where newWebSocket was called
11+
// before the dynamic import resolved, falling back to browser WebSocket which doesn't exist in Node.js
12+
try {
13+
NodeWebSocket = require("ws");
14+
} catch (e) {
15+
console.log("Error importing 'ws':", e);
16+
}
1517
}
1618

1719
type ComboWebSocket = NodeWebSocketType | WebSocket;

0 commit comments

Comments
 (0)