diff --git a/app.py b/app.py index ba877de..25e91c0 100644 --- a/app.py +++ b/app.py @@ -2566,8 +2566,8 @@ def clear_history(): os.unlink(file_path) elif os.path.isdir(file_path): shutil.rmtree(file_path) - except Exception: - pass + except Exception as exc: + logger.warning("Failed to remove execution log entry %s: %s", file_path, exc) # Clear session logs if os.path.exists(SESSION_LOG_DIR): @@ -2578,8 +2578,8 @@ def clear_history(): os.unlink(file_path) elif os.path.isdir(file_path): shutil.rmtree(file_path) - except Exception: - pass + except Exception as exc: + logger.warning("Failed to remove session log entry %s: %s", file_path, exc) return jsonify({ 'success': True, diff --git a/ui/app.js b/ui/app.js index bf7725b..6174ae2 100644 --- a/ui/app.js +++ b/ui/app.js @@ -70,6 +70,7 @@ let state = { sessionId: null, lastSaveTimestamp: 0, runningScripts: {}, // termId -> { step, total, command, status } + terminalTimerInterval: null, reliabilitySummary: null, reliabilityFailures: null, reliabilityTrends: null, @@ -1035,11 +1036,51 @@ function cleanupRunningScript(termId) { delete state.runningScripts[termId]; if (termId === state.activeTerminalId) { + stopTerminalTimer(); updateRunButton(); updateProgressTrackerUI(); } } +function formatElapsedTime(ms) { + const totalSeconds = Math.max(0, Math.floor(ms / 1000)); + const minutes = String(Math.floor(totalSeconds / 60)).padStart(2, '0'); + const seconds = String(totalSeconds % 60).padStart(2, '0'); + return `${minutes}:${seconds}`; +} + +function updateTerminalTimer() { + const timer = document.getElementById('terminal-session-timer'); + if (!timer) return; + + const running = state.runningScripts[state.activeTerminalId]; + if (!running?.startedAt) { + timer.style.display = 'none'; + timer.textContent = '00:00'; + return; + } + + timer.textContent = formatElapsedTime(Date.now() - running.startedAt); + timer.style.display = 'inline-flex'; +} + +function startTerminalTimer() { + stopTerminalTimer(false); + updateTerminalTimer(); + state.terminalTimerInterval = setInterval(updateTerminalTimer, 1000); +} + +function stopTerminalTimer(reset = true) { + if (state.terminalTimerInterval) { + clearInterval(state.terminalTimerInterval); + state.terminalTimerInterval = null; + } + + if (reset) { + updateTerminalTimer(); + } +} + async function abortScriptRun(termId = state.activeTerminalId) { const running = state.runningScripts[termId]; if (!running) return; @@ -1145,9 +1186,11 @@ async function executeScriptWithArguments(relPath, argumentsText) { total: 0, command: 'Starting script...', status: 'running', + startedAt: Date.now(), controller: controller }; updateRunButton(); + startTerminalTimer(); if (termId === state.activeTerminalId) { runStatus.textContent = 'Executing...'; @@ -2805,9 +2848,11 @@ function switchTerminal(id) { runStatus.textContent = running.aborting ? 'Aborting...' : 'Executing...'; runStatus.className = 'run-status running'; resourcePanel.style.display = 'none'; + startTerminalTimer(); } else { runStatus.textContent = ''; runStatus.className = 'run-status'; + stopTerminalTimer(); } updateRunButton(); highlightTerminalSearch(); diff --git a/ui/index.html b/ui/index.html index 58399eb..9fa238b 100644 --- a/ui/index.html +++ b/ui/index.html @@ -360,6 +360,7 @@

Scripts

+