From c1ec28eacee660964460386bb957a69ef3ff31ce Mon Sep 17 00:00:00 2001 From: arkon Date: Sun, 18 Jan 2026 22:18:17 +0100 Subject: [PATCH] feat(ui): add help modal and copy terminal button - Add floating help button (?) with keyboard shortcuts modal - Add Ctrl+? shortcut to open help modal - Add copy terminal button to copy output to clipboard - Style kbd elements for keyboard shortcut display - Update Escape key to close all modals Co-Authored-By: Claude Opus 4.5 --- src/web/public/app.js | 39 +++++++++++++++++++++ src/web/public/index.html | 68 ++++++++++++++++++++++++++++++++++++ src/web/public/styles.css | 72 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 179 insertions(+) diff --git a/src/web/public/app.js b/src/web/public/app.js index 0444ae07..908fd8e8 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -158,6 +158,13 @@ class ClaudemanApp { // Escape to close modals if (e.key === 'Escape') { this.closeCaseSelector(); + this.closeHelp(); + } + + // Ctrl/Cmd + ? to show help + if ((e.ctrlKey || e.metaKey) && (e.key === '?' || e.key === '/')) { + e.preventDefault(); + this.showHelp(); } // Ctrl/Cmd + Enter to quick start (when not in terminal) @@ -726,6 +733,15 @@ class ClaudemanApp { document.getElementById('caseSelectorModal').classList.remove('active'); } + // Help modal + showHelp() { + document.getElementById('helpModal').classList.add('active'); + } + + closeHelp() { + document.getElementById('helpModal').classList.remove('active'); + } + // Actions async startRun() { const prompt = document.getElementById('promptInput').value.trim(); @@ -971,6 +987,29 @@ class ClaudemanApp { this.terminal.writeln('\x1b[90mTerminal cleared\x1b[0m'); } + // Copy terminal contents to clipboard + async copyTerminal() { + try { + // Get all text from terminal + const buffer = this.terminal.buffer.active; + let text = ''; + for (let i = 0; i < buffer.length; i++) { + const line = buffer.getLine(i); + if (line) { + text += line.translateToString(true) + '\n'; + } + } + + // Strip trailing empty lines + text = text.replace(/\n+$/, '\n'); + + await navigator.clipboard.writeText(text); + this.showToast('Terminal output copied to clipboard', 'success'); + } catch (err) { + this.showToast('Failed to copy: ' + err.message, 'error'); + } + } + // Font size controls increaseFontSize() { const current = this.terminal.options.fontSize || 14; diff --git a/src/web/public/index.html b/src/web/public/index.html index 1bc903c8..464b5c3d 100644 --- a/src/web/public/index.html +++ b/src/web/public/index.html @@ -240,6 +240,7 @@ 14px + @@ -277,6 +278,73 @@ + + + + + + diff --git a/src/web/public/styles.css b/src/web/public/styles.css index 1162cc5f..eeb3b480 100644 --- a/src/web/public/styles.css +++ b/src/web/public/styles.css @@ -1694,3 +1694,75 @@ input[type="checkbox"] { align-items: center; gap: 0.5rem; } + +/* Keyboard Shortcuts Modal */ +.shortcuts-list { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.shortcut-group h4 { + font-size: 0.75rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.05em; + color: var(--text-muted); + margin-bottom: 0.75rem; + padding-bottom: 0.5rem; + border-bottom: 1px solid var(--border); +} + +.shortcut-row { + display: flex; + justify-content: space-between; + align-items: center; + padding: 0.5rem 0; + font-size: 0.875rem; +} + +.shortcut-row span { + color: var(--text-dim); +} + +kbd { + display: inline-block; + padding: 0.25rem 0.5rem; + background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%); + border: 1px solid var(--border); + border-radius: 4px; + font-family: 'SF Mono', Monaco, monospace; + font-size: 0.75rem; + color: var(--text); + box-shadow: 0 2px 0 var(--border); + min-width: 24px; + text-align: center; +} + +/* Help button */ +.help-btn { + position: fixed; + bottom: 1rem; + right: 1rem; + width: 40px; + height: 40px; + border-radius: 50%; + background: linear-gradient(135deg, #2a2a2a 0%, #1a1a1a 100%); + border: 1px solid var(--border); + color: var(--text-dim); + font-size: 1.25rem; + cursor: pointer; + z-index: 100; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s ease; + box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3); +} + +.help-btn:hover { + background: linear-gradient(135deg, #3a3a3a 0%, #2a2a2a 100%); + color: var(--text); + transform: translateY(-2px); + box-shadow: 0 6px 16px rgba(0, 0, 0, 0.4); +}