sendkeys-macos
Advanced tools
Comparing version 1.1.2 to 1.2.0
# Changelog | ||
## [1.2.0](https://www.github.com/socsieng/sendkeys-macos/compare/v1.1.2...v1.2.0) (2020-12-24) | ||
### Features | ||
* add experimental support for mouse movements ([7f5b93f](https://www.github.com/socsieng/sendkeys-macos/commit/7f5b93fe3cea210d587fe7b700064583b94aad2b)) | ||
### [1.1.2](https://www.github.com/socsieng/sendkeys-macos/compare/v1.1.1...v1.1.2) (2020-09-22) | ||
@@ -4,0 +11,0 @@ |
{ | ||
"name": "sendkeys-macos", | ||
"version": "1.1.2", | ||
"version": "1.2.0", | ||
"description": "Sends keystrokes to a given application with customizable delays to simulate typing", | ||
@@ -13,3 +13,5 @@ "repository": { | ||
"test": "mocha ./**/*.spec.js", | ||
"tag": "git tag -f \"v$(bin/version)\"" | ||
"tag": "git tag -f \"v$(bin/version)\"", | ||
"build": "scripts/build.sh", | ||
"install": "scripts/build.sh" | ||
}, | ||
@@ -21,3 +23,7 @@ "keywords": [ | ||
"macos", | ||
"automator" | ||
"automator", | ||
"mouse", | ||
"move", | ||
"drag", | ||
"click" | ||
], | ||
@@ -24,0 +30,0 @@ "author": "socsieng", |
@@ -8,2 +8,4 @@ # SendKeys for macOS | ||
_Includes experimental support for mouse operations._ | ||
# Installation | ||
@@ -87,2 +89,13 @@ | ||
### Mouse commands | ||
Experimental support for mouse commands, including: | ||
- Mouse move: `<m:x1,y1,x2,y2:duration_in_seconds>` | ||
- Mouse click: `<m:button:number_of_clicks>` | ||
- `button` supported values `left`, `center`, `right`. | ||
- `number_of_clicks` defaults to `1` | ||
- Mouse drag: `<d:x1,y1,x2,y2:duration_in_seconds:button>` | ||
- `button` supported values `left`, `center`, `right`. Defaults to `left`. | ||
### Continuation | ||
@@ -89,0 +102,0 @@ |
101
src/index.js
const { execFileSync } = require('child_process'); | ||
const generator = require('./generator'); | ||
const { splitIncluding } = require('./splitter'); | ||
const path = require('path'); | ||
const { existsSync } = require('fs'); | ||
@@ -10,3 +13,12 @@ const defaultOptions = { | ||
function sendKeys(appName, keystrokes, options = {}) { | ||
// matches <m:1,2,3,4:0.5> or <m:1,2,3,4> | ||
const mouseMoveExpression = /\<m:((\d+),(\d+),)?(\d+),(\d+)(:([\d.]+))?\>/; | ||
// matches <m:left:1> or <m:right> | ||
const mouseClickExpression = /\<m:([a-z]+)(:(\d+))?\>/; | ||
// matches <d:1,2,3,4:0.5:left> or <d:1,2:0.5:left> or <d:1,2,3,4:0.5> | ||
const mouseDragExpression = /\<d:((\d+),(\d+),)?(\d+),(\d+)(:([\d.]+))?(:([a-z]+))?\>/; | ||
const mouseExpressions = [mouseMoveExpression, mouseClickExpression, mouseDragExpression]; | ||
function sendKeys(appName, /** @type {string} */ keystrokes, options = {}) { | ||
const mergedOptions = { | ||
@@ -16,7 +28,59 @@ ...defaultOptions, | ||
}; | ||
const output = generator.generate(keystrokes, appName, mergedOptions.delay, mergedOptions.initialDelay); | ||
if (mergedOptions.generateScript) { | ||
const output = generator.generate(keystrokes, appName, mergedOptions.delay, mergedOptions.initialDelay); | ||
process.stdout.write(output); | ||
return; | ||
} | ||
const usingMouse = hasMouseCommands(keystrokes); | ||
const hasMouse = existsSync(path.resolve(__dirname, '../build/mouse')); | ||
if (usingMouse && hasMouse) { | ||
// mouse support | ||
const fragments = splitIncluding( | ||
keystrokes, | ||
mouseExpressions.map(exp => new RegExp(exp, 'g')), | ||
); | ||
let isFirstActivation = true; | ||
for (const fragment of fragments) { | ||
let result; | ||
let match; | ||
if ((match = mouseMoveExpression.exec(fragment))) { | ||
const [, , x1, y1, x2, y2, , duration] = match; | ||
result = sendMouseMove(x1, y1, x2, y2, duration); | ||
} else if ((match = mouseClickExpression.exec(fragment))) { | ||
const [, button, , count] = match; | ||
result = sendMouseClick(button, count); | ||
} else if ((match = mouseDragExpression.exec(fragment))) { | ||
const [, , x1, y1, x2, y2, , duration, , button] = match; | ||
result = sendMouseDrag(x1, y1, x2, y2, duration, button); | ||
} else { | ||
const output = generator.generate( | ||
fragment, | ||
isFirstActivation ? appName : undefined, | ||
mergedOptions.delay, | ||
isFirstActivation ? mergedOptions.initialDelay : 0, | ||
); | ||
result = execFileSync('osascript', ['-l', 'JavaScript', '-e', output], { | ||
encoding: 'utf8', | ||
stdio: 'pipe', | ||
}); | ||
isFirstActivation = false; | ||
} | ||
if (result && result.trim()) { | ||
console.log(result); | ||
} | ||
} | ||
} else { | ||
if (usingMouse && !hasMouse) { | ||
console.warn( | ||
'Script appears to making use of mouse commands without supported binary. Mouse commands will appear as keystrokes.', | ||
); | ||
} | ||
const output = generator.generate(keystrokes, appName, mergedOptions.delay, mergedOptions.initialDelay); | ||
const result = execFileSync('osascript', ['-l', 'JavaScript', '-e', output], { | ||
@@ -33,2 +97,35 @@ encoding: 'utf8', | ||
function hasMouseCommands(keystrokes) { | ||
return mouseExpressions.map(exp => new RegExp(exp, 'g')).some(exp => exp.test(keystrokes)); | ||
} | ||
function sendMouseMove(x1, y1, x2, y2, duration) { | ||
return sendMouseCommands({ x1, y1, x2, y2, duration }); | ||
} | ||
function sendMouseClick(button, count) { | ||
return sendMouseCommands({ click: button, clicks: count }); | ||
} | ||
function sendMouseDrag(x1, y1, x2, y2, duration, button) { | ||
return sendMouseCommands({ x1, y1, x2, y2, duration, drag: button || 'left' }); | ||
} | ||
function sendMouseCommands(options) { | ||
const args = []; | ||
const fixedOptions = { | ||
...options, | ||
duration: options.duration ? Number(options.duration) * 1000 : undefined, | ||
}; | ||
Object.keys(fixedOptions) | ||
.filter(key => fixedOptions[key] != null) | ||
.forEach(key => args.push(`-${key}`, `${fixedOptions[key]}`)); | ||
return execFileSync(path.resolve(__dirname, '../build/mouse'), args, { | ||
encoding: 'utf8', | ||
stdio: 'pipe', | ||
}); | ||
} | ||
module.exports = sendKeys; |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Install scripts
Supply chain riskInstall scripts are run when the package is installed. The majority of malware in npm is hidden in install scripts.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
33109
11
277
113
1
3