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> = { 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) }; }