Skip to content
Open
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
143 changes: 47 additions & 96 deletions src/components/Toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -327,17 +327,23 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
const targetTasks = targetInstance.selectedTasks || [];
lastStartCancelledRef.current = false;

const failStart = (reason: string): false => {
const message = `${t('taskList.autoConnect.startFailed')}: ${reason}`;
log.warn(`实例 ${targetInstance.name}: ${message}`);
addLog(targetId, { type: 'error', message });
onPhaseChange?.('idle');
return false;
};

// 检查是否有启用的任务
const enabledTasks = targetTasks.filter((t) => t.enabled);
if (enabledTasks.length === 0) {
log.warn(`实例 ${targetInstance.name} 没有启用的任务`);
return false;
return failStart(t('dashboard.noEnabledTasks'));
}

// 检查是否正在运行
if (targetInstance.isRunning || preActionControlledInstanceIdRef.current === targetId) {
log.warn(`实例 ${targetInstance.name} 正在运行中`);
return false;
return failStart(t('taskList.autoConnect.alreadyRunning'));
}

// 获取控制器和资源配置
Expand Down Expand Up @@ -392,13 +398,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr

// 如果所有启用的任务都被过滤掉了,则无法启动
if (compatibleTasks.length === 0) {
log.warn(`实例 ${targetInstance.name}: ${t('taskList.noCompatibleTasks')}`);
// 向用户显示明确的错误信息
addLog(targetId, {
type: 'error',
message: t('taskList.noCompatibleTasks'),
});
return false;
return failStart(t('taskList.noCompatibleTasks'));
}

const controller = projectInterface?.controller.find((c) => c.name === controllerName);
Expand All @@ -422,11 +422,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
log.warn(
`实例 ${targetInstance.name}: 找不到控制器配置${controllerName ? ` (${controllerName})` : ''}`,
);
addLog(targetId, {
type: 'error',
message: t('errors.controllerNotFound'),
});
return false;
return failStart(t('errors.controllerNotFound'));
}

// 只有依赖 Windows 交互式桌面的实际控制器才受锁屏限制。
Expand All @@ -436,11 +432,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
(await maaService.isWorkstationLocked())
) {
log.warn(`实例 ${targetInstance.name}: 检测到电脑处于锁屏状态,取消启动`);
addLog(targetId, {
type: 'error',
message: t('taskList.autoConnect.workstationLocked'),
});
return false;
return failStart(t('taskList.autoConnect.workstationLocked'));
}
}

Expand All @@ -461,8 +453,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
(shouldUseDummyController && resource);

if (!canStartTask) {
log.warn(`实例 ${targetInstance.name} 无法启动:未连接且没有可用的控制器或资源配置`);
return false;
return failStart(t('taskList.autoConnect.needConfig'));
}

