From 431732e32643214b7192bdc12345bd012016b20c Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 18 Jan 2026 21:42:57 +0100 Subject: [PATCH] feat(ui): add keyboard shortcuts and improve states Keyboard Shortcuts: - Ctrl+Enter: Quick Start - Ctrl+K: Kill All Sessions - Ctrl+L: Clear Terminal - Ctrl+1/2/3: Switch Tabs - Escape: Close modals UI Improvements: - Add keyboard shortcuts to terminal welcome message - Improve scrollbar styling with gradients - Add tooltip base styles - Add loading states (loading, loading-lg, loading-container) - Add empty state styles with icon/title/text - Add error state styling with red background - Add success state styling with green background Co-Authored-By: Claude Opus 4.5 --- src/web/public/app.js | 50 +++++++++++++-- src/web/public/styles.css | 125 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 166 insertions(+), 9 deletions(-) diff --git a/src/web/public/app.js b/src/web/public/app.js index 39e2f4c6..51609a0d 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -93,10 +93,12 @@ class ClaudemanApp { this.fitAddon.fit(); // Welcome message - this.terminal.writeln('\x1b[1;36m╔═══════════════════════════════════════════════════════════╗'); - this.terminal.writeln('║ \x1b[1;33m⚡ Claudeman Terminal \x1b[1;36m ║'); - this.terminal.writeln('║ \x1b[0;90mRun prompts to see output here\x1b[1;36m ║'); - this.terminal.writeln('╚═══════════════════════════════════════════════════════════╝\x1b[0m'); + this.terminal.writeln('\x1b[1;36m╔═══════════════════════════════════════════════════════════════╗'); + this.terminal.writeln('║ \x1b[1;33m⚡ Claudeman Terminal \x1b[1;36m ║'); + this.terminal.writeln('╠═══════════════════════════════════════════════════════════════╣'); + this.terminal.writeln('║ \x1b[0;90mCtrl+Enter\x1b[1;36m Quick Start \x1b[0;90mCtrl+K\x1b[1;36m Kill All Sessions ║'); + this.terminal.writeln('║ \x1b[0;90mCtrl+L\x1b[1;36m Clear Terminal \x1b[0;90mCtrl+1/2/3\x1b[1;36m Switch Tabs ║'); + this.terminal.writeln('╚═══════════════════════════════════════════════════════════════╝\x1b[0m'); this.terminal.writeln(''); // Handle resize @@ -148,6 +150,46 @@ class ClaudemanApp { document.getElementById('quickStartCase').addEventListener('change', (e) => { this.handleQuickStartSelect(e.target.value); }); + + // Keyboard shortcuts + document.addEventListener('keydown', (e) => { + // Escape to close modals + if (e.key === 'Escape') { + this.closeCaseSelector(); + } + + // Ctrl/Cmd + Enter to quick start (when not in terminal) + if ((e.ctrlKey || e.metaKey) && e.key === 'Enter' && document.activeElement.tagName !== 'TEXTAREA') { + e.preventDefault(); + this.quickStart(); + } + + // Ctrl/Cmd + K to kill all sessions + if ((e.ctrlKey || e.metaKey) && e.key === 'k') { + e.preventDefault(); + this.killAllSessions(); + } + + // Ctrl/Cmd + L to clear terminal + if ((e.ctrlKey || e.metaKey) && e.key === 'l' && document.activeElement !== document.getElementById('promptInput')) { + e.preventDefault(); + this.clearTerminal(); + } + + // Ctrl/Cmd + 1/2/3 to switch tabs + if ((e.ctrlKey || e.metaKey) && e.key === '1') { + e.preventDefault(); + this.switchTab('run'); + } + if ((e.ctrlKey || e.metaKey) && e.key === '2') { + e.preventDefault(); + this.switchTab('cases'); + } + if ((e.ctrlKey || e.metaKey) && e.key === '3') { + e.preventDefault(); + this.switchTab('settings'); + } + }); } // Settings diff --git a/src/web/public/styles.css b/src/web/public/styles.css index 3ef546c6..6a726ac8 100644 --- a/src/web/public/styles.css +++ b/src/web/public/styles.css @@ -699,10 +699,58 @@ body { .msg-cost { color: var(--green); font-weight: 500; } /* Scrollbar */ -::-webkit-scrollbar { width: 8px; height: 8px; } -::-webkit-scrollbar-track { background: var(--bg-dark); } -::-webkit-scrollbar-thumb { background: var(--border); border-radius: 4px; } -::-webkit-scrollbar-thumb:hover { background: var(--text-muted); } +::-webkit-scrollbar { width: 10px; height: 10px; } +::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.2); + border-radius: 5px; +} +::-webkit-scrollbar-thumb { + background: linear-gradient(135deg, #3a3a3a 0%, #2a2a2a 100%); + border-radius: 5px; + border: 2px solid transparent; + background-clip: padding-box; +} +::-webkit-scrollbar-thumb:hover { + background: linear-gradient(135deg, #4a4a4a 0%, #3a3a3a 100%); + background-clip: padding-box; +} +::-webkit-scrollbar-corner { + background: transparent; +} + +/* Tooltips */ +[title] { + position: relative; +} + +.tooltip { + position: absolute; + bottom: 100%; + left: 50%; + transform: translateX(-50%) translateY(-8px); + background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%); + color: var(--text); + padding: 0.5rem 0.75rem; + border-radius: 6px; + font-size: 0.75rem; + white-space: nowrap; + pointer-events: none; + opacity: 0; + transition: opacity 0.2s ease, transform 0.2s ease; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); + border: 1px solid var(--border); + z-index: 1000; +} + +.tooltip::after { + content: ''; + position: absolute; + top: 100%; + left: 50%; + transform: translateX(-50%); + border: 6px solid transparent; + border-top-color: #1a1a1a; +} /* Responsive */ @media (max-width: 768px) { @@ -743,16 +791,83 @@ body { display: inline-block; width: 16px; height: 16px; - border: 2px solid var(--border); + border: 2px solid rgba(59, 130, 246, 0.2); border-top-color: var(--accent); border-radius: 50%; animation: spin 0.8s linear infinite; } +.loading-lg { + width: 32px; + height: 32px; + border-width: 3px; +} + +.loading-container { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 2rem; + gap: 1rem; + color: var(--text-muted); +} + @keyframes spin { to { transform: rotate(360deg); } } +/* Empty state */ +.empty-state { + text-align: center; + padding: 2rem; + color: var(--text-muted); + font-size: 0.875rem; +} + +.empty-state-icon { + font-size: 2.5rem; + margin-bottom: 1rem; + opacity: 0.5; +} + +.empty-state-title { + font-size: 1rem; + font-weight: 500; + color: var(--text-dim); + margin-bottom: 0.5rem; +} + +.empty-state-text { + font-size: 0.8rem; + color: var(--text-muted); +} + +/* Error state */ +.error-state { + text-align: center; + padding: 2rem; + background: rgba(239, 68, 68, 0.1); + border: 1px solid rgba(239, 68, 68, 0.3); + border-radius: var(--radius); + color: var(--red); +} + +.error-state-icon { + font-size: 2rem; + margin-bottom: 0.5rem; +} + +/* Success state */ +.success-state { + text-align: center; + padding: 1rem; + background: rgba(34, 197, 94, 0.1); + border: 1px solid rgba(34, 197, 94, 0.3); + border-radius: var(--radius); + color: var(--green); +} + /* Tabs */ .tabs { display: flex;