103 lines
4.9 KiB
TypeScript
103 lines
4.9 KiB
TypeScript
import { ActionParam } from '../core/models/stream.model';
|
|
|
|
// Display-only translations. Protocol names and original trace payloads stay intact.
|
|
const ACTION_LABELS = new Map(Object.entries({
|
|
pinch: '双指缩放/旋转',
|
|
touch_gesture: '连续多指手势',
|
|
read_control: '读取原生控件',
|
|
control_action: '操作原生控件',
|
|
wait_until: '等待控件条件',
|
|
scroll_to: '滚动查找控件',
|
|
action_sequence: '动作序列'
|
|
}));
|
|
|
|
const CONTROL_LABELS = new Map(Object.entries({
|
|
click: '原生点击', long_click: '原生长按', focus: '聚焦控件',
|
|
show_on_screen: '显示控件', set_progress: '设置控件值',
|
|
scroll_forward: '向前滚动', scroll_backward: '向后滚动',
|
|
scroll_up: '向上滚动', scroll_down: '向下滚动',
|
|
scroll_left: '向左滚动', scroll_right: '向右滚动',
|
|
set_selection: '设置文本选区', replace_selection: '替换选区文本',
|
|
set_text: '设置文本', ime_enter: '提交输入法操作'
|
|
}));
|
|
|
|
const STEP_LABELS = new Map(Object.entries({
|
|
click: '点击元素', long_press: '长按元素', drag: '拖动屏幕', wait: '等待'
|
|
}));
|
|
|
|
const PARAM_LABELS = new Map(Object.entries({
|
|
center: '中心点', start_span: '起始指间距', end_span: '结束指间距',
|
|
angle_degrees: '起始角度', rotation_degrees: '旋转角度',
|
|
duration_ms: '持续时间(毫秒)', coordinate_space: '坐标空间',
|
|
frames: '触摸轨迹', at_ms: '时间点(毫秒)', points: '触点',
|
|
selector: '控件选择器', container: '滚动容器', target: '目标控件',
|
|
resource_id: '资源 ID', text: '文本', content_desc: '控件描述',
|
|
class_name: '控件类名', package_name: '应用包名', bounds: '控件边界',
|
|
expected_token: '预期控件标识', value: '目标值',
|
|
selection_start: '选区起点', selection_end: '选区终点',
|
|
state: '等待条件', expected_text: '预期文本', expected_value: '预期值',
|
|
tolerance: '允许误差', timeout_ms: '超时(毫秒)',
|
|
poll_interval_ms: '检查间隔(毫秒)', stable_ms: '稳定时长(毫秒)',
|
|
direction: '滚动方向', max_scrolls: '最多滚动次数', settle_ms: '停稳时长(毫秒)',
|
|
require_visible: '可见条件', unless_visible: '跳过条件',
|
|
steps: '动作步骤', action: '操作', args: '参数',
|
|
time_in_ms: '等待时长(毫秒)', target_description: '目标说明',
|
|
execution: '执行状态', verification: '验证状态'
|
|
}));
|
|
|
|
const ENUM_LABELS: Record<string, Map<string, string>> = {
|
|
coordinate_space: new Map(Object.entries({ normalized: '归一化坐标', pixel: '像素坐标' })),
|
|
state: new Map(Object.entries({
|
|
visible: '可见', absent: '消失', enabled: '可用', disabled: '禁用',
|
|
checked: '已勾选', unchecked: '未勾选', selected: '已选中',
|
|
unselected: '未选中', text: '文本匹配', value: '数值匹配', stable: '持续稳定'
|
|
})),
|
|
direction: new Map(Object.entries({
|
|
forward: '向前', backward: '向后', up: '向上', down: '向下', left: '向左', right: '向右'
|
|
})),
|
|
execution: new Map(Object.entries({
|
|
not_started: '未开始', observed: '已观测', completed: '已完成',
|
|
partial: '部分完成', cancelled: '已取消', unknown: '结果未知'
|
|
})),
|
|
verification: new Map(Object.entries({ pending_observation: '等待观测' }))
|
|
};
|
|
|
|
export function getNativeActionTitle(name: string, args: any = {}): string | null {
|
|
const clean = String(name).toLowerCase().replace(/^_?(?:self\.)?exec_/, '')
|
|
.replace(/^action_sequence\./, '');
|
|
if (clean === 'control_action') {
|
|
const request = getNativeActionArgs(args);
|
|
return CONTROL_LABELS.get(request.action) ?? ACTION_LABELS.get(clean)!;
|
|
}
|
|
return ACTION_LABELS.get(clean) ?? null;
|
|
}
|
|
|
|
export function getNativeActionArgs(args: any): any {
|
|
return args?.action === 'control_action' && args.args ? args.args : (args ?? {});
|
|
}
|
|
|
|
function localizedValue(value: any, key: string, controlAction = false): any {
|
|
if (Array.isArray(value)) return value.map(item => localizedValue(item, key, controlAction));
|
|
if (value && typeof value === 'object') {
|
|
return Object.fromEntries(Object.entries(value).map(([field, item]) => [
|
|
PARAM_LABELS.get(field) ?? field,
|
|
localizedValue(item, field, field === 'action' ? controlAction : field === 'args' && value.action === 'control_action')
|
|
]));
|
|
}
|
|
if (typeof value === 'string') {
|
|
if (key === 'action') {
|
|
return ACTION_LABELS.get(value) ?? (controlAction ? CONTROL_LABELS : STEP_LABELS).get(value) ?? value;
|
|
}
|
|
if (Object.hasOwn(ENUM_LABELS, key)) return ENUM_LABELS[key].get(value) ?? value;
|
|
}
|
|
return value;
|
|
}
|
|
|
|
export function formatNativeActionParam(key: string, value: any): ActionParam {
|
|
const localized = localizedValue(value, key);
|
|
return {
|
|
key: PARAM_LABELS.get(key) ?? key.replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()),
|
|
value: typeof localized === 'object' ? JSON.stringify(localized) : String(localized)
|
|
};
|
|
}
|