@midscene/harmony 1.8.0 → 1.8.1
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/dist/es/bin.mjs +72 -103
- package/dist/es/cli.mjs +73 -104
- package/dist/es/index.mjs +72 -103
- package/dist/es/mcp-server.mjs +73 -104
- package/dist/lib/bin.js +70 -101
- package/dist/lib/cli.js +71 -102
- package/dist/lib/index.js +70 -101
- package/dist/lib/mcp-server.js +71 -102
- package/dist/types/index.d.ts +8 -6
- package/dist/types/mcp-server.d.ts +8 -6
- package/package.json +4 -4
- package/static/index.html +1 -1
- package/static/static/css/{index.63b028da.css → index.26c9c911.css} +2 -2
- package/static/static/css/{index.63b028da.css.map → index.26c9c911.css.map} +1 -1
- package/static/static/js/{889.c8e2e995.js → 596.5426be9e.js} +27 -27
- package/static/static/js/596.5426be9e.js.map +1 -0
- package/static/static/js/index.acaa5ec1.js +914 -0
- package/static/static/js/index.acaa5ec1.js.map +1 -0
- package/static/static/js/889.c8e2e995.js.map +0 -1
- package/static/static/js/index.7d3d953d.js +0 -914
- package/static/static/js/index.7d3d953d.js.map +0 -1
- /package/static/static/js/{889.c8e2e995.js.LICENSE.txt → 596.5426be9e.js.LICENSE.txt} +0 -0
- /package/static/static/js/{index.7d3d953d.js.LICENSE.txt → index.acaa5ec1.js.LICENSE.txt} +0 -0
package/dist/lib/bin.js
CHANGED
|
@@ -279,15 +279,6 @@ function device_define_property(obj, key, value) {
|
|
|
279
279
|
else obj[key] = value;
|
|
280
280
|
return obj;
|
|
281
281
|
}
|
|
282
|
-
const harmonyInputParamSchema = core_namespaceObject.z.object({
|
|
283
|
-
value: core_namespaceObject.z.string().describe('The text to input. Provide the final content for replace/append modes, or an empty string when using clear mode to remove existing text.'),
|
|
284
|
-
mode: core_namespaceObject.z.preprocess((val)=>'append' === val ? 'typeOnly' : val, core_namespaceObject.z["enum"]([
|
|
285
|
-
'replace',
|
|
286
|
-
'clear',
|
|
287
|
-
'typeOnly'
|
|
288
|
-
]).default('replace').optional().describe('Input mode: "replace" (default) - clear the field and input the value; "typeOnly" - type the value directly without clearing the field first; "clear" - attempt to clear the field (limited support on HarmonyOS).')),
|
|
289
|
-
locate: (0, core_namespaceObject.getMidsceneLocationSchema)().describe('The input field to be filled').optional()
|
|
290
|
-
});
|
|
291
282
|
const defaultScrollUntilTimes = 10;
|
|
292
283
|
const defaultFastSwipeSpeed = 2000;
|
|
293
284
|
const maxScrollDistance = 9999999;
|
|
@@ -328,89 +319,15 @@ const keyNameAliasMap = {
|
|
|
328
319
|
};
|
|
329
320
|
class device_HarmonyDevice {
|
|
330
321
|
actionSpace() {
|
|
322
|
+
const mobileActionContext = {
|
|
323
|
+
input: this.inputPrimitives,
|
|
324
|
+
size: ()=>this.size(),
|
|
325
|
+
sleep: async (timeMs)=>{
|
|
326
|
+
await (0, core_utils_namespaceObject.sleep)(timeMs);
|
|
327
|
+
}
|
|
328
|
+
};
|
|
331
329
|
const defaultActions = [
|
|
332
|
-
(0, device_namespaceObject.
|
|
333
|
-
const element = param.locate;
|
|
334
|
-
external_node_assert_default()(element, 'Element not found, cannot tap');
|
|
335
|
-
await this.tap(element.center[0], element.center[1]);
|
|
336
|
-
}),
|
|
337
|
-
(0, device_namespaceObject.defineActionDoubleClick)(async (param)=>{
|
|
338
|
-
const element = param.locate;
|
|
339
|
-
external_node_assert_default()(element, 'Element not found, cannot double click');
|
|
340
|
-
await this.doubleTap(element.center[0], element.center[1]);
|
|
341
|
-
}),
|
|
342
|
-
(0, device_namespaceObject.defineAction)({
|
|
343
|
-
name: 'Input',
|
|
344
|
-
description: 'Input text into the input field',
|
|
345
|
-
interfaceAlias: 'aiInput',
|
|
346
|
-
paramSchema: harmonyInputParamSchema,
|
|
347
|
-
sample: {
|
|
348
|
-
value: 'test@example.com',
|
|
349
|
-
locate: {
|
|
350
|
-
prompt: 'the email input field'
|
|
351
|
-
}
|
|
352
|
-
},
|
|
353
|
-
call: async (param)=>{
|
|
354
|
-
const element = param.locate;
|
|
355
|
-
if ('clear' === param.mode) return void await this.clearInput(element);
|
|
356
|
-
if (!param || !param.value) return;
|
|
357
|
-
const shouldReplace = 'typeOnly' !== param.mode;
|
|
358
|
-
await this.inputText(param.value, element, shouldReplace);
|
|
359
|
-
}
|
|
360
|
-
}),
|
|
361
|
-
(0, device_namespaceObject.defineActionScroll)(async (param)=>{
|
|
362
|
-
const element = param.locate;
|
|
363
|
-
const startingPoint = element ? {
|
|
364
|
-
left: element.center[0],
|
|
365
|
-
top: element.center[1]
|
|
366
|
-
} : void 0;
|
|
367
|
-
const scrollToEventName = param?.scrollType;
|
|
368
|
-
if ('scrollToTop' === scrollToEventName) await this.scrollUntilTop(startingPoint);
|
|
369
|
-
else if ('scrollToBottom' === scrollToEventName) await this.scrollUntilBottom(startingPoint);
|
|
370
|
-
else if ('scrollToRight' === scrollToEventName) await this.scrollUntilRight(startingPoint);
|
|
371
|
-
else if ('scrollToLeft' === scrollToEventName) await this.scrollUntilLeft(startingPoint);
|
|
372
|
-
else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
|
|
373
|
-
else {
|
|
374
|
-
if (param?.direction !== 'down' && param && param.direction) if ('up' === param.direction) await this.scrollUp(param.distance ?? void 0, startingPoint);
|
|
375
|
-
else if ('left' === param.direction) await this.scrollLeft(param.distance ?? void 0, startingPoint);
|
|
376
|
-
else if ('right' === param.direction) await this.scrollRight(param.distance ?? void 0, startingPoint);
|
|
377
|
-
else throw new Error(`Unknown scroll direction: ${param.direction}`);
|
|
378
|
-
else await this.scrollDown(param?.distance ?? void 0, startingPoint);
|
|
379
|
-
await (0, core_utils_namespaceObject.sleep)(500);
|
|
380
|
-
}
|
|
381
|
-
}),
|
|
382
|
-
(0, device_namespaceObject.defineActionDragAndDrop)(async (param)=>{
|
|
383
|
-
const from = param.from;
|
|
384
|
-
const to = param.to;
|
|
385
|
-
external_node_assert_default()(from, 'missing "from" param for drag and drop');
|
|
386
|
-
external_node_assert_default()(to, 'missing "to" param for drag and drop');
|
|
387
|
-
const hdc = await this.getHdc();
|
|
388
|
-
await hdc.drag(from.center[0], from.center[1], to.center[0], to.center[1]);
|
|
389
|
-
}),
|
|
390
|
-
(0, device_namespaceObject.defineActionSwipe)(async (param)=>{
|
|
391
|
-
const { startPoint, endPoint, duration, repeatCount } = (0, device_namespaceObject.normalizeMobileSwipeParam)(param, await this.size());
|
|
392
|
-
const hdc = await this.getHdc();
|
|
393
|
-
for(let i = 0; i < repeatCount; i++)await hdc.swipe(startPoint.x, startPoint.y, endPoint.x, endPoint.y, duration ? Math.round(duration) : void 0);
|
|
394
|
-
}),
|
|
395
|
-
(0, device_namespaceObject.defineActionKeyboardPress)(async (param)=>{
|
|
396
|
-
await this.keyboardPress(param.keyName);
|
|
397
|
-
}),
|
|
398
|
-
(0, device_namespaceObject.defineActionCursorMove)(async (param)=>{
|
|
399
|
-
const arrowKey = 'left' === param.direction ? 'ArrowLeft' : 'ArrowRight';
|
|
400
|
-
const times = param.times ?? 1;
|
|
401
|
-
for(let i = 0; i < times; i++){
|
|
402
|
-
await this.keyboardPress(arrowKey);
|
|
403
|
-
await (0, core_utils_namespaceObject.sleep)(100);
|
|
404
|
-
}
|
|
405
|
-
}),
|
|
406
|
-
(0, device_namespaceObject.defineActionLongPress)(async (param)=>{
|
|
407
|
-
const element = param.locate;
|
|
408
|
-
if (!element) throw new Error('LongPress requires an element to be located');
|
|
409
|
-
await this.longPress(element.center[0], element.center[1]);
|
|
410
|
-
}),
|
|
411
|
-
(0, device_namespaceObject.defineActionClearInput)(async (param)=>{
|
|
412
|
-
await this.clearInput(param.locate);
|
|
413
|
-
})
|
|
330
|
+
...(0, device_namespaceObject.createDefaultMobileActions)(mobileActionContext)
|
|
414
331
|
];
|
|
415
332
|
const platformSpecificActions = Object.values(createPlatformActions(this));
|
|
416
333
|
const customActions = this.customActions ?? [];
|
|
@@ -420,6 +337,27 @@ class device_HarmonyDevice {
|
|
|
420
337
|
...customActions
|
|
421
338
|
];
|
|
422
339
|
}
|
|
340
|
+
async performActionScroll(param) {
|
|
341
|
+
const element = param.locate;
|
|
342
|
+
const startingPoint = element ? {
|
|
343
|
+
left: element.center[0],
|
|
344
|
+
top: element.center[1]
|
|
345
|
+
} : void 0;
|
|
346
|
+
const scrollToEventName = param?.scrollType;
|
|
347
|
+
if ('scrollToTop' === scrollToEventName) await this.scrollUntilTop(startingPoint);
|
|
348
|
+
else if ('scrollToBottom' === scrollToEventName) await this.scrollUntilBottom(startingPoint);
|
|
349
|
+
else if ('scrollToRight' === scrollToEventName) await this.scrollUntilRight(startingPoint);
|
|
350
|
+
else if ('scrollToLeft' === scrollToEventName) await this.scrollUntilLeft(startingPoint);
|
|
351
|
+
else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
|
|
352
|
+
else {
|
|
353
|
+
if (param?.direction !== 'down' && param && param.direction) if ('up' === param.direction) await this.scrollUp(param.distance ?? void 0, startingPoint);
|
|
354
|
+
else if ('left' === param.direction) await this.scrollLeft(param.distance ?? void 0, startingPoint);
|
|
355
|
+
else if ('right' === param.direction) await this.scrollRight(param.distance ?? void 0, startingPoint);
|
|
356
|
+
else throw new Error(`Unknown scroll direction: ${param.direction}`);
|
|
357
|
+
else await this.scrollDown(param?.distance ?? void 0, startingPoint);
|
|
358
|
+
await (0, core_utils_namespaceObject.sleep)(500);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
423
361
|
describe() {
|
|
424
362
|
return this.descriptionText || `DeviceId: ${this.deviceId}`;
|
|
425
363
|
}
|
|
@@ -550,23 +488,23 @@ class device_HarmonyDevice {
|
|
|
550
488
|
}
|
|
551
489
|
throw new Error('Screenshot buffer is empty after retries');
|
|
552
490
|
}
|
|
553
|
-
async
|
|
491
|
+
async tapPoint(point) {
|
|
554
492
|
this.lastTapPosition = {
|
|
555
|
-
x,
|
|
556
|
-
y
|
|
493
|
+
x: point.x,
|
|
494
|
+
y: point.y
|
|
557
495
|
};
|
|
558
496
|
const hdc = await this.getHdc();
|
|
559
|
-
await hdc.click(x, y);
|
|
497
|
+
await hdc.click(point.x, point.y);
|
|
560
498
|
}
|
|
561
|
-
async
|
|
499
|
+
async doubleTapPoint(point) {
|
|
562
500
|
const hdc = await this.getHdc();
|
|
563
|
-
await hdc.doubleClick(x, y);
|
|
501
|
+
await hdc.doubleClick(point.x, point.y);
|
|
564
502
|
}
|
|
565
|
-
async
|
|
503
|
+
async longPressPoint(point) {
|
|
566
504
|
const hdc = await this.getHdc();
|
|
567
|
-
await hdc.longClick(x, y);
|
|
505
|
+
await hdc.longClick(point.x, point.y);
|
|
568
506
|
}
|
|
569
|
-
async
|
|
507
|
+
async typeText(text, element, shouldReplace) {
|
|
570
508
|
if (!text) return;
|
|
571
509
|
const hdc = await this.getHdc();
|
|
572
510
|
let x;
|
|
@@ -597,7 +535,7 @@ class device_HarmonyDevice {
|
|
|
597
535
|
}
|
|
598
536
|
await hdc.clearTextField(100);
|
|
599
537
|
}
|
|
600
|
-
async
|
|
538
|
+
async pressKey(key) {
|
|
601
539
|
const normalizedKey = keyNameAliasMap[key.toLowerCase()] ?? key;
|
|
602
540
|
const harmonyKey = harmonyKeyCodeMap[normalizedKey] ?? key;
|
|
603
541
|
const hdc = await this.getHdc();
|
|
@@ -797,6 +735,37 @@ class device_HarmonyDevice {
|
|
|
797
735
|
device_define_property(this, "interfaceType", 'harmony');
|
|
798
736
|
device_define_property(this, "uri", void 0);
|
|
799
737
|
device_define_property(this, "options", void 0);
|
|
738
|
+
device_define_property(this, "inputPrimitives", {
|
|
739
|
+
pointer: {
|
|
740
|
+
tap: (point)=>this.tapPoint(point),
|
|
741
|
+
doubleClick: (point)=>this.doubleTapPoint(point),
|
|
742
|
+
longPress: (point)=>this.longPressPoint(point),
|
|
743
|
+
dragAndDrop: async (from, to)=>{
|
|
744
|
+
const hdc = await this.getHdc();
|
|
745
|
+
await hdc.drag(from.x, from.y, to.x, to.y);
|
|
746
|
+
}
|
|
747
|
+
},
|
|
748
|
+
keyboard: {
|
|
749
|
+
keyboardPress: (keyName)=>this.pressKey(keyName),
|
|
750
|
+
typeText: (value, opts)=>opts?.focusOnly ? Promise.resolve() : this.typeText(value, opts?.target, opts?.replace ?? true),
|
|
751
|
+
clearInput: (target)=>this.clearInput(target),
|
|
752
|
+
cursorMove: async (direction, times = 1)=>{
|
|
753
|
+
const arrowKey = 'left' === direction ? 'ArrowLeft' : 'ArrowRight';
|
|
754
|
+
for(let i = 0; i < times; i++)await this.pressKey(arrowKey);
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
touch: {
|
|
758
|
+
swipe: async (start, end, opts)=>{
|
|
759
|
+
const duration = opts?.duration;
|
|
760
|
+
const repeatCount = opts?.repeat ?? 1;
|
|
761
|
+
const hdc = await this.getHdc();
|
|
762
|
+
for(let i = 0; i < repeatCount; i++)await hdc.swipe(start.x, start.y, end.x, end.y, duration ? Math.round(duration) : void 0);
|
|
763
|
+
}
|
|
764
|
+
},
|
|
765
|
+
scroll: {
|
|
766
|
+
scroll: (param)=>this.performActionScroll(param)
|
|
767
|
+
}
|
|
768
|
+
});
|
|
800
769
|
device_define_property(this, "remoteScreenshotPath", '/data/local/tmp/ms_screen.jpeg');
|
|
801
770
|
device_define_property(this, "localScreenshotPath", null);
|
|
802
771
|
external_node_assert_default()(deviceId, 'deviceId is required for HarmonyDevice');
|
package/dist/lib/cli.js
CHANGED
|
@@ -275,15 +275,6 @@ function device_define_property(obj, key, value) {
|
|
|
275
275
|
else obj[key] = value;
|
|
276
276
|
return obj;
|
|
277
277
|
}
|
|
278
|
-
const harmonyInputParamSchema = core_namespaceObject.z.object({
|
|
279
|
-
value: core_namespaceObject.z.string().describe('The text to input. Provide the final content for replace/append modes, or an empty string when using clear mode to remove existing text.'),
|
|
280
|
-
mode: core_namespaceObject.z.preprocess((val)=>'append' === val ? 'typeOnly' : val, core_namespaceObject.z["enum"]([
|
|
281
|
-
'replace',
|
|
282
|
-
'clear',
|
|
283
|
-
'typeOnly'
|
|
284
|
-
]).default('replace').optional().describe('Input mode: "replace" (default) - clear the field and input the value; "typeOnly" - type the value directly without clearing the field first; "clear" - attempt to clear the field (limited support on HarmonyOS).')),
|
|
285
|
-
locate: (0, core_namespaceObject.getMidsceneLocationSchema)().describe('The input field to be filled').optional()
|
|
286
|
-
});
|
|
287
278
|
const defaultScrollUntilTimes = 10;
|
|
288
279
|
const defaultFastSwipeSpeed = 2000;
|
|
289
280
|
const maxScrollDistance = 9999999;
|
|
@@ -324,89 +315,15 @@ const keyNameAliasMap = {
|
|
|
324
315
|
};
|
|
325
316
|
class HarmonyDevice {
|
|
326
317
|
actionSpace() {
|
|
318
|
+
const mobileActionContext = {
|
|
319
|
+
input: this.inputPrimitives,
|
|
320
|
+
size: ()=>this.size(),
|
|
321
|
+
sleep: async (timeMs)=>{
|
|
322
|
+
await (0, core_utils_namespaceObject.sleep)(timeMs);
|
|
323
|
+
}
|
|
324
|
+
};
|
|
327
325
|
const defaultActions = [
|
|
328
|
-
(0, device_namespaceObject.
|
|
329
|
-
const element = param.locate;
|
|
330
|
-
external_node_assert_default()(element, 'Element not found, cannot tap');
|
|
331
|
-
await this.tap(element.center[0], element.center[1]);
|
|
332
|
-
}),
|
|
333
|
-
(0, device_namespaceObject.defineActionDoubleClick)(async (param)=>{
|
|
334
|
-
const element = param.locate;
|
|
335
|
-
external_node_assert_default()(element, 'Element not found, cannot double click');
|
|
336
|
-
await this.doubleTap(element.center[0], element.center[1]);
|
|
337
|
-
}),
|
|
338
|
-
(0, device_namespaceObject.defineAction)({
|
|
339
|
-
name: 'Input',
|
|
340
|
-
description: 'Input text into the input field',
|
|
341
|
-
interfaceAlias: 'aiInput',
|
|
342
|
-
paramSchema: harmonyInputParamSchema,
|
|
343
|
-
sample: {
|
|
344
|
-
value: 'test@example.com',
|
|
345
|
-
locate: {
|
|
346
|
-
prompt: 'the email input field'
|
|
347
|
-
}
|
|
348
|
-
},
|
|
349
|
-
call: async (param)=>{
|
|
350
|
-
const element = param.locate;
|
|
351
|
-
if ('clear' === param.mode) return void await this.clearInput(element);
|
|
352
|
-
if (!param || !param.value) return;
|
|
353
|
-
const shouldReplace = 'typeOnly' !== param.mode;
|
|
354
|
-
await this.inputText(param.value, element, shouldReplace);
|
|
355
|
-
}
|
|
356
|
-
}),
|
|
357
|
-
(0, device_namespaceObject.defineActionScroll)(async (param)=>{
|
|
358
|
-
const element = param.locate;
|
|
359
|
-
const startingPoint = element ? {
|
|
360
|
-
left: element.center[0],
|
|
361
|
-
top: element.center[1]
|
|
362
|
-
} : void 0;
|
|
363
|
-
const scrollToEventName = param?.scrollType;
|
|
364
|
-
if ('scrollToTop' === scrollToEventName) await this.scrollUntilTop(startingPoint);
|
|
365
|
-
else if ('scrollToBottom' === scrollToEventName) await this.scrollUntilBottom(startingPoint);
|
|
366
|
-
else if ('scrollToRight' === scrollToEventName) await this.scrollUntilRight(startingPoint);
|
|
367
|
-
else if ('scrollToLeft' === scrollToEventName) await this.scrollUntilLeft(startingPoint);
|
|
368
|
-
else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
|
|
369
|
-
else {
|
|
370
|
-
if (param?.direction !== 'down' && param && param.direction) if ('up' === param.direction) await this.scrollUp(param.distance ?? void 0, startingPoint);
|
|
371
|
-
else if ('left' === param.direction) await this.scrollLeft(param.distance ?? void 0, startingPoint);
|
|
372
|
-
else if ('right' === param.direction) await this.scrollRight(param.distance ?? void 0, startingPoint);
|
|
373
|
-
else throw new Error(`Unknown scroll direction: ${param.direction}`);
|
|
374
|
-
else await this.scrollDown(param?.distance ?? void 0, startingPoint);
|
|
375
|
-
await (0, core_utils_namespaceObject.sleep)(500);
|
|
376
|
-
}
|
|
377
|
-
}),
|
|
378
|
-
(0, device_namespaceObject.defineActionDragAndDrop)(async (param)=>{
|
|
379
|
-
const from = param.from;
|
|
380
|
-
const to = param.to;
|
|
381
|
-
external_node_assert_default()(from, 'missing "from" param for drag and drop');
|
|
382
|
-
external_node_assert_default()(to, 'missing "to" param for drag and drop');
|
|
383
|
-
const hdc = await this.getHdc();
|
|
384
|
-
await hdc.drag(from.center[0], from.center[1], to.center[0], to.center[1]);
|
|
385
|
-
}),
|
|
386
|
-
(0, device_namespaceObject.defineActionSwipe)(async (param)=>{
|
|
387
|
-
const { startPoint, endPoint, duration, repeatCount } = (0, device_namespaceObject.normalizeMobileSwipeParam)(param, await this.size());
|
|
388
|
-
const hdc = await this.getHdc();
|
|
389
|
-
for(let i = 0; i < repeatCount; i++)await hdc.swipe(startPoint.x, startPoint.y, endPoint.x, endPoint.y, duration ? Math.round(duration) : void 0);
|
|
390
|
-
}),
|
|
391
|
-
(0, device_namespaceObject.defineActionKeyboardPress)(async (param)=>{
|
|
392
|
-
await this.keyboardPress(param.keyName);
|
|
393
|
-
}),
|
|
394
|
-
(0, device_namespaceObject.defineActionCursorMove)(async (param)=>{
|
|
395
|
-
const arrowKey = 'left' === param.direction ? 'ArrowLeft' : 'ArrowRight';
|
|
396
|
-
const times = param.times ?? 1;
|
|
397
|
-
for(let i = 0; i < times; i++){
|
|
398
|
-
await this.keyboardPress(arrowKey);
|
|
399
|
-
await (0, core_utils_namespaceObject.sleep)(100);
|
|
400
|
-
}
|
|
401
|
-
}),
|
|
402
|
-
(0, device_namespaceObject.defineActionLongPress)(async (param)=>{
|
|
403
|
-
const element = param.locate;
|
|
404
|
-
if (!element) throw new Error('LongPress requires an element to be located');
|
|
405
|
-
await this.longPress(element.center[0], element.center[1]);
|
|
406
|
-
}),
|
|
407
|
-
(0, device_namespaceObject.defineActionClearInput)(async (param)=>{
|
|
408
|
-
await this.clearInput(param.locate);
|
|
409
|
-
})
|
|
326
|
+
...(0, device_namespaceObject.createDefaultMobileActions)(mobileActionContext)
|
|
410
327
|
];
|
|
411
328
|
const platformSpecificActions = Object.values(createPlatformActions(this));
|
|
412
329
|
const customActions = this.customActions ?? [];
|
|
@@ -416,6 +333,27 @@ class HarmonyDevice {
|
|
|
416
333
|
...customActions
|
|
417
334
|
];
|
|
418
335
|
}
|
|
336
|
+
async performActionScroll(param) {
|
|
337
|
+
const element = param.locate;
|
|
338
|
+
const startingPoint = element ? {
|
|
339
|
+
left: element.center[0],
|
|
340
|
+
top: element.center[1]
|
|
341
|
+
} : void 0;
|
|
342
|
+
const scrollToEventName = param?.scrollType;
|
|
343
|
+
if ('scrollToTop' === scrollToEventName) await this.scrollUntilTop(startingPoint);
|
|
344
|
+
else if ('scrollToBottom' === scrollToEventName) await this.scrollUntilBottom(startingPoint);
|
|
345
|
+
else if ('scrollToRight' === scrollToEventName) await this.scrollUntilRight(startingPoint);
|
|
346
|
+
else if ('scrollToLeft' === scrollToEventName) await this.scrollUntilLeft(startingPoint);
|
|
347
|
+
else if ('singleAction' !== scrollToEventName && scrollToEventName) throw new Error(`Unknown scroll event type: ${scrollToEventName}, param: ${JSON.stringify(param)}`);
|
|
348
|
+
else {
|
|
349
|
+
if (param?.direction !== 'down' && param && param.direction) if ('up' === param.direction) await this.scrollUp(param.distance ?? void 0, startingPoint);
|
|
350
|
+
else if ('left' === param.direction) await this.scrollLeft(param.distance ?? void 0, startingPoint);
|
|
351
|
+
else if ('right' === param.direction) await this.scrollRight(param.distance ?? void 0, startingPoint);
|
|
352
|
+
else throw new Error(`Unknown scroll direction: ${param.direction}`);
|
|
353
|
+
else await this.scrollDown(param?.distance ?? void 0, startingPoint);
|
|
354
|
+
await (0, core_utils_namespaceObject.sleep)(500);
|
|
355
|
+
}
|
|
356
|
+
}
|
|
419
357
|
describe() {
|
|
420
358
|
return this.descriptionText || `DeviceId: ${this.deviceId}`;
|
|
421
359
|
}
|
|
@@ -546,23 +484,23 @@ class HarmonyDevice {
|
|
|
546
484
|
}
|
|
547
485
|
throw new Error('Screenshot buffer is empty after retries');
|
|
548
486
|
}
|
|
549
|
-
async
|
|
487
|
+
async tapPoint(point) {
|
|
550
488
|
this.lastTapPosition = {
|
|
551
|
-
x,
|
|
552
|
-
y
|
|
489
|
+
x: point.x,
|
|
490
|
+
y: point.y
|
|
553
491
|
};
|
|
554
492
|
const hdc = await this.getHdc();
|
|
555
|
-
await hdc.click(x, y);
|
|
493
|
+
await hdc.click(point.x, point.y);
|
|
556
494
|
}
|
|
557
|
-
async
|
|
495
|
+
async doubleTapPoint(point) {
|
|
558
496
|
const hdc = await this.getHdc();
|
|
559
|
-
await hdc.doubleClick(x, y);
|
|
497
|
+
await hdc.doubleClick(point.x, point.y);
|
|
560
498
|
}
|
|
561
|
-
async
|
|
499
|
+
async longPressPoint(point) {
|
|
562
500
|
const hdc = await this.getHdc();
|
|
563
|
-
await hdc.longClick(x, y);
|
|
501
|
+
await hdc.longClick(point.x, point.y);
|
|
564
502
|
}
|
|
565
|
-
async
|
|
503
|
+
async typeText(text, element, shouldReplace) {
|
|
566
504
|
if (!text) return;
|
|
567
505
|
const hdc = await this.getHdc();
|
|
568
506
|
let x;
|
|
@@ -593,7 +531,7 @@ class HarmonyDevice {
|
|
|
593
531
|
}
|
|
594
532
|
await hdc.clearTextField(100);
|
|
595
533
|
}
|
|
596
|
-
async
|
|
534
|
+
async pressKey(key) {
|
|
597
535
|
const normalizedKey = keyNameAliasMap[key.toLowerCase()] ?? key;
|
|
598
536
|
const harmonyKey = harmonyKeyCodeMap[normalizedKey] ?? key;
|
|
599
537
|
const hdc = await this.getHdc();
|
|
@@ -793,6 +731,37 @@ class HarmonyDevice {
|
|
|
793
731
|
device_define_property(this, "interfaceType", 'harmony');
|
|
794
732
|
device_define_property(this, "uri", void 0);
|
|
795
733
|
device_define_property(this, "options", void 0);
|
|
734
|
+
device_define_property(this, "inputPrimitives", {
|
|
735
|
+
pointer: {
|
|
736
|
+
tap: (point)=>this.tapPoint(point),
|
|
737
|
+
doubleClick: (point)=>this.doubleTapPoint(point),
|
|
738
|
+
longPress: (point)=>this.longPressPoint(point),
|
|
739
|
+
dragAndDrop: async (from, to)=>{
|
|
740
|
+
const hdc = await this.getHdc();
|
|
741
|
+
await hdc.drag(from.x, from.y, to.x, to.y);
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
keyboard: {
|
|
745
|
+
keyboardPress: (keyName)=>this.pressKey(keyName),
|
|
746
|
+
typeText: (value, opts)=>opts?.focusOnly ? Promise.resolve() : this.typeText(value, opts?.target, opts?.replace ?? true),
|
|
747
|
+
clearInput: (target)=>this.clearInput(target),
|
|
748
|
+
cursorMove: async (direction, times = 1)=>{
|
|
749
|
+
const arrowKey = 'left' === direction ? 'ArrowLeft' : 'ArrowRight';
|
|
750
|
+
for(let i = 0; i < times; i++)await this.pressKey(arrowKey);
|
|
751
|
+
}
|
|
752
|
+
},
|
|
753
|
+
touch: {
|
|
754
|
+
swipe: async (start, end, opts)=>{
|
|
755
|
+
const duration = opts?.duration;
|
|
756
|
+
const repeatCount = opts?.repeat ?? 1;
|
|
757
|
+
const hdc = await this.getHdc();
|
|
758
|
+
for(let i = 0; i < repeatCount; i++)await hdc.swipe(start.x, start.y, end.x, end.y, duration ? Math.round(duration) : void 0);
|
|
759
|
+
}
|
|
760
|
+
},
|
|
761
|
+
scroll: {
|
|
762
|
+
scroll: (param)=>this.performActionScroll(param)
|
|
763
|
+
}
|
|
764
|
+
});
|
|
796
765
|
device_define_property(this, "remoteScreenshotPath", '/data/local/tmp/ms_screen.jpeg');
|
|
797
766
|
device_define_property(this, "localScreenshotPath", null);
|
|
798
767
|
external_node_assert_default()(deviceId, 'deviceId is required for HarmonyDevice');
|
|
@@ -1047,7 +1016,7 @@ class HarmonyMidsceneTools extends base_tools_namespaceObject.BaseMidsceneTools
|
|
|
1047
1016
|
const tools = new HarmonyMidsceneTools();
|
|
1048
1017
|
(0, cli_namespaceObject.runToolsCLI)(tools, 'midscene-harmony', {
|
|
1049
1018
|
stripPrefix: 'harmony_',
|
|
1050
|
-
version: "1.8.
|
|
1019
|
+
version: "1.8.1",
|
|
1051
1020
|
extraCommands: (0, core_namespaceObject.createReportCliCommands)()
|
|
1052
1021
|
}).catch((e)=>{
|
|
1053
1022
|
process.exit((0, cli_namespaceObject.reportCLIError)(e));
|