@flighthq/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.
@@ -0,0 +1,34 @@
1
+ import type { ClipboardBackend, ClipboardBookmark, ClipboardWatch, ClipboardWriteItem } from '@flighthq/types';
2
+ export declare function attachClipboardWatch(watch: ClipboardWatch): void;
3
+ export declare function clearClipboard(): Promise<boolean>;
4
+ export declare function createClipboardWatch(): ClipboardWatch;
5
+ export declare function createWebClipboardBackend(): ClipboardBackend;
6
+ export declare function detachClipboardWatch(watch: ClipboardWatch): void;
7
+ export declare function disposeClipboardWatch(watch: ClipboardWatch): void;
8
+ export declare function getClipboardBackend(): ClipboardBackend;
9
+ export declare function getClipboardChangeCount(): number;
10
+ export declare function getClipboardFormats(): Promise<readonly string[]>;
11
+ export declare function hasClipboardBookmark(): Promise<boolean>;
12
+ export declare function hasClipboardFormat(format: string): Promise<boolean>;
13
+ export declare function hasClipboardHtml(): Promise<boolean>;
14
+ export declare function hasClipboardImage(): Promise<boolean>;
15
+ export declare function hasClipboardRTF(): Promise<boolean>;
16
+ export declare function hasClipboardText(): Promise<boolean>;
17
+ export declare function readClipboard(formats: readonly string[]): Promise<Readonly<Record<string, string>>>;
18
+ export declare function readClipboardBookmark(): Promise<ClipboardBookmark | null>;
19
+ export declare function readClipboardFiles(): Promise<readonly string[]>;
20
+ export declare function readClipboardFormat(format: string): Promise<string>;
21
+ export declare function readClipboardHtml(): Promise<string>;
22
+ export declare function readClipboardImage(): Promise<string>;
23
+ export declare function readClipboardRTF(): Promise<string>;
24
+ export declare function readClipboardText(): Promise<string>;
25
+ export declare function setClipboardBackend(backend: ClipboardBackend | null): void;
26
+ export declare function writeClipboard(items: readonly Readonly<ClipboardWriteItem>[]): Promise<boolean>;
27
+ export declare function writeClipboardBookmark(title: string, url: string): Promise<boolean>;
28
+ export declare function writeClipboardFiles(paths: readonly string[]): Promise<boolean>;
29
+ export declare function writeClipboardFormat(format: string, data: string): Promise<boolean>;
30
+ export declare function writeClipboardHtml(html: string): Promise<boolean>;
31
+ export declare function writeClipboardImage(dataUrl: string): Promise<boolean>;
32
+ export declare function writeClipboardRTF(rtf: string): Promise<boolean>;
33
+ export declare function writeClipboardText(text: string): Promise<boolean>;
34
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"clipboard.d.ts","sourceRoot":"","sources":["../src/clipboard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,391 @@
1
+ import { createSignal, emitSignal } from '@flighthq/signals';
2
+ import { ClipboardFormatBookmark, ClipboardFormatHtml, ClipboardFormatRtf } from '@flighthq/types';
3
+ // Attaches `watch` to the active backend's change subscription. Emits watch.onChange on each
4
+ // clipboard change. Idempotent: a prior subscription is torn down first.
5
+ // Pair with detachClipboardWatch / disposeClipboardWatch.
6
+ export function attachClipboardWatch(watch) {
7
+ detachClipboardWatch(watch);
8
+ const unsubscribe = getClipboardBackend().subscribeClipboardChange(() => {
9
+ emitSignal(watch.onChange);
10
+ });
11
+ _watchSubscriptions.set(watch, unsubscribe);
12
+ }
13
+ // Clears the system clipboard. Returns false when the host denies access. Sentinel, not throw.
14
+ export function clearClipboard() {
15
+ return getClipboardBackend().clear();
16
+ }
17
+ // Allocates a ClipboardWatch event entity with an inert signal.
18
+ // Call attachClipboardWatch to start delivery; call disposeClipboardWatch when done.
19
+ export function createClipboardWatch() {
20
+ return { onChange: createSignal() };
21
+ }
22
+ // Builds the default web backend over navigator.clipboard. Reads return '' / false / [] when the API
23
+ // is absent (non-secure context, jsdom) or the user denies permission.
24
+ export function createWebClipboardBackend() {
25
+ return {
26
+ async readFormat(format) {
27
+ const cb = getWebClipboard();
28
+ if (cb === null || typeof cb.read !== 'function')
29
+ return '';
30
+ try {
31
+ const items = await cb.read();
32
+ for (const item of items) {
33
+ if (item.types.includes(format)) {
34
+ const blob = await item.getType(format);
35
+ if (format.startsWith('image/'))
36
+ return readBlobAsDataUrl(blob);
37
+ return blob.text();
38
+ }
39
+ }
40
+ }
41
+ catch {
42
+ return '';
43
+ }
44
+ return '';
45
+ },
46
+ async writeFormat(format, data) {
47
+ const cb = getWritableWebClipboard();
48
+ if (cb === null)
49
+ return false;
50
+ try {
51
+ const blob = await blobFromFormatData(format, data);
52
+ await cb.write([new ClipboardItem({ [format]: blob })]);
53
+ return true;
54
+ }
55
+ catch {
56
+ return false;
57
+ }
58
+ },
59
+ async hasFormat(format) {
60
+ const formats = await this.getFormats();
61
+ return formats.includes(format);
62
+ },
63
+ async getFormats() {
64
+ const cb = getWebClipboard();
65
+ if (cb === null || typeof cb.read !== 'function')
66
+ return [];
67
+ try {
68
+ const items = await cb.read();
69
+ const out = [];
70
+ for (const item of items) {
71
+ for (const t of item.types) {
72
+ if (!out.includes(t))
73
+ out.push(t);
74
+ }
75
+ }
76
+ return out;
77
+ }
78
+ catch {
79
+ return [];
80
+ }
81
+ },
82
+ async writeItems(items) {
83
+ const cb = getWritableWebClipboard();
84
+ if (cb === null)
85
+ return false;
86
+ try {
87
+ const entry = {};
88
+ for (const item of items) {
89
+ entry[item.format] = await blobFromFormatData(item.format, item.data);
90
+ }
91
+ await cb.write([new ClipboardItem(entry)]);
92
+ return true;
93
+ }
94
+ catch {
95
+ return false;
96
+ }
97
+ },
98
+ async readItems(formats) {
99
+ const cb = getWebClipboard();
100
+ if (cb === null || typeof cb.read !== 'function')
101
+ return {};
102
+ try {
103
+ const clipItems = await cb.read();
104
+ const result = {};
105
+ for (const clipItem of clipItems) {
106
+ for (const format of formats) {
107
+ if (clipItem.types.includes(format) && !(format in result)) {
108
+ const blob = await clipItem.getType(format);
109
+ result[format] = format.startsWith('image/') ? await readBlobAsDataUrl(blob) : await blob.text();
110
+ }
111
+ }
112
+ }
113
+ return result;
114
+ }
115
+ catch {
116
+ return {};
117
+ }
118
+ },
119
+ async readText() {
120
+ const cb = getWebClipboard();
121
+ if (cb === null || typeof cb.readText !== 'function')
122
+ return '';
123
+ try {
124
+ return await cb.readText();
125
+ }
126
+ catch {
127
+ return '';
128
+ }
129
+ },
130
+ async writeText(text) {
131
+ const cb = getWebClipboard();
132
+ if (cb === null || typeof cb.writeText !== 'function')
133
+ return false;
134
+ try {
135
+ await cb.writeText(text);
136
+ return true;
137
+ }
138
+ catch {
139
+ return false;
140
+ }
141
+ },
142
+ async readHtml() {
143
+ return this.readFormat(ClipboardFormatHtml);
144
+ },
145
+ async writeHtml(html) {
146
+ return this.writeFormat(ClipboardFormatHtml, html);
147
+ },
148
+ async hasText() {
149
+ return (await this.readText()).length > 0;
150
+ },
151
+ async readImage() {
152
+ const cb = getWebClipboard();
153
+ if (cb === null || typeof cb.read !== 'function')
154
+ return '';
155
+ try {
156
+ const items = await cb.read();
157
+ for (const item of items) {
158
+ const type = item.types.find((t) => t.startsWith('image/'));
159
+ if (type !== undefined) {
160
+ const blob = await item.getType(type);
161
+ return readBlobAsDataUrl(blob);
162
+ }
163
+ }
164
+ }
165
+ catch {
166
+ return '';
167
+ }
168
+ return '';
169
+ },
170
+ async writeImage(dataUrl) {
171
+ const cb = getWritableWebClipboard();
172
+ if (cb === null)
173
+ return false;
174
+ try {
175
+ const response = await fetch(dataUrl);
176
+ const blob = await response.blob();
177
+ await cb.write([new ClipboardItem({ [blob.type]: blob })]);
178
+ return true;
179
+ }
180
+ catch {
181
+ return false;
182
+ }
183
+ },
184
+ async hasImage() {
185
+ return (await this.readImage()).length > 0;
186
+ },
187
+ async readRTF() {
188
+ return this.readFormat(ClipboardFormatRtf);
189
+ },
190
+ async writeRTF(rtf) {
191
+ return this.writeFormat(ClipboardFormatRtf, rtf);
192
+ },
193
+ // The web platform exposes no clipboard bookmark format; a native host is required to read one.
194
+ async readBookmark() {
195
+ return null;
196
+ },
197
+ // The web platform exposes no clipboard bookmark format; a native host is required to write one.
198
+ async writeBookmark() {
199
+ return false;
200
+ },
201
+ // The web platform exposes no file-path clipboard format; a native host is required.
202
+ async readFiles() {
203
+ return [];
204
+ },
205
+ // The web platform exposes no file-path clipboard format; a native host is required.
206
+ async writeFiles() {
207
+ return false;
208
+ },
209
+ async clear() {
210
+ return this.writeText('');
211
+ },
212
+ getChangeCount() {
213
+ return -1;
214
+ },
215
+ subscribeClipboardChange(listener) {
216
+ if (typeof window === 'undefined')
217
+ return () => { };
218
+ // Use the experimental 'clipboardchange' event where present; fall back to no-op.
219
+ const target = window;
220
+ if ('onclipboardchange' in target ||
221
+ typeof window['clipboardchange'] !== 'undefined') {
222
+ const handler = () => listener();
223
+ window.addEventListener('clipboardchange', handler);
224
+ return () => window.removeEventListener('clipboardchange', handler);
225
+ }
226
+ return () => { };
227
+ },
228
+ };
229
+ }
230
+ // Stops delivery to `watch` and forgets its subscription. Safe to call when not attached.
231
+ export function detachClipboardWatch(watch) {
232
+ const unsubscribe = _watchSubscriptions.get(watch);
233
+ if (unsubscribe !== undefined) {
234
+ unsubscribe();
235
+ _watchSubscriptions.delete(watch);
236
+ }
237
+ }
238
+ // Detaches `watch`'s backend subscription and releases it for garbage collection.
239
+ // The signal remains plain GC-managed memory afterward.
240
+ export function disposeClipboardWatch(watch) {
241
+ detachClipboardWatch(watch);
242
+ }
243
+ // The active clipboard backend, or a lazily-created web default. There is always a backend.
244
+ export function getClipboardBackend() {
245
+ if (_backend === null)
246
+ _backend = createWebClipboardBackend();
247
+ return _backend;
248
+ }
249
+ // Returns a monotonically increasing count from the active backend, or -1 if unsupported.
250
+ export function getClipboardChangeCount() {
251
+ return getClipboardBackend().getChangeCount();
252
+ }
253
+ // Returns the list of MIME/format strings currently on the clipboard. [] sentinel on access denied.
254
+ export function getClipboardFormats() {
255
+ return getClipboardBackend().getFormats();
256
+ }
257
+ // True when the clipboard currently holds a bookmark. Returns false when access is denied.
258
+ export function hasClipboardBookmark() {
259
+ return getClipboardBackend().hasFormat(ClipboardFormatBookmark);
260
+ }
261
+ // True when the given MIME/format string is currently present on the clipboard.
262
+ export function hasClipboardFormat(format) {
263
+ return getClipboardBackend().hasFormat(format);
264
+ }
265
+ // True when the clipboard currently holds HTML content. Returns false when access is denied.
266
+ export function hasClipboardHtml() {
267
+ return getClipboardBackend().hasFormat(ClipboardFormatHtml);
268
+ }
269
+ // True when the clipboard currently holds an image. Returns false when access is denied.
270
+ export function hasClipboardImage() {
271
+ return getClipboardBackend().hasImage();
272
+ }
273
+ // True when the clipboard currently holds RTF content. Returns false when access is denied.
274
+ export function hasClipboardRTF() {
275
+ return getClipboardBackend().hasFormat(ClipboardFormatRtf);
276
+ }
277
+ // True when the clipboard currently holds non-empty text. Returns false when access is denied.
278
+ export function hasClipboardText() {
279
+ return getClipboardBackend().hasText();
280
+ }
281
+ // Reads multiple formats in one round-trip; missing formats are omitted from the result.
282
+ export function readClipboard(formats) {
283
+ return getClipboardBackend().readItems(formats);
284
+ }
285
+ // Reads a bookmark (title + URL) from the clipboard, or null when none is present or access is denied.
286
+ export function readClipboardBookmark() {
287
+ return getClipboardBackend().readBookmark();
288
+ }
289
+ // Reads the file paths currently on the clipboard. Returns [] when none are present or on web.
290
+ export function readClipboardFiles() {
291
+ return getClipboardBackend().readFiles();
292
+ }
293
+ // Reads an arbitrary MIME/format flavor as a string; returns '' when absent or access is denied.
294
+ export function readClipboardFormat(format) {
295
+ return getClipboardBackend().readFormat(format);
296
+ }
297
+ // Reads HTML from the clipboard, or '' when none is present or access is denied.
298
+ export function readClipboardHtml() {
299
+ return getClipboardBackend().readHtml();
300
+ }
301
+ // Reads an image from the clipboard as a data URL, or '' when none is present or access is denied.
302
+ export function readClipboardImage() {
303
+ return getClipboardBackend().readImage();
304
+ }
305
+ // Reads RTF (Rich Text Format) markup from the clipboard, or '' when none is present or access is denied.
306
+ export function readClipboardRTF() {
307
+ return getClipboardBackend().readRTF();
308
+ }
309
+ // Reads plain text from the clipboard, or '' when empty or access is denied.
310
+ export function readClipboardText() {
311
+ return getClipboardBackend().readText();
312
+ }
313
+ // Installs a native host clipboard backend; pass null to fall back to the web default.
314
+ export function setClipboardBackend(backend) {
315
+ _backend = backend;
316
+ }
317
+ // Writes multiple formats atomically so a paste target picks its best representation.
318
+ export function writeClipboard(items) {
319
+ return getClipboardBackend().writeItems(items);
320
+ }
321
+ // Writes a bookmark (title + URL) to the clipboard. Returns false when the host denies access.
322
+ export function writeClipboardBookmark(title, url) {
323
+ return getClipboardBackend().writeBookmark(title, url);
324
+ }
325
+ // Writes file paths to the clipboard. Returns false when the host denies access or on web.
326
+ export function writeClipboardFiles(paths) {
327
+ return getClipboardBackend().writeFiles(paths);
328
+ }
329
+ // Writes an arbitrary MIME/format flavor. Returns false when the host denies access.
330
+ export function writeClipboardFormat(format, data) {
331
+ return getClipboardBackend().writeFormat(format, data);
332
+ }
333
+ // Writes HTML to the clipboard. Returns false when the host denies access.
334
+ export function writeClipboardHtml(html) {
335
+ return getClipboardBackend().writeHtml(html);
336
+ }
337
+ // Writes an image (given as a data URL) to the clipboard. Returns false when the host denies access.
338
+ export function writeClipboardImage(dataUrl) {
339
+ return getClipboardBackend().writeImage(dataUrl);
340
+ }
341
+ // Writes RTF (Rich Text Format) markup to the clipboard. Returns false when the host denies access.
342
+ export function writeClipboardRTF(rtf) {
343
+ return getClipboardBackend().writeRTF(rtf);
344
+ }
345
+ // Writes plain text to the clipboard. Returns false when the host denies access.
346
+ export function writeClipboardText(text) {
347
+ return getClipboardBackend().writeText(text);
348
+ }
349
+ let _backend = null;
350
+ const _watchSubscriptions = new WeakMap();
351
+ // Converts a format/data pair into a Blob. Image data URLs are fetched into their decoded bytes;
352
+ // every other flavor wraps the string payload directly under its MIME type.
353
+ async function blobFromFormatData(format, data) {
354
+ if (format.startsWith('image/') && data.startsWith('data:')) {
355
+ const response = await fetch(data);
356
+ return response.blob();
357
+ }
358
+ return new Blob([data], { type: format });
359
+ }
360
+ function getWebClipboard() {
361
+ if (typeof navigator === 'undefined')
362
+ return null;
363
+ return navigator.clipboard ?? null;
364
+ }
365
+ // The web clipboard when ClipboardItem writes are possible, or null. Centralizes the shared
366
+ // write-path guard so every write/writeItems/writeImage caller folds to one null check.
367
+ function getWritableWebClipboard() {
368
+ const cb = getWebClipboard();
369
+ if (cb === null || typeof cb.write !== 'function' || typeof ClipboardItem === 'undefined')
370
+ return null;
371
+ return cb;
372
+ }
373
+ // Reads a Blob into a data URL via FileReader, resolving '' when reading fails.
374
+ function readBlobAsDataUrl(blob) {
375
+ return new Promise((resolve) => {
376
+ if (typeof FileReader === 'undefined') {
377
+ resolve('');
378
+ return;
379
+ }
380
+ try {
381
+ const reader = new FileReader();
382
+ reader.onloadend = () => resolve(typeof reader.result === 'string' ? reader.result : '');
383
+ reader.onerror = () => resolve('');
384
+ reader.readAsDataURL(blob);
385
+ }
386
+ catch {
387
+ resolve('');
388
+ }
389
+ });
390
+ }
391
+ //# 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,2 @@
1
+ export * from './clipboard';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export * from './clipboard';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@flighthq/clipboard",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "main": "dist/index.js",
6
+ "types": "dist/index.d.ts",
7
+ "exports": {
8
+ ".": {
9
+ "types": "./dist/index.d.ts",
10
+ "default": "./dist/index.js"
11
+ }
12
+ },
13
+ "files": [
14
+ "dist",
15
+ "src/**/*.test.ts",
16
+ "!dist/**/*.test.js",
17
+ "!dist/**/*.test.d.ts",
18
+ "!dist/**/*.test.js.map",
19
+ "!dist/**/*.test.d.ts.map"
20
+ ],
21
+ "scripts": {
22
+ "build": "tsc -b",
23
+ "clean": "tsc -b --clean",
24
+ "test": "vitest run --config vitest.config.ts",
25
+ "test:watch": "vitest --watch --config vitest.config.ts",
26
+ "prepack": "npm run clean && npm run clean:dist && npm run build",
27
+ "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
28
+ },
29
+ "dependencies": {
30
+ "@flighthq/signals": "0.1.0",
31
+ "@flighthq/types": "0.1.0"
32
+ },
33
+ "devDependencies": {
34
+ "typescript": "^5.3.0"
35
+ },
36
+ "description": "System clipboard read/write (text, HTML, image, RTF, bookmark, files, generic MIME formats) with atomic multi-flavor write and change events, over a swappable web/native backend",
37
+ "sideEffects": false
38
+ }
@@ -0,0 +1,613 @@
1
+ import type { ClipboardBackend, ClipboardBookmark } from '@flighthq/types';
2
+
3
+ import {
4
+ attachClipboardWatch,
5
+ clearClipboard,
6
+ createClipboardWatch,
7
+ createWebClipboardBackend,
8
+ detachClipboardWatch,
9
+ disposeClipboardWatch,
10
+ getClipboardBackend,
11
+ getClipboardChangeCount,
12
+ getClipboardFormats,
13
+ hasClipboardBookmark,
14
+ hasClipboardFormat,
15
+ hasClipboardHtml,
16
+ hasClipboardImage,
17
+ hasClipboardRTF,
18
+ hasClipboardText,
19
+ readClipboard,
20
+ readClipboardBookmark,
21
+ readClipboardFiles,
22
+ readClipboardFormat,
23
+ readClipboardHtml,
24
+ readClipboardImage,
25
+ readClipboardRTF,
26
+ readClipboardText,
27
+ setClipboardBackend,
28
+ writeClipboard,
29
+ writeClipboardBookmark,
30
+ writeClipboardFiles,
31
+ writeClipboardFormat,
32
+ writeClipboardHtml,
33
+ writeClipboardImage,
34
+ writeClipboardRTF,
35
+ writeClipboardText,
36
+ } from './clipboard';
37
+
38
+ function fakeBackend(): ClipboardBackend & {
39
+ text: string;
40
+ html: string;
41
+ image: string;
42
+ rtf: string;
43
+ bookmark: ClipboardBookmark | null;
44
+ files: string[];
45
+ formats: Record<string, string>;
46
+ changeCount: number;
47
+ } {
48
+ return {
49
+ text: '',
50
+ html: '',
51
+ image: '',
52
+ rtf: '',
53
+ bookmark: null,
54
+ files: [],
55
+ formats: {},
56
+ changeCount: 0,
57
+ async readFormat(format) {
58
+ if (format === 'text/plain') return this.text;
59
+ if (format === 'text/html') return this.html;
60
+ if (format === 'text/rtf') return this.rtf;
61
+ return this.formats[format] ?? '';
62
+ },
63
+ async writeFormat(format, data) {
64
+ if (format === 'text/plain') this.text = data;
65
+ else if (format === 'text/html') this.html = data;
66
+ else if (format === 'text/rtf') this.rtf = data;
67
+ else this.formats[format] = data;
68
+ this.changeCount++;
69
+ return true;
70
+ },
71
+ async hasFormat(format) {
72
+ const data = await this.readFormat(format);
73
+ return data.length > 0;
74
+ },
75
+ async getFormats() {
76
+ const out: string[] = [];
77
+ if (this.text.length > 0) out.push('text/plain');
78
+ if (this.html.length > 0) out.push('text/html');
79
+ if (this.rtf.length > 0) out.push('text/rtf');
80
+ if (this.image.length > 0) out.push('image/png');
81
+ if (this.bookmark !== null) out.push('text/x-moz-url');
82
+ for (const k of Object.keys(this.formats)) out.push(k);
83
+ return out;
84
+ },
85
+ async writeItems(items) {
86
+ for (const item of items) await this.writeFormat(item.format, item.data);
87
+ return true;
88
+ },
89
+ async readItems(formats) {
90
+ const result: Record<string, string> = {};
91
+ for (const format of formats) {
92
+ const data = await this.readFormat(format);
93
+ if (data.length > 0) result[format] = data;
94
+ }
95
+ return result;
96
+ },
97
+ async readText() {
98
+ return this.text;
99
+ },
100
+ async writeText(text) {
101
+ this.text = text;
102
+ this.changeCount++;
103
+ return true;
104
+ },
105
+ async readHtml() {
106
+ return this.html;
107
+ },
108
+ async writeHtml(html) {
109
+ this.html = html;
110
+ this.changeCount++;
111
+ return true;
112
+ },
113
+ async hasText() {
114
+ return this.text.length > 0;
115
+ },
116
+ async readImage() {
117
+ return this.image;
118
+ },
119
+ async writeImage(dataUrl) {
120
+ this.image = dataUrl;
121
+ this.changeCount++;
122
+ return true;
123
+ },
124
+ async hasImage() {
125
+ return this.image.length > 0;
126
+ },
127
+ async readRTF() {
128
+ return this.rtf;
129
+ },
130
+ async writeRTF(rtf) {
131
+ this.rtf = rtf;
132
+ this.changeCount++;
133
+ return true;
134
+ },
135
+ async readBookmark() {
136
+ return this.bookmark;
137
+ },
138
+ async writeBookmark(title, url) {
139
+ this.bookmark = { title, url };
140
+ this.changeCount++;
141
+ return true;
142
+ },
143
+ async readFiles() {
144
+ return [...this.files];
145
+ },
146
+ async writeFiles(paths) {
147
+ this.files = [...paths];
148
+ this.changeCount++;
149
+ return true;
150
+ },
151
+ async clear() {
152
+ this.text = '';
153
+ this.html = '';
154
+ this.image = '';
155
+ this.rtf = '';
156
+ this.bookmark = null;
157
+ this.files = [];
158
+ this.formats = {};
159
+ this.changeCount++;
160
+ return true;
161
+ },
162
+ getChangeCount() {
163
+ return this.changeCount;
164
+ },
165
+ subscribeClipboardChange(listener) {
166
+ const listeners = (this as unknown as { _listeners: Array<() => void> })._listeners;
167
+ if (!listeners) (this as unknown as { _listeners: Array<() => void> })._listeners = [];
168
+ (this as unknown as { _listeners: Array<() => void> })._listeners.push(listener);
169
+ return () => {
170
+ const idx = (this as unknown as { _listeners: Array<() => void> })._listeners.indexOf(listener);
171
+ if (idx >= 0) (this as unknown as { _listeners: Array<() => void> })._listeners.splice(idx, 1);
172
+ };
173
+ },
174
+ };
175
+ }
176
+
177
+ afterEach(() => setClipboardBackend(null));
178
+
179
+ describe('attachClipboardWatch', () => {
180
+ it('emits onChange when the backend notifies', () => {
181
+ const backend = fakeBackend();
182
+ setClipboardBackend(backend);
183
+ const watch = createClipboardWatch();
184
+ let count = 0;
185
+ watch.onChange.emit = () => {
186
+ count++;
187
+ };
188
+ attachClipboardWatch(watch);
189
+ // Simulate backend notification by calling all subscribed listeners
190
+ const listeners = (backend as unknown as { _listeners: Array<() => void> })._listeners;
191
+ expect(listeners.length).toBeGreaterThan(0);
192
+ listeners.forEach((l) => l());
193
+ expect(count).toBeGreaterThan(0);
194
+ disposeClipboardWatch(watch);
195
+ });
196
+
197
+ it('is idempotent — attaching twice only has one active subscription', () => {
198
+ const backend = fakeBackend();
199
+ setClipboardBackend(backend);
200
+ const watch = createClipboardWatch();
201
+ attachClipboardWatch(watch);
202
+ attachClipboardWatch(watch);
203
+ const listeners = (backend as unknown as { _listeners: Array<() => void> })._listeners ?? [];
204
+ expect(listeners.length).toBe(1);
205
+ disposeClipboardWatch(watch);
206
+ });
207
+ });
208
+
209
+ describe('clearClipboard', () => {
210
+ it('clears via the active backend', async () => {
211
+ const backend = fakeBackend();
212
+ backend.text = 'x';
213
+ setClipboardBackend(backend);
214
+ expect(await clearClipboard()).toBe(true);
215
+ expect(backend.text).toBe('');
216
+ });
217
+ });
218
+
219
+ describe('createClipboardWatch', () => {
220
+ it('returns an entity with an onChange signal', () => {
221
+ const watch = createClipboardWatch();
222
+ expect(watch.onChange).toBeDefined();
223
+ });
224
+ });
225
+
226
+ describe('createWebClipboardBackend', () => {
227
+ it('returns a backend whose reads yield strings without throwing', async () => {
228
+ const backend = createWebClipboardBackend();
229
+ expect(typeof (await backend.readText())).toBe('string');
230
+ expect(typeof (await backend.readHtml())).toBe('string');
231
+ expect(typeof (await backend.readFormat('text/plain'))).toBe('string');
232
+ });
233
+
234
+ it('getFormats returns an array without throwing', async () => {
235
+ const backend = createWebClipboardBackend();
236
+ const formats = await backend.getFormats();
237
+ expect(Array.isArray(formats)).toBe(true);
238
+ });
239
+
240
+ it('getChangeCount returns -1 (unsupported on web)', () => {
241
+ const backend = createWebClipboardBackend();
242
+ expect(backend.getChangeCount()).toBe(-1);
243
+ });
244
+
245
+ it('subscribeClipboardChange returns a function without throwing', () => {
246
+ const backend = createWebClipboardBackend();
247
+ const unsub = backend.subscribeClipboardChange(() => {});
248
+ expect(typeof unsub).toBe('function');
249
+ unsub();
250
+ });
251
+ });
252
+
253
+ describe('detachClipboardWatch', () => {
254
+ it('stops delivery after detach', () => {
255
+ const backend = fakeBackend();
256
+ setClipboardBackend(backend);
257
+ const watch = createClipboardWatch();
258
+ let count = 0;
259
+ watch.onChange.emit = () => {
260
+ count++;
261
+ };
262
+ attachClipboardWatch(watch);
263
+ detachClipboardWatch(watch);
264
+ const listeners = (backend as unknown as { _listeners: Array<() => void> })._listeners ?? [];
265
+ expect(listeners.length).toBe(0);
266
+ expect(count).toBe(0);
267
+ });
268
+
269
+ it('is safe to call when not attached', () => {
270
+ const watch = createClipboardWatch();
271
+ expect(() => detachClipboardWatch(watch)).not.toThrow();
272
+ });
273
+ });
274
+
275
+ describe('disposeClipboardWatch', () => {
276
+ it('detaches and does not throw', () => {
277
+ const backend = fakeBackend();
278
+ setClipboardBackend(backend);
279
+ const watch = createClipboardWatch();
280
+ attachClipboardWatch(watch);
281
+ expect(() => disposeClipboardWatch(watch)).not.toThrow();
282
+ const listeners = (backend as unknown as { _listeners: Array<() => void> })._listeners ?? [];
283
+ expect(listeners.length).toBe(0);
284
+ });
285
+ });
286
+
287
+ describe('getClipboardBackend', () => {
288
+ it('falls back to a web backend', () => {
289
+ expect(getClipboardBackend()).not.toBeNull();
290
+ });
291
+
292
+ it('returns the registered backend', () => {
293
+ const backend = fakeBackend();
294
+ setClipboardBackend(backend);
295
+ expect(getClipboardBackend()).toBe(backend);
296
+ });
297
+ });
298
+
299
+ describe('getClipboardChangeCount', () => {
300
+ it('reflects the backend change count', async () => {
301
+ const backend = fakeBackend();
302
+ setClipboardBackend(backend);
303
+ const before = getClipboardChangeCount();
304
+ await writeClipboardText('x');
305
+ expect(getClipboardChangeCount()).toBe(before + 1);
306
+ });
307
+
308
+ it('returns -1 from the web backend', () => {
309
+ expect(createWebClipboardBackend().getChangeCount()).toBe(-1);
310
+ });
311
+ });
312
+
313
+ describe('getClipboardFormats', () => {
314
+ it('returns the active formats from the backend', async () => {
315
+ const backend = fakeBackend();
316
+ setClipboardBackend(backend);
317
+ expect(await getClipboardFormats()).toEqual([]);
318
+ await writeClipboardText('hi');
319
+ const formats = await getClipboardFormats();
320
+ expect(formats).toContain('text/plain');
321
+ });
322
+
323
+ it('returns [] from the web backend without throwing', async () => {
324
+ const formats = await createWebClipboardBackend().getFormats();
325
+ expect(Array.isArray(formats)).toBe(true);
326
+ });
327
+ });
328
+
329
+ describe('hasClipboardBookmark', () => {
330
+ it('returns false when no bookmark is present', async () => {
331
+ const backend = fakeBackend();
332
+ setClipboardBackend(backend);
333
+ expect(await hasClipboardBookmark()).toBe(false);
334
+ });
335
+
336
+ it('returns true after a bookmark is written by the backend', async () => {
337
+ const backend = fakeBackend();
338
+ // Simulate a backend that puts 'text/x-moz-url' in formats when a bookmark is written
339
+ backend.formats['text/x-moz-url'] = 'https://example.com\nFlight';
340
+ setClipboardBackend(backend);
341
+ expect(await hasClipboardBookmark()).toBe(true);
342
+ });
343
+ });
344
+
345
+ describe('hasClipboardFormat', () => {
346
+ it('reflects whether a format is present', async () => {
347
+ const backend = fakeBackend();
348
+ setClipboardBackend(backend);
349
+ expect(await hasClipboardFormat('text/plain')).toBe(false);
350
+ await writeClipboardText('x');
351
+ expect(await hasClipboardFormat('text/plain')).toBe(true);
352
+ });
353
+ });
354
+
355
+ describe('hasClipboardHtml', () => {
356
+ it('reflects backend state', async () => {
357
+ const backend = fakeBackend();
358
+ setClipboardBackend(backend);
359
+ expect(await hasClipboardHtml()).toBe(false);
360
+ await writeClipboardHtml('<b>x</b>');
361
+ expect(await hasClipboardHtml()).toBe(true);
362
+ });
363
+ });
364
+
365
+ describe('hasClipboardImage', () => {
366
+ it('reflects backend state', async () => {
367
+ const backend = fakeBackend();
368
+ setClipboardBackend(backend);
369
+ expect(await hasClipboardImage()).toBe(false);
370
+ await writeClipboardImage('data:image/png;base64,AAAA');
371
+ expect(await hasClipboardImage()).toBe(true);
372
+ });
373
+
374
+ it('returns false from the web backend without throwing', async () => {
375
+ expect(await createWebClipboardBackend().hasImage()).toBe(false);
376
+ });
377
+ });
378
+
379
+ describe('hasClipboardRTF', () => {
380
+ it('reflects backend state', async () => {
381
+ const backend = fakeBackend();
382
+ setClipboardBackend(backend);
383
+ expect(await hasClipboardRTF()).toBe(false);
384
+ await writeClipboardRTF('{\\rtf1 hi}');
385
+ expect(await hasClipboardRTF()).toBe(true);
386
+ });
387
+ });
388
+
389
+ describe('hasClipboardText', () => {
390
+ it('reflects backend state', async () => {
391
+ const backend = fakeBackend();
392
+ setClipboardBackend(backend);
393
+ expect(await hasClipboardText()).toBe(false);
394
+ await writeClipboardText('hi');
395
+ expect(await hasClipboardText()).toBe(true);
396
+ });
397
+ });
398
+
399
+ describe('readClipboard', () => {
400
+ it('reads multiple formats in one call', async () => {
401
+ const backend = fakeBackend();
402
+ setClipboardBackend(backend);
403
+ await writeClipboardText('hello');
404
+ await writeClipboardHtml('<b>hello</b>');
405
+ const result = await readClipboard(['text/plain', 'text/html']);
406
+ expect(result['text/plain']).toBe('hello');
407
+ expect(result['text/html']).toBe('<b>hello</b>');
408
+ });
409
+
410
+ it('omits formats that are not present', async () => {
411
+ const backend = fakeBackend();
412
+ setClipboardBackend(backend);
413
+ await writeClipboardText('hi');
414
+ const result = await readClipboard(['text/plain', 'text/rtf']);
415
+ expect(result['text/plain']).toBe('hi');
416
+ expect(result['text/rtf']).toBeUndefined();
417
+ });
418
+
419
+ it('returns {} from the web backend without throwing', async () => {
420
+ const result = await createWebClipboardBackend().readItems(['text/plain']);
421
+ expect(typeof result).toBe('object');
422
+ });
423
+ });
424
+
425
+ describe('readClipboardBookmark', () => {
426
+ it('round-trips through the backend', async () => {
427
+ setClipboardBackend(fakeBackend());
428
+ await writeClipboardBookmark('Flight', 'https://example.com');
429
+ expect(await readClipboardBookmark()).toEqual({ title: 'Flight', url: 'https://example.com' });
430
+ });
431
+
432
+ it('returns the null sentinel from the web backend without throwing', async () => {
433
+ expect(await createWebClipboardBackend().readBookmark()).toBeNull();
434
+ });
435
+ });
436
+
437
+ describe('readClipboardFiles', () => {
438
+ it('round-trips file paths through the backend', async () => {
439
+ const backend = fakeBackend();
440
+ setClipboardBackend(backend);
441
+ await writeClipboardFiles(['/a/b.txt', '/c/d.txt']);
442
+ expect(await readClipboardFiles()).toEqual(['/a/b.txt', '/c/d.txt']);
443
+ });
444
+
445
+ it('returns [] from the web backend without throwing', async () => {
446
+ const files = await createWebClipboardBackend().readFiles();
447
+ expect(Array.isArray(files)).toBe(true);
448
+ expect(files.length).toBe(0);
449
+ });
450
+ });
451
+
452
+ describe('readClipboardFormat', () => {
453
+ it('round-trips an arbitrary format through the backend', async () => {
454
+ const backend = fakeBackend();
455
+ setClipboardBackend(backend);
456
+ await writeClipboardFormat('application/x-custom', 'mydata');
457
+ expect(await readClipboardFormat('application/x-custom')).toBe('mydata');
458
+ });
459
+
460
+ it('returns the empty-string sentinel from the web backend without throwing', async () => {
461
+ expect(await createWebClipboardBackend().readFormat('application/x-custom')).toBe('');
462
+ });
463
+ });
464
+
465
+ describe('readClipboardHtml', () => {
466
+ it('round-trips through the backend', async () => {
467
+ setClipboardBackend(fakeBackend());
468
+ await writeClipboardHtml('<b>x</b>');
469
+ expect(await readClipboardHtml()).toBe('<b>x</b>');
470
+ });
471
+ });
472
+
473
+ describe('readClipboardImage', () => {
474
+ it('round-trips through the backend', async () => {
475
+ setClipboardBackend(fakeBackend());
476
+ await writeClipboardImage('data:image/png;base64,BBBB');
477
+ expect(await readClipboardImage()).toBe('data:image/png;base64,BBBB');
478
+ });
479
+
480
+ it('returns the empty-string sentinel from the web backend without throwing', async () => {
481
+ expect(await createWebClipboardBackend().readImage()).toBe('');
482
+ });
483
+ });
484
+
485
+ describe('readClipboardRTF', () => {
486
+ it('round-trips through the backend', async () => {
487
+ setClipboardBackend(fakeBackend());
488
+ await writeClipboardRTF('{\\rtf1 hi}');
489
+ expect(await readClipboardRTF()).toBe('{\\rtf1 hi}');
490
+ });
491
+
492
+ it('returns the empty-string sentinel from the web backend without throwing', async () => {
493
+ expect(await createWebClipboardBackend().readRTF()).toBe('');
494
+ });
495
+ });
496
+
497
+ describe('readClipboardText', () => {
498
+ it('round-trips through the backend', async () => {
499
+ setClipboardBackend(fakeBackend());
500
+ await writeClipboardText('hello');
501
+ expect(await readClipboardText()).toBe('hello');
502
+ });
503
+ });
504
+
505
+ describe('setClipboardBackend', () => {
506
+ it('clears back to the web fallback when passed null', () => {
507
+ setClipboardBackend(fakeBackend());
508
+ setClipboardBackend(null);
509
+ expect(getClipboardBackend()).not.toBeNull();
510
+ });
511
+ });
512
+
513
+ describe('writeClipboard', () => {
514
+ it('writes multiple formats atomically', async () => {
515
+ const backend = fakeBackend();
516
+ setClipboardBackend(backend);
517
+ expect(
518
+ await writeClipboard([
519
+ { format: 'text/plain', data: 'hello' },
520
+ { format: 'text/html', data: '<b>hello</b>' },
521
+ ]),
522
+ ).toBe(true);
523
+ expect(backend.text).toBe('hello');
524
+ expect(backend.html).toBe('<b>hello</b>');
525
+ });
526
+
527
+ it('returns false from the web backend without throwing', async () => {
528
+ expect(await createWebClipboardBackend().writeItems([{ format: 'text/plain', data: 'x' }])).toBe(false);
529
+ });
530
+ });
531
+
532
+ describe('writeClipboardBookmark', () => {
533
+ it('writes via the active backend', async () => {
534
+ const backend = fakeBackend();
535
+ setClipboardBackend(backend);
536
+ expect(await writeClipboardBookmark('Flight', 'https://example.com')).toBe(true);
537
+ expect(backend.bookmark).toEqual({ title: 'Flight', url: 'https://example.com' });
538
+ });
539
+
540
+ it('returns false from the web backend without throwing', async () => {
541
+ expect(await createWebClipboardBackend().writeBookmark('Flight', 'https://example.com')).toBe(false);
542
+ });
543
+ });
544
+
545
+ describe('writeClipboardFiles', () => {
546
+ it('writes file paths via the active backend', async () => {
547
+ const backend = fakeBackend();
548
+ setClipboardBackend(backend);
549
+ expect(await writeClipboardFiles(['/a/b.txt'])).toBe(true);
550
+ expect(backend.files).toEqual(['/a/b.txt']);
551
+ });
552
+
553
+ it('returns false from the web backend without throwing', async () => {
554
+ expect(await createWebClipboardBackend().writeFiles(['/a.txt'])).toBe(false);
555
+ });
556
+ });
557
+
558
+ describe('writeClipboardFormat', () => {
559
+ it('writes an arbitrary format via the active backend', async () => {
560
+ const backend = fakeBackend();
561
+ setClipboardBackend(backend);
562
+ expect(await writeClipboardFormat('application/x-custom', 'data')).toBe(true);
563
+ expect(backend.formats['application/x-custom']).toBe('data');
564
+ });
565
+
566
+ it('returns false from the web backend without throwing', async () => {
567
+ expect(await createWebClipboardBackend().writeFormat('application/x-custom', 'data')).toBe(false);
568
+ });
569
+ });
570
+
571
+ describe('writeClipboardHtml', () => {
572
+ it('writes via the active backend', async () => {
573
+ const backend = fakeBackend();
574
+ setClipboardBackend(backend);
575
+ expect(await writeClipboardHtml('<i>y</i>')).toBe(true);
576
+ expect(backend.html).toBe('<i>y</i>');
577
+ });
578
+ });
579
+
580
+ describe('writeClipboardImage', () => {
581
+ it('writes via the active backend', async () => {
582
+ const backend = fakeBackend();
583
+ setClipboardBackend(backend);
584
+ expect(await writeClipboardImage('data:image/png;base64,CCCC')).toBe(true);
585
+ expect(backend.image).toBe('data:image/png;base64,CCCC');
586
+ });
587
+
588
+ it('returns false from the web backend without throwing', async () => {
589
+ expect(await createWebClipboardBackend().writeImage('data:image/png;base64,DDDD')).toBe(false);
590
+ });
591
+ });
592
+
593
+ describe('writeClipboardRTF', () => {
594
+ it('writes via the active backend', async () => {
595
+ const backend = fakeBackend();
596
+ setClipboardBackend(backend);
597
+ expect(await writeClipboardRTF('{\\rtf1 z}')).toBe(true);
598
+ expect(backend.rtf).toBe('{\\rtf1 z}');
599
+ });
600
+
601
+ it('returns false from the web backend without throwing', async () => {
602
+ expect(await createWebClipboardBackend().writeRTF('{\\rtf1 z}')).toBe(false);
603
+ });
604
+ });
605
+
606
+ describe('writeClipboardText', () => {
607
+ it('writes via the active backend', async () => {
608
+ const backend = fakeBackend();
609
+ setClipboardBackend(backend);
610
+ expect(await writeClipboardText('z')).toBe(true);
611
+ expect(backend.text).toBe('z');
612
+ });
613
+ });