@followbin/dsh-selection 0.0.0-stage → 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.
Files changed (44) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +140 -2
  3. package/README.zh-CN.md +141 -0
  4. package/THIRD_PARTY_NOTICES.md +22 -0
  5. package/client.js +1055 -0
  6. package/cordis.patch.yml +4 -0
  7. package/icon.svg +1 -0
  8. package/index.js +41 -0
  9. package/locale/en.json +6 -0
  10. package/locale/zh.json +6 -0
  11. package/package.json +90 -4
  12. package/src/host/files.mjs +29 -0
  13. package/src/host/http.mjs +153 -0
  14. package/src/host/llm.mjs +194 -0
  15. package/src/host/log.mjs +40 -0
  16. package/src/host/service.mjs +324 -0
  17. package/src/host/settings.mjs +279 -0
  18. package/src/host/skills.mjs +88 -0
  19. package/src/host/supervisor.mjs +126 -0
  20. package/src/host/usage.mjs +128 -0
  21. package/src/shared/icons.mjs +43 -0
  22. package/src/shared/open-with-system.mjs +15 -0
  23. package/src/shared/paths.mjs +7 -0
  24. package/src/shared/protocol.mjs +4 -0
  25. package/src/worker/controller.mjs +261 -0
  26. package/src/worker/main.mjs +147 -0
  27. package/src/worker/native-window.mjs +319 -0
  28. package/src/worker/open-url.mjs +20 -0
  29. package/src/worker/overlay.mjs +263 -0
  30. package/src/worker/page.mjs +14 -0
  31. package/src/worker/placement.mjs +58 -0
  32. package/src/worker/speech.mjs +136 -0
  33. package/ui/markdown.js +122 -0
  34. package/ui/toolbar.css +152 -0
  35. package/ui/toolbar.html +52 -0
  36. package/ui/toolbar.js +492 -0
  37. package/vendor/selection-hook/LICENSE +21 -0
  38. package/vendor/selection-hook/index.d.ts +517 -0
  39. package/vendor/selection-hook/index.js +662 -0
  40. package/vendor/selection-hook/package.json +10 -0
  41. package/vendor/selection-hook/prebuilds/darwin-arm64/selection-hook.node +0 -0
  42. package/vendor/selection-hook/prebuilds/darwin-x64/selection-hook.node +0 -0
  43. package/vendor/selection-hook/prebuilds/win32-arm64/selection-hook.node +0 -0
  44. package/vendor/selection-hook/prebuilds/win32-x64/selection-hook.node +0 -0
