@bettergi/utils 0.0.11 → 0.1.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/mouse.d.ts CHANGED
@@ -1,3 +1,52 @@
1
+ type MouseWaypointsOptions = {
2
+ /** 是否按住鼠标左键拖动 */
3
+ shouldDrag?: boolean;
4
+ /** 超时时间(毫秒,默认: 不超时) */
5
+ timeout?: number;
6
+ };
7
+ /**
8
+ * 沿着指定路径点移动鼠标
9
+ * @param waypoints 鼠标路径点
10
+ * @param options 鼠标移动选项
11
+ */
12
+ export declare const mouseMoveAlongWaypoints: (waypoints: {
13
+ x: number;
14
+ y: number;
15
+ delay?: number;
16
+ }[], options?: MouseWaypointsOptions) => Promise<boolean>;
17
+ /**
18
+ * 鼠标拖拽滑动到指定位置
19
+ * @param x1 起始水平方向偏移量(像素)
20
+ * @param y1 起始垂直方向偏移量(像素)
21
+ * @param x2 终止水平方向偏移量(像素)
22
+ * @param y2 终止垂直方向偏移量(像素)
23
+ */
24
+ export declare const mouseDrag: (x1: number, y1: number, x2: number, y2: number) => Promise<boolean>;
25
+ type NaturalMouseMoveOptions = {
26
+ /** 移动持续时间(毫秒,默认: 800) */
27
+ duration: number;
28
+ /** 摆动幅度(默认: 30) */
29
+ wiggle?: number;
30
+ /** 随机种子(可选) */
31
+ seed?: number;
32
+ /** 缓动函数(默认: ease-out-cubic) */
33
+ easing?: (progress: number) => number;
34
+ };
35
+ /** 贝塞尔曲线生成鼠标移动路径点 */
36
+ export declare const bezierWaypoints: (x1: number, y1: number, x2: number, y2: number, options?: NaturalMouseMoveOptions) => Promise<{
37
+ x: number;
38
+ y: number;
39
+ delay: number;
40
+ }[]>;
41
+ /**
42
+ * 自然地移动鼠标到指定位置
43
+ * @param x1 起始水平方向偏移量(像素)
44
+ * @param y1 起始垂直方向偏移量(像素)
45
+ * @param x2 终止水平方向偏移量(像素)
46
+ * @param y2 终止垂直方向偏移量(像素)
47
+ * @param options 鼠标移动选项
48
+ */
49
+ export declare const naturalMouseMove: (x1: number, y1: number, x2: number, y2: number, options?: NaturalMouseMoveOptions & MouseWaypointsOptions) => Promise<boolean>;
1
50
  /**
2
51
  * 鼠标滚轮向上滚动指定高度
3
52
  * @param height 滚动高度
@@ -22,25 +71,4 @@ export declare const mouseScrollUpLines: (lines: number, lineHeight?: number) =>
22
71
  * @param lineHeight 行高(默认值为175像素)
23
72
  */
24
73
  export declare const mouseScrollDownLines: (lines: number, lineHeight?: number) => Promise<void>;
