mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-08 08:29:42 +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`
|
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`
|
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
|
## Project Overview
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "claudeman",
|
"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",
|
"description": "The missing control plane for Claude Code - run 20 autonomous agents with real-time monitoring and session persistence",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
|
|||||||
+80
-7
@@ -2715,6 +2715,9 @@ class ClaudemanApp {
|
|||||||
// Remove agents minimized tab if exists
|
// Remove agents minimized tab if exists
|
||||||
this.removeAgentsMinimizedTab();
|
this.removeAgentsMinimizedTab();
|
||||||
|
|
||||||
|
// Hide minimized indicator
|
||||||
|
this.hideWizardMinimizedIndicator();
|
||||||
|
|
||||||
// Reset wizard position
|
// Reset wizard position
|
||||||
this.wizardPosition = null;
|
this.wizardPosition = null;
|
||||||
this.planAgentsMinimized = false;
|
this.planAgentsMinimized = false;
|
||||||
@@ -2723,6 +2726,49 @@ class ClaudemanApp {
|
|||||||
this.updateConnectionLines();
|
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() {
|
populateRalphCaseSelector() {
|
||||||
const select = document.getElementById('ralphCaseSelect');
|
const select = document.getElementById('ralphCaseSelect');
|
||||||
const quickStartSelect = document.getElementById('quickStartCase');
|
const quickStartSelect = document.getElementById('quickStartCase');
|
||||||
@@ -3296,14 +3342,41 @@ class ClaudemanApp {
|
|||||||
? { taskDescription: config.taskDescription, caseName: config.caseName }
|
? { taskDescription: config.taskDescription, caseName: config.caseName }
|
||||||
: { taskDescription: config.taskDescription, detailLevel: config.planDetailLevel };
|
: { taskDescription: config.taskDescription, detailLevel: config.planDetailLevel };
|
||||||
|
|
||||||
const res = await fetch(endpoint, {
|
// Retry logic for network errors
|
||||||
method: 'POST',
|
const maxRetries = 3;
|
||||||
headers: { 'Content-Type': 'application/json' },
|
let lastError = null;
|
||||||
body: JSON.stringify(body),
|
let data = null;
|
||||||
signal: this.planGenerationAbortController?.signal,
|
|
||||||
});
|
|
||||||
|
|
||||||
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
|
// Stop timer
|
||||||
if (this.planLoadingTimer) {
|
if (this.planLoadingTimer) {
|
||||||
|
|||||||
@@ -951,6 +951,7 @@
|
|||||||
<span class="icon">🤖</span>
|
<span class="icon">🤖</span>
|
||||||
<span class="agent-count" id="wizardAgentCount">0</span>
|
<span class="agent-count" id="wizardAgentCount">0</span>
|
||||||
</button>
|
</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>
|
<button class="modal-close" onclick="app.closeRalphWizard()">×</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -1143,6 +1144,13 @@
|
|||||||
</div>
|
</div>
|
||||||
</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 -->
|
<!-- Kill All Confirmation Modal -->
|
||||||
<div class="modal" id="killAllModal">
|
<div class="modal" id="killAllModal">
|
||||||
<div class="modal-backdrop" onclick="app.closeKillAllModal()"></div>
|
<div class="modal-backdrop" onclick="app.closeKillAllModal()"></div>
|
||||||
|
|||||||
@@ -5635,6 +5635,61 @@ kbd {
|
|||||||
font-weight: 600;
|
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 {
|
.modal-wizard .modal-body {
|
||||||
overflow-y: auto;
|
overflow-y: auto;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
|
|||||||
Reference in New Issue
Block a user