mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-02 13:39:41 +02:00
feat(wizard): add minimize button and network retry for Ralph wizard
- Add minimize button to Ralph wizard modal header - Minimized indicator shows at bottom of screen with elapsed time - Click indicator to restore wizard - Add retry logic (3 attempts with exponential backoff) for plan generation - Shows "Connection lost, retrying..." message during retries Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,7 +16,7 @@ When user says "COM":
|
||||
1. Increment version in BOTH `package.json` AND `CLAUDE.md`
|
||||
2. Run: `git add -A && git commit -m "chore: bump version to X.XXXX" && git push && npm run build && systemctl --user restart claudeman-web`
|
||||
|
||||
**Version**: 0.1413 (must match `package.json`)
|
||||
**Version**: 0.1414 (must match `package.json`)
|
||||
|
||||
## Project Overview
|
||||
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claudeman",
|
||||
"version": "0.1413",
|
||||
"version": "0.1414",
|
||||
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||
"type": "module",
|
||||
"main": "dist/index.js",
|
||||
|
||||
+80
-7
@@ -2715,6 +2715,9 @@ class ClaudemanApp {
|
||||
// Remove agents minimized tab if exists
|
||||
this.removeAgentsMinimizedTab();
|
||||
|
||||
// Hide minimized indicator
|
||||
this.hideWizardMinimizedIndicator();
|
||||
|
||||
// Reset wizard position
|
||||
this.wizardPosition = null;
|
||||
this.planAgentsMinimized = false;
|
||||
@@ -2723,6 +2726,49 @@ class ClaudemanApp {
|
||||
this.updateConnectionLines();
|
||||
}
|
||||
|
||||
minimizeRalphWizard() {
|
||||
const modal = document.getElementById('ralphWizardModal');
|
||||
modal?.classList.remove('active');
|
||||
|
||||
// Show the minimized indicator
|
||||
this.showWizardMinimizedIndicator();
|
||||
}
|
||||
|
||||
restoreRalphWizard() {
|
||||
// Hide the minimized indicator
|
||||
this.hideWizardMinimizedIndicator();
|
||||
|
||||
// Show the modal again
|
||||
const modal = document.getElementById('ralphWizardModal');
|
||||
modal?.classList.add('active');
|
||||
}
|
||||
|
||||
showWizardMinimizedIndicator() {
|
||||
const indicator = document.getElementById('wizardMinimizedIndicator');
|
||||
if (!indicator) return;
|
||||
|
||||
indicator.classList.add('visible');
|
||||
|
||||
// Start time counter if plan generation is active
|
||||
if (this.planLoadingStartTime) {
|
||||
this.wizardMinimizedTimer = setInterval(() => {
|
||||
const elapsed = Math.floor((Date.now() - this.planLoadingStartTime) / 1000);
|
||||
const timeEl = document.getElementById('wizardMinimizedTime');
|
||||
if (timeEl) timeEl.textContent = `${elapsed}s`;
|
||||
}, 1000);
|
||||
}
|
||||
}
|
||||
|
||||
hideWizardMinimizedIndicator() {
|
||||
const indicator = document.getElementById('wizardMinimizedIndicator');
|
||||
if (indicator) indicator.classList.remove('visible');
|
||||
|
||||
if (this.wizardMinimizedTimer) {
|
||||
clearInterval(this.wizardMinimizedTimer);
|
||||
this.wizardMinimizedTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
populateRalphCaseSelector() {
|
||||
const select = document.getElementById('ralphCaseSelect');
|
||||
const quickStartSelect = document.getElementById('quickStartCase');
|
||||
@@ -3296,14 +3342,41 @@ class ClaudemanApp {
|
||||
? { taskDescription: config.taskDescription, caseName: config.caseName }
|
||||
: { taskDescription: config.taskDescription, detailLevel: config.planDetailLevel };
|
||||
|
||||
const res = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
signal: this.planGenerationAbortController?.signal,
|
||||
});
|
||||
// Retry logic for network errors
|
||||
const maxRetries = 3;
|
||||
let lastError = null;
|
||||
let data = null;
|
||||
|
||||
const data = await res.json();
|
||||
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
||||
try {
|
||||
const res = await fetch(endpoint, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
signal: this.planGenerationAbortController?.signal,
|
||||
});
|
||||
data = await res.json();
|
||||
break; // Success, exit retry loop
|
||||
} catch (fetchErr) {
|
||||
lastError = fetchErr;
|
||||
// Don't retry if aborted
|
||||
if (fetchErr.name === 'AbortError') throw fetchErr;
|
||||
// Network error - retry with exponential backoff
|
||||
console.warn(`[Plan] Fetch attempt ${attempt + 1} failed:`, fetchErr.message);
|
||||
if (attempt < maxRetries - 1) {
|
||||
const delay = Math.pow(2, attempt) * 1000; // 1s, 2s, 4s
|
||||
const titleEl = document.getElementById('planLoadingTitle');
|
||||
const hintEl = document.getElementById('planLoadingHint');
|
||||
if (titleEl) titleEl.textContent = 'Connection lost, retrying...';
|
||||
if (hintEl) hintEl.textContent = `Attempt ${attempt + 2} of ${maxRetries} in ${delay / 1000}s`;
|
||||
await new Promise(r => setTimeout(r, delay));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!data) {
|
||||
throw lastError || new Error('Failed to fetch after retries');
|
||||
}
|
||||
|
||||
// Stop timer
|
||||
if (this.planLoadingTimer) {
|
||||
|
||||
@@ -951,6 +951,7 @@
|
||||
<span class="icon">🤖</span>
|
||||
<span class="agent-count" id="wizardAgentCount">0</span>
|
||||
</button>
|
||||
<button class="btn-icon-only wizard-minimize-btn" id="wizardMinimizeBtn" onclick="app.minimizeRalphWizard()" title="Minimize wizard">‒</button>
|
||||
<button class="modal-close" onclick="app.closeRalphWizard()">×</button>
|
||||
</div>
|
||||
</div>
|
||||
@@ -1143,6 +1144,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Ralph Wizard Minimized Indicator -->
|
||||
<div class="wizard-minimized-indicator" id="wizardMinimizedIndicator" onclick="app.restoreRalphWizard()">
|
||||
<span class="wizard-minimized-icon">🔄</span>
|
||||
<span class="wizard-minimized-text">Ralph Wizard</span>
|
||||
<span class="wizard-minimized-time" id="wizardMinimizedTime">0s</span>
|
||||
</div>
|
||||
|
||||
<!-- Kill All Confirmation Modal -->
|
||||
<div class="modal" id="killAllModal">
|
||||
<div class="modal-backdrop" onclick="app.closeKillAllModal()"></div>
|
||||
|
||||
@@ -5635,6 +5635,61 @@ kbd {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
/* Wizard minimize button */
|
||||
.wizard-minimize-btn {
|
||||
margin-right: 0.25rem;
|
||||
font-size: 1.2rem;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
/* Wizard minimized floating indicator */
|
||||
.wizard-minimized-indicator {
|
||||
display: none;
|
||||
position: fixed;
|
||||
bottom: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--accent);
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 1rem;
|
||||
cursor: pointer;
|
||||
z-index: 1000;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
|
||||
transition: all 0.2s ease;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.wizard-minimized-indicator.visible {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.wizard-minimized-indicator:hover {
|
||||
background: var(--bg-hover);
|
||||
border-color: var(--accent-bright);
|
||||
transform: translateX(-50%) scale(1.02);
|
||||
}
|
||||
|
||||
.wizard-minimized-icon {
|
||||
font-size: 1.1rem;
|
||||
animation: spin 2s linear infinite;
|
||||
}
|
||||
|
||||
.wizard-minimized-text {
|
||||
font-size: 0.8rem;
|
||||
color: var(--text);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.wizard-minimized-time {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-dim);
|
||||
background: var(--bg-input);
|
||||
padding: 0.15rem 0.4rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.modal-wizard .modal-body {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
|
||||
Reference in New Issue
Block a user