feat(ui): improve respawn duration with preset buttons

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 <noreply@anthropic.com>
This commit is contained in:
arkon
2026-01-19 14:37:20 +01:00
co-authored by Claude Opus 4.5
parent c82686f08e
commit 035ffb42b9
3 changed files with 109 additions and 10 deletions
+42 -2
View File
@@ -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;
+19 -8
View File
@@ -209,17 +209,28 @@
</div>
<div class="form-row">
<label>Update Prompt</label>
<input type="text" id="modalRespawnPrompt" value="update all the docs and CLAUDE.md" placeholder="Prompt to send when idle">
<label>Run Duration</label>
<div class="duration-presets">
<button type="button" class="duration-preset-btn" data-minutes="30" onclick="app.selectDurationPreset('30')">30m</button>
<button type="button" class="duration-preset-btn" data-minutes="60" onclick="app.selectDurationPreset('60')">1h</button>
<button type="button" class="duration-preset-btn" data-minutes="120" onclick="app.selectDurationPreset('120')">2h</button>
<button type="button" class="duration-preset-btn" data-minutes="240" onclick="app.selectDurationPreset('240')">4h</button>
<button type="button" class="duration-preset-btn" data-minutes="480" onclick="app.selectDurationPreset('480')">8h</button>
<button type="button" class="duration-preset-btn active" data-minutes="" onclick="app.selectDurationPreset('')">∞</button>
<div class="duration-custom">
<button type="button" class="duration-preset-btn" data-minutes="custom" onclick="app.selectDurationPreset('custom')">Custom</button>
<div class="input-suffix input-suffix-sm duration-custom-input">
<input type="number" id="modalRespawnDuration" value="" min="1" placeholder="min">
<span>min</span>
</div>
</div>
</div>
<span class="form-hint">How long to run the respawn loop</span>
</div>
<div class="form-row">
<label>Run Duration</label>
<div class="input-suffix">
<input type="number" id="modalRespawnDuration" value="" min="1" placeholder="No limit">
<span>min</span>
</div>
<span class="form-hint">Leave empty for unlimited. Respawn stops after duration.</span>
<label>Update Prompt</label>
<input type="text" id="modalRespawnPrompt" value="update all the docs and CLAUDE.md" placeholder="Prompt to send when idle">
</div>
<div class="form-row">
+48
View File
@@ -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;