diff --git a/addons/addon-clipboard/src/ClipboardAddon.ts b/addons/addon-clipboard/src/ClipboardAddon.ts index b6dad8a5..58425924 100644 --- a/addons/addon-clipboard/src/ClipboardAddon.ts +++ b/addons/addon-clipboard/src/ClipboardAddon.ts @@ -4,14 +4,17 @@ */ import type { IDisposable, ITerminalAddon, Terminal } from '@xterm/xterm'; -import { type IClipboardProvider, ClipboardSelectionType } from '@xterm/addon-clipboard'; +import { type IClipboardProvider, ClipboardSelectionType, type IBase64 } from '@xterm/addon-clipboard'; import { Base64 as JSBase64 } from 'js-base64'; export class ClipboardAddon implements ITerminalAddon { private _terminal?: Terminal; private _disposable?: IDisposable; - constructor(private _provider: IClipboardProvider = new ClipboardProvider()) {} + constructor( + private _base64: IBase64 = new Base64(), + private _provider: IClipboardProvider = new BrowserClipboardProvider() + ) {} public activate(terminal: Terminal): void { this._terminal = terminal; @@ -22,90 +25,66 @@ export class ClipboardAddon implements ITerminalAddon { return this._disposable?.dispose(); } + private _readText(sel: ClipboardSelectionType, data: string): void { + const b64 = this._base64.encodeText(data); + this._terminal?.input(`\x1b]52;${sel};${b64}\x07`, false); + } + private _setOrReportClipboard(data: string): boolean | Promise { const args = data.split(';'); if (args.length < 2) { return true; } - const pc = args[0]; + const pc = args[0] as ClipboardSelectionType; const pd = args[1]; - switch (pc) { - case ClipboardSelectionType.SYSTEM: - case ClipboardSelectionType.PRIMARY: - try { - if (pd === '?') { - // Report clipboard - return this._provider.readText(pc).then((data) => { - this._terminal?.input(`\x1b]52;${pc};${data}\x07`, false); - return true; - }); - } - return this._provider.writeText(pc, pd).then(() => true); - } catch (e) { - console.error(e); - } + if (pd === '?') { + const text = this._provider.readText(pc); + + // Report clipboard + if (text instanceof Promise) { + return text.then((data) => { + this._readText(pc, data); + return true; + }); + } + + this._readText(pc, text); + return true; + } + + // Clear clipboard if text is not a base64 encoded string. + let text = ''; + try { + text = this._base64.decodeText(pd); + } catch {} + + + const result = this._provider.writeText(pc, text); + if (result instanceof Promise) { + return result.then(() => true); } return true; } } -export class ClipboardProvider implements IClipboardProvider { - private _base64: IBase64; - public limit: number; - - constructor( - /** - * The base64 encoder/decoder to use. - */ - base64: IBase64 = new Base64(), - - /** - * The maximum amount of data that can be copied to the clipboard. - * Zero means no limit. - */ - limit: number = 0 // unlimited - ) { - this._base64 = base64; - this.limit = limit; - } - - public readText(selection: ClipboardSelectionType): Promise { +export class BrowserClipboardProvider implements IClipboardProvider { + public async readText(selection: ClipboardSelectionType): Promise { if (selection !== 'c') { return Promise.resolve(''); } - return navigator.clipboard.readText().then(this._base64.encodeText); + return navigator.clipboard.readText(); } - public writeText(selection: ClipboardSelectionType, data: string): Promise { - if (selection !== 'c' || (this.limit > 0 && data.length > this.limit)) { + public async writeText(selection: ClipboardSelectionType, text: string): Promise { + if (selection !== 'c') { return Promise.resolve(); } - try { - const text = this._base64.decodeText(data); - return navigator.clipboard.writeText(text); - } catch { - // clear the clipboard if the data is not valid base64 - return navigator.clipboard.writeText(''); - } + return navigator.clipboard.writeText(text); } } -export interface IBase64 { - /** - * Converts a utf-8 string to a base64 string. - * @param data The utf-8 string to convert to base64 string. - */ - encodeText(data: string): string; - - /** - * Converts a base64 string to a utf-8 string. - * @param data The base64 string to convert to utf-8 string. - */ - decodeText(data: string): string; -} - export class Base64 implements IBase64 { public encodeText(data: string): string { return JSBase64.encode(data); diff --git a/addons/addon-clipboard/typings/addon-clipboard.d.ts b/addons/addon-clipboard/typings/addon-clipboard.d.ts index 06cb3703..f37748fa 100644 --- a/addons/addon-clipboard/typings/addon-clipboard.d.ts +++ b/addons/addon-clipboard/typings/addon-clipboard.d.ts @@ -28,16 +28,71 @@ declare module '@xterm/addon-clipboard' { public dispose(): void } + /** + * Clipboard selection type. This is used to specify which selection buffer to + * read or write to. + * - SYSTEM `c`: The system clipboard. + * - PRIMARY `p`: The primary clipboard. This is provided for compatibility + * with Linux X11. + */ + export const enum ClipboardSelectionType { + SYSTEM = 'c', + PRIMARY = 'p', + } + + export interface IBase64 { + /** + * Converts a utf-8 string to a base64 string. + * @param data The utf-8 string to convert to base64 string. + */ + encodeText(data: string): string; + + /** + * Converts a base64 string to a utf-8 string. + * @param data The base64 string to convert to utf-8 string. + * @throws An error if the input is not valid base64. + */ + decodeText(data: string): string; + } + + /** + * A default Base64 encoding and decoding type. + **/ + export class Base64 implements IBase64 { + /** + * Converts a utf-8 string to a base64 string. + * @param data The utf-8 string to convert to base64 string. + */ + public encodeText(data: string): string; + + /** + * Converts a base64 string to a utf-8 string. + * @param data The base64 string to convert to utf-8 string. + * @throws An error if the input is not valid base64. + */ + public decodeText(data: string): string; + } + + export interface IClipboardProvider { + /** + * Gets the clipboard content. + * @param selection The clipboard selection to read. + * @returns A promise that resolves with clipboard selection data. + */ + readText(selection: ClipboardSelectionType): string | Promise; + + /** + * Sets the clipboard content. + * @param selection The clipboard selection to set. + * @param data The clipboard text to write. + */ + writeText(selection: ClipboardSelectionType, text: string): void | Promise; + } + /** * The clipboard provider interface that enables xterm.js to access the system clipboard. */ - export class ClipboardProvider implements IClipboardProvider{ - /** - * Creates a new clipboard provider. - * @param _base64 The base64 encoder/decoder to use. - */ - constructor(base64?: IBase64, limit?: number); - + export class BrowserClipboardProvider implements IClipboardProvider{ /** * Reads text from the clipboard. * @param selection The selection type to read from. @@ -53,48 +108,4 @@ declare module '@xterm/addon-clipboard' { */ public writeText(selection: ClipboardSelectionType, data: string): Promise; } - - - export interface IBase64 { - /** - * Converts a utf-8 string to a base64 string. - * @param data The utf-8 string to convert to base64 string. - */ - encodeText(data: string): string; - - /** - * Converts a base64 string to a utf-8 string. - * @param data The base64 string to convert to utf-8 string. - */ - decodeText(data: string): string; - } - - export interface IClipboardProvider { - /** - * Gets the clipboard content. - * @param selection The clipboard selection to read. - * @returns A promise that resolves with the base64 encoded data. - */ - readText(selection: ClipboardSelectionType): Promise; - - /** - * Sets the clipboard content. - * @param selection The clipboard selection to set. - * @param data The base64 encoded data to set. If the data is invalid - * base64, the clipboard is cleared. - */ - writeText(selection: ClipboardSelectionType, data: string): Promise; - } - - /** - * Clipboard selection type. This is used to specify which selection buffer to - * read or write to. - * - SYSTEM `c`: The system clipboard. - * - PRIMARY `p`: The primary clipboard. This is provided for compatibility - * with Linux X11. - */ - export const enum ClipboardSelectionType { - SYSTEM = 'c', - PRIMARY = 'p', - } }