try {
Expand Down Expand Up @@ -780,8 +771,9 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
const devices = await maaService.findAdbDevices();
const matchedDevice = findMatchingAdbDevice(devices, savedDevice);
if (!matchedDevice) {
log.warn(`实例 ${targetInstance.name}: 未找到设备 ${savedDevice.adbDeviceName}`);
return false;
return failStart(
t('taskList.autoConnect.deviceNotFound', { name: savedDevice.adbDeviceName }),
);
}
config = {
type: 'Adb',
Expand All @@ -799,19 +791,19 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
const windows = await maaService.findWin32Windows(classRegex, titleRegex);
const matchedWindow = windows.find((w) => w.window_name === savedDevice.windowName);
if (!matchedWindow) {
log.warn(`实例 ${targetInstance.name}: 未找到窗口 ${savedDevice.windowName}`);
return false;
return failStart(
t('taskList.autoConnect.windowNotFound', { name: savedDevice.windowName }),
);
}
config = buildDesktopWindowControllerConfig(controller, matchedWindow.handle);
deviceName = matchedWindow.window_name || matchedWindow.class_name;
targetType = 'window';
} else if (controllerType === 'WlRoots' && savedDevice.wlrSocketPath) {
const sockets = await maaService.findWlrootsSockets();
if (!sockets.includes(savedDevice.wlrSocketPath)) {
log.warn(
`实例 ${targetInstance.name}: 未找到 WlRoots socket ${savedDevice.wlrSocketPath}`,
return failStart(
t('taskList.autoConnect.deviceNotFound', { name: savedDevice.wlrSocketPath }),
);
return false;
}
config = {
type: 'WlRoots',
Expand All @@ -834,8 +826,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
linux: t('controller.linux'),
});
if (!found) {
log.warn(`实例 ${targetInstance.name}: 未找到 Linux 控制器所需设备`);
return false;
return failStart(t('taskList.autoConnect.noDeviceFound'));
}
config = found.config;
deviceName = found.deviceName;
Expand All @@ -849,12 +840,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
if (controllerType === 'Adb') {
const devices = await maaService.findAdbDevices();
if (devices.length === 0) {
log.warn(`实例 ${targetInstance.name}: 未搜索到任何 ADB 设备`);
addLog(targetId, {
type: 'error',
message: t('taskList.autoConnect.noDeviceFound'),
});
return false;
return failStart(t('taskList.autoConnect.noDeviceFound'));
}
const firstDevice = devices[0];
log.info(`实例 ${targetInstance.name}: 自动选择设备: ${firstDevice.name}`);
Expand All @@ -880,12 +866,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
const { classRegex, titleRegex } = getDesktopWindowFilters(controller);
const windows = await maaService.findWin32Windows(classRegex, titleRegex);
if (windows.length === 0) {
log.warn(`实例 ${targetInstance.name}: 未搜索到任何窗口`);
addLog(targetId, {
type: 'error',
message: t('taskList.autoConnect.noWindowFound'),
});
return false;
return failStart(t('taskList.autoConnect.noWindowFound'));
}
const firstWindow = windows[0];
log.info(`实例 ${targetInstance.name}: 自动选择窗口: ${firstWindow.window_name}`);
Expand All @@ -902,12 +883,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
} else if (controllerType === 'WlRoots') {
const sockets = await maaService.findWlrootsSockets();
if (sockets.length === 0) {
log.warn(`实例 ${targetInstance.name}: 未搜索到任何 WlRoots socket`);
addLog(targetId, {
type: 'error',
message: t('taskList.autoConnect.noDeviceFound'),
});
return false;
return failStart(t('taskList.autoConnect.noDeviceFound'));
}
const firstSocket = sockets[0];
log.info(`实例 ${targetInstance.name}: 自动选择 WlRoots socket: ${firstSocket}`);
Expand All @@ -926,24 +902,14 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
targetType = 'device';
} else if (controllerType === 'PlayCover') {
// PlayCover 没有搜索功能,无法自动连接
log.warn(`实例 ${targetInstance.name}: PlayCover 控制器需要手动配置地址`);
addLog(targetId, {
type: 'error',
message: t('taskList.autoConnect.needConfig'),
});
return false;
return failStart(t('taskList.autoConnect.needConfig'));
} else if (controllerType === 'Linux') {
const found = await discoverLinuxControllerConfig(controller, undefined, {
portal: t('controller.portal'),
linux: t('controller.linux'),
});
if (!found) {
log.warn(`实例 ${targetInstance.name}: 未搜索到 Linux 控制器所需设备`);
addLog(targetId, {
type: 'error',
message: t('taskList.autoConnect.noDeviceFound'),
});
return false;
return failStart(t('taskList.autoConnect.noDeviceFound'));
}
config = found.config;
deviceName = found.deviceName;
Expand All @@ -959,8 +925,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
}

if (!shouldUseDummyController && !config) {
log.warn(`实例 ${targetInstance.name}: 无法构建控制器配置`);
return false;
return failStart(t('taskList.autoConnect.needConfig'));
}

if (shouldUseDummyController) {
Expand All @@ -974,8 +939,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
}

if (!config) {
log.warn(`实例 ${targetInstance.name}: 无法构建控制器配置`);
return false;
return failStart(t('taskList.autoConnect.needConfig'));
}

onPhaseChange?.('connecting');
Expand Down Expand Up @@ -1116,8 +1080,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
}

if (!connectResult) {
log.warn(`实例 ${targetInstance.name}: 连接设备失败(已重试 ${maxRetries - 1} 次)`);
return false;
return failStart(t('taskList.autoConnect.connectFailed'));
}

if (shouldDelayAfterAdbConnected) {
Expand Down Expand Up @@ -1170,8 +1133,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
}

if (!loadResult) {
log.warn(`实例 ${targetInstance.name}: 资源加载失败`);
return false;
return failStart(t('taskList.autoConnect.resourceFailed'));
}
}

