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
8 changes: 4 additions & 4 deletions app.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand All @@ -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,
Expand Down
62 changes: 57 additions & 5 deletions ui/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -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 = `
<div class="terminal-empty-icon" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<polyline points="4 17 10 11 4 5" />
<line x1="12" x2="20" y1="19" y2="19" />
</svg>
</div>
<div>
<strong>Terminal ready</strong>
<span>${escapeHtml(message)}</span>
</div>`;
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}`;
Expand All @@ -2177,7 +2224,7 @@ function appendToCli(text, className = '', termId = state.activeTerminalId) {
function clearCli() {
const termBody = getTerminalBody(state.activeTerminalId);
if (termBody) {
termBody.innerHTML = '<div class="cli-welcome"><span class="cli-prompt">$</span> <span class="cli-welcome-text">Terminal cleared.</span></div>';
showTerminalEmptyState(termBody, 'Terminal cleared. Run another script or command when ready.');
}
document.getElementById('run-status').textContent = '';
document.getElementById('run-status').className = 'run-status';
Expand Down Expand Up @@ -2381,6 +2428,8 @@ async function restoreSession() {

body.appendChild(div);
}

syncTerminalEmptyState(body);
}

switchTerminal(state.activeTerminalId);
Expand Down Expand Up @@ -2775,7 +2824,7 @@ function addTerminal() {
bodyContainer.setAttribute('aria-live', 'polite');
bodyContainer.id = `terminal-body-${id}`;
bodyContainer.style.display = 'none';
bodyContainer.innerHTML = '<div class="cli-welcome"><span class="cli-prompt">$</span> <span class="cli-welcome-text">Terminal ready.</span></div>';
showTerminalEmptyState(bodyContainer);

document.getElementById('cli-area').insertBefore(bodyContainer, document.querySelector('.cli-input-bar'));
applyTerminalDensity();
Expand All @@ -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);
Expand Down
18 changes: 14 additions & 4 deletions ui/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -394,10 +394,20 @@ <h2>Scripts</h2>
<span id="progress-tracker-status" class="progress-tracker-status">Status: Idle</span>
</div>
</div>
<div class="cli-body" id="terminal-body" role="log" aria-live="polite">
<div class="cli-welcome">
<span class="cli-prompt">$</span> <span class="cli-welcome-text">Welcome to DevShell. Select a
script to run, or type a command below.</span>
<div class="cli-body terminal-empty" id="terminal-body" role="log" aria-live="polite">
<div class="terminal-empty-state">
<div class="terminal-empty-icon" aria-hidden="true">
<svg xmlns="http://www.w3.org/2000/svg" width="26" height="26" viewBox="0 0 24 24"
fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"
stroke-linejoin="round">
<polyline points="4 17 10 11 4 5" />
<line x1="12" x2="20" y1="19" y2="19" />
</svg>
</div>
<div>
<strong>Terminal ready</strong>
<span>Select a script or run a command to start streaming output.</span>
</div>
</div>

<div class="terminal-scroll-shortcuts" id="terminal-scroll-actions">
Expand Down
47 changes: 47 additions & 0 deletions ui/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
Loading