@@ -0,0 +1,662 @@
1
+ /**
2
+ * Node Selection Hook
3
+ *
4
+ * This module provides a Node.js interface for monitoring text selections
5
+ * across applications on Windows, macOS, and Linux using UI Automation and Accessibility APIs.
6
+ *
7
+ * Copyright (c) 2025 0xfullex (https://github.com/0xfullex/selection-hook)
8
+ * Licensed under the MIT License
9
+ */
10
+
11
+ const EventEmitter = require("events");
12
+ const path = require("path");
13
+
14
+ const isWindows = process.platform === "win32";
15
+ const isMac = process.platform === "darwin";
16
+ const isLinux = process.platform === "linux";
17
+
18
+ let nativeModule = null;
19
+ // Make debugFlag a private module variable to avoid global state issues
20
+ let _debugFlag = false;
21
+
22
+ try {
23
+ if (!isWindows && !isMac && !isLinux) {
24
+ throw new Error("[selection-hook] Only supports Windows, macOS, and Linux platforms");
25
+ }
26
+ nativeModule = require(path.join(__dirname, "prebuilds", `${process.platform}-${process.arch}`, "selection-hook.node"));
27
+ } catch (err) {
28
+ console.error("[selection-hook] Failed to load native module:", err.message);
29
+ }
30
+
31
+ class SelectionHook extends EventEmitter {
32
+ #instance = null;
33
+ #running = false;
34
+
35
+ static SelectionMethod = {
36
+ NONE: 0,
37
+ UIA: 1,
38
+ /** @deprecated This method has been removed */
39
+ FOCUSCTL: 2,
40
+ ACCESSIBLE: 3,
41
+ AXAPI: 11,
42
+ /** @reserved AT-SPI method is reserved for future use */
43
+ ATSPI: 21,
44
+ PRIMARY: 22,
45
+ CLIPBOARD: 99,
46
+ };
47
+
48
+ static PositionLevel = {
49
+ NONE: 0,
50
+ MOUSE_SINGLE: 1,
51
+ MOUSE_DUAL: 2,
52
+ SEL_FULL: 3,
53
+ SEL_DETAILED: 4,
54
+ };
55
+
56
+ static FilterMode = {
57
+ DEFAULT: 0,
58
+ INCLUDE_LIST: 1,
59
+ EXCLUDE_LIST: 2,
60
+ };
61
+
62
+ static FineTunedListType = {
63
+ EXCLUDE_CLIPBOARD_CURSOR_DETECT: 0,
64
+ INCLUDE_CLIPBOARD_DELAY_READ: 1,
65
+ };
66
+
67
+ /** Sentinel value indicating coordinates are unavailable (-99999) */
68
+ static INVALID_COORDINATE = -99999;
69
+
70
+ // Linux only
71
+ static DisplayProtocol = {
72
+ UNKNOWN: 0,
73
+ X11: 1,
74
+ WAYLAND: 2,
75
+ };
76
+
77
+ /**
78
+ * Compositor type constants (Linux only).
79
+ *
80
+ * Values represent the compositor, not the desktop environment (DE).
81
+ * DE-bundled compositors are detected via XDG_CURRENT_DESKTOP:
82
+ * KDE Plasma → KWin, GNOME → mutter, COSMIC → cosmic-comp.
83
+ * Standalone compositors are detected via their own environment variables:
84
+ * Hyprland → HYPRLAND_INSTANCE_SIGNATURE, sway → SWAYSOCK.
85
+ * WLROOTS is a catch-all for generic wlroots-based compositors (labwc, river, etc.).
86
+ */
87
+ static CompositorType = {
88
+ UNKNOWN: 0,
89
+ KWIN: 1,
90
+ MUTTER: 2,
91
+ HYPRLAND: 3,
92
+ SWAY: 4,
93
+ WLROOTS: 5,
94
+ COSMIC_COMP: 6,
95
+ };
96
+
97
+ constructor() {
98
+ if (!nativeModule) {
99
+ throw new Error(
100
+ "[selection-hook] Native module failed to load - only works on Windows, macOS, and Linux"
101
+ );
102
+ }
103
+ super();
104
+ try {
105
+ this.#instance = new nativeModule.TextSelectionHook();
106
+ } catch (err) {
107
+ throw new Error(`[selection-hook] Failed to create native instance: ${err.message}`);
108
+ }
109
+ }
110
+
111
+ /**
112
+ * Start monitoring text selections
113
+ * @param {SelectionConfig} [config] Optional configuration options
114
+ * @returns {boolean} Success status
115
+ */
116
+ start(config = null) {
117
+ const defaultConfig = this.#getDefaultConfig();
118
+
119
+ _debugFlag = config?.debug ?? defaultConfig.debug;
120
+
121
+ if (this.#running) {
122
+ this.#logDebug("Text selection hook already running");
123
+ return true;
124
+ }
125
+
126
+ if (!this.#instance) {
127
+ try {
128
+ this.#instance = new nativeModule.TextSelectionHook();
129
+ } catch (err) {
130
+ this.#handleError("Failed to create hook instance", err);
131
+ return false;
132
+ }
133
+ }
134
+
135
+ if (config) {
136
+ this.#initByConfig(defaultConfig, config);
137
+ }
138
+
139
+ try {
140
+ const callback = (data) => {
141
+ try {
142
+ if (!data || !data.type || !this.#running) return;
143
+
144
+ switch (data.type) {
145
+ case "text-selection":
146
+ const formattedData = this.#formatSelectionData(data);
147
+ if (formattedData) {
148
+ this.emit("text-selection", formattedData);
149
+ }
150
+ break;
151
+ case "mouse-event":
152
+ if (data.action === "mouse-wheel") {
153
+ const { x, y, button, flag } = data;
154
+ this.emit(data.action, { x, y, button, flag });
155
+ } else {
156
+ const { x, y, button } = data;
157
+ this.emit(data.action, { x, y, button });
158
+ }
159
+ break;
160
+ case "keyboard-event":
161
+ {
162
+ const { uniKey, vkCode, sys, scanCode, flags } = data;
163
+ const keyData = { uniKey, vkCode, sys, flags };
164
+ if (scanCode !== undefined) keyData.scanCode = scanCode;
165
+ this.emit(data.action, keyData);
166
+ }
167
+ break;
168
+ case "status":
169
+ this.emit("status", data.status);
170
+ break;
171
+ case "error":
172
+ this.emit("error", new Error(data.error));
173
+ break;
174
+ }
175
+ } catch (err) {
176
+ this.#handleError("Failed to process event data", err);
177
+ }
178
+ };
179
+
180
+ this.#instance.start(callback);
181
+ this.#running = true;
182
+ this.emit("status", "started");
183
+ return true;
184
+ } catch (err) {
185
+ this.#handleError("Failed to start hook", err, "fatal");
186
+ return false;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Stop monitoring text selections, can start again using start()
192
+ * @returns {boolean} Success status
193
+ */
194
+ stop() {
195
+ if (!this.#instance || !this.#running) {
196
+ this.#logDebug("Text selection hook not running");
197
+ return true;
198
+ }
199
+
200
+ try {
201
+ this.#instance.stop();
202
+ this.#running = false;
203
+ this.emit("status", "stopped");
204
+ return true;
205
+ } catch (err) {
206
+ this.#handleError("Failed to stop hook", err, "fatal");
207
+ this.#running = false;
208
+ return false;
209
+ }
210
+ }
211
+
212
+ /**
213
+ * Get current text selection
214
+ * @returns {object|null} Selection data or null
215
+ */
216
+ getCurrentSelection() {
217
+ if (!this.#instance || !this.#running) {
218
+ this.#logDebug("Text selection hook not running");
219
+ return null;
220
+ }
221
+
222
+ try {
223
+ const data = this.#instance.getCurrentSelection();
224
+ return this.#formatSelectionData(data);
225
+ } catch (err) {
226
+ this.#handleError("Failed to get current selection", err);
227
+ return null;
228
+ }
229
+ }
230
+
231
+ /**
232
+ * Enable mousemove events (high CPU usage)
233
+ * @returns {boolean} Success status
234
+ */
235
+ enableMouseMoveEvent() {
236
+ if (!this.#checkInstance()) return false;
237
+
238
+ try {
239
+ this.#instance.enableMouseMoveEvent();
240
+ return true;
241
+ } catch (err) {
242
+ this.#handleError("Failed to enable mouse move events", err);
243
+ return false;
244
+ }
245
+ }
246
+
247
+ /**
248
+ * Disable mousemove events
249
+ * @returns {boolean} Success status
250
+ */
251
+ disableMouseMoveEvent() {
252
+ if (!this.#checkInstance()) return false;
253
+
254
+ try {
255
+ this.#instance.disableMouseMoveEvent();
256
+ return true;
257
+ } catch (err) {
258
+ this.#handleError("Failed to disable mouse move events", err);
259
+ return false;
260
+ }
261
+ }
262
+
263
+ /**
264
+ * Enable clipboard fallback for text selection
265
+ * Uses Ctrl+C as a last resort to get selected text
266
+ * @returns {boolean} Success status
267
+ */
268
+ enableClipboard() {
269
+ if (!this.#checkInstance()) return false;
270
+
271
+ try {
272
+ this.#instance.enableClipboard();
273
+ return true;
274
+ } catch (err) {
275
+ this.#handleError("Failed to enable clipboard fallback", err);
276
+ return false;
277
+ }
278
+ }
279
+
280
+ /**
281
+ * Disable clipboard fallback for text selection
282
+ * Will not use Ctrl+C to get selected text
283
+ * @returns {boolean} Success status
284
+ */
285
+ disableClipboard() {
286
+ if (!this.#checkInstance()) return false;
287
+
288
+ try {
289
+ this.#instance.disableClipboard();
290
+ return true;
291
+ } catch (err) {
292
+ this.#handleError("Failed to disable clipboard fallback", err);
293
+ return false;
294
+ }
295
+ }
296
+
297
+ /**
298
+ * Set clipboard mode and program list for text selection
299
+ *
300
+ * Configures how the clipboard fallback mechanism works for different programs.
301
+ * Mode can be:
302
+ * - DEFAULT: Use clipboard for all programs
303
+ * - INCLUDE_LIST: Only use clipboard for programs in the list
304
+ * - EXCLUDE_LIST: Use clipboard for all programs except those in the list
305
+ *
306
+ * @param {number} mode - Clipboard mode (SelectionHook.ClipboardMode)
307
+ * @param {string[]} programList - Array of program names to include/exclude.
308
+ * @returns {boolean} Success status
309
+ */
310
+ setClipboardMode(mode, programList = []) {
311
+ if (!this.#checkInstance()) return false;
312
+
313
+ const validModes = Object.values(SelectionHook.FilterMode);
314
+ if (!validModes.includes(mode)) {
315
+ this.#handleError("Invalid clipboard mode", new Error("Invalid argument"));
316
+ return false;
317
+ }
318
+
319
+ if (!Array.isArray(programList)) {
320
+ this.#handleError("Program list must be an array", new Error("Invalid argument"));
321
+ return false;
322
+ }
323
+
324
+ try {
325
+ this.#instance.setClipboardMode(mode, programList);
326
+ return true;
327
+ } catch (err) {
328
+ this.#handleError("Failed to set clipboard mode and list", err);
329
+ return false;
330
+ }
331
+ }
332
+
333
+ /**
334
+ * Set global filter mode for text selection
335
+ *
336
+ * Configures how the global filter mechanism works for different programs.
337
+ * Mode can be:
338
+ * - DEFAULT: disable global filter
339
+ * - INCLUDE_LIST: Only use global filter for programs in the list
340
+ * - EXCLUDE_LIST: Use global filter for all programs except those in the list
341
+ *
342
+ * @param {number} mode - Filter mode (SelectionHook.FilterMode)
343
+ * @param {string[]} programList - Array of program names to include/exclude
344
+ * @returns {boolean} Success status
345
+ */
346
+ setGlobalFilterMode(mode, programList = []) {
347
+ if (!this.#checkInstance()) return false;
348
+
349
+ const validModes = Object.values(SelectionHook.FilterMode);
350
+ if (!validModes.includes(mode)) {
351
+ this.#handleError("Invalid filter mode", new Error("Invalid argument"));
352
+ return false;
353
+ }
354
+
355
+ if (!Array.isArray(programList)) {
356
+ this.#handleError("Program list must be an array", new Error("Invalid argument"));
357
+ return false;
358
+ }
359
+
360
+ try {
361
+ this.#instance.setGlobalFilterMode(mode, programList);
362
+ return true;
363
+ } catch (err) {
364
+ this.#handleError("Failed to set global filter mode and list", err);
365
+ return false;
366
+ }
367
+ }
368
+
369
+ /**
370
+ * Set fine-tuned list for specific behaviors
371
+ *
372
+ * Configures fine-tuned lists for specific application behaviors.
373
+ * List types:
374
+ * - EXCLUDE_CLIPBOARD_CURSOR_DETECT: Exclude cursor detection for clipboard operations
375
+ * - INCLUDE_CLIPBOARD_DELAY_READ: Include delay when reading clipboard content
376
+ *
377
+ * @param {number} listType - Fine-tuned list type (SelectionHook.FineTunedListType)
378
+ * @param {string[]} programList - Array of program names for the fine-tuned list
379
+ * @returns {boolean} Success status
380
+ */
381
+ setFineTunedList(listType, programList = []) {
382
+ if (!this.#checkInstance()) return false;
383
+
384
+ const validTypes = Object.values(SelectionHook.FineTunedListType);
385
+ if (!validTypes.includes(listType)) {
386
+ this.#handleError("Invalid fine-tuned list type", new Error("Invalid argument"));
387
+ return false;
388
+ }
389
+
390
+ if (!Array.isArray(programList)) {
391
+ this.#handleError("Program list must be an array", new Error("Invalid argument"));
392
+ return false;
393
+ }
394
+
395
+ try {
396
+ this.#instance.setFineTunedList(listType, programList);
397
+ return true;
398
+ } catch (err) {
399
+ this.#handleError("Failed to set fine-tuned list", err);
400
+ return false;
401
+ }
402
+ }
403
+
404
+ /**
405
+ * Set selection passive mode
406
+ * @param {boolean} passive - Passive mode
407
+ * @returns {boolean} Success status
408
+ */
409
+ setSelectionPassiveMode(passive) {
410
+ if (!this.#checkInstance()) return false;
411
+
412
+ try {
413
+ this.#instance.setSelectionPassiveMode(passive);
414
+ return true;
415
+ } catch (err) {
416
+ this.#handleError("Failed to set selection passive mode", err);
417
+ return false;
418
+ }
419
+ }
420
+
421
+ /**
422
+ * Write text to clipboard
423
+ * @param {string} text - Text to write to clipboard
424
+ * @returns {boolean} Success status
425
+ */
426
+ writeToClipboard(text) {
427
+ if (isLinux) {
428
+ // Linux clipboard write/read is not implemented in native code.
429
+ // Host applications should use their own clipboard API (e.g., Electron clipboard).
430
+ this.#logDebug("writeToClipboard is not supported on Linux");
431
+ return false;
432
+ }
433
+
434
+ if (!this.#checkInstance()) return false;
435
+
436
+ if (typeof text !== "string") {
437
+ this.#handleError("Text must be a string", new Error("Invalid argument"));
438
+ return false;
439
+ }
440
+
441
+ try {
442
+ return this.#instance.writeToClipboard(text);
443
+ } catch (err) {
444
+ this.#handleError("Failed to write text to clipboard", err);
445
+ return false;
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Read text from clipboard
451
+ * @returns {string|null} Text from clipboard or null if empty or error
452
+ */
453
+ readFromClipboard() {
454
+ if (isLinux) {
455
+ // Linux clipboard write/read is not implemented in native code.
456
+ // Host applications should use their own clipboard API (e.g., Electron clipboard).
457
+ this.#logDebug("readFromClipboard is not supported on Linux");
458
+ return null;
459
+ }
460
+
461
+ if (!this.#checkInstance()) return null;
462
+
463
+ try {
464
+ return this.#instance.readFromClipboard();
465
+ } catch (err) {
466
+ this.#handleError("Failed to read text from clipboard", err);
467
+ return null;
468
+ }
469
+ }
470
+
471
+ /**
472
+ * Check if the process is trusted for accessibility (macOS only)
473
+ * @returns {boolean} True if the process is trusted for accessibility, false otherwise
474
+ */
475
+ macIsProcessTrusted() {
476
+ if (!isMac) {
477
+ this.#logDebug("Not supported on this platform");
478
+ return false;
479
+ }
480
+
481
+ if (!this.#checkInstance()) return false;
482
+
483
+ try {
484
+ return this.#instance.macIsProcessTrusted();
485
+ } catch (err) {
486
+ this.#handleError("Failed to check macOS process trust status", err);
487
+ return false;
488
+ }
489
+ }
490
+
491
+ /**
492
+ * Try to request accessibility permissions (macOS only)
493
+ * This MAY show a dialog to the user if permissions are not granted
494
+ * @returns {boolean} The current permission status, not the request result
495
+ */
496
+ macRequestProcessTrust() {
497
+ if (!isMac) {
498
+ this.#logDebug("Not supported on this platform");
499
+ return false;
500
+ }
501
+
502
+ if (!this.#checkInstance()) return false;
503
+
504
+ try {
505
+ return this.#instance.macRequestProcessTrust();
506
+ } catch (err) {
507
+ this.#handleError("Failed to request macOS process trust", err);
508
+ return false;
509
+ }
510
+ }
511
+
512
+ /**
513
+ * Get Linux environment information (Linux only)
514
+ * @returns {object|null} Linux environment info object or null on non-Linux
515
+ */
516
+ linuxGetEnvInfo() {
517
+ if (!isLinux) {
518
+ this.#logDebug("linuxGetEnvInfo is only supported on Linux");
519
+ return null;
520
+ }
521
+
522
+ if (!this.#checkInstance()) return null;
523
+
524
+ try {
525
+ return this.#instance.linuxGetEnvInfo();
526
+ } catch (err) {
527
+ this.#handleError("Failed to get Linux environment info", err);
528
+ return null;
529
+ }
530
+ }
531
+
532
+ /**
533
+ * Check if hook is running
534
+ * @returns {boolean} Running status
535
+ */
536
+ isRunning() {
537
+ return this.#running;
538
+ }
539
+
540
+ /**
541
+ * Release resources
542
+ */
543
+ cleanup() {
544
+ this.stop();
545
+ this.removeAllListeners();
546
+ this.#instance = null;
547
+ }
548
+
549
+ #getDefaultConfig() {
550
+ return {
551
+ debug: false,
552
+ enableMouseMoveEvent: false,
553
+ enableClipboard: true,
554
+ selectionPassiveMode: false,
555
+ clipboardMode: SelectionHook.FilterMode.DEFAULT,
556
+ globalFilterMode: SelectionHook.FilterMode.DEFAULT,
557
+ clipboardFilterList: [],
558
+ globalFilterList: [],
559
+ };
560
+ }
561
+
562
+ #initByConfig(defaultConfig, userConfig) {
563
+ const config = {};
564
+
565
+ // Only keep values that exist in userConfig and differ from defaultConfig
566
+ for (const key in userConfig) {
567
+ if (userConfig[key] !== defaultConfig[key]) {
568
+ config[key] = userConfig[key];
569
+ }
570
+ }
571
+
572
+ // Apply the filtered config
573
+ if (config.enableMouseMoveEvent !== undefined) {
574
+ if (config.enableMouseMoveEvent) {
575
+ this.#instance.enableMouseMoveEvent();
576
+ } else {
577
+ this.#instance.disableMouseMoveEvent();
578
+ }
579
+ }
580
+
581
+ if (config.enableClipboard !== undefined) {
582
+ if (config.enableClipboard) {
583
+ this.#instance.enableClipboard();
584
+ } else {
585
+ this.#instance.disableClipboard();
586
+ }
587
+ }
588
+
589
+ if (config.selectionPassiveMode !== undefined) {
590
+ this.#instance.setSelectionPassiveMode(config.selectionPassiveMode);
591
+ }
592
+
593
+ if (config.clipboardMode !== undefined || config.clipboardFilterList !== undefined) {
594
+ this.#instance.setClipboardMode(
595
+ config.clipboardMode ?? defaultConfig.clipboardMode,
596
+ config.clipboardFilterList ?? defaultConfig.clipboardFilterList
597
+ );
598
+ }
599
+
600
+ if (config.globalFilterMode !== undefined || config.globalFilterList !== undefined) {
601
+ this.#instance.setGlobalFilterMode(
602
+ config.globalFilterMode ?? defaultConfig.globalFilterMode,
603
+ config.globalFilterList ?? defaultConfig.globalFilterList
604
+ );
605
+ }
606
+ }
607
+
608
+ #formatSelectionData(data) {
609
+ if (!data) return null;
610
+
611
+ const selectionInfo = {
612
+ text: data.text,
613
+ programName: data.programName,
614
+ startTop: { x: data.startTopX, y: data.startTopY },
615
+ startBottom: { x: data.startBottomX, y: data.startBottomY },
616
+ endTop: { x: data.endTopX, y: data.endTopY },
617
+ endBottom: { x: data.endBottomX, y: data.endBottomY },
618
+ mousePosStart: { x: data.mouseStartX, y: data.mouseStartY },
619
+ mousePosEnd: { x: data.mouseEndX, y: data.mouseEndY },
620
+ method: data.method || 0,
621
+ posLevel: data.posLevel || 0,
622
+ };
623
+
624
+ if (isMac) {
625
+ selectionInfo.isFullscreen = data.isFullscreen;
626
+ }
627
+
628
+ return selectionInfo;
629
+ }
630
+
631
+ // Private helper methods
632
+ #checkInstance() {
633
+ if (!this.#instance) {
634
+ this.#logDebug("Text selection hook instance not created");
635
+ return false;
636
+ }
637
+ return true;
638
+ }
639
+
640
+ // level: "error" or "fatal"
641
+ // fatal will always show the error message
642
+ #handleError(message, err, level = "error") {
643
+ if (!_debugFlag && level === "error") return;
644
+
645
+ const errorMsg = `${message}: ${err.message}`;
646
+ console.error("[selection-hook] ", errorMsg);
647
+
648
+ if (err.stack) {
649
+ console.error(err.stack);
650
+ }
651
+
652
+ this.emit("error", new Error(errorMsg));
653
+ }
654
+
655
+ #logDebug(message) {
656
+ if (_debugFlag) {
657
+ console.warn("[selection-hook] ", message);
658
+ }
659
+ }
660
+ }
661
+
662
+ module.exports = SelectionHook;
@@ -0,0 +1,10 @@
1
+ {
2
+ "name": "selection-hook",
3
+ "version": "2.1.2",
4
+ "private": true,
5
+ "type": "commonjs",
6
+ "main": "index.js",
7
+ "types": "index.d.ts",
8
+ "license": "MIT",
9
+ "description": "Vendored from selection-hook@2.1.2 (https://github.com/0xfullex/selection-hook) with prebuilds for darwin-arm64, darwin-x64, win32-x64, win32-arm64."
10
+ }