From 79c08af2009fb30bdeb64ce5dd28c567b9bb8afd Mon Sep 17 00:00:00 2001 From: HarcoChen Date: Fri, 21 Aug 2026 20:16:08 +0800 Subject: [PATCH] =?UTF-8?q?feat(toolbar):=20=E5=A2=9E=E5=BC=BA=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=90=AF=E5=8A=A8=E5=A4=B1=E8=B4=A5=E6=98=BE=E7=A4=BA?= =?UTF-8?q?=20=E5=85=88=E5=89=8D=E9=83=A8=E5=88=86=E5=88=86=E6=94=AF?= =?UTF-8?q?=E7=9B=B4=E6=8E=A5return=20false=E5=AF=BC=E8=87=B4=E5=BF=AB?= =?UTF-8?q?=E6=8D=B7=E9=94=AE=E5=A4=84=E7=90=86=E5=85=9C=E5=BA=95=EF=BC=8C?= =?UTF-8?q?web=E5=8C=BA=E5=8F=AA=E6=89=93=E5=8D=B0=E5=BF=AB=E6=8D=B7?= =?UTF-8?q?=E9=94=AE=E5=90=AF=E5=8A=A8=E4=BB=BB=E5=8A=A1=E5=A4=B1=E8=B4=A5?= =?UTF-8?q?=EF=BC=8C=E9=80=A0=E6=88=90=E8=AF=AF=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components/Toolbar.tsx | 132 +++++++++++++------------------------ src/i18n/locales/en-US.ts | 4 +- src/i18n/locales/ja-JP.ts | 4 +- src/i18n/locales/ko-KR.ts | 4 +- src/i18n/locales/zh-CN.ts | 4 +- src/i18n/locales/zh-TW.ts | 4 +- 6 files changed, 60 insertions(+), 92 deletions(-) diff --git a/src/components/Toolbar.tsx b/src/components/Toolbar.tsx index 34724ba3..731deec6 100644 --- a/src/components/Toolbar.tsx +++ b/src/components/Toolbar.tsx @@ -249,17 +249,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')); } // 获取控制器和资源配置 @@ -314,13 +320,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); @@ -344,11 +344,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 交互式桌面的实际控制器才受锁屏限制。 @@ -358,11 +354,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')); } } @@ -383,8 +375,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 { @@ -702,8 +693,9 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr const devices = await maaService.findAdbDevices(); const matchedDevice = devices.find((d) => d.name === savedDevice.adbDeviceName); if (!matchedDevice) { - log.warn(`实例 ${targetInstance.name}: 未找到设备 ${savedDevice.adbDeviceName}`); - return false; + return failStart( + t('taskList.autoConnect.deviceNotFound', { name: savedDevice.adbDeviceName }), + ); } config = { type: 'Adb', @@ -721,8 +713,9 @@ 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; @@ -730,10 +723,9 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr } 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', @@ -759,12 +751,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}`); @@ -790,12 +777,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}`); @@ -812,12 +794,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}`); @@ -836,18 +813,12 @@ 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')); } } if (!shouldUseDummyController && !config) { - log.warn(`实例 ${targetInstance.name}: 无法构建控制器配置`); - return false; + return failStart(t('taskList.autoConnect.needConfig')); } if (shouldUseDummyController) { @@ -861,8 +832,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr } if (!config) { - log.warn(`实例 ${targetInstance.name}: 无法构建控制器配置`); - return false; + return failStart(t('taskList.autoConnect.needConfig')); } onPhaseChange?.('connecting'); @@ -1003,8 +973,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) { @@ -1057,8 +1026,7 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr } if (!loadResult) { - log.warn(`实例 ${targetInstance.name}: 资源加载失败`); - return false; + return failStart(t('taskList.autoConnect.resourceFailed')); } } @@ -1095,8 +1063,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); @@ -1234,8 +1201,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); @@ -1498,24 +1464,16 @@ export function Toolbar({ showAddPanel, onToggleAddPanel, className }: ToolbarPr const detail = (evt as CustomEvent | undefined)?.detail as | { source?: string; combo?: string } | undefined; + const isHotkey = detail?.source === 'hotkey' || detail?.source === 'global-hotkey'; 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'), + }), }); - return; } // 直接使用从 store 获取的最新 instance,避免闭包捕获旧的 selectedTasks @@ -1524,12 +1482,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'), + }); + } } finally { hotkeyStartingRef.current = false; } diff --git a/src/i18n/locales/en-US.ts b/src/i18n/locales/en-US.ts index afc47c04..da43a8ea 100644 --- a/src/i18n/locales/en-US.ts +++ b/src/i18n/locales/en-US.ts @@ -242,6 +242,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: @@ -515,7 +518,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', }, diff --git a/src/i18n/locales/ja-JP.ts b/src/i18n/locales/ja-JP.ts index d3a6ce17..2a2a5310 100644 --- a/src/i18n/locales/ja-JP.ts +++ b/src/i18n/locales/ja-JP.ts @@ -236,6 +236,9 @@ export default { 'ウィンドウが未設定のため、「{{name}}」を自動的に選択しました。変更する場合は接続設定で手動選択してください。次回以降は選択内容が保存されます。', resourceFailed: 'リソースの読み込みに失敗しました', startFailed: 'タスクの開始に失敗しました', + alreadyRunning: 'タスクは既に実行中か、前処理を実行しています', + noRunnableTasks: '実行可能なタスクがありません。タスク定義とエントリ設定を確認してください', + primaryTasksIncomplete: '前段タスクが正常に終了しなかったため、後段タスクをスキップしました', workstationLocked: 'パソコンがロック画面の状態です。ロックを解除してからタスクを実行してください', agentStartParams: 'Agent #{{index}} 起動パラメータ: {{cmd}} (作業ディレクトリ: {{cwd}})', @@ -513,7 +516,6 @@ export default { hotkeyActionStart: 'タスク開始', hotkeyActionStop: 'タスク停止', hotkeyStartSuccess: 'ショートカットキーでタスクを開始しました:', - hotkeyStartFailed: 'ショートカットキーでタスクを開始できませんでした', hotkeyStopSuccess: 'ショートカットキーでタスクを停止しました', hotkeyStopFailed: 'ショートカットキーでタスクを停止できませんでした', }, diff --git a/src/i18n/locales/ko-KR.ts b/src/i18n/locales/ko-KR.ts index ee12a626..488e8a38 100644 --- a/src/i18n/locales/ko-KR.ts +++ b/src/i18n/locales/ko-KR.ts @@ -234,6 +234,9 @@ export default { '창이 설정되지 않아 「{{name}}」을(를) 자동으로 선택했습니다. 변경하려면 연결 설정에서 수동으로 선택하세요. 다음 번에는 선택 내용이 저장됩니다.', resourceFailed: '리소스 로딩에 실패했습니다', startFailed: '작업 시작에 실패했습니다', + alreadyRunning: '작업이 이미 실행 중이거나 사전 작업을 실행하고 있습니다', + noRunnableTasks: '실행 가능한 작업이 없습니다. 작업 정의와 진입점 설정을 확인하세요', + primaryTasksIncomplete: '앞쪽 작업이 정상 종료되지 않아 마무리 작업을 건너뛰었습니다', workstationLocked: '컴퓨터가 잠금 화면 상태입니다. 잠금을 해제한 후 작업을 실행하세요', agentStartParams: 'Agent #{{index}} 시작 파라미터: {{cmd}} (작업 디렉토리: {{cwd}})', agentSpawnHintFileNotFound: @@ -505,7 +508,6 @@ export default { hotkeyActionStart: '작업 시작', hotkeyActionStop: '작업 중지', hotkeyStartSuccess: '단축키로 작업을 시작했습니다:', - hotkeyStartFailed: '단축키로 작업을 시작하지 못했습니다', hotkeyStopSuccess: '단축키로 작업을 중지했습니다', hotkeyStopFailed: '단축키로 작업을 중지하지 못했습니다', }, diff --git a/src/i18n/locales/zh-CN.ts b/src/i18n/locales/zh-CN.ts index bd493b40..b2e5391a 100644 --- a/src/i18n/locales/zh-CN.ts +++ b/src/i18n/locales/zh-CN.ts @@ -234,6 +234,9 @@ export default { '尚未手动选择过窗口,已自动匹配到「{{name}}」。如需更换,请在连接设置中手动选择,下次将记住您的选择。', resourceFailed: '资源加载失败', startFailed: '任务启动失败', + alreadyRunning: '任务已在运行或正在执行前置动作', + noRunnableTasks: '没有可执行的任务,请检查任务定义和入口配置', + primaryTasksIncomplete: '前段任务未正常结束,已跳过收尾特殊任务', workstationLocked: '检测到电脑处于锁屏状态,请先解锁后再运行任务', agentStartParams: 'Agent #{{index}} 启动参数: {{cmd}} (工作目录: {{cwd}})', agentSpawnHintFileNotFound: '请先检查 Agent 是否被杀软拦截,确认无误后重新覆盖安装。', @@ -498,7 +501,6 @@ export default { hotkeyActionStart: '开始任务', hotkeyActionStop: '停止任务', hotkeyStartSuccess: '已通过快捷键开始任务:', - hotkeyStartFailed: '未能通过快捷键开始任务', hotkeyStopSuccess: '已通过快捷键停止任务', hotkeyStopFailed: '未能通过快捷键停止任务', }, diff --git a/src/i18n/locales/zh-TW.ts b/src/i18n/locales/zh-TW.ts index ae92403e..e454ce5f 100644 --- a/src/i18n/locales/zh-TW.ts +++ b/src/i18n/locales/zh-TW.ts @@ -230,6 +230,9 @@ export default { '尚未手動選擇過視窗,已自動匹配到「{{name}}」。如需更換,請在連接設定中手動選擇,下次將記住您的選擇。', resourceFailed: '資源載入失敗', startFailed: '任務啟動失敗', + alreadyRunning: '任務已在執行或正在執行前置動作', + noRunnableTasks: '沒有可執行的任務,請檢查任務定義與入口設定', + primaryTasksIncomplete: '前段任務未正常結束,已略過收尾特殊任務', workstationLocked: '偵測到電腦處於鎖定畫面狀態,請先解鎖後再執行任務', agentStartParams: 'Agent #{{index}} 啟動參數: {{cmd}} (工作目錄: {{cwd}})', agentSpawnHintFileNotFound: '請先檢查 Agent 是否被防毒軟體攔截,確認無誤後重新覆蓋安裝。', @@ -494,7 +497,6 @@ export default { hotkeyActionStart: '開始任務', hotkeyActionStop: '停止任務', hotkeyStartSuccess: '透過快捷鍵開始任務:', - hotkeyStartFailed: '無法透過快捷鍵開始任務', hotkeyStopSuccess: '透過快捷鍵停止任務', hotkeyStopFailed: '無法透過快捷鍵停止任務', },