mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-04 22:49:41 +02:00
chore: bump version to 0.1402
This commit is contained in:
+349
-26
@@ -464,6 +464,11 @@ class ClaudemanApp {
|
||||
this._subagentHideTimeout = null; // Timeout for hover-based dropdown hide
|
||||
this.ralphStatePanelCollapsed = true; // Default to collapsed
|
||||
|
||||
// Plan subagent windows (visible Opus agents during plan generation)
|
||||
this.planSubagents = new Map(); // Map<agentId, { type, model, status, startTime, element }>
|
||||
this.planSubagentWindowZIndex = 1100;
|
||||
this.planGenerationStopped = false; // Flag to ignore SSE events after Stop
|
||||
|
||||
// Project Insights tracking (active Bash tools with clickable file paths)
|
||||
this.projectInsights = new Map(); // Map<sessionId, ActiveBashTool[]>
|
||||
this.logViewerWindows = new Map(); // Map<windowId, { element, eventSource, filePath }>
|
||||
@@ -1907,6 +1912,13 @@ class ClaudemanApp {
|
||||
}, 5 * 60 * 1000); // 5 minutes
|
||||
});
|
||||
|
||||
// Plan subagent visibility events (show Opus agents during plan generation)
|
||||
addListener('plan:subagent', (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
console.log('[Plan Subagent]', data);
|
||||
this.handlePlanSubagentEvent(data);
|
||||
});
|
||||
|
||||
// Plan generation progress events (for detailed mode with subagents)
|
||||
addListener('plan:progress', (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
@@ -2578,7 +2590,7 @@ class ClaudemanApp {
|
||||
generatedPlan: null, // [{content, priority, enabled, id}] or null
|
||||
planGenerated: false,
|
||||
skipPlanGeneration: false,
|
||||
planDetailLevel: 'standard', // 'brief', 'standard', 'detailed'
|
||||
planDetailLevel: 'detailed', // 'brief', 'standard', 'detailed'
|
||||
};
|
||||
planLoadingTimer = null;
|
||||
planLoadingStartTime = null;
|
||||
@@ -2596,7 +2608,7 @@ class ClaudemanApp {
|
||||
generatedPlan: null,
|
||||
planGenerated: false,
|
||||
skipPlanGeneration: false,
|
||||
planDetailLevel: 'standard',
|
||||
planDetailLevel: 'detailed',
|
||||
// Existing plan detection
|
||||
existingPlan: null, // { todos, stats, content } from @fix_plan.md
|
||||
useExistingPlan: false, // User chose to use existing plan
|
||||
@@ -2620,10 +2632,16 @@ class ClaudemanApp {
|
||||
this.updateRalphWizardUI();
|
||||
document.getElementById('ralphWizardModal').classList.add('active');
|
||||
document.getElementById('ralphTaskDescription').focus();
|
||||
|
||||
// Update connection lines to show wizard connections
|
||||
this.updateConnectionLines();
|
||||
}
|
||||
|
||||
closeRalphWizard() {
|
||||
document.getElementById('ralphWizardModal')?.classList.remove('active');
|
||||
|
||||
// Update connection lines (wizard closed, revert to normal)
|
||||
this.updateConnectionLines();
|
||||
}
|
||||
|
||||
populateRalphCaseSelector() {
|
||||
@@ -2925,6 +2943,10 @@ class ClaudemanApp {
|
||||
const spinnerEl = document.querySelector('.plan-spinner');
|
||||
if (spinnerEl) spinnerEl.style.display = '';
|
||||
|
||||
// Reset stopped indicator
|
||||
const stoppedIndicator = document.getElementById('planStoppedIndicator');
|
||||
if (stoppedIndicator) stoppedIndicator.style.display = 'none';
|
||||
|
||||
// Reset existing plan badge
|
||||
const badge = document.getElementById('existingPlanBadge');
|
||||
if (badge) badge.style.display = 'none';
|
||||
@@ -2937,6 +2959,9 @@ class ClaudemanApp {
|
||||
// Stop any existing generation first
|
||||
this.stopPlanGeneration();
|
||||
|
||||
// Reset stopped flag to allow new SSE events
|
||||
this.planGenerationStopped = false;
|
||||
|
||||
// Create abort controller for this generation
|
||||
this.planGenerationAbortController = new AbortController();
|
||||
|
||||
@@ -3164,6 +3189,10 @@ class ClaudemanApp {
|
||||
|
||||
const errorEl = document.getElementById('planGenerationError');
|
||||
const msgEl = document.getElementById('planErrorMsg');
|
||||
const stoppedIndicator = document.getElementById('planStoppedIndicator');
|
||||
|
||||
// Hide the stopped indicator for real errors, show error message
|
||||
if (stoppedIndicator) stoppedIndicator.style.display = 'none';
|
||||
if (msgEl) msgEl.textContent = message;
|
||||
errorEl?.classList.remove('hidden');
|
||||
}
|
||||
@@ -3176,6 +3205,9 @@ class ClaudemanApp {
|
||||
document.getElementById('planGenerationError')?.classList.add('hidden');
|
||||
document.getElementById('planEditor')?.classList.remove('hidden');
|
||||
|
||||
// Close plan subagent windows since generation is complete
|
||||
this.closePlanSubagentWindows();
|
||||
|
||||
const list = document.getElementById('planItemsList');
|
||||
if (!list) return;
|
||||
|
||||
@@ -3565,13 +3597,183 @@ class ClaudemanApp {
|
||||
|
||||
cancelPlanGeneration() {
|
||||
this.stopPlanGeneration();
|
||||
this.showToast('Plan generation cancelled', 'info');
|
||||
this.planGenerationStopped = true; // Ignore future SSE events
|
||||
this.showToast('Plan generation stopped', 'info');
|
||||
|
||||
// Allow user to proceed without a plan by clicking Next
|
||||
this.ralphWizardConfig.skipPlanGeneration = true;
|
||||
|
||||
// Show stopped state with clear visual indicator
|
||||
const errorEl = document.getElementById('planGenerationError');
|
||||
const msgEl = document.getElementById('planErrorMsg');
|
||||
if (msgEl) msgEl.textContent = 'Plan generation was cancelled.';
|
||||
errorEl?.classList.remove('hidden');
|
||||
const stoppedIndicator = document.getElementById('planStoppedIndicator');
|
||||
|
||||
// Show the stopped indicator, hide error message since this was intentional
|
||||
if (stoppedIndicator) stoppedIndicator.style.display = 'flex';
|
||||
if (msgEl) msgEl.textContent = '';
|
||||
|
||||
// Hide spinner immediately and show stopped state
|
||||
document.getElementById('planGenerationLoading')?.classList.add('hidden');
|
||||
errorEl?.classList.remove('hidden');
|
||||
|
||||
// Close all plan subagent windows
|
||||
this.closePlanSubagentWindows();
|
||||
}
|
||||
|
||||
// ========== Plan Subagent Windows (Visible Opus Agents) ==========
|
||||
|
||||
handlePlanSubagentEvent(event) {
|
||||
// Ignore events if user has stopped plan generation
|
||||
if (this.planGenerationStopped) return;
|
||||
|
||||
const { type, agentId, agentType, model, status, detail, itemCount, durationMs, error } = event;
|
||||
|
||||
if (type === 'started') {
|
||||
// Create new plan subagent window
|
||||
this.createPlanSubagentWindow(agentId, agentType, model, detail);
|
||||
} else if (type === 'completed' || type === 'failed') {
|
||||
// Update window to show completion/failure
|
||||
this.updatePlanSubagentWindow(agentId, status, itemCount, durationMs, error);
|
||||
} else if (type === 'progress') {
|
||||
// Update progress detail
|
||||
const windowData = this.planSubagents.get(agentId);
|
||||
if (windowData?.element) {
|
||||
const detailEl = windowData.element.querySelector('.plan-subagent-detail');
|
||||
if (detailEl) detailEl.textContent = detail || '';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createPlanSubagentWindow(agentId, agentType, model, detail) {
|
||||
if (this.planSubagents.has(agentId)) return;
|
||||
|
||||
const wizardModal = document.getElementById('ralphWizardModal');
|
||||
const wizardContent = wizardModal?.querySelector('.modal-content');
|
||||
const wizardRect = wizardContent?.getBoundingClientRect();
|
||||
|
||||
// Calculate position - alternate left/right of wizard
|
||||
const windowWidth = 280;
|
||||
const windowHeight = 100;
|
||||
const windowCount = this.planSubagents.size;
|
||||
const viewportWidth = window.innerWidth;
|
||||
|
||||
let x, y;
|
||||
if (wizardRect) {
|
||||
const centerX = wizardRect.left + wizardRect.width / 2;
|
||||
const gap = 30;
|
||||
|
||||
// Stack windows vertically on each side
|
||||
const side = windowCount % 2 === 0 ? 'right' : 'left';
|
||||
const sideIndex = Math.floor(windowCount / 2);
|
||||
|
||||
if (side === 'right') {
|
||||
x = wizardRect.right + gap;
|
||||
} else {
|
||||
x = wizardRect.left - windowWidth - gap;
|
||||
}
|
||||
y = wizardRect.top + sideIndex * (windowHeight + 15);
|
||||
|
||||
// Clamp to viewport
|
||||
x = Math.max(10, Math.min(x, viewportWidth - windowWidth - 10));
|
||||
y = Math.max(60, Math.min(y, window.innerHeight - windowHeight - 10));
|
||||
} else {
|
||||
// Fallback positioning
|
||||
x = 50 + windowCount * 30;
|
||||
y = 120 + windowCount * 30;
|
||||
}
|
||||
|
||||
// Create window element
|
||||
const win = document.createElement('div');
|
||||
win.className = 'plan-subagent-window';
|
||||
win.id = `plan-subagent-${agentId}`;
|
||||
win.style.left = `${x}px`;
|
||||
win.style.top = `${y}px`;
|
||||
win.style.zIndex = ++this.planSubagentWindowZIndex;
|
||||
|
||||
const typeLabels = {
|
||||
requirements: 'Requirements Analyst',
|
||||
architecture: 'Architecture Planner',
|
||||
testing: 'TDD Specialist',
|
||||
risks: 'Risk Analyst',
|
||||
verification: 'Verification Expert',
|
||||
};
|
||||
|
||||
const typeIcons = {
|
||||
requirements: '📋',
|
||||
architecture: '🏗️',
|
||||
testing: '🧪',
|
||||
risks: '⚠️',
|
||||
verification: '✓',
|
||||
};
|
||||
|
||||
win.innerHTML = `
|
||||
<div class="plan-subagent-header">
|
||||
<span class="plan-subagent-icon">${typeIcons[agentType] || '🤖'}</span>
|
||||
<span class="plan-subagent-title">${typeLabels[agentType] || agentType}</span>
|
||||
<span class="plan-subagent-model">${model}</span>
|
||||
</div>
|
||||
<div class="plan-subagent-body">
|
||||
<div class="plan-subagent-status running">
|
||||
<span class="plan-subagent-spinner"></span>
|
||||
<span class="plan-subagent-status-text">Running...</span>
|
||||
</div>
|
||||
<div class="plan-subagent-detail">${detail || ''}</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(win);
|
||||
|
||||
// Store reference
|
||||
this.planSubagents.set(agentId, {
|
||||
type: agentType,
|
||||
model,
|
||||
status: 'running',
|
||||
startTime: Date.now(),
|
||||
element: win,
|
||||
});
|
||||
|
||||
// Update connection lines
|
||||
this.updateConnectionLines();
|
||||
}
|
||||
|
||||
updatePlanSubagentWindow(agentId, status, itemCount, durationMs, error) {
|
||||
const windowData = this.planSubagents.get(agentId);
|
||||
if (!windowData?.element) return;
|
||||
|
||||
const win = windowData.element;
|
||||
const statusEl = win.querySelector('.plan-subagent-status');
|
||||
const statusTextEl = win.querySelector('.plan-subagent-status-text');
|
||||
const spinnerEl = win.querySelector('.plan-subagent-spinner');
|
||||
const detailEl = win.querySelector('.plan-subagent-detail');
|
||||
|
||||
windowData.status = status;
|
||||
|
||||
if (status === 'completed') {
|
||||
statusEl?.classList.remove('running');
|
||||
statusEl?.classList.add('completed');
|
||||
if (spinnerEl) spinnerEl.style.display = 'none';
|
||||
if (statusTextEl) statusTextEl.textContent = `Done (${itemCount || 0} items)`;
|
||||
if (detailEl && durationMs) detailEl.textContent = `${(durationMs / 1000).toFixed(1)}s`;
|
||||
} else if (status === 'failed' || status === 'cancelled') {
|
||||
statusEl?.classList.remove('running');
|
||||
statusEl?.classList.add('failed');
|
||||
if (spinnerEl) spinnerEl.style.display = 'none';
|
||||
if (statusTextEl) statusTextEl.textContent = status === 'cancelled' ? 'Cancelled' : 'Failed';
|
||||
if (detailEl) detailEl.textContent = error || '';
|
||||
}
|
||||
|
||||
// Update connection lines
|
||||
this.updateConnectionLines();
|
||||
}
|
||||
|
||||
closePlanSubagentWindows() {
|
||||
for (const [agentId, windowData] of this.planSubagents) {
|
||||
if (windowData.element) {
|
||||
windowData.element.remove();
|
||||
}
|
||||
}
|
||||
this.planSubagents.clear();
|
||||
this.updateConnectionLines();
|
||||
}
|
||||
|
||||
skipPlanGeneration() {
|
||||
@@ -7768,34 +7970,118 @@ class ClaudemanApp {
|
||||
|
||||
svg.innerHTML = '';
|
||||
|
||||
// Check if Ralph wizard modal is open
|
||||
const wizardModal = document.getElementById('ralphWizardModal');
|
||||
const wizardOpen = wizardModal && !wizardModal.classList.contains('hidden') &&
|
||||
wizardModal.style.display !== 'none';
|
||||
const wizardContent = wizardOpen ? wizardModal.querySelector('.modal-content') : null;
|
||||
|
||||
for (const [agentId, windowInfo] of this.subagentWindows) {
|
||||
if (windowInfo.minimized || windowInfo.hidden) continue;
|
||||
|
||||
const agent = this.subagents.get(agentId);
|
||||
if (!agent?.parentSessionId) continue;
|
||||
|
||||
const tab = document.querySelector(`.session-tab[data-id="${agent.parentSessionId}"]`);
|
||||
const win = windowInfo.element;
|
||||
if (!tab || !win) continue;
|
||||
if (!win) continue;
|
||||
|
||||
const tabRect = tab.getBoundingClientRect();
|
||||
const winRect = win.getBoundingClientRect();
|
||||
|
||||
// Draw curved line from tab bottom-center to window top-center
|
||||
const x1 = tabRect.left + tabRect.width / 2;
|
||||
const y1 = tabRect.bottom;
|
||||
const x2 = winRect.left + winRect.width / 2;
|
||||
const y2 = winRect.top;
|
||||
// If wizard is open, draw lines from wizard to subagent windows
|
||||
if (wizardOpen && wizardContent) {
|
||||
const wizardRect = wizardContent.getBoundingClientRect();
|
||||
|
||||
// Bezier curve control points for smooth curve
|
||||
const midY = (y1 + y2) / 2;
|
||||
const path = `M ${x1} ${y1} C ${x1} ${midY}, ${x2} ${midY}, ${x2} ${y2}`;
|
||||
// Determine which side of wizard the window is on
|
||||
const winCenterX = winRect.left + winRect.width / 2;
|
||||
const wizardCenterX = wizardRect.left + wizardRect.width / 2;
|
||||
|
||||
const line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
line.setAttribute('d', path);
|
||||
line.setAttribute('class', 'connection-line');
|
||||
line.setAttribute('data-agent-id', agentId);
|
||||
svg.appendChild(line);
|
||||
let x1, y1, x2, y2;
|
||||
|
||||
if (winCenterX < wizardCenterX) {
|
||||
// Window is on the left - connect from wizard left edge
|
||||
x1 = wizardRect.left;
|
||||
y1 = wizardRect.top + wizardRect.height / 2;
|
||||
x2 = winRect.right;
|
||||
y2 = winRect.top + winRect.height / 2;
|
||||
} else {
|
||||
// Window is on the right - connect from wizard right edge
|
||||
x1 = wizardRect.right;
|
||||
y1 = wizardRect.top + wizardRect.height / 2;
|
||||
x2 = winRect.left;
|
||||
y2 = winRect.top + winRect.height / 2;
|
||||
}
|
||||
|
||||
// Bezier curve for smooth horizontal connection
|
||||
const midX = (x1 + x2) / 2;
|
||||
const path = `M ${x1} ${y1} C ${midX} ${y1}, ${midX} ${y2}, ${x2} ${y2}`;
|
||||
|
||||
const line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
line.setAttribute('d', path);
|
||||
line.setAttribute('class', 'connection-line wizard-connection');
|
||||
line.setAttribute('data-agent-id', agentId);
|
||||
svg.appendChild(line);
|
||||
} else if (agent?.parentSessionId) {
|
||||
// Normal mode - connect to parent session tab
|
||||
const tab = document.querySelector(`.session-tab[data-id="${agent.parentSessionId}"]`);
|
||||
if (!tab) continue;
|
||||
|
||||
const tabRect = tab.getBoundingClientRect();
|
||||
|
||||
// Draw curved line from tab bottom-center to window top-center
|
||||
const x1 = tabRect.left + tabRect.width / 2;
|
||||
const y1 = tabRect.bottom;
|
||||
const x2 = winRect.left + winRect.width / 2;
|
||||
const y2 = winRect.top;
|
||||
|
||||
// Bezier curve control points for smooth curve
|
||||
const midY = (y1 + y2) / 2;
|
||||
const path = `M ${x1} ${y1} C ${x1} ${midY}, ${x2} ${midY}, ${x2} ${y2}`;
|
||||
|
||||
const line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
line.setAttribute('d', path);
|
||||
line.setAttribute('class', 'connection-line');
|
||||
line.setAttribute('data-agent-id', agentId);
|
||||
svg.appendChild(line);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw lines from wizard to plan subagent windows (Opus agents during plan generation)
|
||||
if (wizardOpen && wizardContent && this.planSubagents.size > 0) {
|
||||
const wizardRect = wizardContent.getBoundingClientRect();
|
||||
|
||||
for (const [agentId, windowData] of this.planSubagents) {
|
||||
const win = windowData.element;
|
||||
if (!win) continue;
|
||||
|
||||
const winRect = win.getBoundingClientRect();
|
||||
|
||||
// Determine which side of wizard the window is on
|
||||
const winCenterX = winRect.left + winRect.width / 2;
|
||||
const wizardCenterX = wizardRect.left + wizardRect.width / 2;
|
||||
|
||||
let x1, y1, x2, y2;
|
||||
|
||||
if (winCenterX < wizardCenterX) {
|
||||
// Window is on the left
|
||||
x1 = wizardRect.left;
|
||||
y1 = wizardRect.top + wizardRect.height / 3 + (this.planSubagents.size > 3 ? 0 : 50);
|
||||
x2 = winRect.right;
|
||||
y2 = winRect.top + winRect.height / 2;
|
||||
} else {
|
||||
// Window is on the right
|
||||
x1 = wizardRect.right;
|
||||
y1 = wizardRect.top + wizardRect.height / 3 + (this.planSubagents.size > 3 ? 0 : 50);
|
||||
x2 = winRect.left;
|
||||
y2 = winRect.top + winRect.height / 2;
|
||||
}
|
||||
|
||||
const midX = (x1 + x2) / 2;
|
||||
const path = `M ${x1} ${y1} C ${midX} ${y1}, ${midX} ${y2}, ${x2} ${y2}`;
|
||||
|
||||
const line = document.createElementNS('http://www.w3.org/2000/svg', 'path');
|
||||
line.setAttribute('d', path);
|
||||
line.setAttribute('class', 'connection-line wizard-connection plan-subagent-line');
|
||||
line.setAttribute('data-plan-agent-id', agentId);
|
||||
svg.appendChild(line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7874,11 +8160,48 @@ class ClaudemanApp {
|
||||
const windowWidth = 420;
|
||||
const windowHeight = 350;
|
||||
const gap = 20;
|
||||
const startX = 50;
|
||||
const startY = 120;
|
||||
const viewportWidth = window.innerWidth;
|
||||
const viewportHeight = window.innerHeight;
|
||||
const maxCols = Math.floor((viewportWidth - startX - 50) / (windowWidth + gap)) || 1;
|
||||
|
||||
// Check if Ralph wizard modal is open - if so, position windows on the sides
|
||||
const wizardModal = document.getElementById('ralphWizardModal');
|
||||
const wizardOpen = wizardModal && !wizardModal.classList.contains('hidden') &&
|
||||
wizardModal.style.display !== 'none';
|
||||
|
||||
let startX, startY, maxCols;
|
||||
|
||||
if (wizardOpen) {
|
||||
// Wizard is ~720px wide, centered. Position windows on left/right sides
|
||||
const wizardWidth = 720;
|
||||
const centerX = viewportWidth / 2;
|
||||
const wizardLeft = centerX - wizardWidth / 2;
|
||||
const wizardRight = centerX + wizardWidth / 2;
|
||||
|
||||
// Alternate between left and right sides of the wizard
|
||||
const leftSideSpace = wizardLeft - 20;
|
||||
const rightSideSpace = viewportWidth - wizardRight - 20;
|
||||
|
||||
if (windowCount % 2 === 0 && rightSideSpace >= windowWidth) {
|
||||
// Even windows go to the right
|
||||
startX = wizardRight + 20;
|
||||
maxCols = Math.floor(rightSideSpace / (windowWidth + gap)) || 1;
|
||||
} else if (leftSideSpace >= windowWidth) {
|
||||
// Odd windows go to the left
|
||||
startX = Math.max(10, wizardLeft - windowWidth - 20);
|
||||
maxCols = 1; // Usually only room for 1 column on left
|
||||
} else {
|
||||
// Not enough side space, use right side
|
||||
startX = wizardRight + 20;
|
||||
maxCols = 1;
|
||||
}
|
||||
startY = 80; // Start higher when wizard is open
|
||||
} else {
|
||||
// Normal positioning
|
||||
startX = 50;
|
||||
startY = 120;
|
||||
maxCols = Math.floor((viewportWidth - startX - 50) / (windowWidth + gap)) || 1;
|
||||
}
|
||||
|
||||
const maxRows = Math.floor((viewportHeight - startY - 50) / (windowHeight + gap)) || 1;
|
||||
const col = windowCount % maxCols;
|
||||
const row = Math.floor(windowCount / maxCols) % maxRows; // Wrap rows to stay in viewport
|
||||
|
||||
@@ -172,6 +172,13 @@
|
||||
<span class="ralph-info-label">Elapsed</span>
|
||||
<span id="ralphElapsed">0m</span>
|
||||
</div>
|
||||
<div class="ralph-info-row" id="ralphVersionRow" style="display: none;">
|
||||
<span class="ralph-info-label">Plan</span>
|
||||
<div class="ralph-version-info">
|
||||
<span class="version-badge" id="ralphPlanVersion">v1</span>
|
||||
<button class="rollback-btn" id="ralphRollbackBtn" onclick="app.showPlanHistory()" title="View history">history</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="ralph-tasks-grid" id="ralphTasksGrid"></div>
|
||||
</div>
|
||||
@@ -1013,16 +1020,23 @@
|
||||
</div>
|
||||
</div>
|
||||
<p class="plan-loading-hint" id="planLoadingHint">Initializing deep reasoning model</p>
|
||||
<button class="btn-toolbar btn-sm plan-skip-btn" onclick="app.skipPlanGeneration()">Skip</button>
|
||||
<div class="plan-loading-actions">
|
||||
<button class="btn-toolbar btn-sm plan-cancel-btn" onclick="app.cancelPlanGeneration()">Stop</button>
|
||||
<button class="btn-toolbar btn-sm" onclick="app.regeneratePlan()">Regenerate</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Error state -->
|
||||
<!-- Stopped/Error state -->
|
||||
<div id="planGenerationError" class="plan-section hidden">
|
||||
<div class="plan-stopped-indicator" id="planStoppedIndicator">
|
||||
<span class="plan-stopped-icon">■</span>
|
||||
<span class="plan-stopped-text">Stopped</span>
|
||||
</div>
|
||||
<p class="plan-error-msg" id="planErrorMsg"></p>
|
||||
<div class="plan-actions">
|
||||
<button class="btn-toolbar btn-primary" onclick="app.generatePlan()">Retry</button>
|
||||
<button class="btn-toolbar" onclick="app.skipPlanGeneration()">Skip</button>
|
||||
<button class="btn-toolbar btn-primary" onclick="app.generatePlan()">Regenerate Plan</button>
|
||||
</div>
|
||||
<p class="plan-skip-hint">Use <strong>Back</strong> to edit your task, or <strong>Next</strong> to continue without a plan</p>
|
||||
</div>
|
||||
|
||||
<!-- Editor state -->
|
||||
@@ -1034,8 +1048,8 @@
|
||||
<label>Detail:</label>
|
||||
<div class="plan-detail-btns">
|
||||
<button type="button" class="plan-detail-btn" data-detail="brief" onclick="app.setPlanDetail('brief')" title="High-level milestones only">Brief</button>
|
||||
<button type="button" class="plan-detail-btn active" data-detail="standard" onclick="app.setPlanDetail('standard')" title="Single-pass generation with Opus 4.5">Standard</button>
|
||||
<button type="button" class="plan-detail-btn" data-detail="detailed" onclick="app.setPlanDetail('detailed')" title="Enhanced: 4 parallel subagents + verification (slower but more thorough)">Enhanced</button>
|
||||
<button type="button" class="plan-detail-btn" data-detail="standard" onclick="app.setPlanDetail('standard')" title="Single-pass generation with Opus 4.5">Standard</button>
|
||||
<button type="button" class="plan-detail-btn active" data-detail="detailed" onclick="app.setPlanDetail('detailed')" title="Enhanced: 4 parallel subagents + verification (slower but more thorough)">Enhanced</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="btn-toolbar btn-sm" onclick="app.regeneratePlan()">Regenerate</button>
|
||||
@@ -1045,6 +1059,10 @@
|
||||
<div id="planItemsList" class="plan-items-list"></div>
|
||||
<div class="plan-editor-footer">
|
||||
<button class="btn-toolbar btn-sm" onclick="app.addPlanItem()">+ Add Step</button>
|
||||
<div class="plan-view-toggle">
|
||||
<button type="button" class="plan-view-btn active" data-view="list" onclick="app.setPlanViewMode('list')" title="Flat list">☰</button>
|
||||
<button type="button" class="plan-view-btn" data-view="grouped" onclick="app.setPlanViewMode('grouped')" title="Group by TDD phase">⊞</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
+738
-7
@@ -4692,6 +4692,19 @@ kbd {
|
||||
stroke-dashoffset: 1000;
|
||||
}
|
||||
|
||||
/* Wizard connection lines - different color to show plan generation link */
|
||||
.connection-line.wizard-connection {
|
||||
stroke: #60a5fa;
|
||||
filter: drop-shadow(0 0 4px #60a5fa);
|
||||
stroke-dasharray: 8 4;
|
||||
animation: wizard-pulse 1.5s ease-in-out infinite;
|
||||
}
|
||||
|
||||
@keyframes wizard-pulse {
|
||||
0%, 100% { opacity: 0.5; }
|
||||
50% { opacity: 0.9; }
|
||||
}
|
||||
|
||||
/* ========== Project Insights Panel (Bash File Viewers) ========== */
|
||||
|
||||
.project-insights-panel {
|
||||
@@ -5366,10 +5379,23 @@ kbd {
|
||||
gap: 0.5rem;
|
||||
}
|
||||
|
||||
/* Wizard Modal - wider layout */
|
||||
/* Wizard Modal - wider layout with height constraints */
|
||||
.modal-wizard {
|
||||
width: 720px;
|
||||
max-width: 95vw;
|
||||
max-height: 80vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.modal-wizard .modal-body {
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
min-height: 0;
|
||||
}
|
||||
|
||||
.modal-wizard .wizard-footer {
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.wizard-page-compact {
|
||||
@@ -5641,20 +5667,158 @@ kbd {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.35rem;
|
||||
max-height: 300px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.plan-item {
|
||||
.plan-loading-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.plan-cancel-btn {
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
/* Plan Stopped State */
|
||||
.plan-stopped-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.35rem 0.5rem;
|
||||
background: var(--bg-input);
|
||||
border-radius: 4px;
|
||||
justify-content: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 1rem;
|
||||
padding: 0.75rem;
|
||||
background: var(--surface);
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.plan-stopped-icon {
|
||||
color: var(--red);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.plan-stopped-text {
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.plan-actions-single {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.plan-skip-hint {
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
margin-top: 0.75rem;
|
||||
}
|
||||
|
||||
.plan-skip-hint strong {
|
||||
color: var(--text-dim);
|
||||
}
|
||||
|
||||
/* ========== Plan Subagent Windows (Visible Opus Agents) ========== */
|
||||
.plan-subagent-window {
|
||||
position: fixed;
|
||||
width: 280px;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--accent);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.4), 0 0 20px rgba(96, 165, 250, 0.2);
|
||||
overflow: hidden;
|
||||
animation: planSubagentSpawn 0.3s ease-out;
|
||||
}
|
||||
|
||||
@keyframes planSubagentSpawn {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: scale(0.8);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
.plan-subagent-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.5rem 0.75rem;
|
||||
background: linear-gradient(135deg, rgba(96, 165, 250, 0.2), rgba(96, 165, 250, 0.1));
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.plan-subagent-icon {
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.plan-subagent-title {
|
||||
flex: 1;
|
||||
font-weight: 500;
|
||||
font-size: 0.8rem;
|
||||
color: var(--text);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.plan-subagent-model {
|
||||
font-size: 0.65rem;
|
||||
padding: 0.15rem 0.4rem;
|
||||
background: #7c3aed;
|
||||
color: white;
|
||||
border-radius: 3px;
|
||||
text-transform: uppercase;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.plan-subagent-body {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.plan-subagent-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
margin-bottom: 0.35rem;
|
||||
}
|
||||
|
||||
.plan-subagent-status.running .plan-subagent-spinner {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border: 2px solid var(--border);
|
||||
border-top-color: var(--accent);
|
||||
border-radius: 50%;
|
||||
animation: spin 1s linear infinite;
|
||||
}
|
||||
|
||||
.plan-subagent-status.completed .plan-subagent-status-text {
|
||||
color: var(--green);
|
||||
}
|
||||
|
||||
.plan-subagent-status.failed .plan-subagent-status-text {
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
.plan-subagent-status-text {
|
||||
font-size: 0.8rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.plan-subagent-detail {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
/* Note: main .plan-item styles are in "Enhanced Plan Item Styling" section below */
|
||||
|
||||
.plan-item-checkbox {
|
||||
flex-shrink: 0;
|
||||
width: 14px;
|
||||
@@ -5701,6 +5865,8 @@ kbd {
|
||||
color: var(--red);
|
||||
}
|
||||
|
||||
/* Note: .plan-item-phase and .plan-item-verify enhanced styles are in section below */
|
||||
|
||||
.plan-item:has(.plan-item-checkbox:not(:checked)) {
|
||||
opacity: 0.5;
|
||||
}
|
||||
@@ -5761,6 +5927,571 @@ kbd {
|
||||
color: #fbbf24;
|
||||
}
|
||||
|
||||
/* Enhanced Plan Item Styling */
|
||||
.plan-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.4rem;
|
||||
padding: 0.35rem 0.5rem;
|
||||
background: var(--bg-input);
|
||||
border-radius: 4px;
|
||||
border-left: 3px solid transparent;
|
||||
transition: border-color 0.15s, background-color 0.15s, transform 0.15s;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.plan-item.dragging {
|
||||
opacity: 0.5;
|
||||
transform: scale(0.98);
|
||||
}
|
||||
|
||||
.plan-item.drag-over {
|
||||
background: var(--accent-dim);
|
||||
border-left-color: var(--accent);
|
||||
}
|
||||
|
||||
/* Priority color borders */
|
||||
.plan-item.priority-p0 {
|
||||
border-left-color: #ef4444;
|
||||
}
|
||||
|
||||
.plan-item.priority-p1 {
|
||||
border-left-color: #f59e0b;
|
||||
}
|
||||
|
||||
.plan-item.priority-p2 {
|
||||
border-left-color: #3b82f6;
|
||||
}
|
||||
|
||||
/* Drag handle */
|
||||
.plan-item-drag {
|
||||
cursor: grab;
|
||||
color: var(--text-muted);
|
||||
font-size: 0.9rem;
|
||||
padding: 0 0.2rem;
|
||||
opacity: 0.5;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.plan-item-drag:hover {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.plan-item-drag:active {
|
||||
cursor: grabbing;
|
||||
}
|
||||
|
||||
/* TDD Phase badge - enhanced */
|
||||
.plan-item-phase {
|
||||
flex-shrink: 0;
|
||||
min-width: 22px;
|
||||
height: 18px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 0.7rem;
|
||||
border-radius: 3px;
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
.plan-item-phase.phase-setup {
|
||||
background: #6366f1;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.plan-item-phase.phase-test {
|
||||
background: #8b5cf6;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.plan-item-phase.phase-impl {
|
||||
background: #06b6d4;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.plan-item-phase.phase-review {
|
||||
background: #f59e0b;
|
||||
color: white;
|
||||
}
|
||||
|
||||
.plan-item-phase.phase-verify {
|
||||
background: #22c55e;
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Enhanced verification indicator */
|
||||
.plan-item-verify {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.15rem;
|
||||
color: var(--green);
|
||||
font-size: 0.65rem;
|
||||
cursor: help;
|
||||
padding: 0.1rem 0.25rem;
|
||||
background: rgba(34, 197, 94, 0.1);
|
||||
border-radius: 3px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.plan-item-verify:hover {
|
||||
opacity: 1;
|
||||
background: rgba(34, 197, 94, 0.2);
|
||||
}
|
||||
|
||||
/* Dependencies indicator */
|
||||
.plan-item-deps {
|
||||
flex-shrink: 0;
|
||||
font-size: 0.65rem;
|
||||
color: var(--text-muted);
|
||||
padding: 0.1rem 0.25rem;
|
||||
background: var(--bg-card);
|
||||
border-radius: 3px;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.plan-item-deps:hover {
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Review checklist indicator */
|
||||
.plan-item-checklist {
|
||||
flex-shrink: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.15rem;
|
||||
color: #f59e0b;
|
||||
font-size: 0.65rem;
|
||||
cursor: help;
|
||||
padding: 0.1rem 0.25rem;
|
||||
background: rgba(245, 158, 11, 0.1);
|
||||
border-radius: 3px;
|
||||
opacity: 0.85;
|
||||
}
|
||||
|
||||
.plan-item-checklist:hover {
|
||||
opacity: 1;
|
||||
background: rgba(245, 158, 11, 0.2);
|
||||
}
|
||||
|
||||
/* Checklist modal */
|
||||
.checklist-modal-container {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0, 0, 0, 0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 9999;
|
||||
}
|
||||
|
||||
.checklist-modal {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.checklist-modal h3 {
|
||||
margin: 0 0 0.5rem 0;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.checklist-task {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.85rem;
|
||||
margin-bottom: 1rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.checklist-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin: 0 0 1rem 0;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.checklist-item {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 0.5rem;
|
||||
padding: 0.35rem 0;
|
||||
}
|
||||
|
||||
.checklist-checkbox {
|
||||
flex-shrink: 0;
|
||||
margin-top: 0.15rem;
|
||||
}
|
||||
|
||||
.checklist-item label {
|
||||
color: var(--text);
|
||||
font-size: 0.85rem;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.checklist-footer {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Group headers for TDD phases */
|
||||
.plan-group-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.3rem 0.5rem;
|
||||
font-size: 0.7rem;
|
||||
font-weight: 600;
|
||||
text-transform: uppercase;
|
||||
color: var(--text-muted);
|
||||
border-bottom: 1px solid var(--border);
|
||||
margin-top: 0.5rem;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
}
|
||||
|
||||
.plan-group-header:first-child {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
||||
.plan-group-header:hover {
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.plan-group-header .group-icon {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
|
||||
.plan-group-header .group-count {
|
||||
margin-left: auto;
|
||||
color: var(--text-dim);
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.plan-group-header .group-chevron {
|
||||
transition: transform 0.15s;
|
||||
}
|
||||
|
||||
.plan-group-header.collapsed .group-chevron {
|
||||
transform: rotate(-90deg);
|
||||
}
|
||||
|
||||
.plan-group-items {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.25rem;
|
||||
padding: 0.25rem 0;
|
||||
}
|
||||
|
||||
.plan-group-items.collapsed {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* View mode toggle */
|
||||
.plan-view-toggle {
|
||||
display: flex;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
|
||||
.plan-view-btn {
|
||||
padding: 0.2rem 0.4rem;
|
||||
font-size: 0.7rem;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 3px;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
transition: all 0.15s;
|
||||
}
|
||||
|
||||
.plan-view-btn:hover {
|
||||
color: var(--text);
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
.plan-view-btn.active {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
/* Verification criteria tooltip expansion */
|
||||
.plan-item-verify-expanded {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: calc(100% + 4px);
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
padding: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text);
|
||||
z-index: 10;
|
||||
box-shadow: 0 -2px 8px rgba(0,0,0,0.2);
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.plan-item-verify-expanded::after {
|
||||
content: '';
|
||||
position: absolute;
|
||||
bottom: -6px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
border: 5px solid transparent;
|
||||
border-top-color: var(--border);
|
||||
}
|
||||
|
||||
/* Plan toolbar improvements */
|
||||
.plan-editor-toolbar {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.plan-toolbar-left {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.plan-toolbar-right {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
/* Plan item inline actions */
|
||||
.plan-item-actions {
|
||||
display: flex;
|
||||
gap: 0.1rem;
|
||||
opacity: 0;
|
||||
transition: opacity 0.15s;
|
||||
}
|
||||
|
||||
.plan-item:hover .plan-item-actions {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.plan-item-action {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--text-muted);
|
||||
cursor: pointer;
|
||||
font-size: 0.75rem;
|
||||
padding: 0.1rem 0.2rem;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.plan-item-action:hover {
|
||||
color: var(--text);
|
||||
background: var(--bg-card);
|
||||
}
|
||||
|
||||
/* Runtime task card enhancements */
|
||||
.ralph-task-card {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.ralph-task-attempts {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 2px;
|
||||
font-size: 0.6rem;
|
||||
color: var(--text-muted);
|
||||
background: var(--bg-card);
|
||||
padding: 0 3px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.ralph-task-attempts.has-errors {
|
||||
color: var(--red);
|
||||
background: rgba(239, 68, 68, 0.1);
|
||||
}
|
||||
|
||||
.ralph-task-verify-badge {
|
||||
position: absolute;
|
||||
bottom: 2px;
|
||||
right: 2px;
|
||||
font-size: 0.6rem;
|
||||
color: var(--green);
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
.ralph-task-deps-indicator {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 2px;
|
||||
font-size: 0.55rem;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
/* Quick actions on task hover */
|
||||
.ralph-task-actions {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
display: none;
|
||||
gap: 0.25rem;
|
||||
background: rgba(0,0,0,0.85);
|
||||
padding: 0.25rem;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.ralph-task-card:hover .ralph-task-actions {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.ralph-task-action-btn {
|
||||
padding: 0.2rem 0.35rem;
|
||||
font-size: 0.65rem;
|
||||
background: var(--bg-input);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 3px;
|
||||
color: var(--text);
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.ralph-task-action-btn:hover {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
border-color: var(--accent);
|
||||
}
|
||||
|
||||
/* Plan version indicator */
|
||||
.plan-version-indicator {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 0.75rem;
|
||||
color: var(--text-muted);
|
||||
padding: 0.25rem 0.5rem;
|
||||
background: var(--bg-input);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.plan-version-badge {
|
||||
background: var(--accent);
|
||||
color: white;
|
||||
padding: 0.1rem 0.3rem;
|
||||
border-radius: 3px;
|
||||
font-size: 0.65rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.plan-version-history {
|
||||
cursor: pointer;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.plan-version-history:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Plan history dropdown */
|
||||
.plan-history-dropdown {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0;
|
||||
background: var(--bg-card);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.3);
|
||||
z-index: 100;
|
||||
min-width: 200px;
|
||||
max-height: 200px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.plan-history-list {
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.plan-history-item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0.5rem 0.75rem;
|
||||
border-bottom: 1px solid var(--border);
|
||||
cursor: pointer;
|
||||
font-size: 0.8rem;
|
||||
transition: background-color 0.15s;
|
||||
}
|
||||
|
||||
.plan-history-item:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.plan-history-item:hover {
|
||||
background: var(--bg-input);
|
||||
}
|
||||
|
||||
.plan-history-item.current {
|
||||
background: var(--accent-dim);
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.plan-history-item.current::after {
|
||||
content: '(current)';
|
||||
font-size: 0.65rem;
|
||||
color: var(--accent);
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.plan-history-version {
|
||||
font-weight: 500;
|
||||
color: var(--text);
|
||||
}
|
||||
|
||||
.plan-history-tasks {
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
.plan-history-time {
|
||||
color: var(--text-muted);
|
||||
font-size: 0.7rem;
|
||||
}
|
||||
|
||||
/* Ralph panel version info */
|
||||
.ralph-version-info {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
font-size: 0.7rem;
|
||||
color: var(--text-muted);
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
.ralph-version-info .version-badge {
|
||||
background: var(--bg-card);
|
||||
padding: 0.1rem 0.25rem;
|
||||
border-radius: 2px;
|
||||
font-family: 'SF Mono', Monaco, monospace;
|
||||
}
|
||||
|
||||
.ralph-version-info .rollback-btn {
|
||||
background: none;
|
||||
border: none;
|
||||
color: var(--accent);
|
||||
cursor: pointer;
|
||||
font-size: 0.65rem;
|
||||
padding: 0.1rem 0.25rem;
|
||||
}
|
||||
|
||||
.ralph-version-info .rollback-btn:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
/* Textarea with AI Assist button */
|
||||
.textarea-with-assist {
|
||||
position: relative;
|
||||
|
||||
Reference in New Issue
Block a user