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..0b3b9b5 100644 --- a/ui/app.js +++ b/ui/app.js @@ -2153,12 +2153,59 @@ function updateProgressTrackerUI() { // ─── CLI Helpers ─── +function createTerminalEmptyState(message = 'Select a script or run a command to start streaming output.') { + const emptyState = document.createElement('div'); + emptyState.className = 'terminal-empty-state'; + emptyState.innerHTML = ` + +
+ Terminal ready + ${escapeHtml(message)} +
`; + return emptyState; +} + +function showTerminalEmptyState(termBody, message) { + if (!termBody) return; + termBody.innerHTML = ''; + termBody.appendChild(createTerminalEmptyState(message)); + termBody.classList.add('terminal-empty'); + if (termBody.style.display !== 'none') { + termBody.style.display = 'flex'; + } +} + +function syncTerminalEmptyState(termBody) { + if (!termBody) return; + const hasOutput = !!termBody.querySelector('.cli-output-block'); + const hasEmptyState = !!termBody.querySelector('.terminal-empty-state, .cli-welcome'); + + if (hasOutput) { + termBody.classList.remove('terminal-empty'); + termBody.querySelectorAll('.terminal-empty-state, .cli-welcome').forEach(el => el.remove()); + return; + } + + if (!hasEmptyState) { + termBody.appendChild(createTerminalEmptyState()); + } + termBody.classList.add('terminal-empty'); +} + function appendToCli(text, className = '', termId = state.activeTerminalId) { const termBody = getTerminalBody(termId); if (!termBody) return; - const welcomeEl = termBody.querySelector('.cli-welcome'); - if (welcomeEl) welcomeEl.remove(); + termBody.classList.remove('terminal-empty'); + termBody.querySelectorAll('.terminal-empty-state, .cli-welcome').forEach(el => el.remove()); + if (termId === state.activeTerminalId) { + termBody.style.display = 'block'; + } const line = document.createElement('div'); line.className = `cli-output-block ${className}`; @@ -2177,7 +2224,7 @@ function appendToCli(text, className = '', termId = state.activeTerminalId) { function clearCli() { const termBody = getTerminalBody(state.activeTerminalId); if (termBody) { - termBody.innerHTML = '
$ Terminal cleared.
'; + showTerminalEmptyState(termBody, 'Terminal cleared. Run another script or command when ready.'); } document.getElementById('run-status').textContent = ''; document.getElementById('run-status').className = 'run-status'; @@ -2381,6 +2428,8 @@ async function restoreSession() { body.appendChild(div); } + + syncTerminalEmptyState(body); } switchTerminal(state.activeTerminalId); @@ -2775,7 +2824,7 @@ function addTerminal() { bodyContainer.setAttribute('aria-live', 'polite'); bodyContainer.id = `terminal-body-${id}`; bodyContainer.style.display = 'none'; - bodyContainer.innerHTML = '
$ Terminal ready.
'; + showTerminalEmptyState(bodyContainer); document.getElementById('cli-area').insertBefore(bodyContainer, document.querySelector('.cli-input-bar')); applyTerminalDensity(); @@ -2793,7 +2842,10 @@ function switchTerminal(id) { document.querySelectorAll('.cli-body').forEach(b => b.style.display = 'none'); const activeBody = getTerminalBody(id); - if (activeBody) activeBody.style.display = 'block'; + if (activeBody) { + syncTerminalEmptyState(activeBody); + activeBody.style.display = activeBody.classList.contains('terminal-empty') ? 'flex' : 'block'; + } // Sync auto-scroll button to the newly active terminal's state updateAutoScrollBtn(id, state.autoScroll[id] !== false); diff --git a/ui/index.html b/ui/index.html index 58399eb..35d9fa0 100644 --- a/ui/index.html +++ b/ui/index.html @@ -394,10 +394,20 @@

Scripts

Status: Idle -
-
- $ Welcome to DevShell. Select a - script to run, or type a command below. +
+
+ +
+ Terminal ready + Select a script or run a command to start streaming output. +
diff --git a/ui/style.css b/ui/style.css index 312720a..f5039a3 100644 --- a/ui/style.css +++ b/ui/style.css @@ -914,6 +914,53 @@ body { word-break: break-word; } +.cli-body.terminal-empty { + display: flex; + align-items: center; + justify-content: center; +} + +.terminal-empty-state { + width: min(460px, 100%); + display: flex; + align-items: center; + gap: 14px; + padding: 18px; + border: 1px dashed var(--border-bright); + border-radius: 8px; + background: rgba(15, 17, 21, 0.82); + color: var(--text-secondary); +} + +.terminal-empty-icon { + width: 44px; + height: 44px; + flex: 0 0 44px; + display: inline-flex; + align-items: center; + justify-content: center; + border-radius: 8px; + background: var(--accent-glow); + color: var(--accent); +} + +.terminal-empty-state strong, +.terminal-empty-state span { + display: block; +} + +.terminal-empty-state strong { + margin-bottom: 4px; + color: var(--text-primary); + font-family: var(--font-ui); + font-size: 13px; +} + +.terminal-empty-state span { + font-family: var(--font-ui); + font-size: 12px; +} + .cli-body.terminal-density-compact { --terminal-line-height: 1.35; }