Expand Down Expand Up @@ -1208,8 +1170,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
}

if (runnableTasks.length === 0) {
log.warn(`实例 ${targetInstance.name}: 没有可执行的任务`);
return false;
return failStart(t('taskList.autoConnect.noRunnableTasks'));
}

const { leading, middle, trailing } = splitTasksIntoThreeSegments(runnableTasks);
Expand Down Expand Up @@ -1352,8 +1313,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
if (hasTrailingBatch && primaryTaskIds.length > 0) {
const primaryResult = await maaService.waitForTasks(targetId, primaryTaskIds);
if (!primaryResult.allDone || primaryResult.stopped) {
log.warn(`实例 ${targetInstance.name}: 前段任务未正常结束,跳过收尾特殊任务`);
return false;
return failStart(t('taskList.autoConnect.primaryTasksIncomplete'));
}
const trailingTaskIds = await runTaskBatch(trailing, false, '收尾', true);
startedTaskIds.push(...trailingTaskIds);
Expand Down Expand Up @@ -1612,6 +1572,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
const detail = (evt as CustomEvent | undefined)?.detail as
| { source?: string; combo?: string; onSettled?: (started: boolean) => void }
| undefined;
const isHotkey = detail?.source === 'hotkey' || detail?.source === 'global-hotkey';
let settled = false;
const notifySettled = (started: boolean) => {
if (settled) return;
Expand All @@ -1630,24 +1591,14 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
}

const combo = detail?.combo || '';
addLog(currentInstance.id, {
type: 'info',
message: t('logs.messages.hotkeyDetected', {
combo,
action: t('logs.messages.hotkeyActionStart'),
}),
});

if (
currentInstance.isRunning ||
preActionControlledInstanceIdRef.current === currentInstance.id
) {
if (isHotkey) {
addLog(currentInstance.id, {
type: 'error',
message: t('logs.messages.hotkeyStartFailed'),
type: 'info',
message: t('logs.messages.hotkeyDetected', {
combo,
action: t('logs.messages.hotkeyActionStart'),
}),
});
notifySettled(false);
return;
}

// 直接使用从 store 获取的最新 instance,避免闭包捕获旧的 selectedTasks
Expand All @@ -1656,12 +1607,12 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr
const success = await startTasksForInstance(currentInstance, {
onPhaseChange: setAutoConnectPhase,
});
addLog(currentInstance.id, {
type: success ? 'success' : 'error',
message: success
? t('logs.messages.hotkeyStartSuccess')
: t('logs.messages.hotkeyStartFailed'),
});
if (isHotkey && success) {
addLog(currentInstance.id, {
type: 'success',
message: t('logs.messages.hotkeyStartSuccess'),
});
}
notifySettled(success);
} finally {
hotkeyStartingRef.current = false;
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/en-US.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,9 @@ export default {
'No window was previously selected. Automatically matched "{{name}}". To change, select manually in Connection Settings — your choice will be remembered next time.',
resourceFailed: 'Resource loading failed',
startFailed: 'Failed to start tasks',
alreadyRunning: 'Tasks are already running or a pre-action is still in progress',
noRunnableTasks: 'No runnable tasks; check the task definitions and entry configuration',
primaryTasksIncomplete: 'Primary tasks did not finish normally; trailing tasks were skipped',
workstationLocked: 'The computer is locked. Please unlock it before running tasks.',
agentStartParams: 'Agent #{{index}} start params: {{cmd}} (cwd: {{cwd}})',
agentSpawnHintFileNotFound:
Expand Down Expand Up @@ -522,7 +525,6 @@ export default {
hotkeyActionStart: 'Start tasks',
hotkeyActionStop: 'Stop tasks',
hotkeyStartSuccess: 'Started tasks via hotkey:',
hotkeyStartFailed: 'Failed to start tasks via hotkey',
hotkeyStopSuccess: 'Stopped tasks via hotkey',
hotkeyStopFailed: 'Failed to stop tasks via hotkey',
},
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/ja-JP.ts
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,9 @@ export default {
'ウィンドウが未設定のため、「{{name}}」を自動的に選択しました。変更する場合は接続設定で手動選択してください。次回以降は選択内容が保存されます。',
resourceFailed: 'リソースの読み込みに失敗しました',
startFailed: 'タスクの開始に失敗しました',
alreadyRunning: 'タスクは既に実行中か、前処理を実行しています',
noRunnableTasks: '実行可能なタスクがありません。タスク定義とエントリ設定を確認してください',
primaryTasksIncomplete: '前段タスクが正常に終了しなかったため、後段タスクをスキップしました',
workstationLocked:
'パソコンがロック画面の状態です。ロックを解除してからタスクを実行してください',
agentStartParams: 'Agent #{{index}} 起動パラメータ: {{cmd}} (作業ディレクトリ: {{cwd}})',
Expand Down Expand Up @@ -520,7 +523,6 @@ export default {
hotkeyActionStart: 'タスク開始',
hotkeyActionStop: 'タスク停止',
hotkeyStartSuccess: 'ショートカットキーでタスクを開始しました:',
hotkeyStartFailed: 'ショートカットキーでタスクを開始できませんでした',
hotkeyStopSuccess: 'ショートカットキーでタスクを停止しました',
hotkeyStopFailed: 'ショートカットキーでタスクを停止できませんでした',
},
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/ko-KR.ts
Original file line number Diff line number Diff line change
Expand Up @@ -235,6 +235,9 @@ export default {
'창이 설정되지 않아 「{{name}}」을(를) 자동으로 선택했습니다. 변경하려면 연결 설정에서 수동으로 선택하세요. 다음 번에는 선택 내용이 저장됩니다.',
resourceFailed: '리소스 로딩에 실패했습니다',
startFailed: '작업 시작에 실패했습니다',
alreadyRunning: '작업이 이미 실행 중이거나 사전 작업을 실행하고 있습니다',
noRunnableTasks: '실행 가능한 작업이 없습니다. 작업 정의와 진입점 설정을 확인하세요',
primaryTasksIncomplete: '앞쪽 작업이 정상 종료되지 않아 마무리 작업을 건너뛰었습니다',
workstationLocked: '컴퓨터가 잠금 화면 상태입니다. 잠금을 해제한 후 작업을 실행하세요',
agentStartParams: 'Agent #{{index}} 시작 파라미터: {{cmd}} (작업 디렉토리: {{cwd}})',
agentSpawnHintFileNotFound:
Expand Down Expand Up @@ -511,7 +514,6 @@ export default {
hotkeyActionStart: '작업 시작',
hotkeyActionStop: '작업 중지',
hotkeyStartSuccess: '단축키로 작업을 시작했습니다:',
hotkeyStartFailed: '단축키로 작업을 시작하지 못했습니다',
hotkeyStopSuccess: '단축키로 작업을 중지했습니다',
hotkeyStopFailed: '단축키로 작업을 중지하지 못했습니다',
},
Expand Down
4 changes: 3 additions & 1 deletion src/i18n/locales/zh-CN.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,9 @@ export default {
'尚未手动选择过窗口,已自动匹配到「{{name}}」。如需更换,请在连接设置中手动选择,下次将记住您的选择。',
resourceFailed: '资源加载失败',
startFailed: '任务启动失败',
alreadyRunning: '任务已在运行或正在执行前置动作',
noRunnableTasks: '没有可执行的任务,请检查任务定义和入口配置',
primaryTasksIncomplete: '前段任务未正常结束,已跳过收尾特殊任务',
workstationLocked: '检测到电脑处于锁屏状态,请先解锁后再运行任务',
agentStartParams: 'Agent #{{index}} 启动参数: {{cmd}} (工作目录: {{cwd}})',
agentSpawnHintFileNotFound: '请先检查 Agent 是否被杀软拦截,确认无误后重新覆盖安装。',
Expand Down Expand Up @@ -505,7 +508,6 @@ export default {
hotkeyActionStart: '开始任务',
hotkeyActionStop: '停止任务',
hotkeyStartSuccess: '已通过快捷键开始任务:',
hotkeyStartFailed: '未能通过快捷键开始任务',
hotkeyStopSuccess: '已通过快捷键停止任务',
hotkeyStopFailed: '未能通过快捷键停止任务',
},
Expand Down
Loading