mirror of
https://github.com/electron/electron.git
synced 2026-01-07 22:54:25 -05:00
test: drag region tests
This commit is contained in:
@@ -12,6 +12,7 @@
|
|||||||
"@electron/github-app-auth": "^3.2.0",
|
"@electron/github-app-auth": "^3.2.0",
|
||||||
"@electron/lint-roller": "^3.1.2",
|
"@electron/lint-roller": "^3.1.2",
|
||||||
"@electron/typescript-definitions": "^9.1.5",
|
"@electron/typescript-definitions": "^9.1.5",
|
||||||
|
"@hurdlegroup/robotjs": "^0.12.3",
|
||||||
"@octokit/rest": "^20.1.2",
|
"@octokit/rest": "^20.1.2",
|
||||||
"@primer/octicons": "^10.0.0",
|
"@primer/octicons": "^10.0.0",
|
||||||
"@types/minimist": "^1.2.5",
|
"@types/minimist": "^1.2.5",
|
||||||
|
|||||||
@@ -6979,119 +6979,4 @@ describe('BrowserWindow module', () => {
|
|||||||
await screenCapture.expectColorAtCenterMatches(HexColors.BLUE);
|
await screenCapture.expectColorAtCenterMatches(HexColors.BLUE);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('draggable regions', () => {
|
|
||||||
afterEach(closeAllWindows);
|
|
||||||
|
|
||||||
ifit(hasCapturableScreen())('should allow the window to be dragged when enabled', async () => {
|
|
||||||
// FIXME: nut-js has been removed from npm; we need to find a replacement
|
|
||||||
// WOA fails to load libnut so we're using require to defer loading only
|
|
||||||
// on supported platforms.
|
|
||||||
// "@nut-tree\libnut-win32\build\Release\libnut.node is not a valid Win32 application."
|
|
||||||
// @ts-ignore: nut-js is an optional dependency so it may not be installed
|
|
||||||
const { mouse, straightTo, centerOf, Region, Button } = require('@nut-tree/nut-js') as typeof import('@nut-tree/nut-js');
|
|
||||||
|
|
||||||
const display = screen.getPrimaryDisplay();
|
|
||||||
|
|
||||||
const w = new BrowserWindow({
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: display.bounds.width / 2,
|
|
||||||
height: display.bounds.height / 2,
|
|
||||||
frame: false,
|
|
||||||
titleBarStyle: 'hidden'
|
|
||||||
});
|
|
||||||
|
|
||||||
const overlayHTML = path.join(__dirname, 'fixtures', 'pages', 'overlay.html');
|
|
||||||
w.loadFile(overlayHTML);
|
|
||||||
await once(w, 'ready-to-show');
|
|
||||||
|
|
||||||
const winBounds = w.getBounds();
|
|
||||||
const titleBarHeight = 30;
|
|
||||||
const titleBarRegion = new Region(winBounds.x, winBounds.y, winBounds.width, titleBarHeight);
|
|
||||||
const screenRegion = new Region(display.bounds.x, display.bounds.y, display.bounds.width, display.bounds.height);
|
|
||||||
|
|
||||||
const startPos = w.getPosition();
|
|
||||||
|
|
||||||
await mouse.setPosition(await centerOf(titleBarRegion));
|
|
||||||
await mouse.pressButton(Button.LEFT);
|
|
||||||
await mouse.drag(straightTo(centerOf(screenRegion)));
|
|
||||||
|
|
||||||
// Wait for move to complete
|
|
||||||
await Promise.race([
|
|
||||||
once(w, 'move'),
|
|
||||||
setTimeout(100) // fallback for possible race condition
|
|
||||||
]);
|
|
||||||
|
|
||||||
const endPos = w.getPosition();
|
|
||||||
|
|
||||||
expect(startPos).to.not.deep.equal(endPos);
|
|
||||||
});
|
|
||||||
|
|
||||||
ifit(hasCapturableScreen())('should allow the window to be dragged when no WCO and --webkit-app-region: drag enabled', async () => {
|
|
||||||
// FIXME: nut-js has been removed from npm; we need to find a replacement
|
|
||||||
// @ts-ignore: nut-js is an optional dependency so it may not be installed
|
|
||||||
const { mouse, straightTo, centerOf, Region, Button } = require('@nut-tree/nut-js') as typeof import('@nut-tree/nut-js');
|
|
||||||
|
|
||||||
const display = screen.getPrimaryDisplay();
|
|
||||||
const w = new BrowserWindow({
|
|
||||||
x: 0,
|
|
||||||
y: 0,
|
|
||||||
width: display.bounds.width / 2,
|
|
||||||
height: display.bounds.height / 2,
|
|
||||||
frame: false
|
|
||||||
});
|
|
||||||
|
|
||||||
const basePageHTML = path.join(__dirname, 'fixtures', 'pages', 'base-page.html');
|
|
||||||
w.loadFile(basePageHTML);
|
|
||||||
await once(w, 'ready-to-show');
|
|
||||||
|
|
||||||
await w.webContents.executeJavaScript(`
|
|
||||||
const style = document.createElement('style');
|
|
||||||
style.innerHTML = \`
|
|
||||||
#titlebar {
|
|
||||||
|
|
||||||
background-color: red;
|
|
||||||
height: 30px;
|
|
||||||
width: 100%;
|
|
||||||
-webkit-user-select: none;
|
|
||||||
-webkit-app-region: drag;
|
|
||||||
position: fixed;
|
|
||||||
top: 0;
|
|
||||||
left: 0;
|
|
||||||
z-index: 1000000000000;
|
|
||||||
}
|
|
||||||
\`;
|
|
||||||
|
|
||||||
const titleBar = document.createElement('title-bar');
|
|
||||||
titleBar.id = 'titlebar';
|
|
||||||
titleBar.textContent = 'test-titlebar';
|
|
||||||
|
|
||||||
document.body.append(style);
|
|
||||||
document.body.append(titleBar);
|
|
||||||
`);
|
|
||||||
// allow time for titlebar to finish loading
|
|
||||||
await setTimeout(2000);
|
|
||||||
|
|
||||||
const winBounds = w.getBounds();
|
|
||||||
const titleBarHeight = 30;
|
|
||||||
const titleBarRegion = new Region(winBounds.x, winBounds.y, winBounds.width, titleBarHeight);
|
|
||||||
const screenRegion = new Region(display.bounds.x, display.bounds.y, display.bounds.width, display.bounds.height);
|
|
||||||
|
|
||||||
const startPos = w.getPosition();
|
|
||||||
await mouse.setPosition(await centerOf(titleBarRegion));
|
|
||||||
await mouse.pressButton(Button.LEFT);
|
|
||||||
await mouse.drag(straightTo(centerOf(screenRegion)));
|
|
||||||
|
|
||||||
// Wait for move to complete
|
|
||||||
await Promise.race([
|
|
||||||
once(w, 'move'),
|
|
||||||
setTimeout(1000) // fallback for possible race condition
|
|
||||||
]);
|
|
||||||
|
|
||||||
const endPos = w.getPosition();
|
|
||||||
|
|
||||||
expect(startPos).to.not.deep.equal(endPos);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|||||||
267
spec/drag-region-spec.ts
Normal file
267
spec/drag-region-spec.ts
Normal file
@@ -0,0 +1,267 @@
|
|||||||
|
import { BrowserWindow, screen } from 'electron/main';
|
||||||
|
|
||||||
|
import { expect } from 'chai';
|
||||||
|
|
||||||
|
import { once } from 'node:events';
|
||||||
|
import * as path from 'node:path';
|
||||||
|
import { setTimeout } from 'node:timers/promises';
|
||||||
|
import { pathToFileURL } from 'node:url';
|
||||||
|
|
||||||
|
import { hasCapturableScreen } from './lib/screen-helpers';
|
||||||
|
import { closeAllWindows } from './lib/window-helpers';
|
||||||
|
|
||||||
|
const display = screen.getPrimaryDisplay();
|
||||||
|
|
||||||
|
const fixtures = path.resolve(__dirname, 'fixtures');
|
||||||
|
|
||||||
|
// Try to load robotjs
|
||||||
|
let robot: typeof import('@hurdlegroup/robotjs');
|
||||||
|
try {
|
||||||
|
robot = require('@hurdlegroup/robotjs');
|
||||||
|
} catch {
|
||||||
|
// ignore. tests are skipped below if this is undefined.
|
||||||
|
}
|
||||||
|
|
||||||
|
const draggablePageURL = pathToFileURL(
|
||||||
|
path.join(fixtures, 'pages', 'draggable-page.html')
|
||||||
|
);
|
||||||
|
const iframePageURL = pathToFileURL(
|
||||||
|
path.join(fixtures, 'pages', 'iframe.html')
|
||||||
|
);
|
||||||
|
const webviewPageURL = pathToFileURL(
|
||||||
|
path.join(fixtures, 'pages', 'webview.html')
|
||||||
|
);
|
||||||
|
|
||||||
|
const testWindowOpts: Electron.BrowserWindowConstructorOptions = {
|
||||||
|
x: 0,
|
||||||
|
y: 0,
|
||||||
|
width: Math.round(display.bounds.width / 2),
|
||||||
|
height: Math.round(display.bounds.height / 2),
|
||||||
|
frame: false
|
||||||
|
};
|
||||||
|
|
||||||
|
const center = (rect: Electron.Rectangle): Electron.Point => ({
|
||||||
|
x: Math.round(rect.x + rect.width / 2),
|
||||||
|
y: Math.round(rect.y + rect.height / 2)
|
||||||
|
});
|
||||||
|
|
||||||
|
const performDrag = async (
|
||||||
|
w: BrowserWindow
|
||||||
|
): Promise<{
|
||||||
|
start: [number, number];
|
||||||
|
end: [number, number];
|
||||||
|
}> => {
|
||||||
|
const winBounds = w.getBounds();
|
||||||
|
const winCenter = center(winBounds);
|
||||||
|
const screenCenter = center(display.bounds);
|
||||||
|
|
||||||
|
const start = w.getPosition() as [number, number];
|
||||||
|
const moved = once(w, 'move');
|
||||||
|
|
||||||
|
// Extra events based on research from https://github.com/octalmage/robotjs/issues/389
|
||||||
|
robot.moveMouse(winCenter.x, winCenter.y);
|
||||||
|
robot.mouseToggle('down', 'left');
|
||||||
|
robot.moveMouse(winCenter.x + 2, winCenter.y + 2); // extra
|
||||||
|
await setTimeout(200); // extra
|
||||||
|
robot.dragMouse(screenCenter.x, screenCenter.y);
|
||||||
|
robot.mouseToggle('up', 'left');
|
||||||
|
|
||||||
|
await Promise.race([moved, setTimeout(1000)]);
|
||||||
|
|
||||||
|
const end = w.getPosition() as [number, number];
|
||||||
|
return { start, end };
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadDraggableSubframe = async (w: BrowserWindow): Promise<void> => {
|
||||||
|
let selector: string;
|
||||||
|
let eventName: string;
|
||||||
|
if (w.getURL() === iframePageURL.href) {
|
||||||
|
selector = 'iframe';
|
||||||
|
eventName = 'load';
|
||||||
|
} else if (w.getURL() === webviewPageURL.href) {
|
||||||
|
selector = 'webview';
|
||||||
|
eventName = 'did-finish-load';
|
||||||
|
} else {
|
||||||
|
throw new Error('Unexpected page loaded');
|
||||||
|
}
|
||||||
|
|
||||||
|
await w.webContents.executeJavaScript(`
|
||||||
|
new Promise((resolve) => {
|
||||||
|
const frame = document.querySelector('${selector}');
|
||||||
|
frame.addEventListener(
|
||||||
|
'${eventName}',
|
||||||
|
() => resolve(),
|
||||||
|
{ once: true }
|
||||||
|
);
|
||||||
|
frame.src = '${draggablePageURL.href}';
|
||||||
|
})
|
||||||
|
`);
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('draggable regions', function () {
|
||||||
|
before(async function () {
|
||||||
|
if (!robot || !robot.moveMouse || !hasCapturableScreen()) {
|
||||||
|
this.skip();
|
||||||
|
}
|
||||||
|
|
||||||
|
// The first window may not properly receive events due to UI transitions or
|
||||||
|
// focus management. To mitigate this, warm up with a test run.
|
||||||
|
const w = new BrowserWindow(testWindowOpts);
|
||||||
|
await w.loadURL(draggablePageURL.href);
|
||||||
|
await performDrag(w);
|
||||||
|
w.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(closeAllWindows);
|
||||||
|
|
||||||
|
describe('main window', () => {
|
||||||
|
let w: BrowserWindow;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
w = new BrowserWindow(testWindowOpts);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('drags with app-region: drag', async () => {
|
||||||
|
await w.loadURL(draggablePageURL.href);
|
||||||
|
|
||||||
|
const { start, end } = await performDrag(w);
|
||||||
|
|
||||||
|
expect(start).to.not.deep.equal(end);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not drag when app-region: no-drag overlaps drag region', async () => {
|
||||||
|
const noDragURL = new URL(draggablePageURL.href);
|
||||||
|
noDragURL.searchParams.set('no-drag', '1');
|
||||||
|
await w.loadURL(noDragURL.href);
|
||||||
|
|
||||||
|
const { start, end } = await performDrag(w);
|
||||||
|
|
||||||
|
expect(start).to.deep.equal(end);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('drags after navigation', async () => {
|
||||||
|
await w.loadFile(path.join(fixtures, 'pages', 'base-page.html'));
|
||||||
|
await w.loadURL(draggablePageURL.href);
|
||||||
|
|
||||||
|
const { start, end } = await performDrag(w);
|
||||||
|
|
||||||
|
expect(start).to.not.deep.equal(end);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('drags after in-page navigation', async () => {
|
||||||
|
await w.loadURL(draggablePageURL.href);
|
||||||
|
|
||||||
|
const didNavigate = once(w.webContents, 'did-navigate-in-page');
|
||||||
|
await w.webContents.executeJavaScript(`
|
||||||
|
window.history.pushState({}, '', '/new-path');
|
||||||
|
`);
|
||||||
|
await didNavigate;
|
||||||
|
|
||||||
|
const { start, end } = await performDrag(w);
|
||||||
|
|
||||||
|
expect(start).to.not.deep.equal(end);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('child windows (window.open)', () => {
|
||||||
|
let childWindow: BrowserWindow;
|
||||||
|
|
||||||
|
beforeEach(async () => {
|
||||||
|
const parentWindow = new BrowserWindow({ show: false });
|
||||||
|
await parentWindow.loadFile(
|
||||||
|
path.join(fixtures, 'pages', 'base-page.html')
|
||||||
|
);
|
||||||
|
|
||||||
|
parentWindow.webContents.setWindowOpenHandler(() => ({
|
||||||
|
action: 'allow',
|
||||||
|
overrideBrowserWindowOptions: testWindowOpts
|
||||||
|
}));
|
||||||
|
|
||||||
|
const newBrowserWindow = once(parentWindow.webContents, 'did-create-window');
|
||||||
|
|
||||||
|
await parentWindow.webContents.executeJavaScript(
|
||||||
|
`void window.open('${draggablePageURL.href}', '_blank');`
|
||||||
|
);
|
||||||
|
|
||||||
|
[childWindow] = await newBrowserWindow;
|
||||||
|
await once(childWindow, 'ready-to-show');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('drags with app-region: drag', async () => {
|
||||||
|
const { start, end } = await performDrag(childWindow);
|
||||||
|
|
||||||
|
expect(start).to.not.deep.equal(end);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('drags after navigation', async () => {
|
||||||
|
await childWindow.loadURL(draggablePageURL.href);
|
||||||
|
|
||||||
|
const { start, end } = await performDrag(childWindow);
|
||||||
|
|
||||||
|
expect(start).to.not.deep.equal(end);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const frameType of ['webview', 'iframe'] as const) {
|
||||||
|
// FIXME: this behavior is broken before the tests were added
|
||||||
|
// See: https://github.com/electron/electron/issues/49256
|
||||||
|
describe.skip(`child frames (${frameType})`, () => {
|
||||||
|
const subframePageURL = frameType === 'webview' ? webviewPageURL : iframePageURL;
|
||||||
|
let w: BrowserWindow;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
w = new BrowserWindow({
|
||||||
|
...testWindowOpts,
|
||||||
|
webPreferences: frameType === 'webview'
|
||||||
|
? {
|
||||||
|
webviewTag: true
|
||||||
|
}
|
||||||
|
: {}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('drags in subframe with app-region: drag', async () => {
|
||||||
|
await w.loadURL(subframePageURL.href);
|
||||||
|
await loadDraggableSubframe(w);
|
||||||
|
|
||||||
|
const { start, end } = await performDrag(w);
|
||||||
|
|
||||||
|
expect(start).to.not.deep.equal(end);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('drags after subframe navigation', async () => {
|
||||||
|
await w.loadURL(subframePageURL.href);
|
||||||
|
await loadDraggableSubframe(w);
|
||||||
|
await loadDraggableSubframe(w);
|
||||||
|
|
||||||
|
const { start, end } = await performDrag(w);
|
||||||
|
|
||||||
|
expect(start).to.not.deep.equal(end);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not drag after host page navigation without draggable region', async () => {
|
||||||
|
await w.loadURL(subframePageURL.href);
|
||||||
|
await loadDraggableSubframe(w);
|
||||||
|
|
||||||
|
await w.loadFile(
|
||||||
|
path.join(fixtures, 'pages', 'base-page.html')
|
||||||
|
);
|
||||||
|
|
||||||
|
const { start, end } = await performDrag(w);
|
||||||
|
|
||||||
|
expect(start).to.deep.equal(end);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('drags after host page navigation', async () => {
|
||||||
|
await w.loadURL(subframePageURL.href);
|
||||||
|
await loadDraggableSubframe(w);
|
||||||
|
await w.loadURL(subframePageURL.href);
|
||||||
|
await loadDraggableSubframe(w);
|
||||||
|
|
||||||
|
const { start, end } = await performDrag(w);
|
||||||
|
|
||||||
|
expect(start).to.not.deep.equal(end);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
52
spec/fixtures/pages/draggable-page.html
vendored
52
spec/fixtures/pages/draggable-page.html
vendored
@@ -1,22 +1,36 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>Draggable Page</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
app-region: drag;
|
||||||
|
|
||||||
<head>
|
display: flex;
|
||||||
<meta charset="UTF-8">
|
justify-content: center;
|
||||||
<title>Draggable Page</title>
|
align-items: center;
|
||||||
<style>
|
}
|
||||||
.draggable {
|
#no-drag {
|
||||||
-webkit-app-region: drag;
|
width: 100px;
|
||||||
height: 100vh;
|
height: 100px;
|
||||||
width: 100vw;
|
background-color: orangered;
|
||||||
margin: 0;
|
app-region: no-drag;
|
||||||
background-color: #f0f0f0;
|
}
|
||||||
}
|
</style>
|
||||||
</style>
|
</head>
|
||||||
</head>
|
<body>
|
||||||
|
<script>
|
||||||
<body>
|
if (window.location.search.includes('no-drag')) {
|
||||||
<div class="draggable"></div>
|
const noDragDiv = document.createElement('div');
|
||||||
</body>
|
noDragDiv.id = 'no-drag';
|
||||||
|
document.body.appendChild(noDragDiv);
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|||||||
27
spec/fixtures/pages/iframe.html
vendored
Normal file
27
spec/fixtures/pages/iframe.html
vendored
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>iframe Host</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
iframe {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border: none;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<iframe></iframe>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
26
spec/fixtures/pages/webview.html
vendored
Normal file
26
spec/fixtures/pages/webview.html
vendored
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>webview Host</title>
|
||||||
|
<style>
|
||||||
|
html,
|
||||||
|
body {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
webview {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<webview></webview>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
32
yarn.lock
32
yarn.lock
@@ -594,6 +594,7 @@ __metadata:
|
|||||||
"@electron/github-app-auth": "npm:^3.2.0"
|
"@electron/github-app-auth": "npm:^3.2.0"
|
||||||
"@electron/lint-roller": "npm:^3.1.2"
|
"@electron/lint-roller": "npm:^3.1.2"
|
||||||
"@electron/typescript-definitions": "npm:^9.1.5"
|
"@electron/typescript-definitions": "npm:^9.1.5"
|
||||||
|
"@hurdlegroup/robotjs": "npm:^0.12.3"
|
||||||
"@octokit/rest": "npm:^20.1.2"
|
"@octokit/rest": "npm:^20.1.2"
|
||||||
"@primer/octicons": "npm:^10.0.0"
|
"@primer/octicons": "npm:^10.0.0"
|
||||||
"@types/minimist": "npm:^1.2.5"
|
"@types/minimist": "npm:^1.2.5"
|
||||||
@@ -1090,6 +1091,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"@hurdlegroup/robotjs@npm:^0.12.3":
|
||||||
|
version: 0.12.3
|
||||||
|
resolution: "@hurdlegroup/robotjs@npm:0.12.3"
|
||||||
|
dependencies:
|
||||||
|
node-addon-api: "npm:*"
|
||||||
|
node-gyp: "npm:latest"
|
||||||
|
node-gyp-build: "npm:^4.8.1"
|
||||||
|
checksum: 10c0/6d5310320187bacfccaa7f636315b8d5d5a4ecdc586b7d3467b1def22ee31428d6b39f06dadb240e72d0ac4548068be1f896f2501dd7115d0100820a0640c562
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"@inquirer/external-editor@npm:^1.0.0":
|
"@inquirer/external-editor@npm:^1.0.0":
|
||||||
version: 1.0.2
|
version: 1.0.2
|
||||||
resolution: "@inquirer/external-editor@npm:1.0.2"
|
resolution: "@inquirer/external-editor@npm:1.0.2"
|
||||||
@@ -10019,6 +10031,15 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"node-addon-api@npm:*":
|
||||||
|
version: 8.5.0
|
||||||
|
resolution: "node-addon-api@npm:8.5.0"
|
||||||
|
dependencies:
|
||||||
|
node-gyp: "npm:latest"
|
||||||
|
checksum: 10c0/e4de0b4e70998fed7ef41933946f60565fc3a17cb83b7d626a0c0bb1f734cf7852e0e596f12681e7c8ed424163ee3cdbb4f0abaa9cc269d03f48834c263ba162
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"node-addon-api@npm:8.0.0":
|
"node-addon-api@npm:8.0.0":
|
||||||
version: 8.0.0
|
version: 8.0.0
|
||||||
resolution: "node-addon-api@npm:8.0.0"
|
resolution: "node-addon-api@npm:8.0.0"
|
||||||
@@ -10056,6 +10077,17 @@ __metadata:
|
|||||||
languageName: node
|
languageName: node
|
||||||
linkType: hard
|
linkType: hard
|
||||||
|
|
||||||
|
"node-gyp-build@npm:^4.8.1":
|
||||||
|
version: 4.8.4
|
||||||
|
resolution: "node-gyp-build@npm:4.8.4"
|
||||||
|
bin:
|
||||||
|
node-gyp-build: bin.js
|
||||||
|
node-gyp-build-optional: optional.js
|
||||||
|
node-gyp-build-test: build-test.js
|
||||||
|
checksum: 10c0/444e189907ece2081fe60e75368784f7782cfddb554b60123743dfb89509df89f1f29c03bbfa16b3a3e0be3f48799a4783f487da6203245fa5bed239ba7407e1
|
||||||
|
languageName: node
|
||||||
|
linkType: hard
|
||||||
|
|
||||||
"node-gyp@npm:^11.4.2, node-gyp@npm:latest":
|
"node-gyp@npm:^11.4.2, node-gyp@npm:latest":
|
||||||
version: 11.5.0
|
version: 11.5.0
|
||||||
resolution: "node-gyp@npm:11.5.0"
|
resolution: "node-gyp@npm:11.5.0"
|
||||||
|
|||||||
Reference in New Issue
Block a user