android-midscene-automation 0.1.21 → 0.1.23

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.
@@ -0,0 +1,81 @@
1
+ import type { FlowKind } from './flow-graph';
2
+ import type { AppiumRecordedStep } from './types';
3
+
4
+ export function defaultFlowKind(step: AppiumRecordedStep): FlowKind {
5
+ if (step.flow?.nodeKind) return step.flow.nodeKind;
6
+ if (step.type === 'assertExists' || step.type === 'assertText') return 'assertion';
7
+ return 'action';
8
+ }
9
+
10
+ export function flowKindLabel(kind: FlowKind) {
11
+ return { action: '操作', condition: '判断', assertion: '校验' }[kind];
12
+ }
13
+
14
+ export function flowTypeLabel(step: AppiumRecordedStep) {
15
+ const labelMap: Partial<Record<AppiumRecordedStep['type'], string>> = {
16
+ tap: '点击',
17
+ input: '输入',
18
+ tapIfExists: '存在则点击',
19
+ inputIfExists: '存在则输入',
20
+ clearIfExists: '存在则清空',
21
+ backIfExists: '存在则返回',
22
+ clearInput: '清空',
23
+ waitFor: '等待出现',
24
+ waitDisappear: '等待消失',
25
+ assertExists: '断言存在',
26
+ assertText: '断言文本',
27
+ key: '按键',
28
+ waitActivity: '等待 Activity',
29
+ delay: '延时',
30
+ coordinateTap: '坐标点击',
31
+ swipe: '滑动',
32
+ screenshot: '截图',
33
+ launchApp: '启动 APP',
34
+ clearAppData: '清理 APP 缓存',
35
+ longPress: '长按',
36
+ pinch: '双指缩放',
37
+ runScript: '连接脚本',
38
+ };
39
+ return labelMap[step.type] || step.type;
40
+ }
41
+
42
+ export function flowStepMeta(step: AppiumRecordedStep) {
43
+ if (step.type === 'delay') return `${step.timeoutMs || 1000}ms`;
44
+ if (step.type === 'input' || step.type === 'inputIfExists') return `输入内容:${step.value || '空'}`;
45
+ if (defaultFlowKind(step) === 'condition' && step.value) {
46
+ return `${step.flow?.textMatch === 'exact' ? '精准匹配' : '模糊匹配'}:${step.value}`;
47
+ }
48
+ if (step.contextSelector && step.selector) {
49
+ return `父级 ${step.contextSelector.strategy} ${step.contextSelector.value || ''} + 子级 ${step.selector.strategy} ${step.selector.value || ''}`;
50
+ }
51
+ if (step.type === 'key') return `keyCode ${step.keyCode || ''}`;
52
+ if (step.type === 'waitActivity') return step.value || '';
53
+ if (step.type === 'launchApp') return step.value || '';
54
+ if (step.type === 'clearAppData') return `${step.value || ''}(清除数据与缓存)`;
55
+ if (step.type === 'runScript') return step.value ? `脚本 ${step.value}` : '';
56
+ if (step.type === 'screenshot') return '保存当前截图';
57
+ if (step.type === 'swipe') {
58
+ const swipe = step.swipe;
59
+ return swipe ? `[${swipe.startX},${swipe.startY}] -> [${swipe.endX},${swipe.endY}]` : '';
60
+ }
61
+ if (step.type === 'pinch') return step.pinch?.direction === 'out' ? '放大' : '缩小';
62
+ if (step.type === 'longPress' || step.type === 'coordinateTap') {
63
+ return `${step.fallback?.centerX || ''},${step.fallback?.centerY || ''}`;
64
+ }
65
+ if (step.type === 'assertText') return step.value || '';
66
+ return `${step.selector?.strategy || ''} ${step.selector?.value || ''}`.trim();
67
+ }
68
+
69
+ export function labelFlowStep(step: AppiumRecordedStep) {
70
+ const meta = [
71
+ flowKindLabel(defaultFlowKind(step)),
72
+ flowTypeLabel(step),
73
+ step.optional ? '可选' : '',
74
+ flowStepMeta(step),
75
+ ].filter(Boolean).join(' · ');
76
+ return {
77
+ title: step.label,
78
+ meta,
79
+ note: step.note,
80
+ };
81
+ }
@@ -60,6 +60,7 @@ export type AppiumRecordedStep = {
60
60
  | 'swipe'
61
61
  | 'screenshot'
62
62
  | 'launchApp'
63
+ | 'clearAppData'
63
64
  | 'waitDisappear'
64
65
  | 'assertText'
65
66
  | 'longPress'
@@ -195,7 +195,10 @@ function handlePointerCancel(event: PointerEvent) {
195
195
 
196
196
  <div
197
197
  class="device-preview"
198
- :class="{ 'device-preview--image': imageUrl && !frameUrl }"
198
+ :class="{
199
+ 'device-preview--image': imageUrl && !frameUrl,
200
+ 'device-preview--empty': !hasPreview,
201
+ }"
199
202
  >
200
203
  <div
201
204
  v-if="hasPreview"
package/src/main.ts CHANGED
@@ -1,7 +1,10 @@
1
1
  import { createApp } from 'vue';
2
2
  import ElementPlus from 'element-plus';
3
3
  import 'element-plus/dist/index.css';
4
+ import '@vue-flow/core/dist/style.css';
5
+ import '@vue-flow/core/dist/theme-default.css';
4
6
  import App from './App.vue';
5
7
  import './style.css';
8
+ import './ui-refresh.css';
6
9
 
7
10
  createApp(App).use(ElementPlus).mount('#app');
package/src/style.css CHANGED
@@ -704,6 +704,7 @@ select {
704
704
  .appium-flow-content {
705
705
  --appium-flow-line-width: 3px;
706
706
  --appium-flow-line-color: #dbe5f1;
707
+ position: relative;
707
708
  display: grid;
708
709
  gap: 8px;
709
710
  align-content: start;
@@ -830,6 +831,10 @@ select {
830
831
  transform: translateX(-50%);
831
832
  }
832
833
 
834
+ .appium-flow-item--condition > .appium-flow-item__line {
835
+ display: none;
836
+ }
837
+
833
838
  .appium-flow-node {
834
839
  position: relative;
835
840
  z-index: 1;
@@ -992,7 +997,7 @@ select {
992
997
  }
993
998
 
994
999
  .appium-flow-branch-lines path,
995
- .appium-flow-merge path {
1000
+ .appium-flow-connection-layer path {
996
1001
  fill: none;
997
1002
  stroke: var(--appium-flow-line-color);
998
1003
  stroke-width: var(--appium-flow-line-width);
@@ -1115,10 +1120,10 @@ select {
1115
1120
  position: relative;
1116
1121
  z-index: 1;
1117
1122
  display: flex;
1118
- flex-wrap: wrap;
1119
- gap: 6px;
1120
- justify-self: center;
1123
+ align-items: center;
1121
1124
  justify-content: center;
1125
+ justify-self: center;
1126
+ width: 100%;
1122
1127
  }
1123
1128
 
1124
1129
  .appium-flow-branch:has(
@@ -1134,12 +1139,6 @@ select {
1134
1139
  display: none;
1135
1140
  }
1136
1141
 
1137
- .appium-flow-branch__connect {
1138
- height: 24px;
1139
- padding: 0 8px;
1140
- font-size: 12px;
1141
- }
1142
-
1143
1142
  .appium-flow-branch--yes .appium-flow-branch__label {
1144
1143
  background: #67c23a;
1145
1144
  }
@@ -1156,43 +1155,22 @@ select {
1156
1155
  background: transparent;
1157
1156
  }
1158
1157
 
1159
- .appium-flow-branch--connected .appium-flow-branch__line {
1160
- bottom: -1px;
1161
- height: calc(100% - 17px);
1162
- }
1163
-
1164
- .appium-flow-branch--connected {
1165
- align-self: stretch;
1166
- }
1167
-
1168
- .appium-flow-merge {
1169
- order: 3;
1158
+ .appium-flow-connection-space {
1159
+ order: 4;
1170
1160
  position: relative;
1171
- z-index: 0;
1172
- justify-self: stretch;
1173
1161
  height: 36px;
1174
1162
  margin: -6px 0 0;
1175
1163
  pointer-events: none;
1176
1164
  }
1177
1165
 
1178
- .appium-flow-merge svg {
1166
+ .appium-flow-connection-layer {
1179
1167
  position: absolute;
1180
- inset: 0 0 -8px;
1168
+ inset: 0;
1169
+ z-index: 0;
1181
1170
  width: 100%;
1182
- height: calc(100% + 8px);
1171
+ height: 100%;
1183
1172
  overflow: visible;
1184
- }
1185
-
1186
- .appium-flow-merge path {
1187
- stroke: var(--appium-flow-line-color);
1188
- }
1189
-
1190
- .appium-flow-merge__yes {
1191
- stroke: var(--appium-flow-line-color);
1192
- }
1193
-
1194
- .appium-flow-merge__no {
1195
- stroke: var(--appium-flow-line-color);
1173
+ pointer-events: none;
1196
1174
  }
1197
1175
 
1198
1176
  .appium-flow-branch--yes .appium-flow-branch-step:not(.appium-flow-node--selected) {
@@ -1264,6 +1242,18 @@ select {
1264
1242
  width: 100%;
1265
1243
  }
1266
1244
 
1245
+ .appium-linked-script-picker {
1246
+ display: flex;
1247
+ align-items: center;
1248
+ gap: 8px;
1249
+ width: 100%;
1250
+ }
1251
+
1252
+ .appium-linked-script-picker .appium-linked-script-select {
1253
+ flex: 1;
1254
+ min-width: 0;
1255
+ }
1256
+
1267
1257
  .appium-linked-script-activity {
1268
1258
  display: block;
1269
1259
  box-sizing: border-box;
@@ -1278,6 +1268,27 @@ select {
1278
1268
  overflow-wrap: anywhere;
1279
1269
  }
1280
1270
 
1271
+ .appium-linked-script-preview-dialog .el-dialog__body {
1272
+ padding-top: 10px;
1273
+ }
1274
+
1275
+ .appium-linked-script-preview-meta {
1276
+ display: grid;
1277
+ gap: 4px;
1278
+ margin-bottom: 10px;
1279
+ color: #64748b;
1280
+ font-size: 13px;
1281
+ }
1282
+
1283
+ .appium-linked-script-preview-meta strong {
1284
+ color: #1f2937;
1285
+ }
1286
+
1287
+ .appium-linked-script-preview-canvas.appium-flow-canvas--vue {
1288
+ height: 68vh;
1289
+ min-height: 520px;
1290
+ }
1291
+
1281
1292
  .appium-replay-output {
1282
1293
  min-height: 120px;
1283
1294
  max-height: 260px;
@@ -2772,3 +2783,341 @@ select {
2772
2783
  grid-template-columns: 1fr;
2773
2784
  }
2774
2785
  }
2786
+
2787
+ /* Vue Flow based Appium recorder canvas. Keep these rules after the legacy
2788
+ flow styles so the old hand-drawn connector CSS cannot leak into it. */
2789
+ .appium-flow-canvas--vue {
2790
+ position: relative;
2791
+ width: 100%;
2792
+ height: clamp(520px, calc(100vh - 300px), 780px);
2793
+ min-height: 520px;
2794
+ overflow: hidden;
2795
+ border: 1px solid #e6edf5;
2796
+ border-radius: 12px;
2797
+ background: #f7f9fc;
2798
+ }
2799
+
2800
+ .appium-flow-canvas--dialog.appium-flow-canvas--vue {
2801
+ height: 72vh;
2802
+ min-height: 620px;
2803
+ }
2804
+
2805
+ .appium-vue-flow {
2806
+ width: 100%;
2807
+ height: 100%;
2808
+ background:
2809
+ radial-gradient(circle at 1px 1px, #d9e6f5 1.25px, transparent 0) 0 0 / 22px 22px,
2810
+ #f8fafc;
2811
+ }
2812
+
2813
+ .appium-vue-flow .vue-flow__pane {
2814
+ cursor: grab;
2815
+ }
2816
+
2817
+ .appium-vue-flow .vue-flow__pane:active {
2818
+ cursor: grabbing;
2819
+ }
2820
+
2821
+ .appium-vue-flow .vue-flow__node {
2822
+ border: 0;
2823
+ background: transparent;
2824
+ box-shadow: none;
2825
+ pointer-events: all;
2826
+ z-index: 2;
2827
+ }
2828
+
2829
+ .appium-vue-flow .vue-flow__edges {
2830
+ z-index: 0;
2831
+ }
2832
+
2833
+ .appium-vue-flow .vue-flow__nodes {
2834
+ z-index: 1;
2835
+ }
2836
+
2837
+ .appium-vue-flow .vue-flow__edge {
2838
+ pointer-events: none;
2839
+ }
2840
+
2841
+ .appium-vue-flow .vue-flow__edge-path,
2842
+ .appium-vue-flow .vue-flow__connection-path {
2843
+ stroke: #d8e4f2;
2844
+ stroke-width: 2.5;
2845
+ stroke-linecap: round;
2846
+ stroke-linejoin: round;
2847
+ fill: none;
2848
+ }
2849
+
2850
+ .appium-flow-graph-node {
2851
+ position: relative;
2852
+ width: max-content;
2853
+ min-width: 0;
2854
+ pointer-events: auto;
2855
+ }
2856
+
2857
+ .appium-flow-graph-node--start {
2858
+ width: 180px;
2859
+ }
2860
+
2861
+ .appium-flow-graph-node--step {
2862
+ width: 340px;
2863
+ }
2864
+
2865
+ .appium-flow-graph-node--insert {
2866
+ width: 34px;
2867
+ height: 34px;
2868
+ }
2869
+
2870
+ .appium-flow-graph-node--branch {
2871
+ width: 34px;
2872
+ height: 26px;
2873
+ }
2874
+
2875
+ .appium-flow-graph-node--split {
2876
+ width: 10px;
2877
+ height: 10px;
2878
+ }
2879
+
2880
+ .appium-flow-handle {
2881
+ width: 1px;
2882
+ height: 1px;
2883
+ border: 0;
2884
+ opacity: 0;
2885
+ pointer-events: none;
2886
+ }
2887
+
2888
+ .appium-flow-handle--top {
2889
+ top: 0;
2890
+ }
2891
+
2892
+ .appium-flow-handle--bottom {
2893
+ bottom: 0;
2894
+ }
2895
+
2896
+ .appium-flow-graph-node--split .appium-flow-handle--top {
2897
+ top: 50%;
2898
+ }
2899
+
2900
+ .appium-flow-graph-node--split .appium-flow-handle--bottom {
2901
+ bottom: 50%;
2902
+ }
2903
+
2904
+ .appium-flow-start-card {
2905
+ display: flex;
2906
+ align-items: center;
2907
+ gap: 10px;
2908
+ width: 180px;
2909
+ height: 62px;
2910
+ padding: 10px 14px;
2911
+ border: 1px solid #e6edf5;
2912
+ border-radius: 8px;
2913
+ background: #fff;
2914
+ box-shadow: 0 10px 24px rgba(15, 23, 42, 0.06);
2915
+ }
2916
+
2917
+ .appium-flow-start-card__icon {
2918
+ display: inline-flex;
2919
+ align-items: center;
2920
+ justify-content: center;
2921
+ width: 28px;
2922
+ height: 28px;
2923
+ border-radius: 8px;
2924
+ background: #e8f1ff;
2925
+ color: #1677ff;
2926
+ font-weight: 700;
2927
+ font-size: 13px;
2928
+ }
2929
+
2930
+ .appium-flow-start-card strong,
2931
+ .appium-flow-start-card small {
2932
+ display: block;
2933
+ }
2934
+
2935
+ .appium-flow-start-card strong {
2936
+ color: #1f2937;
2937
+ font-size: 14px;
2938
+ line-height: 1.2;
2939
+ }
2940
+
2941
+ .appium-flow-start-card small {
2942
+ margin-top: 4px;
2943
+ color: #64748b;
2944
+ font-size: 12px;
2945
+ }
2946
+
2947
+ .appium-flow-branch-pill {
2948
+ position: relative;
2949
+ z-index: 2;
2950
+ display: inline-flex;
2951
+ align-items: center;
2952
+ justify-content: center;
2953
+ width: 34px;
2954
+ height: 26px;
2955
+ border: 0;
2956
+ border-radius: 999px;
2957
+ color: #fff;
2958
+ font-weight: 700;
2959
+ font-size: 14px;
2960
+ line-height: 1;
2961
+ }
2962
+
2963
+ .appium-flow-branch-pill--yes {
2964
+ background: #5cc43a;
2965
+ }
2966
+
2967
+ .appium-flow-branch-pill--no {
2968
+ background: #ff6872;
2969
+ }
2970
+
2971
+ .appium-flow-split-joint {
2972
+ display: block;
2973
+ width: 10px;
2974
+ height: 10px;
2975
+ border: 0;
2976
+ background: transparent;
2977
+ box-sizing: border-box;
2978
+ }
2979
+
2980
+ .appium-flow-insert-node {
2981
+ position: relative;
2982
+ z-index: 2;
2983
+ display: flex;
2984
+ align-items: center;
2985
+ justify-content: center;
2986
+ width: 34px;
2987
+ height: 34px;
2988
+ }
2989
+
2990
+ .appium-flow-insert-node .el-button.is-circle {
2991
+ width: 34px;
2992
+ height: 34px;
2993
+ border: 0;
2994
+ background: #1677ff;
2995
+ box-shadow: 0 8px 18px rgba(22, 119, 255, 0.25);
2996
+ }
2997
+
2998
+ .appium-flow-insert-preview-point {
2999
+ width: 8px;
3000
+ height: 8px;
3001
+ border-radius: 999px;
3002
+ background: #cbd5e1;
3003
+ box-shadow: 0 0 0 4px rgba(203, 213, 225, 0.28);
3004
+ }
3005
+
3006
+ .appium-flow-step-shell {
3007
+ position: relative;
3008
+ width: 340px;
3009
+ }
3010
+
3011
+ .appium-flow-step-card {
3012
+ width: 340px;
3013
+ min-height: 78px;
3014
+ padding: 10px 96px 10px 14px;
3015
+ border: 1px solid #dfe8f3;
3016
+ border-radius: 8px;
3017
+ background: #fff;
3018
+ box-shadow: 0 8px 18px rgba(15, 23, 42, 0.04);
3019
+ color: inherit;
3020
+ text-align: left;
3021
+ cursor: pointer;
3022
+ }
3023
+
3024
+ .appium-flow-step-shell--readonly .appium-flow-step-card {
3025
+ padding-right: 14px;
3026
+ cursor: default;
3027
+ }
3028
+
3029
+ .appium-flow-step-card:hover {
3030
+ border-color: #a7c7f9;
3031
+ box-shadow: 0 10px 24px rgba(39, 111, 236, 0.12);
3032
+ }
3033
+
3034
+ .appium-flow-step-shell--condition .appium-flow-step-card {
3035
+ border-color: #f6a23a;
3036
+ background: #fffbf4;
3037
+ }
3038
+
3039
+ .appium-flow-step-shell--assertion .appium-flow-step-card {
3040
+ border-color: #95d475;
3041
+ background: #f7fff5;
3042
+ }
3043
+
3044
+ .appium-flow-step-shell--selected .appium-flow-step-card {
3045
+ border-color: #2f7df6;
3046
+ background: #eaf2ff;
3047
+ box-shadow: 0 0 0 3px rgba(47, 125, 246, 0.16);
3048
+ }
3049
+
3050
+ .appium-flow-step-shell--copying .appium-flow-step-card {
3051
+ cursor: copy;
3052
+ }
3053
+
3054
+ .appium-flow-step-card__content {
3055
+ display: block;
3056
+ min-width: 0;
3057
+ }
3058
+
3059
+ .appium-flow-step-card__content strong,
3060
+ .appium-flow-step-card__content small,
3061
+ .appium-flow-step-card__content em {
3062
+ display: block;
3063
+ min-width: 0;
3064
+ overflow-wrap: anywhere;
3065
+ word-break: break-word;
3066
+ white-space: normal;
3067
+ }
3068
+
3069
+ .appium-flow-step-card__content strong {
3070
+ color: #1f2937;
3071
+ font-size: 14px;
3072
+ line-height: 1.35;
3073
+ }
3074
+
3075
+ .appium-flow-step-card__content small {
3076
+ margin-top: 3px;
3077
+ color: #64748b;
3078
+ font-size: 12px;
3079
+ line-height: 1.35;
3080
+ }
3081
+
3082
+ .appium-flow-step-card__content em {
3083
+ margin-top: 3px;
3084
+ color: #1f2937;
3085
+ font-style: normal;
3086
+ font-size: 12px;
3087
+ line-height: 1.35;
3088
+ }
3089
+
3090
+ .appium-flow-step-card__actions {
3091
+ position: absolute;
3092
+ top: 11px;
3093
+ right: 10px;
3094
+ display: inline-flex;
3095
+ align-items: center;
3096
+ gap: 2px;
3097
+ }
3098
+
3099
+ .appium-flow-step-card__actions .el-button {
3100
+ width: 22px;
3101
+ height: 22px;
3102
+ margin: 0;
3103
+ color: #64748b;
3104
+ }
3105
+
3106
+ .appium-flow-step-editor {
3107
+ width: 340px;
3108
+ margin-top: 8px;
3109
+ border-color: #dfe8f3;
3110
+ border-radius: 8px;
3111
+ background: #fff;
3112
+ }
3113
+
3114
+ .appium-action-dropdown .el-dropdown-menu {
3115
+ min-width: 180px;
3116
+ }
3117
+
3118
+ .appium-action-dropdown__group {
3119
+ padding: 8px 14px 4px;
3120
+ color: #94a3b8;
3121
+ font-weight: 600;
3122
+ font-size: 12px;
3123
+ }