25
- /**
26
- * 鼠标拖拽滑动到指定位置
27
- * @param x1 起始水平方向偏移量(像素)
28
- * @param y1 起始垂直方向偏移量(像素)
29
- * @param x2 终止水平方向偏移量(像素)
30
- * @param y2 终止垂直方向偏移量(像素)
31
- */
32
- export declare const mouseSlide: (x1: number, y1: number, x2: number, y2: number) => Promise<void>;
33
- /**
34
- * 鼠标水平拖拽滑动指定距离
35
- * @param x 起始水平方向偏移量(像素)
36
- * @param y 起始垂直方向偏移量(像素)
37
- * @param distance 水平拖拽滑动距离(像素) 正数向右,负数向左
38
- */
39
- export declare const mouseSlideX: (x: number, y: number, distance: number) => Promise<void>;
40
- /**
41
- * 鼠标垂直拖拽滑动指定距离
42
- * @param x 起始水平方向偏移量(像素)
43
- * @param y 起始垂直方向偏移量(像素)
44
- * @param distance 垂直拖拽滑动距离(像素) 正数向下,负数向上
45
- */
46
- export declare const mouseSlideY: (x: number, y: number, distance: number) => Promise<void>;
74
+ export {};
package/dist/mouse.js CHANGED
@@ -1,7 +1,81 @@
1
- const _simulateScroll = async (scrollAmountInClicks, times) => {
1
+ /**
2
+ * 沿着指定路径点移动鼠标
3
+ * @param waypoints 鼠标路径点
4
+ * @param options 鼠标移动选项
5
+ */
6
+ export const mouseMoveAlongWaypoints = async (waypoints, options) => {
7
+ const { shouldDrag = false, timeout = 0 } = options || {};
8
+ try {
9
+ const startTime = Date.now();
10
+ for (let i = 0; i < waypoints.length; i++) {
11
+ // 开始拖拽
12
+ if (i === 0 && shouldDrag)
13
+ leftButtonDown();
14
+ moveMouseTo(Math.trunc(waypoints[i].x), Math.trunc(waypoints[i].y));
15
+ // 等待指定延迟
16
+ const delay = Math.trunc(waypoints[i].delay || 50);
17
+ if (delay > 0)
18
+ await sleep(delay);
19
+ // 超时检查
20
+ if (timeout > 0 && Date.now() - startTime > timeout)
21
+ return false;
22
+ }
23
+ }
24
+ finally {
25
+ // 结束拖拽
26
+ if (shouldDrag)
27
+ leftButtonUp();
28
+ }
29
+ return true;
30
+ };
31
+ /**
32
+ * 鼠标拖拽滑动到指定位置
33
+ * @param x1 起始水平方向偏移量(像素)
34
+ * @param y1 起始垂直方向偏移量(像素)
35
+ * @param x2 终止水平方向偏移量(像素)
36
+ * @param y2 终止垂直方向偏移量(像素)
37
+ */
38
+ export const mouseDrag = async (x1, y1, x2, y2) => {
39
+ return mouseMoveAlongWaypoints([
40
+ { x: x1, y: y1 },
41
+ { x: x2, y: y2 }
42
+ ], { shouldDrag: true });
43
+ };
44
+ /** 贝塞尔曲线生成鼠标移动路径点 */
45
+ export const bezierWaypoints = async (x1, y1, x2, y2, options) => {
46
+ const { duration = 800, wiggle = 30, seed = Math.random(), easing = (t) => 1 - Math.pow(1 - t, 3) // ease-out-cubic
47
+ } = options || {};
48
+ const random = () => ((seed * 9301 + 49297) % 233280) / 233280;
49
+ const controlX = (x1 + x2) / 2 + random() * wiggle * 2 - wiggle;
50
+ const controlY = (y1 + y2) / 2 + random() * wiggle * 2 - wiggle;
51
+ const steps = Math.max(duration / 16, 10);
52
+ return Array.from({ length: steps + 1 }, (_, i) => {
53
+ const t = easing(i / steps);
54
+ const u = 1 - t;
55
+ return {
56
+ x: u * u * x1 + 2 * u * t * controlX + t * t * x2,
57
+ y: u * u * y1 + 2 * u * t * controlY + t * t * y2,
58
+ delay: Math.round((duration / steps) * (0.8 + Math.random() * 0.4))
59
+ };
60
+ }).concat([{ x: x2, y: y2, delay: 0 }]);
61
+ };
62
+ /**
63
+ * 自然地移动鼠标到指定位置
64
+ * @param x1 起始水平方向偏移量(像素)
65
+ * @param y1 起始垂直方向偏移量(像素)
66
+ * @param x2 终止水平方向偏移量(像素)
67
+ * @param y2 终止垂直方向偏移量(像素)
68
+ * @param options 鼠标移动选项
69
+ */
70
+ export const naturalMouseMove = async (x1, y1, x2, y2, options) => {
71
+ const waypoints = await bezierWaypoints(x1, y1, x2, y2, options);
72
+ return mouseMoveAlongWaypoints(waypoints, options);
73
+ };
74
+ /** 使用回放脚本模拟滚动 */
75
+ const simulateScroll = async (scrollAmountInClicks, times) => {
2
76
  const script = {
3
77
  macroEvents: Array(times).fill({ type: 6, mouseX: 0, mouseY: scrollAmountInClicks, time: 0 }),
4
- info: { name: "", description: "", x: 0, y: 0, width: 1920, height: 1080, recordDpi: 1.25 }
78
+ info: { name: "", description: "", x: 0, y: 0, width: 1920, height: 1080, recordDpi: 1.5 }
5
79
  };
6
80
  await keyMouseScript.run(JSON.stringify(script));
7
81
  };
@@ -11,7 +85,7 @@ const _simulateScroll = async (scrollAmountInClicks, times) => {
11
85
  * @param algorithm 自定义滚动算法函数,接收高度参数并返回滚动次数(默认算法为每18像素滚动一次)
12
86
  */
13
87
  export const mouseScrollUp = (height, algorithm = h => Math.floor(h / 18)) => {
14
- return _simulateScroll(120, algorithm(height));
88
+ return simulateScroll(120, algorithm(height));
15
89
  };
16
90
  /**
17
91
  * 鼠标滚轮向下滚动指定高度
@@ -19,7 +93,7 @@ export const mouseScrollUp = (height, algorithm = h => Math.floor(h / 18)) => {
19
93
  * @param algorithm 自定义滚动算法函数,接收高度参数并返回滚动次数(默认算法为每18像素滚动一次)
20
94
  */
21
95
  export const mouseScrollDown = (height, algorithm = h => Math.floor(h / 18)) => {
22
- return _simulateScroll(-120, algorithm(height));
96
+ return simulateScroll(-120, algorithm(height));
23
97
  };
24
98
  /**
25
99
  * 鼠标滚轮向上滚动指定行数
@@ -37,37 +111,3 @@ export const mouseScrollUpLines = (lines, lineHeight = 175) => {
37
111
  export const mouseScrollDownLines = (lines, lineHeight = 175) => {
38
112
  return mouseScrollDown(lines * lineHeight);
39
113
  };
40
- /**
41
- * 鼠标拖拽滑动到指定位置
42
- * @param x1 起始水平方向偏移量(像素)
43
- * @param y1 起始垂直方向偏移量(像素)
44
- * @param x2 终止水平方向偏移量(像素)
45
- * @param y2 终止垂直方向偏移量(像素)
46
- */
47
- export const mouseSlide = async (x1, y1, x2, y2) => {
48
- moveMouseTo(x1, y1);
49
- await sleep(100);
50
- leftButtonDown();
51
- await sleep(100);
52
- moveMouseTo(x2, y2);
53
- await sleep(100);
54
- leftButtonUp();
55
- };
56
- /**
57
- * 鼠标水平拖拽滑动指定距离
58
- * @param x 起始水平方向偏移量(像素)
59
- * @param y 起始垂直方向偏移量(像素)
60
- * @param distance 水平拖拽滑动距离(像素) 正数向右,负数向左
61
- */
62
- export const mouseSlideX = (x, y, distance) => {
63
- return mouseSlide(x, y, x + distance, y);
64
- };
65
- /**
66
- * 鼠标垂直拖拽滑动指定距离
67
- * @param x 起始水平方向偏移量(像素)
68
- * @param y 起始垂直方向偏移量(像素)
69
- * @param distance 垂直拖拽滑动距离(像素) 正数向下,负数向上
70
- */
71
- export const mouseSlideY = (x, y, distance) => {
72
- return mouseSlide(x, y, x, y + distance);
73
- };
package/dist/ocr.d.ts CHANGED
@@ -1,73 +1,83 @@
1
- type Direction = "north" | "north-east" | "east" | "south-east" | "south" | "south-west" | "west" | "north-west";
1
+ import { RetryOptions } from "./workflow";
2
+ type MatchDirection = "north" /** 上半边 */ | "north-east" /** 右上四分之一 */ | "east" /** 右半边 */ | "south-east" /** 右下四分之一 */ | "south" /** 下半边 */ | "south-west" /** 左下四分之一 */ | "west" /** 左半边 */ | "north-west"; /** 左上四分之一 */
2
3
  /**
3
4
  * 在整个画面内搜索图片
4
5
  * @param path 图片路径
5
- * @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
6
+ * @returns 如果找到匹配的图片区域,则返回该区域
6
7
  */
7
- export declare const findImage: (path: string) => globalThis.Region | undefined;
8
+ export declare const findImage: (path: string) => Region | undefined;
8
9
  /**
9
10
  * 在指定区域内搜索图片
10
11
  * @param path 图片路径
11
- * @param x - 水平方向偏移量(像素)
12
- * @param y - 垂直方向偏移量(像素)
12
+ * @param x 水平方向偏移量(像素)
13
+ * @param y 垂直方向偏移量(像素)
13
14
  * @param w 宽度
14
15
  * @param h 高度
15
- * @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
16
+ * @returns 如果找到匹配的图片区域,则返回该区域
16
17
  */
17
- export declare const findImageWithinBounds: (path: string, x: number, y: number, w: number, h: number) => globalThis.Region | undefined;
18
+ export declare const findImageWithinBounds: (path: string, x: number, y: number, w: number, h: number) => Region | undefined;
18
19
  /**
19
20
  * 在指定方向上搜索图片
20
21
  * @param path 图片路径
21
22
  * @param direction 搜索方向
22
- * @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
23
+ * @returns 如果找到匹配的图片区域,则返回该区域
23
24
  */
24
- export declare const findImageInDirection: (path: string, direction: Direction) => globalThis.Region | undefined;
25
+ export declare const findImageInDirection: (path: string, direction: MatchDirection) => Region | undefined;
26
+ /** 文本搜索选项 */
27
+ type TextMatchOptions = {
28
+ /** 是否忽略大小写(默认: 是) */
29
+ ignoreCase?: boolean;
30
+ /** 是否非完全匹配(默认: 否) */
31
+ contains?: boolean;
32
+ };
25
33
  /**
26
34
  * 在整个画面内搜索文本
27
35
  * @param text 待搜索文本
28
- * @param contains 是否包含
29
- * @param ignoreCase 是否忽略大小写
30
- * @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
36
+ * @param options 搜索选项
37
+ * @returns 如果找到匹配的文本区域,则返回该区域
31
38
  */
32
- export declare const findText: (text: string, contains: boolean, ignoreCase: boolean) => globalThis.Region | undefined;
39
+ export declare const findText: (text: string, options?: TextMatchOptions) => Region | undefined;
33
40
  /**
34
41
  * 在指定区域内搜索文本
35
42
  * @param text 待搜索文本
36
- * @param contains 是否包含
37
- * @param ignoreCase 是否忽略大小写
38
43
  * @param x 水平方向偏移量(像素)
39
- * @param x - 水平方向偏移量(像素)
40
- * @param y - 垂直方向偏移量(像素)
44
+ * @param y 垂直方向偏移量(像素)
41
45
  * @param w 宽度
42
46
  * @param h 高度
43
- * @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
47
+ * @param options 搜索选项
48
+ * @returns 如果找到匹配的文本区域,则返回该区域
44
49
  */
45
- export declare const findTextWithinBounds: (text: string, contains: boolean, ignoreCase: boolean, x: number, y: number, w: number, h: number) => globalThis.Region | undefined;
50
+ export declare const findTextWithinBounds: (text: string, x: number, y: number, w: number, h: number, options?: TextMatchOptions) => Region | undefined;
46
51
  /**
47
52
  * 在指定方向上搜索文本
48
53
  * @param text 待搜索文本
49
- * @param contains 是否包含
50
- * @param ignoreCase 是否忽略大小写
51
54
  * @param direction 搜索方向
52
- * @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
55
+ * @param options 搜索选项
56
+ * @returns 如果找到匹配的文本区域,则返回该区域
53
57
  */
54
- export declare const findTextInDirection: (text: string, contains: boolean, ignoreCase: boolean, direction: Direction) => globalThis.Region | undefined;
58
+ export declare const findTextInDirection: (text: string, direction: MatchDirection, options?: TextMatchOptions) => Region | undefined;
59
+ /** 列表视图参数 */
60
+ export type ListView = {
61
+ x: number;
62
+ y: number;
63
+ w: number;
64
+ h: number;
65
+ /** 列表项高度 */
66
+ lineHeight: number;
67
+ /** 每次滚动的行数(默认: 1) */
68
+ scrollLines?: number;
69
+ /** 横向内边距 (默认: 10) */
70
+ paddingX?: number;
71
+ /** 纵向内边距 (默认: 10) */
72
+ paddingY?: number;
73
+ };
55
74
  /**
56
75
  * 在列表视图中滚动搜索文本
57
76
  * @param text 待搜索文本
58
- * @param contains 是否包含
59
- * @param ignoreCase 是否忽略大小写
60
77
  * @param listView 列表视图参数
78
+ * @param matchOptions 搜索选项
61
79
  * @param timeout 搜索超时
62
80
  * @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
63
81
  */
64
- export declare const findTextWithinListView: (text: string, contains: boolean, ignoreCase: boolean, listView: {
65
- x: number;
66
- y: number;
67
- w: number;
68
- h: number;
69
- maxListItems: number;
70
- lineHeight: number;
71
- padding?: number;
72
- }, timeout?: number) => Promise<globalThis.Region | undefined>;
82
+ export declare const findTextWithinListView: (text: string, listView: ListView, matchOptions?: TextMatchOptions, retryOptions?: RetryOptions) => Promise<Region | undefined>;
73
83
  export {};
package/dist/ocr.js CHANGED
@@ -1,5 +1,5 @@
1
- import { waitUntil } from "./flow";
2
1
  import { mouseScrollDownLines } from "./mouse";
2
+ import { waitForAction } from "./workflow";
3
3
  const findFirst = (ir, ro, predicate) => {
4
4
  const candidates = ir.findMulti(ro);
5
5
  for (let i = 0; i < candidates.count; i++) {
@@ -18,7 +18,7 @@ const directionToBounds = (direction) => {
18
18
  /**
19
19
  * 在整个画面内搜索图片
20
20
  * @param path 图片路径
21
- * @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
21
+ * @returns 如果找到匹配的图片区域,则返回该区域
22
22
  */
23
23
  export const findImage = (path) => {
24
24
  try {
@@ -33,11 +33,11 @@ export const findImage = (path) => {
33
33
  /**
34
34
  * 在指定区域内搜索图片
35
35
  * @param path 图片路径
36
- * @param x - 水平方向偏移量(像素)
37
- * @param y - 垂直方向偏移量(像素)
36
+ * @param x 水平方向偏移量(像素)
37
+ * @param y 垂直方向偏移量(像素)
38
38
  * @param w 宽度
39
39
  * @param h 高度
40
- * @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
40
+ * @returns 如果找到匹配的图片区域,则返回该区域
41
41
  */
42
42
  export const findImageWithinBounds = (path, x, y, w, h) => {
43
43
  try {
@@ -53,7 +53,7 @@ export const findImageWithinBounds = (path, x, y, w, h) => {
53
53
  * 在指定方向上搜索图片
54
54
  * @param path 图片路径
55
55
  * @param direction 搜索方向
56
- * @returns 如果找到匹配的图片区域,则返回该区域,否则返回 undefined
56
+ * @returns 如果找到匹配的图片区域,则返回该区域
57
57
  */
58
58
  export const findImageInDirection = (path, direction) => {
59
59
  const { x, y, w, h } = directionToBounds(direction);
@@ -62,11 +62,11 @@ export const findImageInDirection = (path, direction) => {
62
62
  /**
63
63
  * 在整个画面内搜索文本
64
64
  * @param text 待搜索文本
65
- * @param contains 是否包含
66
- * @param ignoreCase 是否忽略大小写
67
- * @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
65
+ * @param options 搜索选项
66
+ * @returns 如果找到匹配的文本区域,则返回该区域
68
67
  */
69
- export const findText = (text, contains, ignoreCase) => {
68
+ export const findText = (text, options) => {
69
+ const { ignoreCase = true, contains = false } = options || {};
70
70
  const searchText = ignoreCase ? text.toLowerCase() : text;
71
71
  const ir = captureGameRegion();
72
72
  const ro = RecognitionObject.ocrThis;
@@ -79,16 +79,15 @@ export const findText = (text, contains, ignoreCase) => {
79
79
  /**
80
80
  * 在指定区域内搜索文本
81
81
  * @param text 待搜索文本
82
- * @param contains 是否包含
83
- * @param ignoreCase 是否忽略大小写
84
82
  * @param x 水平方向偏移量(像素)
85
- * @param x - 水平方向偏移量(像素)
86
- * @param y - 垂直方向偏移量(像素)
83
+ * @param y 垂直方向偏移量(像素)
87
84
  * @param w 宽度
88
85
  * @param h 高度
89
- * @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
86
+ * @param options 搜索选项
87
+ * @returns 如果找到匹配的文本区域,则返回该区域
90
88
  */
91
- export const findTextWithinBounds = (text, contains, ignoreCase, x, y, w, h) => {
89
+ export const findTextWithinBounds = (text, x, y, w, h, options) => {
90
+ const { ignoreCase = true, contains = false } = options || {};
92
91
  const searchText = ignoreCase ? text.toLowerCase() : text;
93
92
  const ir = captureGameRegion();
94
93
  const ro = RecognitionObject.ocr(x, y, w, h);
@@ -101,49 +100,47 @@ export const findTextWithinBounds = (text, contains, ignoreCase, x, y, w, h) =>
101
100
  /**
102
101
  * 在指定方向上搜索文本
103
102
  * @param text 待搜索文本
104
- * @param contains 是否包含
105
- * @param ignoreCase 是否忽略大小写
106
103
  * @param direction 搜索方向
107
- * @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
104
+ * @param options 搜索选项
105
+ * @returns 如果找到匹配的文本区域,则返回该区域
108
106
  */
109
- export const findTextInDirection = (text, contains, ignoreCase, direction) => {
107
+ export const findTextInDirection = (text, direction, options) => {
110
108
  const { x, y, w, h } = directionToBounds(direction);
111
- return findTextWithinBounds(text, contains, ignoreCase, x, y, w, h);
109
+ return findTextWithinBounds(text, x, y, w, h, options);
112
110
  };
113
111
  /**
114
112
  * 在列表视图中滚动搜索文本
115
113
  * @param text 待搜索文本
116
- * @param contains 是否包含
117
- * @param ignoreCase 是否忽略大小写
118
114
  * @param listView 列表视图参数
115
+ * @param matchOptions 搜索选项
119
116
  * @param timeout 搜索超时
120
117
  * @returns 如果找到匹配的文本区域,则返回该区域,否则返回 undefined
121
118
  */
122
- export const findTextWithinListView = async (text, contains, ignoreCase, listView, timeout = 30 * 1000) => {
123
- const { x, y, w, h, maxListItems, lineHeight, padding = 10 } = listView;
124
- const find = () => {
125
- return findTextWithinBounds(text, contains, ignoreCase, x, y, w, h);
126
- };
127
- let firstRegion;
128
- const isBottomTouched = () => {
129
- const ro = RecognitionObject.ocr(x, y, w, h);
130
- const list = captureGameRegion().findMulti(ro);
131
- if (list.count > 0) {
132
- if (firstRegion?.text === list[0].text && Math.abs(list[0].y - firstRegion.y) < lineHeight) {
119
+ export const findTextWithinListView = async (text, listView, matchOptions, retryOptions) => {
120
+ const { x, y, w, h, lineHeight, scrollLines = 1, paddingX = 10, paddingY = 10 } = listView;
121
+ const { maxAttempts = 30, retryInterval = 1000 } = retryOptions || {};
122
+ const findTargetText = () => findTextWithinBounds(text, x, y, w, h, matchOptions);
123
+ let lastTextRegion;
124
+ const isReachedBottom = () => {
125
+ const textRegion = findFirst(captureGameRegion(), RecognitionObject.ocr(x, y, w, h), region => {
126
+ return region.isExist() && region.text.trim().length > 0;
127
+ });
128
+ if (textRegion) {
129
+ if (lastTextRegion?.text === textRegion.text &&
130
+ Math.abs(textRegion.y - lastTextRegion.y) < lineHeight) {
133
131
  return true;
134
132
  }
135
133
  else {
136
- firstRegion = list[0];
134
+ lastTextRegion = textRegion;
137
135
  return false;
138
136
  }
139
137
  }
140
- else {
141
- return true;
142
- }
138
+ // 异常情况: 找不到任何文本
139
+ return true;
143
140
  };
144
- const ok = await waitUntil(() => find() != undefined || isBottomTouched(), timeout, 1000, async () => {
145
- moveMouseTo(x + w - padding, y + padding);
146
- await mouseScrollDownLines(maxListItems, lineHeight);
147
- });
148
- return ok ? find() : undefined;
141
+ const isTextFoundOrBottomReached = await waitForAction(() => findTargetText() != undefined || isReachedBottom(), async () => {
142
+ moveMouseTo(x + w - paddingX, y + paddingY); // 移动到滚动条附近
143
+ await mouseScrollDownLines(scrollLines, lineHeight); // 滚动指定行数
144
+ }, { maxAttempts, retryInterval });
145
+ return isTextFoundOrBottomReached ? findTargetText() : undefined;
149
146
  };
package/dist/store.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * 创建一个持久化存储对象,用于管理应用状态数据
3
3
  * 该函数会创建一个代理对象,对该对象的所有属性的修改都会自动同步到相应的JSON文件(脚本的 `store` 目录下)中。
4
+ * 支持深层嵌套对象的代理。
4
5
  * @param name 存储对象的名称,将作为文件名(不包扩展名)
5
6
  */
6
7
  export declare const useStore: <T extends Record<string, any>>(name: string) => T;
package/dist/store.js CHANGED
@@ -1,20 +1,52 @@
1
1
  /**
2
2
  * 创建一个持久化存储对象,用于管理应用状态数据
3
3
  * 该函数会创建一个代理对象,对该对象的所有属性的修改都会自动同步到相应的JSON文件(脚本的 `store` 目录下)中。
4
+ * 支持深层嵌套对象的代理。
4
5
  * @param name 存储对象的名称,将作为文件名(不包扩展名)
5
6
  */
6
7
  export const useStore = (name) => {
7
- const path = `store/${name}.json`;
8
+ const filePath = `store/${name}.json`;
9
+ // 读取文件数据
8
10
  const obj = (() => {
9
11
  try {
10
- const text = file.readTextSync(path);
12
+ const text = file.readTextSync(filePath);
11
13
  return JSON.parse(text);
12
14
  }
13
15
  catch {
14
- return {};
16
+ return {}; // 创建空对象
15
17
  }
16
18
  })();
17
- return new Proxy(obj, {
18
- set: (target, key, value) => Reflect.set(target, key, value) && file.writeTextSync(path, JSON.stringify(target, null, 2))
19
- });
19
+ // 创建代理函数
20
+ const createProxy = (target, parentPath = []) => {
21
+ if (typeof target !== "object" || target === null) {
22
+ return target;
23
+ }
24
+ return new Proxy(target, {
25
+ get: (target, key) => {
26
+ const value = Reflect.get(target, key);
27
+ return typeof value === "object" && value !== null
28
+ ? createProxy(value, [...parentPath, key]) // 递归创建代理
29
+ : value;
30
+ },
31
+ set: (target, key, value) => {
32
+ const success = Reflect.set(target, key, value);
33
+ if (success) {
34
+ Promise.resolve().then(() => {
35
+ file.writeTextSync(filePath, JSON.stringify(obj, null, 2));
36
+ });
37
+ }
38
+ return success;
39
+ },
40
+ deleteProperty: (target, key) => {
41
+ const success = Reflect.deleteProperty(target, key);
42
+ if (success) {
43
+ Promise.resolve().then(() => {
44
+ file.writeTextSync(filePath, JSON.stringify(obj, null, 2));
45
+ });
46
+ }
47
+ return success;
48
+ }
49
+ });
50
+ };
51
+ return createProxy(obj);
20
52
  };
@@ -0,0 +1,52 @@
1
+ export type Action = () => Promise<void> | void;
2
+ export type RetryOptions = {
3
+ /** 重试次数(默认: 5) */
4
+ maxAttempts?: number;
5
+ /** 重试间隔(毫秒,默认: 1000) */
6
+ retryInterval?: number;
7
+ };
8
+ /**
9
+ * 等待直到条件满足或超时,期间执行重试操作
10
+ * @param condition 返回条件是否满足的函数
11
+ * @param retryAction 每次重试时执行的操作(可选)
12
+ * @param options 配置选项
13
+ * @returns - true 条件满足
14
+ * - false 达到最大重试次数
15
+ */
16
+ export declare const waitForAction: (condition: () => boolean, retryAction?: Action, options?: RetryOptions) => Promise<boolean>;
17
+ /**
18
+ * 等待某个区域出现,期间执行重试操作
19
+ * @param regionProvider 返回区域的函数
20
+ * @param retryAction 每次重试时执行的操作(可选)
21
+ * @param options 配置选项
22
+ * @returns - true 区域出现
23
+ * - false 达到最大重试次数
24
+ */
25
+ export declare const waitForRegionAppear: (regionProvider: () => Region | null | undefined, retryAction?: Action, options?: RetryOptions) => Promise<boolean>;
26
+ /**
27
+ * 等待某个区域消失,期间执行重试操作
28
+ * @param regionProvider 返回区域的函数
29
+ * @param retryAction 每次重试时执行的操作(可选)
30
+ * @param options 配置选项
31
+ * @returns - true 区域消失
32
+ * - false 达到最大重试次数
33
+ */
34
+ export declare const waitForRegionDisappear: (regionProvider: () => Region | null | undefined, retryAction?: Action, options?: RetryOptions) => Promise<boolean>;
35
+ /**
36
+ * 等待整个画面上某个元素出现,期间执行重试操作
37
+ * @param recognitionObject 识别对象
38
+ * @param retryAction 每次重试时执行的操作(可选)
39
+ * @param options 配置选项
40
+ * @returns - true 整个画面上某个元素出现
41
+ * - false 达到最大重试次数
42
+ */
43
+ export declare const waitForElementAppear: (recognitionObject: RecognitionObject, retryAction?: Action, options?: RetryOptions) => Promise<boolean>;
44
+ /**
45
+ * 等待整个画面上某个元素消失,期间执行重试操作
46
+ * @param recognitionObject 识别对象
47
+ * @param retryAction 每次重试时执行的操作(可选)
48
+ * @param options 配置选项
49
+ * @returns - true 整个画面上某个元素消失
50
+ * - false 达到最大重试次数
51
+ */
52
+ export declare const waitForElementDisappear: (recognitionObject: RecognitionObject, retryAction?: Action, options?: RetryOptions) => Promise<boolean>;