From 035ffb42b962e557cc351be9650f6fafaf372f84 Mon Sep 17 00:00:00 2001 From: arkon Date: Mon, 19 Jan 2026 14:37:20 +0100 Subject: [PATCH] feat(ui): improve respawn duration with preset buttons MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace the simple number input for respawn duration with a preset button group (30m, 1h, 2h, 4h, 8h, ∞, Custom). Move duration to be the first option in the Respawn Controller settings. - Add duration preset buttons with visual highlighting for selection - ∞ (unlimited) is selected by default - Custom option reveals a number input for arbitrary values - Reset to default when modal opens Co-Authored-By: Claude Opus 4.5 --- src/web/public/app.js | 44 +++++++++++++++++++++++++++++++++-- src/web/public/index.html | 27 +++++++++++++++------- src/web/public/styles.css | 48 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 109 insertions(+), 10 deletions(-) diff --git a/src/web/public/app.js b/src/web/public/app.js index 0ece7ac8..1145660b 100644 --- a/src/web/public/app.js +++ b/src/web/public/app.js @@ -1302,20 +1302,60 @@ class ClaudemanApp { respawnSection.style.display = 'none'; } + // Reset duration presets to default (unlimited) + this.selectDurationPreset(''); + document.getElementById('sessionOptionsModal').classList.add('active'); // Focus the name input setTimeout(() => document.getElementById('sessionNameInput').focus(), 100); } + // Handle duration preset selection + selectDurationPreset(value) { + // Remove active from all buttons + document.querySelectorAll('.duration-preset-btn').forEach(btn => btn.classList.remove('active')); + + // Find and activate the clicked button + const btn = document.querySelector(`.duration-preset-btn[data-minutes="${value}"]`); + if (btn) btn.classList.add('active'); + + // Show/hide custom input + const customInput = document.querySelector('.duration-custom-input'); + const durationInput = document.getElementById('modalRespawnDuration'); + + if (value === 'custom') { + customInput.classList.add('visible'); + durationInput.focus(); + } else { + customInput.classList.remove('visible'); + durationInput.value = ''; // Clear custom value when using preset + } + } + + // Get selected duration from preset buttons or custom input + getSelectedDuration() { + const customInput = document.querySelector('.duration-custom-input'); + const durationInput = document.getElementById('modalRespawnDuration'); + + if (customInput.classList.contains('visible')) { + // Custom mode - use input value + return durationInput.value ? parseInt(durationInput.value) : null; + } else { + // Preset mode - get from active button + const activeBtn = document.querySelector('.duration-preset-btn.active'); + const minutes = activeBtn?.dataset.minutes; + return minutes ? parseInt(minutes) : null; + } + } + // Get respawn config from modal inputs getModalRespawnConfig() { const updatePrompt = document.getElementById('modalRespawnPrompt').value; const sendClear = document.getElementById('modalRespawnSendClear').checked; const sendInit = document.getElementById('modalRespawnSendInit').checked; const kickstartPrompt = document.getElementById('modalRespawnKickstart').value.trim() || undefined; - const durationStr = document.getElementById('modalRespawnDuration').value; - const durationMinutes = durationStr ? parseInt(durationStr) : null; + const durationMinutes = this.getSelectedDuration(); // Auto-compact settings const autoCompactEnabled = document.getElementById('modalAutoCompactEnabled').checked; diff --git a/src/web/public/index.html b/src/web/public/index.html index 18339582..3fea64c4 100644 --- a/src/web/public/index.html +++ b/src/web/public/index.html @@ -209,17 +209,28 @@
- - + +
+ + + + + + +
+ +
+ + min +
+
+
+ How long to run the respawn loop
- -
- - min -
- Leave empty for unlimited. Respawn stops after duration. + +
diff --git a/src/web/public/styles.css b/src/web/public/styles.css index 7de21f81..c57de317 100644 --- a/src/web/public/styles.css +++ b/src/web/public/styles.css @@ -1351,6 +1351,54 @@ body { font-size: 0.7rem; } +/* Duration preset buttons */ +.duration-presets { + display: flex; + flex-wrap: wrap; + gap: 0.35rem; + align-items: center; +} + +.duration-preset-btn { + padding: 0.4rem 0.7rem; + font-size: 0.75rem; + background: var(--bg-input); + border: 1px solid var(--border); + border-radius: 4px; + color: var(--text-dim); + cursor: pointer; + transition: all 0.15s; +} + +.duration-preset-btn:hover { + border-color: var(--border-light); + color: var(--text); +} + +.duration-preset-btn.active { + background: var(--accent); + border-color: var(--accent); + color: #fff; +} + +.duration-custom { + display: flex; + align-items: center; + gap: 0.35rem; +} + +.duration-custom-input { + display: none; +} + +.duration-custom-input.visible { + display: flex; +} + +.duration-custom-input input { + width: 60px; +} + /* Respawn actions in modal */ .respawn-actions { display: flex;