From b0414278e9ca65229cec21191fc0201df7d64c07 Mon Sep 17 00:00:00 2001 From: tanmayythakare Date: Thu, 28 May 2026 17:00:21 +0530 Subject: [PATCH] feat: implement frontend shell script management UI with sidebar, search, and themes --- app.py | 4 +- ui/app.js | 936 ++++---- ui/index.html | 119 +- ui/style.css | 6357 ++++++++++++++++++++++++++----------------------- 4 files changed, 3923 insertions(+), 3493 deletions(-) diff --git a/app.py b/app.py index 3afa3d9..ae4d804 100644 --- a/app.py +++ b/app.py @@ -2285,7 +2285,9 @@ def parse_script_metadata(filepath): for line in f: line = line.strip() if line.startswith('# name:'): - metadata['name'] = line[7:].strip() + name_val = line[7:].strip() + if name_val: + metadata['name'] = name_val elif line.startswith('# desc:'): metadata['desc'] = line[7:].strip() elif line.startswith('# tag:'): diff --git a/ui/app.js b/ui/app.js index 1fed243..a519e03 100644 --- a/ui/app.js +++ b/ui/app.js @@ -30,7 +30,7 @@ const API = { let state = { scripts: {}, activeScript: null, - expandedCategories: new Set(), + expandedCategories: new Set(['Favorites']), searchQuery: '', cmdHistory: [], cmdHistoryIndex: -1, @@ -72,6 +72,7 @@ let state = { reliabilityFilter: 'all', reliabilitySearch: '', reliabilityDiagnostics: null, + terminalStatuses: {}, }; const unlockCredentials = new Map(); @@ -80,6 +81,96 @@ function isScriptUnlocked(relPath) { return !!state.unlockedScripts[relPath]; } +function getTerminalDisplayName(termId) { + termId = Number(termId); + const index = state.terminals.indexOf(termId); + return index !== -1 ? `Terminal ${index + 1}` : `Terminal ${termId}`; +} + +function renumberTabs() { + state.terminals.forEach((id, index) => { + const tabBtn = document.getElementById(`tab-btn-${id}`) || document.querySelector(`.cli-tab[data-id="${id}"]`); + if (tabBtn) { + const spans = tabBtn.getElementsByTagName('span'); + for (let span of spans) { + if (span.textContent.trim().startsWith('Terminal')) { + span.textContent = getTerminalDisplayName(id); + break; + } + } + } + }); +} + +function updateTabStatusIndicator(termId) { + termId = Number(termId); + const indicator = document.getElementById(`tab-status-${termId}`); + if (!indicator) return; + + const status = state.terminalStatuses[termId]; + if (status === 'running') { + indicator.innerHTML = ``; + } else if (status === 'success') { + indicator.innerHTML = ``; + } else if (status === 'failed') { + indicator.innerHTML = ``; + } else { + indicator.innerHTML = ''; + } +} + +function showCustomConfirm(message, title = 'Confirmation') { + return new Promise((resolve) => { + const overlay = document.getElementById('confirm-modal-overlay'); + const titleEl = document.getElementById('confirm-modal-title'); + const messageEl = document.getElementById('confirm-modal-message'); + const confirmBtn = document.getElementById('confirm-modal-confirm'); + const cancelBtn = document.getElementById('confirm-modal-cancel'); + const closeBtn = document.getElementById('confirm-modal-close'); + + if (!overlay || !titleEl || !messageEl || !confirmBtn || !cancelBtn || !closeBtn) { + resolve(confirm(message)); + return; + } + + titleEl.textContent = title; + messageEl.textContent = message; + + overlay.classList.add('active'); + confirmBtn.focus(); + + const cleanup = (value) => { + overlay.classList.remove('active'); + confirmBtn.removeEventListener('click', onConfirm); + cancelBtn.removeEventListener('click', onCancel); + closeBtn.removeEventListener('click', onCancel); + document.removeEventListener('keydown', onKeyDown); + resolve(value); + }; + + const onConfirm = () => cleanup(true); + const onCancel = () => cleanup(false); + const onKeyDown = (e) => { + if (e.key === 'Escape') { + onCancel(); + } + }; + + confirmBtn.addEventListener('click', onConfirm); + cancelBtn.addEventListener('click', onCancel); + closeBtn.addEventListener('click', onCancel); + document.addEventListener('keydown', onKeyDown); + }); +} + +function changeTerminalFontSize(delta) { + let terminalFontSize = parseInt(localStorage.getItem('terminal-font-size')) || 13; + terminalFontSize = Math.max(9, Math.min(24, terminalFontSize + delta)); + localStorage.setItem('terminal-font-size', terminalFontSize); + document.documentElement.style.setProperty('--terminal-font-size', `${terminalFontSize}px`); +} + + function getUnlockPassword(relPath) { return unlockCredentials.get(relPath) || ''; } @@ -170,6 +261,77 @@ if (!window.__devshell_lifecycle_registered) { } // ─── Init ────────────────────────────────────────────────── +const ANALYTICS_TEMPLATE = ` +
+
+
+

Total Runs

+ +
+
0
+
+
+
+
All executions
+
+
+
+

Successful

+ +
+
0
+
+
+
+
0% rate
+
+
+
+

Failed

+ +
+
0
+
+
+
+
0% rate
+
+
+
+

Average Runtime

+ +
+
0s
+
+
+
+
Latency average
+
+
+
+
+

Top Executed Scripts

+
+
+
+

Slowest Executions

+
+
+
+

Recent Failures

+
+
+
+`; + +const ANALYTICS_EMPTY_TEMPLATE = ` +
+ +

No Execution History

+

Analytics will appear here after you run commands or execute scripts.

+
+`; + async function openAnalytics() { try { const res = await fetch('/api/history/analytics'); @@ -181,32 +343,60 @@ async function openAnalytics() { } const summary = data.summary; + const total = summary.total || 0; + + const contentEl = document.getElementById('analytics-content'); + if (!contentEl) return; + + if (total === 0) { + contentEl.innerHTML = ANALYTICS_EMPTY_TEMPLATE; + document.getElementById('analytics-modal-overlay').classList.add('active'); + return; + } - document.getElementById('analytics-total').textContent = summary.total; + contentEl.innerHTML = ANALYTICS_TEMPLATE; + + const successRate = total > 0 ? Math.round((summary.successful / total) * 100) : 0; + const failedRate = total > 0 ? Math.round((summary.failed / total) * 100) : 0; + + document.getElementById('analytics-total').textContent = total; document.getElementById('analytics-success').textContent = summary.successful; document.getElementById('analytics-failed').textContent = summary.failed; document.getElementById('analytics-avg').textContent = `${summary.avg_duration}s`; + const successFill = document.getElementById('analytics-success-fill'); + const successRateEl = document.getElementById('analytics-success-rate'); + if (successFill) successFill.style.width = `${successRate}%`; + if (successRateEl) successRateEl.textContent = `${successRate}% rate`; + + const failedFill = document.getElementById('analytics-failed-fill'); + const failedRateEl = document.getElementById('analytics-failed-rate'); + if (failedFill) failedFill.style.width = `${failedRate}%`; + if (failedRateEl) failedRateEl.textContent = `${failedRate}% rate`; + document.getElementById('analytics-top-scripts').innerHTML = data.top_scripts.map(([name, count]) => `
- ${escapeHtml(name)} — ${count} runs + ${escapeHtml(name)} + ${count} ${count === 1 ? 'run' : 'runs'}
- `).join(''); + `).join('') || '
No execution data available.
'; document.getElementById('analytics-slowest').innerHTML = data.slowest.map(entry => `
- ${escapeHtml(entry.display_name)} — ${entry.duration_seconds}s + ${escapeHtml(entry.display_name)} + ${entry.duration_seconds}s
- `).join(''); + `).join('') || '
No slow executions recorded.
'; document.getElementById('analytics-failures').innerHTML = data.recent_failures.map(entry => `
- ${escapeHtml(entry.display_name)} — Exit ${entry.exit_code} + ${escapeHtml(entry.display_name)} + Exit ${entry.exit_code}
- `).join(''); + `).join('') || '
No recent failures recorded.
'; document.getElementById('analytics-modal-overlay').classList.add('active'); @@ -877,6 +1067,9 @@ async function loadCommandHistory() { } document.addEventListener('DOMContentLoaded', async () => { + let terminalFontSize = parseInt(localStorage.getItem('terminal-font-size')) || 13; + document.documentElement.style.setProperty('--terminal-font-size', `${terminalFontSize}px`); + await loadScripts(); await loadCommandHistory(); bindEvents(); @@ -986,6 +1179,7 @@ async function fetchScriptContent(relPath, password = '') { } function getTerminalBody(termId = state.activeTerminalId) { + termId = Number(termId); return document.getElementById(`terminal-body-${termId}`) || (termId === 1 ? document.getElementById('terminal-body') : null); } @@ -1095,6 +1289,8 @@ async function runScript(relPath) { controller: controller }; updateRunButton(); + state.terminalStatuses[termId] = 'running'; + updateTabStatusIndicator(termId); if (termId === state.activeTerminalId) { runStatus.textContent = 'Executing...'; @@ -1190,6 +1386,8 @@ async function runScript(relPath) { } } else if (data.type === 'aborted') { receivedTerminalEvent = true; + state.terminalStatuses[termId] = 'failed'; + updateTabStatusIndicator(termId); appendToCli(data.content, 'error', termId); if (typeof DebuggerConsole !== 'undefined') { DebuggerConsole.addEntry('error', `Script aborted (ID: ${data.run_id})`, 'script'); @@ -1201,6 +1399,8 @@ async function runScript(relPath) { } else if (data.type === 'metrics') { receivedTerminalEvent = true; if (data.success) { + state.terminalStatuses[termId] = 'success'; + updateTabStatusIndicator(termId); appendToCli(`Script completed (Exit code: ${data.exit_code})`, 'success', termId); if (typeof DebuggerConsole !== 'undefined') { DebuggerConsole.addEntry('info', `✓ Script completed — exit code: ${data.exit_code} | time: ${data.resources?.execution_time_formatted || ''} | cpu: ${data.resources?.cpu_percent || 0}% | mem: ${data.resources?.memory_used_mb || 0}MB`, 'metrics'); @@ -1223,6 +1423,8 @@ async function runScript(relPath) { }, 3000); } } else { + state.terminalStatuses[termId] = 'failed'; + updateTabStatusIndicator(termId); appendToCli(`Script failed (Exit code: ${data.exit_code})`, 'error', termId); if (typeof DebuggerConsole !== 'undefined') { DebuggerConsole.addEntry('error', `✗ Script failed — exit code: ${data.exit_code}`, 'metrics'); @@ -1262,6 +1464,8 @@ async function runScript(relPath) { const running = state.runningScripts[termId]; if (!receivedTerminalEvent && running && !running.abortRequested) { + state.terminalStatuses[termId] = 'failed'; + updateTabStatusIndicator(termId); appendToCli('Connection to script stream lost unexpectedly.', 'error', termId); if (termId === state.activeTerminalId) { runStatus.textContent = 'Disconnected'; @@ -1276,6 +1480,8 @@ async function runScript(relPath) { } } } catch (err) { + state.terminalStatuses[termId] = 'failed'; + updateTabStatusIndicator(termId); if (err.name === 'AbortError') { appendToCli('Script run aborted.', 'system', termId); if (termId === state.activeTerminalId) { @@ -1570,7 +1776,7 @@ async function saveScript(category, filename, content) { } async function deleteScript(relPath) { - if (!confirm('Are you sure you want to delete this script permanently?')) return; + if (!await showCustomConfirm('Are you sure you want to delete this script permanently?', 'Delete Script')) return; try { const res = await fetch(API.delete, { method: 'DELETE', @@ -1799,8 +2005,9 @@ async function executePR(relPath, branch, message, repoUrl) { // Offer PR page opening if ( - confirm( - `Successfully pushed to branch '${data.branch}'.\n\nWould you like to open the Pull Request page on GitHub?` + await showCustomConfirm( + `Successfully pushed to branch '${data.branch}'.\n\nWould you like to open the Pull Request page on GitHub?`, + 'PR Created' ) ) { window.open(data.pr_url, '_blank'); @@ -2072,7 +2279,7 @@ function appendToCli(text, className = '', termId = state.activeTerminalId) { function clearCli() { const termBody = getTerminalBody(state.activeTerminalId); if (termBody) { - termBody.innerHTML = '
$ Terminal cleared.
'; + termBody.innerHTML = '
$Terminal cleared.
Ready for new commands.
'; } document.getElementById('run-status').textContent = ''; document.getElementById('run-status').className = 'run-status'; @@ -2085,207 +2292,7 @@ function clearCli() { } -// ─── Session Persistence ────────────────────────────────── - -async function saveSession() { - const sessionData = { - sessionId: state.sessionId || generateUUID(), - timestamp: Date.now(), - - terminals: state.terminals.map(id => { - const body = - document.getElementById(`terminal-body-${id}`) || - (id === 1 - ? document.getElementById('terminal-body') - : null); - - if (!body) return null; - const lines = Array.from( - body.querySelectorAll('.cli-output-block') - ) - .slice(-100) - .map(el => ({ - text: el.textContent, - className: el.className.replace( - 'cli-output-block ', - '' - ) - })); - - return { - id, - lines - }; - }).filter(t => t !== null), - - activeTerminalId: state.activeTerminalId, - nextTerminalId: state.nextTerminalId, - - cmdHistory: state.cmdHistory, - cmdHistoryIndex: state.cmdHistoryIndex, - - unlockedScripts: serializeUnlockedScripts() - }; - - try { - await fetch('/api/sessions/save', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - session: sessionData - }) - }); - - state.sessionId = sessionData.sessionId; - state.lastSaveTimestamp = Date.now(); - - } catch (e) { - console.error('Failed to save session:', e); - } -} - - -function generateUUID() { - return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx' - .replace(/[xy]/g, c => { - const r = Math.random() * 16 | 0; - const v = c === 'x' - ? r - : (r & 0x3 | 0x8); - - return v.toString(16); - }); -} - - -// let saveSessionTimeout = null; - -function saveSessionDebounced() { - if (saveSessionTimeout) { - clearTimeout(saveSessionTimeout); - } - - saveSessionTimeout = setTimeout(() => { - saveSession(); - }, 2000); -} - - -async function restoreSession() { - try { - const res = await fetch('/api/sessions/restore'); - const data = await res.json(); - - if (!data.success || !data.session) { - return; - } - - const session = data.session; - - state.sessionId = session.sessionId || null; - - const terminalIds = session.terminals?.map(t => t.id); - state.terminals = terminalIds?.length ? terminalIds : [1]; - - state.activeTerminalId = - session.activeTerminalId || 1; - - state.nextTerminalId = - Math.max(...state.terminals) + 1; - - state.cmdHistory = - session.cmdHistory || []; - - state.cmdHistoryIndex = - session.cmdHistoryIndex || -1; - - restoreUnlockedScripts(session.unlockedScripts); - - const existingTabs = - document.querySelectorAll('.cli-tab'); - - existingTabs.forEach(tab => { - if (tab.id !== 'tab-btn-1') { - tab.remove(); - } - }); - - const existingBodies = - document.querySelectorAll('.cli-body'); - - existingBodies.forEach(body => { - if (body.id !== 'terminal-body') { - body.remove(); - } - }); - - for (const term of session.terminals || []) { - - if (term.id !== 1) { - // Create terminal DOM directly with the saved ID - // instead of calling addTerminal() which would - // corrupt state.nextTerminalId and state.terminals - const tabsContainer = document.getElementById('cli-tabs'); - const tabBtn = document.createElement('div'); - tabBtn.className = 'cli-tab'; - tabBtn.id = `tab-btn-${term.id}`; - tabBtn.innerHTML = ` - - - - - - Terminal ${term.id} - `; - tabBtn.onclick = () => switchTerminal(term.id); - tabsContainer.insertBefore(tabBtn, document.getElementById('btn-add-tab')); - - const bodyContainer = document.createElement('div'); - bodyContainer.className = 'cli-body'; - bodyContainer.setAttribute('role', 'log'); - bodyContainer.setAttribute('aria-live', 'polite'); - bodyContainer.id = `terminal-body-${term.id}`; - bodyContainer.style.display = 'none'; - - document.getElementById('cli-area').insertBefore( - bodyContainer, - document.querySelector('.cli-input-bar') - ); - } - - const body = - document.getElementById(`terminal-body-${term.id}`) || - (term.id === 1 - ? document.getElementById('terminal-body') - : null); - - if (!body) continue; - - body.innerHTML = ''; - - for (const line of term.lines || []) { - const div = document.createElement('div'); - - div.className = - `cli-output-block ${line.className}`; - - div.textContent = line.text; - - body.appendChild(div); - } - } - - switchTerminal(state.activeTerminalId); - - console.log('Session restored successfully'); - - } catch (e) { - console.error('Failed to restore session:', e); - } -} // ─── Terminal Utility Actions ─────────────────────────────── @@ -2373,7 +2380,7 @@ function toggleAutoScroll() { state.autoScroll[termId] = state.autoScroll[termId] === false ? true : false; const isOn = state.autoScroll[termId] !== false; updateAutoScrollBtn(termId, isOn); - notify(`Auto-scroll ${isOn ? 'enabled' : 'disabled'} for Terminal ${termId}.`, 'info'); + notify(`Auto-scroll ${isOn ? 'enabled' : 'disabled'} for ${getTerminalDisplayName(termId)}.`, 'info'); } /** @@ -2399,7 +2406,7 @@ function updateAutoScrollBtn(termId, isOn) { function clearCli() { const termBody = getTerminalBody(state.activeTerminalId); if (termBody) { - termBody.innerHTML = '
$ Terminal cleared.
'; + termBody.innerHTML = '
$Terminal cleared.
Ready for new commands.
'; } document.getElementById('run-status').textContent = ''; document.getElementById('run-status').className = 'run-status'; @@ -2419,11 +2426,8 @@ async function saveSession() { timestamp: Date.now(), terminals: state.terminals.map(id => { - const body = - document.getElementById(`terminal-body-${id}`) || - (id === 1 - ? document.getElementById('terminal-body') - : null); + id = Number(id); + const body = getTerminalBody(id); if (!body) return null; @@ -2445,8 +2449,8 @@ async function saveSession() { }; }).filter(t => t !== null), - activeTerminalId: state.activeTerminalId, - nextTerminalId: state.nextTerminalId, + activeTerminalId: Number(state.activeTerminalId), + nextTerminalId: Number(state.nextTerminalId), cmdHistory: state.cmdHistory, cmdHistoryIndex: state.cmdHistoryIndex, @@ -2513,97 +2517,77 @@ async function restoreSession() { state.sessionId = session.sessionId || null; - const terminalIds = session.terminals?.map(t => t.id); + const terminalIds = session.terminals?.map(t => Number(t.id)); state.terminals = terminalIds?.length ? terminalIds : [1]; - state.activeTerminalId = - session.activeTerminalId || 1; + state.activeTerminalId = Number(session.activeTerminalId || 1); - state.nextTerminalId = - Math.max(...state.terminals) + 1; + state.nextTerminalId = Math.max(...state.terminals, 1) + 1; - state.cmdHistory = - session.cmdHistory || []; - - state.cmdHistoryIndex = - session.cmdHistoryIndex || -1; + state.cmdHistory = session.cmdHistory || []; + state.cmdHistoryIndex = session.cmdHistoryIndex || -1; restoreUnlockedScripts(session.unlockedScripts); - const existingTabs = - document.querySelectorAll('.cli-tab'); - - existingTabs.forEach(tab => { - if (tab.id !== 'tab-btn-1') { - tab.remove(); - } + // Remove all existing terminal tabs from the DOM + document.querySelectorAll('.cli-tab').forEach(tab => { + tab.remove(); }); - const existingBodies = - document.querySelectorAll('.cli-body'); - - existingBodies.forEach(body => { - if (body.id !== 'terminal-body') { - body.remove(); - } + // Remove all existing terminal bodies from the DOM + document.querySelectorAll('.cli-body').forEach(body => { + body.remove(); }); - for (const term of session.terminals || []) { - - if (term.id !== 1) { - // Create terminal DOM directly with the saved ID - // instead of calling addTerminal() which would - // corrupt state.nextTerminalId and state.terminals - const tabsContainer = document.getElementById('cli-tabs'); - const tabBtn = document.createElement('div'); - tabBtn.className = 'cli-tab'; - tabBtn.id = `tab-btn-${term.id}`; - tabBtn.innerHTML = ` - - - - - - Terminal ${term.id} - `; - tabBtn.onclick = () => switchTerminal(term.id); - tabsContainer.insertBefore(tabBtn, document.getElementById('btn-add-tab')); - - const bodyContainer = document.createElement('div'); - bodyContainer.className = 'cli-body'; - bodyContainer.setAttribute('role', 'log'); - bodyContainer.setAttribute('aria-live', 'polite'); - bodyContainer.id = `terminal-body-${term.id}`; - bodyContainer.style.display = 'none'; - - document.getElementById('cli-area').insertBefore( - bodyContainer, - document.querySelector('.cli-input-bar') - ); - } - - const body = - document.getElementById(`terminal-body-${term.id}`) || - (term.id === 1 - ? document.getElementById('terminal-body') - : null); - - if (!body) continue; - - body.innerHTML = ''; - - for (const line of term.lines || []) { - const div = document.createElement('div'); + const tabsContainer = document.getElementById('cli-tabs'); + const cliArea = document.getElementById('cli-area'); - div.className = - `cli-output-block ${line.className}`; - - div.textContent = line.text; + for (const term of session.terminals || []) { + const termId = Number(term.id); + + const tabBtn = document.createElement('div'); + tabBtn.className = 'cli-tab'; + tabBtn.id = `tab-btn-${termId}`; + tabBtn.dataset.id = termId; + tabBtn.innerHTML = ` + >_ + ${getTerminalDisplayName(termId)} + `; + tabBtn.onclick = () => switchTerminal(termId); + tabsContainer.insertBefore(tabBtn, document.getElementById('btn-add-tab')); + + const bodyContainer = document.createElement('div'); + bodyContainer.className = 'cli-body'; + bodyContainer.setAttribute('role', 'log'); + bodyContainer.setAttribute('aria-live', 'polite'); + bodyContainer.id = `terminal-body-${termId}`; + bodyContainer.style.display = 'none'; + + cliArea.insertBefore( + bodyContainer, + document.querySelector('.cli-input-bar') + ); - body.appendChild(div); + // Populate terminal log lines + bodyContainer.innerHTML = ''; + if (!term.lines || term.lines.length === 0) { + if (termId === 1) { + bodyContainer.innerHTML = '
$Welcome to DevShell.
Select a script to run, or type a command below.
'; + } else { + bodyContainer.innerHTML = '
$Terminal ready.
Ready for interaction.
'; + } + } else { + for (const line of term.lines || []) { + const div = document.createElement('div'); + div.className = `cli-output-block ${line.className}`; + div.textContent = line.text; + bodyContainer.appendChild(div); + } } } + renumberTabs(); + state.terminals.forEach(termId => updateTabStatusIndicator(termId)); switchTerminal(state.activeTerminalId); console.log('Session restored successfully'); @@ -2625,15 +2609,12 @@ function addTerminal() { tabBtn.className = 'cli-tab'; tabBtn.id = `tab-btn-${id}`; tabBtn.innerHTML = ` - - - - - - Terminal ${id} + >_ + ${getTerminalDisplayName(id)} `; tabBtn.onclick = () => switchTerminal(id); tabsContainer.insertBefore(tabBtn, document.getElementById('btn-add-tab')); + renumberTabs(); const bodyContainer = document.createElement('div'); bodyContainer.className = 'cli-body'; @@ -2641,7 +2622,7 @@ function addTerminal() { bodyContainer.setAttribute('aria-live', 'polite'); bodyContainer.id = `terminal-body-${id}`; bodyContainer.style.display = 'none'; - bodyContainer.innerHTML = '
$ Terminal ready.
'; + bodyContainer.innerHTML = '
$Terminal ready.
Ready for interaction.
'; document.getElementById('cli-area').insertBefore(bodyContainer, document.querySelector('.cli-input-bar')); switchTerminal(id); @@ -2650,6 +2631,7 @@ function addTerminal() { } function switchTerminal(id) { + id = Number(id); state.activeTerminalId = id; document.querySelectorAll('.cli-tab').forEach(t => t.classList.remove('active')); @@ -2683,6 +2665,7 @@ function switchTerminal(id) { } function closeTerminal(id) { + id = Number(id); if (state.terminals.length <= 1) return; if (state.runningScripts && state.runningScripts[id]) { @@ -2691,9 +2674,11 @@ function closeTerminal(id) { state.terminals = state.terminals.filter(t => t !== id); delete state.autoScroll[id]; + delete state.terminalStatuses[id]; const tabBtn = document.getElementById(`tab-btn-${id}`) || document.querySelector(`.cli-tab[data-id="${id}"]`); if (tabBtn) tabBtn.remove(); + renumberTabs(); const bodyContainer = getTerminalBody(id); if (bodyContainer) bodyContainer.remove(); @@ -2776,7 +2761,8 @@ function renderSidebar() { const tree = document.getElementById('category-tree'); const countEl = document.getElementById('script-count'); const favsSection = document.getElementById('favorites-section'); - const favsList = document.getElementById('favorites-list'); + + if (favsSection) favsSection.style.display = 'none'; let totalScripts = 0; let favScripts = []; @@ -2784,6 +2770,63 @@ function renderSidebar() { const query = state.searchQuery.toLowerCase(); + // Collect all favorites matching search query + for (const [cat, scripts] of Object.entries(state.scripts)) { + scripts.forEach(s => { + if (s.favorite) { + const matches = !query || + s.name.toLowerCase().includes(query) || + (s.desc && s.desc.toLowerCase().includes(query)) || + (s.tag && s.tag.toLowerCase().includes(query)) || + s.file.toLowerCase().includes(query); + if (matches) { + favScripts.push(s); + } + } + }); + } + + // Render Favorites virtual category folder + if (favScripts.length > 0) { + const isExpanded = state.expandedCategories.has('Favorites') || !!query; + html += ` +
+
+ + + + ${ICONS.favorite} + Favorites + ${favScripts.length} +
+ +
+ `; + } + + // Render other categories for (const [cat, scripts] of Object.entries(state.scripts)) { const filteredScripts = query ? scripts.filter(s => @@ -2799,8 +2842,8 @@ function renderSidebar() { const isExpanded = state.expandedCategories.has(cat) || !!query; html += ` -
-
+
+
@@ -2811,15 +2854,19 @@ function renderSidebar() {
    ${filteredScripts.map(s => { let lockIcon = s.locked ? `${ICONS.lock}` : ''; - + const displayName = s.name && s.name.trim() ? s.name : s.file.replace('.sh', '').replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); return `
  • + title="${escapeAttr(s.desc)}" + data-file="${escapeAttr(s.file)}" + data-path="${escapeAttr(s.relative_path)}" + data-tag="${escapeAttr(s.tag || '')}" + data-desc="${escapeAttr(s.desc || '')}"> ${lockIcon} ${ICONS.script} - ${escapeHtml(s.name)} + ${escapeHtml(displayName)} ${ICONS.favorite} @@ -2829,27 +2876,10 @@ function renderSidebar() {
`; - - // Populate favs - scripts.forEach(s => { if (s.favorite) favScripts.push(s); }); } tree.innerHTML = html || '
No scripts found. Create one to get started.
'; countEl.textContent = totalScripts; - - if (favScripts.length > 0) { - favsSection.style.display = ''; - favsList.innerHTML = favScripts.map(s => ` -
  • - ${ICONS.favorite} - ${escapeHtml(s.name)} -
  • - `).join(''); - } else { - favsSection.style.display = 'none'; - } } function renderWelcomeStats() { @@ -2936,8 +2966,9 @@ async function selectScript(relPath) { detailPanel.style.display = ''; // Fill details + const scriptDisplayName = script.name && script.name.trim() ? script.name : script.file.replace('.sh', '').replace(/_/g, ' ').replace(/\b\w/g, c => c.toUpperCase()); document.getElementById('detail-category').textContent = script.category; - document.getElementById('detail-name').textContent = script.name; + document.getElementById('detail-name').textContent = scriptDisplayName; document.getElementById('detail-desc').textContent = script.desc || 'No description provided'; document.getElementById('detail-path').textContent = script.relative_path; @@ -3071,119 +3102,102 @@ function bindEvents() { if (cliSearchInput) { cliSearchInput.addEventListener('input', () => highlightTerminalSearch()); } - function scoreMatch(query, candidate) { - const normalizedQuery = String(query || '').toLowerCase(); - const normalizedCandidate = String(candidate || '').toLowerCase(); - - if (!normalizedQuery) return 0; - if (normalizedCandidate.startsWith(normalizedQuery)) return 2; - if (normalizedCandidate.includes(normalizedQuery)) return 1; - return 0; - } - - function fuzzyMatch(query, str) { - const normalizedQuery = String(query || '').toLowerCase(); - const normalizedStr = String(str || '').toLowerCase(); - - if (!normalizedQuery) return true; - - let queryIndex = 0; - for (let strIndex = 0; strIndex < normalizedStr.length && queryIndex < normalizedQuery.length; strIndex++) { - if (normalizedStr[strIndex] === normalizedQuery[queryIndex]) { - queryIndex++; - } - } - - return queryIndex === normalizedQuery.length; - } - - // Real-Time Sidebar Script Filter Logic (Fixed Variant) + // Real-Time Sidebar Script Filter Logic const scriptSearchBar = document.getElementById('script-search-bar'); if (scriptSearchBar) { scriptSearchBar.addEventListener('input', (e) => { const filterText = e.target.value.toLowerCase().trim(); - const categoryLists = document.querySelectorAll('#category-tree .script-list'); - const categoryContainers = Array.from(document.querySelectorAll('#category-tree > .category-header')); + const categorySections = document.querySelectorAll('#category-tree .category-section'); - if (filterText === '') { - const scriptItems = document.querySelectorAll('#category-tree .script-item'); - scriptItems.forEach(item => { - item.style.display = 'flex'; - item.removeAttribute('data-score'); - }); + categorySections.forEach(section => { + const scriptItems = section.querySelectorAll('.script-item'); + let visibleCount = 0; - categoryLists.forEach(list => { - list.style.maxHeight = ''; - }); + scriptItems.forEach(item => { + if (filterText === '') { + item.style.display = 'flex'; + visibleCount++; + return; + } - categoryContainers.forEach(container => { - container.style.display = ''; + // Read 5 fields + const name = (item.querySelector('.script-item-name')?.textContent || '').toLowerCase(); + const file = (item.getAttribute('data-file') || '').toLowerCase(); + const path = (item.getAttribute('data-path') || '').toLowerCase(); + const tag = (item.getAttribute('data-tag') || '').toLowerCase(); + const desc = (item.getAttribute('data-desc') || '').toLowerCase(); + + if (name.includes(filterText) || + file.includes(filterText) || + path.includes(filterText) || + tag.includes(filterText) || + desc.includes(filterText)) { + item.style.display = 'flex'; + visibleCount++; + } else { + item.style.display = 'none'; + } }); - return; - } - - const scriptItems = Array.from(document.querySelectorAll('#category-tree .script-item')); - const visibleByParent = new Map(); - const bestScoreByParent = new Map(); - - scriptItems.forEach(item => { - const scriptNameEl = item.querySelector('.script-item-name'); - if (!scriptNameEl) return; - - const scriptName = scriptNameEl.textContent.toLowerCase(); - - if (!fuzzyMatch(filterText, scriptName)) { - item.style.display = 'none'; - item.removeAttribute('data-score'); - return; + // Handle category section visibility + if (filterText !== '' && visibleCount === 0) { + section.style.display = 'none'; + } else { + section.style.display = 'block'; } - const score = scoreMatch(filterText, scriptName); - item.dataset.score = String(score); - item.style.display = 'flex'; - - const parent = item.parentElement; - if (!visibleByParent.has(parent)) { - visibleByParent.set(parent, []); + // Handle category list auto-expansion + const list = section.querySelector('.script-list'); + const arrow = section.querySelector('.category-arrow'); + const header = section.querySelector('.category-header'); + if (list) { + if (filterText !== '' && visibleCount > 0) { + list.style.maxHeight = 'none'; + list.classList.remove('collapsed'); + if (arrow) arrow.classList.add('expanded'); + if (header) header.setAttribute('aria-expanded', 'true'); + } else if (filterText === '') { + // Restore state based on state.expandedCategories + const catName = section.dataset.category; + const isExpanded = state.expandedCategories.has(catName); + if (isExpanded) { + list.style.maxHeight = `${scriptItems.length * 44}px`; + list.classList.remove('collapsed'); + if (arrow) arrow.classList.add('expanded'); + if (header) header.setAttribute('aria-expanded', 'true'); + } else { + list.style.maxHeight = '0px'; + list.classList.add('collapsed'); + if (arrow) arrow.classList.remove('expanded'); + if (header) header.setAttribute('aria-expanded', 'false'); + } + } } - visibleByParent.get(parent).push(item); - bestScoreByParent.set(parent, Math.max(bestScoreByParent.get(parent) ?? -1, score)); - }); - - visibleByParent.forEach((items, parent) => { - items.sort((a, b) => Number(b.dataset.score || 0) - Number(a.dataset.score || 0)); - items.forEach(item => parent.appendChild(item)); - }); - - categoryContainers.forEach(container => { - const list = container.querySelector('.script-list'); - const hasVisibleItems = list && visibleByParent.has(list); - container.style.display = hasVisibleItems ? '' : 'none'; }); + }); + } - const rankedCategories = categoryContainers - .map(container => { - const list = container.querySelector('.script-list'); - return { - container, - score: list ? (bestScoreByParent.get(list) ?? -1) : -1 - }; - }) - .filter(entry => entry.score >= 0) - .sort((a, b) => b.score - a.score); - - const tree = document.getElementById('category-tree'); - rankedCategories.forEach(({ container }) => tree.appendChild(container)); - - // Handle category auto-expansion smoothly without resetting terminal CSS - categoryLists.forEach(list => { - list.style.maxHeight = 'none'; - list.classList.remove('collapsed'); - }); + // Collapsible Sidebar Toggle + const btnToggleSidebar = document.getElementById('btn-toggle-sidebar'); + if (btnToggleSidebar) { + btnToggleSidebar.addEventListener('click', () => { + const sidebar = document.getElementById('sidebar'); + const resizerLeft = document.getElementById('resizer-left'); + if (sidebar) sidebar.classList.toggle('collapsed'); + if (resizerLeft) resizerLeft.classList.toggle('collapsed'); }); } + // Font Size Controls + const btnFontDecrease = document.getElementById('btn-font-decrease'); + const btnFontIncrease = document.getElementById('btn-font-increase'); + if (btnFontDecrease) { + btnFontDecrease.addEventListener('click', () => changeTerminalFontSize(-1)); + } + if (btnFontIncrease) { + btnFontIncrease.addEventListener('click', () => changeTerminalFontSize(1)); + } + // ─── THEME TOGGLE ENGINE LAYER ─── const themeToggleBtn = document.getElementById('theme-toggle-btn'); const moonIcon = document.getElementById('theme-icon-moon'); @@ -3216,39 +3230,6 @@ function bindEvents() { }); } - // Real-Time Sidebar Script Filter Logic (Fixed Variant) - const scriptSearchBar = document.getElementById('script-search-bar'); - if (scriptSearchBar) { - scriptSearchBar.addEventListener('input', (e) => { - const filterText = e.target.value.toLowerCase().trim(); - const scriptItems = document.querySelectorAll('#category-tree .script-item'); - - scriptItems.forEach(item => { - const scriptNameEl = item.querySelector('.script-item-name'); - if (!scriptNameEl) return; - - const scriptName = scriptNameEl.textContent.toLowerCase(); - - if (scriptName.includes(filterText)) { - item.style.display = 'flex'; - } else { - item.style.display = 'none'; - } - }); - - // Handle category auto-expansion smoothly without resetting terminal CSS - const categoryLists = document.querySelectorAll('#category-tree .script-list'); - categoryLists.forEach(list => { - if (filterText !== '') { - list.style.maxHeight = 'none'; - list.classList.remove('collapsed'); - } else { - list.style.maxHeight = ''; - } - }); - }); - } - // Terminal Tabs const btnAddTab = document.getElementById('btn-add-tab'); if (btnAddTab) { @@ -3412,7 +3393,7 @@ function bindEvents() { const historyClearBtn = document.getElementById('history-clear-btn'); if (historyClearBtn) { historyClearBtn.addEventListener('click', async () => { - const confirmation = confirm('Are you sure you want to permanently clear your command history log?'); + const confirmation = await showCustomConfirm('Are you sure you want to permanently clear your command history log?', 'Clear History'); if (!confirmation) return; try { @@ -3781,18 +3762,18 @@ function notify(message, type = 'info') { function serializeWorkspace() { const terminalSnapshots = state.terminals.map(id => { - const terminalBody = document.getElementById(`terminal-body-${id}`); + const terminalBody = getTerminalBody(id); return { - id, + id: Number(id), content: terminalBody?.innerHTML || '', pendingInput: document.getElementById('cli-input')?.value || '' }; }); return { - terminals: state.terminals, + terminals: state.terminals.map(Number), terminalSnapshots, - activeTerminalId: state.activeTerminalId, + activeTerminalId: Number(state.activeTerminalId), activeScript: state.activeScript, searchQuery: state.searchQuery, debuggerVisible: @@ -3908,6 +3889,9 @@ function sanitizeWorkspaceSnapshot(data) { } function rebuildTerminalWorkspace(terminals, activeTerminalId, dataSnapshots = []) { + terminals = terminals.map(Number); + activeTerminalId = Number(activeTerminalId); + const tabsContainer = document.getElementById('cli-tabs'); const cliArea = document.getElementById('cli-area'); @@ -3935,19 +3919,10 @@ function rebuildTerminalWorkspace(terminals, activeTerminalId, dataSnapshots = [ tabBtn.dataset.id = id; tabBtn.id = `tab-btn-${id}`; tabBtn.innerHTML = ` - - - - - Terminal ${id} - - - `; + >_ + ${getTerminalDisplayName(id)} + `; tabBtn.onclick = () => switchTerminal(id); - tabBtn.querySelector('.cli-tab-close')?.addEventListener('click', (e) => { - e.stopPropagation(); - closeTerminal(id); - }); tabsContainer.insertBefore(tabBtn, document.getElementById('btn-add-tab')); const bodyContainer = document.createElement('div'); @@ -3956,17 +3931,18 @@ function rebuildTerminalWorkspace(terminals, activeTerminalId, dataSnapshots = [ bodyContainer.style.display = 'none'; bodyContainer.setAttribute('role', 'log'); bodyContainer.setAttribute('aria-live', 'polite'); - const snapshot = dataSnapshots?.find(snap => snap.id === id); - bodyContainer.innerHTML = snapshot?.content || - `
    - $ - Restored terminal session. -
    `; + const snapshot = dataSnapshots?.find(snap => Number(snap.id) === id); + bodyContainer.innerHTML = snapshot?.content || '
    $Restored terminal session.
    Ready for interaction.
    '; cliArea.insertBefore(bodyContainer, document.querySelector('.cli-input-bar')); state.terminals.push(id); }); + renumberTabs(); + terminals.forEach(id => { + updateTabStatusIndicator(id); + }); + // Restore pending input from first snapshot const firstSnapshot = dataSnapshots?.[0]; if (firstSnapshot?.pendingInput) { @@ -4127,7 +4103,7 @@ async function loadWorkspaceProfile(name) { } async function deleteWorkspaceProfile(name) { - const confirmed = confirm(`Delete workspace profile "${name}"?`); + const confirmed = await showCustomConfirm(`Delete workspace profile "${name}"?`, 'Delete Profile'); if (!confirmed) { return; } diff --git a/ui/index.html b/ui/index.html index f42f45b..3709f70 100644 --- a/ui/index.html +++ b/ui/index.html @@ -27,6 +27,11 @@

    DevShell

    v1.0 +
    @@ -164,13 +169,9 @@

    Scripts

    -
    - - - - - - Terminal 1 +
    + >_ + Terminal 1
    + +
    -
    -
    - $ Welcome to DevShell. Select a - script to run, or type a command below. -
    -
    +
    $Welcome to DevShell.
    Select a script to run, or type a command below.
    $ Execution Analytics
    -

    Total Runs

    -
    +
    +

    Total Runs

    + +
    +
    0
    +
    +
    +
    +
    All executions
    -

    Successful

    -
    +
    +

    Successful

    + +
    +
    0
    +
    +
    +
    +
    0% rate
    -

    Failed

    -
    +
    +

    Failed

    + +
    +
    0
    +
    +
    +
    +
    0% rate
    -

    Average Runtime

    -
    +
    +

    Average Runtime

    + +
    +
    0s
    +
    +
    +
    +
    Latency average
    -
    -

    Top Executed Scripts

    -
    -
    -
    -

    Slowest Executions

    -
    -
    -
    -

    Recent Failures

    -
    +
    +
    +

    Top Executed Scripts

    +
    +
    +
    +

    Slowest Executions

    +
    +
    +
    +

    Recent Failures

    +
    +
    @@ -1002,6 +1034,29 @@

    Workspace Profiles

    + + + \ No newline at end of file diff --git a/ui/style.css b/ui/style.css index 5d6b79d..d8c4a43 100644 --- a/ui/style.css +++ b/ui/style.css @@ -1,2980 +1,3377 @@ -/* ═══════════════════════════════════════════════════════════ - DevShell — Premium Dark Theme (CLI + Analysis Layout) - ═══════════════════════════════════════════════════════════ */ - -:root { - /* Core palette */ - --bg-primary: #09090b; - --bg-secondary: #0f1115; - --bg-tertiary: #18181b; - --bg-card: #1f1f22; - --bg-hover: #27272a; - --bg-active: #3f3f46; - - /* Accent colors */ - --accent: #10b981; - --accent-dim: #059669; - --accent-glow: rgba(16, 185, 129, 0.15); - --accent-glow-strong: rgba(16, 185, 129, 0.3); - --accent-blue: #3b82f6; - --accent-purple: #8b5cf6; - --accent-orange: #f59e0b; - --accent-red: #ef4444; - --accent-yellow: #eab308; - - /* Text */ - --text-primary: #f8fafc; - --text-secondary: #94a3b8; - --text-muted: #64748b; - --text-accent: #10b981; - - /* Borders */ - --border: #27272a; - --border-bright: #3f3f46; - --border-glow: rgba(16, 185, 129, 0.2); - - /* Sizing */ - --sidebar-width: 250px; - --analysis-width: 400px; - --header-height: 60px; - --radius: 6px; - --radius-lg: 10px; - - /* Typography */ - --font-ui: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; - --font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace; - - /* Transitions */ - --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); - --transition: 250ms cubic-bezier(0.4, 0, 0.2, 1); - --transition-slow: 400ms cubic-bezier(0.4, 0, 0.2, 1); -} - -/* ─── Reset ───────────────────────────────────────────── */ -*, -*::before, -*::after { - margin: 0; - padding: 0; - box-sizing: border-box; -} - -html, -body { - height: 100%; - overflow: hidden; - font-family: var(--font-ui); - background: var(--bg-primary); - color: var(--text-primary); - font-size: 14px; - line-height: 1.5; - -webkit-font-smoothing: antialiased; -} - -::-webkit-scrollbar { - width: 6px; - height: 6px; -} - -::-webkit-scrollbar-track { - background: transparent; -} - -::-webkit-scrollbar-thumb { - background: var(--border-bright); - border-radius: 3px; -} - -::-webkit-scrollbar-thumb:hover { - background: var(--text-muted); -} - -/* ─── Header ──────────────────────────────────────────── */ -#app-header { - height: var(--header-height); - background: var(--bg-secondary); - border-bottom: 1px solid var(--border); - display: flex; - align-items: center; - justify-content: space-between; - padding: 0 24px; - position: relative; - z-index: 100; -} - -.header-left { - display: flex; - align-items: center; - gap: 12px; -} - -.logo { - display: flex; - align-items: center; - gap: 10px; -} - -.logo-icon { - color: var(--accent); - filter: drop-shadow(0 0 8px var(--accent-glow-strong)); -} - -.logo h1 { - font-size: 18px; - font-weight: 700; - letter-spacing: -0.5px; - color: var(--text-primary); -} - -.logo h1 .accent { - color: var(--accent); -} - -.version-badge { - font-size: 11px; - color: var(--text-muted); - background: var(--bg-tertiary); - padding: 2px 8px; - border-radius: 20px; - border: 1px solid var(--border); - font-weight: 500; -} - -.header-center { - flex: 1; - max-width: 480px; - margin: 0 24px; -} - -.search-container { - position: relative; - display: flex; - align-items: center; - background: var(--bg-tertiary); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 0 12px; - transition: var(--transition); -} - -.search-container:focus-within { - border-color: var(--accent-dim); - box-shadow: 0 0 0 3px var(--accent-glow); -} - -.search-icon { - color: var(--text-muted); - margin-right: 8px; -} - -#search-input { - flex: 1; - background: transparent; - border: none; - outline: none; - color: var(--text-primary); - font-family: var(--font-ui); - font-size: 13px; - padding: 8px 0; -} - -#search-input::placeholder { - color: var(--text-muted); -} - -.search-kbd { - font-family: var(--font-mono); - font-size: 10px; - color: var(--text-muted); - background: var(--bg-primary); - padding: 2px 6px; - border-radius: 4px; - border: 1px solid var(--border); -} - -.header-right { - display: flex; - align-items: center; - gap: 12px; -} - -/* ─── Buttons ─────────────────────────────────────────── */ -.btn-icon { - width: 32px; - height: 32px; - display: flex; - align-items: center; - justify-content: center; - background: transparent; - border: 1px solid transparent; - border-radius: var(--radius); - color: var(--text-secondary); - cursor: pointer; - transition: var(--transition-fast); -} - -.btn-icon:hover { - background: var(--bg-hover); - color: var(--text-primary); -} - -.btn { - display: inline-flex; - align-items: center; - justify-content: center; - gap: 8px; - padding: 8px 16px; - border: 1px solid var(--border); - border-radius: var(--radius); - font-family: var(--font-ui); - font-size: 13px; - font-weight: 500; - cursor: pointer; - transition: var(--transition-fast); - background: var(--bg-tertiary); - color: var(--text-primary); -} - -.btn:hover { - background: var(--bg-hover); -} - -.btn-action { - padding: 6px 12px; - font-size: 12px; -} - -.btn-action:hover { - color: var(--text-primary); - border-color: var(--border-bright); -} - -.btn-danger:hover { - color: var(--accent-red); - border-color: rgba(239, 68, 68, 0.3); - background: rgba(239, 68, 68, 0.1); -} - -.btn-run { - background: var(--accent); - color: #fff; - border-color: var(--accent); - font-weight: 600; -} - -.btn-run:hover { - background: var(--accent-dim); - border-color: var(--accent-dim); -} - -.btn-run.running { - background: var(--accent-orange); - border-color: var(--accent-orange); - animation: btn-pulse 1.5s infinite; -} - -.btn-run.abort, -.btn-run.aborting { - background: var(--accent-red); - border-color: var(--accent-red); - animation: btn-pulse 1.5s infinite; -} - -.btn-run.abort:hover, -.btn-run.aborting:hover { - background: #dc2626; - border-color: #dc2626; -} - -@keyframes btn-pulse { - - 0%, - 100% { - opacity: 1; - } - - 50% { - opacity: 0.8; - } -} - -.btn-fav.active { - color: var(--accent-yellow); -} - -.btn-fav.active svg { - fill: var(--accent-yellow); - stroke: var(--accent-yellow); -} - -.btn-save { - background: var(--accent); - color: #fff; - border-color: var(--accent); - font-weight: 600; -} - -.btn-save:hover { - background: var(--accent-dim); -} - -/* ─── Main Layout ─────────────────────────────────────── */ -#app-main { - display: flex; - height: calc(100vh - var(--header-height)); -} - -#left-panel { - flex: 1; - display: flex; - min-width: 0; -} - -/* ─── Resizers ────────────────────────────────────────── */ -.resizer { - width: 6px; - background: transparent; - cursor: col-resize; - transition: background 0.2s ease; - z-index: 10; - position: relative; -} - -.resizer::after { - content: ''; - position: absolute; - top: 0; - bottom: 0; - left: 2px; - width: 2px; - background: var(--border); - transition: background 0.2s ease; -} - -.resizer:hover::after, -.resizer.resizing::after { - background: var(--accent); -} - -/* ─── Sidebar ─────────────────────────────────────────── */ -#sidebar { - width: var(--sidebar-width); - min-width: var(--sidebar-width); - background: var(--bg-secondary); - border-right: 1px solid var(--border); - display: flex; - flex-direction: column; -} - -.sidebar-header { - padding: 16px 20px; - display: flex; - align-items: center; - justify-content: space-between; - border-bottom: 1px solid var(--border); -} - -.sidebar-title { - display: flex; - align-items: center; - gap: 10px; - color: var(--text-secondary); -} - -.sidebar-title h2 { - font-size: 12px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -.script-count { - font-size: 11px; - font-weight: 600; - color: var(--accent); - background: var(--accent-glow); - padding: 2px 8px; - border-radius: 20px; -} - -.sidebar-section { - flex: 1; - overflow-y: auto; - padding: 12px 0; -} - -.section-title { - display: flex; - align-items: center; - gap: 8px; - padding: 8px 20px; - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; - color: var(--text-muted); -} - -/* Category tree */ -.category { - margin-bottom: 4px; -} - -.category-header { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 20px; - cursor: pointer; - transition: var(--transition-fast); - user-select: none; - color: var(--text-secondary); -} - -.category-header:hover { - background: var(--bg-hover); - color: var(--text-primary); -} - -.category-arrow { - display: flex; - align-items: center; - justify-content: center; - transition: transform var(--transition-fast); - color: var(--text-muted); - width: 14px; -} - -.category-arrow.expanded { - transform: rotate(90deg); -} - -.category-icon { - display: flex; - align-items: center; - justify-content: center; -} - -.category-name { - font-size: 13px; - font-weight: 500; - flex: 1; -} - -.category-count { - font-size: 11px; - color: var(--text-muted); - background: var(--bg-tertiary); - padding: 2px 6px; - border-radius: 4px; -} - -/* Script list */ -.script-list { - list-style: none; - overflow: hidden; - transition: max-height var(--transition-slow); -} - -.script-list.collapsed { - max-height: 0 !important; -} - -.script-item { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 20px 8px 44px; - cursor: pointer; - transition: var(--transition-fast); - position: relative; - color: var(--text-secondary); -} - -.script-item::before { - content: ''; - position: absolute; - left: 26px; - top: 0; - bottom: 0; - width: 1px; - background: var(--border); -} - -.script-item:hover { - background: var(--bg-hover); - color: var(--text-primary); -} - -.script-item.active { - background: var(--bg-active); - color: var(--text-primary); -} - -.script-item.active::after { - content: ''; - position: absolute; - left: 0; - top: 0; - bottom: 0; - width: 3px; - background: var(--accent); -} - -.script-item-icon { - display: flex; - align-items: center; - justify-content: center; - color: var(--text-muted); -} - -.script-item.active .script-item-icon { - color: var(--accent); -} - -.script-item-name { - font-size: 13px; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - flex: 1; -} - -.script-item-fav { - display: flex; - align-items: center; - justify-content: center; - opacity: 0; - transition: var(--transition-fast); - color: var(--text-muted); -} - -.script-item:hover .script-item-fav, -.script-item-fav.visible { - opacity: 1; -} - -.script-item-fav.visible svg { - fill: var(--accent-yellow); - stroke: var(--accent-yellow); -} - -/* ─── CLI Terminal ────────────────────────────────────── */ -#cli-area { - flex: 1; - display: flex; - flex-direction: column; - min-width: 0; - background: #09090b; -} - -/* ─── Terminal Tabs ───────────────────────────────────── */ -.cli-tabs { - display: flex; - align-items: center; - background: var(--bg-tertiary); - border-bottom: 1px solid var(--border); - padding: 0 12px; - gap: 4px; - height: 36px; - overflow-x: auto; -} - -.cli-tabs::-webkit-scrollbar { - height: 0; -} - -.cli-tab { - display: flex; - align-items: center; - gap: 8px; - padding: 0 12px; - height: 28px; - background: transparent; - border: 1px solid transparent; - border-radius: var(--radius) var(--radius) 0 0; - font-size: 11px; - font-weight: 500; - color: var(--text-muted); - cursor: pointer; - transition: var(--transition-fast); - white-space: nowrap; -} - -.cli-tab:hover { - background: var(--bg-hover); - color: var(--text-primary); -} - -.cli-tab.active { - background: var(--bg-primary); - border-color: var(--border); - border-bottom-color: transparent; - color: var(--accent); -} - -.cli-tab-close { - width: 16px; - height: 16px; - display: inline-flex; - align-items: center; - justify-content: center; - border: none; - background: transparent; - color: var(--text-muted); - border-radius: 4px; - cursor: pointer; -} - -.cli-tab-close:hover { - background: rgba(239, 68, 68, 0.1); - color: var(--accent-red); -} - -.btn-add-tab { - width: 24px; - height: 24px; - margin-left: 4px; -} - -/* ─── Terminal Search ─────────────────────────────────── */ -.terminal-search-wrapper { - position: relative; - display: flex; - align-items: center; -} - -#cli-search-input { - background: var(--bg-tertiary); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 4px 6px 4px 26px; - color: var(--text-primary); - font-size: 11px; - width: 140px; - outline: none; - transition: var(--transition-fast); -} - -#cli-search-input:focus { - width: 180px; - border-color: var(--accent); -} - -#cli-search-results { - position: absolute; - right: 8px; - font-size: 10px; - color: var(--text-muted); - pointer-events: none; -} - -.highlight { - background: var(--accent-yellow); - color: #000; - border-radius: 2px; -} - -.cli-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 10px 20px; - background: var(--bg-primary); - border-bottom: 1px solid var(--border); -} - -.cli-header-left { - display: flex; - align-items: center; - gap: 16px; -} - -.cli-header-right { - display: flex; - align-items: center; - gap: 12px; -} - -.cli-dots { - display: flex; - gap: 4px; -} - -.dot { - width: 9px; - height: 9px; - border-radius: 50%; -} - -.dot-red { - background: #ed655a; -} - -.dot-yellow { - background: #e0c04c; -} - -.dot-green { - background: #72c05b; -} - -.cli-title { - display: flex; - align-items: center; - font-size: 12px; - font-weight: 500; - color: var(--text-secondary); -} - -.cli-body { - flex: 1; - overflow-y: auto; - padding: 20px; - font-family: var(--font-mono); - font-size: 13px; - line-height: 1.6; - color: #e2e8f0; - white-space: pre-wrap; - word-break: break-word; -} - -.cli-welcome { - color: var(--text-muted); - margin-bottom: 12px; -} - -.cli-welcome-text { - font-style: italic; -} - -.cli-prompt { - color: var(--accent); - font-weight: 600; - margin-right: 8px; -} - -.cli-input-bar { - display: flex; - align-items: center; - gap: 12px; - padding: 12px 20px; - background: var(--bg-tertiary); - border-top: 1px solid var(--border); -} - -.cli-prompt-input { - color: var(--accent); - font-family: var(--font-mono); - font-weight: 600; - font-size: 14px; -} - -#cli-input { - flex: 1; - background: transparent; - border: none; - outline: none; - color: var(--text-primary); - font-family: var(--font-mono); - font-size: 13px; - caret-color: var(--accent); -} - -#cli-input::placeholder { - color: var(--text-muted); -} - -.btn-run-cmd { - display: flex; - align-items: center; - justify-content: center; - width: 48px; - height: 28px; - background: var(--accent); - color: black; - border: none; - border-radius: var(--radius); - cursor: pointer; - transition: var(--transition-fast); -} - -.btn-run-cmd:hover { - background: var(--accent-dim); -} - -/* ─── Analysis Panel (Right Sidebar) ─────────────────── */ -#analysis-panel { - width: var(--analysis-width); - min-width: var(--analysis-width); - background: var(--bg-secondary); - border-left: 1px solid var(--border); - display: flex; - flex-direction: column; - overflow-y: auto; -} - -.analysis-state { - flex: 1; - display: flex; - flex-direction: column; -} - -/* Empty / Welcome */ -.analysis-empty { - flex: 1; - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - text-align: center; - padding: 40px; - gap: 16px; -} - -.analysis-empty-icon { - color: var(--border-bright); - margin-bottom: 8px; -} - -.analysis-empty h3 { - font-size: 18px; - font-weight: 600; - color: var(--text-primary); -} - -.analysis-empty p { - color: var(--text-muted); - font-size: 13px; - line-height: 1.5; - max-width: 280px; - margin: 0 auto; -} - -.welcome-stats { - display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 12px; - margin-top: 24px; - width: 100%; -} - -.stat-card { - background: var(--bg-tertiary); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 16px 12px; - text-align: center; -} - -.stat-value { - font-size: 20px; - font-weight: 600; - color: var(--text-primary); - font-family: var(--font-mono); -} - -.stat-label { - font-size: 11px; - color: var(--text-muted); - margin-top: 4px; -} - -/* ─── Analysis Sections ──────────────────────────────── */ -.analysis-section { - border-bottom: 1px solid var(--border); - background: var(--bg-primary); - margin: 16px; - border-radius: var(--radius-lg); - border: 1px solid var(--border); - overflow: hidden; -} - -.analysis-section-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 12px 16px; - background: var(--bg-tertiary); - border-bottom: 1px solid var(--border); -} - -.section-badge { - display: flex; - align-items: center; - gap: 8px; - font-size: 12px; - font-weight: 600; - color: var(--text-primary); -} - -.section-badge svg { - color: var(--text-muted); -} - -.btn-close-detail { - color: var(--text-muted); -} - -/* Script Info Card */ -.script-info-body { - padding: 20px; - display: flex; - flex-direction: column; - gap: 12px; -} - -.script-category-badge { - font-size: 10px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; - color: var(--accent); - background: var(--accent-glow); - padding: 4px 10px; - border-radius: 4px; - width: fit-content; -} - -.script-info-body h2 { - font-size: 20px; - font-weight: 600; - color: var(--text-primary); - line-height: 1.3; -} - -.script-description { - color: var(--text-secondary); - font-size: 13px; - line-height: 1.6; -} - -.tag-list { - display: flex; - gap: 8px; - flex-wrap: wrap; - margin-top: 4px; -} - -.tag { - font-size: 11px; - color: var(--text-primary); - background: var(--bg-tertiary); - padding: 4px 10px; - border-radius: 4px; - border: 1px solid var(--border); -} - -.script-actions { - display: flex; - flex-wrap: wrap; - gap: 8px; - align-items: center; - margin-top: 12px; - padding-top: 16px; - border-top: 1px dashed var(--border); -} - -/* ─── Code Panel ──────────────────────────────────────── */ -.file-path { - font-family: var(--font-mono); - font-size: 11px; - color: var(--text-muted); - max-width: 200px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -.code-block { - padding: 16px; - overflow-x: auto; - font-family: var(--font-mono); - font-size: 12px; - line-height: 1.6; - color: var(--text-primary); - background: var(--bg-primary); - max-height: 320px; - overflow-y: auto; -} - -/* ─── Resource Monitor ────────────────────────────────── */ -.resource-grid { - display: grid; - grid-template-columns: repeat(2, 1fr); -} - -.resource-card { - padding: 16px; - display: flex; - align-items: flex-start; - gap: 12px; - border-right: 1px solid var(--border); - border-bottom: 1px solid var(--border); -} - -.resource-card:nth-child(even) { - border-right: none; -} - -.resource-card:nth-last-child(-n+2) { - border-bottom: none; -} - -.resource-icon { - color: var(--text-muted); - flex-shrink: 0; -} - -.resource-info { - flex: 1; - display: flex; - flex-direction: column; - gap: 4px; -} - -.resource-label { - font-size: 11px; - font-weight: 500; - color: var(--text-secondary); -} - -.resource-value { - font-family: var(--font-mono); - font-size: 16px; - font-weight: 600; - color: var(--text-primary); -} - -.resource-bar { - width: 100%; - height: 4px; - background: var(--bg-tertiary); - border-radius: 2px; - overflow: hidden; - margin-top: 4px; -} - -.resource-bar-fill { - height: 100%; - border-radius: 2px; - background: var(--accent); - transition: width var(--transition-slow); - width: 0%; -} - -/* ─── Modal ───────────────────────────────────────────── */ -.modal-overlay { - position: fixed; - inset: 0; - background: rgba(0, 0, 0, 0.6); - backdrop-filter: blur(4px); - display: flex; - align-items: center; - justify-content: center; - z-index: 1000; - opacity: 0; - visibility: hidden; - transition: var(--transition); -} - -.modal-overlay.active { - opacity: 1; - visibility: visible; -} - -.modal { - background: var(--bg-secondary); - border: 1px solid var(--border); - border-radius: var(--radius-lg); - width: 600px; - max-width: 90vw; - display: flex; - flex-direction: column; - transform: translateY(10px) scale(0.98); - transition: var(--transition); - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4); -} - -.modal-overlay.active .modal { - transform: translateY(0) scale(1); -} - -.modal-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 20px 24px; - border-bottom: 1px solid var(--border); -} - -.modal-header h2 { - font-size: 16px; - font-weight: 600; - color: var(--text-primary); -} - -.modal-body { - padding: 24px; - display: flex; - flex-direction: column; - gap: 20px; -} - -.form-row { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 20px; -} - -.form-group { - display: flex; - flex-direction: column; - gap: 8px; -} - -.form-group label { - font-size: 12px; - font-weight: 500; - color: var(--text-secondary); -} - -.form-group input, -.form-group textarea { - background: var(--bg-tertiary); - border: 1px solid var(--border); - border-radius: var(--radius); - padding: 10px 14px; - color: var(--text-primary); - font-family: var(--font-ui); - font-size: 13px; - outline: none; - transition: var(--transition-fast); -} - -.form-group input:focus, -.form-group textarea:focus { - border-color: var(--accent); - box-shadow: 0 0 0 2px var(--accent-glow); -} - -.form-group textarea { - font-family: var(--font-mono); - min-height: 200px; - resize: vertical; - line-height: 1.5; -} - -.modal-footer { - display: flex; - justify-content: flex-end; - gap: 12px; - padding: 20px 24px; - border-top: 1px solid var(--border); - background: var(--bg-tertiary); - border-radius: 0 0 var(--radius-lg) var(--radius-lg); -} - -/* Execution History Modal */ -.history-modal { - width: min(980px, 94vw); -} - -.history-modal .modal-body { - gap: 16px; - max-height: min(72vh, 760px); -} - -.history-modal-subtitle { - margin-top: 4px; - color: var(--text-muted); - font-size: 12px; -} - -.history-header-actions { - display: flex; - align-items: center; - gap: 8px; -} - -.history-toolbar { - display: flex; - flex-wrap: wrap; - gap: 14px; - align-items: flex-end; - justify-content: space-between; -} - -.history-search-group { - flex: 1; - min-width: 280px; -} - -.history-filter-group { - display: flex; - flex-wrap: wrap; - gap: 8px; -} - -.history-filter { - border: 1px solid var(--border); - background: var(--bg-tertiary); - color: var(--text-secondary); - border-radius: var(--radius); - padding: 8px 12px; - font-size: 12px; - cursor: pointer; - transition: var(--transition-fast); -} - -.history-filter:hover { - color: var(--text-primary); - border-color: var(--border-bright); -} - -.history-filter.active { - background: rgba(16, 185, 129, 0.12); - color: var(--accent); - border-color: rgba(16, 185, 129, 0.35); -} - -.history-summary { - display: flex; - flex-wrap: wrap; - gap: 10px; -} - -.history-summary-item { - display: inline-flex; - align-items: center; - gap: 6px; - padding: 6px 10px; - border-radius: 999px; - background: var(--bg-tertiary); - border: 1px solid var(--border); - color: var(--text-secondary); - font-size: 12px; -} - -.history-summary-item strong { - color: var(--text-primary); -} - -.history-summary-item.failed strong { - color: var(--accent-red); -} - -.history-list { - display: flex; - flex-direction: column; - gap: 12px; - overflow-y: auto; - max-height: 52vh; - padding-right: 4px; -} - -.history-entry { - border: 1px solid var(--border); - border-radius: var(--radius-lg); - background: linear-gradient(180deg, rgba(31, 31, 34, 0.96), rgba(15, 17, 21, 0.98)); - padding: 14px 16px; - box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18); -} - -.history-entry.success { - border-color: rgba(16, 185, 129, 0.18); -} - -.history-entry.failed { - border-color: rgba(239, 68, 68, 0.25); - background: linear-gradient(180deg, rgba(40, 20, 23, 0.96), rgba(18, 15, 17, 0.98)); -} - -.history-entry-head { - display: flex; - flex-wrap: wrap; - gap: 10px; - align-items: flex-start; - justify-content: space-between; -} - -.history-entry-title-row, -.history-entry-meta { - display: flex; - flex-wrap: wrap; - align-items: center; - gap: 8px; -} - -.history-entry-meta { - justify-content: flex-end; - color: var(--text-muted); - font-size: 11px; -} - -.history-entry-status, -.history-entry-kind, -.history-entry-time { - font-size: 11px; - padding: 3px 8px; - border-radius: 999px; - border: 1px solid var(--border); - background: var(--bg-secondary); -} - -.history-entry-status.success { - color: var(--accent); - border-color: rgba(16, 185, 129, 0.2); -} - -.history-entry-status.failed { - color: var(--accent-red); - border-color: rgba(239, 68, 68, 0.28); -} - -.history-entry-kind { - color: var(--text-secondary); -} - -.history-entry-time { - color: var(--text-muted); - font-family: var(--font-mono); -} - -.history-entry-command { - margin-top: 10px; - font-family: var(--font-mono); - color: var(--text-primary); - word-break: break-word; -} - -.history-entry-error { - margin-top: 8px; - color: var(--accent-red); - font-family: var(--font-mono); - white-space: pre-wrap; - word-break: break-word; -} - -.history-entry-excerpt { - margin-top: 10px; - color: var(--text-secondary); - font-family: var(--font-mono); - font-size: 12px; - white-space: pre-wrap; - word-break: break-word; -} - -.history-excerpt-empty { - color: var(--text-muted); -} - -.history-empty-state { - padding: 24px; - text-align: center; - color: var(--text-muted); - border: 1px dashed var(--border); - border-radius: var(--radius-lg); - background: rgba(15, 17, 21, 0.65); -} - -/* ─── Notifications ──────────────────────────────────── */ -#notification-container { - position: fixed; - top: 20px; - right: 20px; - z-index: 9999; - - display: flex; - flex-direction: column; - gap: 12px; - - pointer-events: none; - max-width: 380px; -} - -.notification { - display: flex; - align-items: flex-start; - gap: 12px; - - background: var(--bg-card); - border: 1px solid var(--border); - border-left: 4px solid var(--accent); - - color: var(--text-primary); - - border-radius: var(--radius-lg); - padding: 14px 16px; - - box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35); - - animation: notificationSlideIn 220ms ease forwards; - - pointer-events: auto; - - min-width: 280px; - max-width: 100%; -} - -.notification.success { - border-left-color: var(--accent); -} - -.notification.error { - border-left-color: var(--accent-red); -} - -.notification.warning { - border-left-color: var(--accent-yellow); -} - -.notification.info { - border-left-color: var(--accent-blue); -} - -.notification-content { - flex: 1; - min-width: 0; -} - -.notification-message { - font-size: 13px; - line-height: 1.5; - word-break: break-word; -} - -.notification-close { - background: transparent; - border: none; - - color: var(--text-muted); - - cursor: pointer; - - width: 24px; - height: 24px; - - display: flex; - align-items: center; - justify-content: center; - - border-radius: 4px; - - transition: var(--transition-fast); - - flex-shrink: 0; -} - -.notification-close:hover { - background: var(--bg-hover); - color: var(--text-primary); -} - -.notification-close:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -.notification.removing { - animation: notificationSlideOut 180ms ease forwards; -} - -@keyframes notificationSlideIn { - from { - opacity: 0; - transform: translateX(18px) translateY(-4px); - } - - to { - opacity: 1; - transform: translateX(0) translateY(0); - } -} - -@keyframes notificationSlideOut { - from { - opacity: 1; - transform: translateX(0); - } - - to { - opacity: 0; - transform: translateX(20px); - } -} - -@media (prefers-reduced-motion: reduce) { - - .notification, - .notification.removing { - animation: none; - } -} - -/* ─── Utility & CLI text ──────────────────────────────── */ -.accent { - color: var(--accent); -} - -.cli-output-block { - margin: 4px 0; - padding: 4px 0; - white-space: pre-wrap; - word-break: break-word; -} - -.cli-output-block.cmd-line { - color: var(--accent); - font-weight: 600; -} - -.cli-output-block.stdout { - color: var(--text-primary); -} - -.cli-output-block.stderr { - color: var(--accent-red); -} - -.cli-output-block.info { - color: var(--text-secondary); -} - -.cli-output-block.success { - color: var(--accent); -} - -.cli-output-block.error { - color: var(--accent-red); -} - -.cli-output-block.system { - color: var(--accent-blue); -} - -/* Run Status */ -.run-status { - font-size: 11px; - font-weight: 500; -} - -.run-status.success { - color: var(--accent); -} - -.run-status.error { - color: var(--accent-red); -} - -.run-status.running { - color: var(--accent-orange); - animation: pulse 1.5s infinite; -} - -@keyframes pulse { - - 0%, - 100% { - opacity: 1; - } - - 50% { - opacity: 0.6; - } -} - -/* ─── Responsive ──────────────────────────────────────── */ -@media (max-width: 1100px) { - #analysis-panel { - width: 340px; - min-width: 340px; - } -} - -@media (max-width: 900px) { - #sidebar { - width: 220px; - min-width: 220px; - } - - #analysis-panel { - width: 300px; - min-width: 300px; - } - - .header-center { - display: none; - } -} - -@media (max-width: 768px) { - - #sidebar, - #analysis-panel { - display: none; - } -} - -/* ─── Debugger Console ────────────────────────────────── */ -.debugger-console { - position: fixed; - bottom: 0; - left: 0; - right: 0; - height: 280px; - background: var(--bg-secondary); - border-top: 1px solid var(--border); - display: flex; - flex-direction: column; - z-index: 200; - transform: translateY(100%); - transition: transform var(--transition-slow); - box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.3); -} - -.debugger-console.open { - transform: translateY(0); -} - -.debugger-resize-handle { - height: 5px; - background: transparent; - cursor: ns-resize; - position: relative; - flex-shrink: 0; -} - -.debugger-resize-handle::after { - content: ''; - position: absolute; - left: 50%; - top: 1px; - transform: translateX(-50%); - width: 40px; - height: 3px; - background: var(--border-bright); - border-radius: 3px; - transition: background var(--transition-fast); -} - -.debugger-resize-handle:hover::after { - background: var(--accent); -} - -.debugger-header { - display: flex; - align-items: center; - justify-content: space-between; - padding: 6px 16px; - background: var(--bg-tertiary); - border-bottom: 1px solid var(--border); - flex-shrink: 0; -} - -.debugger-header-left { - display: flex; - align-items: center; - gap: 16px; -} - -.debugger-title { - display: flex; - align-items: center; - gap: 8px; - font-size: 12px; - font-weight: 600; - color: var(--text-primary); - white-space: nowrap; -} - -.debugger-title svg { - color: var(--text-muted); -} - -.debugger-filter-tabs { - display: flex; - align-items: center; - gap: 2px; -} - -.debugger-filter-tab { - padding: 3px 10px; - border: none; - background: transparent; - color: var(--text-muted); - font-family: var(--font-ui); - font-size: 11px; - font-weight: 500; - border-radius: 4px; - cursor: pointer; - transition: var(--transition-fast); - white-space: nowrap; -} - -.debugger-filter-tab:hover { - background: var(--bg-hover); - color: var(--text-primary); -} - -.debugger-filter-tab.active { - background: var(--bg-active); - color: var(--text-primary); -} - -.debugger-filter-tab[data-filter="error"].has-entries { - color: var(--accent-red); -} - -.debugger-filter-tab[data-filter="warn"].has-entries { - color: var(--accent-orange); -} - -.debugger-header-right { - display: flex; - align-items: center; - gap: 8px; -} - -.debugger-log-count { - font-size: 11px; - color: var(--text-muted); - font-family: var(--font-mono); - font-weight: 500; - padding-right: 4px; -} - -.debugger-body { - flex: 1; - overflow-y: auto; - padding: 8px 0; - font-family: var(--font-mono); - font-size: 12px; - line-height: 1.5; -} - -.debugger-welcome { - display: flex; - align-items: center; - gap: 10px; - padding: 12px 16px; - color: var(--text-muted); - font-size: 12px; - font-family: var(--font-ui); -} - -.debugger-welcome-icon { - flex-shrink: 0; - display: flex; - align-items: center; - color: var(--accent-blue); -} - -/* Debugger log entries */ -.debugger-entry { - display: flex; - align-items: flex-start; - gap: 10px; - padding: 4px 16px; - border-bottom: 1px solid rgba(39, 39, 42, 0.5); - transition: background var(--transition-fast); - animation: debugEntryIn 200ms ease forwards; -} - -@keyframes debugEntryIn { - from { - opacity: 0; - transform: translateX(-4px); - } - - to { - opacity: 1; - transform: translateX(0); - } -} - -.debugger-entry:hover { - background: var(--bg-hover); -} - -.debugger-entry.hidden { - display: none; -} - -.debugger-entry-icon { - flex-shrink: 0; - width: 16px; - height: 16px; - display: flex; - align-items: center; - justify-content: center; - margin-top: 2px; -} - -.debugger-entry-time { - flex-shrink: 0; - font-size: 10px; - color: var(--text-muted); - min-width: 58px; - margin-top: 2px; - font-family: var(--font-mono); -} - -.debugger-entry-content { - flex: 1; - word-break: break-all; - white-space: pre-wrap; - min-width: 0; -} - -.debugger-entry-source { - flex-shrink: 0; - font-size: 10px; - color: var(--text-muted); - margin-top: 2px; - max-width: 120px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} - -/* Entry type styling */ -.debugger-entry.log { - color: var(--text-primary); -} - -.debugger-entry.log .debugger-entry-icon { - color: var(--text-muted); -} - -.debugger-entry.warn { - color: var(--accent-orange); - background: rgba(245, 158, 11, 0.04); -} - -.debugger-entry.warn .debugger-entry-icon { - color: var(--accent-orange); -} - -.debugger-entry.error { - color: var(--accent-red); - background: rgba(239, 68, 68, 0.04); -} - -.debugger-entry.error .debugger-entry-icon { - color: var(--accent-red); -} - -.debugger-entry.info { - color: var(--accent-blue); -} - -.debugger-entry.info .debugger-entry-icon { - color: var(--accent-blue); -} - -.debugger-entry.network { - color: var(--accent-purple); -} - -.debugger-entry.network .debugger-entry-icon { - color: var(--accent-purple); -} - -.debugger-entry.result { - color: var(--accent); - background: rgba(16, 185, 129, 0.04); -} - -.debugger-entry.result .debugger-entry-icon { - color: var(--accent); -} - -.debugger-entry.input { - color: var(--accent-blue); -} - -.debugger-entry.input .debugger-entry-icon { - color: var(--accent-blue); -} - -/* Debugger input area */ -.debugger-input-area { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 16px; - background: var(--bg-primary); - border-top: 1px solid var(--border); - flex-shrink: 0; -} - -.debugger-input-prompt { - color: var(--accent-blue); - font-family: var(--font-mono); - font-weight: 700; - font-size: 16px; - line-height: 1; -} - -.debugger-input-wrapper { - position: relative; - flex: 1; -} - -#debugger-input { - width: 100%; - background: transparent; - border: none; - outline: none; - color: var(--text-primary); - font-family: var(--font-mono); - font-size: 12px; - caret-color: var(--accent-blue); - padding: 4px 0; -} - -#debugger-input::placeholder { - color: var(--text-muted); -} - -/* Smart Suggestions dropdown */ -.debugger-suggestions { - position: absolute; - bottom: calc(100% + 8px); - left: 0; - right: 0; - background: var(--bg-card); - border: 1px solid var(--border-bright); - border-radius: var(--radius); - max-height: 240px; - overflow-y: auto; - display: none; - box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.3); - z-index: 300; -} - -.debugger-suggestions.open { - display: block; -} - -.debugger-suggestion-item { - display: flex; - align-items: center; - gap: 10px; - padding: 8px 14px; - cursor: pointer; - transition: background var(--transition-fast); - border-bottom: 1px solid rgba(39, 39, 42, 0.3); -} - -.debugger-suggestion-item:last-child { - border-bottom: none; -} - -.debugger-suggestion-item:hover, -.debugger-suggestion-item.selected { - background: var(--bg-hover); -} - -.debugger-suggestion-item.selected { - background: var(--accent-glow); -} - -.suggestion-icon { - flex-shrink: 0; - width: 20px; - height: 20px; - display: flex; - align-items: center; - justify-content: center; - border-radius: 4px; - font-size: 11px; - font-weight: 700; - font-family: var(--font-mono); -} - -.suggestion-icon.cmd { - background: rgba(59, 130, 246, 0.15); - color: var(--accent-blue); -} - -.suggestion-icon.bash { - background: rgba(16, 185, 129, 0.15); - color: var(--accent); -} - -.suggestion-icon.debug { - background: rgba(139, 92, 246, 0.15); - color: var(--accent-purple); -} - -.suggestion-icon.script { - background: rgba(245, 158, 11, 0.15); - color: var(--accent-orange); -} - -.suggestion-content { - flex: 1; - min-width: 0; -} - -.suggestion-title { - font-family: var(--font-mono); - font-size: 12px; - font-weight: 500; - color: var(--text-primary); -} - -.suggestion-desc { - font-family: var(--font-ui); - font-size: 11px; - color: var(--text-muted); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.suggestion-kbd { - font-family: var(--font-mono); - font-size: 10px; - color: var(--text-muted); - background: var(--bg-primary); - padding: 2px 6px; - border-radius: 3px; - border: 1px solid var(--border); - flex-shrink: 0; -} - -/* Debugger toggle active state in header */ -#btn-debugger-toggle.active { - color: var(--accent); - background: var(--accent-glow); - border-color: var(--accent-dim); -} - -/* Body adjustment when debugger is open */ -body.debugger-open #app-main { - height: calc(100vh - var(--header-height) - var(--debugger-height, 280px)); -} - -/* ─── Animations ─────────────────────────────────────── */ -@keyframes fadeIn { - from { - opacity: 0; - transform: translateY(4px); - } - - to { - opacity: 1; - transform: translateY(0); - } -} - -.animate-in { - animation: fadeIn var(--transition) forwards; -} - -/* ─── Replay Modal ───────────────────────── */ - -#replay-modal { - width: min(1100px, 95vw); - height: min(800px, 90vh); - - display: flex; - flex-direction: column; -} - -.replay-controls { - display: flex; - gap: 10px; - align-items: center; -} - -#replay-terminal { - flex: 1; - - overflow-y: auto; - - padding: 16px; - - background: #0d1117; - - border-radius: 10px; - - font-family: monospace; - font-size: 13px; - line-height: 1.5; -} - -#replay-metadata { - padding: 12px 16px; - - border-bottom: 1px solid var(--border-color); - - color: var(--text-secondary); - - font-size: 13px; -} - -.replay-line { - white-space: pre-wrap; - word-break: break-word; - - margin-bottom: 2px; -} - -.replay-line.stdout { - color: #e6edf3; -} - -.replay-line.error { - color: #ff7b72; -} - -.replay-line.system { - color: #7ee787; -} -/* ─── Replay Buttons ───────────────────── */ - -.history-replay-btn { - padding: 6px 10px; - border: 1px solid var(--border-color); - background: var(--bg-secondary); - color: var(--text-primary); - border-radius: 8px; - cursor: pointer; - transition: all 0.2s ease; -} - -.history-replay-btn:hover { - background: var(--accent-color); - color: white; -} -/* ─── Analytics Dashboard ───────────────── */ - -#analytics-modal { - width: min(1200px, 95vw); - max-height: 90vh; - overflow-y: auto; -} - -.analytics-grid { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); - gap: 16px; - margin-bottom: 24px; -} - -.analytics-card { - padding: 20px; - border-radius: 12px; - background: var(--bg-secondary); - border: 1px solid var(--border-color); -} - -.analytics-card h3 { - margin-bottom: 10px; - font-size: 14px; - color: var(--text-secondary); -} - -.analytics-card div { - font-size: 28px; - font-weight: bold; -} - -.analytics-section { - margin-top: 24px; -} - -.analytics-item { - padding: 10px 14px; - margin-bottom: 8px; - border-radius: 8px; - background: var(--bg-secondary); - border: 1px solid var(--border-color); -} - -/* ─── Reliability Dashboard ─────────────────────────────── */ - -.reliability-modal { - width: min(1200px, 95vw); - max-height: 92vh; -} - -.reliability-modal .modal-body { - overflow-y: auto; - max-height: calc(92vh - 72px); -} - -.reliability-modal-subtitle { - margin: 4px 0 0; - font-size: 13px; - color: var(--text-secondary); -} - -.reliability-updated-at { - display: block; - margin-top: 4px; - font-size: 12px; - color: var(--text-secondary); - font-family: var(--font-mono); - opacity: 0.9; -} - -.reliability-header-actions { - display: flex; - align-items: center; - gap: 8px; -} - -.reliability-toolbar { - display: flex; - flex-wrap: wrap; - gap: 12px; - align-items: flex-end; - margin-bottom: 16px; -} - -.reliability-search-group { - flex: 1; - min-width: 200px; -} - -.reliability-filter-group { - display: flex; - flex-wrap: wrap; - gap: 6px; -} - -.reliability-filter { - padding: 6px 12px; - border-radius: 6px; - border: 1px solid var(--border-color); - background: var(--bg-secondary); - color: var(--text-secondary); - font-size: 12px; - cursor: pointer; - transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.1s ease; -} - -.reliability-filter:hover { - color: var(--text-primary); - border-color: var(--accent); -} - -.reliability-filter:focus-visible { - outline: 2px solid var(--accent); - outline-offset: 2px; -} - -.reliability-filter.active { - background: var(--accent); - color: #fff; - border-color: var(--accent); -} - -/* Status banners: loading, error, corrupted/warning */ -.reliability-status-banner { - display: flex; - align-items: flex-start; - gap: 10px; - padding: 10px 14px; - border-radius: 8px; - margin-bottom: 14px; - font-size: 13px; - line-height: 1.45; - transition: opacity 0.2s ease; -} - -.reliability-status-banner p { - margin: 0 0 4px; -} - -.reliability-status-banner p:last-child { - margin-bottom: 0; -} - -.reliability-status-icon { - flex-shrink: 0; - width: 8px; - height: 8px; - margin-top: 5px; - border-radius: 50%; -} - -.reliability-status-banner.loading { - background: rgba(59, 130, 246, 0.12); - border: 1px solid rgba(59, 130, 246, 0.35); - color: #93c5fd; -} - -.reliability-status-banner.loading .reliability-status-icon { - background: #93c5fd; - animation: reliability-pulse 1.2s ease-in-out infinite; -} - -.reliability-status-banner.error { - background: rgba(220, 53, 69, 0.12); - border: 1px solid rgba(220, 53, 69, 0.35); - color: #f8a5a5; -} - -.reliability-status-banner.error .reliability-status-icon { - background: #f87171; - box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.25); -} - -.reliability-status-banner.warning { - background: rgba(234, 179, 8, 0.1); - border: 1px solid rgba(234, 179, 8, 0.4); - color: #fde047; -} - -.reliability-status-banner.warning .reliability-status-icon { - background: #facc15; - box-shadow: 0 0 0 3px rgba(250, 204, 21, 0.2); -} - -.reliability-status-banner.critical { - background: rgba(220, 53, 69, 0.18); - border: 1px solid rgba(220, 53, 69, 0.5); - color: #fecaca; -} - -.reliability-status-banner.critical .reliability-status-icon { - background: #ef4444; - box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.3); -} - -.diagnostic-source-row { - display: flex; - flex-wrap: wrap; - gap: 6px; - margin-top: 8px; -} - -/* Scorecards + shared card base */ -.reliability-scorecards { - display: grid; - grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); - gap: 12px; - margin-bottom: 20px; -} - -.reliability-card, -.reliability-scorecard { - padding: 16px; - border-radius: 10px; - background: var(--bg-secondary); - border: 1px solid var(--border-color); - transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease; -} - -.reliability-card:hover, -.reliability-scorecard:hover { - box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18); -} - -.reliability-animate-in { - animation: reliability-fade-in 0.28s ease forwards; -} - -.reliability-scorecard h4 { - font-size: 12px; - color: var(--text-secondary); - margin-bottom: 6px; - text-transform: uppercase; - letter-spacing: 0.04em; -} - -.reliability-scorecard .value { - font-size: 26px; - font-weight: 700; - line-height: 1.1; -} - -.reliability-scorecard.score-good { - border-color: rgba(34, 197, 94, 0.45); -} - -.reliability-scorecard.score-warn { - border-color: rgba(234, 179, 8, 0.45); -} - -.reliability-scorecard.score-bad { - border-color: rgba(239, 68, 68, 0.45); -} - -.reliability-scorecard.is-loading { - pointer-events: none; -} - -.reliability-skeleton { - border-radius: 4px; - background: linear-gradient( - 90deg, - var(--bg-tertiary, #1a1d24) 0%, - rgba(255, 255, 255, 0.06) 50%, - var(--bg-tertiary, #1a1d24) 100% - ); - background-size: 200% 100%; - animation: reliability-shimmer 1.4s ease-in-out infinite; -} - -.reliability-skeleton-label { - height: 12px; - width: 60%; - margin-bottom: 10px; -} - -.reliability-skeleton-value { - height: 28px; - width: 45%; -} - -/* Panels */ -.reliability-panels { - display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); - gap: 14px; -} - -.reliability-panel { - background: var(--bg-secondary); - border: 1px solid var(--border-color); - border-radius: 10px; - padding: 14px; - transition: border-color 0.2s ease; -} - -.reliability-panel--trends { - border-top: 2px solid rgba(59, 130, 246, 0.45); -} - -.reliability-panel--failures { - border-top: 2px solid rgba(239, 68, 68, 0.4); -} - -.reliability-panel--recommendations { - border-top: 2px solid rgba(234, 179, 8, 0.4); -} - -.reliability-panel--orchestration { - border-top: 2px solid rgba(148, 163, 184, 0.45); - margin-bottom: 14px; -} - -.diagnostic-indicator-row { - display: flex; - flex-wrap: wrap; - gap: 8px; - margin-top: 6px; -} - -.diagnostic-pill { - display: inline-block; - padding: 4px 10px; - border-radius: 999px; - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.03em; - border: 1px solid transparent; -} - -.diagnostic-pill.ok { - background: rgba(34, 197, 94, 0.15); - color: #86efac; - border-color: rgba(34, 197, 94, 0.35); -} - -.diagnostic-pill.warn { - background: rgba(234, 179, 8, 0.15); - color: #fde047; - border-color: rgba(234, 179, 8, 0.35); -} - -.diagnostic-pill.error { - background: rgba(239, 68, 68, 0.15); - color: #fca5a5; - border-color: rgba(239, 68, 68, 0.35); -} - -.diagnostic-replay-unstable { - border-left: 3px solid rgba(249, 115, 22, 0.55); -} - -.diagnostic-corrupted { - border-left: 3px solid rgba(239, 68, 68, 0.55); -} - -.diagnostic-workspace { - border-left: 3px solid rgba(234, 179, 8, 0.5); -} - -.replay-diagnostics { - margin: 10px 14px 0; - padding: 10px 12px; - border-radius: 8px; - font-size: 13px; - line-height: 1.45; -} - -.replay-diagnostics.warning { - background: rgba(234, 179, 8, 0.1); - border: 1px solid rgba(234, 179, 8, 0.35); - color: #fde047; -} - -.replay-diagnostics-reasons { - display: block; - margin-top: 4px; - font-size: 12px; - opacity: 0.9; -} - -.history-diagnostic-badge { - display: inline-block; - padding: 2px 6px; - border-radius: 4px; - font-size: 10px; - font-weight: 700; - text-transform: uppercase; - background: rgba(249, 115, 22, 0.2); - color: #fdba74; -} - -.reliability-panel-wide { - grid-column: 1 / -1; -} - -.reliability-panel h3 { - font-size: 13px; - margin-bottom: 10px; - color: var(--text-secondary); - text-transform: uppercase; - letter-spacing: 0.04em; -} - -.reliability-panel-content { - display: flex; - flex-direction: column; - gap: 8px; - max-height: 220px; - overflow-y: auto; - scroll-behavior: smooth; -} - -.reliability-panel-content:focus-within { - outline: none; -} - -/* List cards */ -.reliability-item { - padding: 10px 12px; - border-radius: 8px; - background: var(--bg-tertiary, #1a1d24); - border: 1px solid var(--border-color); - font-size: 13px; - list-style: none; - transition: background 0.15s ease, border-color 0.15s ease; -} - -.reliability-item[role="listitem"] { - display: block; -} - -.reliability-item:hover { - border-color: rgba(255, 255, 255, 0.12); -} - -.reliability-item-head { - display: flex; - justify-content: space-between; - align-items: center; - gap: 8px; - margin-bottom: 4px; - flex-wrap: wrap; -} - -.reliability-item-meta { - color: var(--text-secondary); - font-size: 12px; -} - -/* Badges: priority, trend, score */ -.reliability-badge, -.failure-badge { - display: inline-block; - padding: 2px 8px; - border-radius: 4px; - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.02em; - white-space: nowrap; -} - -.reliability-badge.critical, -.reliability-recommendation--critical { --rec-accent: #ef4444; } -.reliability-badge.high, -.reliability-recommendation--high { --rec-accent: #f97316; } -.reliability-badge.medium, -.reliability-recommendation--medium { --rec-accent: #eab308; } -.reliability-badge.info, -.reliability-recommendation--info { --rec-accent: #3b82f6; } - -.reliability-badge.critical { background: rgba(239, 68, 68, 0.2); color: #fca5a5; } -.reliability-badge.high { background: rgba(249, 115, 22, 0.2); color: #fdba74; } -.reliability-badge.medium { background: rgba(234, 179, 8, 0.2); color: #fde047; } -.reliability-badge.info { background: rgba(59, 130, 246, 0.2); color: #93c5fd; } - -.reliability-badge.trend-improving { background: rgba(34, 197, 94, 0.2); color: #86efac; } -.reliability-badge.trend-degrading { background: rgba(239, 68, 68, 0.2); color: #fca5a5; } -.reliability-badge.trend-stable { background: rgba(148, 163, 184, 0.2); color: #cbd5e1; } - -.reliability-badge.score-good { background: rgba(34, 197, 94, 0.2); color: #86efac; } -.reliability-badge.score-warn { background: rgba(234, 179, 8, 0.2); color: #fde047; } -.reliability-badge.score-bad { background: rgba(239, 68, 68, 0.2); color: #fca5a5; } - -.reliability-badge.indicator-flaky { background: rgba(249, 115, 22, 0.22); color: #fdba74; } -.reliability-badge.indicator-slow { background: rgba(234, 179, 8, 0.22); color: #fde047; } -.reliability-badge.indicator-regressed { - background: rgba(239, 68, 68, 0.18); - color: #fca5a5; -} - -/* Failure type badges */ -.failure-badge--permission_error { background: rgba(168, 85, 247, 0.2); color: #d8b4fe; } -.failure-badge--dependency_error { background: rgba(59, 130, 246, 0.2); color: #93c5fd; } -.failure-badge--timeout { background: rgba(234, 179, 8, 0.2); color: #fde047; } -.failure-badge--shell_error { background: rgba(249, 115, 22, 0.2); color: #fdba74; } -.failure-badge--missing_file { background: rgba(148, 163, 184, 0.25); color: #e2e8f0; } -.failure-badge--interrupted { background: rgba(100, 116, 139, 0.25); color: #cbd5e1; } -.failure-badge--unknown_failure { background: rgba(239, 68, 68, 0.15); color: #fca5a5; } - -.reliability-failure-card { - border-left: 3px solid rgba(239, 68, 68, 0.55); -} - -/* Trend cards */ -.reliability-trend-card { - border-left: 3px solid rgba(59, 130, 246, 0.5); -} - -.reliability-trend-card.trend-improving { - border-left-color: rgba(34, 197, 94, 0.65); -} - -.reliability-trend-card.trend-degrading { - border-left-color: rgba(239, 68, 68, 0.65); -} - -/* Recommendation blocks */ -.reliability-recommendation { - border-left: 3px solid var(--rec-accent, #3b82f6); - padding-left: 12px; -} - -.reliability-recommendation .reliability-item-head { - margin-bottom: 6px; -} - -/* Script rows */ -.reliability-script-row { - display: grid; - grid-template-columns: 1fr auto auto minmax(100px, auto); - gap: 10px; - align-items: center; -} - -.reliability-script-row .script-name { - font-weight: 600; - font-family: var(--font-mono); - font-size: 12px; - word-break: break-all; -} - -.reliability-script-row .reliability-script-stats { - font-size: 12px; - color: var(--text-secondary); - text-align: right; -} - -/* Empty + loading states */ -.reliability-empty { - display: flex; - align-items: center; - justify-content: center; - min-height: 52px; - padding: 12px; - border-radius: 8px; - border: 1px dashed var(--border-color); - color: var(--text-secondary); - font-size: 13px; - text-align: center; - font-style: italic; -} - -.reliability-empty--loading { - border-style: solid; - background: rgba(59, 130, 246, 0.05); - animation: reliability-pulse 1.5s ease-in-out infinite; -} - -@keyframes reliability-fade-in { - from { - opacity: 0; - transform: translateY(4px); - } - to { - opacity: 1; - transform: translateY(0); - } -} - -@keyframes reliability-shimmer { - 0% { background-position: 100% 0; } - 100% { background-position: -100% 0; } -} - -@keyframes reliability-pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.55; } -} - -@media (max-width: 900px) { - .reliability-panels { - grid-template-columns: 1fr; - } - .reliability-script-row { - grid-template-columns: 1fr; - gap: 6px; - } - .reliability-script-row .reliability-script-stats { - text-align: left; - } -} - -@media (prefers-reduced-motion: reduce) { - .reliability-animate-in, - .reliability-skeleton, - .reliability-status-banner.loading .reliability-status-icon, - .reliability-empty--loading { - animation: none; - } - .reliability-card:hover, - .reliability-scorecard:hover { - transform: none; - } -} - -/* ─── Progress Tracker Panel ───────────────────────────── */ -.progress-tracker-panel { - background: var(--bg-secondary); - border-bottom: 1px solid var(--border); - padding: 10px 16px; - display: flex; - flex-direction: column; - gap: 6px; -} - -.progress-tracker-header { - display: flex; - justify-content: space-between; - align-items: center; -} - -.progress-tracker-title { - font-size: 11px; - font-weight: 600; - text-transform: uppercase; - letter-spacing: 0.5px; - color: var(--text-secondary); -} - -.progress-tracker-step { - font-family: var(--font-mono); - font-size: 11px; - font-weight: 500; - color: var(--accent); -} - -.progress-bar-container { - height: 4px; - background: var(--bg-tertiary); - border-radius: 2px; - overflow: hidden; - width: 100%; -} - -.progress-bar-fill { - height: 100%; - background: var(--accent); - width: 0%; - transition: width 0.2s ease; -} - -.progress-tracker-footer { - display: flex; - justify-content: space-between; - align-items: center; - font-size: 11px; -} - -.progress-tracker-command { - font-family: var(--font-mono); - color: var(--text-primary); - background: var(--bg-tertiary); - padding: 1px 6px; - border-radius: var(--radius); - border: 1px solid var(--border); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - max-width: 70%; -} - -.progress-tracker-status { - color: var(--text-secondary); - display: flex; - align-items: center; - gap: 6px; -} - -.progress-tracker-status::before { - content: ''; - display: inline-block; - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--text-muted); -} - -.progress-tracker-status.running::before { - background: var(--accent-orange); -} - -.progress-tracker-status.success::before { - background: var(--accent); -} - -.progress-tracker-status.failed::before { - background: var(--accent-red); -} -/* ─── Workspace Recovery ───────────────── */ - -.workspace-recovery-actions { - display: flex; - gap: 12px; - margin-top: 20px; - flex-wrap: wrap; -} - -.workspace-recovery-actions .btn { - flex: 1; -} - -#workspace-safe-btn { - border: 1px solid var(--accent-yellow); - color: var(--accent-yellow); -} -/* ─── Workspace Profiles ───────────────── */ - -.workspace-profile-create { - display: flex; - gap: 12px; - margin-bottom: 20px; -} - -.workspace-profile-create input { - flex: 1; -} - -.workspace-profile-item { - display: flex; - justify-content: space-between; - align-items: center; - padding: 12px; - margin-bottom: 10px; - border-radius: 10px; - background: var(--bg-secondary); - border: 1px solid var(--border-color); -} - -.workspace-profile-actions { - display: flex; - gap: 8px; -} -/* ─── Workspace Snapshot Metadata ─────── */ - -.workspace-snapshot-meta { - margin-top: 16px; - font-size: 13px; - color: var(--text-secondary); - opacity: 0.8; -} -/* ─── LIGHT THEME OVERRIDES ─── */ -body.light-theme { - background-color: #f8f9fa !important; - color: #212529 !important; - --border-color: #dee2e6; -} - -body.light-theme #app-header { - background-color: #ffffff !important; - border-bottom: 1px solid #dee2e6 !important; -} - -body.light-theme #app-header h1 { - color: #212529 !important; -} - -body.light-theme #sidebar { - background-color: #e9ecef !important; - border-right: 1px solid #dee2e6 !important; -} - -body.light-theme .sidebar-search-wrapper input { - background: #ffffff !important; - color: #212529 !important; - border: 1px solid #ced4da !important; -} - -body.light-theme #cli-area { - background-color: #ffffff !important; -} - -body.light-theme .cli-header { - background-color: #f1f3f5 !important; - border-bottom: 1px solid #dee2e6 !important; - color: #212529 !important; -} - -body.light-theme .cli-body { - background-color: #ffffff !important; - color: #212529 !important; -} - -body.light-theme .cli-welcome-text { - color: #495057 !important; -} - -body.light-theme #cli-input { - background-color: #f1f3f5 !important; - color: #212529 !important; - border: 1px solid #ced4da !important; -} - -body.light-theme #analysis-panel { - background-color: #e9ecef !important; - border-left: 1px solid #dee2e6 !important; - color: #212529 !important; -} - -body.light-theme .analysis-empty h3 { - color: #212529 !important; -} -/* ───────────────────────────── */ \ No newline at end of file +/* ═══════════════════════════════════════════════════════════ + DevShell — Premium Dark Theme (CLI + Analysis Layout) + ═══════════════════════════════════════════════════════════ */ + +:root { + /* Core palette */ + --bg-primary: #09090b; + --bg-secondary: #0f1115; + --bg-tertiary: #18181b; + --bg-card: #1f1f22; + --bg-hover: #27272a; + --bg-active: #3f3f46; + + /* Accent colors */ + --accent: #10b981; + --accent-dim: #059669; + --accent-glow: rgba(16, 185, 129, 0.15); + --accent-glow-strong: rgba(16, 185, 129, 0.3); + --accent-blue: #3b82f6; + --accent-purple: #8b5cf6; + --accent-orange: #f59e0b; + --accent-red: #ef4444; + --accent-yellow: #eab308; + + /* Text */ + --text-primary: #f8fafc; + --text-secondary: #94a3b8; + --text-muted: #64748b; + --text-accent: #10b981; + + /* Borders */ + --border: #27272a; + --border-bright: #3f3f46; + --border-glow: rgba(16, 185, 129, 0.2); + + /* Sizing */ + --sidebar-width: 250px; + --analysis-width: 400px; + --header-height: 60px; + --radius: 6px; + --radius-lg: 10px; + + /* Typography */ + --font-ui: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; + --font-mono: 'JetBrains Mono', 'Fira Code', 'Consolas', monospace; + + /* Transitions */ + --transition-fast: 150ms cubic-bezier(0.4, 0, 0.2, 1); + --transition: 250ms cubic-bezier(0.4, 0, 0.2, 1); + --transition-slow: 400ms cubic-bezier(0.4, 0, 0.2, 1); +} + +/* ─── Reset ───────────────────────────────────────────── */ +*, +*::before, +*::after { + margin: 0; + padding: 0; + box-sizing: border-box; +} + +html, +body { + height: 100%; + overflow: hidden; + font-family: var(--font-ui); + background: var(--bg-primary); + color: var(--text-primary); + font-size: 14px; + line-height: 1.5; + -webkit-font-smoothing: antialiased; +} + +::-webkit-scrollbar { + width: 6px; + height: 6px; +} + +::-webkit-scrollbar-track { + background: transparent; +} + +::-webkit-scrollbar-thumb { + background: var(--border-bright); + border-radius: 3px; +} + +::-webkit-scrollbar-thumb:hover { + background: var(--text-muted); +} + +/* ─── Header ──────────────────────────────────────────── */ +#app-header { + height: var(--header-height); + background: var(--bg-secondary); + border-bottom: 1px solid var(--border); + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 24px; + position: relative; + z-index: 100; +} + +.header-left { + display: flex; + align-items: center; + gap: 12px; +} + +.logo { + display: flex; + align-items: center; + gap: 10px; +} + +.logo-icon { + color: var(--accent); + filter: drop-shadow(0 0 8px var(--accent-glow-strong)); +} + +.logo h1 { + font-size: 18px; + font-weight: 700; + letter-spacing: -0.5px; + color: var(--text-primary); +} + +.logo h1 .accent { + color: var(--accent); +} + +.version-badge { + font-size: 11px; + color: var(--text-muted); + background: var(--bg-tertiary); + padding: 2px 8px; + border-radius: 20px; + border: 1px solid var(--border); + font-weight: 500; +} + +.header-center { + flex: 1; + max-width: 480px; + margin: 0 24px; +} + +.search-container { + position: relative; + display: flex; + align-items: center; + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 0 12px; + transition: var(--transition); +} + +.search-container:focus-within { + border-color: var(--accent-dim); + box-shadow: 0 0 0 3px var(--accent-glow); +} + +.search-icon { + color: var(--text-muted); + margin-right: 8px; +} + +#search-input { + flex: 1; + background: transparent; + border: none; + outline: none; + color: var(--text-primary); + font-family: var(--font-ui); + font-size: 13px; + padding: 8px 0; +} + +#search-input::placeholder { + color: var(--text-muted); +} + +.search-kbd { + font-family: var(--font-mono); + font-size: 10px; + color: var(--text-muted); + background: var(--bg-primary); + padding: 2px 6px; + border-radius: 4px; + border: 1px solid var(--border); +} + +.header-right { + display: flex; + align-items: center; + gap: 12px; +} + +/* ─── Buttons ─────────────────────────────────────────── */ +.btn-icon { + width: 32px; + height: 32px; + display: flex; + align-items: center; + justify-content: center; + background: transparent; + border: 1px solid transparent; + border-radius: var(--radius); + color: var(--text-secondary); + cursor: pointer; + transition: var(--transition-fast); +} + +.btn-icon:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.btn { + display: inline-flex; + align-items: center; + justify-content: center; + gap: 8px; + padding: 8px 16px; + border: 1px solid var(--border); + border-radius: var(--radius); + font-family: var(--font-ui); + font-size: 13px; + font-weight: 500; + cursor: pointer; + transition: var(--transition-fast); + background: var(--bg-tertiary); + color: var(--text-primary); +} + +.btn:hover { + background: var(--bg-hover); +} + +.btn-action { + padding: 6px 12px; + font-size: 12px; +} + +.btn-action:hover { + color: var(--text-primary); + border-color: var(--border-bright); +} + +.btn-danger:hover { + color: var(--accent-red); + border-color: rgba(239, 68, 68, 0.3); + background: rgba(239, 68, 68, 0.1); +} + +.btn-run { + background: var(--accent); + color: #fff; + border-color: var(--accent); + font-weight: 600; +} + +.btn-run:hover { + background: var(--accent-dim); + border-color: var(--accent-dim); +} + +.btn-run.running { + background: var(--accent-orange); + border-color: var(--accent-orange); + animation: btn-pulse 1.5s infinite; +} + +.btn-run.abort, +.btn-run.aborting { + background: var(--accent-red); + border-color: var(--accent-red); + animation: btn-pulse 1.5s infinite; +} + +.btn-run.abort:hover, +.btn-run.aborting:hover { + background: #dc2626; + border-color: #dc2626; +} + +@keyframes btn-pulse { + + 0%, + 100% { + opacity: 1; + } + + 50% { + opacity: 0.8; + } +} + +.btn-fav.active { + color: var(--accent-yellow); +} + +.btn-fav.active svg { + fill: var(--accent-yellow); + stroke: var(--accent-yellow); +} + +.btn-save { + background: var(--accent); + color: #fff; + border-color: var(--accent); + font-weight: 600; +} + +.btn-save:hover { + background: var(--accent-dim); +} + +/* ─── Main Layout ─────────────────────────────────────── */ +#app-main { + display: flex; + height: calc(100vh - var(--header-height)); +} + +#left-panel { + flex: 1; + display: flex; + min-width: 0; +} + +/* ─── Resizers ────────────────────────────────────────── */ +.resizer { + width: 6px; + background: transparent; + cursor: col-resize; + transition: background 0.2s ease; + z-index: 10; + position: relative; +} + +.resizer::after { + content: ''; + position: absolute; + top: 0; + bottom: 0; + left: 2px; + width: 2px; + background: var(--border); + transition: background 0.2s ease; +} + +.resizer:hover::after, +.resizer.resizing::after { + background: var(--accent); +} + +/* ─── Sidebar ─────────────────────────────────────────── */ +#sidebar { + width: var(--sidebar-width); + min-width: var(--sidebar-width); + background: var(--bg-secondary); + border-right: 1px solid var(--border); + display: flex; + flex-direction: column; +} + +.sidebar-header { + padding: 16px 20px; + display: flex; + align-items: center; + justify-content: space-between; + border-bottom: 1px solid var(--border); +} + +.sidebar-title { + display: flex; + align-items: center; + gap: 10px; + color: var(--text-secondary); +} + +.sidebar-title h2 { + font-size: 12px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.script-count { + font-size: 11px; + font-weight: 600; + color: var(--accent); + background: var(--accent-glow); + padding: 2px 8px; + border-radius: 20px; +} + +.sidebar-section { + flex: 1; + overflow-y: auto; + padding: 12px 0; +} + +.section-title { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 20px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--text-muted); +} + +/* Category tree */ +.category { + margin-bottom: 4px; +} + +.category-header { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 20px; + cursor: pointer; + transition: var(--transition-fast); + user-select: none; + color: var(--text-secondary); +} + +.category-header:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.category-arrow { + display: flex; + align-items: center; + justify-content: center; + transition: transform var(--transition-fast); + color: var(--text-muted); + width: 14px; +} + +.category-arrow.expanded { + transform: rotate(90deg); +} + +.category-icon { + display: flex; + align-items: center; + justify-content: center; +} + +.category-name { + font-size: 13px; + font-weight: 500; + flex: 1; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.category-count { + font-size: 11px; + color: var(--text-muted); + background: var(--bg-tertiary); + padding: 2px 6px; + border-radius: 4px; +} + +/* Script list */ +.script-list { + list-style: none; + overflow: hidden; + transition: max-height var(--transition-slow); +} + +.script-list.collapsed { + max-height: 0 !important; +} + +.script-item { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 20px 8px 44px; + cursor: pointer; + transition: var(--transition-fast); + position: relative; + color: var(--text-secondary); +} + +.script-item::before { + content: ''; + position: absolute; + left: 26px; + top: 0; + bottom: 0; + width: 1px; + background: var(--border); +} + +.script-item:last-child::before { + bottom: 50%; +} + +.script-item:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.script-item.active { + background: linear-gradient(90deg, rgba(34, 197, 94, 0.08), transparent) !important; + color: var(--text-primary); +} + +.script-item.active::after { + content: ''; + position: absolute; + left: 0; + top: 6px; + bottom: 6px; + width: 3px; + background: var(--accent); + box-shadow: 0 0 8px var(--accent); + border-radius: 0 2px 2px 0; +} + +.script-item-icon { + display: flex; + align-items: center; + justify-content: center; + color: var(--text-muted); +} + +.script-item.active .script-item-icon { + color: var(--accent); +} + +.script-item-name { + font-size: 13px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; +} + +.script-item-fav { + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + transition: var(--transition-fast); + color: var(--text-muted); +} + +.script-item:hover .script-item-fav, +.script-item-fav.visible { + opacity: 1; +} + +.script-item-fav.visible svg { + fill: var(--accent-yellow); + stroke: var(--accent-yellow); +} + +/* ─── CLI Terminal ────────────────────────────────────── */ +#cli-area { + flex: 1; + display: flex; + flex-direction: column; + min-width: 0; + background: #09090b; +} + +/* ─── Terminal Tabs ───────────────────────────────────── */ +.cli-tabs { + display: flex; + align-items: flex-end; + background: transparent; + border-bottom: none; + padding: 0; + gap: 4px; + height: 100%; + overflow-x: auto; + margin-bottom: -1px; + z-index: 1; +} + +.cli-tabs::-webkit-scrollbar { + height: 0; +} + +.cli-tab { + display: flex; + align-items: center; + gap: 8px; + padding: 0 14px; + height: 32px; + background: transparent; + border: 1px solid transparent; + border-bottom: none; + border-radius: var(--radius) var(--radius) 0 0; + font-size: 11px; + font-weight: 500; + color: var(--text-muted); + cursor: pointer; + transition: var(--transition-fast); + white-space: nowrap; + position: relative; + user-select: none; +} + +.cli-tab:hover { + background: rgba(255, 255, 255, 0.03) !important; + color: var(--text-secondary) !important; +} + +.cli-tab.active { + background: var(--bg-primary); + border-color: var(--border); + border-bottom-color: var(--bg-primary); + color: var(--text-primary); +} + +.cli-tab-close { + width: 14px; + height: 14px; + display: inline-flex; + align-items: center; + justify-content: center; + border: none; + background: transparent; + color: currentColor; + opacity: 0.4; + border-radius: 3px; + cursor: pointer; + transition: opacity var(--transition-fast), background var(--transition-fast); + margin-left: 6px; +} + +.cli-tab:hover .cli-tab-close, +.cli-tab.active .cli-tab-close { + opacity: 0.7; +} + +.cli-tab-close:hover { + background: rgba(255, 255, 255, 0.1) !important; + color: var(--text-primary) !important; + opacity: 1 !important; +} + +.btn-add-tab { + width: 24px; + height: 24px; + display: inline-flex; + align-items: center; + justify-content: center; + background: transparent !important; + border: 1px solid transparent !important; + border-radius: var(--radius) !important; + color: var(--text-muted) !important; + cursor: pointer !important; + transition: var(--transition-fast) !important; + margin-left: 8px !important; + align-self: center !important; +} + +.btn-add-tab:hover { + background: rgba(255, 255, 255, 0.05) !important; + color: var(--text-primary) !important; + border-color: rgba(255, 255, 255, 0.08) !important; +} + +/* ─── Terminal Search ─────────────────────────────────── */ +.terminal-search-wrapper { + position: relative; + display: flex; + align-items: center; +} + +#cli-search-input { + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 4px 6px 4px 26px; + color: var(--text-primary); + font-size: 11px; + width: 140px; + outline: none; + transition: var(--transition-fast); +} + +#cli-search-input:focus { + width: 180px; + border-color: var(--accent); +} + +#cli-search-results { + position: absolute; + right: 8px; + font-size: 10px; + color: var(--text-muted); + pointer-events: none; +} + +.highlight { + background: var(--accent-yellow); + color: #000; + border-radius: 2px; +} + +.cli-header { + display: flex; + align-items: center; + justify-content: space-between; + height: 40px; + padding: 0 16px; + background: var(--bg-secondary); + border-bottom: 1px solid var(--border); + position: relative; +} + +.cli-welcome { + font-family: var(--font-mono); + color: var(--text-muted); + line-height: 1.6; + margin-bottom: 16px; +} + +.cli-welcome > div:first-child { + display: flex; + align-items: baseline; + margin-bottom: 6px; +} + +.cli-welcome .cli-prompt { + color: var(--accent); + width: 1ch; + display: inline-block; + margin-right: 12px; + font-weight: 600; +} + +.cli-welcome-text { + color: var(--text-primary); + font-style: normal; + font-weight: 600; +} + +.cli-welcome-sub { + color: var(--text-muted); + margin-top: 0; + margin-left: calc(1ch + 12px); +} + +.cli-header-left { + display: flex; + align-items: center; + gap: 16px; +} + +.cli-header-right { + display: flex; + align-items: center; + gap: 12px; +} + +.cli-dots { + display: flex; + gap: 4px; +} + +.dot { + width: 9px; + height: 9px; + border-radius: 50%; +} + +.dot-red { + background: #ed655a; +} + +.dot-yellow { + background: #e0c04c; +} + +.dot-green { + background: #72c05b; +} + +.cli-title { + display: flex; + align-items: center; + font-size: 12px; + font-weight: 500; + color: var(--text-secondary); +} + +.cli-body { + flex: 1; + overflow-y: auto; + padding: 20px; + font-family: var(--font-mono); + font-size: 13px; + line-height: 1.6; + color: #e2e8f0; + white-space: pre-wrap; + word-break: break-word; +} + + +.cli-prompt { + color: var(--accent); + font-weight: 600; + margin-right: 8px; +} + +.cli-input-bar { + display: flex; + align-items: center; + gap: 12px; + padding: 12px 20px; + background: var(--bg-tertiary); + border-top: 1px solid var(--border); +} + +.cli-prompt-input { + color: var(--accent); + font-family: var(--font-mono); + font-weight: 600; + font-size: var(--terminal-font-size, 13px); +} + +#cli-input { + flex: 1; + background: transparent; + border: none; + outline: none; + color: var(--text-primary); + font-family: var(--font-mono); + font-size: 13px; + caret-color: var(--accent); +} + +#cli-input::placeholder { + color: var(--text-muted); +} + +.btn-run-cmd { + display: flex; + align-items: center; + justify-content: center; + width: 48px; + height: 28px; + background: var(--accent); + color: black; + border: none; + border-radius: var(--radius); + cursor: pointer; + transition: var(--transition-fast); +} + +.btn-run-cmd:hover { + background: var(--accent-dim); +} + +/* ─── Analysis Panel (Right Sidebar) ─────────────────── */ +#analysis-panel { + width: var(--analysis-width); + min-width: var(--analysis-width); + background: var(--bg-secondary); + border-left: 1px solid var(--border); + display: flex; + flex-direction: column; + overflow-y: auto; +} + +.analysis-state { + flex: 1; + display: flex; + flex-direction: column; +} + +/* Empty / Welcome */ +.analysis-empty { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + text-align: center; + padding: 40px; + gap: 16px; +} + +.analysis-empty-icon { + color: var(--border-bright); + margin-bottom: 8px; +} + +.analysis-empty h3 { + font-size: 18px; + font-weight: 600; + color: var(--text-primary); +} + +.analysis-empty p { + color: var(--text-muted); + font-size: 13px; + line-height: 1.5; + max-width: 280px; + margin: 0 auto; +} + +.welcome-stats { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 12px; + margin-top: 24px; + width: 100%; +} + +.stat-card { + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 16px 12px; + text-align: center; +} + +.stat-value { + font-size: 20px; + font-weight: 600; + color: var(--text-primary); + font-family: var(--font-mono); +} + +.stat-label { + font-size: 11px; + color: var(--text-muted); + margin-top: 4px; +} + +/* ─── Analysis Sections ──────────────────────────────── */ +.analysis-section { + border-bottom: 1px solid var(--border); + background: var(--bg-primary); + margin: 16px; + border-radius: var(--radius-lg); + border: 1px solid var(--border); + overflow: hidden; +} + +.analysis-section-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 12px 16px; + background: var(--bg-tertiary); + border-bottom: 1px solid var(--border); +} + +.section-badge { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + font-weight: 600; + color: var(--text-primary); +} + +.section-badge svg { + color: var(--text-muted); +} + +.btn-close-detail { + color: var(--text-muted); +} + +/* Script Info Card */ +.script-info-body { + padding: 20px; + display: flex; + flex-direction: column; + gap: 12px; +} + +.script-category-badge { + font-size: 10px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--accent); + background: var(--accent-glow); + padding: 4px 10px; + border-radius: 4px; + width: fit-content; +} + +.script-info-body h2 { + font-size: 20px; + font-weight: 600; + color: var(--text-primary); + line-height: 1.3; +} + +.script-description { + color: var(--text-secondary); + font-size: 13px; + line-height: 1.6; +} + +.tag-list { + display: flex; + gap: 8px; + flex-wrap: wrap; + margin-top: 4px; +} + +.tag { + font-size: 11px; + color: var(--text-primary); + background: var(--bg-tertiary); + padding: 4px 10px; + border-radius: 4px; + border: 1px solid var(--border); +} + +.script-actions { + display: flex; + flex-wrap: wrap; + gap: 8px; + align-items: center; + margin-top: 12px; + padding-top: 16px; + border-top: 1px dashed var(--border); +} + +/* ─── Code Panel ──────────────────────────────────────── */ +.file-path { + font-family: var(--font-mono); + font-size: 11px; + color: var(--text-muted); + max-width: 200px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +.code-block { + padding: 16px; + overflow-x: auto; + font-family: var(--font-mono); + font-size: 12px; + line-height: 1.6; + color: var(--text-primary); + background: var(--bg-primary); + max-height: 320px; + overflow-y: auto; +} + +/* ─── Resource Monitor ────────────────────────────────── */ +.resource-grid { + display: grid; + grid-template-columns: repeat(2, 1fr); +} + +.resource-card { + padding: 16px; + display: flex; + align-items: flex-start; + gap: 12px; + border-right: 1px solid var(--border); + border-bottom: 1px solid var(--border); +} + +.resource-card:nth-child(even) { + border-right: none; +} + +.resource-card:nth-last-child(-n+2) { + border-bottom: none; +} + +.resource-icon { + color: var(--text-muted); + flex-shrink: 0; +} + +.resource-info { + flex: 1; + display: flex; + flex-direction: column; + gap: 4px; +} + +.resource-label { + font-size: 11px; + font-weight: 500; + color: var(--text-secondary); +} + +.resource-value { + font-family: var(--font-mono); + font-size: 16px; + font-weight: 600; + color: var(--text-primary); +} + +.resource-bar { + width: 100%; + height: 4px; + background: var(--bg-tertiary); + border-radius: 2px; + overflow: hidden; + margin-top: 4px; +} + +.resource-bar-fill { + height: 100%; + border-radius: 2px; + background: var(--accent); + transition: width var(--transition-slow); + width: 0%; +} + +/* ─── Modal ───────────────────────────────────────────── */ +.modal-overlay { + position: fixed; + inset: 0; + background: rgba(0, 0, 0, 0.6); + backdrop-filter: blur(4px); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + opacity: 0; + visibility: hidden; + transition: var(--transition); +} + +.modal-overlay.active { + opacity: 1; + visibility: visible; +} + +.modal { + background: var(--bg-secondary); + border: 1px solid var(--border); + border-radius: var(--radius-lg); + width: 600px; + max-width: 90vw; + display: flex; + flex-direction: column; + transform: translateY(10px) scale(0.98); + transition: var(--transition); + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.4); +} + +.modal-overlay.active .modal { + transform: translateY(0) scale(1); +} + +.modal-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 20px 24px; + border-bottom: 1px solid var(--border); +} + +.modal-header h2 { + font-size: 16px; + font-weight: 600; + color: var(--text-primary); +} + +.modal-body { + padding: 24px; + display: flex; + flex-direction: column; + gap: 20px; +} + +.form-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 20px; +} + +.form-group { + display: flex; + flex-direction: column; + gap: 8px; +} + +.form-group label { + font-size: 12px; + font-weight: 500; + color: var(--text-secondary); +} + +.form-group input, +.form-group textarea { + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-radius: var(--radius); + padding: 10px 14px; + color: var(--text-primary); + font-family: var(--font-ui); + font-size: 13px; + outline: none; + transition: var(--transition-fast); +} + +.form-group input:focus, +.form-group textarea:focus { + border-color: var(--accent); + box-shadow: 0 0 0 2px var(--accent-glow); +} + +.form-group textarea { + font-family: var(--font-mono); + min-height: 200px; + resize: vertical; + line-height: 1.5; +} + +.modal-footer { + display: flex; + justify-content: flex-end; + gap: 12px; + padding: 20px 24px; + border-top: 1px solid var(--border); + background: var(--bg-tertiary); + border-radius: 0 0 var(--radius-lg) var(--radius-lg); +} + +/* Execution History Modal */ +.history-modal { + width: min(980px, 94vw); +} + +.history-modal .modal-body { + gap: 16px; + max-height: min(72vh, 760px); +} + +.history-modal-subtitle { + margin-top: 4px; + color: var(--text-muted); + font-size: 12px; +} + +.history-header-actions { + display: flex; + align-items: center; + gap: 8px; +} + +.history-toolbar { + display: flex; + flex-wrap: wrap; + gap: 14px; + align-items: flex-end; + justify-content: space-between; +} + +.history-search-group { + flex: 1; + min-width: 280px; +} + +.history-filter-group { + display: flex; + flex-wrap: wrap; + gap: 8px; +} + +.history-filter { + border: 1px solid var(--border); + background: var(--bg-tertiary); + color: var(--text-secondary); + border-radius: var(--radius); + padding: 8px 12px; + font-size: 12px; + cursor: pointer; + transition: var(--transition-fast); +} + +.history-filter:hover { + color: var(--text-primary); + border-color: var(--border-bright); +} + +.history-filter.active { + background: rgba(16, 185, 129, 0.12); + color: var(--accent); + border-color: rgba(16, 185, 129, 0.35); +} + +.history-summary { + display: flex; + flex-wrap: wrap; + gap: 10px; +} + +.history-summary-item { + display: inline-flex; + align-items: center; + gap: 6px; + padding: 6px 10px; + border-radius: 999px; + background: var(--bg-tertiary); + border: 1px solid var(--border); + color: var(--text-secondary); + font-size: 12px; +} + +.history-summary-item strong { + color: var(--text-primary); +} + +.history-summary-item.failed strong { + color: var(--accent-red); +} + +.history-list { + display: flex; + flex-direction: column; + gap: 12px; + overflow-y: auto; + max-height: 52vh; + padding-right: 4px; +} + +.history-entry { + border: 1px solid var(--border); + border-radius: var(--radius-lg); + background: linear-gradient(180deg, rgba(31, 31, 34, 0.96), rgba(15, 17, 21, 0.98)); + padding: 14px 16px; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.18); +} + +.history-entry.success { + border-color: rgba(16, 185, 129, 0.18); +} + +.history-entry.failed { + border-color: rgba(239, 68, 68, 0.25); + background: linear-gradient(180deg, rgba(40, 20, 23, 0.96), rgba(18, 15, 17, 0.98)); +} + +.history-entry-head { + display: flex; + flex-wrap: wrap; + gap: 10px; + align-items: flex-start; + justify-content: space-between; +} + +.history-entry-title-row, +.history-entry-meta { + display: flex; + flex-wrap: wrap; + align-items: center; + gap: 8px; +} + +.history-entry-meta { + justify-content: flex-end; + color: var(--text-muted); + font-size: 11px; +} + +.history-entry-status, +.history-entry-kind, +.history-entry-time { + font-size: 11px; + padding: 3px 8px; + border-radius: 999px; + border: 1px solid var(--border); + background: var(--bg-secondary); +} + +.history-entry-status.success { + color: var(--accent); + border-color: rgba(16, 185, 129, 0.2); +} + +.history-entry-status.failed { + color: var(--accent-red); + border-color: rgba(239, 68, 68, 0.28); +} + +.history-entry-kind { + color: var(--text-secondary); +} + +.history-entry-time { + color: var(--text-muted); + font-family: var(--font-mono); +} + +.history-entry-command { + margin-top: 10px; + font-family: var(--font-mono); + color: var(--text-primary); + word-break: break-word; +} + +.history-entry-error { + margin-top: 8px; + color: var(--accent-red); + font-family: var(--font-mono); + white-space: pre-wrap; + word-break: break-word; +} + +.history-entry-excerpt { + margin-top: 10px; + color: var(--text-secondary); + font-family: var(--font-mono); + font-size: 12px; + white-space: pre-wrap; + word-break: break-word; +} + +.history-excerpt-empty { + color: var(--text-muted); +} + +.history-empty-state { + padding: 24px; + text-align: center; + color: var(--text-muted); + border: 1px dashed var(--border); + border-radius: var(--radius-lg); + background: rgba(15, 17, 21, 0.65); +} + +/* ─── Notifications ──────────────────────────────────── */ +#notification-container { + position: fixed; + top: 20px; + right: 20px; + z-index: 9999; + + display: flex; + flex-direction: column; + gap: 12px; + + pointer-events: none; + max-width: 380px; +} + +.notification { + display: flex; + align-items: flex-start; + gap: 12px; + + background: var(--bg-card); + border: 1px solid var(--border); + border-left: 4px solid var(--accent); + + color: var(--text-primary); + + border-radius: var(--radius-lg); + padding: 14px 16px; + + box-shadow: 0 12px 32px rgba(0, 0, 0, 0.35); + + animation: notificationSlideIn 220ms ease forwards; + + pointer-events: auto; + + min-width: 280px; + max-width: 100%; +} + +.notification.success { + border-left-color: var(--accent); +} + +.notification.error { + border-left-color: var(--accent-red); +} + +.notification.warning { + border-left-color: var(--accent-yellow); +} + +.notification.info { + border-left-color: var(--accent-blue); +} + +.notification-content { + flex: 1; + min-width: 0; +} + +.notification-message { + font-size: 13px; + line-height: 1.5; + word-break: break-word; +} + +.notification-close { + background: transparent; + border: none; + + color: var(--text-muted); + + cursor: pointer; + + width: 24px; + height: 24px; + + display: flex; + align-items: center; + justify-content: center; + + border-radius: 4px; + + transition: var(--transition-fast); + + flex-shrink: 0; +} + +.notification-close:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.notification-close:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +.notification.removing { + animation: notificationSlideOut 180ms ease forwards; +} + +@keyframes notificationSlideIn { + from { + opacity: 0; + transform: translateX(18px) translateY(-4px); + } + + to { + opacity: 1; + transform: translateX(0) translateY(0); + } +} + +@keyframes notificationSlideOut { + from { + opacity: 1; + transform: translateX(0); + } + + to { + opacity: 0; + transform: translateX(20px); + } +} + +@media (prefers-reduced-motion: reduce) { + + .notification, + .notification.removing { + animation: none; + } +} + +/* ─── Utility & CLI text ──────────────────────────────── */ +.accent { + color: var(--accent); +} + +.cli-output-block { + margin: 4px 0; + padding: 4px 0; + white-space: pre-wrap; + word-break: break-word; +} + +.cli-output-block.cmd-line { + color: var(--accent); + font-weight: 600; +} + +.cli-output-block.stdout { + color: var(--text-primary); +} + +.cli-output-block.stderr { + color: var(--accent-red); +} + +.cli-output-block.info { + color: var(--text-secondary); +} + +.cli-output-block.success { + color: var(--accent); +} + +.cli-output-block.error { + color: var(--accent-red); +} + +.cli-output-block.system { + color: var(--accent-blue); +} + +/* Run Status */ +.run-status { + font-size: 11px; + font-weight: 500; +} + +.run-status.success { + color: var(--accent); +} + +.run-status.error { + color: var(--accent-red); +} + +.run-status.running { + color: var(--accent-orange); + animation: pulse 1.5s infinite; +} + +@keyframes pulse { + + 0%, + 100% { + opacity: 1; + } + + 50% { + opacity: 0.6; + } +} + +/* ─── Responsive ──────────────────────────────────────── */ +@media (max-width: 1100px) { + #analysis-panel { + width: 340px; + min-width: 340px; + } +} + +@media (max-width: 900px) { + #sidebar { + width: 220px; + min-width: 220px; + } + + #analysis-panel { + width: 300px; + min-width: 300px; + } + + .header-center { + display: none; + } +} + +@media (max-width: 768px) { + + #sidebar, + #analysis-panel { + display: none; + } +} + +/* ─── Debugger Console ────────────────────────────────── */ +.debugger-console { + position: fixed; + bottom: 0; + left: 0; + right: 0; + height: 280px; + background: var(--bg-secondary); + border-top: 1px solid var(--border); + display: flex; + flex-direction: column; + z-index: 200; + transform: translateY(100%); + transition: transform var(--transition-slow); + box-shadow: 0 -4px 24px rgba(0, 0, 0, 0.3); +} + +.debugger-console.open { + transform: translateY(0); +} + +.debugger-resize-handle { + height: 5px; + background: transparent; + cursor: ns-resize; + position: relative; + flex-shrink: 0; +} + +.debugger-resize-handle::after { + content: ''; + position: absolute; + left: 50%; + top: 1px; + transform: translateX(-50%); + width: 40px; + height: 3px; + background: var(--border-bright); + border-radius: 3px; + transition: background var(--transition-fast); +} + +.debugger-resize-handle:hover::after { + background: var(--accent); +} + +.debugger-header { + display: flex; + align-items: center; + justify-content: space-between; + padding: 6px 16px; + background: var(--bg-tertiary); + border-bottom: 1px solid var(--border); + flex-shrink: 0; +} + +.debugger-header-left { + display: flex; + align-items: center; + gap: 16px; +} + +.debugger-title { + display: flex; + align-items: center; + gap: 8px; + font-size: 12px; + font-weight: 600; + color: var(--text-primary); + white-space: nowrap; +} + +.debugger-title svg { + color: var(--text-muted); +} + +.debugger-filter-tabs { + display: flex; + align-items: center; + gap: 2px; +} + +.debugger-filter-tab { + padding: 3px 10px; + border: none; + background: transparent; + color: var(--text-muted); + font-family: var(--font-ui); + font-size: 11px; + font-weight: 500; + border-radius: 4px; + cursor: pointer; + transition: var(--transition-fast); + white-space: nowrap; +} + +.debugger-filter-tab:hover { + background: var(--bg-hover); + color: var(--text-primary); +} + +.debugger-filter-tab.active { + background: var(--bg-active); + color: var(--text-primary); +} + +.debugger-filter-tab[data-filter="error"].has-entries { + color: var(--accent-red); +} + +.debugger-filter-tab[data-filter="warn"].has-entries { + color: var(--accent-orange); +} + +.debugger-header-right { + display: flex; + align-items: center; + gap: 8px; +} + +.debugger-log-count { + font-size: 11px; + color: var(--text-muted); + font-family: var(--font-mono); + font-weight: 500; + padding-right: 4px; +} + +.debugger-body { + flex: 1; + overflow-y: auto; + padding: 8px 0; + font-family: var(--font-mono); + font-size: 12px; + line-height: 1.5; +} + +.debugger-welcome { + display: flex; + align-items: center; + gap: 10px; + padding: 12px 16px; + color: var(--text-muted); + font-size: 12px; + font-family: var(--font-ui); +} + +.debugger-welcome-icon { + flex-shrink: 0; + display: flex; + align-items: center; + color: var(--accent-blue); +} + +/* Debugger log entries */ +.debugger-entry { + display: flex; + align-items: flex-start; + gap: 10px; + padding: 4px 16px; + border-bottom: 1px solid rgba(39, 39, 42, 0.5); + transition: background var(--transition-fast); + animation: debugEntryIn 200ms ease forwards; +} + +@keyframes debugEntryIn { + from { + opacity: 0; + transform: translateX(-4px); + } + + to { + opacity: 1; + transform: translateX(0); + } +} + +.debugger-entry:hover { + background: var(--bg-hover); +} + +.debugger-entry.hidden { + display: none; +} + +.debugger-entry-icon { + flex-shrink: 0; + width: 16px; + height: 16px; + display: flex; + align-items: center; + justify-content: center; + margin-top: 2px; +} + +.debugger-entry-time { + flex-shrink: 0; + font-size: 10px; + color: var(--text-muted); + min-width: 58px; + margin-top: 2px; + font-family: var(--font-mono); +} + +.debugger-entry-content { + flex: 1; + word-break: break-all; + white-space: pre-wrap; + min-width: 0; +} + +.debugger-entry-source { + flex-shrink: 0; + font-size: 10px; + color: var(--text-muted); + margin-top: 2px; + max-width: 120px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} + +/* Entry type styling */ +.debugger-entry.log { + color: var(--text-primary); +} + +.debugger-entry.log .debugger-entry-icon { + color: var(--text-muted); +} + +.debugger-entry.warn { + color: var(--accent-orange); + background: rgba(245, 158, 11, 0.04); +} + +.debugger-entry.warn .debugger-entry-icon { + color: var(--accent-orange); +} + +.debugger-entry.error { + color: var(--accent-red); + background: rgba(239, 68, 68, 0.04); +} + +.debugger-entry.error .debugger-entry-icon { + color: var(--accent-red); +} + +.debugger-entry.info { + color: var(--accent-blue); +} + +.debugger-entry.info .debugger-entry-icon { + color: var(--accent-blue); +} + +.debugger-entry.network { + color: var(--accent-purple); +} + +.debugger-entry.network .debugger-entry-icon { + color: var(--accent-purple); +} + +.debugger-entry.result { + color: var(--accent); + background: rgba(16, 185, 129, 0.04); +} + +.debugger-entry.result .debugger-entry-icon { + color: var(--accent); +} + +.debugger-entry.input { + color: var(--accent-blue); +} + +.debugger-entry.input .debugger-entry-icon { + color: var(--accent-blue); +} + +/* Debugger input area */ +.debugger-input-area { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 16px; + background: var(--bg-primary); + border-top: 1px solid var(--border); + flex-shrink: 0; +} + +.debugger-input-prompt { + color: var(--accent-blue); + font-family: var(--font-mono); + font-weight: 700; + font-size: 16px; + line-height: 1; +} + +.debugger-input-wrapper { + position: relative; + flex: 1; +} + +#debugger-input { + width: 100%; + background: transparent; + border: none; + outline: none; + color: var(--text-primary); + font-family: var(--font-mono); + font-size: 12px; + caret-color: var(--accent-blue); + padding: 4px 0; +} + +#debugger-input::placeholder { + color: var(--text-muted); +} + +/* Smart Suggestions dropdown */ +.debugger-suggestions { + position: absolute; + bottom: calc(100% + 8px); + left: 0; + right: 0; + background: var(--bg-card); + border: 1px solid var(--border-bright); + border-radius: var(--radius); + max-height: 240px; + overflow-y: auto; + display: none; + box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.3); + z-index: 300; +} + +.debugger-suggestions.open { + display: block; +} + +.debugger-suggestion-item { + display: flex; + align-items: center; + gap: 10px; + padding: 8px 14px; + cursor: pointer; + transition: background var(--transition-fast); + border-bottom: 1px solid rgba(39, 39, 42, 0.3); +} + +.debugger-suggestion-item:last-child { + border-bottom: none; +} + +.debugger-suggestion-item:hover, +.debugger-suggestion-item.selected { + background: var(--bg-hover); +} + +.debugger-suggestion-item.selected { + background: var(--accent-glow); +} + +.suggestion-icon { + flex-shrink: 0; + width: 20px; + height: 20px; + display: flex; + align-items: center; + justify-content: center; + border-radius: 4px; + font-size: 11px; + font-weight: 700; + font-family: var(--font-mono); +} + +.suggestion-icon.cmd { + background: rgba(59, 130, 246, 0.15); + color: var(--accent-blue); +} + +.suggestion-icon.bash { + background: rgba(16, 185, 129, 0.15); + color: var(--accent); +} + +.suggestion-icon.debug { + background: rgba(139, 92, 246, 0.15); + color: var(--accent-purple); +} + +.suggestion-icon.script { + background: rgba(245, 158, 11, 0.15); + color: var(--accent-orange); +} + +.suggestion-content { + flex: 1; + min-width: 0; +} + +.suggestion-title { + font-family: var(--font-mono); + font-size: 12px; + font-weight: 500; + color: var(--text-primary); +} + +.suggestion-desc { + font-family: var(--font-ui); + font-size: 11px; + color: var(--text-muted); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.suggestion-kbd { + font-family: var(--font-mono); + font-size: 10px; + color: var(--text-muted); + background: var(--bg-primary); + padding: 2px 6px; + border-radius: 3px; + border: 1px solid var(--border); + flex-shrink: 0; +} + +/* Debugger toggle active state in header */ +#btn-debugger-toggle.active { + color: var(--accent); + background: var(--accent-glow); + border-color: var(--accent-dim); +} + +/* Body adjustment when debugger is open */ +body.debugger-open #app-main { + height: calc(100vh - var(--header-height) - var(--debugger-height, 280px)); +} + +/* ─── Animations ─────────────────────────────────────── */ +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(4px); + } + + to { + opacity: 1; + transform: translateY(0); + } +} + +.animate-in { + animation: fadeIn var(--transition) forwards; +} + +/* ─── Replay Modal ───────────────────────── */ + +#replay-modal { + width: min(1100px, 95vw); + height: min(800px, 90vh); + + display: flex; + flex-direction: column; +} + +.replay-controls { + display: flex; + gap: 10px; + align-items: center; +} + +#replay-terminal { + flex: 1; + + overflow-y: auto; + + padding: 16px; + + background: #0d1117; + + border-radius: 10px; + + font-family: monospace; + font-size: 13px; + line-height: 1.5; +} + +#replay-metadata { + padding: 12px 16px; + + border-bottom: 1px solid var(--border-color); + + color: var(--text-secondary); + + font-size: 13px; +} + +.replay-line { + white-space: pre-wrap; + word-break: break-word; + + margin-bottom: 2px; +} + +.replay-line.stdout { + color: #e6edf3; +} + +.replay-line.error { + color: #ff7b72; +} + +.replay-line.system { + color: #7ee787; +} +/* ─── Replay Buttons ───────────────────── */ + +.history-replay-btn { + padding: 6px 10px; + border: 1px solid var(--border-color); + background: var(--bg-secondary); + color: var(--text-primary); + border-radius: 8px; + cursor: pointer; + transition: all 0.2s ease; +} + +.history-replay-btn:hover { + background: var(--accent-color); + color: white; +} +/* ─── Analytics Dashboard ───────────────── */ +#analytics-modal { + width: min(1200px, 95vw); + max-height: 90vh; + overflow-y: auto; +} + +#analytics-content { + padding: 24px; + display: flex; + flex-direction: column; + gap: 24px; +} + +.analytics-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); + gap: 16px; + margin: 0; +} + +.analytics-card { + padding: 16px 20px; + border-radius: var(--radius-lg); + background: var(--bg-tertiary); + border: 1px solid var(--border); + border-top: 2px solid var(--border-bright); + display: flex; + flex-direction: column; + transition: var(--transition-fast); +} + +.analytics-card:hover { + border-color: var(--border-bright); + transform: translateY(-2px); +} + +.analytics-card:nth-child(1) { border-top-color: var(--accent-blue); } +.analytics-card:nth-child(2) { border-top-color: var(--accent); } +.analytics-card:nth-child(3) { border-top-color: var(--accent-red); } +.analytics-card:nth-child(4) { border-top-color: var(--accent-orange); } + +.analytics-card h3 { + margin: 0; + font-size: 11px; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); + font-weight: 600; +} + +.analytics-card-value { + font-size: 32px; + font-weight: 700; + color: var(--text-primary); + line-height: 1; + font-family: var(--font-mono); + margin-top: 4px; +} + +.analytics-card-value.success { color: var(--accent); } +.analytics-card-value.failed { color: var(--accent-red); } +.analytics-card-value.runtime { color: var(--accent-orange); } + +.analytics-card-icon { + width: 16px; + height: 16px; + color: var(--text-muted); +} +.analytics-card-icon.total { color: var(--accent-blue); } +.analytics-card-icon.success { color: var(--accent); } +.analytics-card-icon.failed { color: var(--accent-red); } +.analytics-card-icon.runtime { color: var(--accent-orange); } + +.analytics-section { + margin-top: 0; +} + +.analytics-section h3 { + font-size: 12px; + font-weight: 600; + color: var(--text-primary); + margin-bottom: 12px; + text-transform: uppercase; + letter-spacing: 0.05em; + border-bottom: 1px solid var(--border); + padding-bottom: 6px; +} + +.analytics-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 8px 12px; + margin-bottom: 6px; + border-radius: var(--radius); + background: var(--bg-tertiary); + border: 1px solid var(--border); + transition: var(--transition-fast); +} + +.analytics-item:hover { + border-color: var(--border-bright); + background: var(--bg-hover); +} + +.analytics-item-name { + font-family: var(--font-mono); + font-size: 11px; + color: var(--text-secondary); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 220px; +} + +.analytics-item-meta { + font-family: var(--font-mono); + font-size: 11px; + font-weight: 600; + white-space: nowrap; +} + +/* Graceful Empty State */ +.analytics-dashboard-empty { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 40px; + text-align: center; + color: var(--text-muted); +} + +.analytics-dashboard-empty h3 { + font-size: 15px; + font-weight: 600; + color: var(--text-primary); + margin-top: 16px; + margin-bottom: 8px; +} + +.analytics-dashboard-empty p { + font-size: 12px; + max-width: 320px; + line-height: 1.5; +} + +.analytics-empty-icon { + color: var(--text-muted); + opacity: 0.4; +} + +/* Outline Close Button */ +#analytics-close { + background: transparent !important; + border: 1px solid var(--border) !important; + color: var(--text-secondary) !important; + padding: 6px 14px !important; + font-size: 12px !important; + border-radius: var(--radius) !important; + cursor: pointer !important; + transition: var(--transition-fast) !important; +} + +#analytics-close:hover { + background: rgba(255, 255, 255, 0.05) !important; + color: var(--text-primary) !important; + border-color: var(--border-bright) !important; +} + +/* ─── Reliability Dashboard ─────────────────────────────── */ + +.reliability-modal { + width: min(1200px, 95vw); + max-height: 92vh; +} + +.reliability-modal .modal-body { + overflow-y: auto; + max-height: calc(92vh - 72px); +} + +.reliability-modal-subtitle { + margin: 4px 0 0; + font-size: 13px; + color: var(--text-secondary); +} + +.reliability-updated-at { + display: block; + margin-top: 4px; + font-size: 12px; + color: var(--text-secondary); + font-family: var(--font-mono); + opacity: 0.9; +} + +.reliability-header-actions { + display: flex; + align-items: center; + gap: 8px; +} + +.reliability-toolbar { + display: flex; + flex-wrap: wrap; + gap: 12px; + align-items: flex-end; + margin-bottom: 16px; +} + +.reliability-search-group { + flex: 1; + min-width: 200px; +} + +.reliability-filter-group { + display: flex; + flex-wrap: wrap; + gap: 6px; +} + +.reliability-filter { + padding: 6px 12px; + border-radius: 6px; + border: 1px solid var(--border-color); + background: var(--bg-secondary); + color: var(--text-secondary); + font-size: 12px; + cursor: pointer; + transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease, transform 0.1s ease; +} + +.reliability-filter:hover { + color: var(--text-primary); + border-color: var(--accent); +} + +.reliability-filter:focus-visible { + outline: 2px solid var(--accent); + outline-offset: 2px; +} + +.reliability-filter.active { + background: var(--accent); + color: #fff; + border-color: var(--accent); +} + +/* Status banners: loading, error, corrupted/warning */ +.reliability-status-banner { + display: flex; + align-items: flex-start; + gap: 10px; + padding: 10px 14px; + border-radius: 8px; + margin-bottom: 14px; + font-size: 13px; + line-height: 1.45; + transition: opacity 0.2s ease; +} + +.reliability-status-banner p { + margin: 0 0 4px; +} + +.reliability-status-banner p:last-child { + margin-bottom: 0; +} + +.reliability-status-icon { + flex-shrink: 0; + width: 8px; + height: 8px; + margin-top: 5px; + border-radius: 50%; +} + +.reliability-status-banner.loading { + background: rgba(59, 130, 246, 0.12); + border: 1px solid rgba(59, 130, 246, 0.35); + color: #93c5fd; +} + +.reliability-status-banner.loading .reliability-status-icon { + background: #93c5fd; + animation: reliability-pulse 1.2s ease-in-out infinite; +} + +.reliability-status-banner.error { + background: rgba(220, 53, 69, 0.12); + border: 1px solid rgba(220, 53, 69, 0.35); + color: #f8a5a5; +} + +.reliability-status-banner.error .reliability-status-icon { + background: #f87171; + box-shadow: 0 0 0 3px rgba(248, 113, 113, 0.25); +} + +.reliability-status-banner.warning { + background: rgba(234, 179, 8, 0.1); + border: 1px solid rgba(234, 179, 8, 0.4); + color: #fde047; +} + +.reliability-status-banner.warning .reliability-status-icon { + background: #facc15; + box-shadow: 0 0 0 3px rgba(250, 204, 21, 0.2); +} + +.reliability-status-banner.critical { + background: rgba(220, 53, 69, 0.18); + border: 1px solid rgba(220, 53, 69, 0.5); + color: #fecaca; +} + +.reliability-status-banner.critical .reliability-status-icon { + background: #ef4444; + box-shadow: 0 0 0 3px rgba(239, 68, 68, 0.3); +} + +.diagnostic-source-row { + display: flex; + flex-wrap: wrap; + gap: 6px; + margin-top: 8px; +} + +/* Scorecards + shared card base */ +.reliability-scorecards { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); + gap: 12px; + margin-bottom: 20px; +} + +.reliability-card, +.reliability-scorecard { + padding: 16px; + border-radius: 10px; + background: var(--bg-secondary); + border: 1px solid var(--border-color); + transition: border-color 0.2s ease, box-shadow 0.2s ease, transform 0.15s ease; +} + +.reliability-card:hover, +.reliability-scorecard:hover { + box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18); +} + +.reliability-animate-in { + animation: reliability-fade-in 0.28s ease forwards; +} + +.reliability-scorecard h4 { + font-size: 12px; + color: var(--text-secondary); + margin-bottom: 6px; + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.reliability-scorecard .value { + font-size: 26px; + font-weight: 700; + line-height: 1.1; +} + +.reliability-scorecard.score-good { + border-color: rgba(34, 197, 94, 0.45); +} + +.reliability-scorecard.score-warn { + border-color: rgba(234, 179, 8, 0.45); +} + +.reliability-scorecard.score-bad { + border-color: rgba(239, 68, 68, 0.45); +} + +.reliability-scorecard.is-loading { + pointer-events: none; +} + +.reliability-skeleton { + border-radius: 4px; + background: linear-gradient( + 90deg, + var(--bg-tertiary, #1a1d24) 0%, + rgba(255, 255, 255, 0.06) 50%, + var(--bg-tertiary, #1a1d24) 100% + ); + background-size: 200% 100%; + animation: reliability-shimmer 1.4s ease-in-out infinite; +} + +.reliability-skeleton-label { + height: 12px; + width: 60%; + margin-bottom: 10px; +} + +.reliability-skeleton-value { + height: 28px; + width: 45%; +} + +/* Panels */ +.reliability-panels { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 14px; +} + +.reliability-panel { + background: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 10px; + padding: 14px; + transition: border-color 0.2s ease; +} + +.reliability-panel--trends { + border-top: 2px solid rgba(59, 130, 246, 0.45); +} + +.reliability-panel--failures { + border-top: 2px solid rgba(239, 68, 68, 0.4); +} + +.reliability-panel--recommendations { + border-top: 2px solid rgba(234, 179, 8, 0.4); +} + +.reliability-panel--orchestration { + border-top: 2px solid rgba(148, 163, 184, 0.45); + margin-bottom: 14px; +} + +.diagnostic-indicator-row { + display: flex; + flex-wrap: wrap; + gap: 8px; + margin-top: 6px; +} + +.diagnostic-pill { + display: inline-block; + padding: 4px 10px; + border-radius: 999px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.03em; + border: 1px solid transparent; +} + +.diagnostic-pill.ok { + background: rgba(34, 197, 94, 0.15); + color: #86efac; + border-color: rgba(34, 197, 94, 0.35); +} + +.diagnostic-pill.warn { + background: rgba(234, 179, 8, 0.15); + color: #fde047; + border-color: rgba(234, 179, 8, 0.35); +} + +.diagnostic-pill.error { + background: rgba(239, 68, 68, 0.15); + color: #fca5a5; + border-color: rgba(239, 68, 68, 0.35); +} + +.diagnostic-replay-unstable { + border-left: 3px solid rgba(249, 115, 22, 0.55); +} + +.diagnostic-corrupted { + border-left: 3px solid rgba(239, 68, 68, 0.55); +} + +.diagnostic-workspace { + border-left: 3px solid rgba(234, 179, 8, 0.5); +} + +.replay-diagnostics { + margin: 10px 14px 0; + padding: 10px 12px; + border-radius: 8px; + font-size: 13px; + line-height: 1.45; +} + +.replay-diagnostics.warning { + background: rgba(234, 179, 8, 0.1); + border: 1px solid rgba(234, 179, 8, 0.35); + color: #fde047; +} + +.replay-diagnostics-reasons { + display: block; + margin-top: 4px; + font-size: 12px; + opacity: 0.9; +} + +.history-diagnostic-badge { + display: inline-block; + padding: 2px 6px; + border-radius: 4px; + font-size: 10px; + font-weight: 700; + text-transform: uppercase; + background: rgba(249, 115, 22, 0.2); + color: #fdba74; +} + +.reliability-panel-wide { + grid-column: 1 / -1; +} + +.reliability-panel h3 { + font-size: 13px; + margin-bottom: 10px; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.04em; +} + +.reliability-panel-content { + display: flex; + flex-direction: column; + gap: 8px; + max-height: 220px; + overflow-y: auto; + scroll-behavior: smooth; +} + +.reliability-panel-content:focus-within { + outline: none; +} + +/* List cards */ +.reliability-item { + padding: 10px 12px; + border-radius: 8px; + background: var(--bg-tertiary, #1a1d24); + border: 1px solid var(--border-color); + font-size: 13px; + list-style: none; + transition: background 0.15s ease, border-color 0.15s ease; +} + +.reliability-item[role="listitem"] { + display: block; +} + +.reliability-item:hover { + border-color: rgba(255, 255, 255, 0.12); +} + +.reliability-item-head { + display: flex; + justify-content: space-between; + align-items: center; + gap: 8px; + margin-bottom: 4px; + flex-wrap: wrap; +} + +.reliability-item-meta { + color: var(--text-secondary); + font-size: 12px; +} + +/* Badges: priority, trend, score */ +.reliability-badge, +.failure-badge { + display: inline-block; + padding: 2px 8px; + border-radius: 4px; + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.02em; + white-space: nowrap; +} + +.reliability-badge.critical, +.reliability-recommendation--critical { --rec-accent: #ef4444; } +.reliability-badge.high, +.reliability-recommendation--high { --rec-accent: #f97316; } +.reliability-badge.medium, +.reliability-recommendation--medium { --rec-accent: #eab308; } +.reliability-badge.info, +.reliability-recommendation--info { --rec-accent: #3b82f6; } + +.reliability-badge.critical { background: rgba(239, 68, 68, 0.2); color: #fca5a5; } +.reliability-badge.high { background: rgba(249, 115, 22, 0.2); color: #fdba74; } +.reliability-badge.medium { background: rgba(234, 179, 8, 0.2); color: #fde047; } +.reliability-badge.info { background: rgba(59, 130, 246, 0.2); color: #93c5fd; } + +.reliability-badge.trend-improving { background: rgba(34, 197, 94, 0.2); color: #86efac; } +.reliability-badge.trend-degrading { background: rgba(239, 68, 68, 0.2); color: #fca5a5; } +.reliability-badge.trend-stable { background: rgba(148, 163, 184, 0.2); color: #cbd5e1; } + +.reliability-badge.score-good { background: rgba(34, 197, 94, 0.2); color: #86efac; } +.reliability-badge.score-warn { background: rgba(234, 179, 8, 0.2); color: #fde047; } +.reliability-badge.score-bad { background: rgba(239, 68, 68, 0.2); color: #fca5a5; } + +.reliability-badge.indicator-flaky { background: rgba(249, 115, 22, 0.22); color: #fdba74; } +.reliability-badge.indicator-slow { background: rgba(234, 179, 8, 0.22); color: #fde047; } +.reliability-badge.indicator-regressed { + background: rgba(239, 68, 68, 0.18); + color: #fca5a5; +} + +/* Failure type badges */ +.failure-badge--permission_error { background: rgba(168, 85, 247, 0.2); color: #d8b4fe; } +.failure-badge--dependency_error { background: rgba(59, 130, 246, 0.2); color: #93c5fd; } +.failure-badge--timeout { background: rgba(234, 179, 8, 0.2); color: #fde047; } +.failure-badge--shell_error { background: rgba(249, 115, 22, 0.2); color: #fdba74; } +.failure-badge--missing_file { background: rgba(148, 163, 184, 0.25); color: #e2e8f0; } +.failure-badge--interrupted { background: rgba(100, 116, 139, 0.25); color: #cbd5e1; } +.failure-badge--unknown_failure { background: rgba(239, 68, 68, 0.15); color: #fca5a5; } + +.reliability-failure-card { + border-left: 3px solid rgba(239, 68, 68, 0.55); +} + +/* Trend cards */ +.reliability-trend-card { + border-left: 3px solid rgba(59, 130, 246, 0.5); +} + +.reliability-trend-card.trend-improving { + border-left-color: rgba(34, 197, 94, 0.65); +} + +.reliability-trend-card.trend-degrading { + border-left-color: rgba(239, 68, 68, 0.65); +} + +/* Recommendation blocks */ +.reliability-recommendation { + border-left: 3px solid var(--rec-accent, #3b82f6); + padding-left: 12px; +} + +.reliability-recommendation .reliability-item-head { + margin-bottom: 6px; +} + +/* Script rows */ +.reliability-script-row { + display: grid; + grid-template-columns: 1fr auto auto minmax(100px, auto); + gap: 10px; + align-items: center; +} + +.reliability-script-row .script-name { + font-weight: 600; + font-family: var(--font-mono); + font-size: 12px; + word-break: break-all; +} + +.reliability-script-row .reliability-script-stats { + font-size: 12px; + color: var(--text-secondary); + text-align: right; +} + +/* Empty + loading states */ +.reliability-empty { + display: flex; + align-items: center; + justify-content: center; + min-height: 52px; + padding: 12px; + border-radius: 8px; + border: 1px dashed var(--border-color); + color: var(--text-secondary); + font-size: 13px; + text-align: center; + font-style: italic; +} + +.reliability-empty--loading { + border-style: solid; + background: rgba(59, 130, 246, 0.05); + animation: reliability-pulse 1.5s ease-in-out infinite; +} + +@keyframes reliability-fade-in { + from { + opacity: 0; + transform: translateY(4px); + } + to { + opacity: 1; + transform: translateY(0); + } +} + +@keyframes reliability-shimmer { + 0% { background-position: 100% 0; } + 100% { background-position: -100% 0; } +} + +@keyframes reliability-pulse { + 0%, 100% { opacity: 1; } + 50% { opacity: 0.55; } +} + +@media (max-width: 900px) { + .reliability-panels { + grid-template-columns: 1fr; + } + .reliability-script-row { + grid-template-columns: 1fr; + gap: 6px; + } + .reliability-script-row .reliability-script-stats { + text-align: left; + } +} + +@media (prefers-reduced-motion: reduce) { + .reliability-animate-in, + .reliability-skeleton, + .reliability-status-banner.loading .reliability-status-icon, + .reliability-empty--loading { + animation: none; + } + .reliability-card:hover, + .reliability-scorecard:hover { + transform: none; + } +} + +/* ─── Progress Tracker Panel ───────────────────────────── */ +.progress-tracker-panel { + background: var(--bg-secondary); + border-bottom: 1px solid var(--border); + padding: 10px 16px; + display: flex; + flex-direction: column; + gap: 6px; +} + +.progress-tracker-header { + display: flex; + justify-content: space-between; + align-items: center; +} + +.progress-tracker-title { + font-size: 11px; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.5px; + color: var(--text-secondary); +} + +.progress-tracker-step { + font-family: var(--font-mono); + font-size: 11px; + font-weight: 500; + color: var(--accent); +} + +.progress-bar-container { + height: 4px; + background: var(--bg-tertiary); + border-radius: 2px; + overflow: hidden; + width: 100%; +} + +.progress-bar-fill { + height: 100%; + background: var(--accent); + width: 0%; + transition: width 0.2s ease; +} + +.progress-tracker-footer { + display: flex; + justify-content: space-between; + align-items: center; + font-size: 11px; +} + +.progress-tracker-command { + font-family: var(--font-mono); + color: var(--text-primary); + background: var(--bg-tertiary); + padding: 1px 6px; + border-radius: var(--radius); + border: 1px solid var(--border); + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 70%; +} + +.progress-tracker-status { + color: var(--text-secondary); + display: flex; + align-items: center; + gap: 6px; +} + +.progress-tracker-status::before { + content: ''; + display: inline-block; + width: 6px; + height: 6px; + border-radius: 50%; + background: var(--text-muted); +} + +.progress-tracker-status.running::before { + background: var(--accent-orange); +} + +.progress-tracker-status.success::before { + background: var(--accent); +} + +.progress-tracker-status.failed::before { + background: var(--accent-red); +} +/* ─── Workspace Recovery ───────────────── */ + +.workspace-recovery-actions { + display: flex; + gap: 12px; + margin-top: 20px; + flex-wrap: wrap; +} + +.workspace-recovery-actions .btn { + flex: 1; +} + +#workspace-safe-btn { + border: 1px solid var(--accent-yellow); + color: var(--accent-yellow); +} +/* ─── Workspace Profiles ───────────────── */ + +.workspace-profile-create { + display: flex; + gap: 12px; + margin-bottom: 20px; +} + +.workspace-profile-create input { + flex: 1; +} + +.workspace-profile-item { + display: flex; + justify-content: space-between; + align-items: center; + padding: 12px; + margin-bottom: 10px; + border-radius: 10px; + background: var(--bg-secondary); + border: 1px solid var(--border-color); +} + +.workspace-profile-actions { + display: flex; + gap: 8px; +} +/* ─── Workspace Snapshot Metadata ─────── */ + +.workspace-snapshot-meta { + margin-top: 16px; + font-size: 13px; + color: var(--text-secondary); + opacity: 0.8; +} +/* ─── LIGHT THEME OVERRIDES ─── */ +body.light-theme { + background-color: #f8f9fa !important; + color: #212529 !important; + --border-color: #dee2e6; +} + +body.light-theme #app-header { + background-color: #ffffff !important; + border-bottom: 1px solid #dee2e6 !important; +} + +body.light-theme #app-header h1 { + color: #212529 !important; +} + +body.light-theme #sidebar { + background-color: #e9ecef !important; + border-right: 1px solid #dee2e6 !important; +} + +body.light-theme .sidebar-search-wrapper input { + background: #ffffff !important; + color: #212529 !important; + border: 1px solid #ced4da !important; +} + +body.light-theme #cli-area { + background-color: #ffffff !important; +} + +body.light-theme .cli-header { + background-color: #f1f3f5 !important; + border-bottom: 1px solid #dee2e6 !important; + color: #212529 !important; +} + +body.light-theme .cli-body { + background-color: #ffffff !important; + color: #212529 !important; +} + +body.light-theme .cli-welcome-text { + color: #495057 !important; +} + +body.light-theme #cli-input { + background-color: #f1f3f5 !important; + color: #212529 !important; + border: 1px solid #ced4da !important; +} + +body.light-theme #analysis-panel { + background-color: #e9ecef !important; + border-left: 1px solid #dee2e6 !important; + color: #212529 !important; +} + +body.light-theme .analysis-empty h3 { + color: #212529 !important; +} +/* ───────────────────────────── */ +/* ─── Issue #104 PREMIUM ENHANCEMENTS ─── */ + +.category-section { + display: block !important; + margin-bottom: 4px; +} + +.category-header { + display: flex !important; + align-items: center; + gap: 10px; + padding: 8px 20px; + cursor: pointer; + transition: var(--transition-fast); + user-select: none; + color: var(--text-secondary); +} + +.tab-terminal-icon { + font-family: var(--font-mono); + font-size: 11px; + color: var(--accent); + opacity: 0.8; +} + +/* Tab Glow / Underline */ +.cli-tab.active { + background: var(--bg-primary) !important; + border-left: 1px solid var(--border) !important; + border-right: 1px solid var(--border) !important; + border-top: 2px solid var(--accent) !important; + border-bottom: 1px solid var(--bg-primary) !important; + color: var(--text-primary) !important; + box-shadow: none !important; + height: 33px !important; + padding: 0 16px !important; + z-index: 2; +} + +body.light-theme .cli-tab.active { + background: #ffffff !important; + border-left-color: #dee2e6 !important; + border-right-color: #dee2e6 !important; + border-bottom-color: #ffffff !important; +} + +body.light-theme .cli-welcome-sub { + color: #868e96 !important; +} + +/* Themed Modal Inputs */ +.modal input, +.modal textarea, +.modal select, +.modal-overlay input { + background: var(--bg-tertiary) !important; + border: 1px solid var(--border) !important; + border-radius: var(--radius) !important; + color: var(--text-primary) !important; + font-family: var(--font-ui) !important; + font-size: 13px !important; + padding: 8px 12px !important; + outline: none !important; + transition: var(--transition-fast) !important; +} + +.modal input:focus, +.modal textarea:focus, +.modal select:focus, +.modal-overlay input:focus { + border-color: var(--accent) !important; + box-shadow: 0 0 0 2px var(--accent-glow) !important; +} + +/* Custom Dropdown select style */ +.modal select, +.modal-overlay select { + appearance: none !important; + -webkit-appearance: none !important; + -moz-appearance: none !important; + background-image: url("data:image/svg+xml;utf8,") !important; + background-repeat: no-repeat !important; + background-position: right 8px center !important; + background-size: 16px !important; + padding-right: 28px !important; + cursor: pointer !important; +} + +body.light-theme .modal select, +body.light-theme .modal-overlay select { + background-image: url("data:image/svg+xml;utf8,") !important; +} + +/* Analytics Lists Grid styling */ +.analytics-lists-grid { + display: grid; + grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); + gap: 20px; + margin: 0; +} +.analytics-lists-grid .analytics-section { + margin-top: 0; +} + + +/* Collapsible Sidebar transition and collapsed classes */ +#sidebar { + transition: width 0.3s cubic-bezier(0.4, 0, 0.2, 1), min-width 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important; +} + +#sidebar.collapsed { + width: 0 !important; + min-width: 0 !important; + border-right: none !important; + overflow: hidden !important; +} + +#resizer-left.collapsed { + display: none !important; +} + +/* Tab status indicators */ +.tab-spinner { + display: inline-block; + width: 10px; + height: 10px; + border: 2px solid var(--accent); + border-top-color: transparent; + border-radius: 50%; + animation: spin 1s linear infinite; + margin-left: 6px; + vertical-align: middle; +} + +.tab-success { + color: var(--accent) !important; + margin-left: 6px; + font-weight: bold; + vertical-align: middle; +} + +.tab-failed { + color: var(--accent-red) !important; + margin-left: 6px; + font-weight: bold; + vertical-align: middle; +} + +@keyframes spin { + to { transform: rotate(360deg); } +} + +/* Terminal Font Scaling custom property */ +.cli-body { + font-size: var(--terminal-font-size, 13px) !important; +} +#cli-input { + font-size: var(--terminal-font-size, 13px) !important; +} +.cli-prompt-input { + font-size: var(--terminal-font-size, 13px) !important; +} + +/* Analytics Progress Bars */ +.analytics-progress-bar { + height: 4px; + background: rgba(255, 255, 255, 0.06); + border-radius: 2px; + margin-top: 12px; + margin-bottom: 6px; + overflow: hidden; + width: 100%; +} + +.analytics-progress-fill { + height: 100%; + width: 0%; + transition: width 0.4s ease-out; +} + +.success-bar .analytics-progress-fill { + background: var(--accent, #22c55e); + box-shadow: 0 0 6px rgba(16, 185, 129, 0.3); +} + +.failed-bar .analytics-progress-fill { + background: var(--accent-red, #dc3545); + box-shadow: 0 0 6px rgba(239, 68, 68, 0.3); +} + +.neutral-bar .analytics-progress-fill { + background: var(--border-bright); +} + +.runtime-bar .analytics-progress-fill { + background: var(--accent-orange); + box-shadow: 0 0 6px rgba(245, 158, 11, 0.3); +} + +.analytics-rate-label { + font-size: 11px !important; + color: var(--text-muted); + font-weight: 500 !important; + margin-top: 2px; +} \ No newline at end of file