@mintplex-labs/advanced-selection-hook 1.0.1 → 1.0.3

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/index.d.ts CHANGED
@@ -321,6 +321,15 @@ declare class SelectionHook extends EventEmitter {
321
321
  */
322
322
  readFromClipboard(): string | null;
323
323
 
324
+ /**
325
+ * Replace the last selected text with new text (Windows only).
326
+ * Uses UI Automation ValuePattern.SetValue so the highlighted range is replaced
327
+ * without focus/clipboard. Only works when the last selection was via UIA in an editable control.
328
+ * @param replacementText - UTF-8 text to insert in place of the selection
329
+ * @returns true if replacement was applied, false otherwise
330
+ */
331
+ replaceSelectedText(replacementText: string): boolean;
332
+
324
333
  /**
325
334
  * Check if the process is trusted for accessibility (macOS only)
326
335
  *
package/index.js CHANGED
@@ -422,6 +422,29 @@ class SelectionHook extends EventEmitter {
422
422
  }
423
423
  }
424
424
 
425
+ /**
426
+ * Replace the last selected text with new text (Windows only).
427
+ * Uses UI Automation ValuePattern.SetValue to push text into the highlighted range
428
+ * without relying on focus/clipboard. Only works when the last selection was obtained
429
+ * via UI Automation in an editable control.
430
+ * @param {string} replacementText - UTF-8 text to insert in place of the selection
431
+ * @returns {boolean} true if replacement was applied, false otherwise
432
+ */
433
+ replaceSelectedText(replacementText) {
434
+ if (typeof replacementText !== "string") {
435
+ this.#handleError("replaceSelectedText requires a string", new Error("Invalid argument"));
436
+ return false;
437
+ }
438
+ if (!this.#instance) return false;
439
+
440
+ try {
441
+ return this.#instance.replaceSelectedText(replacementText);
442
+ } catch (err) {
443
+ this.#handleError("Failed to replace selected text", err);
444
+ return false;
445
+ }
446
+ }
447
+
425
448
  /**
426
449
  * Check if the process is trusted for accessibility (macOS only)
427
450
  * @returns {boolean} True if the process is trusted for accessibility, false otherwise
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mintplex-labs/advanced-selection-hook",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
4
4
  "description": "Text selection monitoring of native Node.js module with N-API across applications",
5
5
  "author": "@mintplex-labs",
6
6
  "repository": {
@@ -10,21 +10,16 @@
10
10
  "main": "index.js",
11
11
  "types": "index.d.ts",
12
12
  "files": [
13
- "examples",
14
13
  "prebuilds",
15
14
  "index.d.ts",
16
15
  "index.js"
17
16
  ],
18
17
  "scripts": {
19
- "install": "node-gyp-build",
20
18
  "rebuild": "node-gyp rebuild",
21
- "prebuild": "npm run prebuild:win && npm run prebuild:mac",
22
- "prebuild:win": "npm run prebuild:win:x64 && npm run prebuild:win:arm64",
23
- "prebuild:win:x64": "prebuildify --napi --platform=win32 --arch=x64",
24
- "prebuild:win:arm64": "prebuildify --napi --platform=win32 --arch=arm64",
25
- "prebuild:mac": "npm run prebuild:mac:x64 && npm run prebuild:mac:arm64",
26
- "prebuild:mac:x64": "prebuildify --napi --platform=darwin --arch=x64",
27
- "prebuild:mac:arm64": "prebuildify --napi --platform=darwin --arch=arm64",
19
+ "build:win:x64": "prebuildify --napi --platform=win32 --arch=x64",
20
+ "build:win:arm64": "prebuildify --napi --target 20.0.0 --platform=win32 --arch=arm64",
21
+ "build:mac:x64": "prebuildify --napi --platform=darwin --arch=x64",
22
+ "build:mac:arm64": "prebuildify --napi --platform=darwin --arch=arm64",
28
23
  "demo": "node --trace-deprecation --force-node-api-uncaught-exceptions-policy=true examples/node-demo.js",
29
24
  "publish:dev": "npm publish --dry-run",
30
25
  "publish:prod": "npm publish --access public"
@@ -59,4 +54,4 @@
59
54
  "publishConfig": {
60
55
  "access": "public"
61
56
  }
62
- }
57
+ }
@@ -1,727 +0,0 @@
1
- /**
2
- * Text Selection Hook - Test Application
3
- *
4
- * This test file demonstrates the functionality of the Node-API native module
5
- * for monitoring text selections across applications on Windows.
6
- *
7
- * Features demonstrated:
8
- * - Text selection detection (drag and double-click)
9
- * - Mouse and keyboard event monitoring
10
- * - Interactive controls for testing different features
11
- */
12
-
13
- // ===========================
14
- // === Module Dependencies ===
15
- // ===========================
16
- const SelectionHook = require("../index.js");
17
-
18
- // ===========================
19
- // === Configuration ========
20
- // ===========================
21
-
22
- /**
23
- * Configuration flags for event display
24
- * Toggle these using keyboard shortcuts during runtime
25
- */
26
- const config = {
27
- showMouseMoveEvents: false, // High CPU usage when enabled
28
- showMouseEvents: false, // Mouse clicks and wheel events
29
- showKeyboardEvents: false, // Keyboard key press/release events
30
- clipboardFallbackEnabled: false, // Use clipboard as fallback for text selection
31
- clipboardMode: 0, // Default clipboard mode (see SelectionHook.FilterMode)
32
- globalFilterMode: 0, // Default global filter mode (see SelectionHook.FilterMode)
33
- passiveModeEnabled: false, // Passive mode for text selection
34
- fineTunedListEnabled: false, // Fine-tuned list for specific application behaviors
35
- hookStarted: true, // Track if the hook is started (default: ON)
36
- };
37
-
38
- // Add variables for Ctrl key hold detection
39
- let ctrlKeyPressTime = null;
40
- let ctrlKeyTriggered = false; // Flag to prevent multiple triggers
41
- const CTRL_HOLD_THRESHOLD = 500; // 500ms threshold for Ctrl key hold
42
-
43
- // Program list for clipboard mode and global filter
44
- const programList = ["cursor.exe", "vscode.exe", "notepad.exe"];
45
-
46
- // Fine-tuned list for specific application behaviors
47
- const fineTunedList = ["acrobat.exe", "wps.exe", "cajviewer.exe"];
48
-
49
- /**
50
- * ANSI color codes for console output formatting
51
- */
52
- const colors = {
53
- info: "\x1b[36m%s\x1b[0m", // Cyan - Used for general information
54
- success: "\x1b[32m%s\x1b[0m", // Green - Used for successful operations and selections
55
- warning: "\x1b[33m%s\x1b[0m", // Yellow - Used for position data and toggles
56
- error: "\x1b[31m%s\x1b[0m", // Red - Used for errors
57
- highlight: "\x1b[35m%s\x1b[0m", // Magenta - Used for keyboard events
58
- };
59
-
60
- // ===========================
61
- // === Initialize Hook ======
62
- // ===========================
63
-
64
- // Create instance of the hook
65
- const hook = new SelectionHook();
66
-
67
- // Exit if initialization failed
68
- if (!hook) {
69
- console.error(colors.error, "Failed to initialize text selection hook");
70
- process.exit(1);
71
- }
72
-
73
- // ===========================
74
- // === Core Functions =======
75
- // ===========================
76
-
77
- /**
78
- * Main entry point - initializes and starts the application
79
- */
80
- function main() {
81
- printWelcomeMessage();
82
- setupEventListeners();
83
- startHook();
84
- setupInputHandlers();
85
- }
86
-
87
- /**
88
- * Display welcome message and usage instructions
89
- */
90
- function printWelcomeMessage() {
91
- console.log(colors.info, "=== Text Selection Hook Test ===");
92
- console.log("Testing mouse action detection for text selection");
93
-
94
- console.log("\nPlease perform the following actions:");
95
- console.log("1. Drag to select text (distance > 8px) - should log 'action_drag_selection'");
96
- console.log("2. Double-click to select text - should log 'action_dblclick_selection'");
97
-
98
- console.log("\nCoordinates Information:");
99
- console.log(" - startTop/startBottom: First paragraph's left-top and left-bottom points");
100
- console.log(" - endTop/endBottom: Last paragraph's right-top and right-bottom points");
101
- console.log(" - mousePosStart: Initial mouse position when selection started");
102
- console.log(" - mousePosEnd: Final mouse position when selection ended");
103
-
104
- console.log("\nKeyboard Controls:");
105
- console.log(" S - Toggle start/stop hook (default: ON)");
106
- console.log(" M - Toggle mouse move events (default: OFF)");
107
- console.log(" E - Toggle other mouse events (default: OFF)");
108
- console.log(" K - Toggle keyboard events (default: OFF)");
109
- console.log(" C - Display current selection");
110
- console.log(" B - Toggle clipboard fallback (default: OFF)");
111
- console.log(" L - Toggle clipboard mode (DEFAULT → EXCLUDE_LIST → INCLUDE_LIST)");
112
- console.log(" F - Toggle global filter mode (DEFAULT → EXCLUDE_LIST → INCLUDE_LIST)");
113
- console.log(" P - Toggle passive mode (default: OFF)");
114
- console.log(" T - Toggle fine-tuned list (default: OFF)");
115
- console.log(" W - Write text 'Test clipboard write from selection-hook' to clipboard");
116
- console.log(" R - Read text from clipboard");
117
- console.log(" A - Check accessibility permissions (macOS only)");
118
- console.log(" Q - Request accessibility permissions (macOS only)");
119
- console.log(" ? - Show help");
120
- console.log(" Ctrl+C - Exit program");
121
- console.log("\nIn passive mode, press & hold Ctrl key to trigger text selection");
122
- }
123
-
124
- /**
125
- * Start the hook and begin listening for events
126
- */
127
- function startHook() {
128
- if (hook.start({ debug: true })) {
129
- console.log(colors.success, "Text selection listener started successfully");
130
- console.log(colors.info, "Mouse move events are disabled by default (press 'M' to toggle)");
131
- } else {
132
- console.error(colors.error, "Failed to start text selection listener");
133
- }
134
- }
135
-
136
- /**
137
- * Clean up resources and exit the application
138
- */
139
- function cleanup() {
140
- console.log(colors.info, "\nStopping text selection listener...");
141
-
142
- // Make sure to disable mouse move events before stopping to reduce resource usage
143
- if (config.showMouseMoveEvents) {
144
- hook.disableMouseMoveEvent();
145
- }
146
-
147
- // Disable clipboard fallback if enabled
148
- if (config.clipboardFallbackEnabled) {
149
- hook.disableClipboard();
150
- }
151
-
152
- hook.stop();
153
- hook.cleanup();
154
- process.exit(0);
155
- }
156
-
157
- // ===========================
158
- // === Event Listeners ======
159
- // ===========================
160
-
161
- /**
162
- * Set up all event listeners for the hook
163
- */
164
- function setupEventListeners() {
165
- // Text selection events - our primary functionality
166
- hook.on("text-selection", (selectionData) => {
167
- showSelection(selectionData);
168
- });
169
-
170
- // Status events from the native module
171
- hook.on("status", (status) => {
172
- console.log(colors.info, "Listener status:", status);
173
- });
174
-
175
- // Setup mouse events
176
- setupMouseEventListeners();
177
-
178
- // Setup keyboard events
179
- setupKeyboardEventListeners();
180
-
181
- // Error events
182
- hook.on("error", (error) => {
183
- console.error(colors.error, "Error:", error.message);
184
- });
185
- }
186
-
187
- /**
188
- * Set up mouse-related event listeners
189
- */
190
- function setupMouseEventListeners() {
191
- // Mouse move events (high CPU usage - disabled by default)
192
- hook.on("mouse-move", (eventData) => {
193
- if (config.showMouseMoveEvents) {
194
- console.log(
195
- colors.warning,
196
- "Mouse move:",
197
- `x: ${eventData.x}, y: ${eventData.y}, button: ${eventData.button}`
198
- );
199
- }
200
- });
201
-
202
- // Mouse button events
203
- hook.on("mouse-up", (eventData) => {
204
- if (config.showMouseEvents) {
205
- console.log(
206
- colors.warning,
207
- "Mouse up:",
208
- `button: ${eventData.button}, x: ${eventData.x}, y: ${eventData.y}`
209
- );
210
- }
211
- });
212
-
213
- hook.on("mouse-down", (eventData) => {
214
- if (config.showMouseEvents) {
215
- console.log(
216
- colors.warning,
217
- "Mouse down:",
218
- `button: ${eventData.button}, x: ${eventData.x}, y: ${eventData.y}`
219
- );
220
- }
221
- });
222
-
223
- // Mouse wheel events
224
- hook.on("mouse-wheel", (eventData) => {
225
- if (config.showMouseEvents) {
226
- console.log(
227
- colors.warning,
228
- "Mouse wheel:",
229
- `button: ${eventData.button}, direction: ${eventData.flag > 0 ? "up/right" : "down/left"}`
230
- );
231
- }
232
- });
233
- }
234
-
235
- /**
236
- * Set up keyboard-related event listeners
237
- */
238
- function setupKeyboardEventListeners() {
239
- // Key down events
240
- hook.on("key-down", (eventData) => {
241
- if (config.showKeyboardEvents) {
242
- console.log(
243
- colors.highlight,
244
- "Key down:",
245
- `uniKey: ${eventData.uniKey}, vkCode: ${eventData.vkCode}, scanCode: ${
246
- eventData.scanCode
247
- }, flags: ${eventData.flags}${eventData.sys ? ", system key" : ""}`
248
- );
249
- }
250
-
251
- // Handle Ctrl+C for exit
252
- if (eventData.vkCode === 67 && eventData.flags & 0x0001) {
253
- // 67 is 'C', 0x0001 is Ctrl flag
254
- cleanup();
255
- return;
256
- }
257
-
258
- // Special handling for Ctrl key (vkCode 162) to get current selection in passive mode
259
- if (eventData.vkCode === 162 && config.passiveModeEnabled) {
260
- const currentTime = Date.now();
261
-
262
- // Initialize press time if not set
263
- if (ctrlKeyPressTime === null) {
264
- ctrlKeyPressTime = currentTime;
265
- ctrlKeyTriggered = false; // Reset trigger flag
266
- return;
267
- }
268
-
269
- // Check if held long enough and not already triggered
270
- const holdDuration = currentTime - ctrlKeyPressTime;
271
- if (holdDuration >= CTRL_HOLD_THRESHOLD && !ctrlKeyTriggered) {
272
- console.log(
273
- colors.info,
274
- `Ctrl held for ${holdDuration}ms, requesting current selection in passive mode`
275
- );
276
- const selectionData = hook.getCurrentSelection();
277
- if (selectionData) {
278
- showSelection(selectionData);
279
- } else {
280
- console.log(colors.warning, "No selection data available");
281
- }
282
- ctrlKeyTriggered = true; // Set flag to prevent further triggers
283
- }
284
- }
285
- });
286
-
287
- // Key up events
288
- hook.on("key-up", (eventData) => {
289
- if (config.showKeyboardEvents) {
290
- console.log(
291
- colors.highlight,
292
- "Key up:",
293
- `uniKey: ${eventData.uniKey}, vkCode: ${eventData.vkCode}, scanCode: ${
294
- eventData.scanCode
295
- }, flags: ${eventData.flags}${eventData.sys ? ", system key" : ""}`
296
- );
297
- }
298
-
299
- // Reset variables when Ctrl is released
300
- if (eventData.vkCode === 162 && config.passiveModeEnabled) {
301
- ctrlKeyPressTime = null;
302
- ctrlKeyTriggered = false;
303
- }
304
- });
305
- }
306
-
307
- // ===========================
308
- // === User Input Handling ==
309
- // ===========================
310
-
311
- /**
312
- * Configure handlers for user input
313
- */
314
- function setupInputHandlers() {
315
- // Set up standard input in raw mode for immediate key handling
316
- process.stdin.setRawMode(true);
317
- process.stdin.resume();
318
- process.stdin.on("data", handleKeyPress);
319
-
320
- // Handle Ctrl+C for graceful exit
321
- process.on("SIGINT", cleanup);
322
- }
323
-
324
- /**
325
- * Process keyboard input from the user
326
- * @param {Buffer} key - The key buffer from stdin
327
- */
328
- function handleKeyPress(key) {
329
- const keyStr = key.toString();
330
-
331
- // Exit on Ctrl+C
332
- if (keyStr === "\u0003") {
333
- cleanup();
334
- return;
335
- }
336
-
337
- switch (keyStr.toLowerCase()) {
338
- case "s": // Toggle start/stop hook
339
- toggleHookStartStop();
340
- break;
341
-
342
- case "m": // Toggle mouse move events
343
- toggleMouseMoveEvents();
344
- break;
345
-
346
- case "e": // Toggle other mouse events
347
- config.showMouseEvents = !config.showMouseEvents;
348
- console.log(colors.success, `Mouse events: ${config.showMouseEvents ? "ON" : "OFF"}`);
349
- break;
350
-
351
- case "k": // Toggle keyboard events
352
- config.showKeyboardEvents = !config.showKeyboardEvents;
353
- console.log(colors.success, `Keyboard events: ${config.showKeyboardEvents ? "ON" : "OFF"}`);
354
- break;
355
-
356
- case "c": // Display current selection
357
- const selectionData = hook.getCurrentSelection();
358
- if (selectionData) {
359
- console.log(colors.success, "Current selection retrieved");
360
- showSelection(selectionData);
361
- } else {
362
- console.log(colors.warning, "No current selection available");
363
- }
364
- break;
365
-
366
- case "b": // Toggle clipboard fallback
367
- config.clipboardFallbackEnabled = !config.clipboardFallbackEnabled;
368
-
369
- if (config.clipboardFallbackEnabled) {
370
- // Enable clipboard fallback in the native module
371
- const success = hook.enableClipboard();
372
- if (success) {
373
- console.log(colors.success, "Clipboard fallback: ENABLED");
374
- } else {
375
- config.clipboardFallbackEnabled = false;
376
- console.log(colors.error, "Failed to enable clipboard fallback");
377
- }
378
- } else {
379
- // Disable clipboard fallback in the native module
380
- const success = hook.disableClipboard();
381
- if (success) {
382
- console.log(colors.warning, "Clipboard fallback: DISABLED");
383
- } else {
384
- console.log(colors.error, "Failed to disable clipboard fallback");
385
- }
386
- }
387
- break;
388
-
389
- case "l": // Toggle clipboard mode
390
- toggleClipboardMode();
391
- break;
392
-
393
- case "f": // Toggle global filter mode
394
- toggleGlobalFilterMode();
395
- break;
396
-
397
- case "p": // Toggle passive mode
398
- togglePassiveMode();
399
- break;
400
-
401
- case "t": // Toggle fine-tuned list
402
- toggleFineTunedList();
403
- break;
404
-
405
- case "w":
406
- // Write test text to clipboard
407
- const testText = "Test clipboard write from selection-hook";
408
- const success = hook.writeToClipboard(testText);
409
- if (success) {
410
- console.log(colors.success, `Successfully wrote text to clipboard: "${testText}"`);
411
- } else {
412
- console.log(colors.error, "Failed to write text to clipboard");
413
- }
414
- break;
415
-
416
- case "r":
417
- // Read text from clipboard
418
- const clipboardText = hook.readFromClipboard();
419
- if (clipboardText) {
420
- console.log(colors.success, `Text read from clipboard: "${clipboardText}"`);
421
- } else {
422
- console.log(colors.warning, "Failed to read text from clipboard");
423
- }
424
- break;
425
-
426
- case "a":
427
- // Check accessibility permissions (macOS only)
428
- if (process.platform === "darwin") {
429
- const isTrusted = hook.macIsProcessTrusted();
430
- if (isTrusted) {
431
- console.log(colors.success, "Process is trusted for accessibility on macOS");
432
- } else {
433
- console.log(colors.warning, "Process is NOT trusted for accessibility on macOS");
434
- console.log(
435
- colors.info,
436
- "You may need to grant accessibility permissions in System Preferences"
437
- );
438
- }
439
- } else {
440
- console.log(colors.info, "Accessibility permission check is only available on macOS");
441
- }
442
- break;
443
-
444
- case "q":
445
- // Request accessibility permissions (macOS only)
446
- if (process.platform === "darwin") {
447
- console.log(colors.info, "Requesting accessibility permissions...");
448
- const currentStatus = hook.macRequestProcessTrust();
449
- if (currentStatus) {
450
- console.log(colors.success, "Process is already trusted for accessibility on macOS");
451
- } else {
452
- console.log(colors.warning, "Process is not trusted for accessibility on macOS");
453
- console.log(colors.info, "A system dialog may have appeared to grant permissions");
454
- console.log(
455
- colors.info,
456
- "Please check System Preferences > Security & Privacy > Accessibility"
457
- );
458
- }
459
- } else {
460
- console.log(colors.info, "Accessibility permission request is only available on macOS");
461
- }
462
- break;
463
-
464
- case "?":
465
- // Show help
466
- printWelcomeMessage();
467
- break;
468
-
469
- default:
470
- break;
471
- }
472
- }
473
-
474
- /**
475
- * Toggle hook start/stop
476
- * Controls whether the hook is actively listening for events
477
- */
478
- function toggleHookStartStop() {
479
- config.hookStarted = !config.hookStarted;
480
-
481
- if (config.hookStarted) {
482
- // Start the hook
483
- const success = hook.start({ debug: true });
484
- if (success) {
485
- console.log(colors.success, "Text selection hook: STARTED");
486
- } else {
487
- config.hookStarted = false; // Revert the toggle
488
- console.log(colors.error, "Failed to start text selection hook");
489
- }
490
- } else {
491
- // Stop the hook
492
- const success = hook.stop();
493
- if (success) {
494
- console.log(colors.warning, "Text selection hook: STOPPED");
495
- } else {
496
- config.hookStarted = true; // Revert the toggle
497
- console.log(colors.error, "Failed to stop text selection hook");
498
- }
499
- }
500
- }
501
-
502
- /**
503
- * Toggle mouse move event tracking
504
- * Mouse move events use significant CPU resources, so they're disabled by default
505
- */
506
- function toggleMouseMoveEvents() {
507
- config.showMouseMoveEvents = !config.showMouseMoveEvents;
508
-
509
- if (config.showMouseMoveEvents) {
510
- // Enable mouse move events in the native module
511
- const success = hook.enableMouseMoveEvent();
512
- if (success) {
513
- console.log(colors.success, "Mouse move events: ENABLED");
514
- } else {
515
- config.showMouseMoveEvents = false;
516
- console.log(colors.error, "Failed to enable mouse move events");
517
- }
518
- } else {
519
- // Disable mouse move events in the native module
520
- const success = hook.disableMouseMoveEvent();
521
- if (success) {
522
- console.log(colors.warning, "Mouse move events: DISABLED");
523
- } else {
524
- console.log(
525
- colors.error,
526
- "Failed to disable mouse move events, but events won't be displayed"
527
- );
528
- }
529
- }
530
- }
531
-
532
- /**
533
- * Toggle passive mode for text selection
534
- * In passive mode, the hook doesn't automatically monitor selections
535
- * and requires manual triggering (e.g., with Alt key)
536
- */
537
- function togglePassiveMode() {
538
- config.passiveModeEnabled = !config.passiveModeEnabled;
539
-
540
- // Set passive mode in the native module
541
- const success = hook.setSelectionPassiveMode(config.passiveModeEnabled);
542
- if (success) {
543
- console.log(
544
- colors.success,
545
- `Passive mode: ${config.passiveModeEnabled ? "ENABLED" : "DISABLED"}`
546
- );
547
- if (config.passiveModeEnabled) {
548
- console.log(colors.info, "Press & hold Ctrl key to trigger text selection manually");
549
- }
550
- } else {
551
- config.passiveModeEnabled = !config.passiveModeEnabled; // Revert the toggle
552
- console.log(colors.error, "Failed to set passive mode");
553
- }
554
- }
555
-
556
- /**
557
- * Toggle fine-tuned list for specific application behaviors
558
- */
559
- function toggleFineTunedList() {
560
- config.fineTunedListEnabled = !config.fineTunedListEnabled;
561
-
562
- const programList = config.fineTunedListEnabled ? fineTunedList : [];
563
-
564
- // Set fine-tuned list for both types simultaneously
565
- const excludeClipboardSuccess = hook.setFineTunedList(
566
- SelectionHook.FineTunedListType.EXCLUDE_CLIPBOARD_CURSOR_DETECT,
567
- programList
568
- );
569
-
570
- const includeDelaySuccess = hook.setFineTunedList(
571
- SelectionHook.FineTunedListType.INCLUDE_CLIPBOARD_DELAY_READ,
572
- programList
573
- );
574
-
575
- if (excludeClipboardSuccess && includeDelaySuccess) {
576
- console.log(
577
- colors.success,
578
- `Fine-tuned list: ${config.fineTunedListEnabled ? "ENABLED" : "DISABLED"}`
579
- );
580
- if (config.fineTunedListEnabled) {
581
- console.log(colors.info, `Programs in fine-tuned list: ${fineTunedList.join(", ")}`);
582
- console.log(
583
- colors.info,
584
- "Applied to both EXCLUDE_CLIPBOARD_CURSOR_DETECT and INCLUDE_CLIPBOARD_DELAY_READ"
585
- );
586
- }
587
- } else {
588
- config.fineTunedListEnabled = !config.fineTunedListEnabled; // Revert the toggle
589
- console.log(colors.error, "Failed to set fine-tuned list");
590
- if (!excludeClipboardSuccess) {
591
- console.log(colors.error, "- EXCLUDE_CLIPBOARD_CURSOR_DETECT failed");
592
- }
593
- if (!includeDelaySuccess) {
594
- console.log(colors.error, "- INCLUDE_CLIPBOARD_DELAY_READ failed");
595
- }
596
- }
597
- }
598
-
599
- /**
600
- * Get clipboard mode name from numeric value
601
- * @param {number} mode - Clipboard mode value
602
- * @returns {string} Mode name
603
- */
604
- function getFilterModeName(mode) {
605
- const modeNames = {
606
- [SelectionHook.FilterMode.DEFAULT]: "DEFAULT",
607
- [SelectionHook.FilterMode.EXCLUDE_LIST]: "EXCLUDE_LIST",
608
- [SelectionHook.FilterMode.INCLUDE_LIST]: "INCLUDE_LIST",
609
- };
610
- return modeNames[mode] || "UNKNOWN";
611
- }
612
-
613
- /**
614
- * Toggle clipboard mode (DEFAULT -> EXCLUDE_LIST -> INCLUDE_LIST -> DEFAULT)
615
- */
616
- function toggleClipboardMode() {
617
- // Rotate through the modes
618
- config.clipboardMode = (config.clipboardMode + 1) % 3;
619
-
620
- // Apply the new mode if clipboard fallback is enabled
621
- const success = hook.setClipboardMode(config.clipboardMode, programList);
622
- if (success) {
623
- console.log(
624
- colors.success,
625
- `Clipboard mode set to ${getFilterModeName(config.clipboardMode)} for ${programList.join(
626
- ", "
627
- )}`
628
- );
629
- } else {
630
- console.log(colors.error, "Failed to set clipboard mode");
631
- }
632
- }
633
-
634
- /**
635
- * Toggle global filter mode (DEFAULT -> EXCLUDE_LIST -> INCLUDE_LIST -> DEFAULT)
636
- */
637
- function toggleGlobalFilterMode() {
638
- // Rotate through the modes
639
- config.globalFilterMode = (config.globalFilterMode + 1) % 3;
640
-
641
- // Apply the new mode
642
- const success = hook.setGlobalFilterMode(config.globalFilterMode, programList);
643
- if (success) {
644
- console.log(
645
- colors.success,
646
- `Global filter mode set to ${getFilterModeName(
647
- config.globalFilterMode
648
- )} for ${programList.join(", ")}`
649
- );
650
- } else {
651
- console.log(colors.error, "Failed to set global filter mode");
652
- }
653
- }
654
-
655
- // ===========================
656
- // === Utility Functions ====
657
- // ===========================
658
-
659
- /**
660
- * Display text selection data
661
- * @param {Object} selectionData - Selection data from the hook
662
- */
663
- function showSelection(selectionData) {
664
- if (!selectionData) {
665
- return;
666
- }
667
-
668
- console.log(colors.info, `=== Detected selected text (${selectionData.text.length}) ===`);
669
-
670
- console.log(selectionData.text.replace(/\r\n/g, "\n").replace(/\r(?!\n)/g, "\n"));
671
-
672
- // Selection method and position level maps
673
- const methodMap = {
674
- 0: "None",
675
- 1: "UI Automation",
676
- 2: "Focus Control",
677
- 3: "Accessibility",
678
- 99: "Clipboard",
679
- };
680
-
681
- const posLevelMap = {
682
- 0: "None",
683
- 1: "Mouse Single",
684
- 2: "Mouse Dual",
685
- 3: "Selection Full",
686
- 4: "Selection Detailed",
687
- };
688
-
689
- console.log(colors.warning, "=== Selection Information ===");
690
- console.log(colors.warning, "Program Name:", selectionData.programName);
691
- console.log(
692
- colors.warning,
693
- "Method: ",
694
- `${methodMap[selectionData.method] || selectionData.method}`
695
- );
696
- console.log(
697
- colors.warning,
698
- "Position Level: ",
699
- `${posLevelMap[selectionData.posLevel] || selectionData.posLevel}`
700
- );
701
-
702
- // Display all available coordinates
703
- if (selectionData.posLevel >= 1) {
704
- console.log(
705
- colors.warning,
706
- "Mouse: ",
707
- `(${selectionData.mousePosStart.x}, ${selectionData.mousePosStart.y}) -> (${selectionData.mousePosEnd.x}, ${selectionData.mousePosEnd.y})`
708
- );
709
- }
710
- if (selectionData.posLevel >= 3) {
711
- console.log(
712
- colors.warning,
713
- ` startTop(${selectionData.startTop.x}, ${selectionData.startTop.y})\t endTop(${selectionData.endTop.x}, ${selectionData.endTop.y})`
714
- );
715
- console.log(
716
- colors.warning,
717
- `startBottom(${selectionData.startBottom.x}, ${selectionData.startBottom.y})\tendBottom(${selectionData.endBottom.x}, ${selectionData.endBottom.y})`
718
- );
719
- }
720
- }
721
-
722
- // ===========================
723
- // === Start Application ====
724
- // ===========================
725
-
726
- // Launch the application
727
- main();