Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 3 additions & 10 deletions docs/add-special-task.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,9 +168,7 @@ const MXU_EXAMPLE_OPTION: InputOption = {
pipeline_type: 'int', // 'string' | 'int' | 'bool'
verify: '^[1-9]\\d*$', // 可选,输入校验正则
pattern_msg: 'specialTask.example.inputError', // 校验失败提示 i18n key
// MXU 扩展字段(可选):
input_type: 'file', // 'text'(默认) | 'file'(渲染文件选择器)
placeholder: 'specialTask.example.placeholder', // 占位提示文本 i18n key
input_type: 'file', // PI v2.10.5: 'text' | 'file' | 'directory'
},
],
pipeline_override: {
Expand All @@ -183,12 +181,7 @@ const MXU_EXAMPLE_OPTION: InputOption = {
};
```

**MXU 扩展字段说明**(`InputItem` 上的可选字段,不影响 PI V2 协议):

| 字段 | 类型 | 说明 |
| ------ | ------ | ------ |
| `input_type` | `'text' \| 'file' \| 'time'` | 控制 UI 渲染:`'file'` 渲染带浏览按钮的文件选择器;`'time'` 渲染 HH:MM 时间选择器 |
| `placeholder` | `string` | 输入框占位提示文本的 i18n key,通过 `t()` 翻译。若未设置则 fallback 到 `default` |
选项定义使用 PI V2 标准字段。内置任务如需时间选择器或占位提示,请在 `src/types/specialTasks.ts` 的 `MXU_INPUT_UI` 中配置 `timePicker` / `placeholderKey`;这些是客户端内部界面配置,不从 `interface.json` 读取。

**switch 类型**(开关):

Expand Down Expand Up @@ -316,6 +309,6 @@ specialTask: {
- **任务添加/删除**:`addMxuSpecialTask` 根据注册表初始化选项值
- **UI 渲染**:`TaskItem` / `AddTaskPanel` / `OptionEditor` 检测 `__MXU_` 前缀自动适配
- **选项定义查找**:`OptionEditor` 通过 `findMxuOptionByKey()` 遍历所有特殊任务反查选项定义,无需选项 key 与任务名严格对应
- **i18n 翻译**:选项的 `label`、`description`、`placeholder`、`pattern_msg` 等字段均通过 `t()` 翻译
- **i18n 翻译**:选项的 `label`、`description`、`pattern_msg` 等字段均通过 `t()` 翻译
- **配置持久化**:`appStore` 中的配置恢复逻辑通过 `isMxuSpecialTask` 正确保留选项值
- **任务校验**:配置加载时通过 `MXU_SPECIAL_TASKS` key 集合判断任务有效性
2 changes: 1 addition & 1 deletion src-tauri/src/commands/app_config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -766,7 +766,7 @@ fn merge_imported(interface: &mut serde_json::Value, imported: &serde_json::Valu
}
}

// MXU 扩展:合并 setting 数组(追加到末尾,保持导入顺序)
// PI v2.8.0: 合并 setting 数组(追加到末尾,保持导入顺序)
if let Some(settings) = imported.get("setting").and_then(|v| v.as_array()) {
if let Some(arr) = interface.get_mut("setting").and_then(|v| v.as_array_mut()) {
arr.extend(settings.iter().cloned());
Expand Down
5 changes: 2 additions & 3 deletions src-tauri/src/commands/maa_agent.rs
Original file line number Diff line number Diff line change
Expand Up @@ -525,9 +525,8 @@ async fn start_single_agent(
);
}

// 设置连接超时
let timeout = agent.timeout.unwrap_or(-1);
if let Err(e) = client.set_timeout(timeout) {
// Agent 连接沿用无限等待,不从 PI 读取额外的超时字段。
if let Err(e) = client.set_timeout(-1) {
warn!("Failed to set timeout for agent #{}: {}", agent_index, e);
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
Resolved
}

Expand Down
21 changes: 0 additions & 21 deletions src-tauri/src/commands/maa_core.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1024,27 +1024,6 @@ pub async fn connect_controller_impl(
input_method,
..
} => create_macos_controller(*handle, *screencap_method, *input_method)?,
ControllerConfig::WlRoots {
wlr_socket_path,
use_win32_vk_code,
..
} => {
// 迁移:WlRoots 已废弃,内部改用 Linux 控制器(Wlr 截图 + Wlr 输入)
ensure_linux_maafw_version()?;
let linux_config = build_linux_controller_config(
maa_framework::common::LinuxScreencapMethod::WLR.bits(),
maa_framework::common::LinuxInputMethod::WLR.bits(),
Some(wlr_socket_path.as_str()),
None,
None,
None,
None,
None,
None,
Some(*use_win32_vk_code),
);
Controller::new_linux(&linux_config).map_err(|e| e.to_string())?
}
ControllerConfig::PlayCover { address, uuid, .. } => {
let uuid_str = uuid.as_deref().unwrap_or("");
Controller::new_playcover(address, uuid_str).map_err(|e| e.to_string())?
Expand Down
22 changes: 0 additions & 22 deletions src-tauri/src/commands/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,6 @@ pub enum ControllerConfig {
#[serde(default)]
display_raw: Option<bool>,
},
WlRoots {
wlr_socket_path: String,
#[serde(default)]
use_win32_vk_code: bool,
#[serde(default)]
display_short_side: Option<i32>,
#[serde(default)]
display_long_side: Option<i32>,
#[serde(default)]
display_expand: Option<[i32; 2]>,
#[serde(default)]
display_raw: Option<bool>,
},
Gamepad {
handle: u64,
#[serde(default)]
Expand Down Expand Up @@ -212,13 +199,6 @@ impl ControllerConfig {
display_raw,
..
}
| Self::WlRoots {
display_short_side,
display_long_side,
display_expand,
display_raw,
..
}
| Self::Gamepad {
display_short_side,
display_long_side,
Expand Down Expand Up @@ -861,8 +841,6 @@ pub struct AgentConfig {
pub child_exec: String,
pub child_args: Option<Vec<String>>,
pub identifier: Option<String>,
/// 连接超时时间(毫秒),-1 表示无限等待
pub timeout: Option<i64>,
}

/// 任务配置
Expand Down
152 changes: 3 additions & 149 deletions src/components/ConnectionPanel.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -350,10 +350,7 @@ export function ConnectionPanel() {

// 判断是否需要搜索设备(PlayCover 不需要搜索)
const needsDeviceSearch =
controllerType === 'Adb' ||
isDesktopWindowController ||
controllerType === 'WlRoots' ||
controllerType === 'Linux';
controllerType === 'Adb' || isDesktopWindowController || controllerType === 'Linux';

// 记录上一次的控制器名称,用于检测切换
const prevControllerNameRef = useRef<string | undefined>(currentControllerName);
Expand Down Expand Up @@ -387,7 +384,6 @@ export function ConnectionPanel() {
savedDevice &&
((controllerType === 'Adb' && savedDevice.adbDeviceName) ||
(isDesktopWindowController && savedDevice.windowName) ||
(controllerType === 'WlRoots' && savedDevice.wlrSocketPath) ||
(controllerType === 'Linux' &&
((savedDevice.gamescopeDisplayNo !== undefined &&
(linuxNeeds?.needGamescopeNode || linuxNeeds?.needEisSocket)) ||
Expand Down Expand Up @@ -498,36 +494,6 @@ export function ConnectionPanel() {
// 有保存窗口但匹配失败,显示下拉框让用户选择
setShowDeviceDropdown(true);
}
} else if (controllerType === 'WlRoots') {
const sockets = await maaService.findWlrootsSockets();
setCachedWlrootsSockets(sockets);

let autoSelected: string | null = null;
if (savedDevice?.wlrSocketPath) {
const matched = sockets.filter((s) => s === savedDevice.wlrSocketPath);
if (matched.length === 1) {
autoSelected = matched[0];
}
} else if (sockets.length > 0) {
// 没有保存连接时自动选择第一个
autoSelected = sockets[0];
// 给出首次自动匹配提示
if (instanceId) {
addLog(instanceId, {
type: 'info',
message: t('taskList.autoConnect.autoSelectedDevice', {
name: autoSelected,
}),
});
}
}

if (autoSelected) {
handleSelectWlrootsSocket(autoSelected);
} else if (sockets.length > 0) {
// 有保存连接但匹配失败,显示下拉框让用户选择
setShowDeviceDropdown(true);
}
} else if (controllerType === 'Linux' && linuxNeeds) {
// 发现所需的全部设备(按当前配置的截图/输入方法)
let wlrSockets: string[] = [];
Expand Down Expand Up @@ -667,15 +633,6 @@ export function ConnectionPanel() {
config = desktopConfig;
deviceName = selectedWindow.window_name || selectedWindow.class_name;
targetType = 'window';
} else if (controllerType === 'WlRoots' && selectedWlrootsSocket) {
config = {
type: 'WlRoots',
...getControllerDisplayOptions(currentController),
wlr_socket_path: selectedWlrootsSocket,
use_win32_vk_code: currentController?.wlroots?.use_win32_vk_code ?? false,
};
deviceName = selectedWlrootsSocket;
targetType = 'device';
} else if (controllerType === 'PlayCover') {
// 保存 PlayCover 地址到实例配置
setInstanceSavedDevice(instanceId, { playcoverAddress });
Expand Down Expand Up @@ -813,7 +770,6 @@ export function ConnectionPanel() {
case 'Adb':
return <Smartphone className="w-4 h-4" />;
case 'Win32':
case 'WlRoots':
case 'Linux':
return <Monitor className="w-4 h-4" />;
case 'MacOS':
Expand Down Expand Up @@ -850,16 +806,7 @@ export function ConnectionPanel() {
}
return t('controller.selectWindow');
}
if (controllerType === 'WlRoots') {
if (selectedWlrootsSocket) {
return selectedWlrootsSocket;
}
// 缓存为空但有历史窗口名称
if (savedDevice?.wlrSocketPath) {
return savedDevice.wlrSocketPath;
}
return t('controller.selectDevice');
}

if (controllerType === 'Linux') {
const parts: string[] = [];
if (linuxNeeds?.needGamescopeNode || linuxNeeds?.needEisSocket) {
Expand Down Expand Up @@ -989,49 +936,6 @@ export function ConnectionPanel() {
}
};

// 选择 WlRoots socket 并自动连接(如已连接会先断开旧连接)
const handleSelectWlrootsSocket = async (socketPath: string) => {
setSelectedWlrootsSocket(socketPath);
setShowDeviceDropdown(false);

// 保存 socket path 到实例配置
setInstanceSavedDevice(instanceId, { wlrSocketPath: socketPath });

// 自动连接
setIsConnecting(true);
setDeviceError(null);

try {
// 先断开旧连接
if (isConnected) {
await maaService.destroyInstance(instanceId).catch(() => {});
setIsConnected(false);
setInstanceResourceLoaded(instanceId, false);
}

const initialized = await ensureMaaInitialized();
if (!initialized) {
throw new Error(t('maa.initFailed'));
}

await maaService.createInstance(instanceId).catch(() => {});

const config: ControllerConfig = {
type: 'WlRoots',
...getControllerDisplayOptions(currentController),
wlr_socket_path: socketPath,
use_win32_vk_code: currentController?.wlroots?.use_win32_vk_code ?? false,
};

await connectControllerInternal(config, socketPath, 'device');
} catch (err) {
setDeviceError(controllerErrorMessage(err));
setIsConnected(false);
setInstanceConnectionStatus(instanceId, 'Disconnected');
setIsConnecting(false);
}
};

// === Linux 控制器连接逻辑 ===

// Linux:判断给定选择是否满足当前配置所需的全部设备
Expand Down Expand Up @@ -1204,27 +1108,6 @@ export function ConnectionPanel() {
if (windows.length > 0) {
setShowDeviceDropdown(true);
}
} else if (controllerType === 'WlRoots') {
const sockets = await maaService.findWlrootsSockets();
setCachedWlrootsSockets(sockets);

// 尝试匹配保存的窗口名称
if (savedDevice?.wlrSocketPath) {
const matched = sockets.find((s) => s === savedDevice.wlrSocketPath);
if (matched) {
// 找到匹配的,自动连接
setIsSearching(false);
handleSelectWlrootsSocket(matched);
return;
}
// 没找到匹配的,显示错误提示
setDeviceError(t('controller.savedDeviceNotFound'));
}

// 显示搜索结果供用户选择
if (sockets.length > 0) {
setShowDeviceDropdown(true);
}
} else if (controllerType === 'Linux' && linuxNeeds) {
// Linux:复用 handleSearch 的发现+匹配+自动连接逻辑
setIsSearching(false);
Expand Down Expand Up @@ -1300,35 +1183,7 @@ export function ConnectionPanel() {

return [];
}
if (controllerType === 'WlRoots') {
// 如果缓存中有窗口,使用缓存列表
if (cachedWlrootsSockets.length > 0) {
return cachedWlrootsSockets.map((socket) => ({
id: `wlr:${socket}`,
name: socket,
description: socket,
selected: selectedWlrootsSocket === socket,
onClick: () => handleSelectWlrootsSocket(socket),
isHistorical: false,
}));
}

// 缓存为空但有历史窗口名称,显示历史窗口条目
if (savedDevice?.wlrSocketPath) {
return [
{
id: 'historical-wlroots-socket',
name: savedDevice.wlrSocketPath,
description: t('controller.lastSelected'),
selected: true,
onClick: handleSearchAndConnectHistorical,
isHistorical: true,
},
];
}

return [];
}
if (controllerType === 'Linux' && linuxNeeds) {
// 展示所有已发现的选择项(含当前选中项),保证下拉不为空
const instanceItems =
Expand Down Expand Up @@ -1372,7 +1227,7 @@ export function ConnectionPanel() {
const canConnect = () => {
if (controllerType === 'Adb') return !!selectedAdbDevice;
if (isDesktopWindowController) return !!selectedWindow;
if (controllerType === 'WlRoots') return !!selectedWlrootsSocket;

if (controllerType === 'Linux') {
return isLinuxReadyWith(selectedGamescopeInstance, selectedWlrootsSocket);
}
Expand Down Expand Up @@ -1446,7 +1301,6 @@ export function ConnectionPanel() {
return (
s.adbDeviceName ||
s.windowName ||
(controllerType === 'WlRoots' && s.wlrSocketPath) ||
(controllerType === 'Linux' &&
((s.gamescopeDisplayNo !== undefined &&
(linuxNeeds?.needGamescopeNode || linuxNeeds?.needEisSocket)) ||
Expand Down
Loading
Loading